• 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     // Initialize output work
180     work->result = C2_OK;
181     work->workletsProcessed = 1u;
182     work->worklets.front()->output.flags = work->input.flags;
183 
184     if (mSignalledError || mSignalledOutputEos) {
185         work->result = C2_BAD_VALUE;
186         return;
187     }
188 
189     bool eos = ((work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0);
190     C2ReadView rView = mDummyReadView;
191     size_t inOffset = 0u;
192     size_t inSize = 0u;
193     if (!work->input.buffers.empty()) {
194         rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
195         inSize = rView.capacity();
196         if (inSize && rView.error()) {
197             ALOGE("read view map failed %d", rView.error());
198             work->result = C2_CORRUPTED;
199             return;
200         }
201     }
202 
203     ALOGV("in buffer attr. size %zu timestamp %d frameindex %d, flags %x",
204               inSize, (int)work->input.ordinal.timestamp.peeku(),
205               (int)work->input.ordinal.frameIndex.peeku(), work->input.flags);
206     if (mIsFirstFrame && inSize) {
207         mAnchorTimeStamp = work->input.ordinal.timestamp.peekull();
208         mIsFirstFrame = false;
209     }
210 
211     if (!mWroteHeader) {
212         std::unique_ptr<C2StreamCsdInfo::output> csd =
213             C2StreamCsdInfo::output::AllocUnique(mHeaderOffset, 0u);
214         if (!csd) {
215             ALOGE("CSD allocation failed");
216             mSignalledError = true;
217             work->result = C2_NO_MEMORY;
218             return;
219         }
220         memcpy(csd->m.value, mHeader, mHeaderOffset);
221         ALOGV("put csd, %d bytes", mHeaderOffset);
222 
223         work->worklets.front()->output.configUpdate.push_back(std::move(csd));
224         mWroteHeader = true;
225     }
226 
227     uint32_t sampleRate = mIntf->getSampleRate();
228     uint32_t channelCount = mIntf->getChannelCount();
229     uint64_t outTimeStamp = mProcessedSamples * 1000000ll / sampleRate;
230 
231     size_t outCapacity = inSize;
232     outCapacity += mBlockSize * channelCount * sizeof(int16_t);
233 
234     C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
235     c2_status_t err = pool->fetchLinearBlock(outCapacity, usage, &mOutputBlock);
236     if (err != C2_OK) {
237         ALOGE("fetchLinearBlock for Output failed with status %d", err);
238         work->result = C2_NO_MEMORY;
239         return;
240     }
241     C2WriteView wView = mOutputBlock->map().get();
242     if (wView.error()) {
243         ALOGE("write view map failed %d", wView.error());
244         work->result = C2_CORRUPTED;
245         return;
246     }
247 
248     mEncoderWriteData = true;
249     mEncoderReturnedNbBytes = 0;
250     size_t inPos = 0;
251     while (inPos < inSize) {
252         const uint8_t *inPtr = rView.data() + inOffset;
253         size_t processSize = MIN(kInBlockSize * channelCount * sizeof(int16_t), (inSize - inPos));
254         const unsigned nbInputFrames = processSize / (channelCount * sizeof(int16_t));
255         const unsigned nbInputSamples = processSize / sizeof(int16_t);
256         const int16_t *pcm16 = reinterpret_cast<const int16_t *>(inPtr + inPos);
257         ALOGV("about to encode %zu bytes", processSize);
258 
259         for (unsigned i = 0; i < nbInputSamples; i++) {
260             mInputBufferPcm32[i] = (FLAC__int32) pcm16[i];
261         }
262 
263         FLAC__bool ok = FLAC__stream_encoder_process_interleaved(
264                 mFlacStreamEncoder, mInputBufferPcm32, nbInputFrames);
265         if (!ok) {
266             ALOGE("error encountered during encoding");
267             mSignalledError = true;
268             work->result = C2_CORRUPTED;
269             mOutputBlock.reset();
270             return;
271         }
272         inPos += processSize;
273     }
274     if (eos && (C2_OK != drain(DRAIN_COMPONENT_WITH_EOS, pool))) {
275         ALOGE("error encountered during encoding");
276         mSignalledError = true;
277         work->result = C2_CORRUPTED;
278         mOutputBlock.reset();
279         return;
280     }
281     fillEmptyWork(work);
282     if (mEncoderReturnedNbBytes != 0) {
283         std::shared_ptr<C2Buffer> buffer = createLinearBuffer(std::move(mOutputBlock), 0, mEncoderReturnedNbBytes);
284         work->worklets.front()->output.buffers.push_back(buffer);
285         work->worklets.front()->output.ordinal.timestamp = mAnchorTimeStamp + outTimeStamp;
286     } else {
287         ALOGV("encoder process_interleaved returned without data to write");
288     }
289     mOutputBlock = nullptr;
290     if (eos) {
291         mSignalledOutputEos = true;
292         ALOGV("signalled EOS");
293     }
294     mEncoderWriteData = false;
295     mEncoderReturnedNbBytes = 0;
296 }
297 
onEncodedFlacAvailable(const FLAC__byte buffer[],size_t bytes,unsigned samples,unsigned current_frame)298 FLAC__StreamEncoderWriteStatus C2SoftFlacEnc::onEncodedFlacAvailable(
299         const FLAC__byte buffer[], size_t bytes, unsigned samples,
300         unsigned current_frame) {
301     (void) current_frame;
302     ALOGV("%s (bytes=%zu, samples=%u, curr_frame=%u)", __func__, bytes, samples,
303           current_frame);
304 
305     if (samples == 0) {
306         ALOGI("saving %zu bytes of header", bytes);
307         memcpy(mHeader + mHeaderOffset, buffer, bytes);
308         mHeaderOffset += bytes;// will contain header size when finished receiving header
309         return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
310     }
311 
312     if ((samples == 0) || !mEncoderWriteData) {
313         // called by the encoder because there's header data to save, but it's not the role
314         // of this component (unless WRITE_FLAC_HEADER_IN_FIRST_BUFFER is defined)
315         ALOGV("ignoring %zu bytes of header data (samples=%d)", bytes, samples);
316         return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
317     }
318 
319     // write encoded data
320     C2WriteView wView = mOutputBlock->map().get();
321     uint8_t* outData = wView.data();
322     ALOGV("writing %zu bytes of encoded data on output", bytes);
323     // increment mProcessedSamples to maintain audio synchronization during
324     // play back
325     mProcessedSamples += samples;
326     if (bytes + mEncoderReturnedNbBytes > mOutputBlock->capacity()) {
327         ALOGE("not enough space left to write encoded data, dropping %zu bytes", bytes);
328         // a fatal error would stop the encoding
329         return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
330     }
331     memcpy(outData + mEncoderReturnedNbBytes, buffer, bytes);
332     mEncoderReturnedNbBytes += bytes;
333     return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
334 }
335 
336 
configureEncoder()337 status_t C2SoftFlacEnc::configureEncoder() {
338     ALOGV("%s numChannel=%d, sampleRate=%d", __func__, mIntf->getChannelCount(), mIntf->getSampleRate());
339 
340     if (mSignalledError || !mFlacStreamEncoder) {
341         ALOGE("can't configure encoder: no encoder or invalid state");
342         return UNKNOWN_ERROR;
343     }
344 
345     FLAC__bool ok = true;
346     ok = ok && FLAC__stream_encoder_set_channels(mFlacStreamEncoder, mIntf->getChannelCount());
347     ok = ok && FLAC__stream_encoder_set_sample_rate(mFlacStreamEncoder, mIntf->getSampleRate());
348     ok = ok && FLAC__stream_encoder_set_bits_per_sample(mFlacStreamEncoder, 16);
349     ok = ok && FLAC__stream_encoder_set_compression_level(mFlacStreamEncoder, mCompressionLevel);
350     ok = ok && FLAC__stream_encoder_set_verify(mFlacStreamEncoder, false);
351     if (!ok) {
352         ALOGE("unknown error when configuring encoder");
353         return UNKNOWN_ERROR;
354     }
355 
356     ok &= FLAC__STREAM_ENCODER_INIT_STATUS_OK ==
357             FLAC__stream_encoder_init_stream(mFlacStreamEncoder,
358                     flacEncoderWriteCallback    /*write_callback*/,
359                     nullptr /*seek_callback*/,
360                     nullptr /*tell_callback*/,
361                     nullptr /*metadata_callback*/,
362                     (void *) this /*client_data*/);
363 
364     if (!ok) {
365         ALOGE("unknown error when configuring encoder");
366         return UNKNOWN_ERROR;
367     }
368 
369     mBlockSize = FLAC__stream_encoder_get_blocksize(mFlacStreamEncoder);
370 
371     ALOGV("encoder successfully configured");
372     return OK;
373 }
374 
flacEncoderWriteCallback(const FLAC__StreamEncoder *,const FLAC__byte buffer[],size_t bytes,unsigned samples,unsigned current_frame,void * client_data)375 FLAC__StreamEncoderWriteStatus C2SoftFlacEnc::flacEncoderWriteCallback(
376             const FLAC__StreamEncoder *,
377             const FLAC__byte buffer[],
378             size_t bytes,
379             unsigned samples,
380             unsigned current_frame,
381             void *client_data) {
382     return ((C2SoftFlacEnc*) client_data)->onEncodedFlacAvailable(
383             buffer, bytes, samples, current_frame);
384 }
385 
drain(uint32_t drainMode,const std::shared_ptr<C2BlockPool> & pool)386 c2_status_t C2SoftFlacEnc::drain(
387         uint32_t drainMode,
388         const std::shared_ptr<C2BlockPool> &pool) {
389     (void) pool;
390     switch (drainMode) {
391         case NO_DRAIN:
392             ALOGW("drain with NO_DRAIN: no-op");
393             return C2_OK;
394         case DRAIN_CHAIN:
395             ALOGW("DRAIN_CHAIN not supported");
396             return C2_OMITTED;
397         case DRAIN_COMPONENT_WITH_EOS:
398             // TODO: This flag is not being sent back to the client
399             // because there are no items in PendingWork queue as all the
400             // inputs are being sent back with emptywork or valid encoded data
401             // mSignalledOutputEos = true;
402         case DRAIN_COMPONENT_NO_EOS:
403             break;
404         default:
405             return C2_BAD_VALUE;
406     }
407     FLAC__bool ok = FLAC__stream_encoder_finish(mFlacStreamEncoder);
408     if (!ok) return C2_CORRUPTED;
409     mIsFirstFrame = true;
410     mAnchorTimeStamp = 0ull;
411     mProcessedSamples = 0u;
412 
413     return C2_OK;
414 }
415 
416 class C2SoftFlacEncFactory : public C2ComponentFactory {
417 public:
C2SoftFlacEncFactory()418     C2SoftFlacEncFactory() : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
419             GetCodec2PlatformComponentStore()->getParamReflector())) {
420     }
421 
createComponent(c2_node_id_t id,std::shared_ptr<C2Component> * const component,std::function<void (C2Component *)> deleter)422     virtual c2_status_t createComponent(
423             c2_node_id_t id,
424             std::shared_ptr<C2Component>* const component,
425             std::function<void(C2Component*)> deleter) override {
426         *component = std::shared_ptr<C2Component>(
427                 new C2SoftFlacEnc(COMPONENT_NAME,
428                                   id,
429                                   std::make_shared<C2SoftFlacEnc::IntfImpl>(mHelper)),
430                 deleter);
431         return C2_OK;
432     }
433 
createInterface(c2_node_id_t id,std::shared_ptr<C2ComponentInterface> * const interface,std::function<void (C2ComponentInterface *)> deleter)434     virtual c2_status_t createInterface(
435             c2_node_id_t id,
436             std::shared_ptr<C2ComponentInterface>* const interface,
437             std::function<void(C2ComponentInterface*)> deleter) override {
438         *interface = std::shared_ptr<C2ComponentInterface>(
439                 new SimpleInterface<C2SoftFlacEnc::IntfImpl>(
440                         COMPONENT_NAME, id, std::make_shared<C2SoftFlacEnc::IntfImpl>(mHelper)),
441                 deleter);
442         return C2_OK;
443     }
444 
445     virtual ~C2SoftFlacEncFactory() override = default;
446 private:
447     std::shared_ptr<C2ReflectorHelper> mHelper;
448 };
449 
450 }  // namespace android
451 
CreateCodec2Factory()452 extern "C" ::C2ComponentFactory* CreateCodec2Factory() {
453     ALOGV("in %s", __func__);
454     return new ::android::C2SoftFlacEncFactory();
455 }
456 
DestroyCodec2Factory(::C2ComponentFactory * factory)457 extern "C" void DestroyCodec2Factory(::C2ComponentFactory* factory) {
458     ALOGV("in %s", __func__);
459     delete factory;
460 }
461