aboutsummaryrefslogtreecommitdiff
path: root/Src/h264/MFTDecoder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Src/h264/MFTDecoder.cpp')
-rw-r--r--Src/h264/MFTDecoder.cpp605
1 files changed, 605 insertions, 0 deletions
diff --git a/Src/h264/MFTDecoder.cpp b/Src/h264/MFTDecoder.cpp
new file mode 100644
index 00000000..6c8d6127
--- /dev/null
+++ b/Src/h264/MFTDecoder.cpp
@@ -0,0 +1,605 @@
+#include "MFTDecoder.h"
+#include <Mfapi.h>
+#include <wmcodecdsp.h>
+#include <Mferror.h>
+
+//-----------------------------------------------------------------------------
+// GetDefaultStride
+//
+// Gets the default stride for a video frame, assuming no extra padding bytes.
+//
+//-----------------------------------------------------------------------------
+
+HRESULT GetDefaultStride(IMFMediaType *pType, LONG *plStride)
+{
+ LONG lStride = 0;
+
+ // Try to get the default stride from the media type.
+ HRESULT hr = pType->GetUINT32(MF_MT_DEFAULT_STRIDE, (UINT32*)&lStride);
+ if (FAILED(hr))
+ {
+ // Attribute not set. Try to calculate the default stride.
+ GUID subtype = GUID_NULL;
+
+ UINT32 width = 0;
+ UINT32 height = 0;
+
+ // Get the subtype and the image size.
+ hr = pType->GetGUID(MF_MT_SUBTYPE, &subtype);
+ if (SUCCEEDED(hr))
+ {
+ hr = MFGetAttributeSize(pType, MF_MT_FRAME_SIZE, &width, &height);
+ }
+ if (SUCCEEDED(hr))
+ {
+ hr = MFGetStrideForBitmapInfoHeader(subtype.Data1, width, &lStride);
+ }
+
+ // Set the attribute for later reference.
+ if (SUCCEEDED(hr))
+ {
+ (void)pType->SetUINT32(MF_MT_DEFAULT_STRIDE, UINT32(lStride));
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ *plStride = lStride;
+ }
+ return hr;
+}
+
+MFTDecoder::MFTDecoder()
+{
+ decoder = 0;
+ stride = 0;
+ width = 0;
+ height = 0;
+}
+
+MFTDecoder::~MFTDecoder()
+{
+ if (decoder) {
+ decoder->Release();
+ }
+}
+
+static HRESULT CreateInputMediaType(IMFMediaType **_media_type)
+{
+ HRESULT hr=E_FAIL;
+ IMFMediaType *media_type=0;
+
+ do {
+ hr = MFCreateMediaType(&media_type);
+ if (FAILED(hr)) {
+ break;
+ }
+ hr = media_type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
+ if (FAILED(hr)) {
+ break;
+ }
+
+ hr = media_type->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264);
+ if (FAILED(hr)) {
+ break;
+ }
+
+ *_media_type = media_type;
+ return S_OK;
+ } while(0);
+
+ if (media_type) {
+ media_type->Release();
+ }
+ return hr;
+}
+
+HRESULT MFTDecoder::Open()
+{
+ HRESULT hr=E_FAIL;
+ hr = CoCreateInstance(CLSID_CMSH264DecoderMFT, NULL, CLSCTX_INPROC_SERVER, __uuidof(IMFTransform), (void**)&decoder);
+
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ /* set input */
+ IMFMediaType *media_type=0;
+ hr = CreateInputMediaType(&media_type);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ hr = decoder->SetInputType(0, media_type, 0);
+ media_type->Release();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ /* set output */
+
+ hr = decoder->GetOutputAvailableType(0, 0, &media_type);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ hr = decoder->SetOutputType(0, media_type, 0);
+ media_type->Release();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ decoder->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0);
+ decoder->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0);
+
+ return S_OK;
+}
+MFOffset MFTDecoder::MakeOffset(float v)
+{
+ MFOffset offset{};
+ offset.value = short(v);
+ offset.fract = WORD(65536 * (v - offset.value));
+ return offset;
+}
+MFVideoArea MFTDecoder::MakeArea(float x, float y, DWORD width, DWORD height)
+{
+ MFVideoArea area{};
+ area.OffsetX = MakeOffset(x);
+ area.OffsetY = MakeOffset(y);
+ area.Area.cx = width;
+ area.Area.cy = height;
+ return area;
+}
+
+HRESULT MFTDecoder::GetVideoDisplayArea(IMFMediaType* pType, MFVideoArea* pArea)
+{
+ HRESULT hr = S_OK;
+ BOOL bPanScan = FALSE;
+ UINT32 width = 0, height = 0;
+
+ bPanScan = MFGetAttributeUINT32(pType, MF_MT_PAN_SCAN_ENABLED, FALSE);
+
+ // In pan-and-scan mode, try to get the pan-and-scan region.
+ if (bPanScan)
+ {
+ hr = pType->GetBlob(MF_MT_PAN_SCAN_APERTURE, (UINT8*)pArea,
+ sizeof(MFVideoArea), NULL);
+ }
+
+ // If not in pan-and-scan mode, or the pan-and-scan region is not set,
+ // get the minimimum display aperture.
+
+ if (!bPanScan || hr == MF_E_ATTRIBUTENOTFOUND)
+ {
+ hr = pType->GetBlob(MF_MT_MINIMUM_DISPLAY_APERTURE, (UINT8*)pArea,
+ sizeof(MFVideoArea), NULL);
+
+ if (hr == MF_E_ATTRIBUTENOTFOUND)
+ {
+ // Minimum display aperture is not set.
+
+ // For backward compatibility with some components,
+ // check for a geometric aperture.
+
+ hr = pType->GetBlob(MF_MT_GEOMETRIC_APERTURE, (UINT8*)pArea,
+ sizeof(MFVideoArea), NULL);
+ }
+
+ // Default: Use the entire video area.
+
+ if (hr == MF_E_ATTRIBUTENOTFOUND)
+ {
+ hr = MFGetAttributeSize(pType, MF_MT_FRAME_SIZE, &width, &height);
+
+ if (SUCCEEDED(hr))
+ {
+ *pArea = MakeArea(0.0, 0.0, width, height);
+ }
+ }
+ }
+ return hr;
+}
+
+HRESULT MFTDecoder::GetOutputFormat(UINT *width, UINT *height, bool *flip, double *aspect)
+{
+ HRESULT hr=E_FAIL;
+ IMFMediaType *media_type = 0;
+ MFVideoArea pArea;
+
+ do {
+ hr = decoder->GetOutputCurrentType(0, &media_type);
+ if (FAILED(hr)) {
+ break;
+ }
+
+ //if (width && height) {
+ // hr = MFGetAttributeSize(media_type, MF_MT_FRAME_SIZE, width, height);
+ // if (FAILED(hr)) {
+ // break;
+ // }
+ //}
+ if (width && height) {
+ hr = GetVideoDisplayArea(media_type, &pArea);
+ if (FAILED(hr)) {
+ break;
+ }
+ *width = pArea.Area.cx;
+ *height = pArea.Area.cy;
+ }
+
+ if (flip) {
+ LONG stride;
+ hr = GetDefaultStride(media_type, &stride);
+ if (FAILED(hr)) {
+ break;
+ }
+ *flip = stride<0;
+ }
+
+ if (aspect) {
+ MFRatio PAR = {0};
+ hr = MFGetAttributeRatio(media_type, MF_MT_PIXEL_ASPECT_RATIO,
+ (UINT32*)&PAR.Numerator,
+ (UINT32*)&PAR.Denominator);
+
+ if (FAILED(hr)) {
+ *aspect = 1.0;
+ } else {
+ *aspect = (double)PAR.Numerator / (double)PAR.Denominator;
+ }
+
+ }
+ } while(0);
+ if (media_type) {
+ media_type->Release();
+ }
+ return hr;
+}
+
+static HRESULT ConfigureOutput(IMFTransform *decoder, LONG *stride)
+{
+ HRESULT hr = S_OK;
+ IMFMediaType *media_type = 0;
+ AM_MEDIA_TYPE *format = NULL;
+ int index=0;
+ while(SUCCEEDED(hr)) {
+ hr = decoder->GetOutputAvailableType(0, index++, &media_type);
+ if (FAILED(hr)) {
+ break;
+ }
+ media_type->GetRepresentation(FORMAT_MFVideoFormat, (LPVOID*)&format);
+ MFVIDEOFORMAT* z = (MFVIDEOFORMAT*)format->pbFormat;
+ unsigned int surface_format = z->surfaceInfo.Format;
+ media_type->FreeRepresentation(FORMAT_MFVideoFormat, (LPVOID)format);
+
+ if (surface_format == '21VY') { // MFVideoFormat_YV12
+ hr = GetDefaultStride(media_type, stride);
+ hr = decoder->SetOutputType(0, media_type, 0);
+ break;
+ }
+
+ }
+ if(media_type) {
+ media_type->Release();
+ }
+ return hr;
+}
+
+HRESULT MFTDecoder::Feed(const void *data, size_t data_size, uint64_t timestamp_hundred_nanos)
+{
+ HRESULT hr=E_FAIL;
+ const BYTE start_code[] = {0, 0, 0, 1};
+ IMFMediaBuffer *buffer = 0;
+ BYTE *buffer_pointer = 0;
+ IMFSample *sample = 0;
+
+ do {
+ hr = MFCreateMemoryBuffer((DWORD)data_size+4, &buffer);
+ if (FAILED(hr)) {
+ break;
+ }
+ hr = buffer->Lock(&buffer_pointer, NULL, NULL);
+ if (FAILED(hr)) {
+ break;
+ }
+
+ memcpy(buffer_pointer, start_code, 4);
+ memcpy(buffer_pointer+4, data, data_size);
+
+ hr = buffer->Unlock();
+ if (FAILED(hr)) {
+ break;
+ }
+
+ hr = buffer->SetCurrentLength((DWORD)data_size+4);
+ if (FAILED(hr)) {
+ break;
+ }
+
+ hr = MFCreateSample(&sample);
+ if (FAILED(hr)) {
+ break;
+ }
+
+ hr = sample->AddBuffer(buffer);
+ if (FAILED(hr)) {
+ break;
+ }
+
+ hr = sample->SetSampleTime(timestamp_hundred_nanos);
+ if (FAILED(hr)) {
+ break;
+ }
+
+ hr = decoder->ProcessInput(0, sample, 0);
+ if (FAILED(hr)) {
+ break;
+ }
+ } while(0);
+ if (buffer) {
+ buffer->Release();
+ }
+
+ if (sample) {
+ sample->Release();
+ }
+ return hr;
+}
+
+HRESULT MFTDecoder::FeedRaw(const void *data, size_t data_size, uint64_t timestamp_hundred_nanos)
+{
+ HRESULT hr=E_FAIL;
+ IMFMediaBuffer *buffer = 0;
+ BYTE *buffer_pointer = 0;
+ IMFSample *sample = 0;
+
+ do {
+ hr = MFCreateMemoryBuffer((DWORD)data_size, &buffer);
+ if (FAILED(hr)) {
+ break;
+ }
+ hr = buffer->Lock(&buffer_pointer, NULL, NULL);
+ if (FAILED(hr)) {
+ break;
+ }
+
+ memcpy(buffer_pointer, data, data_size);
+
+ hr = buffer->Unlock();
+ if (FAILED(hr)) {
+ break;
+ }
+
+ hr = buffer->SetCurrentLength((DWORD)data_size);
+ if (FAILED(hr)) {
+ break;
+ }
+
+ hr = MFCreateSample(&sample);
+ if (FAILED(hr)) {
+ break;
+ }
+
+ hr = sample->AddBuffer(buffer);
+ if (FAILED(hr)) {
+ break;
+ }
+
+ hr = sample->SetSampleTime(timestamp_hundred_nanos);
+ if (FAILED(hr)) {
+ break;
+ }
+
+ hr = decoder->ProcessInput(0, sample, 0);
+ if (FAILED(hr)) {
+ break;
+ }
+ } while(0);
+ if (buffer) {
+ buffer->Release();
+ }
+
+ if (sample) {
+ sample->Release();
+ }
+ return hr;
+}
+
+static HRESULT CreateOutputSample(IMFTransform *decoder, IMFSample **_output_sample)
+{
+ HRESULT hr=E_FAIL;
+ MFT_OUTPUT_STREAM_INFO stream_info;
+ IMFMediaBuffer *media_buffer = 0;
+ IMFSample *sample = 0;
+
+ do {
+ hr = MFCreateSample(&sample);
+ if (FAILED(hr)) {
+ break;
+ }
+
+ hr = decoder->GetOutputStreamInfo(0, &stream_info);
+ if (FAILED(hr)) {
+ break;
+ }
+
+ hr = MFCreateAlignedMemoryBuffer(stream_info.cbSize, MF_16_BYTE_ALIGNMENT, &media_buffer);
+ if (FAILED(hr)) {
+ break;
+ }
+
+ hr = sample->AddBuffer(media_buffer);
+ if (FAILED(hr)) {
+ break;
+ }
+
+ if (media_buffer) {
+ media_buffer->Release();
+ }
+ *_output_sample = sample;
+ return S_OK;
+ } while(0);
+ if (sample) {
+ sample->Release();
+ }
+
+ if (media_buffer) {
+ media_buffer->Release();
+ }
+ return hr;
+}
+
+// Release the events that an MFT might allocate in IMFTransform::ProcessOutput().
+static void ReleaseEventCollection(MFT_OUTPUT_DATA_BUFFER &pBuffers)
+{
+ if (pBuffers.pEvents) {
+ pBuffers.pEvents->Release();
+ pBuffers.pEvents = NULL;
+ }
+
+}
+HRESULT MFTDecoder::GetFrame(IMFMediaBuffer **out_buffer, uint64_t *hundrednanos)
+{
+ HRESULT hr=E_FAIL;
+ IMFSample *output_sample=0;
+ DWORD mftStatus;
+ do {
+ hr = CreateOutputSample(decoder, &output_sample);
+ if (FAILED(hr)) {
+ break;
+ }
+
+ MFT_OUTPUT_DATA_BUFFER mftDataBuffer = {0, };
+ mftDataBuffer.pSample = output_sample;
+ mftStatus = 0;
+
+ hr = decoder->ProcessOutput(0, 1, &mftDataBuffer, &mftStatus);
+ if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
+ break;
+ }
+ if (hr == MF_E_TRANSFORM_STREAM_CHANGE) {
+ ConfigureOutput(decoder, &stride);
+ width=0;
+ height=0;
+ } else if (FAILED(hr)) {
+ break;
+ } else {
+ if (mftDataBuffer.pSample) {
+ IMFMediaBuffer *mediaBuffer;
+ hr = mftDataBuffer.pSample->GetBufferByIndex(0, &mediaBuffer);
+ if (FAILED(hr)) {
+ break;
+ }
+
+ LONGLONG sample_time;
+ output_sample->GetSampleTime(&sample_time);
+ if (hundrednanos) {
+ *hundrednanos = sample_time;
+ }
+ *out_buffer = mediaBuffer;
+ }
+ ReleaseEventCollection(mftDataBuffer);
+ }
+ } while (0);
+
+ if (output_sample) {
+ output_sample->Release();
+ }
+
+ return hr;
+}
+
+HRESULT MFTDecoder::Flush()
+{
+ return decoder->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, 0);
+}
+
+HRESULT MFTDecoder::Drain()
+{
+ return decoder->ProcessMessage(MFT_MESSAGE_COMMAND_DRAIN, 0);
+}
+
+HRESULT MFTDecoder::GetFrame(YV12_PLANES **data, void **decoder_data, uint64_t *mft_timestamp)
+{
+ HRESULT hr=E_FAIL;
+ IMFMediaBuffer *media_buffer = 0;
+ IMFMediaType *media_type = 0;
+ do {
+ if (!height || !stride) {
+ hr = decoder->GetOutputCurrentType(0, &media_type);
+ if (FAILED(hr)) {
+ break;
+ }
+
+ hr = MFGetAttributeSize(media_type, MF_MT_FRAME_SIZE, &width, &height);
+ if (FAILED(hr)) {
+ break;
+ }
+
+ hr = GetDefaultStride(media_type, &stride);
+ if (FAILED(hr)) {
+ break;
+ }
+
+ }
+ hr = this->GetFrame(&media_buffer, mft_timestamp);
+ if (FAILED(hr)) {
+ break;
+ }
+
+ YV12_PLANES *planes = (YV12_PLANES *)malloc(sizeof(YV12_PLANES));
+ IMF2DBuffer *buffer2d=0;
+ if (SUCCEEDED(media_buffer->QueryInterface(&buffer2d))) {
+ BYTE *pbScanline0;
+ LONG pitch;
+ buffer2d->Lock2D(&pbScanline0, &pitch);
+ planes->y.baseAddr = pbScanline0;
+ planes->y.rowBytes = pitch;
+ pbScanline0 += pitch * height;
+ planes->v.baseAddr = pbScanline0;
+ planes->v.rowBytes = pitch/2;
+ pbScanline0 += pitch * height/4;
+ planes->u.baseAddr = pbScanline0;
+ planes->u.rowBytes = pitch/2;
+ buffer2d->Release();
+ } else {
+ DWORD length, max_length;
+ BYTE *video_data;
+ media_buffer->Lock(&video_data, &length, &max_length);
+
+ planes->y.baseAddr = video_data;
+ planes->y.rowBytes = stride;
+ video_data += stride * height;
+ planes->v.baseAddr = video_data;
+ planes->v.rowBytes = stride/2;
+ video_data += (stride/2) * (height/2);
+ planes->u.baseAddr = video_data;
+ planes->u.rowBytes = stride/2;
+ }
+ *data = planes;
+ *decoder_data = media_buffer;
+ } while(0);
+
+ if (media_type) {
+ media_type->Release();
+ }
+
+ return hr;
+}
+
+HRESULT MFTDecoder::FreeFrame(YV12_PLANES *data, void *decoder_data)
+{
+ IMFMediaBuffer *buffer= (IMFMediaBuffer *)decoder_data;
+ if (buffer) {
+ IMF2DBuffer *buffer2d=0;
+ if (SUCCEEDED(buffer->QueryInterface(&buffer2d))) {
+ buffer2d->Unlock2D();
+ buffer2d->Release();
+ } else {
+ buffer->Unlock();
+ }
+ buffer->Release();
+ }
+ free(data);
+ return S_OK;
+}