diff options
author | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
---|---|---|
committer | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
commit | 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch) | |
tree | 12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/h264/MFTDecoder.cpp | |
parent | 537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff) | |
download | winamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz |
Initial community commit
Diffstat (limited to 'Src/h264/MFTDecoder.cpp')
-rw-r--r-- | Src/h264/MFTDecoder.cpp | 605 |
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; +} |