Reputation:
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
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
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
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...
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.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;
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;
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);
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 + '.###';
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