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;
BOOL discont, initial, ignore_flush;
GstElement *container;
GstElement *gstfilter;
GstPad *my_src, *their_sink;
GstBus *bus;
@ -78,6 +79,8 @@ struct GSTOutPin {
BaseOutputPin pin;
IQualityControl IQualityControl_iface;
GstElement *flipfilter;
GstPad *flip_sink, *flip_src;
GstPad *their_src;
GstPad *my_sink;
GstBufferPool *gstpool;
@ -751,8 +754,14 @@ static void removed_decoded_pad(GstElement *bin, GstPad *pad, gpointer user)
}
if (x == This->cStreams)
goto out;
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);
pin->their_src = NULL;
out:
@ -819,10 +828,72 @@ static void init_new_decoded_pad(GstElement *bin, GstPad *pad, GSTImpl *This)
pin->isvid = isvid;
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);
exit:
TRACE("Linking: %i\n", ret);
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)
{
GSTImpl *This = (GSTImpl*)user;
int x;
int x, ret;
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];
if (!pin->their_src) {
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;
gst_object_ref(pin->their_src);
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);
}
This->container = gst_bin_new(NULL);
This->gstfilter = gst_element_factory_make("decodebin", NULL);
if (!This->gstfilter) {
FIXME("Could not make source filter, are gstreamer-plugins-* installed for %u bits?\n",
8 * (int)sizeof(void*));
return E_FAIL;
}
gst_bin_add(GST_BIN(This->container), This->gstfilter);
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-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 */
This->initial = This->discont = TRUE;
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);
gst_element_get_state(This->gstfilter, NULL, NULL, -1);
gst_element_get_state(This->container, NULL, NULL, -1);
if (ret < 0) {
WARN("Ret: %i\n", ret);
@ -1078,8 +1160,8 @@ static HRESULT GST_Connect(GSTInPin *pPin, IPin *pConnectPin, ALLOCATOR_PROPERTI
*props = This->props;
This->ignore_flush = TRUE;
gst_element_set_state(This->gstfilter, GST_STATE_READY);
gst_element_get_state(This->gstfilter, NULL, NULL, -1);
gst_element_set_state(This->container, GST_STATE_READY);
gst_element_get_state(This->container, NULL, NULL, -1);
This->ignore_flush = FALSE;
This->initial = FALSE;
@ -1262,10 +1344,10 @@ static HRESULT WINAPI GST_Stop(IBaseFilter *iface)
mark_wine_thread();
if (This->gstfilter) {
if (This->container) {
This->ignore_flush = TRUE;
gst_element_set_state(This->gstfilter, GST_STATE_READY);
gst_element_get_state(This->gstfilter, NULL, NULL, -1);
gst_element_set_state(This->container, GST_STATE_READY);
gst_element_get_state(This->container, NULL, NULL, -1);
This->ignore_flush = FALSE;
}
return S_OK;
@ -1280,19 +1362,19 @@ static HRESULT WINAPI GST_Pause(IBaseFilter *iface)
TRACE("(%p)\n", This);
if (!This->gstfilter)
if (!This->container)
return VFW_E_NOT_CONNECTED;
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)
return S_OK;
if (now != GST_STATE_PLAYING)
hr = IBaseFilter_Run(iface, -1);
if (FAILED(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)
hr = S_FALSE;
return hr;
@ -1310,26 +1392,26 @@ static HRESULT WINAPI GST_Run(IBaseFilter *iface, REFERENCE_TIME tStart)
mark_wine_thread();
if (!This->gstfilter)
if (!This->container)
return VFW_E_NOT_CONNECTED;
EnterCriticalSection(&This->filter.csFilter);
This->filter.rtStreamStart = tStart;
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)
return S_OK;
if (now == GST_STATE_PAUSED) {
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)
return S_FALSE;
return S_OK;
}
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;
for (i = 0; i < This->cStreams; i++) {
@ -1355,12 +1437,12 @@ static HRESULT WINAPI GST_GetState(IBaseFilter *iface, DWORD dwMilliSecsTimeout,
mark_wine_thread();
if (!This->gstfilter) {
if (!This->container) {
*pState = State_Stopped;
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)
hr = VFW_S_STATE_INTERMEDIATE;
@ -1642,7 +1724,14 @@ static ULONG WINAPI GSTOutPin_Release(IPin *iface)
if (!refCount) {
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->my_sink);
@ -1793,10 +1882,10 @@ static HRESULT GST_RemoveOutputPins(GSTImpl *This)
TRACE("(%p)\n", This);
mark_wine_thread();
if (!This->gstfilter)
if (!This->container)
return S_OK;
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_object_unref(This->my_src);
gst_object_unref(This->their_sink);
@ -1811,6 +1900,8 @@ static HRESULT GST_RemoveOutputPins(GSTImpl *This)
This->ppPins = NULL;
gst_object_unref(This->gstfilter);
This->gstfilter = NULL;
gst_object_unref(This->container);
This->container = NULL;
BaseFilterImpl_IncrementPinVersion((BaseFilter*)This);
CoTaskMemFree(ppOldPins);
return S_OK;

View File

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