| Pawin Vongmasa | 3665390 | 2018-11-15 00:10:25 -0800 | [diff] [blame] | 1 | /* | 
 | 2 |  * Copyright (C) 2018 The Android Open Source Project | 
 | 3 |  * | 
 | 4 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 | 5 |  * you may not use this file except in compliance with the License. | 
 | 6 |  * You may obtain a copy of the License at | 
 | 7 |  * | 
 | 8 |  *      http://www.apache.org/licenses/LICENSE-2.0 | 
 | 9 |  * | 
 | 10 |  * Unless required by applicable law or agreed to in writing, software | 
 | 11 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 | 12 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | 13 |  * See the License for the specific language governing permissions and | 
 | 14 |  * limitations under the License. | 
 | 15 |  */ | 
 | 16 |  | 
 | 17 | //#define LOG_NDEBUG 0 | 
 | 18 | #define LOG_TAG "C2SoftOpusDec" | 
 | 19 | #include <log/log.h> | 
 | 20 |  | 
 | 21 | #include <media/stagefright/foundation/MediaDefs.h> | 
| Harish Mahendrakar | 94d6bb5 | 2018-11-16 17:40:29 -0800 | [diff] [blame] | 22 | #include <media/stagefright/foundation/OpusHeader.h> | 
| Pawin Vongmasa | 3665390 | 2018-11-15 00:10:25 -0800 | [diff] [blame] | 23 | #include <C2PlatformSupport.h> | 
 | 24 | #include <SimpleC2Interface.h> | 
