1 /* GStreamer
2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include <assert.h>
25 #include <string.h>
26 /* for stats file handling */
27 #include <stdio.h>
28 #include <glib/gstdio.h>
29 #include <errno.h>
30
31 #include <libavcodec/avcodec.h>
32 #include <libavutil/stereo3d.h>
33 #include <libavutil/opt.h>
34
35 #include "gstav.h"
36 #include "gstavcodecmap.h"
37 #include "gstavutils.h"
38 #include "gstavvidenc.h"
39 #include "gstavcfg.h"
40
41
42 enum
43 {
44 PROP_0,
45 PROP_QUANTIZER,
46 PROP_PASS,
47 PROP_FILENAME,
48 PROP_CFG_BASE,
49 };
50
51 static void gst_ffmpegvidenc_class_init (GstFFMpegVidEncClass * klass);
52 static void gst_ffmpegvidenc_base_init (GstFFMpegVidEncClass * klass);
53 static void gst_ffmpegvidenc_init (GstFFMpegVidEnc * ffmpegenc);
54 static void gst_ffmpegvidenc_finalize (GObject * object);
55
56 static gboolean gst_ffmpegvidenc_start (GstVideoEncoder * encoder);
57 static gboolean gst_ffmpegvidenc_stop (GstVideoEncoder * encoder);
58 static GstFlowReturn gst_ffmpegvidenc_finish (GstVideoEncoder * encoder);
59 static gboolean gst_ffmpegvidenc_set_format (GstVideoEncoder * encoder,
60 GstVideoCodecState * state);
61 static gboolean gst_ffmpegvidenc_propose_allocation (GstVideoEncoder * encoder,
62 GstQuery * query);
63 static gboolean gst_ffmpegvidenc_flush (GstVideoEncoder * encoder);
64
65 static GstFlowReturn gst_ffmpegvidenc_handle_frame (GstVideoEncoder * encoder,
66 GstVideoCodecFrame * frame);
67
68 static void gst_ffmpegvidenc_set_property (GObject * object,
69 guint prop_id, const GValue * value, GParamSpec * pspec);
70 static void gst_ffmpegvidenc_get_property (GObject * object,
71 guint prop_id, GValue * value, GParamSpec * pspec);
72
73 #define GST_FFENC_PARAMS_QDATA g_quark_from_static_string("avenc-params")
74
75 static GstElementClass *parent_class = NULL;
76
77 #define GST_TYPE_FFMPEG_PASS (gst_ffmpeg_pass_get_type ())
78 static GType
gst_ffmpeg_pass_get_type(void)79 gst_ffmpeg_pass_get_type (void)
80 {
81 static GType ffmpeg_pass_type = 0;
82
83 if (!ffmpeg_pass_type) {
84 static const GEnumValue ffmpeg_passes[] = {
85 {0, "Constant Bitrate Encoding", "cbr"},
86 {AV_CODEC_FLAG_QSCALE, "Constant Quantizer", "quant"},
87 {AV_CODEC_FLAG_PASS1, "VBR Encoding - Pass 1", "pass1"},
88 {AV_CODEC_FLAG_PASS2, "VBR Encoding - Pass 2", "pass2"},
89 {0, NULL, NULL},
90 };
91
92 ffmpeg_pass_type =
93 g_enum_register_static ("GstLibAVEncPass", ffmpeg_passes);
94 }
95
96 return ffmpeg_pass_type;
97 }
98
99 static void
gst_ffmpegvidenc_base_init(GstFFMpegVidEncClass * klass)100 gst_ffmpegvidenc_base_init (GstFFMpegVidEncClass * klass)
101 {
102 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
103 AVCodec *in_plugin;
104 GstPadTemplate *srctempl = NULL, *sinktempl = NULL;
105 GstCaps *srccaps = NULL, *sinkcaps = NULL;
106 gchar *longname, *description;
107 const gchar *classification;
108
109 in_plugin =
110 (AVCodec *) g_type_get_qdata (G_OBJECT_CLASS_TYPE (klass),
111 GST_FFENC_PARAMS_QDATA);
112 g_assert (in_plugin != NULL);
113
114 /* construct the element details struct */
115 longname = g_strdup_printf ("libav %s encoder", in_plugin->long_name);
116 description = g_strdup_printf ("libav %s encoder", in_plugin->name);
117 classification =
118 gst_ffmpeg_codecid_is_image (in_plugin->id) ? "Codec/Encoder/Image" :
119 "Codec/Encoder/Video";
120 gst_element_class_set_metadata (element_class, longname,
121 classification, description,
122 "Wim Taymans <wim.taymans@gmail.com>, "
123 "Ronald Bultje <rbultje@ronald.bitfreak.net>");
124 g_free (longname);
125 g_free (description);
126
127 if (!(srccaps = gst_ffmpeg_codecid_to_caps (in_plugin->id, NULL, TRUE))) {
128 GST_DEBUG ("Couldn't get source caps for encoder '%s'", in_plugin->name);
129 srccaps = gst_caps_new_empty_simple ("unknown/unknown");
130 }
131
132 sinkcaps = gst_ffmpeg_codectype_to_video_caps (NULL,
133 in_plugin->id, TRUE, in_plugin);
134 if (!sinkcaps) {
135 GST_DEBUG ("Couldn't get sink caps for encoder '%s'", in_plugin->name);
136 sinkcaps = gst_caps_new_empty_simple ("unknown/unknown");
137 }
138
139 /* pad templates */
140 sinktempl = gst_pad_template_new ("sink", GST_PAD_SINK,
141 GST_PAD_ALWAYS, sinkcaps);
142 srctempl = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, srccaps);
143
144 gst_element_class_add_pad_template (element_class, srctempl);
145 gst_element_class_add_pad_template (element_class, sinktempl);
146
147 gst_caps_unref (sinkcaps);
148 gst_caps_unref (srccaps);
149
150 klass->in_plugin = in_plugin;
151 klass->srctempl = srctempl;
152 klass->sinktempl = sinktempl;
153
154 return;
155 }
156
157 static void
gst_ffmpegvidenc_class_init(GstFFMpegVidEncClass * klass)158 gst_ffmpegvidenc_class_init (GstFFMpegVidEncClass * klass)
159 {
160 GObjectClass *gobject_class;
161 GstVideoEncoderClass *venc_class;
162
163 gobject_class = (GObjectClass *) klass;
164 venc_class = (GstVideoEncoderClass *) klass;
165
166 parent_class = g_type_class_peek_parent (klass);
167
168 gobject_class->set_property = gst_ffmpegvidenc_set_property;
169 gobject_class->get_property = gst_ffmpegvidenc_get_property;
170
171 g_object_class_install_property (gobject_class, PROP_QUANTIZER,
172 g_param_spec_float ("quantizer", "Constant Quantizer",
173 "Constant Quantizer", 0, 30, 0.01f,
174 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
175
176 g_object_class_install_property (gobject_class, PROP_PASS,
177 g_param_spec_enum ("pass", "Encoding pass/type",
178 "Encoding pass/type", GST_TYPE_FFMPEG_PASS, 0,
179 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
180
181 g_object_class_install_property (gobject_class, PROP_FILENAME,
182 g_param_spec_string ("multipass-cache-file", "Multipass Cache File",
183 "Filename for multipass cache file", "stats.log",
184 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
185
186 /* register additional properties, possibly dependent on the exact CODEC */
187 gst_ffmpeg_cfg_install_properties (gobject_class, klass->in_plugin,
188 PROP_CFG_BASE, AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_VIDEO_PARAM);
189
190 venc_class->start = gst_ffmpegvidenc_start;
191 venc_class->stop = gst_ffmpegvidenc_stop;
192 venc_class->finish = gst_ffmpegvidenc_finish;
193 venc_class->handle_frame = gst_ffmpegvidenc_handle_frame;
194 venc_class->set_format = gst_ffmpegvidenc_set_format;
195 venc_class->propose_allocation = gst_ffmpegvidenc_propose_allocation;
196 venc_class->flush = gst_ffmpegvidenc_flush;
197
198 gobject_class->finalize = gst_ffmpegvidenc_finalize;
199 }
200
201 static void
gst_ffmpegvidenc_init(GstFFMpegVidEnc * ffmpegenc)202 gst_ffmpegvidenc_init (GstFFMpegVidEnc * ffmpegenc)
203 {
204 GstFFMpegVidEncClass *klass =
205 (GstFFMpegVidEncClass *) G_OBJECT_GET_CLASS (ffmpegenc);
206
207 GST_PAD_SET_ACCEPT_TEMPLATE (GST_VIDEO_ENCODER_SINK_PAD (ffmpegenc));
208
209 ffmpegenc->context = avcodec_alloc_context3 (klass->in_plugin);
210 ffmpegenc->refcontext = avcodec_alloc_context3 (klass->in_plugin);
211 ffmpegenc->picture = av_frame_alloc ();
212 ffmpegenc->opened = FALSE;
213 ffmpegenc->file = NULL;
214 }
215
216 static void
gst_ffmpegvidenc_finalize(GObject * object)217 gst_ffmpegvidenc_finalize (GObject * object)
218 {
219 GstFFMpegVidEnc *ffmpegenc = (GstFFMpegVidEnc *) object;
220
221 /* clean up remaining allocated data */
222 av_frame_free (&ffmpegenc->picture);
223 gst_ffmpeg_avcodec_close (ffmpegenc->context);
224 av_free (ffmpegenc->context);
225 av_free (ffmpegenc->refcontext);
226
227 G_OBJECT_CLASS (parent_class)->finalize (object);
228 }
229
230 static gboolean
gst_ffmpegvidenc_set_format(GstVideoEncoder * encoder,GstVideoCodecState * state)231 gst_ffmpegvidenc_set_format (GstVideoEncoder * encoder,
232 GstVideoCodecState * state)
233 {
234 GstCaps *other_caps;
235 GstCaps *allowed_caps;
236 GstCaps *icaps;
237 GstVideoCodecState *output_format;
238 enum AVPixelFormat pix_fmt;
239 GstFFMpegVidEnc *ffmpegenc = (GstFFMpegVidEnc *) encoder;
240 GstFFMpegVidEncClass *oclass =
241 (GstFFMpegVidEncClass *) G_OBJECT_GET_CLASS (ffmpegenc);
242
243 /* close old session */
244 if (ffmpegenc->opened) {
245 gst_ffmpeg_avcodec_close (ffmpegenc->context);
246 ffmpegenc->opened = FALSE;
247 if (avcodec_get_context_defaults3 (ffmpegenc->context,
248 oclass->in_plugin) < 0) {
249 GST_DEBUG_OBJECT (ffmpegenc, "Failed to set context defaults");
250 return FALSE;
251 }
252 }
253
254 /* additional avcodec settings */
255 gst_ffmpeg_cfg_fill_context (G_OBJECT (ffmpegenc), ffmpegenc->context);
256
257 if (GST_VIDEO_INFO_IS_INTERLACED (&state->info))
258 ffmpegenc->context->flags |=
259 AV_CODEC_FLAG_INTERLACED_DCT | AV_CODEC_FLAG_INTERLACED_ME;
260
261 /* and last but not least the pass; CBR, 2-pass, etc */
262 ffmpegenc->context->flags |= ffmpegenc->pass;
263 switch (ffmpegenc->pass) {
264 /* some additional action depends on type of pass */
265 case AV_CODEC_FLAG_QSCALE:
266 ffmpegenc->context->global_quality
267 = ffmpegenc->picture->quality = FF_QP2LAMBDA * ffmpegenc->quantizer;
268 break;
269 case AV_CODEC_FLAG_PASS1: /* need to prepare a stats file */
270 /* we don't close when changing caps, fingers crossed */
271 if (!ffmpegenc->file)
272 ffmpegenc->file = g_fopen (ffmpegenc->filename, "w");
273 if (!ffmpegenc->file)
274 goto open_file_err;
275 break;
276 case AV_CODEC_FLAG_PASS2:
277 { /* need to read the whole stats file ! */
278 gsize size;
279
280 if (!g_file_get_contents (ffmpegenc->filename,
281 &ffmpegenc->context->stats_in, &size, NULL))
282 goto file_read_err;
283
284 break;
285 }
286 default:
287 break;
288 }
289
290 GST_DEBUG_OBJECT (ffmpegenc, "Extracting common video information");
291 /* fetch pix_fmt, fps, par, width, height... */
292 gst_ffmpeg_videoinfo_to_context (&state->info, ffmpegenc->context);
293
294 /* sanitize time base */
295 if (ffmpegenc->context->time_base.num <= 0
296 || ffmpegenc->context->time_base.den <= 0)
297 goto insane_timebase;
298
299 if ((oclass->in_plugin->id == AV_CODEC_ID_MPEG4)
300 && (ffmpegenc->context->time_base.den > 65535)) {
301 /* MPEG4 Standards do not support time_base denominator greater than
302 * (1<<16) - 1 . We therefore scale them down.
303 * Agreed, it will not be the exact framerate... but the difference
304 * shouldn't be that noticeable */
305 ffmpegenc->context->time_base.num =
306 (gint) gst_util_uint64_scale_int (ffmpegenc->context->time_base.num,
307 65535, ffmpegenc->context->time_base.den);
308 ffmpegenc->context->time_base.den = 65535;
309 GST_LOG_OBJECT (ffmpegenc, "MPEG4 : scaled down framerate to %d / %d",
310 ffmpegenc->context->time_base.den, ffmpegenc->context->time_base.num);
311 }
312
313 pix_fmt = ffmpegenc->context->pix_fmt;
314
315 /* some codecs support more than one format, first auto-choose one */
316 GST_DEBUG_OBJECT (ffmpegenc, "picking an output format ...");
317 allowed_caps = gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (encoder));
318 if (!allowed_caps) {
319 GST_DEBUG_OBJECT (ffmpegenc, "... but no peer, using template caps");
320 /* we need to copy because get_allowed_caps returns a ref, and
321 * get_pad_template_caps doesn't */
322 allowed_caps =
323 gst_pad_get_pad_template_caps (GST_VIDEO_ENCODER_SRC_PAD (encoder));
324 }
325 GST_DEBUG_OBJECT (ffmpegenc, "chose caps %" GST_PTR_FORMAT, allowed_caps);
326 gst_ffmpeg_caps_with_codecid (oclass->in_plugin->id,
327 oclass->in_plugin->type, allowed_caps, ffmpegenc->context);
328
329 /* open codec */
330 if (gst_ffmpeg_avcodec_open (ffmpegenc->context, oclass->in_plugin) < 0) {
331 gst_caps_unref (allowed_caps);
332 goto open_codec_fail;
333 }
334
335 /* is the colourspace correct? */
336 if (pix_fmt != ffmpegenc->context->pix_fmt) {
337 gst_caps_unref (allowed_caps);
338 goto pix_fmt_err;
339 }
340
341 /* we may have failed mapping caps to a pixfmt,
342 * and quite some codecs do not make up their own mind about that
343 * in any case, _NONE can never work out later on */
344 if (pix_fmt == AV_PIX_FMT_NONE) {
345 gst_caps_unref (allowed_caps);
346 goto bad_input_fmt;
347 }
348
349 /* second pass stats buffer no longer needed */
350 g_free (ffmpegenc->context->stats_in);
351
352 /* try to set this caps on the other side */
353 other_caps = gst_ffmpeg_codecid_to_caps (oclass->in_plugin->id,
354 ffmpegenc->context, TRUE);
355
356 if (!other_caps) {
357 gst_caps_unref (allowed_caps);
358 goto unsupported_codec;
359 }
360
361 icaps = gst_caps_intersect (allowed_caps, other_caps);
362 gst_caps_unref (allowed_caps);
363 gst_caps_unref (other_caps);
364 if (gst_caps_is_empty (icaps)) {
365 gst_caps_unref (icaps);
366 goto unsupported_codec;
367 }
368 icaps = gst_caps_fixate (icaps);
369
370 GST_DEBUG_OBJECT (ffmpegenc, "codec flags 0x%08x", ffmpegenc->context->flags);
371
372 /* Store input state and set output state */
373 if (ffmpegenc->input_state)
374 gst_video_codec_state_unref (ffmpegenc->input_state);
375 ffmpegenc->input_state = gst_video_codec_state_ref (state);
376
377 output_format = gst_video_encoder_set_output_state (encoder, icaps, state);
378 gst_video_codec_state_unref (output_format);
379
380 /* Store some tags */
381 {
382 GstTagList *tags = gst_tag_list_new_empty ();
383 const gchar *codec;
384
385 gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_NOMINAL_BITRATE,
386 (guint) ffmpegenc->context->bit_rate, NULL);
387
388 if ((codec =
389 gst_ffmpeg_get_codecid_longname (ffmpegenc->context->codec_id)))
390 gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_VIDEO_CODEC, codec,
391 NULL);
392
393 gst_video_encoder_merge_tags (encoder, tags, GST_TAG_MERGE_REPLACE);
394 gst_tag_list_unref (tags);
395 }
396
397 /* success! */
398 ffmpegenc->opened = TRUE;
399
400 return TRUE;
401
402 /* ERRORS */
403 open_file_err:
404 {
405 GST_ELEMENT_ERROR (ffmpegenc, RESOURCE, OPEN_WRITE,
406 (("Could not open file \"%s\" for writing."), ffmpegenc->filename),
407 GST_ERROR_SYSTEM);
408 return FALSE;
409 }
410 file_read_err:
411 {
412 GST_ELEMENT_ERROR (ffmpegenc, RESOURCE, READ,
413 (("Could not get contents of file \"%s\"."), ffmpegenc->filename),
414 GST_ERROR_SYSTEM);
415 return FALSE;
416 }
417
418 insane_timebase:
419 {
420 GST_ERROR_OBJECT (ffmpegenc, "Rejecting time base %d/%d",
421 ffmpegenc->context->time_base.den, ffmpegenc->context->time_base.num);
422 goto cleanup_stats_in;
423 }
424 unsupported_codec:
425 {
426 GST_DEBUG ("Unsupported codec - no caps found");
427 goto cleanup_stats_in;
428 }
429 open_codec_fail:
430 {
431 GST_DEBUG_OBJECT (ffmpegenc, "avenc_%s: Failed to open libav codec",
432 oclass->in_plugin->name);
433 goto close_codec;
434 }
435
436 pix_fmt_err:
437 {
438 GST_DEBUG_OBJECT (ffmpegenc,
439 "avenc_%s: AV wants different colourspace (%d given, %d wanted)",
440 oclass->in_plugin->name, pix_fmt, ffmpegenc->context->pix_fmt);
441 goto close_codec;
442 }
443
444 bad_input_fmt:
445 {
446 GST_DEBUG_OBJECT (ffmpegenc, "avenc_%s: Failed to determine input format",
447 oclass->in_plugin->name);
448 goto close_codec;
449 }
450 close_codec:
451 {
452 gst_ffmpeg_avcodec_close (ffmpegenc->context);
453 if (avcodec_get_context_defaults3 (ffmpegenc->context,
454 oclass->in_plugin) < 0)
455 GST_DEBUG_OBJECT (ffmpegenc, "Failed to set context defaults");
456 goto cleanup_stats_in;
457 }
458 cleanup_stats_in:
459 {
460 g_free (ffmpegenc->context->stats_in);
461 return FALSE;
462 }
463 }
464
465
466 static gboolean
gst_ffmpegvidenc_propose_allocation(GstVideoEncoder * encoder,GstQuery * query)467 gst_ffmpegvidenc_propose_allocation (GstVideoEncoder * encoder,
468 GstQuery * query)
469 {
470 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
471
472 return GST_VIDEO_ENCODER_CLASS (parent_class)->propose_allocation (encoder,
473 query);
474 }
475
476 static void
gst_ffmpegvidenc_free_avpacket(gpointer pkt)477 gst_ffmpegvidenc_free_avpacket (gpointer pkt)
478 {
479 av_packet_unref ((AVPacket *) pkt);
480 g_slice_free (AVPacket, pkt);
481 }
482
483 typedef struct
484 {
485 GstBuffer *buffer;
486 GstVideoFrame vframe;
487 } BufferInfo;
488
489 static void
buffer_info_free(void * opaque,guint8 * data)490 buffer_info_free (void *opaque, guint8 * data)
491 {
492 BufferInfo *info = opaque;
493
494 gst_video_frame_unmap (&info->vframe);
495 gst_buffer_unref (info->buffer);
496 g_slice_free (BufferInfo, info);
497 }
498
499 static enum AVStereo3DType
stereo_gst_to_av(GstVideoMultiviewMode mview_mode)500 stereo_gst_to_av (GstVideoMultiviewMode mview_mode)
501 {
502 switch (mview_mode) {
503 case GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE:
504 return AV_STEREO3D_SIDEBYSIDE;
505 case GST_VIDEO_MULTIVIEW_MODE_TOP_BOTTOM:
506 return AV_STEREO3D_TOPBOTTOM;
507 case GST_VIDEO_MULTIVIEW_MODE_FRAME_BY_FRAME:
508 return AV_STEREO3D_FRAMESEQUENCE;
509 case GST_VIDEO_MULTIVIEW_MODE_CHECKERBOARD:
510 return AV_STEREO3D_CHECKERBOARD;
511 case GST_VIDEO_MULTIVIEW_MODE_SIDE_BY_SIDE_QUINCUNX:
512 return AV_STEREO3D_SIDEBYSIDE_QUINCUNX;
513 case GST_VIDEO_MULTIVIEW_MODE_ROW_INTERLEAVED:
514 return AV_STEREO3D_LINES;
515 case GST_VIDEO_MULTIVIEW_MODE_COLUMN_INTERLEAVED:
516 return AV_STEREO3D_COLUMNS;
517 default:
518 break;
519 }
520 GST_WARNING ("Unsupported multiview mode - no mapping in libav");
521 return AV_STEREO3D_2D;
522 }
523
524 static void
gst_ffmpegvidenc_add_cc(GstBuffer * buffer,AVFrame * picture)525 gst_ffmpegvidenc_add_cc (GstBuffer * buffer, AVFrame * picture)
526 {
527 GstVideoCaptionMeta *cc_meta;
528 gpointer iter = NULL;
529
530 while ((cc_meta =
531 (GstVideoCaptionMeta *) gst_buffer_iterate_meta_filtered (buffer,
532 &iter, GST_VIDEO_CAPTION_META_API_TYPE))) {
533 AVFrameSideData *sd;
534
535 if (cc_meta->caption_type != GST_VIDEO_CAPTION_TYPE_CEA708_RAW)
536 continue;
537
538 sd = av_frame_new_side_data (picture, AV_FRAME_DATA_A53_CC, cc_meta->size);
539 memcpy (sd->data, cc_meta->data, cc_meta->size);
540 }
541 }
542
543 static GstFlowReturn
gst_ffmpegvidenc_send_frame(GstFFMpegVidEnc * ffmpegenc,GstVideoCodecFrame * frame)544 gst_ffmpegvidenc_send_frame (GstFFMpegVidEnc * ffmpegenc,
545 GstVideoCodecFrame * frame)
546 {
547 GstVideoInfo *info = &ffmpegenc->input_state->info;
548 BufferInfo *buffer_info;
549 guint c;
550 gint res;
551 GstFlowReturn ret = GST_FLOW_ERROR;
552 AVFrame *picture = NULL;
553
554 if (!frame)
555 goto send_frame;
556
557 picture = ffmpegenc->picture;
558
559 gst_ffmpegvidenc_add_cc (frame->input_buffer, picture);
560
561 if (GST_VIDEO_INFO_IS_INTERLACED (&ffmpegenc->input_state->info)) {
562 picture->interlaced_frame = TRUE;
563 picture->top_field_first =
564 GST_BUFFER_FLAG_IS_SET (frame->input_buffer, GST_VIDEO_BUFFER_FLAG_TFF)
565 || GST_VIDEO_INFO_FIELD_ORDER (&ffmpegenc->input_state->info) ==
566 GST_VIDEO_FIELD_ORDER_TOP_FIELD_FIRST;
567 picture->repeat_pict =
568 GST_BUFFER_FLAG_IS_SET (frame->input_buffer, GST_VIDEO_BUFFER_FLAG_RFF);
569 }
570
571 if (GST_VIDEO_INFO_MULTIVIEW_MODE (info) != GST_VIDEO_MULTIVIEW_MODE_NONE) {
572 AVStereo3D *stereo = av_stereo3d_create_side_data (picture);
573 stereo->type = stereo_gst_to_av (GST_VIDEO_INFO_MULTIVIEW_MODE (info));
574
575 if (GST_VIDEO_INFO_MULTIVIEW_FLAGS (info) &
576 GST_VIDEO_MULTIVIEW_FLAGS_RIGHT_VIEW_FIRST) {
577 stereo->flags = AV_STEREO3D_FLAG_INVERT;
578 }
579 }
580
581 if (GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME (frame))
582 picture->pict_type = AV_PICTURE_TYPE_I;
583
584 buffer_info = g_slice_new0 (BufferInfo);
585 buffer_info->buffer = gst_buffer_ref (frame->input_buffer);
586
587 if (!gst_video_frame_map (&buffer_info->vframe, info, frame->input_buffer,
588 GST_MAP_READ)) {
589 GST_ERROR_OBJECT (ffmpegenc, "Failed to map input buffer");
590 gst_buffer_unref (buffer_info->buffer);
591 g_slice_free (BufferInfo, buffer_info);
592 gst_video_codec_frame_unref (frame);
593 goto done;
594 }
595
596 /* Fill avpicture */
597 picture->buf[0] =
598 av_buffer_create (NULL, 0, buffer_info_free, buffer_info, 0);
599 for (c = 0; c < AV_NUM_DATA_POINTERS; c++) {
600 if (c < GST_VIDEO_INFO_N_COMPONENTS (info)) {
601 picture->data[c] = GST_VIDEO_FRAME_PLANE_DATA (&buffer_info->vframe, c);
602 picture->linesize[c] =
603 GST_VIDEO_FRAME_COMP_STRIDE (&buffer_info->vframe, c);
604 } else {
605 picture->data[c] = NULL;
606 picture->linesize[c] = 0;
607 }
608 }
609
610 picture->format = ffmpegenc->context->pix_fmt;
611 picture->width = GST_VIDEO_FRAME_WIDTH (&buffer_info->vframe);
612 picture->height = GST_VIDEO_FRAME_HEIGHT (&buffer_info->vframe);
613
614 picture->pts =
615 gst_ffmpeg_time_gst_to_ff (frame->pts /
616 ffmpegenc->context->ticks_per_frame, ffmpegenc->context->time_base);
617
618 send_frame:
619 res = avcodec_send_frame (ffmpegenc->context, picture);
620
621 if (picture)
622 av_frame_unref (picture);
623
624 if (res == 0)
625 ret = GST_FLOW_OK;
626 else if (res == AVERROR_EOF)
627 ret = GST_FLOW_EOS;
628
629 done:
630 return ret;
631 }
632
633 static GstFlowReturn
gst_ffmpegvidenc_receive_packet(GstFFMpegVidEnc * ffmpegenc,gboolean * got_packet,gboolean send)634 gst_ffmpegvidenc_receive_packet (GstFFMpegVidEnc * ffmpegenc,
635 gboolean * got_packet, gboolean send)
636 {
637 AVPacket *pkt;
638 GstBuffer *outbuf;
639 GstVideoCodecFrame *frame;
640 gint res;
641 GstFlowReturn ret = GST_FLOW_OK;
642
643 *got_packet = FALSE;
644
645 pkt = g_slice_new0 (AVPacket);
646
647 res = avcodec_receive_packet (ffmpegenc->context, pkt);
648
649 if (res == AVERROR (EAGAIN)) {
650 g_slice_free (AVPacket, pkt);
651 goto done;
652 } else if (res == AVERROR_EOF) {
653 ret = GST_FLOW_EOS;
654 goto done;
655 } else if (res < 0) {
656 ret = GST_FLOW_ERROR;
657 goto done;
658 }
659
660 *got_packet = TRUE;
661
662 /* save stats info if there is some as well as a stats file */
663 if (ffmpegenc->file && ffmpegenc->context->stats_out)
664 if (fprintf (ffmpegenc->file, "%s", ffmpegenc->context->stats_out) < 0)
665 GST_ELEMENT_ERROR (ffmpegenc, RESOURCE, WRITE,
666 (("Could not write to file \"%s\"."), ffmpegenc->filename),
667 GST_ERROR_SYSTEM);
668
669 /* Get oldest frame */
670 frame = gst_video_encoder_get_oldest_frame (GST_VIDEO_ENCODER (ffmpegenc));
671
672 if (send) {
673 outbuf =
674 gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, pkt->data,
675 pkt->size, 0, pkt->size, pkt, gst_ffmpegvidenc_free_avpacket);
676 frame->output_buffer = outbuf;
677
678 if (pkt->flags & AV_PKT_FLAG_KEY)
679 GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame);
680 else
681 GST_VIDEO_CODEC_FRAME_UNSET_SYNC_POINT (frame);
682 }
683
684 ret = gst_video_encoder_finish_frame (GST_VIDEO_ENCODER (ffmpegenc), frame);
685
686 done:
687 return ret;
688 }
689
690 static GstFlowReturn
gst_ffmpegvidenc_handle_frame(GstVideoEncoder * encoder,GstVideoCodecFrame * frame)691 gst_ffmpegvidenc_handle_frame (GstVideoEncoder * encoder,
692 GstVideoCodecFrame * frame)
693 {
694 GstFFMpegVidEnc *ffmpegenc = (GstFFMpegVidEnc *) encoder;
695 GstFlowReturn ret;
696 gboolean got_packet;
697
698 ret = gst_ffmpegvidenc_send_frame (ffmpegenc, frame);
699
700 if (ret != GST_FLOW_OK)
701 goto encode_fail;
702
703 gst_video_codec_frame_unref (frame);
704
705 do {
706 ret = gst_ffmpegvidenc_receive_packet (ffmpegenc, &got_packet, TRUE);
707 if (ret != GST_FLOW_OK)
708 break;
709 } while (got_packet);
710
711 done:
712 return ret;
713
714 /* We choose to be error-resilient */
715 encode_fail:
716 {
717 #ifndef GST_DISABLE_GST_DEBUG
718 GstFFMpegVidEncClass *oclass =
719 (GstFFMpegVidEncClass *) (G_OBJECT_GET_CLASS (ffmpegenc));
720 GST_ERROR_OBJECT (ffmpegenc,
721 "avenc_%s: failed to encode buffer", oclass->in_plugin->name);
722 #endif /* GST_DISABLE_GST_DEBUG */
723 /* avoid frame (and ts etc) piling up */
724 ret = gst_video_encoder_finish_frame (encoder, frame);
725 goto done;
726 }
727 }
728
729 static GstFlowReturn
gst_ffmpegvidenc_flush_buffers(GstFFMpegVidEnc * ffmpegenc,gboolean send)730 gst_ffmpegvidenc_flush_buffers (GstFFMpegVidEnc * ffmpegenc, gboolean send)
731 {
732 GstFlowReturn ret = GST_FLOW_OK;
733 gboolean got_packet;
734
735 GST_DEBUG_OBJECT (ffmpegenc, "flushing buffers with sending %d", send);
736
737 /* no need to empty codec if there is none */
738 if (!ffmpegenc->opened)
739 goto done;
740
741 ret = gst_ffmpegvidenc_send_frame (ffmpegenc, NULL);
742
743 if (ret != GST_FLOW_OK)
744 goto done;
745
746 do {
747 ret = gst_ffmpegvidenc_receive_packet (ffmpegenc, &got_packet, send);
748 if (ret != GST_FLOW_OK)
749 break;
750 } while (got_packet);
751
752 done:
753 return ret;
754 }
755
756 static void
gst_ffmpegvidenc_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)757 gst_ffmpegvidenc_set_property (GObject * object,
758 guint prop_id, const GValue * value, GParamSpec * pspec)
759 {
760 GstFFMpegVidEnc *ffmpegenc;
761
762 ffmpegenc = (GstFFMpegVidEnc *) (object);
763
764 if (ffmpegenc->opened) {
765 GST_WARNING_OBJECT (ffmpegenc,
766 "Can't change properties once decoder is setup !");
767 return;
768 }
769
770 switch (prop_id) {
771 case PROP_QUANTIZER:
772 ffmpegenc->quantizer = g_value_get_float (value);
773 break;
774 case PROP_PASS:
775 ffmpegenc->pass = g_value_get_enum (value);
776 break;
777 case PROP_FILENAME:
778 g_free (ffmpegenc->filename);
779 ffmpegenc->filename = g_value_dup_string (value);
780 break;
781 default:
782 if (!gst_ffmpeg_cfg_set_property (ffmpegenc->refcontext, value, pspec))
783 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
784 break;
785 }
786 }
787
788 static void
gst_ffmpegvidenc_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)789 gst_ffmpegvidenc_get_property (GObject * object,
790 guint prop_id, GValue * value, GParamSpec * pspec)
791 {
792 GstFFMpegVidEnc *ffmpegenc;
793
794 ffmpegenc = (GstFFMpegVidEnc *) (object);
795
796 switch (prop_id) {
797 case PROP_QUANTIZER:
798 g_value_set_float (value, ffmpegenc->quantizer);
799 break;
800 case PROP_PASS:
801 g_value_set_enum (value, ffmpegenc->pass);
802 break;
803 case PROP_FILENAME:
804 g_value_take_string (value, g_strdup (ffmpegenc->filename));
805 break;
806 default:
807 if (!gst_ffmpeg_cfg_get_property (ffmpegenc->refcontext, value, pspec))
808 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
809 break;
810 }
811 }
812
813 static gboolean
gst_ffmpegvidenc_flush(GstVideoEncoder * encoder)814 gst_ffmpegvidenc_flush (GstVideoEncoder * encoder)
815 {
816 GstFFMpegVidEnc *ffmpegenc = (GstFFMpegVidEnc *) encoder;
817
818 if (ffmpegenc->opened)
819 avcodec_flush_buffers (ffmpegenc->context);
820
821 return TRUE;
822 }
823
824 static gboolean
gst_ffmpegvidenc_start(GstVideoEncoder * encoder)825 gst_ffmpegvidenc_start (GstVideoEncoder * encoder)
826 {
827 GstFFMpegVidEnc *ffmpegenc = (GstFFMpegVidEnc *) encoder;
828 GstFFMpegVidEncClass *oclass =
829 (GstFFMpegVidEncClass *) G_OBJECT_GET_CLASS (ffmpegenc);
830
831 /* close old session */
832 gst_ffmpeg_avcodec_close (ffmpegenc->context);
833 if (avcodec_get_context_defaults3 (ffmpegenc->context, oclass->in_plugin) < 0) {
834 GST_DEBUG_OBJECT (ffmpegenc, "Failed to set context defaults");
835 return FALSE;
836 }
837
838 return TRUE;
839 }
840
841 static gboolean
gst_ffmpegvidenc_stop(GstVideoEncoder * encoder)842 gst_ffmpegvidenc_stop (GstVideoEncoder * encoder)
843 {
844 GstFFMpegVidEnc *ffmpegenc = (GstFFMpegVidEnc *) encoder;
845
846 gst_ffmpegvidenc_flush_buffers (ffmpegenc, FALSE);
847 gst_ffmpeg_avcodec_close (ffmpegenc->context);
848 ffmpegenc->opened = FALSE;
849
850 if (ffmpegenc->input_state) {
851 gst_video_codec_state_unref (ffmpegenc->input_state);
852 ffmpegenc->input_state = NULL;
853 }
854
855 return TRUE;
856 }
857
858 static GstFlowReturn
gst_ffmpegvidenc_finish(GstVideoEncoder * encoder)859 gst_ffmpegvidenc_finish (GstVideoEncoder * encoder)
860 {
861 GstFFMpegVidEnc *ffmpegenc = (GstFFMpegVidEnc *) encoder;
862
863 return gst_ffmpegvidenc_flush_buffers (ffmpegenc, TRUE);
864 }
865
866 gboolean
gst_ffmpegvidenc_register(GstPlugin * plugin)867 gst_ffmpegvidenc_register (GstPlugin * plugin)
868 {
869 GTypeInfo typeinfo = {
870 sizeof (GstFFMpegVidEncClass),
871 (GBaseInitFunc) gst_ffmpegvidenc_base_init,
872 NULL,
873 (GClassInitFunc) gst_ffmpegvidenc_class_init,
874 NULL,
875 NULL,
876 sizeof (GstFFMpegVidEnc),
877 0,
878 (GInstanceInitFunc) gst_ffmpegvidenc_init,
879 };
880 GType type;
881 AVCodec *in_plugin;
882 void *i = 0;
883
884 GST_LOG ("Registering encoders");
885
886 while ((in_plugin = (AVCodec *) av_codec_iterate (&i))) {
887 gchar *type_name;
888
889 /* Skip non-AV codecs */
890 if (in_plugin->type != AVMEDIA_TYPE_VIDEO)
891 continue;
892
893 /* no quasi codecs, please */
894 if (in_plugin->id == AV_CODEC_ID_RAWVIDEO ||
895 in_plugin->id == AV_CODEC_ID_V210 ||
896 in_plugin->id == AV_CODEC_ID_V210X ||
897 in_plugin->id == AV_CODEC_ID_V308 ||
898 in_plugin->id == AV_CODEC_ID_V408 ||
899 in_plugin->id == AV_CODEC_ID_V410 ||
900 in_plugin->id == AV_CODEC_ID_R210
901 || in_plugin->id == AV_CODEC_ID_AYUV
902 || in_plugin->id == AV_CODEC_ID_Y41P
903 || in_plugin->id == AV_CODEC_ID_012V
904 || in_plugin->id == AV_CODEC_ID_YUV4
905 #if AV_VERSION_INT (LIBAVCODEC_VERSION_MAJOR, LIBAVCODEC_VERSION_MINOR, LIBAVCODEC_VERSION_MICRO) >= \
906 AV_VERSION_INT (57,4,0)
907 || in_plugin->id == AV_CODEC_ID_WRAPPED_AVFRAME
908 #endif
909 || in_plugin->id == AV_CODEC_ID_ZLIB) {
910 continue;
911 }
912
913 /* No encoders depending on external libraries (we don't build them, but
914 * people who build against an external ffmpeg might have them.
915 * We have native gstreamer plugins for all of those libraries anyway. */
916 if (!strncmp (in_plugin->name, "lib", 3)) {
917 GST_DEBUG
918 ("Not using external library encoder %s. Use the gstreamer-native ones instead.",
919 in_plugin->name);
920 continue;
921 }
922
923 if (strstr (in_plugin->name, "vaapi")) {
924 GST_DEBUG
925 ("Ignoring VAAPI encoder %s. We can't handle this outside of ffmpeg",
926 in_plugin->name);
927 continue;
928 }
929
930 if (strstr (in_plugin->name, "nvenc")) {
931 GST_DEBUG
932 ("Ignoring nvenc encoder %s. We can't handle this outside of ffmpeg",
933 in_plugin->name);
934 continue;
935 }
936
937 if (g_str_has_suffix (in_plugin->name, "_qsv")) {
938 GST_DEBUG
939 ("Ignoring qsv encoder %s. We can't handle this outside of ffmpeg",
940 in_plugin->name);
941 continue;
942 }
943
944 if (g_str_has_suffix (in_plugin->name, "_v4l2m2m")) {
945 GST_DEBUG
946 ("Ignoring V4L2 mem-to-mem encoder %s. We can't handle this outside of ffmpeg",
947 in_plugin->name);
948 continue;
949 }
950
951 /* only video encoders */
952 if (!av_codec_is_encoder (in_plugin)
953 || in_plugin->type != AVMEDIA_TYPE_VIDEO)
954 continue;
955
956 /* FIXME : We should have a method to know cheaply whether we have a mapping
957 * for the given plugin or not */
958
959 GST_DEBUG ("Trying plugin %s [%s]", in_plugin->name, in_plugin->long_name);
960
961 /* no codecs for which we're GUARANTEED to have better alternatives */
962 if (!strcmp (in_plugin->name, "gif")) {
963 GST_LOG ("Ignoring encoder %s", in_plugin->name);
964 continue;
965 }
966
967 /* construct the type */
968 type_name = g_strdup_printf ("avenc_%s", in_plugin->name);
969
970 type = g_type_from_name (type_name);
971
972 if (!type) {
973
974 /* create the glib type now */
975 type =
976 g_type_register_static (GST_TYPE_VIDEO_ENCODER, type_name, &typeinfo,
977 0);
978 g_type_set_qdata (type, GST_FFENC_PARAMS_QDATA, (gpointer) in_plugin);
979
980 {
981 static const GInterfaceInfo preset_info = {
982 NULL,
983 NULL,
984 NULL
985 };
986 g_type_add_interface_static (type, GST_TYPE_PRESET, &preset_info);
987 }
988 }
989
990 if (!gst_element_register (plugin, type_name, GST_RANK_SECONDARY, type)) {
991 g_free (type_name);
992 return FALSE;
993 }
994
995 g_free (type_name);
996 }
997
998 GST_LOG ("Finished registering encoders");
999
1000 return TRUE;
1001 }
1002