Re-land "Create a joint encoder/decoder wrapper for iSAC in ACM"

The change was reverted since it was thought to cause a flaky test.
But the test kept flaking after the change was reverted.

This effectively reverts r6394, relanding r6377.

BUG=3496
TBR=minyue@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/20629004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@6404 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
henrik.lundin@webrtc.org 2014-06-11 14:37:21 +00:00
parent 8454ad1b3e
commit 5b111b06fa
2 changed files with 203 additions and 167 deletions

View File

@ -15,6 +15,7 @@
#include "webrtc/modules/audio_coding/main/acm2/acm_codec_database.h"
#include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h"
#include "webrtc/modules/audio_coding/neteq/interface/audio_decoder.h"
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
#include "webrtc/system_wrappers/interface/trace.h"
#ifdef WEBRTC_CODEC_ISAC
@ -59,14 +60,15 @@ static const int32_t kIsacRatesSwb[NR_ISAC_BANDWIDTHS] = {
#if (!defined(WEBRTC_CODEC_ISAC) && !defined(WEBRTC_CODEC_ISACFX))
ACMISAC::ACMISAC(int16_t /* codec_id */)
: codec_inst_ptr_(NULL),
: codec_inst_crit_sect_(CriticalSectionWrapper::CreateCriticalSection()),
codec_inst_ptr_(NULL),
is_enc_initialized_(false),
isac_coding_mode_(CHANNEL_INDEPENDENT),
enforce_frame_size_(false),
isac_currentBN_(32000),
samples_in10MsAudio_(160), // Initiates to 16 kHz mode.
audio_decoder_(NULL),
decoder_initialized_(false) {}
decoder_initialized_(false) {
}
ACMISAC::~ACMISAC() {
return;
@ -261,81 +263,14 @@ static uint16_t ACMISACFixGetDecSampRate(ACM_ISAC_STRUCT* /* inst */) {
#endif
// Decoder class to be injected into NetEq.
class AcmAudioDecoderIsac : public AudioDecoder {
public:
AcmAudioDecoderIsac(int codec_id, void* state)
: AudioDecoder(ACMCodecDB::neteq_decoders_[codec_id]) {
state_ = state;
}
// ACMISAC is the owner of the object where |state_| is pointing to.
// Therefore, it should not be deleted in this destructor.
virtual ~AcmAudioDecoderIsac() {}
virtual int Decode(const uint8_t* encoded, size_t encoded_len,
int16_t* decoded, SpeechType* speech_type) {
int16_t temp_type;
int ret = ACM_ISAC_DECODE_B(static_cast<ACM_ISAC_STRUCT*>(state_),
reinterpret_cast<const uint16_t*>(encoded),
static_cast<int16_t>(encoded_len), decoded,
&temp_type);
*speech_type = ConvertSpeechType(temp_type);
return ret;
}
virtual bool HasDecodePlc() const { return true; }
virtual int DecodePlc(int num_frames, int16_t* decoded) {
return ACM_ISAC_DECODEPLC(static_cast<ACM_ISAC_STRUCT*>(state_),
decoded, static_cast<int16_t>(num_frames));
}
virtual int Init() {
return 0; // We expect that the initialized instance is injected in the
// constructor.
}
virtual int IncomingPacket(const uint8_t* payload,
size_t payload_len,
uint16_t rtp_sequence_number,
uint32_t rtp_timestamp,
uint32_t arrival_timestamp) {
return ACM_ISAC_DECODE_BWE(static_cast<ACM_ISAC_STRUCT*>(state_),
reinterpret_cast<const uint16_t*>(payload),
static_cast<uint32_t>(payload_len),
rtp_sequence_number,
rtp_timestamp,
arrival_timestamp);
}
virtual int DecodeRedundant(const uint8_t* encoded,
size_t encoded_len, int16_t* decoded,
SpeechType* speech_type) {
int16_t temp_type = 1; // Default is speech.
int16_t ret = ACM_ISAC_DECODERCU(static_cast<ACM_ISAC_STRUCT*>(state_),
reinterpret_cast<const uint16_t*>(encoded),
static_cast<int16_t>(encoded_len), decoded,
&temp_type);
*speech_type = ConvertSpeechType(temp_type);
return ret;
}
virtual int ErrorCode() {
return ACM_ISAC_GETERRORCODE(static_cast<ACM_ISAC_STRUCT*>(state_));
}
private:
DISALLOW_COPY_AND_ASSIGN(AcmAudioDecoderIsac);
};
ACMISAC::ACMISAC(int16_t codec_id)
: is_enc_initialized_(false),
: AudioDecoder(ACMCodecDB::neteq_decoders_[codec_id]),
codec_inst_crit_sect_(CriticalSectionWrapper::CreateCriticalSection()),
is_enc_initialized_(false),
isac_coding_mode_(CHANNEL_INDEPENDENT),
enforce_frame_size_(false),
isac_current_bn_(32000),
samples_in_10ms_audio_(160), // Initiates to 16 kHz mode.
audio_decoder_(NULL),
decoder_initialized_(false) {
codec_id_ = codec_id;
@ -345,14 +280,10 @@ ACMISAC::ACMISAC(int16_t codec_id)
return;
}
codec_inst_ptr_->inst = NULL;
state_ = codec_inst_ptr_;
}
ACMISAC::~ACMISAC() {
if (audio_decoder_ != NULL) {
delete audio_decoder_;
audio_decoder_ = NULL;
}
if (codec_inst_ptr_ != NULL) {
if (codec_inst_ptr_->inst != NULL) {
ACM_ISAC_FREE(codec_inst_ptr_->inst);
@ -364,6 +295,34 @@ ACMISAC::~ACMISAC() {
return;
}
int16_t ACMISAC::InternalInitDecoder(WebRtcACMCodecParams* codec_params) {
// set decoder sampling frequency.
if (codec_params->codec_inst.plfreq == 32000 ||
codec_params->codec_inst.plfreq == 48000) {
UpdateDecoderSampFreq(ACMCodecDB::kISACSWB);
} else {
UpdateDecoderSampFreq(ACMCodecDB::kISAC);
}
// in a one-way communication we may never register send-codec.
// However we like that the BWE to work properly so it has to
// be initialized. The BWE is initialized when iSAC encoder is initialized.
// Therefore, we need this.
if (!encoder_initialized_) {
// Since we don't require a valid rate or a valid packet size when
// initializing the decoder, we set valid values before initializing encoder
codec_params->codec_inst.rate = kIsacWbDefaultRate;
codec_params->codec_inst.pacsize = kIsacPacSize960;
if (InternalInitEncoder(codec_params) < 0) {
return -1;
}
encoder_initialized_ = true;
}
CriticalSectionScoped lock(codec_inst_crit_sect_.get());
return ACM_ISAC_DECODERINIT(codec_inst_ptr_->inst);
}
ACMGenericCodec* ACMISAC::CreateInstance(void) { return NULL; }
int16_t ACMISAC::InternalEncode(uint8_t* bitstream,
@ -375,6 +334,7 @@ int16_t ACMISAC::InternalEncode(uint8_t* bitstream,
// at the first 10ms pushed in to iSAC if the bit-rate is low, this is
// sort of a bug in iSAC. to address this we treat iSAC as the
// following.
CriticalSectionScoped lock(codec_inst_crit_sect_.get());
if (codec_inst_ptr_ == NULL) {
return -1;
}
@ -428,6 +388,7 @@ int16_t ACMISAC::InternalInitEncoder(WebRtcACMCodecParams* codec_params) {
if (UpdateEncoderSampFreq((uint16_t)codec_params->codec_inst.plfreq) < 0) {
return -1;
}
CriticalSectionScoped lock(codec_inst_crit_sect_.get());
if (ACM_ISAC_ENCODERINIT(codec_inst_ptr_->inst, isac_coding_mode_) < 0) {
return -1;
}
@ -450,38 +411,8 @@ int16_t ACMISAC::InternalInitEncoder(WebRtcACMCodecParams* codec_params) {
return 0;
}
int16_t ACMISAC::InternalInitDecoder(WebRtcACMCodecParams* codec_params) {
if (codec_inst_ptr_ == NULL) {
return -1;
}
// set decoder sampling frequency.
if (codec_params->codec_inst.plfreq == 32000 ||
codec_params->codec_inst.plfreq == 48000) {
UpdateDecoderSampFreq(ACMCodecDB::kISACSWB);
} else {
UpdateDecoderSampFreq(ACMCodecDB::kISAC);
}
// in a one-way communication we may never register send-codec.
// However we like that the BWE to work properly so it has to
// be initialized. The BWE is initialized when iSAC encoder is initialized.
// Therefore, we need this.
if (!encoder_initialized_) {
// Since we don't require a valid rate or a valid packet size when
// initializing the decoder, we set valid values before initializing encoder
codec_params->codec_inst.rate = kIsacWbDefaultRate;
codec_params->codec_inst.pacsize = kIsacPacSize960;
if (InternalInitEncoder(codec_params) < 0) {
return -1;
}
encoder_initialized_ = true;
}
return ACM_ISAC_DECODERINIT(codec_inst_ptr_->inst);
}
int16_t ACMISAC::InternalCreateEncoder() {
CriticalSectionScoped lock(codec_inst_crit_sect_.get());
if (codec_inst_ptr_ == NULL) {
return -1;
}
@ -493,19 +424,6 @@ int16_t ACMISAC::InternalCreateEncoder() {
return status;
}
void ACMISAC::DestructEncoderSafe() {
// codec with shared instance cannot delete.
encoder_initialized_ = false;
return;
}
void ACMISAC::InternalDestructEncoderInst(void* ptr_inst) {
if (ptr_inst != NULL) {
ACM_ISAC_FREE(static_cast<ACM_ISAC_STRUCT *>(ptr_inst));
}
return;
}
int16_t ACMISAC::Transcode(uint8_t* bitstream,
int16_t* bitstream_len_byte,
int16_t q_bwe,
@ -513,6 +431,7 @@ int16_t ACMISAC::Transcode(uint8_t* bitstream,
bool is_red) {
int16_t jitter_info = 0;
// transcode from a higher rate to lower rate sanity check
CriticalSectionScoped lock(codec_inst_crit_sect_.get());
if (codec_inst_ptr_ == NULL) {
return -1;
}
@ -530,7 +449,27 @@ int16_t ACMISAC::Transcode(uint8_t* bitstream,
}
}
void ACMISAC::UpdateFrameLen() {
CriticalSectionScoped lock(codec_inst_crit_sect_.get());
frame_len_smpl_ = ACM_ISAC_GETNEWFRAMELEN(codec_inst_ptr_->inst);
encoder_params_.codec_inst.pacsize = frame_len_smpl_;
}
void ACMISAC::DestructEncoderSafe() {
// codec with shared instance cannot delete.
encoder_initialized_ = false;
return;
}
void ACMISAC::InternalDestructEncoderInst(void* ptr_inst) {
if (ptr_inst != NULL) {
ACM_ISAC_FREE(static_cast<ACM_ISAC_STRUCT *>(ptr_inst));
}
return;
}
int16_t ACMISAC::SetBitRateSafe(int32_t bit_rate) {
CriticalSectionScoped lock(codec_inst_crit_sect_.get());
if (codec_inst_ptr_ == NULL) {
return -1;
}
@ -594,6 +533,7 @@ int32_t ACMISAC::GetEstimatedBandwidthSafe() {
int samp_rate;
// Get bandwidth information
CriticalSectionScoped lock(codec_inst_crit_sect_.get());
ACM_ISAC_GETSENDBWE(codec_inst_ptr_->inst, &bandwidth_index, &delay_index);
// Validy check of index
@ -615,6 +555,7 @@ int32_t ACMISAC::SetEstimatedBandwidthSafe(int32_t estimated_bandwidth) {
int16_t bandwidth_index;
// Check sample frequency and choose appropriate table
CriticalSectionScoped lock(codec_inst_crit_sect_.get());
samp_rate = ACM_ISAC_GETENCSAMPRATE(codec_inst_ptr_->inst);
if (samp_rate == 16000) {
@ -657,6 +598,7 @@ int32_t ACMISAC::GetRedPayloadSafe(
return -1;
#else
uint8_t* red_payload, int16_t* payload_bytes) {
CriticalSectionScoped lock(codec_inst_crit_sect_.get());
int16_t bytes =
WebRtcIsac_GetRedPayload(
codec_inst_ptr_->inst, reinterpret_cast<int16_t*>(red_payload));
@ -672,6 +614,7 @@ int16_t ACMISAC::UpdateDecoderSampFreq(
#ifdef WEBRTC_CODEC_ISAC
int16_t codec_id) {
// The decoder supports only wideband and super-wideband.
CriticalSectionScoped lock(codec_inst_crit_sect_.get());
if (ACMCodecDB::kISAC == codec_id) {
return WebRtcIsac_SetDecSampRate(codec_inst_ptr_->inst, 16000);
} else if (ACMCodecDB::kISACSWB == codec_id ||
@ -700,6 +643,7 @@ int16_t ACMISAC::UpdateEncoderSampFreq(
in_audio_ix_read_ = 0;
in_audio_ix_write_ = 0;
in_timestamp_ix_write_ = 0;
CriticalSectionScoped lock(codec_inst_crit_sect_.get());
if (WebRtcIsac_SetEncSampRate(codec_inst_ptr_->inst,
encoder_samp_freq_hz) < 0) {
return -1;
@ -718,6 +662,7 @@ int16_t ACMISAC::UpdateEncoderSampFreq(
}
int16_t ACMISAC::EncoderSampFreq(uint16_t* samp_freq_hz) {
CriticalSectionScoped lock(codec_inst_crit_sect_.get());
*samp_freq_hz = ACM_ISAC_GETENCSAMPRATE(codec_inst_ptr_->inst);
return 0;
}
@ -730,6 +675,7 @@ int32_t ACMISAC::ConfigISACBandwidthEstimator(
{
uint16_t samp_freq_hz;
EncoderSampFreq(&samp_freq_hz);
CriticalSectionScoped lock(codec_inst_crit_sect_.get());
// TODO(turajs): at 32kHz we hardcode calling with 30ms and enforce
// the frame-size otherwise we might get error. Revise if
// control-bwe is changed.
@ -749,26 +695,25 @@ int32_t ACMISAC::ConfigISACBandwidthEstimator(
return -1;
}
UpdateFrameLen();
CriticalSectionScoped lock(codec_inst_crit_sect_.get());
ACM_ISAC_GETSENDBITRATE(codec_inst_ptr_->inst, &isac_current_bn_);
return 0;
}
int32_t ACMISAC::SetISACMaxPayloadSize(const uint16_t max_payload_len_bytes) {
CriticalSectionScoped lock(codec_inst_crit_sect_.get());
return ACM_ISAC_SETMAXPAYLOADSIZE(codec_inst_ptr_->inst,
max_payload_len_bytes);
}
int32_t ACMISAC::SetISACMaxRate(const uint32_t max_rate_bit_per_sec) {
CriticalSectionScoped lock(codec_inst_crit_sect_.get());
return ACM_ISAC_SETMAXRATE(codec_inst_ptr_->inst, max_rate_bit_per_sec);
}
void ACMISAC::UpdateFrameLen() {
frame_len_smpl_ = ACM_ISAC_GETNEWFRAMELEN(codec_inst_ptr_->inst);
encoder_params_.codec_inst.pacsize = frame_len_smpl_;
}
void ACMISAC::CurrentRate(int32_t* rate_bit_per_sec) {
if (isac_coding_mode_ == ADAPTIVE) {
CriticalSectionScoped lock(codec_inst_crit_sect_.get());
ACM_ISAC_GETSENDBITRATE(codec_inst_ptr_->inst, rate_bit_per_sec);
}
}
@ -784,12 +729,71 @@ int16_t ACMISAC::REDPayloadISAC(const int32_t isac_rate,
return status;
}
AudioDecoder* ACMISAC::Decoder(int codec_id) {
if (audio_decoder_)
return audio_decoder_;
int ACMISAC::Decode(const uint8_t* encoded,
size_t encoded_len,
int16_t* decoded,
SpeechType* speech_type) {
int16_t temp_type;
CriticalSectionScoped lock(codec_inst_crit_sect_.get());
int ret =
ACM_ISAC_DECODE_B(static_cast<ACM_ISAC_STRUCT*>(codec_inst_ptr_->inst),
reinterpret_cast<const uint16_t*>(encoded),
static_cast<int16_t>(encoded_len),
decoded,
&temp_type);
*speech_type = ConvertSpeechType(temp_type);
return ret;
}
int ACMISAC::DecodePlc(int num_frames, int16_t* decoded) {
CriticalSectionScoped lock(codec_inst_crit_sect_.get());
return ACM_ISAC_DECODEPLC(
static_cast<ACM_ISAC_STRUCT*>(codec_inst_ptr_->inst),
decoded,
static_cast<int16_t>(num_frames));
}
int ACMISAC::IncomingPacket(const uint8_t* payload,
size_t payload_len,
uint16_t rtp_sequence_number,
uint32_t rtp_timestamp,
uint32_t arrival_timestamp) {
CriticalSectionScoped lock(codec_inst_crit_sect_.get());
return ACM_ISAC_DECODE_BWE(
static_cast<ACM_ISAC_STRUCT*>(codec_inst_ptr_->inst),
reinterpret_cast<const uint16_t*>(payload),
static_cast<uint32_t>(payload_len),
rtp_sequence_number,
rtp_timestamp,
arrival_timestamp);
}
int ACMISAC::DecodeRedundant(const uint8_t* encoded,
size_t encoded_len,
int16_t* decoded,
SpeechType* speech_type) {
int16_t temp_type = 1; // Default is speech.
CriticalSectionScoped lock(codec_inst_crit_sect_.get());
int16_t ret =
ACM_ISAC_DECODERCU(static_cast<ACM_ISAC_STRUCT*>(codec_inst_ptr_->inst),
reinterpret_cast<const uint16_t*>(encoded),
static_cast<int16_t>(encoded_len),
decoded,
&temp_type);
*speech_type = ConvertSpeechType(temp_type);
return ret;
}
int ACMISAC::ErrorCode() {
CriticalSectionScoped lock(codec_inst_crit_sect_.get());
return ACM_ISAC_GETERRORCODE(
static_cast<ACM_ISAC_STRUCT*>(codec_inst_ptr_->inst));
}
AudioDecoder* ACMISAC::Decoder(int codec_id) {
// Create iSAC instance if it does not exist.
if (!encoder_exist_) {
CriticalSectionScoped lock(codec_inst_crit_sect_.get());
assert(codec_inst_ptr_->inst == NULL);
encoder_initialized_ = false;
decoder_initialized_ = false;
@ -822,8 +826,7 @@ AudioDecoder* ACMISAC::Decoder(int codec_id) {
decoder_initialized_ = true;
}
audio_decoder_ = new AcmAudioDecoderIsac(codec_id, codec_inst_ptr_->inst);
return audio_decoder_;
return this;
}
#endif

View File

@ -12,86 +12,119 @@
#define WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_ACM_ISAC_H_
#include "webrtc/modules/audio_coding/main/acm2/acm_generic_codec.h"
#include "webrtc/modules/audio_coding/neteq/interface/audio_decoder.h"
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
#include "webrtc/system_wrappers/interface/thread_annotations.h"
namespace webrtc {
class CriticalSectionWrapper;
namespace acm2 {
struct ACMISACInst;
class AcmAudioDecoderIsac;
enum IsacCodingMode {
ADAPTIVE,
CHANNEL_INDEPENDENT
};
class ACMISAC : public ACMGenericCodec {
class ACMISAC : public ACMGenericCodec, AudioDecoder {
public:
explicit ACMISAC(int16_t codec_id);
~ACMISAC();
// for FEC
ACMGenericCodec* CreateInstance(void);
int16_t InternalEncode(uint8_t* bitstream, int16_t* bitstream_len_byte);
int16_t InternalInitEncoder(WebRtcACMCodecParams* codec_params);
int16_t InternalInitDecoder(WebRtcACMCodecParams* codec_params);
int16_t UpdateDecoderSampFreq(int16_t codec_id);
// Methods below are inherited from ACMGenericCodec.
ACMGenericCodec* CreateInstance(void) OVERRIDE;
int16_t UpdateEncoderSampFreq(uint16_t samp_freq_hz);
int16_t InternalEncode(uint8_t* bitstream,
int16_t* bitstream_len_byte) OVERRIDE;
int16_t EncoderSampFreq(uint16_t* samp_freq_hz);
int16_t InternalInitEncoder(WebRtcACMCodecParams* codec_params) OVERRIDE;
int16_t UpdateDecoderSampFreq(int16_t codec_id) OVERRIDE;
int16_t UpdateEncoderSampFreq(uint16_t samp_freq_hz) OVERRIDE;
int16_t EncoderSampFreq(uint16_t* samp_freq_hz) OVERRIDE;
int32_t ConfigISACBandwidthEstimator(const uint8_t init_frame_size_msec,
const uint16_t init_rate_bit_per_sec,
const bool enforce_frame_size);
const bool enforce_frame_size) OVERRIDE;
int32_t SetISACMaxPayloadSize(const uint16_t max_payload_len_bytes);
int32_t SetISACMaxPayloadSize(const uint16_t max_payload_len_bytes) OVERRIDE;
int32_t SetISACMaxRate(const uint32_t max_rate_bit_per_sec);
int32_t SetISACMaxRate(const uint32_t max_rate_bit_per_sec) OVERRIDE;
int16_t REDPayloadISAC(const int32_t isac_rate,
const int16_t isac_bw_estimate,
uint8_t* payload,
int16_t* payload_len_bytes);
int16_t* payload_len_bytes) OVERRIDE;
// Methods below are inherited from AudioDecoder.
virtual int Decode(const uint8_t* encoded,
size_t encoded_len,
int16_t* decoded,
SpeechType* speech_type) OVERRIDE;
virtual bool HasDecodePlc() const OVERRIDE { return true; }
virtual int DecodePlc(int num_frames, int16_t* decoded) OVERRIDE;
virtual int Init() OVERRIDE { return 0; }
virtual int IncomingPacket(const uint8_t* payload,
size_t payload_len,
uint16_t rtp_sequence_number,
uint32_t rtp_timestamp,
uint32_t arrival_timestamp) OVERRIDE;
virtual int DecodeRedundant(const uint8_t* encoded,
size_t encoded_len,
int16_t* decoded,
SpeechType* speech_type) OVERRIDE;
virtual int ErrorCode() OVERRIDE;
protected:
void DestructEncoderSafe();
int16_t SetBitRateSafe(const int32_t bit_rate);
int32_t GetEstimatedBandwidthSafe();
int32_t SetEstimatedBandwidthSafe(int32_t estimated_bandwidth);
int32_t GetRedPayloadSafe(uint8_t* red_payload, int16_t* payload_bytes);
int16_t InternalCreateEncoder();
void InternalDestructEncoderInst(void* ptr_inst);
int16_t Transcode(uint8_t* bitstream,
int16_t* bitstream_len_byte,
int16_t q_bwe,
int32_t rate,
bool is_red);
void CurrentRate(int32_t* rate_bit_per_sec);
void UpdateFrameLen();
virtual AudioDecoder* Decoder(int codec_id);
// Methods below are inherited from ACMGenericCodec.
void DestructEncoderSafe() OVERRIDE;
ACMISACInst* codec_inst_ptr_;
int16_t SetBitRateSafe(const int32_t bit_rate) OVERRIDE;
int32_t GetEstimatedBandwidthSafe() OVERRIDE;
int32_t SetEstimatedBandwidthSafe(int32_t estimated_bandwidth) OVERRIDE;
int32_t GetRedPayloadSafe(uint8_t* red_payload,
int16_t* payload_bytes) OVERRIDE;
int16_t InternalCreateEncoder() OVERRIDE;
void InternalDestructEncoderInst(void* ptr_inst) OVERRIDE;
void CurrentRate(int32_t* rate_bit_per_sec) OVERRIDE;
virtual AudioDecoder* Decoder(int codec_id) OVERRIDE;
// |codec_inst_crit_sect_| protects |codec_inst_ptr_|.
const scoped_ptr<CriticalSectionWrapper> codec_inst_crit_sect_;
ACMISACInst* codec_inst_ptr_ GUARDED_BY(codec_inst_crit_sect_);
bool is_enc_initialized_;
IsacCodingMode isac_coding_mode_;
bool enforce_frame_size_;
int32_t isac_current_bn_;
uint16_t samples_in_10ms_audio_;
AcmAudioDecoderIsac* audio_decoder_;
bool decoder_initialized_;
};