| Pawin Vongmasa | 3665390 | 2018-11-15 00:10:25 -0800 | [diff] [blame] | 25 | #include "C2SoftOpusDec.h" | 
 | 26 |  | 
 | 27 | extern "C" { | 
 | 28 |     #include <opus.h> | 
 | 29 |     #include <opus_multistream.h> | 
 | 30 | } | 
 | 31 |  | 
 | 32 | namespace android { | 
 | 33 |  | 
 | 34 | constexpr char COMPONENT_NAME[] = "c2.android.opus.decoder"; | 
 | 35 |  | 
 | 36 | class C2SoftOpusDec::IntfImpl : public C2InterfaceHelper { | 
 | 37 |    public: | 
 | 38 |     explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper>& helper) | 
 | 39 |         : C2InterfaceHelper(helper) { | 
 | 40 |         setDerivedInstance(this); | 
 | 41 |  | 
 | 42 |         addParameter( | 
| Lajos Molnar | 3bb81cd | 2019-02-20 15:10:30 -0800 | [diff] [blame] | 43 |                 DefineParam(mInputFormat, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE) | 
 | 44 |                 .withConstValue(new C2StreamBufferTypeSetting::input(0u, C2BufferData::LINEAR)) | 
| Pawin Vongmasa | 3665390 | 2018-11-15 00:10:25 -0800 | [diff] [blame] | 45 |                 .build()); | 
 | 46 |  | 
 | 47 |         addParameter( | 
| Lajos Molnar | 3bb81cd | 2019-02-20 15:10:30 -0800 | [diff] [blame] | 48 |                 DefineParam(mOutputFormat, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE) | 
 | 49 |                 .withConstValue(new C2StreamBufferTypeSetting::output(0u, C2BufferData::LINEAR)) | 
| Pawin Vongmasa | 3665390 | 2018-11-15 00:10:25 -0800 | [diff] [blame] | 50 |                 .build()); | 
 | 51 |  | 
 | 52 |         addParameter( | 
| Lajos Molnar | 3bb81cd | 2019-02-20 15:10:30 -0800 | [diff] [blame] | 53 |                 DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) | 
 | 54 |                 .withConstValue(AllocSharedString<C2PortMediaTypeSetting::input>( | 
| Pawin Vongmasa | 3665390 | 2018-11-15 00:10:25 -0800 | [diff] [blame] | 55 |                         MEDIA_MIMETYPE_AUDIO_OPUS)) | 
 | 56 |                 .build()); | 
 | 57 |  | 
 | 58 |         addParameter( | 
| Lajos Molnar | 3bb81cd | 2019-02-20 15:10:30 -0800 | [diff] [blame] | 59 |                 DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) | 
 | 60 |                 .withConstValue(AllocSharedString<C2PortMediaTypeSetting::output>( | 
| Pawin Vongmasa | 3665390 | 2018-11-15 00:10:25 -0800 | [diff] [blame] | 61 |                         MEDIA_MIMETYPE_AUDIO_RAW)) | 
 | 62 |                 .build()); | 
 | 63 |  | 
 | 64 |         addParameter( | 
| Lajos Molnar | 3bb81cd | 2019-02-20 15:10:30 -0800 | [diff] [blame] | 65 |                 DefineParam(mSampleRate, C2_PARAMKEY_SAMPLE_RATE) | 
| Pawin Vongmasa | 3665390 | 2018-11-15 00:10:25 -0800 | [diff] [blame] | 66 |                 .withDefault(new C2StreamSampleRateInfo::output(0u, 48000)) | 
 | 67 |                 .withFields({C2F(mSampleRate, value).equalTo(48000)}) | 
 | 68 |                 .withSetter((Setter<decltype(*mSampleRate)>::StrictValueWithNoDeps)) | 
 | 69 |                 .build()); | 
 | 70 |  | 
 | 71 |         addParameter( | 
| Lajos Molnar | 3bb81cd | 2019-02-20 15:10:30 -0800 | [diff] [blame] | 72 |                 DefineParam(mChannelCount, C2_PARAMKEY_CHANNEL_COUNT) | 
| Pawin Vongmasa | 3665390 | 2018-11-15 00:10:25 -0800 | [diff] [blame] | 73 |                 .withDefault(new C2StreamChannelCountInfo::output(0u, 1)) | 
 | 74 |                 .withFields({C2F(mChannelCount, value).inRange(1, 8)}) | 
 | 75 |                 .withSetter(Setter<decltype(*mChannelCount)>::StrictValueWithNoDeps) | 
 | 76 |                 .build()); | 
 | 77 |  | 
 | 78 |         addParameter( | 
| Lajos Molnar | 3bb81cd | 2019-02-20 15:10:30 -0800 | [diff] [blame] | 79 |                 DefineParam(mBitrate, C2_PARAMKEY_BITRATE) | 
 | 80 |                 .withDefault(new C2StreamBitrateInfo::input(0u, 6000)) | 
| Pawin Vongmasa | 3665390 | 2018-11-15 00:10:25 -0800 | [diff] [blame] | 81 |                 .withFields({C2F(mBitrate, value).inRange(6000, 510000)}) | 
 | 82 |                 .withSetter(Setter<decltype(*mBitrate)>::NonStrictValueWithNoDeps) | 
 | 83 |                 .build()); | 
 | 84 |  | 
 | 85 |         addParameter( | 
 | 86 |                 DefineParam(mInputMaxBufSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE) | 
 | 87 |                 .withConstValue(new C2StreamMaxBufferSizeInfo::input(0u, 960 * 6)) | 
 | 88 |                 .build()); | 
 | 89 |     } | 
 | 90 |  | 
 | 91 |    private: | 
| Lajos Molnar | 3bb81cd | 2019-02-20 15:10:30 -0800 | [diff] [blame] | 92 |     std::shared_ptr<C2StreamBufferTypeSetting::input> mInputFormat; | 
 | 93 |     std::shared_ptr<C2StreamBufferTypeSetting::output> mOutputFormat; | 
 | 94 |     std::shared_ptr<C2PortMediaTypeSetting::input> mInputMediaType; | 
 | 95 |     std::shared_ptr<C2PortMediaTypeSetting::output> mOutputMediaType; | 
| Pawin Vongmasa | 3665390 | 2018-11-15 00:10:25 -0800 | [diff] [blame] | 96 |     std::shared_ptr<C2StreamSampleRateInfo::output> mSampleRate; | 
 | 97 |     std::shared_ptr<C2StreamChannelCountInfo::output> mChannelCount; | 
