static void gst_ffmpegenc_flush_buffers (GstFFMpegEnc * ffmpegenc, gboolean send) { GstBuffer *outbuf, *inbuf; gint ret_size; GST_DEBUG_OBJECT (ffmpegenc, "flushing buffers with sending %d", send); /* no need to empty codec if there is none */ if (!ffmpegenc->opened) goto flush; while (!g_queue_is_empty (ffmpegenc->delay)) { ffmpegenc_setup_working_buf (ffmpegenc); ret_size = avcodec_encode_video (ffmpegenc->context, ffmpegenc->working_buf, ffmpegenc->working_buf_size, NULL); if (ret_size < 0) { /* there should be something, notify and give up */ #ifndef GST_DISABLE_GST_DEBUG GstFFMpegEncClass *oclass = (GstFFMpegEncClass *) (G_OBJECT_GET_CLASS (ffmpegenc)); GST_WARNING_OBJECT (ffmpegenc, "ffenc_%s: failed to flush buffer", oclass->in_plugin->name); #endif /* GST_DISABLE_GST_DEBUG */ 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); /* handle b-frame delay when no output, so we don't output empty frames */ inbuf = g_queue_pop_head (ffmpegenc->delay); outbuf = gst_buffer_new_and_alloc (ret_size); memcpy (GST_BUFFER_DATA (outbuf), ffmpegenc->working_buf, ret_size); GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (inbuf); GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (inbuf); if (!ffmpegenc->context->coded_frame->key_frame) GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT); gst_buffer_set_caps (outbuf, GST_PAD_CAPS (ffmpegenc->srcpad)); gst_buffer_unref (inbuf); if (send) gst_pad_push (ffmpegenc->srcpad, outbuf); else gst_buffer_unref (outbuf); } flush: { /* make sure that we empty the queue, is still needed if we had to break */ while (!g_queue_is_empty (ffmpegenc->delay)) gst_buffer_unref (g_queue_pop_head (ffmpegenc->delay)); } }
static GstFlowReturn gst_ffmpegvidenc_flush_buffers (GstFFMpegVidEnc * ffmpegenc, gboolean send) { GstVideoCodecFrame *frame; GstFlowReturn flow_ret = GST_FLOW_OK; GstBuffer *outbuf; gint ret_size; GST_DEBUG_OBJECT (ffmpegenc, "flushing buffers with sending %d", send); /* no need to empty codec if there is none */ if (!ffmpegenc->opened) goto done; while ((frame = gst_video_encoder_get_oldest_frame (GST_VIDEO_ENCODER (ffmpegenc)))) { ffmpegenc_setup_working_buf (ffmpegenc); ret_size = avcodec_encode_video (ffmpegenc->context, ffmpegenc->working_buf, ffmpegenc->working_buf_size, NULL); if (ret_size < 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 */ gst_video_codec_frame_unref (frame); 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) { if (gst_video_encoder_allocate_output_frame (GST_VIDEO_ENCODER (ffmpegenc), frame, ret_size) != GST_FLOW_OK) { #ifndef GST_DISABLE_GST_DEBUG GstFFMpegVidEncClass *oclass = (GstFFMpegVidEncClass *) (G_OBJECT_GET_CLASS (ffmpegenc)); GST_WARNING_OBJECT (ffmpegenc, "avenc_%s: failed to allocate buffer", oclass->in_plugin->name); #endif /* GST_DISABLE_GST_DEBUG */ gst_video_codec_frame_unref (frame); break; } outbuf = frame->output_buffer; gst_buffer_fill (outbuf, 0, ffmpegenc->working_buf, ret_size); if (ffmpegenc->context->coded_frame->key_frame) GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame); flow_ret = gst_video_encoder_finish_frame (GST_VIDEO_ENCODER (ffmpegenc), frame); } else { gst_video_codec_frame_unref (frame); } } done: return flow_ret; }
static GstFlowReturn gst_ffmpegenc_chain_video (GstPad * pad, GstBuffer * inbuf) { GstFFMpegEnc *ffmpegenc = (GstFFMpegEnc *) (GST_PAD_PARENT (pad)); GstBuffer *outbuf; gint ret_size = 0, frame_size; gboolean force_keyframe; GST_DEBUG_OBJECT (ffmpegenc, "Received buffer of time %" GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (inbuf))); GST_OBJECT_LOCK (ffmpegenc); force_keyframe = ffmpegenc->force_keyframe; ffmpegenc->force_keyframe = FALSE; GST_OBJECT_UNLOCK (ffmpegenc); if (force_keyframe) ffmpegenc->picture->pict_type = FF_I_TYPE; frame_size = gst_ffmpeg_avpicture_fill ((AVPicture *) ffmpegenc->picture, GST_BUFFER_DATA (inbuf), ffmpegenc->context->pix_fmt, ffmpegenc->context->width, ffmpegenc->context->height); g_return_val_if_fail (frame_size == GST_BUFFER_SIZE (inbuf), GST_FLOW_ERROR); ffmpegenc->picture->pts = gst_ffmpeg_time_gst_to_ff (GST_BUFFER_TIMESTAMP (inbuf) / ffmpegenc->context->ticks_per_frame, ffmpegenc->context->time_base); ffmpegenc_setup_working_buf (ffmpegenc); ret_size = avcodec_encode_video (ffmpegenc->context, ffmpegenc->working_buf, ffmpegenc->working_buf_size, ffmpegenc->picture); if (ret_size < 0) { #ifndef GST_DISABLE_GST_DEBUG GstFFMpegEncClass *oclass = (GstFFMpegEncClass *) (G_OBJECT_GET_CLASS (ffmpegenc)); GST_ERROR_OBJECT (ffmpegenc, "ffenc_%s: failed to encode buffer", oclass->in_plugin->name); #endif /* GST_DISABLE_GST_DEBUG */ gst_buffer_unref (inbuf); return GST_FLOW_OK; } /* handle b-frame delay when no output, so we don't output empty frames; * timestamps and so can permute a bit between coding and display order * but keyframes should still end up with the proper metadata */ g_queue_push_tail (ffmpegenc->delay, inbuf); if (ret_size) inbuf = g_queue_pop_head (ffmpegenc->delay); else return GST_FLOW_OK; /* 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); outbuf = gst_buffer_new_and_alloc (ret_size); memcpy (GST_BUFFER_DATA (outbuf), ffmpegenc->working_buf, ret_size); GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (inbuf); GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (inbuf); /* buggy codec may not set coded_frame */ if (ffmpegenc->context->coded_frame) { if (!ffmpegenc->context->coded_frame->key_frame) GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT); } else GST_WARNING_OBJECT (ffmpegenc, "codec did not provide keyframe info"); gst_buffer_set_caps (outbuf, GST_PAD_CAPS (ffmpegenc->srcpad)); gst_buffer_unref (inbuf); /* Reset frame type */ if (ffmpegenc->picture->pict_type) ffmpegenc->picture->pict_type = 0; if (force_keyframe) { gst_pad_push_event (ffmpegenc->srcpad, gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, gst_structure_new ("GstForceKeyUnit", "timestamp", G_TYPE_UINT64, GST_BUFFER_TIMESTAMP (outbuf), NULL))); } return gst_pad_push (ffmpegenc->srcpad, outbuf); }
static GstFlowReturn gst_ffmpegvidenc_handle_frame (GstVideoEncoder * encoder, GstVideoCodecFrame * frame) { GstFFMpegVidEnc *ffmpegenc = (GstFFMpegVidEnc *) encoder; GstBuffer *outbuf; gint ret_size = 0, c; GstVideoInfo *info = &ffmpegenc->input_state->info; GstVideoFrame vframe; if (GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME (frame)) ffmpegenc->picture->pict_type = AV_PICTURE_TYPE_I; if (!gst_video_frame_map (&vframe, info, frame->input_buffer, GST_MAP_READ)) { GST_ERROR_OBJECT (encoder, "Failed to map input buffer"); return GST_FLOW_ERROR; } /* Fill avpicture */ 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 (&vframe, c); ffmpegenc->picture->linesize[c] = GST_VIDEO_FRAME_COMP_STRIDE (&vframe, c); } else { ffmpegenc->picture->data[c] = NULL; ffmpegenc->picture->linesize[c] = 0; } } ffmpegenc->picture->pts = gst_ffmpeg_time_gst_to_ff (frame->pts / ffmpegenc->context->ticks_per_frame, ffmpegenc->context->time_base); ffmpegenc_setup_working_buf (ffmpegenc); ret_size = avcodec_encode_video (ffmpegenc->context, ffmpegenc->working_buf, ffmpegenc->working_buf_size, ffmpegenc->picture); gst_video_frame_unmap (&vframe); if (ret_size < 0) goto encode_fail; /* Encoder needs more data */ if (!ret_size) return GST_FLOW_OK; /* 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); gst_video_codec_frame_unref (frame); /* Get oldest frame */ frame = gst_video_encoder_get_oldest_frame (encoder); /* Allocate output buffer */ if (gst_video_encoder_allocate_output_frame (encoder, frame, ret_size) != GST_FLOW_OK) { gst_video_codec_frame_unref (frame); goto alloc_fail; } outbuf = frame->output_buffer; gst_buffer_fill (outbuf, 0, ffmpegenc->working_buf, ret_size); /* buggy codec may not set coded_frame */ if (ffmpegenc->context->coded_frame) { if (ffmpegenc->context->coded_frame->key_frame) GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame); } else GST_WARNING_OBJECT (ffmpegenc, "codec did not provide keyframe info"); /* Reset frame type */ if (ffmpegenc->picture->pict_type) ffmpegenc->picture->pict_type = 0; return gst_video_encoder_finish_frame (encoder, frame); /* ERRORS */ encode_fail: { #ifndef GST_DISABLE_GST_DEBUG GstFFMpegVidEncClass *oclass = (GstFFMpegVidEncClass *) (G_OBJECT_GET_CLASS (ffmpegenc)); GST_ERROR_OBJECT (ffmpegenc, "avenc_%s: failed to encode buffer", oclass->in_plugin->name); #endif /* GST_DISABLE_GST_DEBUG */ return GST_FLOW_OK; } alloc_fail: { #ifndef GST_DISABLE_GST_DEBUG GstFFMpegVidEncClass *oclass = (GstFFMpegVidEncClass *) (G_OBJECT_GET_CLASS (ffmpegenc)); GST_ERROR_OBJECT (ffmpegenc, "avenc_%s: failed to allocate buffer", oclass->in_plugin->name); #endif /* GST_DISABLE_GST_DEBUG */ return GST_FLOW_ERROR; } }