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

Reply via email to