codecs: Add mpeg2 stateless decoder base class.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/1798>
This commit is contained in:
He Junyan 2020-12-18 21:25:08 +08:00
parent e82f1f8b0b
commit 0e161dd363
5 changed files with 1807 additions and 0 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,179 @@
/* GStreamer
* Copyright (C) 2020 Intel Corporation
* Author: He Junyan <junyan.he@intel.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.
*/
#ifndef __GST_MPEG2_DECODER_H__
#define __GST_MPEG2_DECODER_H__
#include <gst/codecs/codecs-prelude.h>
#include <gst/video/video.h>
#include <gst/codecparsers/gstmpegvideoparser.h>
#include <gst/codecs/gstmpeg2picture.h>
G_BEGIN_DECLS
#define GST_TYPE_MPEG2_DECODER (gst_mpeg2_decoder_get_type())
#define GST_MPEG2_DECODER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MPEG2_DECODER,GstMpeg2Decoder))
#define GST_MPEG2_DECODER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_MPEG2_DECODER,GstMpeg2DecoderClass))
#define GST_MPEG2_DECODER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_MPEG2_DECODER,GstMpeg2DecoderClass))
#define GST_IS_MPEG2_DECODER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_MPEG2_DECODER))
#define GST_IS_MPEG2_DECODER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_MPEG2_DECODER))
#define GST_MPEG2_DECODER_CAST(obj) ((GstMpeg2Decoder*)obj)
typedef struct _GstMpeg2Decoder GstMpeg2Decoder;
typedef struct _GstMpeg2DecoderClass GstMpeg2DecoderClass;
typedef struct _GstMpeg2DecoderPrivate GstMpeg2DecoderPrivate;
/**
* GstMpeg2Decoder:
*
* The opaque #GstMpeg2Decoder data structure.
*/
struct _GstMpeg2Decoder
{
/*< private >*/
GstVideoDecoder parent;
/*< protected >*/
GstVideoCodecState * input_state;
/*< private >*/
GstMpeg2DecoderPrivate *priv;
gpointer padding[GST_PADDING_LARGE];
};
/**
* GstMpeg2DecoderClass:
*/
struct _GstMpeg2DecoderClass
{
GstVideoDecoderClass parent_class;
/**
* GstMpeg2DecoderClass::new_sequence:
* @decoder: a #GstMpeg2Decoder
* @seq: a #GstMpegVideoSequenceHdr
* @seq_ext: a #GstMpegVideoSequenceExt
*
* Notifies subclass of SPS update
*/
gboolean (*new_sequence) (GstMpeg2Decoder * decoder,
const GstMpegVideoSequenceHdr * seq,
const GstMpegVideoSequenceExt * seq_ext,
const GstMpegVideoSequenceDisplayExt * seq_display_ext,
const GstMpegVideoSequenceScalableExt * seq_scalable_ext);
/**
* GstMpeg2DecoderClass::new_picture:
* @decoder: a #GstMpeg2Decoder
* @frame: (transfer none): a #GstVideoCodecFrame
* @picture: (transfer none): a #GstMpeg2Picture
*
* Optional. Called whenever new #GstMpeg2Picture is created.
* Subclass can set implementation specific user data
* on the #GstMpeg2Picture via gst_mpeg2_picture_set_user_data()
*/
gboolean (*new_picture) (GstMpeg2Decoder * decoder,
GstVideoCodecFrame * frame,
GstMpeg2Picture * picture);
/**
* GstMpeg2DecoderClass::new_field_picture:
* @decoder: a #GstMpeg2Decoder
* @first_field: (transfer none): the first field #GstMpeg2Picture already decoded
* @second_field: (transfer none): a #GstMpeg2Picture for the second field
*
* Called when a new field picture is created for interlaced field picture.
* Subclass can attach implementation specific user data on @second_field via
* gst_mpeg2_picture_set_user_data()
*
* Since: 1.20
*/
gboolean (*new_field_picture) (GstMpeg2Decoder * decoder,
const GstMpeg2Picture * first_field,
GstMpeg2Picture * second_field);
/**
* GstMpeg2DecoderClass::start_picture:
* @decoder: a #GstMpeg2Decoder
* @picture: (transfer none): a #GstMpeg2Picture
* @slice: (transfer none): a #GstMpeg2Slice
* @prev_picture: (transfer none): a #GstMpeg2Picture
* @next_picture: (transfer none): a #GstMpeg2Picture
*
* Optional. Called per one #GstMpeg2Picture to notify subclass to prepare
* decoding process for the #GstMpeg2Picture
*/
gboolean (*start_picture) (GstMpeg2Decoder * decoder,
GstMpeg2Picture * picture,
GstMpeg2Slice * slice,
GstMpeg2Picture * prev_picture,
GstMpeg2Picture * next_picture);
/**
* GstMpeg2DecoderClass::decode_slice:
* @decoder: a #GstMpeg2Decoder
* @picture: (transfer none): a #GstMpeg2Picture
* @slice: (transfer none): a #GstMpeg2Slice
*
* Provides per slice data with parsed slice header and required raw bitstream
* for subclass to decode it.
*/
gboolean (*decode_slice) (GstMpeg2Decoder * decoder,
GstMpeg2Picture * picture,
GstMpeg2Slice * slice);
/**
* GstMpeg2DecoderClass::end_picture:
* @decoder: a #GstMpeg2Decoder
* @picture: (transfer none): a #GstMpeg2Picture
*
* Optional. Called per one #GstMpeg2Picture to notify subclass to finish
* decoding process for the #GstMpeg2Picture
*/
gboolean (*end_picture) (GstMpeg2Decoder * decoder,
GstMpeg2Picture * picture);
/**
* GstMpeg2DecoderClass::output_picture:
* @decoder: a #GstMpeg2Decoder
* @frame: (transfer full): a #GstVideoCodecFrame
* @picture: (transfer full): a #GstMpeg2Picture
*
* Called with a #GstMpeg2Picture which is required to be outputted.
* The #GstVideoCodecFrame must be consumed by subclass.
*/
GstFlowReturn (*output_picture) (GstMpeg2Decoder * decoder,
GstVideoCodecFrame * frame,
GstMpeg2Picture * picture);
/*< private >*/
gpointer padding[GST_PADDING_LARGE];
};
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstMpeg2Decoder, gst_object_unref)
GST_CODECS_API
GType gst_mpeg2_decoder_get_type (void);
G_END_DECLS
#endif /* __GST_VP8_DECODER_H__ */

