aboutsummaryrefslogtreecommitdiffstats
path: root/mediaplugin/src/media
diff options
context:
space:
mode:
authortobigun <tobigun@b956fd51-792f-4845-bead-9b4dfca2ff2c>2010-10-28 14:38:26 +0000
committertobigun <tobigun@b956fd51-792f-4845-bead-9b4dfca2ff2c>2010-10-28 14:38:26 +0000
commitf8f21b447558c911bcd4bbaaf45e9d7ba52e8fe1 (patch)
treebd3b688d3411eff8224ce4db65f17b1159ba288b /mediaplugin/src/media
parent40acf26d6752ec031d814dd88136afb6fca966d1 (diff)
downloadusdx-f8f21b447558c911bcd4bbaaf45e9d7ba52e8fe1.tar.gz
usdx-f8f21b447558c911bcd4bbaaf45e9d7ba52e8fe1.tar.xz
usdx-f8f21b447558c911bcd4bbaaf45e9d7ba52e8fe1.zip
removed ffmpeg stuff
git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/branches/experimental@2694 b956fd51-792f-4845-bead-9b4dfca2ff2c
Diffstat (limited to 'mediaplugin/src/media')
-rw-r--r--mediaplugin/src/media/UAudioDecoder_FFmpeg.pas2
-rw-r--r--mediaplugin/src/media/UMediaCore_FFmpeg.pas343
-rw-r--r--mediaplugin/src/media/UMediaPlugin.pas16
-rw-r--r--mediaplugin/src/media/UVideoDecoder_FFmpeg.pas654
4 files changed, 54 insertions, 961 deletions
diff --git a/mediaplugin/src/media/UAudioDecoder_FFmpeg.pas b/mediaplugin/src/media/UAudioDecoder_FFmpeg.pas
index bef162bd..2865a7d8 100644
--- a/mediaplugin/src/media/UAudioDecoder_FFmpeg.pas
+++ b/mediaplugin/src/media/UAudioDecoder_FFmpeg.pas
@@ -207,7 +207,7 @@ end;
function TAudioDecoder_FFmpeg.GetName: String;
begin
- Result := 'Plugin:' + fPluginInfo.name;
+ Result := 'Plugin:AudioDecoder:' + fPluginInfo.name;
end;
function TAudioDecoder_FFmpeg.InitializeDecoder: boolean;
diff --git a/mediaplugin/src/media/UMediaCore_FFmpeg.pas b/mediaplugin/src/media/UMediaCore_FFmpeg.pas
deleted file mode 100644
index 2f589442..00000000
--- a/mediaplugin/src/media/UMediaCore_FFmpeg.pas
+++ /dev/null
@@ -1,343 +0,0 @@
-{* UltraStar Deluxe - Karaoke Game
- *
- * UltraStar Deluxe is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- *
- * $URL$
- * $Id$
- *}
-
-unit UMediaCore_FFmpeg;
-
-interface
-
-{$IFDEF FPC}
- {$MODE Delphi}
-{$ENDIF}
-
-{$I switches.inc}
-
-uses
- Classes,
- ctypes,
- sdl,
- avcodec,
- avformat,
- avutil,
- avio,
- swscale,
- UMusic,
- ULog,
- UPath;
-
-const
- STATUS_PACKET: PChar = 'STATUS_PACKET';
-const
- PKT_STATUS_FLAG_EOF = 1; // signal end-of-file
- PKT_STATUS_FLAG_FLUSH = 2; // request the decoder to flush its avcodec decode buffers
- PKT_STATUS_FLAG_ERROR = 3; // signal an error state
- PKT_STATUS_FLAG_EMPTY = 4; // request the decoder to output empty data (silence or black frames)
-
-type
- TMediaCore_FFmpeg = class
- private
- AVCodecLock: PSDL_Mutex;
- public
- constructor Create();
- destructor Destroy(); override;
- class function GetInstance(): TMediaCore_FFmpeg;
-
- function GetErrorString(ErrorNum: integer): string;
- function FindStreamIDs(FormatCtx: PAVFormatContext; out FirstVideoStream, FirstAudioStream: integer ): boolean;
- function FindAudioStreamIndex(FormatCtx: PAVFormatContext): integer;
- function ConvertFFmpegToAudioFormat(FFmpegFormat: TSampleFormat; out Format: TAudioSampleFormat): boolean;
- procedure LockAVCodec();
- procedure UnlockAVCodec();
- end;
-
-implementation
-
-uses
- SysUtils,
- UConfig;
-
-function FFmpegStreamOpen(h: PURLContext; filename: PChar; flags: cint): cint; cdecl; forward;
-function FFmpegStreamRead(h: PURLContext; buf: PByteArray; size: cint): cint; cdecl; forward;
-function FFmpegStreamWrite(h: PURLContext; buf: PByteArray; size: cint): cint; cdecl; forward;
-function FFmpegStreamSeek(h: PURLContext; pos: int64; whence: cint): int64; cdecl; forward;
-function FFmpegStreamClose(h: PURLContext): cint; cdecl; forward;
-
-const
- UTF8FileProtocol: TURLProtocol = (
- name: 'ufile';
- url_open: FFmpegStreamOpen;
- url_read: FFmpegStreamRead;
- url_write: FFmpegStreamWrite;
- url_seek: FFmpegStreamSeek;
- url_close: FFmpegStreamClose;
- );
-
-var
- Instance: TMediaCore_FFmpeg;
-
-procedure CheckVersions();
-begin
-end;
-
-constructor TMediaCore_FFmpeg.Create();
-begin
- inherited;
-
- CheckVersions();
- av_register_protocol(@UTF8FileProtocol);
- AVCodecLock := SDL_CreateMutex();
-end;
-
-destructor TMediaCore_FFmpeg.Destroy();
-begin
- SDL_DestroyMutex(AVCodecLock);
- inherited;
-end;
-
-class function TMediaCore_FFmpeg.GetInstance(): TMediaCore_FFmpeg;
-begin
- if (not Assigned(Instance)) then
- Instance := TMediaCore_FFmpeg.Create();
- Result := Instance;
-end;
-
-procedure TMediaCore_FFmpeg.LockAVCodec();
-begin
- SDL_mutexP(AVCodecLock);
-end;
-
-procedure TMediaCore_FFmpeg.UnlockAVCodec();
-begin
- SDL_mutexV(AVCodecLock);
-end;
-
-function TMediaCore_FFmpeg.GetErrorString(ErrorNum: integer): string;
-begin
- case ErrorNum of
- AVERROR_IO: Result := 'AVERROR_IO';
- AVERROR_NUMEXPECTED: Result := 'AVERROR_NUMEXPECTED';
- AVERROR_INVALIDDATA: Result := 'AVERROR_INVALIDDATA';
- AVERROR_NOMEM: Result := 'AVERROR_NOMEM';
- AVERROR_NOFMT: Result := 'AVERROR_NOFMT';
- AVERROR_NOTSUPP: Result := 'AVERROR_NOTSUPP';
- AVERROR_NOENT: Result := 'AVERROR_NOENT';
- AVERROR_PATCHWELCOME: Result := 'AVERROR_PATCHWELCOME';
- else Result := 'AVERROR_#'+inttostr(ErrorNum);
- end;
-end;
-
-{
- @param(FormatCtx is a PAVFormatContext returned from av_open_input_file )
- @param(FirstVideoStream is an OUT value of type integer, this is the index of the video stream)
- @param(FirstAudioStream is an OUT value of type integer, this is the index of the audio stream)
- @returns(@true on success, @false otherwise)
-}
-function TMediaCore_FFmpeg.FindStreamIDs(FormatCtx: PAVFormatContext; out FirstVideoStream, FirstAudioStream: integer): boolean;
-var
- i: integer;
- Stream: PAVStream;
-begin
- // find the first video stream
- FirstAudioStream := -1;
- FirstVideoStream := -1;
-
- for i := 0 to FormatCtx.nb_streams-1 do
- begin
- Stream := FormatCtx.streams[i];
-
-{$IF LIBAVCODEC_VERSION < 52064000} // < 52.64.0
- if (Stream.codec.codec_type = CODEC_TYPE_VIDEO) and
- (FirstVideoStream < 0) then
- begin
- FirstVideoStream := i;
- end;
-
- if (Stream.codec.codec_type = CODEC_TYPE_AUDIO) and
- (FirstAudioStream < 0) then
- begin
- FirstAudioStream := i;
- end;
- end;
-{$ELSE}
- if (Stream.codec.codec_type = AVMEDIA_TYPE_VIDEO) and
- (FirstVideoStream < 0) then
- begin
- FirstVideoStream := i;
- end;
-
- if (Stream.codec.codec_type = AVMEDIA_TYPE_AUDIO) and
- (FirstAudioStream < 0) then
- begin
- FirstAudioStream := i;
- end;
- end;
-{$IFEND}
-
- // return true if either an audio- or video-stream was found
- Result := (FirstAudioStream > -1) or
- (FirstVideoStream > -1) ;
-end;
-
-function TMediaCore_FFmpeg.FindAudioStreamIndex(FormatCtx: PAVFormatContext): integer;
-var
- i: integer;
- StreamIndex: integer;
- Stream: PAVStream;
-begin
- // find the first audio stream
- StreamIndex := -1;
-
- for i := 0 to FormatCtx^.nb_streams-1 do
- begin
- Stream := FormatCtx^.streams[i];
-
-{$IF LIBAVCODEC_VERSION < 52064000} // < 52.64.0
- if (Stream.codec^.codec_type = CODEC_TYPE_AUDIO) then
-{$ELSE}
- if (Stream.codec^.codec_type = AVMEDIA_TYPE_AUDIO) then
-{$IFEND}
- begin
- StreamIndex := i;
- Break;
- end;
- end;
-
- Result := StreamIndex;
-end;
-
-function TMediaCore_FFmpeg.ConvertFFmpegToAudioFormat(FFmpegFormat: TSampleFormat; out Format: TAudioSampleFormat): boolean;
-begin
- case FFmpegFormat of
- SAMPLE_FMT_U8: Format := asfU8;
- SAMPLE_FMT_S16: Format := asfS16;
- SAMPLE_FMT_S32: Format := asfS32;
- SAMPLE_FMT_FLT: Format := asfFloat;
- SAMPLE_FMT_DBL: Format := asfDouble;
- else begin
- Result := false;
- Exit;
- end;
- end;
- Result := true;
-end;
-
-
-{**
- * UTF-8 Filename wrapper based on:
- * http://www.mail-archive.com/libav-user@mplayerhq.hu/msg02460.html
- *}
-
-function FFmpegStreamOpen(h: PURLContext; filename: PChar; flags: cint): cint; cdecl;
-var
- Stream: TStream;
- Mode: word;
- ProtPrefix: string;
- FilePath: IPath;
-begin
- // check for protocol prefix ('ufile:') and strip it
- ProtPrefix := Format('%s:', [UTF8FileProtocol.name]);
- if (StrLComp(filename, PChar(ProtPrefix), Length(ProtPrefix)) = 0) then
- begin
- Inc(filename, Length(ProtPrefix));
- end;
-
- FilePath := Path(filename);
-
- if ((flags and URL_RDWR) <> 0) then
- Mode := fmCreate
- else if ((flags and URL_WRONLY) <> 0) then
- Mode := fmCreate // TODO: fmCreate is Read+Write -> reopen with fmOpenWrite
- else
- Mode := fmOpenRead or fmShareDenyWrite;
-
- Result := 0;
-
- try
- Stream := TBinaryFileStream.Create(FilePath, Mode);
- h.priv_data := Stream;
- except
- Result := AVERROR_NOENT;
- end;
-end;
-
-function FFmpegStreamRead(h: PURLContext; buf: PByteArray; size: cint): cint; cdecl;
-var
- Stream: TStream;
-begin
- Stream := TStream(h.priv_data);
- if (Stream = nil) then
- raise EInvalidContainer.Create('FFmpegStreamRead on nil');
- try
- Result := Stream.Read(buf[0], size);
- except
- Result := -1;
- end;
-end;
-
-function FFmpegStreamWrite(h: PURLContext; buf: PByteArray; size: cint): cint; cdecl;
-var
- Stream: TStream;
-begin
- Stream := TStream(h.priv_data);
- if (Stream = nil) then
- raise EInvalidContainer.Create('FFmpegStreamWrite on nil');
- try
- Result := Stream.Write(buf[0], size);
- except
- Result := -1;
- end;
-end;
-
-function FFmpegStreamSeek(h: PURLContext; pos: int64; whence: cint): int64; cdecl;
-var
- Stream : TStream;
- Origin : TSeekOrigin;
-begin
- Stream := TStream(h.priv_data);
- if (Stream = nil) then
- raise EInvalidContainer.Create('FFmpegStreamSeek on nil');
- case whence of
- 0 {SEEK_SET}: Origin := soBeginning;
- 1 {SEEK_CUR}: Origin := soCurrent;
- 2 {SEEK_END}: Origin := soEnd;
- AVSEEK_SIZE: begin
- Result := Stream.Size;
- Exit;
- end
- else
- Origin := soBeginning;
- end;
- Result := Stream.Seek(pos, Origin);
-end;
-
-function FFmpegStreamClose(h: PURLContext): cint; cdecl;
-var
- Stream : TStream;
-begin
- Stream := TStream(h.priv_data);
- Stream.Free;
- Result := 0;
-end;
-
-end.
diff --git a/mediaplugin/src/media/UMediaPlugin.pas b/mediaplugin/src/media/UMediaPlugin.pas
index c676619c..2eb0772e 100644
--- a/mediaplugin/src/media/UMediaPlugin.pas
+++ b/mediaplugin/src/media/UMediaPlugin.pas
@@ -80,6 +80,7 @@ type
PAudioDecodeStream = Pointer;
PAudioConvertStream = Pointer;
+ PVideoDecodeStream = Pointer;
PCAudioFormatInfo = ^TCAudioFormatInfo;
TCAudioFormatInfo = record
@@ -112,6 +113,20 @@ type
getRatio: function(stream: PAudioConvertStream): double; cdecl;
end;
+ PVideoDecoderInfo = ^TVideoDecoderInfo;
+ TVideoDecoderInfo = record
+ open: function(filename: PAnsiChar): PVideoDecodeStream; cdecl;
+ close: procedure(stream: PVideoDecodeStream); cdecl;
+ setLoop: procedure(stream: PVideoDecodeStream; enable: cbool); cdecl;
+ getLoop: function(stream: PVideoDecodeStream): cbool; cdecl;
+ setPosition: procedure(stream: PVideoDecodeStream; time: double); cdecl;
+ getPosition: function(stream: PVideoDecodeStream): double; cdecl;
+ getFrameWidth: function(stream: PVideoDecodeStream): cint; cdecl;
+ getFrameHeight: function(stream: PVideoDecodeStream): cint; cdecl;
+ getFrameAspect: function(stream: PVideoDecodeStream): double; cdecl;
+ getFrame: function (stream: PVideoDecodeStream; time: clongdouble): PCuint8; cdecl;
+ end;
+
PMediaPluginInfo = ^TMediaPluginInfo;
TMediaPluginInfo = record
version: cint;
@@ -120,6 +135,7 @@ type
finalize: function(): cbool; cdecl;
audioDecoder: PAudioDecoderInfo;
audioConverter: PAudioConverterInfo;
+ videoDecoder: PVideoDecoderInfo;
end;
Plugin_registerFunc = function(core: PMediaPluginCore): PMediaPluginInfo; cdecl;
diff --git a/mediaplugin/src/media/UVideoDecoder_FFmpeg.pas b/mediaplugin/src/media/UVideoDecoder_FFmpeg.pas
index b781b0d2..51916276 100644
--- a/mediaplugin/src/media/UVideoDecoder_FFmpeg.pas
+++ b/mediaplugin/src/media/UVideoDecoder_FFmpeg.pas
@@ -25,14 +25,6 @@
unit UVideoDecoder_FFmpeg;
-{*
- * based on 'An ffmpeg and SDL Tutorial' (http://www.dranger.com/ffmpeg/)
- *}
-
-// uncomment if you want to see the debug stuff
-{.$define DebugDisplay}
-{.$define DebugFrames}
-
interface
{$IFDEF FPC}
@@ -41,80 +33,24 @@ interface
{$I switches.inc}
-// use BGR-format for accelerated colorspace conversion with swscale
-{$IFDEF UseSWScale}
- {$DEFINE PIXEL_FMT_BGR}
-{$ENDIF}
-
implementation
uses
SysUtils,
Math,
- avcodec,
- avformat,
- avutil,
- avio,
- rational,
- {$IFDEF UseSWScale}
- swscale,
- {$ENDIF}
- UMediaCore_FFmpeg,
+ UMediaPlugin,
UCommon,
UConfig,
ULog,
UMusic,
UPath;
-{$DEFINE PIXEL_FMT_BGR}
-
-const
-{$IFDEF PIXEL_FMT_BGR}
- PIXEL_FMT_FFMPEG = PIX_FMT_BGR24;
- PIXEL_FMT_SIZE = 3;
-
- // looks strange on linux:
- //PIXEL_FMT_FFMPEG = PIX_FMT_BGR32;
- //PIXEL_FMT_SIZE = 4;
-{$ELSE}
- // looks strange on linux:
- PIXEL_FMT_FFMPEG = PIX_FMT_RGB24;
- PIXEL_FMT_SIZE = 3;
-{$ENDIF}
-
type
TVideoDecodeStream_FFmpeg = class (TVideoDecodeStream)
private
- fOpened: boolean; //**< stream successfully opened
- fEOF: boolean; //**< end-of-file state
-
- fLoop: boolean; //**< looping enabled
-
- fStream: PAVStream;
- fStreamIndex : integer;
- fFormatContext: PAVFormatContext;
- fCodecContext: PAVCodecContext;
- fCodec: PAVCodec;
-
- fAVFrame: PAVFrame;
- fAVFrameRGB: PAVFrame;
-
- fFrameBuffer: PByte; //**< stores a FFmpeg video frame
- fFrameTexValid: boolean; //**< if true, fFrameTex contains the current frame
-
- {$IFDEF UseSWScale}
- fSwScaleContext: PSwsContext;
- {$ENDIF}
-
- fAspect: real; //**< width/height ratio
-
- fFrameDuration: extended; //**< duration of a video frame in seconds (= 1/fps)
- fFrameTime: extended; //**< video time position (absolute)
- fLoopTime: extended; //**< start time of the current loop
-
- procedure Reset();
- function DecodeFrame(): boolean;
- procedure SynchronizeTime(Frame: PAVFrame; var pts: double);
+ private
+ fFilename: IPath;
+ fStream: PVideoDecodeStream;
public
constructor Create;
@@ -138,9 +74,10 @@ type
TVideoDecoder_FFmpeg = class( TInterfacedObject, IVideoDecoder )
private
- fInitialized: boolean;
-
+ fPluginInfo: PMediaPluginInfo;
public
+ constructor Create();
+
function GetName: String;
function InitializeDecoder(): boolean;
@@ -150,60 +87,33 @@ type
end;
var
- FFmpegCore: TMediaCore_FFmpeg;
-
-
-// These are called whenever we allocate a frame buffer.
-// We use this to store the global_pts in a frame at the time it is allocated.
-function PtsGetBuffer(CodecCtx: PAVCodecContext; Frame: PAVFrame): integer; cdecl;
-var
- pts: Pint64;
- VideoPktPts: Pint64;
-begin
- Result := avcodec_default_get_buffer(CodecCtx, Frame);
- VideoPktPts := CodecCtx^.opaque;
- if (VideoPktPts <> nil) then
- begin
- // Note: we must copy the pts instead of passing a pointer, because the packet
- // (and with it the pts) might change before a frame is returned by av_decode_video.
- pts := av_malloc(sizeof(int64));
- pts^ := VideoPktPts^;
- Frame^.opaque := pts;
- end;
-end;
-
-procedure PtsReleaseBuffer(CodecCtx: PAVCodecContext; Frame: PAVFrame); cdecl;
-begin
- if (Frame <> nil) then
- av_freep(@Frame^.opaque);
- avcodec_default_release_buffer(CodecCtx, Frame);
-end;
-
+ VideoDecoderInfo: PVideoDecoderInfo;
{*------------------------------------------------------------------------------
* TVideoPlayback_ffmpeg
*------------------------------------------------------------------------------}
+constructor TVideoDecoder_FFmpeg.Create();
+begin
+ inherited Create();
+ fPluginInfo := Plugin_register(MediaPluginCore);
+end;
+
function TVideoDecoder_FFmpeg.GetName: String;
begin
- result := 'FFmpeg_VideoDecoder';
+ Result := 'Plugin:VideoDecoder:' + fPluginInfo.name;
end;
function TVideoDecoder_FFmpeg.InitializeDecoder(): boolean;
begin
+ fPluginInfo.initialize();
+ VideoDecoderInfo := fPluginInfo.videoDecoder;
Result := true;
-
- if (fInitialized) then
- Exit;
- fInitialized := true;
-
- FFmpegCore := TMediaCore_FFmpeg.GetInstance();
-
- av_register_all();
end;
function TVideoDecoder_FFmpeg.FinalizeDecoder(): boolean;
begin
+ fPluginInfo.finalize();
Result := true;
end;
@@ -228,569 +138,79 @@ end;
constructor TVideoDecodeStream_FFmpeg.Create;
begin
- Reset();
+ inherited Create();
+ fFilename := PATH_NONE;
end;
destructor TVideoDecodeStream_FFmpeg.Destroy;
begin
Close();
+ inherited;
end;
function TVideoDecodeStream_FFmpeg.Open(const FileName: IPath): boolean;
-var
- errnum: Integer;
- AudioStreamIndex: integer;
begin
Result := false;
- Reset();
-
- // use custom 'ufile' protocol for UTF-8 support
- errnum := av_open_input_file(fFormatContext, PAnsiChar('ufile:'+FileName.ToUTF8), nil, 0, nil);
- if (errnum <> 0) then
- begin
- Log.LogError('Failed to open file "'+ FileName.ToNative +'" ('+FFmpegCore.GetErrorString(errnum)+')');
- Exit;
- end;
-
- // update video info
- if (av_find_stream_info(fFormatContext) < 0) then
- begin
- Log.LogError('No stream info found', 'TVideoPlayback_ffmpeg.Open');
- Close();
- Exit;
- end;
- Log.LogInfo('VideoStreamIndex : ' + inttostr(fStreamIndex), 'TVideoPlayback_ffmpeg.Open');
-
- // find video stream
- FFmpegCore.FindStreamIDs(fFormatContext, fStreamIndex, AudioStreamIndex);
- if (fStreamIndex < 0) then
- begin
- Log.LogError('No video stream found', 'TVideoPlayback_ffmpeg.Open');
- Close();
- Exit;
- end;
-
- fStream := fFormatContext^.streams[fStreamIndex];
- fCodecContext := fStream^.codec;
- fCodec := avcodec_find_decoder(fCodecContext^.codec_id);
- if (fCodec = nil) then
- begin
- Log.LogError('No matching codec found', 'TVideoPlayback_ffmpeg.Open');
- Close();
- Exit;
- end;
-
- // set debug options
- fCodecContext^.debug_mv := 0;
- fCodecContext^.debug := 0;
-
- // detect bug-workarounds automatically
- fCodecContext^.workaround_bugs := FF_BUG_AUTODETECT;
- // error resilience strategy (careful/compliant/agressive/very_aggressive)
- //fCodecContext^.error_resilience := FF_ER_CAREFUL; //FF_ER_COMPLIANT;
- // allow non spec compliant speedup tricks.
- //fCodecContext^.flags2 := fCodecContext^.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
- errnum := avcodec_open(fCodecContext, fCodec);
- finally
- FFmpegCore.UnlockAVCodec();
- end;
- if (errnum < 0) then
- begin
- Log.LogError('No matching codec found', 'TVideoPlayback_ffmpeg.Open');
- Close();
- Exit;
- end;
-
- // register custom callbacks for pts-determination
- fCodecContext^.get_buffer := PtsGetBuffer;
- fCodecContext^.release_buffer := PtsReleaseBuffer;
-
- {$ifdef DebugDisplay}
- DebugWriteln('Found a matching Codec: '+ fCodecContext^.Codec.Name + sLineBreak +
- sLineBreak +
- ' Width = '+inttostr(fCodecContext^.width) +
- ', Height='+inttostr(fCodecContext^.height) + sLineBreak +
- ' Aspect : '+inttostr(fCodecContext^.sample_aspect_ratio.num) + '/' +
- inttostr(fCodecContext^.sample_aspect_ratio.den) + sLineBreak +
- ' Framerate : '+inttostr(fCodecContext^.time_base.num) + '/' +
- inttostr(fCodecContext^.time_base.den));
- {$endif}
-
- // allocate space for decoded frame and rgb frame
- fAVFrame := avcodec_alloc_frame();
- fAVFrameRGB := avcodec_alloc_frame();
- fFrameBuffer := av_malloc(avpicture_get_size(PIXEL_FMT_FFMPEG,
- fCodecContext^.width, fCodecContext^.height));
-
- if ((fAVFrame = nil) or (fAVFrameRGB = nil) or (fFrameBuffer = nil)) then
- begin
- Log.LogError('Failed to allocate buffers', 'TVideoPlayback_ffmpeg.Open');
- Close();
- Exit;
- end;
+ Close();
- // TODO: pad data for OpenGL to GL_UNPACK_ALIGNMENT
- // (otherwise video will be distorted if width/height is not a multiple of the alignment)
- errnum := avpicture_fill(PAVPicture(fAVFrameRGB), fFrameBuffer, PIXEL_FMT_FFMPEG,
- fCodecContext^.width, fCodecContext^.height);
- if (errnum < 0) then
- begin
- Log.LogError('avpicture_fill failed: ' + FFmpegCore.GetErrorString(errnum), 'TVideoPlayback_ffmpeg.Open');
- Close();
+ fStream := VideoDecoderInfo.open(PChar(Filename.ToUTF8()));
+ if (fStream = nil) then
Exit;
- end;
- // calculate some information for video display
- fAspect := av_q2d(fCodecContext^.sample_aspect_ratio);
- if (fAspect = 0) then
- fAspect := fCodecContext^.width /
- fCodecContext^.height
- else
- fAspect := fAspect * fCodecContext^.width /
- fCodecContext^.height;
+ fFilename := Filename;
- fFrameDuration := 1/av_q2d(fStream^.r_frame_rate);
-
- // hack to get reasonable framerate (for divx and others)
- if (fFrameDuration < 0.02) then // 0.02 <-> 50 fps
- begin
- fFrameDuration := av_q2d(fStream^.r_frame_rate);
- while (fFrameDuration > 50) do
- fFrameDuration := fFrameDuration/10;
- fFrameDuration := 1/fFrameDuration;
- end;
-
- Log.LogInfo('Framerate: '+inttostr(floor(1/fFrameDuration))+'fps', 'TVideoPlayback_ffmpeg.Open');
-
- {$IFDEF UseSWScale}
- // if available get a SWScale-context -> faster than the deprecated img_convert().
- // SWScale has accelerated support for PIX_FMT_RGB32/PIX_FMT_BGR24/PIX_FMT_BGR565/PIX_FMT_BGR555.
- // Note: PIX_FMT_RGB32 is a BGR- and not an RGB-format (maybe a bug)!!!
- // The BGR565-formats (GL_UNSIGNED_SHORT_5_6_5) is way too slow because of its
- // bad OpenGL support. The BGR formats have MMX(2) implementations but no speed-up
- // could be observed in comparison to the RGB versions.
- fSwScaleContext := sws_getContext(
- fCodecContext^.width, fCodecContext^.height,
- fCodecContext^.pix_fmt,
- fCodecContext^.width, fCodecContext^.height,
- PIXEL_FMT_FFMPEG,
- SWS_FAST_BILINEAR, nil, nil, nil);
- if (fSwScaleContext = nil) then
- begin
- Log.LogError('Failed to get swscale context', 'TVideoPlayback_ffmpeg.Open');
- Close();
- Exit;
- end;
- {$ENDIF}
-
- fOpened := true;
Result := true;
end;
-procedure TVideoDecodeStream_FFmpeg.Reset();
-begin
- // close previously opened video
- Close();
-
- fOpened := False;
- fFrameDuration := 0;
- fFrameTime := 0;
- fStream := nil;
- fStreamIndex := -1;
- fFrameTexValid := false;
-
- fEOF := false;
-
- fLoop := false;
- fLoopTime := 0;
-end;
-
procedure TVideoDecodeStream_FFmpeg.Close;
begin
- if (fFrameBuffer <> nil) then
- av_free(fFrameBuffer);
- if (fAVFrameRGB <> nil) then
- av_free(fAVFrameRGB);
- if (fAVFrame <> nil) then
- av_free(fAVFrame);
-
- fAVFrame := nil;
- fAVFrameRGB := nil;
- fFrameBuffer := nil;
-
- if (fCodecContext <> nil) then
- begin
- // avcodec_close() is not thread-safe
- FFmpegCore.LockAVCodec();
- try
- avcodec_close(fCodecContext);
- finally
- FFmpegCore.UnlockAVCodec();
- end;
- end;
-
- if (fFormatContext <> nil) then
- av_close_input_file(fFormatContext);
-
- fCodecContext := nil;
- fFormatContext := nil;
-
- fOpened := False;
-end;
-
-procedure TVideoDecodeStream_FFmpeg.SynchronizeTime(Frame: PAVFrame; var pts: double);
-var
- FrameDelay: double;
-begin
- if (pts <> 0) then
+ Self.fFilename := PATH_NONE;
+ if (fStream <> nil) then
begin
- // if we have pts, set video clock to it
- fFrameTime := pts;
- end else
- begin
- // if we aren't given a pts, set it to the clock
- pts := fFrameTime;
+ VideoDecoderInfo.close(fStream);
+ fStream := nil;
end;
- // update the video clock
- FrameDelay := av_q2d(fCodecContext^.time_base);
- // if we are repeating a frame, adjust clock accordingly
- FrameDelay := FrameDelay + Frame^.repeat_pict * (FrameDelay * 0.5);
- fFrameTime := fFrameTime + FrameDelay;
-end;
-
-{**
- * Decode a new frame from the video stream.
- * The decoded frame is stored in fAVFrame. fFrameTime is updated to the new frame's
- * time.
- * @param pts will be updated to the presentation time of the decoded frame.
- * returns true if a frame could be decoded. False if an error or EOF occured.
- *}
-function TVideoDecodeStream_FFmpeg.DecodeFrame(): boolean;
-var
- FrameFinished: Integer;
- VideoPktPts: int64;
- pbIOCtx: PByteIOContext;
- errnum: integer;
- AVPacket: TAVPacket;
- pts: double;
-begin
- Result := false;
- FrameFinished := 0;
-
- if fEOF then
- Exit;
-
- // read packets until we have a finished frame (or there are no more packets)
- while (FrameFinished = 0) do
- begin
- errnum := av_read_frame(fFormatContext, AVPacket);
- if (errnum < 0) then
- begin
- // failed to read a frame, check reason
-
- {$IF (LIBAVFORMAT_VERSION_MAJOR >= 52)}
- pbIOCtx := fFormatContext^.pb;
- {$ELSE}
- pbIOCtx := @fFormatContext^.pb;
- {$IFEND}
-
- // check for end-of-file (EOF is not an error)
- if (url_feof(pbIOCtx) <> 0) then
- begin
- fEOF := true;
- Exit;
- end;
-
- // check for errors
- if (url_ferror(pbIOCtx) <> 0) then
- begin
- Log.LogError('Video decoding file error', 'TVideoPlayback_FFmpeg.DecodeFrame');
- Exit;
- end;
-
- // url_feof() does not detect an EOF for some mov-files (e.g. deluxe.mov)
- // so we have to do it this way.
- if ((fFormatContext^.file_size <> 0) and
- (pbIOCtx^.pos >= fFormatContext^.file_size)) then
- begin
- fEOF := true;
- Exit;
- end;
-
- // error occured, log and exit
- Log.LogError('Video decoding error', 'TVideoPlayback_FFmpeg.DecodeFrame');
- Exit;
- end;
-
- // if we got a packet from the video stream, then decode it
- if (AVPacket.stream_index = fStreamIndex) then
- begin
- // save pts to be stored in pFrame in first call of PtsGetBuffer()
- VideoPktPts := AVPacket.pts;
- fCodecContext^.opaque := @VideoPktPts;
-
- // decode packet
- avcodec_decode_video(fCodecContext, fAVFrame,
- frameFinished, AVPacket.data, AVPacket.size);
-
- // reset opaque data
- fCodecContext^.opaque := nil;
-
- // update pts
- if (AVPacket.dts <> AV_NOPTS_VALUE) then
- begin
- pts := AVPacket.dts;
- end
- else if ((fAVFrame^.opaque <> nil) and
- (Pint64(fAVFrame^.opaque)^ <> AV_NOPTS_VALUE)) then
- begin
- pts := Pint64(fAVFrame^.opaque)^;
- end
- else
- begin
- pts := 0;
- end;
-
- if fStream^.start_time <> AV_NOPTS_VALUE then
- pts := pts - fStream^.start_time;
-
- pts := pts * av_q2d(fStream^.time_base);
-
- // synchronize time on each complete frame
- if (frameFinished <> 0) then
- SynchronizeTime(fAVFrame, pts);
- end;
-
- // free the packet from av_read_frame
- av_free_packet( @AVPacket );
- end;
-
- Result := true;
end;
function TVideoDecodeStream_FFmpeg.GetFrame(Time: Extended): PByteArray;
-var
- errnum: Integer;
- CurrentTime: Extended;
- TimeDiff: Extended;
- DropFrameCount: Integer;
- i: Integer;
- Success: boolean;
-const
- SKIP_FRAME_DIFF = 0.010; // start skipping if we are >= 10ms too late
begin
- Result := nil;
-
- if not fOpened then
- Exit;
-
- {*
- * Synchronization - begin
- *}
-
- // requested stream position (relative to the last loop's start)
- if (fLoop) then
- CurrentTime := Time - fLoopTime
- else
- CurrentTime := Time;
-
- // check if current texture still contains the active frame
- if (fFrameTexValid) then
- begin
- // time since the last frame was returned
- TimeDiff := CurrentTime - fFrameTime;
-
- {$IFDEF DebugDisplay}
- DebugWriteln('Time: '+inttostr(floor(Time*1000)) + sLineBreak +
- 'VideoTime: '+inttostr(floor(fFrameTime*1000)) + sLineBreak +
- 'TimeBase: '+inttostr(floor(fFrameDuration*1000)) + sLineBreak +
- 'TimeDiff: '+inttostr(floor(TimeDiff*1000)));
- {$endif}
-
- // check if time has reached the next frame
- if (TimeDiff < fFrameDuration) then
- begin
- {$ifdef DebugFrames}
- // frame delay debug display
- GoldenRec.Spawn(200,15,1,16,0,-1,ColoredStar,$00ff00);
- {$endif}
-
- {$IFDEF DebugDisplay}
- DebugWriteln('not getting new frame' + sLineBreak +
- 'Time: '+inttostr(floor(Time*1000)) + sLineBreak +
- 'VideoTime: '+inttostr(floor(fFrameTime*1000)) + sLineBreak +
- 'TimeBase: '+inttostr(floor(fFrameDuration*1000)) + sLineBreak +
- 'TimeDiff: '+inttostr(floor(TimeDiff*1000)));
- {$endif}
-
- // we do not need a new frame now
- Exit;
- end;
- end;
-
- // fetch new frame (updates fFrameTime)
- Success := DecodeFrame();
- TimeDiff := CurrentTime - fFrameTime;
-
- // check if we have to skip frames
- // Either if we are one frame behind or if the skip threshold has been reached.
- // Do not skip if the difference is less than fFrameDuration as there is no next frame.
- // Note: We assume that fFrameDuration is the length of one frame.
- if (TimeDiff >= Max(fFrameDuration, SKIP_FRAME_DIFF)) then
- begin
- {$IFDEF DebugFrames}
- //frame drop debug display
- GoldenRec.Spawn(200,55,1,16,0,-1,ColoredStar,$ff0000);
- {$ENDIF}
- {$IFDEF DebugDisplay}
- DebugWriteln('skipping frames' + sLineBreak +
- 'TimeBase: '+inttostr(floor(fFrameDuration*1000)) + sLineBreak +
- 'TimeDiff: '+inttostr(floor(TimeDiff*1000)));
- {$endif}
-
- // update video-time
- DropFrameCount := Trunc(TimeDiff / fFrameDuration);
- fFrameTime := fFrameTime + DropFrameCount*fFrameDuration;
-
- // skip frames
- for i := 1 to DropFrameCount do
- Success := DecodeFrame();
- end;
-
- // check if we got an EOF or error
- if (not Success) then
- begin
- if fLoop then
- begin
- // we have to loop, so rewind
- SetPosition(0);
- // record the start-time of the current loop, so we can
- // determine the position in the stream (fFrameTime-fLoopTime) later.
- fLoopTime := Time;
- end;
- Exit;
- end;
-
- {*
- * Synchronization - end
- *}
-
- // TODO: support for pan&scan
- //if (fAVFrame.pan_scan <> nil) then
- //begin
- // Writeln(Format('PanScan: %d/%d', [fAVFrame.pan_scan.width, fAVFrame.pan_scan.height]));
- //end;
-
- // otherwise we convert the pixeldata from YUV to RGB
- {$IFDEF UseSWScale}
- errnum := sws_scale(fSwScaleContext, @fAVFrame.data, @fAVFrame.linesize,
- 0, fCodecContext^.Height,
- @fAVFrameRGB.data, @fAVFrameRGB.linesize);
- {$ELSE}
- // img_convert from lib/ffmpeg/avcodec.pas is actually deprecated.
- // If ./configure does not find SWScale then this gives the error
- // that the identifier img_convert is not known or similar.
- // I think this should be removed, but am not sure whether there should
- // be some other replacement or a warning, Therefore, I leave it for now.
- // April 2009, mischi
- errnum := img_convert(PAVPicture(fAVFrameRGB), PIXEL_FMT_FFMPEG,
- PAVPicture(fAVFrame), fCodecContext^.pix_fmt,
- fCodecContext^.width, fCodecContext^.height);
- {$ENDIF}
-
- if (errnum < 0) then
- begin
- Log.LogError('Image conversion failed', 'TVideoPlayback_ffmpeg.GetFrame');
- Exit;
- end;
-
- if (not fFrameTexValid) then
- fFrameTexValid := true;
-
- Result := PByteArray(fAVFrameRGB^.data[0]);
+ Result := PByteArray(VideoDecoderInfo.getFrame(fStream, Time));
end;
procedure TVideoDecodeStream_FFmpeg.SetLoop(Enable: boolean);
begin
- fLoop := Enable;
- fLoopTime := 0;
+ VideoDecoderInfo.setLoop(fStream, Enable);
end;
function TVideoDecodeStream_FFmpeg.GetLoop(): boolean;
begin
- Result := fLoop;
+ Result := VideoDecoderInfo.getLoop(fStream);
end;
-{**
- * Sets the stream's position.
- * The stream is set to the first keyframe with timestamp <= Time.
- * Note that fFrameTime is set to Time no matter if the actual position seeked to is
- * at Time or the time of a preceding keyframe. fFrameTime will be updated to the
- * actual frame time when GetFrame() is called the next time.
- * @param Time new position in seconds
- *}
procedure TVideoDecodeStream_FFmpeg.SetPosition(Time: real);
-var
- SeekFlags: integer;
begin
- if not fOpened then
- Exit;
-
- if (Time < 0) then
- Time := 0;
-
- // TODO: handle fLoop-times
- //Time := Time mod VideoDuration;
-
- // Do not use the AVSEEK_FLAG_ANY here. It will seek to any frame, even
- // non keyframes (P-/B-frames). It will produce corrupted video frames as
- // FFmpeg does not use the information of the preceding I-frame.
- // The picture might be gray or green until the next keyframe occurs.
- // Instead seek the first keyframe smaller than the requested time
- // (AVSEEK_FLAG_BACKWARD). As this can be some seconds earlier than the
- // requested time, let the sync in GetFrame() do its job.
- SeekFlags := AVSEEK_FLAG_BACKWARD;
-
- fFrameTime := Time;
- fEOF := false;
- fFrameTexValid := false;
-
- if (av_seek_frame(fFormatContext,
- fStreamIndex,
- Round(Time / av_q2d(fStream^.time_base)),
- SeekFlags) < 0) then
- begin
- Log.LogError('av_seek_frame() failed', 'TVideoPlayback_ffmpeg.SetPosition');
- Exit;
- end;
-
- avcodec_flush_buffers(fCodecContext);
+ VideoDecoderInfo.setPosition(fStream, Time);
end;
function TVideoDecodeStream_FFmpeg.GetPosition: real;
begin
- Result := fFrameTime;
+ Result := VideoDecoderInfo.getPosition(fStream);
end;
function TVideoDecodeStream_FFmpeg.GetFrameWidth(): integer;
begin
- Result := fCodecContext^.width;
+ Result := VideoDecoderInfo.getFrameWidth(fStream);
end;
function TVideoDecodeStream_FFmpeg.GetFrameHeight(): integer;
begin
- Result := fCodecContext^.height;
+ Result := VideoDecoderInfo.getFrameHeight(fStream);
end;
function TVideoDecodeStream_FFmpeg.GetFrameAspect(): real;
begin
- Result := fAspect;
+ Result := VideoDecoderInfo.getFrameAspect(fStream);
end;
initialization