zenilib  0.5.3.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
alffmpeg.c
Go to the documentation of this file.
1 /*
2  * FFmpeg Decoder Helpers
3  *
4  * Copyright (c) 2011 by Chris Robinson <chris.kcat@gmail.com>
5  *
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:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
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.
23  */
24 
25 /* This file contains routines for helping to decode audio using libavformat
26  * and libavcodec (ffmpeg). There's very little OpenAL-specific code here. */
27 
28 #include <string.h>
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <signal.h>
32 #include <assert.h>
33 
34 #include "AL/al.h"
35 #include "AL/alc.h"
36 #include "AL/alext.h"
37 
38 #include "alhelpers.h"
39 #include "alffmpeg.h"
40 
41 
42 static size_t NextPowerOf2(size_t value)
43 {
44  size_t powerOf2 = 1;
45 
46  if(value)
47  {
48  value--;
49  while(value)
50  {
51  value >>= 1;
52  powerOf2 <<= 1;
53  }
54  }
55  return powerOf2;
56 }
57 
58 
59 struct MemData {
60  char *buffer;
61  size_t length;
62  size_t pos;
63 };
64 
65 static int MemData_read(void *opaque, uint8_t *buf, int buf_size)
66 {
67  struct MemData *membuf = (struct MemData*)opaque;
68  int rem = membuf->length - membuf->pos;
69 
70  if(rem > buf_size)
71  rem = buf_size;
72 
73  memcpy(buf, &membuf->buffer[membuf->pos], rem);
74  membuf->pos += rem;
75 
76  return rem;
77 }
78 
79 static int MemData_write(void *opaque, uint8_t *buf, int buf_size)
80 {
81  struct MemData *membuf = (struct MemData*)opaque;
82  int rem = membuf->length - membuf->pos;
83 
84  if(rem > buf_size)
85  rem = buf_size;
86 
87  memcpy(&membuf->buffer[membuf->pos], buf, rem);
88  membuf->pos += rem;
89 
90  return rem;
91 }
92 
93 static int64_t MemData_seek(void *opaque, int64_t offset, int whence)
94 {
95  struct MemData *membuf = (struct MemData*)opaque;
96 
97  whence &= ~AVSEEK_FORCE;
98  switch(whence)
99  {
100  case SEEK_SET:
101  if(offset < 0 || (uint64_t)offset > membuf->length)
102  return -1;
103  membuf->pos = offset;
104  break;
105 
106  case SEEK_CUR:
107  if((offset >= 0 && (uint64_t)offset > membuf->length-membuf->pos) ||
108  (offset < 0 && (uint64_t)(-offset) > membuf->pos))
109  return -1;
110  membuf->pos += offset;
111  break;
112 
113  case SEEK_END:
114  if(offset > 0 || (uint64_t)(-offset) > membuf->length)
115  return -1;
116  membuf->pos = membuf->length + offset;
117  break;
118 
119  case AVSEEK_SIZE:
120  return membuf->length;
121 
122  default:
123  return -1;
124  }
125 
126  return membuf->pos;
127 }
128 
129 
130 struct PacketList {
131  AVPacket pkt;
132  struct PacketList *next;
133 };
134 
135 struct MyStream {
136  AVCodecContext *CodecCtx;
137  int StreamIdx;
138 
139  struct PacketList *Packets;
140 
141  AVFrame *Frame;
142 
143  const uint8_t *FrameData;
144  size_t FrameDataSize;
145 
146  FilePtr parent;
147 };
148 
149 struct MyFile {
150  AVFormatContext *FmtCtx;
151 
152  StreamPtr *Streams;
153  size_t StreamsSize;
154 
155  struct MemData membuf;
156 };
157 
158 
159 static int done_init = 0;
160 
161 FilePtr openAVFile(const char *fname)
162 {
163  FilePtr file;
164 
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);
169  done_init = 1;}
170 
171  file = (FilePtr)calloc(1, sizeof(*file));
172  if(file && avformat_open_input(&file->FmtCtx, fname, NULL, NULL) == 0)
173  {
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)
177  return file;
178  avformat_close_input(&file->FmtCtx);
179  }
180 
181  free(file);
182  return NULL;
183 }
184 
185 FilePtr openAVData(const char *name, char *buffer, size_t buffer_len)
186 {
187  FilePtr file;
188 
189  if(!done_init) {av_register_all();
190  av_log_set_level(AV_LOG_ERROR);
191  done_init = 1;}
192 
193  if(!name)
194  name = "";
195 
196  file = (FilePtr)calloc(1, sizeof(*file));
197  if(file && (file->FmtCtx=avformat_alloc_context()) != NULL)
198  {
199  file->membuf.buffer = buffer;
200  file->membuf.length = buffer_len;
201  file->membuf.pos = 0;
202 
203  file->FmtCtx->pb = avio_alloc_context(NULL, 0, 0, &file->membuf,
205  MemData_seek);
206  if(file->FmtCtx->pb && avformat_open_input(&file->FmtCtx, name, NULL, NULL) == 0)
207  {
208  if(avformat_find_stream_info(file->FmtCtx, NULL) >= 0)
209  return file;
210  avformat_close_input(&file->FmtCtx);
211  }
212  if(file->FmtCtx)
213  avformat_free_context(file->FmtCtx);
214  file->FmtCtx = NULL;
215  }
216 
217  free(file);
218  return NULL;
219 }
220 
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))
225 {
226  FilePtr file;
227 
228  if(!done_init) {av_register_all();
229  av_log_set_level(AV_LOG_ERROR);
230  done_init = 1;}
231 
232  if(!name)
233  name = "";
234 
235  file = (FilePtr)calloc(1, sizeof(*file));
236  if(file && (file->FmtCtx=avformat_alloc_context()) != NULL)
237  {
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)
241  {
242  if(avformat_find_stream_info(file->FmtCtx, NULL) >= 0)
243  return file;
244  avformat_close_input(&file->FmtCtx);
245  }
246  if(file->FmtCtx)
247  avformat_free_context(file->FmtCtx);
248  file->FmtCtx = NULL;
249  }
250 
251  free(file);
252  return NULL;
253 }
254 
255 
257 {
258  while(stream->Packets)
259  {
260  struct PacketList *self;
261 
262  self = stream->Packets;
263  stream->Packets = self->next;
264 
265  av_free_packet(&self->pkt);
266  av_free(self);
267  }
268 }
269 
270 
272 {
273  size_t i;
274 
275  if(!file) return;
276 
277  for(i = 0;i < file->StreamsSize;i++)
278  {
279  StreamPtr stream = file->Streams[i];
280 
281  while(stream->Packets)
282  {
283  struct PacketList *self;
284 
285  self = stream->Packets;
286  stream->Packets = self->next;
287 
288  av_free_packet(&self->pkt);
289  av_free(self);
290  }
291 
292  avcodec_close(stream->CodecCtx);
293  av_free(stream->Frame);
294  free(stream);
295  }
296  free(file->Streams);
297 
298  avformat_close_input(&file->FmtCtx);
299  free(file);
300 }
301 
302 
303 int getAVFileInfo(FilePtr file, int *numaudiostreams)
304 {
305  unsigned int i;
306  int audiocount = 0;
307 
308  if(!file) return 1;
309  for(i = 0;i < file->FmtCtx->nb_streams;i++)
310  {
311  if(file->FmtCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
312  audiocount++;
313  }
314  *numaudiostreams = audiocount;
315  return 0;
316 }
317 
319 {
320  unsigned int i;
321  if(!file) return NULL;
322  for(i = 0;i < file->FmtCtx->nb_streams;i++)
323  {
324  if(file->FmtCtx->streams[i]->codec->codec_type != AVMEDIA_TYPE_AUDIO)
325  continue;
326 
327  if(streamnum == 0)
328  {
330  AVCodec *codec;
331  void *temp;
332  size_t j;
333 
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++)
337  {
338  if(file->Streams[j]->StreamIdx == (int)i)
339  return file->Streams[j];
340  }
341 
342  /* Doesn't yet exist. Now allocate a new stream object and fill in
343  * its info */
344  stream = (StreamPtr)calloc(1, sizeof(*stream));
345  if(!stream) return NULL;
346 
347  stream->parent = file;
348  stream->CodecCtx = file->FmtCtx->streams[i]->codec;
349  stream->StreamIdx = i;
350 
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)
354  {
355  free(stream);
356  return NULL;
357  }
358 
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();
362  if(!stream->Frame)
363  {
364  avcodec_close(stream->CodecCtx);
365  free(stream);
366  return NULL;
367  }
368  stream->FrameData = NULL;
369  stream->FrameDataSize = 0;
370 
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));
376  if(!temp)
377  {
378  avcodec_close(stream->CodecCtx);
379  av_free(stream->Frame);
380  free(stream);
381  return NULL;
382  }
383  file->Streams = (StreamPtr*)temp;
384  file->Streams[file->StreamsSize++] = stream;
385  return stream;
386  }
387  streamnum--;
388  }
389  return NULL;
390 }
391 
393 {
394  if(!stream || stream->CodecCtx->codec_type != AVMEDIA_TYPE_AUDIO)
395  return 1;
396 
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)
403  *type = AL_INT_SOFT;
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;
408  else
409  {
410  fprintf(stderr, "Unsupported ffmpeg sample format: %s\n",
411  av_get_sample_fmt_name(stream->CodecCtx->sample_fmt));
412  return 1;
413  }
414 
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)
429  {
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;
435  else
436  {
437  fprintf(stderr, "Unsupported ffmpeg raw channel count: %d\n",
438  stream->CodecCtx->channels);
439  return 1;
440  }
441  }
442  else
443  {
444  char str[1024];
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);
448  return 1;
449  }
450 
451  *rate = stream->CodecCtx->sample_rate;
452 
453  return 0;
454 }
455 
456 
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
459  * handle for. */
460 static int getNextPacket(FilePtr file, int streamidx)
461 {
462  struct PacketList *packet;
463 
464  packet = (struct PacketList*)av_malloc(sizeof(*packet));
465  packet->next = NULL;
466 
467 next_packet:
468  while(av_read_frame(file->FmtCtx, &packet->pkt) >= 0)
469  {
470  StreamPtr *iter = file->Streams;
471  StreamPtr *iter_end = iter + file->StreamsSize;
472 
473  /* Check each stream the user has a handle for, looking for the one
474  * this packet belongs to */
475  while(iter != iter_end)
476  {
477  if((*iter)->StreamIdx == packet->pkt.stream_index)
478  {
479  struct PacketList **last;
480 
481  last = &(*iter)->Packets;
482  while(*last != NULL)
483  last = &(*last)->next;
484 
485  *last = packet;
486  if((*iter)->StreamIdx == streamidx)
487  return 1;
488 
489  packet = (struct PacketList*)av_malloc(sizeof(*packet));
490  packet->next = NULL;
491  goto next_packet;
492  }
493  iter++;
494  }
495  /* Free the packet and look for another */
496  av_free_packet(&packet->pkt);
497  }
498 
499  av_free(packet);
500  return 0;
501 }
502 
504 {
505  int got_frame;
506  int len;
507 
508  if(length) *length = 0;
509 
510  if(!stream || stream->CodecCtx->codec_type != AVMEDIA_TYPE_AUDIO)
511  return NULL;
512 
513 next_packet:
514  if(!stream->Packets && !getNextPacket(stream->parent, stream->StreamIdx))
515  return NULL;
516 
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)
521  {
522  struct PacketList *self;
523 
524  /* Error? Drop it and try the next, I guess... */
525  self = stream->Packets;
526  stream->Packets = self->next;
527 
528  av_free_packet(&self->pkt);
529  av_free(self);
530 
531  if(!stream->Packets)
532  goto next_packet;
533  }
534 
535  if(len < stream->Packets->pkt.size)
536  {
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],
540  remaining);
541  memset(&stream->Packets->pkt.data[remaining], 0,
542  stream->Packets->pkt.size - remaining);
543  stream->Packets->pkt.size -= len;
544  }
545  else
546  {
547  struct PacketList *self;
548 
549  self = stream->Packets;
550  stream->Packets = self->next;
551 
552  av_free_packet(&self->pkt);
553  av_free(self);
554  }
555 
556  if(!got_frame || stream->Frame->nb_samples == 0)
557  goto next_packet;
558 
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);
563 
564  return stream->Frame->data[0];
565 }
566 
568 {
569  size_t dec = 0;
570 
571  if(!stream || stream->CodecCtx->codec_type != AVMEDIA_TYPE_AUDIO)
572  return 0;
573 
574  while(dec < length)
575  {
576  /* If there's no decoded data, find some */
577  if(stream->FrameDataSize == 0)
578  {
579  stream->FrameData = getAVAudioData(stream, &stream->FrameDataSize);
580  if(!stream->FrameData)
581  break;
582  }
583 
584  if(stream->FrameDataSize > 0)
585  {
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;
591 
592  /* Copy the data to the app's buffer and increment */
593  if(data != NULL)
594  {
595  memcpy(data, stream->FrameData, rem);
596  data = (char*)data + rem;
597  }
598  dec += rem;
599 
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;
604  }
605  }
606 
607  /* Return the number of bytes we were able to get */
608  return dec;
609 }
610 
612 {
613  char *outbuf = NULL;
614  size_t buflen = 0;
615  void *inbuf;
616  size_t got;
617 
618  *length = 0;
619  if(!stream || stream->CodecCtx->codec_type != AVMEDIA_TYPE_AUDIO)
620  return NULL;
621 
622  while((inbuf=getAVAudioData(stream, &got)) != NULL && got > 0)
623  {
624  void *ptr;
625 
626  ptr = realloc(outbuf, NextPowerOf2(buflen+got));
627  if(ptr == NULL)
628  break;
629  outbuf = (char*)ptr;
630 
631  memcpy(&outbuf[buflen], inbuf, got);
632  buflen += got;
633  }
634  outbuf = (char*)realloc(outbuf, buflen);
635 
636  *length = buflen;
637  return outbuf;
638 }
static int getNextPacket(FilePtr file, int streamidx)
Definition: alffmpeg.c:460
struct MyFile * FilePtr
Definition: alffmpeg.h:13
#define AL_STEREO_SOFT
Definition: alext.h:210
GLint GLenum GLsizei GLsizei GLsizei GLint GLenum GLenum type
Definition: gl2ext.h:845
long long int64_t
Definition: types.h:10
FilePtr openAVFile(const char *fname)
Definition: alffmpeg.c:161
#define NULL
Definition: ftobjs.h:61
GLuint GLuint stream
Definition: glew.h:6573
void * decodeAVAudioStream(StreamPtr stream, size_t *length)
Definition: alffmpeg.c:611
#define memmove
Definition: SDL_qsort.c:81
SDL_EventEntry * free
Definition: SDL_events.c:80
static int done_init
Definition: alffmpeg.c:159
unsigned long long uint64_t
FilePtr openAVData(const char *name, char *buffer, size_t buffer_len)
Definition: alffmpeg.c:185
int32_t j
Definition: e_log.c:102
#define memset
Definition: SDL_malloc.c:633
FILE * file
Definition: visualinfo.c:88
EGLImageKHR EGLint * name
Definition: eglext.h:284
GLenum GLsizei len
Definition: glew.h:7035
static int MemData_read(void *opaque, uint8_t *buf, int buf_size)
Definition: alffmpeg.c:65
#define calloc
Definition: SDL_malloc.c:636
FilePtr openAVCustom(const char *name, void *user_data, int(*read_packet)(void *user_data, uint8_t *buf, int buf_size), int(*write_packet)(void *user_data, uint8_t *buf, int buf_size), int64_t(*seek)(void *user_data, int64_t offset, int whence))
Definition: alffmpeg.c:221
size_t readAVAudioData(StreamPtr stream, void *data, size_t length)
Definition: alffmpeg.c:567
#define AL_INT_SOFT
Definition: alext.h:222
StreamPtr getAVAudioStream(FilePtr file, int streamnum)
Definition: alffmpeg.c:318
#define AL_DOUBLE_SOFT
Definition: alext.h:225
#define SEEK_CUR
Definition: zconf.h:250
EGLContext EGLenum EGLClientBuffer buffer
Definition: eglext.h:87
#define AL_5POINT1_SOFT
Definition: alext.h:213
#define AL_SHORT_SOFT
Definition: alext.h:220
#define AL_UNSIGNED_BYTE_SOFT
Definition: alext.h:219
#define AL_FLOAT_SOFT
Definition: alext.h:224
GLsizei GLsizei * length
Definition: gl2ext.h:792
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: gl2ext.h:848
#define AL_7POINT1_SOFT
Definition: alext.h:215
struct MyStream * StreamPtr
Definition: alffmpeg.h:14
#define realloc
Definition: SDL_malloc.c:637
int ALenum
Definition: al.h:65
#define AL_QUAD_SOFT
Definition: alext.h:212
unsigned int ALuint
Definition: al.h:59
void clearAVAudioData(StreamPtr stream)
Definition: alffmpeg.c:256
#define SEEK_SET
Definition: zconf.h:249
#define SEEK_END
Definition: zconf.h:251
unsigned char uint8_t
EGLSurface EGLint void ** value
Definition: eglext.h:301
int getAVFileInfo(FilePtr file, int *numaudiostreams)
Definition: alffmpeg.c:303
GLintptr offset
Definition: glew.h:1668
static int64_t MemData_seek(void *opaque, int64_t offset, int whence)
Definition: alffmpeg.c:93
GLenum GLuint GLsizei const GLchar * buf
Definition: glew.h:2539
uint8_t * getAVAudioData(StreamPtr stream, size_t *length)
Definition: alffmpeg.c:503
#define memcpy
Definition: SDL_malloc.c:634
static size_t NextPowerOf2(size_t value)
Definition: alffmpeg.c:42
#define str(s)
void closeAVFile(FilePtr file)
Definition: alffmpeg.c:271
int i
Definition: pngrutil.c:1377
static int MemData_write(void *opaque, uint8_t *buf, int buf_size)
Definition: alffmpeg.c:79
int getAVAudioInfo(StreamPtr stream, ALuint *rate, ALenum *channels, ALenum *type)
Definition: alffmpeg.c:392
#define AL_MONO_SOFT
Definition: alext.h:209