AVbin  Version 10
Cross-platform audio/video media decoding library with long-term ABI support.
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
avbin.c
Go to the documentation of this file.
1 /* avbin.c
2  * Copyright 2012 AVbin Team
3  *
4  * This file is part of AVbin.
5  *
6  * AVbin is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as
8  * published by the Free Software Foundation; either version 3 of
9  * the License, or (at your option) any later version.
10  *
11  * AVbin is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this program. If not, see
18  * <http://www.gnu.org/licenses/>.
19  */
20 
21 #include <stdarg.h>
22 #include <stdlib.h>
23 #include <string.h>
24 
25 #include <avbin.h>
26 
27 /* libav */
28 #include <libavformat/avformat.h>
29 #include <libavcodec/avcodec.h>
30 #include <libavutil/avutil.h>
31 #include <libavutil/dict.h>
32 #include <libavutil/mathematics.h>
33 #include <libswscale/swscale.h>
34 
35 static int32_t avbin_thread_count = 1;
36 
37 struct _AVbinFile {
38  AVFormatContext *context;
39  AVPacket *packet;
40 };
41 
42 struct _AVbinStream {
43  int32_t type;
44  AVFormatContext *format_context;
45  AVCodecContext *codec_context;
46  AVFrame *frame;
47 };
48 
49 static AVbinLogCallback user_log_callback = NULL;
50 
55 static void avbin_log_callback(void *ptr,
56  int level,
57  const char *fmt,
58  va_list vl)
59 {
60  static char message[8192];
61  const char *module = NULL;
62 
63 // if (level > av_log_level || !user_log_callback)
64  if (level > av_log_get_level() || !user_log_callback)
65  return;
66 
67  if (ptr)
68  {
69  AVClass *avc = *(AVClass**) ptr;
70  module = avc->item_name(ptr);
71  }
72 
73  vsnprintf(message, sizeof message, fmt, vl);
74  user_log_callback(module, (AVbinLogLevel) level, message);
75 }
76 
78 {
79  return AVBIN_VERSION;
80 }
81 
83 {
84  AVbinInfo *info = malloc(sizeof(*info));
85 
86  info->structure_size = sizeof(*info);
87  info->version = avbin_get_version();
88  info->version_string = AVBIN_VERSION_STRING;
89  info->build_date = AVBIN_BUILD_DATE;
90  info->repo = AVBIN_REPO;
91  info->commit = AVBIN_COMMIT;
92  info->backend = AVBIN_BACKEND;
93  info->backend_version_string = AVBIN_BACKEND_VERSION_STRING;
94  info->backend_repo = AVBIN_BACKEND_REPO;
95  info->backend_commit = AVBIN_BACKEND_COMMIT;
96  return info;
97 }
98 
100 {
101  return 0;
102 }
103 
105 {
106  return AVCODEC_MAX_AUDIO_FRAME_SIZE;
107 }
108 
109 int32_t avbin_have_feature(const char *feature)
110 {
111  if (strcmp(feature, "frame_rate") == 0)
112  return 1;
113  if (strcmp(feature, "options") == 0)
114  return 1;
115  if (strcmp(feature, "info") == 0)
116  return 1;
117  return 0;
118 }
119 
121 {
122  return avbin_init_options(NULL);
123 }
124 
126 {
127  if (options_ptr == NULL)
128  {
129  options_ptr = malloc(sizeof(options_ptr));
130  if (options_ptr == NULL)
131  return AVBIN_RESULT_ERROR;
132 
133  // Set defaults...
134  options_ptr->structure_size = sizeof(AVbinOptions);
135  options_ptr->thread_count = 1;
136  }
137 
138  // What version did we get?
139  AVbinOptions * options = NULL;
140  if (options_ptr->structure_size == sizeof(AVbinOptions))
141  {
142  options = options_ptr;
143  } else {
144  return AVBIN_RESULT_ERROR;
145  }
146 
147  // Stupid choices deserve single-threading
148  if (options->thread_count < 0)
149  options->thread_count = 1;
150 
151  avbin_thread_count = options->thread_count;
152 
153  av_register_all();
154  avcodec_register_all();
155 
156  return AVBIN_RESULT_OK;
157 }
158 
160 {
161  av_log_set_level(level);
162  return AVBIN_RESULT_OK;
163 }
164 
166 {
167  user_log_callback = callback;
168 
169  /* Note av_log_set_callback looks set to disappear at
170  * LIBAVUTIL_VERSION >= 50; at which point av_vlog must be
171  * set directly.
172  */
173  if (callback)
174  av_log_set_callback(avbin_log_callback);
175  else
176  av_log_set_callback(av_log_default_callback);
177  return AVBIN_RESULT_OK;
178 }
179 
180 AVbinFile *avbin_open_filename(const char *filename) { return avbin_open_filename_with_format(filename, NULL); }
181 
182 AVbinFile *avbin_open_filename_with_format(const char *filename, char* format)
183 {
184  AVbinFile *file = malloc(sizeof *file);
185  AVInputFormat *avformat = NULL;
186  if (format) avformat = av_find_input_format(format);
187 
188  file->context = NULL; // Zero-initialize
189  if (avformat_open_input(&file->context, filename, avformat, NULL) != 0)
190  goto error;
191 
192  if (avformat_find_stream_info(file->context, NULL) < 0)
193  goto error;
194 
195  file->packet = NULL;
196  return file;
197 
198 error:
199  free(file);
200  return NULL;
201 }
202 
204 {
205  if (file->packet)
206  {
207  av_free_packet(file->packet);
208  free(file->packet);
209  }
210 
211  avformat_close_input(&file->context);
212  free(file);
213 }
214 
216 {
217  int i;
218  AVCodecContext *codec_context;
219 
220  if (!timestamp)
221  av_seek_frame(file->context, -1, 0,
222  AVSEEK_FLAG_ANY | AVSEEK_FLAG_BYTE);
223  else
224  av_seek_frame(file->context, -1, timestamp, 0);
225 
226  for (i = 0; i < file->context->nb_streams; i++)
227  {
228  codec_context = file->context->streams[i]->codec;
229  if (codec_context)
230  avcodec_flush_buffers(codec_context);
231  }
232  return AVBIN_RESULT_OK;
233 }
234 
236 {
237  if (info->structure_size < sizeof *info)
238  return AVBIN_RESULT_ERROR;
239 
240  info->n_streams = file->context->nb_streams;
241  info->start_time = file->context->start_time;
242  info->duration = file->context->duration;
243 
244  // Zero-initialize fields first
245  memset(info->title, 0, sizeof(info->title));
246  memset(info->author, 0, sizeof(info->author));
247  memset(info->copyright, 0, sizeof(info->copyright));
248  memset(info->comment, 0, sizeof(info->comment));
249  memset(info->album, 0, sizeof(info->album));
250  memset(info->genre, 0, sizeof(info->genre));
251  info->year = 0;
252  info->track = 0;
253 
254  AVDictionaryEntry* entry;
255  if ((entry = av_dict_get(file->context->metadata, "title", NULL, 0)) != NULL) {
256  strncpy(info->title, entry->value, sizeof(info->title));
257  }
258 
259  if (((entry = av_dict_get(file->context->metadata, "artist", NULL, 0)) != NULL) ||
260  (entry = av_dict_get(file->context->metadata, "album_artist", NULL, 0)) != NULL) {
261  strncpy(info->author, entry->value, sizeof(info->author));
262  }
263  if ((entry = av_dict_get(file->context->metadata, "copyright", NULL, 0)) != NULL) {
264  strncpy(info->copyright, entry->value, sizeof(info->copyright));
265  }
266  if ((entry = av_dict_get(file->context->metadata, "comment", NULL, 0)) != NULL) {
267  strncpy(info->comment, entry->value, sizeof(info->comment));
268  }
269  if ((entry = av_dict_get(file->context->metadata, "album", NULL, 0)) != NULL) {
270  strncpy(info->album, entry->value, sizeof(info->album));
271  }
272  if ((entry = av_dict_get(file->context->metadata, "date", NULL, 0)) != NULL) {
273  info->year = atoi(entry->value);
274  }
275  if ((entry = av_dict_get(file->context->metadata, "track", NULL, 0)) != NULL) {
276  info->track = atoi(entry->value);
277  }
278  if ((entry = av_dict_get(file->context->metadata, "genre", NULL, 0)) != NULL) {
279  strncpy(info->genre, entry->value, sizeof(info->genre));
280  }
281 
282  return AVBIN_RESULT_OK;
283 }
284 
285 int32_t avbin_stream_info(AVbinFile *file, int32_t stream_index,
286  AVbinStreamInfo *info)
287 {
288  AVCodecContext *context = file->context->streams[stream_index]->codec;
289  AVbinStreamInfo8 *info_8 = NULL;
290 
291  /* Error if not large enough for version 1 */
292  if (info->structure_size < sizeof *info)
293  return AVBIN_RESULT_ERROR;
294 
295  /* Version 8 adds frame_rate feature */
296  if (info->structure_size >= sizeof(AVbinStreamInfo8))
297  info_8 = (AVbinStreamInfo8 *) info;
298 
299  switch (context->codec_type)
300  {
301  case AVMEDIA_TYPE_VIDEO:
303  info->video.width = context->width;
304  info->video.height = context->height;
305  info->video.sample_aspect_num = context->sample_aspect_ratio.num;
306  info->video.sample_aspect_den = context->sample_aspect_ratio.den;
307  if (info_8)
308  {
309  AVRational frame_rate = \
310  file->context->streams[stream_index]->r_frame_rate;
311  info_8->video.frame_rate_num = frame_rate.num;
312  info_8->video.frame_rate_den = frame_rate.den;
313 
314  /* Work around bug in Libav: if frame rate over 1000, divide
315  * by 1000.
316  */
317  if (info_8->video.frame_rate_num /
318  info_8->video.frame_rate_den > 1000)
319  info_8->video.frame_rate_den *= 1000;
320  }
321  break;
322  case AVMEDIA_TYPE_AUDIO:
324  info->audio.sample_rate = context->sample_rate;
325  info->audio.channels = context->channels;
326  switch (context->sample_fmt)
327  {
328  case AV_SAMPLE_FMT_U8:
329  info->audio.sample_rate = AVBIN_SAMPLE_FORMAT_U8;
330  info->audio.sample_bits = 8;
331  break;
332  case AV_SAMPLE_FMT_S16:
333  info->audio.sample_format = AVBIN_SAMPLE_FORMAT_S16;
334  info->audio.sample_bits = 16;
335  break;
336  case AV_SAMPLE_FMT_S32:
337  info->audio.sample_format = AVBIN_SAMPLE_FORMAT_S32;
338  info->audio.sample_bits = 32;
339  break;
340  case AV_SAMPLE_FMT_FLT:
341  info->audio.sample_format = AVBIN_SAMPLE_FORMAT_FLOAT;
342  info->audio.sample_bits = 32;
343  break;
344  default:
345  // Unknown sample format
346  info->audio.sample_format = -1;
347  info->audio.sample_bits = -1;
348  break;
349 
350  // TODO: support planar formats
351  }
352  break;
353 
354  default:
356  break;
357  }
358 
359  return AVBIN_RESULT_OK;
360 }
361 
363 {
364  AVCodecContext *codec_context;
365  AVCodec *codec;
366 
367  if (index < 0 || index >= file->context->nb_streams)
368  return NULL;
369 
370  codec_context = file->context->streams[index]->codec;
371  codec = avcodec_find_decoder(codec_context->codec_id);
372  if (!codec)
373  return NULL;
374 
375  if (avbin_thread_count != 1)
376  codec_context->thread_count = avbin_thread_count;
377 
378  if (avcodec_open2(codec_context, codec, NULL) < 0)
379  return NULL;
380 
381  AVbinStream *stream = malloc(sizeof *stream);
382  stream->format_context = file->context;
383  stream->codec_context = codec_context;
384  stream->type = codec_context->codec_type;
385  if (stream->type == AVMEDIA_TYPE_VIDEO)
386  stream->frame = avcodec_alloc_frame();
387  else
388  stream->frame = NULL;
389  return stream;
390 }
391 
393 {
394  if (stream->frame)
395  av_free(stream->frame);
396  avcodec_close(stream->codec_context);
397  free(stream);
398 }
399 
400 int32_t avbin_read(AVbinFile *file, AVbinPacket *packet)
401 {
402  if (packet->structure_size < sizeof *packet)
403  return AVBIN_RESULT_ERROR;
404 
405  if (file->packet)
406  av_free_packet(file->packet);
407  else
408  file->packet = malloc(sizeof *file->packet);
409 
410  if (av_read_frame(file->context, file->packet) < 0)
411  return AVBIN_RESULT_ERROR;
412 
413  packet->timestamp = av_rescale_q(file->packet->dts,
414  file->context->streams[file->packet->stream_index]->time_base,
415  AV_TIME_BASE_Q);
416  packet->stream_index = file->packet->stream_index;
417  packet->data = file->packet->data;
418  packet->size = file->packet->size;
419 
420  return AVBIN_RESULT_OK;
421 }
422 
424  uint8_t *data_in, size_t size_in,
425  uint8_t *data_out, int *size_out)
426 {
427  int used;
428  if (stream->type != AVMEDIA_TYPE_AUDIO)
429  return AVBIN_RESULT_ERROR;
430 
431  AVPacket packet;
432  av_init_packet(&packet);
433  packet.data = data_in;
434  packet.size = size_in;
435 
436  AVFrame frame;
437  int got_frame = 0;
438  used = avcodec_decode_audio4(stream->codec_context, &frame, &got_frame, &packet);
439 
440  if (used < 0)
441  return AVBIN_RESULT_ERROR;
442 
443  // TODO: support planar formats
444  if (used >= 0 && got_frame) {
445  int plane_size;
446  int data_size = av_samples_get_buffer_size(&plane_size,
447  stream->codec_context->channels,
448  frame.nb_samples,
449  stream->codec_context->sample_fmt, 1);
450  if (*size_out < data_size) {
451  av_log(stream->codec_context, AV_LOG_ERROR, "Output audio buffer is too small for current audio frame!");
452  return AVBIN_RESULT_ERROR;
453  }
454 
455  memcpy(data_out, frame.extended_data[0], data_size);
456  *size_out = data_size;
457  } else {
458  *size_out = 0;
459  }
460 
461  return used;
462 }
463 
465  uint8_t *data_in, size_t size_in,
466  uint8_t *data_out)
467 {
468  AVPicture picture_rgb;
469  int got_picture;
470  int width = stream->codec_context->width;
471  int height = stream->codec_context->height;
472  int used;
473 
474  if (stream->type != AVMEDIA_TYPE_VIDEO)
475  return AVBIN_RESULT_ERROR;
476 
477  AVPacket packet;
478  av_init_packet(&packet);
479  packet.data = data_in;
480  packet.size = size_in;
481 
482  used = avcodec_decode_video2(stream->codec_context,
483  stream->frame, &got_picture,
484  &packet);
485 
486  if (!got_picture)
487  return AVBIN_RESULT_ERROR;
488 
489 
490  avpicture_fill(&picture_rgb, data_out, PIX_FMT_RGB24, width, height);
491  static struct SwsContext *img_convert_ctx = NULL;
492  img_convert_ctx = sws_getCachedContext(img_convert_ctx,width, height,stream->codec_context->pix_fmt,width, height,PIX_FMT_RGB24, SWS_FAST_BILINEAR, NULL, NULL, NULL);
493  sws_scale(img_convert_ctx, (const uint8_t* const*)stream->frame->data, stream->frame->linesize,0, height, picture_rgb.data, picture_rgb.linesize);
494 
495  return used;
496 }
497