From 5315ce20f8730c13c3b79f98d90caf8ce74f9cae Mon Sep 17 00:00:00 2001 From: tobigun Date: Wed, 27 Oct 2010 20:09:47 +0000 Subject: ffmpeg audio converter moved to media plugin git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/branches/experimental@2690 b956fd51-792f-4845-bead-9b4dfca2ff2c --- mediaplugin/src/media/UAudioConverter.pas | 82 ++----- mediaplugin/src/media/UAudioDecoder_FFmpeg.pas | 15 -- mediaplugin/src/media/UMediaCore_FFmpeg.pas | 302 ------------------------- mediaplugin/src/media/UMediaPlugin.pas | 40 +++- 4 files changed, 57 insertions(+), 382 deletions(-) diff --git a/mediaplugin/src/media/UAudioConverter.pas b/mediaplugin/src/media/UAudioConverter.pas index 657b80dd..aa3e343e 100644 --- a/mediaplugin/src/media/UAudioConverter.pas +++ b/mediaplugin/src/media/UAudioConverter.pas @@ -40,9 +40,7 @@ uses {$IFDEF UseSRCResample} samplerate, {$ENDIF} - {$IFDEF UseFFmpegResample} - avcodec, - {$ENDIF} + UMediaPlugin, UMediaCore_SDL, sdl, SysUtils, @@ -76,13 +74,11 @@ type end; {$IFDEF UseFFmpegResample} - // Note: FFmpeg seems to be using "kaiser windowed sinc" for resampling, so - // the quality should be good. TAudioConverter_FFmpeg = class(TAudioConverter) private - // TODO: use SDL for multi-channel->stereo and format conversion - ResampleContext: PReSampleContext; - Ratio: double; + fPluginInfo: PMediaPluginInfo; + fAudioConverter: PAudioConverterInfo; + fStream: PAudioConvertStream; public function Init(SrcFormatInfo: TAudioFormatInfo; DstFormatInfo: TAudioFormatInfo): boolean; override; destructor Destroy(); override; @@ -199,84 +195,50 @@ end; {$IFDEF UseFFmpegResample} function TAudioConverter_FFmpeg.Init(SrcFormatInfo: TAudioFormatInfo; DstFormatInfo: TAudioFormatInfo): boolean; +var + CSrcFormatInfo: TCAudioFormatInfo; + CDstFormatInfo: TCAudioFormatInfo; begin inherited Init(SrcFormatInfo, DstFormatInfo); Result := false; - - // Note: ffmpeg does not support resampling for more than 2 input channels - - if (srcFormatInfo.Format <> asfS16) then - begin - Log.LogError('Unsupported format', 'TAudioConverter_FFmpeg.Init'); - Exit; - end; - - // TODO: use SDL here - if (srcFormatInfo.Format <> dstFormatInfo.Format) then + fPluginInfo := Plugin_register(MediaPluginCore); + fPluginInfo.initialize(); + fAudioConverter := fPluginInfo.audioConverter; + + AudioFormatInfoToCStruct(SrcFormatInfo, CSrcFormatInfo); + AudioFormatInfoToCStruct(DstFormatInfo, CDstFormatInfo); + fStream := fAudioConverter.open(@CSrcFormatInfo, @CDstFormatInfo); + if (fStream = nil) then begin - Log.LogError('Incompatible formats', 'TAudioConverter_FFmpeg.Init'); + Log.LogError('fAudioConverter.open() failed', 'TAudioConverter_FFmpeg.Init'); Exit; end; - ResampleContext := audio_resample_init( - dstFormatInfo.Channels, srcFormatInfo.Channels, - Round(dstFormatInfo.SampleRate), Round(srcFormatInfo.SampleRate)); - if (ResampleContext = nil) then - begin - Log.LogError('audio_resample_init() failed', 'TAudioConverter_FFmpeg.Init'); - Exit; - end; - - // calculate ratio - Ratio := (dstFormatInfo.Channels / srcFormatInfo.Channels) * - (dstFormatInfo.SampleRate / srcFormatInfo.SampleRate); - Result := true; end; destructor TAudioConverter_FFmpeg.Destroy(); begin - if (ResampleContext <> nil) then - audio_resample_close(ResampleContext); + if (fStream <> nil) then + fAudioConverter.close(fStream); inherited; end; function TAudioConverter_FFmpeg.Convert(InputBuffer: PByteArray; OutputBuffer: PByteArray; var InputSize: integer): integer; -var - InputSampleCount: integer; - OutputSampleCount: integer; begin - Result := -1; - - if (InputSize <= 0) then - begin - // avoid div-by-zero in audio_resample() - if (InputSize = 0) then - Result := 0; - Exit; - end; - - InputSampleCount := InputSize div SrcFormatInfo.FrameSize; - OutputSampleCount := audio_resample( - ResampleContext, PSmallInt(OutputBuffer), PSmallInt(InputBuffer), - InputSampleCount); - if (OutputSampleCount = -1) then - begin - Log.LogError('audio_resample() failed', 'TAudioConverter_FFmpeg.Convert'); - Exit; - end; - Result := OutputSampleCount * DstFormatInfo.FrameSize; + Result := fAudioConverter.convert(fStream, + PCuint8(InputBuffer), PCuint8(OutputBuffer), @InputSize); end; function TAudioConverter_FFmpeg.GetOutputBufferSize(InputSize: integer): integer; begin - Result := Ceil(InputSize * GetRatio()); + Result := fAudioConverter.getOutputBufferSize(fStream, InputSize); end; function TAudioConverter_FFmpeg.GetRatio(): double; begin - Result := Ratio; + Result := fAudioConverter.getRatio(fStream); end; {$ENDIF} diff --git a/mediaplugin/src/media/UAudioDecoder_FFmpeg.pas b/mediaplugin/src/media/UAudioDecoder_FFmpeg.pas index 3031b46a..bef162bd 100644 --- a/mediaplugin/src/media/UAudioDecoder_FFmpeg.pas +++ b/mediaplugin/src/media/UAudioDecoder_FFmpeg.pas @@ -199,21 +199,6 @@ end; { TAudioDecoder_FFmpeg } -const -{$IFDEF MSWINDOWS} - ffmpegPlugin = 'ffmpeg_playback.dll'; -{$ENDIF} -{$IFDEF LINUX} - ffmpegPlugin = 'ffmpeg_playback'; -{$ENDIF} -{$IFDEF DARWIN} - ffmpegPlugin = 'ffmpeg_playback.dylib'; - {$linklib ffmpegPlugin} -{$ENDIF} - -function Plugin_register(core: PMediaPluginCore): PMediaPluginInfo; - cdecl; external ffmpegPlugin; - constructor TAudioDecoder_FFmpeg.Create(); begin inherited Create(); diff --git a/mediaplugin/src/media/UMediaCore_FFmpeg.pas b/mediaplugin/src/media/UMediaCore_FFmpeg.pas index eb136995..2f589442 100644 --- a/mediaplugin/src/media/UMediaCore_FFmpeg.pas +++ b/mediaplugin/src/media/UMediaCore_FFmpeg.pas @@ -46,32 +46,6 @@ uses ULog, UPath; -type - PPacketQueue = ^TPacketQueue; - TPacketQueue = class - private - FirstListEntry: PAVPacketList; - LastListEntry: PAVPacketList; - PacketCount: integer; - Mutex: PSDL_Mutex; - Condition: PSDL_Cond; - Size: integer; - AbortRequest: boolean; - public - constructor Create(); - destructor Destroy(); override; - - function Put(Packet : PAVPacket): integer; - function PutStatus(StatusFlag: integer; StatusInfo: Pointer): integer; - procedure FreeStatusInfo(var Packet: TAVPacket); - function GetStatusInfo(var Packet: TAVPacket): Pointer; - function Get(var Packet: TAVPacket; Blocking: boolean): integer; - function GetSize(): integer; - procedure Flush(); - procedure Abort(); - function IsAborted(): boolean; - end; - const STATUS_PACKET: PChar = 'STATUS_PACKET'; const @@ -122,76 +96,8 @@ const var Instance: TMediaCore_FFmpeg; -function AV_VERSION_INT(a, b, c: cardinal): cuint; -begin - Result := (a shl 16) or (b shl 8) or c; -end; - procedure CheckVersions(); -var - libVersion: cuint; - headerVersion: cuint; - - function hexVerToStr(Version: cuint): string; - var - Major, Minor, Release: cardinal; - begin - Major := (Version shr 16) and $FF;; - Minor := (Version shr 8) and $FF; - Release := Version and $FF; - Result := Format('%d.%d.%d', [Major, Minor, Release]); - end; - begin - libVersion := avcodec_version(); - headerVersion := AV_VERSION_INT( - LIBAVCODEC_VERSION_MAJOR, - LIBAVCODEC_VERSION_MINOR, - LIBAVCODEC_VERSION_RELEASE); - if (libVersion <> headerVersion) then - begin - Log.LogError(Format('%s header (%s) and DLL (%s) versions do not match.', - ['libavcodec', hexVerToStr(headerVersion), hexVerToStr(libVersion)])); - end; - - {$IF LIBAVFORMAT_VERSION >= 52020000} // 52.20.0 - libVersion := avformat_version(); - headerVersion := AV_VERSION_INT( - LIBAVFORMAT_VERSION_MAJOR, - LIBAVFORMAT_VERSION_MINOR, - LIBAVFORMAT_VERSION_RELEASE); - if (libVersion <> headerVersion) then - begin - Log.LogError(Format('%s header (%s) and DLL (%s) versions do not match.', - ['libavformat', hexVerToStr(headerVersion), hexVerToStr(libVersion)])); - end; - {$IFEND} - - {$IF LIBAVUTIL_VERSION >= 49008000} // 49.8.0 - libVersion := avutil_version(); - headerVersion := AV_VERSION_INT( - LIBAVUTIL_VERSION_MAJOR, - LIBAVUTIL_VERSION_MINOR, - LIBAVUTIL_VERSION_RELEASE); - if (libVersion <> headerVersion) then - begin - Log.LogError(Format('%s header (%s) and DLL (%s) versions do not match.', - ['libavutil', hexVerToStr(headerVersion), hexVerToStr(libVersion)])); - end; - {$IFEND} - - {$IF LIBSWSCALE_VERSION >= 000006001} // 0.6.1 - libVersion := swscale_version(); - headerVersion := AV_VERSION_INT( - LIBSWSCALE_VERSION_MAJOR, - LIBSWSCALE_VERSION_MINOR, - LIBSWSCALE_VERSION_RELEASE); - if (libVersion <> headerVersion) then - begin - Log.LogError(Format('%s header (%s) and DLL (%s) versions do not match.', - ['libswscale', hexVerToStr(headerVersion), hexVerToStr(libVersion)])); - end; - {$IFEND} end; constructor TMediaCore_FFmpeg.Create(); @@ -434,212 +340,4 @@ begin Result := 0; end; - -{ TPacketQueue } - -constructor TPacketQueue.Create(); -begin - inherited; - - FirstListEntry := nil; - LastListEntry := nil; - PacketCount := 0; - Size := 0; - - Mutex := SDL_CreateMutex(); - Condition := SDL_CreateCond(); -end; - -destructor TPacketQueue.Destroy(); -begin - Flush(); - SDL_DestroyMutex(Mutex); - SDL_DestroyCond(Condition); - inherited; -end; - -procedure TPacketQueue.Abort(); -begin - SDL_LockMutex(Mutex); - - AbortRequest := true; - - SDL_CondBroadcast(Condition); - SDL_UnlockMutex(Mutex); -end; - -function TPacketQueue.IsAborted(): boolean; -begin - SDL_LockMutex(Mutex); - Result := AbortRequest; - SDL_UnlockMutex(Mutex); -end; - -function TPacketQueue.Put(Packet : PAVPacket): integer; -var - CurrentListEntry : PAVPacketList; -begin - Result := -1; - - if (Packet = nil) then - Exit; - - if (PChar(Packet^.data) <> STATUS_PACKET) then - begin - if (av_dup_packet(Packet) < 0) then - Exit; - end; - - CurrentListEntry := av_malloc(SizeOf(TAVPacketList)); - if (CurrentListEntry = nil) then - Exit; - - CurrentListEntry^.pkt := Packet^; - CurrentListEntry^.next := nil; - - SDL_LockMutex(Mutex); - try - if (LastListEntry = nil) then - FirstListEntry := CurrentListEntry - else - LastListEntry^.next := CurrentListEntry; - - LastListEntry := CurrentListEntry; - Inc(PacketCount); - - Size := Size + CurrentListEntry^.pkt.size; - SDL_CondSignal(Condition); - finally - SDL_UnlockMutex(Mutex); - end; - - Result := 0; -end; - -(** - * Adds a status packet (EOF, Flush, etc.) to the end of the queue. - * StatusInfo can be used to pass additional information to the decoder. - * Only assign nil or a valid pointer to data allocated with Getmem() to - * StatusInfo because the pointer will be disposed with Freemem() on a call - * to Flush(). If the packet is removed from the queue it is the decoder's - * responsibility to free the StatusInfo data with FreeStatusInfo(). - *) -function TPacketQueue.PutStatus(StatusFlag: integer; StatusInfo: Pointer): integer; -var - TempPacket: PAVPacket; -begin - // create temp. package - TempPacket := av_malloc(SizeOf(TAVPacket)); - if (TempPacket = nil) then - begin - Result := -1; - Exit; - end; - // init package - av_init_packet(TempPacket^); - TempPacket^.data := Pointer(STATUS_PACKET); - TempPacket^.flags := StatusFlag; - TempPacket^.priv := StatusInfo; - // put a copy of the package into the queue - Result := Put(TempPacket); - // data has been copied -> delete temp. package - av_free(TempPacket); -end; - -procedure TPacketQueue.FreeStatusInfo(var Packet: TAVPacket); -begin - if (Packet.priv <> nil) then - FreeMem(Packet.priv); -end; - -function TPacketQueue.GetStatusInfo(var Packet: TAVPacket): Pointer; -begin - Result := Packet.priv; -end; - -function TPacketQueue.Get(var Packet: TAVPacket; Blocking: boolean): integer; -var - CurrentListEntry: PAVPacketList; -const - WAIT_TIMEOUT = 10; // timeout in ms -begin - Result := -1; - - SDL_LockMutex(Mutex); - try - while (true) do - begin - if (AbortRequest) then - Exit; - - CurrentListEntry := FirstListEntry; - if (CurrentListEntry <> nil) then - begin - FirstListEntry := CurrentListEntry^.next; - if (FirstListEntry = nil) then - LastListEntry := nil; - Dec(PacketCount); - - Size := Size - CurrentListEntry^.pkt.size; - Packet := CurrentListEntry^.pkt; - av_free(CurrentListEntry); - - Result := 1; - Break; - end - else if (not Blocking) then - begin - Result := 0; - Break; - end - else - begin - // block until a new package arrives, - // but do not wait till infinity to avoid deadlocks - if (SDL_CondWaitTimeout(Condition, Mutex, WAIT_TIMEOUT) = SDL_MUTEX_TIMEDOUT) then - begin - Result := 0; - Break; - end; - end; - end; - finally - SDL_UnlockMutex(Mutex); - end; -end; - -function TPacketQueue.GetSize(): integer; -begin - SDL_LockMutex(Mutex); - Result := Size; - SDL_UnlockMutex(Mutex); -end; - -procedure TPacketQueue.Flush(); -var - CurrentListEntry, TempListEntry: PAVPacketList; -begin - SDL_LockMutex(Mutex); - - CurrentListEntry := FirstListEntry; - while(CurrentListEntry <> nil) do - begin - TempListEntry := CurrentListEntry^.next; - // free status data - if (PChar(CurrentListEntry^.pkt.data) = STATUS_PACKET) then - FreeStatusInfo(CurrentListEntry^.pkt); - // free packet data - av_free_packet(@CurrentListEntry^.pkt); - // Note: param must be a pointer to a pointer! - av_freep(@CurrentListEntry); - CurrentListEntry := TempListEntry; - end; - LastListEntry := nil; - FirstListEntry := nil; - PacketCount := 0; - Size := 0; - - SDL_UnlockMutex(Mutex); -end; - end. diff --git a/mediaplugin/src/media/UMediaPlugin.pas b/mediaplugin/src/media/UMediaPlugin.pas index 8e2708bf..c676619c 100644 --- a/mediaplugin/src/media/UMediaPlugin.pas +++ b/mediaplugin/src/media/UMediaPlugin.pas @@ -36,6 +36,7 @@ interface {$ENDIF} uses + UMusic, ctypes; type @@ -78,7 +79,7 @@ type end; PAudioDecodeStream = Pointer; - PAudioResampleStream = Pointer; + PAudioConvertStream = Pointer; PCAudioFormatInfo = ^TCAudioFormatInfo; TCAudioFormatInfo = record @@ -104,9 +105,11 @@ type PAudioConverterInfo = ^TAudioConverterInfo; TAudioConverterInfo = record - open: function(inputFormat: PCAudioFormatInfo; outputFormat: PCAudioFormatInfo): PAudioResampleStream; cdecl; - close: procedure(stream: PAudioResampleStream); cdecl; - convert: function(stream: PAudioResampleStream; input, output: PCuint8; numSamples: cint): cint; cdecl; + open: function(inputFormat: PCAudioFormatInfo; outputFormat: PCAudioFormatInfo): PAudioConvertStream; cdecl; + close: procedure(stream: PAudioConvertStream); cdecl; + convert: function(stream: PAudioConvertStream; input, output: PCuint8; numSamples: PCint): cint; cdecl; + getOutputBufferSize: function(stream: PAudioConvertStream; inputSize: cint): cint; cdecl; + getRatio: function(stream: PAudioConvertStream): double; cdecl; end; PMediaPluginInfo = ^TMediaPluginInfo; @@ -119,10 +122,29 @@ type audioConverter: PAudioConverterInfo; end; - Plugin_register = function(core: PMediaPluginCore): PMediaPluginInfo; cdecl; + Plugin_registerFunc = function(core: PMediaPluginCore): PMediaPluginInfo; cdecl; + +const +{$IFDEF MSWINDOWS} + ffmpegPlugin = 'ffmpeg_playback.dll'; +{$ENDIF} +{$IFDEF LINUX} + ffmpegPlugin = 'ffmpeg_playback'; +{$ENDIF} +{$IFDEF DARWIN} + ffmpegPlugin = 'ffmpeg_playback.dylib'; + {$linklib ffmpegPlugin} +{$ENDIF} + +function Plugin_register(core: PMediaPluginCore): PMediaPluginInfo; + cdecl; external ffmpegPlugin; + function MediaPluginCore: PMediaPluginCore; +procedure AudioFormatInfoToCStruct( + const Info: TAudioFormatInfo; var CInfo: TCAudioFormatInfo); + implementation uses @@ -147,6 +169,14 @@ begin Result := @MediaPluginCore_Instance; end; +procedure AudioFormatInfoToCStruct( + const Info: TAudioFormatInfo; var CInfo: TCAudioFormatInfo); +begin + CInfo.sampleRate := Info.SampleRate; + CInfo.channels := Info.Channels; + CInfo.format := Ord(Info.Format); +end; + {* Misc *} procedure Core_log(level: cint; msg: PChar; context: PChar); cdecl; -- cgit v1.2.3