[FFmpeg-devel,1/3] ffmpeg: use avformat_init_output to initialize output files

Submitted by James Almer on Nov. 26, 2017, 8:51 p.m.

Details

Message ID 20171126205104.1064-1-jamrial@gmail.com
State New
Headers show

Commit Message

James Almer Nov. 26, 2017, 8:51 p.m.
Postpone writing the header until the first output packet is ready to be
written.
This makes sure any stream parameter change that could take place while
processing an input frame will be taken into account when writing the
output file header.

Signed-off-by: James Almer <jamrial@gmail.com>
---
 fftools/ffmpeg.c | 31 ++++++++++++++++++++++++++-----
 fftools/ffmpeg.h |  3 +++
 2 files changed, 29 insertions(+), 5 deletions(-)

Comments

Michael Niedermayer Nov. 27, 2017, 9:25 p.m.
On Sun, Nov 26, 2017 at 05:51:02PM -0300, James Almer wrote:
> Postpone writing the header until the first output packet is ready to be
> written.
> This makes sure any stream parameter change that could take place while
> processing an input frame will be taken into account when writing the
> output file header.
> 
> Signed-off-by: James Almer <jamrial@gmail.com>
> ---
>  fftools/ffmpeg.c | 31 ++++++++++++++++++++++++++-----
>  fftools/ffmpeg.h |  3 +++
>  2 files changed, 29 insertions(+), 5 deletions(-)

This changes (generates a file before, does not afterwards)
./ffmpeg  -i ~/tickets/5114/test_case.ivf -an -y test.mp4
https://trac.ffmpeg.org/attachment/ticket/5114/test_case.ivf

ill see if i can find a failure that is more clear, as this one
arguable doesnt produce a very usefull file

[...]
James Almer Nov. 27, 2017, 9:44 p.m.
On 11/27/2017 6:25 PM, Michael Niedermayer wrote:
> On Sun, Nov 26, 2017 at 05:51:02PM -0300, James Almer wrote:
>> Postpone writing the header until the first output packet is ready to be
>> written.
>> This makes sure any stream parameter change that could take place while
>> processing an input frame will be taken into account when writing the
>> output file header.
>>
>> Signed-off-by: James Almer <jamrial@gmail.com>
>> ---
>>  fftools/ffmpeg.c | 31 ++++++++++++++++++++++++++-----
>>  fftools/ffmpeg.h |  3 +++
>>  2 files changed, 29 insertions(+), 5 deletions(-)
> 
> This changes (generates a file before, does not afterwards)
> ./ffmpeg  -i ~/tickets/5114/test_case.ivf -an -y test.mp4
> https://trac.ffmpeg.org/attachment/ticket/5114/test_case.ivf
> 
> ill see if i can find a failure that is more clear, as this one
> arguable doesnt produce a very usefull file

Note the log message you get after this patch: "Nothing was written into
output file 0 (test.mp4), because at least one of its streams received
no packets."

Before this patch, of->header_written was set to 1 after calling
avformat_write_header even if no header was really written (Because of
the delayed header code in lavf I'm removing in patch 2), so ffmpeg.c
instead of printing the above log would call av_write_trailer() which,
again, would trigger the delayed header code in lavf and write the
header and the trailer in one go, but no packets in between.

After this patch, where we used to call avformat_write_header we're now
calling avformat_init_output, so of->header_written is 0 at the time the
invalid input data error happens, and as such av_write_trailer is
effectively never called.

Patch hide | download patch | download mbox

diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c
index 0c16e75ab0..07476e88e7 100644
--- a/fftools/ffmpeg.c
+++ b/fftools/ffmpeg.c
@@ -700,7 +700,7 @@  static void write_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost, int u
         ost->frame_number++;
     }
 
-    if (!of->header_written) {
+    if (!of->initialized) {
         AVPacket tmp_pkt = {0};
         /* the muxer is not initialized yet, buffer the packet */
         if (!av_fifo_space(ost->muxing_queue)) {
@@ -804,6 +804,17 @@  static void write_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost, int u
               );
     }
 
+    if (!of->header_written) {
+        ret = avformat_write_header(s, &of->opts);
+        if (ret < 0) {
+            av_log(NULL, AV_LOG_ERROR,
+                   "Could not write header for output file #%d: %s\n",
+                   ost->file_index, av_err2str(ret));
+            exit_program(1);
+        }
+        of->header_written = 1;
+    }
+
     ret = av_interleaved_write_frame(s, pkt);
     if (ret < 0) {
         print_error("av_interleaved_write_frame()", ret);
@@ -2756,7 +2767,7 @@  static void print_sdp(void)
     AVFormatContext **avc;
 
     for (i = 0; i < nb_output_files; i++) {
-        if (!output_files[i]->header_written)
+        if (!output_files[i]->initialized)
             return;
     }
 
@@ -2947,16 +2958,26 @@  static int check_init_output_file(OutputFile *of, int file_index)
 
     of->ctx->interrupt_callback = int_cb;
 
-    ret = avformat_write_header(of->ctx, &of->opts);
+    ret = avformat_init_output(of->ctx, &of->opts);
     if (ret < 0) {
         av_log(NULL, AV_LOG_ERROR,
-               "Could not write header for output file #%d "
+               "Could not initialize output file #%d "
                "(incorrect codec parameters ?): %s\n",
                file_index, av_err2str(ret));
         return ret;
     }
     //assert_avoptions(of->opts);
-    of->header_written = 1;
+    of->initialized = ret;
+    if (!ret) {
+        ret = avformat_write_header(of->ctx, &of->opts);
+        if (ret < 0) {
+            av_log(NULL, AV_LOG_ERROR,
+                   "Could not write header for output file #%d: %s\n",
+                   file_index, av_err2str(ret));
+            return ret;
+        }
+        of->initialized = of->header_written = 1;
+    }
 
     av_dump_format(of->ctx, file_index, of->ctx->filename, 1);
 
diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h
index e0977e1bf1..c46ffd8b03 100644
--- a/fftools/ffmpeg.h
+++ b/fftools/ffmpeg.h
@@ -571,6 +571,9 @@  typedef struct OutputFile {
 
     int shortest;
 
+    // avformat_init_output() has been called for this file
+    int initialized;
+    // avformat_write_header() has been called for this file
     int header_written;
 } OutputFile;