user1099053
user1099053

Reputation:

TMediaPlayerControl Video Size

Good morning guys!

I'm currently experimenting with XE3, and i'm interested in building a video (and general media) player using FM2. I'm facing an issue regarding video scale, or rather, the seemingly complete lack of support for it.

I added a TMediaPlayer and a TMediaPlayerControl to a form and hooked them up. I then added a button with simple play/stop functionality, and another to load a video file. However, every video that's played does so at it's actual resolution (despite align being set to alClient). I've looked as far as i can through both the documentation and the source, and i can't seem to find any way of scaling or re-sizing the actual video area. The ONLY exception to this is if i resize the actual window to be smaller than the video area, at which point it'll scale down while maintaining the aspect ratio.

As an additional note, the video area appears above all standard controls regardless of it being "Sent to Back" or not.

At the very least, i'd appreciate any input on resizing, or scaling, the video area with the new FM²/Firemonkey2 XE3 media components. Is it currently possible, or are we going to be waiting on patches to improve the implementation?

Upvotes: 1

Views: 5052

Answers (3)

user1981552
user1981552

Reputation: 1

For a real stretch, I suggest the following changes to the MyRectFit function:

  function MyRectFit(var R: TRectF; const BoundsRect: TRectF): Single;
  var
    RatioX, RatioY: Single;
  begin
    Result := 1;
    if BoundsRect.Width * BoundsRect.Height = 0 then
      Exit;

    RatioX := R.Width / BoundsRect.Width;
    RatioY := R.Height / BoundsRect.Height;
    R := RectF(0, 0, R.Width / RatioX, R.Height / RatioY);

    Result := RatioX;
    RectCenter(R, BoundsRect);
  end;

Upvotes: 0

laggyluk
laggyluk

Reputation: 195

And if you don't have access to sources you can write a class helper:

unit mediaPlayerStretchFix;

interface
uses windows,FMX.Platform.Win,FMX.Media.Win,FMX.Forms, system.types, fmx.controls,
     system.Classes,directshow9;

type

  TMediaPlayerTurbo = class helper for TWindowsMedia
  private
    function getFWnd: HWND;
    function getFControl: TControl;
    function getVMRWC: IVMRWindowlessControl9;
    property leFWnd:HWND read getFWnd;
    property leControl:TControl read getFControl;
    property leFVMRWindowlessControl:IVMRWindowlessControl9 read getVMRWC;
  public
    procedure Stretch;
  end;

implementation

procedure TMediaPlayerTurbo.Stretch;
var
  P: TPointF;
  R: TRect;
  Bounds: TRectF;
  Form: TCommonCustomForm;

  // this is just an updated version of TRecF.Fit to support scaling up
  function MyRectFit(var R: TRectF; const BoundsRect: TRectF): Single;
  var
    ratio: Single;
  begin
    Result := 1;
    if BoundsRect.Width * BoundsRect.Height = 0 then
      Exit;
    if (R.Width / BoundsRect.Width) > (R.Height / BoundsRect.Height) then
      ratio := R.Width / BoundsRect.Width
    else
      ratio := R.Height / BoundsRect.Height;

    // UPDATED
    R := RectF(0, 0, R.Width / ratio, R.Height / ratio);

    Result := ratio;
    RectCenter(R, BoundsRect);
  end;

begin
  if leFWnd <> 0 then
  begin
    if (leControl <> nil) and not(csDesigning in Control.ComponentState) and
      (Control.ParentedVisible) and (Control.Root <> nil) and
      (Control.Root.GetObject is TCommonCustomForm) then
    begin
      Form := TCommonCustomForm(Control.Root.GetObject);
      P := self.GetVideoSize;
      Bounds := TRectF.Create(0, 0, P.X, P.Y);

      // UPDATED:
      // Bounds.Fit(RectF(0, 0, Control.AbsoluteWidth, Control.AbsoluteHeight));
      MyRectFit(Bounds, RectF(0, 0, Control.AbsoluteWidth, Control.AbsoluteHeight));

      Bounds.Offset(Control.AbsoluteRect.Left, Control.AbsoluteRect.Top);
      SetParent(leFWnd, FmxHandleToHWND(Form.Handle));
      SetWindowPos(leFWnd, 0, Bounds.Round.Left, Bounds.Round.Top, Bounds.Round.Width,
                   Bounds.Round.Height, 0);
      R.Create(0, 0, Bounds.Round.Width, Bounds.Round.Height);
      if leFVMRWindowlessControl <> nil then
        leFVMRWindowlessControl.SetVideoPosition(nil, @R);
      ShowWindow(leFWnd, SW_SHOW)
    end
    else
      ShowWindow(leFWnd, SW_HIDE)
  end;
end;

function TMediaPlayerTurbo.getFControl: TControl;
begin
  result:=TControl(fCOntrol);
end;

function TMediaPlayerTurbo.getFWnd: HWND;
begin
  result:=self.fWnd;
end;


function TMediaPlayerTurbo.getVMRWC: IVMRWindowlessControl9;
begin
  result:=self.FVMRWindowlessControl;
end;

end.

test:

var
  mp:TWindowsMedia
begin
  mp:=TWindowsMedia.create(filename);
  mp.Control:=videoframe;
  mp.Play;
  mp.Stretch;
