Handle many more edge cases in dshowvideosink.
Instrument various codepaths with debug messages. Handle (as best as I could see how - it's pretty nasty) moving a video window to another monitor. Add listening for directshow events.
This commit is contained in:
parent
e3fcf51e2c
commit
2e401cc71d
@ -19,13 +19,16 @@
|
|||||||
|
|
||||||
#include "dshowvideofakesrc.h"
|
#include "dshowvideofakesrc.h"
|
||||||
|
|
||||||
|
GST_DEBUG_CATEGORY_EXTERN (dshowvideosink_debug);
|
||||||
|
#define GST_CAT_DEFAULT dshowvideosink_debug
|
||||||
|
|
||||||
// {A0A5CF33-BD0C-4158-9A56-3011DEE3AF6B}
|
// {A0A5CF33-BD0C-4158-9A56-3011DEE3AF6B}
|
||||||
const GUID CLSID_VideoFakeSrc =
|
const GUID CLSID_VideoFakeSrc =
|
||||||
{ 0xa0a5cf33, 0xbd0c, 0x4158, { 0x9a, 0x56, 0x30, 0x11, 0xde, 0xe3, 0xaf, 0x6b } };
|
{ 0xa0a5cf33, 0xbd0c, 0x4158, { 0x9a, 0x56, 0x30, 0x11, 0xde, 0xe3, 0xaf, 0x6b } };
|
||||||
|
|
||||||
/* output pin*/
|
/* output pin*/
|
||||||
VideoFakeSrcPin::VideoFakeSrcPin (CBaseFilter *pFilter, CCritSec *sec, HRESULT *hres):
|
VideoFakeSrcPin::VideoFakeSrcPin (CBaseFilter *pFilter, CCritSec *sec, HRESULT *hres):
|
||||||
CBaseOutputPin("VideoFakeSrcPin", pFilter, sec, hres, L"output")
|
CDynamicOutputPin("VideoFakeSrcPin", pFilter, sec, hres, L"output")
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +104,8 @@ HRESULT VideoFakeSrcPin::DecideBufferSize (IMemAllocator *pAlloc, ALLOCATOR_PROP
|
|||||||
properties.cbPrefix, properties.cBuffers);
|
properties.cbPrefix, properties.cBuffers);
|
||||||
|
|
||||||
/* Then actually allocate the buffers */
|
/* Then actually allocate the buffers */
|
||||||
pAlloc->Commit();
|
hres = pAlloc->Commit();
|
||||||
|
GST_DEBUG ("Allocator commit returned %x", hres);
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
@ -199,20 +203,81 @@ STDMETHODIMP VideoFakeSrcPin::CopyToDestinationBuffer (byte *srcbuf, byte *dstbu
|
|||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP VideoFakeSrcPin::Disconnect ()
|
||||||
|
{
|
||||||
|
GST_DEBUG_OBJECT (this, "Disconnecting pin");
|
||||||
|
HRESULT hr = CDynamicOutputPin::Disconnect();
|
||||||
|
GST_DEBUG_OBJECT (this, "Pin disconnected");
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT VideoFakeSrcPin::Inactive ()
|
||||||
|
{
|
||||||
|
GST_DEBUG_OBJECT (this, "Pin going inactive");
|
||||||
|
HRESULT hr = CDynamicOutputPin::Inactive();
|
||||||
|
GST_DEBUG_OBJECT (this, "Pin inactivated");
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT VideoFakeSrcPin::BreakConnect ()
|
||||||
|
{
|
||||||
|
GST_DEBUG_OBJECT (this, "Breaking connection");
|
||||||
|
HRESULT hr = CDynamicOutputPin::BreakConnect();
|
||||||
|
GST_DEBUG_OBJECT (this, "Connection broken");
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT VideoFakeSrcPin::CompleteConnect (IPin *pReceivePin)
|
||||||
|
{
|
||||||
|
GST_DEBUG_OBJECT (this, "Completing connection");
|
||||||
|
HRESULT hr = CDynamicOutputPin::CompleteConnect(pReceivePin);
|
||||||
|
GST_DEBUG_OBJECT (this, "Completed connection: %x", hr);
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP VideoFakeSrcPin::Block(DWORD dwBlockFlags, HANDLE hEvent)
|
||||||
|
{
|
||||||
|
GST_DEBUG_OBJECT (this, "Calling Block()");
|
||||||
|
HRESULT hr = CDynamicOutputPin::Block (dwBlockFlags, hEvent);
|
||||||
|
GST_DEBUG_OBJECT (this, "Called Block()");
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* When moving the video to a different monitor, directshow stops and restarts the playback pipeline.
|
||||||
|
* Unfortunately, it doesn't properly block pins or do anything special, so we racily just fail
|
||||||
|
* at this point.
|
||||||
|
* So, we try multiple times in a loop, hoping that it'll have finished (we get no notifications at all!)
|
||||||
|
* at some point.
|
||||||
|
*/
|
||||||
|
#define MAX_ATTEMPTS 10
|
||||||
|
|
||||||
GstFlowReturn VideoFakeSrcPin::PushBuffer(GstBuffer *buffer)
|
GstFlowReturn VideoFakeSrcPin::PushBuffer(GstBuffer *buffer)
|
||||||
{
|
{
|
||||||
IMediaSample *pSample = NULL;
|
IMediaSample *pSample = NULL;
|
||||||
|
|
||||||
byte *data = GST_BUFFER_DATA (buffer);
|
byte *data = GST_BUFFER_DATA (buffer);
|
||||||
|
int attempts = 0;
|
||||||
/* TODO: Use more of the arguments here? */
|
HRESULT hres;
|
||||||
HRESULT hres = GetDeliveryBuffer(&pSample, NULL, NULL, 0);
|
|
||||||
if (SUCCEEDED (hres))
|
|
||||||
{
|
|
||||||
BYTE *sample_buffer;
|
BYTE *sample_buffer;
|
||||||
AM_MEDIA_TYPE *mediatype;
|
AM_MEDIA_TYPE *mediatype;
|
||||||
|
|
||||||
|
StartUsingOutputPin();
|
||||||
|
|
||||||
|
while (attempts < MAX_ATTEMPTS)
|
||||||
|
{
|
||||||
|
hres = GetDeliveryBuffer(&pSample, NULL, NULL, 0);
|
||||||
|
if (SUCCEEDED (hres))
|
||||||
|
break;
|
||||||
|
attempts++;
|
||||||
|
Sleep(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FAILED (hres))
|
||||||
|
{
|
||||||
|
StopUsingOutputPin();
|
||||||
|
GST_WARNING ("Could not get sample for delivery to sink: %x", hres);
|
||||||
|
return GST_FLOW_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
pSample->GetPointer(&sample_buffer);
|
pSample->GetPointer(&sample_buffer);
|
||||||
pSample->GetMediaType(&mediatype);
|
pSample->GetMediaType(&mediatype);
|
||||||
if (mediatype)
|
if (mediatype)
|
||||||
@ -236,20 +301,28 @@ GstFlowReturn VideoFakeSrcPin::PushBuffer(GstBuffer *buffer)
|
|||||||
* this at a higher level, inside BaseSink. */
|
* this at a higher level, inside BaseSink. */
|
||||||
pSample->SetTime(NULL, NULL);
|
pSample->SetTime(NULL, NULL);
|
||||||
|
|
||||||
|
while (attempts < MAX_ATTEMPTS)
|
||||||
|
{
|
||||||
hres = Deliver(pSample);
|
hres = Deliver(pSample);
|
||||||
|
if (SUCCEEDED (hres))
|
||||||
|
break;
|
||||||
|
attempts++;
|
||||||
|
Sleep(100);
|
||||||
|
}
|
||||||
|
|
||||||
pSample->Release();
|
pSample->Release();
|
||||||
|
|
||||||
|
StopUsingOutputPin();
|
||||||
|
|
||||||
if (SUCCEEDED (hres))
|
if (SUCCEEDED (hres))
|
||||||
return GST_FLOW_OK;
|
return GST_FLOW_OK;
|
||||||
else if (hres == VFW_E_NOT_CONNECTED)
|
else {
|
||||||
|
GST_WARNING_OBJECT (this, "Failed to deliver sample: %x", hres);
|
||||||
|
if (hres == VFW_E_NOT_CONNECTED)
|
||||||
return GST_FLOW_NOT_LINKED;
|
return GST_FLOW_NOT_LINKED;
|
||||||
else
|
else
|
||||||
return GST_FLOW_ERROR;
|
return GST_FLOW_ERROR;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
GST_WARNING ("Could not get sample for delivery to sink: %x", hres);
|
|
||||||
return GST_FLOW_ERROR;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
STDMETHODIMP VideoFakeSrcPin::Flush ()
|
STDMETHODIMP VideoFakeSrcPin::Flush ()
|
||||||
@ -259,7 +332,8 @@ STDMETHODIMP VideoFakeSrcPin::Flush ()
|
|||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoFakeSrc::VideoFakeSrc() : CBaseFilter("VideoFakeSrc", NULL, &m_critsec, CLSID_VideoFakeSrc)
|
VideoFakeSrc::VideoFakeSrc() : CBaseFilter("VideoFakeSrc", NULL, &m_critsec, CLSID_VideoFakeSrc),
|
||||||
|
m_evFilterStoppingEvent(TRUE)
|
||||||
{
|
{
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
m_pOutputPin = new VideoFakeSrcPin ((CSource *)this, &m_critsec, &hr);
|
m_pOutputPin = new VideoFakeSrcPin ((CSource *)this, &m_critsec, &hr);
|
||||||
@ -279,3 +353,62 @@ VideoFakeSrcPin *VideoFakeSrc::GetOutputPin()
|
|||||||
{
|
{
|
||||||
return m_pOutputPin;
|
return m_pOutputPin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP VideoFakeSrc::Stop(void)
|
||||||
|
{
|
||||||
|
GST_DEBUG_OBJECT (this, "Stop()");
|
||||||
|
m_evFilterStoppingEvent.Set();
|
||||||
|
|
||||||
|
return CBaseFilter::Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP VideoFakeSrc::Pause(void)
|
||||||
|
{
|
||||||
|
GST_DEBUG_OBJECT (this, "Pause()");
|
||||||
|
|
||||||
|
m_evFilterStoppingEvent.Reset();
|
||||||
|
|
||||||
|
return CBaseFilter::Pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP VideoFakeSrc::Run(REFERENCE_TIME tStart)
|
||||||
|
{
|
||||||
|
GST_DEBUG_OBJECT (this, "Run()");
|
||||||
|
|
||||||
|
return CBaseFilter::Run(tStart);
|
||||||
|
}
|
||||||
|
|
||||||
|
STDMETHODIMP VideoFakeSrc::JoinFilterGraph(IFilterGraph* pGraph, LPCWSTR pName)
|
||||||
|
{
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
// The filter is joining the filter graph.
|
||||||
|
if(NULL != pGraph)
|
||||||
|
{
|
||||||
|
IGraphConfig* pGraphConfig = NULL;
|
||||||
|
hr = pGraph->QueryInterface(IID_IGraphConfig, (void**)&pGraphConfig);
|
||||||
|
if(FAILED(hr))
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
hr = CBaseFilter::JoinFilterGraph(pGraph, pName);
|
||||||
|
if(FAILED(hr))
|
||||||
|
{
|
||||||
|
pGraphConfig->Release();
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_pOutputPin->SetConfigInfo(pGraphConfig, m_evFilterStoppingEvent);
|
||||||
|
pGraphConfig->Release();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
hr = CBaseFilter::JoinFilterGraph(pGraph, pName);
|
||||||
|
if(FAILED(hr))
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
m_pOutputPin->SetConfigInfo(NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
#include <streams.h>
|
#include <streams.h>
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
|
|
||||||
class VideoFakeSrcPin : public CBaseOutputPin
|
class VideoFakeSrcPin : public CDynamicOutputPin
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
/* members */
|
/* members */
|
||||||
@ -41,11 +41,14 @@ public:
|
|||||||
virtual HRESULT CheckMediaType(const CMediaType *pmt);
|
virtual HRESULT CheckMediaType(const CMediaType *pmt);
|
||||||
HRESULT GetMediaType(int iPosition, CMediaType *pMediaType);
|
HRESULT GetMediaType(int iPosition, CMediaType *pMediaType);
|
||||||
virtual HRESULT DecideBufferSize (IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest);
|
virtual HRESULT DecideBufferSize (IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest);
|
||||||
|
virtual HRESULT BreakConnect();
|
||||||
|
virtual HRESULT CompleteConnect(IPin *pReceivePin);
|
||||||
|
virtual HRESULT Inactive();
|
||||||
STDMETHOD (SetMediaType) (AM_MEDIA_TYPE *pmt);
|
STDMETHOD (SetMediaType) (AM_MEDIA_TYPE *pmt);
|
||||||
STDMETHOD (Flush) ();
|
STDMETHOD (Flush) ();
|
||||||
STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);
|
STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);
|
||||||
|
STDMETHODIMP Disconnect();
|
||||||
|
STDMETHODIMP Block(DWORD dwBlockFlags, HANDLE hEvent);
|
||||||
};
|
};
|
||||||
|
|
||||||
class VideoFakeSrc : public CBaseFilter
|
class VideoFakeSrc : public CBaseFilter
|
||||||
@ -55,6 +58,8 @@ private:
|
|||||||
CCritSec m_critsec;
|
CCritSec m_critsec;
|
||||||
VideoFakeSrcPin *m_pOutputPin;
|
VideoFakeSrcPin *m_pOutputPin;
|
||||||
|
|
||||||
|
CAMEvent m_evFilterStoppingEvent;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/* methods */
|
/* methods */
|
||||||
VideoFakeSrc (void);
|
VideoFakeSrc (void);
|
||||||
@ -65,6 +70,11 @@ public:
|
|||||||
/* Overrides */
|
/* Overrides */
|
||||||
int GetPinCount();
|
int GetPinCount();
|
||||||
CBasePin *GetPin(int n);
|
CBasePin *GetPin(int n);
|
||||||
|
|
||||||
|
STDMETHODIMP Run(REFERENCE_TIME tStart);
|
||||||
|
STDMETHODIMP Stop(void);
|
||||||
|
STDMETHODIMP Pause(void);
|
||||||
|
STDMETHODIMP JoinFilterGraph(IFilterGraph* pGraph, LPCWSTR pName);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* __DSHOWVIDEOFAKESRC_H__ */
|
#endif /* __DSHOWVIDEOFAKESRC_H__ */
|
||||||
|
@ -28,13 +28,15 @@
|
|||||||
|
|
||||||
#include "windows.h"
|
#include "windows.h"
|
||||||
|
|
||||||
|
#define WM_GRAPH_NOTIFY WM_APP + 1 /* Private message */
|
||||||
|
|
||||||
static const GstElementDetails gst_dshowvideosink_details =
|
static const GstElementDetails gst_dshowvideosink_details =
|
||||||
GST_ELEMENT_DETAILS ("DirectShow video sink",
|
GST_ELEMENT_DETAILS ("DirectShow video sink",
|
||||||
"Sink/Video",
|
"Sink/Video",
|
||||||
"Display data using a DirectShow video renderer",
|
"Display data using a DirectShow video renderer",
|
||||||
"Pioneers of the Inevitable <songbird@songbirdnest.com>");
|
"Pioneers of the Inevitable <songbird@songbirdnest.com>");
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY_STATIC (dshowvideosink_debug);
|
GST_DEBUG_CATEGORY (dshowvideosink_debug);
|
||||||
#define GST_CAT_DEFAULT dshowvideosink_debug
|
#define GST_CAT_DEFAULT dshowvideosink_debug
|
||||||
|
|
||||||
static GstCaps * gst_directshow_media_type_to_caps (AM_MEDIA_TYPE *mediatype);
|
static GstCaps * gst_directshow_media_type_to_caps (AM_MEDIA_TYPE *mediatype);
|
||||||
@ -198,6 +200,7 @@ gst_dshowvideosink_clear (GstDshowVideoSink *sink)
|
|||||||
sink->renderersupport = NULL;
|
sink->renderersupport = NULL;
|
||||||
sink->fakesrc = NULL;
|
sink->fakesrc = NULL;
|
||||||
sink->filter_graph = NULL;
|
sink->filter_graph = NULL;
|
||||||
|
sink->filter_media_event = NULL;
|
||||||
|
|
||||||
sink->keep_aspect_ratio = FALSE;
|
sink->keep_aspect_ratio = FALSE;
|
||||||
sink->full_screen = FALSE;
|
sink->full_screen = FALSE;
|
||||||
@ -396,6 +399,21 @@ gst_dshow_get_pin_from_filter (IBaseFilter *filter, PIN_DIRECTION pindir, IPin *
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_dshowvideosink_handle_event (GstDshowVideoSink *sink)
|
||||||
|
{
|
||||||
|
if (sink->filter_media_event) {
|
||||||
|
long evCode;
|
||||||
|
LONG_PTR param1, param2;
|
||||||
|
HRESULT hr;
|
||||||
|
while (SUCCEEDED (sink->filter_media_event->GetEvent(&evCode, ¶m1, ¶m2, 0)))
|
||||||
|
{
|
||||||
|
GST_INFO_OBJECT (sink, "Received DirectShow graph event code 0x%x", evCode);
|
||||||
|
sink->filter_media_event->FreeEventParams(evCode, param1, param2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* WNDPROC for application-supplied windows */
|
/* WNDPROC for application-supplied windows */
|
||||||
LRESULT APIENTRY WndProcHook (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
LRESULT APIENTRY WndProcHook (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||||
{
|
{
|
||||||
@ -405,6 +423,9 @@ LRESULT APIENTRY WndProcHook (HWND hWnd, UINT message, WPARAM wParam, LPARAM lPa
|
|||||||
GstDshowVideoSink *sink = (GstDshowVideoSink *)GetProp (hWnd, L"GstDShowVideoSink");
|
GstDshowVideoSink *sink = (GstDshowVideoSink *)GetProp (hWnd, L"GstDShowVideoSink");
|
||||||
|
|
||||||
switch (message) {
|
switch (message) {
|
||||||
|
case WM_GRAPH_NOTIFY:
|
||||||
|
gst_dshowvideosink_handle_event (sink);
|
||||||
|
return 0;
|
||||||
case WM_PAINT:
|
case WM_PAINT:
|
||||||
sink->renderersupport->PaintWindow ();
|
sink->renderersupport->PaintWindow ();
|
||||||
break;
|
break;
|
||||||
@ -436,9 +457,13 @@ WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|||||||
return DefWindowProc (hWnd, message, wParam, lParam);
|
return DefWindowProc (hWnd, message, wParam, lParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (sink, "Got a window message for %x, %x", hWnd, message);
|
//GST_DEBUG_OBJECT (sink, "Got a window message for %x, %x", hWnd, message);
|
||||||
|
|
||||||
switch (message) {
|
switch (message) {
|
||||||
|
case WM_GRAPH_NOTIFY:
|
||||||
|
GST_LOG_OBJECT (sink, "GRAPH_NOTIFY WINDOW MESSAGE");
|
||||||
|
gst_dshowvideosink_handle_event (sink);
|
||||||
|
return 0;
|
||||||
case WM_PAINT:
|
case WM_PAINT:
|
||||||
sink->renderersupport->PaintWindow ();
|
sink->renderersupport->PaintWindow ();
|
||||||
break;
|
break;
|
||||||
@ -538,6 +563,8 @@ gst_dshowvideosink_window_thread (GstDshowVideoSink * sink)
|
|||||||
|
|
||||||
SetWindowLongPtr (video_window, GWLP_USERDATA, (LONG)sink);
|
SetWindowLongPtr (video_window, GWLP_USERDATA, (LONG)sink);
|
||||||
|
|
||||||
|
sink->window_id = video_window;
|
||||||
|
|
||||||
/* signal application we created a window */
|
/* signal application we created a window */
|
||||||
gst_x_overlay_got_xwindow_id (GST_X_OVERLAY (sink),
|
gst_x_overlay_got_xwindow_id (GST_X_OVERLAY (sink),
|
||||||
(gulong)video_window);
|
(gulong)video_window);
|
||||||
@ -637,6 +664,8 @@ static void gst_dshowvideosink_set_window_for_renderer (GstDshowVideoSink *sink)
|
|||||||
static void
|
static void
|
||||||
gst_dshowvideosink_prepare_window (GstDshowVideoSink *sink)
|
gst_dshowvideosink_prepare_window (GstDshowVideoSink *sink)
|
||||||
{
|
{
|
||||||
|
HRESULT hres;
|
||||||
|
|
||||||
/* Give the app a last chance to supply a window id */
|
/* Give the app a last chance to supply a window id */
|
||||||
if (!sink->window_id) {
|
if (!sink->window_id) {
|
||||||
gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (sink));
|
gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (sink));
|
||||||
@ -650,6 +679,23 @@ gst_dshowvideosink_prepare_window (GstDshowVideoSink *sink)
|
|||||||
else {
|
else {
|
||||||
gst_dshowvideosink_create_default_window (sink);
|
gst_dshowvideosink_create_default_window (sink);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sink->filter_media_event) {
|
||||||
|
sink->filter_media_event->Release();
|
||||||
|
sink->filter_media_event = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
hres = sink->filter_graph->QueryInterface(
|
||||||
|
IID_IMediaEventEx, (void **) &sink->filter_media_event);
|
||||||
|
|
||||||
|
if (FAILED (hres)) {
|
||||||
|
GST_WARNING_OBJECT (sink, "Failed to get IMediaEventEx");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
hres = sink->filter_media_event->SetNotifyWindow ((OAHWND)sink->window_id,
|
||||||
|
WM_GRAPH_NOTIFY, 0);
|
||||||
|
GST_DEBUG_OBJECT (sink, "SetNotifyWindow(%p) returned %x", sink->window_id, hres);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
@ -1152,6 +1198,11 @@ static gboolean
|
|||||||
gst_dshowvideosink_build_filtergraph (GstDshowVideoSink *sink)
|
gst_dshowvideosink_build_filtergraph (GstDshowVideoSink *sink)
|
||||||
{
|
{
|
||||||
HRESULT hres;
|
HRESULT hres;
|
||||||
|
gboolean comInit = FALSE;
|
||||||
|
|
||||||
|
hres = CoInitialize(0);
|
||||||
|
if (SUCCEEDED (hres))
|
||||||
|
comInit = TRUE;
|
||||||
|
|
||||||
/* Build our DirectShow FilterGraph, looking like:
|
/* Build our DirectShow FilterGraph, looking like:
|
||||||
*
|
*
|
||||||
@ -1203,6 +1254,8 @@ gst_dshowvideosink_build_filtergraph (GstDshowVideoSink *sink)
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (comInit)
|
||||||
|
CoUninitialize();
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
@ -1216,6 +1269,14 @@ error:
|
|||||||
sink->filter_graph = NULL;
|
sink->filter_graph = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sink->filter_media_event) {
|
||||||
|
sink->filter_media_event->Release();
|
||||||
|
sink->filter_media_event = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (comInit)
|
||||||
|
CoUninitialize();
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1234,18 +1295,24 @@ gst_dshowvideosink_set_caps (GstBaseSink * bsink, GstCaps * caps)
|
|||||||
{
|
{
|
||||||
GstDshowVideoSink *sink = GST_DSHOWVIDEOSINK (bsink);
|
GstDshowVideoSink *sink = GST_DSHOWVIDEOSINK (bsink);
|
||||||
|
|
||||||
/* TODO: What do we want to do if the caps change while we're running?
|
if (sink->connected) {
|
||||||
* Find out if we can handle this or not... */
|
/* Look at the DShow APIs for dynamically modifying the pipeline and see
|
||||||
|
* if we can make this work later... */
|
||||||
|
GST_WARNING_OBJECT (sink, "Changing caps at runtime is not yet supported");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
if (!gst_caps_to_directshow_media_type (caps, &sink->mediatype)) {
|
if (!gst_caps_to_directshow_media_type (caps, &sink->mediatype)) {
|
||||||
GST_WARNING_OBJECT (sink, "Cannot convert caps to AM_MEDIA_TYPE, rejecting");
|
GST_WARNING_OBJECT (sink, "Cannot convert caps to AM_MEDIA_TYPE, rejecting");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (sink, "Configuring output pin media type");
|
||||||
/* Now we have an AM_MEDIA_TYPE describing what we're going to send.
|
/* Now we have an AM_MEDIA_TYPE describing what we're going to send.
|
||||||
* We set this on our DirectShow fakesrc's output pin.
|
* We set this on our DirectShow fakesrc's output pin.
|
||||||
*/
|
*/
|
||||||
sink->fakesrc->GetOutputPin()->SetMediaType (&sink->mediatype);
|
sink->fakesrc->GetOutputPin()->SetMediaType (&sink->mediatype);
|
||||||
|
GST_DEBUG_OBJECT (sink, "Configured output pin media type");
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@ -1296,7 +1363,7 @@ gst_dshowvideosink_render (GstBaseSink *bsink, GstBuffer *buffer)
|
|||||||
|
|
||||||
GST_DEBUG_OBJECT (sink, "Pushing buffer through fakesrc->renderer");
|
GST_DEBUG_OBJECT (sink, "Pushing buffer through fakesrc->renderer");
|
||||||
ret = sink->fakesrc->GetOutputPin()->PushBuffer (buffer);
|
ret = sink->fakesrc->GetOutputPin()->PushBuffer (buffer);
|
||||||
GST_DEBUG_OBJECT (sink, "Done pushing buffer through fakesrc->renderer");
|
GST_DEBUG_OBJECT (sink, "Done pushing buffer through fakesrc->renderer: %s", gst_flow_get_name(ret));
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -65,6 +65,8 @@ struct _GstDshowVideoSink
|
|||||||
/* The filter graph (DirectShow equivalent to pipeline */
|
/* The filter graph (DirectShow equivalent to pipeline */
|
||||||
IFilterGraph *filter_graph;
|
IFilterGraph *filter_graph;
|
||||||
|
|
||||||
|
IMediaEventEx *filter_media_event;
|
||||||
|
|
||||||
/* Renderer wrapper (EVR, VMR9, or VMR) and support code */
|
/* Renderer wrapper (EVR, VMR9, or VMR) and support code */
|
||||||
RendererSupport *renderersupport;
|
RendererSupport *renderersupport;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user