mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-28 20:51:13 +00:00
905 lines
26 KiB
C
905 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 void
|
|
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);
|
|
|
|
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 gboolean
|
|
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 ret;
|
|
}
|
|
}
|
|
|
|
/* 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);
|
|
}
|
|
|
|
gst_h264_dpb_add (h264_dec->dpb, h264_frame);
|
|
|
|
return GST_FLOW_OK;
|
|
}
|
|
|
|
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;
|
|
}
|