Hi,
Recently I wrote some code using libavcodec & friends to extract the
first frame from a mpeg2ts containing H.264 and encode it as PNG still
image with user specified dimensions.
Later, when I wanted to display a default PNG image in the case where the
ts file was missing or corrupted, I realized I could use the same code and
provide it a PNG file as input and have it "transcode" to a PNG file that
had the correct dimensions. While I could write separate code to do this,
it was convenient to use the same code calling FFmpeg APIs to do both tasks.
However, when I did this PNG->PNG transcode, on some machines my code
worked, and on others, the code failed. I tracked this down through my code
and down into FFmpeg. The machines (both real and virtual) I was running on
had 2-4 cores, and I noted multiple threads were spun up to decode the PNG
image, and something went wrong on some machines but the code worked fine
on others. So this is a hard bug report to reproduce. I have trimmed my
code down to something which just reads in a PNG and tries to decode it,
hopefully it's small enough to be useful.
For what it's worth, whatever the race is, I found on some machines the
attached code always works, and on others, it never works. I am using
CentOS 6.4 amd64, and had the same RPM manifest on machines that worked and
machines that didn't. I spent a lot of time trying to track down
environmental issues and I could not find any.
If the underlying ffmpeg library is compiled withe --disable-pthreads the
code always works correctly. If that is not the case, sometimes
avcodec_decode_video2() returns the number of bytes in the PNG, but does
not set the frame complete flag to true. When this happens, the output is
corrupt, and subsequent calls to avcodec_decode_video2() fail.
Exact flags used to compile ffmpeg and command line to compile C test
program are in comments in the C code.
Regards,
Ben
--
*Ben Mesander*
(303)570-1606 | Email <[email protected]> | vCard
<http://www.cardinalpeak.com/vcard/bmesander.vcf> | Web
<http://www.cardinalpeak.com/> | Company Blog
<http://www.cardinalpeak.com/blog> | LinkedIn
<https://cardinalpeak.basecamphq.com/projects/6118601/file/79455231/Linked%20In%20url>
// gcc -L/usr/local/lib -lavcodec -lavformat -Wall bugreport.c -o bugreport
// bugreport random.png
// if "avcodec_decode_video2 suceeded is not seen on stderr, the bug happened.
// if ffmpeg built this way, always succeeds (--disable-pthreads)
// configure --disable-pthreads --enable-pic --enable-shared --disable-programs --disable-doc --disable-everything --enable-decoder=h264 --enable-decoder=png --enable-demuxer=mpegts --enable-demuxer=image2 --enable-encoder=png --enable-parser=h264 --enable-protocol=file --enable-protocol=http
// if ffmpeg built this way (with pthreads), fails on some machines, succeeds on others
// ./configure --enable-pic --enable-shared --disable-programs --disable-doc --disable-everything --enable-decoder=h264 --enable-decoder=png --enable-demuxer=mpegts --enable-demuxer=image2 --enable-encoder=png --enable-parser=h264 --enable-protocol=file --enable-protocol=http
#include <stdio.h>
#include <stdlib.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
int main(int argc, char **argv)
{
AVFrame *frame = NULL;
AVCodecContext *codecCtx = NULL;
AVFormatContext *formatCtx = NULL;
if (argc != 2) {
fprintf(stderr, "usage: %s filename.png\n", argv[0]);
exit(1);
}
int rc = 0;
//////////////////////////////////////////////////////////////////////
// initialize library
av_register_all();
avcodec_register_all();
//////////////////////////////////////////////////////////////////////
// open ts container
int openrc;
if (((openrc = avformat_open_input(&formatCtx, argv[1], NULL, NULL)) != 0) || (formatCtx == NULL)) {
char errorbuf[256];
fprintf(stderr, "can't open input file:%s errcode: %d reason: %s\n", argv[1], rc, av_make_error_string(errorbuf, 256, openrc));
goto cleanup;
}
if (avformat_find_stream_info(formatCtx, NULL)) {
fprintf(stderr, "can't find format of input.\n");
goto cleanup;
}
//////////////////////////////////////////////////////////////////////
// find video stream within container
int i, videoStreamIdx;
for (i = 0; i <formatCtx->nb_streams; i++) {
if (formatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
videoStreamIdx = i;
break;
}
}
if (videoStreamIdx == -1) {
fprintf(stderr, "didn't find a video stream.\n");
goto cleanup;
}
//////////////////////////////////////////////////////////////////////
// open video codec
codecCtx = formatCtx->streams[videoStreamIdx]->codec;
AVCodec *codec = avcodec_find_decoder(codecCtx->codec_id);
if (! codec) {
fprintf(stderr, "unsupported codec.\n");
goto cleanup;
}
if (avcodec_open2(codecCtx, codec, NULL) < 0) {
fprintf(stderr, "unable to open codec.\n");
goto cleanup;
}
//////////////////////////////////////////////////////////////////////
// allocate storage for input video frame
frame = av_frame_alloc();
if (! frame) {
fprintf(stderr, "unable to allocate frame.\n");
goto cleanup;
}
//////////////////////////////////////////////////////////////////////
// read the data from the .ts and decode a video frame
int frameFinished = 0;
AVPacket packet;
i = 0;
while (1) {
rc = av_read_frame(formatCtx, &packet);
fprintf(stderr, "av_read_frame rc=%d\n", rc);
if (rc < 0) break;
// is this packet from the video stream? if not, skip to next packet
if (packet.stream_index == videoStreamIdx) {
rc = avcodec_decode_video2(codecCtx, frame, &frameFinished, &packet);
fprintf(stderr, "avcodec_decode_video2 rc=%d\n", rc);
// have we accumulated an entire video frame?
if (frameFinished) {
fprintf(stderr, "avcodec_decode_video2 suceeded\n");
av_free_packet(&packet);
break;
}
}
av_free_packet(&packet);
}
//////////////////////////////////////////////////////////////////////
// Clean up resources
cleanup:
if (frame) av_frame_free(&frame);
if (codecCtx) avcodec_close(codecCtx);
if (formatCtx) avformat_close_input(&formatCtx);
return rc;
}
_______________________________________________
Libav-user mailing list
[email protected]
http://ffmpeg.org/mailman/listinfo/libav-user