winegstreamer: Flip video output.

Gstreamer handles video top-down, but Windows's dshow handles it
bottom-up. So let's insert a videoflip filter to fix the discrepancy
instead of relying on videorenderer to flip it like we did before.

Signed-off-by: Andrew Eikum <aeikum@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Andrew Eikum 2016-05-12 14:27:04 -05:00 committed by Alexandre Julliard
parent 008e4d3e86
commit 6107b36e56
2 changed files with 114 additions and 31 deletions

View File

@ -64,6 +64,7 @@ typedef struct GSTImpl {
LONGLONG filesize; LONGLONG filesize;
BOOL discont, initial, ignore_flush; BOOL discont, initial, ignore_flush;
GstElement *container;
GstElement *gstfilter; GstElement *gstfilter;
GstPad *my_src, *their_sink; GstPad *my_src, *their_sink;
GstBus *bus; GstBus *bus;
@ -78,6 +79,8 @@ struct GSTOutPin {
BaseOutputPin pin; BaseOutputPin pin;
IQualityControl IQualityControl_iface; IQualityControl IQualityControl_iface;
GstElement *flipfilter;
GstPad *flip_sink, *flip_src;
GstPad *their_src; GstPad *their_src;
GstPad *my_sink; GstPad *my_sink;
GstBufferPool *gstpool; GstBufferPool *gstpool;
@ -751,8 +754,14 @@ static void removed_decoded_pad(GstElement *bin, GstPad *pad, gpointer user)
} }
if (x == This->cStreams) if (x == This->cStreams)
goto out; goto out;
pin = This->ppPins[x]; pin = This->ppPins[x];
gst_pad_unlink(pin->their_src, pin->my_sink);
if(pin->flipfilter)
gst_pad_unlink(pin->their_src, pin->flip_sink);
else
gst_pad_unlink(pin->their_src, pin->my_sink);
gst_object_unref(pin->their_src); gst_object_unref(pin->their_src);
pin->their_src = NULL; pin->their_src = NULL;
out: out:
@ -819,10 +828,72 @@ static void init_new_decoded_pad(GstElement *bin, GstPad *pad, GSTImpl *This)
pin->isvid = isvid; pin->isvid = isvid;
gst_segment_init(pin->segment, GST_FORMAT_TIME); gst_segment_init(pin->segment, GST_FORMAT_TIME);
ret = gst_pad_link(pad, mypad);
if (isvid) {
TRACE("setting up videoflip filter for pin %p, my_sink: %p, their_src: %p\n",
pin, pin->my_sink, pad);
/* gstreamer outputs video top-down, but dshow expects bottom-up, so
* make new transform filter to invert video */
pin->flipfilter = gst_element_factory_make("videoflip", NULL);
if(!pin->flipfilter){
ERR("Missing videoflip filter?\n");
ret = -1;
goto exit;
}
gst_util_set_object_arg(G_OBJECT(pin->flipfilter), "method", "vertical-flip");
gst_bin_add(GST_BIN(This->container), pin->flipfilter);
gst_element_sync_state_with_parent(pin->flipfilter);
pin->flip_sink = gst_element_get_static_pad(pin->flipfilter, "sink");
if(!pin->flip_sink){
WARN("Couldn't find sink on flip filter\n");
gst_object_unref(pin->flipfilter);
pin->flipfilter = NULL;
ret = -1;
goto exit;
}
ret = gst_pad_link(pad, pin->flip_sink);
if(ret < 0){
WARN("gst_pad_link failed: %d\n", ret);
gst_object_unref(pin->flip_sink);
pin->flip_sink = NULL;
gst_object_unref(pin->flipfilter);
pin->flipfilter = NULL;
goto exit;
}
pin->flip_src = gst_element_get_static_pad(pin->flipfilter, "src");
if(!pin->flip_src){
WARN("Couldn't find src on flip filter\n");
gst_object_unref(pin->flip_sink);
pin->flip_sink = NULL;
gst_object_unref(pin->flipfilter);
pin->flipfilter = NULL;
ret = -1;
goto exit;
}
ret = gst_pad_link(pin->flip_src, pin->my_sink);
if(ret < 0){
WARN("gst_pad_link failed: %d\n", ret);
gst_object_unref(pin->flip_src);
pin->flip_src = NULL;
gst_object_unref(pin->flip_sink);
pin->flip_sink = NULL;
gst_object_unref(pin->flipfilter);
pin->flipfilter = NULL;
goto exit;
}
} else
ret = gst_pad_link(pad, mypad);
gst_pad_set_active(mypad, 1); gst_pad_set_active(mypad, 1);
exit:
TRACE("Linking: %i\n", ret); TRACE("Linking: %i\n", ret);
if (ret >= 0) { if (ret >= 0) {
@ -834,7 +905,7 @@ static void init_new_decoded_pad(GstElement *bin, GstPad *pad, GSTImpl *This)
static void existing_new_pad(GstElement *bin, GstPad *pad, gpointer user) static void existing_new_pad(GstElement *bin, GstPad *pad, gpointer user)
{ {
GSTImpl *This = (GSTImpl*)user; GSTImpl *This = (GSTImpl*)user;
int x; int x, ret;
TRACE("%p %p %p\n", This, bin, pad); TRACE("%p %p %p\n", This, bin, pad);
@ -852,7 +923,13 @@ static void existing_new_pad(GstElement *bin, GstPad *pad, gpointer user)
GSTOutPin *pin = This->ppPins[x]; GSTOutPin *pin = This->ppPins[x];
if (!pin->their_src) { if (!pin->their_src) {
gst_segment_init(pin->segment, GST_FORMAT_TIME); gst_segment_init(pin->segment, GST_FORMAT_TIME);
if (gst_pad_link(pad, pin->my_sink) >= 0) {
if (pin->flipfilter)
ret = gst_pad_link(pad, pin->flip_sink);
else
ret = gst_pad_link(pad, pin->my_sink);
if (ret >= 0) {
pin->their_src = pad; pin->their_src = pad;
gst_object_ref(pin->their_src); gst_object_ref(pin->their_src);
TRACE("Relinked\n"); TRACE("Relinked\n");
@ -1023,12 +1100,17 @@ static HRESULT GST_Connect(GSTInPin *pPin, IPin *pConnectPin, ALLOCATOR_PROPERTI
gst_bus_set_sync_handler(This->bus, watch_bus_wrapper, This, NULL); gst_bus_set_sync_handler(This->bus, watch_bus_wrapper, This, NULL);
} }
This->container = gst_bin_new(NULL);
This->gstfilter = gst_element_factory_make("decodebin", NULL); This->gstfilter = gst_element_factory_make("decodebin", NULL);
if (!This->gstfilter) { if (!This->gstfilter) {
FIXME("Could not make source filter, are gstreamer-plugins-* installed for %u bits?\n", FIXME("Could not make source filter, are gstreamer-plugins-* installed for %u bits?\n",
8 * (int)sizeof(void*)); 8 * (int)sizeof(void*));
return E_FAIL; return E_FAIL;
} }
gst_bin_add(GST_BIN(This->container), This->gstfilter);
gst_element_set_bus(This->gstfilter, This->bus); gst_element_set_bus(This->gstfilter, This->bus);
g_signal_connect(This->gstfilter, "pad-added", G_CALLBACK(existing_new_pad_wrapper), This); g_signal_connect(This->gstfilter, "pad-added", G_CALLBACK(existing_new_pad_wrapper), This);
g_signal_connect(This->gstfilter, "pad-removed", G_CALLBACK(removed_decoded_pad_wrapper), This); g_signal_connect(This->gstfilter, "pad-removed", G_CALLBACK(removed_decoded_pad_wrapper), This);
@ -1054,9 +1136,9 @@ static HRESULT GST_Connect(GSTInPin *pPin, IPin *pConnectPin, ALLOCATOR_PROPERTI
/* Add initial pins */ /* Add initial pins */
This->initial = This->discont = TRUE; This->initial = This->discont = TRUE;
ResetEvent(This->event); ResetEvent(This->event);
gst_element_set_state(This->gstfilter, GST_STATE_PLAYING); gst_element_set_state(This->container, GST_STATE_PLAYING);
WaitForSingleObject(This->event, -1); WaitForSingleObject(This->event, -1);
gst_element_get_state(This->gstfilter, NULL, NULL, -1); gst_element_get_state(This->container, NULL, NULL, -1);
if (ret < 0) { if (ret < 0) {
WARN("Ret: %i\n", ret); WARN("Ret: %i\n", ret);
@ -1078,8 +1160,8 @@ static HRESULT GST_Connect(GSTInPin *pPin, IPin *pConnectPin, ALLOCATOR_PROPERTI
*props = This->props; *props = This->props;
This->ignore_flush = TRUE; This->ignore_flush = TRUE;
gst_element_set_state(This->gstfilter, GST_STATE_READY); gst_element_set_state(This->container, GST_STATE_READY);
gst_element_get_state(This->gstfilter, NULL, NULL, -1); gst_element_get_state(This->container, NULL, NULL, -1);
This->ignore_flush = FALSE; This->ignore_flush = FALSE;
This->initial = FALSE; This->initial = FALSE;
@ -1262,10 +1344,10 @@ static HRESULT WINAPI GST_Stop(IBaseFilter *iface)
mark_wine_thread(); mark_wine_thread();
if (This->gstfilter) { if (This->container) {
This->ignore_flush = TRUE; This->ignore_flush = TRUE;
gst_element_set_state(This->gstfilter, GST_STATE_READY); gst_element_set_state(This->container, GST_STATE_READY);
gst_element_get_state(This->gstfilter, NULL, NULL, -1); gst_element_get_state(This->container, NULL, NULL, -1);
This->ignore_flush = FALSE; This->ignore_flush = FALSE;
} }
return S_OK; return S_OK;
@ -1280,19 +1362,19 @@ static HRESULT WINAPI GST_Pause(IBaseFilter *iface)
TRACE("(%p)\n", This); TRACE("(%p)\n", This);
if (!This->gstfilter) if (!This->container)
return VFW_E_NOT_CONNECTED; return VFW_E_NOT_CONNECTED;
mark_wine_thread(); mark_wine_thread();
gst_element_get_state(This->gstfilter, &now, NULL, -1); gst_element_get_state(This->container, &now, NULL, -1);
if (now == GST_STATE_PAUSED) if (now == GST_STATE_PAUSED)
return S_OK; return S_OK;
if (now != GST_STATE_PLAYING) if (now != GST_STATE_PLAYING)
hr = IBaseFilter_Run(iface, -1); hr = IBaseFilter_Run(iface, -1);
if (FAILED(hr)) if (FAILED(hr))
return hr; return hr;
ret = gst_element_set_state(This->gstfilter, GST_STATE_PAUSED); ret = gst_element_set_state(This->container, GST_STATE_PAUSED);
if (ret == GST_STATE_CHANGE_ASYNC) if (ret == GST_STATE_CHANGE_ASYNC)
hr = S_FALSE; hr = S_FALSE;
return hr; return hr;
@ -1310,26 +1392,26 @@ static HRESULT WINAPI GST_Run(IBaseFilter *iface, REFERENCE_TIME tStart)
mark_wine_thread(); mark_wine_thread();
if (!This->gstfilter) if (!This->container)
return VFW_E_NOT_CONNECTED; return VFW_E_NOT_CONNECTED;
EnterCriticalSection(&This->filter.csFilter); EnterCriticalSection(&This->filter.csFilter);
This->filter.rtStreamStart = tStart; This->filter.rtStreamStart = tStart;
LeaveCriticalSection(&This->filter.csFilter); LeaveCriticalSection(&This->filter.csFilter);
gst_element_get_state(This->gstfilter, &now, NULL, -1); gst_element_get_state(This->container, &now, NULL, -1);
if (now == GST_STATE_PLAYING) if (now == GST_STATE_PLAYING)
return S_OK; return S_OK;
if (now == GST_STATE_PAUSED) { if (now == GST_STATE_PAUSED) {
GstStateChangeReturn ret; GstStateChangeReturn ret;
ret = gst_element_set_state(This->gstfilter, GST_STATE_PLAYING); ret = gst_element_set_state(This->container, GST_STATE_PLAYING);
if (ret == GST_STATE_CHANGE_ASYNC) if (ret == GST_STATE_CHANGE_ASYNC)
return S_FALSE; return S_FALSE;
return S_OK; return S_OK;
} }
EnterCriticalSection(&This->filter.csFilter); EnterCriticalSection(&This->filter.csFilter);
gst_element_set_state(This->gstfilter, GST_STATE_PLAYING); gst_element_set_state(This->container, GST_STATE_PLAYING);
This->filter.rtStreamStart = tStart; This->filter.rtStreamStart = tStart;
for (i = 0; i < This->cStreams; i++) { for (i = 0; i < This->cStreams; i++) {
@ -1355,12 +1437,12 @@ static HRESULT WINAPI GST_GetState(IBaseFilter *iface, DWORD dwMilliSecsTimeout,
mark_wine_thread(); mark_wine_thread();
if (!This->gstfilter) { if (!This->container) {
*pState = State_Stopped; *pState = State_Stopped;
return S_OK; return S_OK;
} }
ret = gst_element_get_state(This->gstfilter, &now, &pending, dwMilliSecsTimeout == INFINITE ? -1 : dwMilliSecsTimeout * 1000); ret = gst_element_get_state(This->container, &now, &pending, dwMilliSecsTimeout == INFINITE ? -1 : dwMilliSecsTimeout * 1000);
if (ret == GST_STATE_CHANGE_ASYNC) if (ret == GST_STATE_CHANGE_ASYNC)
hr = VFW_S_STATE_INTERMEDIATE; hr = VFW_S_STATE_INTERMEDIATE;
@ -1642,7 +1724,14 @@ static ULONG WINAPI GSTOutPin_Release(IPin *iface)
if (!refCount) { if (!refCount) {
if (This->their_src) { if (This->their_src) {
gst_pad_unlink(This->their_src, This->my_sink); if (This->flipfilter) {
gst_pad_unlink(This->their_src, This->flip_sink);
gst_pad_unlink(This->flip_src, This->my_sink);
gst_object_unref(This->flip_src);
gst_object_unref(This->flip_sink);
gst_object_unref(This->flipfilter);
} else
gst_pad_unlink(This->their_src, This->my_sink);
gst_object_unref(This->their_src); gst_object_unref(This->their_src);
} }
gst_object_unref(This->my_sink); gst_object_unref(This->my_sink);
@ -1793,10 +1882,10 @@ static HRESULT GST_RemoveOutputPins(GSTImpl *This)
TRACE("(%p)\n", This); TRACE("(%p)\n", This);
mark_wine_thread(); mark_wine_thread();
if (!This->gstfilter) if (!This->container)
return S_OK; return S_OK;
gst_element_set_bus(This->gstfilter, NULL); gst_element_set_bus(This->gstfilter, NULL);
gst_element_set_state(This->gstfilter, GST_STATE_NULL); gst_element_set_state(This->container, GST_STATE_NULL);
gst_pad_unlink(This->my_src, This->their_sink); gst_pad_unlink(This->my_src, This->their_sink);
gst_object_unref(This->my_src); gst_object_unref(This->my_src);
gst_object_unref(This->their_sink); gst_object_unref(This->their_sink);
@ -1811,6 +1900,8 @@ static HRESULT GST_RemoveOutputPins(GSTImpl *This)
This->ppPins = NULL; This->ppPins = NULL;
gst_object_unref(This->gstfilter); gst_object_unref(This->gstfilter);
This->gstfilter = NULL; This->gstfilter = NULL;
gst_object_unref(This->container);
This->container = NULL;
BaseFilterImpl_IncrementPinVersion((BaseFilter*)This); BaseFilterImpl_IncrementPinVersion((BaseFilter*)This);
CoTaskMemFree(ppOldPins); CoTaskMemFree(ppOldPins);
return S_OK; return S_OK;

View File

@ -704,8 +704,6 @@ static HRESULT WINAPI Gstreamer_YUV2RGB_SetMediaType(TransformFilter *tf, PIN_DI
avgtime = vih->AvgTimePerFrame; avgtime = vih->AvgTimePerFrame;
width = vih->bmiHeader.biWidth; width = vih->bmiHeader.biWidth;
height = vih->bmiHeader.biHeight; height = vih->bmiHeader.biHeight;
if (vih->bmiHeader.biHeight > 0)
vih->bmiHeader.biHeight = -vih->bmiHeader.biHeight;
vih->bmiHeader.biBitCount = 24; vih->bmiHeader.biBitCount = 24;
vih->bmiHeader.biCompression = BI_RGB; vih->bmiHeader.biCompression = BI_RGB;
vih->bmiHeader.biSizeImage = width * abs(height) * 3; vih->bmiHeader.biSizeImage = width * abs(height) * 3;
@ -714,8 +712,6 @@ static HRESULT WINAPI Gstreamer_YUV2RGB_SetMediaType(TransformFilter *tf, PIN_DI
avgtime = vih->AvgTimePerFrame; avgtime = vih->AvgTimePerFrame;
width = vih->bmiHeader.biWidth; width = vih->bmiHeader.biWidth;
height = vih->bmiHeader.biHeight; height = vih->bmiHeader.biHeight;
if (vih->bmiHeader.biHeight > 0)
vih->bmiHeader.biHeight = -vih->bmiHeader.biHeight;
vih->bmiHeader.biBitCount = 24; vih->bmiHeader.biBitCount = 24;
vih->bmiHeader.biCompression = BI_RGB; vih->bmiHeader.biCompression = BI_RGB;
vih->bmiHeader.biSizeImage = width * abs(height) * 3; vih->bmiHeader.biSizeImage = width * abs(height) * 3;
@ -810,8 +806,6 @@ static HRESULT WINAPI Gstreamer_YUV2ARGB_SetMediaType(TransformFilter *tf, PIN_D
avgtime = vih->AvgTimePerFrame; avgtime = vih->AvgTimePerFrame;
width = vih->bmiHeader.biWidth; width = vih->bmiHeader.biWidth;
height = vih->bmiHeader.biHeight; height = vih->bmiHeader.biHeight;
if (vih->bmiHeader.biHeight > 0)
vih->bmiHeader.biHeight = -vih->bmiHeader.biHeight;
vih->bmiHeader.biBitCount = 32; vih->bmiHeader.biBitCount = 32;
vih->bmiHeader.biCompression = BI_RGB; vih->bmiHeader.biCompression = BI_RGB;
vih->bmiHeader.biSizeImage = width * abs(height) * 3; vih->bmiHeader.biSizeImage = width * abs(height) * 3;
@ -820,8 +814,6 @@ static HRESULT WINAPI Gstreamer_YUV2ARGB_SetMediaType(TransformFilter *tf, PIN_D
avgtime = vih->AvgTimePerFrame; avgtime = vih->AvgTimePerFrame;
width = vih->bmiHeader.biWidth; width = vih->bmiHeader.biWidth;
height = vih->bmiHeader.biHeight; height = vih->bmiHeader.biHeight;
if (vih->bmiHeader.biHeight > 0)
vih->bmiHeader.biHeight = -vih->bmiHeader.biHeight;
vih->bmiHeader.biBitCount = 32; vih->bmiHeader.biBitCount = 32;
vih->bmiHeader.biCompression = BI_RGB; vih->bmiHeader.biCompression = BI_RGB;
vih->bmiHeader.biSizeImage = width * abs(height) * 3; vih->bmiHeader.biSizeImage = width * abs(height) * 3;