784 lines · 25.1 KB · C
RawDownload
/* * Opus decoder * Copyright (c) 2012 Andrew D'Addesio * Copyright (c) 2013-2014 Mozilla Corporation * * This file is part of FFmpeg. * * FFmpeg 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. * * FFmpeg 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 FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * @file * Opus decoder * @author Andrew D'Addesio, Anton Khirnov * * Codec homepage: http://opus-codec.org/ * Specification: http://tools.ietf.org/html/rfc6716 * Ogg Opus specification: https://tools.ietf.org/html/draft-ietf-codec-oggopus-03 * * Ogg-contained .opus files can be produced with opus-tools: * http://git.xiph.org/?p=opus-tools.git */ #include <stdint.h> #include "libavutil/attributes.h"#include "libavutil/audio_fifo.h"#include "libavutil/channel_layout.h"#include "libavutil/ffmath.h"#include "libavutil/float_dsp.h"#include "libavutil/frame.h"#include "libavutil/mem.h"#include "libavutil/mem_internal.h"#include "libavutil/opt.h" #include "libswresample/swresample.h" #include "avcodec.h"#include "codec_internal.h"#include "decode.h"#include "opus.h"#include "tab.h"#include "celt.h"#include "parse.h"#include "rc.h"#include "silk.h" static const uint16_t silk_frame_duration_ms[16] = {    10, 20, 40, 60,    10, 20, 40, 60,    10, 20, 40, 60,    10, 20,    10, 20,}; /* number of samples of silence to feed to the resampler * at the beginning */static const int silk_resample_delay[] = {    4, 8, 11, 11, 11}; typedef struct OpusStreamContext {    AVCodecContext *avctx;    int output_channels;     /* number of decoded samples for this stream */    int decoded_samples;    /* current output buffers for this stream */    float *out[2];    int out_size;    /* Buffer with samples from this stream for synchronizing     * the streams when they have different resampling delays */    AVAudioFifo *sync_buffer;     OpusRangeCoder rc;    OpusRangeCoder redundancy_rc;    SilkContext *silk;    CeltFrame *celt;    AVFloatDSPContext *fdsp;     float silk_buf[2][960];    float *silk_output[2];    DECLARE_ALIGNED(32, float, celt_buf)[2][960];    float *celt_output[2];     DECLARE_ALIGNED(32, float, redundancy_buf)[2][960];    float *redundancy_output[2];     /* buffers for the next samples to be decoded */    float *cur_out[2];    int remaining_out_size;     float *out_dummy;    int    out_dummy_allocated_size;     SwrContext *swr;    AVAudioFifo *celt_delay;    int silk_samplerate;    /* number of samples we still want to get from the resampler */    int delayed_samples;     OpusPacket packet;     int redundancy_idx;} OpusStreamContext; typedef struct OpusContext {    AVClass *av_class;     struct OpusStreamContext *streams;    int apply_phase_inv;     AVFloatDSPContext *fdsp;    float   gain;     OpusParseContext p;} OpusContext; static int get_silk_samplerate(int config){    if (config < 4)        return 8000;    else if (config < 8)        return 12000;    return 16000;} static void opus_fade(float *out,                      const float *in1, const float *in2,                      const float *window, int len){    int i;    for (i = 0; i < len; i++)        out[i] = in2[i] * window[i] + in1[i] * (1.0 - window[i]);} static int opus_flush_resample(OpusStreamContext *s, int nb_samples){    int celt_size = av_audio_fifo_size(s->celt_delay);    int ret, i;    ret = swr_convert(s->swr,                      (uint8_t**)s->cur_out, nb_samples,                      NULL, 0);    if (ret < 0)        return ret;    else if (ret != nb_samples) {        av_log(s->avctx, AV_LOG_ERROR, "Wrong number of flushed samples: %d\n",               ret);        return AVERROR_BUG;    }     if (celt_size) {        if (celt_size != nb_samples) {            av_log(s->avctx, AV_LOG_ERROR, "Wrong number of CELT delay samples.\n");            return AVERROR_BUG;        }        av_audio_fifo_read(s->celt_delay, (void**)s->celt_output, nb_samples);        for (i = 0; i < s->output_channels; i++) {            s->fdsp->vector_fmac_scalar(s->cur_out[i],                                        s->celt_output[i], 1.0,                                        nb_samples);        }    }     if (s->redundancy_idx) {        for (i = 0; i < s->output_channels; i++)            opus_fade(s->cur_out[i], s->cur_out[i],                      s->redundancy_output[i] + 120 + s->redundancy_idx,                      ff_celt_window2 + s->redundancy_idx, 120 - s->redundancy_idx);        s->redundancy_idx = 0;    }     s->cur_out[0]         += nb_samples;    s->cur_out[1]         += nb_samples;    s->remaining_out_size -= nb_samples * sizeof(float);     return 0;} static int opus_init_resample(OpusStreamContext *s){    static const float delay[16] = { 0.0 };    const uint8_t *delayptr[2] = { (uint8_t*)delay, (uint8_t*)delay };    int ret;     av_opt_set_int(s->swr, "in_sample_rate", s->silk_samplerate, 0);    ret = swr_init(s->swr);    if (ret < 0) {        av_log(s->avctx, AV_LOG_ERROR, "Error opening the resampler.\n");        return ret;    }     ret = swr_convert(s->swr,                      NULL, 0,                      delayptr, silk_resample_delay[s->packet.bandwidth]);    if (ret < 0) {        av_log(s->avctx, AV_LOG_ERROR,               "Error feeding initial silence to the resampler.\n");        return ret;    }     return 0;} static int opus_decode_redundancy(OpusStreamContext *s, const uint8_t *data, int size){    int ret = ff_opus_rc_dec_init(&s->redundancy_rc, data, size);    if (ret < 0)        goto fail;    ff_opus_rc_dec_raw_init(&s->redundancy_rc, data + size, size);     ret = ff_celt_decode_frame(s->celt, &s->redundancy_rc,                               s->redundancy_output,                               s->packet.stereo + 1, 240,                               0, ff_celt_band_end[s->packet.bandwidth]);    if (ret < 0)        goto fail;     return 0;fail:    av_log(s->avctx, AV_LOG_ERROR, "Error decoding the redundancy frame.\n");    return ret;} static int opus_decode_frame(OpusStreamContext *s, const uint8_t *data, int size){    int samples    = s->packet.frame_duration;    int redundancy = 0;    int redundancy_size, redundancy_pos;    int ret, i, consumed;    int delayed_samples = s->delayed_samples;     ret = ff_opus_rc_dec_init(&s->rc, data, size);    if (ret < 0)        return ret;     /* decode the silk frame */    if (s->packet.mode == OPUS_MODE_SILK || s->packet.mode == OPUS_MODE_HYBRID) {        if (!swr_is_initialized(s->swr)) {            ret = opus_init_resample(s);            if (ret < 0)                return ret;        }         samples = ff_silk_decode_superframe(s->silk, &s->rc, s->silk_output,                                            FFMIN(s->packet.bandwidth, OPUS_BANDWIDTH_WIDEBAND),                                            s->packet.stereo + 1,                                            silk_frame_duration_ms[s->packet.config]);        if (samples < 0) {            av_log(s->avctx, AV_LOG_ERROR, "Error decoding a SILK frame.\n");            return samples;        }        samples = swr_convert(s->swr,                              (uint8_t**)s->cur_out, s->packet.frame_duration,                              (const uint8_t**)s->silk_output, samples);        if (samples < 0) {            av_log(s->avctx, AV_LOG_ERROR, "Error resampling SILK data.\n");            return samples;        }        av_assert2((samples & 7) == 0);        s->delayed_samples += s->packet.frame_duration - samples;    } else        ff_silk_flush(s->silk);     // decode redundancy information    consumed = opus_rc_tell(&s->rc);    if (s->packet.mode == OPUS_MODE_HYBRID && consumed + 37 <= size * 8)        redundancy = ff_opus_rc_dec_log(&s->rc, 12);    else if (s->packet.mode == OPUS_MODE_SILK && consumed + 17 <= size * 8)        redundancy = 1;     if (redundancy) {        redundancy_pos = ff_opus_rc_dec_log(&s->rc, 1);         if (s->packet.mode == OPUS_MODE_HYBRID)            redundancy_size = ff_opus_rc_dec_uint(&s->rc, 256) + 2;        else            redundancy_size = size - (consumed + 7) / 8;        size -= redundancy_size;        if (size < 0) {            av_log(s->avctx, AV_LOG_ERROR, "Invalid redundancy frame size.\n");            return AVERROR_INVALIDDATA;        }         if (redundancy_pos) {            ret = opus_decode_redundancy(s, data + size, redundancy_size);            if (ret < 0)                return ret;            ff_celt_flush(s->celt);        }    }     /* decode the CELT frame */    if (s->packet.mode == OPUS_MODE_CELT || s->packet.mode == OPUS_MODE_HYBRID) {        float *out_tmp[2] = { s->cur_out[0], s->cur_out[1] };        float **dst = (s->packet.mode == OPUS_MODE_CELT) ?                      out_tmp : s->celt_output;        int celt_output_samples = samples;        int delay_samples = av_audio_fifo_size(s->celt_delay);         if (delay_samples) {            if (s->packet.mode == OPUS_MODE_HYBRID) {                av_audio_fifo_read(s->celt_delay, (void**)s->celt_output, delay_samples);                 for (i = 0; i < s->output_channels; i++) {                    s->fdsp->vector_fmac_scalar(out_tmp[i], s->celt_output[i], 1.0,                                                delay_samples);                    out_tmp[i] += delay_samples;                }                celt_output_samples -= delay_samples;            } else {                av_log(s->avctx, AV_LOG_WARNING,                       "Spurious CELT delay samples present.\n");                av_audio_fifo_drain(s->celt_delay, delay_samples);                if (s->avctx->err_recognition & AV_EF_EXPLODE)                    return AVERROR_BUG;            }        }         ff_opus_rc_dec_raw_init(&s->rc, data + size, size);         ret = ff_celt_decode_frame(s->celt, &s->rc, dst,                                   s->packet.stereo + 1,                                   s->packet.frame_duration,                                   (s->packet.mode == OPUS_MODE_HYBRID) ? 17 : 0,                                   ff_celt_band_end[s->packet.bandwidth]);        if (ret < 0)            return ret;         if (s->packet.mode == OPUS_MODE_HYBRID) {            int celt_delay = s->packet.frame_duration - celt_output_samples;            void *delaybuf[2] = { s->celt_output[0] + celt_output_samples,                                  s->celt_output[1] + celt_output_samples };             for (i = 0; i < s->output_channels; i++) {                s->fdsp->vector_fmac_scalar(out_tmp[i],                                            s->celt_output[i], 1.0,                                            celt_output_samples);            }             ret = av_audio_fifo_write(s->celt_delay, delaybuf, celt_delay);            if (ret < 0)                return ret;        }    } else        ff_celt_flush(s->celt);     if (s->redundancy_idx) {        for (i = 0; i < s->output_channels; i++)            opus_fade(s->cur_out[i], s->cur_out[i],                      s->redundancy_output[i] + 120 + s->redundancy_idx,                      ff_celt_window2 + s->redundancy_idx, 120 - s->redundancy_idx);        s->redundancy_idx = 0;    }    if (redundancy) {        if (!redundancy_pos) {            ff_celt_flush(s->celt);            ret = opus_decode_redundancy(s, data + size, redundancy_size);            if (ret < 0)                return ret;             for (i = 0; i < s->output_channels; i++) {                opus_fade(s->cur_out[i] + samples - 120 + delayed_samples,                          s->cur_out[i] + samples - 120 + delayed_samples,                          s->redundancy_output[i] + 120,                          ff_celt_window2, 120 - delayed_samples);                if (delayed_samples)                    s->redundancy_idx = 120 - delayed_samples;            }        } else {            for (i = 0; i < s->output_channels; i++) {                memcpy(s->cur_out[i] + delayed_samples, s->redundancy_output[i], 120 * sizeof(float));                opus_fade(s->cur_out[i] + 120 + delayed_samples,                          s->redundancy_output[i] + 120,                          s->cur_out[i] + 120 + delayed_samples,                          ff_celt_window2, 120);            }        }    }     return samples;} static int opus_decode_subpacket(OpusStreamContext *s,                                 const uint8_t *buf, int buf_size,                                 int nb_samples){    int output_samples = 0;    int flush_needed   = 0;    int i, j, ret;     s->cur_out[0]         = s->out[0];    s->cur_out[1]         = s->out[1];    s->remaining_out_size = s->out_size;     /* check if we need to flush the resampler */    if (swr_is_initialized(s->swr)) {        if (buf) {            int64_t cur_samplerate;            av_opt_get_int(s->swr, "in_sample_rate", 0, &cur_samplerate);            flush_needed = (s->packet.mode == OPUS_MODE_CELT) || (cur_samplerate != s->silk_samplerate);        } else {            flush_needed = !!s->delayed_samples;        }    }     if (!buf && !flush_needed)        return 0;     /* use dummy output buffers if the channel is not mapped to anything */    if (!s->cur_out[0] ||        (s->output_channels == 2 && !s->cur_out[1])) {        av_fast_malloc(&s->out_dummy, &s->out_dummy_allocated_size,                       s->remaining_out_size);        if (!s->out_dummy)            return AVERROR(ENOMEM);        if (!s->cur_out[0])            s->cur_out[0] = s->out_dummy;        if (!s->cur_out[1])            s->cur_out[1] = s->out_dummy;    }     /* flush the resampler if necessary */    if (flush_needed) {        ret = opus_flush_resample(s, s->delayed_samples);        if (ret < 0) {            av_log(s->avctx, AV_LOG_ERROR, "Error flushing the resampler.\n");            return ret;        }        swr_close(s->swr);        output_samples += s->delayed_samples;        s->delayed_samples = 0;         if (!buf)            goto finish;    }     /* decode all the frames in the packet */    for (i = 0; i < s->packet.frame_count; i++) {        int size = s->packet.frame_size[i];        int samples = opus_decode_frame(s, buf + s->packet.frame_offset[i], size);         if (samples < 0) {            av_log(s->avctx, AV_LOG_ERROR, "Error decoding an Opus frame.\n");            if (s->avctx->err_recognition & AV_EF_EXPLODE)                return samples;             for (j = 0; j < s->output_channels; j++)                memset(s->cur_out[j], 0, s->packet.frame_duration * sizeof(float));            samples = s->packet.frame_duration;        }        output_samples += samples;         for (j = 0; j < s->output_channels; j++)            s->cur_out[j] += samples;        s->remaining_out_size -= samples * sizeof(float);    } finish:    s->cur_out[0] = s->cur_out[1] = NULL;    s->remaining_out_size = 0;     return output_samples;} static int opus_decode_packet(AVCodecContext *avctx, AVFrame *frame,                              int *got_frame_ptr, AVPacket *avpkt){    OpusContext *c      = avctx->priv_data;    const uint8_t *buf  = avpkt->data;    int buf_size        = avpkt->size;    int coded_samples   = 0;    int decoded_samples = INT_MAX;    int delayed_samples = 0;    int i, ret;     /* calculate the number of delayed samples */    for (int i = 0; i < c->p.nb_streams; i++) {        OpusStreamContext *s = &c->streams[i];        s->out[0] =        s->out[1] = NULL;        delayed_samples = FFMAX(delayed_samples,                                s->delayed_samples + av_audio_fifo_size(s->sync_buffer));    }     /* decode the header of the first sub-packet to find out the sample count */    if (buf) {        OpusPacket *pkt = &c->streams[0].packet;

Lines 1–500 of 784.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t