| /* |
| * Format register and lookup |
| * Copyright (c) 2000, 2001, 2002 Fabrice Bellard |
| * |
| * This file is part of FFmpeg. |
| * |
| * FFmpeg is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * FFmpeg is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with FFmpeg; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #include "libavutil/avstring.h" |
| #include "libavutil/bprint.h" |
| #include "libavutil/opt.h" |
| #include "libavutil/thread.h" |
| |
| #include "avio_internal.h" |
| #include "avformat.h" |
| #include "id3v2.h" |
| #include "internal.h" |
| |
| |
| /** |
| * @file |
| * Format register and lookup |
| */ |
| |
| int av_match_ext(const char *filename, const char *extensions) |
| { |
| const char *ext; |
| |
| if (!filename) |
| return 0; |
| |
| ext = strrchr(filename, '.'); |
| if (ext) |
| return av_match_name(ext + 1, extensions); |
| return 0; |
| } |
| |
| ff_const59 AVOutputFormat *av_guess_format(const char *short_name, const char *filename, |
| const char *mime_type) |
| { |
| const AVOutputFormat *fmt = NULL; |
| AVOutputFormat *fmt_found = NULL; |
| void *i = 0; |
| int score_max, score; |
| |
| /* specific test for image sequences */ |
| #if CONFIG_IMAGE2_MUXER |
| if (!short_name && filename && |
| av_filename_number_test(filename) && |
| ff_guess_image2_codec(filename) != AV_CODEC_ID_NONE) { |
| return av_guess_format("image2", NULL, NULL); |
| } |
| #endif |
| /* Find the proper file type. */ |
| score_max = 0; |
| while ((fmt = av_muxer_iterate(&i))) { |
| score = 0; |
| if (fmt->name && short_name && av_match_name(short_name, fmt->name)) |
| score += 100; |
| if (fmt->mime_type && mime_type && !strcmp(fmt->mime_type, mime_type)) |
| score += 10; |
| if (filename && fmt->extensions && |
| av_match_ext(filename, fmt->extensions)) { |
| score += 5; |
| } |
| if (score > score_max) { |
| score_max = score; |
| fmt_found = (AVOutputFormat*)fmt; |
| } |
| } |
| return fmt_found; |
| } |
| |
| enum AVCodecID av_guess_codec(ff_const59 AVOutputFormat *fmt, const char *short_name, |
| const char *filename, const char *mime_type, |
| enum AVMediaType type) |
| { |
| if (av_match_name("segment", fmt->name) || av_match_name("ssegment", fmt->name)) { |
| ff_const59 AVOutputFormat *fmt2 = av_guess_format(NULL, filename, NULL); |
| if (fmt2) |
| fmt = fmt2; |
| } |
| |
| if (type == AVMEDIA_TYPE_VIDEO) { |
| enum AVCodecID codec_id = AV_CODEC_ID_NONE; |
| |
| #if CONFIG_IMAGE2_MUXER |
| if (!strcmp(fmt->name, "image2") || !strcmp(fmt->name, "image2pipe")) { |
| codec_id = ff_guess_image2_codec(filename); |
| } |
| #endif |
| if (codec_id == AV_CODEC_ID_NONE) |
| codec_id = fmt->video_codec; |
| return codec_id; |
| } else if (type == AVMEDIA_TYPE_AUDIO) |
| return fmt->audio_codec; |
| else if (type == AVMEDIA_TYPE_SUBTITLE) |
| return fmt->subtitle_codec; |
| else if (type == AVMEDIA_TYPE_DATA) |
| return fmt->data_codec; |
| else |
| return AV_CODEC_ID_NONE; |
| } |
| |
| ff_const59 AVInputFormat *av_find_input_format(const char *short_name) |
| { |
| const AVInputFormat *fmt = NULL; |
| void *i = 0; |
| while ((fmt = av_demuxer_iterate(&i))) |
| if (av_match_name(short_name, fmt->name)) |
| return (AVInputFormat*)fmt; |
| return NULL; |
| } |
| |
| ff_const59 AVInputFormat *av_probe_input_format3(ff_const59 AVProbeData *pd, int is_opened, |
| int *score_ret) |
| { |
| AVProbeData lpd = *pd; |
| const AVInputFormat *fmt1 = NULL; |
| ff_const59 AVInputFormat *fmt = NULL; |
| int score, score_max = 0; |
| void *i = 0; |
| const static uint8_t zerobuffer[AVPROBE_PADDING_SIZE]; |
| enum nodat { |
| NO_ID3, |
| ID3_ALMOST_GREATER_PROBE, |
| ID3_GREATER_PROBE, |
| ID3_GREATER_MAX_PROBE, |
| } nodat = NO_ID3; |
| |
| if (!lpd.buf) |
| lpd.buf = (unsigned char *) zerobuffer; |
| |
| if (lpd.buf_size > 10 && ff_id3v2_match(lpd.buf, ID3v2_DEFAULT_MAGIC)) { |
| int id3len = ff_id3v2_tag_len(lpd.buf); |
| if (lpd.buf_size > id3len + 16) { |
| if (lpd.buf_size < 2LL*id3len + 16) |
| nodat = ID3_ALMOST_GREATER_PROBE; |
| lpd.buf += id3len; |
| lpd.buf_size -= id3len; |
| } else if (id3len >= PROBE_BUF_MAX) { |
| nodat = ID3_GREATER_MAX_PROBE; |
| } else |
| nodat = ID3_GREATER_PROBE; |
| } |
| |
| while ((fmt1 = av_demuxer_iterate(&i))) { |
| if (!is_opened == !(fmt1->flags & AVFMT_NOFILE) && strcmp(fmt1->name, "image2")) |
| continue; |
| score = 0; |
| if (fmt1->read_probe) { |
| score = fmt1->read_probe(&lpd); |
| if (score) |
| av_log(NULL, AV_LOG_TRACE, "Probing %s score:%d size:%d\n", fmt1->name, score, lpd.buf_size); |
| if (fmt1->extensions && av_match_ext(lpd.filename, fmt1->extensions)) { |
| switch (nodat) { |
| case NO_ID3: |
| score = FFMAX(score, 1); |
| break; |
| case ID3_GREATER_PROBE: |
| case ID3_ALMOST_GREATER_PROBE: |
| score = FFMAX(score, AVPROBE_SCORE_EXTENSION / 2 - 1); |
| break; |
| case ID3_GREATER_MAX_PROBE: |
| score = FFMAX(score, AVPROBE_SCORE_EXTENSION); |
| break; |
| } |
| } |
| } else if (fmt1->extensions) { |
| if (av_match_ext(lpd.filename, fmt1->extensions)) |
| score = AVPROBE_SCORE_EXTENSION; |
| } |
| if (av_match_name(lpd.mime_type, fmt1->mime_type)) { |
| if (AVPROBE_SCORE_MIME > score) { |
| av_log(NULL, AV_LOG_DEBUG, "Probing %s score:%d increased to %d due to MIME type\n", fmt1->name, score, AVPROBE_SCORE_MIME); |
| score = AVPROBE_SCORE_MIME; |
| } |
| } |
| if (score > score_max) { |
| score_max = score; |
| fmt = (AVInputFormat*)fmt1; |
| } else if (score == score_max) |
| fmt = NULL; |
| } |
| if (nodat == ID3_GREATER_PROBE) |
| score_max = FFMIN(AVPROBE_SCORE_EXTENSION / 2 - 1, score_max); |
| *score_ret = score_max; |
| |
| return fmt; |
| } |
| |
| ff_const59 AVInputFormat *av_probe_input_format2(ff_const59 AVProbeData *pd, int is_opened, int *score_max) |
| { |
| int score_ret; |
| ff_const59 AVInputFormat *fmt = av_probe_input_format3(pd, is_opened, &score_ret); |
| if (score_ret > *score_max) { |
| *score_max = score_ret; |
| return fmt; |
| } else |
| return NULL; |
| } |
| |
| ff_const59 AVInputFormat *av_probe_input_format(ff_const59 AVProbeData *pd, int is_opened) |
| { |
| int score = 0; |
| return av_probe_input_format2(pd, is_opened, &score); |
| } |
| |
| int av_probe_input_buffer2(AVIOContext *pb, ff_const59 AVInputFormat **fmt, |
| const char *filename, void *logctx, |
| unsigned int offset, unsigned int max_probe_size) |
| { |
| AVProbeData pd = { filename ? filename : "" }; |
| uint8_t *buf = NULL; |
| int ret = 0, probe_size, buf_offset = 0; |
| int score = 0; |
| int ret2; |
| |
| if (!max_probe_size) |
| max_probe_size = PROBE_BUF_MAX; |
| else if (max_probe_size < PROBE_BUF_MIN) { |
| av_log(logctx, AV_LOG_ERROR, |
| "Specified probe size value %u cannot be < %u\n", max_probe_size, PROBE_BUF_MIN); |
| return AVERROR(EINVAL); |
| } |
| |
| if (offset >= max_probe_size) |
| return AVERROR(EINVAL); |
| |
| if (pb->av_class) { |
| uint8_t *mime_type_opt = NULL; |
| char *semi; |
| av_opt_get(pb, "mime_type", AV_OPT_SEARCH_CHILDREN, &mime_type_opt); |
| pd.mime_type = (const char *)mime_type_opt; |
| semi = pd.mime_type ? strchr(pd.mime_type, ';') : NULL; |
| if (semi) { |
| *semi = '\0'; |
| } |
| } |
| |
| for (probe_size = PROBE_BUF_MIN; probe_size <= max_probe_size && !*fmt; |
| probe_size = FFMIN(probe_size << 1, |
| FFMAX(max_probe_size, probe_size + 1))) { |
| score = probe_size < max_probe_size ? AVPROBE_SCORE_RETRY : 0; |
| |
| /* Read probe data. */ |
| if ((ret = av_reallocp(&buf, probe_size + AVPROBE_PADDING_SIZE)) < 0) |
| goto fail; |
| if ((ret = avio_read(pb, buf + buf_offset, |
| probe_size - buf_offset)) < 0) { |
| /* Fail if error was not end of file, otherwise, lower score. */ |
| if (ret != AVERROR_EOF) |
| goto fail; |
| |
| score = 0; |
| ret = 0; /* error was end of file, nothing read */ |
| } |
| buf_offset += ret; |
| if (buf_offset < offset) |
| continue; |
| pd.buf_size = buf_offset - offset; |
| pd.buf = &buf[offset]; |
| |
| memset(pd.buf + pd.buf_size, 0, AVPROBE_PADDING_SIZE); |
| |
| /* Guess file format. */ |
| *fmt = av_probe_input_format2(&pd, 1, &score); |
| if (*fmt) { |
| /* This can only be true in the last iteration. */ |
| if (score <= AVPROBE_SCORE_RETRY) { |
| av_log(logctx, AV_LOG_WARNING, |
| "Format %s detected only with low score of %d, " |
| "misdetection possible!\n", (*fmt)->name, score); |
| } else |
| av_log(logctx, AV_LOG_DEBUG, |
| "Format %s probed with size=%d and score=%d\n", |
| (*fmt)->name, probe_size, score); |
| #if 0 |
| FILE *f = fopen("probestat.tmp", "ab"); |
| fprintf(f, "probe_size:%d format:%s score:%d filename:%s\n", probe_size, (*fmt)->name, score, filename); |
| fclose(f); |
| #endif |
| } |
| } |
| |
| if (!*fmt) |
| ret = AVERROR_INVALIDDATA; |
| |
| fail: |
| /* Rewind. Reuse probe buffer to avoid seeking. */ |
| ret2 = ffio_rewind_with_probe_data(pb, &buf, buf_offset); |
| if (ret >= 0) |
| ret = ret2; |
| |
| av_freep(&pd.mime_type); |
| return ret < 0 ? ret : score; |
| } |
| |
| int av_probe_input_buffer(AVIOContext *pb, ff_const59 AVInputFormat **fmt, |
| const char *filename, void *logctx, |
| unsigned int offset, unsigned int max_probe_size) |
| { |
| int ret = av_probe_input_buffer2(pb, fmt, filename, logctx, offset, max_probe_size); |
| return ret < 0 ? ret : 0; |
| } |