1 /*******************************************************************************
2 * *
3 * SDL_ffmpeg is a library for basic multimedia functionality. *
4 * SDL_ffmpeg is based on ffmpeg. *
5 * *
6 * Copyright (C) 2007 Arjan Houben *
7 * *
8 * SDL_ffmpeg is free software: you can redistribute it and/or modify *
9 * it under the terms of the GNU Lesser General Public License as published *
10 * by the Free Software Foundation, either version 3 of the License, or any *
11 * later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU Lesser General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU Lesser General Public License *
19 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
20 * *
21 *******************************************************************************/
22
23 /**
24 @mainpage
25 @version 1.3.2
26 @author Arjan Houben
27
28 SDL_ffmpeg is designed with ease of use in mind.
29 Even the beginning programmer should be able to use this library
30 so he or she can use multimedia in his/her program.
31 **/
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include <SDL.h>
38 #include <SDL_thread.h>
39
40 #ifdef __cplusplus
41 extern "C"
42 {
43 #endif
44 #include "libavformat/avformat.h"
45 #include "libavutil/mathematics.h"
46 #include "libswscale/swscale.h"
47 #ifdef __cplusplus
48 }
49 #endif
50
51 // FFmpeg compatibility
52 #ifndef AVCODEC_MAX_AUDIO_FRAME_SIZE
53 #define AVCODEC_MAX_AUDIO_FRAME_SIZE 192000
54 #endif
55
56 #include "SDL_ffmpeg.h"
57
58 #ifdef MSVC
59 #define snprintf( buf, count, format, ... ) _snprintf_s( buf, 512, count, format, __VA_ARGS__ )
60 #ifndef INT64_C
61 #define INT64_C(i) i
62 #endif
63 #endif
64
65 #ifdef __cplusplus
66 extern "C"
67 {
68 #endif
69
70 // Use audio conversion from Movie.cpp
71 extern int convert_audio(int in_samples, int in_channels, int in_stride,
72 enum AVSampleFormat in_fmt, const uint8_t *in_buf,
73 int out_samples, int out_channels, int out_stride,
74 enum AVSampleFormat out_fmt, uint8_t *out_buf);
75
76 #ifdef __cplusplus
77 }
78 #endif
79
80
81 /**
82 \cond
83 */
84
85 /**
86 * Provide a fast way to get the correct context.
87 * \returns The context matching the input values.
88 */
getContext(SDL_ffmpegConversionContext ** context,int inWidth,int inHeight,enum AVPixelFormat inFormat,int outWidth,int outHeight,enum AVPixelFormat outFormat)89 struct SwsContext* getContext( SDL_ffmpegConversionContext **context, int inWidth, int inHeight, enum AVPixelFormat inFormat, int outWidth, int outHeight, enum AVPixelFormat outFormat )
90 {
91 SDL_ffmpegConversionContext *ctx = *context;
92
93 if ( ctx )
94 {
95 /* check for a matching context */
96 while ( ctx )
97 {
98 if ( ctx->inWidth == inWidth &&
99 ctx->inHeight == inHeight &&
100 ctx->inFormat == inFormat &&
101 ctx->outWidth == outWidth &&
102 ctx->outHeight == outHeight &&
103 ctx->outFormat == outFormat )
104 {
105 return ctx->context;
106 }
107
108 ctx = ctx->next;
109 }
110
111 ctx = *context;
112
113 /* find the last context */
114 while ( ctx && ctx->next ) ctx = ctx->next;
115
116 /* allocate a new context */
117 ctx->next = ( struct SDL_ffmpegConversionContext* ) malloc( sizeof( SDL_ffmpegConversionContext ) );
118 ctx = ctx->next;
119 }
120 else
121 {
122 ctx = *context = ( struct SDL_ffmpegConversionContext* ) malloc( sizeof( SDL_ffmpegConversionContext ) );
123 // TODO handle the case where ctx is still null due to malloc failure
124 }
125
126 /* fill context with correct information */
127 ctx->context = sws_getContext( inWidth, inHeight, inFormat,
128 outWidth, outHeight, outFormat,
129 SWS_BILINEAR,
130 0,
131 0,
132 0 );
133
134 ctx->inWidth = inWidth;
135 ctx->inHeight = inHeight;
136 ctx->inFormat = inFormat;
137 ctx->outWidth = outWidth;
138 ctx->outHeight = outHeight;
139 ctx->outFormat = outFormat;
140 ctx->next = 0;
141
142 return ctx->context;
143 }
144
145 uint32_t SDL_ffmpegInitWasCalled = 0;
146
147 /* error handling */
148 char SDL_ffmpegErrorMessage[ 512 ];
149
150 void SDL_ffmpegSetError( const char *error );
151
152 /* packet handling */
153 int SDL_ffmpegGetPacket( SDL_ffmpegFile* );
154
155 SDL_ffmpegPacket* SDL_ffmpegGetAudioPacket( SDL_ffmpegFile* );
156
157 SDL_ffmpegPacket* SDL_ffmpegGetVideoPacket( SDL_ffmpegFile* );
158
159 /* frame handling */
160 int SDL_ffmpegDecodeAudioFrame( SDL_ffmpegFile*, AVPacket*, SDL_ffmpegAudioFrame* );
161
162 int SDL_ffmpegDecodeVideoFrame( SDL_ffmpegFile*, AVPacket*, SDL_ffmpegVideoFrame* );
163
164 const SDL_ffmpegCodec SDL_ffmpegCodecAUTO =
165 {
166 -1,
167 720, 576,
168 1, 25,
169 6000000,
170 -1, -1,
171 -1,
172 2, 48000,
173 192000,
174 -1, -1
175 };
176
177 const SDL_ffmpegCodec SDL_ffmpegCodecPALDVD =
178 {
179 AV_CODEC_ID_MPEG2VIDEO,
180 720, 576,
181 1, 25,
182 6000000,
183 -1, -1,
184 AV_CODEC_ID_MP2,
185 2, 48000,
186 192000,
187 -1, -1
188 };
189
190 const SDL_ffmpegCodec SDL_ffmpegCodecPALDV =
191 {
192 AV_CODEC_ID_DVVIDEO,
193 720, 576,
194 1, 25,
195 6553600,
196 -1, -1,
197 AV_CODEC_ID_DVAUDIO,
198 2, 48000,
199 256000,
200 -1, -1
201 };
202
SDL_ffmpegCreateFile()203 SDL_ffmpegFile* SDL_ffmpegCreateFile()
204 {
205 /* create SDL_ffmpegFile pointer */
206 SDL_ffmpegFile *file = ( SDL_ffmpegFile* )malloc( sizeof( SDL_ffmpegFile ) );
207 if ( !file )
208 {
209 SDL_ffmpegSetError( "could not allocate SDL_ffmpegFile" );
210 return 0;
211 }
212
213 memset( file, 0, sizeof( SDL_ffmpegFile ) );
214
215 file->streamMutex = SDL_CreateMutex();
216
217 return file;
218 }
219
220 /**
221 \endcond
222 */
223
224
225 /** \brief Initializes the SDL_ffmpeg library
226
227 This is done automatically when using SDL_ffmpegOpen or
228 SDL_ffmpegCreateFile. This means that it is usualy unnescecairy
229 to explicitly call this function
230 */
SDL_ffmpegInit()231 void SDL_ffmpegInit()
232 {
233 /* register all codecs */
234 if ( !SDL_ffmpegInitWasCalled )
235 {
236 SDL_ffmpegInitWasCalled = 1;
237
238 avcodec_register_all();
239 av_register_all();
240 }
241 }
242
243 /** \brief Use this to free an SDL_ffmpegFile.
244
245 This function stops the decoding thread if needed
246 and flushes the buffers before releasing the memory.
247 \param file SDL_ffmpegFile which needs to be removed
248 */
SDL_ffmpegFree(SDL_ffmpegFile * file)249 void SDL_ffmpegFree( SDL_ffmpegFile *file )
250 {
251 if ( !file ) return;
252
253 SDL_ffmpegFlush( file );
254
255 /* only write trailer when handling output streams */
256 if ( file->type == SDL_ffmpegOutputStream )
257 {
258 av_write_trailer( file->_ffmpeg );
259 }
260
261 SDL_ffmpegStream *s = file->vs;
262 while ( s )
263 {
264 SDL_ffmpegStream *old = s;
265
266 s = s->next;
267
268 SDL_DestroyMutex( old->mutex );
269
270 while ( old->buffer )
271 {
272 SDL_ffmpegPacket *pack = old->buffer;
273
274 old->buffer = old->buffer->next;
275
276 av_free_packet( pack->data );
277
278 av_free( pack->data );
279
280 free( pack );
281 }
282
283 while ( old->conversionContext )
284 {
285 SDL_ffmpegConversionContext *ctx = old->conversionContext;
286
287 old->conversionContext = old->conversionContext->next;
288
289 sws_freeContext( ctx->context );
290
291 free( ctx );
292 }
293
294 av_free( old->decodeFrame );
295
296 if ( old->_ffmpeg ) avcodec_close( old->_ffmpeg->codec );
297
298 free( old );
299 }
300
301 s = file->as;
302 while ( s )
303 {
304 SDL_ffmpegStream *old = s;
305
306 s = s->next;
307
308 SDL_DestroyMutex( old->mutex );
309
310 while ( old->buffer )
311 {
312 SDL_ffmpegPacket *pack = old->buffer;
313
314 old->buffer = old->buffer->next;
315
316 av_free_packet( pack->data );
317
318 av_free( pack->data );
319
320 free( pack );
321 }
322
323 av_free( old->sampleBuffer );
324
325 if ( old->_ffmpeg ) avcodec_close( old->_ffmpeg->codec );
326
327 free( old );
328 }
329
330 if ( file->_ffmpeg )
331 {
332 if ( file->type == SDL_ffmpegInputStream )
333 {
334 #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53,17,0)
335 av_close_input_file( file->_ffmpeg );
336 #else
337 avformat_close_input( &file->_ffmpeg );
338 #endif
339 }
340 else if ( file->type == SDL_ffmpegOutputStream )
341 {
342 avio_close( file->_ffmpeg->pb );
343
344 av_free( file->_ffmpeg );
345 }
346 }
347
348 SDL_DestroyMutex( file->streamMutex );
349
350 free( file );
351 }
352
353
354 /** \brief Use this to free an SDL_ffmpegAudioFrame.
355
356 This releases all buffers which where allocated in SDL_ffmpegCreateAudioFrame
357 \param frame SDL_ffmpegAudioFrame which needs to be deleted
358 */
SDL_ffmpegFreeAudioFrame(SDL_ffmpegAudioFrame * frame)359 void SDL_ffmpegFreeAudioFrame( SDL_ffmpegAudioFrame* frame )
360 {
361 av_free( frame->buffer );
362
363 free( frame );
364 }
365
366
367 /** \brief Use this to free an SDL_ffmpegVideoFrame.
368
369 This releases all buffers which where allocated in SDL_ffmpegCreateVideoFrame
370 \param frame SDL_ffmpegVideoFrame which needs to be deleted
371 */
SDL_ffmpegFreeVideoFrame(SDL_ffmpegVideoFrame * frame)372 void SDL_ffmpegFreeVideoFrame( SDL_ffmpegVideoFrame* frame )
373 {
374 if ( !frame ) return;
375
376 if ( frame->surface ) SDL_FreeSurface( frame->surface );
377
378 // if ( frame->overlay ) SDL_FreeYUVOverlay( frame->overlay );
379
380 free( frame );
381 }
382
383
384 /** \brief Use this to open the multimedia file of your choice.
385
386 This function is used to open a multimedia file.
387 When the file could be opened, but no decodable streams where detected
388 this function still returns a pointer to a valid SDL_ffmpegFile.
389 \param filename string containing the location of the file
390 \returns a pointer to a SDL_ffmpegFile structure, or NULL if a file could not be opened
391 */
SDL_ffmpegOpen(const char * filename)392 SDL_ffmpegFile* SDL_ffmpegOpen( const char* filename )
393 {
394 SDL_ffmpegInit();
395
396 /* open new ffmpegFile */
397 SDL_ffmpegFile *file = SDL_ffmpegCreateFile();
398 if ( !file ) return 0;
399
400 /* information about format is stored in file->_ffmpeg */
401 file->type = SDL_ffmpegInputStream;
402
403 /* open the file */
404 if ( avformat_open_input(&file->_ffmpeg, filename, NULL, NULL) != 0 )
405 {
406 char c[512];
407 snprintf( c, 512, "could not open \"%s\"", filename );
408 SDL_ffmpegSetError( c );
409 free( file );
410 return 0;
411 }
412
413 /* retrieve format information */
414 if ( avformat_find_stream_info( file->_ffmpeg, NULL ) < 0 )
415 {
416 char c[512];
417 snprintf( c, 512, "could not retrieve file info for \"%s\"", filename );
418 SDL_ffmpegSetError( c );
419 free( file );
420 return 0;
421 }
422
423 /* iterate through all the streams and store audio/video streams */
424 for ( uint32_t i = 0; i < file->_ffmpeg->nb_streams; i++ )
425 {
426 /* disable all streams by default */
427 file->_ffmpeg->streams[i]->discard = AVDISCARD_ALL;
428
429 if ( file->_ffmpeg->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO )
430 {
431 /* if this is a packet of the correct type we create a new stream */
432 SDL_ffmpegStream* stream = ( SDL_ffmpegStream* )malloc( sizeof( SDL_ffmpegStream ) );
433
434 if ( stream )
435 {
436 /* we set our stream to zero */
437 memset( stream, 0, sizeof( SDL_ffmpegStream ) );
438
439 /* save unique streamid */
440 stream->id = i;
441
442 /* _ffmpeg holds data about streamcodec */
443 stream->_ffmpeg = file->_ffmpeg->streams[i];
444
445 /* get the correct decoder for this stream */
446 AVCodec *codec = avcodec_find_decoder( stream->_ffmpeg->codec->codec_id );
447
448 if ( !codec )
449 {
450 free( stream );
451 SDL_ffmpegSetError( "could not find video codec" );
452 }
453 else if ( avcodec_open2( file->_ffmpeg->streams[i]->codec, codec, NULL ) < 0 )
454 {
455 free( stream );
456 SDL_ffmpegSetError( "could not open video codec" );
457 }
458 else
459 {
460 stream->mutex = SDL_CreateMutex();
461
462 #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,28,0)
463 stream->decodeFrame = avcodec_alloc_frame();
464 #else
465 stream->decodeFrame = av_frame_alloc();
466 #endif
467
468 SDL_ffmpegStream **s = &file->vs;
469 while ( *s )
470 {
471 *s = ( *s )->next;
472 }
473
474 *s = stream;
475
476 file->videoStreams++;
477 }
478 }
479 }
480 else if ( file->_ffmpeg->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO )
481 {
482 /* if this is a packet of the correct type we create a new stream */
483 SDL_ffmpegStream* stream = ( SDL_ffmpegStream* )malloc( sizeof( SDL_ffmpegStream ) );
484
485 if ( stream )
486 {
487 /* we set our stream to zero */
488 memset( stream, 0, sizeof( SDL_ffmpegStream ) );
489
490 /* save unique streamid */
491 stream->id = i;
492
493 /* _ffmpeg holds data about streamcodec */
494 stream->_ffmpeg = file->_ffmpeg->streams[i];
495
496 /* get the correct decoder for this stream */
497 AVCodec *codec = avcodec_find_decoder( file->_ffmpeg->streams[i]->codec->codec_id );
498
499 if ( !codec )
500 {
501 free( stream );
502 SDL_ffmpegSetError( "could not find audio codec" );
503 }
504 else if ( avcodec_open2( file->_ffmpeg->streams[i]->codec, codec, NULL ) < 0 )
505 {
506 free( stream );
507 SDL_ffmpegSetError( "could not open audio codec" );
508 }
509 else
510 {
511 stream->mutex = SDL_CreateMutex();
512
513 stream->sampleBuffer = ( int8_t* )av_malloc( AVCODEC_MAX_AUDIO_FRAME_SIZE * sizeof( int16_t ) );
514 stream->sampleBufferSize = 0;
515 stream->sampleBufferOffset = 0;
516 stream->sampleBufferTime = AV_NOPTS_VALUE;
517
518 SDL_ffmpegStream **s = &file->as;
519 while ( *s )
520 {
521 *s = ( *s )->next;
522 }
523
524 *s = stream;
525
526 file->audioStreams++;
527 }
528 }
529 }
530 }
531
532 return file;
533 }
534
535 #ifdef SDL_FF_WRITE
536 /** \brief Use this to create the multimedia file of your choice.
537
538 This function is used to create a multimedia file.
539
540 \param filename string containing the location to which the data will be written
541 \returns a pointer to a SDL_ffmpegFile structure, or NULL if a file could not be opened
542 */
SDL_ffmpegCreate(const char * filename)543 SDL_ffmpegFile* SDL_ffmpegCreate( const char* filename )
544 {
545 SDL_ffmpegInit();
546
547 SDL_ffmpegFile *file = SDL_ffmpegCreateFile();
548
549 file->_ffmpeg = avformat_alloc_context();
550
551 /* guess output format based on filename */
552 #if ( LIBAVFORMAT_VERSION_MAJOR <= 52 && LIBAVFORMAT_VERSION_MINOR <= 45 )
553 file->_ffmpeg->oformat = guess_format( 0, filename, 0 );
554 #else
555 file->_ffmpeg->oformat = av_guess_format( 0, filename, 0 );
556 #endif
557
558 if ( !file->_ffmpeg->oformat )
559 {
560 #if ( LIBAVFORMAT_VERSION_MAJOR <= 52 && LIBAVFORMAT_VERSION_MINOR <= 45 )
561 file->_ffmpeg->oformat = guess_format( "dvd", 0, 0 );
562 #else
563 file->_ffmpeg->oformat = av_guess_format( "dvd", 0, 0 );
564 #endif
565 }
566
567 /* preload as shown in ffmpeg.c */
568 file->_ffmpeg->preload = ( int )( 0.5 * AV_TIME_BASE );
569
570 /* max delay as shown in ffmpeg.c */
571 file->_ffmpeg->max_delay = ( int )( 0.7 * AV_TIME_BASE );
572
573 /* open the output file, if needed */
574 if ( url_fopen( &file->_ffmpeg->pb, filename, AVIO_FLAG_WRITE ) < 0 )
575 {
576 char c[512];
577 snprintf( c, 512, "could not open \"%s\"", filename );
578 SDL_ffmpegSetError( c );
579 SDL_ffmpegFree( file );
580 return 0;
581 }
582
583 file->type = SDL_ffmpegOutputStream;
584
585 return file;
586 }
587 #endif
588
589
590 #ifdef SDL_FF_WRITE
591 /** \brief Use this to add a SDL_ffmpegVideoFrame to file
592
593 By adding frames to file, a video stream is build. If an audio stream
594 is present, syncing of both streams needs to be done by user.
595 \param file SDL_ffmpegFile to which a frame needs to be added.
596 \param frame SDL_ffmpegVideoFrame which will be added to the stream.
597 \returns 0 if frame was added, non-zero if an error occured.
598 */
SDL_ffmpegAddVideoFrame(SDL_ffmpegFile * file,SDL_Surface * frame)599 int SDL_ffmpegAddVideoFrame( SDL_ffmpegFile *file, SDL_Surface *frame )
600 {
601 /* when accesing audio/video stream, streamMutex should be locked */
602 SDL_LockMutex( file->streamMutex );
603
604 if ( !file->videoStream || !frame || !frame->format )
605 {
606 SDL_UnlockMutex( file->streamMutex );
607 return -1;
608 }
609
610 int pitch [] =
611 {
612 frame->pitch,
613 0
614 };
615
616 const uint8_t *const data [] =
617 {
618 (uint8_t *)frame->pixels,
619 0
620 };
621
622 switch ( frame->format->BitsPerPixel )
623 {
624 case 24:
625 sws_scale( getContext( &file->videoStream->conversionContext,
626 frame->w, frame->h, AV_PIX_FMT_RGB24,
627 file->videoStream->_ffmpeg->codec->width,
628 file->videoStream->_ffmpeg->codec->height,
629 file->videoStream->_ffmpeg->codec->pix_fmt ),
630 data,
631 pitch,
632 0,
633 frame->h,
634 file->videoStream->encodeFrame->data,
635 file->videoStream->encodeFrame->linesize );
636 break;
637 case 32:
638 sws_scale( getContext( &file->videoStream->conversionContext,
639 frame->w, frame->h, AV_PIX_FMT_BGR32,
640 file->videoStream->_ffmpeg->codec->width,
641 file->videoStream->_ffmpeg->codec->height,
642 file->videoStream->_ffmpeg->codec->pix_fmt ),
643 data,
644 pitch,
645 0,
646 frame->h,
647 file->videoStream->encodeFrame->data,
648 file->videoStream->encodeFrame->linesize );
649 break;
650 default:
651 break;
652 }
653
654 /* PAL = upper field first
655 file->videoStream->encodeFrame->top_field_first = 1;
656 */
657
658 int out_size = avcodec_encode_video( file->videoStream->_ffmpeg->codec, file->videoStream->encodeFrameBuffer, file->videoStream->encodeFrameBufferSize, file->videoStream->encodeFrame );
659
660 /* if zero size, it means the image was buffered */
661 if ( out_size > 0 )
662 {
663 AVPacket pkt;
664 av_init_packet( &pkt );
665
666 /* set correct stream index for this packet */
667 pkt.stream_index = file->videoStream->_ffmpeg->index;
668 /* set keyframe flag if needed */
669 if ( file->videoStream->_ffmpeg->codec->coded_frame->key_frame ) pkt.flags |= AV_PKT_FLAG_KEY;
670 /* write encoded data into packet */
671 pkt.data = file->videoStream->encodeFrameBuffer;
672 /* set the correct size of this packet */
673 pkt.size = out_size;
674 /* set the correct duration of this packet */
675 pkt.duration = AV_TIME_BASE / file->videoStream->_ffmpeg->time_base.den;
676
677 /* if needed info is available, write pts for this packet */
678 if ( file->videoStream->_ffmpeg->codec->coded_frame->pts != AV_NOPTS_VALUE )
679 {
680 pkt.pts = av_rescale_q( file->videoStream->_ffmpeg->codec->coded_frame->pts, file->videoStream->_ffmpeg->codec->time_base, file->videoStream->_ffmpeg->time_base );
681 }
682
683 av_write_frame( file->_ffmpeg, &pkt );
684
685 av_free_packet( &pkt );
686
687 file->videoStream->frameCount++;
688 }
689
690 SDL_UnlockMutex( file->streamMutex );
691
692 return 0;
693 }
694 #endif
695
696
697 #ifdef SDL_FF_WRITE
698 /** \brief Use this to add a SDL_ffmpegAudioFrame to file
699
700 By adding frames to file, an audio stream is build. If a video stream
701 is present, syncing of both streams needs to be done by user.
702 \param file SDL_ffmpegFile to which a frame needs to be added.
703 \param frame SDL_ffmpegAudioFrame which will be added to the stream.
704 \returns 0 if frame was added, non-zero if an error occured.
705 */
SDL_ffmpegAddAudioFrame(SDL_ffmpegFile * file,SDL_ffmpegAudioFrame * frame)706 int SDL_ffmpegAddAudioFrame( SDL_ffmpegFile *file, SDL_ffmpegAudioFrame *frame )
707 {
708 /* when accesing audio/video stream, streamMutex should be locked */
709 SDL_LockMutex( file->streamMutex );
710
711 if ( !file || !file->audioStream || !frame )
712 {
713 SDL_UnlockMutex( file->streamMutex );
714 return -1;
715 }
716
717 AVPacket pkt;
718
719 /* initialize a packet to write */
720 av_init_packet( &pkt );
721
722 /* set correct stream index for this packet */
723 pkt.stream_index = file->audioStream->_ffmpeg->index;
724
725 /* set keyframe flag if needed */
726 pkt.flags |= AV_PKT_FLAG_KEY;
727
728 /* set the correct size of this packet */
729 pkt.size = avcodec_encode_audio( file->audioStream->_ffmpeg->codec, ( uint8_t* )file->audioStream->sampleBuffer, file->audioStream->sampleBufferSize, ( int16_t* )frame->buffer );
730
731 /* write encoded data into packet */
732 pkt.data = ( uint8_t* )file->audioStream->sampleBuffer;
733
734 /* if needed info is available, write pts for this packet */
735 if ( file->audioStream->_ffmpeg->codec->coded_frame->pts != AV_NOPTS_VALUE )
736 {
737 pkt.pts = av_rescale_q( file->audioStream->_ffmpeg->codec->coded_frame->pts, file->audioStream->_ffmpeg->codec->time_base, file->audioStream->_ffmpeg->time_base );
738 }
739
740 /* write packet to stream */
741 av_write_frame( file->_ffmpeg, &pkt );
742
743 av_free_packet( &pkt );
744
745 file->audioStream->frameCount++;
746
747 SDL_UnlockMutex( file->streamMutex );
748
749 return 0;
750 }
751 #endif
752
753 /** \brief Use this to create a SDL_ffmpegAudioFrame
754
755 With this frame, you can receive audio data from the stream using
756 SDL_ffmpegGetAudioFrame.
757 \param file SDL_ffmpegFile for which a frame needs to be created
758 \param bytes When current active audio stream is an input stream, this holds
759 the size of the buffer which will be allocated. In case of an
760 output stream, this value is ignored.
761 \returns Pointer to SDL_ffmpegAudioFrame, or NULL if no frame could be created
762 */
SDL_ffmpegCreateAudioFrame(SDL_ffmpegFile * file,uint32_t bytes)763 SDL_ffmpegAudioFrame* SDL_ffmpegCreateAudioFrame( SDL_ffmpegFile *file, uint32_t bytes )
764 {
765 if (!file) {
766 return 0;
767 }
768 /* when accesing audio/video stream, streamMutex should be locked */
769 SDL_LockMutex( file->streamMutex );
770
771 if (!file->audioStream || ( !bytes && file->type == SDL_ffmpegInputStream ) )
772 {
773 SDL_UnlockMutex( file->streamMutex );
774 return 0;
775 }
776
777 /* allocate new frame */
778 SDL_ffmpegAudioFrame *frame = ( SDL_ffmpegAudioFrame* )malloc( sizeof( SDL_ffmpegAudioFrame ) );
779 memset( frame, 0, sizeof( SDL_ffmpegAudioFrame ) );
780
781 if ( file->type == SDL_ffmpegOutputStream )
782 {
783 bytes = file->audioStream->encodeAudioInputSize * 2 * file->audioStream->_ffmpeg->codec->channels;
784 }
785
786 SDL_UnlockMutex( file->streamMutex );
787
788 /* set capacity of new frame */
789 frame->capacity = bytes;
790
791 /* allocate buffer */
792 frame->buffer = ( uint8_t* )av_malloc( bytes );
793
794 /* initialize a non-valid timestamp */
795 frame->pts = AV_NOPTS_VALUE;
796
797 return frame;
798 }
799
800
801 /** \brief Use this to create a SDL_ffmpegVideoFrame
802
803 In order to receive video data, either SDL_ffmpegVideoFrame.surface or
804 SDL_ffmpegVideoFrame.overlay need to be set by user.
805 \returns Pointer to SDL_ffmpegVideoFrame, or NULL if no frame could be created
806 */
SDL_ffmpegCreateVideoFrame()807 SDL_ffmpegVideoFrame* SDL_ffmpegCreateVideoFrame()
808 {
809 SDL_ffmpegVideoFrame *frame = ( SDL_ffmpegVideoFrame* )malloc( sizeof( SDL_ffmpegVideoFrame ) );
810 // TODO return failure if frame could not be allocated, unsure how that should look
811 memset( frame, 0, sizeof( SDL_ffmpegVideoFrame ) );
812
813 return frame;
814 }
815
816
817 /** \brief Use this to get new video data from file.
818
819 Using this function, you can retreive video data from file. This data
820 gets written to frame.
821 \param file SDL_ffmpegFile from which the data is required
822 \param frame SDL_ffmpegVideoFrame to which the data will be written
823 \returns non-zero when a frame was retreived, zero otherwise
824 */
SDL_ffmpegGetVideoFrame(SDL_ffmpegFile * file,SDL_ffmpegVideoFrame * frame)825 int SDL_ffmpegGetVideoFrame( SDL_ffmpegFile* file, SDL_ffmpegVideoFrame *frame )
826 {
827 if (!file) {
828 return 0;
829 }
830 /* when accesing audio/video stream, streamMutex should be locked */
831 SDL_LockMutex( file->streamMutex );
832
833 if ( !frame || !file || !file->videoStream )
834 {
835 SDL_UnlockMutex( file->streamMutex );
836 return 0;
837 }
838
839 SDL_LockMutex( file->videoStream->mutex );
840
841 /* assume current frame is empty */
842 frame->ready = 0;
843 frame->last = 0;
844
845 /* get new packet */
846 SDL_ffmpegPacket *pack = SDL_ffmpegGetVideoPacket( file );
847
848 while ( !pack && !frame->last )
849 {
850 pack = SDL_ffmpegGetVideoPacket( file );
851
852 frame->last = SDL_ffmpegGetPacket( file );
853 }
854
855 while ( pack && !frame->ready )
856 {
857 /* when a frame is received, frame->ready will be set */
858 SDL_ffmpegDecodeVideoFrame( file, pack->data, frame );
859
860 /* destroy used packet */
861 av_free_packet( pack->data );
862
863 av_free( pack->data );
864
865 free( pack );
866
867 pack = SDL_ffmpegGetVideoPacket( file );
868
869 while ( !pack && !frame->last )
870 {
871 pack = SDL_ffmpegGetVideoPacket( file );
872
873 frame->last = SDL_ffmpegGetPacket( file );
874 }
875 }
876
877 /* pack retreived, but was not used, push it back in the buffer */
878 if ( pack )
879 {
880 /* take current buffer as next pointer */
881 pack->next = file->videoStream->buffer;
882
883 /* store pack as current buffer */
884 file->videoStream->buffer = pack;
885 }
886 else if ( !frame->ready && frame->last )
887 {
888 /* check if there is still a frame in the buffer */
889 SDL_ffmpegDecodeVideoFrame( file, 0, frame );
890 }
891
892 SDL_UnlockMutex( file->videoStream->mutex );
893
894 SDL_UnlockMutex( file->streamMutex );
895
896 return frame->ready;
897 }
898
899
900 /** \brief Get the desired audio stream from file.
901
902 This returns a pointer to the requested stream. With this stream pointer you can
903 get information about the stream, like language, samplerate, size etc.
904 Based on this information you can choose the stream you want to use.
905 \param file SDL_ffmpegFile from which the information is required
906 \param audioID is the stream you whish to select.
907 \returns Pointer to SDL_ffmpegStream, or NULL if selected stream could not be found
908 */
SDL_ffmpegGetAudioStream(SDL_ffmpegFile * file,uint32_t audioID)909 SDL_ffmpegStream* SDL_ffmpegGetAudioStream( SDL_ffmpegFile *file, uint32_t audioID )
910 {
911 /* check if we have any audiostreams */
912 if ( !file || !file->audioStreams ) return 0;
913
914 SDL_ffmpegStream *s = file->as;
915
916 /* return audiostream linked to audioID */
917 for ( uint32_t i = 0; i < audioID && s; i++ ) s = s->next;
918
919 return s;
920 }
921
922
923 /** \brief Select an audio stream from file.
924
925 Use this function to select an audio stream for decoding.
926 Using SDL_ffmpegGetAudioStream you can get information about the streams.
927 Based on that you can chose the stream you want.
928 \param file SDL_ffmpegFile on which an action is required
929 \param audioID is the stream you whish to select. negative values de-select any audio stream.
930 \returns -1 on error, otherwise 0
931 */
SDL_ffmpegSelectAudioStream(SDL_ffmpegFile * file,int audioID)932 int SDL_ffmpegSelectAudioStream( SDL_ffmpegFile* file, int audioID )
933 {
934 if ( !file ) return -1;
935
936 /* when changing audio/video stream, streamMutex should be locked */
937 SDL_LockMutex( file->streamMutex );
938
939 /* check if we have any audiostreams and if the requested ID is available */
940 if ( !file->audioStreams || audioID >= ( int )file->audioStreams )
941 {
942 SDL_UnlockMutex( file->streamMutex );
943
944 SDL_ffmpegSetError( "requested audio stream ID is not available in file" );
945 return -1;
946 }
947
948 /* set all audio streams to discard */
949 SDL_ffmpegStream *stream = file->as;
950
951 /* discard by default */
952 while ( stream && stream->_ffmpeg )
953 {
954 stream->_ffmpeg->discard = AVDISCARD_ALL;
955 stream = stream->next;
956 }
957
958 if ( audioID < 0 )
959 {
960 /* reset audiostream */
961 file->audioStream = 0;
962 }
963 else
964 {
965 /* set current audiostream to stream linked to audioID */
966 file->audioStream = file->as;
967
968 for ( int i = 0; i < audioID && file->audioStream; i++ ) file->audioStream = file->audioStream->next;
969
970 /* active stream need not be discarded */
971 if (file->audioStream && file->audioStream->_ffmpeg)
972 file->audioStream->_ffmpeg->discard = AVDISCARD_DEFAULT;
973 }
974
975 SDL_UnlockMutex( file->streamMutex );
976
977 return 0;
978 }
979
980
981 /** \brief Get the desired video stream from file.
982
983 This returns a pointer to the requested stream. With this stream pointer you can
984 get information about the stream, like language, samplerate, size etc.
985 Based on this information you can choose the stream you want to use.
986 \param file SDL_ffmpegFile from which the information is required
987 \param videoID is the stream you whish to select.
988 \returns Pointer to SDL_ffmpegStream, or NULL if selected stream could not be found
989 */
SDL_ffmpegGetVideoStream(SDL_ffmpegFile * file,uint32_t videoID)990 SDL_ffmpegStream* SDL_ffmpegGetVideoStream( SDL_ffmpegFile *file, uint32_t videoID )
991 {
992 /* check if we have any audiostreams */
993 if ( !file || !file->videoStreams ) return 0;
994
995 /* check if the requested id is possible */
996 if ( videoID >= file->videoStreams ) return 0;
997
998 SDL_ffmpegStream *s = file->vs;
999
1000 /* return audiostream linked to audioID */
1001 for ( uint32_t i = 0; i < videoID && s; i++ ) s = s->next;
1002
1003 return s;
1004 }
1005
1006
1007 /** \brief Select a video stream from file.
1008
1009 Use this function to select a video stream for decoding.
1010 Using SDL_ffmpegGetVideoStream you can get information about the streams.
1011 Based on that you can chose the stream you want.
1012 \param file SDL_ffmpegFile on which an action is required
1013 \param videoID is the stream you whish to select.
1014 \returns -1 on error, otherwise 0
1015 */
SDL_ffmpegSelectVideoStream(SDL_ffmpegFile * file,int videoID)1016 int SDL_ffmpegSelectVideoStream( SDL_ffmpegFile* file, int videoID )
1017 {
1018 if ( !file ) return -1;
1019
1020 /* when changing audio/video stream, streamMutex should be locked */
1021 SDL_LockMutex( file->streamMutex );
1022
1023 /* check if we have any videostreams */
1024 if ( videoID >= ( int )file->videoStreams )
1025 {
1026 SDL_UnlockMutex( file->streamMutex );
1027
1028 SDL_ffmpegSetError( "requested video stream ID is not available in file" );
1029
1030 return -1;
1031 }
1032
1033 /* set all video streams to discard */
1034 SDL_ffmpegStream *stream = file->vs;
1035
1036 /* discard by default */
1037 while ( stream && stream->_ffmpeg )
1038 {
1039 stream->_ffmpeg->discard = AVDISCARD_ALL;
1040 stream = stream->next;
1041 }
1042
1043 if ( videoID < 0 )
1044 {
1045 /* reset videostream */
1046 file->videoStream = 0;
1047 }
1048 else
1049 {
1050 /* set current videostream to stream linked to videoID */
1051 file->videoStream = file->vs;
1052
1053 /* keep searching for correct videostream */
1054 for ( int i = 0; i < videoID && file->videoStream; i++ ) file->videoStream = file->videoStream->next;
1055
1056 /* active stream need not be discarded */
1057 if (file->videoStream && file->videoStream->_ffmpeg)
1058 file->videoStream->_ffmpeg->discard = AVDISCARD_DEFAULT;
1059 }
1060
1061 SDL_UnlockMutex( file->streamMutex );
1062
1063 return 0;
1064 }
1065
1066 /** \brief Seek to a certain point in file.
1067
1068 Tries to seek to specified point in file.
1069 \param file SDL_ffmpegFile on which an action is required
1070 \param timestamp is represented in milliseconds.
1071 \returns -1 on error, otherwise 0
1072 */
SDL_ffmpegSeek(SDL_ffmpegFile * file,uint64_t timestamp)1073 int SDL_ffmpegSeek( SDL_ffmpegFile* file, uint64_t timestamp )
1074 {
1075 if ( !file ) return -1;
1076
1077 if ( SDL_ffmpegDuration( file ) < timestamp )
1078 {
1079 SDL_ffmpegSetError( "can not seek past end of file" );
1080
1081 return -1;
1082 }
1083
1084 /* convert milliseconds to AV_TIME_BASE units */
1085 uint64_t seekPos = timestamp * ( AV_TIME_BASE / 1000 );
1086
1087 /* AVSEEK_FLAG_BACKWARD means we jump to the first keyframe before seekPos */
1088 av_seek_frame( file->_ffmpeg, -1, seekPos, AVSEEK_FLAG_BACKWARD );
1089
1090 /* set minimal timestamp to decode */
1091 file->minimalTimestamp = timestamp;
1092
1093 /* flush buffers */
1094 SDL_ffmpegFlush( file );
1095
1096 return 0;
1097 }
1098
1099 /** \brief Seek to a relative point in file.
1100
1101 Tries to seek to new location, based on current location in file.
1102 \param file SDL_ffmpegFile on which an action is required
1103 \param timestamp is represented in milliseconds.
1104 \returns -1 on error, otherwise 0
1105 */
SDL_ffmpegSeekRelative(SDL_ffmpegFile * file,int64_t timestamp)1106 int SDL_ffmpegSeekRelative( SDL_ffmpegFile *file, int64_t timestamp )
1107 {
1108 /* same thing as normal seek, just take into account the current position */
1109 return SDL_ffmpegSeek( file, SDL_ffmpegGetPosition( file ) + timestamp );
1110 }
1111
1112 /**
1113 \cond
1114 */
SDL_ffmpegFlush(SDL_ffmpegFile * file)1115 int SDL_ffmpegFlush( SDL_ffmpegFile *file )
1116 {
1117 /* check for file and permission to flush buffers */
1118 if ( !file ) return -1;
1119
1120 /* when accesing audio/video stream, streamMutex should be locked */
1121 SDL_LockMutex( file->streamMutex );
1122
1123 /* if we have a valid audio stream, we flush it */
1124 if ( file->audioStream )
1125 {
1126 SDL_LockMutex( file->audioStream->mutex );
1127
1128 SDL_ffmpegPacket *pack = file->audioStream->buffer;
1129
1130 while ( pack )
1131 {
1132 SDL_ffmpegPacket *old = pack;
1133
1134 pack = pack->next;
1135
1136 av_free_packet( old->data );
1137
1138 av_free( old->data );
1139
1140 free( old );
1141 }
1142
1143 file->audioStream->buffer = 0;
1144
1145 /* flush internal ffmpeg buffers */
1146 if ( file->audioStream->_ffmpeg )
1147 {
1148 avcodec_flush_buffers( file->audioStream->_ffmpeg->codec );
1149 }
1150
1151 SDL_UnlockMutex( file->audioStream->mutex );
1152 }
1153
1154 /* if we have a valid video stream, we flush some more */
1155 if ( file->videoStream )
1156 {
1157 SDL_LockMutex( file->videoStream->mutex );
1158
1159 SDL_ffmpegPacket *pack = file->videoStream->buffer;
1160
1161 while ( pack )
1162 {
1163 SDL_ffmpegPacket *old = pack;
1164
1165 pack = pack->next;
1166
1167 av_free_packet( old->data );
1168
1169 av_free( old->data );
1170
1171 free( old );
1172 }
1173
1174 file->videoStream->buffer = 0;
1175
1176 /* flush internal ffmpeg buffers */
1177 if ( file->videoStream->_ffmpeg ) avcodec_flush_buffers( file->videoStream->_ffmpeg->codec );
1178
1179 SDL_UnlockMutex( file->videoStream->mutex );
1180 }
1181
1182 SDL_UnlockMutex( file->streamMutex );
1183
1184 return 0;
1185 }
1186 /**
1187 \endcond
1188 */
1189
1190 /** \brief Use this to get a pointer to a SDL_ffmpegAudioFrame.
1191
1192 If you receive a frame, it is valid until you receive a new frame, or
1193 until the file is freed, using SDL_ffmpegFree( SDL_ffmpegFile* ).
1194 I you use data from the frame, you should adjust the size member by
1195 the amount of data used in bytes. This is needed so that SDL_ffmpeg can
1196 calculate the next frame.
1197 \param file SDL_ffmpegFile from which the information is required
1198 \param frame The frame to which the data will be decoded.
1199 \returns Pointer to SDL_ffmpegAudioFrame, or NULL if no frame was available.
1200 */
SDL_ffmpegGetAudioFrame(SDL_ffmpegFile * file,SDL_ffmpegAudioFrame * frame)1201 int SDL_ffmpegGetAudioFrame( SDL_ffmpegFile *file, SDL_ffmpegAudioFrame *frame )
1202 {
1203 if ( !file || !frame ) return -1;
1204
1205 /* when accesing audio/video stream, streamMutex should be locked */
1206 SDL_LockMutex( file->streamMutex );
1207
1208 if ( !file->audioStream )
1209 {
1210 SDL_UnlockMutex( file->streamMutex );
1211
1212 SDL_ffmpegSetError( "no valid audio stream selected" );
1213 return 0;
1214 }
1215
1216 /* lock audio buffer */
1217 SDL_LockMutex( file->audioStream->mutex );
1218
1219 /* reset frame end pointer and size */
1220 frame->last = 0;
1221 frame->size = 0;
1222
1223 /* get new packet */
1224 SDL_ffmpegPacket *pack = SDL_ffmpegGetAudioPacket( file );
1225
1226 while ( !pack && !frame->last )
1227 {
1228 pack = SDL_ffmpegGetAudioPacket( file );
1229
1230 frame->last = SDL_ffmpegGetPacket( file );
1231 }
1232
1233 /* SDL_ffmpegDecodeAudioFrame will return true if data from pack was used
1234 frame will be updated with the new data */
1235 while ( pack && SDL_ffmpegDecodeAudioFrame( file, pack->data, frame ) )
1236 {
1237 /* destroy used packet */
1238 av_free_packet( pack->data );
1239
1240 av_free( pack->data );
1241
1242 free( pack );
1243
1244 pack = 0;
1245
1246 /* check if new packet is required */
1247 if ( frame->size < frame->capacity )
1248 {
1249 /* try to get a new packet */
1250 pack = SDL_ffmpegGetAudioPacket( file );
1251
1252 while ( !pack && !frame->last )
1253 {
1254 pack = SDL_ffmpegGetAudioPacket( file );
1255
1256 frame->last = SDL_ffmpegGetPacket( file );
1257 }
1258 }
1259 }
1260
1261 /* pack retreived, but was not used, push it back in the buffer */
1262 if ( pack )
1263 {
1264 /* take current buffer as next pointer */
1265 pack->next = file->audioStream->buffer;
1266
1267 /* store pack as current buffer */
1268 file->audioStream->buffer = pack;
1269 }
1270
1271 /* unlock audio buffer */
1272 SDL_UnlockMutex( file->audioStream->mutex );
1273
1274 SDL_UnlockMutex( file->streamMutex );
1275
1276 return ( frame->size == frame->capacity );
1277 }
1278
1279
1280 /** \brief Returns the current position of the file in milliseconds.
1281
1282 \param file SDL_ffmpegFile from which the information is required
1283 \returns -1 on error, otherwise the length of the file in milliseconds
1284 */
SDL_ffmpegGetPosition(SDL_ffmpegFile * file)1285 int64_t SDL_ffmpegGetPosition( SDL_ffmpegFile *file )
1286 {
1287 if ( !file ) return -1;
1288
1289 /* when accesing audio/video stream, streamMutex should be locked */
1290 SDL_LockMutex( file->streamMutex );
1291
1292 int64_t pos = 0;
1293
1294 if ( file->audioStream )
1295 {
1296 pos = file->audioStream->lastTimeStamp;
1297 }
1298
1299 if ( file->videoStream && file->videoStream->lastTimeStamp > pos )
1300 {
1301 pos = file->videoStream->lastTimeStamp;
1302 }
1303
1304 SDL_UnlockMutex( file->streamMutex );
1305
1306 /* return the current playposition of our file */
1307 return pos;
1308 }
1309
1310
1311 /** \brief Returns the frame rate of the stream as a fraction.
1312
1313 This retreives the frame rate of the supplied stream.
1314 For example, a framerate of 25 frames per second will have a nominator
1315 of 1, and a denominator of 25.
1316 \param stream SDL_ffmpegStream from which the information is required.
1317 \param nominator Nominator part of the fraction. Can be 0 if exact value of
1318 nominator is not required.
1319 \param denominator Denominator part of the fraction. Can be 0 if exact value
1320 of denominator is not required.
1321 \returns The result of nominator / denominator as floating point value.
1322 */
SDL_ffmpegGetFrameRate(SDL_ffmpegStream * stream,int * nominator,int * denominator)1323 float SDL_ffmpegGetFrameRate( SDL_ffmpegStream *stream, int *nominator, int *denominator )
1324 {
1325 if ( stream && stream->_ffmpeg && stream->_ffmpeg->codec )
1326 {
1327 AVRational frate;
1328 #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(55,12,100)
1329 frate = stream->_ffmpeg->r_frame_rate;
1330 #elif defined(av_stream_get_r_frame_rate)
1331 frate = av_stream_get_r_frame_rate(stream->_ffmpeg);
1332 #else
1333 frate = stream->_ffmpeg->avg_frame_rate;
1334 #endif
1335 if ( nominator ) *nominator = frate.num;
1336
1337 if ( denominator ) *denominator = frate.den;
1338
1339 return ( float )frate.num / frate.den;
1340 }
1341 else
1342 {
1343 SDL_ffmpegSetError( "could not retreive frame rate from stream" );
1344
1345 if ( nominator ) *nominator = 0;
1346
1347 if ( denominator ) *denominator = 0;
1348 }
1349
1350 return 0.0;
1351 }
1352
1353 /** \brief This can be used to get a SDL_AudioSpec based on values found in file
1354
1355 This returns a SDL_AudioSpec, if you have selected a valid audio
1356 stream. Otherwise, all values are set to NULL.
1357 \param file SDL_ffmpegFile from which the information is required
1358 \param samples Amount of samples required every time the callback is called.
1359 Lower values mean less latency, but please note that SDL has a minimal value.
1360 \param callback Pointer to callback function
1361 \returns SDL_AudioSpec with values set according to the selected audio stream.
1362 If no valid audio stream was available, all values of returned SDL_AudioSpec are set to 0
1363 */
SDL_ffmpegGetAudioSpec(SDL_ffmpegFile * file,uint16_t samples,SDL_ffmpegCallback callback)1364 SDL_AudioSpec SDL_ffmpegGetAudioSpec( SDL_ffmpegFile *file, uint16_t samples, SDL_ffmpegCallback callback )
1365 {
1366 /* create audio spec */
1367 SDL_AudioSpec spec;
1368
1369 memset( &spec, 0, sizeof( SDL_AudioSpec ) );
1370
1371 if ( !file ) return spec;
1372
1373 /* when accesing audio/video stream, streamMutex should be locked */
1374 SDL_LockMutex( file->streamMutex );
1375
1376 /* if we have a valid audiofile, we can use its data to create a
1377 more appropriate audio spec */
1378 if ( file->audioStream )
1379 {
1380 spec.format = AUDIO_S16SYS;
1381 spec.samples = samples;
1382 spec.userdata = file;
1383 spec.callback = callback;
1384 spec.freq = file->audioStream->_ffmpeg->codec->sample_rate;
1385 spec.channels = ( uint8_t )file->audioStream->_ffmpeg->codec->channels;
1386 }
1387 else
1388 {
1389 SDL_ffmpegSetError( "no valid audio stream selected" );
1390 }
1391
1392 SDL_UnlockMutex( file->streamMutex );
1393
1394 return spec;
1395 }
1396
1397
1398 /** \brief Returns the Duration of the file in milliseconds.
1399
1400 Please note that this value is guestimated by FFmpeg, it may differ from
1401 actual playing time.
1402 \param file SDL_ffmpegFile from which the information is required
1403 \returns -1 on error, otherwise the length of the file in milliseconds
1404 */
SDL_ffmpegDuration(SDL_ffmpegFile * file)1405 uint64_t SDL_ffmpegDuration( SDL_ffmpegFile *file )
1406 {
1407 if ( !file ) return 0;
1408
1409 if ( file->type == SDL_ffmpegInputStream )
1410 {
1411 /* returns the duration of the entire file, please note that ffmpeg doesn't
1412 always get this value right! so don't bet your life on it... */
1413 return file->_ffmpeg->duration / ( AV_TIME_BASE / 1000 );
1414 }
1415
1416 if ( file->type == SDL_ffmpegOutputStream )
1417 {
1418 uint64_t v = SDL_ffmpegVideoDuration( file );
1419 uint64_t a = SDL_ffmpegAudioDuration( file );
1420
1421 if ( v > a ) return v;
1422 return a;
1423 }
1424
1425 return 0;
1426 }
1427
1428
1429 /** \brief Returns the duration of the audio stream in milliseconds.
1430
1431 This value can be used to sync two output streams.
1432 \param file SDL_ffmpegFile from which the information is required
1433 \returns -1 on error, otherwise the length of the file in milliseconds
1434 */
SDL_ffmpegAudioDuration(SDL_ffmpegFile * file)1435 uint64_t SDL_ffmpegAudioDuration( SDL_ffmpegFile *file )
1436 {
1437 if ( !file ) return 0;
1438
1439 /* when accesing audio/video stream, streamMutex should be locked */
1440 SDL_LockMutex( file->streamMutex );
1441
1442 uint64_t duration = 0;
1443
1444 if ( file->audioStream )
1445 {
1446 if ( file->type == SDL_ffmpegInputStream )
1447 {
1448 duration = av_rescale( 1000 * file->audioStream->_ffmpeg->duration, file->audioStream->_ffmpeg->time_base.num, file->audioStream->_ffmpeg->time_base.den );
1449 }
1450 else if ( file->type == SDL_ffmpegOutputStream )
1451 {
1452 duration = file->audioStream->frameCount * file->audioStream->encodeAudioInputSize / ( file->audioStream->_ffmpeg->codec->sample_rate / 1000 );
1453 }
1454 }
1455 else
1456 {
1457 SDL_ffmpegSetError( "no valid audio stream selected" );
1458 }
1459
1460 SDL_UnlockMutex( file->streamMutex );
1461
1462 return duration;
1463 }
1464
1465
1466 /** \brief Returns the duration of the video stream in milliseconds.
1467
1468 This value can be used to sync two output streams.
1469 \param file SDL_ffmpegFile from which the information is required
1470 \returns -1 on error, otherwise the length of the file in milliseconds
1471 */
SDL_ffmpegVideoDuration(SDL_ffmpegFile * file)1472 uint64_t SDL_ffmpegVideoDuration( SDL_ffmpegFile *file )
1473 {
1474 if ( !file ) return 0;
1475
1476 /* when accesing audio/video stream, streamMutex should be locked */
1477 SDL_LockMutex( file->streamMutex );
1478
1479 uint64_t duration = 0;
1480
1481 if ( file->videoStream )
1482 {
1483 if ( file->type == SDL_ffmpegInputStream )
1484 {
1485 duration = av_rescale( 1000 * file->videoStream->_ffmpeg->duration, file->videoStream->_ffmpeg->time_base.num, file->videoStream->_ffmpeg->time_base.den );
1486 }
1487 else if ( file->type == SDL_ffmpegOutputStream )
1488 {
1489 duration = av_rescale( 1000 * file->videoStream->frameCount, file->videoStream->_ffmpeg->codec->time_base.num, file->videoStream->_ffmpeg->codec->time_base.den );
1490 }
1491 }
1492 else
1493 {
1494 SDL_ffmpegSetError( "no valid video stream selected" );
1495 }
1496
1497 SDL_UnlockMutex( file->streamMutex );
1498
1499 return duration;
1500 }
1501
1502 /** \brief retreive the width/height of a frame beloning to file
1503
1504 With this function you can get the width and height of a frame, belonging to
1505 your file. If there is no (valid) videostream selected w and h default
1506 to 0. Please not that you will have to make sure the pointers are
1507 allocated.
1508
1509 \param file SDL_ffmpegFile from which the information is required
1510 \param w width
1511 \param h height
1512 \returns -1 on error, otherwise 0
1513 */
SDL_ffmpegGetVideoSize(SDL_ffmpegFile * file,int * w,int * h)1514 int SDL_ffmpegGetVideoSize( SDL_ffmpegFile *file, int *w, int *h )
1515 {
1516 if ( !file || !w || !h ) return -1;
1517
1518 /* when accesing audio/video stream, streamMutex should be locked */
1519 SDL_LockMutex( file->streamMutex );
1520
1521 /* if we have a valid video file selected, we use it
1522 if not, we send default values and return.
1523 by checking the return value you can check if you got a valid size */
1524 if ( file->videoStream )
1525 {
1526 *w = file->videoStream->_ffmpeg->codec->width;
1527 *h = file->videoStream->_ffmpeg->codec->height;
1528
1529 SDL_UnlockMutex( file->streamMutex );
1530
1531 return 0;
1532 }
1533 else
1534 {
1535 SDL_ffmpegSetError( "no valid video stream selected" );
1536 }
1537
1538 *w = 0;
1539 *h = 0;
1540
1541 SDL_UnlockMutex( file->streamMutex );
1542
1543 return -1;
1544 }
1545
1546
1547 /** \brief This is used to check if a valid audio stream is selected.
1548
1549 \param file SDL_ffmpegFile from which the information is required
1550 \returns non-zero if a valid video stream is selected, otherwise 0
1551 */
SDL_ffmpegValidAudio(SDL_ffmpegFile * file)1552 int SDL_ffmpegValidAudio( SDL_ffmpegFile* file )
1553 {
1554 /* this function is used to check if we selected a valid audio stream */
1555 return ( file && file->audioStream );
1556 }
1557
1558
1559 /** \brief This is used to check if a valid video stream is selected.
1560
1561 \param file SDL_ffmpegFile from which the information is required
1562 \returns non-zero if a valid video stream is selected, otherwise 0
1563 */
SDL_ffmpegValidVideo(SDL_ffmpegFile * file)1564 int SDL_ffmpegValidVideo( SDL_ffmpegFile* file )
1565 {
1566 /* this function is used to check if we selected a valid video stream */
1567 return ( file && file->videoStream );
1568 }
1569
1570
1571 #ifdef SDL_FF_WRITE
1572 /** \brief This is used to add a video stream to file
1573
1574 \param file SDL_ffmpegFile to which the stream will be added
1575 \param codec SDL_ffmpegCodec describing the type encoding to be used by this
1576 stream.
1577 \returns The stream which was added, or NULL if no stream could be added.
1578 */
SDL_ffmpegAddVideoStream(SDL_ffmpegFile * file,SDL_ffmpegCodec codec)1579 SDL_ffmpegStream* SDL_ffmpegAddVideoStream( SDL_ffmpegFile *file, SDL_ffmpegCodec codec )
1580 {
1581 /* add a video stream */
1582 AVStream *stream = av_new_stream( file->_ffmpeg, 0 );
1583 if ( !stream )
1584 {
1585 SDL_ffmpegSetError( "could not allocate video stream" );
1586 return 0;
1587 }
1588
1589 stream->codec = avcodec_alloc_context();
1590
1591 avcodec_get_context_defaults2( stream->codec, AVMEDIA_TYPE_VIDEO );
1592
1593 if ( codec.videoCodecID < 0 )
1594 {
1595 stream->codec->codec_id = file->_ffmpeg->oformat->video_codec;
1596 }
1597 else
1598 {
1599 stream->codec->codec_id = ( enum CodecID ) codec.videoCodecID;
1600 }
1601
1602 stream->codec->codec_type = AVMEDIA_TYPE_VIDEO;
1603
1604 stream->codec->bit_rate = codec.videoBitrate;
1605
1606 /* resolution must be a multiple of two */
1607 stream->codec->width = codec.width;
1608 stream->codec->height = codec.height;
1609
1610 /* set time_base */
1611 stream->codec->time_base.num = codec.framerateNum;
1612 stream->codec->time_base.den = codec.framerateDen;
1613
1614 /* emit one intra frame every twelve frames at most */
1615 stream->codec->gop_size = 12;
1616
1617 /* set pixel format */
1618 stream->codec->pix_fmt = AV_PIX_FMT_YUV420P;
1619
1620 /* set mpeg2 codec parameters */
1621 if ( stream->codec->codec_id == AV_CODEC_ID_MPEG2VIDEO )
1622 {
1623 stream->codec->max_b_frames = 2;
1624 }
1625
1626 /* set mpeg1 codec parameters */
1627 if ( stream->codec->codec_id == AV_CODEC_ID_MPEG1VIDEO )
1628 {
1629 /* needed to avoid using macroblocks in which some coeffs overflow
1630 this doesnt happen with normal video, it just happens here as the
1631 motion of the chroma plane doesnt match the luma plane */
1632 stream->codec->mb_decision = 2;
1633 }
1634
1635 /* some formats want stream headers to be separate */
1636 if ( file->_ffmpeg->oformat->flags & AVFMT_GLOBALHEADER )
1637 {
1638 stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
1639 }
1640
1641 /* find the video encoder */
1642 AVCodec *videoCodec = avcodec_find_encoder( stream->codec->codec_id );
1643 if ( !videoCodec )
1644 {
1645 SDL_ffmpegSetError( "video codec not found" );
1646 return 0;
1647 }
1648
1649 /* open the codec */
1650 if ( avcodec_open( stream->codec, videoCodec ) < 0 )
1651 {
1652 SDL_ffmpegSetError( "could not open video codec" );
1653 return 0;
1654 }
1655
1656 /* create a new stream */
1657 SDL_ffmpegStream *str = ( SDL_ffmpegStream* )malloc( sizeof( SDL_ffmpegStream ) );
1658
1659 if ( str )
1660 {
1661 /* we set our stream to zero */
1662 memset( str, 0, sizeof( SDL_ffmpegStream ) );
1663
1664 str->id = file->audioStreams + file->videoStreams;
1665
1666 /* _ffmpeg holds data about streamcodec */
1667 str->_ffmpeg = stream;
1668
1669 str->mutex = SDL_CreateMutex();
1670
1671 str->encodeFrame = avcodec_alloc_frame();
1672
1673 uint8_t *picture_buf;
1674 int size = avpicture_get_size( stream->codec->pix_fmt, stream->codec->width, stream->codec->height );
1675 picture_buf = ( uint8_t* )av_malloc( size + FF_INPUT_BUFFER_PADDING_SIZE );
1676 avpicture_fill(( AVPicture* )str->encodeFrame, picture_buf, stream->codec->pix_fmt, stream->codec->width, stream->codec->height );
1677
1678 str->encodeFrameBufferSize = stream->codec->width * stream->codec->height * 4 + FF_INPUT_BUFFER_PADDING_SIZE;
1679
1680 str->encodeFrameBuffer = ( uint8_t* )av_malloc( str->encodeFrameBufferSize );
1681
1682 file->videoStreams++;
1683
1684 /* find correct place to save the stream */
1685 SDL_ffmpegStream **s = &file->vs;
1686 while ( *s )
1687 {
1688 *s = ( *s )->next;
1689 }
1690
1691 *s = str;
1692
1693 if ( av_set_parameters( file->_ffmpeg, 0 ) < 0 )
1694 {
1695 SDL_ffmpegSetError( "could not set encoding parameters" );
1696 }
1697
1698 /* try to write a header */
1699 av_write_header( file->_ffmpeg );
1700 }
1701
1702 return str;
1703 }
1704 #endif
1705
1706
1707 #ifdef SDL_FF_WRITE
1708 /** \brief This is used to add a video stream to file
1709
1710 \param file SDL_ffmpegFile to which the stream will be added
1711 \param codec SDL_ffmpegCodec describing the type encoding to be used by this
1712 stream.
1713 \returns The stream which was added, or NULL if no stream could be added.
1714 */
SDL_ffmpegAddAudioStream(SDL_ffmpegFile * file,SDL_ffmpegCodec codec)1715 SDL_ffmpegStream* SDL_ffmpegAddAudioStream( SDL_ffmpegFile *file, SDL_ffmpegCodec codec )
1716 {
1717 // add an audio stream
1718 AVStream *stream = av_new_stream( file->_ffmpeg, 1 );
1719 if ( !stream )
1720 {
1721 SDL_ffmpegSetError( "could not allocate audio stream" );
1722 return 0;
1723 }
1724
1725 if ( codec.audioCodecID < 0 )
1726 {
1727 stream->codec->codec_id = file->_ffmpeg->oformat->audio_codec;
1728 }
1729 else
1730 {
1731 stream->codec->codec_id = ( enum CodecID ) codec.audioCodecID;
1732 }
1733
1734 stream->codec->codec_type = AVMEDIA_TYPE_AUDIO;
1735 stream->codec->bit_rate = codec.audioBitrate;
1736 stream->codec->sample_rate = codec.sampleRate;
1737 stream->codec->sample_fmt = AV_SAMPLE_FMT_S16;
1738 stream->codec->channels = codec.channels;
1739
1740 // find the audio encoder
1741 AVCodec *audioCodec = avcodec_find_encoder( stream->codec->codec_id );
1742 if ( !audioCodec )
1743 {
1744 SDL_ffmpegSetError( "audio codec not found" );
1745 return 0;
1746 }
1747
1748 // open the codec
1749 if ( avcodec_open( stream->codec, audioCodec ) < 0 )
1750 {
1751 SDL_ffmpegSetError( "could not open audio codec" );
1752 return 0;
1753 }
1754
1755 /* some formats want stream headers to be separate */
1756 if ( file->_ffmpeg->oformat->flags & AVFMT_GLOBALHEADER )
1757 {
1758 stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
1759 }
1760
1761
1762 /* create a new stream */
1763 SDL_ffmpegStream *str = ( SDL_ffmpegStream* )malloc( sizeof( SDL_ffmpegStream ) );
1764
1765 if ( str )
1766 {
1767 str->_ffmpeg = stream;
1768
1769 /* we set our stream to zero */
1770 memset( str, 0, sizeof( SDL_ffmpegStream ) );
1771
1772 str->id = file->audioStreams + file->videoStreams;
1773
1774 /* _ffmpeg holds data about streamcodec */
1775 str->_ffmpeg = stream;
1776
1777 str->mutex = SDL_CreateMutex();
1778
1779 str->sampleBufferSize = 10000;
1780
1781 str->sampleBuffer = ( int8_t* )av_malloc( str->sampleBufferSize );
1782
1783 /* ugly hack for PCM codecs (will be removed ASAP with new PCM
1784 support to compute the input frame size in samples */
1785 if ( stream->codec->frame_size <= 1 )
1786 {
1787 str->encodeAudioInputSize = str->sampleBufferSize / stream->codec->channels;
1788
1789 switch ( stream->codec->codec_id )
1790 {
1791 case AV_CODEC_ID_PCM_S16LE:
1792 case AV_CODEC_ID_PCM_S16BE:
1793 case AV_CODEC_ID_PCM_U16LE:
1794 case AV_CODEC_ID_PCM_U16BE:
1795 str->encodeAudioInputSize >>= 1;
1796 break;
1797 default:
1798 break;
1799 }
1800 }
1801 else
1802 {
1803 str->encodeAudioInputSize = stream->codec->frame_size;
1804 }
1805
1806 file->audioStreams++;
1807
1808 /* find correct place to save the stream */
1809 SDL_ffmpegStream **s = &file->as;
1810 while ( *s )
1811 {
1812 *s = ( *s )->next;
1813 }
1814
1815 *s = str;
1816
1817 if ( av_set_parameters( file->_ffmpeg, 0 ) < 0 )
1818 {
1819 SDL_ffmpegSetError( "could not set encoding parameters" );
1820 return 0;
1821 }
1822
1823 /* try to write a header */
1824 av_write_header( file->_ffmpeg );
1825 }
1826
1827 return str;
1828 }
1829 #endif
1830
1831
1832 /** \brief Use this function to query if an error occured
1833
1834 \returns non-zero when an error occured
1835 */
SDL_ffmpegError()1836 int SDL_ffmpegError()
1837 {
1838 return SDL_ffmpegErrorMessage[ 0 ];
1839 }
1840
1841
1842 /** \brief Use this function to get the last error string
1843
1844 \returns When no error was found, NULL is returned
1845 */
SDL_ffmpegGetError()1846 const char* SDL_ffmpegGetError()
1847 {
1848 return SDL_ffmpegErrorMessage;
1849 }
1850
1851
1852 /** \brief Use this function to clear all standing errors
1853
1854 */
SDL_ffmpegClearError()1855 void SDL_ffmpegClearError()
1856 {
1857 SDL_ffmpegErrorMessage[ 0 ] = 0;
1858 }
1859
1860 /**
1861 \cond
1862 */
1863
SDL_ffmpegSetError(const char * error)1864 void SDL_ffmpegSetError( const char *error )
1865 {
1866 if ( !error ) return;
1867
1868 if ( snprintf( SDL_ffmpegErrorMessage, 512, "%s", error ) >= 511 )
1869 {
1870 SDL_ffmpegErrorMessage[ 511 ] = 0;
1871 }
1872 }
1873
SDL_ffmpegGetPacket(SDL_ffmpegFile * file)1874 int SDL_ffmpegGetPacket( SDL_ffmpegFile *file )
1875 {
1876 /* entering this function, streamMutex should have been locked */
1877
1878 /* create a packet for our data */
1879 AVPacket *pack = ( AVPacket* )av_malloc( sizeof( AVPacket ) );
1880
1881 /* initialize packet */
1882 av_init_packet( pack );
1883
1884 /* read a packet from the file */
1885 int decode = av_read_frame( file->_ffmpeg, pack );
1886
1887 /* if we did not get a packet, we probably reached the end of the file */
1888 if ( decode < 0 )
1889 {
1890 av_free( pack );
1891
1892 /* signal EOF */
1893 return 1;
1894 }
1895
1896 /* we got a packet, lets handle it */
1897
1898 /* try to allocate the packet */
1899 if ( av_dup_packet( pack ) )
1900 {
1901 /* error allocating packet */
1902 av_free_packet( pack );
1903 }
1904 else
1905 {
1906 /* If it's a packet from either of our streams, return it */
1907 if ( file->audioStream && pack->stream_index == file->audioStream->id )
1908 {
1909 /* prepare packet */
1910 SDL_ffmpegPacket *temp = ( SDL_ffmpegPacket* )malloc( sizeof( SDL_ffmpegPacket ) );
1911 // TODO check and handle the case where temp failed to malloc
1912 temp->data = pack;
1913 temp->next = 0;
1914
1915 SDL_ffmpegPacket **p = &file->audioStream->buffer;
1916
1917 while ( *p )
1918 {
1919 p = &( *p )->next;
1920 }
1921
1922 *p = temp;
1923 }
1924 else if ( file->videoStream && pack->stream_index == file->videoStream->id )
1925 {
1926 /* prepare packet */
1927 SDL_ffmpegPacket *temp = ( SDL_ffmpegPacket* )malloc( sizeof( SDL_ffmpegPacket ) );
1928 temp->data = pack;
1929 temp->next = 0;
1930
1931 // SDL_LockMutex( file->videoStream->mutex );
1932
1933 SDL_ffmpegPacket **p = &file->videoStream->buffer;
1934
1935 while ( *p )
1936 {
1937 p = &( *p )->next;
1938 }
1939
1940 *p = temp;
1941
1942 // SDL_UnlockMutex( file->videoStream->mutex );
1943 }
1944 else
1945 {
1946 av_free_packet( pack );
1947 }
1948 }
1949
1950 return 0;
1951 }
1952
SDL_ffmpegGetAudioPacket(SDL_ffmpegFile * file)1953 SDL_ffmpegPacket* SDL_ffmpegGetAudioPacket( SDL_ffmpegFile *file )
1954 {
1955 if ( !file->audioStream ) return 0;
1956
1957 /* file->audioStream->mutex should be locked before entering this function */
1958
1959 SDL_ffmpegPacket *pack = 0;
1960
1961 /* check if there are still packets in buffer */
1962 if ( file->audioStream->buffer )
1963 {
1964 pack = file->audioStream->buffer;
1965
1966 file->audioStream->buffer = pack->next;
1967 }
1968
1969 /* if a packet was found, return it */
1970 return pack;
1971 }
1972
SDL_ffmpegGetVideoPacket(SDL_ffmpegFile * file)1973 SDL_ffmpegPacket* SDL_ffmpegGetVideoPacket( SDL_ffmpegFile *file )
1974 {
1975 if ( !file->videoStream ) return 0;
1976
1977 /* file->videoStream->mutex should be locked before entering this function */
1978
1979 SDL_ffmpegPacket *pack = 0;
1980
1981 /* check if there are still packets in buffer */
1982 if ( file->videoStream->buffer )
1983 {
1984 pack = file->videoStream->buffer;
1985
1986 file->videoStream->buffer = pack->next;
1987 }
1988
1989 /* if a packet was found, return it */
1990 return pack;
1991 }
1992
SDL_ffmpegDecodeAudioFrame(SDL_ffmpegFile * file,AVPacket * pack,SDL_ffmpegAudioFrame * frame)1993 int SDL_ffmpegDecodeAudioFrame( SDL_ffmpegFile *file, AVPacket *pack, SDL_ffmpegAudioFrame *frame )
1994 {
1995 uint8_t *data = pack->data;
1996 int size = pack->size;
1997 int audioSize = AVCODEC_MAX_AUDIO_FRAME_SIZE * sizeof( int16_t );
1998
1999 int channels = file->audioStream->_ffmpeg->codec->channels;
2000 enum AVSampleFormat in_fmt = file->audioStream->_ffmpeg->codec->sample_fmt;
2001 int in_bps = av_get_bytes_per_sample(in_fmt);
2002 enum AVSampleFormat out_fmt = AV_SAMPLE_FMT_S16;
2003 int out_bps = av_get_bytes_per_sample(out_fmt);
2004
2005 /* check if there is still data in the buffer */
2006 if ( file->audioStream->sampleBufferSize )
2007 {
2008 /* set new pts */
2009 if ( !frame->size ) frame->pts = file->audioStream->sampleBufferTime;
2010
2011 /* calculate free space in frame */
2012 int fs = frame->capacity - frame->size;
2013
2014 /* check the amount of data which needs to be copied */
2015 int in_samples = file->audioStream->sampleBufferSize / (channels * in_bps);
2016 int out_samples = fs / (channels * out_bps);
2017
2018 if (out_samples < in_samples)
2019 {
2020 /* copy data from sampleBuffer into frame buffer until frame buffer is full */
2021 int written = convert_audio(out_samples, channels, file->audioStream->sampleBufferStride,
2022 in_fmt, (uint8_t *)(file->audioStream->sampleBuffer + file->audioStream->sampleBufferOffset),
2023 out_samples, channels, -1,
2024 out_fmt, frame->buffer + frame->size);
2025
2026 /* mark the amount of bytes still in the buffer */
2027 file->audioStream->sampleBufferSize -= out_samples * channels * in_bps;
2028
2029 /* move offset accordingly */
2030 if (av_sample_fmt_is_planar(in_fmt))
2031 file->audioStream->sampleBufferOffset += out_samples * in_bps;
2032 else
2033 file->audioStream->sampleBufferOffset += out_samples * in_bps * channels;
2034
2035 /* update framesize */
2036 frame->size += written;
2037 }
2038 else
2039 {
2040 /* copy data from sampleBuffer into frame buffer until sampleBuffer is empty */
2041 int written = convert_audio(in_samples, channels, file->audioStream->sampleBufferStride,
2042 in_fmt, (uint8_t *)(file->audioStream->sampleBuffer + file->audioStream->sampleBufferOffset),
2043 in_samples, channels, -1,
2044 out_fmt, frame->buffer + frame->size);
2045
2046 /* update framesize */
2047 frame->size += written;
2048
2049 /* at this point, samplebuffer should have been handled */
2050 file->audioStream->sampleBufferSize = 0;
2051
2052 /* no more data in buffer, reset offset */
2053 file->audioStream->sampleBufferOffset = 0;
2054 }
2055
2056 /* return 0 to signal caller that 'pack' was not used */
2057 if ( frame->size == frame->capacity ) return 0;
2058 }
2059
2060
2061 /* calculate pts to determine wheter or not this frame should be stored */
2062 file->audioStream->sampleBufferTime = av_rescale(( pack->dts - file->audioStream->_ffmpeg->start_time ) * 1000, file->audioStream->_ffmpeg->time_base.num, file->audioStream->_ffmpeg->time_base.den );
2063
2064 while ( size > 0 )
2065 {
2066 /* Decode the packet */
2067 AVCodecContext *avctx = file->audioStream->_ffmpeg->codec;
2068 #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,28,1)
2069 AVFrame *dframe = avcodec_alloc_frame();
2070 avcodec_get_frame_defaults(dframe);
2071 #else
2072 AVFrame *dframe = av_frame_alloc();
2073 #endif
2074 int got_frame = 0;
2075 int len = avcodec_decode_audio4( avctx, dframe, &got_frame, pack );
2076
2077 if (len < 0 || !got_frame)
2078 {
2079 SDL_ffmpegSetError( "error decoding audio frame" );
2080 break;
2081 }
2082
2083 int planar = av_sample_fmt_is_planar( avctx->sample_fmt );
2084 int plane_size;
2085 int data_size = av_samples_get_buffer_size( &plane_size, avctx->channels, dframe->nb_samples, avctx->sample_fmt, 1 );
2086 if ( data_size > 10000 )
2087 {
2088 SDL_ffmpegSetError( "too much data in decoded audio frame" );
2089 break;
2090 }
2091 memcpy( file->audioStream->sampleBuffer, dframe->extended_data[0], plane_size );
2092 audioSize = plane_size;
2093 if ( planar && avctx->channels > 1 )
2094 {
2095 int8_t *out = file->audioStream->sampleBuffer + plane_size;
2096 int ch;
2097 for ( ch = 1; ch < avctx->channels; ch++ )
2098 {
2099 memcpy( out, dframe->extended_data[ch], plane_size );
2100 out += plane_size;
2101 audioSize += plane_size;
2102 }
2103 }
2104
2105 /* change pointers */
2106 data += len;
2107 size -= len;
2108 }
2109
2110 {
2111 /* set new pts */
2112 if ( !frame->size ) frame->pts = file->audioStream->sampleBufferTime;
2113
2114 /* save stride of data we just grabbed */
2115 file->audioStream->sampleBufferStride = audioSize / channels;
2116
2117 /* room in frame */
2118 int fs = frame->capacity - frame->size;
2119
2120 /* check if there is room at all */
2121 if ( fs )
2122 {
2123 /* check the amount of data which needs to be copied */
2124 int in_samples = audioSize / (channels * in_bps);
2125 int out_samples = fs / (channels * out_bps);
2126
2127 if (out_samples < in_samples)
2128 {
2129 /* copy data from sampleBuffer into frame buffer until frame buffer is full */
2130 int written = convert_audio(out_samples, channels, file->audioStream->sampleBufferStride,
2131 in_fmt, (uint8_t *)(file->audioStream->sampleBuffer),
2132 out_samples, channels, -1,
2133 out_fmt, frame->buffer + frame->size);
2134
2135 /* mark the amount of bytes still in the buffer */
2136 file->audioStream->sampleBufferSize = ((in_samples - out_samples) * channels * in_bps);
2137
2138 /* set the offset so the remaining data can be found */
2139 if (av_sample_fmt_is_planar(in_fmt))
2140 file->audioStream->sampleBufferOffset = out_samples * in_bps;
2141 else
2142 file->audioStream->sampleBufferOffset = out_samples * in_bps * channels;
2143
2144 /* update framesize */
2145 frame->size += written;
2146 }
2147 else
2148 {
2149 /* copy data from sampleBuffer into frame buffer until sampleBuffer is empty */
2150 int written = convert_audio(in_samples, channels, file->audioStream->sampleBufferStride,
2151 in_fmt, (uint8_t *)(file->audioStream->sampleBuffer),
2152 in_samples, channels, -1,
2153 out_fmt, frame->buffer + frame->size);
2154
2155 /* mark the amount of bytes still in the buffer */
2156 file->audioStream->sampleBufferSize = 0;
2157
2158 /* reset buffer offset */
2159 file->audioStream->sampleBufferOffset = 0;
2160
2161 /* update framesize */
2162 frame->size += written;
2163 }
2164 }
2165 else
2166 {
2167 /* no room in frame, mark samplebuffer as full */
2168 file->audioStream->sampleBufferSize = audioSize;
2169
2170 /* reset buffer offset */
2171 file->audioStream->sampleBufferOffset = 0;
2172 }
2173 }
2174
2175 /* pack was used, return 1 */
2176 return 1;
2177 }
2178
SDL_ffmpegDecodeVideoFrame(SDL_ffmpegFile * file,AVPacket * pack,SDL_ffmpegVideoFrame * frame)2179 int SDL_ffmpegDecodeVideoFrame( SDL_ffmpegFile* file, AVPacket *pack, SDL_ffmpegVideoFrame *frame )
2180 {
2181 int got_frame = 0;
2182
2183 if ( pack )
2184 {
2185 /* usefull when dealing with B frames */
2186 if ( pack->dts == AV_NOPTS_VALUE )
2187 {
2188 /* if we did not get a valid timestamp, we make one up based on the last
2189 valid timestamp + the duration of a frame */
2190 frame->pts = file->videoStream->lastTimeStamp + av_rescale( 1000 * pack->duration, file->videoStream->_ffmpeg->time_base.num, file->videoStream->_ffmpeg->time_base.den );
2191 }
2192 else
2193 {
2194 /* write timestamp into the buffer */
2195 frame->pts = av_rescale(( pack->dts - file->videoStream->_ffmpeg->start_time ) * 1000, file->videoStream->_ffmpeg->time_base.num, file->videoStream->_ffmpeg->time_base.den );
2196 }
2197
2198 /* Decode the packet */
2199 #if ( ( LIBAVCODEC_VERSION_MAJOR <= 52 ) && ( LIBAVCODEC_VERSION_MINOR <= 20 ) )
2200 avcodec_decode_video( file->videoStream->_ffmpeg->codec, file->videoStream->decodeFrame, &got_frame, pack->data, pack->size );
2201 #else
2202 avcodec_decode_video2( file->videoStream->_ffmpeg->codec, file->videoStream->decodeFrame, &got_frame, pack );
2203 #endif
2204 }
2205 else
2206 {
2207 /* check if there is still a frame left in the buffer */
2208
2209 #if ( LIBAVCODEC_VERSION_MAJOR <= 52 && LIBAVCODEC_VERSION_MINOR <= 20 )
2210 avcodec_decode_video( file->videoStream->_ffmpeg->codec, file->videoStream->decodeFrame, &got_frame, 0, 0 );
2211 #else
2212 AVPacket temp;
2213 av_init_packet( &temp );
2214 temp.data = 0;
2215 temp.size = 0;
2216 temp.stream_index = file->videoStream->_ffmpeg->index;
2217 avcodec_decode_video2( file->videoStream->_ffmpeg->codec, file->videoStream->decodeFrame, &got_frame, &temp );
2218 #endif
2219 }
2220
2221 /* if we did not get a frame, we return */
2222 if ( got_frame )
2223 {
2224 /* convert YUV 420 to YUYV 422 data */
2225 // if ( frame->overlay && frame->overlay->format == SDL_YUY2_OVERLAY )
2226 // {
2227 // int pitch[] =
2228 // {
2229 // frame->overlay->pitches[ 0 ],
2230 // frame->overlay->pitches[ 1 ],
2231 // frame->overlay->pitches[ 2 ]
2232 // };
2233 //
2234 // sws_scale( getContext( &file->videoStream->conversionContext,
2235 // file->videoStream->_ffmpeg->codec->width,
2236 // file->videoStream->_ffmpeg->codec->height,
2237 // file->videoStream->_ffmpeg->codec->pix_fmt,
2238 // frame->overlay->w, frame->overlay->h,
2239 // AV_PIX_FMT_YUYV422 ),
2240 // ( const uint8_t* const* )file->videoStream->decodeFrame->data,
2241 // file->videoStream->decodeFrame->linesize,
2242 // 0,
2243 // file->videoStream->_ffmpeg->codec->height,
2244 // ( uint8_t* const* )frame->overlay->pixels,
2245 // pitch );
2246 // }
2247
2248 /* convert YUV to RGB data */
2249 if ( frame->surface && frame->surface->format )
2250 {
2251 int pitch = frame->surface->pitch;
2252
2253 switch ( frame->surface->format->BitsPerPixel )
2254 {
2255 case 32:
2256 sws_scale( getContext( &file->videoStream->conversionContext,
2257 file->videoStream->_ffmpeg->codec->width,
2258 file->videoStream->_ffmpeg->codec->height,
2259 file->videoStream->_ffmpeg->codec->pix_fmt,
2260 frame->surface->w, frame->surface->h,
2261 AV_PIX_FMT_RGB32 ),
2262 ( const uint8_t* const* )file->videoStream->decodeFrame->data,
2263 file->videoStream->decodeFrame->linesize,
2264 0,
2265 file->videoStream->_ffmpeg->codec->height,
2266 ( uint8_t* const* )&frame->surface->pixels,
2267 &pitch );
2268 break;
2269 case 24:
2270 sws_scale( getContext( &file->videoStream->conversionContext,
2271 file->videoStream->_ffmpeg->codec->width,
2272 file->videoStream->_ffmpeg->codec->height,
2273 file->videoStream->_ffmpeg->codec->pix_fmt,
2274 frame->surface->w, frame->surface->h,
2275 AV_PIX_FMT_RGB24 ),
2276 ( const uint8_t* const* )file->videoStream->decodeFrame->data,
2277 file->videoStream->decodeFrame->linesize,
2278 0,
2279 file->videoStream->_ffmpeg->codec->height,
2280 ( uint8_t* const* )&frame->surface->pixels,
2281 &pitch );
2282 break;
2283 default:
2284 break;
2285 }
2286 }
2287
2288 /* we write the lastTimestamp we got */
2289 file->videoStream->lastTimeStamp = frame->pts;
2290
2291 /* flag this frame as ready */
2292 frame->ready = 1;
2293 }
2294
2295 return frame->ready;
2296 }
2297 /**
2298 \endcond
2299 */
2300