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