View file

@ -0,0 +1,314 @@
/* GStreamer
* Copyright (C) 2020 Intel Corporation
* Author: He Junyan <junyan.he@intel.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.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "gstmpeg2picture.h"
GST_DEBUG_CATEGORY_EXTERN (gst_mpeg2_decoder_debug);
#define GST_CAT_DEFAULT gst_mpeg2_decoder_debug
GST_DEFINE_MINI_OBJECT_TYPE (GstMpeg2Picture, gst_mpeg2_picture);
static void
_gst_mpeg2_picture_free (GstMpeg2Picture * picture)
{
GST_TRACE ("Free picture %p", picture);
if (picture->first_field)
gst_mpeg2_picture_unref (picture->first_field);
if (picture->notify)
picture->notify (picture->user_data);
g_free (picture);
}
/**
* gst_mpeg2_picture_new:
*
* Create new #GstMpeg2Picture
*
* Returns: a new #GstMpeg2Picture
*/
GstMpeg2Picture *
gst_mpeg2_picture_new (void)
{
GstMpeg2Picture *pic;
pic = g_new0 (GstMpeg2Picture, 1);
pic->pic_order_cnt = G_MAXINT32;
pic->structure = GST_MPEG_VIDEO_PICTURE_STRUCTURE_FRAME;
gst_mini_object_init (GST_MINI_OBJECT_CAST (pic), 0,
GST_TYPE_MPEG2_PICTURE, NULL, NULL,
(GstMiniObjectFreeFunction) _gst_mpeg2_picture_free);
GST_TRACE ("New picture %p", pic);
return pic;
}
/**
* gst_mpeg2_picture_set_user_data:
* @picture: a #GstMpeg2Picture
* @user_data: private data
* @notify: (closure user_data): a #GDestroyNotify
*
* Sets @user_data on the picture and the #GDestroyNotify that will be called when
* the picture is freed.
*
* If a @user_data was previously set, then the previous set @notify will be called
* before the @user_data is replaced.
*/
void
gst_mpeg2_picture_set_user_data (GstMpeg2Picture * picture, gpointer user_data,
GDestroyNotify notify)
{
g_return_if_fail (GST_IS_MPEG2_PICTURE (picture));
if (picture->notify)
picture->notify (picture->user_data);
picture->user_data = user_data;
picture->notify = notify;
}
/**
* gst_mpeg2_picture_get_user_data:
* @picture: a #GstMpeg2Picture
*
* Gets private data set on the picture via
* gst_mpeg2_picture_set_user_data() previously.
*
* Returns: (transfer none): The previously set user_data
*/
gpointer
gst_mpeg2_picture_get_user_data (GstMpeg2Picture * picture)
{
return picture->user_data;
}
/*******************
* GstMpeg2Dpb *
*******************/
struct _GstMpeg2Dpb
{
GstMpeg2Picture *ref_pic_list[2];
guint num_ref_pictures;
/* last added picture */
GstMpeg2Picture *new_pic;
};
/**
* gst_mpeg2_dpb_new: (skip)
*
* Create new #GstMpeg2Dpb
*
* Returns: a new #GstMpeg2Dpb
*/
GstMpeg2Dpb *
gst_mpeg2_dpb_new (void)
{
return g_new0 (GstMpeg2Dpb, 1);
}
/**
* gst_mpeg2_dpb_free:
* @dpb: a #GstMpeg2Dpb to free
*
* Free the @dpb
*/
void
gst_mpeg2_dpb_free (GstMpeg2Dpb * dpb)
{
guint i;
g_return_if_fail (dpb != NULL);
gst_mpeg2_picture_clear (&dpb->new_pic);
g_assert (dpb->num_ref_pictures <= 2);
for (i = 0; i < dpb->num_ref_pictures; i++)
gst_mpeg2_picture_clear (&dpb->ref_pic_list[i]);
g_free (dpb);
}
/**
* gst_mpeg2_dpb_clear:
* @dpb: a #GstMpeg2Dpb
*
* Clear all stored #GstMpeg2Picture
*/
void
gst_mpeg2_dpb_clear (GstMpeg2Dpb * dpb)
{
guint i;
g_return_if_fail (dpb != NULL);
gst_mpeg2_picture_clear (&dpb->new_pic);
g_assert (dpb->num_ref_pictures <= 2);
for (i = 0; i < dpb->num_ref_pictures; i++)
gst_mpeg2_picture_clear (&dpb->ref_pic_list[i]);
dpb->num_ref_pictures = 0;
}
static void
_dpb_add_to_reference (GstMpeg2Dpb * dpb, GstMpeg2Picture * pic)
{
gint index = -1;
if (G_LIKELY (dpb->num_ref_pictures == 2)) {
index = (dpb->ref_pic_list[0]->pic_order_cnt >
dpb->ref_pic_list[1]->pic_order_cnt);
if (dpb->ref_pic_list[index]->pic_order_cnt > pic->pic_order_cnt)
return;
}
if (index < 0) {
index = dpb->num_ref_pictures;
dpb->num_ref_pictures++;
}
gst_mpeg2_picture_replace (&dpb->ref_pic_list[index], pic);
}
/**
* gst_mpeg2_dpb_add:
* @dpb: a #GstMpeg2Dpb
* @picture: (transfer full): a #GstMpeg2Picture
*
* Store the @picture
*/
void
gst_mpeg2_dpb_add (GstMpeg2Dpb * dpb, GstMpeg2Picture * picture)
{
g_return_if_fail (dpb != NULL);
g_return_if_fail (GST_IS_MPEG2_PICTURE (picture));
g_assert (dpb->num_ref_pictures <= 2);
if (!GST_MPEG2_PICTURE_IS_REF (picture) || dpb->num_ref_pictures == 2) {
gst_mpeg2_picture_replace (&dpb->new_pic, picture);
} else {
_dpb_add_to_reference (dpb, picture);
}
}
gboolean
gst_mpeg2_dpb_need_bump (GstMpeg2Dpb * dpb)
{
g_return_val_if_fail (dpb != NULL, FALSE);
g_assert (dpb->num_ref_pictures <= 2);
if (dpb->new_pic)
return TRUE;
return FALSE;
}
/**
* gst_mpeg2_dpb_bump:
* @dpb: a #GstMpeg2Dpb
*
* Returns: (nullable) (transfer full): a #GstMpeg2Picture which is needed to be
* outputted
*
* Since: 1.20
*/
GstMpeg2Picture *
gst_mpeg2_dpb_bump (GstMpeg2Dpb * dpb)
{
GstMpeg2Picture *pic = NULL;
guint i;
g_return_val_if_fail (dpb != NULL, FALSE);
g_assert (dpb->num_ref_pictures <= 2);
/* First, find the lowest poc. */
for (i = 0; i < 2; i++) {
if (!dpb->ref_pic_list[i])
continue;
if (dpb->ref_pic_list[i]->needed_for_output) {
if (!pic || pic->pic_order_cnt > dpb->ref_pic_list[i]->pic_order_cnt)
gst_mpeg2_picture_replace (&pic, dpb->ref_pic_list[i]);
}
}
if (dpb->new_pic && dpb->new_pic->needed_for_output &&
(!pic || pic->pic_order_cnt > dpb->new_pic->pic_order_cnt))
gst_mpeg2_picture_replace (&pic, dpb->new_pic);
/* Then, replace the reference if needed. */
if (dpb->new_pic && GST_MPEG2_PICTURE_IS_REF (dpb->new_pic)) {
_dpb_add_to_reference (dpb, dpb->new_pic);
gst_mpeg2_picture_clear (&dpb->new_pic);
}
if (pic) {
pic->needed_for_output = FALSE;
if (pic == dpb->new_pic)
gst_mpeg2_picture_clear (&dpb->new_pic);
}
return pic;
}
void
gst_mpeg2_dpb_get_neighbours (GstMpeg2Dpb * dpb,
GstMpeg2Picture * picture, GstMpeg2Picture ** prev_picture_ptr,
GstMpeg2Picture ** next_picture_ptr)
{
GstMpeg2Picture *ref_picture, *ref_pictures[2];
GstMpeg2Picture **picture_ptr;
guint i, index;
g_return_if_fail (dpb != NULL);
g_return_if_fail (picture != NULL);
g_assert (dpb->num_ref_pictures <= 2);
ref_pictures[0] = NULL;
ref_pictures[1] = NULL;
for (i = 0; i < 2; i++) {
ref_picture = dpb->ref_pic_list[i];
if (!ref_picture)
continue;
index = ref_picture->pic_order_cnt > picture->pic_order_cnt;
picture_ptr = &ref_pictures[index];
if (!*picture_ptr ||
((*picture_ptr)->pic_order_cnt > ref_picture->pic_order_cnt) == index)
*picture_ptr = ref_picture;
}
if (prev_picture_ptr)
*prev_picture_ptr = ref_pictures[0];
if (next_picture_ptr)
*next_picture_ptr = ref_pictures[1];
}

