diff options
Diffstat (limited to 'audio/format.c')
-rw-r--r-- | audio/format.c | 267 |
1 files changed, 267 insertions, 0 deletions
diff --git a/audio/format.c b/audio/format.c new file mode 100644 index 0000000..4441456 --- /dev/null +++ b/audio/format.c @@ -0,0 +1,267 @@ +/* + * Copyright (C) 2005 Alex Beregszaszi + * + * 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 <limits.h> + +#include "common/common.h" +#include "format.h" + +// number of bytes per sample, 0 if invalid/unknown +int af_fmt_to_bytes(int format) +{ + switch (af_fmt_from_planar(format)) { + case AF_FORMAT_U8: return 1; + case AF_FORMAT_S16: return 2; + case AF_FORMAT_S32: return 4; + case AF_FORMAT_S64: return 8; + case AF_FORMAT_FLOAT: return 4; + case AF_FORMAT_DOUBLE: return 8; + } + if (af_fmt_is_spdif(format)) + return 2; + return 0; +} + +// All formats are considered signed, except explicitly unsigned int formats. +bool af_fmt_is_unsigned(int format) +{ + return format == AF_FORMAT_U8 || format == AF_FORMAT_U8P; +} + +bool af_fmt_is_float(int format) +{ + format = af_fmt_from_planar(format); + return format == AF_FORMAT_FLOAT || format == AF_FORMAT_DOUBLE; +} + +// true for both unsigned and signed ints +bool af_fmt_is_int(int format) +{ + return format && !af_fmt_is_spdif(format) && !af_fmt_is_float(format); +} + +bool af_fmt_is_spdif(int format) +{ + return af_format_sample_alignment(format) > 1; +} + +bool af_fmt_is_pcm(int format) +{ + return af_fmt_is_valid(format) && !af_fmt_is_spdif(format); +} + +static const int planar_formats[][2] = { + {AF_FORMAT_U8P, AF_FORMAT_U8}, + {AF_FORMAT_S16P, AF_FORMAT_S16}, + {AF_FORMAT_S32P, AF_FORMAT_S32}, + {AF_FORMAT_S64P, AF_FORMAT_S64}, + {AF_FORMAT_FLOATP, AF_FORMAT_FLOAT}, + {AF_FORMAT_DOUBLEP, AF_FORMAT_DOUBLE}, +}; + +bool af_fmt_is_planar(int format) +{ + for (int n = 0; n < MP_ARRAY_SIZE(planar_formats); n++) { + if (planar_formats[n][0] == format) + return true; + } + return false; +} + +// Return the planar format corresponding to the given format. +// If the format is already planar or if there's no equivalent, +// return it. +int af_fmt_to_planar(int format) +{ + for (int n = 0; n < MP_ARRAY_SIZE(planar_formats); n++) { + if (planar_formats[n][1] == format) + return planar_formats[n][0]; + } + return format; +} + +// Return the interleaved format corresponding to the given format. +// If the format is already interleaved or if there's no equivalent, +// return it. +int af_fmt_from_planar(int format) +{ + for (int n = 0; n < MP_ARRAY_SIZE(planar_formats); n++) { + if (planar_formats[n][0] == format) + return planar_formats[n][1]; + } + return format; +} + +bool af_fmt_is_valid(int format) +{ + return format > 0 && format < AF_FORMAT_COUNT; +} + +const char *af_fmt_to_str(int format) +{ + switch (format) { + case AF_FORMAT_U8: return "u8"; + case AF_FORMAT_S16: return "s16"; + case AF_FORMAT_S32: return "s32"; + case AF_FORMAT_S64: return "s64"; + case AF_FORMAT_FLOAT: return "float"; + case AF_FORMAT_DOUBLE: return "double"; + case AF_FORMAT_U8P: return "u8p"; + case AF_FORMAT_S16P: return "s16p"; + case AF_FORMAT_S32P: return "s32p"; + case AF_FORMAT_S64P: return "s64p"; + case AF_FORMAT_FLOATP: return "floatp"; + case AF_FORMAT_DOUBLEP: return "doublep"; + case AF_FORMAT_S_AAC: return "spdif-aac"; + case AF_FORMAT_S_AC3: return "spdif-ac3"; + case AF_FORMAT_S_DTS: return "spdif-dts"; + case AF_FORMAT_S_DTSHD: return "spdif-dtshd"; + case AF_FORMAT_S_EAC3: return "spdif-eac3"; + case AF_FORMAT_S_MP3: return "spdif-mp3"; + case AF_FORMAT_S_TRUEHD: return "spdif-truehd"; + } + return "??"; +} + +void af_fill_silence(void *dst, size_t bytes, int format) +{ + memset(dst, af_fmt_is_unsigned(format) ? 0x80 : 0, bytes); +} + +// Returns a "score" that serves as heuristic how lossy or hard a conversion is. +// If the formats are equal, 1024 is returned. If they are gravely incompatible +// (like s16<->ac3), INT_MIN is returned. If there is implied loss of precision +// (like s16->s8), a value <0 is returned. +int af_format_conversion_score(int dst_format, int src_format) +{ + if (dst_format == AF_FORMAT_UNKNOWN || src_format == AF_FORMAT_UNKNOWN) + return INT_MIN; + if (dst_format == src_format) + return 1024; + // Can't be normally converted + if (!af_fmt_is_pcm(dst_format) || !af_fmt_is_pcm(src_format)) + return INT_MIN; + int score = 1024; + if (af_fmt_is_planar(dst_format) != af_fmt_is_planar(src_format)) + score -= 1; // has to (de-)planarize + if (af_fmt_is_float(dst_format) != af_fmt_is_float(src_format)) { + int dst_bytes = af_fmt_to_bytes(dst_format); + if (af_fmt_is_float(dst_format)) { + // For int->float, consider a lower bound on the precision difference. + int bytes = (dst_bytes == 4 ? 3 : 6) - af_fmt_to_bytes(src_format); + if (bytes >= 0) { + score -= 8 * bytes; // excess precision + } else { + score += 1024 * (bytes - 1); // precision is lost (i.e. s32 -> float) + } + } else { + // float->int is the worst case. Penalize heavily and + // prefer highest bit depth int. + score -= 1048576 * (8 - dst_bytes); + } + score -= 512; // penalty for any float <-> int conversion + } else { + int bytes = af_fmt_to_bytes(dst_format) - af_fmt_to_bytes(src_format); + if (bytes > 0) { + score -= 8 * bytes; // has to add padding + } else if (bytes < 0) { + score += 1024 * (bytes - 1); // has to reduce bit depth + } + } + return score; +} + +struct mp_entry { + int fmt; + int score; +}; + +static int cmp_entry(const void *a, const void *b) +{ +#define CMP_INT(a, b) (a > b ? 1 : (a < b ? -1 : 0)) + return -CMP_INT(((struct mp_entry *)a)->score, ((struct mp_entry *)b)->score); +} + +// Return a list of sample format compatible to src_format, sorted by order +// of preference. out_formats[0] will be src_format (as long as it's valid), +// and the list is terminated with 0 (AF_FORMAT_UNKNOWN). +// Keep in mind that this also returns formats with flipped interleaving +// (e.g. for s16, it returns [s16, s16p, ...]). +// out_formats must be an int[AF_FORMAT_COUNT + 1] array. +void af_get_best_sample_formats(int src_format, int *out_formats) +{ + int num = 0; + struct mp_entry e[AF_FORMAT_COUNT + 1]; + for (int fmt = 1; fmt < AF_FORMAT_COUNT; fmt++) { + int score = af_format_conversion_score(fmt, src_format); + if (score > INT_MIN) + e[num++] = (struct mp_entry){fmt, score}; + } + qsort(e, num, sizeof(e[0]), cmp_entry); + for (int n = 0; n < num; n++) + out_formats[n] = e[n].fmt; + out_formats[num] = 0; +} + +// Return the best match to src_samplerate from the list provided in the array +// *available, which must be terminated by 0, or itself NULL. If *available is +// empty or NULL, return a negative value. Exact match to src_samplerate is +// most preferred, followed by the lowest integer multiple, followed by the +// maximum of *available. +int af_select_best_samplerate(int src_samplerate, const int *available) +{ + if (!available) + return -1; + + int min_mult_rate = INT_MAX; + int max_rate = INT_MIN; + for (int i = 0; available[i]; i++) { + if (available[i] == src_samplerate) + return available[i]; + + if (!(available[i] % src_samplerate)) + min_mult_rate = MPMIN(min_mult_rate, available[i]); + + max_rate = MPMAX(max_rate, available[i]); + } + + if (min_mult_rate < INT_MAX) + return min_mult_rate; + + if (max_rate > INT_MIN) + return max_rate; + + return -1; +} + +// Return the number of samples that make up one frame in this format. +// You get the byte size by multiplying them with sample size and channel count. +int af_format_sample_alignment(int format) +{ + switch (format) { + case AF_FORMAT_S_AAC: return 16384 / 4; + case AF_FORMAT_S_AC3: return 6144 / 4; + case AF_FORMAT_S_DTSHD: return 32768 / 16; + case AF_FORMAT_S_DTS: return 2048 / 4; + case AF_FORMAT_S_EAC3: return 24576 / 4; + case AF_FORMAT_S_MP3: return 4608 / 4; + case AF_FORMAT_S_TRUEHD: return 61440 / 16; + default: return 1; + } +} |