From eb04519b61a3af0a46f59bb71a9ce7634023a70e Mon Sep 17 00:00:00 2001
From: tobigun <tobigun@b956fd51-792f-4845-bead-9b4dfca2ff2c>
Date: Fri, 31 Oct 2008 14:56:19 +0000
Subject: - cleanup (private and protected class members prepended with f...) -
 TAspectCorrection introduced (default: crop)

git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/trunk@1487 b956fd51-792f-4845-bead-9b4dfca2ff2c
---
 src/media/UVideo.pas | 480 ++++++++++++++++++++++++++++-----------------------
 1 file changed, 266 insertions(+), 214 deletions(-)

(limited to 'src')

diff --git a/src/media/UVideo.pas b/src/media/UVideo.pas
index 84949766..9f3fc6da 100644
--- a/src/media/UVideo.pas
+++ b/src/media/UVideo.pas
@@ -27,9 +27,6 @@ unit UVideo;
 
 {*
  * based on 'An ffmpeg and SDL Tutorial' (http://www.dranger.com/ffmpeg/)
- *
- * http://www.mail-archive.com/fpc-pascal@lists.freepascal.org/msg09949.html
- * http://www.nabble.com/file/p11795857/mpegpas01.zip
  *}
 
 // uncomment if you want to see the debug stuff
@@ -51,6 +48,24 @@ interface
   {$DEFINE PIXEL_FMT_BGR}
 {$ENDIF}
 
+type
+  {**
+   * vacStretch: Stretch to screen width and height
+   *   - ignores aspect
+   *   + no borders
+   *   + no image data loss
+   * vacCrop: Stretch to screen width or height, crop the other dimension
+   *   + keeps aspect
+   *   + no borders
+   *   - frame borders are cropped (image data loss)
+   * vacLetterBox: Stretch to screen width, add bars at or crop top and bottom
+   *   + keeps aspect
+   *   - borders at top and bottom
+   *   o top/bottom is cropped if width < height (unusual)
+   *}
+  TAspectCorrection = (acoStretch, acoCrop, acoLetterBox);
+
+
 implementation
 
 uses
@@ -86,40 +101,52 @@ const
 {$ENDIF}
 
 type
+  TRectCoords = record
+    Left, Right:  double;
+    Upper, Lower: double;
+  end;
+
   TVideoPlayback_FFmpeg = class( TInterfacedObject, IVideoPlayback )
   private
-    fVideoOpened,
-    fVideoPaused: Boolean;
+    fOpened: boolean;     //**< stream successfully opened
+    fPaused: boolean;     //**< stream paused
+    fInitialized: boolean;
+    fEOF: boolean;        //**< end-of-file state
 
-    VideoStream: PAVStream;
-    VideoStreamIndex : Integer;
-    VideoFormatContext: PAVFormatContext;
-    VideoCodecContext: PAVCodecContext;
-    VideoCodec: PAVCodec;
+    fLoop: boolean;       //**< looping enabled
 
-    AVFrame: PAVFrame;
-    AVFrameRGB: PAVFrame;
-    FrameBuffer: PByte;
+    fStream:        PAVStream;
+    fStreamIndex :  integer;
+    fFormatContext: PAVFormatContext;
+    fCodecContext:  PAVCodecContext;
+    fCodec:         PAVCodec;
 
-    {$IFDEF UseSWScale}
-    SoftwareScaleContext: PSwsContext;
-    {$ENDIF}
-
-    fVideoTex: GLuint;
-    TexWidth, TexHeight: Cardinal;
+    fAVFrame:     PAVFrame;
+    fAVFrameRGB:  PAVFrame;
 
-    VideoAspect: Real;
-    VideoTimeBase, VideoTime: Extended;
-    fLoopTime: Extended;
+    fFrameBuffer: PByte;  //**< stores a FFmpeg video frame
+    fFrameTex:    GLuint; //**< OpenGL texture for FrameBuffer
+    fTexWidth, fTexHeight: cardinal;
 
-    EOF: boolean;
-    Loop: boolean;
+    {$IFDEF UseSWScale}
+    fSwScaleContext: PSwsContext;
+    {$ENDIF}
 
-    Initialized: boolean;
+    fAspect: real;        //**< width/height ratio
+    fAspectCorrection: TAspectCorrection;
     
+    fTimeBase: extended;  //**< FFmpeg time base per time unit
+    fTime:     extended;  //**< video time position (absolute)
+    fLoopTime: extended;  //**< video time position (relative to current loop)
+
     procedure Reset();
     function DecodeFrame(var AVPacket: TAVPacket; out pts: double): boolean;
     procedure SynchronizeVideo(Frame: PAVFrame; var pts: double);
+
+    procedure GetVideoRect(var ScreenRect, TexRect: TRectCoords);
+    
+    procedure ShowDebugInfo();
+
   public
     function    GetName: String;
 
@@ -184,21 +211,21 @@ function TVideoPlayback_FFmpeg.Init(): boolean;
 begin
   Result := true;
 
-  if (Initialized) then
+  if (fInitialized) then
     Exit;
-  Initialized := true;
+  fInitialized := true;
 
   FFmpegCore := TMediaCore_FFmpeg.GetInstance();
 
   Reset();
   av_register_all();
-  glGenTextures(1, PGLuint(@fVideoTex));
+  glGenTextures(1, PGLuint(@fFrameTex));
 end;
 
 function TVideoPlayback_FFmpeg.Finalize(): boolean;
 begin
   Close();
-  glDeleteTextures(1, PGLuint(@fVideoTex));
+  glDeleteTextures(1, PGLuint(@fFrameTex));
   Result := true;
 end;
 
@@ -207,18 +234,20 @@ begin
   // close previously opened video
   Close();
 
-  fVideoOpened       := False;
-  fVideoPaused       := False;
-  VideoTimeBase      := 0;
-  VideoTime          := 0;
-  VideoStream := nil;
-  VideoStreamIndex := -1;
+  fOpened       := False;
+  fPaused       := False;
+  fTimeBase      := 0;
+  fTime          := 0;
+  fStream := nil;
+  fStreamIndex := -1;
 
-  EOF := false;
+  fEOF := false;
 
   // TODO: do we really want this by default?
-  Loop := true;
+  fLoop := true;
   fLoopTime := 0;
+  
+  fAspectCorrection := acoCrop;
 end;
 
 function TVideoPlayback_FFmpeg.Open(const aFileName : string): boolean; // true if succeed
@@ -230,7 +259,7 @@ begin
 
   Reset();
 
-  errnum := av_open_input_file(VideoFormatContext, PChar(aFileName), nil, 0, nil);
+  errnum := av_open_input_file(fFormatContext, PChar(aFileName), nil, 0, nil);
   if (errnum <> 0) then
   begin
     Log.LogError('Failed to open file "'+aFileName+'" ('+FFmpegCore.GetErrorString(errnum)+')');
@@ -238,28 +267,28 @@ begin
   end;
 
   // update video info
-  if (av_find_stream_info(VideoFormatContext) < 0) then
+  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(VideoStreamIndex), 'TVideoPlayback_ffmpeg.Open');
+  Log.LogInfo('VideoStreamIndex : ' + inttostr(fStreamIndex), 'TVideoPlayback_ffmpeg.Open');
 
   // find video stream
