So, I finally managed to do it somehow. I am pretty sure that my code is not elegant and there's a big room for optimizations. Particularly, I didn't understand why we need all those
opaque,
picture and
plane parameters in callbacks. But at least it works. Oh, and it's in Pascal. All corrections are welcome and will be received with gratitude.
Declaration
Code: Select all
const
video_width=800;
video_height=600;
var
vlcMutex: TCriticalSection;
vlcBuffer: array [0 .. video_width*video_height*4+32] of byte;
vlcVideoTexture: IDirect3DTexture9;
vlcMemoryTexture: IDirect3DTexture9;
pSprite: ID3DXSprite;
Initialization
Code: Select all
pd3dDevice.CreateTexture(video_width, video_height, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, vlcVideoTexture, nil);
pd3dDevice.CreateTexture(video_width, video_height, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, vlcMemoryTexture, nil);
D3DXCreateSprite(pd3dDevice, pSprite);
vlcMutex:=TCriticalSection.Create;
Deinitialization
Code: Select all
SAFE_RELEASE(vlcVideoTexture);
SAFE_RELEASE(vlcMemoryTexture);
SAFE_RELEASE(pSprite);
vlcMutex.Free;
Callbacks
Code: Select all
function VLCLock(opaque: Pointer; plane: Pointer): Pointer; cdecl;
begin
Result:=nil;
Pointer(plane^):=@(vlcBuffer[0]);
vlcMutex.Enter;
end;
function VLCUnlock(opaque: Pointer; picture: Pointer; plane: Pointer): Pointer; cdecl;
begin
Result:=nil;
vlcMutex.Leave;
end;
function VLCdisplay(opaque: Pointer; picture: Pointer): Pointer; cdecl;
var
i, j: integer;
prect: D3DLOCKED_RECT;
pTexBuffer: PByte;
begin
Result:=nil;
i:=0;
/// VLC outputs data in RGBA while we need to load data in BGRA into texture
/// so, swap all Red and Blue values
while i<=(video_width*video_height-1)*4 do
begin
j:=vlcBuffer[i];
vlcBuffer[i]:=vlcBuffer[i+2];
vlcBuffer[i+2]:=j;
inc(i, 4);
end;
/// lock system memory texture and fill it with data
if vlcMemoryTexture.LockRect(0, prect, nil, 0)= S_OK then
begin
pTexBuffer:=prect.pBits;
for i:=0 to video_height-1 do
begin
/// fill data into texture line by line
MoveMemory(pTexBuffer, @vlcBuffer[i*video_width*4], video_width*4);
/// and don't forget to offset by pitch, not by video_width*4
inc(pTexBuffer, prect.Pitch);
end;
vlcMemoryTexture.UnlockRect(0);
end;
end;
Starting playback
Code: Select all
procedure VideoPlay(videofile: PAnsiChar);
var
pMedia, pMediaPlayer: Pointer;
begin
pMedia:=libvlc_media_new_path(vlc, videofile);
pMediaPlayer:=libvlc_media_player_new_from_media(pMedia);
libvlc_media_release(pMedia);
libvlc_video_set_callbacks(pMediaPlayer, @VLCLock, @VLCUnlock, @VLCDisplay, nil);
libvlc_video_set_format(pMediaPlayer, 'RGBA', video_width, video_height, video_width*4);
libvlc_media_player_play(pMediaPlayer);
end;
Rendering
Code: Select all
procedure OnFrameRender;
var
mat: TD3DXMatrixA16;
scale: TD3DXVector2;
begin
{ Prepare to render the scene here }
pSprite._Begin(D3DXSPRITE_DONOTSAVESTATE or D3DXSPRITE_DO_NOT_ADDREF_TEXTURE);
/// update video texture which we are going to render now, from system memory texture
pd3dDevice.UpdateTexture(vlcMemoryTexture, vlcVideoTexture);
/// stretch sprite so video texture will fit in window.
/// window_width and window_height are window's dimensions
scale.x:=window_width/video_width;
scale.y:=window_height/video_height;
D3DXMatrixTransformation2D(mat, nil, 0, @scale, nil, 0, nil);
pSprite.SetTransform(mat);
/// finally, render it
pSprite.Draw( vlcVideoTexture, nil, nil, nil, $FFFFFFFF );
pSprite._End;
{ End scene rendering here}
end;