diff options
Diffstat (limited to 'ext/libav/gstavviddec.c')
-rw-r--r-- | ext/libav/gstavviddec.c | 113 |
1 files changed, 95 insertions, 18 deletions
diff --git a/ext/libav/gstavviddec.c b/ext/libav/gstavviddec.c index d5dc37d..3e537a0 100644 --- a/ext/libav/gstavviddec.c +++ b/ext/libav/gstavviddec.c @@ -68,6 +68,7 @@ static gboolean gst_ffmpegviddec_set_format (GstVideoDecoder * decoder, GstVideoCodecState * state); static GstFlowReturn gst_ffmpegviddec_handle_frame (GstVideoDecoder * decoder, GstVideoCodecFrame * frame); +static gboolean gst_ffmpegviddec_start (GstVideoDecoder * decoder); static gboolean gst_ffmpegviddec_stop (GstVideoDecoder * decoder); static gboolean gst_ffmpegviddec_flush (GstVideoDecoder * decoder); static gboolean gst_ffmpegviddec_decide_allocation (GstVideoDecoder * decoder, @@ -81,7 +82,7 @@ static void gst_ffmpegviddec_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); static gboolean gst_ffmpegviddec_negotiate (GstFFMpegVidDec * ffmpegdec, - gboolean force); + AVCodecContext * context, gboolean force); /* some sort of bufferpool handling, but different */ static int gst_ffmpegviddec_get_buffer (AVCodecContext * context, @@ -234,6 +235,7 @@ gst_ffmpegviddec_class_init (GstFFMpegVidDecClass * klass) viddec_class->set_format = gst_ffmpegviddec_set_format; viddec_class->handle_frame = gst_ffmpegviddec_handle_frame; + viddec_class->start = gst_ffmpegviddec_start; viddec_class->stop = gst_ffmpegviddec_stop; viddec_class->flush = gst_ffmpegviddec_flush; viddec_class->finish = gst_ffmpegviddec_finish; @@ -275,23 +277,40 @@ gst_ffmpegviddec_finalize (GObject * object) /* with LOCK */ -static void -gst_ffmpegviddec_close (GstFFMpegVidDec * ffmpegdec) +static gboolean +gst_ffmpegviddec_close (GstFFMpegVidDec * ffmpegdec, gboolean reset) { + GstFFMpegVidDecClass *oclass; + gint i; + + oclass = (GstFFMpegVidDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec)); + GST_LOG_OBJECT (ffmpegdec, "closing ffmpeg codec"); gst_caps_replace (&ffmpegdec->last_caps, NULL); - if (ffmpegdec->context->priv_data) - gst_ffmpeg_avcodec_close (ffmpegdec->context); + gst_ffmpeg_avcodec_close (ffmpegdec->context); ffmpegdec->opened = FALSE; + for (i = 0; i < G_N_ELEMENTS (ffmpegdec->stride); i++) + ffmpegdec->stride[i] = -1; + gst_buffer_replace (&ffmpegdec->palette, NULL); if (ffmpegdec->context->extradata) { av_free (ffmpegdec->context->extradata); ffmpegdec->context->extradata = NULL; } + + if (reset) { + if (avcodec_get_context_defaults3 (ffmpegdec->context, + oclass->in_plugin) < 0) { + GST_DEBUG_OBJECT (ffmpegdec, "Failed to set context defaults"); + return FALSE; + } + ffmpegdec->context->opaque = ffmpegdec; + } + return TRUE; } /* with LOCK */ @@ -299,12 +318,16 @@ static gboolean gst_ffmpegviddec_open (GstFFMpegVidDec * ffmpegdec) { GstFFMpegVidDecClass *oclass; + gint i; oclass = (GstFFMpegVidDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec)); if (gst_ffmpeg_avcodec_open (ffmpegdec->context, oclass->in_plugin) < 0) goto could_not_open; + for (i = 0; i < G_N_ELEMENTS (ffmpegdec->stride); i++) + ffmpegdec->stride[i] = -1; + ffmpegdec->opened = TRUE; ffmpegdec->is_realvideo = FALSE; @@ -328,7 +351,7 @@ gst_ffmpegviddec_open (GstFFMpegVidDec * ffmpegdec) /* ERRORS */ could_not_open: { - gst_ffmpegviddec_close (ffmpegdec); + gst_ffmpegviddec_close (ffmpegdec, TRUE); GST_DEBUG_OBJECT (ffmpegdec, "avdec_%s: Failed to open libav codec", oclass->in_plugin->name); return FALSE; @@ -384,7 +407,10 @@ gst_ffmpegviddec_set_format (GstVideoDecoder * decoder, GST_OBJECT_UNLOCK (ffmpegdec); gst_ffmpegviddec_drain (ffmpegdec); GST_OBJECT_LOCK (ffmpegdec); - gst_ffmpegviddec_close (ffmpegdec); + if (!gst_ffmpegviddec_close (ffmpegdec, TRUE)) { + GST_OBJECT_UNLOCK (ffmpegdec); + return FALSE; + } } gst_caps_replace (&ffmpegdec->last_caps, state->caps); @@ -559,7 +585,7 @@ gst_ffmpegviddec_get_buffer (AVCodecContext * context, AVFrame * picture) ffmpegdec->context->pix_fmt = context->pix_fmt; /* see if we need renegotiation */ - if (G_UNLIKELY (!gst_ffmpegviddec_negotiate (ffmpegdec, FALSE))) + if (G_UNLIKELY (!gst_ffmpegviddec_negotiate (ffmpegdec, context, FALSE))) goto negotiate_failed; if (!ffmpegdec->current_dr) @@ -582,6 +608,30 @@ gst_ffmpegviddec_get_buffer (AVCodecContext * context, AVFrame * picture) if (c < GST_VIDEO_INFO_N_PLANES (info)) { picture->data[c] = GST_VIDEO_FRAME_PLANE_DATA (&dframe->vframe, c); picture->linesize[c] = GST_VIDEO_FRAME_PLANE_STRIDE (&dframe->vframe, c); + + /* libav does not allow stride changes currently, fall back to + * non-direct rendering here: + * https://bugzilla.gnome.org/show_bug.cgi?id=704769 + * https://bugzilla.libav.org/show_bug.cgi?id=556 + */ + if (ffmpegdec->stride[c] == -1) { + ffmpegdec->stride[c] = picture->linesize[c]; + } else if (picture->linesize[c] != ffmpegdec->stride[c]) { + GST_LOG_OBJECT (ffmpegdec, + "No direct rendering, stride changed c=%d %d->%d", c, + ffmpegdec->stride[c], picture->linesize[c]); + + for (c = 0; c < AV_NUM_DATA_POINTERS; c++) { + picture->data[c] = NULL; + picture->linesize[c] = 0; + } + gst_video_frame_unmap (&dframe->vframe); + dframe->mapped = FALSE; + gst_buffer_replace (&frame->output_buffer, NULL); + ffmpegdec->current_dr = FALSE; + + goto no_dr; + } } else { picture->data[c] = NULL; picture->linesize[c] = 0; @@ -625,7 +675,13 @@ invalid_frame: } fallback: { - return avcodec_default_get_buffer (context, picture); + int c; + int ret = avcodec_default_get_buffer (context, picture); + + for (c = 0; c < AV_NUM_DATA_POINTERS; c++) + ffmpegdec->stride[c] = picture->linesize[c]; + + return ret; } duplicate_frame: { @@ -722,10 +778,9 @@ gst_ffmpegviddec_release_buffer (AVCodecContext * context, AVFrame * picture) } static gboolean -update_video_context (GstFFMpegVidDec * ffmpegdec, gboolean force) +update_video_context (GstFFMpegVidDec * ffmpegdec, AVCodecContext * context, + gboolean force) { - AVCodecContext *context = ffmpegdec->context; - if (!force && ffmpegdec->ctx_width == context->width && ffmpegdec->ctx_height == context->height && ffmpegdec->ctx_ticks == context->ticks_per_frame @@ -833,14 +888,15 @@ no_par: } static gboolean -gst_ffmpegviddec_negotiate (GstFFMpegVidDec * ffmpegdec, gboolean force) +gst_ffmpegviddec_negotiate (GstFFMpegVidDec * ffmpegdec, + AVCodecContext * context, gboolean force) { GstVideoFormat fmt; GstVideoInfo *in_info, *out_info; GstVideoCodecState *output_state; gint fps_n, fps_d; - if (!update_video_context (ffmpegdec, force)) + if (!update_video_context (ffmpegdec, context, force)) return TRUE; fmt = gst_ffmpeg_pixfmt_to_videoformat (ffmpegdec->ctx_pix_fmt); @@ -1158,7 +1214,7 @@ gst_ffmpegviddec_video_frame (GstFFMpegVidDec * ffmpegdec, GST_WARNING ("Change in interlacing ! picture:%d, recorded:%d", ffmpegdec->picture->interlaced_frame, ffmpegdec->ctx_interlaced); ffmpegdec->ctx_interlaced = ffmpegdec->picture->interlaced_frame; - if (!gst_ffmpegviddec_negotiate (ffmpegdec, TRUE)) + if (!gst_ffmpegviddec_negotiate (ffmpegdec, ffmpegdec->context, TRUE)) goto negotiation_error; } @@ -1404,13 +1460,34 @@ gst_ffmpegviddec_handle_frame (GstVideoDecoder * decoder, return ret; } + +static gboolean +gst_ffmpegviddec_start (GstVideoDecoder * decoder) +{ + GstFFMpegVidDec *ffmpegdec = (GstFFMpegVidDec *) decoder; + GstFFMpegVidDecClass *oclass; + + oclass = (GstFFMpegVidDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec)); + + GST_OBJECT_LOCK (ffmpegdec); + if (avcodec_get_context_defaults3 (ffmpegdec->context, oclass->in_plugin) < 0) { + GST_DEBUG_OBJECT (ffmpegdec, "Failed to set context defaults"); + GST_OBJECT_UNLOCK (ffmpegdec); + return FALSE; + } + ffmpegdec->context->opaque = ffmpegdec; + GST_OBJECT_UNLOCK (ffmpegdec); + + return TRUE; +} + static gboolean gst_ffmpegviddec_stop (GstVideoDecoder * decoder) { GstFFMpegVidDec *ffmpegdec = (GstFFMpegVidDec *) decoder; GST_OBJECT_LOCK (ffmpegdec); - gst_ffmpegviddec_close (ffmpegdec); + gst_ffmpegviddec_close (ffmpegdec, FALSE); GST_OBJECT_UNLOCK (ffmpegdec); g_free (ffmpegdec->padded); ffmpegdec->padded = NULL; @@ -1503,8 +1580,8 @@ gst_ffmpegviddec_decide_allocation (GstVideoDecoder * decoder, GstQuery * query) avcodec_align_dimensions2 (ffmpegdec->context, &width, &height, linesize_align); edge = - ffmpegdec-> - context->flags & CODEC_FLAG_EMU_EDGE ? 0 : avcodec_get_edge_width (); + ffmpegdec->context-> + flags & CODEC_FLAG_EMU_EDGE ? 0 : avcodec_get_edge_width (); /* increase the size for the padding */ width += edge << 1; height += edge << 1; |