gstreamer/sys/vdpau/h264/gstvdph264dec.c
Carl-Anton Ingmarsson bd20e5d077 vdpau: fixup GstFlowReturn handling
Previously the different decoders would discard errounous GstFlowReturns coming
from downstream. Now we properly return these further upstream so that we
properly error out on eg. negotiation problems.
2011-03-27 19:51:31 +02:00

903 lines
26 KiB
C

/* GStreamer
*
* Copyright (C) 2009 Carl-Anton Ingmarsson <ca.ingmarsson@gmail.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/base/gstadapter.h>
#include <gst/base/gstbitreader.h>
#include <string.h>
#include "gsth264frame.h"
#include "gstvdph264dec.h"
GST_DEBUG_CATEGORY_STATIC (gst_vdp_h264_dec_debug);
#define GST_CAT_DEFAULT gst_vdp_h264_dec_debug
static GstStaticPadTemplate sink_template =
GST_STATIC_PAD_TEMPLATE (GST_BASE_VIDEO_DECODER_SINK_NAME,
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("video/x-h264, " "interlaced = (boolean) false")
);
#define DEBUG_INIT(bla) \
GST_DEBUG_CATEGORY_INIT (gst_vdp_h264_dec_debug, "vdpauh264dec", 0, \
"VDPAU h264 decoder");
GST_BOILERPLATE_FULL (GstVdpH264Dec, gst_vdp_h264_dec, GstVdpDecoder,
GST_TYPE_VDP_DECODER, DEBUG_INIT);
#define SYNC_CODE_SIZE 3
#define READ_UINT8(reader, val, nbits) { \
if (!gst_bit_reader_get_bits_uint8 (reader, &val, nbits)) { \
GST_WARNING ("failed to read uint8, nbits: %d", nbits); \
return FALSE; \
} \
}
#define READ_UINT16(reader, val, nbits) { \
if (!gst_bit_reader_get_bits_uint16 (reader, &val, nbits)) { \
GST_WARNING ("failed to read uint16, nbits: %d", nbits); \
return FALSE; \
} \
}
#define SKIP(reader, nbits) { \
if (!gst_bit_reader_skip (reader, nbits)) { \
GST_WARNING ("failed to skip nbits: %d", nbits); \
return FALSE; \
} \
}
static gboolean
gst_vdp_h264_dec_set_sink_caps (GstBaseVideoDecoder * base_video_decoder,
GstCaps * caps)
{
GstVdpH264Dec *h264_dec;
GstStructure *structure;
const GValue *value;
h264_dec = GST_VDP_H264_DEC (base_video_decoder);
structure = gst_caps_get_structure (caps, 0);
/* packetized video has a codec_data */
if ((value = gst_structure_get_value (structure, "codec_data"))) {
GstBuffer *buf;
GstBitReader reader;
guint8 version;
guint8 n_sps, n_pps;
gint i;
GST_DEBUG_OBJECT (h264_dec, "have packetized h264");
h264_dec->packetized = TRUE;
buf = gst_value_get_buffer (value);
GST_MEMDUMP ("avcC:", GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
/* parse the avcC data */
if (GST_BUFFER_SIZE (buf) < 7) {
GST_ERROR_OBJECT (h264_dec, "avcC size %u < 7", GST_BUFFER_SIZE (buf));
return FALSE;
}
gst_bit_reader_init_from_buffer (&reader, buf);
READ_UINT8 (&reader, version, 8);
if (version != 1)
return FALSE;
SKIP (&reader, 30);
READ_UINT8 (&reader, h264_dec->nal_length_size, 2);
h264_dec->nal_length_size += 1;
GST_DEBUG_OBJECT (h264_dec, "nal length %u", h264_dec->nal_length_size);
SKIP (&reader, 3);
READ_UINT8 (&reader, n_sps, 5);
for (i = 0; i < n_sps; i++) {
guint16 sps_length;
guint8 *data;
READ_UINT16 (&reader, sps_length, 16);
sps_length -= 1;
SKIP (&reader, 8);
data = GST_BUFFER_DATA (buf) + gst_bit_reader_get_pos (&reader) / 8;
if (!gst_h264_parser_parse_sequence (h264_dec->parser, data, sps_length))
return FALSE;
SKIP (&reader, sps_length * 8);
}
READ_UINT8 (&reader, n_pps, 8);
for (i = 0; i < n_pps; i++) {
guint16 pps_length;
guint8 *data;
READ_UINT16 (&reader, pps_length, 16);
pps_length -= 1;
SKIP (&reader, 8);
data = GST_BUFFER_DATA (buf) + gst_bit_reader_get_pos (&reader) / 8;
if (!gst_h264_parser_parse_picture (h264_dec->parser, data, pps_length))
return FALSE;
SKIP (&reader, pps_length * 8);
}
}
return TRUE;
}
static GstFlowReturn
gst_vdp_h264_dec_output (GstH264DPB * dpb, GstH264Frame * h264_frame,
gpointer user_data)
{
GstBaseVideoDecoder *base_video_decoder = (GstBaseVideoDecoder *) user_data;
GST_DEBUG ("poc: %d", h264_frame->poc);
return gst_base_video_decoder_finish_frame (base_video_decoder,
GST_VIDEO_FRAME_CAST (h264_frame));
}
static guint
gst_vdp_h264_dec_calculate_poc (GstVdpH264Dec * h264_dec, GstH264Slice * slice)
{
GstH264Picture *pic;
GstH264Sequence *seq;
guint poc;
pic = slice->picture;
seq = pic->sequence;
if (seq->pic_order_cnt_type == 0) {
guint32 max_poc_cnt_lsb = 1 << (seq->log2_max_pic_order_cnt_lsb_minus4 + 4);
if ((slice->pic_order_cnt_lsb < h264_dec->prev_poc_lsb) &&
((h264_dec->prev_poc_lsb - slice->pic_order_cnt_lsb) >=
(max_poc_cnt_lsb / 2)))
h264_dec->poc_msb = h264_dec->poc_msb + max_poc_cnt_lsb;
else if ((slice->pic_order_cnt_lsb > h264_dec->prev_poc_lsb) &&
((slice->pic_order_cnt_lsb - h264_dec->prev_poc_lsb) >
(max_poc_cnt_lsb / 2)))
h264_dec->poc_msb = h264_dec->poc_msb - max_poc_cnt_lsb;
poc = h264_dec->poc_msb + slice->pic_order_cnt_lsb;
h264_dec->prev_poc_lsb = slice->pic_order_cnt_lsb;
}
return poc;
}
static void
gst_vdp_h264_dec_init_frame_info (GstVdpH264Dec * h264_dec,
GstH264Frame * h264_frame)
{
GstH264Slice *slice;
slice = &h264_frame->slice_hdr;
h264_frame->poc = gst_vdp_h264_dec_calculate_poc (h264_dec, slice);
h264_frame->output_needed = TRUE;
h264_frame->is_long_term = FALSE;
h264_frame->frame_idx = slice->frame_num;
/* is reference */
if (slice->nal_unit.ref_idc == 0)
h264_frame->is_reference = FALSE;
else if (slice->nal_unit.IdrPicFlag) {
h264_frame->is_reference = TRUE;
if (slice->dec_ref_pic_marking.long_term_reference_flag) {
h264_frame->is_long_term = TRUE;
h264_frame->frame_idx = 0;
}
} else {
h264_frame->is_reference = TRUE;
if (slice->dec_ref_pic_marking.adaptive_ref_pic_marking_mode_flag) {
GstH264RefPicMarking *marking;
guint i;
marking = slice->dec_ref_pic_marking.ref_pic_marking;
for (i = 0; i < slice->dec_ref_pic_marking.n_ref_pic_marking; i++) {
if (marking[i].memory_management_control_operation == 6) {
h264_frame->is_long_term = TRUE;
h264_frame->frame_idx = marking[i].long_term_frame_idx;
break;
}
}
}
}
}
static gboolean
gst_vdp_h264_dec_calculate_par (GstH264VUIParameters * vui, guint16 * par_n,
guint16 * par_d)
{
guint16 aspect[16][2] = { {1, 1}, {12, 11}, {10, 11}, {16, 11}, {40, 33},
{24, 11}, {20, 11}, {32, 11}, {80, 33}, {18, 11}, {15, 11}, {64, 33},
{160, 99}, {4, 3}, {3, 2}, {2, 1}
};
if (vui->aspect_ratio_idc >= 1 && vui->aspect_ratio_idc <= 16) {
*par_n = aspect[vui->aspect_ratio_idc - 1][0];
*par_d = aspect[vui->aspect_ratio_idc - 1][1];
return TRUE;
} else if (vui->aspect_ratio_idc == 255) {
*par_n = vui->sar_height;
*par_d = vui->sar_width;
return TRUE;
}
return FALSE;
}
static GstFlowReturn
gst_vdp_h264_dec_idr (GstVdpH264Dec * h264_dec, GstH264Frame * h264_frame)
{
GstH264Slice *slice;
GstH264Sequence *seq;
h264_dec->poc_msb = 0;
h264_dec->prev_poc_lsb = 0;
slice = &h264_frame->slice_hdr;
if (slice->dec_ref_pic_marking.no_output_of_prior_pics_flag)
gst_h264_dpb_flush (h264_dec->dpb, FALSE);
else
gst_h264_dpb_flush (h264_dec->dpb, TRUE);
if (slice->dec_ref_pic_marking.long_term_reference_flag)
g_object_set (h264_dec->dpb, "max-longterm-frame-idx", 0, NULL);
else
g_object_set (h264_dec->dpb, "max-longterm-frame-idx", -1, NULL);
seq = slice->picture->sequence;
if (seq != h264_dec->sequence) {
GstVideoState state;
VdpDecoderProfile profile;
GstFlowReturn ret;
state =
gst_base_video_decoder_get_state (GST_BASE_VIDEO_DECODER (h264_dec));
state.width = (seq->pic_width_in_mbs_minus1 + 1) * 16 -
2 * seq->frame_crop_right_offset;
state.height = (2 - seq->frame_mbs_only_flag) *
(seq->pic_height_in_map_units_minus1 + 1) * 16;
if (seq->frame_mbs_only_flag)
state.height -= 2 * seq->frame_crop_bottom_offset;
else
state.height -= 4 * seq->frame_crop_bottom_offset;
/* calculate framerate if we haven't got one */
if (state.fps_n == 0 && seq->vui_parameters_present_flag) {
GstH264VUIParameters *vui;
guint16 par_n, par_d;
vui = &seq->vui_parameters;
if (gst_vdp_h264_dec_calculate_par (vui, &par_n, &par_d)) {
state.par_n = par_n;
state.par_d = par_d;
}
if (vui->timing_info_present_flag && vui->fixed_frame_rate_flag) {
state.fps_n = vui->time_scale;
state.fps_d = vui->num_units_in_tick;
if (seq->frame_mbs_only_flag)
state.fps_d *= 2;
}
}
gst_base_video_decoder_set_state (GST_BASE_VIDEO_DECODER (h264_dec), state);
switch (seq->profile_idc) {
case 66:
profile = VDP_DECODER_PROFILE_H264_BASELINE;
break;
case 77:
profile = VDP_DECODER_PROFILE_H264_MAIN;
break;
case 100:
profile = VDP_DECODER_PROFILE_H264_HIGH;
break;
default:
GST_ELEMENT_ERROR (h264_dec, STREAM, WRONG_TYPE,
("vdpauh264dec doesn't support this streams profile"),
("profile_idc: %d", seq->profile_idc));
return GST_FLOW_ERROR;
}
ret = gst_vdp_decoder_init_decoder (GST_VDP_DECODER (h264_dec), profile,
seq->num_ref_frames);
if (ret != GST_FLOW_OK)
return ret;
g_object_set (h264_dec->dpb, "num-ref-frames", seq->num_ref_frames, NULL);
h264_dec->sequence = seq;
}
return GST_FLOW_OK;
}
static VdpPictureInfoH264
gst_vdp_h264_dec_fill_info (GstVdpH264Dec * h264_dec, GstH264Frame * h264_frame)
{
GstH264Slice *slice;
GstH264Picture *pic;
GstH264Sequence *seq;
VdpPictureInfoH264 info;
slice = &h264_frame->slice_hdr;
pic = slice->picture;
seq = pic->sequence;
info.slice_count = h264_frame->slices->len;
/* FIXME: we only handle frames for now */
info.field_order_cnt[0] = h264_frame->poc;
info.field_order_cnt[1] = h264_frame->poc;
info.is_reference = h264_frame->is_reference;
info.frame_num = slice->frame_num;
info.field_pic_flag = slice->field_pic_flag;
info.bottom_field_flag = slice->bottom_field_flag;
info.num_ref_idx_l0_active_minus1 = slice->num_ref_idx_l0_active_minus1;
info.num_ref_idx_l1_active_minus1 = slice->num_ref_idx_l1_active_minus1;
info.num_ref_frames = seq->num_ref_frames;
info.frame_mbs_only_flag = seq->frame_mbs_only_flag;
info.mb_adaptive_frame_field_flag = seq->mb_adaptive_frame_field_flag;
info.log2_max_frame_num_minus4 = seq->log2_max_frame_num_minus4;
info.pic_order_cnt_type = seq->pic_order_cnt_type;
info.log2_max_pic_order_cnt_lsb_minus4 =
seq->log2_max_pic_order_cnt_lsb_minus4;
info.delta_pic_order_always_zero_flag = seq->delta_pic_order_always_zero_flag;
info.direct_8x8_inference_flag = seq->direct_8x8_inference_flag;
info.constrained_intra_pred_flag = pic->constrained_intra_pred_flag;
info.weighted_pred_flag = pic->weighted_pred_flag;
info.weighted_bipred_idc = pic->weighted_bipred_idc;
info.transform_8x8_mode_flag = pic->transform_8x8_mode_flag;
info.chroma_qp_index_offset = pic->chroma_qp_index_offset;
info.second_chroma_qp_index_offset = pic->second_chroma_qp_index_offset;
info.pic_init_qp_minus26 = pic->pic_init_qp_minus26;
info.entropy_coding_mode_flag = pic->entropy_coding_mode_flag;
info.pic_order_present_flag = pic->pic_order_present_flag;
info.deblocking_filter_control_present_flag =
pic->deblocking_filter_control_present_flag;
info.redundant_pic_cnt_present_flag = pic->redundant_pic_cnt_present_flag;
memcpy (&info.scaling_lists_4x4, &pic->scaling_lists_4x4, 96);
memcpy (&info.scaling_lists_8x8, &pic->scaling_lists_8x8, 128);
gst_h264_dpb_fill_reference_frames (h264_dec->dpb, info.referenceFrames);
return info;
}
static VdpBitstreamBuffer *
gst_vdp_h264_dec_create_bitstream_buffers (GstVdpH264Dec * h264_dec,
GstH264Frame * h264_frame, guint * n_bufs)
{
VdpBitstreamBuffer *bufs;
if (h264_dec->packetized) {
guint i;
bufs = g_new (VdpBitstreamBuffer, h264_frame->slices->len * 2);
*n_bufs = h264_frame->slices->len * 2;
for (i = 0; i < h264_frame->slices->len; i++) {
static const guint8 start_code[] = { 0x00, 0x00, 0x01 };
guint idx;
GstBuffer *buf;
idx = i * 2;
bufs[idx].bitstream = start_code;
bufs[idx].bitstream_bytes = 3;
bufs[idx].struct_version = VDP_BITSTREAM_BUFFER_VERSION;
idx = idx + 1;
buf = GST_BUFFER_CAST (g_ptr_array_index (h264_frame->slices, i));
bufs[idx].bitstream = GST_BUFFER_DATA (buf) + h264_dec->nal_length_size;
bufs[idx].bitstream_bytes = GST_BUFFER_SIZE (buf) -
h264_dec->nal_length_size;
bufs[idx].struct_version = VDP_BITSTREAM_BUFFER_VERSION;
}
}
else {
guint i;
bufs = g_new (VdpBitstreamBuffer, h264_frame->slices->len);
*n_bufs = h264_frame->slices->len;
for (i = 0; i < h264_frame->slices->len; i++) {
GstBuffer *buf;
buf = GST_BUFFER_CAST (g_ptr_array_index (h264_frame->slices, i));
bufs[i].bitstream = GST_BUFFER_DATA (buf);
bufs[i].bitstream_bytes = GST_BUFFER_SIZE (buf);
bufs[i].struct_version = VDP_BITSTREAM_BUFFER_VERSION;
}
}
return bufs;
}
static GstFlowReturn
gst_vdp_h264_dec_handle_frame (GstBaseVideoDecoder * base_video_decoder,
GstVideoFrame * frame, GstClockTimeDiff deadline)
{
GstVdpH264Dec *h264_dec = GST_VDP_H264_DEC (base_video_decoder);
GstH264Frame *h264_frame;
GstH264Slice *slice;
GstH264Picture *pic;
GstH264Sequence *seq;
GstFlowReturn ret;
GstVdpVideoBuffer *outbuf;
VdpPictureInfoH264 info;
VdpBitstreamBuffer *bufs;
guint n_bufs;
GST_DEBUG ("handle_frame");
h264_frame = GST_H264_FRAME_CAST (frame);
slice = &h264_frame->slice_hdr;
pic = slice->picture;
seq = pic->sequence;
if (slice->nal_unit.IdrPicFlag) {
ret = gst_vdp_h264_dec_idr (h264_dec, h264_frame);
if (ret == GST_FLOW_OK)
h264_dec->got_idr = TRUE;
else {
gst_base_video_decoder_skip_frame (base_video_decoder, frame);
return GST_FLOW_OK;
}
}
/* check if we've got a IDR frame yet */
if (!h264_dec->got_idr) {
gst_base_video_decoder_skip_frame (base_video_decoder, frame);
return GST_FLOW_OK;
}
gst_vdp_h264_dec_init_frame_info (h264_dec, h264_frame);
info = gst_vdp_h264_dec_fill_info (h264_dec, h264_frame);
bufs = gst_vdp_h264_dec_create_bitstream_buffers (h264_dec, h264_frame,
&n_bufs);
ret = gst_vdp_decoder_render (GST_VDP_DECODER (h264_dec),
(VdpPictureInfo *) & info, n_bufs, bufs, &outbuf);
g_free (bufs);
if (ret != GST_FLOW_OK) {
gst_base_video_decoder_skip_frame (base_video_decoder, frame);
return ret;
}
frame->src_buffer = GST_BUFFER_CAST (outbuf);
/* DPB handling */
if (slice->nal_unit.ref_idc != 0 && !slice->nal_unit.IdrPicFlag) {
if (slice->dec_ref_pic_marking.adaptive_ref_pic_marking_mode_flag) {
GstH264RefPicMarking *marking;
guint i;
marking = slice->dec_ref_pic_marking.ref_pic_marking;
for (i = 0; i < slice->dec_ref_pic_marking.n_ref_pic_marking; i++) {
switch (marking[i].memory_management_control_operation) {
case 1:
{
guint16 pic_num;
pic_num = slice->frame_num -
(marking[i].difference_of_pic_nums_minus1 + 1);
gst_h264_dpb_mark_short_term_unused (h264_dec->dpb, pic_num);
break;
}
case 2:
{
gst_h264_dpb_mark_long_term_unused (h264_dec->dpb,
marking[i].long_term_pic_num);
break;
}
case 3:
{
guint16 pic_num;
pic_num = slice->frame_num -
(marking[i].difference_of_pic_nums_minus1 + 1);
gst_h264_dpb_mark_long_term (h264_dec->dpb, pic_num,
marking[i].long_term_frame_idx);
break;
}
case 4:
{
g_object_set (h264_dec->dpb, "max-longterm-frame-idx",
marking[i].max_long_term_frame_idx_plus1 - 1, NULL);
break;
}
case 5:
{
gst_h264_dpb_mark_all_unused (h264_dec->dpb);
g_object_set (h264_dec->dpb, "max-longterm-frame-idx", -1, NULL);
break;
}
default:
break;
}
}
} else
gst_h264_dpb_mark_sliding (h264_dec->dpb);
}
return gst_h264_dpb_add (h264_dec->dpb, h264_frame);
}
static gint
gst_vdp_h264_dec_scan_for_sync (GstBaseVideoDecoder * base_video_decoder,
GstAdapter * adapter)
{
GstVdpH264Dec *h264_dec = GST_VDP_H264_DEC (base_video_decoder);
gint m;
if (h264_dec->packetized)
return 0;
m = gst_adapter_masked_scan_uint32 (adapter, 0xffffff00, 0x00000100,
0, gst_adapter_available (adapter));
if (m == -1)
return gst_adapter_available (adapter) - SYNC_CODE_SIZE;
return m;
}
static GstBaseVideoDecoderScanResult
gst_vdp_h264_dec_scan_for_packet_end (GstBaseVideoDecoder * base_video_decoder,
GstAdapter * adapter, guint * size, gboolean at_eos)
{
GstVdpH264Dec *h264_dec = GST_VDP_H264_DEC (base_video_decoder);
guint avail;
avail = gst_adapter_available (adapter);
if (avail < h264_dec->nal_length_size)
return GST_BASE_VIDEO_DECODER_SCAN_RESULT_NEED_DATA;
if (h264_dec->packetized) {
guint8 *data;
gint i;
guint32 nal_length = 0;
data = g_slice_alloc (h264_dec->nal_length_size);
gst_adapter_copy (adapter, data, 0, h264_dec->nal_length_size);
for (i = 0; i < h264_dec->nal_length_size; i++)
nal_length = (nal_length << 8) | data[i];
g_slice_free1 (h264_dec->nal_length_size, data);
nal_length += h264_dec->nal_length_size;
/* check for invalid NALU sizes, assume the size if the available bytes
* when something is fishy */
if (nal_length <= 1 || nal_length > avail) {
nal_length = avail - h264_dec->nal_length_size;
GST_DEBUG ("fixing invalid NALU size to %u", nal_length);
}
*size = nal_length;
}
else {
guint8 *data;
guint32 start_code;
guint n;
data = g_slice_alloc (SYNC_CODE_SIZE);
gst_adapter_copy (adapter, data, 0, SYNC_CODE_SIZE);
start_code = ((data[0] << 16) && (data[1] << 8) && data[2]);
g_slice_free1 (SYNC_CODE_SIZE, data);
GST_DEBUG ("start_code: %d", start_code);
if (start_code == 0x000001)
return GST_BASE_VIDEO_DECODER_SCAN_RESULT_LOST_SYNC;
n = gst_adapter_masked_scan_uint32 (adapter, 0xffffff00, 0x00000100,
SYNC_CODE_SIZE, avail - SYNC_CODE_SIZE);
if (n == -1)
return GST_BASE_VIDEO_DECODER_SCAN_RESULT_NEED_DATA;
*size = n;
}
GST_DEBUG ("NAL size: %d", *size);
return GST_BASE_VIDEO_DECODER_SCAN_RESULT_OK;
}
static GstFlowReturn
gst_vdp_h264_dec_parse_data (GstBaseVideoDecoder * base_video_decoder,
GstBuffer * buf, gboolean at_eos, GstVideoFrame * frame)
{
GstVdpH264Dec *h264_dec = GST_VDP_H264_DEC (base_video_decoder);
GstBitReader reader;
GstNalUnit nal_unit;
guint8 forbidden_zero_bit;
guint8 *data;
guint size;
gint i;
GstFlowReturn ret = GST_FLOW_OK;
GST_MEMDUMP ("data", GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
gst_bit_reader_init_from_buffer (&reader, buf);
/* skip nal_length or sync code */
gst_bit_reader_skip (&reader, h264_dec->nal_length_size * 8);
if (!gst_bit_reader_get_bits_uint8 (&reader, &forbidden_zero_bit, 1))
goto invalid_packet;
if (forbidden_zero_bit != 0) {
GST_WARNING ("forbidden_zero_bit != 0");
return GST_FLOW_ERROR;
}
if (!gst_bit_reader_get_bits_uint16 (&reader, &nal_unit.ref_idc, 2))
goto invalid_packet;
GST_DEBUG ("nal_ref_idc: %u", nal_unit.ref_idc);
/* read nal_unit_type */
if (!gst_bit_reader_get_bits_uint16 (&reader, &nal_unit.type, 5))
goto invalid_packet;
GST_DEBUG ("nal_unit_type: %u", nal_unit.type);
if (nal_unit.type == 14 || nal_unit.type == 20) {
if (!gst_bit_reader_skip (&reader, 24))
goto invalid_packet;
}
nal_unit.IdrPicFlag = (nal_unit.type == 5 ? 1 : 0);
data = GST_BUFFER_DATA (buf) + gst_bit_reader_get_pos (&reader) / 8;
size = gst_bit_reader_get_remaining (&reader) / 8;
i = size - 1;
while (size >= 0 && data[i] == 0x00) {
size--;
i--;
}
if (GST_VIDEO_FRAME_FLAG_IS_SET (frame, GST_H264_FRAME_GOT_PRIMARY)) {
if (nal_unit.type == GST_NAL_SPS || nal_unit.type == GST_NAL_PPS ||
nal_unit.type == GST_NAL_SEI || nal_unit.type == GST_NAL_AU_DELIMITER ||
(nal_unit.type >= 14 && nal_unit.type <= 18))
ret =
gst_base_video_decoder_have_frame (base_video_decoder, FALSE, &frame);
}
if (nal_unit.type >= GST_NAL_SLICE && nal_unit.type <= GST_NAL_SLICE_IDR) {
GstH264Slice slice;
if (!gst_h264_parser_parse_slice_header (h264_dec->parser, &slice, data,
size, nal_unit))
goto invalid_packet;
if (slice.redundant_pic_cnt == 0) {
if (GST_VIDEO_FRAME_FLAG_IS_SET (frame, GST_H264_FRAME_GOT_PRIMARY)) {
GstH264Slice *p_slice;
guint8 pic_order_cnt_type, p_pic_order_cnt_type;
gboolean finish_frame = FALSE;
p_slice = &(GST_H264_FRAME_CAST (frame)->slice_hdr);
pic_order_cnt_type = slice.picture->sequence->pic_order_cnt_type;
p_pic_order_cnt_type = p_slice->picture->sequence->pic_order_cnt_type;
if (slice.frame_num != p_slice->frame_num)
finish_frame = TRUE;
else if (slice.picture != p_slice->picture)
finish_frame = TRUE;
else if (slice.bottom_field_flag != p_slice->bottom_field_flag)
finish_frame = TRUE;
else if (nal_unit.ref_idc != p_slice->nal_unit.ref_idc &&
(nal_unit.ref_idc == 0 || p_slice->nal_unit.ref_idc == 0))
finish_frame = TRUE;
else if ((pic_order_cnt_type == 0 && p_pic_order_cnt_type == 0) &&
(slice.pic_order_cnt_lsb != p_slice->pic_order_cnt_lsb ||
slice.delta_pic_order_cnt_bottom !=
p_slice->delta_pic_order_cnt_bottom))
finish_frame = TRUE;
else if ((p_pic_order_cnt_type == 1 && p_pic_order_cnt_type == 1) &&
(slice.delta_pic_order_cnt[0] != p_slice->delta_pic_order_cnt[0] ||
slice.delta_pic_order_cnt[1] !=
p_slice->delta_pic_order_cnt[1]))
finish_frame = TRUE;
if (finish_frame)
ret =
gst_base_video_decoder_have_frame (base_video_decoder, FALSE,
&frame);
}
if (!GST_VIDEO_FRAME_FLAG_IS_SET (frame, GST_H264_FRAME_GOT_PRIMARY)) {
if (GST_H264_IS_I_SLICE (slice.type)
|| GST_H264_IS_SI_SLICE (slice.type))
GST_VIDEO_FRAME_FLAG_SET (frame, GST_VIDEO_FRAME_FLAG_KEYFRAME);
GST_H264_FRAME_CAST (frame)->slice_hdr = slice;
GST_VIDEO_FRAME_FLAG_SET (frame, GST_H264_FRAME_GOT_PRIMARY);
}
}
gst_h264_frame_add_slice ((GstH264Frame *) frame, buf);
}
if (nal_unit.type == GST_NAL_SPS) {
if (!gst_h264_parser_parse_sequence (h264_dec->parser, data, size))
goto invalid_packet;
}
if (nal_unit.type == GST_NAL_PPS) {
if (!gst_h264_parser_parse_picture (h264_dec->parser, data, size))
goto invalid_packet;
}
gst_buffer_unref (buf);
return ret;
invalid_packet:
GST_WARNING ("Invalid packet size!");
gst_buffer_unref (buf);
return GST_FLOW_OK;
}
static GstVideoFrame *
gst_vdp_h264_dec_create_frame (GstBaseVideoDecoder * base_video_decoder)
{
return GST_VIDEO_FRAME_CAST (gst_h264_frame_new ());
}
static gboolean
gst_vdp_h264_dec_flush (GstBaseVideoDecoder * base_video_decoder)
{
GstVdpH264Dec *h264_dec = GST_VDP_H264_DEC (base_video_decoder);
h264_dec->got_idr = FALSE;
gst_h264_dpb_flush (h264_dec->dpb, FALSE);
return TRUE;
}
static gboolean
gst_vdp_h264_dec_start (GstBaseVideoDecoder * base_video_decoder)
{
GstVdpH264Dec *h264_dec = GST_VDP_H264_DEC (base_video_decoder);
h264_dec->packetized = FALSE;
h264_dec->nal_length_size = SYNC_CODE_SIZE;
h264_dec->got_idr = FALSE;
h264_dec->sequence = NULL;
h264_dec->parser = g_object_new (GST_TYPE_H264_PARSER, NULL);
h264_dec->dpb = g_object_new (GST_TYPE_H264_DPB, NULL);
gst_h264_dpb_set_output_func (h264_dec->dpb, gst_vdp_h264_dec_output,
h264_dec);
return GST_BASE_VIDEO_DECODER_CLASS
(parent_class)->start (base_video_decoder);
}
static gboolean
gst_vdp_h264_dec_stop (GstBaseVideoDecoder * base_video_decoder)
{
GstVdpH264Dec *h264_dec = GST_VDP_H264_DEC (base_video_decoder);
g_object_unref (h264_dec->parser);
g_object_unref (h264_dec->dpb);
return GST_BASE_VIDEO_DECODER_CLASS (parent_class)->stop (base_video_decoder);
}
static void
gst_vdp_h264_dec_base_init (gpointer g_class)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
gst_element_class_set_details_simple (element_class,
"VDPAU H264 Decoder",
"Decoder",
"Decode h264 stream with vdpau",
"Carl-Anton Ingmarsson <ca.ingmarsson@gmail.com>");
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&sink_template));
}
static void
gst_vdp_h264_dec_init (GstVdpH264Dec * h264_dec, GstVdpH264DecClass * klass)
{
}
static void
gst_vdp_h264_dec_class_init (GstVdpH264DecClass * klass)
{
GstBaseVideoDecoderClass *base_video_decoder_class;
base_video_decoder_class = GST_BASE_VIDEO_DECODER_CLASS (klass);
base_video_decoder_class->start = gst_vdp_h264_dec_start;
base_video_decoder_class->stop = gst_vdp_h264_dec_stop;
base_video_decoder_class->flush = gst_vdp_h264_dec_flush;
base_video_decoder_class->set_sink_caps = gst_vdp_h264_dec_set_sink_caps;
base_video_decoder_class->scan_for_sync = gst_vdp_h264_dec_scan_for_sync;
base_video_decoder_class->scan_for_packet_end =
gst_vdp_h264_dec_scan_for_packet_end;
base_video_decoder_class->parse_data = gst_vdp_h264_dec_parse_data;
base_video_decoder_class->handle_frame = gst_vdp_h264_dec_handle_frame;
base_video_decoder_class->create_frame = gst_vdp_h264_dec_create_frame;
}