2 * FFmpeg Decoder Helpers
4 * Copyright (c) 2011 by Chris Robinson <chris.kcat@gmail.com>
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 /* This file contains routines for helping to decode audio using libavformat
26 * and libavcodec (ffmpeg). There's very little OpenAL-specific code here. */
38 #include "alhelpers.h"
42 static size_t NextPowerOf2(size_t value
)
65 static int MemData_read(void *opaque
, uint8_t *buf
, int buf_size
)
67 struct MemData
*membuf
= (struct MemData
*)opaque
;
68 int rem
= membuf
->length
- membuf
->pos
;
73 memcpy(buf
, &membuf
->buffer
[membuf
->pos
], rem
);
79 static int MemData_write(void *opaque
, uint8_t *buf
, int buf_size
)
81 struct MemData
*membuf
= (struct MemData
*)opaque
;
82 int rem
= membuf
->length
- membuf
->pos
;
87 memcpy(&membuf
->buffer
[membuf
->pos
], buf
, rem
);
93 static int64_t MemData_seek(void *opaque
, int64_t offset
, int whence
)
95 struct MemData
*membuf
= (struct MemData
*)opaque
;
97 whence
&= ~AVSEEK_FORCE
;
101 if(offset
< 0 || (uint64_t)offset
> membuf
->length
)
103 membuf
->pos
= offset
;
107 if((offset
>= 0 && (uint64_t)offset
> membuf
->length
-membuf
->pos
) ||
108 (offset
< 0 && (uint64_t)(-offset
) > membuf
->pos
))
110 membuf
->pos
+= offset
;
114 if(offset
> 0 || (uint64_t)(-offset
) > membuf
->length
)
116 membuf
->pos
= membuf
->length
+ offset
;
120 return membuf
->length
;
132 struct PacketList
*next
;
136 AVCodecContext
*CodecCtx
;
139 struct PacketList
*Packets
;
143 const uint8_t *FrameData
;
144 size_t FrameDataSize
;
150 AVFormatContext
*FmtCtx
;
155 struct MemData membuf
;
159 static int done_init
= 0;
161 FilePtr
openAVFile(const char *fname
)
165 /* We need to make sure ffmpeg is initialized. Optionally silence warning
166 * output from the lib */
167 if(!done_init
) {av_register_all();
168 av_log_set_level(AV_LOG_ERROR
);
171 file
= (FilePtr
)calloc(1, sizeof(*file
));
172 if(file
&& avformat_open_input(&file
->FmtCtx
, fname
, NULL
, NULL
) == 0)
174 /* After opening, we must search for the stream information because not
175 * all formats will have it in stream headers */
176 if(avformat_find_stream_info(file
->FmtCtx
, NULL
) >= 0)
178 avformat_close_input(&file
->FmtCtx
);
185 FilePtr
openAVData(const char *name
, char *buffer
, size_t buffer_len
)
189 if(!done_init
) {av_register_all();
190 av_log_set_level(AV_LOG_ERROR
);
196 file
= (FilePtr
)calloc(1, sizeof(*file
));
197 if(file
&& (file
->FmtCtx
=avformat_alloc_context()) != NULL
)
199 file
->membuf
.buffer
= buffer
;
200 file
->membuf
.length
= buffer_len
;
201 file
->membuf
.pos
= 0;
203 file
->FmtCtx
->pb
= avio_alloc_context(NULL
, 0, 0, &file
->membuf
,
204 MemData_read
, MemData_write
,
206 if(file
->FmtCtx
->pb
&& avformat_open_input(&file
->FmtCtx
, name
, NULL
, NULL
) == 0)
208 if(avformat_find_stream_info(file
->FmtCtx
, NULL
) >= 0)
210 avformat_close_input(&file
->FmtCtx
);
213 avformat_free_context(file
->FmtCtx
);
221 FilePtr
openAVCustom(const char *name
, void *user_data
,
222 int (*read_packet
)(void *user_data
, uint8_t *buf
, int buf_size
),
223 int (*write_packet
)(void *user_data
, uint8_t *buf
, int buf_size
),
224 int64_t (*seek
)(void *user_data
, int64_t offset
, int whence
))
228 if(!done_init
) {av_register_all();
229 av_log_set_level(AV_LOG_ERROR
);
235 file
= (FilePtr
)calloc(1, sizeof(*file
));
236 if(file
&& (file
->FmtCtx
=avformat_alloc_context()) != NULL
)
238 file
->FmtCtx
->pb
= avio_alloc_context(NULL
, 0, 0, user_data
,
239 read_packet
, write_packet
, seek
);
240 if(file
->FmtCtx
->pb
&& avformat_open_input(&file
->FmtCtx
, name
, NULL
, NULL
) == 0)
242 if(avformat_find_stream_info(file
->FmtCtx
, NULL
) >= 0)
244 avformat_close_input(&file
->FmtCtx
);
247 avformat_free_context(file
->FmtCtx
);
256 void clearAVAudioData(StreamPtr stream
)
258 while(stream
->Packets
)
260 struct PacketList
*self
;
262 self
= stream
->Packets
;
263 stream
->Packets
= self
->next
;
265 av_free_packet(&self
->pkt
);
271 void closeAVFile(FilePtr file
)
277 for(i
= 0;i
< file
->StreamsSize
;i
++)
279 StreamPtr stream
= file
->Streams
[i
];
281 while(stream
->Packets
)
283 struct PacketList
*self
;
285 self
= stream
->Packets
;
286 stream
->Packets
= self
->next
;
288 av_free_packet(&self
->pkt
);
292 avcodec_close(stream
->CodecCtx
);
293 av_free(stream
->Frame
);
298 avformat_close_input(&file
->FmtCtx
);
303 int getAVFileInfo(FilePtr file
, int *numaudiostreams
)
309 for(i
= 0;i
< file
->FmtCtx
->nb_streams
;i
++)
311 if(file
->FmtCtx
->streams
[i
]->codec
->codec_type
== AVMEDIA_TYPE_AUDIO
)
314 *numaudiostreams
= audiocount
;
318 StreamPtr
getAVAudioStream(FilePtr file
, int streamnum
)
321 if(!file
) return NULL
;
322 for(i
= 0;i
< file
->FmtCtx
->nb_streams
;i
++)
324 if(file
->FmtCtx
->streams
[i
]->codec
->codec_type
!= AVMEDIA_TYPE_AUDIO
)
334 /* Found the requested stream. Check if a handle to this stream
335 * already exists and return it if it does */
336 for(j
= 0;j
< file
->StreamsSize
;j
++)
338 if(file
->Streams
[j
]->StreamIdx
== (int)i
)
339 return file
->Streams
[j
];
342 /* Doesn't yet exist. Now allocate a new stream object and fill in
344 stream
= (StreamPtr
)calloc(1, sizeof(*stream
));
345 if(!stream
) return NULL
;
347 stream
->parent
= file
;
348 stream
->CodecCtx
= file
->FmtCtx
->streams
[i
]->codec
;
349 stream
->StreamIdx
= i
;
351 /* Try to find the codec for the given codec ID, and open it */
352 codec
= avcodec_find_decoder(stream
->CodecCtx
->codec_id
);
353 if(!codec
|| avcodec_open2(stream
->CodecCtx
, codec
, NULL
) < 0)
359 /* Allocate space for the decoded data to be stored in before it
360 * gets passed to the app */
361 stream
->Frame
= avcodec_alloc_frame();
364 avcodec_close(stream
->CodecCtx
);
368 stream
->FrameData
= NULL
;
369 stream
->FrameDataSize
= 0;
371 /* Append the new stream object to the stream list. The original
372 * pointer will remain valid if realloc fails, so we need to use
373 * another pointer to watch for errors and not leak memory */
374 temp
= realloc(file
->Streams
, (file
->StreamsSize
+1) *
375 sizeof(*file
->Streams
));
378 avcodec_close(stream
->CodecCtx
);
379 av_free(stream
->Frame
);
383 file
->Streams
= (StreamPtr
*)temp
;
384 file
->Streams
[file
->StreamsSize
++] = stream
;
392 int getAVAudioInfo(StreamPtr stream
, ALuint
*rate
, ALenum
*channels
, ALenum
*type
)
394 if(!stream
|| stream
->CodecCtx
->codec_type
!= AVMEDIA_TYPE_AUDIO
)
397 /* Get the sample type for OpenAL given the format detected by ffmpeg. */
398 if(stream
->CodecCtx
->sample_fmt
== AV_SAMPLE_FMT_U8
)
399 *type
= AL_UNSIGNED_BYTE_SOFT
;
400 else if(stream
->CodecCtx
->sample_fmt
== AV_SAMPLE_FMT_S16
)
401 *type
= AL_SHORT_SOFT
;
402 else if(stream
->CodecCtx
->sample_fmt
== AV_SAMPLE_FMT_S32
)
404 else if(stream
->CodecCtx
->sample_fmt
== AV_SAMPLE_FMT_FLT
)
405 *type
= AL_FLOAT_SOFT
;
406 else if(stream
->CodecCtx
->sample_fmt
== AV_SAMPLE_FMT_DBL
)
407 *type
= AL_DOUBLE_SOFT
;
410 fprintf(stderr
, "Unsupported ffmpeg sample format: %s\n",
411 av_get_sample_fmt_name(stream
->CodecCtx
->sample_fmt
));
415 /* Get the OpenAL channel configuration using the channel layout detected
416 * by ffmpeg. NOTE: some file types may not specify a channel layout. In
417 * that case, one must be guessed based on the channel count. */
418 if(stream
->CodecCtx
->channel_layout
== AV_CH_LAYOUT_MONO
)
419 *channels
= AL_MONO_SOFT
;
420 else if(stream
->CodecCtx
->channel_layout
== AV_CH_LAYOUT_STEREO
)
421 *channels
= AL_STEREO_SOFT
;
422 else if(stream
->CodecCtx
->channel_layout
== AV_CH_LAYOUT_QUAD
)
423 *channels
= AL_QUAD_SOFT
;
424 else if(stream
->CodecCtx
->channel_layout
== AV_CH_LAYOUT_5POINT1_BACK
)
425 *channels
= AL_5POINT1_SOFT
;
426 else if(stream
->CodecCtx
->channel_layout
== AV_CH_LAYOUT_7POINT1
)
427 *channels
= AL_7POINT1_SOFT
;
428 else if(stream
->CodecCtx
->channel_layout
== 0)
430 /* Unknown channel layout. Try to guess. */
431 if(stream
->CodecCtx
->channels
== 1)
432 *channels
= AL_MONO_SOFT
;
433 else if(stream
->CodecCtx
->channels
== 2)
434 *channels
= AL_STEREO_SOFT
;
437 fprintf(stderr
, "Unsupported ffmpeg raw channel count: %d\n",
438 stream
->CodecCtx
->channels
);
445 av_get_channel_layout_string(str
, sizeof(str
), stream
->CodecCtx
->channels
,
446 stream
->CodecCtx
->channel_layout
);
447 fprintf(stderr
, "Unsupported ffmpeg channel layout: %s\n", str
);
451 *rate
= stream
->CodecCtx
->sample_rate
;
457 /* Used by getAV*Data to search for more compressed data, and buffer it in the
458 * correct stream. It won't buffer data for streams that the app doesn't have a
460 static int getNextPacket(FilePtr file
, int streamidx
)
462 struct PacketList
*packet
;
464 packet
= (struct PacketList
*)av_malloc(sizeof(*packet
));
468 while(av_read_frame(file
->FmtCtx
, &packet
->pkt
) >= 0)
470 StreamPtr
*iter
= file
->Streams
;
471 StreamPtr
*iter_end
= iter
+ file
->StreamsSize
;
473 /* Check each stream the user has a handle for, looking for the one
474 * this packet belongs to */
475 while(iter
!= iter_end
)
477 if((*iter
)->StreamIdx
== packet
->pkt
.stream_index
)
479 struct PacketList
**last
;
481 last
= &(*iter
)->Packets
;
483 last
= &(*last
)->next
;
486 if((*iter
)->StreamIdx
== streamidx
)
489 packet
= (struct PacketList
*)av_malloc(sizeof(*packet
));
495 /* Free the packet and look for another */
496 av_free_packet(&packet
->pkt
);
503 uint8_t *getAVAudioData(StreamPtr stream
, size_t *length
)
508 if(length
) *length
= 0;
510 if(!stream
|| stream
->CodecCtx
->codec_type
!= AVMEDIA_TYPE_AUDIO
)
514 if(!stream
->Packets
&& !getNextPacket(stream
->parent
, stream
->StreamIdx
))
517 /* Decode some data, and check for errors */
518 avcodec_get_frame_defaults(stream
->Frame
);
519 while((len
=avcodec_decode_audio4(stream
->CodecCtx
, stream
->Frame
,
520 &got_frame
, &stream
->Packets
->pkt
)) < 0)
522 struct PacketList
*self
;
524 /* Error? Drop it and try the next, I guess... */
525 self
= stream
->Packets
;
526 stream
->Packets
= self
->next
;
528 av_free_packet(&self
->pkt
);
535 if(len
< stream
->Packets
->pkt
.size
)
537 /* Move the unread data to the front and clear the end bits */
538 int remaining
= stream
->Packets
->pkt
.size
- len
;
539 memmove(stream
->Packets
->pkt
.data
, &stream
->Packets
->pkt
.data
[len
],
541 memset(&stream
->Packets
->pkt
.data
[remaining
], 0,
542 stream
->Packets
->pkt
.size
- remaining
);
543 stream
->Packets
->pkt
.size
-= len
;
547 struct PacketList
*self
;
549 self
= stream
->Packets
;
550 stream
->Packets
= self
->next
;
552 av_free_packet(&self
->pkt
);
556 if(!got_frame
|| stream
->Frame
->nb_samples
== 0)
559 /* Set the output buffer size */
560 *length
= av_samples_get_buffer_size(NULL
, stream
->CodecCtx
->channels
,
561 stream
->Frame
->nb_samples
,
562 stream
->CodecCtx
->sample_fmt
, 1);
564 return stream
->Frame
->data
[0];
567 size_t readAVAudioData(StreamPtr stream
, void *data
, size_t length
)
571 if(!stream
|| stream
->CodecCtx
->codec_type
!= AVMEDIA_TYPE_AUDIO
)
576 /* If there's no decoded data, find some */
577 if(stream
->FrameDataSize
== 0)
579 stream
->FrameData
= getAVAudioData(stream
, &stream
->FrameDataSize
);
580 if(!stream
->FrameData
)
584 if(stream
->FrameDataSize
> 0)
586 /* Get the amount of bytes remaining to be written, and clamp to
587 * the amount of decoded data we have */
588 size_t rem
= length
-dec
;
589 if(rem
> stream
->FrameDataSize
)
590 rem
= stream
->FrameDataSize
;
592 /* Copy the data to the app's buffer and increment */
595 memcpy(data
, stream
->FrameData
, rem
);
596 data
= (char*)data
+ rem
;
600 /* If there's any decoded data left, move it to the front of the
601 * buffer for next time */
602 stream
->FrameData
+= rem
;
603 stream
->FrameDataSize
-= rem
;
607 /* Return the number of bytes we were able to get */
611 void *decodeAVAudioStream(StreamPtr stream
, size_t *length
)
619 if(!stream
|| stream
->CodecCtx
->codec_type
!= AVMEDIA_TYPE_AUDIO
)
622 while((inbuf
=getAVAudioData(stream
, &got
)) != NULL
&& got
> 0)
626 ptr
= realloc(outbuf
, NextPowerOf2(buflen
+got
));
631 memcpy(&outbuf
[buflen
], inbuf
, got
);
634 outbuf
= (char*)realloc(outbuf
, buflen
);