-  FFmpegCore.FindStreamIDs(VideoFormatContext, VideoStreamIndex, AudioStreamIndex);
-  if (VideoStreamIndex < 0) then
+  FFmpegCore.FindStreamIDs(fFormatContext, fStreamIndex, AudioStreamIndex);
+  if (fStreamIndex < 0) then
   begin
     Log.LogError('No video stream found', 'TVideoPlayback_ffmpeg.Open');
     Close();
     Exit;
   end;
 
-  VideoStream := VideoFormatContext^.streams[VideoStreamIndex];
-  VideoCodecContext := VideoStream^.codec;
+  fStream := fFormatContext^.streams[fStreamIndex];
+  fCodecContext := fStream^.codec;
 
-  VideoCodec := avcodec_find_decoder(VideoCodecContext^.codec_id);
-  if (VideoCodec = nil) then
+  fCodec := avcodec_find_decoder(fCodecContext^.codec_id);
+  if (fCodec = nil) then
   begin
     Log.LogError('No matching codec found', 'TVideoPlayback_ffmpeg.Open');
     Close();
@@ -267,21 +296,21 @@ begin
   end;
 
   // set debug options
-  VideoCodecContext^.debug_mv := 0;
-  VideoCodecContext^.debug := 0;
+  fCodecContext^.debug_mv := 0;
+  fCodecContext^.debug := 0;
 
   // detect bug-workarounds automatically
