avvidenc: Fix error propagation
[platform/upstream/gstreamer.git] / ext / libav / gstavvidenc.c
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }