aboutsummaryrefslogtreecommitdiffstats
path: root/mediaplugin/src/plugins/media/ffmpeg/ffmpeg_audio_decode.cpp
diff options
context:
space:
mode:
authortobigun <tobigun@b956fd51-792f-4845-bead-9b4dfca2ff2c>2010-11-05 09:31:03 +0000
committertobigun <tobigun@b956fd51-792f-4845-bead-9b4dfca2ff2c>2010-11-05 09:31:03 +0000
commit4b808ab7f4b12551f5737bb666361efd79f62a3a (patch)
tree121a00d5ecf189837a7f49438722864ed260d09d /mediaplugin/src/plugins/media/ffmpeg/ffmpeg_audio_decode.cpp
parent3a079aafc4f98dc83ea36858bbca071342beb03d (diff)
downloadusdx-4b808ab7f4b12551f5737bb666361efd79f62a3a.tar.gz
usdx-4b808ab7f4b12551f5737bb666361efd79f62a3a.tar.xz
usdx-4b808ab7f4b12551f5737bb666361efd79f62a3a.zip
media plugins
git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/branches/experimental@2710 b956fd51-792f-4845-bead-9b4dfca2ff2c
Diffstat (limited to 'mediaplugin/src/plugins/media/ffmpeg/ffmpeg_audio_decode.cpp')
-rw-r--r--mediaplugin/src/plugins/media/ffmpeg/ffmpeg_audio_decode.cpp705
1 files changed, 705 insertions, 0 deletions
diff --git a/mediaplugin/src/plugins/media/ffmpeg/ffmpeg_audio_decode.cpp b/mediaplugin/src/plugins/media/ffmpeg/ffmpeg_audio_decode.cpp
new file mode 100644
index 00000000..da49891c
--- /dev/null
+++ b/mediaplugin/src/plugins/media/ffmpeg/ffmpeg_audio_decode.cpp
@@ -0,0 +1,705 @@
+/* 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$
+ */
+
+/*******************************************************************************
+ *
+ * This code is primarily based upon -
+ * http://www.dranger.com/ffmpeg/ffmpegtutorial_all.html
+ *
+ * and tutorial03.c
+ *
+ * http://www.inb.uni-luebeck.de/~boehme/using_libavcodec.html
+ *
+ *******************************************************************************/
+
+#include "ffmpeg_audio_decode.h"
+#include <string>
+#include <sstream>
+#include <cmath>
+
+// show FFmpeg specific debug output
+//#define DEBUG_FFMPEG_DECODE
+
+// FFmpeg is very verbose and shows a bunch of errors.
+// Those errors (they can be considered as warnings by us) can be ignored
+// as they do not give any useful information.
+// There is no solution to fix this except for turning them off.
+//#define ENABLE_FFMPEG_ERROR_OUTPUT
+
+#define MAX_AUDIOQ_SIZE (5 * 16 * 1024)
+
+/* FFmpegDecodeStream */
+
+FFmpegAudioDecodeStream::FFmpegAudioDecodeStream() :
+ _eofState(false),
+ _errorState(false),
+ _quitRequest(false),
+ _parserLocked(false),
+ _parserPauseRequestCount(0),
+ _seekRequest(false),
+ _seekFlags(0),
+ _seekPos(0),
+ _seekFlush(false),
+ _loop(false),
+ _formatCtx(NULL),
+ _codecCtx(NULL),
+ _codec(NULL),
+ _audioStreamIndex(-1),
+ _audioStream(NULL),
+ _audioStreamPos(0.0),
+ _decoderLocked(false),
+ _decoderPauseRequestCount(0),
+ _audioPaketSilence(0),
+ _audioBufferPos(0),
+ _audioBufferSize(0),
+ _filename("")
+{
+ memset(&_audioPaket, 0, sizeof(_audioPaket));
+ memset(&_audioPaketTemp, 0, sizeof(_audioPaketTemp));
+}
+
+FFmpegAudioDecodeStream* FFmpegAudioDecodeStream::open(const IPath &filename) {
+ FFmpegAudioDecodeStream *stream = new FFmpegAudioDecodeStream();
+ if (!stream->_open(filename)) {
+ delete stream;
+ return 0;
+ }
+ return stream;
+}
+
+bool FFmpegAudioDecodeStream::_open(const IPath &filename) {
+ _filename = filename;
+ if (!filename.isFile()) {
+ logger.error("Audio-file does not exist: '" + filename.toNative() + "'", "UAudio_FFmpeg");
+ return false;
+ }
+
+ // use custom 'ufile' protocol for UTF-8 support
+ if (av_open_input_file(&_formatCtx,
+ ("ufile:" + filename.toUTF8()).c_str(), NULL, 0, NULL) != 0)
+ {
+ logger.error("av_open_input_file failed: '" + filename.toNative() + "'", "UAudio_FFmpeg");
+ return false;
+ }
+
+ // generate PTS values if they do not exist
+ _formatCtx->flags |= AVFMT_FLAG_GENPTS;
+
+ // retrieve stream information
+ if (av_find_stream_info(_formatCtx) < 0) {
+ logger.error("av_find_stream_info failed: '" + filename.toNative() + "'", "UAudio_FFmpeg");
+ return false;
+ }
+
+ // FIXME: hack used by ffplay. Maybe should not use url_feof() to test for the end
+ _formatCtx->pb->eof_reached = 0;
+
+#ifdef DEBUG_FFMPEG_DECODE
+ dump_format(_formatCtx, 0, filename.toNative(), 0);
+#endif
+
+ _audioStreamIndex = ffmpegCore->findAudioStreamIndex(_formatCtx);
+ if (_audioStreamIndex < 0) {
+ logger.error("FindAudioStreamIndex: No Audio-stream found '" + filename.toNative() + "'", "UAudio_FFmpeg");
+ return false;
+ }
+
+ //std::stringstream s;
+ //s << _audioStreamIndex;
+ //logger.status("AudioStreamIndex is: " + s.str(), "UAudio_FFmpeg");
+
+ _audioStream = _formatCtx->streams[_audioStreamIndex];
+ _audioStreamPos = 0;
+ _codecCtx = _audioStream->codec;
+
+#ifdef REQUEST_CHANNELS
+ // TODO: should we use this or not? Should we allow 5.1 channel audio?
+ #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(51,42,0)
+ if (_codecCtx->channels > 0)
+ _codecCtx->request_channels = std::min(2, _codecCtx->channels);
+ else
+ _codecCtx->request_channels = 2;
+ #endif
+#endif
+
+ _codec = avcodec_find_decoder(_codecCtx->codec_id);
+ if (!_codec) {
+ logger.error("Unsupported codec!", "UAudio_FFmpeg");
+ _codecCtx = NULL;
+ return false;
+ }
+
+ // set debug options
+ _codecCtx->debug_mv = 0;
+ _codecCtx->debug = 0;
+
+ // detect bug-workarounds automatically
+ _codecCtx->workaround_bugs = FF_BUG_AUTODETECT;
+ // error resilience strategy (careful/compliant/agressive/very_aggressive)
+ //CodecCtx->error_resilience = FF_ER_CAREFUL; //FF_ER_COMPLIANT;
+ // allow non spec compliant speedup tricks.
+ //CodecCtx->flags2 |= CODEC_FLAG2_FAST;
+
+ // Note: avcodec_open() and avcodec_close() are not thread-safe and will
+ // fail if called concurrently by different threads.
+ int avResult;
+ {
+ MediaCore_FFmpeg::AVCodecLock codecLock;
+ avResult = avcodec_open(_codecCtx, _codec);
+ }
+ if (avResult < 0) {
+ logger.error("avcodec_open failed!", "UAudio_FFmpeg");
+ return false;
+ }
+
+ // now initialize the audio-format
+
+ audioSampleFormat_t sampleFormat;
+ if (!ffmpegCore->convertFFmpegToAudioFormat(_codecCtx->sample_fmt, &sampleFormat)) {
+ // try standard format
+ sampleFormat = asfS16;
+ }
+ if (_codecCtx->channels > 255) {
+ logger.status("Error: _codecCtx->channels > 255", "TFFmpegDecodeStream.Open");
+ }
+ _formatInfo = AudioFormatInfo(
+ _codecCtx->channels,
+ _codecCtx->sample_rate,
+ sampleFormat
+ );
+
+ // finally start the decode thread
+ start();
+
+ return true;
+}
+
+void FFmpegAudioDecodeStream::close() {
+ // wake threads waiting for packet-queue data
+ // Note: normally, there are no waiting threads. If there were waiting
+ // ones, they would block the audio-callback thread.
+ _packetQueue.abort();
+
+ // send quit request (to parse-thread etc)
+ {
+ Mutex::RegionLock lock(_stateLock);
+ _quitRequest = true;
+ _parserIdleCond.broadcast();
+ }
+
+ // abort parse-thread
+ // and wait until it terminates
+ wait();
+
+ // Close the codec
+ if (_codecCtx) {
+ // avcodec_close() is not thread-safe
+ MediaCore_FFmpeg::AVCodecLock codecLock;
+ avcodec_close(_codecCtx);
+ }
+
+ // Close the video file
+ if (_formatCtx) {
+ av_close_input_file(_formatCtx);
+ }
+}
+
+double FFmpegAudioDecodeStream::getLength() {
+ // do not forget to consider the start_time value here
+ // there is a type size mismatch warnign because start_time and duration are cint64.
+ // So, in principle there could be an overflow when doing the sum.
+ return (double)(_formatCtx->start_time + _formatCtx->duration) / AV_TIME_BASE;
+}
+
+double FFmpegAudioDecodeStream::getPosition() {
+ DecoderPauser decoderPauser(this);
+
+ // ReadData() does not return all of the buffer retrieved by DecodeFrame().
+ // Determine the size of the unused part of the decode-buffer.
+ double bufferSizeSec = (double)(_audioBufferSize - _audioBufferPos) /
+ _formatInfo.getBytesPerSec();
+
+ // subtract the size of unused buffer-data from the audio clock.
+ return _audioStreamPos - bufferSizeSec;
+}
+
+void FFmpegAudioDecodeStream::setPosition(double time) {
+ setPositionIntern(time, true, true);
+}
+
+/********************************************
+* Parser section
+********************************************/
+
+void FFmpegAudioDecodeStream::setPositionIntern(double time, bool flush, bool blocking) {
+ // - Pause the parser first to prevent it from putting obsolete packages
+ // into the queue after the queue was flushed and before seeking is done.
+ // Otherwise we will hear fragments of old data, if the stream was seeked
+ // in stopped mode and resumed afterwards (applies to non-blocking mode only).
+ // - Pause the decoder to avoid race-condition that might occur otherwise.
+ // - Last lock the state lock because we are manipulating some shared state-vars.
+ {
+ ParserPauser parserPauser(this);
+ DecoderPauser decoderPauser(this);
+ Mutex::RegionLock lock(_stateLock);
+
+ _eofState = false;
+ _errorState = false;
+
+ // do not seek if we are already at the correct position.
+ // This is important especially for seeking to position 0 if we already are
+ // at the beginning. Although seeking with AVSEEK_FLAG_BACKWARD for pos 0 works,
+ // it is still a bit choppy (although much better than w/o AVSEEK_FLAG_BACKWARD).
+ if (time == _audioStreamPos)
+ return;
+
+ // configure seek parameters
+ _seekPos = time;
+ _seekFlush = flush;
+ _seekFlags = AVSEEK_FLAG_ANY;
+ _seekRequest = true;
+
+ // Note: the BACKWARD-flag seeks to the first position <= the position
+ // searched for. Otherwise e.g. position 0 might not be seeked correct.
+ // For some reason ffmpeg sometimes doesn't use position 0 but the key-frame
+ // following. In streams with few key-frames (like many flv-files) the next
+ // key-frame after 0 might be 5secs ahead.
+ if (time <= _audioStreamPos)
+ _seekFlags |= AVSEEK_FLAG_BACKWARD;
+
+ // send a reuse signal in case the parser was stopped (e.g. because of an EOF)
+ _parserIdleCond.signal();
+ }
+
+ // in blocking mode, wait until seeking is done
+ if (blocking) {
+ Mutex::RegionLock lock(_stateLock);
+ while (_seekRequest)
+ _seekFinishedCond.wait(_stateLock);
+ }
+}
+
+int FFmpegAudioDecodeStream::run() {
+ // reuse thread as long as the stream is not terminated
+ while (parseLoop()) {
+ // wait for reuse or destruction of stream
+ Mutex::RegionLock lock(_stateLock);
+ while (!(_seekRequest || _quitRequest)) {
+ _parserIdleCond.wait(_stateLock);
+ }
+ }
+ return 0;
+}
+
+/**
+* Parser main loop.
+* Will not return until parsing of the stream is finished.
+* Reasons for the parser to return are:
+* - the end-of-file is reached
+* - an error occured
+* - the stream was quited (received a quit-request)
+* Returns true if the stream can be resumed or false if the stream has to
+* be terminated.
+*/
+bool FFmpegAudioDecodeStream::parseLoop() {
+ AVPacket packet;
+ while (true) {
+ ParserLock parserLock(this);
+ if (isQuit()) {
+ return false;
+ }
+
+ // handle seek-request (Note: no need to lock SeekRequest here)
+ if (_seekRequest) {
+ // first try: seek on the audio stream
+ int64_t seekTarget = llround(_seekPos / av_q2d(_audioStream->time_base));
+ // duration of silence at start of stream
+ double startSilence = 0;
+ if (seekTarget < _audioStream->start_time)
+ startSilence = (double)(_audioStream->start_time - seekTarget) * av_q2d(_audioStream->time_base);
+ int errorCode = av_seek_frame(_formatCtx, _audioStreamIndex, seekTarget, _seekFlags);
+
+ if (errorCode < 0) {
+ // second try: seek on the default stream (necessary for flv-videos and some ogg-files)
+ seekTarget = llround(_seekPos * AV_TIME_BASE);
+ startSilence = 0;
+ if (seekTarget < _formatCtx->start_time)
+ startSilence = (double)(_formatCtx->start_time - seekTarget) / AV_TIME_BASE;
+ errorCode = av_seek_frame(_formatCtx, -1, seekTarget, _seekFlags);
+ }
+
+ // pause decoder and lock state (keep the lock-order to avoid deadlocks).
+ // Note that the decoder does not block in the packet-queue in seeking state,
+ // so locking the decoder here does not cause a dead-lock.
+ {
+ DecoderPauser pause(this);
+ Mutex::RegionLock lock(_stateLock);
+
+ if (errorCode < 0) {
+ // seeking failed
+ _errorState = true;
+ std::stringstream s;
+ s << "Seek Error in '" << _formatCtx->filename << "'";
+ logger.error(s.str(), "UAudioDecoder_FFmpeg");
+ } else {
+ if (_seekFlush) {
+ // flush queue (we will send a Flush-Packet when seeking is finished)
+ _packetQueue.flush();
+
+ // flush the decode buffers
+ _audioBufferSize = 0;
+ _audioBufferPos = 0;
+ _audioPaketSilence = 0;
+ memset(&_audioPaketTemp, 0, sizeof(_audioPaketTemp));
+ flushCodecBuffers();
+
+ // Set preliminary stream position. The position will be set to
+ // the correct value as soon as the first packet is decoded.
+ _audioStreamPos = _seekPos;
+ } else {
+ // request avcodec buffer flush
+ _packetQueue.putStatus(PKT_STATUS_FLAG_FLUSH, NULL);
+ }
+
+ // fill the gap between position 0 and start_time with silence
+ // but not if we are in loop mode
+ if (startSilence > 0 && !_loop) {
+ // pointer for the EMPTY status packet
+ double *startSilencePtr = (double*)malloc(sizeof(startSilence));
+ *startSilencePtr = startSilence;
+ _packetQueue.putStatus(PKT_STATUS_FLAG_EMPTY, startSilencePtr);
+ }
+ }
+ _seekRequest = false;
+ _seekFinishedCond.broadcast();
+ }
+ }
+
+ if (_packetQueue.getSize() > MAX_AUDIOQ_SIZE) {
+ Thread::sleep(10);
+ continue;
+ }
+
+ if (av_read_frame(_formatCtx, &packet) < 0) {
+ // failed to read a frame, check reason
+ ByteIOContext *byteIOCtx;
+#if LIBAVFORMAT_VERSION_MAJOR >= 52
+ byteIOCtx = _formatCtx->pb;
+#else
+ byteIOCtx = &_formatCtx->pb;
+#endif
+
+ // check for end-of-file (eof is not an error)
+ if (url_feof(byteIOCtx) != 0) {
+ if (getLoop()) {
+ // rewind stream (but do not flush)
+ setPositionIntern(0, false, false);
+ continue;
+ } else {
+ // signal end-of-file
+ _packetQueue.putStatus(PKT_STATUS_FLAG_EOF, NULL);
+ return true;
+ }
+ }
+
+ // check for errors
+ if (url_ferror(byteIOCtx) != 0) {
+ // an error occured -> abort and wait for repositioning or termination
+ _packetQueue.putStatus(PKT_STATUS_FLAG_ERROR, NULL);
+ return true;
+ }
+
+ // url_feof() does not detect an EOF for some files
+ // so we have to do it this way.
+ if ((_formatCtx->file_size != 0) && (byteIOCtx->pos >= _formatCtx->file_size)) {
+ _packetQueue.putStatus(PKT_STATUS_FLAG_EOF, NULL);
+ return true;
+ }
+
+ // unknown error occured, exit
+ _packetQueue.putStatus(PKT_STATUS_FLAG_ERROR, NULL);
+ return true;
+ }
+
+ if (packet.stream_index == _audioStreamIndex) {
+ _packetQueue.put(&packet);
+ } else {
+ av_free_packet(&packet);
+ }
+ }
+}
+
+/********************************************
+* Decoder section
+********************************************/
+
+void FFmpegAudioDecodeStream::flushCodecBuffers() {
+ // if no flush operation is specified, avcodec_flush_buffers will not do anything.
+ if (_codecCtx->codec->flush) {
+ // flush buffers used by avcodec_decode_audio, etc.
+ avcodec_flush_buffers(_codecCtx);
+ } else {
+ // we need a Workaround to avoid plopping noise with ogg-vorbis and
+ // mp3 (in older versions of FFmpeg).
+ // We will just reopen the codec.
+ MediaCore_FFmpeg::AVCodecLock codecLock;
+ avcodec_close(_codecCtx);
+ avcodec_open(_codecCtx, _codec);
+ }
+}
+
+int FFmpegAudioDecodeStream::decodeFrame(uint8_t *buffer, int bufferSize) {
+ if (isEOF())
+ return -1;
+
+ while (true) {
+ // for titles with start_time > 0 we have to generate silence
+ // until we reach the pts of the first data packet.
+ if (_audioPaketSilence > 0) {
+ int dataSize = std::min(_audioPaketSilence, bufferSize);
+ memset(buffer, 0, dataSize);
+ _audioPaketSilence -= dataSize;
+ _audioStreamPos += dataSize / _formatInfo.getBytesPerSec();
+ return dataSize;
+ }
+
+ // read packet data
+ while (_audioPaketTemp.size > 0) {
+ // size of output data decoded by FFmpeg
+ int dataSize = bufferSize;
+
+ int paketDecodedSize; // size of packet data used for decoding
+ paketDecodedSize = avcodec_decode_audio3(_codecCtx, (int16_t*)buffer,
+ &dataSize, &_audioPaketTemp);
+
+ if (paketDecodedSize < 0) {
+ // if error, skip frame
+#ifdef DEBUG_FFMPEG_DECODE
+ logger.status("Skip audio frame", "");
+#endif
+ _audioPaketTemp.size = 0;
+ break;
+ }
+
+ _audioPaketTemp.data += paketDecodedSize;
+ _audioPaketTemp.size -= paketDecodedSize;
+
+ // check if avcodec_decode_audio returned data, otherwise fetch more frames
+ if (dataSize <= 0)
+ continue;
+
+ // update stream position by the amount of fetched data
+ _audioStreamPos += dataSize / _formatInfo.getBytesPerSec();
+
+ // we have data, return it and come back for more later
+ return dataSize;
+ }
+
+ // free old packet data
+ if (_audioPaket.data)
+ av_free_packet(&_audioPaket);
+
+ // do not block queue on seeking (to avoid deadlocks on the DecoderLock)
+ bool blockQueue = !isSeeking();
+
+ // request a new packet and block if none available.
+ // If this fails, the queue was aborted.
+ if (_packetQueue.get(&_audioPaket, blockQueue) <= 0)
+ return -1;
+
+ // handle Status-packet
+ if (_audioPaket.data == STATUS_PACKET) {
+ _audioPaket.data = NULL;
+ memset(&_audioPaketTemp, 0, sizeof(_audioPaketTemp));
+
+ switch (_audioPaket.flags) {
+ case PKT_STATUS_FLAG_FLUSH:
+ // just used if SetPositionIntern was called without the flush flag.
+ flushCodecBuffers();
+ break;
+ case PKT_STATUS_FLAG_EOF: // end-of-file
+ // ignore EOF while seeking
+ if (!isSeeking())
+ setEOF(true);
+ // buffer contains no data
+ return -1;
+ case PKT_STATUS_FLAG_ERROR:
+ setError(true);
+ logger.status("I/O Error", "TFFmpegDecodeStream.DecodeFrame");
+ return -1;
+ case PKT_STATUS_FLAG_EMPTY: {
+ double silenceDuration = *(double*) (_packetQueue.getStatusInfo(&_audioPaket));
+ _audioPaketSilence = lround(silenceDuration * _formatInfo.getSampleRate()) *
+ _formatInfo.getFrameSize();
+ _packetQueue.freeStatusInfo(&_audioPaket);
+ break;
+ }
+ default:
+ logger.status("Unknown status", "TFFmpegDecodeStream.DecodeFrame");
+ }
+
+ continue;
+ }
+
+ _audioPaketTemp.data = _audioPaket.data;
+ _audioPaketTemp.size = _audioPaket.size;
+
+ // if available, update the stream position to the presentation time of this package
+ if (_audioPaket.pts != (int64_t)AV_NOPTS_VALUE) {
+#ifdef DEBUG_FFMPEG_DECODE
+ double tmpPos = _audioStreamPos;
+#endif
+ _audioStreamPos = av_q2d(_audioStream->time_base) * _audioPaket.pts;
+#ifdef DEBUG_FFMPEG_DECODE
+ stringstream s;
+ s << "Timestamp: " << _audioStreamPos << " "
+ << "(Calc: " << tmpPos << "), "
+ << "Diff: " << (_audioStreamPos-TmpPos);
+ logger.status(s.str(), "");
+#endif
+ }
+ }
+}
+
+int FFmpegAudioDecodeStream::readData(uint8_t *buffer, int bufferSize) {
+ // set number of bytes to copy to the output buffer
+ int bufferPos = 0;
+
+ {
+ DecoderLock decoderLock(this);
+
+ // leave if end-of-file is reached
+ if (isEOF()) {
+ return -1;
+ }
+
+ // copy data to output buffer
+ while (bufferPos < bufferSize) {
+ // check if we need more data
+ if (_audioBufferPos >= _audioBufferSize) {
+ _audioBufferPos = 0;
+
+ // we have already sent all our data; get more
+ _audioBufferSize = decodeFrame(_audioBuffer, AUDIO_BUFFER_SIZE);
+ if (_audioBufferSize < 0) {
+ // error or EOF occurred
+ return bufferPos;
+ }
+ }
+
+ // calc number of new bytes in the decode-buffer (number of bytes to copy)
+ int copyByteCount = _audioBufferSize - _audioBufferPos;
+ // number of bytes left (remain) to read
+ int remainByteCount = bufferSize - bufferPos;
+ // resize copy-count if more bytes available than needed (remaining bytes are used the next time)
+ if (copyByteCount > remainByteCount)
+ copyByteCount = remainByteCount;
+
+ memcpy(&buffer[bufferPos], &_audioBuffer[_audioBufferPos], copyByteCount);
+ bufferPos += copyByteCount;
+ _audioBufferPos += copyByteCount;
+ }
+ }
+ return bufferSize;
+}
+
+/************************************
+ * C Interface
+ ************************************/
+
+#define DecodeStreamObj(ptr) reinterpret_cast<PluginDecodeStream*>(ptr)
+
+static BOOL PLUGIN_CALL ffmpegAudioDecoder_init() {
+ return TRUE;
+}
+
+static BOOL PLUGIN_CALL ffmpegAudioDecoder_finalize() {
+ return TRUE;
+}
+
+static audioDecodeStream_t* PLUGIN_CALL ffmpegAudioDecoder_open(const char *filename) {
+ return (audioDecodeStream_t*)FFmpegAudioDecodeStream::open(filename);
+}
+
+static void PLUGIN_CALL ffmpegAudioDecoder_close(audioDecodeStream_t *stream) {
+ delete DecodeStreamObj(stream);
+}
+
+static double PLUGIN_CALL ffmpegAudioDecoder_getLength(audioDecodeStream_t *stream) {
+ return DecodeStreamObj(stream)->getLength();
+}
+
+static void PLUGIN_CALL ffmpegAudioDecoder_getAudioFormatInfo(audioDecodeStream_t *stream, audioFormatInfo_t *info) {
+ DecodeStreamObj(stream)->getAudioFormatInfo().toCStruct(info);
+}
+
+static double PLUGIN_CALL ffmpegAudioDecoder_getPosition(audioDecodeStream_t *stream) {
+ return DecodeStreamObj(stream)->getPosition();
+}
+
+static void PLUGIN_CALL ffmpegAudioDecoder_setPosition(audioDecodeStream_t *stream, double time) {
+ DecodeStreamObj(stream)->setPosition(time);
+}
+
+static BOOL PLUGIN_CALL ffmpegAudioDecoder_getLoop(audioDecodeStream_t *stream) {
+ return (BOOL)DecodeStreamObj(stream)->getLoop();
+}
+
+static void PLUGIN_CALL ffmpegAudioDecoder_setLoop(audioDecodeStream_t *stream, BOOL enabled) {
+ DecodeStreamObj(stream)->setLoop(enabled);
+}
+
+static BOOL PLUGIN_CALL ffmpegAudioDecoder_isEOF(audioDecodeStream_t *stream) {
+ return (BOOL)DecodeStreamObj(stream)->isEOF();
+}
+
+static BOOL PLUGIN_CALL ffmpegAudioDecoder_isError(audioDecodeStream_t *stream) {
+ return (BOOL)DecodeStreamObj(stream)->isError();
+}
+
+static int PLUGIN_CALL ffmpegAudioDecoder_readData(audioDecodeStream_t *stream, uint8_t *buffer, int bufferSize) {
+ return DecodeStreamObj(stream)->readData(buffer, bufferSize);
+}
+
+/************************************
+ * Module information
+ ************************************/
+
+const audioDecoderInfo_t audioDecoderInfo = {
+ 50,
+ ffmpegAudioDecoder_init,
+ ffmpegAudioDecoder_finalize,
+ ffmpegAudioDecoder_open,
+ ffmpegAudioDecoder_close,
+ ffmpegAudioDecoder_getLength,
+ ffmpegAudioDecoder_getAudioFormatInfo,
+ ffmpegAudioDecoder_getPosition,
+ ffmpegAudioDecoder_setPosition,
+ ffmpegAudioDecoder_getLoop,
+ ffmpegAudioDecoder_setLoop,
+ ffmpegAudioDecoder_isEOF,
+ ffmpegAudioDecoder_isError,
+ ffmpegAudioDecoder_readData
+};