aboutsummaryrefslogtreecommitdiff
path: root/Src/h264/h264_flv_decoder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Src/h264/h264_flv_decoder.cpp')
-rw-r--r--Src/h264/h264_flv_decoder.cpp220
1 files changed, 220 insertions, 0 deletions
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
+