[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