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 "C2SoftFlacEnc"
19#include <log/log.h>
20
21#include <media/stagefright/foundation/MediaDefs.h>
22
23#include <C2PlatformSupport.h>
24#include <SimpleC2Interface.h>
25
26#include "C2SoftFlacEnc.h"
27
28namespace android {
29
30class C2SoftFlacEnc::IntfImpl : public C2InterfaceHelper {
31public:
32    explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
33        : C2InterfaceHelper(helper) {
34        setDerivedInstance(this);
35        addParameter(
36                DefineParam(mInputFormat, C2_NAME_INPUT_STREAM_FORMAT_SETTING)
37                .withConstValue(new C2StreamFormatConfig::input(0u, C2FormatAudio))
38                .build());
39        addParameter(
40                DefineParam(mOutputFormat, C2_NAME_OUTPUT_STREAM_FORMAT_SETTING)
41                .withConstValue(new C2StreamFormatConfig::output(0u, C2FormatCompressed))
42                .build());
43        addParameter(
44                DefineParam(mInputMediaType, C2_NAME_INPUT_PORT_MIME_SETTING)
45                .withConstValue(AllocSharedString<C2PortMimeConfig::input>(
46                        MEDIA_MIMETYPE_AUDIO_RAW))
47                .build());
48        addParameter(
49                DefineParam(mOutputMediaType, C2_NAME_OUTPUT_PORT_MIME_SETTING)
50                .withConstValue(AllocSharedString<C2PortMimeConfig::output>(
51                        MEDIA_MIMETYPE_AUDIO_FLAC))
52                .build());
53        addParameter(
54                DefineParam(mSampleRate, C2_NAME_STREAM_SAMPLE_RATE_SETTING)
55                .withDefault(new C2StreamSampleRateInfo::input(0u, 44100))
56                .withFields({C2F(mSampleRate, value).inRange(1, 655350)})
57                .withSetter((Setter<decltype(*mSampleRate)>::StrictValueWithNoDeps))
58                .build());
59        addParameter(
60                DefineParam(mChannelCount, C2_NAME_STREAM_CHANNEL_COUNT_SETTING)
61                .withDefault(new C2StreamChannelCountInfo::input(0u, 1))
62                .withFields({C2F(mChannelCount, value).inRange(1, 2)})
63                .withSetter(Setter<decltype(*mChannelCount)>::StrictValueWithNoDeps)
64                .build());
65        addParameter(
66                DefineParam(mBitrate, C2_NAME_STREAM_BITRATE_SETTING)
67                .withDefault(new C2BitrateTuning::output(0u, 768000))
68                .withFields({C2F(mBitrate, value).inRange(1, 21000000)})
69                .withSetter(Setter<decltype(*mBitrate)>::NonStrictValueWithNoDeps)
70                .build());
71        addParameter(
72                DefineParam(mInputMaxBufSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE)
73                .withConstValue(new C2StreamMaxBufferSizeInfo::input(0u, 4608))
74                .build());
75    }
76
77    uint32_t getSampleRate() const { return mSampleRate->value; }
78    uint32_t getChannelCount() const { return mChannelCount->value; }
79    uint32_t getBitrate() const { return mBitrate->value; }
80
81private:
82    std::shared_ptr<C2StreamFormatConfig::input> mInputFormat;
83    std::shared_ptr<C2StreamFormatConfig::output> mOutputFormat;
84    std::shared_ptr<C2PortMimeConfig::input> mInputMediaType;
85    std::shared_ptr<C2PortMimeConfig::output> mOutputMediaType;
86    std::shared_ptr<C2StreamSampleRateInfo::input> mSampleRate;
87    std::shared_ptr<C2StreamChannelCountInfo::input> mChannelCount;
88    std::shared_ptr<C2BitrateTuning::output> mBitrate;
89    std::shared_ptr<C2StreamMaxBufferSizeInfo::input> mInputMaxBufSize;
90};
91constexpr char COMPONENT_NAME[] = "c2.android.flac.encoder";
92
93C2SoftFlacEnc::C2SoftFlacEnc(
94        const char *name,
95        c2_node_id_t id,
96        const std::shared_ptr<IntfImpl> &intfImpl)
97    : SimpleC2Component(std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
98      mIntf(intfImpl),
99      mFlacStreamEncoder(nullptr),
100      mInputBufferPcm32(nullptr) {
101}
102
103C2SoftFlacEnc::~C2SoftFlacEnc() {
104    onRelease();
105}
106
107c2_status_t C2SoftFlacEnc::onInit() {
108    mFlacStreamEncoder = FLAC__stream_encoder_new();
109    if (!mFlacStreamEncoder) return C2_CORRUPTED;
110
111    mInputBufferPcm32 = (FLAC__int32*) malloc(
112            kInBlockSize * kMaxNumChannels * sizeof(FLAC__int32));
113    if (!mInputBufferPcm32) return C2_NO_MEMORY;
114
115    mSignalledError = false;
116    mSignalledOutputEos = false;
117    mCompressionLevel = FLAC_COMPRESSION_LEVEL_DEFAULT;
118    mIsFirstFrame = true;
119    mAnchorTimeStamp = 0ull;
120    mProcessedSamples = 0u;
121    mEncoderWriteData = false;
122    mEncoderReturnedNbBytes = 0;
123    mHeaderOffset = 0;
124    mWroteHeader = false;
125
126    status_t err = configureEncoder();
127    return err == OK ? C2_OK : C2_CORRUPTED;
128}
129
130void C2SoftFlacEnc::onRelease() {
131    if (mFlacStreamEncoder) {
132        FLAC__stream_encoder_delete(mFlacStreamEncoder);
133        mFlacStreamEncoder = nullptr;
134    }
135
136    if (mInputBufferPcm32) {
137        free(mInputBufferPcm32);
138        mInputBufferPcm32 = nullptr;
139    }
140}
141
142void C2SoftFlacEnc::onReset() {
143    mCompressionLevel = FLAC_COMPRESSION_LEVEL_DEFAULT;
144    (void) onStop();
145}
146
147c2_status_t C2SoftFlacEnc::onStop() {
148    mSignalledError = false;
149    mSignalledOutputEos = false;
150    mIsFirstFrame = true;
151    mAnchorTimeStamp = 0ull;
152    mProcessedSamples = 0u;
153    mEncoderWriteData = false;
154    mEncoderReturnedNbBytes = 0;
155    mHeaderOffset = 0;
156    mWroteHeader = false;
157
158    c2_status_t status = drain(DRAIN_COMPONENT_NO_EOS, nullptr);
159    if (C2_OK != status) return status;
160
161    status_t err = configureEncoder();
162    if (err != OK) mSignalledError = true;
163    return C2_OK;
164}
165
166c2_status_t C2SoftFlacEnc::onFlush_sm() {
167    return onStop();
168}
169
170static void fillEmptyWork(const std::unique_ptr<C2Work> &work) {
171    work->worklets.front()->output.flags = work->input.flags;
172    work->worklets.front()->output.buffers.clear();
173    work->worklets.front()->output.ordinal = work->input.ordinal;
174}
175
176void C2SoftFlacEnc::process(
177        const std::unique_ptr<C2Work> &work,
178        const std::shared_ptr<C2BlockPool> &pool) {
179    work->result = C2_OK;
180    work->workletsProcessed = 1u;
181    if (mSignalledError || mSignalledOutputEos) {
182        work->result = C2_BAD_VALUE;
183        return;
184    }
185
186    bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
187    C2ReadView rView = mDummyReadView;
188    size_t inOffset = 0u;
189    size_t inSize = 0u;
190    if (!work->input.buffers.empty()) {
191        rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
192        inSize = rView.capacity();
193        if (inSize && rView.error()) {
194            ALOGE("read view map failed %d", rView.error());
195            work->result = C2_CORRUPTED;
196            return;
197        }
198    }
199
200    ALOGV("in buffer attr. size %zu timestamp %d frameindex %d, flags %x",
201              inSize, (int)work->input.ordinal.timestamp.peeku(),
202              (int)work->input.ordinal.frameIndex.peeku(), work->input.flags);
203    if (mIsFirstFrame && inSize) {
204        mAnchorTimeStamp = work->input.ordinal.timestamp.peekull();
205        mIsFirstFrame = false;
206    }
207
208    if (!mWroteHeader) {
209        std::unique_ptr<C2StreamCsdInfo::output> csd =
210            C2StreamCsdInfo::output::AllocUnique(mHeaderOffset, 0u);
211        // TODO: check NO_MEMORY
212        memcpy(csd->m.value, mHeader, mHeaderOffset);
213        ALOGV("put csd, %d bytes", mHeaderOffset);
214
215        work->worklets.front()->output.configUpdate.push_back(std::move(csd));
216        mWroteHeader = true;
217    }
218
219    uint32_t sampleRate = mIntf->getSampleRate();
220    uint32_t channelCount = mIntf->getChannelCount();
221    uint64_t outTimeStamp = mProcessedSamples * 1000000ll / sampleRate;
222
223    size_t outCapacity = inSize;
224    outCapacity += mBlockSize * channelCount * sizeof(int16_t);
225
226    C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
227    c2_status_t err = pool->fetchLinearBlock(outCapacity, usage, &mOutputBlock);
228    if (err != C2_OK) {
229        ALOGE("fetchLinearBlock for Output failed with status %d", err);
230        work->result = C2_NO_MEMORY;
231        return;
232    }
233    C2WriteView wView = mOutputBlock->map().get();
234    if (wView.error()) {
235        ALOGE("write view map failed %d", wView.error());
236        work->result = C2_CORRUPTED;
237        return;
238    }
239
240    mEncoderWriteData = true;
241    mEncoderReturnedNbBytes = 0;
242    size_t inPos = 0;
243    while (inPos < inSize) {
244        const uint8_t *inPtr = rView.data() + inOffset;
245        size_t processSize = MIN(kInBlockSize * channelCount * sizeof(int16_t), (inSize - inPos));
246        const unsigned nbInputFrames = processSize / (channelCount * sizeof(int16_t));
247        const unsigned nbInputSamples = processSize / sizeof(int16_t);
248        const int16_t *pcm16 = reinterpret_cast<const int16_t *>(inPtr + inPos);
249        ALOGV("about to encode %zu bytes", processSize);
250
251        for (unsigned i = 0; i < nbInputSamples; i++) {
252            mInputBufferPcm32[i] = (FLAC__int32) pcm16[i];
253        }
254
255        FLAC__bool ok = FLAC__stream_encoder_process_interleaved(
256                mFlacStreamEncoder, mInputBufferPcm32, nbInputFrames);
257        if (!ok) {
258            ALOGE("error encountered during encoding");
259            mSignalledError = true;
260            work->result = C2_CORRUPTED;
261            mOutputBlock.reset();
262            return;
263        }
264        inPos += processSize;
265    }
266    if (eos && (C2_OK != drain(DRAIN_COMPONENT_WITH_EOS, pool))) {
267        ALOGE("error encountered during encoding");
268        mSignalledError = true;
269        work->result = C2_CORRUPTED;
270        mOutputBlock.reset();
271        return;
272    }
273    fillEmptyWork(work);
274    if (mEncoderReturnedNbBytes != 0) {
275        std::shared_ptr<C2Buffer> buffer = createLinearBuffer(std::move(mOutputBlock), 0, mEncoderReturnedNbBytes);
276        work->worklets.front()->output.buffers.push_back(buffer);
277        work->worklets.front()->output.ordinal.timestamp = mAnchorTimeStamp + outTimeStamp;
278    } else {
279        ALOGV("encoder process_interleaved returned without data to write");
280    }
281    mOutputBlock = nullptr;
282    if (eos) {
283        mSignalledOutputEos = true;
284        ALOGV("signalled EOS");
285    }
286    mEncoderWriteData = false;
287    mEncoderReturnedNbBytes = 0;
288}
289
290FLAC__StreamEncoderWriteStatus C2SoftFlacEnc::onEncodedFlacAvailable(
291        const FLAC__byte buffer[], size_t bytes, unsigned samples,
292        unsigned current_frame) {
293    (void) current_frame;
294    ALOGV("%s (bytes=%zu, samples=%u, curr_frame=%u)", __func__, bytes, samples,
295          current_frame);
296
297    if (samples == 0) {
298        ALOGI("saving %zu bytes of header", bytes);
299        memcpy(mHeader + mHeaderOffset, buffer, bytes);
300        mHeaderOffset += bytes;// will contain header size when finished receiving header
301        return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
302    }
303
304    if ((samples == 0) || !mEncoderWriteData) {
305        // called by the encoder because there's header data to save, but it's not the role
306        // of this component (unless WRITE_FLAC_HEADER_IN_FIRST_BUFFER is defined)
307        ALOGV("ignoring %zu bytes of header data (samples=%d)", bytes, samples);
308        return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
309    }
310
311    // write encoded data
312    C2WriteView wView = mOutputBlock->map().get();
313    uint8_t* outData = wView.data();
314    ALOGV("writing %zu bytes of encoded data on output", bytes);
315    // increment mProcessedSamples to maintain audio synchronization during
316    // play back
317    mProcessedSamples += samples;
318    if (bytes + mEncoderReturnedNbBytes > mOutputBlock->capacity()) {
319        ALOGE("not enough space left to write encoded data, dropping %zu bytes", bytes);
320        // a fatal error would stop the encoding
321        return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
322    }
323    memcpy(outData + mEncoderReturnedNbBytes, buffer, bytes);
324    mEncoderReturnedNbBytes += bytes;
325    return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
326}
327
328
329status_t C2SoftFlacEnc::configureEncoder() {
330    ALOGV("%s numChannel=%d, sampleRate=%d", __func__, mIntf->getChannelCount(), mIntf->getSampleRate());
331
332    if (mSignalledError || !mFlacStreamEncoder) {
333        ALOGE("can't configure encoder: no encoder or invalid state");
334        return UNKNOWN_ERROR;
335    }
336
337    FLAC__bool ok = true;
338    ok = ok && FLAC__stream_encoder_set_channels(mFlacStreamEncoder, mIntf->getChannelCount());
339    ok = ok && FLAC__stream_encoder_set_sample_rate(mFlacStreamEncoder, mIntf->getSampleRate());
340    ok = ok && FLAC__stream_encoder_set_bits_per_sample(mFlacStreamEncoder, 16);
341    ok = ok && FLAC__stream_encoder_set_compression_level(mFlacStreamEncoder, mCompressionLevel);
342    ok = ok && FLAC__stream_encoder_set_verify(mFlacStreamEncoder, false);
343    if (!ok) {
344        ALOGE("unknown error when configuring encoder");
345        return UNKNOWN_ERROR;
346    }
347
348    ok &= FLAC__STREAM_ENCODER_INIT_STATUS_OK ==
349            FLAC__stream_encoder_init_stream(mFlacStreamEncoder,
350                    flacEncoderWriteCallback    /*write_callback*/,
351                    nullptr /*seek_callback*/,
352                    nullptr /*tell_callback*/,
353                    nullptr /*metadata_callback*/,
354                    (void *) this /*client_data*/);
355
356    if (!ok) {
357        ALOGE("unknown error when configuring encoder");
358        return UNKNOWN_ERROR;
359    }
360
361    mBlockSize = FLAC__stream_encoder_get_blocksize(mFlacStreamEncoder);
362
363    ALOGV("encoder successfully configured");
364    return OK;
365}
366
367FLAC__StreamEncoderWriteStatus C2SoftFlacEnc::flacEncoderWriteCallback(
368            const FLAC__StreamEncoder *,
369            const FLAC__byte buffer[],
370            size_t bytes,
371            unsigned samples,
372            unsigned current_frame,
373            void *client_data) {
374    return ((C2SoftFlacEnc*) client_data)->onEncodedFlacAvailable(
375            buffer, bytes, samples, current_frame);
376}
377
378c2_status_t C2SoftFlacEnc::drain(
379        uint32_t drainMode,
380        const std::shared_ptr<C2BlockPool> &pool) {
381    (void) pool;
382    switch (drainMode) {
383        case NO_DRAIN:
384            ALOGW("drain with NO_DRAIN: no-op");
385            return C2_OK;
386        case DRAIN_CHAIN:
387            ALOGW("DRAIN_CHAIN not supported");
388            return C2_OMITTED;
389        case DRAIN_COMPONENT_WITH_EOS:
390            // TODO: This flag is not being sent back to the client
391            // because there are no items in PendingWork queue as all the
392            // inputs are being sent back with emptywork or valid encoded data
393            // mSignalledOutputEos = true;
394        case DRAIN_COMPONENT_NO_EOS:
395            break;
396        default:
397            return C2_BAD_VALUE;
398    }
399    FLAC__bool ok = FLAC__stream_encoder_finish(mFlacStreamEncoder);
400    if (!ok) return C2_CORRUPTED;
401    mIsFirstFrame = true;
402    mAnchorTimeStamp = 0ull;
403    mProcessedSamples = 0u;
404
405    return C2_OK;
406}
407
408class C2SoftFlacEncFactory : public C2ComponentFactory {
409public:
410    C2SoftFlacEncFactory() : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
411            GetCodec2PlatformComponentStore()->getParamReflector())) {
412    }
413
414    virtual c2_status_t createComponent(
415            c2_node_id_t id,
416            std::shared_ptr<C2Component>* const component,
417            std::function<void(C2Component*)> deleter) override {
418        *component = std::shared_ptr<C2Component>(
419                new C2SoftFlacEnc(COMPONENT_NAME,
420                                  id,
421                                  std::make_shared<C2SoftFlacEnc::IntfImpl>(mHelper)),
422                deleter);
423        return C2_OK;
424    }
425
426    virtual c2_status_t createInterface(
427            c2_node_id_t id,
428            std::shared_ptr<C2ComponentInterface>* const interface,
429            std::function<void(C2ComponentInterface*)> deleter) override {
430        *interface = std::shared_ptr<C2ComponentInterface>(
431                new SimpleInterface<C2SoftFlacEnc::IntfImpl>(
432                        COMPONENT_NAME, id, std::make_shared<C2SoftFlacEnc::IntfImpl>(mHelper)),
433                deleter);
434        return C2_OK;
435    }
436
437    virtual ~C2SoftFlacEncFactory() override = default;
438private:
439    std::shared_ptr<C2ReflectorHelper> mHelper;
440};
441
442}  // namespace android
443
444extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
445    ALOGV("in %s", __func__);
446    return new ::android::C2SoftFlacEncFactory();
447}
448
449extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
450    ALOGV("in %s", __func__);
451    delete factory;
452}
453