-  VideoCodecContext^.workaround_bugs := FF_BUG_AUTODETECT;
+  fCodecContext^.workaround_bugs := FF_BUG_AUTODETECT;
   // error resilience strategy (careful/compliant/agressive/very_aggressive)
-  //VideoCodecContext^.error_resilience := FF_ER_CAREFUL; //FF_ER_COMPLIANT;
+  //fCodecContext^.error_resilience := FF_ER_CAREFUL; //FF_ER_COMPLIANT;
   // allow non spec compliant speedup tricks.
-  //VideoCodecContext^.flags2 := VideoCodecContext^.flags2 or CODEC_FLAG2_FAST;
+  //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(VideoCodecContext, VideoCodec);
+    errnum := avcodec_open(fCodecContext, fCodec);
   finally
     FFmpegCore.UnlockAVCodec();
   end;
@@ -293,27 +322,27 @@ begin
   end;
 
   // register custom callbacks for pts-determination
-  VideoCodecContext^.get_buffer := PtsGetBuffer;
-  VideoCodecContext^.release_buffer := PtsReleaseBuffer;
+  fCodecContext^.get_buffer := PtsGetBuffer;
+  fCodecContext^.release_buffer := PtsReleaseBuffer;
 
   {$ifdef DebugDisplay}
-  DebugWriteln('Found a matching Codec: '+ VideoCodecContext^.Codec.Name + sLineBreak +
+  DebugWriteln('Found a matching Codec: '+ fCodecContext^.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));
+    '  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
-  AVFrame := avcodec_alloc_frame();
-  AVFrameRGB := avcodec_alloc_frame();
-  FrameBuffer := av_malloc(avpicture_get_size(PIXEL_FMT_FFMPEG,
-      VideoCodecContext^.width, VideoCodecContext^.height));
+  fAVFrame := avcodec_alloc_frame();
+  fAVFrameRGB := avcodec_alloc_frame();
+  fFrameBuffer := av_malloc(avpicture_get_size(PIXEL_FMT_FFMPEG,
+      fCodecContext^.width, fCodecContext^.height));
 
-  if ((AVFrame = nil) or (AVFrameRGB = nil) or (FrameBuffer = nil)) then
+  if ((fAVFrame = nil) or (fAVFrameRGB = nil) or (fFrameBuffer = nil)) then
   begin
     Log.LogError('Failed to allocate buffers', 'TVideoPlayback_ffmpeg.Open');
     Close();
@@ -322,8 +351,8 @@ begin
 
   // 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);
+  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');
@@ -332,27 +361,27 @@ begin
   end;
 
   // calculate some information for video display
-  VideoAspect := av_q2d(VideoCodecContext^.sample_aspect_ratio);
-  if (VideoAspect = 0) then
-    VideoAspect := VideoCodecContext^.width /
-                   VideoCodecContext^.height
+  fAspect := av_q2d(fCodecContext^.sample_aspect_ratio);
+  if (fAspect = 0) then
+    fAspect := fCodecContext^.width /
+               fCodecContext^.height
   else
-    VideoAspect := VideoAspect * VideoCodecContext^.width /
-                                 VideoCodecContext^.height;
+    fAspect := fAspect * fCodecContext^.width /
+                         fCodecContext^.height;
 
-  VideoTimeBase := 1/av_q2d(VideoStream^.r_frame_rate);
+  fTimeBase := 1/av_q2d(fStream^.r_frame_rate);
 
   // hack to get reasonable timebase (for divx and others)
