0

Here's a code snippet from libavutil/samplefmt.h:

/**
 * Audio Sample Formats
 *
 * @par
 * The data described by the sample format is always in native-endian order.
 * Sample values can be expressed by native C types, hence the lack of a signed
 * 24-bit sample format even though it is a common raw audio data format.
 *
 * @par
 * The floating-point formats are based on full volume being in the range
 * [-1.0, 1.0]. Any values outside this range are beyond full volume level.
 *
 * @par
 * The data layout as used in av_samples_fill_arrays() and elsewhere in Libav
 * (such as AVFrame in libavcodec) is as follows:
 *
 * @par
 * For planar sample formats, each audio channel is in a separate data plane,
 * and linesize is the buffer size, in bytes, for a single plane. All data
 * planes must be the same size. For packed sample formats, only the first data
 * plane is used, and samples for each channel are interleaved. In this case,
 * linesize is the buffer size, in bytes, for the 1 plane.
 */
enum AVSampleFormat {
    AV_SAMPLE_FMT_NONE = -1,
    AV_SAMPLE_FMT_U8,          ///< unsigned 8 bits
    AV_SAMPLE_FMT_S16,         ///< signed 16 bits
    AV_SAMPLE_FMT_S32,         ///< signed 32 bits
    AV_SAMPLE_FMT_FLT,         ///< float
    AV_SAMPLE_FMT_DBL,         ///< double

    AV_SAMPLE_FMT_U8P,         ///< unsigned 8 bits, planar
    AV_SAMPLE_FMT_S16P,        ///< signed 16 bits, planar
    AV_SAMPLE_FMT_S32P,        ///< signed 32 bits, planar
    AV_SAMPLE_FMT_FLTP,        ///< float, planar
    AV_SAMPLE_FMT_DBLP,        ///< double, planar

    AV_SAMPLE_FMT_NB           ///< Number of sample formats. DO NOT USE if linking dynamically
};

It specifically mentions that 24-bit is missing even though it is a common raw audio data format. So if I were using libav/ffmpeg to export to an audio file, how would I use 24-bit audio?

Exporting an audio file looks something like this:

AVCodec *codec = get_codec();
AVOutputFormat *oformat = get_output_format();
AVFormatContext *fmt_ctx = avformat_alloc_context();
assert(fmt_ctx);
int err = avio_open(&fmt_ctx->pb, get_output_filename(), AVIO_FLAG_WRITE);
assert(err >= 0);
fmt_ctx->oformat = oformat;
AVStream *stream = avformat_new_stream(fmt_ctx, codec);
assert(stream);
AVCodecContext *codec_ctx = stream->codec;
codec_ctx->bit_rate = get_export_bit_rate();

// How to set this to 24 bit instead of 32?
codec_ctx->sample_fmt = AV_SAMPLE_FMT_S32;

codec_ctx->sample_rate = get_sample_rate();
codec_ctx->channel_layout = get_channel_layout()
codec_ctx->channels = get_channel_count();
codec_ctx->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
andrewrk
  • 30,272
  • 27
  • 92
  • 113

1 Answers1

3

Use AV_SAMPLE_FMT_S32 and set ctx->bits_per_raw_sample to 24. The audio needs to be in the MSBs of the 32-bit integer then, ie. padded with 0 at the end.

Hendrik
  • 1,981
  • 16
  • 11
  • Why wouldn't it be LSB? Then the 3-byte value would be equal to the 4-byte value. – andrewrk Mar 18 '15 at 19:44
  • Because then it would be a different format. This way, you can treat it just like 32-bit, which simplifies the process. – Hendrik Mar 18 '15 at 20:38
  • Also, how would you determine if a codec supports 24-bit audio? – andrewrk Mar 18 '15 at 21:51
  • 1
    `0x123456 == 0x00123456`. This looks to me like the LSB's are matching and the MSB's are zeros. Are you saying that to convert 24-bit to 32-bit you would turn `0x123456` into `0x12345600`? – thejoshwolfe Mar 18 '15 at 22:38
  • 1
    According to my test results, you indeed must shift `0x123456` into `0x12345600` before passing the sample to libav/ffmpeg. This is the opposite of treating it just like 32-bit - it's simpler to *not* shift the value. – andrewrk Mar 19 '15 at 23:06