mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-28 04:31:06 +00:00
2c449e42ca
Allocate decoder unit earlier in the main parse() function and don't delegate this task to derived classes. The ultimate purpose is to get rid of dynamic allocation of decoder units.
1208 lines
45 KiB
C
1208 lines
45 KiB
C
/*
|
|
* gstvaapidecoder_mpeg4.c - MPEG-4 decoder
|
|
*
|
|
* Copyright (C) 2011 Intel Corporation
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public License
|
|
* as published by the Free Software Foundation; either version 2.1
|
|
* 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free
|
|
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
/**
|
|
* SECTION:gstvaapidecoder_mpeg4
|
|
* @short_description: MPEG-4 decoder, include h263/divx/xvid support
|
|
*/
|
|
|
|
#include "sysdeps.h"
|
|
#include <string.h>
|
|
#include <gst/base/gstbitreader.h>
|
|
#include <gst/codecparsers/gstmpeg4parser.h>
|
|
#include "gstvaapidecoder_mpeg4.h"
|
|
#include "gstvaapidecoder_objects.h"
|
|
#include "gstvaapidecoder_priv.h"
|
|
#include "gstvaapidisplay_priv.h"
|
|
#include "gstvaapiobject_priv.h"
|
|
|
|
#define DEBUG 1
|
|
#include "gstvaapidebug.h"
|
|
|
|
G_DEFINE_TYPE(GstVaapiDecoderMpeg4,
|
|
gst_vaapi_decoder_mpeg4,
|
|
GST_VAAPI_TYPE_DECODER)
|
|
|
|
#define GST_VAAPI_DECODER_MPEG4_GET_PRIVATE(obj) \
|
|
(G_TYPE_INSTANCE_GET_PRIVATE((obj), \
|
|
GST_VAAPI_TYPE_DECODER_MPEG4, \
|
|
GstVaapiDecoderMpeg4Private))
|
|
|
|
struct _GstVaapiDecoderMpeg4Private {
|
|
GstVaapiProfile profile;
|
|
guint level;
|
|
guint width;
|
|
guint height;
|
|
guint fps_n;
|
|
guint fps_d;
|
|
guint coding_type;
|
|
GstMpeg4VisualObjectSequence vos_hdr;
|
|
GstMpeg4VisualObject vo_hdr;
|
|
GstMpeg4VideoSignalType signal_type;
|
|
GstMpeg4VideoObjectLayer vol_hdr;
|
|
GstMpeg4VideoObjectPlane vop_hdr;
|
|
GstMpeg4VideoPlaneShortHdr svh_hdr;
|
|
GstMpeg4VideoPacketHdr packet_hdr;
|
|
GstMpeg4SpriteTrajectory sprite_trajectory;
|
|
VAIQMatrixBufferMPEG4 iq_matrix;
|
|
GstVaapiPicture *curr_picture;
|
|
// forward reference pic
|
|
GstVaapiPicture *next_picture;
|
|
// backward reference pic
|
|
GstVaapiPicture *prev_picture;
|
|
GstClockTime seq_pts;
|
|
GstClockTime gop_pts;
|
|
GstClockTime pts_diff;
|
|
GstClockTime max_pts;
|
|
// anchor sync time base for any picture type,
|
|
// it is time base of backward reference frame
|
|
GstClockTime last_sync_time;
|
|
// time base for recent I/P/S frame,
|
|
// it is time base of forward reference frame for B frame
|
|
GstClockTime sync_time;
|
|
|
|
/* last non-b-frame time by resolution */
|
|
GstClockTime last_non_b_scale_time;
|
|
GstClockTime non_b_scale_time;
|
|
GstClockTime trb;
|
|
GstClockTime trd;
|
|
// temporal_reference of previous frame of svh
|
|
guint8 prev_t_ref;
|
|
guint is_constructed : 1;
|
|
guint is_opened : 1;
|
|
guint is_first_field : 1;
|
|
guint size_changed : 1;
|
|
guint profile_changed : 1;
|
|
guint progressive_sequence : 1;
|
|
guint closed_gop : 1;
|
|
guint broken_link : 1;
|
|
guint calculate_pts_diff : 1;
|
|
guint is_svh : 1;
|
|
};
|
|
|
|
static void
|
|
gst_vaapi_decoder_mpeg4_close(GstVaapiDecoderMpeg4 *decoder)
|
|
{
|
|
GstVaapiDecoderMpeg4Private * const priv = decoder->priv;
|
|
|
|
gst_vaapi_picture_replace(&priv->curr_picture, NULL);
|
|
gst_vaapi_picture_replace(&priv->next_picture, NULL);
|
|
gst_vaapi_picture_replace(&priv->prev_picture, NULL);
|
|
}
|
|
|
|
static gboolean
|
|
gst_vaapi_decoder_mpeg4_open(GstVaapiDecoderMpeg4 *decoder)
|
|
{
|
|
GstVaapiDecoder *const base_decoder = GST_VAAPI_DECODER(decoder);
|
|
GstVaapiDecoderMpeg4Private * const priv = decoder->priv;
|
|
GstCaps *caps = NULL;
|
|
GstStructure *structure = NULL;
|
|
|
|
gst_vaapi_decoder_mpeg4_close(decoder);
|
|
|
|
priv->is_svh = 0;
|
|
caps = gst_vaapi_decoder_get_caps(base_decoder);
|
|
if (caps) {
|
|
structure = gst_caps_get_structure(caps, 0);
|
|
if (structure) {
|
|
if (gst_structure_has_name(structure, "video/x-h263")) {
|
|
priv->is_svh = 1;
|
|
priv->profile = GST_VAAPI_PROFILE_MPEG4_SIMPLE;
|
|
priv->prev_t_ref = -1;
|
|
}
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gst_vaapi_decoder_mpeg4_destroy(GstVaapiDecoderMpeg4 *decoder)
|
|
{
|
|
gst_vaapi_decoder_mpeg4_close(decoder);
|
|
}
|
|
|
|
static gboolean
|
|
gst_vaapi_decoder_mpeg4_create(GstVaapiDecoderMpeg4 *decoder)
|
|
{
|
|
if (!GST_VAAPI_DECODER_CODEC(decoder))
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
static inline void
|
|
copy_quant_matrix(guint8 dst[64], const guint8 src[64])
|
|
{
|
|
memcpy(dst, src, 64);
|
|
}
|
|
|
|
static GstVaapiDecoderStatus
|
|
ensure_context(GstVaapiDecoderMpeg4 *decoder)
|
|
{
|
|
GstVaapiDecoderMpeg4Private * const priv = decoder->priv;
|
|
GstVaapiProfile profiles[2];
|
|
GstVaapiEntrypoint entrypoint = GST_VAAPI_ENTRYPOINT_VLD;
|
|
guint i, n_profiles = 0;
|
|
gboolean reset_context = FALSE;
|
|
|
|
if (priv->profile_changed) {
|
|
GST_DEBUG("profile changed");
|
|
priv->profile_changed = FALSE;
|
|
reset_context = TRUE;
|
|
|
|
profiles[n_profiles++] = priv->profile;
|
|
if (priv->profile == GST_VAAPI_PROFILE_MPEG4_SIMPLE)
|
|
profiles[n_profiles++] = GST_VAAPI_PROFILE_MPEG4_ADVANCED_SIMPLE;
|
|
|
|
for (i = 0; i < n_profiles; i++) {
|
|
if (gst_vaapi_display_has_decoder(GST_VAAPI_DECODER_DISPLAY(decoder),
|
|
profiles[i], entrypoint))
|
|
break;
|
|
}
|
|
if (i == n_profiles)
|
|
return GST_VAAPI_DECODER_STATUS_ERROR_UNSUPPORTED_PROFILE;
|
|
priv->profile = profiles[i];
|
|
}
|
|
|
|
if (priv->size_changed) {
|
|
GST_DEBUG("size changed");
|
|
priv->size_changed = FALSE;
|
|
reset_context = TRUE;
|
|
}
|
|
|
|
if (reset_context) {
|
|
GstVaapiContextInfo info;
|
|
|
|
info.profile = priv->profile;
|
|
info.entrypoint = entrypoint;
|
|
info.width = priv->width;
|
|
info.height = priv->height;
|
|
info.ref_frames = 2;
|
|
reset_context = gst_vaapi_decoder_ensure_context(
|
|
GST_VAAPI_DECODER(decoder),
|
|
&info
|
|
);
|
|
if (!reset_context)
|
|
return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN;
|
|
}
|
|
return GST_VAAPI_DECODER_STATUS_SUCCESS;
|
|
}
|
|
|
|
static GstVaapiDecoderStatus
|
|
ensure_quant_matrix(GstVaapiDecoderMpeg4 *decoder, GstVaapiPicture *picture)
|
|
{
|
|
GstVaapiDecoderMpeg4Private * const priv = decoder->priv;
|
|
VAIQMatrixBufferMPEG4 *iq_matrix;
|
|
|
|
if (!priv->vol_hdr.load_intra_quant_mat && !priv->vol_hdr.load_non_intra_quant_mat) {
|
|
return GST_VAAPI_DECODER_STATUS_SUCCESS;
|
|
}
|
|
|
|
picture->iq_matrix = GST_VAAPI_IQ_MATRIX_NEW(MPEG4, decoder);
|
|
if (!picture->iq_matrix) {
|
|
GST_DEBUG("failed to allocate IQ matrix");
|
|
return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED;
|
|
}
|
|
iq_matrix = picture->iq_matrix->param;
|
|
|
|
if (priv->vol_hdr.load_intra_quant_mat) {
|
|
iq_matrix->load_intra_quant_mat = 1;
|
|
copy_quant_matrix(iq_matrix->intra_quant_mat,
|
|
priv->vol_hdr.intra_quant_mat);
|
|
}
|
|
else
|
|
iq_matrix->load_intra_quant_mat = 0;
|
|
|
|
if (priv->vol_hdr.load_non_intra_quant_mat) {
|
|
iq_matrix->load_non_intra_quant_mat = 1;
|
|
copy_quant_matrix(iq_matrix->non_intra_quant_mat,
|
|
priv->vol_hdr.non_intra_quant_mat);
|
|
}
|
|
else
|
|
iq_matrix->load_non_intra_quant_mat = 0;
|
|
|
|
|
|
return GST_VAAPI_DECODER_STATUS_SUCCESS;
|
|
}
|
|
|
|
static inline GstVaapiDecoderStatus
|
|
render_picture(GstVaapiDecoderMpeg4 *decoder, GstVaapiPicture *picture)
|
|
{
|
|
if (!gst_vaapi_picture_output(picture))
|
|
return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED;
|
|
return GST_VAAPI_DECODER_STATUS_SUCCESS;
|
|
}
|
|
|
|
/* decode_picture() start to decode a frame/picture
|
|
* decode_current_picture() finishe decoding a frame/picture
|
|
* (commit buffer to driver for decoding)
|
|
*/
|
|
static GstVaapiDecoderStatus
|
|
decode_current_picture(GstVaapiDecoderMpeg4 *decoder)
|
|
{
|
|
GstVaapiDecoderMpeg4Private * const priv = decoder->priv;
|
|
GstVaapiPicture * const picture = priv->curr_picture;
|
|
GstVaapiDecoderStatus status = GST_VAAPI_DECODER_STATUS_SUCCESS;
|
|
|
|
if (picture) {
|
|
if (!gst_vaapi_picture_decode(picture))
|
|
status = GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN;
|
|
if (!GST_VAAPI_PICTURE_IS_REFERENCE(picture)) {
|
|
if ((priv->prev_picture && priv->next_picture) ||
|
|
(priv->closed_gop && priv->next_picture))
|
|
status = render_picture(decoder, picture);
|
|
}
|
|
gst_vaapi_picture_replace(&priv->curr_picture, NULL);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
static GstVaapiDecoderStatus
|
|
decode_sequence(GstVaapiDecoderMpeg4 *decoder, const guint8 *buf, guint buf_size)
|
|
{
|
|
GstVaapiDecoderMpeg4Private * const priv = decoder->priv;
|
|
GstMpeg4VisualObjectSequence * const vos_hdr = &priv->vos_hdr;
|
|
GstVaapiProfile profile;
|
|
|
|
if (gst_mpeg4_parse_visual_object_sequence(vos_hdr, buf, buf_size) != GST_MPEG4_PARSER_OK) {
|
|
GST_DEBUG("failed to parse sequence header");
|
|
return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER;
|
|
}
|
|
|
|
priv->level = vos_hdr->level;
|
|
switch (vos_hdr->profile) {
|
|
case GST_MPEG4_PROFILE_SIMPLE:
|
|
profile = GST_VAAPI_PROFILE_MPEG4_SIMPLE;
|
|
break;
|
|
case GST_MPEG4_PROFILE_ADVANCED_SIMPLE:
|
|
case GST_MPEG4_PROFILE_SIMPLE_SCALABLE: /* shared profile with ADVANCED_SIMPLE */
|
|
profile = GST_VAAPI_PROFILE_MPEG4_ADVANCED_SIMPLE;
|
|
break;
|
|
default:
|
|
GST_DEBUG("unsupported profile %d", vos_hdr->profile);
|
|
return GST_VAAPI_DECODER_STATUS_ERROR_UNSUPPORTED_PROFILE;
|
|
}
|
|
if (priv->profile != profile) {
|
|
priv->profile = profile;
|
|
priv->profile_changed = TRUE;
|
|
}
|
|
priv->seq_pts = GST_VAAPI_DECODER_CODEC_FRAME(decoder)->pts;
|
|
priv->size_changed = TRUE;
|
|
|
|
return GST_VAAPI_DECODER_STATUS_SUCCESS;
|
|
}
|
|
|
|
static GstVaapiDecoderStatus
|
|
decode_sequence_end(GstVaapiDecoderMpeg4 *decoder)
|
|
{
|
|
GstVaapiDecoderMpeg4Private * const priv = decoder->priv;
|
|
GstVaapiDecoderStatus status;
|
|
|
|
if (priv->curr_picture) {
|
|
status = decode_current_picture(decoder);
|
|
if (status != GST_VAAPI_DECODER_STATUS_SUCCESS)
|
|
return status;
|
|
status = render_picture(decoder, priv->curr_picture);
|
|
if (status != GST_VAAPI_DECODER_STATUS_SUCCESS)
|
|
return status;
|
|
}
|
|
|
|
if (priv->next_picture) {
|
|
status = render_picture(decoder, priv->next_picture);
|
|
if (status != GST_VAAPI_DECODER_STATUS_SUCCESS)
|
|
return status;
|
|
}
|
|
return GST_VAAPI_DECODER_STATUS_END_OF_STREAM;
|
|
}
|
|
|
|
static GstVaapiDecoderStatus
|
|
decode_visual_object(GstVaapiDecoderMpeg4 *decoder, const guint8 *buf, guint buf_size)
|
|
{
|
|
GstVaapiDecoderMpeg4Private * const priv = decoder->priv;
|
|
GstMpeg4VisualObject * vo_hdr = &priv->vo_hdr;
|
|
GstMpeg4VideoSignalType * signal_type = &priv->signal_type;
|
|
|
|
if (gst_mpeg4_parse_visual_object (vo_hdr, signal_type, buf, buf_size) != GST_MPEG4_PARSER_OK) {
|
|
GST_DEBUG("failed to parse visual object");
|
|
return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER;
|
|
}
|
|
|
|
/* XXX: video_signal_type isn't used for decoding */
|
|
return GST_VAAPI_DECODER_STATUS_SUCCESS;
|
|
}
|
|
|
|
static GstVaapiDecoderStatus
|
|
decode_video_object_layer(GstVaapiDecoderMpeg4 *decoder, const guint8 *buf, guint buf_size)
|
|
{
|
|
GstVaapiDecoder * const base_decoder = GST_VAAPI_DECODER(decoder);
|
|
GstVaapiDecoderMpeg4Private * const priv = decoder->priv;
|
|
GstMpeg4VisualObject * vo_hdr = &priv->vo_hdr;
|
|
GstMpeg4VideoObjectLayer * vol_hdr = &priv->vol_hdr;
|
|
|
|
if (gst_mpeg4_parse_video_object_layer (vol_hdr, vo_hdr, buf, buf_size) != GST_MPEG4_PARSER_OK) {
|
|
GST_DEBUG("failed to parse video object layer");
|
|
return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER;
|
|
}
|
|
|
|
priv->width = vol_hdr->width;
|
|
priv->height = vol_hdr->height;
|
|
|
|
priv->progressive_sequence = !vol_hdr->interlaced;
|
|
|
|
if (vol_hdr->fixed_vop_rate) {
|
|
priv->fps_n = vol_hdr->vop_time_increment_resolution;
|
|
priv->fps_d = vol_hdr->fixed_vop_time_increment;
|
|
gst_vaapi_decoder_set_framerate(base_decoder, priv->fps_n, priv->fps_d);
|
|
}
|
|
|
|
gst_vaapi_decoder_set_pixel_aspect_ratio(base_decoder, priv->vol_hdr.par_width, priv->vol_hdr.par_height);
|
|
gst_vaapi_decoder_set_picture_size(base_decoder, priv->width, priv->height);
|
|
|
|
return GST_VAAPI_DECODER_STATUS_SUCCESS;
|
|
}
|
|
|
|
static GstVaapiDecoderStatus
|
|
decode_gop(GstVaapiDecoderMpeg4 *decoder, const guint8 *buf, guint buf_size)
|
|
{
|
|
GstVaapiDecoderMpeg4Private * const priv = decoder->priv;
|
|
GstMpeg4GroupOfVOP gop;
|
|
GstClockTime gop_time;
|
|
|
|
if (buf_size >4) {
|
|
if (gst_mpeg4_parse_group_of_vop(&gop, buf, buf_size) != GST_MPEG4_PARSER_OK) {
|
|
GST_DEBUG("failed to parse GOP");
|
|
return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER;
|
|
}
|
|
}
|
|
else {
|
|
gop.closed = 1;
|
|
gop.broken_link = 0;
|
|
gop.hours = 0;
|
|
gop.minutes = 0;
|
|
gop.seconds = 0;
|
|
}
|
|
|
|
priv->closed_gop = gop.closed;
|
|
priv->broken_link = gop.broken_link;
|
|
|
|
GST_DEBUG("GOP %02u:%02u:%02u (closed_gop %d, broken_link %d)",
|
|
gop.hours, gop.minutes, gop.seconds,
|
|
priv->closed_gop, priv->broken_link);
|
|
|
|
gop_time = gop.hours * 3600 + gop.minutes * 60 + gop.seconds;
|
|
priv->last_sync_time = gop_time;
|
|
priv->sync_time = gop_time;
|
|
|
|
if (priv->gop_pts != GST_CLOCK_TIME_NONE)
|
|
priv->pts_diff += gop_time * GST_SECOND - priv->gop_pts;
|
|
priv->gop_pts = gop_time * GST_SECOND;
|
|
priv->calculate_pts_diff = TRUE;
|
|
priv->is_first_field = TRUE;
|
|
|
|
return GST_VAAPI_DECODER_STATUS_SUCCESS;
|
|
}
|
|
|
|
void
|
|
calculate_pts_diff(GstVaapiDecoderMpeg4 *decoder,
|
|
GstMpeg4VideoObjectLayer *vol_hdr,
|
|
GstMpeg4VideoObjectPlane *vop_hdr)
|
|
{
|
|
GstVaapiDecoderMpeg4Private * const priv = decoder->priv;
|
|
GstClockTime frame_timestamp;
|
|
|
|
frame_timestamp = GST_VAAPI_DECODER_CODEC_FRAME(decoder)->pts;
|
|
if (frame_timestamp && frame_timestamp != GST_CLOCK_TIME_NONE) {
|
|
/* Buffer with timestamp */
|
|
if (priv->max_pts != GST_CLOCK_TIME_NONE &&
|
|
frame_timestamp < priv->max_pts) {
|
|
frame_timestamp = priv->max_pts +
|
|
gst_util_uint64_scale((vol_hdr->fixed_vop_rate ?
|
|
vol_hdr->fixed_vop_time_increment : 1),
|
|
GST_SECOND,
|
|
vol_hdr->vop_time_increment_resolution);
|
|
}
|
|
} else {
|
|
/* Buffer without timestamp set */
|
|
if (priv->max_pts == GST_CLOCK_TIME_NONE) /* first buffer */
|
|
frame_timestamp = 0;
|
|
else {
|
|
GstClockTime tmp_pts;
|
|
tmp_pts = priv->pts_diff + priv->gop_pts +
|
|
vop_hdr->modulo_time_base * GST_SECOND +
|
|
gst_util_uint64_scale(vop_hdr->time_increment,
|
|
GST_SECOND,
|
|
vol_hdr->vop_time_increment_resolution);
|
|
if (tmp_pts > priv->max_pts)
|
|
frame_timestamp = tmp_pts;
|
|
else
|
|
frame_timestamp = priv->max_pts +
|
|
gst_util_uint64_scale((vol_hdr->fixed_vop_rate ?
|
|
vol_hdr->fixed_vop_time_increment : 1),
|
|
GST_SECOND,
|
|
vol_hdr->vop_time_increment_resolution);
|
|
}
|
|
}
|
|
|
|
priv->pts_diff = frame_timestamp -
|
|
(priv->gop_pts + vop_hdr->modulo_time_base * GST_SECOND +
|
|
gst_util_uint64_scale(vop_hdr->time_increment, GST_SECOND,
|
|
vol_hdr->vop_time_increment_resolution));
|
|
}
|
|
|
|
static GstVaapiDecoderStatus
|
|
decode_picture(GstVaapiDecoderMpeg4 *decoder, const guint8 *buf, guint buf_size)
|
|
{
|
|
GstMpeg4ParseResult parser_result = GST_MPEG4_PARSER_OK;
|
|
GstVaapiDecoderMpeg4Private * const priv = decoder->priv;
|
|
GstMpeg4VideoObjectPlane * const vop_hdr = &priv->vop_hdr;
|
|
GstMpeg4VideoObjectLayer * const vol_hdr = &priv->vol_hdr;
|
|
GstMpeg4SpriteTrajectory * const sprite_trajectory = &priv->sprite_trajectory;
|
|
GstVaapiPicture *picture;
|
|
GstVaapiDecoderStatus status;
|
|
GstClockTime pts;
|
|
|
|
// context depends on priv->width and priv->height, so we move parse_vop a little earlier
|
|
if (priv->is_svh) {
|
|
parser_result = gst_mpeg4_parse_video_plane_short_header(&priv->svh_hdr, buf, buf_size);
|
|
|
|
}
|
|
else {
|
|
parser_result = gst_mpeg4_parse_video_object_plane(vop_hdr, sprite_trajectory, vol_hdr, buf, buf_size);
|
|
/* Need to skip this frame if VOP was not coded */
|
|
if (GST_MPEG4_PARSER_OK == parser_result && !vop_hdr->coded)
|
|
return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA;
|
|
}
|
|
|
|
if (parser_result != GST_MPEG4_PARSER_OK) {
|
|
GST_DEBUG("failed to parse picture header");
|
|
return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER;
|
|
}
|
|
|
|
if (priv->is_svh) {
|
|
priv->width = priv->svh_hdr.vop_width;
|
|
priv->height = priv->svh_hdr.vop_height;
|
|
}
|
|
else {
|
|
if (!vop_hdr->width && !vop_hdr->height) {
|
|
vop_hdr->width = vol_hdr->width;
|
|
vop_hdr->height = vol_hdr->height;
|
|
}
|
|
priv->width = vop_hdr->width;
|
|
priv->height = vop_hdr->height;
|
|
}
|
|
|
|
status = ensure_context(decoder);
|
|
if (status != GST_VAAPI_DECODER_STATUS_SUCCESS) {
|
|
GST_DEBUG("failed to reset context");
|
|
return status;
|
|
}
|
|
|
|
if (priv->curr_picture) {
|
|
status = decode_current_picture(decoder);
|
|
if (status != GST_VAAPI_DECODER_STATUS_SUCCESS)
|
|
return status;
|
|
}
|
|
|
|
priv->curr_picture = GST_VAAPI_PICTURE_NEW(MPEG4, decoder);
|
|
if (!priv->curr_picture) {
|
|
GST_DEBUG("failed to allocate picture");
|
|
return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED;
|
|
}
|
|
picture = priv->curr_picture;
|
|
|
|
status = ensure_quant_matrix(decoder, picture);
|
|
if (status != GST_VAAPI_DECODER_STATUS_SUCCESS) {
|
|
GST_DEBUG("failed to reset quantizer matrix");
|
|
return status;
|
|
}
|
|
|
|
/* 7.6.7 Temporal prediction structure
|
|
* forward reference frame B B B B B B backward reference frame
|
|
* | |
|
|
* nearest I/P/S in the past with vop_coded ==1 |
|
|
* nearest I/P/S in the future with any vop_coded
|
|
* fixme, it said that B frame shouldn't use backward reference frame
|
|
* when backward reference frame coded is 0
|
|
*/
|
|
if (priv->is_svh) {
|
|
priv->coding_type = priv->svh_hdr.picture_coding_type;
|
|
}
|
|
else {
|
|
priv->coding_type = priv->vop_hdr.coding_type;
|
|
}
|
|
switch (priv->coding_type) {
|
|
case GST_MPEG4_I_VOP:
|
|
picture->type = GST_VAAPI_PICTURE_TYPE_I;
|
|
if (priv->is_svh || vop_hdr->coded)
|
|
GST_VAAPI_PICTURE_FLAG_SET(picture, GST_VAAPI_PICTURE_FLAG_REFERENCE);
|
|
break;
|
|
case GST_MPEG4_P_VOP:
|
|
picture->type = GST_VAAPI_PICTURE_TYPE_P;
|
|
if (priv->is_svh || vop_hdr->coded)
|
|
GST_VAAPI_PICTURE_FLAG_SET(picture, GST_VAAPI_PICTURE_FLAG_REFERENCE);
|
|
break;
|
|
case GST_MPEG4_B_VOP:
|
|
picture->type = GST_VAAPI_PICTURE_TYPE_B;
|
|
break;
|
|
case GST_MPEG4_S_VOP:
|
|
picture->type = GST_VAAPI_PICTURE_TYPE_S;
|
|
// see 3.175 reference VOP
|
|
if (vop_hdr->coded)
|
|
GST_VAAPI_PICTURE_FLAG_SET(picture, GST_VAAPI_PICTURE_FLAG_REFERENCE);
|
|
break;
|
|
default:
|
|
GST_DEBUG("unsupported picture type %d", priv->coding_type);
|
|
return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN;
|
|
}
|
|
|
|
if (!priv->is_svh && !vop_hdr->coded) {
|
|
status = render_picture(decoder, priv->prev_picture);
|
|
return status;
|
|
}
|
|
|
|
if (priv->is_svh) {
|
|
guint temp_ref = priv->svh_hdr.temporal_reference;
|
|
if (temp_ref < priv->prev_t_ref) {
|
|
temp_ref += 256;
|
|
}
|
|
guint delta_ref = temp_ref - priv->prev_t_ref;
|
|
|
|
pts = priv->sync_time;
|
|
// see temporal_reference definition in spec, 30000/1001Hz
|
|
pts += gst_util_uint64_scale(delta_ref, GST_SECOND*1001, 30000);
|
|
priv->sync_time = pts;
|
|
priv->prev_t_ref = priv->svh_hdr.temporal_reference;
|
|
}
|
|
else {
|
|
/* Update priv->pts_diff */
|
|
if (priv->calculate_pts_diff) {
|
|
calculate_pts_diff(decoder, vol_hdr, vop_hdr);
|
|
priv->calculate_pts_diff = FALSE;
|
|
}
|
|
|
|
/* Update presentation time, 6.3.5 */
|
|
if(vop_hdr->coding_type != GST_MPEG4_B_VOP) {
|
|
// increment basing on decoding order
|
|
priv->last_sync_time = priv->sync_time;
|
|
priv->sync_time = priv->last_sync_time + vop_hdr->modulo_time_base;
|
|
pts = priv->sync_time * GST_SECOND;
|
|
pts += gst_util_uint64_scale(vop_hdr->time_increment, GST_SECOND, vol_hdr->vop_time_increment_resolution);
|
|
priv->last_non_b_scale_time = priv->non_b_scale_time;
|
|
priv->non_b_scale_time = priv->sync_time * vol_hdr->vop_time_increment_resolution + vop_hdr->time_increment;
|
|
priv->trd = priv->non_b_scale_time - priv->last_non_b_scale_time;
|
|
}
|
|
else {
|
|
// increment basing on display oder
|
|
pts = (priv->last_sync_time + vop_hdr->modulo_time_base) * GST_SECOND;
|
|
pts += gst_util_uint64_scale(vop_hdr->time_increment, GST_SECOND, vol_hdr->vop_time_increment_resolution);
|
|
priv->trb = (priv->last_sync_time + vop_hdr->modulo_time_base) * vol_hdr->vop_time_increment_resolution +
|
|
vop_hdr->time_increment - priv->last_non_b_scale_time;
|
|
}
|
|
}
|
|
picture->pts = pts + priv->pts_diff;
|
|
if (priv->max_pts == GST_CLOCK_TIME_NONE || priv->max_pts < picture->pts)
|
|
priv->max_pts = picture->pts;
|
|
|
|
/* Update reference pictures */
|
|
/* XXX: consider priv->vol_hdr.low_delay, consider packed video frames for DivX/XviD */
|
|
if (GST_VAAPI_PICTURE_IS_REFERENCE(picture)) {
|
|
if (priv->next_picture)
|
|
status = render_picture(decoder, priv->next_picture);
|
|
gst_vaapi_picture_replace(&priv->prev_picture, priv->next_picture);
|
|
gst_vaapi_picture_replace(&priv->next_picture, picture);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
static inline guint
|
|
get_vop_coding_type(GstVaapiPicture *picture)
|
|
{
|
|
return picture->type - GST_VAAPI_PICTURE_TYPE_I;
|
|
}
|
|
|
|
static gboolean
|
|
fill_picture(GstVaapiDecoderMpeg4 *decoder, GstVaapiPicture *picture)
|
|
{
|
|
GstVaapiDecoderMpeg4Private * const priv = decoder->priv;
|
|
VAPictureParameterBufferMPEG4 * const pic_param = picture->param;
|
|
GstMpeg4VideoObjectPlane * const vop_hdr = &priv->vop_hdr;
|
|
|
|
/* Fill in VAPictureParameterBufferMPEG4 */
|
|
pic_param->forward_reference_picture = VA_INVALID_ID;
|
|
pic_param->backward_reference_picture = VA_INVALID_ID;
|
|
|
|
pic_param->vol_fields.value = 0;
|
|
pic_param->vop_fields.value = 0;
|
|
if(priv->is_svh) {
|
|
// vol_hdr Parameters
|
|
pic_param->vol_fields.bits.short_video_header = 1;
|
|
// does the following vol_hdr parameters matter for short video header?
|
|
pic_param->vol_fields.bits.chroma_format = 1; // I420, see table 6-15.
|
|
pic_param->vol_fields.bits.interlaced = 0;
|
|
pic_param->vol_fields.bits.obmc_disable = 1;
|
|
pic_param->vol_fields.bits.sprite_enable = 0;
|
|
pic_param->vol_fields.bits.sprite_warping_accuracy = 0;
|
|
pic_param->vol_fields.bits.quant_type = 0; //method 1; $7.4.4
|
|
pic_param->vol_fields.bits.quarter_sample = 0;
|
|
pic_param->vol_fields.bits.data_partitioned = 0;
|
|
pic_param->vol_fields.bits.reversible_vlc = 0;
|
|
pic_param->vol_fields.bits.resync_marker_disable = 1;
|
|
pic_param->no_of_sprite_warping_points = 0;
|
|
pic_param->quant_precision = 5;
|
|
// VOP parameters
|
|
pic_param->vop_width = priv->svh_hdr.vop_width;
|
|
pic_param->vop_height = priv->svh_hdr.vop_height;
|
|
pic_param->vop_fields.bits.vop_coding_type = priv->svh_hdr.picture_coding_type;
|
|
pic_param->vop_time_increment_resolution = priv->vol_hdr.vop_time_increment_resolution;
|
|
|
|
pic_param->num_gobs_in_vop = priv->svh_hdr.num_gobs_in_vop;
|
|
pic_param->num_macroblocks_in_gob = priv->svh_hdr.num_macroblocks_in_gob;
|
|
}
|
|
else {
|
|
// VOL parameters
|
|
pic_param->vol_fields.bits.short_video_header = 0;
|
|
pic_param->vol_fields.bits.chroma_format = priv->vol_hdr.chroma_format;
|
|
pic_param->vol_fields.bits.interlaced = priv->vol_hdr.interlaced;
|
|
pic_param->vol_fields.bits.obmc_disable = priv->vol_hdr.obmc_disable;
|
|
pic_param->vol_fields.bits.sprite_enable = priv->vol_hdr.sprite_enable;
|
|
pic_param->vol_fields.bits.sprite_warping_accuracy = priv->vol_hdr.sprite_warping_accuracy;
|
|
pic_param->vol_fields.bits.quant_type = priv->vol_hdr.quant_type;
|
|
pic_param->vol_fields.bits.quarter_sample = priv->vol_hdr.quarter_sample;
|
|
pic_param->vol_fields.bits.data_partitioned = priv->vol_hdr.data_partitioned;
|
|
pic_param->vol_fields.bits.reversible_vlc = priv->vol_hdr.reversible_vlc;
|
|
pic_param->vol_fields.bits.resync_marker_disable = priv->vol_hdr.resync_marker_disable;
|
|
pic_param->no_of_sprite_warping_points = priv->vol_hdr.no_of_sprite_warping_points;
|
|
int i =0;
|
|
for (i=0; i<3 && i<priv->vol_hdr.no_of_sprite_warping_points ; i++) {
|
|
pic_param->sprite_trajectory_du[i] = priv->sprite_trajectory.vop_ref_points[i];
|
|
pic_param->sprite_trajectory_dv[i] = priv->sprite_trajectory.sprite_ref_points[i];
|
|
}
|
|
pic_param->quant_precision = priv->vol_hdr.quant_precision;
|
|
|
|
// VOP parameters
|
|
pic_param->vop_width = vop_hdr->width;
|
|
pic_param->vop_height = vop_hdr->height;
|
|
pic_param->vop_fields.bits.vop_coding_type = vop_hdr->coding_type;
|
|
pic_param->vop_fields.bits.vop_rounding_type = vop_hdr->rounding_type;
|
|
pic_param->vop_fields.bits.intra_dc_vlc_thr = vop_hdr->intra_dc_vlc_thr;
|
|
pic_param->vop_fields.bits.top_field_first = vop_hdr->top_field_first;
|
|
pic_param->vop_fields.bits.alternate_vertical_scan_flag = vop_hdr->alternate_vertical_scan_flag;
|
|
|
|
pic_param->vop_fcode_forward = vop_hdr->fcode_forward;
|
|
pic_param->vop_fcode_backward = vop_hdr->fcode_backward;
|
|
pic_param->vop_time_increment_resolution = priv->vol_hdr.vop_time_increment_resolution;
|
|
}
|
|
|
|
pic_param->TRB = 0;
|
|
pic_param->TRD = 0;
|
|
switch (priv->coding_type) {
|
|
case GST_MPEG4_B_VOP:
|
|
pic_param->TRB = priv->trb;
|
|
pic_param->backward_reference_picture = priv->next_picture->surface_id;
|
|
pic_param->vop_fields.bits.backward_reference_vop_coding_type = get_vop_coding_type(priv->next_picture);
|
|
// fall-through
|
|
case GST_MPEG4_P_VOP:
|
|
pic_param->TRD = priv->trd;
|
|
if (priv->prev_picture)
|
|
pic_param->forward_reference_picture = priv->prev_picture->surface_id;
|
|
break;
|
|
}
|
|
|
|
if (priv->vol_hdr.interlaced) {
|
|
priv->is_first_field ^= 1;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static GstVaapiDecoderStatus
|
|
decode_slice(
|
|
GstVaapiDecoderMpeg4 *decoder,
|
|
const guint8 *buf,
|
|
guint buf_size,
|
|
gboolean has_packet_header
|
|
)
|
|
{
|
|
GstVaapiDecoderMpeg4Private * const priv = decoder->priv;
|
|
GstVaapiPicture * const picture = priv->curr_picture;
|
|
GstVaapiSlice *slice;
|
|
VASliceParameterBufferMPEG4 *slice_param;
|
|
|
|
GST_DEBUG("decoder silce: %p, %u bytes)", buf, buf_size);
|
|
|
|
// has_packet_header is ture for the 2+ slice
|
|
if (!has_packet_header && !fill_picture(decoder, picture))
|
|
return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN;
|
|
|
|
slice = GST_VAAPI_SLICE_NEW(MPEG4, decoder, buf, buf_size);
|
|
if (!slice) {
|
|
GST_DEBUG("failed to allocate slice");
|
|
return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED;
|
|
}
|
|
gst_vaapi_picture_add_slice(picture, slice);
|
|
|
|
/* Fill in VASliceParameterBufferMPEG4 */
|
|
slice_param = slice->param;
|
|
if (priv->is_svh) {
|
|
slice_param->macroblock_offset = (priv->svh_hdr.size)%8;
|
|
slice_param->macroblock_number = 0;
|
|
// the header of first gob_layer is empty (gob_header_empty=1), use vop_quant
|
|
slice_param->quant_scale = priv->svh_hdr.vop_quant;
|
|
}
|
|
else {
|
|
if (has_packet_header) {
|
|
slice_param->macroblock_offset = priv->packet_hdr.size % 8;
|
|
slice_param->macroblock_number = priv->packet_hdr.macroblock_number;
|
|
slice_param->quant_scale = priv->packet_hdr.quant_scale;
|
|
}
|
|
else {
|
|
slice_param->macroblock_offset = priv->vop_hdr.size % 8;
|
|
slice_param->macroblock_number = 0;
|
|
slice_param->quant_scale = priv->vop_hdr.quant;
|
|
}
|
|
}
|
|
return GST_VAAPI_DECODER_STATUS_SUCCESS;
|
|
}
|
|
|
|
static GstVaapiDecoderStatus
|
|
decode_packet(GstVaapiDecoderMpeg4 *decoder, GstMpeg4Packet packet)
|
|
{
|
|
GstVaapiDecoderMpeg4Private * const priv = decoder->priv;
|
|
GstMpeg4Packet *tos = &packet;
|
|
GstVaapiDecoderStatus status;
|
|
|
|
if (tos->size < 0)
|
|
return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA;
|
|
|
|
// packet.size is the size from current marker to the next.
|
|
if (tos->type == GST_MPEG4_VISUAL_OBJ_SEQ_START) {
|
|
status = decode_sequence(decoder, packet.data + packet.offset, packet.size);
|
|
}
|
|
else if (tos->type == GST_MPEG4_VISUAL_OBJ_SEQ_END) {
|
|
status = decode_sequence_end(decoder);
|
|
}
|
|
else if (tos->type == GST_MPEG4_VISUAL_OBJ) {
|
|
status = decode_visual_object(decoder, packet.data + packet.offset, packet.size);
|
|
}
|
|
else if (tos->type >= GST_MPEG4_VIDEO_OBJ_FIRST && tos->type <= GST_MPEG4_VIDEO_OBJ_LAST) {
|
|
GST_WARNING("unexpected marker: (GST_MPEG4_VIDEO_OBJ_FIRST, GST_MPEG4_VIDEO_OBJ_LAST)");
|
|
status = GST_VAAPI_DECODER_STATUS_SUCCESS;
|
|
}
|
|
else if (tos->type >= GST_MPEG4_VIDEO_LAYER_FIRST && tos->type <= GST_MPEG4_VIDEO_LAYER_LAST) {
|
|
status = decode_video_object_layer(decoder, packet.data + packet.offset, packet.size);
|
|
}
|
|
else if (tos->type == GST_MPEG4_GROUP_OF_VOP) {
|
|
status = decode_gop(decoder, packet.data + packet.offset, packet.size);
|
|
}
|
|
else if (tos->type == GST_MPEG4_VIDEO_OBJ_PLANE) {
|
|
status = decode_picture(decoder, packet.data + packet.offset, packet.size);
|
|
if (status != GST_VAAPI_DECODER_STATUS_SUCCESS)
|
|
return status;
|
|
|
|
/* decode slice
|
|
* A resync marker shall only be located immediately before a macroblock
|
|
* (or video packet header if exists) and aligned with a byte
|
|
* either start_code or resync_marker are scaned/measured by byte,
|
|
* while the header itself are parsed/measured in bit
|
|
* it means: resync_marker(video_packet_header) start from byte boundary,
|
|
* while MB doesn't start from byte boundary -- it is what 'macroblock_offset'
|
|
* in slice refer to
|
|
*/
|
|
const guint8 *_data = packet.data + packet.offset + priv->vop_hdr.size/8;
|
|
gint _data_size = packet.size - (priv->vop_hdr.size/8);
|
|
GstMpeg4Packet video_packet;
|
|
|
|
if (priv->vol_hdr.resync_marker_disable) {
|
|
status = decode_slice(decoder, _data, _data_size, FALSE);
|
|
if (status != GST_VAAPI_DECODER_STATUS_SUCCESS)
|
|
return status;
|
|
}
|
|
else {
|
|
// next start_code is required to determine the end of last slice
|
|
_data_size += 4;
|
|
GstMpeg4ParseResult ret = GST_MPEG4_PARSER_OK;
|
|
|
|
gboolean first_slice = TRUE;
|
|
while (_data_size > 0) {
|
|
// we can skip user data here
|
|
ret = gst_mpeg4_parse(&video_packet, TRUE, &priv->vop_hdr, _data, 0, _data_size);
|
|
if(ret != GST_MPEG4_PARSER_OK) {
|
|
break;
|
|
}
|
|
|
|
if (first_slice) {
|
|
status = decode_slice(decoder, _data, video_packet.size, FALSE);
|
|
first_slice = FALSE;
|
|
}
|
|
else {
|
|
_data += video_packet.offset;
|
|
_data_size -= video_packet.offset;
|
|
|
|
ret = gst_mpeg4_parse_video_packet_header (&priv->packet_hdr, &priv->vol_hdr, &priv->vop_hdr, &priv->sprite_trajectory, _data, _data_size);
|
|
status = decode_slice(decoder,_data + priv->packet_hdr.size/8, video_packet.size - priv->packet_hdr.size/8, TRUE);
|
|
}
|
|
|
|
_data += video_packet.size;
|
|
_data_size -= video_packet.size;
|
|
}
|
|
}
|
|
status = decode_current_picture(decoder);
|
|
}
|
|
else if (tos->type == GST_MPEG4_USER_DATA
|
|
|| tos->type == GST_MPEG4_VIDEO_SESSION_ERR
|
|
|| tos->type == GST_MPEG4_FBA
|
|
|| tos->type == GST_MPEG4_FBA_PLAN
|
|
|| tos->type == GST_MPEG4_MESH
|
|
|| tos->type == GST_MPEG4_MESH_PLAN
|
|
|| tos->type == GST_MPEG4_STILL_TEXTURE_OBJ
|
|
|| tos->type == GST_MPEG4_TEXTURE_SPATIAL
|
|
|| tos->type == GST_MPEG4_TEXTURE_SNR_LAYER
|
|
|| tos->type == GST_MPEG4_TEXTURE_TILE
|
|
|| tos->type == GST_MPEG4_SHAPE_LAYER
|
|
|| tos->type == GST_MPEG4_STUFFING
|
|
|| tos->type == GST_MPEG4_SYSTEM_FIRST
|
|
|| tos->type == GST_MPEG4_SYSTEM_LAST) {
|
|
GST_WARNING("Ignore marker: %x\n", tos->type);
|
|
status = GST_VAAPI_DECODER_STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
GST_ERROR("unsupported start code %x\n", tos->type);
|
|
status = GST_VAAPI_DECODER_STATUS_SUCCESS;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
static GstVaapiDecoderStatus
|
|
decode_buffer(GstVaapiDecoderMpeg4 *decoder, GstBuffer *buffer)
|
|
{
|
|
GstVaapiDecoderMpeg4Private * const priv = decoder->priv;
|
|
GstVaapiDecoderStatus status;
|
|
GstMpeg4Packet packet;
|
|
const guchar *buf;
|
|
guint buf_size, ofs;
|
|
|
|
buf = GST_BUFFER_DATA(buffer);
|
|
buf_size = GST_BUFFER_SIZE(buffer);
|
|
|
|
if (priv->is_svh) {
|
|
status = decode_picture(decoder, buf, buf_size);
|
|
if (status != GST_VAAPI_DECODER_STATUS_SUCCESS)
|
|
return status;
|
|
|
|
ofs = priv->svh_hdr.size / 8;
|
|
status = decode_slice(decoder, buf + ofs, buf_size - ofs, FALSE);
|
|
if (status != GST_VAAPI_DECODER_STATUS_SUCCESS)
|
|
return status;
|
|
}
|
|
else {
|
|
packet.data = buf;
|
|
packet.offset = 0;
|
|
packet.size = buf_size;
|
|
packet.type = (GstMpeg4StartCode)packet.data[0];
|
|
|
|
status = decode_packet(decoder, packet);
|
|
if (status != GST_VAAPI_DECODER_STATUS_SUCCESS)
|
|
return status;
|
|
}
|
|
return GST_VAAPI_DECODER_STATUS_SUCCESS;
|
|
}
|
|
|
|
static GstVaapiDecoderStatus
|
|
decode_codec_data(GstVaapiDecoderMpeg4 *decoder, GstBuffer *buffer)
|
|
{
|
|
GstVaapiDecoderStatus status;
|
|
guchar *buf, *_buf;
|
|
guint pos, buf_size, _buf_size;
|
|
|
|
_buf = GST_BUFFER_DATA(buffer);
|
|
_buf_size = GST_BUFFER_SIZE(buffer);
|
|
// add additional 0x000001b2 to enclose the last header
|
|
buf_size = _buf_size + 4;
|
|
buf = malloc(buf_size);
|
|
memcpy(buf, _buf, buf_size);
|
|
buf[buf_size-4] = 0;
|
|
buf[buf_size-3] = 0;
|
|
buf[buf_size-2] = 1;
|
|
buf[buf_size-1] = 0xb2;
|
|
|
|
pos = 0;
|
|
GstMpeg4Packet packet;
|
|
GstMpeg4ParseResult result = GST_MPEG4_PARSER_OK;
|
|
|
|
while (result == GST_MPEG4_PARSER_OK && pos < buf_size) {
|
|
result = gst_mpeg4_parse(&packet, FALSE, NULL, buf, pos, buf_size);
|
|
if (result != GST_MPEG4_PARSER_OK) {
|
|
break;
|
|
}
|
|
status = decode_packet(decoder, packet);
|
|
if (GST_VAAPI_DECODER_STATUS_SUCCESS == status) {
|
|
pos = packet.offset + packet.size;
|
|
}
|
|
else {
|
|
GST_WARNING("decode mp4 packet failed when decoding codec data\n");
|
|
break;
|
|
}
|
|
}
|
|
free(buf);
|
|
return status;
|
|
}
|
|
|
|
static GstVaapiDecoderStatus
|
|
ensure_decoder(GstVaapiDecoderMpeg4 *decoder)
|
|
{
|
|
GstVaapiDecoderMpeg4Private * const priv = decoder->priv;
|
|
GstVaapiDecoderStatus status;
|
|
GstBuffer *codec_data;
|
|
|
|
g_return_val_if_fail(priv->is_constructed,
|
|
GST_VAAPI_DECODER_STATUS_ERROR_INIT_FAILED);
|
|
|
|
if (!priv->is_opened) {
|
|
priv->is_opened = gst_vaapi_decoder_mpeg4_open(decoder);
|
|
if (!priv->is_opened)
|
|
return GST_VAAPI_DECODER_STATUS_ERROR_UNSUPPORTED_CODEC;
|
|
|
|
codec_data = GST_VAAPI_DECODER_CODEC_DATA(decoder);
|
|
if (codec_data) {
|
|
status = decode_codec_data(decoder, codec_data);
|
|
if (status != GST_VAAPI_DECODER_STATUS_SUCCESS)
|
|
return status;
|
|
}
|
|
}
|
|
return GST_VAAPI_DECODER_STATUS_SUCCESS;
|
|
}
|
|
|
|
static GstVaapiDecoderStatus
|
|
gst_vaapi_decoder_mpeg4_parse(GstVaapiDecoder *base_decoder,
|
|
GstAdapter *adapter, gboolean at_eos, GstVaapiDecoderUnit *unit)
|
|
{
|
|
GstVaapiDecoderMpeg4 * const decoder =
|
|
GST_VAAPI_DECODER_MPEG4(base_decoder);
|
|
GstVaapiDecoderMpeg4Private * const priv = decoder->priv;
|
|
GstVaapiDecoderStatus status;
|
|
GstMpeg4Packet packet;
|
|
GstMpeg4ParseResult result;
|
|
const guchar *buf;
|
|
guint size, buf_size, flags = 0;
|
|
|
|
status = ensure_decoder(decoder);
|
|
if (status != GST_VAAPI_DECODER_STATUS_SUCCESS)
|
|
return status;
|
|
|
|
size = gst_adapter_available(adapter);
|
|
buf = gst_adapter_peek(adapter, size);
|
|
if (!buf)
|
|
return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA;
|
|
|
|
if (priv->is_svh)
|
|
result = gst_h263_parse(&packet, buf, 0, size);
|
|
else
|
|
result = gst_mpeg4_parse(&packet, FALSE, NULL, buf, 0, size);
|
|
if (result == GST_MPEG4_PARSER_NO_PACKET_END && at_eos)
|
|
packet.size = size - packet.offset;
|
|
else if (result == GST_MPEG4_PARSER_ERROR)
|
|
return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER;
|
|
else if (result != GST_MPEG4_PARSER_OK)
|
|
return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA;
|
|
|
|
buf_size = packet.size;
|
|
gst_adapter_flush(adapter, packet.offset);
|
|
unit->size = buf_size;
|
|
|
|
/* Check for start of new picture */
|
|
switch (packet.type) {
|
|
case GST_MPEG4_VIDEO_SESSION_ERR:
|
|
case GST_MPEG4_FBA:
|
|
case GST_MPEG4_FBA_PLAN:
|
|
case GST_MPEG4_MESH:
|
|
case GST_MPEG4_MESH_PLAN:
|
|
case GST_MPEG4_STILL_TEXTURE_OBJ:
|
|
case GST_MPEG4_TEXTURE_SPATIAL:
|
|
case GST_MPEG4_TEXTURE_SNR_LAYER:
|
|
case GST_MPEG4_TEXTURE_TILE:
|
|
case GST_MPEG4_SHAPE_LAYER:
|
|
case GST_MPEG4_STUFFING:
|
|
gst_adapter_flush(adapter, packet.size);
|
|
return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA;
|
|
case GST_MPEG4_USER_DATA:
|
|
flags |= GST_VAAPI_DECODER_UNIT_FLAG_SKIP;
|
|
break;
|
|
case GST_MPEG4_VISUAL_OBJ_SEQ_END:
|
|
flags |= GST_VAAPI_DECODER_UNIT_FLAG_FRAME_END;
|
|
flags |= GST_VAAPI_DECODER_UNIT_FLAG_STREAM_END;
|
|
break;
|
|
case GST_MPEG4_VIDEO_OBJ_PLANE:
|
|
flags |= GST_VAAPI_DECODER_UNIT_FLAG_SLICE;
|
|
flags |= GST_VAAPI_DECODER_UNIT_FLAG_FRAME_END;
|
|
/* fall-through */
|
|
case GST_MPEG4_VISUAL_OBJ_SEQ_START:
|
|
case GST_MPEG4_VISUAL_OBJ:
|
|
case GST_MPEG4_GROUP_OF_VOP:
|
|
flags |= GST_VAAPI_DECODER_UNIT_FLAG_FRAME_START;
|
|
break;
|
|
default:
|
|
if (packet.type >= GST_MPEG4_VIDEO_OBJ_FIRST &&
|
|
packet.type <= GST_MPEG4_VIDEO_OBJ_LAST) {
|
|
gst_adapter_flush(adapter, packet.size);
|
|
return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA;
|
|
}
|
|
if (packet.type >= GST_MPEG4_VIDEO_LAYER_FIRST &&
|
|
packet.type <= GST_MPEG4_VIDEO_LAYER_LAST) {
|
|
break;
|
|
}
|
|
if (packet.type >= GST_MPEG4_SYSTEM_FIRST &&
|
|
packet.type <= GST_MPEG4_SYSTEM_LAST) {
|
|
flags |= GST_VAAPI_DECODER_UNIT_FLAG_SKIP;
|
|
break;
|
|
}
|
|
GST_WARNING("unsupported start code (0x%02x)", packet.type);
|
|
return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER;
|
|
}
|
|
GST_VAAPI_DECODER_UNIT_FLAG_SET(unit, flags);
|
|
return GST_VAAPI_DECODER_STATUS_SUCCESS;
|
|
}
|
|
|
|
static GstVaapiDecoderStatus
|
|
gst_vaapi_decoder_mpeg4_decode(GstVaapiDecoder *base_decoder,
|
|
GstVaapiDecoderUnit *unit)
|
|
{
|
|
GstVaapiDecoderMpeg4 * const decoder =
|
|
GST_VAAPI_DECODER_MPEG4(base_decoder);
|
|
GstVaapiDecoderStatus status;
|
|
|
|
status = ensure_decoder(decoder);
|
|
if (status != GST_VAAPI_DECODER_STATUS_SUCCESS)
|
|
return status;
|
|
|
|
unit->buffer = gst_buffer_create_sub(
|
|
GST_VAAPI_DECODER_CODEC_FRAME(decoder)->input_buffer,
|
|
unit->offset, unit->size);
|
|
if (!unit->buffer)
|
|
return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED;
|
|
|
|
status = decode_buffer(decoder, unit->buffer);
|
|
if (status != GST_VAAPI_DECODER_STATUS_SUCCESS)
|
|
return status;
|
|
return GST_VAAPI_DECODER_STATUS_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
gst_vaapi_decoder_mpeg4_finalize(GObject *object)
|
|
{
|
|
GstVaapiDecoderMpeg4 * const decoder = GST_VAAPI_DECODER_MPEG4(object);
|
|
|
|
gst_vaapi_decoder_mpeg4_destroy(decoder);
|
|
|
|
G_OBJECT_CLASS(gst_vaapi_decoder_mpeg4_parent_class)->finalize(object);
|
|
}
|
|
|
|
static void
|
|
gst_vaapi_decoder_mpeg4_constructed(GObject *object)
|
|
{
|
|
GstVaapiDecoderMpeg4 * const decoder = GST_VAAPI_DECODER_MPEG4(object);
|
|
GstVaapiDecoderMpeg4Private * const priv = decoder->priv;
|
|
GObjectClass *parent_class;
|
|
|
|
parent_class = G_OBJECT_CLASS(gst_vaapi_decoder_mpeg4_parent_class);
|
|
if (parent_class->constructed)
|
|
parent_class->constructed(object);
|
|
|
|
priv->is_constructed = gst_vaapi_decoder_mpeg4_create(decoder);
|
|
}
|
|
|
|
static void
|
|
gst_vaapi_decoder_mpeg4_class_init(GstVaapiDecoderMpeg4Class *klass)
|
|
{
|
|
GObjectClass * const object_class = G_OBJECT_CLASS(klass);
|
|
GstVaapiDecoderClass * const decoder_class = GST_VAAPI_DECODER_CLASS(klass);
|
|
|
|
g_type_class_add_private(klass, sizeof(GstVaapiDecoderMpeg4Private));
|
|
|
|
object_class->finalize = gst_vaapi_decoder_mpeg4_finalize;
|
|
object_class->constructed = gst_vaapi_decoder_mpeg4_constructed;
|
|
|
|
decoder_class->parse = gst_vaapi_decoder_mpeg4_parse;
|
|
decoder_class->decode = gst_vaapi_decoder_mpeg4_decode;
|
|
}
|
|
|
|
static void
|
|
gst_vaapi_decoder_mpeg4_init(GstVaapiDecoderMpeg4 *decoder)
|
|
{
|
|
GstVaapiDecoderMpeg4Private *priv;
|
|
|
|
priv = GST_VAAPI_DECODER_MPEG4_GET_PRIVATE(decoder);
|
|
decoder->priv = priv;
|
|
priv->width = 0;
|
|
priv->height = 0;
|
|
priv->fps_n = 0;
|
|
priv->fps_d = 0;
|
|
priv->profile = GST_VAAPI_PROFILE_MPEG4_SIMPLE;
|
|
priv->curr_picture = NULL;
|
|
priv->next_picture = NULL;
|
|
priv->prev_picture = NULL;
|
|
priv->seq_pts = GST_CLOCK_TIME_NONE;
|
|
priv->gop_pts = GST_CLOCK_TIME_NONE;
|
|
priv->max_pts = GST_CLOCK_TIME_NONE;
|
|
priv->pts_diff = 0;
|
|
priv->calculate_pts_diff = TRUE;
|
|
priv->is_constructed = FALSE;
|
|
priv->is_opened = FALSE;
|
|
priv->is_first_field = FALSE;
|
|
priv->size_changed = TRUE;
|
|
priv->profile_changed = TRUE;
|
|
priv->progressive_sequence = FALSE;
|
|
priv->closed_gop = FALSE;
|
|
priv->broken_link = FALSE;
|
|
priv->last_non_b_scale_time = 0;
|
|
priv->non_b_scale_time = 0;
|
|
priv->trb = 0;
|
|
priv->trd = 0;
|
|
}
|
|
|
|
/**
|
|
* gst_vaapi_decoder_mpeg4_new:
|
|
* @display: a #GstVaapiDisplay
|
|
* @caps: a #GstCaps holding codec information
|
|
*
|
|
* Creates a new #GstVaapiDecoder for MPEG-2 decoding. The @caps can
|
|
* hold extra information like codec-data and pictured coded size.
|
|
*
|
|
* Return value: the newly allocated #GstVaapiDecoder object
|
|
*/
|
|
GstVaapiDecoder *
|
|
gst_vaapi_decoder_mpeg4_new(GstVaapiDisplay *display, GstCaps *caps)
|
|
{
|
|
GstVaapiDecoderMpeg4 *decoder;
|
|
|
|
g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), NULL);
|
|
g_return_val_if_fail(GST_IS_CAPS(caps), NULL);
|
|
|
|
decoder = g_object_new(
|
|
GST_VAAPI_TYPE_DECODER_MPEG4,
|
|
"display", display,
|
|
"caps", caps,
|
|
NULL
|
|
);
|
|
if (!decoder->priv->is_constructed) {
|
|
g_object_unref(decoder);
|
|
return NULL;
|
|
}
|
|
return GST_VAAPI_DECODER_CAST(decoder);
|
|
}
|