diff options
author | Mathieu Duponchelle <mathieu@centricular.com> | 2018-07-02 04:12:36 +0200 |
---|---|---|
committer | Mathieu Duponchelle <mathieu@centricular.com> | 2018-07-13 00:53:27 +0200 |
commit | 8f1543838b83e0cc13550be785ae2bb0e3c8f8a7 (patch) | |
tree | a346b7c9ac132667dcf448e8d7116011cd0bbd0c /ext/libav/gstavvidenc.c | |
parent | be9b6969cb7d945170b3a05da1a643aad5870f1e (diff) | |
download | gst-libav-8f1543838b83e0cc13550be785ae2bb0e3c8f8a7.tar.gz |
avvidenc: port to send_frame / receive_packet
https://bugzilla.gnome.org/show_bug.cgi?id=792900
Diffstat (limited to 'ext/libav/gstavvidenc.c')
-rw-r--r-- | ext/libav/gstavvidenc.c | 214 |
1 files changed, 115 insertions, 99 deletions
diff --git a/ext/libav/gstavvidenc.c b/ext/libav/gstavvidenc.c index 385abc5..6a9ab5b 100644 --- a/ext/libav/gstavvidenc.c +++ b/ext/libav/gstavvidenc.c @@ -518,27 +518,31 @@ stereo_gst_to_av (GstVideoMultiviewMode mview_mode) } static GstFlowReturn -gst_ffmpegvidenc_handle_frame (GstVideoEncoder * encoder, +gst_ffmpegvidenc_send_frame (GstFFMpegVidEnc * ffmpegenc, GstVideoCodecFrame * frame) { - GstFFMpegVidEnc *ffmpegenc = (GstFFMpegVidEnc *) encoder; - GstBuffer *outbuf; - gint ret = 0, c; GstVideoInfo *info = &ffmpegenc->input_state->info; - AVPacket *pkt; - int have_data = 0; BufferInfo *buffer_info; + guint c; + gint res; + GstFlowReturn ret = GST_FLOW_ERROR; + AVFrame *picture = NULL; + + if (!frame) + goto send_frame; + + picture = ffmpegenc->picture; if (ffmpegenc->context->flags & (AV_CODEC_FLAG_INTERLACED_DCT | AV_CODEC_FLAG_INTERLACED_ME)) { - ffmpegenc->picture->interlaced_frame = TRUE; + picture->interlaced_frame = TRUE; /* if this is not the case, a filter element should be used to swap fields */ - ffmpegenc->picture->top_field_first = + picture->top_field_first = GST_BUFFER_FLAG_IS_SET (frame->input_buffer, GST_VIDEO_BUFFER_FLAG_TFF); } if (GST_VIDEO_INFO_MULTIVIEW_MODE (info) != GST_VIDEO_MULTIVIEW_MODE_NONE) { - AVStereo3D *stereo = av_stereo3d_create_side_data (ffmpegenc->picture); + AVStereo3D *stereo = av_stereo3d_create_side_data (picture); stereo->type = stereo_gst_to_av (GST_VIDEO_INFO_MULTIVIEW_MODE (info)); if (GST_VIDEO_INFO_MULTIVIEW_FLAGS (info) & @@ -548,64 +552,86 @@ gst_ffmpegvidenc_handle_frame (GstVideoEncoder * encoder, } if (GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME (frame)) - ffmpegenc->picture->pict_type = AV_PICTURE_TYPE_I; + picture->pict_type = AV_PICTURE_TYPE_I; buffer_info = g_slice_new0 (BufferInfo); buffer_info->buffer = gst_buffer_ref (frame->input_buffer); if (!gst_video_frame_map (&buffer_info->vframe, info, frame->input_buffer, GST_MAP_READ)) { - GST_ERROR_OBJECT (encoder, "Failed to map input buffer"); + GST_ERROR_OBJECT (ffmpegenc, "Failed to map input buffer"); gst_buffer_unref (buffer_info->buffer); g_slice_free (BufferInfo, buffer_info); gst_video_codec_frame_unref (frame); - return GST_FLOW_ERROR; + goto done; } /* Fill avpicture */ - ffmpegenc->picture->buf[0] = + picture->buf[0] = av_buffer_create (NULL, 0, buffer_info_free, buffer_info, 0); for (c = 0; c < AV_NUM_DATA_POINTERS; c++) { if (c < GST_VIDEO_INFO_N_COMPONENTS (info)) { - ffmpegenc->picture->data[c] = - GST_VIDEO_FRAME_PLANE_DATA (&buffer_info->vframe, c); - ffmpegenc->picture->linesize[c] = + picture->data[c] = GST_VIDEO_FRAME_PLANE_DATA (&buffer_info->vframe, c); + picture->linesize[c] = GST_VIDEO_FRAME_COMP_STRIDE (&buffer_info->vframe, c); } else { - ffmpegenc->picture->data[c] = NULL; - ffmpegenc->picture->linesize[c] = 0; + picture->data[c] = NULL; + picture->linesize[c] = 0; } } - ffmpegenc->picture->format = ffmpegenc->context->pix_fmt; - ffmpegenc->picture->width = GST_VIDEO_FRAME_WIDTH (&buffer_info->vframe); - ffmpegenc->picture->height = GST_VIDEO_FRAME_HEIGHT (&buffer_info->vframe); + picture->format = ffmpegenc->context->pix_fmt; + picture->width = GST_VIDEO_FRAME_WIDTH (&buffer_info->vframe); + picture->height = GST_VIDEO_FRAME_HEIGHT (&buffer_info->vframe); - ffmpegenc->picture->pts = + picture->pts = gst_ffmpeg_time_gst_to_ff (frame->pts / ffmpegenc->context->ticks_per_frame, ffmpegenc->context->time_base); - have_data = 0; - pkt = g_slice_new0 (AVPacket); +send_frame: + res = avcodec_send_frame (ffmpegenc->context, picture); - ret = - avcodec_encode_video2 (ffmpegenc->context, pkt, ffmpegenc->picture, - &have_data); + if (picture) + av_frame_unref (picture); - av_frame_unref (ffmpegenc->picture); + if (res == 0) + ret = GST_FLOW_OK; + else if (res == AVERROR_EOF) + ret = GST_FLOW_EOS; - if (ret < 0 || !have_data) - g_slice_free (AVPacket, pkt); +done: + return ret; +} - if (ret < 0) - goto encode_fail; +static GstFlowReturn +gst_ffmpegvidenc_receive_packet (GstFFMpegVidEnc * ffmpegenc, + gboolean * got_packet, gboolean send) +{ + AVPacket *pkt; + GstBuffer *outbuf; + GstVideoCodecFrame *frame; + gint res; + GstFlowReturn ret = GST_FLOW_OK; - /* Encoder needs more data */ - if (!have_data) { - gst_video_codec_frame_unref (frame); - return GST_FLOW_OK; + *got_packet = FALSE; + + pkt = g_slice_new0 (AVPacket); + + res = avcodec_receive_packet (ffmpegenc->context, pkt); + + if (res == AVERROR (EAGAIN)) { + g_slice_free (AVPacket, pkt); + goto done; + } else if (res == AVERROR_EOF) { + ret = GST_FLOW_EOS; + goto done; + } else if (res < 0) { + res = GST_FLOW_ERROR; + goto done; } + *got_packet = TRUE; + /* save stats info if there is some as well as a stats file */ if (ffmpegenc->file && ffmpegenc->context->stats_out) if (fprintf (ffmpegenc->file, "%s", ffmpegenc->context->stats_out) < 0) @@ -613,24 +639,52 @@ gst_ffmpegvidenc_handle_frame (GstVideoEncoder * encoder, (("Could not write to file \"%s\"."), ffmpegenc->filename), GST_ERROR_SYSTEM); - gst_video_codec_frame_unref (frame); - /* Get oldest frame */ - frame = gst_video_encoder_get_oldest_frame (encoder); + frame = gst_video_encoder_get_oldest_frame (GST_VIDEO_ENCODER (ffmpegenc)); - outbuf = - gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, pkt->data, - pkt->size, 0, pkt->size, pkt, gst_ffmpegvidenc_free_avpacket); - frame->output_buffer = outbuf; + if (send) { + outbuf = + gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, pkt->data, + pkt->size, 0, pkt->size, pkt, gst_ffmpegvidenc_free_avpacket); + frame->output_buffer = outbuf; - if (pkt->flags & AV_PKT_FLAG_KEY) - GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame); - else - GST_VIDEO_CODEC_FRAME_UNSET_SYNC_POINT (frame); + if (pkt->flags & AV_PKT_FLAG_KEY) + GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame); + else + GST_VIDEO_CODEC_FRAME_UNSET_SYNC_POINT (frame); + } - return gst_video_encoder_finish_frame (encoder, frame); + ret = gst_video_encoder_finish_frame (GST_VIDEO_ENCODER (ffmpegenc), frame); - /* ERRORS */ +done: + return ret; +} + +static GstFlowReturn +gst_ffmpegvidenc_handle_frame (GstVideoEncoder * encoder, + GstVideoCodecFrame * frame) +{ + GstFFMpegVidEnc *ffmpegenc = (GstFFMpegVidEnc *) encoder; + GstFlowReturn ret; + gboolean got_packet; + + ret = gst_ffmpegvidenc_send_frame (ffmpegenc, frame); + + if (ret != GST_FLOW_OK) + goto encode_fail; + + gst_video_codec_frame_unref (frame); + + do { + ret = gst_ffmpegvidenc_receive_packet (ffmpegenc, &got_packet, TRUE); + if (ret != GST_FLOW_OK) + break; + } while (got_packet); + +done: + return ret; + + /* We choose to be error-resilient */ encode_fail: { #ifndef GST_DISABLE_GST_DEBUG @@ -640,19 +694,16 @@ encode_fail: "avenc_%s: failed to encode buffer", oclass->in_plugin->name); #endif /* GST_DISABLE_GST_DEBUG */ /* avoid frame (and ts etc) piling up */ - return gst_video_encoder_finish_frame (encoder, frame); + ret = gst_video_encoder_finish_frame (encoder, frame); + goto done; } } static GstFlowReturn gst_ffmpegvidenc_flush_buffers (GstFFMpegVidEnc * ffmpegenc, gboolean send) { - GstVideoCodecFrame *frame; - GstFlowReturn flow_ret = GST_FLOW_OK; - GstBuffer *outbuf; - gint ret; - AVPacket *pkt; - int have_data = 0; + GstFlowReturn ret = GST_FLOW_OK; + gboolean got_packet; GST_DEBUG_OBJECT (ffmpegenc, "flushing buffers with sending %d", send); @@ -660,54 +711,19 @@ gst_ffmpegvidenc_flush_buffers (GstFFMpegVidEnc * ffmpegenc, gboolean send) if (!ffmpegenc->opened) goto done; - while ((frame = - gst_video_encoder_get_oldest_frame (GST_VIDEO_ENCODER (ffmpegenc)))) { - pkt = g_slice_new0 (AVPacket); - have_data = 0; + ret = gst_ffmpegvidenc_send_frame (ffmpegenc, NULL); - ret = avcodec_encode_video2 (ffmpegenc->context, pkt, NULL, &have_data); + if (ret != GST_FLOW_OK) + goto done; - if (ret < 0) { /* there should be something, notify and give up */ -#ifndef GST_DISABLE_GST_DEBUG - GstFFMpegVidEncClass *oclass = - (GstFFMpegVidEncClass *) (G_OBJECT_GET_CLASS (ffmpegenc)); - GST_WARNING_OBJECT (ffmpegenc, - "avenc_%s: failed to flush buffer", oclass->in_plugin->name); -#endif /* GST_DISABLE_GST_DEBUG */ - g_slice_free (AVPacket, pkt); - gst_video_codec_frame_unref (frame); + do { + ret = gst_ffmpegvidenc_receive_packet (ffmpegenc, &got_packet, send); + if (ret != GST_FLOW_OK) break; - } - - /* save stats info if there is some as well as a stats file */ - if (ffmpegenc->file && ffmpegenc->context->stats_out) - if (fprintf (ffmpegenc->file, "%s", ffmpegenc->context->stats_out) < 0) - GST_ELEMENT_ERROR (ffmpegenc, RESOURCE, WRITE, - (("Could not write to file \"%s\"."), ffmpegenc->filename), - GST_ERROR_SYSTEM); - - if (send && have_data) { - outbuf = - gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, pkt->data, - pkt->size, 0, pkt->size, pkt, gst_ffmpegvidenc_free_avpacket); - frame->output_buffer = outbuf; - - if (pkt->flags & AV_PKT_FLAG_KEY) - GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame); - else - GST_VIDEO_CODEC_FRAME_UNSET_SYNC_POINT (frame); - - flow_ret = - gst_video_encoder_finish_frame (GST_VIDEO_ENCODER (ffmpegenc), frame); - } else { - /* no frame attached, so will be skipped and removed from frame list */ - gst_video_encoder_finish_frame (GST_VIDEO_ENCODER (ffmpegenc), frame); - } - } + } while (got_packet); done: - - return flow_ret; + return ret; } static void |