| Lajos Molnar | 3bb81cd | 2019-02-20 15:10:30 -0800 | [diff] [blame] | 98 |     std::shared_ptr<C2StreamBitrateInfo::input> mBitrate; | 
| Pawin Vongmasa | 3665390 | 2018-11-15 00:10:25 -0800 | [diff] [blame] | 99 |     std::shared_ptr<C2StreamMaxBufferSizeInfo::input> mInputMaxBufSize; | 
 | 100 | }; | 
 | 101 |  | 
 | 102 | C2SoftOpusDec::C2SoftOpusDec(const char *name, c2_node_id_t id, | 
 | 103 |                        const std::shared_ptr<IntfImpl>& intfImpl) | 
 | 104 |     : SimpleC2Component( | 
 | 105 |         std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)), | 
 | 106 |       mIntf(intfImpl), | 
 | 107 |       mDecoder(nullptr) { | 
 | 108 | } | 
 | 109 |  | 
 | 110 | C2SoftOpusDec::~C2SoftOpusDec() { | 
 | 111 |     onRelease(); | 
 | 112 | } | 
 | 113 |  | 
 | 114 | c2_status_t C2SoftOpusDec::onInit() { | 
 | 115 |     status_t err = initDecoder(); | 
 | 116 |     return err == OK ? C2_OK : C2_NO_MEMORY; | 
 | 117 | } | 
 | 118 |  | 
 | 119 | c2_status_t C2SoftOpusDec::onStop() { | 
 | 120 |     if (mDecoder) { | 
 | 121 |         opus_multistream_decoder_destroy(mDecoder); | 
 | 122 |         mDecoder = nullptr; | 
 | 123 |     } | 
 | 124 |     memset(&mHeader, 0, sizeof(mHeader)); | 
 | 125 |     mCodecDelay = 0; | 
 | 126 |     mSeekPreRoll = 0; | 
 | 127 |     mSamplesToDiscard = 0; | 
 | 128 |     mInputBufferCount = 0; | 
 | 129 |     mSignalledError = false; | 
 | 130 |     mSignalledOutputEos = false; | 
 | 131 |  | 
 | 132 |     return C2_OK; | 
 | 133 | } | 
 | 134 |  | 
 | 135 | void C2SoftOpusDec::onReset() { | 
 | 136 |     (void)onStop(); | 
 | 137 | } | 
 | 138 |  | 
 | 139 | void C2SoftOpusDec::onRelease() { | 
 | 140 |     if (mDecoder) { | 
 | 141 |         opus_multistream_decoder_destroy(mDecoder); | 
 | 142 |         mDecoder = nullptr; | 
 | 143 |     } | 
 | 144 | } | 
 | 145 |  | 
 | 146 | status_t C2SoftOpusDec::initDecoder() { | 
 | 147 |     memset(&mHeader, 0, sizeof(mHeader)); | 
 | 148 |     mCodecDelay = 0; | 
 | 149 |     mSeekPreRoll = 0; | 
 | 150 |     mSamplesToDiscard = 0; | 
 | 151 |     mInputBufferCount = 0; | 
 | 152 |     mSignalledError = false; | 
 | 153 |     mSignalledOutputEos = false; | 
 | 154 |  | 
 | 155 |     return OK; | 
 | 156 | } | 
 | 157 |  | 
 | 158 | c2_status_t C2SoftOpusDec::onFlush_sm() { | 
 | 159 |     if (mDecoder) { | 
 | 160 |         opus_multistream_decoder_ctl(mDecoder, OPUS_RESET_STATE); | 
 | 161 |         mSamplesToDiscard = mSeekPreRoll; | 
 | 162 |         mSignalledOutputEos = false; | 
 | 163 |     } | 
 | 164 |     return C2_OK; | 
 | 165 | } | 
 | 166 |  | 
 | 167 | c2_status_t C2SoftOpusDec::drain( | 
 | 168 |         uint32_t drainMode, | 
 | 169 |         const std::shared_ptr<C2BlockPool> &pool) { | 
 | 170 |     (void) pool; | 
 | 171 |     if (drainMode == NO_DRAIN) { | 
 | 172 |         ALOGW("drain with NO_DRAIN: no-op"); | 
 | 173 |         return C2_OK; | 
 | 174 |     } | 
 | 175 |     if (drainMode == DRAIN_CHAIN) { | 
 | 176 |         ALOGW("DRAIN_CHAIN not supported"); | 
 | 177 |         return C2_OMITTED; | 
 | 178 |     } | 
 | 179 |  | 
 | 180 |     return C2_OK; | 
 | 181 | } | 
 | 182 |  | 
 | 183 | static void fillEmptyWork(const std::unique_ptr<C2Work> &work) { | 
 | 184 |     work->worklets.front()->output.flags = work->input.flags; | 
 | 185 |     work->worklets.front()->output.buffers.clear(); | 
 | 186 |     work->worklets.front()->output.ordinal = work->input.ordinal; | 
 | 187 |     work->workletsProcessed = 1u; | 
 | 188 | } | 
 | 189 |  | 
