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