FreeWRL/FreeX3D  3.0.0
MPEG_Utils_ffmpeg.c
1 
2 #ifdef MOVIETEXTURE_FFMPEG
3 // http://dranger.com/ffmpeg/tutorial01.html
4 #ifdef HAVE_INTTYPES_H
5 #include <inttypes.h>
6 #endif
7 #include <math.h>
8 #include <limits.h>
9 #include <signal.h>
10 #include <stdint.h>
11 
12 #define inline //someone in ffmpeg put a bit of cpp in their headers, this seemed to fix it
13 //#include "libavutil/avstring.h"
14 //#include "libavutil/colorspace.h"
15 //#include "libavutil/mathematics.h"
16 //#include "libavutil/pixdesc.h"
17 #include "libavutil/imgutils.h"
18 //#include "libavutil/pixfmt.h"
19 //#include "libavutil/dict.h"
20 //#include "libavutil/parseutils.h"
21 //#include "libavutil/samplefmt.h"
22 //#include "libavutil/avassert.h"
23 //#include "libavutil/time.h"
24 #include "libavformat/avformat.h"
25 //#include "libavdevice/avdevice.h"
26 #include "libswscale/swscale.h"
27 //#include "libswresample/swresample.h"
28 //#include "libavutil/opt.h"
29 //#include "libavcodec/avfft.h"
30 #include "libswresample/swresample.h"
31 
32 #include "internal.h"
33 #include "Vector.h"
34 #include "../opengl/textures.h"
35 void saveImage_web3dit(struct textureTableIndexStruct *tti, char *fname);
36 // compatibility with newer API
37 #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,28,1)
38 #define av_frame_alloc avcodec_alloc_frame
39 #define av_frame_free avcodec_free_frame
40 #endif
41 
42 
43 //from ffmpeg tutorial01.c
44 //save to .ppm imge format for debugging, which gimp will read but only if RGB24 / nchan==3
45 void SaveFrame(AVFrame *pFrame, int width, int height, int nchan, int iFrame) {
46  FILE *pFile;
47  char szFilename[32];
48  int y;
49 
50  // Open file
51  sprintf(szFilename, "frame%d.ppm", iFrame);
52  pFile=fopen(szFilename, "wb");
53  if(pFile==NULL)
54  return;
55 
56  // Write header
57  fprintf(pFile, "P6\n%d %d\n255\n", width, height);
58 
59  // Write pixel data
60  for(y=0; y<height; y++)
61  fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*nchan, pFile);
62 
63  // Close file
64  fclose(pFile);
65 }
66 float fwroundf(float val){
67  //some math.h don't have round. here's dug9 version.
68  int ival;
69  float singv, valv;
70  singv = val < 0.0f ? -1.0f : 1.0f;
71  valv = fabsf(val);
72  valv = valv + .5f;
73  ival = (int)valv;
74  valv = (float)ival;
75  valv *= singv;
76  return valv;
77 }
78 //our opaque pointer is a struct:
79 struct fw_movietexture {
80  //AVFormatContext *pFormatCtx; //don't need to save for decode-on-load
81  //AVCodecContext *pVideoCodecCtx; //don't need to save for decode-on-load
82  //video and audio section:
83  double duration;
84  //video section:
85  int width,height,nchan,nframes,fps;
86  unsigned char **frames;
87  //audio section:
88  unsigned char *audio_buf;
89  int audio_buf_size;
90  int channels;
91  int freq;
92  int bits_per_channel;
93 };
94 int movie_load_from_file(char *fname, void **opaque){
95  static int once = 0;
96  struct fw_movietexture fw_movie;
97  AVFormatContext *pFormatCtx;
98  int i, videoStream, audioStream;
99  AVCodecContext *pCodecCtxOrig;
100  AVCodecContext *pCodecCtx;
101  AVCodecContext *aCodecCtxOrig;
102  AVCodecContext *aCodecCtx;
103  AVCodec *aCodec;
104  AVFrame *aFrame;
105  AVFrame *aFrameB;
106  //uint8_t *audio_pkt_data = NULL;
107  //int audio_pkt_size = 0;
108  unsigned int audio_buf_size;
109  unsigned int audio_buf_index;
110  uint8_t * audio_buf;
111  SwrContext *swr;
112  int audio_resample_target_fmt;
113  int do_audio_resample;
114  struct SwsContext *sws_ctx;
115  int frameFinished;
116  AVPacket packet;
117  AVFrame *pFrame;
118  AVCodec *pCodec;
119  Stack *fw_framequeue;
120  AVFrame *pFrameRGB;
121  int nchan;
122  uint8_t *buffer;
123 
124 
125 
126  *opaque = NULL;
127  //initialize ffmpeg libs once per process
128  if(once == 0){
129  av_register_all(); //register all codecs - will filter in the future for patent non-expiry
130  once = 1;
131  }
132  pFormatCtx = NULL;
133 
134  // Open video file
135  if(avformat_open_input(&pFormatCtx, fname, NULL, NULL)!=0)
136  return -1; // Couldn't open file
137 
138  // Retrieve stream information
139  if(avformat_find_stream_info(pFormatCtx, NULL)<0)
140  return -1; // Couldn't find stream information
141 
142  // Dump information about file onto standard error
143  av_dump_format(pFormatCtx, 0, fname, 0);
144  //fw_movie.pFormatCtx = pFormatCtx;
145 
146  pCodecCtxOrig = NULL;
147  pCodecCtx = NULL;
148 
149  // Find the first video stream
150  videoStream=-1;
151  audioStream=-1;
152  for(i=0; i<pFormatCtx->nb_streams; i++){
153  if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO && videoStream < 0) {
154  videoStream=i;
155  }
156  if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO && audioStream < 0) {
157  audioStream=i;
158  }
159  }
160  if(videoStream==-1 && audioStream == -1)
161  return -1; // Didn't find either video or audio stream
162 
163 
164 
165  //audio and video prep
166  memset(&fw_movie,0,sizeof(struct fw_movietexture));
167  fw_movie.frames = NULL;
168  fw_movie.nframes = 0;
169  fw_movie.audio_buf = NULL;
170  fw_movie.audio_buf_size = 0;
171 
172  //audio function-scope variables
173  aCodecCtxOrig = NULL;
174  aCodecCtx = NULL;
175  aCodec = NULL;
176  aFrame = NULL;
177  aFrameB = NULL;
178  //uint8_t *audio_pkt_data = NULL;
179  //int audio_pkt_size = 0;
180  audio_buf_size = 1000000;
181  audio_buf_index = 0;
182  audio_buf = NULL;
183  swr = NULL;
184  audio_resample_target_fmt = 0;
185  do_audio_resample = FALSE;
186 
187  //audio prep
188  if(audioStream > -1){
189  AVCodecParameters *aparams;
190 
191  aCodecCtxOrig=pFormatCtx->streams[audioStream]->codec;
192  aCodec = avcodec_find_decoder(aCodecCtxOrig->codec_id);
193  if(!aCodec) {
194  fprintf(stderr, "Unsupported codec!\n");
195  return -1;
196  }
197 
198  // Copy context
199  aCodecCtx = avcodec_alloc_context3(aCodec);
200  aparams = avcodec_parameters_alloc();
201  avcodec_parameters_from_context(aparams, aCodecCtxOrig);
202  avcodec_parameters_to_context(aCodecCtx,aparams);
203  avcodec_parameters_free(&aparams);
204  //if(avcodec_copy_context(aCodecCtx, aCodecCtxOrig) != 0) {
205  // fprintf(stderr, "Couldn't copy codec context");
206  // return -1; // Error copying codec context
207  //}
208 
209  // Set audio settings from codec info
210  fw_movie.channels = aCodecCtx->channels;
211  fw_movie.freq = aCodecCtx->sample_rate;
212  fw_movie.bits_per_channel = aCodecCtx->bits_per_raw_sample;
213 
214  //printf("audio sample format %d\n",aCodecCtx->sample_fmt);
215  // online I found request_sample_fmt is for older versions 1.1 and down, use swresample now
216  //aCodecCtx->request_sample_fmt = AV_SAMPLE_FMT_FLTP; //AV_SAMPLE_FMT_S16P; //AV_SAMPLE_FMT_S16;
217 
218  printf("bits per coded channel=%d\n",aCodecCtx->bits_per_coded_sample);
219 
220 
221  if(avcodec_open2(aCodecCtx, aCodec, NULL) < 0){
222  fprintf(stderr, "Could not open codec\n");
223  return -1;
224  }
225 
226 
227  audio_buf = malloc(audio_buf_size);
228  aFrame=av_frame_alloc();
229  aFrameB=av_frame_alloc();
230 
231  //assuming we resample to what we want:
232  audio_resample_target_fmt = aCodecCtx->sample_fmt;
233  if(aCodecCtx->sample_fmt != AV_SAMPLE_FMT_S16) {
234  fw_movie.channels = 2;
235  fw_movie.freq = 44100;
236  fw_movie.bits_per_channel = 16;
237  audio_resample_target_fmt = AV_SAMPLE_FMT_S16;
238  do_audio_resample = TRUE;
239 
240  // win32 openAL has problems with FLTP (float) audio format
241  // and android openSLES says when queuing chunks can only use PCM
242  // recent versions of libavcodec convert mp4 audio to FLTP
243  // so we will convert to an older S16 or S16P format
244  //swresample didn't work for me, hand-coded did
246  //swr = swr_alloc();
247  //av_opt_set_int(swr, "in_channel_layout", aCodecCtx->channel_layout, 0);
248  //av_opt_set_int(swr, "out_channel_layout", aCodecCtx->channel_layout, 0);
249  //av_opt_set_int(swr, "in_sample_rate", aCodecCtx->sample_rate, 0);
250  //av_opt_set_int(swr, "out_sample_rate", aCodecCtx->sample_rate, 0);
251  //av_opt_set_sample_fmt(swr, "in_sample_fmt", aCodecCtx->sample_fmt, 0);
252  //av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);
253  // https://www.ffmpeg.org/doxygen/3.2/group__lswr.html#details
254 
255  swr = swr_alloc_set_opts(NULL, // we're allocating a new context
256  AV_CH_LAYOUT_STEREO, // out_ch_layout
257  AV_SAMPLE_FMT_S16, // out_sample_fmt
258  44100, // out_sample_rate
259  aCodecCtx->channel_layout, // in_ch_layout
260  aCodecCtx->sample_fmt, // in_sample_fmt
261  aCodecCtx->sample_rate, // in_sample_rate
262  0, // log_offset
263  NULL); // log_ctx
264  swr_init(swr);
265  }
266 
267  }
268 
269  //video function-scope variables
270  sws_ctx = NULL;
271  pFrame = NULL;
272  pCodec = NULL;
273  fw_framequeue = NULL;
274  pFrameRGB = NULL;
275  buffer = NULL;
276  //video prep
277  if(videoStream > -1){
278  AVCodecParameters *vparams;
279  int numBytes;
280  int av_pix_fmt;
281 
282  // Get a pointer to the codec context for the video stream
283  pCodecCtxOrig = pFormatCtx->streams[videoStream]->codec;
284 
285 
286  // Find the decoder for the video stream
287  pCodec=avcodec_find_decoder(pCodecCtxOrig->codec_id);
288  if(pCodec==NULL) {
289  fprintf(stderr, "Unsupported codec!\n");
290  return -1; // Codec not found
291  }
292  // Copy context
293  pCodecCtx = avcodec_alloc_context3(pCodec);
294  vparams = avcodec_parameters_alloc();
295  avcodec_parameters_from_context(vparams, pCodecCtxOrig);
296  avcodec_parameters_to_context(pCodecCtx, vparams);
297  avcodec_parameters_free(&vparams);
298  //if(avcodec_copy_context(pCodecCtx, pCodecCtxOrig) != 0) {
299  // fprintf(stderr, "Couldn't copy codec context");
300  // return -1; // Error copying codec context
301  //}
302  // Open codec
303  if(avcodec_open2(pCodecCtx, pCodec, NULL)<0)
304  return -1; // Could not open codec
305  //fw_movie.pVideoCodecCtx = pCodecCtx;
306 
307 
308  // Allocate video frame
309  pFrame=av_frame_alloc();
310 
311  // Allocate an AVFrame structure
312  pFrameRGB=av_frame_alloc();
313  if(pFrameRGB==NULL)
314  return -1;
315 
316  // Determine required buffer size and allocate buffer
317  if(0){
318  nchan = 3;
319  av_pix_fmt = AV_PIX_FMT_RGB24;
320  }else{
321  nchan = 4;
322  av_pix_fmt = AV_PIX_FMT_RGBA;
323  }
324 
325  fw_movie.nchan = nchan; //RGB24 == 3, RGBA == 4
326  fw_movie.width = pCodecCtx->width;
327  fw_movie.height = pCodecCtx->height;
328 
329  //numBytes=avpicture_get_size(av_pix_fmt, pCodecCtx->width, //AV_PIX_FMT_RGB24, AV_PIX_FMT_RGBA
330  // pCodecCtx->height);
331  numBytes = av_image_get_buffer_size(av_pix_fmt, pCodecCtx->width, pCodecCtx->height,1); //in ffmpeg code I see 1, 16, 32 for align
332  buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));
333 
334 
335  // Assign appropriate parts of buffer to image planes in pFrameRGB
336  // Note that pFrameRGB is an AVFrame, but AVFrame is a superset
337  // of AVPicture
338  //avpicture_fill((AVPicture *)pFrameRGB, buffer,av_pix_fmt, //AV_PIX_FMT_RGBA, //AV_PIX_FMT_RGB24,
339  // pCodecCtx->width, pCodecCtx->height);
340  av_image_fill_arrays(pFrameRGB->data,pFrameRGB->linesize,buffer,av_pix_fmt,pCodecCtx->width, pCodecCtx->height,1);
341 
342  // initialize SWS context for software scaling
343  sws_ctx = sws_getContext(pCodecCtx->width,
344  pCodecCtx->height,
345  pCodecCtx->pix_fmt,
346  pCodecCtx->width,
347  pCodecCtx->height,
348  av_pix_fmt, //AV_PIX_FMT_RGBA, //AV_PIX_FMT_RGB24,
349  SWS_BILINEAR,
350  NULL,
351  NULL,
352  NULL
353  );
354 
355  //if( METHOD_DECODE_ON_LOAD ) - decodes all frames in resource thread when loading the file
356  fw_framequeue = newStack(unsigned char *); //I like stack because stack_push will realloc
357  }
358 
359  //video and audo decoded in combined loop (could split for decode-on-load)
360  i=0;
361  while(av_read_frame(pFormatCtx, &packet)>=0) {
362  // Is this a packet from the video stream?
363  if(packet.stream_index==videoStream) {
364  // Decode video frame
365  //avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
366  avcodec_send_packet(pCodecCtx,&packet);
367  frameFinished = avcodec_receive_frame(pCodecCtx,pFrame) == 0? TRUE : FALSE;
368  // Did we get a video frame?
369  if(frameFinished) {
370  // Convert the image from its native format to RGB
371  unsigned char * fw_frame;
372  int k;
373  sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data,
374  pFrame->linesize, 0, pCodecCtx->height,
375  pFrameRGB->data, pFrameRGB->linesize);
376 
377  // Save the frame to disk
378  ++i;
379  //if(++i<=5)
380  if(0) if(i<=5){
381  SaveFrame(pFrameRGB, pCodecCtx->width,
382  pCodecCtx->height, nchan, i);
383  }
384  //printf("saving frame %d %d %d\n",pCodecCtx->width,pCodecCtx->height, i);
385  //printf("linesize = %d \n",pFrameRGB->linesize[0]);
386 
387  fw_frame = malloc(fw_movie.height * fw_movie.width * nchan); //assumes width == linesize[0]
388 
389  for(k=0;k<pCodecCtx->height;k++){
390  int kd,ks,kk;
391  unsigned char *src;
392  kk = pCodecCtx->height - k - 1; //flip y-down to y-up for opengl
393  ks = k*pFrame->linesize[0]*nchan;
394  kd = kk * fw_movie.width * nchan;
395  src = ((unsigned char *)pFrameRGB->data[0]) + ks;
396  memcpy(&fw_frame[kd],src,fw_movie.width * nchan);
397  }
398  stack_push(unsigned char *,fw_framequeue,fw_frame);
399  }
400  //av_free_packet(&packet);
401  } else if(packet.stream_index==audioStream) {
402  // http://open-activewrl.sourceforge.net/data/OpenAL_PGuide.pdf
403  // page 5:
404  // "Fill the buffers with PCM data using alBufferData."
405  // alBufferData(g_Buffers[0],format,data,size,freq);
406  // Goal: PCM data
407  // taking code from decode_audio_frame in ffmpeg tutorial03.c
408  int buf_size;
409  int got_frame = 0;
410  int data_size = 0;
411  //int len1;
412  //len1 = avcodec_decode_audio4(aCodecCtx, aFrame, &got_frame, &packet);
413  avcodec_send_packet(aCodecCtx, &packet);
414  got_frame = avcodec_receive_frame(aCodecCtx, aFrame) == 0 ? TRUE : FALSE;
415 
416  buf_size = audio_buf_size - audio_buf_index;
417  if(got_frame) {
418  //aFrameOut->format = aCodecCtx->sample_fmt;
419  if(aFrame->nb_samples > 0){
420  data_size = av_samples_get_buffer_size(NULL,
421  aFrame->channels, //aCodecCtx->channels,
422  aFrame->nb_samples,
423  aFrame->format, //AV_SAMPLE_FMT_S16P, //aCodecCtx->sample_fmt,
424  1);
425  //printf("aCodecCtx->sample_fmt= %d channels=%d samples=%d",aCodecCtx->sample_fmt,aCodecCtx->channels,aFrame->nb_samples);
426  //if this chunk's reformatted output will be bigger than the room we have left
427  // in our allocated big audio buffer, then realloc the big audio buffer * 2
428  if(data_size * 2 > buf_size){
429  audio_buf = realloc(audio_buf,audio_buf_size *2);
430  audio_buf_size *= 2;
431  }
432  if (do_audio_resample) //TRUE && aCodecCtx->sample_fmt == AV_SAMPLE_FMT_FLTP)
433  {
434  if(0){
435  //hand-coded FLTP to S16
436  // works with apple1984veryshort.mp4 on win32 openAL, but not generally trusted, just as hacker code
437  //http://stackoverflow.com/questions/14989397/how-to-convert-sample-rate-from-av-sample-fmt-fltp-to-av-sample-fmt-s16
438  int i,c;
439  int nb_samples = aFrame->nb_samples;
440  int channels = aFrame->channels;
441  int outputBufferLen = nb_samples * channels * 2;
442  short* outputBuffer = (short*)&audio_buf[audio_buf_index];
443 
444  for (i = 0; i < nb_samples; i++)
445  {
446  for (c = 0; c < channels; c++)
447  {
448  float* extended_data = (float*)aFrame->extended_data[c];
449  float sample = extended_data[i];
450  if (sample < -1.0f) sample = -1.0f;
451  else if (sample > 1.0f) sample = 1.0f;
452  outputBuffer[i * channels + c] = (short)fwroundf(sample * 32767.0f);
453  }
454  }
455  audio_buf_index += outputBufferLen;
456  }
457  else if(1){
458  //swresample module > swr_convert - works uwp and win32
459  //should convert non PCM 16 formats to PCM 16bit/channel stereo, 44100Hz
460  uint8_t *output;
461  int in_samples = aFrame->nb_samples;
462 
463  int out_samples = (int)av_rescale_rnd(swr_get_delay(swr, aCodecCtx->sample_rate) + in_samples, 44100, aCodecCtx->sample_rate, AV_ROUND_UP);
464  av_samples_alloc(&output, NULL, 2, out_samples, AV_SAMPLE_FMT_S16, 0);
465  out_samples = swr_convert(swr,&output,out_samples, aFrame->extended_data, aFrame->nb_samples);
466  memcpy(&audio_buf[audio_buf_index],output, out_samples * 2 * 2);
467  audio_buf_index += out_samples * 2 * 2;
468  av_freep(&output);
469  }
470  }else{
471  //works when incoming audio is already in s16 format and decoder doesn't change it
472  //ie mpgsys.mpg
473  //(but for mp4 audio, libav gives FLP/float format, and using this simple
474  // memcpy it comes out junk/noise in openAL H: openal can't handle float, just s16)
475  memcpy(&audio_buf[audio_buf_index], aFrame->data[0], data_size);
476  audio_buf_index += data_size;
477  }
478  }
479  }
480 
481  } else {
482  // Free the packet that was allocated by av_read_frame
483  //av_free_packet(&packet);
484  }
485  }
486 
487  //video fin
488  if(videoStream > -1){
489  fw_movie.frames = fw_framequeue->data;
490  fw_movie.nframes = fw_framequeue->n;
491  fw_movie.duration = (double)(fw_movie.nframes) / 30.0; //s = frames / fps
492 
493  if(0){
494  //write out frames in .web3dit image format for testing
495  int k;
496  textureTableIndexStruct_s ttipp, *ttip;
497  ttip = &ttipp;
498  ttip->x = fw_movie.width;
499  ttip->y = fw_movie.height;
500  ttip->z = 1;
501  ttip->hasAlpha = 1;
502  ttip->channels = nchan;
503 
504  for(k=0;k<fw_movie.nframes;k++){
505  char namebuf[100];
506  ttip->texdata = fw_movie.frames[k];
507  sprintf(namebuf,"%s%d.web3dit","ffmpeg_frame_",k);
508  saveImage_web3dit(ttip, namebuf);
509  }
510  }
511  //IF(METHOD_DECODE_ON_LOAD)
512  // GARBAGE COLLECT FFMPEG STUFF
513  // Free the RGB image
514  av_free(buffer);
515  av_frame_free(&pFrameRGB);
516 
517  // Free the YUV frame
518  av_frame_free(&pFrame);
519 
520  // Close the codecs
521  avcodec_close(pCodecCtx);
522  avcodec_close(pCodecCtxOrig);
523  }
524  //audio fin
525  if(audioStream > -1){
526  fw_movie.audio_buf = audio_buf;
527  fw_movie.audio_buf_size = audio_buf_index;
528  fw_movie.duration = (double)(fw_movie.nframes) / 30.0; //s = frames / fps
529 
530  avcodec_close(aCodecCtxOrig);
531  avcodec_close(aCodecCtx);
532  }
533 
534  //audio and video fin
535  // Close the video file
536  avformat_close_input(&pFormatCtx);
537  *opaque = malloc(sizeof(struct fw_movietexture));
538  memcpy(*opaque,&fw_movie,sizeof(struct fw_movietexture));
539 
540 
541  return 1;
542 }
543 double movie_get_duration(void *opaque){
544  struct fw_movietexture *fw_movie = (struct fw_movietexture *)opaque;
545  return fw_movie->duration;
546 }
547 
548 unsigned char *movie_get_frame_by_fraction(void *opaque, float fraction, int *width, int *height, int *nchan){
549  int iframe;
550  struct fw_movietexture *fw_movie = (struct fw_movietexture *)opaque;
551  if(!fw_movie) return NULL;
552 
553  iframe = (int)(fraction * ((float)(fw_movie->nframes -1) + .5f));
554  iframe = max(0,iframe);
555  iframe = min(fw_movie->nframes -1,iframe);
556  *width = fw_movie->width;
557  *height = fw_movie->height;
558  *nchan = fw_movie->nchan;
559  return fw_movie->frames[iframe];
560 }
561 unsigned char * movie_get_audio_PCM_buffer(void *opaque,int *freq, int *channels, int *size, int *bits){
562  struct fw_movietexture *fw_movie = (struct fw_movietexture *)opaque;
563  if(!fw_movie) return NULL;
564  if(!fw_movie->audio_buf) return NULL;
565  *freq = fw_movie->freq;
566  *channels = fw_movie->channels;
567  *size = fw_movie->audio_buf_size;
568  *bits = fw_movie->bits_per_channel;
569  return fw_movie->audio_buf;
570 }
571 void movie_free(void *opaque){
572  struct fw_movietexture *fw_movie = (struct fw_movietexture *)opaque;
573  if(fw_movie) {
574  int k;
575  for(k=0;k<fw_movie->nframes;k++){
576  FREE_IF_NZ(fw_movie->frames[k]);
577  }
578  free(opaque);
579  }
580 }
581 
582 #endif //MOVIETEXTURE_FFMPEG
Definition: Vector.h:36