| Pawin Vongmasa | 3665390 | 2018-11-15 00:10:25 -0800 | [diff] [blame] | 190 | static const int kRate = 48000; | 
 | 191 |  | 
 | 192 | // Opus uses Vorbis channel mapping, and Vorbis channel mapping specifies | 
 | 193 | // mappings for up to 8 channels. This information is part of the Vorbis I | 
 | 194 | // Specification: | 
 | 195 | // http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html | 
 | 196 | static const int kMaxChannels = 8; | 
 | 197 |  | 
 | 198 | // Maximum packet size used in Xiph's opusdec. | 
 | 199 | static const int kMaxOpusOutputPacketSizeSamples = 960 * 6; | 
 | 200 |  | 
 | 201 | // Default audio output channel layout. Used to initialize |stream_map| in | 
 | 202 | // OpusHeader, and passed to opus_multistream_decoder_create() when the header | 
 | 203 | // does not contain mapping information. The values are valid only for mono and | 
 | 204 | // stereo output: Opus streams with more than 2 channels require a stream map. | 
 | 205 | static const int kMaxChannelsWithDefaultLayout = 2; | 
 | 206 | static const uint8_t kDefaultOpusChannelLayout[kMaxChannelsWithDefaultLayout] = { 0, 1 }; | 
 | 207 |  | 