View file

@ -0,0 +1,148 @@
/* GStreamer
* Copyright (C) 2020 Intel Corporation
* Author: He Junyan <junyan.he@intel.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.
*/
#ifndef __GST_MPEG2_PICTURE_H__
#define __GST_MPEG2_PICTURE_H__
#include <gst/codecs/codecs-prelude.h>
#include <gst/codecparsers/gstmpegvideoparser.h>
G_BEGIN_DECLS
#define GST_TYPE_MPEG2_PICTURE (gst_mpeg2_picture_get_type())
#define GST_IS_MPEG2_PICTURE(obj) (GST_IS_MINI_OBJECT_TYPE(obj, GST_TYPE_MPEG2_PICTURE))
#define GST_MPEG2_PICTURE(obj) ((GstMpeg2Picture *)obj)
#define GST_MPEG2_PICTURE_CAST(obj) (GST_MPEG2_PICTURE(obj))
typedef struct _GstMpeg2Slice GstMpeg2Slice;
struct _GstMpeg2Slice
{
/* The parameter set */
GstMpegVideoQuantMatrixExt *quant_matrix;
GstMpegVideoPictureHdr *pic_hdr;
GstMpegVideoPictureExt *pic_ext;
GstMpegVideoSliceHdr header;
/* parsed video packet (doesn't take ownership of raw data) */
GstMpegVideoPacket packet;
};
typedef struct _GstMpeg2Picture GstMpeg2Picture;
struct _GstMpeg2Picture
{
/*< private >*/
GstMiniObject parent;
/* From GstVideoCodecFrame */
guint32 system_frame_number;
gboolean needed_for_output;
/* For interlaced streams */
GstMpeg2Picture *first_field;
gint pic_order_cnt;
gint tsn;
GstMpegVideoPictureStructure structure;
GstMpegVideoPictureType type;
gpointer user_data;
GDestroyNotify notify;
};
#define GST_MPEG2_PICTURE_IS_REF(picture) \
(((GstMpeg2Picture *) picture)->type == GST_MPEG_VIDEO_PICTURE_TYPE_I || \
((GstMpeg2Picture *) picture)->type == GST_MPEG_VIDEO_PICTURE_TYPE_P)
GST_CODECS_API
GType gst_mpeg2_picture_get_type (void);
GST_CODECS_API
GstMpeg2Picture * gst_mpeg2_picture_new (void);
static inline GstMpeg2Picture *
gst_mpeg2_picture_ref (GstMpeg2Picture * picture)
{
return (GstMpeg2Picture *) gst_mini_object_ref (GST_MINI_OBJECT_CAST (picture));
}
static inline void
gst_mpeg2_picture_unref (GstMpeg2Picture * picture)
{
gst_mini_object_unref (GST_MINI_OBJECT_CAST (picture));
}
static inline gboolean
gst_mpeg2_picture_replace (GstMpeg2Picture ** old_picture,
GstMpeg2Picture * new_picture)
{
return gst_mini_object_replace ((GstMiniObject **) old_picture,
(GstMiniObject *) new_picture);
}
static inline void
gst_mpeg2_picture_clear (GstMpeg2Picture ** picture)
{
if (picture && *picture) {
gst_mpeg2_picture_unref (*picture);
*picture = NULL;
}
}
GST_CODECS_API
void gst_mpeg2_picture_set_user_data (GstMpeg2Picture * picture,
gpointer user_data,
GDestroyNotify notify);
GST_CODECS_API
gpointer gst_mpeg2_picture_get_user_data (GstMpeg2Picture * picture);
typedef struct _GstMpeg2Dpb GstMpeg2Dpb;
GST_CODECS_API
GstMpeg2Dpb * gst_mpeg2_dpb_new (void);
GST_CODECS_API
void gst_mpeg2_dpb_free (GstMpeg2Dpb * dpb);
GST_CODECS_API
void gst_mpeg2_dpb_clear (GstMpeg2Dpb * dpb);
GST_CODECS_API
void gst_mpeg2_dpb_add (GstMpeg2Dpb * dpb,
GstMpeg2Picture * picture);
GST_CODECS_API
GstMpeg2Picture * gst_mpeg2_dpb_bump (GstMpeg2Dpb * dpb);
GST_CODECS_API
gboolean gst_mpeg2_dpb_need_bump (GstMpeg2Dpb * dpb);
GST_CODECS_API
void gst_mpeg2_dpb_get_neighbours (GstMpeg2Dpb * dpb,
GstMpeg2Picture * picture,
GstMpeg2Picture ** prev_picture_ptr,
GstMpeg2Picture ** next_picture_ptr);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstMpeg2Picture, gst_mpeg2_picture_unref)
G_END_DECLS
#endif /* __GST_MPEG2_PICTURE_H__ */

View file

@ -7,6 +7,8 @@ codecs_sources = files([
'gstvp9picture.c',
'gstvp8decoder.c',
'gstvp8picture.c',
'gstmpeg2decoder.c',
'gstmpeg2picture.c',
])
codecs_headers = [
@ -18,6 +20,8 @@ codecs_headers = [
'gstvp9picture.h',
'gstvp8decoder.h',
'gstvp8picture.h',
'gstmpeg2decoder.h',
'gstmpeg2picture.h',
]
cp_args = [
@ -52,6 +56,7 @@ if build_gir
'--c-include=gst/codecs/gsth265decoder.h',
'--c-include=gst/codecs/gstvp9decoder.h',
'--c-include=gst/codecs/gstvp8decoder.h',
'--c-include=gst/codecs/gstmpeg2decoder.h',
],
dependencies : [gstvideo_dep, gstcodecparsers_dep]
)