diff options
Diffstat (limited to '')
-rw-r--r-- | demux/codec_tags.c | 280 |
1 files changed, 280 insertions, 0 deletions
diff --git a/demux/codec_tags.c b/demux/codec_tags.c new file mode 100644 index 0000000..55700d9 --- /dev/null +++ b/demux/codec_tags.c @@ -0,0 +1,280 @@ +/* + * This file is part of mpv. + * + * mpv 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. + * + * mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <libavformat/avformat.h> +#include <libavcodec/avcodec.h> +#include <libavutil/common.h> +#include <libavutil/intreadwrite.h> + +#include "codec_tags.h" +#include "stheader.h" +#include "common/av_common.h" + +static const char *lookup_tag(int type, uint32_t tag) +{ + const struct AVCodecTag *av_tags[3] = {0}; + switch (type) { + case STREAM_VIDEO: { + av_tags[0] = avformat_get_riff_video_tags(); + av_tags[1] = avformat_get_mov_video_tags(); + break; + } + case STREAM_AUDIO: { + av_tags[0] = avformat_get_riff_audio_tags(); + av_tags[1] = avformat_get_mov_audio_tags(); + break; + } + } + + int id = av_codec_get_id(av_tags, tag); + return id == AV_CODEC_ID_NONE ? NULL : mp_codec_from_av_codec_id(id); +} + + +/* + * As seen in the following page: + * + * https://web.archive.org/web/20220406060153/ + * http://dream.cs.bath.ac.uk/researchdev/wave-ex/bformat.html + * + * Note that the GUID struct in the above citation has its + * integers encoded in little-endian format, which means that + * the unsigned short and unsigned long entries need to be + * byte-flipped for this encoding. + * + * In theory only the first element of this array should be used, + * however some encoders incorrectly encoded the GUID byte-for-byte + * and thus the second one exists as a fallback. + */ +static const unsigned char guid_ext_base[][16] = { + // MEDIASUBTYPE_BASE_GUID + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71}, + // SUBTYPE_AMBISONIC_B_FORMAT_PCM + {0x01, 0x00, 0x00, 0x00, 0x21, 0x07, 0xD3, 0x11, + 0x86, 0x44, 0xC8, 0xC1, 0xCA, 0x00, 0x00, 0x00} +}; + +struct mp_waveformatex_guid { + const char *codec; + const unsigned char guid[16]; +}; + +static const struct mp_waveformatex_guid guid_ext_other[] = { + {"ac3", + {0x2C, 0x80, 0x6D, 0xE0, 0x46, 0xDB, 0xCF, 0x11, + 0xB4, 0xD1, 0x00, 0x80, 0x5F, 0x6C, 0xBB, 0xEA}}, + {"adpcm_agm", + {0x82, 0xEC, 0x1F, 0x6A, 0xCA, 0xDB, 0x19, 0x45, + 0xBD, 0xE7, 0x56, 0xD3, 0xB3, 0xEF, 0x98, 0x1D}}, + {"atrac3p", + {0xBF, 0xAA, 0x23, 0xE9, 0x58, 0xCB, 0x71, 0x44, + 0xA1, 0x19, 0xFF, 0xFA, 0x01, 0xE4, 0xCE, 0x62}}, + {"atrac9", + {0xD2, 0x42, 0xE1, 0x47, 0xBA, 0x36, 0x8D, 0x4D, + 0x88, 0xFC, 0x61, 0x65, 0x4F, 0x8C, 0x83, 0x6C}}, + {"dfpwm", + {0x3A, 0xC1, 0xFA, 0x38, 0x81, 0x1D, 0x43, 0x61, + 0xA4, 0x0D, 0xCE, 0x53, 0xCA, 0x60, 0x7C, 0xD1}}, + {"eac3", + {0xAF, 0x87, 0xFB, 0xA7, 0x02, 0x2D, 0xFB, 0x42, + 0xA4, 0xD4, 0x05, 0xCD, 0x93, 0x84, 0x3B, 0xDD}}, + {"mp2", + {0x2B, 0x80, 0x6D, 0xE0, 0x46, 0xDB, 0xCF, 0x11, + 0xB4, 0xD1, 0x00, 0x80, 0x5F, 0x6C, 0xBB, 0xEA}} +}; + +static void map_audio_pcm_tag(struct mp_codec_params *c) +{ + // MS PCM, Extended + if (c->codec_tag == 0xfffe && c->extradata_size >= 22) { + // WAVEFORMATEXTENSIBLE.wBitsPerSample + int bits_per_sample = AV_RL16(c->extradata); + if (bits_per_sample) + c->bits_per_coded_sample = bits_per_sample; + + // WAVEFORMATEXTENSIBLE.dwChannelMask + uint64_t chmask = AV_RL32(c->extradata + 2); + struct mp_chmap chmap; + mp_chmap_from_waveext(&chmap, chmask); + if (c->channels.num == chmap.num) + c->channels = chmap; + + // WAVEFORMATEXTENSIBLE.SubFormat + unsigned char *subformat = c->extradata + 6; + for (int i = 0; i < MP_ARRAY_SIZE(guid_ext_base); i++) { + if (memcmp(subformat + 4, guid_ext_base[i] + 4, 12) == 0) { + c->codec_tag = AV_RL32(subformat); + c->codec = lookup_tag(c->type, c->codec_tag); + break; + } + } + + // extra subformat, not a base one + if (c->codec_tag == 0xfffe) { + for (int i = 0; i < MP_ARRAY_SIZE(guid_ext_other); i++) { + if (memcmp(subformat, &guid_ext_other[i].guid, 16) == 0) { + c->codec = guid_ext_other[i].codec; + c->codec_tag = mp_codec_to_av_codec_id(c->codec); + break; + } + } + } + + // Compressed formats might use this. + c->extradata += 22; + c->extradata_size -= 22; + } + + int bits = c->bits_per_coded_sample; + if (!bits) + return; + + int bytes = (bits + 7) / 8; + switch (c->codec_tag) { + case 0x0: // Microsoft PCM + case 0x1: + if (bytes >= 1 && bytes <= 4) + mp_set_pcm_codec(c, bytes > 1, false, bytes * 8, false); + break; + case 0x3: // IEEE float + mp_set_pcm_codec(c, true, true, bits == 64 ? 64 : 32, false); + break; + } +} + +void mp_set_codec_from_tag(struct mp_codec_params *c) +{ + c->codec = lookup_tag(c->type, c->codec_tag); + if (c->type == STREAM_AUDIO) + map_audio_pcm_tag(c); +} + +void mp_set_pcm_codec(struct mp_codec_params *c, bool sign, bool is_float, + int bits, bool is_be) +{ + // This uses libavcodec pcm codec names, e.g. "pcm_u16le". + char codec[64] = "pcm_"; + if (is_float) { + mp_snprintf_cat(codec, sizeof(codec), "f"); + } else { + mp_snprintf_cat(codec, sizeof(codec), sign ? "s" : "u"); + } + mp_snprintf_cat(codec, sizeof(codec), "%d", bits); + if (bits != 8) + mp_snprintf_cat(codec, sizeof(codec), is_be ? "be" : "le"); + c->codec = talloc_strdup(c, codec); +} + +// map file extension/type to an image codec name +static const char *const type_to_codec[][2] = { + { "bmp", "bmp" }, + { "dpx", "dpx" }, + { "j2c", "jpeg2000" }, + { "j2k", "jpeg2000" }, + { "jp2", "jpeg2000" }, + { "jpc", "jpeg2000" }, + { "jpeg", "mjpeg" }, + { "jpg", "mjpeg" }, + { "jps", "mjpeg" }, + { "jls", "ljpeg" }, + { "thm", "mjpeg" }, + { "db", "mjpeg" }, + { "pcd", "photocd" }, + { "pfm", "pfm" }, + { "phm", "phm" }, + { "hdr", "hdr" }, + { "pcx", "pcx" }, + { "png", "png" }, + { "pns", "png" }, + { "ptx", "ptx" }, + { "tga", "targa" }, + { "tif", "tiff" }, + { "tiff", "tiff" }, + { "sgi", "sgi" }, + { "sun", "sunrast" }, + { "ras", "sunrast" }, + { "rs", "sunrast" }, + { "ra", "sunrast" }, + { "im1", "sunrast" }, + { "im8", "sunrast" }, + { "im24", "sunrast" }, + { "im32", "sunrast" }, + { "sunras", "sunrast" }, + { "xbm", "xbm" }, + { "pam", "pam" }, + { "pbm", "pbm" }, + { "pgm", "pgm" }, + { "pgmyuv", "pgmyuv" }, + { "ppm", "ppm" }, + { "pnm", "ppm" }, + { "gif", "gif" }, + { "pix", "brender_pix" }, + { "exr", "exr" }, + { "pic", "pictor" }, + { "qoi", "qoi" }, + { "xface", "xface" }, + { "xwd", "xwd" }, + { "svg", "svg" }, + {0} +}; + +bool mp_codec_is_image(const char *codec) +{ + if (codec) { + for (int n = 0; type_to_codec[n][0]; n++) { + if (strcasecmp(type_to_codec[n][1], codec) == 0) + return true; + } + } + return false; +} + +const char *mp_map_type_to_image_codec(const char *type) +{ + if (type) { + for (int n = 0; type_to_codec[n][0]; n++) { + if (strcasecmp(type_to_codec[n][0], type) == 0) + return type_to_codec[n][1]; + } + } + return NULL; +}; + +static const char *const mimetype_to_codec[][2] = { + {"image/apng", "apng"}, + {"image/avif", "av1"}, + {"image/bmp", "bmp"}, + {"image/gif", "gif"}, + {"image/jpeg", "mjpeg"}, + {"image/jxl", "jpegxl"}, + {"image/png", "png"}, + {"image/tiff", "tiff"}, + {"image/webp", "webp"}, + {0} +}; + +const char *mp_map_mimetype_to_video_codec(const char *mimetype) +{ + if (mimetype) { + for (int n = 0; mimetype_to_codec[n][0]; n++) { + if (strcasecmp(mimetype_to_codec[n][0], mimetype) == 0) + return mimetype_to_codec[n][1]; + } + } + return NULL; +} |