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 "C2SoftAmrWbEnc"
19#include <log/log.h>
20
21#include <media/stagefright/foundation/MediaDefs.h>
22
23#include <C2Debug.h>
24#include <C2PlatformSupport.h>
25#include <SimpleC2Interface.h>
26
27#include "C2SoftAmrWbEnc.h"
28#include "cmnMemory.h"
29
30namespace android {
31
32constexpr char COMPONENT_NAME[] = "c2.android.amrwb.encoder";
33
34class C2SoftAmrWbEnc::IntfImpl : public C2InterfaceHelper {
35   public:
36    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper>& helper)
37        : C2InterfaceHelper(helper) {
38        setDerivedInstance(this);
39
40        addParameter(
41            DefineParam(mInputFormat, C2_NAME_INPUT_STREAM_FORMAT_SETTING)
42                .withConstValue(
43                    new C2StreamFormatConfig::input(0u, C2FormatAudio))
44                .build());
45
46        addParameter(
47            DefineParam(mOutputFormat, C2_NAME_OUTPUT_STREAM_FORMAT_SETTING)
48                .withConstValue(
49                    new C2StreamFormatConfig::output(0u, C2FormatCompressed))
50                .build());
51
52        addParameter(
53            DefineParam(mInputMediaType, C2_NAME_INPUT_PORT_MIME_SETTING)
54                .withConstValue(AllocSharedString<C2PortMimeConfig::input>(
55                    MEDIA_MIMETYPE_AUDIO_RAW))
56                .build());
57
58        addParameter(
59            DefineParam(mOutputMediaType, C2_NAME_OUTPUT_PORT_MIME_SETTING)
60                .withConstValue(AllocSharedString<C2PortMimeConfig::output>(
61                    MEDIA_MIMETYPE_AUDIO_AMR_WB))
62                .build());
63
64        addParameter(
65                DefineParam(mChannelCount, C2_NAME_STREAM_CHANNEL_COUNT_SETTING)
66                .withDefault(new C2StreamChannelCountInfo::input(0u, 1))
67                .withFields({C2F(mChannelCount, value).equalTo(1)})
68                .withSetter((Setter<decltype(*mChannelCount)>::StrictValueWithNoDeps))
69                .build());
70
71        addParameter(
72            DefineParam(mSampleRate, C2_NAME_STREAM_SAMPLE_RATE_SETTING)
73                .withDefault(new C2StreamSampleRateInfo::input(0u, 16000))
74                .withFields({C2F(mSampleRate, value).equalTo(16000)})
75                .withSetter(
76                    (Setter<decltype(*mSampleRate)>::StrictValueWithNoDeps))
77                .build());
78
79        addParameter(
80                DefineParam(mBitrate, C2_NAME_STREAM_BITRATE_SETTING)
81                .withDefault(new C2BitrateTuning::output(0u, 6600))
82                .withFields({C2F(mBitrate, value).inRange(6600, 23850)})
83                .withSetter(Setter<decltype(*mBitrate)>::NonStrictValueWithNoDeps)
84                .build());
85
86        addParameter(
87                DefineParam(mInputMaxBufSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
88                .withConstValue(new C2StreamMaxBufferSizeInfo::input(0u, 8192))
89                .build());
90    }
91
92    uint32_t getSampleRate() const { return mSampleRate->value; }
93    uint32_t getChannelCount() const { return mChannelCount->value; }
94    uint32_t getBitrate() const { return mBitrate->value; }
95
96   private:
97    std::shared_ptr<C2StreamFormatConfig::input> mInputFormat;
98    std::shared_ptr<C2StreamFormatConfig::output> mOutputFormat;
99    std::shared_ptr<C2PortMimeConfig::input> mInputMediaType;
100    std::shared_ptr<C2PortMimeConfig::output> mOutputMediaType;
101    std::shared_ptr<C2StreamSampleRateInfo::input> mSampleRate;
102    std::shared_ptr<C2StreamChannelCountInfo::input> mChannelCount;
103    std::shared_ptr<C2BitrateTuning::output> mBitrate;
104    std::shared_ptr<C2StreamMaxBufferSizeInfo::input> mInputMaxBufSize;
105};
106
107C2SoftAmrWbEnc::C2SoftAmrWbEnc(const char* name, c2_node_id_t id,
108                               const std::shared_ptr<IntfImpl>& intfImpl)
109    : SimpleC2Component(
110          std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
111      mIntf(intfImpl),
112      mEncoderHandle(nullptr),
113      mApiHandle(nullptr),
114      mMemOperator(nullptr) {
115}
116
117C2SoftAmrWbEnc::~C2SoftAmrWbEnc() {
118    onRelease();
119}
120
121c2_status_t C2SoftAmrWbEnc::onInit() {
122    // TODO: get mode directly from config
123    switch(mIntf->getBitrate()) {
124        case 6600: mMode = VOAMRWB_MD66;
125            break;
126        case 8850: mMode = VOAMRWB_MD885;
127            break;
128        case 12650: mMode = VOAMRWB_MD1265;
129            break;
130        case 14250: mMode = VOAMRWB_MD1425;
131            break;
132        case 15850: mMode = VOAMRWB_MD1585;
133            break;
134        case 18250: mMode = VOAMRWB_MD1825;
135            break;
136        case 19850: mMode = VOAMRWB_MD1985;
137            break;
138        case 23050: mMode = VOAMRWB_MD2305;
139            break;
140        case 23850: mMode = VOAMRWB_MD2385;
141            break;
142        default: mMode = VOAMRWB_MD2305;
143    }
144    status_t err = initEncoder();
145    mIsFirst = true;
146    mSignalledError = false;
147    mSignalledOutputEos = false;
148    mAnchorTimeStamp = 0;
149    mProcessedSamples = 0;
150    mFilledLen = 0;
151
152    return err == OK ? C2_OK : C2_NO_MEMORY;
153}
154
155void C2SoftAmrWbEnc::onRelease() {
156    if (mEncoderHandle) {
157        CHECK_EQ((VO_U32)VO_ERR_NONE, mApiHandle->Uninit(mEncoderHandle));
158        mEncoderHandle = nullptr;
159    }
160    if (mApiHandle) {
161        delete mApiHandle;
162        mApiHandle = nullptr;
163    }
164    if (mMemOperator) {
165        delete mMemOperator;
166        mMemOperator = nullptr;
167    }
168}
169
170c2_status_t C2SoftAmrWbEnc::onStop() {
171    for (int i = 0; i < kNumSamplesPerFrame; i++) {
172        mInputFrame[i] = 0x0008; /* EHF_MASK */
173    }
174    uint8_t outBuffer[kNumBytesPerInputFrame];
175    (void) encodeInput(outBuffer, kNumBytesPerInputFrame);
176    mIsFirst = true;
177    mSignalledError = false;
178    mSignalledOutputEos = false;
179    mAnchorTimeStamp = 0;
180    mProcessedSamples = 0;
181    mFilledLen = 0;
182
183    return C2_OK;
184}
185
186void C2SoftAmrWbEnc::onReset() {
187    (void) onStop();
188}
189
190c2_status_t C2SoftAmrWbEnc::onFlush_sm() {
191    return onStop();
192}
193
194status_t C2SoftAmrWbEnc::initEncoder() {
195    mApiHandle = new VO_AUDIO_CODECAPI;
196    if (!mApiHandle) return NO_MEMORY;
197
198    if (VO_ERR_NONE != voGetAMRWBEncAPI(mApiHandle)) {
199        ALOGE("Failed to get api handle");
200        return UNKNOWN_ERROR;
201    }
202
203    mMemOperator = new VO_MEM_OPERATOR;
204    if (!mMemOperator) return NO_MEMORY;
205
206    mMemOperator->Alloc = cmnMemAlloc;
207    mMemOperator->Copy = cmnMemCopy;
208    mMemOperator->Free = cmnMemFree;
209    mMemOperator->Set = cmnMemSet;
210    mMemOperator->Check = cmnMemCheck;
211
212    VO_CODEC_INIT_USERDATA userData;
213    memset(&userData, 0, sizeof(userData));
214    userData.memflag = VO_IMF_USERMEMOPERATOR;
215    userData.memData = (VO_PTR) mMemOperator;
216
217    if (VO_ERR_NONE != mApiHandle->Init(
218                &mEncoderHandle, VO_AUDIO_CodingAMRWB, &userData)) {
219        ALOGE("Failed to init AMRWB encoder");
220        return UNKNOWN_ERROR;
221    }
222
223    VOAMRWBFRAMETYPE type = VOAMRWB_RFC3267;
224    if (VO_ERR_NONE != mApiHandle->SetParam(
225                mEncoderHandle, VO_PID_AMRWB_FRAMETYPE, &type)) {
226        ALOGE("Failed to set AMRWB encoder frame type to %d", type);
227        return UNKNOWN_ERROR;
228    }
229
230    if (VO_ERR_NONE !=
231            mApiHandle->SetParam(
232                    mEncoderHandle, VO_PID_AMRWB_MODE,  &mMode)) {
233        ALOGE("Failed to set AMRWB encoder mode to %d", mMode);
234        return UNKNOWN_ERROR;
235    }
236
237    return OK;
238}
239
240int C2SoftAmrWbEnc::encodeInput(uint8_t *buffer, uint32_t length) {
241    VO_CODECBUFFER inputData;
242    memset(&inputData, 0, sizeof(inputData));
243    inputData.Buffer = (unsigned char *) mInputFrame;
244    inputData.Length = kNumBytesPerInputFrame;
245
246    CHECK_EQ((VO_U32)VO_ERR_NONE,
247             mApiHandle->SetInputData(mEncoderHandle, &inputData));
248
249    VO_AUDIO_OUTPUTINFO outputInfo;
250    memset(&outputInfo, 0, sizeof(outputInfo));
251    VO_CODECBUFFER outputData;
252    memset(&outputData, 0, sizeof(outputData));
253    outputData.Buffer = buffer;
254    outputData.Length = length;
255    VO_U32 ret = mApiHandle->GetOutputData(
256            mEncoderHandle, &outputData, &outputInfo);
257    if (ret != VO_ERR_NONE && ret != VO_ERR_INPUT_BUFFER_SMALL) {
258        ALOGD("encountered error during encode call");
259        return -1;
260    }
261    return outputData.Length;
262}
263
264static void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
265    work->worklets.front()->output.flags = work->input.flags;
266    work->worklets.front()->output.buffers.clear();
267    work->worklets.front()->output.ordinal = work->input.ordinal;
268    work->workletsProcessed = 1u;
269}
270
271void C2SoftAmrWbEnc::process(
272        const std::unique_ptr<C2Work> &work,
273        const std::shared_ptr<C2BlockPool> &pool) {
274    work->result = C2_OK;
275    work->workletsProcessed = 0u;
276    if (mSignalledError || mSignalledOutputEos) {
277        work->result = C2_BAD_VALUE;
278        return;
279    }
280
281    size_t inOffset = 0u;
282    size_t inSize = 0u;
283    C2ReadView rView = mDummyReadView;
284    if (!work->input.buffers.empty()) {
285        rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
286        inSize = rView.capacity();
287        if (inSize && rView.error()) {
288            ALOGE("read view map failed %d", rView.error());
289            work->result = rView.error();
290            return;
291        }
292    }
293    bool eos = (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0;
294
295    ALOGV("in buffer attr. size %zu timestamp %d frameindex %d, flags %x",
296          inSize, (int)work->input.ordinal.timestamp.peeku(),
297          (int)work->input.ordinal.frameIndex.peeku(), work->input.flags);
298
299    size_t outCapacity = kNumBytesPerInputFrame;
300    outCapacity += mFilledLen + inSize;
301    std::shared_ptr<C2LinearBlock> outputBlock;
302    C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
303    c2_status_t err = pool->fetchLinearBlock(outCapacity, usage, &outputBlock);
304    if (err != C2_OK) {
305        ALOGE("fetchLinearBlock for Output failed with status %d", err);
306        work->result = C2_NO_MEMORY;
307        return;
308    }
309    C2WriteView wView = outputBlock->map().get();
310    if (wView.error()) {
311        ALOGE("write view map failed %d", wView.error());
312        work->result = wView.error();
313        return;
314    }
315    uint64_t outTimeStamp =
316        mProcessedSamples * 1000000ll / mIntf->getSampleRate();
317    size_t inPos = 0;
318    size_t outPos = 0;
319    while (inPos < inSize) {
320        const uint8_t *inPtr = rView.data() + inOffset;
321        int validSamples = mFilledLen / sizeof(int16_t);
322        if ((inPos + (kNumBytesPerInputFrame - mFilledLen)) <= inSize) {
323            memcpy(mInputFrame + validSamples, inPtr + inPos,
324                   (kNumBytesPerInputFrame - mFilledLen));
325            inPos += (kNumBytesPerInputFrame - mFilledLen);
326        } else {
327            memcpy(mInputFrame + validSamples, inPtr + inPos, (inSize - inPos));
328            mFilledLen += (inSize - inPos);
329            inPos += (inSize - inPos);
330            if (eos) {
331                validSamples = mFilledLen / sizeof(int16_t);
332                memset(mInputFrame + validSamples, 0, (kNumBytesPerInputFrame - mFilledLen));
333            } else break;
334        }
335        int numEncBytes = encodeInput((wView.data() + outPos), outCapacity - outPos);
336        if (numEncBytes < 0) {
337            ALOGE("encodeFrame call failed, state [%d %zu %zu]", numEncBytes, outPos, outCapacity);
338            mSignalledError = true;
339            work->result = C2_CORRUPTED;
340            return;
341        }
342        outPos += numEncBytes;
343        mProcessedSamples += kNumSamplesPerFrame;
344        mFilledLen = 0;
345    }
346    ALOGV("causal sample size %d", mFilledLen);
347    if (mIsFirst) {
348        mIsFirst = false;
349        mAnchorTimeStamp = work->input.ordinal.timestamp.peekull();
350    }
351    fillEmptyWork(work);
352    if (outPos != 0) {
353        work->worklets.front()->output.buffers.push_back(
354                createLinearBuffer(std::move(outputBlock), 0, outPos));
355        work->worklets.front()->output.ordinal.timestamp = mAnchorTimeStamp + outTimeStamp;
356    }
357    if (eos) {
358        mSignalledOutputEos = true;
359        ALOGV("signalled EOS");
360        if (mFilledLen) ALOGV("Discarding trailing %d bytes", mFilledLen);
361    }
362}
363
364c2_status_t C2SoftAmrWbEnc::drain(
365        uint32_t drainMode,
366        const std::shared_ptr<C2BlockPool> &pool) {
367    (void) pool;
368    if (drainMode == NO_DRAIN) {
369        ALOGW("drain with NO_DRAIN: no-op");
370        return C2_OK;
371    }
372    if (drainMode == DRAIN_CHAIN) {
373        ALOGW("DRAIN_CHAIN not supported");
374        return C2_OMITTED;
375    }
376
377    onFlush_sm();
378    return C2_OK;
379}
380
381class C2SoftAmrWbEncFactory : public C2ComponentFactory {
382public:
383    C2SoftAmrWbEncFactory()
384        : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
385              GetCodec2PlatformComponentStore()->getParamReflector())) {}
386
387    virtual c2_status_t createComponent(
388            c2_node_id_t id,
389            std::shared_ptr<C2Component>* const component,
390            std::function<void(C2Component*)> deleter) override {
391        *component = std::shared_ptr<C2Component>(
392            new C2SoftAmrWbEnc(
393                COMPONENT_NAME, id,
394                std::make_shared<C2SoftAmrWbEnc::IntfImpl>(mHelper)),
395            deleter);
396        return C2_OK;
397    }
398
399    virtual c2_status_t createInterface(
400            c2_node_id_t id,
401            std::shared_ptr<C2ComponentInterface>* const interface,
402            std::function<void(C2ComponentInterface*)> deleter) override {
403        *interface = std::shared_ptr<C2ComponentInterface>(
404            new SimpleInterface<C2SoftAmrWbEnc::IntfImpl>(
405                COMPONENT_NAME, id,
406                std::make_shared<C2SoftAmrWbEnc::IntfImpl>(mHelper)),
407            deleter);
408        return C2_OK;
409    }
410
411    virtual ~C2SoftAmrWbEncFactory() override = default;
412
413private:
414    std::shared_ptr<C2ReflectorHelper> mHelper;
415};
416
417}  // namespace android
418
419extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
420    ALOGV("in %s", __func__);
421    return new ::android::C2SoftAmrWbEncFactory();
422}
423
424extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
425    ALOGV("in %s", __func__);
426    delete factory;
427}
428