aboutsummaryrefslogtreecommitdiffstats
path: root/mediaplugin/src/plugins/media/ffmpeg/ffmpeg_audio_decode.h
blob: c0d1b288a82821c117fc14cb8b50f5f4200c340b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
/* UltraStar Deluxe - Karaoke Game
 *
 * UltraStar Deluxe is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file COPYING. If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 *
 * $URL$
 * $Id$
 */
#ifndef _FFMPEG_AUDIO_DECODE_H_
#define _FFMPEG_AUDIO_DECODE_H_

#include "ffmpeg_core.h"
#include "core/plugin_audio_decode.h"

// TODO: The factor 3/2 might not be necessary as we do not need extra
// space for synchronizing as in the tutorial.
#define AUDIO_BUFFER_SIZE ((AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2)

extern const audioDecoderInfo_t audioDecoderInfo;

class FFmpegAudioDecodeStream : public PluginDecodeStream, private Thread {
private:
	Mutex _stateLock;

	bool _eofState; // end-of-stream flag (locked by StateLock)
	bool _errorState; // error flag (locked by StateLock)

	bool _quitRequest; // (locked by StateLock)
	Condition _parserIdleCond;

	// parser pause/resume data
	bool _parserLocked;
	int _parserPauseRequestCount;
	Condition _parserUnlockedCond;
	Condition _parserResumeCond;

	bool _seekRequest; // (locked by StateLock)
	int _seekFlags; // (locked by StateLock)
	double _seekPos;    // stream position to seek for (in secs) (locked by StateLock)
	bool _seekFlush;   // true if the buffers should be flushed after seeking (locked by StateLock)
	Condition _seekFinishedCond;

	bool _loop; // (locked by StateLock)

	PacketQueue _packetQueue;

	AudioFormatInfo _formatInfo;

	// FFmpeg specific data
	AVFormatContext *_formatCtx;
	AVCodecContext *_codecCtx;
	AVCodec *_codec;

	int _audioStreamIndex;
	AVStream *_audioStream;
	double _audioStreamPos; // stream position in seconds (locked by DecoderLock)

	// decoder pause/resume data
	bool _decoderLocked;
	int _decoderPauseRequestCount;
	Condition _decoderUnlockedCond;
	Condition _decoderResumeCond;

	// state-vars for DecodeFrame (locked by DecoderLock)
	AVPacket _audioPaket;
	AVPacket _audioPaketTemp;
	int _audioPaketSilence; // number of bytes of silence to return

	// state-vars for AudioCallback (locked by DecoderLock)
	int _audioBufferPos;
	int _audioBufferSize;
	DECLARE_ALIGNED(16, uint8_t, _audioBuffer[AUDIO_BUFFER_SIZE]);

	IPath _filename;

private:
	FFmpegAudioDecodeStream();

	void setPositionIntern(double time, bool flush, bool blocking);

	void setEOF(bool state) {
		Mutex::RegionLock lock(_stateLock);
		_eofState = state;
	}

	void setError(bool state) {
		Mutex::RegionLock lock(_stateLock);
		_errorState = state;
	}

	bool isSeeking() {
		Mutex::RegionLock lock(_stateLock);
		return _seekRequest;
	}

	bool isQuit() {
		Mutex::RegionLock lock(_stateLock);
		return _quitRequest;
	}

	bool parseLoop();

	int decodeFrame(uint8_t *buffer, int bufferSize);
	void flushCodecBuffers();

	bool _open(const IPath &filename);
	void close();

public:
	virtual ~FFmpegAudioDecodeStream() {
		close();
	}

	static FFmpegAudioDecodeStream* open(const IPath &filename);

	virtual double getLength();

	virtual const AudioFormatInfo &getAudioFormatInfo() {
		return _formatInfo;
	}

	virtual double getPosition();
	virtual void setPosition(double time);

	virtual bool getLoop() {
		Mutex::RegionLock lock(_stateLock);
		return _loop;
	}

	virtual void setLoop(bool Enabled) {
		Mutex::RegionLock lock(_stateLock);
		_loop = Enabled;
	}

	virtual bool isEOF() {
		Mutex::RegionLock lock(_stateLock);
		return _eofState;
	}

	virtual bool isError() {
		Mutex::RegionLock lock(_stateLock);
		return _errorState;
	}

	virtual int readData(uint8_t *buffer, int bufferSize);

public:
	int run();

public:
	class ParserLock {
	private:
		FFmpegAudioDecodeStream *_stream;
	public:
		// Note: pthreads wakes threads waiting on a mutex in the order of their
		// priority and not in FIFO order. SDL does not provide any option to
		// control priorities. This might (and already did) starve threads waiting
		// on the mutex (e.g. SetPosition) making usdx look like it was froozen.
		// Instead of simply locking the critical section we set a ParserLocked flag
		// instead and give priority to the threads requesting the parser to pause.
		ParserLock(FFmpegAudioDecodeStream *stream) : _stream(stream) {
			Mutex::RegionLock lock(_stream->_stateLock);
			while (_stream->_parserPauseRequestCount > 0)
				_stream->_parserResumeCond.wait(_stream->_stateLock);
			_stream->_parserLocked = true;
		}

		~ParserLock() {
			Mutex::RegionLock lock(_stream->_stateLock);
			_stream->_parserLocked = false;
			_stream->_parserUnlockedCond.broadcast();
		}
	};

	class ParserPauser {
	private:
		FFmpegAudioDecodeStream *_stream;
	public:
		ParserPauser(FFmpegAudioDecodeStream *stream) : _stream(stream) {
			if (Thread::getCurrentThreadID() == _stream->getThreadID())
				return;

			{
				Mutex::RegionLock lock(_stream->_stateLock);
				++_stream->_parserPauseRequestCount;
				while (_stream->_parserLocked)
					_stream->_parserUnlockedCond.wait(_stream->_stateLock);
			}
		}

		~ParserPauser() {
			if (Thread::getCurrentThreadID() == _stream->getThreadID())
				return;

			{
				Mutex::RegionLock lock(_stream->_stateLock);
				--_stream->_parserPauseRequestCount;
				_stream->_parserResumeCond.signal();
			}
		}
	};

	class DecoderLock {
	private:
		FFmpegAudioDecodeStream *_stream;
	public:
		// prioritize pause requests
		DecoderLock(FFmpegAudioDecodeStream *stream) : _stream(stream) {
			Mutex::RegionLock lock(_stream->_stateLock);
			while (_stream->_decoderPauseRequestCount > 0)
				_stream->_decoderResumeCond.wait(_stream->_stateLock);
			_stream->_decoderLocked = true;
		}

		~DecoderLock() {
			Mutex::RegionLock lock(_stream->_stateLock);
			_stream->_decoderLocked = false;
			_stream->_decoderUnlockedCond.broadcast();
		}
	};

	class DecoderPauser {
	private:
		FFmpegAudioDecodeStream *_stream;
	public:
		DecoderPauser(FFmpegAudioDecodeStream *stream) : _stream(stream) {
			Mutex::RegionLock lock(_stream->_stateLock);
			++_stream->_decoderPauseRequestCount;
			while (_stream->_decoderLocked)
				_stream->_decoderUnlockedCond.wait(_stream->_stateLock);
		}

		~DecoderPauser() {
			Mutex::RegionLock lock(_stream->_stateLock);
			--_stream->_decoderPauseRequestCount;
			_stream->_decoderResumeCond.signal();
		}
	};
};

#endif /* _FFMPEG_AUDIO_DECODE_H_ */