[Libav-user] Converting YUV Data to AVFrame
Gaurav Pathak
gauravpathak129 at gmail.com
Wed Apr 29 07:36:26 CEST 2015
Hello all,
I am working on encoding a raw yuv stream coming directly from webcam,
using v4l2. The issue that I am facing is that even after using *avpicture_fill
*and assigning every buffer separately as
*frame->data[0] = buffers[buf.index].start;frame->data[1] =
(buffers[buf.index].start)+1;frame->data[2] = (buffers[buf.index].start)+3;*
The output is not getting encoded and I am getting raw video. My Code is
*#include "libavutil/opt.h"#include "libavcodec/avcodec.h"#include
"libavutil/channel_layout.h"#include "libavutil/common.h"#include
"libavutil/imgutils.h"#include "libavutil/mathematics.h"#include
"libavutil/samplefmt.h"#include "stdio.h"#include "stdlib.h"#include
"string.h"#include "assert.h"#include "getopt.h"#include "fcntl.h"#include
"unistd.h"#include "errno.h"#include "malloc.h"#include
"sys/stat.h"#include "sys/types.h"#include "sys/time.h"#include
"sys/mman.h"#include "sys/ioctl.h"#include "asm/types.h"#include
"linux/videodev2.h"#define INBUF_SIZE 4096#define AUDIO_INBUF_SIZE
20480#define AUDIO_REFILL_THRESH 4096#define CLEAR(x) memset (&(x), 0,
sizeof (x))unsigned char *outbuffer = NULL;unsigned long buff_siz = 0;char
out_name[256];typedef enum {#ifdef IO_READ
IO_METHOD_READ,#endif#ifdef IO_MMAP IO_METHOD_MMAP,#endif#ifdef
IO_USERPTR IO_METHOD_USERPTR,#endif}io_method;struct buffer {
void *start; size_t length;};static io_method io
= IO_METHOD_MMAP;static int fd = -1;struct buffer
* buffers = NULL;static unsigned int n_buffers =
0;// global settingsstatic unsigned int width = 640;static unsigned int
height = 480;static unsigned char jpegQuality = 70;//static char*
jpegFilename = NULL;static char* deviceName = "/dev/video0";AVCodec
*codec;AVCodecContext *c= NULL;int i, ret, x, y, got_output;FILE
*fout;AVFrame *frame;AVPacket pkt;uint8_t endcode[] = { 0, 0, 1, 0xb7
};static int num=0;/** Convert from YUV422 format to RGB888. Formulae are
described on http://en.wikipedia.org/wiki/YUV
<http://en.wikipedia.org/wiki/YUV> \param width width of image \param
height height of image \param src source \param dst destination*/static
void YUV422toRGB888(int width, int height, unsigned char *src, unsigned
char *dst){ int line, column; unsigned char *py, *pu, *pv; unsigned char
*tmp = dst; /* In this format each four bytes is two pixels. Each four
bytes is two Y's, a Cb and a Cr. Each Y goes to one of the pixels, and
the Cb and Cr belong to both pixels. */ py = src; pu = src + 1; pv = src
+ 3; #define CLIP(x) ( (x)>=0xFF ? 0xFF : ( (x) <= 0x00 ? 0x00 : (x) ) )
for (line = 0; line < height; ++line) { for (column = 0; column < width;
++column) { *tmp++ = CLIP((double)*py +
1.402*((double)*pv-128.0)); *tmp++ = CLIP((double)*py -
0.344*((double)*pu-128.0) - 0.714*((double)*pv-128.0)); *tmp++ =
CLIP((double)*py + 1.772*((double)*pu-128.0)); // increase py every
time py += 2; // increase pu,pv every second time if
((column & 1)==1) { pu += 4; pv += 4; } } }}/**
Print error message and terminate programm with EXIT_FAILURE return code.
\param s error message to print*/static void errno_exit(const char* s){
fprintf(stderr, "%s error %d, %s\n", s, errno, strerror (errno));
exit(EXIT_FAILURE);}/** Do ioctl and retry if error was EINTR ("A signal
was caught during the ioctl() operation."). Parameters are the same as on
ioctl. \param fd file descriptor \param request request \param argp
argument \returns result from ioctl*/static int xioctl(int fd, int
request, void* argp){ int r; do r = ioctl(fd, request, argp); while (-1
== r && EINTR == errno); return r;}/* process image read*/static void
imageProcess(const void* p){ unsigned char* src = (unsigned char*)p;
unsigned char* dst = malloc(width*height*3*sizeof(char)); // convert from
YUV422 to RGB888 YUV422toRGB888(width,height,src,dst);}void
print_array(int *ptr,int len){// while(len-- > 0)// {
printf("YUV Data is %x\n",*ptr); ptr++;// }}/** read single
frame*/static int frameRead(void){ struct v4l2_buffer buf;#ifdef
IO_USERPTR unsigned int i;#endif switch (io) {#ifdef IO_READ case
IO_METHOD_READ: if (-1 == read (fd, buffers[0].start,
buffers[0].length)) { switch (errno) { case
EAGAIN: return 0; case EIO: // Could ignore
EIO, see spec. // fall through default:
errno_exit("read"); } }
imageProcess(buffers[0].start); break;#endif#ifdef IO_MMAP case
IO_METHOD_MMAP: CLEAR (buf); buf.type =
V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; if
(-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) { switch (errno)
{ case EAGAIN: return 0; case EIO:
// Could ignore EIO, see spec // fall through
default: errno_exit("VIDIOC_DQBUF"); } } assert
(buf.index < n_buffers); if (-1 == xioctl(fd, VIDIOC_QBUF,
&buf)) errno_exit("VIDIOC_QBUF"); av_init_packet(&pkt);
pkt.data = NULL; // packet data will be allocated by the encoder
pkt.size = 0; fflush(stdout); avpicture_fill((AVPicture*)frame,
NULL, frame->format, frame->width, frame->height); frame->data[0] =
buffers[buf.index].start; frame->data[1] =
(buffers[buf.index].start)+1; frame->data[2] =
(buffers[buf.index].start)+3;//
print_array(buffers[buf.index].start,buffers[buf.index].length);
frame->pts = num; num++; /* encode the image */ ret =
avcodec_encode_video2(c, &pkt, frame, &got_output); if (ret < 0)
{ fprintf(stderr, "Error encoding frame\n"); exit(1);
} if (got_output) {// printf("Write frame %3d
(size=%5d)\n", num, pkt.size); fwrite(pkt.data, 1, pkt.size,
fout); av_free_packet(&pkt); } break;#endif#ifdef
IO_USERPTR case IO_METHOD_USERPTR: CLEAR (buf); buf.type =
V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_USERPTR; if
(-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) { switch (errno) {
case EAGAIN: return 0; case EIO: // Could
ignore EIO, see spec. // fall through
default: errno_exit("VIDIOC_DQBUF");
} } for (i = 0; i < n_buffers; ++i) if
(buf.m.userptr == (unsigned long) buffers[i].start && buf.length ==
buffers[i].length) break; assert (i < n_buffers);
imageProcess((void *) buf.m.userptr); if (-1 == xioctl(fd,
VIDIOC_QBUF, &buf)) errno_exit("VIDIOC_QBUF"); break;#endif }
return 1;}/** mainloop: read frames and process them*/static void
mainLoop(void){ unsigned int count,codec_id=AV_CODEC_ID_H264; char
*filename="abc.flv"; count = 300; avcodec_register_all(); printf("Encode
video file %s\n", filename); /* find the mpeg1 video encoder */ codec =
avcodec_find_encoder(codec_id); if (!codec) { fprintf(stderr, "Codec
not found\n"); exit(1); } c = avcodec_alloc_context3(codec); if
(!c) { fprintf(stderr, "Could not allocate video codec
context\n"); exit(1); } /* put sample parameters */ c->bit_rate =
10000000; /* resolution must be a multiple of two */ c->width = 640;
c->height = 480; /* frames per second */ c->time_base =
(AVRational){1,25}; c->gop_size = 10; /* emit one intra frame every ten
frames */ c->max_b_frames = 1; c->pix_fmt = AV_PIX_FMT_YUV420P; if
(codec_id == AV_CODEC_ID_H264) av_opt_set(c->priv_data, "preset",
"ultrafast", 0); /* open it */ if (avcodec_open2(c, codec, NULL) < 0)
{ fprintf(stderr, "Could not open codec\n"); exit(1); } fout =
fopen(filename, "wb"); if (!fout) { fprintf(stderr, "Could not open
%s\n", filename); exit(1); } frame = av_frame_alloc(); if (!frame)
{ fprintf(stderr, "Could not allocate video frame\n"); exit(1);
} frame->format = c->pix_fmt; frame->width = c->width; frame->height =
c->height; /* the image can be allocated by any means and av_image_alloc()
is * just the most convenient way if av_malloc() is to be used */// ret
= av_image_alloc(frame->data, frame->linesize, c->width,
c->height,c->pix_fmt, 32);// if (ret < 0)// {// fprintf(stderr,
"Could not allocate raw picture buffer\n");// exit(1);// } while
(count-- > 0) { for (;;) { fd_set fds; struct timeval
tv; int r; FD_ZERO(&fds); FD_SET(fd, &fds); /* Timeout.
*/ tv.tv_sec = 10; tv.tv_usec = 0; r = select(fd + 1, &fds,
NULL, NULL, &tv); if (-1 == r) { if (EINTR == errno)
continue; errno_exit("select"); } if (0 == r) {
fprintf (stderr, "select timeout\n"); exit(EXIT_FAILURE);
} if (frameRead()) break; /* EAGAIN - continue select
loop. */ } } for (got_output = 1; got_output; num++) {
fflush(stdout); ret = avcodec_encode_video2(c, &pkt, NULL,
&got_output); if (ret < 0) { fprintf(stderr, "Error
encoding frame\n"); exit(1); } if (got_output)
{// printf("Write frame %3d (size=%5d)\n", num, pkt.size);
fwrite(pkt.data, 1, pkt.size, fout); av_free_packet(&pkt);
} } /* add sequence end code to have a real mpeg file */ fwrite(endcode,
1, sizeof(endcode), fout); fclose(fout); avcodec_close(c);
av_free(c);// av_freep(&frame->data[0]); av_frame_free(&frame);
printf("\n");}/** stop capturing*/static void captureStop(void){ enum
v4l2_buf_type type; switch (io) {#ifdef IO_READ case
IO_METHOD_READ: /* Nothing to do. */ break;#endif#ifdef
IO_MMAP case IO_METHOD_MMAP:#endif#ifdef IO_USERPTR case
IO_METHOD_USERPTR:#endif#if defined(IO_MMAP) || defined(IO_USERPTR)
type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (-1 == xioctl(fd,
VIDIOC_STREAMOFF, &type)) errno_exit("VIDIOC_STREAMOFF");
break;#endif }}/** start capturing*/static void captureStart(void){
unsigned int i; enum v4l2_buf_type type; switch (io) {#ifdef IO_READ
case IO_METHOD_READ: /* Nothing to do. */ break;#endif#ifdef
IO_MMAP case IO_METHOD_MMAP: for (i = 0; i < n_buffers; ++i)
{ struct v4l2_buffer buf; CLEAR (buf); buf.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory =
V4L2_MEMORY_MMAP; buf.index = i; if (-1 == xioctl(fd,
VIDIOC_QBUF, &buf)) errno_exit("VIDIOC_QBUF");
} type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (-1 ==
xioctl(fd, VIDIOC_STREAMON, &type))
errno_exit("VIDIOC_STREAMON"); break;#endif#ifdef IO_USERPTR case
IO_METHOD_USERPTR: for (i = 0; i < n_buffers; ++i) { struct
v4l2_buffer buf; CLEAR (buf); buf.type =
V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory =
V4L2_MEMORY_USERPTR; buf.index = i; buf.m.userptr =
(unsigned long) buffers[i].start; buf.length =
buffers[i].length; if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
errno_exit("VIDIOC_QBUF"); } type =
V4L2_BUF_TYPE_VIDEO_CAPTURE; if (-1 == xioctl(fd, VIDIOC_STREAMON,
&type)) errno_exit("VIDIOC_STREAMON"); break;#endif }}static
void deviceUninit(void){ unsigned int i; switch (io) {#ifdef IO_READ
case IO_METHOD_READ: free(buffers[0].start); break;#endif#ifdef
IO_MMAP case IO_METHOD_MMAP: for (i = 0; i < n_buffers; ++i)
if (-1 == munmap (buffers[i].start, buffers[i].length))
errno_exit("munmap"); break;#endif#ifdef IO_USERPTR case
IO_METHOD_USERPTR: for (i = 0; i < n_buffers; ++i) free
(buffers[i].start); break;#endif } free(buffers);}#ifdef
IO_READstatic void readInit(unsigned int buffer_size){ buffers = calloc(1,
sizeof(*buffers)); if (!buffers) { fprintf (stderr, "Out of
memory\n"); exit(EXIT_FAILURE); } buffers[0].length = buffer_size;
buffers[0].start = malloc (buffer_size); if (!buffers[0].start) {
fprintf (stderr, "Out of memory\n"); exit(EXIT_FAILURE); }}#endif#ifdef
IO_MMAPstatic void mmapInit(void){ struct v4l2_requestbuffers req; CLEAR
(req); req.count = 4; req.type =
V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP;
if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) { if (EINVAL == errno)
{ fprintf(stderr, "%s does not support memory mapping\n",
deviceName); exit(EXIT_FAILURE); } else {
errno_exit("VIDIOC_REQBUFS"); } } if (req.count < 2) {
fprintf(stderr, "Insufficient buffer memory on %s\n", deviceName);
exit(EXIT_FAILURE); } buffers = calloc(req.count, sizeof(*buffers)); if
(!buffers) { fprintf(stderr, "Out of memory\n"); exit(EXIT_FAILURE);
} for (n_buffers = 0; n_buffers < req.count; ++n_buffers) { struct
v4l2_buffer buf; CLEAR (buf); buf.type =
V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP;
buf.index = n_buffers; if (-1 == xioctl(fd, VIDIOC_QUERYBUF,
&buf)) errno_exit("VIDIOC_QUERYBUF"); buffers[n_buffers].length =
buf.length; buffers[n_buffers].start = mmap (NULL /* start anywhere
*/, buf.length, PROT_READ | PROT_WRITE /* required */, MAP_SHARED /*
recommended */, fd, buf.m.offset); if (MAP_FAILED ==
buffers[n_buffers].start) errno_exit("mmap"); }}#endif#ifdef
IO_USERPTRstatic void userptrInit(unsigned int buffer_size){ struct
v4l2_requestbuffers req; unsigned int page_size; page_size = getpagesize
(); buffer_size = (buffer_size + page_size - 1) & ~(page_size - 1);
CLEAR(req); req.count = 4; req.type =
V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory =
V4L2_MEMORY_USERPTR; if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) { if
(EINVAL == errno) { fprintf(stderr, "%s does not support user pointer
i/o\n", deviceName); exit(EXIT_FAILURE); } else {
errno_exit("VIDIOC_REQBUFS"); } } buffers = calloc(4, sizeof
(*buffers)); if (!buffers) { fprintf(stderr, "Out of memory\n");
exit(EXIT_FAILURE); } for (n_buffers = 0; n_buffers < 4; ++n_buffers)
{ buffers[n_buffers].length = buffer_size; buffers[n_buffers].start =
memalign (/* boundary */ page_size, buffer_size); if
(!buffers[n_buffers].start) { fprintf(stderr, "Out of memory\n");
exit(EXIT_FAILURE); } }}#endif/** initialize device*/static void
deviceInit(void){ struct v4l2_streamparm stream; struct v4l2_capability
cap; struct v4l2_cropcap cropcap; struct v4l2_crop crop; struct
v4l2_format fmt; unsigned int min; if (-1 == xioctl(fd, VIDIOC_QUERYCAP,
&cap)) { if (EINVAL == errno) { fprintf(stderr, "%s is no V4L2
device\n",deviceName); exit(EXIT_FAILURE); } else {
errno_exit("VIDIOC_QUERYCAP"); } } if (!(cap.capabilities &
V4L2_CAP_VIDEO_CAPTURE)) { fprintf(stderr, "%s is no video capture
device\n",deviceName); exit(EXIT_FAILURE); } if (-1 == xioctl(fd,
VIDIOC_G_PARM, &stream)) {// if (EINVAL == errno)// {//
fprintf(stderr, "%s is no V4L2 device\n",deviceName);//
exit(EXIT_FAILURE);// }// else// {//
errno_exit("VIDIOC_QUERYCAP");// } } printf("Stream Paramater
Type %u\n",stream.type); printf("Input numerator
%d\n",stream.parm.capture.timeperframe.numerator); printf("Input
denominator %d\n",stream.parm.capture.timeperframe.denominator);
printf("output numerator
%d\n",stream.parm.output.timeperframe.numerator); printf("output
denominator %d\n",stream.parm.output.timeperframe.denominator); switch
(io) {#ifdef IO_READ case IO_METHOD_READ: if (!(cap.capabilities &
V4L2_CAP_READWRITE)) { fprintf(stderr, "%s does not support read
i/o\n",deviceName); exit(EXIT_FAILURE); }
break;#endif#ifdef IO_MMAP case IO_METHOD_MMAP:#endif#ifdef
IO_USERPTR case IO_METHOD_USERPTR:#endif#if defined(IO_MMAP) ||
defined(IO_USERPTR) if (!(cap.capabilities & V4L2_CAP_STREAMING))
{ fprintf(stderr, "%s does not support streaming
i/o\n",deviceName); exit(EXIT_FAILURE); } break;#endif }
/* Select video input, video standard and tune here. */ CLEAR(cropcap);
cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (0 == xioctl(fd,
VIDIOC_CROPCAP, &cropcap)) { crop.type =
V4L2_BUF_TYPE_VIDEO_CAPTURE; crop.c = cropcap.defrect; /* reset to
default */ if (-1 == xioctl(fd, VIDIOC_S_CROP, &crop)) { switch
(errno) { case EINVAL: /* Cropping not supported.
*/ break; default: /* Errors ignored. */
break; } } } else { /* Errors ignored. */ } CLEAR (fmt); //
v4l2_format fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = width; fmt.fmt.pix.height = height;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YVU420; fmt.fmt.pix.field =
V4L2_FIELD_INTERLACED; if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt))
errno_exit("VIDIOC_S_FMT"); /* Note VIDIOC_S_FMT may change width and
height. */ if (width != fmt.fmt.pix.width) { width =
fmt.fmt.pix.width; fprintf(stderr,"Image width set to %i by device
%s.\n",width,deviceName); } if (height != fmt.fmt.pix.height) {
height = fmt.fmt.pix.height; fprintf(stderr,"Image height set to %i by
device %s.\n",height,deviceName); } /* Buggy driver paranoia. */ min =
fmt.fmt.pix.width * 2; if (fmt.fmt.pix.bytesperline < min)
fmt.fmt.pix.bytesperline = min; min = fmt.fmt.pix.bytesperline *
fmt.fmt.pix.height; if (fmt.fmt.pix.sizeimage < min)
fmt.fmt.pix.sizeimage = min; switch (io) {#ifdef IO_READ case
IO_METHOD_READ: readInit(fmt.fmt.pix.sizeimage);
break;#endif#ifdef IO_MMAP case IO_METHOD_MMAP: mmapInit();
break;#endif#ifdef IO_USERPTR case IO_METHOD_USERPTR:
userptrInit(fmt.fmt.pix.sizeimage); break;#endif }}/** close
device*/static void deviceClose(void){ if (-1 == close (fd))
errno_exit("close"); fd = -1;}/** open device*/static void
deviceOpen(void){ struct stat st; // stat file if (-1 ==
stat(deviceName, &st)) { fprintf(stderr, "Cannot identify '%s': %d,
%s\n", deviceName, errno, strerror (errno)); exit(EXIT_FAILURE); } //
check if its device if (!S_ISCHR (st.st_mode)) { fprintf(stderr, "%s
is no device\n", deviceName); exit(EXIT_FAILURE); } // open device fd
= open(deviceName, O_RDWR /* required */ | O_NONBLOCK, 0); // check if
opening was successfull if (-1 == fd) { fprintf(stderr, "Cannot open
'%s': %d, %s\n", deviceName, errno, strerror (errno));
exit(EXIT_FAILURE); }}/** print usage information*/static void
usage(FILE* fp, int argc, char** argv){ fprintf (fp, "Usage: %s
[options]\n\n" "Options:\n" "-d | --device name Video device name
[/dev/video0]\n" "-h | --help Print this message\n" "-o |
--output JPEG output filename\n" "-q | --quality JPEG
quality (0-100)\n" "-m | --mmap Use memory mapped buffers\n"
"-r | --read Use read() calls\n" "-u | --userptr Use
application allocated buffers\n" "-W | --width width\n" "-H |
--height height\n" "", argv[0]);}static const char
short_options [] = "d:ho:q:mruW:H:";static const struct optionlong_options
[] = { { "device", required_argument, NULL, 'd'
}, { "help", no_argument, NULL, 'h'
}, { "output", required_argument, NULL, 'o'
}, { "quality", required_argument, NULL, 'q'
}, { "mmap", no_argument, NULL, 'm'
}, { "read", no_argument, NULL, 'r'
}, { "userptr", no_argument, NULL, 'u'
}, { "width", required_argument, NULL, 'W'
}, { "height", required_argument, NULL, 'H'
}, { 0, 0, 0, 0 }};int main(int argc, char **argv){ for (;;) {
int index, c = 0; c = getopt_long(argc, argv,
short_options, long_options, &index); if (-1 == c) break; switch
(c) { case 0: /* getopt_long() flag */ break; case
'd': deviceName = optarg; break; case 'h': //
print help usage (stdout, argc, argv);
exit(EXIT_SUCCESS); case 'q': // set jpeg filename
jpegQuality = atoi(optarg); break; case 'm':#ifdef
IO_MMAP io = IO_METHOD_MMAP;#else fprintf(stderr, "You didn't
compile for mmap support.\n"); exit(EXIT_FAILURE);
#endif break; case 'r':#ifdef IO_READ io =
IO_METHOD_READ;#else fprintf(stderr, "You didn't compile for read
support.\n"); exit(EXIT_FAILURE); #endif break;
case 'u':#ifdef IO_USERPTR io = IO_METHOD_USERPTR;#else
fprintf(stderr, "You didn't compile for userptr support.\n");
exit(EXIT_FAILURE); #endif break; case 'W': //
set width width = atoi(optarg); break; case 'H':
// set height height = atoi(optarg); break;
default: usage(stderr, argc, argv); exit(EXIT_FAILURE); }
} // open and initialize device deviceOpen(); deviceInit(); // start
capturing captureStart(); // process frames mainLoop(); // stop
capturing captureStop(); // close device deviceUninit();
deviceClose(); exit(EXIT_SUCCESS); return 0;}*
It is just for testing, so there are various unused and irrelevant
variables in this code.
For Compiling I am running this command:
*gcc encode.c -o encode -L/usr/local/lib -lavcodec -lavformat -lavutil
-lavfilter -lavdevice -lm -lswscale -lswresample -lx264 -lpthread -lz
-lfdk-aac -lvpx -lmp3lame -lopus -lvorbis -lvorbisenc -lvorbisfile -ldl
-lva -llzma -ltheora -logg*
Even though my video is not getting encoded and I am not getting proper
output in vlc or videos application under Linux.
---------------------------------------------
*Thanks & RegardsGaurav Pathak*
*Contact No: 9028531335*
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://ffmpeg.org/pipermail/libav-user/attachments/20150429/60395fd6/attachment.html>
More information about the Libav-user
mailing list