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/h264_mp4_decoder.cpp | |
parent | 537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff) | |
download | winamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz |
Initial community commit
Diffstat (limited to 'Src/h264/h264_mp4_decoder.cpp')
-rw-r--r-- | Src/h264/h264_mp4_decoder.cpp | 212 |
1 files changed, 212 insertions, 0 deletions
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 + |