Print the mmdevapi device and GUID found
[openal-soft.git] / examples / alffmpeg.c
blobb6619a57d96d7caf22c9e39b7378f9698565d609
1 /*
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
22 * THE SOFTWARE.
25 /* This file contains routines for helping to decode audio using libavformat
26 * and libavcodec (ffmpeg). There's very little OpenAL-specific code here. */
28 #include <string.h>
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <signal.h>
32 #include <assert.h>
34 #include "AL/al.h"
35 #include "AL/alc.h"
36 #include "AL/alext.h"
38 #include "alhelpers.h"
39 #include "alffmpeg.h"
42 static size_t NextPowerOf2(size_t value)
44 size_t powerOf2 = 1;
46 if(value)
48 value--;
49 while(value)
51 value >>= 1;
52 powerOf2 <<= 1;
55 return powerOf2;
59 struct MemData {
60 char *buffer;
61 size_t length;
62 size_t pos;
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;
70 if(rem > buf_size)
71 rem = buf_size;
73 memcpy(buf, &membuf->buffer[membuf->pos], rem);
74 membuf->pos += rem;
76 return 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;
84 if(rem > buf_size)
85 rem = buf_size;
87 memcpy(&membuf->buffer[membuf->pos], buf, rem);
88 membuf->pos += rem;
90 return 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;
98 switch(whence)
100 case SEEK_SET:
101 if(offset < 0 || offset > membuf->length)
102 return -1;
103 membuf->pos = offset;
104 break;
106 case SEEK_CUR:
107 if((offset >= 0 && offset > membuf->length-membuf->pos) ||
108 (offset < 0 && offset < -membuf->pos))
109 return -1;
110 membuf->pos += offset;
111 break;
113 case SEEK_END:
114 if(offset > 0 || offset < -membuf->length)
115 return -1;
116 membuf->pos = membuf->length + offset;
117 break;
119 case AVSEEK_SIZE:
120 return membuf->length;
122 default:
123 return -1;
126 return membuf->pos;
130 struct PacketList {
131 AVPacket pkt;
132 struct PacketList *next;
135 struct MyStream {
136 AVCodecContext *CodecCtx;
137 int StreamIdx;
139 struct PacketList *Packets;
141 char *DecodedData;
142 size_t DecodedDataSize;
144 FilePtr parent;
147 struct MyFile {
148 AVFormatContext *FmtCtx;
150 StreamPtr *Streams;
151 size_t StreamsSize;
153 struct MemData membuf;
157 static int done_init = 0;
159 FilePtr openAVFile(const char *fname)
161 FilePtr file;
163 /* We need to make sure ffmpeg is initialized. Optionally silence warning
164 * output from the lib */
165 if(!done_init) {av_register_all();
166 av_log_set_level(AV_LOG_ERROR);
167 done_init = 1;}
169 file = (FilePtr)calloc(1, sizeof(*file));
170 if(file && avformat_open_input(&file->FmtCtx, fname, NULL, NULL) == 0)
172 /* After opening, we must search for the stream information because not
173 * all formats will have it in stream headers */
174 if(avformat_find_stream_info(file->FmtCtx, NULL) >= 0)
175 return file;
176 av_close_input_file(file->FmtCtx);
179 free(file);
180 return NULL;
183 FilePtr openAVData(const char *name, char *buffer, size_t buffer_len)
185 FilePtr file;
187 if(!done_init) {av_register_all();
188 av_log_set_level(AV_LOG_ERROR);
189 done_init = 1;}
191 if(!name)
192 name = "";
194 file = (FilePtr)calloc(1, sizeof(*file));
195 if(file && (file->FmtCtx=avformat_alloc_context()) != NULL)
197 file->membuf.buffer = buffer;
198 file->membuf.length = buffer_len;
199 file->membuf.pos = 0;
201 file->FmtCtx->pb = avio_alloc_context(NULL, 0, 0, &file->membuf,
202 MemData_read, MemData_write,
203 MemData_seek);
204 if(file->FmtCtx->pb && avformat_open_input(&file->FmtCtx, name, NULL, NULL) == 0)
206 if(avformat_find_stream_info(file->FmtCtx, NULL) >= 0)
207 return file;
209 av_close_input_file(file->FmtCtx);
212 free(file);
213 return NULL;
216 FilePtr openAVCustom(const char *name, void *user_data,
217 int (*read_packet)(void *user_data, uint8_t *buf, int buf_size),
218 int (*write_packet)(void *user_data, uint8_t *buf, int buf_size),
219 int64_t (*seek)(void *user_data, int64_t offset, int whence))
221 FilePtr file;
223 if(!done_init) {av_register_all();
224 av_log_set_level(AV_LOG_ERROR);
225 done_init = 1;}
227 if(!name)
228 name = "";
230 file = (FilePtr)calloc(1, sizeof(*file));
231 if(file && (file->FmtCtx=avformat_alloc_context()) != NULL)
233 file->FmtCtx->pb = avio_alloc_context(NULL, 0, 0, user_data,
234 read_packet, write_packet, seek);
235 if(file->FmtCtx->pb && avformat_open_input(&file->FmtCtx, name, NULL, NULL) == 0)
237 if(avformat_find_stream_info(file->FmtCtx, NULL) >= 0)
238 return file;
240 av_close_input_file(file->FmtCtx);
243 free(file);
244 return NULL;
248 void closeAVFile(FilePtr file)
250 size_t i;
252 if(!file) return;
254 for(i = 0;i < file->StreamsSize;i++)
256 StreamPtr stream = file->Streams[i];
258 while(stream->Packets)
260 struct PacketList *self;
262 self = stream->Packets;
263 stream->Packets = self->next;
265 av_free_packet(&self->pkt);
266 av_free(self);
269 avcodec_close(stream->CodecCtx);
270 av_free(stream->DecodedData);
271 free(stream);
273 free(file->Streams);
275 av_close_input_file(file->FmtCtx);
276 free(file);
280 int getAVFileInfo(FilePtr file, int *numaudiostreams)
282 unsigned int i;
283 int audiocount = 0;
285 if(!file) return 1;
286 for(i = 0;i < file->FmtCtx->nb_streams;i++)
288 if(file->FmtCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
289 audiocount++;
291 *numaudiostreams = audiocount;
292 return 0;
295 StreamPtr getAVAudioStream(FilePtr file, int streamnum)
297 unsigned int i;
298 if(!file) return NULL;
299 for(i = 0;i < file->FmtCtx->nb_streams;i++)
301 if(file->FmtCtx->streams[i]->codec->codec_type != AVMEDIA_TYPE_AUDIO)
302 continue;
304 if(streamnum == 0)
306 StreamPtr stream;
307 AVCodec *codec;
308 void *temp;
309 size_t j;
311 /* Found the requested stream. Check if a handle to this stream
312 * already exists and return it if it does */
313 for(j = 0;j < file->StreamsSize;j++)
315 if(file->Streams[j]->StreamIdx == (int)i)
316 return file->Streams[j];
319 /* Doesn't yet exist. Now allocate a new stream object and fill in
320 * its info */
321 stream = (StreamPtr)calloc(1, sizeof(*stream));
322 if(!stream) return NULL;
324 stream->parent = file;
325 stream->CodecCtx = file->FmtCtx->streams[i]->codec;
326 stream->StreamIdx = i;
328 /* Try to find the codec for the given codec ID, and open it */
329 codec = avcodec_find_decoder(stream->CodecCtx->codec_id);
330 if(!codec || avcodec_open(stream->CodecCtx, codec) < 0)
332 free(stream);
333 return NULL;
336 /* Allocate space for the decoded data to be stored in before it
337 * gets passed to the app */
338 stream->DecodedData = (char*)av_malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE);
339 if(!stream->DecodedData)
341 avcodec_close(stream->CodecCtx);
342 free(stream);
343 return NULL;
346 /* Append the new stream object to the stream list. The original
347 * pointer will remain valid if realloc fails, so we need to use
348 * another pointer to watch for errors and not leak memory */
349 temp = realloc(file->Streams, (file->StreamsSize+1) *
350 sizeof(*file->Streams));
351 if(!temp)
353 avcodec_close(stream->CodecCtx);
354 av_free(stream->DecodedData);
355 free(stream);
356 return NULL;
358 file->Streams = (StreamPtr*)temp;
359 file->Streams[file->StreamsSize++] = stream;
360 return stream;
362 streamnum--;
364 return NULL;
367 int getAVAudioInfo(StreamPtr stream, ALuint *rate, ALenum *channels, ALenum *type)
369 if(!stream || stream->CodecCtx->codec_type != AVMEDIA_TYPE_AUDIO)
370 return 1;
372 /* Get the sample type for OpenAL given the format detected by ffmpeg. */
373 if(stream->CodecCtx->sample_fmt == AV_SAMPLE_FMT_U8)
374 *type = AL_UNSIGNED_BYTE_SOFT;
375 else if(stream->CodecCtx->sample_fmt == AV_SAMPLE_FMT_S16)
376 *type = AL_SHORT_SOFT;
377 else if(stream->CodecCtx->sample_fmt == AV_SAMPLE_FMT_S32)
378 *type = AL_INT_SOFT;
379 else if(stream->CodecCtx->sample_fmt == AV_SAMPLE_FMT_FLT)
380 *type = AL_FLOAT_SOFT;
381 else if(stream->CodecCtx->sample_fmt == AV_SAMPLE_FMT_DBL)
382 *type = AL_DOUBLE_SOFT;
383 else
384 return 1;
386 /* Get the OpenAL channel configuration using the channel layout detected
387 * by ffmpeg. NOTE: some file types may not specify a channel layout. In
388 * that case, one must be guessed based on the channel count. */
389 if(stream->CodecCtx->channel_layout == AV_CH_LAYOUT_MONO)
390 *channels = AL_MONO_SOFT;
391 else if(stream->CodecCtx->channel_layout == AV_CH_LAYOUT_STEREO)
392 *channels = AL_STEREO_SOFT;
393 else if(stream->CodecCtx->channel_layout == AV_CH_LAYOUT_QUAD)
394 *channels = AL_QUAD_SOFT;
395 else if(stream->CodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1)
396 *channels = AL_5POINT1_SOFT;
397 else if(stream->CodecCtx->channel_layout == AV_CH_LAYOUT_7POINT1)
398 *channels = AL_7POINT1_SOFT;
399 else if(stream->CodecCtx->channel_layout == 0)
401 /* Unknown channel layout. Try to guess. */
402 if(stream->CodecCtx->channels == 1)
403 *channels = AL_MONO_SOFT;
404 else if(stream->CodecCtx->channels == 2)
405 *channels = AL_STEREO_SOFT;
406 else
407 return 1;
409 else
410 return 1;
412 *rate = stream->CodecCtx->sample_rate;
414 return 0;
418 /* Used by getAV*Data to search for more compressed data, and buffer it in the
419 * correct stream. It won't buffer data for streams that the app doesn't have a
420 * handle for. */
421 static int getNextPacket(FilePtr file, int streamidx)
423 struct PacketList *packet;
425 packet = (struct PacketList*)av_malloc(sizeof(*packet));
426 packet->next = NULL;
428 next_packet:
429 while(av_read_frame(file->FmtCtx, &packet->pkt) >= 0)
431 StreamPtr *iter = file->Streams;
432 StreamPtr *iter_end = iter + file->StreamsSize;
434 /* Check each stream the user has a handle for, looking for the one
435 * this packet belongs to */
436 while(iter != iter_end)
438 if((*iter)->StreamIdx == packet->pkt.stream_index)
440 struct PacketList **last;
442 last = &(*iter)->Packets;
443 while(*last != NULL)
444 last = &(*last)->next;
446 *last = packet;
447 if((*iter)->StreamIdx == streamidx)
448 return 1;
450 packet = (struct PacketList*)av_malloc(sizeof(*packet));
451 packet->next = NULL;
452 goto next_packet;
454 iter++;
456 /* Free the packet and look for another */
457 av_free_packet(&packet->pkt);
460 av_free(packet);
461 return 0;
464 void *getAVAudioData(StreamPtr stream, size_t *length)
466 int size;
467 int len;
469 if(length) *length = 0;
471 if(!stream || stream->CodecCtx->codec_type != AVMEDIA_TYPE_AUDIO)
472 return NULL;
474 stream->DecodedDataSize = 0;
476 next_packet:
477 if(!stream->Packets && !getNextPacket(stream->parent, stream->StreamIdx))
478 return NULL;
480 /* Decode some data, and check for errors */
481 size = AVCODEC_MAX_AUDIO_FRAME_SIZE;
482 while((len=avcodec_decode_audio3(stream->CodecCtx,
483 (int16_t*)stream->DecodedData, &size,
484 &stream->Packets->pkt)) == 0)
486 struct PacketList *self;
488 if(size > 0)
489 break;
491 /* Packet went unread and no data was given? Drop it and try the next,
492 * I guess... */
493 self = stream->Packets;
494 stream->Packets = self->next;
496 av_free_packet(&self->pkt);
497 av_free(self);
499 if(!stream->Packets)
500 goto next_packet;
502 size = AVCODEC_MAX_AUDIO_FRAME_SIZE;
505 if(len < 0)
506 return NULL;
508 if(len < stream->Packets->pkt.size)
510 /* Move the unread data to the front and clear the end bits */
511 int remaining = stream->Packets->pkt.size - len;
512 memmove(stream->Packets->pkt.data, &stream->Packets->pkt.data[len],
513 remaining);
514 memset(&stream->Packets->pkt.data[remaining], 0,
515 stream->Packets->pkt.size - remaining);
516 stream->Packets->pkt.size -= len;
518 else
520 struct PacketList *self;
522 self = stream->Packets;
523 stream->Packets = self->next;
525 av_free_packet(&self->pkt);
526 av_free(self);
529 if(size == 0)
530 goto next_packet;
532 /* Set the output buffer size */
533 stream->DecodedDataSize = size;
534 if(length) *length = stream->DecodedDataSize;
536 return stream->DecodedData;
539 size_t readAVAudioData(StreamPtr stream, void *data, size_t length)
541 size_t dec = 0;
543 if(!stream || stream->CodecCtx->codec_type != AVMEDIA_TYPE_AUDIO)
544 return 0;
546 while(dec < length)
548 /* If there's no decoded data, find some */
549 if(stream->DecodedDataSize == 0)
551 if(getAVAudioData(stream, NULL) == NULL)
552 break;
555 if(stream->DecodedDataSize > 0)
557 /* Get the amount of bytes remaining to be written, and clamp to
558 * the amount of decoded data we have */
559 size_t rem = length-dec;
560 if(rem > stream->DecodedDataSize)
561 rem = stream->DecodedDataSize;
563 /* Copy the data to the app's buffer and increment */
564 if(data != NULL)
566 memcpy(data, stream->DecodedData, rem);
567 data = (char*)data + rem;
569 dec += rem;
571 /* If there's any decoded data left, move it to the front of the
572 * buffer for next time */
573 if(rem < stream->DecodedDataSize)
574 memmove(stream->DecodedData, &stream->DecodedData[rem],
575 stream->DecodedDataSize - rem);
576 stream->DecodedDataSize -= rem;
580 /* Return the number of bytes we were able to get */
581 return dec;
584 void *decodeAVAudioStream(StreamPtr stream, size_t *length)
586 char *outbuf = NULL;
587 size_t buflen = 0;
588 void *inbuf;
589 size_t got;
591 *length = 0;
592 if(!stream || stream->CodecCtx->codec_type != AVMEDIA_TYPE_AUDIO)
593 return NULL;
595 while((inbuf=getAVAudioData(stream, &got)) != NULL && got > 0)
597 void *ptr;
599 ptr = realloc(outbuf, NextPowerOf2(buflen+got));
600 if(ptr == NULL)
601 break;
602 outbuf = (char*)ptr;
604 memcpy(&outbuf[buflen], inbuf, got);
605 buflen += got;
607 outbuf = (char*)realloc(outbuf, buflen);
609 *length = buflen;
610 return outbuf;