end;

Upvotes: 4

kmikey
kmikey

Reputation: 61

Currently FM2's built-in implementation does not support a stretched view.

To solve this is actually to work around this bug... Sorry, it will be a long post :)

The magic to adjust video size to the containing control (typically a TMediaPlayerControl) is done in TMedia.UpdateMediaFromControl method, more precisely in the platform-specific TMedia-descendant's UpdateMediaFromControl (TWindowsMedia.UpdateMediaFromControl on win).

The method uses TRectF.Fit to adjust the video size to the control's client area. This method only supports scaling down but not scaling up. So you may want to change this...

My solution may not be perfect but it works for me...

  1. Create your own TMedia descendant (TMyMedia) by copy-pasting your platform specific implementation from FM2. (eg. FMX.Media.Win.TWindowsMedia). Sadly, creating a descendant class will not work since Embarcadero developers made all the necessary fields private so a descendant class will have no access to them. Make sure to copy-paste all the methods and leave them as they are.
  2. Update only UpdateMediaFromControl:

    procedure TMyMedia.UpdateMediaFromControl;
    var
      P: TPointF;
      R: TRect;
      Bounds: TRectF;
      Form: TCommonCustomForm;
    
      // this is just an updated version of TRecF.Fit to support scaling up      
      function MyRectFit(var R: TRectF; const BoundsRect: TRectF): Single;
      var
        ratio: Single;
      begin
        Result := 1;
        if BoundsRect.Width * BoundsRect.Height = 0 then
          Exit;
    
        if (R.Width / BoundsRect.Width) > (R.Height / BoundsRect.Height) then
          ratio := R.Width / BoundsRect.Width
        else
          ratio := R.Height / BoundsRect.Height;
    
        // UPDATED
        R := RectF(0, 0, R.Width / ratio, R.Height / ratio);
    
        Result := ratio;
        RectCenter(R, BoundsRect);
      end;
    
    begin
      if FWnd <> 0 then
      begin
        if (Control <> nil) and not(csDesigning in Control.ComponentState) and
          (Control.ParentedVisible) and (Control.Root <> nil) and 
          (Control.Root.GetObject is TCommonCustomForm) then
        begin
          Form := TCommonCustomForm(Control.Root.GetObject);
          P := GetVideoSize;
          Bounds := TRectF.Create(0, 0, P.X, P.Y);
    
          // UPDATED:
          // Bounds.Fit(RectF(0, 0, Control.AbsoluteWidth, Control.AbsoluteHeight));
          MyRectFit(Bounds, RectF(0, 0, Control.AbsoluteWidth, Control.AbsoluteHeight));
    
          Bounds.Offset(Control.AbsoluteRect.Left, Control.AbsoluteRect.Top);
          SetParent(FWnd, FmxHandleToHWND(Form.Handle));
          SetWindowPos(FWnd, 0, Bounds.Round.Left, Bounds.Round.Top, Bounds.Round.Width,
                       Bounds.Round.Height, 0);
          R.Create(0, 0, Bounds.Round.Width, Bounds.Round.Height);
          if FVMRWindowlessControl <> nil then
            FVMRWindowlessControl.SetVideoPosition(nil, @R);
          ShowWindow(FWnd, SW_SHOW)
        end
        else
          ShowWindow(FWnd, SW_HIDE)
      end;
    end;
    
  3. The trick is done, let's make FM2 use it. FM2 uses a TMediaCodecManager class to pair media type (file extension on win) and TCustomMediaCodec-descendant(s) to play it. Windows implementation uses TWindowsMediaCodec for all supported media file formats. TCustomMediaCodec has only one method: CreateFromFile which is supposed to create a TMedia-descendant class to play the media file. You will have to create your own TCustomMediaCodec-descendant to make use of your very own TMyMedia...

    type
      TMyMediaCodec = class(TCustomMediaCodec)
      public
        function CreateFromFile(const AFileName: string): TMedia; override;
      end;
    
    function TMyMediaCodec.CreateFromFile(const AFileName: string): TMedia;
    begin
      // LeftStr is for the extension trick - see later
      Result := TMyMedia.Create(LeftStr(AFileName, Length(AFileName) - 4));
    end;
    
  4. Let's tell TMediaCodecManager to use our "codec"... FMX.Media.Win adds all the supported media file extensions to the list in the initialization section, and there's no way to remove or change them so we'll have work around this. Register our own extension, eg. .### with our TMyMediaCodec.

    TMediaCodecManager.RegisterMediaCodecClass('.###', 'My Media Codec', TMediaType.Video, TMyMediaCodec);
    
  5. To use it, you'll have to append the .### extension to all media file names when assigning to TMediaPlayer.FileName and of course this extension has to be removed before attempting to play the file (see TMyMediaCodec.CreateFromFile above).

    MediaPlayer1.FileName := OpenDialog1.FileName + '.###';
    
  6. When calling TMediaCodecManager.GetFilterString for a list of supported media file types, you we'll have to manually remove your .### extension before using the list eg. in TOpenDialog.Filter.

I know this solution may not be the most elegant but it works for me until Embarcadero is willing to update FM2.

Upvotes: 6

Related Questions