avcodec/amr*dec: add multichannel support
This commit is contained in:
		
							parent
							
								
									e26c4d252f
								
							
						
					
					
						commit
						f282c34c00
					
				| @ -39,9 +39,17 @@ typedef struct AMRParseContext { | |||||||
|     ParseContext pc; |     ParseContext pc; | ||||||
|     uint64_t cumulated_size; |     uint64_t cumulated_size; | ||||||
|     uint64_t block_count; |     uint64_t block_count; | ||||||
|  |     int current_channel; | ||||||
|     int remaining; |     int remaining; | ||||||
| } AMRParseContext; | } AMRParseContext; | ||||||
| 
 | 
 | ||||||
|  | static av_cold int amr_parse_init(AVCodecParserContext *s1) | ||||||
|  | { | ||||||
|  |     AMRParseContext *s = s1->priv_data; | ||||||
|  |     s->remaining = -1; | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static int amr_parse(AVCodecParserContext *s1, | static int amr_parse(AVCodecParserContext *s1, | ||||||
|                      AVCodecContext *avctx, |                      AVCodecContext *avctx, | ||||||
|                      const uint8_t **poutbuf, int *poutbuf_size, |                      const uint8_t **poutbuf, int *poutbuf_size, | ||||||
| @ -57,10 +65,13 @@ static int amr_parse(AVCodecParserContext *s1, | |||||||
|     if (s1->flags & PARSER_FLAG_COMPLETE_FRAMES) { |     if (s1->flags & PARSER_FLAG_COMPLETE_FRAMES) { | ||||||
|         next = buf_size; |         next = buf_size; | ||||||
|     } else { |     } else { | ||||||
|         if (s->remaining) { |         int ch, offset = 0; | ||||||
|  | 
 | ||||||
|  |         for (ch = s->current_channel; ch < avctx->channels; ch++) { | ||||||
|  |             if (s->remaining >= 0) { | ||||||
|                 next = s->remaining; |                 next = s->remaining; | ||||||
|             } else { |             } else { | ||||||
|             int mode = (buf[0] >> 3) & 0x0F; |                 int mode = (buf[offset] >> 3) & 0x0F; | ||||||
| 
 | 
 | ||||||
|                 if (avctx->codec_id == AV_CODEC_ID_AMR_NB) { |                 if (avctx->codec_id == AV_CODEC_ID_AMR_NB) { | ||||||
|                     next = amrnb_packed_size[mode]; |                     next = amrnb_packed_size[mode]; | ||||||
| @ -69,9 +80,19 @@ static int amr_parse(AVCodecParserContext *s1, | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|         s->remaining = next - FFMIN(buf_size, next); |             offset += next; | ||||||
|         if (s->remaining) |             if (offset >= buf_size) { | ||||||
|  |                 s->remaining = offset - buf_size; | ||||||
|                 next = END_NOT_FOUND; |                 next = END_NOT_FOUND; | ||||||
|  |                 break; | ||||||
|  |             } else { | ||||||
|  |                 s->remaining = -1; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         s->current_channel = ch % avctx->channels; | ||||||
|  |         if (s->remaining < 0) | ||||||
|  |             next = offset; | ||||||
| 
 | 
 | ||||||
|         if (next != END_NOT_FOUND) { |         if (next != END_NOT_FOUND) { | ||||||
|             if (s->cumulated_size < UINT64_MAX - next) { |             if (s->cumulated_size < UINT64_MAX - next) { | ||||||
| @ -98,6 +119,7 @@ static int amr_parse(AVCodecParserContext *s1, | |||||||
| const AVCodecParser ff_amr_parser = { | const AVCodecParser ff_amr_parser = { | ||||||
|     .codec_ids      = { AV_CODEC_ID_AMR_NB, AV_CODEC_ID_AMR_WB }, |     .codec_ids      = { AV_CODEC_ID_AMR_NB, AV_CODEC_ID_AMR_WB }, | ||||||
|     .priv_data_size = sizeof(AMRParseContext), |     .priv_data_size = sizeof(AMRParseContext), | ||||||
|  |     .parser_init    = amr_parse_init, | ||||||
|     .parser_parse   = amr_parse, |     .parser_parse   = amr_parse, | ||||||
|     .parser_close   = ff_parse_close, |     .parser_close   = ff_parse_close, | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -145,6 +145,10 @@ typedef struct AMRContext { | |||||||
| 
 | 
 | ||||||
| } AMRContext; | } AMRContext; | ||||||
| 
 | 
 | ||||||
|  | typedef struct AMRChannelsContext { | ||||||
|  |     AMRContext ch[2]; | ||||||
|  | } AMRChannelsContext; | ||||||
|  | 
 | ||||||
| /** Double version of ff_weighted_vector_sumf() */ | /** Double version of ff_weighted_vector_sumf() */ | ||||||
| static void weighted_vector_sumd(double *out, const double *in_a, | static void weighted_vector_sumd(double *out, const double *in_a, | ||||||
|                                  const double *in_b, double weight_coeff_a, |                                  const double *in_b, double weight_coeff_a, | ||||||
| @ -159,20 +163,24 @@ static void weighted_vector_sumd(double *out, const double *in_a, | |||||||
| 
 | 
 | ||||||
| static av_cold int amrnb_decode_init(AVCodecContext *avctx) | static av_cold int amrnb_decode_init(AVCodecContext *avctx) | ||||||
| { | { | ||||||
|     AMRContext *p = avctx->priv_data; |     AMRChannelsContext *s = avctx->priv_data; | ||||||
|     int i; |     int i; | ||||||
| 
 | 
 | ||||||
|     if (avctx->channels > 1) { |     if (avctx->channels > 2) { | ||||||
|         avpriv_report_missing_feature(avctx, "multi-channel AMR"); |         avpriv_report_missing_feature(avctx, ">2 channel AMR"); | ||||||
|         return AVERROR_PATCHWELCOME; |         return AVERROR_PATCHWELCOME; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     if (!avctx->channels) { | ||||||
|         avctx->channels       = 1; |         avctx->channels       = 1; | ||||||
|         avctx->channel_layout = AV_CH_LAYOUT_MONO; |         avctx->channel_layout = AV_CH_LAYOUT_MONO; | ||||||
|  |     } | ||||||
|     if (!avctx->sample_rate) |     if (!avctx->sample_rate) | ||||||
|         avctx->sample_rate = 8000; |         avctx->sample_rate = 8000; | ||||||
|     avctx->sample_fmt     = AV_SAMPLE_FMT_FLT; |     avctx->sample_fmt     = AV_SAMPLE_FMT_FLTP; | ||||||
| 
 | 
 | ||||||
|  |     for (int ch = 0; ch < avctx->channels; ch++) { | ||||||
|  |         AMRContext *p = &s->ch[ch]; | ||||||
|     // p->excitation always points to the same position in p->excitation_buf
 |     // p->excitation always points to the same position in p->excitation_buf
 | ||||||
|     p->excitation = &p->excitation_buf[PITCH_DELAY_MAX + LP_FILTER_ORDER + 1]; |     p->excitation = &p->excitation_buf[PITCH_DELAY_MAX + LP_FILTER_ORDER + 1]; | ||||||
| 
 | 
 | ||||||
| @ -188,6 +196,7 @@ static av_cold int amrnb_decode_init(AVCodecContext *avctx) | |||||||
|     ff_acelp_vectors_init(&p->acelpv_ctx); |     ff_acelp_vectors_init(&p->acelpv_ctx); | ||||||
|     ff_celp_filter_init(&p->celpf_ctx); |     ff_celp_filter_init(&p->celpf_ctx); | ||||||
|     ff_celp_math_init(&p->celpm_ctx); |     ff_celp_math_init(&p->celpm_ctx); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| @ -949,25 +958,30 @@ static int amrnb_decode_frame(AVCodecContext *avctx, void *data, | |||||||
|                               int *got_frame_ptr, AVPacket *avpkt) |                               int *got_frame_ptr, AVPacket *avpkt) | ||||||
| { | { | ||||||
| 
 | 
 | ||||||
|     AMRContext *p = avctx->priv_data;        // pointer to private data
 |     AMRChannelsContext *s = avctx->priv_data;        // pointer to private data
 | ||||||
|     AVFrame *frame     = data; |     AVFrame *frame     = data; | ||||||
|     const uint8_t *buf = avpkt->data; |     const uint8_t *buf = avpkt->data; | ||||||
|     int buf_size       = avpkt->size; |     int buf_size       = avpkt->size; | ||||||
|     float *buf_out;                          // pointer to the output data buffer
 |     int ret; | ||||||
|     int i, subframe, ret; |  | ||||||
|     float fixed_gain_factor; |  | ||||||
|     AMRFixed fixed_sparse = {0};             // fixed vector up to anti-sparseness processing
 |  | ||||||
|     float spare_vector[AMR_SUBFRAME_SIZE];   // extra stack space to hold result from anti-sparseness processing
 |  | ||||||
|     float synth_fixed_gain;                  // the fixed gain that synthesis should use
 |  | ||||||
|     const float *synth_fixed_vector;         // pointer to the fixed vector that synthesis should use
 |  | ||||||
| 
 | 
 | ||||||
|     /* get output buffer */ |     /* get output buffer */ | ||||||
|     frame->nb_samples = AMR_BLOCK_SIZE; |     frame->nb_samples = AMR_BLOCK_SIZE; | ||||||
|     if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) |     if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) | ||||||
|         return ret; |         return ret; | ||||||
|     buf_out = (float *)frame->data[0]; | 
 | ||||||
|  |     for (int ch = 0; ch < avctx->channels; ch++) { | ||||||
|  |         AMRContext *p = &s->ch[ch]; | ||||||
|  |         float fixed_gain_factor; | ||||||
|  |         AMRFixed fixed_sparse = {0};             // fixed vector up to anti-sparseness processing
 | ||||||
|  |         float spare_vector[AMR_SUBFRAME_SIZE];   // extra stack space to hold result from anti-sparseness processing
 | ||||||
|  |         float synth_fixed_gain;                  // the fixed gain that synthesis should use
 | ||||||
|  |         const float *synth_fixed_vector;         // pointer to the fixed vector that synthesis should use
 | ||||||
|  |         float *buf_out = (float *)frame->extended_data[ch]; | ||||||
|  |         int channel_size; | ||||||
|  |         int i, subframe; | ||||||
| 
 | 
 | ||||||
|     p->cur_frame_mode = unpack_bitstream(p, buf, buf_size); |     p->cur_frame_mode = unpack_bitstream(p, buf, buf_size); | ||||||
|  |         channel_size = frame_sizes_nb[p->cur_frame_mode] + 1; // +7 for rounding and +8 for TOC
 | ||||||
|     if (p->cur_frame_mode == NO_DATA) { |     if (p->cur_frame_mode == NO_DATA) { | ||||||
|         av_log(avctx, AV_LOG_ERROR, "Corrupt bitstream\n"); |         av_log(avctx, AV_LOG_ERROR, "Corrupt bitstream\n"); | ||||||
|         return AVERROR_INVALIDDATA; |         return AVERROR_INVALIDDATA; | ||||||
| @ -1072,11 +1086,13 @@ static int amrnb_decode_frame(AVCodecContext *avctx, void *data, | |||||||
|      * qbar(n-1) rather than qbar(n) in section 6.1(4) equation 71. */ |      * qbar(n-1) rather than qbar(n) in section 6.1(4) equation 71. */ | ||||||
|     p->acelpv_ctx.weighted_vector_sumf(p->lsf_avg, p->lsf_avg, p->lsf_q[3], |     p->acelpv_ctx.weighted_vector_sumf(p->lsf_avg, p->lsf_avg, p->lsf_q[3], | ||||||
|                             0.84, 0.16, LP_FILTER_ORDER); |                             0.84, 0.16, LP_FILTER_ORDER); | ||||||
|  |         buf += channel_size; | ||||||
|  |         buf_size -= channel_size; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     *got_frame_ptr = 1; |     *got_frame_ptr = 1; | ||||||
| 
 | 
 | ||||||
|     /* return the amount of bytes consumed if everything was OK */ |     return avpkt->size; | ||||||
|     return frame_sizes_nb[p->cur_frame_mode] + 1; // +7 for rounding and +8 for TOC
 |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -1085,10 +1101,10 @@ const AVCodec ff_amrnb_decoder = { | |||||||
|     .long_name      = NULL_IF_CONFIG_SMALL("AMR-NB (Adaptive Multi-Rate NarrowBand)"), |     .long_name      = NULL_IF_CONFIG_SMALL("AMR-NB (Adaptive Multi-Rate NarrowBand)"), | ||||||
|     .type           = AVMEDIA_TYPE_AUDIO, |     .type           = AVMEDIA_TYPE_AUDIO, | ||||||
|     .id             = AV_CODEC_ID_AMR_NB, |     .id             = AV_CODEC_ID_AMR_NB, | ||||||
|     .priv_data_size = sizeof(AMRContext), |     .priv_data_size = sizeof(AMRChannelsContext), | ||||||
|     .init           = amrnb_decode_init, |     .init           = amrnb_decode_init, | ||||||
|     .decode         = amrnb_decode_frame, |     .decode         = amrnb_decode_frame, | ||||||
|     .capabilities   = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_CHANNEL_CONF, |     .capabilities   = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_CHANNEL_CONF, | ||||||
|     .sample_fmts    = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_FLT, |     .sample_fmts    = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_FLTP, | ||||||
|                                                      AV_SAMPLE_FMT_NONE }, |                                                      AV_SAMPLE_FMT_NONE }, | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -93,21 +93,30 @@ typedef struct AMRWBContext { | |||||||
| 
 | 
 | ||||||
| } AMRWBContext; | } AMRWBContext; | ||||||
| 
 | 
 | ||||||
|  | typedef struct AMRWBChannelsContext { | ||||||
|  |     AMRWBContext ch[2]; | ||||||
|  | } AMRWBChannelsContext; | ||||||
|  | 
 | ||||||
| static av_cold int amrwb_decode_init(AVCodecContext *avctx) | static av_cold int amrwb_decode_init(AVCodecContext *avctx) | ||||||
| { | { | ||||||
|     AMRWBContext *ctx = avctx->priv_data; |     AMRWBChannelsContext *s = avctx->priv_data; | ||||||
|     int i; |     int i; | ||||||
| 
 | 
 | ||||||
|     if (avctx->channels > 1) { |     if (avctx->channels > 2) { | ||||||
|         avpriv_report_missing_feature(avctx, "multi-channel AMR"); |         avpriv_report_missing_feature(avctx, ">2 channel AMR"); | ||||||
|         return AVERROR_PATCHWELCOME; |         return AVERROR_PATCHWELCOME; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     if (!avctx->channels) { | ||||||
|         avctx->channels       = 1; |         avctx->channels       = 1; | ||||||
|         avctx->channel_layout = AV_CH_LAYOUT_MONO; |         avctx->channel_layout = AV_CH_LAYOUT_MONO; | ||||||
|  |     } | ||||||
|     if (!avctx->sample_rate) |     if (!avctx->sample_rate) | ||||||
|         avctx->sample_rate = 16000; |         avctx->sample_rate = 16000; | ||||||
|     avctx->sample_fmt     = AV_SAMPLE_FMT_FLT; |     avctx->sample_fmt     = AV_SAMPLE_FMT_FLTP; | ||||||
|  | 
 | ||||||
|  |     for (int ch = 0; ch < avctx->channels; ch++) { | ||||||
|  |     AMRWBContext *ctx = &s->ch[ch]; | ||||||
| 
 | 
 | ||||||
|     av_lfg_init(&ctx->prng, 1); |     av_lfg_init(&ctx->prng, 1); | ||||||
| 
 | 
 | ||||||
| @ -124,6 +133,7 @@ static av_cold int amrwb_decode_init(AVCodecContext *avctx) | |||||||
|     ff_acelp_vectors_init(&ctx->acelpv_ctx); |     ff_acelp_vectors_init(&ctx->acelpv_ctx); | ||||||
|     ff_celp_filter_init(&ctx->celpf_ctx); |     ff_celp_filter_init(&ctx->celpf_ctx); | ||||||
|     ff_celp_math_init(&ctx->celpm_ctx); |     ff_celp_math_init(&ctx->celpm_ctx); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| @ -1094,13 +1104,21 @@ static void update_sub_state(AMRWBContext *ctx) | |||||||
| static int amrwb_decode_frame(AVCodecContext *avctx, void *data, | static int amrwb_decode_frame(AVCodecContext *avctx, void *data, | ||||||
|                               int *got_frame_ptr, AVPacket *avpkt) |                               int *got_frame_ptr, AVPacket *avpkt) | ||||||
| { | { | ||||||
|     AMRWBContext *ctx  = avctx->priv_data; |     AMRWBChannelsContext *s  = avctx->priv_data; | ||||||
|     AVFrame *frame     = data; |     AVFrame *frame     = data; | ||||||
|     AMRWBFrame   *cf   = &ctx->frame; |  | ||||||
|     const uint8_t *buf = avpkt->data; |     const uint8_t *buf = avpkt->data; | ||||||
|     int buf_size       = avpkt->size; |     int buf_size       = avpkt->size; | ||||||
|  |     int sub, i, ret; | ||||||
|  | 
 | ||||||
|  |     /* get output buffer */ | ||||||
|  |     frame->nb_samples = 4 * AMRWB_SFR_SIZE_16k; | ||||||
|  |     if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) | ||||||
|  |         return ret; | ||||||
|  | 
 | ||||||
|  |     for (int ch = 0; ch < avctx->channels; ch++) { | ||||||
|  |     AMRWBContext *ctx  = &s->ch[ch]; | ||||||
|  |     AMRWBFrame   *cf   = &ctx->frame; | ||||||
|     int expected_fr_size, header_size; |     int expected_fr_size, header_size; | ||||||
|     float *buf_out; |  | ||||||
|     float spare_vector[AMRWB_SFR_SIZE];      // extra stack space to hold result from anti-sparseness processing
 |     float spare_vector[AMRWB_SFR_SIZE];      // extra stack space to hold result from anti-sparseness processing
 | ||||||
|     float fixed_gain_factor;                 // fixed gain correction factor (gamma)
 |     float fixed_gain_factor;                 // fixed gain correction factor (gamma)
 | ||||||
|     float *synth_fixed_vector;               // pointer to the fixed vector that synthesis should use
 |     float *synth_fixed_vector;               // pointer to the fixed vector that synthesis should use
 | ||||||
| @ -1110,13 +1128,7 @@ static int amrwb_decode_frame(AVCodecContext *avctx, void *data, | |||||||
|     float hb_exc[AMRWB_SFR_SIZE_16k];        // excitation for the high frequency band
 |     float hb_exc[AMRWB_SFR_SIZE_16k];        // excitation for the high frequency band
 | ||||||
|     float hb_samples[AMRWB_SFR_SIZE_16k];    // filtered high-band samples from synthesis
 |     float hb_samples[AMRWB_SFR_SIZE_16k];    // filtered high-band samples from synthesis
 | ||||||
|     float hb_gain; |     float hb_gain; | ||||||
|     int sub, i, ret; |     float *buf_out = (float *)frame->extended_data[ch]; | ||||||
| 
 |  | ||||||
|     /* get output buffer */ |  | ||||||
|     frame->nb_samples = 4 * AMRWB_SFR_SIZE_16k; |  | ||||||
|     if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) |  | ||||||
|         return ret; |  | ||||||
|     buf_out = (float *)frame->data[0]; |  | ||||||
| 
 | 
 | ||||||
|     header_size      = decode_mime_header(ctx, buf); |     header_size      = decode_mime_header(ctx, buf); | ||||||
|     expected_fr_size = ((cf_sizes_wb[ctx->fr_cur_mode] + 7) >> 3) + 1; |     expected_fr_size = ((cf_sizes_wb[ctx->fr_cur_mode] + 7) >> 3) + 1; | ||||||
| @ -1127,9 +1139,10 @@ static int amrwb_decode_frame(AVCodecContext *avctx, void *data, | |||||||
|     if (ctx->fr_cur_mode == NO_DATA || !ctx->fr_quality) { |     if (ctx->fr_cur_mode == NO_DATA || !ctx->fr_quality) { | ||||||
|         /* The specification suggests a "random signal" and
 |         /* The specification suggests a "random signal" and
 | ||||||
|            "a muting technique" to "gradually decrease the output level". */ |            "a muting technique" to "gradually decrease the output level". */ | ||||||
|         av_samples_set_silence(&frame->data[0], 0, frame->nb_samples, 1, AV_SAMPLE_FMT_FLT); |         av_samples_set_silence(&frame->extended_data[ch], 0, frame->nb_samples, 1, AV_SAMPLE_FMT_FLT); | ||||||
|         *got_frame_ptr = 1; |         buf += expected_fr_size; | ||||||
|         return expected_fr_size; |         buf_size -= expected_fr_size; | ||||||
|  |         continue; | ||||||
|     } |     } | ||||||
|     if (ctx->fr_cur_mode > MODE_SID) { |     if (ctx->fr_cur_mode > MODE_SID) { | ||||||
|         av_log(avctx, AV_LOG_ERROR, |         av_log(avctx, AV_LOG_ERROR, | ||||||
| @ -1270,9 +1283,13 @@ static int amrwb_decode_frame(AVCodecContext *avctx, void *data, | |||||||
|     memcpy(ctx->isp_sub4_past, ctx->isp[3], LP_ORDER * sizeof(ctx->isp[3][0])); |     memcpy(ctx->isp_sub4_past, ctx->isp[3], LP_ORDER * sizeof(ctx->isp[3][0])); | ||||||
|     memcpy(ctx->isf_past_final, ctx->isf_cur, LP_ORDER * sizeof(float)); |     memcpy(ctx->isf_past_final, ctx->isf_cur, LP_ORDER * sizeof(float)); | ||||||
| 
 | 
 | ||||||
|  |         buf += expected_fr_size; | ||||||
|  |         buf_size -= expected_fr_size; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     *got_frame_ptr = 1; |     *got_frame_ptr = 1; | ||||||
| 
 | 
 | ||||||
|     return expected_fr_size; |     return avpkt->size; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const AVCodec ff_amrwb_decoder = { | const AVCodec ff_amrwb_decoder = { | ||||||
| @ -1280,7 +1297,7 @@ const AVCodec ff_amrwb_decoder = { | |||||||
|     .long_name      = NULL_IF_CONFIG_SMALL("AMR-WB (Adaptive Multi-Rate WideBand)"), |     .long_name      = NULL_IF_CONFIG_SMALL("AMR-WB (Adaptive Multi-Rate WideBand)"), | ||||||
|     .type           = AVMEDIA_TYPE_AUDIO, |     .type           = AVMEDIA_TYPE_AUDIO, | ||||||
|     .id             = AV_CODEC_ID_AMR_WB, |     .id             = AV_CODEC_ID_AMR_WB, | ||||||
|     .priv_data_size = sizeof(AMRWBContext), |     .priv_data_size = sizeof(AMRWBChannelsContext), | ||||||
|     .init           = amrwb_decode_init, |     .init           = amrwb_decode_init, | ||||||
|     .decode         = amrwb_decode_frame, |     .decode         = amrwb_decode_frame, | ||||||
|     .capabilities   = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_CHANNEL_CONF, |     .capabilities   = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_CHANNEL_CONF, | ||||||
|  | |||||||
| @ -21,13 +21,12 @@ | |||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
| Write and read amr data according to RFC3267, http://www.ietf.org/rfc/rfc3267.txt?number=3267
 | Write and read amr data according to RFC3267, http://www.ietf.org/rfc/rfc3267.txt?number=3267
 | ||||||
| 
 |  | ||||||
| Only mono files are supported. |  | ||||||
| 
 |  | ||||||
| */ | */ | ||||||
| 
 | 
 | ||||||
| #include "libavutil/channel_layout.h" | #include "libavutil/channel_layout.h" | ||||||
|  | #include "libavutil/intreadwrite.h" | ||||||
| #include "avformat.h" | #include "avformat.h" | ||||||
|  | #include "avio_internal.h" | ||||||
| #include "internal.h" | #include "internal.h" | ||||||
| #include "rawdec.h" | #include "rawdec.h" | ||||||
| #include "rawenc.h" | #include "rawenc.h" | ||||||
| @ -36,8 +35,10 @@ typedef struct AMRContext { | |||||||
|     FFRawDemuxerContext rawctx; |     FFRawDemuxerContext rawctx; | ||||||
| } AMRContext; | } AMRContext; | ||||||
| 
 | 
 | ||||||
| static const char AMR_header[]   = "#!AMR\n"; | static const uint8_t AMR_header[6]      = "#!AMR\x0a"; | ||||||
| static const char AMRWB_header[] = "#!AMR-WB\n"; | static const uint8_t AMRMC_header[12]   = "#!AMR_MC1.0\x0a"; | ||||||
|  | static const uint8_t AMRWB_header[9]    = "#!AMR-WB\x0a"; | ||||||
|  | static const uint8_t AMRWBMC_header[15] = "#!AMR-WB_MC1.0\x0a"; | ||||||
| 
 | 
 | ||||||
| static const uint8_t amrnb_packed_size[16] = { | static const uint8_t amrnb_packed_size[16] = { | ||||||
|     13, 14, 16, 18, 20, 21, 27, 32, 6, 1, 1, 1, 1, 1, 1, 1 |     13, 14, 16, 18, 20, 21, 27, 32, 6, 1, 1, 1, 1, 1, 1, 1 | ||||||
| @ -69,7 +70,7 @@ static int amr_probe(const AVProbeData *p) | |||||||
| { | { | ||||||
|     // Only check for "#!AMR" which could be amr-wb, amr-nb.
 |     // Only check for "#!AMR" which could be amr-wb, amr-nb.
 | ||||||
|     // This will also trigger multichannel files: "#!AMR_MC1.0\n" and
 |     // This will also trigger multichannel files: "#!AMR_MC1.0\n" and
 | ||||||
|     // "#!AMR-WB_MC1.0\n" (not supported)
 |     // "#!AMR-WB_MC1.0\n"
 | ||||||
| 
 | 
 | ||||||
|     if (!memcmp(p->buf, AMR_header, 5)) |     if (!memcmp(p->buf, AMR_header, 5)) | ||||||
|         return AVPROBE_SCORE_MAX; |         return AVPROBE_SCORE_MAX; | ||||||
| @ -82,35 +83,60 @@ static int amr_read_header(AVFormatContext *s) | |||||||
| { | { | ||||||
|     AVIOContext *pb = s->pb; |     AVIOContext *pb = s->pb; | ||||||
|     AVStream *st; |     AVStream *st; | ||||||
|     uint8_t header[9]; |     uint8_t header[19] = { 0 }; | ||||||
|  |     int read, back = 0, ret; | ||||||
| 
 | 
 | ||||||
|     if (avio_read(pb, header, 6) != 6) |     ret = ffio_ensure_seekback(s->pb, sizeof(header)); | ||||||
|         return AVERROR_INVALIDDATA; |     if (ret < 0) | ||||||
|  |         return ret; | ||||||
|  | 
 | ||||||
|  |     read = avio_read(pb, header, sizeof(header)); | ||||||
|  |     if (read < 0) | ||||||
|  |         return ret; | ||||||
| 
 | 
 | ||||||
|     st = avformat_new_stream(s, NULL); |     st = avformat_new_stream(s, NULL); | ||||||
|     if (!st) |     if (!st) | ||||||
|         return AVERROR(ENOMEM); |         return AVERROR(ENOMEM); | ||||||
|     if (memcmp(header, AMR_header, 6)) { |     if (!memcmp(header, AMR_header, sizeof(AMR_header))) { | ||||||
|         if (avio_read(pb, header + 6, 3) != 3) |  | ||||||
|             return AVERROR_INVALIDDATA; |  | ||||||
|         if (memcmp(header, AMRWB_header, 9)) { |  | ||||||
|             return -1; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         st->codecpar->codec_tag   = MKTAG('s', 'a', 'w', 'b'); |  | ||||||
|         st->codecpar->codec_id    = AV_CODEC_ID_AMR_WB; |  | ||||||
|         st->codecpar->sample_rate = 16000; |  | ||||||
|     } else { |  | ||||||
|         st->codecpar->codec_tag   = MKTAG('s', 'a', 'm', 'r'); |         st->codecpar->codec_tag   = MKTAG('s', 'a', 'm', 'r'); | ||||||
|         st->codecpar->codec_id    = AV_CODEC_ID_AMR_NB; |         st->codecpar->codec_id    = AV_CODEC_ID_AMR_NB; | ||||||
|         st->codecpar->sample_rate = 8000; |         st->codecpar->sample_rate = 8000; | ||||||
|     } |  | ||||||
|         st->codecpar->channels = 1; |         st->codecpar->channels = 1; | ||||||
|         st->codecpar->channel_layout = AV_CH_LAYOUT_MONO; |         st->codecpar->channel_layout = AV_CH_LAYOUT_MONO; | ||||||
|  |         back = read - sizeof(AMR_header); | ||||||
|  |     } else if (!memcmp(header, AMRWB_header, sizeof(AMRWB_header))) { | ||||||
|  |         st->codecpar->codec_tag   = MKTAG('s', 'a', 'w', 'b'); | ||||||
|  |         st->codecpar->codec_id    = AV_CODEC_ID_AMR_WB; | ||||||
|  |         st->codecpar->sample_rate = 16000; | ||||||
|  |         st->codecpar->channels = 1; | ||||||
|  |         st->codecpar->channel_layout = AV_CH_LAYOUT_MONO; | ||||||
|  |         back = read - sizeof(AMRWB_header); | ||||||
|  |     } else if (!memcmp(header, AMRMC_header, sizeof(AMRMC_header))) { | ||||||
|  |         st->codecpar->codec_tag   = MKTAG('s', 'a', 'm', 'r'); | ||||||
|  |         st->codecpar->codec_id    = AV_CODEC_ID_AMR_NB; | ||||||
|  |         st->codecpar->sample_rate = 8000; | ||||||
|  |         st->codecpar->channels    = AV_RL32(header + 12); | ||||||
|  |         back = read - 4 - sizeof(AMRMC_header); | ||||||
|  |     } else if (!memcmp(header, AMRWBMC_header, sizeof(AMRWBMC_header))) { | ||||||
|  |         st->codecpar->codec_tag   = MKTAG('s', 'a', 'w', 'b'); | ||||||
|  |         st->codecpar->codec_id    = AV_CODEC_ID_AMR_WB; | ||||||
|  |         st->codecpar->sample_rate = 16000; | ||||||
|  |         st->codecpar->channels    = AV_RL32(header + 15); | ||||||
|  |         back = read - 4 - sizeof(AMRWBMC_header); | ||||||
|  |     } else { | ||||||
|  |         return AVERROR_INVALIDDATA; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (st->codecpar->channels < 1) | ||||||
|  |         return AVERROR_INVALIDDATA; | ||||||
|  | 
 | ||||||
|     st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; |     st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; | ||||||
|     ffstream(st)->need_parsing = AVSTREAM_PARSE_FULL_RAW; |     ffstream(st)->need_parsing = AVSTREAM_PARSE_FULL_RAW; | ||||||
|     avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate); |     avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate); | ||||||
| 
 | 
 | ||||||
|  |     if (back > 0) | ||||||
|  |         avio_seek(pb, -back, SEEK_CUR); | ||||||
|  | 
 | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user