diff options
Diffstat (limited to 'Src/h264')
-rw-r--r-- | Src/h264/MFTDecoder.cpp | 605 | ||||
-rw-r--r-- | Src/h264/MFTDecoder.h | 38 | ||||
-rw-r--r-- | Src/h264/NSVFactory.cpp | 22 | ||||
-rw-r--r-- | Src/h264/NSVFactory.h | 20 | ||||
-rw-r--r-- | Src/h264/annexb.c | 224 | ||||
-rw-r--r-- | Src/h264/annexb.h | 24 | ||||
-rw-r--r-- | Src/h264/api__h264.h | 13 | ||||
-rw-r--r-- | Src/h264/avi_h264_decoder.cpp | 144 | ||||
-rw-r--r-- | Src/h264/avi_h264_decoder.h | 42 | ||||
-rw-r--r-- | Src/h264/h264.rc | 76 | ||||
-rw-r--r-- | Src/h264/h264.sln | 41 | ||||
-rw-r--r-- | Src/h264/h264.vcxproj | 271 | ||||
-rw-r--r-- | Src/h264/h264.vcxproj.filters | 83 | ||||
-rw-r--r-- | Src/h264/h264_flv_decoder.cpp | 220 | ||||
-rw-r--r-- | Src/h264/h264_flv_decoder.h | 42 | ||||
-rw-r--r-- | Src/h264/h264_mkv_decoder.cpp | 237 | ||||
-rw-r--r-- | Src/h264/h264_mkv_decoder.h | 44 | ||||
-rw-r--r-- | Src/h264/h264_mp4_decoder.cpp | 212 | ||||
-rw-r--r-- | Src/h264/h264_mp4_decoder.h | 37 | ||||
-rw-r--r-- | Src/h264/main.cpp | 96 | ||||
-rw-r--r-- | Src/h264/nsv_h264_decoder.cpp | 77 | ||||
-rw-r--r-- | Src/h264/nsv_h264_decoder.h | 24 | ||||
-rw-r--r-- | Src/h264/resource.h | 14 | ||||
-rw-r--r-- | Src/h264/version.rc2 | 39 |
24 files changed, 2645 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; +} diff --git a/Src/h264/MFTDecoder.h b/Src/h264/MFTDecoder.h new file mode 100644 index 00000000..84fd62c5 --- /dev/null +++ b/Src/h264/MFTDecoder.h @@ -0,0 +1,38 @@ +#pragma once +#include <Mftransform.h> +#include <bfc/platform/types.h> +#include "../Winamp/wa_ipc.h" + +struct nullsoft_h264_frame_data +{ + void *data; + void *decoder_data; + uint64_t local_timestamp; +}; + +class MFTDecoder +{ +public: + MFTDecoder(); + ~MFTDecoder(); + + HRESULT Open(); + HRESULT Feed(const void *data, size_t data_size, uint64_t timestamp_hundred_nanos); + HRESULT FeedRaw(const void *data, size_t data_size, uint64_t timestamp_hundred_nanos); + HRESULT GetFrame(IMFMediaBuffer **output_buffer, uint64_t *hundrednanos); + HRESULT GetFrame(YV12_PLANES **data, void **decoder_data, uint64_t *mft_timestamp); + HRESULT FreeFrame(YV12_PLANES *data, void *decoder_data); + HRESULT GetOutputFormat(UINT *width, UINT *height, bool *flip, double *aspect); + HRESULT Flush(); + HRESULT Drain(); + + HRESULT GetVideoDisplayArea(IMFMediaType* pType, MFVideoArea* pArea); + + IMFTransform *decoder; + LONG stride; + UINT32 width, height; + +private: + MFVideoArea MakeArea(float x, float y, DWORD width, DWORD height); + MFOffset MakeOffset(float v); +};
\ No newline at end of file diff --git a/Src/h264/NSVFactory.cpp b/Src/h264/NSVFactory.cpp new file mode 100644 index 00000000..6be624a2 --- /dev/null +++ b/Src/h264/NSVFactory.cpp @@ -0,0 +1,22 @@ +#include "NSVFactory.h" +#include "nsv_h264_decoder.h" +#include "api__h264.h" +#include "../nsv/nsvlib.h" + +IVideoDecoder *NSVFactory::CreateVideoDecoder(int w, int h, double framerate, unsigned int fmt, int *flip) +{ + if (fmt == NSV_MAKETYPE('H','2','6','4')) + { + *flip=0; + void *mem = WASABI_API_MEMMGR->sysMalloc(sizeof(H264_Decoder)); + H264_Decoder *dec = new (mem) H264_Decoder(); + return dec; + } + return NULL; +} + +#define CBCLASS NSVFactory +START_DISPATCH; +CB(SVC_NSVFACTORY_CREATEVIDEODECODER, CreateVideoDecoder) +END_DISPATCH; +#undef CBCLASS diff --git a/Src/h264/NSVFactory.h b/Src/h264/NSVFactory.h new file mode 100644 index 00000000..9d1acb5f --- /dev/null +++ b/Src/h264/NSVFactory.h @@ -0,0 +1,20 @@ +#pragma once +#include "../nsv/svc_nsvFactory.h" + +// {262CCE92-78DC-47a9-AFFB-2471799CA799} +static const GUID h264_nsv_guid = +{ 0x262cce92, 0x78dc, 0x47a9, { 0xaf, 0xfb, 0x24, 0x71, 0x79, 0x9c, 0xa7, 0x99 } }; + + + +class NSVFactory : public svc_nsvFactory +{ +public: + static const char *getServiceName() { return "H.264 NSV Decoder"; } + static GUID getServiceGuid() { return h264_nsv_guid; } + IVideoDecoder *CreateVideoDecoder(int w, int h, double framerate, unsigned int fmt, int *flip); + +protected: + RECVS_DISPATCH; +}; + diff --git a/Src/h264/annexb.c b/Src/h264/annexb.c new file mode 100644 index 00000000..cc6e10e0 --- /dev/null +++ b/Src/h264/annexb.c @@ -0,0 +1,224 @@ +#include "annexb.h" +#include <bfc/platform/types.h> + +enum +{ + InitialUnit = 0, + NewUnit = 1, // start finding start code during AddData + MidUnit = 2, // need to find the next start code from next AddData call to form a complete unit + UnitReady = 3, // a new unit is ready and we are waiting for a GetUnit call +}; + +typedef struct annex_b_demuxer +{ + size_t buffer_position; + size_t number_of_zero_words; // number of zero words as identified from the first unit + size_t current_zero_words; // current zero word count, saved in case NALU crosses two AddData calls + int end_of_stream; // set to 1 when there's no more data (so we know not to look for the next start code) + int state; + size_t buffer_size; + uint8_t buffer[1]; // make sure this is last +} AnnexBDemuxer; + +int AddData(const uint8_t **data, size_t *data_len); // data and length remaining are updated on exit. if data_len>0 on exit, call again after calling GetUnit +void EndOfStream(); + + +h264_annexb_demuxer_t AnnexB_Create(int size) +{ + AnnexBDemuxer *demuxer = (AnnexBDemuxer *)malloc(sizeof(AnnexBDemuxer) + size); + demuxer->buffer_size = size; // MAX_CODED_FRAME_SIZE; + demuxer->state = InitialUnit; + demuxer->buffer_position = 0; + demuxer->number_of_zero_words = 0; + demuxer->current_zero_words = 0; + demuxer->end_of_stream = 0; + return (h264_annexb_demuxer_t)demuxer; +} + +static int AnnexB_GetByte(const uint8_t **data, size_t *data_len, uint8_t *data_byte) +{ + if (*data_len) + { + *data_byte = **data; + *data = *data + 1; + *data_len = *data_len - 1;; + return 1; + } + else + return 0; +} + +int AnnexB_AddData(h264_annexb_demuxer_t d, const void **_data, size_t *data_len) +{ + AnnexBDemuxer *demuxer = (AnnexBDemuxer *)d; + if (demuxer) + { + const uint8_t **data = (const uint8_t **)_data; // cast to something easier to do pointer math with + if (demuxer->state == InitialUnit) + { + // find start code with unknown number of initial zero bytes + while(demuxer->number_of_zero_words == 0) + { + uint8_t data_byte; + if (AnnexB_GetByte(data, data_len, &data_byte)) + { + if (data_byte == 0) + { + demuxer->current_zero_words++; + } + else if (data_byte == 1 && demuxer->current_zero_words >= 2) + { + demuxer->number_of_zero_words = demuxer->current_zero_words; + demuxer->current_zero_words = 0; + demuxer->state = MidUnit; + } + else + { + // re-sync + demuxer->current_zero_words = 0; + } + } + else + { + return AnnexB_NeedMoreData; + } + } + } + else if (demuxer->state == NewUnit) + { + // find start code with known number of initial zero b ytes + while (demuxer->state == NewUnit) + { + uint8_t data_byte; + if (AnnexB_GetByte(data, data_len, &data_byte)) + { + if (data_byte == 0) + { + demuxer->current_zero_words++; + } + else if (data_byte == 1 && demuxer->current_zero_words >= 2) // we might get more start words than required + { + demuxer->current_zero_words = 0; + demuxer->state = MidUnit; + } + else + { + // re-sync + demuxer->current_zero_words = 0; + } + } + else + { + return AnnexB_NeedMoreData; + } + } + } + + if (demuxer->state == MidUnit) // no else because we fall through during the start code scanning) + { + uint8_t data_byte; + while (AnnexB_GetByte(data, data_len, &data_byte)) + { + if (data_byte == 0) + { + demuxer->current_zero_words++; // might be the next start word + /* if (demuxer->current_zero_words == 3) // 00 00 00 is also a valid sequence for end-of-nal detection. + { + demuxer->state = UnitReady; + return AnnexB_UnitAvailable; + }*/ + } + else if (data_byte == 1 && demuxer->current_zero_words >= 2) + { + while (demuxer->current_zero_words > demuxer->number_of_zero_words) + { + // write trailing zero bytes to stream + if (demuxer->buffer_position >= demuxer->buffer_size) + return AnnexB_BufferFull; + demuxer->buffer[demuxer->buffer_position++] = 0; + demuxer->current_zero_words--; + } + demuxer->current_zero_words = 0; + demuxer->state = UnitReady; + return AnnexB_UnitAvailable; + } + else + { + while (demuxer->current_zero_words) + { + // write any zero bytes that we read to the stream + if (demuxer->buffer_position >= demuxer->buffer_size) + return AnnexB_BufferFull; + demuxer->buffer[demuxer->buffer_position++] = 0; + demuxer->current_zero_words--; + } + if (demuxer->buffer_position >= demuxer->buffer_size) + return AnnexB_BufferFull; + demuxer->buffer[demuxer->buffer_position++] = data_byte; + } + } + + if (demuxer->end_of_stream) + { + demuxer->state = UnitReady; + } + else + { + return AnnexB_NeedMoreData; + } + } + + if (demuxer->state == UnitReady) + return AnnexB_UnitAvailable; + + return AnnexB_NeedMoreData; // dunno how we'd get here + } + else + return AnnexB_Error; +} + +void AnnexB_EndOfStream(h264_annexb_demuxer_t d) +{ + AnnexBDemuxer *demuxer = (AnnexBDemuxer *)d; + if (demuxer) + demuxer->end_of_stream = 1; +} + +int AnnexB_GetUnit(h264_annexb_demuxer_t d, const void **data, size_t *data_len) +{ + AnnexBDemuxer *demuxer = (AnnexBDemuxer *)d; + if (demuxer) + { + if (demuxer->state == UnitReady) + { + *data = demuxer->buffer; + *data_len = demuxer->buffer_position; + demuxer->buffer_position = 0; + + // if we've found the next start code, go to MidUnit state + if (demuxer->current_zero_words == 0) + { + demuxer->state = MidUnit; + } + else // no start code, need to find it + { + demuxer->state = NewUnit; + } + return AnnexB_UnitAvailable; + } + else + { + return AnnexB_NeedMoreData; + } + } + + return AnnexB_Error; +} + +void AnnexB_Destroy(h264_annexb_demuxer_t d) +{ + AnnexBDemuxer *demuxer = (AnnexBDemuxer *)d; + if (demuxer) + free(demuxer); +}
\ No newline at end of file diff --git a/Src/h264/annexb.h b/Src/h264/annexb.h new file mode 100644 index 00000000..0adbd5e4 --- /dev/null +++ b/Src/h264/annexb.h @@ -0,0 +1,24 @@ +#pragma once +#include <bfc/platform/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + enum + { + AnnexB_UnitAvailable = 0, // data was added succesfully and a new unit is available via GetUnit(). + AnnexB_BufferFull = 1, // no start code found within the maximum unit length + AnnexB_NeedMoreData = 2, // no unit ready yet, pass in the next data chunk + AnnexB_Error = 3, // general error (out of memory, null pointer, etc) + }; + +typedef void *h264_annexb_demuxer_t; +h264_annexb_demuxer_t AnnexB_Create(int size); +void AnnexB_Destroy(h264_annexb_demuxer_t demuxer); +int AnnexB_AddData(h264_annexb_demuxer_t demuxer, const void **data, size_t *data_len); +void AnnexB_EndOfStream(h264_annexb_demuxer_t demuxer); +int AnnexB_GetUnit(h264_annexb_demuxer_t demuxer, const void **data, size_t *data_len); + +#ifdef __cplusplus +} +#endif
\ No newline at end of file diff --git a/Src/h264/api__h264.h b/Src/h264/api__h264.h new file mode 100644 index 00000000..aeda0b60 --- /dev/null +++ b/Src/h264/api__h264.h @@ -0,0 +1,13 @@ +#ifndef NULLSOFT_APIH +#define NULLSOFT_APIH + +#include <api/service/api_service.h> + +#include <api/memmgr/api_memmgr.h> +extern api_memmgr *memmgrApi; +#define WASABI_API_MEMMGR memmgrApi + +#include "../winamp/api_winamp.h" +extern api_winamp *winampApi; +#define AGAVE_API_WINAMP winampApi +#endif
\ No newline at end of file diff --git a/Src/h264/avi_h264_decoder.cpp b/Src/h264/avi_h264_decoder.cpp new file mode 100644 index 00000000..7411ead7 --- /dev/null +++ b/Src/h264/avi_h264_decoder.cpp @@ -0,0 +1,144 @@ +#include "avi_h264_decoder.h" +#include "../Winamp/wa_ipc.h" +#include <mmsystem.h> +#include <assert.h> +#include <Mferror.h> + + +int AVIDecoderCreator::CreateVideoDecoder(const nsavi::AVIH *avi_header, const nsavi::STRH *stream_header, const nsavi::STRF *stream_format, const nsavi::STRD *stream_data, ifc_avivideodecoder **decoder) +{ + nsavi::video_format *format = (nsavi::video_format *)stream_format; + if (format) + { + if (format->compression == '462H') + { + MFTDecoder *ctx = new MFTDecoder(); + if (!ctx) + return CREATEDECODER_FAILURE; + + if (FAILED(ctx->Open())) { + delete ctx; + return CREATEDECODER_FAILURE; + } + *decoder = new AVIH264(ctx, stream_header); + return CREATEDECODER_SUCCESS; + } + } + return CREATEDECODER_NOT_MINE; +} + + +#define CBCLASS AVIDecoderCreator +START_DISPATCH; +CB(CREATE_VIDEO_DECODER, CreateVideoDecoder) +END_DISPATCH; +#undef CBCLASS + +AVIH264::AVIH264(MFTDecoder *ctx, const nsavi::STRH *stream_header) : decoder(ctx), stream_header(stream_header) +{ +} + +AVIH264::~AVIH264() +{ + for ( size_t i = 0; i < buffered_frames.size(); i++ ) + { + nullsoft_h264_frame_data frame_data = buffered_frames[ i ]; + decoder->FreeFrame( (YV12_PLANES *)frame_data.data, frame_data.decoder_data ); + } + + delete decoder; +} + +int AVIH264::GetOutputProperties(int *x, int *y, int *color_format, double *aspect_ratio, int *flip) +{ + UINT width, height; + bool local_flip=false; + if (SUCCEEDED(decoder->GetOutputFormat(&width, &height, &local_flip, aspect_ratio))) { + *x = width; + *y = height; + *color_format = '21VY'; + *flip = local_flip; + return AVI_SUCCESS; + } + return AVI_FAILURE; +} + +int AVIH264::DecodeChunk(uint16_t type, const void *inputBuffer, size_t inputBufferBytes) +{ + for (;;) { + HRESULT hr = decoder->FeedRaw(inputBuffer, inputBufferBytes, 0); + if (hr == MF_E_NOTACCEPTING) { + nullsoft_h264_frame_data frame_data; + if (FAILED(decoder->GetFrame((YV12_PLANES **)&frame_data.data, &frame_data.decoder_data, &frame_data.local_timestamp))) { + continue; + } + buffered_frames.push_back(frame_data); + } else if (FAILED(hr)) { + return AVI_FAILURE; + } else { + break; + } + } + return AVI_SUCCESS; +} + +void AVIH264::Flush() +{ + for (size_t i=0;i<buffered_frames.size();i++) { + nullsoft_h264_frame_data frame_data = buffered_frames[i]; + decoder->FreeFrame((YV12_PLANES *)frame_data.data, frame_data.decoder_data); + } + decoder->Flush(); +} + +int AVIH264::GetPicture(void **data, void **decoder_data) +{ + if (!buffered_frames.empty()) { + nullsoft_h264_frame_data frame_data = buffered_frames[0]; + buffered_frames.erase(buffered_frames.begin()); + *data = frame_data.data; + *decoder_data = frame_data.decoder_data; + return AVI_SUCCESS; + } + + if (SUCCEEDED(decoder->GetFrame((YV12_PLANES **)data, decoder_data, 0))) { + return AVI_SUCCESS; + } else { + return AVI_FAILURE; + } +} + +void AVIH264::FreePicture(void *data, void *decoder_data) +{ + decoder->FreeFrame((YV12_PLANES *)data, decoder_data); +} + +void AVIH264::EndOfStream() +{ + decoder->Drain(); +} + +void AVIH264::HurryUp(int state) +{ + // TODO(benski) + //if (decoder) +// H264_HurryUp(decoder, state); +} + +void AVIH264::Close() +{ + delete this; +} + +#define CBCLASS AVIH264 +START_DISPATCH; +CB(GET_OUTPUT_PROPERTIES, GetOutputProperties) +CB(DECODE_CHUNK, DecodeChunk) +VCB(FLUSH, Flush) +VCB(CLOSE, Close) +CB(GET_PICTURE, GetPicture) +VCB(FREE_PICTURE, FreePicture) +VCB(END_OF_STREAM, EndOfStream) +VCB(HURRY_UP, HurryUp) +END_DISPATCH; +#undef CBCLASS diff --git a/Src/h264/avi_h264_decoder.h b/Src/h264/avi_h264_decoder.h new file mode 100644 index 00000000..81a55f76 --- /dev/null +++ b/Src/h264/avi_h264_decoder.h @@ -0,0 +1,42 @@ +#pragma once +#include "../Plugins/Input/in_avi/ifc_avivideodecoder.h" +#include "../Plugins/Input/in_avi/svc_avidecoder.h" +#include "MFTDecoder.h" +#include <vector> + +// {AFA1BB51-F41B-4522-9251-25A8DF923DBE} +static const GUID avi_h264_guid = +{ 0xafa1bb51, 0xf41b, 0x4522, { 0x92, 0x51, 0x25, 0xa8, 0xdf, 0x92, 0x3d, 0xbe } }; + + +class AVIDecoderCreator : public svc_avidecoder +{ +public: + static const char *getServiceName() { return "H.264 AVI Decoder"; } + static GUID getServiceGuid() { return avi_h264_guid; } + int CreateVideoDecoder(const nsavi::AVIH *avi_header, const nsavi::STRH *stream_header, const nsavi::STRF *stream_format, const nsavi::STRD *stream_data, ifc_avivideodecoder **decoder); +protected: + RECVS_DISPATCH; +}; + +class AVIH264 : public ifc_avivideodecoder +{ +public: + AVIH264(MFTDecoder *ctx, const nsavi::STRH *stream_header); + ~AVIH264(); + + int GetOutputProperties(int *x, int *y, int *color_format, double *aspect_ratio, int *flip); + int DecodeChunk(uint16_t type, const void *inputBuffer, size_t inputBufferBytes); + void Flush(); + void Close(); + int GetPicture(void **data, void **decoder_data); + void FreePicture(void *data, void *decoder_data); + void EndOfStream(); + void HurryUp(int state); +private: + MFTDecoder *decoder; + const nsavi::STRH *stream_header; + std::vector<nullsoft_h264_frame_data> buffered_frames; +protected: + RECVS_DISPATCH; +};
\ No newline at end of file diff --git a/Src/h264/h264.rc b/Src/h264/h264.rc new file mode 100644 index 00000000..fcff7711 --- /dev/null +++ b/Src/h264/h264.rc @@ -0,0 +1,76 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// English (U.K.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "#include ""version.rc2""\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // English (U.K.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#include "version.rc2" + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Src/h264/h264.sln b/Src/h264/h264.sln new file mode 100644 index 00000000..59ac582c --- /dev/null +++ b/Src/h264/h264.sln @@ -0,0 +1,41 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29613.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "h264", "h264.vcxproj", "{CB4658FB-5E5B-4B1D-8341-6E31E41B49E4}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmp4v2", "..\libmp4v2\libmp4v2.vcxproj", "{EFB9B882-6A8B-463D-A8E3-A2807AFC5D9F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CB4658FB-5E5B-4B1D-8341-6E31E41B49E4}.Debug|Win32.ActiveCfg = Debug|Win32 + {CB4658FB-5E5B-4B1D-8341-6E31E41B49E4}.Debug|Win32.Build.0 = Debug|Win32 + {CB4658FB-5E5B-4B1D-8341-6E31E41B49E4}.Debug|x64.ActiveCfg = Debug|x64 + {CB4658FB-5E5B-4B1D-8341-6E31E41B49E4}.Debug|x64.Build.0 = Debug|x64 + {CB4658FB-5E5B-4B1D-8341-6E31E41B49E4}.Release|Win32.ActiveCfg = Release|Win32 + {CB4658FB-5E5B-4B1D-8341-6E31E41B49E4}.Release|Win32.Build.0 = Release|Win32 + {CB4658FB-5E5B-4B1D-8341-6E31E41B49E4}.Release|x64.ActiveCfg = Release|x64 + {CB4658FB-5E5B-4B1D-8341-6E31E41B49E4}.Release|x64.Build.0 = Release|x64 + {EFB9B882-6A8B-463D-A8E3-A2807AFC5D9F}.Debug|Win32.ActiveCfg = Debug|Win32 + {EFB9B882-6A8B-463D-A8E3-A2807AFC5D9F}.Debug|Win32.Build.0 = Debug|Win32 + {EFB9B882-6A8B-463D-A8E3-A2807AFC5D9F}.Debug|x64.ActiveCfg = Debug|x64 + {EFB9B882-6A8B-463D-A8E3-A2807AFC5D9F}.Debug|x64.Build.0 = Debug|x64 + {EFB9B882-6A8B-463D-A8E3-A2807AFC5D9F}.Release|Win32.ActiveCfg = Release|Win32 + {EFB9B882-6A8B-463D-A8E3-A2807AFC5D9F}.Release|Win32.Build.0 = Release|Win32 + {EFB9B882-6A8B-463D-A8E3-A2807AFC5D9F}.Release|x64.ActiveCfg = Release|x64 + {EFB9B882-6A8B-463D-A8E3-A2807AFC5D9F}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {65BB345C-17C8-42EF-856A-51A9F75EA0E7} + EndGlobalSection +EndGlobal diff --git a/Src/h264/h264.vcxproj b/Src/h264/h264.vcxproj new file mode 100644 index 00000000..8aa88689 --- /dev/null +++ b/Src/h264/h264.vcxproj @@ -0,0 +1,271 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{CB4658FB-5E5B-4B1D-8341-6E31E41B49E4}</ProjectGuid> + <RootNamespace>h264</RootNamespace> + <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <TargetExt>.w5s</TargetExt> + <IncludePath>$(IncludePath)</IncludePath> + <LibraryPath>$(LibraryPath)</LibraryPath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <TargetExt>.w5s</TargetExt> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <TargetExt>.w5s</TargetExt> + <IncludePath>$(IncludePath)</IncludePath> + <LibraryPath>$(LibraryPath)</LibraryPath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <TargetExt>.w5s</TargetExt> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <VcpkgConfiguration>Debug</VcpkgConfiguration> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + <VcpkgConfiguration>Debug</VcpkgConfiguration> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <AdditionalIncludeDirectories>$(ProjectDir)..\external_dependencies\intel_ipp_6.1.1.035\ia32\include;../Wasabi;../libmp4v2;../libmp4v2/include;../h264dec/lcommon/inc;../h264dec/ldecod/inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;H264_EXPORTS;_WIN32_WINNT=0x601;H264_IPP;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MinimalRebuild>false</MinimalRebuild> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <AdditionalDependencies>ws2_32.lib;Mfplat.lib;wmcodecdspuuid.lib;mfuuid.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <AdditionalLibraryDirectories>..\intel_ipp_6.1.1.035\ia32\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <GenerateDebugInformation>true</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <TargetMachine>MachineX86</TargetMachine> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\'</Message> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <AdditionalIncludeDirectories>$(ProjectDir)..\external_dependencies\intel_ipp_6.1.1.035\ia32\include;../Wasabi;../libmp4v2;../libmp4v2/include;../h264dec/lcommon/inc;../h264dec/ldecod/inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN64;_DEBUG;_WINDOWS;_USRDLL;H264_EXPORTS;_WIN32_WINNT=0x601;H264_IPP;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MinimalRebuild>false</MinimalRebuild> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <AdditionalDependencies>ws2_32.lib;Mfplat.lib;wmcodecdspuuid.lib;mfuuid.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <AdditionalLibraryDirectories>..\intel_ipp_6.1.1.035\em64t\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <GenerateDebugInformation>true</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\'</Message> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <Optimization>MaxSpeed</Optimization> + <IntrinsicFunctions>true</IntrinsicFunctions> + <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed> + <OmitFramePointers>true</OmitFramePointers> + <AdditionalIncludeDirectories>$(ProjectDir)..\external_dependencies\intel_ipp_6.1.1.035\ia32\include;../Wasabi;../libmp4v2;../libmp4v2/include;../h264dec/lcommon/inc;../h264dec/ldecod/inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;H264_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <StringPooling>true</StringPooling> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <BufferSecurityCheck>true</BufferSecurityCheck> + <FunctionLevelLinking>true</FunctionLevelLinking> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>None</DebugInformationFormat> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <AdditionalDependencies>ws2_32.lib;Mfplat.lib;wmcodecdspuuid.lib;mfuuid.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <AdditionalLibraryDirectories>..\intel_ipp_6.1.1.035\ia32\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <DelayLoadDLLs>ws2_32.dll;Mfplat.dll;ole32.dll;%(DelayLoadDLLs)</DelayLoadDLLs> + <GenerateDebugInformation>false</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <OptimizeReferences>true</OptimizeReferences> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <ImportLibrary>$(ProjectDir)x86_Release\$(ProjectName).lib</ImportLibrary> + <TargetMachine>MachineX86</TargetMachine> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\'</Message> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <Optimization>MaxSpeed</Optimization> + <IntrinsicFunctions>true</IntrinsicFunctions> + <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed> + <OmitFramePointers>true</OmitFramePointers> + <AdditionalIncludeDirectories>$(ProjectDir)..\external_dependencies\intel_ipp_6.1.1.035\ia32\include;../Wasabi;../libmp4v2;../libmp4v2/include;../h264dec/lcommon/inc;../h264dec/ldecod/inc</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN64;NDEBUG;_WINDOWS;_USRDLL;H264_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <StringPooling>true</StringPooling> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <BufferSecurityCheck>true</BufferSecurityCheck> + <FunctionLevelLinking>true</FunctionLevelLinking> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>None</DebugInformationFormat> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <AdditionalDependencies>ws2_32.lib;Mfplat.lib;wmcodecdspuuid.lib;mfuuid.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <AdditionalLibraryDirectories>..\intel_ipp_6.1.1.035\em64t\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <DelayLoadDLLs>ws2_32.dll;Mfplat.dll;ole32.dll;%(DelayLoadDLLs)</DelayLoadDLLs> + <GenerateDebugInformation>false</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <OptimizeReferences>true</OptimizeReferences> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <ImportLibrary>$(ProjectDir)x64_Release\$(ProjectName).lib</ImportLibrary> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\System\'</Message> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ProjectReference Include="..\external_dependencies\libmp4v2\libmp4v2.vcxproj"> + <Project>{efb9b882-6a8b-463d-a8e3-a2807afc5d9f}</Project> + </ProjectReference> + <ProjectReference Include="..\Wasabi\Wasabi.vcxproj"> + <Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project> + </ProjectReference> + </ItemGroup> + <ItemGroup> + <ClCompile Include="annexb.c" /> + <ClCompile Include="avi_h264_decoder.cpp" /> + <ClCompile Include="h264_flv_decoder.cpp" /> + <ClCompile Include="h264_mkv_decoder.cpp" /> + <ClCompile Include="h264_mp4_decoder.cpp" /> + <ClCompile Include="main.cpp" /> + <ClCompile Include="MFTDecoder.cpp" /> + <ClCompile Include="NSVFactory.cpp" /> + <ClCompile Include="nsv_h264_decoder.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\in_mp4\mpeg4video.h" /> + <ClInclude Include="annexb.h" /> + <ClInclude Include="api__h264.h" /> + <ClInclude Include="avi_h264_decoder.h" /> + <ClInclude Include="h264_flv_decoder.h" /> + <ClInclude Include="h264_mkv_decoder.h" /> + <ClInclude Include="h264_mp4_decoder.h" /> + <ClInclude Include="MFTDecoder.h" /> + <ClInclude Include="NSVFactory.h" /> + <ClInclude Include="nsv_h264_decoder.h" /> + <ClInclude Include="resource.h" /> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="h264.rc" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/Src/h264/h264.vcxproj.filters b/Src/h264/h264.vcxproj.filters new file mode 100644 index 00000000..2b5acfae --- /dev/null +++ b/Src/h264/h264.vcxproj.filters @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <ClCompile Include="annexb.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="avi_h264_decoder.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="h264_flv_decoder.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="h264_mkv_decoder.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="h264_mp4_decoder.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="MFTDecoder.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="nsv_h264_decoder.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="NSVFactory.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="annexb.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="api__h264.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="avi_h264_decoder.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="h264_flv_decoder.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="h264_mkv_decoder.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="h264_mp4_decoder.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="MFTDecoder.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\in_mp4\mpeg4video.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="nsv_h264_decoder.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="NSVFactory.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="resource.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <Filter Include="Header Files"> + <UniqueIdentifier>{2b9031b7-6c12-48a4-b823-a82fe9fbd180}</UniqueIdentifier> + </Filter> + <Filter Include="Ressource Files"> + <UniqueIdentifier>{56f52a2e-633e-436b-aec7-d2db3331ad81}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files"> + <UniqueIdentifier>{6021c9a2-1324-4073-a278-8c8d47db3ae9}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="h264.rc"> + <Filter>Ressource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/Src/h264/h264_flv_decoder.cpp b/Src/h264/h264_flv_decoder.cpp new file mode 100644 index 00000000..f9a6f6c7 --- /dev/null +++ b/Src/h264/h264_flv_decoder.cpp @@ -0,0 +1,220 @@ +#include "h264_flv_decoder.h" +#include "../Winamp/wa_ipc.h" // for YV12_PLANES +#include <Mferror.h> + +int FLVDecoderCreator::CreateVideoDecoder(int format_type, int width, int height, ifc_flvvideodecoder **decoder) +{ + if (format_type == FLV::VIDEO_FORMAT_AVC) + { + MFTDecoder *ctx = new MFTDecoder(); + if (!ctx || FAILED(ctx->Open())) { + delete ctx; + return CREATEDECODER_FAILURE; + } + *decoder = new FLVH264(ctx); + return CREATEDECODER_SUCCESS; + } + return CREATEDECODER_NOT_MINE; +} + +int FLVDecoderCreator::HandlesVideo(int format_type) +{ + if (format_type == FLV::VIDEO_FORMAT_AVC) + { + return CREATEDECODER_SUCCESS; + } + return CREATEDECODER_NOT_MINE; +} + +#define CBCLASS FLVDecoderCreator +START_DISPATCH; +CB(CREATE_VIDEO_DECODER, CreateVideoDecoder) +CB(HANDLES_VIDEO, HandlesVideo) +END_DISPATCH; +#undef CBCLASS + +/* --- */ +uint32_t GetNALUSize(uint64_t nalu_size_bytes, const uint8_t *h264_data, size_t data_len); +uint32_t Read24(const uint8_t *data); + +FLVH264::FLVH264(MFTDecoder *decoder) : decoder(decoder) +{ + sequence_headers_parsed=0; + nalu_size_bytes=0; +} + +FLVH264::~FLVH264() +{ + for (size_t i=0;i<buffered_frames.size();i++) { + nullsoft_h264_frame_data frame_data = buffered_frames[i]; + decoder->FreeFrame((YV12_PLANES *)frame_data.data, frame_data.decoder_data); + } + delete decoder; +} + +int FLVH264::GetOutputFormat(int *x, int *y, int *color_format) +{ + UINT width, height; + bool local_flip=false; + double aspect_ratio; + if (SUCCEEDED(decoder->GetOutputFormat(&width, &height, &local_flip, &aspect_ratio))) { + *x = width; + *y = height; + *color_format = '21VY'; + return FLV_VIDEO_SUCCESS; + } + return FLV_VIDEO_FAILURE; +} + +int FLVH264::DecodeSample(const void *inputBuffer, size_t inputBufferBytes, int32_t timestamp) +{ + const uint8_t *h264_data = (const uint8_t *)inputBuffer; + if (*h264_data == 0 && inputBufferBytes >= 10) // sequence headers + { + h264_data++; // skip packet type + uint32_t timestamp_offset = Read24(h264_data); + h264_data+=3; + inputBufferBytes -=4; + h264_data+=4; // don't care about level & profile + inputBufferBytes -=4; + nalu_size_bytes = (*h264_data++ & 0x3)+1; + inputBufferBytes--; + size_t num_sps = *h264_data++ & 0x1F; + inputBufferBytes--; + for (size_t i=0;i!=num_sps;i++) + { + if (inputBufferBytes > 2) + { + uint16_t sps_size = (h264_data[0] << 8) | h264_data[1]; + h264_data+=2; + inputBufferBytes-=2; + //H264_ProcessSPS(decoder, h264_data+1, sps_size); + if (inputBufferBytes >= sps_size) + { + decoder->Feed(h264_data, sps_size, timestamp+timestamp_offset); + h264_data+=sps_size; + inputBufferBytes-=sps_size; + } + } + } + if (inputBufferBytes) + { + size_t num_pps = *h264_data++; + inputBufferBytes--; + for (size_t i=0;i!=num_pps;i++) + { + if (inputBufferBytes > 2) + { + uint16_t sps_size = (h264_data[0] << 8) | h264_data[1]; + h264_data+=2; + inputBufferBytes-=2; + //H264_ProcessPPS(decoder, h264_data+1, sps_size); + if (inputBufferBytes >= sps_size) + { + decoder->Feed(h264_data, sps_size, timestamp+timestamp_offset); + h264_data+=sps_size; + inputBufferBytes-=sps_size; + } + } + } + } + sequence_headers_parsed=1; + } + else if (*h264_data == 1) // frame + { + h264_data++; + inputBufferBytes--; + if (inputBufferBytes < 3) + return FLV_VIDEO_FAILURE; + uint32_t timestamp_offset = Read24(h264_data); + + h264_data+=3; + inputBufferBytes-=3; + + while (inputBufferBytes) + { + uint32_t this_size =GetNALUSize(nalu_size_bytes, h264_data, inputBufferBytes); + if (this_size == 0) + return FLV_VIDEO_FAILURE; + + inputBufferBytes-=nalu_size_bytes; + h264_data+=nalu_size_bytes; + if (this_size > inputBufferBytes) + return FLV_VIDEO_FAILURE; + for (;;) { + HRESULT hr = decoder->Feed(h264_data, this_size, timestamp+timestamp_offset); + if (hr == MF_E_NOTACCEPTING) { + nullsoft_h264_frame_data frame_data; + if (FAILED(decoder->GetFrame((YV12_PLANES **)&frame_data.data, &frame_data.decoder_data, &frame_data.local_timestamp))) { + continue; + } + buffered_frames.push_back(frame_data); + } else if (FAILED(hr)) { + return FLV_VIDEO_FAILURE; + } else { + break; + } + } + + inputBufferBytes-=this_size; + h264_data+=this_size; + } + } + + return FLV_VIDEO_SUCCESS; +} + +void FLVH264::Flush() +{ + for (size_t i=0;i<buffered_frames.size();i++) { + nullsoft_h264_frame_data frame_data = buffered_frames[i]; + decoder->FreeFrame((YV12_PLANES *)frame_data.data, frame_data.decoder_data); + } + decoder->Flush(); +} + +void FLVH264::Close() +{ + delete this; +} + +int FLVH264::GetPicture(void **data, void **decoder_data, uint64_t *timestamp) +{ + if (!buffered_frames.empty()) { + nullsoft_h264_frame_data frame_data = buffered_frames[0]; + buffered_frames.erase(buffered_frames.begin()); + *data = frame_data.data; + *decoder_data = frame_data.decoder_data; + *timestamp = frame_data.local_timestamp; + return FLV_VIDEO_SUCCESS; + } + + if (SUCCEEDED(decoder->GetFrame((YV12_PLANES **)data, decoder_data, timestamp))) { + return FLV_VIDEO_SUCCESS; + } else { + return FLV_VIDEO_FAILURE; + } +} + +void FLVH264::FreePicture(void *data, void *decoder_data) +{ + decoder->FreeFrame((YV12_PLANES *)data, decoder_data); +} + +int FLVH264::Ready() +{ + return sequence_headers_parsed; +} + +#define CBCLASS FLVH264 +START_DISPATCH; +CB(FLV_VIDEO_GETOUTPUTFORMAT, GetOutputFormat) +CB(FLV_VIDEO_DECODE, DecodeSample) +VCB(FLV_VIDEO_FLUSH, Flush) +VCB(FLV_VIDEO_CLOSE, Close) +CB(FLV_VIDEO_GET_PICTURE, GetPicture) +VCB(FLV_VIDEO_FREE_PICTURE, FreePicture) +CB(FLV_VIDEO_READY, Ready) +END_DISPATCH; +#undef CBCLASS + diff --git a/Src/h264/h264_flv_decoder.h b/Src/h264/h264_flv_decoder.h new file mode 100644 index 00000000..a48d8ceb --- /dev/null +++ b/Src/h264/h264_flv_decoder.h @@ -0,0 +1,42 @@ +#pragma once +#include "../Plugins/Input/in_flv/svc_flvdecoder.h" +#include "../Plugins/Input/in_flv/FLVVideoHeader.h" +#include "../Plugins/Input/in_flv/ifc_flvvideodecoder.h" +#include "MFTDecoder.h" +#include <vector> + +// {7BBC5D47-7E96-4e27-85DB-FF4190428CD0} +static const GUID flv_h264_guid = +{ 0x7bbc5d47, 0x7e96, 0x4e27, { 0x85, 0xdb, 0xff, 0x41, 0x90, 0x42, 0x8c, 0xd0 } }; + +class FLVDecoderCreator : public svc_flvdecoder +{ +public: + static const char *getServiceName() { return "H.264 FLV Decoder"; } + static GUID getServiceGuid() { return flv_h264_guid; } + int CreateVideoDecoder(int format_type, int width, int height, ifc_flvvideodecoder **decoder); + int HandlesVideo(int format_type); +protected: + RECVS_DISPATCH; +}; + +class FLVH264 : public ifc_flvvideodecoder +{ +public: + FLVH264(MFTDecoder *decoder); + ~FLVH264(); + int GetOutputFormat(int *x, int *y, int *color_format); + int DecodeSample(const void *inputBuffer, size_t inputBufferBytes, int32_t timestamp); + void Flush(); + void Close(); + int GetPicture(void **data, void **decoder_data, uint64_t *timestamp); + void FreePicture(void *data, void *decoder_data); + int Ready(); +private: + MFTDecoder *decoder; + int sequence_headers_parsed; + uint32_t nalu_size_bytes; + std::vector<nullsoft_h264_frame_data> buffered_frames; +protected: + RECVS_DISPATCH; +};
\ No newline at end of file diff --git a/Src/h264/h264_mkv_decoder.cpp b/Src/h264/h264_mkv_decoder.cpp new file mode 100644 index 00000000..2628bc1e --- /dev/null +++ b/Src/h264/h264_mkv_decoder.cpp @@ -0,0 +1,237 @@ +#include "h264_mkv_decoder.h" +#include "../Winamp/wa_ipc.h" // for YV12_PLANES +#include <winsock.h> +#include <mmsystem.h> +#include <Mferror.h> + +int MKVDecoderCreator::CreateVideoDecoder(const char *codec_id, const nsmkv::TrackEntryData *track_entry_data, const nsmkv::VideoData *video_data, ifc_mkvvideodecoder **decoder) +{ + if (!strcmp(codec_id, "V_MPEG4/ISO/AVC")) + { + const uint8_t *init_data = (const uint8_t *)track_entry_data->codec_private; + size_t init_data_len = track_entry_data->codec_private_len; + if (init_data && init_data_len >= 6) + { + MFTDecoder *ctx = new MFTDecoder; + if (!ctx) + return CREATEDECODER_FAILURE; + + if (FAILED(ctx->Open())) { + delete ctx; + return CREATEDECODER_FAILURE; + } + + + init_data+=4; // don't care about level & profile + init_data_len-=4; + + // read NALU header size length + uint8_t nalu_minus_one = *init_data++ & 0x3; + init_data_len--; + + // number of SPS NAL units + uint8_t num_sps = *init_data++ & 0x1F; + init_data_len--; + for (uint8_t i=0;i!=num_sps;i++) + { + if (init_data_len < 2) + { + delete ctx; + return CREATEDECODER_FAILURE; + } + uint16_t *s = (uint16_t *)init_data; + uint16_t sps_size = htons(*s); + init_data+=2; + init_data_len-=2; + if (init_data_len < sps_size) + { + delete ctx; + return CREATEDECODER_FAILURE; + } + ctx->Feed(init_data, sps_size, 0); + init_data+=sps_size; + init_data_len-=sps_size; + } + + // read PPS NAL units + if (init_data_len) + { + // number of PPS NAL units + uint8_t num_pps = *init_data++ & 0x1F; + init_data_len--; + for (uint8_t i=0;i!=num_pps;i++) + { + if (init_data_len < 2) + { + delete ctx; + return CREATEDECODER_FAILURE; + } + uint16_t *s = (uint16_t *)init_data; + uint16_t pps_size = htons(*s); + init_data+=2; + init_data_len-=2; + if (init_data_len < pps_size) + { + delete ctx; + return CREATEDECODER_FAILURE; + } + ctx->Feed(init_data, pps_size, 0); + init_data+=pps_size; + init_data_len-=pps_size; + } + } + // if we made it here, we should be good + *decoder = new MKVH264(ctx, nalu_minus_one, video_data); + return CREATEDECODER_SUCCESS; + } + else + { + return CREATEDECODER_FAILURE; + } + } + else + { + return CREATEDECODER_NOT_MINE; + } +} + + +#define CBCLASS MKVDecoderCreator +START_DISPATCH; +CB(CREATE_VIDEO_DECODER, CreateVideoDecoder) +END_DISPATCH; +#undef CBCLASS + +MKVH264::MKVH264(MFTDecoder *ctx, uint8_t nalu_minus_one, const nsmkv::VideoData *video_data) : decoder(ctx), video_data(video_data) +{ + nalu_size = nalu_minus_one + 1; + width=0; + height=0; +} + +MKVH264::~MKVH264() +{ + for (size_t i=0;i<buffered_frames.size();i++) { + nullsoft_h264_frame_data frame_data = buffered_frames[i]; + decoder->FreeFrame((YV12_PLANES *)frame_data.data, frame_data.decoder_data); + } + delete decoder; +} + +int MKVH264::GetOutputProperties(int *x, int *y, int *color_format, double *aspect_ratio) +{ + + if (decoder) { + bool flip; + if (SUCCEEDED(decoder->GetOutputFormat(&width, &height, &flip, aspect_ratio))) { + *x = width; + *y = height; + *color_format = htonl('YV12'); + return MKV_SUCCESS; + } + } + return MKV_FAILURE; +} + +uint32_t GetNALUSize(uint64_t nalu_size_bytes, const uint8_t *h264_data, size_t data_len); + +int MKVH264::DecodeBlock(const void *inputBuffer, size_t inputBufferBytes, uint64_t timestamp) +{ + const uint8_t *h264_data = (const uint8_t *)inputBuffer; + while (inputBufferBytes) { + uint32_t this_size = GetNALUSize(nalu_size, h264_data, inputBufferBytes); + if (this_size == 0) + return MKV_FAILURE; + + inputBufferBytes-=nalu_size; + h264_data+=nalu_size; + if (this_size > inputBufferBytes) + return MKV_FAILURE; + + for (;;) { + HRESULT hr = decoder->Feed(h264_data, this_size, timestamp); + if (hr == MF_E_NOTACCEPTING) { + nullsoft_h264_frame_data frame_data; + if (FAILED(decoder->GetFrame((YV12_PLANES **)&frame_data.data, &frame_data.decoder_data, &frame_data.local_timestamp))) { + continue; + } + buffered_frames.push_back(frame_data); + } else if (FAILED(hr)) { + return MKV_FAILURE; + } else { + break; + } + } + + inputBufferBytes-=this_size; + h264_data+=this_size; + } + return MKV_SUCCESS; +} + +void MKVH264::Flush() +{ + for (size_t i=0;i<buffered_frames.size();i++) { + nullsoft_h264_frame_data frame_data = buffered_frames[i]; + decoder->FreeFrame((YV12_PLANES *)frame_data.data, frame_data.decoder_data); + } + if (decoder) { + decoder->Flush(); + } +} + +int MKVH264::GetPicture(void **data, void **decoder_data, uint64_t *timestamp) +{ + if (!buffered_frames.empty()) { + nullsoft_h264_frame_data frame_data = buffered_frames[0]; + buffered_frames.erase(buffered_frames.begin()); + *data = frame_data.data; + *decoder_data = frame_data.decoder_data; + *timestamp = frame_data.local_timestamp; + return MKV_SUCCESS; + } + + if (SUCCEEDED(decoder->GetFrame((YV12_PLANES **)data, decoder_data, timestamp))) { + return MKV_SUCCESS; + } else { + return MKV_FAILURE; + } +} + +void MKVH264::FreePicture(void *data, void *decoder_data) +{ + decoder->FreeFrame((YV12_PLANES *)data, decoder_data); +} + +void MKVH264::EndOfStream() +{ + if (decoder) { + decoder->Drain(); + } +} + +void MKVH264::HurryUp(int state) +{ + // TODO(benski) + //if (decoder) + // H264_HurryUp(decoder, state); +} + +void MKVH264::Close() +{ + delete this; +} + +#define CBCLASS MKVH264 +START_DISPATCH; +CB(GET_OUTPUT_PROPERTIES, GetOutputProperties) +CB(DECODE_BLOCK, DecodeBlock) +VCB(FLUSH, Flush) +VCB(CLOSE, Close) +CB(GET_PICTURE, GetPicture) +VCB(FREE_PICTURE, FreePicture) +VCB(END_OF_STREAM, EndOfStream) +VCB(HURRY_UP, HurryUp) +END_DISPATCH; +#undef CBCLASS + diff --git a/Src/h264/h264_mkv_decoder.h b/Src/h264/h264_mkv_decoder.h new file mode 100644 index 00000000..9228981a --- /dev/null +++ b/Src/h264/h264_mkv_decoder.h @@ -0,0 +1,44 @@ +#pragma once +#include "../Plugins/Input/in_mkv/ifc_mkvvideodecoder.h" +#include "../Plugins/Input/in_mkv/svc_mkvdecoder.h" +#include "MFTDecoder.h" +#include <vector> + +// {8CC583E7-46DC-4736-A7CA-1159A70ADCAE} +static const GUID mkv_h264_guid = +{ 0x8cc583e7, 0x46dc, 0x4736, { 0xa7, 0xca, 0x11, 0x59, 0xa7, 0xa, 0xdc, 0xae } }; + +class MKVDecoderCreator : public svc_mkvdecoder +{ +public: + static const char *getServiceName() { return "H.264 MKV Decoder"; } + static GUID getServiceGuid() { return mkv_h264_guid; } + int CreateVideoDecoder(const char *codec_id, const nsmkv::TrackEntryData *track_entry_data, const nsmkv::VideoData *video_data, ifc_mkvvideodecoder **decoder); +protected: + RECVS_DISPATCH; +}; + + +class MKVH264 : public ifc_mkvvideodecoder +{ +public: + MKVH264(MFTDecoder *ctx, uint8_t nalu_size_minus_one, const nsmkv::VideoData *video_data); + ~MKVH264(); + + int GetOutputProperties(int *x, int *y, int *color_format, double *aspect_ratio); + int DecodeBlock(const void *inputBuffer, size_t inputBufferBytes, uint64_t timestamp); + void Flush(); + void Close(); + int GetPicture(void **data, void **decoder_data, uint64_t *timestamp); + void FreePicture(void *data, void *decoder_data); + void EndOfStream(); + void HurryUp(int state); +private: + MFTDecoder *decoder; + UINT width, height; + uint8_t nalu_size; + const nsmkv::VideoData *video_data; + std::vector<nullsoft_h264_frame_data> buffered_frames; +protected: + RECVS_DISPATCH; +};
\ No newline at end of file diff --git a/Src/h264/h264_mp4_decoder.cpp b/Src/h264/h264_mp4_decoder.cpp new file mode 100644 index 00000000..afb48bb3 --- /dev/null +++ b/Src/h264/h264_mp4_decoder.cpp @@ -0,0 +1,212 @@ +#include "h264_mp4_decoder.h" +#include "../winamp/wa_ipc.h" +#include <Mferror.h> +uint32_t Read24(const uint8_t *data) +{ + // ugh, 24bit size + uint32_t this_size=0; + uint8_t *this_size_p = (uint8_t *)&this_size; + this_size_p[0] = data[2]; + this_size_p[1] = data[1]; + this_size_p[2] = data[0]; + return this_size; +} + +uint32_t GetNALUSize(uint64_t nalu_size_bytes, const uint8_t *h264_data, size_t data_len) +{ + if ((data_len) < (nalu_size_bytes)) + return 0; + + switch(nalu_size_bytes) + { + case 1: + return *h264_data; + case 2: + { + return (h264_data[0] << 8) | h264_data[1]; + + } + case 3: + { + return Read24(h264_data); + } + case 4: + { + uint32_t this_size = *(uint32_t *)h264_data; + this_size = htonl(this_size); + return this_size; + } + } + return 0; +} + +H264MP4Decoder::H264MP4Decoder() +{ + nalu_size_bytes=0; + width=0; + height=0; +} + +H264MP4Decoder::~H264MP4Decoder() +{ + for (size_t i=0;i<buffered_frames.size();i++) { + nullsoft_h264_frame_data frame_data = buffered_frames[i]; + decoder.FreeFrame((YV12_PLANES *)frame_data.data, frame_data.decoder_data); + } +} + +int H264MP4Decoder::Open(MP4FileHandle mp4_file, MP4TrackId mp4_track) +{ + this->mp4_file=mp4_file; + this->mp4_track=mp4_track; + decoder.Open(); + // TODO error checking + uint8_t **seqHeaders = 0, **pictHeaders = 0; + uint32_t *seqHeadersSize = 0, *pictHeadersSize = 0; + + __try + { + MP4GetTrackH264SeqPictHeaders(mp4_file, mp4_track, + &seqHeaders, &seqHeadersSize, + &pictHeaders, &pictHeadersSize); + + if (seqHeadersSize) + { + for (uint32_t i = 0; seqHeadersSize[i] != 0; i++) + { + decoder.Feed(seqHeaders[i], seqHeadersSize[i], 0); + MP4Free(seqHeaders[i]); + } + } + MP4Free(seqHeadersSize); + + if (pictHeadersSize) + { + for (uint32_t i = 0; pictHeadersSize[i] != 0; i++) + { + decoder.Feed(pictHeaders[i], pictHeadersSize[i], 0); + MP4Free(pictHeaders[i]); + } + } + MP4Free(pictHeadersSize); + + MP4GetTrackH264LengthSize(mp4_file, mp4_track, &nalu_size_bytes); + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + return MP4_VIDEO_FAILURE; + } + + return MP4_VIDEO_SUCCESS; +} + +int H264MP4Decoder::GetOutputFormat(int *x, int *y, int *color_format, double *aspect_ratio) +{ + bool flip; + + if (SUCCEEDED(decoder.GetOutputFormat(&width, &height, &flip, aspect_ratio))) { + *x = width; + *y = height; + *color_format = htonl('YV12'); + return MP4_VIDEO_SUCCESS; + } + return MP4_VIDEO_FAILURE; +} + + +int H264MP4Decoder::DecodeSample(const void *inputBuffer, size_t inputBufferBytes, MP4Timestamp timestamp) +{ + const uint8_t *h264_data = (const uint8_t *)inputBuffer; + while (inputBufferBytes) + { + uint32_t this_size =GetNALUSize(nalu_size_bytes, h264_data, inputBufferBytes); + if (this_size == 0) + return MP4_VIDEO_FAILURE; + + inputBufferBytes-=nalu_size_bytes; + h264_data+=nalu_size_bytes; + if (this_size > inputBufferBytes) + return MP4_VIDEO_FAILURE; + + for (;;) { + uint64_t hundrednanos = MP4ConvertFromTrackTimestamp(mp4_file, mp4_track, timestamp, MP4_NANOSECONDS_TIME_SCALE/100ULL); + HRESULT hr = decoder.Feed(h264_data, this_size, hundrednanos); + if (hr == MF_E_NOTACCEPTING) { + nullsoft_h264_frame_data frame_data; + if (FAILED(decoder.GetFrame((YV12_PLANES **)&frame_data.data, &frame_data.decoder_data, &frame_data.local_timestamp))) { + continue; + } + buffered_frames.push_back(frame_data); + } else if (FAILED(hr)) { + return MP4_VIDEO_FAILURE; + } else { + break; + } + } + + inputBufferBytes-=this_size; + h264_data+=this_size; + } + return MP4_VIDEO_SUCCESS; +} + +int H264MP4Decoder::CanHandleCodec(const char *codecName) +{ + return !strcmp(codecName, "avc1"); +} + +void H264MP4Decoder::Flush() +{ + for (size_t i=0;i<buffered_frames.size();i++) { + nullsoft_h264_frame_data frame_data = buffered_frames[i]; + decoder.FreeFrame((YV12_PLANES *)frame_data.data, frame_data.decoder_data); + } + decoder.Flush(); +} + +int H264MP4Decoder::GetPicture(void **data, void **decoder_data, MP4Timestamp *timestamp) +{ + if (!buffered_frames.empty()) { + nullsoft_h264_frame_data frame_data = buffered_frames[0]; + buffered_frames.erase(buffered_frames.begin()); + *data = frame_data.data; + *decoder_data = frame_data.decoder_data; + *timestamp = MP4ConvertToTrackTimestamp(mp4_file, mp4_track, frame_data.local_timestamp, MP4_NANOSECONDS_TIME_SCALE/100ULL); + return MP4_VIDEO_SUCCESS; + } + + uint64_t local_timestamp; + if (SUCCEEDED(decoder.GetFrame((YV12_PLANES **)data, decoder_data, &local_timestamp))) { + *timestamp = MP4ConvertToTrackTimestamp(mp4_file, mp4_track, local_timestamp, MP4_NANOSECONDS_TIME_SCALE/100ULL); + return MP4_VIDEO_SUCCESS; + } else { + return MP4_VIDEO_FAILURE; + } +} + +void H264MP4Decoder::FreePicture(void *data, void *decoder_data) +{ + decoder.FreeFrame((YV12_PLANES *)data, decoder_data); +} + +void H264MP4Decoder::HurryUp(int state) +{ + // TODO if (decoder) + //H264_HurryUp(decoder, state); +} + + + +#define CBCLASS H264MP4Decoder +START_DISPATCH; +CB(MPEG4_VIDEO_OPEN, Open) +CB(MPEG4_VIDEO_GETOUTPUTFORMAT, GetOutputFormat) +CB(MPEG4_VIDEO_DECODE, DecodeSample) +CB(MPEG4_VIDEO_HANDLES_CODEC, CanHandleCodec) +VCB(MPEG4_VIDEO_FLUSH, Flush) +CB(MPEG4_VIDEO_GET_PICTURE, GetPicture) +VCB(MPEG4_VIDEO_FREE_PICTURE, FreePicture) +VCB(MPEG4_VIDEO_HURRY_UP, HurryUp) +END_DISPATCH; +#undef CBCLASS + diff --git a/Src/h264/h264_mp4_decoder.h b/Src/h264/h264_mp4_decoder.h new file mode 100644 index 00000000..81f04b81 --- /dev/null +++ b/Src/h264/h264_mp4_decoder.h @@ -0,0 +1,37 @@ +#pragma once +#include "../Plugins/Input/in_mp4/mpeg4video.h" +#include "MFTDecoder.h" +#include <vector> + +// {F13CB206-E8F2-4353-B8B9-587D02CB701C} +static const GUID mp4_h264_guid = +{ 0xf13cb206, 0xe8f2, 0x4353, { 0xb8, 0xb9, 0x58, 0x7d, 0x2, 0xcb, 0x70, 0x1c } }; + +class H264MP4Decoder : public MP4VideoDecoder +{ +public: + static const char *getServiceName() { return "H.264 MP4 Decoder"; } + static GUID getServiceGuid() { return mp4_h264_guid; } + H264MP4Decoder(); + ~H264MP4Decoder(); + +private: + /* mpeg4video interface */ + int Open(MP4FileHandle mp4_file, MP4TrackId mp4_track); + int GetOutputFormat(int *x, int *y, int *color_format, double *aspect_ratio); + int DecodeSample(const void *inputBuffer, size_t inputBufferBytes, MP4Timestamp timestamp); + void Flush(); + void Close(); + int CanHandleCodec(const char *codecName); + int GetPicture(void **data, void **decoder_data, MP4Timestamp *timestamp); + void FreePicture(void *data, void *decoder_data); + void HurryUp(int state); + MFTDecoder decoder; + uint32_t nalu_size_bytes; + UINT width, height; + MP4FileHandle mp4_file; + MP4TrackId mp4_track; + std::vector<nullsoft_h264_frame_data> buffered_frames; +protected: + RECVS_DISPATCH; +};
\ No newline at end of file diff --git a/Src/h264/main.cpp b/Src/h264/main.cpp new file mode 100644 index 00000000..df0e712f --- /dev/null +++ b/Src/h264/main.cpp @@ -0,0 +1,96 @@ +#define WIN32_LEAN_AND_MEAN +#include "api__h264.h" +#include <bfc/platform/export.h> +#include "../Agave/Component/ifc_wa5component.h" +#include "../nu/Singleton.h" +#include "../nu/factoryt.h" +#include "h264_flv_decoder.h" +#include "h264_mp4_decoder.h" +#include "h264_mkv_decoder.h" +#include "avi_h264_decoder.h" +#include "NSVFactory.h" + +api_service *WASABI_API_SVC=0; +api_memmgr *WASABI_API_MEMMGR=0; +api_winamp *AGAVE_API_WINAMP=0; + +class H264Component : public ifc_wa5component +{ +public: + void RegisterServices(api_service *service); + void DeregisterServices(api_service *service); +protected: + RECVS_DISPATCH; +}; + +template <class api_T> +void ServiceBuild(api_T *&api_t, GUID factoryGUID_t) +{ + if (WASABI_API_SVC) + { + waServiceFactory *factory = WASABI_API_SVC->service_getServiceByGuid(factoryGUID_t); + if (factory) + api_t = reinterpret_cast<api_T *>( factory->getInterface() ); + } +} + +template <class api_T> +void ServiceRelease(api_T *api_t, GUID factoryGUID_t) +{ + if (WASABI_API_SVC) + { + waServiceFactory *factory = WASABI_API_SVC->service_getServiceByGuid(factoryGUID_t); + if (factory) + factory->releaseInterface(api_t); + } + api_t = NULL; +} + +static FLVDecoderCreator flvCreator; +static SingletonServiceFactory<svc_flvdecoder, FLVDecoderCreator> flvFactory; +static MKVDecoderCreator mkvCreator; +static SingletonServiceFactory<svc_mkvdecoder, MKVDecoderCreator> mkvFactory; +static AVIDecoderCreator aviCreator; +static SingletonServiceFactory<svc_avidecoder, AVIDecoderCreator> aviFactory; +static ServiceFactoryT<MP4VideoDecoder, H264MP4Decoder> mp4Factory; +static NSVFactory nsvCreator; +static SingletonServiceFactory<svc_nsvFactory, NSVFactory> nsvFactory; + +void H264Component::RegisterServices(api_service *service) +{ + WASABI_API_SVC = service; + ServiceBuild(AGAVE_API_WINAMP, winampApiGuid); + if (!AGAVE_API_WINAMP || AGAVE_API_WINAMP->GetRegVer() >= 1) + { + ServiceBuild(WASABI_API_MEMMGR, memMgrApiServiceGuid); + mp4Factory.Register(WASABI_API_SVC); + flvFactory.Register(WASABI_API_SVC, &flvCreator); + mkvFactory.Register(WASABI_API_SVC, &mkvCreator); + aviFactory.Register(WASABI_API_SVC, &aviCreator); + nsvFactory.Register(WASABI_API_SVC, &nsvCreator); + } +} + +void H264Component::DeregisterServices(api_service *service) +{ + mp4Factory.Deregister(WASABI_API_SVC); + flvFactory.Deregister(WASABI_API_SVC); + mkvFactory.Deregister(WASABI_API_SVC); + aviFactory.Deregister(WASABI_API_SVC); + nsvFactory.Deregister(WASABI_API_SVC); + ServiceRelease(WASABI_API_MEMMGR, memMgrApiServiceGuid); + ServiceRelease(AGAVE_API_WINAMP, winampApiGuid); +} + +static H264Component component; +extern "C" DLLEXPORT ifc_wa5component *GetWinamp5SystemComponent() +{ + return &component; +} + +#define CBCLASS H264Component +START_DISPATCH; +VCB(API_WA5COMPONENT_REGISTERSERVICES, RegisterServices) +VCB(API_WA5COMPONENT_DEREEGISTERSERVICES, DeregisterServices) +END_DISPATCH; +#undef CBCLASS diff --git a/Src/h264/nsv_h264_decoder.cpp b/Src/h264/nsv_h264_decoder.cpp new file mode 100644 index 00000000..3fa781ff --- /dev/null +++ b/Src/h264/nsv_h264_decoder.cpp @@ -0,0 +1,77 @@ +#include "nsv_h264_decoder.h" +#include "../nsv/nsvlib.h" +#include "../nsv/dec_if.h" +#include <assert.h> +#include <Mferror.h> + +H264_Decoder::H264_Decoder() +{ + vidbufdec=0; + last_pic = 0; + decoder.Open(); +} + +H264_Decoder::~H264_Decoder() +{ + for (size_t i=0;i<buffered_frames.size();i++) { + nullsoft_h264_frame_data frame_data = buffered_frames[i]; + decoder.FreeFrame((YV12_PLANES *)frame_data.data, frame_data.decoder_data); + } + + decoder.FreeFrame(vidbufdec, last_pic); +} + +int H264_Decoder::decode(int need_kf, + void *_in, int _in_len, + void **out, // out is set to a pointer to data + unsigned int *out_type, // 'Y','V','1','2' is currently defined + int *is_kf) +{ + *out_type=NSV_MAKETYPE('Y','V','1','2'); + + if (last_pic) + { + decoder.FreeFrame(vidbufdec, last_pic); + vidbufdec=0; + last_pic=0; + } + + if (_in_len) { + for (;;) { + HRESULT hr = decoder.FeedRaw(_in, _in_len, 0); + if (hr == MF_E_NOTACCEPTING) { + nullsoft_h264_frame_data frame_data; + if (FAILED(decoder.GetFrame((YV12_PLANES **)&frame_data.data, &frame_data.decoder_data, &frame_data.local_timestamp))) { + continue; + } + buffered_frames.push_back(frame_data); + } else if (FAILED(hr)) { + return -1; + } else { + break; + } + } + } else { + decoder.Drain(); + } + + if (SUCCEEDED(decoder.GetFrame(&vidbufdec, &last_pic, 0))) { + *out = vidbufdec; + *is_kf = 1; + } else { + *out = 0; + } + + return 0; +} + +void H264_Decoder::flush() +{ + for ( size_t i = 0; i < buffered_frames.size(); i++ ) + { + nullsoft_h264_frame_data frame_data = buffered_frames[ i ]; + decoder.FreeFrame( (YV12_PLANES *)frame_data.data, frame_data.decoder_data ); + } + + decoder.Flush(); +} diff --git a/Src/h264/nsv_h264_decoder.h b/Src/h264/nsv_h264_decoder.h new file mode 100644 index 00000000..4a3375b1 --- /dev/null +++ b/Src/h264/nsv_h264_decoder.h @@ -0,0 +1,24 @@ +#pragma once +#include "../nsv/dec_if.h" +#include "annexb.h" +#include "MFTDecoder.h" +#include <vector> + +class H264_Decoder : public IVideoDecoder +{ +public: + H264_Decoder(); + ~H264_Decoder(); + int decode(int need_kf, + void *in, int in_len, + void **out, // out is set to a pointer to data + unsigned int *out_type, // 'Y','V','1','2' is currently defined + int *is_kf); + void flush(); + +private: + MFTDecoder decoder; + YV12_PLANES *vidbufdec; + void *last_pic; + std::vector<nullsoft_h264_frame_data> buffered_frames; +}; diff --git a/Src/h264/resource.h b/Src/h264/resource.h new file mode 100644 index 00000000..b1baeecc --- /dev/null +++ b/Src/h264/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by h264.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Src/h264/version.rc2 b/Src/h264/version.rc2 new file mode 100644 index 00000000..b273b7d6 --- /dev/null +++ b/Src/h264/version.rc2 @@ -0,0 +1,39 @@ + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// +#include "../Winamp/buildType.h" +VS_VERSION_INFO VERSIONINFO + FILEVERSION WINAMP_PRODUCTVER + PRODUCTVERSION WINAMP_PRODUCTVER + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Winamp SA" + VALUE "FileDescription", "Winamp 5.x System Component" + VALUE "FileVersion", STR_WINAMP_PRODUCTVER + VALUE "InternalName", "h264.w5s" + VALUE "LegalCopyright", "Copyright © 2015-2023 Ben Allison" + VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA" + VALUE "OriginalFilename", "h264.w5s" + VALUE "ProductName", "Winamp H.264 Decoder Service" + VALUE "ProductVersion", STR_WINAMP_PRODUCTVER + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END |