Jeg har længe rodet med mit hjemmelavede media center program i C#: SharpMedia og det varer ikke alt for længe før jeg udgiver en beta version af det her på min hjemmeside.
De første versioner af SharpMedia var blot en “glorified” fil browser, når man startede en film åbnede SharpMedia blot en 3. parts afspiller som Media Player Classic eller Zoom Player. Det synes jeg dog var lidt for trist så jeg gav mig i med at implementere DirectShow i programmet. DirectShow er Windows’ indbyggede media framework, det er det der gør at når man installerer DivX decoder på sin computer så kan alle afspillere man har afspille DivX filer.
Til det brugte jeg DirectShow.Net som er en Managed Wrapper til DirectShow. Da jeg gjorde dette første gang var det til Managed DirectX og ikke XNA, men for nyeligt portede jeg hele mit SharpMedia til XNA. Så første opgave når man laver sådan noget er at finde ud af hvordan jeg skal få videoen vist i XNA.
Der findes en løsning allerede (udviklet parallelt med det jeg foreslår) her. Den løsning bruger dog ikke samme metode, som jeg har brugt. I denne løsning bruges noget der kaldes en SampleGrabber, der stortset blot tager et screenshot som derefter kopieres over på en texture.
Jeg har valgt at bruge en Allocator/Presenter, som er en feature VMR9 har. Fordelen ved denne metode er at den er designet til at arbejde med Direct3D (som XNA er baseret på). Den virker ved at du ved start af en video laver en instans af VMR9 renderen og giver den en reference til en Direct3D surface. For hver frame af videoen bliver den surface så tegnet på af VMR9 og dit program får det af vide så du kan tegne surfacens texture i dit spil/program.
Der er flere gode grunde til at bruge denne metode frem for den anden, først er performance, det er meget tungt at kopiere en hel video frame manuelt i Managed kode, specielt hvis vi snakker High Definition content. Ved at bruge VMR9 får codecs også bedere muligheder for hardware acceleration til ting som fx deinterlacing. Det nyder man specielt godt af hvis man bruger Nvidia PureVideo til DVD afspilning. Sidst, men bestemt ikke mindst, giver denne fremgangsmåde mulighed for det der kaldes Exclusive Fullscreen. Fordi et XNA program er Direct3D kan man lave en fullscreen applikation der har 100% råderet over grafikkortet. Det giver bedere muligheder for at undgå visuelle artefakter, som screen tearing, så bedre kvalitet i sidste ende ^^
XNA Implementationen
Men hvordan får vi så adgang til disse fede features? Først og fremmest så skulle jeg finde ud af hvordan man får fat i en pointer til en Direct3D surface i XNA. Pointeren skal VMR9 bruge for at kunne tegne til surfacen. Dette er faktisk ikke en hel triviel opgave i XNA. Alt er pakket godt ind i Managed kode og vi vil jo normalt helst ikke rode rundt med pointere. Først prøvede jeg at bruge reflection til at grave ned i en Texture2D og få fat i en surface. Dette var dog ikke muligt lige umiddelbart, der var en pointer til en Direct3d9 Texture men ikke til dens surface.
Svaret ligger i RenderTarget2D. Det lyder sådan set meget logisk da man normalt bruger et rendertarget til at tegne direkte til en texture istedet for til grafikkortets backbuffer. I XNA kan et rendertarget være sat i to forskellige tilstande, et der efterligner hvordan det virker på en Xbox360, hvor indholdet af rendertargeten bliver slettet efter brug(DiscardContent), og en tilstand til windows hvor det bliver gemt (PreserveContent). For at bruge RenderTarget2D til vores allocator skal den være sat i DiscardContent, for hvis den er sat til PreserveContent findes der ikke en pointer til en Direct3D9Surface gemt dybt begravet i objektet.
Når vi har surfacen er det “bare” at implementere en Allocator/Presenter som beskrevet i linket ovenfor så har man DirectShow i XNA.
Nu har jeg selvfølgelig snydt lidt hjemmefra og kogt et lille projekt sammen der kan det grundlæggende, hvis der er nogen der interesserede i en dybere forklaring eller hjælp til at lave deres egen så sig endelig til
Download:
DirectShow in XNA using Allocator
felizk DirectShow, Programming, XNA