diff --git a/modules/javafx.media/src/main/native/gstreamer/plugins/mfwrapper/mfwrapper.cpp b/modules/javafx.media/src/main/native/gstreamer/plugins/mfwrapper/mfwrapper.cpp index 6136fab06af..284a9824399 100644 --- a/modules/javafx.media/src/main/native/gstreamer/plugins/mfwrapper/mfwrapper.cpp +++ b/modules/javafx.media/src/main/native/gstreamer/plugins/mfwrapper/mfwrapper.cpp @@ -99,7 +99,8 @@ static gboolean mfwrapper_sink_set_caps(GstPad * pad, GstObject *parent, GstCaps static gboolean mfwrapper_activate(GstPad* pad, GstObject *parent); static gboolean mfwrapper_activatemode(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean active); -static HRESULT mfwrapper_load_decoder(GstMFWrapper *decoder, GstCaps *caps); +static HRESULT mfwrapper_load_decoder_caps(GstMFWrapper *decoder, GstCaps *caps); +static HRESULT mfwrapper_load_decoder_media_types(GstMFWrapper *decoder, GUID majorType, GUID subType); static gboolean mfwrapper_is_decoder_by_codec_id_supported(GstMFWrapper *decoder, gint codec_id); @@ -198,8 +199,8 @@ static void gst_mfwrapper_init(GstMFWrapper *decoder) decoder->is_eos = FALSE; decoder->is_decoder_initialized = FALSE; decoder->is_decoder_error = FALSE; - decoder->force_discontinuity = FALSE; - decoder->force_output_discontinuity = FALSE; + decoder->is_force_discontinuity = FALSE; + decoder->is_force_output_discontinuity = FALSE; // Initialize Media Foundation bool bCallCoUninitialize = true; @@ -227,6 +228,7 @@ static void gst_mfwrapper_init(GstMFWrapper *decoder) decoder->header = NULL; decoder->header_size = 0; + decoder->is_send_header = FALSE; decoder->width = 1920; decoder->height = 1080; @@ -236,22 +238,33 @@ static void gst_mfwrapper_init(GstMFWrapper *decoder) decoder->defaultStride = 0; decoder->pixel_num = 0; decoder->pixel_den = 0; + + decoder->is_set_caps = TRUE; } static void gst_mfwrapper_dispose(GObject* object) { GstMFWrapper *decoder = GST_MFWRAPPER(object); - // No need to free pDecoderBuffer, it will be release when interface is - // release by MF. + if (decoder->header != NULL) + { + delete[] decoder->header; + decoder->header = NULL; + decoder->header_size = 0; + } + SafeRelease(&decoder->pDecoderOutput); + // No need to free pDecoderBuffer, it will be released when + // pDecoderOutput is released. + decoder->pDecoderBuffer = NULL; SafeRelease(&decoder->pDecoder); for (int i = 0; i < MAX_COLOR_CONVERT; i++) { - // No need to free pColorConvertBuffer, it will be release when interface is - // release by MF. SafeRelease(&decoder->pColorConvertOutput[i]); + // No need to free pColorConvertBuffer, it will be released when + // pColorConvertOutput is released. + decoder->pColorConvertBuffer[i] = NULL; SafeRelease(&decoder->pColorConvert[i]); } @@ -310,7 +323,7 @@ static gboolean mfwrapper_is_decoder_by_codec_id_supported(GstMFWrapper *decoder "width", G_TYPE_INT, 1920, "height", G_TYPE_INT, 1080, NULL); - hr = mfwrapper_load_decoder(decoder, caps); + hr = mfwrapper_load_decoder_caps(decoder, caps); gst_caps_unref(caps); break; } @@ -393,7 +406,7 @@ static void mfwrapper_set_src_caps(GstMFWrapper *decoder) if (caps_event) { gst_pad_push_event(decoder->srcpad, caps_event); - decoder->force_output_discontinuity = TRUE; + decoder->is_force_output_discontinuity = TRUE; } gst_caps_unref(srcCaps); @@ -516,10 +529,10 @@ static gboolean mfwrapper_process_input(GstMFWrapper *decoder, GstBuffer *buf) HRESULT hr = MFCreateSample(&pSample); - if (SUCCEEDED(hr) && decoder->force_discontinuity) + if (SUCCEEDED(hr) && decoder->is_force_discontinuity) { hr = pSample->SetUINT32(MFSampleExtension_Discontinuity, TRUE); - decoder->force_discontinuity = FALSE; + decoder->is_force_discontinuity = FALSE; } if (SUCCEEDED(hr) && GST_BUFFER_PTS_IS_VALID(buf)) @@ -533,7 +546,8 @@ static gboolean mfwrapper_process_input(GstMFWrapper *decoder, GstBuffer *buf) else hr = E_FAIL; - if (SUCCEEDED(hr) && decoder->header != NULL && decoder->header_size > 0) + if (SUCCEEDED(hr) && decoder->is_send_header && + decoder->header != NULL && decoder->header_size > 0) dwBufferSize = (DWORD)decoder->header_size + (DWORD)info.size; else if (SUCCEEDED(hr)) dwBufferSize = (DWORD)info.size; @@ -550,8 +564,10 @@ static gboolean mfwrapper_process_input(GstMFWrapper *decoder, GstBuffer *buf) if (SUCCEEDED(hr)) unlock_buf = TRUE; - if (SUCCEEDED(hr) && decoder->header != NULL && decoder->header_size > 0) + if (SUCCEEDED(hr) && decoder->is_send_header && + decoder->header != NULL && decoder->header_size > 0) { + decoder->is_send_header = FALSE; if (dwBufferSize >= decoder->header_size) { memcpy_s(pbBuffer, dwBufferSize, decoder->header, decoder->header_size); @@ -579,13 +595,6 @@ static gboolean mfwrapper_process_input(GstMFWrapper *decoder, GstBuffer *buf) mfwrapper_nalu_to_start_code(pbBuffer, info.size); } - if (decoder->header != NULL) - { - delete[] decoder->header; - decoder->header = NULL; - decoder->header_size = 0; - } - if (unlock_buf) hr = pBuffer->Unlock(); @@ -1057,6 +1066,9 @@ static HRESULT mfwrapper_set_decoder_output_type(GstMFWrapper *decoder, { decoder->width = width; decoder->height = height; + + decoder->is_set_caps = TRUE; // Only set caps if resolution changed, so + // we do not trigger it during decoder reload. } hr = S_OK; // Ok if we do not have above attribute @@ -1081,13 +1093,21 @@ static HRESULT mfwrapper_set_decoder_output_type(GstMFWrapper *decoder, } // Init color converter if needed - if (SUCCEEDED(hr) && bInitColorConverter) + if (SUCCEEDED(hr) && bInitColorConverter && decoder->is_set_caps) { IMFTransform *pColorConvert = NULL; IMFSample *pColorConvertOutput = NULL; GUID outputType; CMFGSTBuffer *pMFGSTBuffer = NULL; + // Free old ones if any + for (int i = 0; i < MAX_COLOR_CONVERT; i++) + { + SafeRelease(&decoder->pColorConvertOutput[i]); + decoder->pColorConvertBuffer[i] = NULL; + SafeRelease(&decoder->pColorConvert[i]); + } + hr = mfwrapper_init_colorconvert(decoder, decoder->pDecoder, &pColorConvert, &pColorConvertOutput, &outputType, &pMFGSTBuffer); if (SUCCEEDED(hr) && IsEqualGUID(outputType, MFVideoFormat_NV12)) { @@ -1109,19 +1129,23 @@ static HRESULT mfwrapper_set_decoder_output_type(GstMFWrapper *decoder, } // Update caps on src pad in case if something changed - if (SUCCEEDED(hr)) + if (SUCCEEDED(hr) && decoder->is_set_caps) + { mfwrapper_set_src_caps(decoder); - // By now we should have output sample created. Figure out which one we - // will use to deliver frames and update media buffer in this sample to - // use GStreamer memory directly. - if (SUCCEEDED(hr)) - hr = mfwrapper_configure_media_buffer(decoder); + // By now we should have output sample created. Figure out which one we + // will use to deliver frames and update media buffer in this sample to + // use GStreamer memory directly. + if (SUCCEEDED(hr)) + hr = mfwrapper_configure_media_buffer(decoder); - // Configure GStreamer buffer pool to avoid memory allocation for each - // buffer. - if (SUCCEEDED(hr)) - hr = mfwrapper_configure_buffer_pool(decoder); + // Configure GStreamer buffer pool to avoid memory allocation for each + // buffer. + if (SUCCEEDED(hr)) + hr = mfwrapper_configure_buffer_pool(decoder); + + decoder->is_set_caps = FALSE; + } return hr; } @@ -1312,11 +1336,11 @@ static GstFlowReturn mfwrapper_deliver_sample(GstMFWrapper *decoder, GST_BUFFER_DURATION(pGstBuffer) = llDuration * 100; } - if (SUCCEEDED(hr) && decoder->force_output_discontinuity) + if (SUCCEEDED(hr) && decoder->is_force_output_discontinuity) { pGstBuffer = gst_buffer_make_writable(pGstBuffer); GST_BUFFER_FLAG_SET(pGstBuffer, GST_BUFFER_FLAG_DISCONT); - decoder->force_output_discontinuity = FALSE; + decoder->is_force_output_discontinuity = FALSE; } #if PTS_DEBUG @@ -1444,6 +1468,75 @@ static gboolean mfwrapper_push_sink_event(GstMFWrapper *decoder, GstEvent *event return ret; } +// This function will unload old instance of decoder and will create a new one. +// Input and Output media formats will be exactly same as old one. +// This function will not trigger format change downstream, so it should not +// be used as reload for format change. +// NOTE: This function should be called when stream lock is aquired. From +// serialized events for example like GST_EVENT_FLUSH_STOP. +static gboolean mfwrapper_reload_decoder(GstMFWrapper *decoder) +{ + HRESULT hr = S_OK; + + IMFMediaType *pInputType = NULL; + IMFMediaType *pOutputType = NULL; + + DWORD dwStatus = 0; + + GUID majorType; + GUID subType; + + if (decoder == NULL || decoder->pDecoder == NULL) + return false; + + // Save copy of old decoder + IMFTransform *pOldDecoder = decoder->pDecoder; + + decoder->pDecoder = NULL; + + hr = pOldDecoder->GetInputCurrentType(0, &pInputType); + if (SUCCEEDED(hr)) + hr = pOldDecoder->GetOutputCurrentType(0, &pOutputType); + if (SUCCEEDED(hr)) + hr = pInputType->GetGUID(MF_MT_MAJOR_TYPE, &majorType); + if (SUCCEEDED(hr)) + hr = pInputType->GetGUID(MF_MT_SUBTYPE, &subType); + + // Load decoder based on media types of current one + hr = mfwrapper_load_decoder_media_types(decoder, majorType, subType); + + // Copy input and output types and we should be good to go + if (SUCCEEDED(hr)) + hr = decoder->pDecoder->SetInputType(0, pInputType, 0); + if (SUCCEEDED(hr)) + hr = decoder->pDecoder->SetOutputType(0, pOutputType, 0); + + if (SUCCEEDED(hr)) + hr = decoder->pDecoder->GetInputStatus(0, &dwStatus); + + if (FAILED(hr) || dwStatus != MFT_INPUT_STATUS_ACCEPT_DATA) { + hr = E_FAIL; + } + + if (SUCCEEDED(hr)) + hr = decoder->pDecoder->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, NULL); + + if (SUCCEEDED(hr)) + hr = decoder->pDecoder->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, NULL); + + if (SUCCEEDED(hr)) + hr = decoder->pDecoder->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, NULL); + + SafeRelease(&pInputType); + SafeRelease(&pOutputType); + SafeRelease(&pOldDecoder); + + if (FAILED(hr)) + return false; + + return true; +} + static gboolean mfwrapper_sink_event(GstPad* pad, GstObject *parent, GstEvent *event) { gboolean ret = FALSE; @@ -1454,7 +1547,7 @@ static gboolean mfwrapper_sink_event(GstPad* pad, GstObject *parent, GstEvent *e { case GST_EVENT_SEGMENT: { - decoder->force_discontinuity = TRUE; + decoder->is_force_discontinuity = TRUE; ret = mfwrapper_push_sink_event(decoder, event); decoder->is_eos_received = FALSE; decoder->is_eos = FALSE; @@ -1471,18 +1564,32 @@ static gboolean mfwrapper_sink_event(GstPad* pad, GstObject *parent, GstEvent *e { if (!decoder->is_decoder_error) { - decoder->pDecoder-> - ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, 0); - for (int i = 0; i < MAX_COLOR_CONVERT; i++) + if (!mfwrapper_reload_decoder(decoder)) { - if (decoder->pColorConvert[i]) + decoder->is_decoder_error = TRUE; + gst_element_message_full(GST_ELEMENT(decoder), GST_MESSAGE_ERROR, + GST_STREAM_ERROR, GST_STREAM_ERROR_DECODE, + g_strdup("Failed to reload decoder"), NULL, + ("mfwrapper.c"), ("mfwrapper_sink_event"), 0); + } + else + { + // Send header after reload + decoder->is_send_header = TRUE; + + for (int i = 0; i < MAX_COLOR_CONVERT; i++) { - decoder->pColorConvert[i]-> - ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, 0); + if (decoder->pColorConvert[i]) + { + decoder->pColorConvert[i]-> + ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, 0); + } } } } + // Even if reload failed with critical error push event to unblock + // pipeline. ret = mfwrapper_push_sink_event(decoder, event); decoder->is_flushing = FALSE; @@ -1584,14 +1691,26 @@ static gboolean mfwrapper_get_mf_media_types(GstCaps *caps, GUID *pMajorType, GU return FALSE; } -static HRESULT mfwrapper_load_decoder(GstMFWrapper *decoder, GstCaps *caps) +static HRESULT mfwrapper_load_decoder_caps(GstMFWrapper *decoder, GstCaps *caps) { - HRESULT hr = S_OK; - UINT32 count = 0; - GUID majorType; GUID subType; + if (decoder->pDecoder) + return S_OK; + + if (!mfwrapper_get_mf_media_types(caps, &majorType, &subType)) + return E_FAIL; + + return mfwrapper_load_decoder_media_types(decoder, majorType, subType); +} + +static HRESULT mfwrapper_load_decoder_media_types(GstMFWrapper *decoder, + GUID majorType, GUID subType) +{ + HRESULT hr = S_OK; + UINT32 count = 0; + IMFActivate **ppActivate = NULL; MFT_REGISTER_TYPE_INFO info = { 0 }; @@ -1599,9 +1718,6 @@ static HRESULT mfwrapper_load_decoder(GstMFWrapper *decoder, GstCaps *caps) if (decoder->pDecoder) return S_OK; - if (!mfwrapper_get_mf_media_types(caps, &majorType, &subType)) - return E_FAIL; - info.guidMajorType = majorType; info.guidSubtype = subType; @@ -1833,6 +1949,8 @@ static gboolean mfwrapper_init_mf(GstMFWrapper *decoder, GstCaps *caps) decoder->header = NULL; return FALSE; } + + decoder->is_send_header = TRUE; } } diff --git a/modules/javafx.media/src/main/native/gstreamer/plugins/mfwrapper/mfwrapper.h b/modules/javafx.media/src/main/native/gstreamer/plugins/mfwrapper/mfwrapper.h index 1d5646eee68..3a7c4911cb9 100644 --- a/modules/javafx.media/src/main/native/gstreamer/plugins/mfwrapper/mfwrapper.h +++ b/modules/javafx.media/src/main/native/gstreamer/plugins/mfwrapper/mfwrapper.h @@ -77,8 +77,8 @@ struct _GstMFWrapper // This flag should be set if decoder calls failed. gboolean is_decoder_error; - gboolean force_discontinuity; - gboolean force_output_discontinuity; + gboolean is_force_discontinuity; + gboolean is_force_output_discontinuity; HRESULT hr_mfstartup; @@ -94,6 +94,7 @@ struct _GstMFWrapper BYTE *header; gsize header_size; + gboolean is_send_header; guint width; guint height; @@ -103,6 +104,8 @@ struct _GstMFWrapper guint defaultStride; guint pixel_num; guint pixel_den; + + gboolean is_set_caps; }; struct _GstMFWrapperClass