On Sun, Feb 23, 2014 at 10:29 AM, Anton Khirnov <[email protected]> wrote:
>
> On Sun, 23 Feb 2014 09:34:46 -0500, Reinhard Tartler <[email protected]>
> wrote:
>> The patch quoted below requires quite non-trivial changes to
>> applications. Consider amide's http://amide.sourceforge.net/
>> mpeg_encode_frame() function:
>>
>>
>> gboolean mpeg_encode_frame(gpointer data, GdkPixbuf * pixbuf) {
>> encode_t * encode = data;
>> gint out_size;
>>
>> convert_rgb_pixbuf_to_yuv(encode->yuv, pixbuf);
>>
>> /* encode the image */
>> out_size = avcodec_encode_video(encode->context,
>> encode->output_buffer, encode->output_buffer_size, encode->picture);
>> fwrite(encode->output_buffer, 1, out_size, encode->output_file);
>>
>> return TRUE;
>> };
>>
>> This application so far has successfully used libavcodec without the
>> use of AVPackets. Can someone help with explaining to amide developers
>> why exactly their code improves by using of avcodec_encode_video2
>> instead? I'm not even sure if I got this patch right:
>>
>>
>> --- amide-1.0.4.orig/src/mpeg_encode.c
>> +++ amide-1.0.4/src/mpeg_encode.c
>> @@ -268,7 +269,7 @@ gpointer mpeg_encode_setup(gchar * outpu
>> return NULL;
>> }
>>
>> - encode->picture= avcodec_alloc_frame();
>> + encode->picture= av_frame_alloc();
>> if (!encode->picture) {
>> g_warning("couldn't allocate memory for encode->picture");
>> encode_free(encode);
>> @@ -360,14 +361,37 @@ gpointer mpeg_encode_setup(gchar * outpu
>> gboolean mpeg_encode_frame(gpointer data, GdkPixbuf * pixbuf) {
>> encode_t * encode = data;
>> gint out_size;
>> + AVPacket pkt;
>> + int ret, got_packet = 0;
>>
>> convert_rgb_pixbuf_to_yuv(encode->yuv, pixbuf);
>>
>> - /* encode the image */
>> - out_size = avcodec_encode_video(encode->context,
>> encode->output_buffer, encode->output_buffer_size, encode->picture);
>> - fwrite(encode->output_buffer, 1, out_size, encode->output_file);
>> + av_init_packet(&pkt);
>> + pkt.data = encode->output_buffer;
>> + pkt.size = encode->output_buffer_size;
>
> No need for this, just initialize the whole packet to 0, then the encoder will
> allocate the memory for you. You can the delete output_buffer(_size) and the
> code allocating and freeing it.
>
>>
>> - return TRUE;
>> + /* encode the image */
>> + ret = avcodec_encode_video2(encode->context, &pkt, encode->picture,
>> &got_packet);
>> + if (!ret && got_packet && encode->context->coded_frame) {
>> + encode->context->coded_frame->pts = pkt.pts;
>> + encode->context->coded_frame->key_frame = !!(pkt.flags &
>> AV_PKT_FLAG_KEY);
>> + }
>
> The if() block is not needed.
>
>> +
>> + /* free any side data since we cannot return it */
>> + if (pkt.side_data_elems > 0) {
>> + int i;
>> + for (i = 0; i < pkt.side_data_elems; i++)
>> + av_free(pkt.side_data[i].data);
>> + av_freep(&pkt.side_data);
>> + pkt.side_data_elems = 0;
>> + }
>> +
>> + if (!ret) {
>> + fwrite(encode->output_buffer, 1, pkt.size, encode->output_file);
>> + return TRUE;
>> + } else {
>> + return FALSE;
>> + }
>
> This can be replaced by
> if (ret >= 0 && got_packet) {
> fwrite(pkt.data, 1, pkt.size, encode->output_file);
> av_free_packet(&pkt);
> }
> return (ret >= 0) ? TRUE : FALSE;
>
> With those changes, the new code is about the same length as the old, possibly
> even shorter.
Please double check if I got your suggestions right:
gboolean mpeg_encode_frame(gpointer data, GdkPixbuf * pixbuf) {
encode_t * encode = data;
gint out_size;
AVPacket pkt = { 0 };
int ret, got_packet = 0;
convert_rgb_pixbuf_to_yuv(encode->yuv, pixbuf);
/* encode the image */
ret = avcodec_encode_video2(encode->context, &pkt, encode->picture,
&got_packet);
if (ret >= 0 && got_packet) {
fwrite(pkt.data, 1, pkt.size, encode->output_file);
av_free_packet(&pkt);
}
return (ret >= 0) ? TRUE : FALSE;
};
I'm not sure about the initialization part, did you really mean that
it is not necessary to call av_init_packet()?
>
>
> The most important reasons why this API change was needed were:
> - there was NO WAY WHATSOEVER for the caller to know how large does the output
> buffer need to be; people usually just guessed random numbers that were
> either
> ridiculously large or encoding randomly failed because the buffer was too
> small
> - the old code provided no timestamps. Note that this case, where the caller
> is
> only interested in the raw data, is quite obscure. In the vast majority of
> cases the caller will want to mux the data in some container. For that, you
> need timestamps, and possibly other information, which the old API simply
> does
> not give you. Muxers then have to resort to guessing, usually with horrible
> results.
> - also, if you are muxing with lavf, you need your encoded data wrapped
> in an AVPacket, which the new API already does for you. And since the packet
> is properly allocated, you avoid a copy, which can be a measurable
> performance
> boost.
>
> Due to the above, the code using the old API tended to be fragile and
> error-prone, so I do not think we'll be improving the situation by restoring
> it.
Thanks for writing this up. Let's copy that (and link this thread) to
https://wiki.libav.org/Migration/10
--
regards,
Reinhard
_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel