AVbin
Version 10
Cross-platform audio/video media decoding library with long-term ABI support.
|
00001 /* avbin.c 00002 * Copyright 2012 AVbin Team 00003 * 00004 * This file is part of AVbin. 00005 * 00006 * AVbin is free software; you can redistribute it and/or modify it 00007 * under the terms of the GNU Lesser General Public License as 00008 * published by the Free Software Foundation; either version 3 of 00009 * the License, or (at your option) any later version. 00010 * 00011 * AVbin is distributed in the hope that it will be useful, but 00012 * WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 * Lesser Lesser General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU Lesser General Public 00017 * License along with this program. If not, see 00018 * <http://www.gnu.org/licenses/>. 00019 */ 00020 00021 #include <stdarg.h> 00022 #include <stdlib.h> 00023 #include <string.h> 00024 00025 #include <avbin.h> 00026 00027 /* libav */ 00028 #include <libavformat/avformat.h> 00029 #include <libavcodec/avcodec.h> 00030 #include <libavutil/avutil.h> 00031 #include <libavutil/dict.h> 00032 #include <libavutil/mathematics.h> 00033 #include <libswscale/swscale.h> 00034 00035 static int32_t avbin_thread_count = 1; 00036 00037 struct _AVbinFile { 00038 AVFormatContext *context; 00039 AVPacket *packet; 00040 }; 00041 00042 struct _AVbinStream { 00043 int32_t type; 00044 AVFormatContext *format_context; 00045 AVCodecContext *codec_context; 00046 AVFrame *frame; 00047 }; 00048 00049 static AVbinLogCallback user_log_callback = NULL; 00050 00055 static void avbin_log_callback(void *ptr, 00056 int level, 00057 const char *fmt, 00058 va_list vl) 00059 { 00060 static char message[8192]; 00061 const char *module = NULL; 00062 00063 // if (level > av_log_level || !user_log_callback) 00064 if (level > av_log_get_level() || !user_log_callback) 00065 return; 00066 00067 if (ptr) 00068 { 00069 AVClass *avc = *(AVClass**) ptr; 00070 module = avc->item_name(ptr); 00071 } 00072 00073 vsnprintf(message, sizeof message, fmt, vl); 00074 user_log_callback(module, (AVbinLogLevel) level, message); 00075 } 00076 00077 int32_t avbin_get_version() 00078 { 00079 return AVBIN_VERSION; 00080 } 00081 00082 AVbinInfo *avbin_get_info() 00083 { 00084 AVbinInfo *info = malloc(sizeof(*info)); 00085 00086 info->structure_size = sizeof(*info); 00087 info->version = avbin_get_version(); 00088 info->version_string = AVBIN_VERSION_STRING; 00089 info->build_date = AVBIN_BUILD_DATE; 00090 info->repo = AVBIN_REPO; 00091 info->commit = AVBIN_COMMIT; 00092 info->backend = AVBIN_BACKEND; 00093 info->backend_version_string = AVBIN_BACKEND_VERSION_STRING; 00094 info->backend_repo = AVBIN_BACKEND_REPO; 00095 info->backend_commit = AVBIN_BACKEND_COMMIT; 00096 return info; 00097 } 00098 00099 // Deprecated - use avbin_get_info() instead. Will be removed in version 12. 00100 int32_t avbin_get_ffmpeg_revision() 00101 { 00102 return 0; 00103 } 00104 00105 // Deprecated - useless. Will be removed in version 13. 00106 size_t avbin_get_audio_buffer_size() 00107 { 00108 return 192000; 00109 } 00110 00111 int32_t avbin_have_feature(const char *feature) 00112 { 00113 if (strcmp(feature, "frame_rate") == 0) 00114 { 00115 // See note on avbin_have_feature() in avbin.h 00116 return 0; 00117 } 00118 if (strcmp(feature, "options") == 0) 00119 return 1; 00120 if (strcmp(feature, "info") == 0) 00121 return 1; 00122 return 0; 00123 } 00124 00125 AVbinResult avbin_init() 00126 { 00127 return avbin_init_options(NULL); 00128 } 00129 00130 AVbinResult avbin_init_options(AVbinOptions * options_ptr) 00131 { 00132 if (options_ptr == NULL) 00133 { 00134 options_ptr = malloc(sizeof(options_ptr)); 00135 if (options_ptr == NULL) 00136 return AVBIN_RESULT_ERROR; 00137 00138 // Set defaults... 00139 options_ptr->structure_size = sizeof(AVbinOptions); 00140 options_ptr->thread_count = 1; 00141 } 00142 00143 // What version did we get? 00144 AVbinOptions * options = NULL; 00145 if (options_ptr->structure_size == sizeof(AVbinOptions)) 00146 { 00147 options = options_ptr; 00148 } else { 00149 return AVBIN_RESULT_ERROR; 00150 } 00151 00152 // Stupid choices deserve single-threading 00153 if (options->thread_count < 0) 00154 options->thread_count = 1; 00155 00156 avbin_thread_count = options->thread_count; 00157 00158 av_register_all(); 00159 avcodec_register_all(); 00160 00161 return AVBIN_RESULT_OK; 00162 } 00163 00164 AVbinResult avbin_set_log_level(AVbinLogLevel level) 00165 { 00166 av_log_set_level(level); 00167 return AVBIN_RESULT_OK; 00168 } 00169 00170 AVbinResult avbin_set_log_callback(AVbinLogCallback callback) 00171 { 00172 user_log_callback = callback; 00173 00174 /* Note av_log_set_callback looks set to disappear at 00175 * LIBAVUTIL_VERSION >= 50; at which point av_vlog must be 00176 * set directly. 00177 */ 00178 if (callback) 00179 av_log_set_callback(avbin_log_callback); 00180 else 00181 av_log_set_callback(av_log_default_callback); 00182 return AVBIN_RESULT_OK; 00183 } 00184 00185 AVbinFile *avbin_open_filename(const char *filename) { return avbin_open_filename_with_format(filename, NULL); } 00186 00187 AVbinFile *avbin_open_filename_with_format(const char *filename, char* format) 00188 { 00189 AVbinFile *file = malloc(sizeof *file); 00190 AVInputFormat *avformat = NULL; 00191 if (format) avformat = av_find_input_format(format); 00192 00193 file->context = NULL; // Zero-initialize 00194 if (avformat_open_input(&file->context, filename, avformat, NULL) != 0) 00195 goto error; 00196 00197 if (avformat_find_stream_info(file->context, NULL) < 0) 00198 goto error; 00199 00200 file->packet = NULL; 00201 return file; 00202 00203 error: 00204 free(file); 00205 return NULL; 00206 } 00207 00208 void avbin_close_file(AVbinFile *file) 00209 { 00210 if (file->packet) 00211 { 00212 av_free_packet(file->packet); 00213 free(file->packet); 00214 } 00215 00216 avformat_close_input(&file->context); 00217 free(file); 00218 } 00219 00220 AVbinResult avbin_seek_file(AVbinFile *file, AVbinTimestamp timestamp) 00221 { 00222 int i; 00223 AVCodecContext *codec_context; 00224 int flags = 0; 00225 00226 if (!timestamp) 00227 { 00228 flags = AVSEEK_FLAG_ANY | AVSEEK_FLAG_BYTE; 00229 if (av_seek_frame(file->context, -1, 0, flags) < 0) 00230 return AVBIN_RESULT_ERROR; 00231 } 00232 else 00233 { 00234 flags = AVSEEK_FLAG_BACKWARD; 00235 if (av_seek_frame(file->context, -1, timestamp, flags) < 0) 00236 return AVBIN_RESULT_ERROR; 00237 } 00238 00239 for (i = 0; i < file->context->nb_streams; i++) 00240 { 00241 codec_context = file->context->streams[i]->codec; 00242 if (codec_context && codec_context->codec) 00243 avcodec_flush_buffers(codec_context); 00244 } 00245 return AVBIN_RESULT_OK; 00246 } 00247 00248 AVbinResult avbin_file_info(AVbinFile *file, AVbinFileInfo *info) 00249 { 00250 if (info->structure_size < sizeof *info) 00251 return AVBIN_RESULT_ERROR; 00252 00253 info->n_streams = file->context->nb_streams; 00254 info->start_time = file->context->start_time; 00255 info->duration = file->context->duration; 00256 00257 // Zero-initialize fields first 00258 memset(info->title, 0, sizeof(info->title)); 00259 memset(info->author, 0, sizeof(info->author)); 00260 memset(info->copyright, 0, sizeof(info->copyright)); 00261 memset(info->comment, 0, sizeof(info->comment)); 00262 memset(info->album, 0, sizeof(info->album)); 00263 memset(info->genre, 0, sizeof(info->genre)); 00264 info->year = 0; 00265 info->track = 0; 00266 00267 AVDictionaryEntry* entry; 00268 if ((entry = av_dict_get(file->context->metadata, "title", NULL, 0)) != NULL) { 00269 strncpy(info->title, entry->value, sizeof(info->title)); 00270 } 00271 00272 if (((entry = av_dict_get(file->context->metadata, "artist", NULL, 0)) != NULL) || 00273 (entry = av_dict_get(file->context->metadata, "album_artist", NULL, 0)) != NULL) { 00274 strncpy(info->author, entry->value, sizeof(info->author)); 00275 } 00276 if ((entry = av_dict_get(file->context->metadata, "copyright", NULL, 0)) != NULL) { 00277 strncpy(info->copyright, entry->value, sizeof(info->copyright)); 00278 } 00279 if ((entry = av_dict_get(file->context->metadata, "comment", NULL, 0)) != NULL) { 00280 strncpy(info->comment, entry->value, sizeof(info->comment)); 00281 } 00282 if ((entry = av_dict_get(file->context->metadata, "album", NULL, 0)) != NULL) { 00283 strncpy(info->album, entry->value, sizeof(info->album)); 00284 } 00285 if ((entry = av_dict_get(file->context->metadata, "date", NULL, 0)) != NULL) { 00286 info->year = atoi(entry->value); 00287 } 00288 if ((entry = av_dict_get(file->context->metadata, "track", NULL, 0)) != NULL) { 00289 info->track = atoi(entry->value); 00290 } 00291 if ((entry = av_dict_get(file->context->metadata, "genre", NULL, 0)) != NULL) { 00292 strncpy(info->genre, entry->value, sizeof(info->genre)); 00293 } 00294 00295 return AVBIN_RESULT_OK; 00296 } 00297 00298 AVbinResult avbin_stream_info(AVbinFile *file, int32_t stream_index, 00299 AVbinStreamInfo *info) 00300 { 00301 AVCodecContext *context = file->context->streams[stream_index]->codec; 00302 AVbinStreamInfo8 *info_8 = NULL; 00303 00304 /* Error if not large enough for version 1 */ 00305 if (info->structure_size < sizeof *info) 00306 return AVBIN_RESULT_ERROR; 00307 00308 /* Version 8 adds frame_rate feature, Version 11 removes it, see note on 00309 avbin_have_feature() in avbin.h */ 00310 if (info->structure_size >= sizeof(AVbinStreamInfo8)) 00311 info_8 = (AVbinStreamInfo8 *) info; 00312 00313 switch (context->codec_type) 00314 { 00315 case AVMEDIA_TYPE_VIDEO: 00316 info->type = AVBIN_STREAM_TYPE_VIDEO; 00317 info->video.width = context->width; 00318 info->video.height = context->height; 00319 info->video.sample_aspect_num = context->sample_aspect_ratio.num; 00320 info->video.sample_aspect_den = context->sample_aspect_ratio.den; 00321 00322 /* See note on avbin_have_feature() in avbin.h 00323 if (info_8) 00324 { 00325 AVRational frame_rate = \ 00326 file->context->streams[stream_index]->r_frame_rate; 00327 info_8->video.frame_rate_num = frame_rate.num; 00328 info_8->video.frame_rate_den = frame_rate.den; 00329 00330 // Work around bug in Libav: if frame rate over 1000, divide 00331 // by 1000. 00332 if (info_8->video.frame_rate_num / 00333 info_8->video.frame_rate_den > 1000) 00334 info_8->video.frame_rate_den *= 1000; 00335 } 00336 */ 00337 if (info_8) 00338 { 00339 info_8->video.frame_rate_num = 0; 00340 info_8->video.frame_rate_den = 0; 00341 } 00342 break; 00343 case AVMEDIA_TYPE_AUDIO: 00344 info->type = AVBIN_STREAM_TYPE_AUDIO; 00345 info->audio.sample_rate = context->sample_rate; 00346 info->audio.channels = context->channels; 00347 switch (context->sample_fmt) 00348 { 00349 case AV_SAMPLE_FMT_U8: 00350 info->audio.sample_format = AVBIN_SAMPLE_FORMAT_U8; 00351 info->audio.sample_bits = 8; 00352 break; 00353 case AV_SAMPLE_FMT_S16: 00354 info->audio.sample_format = AVBIN_SAMPLE_FORMAT_S16; 00355 info->audio.sample_bits = 16; 00356 break; 00357 case AV_SAMPLE_FMT_S32: 00358 info->audio.sample_format = AVBIN_SAMPLE_FORMAT_S32; 00359 info->audio.sample_bits = 32; 00360 break; 00361 case AV_SAMPLE_FMT_FLT: 00362 info->audio.sample_format = AVBIN_SAMPLE_FORMAT_FLOAT; 00363 info->audio.sample_bits = 32; 00364 break; 00365 default: 00366 // Unknown sample format 00367 info->audio.sample_format = -1; 00368 info->audio.sample_bits = -1; 00369 break; 00370 00371 // TODO: support planar formats 00372 } 00373 break; 00374 00375 default: 00376 info->type = AVBIN_STREAM_TYPE_UNKNOWN; 00377 break; 00378 } 00379 00380 return AVBIN_RESULT_OK; 00381 } 00382 00383 AVbinStream *avbin_open_stream(AVbinFile *file, int32_t index) 00384 { 00385 AVCodecContext *codec_context; 00386 AVCodec *codec; 00387 00388 if (index < 0 || index >= file->context->nb_streams) 00389 return NULL; 00390 00391 codec_context = file->context->streams[index]->codec; 00392 codec = avcodec_find_decoder(codec_context->codec_id); 00393 if (!codec) 00394 return NULL; 00395 00396 /* The Libav api example does this (see libav/libavcodec-api-example.c). 00397 * The only explanation is "we do not send complete frames". I tried 00398 * adding it, and there seemed to be no effect either way. I'm going to 00399 * leave it here commented out just in case we find the need to enable it 00400 * in the future. 00401 */ 00402 /* if (codec->capabilities & CODEC_CAP_TRUNCATED) 00403 * codec_context->flags |= CODEC_FLAG_TRUNCATED; 00404 */ 00405 if (avbin_thread_count != 1) 00406 codec_context->thread_count = avbin_thread_count; 00407 00408 if (avcodec_open2(codec_context, codec, NULL) < 0) 00409 return NULL; 00410 00411 AVbinStream *stream = malloc(sizeof *stream); 00412 stream->format_context = file->context; 00413 stream->codec_context = codec_context; 00414 stream->type = codec_context->codec_type; 00415 stream->frame = avcodec_alloc_frame(); 00416 00417 return stream; 00418 } 00419 00420 void avbin_close_stream(AVbinStream *stream) 00421 { 00422 if (stream->frame) 00423 avcodec_free_frame(&stream->frame); 00424 avcodec_close(stream->codec_context); 00425 free(stream); 00426 } 00427 00428 int32_t avbin_read(AVbinFile *file, AVbinPacket *packet) 00429 { 00430 if (packet->structure_size < sizeof *packet) 00431 return AVBIN_RESULT_ERROR; 00432 00433 if (file->packet) 00434 av_free_packet(file->packet); 00435 else 00436 file->packet = malloc(sizeof *file->packet); 00437 00438 if (av_read_frame(file->context, file->packet) < 0) 00439 return AVBIN_RESULT_ERROR; 00440 00441 packet->timestamp = av_rescale_q(file->packet->dts, 00442 file->context->streams[file->packet->stream_index]->time_base, 00443 AV_TIME_BASE_Q); 00444 packet->stream_index = file->packet->stream_index; 00445 packet->data = file->packet->data; 00446 packet->size = file->packet->size; 00447 00448 return AVBIN_RESULT_OK; 00449 } 00450 00451 int32_t avbin_decode_audio(AVbinStream *stream, 00452 uint8_t *data_in, size_t size_in, 00453 uint8_t *data_out, int *size_out) 00454 { 00455 int bytes_used; 00456 if (stream->type != AVMEDIA_TYPE_AUDIO) 00457 return AVBIN_RESULT_ERROR; 00458 00459 // Some decoders read big chunks at a time, so you have to make a bigger buffer 00460 uint8_t inbuf[size_in + FF_INPUT_BUFFER_PADDING_SIZE]; 00461 // Set the padding portion of the buffer to all zeros 00462 memset(inbuf + size_in, 0, FF_INPUT_BUFFER_PADDING_SIZE); 00463 // Copy the data into the padded buffer 00464 memcpy(inbuf, data_in, size_in); 00465 00466 AVPacket packet; 00467 av_init_packet(&packet); 00468 packet.data = inbuf; 00469 packet.size = size_in; 00470 00471 int got_frame = 0; 00472 bytes_used = avcodec_decode_audio4(stream->codec_context, stream->frame, &got_frame, &packet); 00473 00474 if (bytes_used < 0) 00475 return AVBIN_RESULT_ERROR; 00476 00477 // TODO: support planar formats 00478 if (got_frame) { 00479 int plane_size; 00480 int data_size = av_samples_get_buffer_size(&plane_size, 00481 stream->codec_context->channels, 00482 stream->frame->nb_samples, 00483 stream->codec_context->sample_fmt, 1); 00484 if (*size_out < data_size) { 00485 av_log(stream->codec_context, AV_LOG_ERROR, "Output audio buffer is too small for current audio frame!"); 00486 return AVBIN_RESULT_ERROR; 00487 } 00488 00489 memcpy(data_out, stream->frame->extended_data[0], data_size); 00490 *size_out = data_size; 00491 } else { 00492 *size_out = 0; 00493 } 00494 00495 return bytes_used; 00496 } 00497 00498 int32_t avbin_decode_video(AVbinStream *stream, 00499 uint8_t *data_in, size_t size_in, 00500 uint8_t *data_out) 00501 { 00502 AVPicture picture_rgb; 00503 int got_picture; 00504 int width = stream->codec_context->width; 00505 int height = stream->codec_context->height; 00506 int bytes_used; 00507 00508 if (stream->type != AVMEDIA_TYPE_VIDEO) 00509 return AVBIN_RESULT_ERROR; 00510 00511 // Some decoders read big chunks at a time, so you have to make a bigger buffer 00512 uint8_t inbuf[size_in + FF_INPUT_BUFFER_PADDING_SIZE]; 00513 // Set the padding portion of the buffer to all zeros 00514 memset(inbuf + size_in, 0, FF_INPUT_BUFFER_PADDING_SIZE); 00515 // Copy the data into the padded buffer 00516 memcpy(inbuf, data_in, size_in); 00517 00518 AVPacket packet; 00519 av_init_packet(&packet); 00520 packet.data = inbuf; 00521 packet.size = size_in; 00522 00523 bytes_used = avcodec_decode_video2(stream->codec_context, 00524 stream->frame, &got_picture, 00525 &packet); 00526 00527 if (!got_picture) 00528 return AVBIN_RESULT_ERROR; 00529 00530 00531 avpicture_fill(&picture_rgb, data_out, PIX_FMT_RGB24, width, height); 00532 static struct SwsContext *img_convert_ctx = NULL; 00533 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); 00534 sws_scale(img_convert_ctx, (const uint8_t* const*)stream->frame->data, stream->frame->linesize,0, height, picture_rgb.data, picture_rgb.linesize); 00535 00536 return bytes_used; 00537 }