aboutsummaryrefslogtreecommitdiffstats
path: root/Game/Code/Classes/UVideo.pas
diff options
context:
space:
mode:
authortobigun <tobigun@b956fd51-792f-4845-bead-9b4dfca2ff2c>2008-07-02 07:50:39 +0000
committertobigun <tobigun@b956fd51-792f-4845-bead-9b4dfca2ff2c>2008-07-02 07:50:39 +0000
commitfaf4c13bf41a17ce920a2194fc396f8bf7b44331 (patch)
tree0049a0dacad82a08b934167660bfabd6c8ea47a8 /Game/Code/Classes/UVideo.pas
parent67d0be6741c5466c786d8d389e34c83e1be7e3c0 (diff)
downloadusdx-faf4c13bf41a17ce920a2194fc396f8bf7b44331.tar.gz
usdx-faf4c13bf41a17ce920a2194fc396f8bf7b44331.tar.xz
usdx-faf4c13bf41a17ce920a2194fc396f8bf7b44331.zip
Audio/Video engine update:
- lyrics<->audio synchronisation (TSyncSource) - better resampling (optional support for libsamplerate) - cleaner termination of audio/video streams/devices - improved decoders and decoder infrastructure - many other improvements/cleanups Currently just for testing (not enabled by default): - Background music - Voice-Passthrough (hear what you sing) - Video VSync git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/trunk@1157 b956fd51-792f-4845-bead-9b4dfca2ff2c
Diffstat (limited to 'Game/Code/Classes/UVideo.pas')
-rw-r--r--Game/Code/Classes/UVideo.pas601
1 files changed, 282 insertions, 319 deletions
diff --git a/Game/Code/Classes/UVideo.pas b/Game/Code/Classes/UVideo.pas
index bef77728..9b55aa35 100644
--- a/Game/Code/Classes/UVideo.pas
+++ b/Game/Code/Classes/UVideo.pas
@@ -12,10 +12,11 @@
unit UVideo;
-//{$define DebugDisplay} // uncomment if u want to see the debug stuff
-//{$define DebugFrames}
-//{$define VideoBenchmark}
-//{$define Info}
+// uncomment if you want to see the debug stuff
+{.$define DebugDisplay}
+{.$define DebugFrames}
+{.$define VideoBenchmark}
+{.$define Info}
interface
@@ -25,12 +26,10 @@ interface
{$I switches.inc}
-(*
- TODO: look into av_read_play
-*)
-
-// use BGR-format for accelerated colorspace conversion with swscale
-{.$DEFINE PIXEL_FMT_BGR}
+// use BGR-format for accelerated colorspace conversion with swscale
+{$IFDEF UseSWScale}
+ {$DEFINE PIXEL_FMT_BGR}
+{$ENDIF}
implementation
@@ -45,6 +44,7 @@ uses
{$IFDEF UseSWScale}
swscale,
{$ENDIF}
+ UMediaCore_FFMpeg,
math,
gl,
glext,
@@ -66,7 +66,7 @@ const
{$ENDIF}
type
- TVideoPlayback_ffmpeg = class( TInterfacedObject, IVideoPlayback )
+ TVideoPlayback_FFMpeg = class( TInterfacedObject, IVideoPlayback )
private
fVideoOpened,
fVideoPaused: Boolean;
@@ -95,14 +95,16 @@ type
EOF: boolean;
Loop: boolean;
+ Initialized: boolean;
+
procedure Reset();
function DecodeFrame(var AVPacket: TAVPacket; out pts: double): boolean;
- function FindStreamIDs( const aFormatCtx : PAVFormatContext; out aFirstVideoStream, aFirstAudioStream : integer ): boolean;
- procedure SynchronizeVideo(pFrame: PAVFrame; var pts: double);
+ procedure SynchronizeVideo(Frame: PAVFrame; var pts: double);
public
- constructor Create();
function GetName: String;
- procedure Init();
+
+ function Init(): boolean;
+ function Finalize: boolean;
function Open(const aFileName : string): boolean; // true if succeed
procedure Close;
@@ -116,53 +118,36 @@ type
procedure GetFrame(Time: Extended);
procedure DrawGL(Screen: integer);
-
end;
var
- singleton_VideoFFMpeg : IVideoPlayback;
-
-
-
-function FFMpegErrorString(Errnum: integer): string;
-begin
- case Errnum 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(Errnum);
- end;
-end;
+ 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(pCodecCtx: PAVCodecContext; pFrame: PAVFrame): integer; cdecl;
+function PtsGetBuffer(CodecCtx: PAVCodecContext; Frame: PAVFrame): integer; cdecl;
var
pts: Pint64;
VideoPktPts: Pint64;
begin
- Result := avcodec_default_get_buffer(pCodecCtx, pFrame);
- VideoPktPts := pCodecCtx^.opaque;
+ 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^;
- pFrame^.opaque := pts;
+ Frame^.opaque := pts;
end;
end;
-procedure PtsReleaseBuffer(pCodecCtx: PAVCodecContext; pFrame: PAVFrame); cdecl;
+procedure PtsReleaseBuffer(CodecCtx: PAVCodecContext; Frame: PAVFrame); cdecl;
begin
- if (pFrame <> nil) then
- av_freep(@pFrame^.opaque);
- avcodec_default_release_buffer(pCodecCtx, pFrame);
+ if (Frame <> nil) then
+ av_freep(@Frame^.opaque);
+ avcodec_default_release_buffer(CodecCtx, Frame);
end;
@@ -170,53 +155,258 @@ end;
* TVideoPlayback_ffmpeg
*------------------------------------------------------------------------------}
-function TVideoPlayback_ffmpeg.GetName: String;
+function TVideoPlayback_FFMpeg.GetName: String;
begin
result := 'FFMpeg_Video';
end;
-{
- @param(aFormatCtx is a PAVFormatContext returned from av_open_input_file )
- @param(aFirstVideoStream is an OUT value of type integer, this is the index of the video stream)
- @param(aFirstAudioStream is an OUT value of type integer, this is the index of the audio stream)
- @returns(@true on success, @false otherwise)
-}
-function TVideoPlayback_ffmpeg.FindStreamIDs(const aFormatCtx: PAVFormatContext; out aFirstVideoStream, aFirstAudioStream: integer): boolean;
+function TVideoPlayback_FFMpeg.Init(): boolean;
+begin
+ Result := true;
+
+ if (Initialized) then
+ Exit;
+ Initialized := true;
+
+ FFMpegCore := TMediaCore_FFMpeg.GetInstance();
+
+ Reset();
+ av_register_all();
+ glGenTextures(1, PGLuint(@fVideoTex));
+end;
+
+function TVideoPlayback_FFMpeg.Finalize(): boolean;
+begin
+ Close();
+ glDeleteTextures(1, PGLuint(@fVideoTex));
+ Result := true;
+end;
+
+procedure TVideoPlayback_FFMpeg.Reset();
+begin
+ // close previously opened video
+ Close();
+
+ fVideoOpened := False;
+ fVideoPaused := False;
+ VideoTimeBase := 0;
+ VideoTime := 0;
+ VideoStream := nil;
+ VideoStreamIndex := -1;
+
+ EOF := false;
+
+ // TODO: do we really want this by default?
+ Loop := true;
+ fLoopTime := 0;
+end;
+
+function TVideoPlayback_FFMpeg.Open(const aFileName : string): boolean; // true if succeed
var
- i : integer;
- st : PAVStream;
+ errnum: Integer;
+ AudioStreamIndex: integer;
begin
- // Find the first video stream
- aFirstAudioStream := -1;
- aFirstVideoStream := -1;
+ Result := false;
- {$IFDEF DebugDisplay}
- debugwriteln('aFormatCtx.nb_streams : ' + inttostr(aFormatCtx.nb_streams));
- {$ENDIF}
+ Reset();
- for i := 0 to aFormatCtx.nb_streams-1 do
+ errnum := av_open_input_file(VideoFormatContext, PChar(aFileName), nil, 0, nil);
+ if (errnum <> 0) then
begin
- st := aFormatCtx.streams[i];
+ Log.LogError('Failed to open file "'+aFileName+'" ('+FFMpegCore.GetErrorString(errnum)+')');
+ Exit;
+ end;
- if (st.codec.codec_type = CODEC_TYPE_VIDEO) and
- (aFirstVideoStream < 0) then
- begin
- aFirstVideoStream := i;
- end;
+ // update video info
+ if (av_find_stream_info(VideoFormatContext) < 0) then
+ begin
+ Log.LogError('No stream info found', 'TVideoPlayback_ffmpeg.Open');
+ Close();
+ Exit;
+ end;
+ Log.LogInfo('VideoStreamIndex : ' + inttostr(VideoStreamIndex), 'TVideoPlayback_ffmpeg.Open');
- if (st.codec.codec_type = CODEC_TYPE_AUDIO) and
- (aFirstAudioStream < 0) then
- begin
- aFirstAudioStream := i;
+ // find video stream
+ FFMpegCore.FindStreamIDs(VideoFormatContext, VideoStreamIndex, AudioStreamIndex);
+ if (VideoStreamIndex < 0) then
+ begin
+ Log.LogError('No video stream found', 'TVideoPlayback_ffmpeg.Open');
+ Close();
+ Exit;
+ end;
+
+ VideoStream := VideoFormatContext^.streams[VideoStreamIndex];
+ VideoCodecContext := VideoStream^.codec;
+
+ VideoCodec := avcodec_find_decoder(VideoCodecContext^.codec_id);
+ if (VideoCodec = nil) then
+ begin
+ Log.LogError('No matching codec found', 'TVideoPlayback_ffmpeg.Open');
+ Close();
+ Exit;
+ end;
+
+ // set debug options
+ VideoCodecContext^.debug_mv := 0;
+ VideoCodecContext^.debug := 0;
+
+ // detect bug-workarounds automatically
+ VideoCodecContext^.workaround_bugs := FF_BUG_AUTODETECT;
+ // error resilience strategy (careful/compliant/agressive/very_aggressive)
+ //VideoCodecContext^.error_resilience := FF_ER_CAREFUL; //FF_ER_COMPLIANT;
+ // allow non spec compliant speedup tricks.
+ //VideoCodecContext^.flags2 := VideoCodecContext^.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(VideoCodecContext, VideoCodec);
+ 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
+ VideoCodecContext^.get_buffer := PtsGetBuffer;
+ VideoCodecContext^.release_buffer := PtsReleaseBuffer;
+
+ {$ifdef DebugDisplay}
+ DebugWriteln('Found a matching Codec: '+ VideoCodecContext^.Codec.Name + sLineBreak +
+ sLineBreak +
+ ' Width = '+inttostr(VideoCodecContext^.width) +
+ ', Height='+inttostr(VideoCodecContext^.height) + sLineBreak +
+ ' Aspect : '+inttostr(VideoCodecContext^.sample_aspect_ratio.num) + '/' +
+ inttostr(VideoCodecContext^.sample_aspect_ratio.den) + sLineBreak +
+ ' Framerate : '+inttostr(VideoCodecContext^.time_base.num) + '/' +
+ inttostr(VideoCodecContext^.time_base.den));
+ {$endif}
+
+ // allocate space for decoded frame and rgb frame
+ AVFrame := avcodec_alloc_frame();
+ AVFrameRGB := avcodec_alloc_frame();
+ FrameBuffer := av_malloc(avpicture_get_size(PIXEL_FMT_FFMPEG,
+ VideoCodecContext^.width, VideoCodecContext^.height));
+
+ if ((AVFrame = nil) or (AVFrameRGB = nil) or (FrameBuffer = nil)) then
+ begin
+ Log.LogError('Failed to allocate buffers', 'TVideoPlayback_ffmpeg.Open');
+ Close();
+ Exit;
+ end;
+
+ // 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(AVFrameRGB), FrameBuffer, PIXEL_FMT_FFMPEG,
+ VideoCodecContext^.width, VideoCodecContext^.height);
+ if (errnum < 0) then
+ begin
+ Log.LogError('avpicture_fill failed: ' + FFMpegCore.GetErrorString(errnum), 'TVideoPlayback_ffmpeg.Open');
+ Close();
+ Exit;
+ end;
+
+ // calculate some information for video display
+ VideoAspect := av_q2d(VideoCodecContext^.sample_aspect_ratio);
+ if (VideoAspect = 0) then
+ VideoAspect := VideoCodecContext^.width /
+ VideoCodecContext^.height
+ else
+ VideoAspect := VideoAspect * VideoCodecContext^.width /
+ VideoCodecContext^.height;
+
+ VideoTimeBase := 1/av_q2d(VideoStream^.r_frame_rate);
+
+ // hack to get reasonable timebase (for divx and others)
+ if (VideoTimeBase < 0.02) then // 0.02 <-> 50 fps
+ begin
+ VideoTimeBase := av_q2d(VideoStream^.r_frame_rate);
+ while (VideoTimeBase > 50) do
+ VideoTimeBase := VideoTimeBase/10;
+ VideoTimeBase := 1/VideoTimeBase;
+ end;
+
+ Log.LogInfo('VideoTimeBase: ' + floattostr(VideoTimeBase), 'TVideoPlayback_ffmpeg.Open');
+ Log.LogInfo('Framerate: '+inttostr(floor(1/VideoTimeBase))+'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.
+ SoftwareScaleContext := sws_getContext(
+ VideoCodecContext^.width, VideoCodecContext^.height,
+ integer(VideoCodecContext^.pix_fmt),
+ VideoCodecContext^.width, VideoCodecContext^.height,
+ integer(PIXEL_FMT_FFMPEG),
+ SWS_FAST_BILINEAR, nil, nil, nil);
+ if (SoftwareScaleContext = nil) then
+ begin
+ Log.LogError('Failed to get swscale context', 'TVideoPlayback_ffmpeg.Open');
+ Close();
+ Exit;
+ end;
+ {$ENDIF}
+
+ TexWidth := Round(Power(2, Ceil(Log2(VideoCodecContext^.width))));
+ TexHeight := Round(Power(2, Ceil(Log2(VideoCodecContext^.height))));
+
+ // we retrieve a texture just once with glTexImage2D and update it with glTexSubImage2D later.
+ // Benefits: glTexSubImage2D is faster and supports non-power-of-two widths/height.
+ glBindTexture(GL_TEXTURE_2D, fVideoTex);
+ glTexEnvi(GL_TEXTURE_2D, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ glTexImage2D(GL_TEXTURE_2D, 0, 3, TexWidth, TexHeight, 0,
+ PIXEL_FMT_OPENGL, GL_UNSIGNED_BYTE, nil);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
+
+ fVideoOpened := True;
+
+ Result := true;
+end;
+
+procedure TVideoPlayback_FFMpeg.Close;
+begin
+ if (FrameBuffer <> nil) then
+ av_free(FrameBuffer);
+ if (AVFrameRGB <> nil) then
+ av_free(AVFrameRGB);
+ if (AVFrame <> nil) then
+ av_free(AVFrame);
+
+ AVFrame := nil;
+ AVFrameRGB := nil;
+ FrameBuffer := nil;
+
+ if (VideoCodecContext <> nil) then
+ begin
+ // avcodec_close() is not thread-safe
+ FFMpegCore.LockAVCodec();
+ try
+ avcodec_close(VideoCodecContext);
+ finally
+ FFMpegCore.UnlockAVCodec();
end;
end;
- // return true if either an audio- or video-stream was found
- result := (aFirstAudioStream > -1) or
- (aFirstVideoStream > -1) ;
+ if (VideoFormatContext <> nil) then
+ av_close_input_file(VideoFormatContext);
+
+ VideoCodecContext := nil;
+ VideoFormatContext := nil;
+
+ fVideoOpened := False;
end;
-procedure TVideoPlayback_ffmpeg.SynchronizeVideo(pFrame: PAVFrame; var pts: double);
+procedure TVideoPlayback_FFMpeg.SynchronizeVideo(Frame: PAVFrame; var pts: double);
var
FrameDelay: double;
begin
@@ -232,11 +422,11 @@ begin
// update the video clock
FrameDelay := av_q2d(VideoCodecContext^.time_base);
// if we are repeating a frame, adjust clock accordingly
- FrameDelay := FrameDelay + pFrame^.repeat_pict * (FrameDelay * 0.5);
+ FrameDelay := FrameDelay + Frame^.repeat_pict * (FrameDelay * 0.5);
VideoTime := VideoTime + FrameDelay;
end;
-function TVideoPlayback_ffmpeg.DecodeFrame(var AVPacket: TAVPacket; out pts: double): boolean;
+function TVideoPlayback_FFMpeg.DecodeFrame(var AVPacket: TAVPacket; out pts: double): boolean;
var
FrameFinished: Integer;
VideoPktPts: int64;
@@ -330,7 +520,7 @@ begin
Result := true;
end;
-procedure TVideoPlayback_ffmpeg.GetFrame(Time: Extended);
+procedure TVideoPlayback_FFMpeg.GetFrame(Time: Extended);
var
AVPacket: TAVPacket;
errnum: Integer;
@@ -429,7 +619,7 @@ begin
{$ELSE}
errnum := img_convert(PAVPicture(AVFrameRGB), PIXEL_FMT_FFMPEG,
PAVPicture(AVFrame), VideoCodecContext^.pix_fmt,
- VideoCodecContext^.width, VideoCodecContext^.height);
+ VideoCodecContext^.width, VideoCodecContext^.height);
{$ENDIF}
if (errnum < 0) then
@@ -464,7 +654,7 @@ begin
{$ENDIF}
end;
-procedure TVideoPlayback_ffmpeg.DrawGL(Screen: integer);
+procedure TVideoPlayback_FFMpeg.DrawGL(Screen: integer);
var
TexVideoRightPos, TexVideoLowerPos: Single;
ScreenLeftPos, ScreenRightPos: Single;
@@ -488,6 +678,10 @@ begin
Log.BenchmarkStart(15);
{$ENDIF}
+ // TODO: add a SetAspectCorrectionMode() function so we can switch
+ // aspect correction. The screens video backgrounds look very ugly with aspect
+ // correction because of the white bars at the top and bottom.
+
ScreenAspect := ScreenW / ScreenH;
RenderAspect := RenderW / RenderH;
ScaledVideoWidth := RenderW;
@@ -574,250 +768,20 @@ begin
{$ENDIF}
end;
-constructor TVideoPlayback_ffmpeg.Create();
-begin
- inherited;
- Reset();
- av_register_all();
-end;
-
-procedure TVideoPlayback_ffmpeg.Init();
-begin
- glGenTextures(1, PGLuint(@fVideoTex));
-end;
-
-procedure TVideoPlayback_ffmpeg.Reset();
-begin
- // close previously opened video
- Close();
-
- fVideoOpened := False;
- fVideoPaused := False;
- VideoTimeBase := 0;
- VideoTime := 0;
- VideoStream := nil;
- VideoFormatContext := nil;
- VideoCodecContext := nil;
- VideoStreamIndex := -1;
-
- AVFrame := nil;
- AVFrameRGB := nil;
- FrameBuffer := nil;
-
- EOF := false;
-
- // TODO: do we really want this by default?
- Loop := true;
- fLoopTime := 0;
-end;
-
-function TVideoPlayback_ffmpeg.Open(const aFileName : string): boolean; // true if succeed
-var
- errnum: Integer;
- err: GLenum;
- AudioStreamIndex: integer;
-
- procedure CleanOnError();
- begin
- if (VideoCodecContext <> nil) then
- avcodec_close(VideoCodecContext);
- if (VideoFormatContext <> nil) then
- av_close_input_file(VideoFormatContext);
- av_free(AVFrameRGB);
- av_free(AVFrame);
- av_free(FrameBuffer);
- end;
-
-begin
- Result := false;
-
- Reset();
-
- errnum := av_open_input_file(VideoFormatContext, pchar( aFileName ), nil, 0, nil);
- if (errnum <> 0) then
- begin
- Log.LogError('Failed to open file "'+aFileName+'" ('+FFMpegErrorString(errnum)+')');
- Exit;
- end;
-
- // update video info
- if (av_find_stream_info(VideoFormatContext) < 0) then
- begin
- Log.LogError('No stream info found', 'TVideoPlayback_ffmpeg.Open');
- CleanOnError();
- Exit;
- end;
- Log.LogInfo('VideoStreamIndex : ' + inttostr(VideoStreamIndex), 'TVideoPlayback_ffmpeg.Open');
-
- // find video stream
- FindStreamIDs(VideoFormatContext, VideoStreamIndex, AudioStreamIndex);
- if (VideoStreamIndex < 0) then
- begin
- Log.LogError('No video stream found', 'TVideoPlayback_ffmpeg.Open');
- CleanOnError();
- Exit;
- end;
-
- VideoStream := VideoFormatContext^.streams[VideoStreamIndex];
- VideoCodecContext := VideoStream^.codec;
-
- VideoCodec := avcodec_find_decoder(VideoCodecContext^.codec_id);
- if (VideoCodec = nil) then
- begin
- Log.LogError('No matching codec found', 'TVideoPlayback_ffmpeg.Open');
- CleanOnError();
- Exit;
- end;
-
- // set debug options
- VideoCodecContext^.debug_mv := 0;
- VideoCodecContext^.debug := 0;
-
- // detect bug-workarounds automatically
- VideoCodecContext^.workaround_bugs := FF_BUG_AUTODETECT;
- // error resilience strategy (careful/compliant/agressive/very_aggressive)
- //VideoCodecContext^.error_resilience := FF_ER_CAREFUL; //FF_ER_COMPLIANT;
- // allow non spec compliant speedup tricks.
- //VideoCodecContext^.flags2 := VideoCodecContext^.flags2 or CODEC_FLAG2_FAST;
-
- errnum := avcodec_open(VideoCodecContext, VideoCodec);
- if (errnum < 0) then
- begin
- Log.LogError('No matching codec found', 'TVideoPlayback_ffmpeg.Open');
- CleanOnError();
- Exit;
- end;
-
- // register custom callbacks for pts-determination
- VideoCodecContext^.get_buffer := PtsGetBuffer;
- VideoCodecContext^.release_buffer := PtsReleaseBuffer;
-
- {$ifdef DebugDisplay}
- DebugWriteln('Found a matching Codec: '+ VideoCodecContext^.Codec.Name + sLineBreak +
- sLineBreak +
- ' Width = '+inttostr(VideoCodecContext^.width) +
- ', Height='+inttostr(VideoCodecContext^.height) + sLineBreak +
- ' Aspect : '+inttostr(VideoCodecContext^.sample_aspect_ratio.num) + '/' +
- inttostr(VideoCodecContext^.sample_aspect_ratio.den) + sLineBreak +
- ' Framerate : '+inttostr(VideoCodecContext^.time_base.num) + '/' +
- inttostr(VideoCodecContext^.time_base.den));
- {$endif}
-
- // allocate space for decoded frame and rgb frame
- AVFrame := avcodec_alloc_frame();
- AVFrameRGB := avcodec_alloc_frame();
- FrameBuffer := av_malloc(avpicture_get_size(PIXEL_FMT_FFMPEG,
- VideoCodecContext^.width, VideoCodecContext^.height));
-
- if ((AVFrame = nil) or (AVFrameRGB = nil) or (FrameBuffer = nil)) then
- begin
- Log.LogError('Failed to allocate buffers', 'TVideoPlayback_ffmpeg.Open');
- CleanOnError();
- Exit;
- end;
-
- // 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(AVFrameRGB), FrameBuffer, PIXEL_FMT_FFMPEG,
- VideoCodecContext^.width, VideoCodecContext^.height);
- if (errnum < 0) then
- begin
- Log.LogError('avpicture_fill failed: ' + FFMpegErrorString(errnum), 'TVideoPlayback_ffmpeg.Open');
- CleanOnError();
- Exit;
- end;
-
- // calculate some information for video display
- VideoAspect := av_q2d(VideoCodecContext^.sample_aspect_ratio);
- if (VideoAspect = 0) then
- VideoAspect := VideoCodecContext^.width /
- VideoCodecContext^.height
- else
- VideoAspect := VideoAspect * VideoCodecContext^.width /
- VideoCodecContext^.height;
-
- VideoTimeBase := 1/av_q2d(VideoStream^.r_frame_rate);
-
- // hack to get reasonable timebase (for divx and others)
- if (VideoTimeBase < 0.02) then // 0.02 <-> 50 fps
- begin
- VideoTimeBase := av_q2d(VideoStream^.r_frame_rate);
- while (VideoTimeBase > 50) do
- VideoTimeBase := VideoTimeBase/10;
- VideoTimeBase := 1/VideoTimeBase;
- end;
-
- Log.LogInfo('VideoTimeBase: ' + floattostr(VideoTimeBase), 'TVideoPlayback_ffmpeg.Open');
- Log.LogInfo('Framerate: '+inttostr(floor(1/VideoTimeBase))+'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.
- SoftwareScaleContext := sws_getContext(
- VideoCodecContext^.width, VideoCodecContext^.height,
- integer(VideoCodecContext^.pix_fmt),
- VideoCodecContext^.width, VideoCodecContext^.height,
- integer(PIXEL_FMT_FFMPEG),
- SWS_FAST_BILINEAR, nil, nil, nil);
- if (SoftwareScaleContext = nil) then
- begin
- Log.LogError('Failed to get swscale context', 'TVideoPlayback_ffmpeg.Open');
- CleanOnError();
- Exit;
- end;
- {$ENDIF}
-
- TexWidth := Round(Power(2, Ceil(Log2(VideoCodecContext^.width))));
- TexHeight := Round(Power(2, Ceil(Log2(VideoCodecContext^.height))));
-
- // we retrieve a texture just once with glTexImage2D and update it with glTexSubImage2D later.
- // Benefits: glTexSubImage2D is faster and supports non-power-of-two widths/height.
- glBindTexture(GL_TEXTURE_2D, fVideoTex);
- glTexEnvi(GL_TEXTURE_2D, GL_TEXTURE_ENV_MODE, GL_REPLACE);
- glTexImage2D(GL_TEXTURE_2D, 0, 3, TexWidth, TexHeight, 0,
- PIXEL_FMT_OPENGL, GL_UNSIGNED_BYTE, nil);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-
-
- fVideoOpened := True;
-
- Result := true;
-end;
-
-procedure TVideoPlayback_ffmpeg.Close;
-begin
- if fVideoOpened then
- begin
- av_free(FrameBuffer);
- av_free(AVFrameRGB);
- av_free(AVFrame);
-
- avcodec_close(VideoCodecContext);
- av_close_input_file(VideoFormatContext);
-
- fVideoOpened := False;
- end;
-end;
-
-procedure TVideoPlayback_ffmpeg.Play;
+procedure TVideoPlayback_FFMpeg.Play;
begin
end;
-procedure TVideoPlayback_ffmpeg.Pause;
+procedure TVideoPlayback_FFMpeg.Pause;
begin
fVideoPaused := not fVideoPaused;
end;
-procedure TVideoPlayback_ffmpeg.Stop;
+procedure TVideoPlayback_FFMpeg.Stop;
begin
end;
-procedure TVideoPlayback_ffmpeg.SetPosition(Time: real);
+procedure TVideoPlayback_FFMpeg.SetPosition(Time: real);
var
SeekFlags: integer;
begin
@@ -838,20 +802,19 @@ begin
if (av_seek_frame(VideoFormatContext, VideoStreamIndex, Floor(Time/VideoTimeBase), SeekFlags) < 0) then
begin
Log.LogError('av_seek_frame() failed', 'TVideoPlayback_ffmpeg.SetPosition');
+ Exit;
end;
+
+ avcodec_flush_buffers(VideoCodecContext);
end;
-function TVideoPlayback_ffmpeg.GetPosition: real;
+function TVideoPlayback_FFMpeg.GetPosition: real;
begin
// TODO: return video-position in seconds
- result := VideoTime;
+ Result := VideoTime;
end;
initialization
- singleton_VideoFFMpeg := TVideoPlayback_ffmpeg.create();
- AudioManager.add( singleton_VideoFFMpeg );
-
-finalization
- AudioManager.Remove( singleton_VideoFFMpeg );
+ MediaManager.Add(TVideoPlayback_FFMpeg.Create);
end.