blob: 5b940aead2fb97b8aded2d40ddf88976adccb87b [file] [log] [blame]
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +00001/*
2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "modules/audio_coding/neteq/decoder_database.h"
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +000012
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +000013#include <utility> // pair
14
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020015#include "api/audio_codecs/audio_decoder.h"
16#include "rtc_base/checks.h"
17#include "rtc_base/logging.h"
Jonas Olssonabbe8412018-04-03 13:40:05 +020018#include "rtc_base/strings/audio_format_to_string.h"
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +000019
20namespace webrtc {
21
kwiberg5178ee82016-05-03 01:39:01 -070022DecoderDatabase::DecoderDatabase(
Karl Wiberg08126342018-03-20 19:18:55 +010023 const rtc::scoped_refptr<AudioDecoderFactory>& decoder_factory,
24 rtc::Optional<AudioCodecPairId> codec_pair_id)
kwiberg5178ee82016-05-03 01:39:01 -070025 : active_decoder_type_(-1),
26 active_cng_decoder_type_(-1),
Karl Wiberg08126342018-03-20 19:18:55 +010027 decoder_factory_(decoder_factory),
28 codec_pair_id_(codec_pair_id) {}
pbos@webrtc.org2d1a55c2013-07-31 15:54:00 +000029
ossu97ba30e2016-04-25 07:55:58 -070030DecoderDatabase::~DecoderDatabase() = default;
pbos@webrtc.org2d1a55c2013-07-31 15:54:00 +000031
Karl Wiberg08126342018-03-20 19:18:55 +010032DecoderDatabase::DecoderInfo::DecoderInfo(
33 const SdpAudioFormat& audio_format,
34 rtc::Optional<AudioCodecPairId> codec_pair_id,
35 AudioDecoderFactory* factory,
36 const std::string& codec_name)
kwiberge9413062016-11-03 05:29:05 -070037 : name_(codec_name),
38 audio_format_(audio_format),
Karl Wiberg08126342018-03-20 19:18:55 +010039 codec_pair_id_(codec_pair_id),
ossu84bc9852016-08-26 05:41:23 -070040 factory_(factory),
kwiberg342f7402016-06-16 03:18:00 -070041 external_decoder_(nullptr),
ossu9f38c212016-10-04 05:23:32 -070042 cng_decoder_(CngDecoder::Create(audio_format)),
43 subtype_(SubtypeFromFormat(audio_format)) {}
kwibergc0f2dcf2016-05-31 06:28:03 -070044
Karl Wiberg08126342018-03-20 19:18:55 +010045DecoderDatabase::DecoderInfo::DecoderInfo(
46 const SdpAudioFormat& audio_format,
47 rtc::Optional<AudioCodecPairId> codec_pair_id,
48 AudioDecoderFactory* factory)
49 : DecoderInfo(audio_format, codec_pair_id, factory, audio_format.name) {}
kwiberge9413062016-11-03 05:29:05 -070050
Karl Wiberg08126342018-03-20 19:18:55 +010051DecoderDatabase::DecoderInfo::DecoderInfo(
52 NetEqDecoder ct,
53 rtc::Optional<AudioCodecPairId> codec_pair_id,
54 AudioDecoderFactory* factory)
55 : DecoderInfo(*NetEqDecoderToSdpAudioFormat(ct), codec_pair_id, factory) {}
ossuf1b08da2016-09-23 02:19:43 -070056
57DecoderDatabase::DecoderInfo::DecoderInfo(const SdpAudioFormat& audio_format,
kwiberge9413062016-11-03 05:29:05 -070058 AudioDecoder* ext_dec,
59 const std::string& codec_name)
60 : name_(codec_name),
61 audio_format_(audio_format),
Karl Wiberg08126342018-03-20 19:18:55 +010062 codec_pair_id_(rtc::nullopt),
ossuf1b08da2016-09-23 02:19:43 -070063 factory_(nullptr),
ossu9f38c212016-10-04 05:23:32 -070064 external_decoder_(ext_dec),
65 subtype_(Subtype::kNormal) {
kwibergc0f2dcf2016-05-31 06:28:03 -070066 RTC_CHECK(ext_dec);
67}
kwiberg0fa0a972016-04-19 05:03:45 -070068
69DecoderDatabase::DecoderInfo::DecoderInfo(DecoderInfo&&) = default;
70DecoderDatabase::DecoderInfo::~DecoderInfo() = default;
71
Karl Wiberg31fbb542017-10-16 12:42:38 +020072bool DecoderDatabase::DecoderInfo::CanGetDecoder() const {
73 if (subtype_ == Subtype::kNormal && !external_decoder_ && !decoder_) {
74 // TODO(ossu): Keep a check here for now, since a number of tests create
75 // DecoderInfos without factories.
76 RTC_DCHECK(factory_);
77 return factory_->IsSupportedDecoder(audio_format_);
78 } else {
79 return true;
80 }
81}
82
ossu84bc9852016-08-26 05:41:23 -070083AudioDecoder* DecoderDatabase::DecoderInfo::GetDecoder() const {
ossu9f38c212016-10-04 05:23:32 -070084 if (subtype_ != Subtype::kNormal) {
ossuf1b08da2016-09-23 02:19:43 -070085 // These are handled internally, so they have no AudioDecoder objects.
86 return nullptr;
87 }
kwiberg342f7402016-06-16 03:18:00 -070088 if (external_decoder_) {
kwiberg0fa0a972016-04-19 05:03:45 -070089 RTC_DCHECK(!decoder_);
kwiberg342f7402016-06-16 03:18:00 -070090 RTC_DCHECK(!cng_decoder_);
91 return external_decoder_;
kwiberg0fa0a972016-04-19 05:03:45 -070092 }
93 if (!decoder_) {
ossuf1b08da2016-09-23 02:19:43 -070094 // TODO(ossu): Keep a check here for now, since a number of tests create
95 // DecoderInfos without factories.
ossu84bc9852016-08-26 05:41:23 -070096 RTC_DCHECK(factory_);
Karl Wiberg08126342018-03-20 19:18:55 +010097 decoder_ = factory_->MakeAudioDecoder(audio_format_, codec_pair_id_);
kwiberg0fa0a972016-04-19 05:03:45 -070098 }
Jonas Olssonabbe8412018-04-03 13:40:05 +020099 RTC_DCHECK(decoder_) << "Failed to create: " << rtc::ToString(audio_format_);
kwiberg0fa0a972016-04-19 05:03:45 -0700100 return decoder_.get();
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000101}
102
ossuf1b08da2016-09-23 02:19:43 -0700103bool DecoderDatabase::DecoderInfo::IsType(const char* name) const {
104 return STR_CASE_CMP(audio_format_.name.c_str(), name) == 0;
105}
106
107bool DecoderDatabase::DecoderInfo::IsType(const std::string& name) const {
108 return IsType(name.c_str());
ossu84bc9852016-08-26 05:41:23 -0700109}
110
kwibergc0f2dcf2016-05-31 06:28:03 -0700111rtc::Optional<DecoderDatabase::DecoderInfo::CngDecoder>
ossuf1b08da2016-09-23 02:19:43 -0700112DecoderDatabase::DecoderInfo::CngDecoder::Create(const SdpAudioFormat& format) {
113 if (STR_CASE_CMP(format.name.c_str(), "CN") == 0) {
kwiberg5adaf732016-10-04 09:33:27 -0700114 // CN has a 1:1 RTP clock rate to sample rate ratio.
115 const int sample_rate_hz = format.clockrate_hz;
116 RTC_DCHECK(sample_rate_hz == 8000 || sample_rate_hz == 16000 ||
117 sample_rate_hz == 32000 || sample_rate_hz == 48000);
Oskar Sundbom12ab00b2017-11-16 15:31:38 +0100118 return DecoderDatabase::DecoderInfo::CngDecoder{sample_rate_hz};
ossuf1b08da2016-09-23 02:19:43 -0700119 } else {
Oskar Sundbom12ab00b2017-11-16 15:31:38 +0100120 return rtc::nullopt;
kwibergc0f2dcf2016-05-31 06:28:03 -0700121 }
122}
123
ossu9f38c212016-10-04 05:23:32 -0700124DecoderDatabase::DecoderInfo::Subtype
125DecoderDatabase::DecoderInfo::SubtypeFromFormat(const SdpAudioFormat& format) {
126 if (STR_CASE_CMP(format.name.c_str(), "CN") == 0) {
127 return Subtype::kComfortNoise;
128 } else if (STR_CASE_CMP(format.name.c_str(), "telephone-event") == 0) {
129 return Subtype::kDtmf;
130 } else if (STR_CASE_CMP(format.name.c_str(), "red") == 0) {
131 return Subtype::kRed;
132 }
133
134 return Subtype::kNormal;
135}
136
pbos@webrtc.org2d1a55c2013-07-31 15:54:00 +0000137bool DecoderDatabase::Empty() const { return decoders_.empty(); }
138
turaj@webrtc.org362a55e2013-09-20 16:25:28 +0000139int DecoderDatabase::Size() const { return static_cast<int>(decoders_.size()); }
pbos@webrtc.org2d1a55c2013-07-31 15:54:00 +0000140
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000141void DecoderDatabase::Reset() {
142 decoders_.clear();
ossu97ba30e2016-04-25 07:55:58 -0700143 active_decoder_type_ = -1;
144 active_cng_decoder_type_ = -1;
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000145}
146
kwiberg1c07c702017-03-27 07:15:49 -0700147std::vector<int> DecoderDatabase::SetCodecs(
148 const std::map<int, SdpAudioFormat>& codecs) {
149 // First collect all payload types that we'll remove or reassign, then remove
150 // them from the database.
151 std::vector<int> changed_payload_types;
152 for (const std::pair<uint8_t, const DecoderInfo&> kv : decoders_) {
153 auto i = codecs.find(kv.first);
154 if (i == codecs.end() || i->second != kv.second.GetFormat()) {
155 changed_payload_types.push_back(kv.first);
156 }
157 }
158 for (int pl_type : changed_payload_types) {
159 Remove(pl_type);
160 }
161
162 // Enter the new and changed payload type mappings into the database.
163 for (const auto& kv : codecs) {
164 const int& rtp_payload_type = kv.first;
165 const SdpAudioFormat& audio_format = kv.second;
166 RTC_DCHECK_GE(rtp_payload_type, 0);
167 RTC_DCHECK_LE(rtp_payload_type, 0x7f);
168 if (decoders_.count(rtp_payload_type) == 0) {
169 decoders_.insert(std::make_pair(
Karl Wiberg08126342018-03-20 19:18:55 +0100170 rtp_payload_type,
171 DecoderInfo(audio_format, codec_pair_id_, decoder_factory_.get())));
kwiberg1c07c702017-03-27 07:15:49 -0700172 } else {
173 // The mapping for this payload type hasn't changed.
174 }
175 }
176
177 return changed_payload_types;
178}
179
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000180int DecoderDatabase::RegisterPayload(uint8_t rtp_payload_type,
henrik.lundin4cf61dd2015-12-09 06:20:58 -0800181 NetEqDecoder codec_type,
182 const std::string& name) {
pkasting@chromium.orgd3245462015-02-23 21:28:22 +0000183 if (rtp_payload_type > 0x7F) {
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000184 return kInvalidRtpPayloadType;
185 }
Karl Wiberg31fbb542017-10-16 12:42:38 +0200186 if (codec_type == NetEqDecoder::kDecoderArbitrary) {
187 return kCodecNotSupported; // Only supported through InsertExternal.
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000188 }
kwiberg65cb70d2017-03-03 06:16:28 -0800189 const auto opt_format = NetEqDecoderToSdpAudioFormat(codec_type);
ossuf1b08da2016-09-23 02:19:43 -0700190 if (!opt_format) {
191 return kCodecNotSupported;
192 }
Karl Wiberg08126342018-03-20 19:18:55 +0100193 DecoderInfo info(*opt_format, codec_pair_id_, decoder_factory_, name);
Karl Wiberg31fbb542017-10-16 12:42:38 +0200194 if (!info.CanGetDecoder()) {
195 return kCodecNotSupported;
196 }
kwiberg0fa0a972016-04-19 05:03:45 -0700197 auto ret =
198 decoders_.insert(std::make_pair(rtp_payload_type, std::move(info)));
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000199 if (ret.second == false) {
200 // Database already contains a decoder with type |rtp_payload_type|.
201 return kDecoderExists;
202 }
203 return kOK;
204}
205
kwiberg5adaf732016-10-04 09:33:27 -0700206int DecoderDatabase::RegisterPayload(int rtp_payload_type,
207 const SdpAudioFormat& audio_format) {
208 if (rtp_payload_type < 0 || rtp_payload_type > 0x7f) {
209 return kInvalidRtpPayloadType;
210 }
211 const auto ret = decoders_.insert(std::make_pair(
Karl Wiberg08126342018-03-20 19:18:55 +0100212 rtp_payload_type,
213 DecoderInfo(audio_format, codec_pair_id_, decoder_factory_.get())));
kwiberg5adaf732016-10-04 09:33:27 -0700214 if (ret.second == false) {
215 // Database already contains a decoder with type |rtp_payload_type|.
216 return kDecoderExists;
217 }
218 return kOK;
219}
220
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000221int DecoderDatabase::InsertExternal(uint8_t rtp_payload_type,
222 NetEqDecoder codec_type,
henrik.lundin4cf61dd2015-12-09 06:20:58 -0800223 const std::string& codec_name,
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000224 AudioDecoder* decoder) {
225 if (rtp_payload_type > 0x7F) {
226 return kInvalidRtpPayloadType;
227 }
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000228 if (!decoder) {
229 return kInvalidPointer;
230 }
ossuf1b08da2016-09-23 02:19:43 -0700231
kwiberg65cb70d2017-03-03 06:16:28 -0800232 const auto opt_db_format = NetEqDecoderToSdpAudioFormat(codec_type);
Danil Chapovalov8aba6b42018-04-17 10:10:57 +0200233 const SdpAudioFormat format =
234 opt_db_format.value_or(SdpAudioFormat("arbitrary", 0, 0));
ossuf1b08da2016-09-23 02:19:43 -0700235
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000236 std::pair<DecoderMap::iterator, bool> ret;
kwiberge9413062016-11-03 05:29:05 -0700237 DecoderInfo info(format, decoder, codec_name);
kwiberg0fa0a972016-04-19 05:03:45 -0700238 ret = decoders_.insert(std::make_pair(rtp_payload_type, std::move(info)));
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000239 if (ret.second == false) {
240 // Database already contains a decoder with type |rtp_payload_type|.
241 return kDecoderExists;
242 }
243 return kOK;
244}
245
246int DecoderDatabase::Remove(uint8_t rtp_payload_type) {
247 if (decoders_.erase(rtp_payload_type) == 0) {
248 // No decoder with that |rtp_payload_type|.
249 return kDecoderNotFound;
250 }
ossu97ba30e2016-04-25 07:55:58 -0700251 if (active_decoder_type_ == rtp_payload_type) {
252 active_decoder_type_ = -1; // No active decoder.
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000253 }
ossu97ba30e2016-04-25 07:55:58 -0700254 if (active_cng_decoder_type_ == rtp_payload_type) {
255 active_cng_decoder_type_ = -1; // No active CNG decoder.
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000256 }
257 return kOK;
258}
259
kwiberg6b19b562016-09-20 04:02:25 -0700260void DecoderDatabase::RemoveAll() {
261 decoders_.clear();
262 active_decoder_type_ = -1; // No active decoder.
263 active_cng_decoder_type_ = -1; // No active CNG decoder.
264}
265
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000266const DecoderDatabase::DecoderInfo* DecoderDatabase::GetDecoderInfo(
267 uint8_t rtp_payload_type) const {
268 DecoderMap::const_iterator it = decoders_.find(rtp_payload_type);
269 if (it == decoders_.end()) {
270 // Decoder not found.
271 return NULL;
272 }
ossuf1b08da2016-09-23 02:19:43 -0700273 return &it->second;
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000274}
275
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000276int DecoderDatabase::SetActiveDecoder(uint8_t rtp_payload_type,
277 bool* new_decoder) {
278 // Check that |rtp_payload_type| exists in the database.
ossu84bc9852016-08-26 05:41:23 -0700279 const DecoderInfo *info = GetDecoderInfo(rtp_payload_type);
280 if (!info) {
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000281 // Decoder not found.
282 return kDecoderNotFound;
283 }
ossu84bc9852016-08-26 05:41:23 -0700284 RTC_CHECK(!info->IsComfortNoise());
285 RTC_DCHECK(new_decoder);
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000286 *new_decoder = false;
ossu97ba30e2016-04-25 07:55:58 -0700287 if (active_decoder_type_ < 0) {
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000288 // This is the first active decoder.
289 *new_decoder = true;
ossu97ba30e2016-04-25 07:55:58 -0700290 } else if (active_decoder_type_ != rtp_payload_type) {
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000291 // Moving from one active decoder to another. Delete the first one.
ossu84bc9852016-08-26 05:41:23 -0700292 const DecoderInfo *old_info = GetDecoderInfo(active_decoder_type_);
293 RTC_DCHECK(old_info);
294 old_info->DropDecoder();
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000295 *new_decoder = true;
296 }
ossu97ba30e2016-04-25 07:55:58 -0700297 active_decoder_type_ = rtp_payload_type;
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000298 return kOK;
299}
300
ossu84bc9852016-08-26 05:41:23 -0700301AudioDecoder* DecoderDatabase::GetActiveDecoder() const {
ossu97ba30e2016-04-25 07:55:58 -0700302 if (active_decoder_type_ < 0) {
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000303 // No active decoder.
304 return NULL;
305 }
ossu97ba30e2016-04-25 07:55:58 -0700306 return GetDecoder(active_decoder_type_);
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000307}
308
309int DecoderDatabase::SetActiveCngDecoder(uint8_t rtp_payload_type) {
310 // Check that |rtp_payload_type| exists in the database.
ossu84bc9852016-08-26 05:41:23 -0700311 const DecoderInfo *info = GetDecoderInfo(rtp_payload_type);
312 if (!info) {
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000313 // Decoder not found.
314 return kDecoderNotFound;
315 }
ossu97ba30e2016-04-25 07:55:58 -0700316 if (active_cng_decoder_type_ >= 0 &&
317 active_cng_decoder_type_ != rtp_payload_type) {
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000318 // Moving from one active CNG decoder to another. Delete the first one.
ossu84bc9852016-08-26 05:41:23 -0700319 RTC_DCHECK(active_cng_decoder_);
ossu97ba30e2016-04-25 07:55:58 -0700320 active_cng_decoder_.reset();
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000321 }
ossu97ba30e2016-04-25 07:55:58 -0700322 active_cng_decoder_type_ = rtp_payload_type;
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000323 return kOK;
324}
325
ossu84bc9852016-08-26 05:41:23 -0700326ComfortNoiseDecoder* DecoderDatabase::GetActiveCngDecoder() const {
ossu97ba30e2016-04-25 07:55:58 -0700327 if (active_cng_decoder_type_ < 0) {
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000328 // No active CNG decoder.
329 return NULL;
330 }
ossu97ba30e2016-04-25 07:55:58 -0700331 if (!active_cng_decoder_) {
332 active_cng_decoder_.reset(new ComfortNoiseDecoder);
333 }
334 return active_cng_decoder_.get();
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000335}
336
ossu84bc9852016-08-26 05:41:23 -0700337AudioDecoder* DecoderDatabase::GetDecoder(uint8_t rtp_payload_type) const {
338 const DecoderInfo *info = GetDecoderInfo(rtp_payload_type);
339 return info ? info->GetDecoder() : nullptr;
340}
341
ossuf1b08da2016-09-23 02:19:43 -0700342bool DecoderDatabase::IsType(uint8_t rtp_payload_type, const char* name) const {
343 const DecoderInfo* info = GetDecoderInfo(rtp_payload_type);
344 return info && info->IsType(name);
345}
346
ossu84bc9852016-08-26 05:41:23 -0700347bool DecoderDatabase::IsType(uint8_t rtp_payload_type,
ossuf1b08da2016-09-23 02:19:43 -0700348 const std::string& name) const {
349 return IsType(rtp_payload_type, name.c_str());
ossu84bc9852016-08-26 05:41:23 -0700350}
351
352bool DecoderDatabase::IsComfortNoise(uint8_t rtp_payload_type) const {
353 const DecoderInfo *info = GetDecoderInfo(rtp_payload_type);
354 return info && info->IsComfortNoise();
355}
356
357bool DecoderDatabase::IsDtmf(uint8_t rtp_payload_type) const {
358 const DecoderInfo *info = GetDecoderInfo(rtp_payload_type);
359 return info && info->IsDtmf();
360}
361
362bool DecoderDatabase::IsRed(uint8_t rtp_payload_type) const {
363 const DecoderInfo *info = GetDecoderInfo(rtp_payload_type);
364 return info && info->IsRed();
365}
366
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000367int DecoderDatabase::CheckPayloadTypes(const PacketList& packet_list) const {
368 PacketList::const_iterator it;
369 for (it = packet_list.begin(); it != packet_list.end(); ++it) {
ossua73f6c92016-10-24 08:25:28 -0700370 if (!GetDecoderInfo(it->payload_type)) {
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000371 // Payload type is not found.
Mirko Bonadei675513b2017-11-09 11:09:25 +0100372 RTC_LOG(LS_WARNING) << "CheckPayloadTypes: unknown RTP payload type "
373 << static_cast<int>(it->payload_type);
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000374 return kDecoderNotFound;
375 }
376 }
377 return kOK;
378}
379
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000380} // namespace webrtc