aboutsummaryrefslogtreecommitdiff
path: root/Src/h264/h264_mkv_decoder.cpp
diff options
context:
space:
mode:
authorJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
committerJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
commit20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch)
tree12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/h264/h264_mkv_decoder.cpp
parent537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff)
downloadwinamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz
Initial community commit
Diffstat (limited to 'Src/h264/h264_mkv_decoder.cpp')
-rw-r--r--Src/h264/h264_mkv_decoder.cpp237
1 files changed, 237 insertions, 0 deletions
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
+