From 9ea6989ff0b34468fdc23d4af922af041b4e56a1 Mon Sep 17 00:00:00 2001 From: Rodrigo Braz Monteiro Date: Wed, 20 Dec 2006 00:31:52 +0000 Subject: [PATCH] Very early DirectShow Video Provider commited, mostly based on Haali's DSS2 code Originally committed to SVN as r584. --- core/VideoSink.h | 49 +++++ core/mkv_wrap.cpp | 42 +++-- core/video_provider.cpp | 23 ++- core/video_provider_dshow.cpp | 325 ++++++++++++++++++++++++++++++++++ core/video_provider_dshow.h | 113 ++++++++++++ 5 files changed, 540 insertions(+), 12 deletions(-) create mode 100644 core/VideoSink.h create mode 100644 core/video_provider_dshow.cpp create mode 100644 core/video_provider_dshow.h diff --git a/core/VideoSink.h b/core/VideoSink.h new file mode 100644 index 000000000..8ba11cf9a --- /dev/null +++ b/core/VideoSink.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2004-2006 Mike Matsnev. All Rights Reserved. + * + * $Id: VideoSink.h,v 1.4 2006/11/12 18:00:20 mike Exp $ + * + */ + +#ifndef VIDEOSINK_H +#define VIDEOSINK_H + +// callback, invoked when a new frame is ready +[uuid("8D9F1DA8-10DB-42fe-8CAC-94A02497B3DD")] +interface IVideoSinkNotify : public IUnknown { + // this may be called on a worker thread! + STDMETHOD(FrameReady)() = 0; +}; + +// supported bitmap types +#define IVS_RGB24 1 +#define IVS_RGB32 2 +#define IVS_YUY2 4 +#define IVS_YV12 8 + +typedef void (*ReadFrameFunc)(long long timestamp, unsigned format, unsigned bpp, + const unsigned char *frame, unsigned width, unsigned height, unsigned stride, + unsigned arx, unsigned ary, + void *arg); + +[uuid("6B9EFC3E-3841-42ca-ABE5-0F963C638249")] +interface IVideoSink : public IUnknown { + STDMETHOD(SetAllowedTypes)(unsigned types) = 0; + STDMETHOD(GetAllowedTypes)(unsigned *types) = 0; + + STDMETHOD(NotifyFrame)(IVideoSinkNotify *notify) = 0; + + // failure return means format is not negotiated yet + STDMETHOD(GetFrameFormat)(unsigned *type, unsigned *width, unsigned *height, unsigned *arx, unsigned *ary) = 0; + + // S_FALSE return means end of file was reached + STDMETHOD(ReadFrame)(ReadFrameFunc f, void *arg) = 0; +}; + +[uuid("80CADA0E-DFA5-4fcc-99DD-52F7C1B0E575")] +interface IVideoSink2 : public IUnknown { + STDMETHOD(NotifyFrame)(HANDLE hEvent) = 0; + STDMETHOD(GetFrameFormat)(unsigned *type, unsigned *width, unsigned *height, unsigned *arx, unsigned *ary, long long *def_duration) = 0; +}; + +#endif \ No newline at end of file diff --git a/core/mkv_wrap.cpp b/core/mkv_wrap.cpp index d6abe09a7..eb462c3af 100644 --- a/core/mkv_wrap.cpp +++ b/core/mkv_wrap.cpp @@ -193,16 +193,38 @@ void MatroskaWrapper::Parse() { rawFrames.push_back(*cur); } - // Process timecodes and keyframes - frames.sort(); - MkvFrame curFrame(false,0,0); - int i = 0; - for (std::list::iterator cur=frames.begin();cur!=frames.end();cur++) { - curFrame = *cur; - if (curFrame.isKey) keyFrames.Add(i); - bytePos.Add(curFrame.filePos); - timecodes.push_back(curFrame.time); - i++; + bool sortFirst = true; + if (sortFirst) { + // Process timecodes and keyframes + frames.sort(); + MkvFrame curFrame(false,0,0); + int i = 0; + for (std::list::iterator cur=frames.begin();cur!=frames.end();cur++) { + curFrame = *cur; + if (curFrame.isKey) keyFrames.Add(i); + bytePos.Add(curFrame.filePos); + timecodes.push_back(curFrame.time); + i++; + } + } + + else { + // Process keyframes + MkvFrame curFrame(false,0,0); + int i = 0; + for (std::list::iterator cur=frames.begin();cur!=frames.end();cur++) { + curFrame = *cur; + if (curFrame.isKey) keyFrames.Add(i); + bytePos.Add(curFrame.filePos); + i++; + } + + // Process timecodes + frames.sort(); + for (std::list::iterator cur=frames.begin();cur!=frames.end();cur++) { + curFrame = *cur; + timecodes.push_back(curFrame.time); + } } } diff --git a/core/video_provider.cpp b/core/video_provider.cpp index 4b1ce174c..3ed825912 100644 --- a/core/video_provider.cpp +++ b/core/video_provider.cpp @@ -38,6 +38,7 @@ // Headers #include "video_provider_avs.h" #include "video_provider_lavc.h" +#include "video_provider_dshow.h" #include "options.h" @@ -46,7 +47,9 @@ VideoProvider *VideoProvider::GetProvider(wxString video,wxString subtitles) { // Check if avisynth is available bool avisynthAvailable = false; + bool dshowAvailable = false; #ifdef __WINDOWS__ + dshowAvailable = true; try { // If avisynth.dll cannot be loaded, an exception will be thrown and avisynthAvailable will never be set to true AviSynthWrapper avs; @@ -63,7 +66,7 @@ VideoProvider *VideoProvider::GetProvider(wxString video,wxString subtitles) { // See if it's OK to use LAVC #ifdef USE_LAVC - if (preffered == _T("ffmpeg") || !avisynthAvailable) { + if (preffered == _T("ffmpeg") || (!avisynthAvailable && !dshowAvailable)) { // Load bool success = false; wxString error; @@ -83,6 +86,7 @@ VideoProvider *VideoProvider::GetProvider(wxString video,wxString subtitles) { if (!success) { // Delete old provider delete provider; + provider = NULL; // Try to fallback to avisynth if (avisynthAvailable) { @@ -96,14 +100,29 @@ VideoProvider *VideoProvider::GetProvider(wxString video,wxString subtitles) { } #endif - // Use avisynth provider #ifdef __WINDOWS__ + #ifdef USE_DIRECTSHOW + // Use DirectShow provider + if (!provider && (preffered == _T("dshow") || !avisynthAvailable)) { + try { + provider = new DirectShowVideoProvider(video,subtitles); + } + catch (...) { + delete provider; + provider = NULL; + throw; + } + } + #endif + + // Use Avisynth provider if (!provider) { try { provider = new AvisynthVideoProvider(video,subtitles); } catch (...) { delete provider; + provider = NULL; throw; } } diff --git a/core/video_provider_dshow.cpp b/core/video_provider_dshow.cpp new file mode 100644 index 000000000..9aa202526 --- /dev/null +++ b/core/video_provider_dshow.cpp @@ -0,0 +1,325 @@ +// Copyright (c) 2006, Rodrigo Braz Monteiro, Mike Matsnev +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Aegisub Group nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// ----------------------------------------------------------------------------- +// +// AEGISUB +// +// Website: http://aegisub.cellosoft.com +// Contact: mailto:zeratul@cellosoft.com +// + + +/////////// +// Headers +#include +#ifdef __WINDOWS__ +#ifdef USE_DIRECTSHOW +#pragma warning(disable: 4995) +#include +#include +#include +#include +#include "video_provider_dshow.h" +#include "utils.h" +#include "vfr.h" + +// CLSID for videosink: {F13D3732-96BD-4108-AFEB-E85F68FF64DC} +DEFINE_GUID(CLSID_VideoSink, 0xf13d3732, 0x96bd, 0x4108, 0xaf, 0xeb, 0xe8, 0x5f, 0x68, 0xff, 0x64, 0xdc); + + +/////////////// +// Constructor +// Based on Haali's code for DirectShowSource2 +DirectShowVideoProvider::DirectShowVideoProvider(wxString _filename, wxString _subfilename) { + m_hFrameReady = CreateEvent(NULL, FALSE, FALSE, NULL); + OpenVideo(_filename); +} + + +////////////// +// Destructor +DirectShowVideoProvider::~DirectShowVideoProvider() { +} + + +//////////// +// Get pin +// Code by Haali +#define ENUM_FILTERS(graph, var) for (CComPtr __pEF__; !__pEF__ && SUCCEEDED(graph->EnumFilters(&__pEF__)); ) for (CComPtr var; __pEF__->Next(1, &var, NULL) == S_OK; var.Release()) +#define ENUM_PINS(filter, var) for (CComPtr __pEP__; !__pEP__ && SUCCEEDED(filter->EnumPins(&__pEP__)); ) for (CComPtr var; __pEP__->Next(1, &var, NULL) == S_OK; var.Release()) + +class MTPtr { + AM_MEDIA_TYPE *pMT; + + MTPtr(const MTPtr&); + MTPtr& operator=(const MTPtr&); +public: + MTPtr() : pMT(NULL) { } + ~MTPtr() { DeleteMediaType(pMT); } + + AM_MEDIA_TYPE *operator->() { return pMT; } + const AM_MEDIA_TYPE *operator->() const { return pMT; } + operator AM_MEDIA_TYPE *() { return pMT; } + AM_MEDIA_TYPE **operator&() { DeleteMediaType(pMT); pMT = NULL; return &pMT; } + + static void FreeMediaType(AM_MEDIA_TYPE *pMT) { + if (pMT == NULL) + return; + if (pMT->cbFormat > 0) { + CoTaskMemFree(pMT->pbFormat); + pMT->pbFormat = NULL; + pMT->cbFormat = 0; + } + if (pMT->pUnk) { + pMT->pUnk->Release(); + pMT->pUnk = NULL; + } + } + + static void DeleteMediaType(AM_MEDIA_TYPE *pMT) { + if (pMT == NULL) + return; + if (pMT->cbFormat > 0) + CoTaskMemFree(pMT->pbFormat); + if (pMT->pUnk) + pMT->pUnk->Release(); + CoTaskMemFree(pMT); + } +}; + +#define ENUM_MT(pin, var) for (CComPtr __pEMT__; !__pEMT__ && SUCCEEDED(pin->EnumMediaTypes(&__pEMT__)); ) for (MTPtr var; __pEMT__->Next(1, &var, NULL) == S_OK; ) + +CComPtr GetPin(IBaseFilter *pF, bool include_connected, PIN_DIRECTION dir, const GUID *pMT = NULL) { + if (pF == NULL) + return CComPtr(); + + ENUM_PINS(pF, pP) { + PIN_DIRECTION pd; + if (FAILED(pP->QueryDirection(&pd))) + continue; + if (pd == dir) { + if (!include_connected) { + CComPtr pQ; + if (SUCCEEDED(pP->ConnectedTo(&pQ))) + continue; + } + if (pMT == NULL) + return pP; + + ENUM_MT(pP, MT) + if (MT->majortype == *pMT) + return pP; + } + } + + return CComPtr(); +} + + +//////////////////// +// More Haali stuff +void DirectShowVideoProvider::RegROT() { + if (!m_pGC || m_registered) + return; + + CComPtr rot; + if (FAILED(GetRunningObjectTable(0, &rot))) + return; + + CStringA name; + name.Format("FilterGraph %08p pid %08x (avss)", m_pGC.p, GetCurrentProcessId()); + + CComPtr mk; + if (FAILED(CreateItemMoniker(L"!", CA2W(name), &mk))) + return; + + if (SUCCEEDED(rot->Register(ROTFLAGS_REGISTRATIONKEEPSALIVE, m_pGC, mk, &m_rot_cookie))) + m_registered = true; +} + +void DirectShowVideoProvider::UnregROT() { + if (!m_registered) + return; + + CComPtr rot; + if (FAILED(GetRunningObjectTable(0, &rot))) + return; + + if (SUCCEEDED(rot->Revoke(m_rot_cookie))) + m_registered = false; +} + + + +////////////// +// Open video +HRESULT DirectShowVideoProvider::OpenVideo(wxString _filename) { + HRESULT hr; + + // Create an instance of the Filter Graph + CComPtr pG; + if (FAILED(hr = pG.CoCreateInstance(CLSID_FilterGraph))) return hr; + + // Create an Instance of the Video Sink + CComPtr pR; + CLSID CLSID_VideoSink; + CLSIDFromString(L"{F13D3732-96BD-4108-AFEB-E85F68FF64DC}",&CLSID_VideoSink); + if (FAILED(hr = pR.CoCreateInstance(CLSID_VideoSink))) return hr; + + // Add VideoSink to graph + pG->AddFilter(pR, L"VideoSink"); + + // Create instance of sink (??) + CComQIPtr sink(pR); + if (!sink) return E_NOINTERFACE; + + // Create another instance of sink (??) + CComQIPtr sink2(pR); + if (!sink2) return E_NOINTERFACE; + + // Set allowed types for sink + sink->SetAllowedTypes(IVS_RGB32|IVS_YV12|IVS_YUY2); + + // I have no clue + ResetEvent(m_hFrameReady); + sink2->NotifyFrame(m_hFrameReady); + + // Create source filter and add it to graph + CComPtr pS; + if (FAILED(hr = pG->AddSourceFilter(_filename.wc_str(), NULL, &pS))) return hr; + + // Property bag? The heck is this? + // Is this supposed to make it "interactive", enabling some actions? + // I have no clue. + CComQIPtr pPB(pS); + if (pPB) pPB->Write(L"ui.interactive", &CComVariant(0u, VT_UI4)); + + // Get source's output pin + CComPtr pO(GetPin(pS, false, PINDIR_OUTPUT, &MEDIATYPE_Video)); + if (!pO) pO = GetPin(pS, false, PINDIR_OUTPUT, &MEDIATYPE_Stream); + + // Get sink's input pin + CComPtr pI(GetPin(pR, false, PINDIR_INPUT)); + + // Check if pins are ok + if (!pO || !pI) return E_FAIL; + + // Connect pins + if (FAILED(hr = pG->Connect(pO, pI))) return hr; + + // Add control stuff to graph + CComQIPtr mc(pG); + CComQIPtr ms(pG); + + // See if they were created correctly + if (!mc || !ms) return E_NOINTERFACE; + + // Run MediaControl, initiating the data flow through it + if (FAILED(hr = mc->Run())) return hr; + + // Get state from media seeking (??) + OAFilterState fs; + if (FAILED(hr = mc->GetState(2000, &fs))) return hr; + + // Wait up to 5 seconds for the first frame to arrive + if (WaitForSingleObject(m_hFrameReady, 5000) != WAIT_OBJECT_0) return E_FAIL; + + // Get frame format + long long defd; + unsigned type, arx, ary; + if (FAILED(hr = sink2->GetFrameFormat(&type, &width, &height, &arx, &ary, &defd))) return hr; + + // Get video duration + REFERENCE_TIME duration; + if (FAILED(hr = ms->GetDuration(&duration))) return hr; + + // Length of each frame? (??) + if (defd == 0) defd = 400000; + + // No clue, either + int avgf = 0; + if (avgf > 0) defd = avgf; + + // Set pixel type + //switch (type) { + // case IVS_RGB32: m_vi.pixel_type = VideoInfo::CS_BGR32; break; + // case IVS_YUY2: m_vi.pixel_type = VideoInfo::CS_YUY2; break; + // case IVS_YV12: m_vi.pixel_type = VideoInfo::CS_YV12; break; + // default: return E_FAIL; + //} + + // Set number of frames and fps + num_frames = duration / defd; + fps = double(10000000) / double(defd); + + // Set frame length + //m_avgframe = defd; + + // Store filters + //m_pR = sink; + //m_pGC = mc; + //m_pGS = ms; + + // Flag frame as ready? + SetEvent(m_hFrameReady); + + // No idea + //RegROT(); + + // Set frame count + //m_f.SetCount(m_vi.num_frames); + return hr; +} + + +//////////////// +// Refresh subs +void DirectShowVideoProvider::RefreshSubtitles() { +} + + +/////////// +// Set DAR +void DirectShowVideoProvider::SetDAR(double _dar) { +} + + +//////////// +// Set Zoom +void DirectShowVideoProvider::SetZoom(double _zoom) { +} + + +/////////////////// +// Get float frame +void DirectShowVideoProvider::GetFloatFrame(float* Buffer, int n) { +} + + +#endif +#endif diff --git a/core/video_provider_dshow.h b/core/video_provider_dshow.h new file mode 100644 index 000000000..ac1d330ca --- /dev/null +++ b/core/video_provider_dshow.h @@ -0,0 +1,113 @@ +// Copyright (c) 2006, Rodrigo Braz Monteiro +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Aegisub Group nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// ----------------------------------------------------------------------------- +// +// AEGISUB +// +// Website: http://aegisub.cellosoft.com +// Contact: mailto:zeratul@cellosoft.com +// + + +#pragma once + + +/////////// +// Headers +#include + +#ifdef __WINDOWS__ +#ifdef USE_DIRECTSHOW +#include "video_provider.h" +#pragma warning(disable: 4995) +#include +#include +#include +#include +#include +#include "videosink.h" + + +/////////////////////////////////// +// DirectShow Video Provider class +class DirectShowVideoProvider: public VideoProvider { +private: + wxString subfilename; + + unsigned int last_fnum; + unsigned int width; + unsigned int height; + unsigned int num_frames; + double fps; + + int depth; + double dar; + double zoom; + + unsigned char* data; + wxBitmap last_frame; + + wxBitmap GetFrame(int n, bool force); + void AttachOverlay(SubtitleProvider::Overlay *_overlay) {} + HRESULT OpenVideo(wxString _filename); + + void RegROT(); + void UnregROT(); + + CComPtr m_pR; + CComPtr m_pGC; + CComPtr m_pGS; + HANDLE m_hFrameReady; + bool m_registered; + DWORD m_rot_cookie; + +public: + DirectShowVideoProvider(wxString _filename, wxString _subfilename); + ~DirectShowVideoProvider(); + + void RefreshSubtitles(); + void SetDAR(double _dar); + void SetZoom(double _zoom); + + wxBitmap GetFrame(int n) { return wxBitmap(64,64); }; + void GetFloatFrame(float* Buffer, int n); + + int GetPosition() { return last_fnum; }; + int GetFrameCount() { return num_frames; }; + double GetFPS() { return fps; }; + + int GetWidth() { return width; }; + int GetHeight() { return height; }; + double GetZoom() { return zoom; }; + + int GetSourceWidth() { return width; }; + int GetSourceHeight() { return height; }; +}; + +#endif +#endif