| Pawin Vongmasa | 3665390 | 2018-11-15 00:10:25 -0800 | [diff] [blame] | 208 |  | 
 | 209 | // Convert nanoseconds to number of samples. | 
 | 210 | static uint64_t ns_to_samples(uint64_t ns, int rate) { | 
 | 211 |     return static_cast<double>(ns) * rate / 1000000000; | 
 | 212 | } | 
 | 213 |  | 
 | 214 | void C2SoftOpusDec::process( | 
 | 215 |         const std::unique_ptr<C2Work> &work, | 
 | 216 |         const std::shared_ptr<C2BlockPool> &pool) { | 
 | 217 |     // Initialize output work | 
 | 218 |     work->result = C2_OK; | 
 | 219 |     work->workletsProcessed = 1u; | 
 | 220 |     work->worklets.front()->output.configUpdate.clear(); | 
 | 221 |     work->worklets.front()->output.flags = work->input.flags; | 
 | 222 |  | 
 | 223 |     if (mSignalledError || mSignalledOutputEos) { | 
 | 224 |         work->result = C2_BAD_VALUE; | 
 | 225 |         return; | 
 | 226 |     } | 
 | 227 |  | 
 | 228 |     bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0); | 
 | 229 |     size_t inOffset = 0u; | 
 | 230 |     size_t inSize = 0u; | 
 | 231 |     C2ReadView rView = mDummyReadView; | 
 | 232 |     if (!work->input.buffers.empty()) { | 
 | 233 |         rView = work->input.buffers[0]->data().linearBlocks().front().map().get(); | 
 | 234 |         inSize = rView.capacity(); | 
 | 235 |         if (inSize && rView.error()) { | 
 | 236 |             ALOGE("read view map failed %d", rView.error()); | 
 | 237 |             work->result = C2_CORRUPTED; | 
 | 238 |             return; | 
 | 239 |         } | 
 | 240 |     } | 
 | 241 |     if (inSize == 0) { | 
 | 242 |         fillEmptyWork(work); | 
 | 243 |         if (eos) { | 
 | 244 |             mSignalledOutputEos = true; | 
 | 245 |             ALOGV("signalled EOS"); | 
 | 246 |         } | 
 | 247 |         return; | 
 | 248 |     } | 
 | 249 |  | 
 | 250 |     ALOGV("in buffer attr. size %zu timestamp %d frameindex %d", inSize, | 
 | 251 |           (int)work->input.ordinal.timestamp.peeku(), (int)work->input.ordinal.frameIndex.peeku()); | 
 | 252 |     const uint8_t *data = rView.data() + inOffset; | 
 | 253 |     if (mInputBufferCount < 3) { | 
 | 254 |         if (mInputBufferCount == 0) { | 
| Harish Mahendrakar | 6c95530 | 2019-01-25 09:15:51 -0800 | [diff] [blame] | 255 |             size_t opusHeadSize = 0; | 
| Harish Mahendrakar | 94d6bb5 | 2018-11-16 17:40:29 -0800 | [diff] [blame] | 256 |             size_t codecDelayBufSize = 0; | 
 | 257 |             size_t seekPreRollBufSize = 0; | 
| Harish Mahendrakar | 6c95530 | 2019-01-25 09:15:51 -0800 | [diff] [blame] | 258 |             void *opusHeadBuf = NULL; | 
| Harish Mahendrakar | 94d6bb5 | 2018-11-16 17:40:29 -0800 | [diff] [blame] | 259 |             void *codecDelayBuf = NULL; | 
 | 260 |             void *seekPreRollBuf = NULL; | 
 | 261 |  | 
| Harish Mahendrakar | 6c95530 | 2019-01-25 09:15:51 -0800 | [diff] [blame] | 262 |             if (!GetOpusHeaderBuffers(data, inSize, &opusHeadBuf, | 
 | 263 |                                      &opusHeadSize, &codecDelayBuf, | 
 | 264 |                                      &codecDelayBufSize, &seekPreRollBuf, | 
 | 265 |                                      &seekPreRollBufSize)) { | 
 | 266 |                 ALOGE("%s encountered error in GetOpusHeaderBuffers", __func__); | 
 | 267 |                 mSignalledError = true; | 
 | 268 |                 work->result = C2_CORRUPTED; | 
 | 269 |                 return; | 
 | 270 |             } | 
| Harish Mahendrakar | 94d6bb5 | 2018-11-16 17:40:29 -0800 | [diff] [blame] | 271 |  | 
 | 272 |             if (!ParseOpusHeader((uint8_t *)opusHeadBuf, opusHeadSize, &mHeader)) { | 
| Harish Mahendrakar | 6c95530 | 2019-01-25 09:15:51 -0800 | [diff] [blame] | 273 |                 ALOGE("%s Encountered error while Parsing Opus Header.", __func__); | 
| Pawin Vongmasa | 3665390 | 2018-11-15 00:10:25 -0800 | [diff] [blame] | 274 |                 mSignalledError = true; | 
 | 275 |                 work->result = C2_CORRUPTED; | 
 | 276 |                 return; | 
 | 277 |             } | 
 | 278 |             uint8_t channel_mapping[kMaxChannels] = {0}; | 
 | 279 |             if (mHeader.channels <= kMaxChannelsWithDefaultLayout) { | 
 | 280 |                 memcpy(&channel_mapping, | 
 | 281 |                        kDefaultOpusChannelLayout, | 
 | 282 |                        kMaxChannelsWithDefaultLayout); | 
 | 283 |             } else { | 
 | 284 |                 memcpy(&channel_mapping, | 
 | 285 |                        mHeader.stream_map, | 
 | 286 |                        mHeader.channels); | 
 | 287 |             } | 
 | 288 |             int status = OPUS_INVALID_STATE; | 
 | 289 |             mDecoder = opus_multistream_decoder_create(kRate, | 
 | 290 |                                                        mHeader.channels, | 
 | 291 |                                                        mHeader.num_streams, | 
 | 292 |                                                        mHeader.num_coupled, | 
 | 293 |                                                        channel_mapping, | 
 | 294 |                                                        &status); | 
 | 295 |             if (!mDecoder || status != OPUS_OK) { | 
 | 296 |                 ALOGE("opus_multistream_decoder_create failed status = %s", | 
 | 297 |                       opus_strerror(status)); | 
 | 298 |                 mSignalledError = true; | 
 | 299 |                 work->result = C2_CORRUPTED; | 
 | 300 |                 return; | 
 | 301 |             } | 
 | 302 |             status = opus_multistream_decoder_ctl(mDecoder, | 
 | 303 |                                                   OPUS_SET_GAIN(mHeader.gain_db)); | 
 | 304 |             if (status != OPUS_OK) { | 
 | 305 |                 ALOGE("Failed to set OPUS header gain; status = %s", | 
 | 306 |                       opus_strerror(status)); | 
 | 307 |                 mSignalledError = true; | 
 | 308 |                 work->result = C2_CORRUPTED; | 
 | 309 |                 return; | 
 | 310 |             } | 
| Harish Mahendrakar | 94d6bb5 | 2018-11-16 17:40:29 -0800 | [diff] [blame] | 311 |  | 
| Harish Mahendrakar | 6c95530 | 2019-01-25 09:15:51 -0800 | [diff] [blame] | 312 |             if (codecDelayBuf && codecDelayBufSize == sizeof(uint64_t)) { | 
| Harish Mahendrakar | 94d6bb5 | 2018-11-16 17:40:29 -0800 | [diff] [blame] | 313 |                 uint64_t value; | 
 | 314 |                 memcpy(&value, codecDelayBuf, sizeof(uint64_t)); | 
 | 315 |                 mCodecDelay = ns_to_samples(value, kRate); | 
 | 316 |                 mSamplesToDiscard = mCodecDelay; | 
 | 317 |                 ++mInputBufferCount; | 
 | 318 |             } | 
| Harish Mahendrakar | 6c95530 | 2019-01-25 09:15:51 -0800 | [diff] [blame] | 319 |             if (seekPreRollBuf && seekPreRollBufSize == sizeof(uint64_t)) { | 
| Harish Mahendrakar | 94d6bb5 | 2018-11-16 17:40:29 -0800 | [diff] [blame] | 320 |                 uint64_t value; | 
| Harish Mahendrakar | 630dda9 | 2019-01-25 09:15:51 -0800 | [diff] [blame] | 321 |                 memcpy(&value, seekPreRollBuf, sizeof(uint64_t)); | 
| Harish Mahendrakar | 94d6bb5 | 2018-11-16 17:40:29 -0800 | [diff] [blame] | 322 |                 mSeekPreRoll = ns_to_samples(value, kRate); | 
 | 323 |                 ++mInputBufferCount; | 
 | 324 |             } | 
| Pawin Vongmasa | 3665390 | 2018-11-15 00:10:25 -0800 | [diff] [blame] | 325 |         } else { | 
 | 326 |             if (inSize < 8) { | 
 | 327 |                 ALOGE("Input sample size is too small."); | 
 | 328 |                 mSignalledError = true; | 
 | 329 |                 work->result = C2_CORRUPTED; | 
 | 330 |                 return; | 
 | 331 |             } | 
 | 332 |             int64_t samples = ns_to_samples( *(reinterpret_cast<int64_t*> | 
 | 333 |                               (const_cast<uint8_t *> (data))), kRate); | 
 | 334 |             if (mInputBufferCount == 1) { | 
 | 335 |                 mCodecDelay = samples; | 
 | 336 |                 mSamplesToDiscard = mCodecDelay; | 
 | 337 |             } | 
 | 338 |             else { | 
 | 339 |                 mSeekPreRoll = samples; | 
| Pawin Vongmasa | 3665390 | 2018-11-15 00:10:25 -0800 | [diff] [blame] | 340 |             } | 
 | 341 |         } | 
 | 342 |  | 
 | 343 |         ++mInputBufferCount; | 
| Harish Mahendrakar | 94d6bb5 | 2018-11-16 17:40:29 -0800 | [diff] [blame] | 344 |         if (mInputBufferCount == 3) { | 
 | 345 |             ALOGI("Configuring decoder: %d Hz, %d channels", | 
 | 346 |                    kRate, mHeader.channels); | 
 | 347 |             C2StreamSampleRateInfo::output sampleRateInfo(0u, kRate); | 
 | 348 |             C2StreamChannelCountInfo::output channelCountInfo(0u, mHeader.channels); | 
 | 349 |             std::vector<std::unique_ptr<C2SettingResult>> failures; | 
 | 350 |             c2_status_t err = mIntf->config( | 
 | 351 |                     { &sampleRateInfo, &channelCountInfo }, | 
 | 352 |                     C2_MAY_BLOCK, | 
 | 353 |                     &failures); | 
 | 354 |             if (err == OK) { | 
 | 355 |                 work->worklets.front()->output.configUpdate.push_back(C2Param::Copy(sampleRateInfo)); | 
 | 356 |                 work->worklets.front()->output.configUpdate.push_back(C2Param::Copy(channelCountInfo)); | 
 | 357 |             } else { | 
 | 358 |                 ALOGE("Config Update failed"); | 
 | 359 |                 mSignalledError = true; | 
 | 360 |                 work->result = C2_CORRUPTED; | 
 | 361 |                 return; | 
 | 362 |             } | 
 | 363 |         } | 
| Pawin Vongmasa | 3665390 | 2018-11-15 00:10:25 -0800 | [diff] [blame] | 364 |         fillEmptyWork(work); | 
 | 365 |         if (eos) { | 
 | 366 |             mSignalledOutputEos = true; | 
 | 367 |             ALOGV("signalled EOS"); | 
 | 368 |         } | 
 | 369 |         return; | 
 | 370 |     } | 
 | 371 |  | 
 | 372 |     // Ignore CSD re-submissions. | 
 | 373 |     if ((work->input.flags & C2FrameData::FLAG_CODEC_CONFIG)) { | 
 | 374 |         fillEmptyWork(work); | 
 | 375 |         return; | 
 | 376 |     } | 
 | 377 |  | 
 | 378 |     // When seeking to zero, |mCodecDelay| samples has to be discarded | 
 | 379 |     // instead of |mSeekPreRoll| samples (as we would when seeking to any | 
 | 380 |     // other timestamp). | 
 | 381 |     if (work->input.ordinal.timestamp.peeku() == 0) mSamplesToDiscard = mCodecDelay; | 
 | 382 |  | 
 | 383 |     std::shared_ptr<C2LinearBlock> block; | 
 | 384 |     C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; | 
 | 385 |     c2_status_t err = pool->fetchLinearBlock( | 
 | 386 |                           kMaxNumSamplesPerBuffer * kMaxChannels * sizeof(int16_t), | 
 | 387 |                           usage, &block); | 
 | 388 |     if (err != C2_OK) { | 
 | 389 |         ALOGE("fetchLinearBlock for Output failed with status %d", err); | 
 | 390 |         work->result = C2_NO_MEMORY; | 
 | 391 |         return; | 
 | 392 |     } | 
 | 393 |     C2WriteView wView = block->map().get(); | 
 | 394 |     if (wView.error()) { | 
 | 395 |         ALOGE("write view map failed %d", wView.error()); | 
 | 396 |         work->result = C2_CORRUPTED; | 
 | 397 |         return; | 
 | 398 |     } | 
 | 399 |  | 
 | 400 |     int numSamples = opus_multistream_decode(mDecoder, | 
 | 401 |                                              data, | 
 | 402 |                                              inSize, | 
 | 403 |                                              reinterpret_cast<int16_t *> (wView.data()), | 
 | 404 |                                              kMaxOpusOutputPacketSizeSamples, | 
 | 405 |                                              0); | 
 | 406 |     if (numSamples < 0) { | 
 | 407 |         ALOGE("opus_multistream_decode returned numSamples %d", numSamples); | 
 | 408 |         numSamples = 0; | 
 | 409 |         mSignalledError = true; | 
 | 410 |         work->result = C2_CORRUPTED; | 
 | 411 |         return; | 
 | 412 |     } | 
 | 413 |  | 
 | 414 |     int outOffset = 0; | 
 | 415 |     if (mSamplesToDiscard > 0) { | 
 | 416 |         if (mSamplesToDiscard > numSamples) { | 
 | 417 |             mSamplesToDiscard -= numSamples; | 
 | 418 |             numSamples = 0; | 
 | 419 |         } else { | 
 | 420 |             numSamples -= mSamplesToDiscard; | 
 | 421 |             outOffset = mSamplesToDiscard * sizeof(int16_t) * mHeader.channels; | 
 | 422 |             mSamplesToDiscard = 0; | 
 | 423 |         } | 
 | 424 |     } | 
 | 425 |  | 
 | 426 |     if (numSamples) { | 
 | 427 |         int outSize = numSamples * sizeof(int16_t) * mHeader.channels; | 
 | 428 |         ALOGV("out buffer attr. offset %d size %d ", outOffset, outSize); | 
 | 429 |  | 
 | 430 |         work->worklets.front()->output.flags = work->input.flags; | 
 | 431 |         work->worklets.front()->output.buffers.clear(); | 
 | 432 |         work->worklets.front()->output.buffers.push_back(createLinearBuffer(block, outOffset, outSize)); | 
 | 433 |         work->worklets.front()->output.ordinal = work->input.ordinal; | 
 | 434 |         work->workletsProcessed = 1u; | 
 | 435 |     } else { | 
 | 436 |         fillEmptyWork(work); | 
 | 437 |         block.reset(); | 
 | 438 |     } | 
 | 439 |     if (eos) { | 
 | 440 |         mSignalledOutputEos = true; | 
 | 441 |         ALOGV("signalled EOS"); | 
 | 442 |     } | 
 | 443 | } | 
 | 444 |  | 
 | 445 | class C2SoftOpusDecFactory : public C2ComponentFactory { | 
 | 446 | public: | 
 | 447 |     C2SoftOpusDecFactory() : mHelper(std::static_pointer_cast<C2ReflectorHelper>( | 
 | 448 |             GetCodec2PlatformComponentStore()->getParamReflector())) { | 
 | 449 |     } | 
 | 450 |  | 
 | 451 |     virtual c2_status_t createComponent( | 
 | 452 |             c2_node_id_t id, | 
 | 453 |             std::shared_ptr<C2Component>* const component, | 
 | 454 |             std::function<void(C2Component*)> deleter) override { | 
 | 455 |         *component = std::shared_ptr<C2Component>( | 
 | 456 |                 new C2SoftOpusDec(COMPONENT_NAME, | 
 | 457 |                                id, | 
 | 458 |                                std::make_shared<C2SoftOpusDec::IntfImpl>(mHelper)), | 
 | 459 |                 deleter); | 
 | 460 |         return C2_OK; | 
 | 461 |     } | 
 | 462 |  | 
 | 463 |     virtual c2_status_t createInterface( | 
 | 464 |             c2_node_id_t id, | 
 | 465 |             std::shared_ptr<C2ComponentInterface>* const interface, | 
 | 466 |             std::function<void(C2ComponentInterface*)> deleter) override { | 
 | 467 |         *interface = std::shared_ptr<C2ComponentInterface>( | 
 | 468 |                 new SimpleInterface<C2SoftOpusDec::IntfImpl>( | 
 | 469 |                         COMPONENT_NAME, id, std::make_shared<C2SoftOpusDec::IntfImpl>(mHelper)), | 
 | 470 |                 deleter); | 
 | 471 |         return C2_OK; | 
 | 472 |     } | 
 | 473 |  | 
 | 474 |     virtual ~C2SoftOpusDecFactory() override = default; | 
 | 475 |  | 
 | 476 | private: | 
 | 477 |     std::shared_ptr<C2ReflectorHelper> mHelper; | 
 | 478 | }; | 
 | 479 |  | 
 | 480 | }  // namespace android | 
 | 481 |  | 
 | 482 | extern "C" ::C2ComponentFactory* CreateCodec2Factory() { | 
 | 483 |     ALOGV("in %s", __func__); | 
 | 484 |     return new ::android::C2SoftOpusDecFactory(); | 
 | 485 | } | 
 | 486 |  | 
 | 487 | extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) { | 
 | 488 |     ALOGV("in %s", __func__); | 
 | 489 |     delete factory; | 
 | 490 | } |