Patch #34204: [Render Animation] Fails with "Error: Specified sample_fmt is not suppo...
[blender.git] / source / blender / blenkernel / intern / writeffmpeg.c
index cd07ac3..082ae38 100644 (file)
@@ -111,6 +111,12 @@ static void delete_picture(AVFrame *f)
        }
 }
 
+static int request_float_audio_buffer(int codec_id)
+{
+       /* If any of these codecs, we prefer the float sample format (if supported) */
+       return codec_id == CODEC_ID_AAC || codec_id == CODEC_ID_AC3 || codec_id == CODEC_ID_VORBIS;
+}
+
 #ifdef WITH_AUDASPACE
 static int write_audio_frame(void) 
 {
@@ -177,58 +183,71 @@ static AVFrame *alloc_picture(int pix_fmt, int width, int height)
 static const char **get_file_extensions(int format)
 {
        switch (format) {
-               case FFMPEG_DV: {
+               case FFMPEG_DV:
+               {
                        static const char *rv[] = { ".dv", NULL };
                        return rv;
                }
-               case FFMPEG_MPEG1: {
+               case FFMPEG_MPEG1:
+               {
                        static const char *rv[] = { ".mpg", ".mpeg", NULL };
                        return rv;
                }
-               case FFMPEG_MPEG2: {
+               case FFMPEG_MPEG2:
+               {
                        static const char *rv[] = { ".dvd", ".vob", ".mpg", ".mpeg", NULL };
                        return rv;
                }
-               case FFMPEG_MPEG4: {
+               case FFMPEG_MPEG4:
+               {
                        static const char *rv[] = { ".mp4", ".mpg", ".mpeg", NULL };
                        return rv;
                }
-               case FFMPEG_AVI: {
+               case FFMPEG_AVI:
+               {
                        static const char *rv[] = { ".avi", NULL };
                        return rv;
                }
-               case FFMPEG_MOV: {
+               case FFMPEG_MOV:
+               {
                        static const char *rv[] = { ".mov", NULL };
                        return rv;
                }
-               case FFMPEG_H264: {
+               case FFMPEG_H264:
+               {
                        /* FIXME: avi for now... */
                        static const char *rv[] = { ".avi", NULL };
                        return rv;
                }
 
-               case FFMPEG_XVID: {
+               case FFMPEG_XVID:
+               {
                        /* FIXME: avi for now... */
                        static const char *rv[] = { ".avi", NULL };
                        return rv;
                }
-               case FFMPEG_FLV: {
+               case FFMPEG_FLV:
+               {
                        static const char *rv[] = { ".flv", NULL };
                        return rv;
                }
-               case FFMPEG_MKV: {
+               case FFMPEG_MKV:
+               {
                        static const char *rv[] = { ".mkv", NULL };
                        return rv;
                }
-               case FFMPEG_OGG: {
+               case FFMPEG_OGG:
+               {
                        static const char *rv[] = { ".ogg", ".ogv", NULL };
                        return rv;
                }
-               case FFMPEG_MP3: {
+               case FFMPEG_MP3:
+               {
                        static const char *rv[] = { ".mp3", NULL };
                        return rv;
                }
-               case FFMPEG_WAV: {
+               case FFMPEG_WAV:
+               {
                        static const char *rv[] = { ".wav", NULL };
                        return rv;
                }
@@ -276,7 +295,7 @@ static int write_video_frame(RenderData *rd, int cfra, AVFrame *frame, ReportLis
        }
 
        if (!success)
-               BKE_report(reports, RPT_ERROR, "Error writing frame.");
+               BKE_report(reports, RPT_ERROR, "Error writing frame");
 
        return success;
 }
@@ -294,7 +313,7 @@ static AVFrame *generate_video_frame(uint8_t *pixels, ReportList *reports)
        if (c->pix_fmt != PIX_FMT_BGR32) {
                rgb_frame = alloc_picture(PIX_FMT_BGR32, width, height);
                if (!rgb_frame) {
-                       BKE_report(reports, RPT_ERROR, "Couldn't allocate temporary frame.");
+                       BKE_report(reports, RPT_ERROR, "Could not allocate temporary frame");
                        return NULL;
                }
        }
@@ -355,7 +374,7 @@ static void set_ffmpeg_property_option(AVCodecContext *c, IDProperty *prop)
 {
        char name[128];
        char *param;
-       const AVOption *rv = NULL;
+       int fail = TRUE;
 
        PRINT("FFMPEG expert option: %s: ", prop->name);
 
@@ -370,30 +389,30 @@ static void set_ffmpeg_property_option(AVCodecContext *c, IDProperty *prop)
        switch (prop->type) {
                case IDP_STRING:
                        PRINT("%s.\n", IDP_String(prop));
-                       av_set_string3(c, prop->name, IDP_String(prop), 1, &rv);
+                       fail = av_opt_set(c, prop->name, IDP_String(prop), 0);
                        break;
                case IDP_FLOAT:
                        PRINT("%g.\n", IDP_Float(prop));
-                       rv = av_set_double(c, prop->name, IDP_Float(prop));
+                       fail = av_opt_set_double(c, prop->name, IDP_Float(prop), 0);
                        break;
                case IDP_INT:
                        PRINT("%d.\n", IDP_Int(prop));
 
                        if (param) {
                                if (IDP_Int(prop)) {
-                                       av_set_string3(c, name, param, 1, &rv);
+                                       fail = av_opt_set(c, name, param, 0);
                                }
                                else {
                                        return;
                                }
                        }
                        else {
-                               rv = av_set_int(c, prop->name, IDP_Int(prop));
+                               fail = av_opt_set_int(c, prop->name, IDP_Int(prop), 0);
                        }
                        break;
        }
 
-       if (!rv) {
+       if (fail) {
                PRINT("ffmpeg-option not supported: %s! Skipping.\n", prop->name);
        }
 }
@@ -446,8 +465,9 @@ static AVStream *alloc_video_stream(RenderData *rd, int codec_id, AVFormatContex
 
        error[0] = '\0';
 
-       st = av_new_stream(of, 0);
+       st = avformat_new_stream(of, NULL);
        if (!st) return NULL;
+       st->id = 0;
 
        /* Set up the codec context */
        
@@ -465,9 +485,7 @@ static AVStream *alloc_video_stream(RenderData *rd, int codec_id, AVFormatContex
                c->time_base.den = 2997;
                c->time_base.num = 100;
        }
-       else if ((double) ((int) rd->frs_sec_base) ==
-                rd->frs_sec_base)
-       {
+       else if ((float) ((int) rd->frs_sec_base) == rd->frs_sec_base) {
                c->time_base.den = rd->frs_sec;
                c->time_base.num = (int) rd->frs_sec_base;
        }
@@ -481,8 +499,15 @@ static AVStream *alloc_video_stream(RenderData *rd, int codec_id, AVFormatContex
        c->rc_max_rate = rd->ffcodecdata.rc_max_rate * 1000;
        c->rc_min_rate = rd->ffcodecdata.rc_min_rate * 1000;
        c->rc_buffer_size = rd->ffcodecdata.rc_buffer_size * 1024;
+
+#if 0
+       /* this options are not set in ffmpeg.c and leads to artifacts with MPEG-4
+        * see #33586: Encoding to mpeg4 makes first frame(s) blocky
+        */
        c->rc_initial_buffer_occupancy = rd->ffcodecdata.rc_buffer_size * 3 / 4;
        c->rc_buffer_aggressivity = 1.0;
+#endif
+
        c->me_method = ME_EPZS;
        
        codec = avcodec_find_encoder(c->codec_id);
@@ -518,16 +543,7 @@ static AVStream *alloc_video_stream(RenderData *rd, int codec_id, AVFormatContex
        }
 
        if (codec_id == CODEC_ID_FFV1) {
-#ifdef FFMPEG_FFV1_ALPHA_SUPPORTED
-               if (rd->im_format.planes == R_IMF_PLANES_RGBA) {
-                       c->pix_fmt = PIX_FMT_RGB32;
-               }
-               else {
-                       c->pix_fmt = PIX_FMT_BGR0;
-               }
-#else
                c->pix_fmt = PIX_FMT_RGB32;
-#endif
        }
 
        if (codec_id == CODEC_ID_QTRLE) {
@@ -536,6 +552,12 @@ static AVStream *alloc_video_stream(RenderData *rd, int codec_id, AVFormatContex
                }
        }
 
+       if (codec_id == CODEC_ID_PNG) {
+               if (rd->im_format.planes == R_IMF_PLANES_RGBA) {
+                       c->pix_fmt = PIX_FMT_ARGB;
+               }
+       }
+
        if ((of->oformat->flags & AVFMT_GLOBALHEADER)
 //             || !strcmp(of->oformat->name, "mp4")
 //         || !strcmp(of->oformat->name, "mov")
@@ -559,7 +581,7 @@ static AVStream *alloc_video_stream(RenderData *rd, int codec_id, AVFormatContex
 
        set_ffmpeg_properties(rd, c, "video");
        
-       if (avcodec_open(c, codec) < 0) {
+       if (avcodec_open2(c, codec, NULL) < 0) {
                BLI_strncpy(error, IMB_ffmpeg_last_error(), error_size);
                return NULL;
        }
@@ -588,14 +610,17 @@ static AVStream *alloc_video_stream(RenderData *rd, int codec_id, AVFormatContex
 
 /* Prepare an audio stream for the output file */
 
-static AVStream *alloc_audio_stream(RenderData *rd, int codec_id, AVFormatContext *of)
+static AVStream *alloc_audio_stream(RenderData *rd, int codec_id, AVFormatContext *of, char *error, int error_size)
 {
        AVStream *st;
        AVCodecContext *c;
        AVCodec *codec;
 
-       st = av_new_stream(of, 1);
+       error[0] = '\0';
+
+       st = avformat_new_stream(of, NULL);
        if (!st) return NULL;
+       st->id = 1;
 
        c = st->codec;
        c->codec_id = codec_id;
@@ -605,16 +630,60 @@ static AVStream *alloc_audio_stream(RenderData *rd, int codec_id, AVFormatContex
        c->bit_rate = ffmpeg_audio_bitrate * 1000;
        c->sample_fmt = AV_SAMPLE_FMT_S16;
        c->channels = rd->ffcodecdata.audio_channels;
+
+       if (request_float_audio_buffer(codec_id)) {
+               /* mainly for AAC codec which is experimental */
+               c->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
+               c->sample_fmt = AV_SAMPLE_FMT_FLT;
+       }
+
        codec = avcodec_find_encoder(c->codec_id);
        if (!codec) {
                //XXX error("Couldn't find a valid audio codec");
                return NULL;
        }
 
+       if (codec->sample_fmts) {
+               /* check if the prefered sample format for this codec is supported.
+                * this is because, depending on the version of libav, and with the whole ffmpeg/libav fork situation,
+                * you have various implementations around. float samples in particular are not always supported.
+                */
+               const enum AVSampleFormat *p = codec->sample_fmts;
+               for (; *p!=-1; p++) {
+                       if (*p == st->codec->sample_fmt)
+                               break;
+               }
+               if (*p == -1) {
+                       /* sample format incompatible with codec. Defaulting to a format known to work */
+                       st->codec->sample_fmt = codec->sample_fmts[0];
+               }
+       }
+
+       if (c->sample_fmt == AV_SAMPLE_FMT_FLTP) {
+               BLI_strncpy(error, "Requested audio codec requires planar float sample format, which is not supported yet", error_size);
+               return NULL;
+       }
+
+       if (codec->supported_samplerates) {
+               const int *p = codec->supported_samplerates;
+               int best = 0;
+               int best_dist = INT_MAX;
+               for (; *p; p++){
+                       int dist = abs(st->codec->sample_rate - *p);
+                       if (dist < best_dist){
+                               best_dist = dist;
+                               best = *p;
+                       }
+               }
+               /* best is the closest supported sample rate (same as selected if best_dist == 0) */
+               st->codec->sample_rate = best;
+       }
+
        set_ffmpeg_properties(rd, c, "audio");
 
-       if (avcodec_open(c, codec) < 0) {
+       if (avcodec_open2(c, codec, NULL) < 0) {
                //XXX error("Couldn't initialize audio codec");
+               BLI_strncpy(error, IMB_ffmpeg_last_error(), error_size);
                return NULL;
        }
 
@@ -635,7 +704,12 @@ static AVStream *alloc_audio_stream(RenderData *rd, int codec_id, AVFormatContex
 
        audio_output_buffer = (uint8_t *) av_malloc(audio_outbuf_size);
 
-       audio_input_buffer = (uint8_t *) av_malloc(audio_input_samples * c->channels * sizeof(int16_t));
+       if (c->sample_fmt == AV_SAMPLE_FMT_FLT) {
+               audio_input_buffer = (uint8_t *) av_malloc(audio_input_samples * c->channels * sizeof(float));
+       }
+       else {
+               audio_input_buffer = (uint8_t *) av_malloc(audio_input_samples * c->channels * sizeof(int16_t));
+       }
 
        audio_time = 0.0f;
 
@@ -682,12 +756,12 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report
        
        exts = get_file_extensions(ffmpeg_type);
        if (!exts) {
-               BKE_report(reports, RPT_ERROR, "No valid formats found.");
+               BKE_report(reports, RPT_ERROR, "No valid formats found");
                return 0;
        }
        fmt = av_guess_format(NULL, exts[0], NULL);
        if (!fmt) {
-               BKE_report(reports, RPT_ERROR, "No valid formats found.");
+               BKE_report(reports, RPT_ERROR, "No valid formats found");
                return 0;
        }
 
@@ -782,7 +856,7 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report
                        if (error[0])
                                BKE_report(reports, RPT_ERROR, error);
                        else
-                               BKE_report(reports, RPT_ERROR, "Error initializing video stream.");
+                               BKE_report(reports, RPT_ERROR, "Error initializing video stream");
 
                        av_dict_free(&opts);
                        return 0;
@@ -790,23 +864,27 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report
        }
 
        if (ffmpeg_audio_codec != CODEC_ID_NONE) {
-               audio_stream = alloc_audio_stream(rd, fmt->audio_codec, of);
+               audio_stream = alloc_audio_stream(rd, fmt->audio_codec, of, error, sizeof(error));
                if (!audio_stream) {
-                       BKE_report(reports, RPT_ERROR, "Error initializing audio stream.");
+                       if (error[0])
+                               BKE_report(reports, RPT_ERROR, error);
+                       else
+                               BKE_report(reports, RPT_ERROR, "Error initializing audio stream");
                        av_dict_free(&opts);
                        return 0;
                }
        }
        if (!(fmt->flags & AVFMT_NOFILE)) {
                if (avio_open(&of->pb, name, AVIO_FLAG_WRITE) < 0) {
-                       BKE_report(reports, RPT_ERROR, "Could not open file for writing.");
+                       BKE_report(reports, RPT_ERROR, "Could not open file for writing");
                        av_dict_free(&opts);
                        return 0;
                }
        }
        if (avformat_write_header(of, NULL) < 0) {
-               BKE_report(reports, RPT_ERROR, "Could not initialize streams. Probably unsupported codec combination.");
-                       av_dict_free(&opts);
+               BKE_report(reports, RPT_ERROR, "Could not initialize streams, probably unsupported codec combination");
+               av_dict_free(&opts);
+               avio_close(of->pb);
                return 0;
        }
 
@@ -826,7 +904,7 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report
  * inter-frames (H.264 B-frames, for example), it can output the frames 
  * in a different order from the one it was given.
  * For example, when sending frames 1, 2, 3, 4 to the encoder, it may write
- * them in the order 1, 4, 2, 3 - first the two frames used for predition, 
+ * them in the order 1, 4, 2, 3 - first the two frames used for prediction,
  * and then the bidirectionally-predicted frames. What this means in practice 
  * is that the encoder may not immediately produce one output frame for each 
  * input frame. These delayed frames must be flushed before we close the 
@@ -834,7 +912,7 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report
  * parameter.
  * </p>
  */
-void flush_ffmpeg(void)
+static void flush_ffmpeg(void)
 {
        int outsize = 0;
        int ret = 0;
@@ -901,8 +979,7 @@ void BKE_ffmpeg_filepath_get(char *string, RenderData *rd)
        }
 
        while (*fe) {
-               if (BLI_strcasecmp(string + strlen(string) - strlen(*fe), *fe) == 0)
-               {
+               if (BLI_strcasecmp(string + strlen(string) - strlen(*fe), *fe) == 0) {
                        break;
                }
                fe++;
@@ -933,7 +1010,12 @@ int BKE_ffmpeg_start(struct Scene *scene, RenderData *rd, int rectx, int recty,
                AVCodecContext *c = audio_stream->codec;
                AUD_DeviceSpecs specs;
                specs.channels = c->channels;
-               specs.format = AUD_FORMAT_S16;
+               if (c->sample_fmt == AV_SAMPLE_FMT_FLT) {
+                       specs.format = AUD_FORMAT_FLOAT32;
+               }
+               else {
+                       specs.format = AUD_FORMAT_S16;
+               }
                specs.rate = rd->ffcodecdata.audio_mixrate;
                audio_mixdown_device = sound_mixdown(scene, specs, rd->sfra, rd->ffcodecdata.audio_volume);
 #ifdef FFMPEG_CODEC_TIME_BASE
@@ -945,7 +1027,6 @@ int BKE_ffmpeg_start(struct Scene *scene, RenderData *rd, int rectx, int recty,
        return success;
 }
 
-void BKE_ffmpeg_end(void);
 static void end_ffmpeg_impl(int is_autosplit);
 
 #ifdef WITH_AUDASPACE
@@ -970,7 +1051,7 @@ int BKE_ffmpeg_append(RenderData *rd, int start_frame, int frame, int *pixels, i
 
        PRINT("Writing frame %i, render width=%d, render height=%d\n", frame, rectx, recty);
 
-// why is this done before writing the video frame and again at end_ffmpeg?
+/* why is this done before writing the video frame and again at end_ffmpeg? */
 //     write_audio_frames(frame / (((double)rd->frs_sec) / rd->frs_sec_base));
 
        if (video_stream) {
@@ -987,7 +1068,7 @@ int BKE_ffmpeg_append(RenderData *rd, int start_frame, int frame, int *pixels, i
        }
 
 #ifdef WITH_AUDASPACE
-       write_audio_frames((frame - rd->sfra) / (((double)rd->frs_sec) / rd->frs_sec_base));
+       write_audio_frames((frame - rd->sfra) / (((double)rd->frs_sec) / (double)rd->frs_sec_base));
 #endif
        return success;
 }
@@ -1109,7 +1190,7 @@ IDProperty *BKE_ffmpeg_property_add(RenderData *rd, const char *type, int opt_in
        
        val.i = 0;
 
-       avcodec_get_context_defaults(&c);
+       avcodec_get_context_defaults3(&c, NULL);
 
        o = c.av_class->option + opt_index;
        parent = c.av_class->option + parent_index;
@@ -1140,23 +1221,23 @@ IDProperty *BKE_ffmpeg_property_add(RenderData *rd, const char *type, int opt_in
        }
 
        switch (o->type) {
-               case FF_OPT_TYPE_INT:
-               case FF_OPT_TYPE_INT64:
+               case AV_OPT_TYPE_INT:
+               case AV_OPT_TYPE_INT64:
                        val.i = FFMPEG_DEF_OPT_VAL_INT(o);
                        idp_type = IDP_INT;
                        break;
-               case FF_OPT_TYPE_DOUBLE:
-               case FF_OPT_TYPE_FLOAT:
+               case AV_OPT_TYPE_DOUBLE:
+               case AV_OPT_TYPE_FLOAT:
                        val.f = FFMPEG_DEF_OPT_VAL_DOUBLE(o);
                        idp_type = IDP_FLOAT;
                        break;
-               case FF_OPT_TYPE_STRING:
+               case AV_OPT_TYPE_STRING:
                        val.string.str = (char *)"                                                                               ";
                        val.string.len = 80;
 /*             val.str = (char *)"                                                                               ";*/
                        idp_type = IDP_STRING;
                        break;
-               case FF_OPT_TYPE_CONST:
+               case AV_OPT_TYPE_CONST:
                        val.i = 1;
                        idp_type = IDP_INT;
                        break;
@@ -1196,7 +1277,7 @@ int BKE_ffmpeg_property_add_string(RenderData *rd, const char *type, const char
        char *param;
        IDProperty *prop = NULL;
        
-       avcodec_get_context_defaults(&c);
+       avcodec_get_context_defaults3(&c, NULL);
 
        strncpy(name_, str, sizeof(name_));
 
@@ -1213,15 +1294,15 @@ int BKE_ffmpeg_property_add_string(RenderData *rd, const char *type, const char
                while (*param == ' ') param++;
        }
        
-       o = my_av_find_opt(&c, name, NULL, 0, 0);       
+       o = my_av_find_opt(&c, name, NULL, 0, 0);
        if (!o) {
                return 0;
        }
-       if (param && o->type == FF_OPT_TYPE_CONST) {
+       if (param && o->type == AV_OPT_TYPE_CONST) {
                return 0;
        }
-       if (param && o->type != FF_OPT_TYPE_CONST && o->unit) {
-               p = my_av_find_opt(&c, param, o->unit, 0, 0);   
+       if (param && o->type != AV_OPT_TYPE_CONST && o->unit) {
+               p = my_av_find_opt(&c, param, o->unit, 0, 0);
                if (p) {
                        prop = BKE_ffmpeg_property_add(rd, (char *) type, p - c.av_class->option, o - c.av_class->option);
                }
@@ -1458,6 +1539,9 @@ int BKE_ffmpeg_alpha_channel_is_supported(RenderData *rd)
        if (codec == CODEC_ID_QTRLE)
                return TRUE;
 
+       if (codec == CODEC_ID_PNG)
+               return TRUE;
+
 #ifdef FFMPEG_FFV1_ALPHA_SUPPORTED
        if (codec == CODEC_ID_FFV1)
                return TRUE;