From b461b3bc4a14fe86aba4ed8e9adf30ab262896a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Reimar=20D=C3=B6ffinger?= Date: Fri, 2 Sep 2005 19:16:48 +0000 Subject: [PATCH] Support de-/encoding of 24 and 32 bit PCM (from and to internal 16 bit). Originally committed as revision 4548 to svn://svn.ffmpeg.org/ffmpeg/trunk --- ffmpeg.c | 13 ++++ libavcodec/allcodecs.c | 9 +++ libavcodec/avcodec.h | 18 +++++ libavcodec/pcm.c | 150 +++++++++++++++++++++++++++++++++++++++++ libavcodec/utils.c | 13 ++++ libavformat/utils.c | 17 +++++ libavformat/wav.c | 12 ++++ 7 files changed, 232 insertions(+) diff --git a/ffmpeg.c b/ffmpeg.c index d3e59a7be8..4c35671da0 100644 --- a/ffmpeg.c +++ b/ffmpeg.c @@ -599,6 +599,19 @@ static void do_audio_out(AVFormatContext *s, /* output a pcm frame */ /* XXX: change encoding codec API to avoid this ? */ switch(enc->codec->id) { + case CODEC_ID_PCM_S32LE: + case CODEC_ID_PCM_S32BE: + case CODEC_ID_PCM_U32LE: + case CODEC_ID_PCM_U32BE: + size_out = size_out << 1; + break; + case CODEC_ID_PCM_S24LE: + case CODEC_ID_PCM_S24BE: + case CODEC_ID_PCM_U24LE: + case CODEC_ID_PCM_U24BE: + case CODEC_ID_PCM_S24DAUD: + size_out = size_out / 2 * 3; + break; case CODEC_ID_PCM_S16LE: case CODEC_ID_PCM_S16BE: case CODEC_ID_PCM_U16LE: diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index ae79b693c1..db0232a65e 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -524,6 +524,15 @@ void avcodec_register_all(void) register_avcodec(& name ## _decoder); #endif +PCM_CODEC(CODEC_ID_PCM_S32LE, pcm_s32le); +PCM_CODEC(CODEC_ID_PCM_S32BE, pcm_s32be); +PCM_CODEC(CODEC_ID_PCM_U32LE, pcm_u32le); +PCM_CODEC(CODEC_ID_PCM_U32BE, pcm_u32be); +PCM_CODEC(CODEC_ID_PCM_S24LE, pcm_s24le); +PCM_CODEC(CODEC_ID_PCM_S24BE, pcm_s24be); +PCM_CODEC(CODEC_ID_PCM_U24LE, pcm_u24le); +PCM_CODEC(CODEC_ID_PCM_U24BE, pcm_u24be); +PCM_CODEC(CODEC_ID_PCM_S24DAUD, pcm_s24daud); PCM_CODEC(CODEC_ID_PCM_S16LE, pcm_s16le); PCM_CODEC(CODEC_ID_PCM_S16BE, pcm_s16be); PCM_CODEC(CODEC_ID_PCM_U16LE, pcm_u16le); diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index e4900dc335..49b864f99a 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -121,6 +121,15 @@ enum CodecID { CODEC_ID_PCM_U8, CODEC_ID_PCM_MULAW, CODEC_ID_PCM_ALAW, + CODEC_ID_PCM_S32LE, + CODEC_ID_PCM_S32BE, + CODEC_ID_PCM_U32LE, + CODEC_ID_PCM_U32BE, + CODEC_ID_PCM_S24LE, + CODEC_ID_PCM_S24BE, + CODEC_ID_PCM_U24LE, + CODEC_ID_PCM_U24BE, + CODEC_ID_PCM_S24DAUD, /* various adpcm codecs */ CODEC_ID_ADPCM_IMA_QT= 0x11000, @@ -2097,6 +2106,15 @@ extern AVCodec libgsm_decoder; extern AVCodec name ## _decoder; \ extern AVCodec name ## _encoder +PCM_CODEC(CODEC_ID_PCM_S32LE, pcm_s32le); +PCM_CODEC(CODEC_ID_PCM_S32BE, pcm_s32be); +PCM_CODEC(CODEC_ID_PCM_U32LE, pcm_u32le); +PCM_CODEC(CODEC_ID_PCM_U32BE, pcm_u32be); +PCM_CODEC(CODEC_ID_PCM_S24LE, pcm_s24le); +PCM_CODEC(CODEC_ID_PCM_S24BE, pcm_s24be); +PCM_CODEC(CODEC_ID_PCM_U24LE, pcm_u24le); +PCM_CODEC(CODEC_ID_PCM_U24BE, pcm_u24be); +PCM_CODEC(CODEC_ID_PCM_S24DAUD, pcm_s24daud); PCM_CODEC(CODEC_ID_PCM_S16LE, pcm_s16le); PCM_CODEC(CODEC_ID_PCM_S16BE, pcm_s16be); PCM_CODEC(CODEC_ID_PCM_U16LE, pcm_u16le); diff --git a/libavcodec/pcm.c b/libavcodec/pcm.c index 8e57d11a1c..5a92bd060b 100644 --- a/libavcodec/pcm.c +++ b/libavcodec/pcm.c @@ -23,6 +23,7 @@ */ #include "avcodec.h" +#include "bitstream.h" // for ff_reverse /* from g711.c by SUN microsystems (unrestricted use) */ @@ -128,6 +129,19 @@ static int pcm_encode_init(AVCodecContext *avctx) } switch(avctx->codec->id) { + case CODEC_ID_PCM_S32LE: + case CODEC_ID_PCM_S32BE: + case CODEC_ID_PCM_U32LE: + case CODEC_ID_PCM_U32BE: + avctx->block_align = 4 * avctx->channels; + break; + case CODEC_ID_PCM_S24LE: + case CODEC_ID_PCM_S24BE: + case CODEC_ID_PCM_U24LE: + case CODEC_ID_PCM_U24BE: + case CODEC_ID_PCM_S24DAUD: + avctx->block_align = 3 * avctx->channels; + break; case CODEC_ID_PCM_S16LE: case CODEC_ID_PCM_S16BE: case CODEC_ID_PCM_U16LE: @@ -170,6 +184,29 @@ static int pcm_encode_close(AVCodecContext *avctx) return 0; } +/** + * \brief convert samples from 16 bit + * \param bps byte per sample for the destination format, must be >= 2 + * \param le 0 for big-, 1 for little-endian + * \param samples input samples + * \param dst output samples + * \param n number of samples in samples buffer. + */ +static inline void encode_from16(int bps, int le, int us, + short **samples, uint8_t **dst, int n) { + if (bps > 2) + memset(*dst, 0, n * bps); + if (le) *dst += bps - 2; + for(;n>0;n--) { + register int v = *(*samples)++; + if (us) v += 0x8000; + (*dst)[le] = v >> 8; + (*dst)[1 - le] = v; + *dst += bps; + } + if (le) *dst -= bps - 2; +} + static int pcm_encode_frame(AVCodecContext *avctx, unsigned char *frame, int buf_size, void *data) { @@ -178,6 +215,19 @@ static int pcm_encode_frame(AVCodecContext *avctx, unsigned char *dst; switch(avctx->codec->id) { + case CODEC_ID_PCM_S32LE: + case CODEC_ID_PCM_S32BE: + case CODEC_ID_PCM_U32LE: + case CODEC_ID_PCM_U32BE: + sample_size = 4; + break; + case CODEC_ID_PCM_S24LE: + case CODEC_ID_PCM_S24BE: + case CODEC_ID_PCM_U24LE: + case CODEC_ID_PCM_U24BE: + case CODEC_ID_PCM_S24DAUD: + sample_size = 3; + break; case CODEC_ID_PCM_S16LE: case CODEC_ID_PCM_S16BE: case CODEC_ID_PCM_U16LE: @@ -193,6 +243,43 @@ static int pcm_encode_frame(AVCodecContext *avctx, dst = frame; switch(avctx->codec->id) { + case CODEC_ID_PCM_S32LE: + encode_from16(4, 1, 0, &samples, &dst, n); + break; + case CODEC_ID_PCM_S32BE: + encode_from16(4, 0, 0, &samples, &dst, n); + break; + case CODEC_ID_PCM_U32LE: + encode_from16(4, 1, 1, &samples, &dst, n); + break; + case CODEC_ID_PCM_U32BE: + encode_from16(4, 0, 1, &samples, &dst, n); + break; + case CODEC_ID_PCM_S24LE: + encode_from16(3, 1, 0, &samples, &dst, n); + break; + case CODEC_ID_PCM_S24BE: + encode_from16(3, 0, 0, &samples, &dst, n); + break; + case CODEC_ID_PCM_U24LE: + encode_from16(3, 1, 1, &samples, &dst, n); + break; + case CODEC_ID_PCM_U24BE: + encode_from16(3, 0, 1, &samples, &dst, n); + break; + case CODEC_ID_PCM_S24DAUD: + for(;n>0;n--) { + uint32_t tmp = ff_reverse[*samples >> 8] + + (ff_reverse[*samples & 0xff] << 8); + tmp <<= 4; // sync flags would go here + dst[2] = tmp & 0xff; + tmp >>= 8; + dst[1] = tmp & 0xff; + dst[0] = tmp >> 8; + samples++; + dst += 3; + } + break; case CODEC_ID_PCM_S16LE: for(;n>0;n--) { v = *samples++; @@ -287,6 +374,26 @@ static int pcm_decode_init(AVCodecContext * avctx) return 0; } +/** + * \brief convert samples to 16 bit + * \param bps byte per sample for the source format, must be >= 2 + * \param le 0 for big-, 1 for little-endian + * \param src input samples + * \param samples output samples + * \param src_len number of bytes in src + */ +static inline void decode_to16(int bps, int le, int us, + uint8_t **src, short **samples, int src_len) +{ + register int n = src_len / bps; + if (le) *src += bps - 2; + for(;n>0;n--) { + *(*samples)++ = ((*src)[le] << 8 | (*src)[1 - le]) - (us?0x8000:0); + *src += bps; + } + if (le) *src -= bps - 2; +} + static int pcm_decode_frame(AVCodecContext *avctx, void *data, int *data_size, uint8_t *buf, int buf_size) @@ -303,6 +410,40 @@ static int pcm_decode_frame(AVCodecContext *avctx, buf_size = AVCODEC_MAX_AUDIO_FRAME_SIZE/2; switch(avctx->codec->id) { + case CODEC_ID_PCM_S32LE: + decode_to16(4, 1, 0, &src, &samples, buf_size); + break; + case CODEC_ID_PCM_S32BE: + decode_to16(4, 0, 0, &src, &samples, buf_size); + break; + case CODEC_ID_PCM_U32LE: + decode_to16(4, 1, 1, &src, &samples, buf_size); + break; + case CODEC_ID_PCM_U32BE: + decode_to16(4, 0, 1, &src, &samples, buf_size); + break; + case CODEC_ID_PCM_S24LE: + decode_to16(3, 1, 0, &src, &samples, buf_size); + break; + case CODEC_ID_PCM_S24BE: + decode_to16(3, 0, 0, &src, &samples, buf_size); + break; + case CODEC_ID_PCM_U24LE: + decode_to16(3, 1, 1, &src, &samples, buf_size); + break; + case CODEC_ID_PCM_U24BE: + decode_to16(3, 0, 1, &src, &samples, buf_size); + break; + case CODEC_ID_PCM_S24DAUD: + n = buf_size / 3; + for(;n>0;n--) { + uint32_t v = src[0] << 16 | src[1] << 8 | src[2]; + v >>= 4; // sync flags are here + *samples++ = ff_reverse[(v >> 8) & 0xff] + + (ff_reverse[v & 0xff] << 8); + src += 3; + } + break; case CODEC_ID_PCM_S16LE: n = buf_size >> 1; for(;n>0;n--) { @@ -382,6 +523,15 @@ AVCodec name ## _decoder = { \ pcm_decode_frame, \ } +PCM_CODEC(CODEC_ID_PCM_S32LE, pcm_s32le); +PCM_CODEC(CODEC_ID_PCM_S32BE, pcm_s32be); +PCM_CODEC(CODEC_ID_PCM_U32LE, pcm_u32le); +PCM_CODEC(CODEC_ID_PCM_U32BE, pcm_u32be); +PCM_CODEC(CODEC_ID_PCM_S24LE, pcm_s24le); +PCM_CODEC(CODEC_ID_PCM_S24BE, pcm_s24be); +PCM_CODEC(CODEC_ID_PCM_U24LE, pcm_u24le); +PCM_CODEC(CODEC_ID_PCM_U24BE, pcm_u24be); +PCM_CODEC(CODEC_ID_PCM_S24DAUD, pcm_s24daud); PCM_CODEC(CODEC_ID_PCM_S16LE, pcm_s16le); PCM_CODEC(CODEC_ID_PCM_S16BE, pcm_s16be); PCM_CODEC(CODEC_ID_PCM_U16LE, pcm_u16le); diff --git a/libavcodec/utils.c b/libavcodec/utils.c index f2a8ae0001..ec234f6222 100644 --- a/libavcodec/utils.c +++ b/libavcodec/utils.c @@ -828,6 +828,19 @@ void avcodec_string(char *buf, int buf_size, AVCodecContext *enc, int encode) /* for PCM codecs, compute bitrate directly */ switch(enc->codec_id) { + case CODEC_ID_PCM_S32LE: + case CODEC_ID_PCM_S32BE: + case CODEC_ID_PCM_U32LE: + case CODEC_ID_PCM_U32BE: + bitrate = enc->sample_rate * enc->channels * 32; + break; + case CODEC_ID_PCM_S24LE: + case CODEC_ID_PCM_S24BE: + case CODEC_ID_PCM_U24LE: + case CODEC_ID_PCM_U24BE: + case CODEC_ID_PCM_S24DAUD: + bitrate = enc->sample_rate * enc->channels * 24; + break; case CODEC_ID_PCM_S16LE: case CODEC_ID_PCM_S16BE: case CODEC_ID_PCM_U16LE: diff --git a/libavformat/utils.c b/libavformat/utils.c index eb19eea527..7dede05b49 100644 --- a/libavformat/utils.c +++ b/libavformat/utils.c @@ -643,6 +643,23 @@ static int get_audio_frame_size(AVCodecContext *enc, int size) /* specific hack for pcm codecs because no frame size is provided */ switch(enc->codec_id) { + case CODEC_ID_PCM_S32LE: + case CODEC_ID_PCM_S32BE: + case CODEC_ID_PCM_U32LE: + case CODEC_ID_PCM_U32BE: + if (enc->channels == 0) + return -1; + frame_size = size / (4 * enc->channels); + break; + case CODEC_ID_PCM_S24LE: + case CODEC_ID_PCM_S24BE: + case CODEC_ID_PCM_U24LE: + case CODEC_ID_PCM_U24BE: + case CODEC_ID_PCM_S24DAUD: + if (enc->channels == 0) + return -1; + frame_size = size / (3 * enc->channels); + break; case CODEC_ID_PCM_S16LE: case CODEC_ID_PCM_S16BE: case CODEC_ID_PCM_U16LE: diff --git a/libavformat/wav.c b/libavformat/wav.c index 6557c0a962..e566f5ee6b 100644 --- a/libavformat/wav.c +++ b/libavformat/wav.c @@ -25,6 +25,8 @@ const CodecTag codec_wav_tags[] = { { CODEC_ID_AC3, 0x2000 }, { CODEC_ID_PCM_S16LE, 0x01 }, { CODEC_ID_PCM_U8, 0x01 }, /* must come after s16le in this list */ + { CODEC_ID_PCM_S24LE, 0x01 }, + { CODEC_ID_PCM_S32LE, 0x01 }, { CODEC_ID_PCM_ALAW, 0x06 }, { CODEC_ID_PCM_MULAW, 0x07 }, { CODEC_ID_ADPCM_MS, 0x02 }, @@ -68,6 +70,10 @@ int put_wav_header(ByteIOContext *pb, AVCodecContext *enc) bps = 0; } else if (enc->codec_id == CODEC_ID_ADPCM_IMA_WAV || enc->codec_id == CODEC_ID_ADPCM_MS || enc->codec_id == CODEC_ID_ADPCM_G726 || enc->codec_id == CODEC_ID_ADPCM_YAMAHA) { // bps = 4; + } else if (enc->codec_id == CODEC_ID_PCM_S24LE) { + bps = 24; + } else if (enc->codec_id == CODEC_ID_PCM_S32LE) { + bps = 32; } else { bps = 16; } @@ -82,6 +88,8 @@ int put_wav_header(ByteIOContext *pb, AVCodecContext *enc) } else blkalign = enc->channels*bps >> 3; if (enc->codec_id == CODEC_ID_PCM_U8 || + enc->codec_id == CODEC_ID_PCM_S24LE || + enc->codec_id == CODEC_ID_PCM_S32LE || enc->codec_id == CODEC_ID_PCM_S16LE) { bytespersec = enc->sample_rate * blkalign; } else { @@ -179,6 +187,10 @@ int wav_codec_get_id(unsigned int tag, int bps) /* handle specific u8 codec */ if (id == CODEC_ID_PCM_S16LE && bps == 8) id = CODEC_ID_PCM_U8; + if (id == CODEC_ID_PCM_S16LE && bps == 24) + id = CODEC_ID_PCM_S24LE; + if (id == CODEC_ID_PCM_S16LE && bps == 32) + id = CODEC_ID_PCM_S32LE; return id; }