mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 20:21:24 +00:00
477 lines
14 KiB
C
477 lines
14 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., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
/**
|
|
* SECTION:element-vdpaumpeg4dec
|
|
*
|
|
* FIXME:Describe vdpaumpeg4dec here.
|
|
*
|
|
* <refsect2>
|
|
* <title>Example launch line</title>
|
|
* |[
|
|
* gst-launch-1.0 -v -m fakesrc ! vdpaumpeg4dec ! fakesink silent=TRUE
|
|
* ]|
|
|
* </refsect2>
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <math.h>
|
|
|
|
#include <gst/gst.h>
|
|
#include <vdpau/vdpau.h>
|
|
#include <string.h>
|
|
|
|
#include "gstvdpmpeg4dec.h"
|
|
|
|
GST_DEBUG_CATEGORY (gst_vdp_mpeg4_dec_debug);
|
|
#define GST_CAT_DEFAULT gst_vdp_mpeg4_dec_debug
|
|
|
|
/* the capabilities of the inputs and outputs.
|
|
*
|
|
* describe the real formats here.
|
|
*/
|
|
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
|
|
GST_PAD_SINK,
|
|
GST_PAD_ALWAYS,
|
|
GST_STATIC_CAPS ("video/mpeg, mpegversion = (int) 4, "
|
|
"systemstream = (boolean) false; "
|
|
"video/x-divx, divxversion = (int) [4, 5]; " "video/x-xvid"));
|
|
|
|
#define DEBUG_INIT(bla) \
|
|
GST_DEBUG_CATEGORY_INIT (gst_vdp_mpeg4_dec_debug, "vdpaumpeg4dec", 0, \
|
|
"VDPAU mpeg4 decoder");
|
|
|
|
G_DEFINE_TYPE_FULL (GstVdpMpeg4Dec, gst_vdp_mpeg4_dec, GST_TYPE_VDP_DECODER,
|
|
DEBUG_INIT);
|
|
|
|
#define SYNC_CODE_SIZE 3
|
|
|
|
static VdpPictureInfoMPEG4Part2
|
|
gst_vdp_mpeg4_dec_fill_info (GstVdpMpeg4Dec * mpeg4_dec,
|
|
GstMpeg4Frame * mpeg4_frame, Mpeg4VideoObjectPlane * vop)
|
|
{
|
|
Mpeg4VideoObjectLayer *vol;
|
|
VdpPictureInfoMPEG4Part2 info;
|
|
|
|
vol = &mpeg4_dec->vol;
|
|
|
|
info.forward_reference = VDP_INVALID_HANDLE;
|
|
info.backward_reference = VDP_INVALID_HANDLE;
|
|
|
|
/* forward reference */
|
|
if (vop->coding_type != I_VOP && mpeg4_dec->f_frame) {
|
|
info.forward_reference =
|
|
GST_VDP_VIDEO_BUFFER (GST_VIDEO_FRAME (mpeg4_dec->f_frame)->
|
|
src_buffer)->surface;
|
|
}
|
|
|
|
if (vop->coding_type == B_VOP) {
|
|
guint32 trd_time, trb_time;
|
|
|
|
trd_time = mpeg4_dec->b_frame->vop_time - mpeg4_dec->f_frame->vop_time;
|
|
trb_time = mpeg4_frame->vop_time - mpeg4_dec->f_frame->vop_time;
|
|
|
|
info.trd[0] = trd_time;
|
|
info.trb[0] = trb_time;
|
|
|
|
info.trd[1] = round ((double) trd_time / (double) mpeg4_dec->tframe);
|
|
info.trb[1] = round ((double) trb_time / (double) mpeg4_dec->tframe);
|
|
|
|
/* backward reference */
|
|
if (mpeg4_dec->b_frame) {
|
|
info.backward_reference =
|
|
GST_VDP_VIDEO_BUFFER (GST_VIDEO_FRAME (mpeg4_dec->b_frame)->
|
|
src_buffer)->surface;
|
|
}
|
|
}
|
|
|
|
memcpy (info.intra_quantizer_matrix, vol->intra_quant_mat, 64);
|
|
memcpy (info.non_intra_quantizer_matrix, vol->non_intra_quant_mat, 64);
|
|
|
|
info.vop_time_increment_resolution = vol->vop_time_increment_resolution;
|
|
info.resync_marker_disable = vol->resync_marker_disable;
|
|
info.interlaced = vol->interlaced;
|
|
info.quant_type = vol->quant_type;
|
|
info.quarter_sample = vol->quarter_sample;
|
|
/* FIXME: support short video header */
|
|
info.short_video_header = FALSE;
|
|
|
|
info.vop_coding_type = vop->coding_type;
|
|
info.vop_fcode_forward = vop->fcode_forward;
|
|
info.vop_fcode_backward = vop->fcode_backward;
|
|
info.rounding_control = vop->rounding_type;
|
|
info.alternate_vertical_scan_flag = vop->alternate_vertical_scan_flag;
|
|
info.top_field_first = vop->top_field_first;
|
|
|
|
return info;
|
|
}
|
|
|
|
static gboolean
|
|
gst_vdp_mpeg4_dec_handle_configuration (GstVdpMpeg4Dec * mpeg4_dec,
|
|
GstMpeg4Frame * mpeg4_frame)
|
|
{
|
|
Mpeg4VisualObjectSequence vos;
|
|
Mpeg4VisualObject vo;
|
|
Mpeg4VideoObjectLayer vol;
|
|
|
|
GstVideoState state;
|
|
guint8 profile_indication;
|
|
VdpDecoderProfile profile;
|
|
|
|
GstFlowReturn ret;
|
|
|
|
if (mpeg4_dec->is_configured)
|
|
return GST_FLOW_OK;
|
|
|
|
if (!mpeg4_frame->vos_buf || !mpeg4_frame->vo_buf || !mpeg4_frame->vol_buf)
|
|
goto skip_frame;
|
|
|
|
if (!mpeg4_util_parse_VOS (mpeg4_frame->vos_buf, &vos))
|
|
goto skip_frame;
|
|
|
|
if (!mpeg4_util_parse_VO (mpeg4_frame->vo_buf, &vo))
|
|
goto skip_frame;
|
|
|
|
if (!mpeg4_util_parse_VOL (mpeg4_frame->vol_buf, &vo, &vol))
|
|
goto skip_frame;
|
|
|
|
state = gst_base_video_decoder_get_state (GST_BASE_VIDEO_DECODER (mpeg4_dec));
|
|
|
|
state.width = vol.width;
|
|
state.height = vol.height;
|
|
|
|
if (vol.fixed_vop_rate) {
|
|
state.fps_n = vol.vop_time_increment_resolution;
|
|
state.fps_d = vol.fixed_vop_time_increment;
|
|
}
|
|
|
|
state.par_n = vol.par_n;
|
|
state.par_d = vol.par_d;
|
|
|
|
gst_base_video_decoder_set_state (GST_BASE_VIDEO_DECODER (mpeg4_dec), state);
|
|
|
|
profile_indication = vos.profile_and_level_indication >> 4;
|
|
switch (profile_indication) {
|
|
case 0x0:
|
|
profile = VDP_DECODER_PROFILE_MPEG4_PART2_SP;
|
|
break;
|
|
|
|
case 0xf:
|
|
profile = VDP_DECODER_PROFILE_MPEG4_PART2_ASP;
|
|
break;
|
|
|
|
default:
|
|
goto unsupported_profile;
|
|
}
|
|
ret = gst_vdp_decoder_init_decoder (GST_VDP_DECODER (mpeg4_dec), profile, 2);
|
|
if (ret != GST_FLOW_OK)
|
|
return ret;
|
|
|
|
mpeg4_dec->vol = vol;
|
|
mpeg4_dec->is_configured = TRUE;
|
|
|
|
return GST_FLOW_OK;
|
|
|
|
skip_frame:
|
|
GST_WARNING ("Skipping frame since we're not configured yet");
|
|
gst_base_video_decoder_skip_frame (GST_BASE_VIDEO_DECODER (mpeg4_dec),
|
|
GST_VIDEO_FRAME (mpeg4_frame));
|
|
return GST_FLOW_CUSTOM_ERROR;
|
|
|
|
unsupported_profile:
|
|
GST_ELEMENT_ERROR (mpeg4_dec, STREAM, WRONG_TYPE,
|
|
("vdpaumpeg4dec doesn't support this streams profile"),
|
|
("profile_and_level_indication: %d", vos.profile_and_level_indication));
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
static GstFlowReturn
|
|
gst_vdp_mpeg4_dec_handle_frame (GstBaseVideoDecoder * base_video_decoder,
|
|
GstVideoFrame * frame, GstClockTimeDiff deadline)
|
|
{
|
|
GstVdpMpeg4Dec *mpeg4_dec = GST_VDP_MPEG4_DEC (base_video_decoder);
|
|
|
|
GstMpeg4Frame *mpeg4_frame;
|
|
GstFlowReturn ret;
|
|
|
|
Mpeg4VideoObjectLayer *vol;
|
|
Mpeg4VideoObjectPlane vop;
|
|
|
|
VdpPictureInfoMPEG4Part2 info;
|
|
VdpBitstreamBuffer bufs[1];
|
|
GstVdpVideoBuffer *video_buf;
|
|
|
|
mpeg4_frame = GST_MPEG4_FRAME (frame);
|
|
|
|
ret = gst_vdp_mpeg4_dec_handle_configuration (mpeg4_dec, mpeg4_frame);
|
|
if (ret != GST_FLOW_OK)
|
|
return ret;
|
|
|
|
vol = &mpeg4_dec->vol;
|
|
if (!mpeg4_util_parse_VOP (mpeg4_frame->vop_buf, vol, &vop)) {
|
|
gst_base_video_decoder_skip_frame (base_video_decoder, frame);
|
|
return GST_FLOW_CUSTOM_ERROR;
|
|
}
|
|
|
|
/* calculate vop time */
|
|
mpeg4_frame->vop_time =
|
|
vop.modulo_time_base * vol->vop_time_increment_resolution +
|
|
vop.time_increment;
|
|
|
|
if (mpeg4_dec->tframe == -1 && vop.coding_type == B_VOP)
|
|
mpeg4_dec->tframe = mpeg4_frame->vop_time - mpeg4_dec->f_frame->vop_time;
|
|
|
|
if (vop.coding_type != B_VOP) {
|
|
if (mpeg4_dec->b_frame) {
|
|
|
|
ret = gst_base_video_decoder_finish_frame (base_video_decoder,
|
|
GST_VIDEO_FRAME_CAST (mpeg4_dec->b_frame));
|
|
|
|
if (mpeg4_dec->f_frame)
|
|
gst_video_frame_unref (GST_VIDEO_FRAME_CAST (mpeg4_dec->f_frame));
|
|
|
|
mpeg4_dec->f_frame = mpeg4_dec->b_frame;
|
|
mpeg4_dec->b_frame = NULL;
|
|
}
|
|
}
|
|
|
|
info = gst_vdp_mpeg4_dec_fill_info (mpeg4_dec, mpeg4_frame, &vop);
|
|
bufs[0].struct_version = VDP_BITSTREAM_BUFFER_VERSION;
|
|
bufs[0].bitstream = GST_BUFFER_DATA (mpeg4_frame->vop_buf);
|
|
bufs[0].bitstream_bytes = GST_BUFFER_SIZE (mpeg4_frame->vop_buf);
|
|
|
|
ret = gst_vdp_decoder_render (GST_VDP_DECODER (base_video_decoder),
|
|
(VdpPictureInfo *) & info, 1, bufs, &video_buf);
|
|
if (ret != GST_FLOW_OK) {
|
|
gst_base_video_decoder_skip_frame (base_video_decoder, frame);
|
|
return ret;
|
|
}
|
|
|
|
frame->src_buffer = GST_BUFFER_CAST (video_buf);
|
|
|
|
if (vop.coding_type == B_VOP)
|
|
ret = gst_base_video_decoder_finish_frame (base_video_decoder, frame);
|
|
else {
|
|
gst_video_frame_ref (GST_VIDEO_FRAME_CAST (mpeg4_frame));
|
|
mpeg4_dec->b_frame = mpeg4_frame;
|
|
ret = GST_FLOW_OK;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static GstFlowReturn
|
|
gst_vdp_mpeg4_dec_parse_data (GstBaseVideoDecoder * base_video_decoder,
|
|
GstBuffer * buf, gboolean at_eos, GstVideoFrame * frame)
|
|
{
|
|
GstBitReader reader = GST_BIT_READER_INIT_FROM_BUFFER (buf);
|
|
guint8 start_code;
|
|
GstMpeg4Frame *mpeg4_frame;
|
|
|
|
GstFlowReturn ret = GST_FLOW_OK;
|
|
|
|
/* start code prefix */
|
|
SKIP (&reader, 24);
|
|
|
|
/* start_code */
|
|
READ_UINT8 (&reader, start_code, 8);
|
|
|
|
mpeg4_frame = GST_MPEG4_FRAME_CAST (frame);
|
|
|
|
/* collect packages */
|
|
if (start_code == MPEG4_PACKET_VOS) {
|
|
if (mpeg4_frame->vop_buf)
|
|
ret = gst_base_video_decoder_have_frame (base_video_decoder, FALSE,
|
|
(GstVideoFrame **) & mpeg4_frame);
|
|
|
|
gst_buffer_replace (&mpeg4_frame->vos_buf, buf);
|
|
}
|
|
|
|
else if (start_code == MPEG4_PACKET_EVOS) {
|
|
if (mpeg4_frame->vop_buf)
|
|
ret = gst_base_video_decoder_have_frame (base_video_decoder, FALSE,
|
|
(GstVideoFrame **) & mpeg4_frame);
|
|
}
|
|
|
|
else if (start_code == MPEG4_PACKET_VO)
|
|
gst_buffer_replace (&mpeg4_frame->vo_buf, buf);
|
|
|
|
else if (start_code >= MPEG4_PACKET_VOL_MIN &&
|
|
start_code <= MPEG4_PACKET_VOL_MAX)
|
|
gst_buffer_replace (&mpeg4_frame->vol_buf, buf);
|
|
|
|
else if (start_code == MPEG4_PACKET_GOV) {
|
|
if (mpeg4_frame->vop_buf)
|
|
ret = gst_base_video_decoder_have_frame (base_video_decoder, FALSE,
|
|
(GstVideoFrame **) & mpeg4_frame);
|
|
|
|
gst_buffer_replace (&mpeg4_frame->gov_buf, buf);
|
|
}
|
|
|
|
else if (start_code == MPEG4_PACKET_VOP) {
|
|
if (mpeg4_frame->vop_buf)
|
|
ret = gst_base_video_decoder_have_frame (base_video_decoder, FALSE,
|
|
(GstVideoFrame **) & mpeg4_frame);
|
|
|
|
mpeg4_frame->vop_buf = buf;
|
|
}
|
|
|
|
else
|
|
gst_buffer_unref (buf);
|
|
|
|
|
|
if (at_eos && mpeg4_frame->vop_buf)
|
|
ret = gst_base_video_decoder_have_frame (base_video_decoder, TRUE,
|
|
(GstVideoFrame **) & mpeg4_frame);
|
|
|
|
return ret;
|
|
|
|
error:
|
|
gst_buffer_unref (buf);
|
|
GST_WARNING ("error parsing packet");
|
|
return GST_FLOW_OK;
|
|
}
|
|
|
|
static gint
|
|
gst_vdp_mpeg4_dec_scan_for_sync (GstBaseVideoDecoder * base_video_decoder,
|
|
GstAdapter * adapter)
|
|
{
|
|
gint m;
|
|
|
|
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_mpeg4_dec_scan_for_packet_end (GstBaseVideoDecoder * base_video_decoder,
|
|
GstAdapter * adapter, guint * size, gboolean at_eos)
|
|
{
|
|
guint8 *data;
|
|
guint32 sync_code;
|
|
|
|
data = g_slice_alloc (SYNC_CODE_SIZE);
|
|
gst_adapter_copy (adapter, data, 0, SYNC_CODE_SIZE);
|
|
sync_code = ((data[0] << 16) | (data[1] << 8) | data[2]);
|
|
|
|
if (sync_code != 0x000001)
|
|
return GST_BASE_VIDEO_DECODER_SCAN_RESULT_LOST_SYNC;
|
|
|
|
*size = gst_adapter_masked_scan_uint32 (adapter, 0xffffff00, 0x00000100,
|
|
SYNC_CODE_SIZE, gst_adapter_available (adapter) - SYNC_CODE_SIZE);
|
|
|
|
if (*size == -1)
|
|
return GST_BASE_VIDEO_DECODER_SCAN_RESULT_NEED_DATA;
|
|
|
|
return GST_BASE_VIDEO_DECODER_SCAN_RESULT_OK;
|
|
}
|
|
|
|
static GstVideoFrame *
|
|
gst_vdp_mpeg4_dec_create_frame (GstBaseVideoDecoder * base_video_decoder)
|
|
{
|
|
return GST_VIDEO_FRAME_CAST (gst_mpeg4_frame_new ());
|
|
}
|
|
|
|
static gboolean
|
|
gst_vdp_mpeg4_dec_flush (GstBaseVideoDecoder * base_video_decoder)
|
|
{
|
|
GstVdpMpeg4Dec *mpeg4_dec = GST_VDP_MPEG4_DEC (base_video_decoder);
|
|
|
|
if (mpeg4_dec->b_frame) {
|
|
gst_video_frame_unref (GST_VIDEO_FRAME_CAST (mpeg4_dec->b_frame));
|
|
mpeg4_dec->b_frame = NULL;
|
|
}
|
|
|
|
if (mpeg4_dec->f_frame) {
|
|
gst_video_frame_unref (GST_VIDEO_FRAME_CAST (mpeg4_dec->f_frame));
|
|
mpeg4_dec->f_frame = NULL;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_vdp_mpeg4_dec_start (GstBaseVideoDecoder * base_video_decoder)
|
|
{
|
|
GstVdpMpeg4Dec *mpeg4_dec = GST_VDP_MPEG4_DEC (base_video_decoder);
|
|
|
|
mpeg4_dec->is_configured = FALSE;
|
|
mpeg4_dec->tframe = -1;
|
|
|
|
mpeg4_dec->b_frame = NULL;
|
|
mpeg4_dec->f_frame = NULL;
|
|
|
|
return GST_BASE_VIDEO_DECODER_CLASS
|
|
(parent_class)->start (base_video_decoder);
|
|
}
|
|
|
|
static gboolean
|
|
gst_vdp_mpeg4_dec_stop (GstBaseVideoDecoder * base_video_decoder)
|
|
{
|
|
return GST_BASE_VIDEO_DECODER_CLASS (parent_class)->stop (base_video_decoder);
|
|
}
|
|
|
|
static void
|
|
gst_vdp_mpeg4_dec_base_init (gpointer gclass)
|
|
{
|
|
GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
|
|
|
|
gst_element_class_set_static_metadata (element_class,
|
|
"VDPAU Mpeg4 Decoder",
|
|
"Decoder",
|
|
"Decode mpeg4 stream with vdpau",
|
|
"Carl-Anton Ingmarsson <ca.ingmarsson@gmail.com>");
|
|
|
|
gst_element_class_add_static_pad_template (element_class, &sink_template);
|
|
}
|
|
|
|
/* initialize the vdpaumpeg4decoder's class */
|
|
static void
|
|
gst_vdp_mpeg4_dec_class_init (GstVdpMpeg4DecClass * klass)
|
|
{
|
|
GstBaseVideoDecoderClass *base_video_decoder_class;
|
|
|
|
base_video_decoder_class = GST_BASE_VIDEO_DECODER_CLASS (klass);
|
|
|
|
base_video_decoder_class->start = gst_vdp_mpeg4_dec_start;
|
|
base_video_decoder_class->stop = gst_vdp_mpeg4_dec_stop;
|
|
base_video_decoder_class->flush = gst_vdp_mpeg4_dec_flush;
|
|
|
|
base_video_decoder_class->create_frame = gst_vdp_mpeg4_dec_create_frame;
|
|
|
|
base_video_decoder_class->scan_for_sync = gst_vdp_mpeg4_dec_scan_for_sync;
|
|
base_video_decoder_class->scan_for_packet_end =
|
|
gst_vdp_mpeg4_dec_scan_for_packet_end;
|
|
base_video_decoder_class->parse_data = gst_vdp_mpeg4_dec_parse_data;
|
|
|
|
base_video_decoder_class->handle_frame = gst_vdp_mpeg4_dec_handle_frame;
|
|
}
|
|
|
|
static void
|
|
gst_vdp_mpeg4_dec_init (GstVdpMpeg4Dec * mpeg4_dec,
|
|
GstVdpMpeg4DecClass * gclass)
|
|
{
|
|
}
|