-  if (VideoTimeBase < 0.02) then // 0.02 <-> 50 fps
+  if (fTimeBase < 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;
+    fTimeBase := av_q2d(fStream^.r_frame_rate);
+    while (fTimeBase > 50) do
+      fTimeBase := fTimeBase/10;
+    fTimeBase := 1/fTimeBase;
   end;
 
-  Log.LogInfo('VideoTimeBase: ' + floattostr(VideoTimeBase), 'TVideoPlayback_ffmpeg.Open');
-  Log.LogInfo('Framerate: '+inttostr(floor(1/VideoTimeBase))+'fps', 'TVideoPlayback_ffmpeg.Open');
+  Log.LogInfo('VideoTimeBase: ' + floattostr(fTimeBase), 'TVideoPlayback_ffmpeg.Open');
+  Log.LogInfo('Framerate: '+inttostr(floor(1/fTimeBase))+'fps', 'TVideoPlayback_ffmpeg.Open');
 
   {$IFDEF UseSWScale}
   // if available get a SWScale-context -> faster than the deprecated img_convert().
@@ -361,13 +390,13 @@ begin
   // 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,
+  fSwScaleContext := sws_getContext(
+      fCodecContext^.width, fCodecContext^.height,
+      integer(fCodecContext^.pix_fmt),
+      fCodecContext^.width, fCodecContext^.height,
       integer(PIXEL_FMT_FFMPEG),
       SWS_FAST_BILINEAR, nil, nil, nil);
-  if (SoftwareScaleContext = nil) then
+  if (fSwScaleContext = nil) then
   begin
     Log.LogError('Failed to get swscale context', 'TVideoPlayback_ffmpeg.Open');
     Close();
@@ -375,55 +404,53 @@ begin
   end;
   {$ENDIF}
 
-  TexWidth   := Round(Power(2, Ceil(Log2(VideoCodecContext^.width))));
-  TexHeight  := Round(Power(2, Ceil(Log2(VideoCodecContext^.height))));
+  fTexWidth   := Round(Power(2, Ceil(Log2(fCodecContext^.width))));
+  fTexHeight  := Round(Power(2, Ceil(Log2(fCodecContext^.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);
+  glBindTexture(GL_TEXTURE_2D, fFrameTex);
   glTexEnvi(GL_TEXTURE_2D, GL_TEXTURE_ENV_MODE, GL_REPLACE);
-  glTexImage2D(GL_TEXTURE_2D, 0, 3, TexWidth, TexHeight, 0,
+  glTexImage2D(GL_TEXTURE_2D, 0, 3, fTexWidth, fTexHeight, 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;
-
+  fOpened := 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 (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 (VideoCodecContext <> nil) then
+  if (fCodecContext <> nil) then
   begin
     // avcodec_close() is not thread-safe
     FFmpegCore.LockAVCodec();
     try
-      avcodec_close(VideoCodecContext);
+      avcodec_close(fCodecContext);
     finally
       FFmpegCore.UnlockAVCodec();
     end;
   end;
 
-  if (VideoFormatContext <> nil) then
-    av_close_input_file(VideoFormatContext);
+  if (fFormatContext <> nil) then
+    av_close_input_file(fFormatContext);
 
-  VideoCodecContext  := nil;
-  VideoFormatContext := nil;
+  fCodecContext  := nil;
+  fFormatContext := nil;
 
-  fVideoOpened := False;
+  fOpened := False;
 end;
 
 procedure TVideoPlayback_FFmpeg.SynchronizeVideo(Frame: PAVFrame; var pts: double);
@@ -433,17 +460,17 @@ begin
   if (pts <> 0) then
   begin
     // if we have pts, set video clock to it
-    VideoTime := pts;
+    fTime := pts;
   end else
   begin
     // if we aren't given a pts, set it to the clock
-    pts := VideoTime;
+    pts := fTime;
   end;
   // update the video clock
-  FrameDelay := av_q2d(VideoCodecContext^.time_base);
+  FrameDelay := av_q2d(fCodecContext^.time_base);
   // if we are repeating a frame, adjust clock accordingly
   FrameDelay := FrameDelay + Frame^.repeat_pict * (FrameDelay * 0.5);
-  VideoTime := VideoTime + FrameDelay;
+  fTime := fTime + FrameDelay;
 end;
 
 function TVideoPlayback_FFmpeg.DecodeFrame(var AVPacket: TAVPacket; out pts: double): boolean;
@@ -456,13 +483,13 @@ begin
   Result := false;
   FrameFinished := 0;
 
-  if EOF then
+  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(VideoFormatContext, AVPacket);
+    errnum := av_read_frame(fFormatContext, AVPacket);
     if (errnum < 0) then
     begin
       // failed to read a frame, check reason
@@ -470,13 +497,13 @@ begin
       {$IF (LIBAVFORMAT_VERSION_MAJOR >= 52)}
       pbIOCtx := VideoFormatContext^.pb;
       {$ELSE}
-      pbIOCtx := @VideoFormatContext^.pb;
+      pbIOCtx := @fFormatContext^.pb;
       {$IFEND}
 
-      // check for end-of-file (eof is not an error)
+      // check for end-of-file (EOF is not an error)
       if (url_feof(pbIOCtx) <> 0) then
       begin
-        EOF := true;
+        fEOF := true;
         Exit;
       end;
 
@@ -486,10 +513,10 @@ begin
 
       // url_feof() does not detect an EOF for some mov-files (e.g. deluxe.mov)
       // so we have to do it this way.
-      if ((VideoFormatContext^.file_size <> 0) and
-          (pbIOCtx^.pos >= VideoFormatContext^.file_size)) then
+      if ((fFormatContext^.file_size <> 0) and
+          (pbIOCtx^.pos >= fFormatContext^.file_size)) then
       begin
-        EOF := true;
+        fEOF := true;
         Exit;
       end;
 
@@ -499,38 +526,38 @@ begin
     end;
 
     // if we got a packet from the video stream, then decode it
-    if (AVPacket.stream_index = VideoStreamIndex) then
+    if (AVPacket.stream_index = fStreamIndex) then
     begin
       // save pts to be stored in pFrame in first call of PtsGetBuffer()
       VideoPktPts := AVPacket.pts;
-      VideoCodecContext^.opaque := @VideoPktPts;
+      fCodecContext^.opaque := @VideoPktPts;
 
       // decode packet
-      avcodec_decode_video(VideoCodecContext, AVFrame,
+      avcodec_decode_video(fCodecContext, fAVFrame,
           frameFinished, AVPacket.data, AVPacket.size);
 
       // reset opaque data
-      VideoCodecContext^.opaque := nil;
+      fCodecContext^.opaque := nil;
 
       // update pts
       if (AVPacket.dts <> AV_NOPTS_VALUE) then
       begin
         pts := AVPacket.dts;
       end
-      else if ((AVFrame^.opaque <> nil) and
-               (Pint64(AVFrame^.opaque)^ <> AV_NOPTS_VALUE)) then
+      else if ((fAVFrame^.opaque <> nil) and
+               (Pint64(fAVFrame^.opaque)^ <> AV_NOPTS_VALUE)) then
       begin
-        pts := Pint64(AVFrame^.opaque)^;
+        pts := Pint64(fAVFrame^.opaque)^;
       end
       else
       begin
         pts := 0;
       end;
-      pts := pts * av_q2d(VideoStream^.time_base);
+      pts := pts * av_q2d(fStream^.time_base);
 
       // synchronize on each complete frame
       if (frameFinished <> 0) then
-        SynchronizeVideo(AVFrame, pts);
+        SynchronizeVideo(fAVFrame, pts);
     end;
 
     // free the packet from av_read_frame
@@ -552,26 +579,26 @@ var
 const
   FRAME_DROPCOUNT = 3;
 begin
-  if not fVideoOpened then
+  if not fOpened then
     Exit;
 
-  if fVideoPaused then
+  if fPaused then
     Exit;
 
-  // current time, relative to last loop (if any)
+  // current time, relative to last fLoop (if any)
   myTime := Time - fLoopTime;
   // time since the last frame was returned
-  TimeDifference := myTime - VideoTime;
+  TimeDifference := myTime - fTime;
 
   {$IFDEF DebugDisplay}
   DebugWriteln('Time:      '+inttostr(floor(Time*1000)) + sLineBreak +
-               'VideoTime: '+inttostr(floor(VideoTime*1000)) + sLineBreak +
-               'TimeBase:  '+inttostr(floor(VideoTimeBase*1000)) + sLineBreak +
+               'VideoTime: '+inttostr(floor(fTime*1000)) + sLineBreak +
+               'TimeBase:  '+inttostr(floor(fTimeBase*1000)) + sLineBreak +
                'TimeDiff:  '+inttostr(floor(TimeDifference*1000)));
   {$endif}
 
   // check if a new frame is needed
-  if (VideoTime <> 0) and (TimeDifference < VideoTimeBase) then
+  if (fTime <> 0) and (TimeDifference < fTimeBase) then
   begin
     {$ifdef DebugFrames}
     // frame delay debug display
@@ -581,8 +608,8 @@ begin
     {$IFDEF DebugDisplay}
     DebugWriteln('not getting new frame' + sLineBreak +
         'Time:      '+inttostr(floor(Time*1000)) + sLineBreak +
-        'VideoTime: '+inttostr(floor(VideoTime*1000)) + sLineBreak +
-        'TimeBase:  '+inttostr(floor(VideoTimeBase*1000)) + sLineBreak +
+        'VideoTime: '+inttostr(floor(fTime*1000)) + sLineBreak +
+        'TimeBase:  '+inttostr(floor(fTimeBase*1000)) + sLineBreak +
         'TimeDiff:  '+inttostr(floor(TimeDifference*1000)));
     {$endif}
 
@@ -591,11 +618,11 @@ begin
   end;
 
   // update video-time to the next frame
-  VideoTime := VideoTime + VideoTimeBase;
-  TimeDifference := myTime - VideoTime;
+  fTime := fTime + fTimeBase;
+  TimeDifference := myTime - fTime;
 
   // check if we have to skip frames
-  if (TimeDifference >= FRAME_DROPCOUNT*VideoTimeBase) then
+  if (TimeDifference >= FRAME_DROPCOUNT*fTimeBase) then
   begin
     {$IFDEF DebugFrames}
     //frame drop debug display
@@ -603,13 +630,13 @@ begin
     {$ENDIF}
     {$IFDEF DebugDisplay}
     DebugWriteln('skipping frames' + sLineBreak +
-        'TimeBase:  '+inttostr(floor(VideoTimeBase*1000)) + sLineBreak +
+        'TimeBase:  '+inttostr(floor(fTimeBase*1000)) + sLineBreak +
         'TimeDiff:  '+inttostr(floor(TimeDifference*1000)));
     {$endif}
 
     // update video-time
-    DropFrameCount := Trunc(TimeDifference / VideoTimeBase);
-    VideoTime := VideoTime + DropFrameCount*VideoTimeBase;
+    DropFrameCount := Trunc(TimeDifference / fTimeBase);
+    fTime := fTime + DropFrameCount*fTimeBase;
 
     // skip half of the frames, this is much smoother than to skip all at once
     for i := 1 to DropFrameCount (*div 2*) do
@@ -622,7 +649,7 @@ begin
 
   if (not DecodeFrame(AVPacket, pts)) then
   begin
-    if Loop then
+    if fLoop then
     begin
       // Record the time we looped. This is used to keep the loops in time. otherwise they speed
       SetPosition(0);
@@ -632,16 +659,16 @@ begin
   end;
 
   // TODO: support for pan&scan
-  //if (AVFrame.pan_scan <> nil) then
+  //if (fAVFrame.pan_scan <> nil) then
   //begin
-  //  Writeln(Format('PanScan: %d/%d', [AVFrame.pan_scan.width, AVFrame.pan_scan.height]));
+  //  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(SoftwareScaleContext, @(AVFrame.data), @(AVFrame.linesize),
-          0, VideoCodecContext^.Height,
-          @(AVFrameRGB.data), @(AVFrameRGB.linesize));
+  errnum := sws_scale(fSwScaleContext, @(fAVFrame.data), @(fAVFrame.linesize),
+          0, fCodecContext^.Height,
+          @(fAVFrameRGB.data), @(fAVFrameRGB.linesize));
   {$ELSE}
   errnum := img_convert(PAVPicture(AVFrameRGB), PIXEL_FMT_FFMPEG,
             PAVPicture(AVFrame), VideoCodecContext^.pix_fmt,
@@ -663,10 +690,10 @@ begin
   //   Or should we add padding with avpicture_fill? (check which one is faster)
   //glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
 
-  glBindTexture(GL_TEXTURE_2D, fVideoTex);
+  glBindTexture(GL_TEXTURE_2D, fFrameTex);
   glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
-      VideoCodecContext^.width, VideoCodecContext^.height,
-      PIXEL_FMT_OPENGL, GL_UNSIGNED_BYTE, AVFrameRGB^.data[0]);
+      fCodecContext^.width, fCodecContext^.height,
+      PIXEL_FMT_OPENGL, GL_UNSIGNED_BYTE, fAVFrameRGB^.data[0]);
 
   {$ifdef DebugFrames}
   //frame decode debug display
@@ -680,76 +707,94 @@ begin
   {$ENDIF}
 end;
 
-procedure TVideoPlayback_FFmpeg.DrawGL(Screen: integer);
+procedure TVideoPlayback_FFmpeg.GetVideoRect(var ScreenRect, TexRect: TRectCoords);
 var
-  TexVideoRightPos, TexVideoLowerPos: Single;
-  ScreenLeftPos,  ScreenRightPos: Single;
-  ScreenUpperPos, ScreenLowerPos: Single;
-  ScaledVideoWidth, ScaledVideoHeight: Single;
-  ScreenMidPosX, ScreenMidPosY: Single;
-  ScreenAspect: Single;
+  ScreenAspect: double;  // aspect of screen resolution
+  ScaledVideoWidth, ScaledVideoHeight: double;
 begin
-  // have a nice black background to draw on (even if there were errors opening the vid)
-  if (Screen = 1) then
-  begin
-    glClearColor(0, 0, 0, 0);
-    glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
+  // Three aspects to take into account:
+  //  1. Screen/display resolution (e.g. 1920x1080 -> 16:9)
+  //  2. Render aspect (fixed to 800x600 -> 4:3)
+  //  3. Movie aspect (video frame aspect stored in fAspect)
+  ScreenAspect := ScreenW / ScreenH;
+
+  case fAspectCorrection of
+    acoStretch: begin
+      ScaledVideoWidth  := RenderW;
+      ScaledVideoHeight := RenderH;
+    end;
+    acoCrop: begin
+      if (ScreenAspect >= fAspect) then
+      begin
+        ScaledVideoWidth  := RenderW;
+        ScaledVideoHeight := RenderH * ScreenAspect/fAspect;
+      end
+      else
+      begin
+        ScaledVideoHeight := RenderH;
+        ScaledVideoWidth  := RenderW * fAspect/ScreenAspect;
+      end;
+    end;
+    acoLetterBox: begin
+      ScaledVideoWidth  := RenderW;
+      ScaledVideoHeight := RenderH * ScreenAspect/fAspect;
+    end;
   end;
 
+  // center video
+  ScreenRect.Left  := (RenderW - ScaledVideoWidth) / 2;
+  ScreenRect.Right := ScreenRect.Left + ScaledVideoWidth;
+  ScreenRect.Upper := (RenderH - ScaledVideoHeight) / 2;
+  ScreenRect.Lower := ScreenRect.Upper + ScaledVideoHeight;
+
+  // texture contains right/lower (power-of-2) padding.
+  // Determine the texture coords of the video frame.
+  TexRect.Left  := 0;
+  TexRect.Right := fCodecContext^.width  / fTexWidth;
+  TexRect.Upper := 0;
+  TexRect.Lower := fCodecContext^.height / fTexHeight;
+end;
+
+procedure TVideoPlayback_FFmpeg.DrawGL(Screen: integer);
+var
+  ScreenRect: TRectCoords;
+  TexRect: TRectCoords;
+begin
+  // have a nice black background to draw on
+  // (even if there were errors opening the vid)
+  glClearColor(0, 0, 0, 0);
+  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
+
   // exit if there's nothing to draw
-  if (not fVideoOpened) then
+  if (not fOpened) then
     Exit;
 
   {$IFDEF VideoBenchmark}
   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;
-  ScaledVideoWidth := RenderW;
-  ScaledVideoHeight := RenderH * ScreenAspect/VideoAspect;
-
-  // Note: Scaling the width does not look good because the video might contain
-  // black borders at the top already 
-  //ScaledVideoHeight := RenderH;
-  //ScaledVideoWidth  := RenderW * VideoAspect/ScreenAspect;
-
-  // center the video
-  ScreenMidPosX := RenderW/2;
-  ScreenMidPosY := RenderH/2;
-  ScreenLeftPos  := ScreenMidPosX - ScaledVideoWidth/2;
-  ScreenRightPos := ScreenMidPosX + ScaledVideoWidth/2;
-  ScreenUpperPos := ScreenMidPosY - ScaledVideoHeight/2;
-  ScreenLowerPos := ScreenMidPosY + ScaledVideoHeight/2;
-  // the video-texture contains empty borders because its width and height must be
-  // a power of 2. So we have to determine the texture coords of the video.
-  TexVideoRightPos := VideoCodecContext^.width  / TexWidth;
-  TexVideoLowerPos := VideoCodecContext^.height / TexHeight;
+  // get texture and screen positions
+  GetVideoRect(ScreenRect, TexRect);
 
   // we could use blending for brightness control, but do we need this?
   glDisable(GL_BLEND);
 
-  // TODO: disable other stuff like lightning, etc.
-
   glEnable(GL_TEXTURE_2D);
-  glBindTexture(GL_TEXTURE_2D, fVideoTex);
+  glBindTexture(GL_TEXTURE_2D, fFrameTex);
   glColor3f(1, 1, 1);
   glBegin(GL_QUADS);
     // upper-left coord
-    glTexCoord2f(0, 0);
-    glVertex2f(ScreenLeftPos, ScreenUpperPos);
+    glTexCoord2f(TexRect.Left, TexRect.Upper);
+    glVertex2f(ScreenRect.Left, ScreenRect.Upper);
     // lower-left coord
-    glTexCoord2f(0, TexVideoLowerPos);
-    glVertex2f(ScreenLeftPos, ScreenLowerPos);
+    glTexCoord2f(TexRect.Left, TexRect.Lower);
+    glVertex2f(ScreenRect.Left, ScreenRect.Lower);
     // lower-right coord
-    glTexCoord2f(TexVideoRightPos, TexVideoLowerPos);
-    glVertex2f(ScreenRightPos, ScreenLowerPos);
+    glTexCoord2f(TexRect.Right, TexRect.Lower);
+    glVertex2f(ScreenRect.Right, ScreenRect.Lower);
     // upper-right coord
-    glTexCoord2f(TexVideoRightPos, 0);
-    glVertex2f(ScreenRightPos, ScreenUpperPos);
+    glTexCoord2f(TexRect.Right, TexRect.Upper);
+    glVertex2f(ScreenRect.Right, ScreenRect.Upper);
   glEnd;
   glDisable(GL_TEXTURE_2D);
 
@@ -758,8 +803,15 @@ begin
   Log.LogBenchmark('DrawGL', 15);
   {$ENDIF}
 
+  {$IF Defined(Info) or Defined(DebugFrames)}
+  ShowDebugInfo();
+  {$IFEND}
+end;
+
+procedure TVideoPlayback_FFmpeg.ShowDebugInfo();
+begin
   {$IFDEF Info}
-  if (fVideoSkipTime+VideoTime+VideoTimeBase < 0) then
+  if (fTime+fTimeBase < 0) then
   begin
     glColor4f(0.7, 1, 0.3, 1);
     SetFontStyle (1);
@@ -799,7 +851,7 @@ end;
 
 procedure TVideoPlayback_FFmpeg.Pause;
 begin
-  fVideoPaused := not fVideoPaused;
+  fPaused := not fPaused;
 end;
 
 procedure TVideoPlayback_FFmpeg.Stop;
@@ -810,36 +862,36 @@ procedure TVideoPlayback_FFmpeg.SetPosition(Time: real);
 var
   SeekFlags: integer;
 begin
-  if not fVideoOpened then
+  if not fOpened then
     Exit;
 
   if (Time < 0) then
     Time := 0;
 
-  // TODO: handle loop-times
+  // TODO: handle fLoop-times
   //Time := Time mod VideoDuration;
 
   // backward seeking might fail without AVSEEK_FLAG_BACKWARD
   SeekFlags := AVSEEK_FLAG_ANY;
-  if (Time < VideoTime) then
+  if (Time < fTime) then
     SeekFlags := SeekFlags or AVSEEK_FLAG_BACKWARD;
 
-  VideoTime := Time;
-  EOF := false;
+  fTime := Time;
+  fEOF := false;
 
-  if (av_seek_frame(VideoFormatContext, VideoStreamIndex, Floor(Time/VideoTimeBase), SeekFlags) < 0) then
+  if (av_seek_frame(fFormatContext, fStreamIndex, Floor(Time/fTimeBase), SeekFlags) < 0) then
   begin
     Log.LogError('av_seek_frame() failed', 'TVideoPlayback_ffmpeg.SetPosition');
     Exit;
   end;
 
-  avcodec_flush_buffers(VideoCodecContext);
+  avcodec_flush_buffers(fCodecContext);
 end;
 
 function  TVideoPlayback_FFmpeg.GetPosition: real;
 begin
   // TODO: return video-position in seconds
-  Result := VideoTime;
+  Result := fTime;
 end;
 
 initialization
-- 
cgit v1.2.3