From e5bdcfc8e584d2867d0ad7995d18bd30c7874d1b Mon Sep 17 00:00:00 2001 From: tobigun Date: Fri, 28 Dec 2007 11:46:04 +0000 Subject: New class-structure seperates decoders/input and playback. The files aren't used in usdx until they are stable so they will not conflict with the old structure. The older files (UAudio_Bass etc.) will be replaced then. git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/trunk@752 b956fd51-792f-4845-bead-9b4dfca2ff2c --- Game/Code/Classes/UAudioDecoder_FFMpeg.pas | 600 +++++++++++++++++++++++++++++ 1 file changed, 600 insertions(+) create mode 100644 Game/Code/Classes/UAudioDecoder_FFMpeg.pas (limited to 'Game/Code/Classes/UAudioDecoder_FFMpeg.pas') diff --git a/Game/Code/Classes/UAudioDecoder_FFMpeg.pas b/Game/Code/Classes/UAudioDecoder_FFMpeg.pas new file mode 100644 index 00000000..54055454 --- /dev/null +++ b/Game/Code/Classes/UAudioDecoder_FFMpeg.pas @@ -0,0 +1,600 @@ +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} + + +uses Classes, + {$IFDEF win32} + windows, + {$ENDIF} + SysUtils, + SDL, + avcodec, // FFMpeg Audio file decoding + avformat, + avutil, + ULog, + UMusic; + +implementation + +uses + {$IFDEF LAZARUS} + lclintf, + {$ifndef win32} + libc, + {$endif} + {$ENDIF} + UIni, + UMain, + UThemes; + + +type + PPacketQueue = ^TPacketQueue; + TPacketQueue = class + private + firstPkt, + lastPkt : PAVPacketList; + nbPackets : integer; + size : integer; + mutex : PSDL_Mutex; + cond : PSDL_Cond; + quit : boolean; + + public + constructor Create(); + destructor Destroy(); override; + + function Put(pkt : PAVPacket): integer; + function Get(var pkt: TAVPacket; block: boolean): integer; + end; + +var + EOSPacket: TAVPacket; + +type + PAudioBuffer = ^TAudioBuffer; + TAudioBuffer = array[0 .. (AVCODEC_MAX_AUDIO_FRAME_SIZE * 3 div 2)-1] of byte; + +type + TFFMpegDecodeStream = class(TAudioDecodeStream) + private + status: TStreamStatus; + + EOS_Flag: boolean; // end-of-stream flag + + parseThread: PSDL_Thread; + packetQueue: TPacketQueue; + + // FFMpeg internal data + pFormatCtx : PAVFormatContext; + pCodecCtx : PAVCodecContext; + pCodec : PAVCodec; + ffmpegStreamID : Integer; + ffmpegStream : PAVStream; + + // "static" vars for DecodeFrame + pkt : TAVPacket; + audio_pkt_data : PChar; + audio_pkt_size : integer; + + // "static" vars for AudioCallback + audio_buf_index : cardinal; + audio_buf_size : cardinal; + audio_buf : TAudioBuffer; + + function DecodeFrame(var buffer: TAudioBuffer; bufSize: integer): integer; + public + constructor Create(pFormatCtx: PAVFormatContext; + pCodecCtx: PAVCodecContext; pCodec: PAVCodec; + ffmpegStreamID : Integer; ffmpegStream: PAVStream); + destructor Destroy(); override; + + procedure Close(); override; + + function GetLength(): real; override; + function GetChannelCount(): cardinal; override; + function GetSampleRate(): cardinal; override; + function GetPosition: real; override; + procedure SetPosition(Time: real); override; + function IsEOS(): boolean; override; + + function ReadData(Buffer: PChar; BufSize: integer): integer; override; + end; + +type + TAudioDecoder_FFMpeg = class( TInterfacedObject, IAudioDecoder ) + private + class function FindAudioStreamID(pFormatCtx : PAVFormatContext): integer; + public + function GetName: String; + + function InitializeDecoder(): boolean; + function Open(const Filename: string): TAudioDecodeStream; + end; + +function ParseAudio(streamPtr: Pointer): integer; cdecl; forward; + +var + singleton_AudioDecoderFFMpeg : IAudioDecoder; + + +{ TFFMpegDecodeStream } + +constructor TFFMpegDecodeStream.Create(pFormatCtx: PAVFormatContext; + pCodecCtx: PAVCodecContext; pCodec: PAVCodec; + ffmpegStreamID : Integer; ffmpegStream: PAVStream); +begin + inherited Create(); + + status := sStopped; + packetQueue := TPacketQueue.Create(); + + audio_pkt_data := nil; + audio_pkt_size := 0; + + audio_buf_index := 0; + audio_buf_size := 0; + + FillChar(pkt, sizeof(TAVPacket), #0); + + Self.pFormatCtx := pFormatCtx; + Self.pCodecCtx := pCodecCtx; + Self.pCodec := pCodec; + Self.ffmpegStreamID := ffmpegStreamID; + Self.ffmpegStream := ffmpegStream; + + EOS_Flag := false; + + parseThread := SDL_CreateThread(@ParseAudio, Self); +end; + +destructor TFFMpegDecodeStream.Destroy(); +begin + packetQueue.Free(); + //SDL_WaitThread(parseThread, nil); + inherited; +end; + +procedure TFFMpegDecodeStream.Close(); +begin + // Close the codec + avcodec_close(pCodecCtx); + + // Close the video file + av_close_input_file(pFormatCtx); + + // TODO: abort thread +end; + +function TFFMpegDecodeStream.GetLength(): real; +begin + result := pFormatCtx^.duration / AV_TIME_BASE; +end; + +function TFFMpegDecodeStream.GetChannelCount(): cardinal; +begin + result := pCodecCtx^.channels; +end; + +function TFFMpegDecodeStream.GetSampleRate(): cardinal; +begin + result := pCodecCtx^.sample_rate; +end; + +function TFFMpegDecodeStream.IsEOS(): boolean; +begin + result := EOS_Flag; +end; + + +function ParseAudio(streamPtr: Pointer): integer; cdecl; +var + packet: TAVPacket; + stream: TFFMpegDecodeStream; +begin + stream := TFFMpegDecodeStream(streamPtr); + + while (av_read_frame(stream.pFormatCtx, packet) >= 0) do + begin + //writeln( 'ffmpeg - av_read_frame' ); + + if (packet.stream_index = stream.ffmpegStreamID) then + begin + //writeln( 'packet_queue_put' ); + stream.packetQueue.put(@packet); + end + else + begin + av_free_packet(packet); + end; + end; + + //Writeln('Done: ' + inttostr(stream.packetQueue.nbPackets)); + + // signal end-of-stream + stream.packetQueue.put(@EOSPacket); + + result := 0; +end; + +function TFFMpegDecodeStream.DecodeFrame(var buffer: TAudioBuffer; bufSize: integer): integer; +var + len1, + data_size: integer; +begin + result := -1; + + if (EOS_Flag) then + exit; + + while true do + begin + while (audio_pkt_size > 0) do + begin + //writeln( 'got audio packet' ); + data_size := bufSize; + + // TODO: should be avcodec_decode_audio2 but this wont link on my ubuntu box. + len1 := avcodec_decode_audio(pCodecCtx, @buffer, + data_size, audio_pkt_data, audio_pkt_size); + + //writeln('avcodec_decode_audio : ' + inttostr( len1 )); + + if(len1 < 0) then + begin + // if error, skip frame + //writeln( 'Skip audio frame' ); + audio_pkt_size := 0; + break; + end; + + Inc(audio_pkt_data, len1); + Dec(audio_pkt_size, len1); + + if (data_size <= 0) then + begin + // No data yet, get more frames + continue; + end; + + // We have data, return it and come back for more later + result := data_size; + exit; + end; + + if (pkt.data <> nil) then + begin + av_free_packet(pkt); + end; + + if (packetQueue.quit) then + exit; + + if (packetQueue.Get(pkt, true) < 0) then + exit; + + audio_pkt_data := PChar(pkt.data); + audio_pkt_size := pkt.size; + + // check for end-of-stream + if (audio_pkt_data = PChar(EOSPacket.data)) then + begin + // end-of-stream reached -> set EOS-flag + EOS_Flag := true; + // note: buffer is not (even partially) filled -> no data to return + exit; + end; + + //writeln( 'Audio Packet Size - ' + inttostr(audio_pkt_size) ); + end; +end; + +function TFFMpegDecodeStream.ReadData(Buffer : PChar; BufSize: integer): integer; +var + outStream : TFFMpegDecodeStream; + len1, + audio_size : integer; + pSrc : Pointer; + len : integer; +begin + len := BufSize; + + // end-of-stream reached + if (EOS_Flag) then + exit; + + while (len > 0) do begin + if (audio_buf_index >= audio_buf_size) then + begin + // We have already sent all our data; get more + audio_size := DecodeFrame(audio_buf, sizeof(TAudioBuffer)); + //writeln('audio_decode_frame : '+ inttostr(audio_size)); + + if(audio_size < 0) then + begin + // If error, output silence + audio_buf_size := 1024; + FillChar(audio_buf, audio_buf_size, #0); + //writeln( 'Silence' ); + end + else + begin + audio_buf_size := audio_size; + end; + audio_buf_index := 0; + end; + + len1 := audio_buf_size - audio_buf_index; + if (len1 > len) then + len1 := len; + + pSrc := PChar(@audio_buf) + audio_buf_index; + {$ifdef WIN32} + CopyMemory(Buffer, pSrc , len1); + {$else} + memcpy(Buffer, pSrc , len1); + {$endif} + + Dec(len, len1); + Inc(PChar(Buffer), len1); + Inc(audio_buf_index, len1); + end; + + result := BufSize; +end; + +function TFFMpegDecodeStream.GetPosition(): real; +var + bytes: integer; +begin + Result := 0; +end; + +procedure TFFMpegDecodeStream.SetPosition(Time: real); +var + bytes: integer; +begin +end; + + +{ TAudioDecoder_FFMpeg } + +function TAudioDecoder_FFMpeg.GetName: String; +begin + result := 'FFMpeg_Decoder'; +end; + +function TAudioDecoder_FFMpeg.InitializeDecoder: boolean; +begin + //Log.LogStatus('InitializeDecoder', 'UAudioDecoder_FFMpeg'); + + av_register_all(); + + // init end-of-stream package + av_init_packet(EOSPacket); + EOSPacket.data := Pointer(PChar('EOS')); + + result := true; +end; + +class function TAudioDecoder_FFMpeg.FindAudioStreamID(pFormatCtx : PAVFormatContext): integer; +var + i : integer; + streamID: integer; + stream : PAVStream; +begin + // Find the first audio stream + streamID := -1; + + for i := 0 to pFormatCtx^.nb_streams-1 do + begin + //Log.LogStatus('aFormatCtx.streams[i] : ' + inttostr(i), 'UAudio_FFMpeg'); + stream := pFormatCtx^.streams[i]; + + if ( stream.codec^.codec_type = CODEC_TYPE_AUDIO ) then + begin + //Log.LogStatus('Found Audio Stream', 'UAudio_FFMpeg'); + streamID := i; + break; + end; + end; + + result := streamID; +end; + +function TAudioDecoder_FFMpeg.Open(const Filename: string): TAudioDecodeStream; +var + pFormatCtx : PAVFormatContext; + pCodecCtx : PAVCodecContext; + pCodec : PAVCodec; + ffmpegStreamID : Integer; + ffmpegStream : PAVStream; + wanted_spec, + csIndex : integer; + stream : TFFMpegDecodeStream; +begin + result := nil; + + if (not FileExists(Filename)) then + begin + Log.LogStatus('LoadSoundFromFile: Sound not found "' + Filename + '"', 'UAudio_FFMpeg'); + exit; + end; + + // Open audio file + if (av_open_input_file(pFormatCtx, PChar(Filename), nil, 0, nil) > 0) then + exit; + + // Retrieve stream information + if (av_find_stream_info(pFormatCtx) < 0) then + exit; + + dump_format(pFormatCtx, 0, pchar(Filename), 0); + + ffmpegStreamID := FindAudioStreamID(pFormatCtx); + if (ffmpegStreamID < 0) then + exit; + + //Log.LogStatus('Audio Stream ID is : '+ inttostr(ffmpegStreamID), 'UAudio_FFMpeg'); + + ffmpegStream := pFormatCtx.streams[ffmpegStreamID]; + pCodecCtx := ffmpegStream^.codec; + + pCodec := avcodec_find_decoder(pCodecCtx^.codec_id); + if (pCodec = nil) then + begin + Log.LogStatus('Unsupported codec!', 'UAudio_FFMpeg'); + exit; + end; + + avcodec_open(pCodecCtx, pCodec); + //writeln( 'Opened the codec' ); + + stream := TFFMpegDecodeStream.Create(pFormatCtx, pCodecCtx, pCodec, + ffmpegStreamID, ffmpegStream); + + result := stream; +end; + + +{ TPacketQueue } + +constructor TPacketQueue.Create(); +begin + inherited; + + firstPkt := nil; + lastPkt := nil; + nbPackets := 0; + size := 0; + + mutex := SDL_CreateMutex(); + cond := SDL_CreateCond(); +end; + +destructor TPacketQueue.Destroy(); +begin + SDL_DestroyMutex(mutex); + SDL_DestroyCond(cond); + inherited; +end; + +function TPacketQueue.Put(pkt : PAVPacket): integer; +var + pkt1 : PAVPacketList; +begin + result := -1; + + if (pkt <> @EOSPacket) then + if (av_dup_packet(pkt) < 0) then + exit; + + pkt1 := av_malloc(sizeof(TAVPacketList)); + if (pkt1 = nil) then + exit; + + pkt1^.pkt := pkt^; + pkt1^.next := nil; + + + SDL_LockMutex(Self.mutex); + try + + if (Self.lastPkt = nil) then + Self.firstPkt := pkt1 + else + Self.lastPkt^.next := pkt1; + + Self.lastPkt := pkt1; + inc(Self.nbPackets); + + //Writeln('Put: ' + inttostr(nbPackets)); + + Self.size := Self.size + pkt1^.pkt.size; + SDL_CondSignal(Self.cond); + + finally + SDL_UnlockMutex(Self.mutex); + end; + + result := 0; +end; + +function TPacketQueue.Get(var pkt: TAVPacket; block: boolean): integer; +var + pkt1 : PAVPacketList; +begin + result := -1; + + SDL_LockMutex(Self.mutex); + try + while true do + begin + if (quit) then + exit; + + pkt1 := Self.firstPkt; + + if (pkt1 <> nil) then + begin + Self.firstPkt := pkt1.next; + if (Self.firstPkt = nil) then + Self.lastPkt := nil; + dec(Self.nbPackets); + + //Writeln('Get: ' + inttostr(nbPackets)); + + Self.size := Self.size - pkt1^.pkt.size; + pkt := pkt1^.pkt; + av_free(pkt1); + + result := 1; + break; + end + else + if (not block) then + begin + result := 0; + break; + end + else + begin + SDL_CondWait(Self.cond, Self.mutex); + end; + end; + finally + SDL_UnlockMutex(Self.mutex); + end; +end; + + + +initialization + singleton_AudioDecoderFFMpeg := TAudioDecoder_FFMpeg.create(); + + //writeln( 'UAudioDecoder_FFMpeg - Register Decoder' ); + AudioManager.add( singleton_AudioDecoderFFMpeg ); + +finalization + AudioManager.Remove( singleton_AudioDecoderFFMpeg ); + + +end. -- cgit v1.2.3 From d69c2a35192623e5d923f17c07d96d980d045adf Mon Sep 17 00:00:00 2001 From: tobigun Date: Thu, 10 Jan 2008 22:59:10 +0000 Subject: partial seeking and mixing support git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/trunk@775 b956fd51-792f-4845-bead-9b4dfca2ff2c --- Game/Code/Classes/UAudioDecoder_FFMpeg.pas | 273 +++++++++++++++++++++++------ 1 file changed, 216 insertions(+), 57 deletions(-) (limited to 'Game/Code/Classes/UAudioDecoder_FFMpeg.pas') diff --git a/Game/Code/Classes/UAudioDecoder_FFMpeg.pas b/Game/Code/Classes/UAudioDecoder_FFMpeg.pas index 54055454..c81e4be1 100644 --- a/Game/Code/Classes/UAudioDecoder_FFMpeg.pas +++ b/Game/Code/Classes/UAudioDecoder_FFMpeg.pas @@ -26,9 +26,11 @@ uses Classes, {$ENDIF} SysUtils, SDL, - avcodec, // FFMpeg Audio file decoding + avcodec, // FFMpeg Audio file decoding avformat, avutil, + avio, // used for url_ferror + mathematics, // used for av_rescale_q ULog, UMusic; @@ -64,10 +66,15 @@ type function Put(pkt : PAVPacket): integer; function Get(var pkt: TAVPacket; block: boolean): integer; + procedure Flush(); end; +const + MAX_AUDIOQ_SIZE = (5 * 16 * 1024); + var - EOSPacket: TAVPacket; + EOFPacket: TAVPacket; + FlushPacket: TAVPacket; type PAudioBuffer = ^TAudioBuffer; @@ -76,9 +83,17 @@ type type TFFMpegDecodeStream = class(TAudioDecodeStream) private - status: TStreamStatus; + _EOF: boolean; // end-of-stream flag + _EOF_lock : PSDL_Mutex; + + lock : PSDL_Mutex; + resumeCond : PSDL_Cond; + + quitRequest : boolean; - EOS_Flag: boolean; // end-of-stream flag + seekRequest: boolean; + seekFlags : integer; + seekPos : int64; parseThread: PSDL_Thread; packetQueue: TPacketQueue; @@ -87,8 +102,8 @@ type pFormatCtx : PAVFormatContext; pCodecCtx : PAVCodecContext; pCodec : PAVCodec; - ffmpegStreamID : Integer; - ffmpegStream : PAVStream; + ffmpegStreamIndex : Integer; + ffmpegStream : PAVStream; // "static" vars for DecodeFrame pkt : TAVPacket; @@ -101,6 +116,7 @@ type audio_buf : TAudioBuffer; function DecodeFrame(var buffer: TAudioBuffer; bufSize: integer): integer; + procedure SetEOF(state: boolean); public constructor Create(pFormatCtx: PAVFormatContext; pCodecCtx: PAVCodecContext; pCodec: PAVCodec; @@ -114,7 +130,7 @@ type function GetSampleRate(): cardinal; override; function GetPosition: real; override; procedure SetPosition(Time: real); override; - function IsEOS(): boolean; override; + function IsEOF(): boolean; override; function ReadData(Buffer: PChar; BufSize: integer): integer; override; end; @@ -122,7 +138,7 @@ type type TAudioDecoder_FFMpeg = class( TInterfacedObject, IAudioDecoder ) private - class function FindAudioStreamID(pFormatCtx : PAVFormatContext): integer; + class function FindAudioStreamIndex(pFormatCtx : PAVFormatContext): integer; public function GetName: String; @@ -144,7 +160,6 @@ constructor TFFMpegDecodeStream.Create(pFormatCtx: PAVFormatContext; begin inherited Create(); - status := sStopped; packetQueue := TPacketQueue.Create(); audio_pkt_data := nil; @@ -158,30 +173,46 @@ begin Self.pFormatCtx := pFormatCtx; Self.pCodecCtx := pCodecCtx; Self.pCodec := pCodec; - Self.ffmpegStreamID := ffmpegStreamID; - Self.ffmpegStream := ffmpegStream; + Self.ffmpegStreamIndex := ffmpegStreamIndex; + Self.ffmpegStream := ffmpegStream; - EOS_Flag := false; + _EOF := false; + _EOF_lock := SDL_CreateMutex(); + + lock := SDL_CreateMutex(); + resumeCond := SDL_CreateCond(); parseThread := SDL_CreateThread(@ParseAudio, Self); end; destructor TFFMpegDecodeStream.Destroy(); begin - packetQueue.Free(); - //SDL_WaitThread(parseThread, nil); + //Close(); + //packetQueue.Free(); inherited; end; procedure TFFMpegDecodeStream.Close(); begin + // TODO: abort thread + //quitRequest := true; + //SDL_WaitThread(parseThread, nil); + + (* // Close the codec - avcodec_close(pCodecCtx); + if (pCodecCtx <> nil) then + begin + avcodec_close(pCodecCtx); + pCodecCtx := nil; + end; // Close the video file - av_close_input_file(pFormatCtx); - - // TODO: abort thread + if (pFormatCtx <> nil) then + begin + av_close_input_file(pFormatCtx); + pFormatCtx := nil; + end; + *) end; function TFFMpegDecodeStream.GetLength(): real; @@ -199,24 +230,134 @@ begin result := pCodecCtx^.sample_rate; end; -function TFFMpegDecodeStream.IsEOS(): boolean; +function TFFMpegDecodeStream.IsEOF(): boolean; +begin + SDL_mutexP(_EOF_lock); + result := _EOF; + SDL_mutexV(_EOF_lock); +end; + +procedure TFFMpegDecodeStream.SetEOF(state: boolean); +begin + SDL_mutexP(_EOF_lock); + _EOF := state; + SDL_mutexV(_EOF_lock); +end; + +function TFFMpegDecodeStream.GetPosition(): real; +var + bytes: integer; begin - result := EOS_Flag; + // see: tutorial on synching (audio-clock) + Result := 0; end; +procedure TFFMpegDecodeStream.SetPosition(Time: real); +var + bytes: integer; +begin + SDL_mutexP(lock); + seekPos := Trunc(Time * AV_TIME_BASE); + // FIXME: seek_flags = rel < 0 ? AVSEEK_FLAG_BACKWARD : 0 + seekFlags := 0; + seekRequest := true; + SDL_CondSignal(resumeCond); + SDL_mutexV(lock); +end; function ParseAudio(streamPtr: Pointer): integer; cdecl; var packet: TAVPacket; stream: TFFMpegDecodeStream; + seekTarget: int64; + eofState: boolean; begin stream := TFFMpegDecodeStream(streamPtr); + eofState := false; - while (av_read_frame(stream.pFormatCtx, packet) >= 0) do + while (true) do begin + + SDL_mutexP(stream.lock); + + // wait if end-of-file reached + if (eofState) then + begin + if (not (stream.seekRequest or stream.quitRequest)) then + begin + // signal end-of-file + stream.packetQueue.put(@EOFPacket); + // wait for reuse or destruction of stream + repeat + SDL_CondWait(stream.resumeCond, stream.lock); + until (stream.seekRequest or stream.quitRequest); + end; + eofState := false; + stream.SetEOF(false); + end; + + if (stream.quitRequest) then + begin + break; + end; + + // handle seek-request + if(stream.seekRequest) then + begin + // TODO: Do we need this? + // The position is converted to AV_TIME_BASE and then to the stream-specific base. + // Why not convert to the stream-specific one from the beginning. + seekTarget := av_rescale_q(stream.seekPos, AV_TIME_BASE_Q, stream.ffmpegStream^.time_base); + if(av_seek_frame(stream.pFormatCtx, stream.ffmpegStreamIndex, + seekTarget, stream.seekFlags) = 0) then + begin + Log.LogStatus(stream.pFormatCtx^.filename + ': error while seeking', 'UAudioDecoder_FFMpeg'); + end + else + begin + stream.packetQueue.Flush(); + stream.packetQueue.Put(@FlushPacket); + end; + stream.seekRequest := false; + end; + + SDL_mutexV(stream.lock); + + + if(stream.packetQueue.size > MAX_AUDIOQ_SIZE) then + begin + SDL_Delay(10); + continue; + end; + + if(av_read_frame(stream.pFormatCtx, packet) < 0) then + begin + // check for end-of-file (eof is not an error) + if(url_feof(@stream.pFormatCtx^.pb) <> 0) then + begin + eofState := true; + continue; + end; + + // check for errors + if(url_ferror(@stream.pFormatCtx^.pb) = 0) then + begin + // no error -> wait for user input + SDL_Delay(100); + continue; + end + else + begin + // an error occured -> abort + // TODO: eof or quit? + eofState := true; + continue; + end; + end; + //writeln( 'ffmpeg - av_read_frame' ); - if (packet.stream_index = stream.ffmpegStreamID) then + if(packet.stream_index = stream.ffmpegStreamIndex) then begin //writeln( 'packet_queue_put' ); stream.packetQueue.put(@packet); @@ -229,9 +370,6 @@ begin //Writeln('Done: ' + inttostr(stream.packetQueue.nbPackets)); - // signal end-of-stream - stream.packetQueue.put(@EOSPacket); - result := 0; end; @@ -242,10 +380,10 @@ var begin result := -1; - if (EOS_Flag) then + if EOF then exit; - while true do + while(true) do begin while (audio_pkt_size > 0) do begin @@ -253,6 +391,8 @@ begin data_size := bufSize; // TODO: should be avcodec_decode_audio2 but this wont link on my ubuntu box. + // FIXME: with avcodec_decode_audio a package could contain several frames + // this is not handled yet len1 := avcodec_decode_audio(pCodecCtx, @buffer, data_size, audio_pkt_data, audio_pkt_size); @@ -294,11 +434,17 @@ begin audio_pkt_data := PChar(pkt.data); audio_pkt_size := pkt.size; - // check for end-of-stream - if (audio_pkt_data = PChar(EOSPacket.data)) then + if (audio_pkt_data = PChar(FlushPacket.data)) then + begin + avcodec_flush_buffers(pCodecCtx); + continue; + end; + + // check for end-of-file + if (audio_pkt_data = PChar(EOFPacket.data)) then begin - // end-of-stream reached -> set EOS-flag - EOS_Flag := true; + // end-of-file reached -> set EOF-flag + SetEOF(true); // note: buffer is not (even partially) filled -> no data to return exit; end; @@ -316,9 +462,10 @@ var len : integer; begin len := BufSize; + result := -1; - // end-of-stream reached - if (EOS_Flag) then + // end-of-file reached + if EOF then exit; while (len > 0) do begin @@ -361,19 +508,6 @@ begin result := BufSize; end; -function TFFMpegDecodeStream.GetPosition(): real; -var - bytes: integer; -begin - Result := 0; -end; - -procedure TFFMpegDecodeStream.SetPosition(Time: real); -var - bytes: integer; -begin -end; - { TAudioDecoder_FFMpeg } @@ -388,21 +522,25 @@ begin av_register_all(); - // init end-of-stream package - av_init_packet(EOSPacket); - EOSPacket.data := Pointer(PChar('EOS')); + // init end-of-file package + av_init_packet(EOFPacket); + EOFPacket.data := Pointer(PChar('EOF')); + + // init flush package + av_init_packet(FlushPacket); + FlushPacket.data := Pointer(PChar('FLUSH')); result := true; end; -class function TAudioDecoder_FFMpeg.FindAudioStreamID(pFormatCtx : PAVFormatContext): integer; +class function TAudioDecoder_FFMpeg.FindAudioStreamIndex(pFormatCtx : PAVFormatContext): integer; var i : integer; - streamID: integer; + streamIndex: integer; stream : PAVStream; begin // Find the first audio stream - streamID := -1; + streamIndex := -1; for i := 0 to pFormatCtx^.nb_streams-1 do begin @@ -412,12 +550,12 @@ begin if ( stream.codec^.codec_type = CODEC_TYPE_AUDIO ) then begin //Log.LogStatus('Found Audio Stream', 'UAudio_FFMpeg'); - streamID := i; + streamIndex := i; break; end; end; - result := streamID; + result := streamIndex; end; function TAudioDecoder_FFMpeg.Open(const Filename: string): TAudioDecodeStream; @@ -449,11 +587,11 @@ begin dump_format(pFormatCtx, 0, pchar(Filename), 0); - ffmpegStreamID := FindAudioStreamID(pFormatCtx); + ffmpegStreamID := FindAudioStreamIndex(pFormatCtx); if (ffmpegStreamID < 0) then exit; - //Log.LogStatus('Audio Stream ID is : '+ inttostr(ffmpegStreamID), 'UAudio_FFMpeg'); + //Log.LogStatus('AudioStreamIndex is: '+ inttostr(ffmpegStreamID), 'UAudio_FFMpeg'); ffmpegStream := pFormatCtx.streams[ffmpegStreamID]; pCodecCtx := ffmpegStream^.codec; @@ -503,7 +641,7 @@ var begin result := -1; - if (pkt <> @EOSPacket) then + if ((pkt <> @EOFPacket) and (pkt <> @FlushPacket)) then if (av_dup_packet(pkt) < 0) then exit; @@ -585,6 +723,27 @@ begin end; end; +procedure TPacketQueue.Flush(); +var + pkt, pkt1: PAVPacketList; +begin + SDL_LockMutex(Self.mutex); + + pkt := Self.firstPkt; + while(pkt <> nil) do + begin + pkt1 := pkt^.next; + av_free_packet(pkt^.pkt); + av_freep(pkt); + pkt := pkt1; + end; + Self.lastPkt := nil; + Self.firstPkt := nil; + Self.nbPackets := 0; + Self.size := 0; + + SDL_UnlockMutex(Self.mutex); +end; initialization -- cgit v1.2.3 From c1a89635c888f21203db62021cb30333d5d80a47 Mon Sep 17 00:00:00 2001 From: tobigun Date: Fri, 11 Jan 2008 03:27:40 +0000 Subject: some minor changes git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/trunk@780 b956fd51-792f-4845-bead-9b4dfca2ff2c --- Game/Code/Classes/UAudioDecoder_FFMpeg.pas | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'Game/Code/Classes/UAudioDecoder_FFMpeg.pas') diff --git a/Game/Code/Classes/UAudioDecoder_FFMpeg.pas b/Game/Code/Classes/UAudioDecoder_FFMpeg.pas index c81e4be1..7ea9dd6a 100644 --- a/Game/Code/Classes/UAudioDecoder_FFMpeg.pas +++ b/Game/Code/Classes/UAudioDecoder_FFMpeg.pas @@ -271,6 +271,7 @@ var stream: TFFMpegDecodeStream; seekTarget: int64; eofState: boolean; + pbIOCtx: PByteIOContext; begin stream := TFFMpegDecodeStream(streamPtr); eofState := false; @@ -333,14 +334,19 @@ begin if(av_read_frame(stream.pFormatCtx, packet) < 0) then begin // check for end-of-file (eof is not an error) - if(url_feof(@stream.pFormatCtx^.pb) <> 0) then + {$IF (LIBAVFORMAT_VERSION >= 52)} + pbIOCtx := stream.pFormatCtx^.pb; + {$ELSE} + pbIOCtx := @stream.pFormatCtx^.pb; + {$IFEND} + if(url_feof(pbIOCtx) <> 0) then begin eofState := true; continue; end; // check for errors - if(url_ferror(@stream.pFormatCtx^.pb) = 0) then + if(url_ferror(pbIOCtx) = 0) then begin // no error -> wait for user input SDL_Delay(100); -- cgit v1.2.3 From 69af8d0951d9c741680e9c7f5c5ccd63f36b8df5 Mon Sep 17 00:00:00 2001 From: tobigun Date: Fri, 11 Jan 2008 11:50:24 +0000 Subject: FPC compatible FFMPEG versioning support. Version-numbers are in the form AAIISS now instead of AA.IISS (which is not supported by fpc) git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/trunk@783 b956fd51-792f-4845-bead-9b4dfca2ff2c --- Game/Code/Classes/UAudioDecoder_FFMpeg.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Game/Code/Classes/UAudioDecoder_FFMpeg.pas') diff --git a/Game/Code/Classes/UAudioDecoder_FFMpeg.pas b/Game/Code/Classes/UAudioDecoder_FFMpeg.pas index 7ea9dd6a..4cd6ec37 100644 --- a/Game/Code/Classes/UAudioDecoder_FFMpeg.pas +++ b/Game/Code/Classes/UAudioDecoder_FFMpeg.pas @@ -334,7 +334,7 @@ begin if(av_read_frame(stream.pFormatCtx, packet) < 0) then begin // check for end-of-file (eof is not an error) - {$IF (LIBAVFORMAT_VERSION >= 52)} + {$IF (LIBAVFORMAT_MAJOR_VERSION >= 52)} pbIOCtx := stream.pFormatCtx^.pb; {$ELSE} pbIOCtx := @stream.pFormatCtx^.pb; -- cgit v1.2.3 From 89ac41a2ccb85d6dfccb501ff4877231cab72094 Mon Sep 17 00:00:00 2001 From: tobigun Date: Tue, 5 Feb 2008 13:43:38 +0000 Subject: Update of the ffmpeg headers to their current trunk versions. IMPORTANT: parameter of av_free_packet is a pointer now procedure av_free_packet (pkt: PAVPacket); as it is with av_free() and av_freep() git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/trunk@814 b956fd51-792f-4845-bead-9b4dfca2ff2c --- Game/Code/Classes/UAudioDecoder_FFMpeg.pas | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'Game/Code/Classes/UAudioDecoder_FFMpeg.pas') diff --git a/Game/Code/Classes/UAudioDecoder_FFMpeg.pas b/Game/Code/Classes/UAudioDecoder_FFMpeg.pas index 4cd6ec37..c705d603 100644 --- a/Game/Code/Classes/UAudioDecoder_FFMpeg.pas +++ b/Game/Code/Classes/UAudioDecoder_FFMpeg.pas @@ -45,7 +45,8 @@ uses {$ENDIF} UIni, UMain, - UThemes; + UThemes, + UConfig; type @@ -334,7 +335,7 @@ begin if(av_read_frame(stream.pFormatCtx, packet) < 0) then begin // check for end-of-file (eof is not an error) - {$IF (LIBAVFORMAT_MAJOR_VERSION >= 52)} + {$IF (LIBAVFORMAT_VERSION_MAJOR >= 52)} pbIOCtx := stream.pFormatCtx^.pb; {$ELSE} pbIOCtx := @stream.pFormatCtx^.pb; @@ -370,7 +371,7 @@ begin end else begin - av_free_packet(packet); + av_free_packet(@packet); end; end; @@ -428,7 +429,7 @@ begin if (pkt.data <> nil) then begin - av_free_packet(pkt); + av_free_packet(@pkt); end; if (packetQueue.quit) then @@ -739,7 +740,7 @@ begin while(pkt <> nil) do begin pkt1 := pkt^.next; - av_free_packet(pkt^.pkt); + av_free_packet(@pkt^.pkt); av_freep(pkt); pkt := pkt1; end; -- cgit v1.2.3 From dee94f5dae9e6b5ae6c7b54a12a567745abc8dc3 Mon Sep 17 00:00:00 2001 From: tobigun Date: Tue, 5 Feb 2008 20:36:19 +0000 Subject: General: - cleanup and adaption of SingDrawOscilloscope Portaudio/SDL audio output: - stuttering in portaudio output has been fixed (SDL_MixBuffers cannot be used without initializing the SDL audio stuff first, so it is not usable with portaudio. Now SDL is used for audio-output instead of portaudio (although the file-name is UAudioPlayback_Portaudio.pas at the moment). - cleaner file closing - volume adjustment UMusic: - cleanup of the audio-interfaces - introduced TNoteType = (ntFreestyle, ntNormal, ntGolden) - some bug-fixes - introduced TSoundLibrary. This is library for all in-game sounds used by USDX. Instead of calling AudioPlayer.PlaySwoosh you should call AudioPlayer.PlaySound(SoundLib.Swoosh) now. You might call SoundLib.Swoosh.Play too, but this is not recommended at the moment because SoundLib.Swoosh could be nil if the file was not found. The SoundLibrary approach is much cleaner than the previous one. The AudioPlayer does not have to specify a Play... and Stop... method for every available sound anymore. In addition it is not an AudioPlayers responsibility to init the in-game sounds. URecord: - polish to english translation of some variables - CaptureSoundLeft/Right is CaptureChannel[0/1] now - TSoundCardInput -> TAudioInputDeviceSource - TGenericSoundCard.Input -> TGenericSoundCard.Source - autocorrelation algorithm more readable now - Clean-up of the audio-input interface - moved cloned code of the input-classes to one base class (TAudioInputBase) - Cleaner finalization - Start-/StopCapture will not crash anymore in the recording-options menu - Fixed several bugs in the autocorrelation stuff (e.g. wrong usage of $10000) - SzczytJest (now ToneValid) was not used correctly. ToneValid is set to true if a valid tone was found (= the sound was louder than the threshold -> no background noise). If i remember correctly the sound was accepted although the tone was invalid. So the old data was used although noone was singing. This resulted in some sort of ghost-singer effect. UIni: - moved TIni.Card to TScreenOptionsRecord.Card because it is not stored in the ini-file and will not be in the future. - TIni.CardList ist now TIni.InputDeviceConfig. The name cardlist was misleading because it just specifies input- but no output-devices. In addition a soundcard can have multiple input-devices (at least in linux). - bugfix on InputDeviceConfig (formerly CardList) usage. USDX expected that the indices of the corresponding elements in TIni.InputDeviceConfig[] and TAudioInputProcessor.Device[] were the same. This is wrong. If device 2 was defined at first place in the ini and device 1 at the second, the indices of the two arrays didn't match (they were swapped) erroneously. To fix this and to support the item listed below the index to TIni.InputDeviceConfig[] is now stored in TAudioInputDevice.CfgIndex. NOTE: InputDeviceConfig[] contains configurations of non-available (unplugged) devices. Iterate over TAudioInputProcessor.Device[] for available devices. - configurations of external devices that are not plugged in will not be deleted anymore. - multiple definitions of one device in the ini-file will not crash USDX anymore - CardList[I].ChannelL/R now are InputDeviceConfig[I].ChannelToPlayerMap[0/1]. I think the new name is more intuitive because it maps a channel to a player number. Now the both vars are joint to one array. Now it is possible to use loops to process them and we might support more than two input channels on one device in the future (if such devices exist) git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/trunk@827 b956fd51-792f-4845-bead-9b4dfca2ff2c --- Game/Code/Classes/UAudioDecoder_FFMpeg.pas | 197 +++++++++++++++-------------- 1 file changed, 101 insertions(+), 96 deletions(-) (limited to 'Game/Code/Classes/UAudioDecoder_FFMpeg.pas') diff --git a/Game/Code/Classes/UAudioDecoder_FFMpeg.pas b/Game/Code/Classes/UAudioDecoder_FFMpeg.pas index c705d603..646e9eef 100644 --- a/Game/Code/Classes/UAudioDecoder_FFMpeg.pas +++ b/Game/Code/Classes/UAudioDecoder_FFMpeg.pas @@ -17,36 +17,33 @@ interface {$MODE Delphi} {$ENDIF} -{$I switches.inc} - - -uses Classes, - {$IFDEF win32} - windows, - {$ENDIF} - SysUtils, - SDL, - avcodec, // FFMpeg Audio file decoding - avformat, - avutil, - avio, // used for url_ferror - mathematics, // used for av_rescale_q - ULog, - UMusic; +{$I ../switches.inc} + + +uses + Classes, + {$IFDEF win32} + windows, + {$ENDIF} + SysUtils, + UMusic; implementation uses - {$IFDEF LAZARUS} - lclintf, - {$ifndef win32} - libc, - {$endif} - {$ENDIF} - UIni, - UMain, - UThemes, - UConfig; + {$ifndef win32} + libc, + {$endif} + UIni, + UMain, + avcodec, // FFMpeg Audio file decoding + avformat, + avutil, + avio, // used for url_ferror + mathematics, // used for av_rescale_q + SDL, + ULog, + UConfig; type @@ -106,12 +103,12 @@ type ffmpegStreamIndex : Integer; ffmpegStream : PAVStream; - // "static" vars for DecodeFrame + // state-vars for DecodeFrame pkt : TAVPacket; audio_pkt_data : PChar; audio_pkt_size : integer; - // "static" vars for AudioCallback + // state-vars for AudioCallback audio_buf_index : cardinal; audio_buf_size : cardinal; audio_buf : TAudioBuffer; @@ -127,8 +124,7 @@ type procedure Close(); override; function GetLength(): real; override; - function GetChannelCount(): cardinal; override; - function GetSampleRate(): cardinal; override; + function GetAudioFormatInfo(): TAudioFormatInfo; override; function GetPosition: real; override; procedure SetPosition(Time: real); override; function IsEOF(): boolean; override; @@ -169,7 +165,7 @@ begin audio_buf_index := 0; audio_buf_size := 0; - FillChar(pkt, sizeof(TAVPacket), #0); + FillChar(pkt, sizeof(TAVPacket), 0); Self.pFormatCtx := pFormatCtx; Self.pCodecCtx := pCodecCtx; @@ -221,14 +217,12 @@ begin result := pFormatCtx^.duration / AV_TIME_BASE; end; -function TFFMpegDecodeStream.GetChannelCount(): cardinal; -begin - result := pCodecCtx^.channels; -end; - -function TFFMpegDecodeStream.GetSampleRate(): cardinal; +function TFFMpegDecodeStream.GetAudioFormatInfo(): TAudioFormatInfo; begin - result := pCodecCtx^.sample_rate; + result.Channels := pCodecCtx^.channels; + result.SampleRate := pCodecCtx^.sample_rate; + //result.Format := pCodecCtx^.sample_fmt; // sample_fmt not yet used by FFMpeg + result.Format := asfS16; // use FFMpeg's standard format end; function TFFMpegDecodeStream.IsEOF(): boolean; @@ -260,7 +254,7 @@ begin SDL_mutexP(lock); seekPos := Trunc(Time * AV_TIME_BASE); // FIXME: seek_flags = rel < 0 ? AVSEEK_FLAG_BACKWARD : 0 - seekFlags := 0; + seekFlags := 0;//AVSEEK_FLAG_BACKWARD; seekRequest := true; SDL_CondSignal(resumeCond); SDL_mutexV(lock); @@ -279,9 +273,9 @@ begin while (true) do begin + //SafeWriteLn('Hallo'); SDL_mutexP(stream.lock); - // wait if end-of-file reached if (eofState) then begin @@ -309,18 +303,19 @@ begin // TODO: Do we need this? // The position is converted to AV_TIME_BASE and then to the stream-specific base. // Why not convert to the stream-specific one from the beginning. - seekTarget := av_rescale_q(stream.seekPos, AV_TIME_BASE_Q, stream.ffmpegStream^.time_base); - if(av_seek_frame(stream.pFormatCtx, stream.ffmpegStreamIndex, - seekTarget, stream.seekFlags) = 0) then - begin - Log.LogStatus(stream.pFormatCtx^.filename + ': error while seeking', 'UAudioDecoder_FFMpeg'); - end - else - begin - stream.packetQueue.Flush(); - stream.packetQueue.Put(@FlushPacket); - end; - stream.seekRequest := false; + seekTarget := av_rescale_q(stream.seekPos, AV_TIME_BASE_Q, stream.ffmpegStream^.time_base); + if(av_seek_frame(stream.pFormatCtx, stream.ffmpegStreamIndex, + seekTarget, stream.seekFlags) < 0) then + begin + // this will crash in FPC due to a bug + //Log.LogStatus({stream.pFormatCtx^.filename +} ': error while seeking', 'UAudioDecoder_FFMpeg'); + end + else + begin + stream.packetQueue.Flush(); + stream.packetQueue.Put(@FlushPacket); + end; + stream.seekRequest := false; end; SDL_mutexV(stream.lock); @@ -340,8 +335,10 @@ begin {$ELSE} pbIOCtx := @stream.pFormatCtx^.pb; {$IFEND} + if(url_feof(pbIOCtx) <> 0) then begin + SafeWriteLn('feof'); eofState := true; continue; end; @@ -349,24 +346,25 @@ begin // check for errors if(url_ferror(pbIOCtx) = 0) then begin + SafeWriteLn('Errorf'); // no error -> wait for user input - SDL_Delay(100); - continue; - end - else - begin - // an error occured -> abort - // TODO: eof or quit? - eofState := true; - continue; - end; - end; - - //writeln( 'ffmpeg - av_read_frame' ); + SDL_Delay(100); + continue; + end + else + begin + // an error occured -> abort + // TODO: eof or quit? + eofState := true; + continue; + end; + end; + + //SafeWriteLn( 'ffmpeg - av_read_frame' ); if(packet.stream_index = stream.ffmpegStreamIndex) then begin - //writeln( 'packet_queue_put' ); + //SafeWriteLn( 'packet_queue_put' ); stream.packetQueue.put(@packet); end else @@ -375,7 +373,7 @@ begin end; end; - //Writeln('Done: ' + inttostr(stream.packetQueue.nbPackets)); + SafeWriteLn('Done: ' + inttostr(stream.packetQueue.nbPackets)); result := 0; end; @@ -394,21 +392,25 @@ begin begin while (audio_pkt_size > 0) do begin - //writeln( 'got audio packet' ); + //SafeWriteLn( 'got audio packet' ); data_size := bufSize; - // TODO: should be avcodec_decode_audio2 but this wont link on my ubuntu box. + {$IF LIBAVCODEC_VERSION >= 51030000} // 51.30.0 + len1 := avcodec_decode_audio2(pCodecCtx, @buffer, + data_size, audio_pkt_data, audio_pkt_size); + {$ELSE} // FIXME: with avcodec_decode_audio a package could contain several frames // this is not handled yet len1 := avcodec_decode_audio(pCodecCtx, @buffer, data_size, audio_pkt_data, audio_pkt_size); + {$IFEND} - //writeln('avcodec_decode_audio : ' + inttostr( len1 )); + //SafeWriteLn('avcodec_decode_audio : ' + inttostr( len1 )); if(len1 < 0) then begin - // if error, skip frame - //writeln( 'Skip audio frame' ); + // if error, skip frame + SafeWriteLn( 'Skip audio frame' ); audio_pkt_size := 0; break; end; @@ -444,6 +446,7 @@ begin if (audio_pkt_data = PChar(FlushPacket.data)) then begin avcodec_flush_buffers(pCodecCtx); + SafeWriteLn('Flush'); continue; end; @@ -452,11 +455,12 @@ begin begin // end-of-file reached -> set EOF-flag SetEOF(true); + SafeWriteLn('EOF'); // note: buffer is not (even partially) filled -> no data to return exit; end; - //writeln( 'Audio Packet Size - ' + inttostr(audio_pkt_size) ); + //SafeWriteLn( 'Audio Packet Size - ' + inttostr(audio_pkt_size) ); end; end; @@ -480,14 +484,14 @@ begin begin // We have already sent all our data; get more audio_size := DecodeFrame(audio_buf, sizeof(TAudioBuffer)); - //writeln('audio_decode_frame : '+ inttostr(audio_size)); + //SafeWriteLn('audio_decode_frame : '+ inttostr(audio_size)); if(audio_size < 0) then begin // If error, output silence audio_buf_size := 1024; FillChar(audio_buf, audio_buf_size, #0); - //writeln( 'Silence' ); + //SafeWriteLn( 'Silence' ); end else begin @@ -531,11 +535,11 @@ begin // init end-of-file package av_init_packet(EOFPacket); - EOFPacket.data := Pointer(PChar('EOF')); + EOFPacket.data := Pointer(PChar('EOF')); // init flush package av_init_packet(FlushPacket); - FlushPacket.data := Pointer(PChar('FLUSH')); + FlushPacket.data := Pointer(PChar('FLUSH')); result := true; end; @@ -611,7 +615,7 @@ begin end; avcodec_open(pCodecCtx, pCodec); - //writeln( 'Opened the codec' ); + //WriteLn( 'Opened the codec' ); stream := TFFMpegDecodeStream.Create(pFormatCtx, pCodecCtx, pCodec, ffmpegStreamID, ffmpegStream); @@ -671,7 +675,7 @@ begin Self.lastPkt := pkt1; inc(Self.nbPackets); - //Writeln('Put: ' + inttostr(nbPackets)); + //SafeWriteLn('Put: ' + inttostr(nbPackets)); Self.size := Self.size + pkt1^.pkt.size; SDL_CondSignal(Self.cond); @@ -705,7 +709,7 @@ begin Self.lastPkt := nil; dec(Self.nbPackets); - //Writeln('Get: ' + inttostr(nbPackets)); + //SafeWriteLn('Get: ' + inttostr(nbPackets)); Self.size := Self.size - pkt1^.pkt.size; pkt := pkt1^.pkt; @@ -734,22 +738,23 @@ procedure TPacketQueue.Flush(); var pkt, pkt1: PAVPacketList; begin - SDL_LockMutex(Self.mutex); - - pkt := Self.firstPkt; - while(pkt <> nil) do - begin - pkt1 := pkt^.next; - av_free_packet(@pkt^.pkt); - av_freep(pkt); - pkt := pkt1; - end; - Self.lastPkt := nil; - Self.firstPkt := nil; - Self.nbPackets := 0; - Self.size := 0; - - SDL_UnlockMutex(Self.mutex); + SDL_LockMutex(Self.mutex); + + pkt := Self.firstPkt; + while(pkt <> nil) do + begin + pkt1 := pkt^.next; + av_free_packet(@pkt^.pkt); + // Note: param must be a pointer to a pointer! + av_freep(@pkt); + pkt := pkt1; + end; + Self.lastPkt := nil; + Self.firstPkt := nil; + Self.nbPackets := 0; + Self.size := 0; + + SDL_UnlockMutex(Self.mutex); end; -- cgit v1.2.3