diff options
-rw-r--r-- | Game/Code/Classes/UAudio_FFMpeg.pas | 998 |
1 files changed, 462 insertions, 536 deletions
diff --git a/Game/Code/Classes/UAudio_FFMpeg.pas b/Game/Code/Classes/UAudio_FFMpeg.pas index b89fa00f..f050eaea 100644 --- a/Game/Code/Classes/UAudio_FFMpeg.pas +++ b/Game/Code/Classes/UAudio_FFMpeg.pas @@ -36,36 +36,6 @@ uses Classes, ULog, UMusic; -type - TPacketQueue = record - first_pkt , - last_pkt : pAVPacketList; - nb_packets : integer; - size : integer; - mutex : pSDL_mutex; - cond : pSDL_cond; - end; - pPacketQueue = ^TPacketQueue; - - function packet_queue_put(var aPacketQueue : TPacketQueue; var AVPacket : TAVPacket): integer; - function packet_queue_get(var aPacketQueue : TPacketQueue; var AVPacket : TAVPacket; block : integer ): integer; - procedure packet_queue_init( var aPacketQueue : TPacketQueue ); - procedure audio_callback( userdata: Pointer; stream: PUInt8; len: Integer ); cdecl; - function audio_decode_frame(aCodecCtx : TAVCodecContext; aAudio_buf : PUInt8; buf_size: integer): integer; - -var - singleton_MusicFFMpeg : IAudioPlayback = nil; - -var - audioq : TPacketQueue; - quit : integer = 0; -// faudio_buf : array[ 0 .. 0 ] of byte; //pUInt8{$ifndef fpc};{$else} = nil;{$endif} -// audio_buf : array[ 0 .. AVCODEC_MAX_AUDIO_FRAME_SIZE ] of byte; //pUInt8{$ifndef fpc};{$else} = nil;{$endif} - -type - Taudiobuff = array[ 0 .. AVCODEC_MAX_AUDIO_FRAME_SIZE-1 ] of byte; - PAudioBuff = ^Taudiobuff; - implementation uses @@ -73,55 +43,104 @@ uses lclintf, libc, {$ENDIF} -// URecord, UIni, UMain, UThemes; -//var -// singleton_MusicFFMpeg : IAudioPlayback = nil; +type + PPacketQueue = ^TPacketQueue; + TPacketQueue = class + private + firstPkt, + lastPkt : PAVPacketList; + nbPackets : integer; + size : integer; + mutex : PSDL_Mutex; + cond : PSDL_Cond; + quit : boolean; -const - RecordSystem = 1; - SDL_AUDIO_BUFFER_SIZE = 1024; - + public + constructor Create(); + + function Put(pkt : PAVPacket): integer; + function Get(var pkt: TAVPacket; block: boolean): integer; + end; type - TMPModes = (mpNotReady, mpStopped, mpPlaying, mpRecording, mpSeeking, mpPaused, mpOpen); + TStreamStatus = (sNotReady, sStopped, sPlaying, sSeeking, sPaused, sOpen); +const + StreamStatusStr: array[TStreamStatus] of string = ('Not ready', 'Stopped', 'Playing', 'Seeking', 'Paused', 'Open'); +type + PAudioBuffer = ^TAudioBuffer; + TAudioBuffer = array[0 .. (AVCODEC_MAX_AUDIO_FRAME_SIZE * 3 div 2)-1] of byte; const - ModeStr: array[TMPModes] of string = ('Not ready', 'Stopped', 'Playing', 'Recording', 'Seeking', 'Paused', 'Open'); + SDL_AUDIO_BUFFER_SIZE = 1024; + +type + TFFMpegOutputStream = class(TAudioOutputStream) + private + parseThread: PSDL_Thread; + packetQueue: TPacketQueue; + + status: TStreamStatus; + + // FFMpeg internal data + pFormatCtx : PAVFormatContext; + pCodecCtx : PAVCodecContext; + pCodec : PAVCodec; + ffmpegStreamID : Integer; + ffmpegStream : PAVStream; + + // static vars for AudioDecodeFrame + 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; + public + constructor Create(); overload; + constructor Create(pFormatCtx: PAVFormatContext; + pCodecCtx: PAVCodecContext; pCodec: PAVCodec; + ffmpegStreamID : Integer; ffmpegStream: PAVStream); overload; + procedure Play(); + procedure Pause(); + procedure Stop(); + procedure Close(); + function AudioDecodeFrame(buffer : PUInt8; bufSize: integer): integer; + end; type - TAudio_ffMpeg = class( TInterfacedObject, IAudioPlayback ) + TAudio_FFMpeg = class( TInterfacedObject, IAudioPlayback ) private - - BassStart: hStream; - BassBack: hStream; - BassSwoosh: hStream; - BassChange: hStream; - BassOption: hStream; - BassClick: hStream; - BassDrum: hStream; - BassHihat: hStream; - BassClap: hStream; - BassShuffle: hStream; + MusicStream: TFFMpegOutputStream; + + StartSoundStream: TFFMpegOutputStream; + BackSoundStream: TFFMpegOutputStream; + SwooshSoundStream: TFFMpegOutputStream; + ChangeSoundStream: TFFMpegOutputStream; + OptionSoundStream: TFFMpegOutputStream; + ClickSoundStream: TFFMpegOutputStream; + DrumSoundStream: TFFMpegOutputStream; + HihatSoundStream: TFFMpegOutputStream; + ClapSoundStream: TFFMpegOutputStream; + ShuffleSoundStream: TFFMpegOutputStream; //Custom Sounds CustomSounds: array of TCustomSoundEntry; Loaded: boolean; Loop: boolean; - fHWND: THandle; - function find_stream_ids( const aFormatCtx : PAVFormatContext; Out aFirstVideoStream, aFirstAudioStream : integer ): boolean; + function FindAudioStreamID(pFormatCtx : PAVFormatContext): integer; public -// Bass: hStream; - constructor create(); function GetName: String; procedure InitializePlayback; procedure SetVolume(Volume: integer); @@ -148,105 +167,120 @@ type procedure PlayClap; procedure PlayShuffle; procedure StopShuffle; -// procedure CaptureStart; -// procedure CaptureStop; -// procedure CaptureCard(RecordI, PlayerLeft, PlayerRight: byte); - procedure StopCard(Card: byte); - function LoadSoundFromFile(var hStream: hStream; Name: string): boolean; + + function LoadSoundFromFile(Name: string): TFFMpegOutputStream; //Equalizer function GetFFTData: TFFTData; + // Interface for Visualizer + function GetPCMData(var data: TPCMData): Cardinal; + //Custom Sounds function LoadCustomSound(const Filename: String): Cardinal; procedure PlayCustomSound(const Index: Cardinal ); -end; + end; -constructor TAudio_ffMpeg.create(); +var + singleton_MusicFFMpeg : IAudioPlayback = nil; + + +function ParseAudio(data: Pointer): integer; cdecl; forward; +procedure AudioCallback( userdata: Pointer; stream: PUInt8; len: Integer ); cdecl; forward; + +constructor TFFMpegOutputStream.Create(); begin -// writeln( 'UVideo_FFMpeg - av_register_all' ); - av_register_all; + inherited; + + packetQueue := TPacketQueue.Create(); + + FillChar(pkt, sizeof(TAVPacket), #0); + + status := sStopped; + + audio_pkt_data := nil; + audio_pkt_size := 0; + + audio_buf_index := 0; + audio_buf_size := 0; end; -function TAudio_ffMpeg.find_stream_ids( const aFormatCtx : PAVFormatContext; Out aFirstVideoStream, aFirstAudioStream : integer ): boolean; -var - i : integer; - st : pAVStream; +constructor TFFMpegOutputStream.Create(pFormatCtx: PAVFormatContext; + pCodecCtx: PAVCodecContext; pCodec: PAVCodec; + ffmpegStreamID : Integer; ffmpegStream: PAVStream); begin - // Find the first video stream - aFirstAudioStream := -1; - aFirstVideoStream := -1; + Create(); + Self.pFormatCtx := pFormatCtx; + Self.pCodecCtx := pCodecCtx; + Self.pCodec := pCodec; + Self.ffmpegStreamID := ffmpegStreamID; + Self.ffmpegStream := ffmpegStream; +end; - i := 0; - while ( i < aFormatCtx.nb_streams ) do +procedure TFFMpegOutputStream.Play(); +begin + writeln('Play request'); + if(status = sStopped) then begin -// writeln( ' aFormatCtx.streams[i] : ' + inttostr( i ) ); - st := aFormatCtx.streams[i]; + writeln('Play ok'); + status := sPlaying; + Self.parseThread := SDL_CreateThread(@ParseAudio, Self); + SDL_PauseAudio(0); + end; +end; - if(st.codec.codec_type = CODEC_TYPE_VIDEO ) AND - (aFirstVideoStream < 0) THEN - begin -// writeln( 'Found Video Stream' ); - aFirstVideoStream := i; - end; +procedure TFFMpegOutputStream.Pause(); +begin +end; - if ( st.codec.codec_type = CODEC_TYPE_AUDIO ) AND - ( aFirstAudioStream < 0) THEN - begin -// writeln( 'Found Audio Stream' ); - aFirstAudioStream := i; - end; +procedure TFFMpegOutputStream.Stop(); +begin +end; - inc( i ); - end; // while +procedure TFFMpegOutputStream.Close(); +begin + // Close the codec + avcodec_close(pCodecCtx); - result := (aFirstAudioStream > -1) OR - (aFirstVideoStream > -1) ; // Didn't find any streams stream + // Close the video file + av_close_input_file(pFormatCtx); end; -function TAudio_ffMpeg.GetName: String; +function TAudio_FFMpeg.GetName: String; begin result := 'FFMpeg'; end; -procedure TAudio_ffMpeg.InitializePlayback; -var -// Pet: integer; - S: integer; +procedure TAudio_FFMpeg.InitializePlayback; begin + Log.LogStatus('InitializePlayback', 'UAudio_FFMpeg'); -// LoadSoundFromFile(BassStart, SoundPath + 'Green Day - American Idiot.mp3'); - -(* - LoadSoundFromFile(BassStart, SoundPath + 'Common start.mp3'); - LoadSoundFromFile(BassBack, SoundPath + 'Common back.mp3'); - LoadSoundFromFile(BassSwoosh, SoundPath + 'menu swoosh.mp3'); - LoadSoundFromFile(BassChange, SoundPath + 'select music change music 50.mp3'); - LoadSoundFromFile(BassOption, SoundPath + 'option change col.mp3'); - LoadSoundFromFile(BassClick, SoundPath + 'rimshot022b.mp3'); - - LoadSoundFromFile(BassDrum, SoundPath + 'bassdrumhard076b.mp3'); - LoadSoundFromFile(BassHihat, SoundPath + 'hihatclosed068b.mp3'); - LoadSoundFromFile(BassClap, SoundPath + 'claps050b.mp3'); -*) + Loaded := false; + Loop := false; -// LoadSoundFromFile(BassShuffle, SoundPath + 'Shuffle.mp3'); + av_register_all(); + SDL_Init(SDL_INIT_AUDIO); -// Log.BenchmarkEnd(4); -// Log.LogBenchmark('--> Loading Sounds', 4); + StartSoundStream := LoadSoundFromFile(SoundPath + 'Common Start.mp3'); + { + BackSoundStream := LoadSoundFromFile(SoundPath + 'Common Back.mp3'); + SwooshSoundStream := LoadSoundFromFile(SoundPath + 'menu swoosh.mp3'); + ChangeSoundStream := LoadSoundFromFile(SoundPath + 'select music change music 50.mp3'); + OptionSoundStream := LoadSoundFromFile(SoundPath + 'option change col.mp3'); + ClickSoundStream := LoadSoundFromFile(SoundPath + 'rimshot022b.mp3'); + } +// DrumSoundStream := LoadSoundFromFile(SoundPath + 'bassdrumhard076b.mp3'); +// HihatSoundStream := LoadSoundFromFile(SoundPath + 'hihatclosed068b.mp3'); +// ClapSoundStream := LoadSoundFromFile(SoundPath + 'claps050b.mp3'); +// ShuffleSoundStream := LoadSoundFromFile(SoundPath + 'Shuffle.mp3'); end; -procedure TAudio_ffMpeg.SetVolume(Volume: integer); +procedure TAudio_FFMpeg.SetVolume(Volume: integer); begin - //Old Sets Wave Volume - //BASS_SetVolume(Volume); //New: Sets Volume only for this Application - - - // TODO : jb_linux replace with something other than bass (* BASS_SetConfig(BASS_CONFIG_GVOL_SAMPLE, Volume); BASS_SetConfig(BASS_CONFIG_GVOL_STREAM, Volume); @@ -254,7 +288,7 @@ begin *) end; -procedure TAudio_ffMpeg.SetMusicVolume(Volume: Integer); +procedure TAudio_FFMpeg.SetMusicVolume(Volume: Integer); begin //Max Volume Prevention if Volume > 100 then @@ -265,112 +299,102 @@ begin //Set Volume - // TODO : jb_linux replace with something other than bass // BASS_ChannelSetAttributes (Bass, -1, Volume, -101); end; -procedure TAudio_ffMpeg.SetLoop(Enabled: boolean); +procedure TAudio_FFMpeg.SetLoop(Enabled: boolean); begin Loop := Enabled; end; -function TAudio_ffMpeg.Open(Name: string): boolean; +function TAudio_FFMpeg.Open(Name: string): boolean; begin Loaded := false; if FileExists(Name) then begin - // TODO : jb_linux replace with something other than bass // Bass := Bass_StreamCreateFile(false, pchar(Name), 0, 0, 0); - + Loaded := true; //Set Max Volume -// SetMusicVolume (100); + SetMusicVolume (100); end; Result := Loaded; end; -procedure TAudio_ffMpeg.Rewind; +procedure TAudio_FFMpeg.Rewind; begin if Loaded then begin end; end; -procedure TAudio_ffMpeg.MoveTo(Time: real); +procedure TAudio_FFMpeg.MoveTo(Time: real); var bytes: integer; begin - // TODO : jb_linux replace with something other than bass // bytes := BASS_ChannelSeconds2Bytes(Bass, Time); // BASS_ChannelSetPosition(Bass, bytes); end; -procedure TAudio_ffMpeg.Play; +procedure TAudio_FFMpeg.Play; begin -(* - // TODO : jb_linux replace with something other than bass + if MusicStream <> nil then if Loaded then begin if Loop then - BASS_ChannelPlay(Bass, True); // start from beginning... actually bass itself does not loop, nor does this TAudio_ffMpeg Class - - BASS_ChannelPlay(Bass, False); // for setting position before playing + begin + end; + // start from beginning... + // actually bass itself does not loop, nor does this TAudio_FFMpeg Class + MusicStream.Play(); end; -*) end; -procedure TAudio_ffMpeg.Pause; //Pause Mod +procedure TAudio_FFMpeg.Pause; //Pause Mod begin -(* - // TODO : jb_linux replace with something other than bass + if MusicStream <> nil then if Loaded then begin - BASS_ChannelPause(Bass); // Pauses Song + MusicStream.Pause(); // Pauses Song end; -*) end; -procedure TAudio_ffMpeg.Stop; +procedure TAudio_FFMpeg.Stop; begin -// Bass_ChannelStop(Bass); + if MusicStream <> nil then + MusicStream.Stop(); end; -procedure TAudio_ffMpeg.Close; +procedure TAudio_FFMpeg.Close; begin -// Bass_StreamFree(Bass); + if MusicStream <> nil then + MusicStream.Close(); end; -function TAudio_ffMpeg.Length: real; +function TAudio_FFMpeg.Length: real; var - bytes: integer; + bytes: integer; begin - Result := 60; -(* - // TODO : jb_linux replace with something other than bass - bytes := BASS_ChannelGetLength(Bass); - Result := BASS_ChannelBytes2Seconds(Bass, bytes); -*) + Result := MusicStream.pFormatCtx^.duration / AV_TIME_BASE; end; -function TAudio_ffMpeg.getPosition: real; +function TAudio_FFMpeg.getPosition: real; var - bytes: integer; + bytes: integer; begin Result := 0; (* - // TODO : jb_linux replace with something other than bass bytes := BASS_ChannelGetPosition(BASS); Result := BASS_ChannelBytes2Seconds(BASS, bytes); *) end; -function TAudio_ffMpeg.Finished: boolean; +function TAudio_FFMpeg.Finished: boolean; begin Result := false; (* - // TODO : jb_linux replace with something other than bass if BASS_ChannelIsActive(BASS) = BASS_ACTIVE_STOPPED then begin Result := true; @@ -378,215 +402,166 @@ begin *) end; -procedure TAudio_ffMpeg.PlayStart; +procedure TAudio_FFMpeg.PlayStart; begin - // LoadSoundFromFile(BassStart, SoundPath + 'foo fighters - best of you.mp3'); - - // TODO : jb_linux replace with something other than bass -// BASS_ChannelPlay(BassStart, True); + if StartSoundStream <> nil then + StartSoundStream.Play(); end; -procedure TAudio_ffMpeg.PlayBack; +procedure TAudio_FFMpeg.PlayBack; begin - // TODO : jb_linux replace with something other than bass -// BASS_ChannelPlay(BassBack, True);// then + if BackSoundStream <> nil then + BackSoundStream.Play(); end; -procedure TAudio_ffMpeg.PlaySwoosh; +procedure TAudio_FFMpeg.PlaySwoosh; begin - // TODO : jb_linux replace with something other than bass -// BASS_ChannelPlay(BassSwoosh, True); - - + if SwooshSoundStream <> nil then + SwooshSoundStream.Play(); end; -procedure TAudio_ffMpeg.PlayChange; +procedure TAudio_FFMpeg.PlayChange; begin - // TODO : jb_linux replace with something other than bass -// BASS_ChannelPlay(BassChange, True); + if ChangeSoundStream <> nil then + ChangeSoundStream.Play(); end; -procedure TAudio_ffMpeg.PlayOption; +procedure TAudio_FFMpeg.PlayOption; begin - // TODO : jb_linux replace with something other than bass -// BASS_ChannelPlay(BassOption, True); + if OptionSoundStream <> nil then + OptionSoundStream.Play(); end; -procedure TAudio_ffMpeg.PlayClick; +procedure TAudio_FFMpeg.PlayClick; begin - // TODO : jb_linux replace with something other than bass -// BASS_ChannelPlay(BassClick, True); + if ClickSoundStream <> nil then + ClickSoundStream.Play(); end; -procedure TAudio_ffMpeg.PlayDrum; +procedure TAudio_FFMpeg.PlayDrum; begin - // TODO : jb_linux replace with something other than bass -// BASS_ChannelPlay(BassDrum, True); + if DrumSoundStream <> nil then + DrumSoundStream.Play(); end; -procedure TAudio_ffMpeg.PlayHihat; +procedure TAudio_FFMpeg.PlayHihat; begin - // TODO : jb_linux replace with something other than bass -// BASS_ChannelPlay(BassHihat, True); + if HihatSoundStream <> nil then + HihatSoundStream.Play(); end; -procedure TAudio_ffMpeg.PlayClap; +procedure TAudio_FFMpeg.PlayClap; begin - // TODO : jb_linux replace with something other than bass -// BASS_ChannelPlay(BassClap, True); + if ClapSoundStream <> nil then + ClapSoundStream.Play(); end; -procedure TAudio_ffMpeg.PlayShuffle; +procedure TAudio_FFMpeg.PlayShuffle; begin - // TODO : jb_linux replace with something other than bass -// BASS_ChannelPlay(BassShuffle, True); + if ShuffleSoundStream <> nil then + ShuffleSoundStream.Play(); end; -procedure TAudio_ffMpeg.StopShuffle; +procedure TAudio_FFMpeg.StopShuffle; begin - // TODO : jb_linux replace with something other than bass -// BASS_ChannelStop(BassShuffle); + if ShuffleSoundStream <> nil then + ShuffleSoundStream.Stop(); end; -procedure TAudio_ffMpeg.StopCard(Card: byte); -begin - - // TODO : jb_linux replace with something other than bass -// BASS_RecordSetDevice(Card); -// BASS_RecordFree; -end; - -function audio_decode_frame(aCodecCtx : TAVCodecContext; aAudio_buf : PUInt8; buf_size: integer): integer; +function TFFMpegOutputStream.AudioDecodeFrame(buffer : PUInt8; bufSize: integer): integer; var - pkt : TAVPacket; - audio_pkt_data : pchar;//PUInt8 = nil; - audio_pkt_size : integer; - len1 , - data_size : integer; + len1, + data_size: integer; begin result := -1; -(* - {$ifdef win32} - FillChar(pkt, sizeof(TAVPacket), #0); - {$else} - memset(@pkt, 0, sizeof(TAVPacket)); // todo : jb memset - {$endif} - - audio_pkt_data := nil; - audio_pkt_size := 0; + if (buffer = nil) then + exit; while true do - begin - - while ( audio_pkt_size > 0 ) do + begin + while (audio_pkt_size > 0) do begin // writeln( 'got audio packet' ); - data_size := buf_size; - - len1 := -1; + data_size := bufSize; - if aAudio_buf <> nil then - begin - len1 := avcodec_decode_audio(@aCodecCtx, Pointer( aAudio_buf ), data_size, audio_pkt_data, audio_pkt_size); // Todo.. should be avcodec_decode_audio2 but this wont link on my ubuntu box. - end; + // TODO: should be avcodec_decode_audio2 but this wont link on my ubuntu box. + len1 := avcodec_decode_audio(pCodecCtx, Pointer(buffer), + data_size, audio_pkt_data, audio_pkt_size); -// writeln('avcodec_decode_audio : ' + inttostr( len1 )); + writeln('avcodec_decode_audio : ' + inttostr( len1 )); if(len1 < 0) then begin - //* if error, skip frame */ - writeln( 'Skip audio frame' ); + // if error, skip frame +// writeln( 'Skip audio frame' ); audio_pkt_size := 0; break; end; - + audio_pkt_data := audio_pkt_data + len1; audio_pkt_size := audio_pkt_size + len1; - + if (data_size <= 0) then begin - //* No data yet, get more frames */ + // No data yet, get more frames continue; end; - - //* We have data, return it and come back for more later */ + + // We have data, return it and come back for more later result := data_size; exit; end; - if ( pkt.data <> nil ) then - av_free_packet( pkt ); + if (pkt.data <> nil) then + av_free_packet(pkt); - if ( quit <> 0 ) then + if (packetQueue.quit) then begin result := -1; exit; end; - if (packet_queue_get(audioq, pkt, 1) < 0) then + if (packetQueue.Get(pkt, true) < 0) then begin result := -1; exit; end; - - audio_pkt_data := pchar( pkt.data ); + audio_pkt_data := PChar(pkt.data); audio_pkt_size := pkt.size; // writeln( 'Audio Packet Size - ' + inttostr(audio_pkt_size) ); - end; *) + end; end; -procedure audio_callback( userdata: Pointer; stream: PUInt8; len: Integer ); +procedure AudioCallback(userdata: Pointer; stream: PUInt8; len: Integer); cdecl; var - audio_buf_index : cardinal; // static unsigned int audio_buf_index = 0; - audio_buf_size : cardinal; // static unsigned int audio_buf_size = 0; - audio_size , - len1 : integer; - aCodecCtx : TAVCodecContext; - - lSrc : pointer; - - // this is used to emulate ...... static uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2]; - lAudio_buf_data : Taudiobuff; // This created the memory we need - laudio_buf : PAudioBuff; // this makes it easy to work with.. since its the pointer to that memeory everywhere + outStream : TFFMpegOutputStream; + len1, + audio_size : integer; + pSrc : Pointer; begin - laudio_buf := @lAudio_buf_data ; - - aCodecCtx := pAVCodecContext(userdata)^; - audio_size := -1; - audio_buf_index := 0; - audio_buf_size := 0; + outStream := TFFMpegOutputStream(userdata); while (len > 0) do - begin - + with outStream do begin if (audio_buf_index >= audio_buf_size) then begin + // We have already sent all our data; get more + audio_size := AudioDecodeFrame(@audio_buf, sizeof(TAudioBuffer)); + writeln('audio_decode_frame : '+ inttostr(audio_size)); - // We have already sent all our data; get more */ - audio_size := audio_decode_frame(aCodecCtx, pUInt8( laudio_buf ), sizeof(Taudiobuff)); - writeln( 'audio_decode_frame : ' + inttostr( audio_size ) + ' / ' + inttostr( sizeof(Taudiobuff) ) ); - - if(audio_size > 0) then + if(audio_size < 0) then begin - audio_buf_size := audio_size; + // If error, output silence + audio_buf_size := 1024; + FillChar(audio_buf, audio_buf_size, #0); + writeln( 'Silence' ); end else begin - // If error, output silence */ - -// audio_buf_size := 1024; // arbitrary? - -// {$ifdef win32} -// FillChar(laudio_buf, audio_buf_size, #0); -// {$else} -// memset(laudio_buf, 0, audio_buf_size); // todo : jb memset -// {$endif} - - writeln( 'Silence' ); + audio_buf_size := audio_size; end; - audio_buf_index := 0; // Todo : jb - SegFault ? end; @@ -594,354 +569,305 @@ begin if (len1 > len) then len1 := len; - lSrc := PUInt8( integer( laudio_buf ) + audio_buf_index ); + pSrc := PChar(@audio_buf) + audio_buf_index; {$ifdef WIN32} - CopyMemory(stream, lSrc , len1); + CopyMemory(stream, pSrc , len1); {$else} - memcpy(stream, lSrc , len1); + memcpy(stream, pSrc , len1); {$endif} - len := len - len1; - stream^ := stream^ + len1; - audio_buf_index := audio_buf_index + len1; + Dec(len, len1); + Inc(stream, len1); + Inc(audio_buf_index, len1); end; end; -function TAudio_ffMpeg.LoadSoundFromFile(var hStream: hStream; Name: string): boolean; +function TAudio_FFMpeg.FindAudioStreamID(pFormatCtx : PAVFormatContext): integer; var - L : Integer; - pFormatCtx : PAVFormatContext; - lVidStreamID , - lAudStreamID : Integer; - aCodecCtx : pAVCodecContext; - wanted_spec , - spec : TSDL_AudioSpec; - lAudioStream : pAVStream; - aCodec : pAVCodec; - i : integer; - packet : TAVPacket; - event : TSDL_Event; -begin - result := false; + i : integer; + streamID: integer; + stream : PAVStream; +begin + // Find the first audio stream + streamID := -1; - if FileExists(Name) then + for i := 0 to pFormatCtx^.nb_streams-1 do begin - writeln('Loading Sound: "' + Name + '"', 'LoadSoundFromFile'); - - // Open video file - if (av_open_input_file(pFormatCtx, pchar(Name), nil, 0, nil) > 0) then - exit; + //Log.LogStatus('aFormatCtx.streams[i] : ' + inttostr(i), 'UAudio_FFMpeg'); + stream := pFormatCtx^.streams[i]; - // Retrieve stream information - if (av_find_stream_info(pFormatCtx)<0) then - exit; - - dump_format(pFormatCtx, 0, pchar(Name), 0); - - if not find_stream_ids( pFormatCtx, lVidStreamID, lAudStreamID ) then - exit; - -// writeln( 'done searching for stream ids' ); - - if lAudStreamID > -1 then - begin -// writeln( 'Audio Stream ID is : '+ inttostr( lAudStreamID ) ); - - lAudioStream := pFormatCtx.streams[lAudStreamID]; - aCodecCtx := lAudioStream.codec; - - // Set audio settings from codec info - wanted_spec.freq := aCodecCtx.sample_rate; - wanted_spec.format := AUDIO_S16SYS; - wanted_spec.channels := aCodecCtx.channels; - wanted_spec.silence := 0; - wanted_spec.samples := SDL_AUDIO_BUFFER_SIZE; - wanted_spec.callback := audio_callback; - wanted_spec.userdata := aCodecCtx; - end; - - if (SDL_OpenAudio(@wanted_spec, @spec) < 0) then - begin - writeln('SDL_OpenAudio: '+SDL_GetError()); - exit + if ( stream.codec^.codec_type = CODEC_TYPE_AUDIO ) then + begin + Log.LogStatus('Found Audio Stream', 'UAudio_FFMpeg'); + streamID := i; + break; + end; end; -// writeln( 'SDL opened audio device' ); - - aCodec := avcodec_find_decoder(aCodecCtx.codec_id); - if (aCodec = nil) then - begin - writeln('Unsupported codec!'); - exit; - end; + result := streamID; +end; - avcodec_open(aCodecCtx, aCodec); +function ParseAudio(data: Pointer): integer; cdecl; +var + packet: TAVPacket; + stream: TFFMpegOutputStream; +begin + stream := TFFMpegOutputStream(data); -// writeln( 'Opened the codec' ); - - packet_queue_init( audioq ); - SDL_PauseAudio(0); - -// writeln( 'SDL_PauseAudio' ); - - i := 0; - while (av_read_frame(pFormatCtx, packet) >= 0) do + while (av_read_frame(stream.pFormatCtx, packet) >= 0) do begin -// writeln( 'ffmpeg - av_read_frame' ); - - if (packet.stream_index = lAudStreamID ) then + writeln( 'ffmpeg - av_read_frame' ); + + if (packet.stream_index = stream.ffmpegStreamID) then begin -// writeln( 'packet_queue_put' ); - packet_queue_put(audioq, packet); + //writeln( 'packet_queue_put' ); + stream.packetQueue.put(@packet); end else begin av_free_packet(packet); end; + end; + Writeln('Done: ' + inttostr(stream.packetQueue.nbPackets)); - // Free the packet that was allocated by av_read_frame - SDL_PollEvent(@event); + result := 0; +end; -(* - if event.type_ = SDL_QUIT the - begin - quit := 1; - SDL_Quit(); - end - else - break; -*) +function TAudio_FFMpeg.LoadSoundFromFile(Name: string): TFFMpegOutputStream; +var + pFormatCtx : PAVFormatContext; + pCodecCtx : PAVCodecContext; + pCodec : PAVCodec; + ffmpegStreamID : Integer; + ffmpegStream : PAVStream; + wanted_spec, + spec : TSDL_AudioSpec; + csIndex : integer; + stream : TFFMpegOutputStream; +begin + result := nil; + + if (not FileExists(Name)) then + begin + Log.LogStatus('LoadSoundFromFile: Sound not found "' + Name + '"', 'UAudio_FFMpeg'); + exit; end; - - writeln( 'Done filling buffer' ); -// halt(0); + // Open audio file + if (av_open_input_file(pFormatCtx, PChar(Name), nil, 0, nil) > 0) then + exit; - // Close the codec -// avcodec_close(aCodecCtx); + // Retrieve stream information + if (av_find_stream_info(pFormatCtx) < 0) then + exit; - // Close the video file -// av_close_input_file(pFormatCtx); + dump_format(pFormatCtx, 0, pchar(Name), 0); -(* - try - // TODO : jb_linux replace with something other than bass - hStream := BASS_StreamCreateFile(False, pchar(Name), 0, 0, 0); - - //Add CustomSound - L := High(CustomSounds) + 1; - SetLength (CustomSounds, L + 1); - CustomSounds[L].Filename := Name; - CustomSounds[L].Handle := hStream; - except - Log.LogError('Failed to open using BASS', 'LoadSoundFromFile'); - end; -*) - - end - else + 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 - writeln('Sound not found: "' + Name + '"', 'LoadSoundFromFile'); + Log.LogStatus('Unsupported codec!', 'UAudio_FFMpeg'); exit; end; + avcodec_open(pCodecCtx, pCodec); + //writeln( 'Opened the codec' ); + + stream := TFFMpegOutputStream.Create(pFormatCtx, pCodecCtx, pCodec, + ffmpegStreamID, ffmpegStream); + + // Set SDL audio settings from codec info + wanted_spec.freq := pCodecCtx^.sample_rate; + wanted_spec.format := AUDIO_S16SYS; + wanted_spec.channels := pCodecCtx^.channels; + wanted_spec.silence := 0; + wanted_spec.samples := SDL_AUDIO_BUFFER_SIZE; + wanted_spec.callback := AudioCallback; + wanted_spec.userdata := stream; + + // TODO: this works only one time (?) + if (SDL_OpenAudio(@wanted_spec, @spec) < 0) then + begin + Log.LogStatus('SDL_OpenAudio: '+SDL_GetError(), 'UAudio_FFMpeg'); + stream.Free(); + exit + end; + + Log.LogStatus('SDL opened audio device', 'UAudio_FFMpeg'); + + //Add CustomSound + csIndex := High(CustomSounds) + 1; + SetLength (CustomSounds, csIndex + 1); + CustomSounds[csIndex].Filename := Name; + CustomSounds[csIndex].Stream := stream; + + result := stream; end; -procedure packet_queue_init(var aPacketQueue : TPacketQueue ); +//Equalizer +function TAudio_FFMpeg.GetFFTData: TFFTData; +var + data: TFFTData; begin - {$ifdef win32} - FillChar(aPacketQueue, sizeof(TPacketQueue), #0); - {$else} - memset(@aPacketQueue, 0, sizeof(TPacketQueue)); - {$endif} - - aPacketQueue.mutex := SDL_CreateMutex(); - aPacketQueue.cond := SDL_CreateCond(); + //Get Channel Data Mono and 256 Values +// BASS_ChannelGetData(Bass, @Result, BASS_DATA_FFT512); + result := data; +end; + +// Interface for Visualizer +function TAudio_FFMpeg.GetPCMData(var data: TPCMData): Cardinal; +begin + result := 0; +end; + +function TAudio_FFMpeg.LoadCustomSound(const Filename: String): Cardinal; +var + S: TFFMpegOutputStream; + I: Integer; + F: String; +begin + //Search for Sound in already loaded Sounds + F := UpperCase(SoundPath + FileName); + For I := 0 to High(CustomSounds) do + begin + if (UpperCase(CustomSounds[I].Filename) = F) then + begin + Result := I; + Exit; + end; + end; + + S := LoadSoundFromFile(SoundPath + Filename); + if (S <> nil) then + Result := High(CustomSounds) + else + Result := 0; +end; + +procedure TAudio_FFMpeg.PlayCustomSound(const Index: Cardinal ); +begin + if Index <= High(CustomSounds) then + (CustomSounds[Index].Stream as TFFMpegOutputStream).Play(); end; -function packet_queue_put(var aPacketQueue : TPacketQueue; var AVPacket : TAVPacket): integer; + +constructor TPacketQueue.Create(); +begin + inherited; + + firstPkt := nil; + lastPkt := nil; + nbPackets := 0; + size := 0; + + mutex := SDL_CreateMutex(); + cond := SDL_CreateCond(); +end; + +function TPacketQueue.Put(pkt : PAVPacket): integer; var - pkt1 : pAVPacketList; + pkt1 : PAVPacketList; begin result := -1; - -// writeln( 'TAudio_ffMpeg.packet_queue_put' ); - - if av_dup_packet(@AVPacket) < 0 then + + if (av_dup_packet(pkt) < 0) then exit; pkt1 := av_malloc(sizeof(TAVPacketList)); if (pkt1 = nil) then exit; - - pkt1.pkt := AVPacket; - pkt1.next := nil; + + pkt1^.pkt := pkt^; + pkt1^.next := nil; - SDL_LockMutex( aPacketQueue.mutex ); + SDL_LockMutex(Self.mutex); try - if (aPacketQueue.last_pkt = nil) then - aPacketQueue.first_pkt := pkt1 + if (Self.lastPkt = nil) then + Self.firstPkt := pkt1 else - aPacketQueue.last_pkt.next := pkt1; + Self.lastPkt^.next := pkt1; - aPacketQueue.last_pkt := pkt1; - inc( aPacketQueue.nb_packets ); - - aPacketQueue.size := aPacketQueue.size + pkt1.pkt.size; - SDL_CondSignal(aPacketQueue.cond); + Self.lastPkt := pkt1; + inc(Self.nbPackets); + + Writeln('Put: ' + inttostr(nbPackets)); + + Self.size := Self.size + pkt1^.pkt.size; + SDL_CondSignal(Self.cond); finally - SDL_UnlockMutex( aPacketQueue.mutex ); + SDL_UnlockMutex(Self.mutex); end; result := 0; end; -function packet_queue_get(var aPacketQueue : TPacketQueue; var AVPacket : TAVPacket; block : integer ): integer; +function TPacketQueue.Get(var pkt: TAVPacket; block: boolean): integer; var - pkt1 : pAVPacketList; + pkt1 : PAVPacketList; begin result := -1; -// writeln( 'packet_queue_get' ); - - SDL_LockMutex(aPacketQueue.mutex); + + SDL_LockMutex(Self.mutex); try while true do begin - - if (quit <> 0) then + if (quit) then exit; - pkt1 := aPacketQueue.first_pkt; - - if ( pkt1 <> nil ) then + pkt1 := Self.firstPkt; + + if (pkt1 <> nil) then begin - aPacketQueue.first_pkt := pkt1.next; - - if (aPacketQueue.first_pkt = nil ) then - aPacketQueue.last_pkt := nil; - - dec(aPacketQueue.nb_packets); - - aPacketQueue.size := aPacketQueue.size - pkt1.pkt.size; - - AVPacket := pkt1.pkt; + 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 (block = 0) then + if (not block) then begin result := 0; break; end else begin - SDL_CondWait(aPacketQueue.cond, aPacketQueue.mutex); + SDL_CondWait(Self.cond, Self.mutex); end; end; finally - SDL_UnlockMutex(aPacketQueue.mutex); - end; -end; - - -//Equalizer -function TAudio_ffMpeg.GetFFTData: TFFTData; -var - Data: TFFTData; -begin - //Get Channel Data Mono and 256 Values -// BASS_ChannelGetData(Bass, @Result, BASS_DATA_FFT512); -end; - -function TAudio_ffMpeg.LoadCustomSound(const Filename: String): Cardinal; -var - S: hStream; - I: Integer; - F: String; -begin -(* - //Search for Sound in already loaded Sounds - F := UpperCase(SoundPath + FileName); - For I := 0 to High(CustomSounds) do - begin - if (UpperCase(CustomSounds[I].Filename) = F) then - begin - Result := I; - Exit; - end; + SDL_UnlockMutex(Self.mutex); end; - - if LoadSoundFromFile(S, SoundPath + Filename) then - Result := High(CustomSounds) - else - Result := 0; -*) end; -procedure TAudio_ffMpeg.PlayCustomSound(const Index: Cardinal ); -begin -// if Index <= High(CustomSounds) then -// BASS_ChannelPlay(CustomSounds[Index].Handle, True); -end; - - -{* - -Sorry guys... this is my mess :( -Im going to try and get ffmpeg to handle audio playback ( at least for linux ) -and Im going to implement it nicly along side BASS, in TAudio_ffMpeg ( where I can ) - -http://www.dranger.com/ffmpeg/ffmpeg.html -http://www.dranger.com/ffmpeg/ffmpegtutorial_all.html - -http://www.inb.uni-luebeck.de/~boehme/using_libavcodec.html - -*} -{* -function TAudio_ffMpeg.FFMPeg_StreamCreateFile(abool : boolean; aFileName : pchar ): THandle; -var - lFormatCtx : PAVFormatContext; -begin - -(* - if(SDL_OpenAudio(&wanted_spec, &spec) < 0) - begin - fprintf(stderr, "SDL_OpenAudio: %s\n", SDL_GetError()); - writeln( 'SDL_OpenAudio' ); - exit; - end; -*) - -(* - if ( av_open_input_file( lFormatCtx, aFileName, NULL, 0, NULL ) <> 0 ) - begin - writeln( 'Unable to open file '+ aFileName ); - exit; - end; - - // Retrieve stream information - if ( av_find_stream_info(pFormatCtx) < 0 ) - begin - writeln( 'Unable to Retrieve stream information' ); - exit; - end; -*) -end; *} initialization - singleton_MusicFFMpeg := TAudio_ffMpeg.create(); + singleton_MusicFFMpeg := TAudio_FFMpeg.create(); - writeln( 'UAudio_Bass - Register Playback' ); + writeln( 'UAudio_FFMpeg - Register Playback' ); AudioManager.add( IAudioPlayback( singleton_MusicFFMpeg ) ); finalization |