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

426 lines
10 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.
*/
#include "gsth264dpb.h"
/* Properties */
enum
{
PROP_0,
PROP_NUM_REF_FRAMES,
PROP_MAX_LONGTERM_IDX
};
GST_DEBUG_CATEGORY_STATIC (h264dpb_debug);
#define GST_CAT_DEFAULT h264dpb_debug
#define DEBUG_INIT \
GST_DEBUG_CATEGORY_INIT (h264dpb_debug, "h264dpb", 0, \
"H264 DPB");
G_DEFINE_TYPE_WITH_CODE (GstH264DPB, gst_h264_dpb, G_TYPE_OBJECT, DEBUG_INIT);
void
gst_h264_dpb_fill_reference_frames (GstH264DPB * dpb,
VdpReferenceFrameH264 reference_frames[16])
{
GstH264Frame **frames;
guint i;
frames = dpb->frames;
for (i = 0; i < dpb->n_frames; i++) {
GstH264Frame *frame = frames[i];
reference_frames[i].surface =
GST_VDP_VIDEO_BUFFER (GST_VIDEO_FRAME_CAST (frame)->src_buffer)->
surface;
reference_frames[i].is_long_term = frame->is_long_term;
reference_frames[i].top_is_reference = frame->is_reference;
reference_frames[i].bottom_is_reference = frame->is_reference;
reference_frames[i].field_order_cnt[0] = frame->poc;
reference_frames[i].field_order_cnt[1] = frame->poc;
reference_frames[i].frame_idx = frame->frame_idx;
}
for (i = dpb->n_frames; i < 16; i++) {
reference_frames[i].surface = VDP_INVALID_HANDLE;
reference_frames[i].top_is_reference = VDP_FALSE;
reference_frames[i].bottom_is_reference = VDP_FALSE;
}
}
static void
gst_h264_dpb_remove (GstH264DPB * dpb, guint idx)
{
GstH264Frame **frames;
guint i;
frames = dpb->frames;
gst_video_frame_unref (GST_VIDEO_FRAME_CAST (frames[idx]));
dpb->n_frames--;
for (i = idx; i < dpb->n_frames; i++)
frames[i] = frames[i + 1];
}
static GstFlowReturn
gst_h264_dpb_output (GstH264DPB * dpb, guint idx)
{
GstFlowReturn ret;
GstH264Frame *frame = dpb->frames[idx];
gst_video_frame_ref (GST_VIDEO_FRAME_CAST (frame));
ret = dpb->output (dpb, frame, dpb->user_data);
frame->output_needed = FALSE;
if (!frame->is_reference)
gst_h264_dpb_remove (dpb, idx);
return ret;
}
static gboolean
gst_h264_dpb_bump (GstH264DPB * dpb, guint poc, GstFlowReturn * ret)
{
GstH264Frame **frames;
guint i;
gint bump_idx;
frames = dpb->frames;
bump_idx = -1;
for (i = 0; i < dpb->n_frames; i++) {
if (frames[i]->output_needed) {
bump_idx = i;
break;
}
}
if (bump_idx != -1) {
for (i = bump_idx + 1; i < dpb->n_frames; i++) {
if (frames[i]->output_needed && (frames[i]->poc < frames[bump_idx]->poc)) {
bump_idx = i;
}
}
if (frames[bump_idx]->poc < poc) {
*ret = gst_h264_dpb_output (dpb, bump_idx);
return TRUE;
}
}
return FALSE;
}
GstFlowReturn
gst_h264_dpb_add (GstH264DPB * dpb, GstH264Frame * h264_frame)
{
GstH264Frame **frames;
GstFlowReturn ret;
GST_DEBUG ("add frame with poc: %d", h264_frame->poc);
frames = dpb->frames;
if (h264_frame->is_reference && h264_frame->is_long_term &&
(h264_frame->frame_idx > dpb->max_longterm_frame_idx))
h264_frame->is_reference = FALSE;
if (h264_frame->is_reference) {
ret = GST_FLOW_OK;
while (dpb->n_frames == dpb->max_frames) {
if (!gst_h264_dpb_bump (dpb, G_MAXUINT, &ret)) {
GST_ERROR_OBJECT (dpb, "Couldn't make room in DPB");
return GST_FLOW_OK;
}
}
dpb->frames[dpb->n_frames++] = h264_frame;
}
else {
while (gst_h264_dpb_bump (dpb, h264_frame->poc, &ret)) {
if (ret != GST_FLOW_OK)
return ret;
}
ret = dpb->output (dpb, h264_frame, dpb->user_data);
}
return ret;
}
void
gst_h264_dpb_flush (GstH264DPB * dpb, gboolean output)
{
GstFlowReturn ret;
GstVideoFrame **frames;
guint i;
GST_DEBUG ("flush");
if (output)
while (gst_h264_dpb_bump (dpb, G_MAXUINT, &ret));
frames = (GstVideoFrame **) dpb->frames;
for (i = 0; i < dpb->n_frames; i++)
gst_video_frame_unref (frames[i]);
dpb->n_frames = 0;
}
void
gst_h264_dpb_mark_sliding (GstH264DPB * dpb)
{
GstH264Frame **frames;
guint i;
gint mark_idx = -1;
if (dpb->n_frames != dpb->max_frames)
return;
frames = dpb->frames;
for (i = 0; i < dpb->n_frames; i++) {
if (frames[i]->is_reference && !frames[i]->is_long_term) {
mark_idx = i;
break;
}
}
if (mark_idx != -1) {
for (i = mark_idx; i < dpb->n_frames; i++) {
if (frames[i]->is_reference && !frames[i]->is_long_term &&
frames[i]->frame_idx < frames[mark_idx]->frame_idx)
mark_idx = i;
}
frames[mark_idx]->is_reference = FALSE;
if (!frames[mark_idx]->output_needed)
gst_h264_dpb_remove (dpb, mark_idx);
}
}
void
gst_h264_dpb_mark_long_term (GstH264DPB * dpb, guint16 pic_num,
guint16 long_term_frame_idx)
{
GstH264Frame **frames;
guint i;
gint mark_idx = -1;
frames = dpb->frames;
for (i = 0; i < dpb->n_frames; i++) {
if (frames[i]->is_reference && !frames[i]->is_long_term &&
frames[i]->frame_idx == pic_num) {
mark_idx = i;
break;
}
}
if (mark_idx != -1) {
frames[mark_idx]->is_long_term = TRUE;
frames[mark_idx]->frame_idx = long_term_frame_idx;
}
}
void
gst_h264_dpb_mark_short_term_unused (GstH264DPB * dpb, guint16 pic_num)
{
GstH264Frame **frames;
guint i;
gint mark_idx = -1;
frames = dpb->frames;
for (i = 0; i < dpb->n_frames; i++) {
if (frames[i]->is_reference && !frames[i]->is_long_term &&
frames[i]->frame_idx == pic_num) {
mark_idx = i;
break;
}
}
if (mark_idx != -1) {
frames[mark_idx]->is_reference = FALSE;
if (!frames[mark_idx]->output_needed)
gst_h264_dpb_remove (dpb, mark_idx);
}
}
void
gst_h264_dpb_mark_long_term_unused (GstH264DPB * dpb, guint16 long_term_pic_num)
{
GstH264Frame **frames;
guint i;
gint mark_idx = -1;
frames = dpb->frames;
for (i = 0; i < dpb->n_frames; i++) {
if (frames[i]->is_reference && frames[i]->is_long_term &&
frames[i]->frame_idx == long_term_pic_num) {
mark_idx = i;
break;
}
}
if (mark_idx != -1) {
frames[mark_idx]->is_reference = FALSE;
if (!frames[mark_idx]->output_needed)
gst_h264_dpb_remove (dpb, mark_idx);
}
}
void
gst_h264_dpb_mark_all_unused (GstH264DPB * dpb)
{
GstH264Frame **frames;
guint i;
frames = dpb->frames;
for (i = 0; i < dpb->n_frames; i++) {
frames[i]->is_reference = FALSE;
if (!frames[i]->output_needed) {
gst_h264_dpb_remove (dpb, i);
i--;
}
}
}
void
gst_h264_dpb_set_output_func (GstH264DPB * dpb, GstH264DPBOutputFunc func,
gpointer user_data)
{
g_return_if_fail (GST_IS_H264_DPB (dpb));
dpb->output = func;
dpb->user_data = user_data;
}
/* GObject vmethod implementations */
static void
gst_h264_dpb_get_property (GObject * object, guint property_id,
GValue * value, GParamSpec * pspec)
{
GstH264DPB *dpb = GST_H264_DPB (object);
switch (property_id) {
case PROP_NUM_REF_FRAMES:
g_value_set_uint (value, dpb->max_frames);
break;
case PROP_MAX_LONGTERM_IDX:
g_value_set_int (value, dpb->max_longterm_frame_idx);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gst_h264_dpb_set_property (GObject * object, guint property_id,
const GValue * value, GParamSpec * pspec)
{
GstH264DPB *dpb = GST_H264_DPB (object);
switch (property_id) {
case PROP_NUM_REF_FRAMES:
{
GstFlowReturn ret;
guint i;
dpb->max_frames = g_value_get_uint (value);
for (i = dpb->n_frames; i > dpb->max_frames; i--)
gst_h264_dpb_bump (dpb, G_MAXUINT, &ret);
break;
}
case PROP_MAX_LONGTERM_IDX:
{
GstH264Frame **frames;
guint i;
dpb->max_longterm_frame_idx = g_value_get_int (value);
frames = dpb->frames;
for (i = dpb->n_frames; i < dpb->n_frames; i++) {
if (frames[i]->is_reference && frames[i]->is_long_term &&
frames[i]->frame_idx > dpb->max_longterm_frame_idx) {
frames[i]->is_reference = FALSE;
if (!frames[i]->output_needed) {
gst_h264_dpb_remove (dpb, i);
i--;
}
}
}
break;
}
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gst_h264_dpb_finalize (GObject * object)
{
GstH264DPB *dpb = GST_H264_DPB (object);
GstVideoFrame **frames;
guint i;
frames = (GstVideoFrame **) dpb->frames;
for (i = 0; i < dpb->n_frames; i++)
gst_video_frame_unref (frames[i]);
G_OBJECT_CLASS (gst_h264_dpb_parent_class)->finalize (object);
}
static void
gst_h264_dpb_init (GstH264DPB * dpb)
{
dpb->n_frames = 0;
dpb->max_longterm_frame_idx = -1;
dpb->max_frames = MAX_DPB_SIZE;
}
static void
gst_h264_dpb_class_init (GstH264DPBClass * klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = gst_h264_dpb_finalize;
object_class->set_property = gst_h264_dpb_set_property;
object_class->get_property = gst_h264_dpb_get_property;
g_object_class_install_property (object_class, PROP_NUM_REF_FRAMES,
g_param_spec_uint ("num-ref-frames", "Num Ref Frames",
"How many reference frames the DPB should hold ",
0, 16, 16, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_MAX_LONGTERM_IDX,
g_param_spec_int ("max-longterm-frame-idx", "MaxLongTermFrameIDX",
"Maximum long-term frame index",
-1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
}