• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
28 namespace android {
29 
30 class C2SoftFlacEnc::IntfImpl : public C2InterfaceHelper {
31 public:
IntfImpl(const std::shared_ptr<C2ReflectorHelper> & helper)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 
getSampleRate() const77     uint32_t getSampleRate() const { return mSampleRate->value; }
getChannelCount() const78     uint32_t getChannelCount() const { return mChannelCount->value; }
getBitrate() const79     uint32_t getBitrate() const { return mBitrate->value; }
80 
81 private:
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 };
91 constexpr char COMPONENT_NAME[] = "c2.android.flac.encoder";
92 
C2SoftFlacEnc(const char * name,c2_node_id_t id,const std::shared_ptr<IntfImpl> & intfImpl)93 C2SoftFlacEnc::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 
~C2SoftFlacEnc()103 C2SoftFlacEnc::~C2SoftFlacEnc() {
104     onRelease();
105 }
106 
onInit()107 c2_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 
onRelease()130 void 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 
onReset()142 void C2SoftFlacEnc::onReset() {
143     mCompressionLevel = FLAC_COMPRESSION_LEVEL_DEFAULT;
144     (void) onStop();
145 }
146 
onStop()147 c2_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 
onFlush_sm()166 c2_status_t C2SoftFlacEnc::onFlush_sm() {
167     return onStop();
168 }
169 
fillEmptyWork(const std::unique_ptr<C2Work> & work)170 static 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 
process(const std::unique_ptr<C2Work> & work,const std::shared_ptr<C2BlockPool> & pool)176 void 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 
onEncodedFlacAvailable(const FLAC__byte buffer[],size_t bytes,unsigned samples,unsigned current_frame)290 FLAC__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 
configureEncoder()329 status_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 
flacEncoderWriteCallback(const FLAC__StreamEncoder *,const FLAC__byte buffer[],size_t bytes,unsigned samples,unsigned current_frame,void * client_data)367 FLAC__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 
drain(uint32_t drainMode,const std::shared_ptr<C2BlockPool> & pool)378 c2_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 
408 class C2SoftFlacEncFactory : public C2ComponentFactory {
409 public:
C2SoftFlacEncFactory()410     C2SoftFlacEncFactory() : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
411             GetCodec2PlatformComponentStore()->getParamReflector())) {
412     }
413 
createComponent(c2_node_id_t id,std::shared_ptr<C2Component> * const component,std::function<void (C2Component *)> deleter)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 
createInterface(c2_node_id_t id,std::shared_ptr<C2ComponentInterface> * const interface,std::function<void (C2ComponentInterface *)> deleter)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;
438 private:
439     std::shared_ptr<C2ReflectorHelper> mHelper;
440 };
441 
442 }  // namespace android
443 
CreateCodec2Factory()444 extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
445     ALOGV("in %s", __func__);
446     return new ::android::C2SoftFlacEncFactory();
447 }
448 
DestroyCodec2Factory(::C2ComponentFactory * factory)449 extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
450     ALOGV("in %s", __func__);
451     delete factory;
452 }
453