From 3a90fb372d1144116759d74331bd8f63eeeaf320 Mon Sep 17 00:00:00 2001
From: tobigun <tobigun@b956fd51-792f-4845-bead-9b4dfca2ff2c>
Date: Wed, 27 Aug 2008 10:26:55 +0000
Subject: FFmpeg spelling fix

git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/trunk@1296 b956fd51-792f-4845-bead-9b4dfca2ff2c
---
 Game/Code/Classes/UAudioDecoder_FFMpeg.pas | 1114 ----------------------------
 Game/Code/Classes/UAudioDecoder_FFmpeg.pas | 1114 ++++++++++++++++++++++++++++
 2 files changed, 1114 insertions(+), 1114 deletions(-)
 delete mode 100644 Game/Code/Classes/UAudioDecoder_FFMpeg.pas
 create mode 100644 Game/Code/Classes/UAudioDecoder_FFmpeg.pas

diff --git a/Game/Code/Classes/UAudioDecoder_FFMpeg.pas b/Game/Code/Classes/UAudioDecoder_FFMpeg.pas
deleted file mode 100644
index d9b4c93c..00000000
--- a/Game/Code/Classes/UAudioDecoder_FFMpeg.pas
+++ /dev/null
@@ -1,1114 +0,0 @@
-unit UAudioDecoder_FFmpeg;
-
-(*******************************************************************************
- *
- * This unit 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
- *
- *******************************************************************************)
-
-interface
-
-{$IFDEF FPC}
-  {$MODE Delphi}
-{$ENDIF}
-
-{$I switches.inc}
-
-// show FFmpeg specific debug output
-{.$DEFINE DebugFFmpegDecode}
-
-// 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 EnableFFmpegErrorOutput}
-
-implementation
-
-uses
-  Classes,
-  SysUtils,
-  Math,
-  UMusic,
-  UIni,
-  UMain,
-  avcodec,
-  avformat,
-  avutil,
-  avio,
-  mathematics, // used for av_rescale_q
-  rational,
-  UMediaCore_FFmpeg,
-  SDL,
-  ULog,
-  UCommon,
-  UConfig;
-
-const
-  MAX_AUDIOQ_SIZE = (5 * 16 * 1024);
-
-const
-  // TODO: The factor 3/2 might not be necessary as we do not need extra
-  // space for synchronizing as in the tutorial.
-  AUDIO_BUFFER_SIZE = (AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) div 2;
-
-type
-  TFFmpegDecodeStream = class(TAudioDecodeStream)
-    private
-      StateLock:   PSDL_Mutex;
-
-      EOFState:   boolean; // end-of-stream flag (locked by StateLock)
-      ErrorState: boolean; // error flag (locked by StateLock)
-
-      QuitRequest: boolean; // (locked by StateLock)
-      ParserIdleCond: PSDL_Cond;
-
-      // parser pause/resume data
-      ParserLocked:            boolean;
-      ParserPauseRequestCount: integer;
-      ParserUnlockedCond:      PSDL_Cond;
-      ParserResumeCond:        PSDL_Cond;
-
-      SeekRequest: boolean; // (locked by StateLock)
-      SeekFlags:   integer; // (locked by StateLock)
-      SeekPos:     double;    // stream position to seek for (in secs) (locked by StateLock)
-      SeekFlush:   boolean;   // true if the buffers should be flushed after seeking (locked by StateLock)
-      SeekFinishedCond: PSDL_Cond;
-
-      Loop: boolean; // (locked by StateLock)
-
-      ParseThread: PSDL_Thread;
-      PacketQueue: TPacketQueue;
-
-      FormatInfo: TAudioFormatInfo;
-
-      // FFmpeg specific data
-      FormatCtx: PAVFormatContext;
-      CodecCtx:  PAVCodecContext;
-      Codec:     PAVCodec;
-
-      AudioStreamIndex: integer;
-      AudioStream: PAVStream;
-      AudioStreamPos: double; // stream position in seconds (locked by DecoderLock)
-
-      // decoder pause/resume data
-      DecoderLocked:            boolean;
-      DecoderPauseRequestCount: integer;
-      DecoderUnlockedCond:      PSDL_Cond;
-      DecoderResumeCond:        PSDL_Cond;
-
-      // state-vars for DecodeFrame (locked by DecoderLock)
-      AudioPaket:        TAVPacket;
-      AudioPaketData:    PChar;
-      AudioPaketSize:    integer;
-      AudioPaketSilence: integer; // number of bytes of silence to return
-
-      // state-vars for AudioCallback (locked by DecoderLock)
-      AudioBufferPos:  integer;
-      AudioBufferSize: integer;
-      AudioBuffer:     PChar;
-
-      Filename: string;
-
-      procedure SetPositionIntern(Time: real; Flush: boolean; Blocking: boolean);
-      procedure SetEOF(State: boolean);   {$IFDEF HasInline}inline;{$ENDIF}
-      procedure SetError(State: boolean); {$IFDEF HasInline}inline;{$ENDIF}
-      function IsSeeking(): boolean;
-      function IsQuit(): boolean;
-
-      procedure Reset();
-
-      procedure Parse();
-      function ParseLoop(): boolean;
-      procedure PauseParser();
-      procedure ResumeParser();
-
-      function DecodeFrame(Buffer: PChar; BufferSize: integer): integer;
-      procedure FlushCodecBuffers();
-      procedure PauseDecoder();
-      procedure ResumeDecoder();
-    public
-      constructor Create();
-      destructor Destroy(); override;
-
-      function Open(const Filename: string): boolean;
-      procedure Close();                     override;
-
-      function GetLength(): real;            override;
-      function GetAudioFormatInfo(): TAudioFormatInfo; override;
-      function GetPosition: real;            override;
-      procedure SetPosition(Time: real);     override;
-      function GetLoop(): boolean;           override;
-      procedure SetLoop(Enabled: boolean);   override;
-      function IsEOF(): boolean;             override;
-      function IsError(): boolean;           override;
-
-      function ReadData(Buffer: PChar; BufferSize: integer): integer; override;
-  end;
-
-type
-  TAudioDecoder_FFmpeg = class( TInterfacedObject, IAudioDecoder )
-    public
-      function GetName: string;
-
-      function InitializeDecoder(): boolean;
-      function FinalizeDecoder(): boolean;
-      function Open(const Filename: string): TAudioDecodeStream;
-  end;
-
-var
-  FFmpegCore: TMediaCore_FFmpeg;
-
-function ParseThreadMain(Data: Pointer): integer; cdecl; forward;
-
-
-{ TFFmpegDecodeStream }
-
-constructor TFFmpegDecodeStream.Create();
-begin
-  inherited Create();
-
-  StateLock := SDL_CreateMutex();
-  ParserUnlockedCond := SDL_CreateCond();
-  ParserResumeCond := SDL_CreateCond();
-  ParserIdleCond := SDL_CreateCond();
-  SeekFinishedCond := SDL_CreateCond();
-  DecoderUnlockedCond := SDL_CreateCond();
-  DecoderResumeCond := SDL_CreateCond();
-
-  // according to the documentation of avcodec_decode_audio(2), sample-data
-  // should be aligned on a 16 byte boundary. Otherwise internal calls
-  // (e.g. to SSE or Altivec operations) might fail or lack performance on some
-  // CPUs. Although GetMem() in Delphi and FPC seems to use a 16 byte or higher
-  // alignment for buffers of this size (alignment depends on the size of the
-  // requested buffer), we will set the alignment explicitly as the minimum
-  // alignment used by Delphi and FPC is on an 8 byte boundary.
-  // 
-  // Note: AudioBuffer was previously defined as a field of type TAudioBuffer
-  // (array[0..AUDIO_BUFFER_SIZE-1] of byte) and hence statically allocated.
-  // Fields of records are aligned different to memory allocated with GetMem(),
-  // aligning depending on the type but will be at least 2 bytes.
-  // AudioBuffer was not aligned to a 16 byte boundary. The {$ALIGN x} directive
-  // was not applicable as Delphi in contrast to FPC provides at most 8 byte
-  // alignment ({$ALIGN 16} is not supported) by this directive.
-  AudioBuffer := GetAlignedMem(AUDIO_BUFFER_SIZE, 16);
-
-  Reset();
-end;
-
-procedure TFFmpegDecodeStream.Reset();
-begin
-  ParseThread := nil;
-
-  EOFState := false;
-  ErrorState := false;
-  Loop := false;
-  QuitRequest := false;
-
-  AudioPaketData := nil;
-  AudioPaketSize := 0;
-  AudioPaketSilence := 0;
-
-  AudioBufferPos := 0;
-  AudioBufferSize := 0;
-
-  ParserLocked := false;
-  ParserPauseRequestCount := 0;
-  DecoderLocked := false;
-  DecoderPauseRequestCount := 0;
-
-  FillChar(AudioPaket, SizeOf(TAVPacket), 0);
-end;
-
-{*
- * Frees the decode-stream data.
- *}
-destructor TFFmpegDecodeStream.Destroy();
-begin
-  Close();
-
-  SDL_DestroyMutex(StateLock);
-  SDL_DestroyCond(ParserUnlockedCond);
-  SDL_DestroyCond(ParserResumeCond);
-  SDL_DestroyCond(ParserIdleCond);
-  SDL_DestroyCond(SeekFinishedCond);
-  SDL_DestroyCond(DecoderUnlockedCond);
-  SDL_DestroyCond(DecoderResumeCond);
-
-  FreeAlignedMem(AudioBuffer);
-
-  inherited;
-end;
-
-function TFFmpegDecodeStream.Open(const Filename: string): boolean;
-var
-  SampleFormat: TAudioSampleFormat;
-  AVResult: integer;
-begin
-  Result := false;
-
-  Close();
-  Reset();
-
-  if (not FileExists(Filename)) then
-  begin
-    Log.LogError('Audio-file does not exist: "' + Filename + '"', 'UAudio_FFmpeg');
-    Exit;
-  end;
-
-  Self.Filename := Filename;
-
-  // open audio file
-  if (av_open_input_file(FormatCtx, PChar(Filename), nil, 0, nil) <> 0) then
-  begin
-    Log.LogError('av_open_input_file failed: "' + Filename + '"', 'UAudio_FFmpeg');
-    Exit;
-  end;
-
-  // generate PTS values if they do not exist
-  FormatCtx^.flags := FormatCtx^.flags or AVFMT_FLAG_GENPTS;
-
-  // retrieve stream information
-  if (av_find_stream_info(FormatCtx) < 0) then
-  begin
-    Log.LogError('av_find_stream_info failed: "' + Filename + '"', 'UAudio_FFmpeg');
-    Close();
-    Exit;
-  end;
-
-  // FIXME: hack used by ffplay. Maybe should not use url_feof() to test for the end
-  FormatCtx^.pb.eof_reached := 0;
-
-  {$IFDEF DebugFFmpegDecode}
-  dump_format(FormatCtx, 0, pchar(Filename), 0);
-  {$ENDIF}
-
-  AudioStreamIndex := FFmpegCore.FindAudioStreamIndex(FormatCtx);
-  if (AudioStreamIndex < 0) then
-  begin
-    Log.LogError('FindAudioStreamIndex: No Audio-stream found "' + Filename + '"', 'UAudio_FFmpeg');
-    Close();
-    Exit;
-  end;
-
-  //Log.LogStatus('AudioStreamIndex is: '+ inttostr(ffmpegStreamID), 'UAudio_FFmpeg');
-
-  AudioStream := FormatCtx.streams[AudioStreamIndex];
-  CodecCtx := AudioStream^.codec;
-
-  // TODO: should we use this or not? Should we allow 5.1 channel audio?
-  (*
-  {$IF LIBAVCODEC_VERSION >= 51042000}
-  if (CodecCtx^.channels > 0) then
-    CodecCtx^.request_channels := Min(2, CodecCtx^.channels)
-  else
-    CodecCtx^.request_channels := 2;
-  {$IFEND}
-  *)
-
-  Codec := avcodec_find_decoder(CodecCtx^.codec_id);
-  if (Codec = nil) then
-  begin
-    Log.LogError('Unsupported codec!', 'UAudio_FFmpeg');
-    CodecCtx := nil;
-    Close();
-    Exit;
-  end;
-
-  // 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 := CodecCtx^.flags2 or CODEC_FLAG2_FAST;
-
-  // Note: avcodec_open() and avcodec_close() are not thread-safe and will
-  // fail if called concurrently by different threads.
-  FFmpegCore.LockAVCodec();
-  try
-    AVResult := avcodec_open(CodecCtx, Codec);
-  finally
-    FFmpegCore.UnlockAVCodec();
-  end;
-  if (AVResult < 0) then
-  begin
-    Log.LogError('avcodec_open failed!', 'UAudio_FFmpeg');
-    Close();
-    Exit;
-  end;
-
-  // now initialize the audio-format
-
-  if (not FFmpegCore.ConvertFFmpegToAudioFormat(CodecCtx^.sample_fmt, SampleFormat)) then
-  begin
-    // try standard format
-    SampleFormat := asfS16;
-  end;
-
-  FormatInfo := TAudioFormatInfo.Create(
-    CodecCtx^.channels,
-    CodecCtx^.sample_rate,
-    SampleFormat
-  );
-
-
-  PacketQueue := TPacketQueue.Create();
-
-  // finally start the decode thread
-  ParseThread := SDL_CreateThread(@ParseThreadMain, Self);
-
-  Result := true;
-end;
-
-procedure TFFmpegDecodeStream.Close();
-var
-  ThreadResult: integer;
-begin
-  // 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.
-  if (assigned(PacketQueue)) then
-    PacketQueue.Abort();
-
-  // send quit request (to parse-thread etc)
-  SDL_mutexP(StateLock);
-  QuitRequest := true;
-  SDL_CondBroadcast(ParserIdleCond);
-  SDL_mutexV(StateLock);
-
-  // abort parse-thread
-  if (ParseThread <> nil) then
-  begin
-    // and wait until it terminates
-    SDL_WaitThread(ParseThread, ThreadResult);
-    ParseThread := nil;
-  end;
-
-  // Close the codec
-  if (CodecCtx <> nil) then
-  begin
-    // avcodec_close() is not thread-safe
-    FFmpegCore.LockAVCodec();
-    try
-      avcodec_close(CodecCtx);
-    finally
-      FFmpegCore.UnlockAVCodec();
-    end;
-    CodecCtx := nil;
-  end;
-
-  // Close the video file
-  if (FormatCtx <> nil) then
-  begin
-    av_close_input_file(FormatCtx);
-    FormatCtx := nil;
-  end;
-
-  PerformOnClose();
-  
-  FreeAndNil(PacketQueue);
-  FreeAndNil(FormatInfo);
-end;
-
-function TFFmpegDecodeStream.GetLength(): real;
-begin
-  // do not forget to consider the start_time value here 
-  Result := (FormatCtx^.start_time + FormatCtx^.duration) / AV_TIME_BASE;
-end;
-
-function TFFmpegDecodeStream.GetAudioFormatInfo(): TAudioFormatInfo;
-begin
-  Result := FormatInfo;
-end;
-
-function TFFmpegDecodeStream.IsEOF(): boolean;
-begin
-  SDL_mutexP(StateLock);
-  Result := EOFState;
-  SDL_mutexV(StateLock);
-end;
-
-procedure TFFmpegDecodeStream.SetEOF(State: boolean);
-begin
-  SDL_mutexP(StateLock);
-  EOFState := State;
-  SDL_mutexV(StateLock);
-end;
-
-function TFFmpegDecodeStream.IsError(): boolean;
-begin
-  SDL_mutexP(StateLock);
-  Result := ErrorState;
-  SDL_mutexV(StateLock);
-end;
-
-procedure TFFmpegDecodeStream.SetError(State: boolean);
-begin
-  SDL_mutexP(StateLock);
-  ErrorState := State;
-  SDL_mutexV(StateLock);
-end;
-
-function TFFmpegDecodeStream.IsSeeking(): boolean;
-begin
-  SDL_mutexP(StateLock);
-  Result := SeekRequest;
-  SDL_mutexV(StateLock);
-end;
-
-function TFFmpegDecodeStream.IsQuit(): boolean;
-begin
-  SDL_mutexP(StateLock);
-  Result := QuitRequest;
-  SDL_mutexV(StateLock);
-end;
-
-function TFFmpegDecodeStream.GetPosition(): real;
-var
-  BufferSizeSec: double;
-begin
-  PauseDecoder();
-
-  // ReadData() does not return all of the buffer retrieved by DecodeFrame().
-  // Determine the size of the unused part of the decode-buffer.
-  BufferSizeSec := (AudioBufferSize - AudioBufferPos) /
-                   FormatInfo.BytesPerSec;
-
-  // subtract the size of unused buffer-data from the audio clock.
-  Result := AudioStreamPos - BufferSizeSec;
-
-  ResumeDecoder();
-end;
-
-procedure TFFmpegDecodeStream.SetPosition(Time: real);
-begin
-  SetPositionIntern(Time, true, true);
-end;
-
-function TFFmpegDecodeStream.GetLoop(): boolean;
-begin
-  SDL_mutexP(StateLock);
-  Result := Loop;
-  SDL_mutexV(StateLock);
-end;
-
-procedure TFFmpegDecodeStream.SetLoop(Enabled: boolean);
-begin
-  SDL_mutexP(StateLock);
-  Loop := Enabled;
-  SDL_mutexV(StateLock);
-end;
-
-
-(********************************************
- * Parser section
- ********************************************)
-
-procedure TFFmpegDecodeStream.PauseParser();
-begin
-  if (SDL_ThreadID() = ParseThread.threadid) then
-    Exit;
-    
-  SDL_mutexP(StateLock);
-  Inc(ParserPauseRequestCount);
-  while (ParserLocked) do
-    SDL_CondWait(ParserUnlockedCond, StateLock);
-  SDL_mutexV(StateLock);
-end;
-
-procedure TFFmpegDecodeStream.ResumeParser();
-begin
-  if (SDL_ThreadID() = ParseThread.threadid) then
-    Exit;
-
-  SDL_mutexP(StateLock);
-  Dec(ParserPauseRequestCount);
-  SDL_CondSignal(ParserResumeCond);
-  SDL_mutexV(StateLock);
-end;
-
-procedure TFFmpegDecodeStream.SetPositionIntern(Time: real; Flush: boolean; Blocking: boolean);
-begin
-  // - 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.
-  PauseParser();
-  PauseDecoder();
-  SDL_mutexP(StateLock);
-
-  // 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) then
-    SeekFlags := SeekFlags or AVSEEK_FLAG_BACKWARD;
-
-  EOFState := false;
-  ErrorState := false;
-
-  // send a reuse signal in case the parser was stopped (e.g. because of an EOF)
-  SDL_CondSignal(ParserIdleCond);
-
-  SDL_mutexV(StateLock);
-  ResumeDecoder();
-  ResumeParser();
-
-  // in blocking mode, wait until seeking is done
-  if (Blocking) then
-  begin
-    SDL_mutexP(StateLock);
-    while (SeekRequest) do
-      SDL_CondWait(SeekFinishedCond, StateLock);
-    SDL_mutexV(StateLock);
-  end;
-end;
-
-function ParseThreadMain(Data: Pointer): integer; cdecl;
-var
-  Stream: TFFmpegDecodeStream;
-begin
-  Stream := TFFmpegDecodeStream(Data);
-  if (Stream <> nil) then
-    Stream.Parse();
-  Result := 0;
-end;
-
-procedure TFFmpegDecodeStream.Parse();
-begin
-  // reuse thread as long as the stream is not terminated
-  while (ParseLoop()) do
-  begin
-    // wait for reuse or destruction of stream
-    SDL_mutexP(StateLock);
-    while (not (SeekRequest or QuitRequest)) do
-      SDL_CondWait(ParserIdleCond, StateLock);
-    SDL_mutexV(StateLock);
-  end;
-end;
-
-(**
- * 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.
- *)
-function TFFmpegDecodeStream.ParseLoop(): boolean;
-var
-  Packet: TAVPacket;
-  StatusPacket: PAVPacket;
-  SeekTarget: int64;
-  ByteIOCtx: PByteIOContext;
-  ErrorCode: integer;
-  StartSilence: double;       // duration of silence at start of stream
-  StartSilencePtr: PDouble;  // pointer for the EMPTY status packet 
-
-  // 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.
-  procedure LockParser();
-  begin
-    SDL_mutexP(StateLock);
-    while (ParserPauseRequestCount > 0) do
-      SDL_CondWait(ParserResumeCond, StateLock);
-    ParserLocked := true;
-    SDL_mutexV(StateLock);
-  end;
-
-  procedure UnlockParser();
-  begin
-    SDL_mutexP(StateLock);
-    ParserLocked := false;
-    SDL_CondBroadcast(ParserUnlockedCond);
-    SDL_mutexV(StateLock);
-  end;
-
-begin
-  Result := true;
-
-  while (true) do
-  begin
-    LockParser();
-    try
-
-      if (IsQuit()) then
-      begin
-        Result := false;
-        Exit;
-      end;
-
-      // handle seek-request (Note: no need to lock SeekRequest here)
-      if (SeekRequest) then
-      begin
-        // first try: seek on the audio stream
-        SeekTarget := Round(SeekPos / av_q2d(AudioStream^.time_base));
-        StartSilence := 0;
-        if (SeekTarget < AudioStream^.start_time) then
-          StartSilence := (AudioStream^.start_time - SeekTarget) * av_q2d(AudioStream^.time_base);
-        ErrorCode := av_seek_frame(FormatCtx, AudioStreamIndex, SeekTarget, SeekFlags);
-
-        if (ErrorCode < 0) then
-        begin
-          // second try: seek on the default stream (necessary for flv-videos and some ogg-files)
-          SeekTarget := Round(SeekPos * AV_TIME_BASE);
-          StartSilence := 0;
-          if (SeekTarget < FormatCtx^.start_time) then
-            StartSilence := (FormatCtx^.start_time - SeekTarget) / AV_TIME_BASE;
-          ErrorCode := av_seek_frame(FormatCtx, -1, SeekTarget, SeekFlags);
-        end;
-
-        // 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.
-        PauseDecoder();
-        SDL_mutexP(StateLock);
-        try
-          if (ErrorCode < 0) then
-          begin
-            // seeking failed
-            ErrorState := true;
-            Log.LogStatus('Seek Error in "'+FormatCtx^.filename+'"', 'UAudioDecoder_FFmpeg');
-          end
-          else
-          begin
-            if (SeekFlush) then
-            begin
-              // flush queue (we will send a Flush-Packet when seeking is finished)
-              PacketQueue.Flush();
-
-              // flush the decode buffers
-              AudioBufferSize := 0;
-              AudioBufferPos := 0;
-              AudioPaketSize := 0;
-              AudioPaketSilence := 0;
-              FlushCodecBuffers();
-              
-              // Set preliminary stream position. The position will be set to
-              // the correct value as soon as the first packet is decoded.
-              AudioStreamPos := SeekPos;
-            end
-            else
-            begin
-              // request avcodec buffer flush
-              PacketQueue.PutStatus(PKT_STATUS_FLAG_FLUSH, nil);
-            end;
-
-            // fill the gap between position 0 and start_time with silence
-            // but not if we are in loop mode
-            if ((StartSilence > 0) and (not Loop)) then
-            begin
-              GetMem(StartSilencePtr, SizeOf(StartSilence));
-              StartSilencePtr^ := StartSilence;
-              PacketQueue.PutStatus(PKT_STATUS_FLAG_EMPTY, StartSilencePtr);
-            end;
-          end;
-
-          SeekRequest := false;
-          SDL_CondBroadcast(SeekFinishedCond);
-        finally
-          SDL_mutexV(StateLock);
-          ResumeDecoder();
-        end;
-      end;
-
-      if (PacketQueue.GetSize() > MAX_AUDIOQ_SIZE) then
-      begin
-        SDL_Delay(10);
-        Continue;
-      end;
-
-      if (av_read_frame(FormatCtx, Packet) < 0) then
-      begin
-        // failed to read a frame, check reason
-        {$IF (LIBAVFORMAT_VERSION_MAJOR >= 52)}
-        ByteIOCtx := FormatCtx^.pb;
-        {$ELSE}
-        ByteIOCtx := @FormatCtx^.pb;
-        {$IFEND}
-
-        // check for end-of-file (eof is not an error)
-        if (url_feof(ByteIOCtx) <> 0) then
-        begin
-          if (GetLoop()) then
-          begin
-            // rewind stream (but do not flush)
-            SetPositionIntern(0, false, false);
-            Continue;
-          end
-          else
-          begin
-            // signal end-of-file
-            PacketQueue.PutStatus(PKT_STATUS_FLAG_EOF, nil);
-            Exit;
-          end;
-        end;
-
-        // check for errors
-        if (url_ferror(ByteIOCtx) <> 0) then
-        begin
-          // an error occured -> abort and wait for repositioning or termination
-          PacketQueue.PutStatus(PKT_STATUS_FLAG_ERROR, nil);
-          Exit;
-        end;
-
-        // no error -> wait for user input
-        SDL_Delay(100);
-        Continue;
-      end;
-
-      if (Packet.stream_index = AudioStreamIndex) then
-        PacketQueue.Put(@Packet)
-      else
-        av_free_packet(@Packet);
-
-    finally
-      UnlockParser();
-    end;
-  end;
-end;
-
-
-(********************************************
- * Decoder section
- ********************************************)
-
-procedure TFFmpegDecodeStream.PauseDecoder();
-begin
-  SDL_mutexP(StateLock);
-  Inc(DecoderPauseRequestCount);
-  while (DecoderLocked) do
-    SDL_CondWait(DecoderUnlockedCond, StateLock);
-  SDL_mutexV(StateLock);
-end;
-
-procedure TFFmpegDecodeStream.ResumeDecoder();
-begin
-  SDL_mutexP(StateLock);
-  Dec(DecoderPauseRequestCount);
-  SDL_CondSignal(DecoderResumeCond);
-  SDL_mutexV(StateLock);
-end;
-
-procedure TFFmpegDecodeStream.FlushCodecBuffers();
-begin
-  // if no flush operation is specified, avcodec_flush_buffers will not do anything.
-  if (@CodecCtx.codec.flush <> nil) then
-  begin
-    // flush buffers used by avcodec_decode_audio, etc.
-    avcodec_flush_buffers(CodecCtx);
-  end
-  else
-  begin
-    // we need a Workaround to avoid plopping noise with ogg-vorbis and
-    // mp3 (in older versions of FFmpeg).
-    // We will just reopen the codec.
-    FFmpegCore.LockAVCodec();
-    try
-      avcodec_close(CodecCtx);
-      avcodec_open(CodecCtx, Codec);
-    finally
-      FFmpegCore.UnlockAVCodec();
-    end;
-  end;
-end;
-
-function TFFmpegDecodeStream.DecodeFrame(Buffer: PChar; BufferSize: integer): integer;
-var
-  PaketDecodedSize: integer; // size of packet data used for decoding
-  DataSize: integer;         // size of output data decoded by FFmpeg
-  BlockQueue: boolean;
-  SilenceDuration: double;
-  {$IFDEF DebugFFmpegDecode}
-  TmpPos: double;
-  {$ENDIF}
-begin
-  Result := -1;
-
-  if (EOF) then
-    Exit;
-
-  while(true) do
-  begin
-    // for titles with start_time > 0 we have to generate silence
-    // until we reach the pts of the first data packet.
-    if (AudioPaketSilence > 0) then
-    begin
-      DataSize := Min(AudioPaketSilence, BufferSize);
-      FillChar(Buffer[0], DataSize, 0);
-      Dec(AudioPaketSilence, DataSize);
-      AudioStreamPos := AudioStreamPos + DataSize / FormatInfo.BytesPerSec; 
-      Result := DataSize;
-      Exit;
-    end;
-
-    // read packet data
-    while (AudioPaketSize > 0) do
-    begin
-      DataSize := BufferSize;
-
-      {$IF LIBAVCODEC_VERSION >= 51030000} // 51.30.0
-      PaketDecodedSize := avcodec_decode_audio2(CodecCtx, PSmallint(Buffer),
-                  DataSize, AudioPaketData, AudioPaketSize);
-      {$ELSE}
-      PaketDecodedSize := avcodec_decode_audio(CodecCtx, PSmallint(Buffer),
-                  DataSize, AudioPaketData, AudioPaketSize);
-      {$IFEND}
-
-      if(PaketDecodedSize < 0) then
-      begin
-        // if error, skip frame
-        {$IFDEF DebugFFmpegDecode}
-        DebugWriteln('Skip audio frame');
-        {$ENDIF}
-        AudioPaketSize := 0;
-        Break;
-      end;
-
-      Inc(AudioPaketData, PaketDecodedSize);
-      Dec(AudioPaketSize, PaketDecodedSize);
-
-      // check if avcodec_decode_audio returned data, otherwise fetch more frames
-      if (DataSize <= 0) then
-        Continue;
-
-      // update stream position by the amount of fetched data
-      AudioStreamPos := AudioStreamPos + DataSize / FormatInfo.BytesPerSec;
-      
-      // we have data, return it and come back for more later
-      Result := DataSize;
-      Exit;
-    end;
-
-    // free old packet data
-    if (AudioPaket.data <> nil) then
-      av_free_packet(@AudioPaket);
-
-    // do not block queue on seeking (to avoid deadlocks on the DecoderLock)
-    if (IsSeeking()) then
-      BlockQueue := false
-    else
-      BlockQueue := true;
-
-    // request a new packet and block if none available.
-    // If this fails, the queue was aborted.
-    if (PacketQueue.Get(AudioPaket, BlockQueue) <= 0) then
-      Exit;
-
-    // handle Status-packet
-    if (PChar(AudioPaket.data) = STATUS_PACKET) then
-    begin
-      AudioPaket.data := nil;
-      AudioPaketData := nil;
-      AudioPaketSize := 0;
-
-      case (AudioPaket.flags) of
-        PKT_STATUS_FLAG_FLUSH:
-        begin
-          // just used if SetPositionIntern was called without the flush flag.
-          FlushCodecBuffers;
-        end;
-        PKT_STATUS_FLAG_EOF: // end-of-file
-        begin
-          // ignore EOF while seeking
-          if (not IsSeeking()) then
-            SetEOF(true);
-          // buffer contains no data -> result = -1
-          Exit;
-        end;
-        PKT_STATUS_FLAG_ERROR:
-        begin
-          SetError(true);
-          Log.LogStatus('I/O Error', 'TFFmpegDecodeStream.DecodeFrame');
-          Exit;
-        end;
-        PKT_STATUS_FLAG_EMPTY:
-        begin
-          SilenceDuration := PDouble(PacketQueue.GetStatusInfo(AudioPaket))^;
-          AudioPaketSilence := Round(SilenceDuration * FormatInfo.SampleRate) * FormatInfo.FrameSize;
-          PacketQueue.FreeStatusInfo(AudioPaket);
-        end
-        else
-        begin
-          Log.LogStatus('Unknown status', 'TFFmpegDecodeStream.DecodeFrame');
-        end;
-      end;
-
-      Continue;
-    end;
-
-    AudioPaketData := PChar(AudioPaket.data);
-    AudioPaketSize := AudioPaket.size;
-
-    // if available, update the stream position to the presentation time of this package
-    if(AudioPaket.pts <> AV_NOPTS_VALUE) then
-    begin
-      {$IFDEF DebugFFmpegDecode}
-      TmpPos := AudioStreamPos;
-      {$ENDIF}
-      AudioStreamPos := av_q2d(AudioStream^.time_base) * AudioPaket.pts;
-      {$IFDEF DebugFFmpegDecode}
-      DebugWriteln('Timestamp: ' + floattostrf(AudioStreamPos, ffFixed, 15, 3) + ' ' +
-                   '(Calc: ' + floattostrf(TmpPos, ffFixed, 15, 3) + '), ' +
-                   'Diff: ' + floattostrf(AudioStreamPos-TmpPos, ffFixed, 15, 3));
-      {$ENDIF}
-    end;
-  end;
-end;
-
-function TFFmpegDecodeStream.ReadData(Buffer: PChar; BufferSize: integer): integer;
-var
-  CopyByteCount:   integer; // number of bytes to copy
-  RemainByteCount: integer; // number of bytes left (remain) to read
-  BufferPos: integer;
-
-  // prioritize pause requests
-  procedure LockDecoder();
-  begin
-    SDL_mutexP(StateLock);
-    while (DecoderPauseRequestCount > 0) do
-      SDL_CondWait(DecoderResumeCond, StateLock);
-    DecoderLocked := true;
-    SDL_mutexV(StateLock);
-  end;
-
-  procedure UnlockDecoder();
-  begin
-    SDL_mutexP(StateLock);
-    DecoderLocked := false;
-    SDL_CondBroadcast(DecoderUnlockedCond);
-    SDL_mutexV(StateLock);
-  end;
-
-begin
-  Result := -1;
-
-  // set number of bytes to copy to the output buffer
-  BufferPos := 0;
-
-  LockDecoder();
-  try
-    // leave if end-of-file is reached
-    if (EOF) then
-      Exit;
-
-    // copy data to output buffer
-    while (BufferPos < BufferSize) do
-    begin
-      // check if we need more data
-      if (AudioBufferPos >= AudioBufferSize) then
-      begin
-        AudioBufferPos := 0;
-
-        // we have already sent all our data; get more
-        AudioBufferSize := DecodeFrame(AudioBuffer, AUDIO_BUFFER_SIZE);
-
-        // check for errors or EOF
-        if(AudioBufferSize < 0) then
-        begin
-          Result := BufferPos;
-          Exit;
-        end;
-      end;
-
-      // calc number of new bytes in the decode-buffer
-      CopyByteCount := AudioBufferSize - AudioBufferPos;
-      // resize copy-count if more bytes available than needed (remaining bytes are used the next time)
-      RemainByteCount := BufferSize - BufferPos;
-      if (CopyByteCount > RemainByteCount) then
-        CopyByteCount := RemainByteCount;
-
-      Move(AudioBuffer[AudioBufferPos], Buffer[BufferPos], CopyByteCount);
-
-      Inc(BufferPos,      CopyByteCount);
-      Inc(AudioBufferPos, CopyByteCount);
-    end;
-  finally
-    UnlockDecoder();
-  end;
-
-  Result := BufferSize;
-end;
-
-
-{ TAudioDecoder_FFmpeg }
-
-function TAudioDecoder_FFmpeg.GetName: String;
-begin
-  Result := 'FFmpeg_Decoder';
-end;
-
-function TAudioDecoder_FFmpeg.InitializeDecoder: boolean;
-begin
-  //Log.LogStatus('InitializeDecoder', 'UAudioDecoder_FFmpeg');
-  FFmpegCore := TMediaCore_FFmpeg.GetInstance();
-  av_register_all();
-
-  // Do not show uninformative error messages by default.
-  // FFmpeg prints all error-infos on the console by default what
-  // is very confusing as the playback of the files is correct.
-  // We consider these errors to be internal to FFMpeg. They can be fixed
-  // by the FFmpeg guys only and do not provide any useful information in
-  // respect to USDX.
-  {$IFNDEF EnableFFmpegErrorOutput}
-    {$IF LIBAVUTIL_VERSION_MAJOR >= 50}
-    av_log_set_level(AV_LOG_FATAL);
-    {$ELSE}
-    // FATAL and ERROR share one log-level, so we have to use QUIET
-    av_log_set_level(AV_LOG_QUIET);
-    {$IFEND}
-  {$ENDIF}
-
-  Result := true;
-end;
-
-function TAudioDecoder_FFmpeg.FinalizeDecoder(): boolean;
-begin
-  Result := true;
-end;
-
-function TAudioDecoder_FFmpeg.Open(const Filename: string): TAudioDecodeStream;
-var
-  Stream: TFFmpegDecodeStream;
-begin
-  Result := nil;
-
-  Stream := TFFmpegDecodeStream.Create();
-  if (not Stream.Open(Filename)) then
-  begin
-    Stream.Free;
-    Exit;
-  end;
-
-  Result := Stream;
-end;
-
-
-initialization
-  MediaManager.Add(TAudioDecoder_FFmpeg.Create);
-
-end.
diff --git a/Game/Code/Classes/UAudioDecoder_FFmpeg.pas b/Game/Code/Classes/UAudioDecoder_FFmpeg.pas
new file mode 100644
index 00000000..d9b4c93c
--- /dev/null
+++ b/Game/Code/Classes/UAudioDecoder_FFmpeg.pas
@@ -0,0 +1,1114 @@
+unit UAudioDecoder_FFmpeg;
+
+(*******************************************************************************
+ *
+ * This unit 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
+ *
+ *******************************************************************************)
+
+interface
+
+{$IFDEF FPC}
+  {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+// show FFmpeg specific debug output
+{.$DEFINE DebugFFmpegDecode}
+
+// 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 EnableFFmpegErrorOutput}
+
+implementation
+
+uses
+  Classes,
+  SysUtils,
+  Math,
+  UMusic,
+  UIni,
+  UMain,
+  avcodec,
+  avformat,
+  avutil,
+  avio,
+  mathematics, // used for av_rescale_q
+  rational,
+  UMediaCore_FFmpeg,
+  SDL,
+  ULog,
+  UCommon,
+  UConfig;
+
+const
+  MAX_AUDIOQ_SIZE = (5 * 16 * 1024);
+
+const
+  // TODO: The factor 3/2 might not be necessary as we do not need extra
+  // space for synchronizing as in the tutorial.
+  AUDIO_BUFFER_SIZE = (AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) div 2;
+
+type
+  TFFmpegDecodeStream = class(TAudioDecodeStream)
+    private
+      StateLock:   PSDL_Mutex;
+
+      EOFState:   boolean; // end-of-stream flag (locked by StateLock)
+      ErrorState: boolean; // error flag (locked by StateLock)
+
+      QuitRequest: boolean; // (locked by StateLock)
+      ParserIdleCond: PSDL_Cond;
+
+      // parser pause/resume data
+      ParserLocked:            boolean;
+      ParserPauseRequestCount: integer;
+      ParserUnlockedCond:      PSDL_Cond;
+      ParserResumeCond:        PSDL_Cond;
+
+      SeekRequest: boolean; // (locked by StateLock)
+      SeekFlags:   integer; // (locked by StateLock)
+      SeekPos:     double;    // stream position to seek for (in secs) (locked by StateLock)
+      SeekFlush:   boolean;   // true if the buffers should be flushed after seeking (locked by StateLock)
+      SeekFinishedCond: PSDL_Cond;
+
+      Loop: boolean; // (locked by StateLock)
+
+      ParseThread: PSDL_Thread;
+      PacketQueue: TPacketQueue;
+
+      FormatInfo: TAudioFormatInfo;
+
+      // FFmpeg specific data
+      FormatCtx: PAVFormatContext;
+      CodecCtx:  PAVCodecContext;
+      Codec:     PAVCodec;
+
+      AudioStreamIndex: integer;
+      AudioStream: PAVStream;
+      AudioStreamPos: double; // stream position in seconds (locked by DecoderLock)
+
+      // decoder pause/resume data
+      DecoderLocked:            boolean;
+      DecoderPauseRequestCount: integer;
+      DecoderUnlockedCond:      PSDL_Cond;
+      DecoderResumeCond:        PSDL_Cond;
+
+      // state-vars for DecodeFrame (locked by DecoderLock)
+      AudioPaket:        TAVPacket;
+      AudioPaketData:    PChar;
+      AudioPaketSize:    integer;
+      AudioPaketSilence: integer; // number of bytes of silence to return
+
+      // state-vars for AudioCallback (locked by DecoderLock)
+      AudioBufferPos:  integer;
+      AudioBufferSize: integer;
+      AudioBuffer:     PChar;
+
+      Filename: string;
+
+      procedure SetPositionIntern(Time: real; Flush: boolean; Blocking: boolean);
+      procedure SetEOF(State: boolean);   {$IFDEF HasInline}inline;{$ENDIF}
+      procedure SetError(State: boolean); {$IFDEF HasInline}inline;{$ENDIF}
+      function IsSeeking(): boolean;
+      function IsQuit(): boolean;
+
+      procedure Reset();
+
+      procedure Parse();
+      function ParseLoop(): boolean;
+      procedure PauseParser();
+      procedure ResumeParser();
+
+      function DecodeFrame(Buffer: PChar; BufferSize: integer): integer;
+      procedure FlushCodecBuffers();
+      procedure PauseDecoder();
+      procedure ResumeDecoder();
+    public
+      constructor Create();
+      destructor Destroy(); override;
+
+      function Open(const Filename: string): boolean;
+      procedure Close();                     override;
+
+      function GetLength(): real;            override;
+      function GetAudioFormatInfo(): TAudioFormatInfo; override;
+      function GetPosition: real;            override;
+      procedure SetPosition(Time: real);     override;
+      function GetLoop(): boolean;           override;
+      procedure SetLoop(Enabled: boolean);   override;
+      function IsEOF(): boolean;             override;
+      function IsError(): boolean;           override;
+
+      function ReadData(Buffer: PChar; BufferSize: integer): integer; override;
+  end;
+
+type
+  TAudioDecoder_FFmpeg = class( TInterfacedObject, IAudioDecoder )
+    public
+      function GetName: string;
+
+      function InitializeDecoder(): boolean;
+      function FinalizeDecoder(): boolean;
+      function Open(const Filename: string): TAudioDecodeStream;
+  end;
+
+var
+  FFmpegCore: TMediaCore_FFmpeg;
+
+function ParseThreadMain(Data: Pointer): integer; cdecl; forward;
+
+
+{ TFFmpegDecodeStream }
+
+constructor TFFmpegDecodeStream.Create();
+begin
+  inherited Create();
+
+  StateLock := SDL_CreateMutex();
+  ParserUnlockedCond := SDL_CreateCond();
+  ParserResumeCond := SDL_CreateCond();
+  ParserIdleCond := SDL_CreateCond();
+  SeekFinishedCond := SDL_CreateCond();
+  DecoderUnlockedCond := SDL_CreateCond();
+  DecoderResumeCond := SDL_CreateCond();
+
+  // according to the documentation of avcodec_decode_audio(2), sample-data
+  // should be aligned on a 16 byte boundary. Otherwise internal calls
+  // (e.g. to SSE or Altivec operations) might fail or lack performance on some
+  // CPUs. Although GetMem() in Delphi and FPC seems to use a 16 byte or higher
+  // alignment for buffers of this size (alignment depends on the size of the
+  // requested buffer), we will set the alignment explicitly as the minimum
+  // alignment used by Delphi and FPC is on an 8 byte boundary.
+  // 
+  // Note: AudioBuffer was previously defined as a field of type TAudioBuffer
+  // (array[0..AUDIO_BUFFER_SIZE-1] of byte) and hence statically allocated.
+  // Fields of records are aligned different to memory allocated with GetMem(),
+  // aligning depending on the type but will be at least 2 bytes.
+  // AudioBuffer was not aligned to a 16 byte boundary. The {$ALIGN x} directive
+  // was not applicable as Delphi in contrast to FPC provides at most 8 byte
+  // alignment ({$ALIGN 16} is not supported) by this directive.
+  AudioBuffer := GetAlignedMem(AUDIO_BUFFER_SIZE, 16);
+
+  Reset();
+end;
+
+procedure TFFmpegDecodeStream.Reset();
+begin
+  ParseThread := nil;
+
+  EOFState := false;
+  ErrorState := false;
+  Loop := false;
+  QuitRequest := false;
+
+  AudioPaketData := nil;
+  AudioPaketSize := 0;
+  AudioPaketSilence := 0;
+
+  AudioBufferPos := 0;
+  AudioBufferSize := 0;
+
+  ParserLocked := false;
+  ParserPauseRequestCount := 0;
+  DecoderLocked := false;
+  DecoderPauseRequestCount := 0;
+
+  FillChar(AudioPaket, SizeOf(TAVPacket), 0);
+end;
+
+{*
+ * Frees the decode-stream data.
+ *}
+destructor TFFmpegDecodeStream.Destroy();
+begin
+  Close();
+
+  SDL_DestroyMutex(StateLock);
+  SDL_DestroyCond(ParserUnlockedCond);
+  SDL_DestroyCond(ParserResumeCond);
+  SDL_DestroyCond(ParserIdleCond);
+  SDL_DestroyCond(SeekFinishedCond);
+  SDL_DestroyCond(DecoderUnlockedCond);
+  SDL_DestroyCond(DecoderResumeCond);
+
+  FreeAlignedMem(AudioBuffer);
+
+  inherited;
+end;
+
+function TFFmpegDecodeStream.Open(const Filename: string): boolean;
+var
+  SampleFormat: TAudioSampleFormat;
+  AVResult: integer;
+begin
+  Result := false;
+
+  Close();
+  Reset();
+
+  if (not FileExists(Filename)) then
+  begin
+    Log.LogError('Audio-file does not exist: "' + Filename + '"', 'UAudio_FFmpeg');
+    Exit;
+  end;
+
+  Self.Filename := Filename;
+
+  // open audio file
+  if (av_open_input_file(FormatCtx, PChar(Filename), nil, 0, nil) <> 0) then
+  begin
+    Log.LogError('av_open_input_file failed: "' + Filename + '"', 'UAudio_FFmpeg');
+    Exit;
+  end;
+
+  // generate PTS values if they do not exist
+  FormatCtx^.flags := FormatCtx^.flags or AVFMT_FLAG_GENPTS;
+
+  // retrieve stream information
+  if (av_find_stream_info(FormatCtx) < 0) then
+  begin
+    Log.LogError('av_find_stream_info failed: "' + Filename + '"', 'UAudio_FFmpeg');
+    Close();
+    Exit;
+  end;
+
+  // FIXME: hack used by ffplay. Maybe should not use url_feof() to test for the end
+  FormatCtx^.pb.eof_reached := 0;
+
+  {$IFDEF DebugFFmpegDecode}
+  dump_format(FormatCtx, 0, pchar(Filename), 0);
+  {$ENDIF}
+
+  AudioStreamIndex := FFmpegCore.FindAudioStreamIndex(FormatCtx);
+  if (AudioStreamIndex < 0) then
+  begin
+    Log.LogError('FindAudioStreamIndex: No Audio-stream found "' + Filename + '"', 'UAudio_FFmpeg');
+    Close();
+    Exit;
+  end;
+
+  //Log.LogStatus('AudioStreamIndex is: '+ inttostr(ffmpegStreamID), 'UAudio_FFmpeg');
+
+  AudioStream := FormatCtx.streams[AudioStreamIndex];
+  CodecCtx := AudioStream^.codec;
+
+  // TODO: should we use this or not? Should we allow 5.1 channel audio?
+  (*
+  {$IF LIBAVCODEC_VERSION >= 51042000}
+  if (CodecCtx^.channels > 0) then
+    CodecCtx^.request_channels := Min(2, CodecCtx^.channels)
+  else
+    CodecCtx^.request_channels := 2;
+  {$IFEND}
+  *)
+
+  Codec := avcodec_find_decoder(CodecCtx^.codec_id);
+  if (Codec = nil) then
+  begin
+    Log.LogError('Unsupported codec!', 'UAudio_FFmpeg');
+    CodecCtx := nil;
+    Close();
+    Exit;
+  end;
+
+  // 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 := CodecCtx^.flags2 or CODEC_FLAG2_FAST;
+
+  // Note: avcodec_open() and avcodec_close() are not thread-safe and will
+  // fail if called concurrently by different threads.
+  FFmpegCore.LockAVCodec();
+  try
+    AVResult := avcodec_open(CodecCtx, Codec);
+  finally
+    FFmpegCore.UnlockAVCodec();
+  end;
+  if (AVResult < 0) then
+  begin
+    Log.LogError('avcodec_open failed!', 'UAudio_FFmpeg');
+    Close();
+    Exit;
+  end;
+
+  // now initialize the audio-format
+
+  if (not FFmpegCore.ConvertFFmpegToAudioFormat(CodecCtx^.sample_fmt, SampleFormat)) then
+  begin
+    // try standard format
+    SampleFormat := asfS16;
+  end;
+
+  FormatInfo := TAudioFormatInfo.Create(
+    CodecCtx^.channels,
+    CodecCtx^.sample_rate,
+    SampleFormat
+  );
+
+
+  PacketQueue := TPacketQueue.Create();
+
+  // finally start the decode thread
+  ParseThread := SDL_CreateThread(@ParseThreadMain, Self);
+
+  Result := true;
+end;
+
+procedure TFFmpegDecodeStream.Close();
+var
+  ThreadResult: integer;
+begin
+  // 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.
+  if (assigned(PacketQueue)) then
+    PacketQueue.Abort();
+
+  // send quit request (to parse-thread etc)
+  SDL_mutexP(StateLock);
+  QuitRequest := true;
+  SDL_CondBroadcast(ParserIdleCond);
+  SDL_mutexV(StateLock);
+
+  // abort parse-thread
+  if (ParseThread <> nil) then
+  begin
+    // and wait until it terminates
+    SDL_WaitThread(ParseThread, ThreadResult);
+    ParseThread := nil;
+  end;
+
+  // Close the codec
+  if (CodecCtx <> nil) then
+  begin
+    // avcodec_close() is not thread-safe
+    FFmpegCore.LockAVCodec();
+    try
+      avcodec_close(CodecCtx);
+    finally
+      FFmpegCore.UnlockAVCodec();
+    end;
+    CodecCtx := nil;
+  end;
+
+  // Close the video file
+  if (FormatCtx <> nil) then
+  begin
+    av_close_input_file(FormatCtx);
+    FormatCtx := nil;
+  end;
+
+  PerformOnClose();
+  
+  FreeAndNil(PacketQueue);
+  FreeAndNil(FormatInfo);
+end;
+
+function TFFmpegDecodeStream.GetLength(): real;
+begin
+  // do not forget to consider the start_time value here 
+  Result := (FormatCtx^.start_time + FormatCtx^.duration) / AV_TIME_BASE;
+end;
+
+function TFFmpegDecodeStream.GetAudioFormatInfo(): TAudioFormatInfo;
+begin
+  Result := FormatInfo;
+end;
+
+function TFFmpegDecodeStream.IsEOF(): boolean;
+begin
+  SDL_mutexP(StateLock);
+  Result := EOFState;
+  SDL_mutexV(StateLock);
+end;
+
+procedure TFFmpegDecodeStream.SetEOF(State: boolean);
+begin
+  SDL_mutexP(StateLock);
+  EOFState := State;
+  SDL_mutexV(StateLock);
+end;
+
+function TFFmpegDecodeStream.IsError(): boolean;
+begin
+  SDL_mutexP(StateLock);
+  Result := ErrorState;
+  SDL_mutexV(StateLock);
+end;
+
+procedure TFFmpegDecodeStream.SetError(State: boolean);
+begin
+  SDL_mutexP(StateLock);
+  ErrorState := State;
+  SDL_mutexV(StateLock);
+end;
+
+function TFFmpegDecodeStream.IsSeeking(): boolean;
+begin
+  SDL_mutexP(StateLock);
+  Result := SeekRequest;
+  SDL_mutexV(StateLock);
+end;
+
+function TFFmpegDecodeStream.IsQuit(): boolean;
+begin
+  SDL_mutexP(StateLock);
+  Result := QuitRequest;
+  SDL_mutexV(StateLock);
+end;
+
+function TFFmpegDecodeStream.GetPosition(): real;
+var
+  BufferSizeSec: double;
+begin
+  PauseDecoder();
+
+  // ReadData() does not return all of the buffer retrieved by DecodeFrame().
+  // Determine the size of the unused part of the decode-buffer.
+  BufferSizeSec := (AudioBufferSize - AudioBufferPos) /
+                   FormatInfo.BytesPerSec;
+
+  // subtract the size of unused buffer-data from the audio clock.
+  Result := AudioStreamPos - BufferSizeSec;
+
+  ResumeDecoder();
+end;
+
+procedure TFFmpegDecodeStream.SetPosition(Time: real);
+begin
+  SetPositionIntern(Time, true, true);
+end;
+
+function TFFmpegDecodeStream.GetLoop(): boolean;
+begin
+  SDL_mutexP(StateLock);
+  Result := Loop;
+  SDL_mutexV(StateLock);
+end;
+
+procedure TFFmpegDecodeStream.SetLoop(Enabled: boolean);
+begin
+  SDL_mutexP(StateLock);
+  Loop := Enabled;
+  SDL_mutexV(StateLock);
+end;
+
+
+(********************************************
+ * Parser section
+ ********************************************)
+
+procedure TFFmpegDecodeStream.PauseParser();
+begin
+  if (SDL_ThreadID() = ParseThread.threadid) then
+    Exit;
+    
+  SDL_mutexP(StateLock);
+  Inc(ParserPauseRequestCount);
+  while (ParserLocked) do
+    SDL_CondWait(ParserUnlockedCond, StateLock);
+  SDL_mutexV(StateLock);
+end;
+
+procedure TFFmpegDecodeStream.ResumeParser();
+begin
+  if (SDL_ThreadID() = ParseThread.threadid) then
+    Exit;
+
+  SDL_mutexP(StateLock);
+  Dec(ParserPauseRequestCount);
+  SDL_CondSignal(ParserResumeCond);
+  SDL_mutexV(StateLock);
+end;
+
+procedure TFFmpegDecodeStream.SetPositionIntern(Time: real; Flush: boolean; Blocking: boolean);
+begin
+  // - 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.
+  PauseParser();
+  PauseDecoder();
+  SDL_mutexP(StateLock);
+
+  // 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) then
+    SeekFlags := SeekFlags or AVSEEK_FLAG_BACKWARD;
+
+  EOFState := false;
+  ErrorState := false;
+
+  // send a reuse signal in case the parser was stopped (e.g. because of an EOF)
+  SDL_CondSignal(ParserIdleCond);
+
+  SDL_mutexV(StateLock);
+  ResumeDecoder();
+  ResumeParser();
+
+  // in blocking mode, wait until seeking is done
+  if (Blocking) then
+  begin
+    SDL_mutexP(StateLock);
+    while (SeekRequest) do
+      SDL_CondWait(SeekFinishedCond, StateLock);
+    SDL_mutexV(StateLock);
+  end;
+end;
+
+function ParseThreadMain(Data: Pointer): integer; cdecl;
+var
+  Stream: TFFmpegDecodeStream;
+begin
+  Stream := TFFmpegDecodeStream(Data);
+  if (Stream <> nil) then
+    Stream.Parse();
+  Result := 0;
+end;
+
+procedure TFFmpegDecodeStream.Parse();
+begin
+  // reuse thread as long as the stream is not terminated
+  while (ParseLoop()) do
+  begin
+    // wait for reuse or destruction of stream
+    SDL_mutexP(StateLock);
+    while (not (SeekRequest or QuitRequest)) do
+      SDL_CondWait(ParserIdleCond, StateLock);
+    SDL_mutexV(StateLock);
+  end;
+end;
+
+(**
+ * 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.
+ *)
+function TFFmpegDecodeStream.ParseLoop(): boolean;
+var
+  Packet: TAVPacket;
+  StatusPacket: PAVPacket;
+  SeekTarget: int64;
+  ByteIOCtx: PByteIOContext;
+  ErrorCode: integer;
+  StartSilence: double;       // duration of silence at start of stream
+  StartSilencePtr: PDouble;  // pointer for the EMPTY status packet 
+
+  // 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.
+  procedure LockParser();
+  begin
+    SDL_mutexP(StateLock);
+    while (ParserPauseRequestCount > 0) do
+      SDL_CondWait(ParserResumeCond, StateLock);
+    ParserLocked := true;
+    SDL_mutexV(StateLock);
+  end;
+
+  procedure UnlockParser();
+  begin
+    SDL_mutexP(StateLock);
+    ParserLocked := false;
+    SDL_CondBroadcast(ParserUnlockedCond);
+    SDL_mutexV(StateLock);
+  end;
+
+begin
+  Result := true;
+
+  while (true) do
+  begin
+    LockParser();
+    try
+
+      if (IsQuit()) then
+      begin
+        Result := false;
+        Exit;
+      end;
+
+      // handle seek-request (Note: no need to lock SeekRequest here)
+      if (SeekRequest) then
+      begin
+        // first try: seek on the audio stream
+        SeekTarget := Round(SeekPos / av_q2d(AudioStream^.time_base));
+        StartSilence := 0;
+        if (SeekTarget < AudioStream^.start_time) then
+          StartSilence := (AudioStream^.start_time - SeekTarget) * av_q2d(AudioStream^.time_base);
+        ErrorCode := av_seek_frame(FormatCtx, AudioStreamIndex, SeekTarget, SeekFlags);
+
+        if (ErrorCode < 0) then
+        begin
+          // second try: seek on the default stream (necessary for flv-videos and some ogg-files)
+          SeekTarget := Round(SeekPos * AV_TIME_BASE);
+          StartSilence := 0;
+          if (SeekTarget < FormatCtx^.start_time) then
+            StartSilence := (FormatCtx^.start_time - SeekTarget) / AV_TIME_BASE;
+          ErrorCode := av_seek_frame(FormatCtx, -1, SeekTarget, SeekFlags);
+        end;
+
+        // 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.
+        PauseDecoder();
+        SDL_mutexP(StateLock);
+        try
+          if (ErrorCode < 0) then
+          begin
+            // seeking failed
+            ErrorState := true;
+            Log.LogStatus('Seek Error in "'+FormatCtx^.filename+'"', 'UAudioDecoder_FFmpeg');
+          end
+          else
+          begin
+            if (SeekFlush) then
+            begin
+              // flush queue (we will send a Flush-Packet when seeking is finished)
+              PacketQueue.Flush();
+
+              // flush the decode buffers
+              AudioBufferSize := 0;
+              AudioBufferPos := 0;
+              AudioPaketSize := 0;
+              AudioPaketSilence := 0;
+              FlushCodecBuffers();
+              
+              // Set preliminary stream position. The position will be set to
+              // the correct value as soon as the first packet is decoded.
+              AudioStreamPos := SeekPos;
+            end
+            else
+            begin
+              // request avcodec buffer flush
+              PacketQueue.PutStatus(PKT_STATUS_FLAG_FLUSH, nil);
+            end;
+
+            // fill the gap between position 0 and start_time with silence
+            // but not if we are in loop mode
+            if ((StartSilence > 0) and (not Loop)) then
+            begin
+              GetMem(StartSilencePtr, SizeOf(StartSilence));
+              StartSilencePtr^ := StartSilence;
+              PacketQueue.PutStatus(PKT_STATUS_FLAG_EMPTY, StartSilencePtr);
+            end;
+          end;
+
+          SeekRequest := false;
+          SDL_CondBroadcast(SeekFinishedCond);
+        finally
+          SDL_mutexV(StateLock);
+          ResumeDecoder();
+        end;
+      end;
+
+      if (PacketQueue.GetSize() > MAX_AUDIOQ_SIZE) then
+      begin
+        SDL_Delay(10);
+        Continue;
+      end;
+
+      if (av_read_frame(FormatCtx, Packet) < 0) then
+      begin
+        // failed to read a frame, check reason
+        {$IF (LIBAVFORMAT_VERSION_MAJOR >= 52)}
+        ByteIOCtx := FormatCtx^.pb;
+        {$ELSE}
+        ByteIOCtx := @FormatCtx^.pb;
+        {$IFEND}
+
+        // check for end-of-file (eof is not an error)
+        if (url_feof(ByteIOCtx) <> 0) then
+        begin
+          if (GetLoop()) then
+          begin
+            // rewind stream (but do not flush)
+            SetPositionIntern(0, false, false);
+            Continue;
+          end
+          else
+          begin
+            // signal end-of-file
+            PacketQueue.PutStatus(PKT_STATUS_FLAG_EOF, nil);
+            Exit;
+          end;
+        end;
+
+        // check for errors
+        if (url_ferror(ByteIOCtx) <> 0) then
+        begin
+          // an error occured -> abort and wait for repositioning or termination
+          PacketQueue.PutStatus(PKT_STATUS_FLAG_ERROR, nil);
+          Exit;
+        end;
+
+        // no error -> wait for user input
+        SDL_Delay(100);
+        Continue;
+      end;
+
+      if (Packet.stream_index = AudioStreamIndex) then
+        PacketQueue.Put(@Packet)
+      else
+        av_free_packet(@Packet);
+
+    finally
+      UnlockParser();
+    end;
+  end;
+end;
+
+
+(********************************************
+ * Decoder section
+ ********************************************)
+
+procedure TFFmpegDecodeStream.PauseDecoder();
+begin
+  SDL_mutexP(StateLock);
+  Inc(DecoderPauseRequestCount);
+  while (DecoderLocked) do
+    SDL_CondWait(DecoderUnlockedCond, StateLock);
+  SDL_mutexV(StateLock);
+end;
+
+procedure TFFmpegDecodeStream.ResumeDecoder();
+begin
+  SDL_mutexP(StateLock);
+  Dec(DecoderPauseRequestCount);
+  SDL_CondSignal(DecoderResumeCond);
+  SDL_mutexV(StateLock);
+end;
+
+procedure TFFmpegDecodeStream.FlushCodecBuffers();
+begin
+  // if no flush operation is specified, avcodec_flush_buffers will not do anything.
+  if (@CodecCtx.codec.flush <> nil) then
+  begin
+    // flush buffers used by avcodec_decode_audio, etc.
+    avcodec_flush_buffers(CodecCtx);
+  end
+  else
+  begin
+    // we need a Workaround to avoid plopping noise with ogg-vorbis and
+    // mp3 (in older versions of FFmpeg).
+    // We will just reopen the codec.
+    FFmpegCore.LockAVCodec();
+    try
+      avcodec_close(CodecCtx);
+      avcodec_open(CodecCtx, Codec);
+    finally
+      FFmpegCore.UnlockAVCodec();
+    end;
+  end;
+end;
+
+function TFFmpegDecodeStream.DecodeFrame(Buffer: PChar; BufferSize: integer): integer;
+var
+  PaketDecodedSize: integer; // size of packet data used for decoding
+  DataSize: integer;         // size of output data decoded by FFmpeg
+  BlockQueue: boolean;
+  SilenceDuration: double;
+  {$IFDEF DebugFFmpegDecode}
+  TmpPos: double;
+  {$ENDIF}
+begin
+  Result := -1;
+
+  if (EOF) then
+    Exit;
+
+  while(true) do
+  begin
+    // for titles with start_time > 0 we have to generate silence
+    // until we reach the pts of the first data packet.
+    if (AudioPaketSilence > 0) then
+    begin
+      DataSize := Min(AudioPaketSilence, BufferSize);
+      FillChar(Buffer[0], DataSize, 0);
+      Dec(AudioPaketSilence, DataSize);
+      AudioStreamPos := AudioStreamPos + DataSize / FormatInfo.BytesPerSec; 
+      Result := DataSize;
+      Exit;
+    end;
+
+    // read packet data
+    while (AudioPaketSize > 0) do
+    begin
+      DataSize := BufferSize;
+
+      {$IF LIBAVCODEC_VERSION >= 51030000} // 51.30.0
+      PaketDecodedSize := avcodec_decode_audio2(CodecCtx, PSmallint(Buffer),
+                  DataSize, AudioPaketData, AudioPaketSize);
+      {$ELSE}
+      PaketDecodedSize := avcodec_decode_audio(CodecCtx, PSmallint(Buffer),
+                  DataSize, AudioPaketData, AudioPaketSize);
+      {$IFEND}
+
+      if(PaketDecodedSize < 0) then
+      begin
+        // if error, skip frame
+        {$IFDEF DebugFFmpegDecode}
+        DebugWriteln('Skip audio frame');
+        {$ENDIF}
+        AudioPaketSize := 0;
+        Break;
+      end;
+
+      Inc(AudioPaketData, PaketDecodedSize);
+      Dec(AudioPaketSize, PaketDecodedSize);
+
+      // check if avcodec_decode_audio returned data, otherwise fetch more frames
+      if (DataSize <= 0) then
+        Continue;
+
+      // update stream position by the amount of fetched data
+      AudioStreamPos := AudioStreamPos + DataSize / FormatInfo.BytesPerSec;
+      
+      // we have data, return it and come back for more later
+      Result := DataSize;
+      Exit;
+    end;
+
+    // free old packet data
+    if (AudioPaket.data <> nil) then
+      av_free_packet(@AudioPaket);
+
+    // do not block queue on seeking (to avoid deadlocks on the DecoderLock)
+    if (IsSeeking()) then
+      BlockQueue := false
+    else
+      BlockQueue := true;
+
+    // request a new packet and block if none available.
+    // If this fails, the queue was aborted.
+    if (PacketQueue.Get(AudioPaket, BlockQueue) <= 0) then
+      Exit;
+
+    // handle Status-packet
+    if (PChar(AudioPaket.data) = STATUS_PACKET) then
+    begin
+      AudioPaket.data := nil;
+      AudioPaketData := nil;
+      AudioPaketSize := 0;
+
+      case (AudioPaket.flags) of
+        PKT_STATUS_FLAG_FLUSH:
+        begin
+          // just used if SetPositionIntern was called without the flush flag.
+          FlushCodecBuffers;
+        end;
+        PKT_STATUS_FLAG_EOF: // end-of-file
+        begin
+          // ignore EOF while seeking
+          if (not IsSeeking()) then
+            SetEOF(true);
+          // buffer contains no data -> result = -1
+          Exit;
+        end;
+        PKT_STATUS_FLAG_ERROR:
+        begin
+          SetError(true);
+          Log.LogStatus('I/O Error', 'TFFmpegDecodeStream.DecodeFrame');
+          Exit;
+        end;
+        PKT_STATUS_FLAG_EMPTY:
+        begin
+          SilenceDuration := PDouble(PacketQueue.GetStatusInfo(AudioPaket))^;
+          AudioPaketSilence := Round(SilenceDuration * FormatInfo.SampleRate) * FormatInfo.FrameSize;
+          PacketQueue.FreeStatusInfo(AudioPaket);
+        end
+        else
+        begin
+          Log.LogStatus('Unknown status', 'TFFmpegDecodeStream.DecodeFrame');
+        end;
+      end;
+
+      Continue;
+    end;
+
+    AudioPaketData := PChar(AudioPaket.data);
+    AudioPaketSize := AudioPaket.size;
+
+    // if available, update the stream position to the presentation time of this package
+    if(AudioPaket.pts <> AV_NOPTS_VALUE) then
+    begin
+      {$IFDEF DebugFFmpegDecode}
+      TmpPos := AudioStreamPos;
+      {$ENDIF}
+      AudioStreamPos := av_q2d(AudioStream^.time_base) * AudioPaket.pts;
+      {$IFDEF DebugFFmpegDecode}
+      DebugWriteln('Timestamp: ' + floattostrf(AudioStreamPos, ffFixed, 15, 3) + ' ' +
+                   '(Calc: ' + floattostrf(TmpPos, ffFixed, 15, 3) + '), ' +
+                   'Diff: ' + floattostrf(AudioStreamPos-TmpPos, ffFixed, 15, 3));
+      {$ENDIF}
+    end;
+  end;
+end;
+
+function TFFmpegDecodeStream.ReadData(Buffer: PChar; BufferSize: integer): integer;
+var
+  CopyByteCount:   integer; // number of bytes to copy
+  RemainByteCount: integer; // number of bytes left (remain) to read
+  BufferPos: integer;
+
+  // prioritize pause requests
+  procedure LockDecoder();
+  begin
+    SDL_mutexP(StateLock);
+    while (DecoderPauseRequestCount > 0) do
+      SDL_CondWait(DecoderResumeCond, StateLock);
+    DecoderLocked := true;
+    SDL_mutexV(StateLock);
+  end;
+
+  procedure UnlockDecoder();
+  begin
+    SDL_mutexP(StateLock);
+    DecoderLocked := false;
+    SDL_CondBroadcast(DecoderUnlockedCond);
+    SDL_mutexV(StateLock);
+  end;
+
+begin
+  Result := -1;
+
+  // set number of bytes to copy to the output buffer
+  BufferPos := 0;
+
+  LockDecoder();
+  try
+    // leave if end-of-file is reached
+    if (EOF) then
+      Exit;
+
+    // copy data to output buffer
+    while (BufferPos < BufferSize) do
+    begin
+      // check if we need more data
+      if (AudioBufferPos >= AudioBufferSize) then
+      begin
+        AudioBufferPos := 0;
+
+        // we have already sent all our data; get more
+        AudioBufferSize := DecodeFrame(AudioBuffer, AUDIO_BUFFER_SIZE);
+
+        // check for errors or EOF
+        if(AudioBufferSize < 0) then
+        begin
+          Result := BufferPos;
+          Exit;
+        end;
+      end;
+
+      // calc number of new bytes in the decode-buffer
+      CopyByteCount := AudioBufferSize - AudioBufferPos;
+      // resize copy-count if more bytes available than needed (remaining bytes are used the next time)
+      RemainByteCount := BufferSize - BufferPos;
+      if (CopyByteCount > RemainByteCount) then
+        CopyByteCount := RemainByteCount;
+
+      Move(AudioBuffer[AudioBufferPos], Buffer[BufferPos], CopyByteCount);
+
+      Inc(BufferPos,      CopyByteCount);
+      Inc(AudioBufferPos, CopyByteCount);
+    end;
+  finally
+    UnlockDecoder();
+  end;
+
+  Result := BufferSize;
+end;
+
+
+{ TAudioDecoder_FFmpeg }
+
+function TAudioDecoder_FFmpeg.GetName: String;
+begin
+  Result := 'FFmpeg_Decoder';
+end;
+
+function TAudioDecoder_FFmpeg.InitializeDecoder: boolean;
+begin
+  //Log.LogStatus('InitializeDecoder', 'UAudioDecoder_FFmpeg');
+  FFmpegCore := TMediaCore_FFmpeg.GetInstance();
+  av_register_all();
+
+  // Do not show uninformative error messages by default.
+  // FFmpeg prints all error-infos on the console by default what
+  // is very confusing as the playback of the files is correct.
+  // We consider these errors to be internal to FFMpeg. They can be fixed
+  // by the FFmpeg guys only and do not provide any useful information in
+  // respect to USDX.
+  {$IFNDEF EnableFFmpegErrorOutput}
+    {$IF LIBAVUTIL_VERSION_MAJOR >= 50}
+    av_log_set_level(AV_LOG_FATAL);
+    {$ELSE}
+    // FATAL and ERROR share one log-level, so we have to use QUIET
+    av_log_set_level(AV_LOG_QUIET);
+    {$IFEND}
+  {$ENDIF}
+
+  Result := true;
+end;
+
+function TAudioDecoder_FFmpeg.FinalizeDecoder(): boolean;
+begin
+  Result := true;
+end;
+
+function TAudioDecoder_FFmpeg.Open(const Filename: string): TAudioDecodeStream;
+var
+  Stream: TFFmpegDecodeStream;
+begin
+  Result := nil;
+
+  Stream := TFFmpegDecodeStream.Create();
+  if (not Stream.Open(Filename)) then
+  begin
+    Stream.Free;
+    Exit;
+  end;
+
+  Result := Stream;
+end;
+
+
+initialization
+  MediaManager.Add(TAudioDecoder_FFmpeg.Create);
+
+end.
-- 
cgit v1.2.3