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