[FFmpeg-trac] #6720(avformat:new): Failure to write last packet to stream

FFmpeg trac at avcodec.org
Fri Oct 6 01:19:19 EEST 2017


#6720: Failure to write last packet to stream
-------------------------------------+-------------------------------------
             Reporter:  pixelmyr     |                     Type:  defect
               Status:  new          |                 Priority:  normal
            Component:  avformat     |                  Version:
             Keywords:  avio, muxer  |  unspecified
             Blocking:               |               Blocked By:
Analyzed by developer:  0            |  Reproduced by developer:  0
-------------------------------------+-------------------------------------
 Hi there,

 I'm using the FFmpeg API to take a bitmap and encode it to a video stream.
 The individual video chunks must be sent over a `WebSocket`, so I
 implemented a custom AVIOContext in order to the write callbacks and pass
 these to a parent C# application which handles the networking.

 During testing, I wanted to only send a video that was only a single frame
 long. However, I noticed that my write callback was only fired to write
 the header, and was not fired for the frame I sent to be encoded. When I
 sent two frames, only the first of the frame was encoded along with the
 header, and so on. It appears that the last frame I send is never encoded.

 I am using VP9 encoding on yuv420p data with a webm muxer, but this bug
 appears to occur with other formats as well.

 My initialization code is below:

 {{{
 HRESULT WINAPI InitializeVideoEncoding(Encoder* encoder,
     LPCSTR codec, LPCSTR outputContainer, LPCSTR* encoderOptions, UINT
 encoderOptCount,
     LPCSTR* muxerOptions, UINT muxerOptCount, LPCSTR* miscOptions, UINT
 miscOptCount)
 {
     // Fill the encoder options
     Log("Loading encoder options.");
     LoadDictionary(&encoder->encoderOptions, encoderOptions,
 encoderOptCount);
     if (!encoder->encoderOptions && encoderOptCount > 0)
     {
         Log("Failed to initialize encoder options.");
         return E_FAIL;
     }

     Log("Loading muxer options.");
     LoadDictionary(&encoder->muxerOptions, muxerOptions, muxerOptCount);
     if (!encoder->muxerOptions && muxerOptCount > 0)
     {
         Log("Failed to initialize encoder options.");
         return E_FAIL;
     }

     Log("Loading misc options.");
     LoadDictionary(&encoder->miscOptions, miscOptions, miscOptCount);
     if (!encoder->miscOptions && muxerOptCount > 0)
     {
         Log("Failed to initialize misc options.");
         return E_FAIL;
     }

     // Create the output context
     // This is effectively the muxer we use for the stream
     avformat_alloc_output_context2(&encoder->outputFormatContext, NULL,
 outputContainer, NULL);

     #if _DEBUG
     encoder->outputFormatContext->debug |= FF_FDEBUG_TS;
     #endif

     if (!encoder->outputFormatContext)
     {
         Log("Couldn't create output format context.");
         return E_FAIL;
     }
     encoder->outputFormat = encoder->outputFormatContext->oformat;

     // need to manually specify some parameters
     if (av_opt_set_dict(encoder->outputFormatContext->priv_data,
                         &encoder->muxerOptions) < 0)
     {
         Log("Failed to set muxer params.");
         return E_FAIL;
     }

     if (av_dict_count(encoder->muxerOptions) > 0)
     {
         Log("Dumping unused muxer params:");
         DumpDictionary(encoder->muxerOptions);
     }

     // Create the output stream using the above muxer
     encoder->outputStream =
 avformat_new_stream(encoder->outputFormatContext, NULL);
     if (!encoder->outputStream)
     {
         Log("Couldn't create output stream.");
         return E_FAIL;
     }
     encoder->outputStream->id = encoder->outputFormatContext->nb_streams -
 1;

     // Find the codec
     encoder->codec = avcodec_find_encoder_by_name(codec);
     if (!encoder->codec)
     {
         Log("Couldn't find encoder.");
         return E_FAIL;
     }

     // Create the encoding context
     encoder->encodingContext = avcodec_alloc_context3(encoder->codec);
     if (!encoder->encodingContext)
     {
         Log("Couldn't create encoding context.");
         return E_FAIL;
     }

     // Set the basics
     encoder->encodingContext->width = encoder->width;
     encoder->encodingContext->height = encoder->height;

     // Open the codec, which pairs it with the encoding context
     int result = avcodec_open2(encoder->encodingContext, encoder->codec,
 &encoder->encoderOptions);
     if (result < 0)
     {
         LogFFmpegError(result, "Couldn't open codec.");
         return E_FAIL;
     }

     if (av_dict_count(encoder->encoderOptions) > 0)
     {
         Log("Dumping unused encoder params:");
         DumpDictionary(encoder->encoderOptions);
     }

     // Set some params afterwards
     encoder->outputStream->time_base =
 encoder->encodingContext->time_base;
     encoder->encodingContext->framerate =
 av_inv_q(encoder->encodingContext->time_base);
     encoder->outputStream->avg_frame_rate =
 encoder->encodingContext->framerate;

     if (encoder->outputFormat->flags & AVFMT_GLOBALHEADER)
         encoder->encodingContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

     // Copy necessary information to the stream
     result =
 avcodec_parameters_from_context(encoder->outputStream->codecpar,
                                              encoder->encodingContext);

     if (result < 0)
     {
         LogFFmpegError(result, "Couldn't copy stream parameters.");
         return E_FAIL;
     }

     av_dump_format(encoder->outputFormatContext, 0, NULL, 1);

     // Initialize IO

     // Grab the IO buffer size
     {
         const char* frameBufKey = "frame_buf_size";
         encoder->ioBufSize = 131072;
         AVDictionaryEntry* e = av_dict_get(encoder->miscOptions,
             frameBufKey,
             NULL, 0);
         if (e)
         {
             // Set the value and remove from the list.
             encoder->ioBufSize = strtol(e->value, NULL, 10);
             av_dict_set(&encoder->miscOptions, frameBufKey, NULL, 0);
         }
     }

     encoder->ioBuf = (LPBYTE)av_malloc(encoder->ioBufSize);
     Log("Encoder IO buffer size: %d", encoder->ioBufSize);

     AVIOContext* ioContext = avio_alloc_context(encoder->ioBuf,
                                                 (int)encoder->ioBufSize,
                                                 1,
                                                 encoder,
                                                 NULL,
                                                 WriteStreamCallback,
                                                 NULL);
     encoder->outputFormatContext->pb = ioContext;
     encoder->outputFormatContext->flags |= AVFMT_FLAG_CUSTOM_IO;

     if (av_dict_count(encoder->miscOptions) > 0)
     {
         Log("Dumping unused misc params:");
         DumpDictionary(encoder->miscOptions);
     }

     result = avformat_write_header(encoder->outputFormatContext, NULL);
     if (result < 0)
     {
         LogFFmpegError(result, "Couldn't write header.");
         return E_FAIL;
     }

     return S_OK;
 }
 }}}

 And my IO function:

 {{{
 int WINAPI WriteStreamCallback(void* opaque, unsigned char* buf, int
 bufSize)
 {
         Encoder* encoder = (Encoder*)opaque;
         return encoder->frameCallback(buf, bufSize);
 }
 }}}


 You'll notice I make heavy use of `AVDictionary`s, and those values can be
 found here:

 {{{
 const char* encoderParams[] =
 {
     "b", "2000000",
     "time_base", "1:15",
     "pixel_format", "yuv420p",
     "speed", "6",
     "tile-columns", "4",
     "frame-parallel", "1",
     "threads", "8",
     "static-thresh", "0",
     "deadline", "realtime",
     "lag-in-frames", "0",
     "error-resilient", "1"
 };

 const char* muxerParams[] =
 {
     "dash", "1",
     "live", "1"
 };

 const char* miscParams[] =
 {
     "frame_buf_size", "8192"
 };
 }}}

 Is this a bug or did I miss something in the initialization?
 Thanks!

--
Ticket URL: <https://trac.ffmpeg.org/ticket/6720>
FFmpeg <https://ffmpeg.org>
FFmpeg issue tracker


More information about the FFmpeg-trac mailing list