Merge "Added opus_enc_fuzzer and opus_multistream_enc_fuzzer"
diff --git a/fuzzer/Android.bp b/fuzzer/Android.bp
index 35dad5c..ec26e0a 100644
--- a/fuzzer/Android.bp
+++ b/fuzzer/Android.bp
@@ -18,18 +18,14 @@
  * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
  */
 
-cc_fuzz {
-    name: "opus_dec_fuzzer",
-    host_supported:true,
+cc_defaults {
+    name: "opus_fuzz_defaults",
+    host_supported: true,
 
     static_libs: [
         "libopus",
     ],
 
-    srcs: [
-        "opus_dec_fuzzer.cpp",
-    ],
-
     fuzz_config: {
         cc: [
             "android-media-fuzzing-reports@google.com",
@@ -39,23 +35,57 @@
 }
 
 cc_fuzz {
-    name: "opus_multistream_dec_fuzzer",
-    host_supported:true,
-
-    static_libs: [
-        "libopus",
-    ],
+    name: "opus_dec_fuzzer",
 
     srcs: [
         "opus_dec_fuzzer.cpp",
     ],
 
-    cflags: ["-DMULTISTREAM"],
+    defaults: [
+        "opus_fuzz_defaults",
+    ],
+}
 
-    fuzz_config: {
-        cc: [
-            "android-media-fuzzing-reports@google.com",
-        ],
-        componentid: 155276,
-    },
+cc_fuzz {
+    name: "opus_multistream_dec_fuzzer",
+
+    srcs: [
+        "opus_dec_fuzzer.cpp",
+    ],
+
+    cflags: [
+        "-DMULTISTREAM",
+    ],
+
+    defaults: [
+        "opus_fuzz_defaults",
+    ],
+}
+
+cc_fuzz {
+    name: "opus_enc_fuzzer",
+
+    srcs: [
+        "opus_enc_fuzzer.cpp",
+    ],
+
+    defaults: [
+        "opus_fuzz_defaults"
+    ],
+}
+
+cc_fuzz {
+    name: "opus_multistream_enc_fuzzer",
+
+    srcs: [
+        "opus_enc_fuzzer.cpp",
+    ],
+
+    defaults: [
+        "opus_fuzz_defaults"
+    ],
+
+    cflags: [
+        "-DMULTISTREAM",
+    ],
 }
diff --git a/fuzzer/README.md b/fuzzer/README.md
index cd6a680..42b9057 100644
--- a/fuzzer/README.md
+++ b/fuzzer/README.md
@@ -57,6 +57,96 @@
   $ $ANDROID_HOST_OUT/fuzz/x86_64/opus_multistream_dec_fuzzer/opus_multistream_dec_fuzzer CORPUS_DIR
 ```
 
+# Fuzzer for libopus encoder
+
+## Plugin Design Considerations
+The fuzzer plugin for opus encoder is designed based on the understanding of the
+codec and tries to achieve the following:
+
+##### Maximize code coverage
+
+This fuzzer provides support for both single stream and multi stream inputs,
+thus enabling fuzzing for API's provided for single stream as well as multi
+stream.
+Following arguments are passed to OPUS_ENC_CREATE_API:
+
+1. Sampling rate (parameter name: `sampleRate`)
+2. Number of channels (parameter name: `channels`)
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+| `sampleRate` | `8000 ` `12000 ` `16000 ` `24000 ` `48000 ` | An index ranging from 0-4 is calculated using first byte of data which is used to assign value to `sampleRate` from array `kSampleRates`|
+| `channels` (single stream)   | `1 ` `2 ` | Calculated using second byte of data |
+| `channels` (multi stream)   | In range `1 `- `255 ` | Calculated using second byte of data |
+
+Following arguments are passed to OPUS_ENC_CTL_API:
+
+1. OPUS_SET_BITRATE (parameter name: `bitRate`)
+2. OPUS_SET_COMPLEXITY (parameter name: `complexity`)
+3. OPUS_SET_APPLICATION (parameter name: `application`)
+4. OPUS_SET_DTX (parameter name: `setDTX`)
+5. OPUS_SET_SIGNAL (parameter name: `signal`)
+6. OPUS_SET_VBR (parameter name: `setVBR`)
+7. OPUS_SET_VBR_CONSTRAINT (parameter name: `setVBRConstraint`)
+8. OPUS_SET_FORCE_CHANNELS (parameter name: `forceChannel`)
+9. OPUS_SET_MAX_BANDWIDTH (parameter name: `maxBandwidth`)
+10. OPUS_SET_INBAND_FEC (parameter name: `setInbandFec`)
+11. OPUS_SET_PACKET_LOSS_PERC (parameter name: `pktLoss`)
+12. OPUS_SET_LSB_DEPTH (parameter name: `lsbDepth`)
+13. OPUS_SET_PREDICTION_DISABLED (parameter name: `setPredDisable`)
+14. OPUS_SET_EXPERT_FRAME_DURATION (parameter name: `frameSizeEnum`)
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+| `bitRate`   | In range `500 ` to `512000 ` | Calculated using all bits of 3rd, 4th and 5th byte of data |
+| `complexity`   |0.`0 ` 1.`1 ` 2.`2 ` 3.`3 ` 4.`4 ` 5.`5 ` 6.`6 ` 7.`7 ` 8.`8 ` 9.`9 ` 10.`10 ` | Calculated using all bits of 6th byte of data |
+| `application`   | 0.`OPUS_APPLICATION_VOIP ` 1.`OPUS_APPLICATION_AUDIO ` 2.`OPUS_APPLICATION_RESTRICTED_LOWDELAY ` | Calculated using all bits of 7th byte of data |
+| `setDTX`   | 0.`0 ` 1.`1 ` | Calculated using bit 0 of 8th byte of data |
+| `signal`   | 0.`OPUS_AUTO ` 1.`OPUS_SIGNAL_VOICE ` 2.`OPUS_SIGNAL_MUSIC ` | Calculated using bit 0 and bit 1 of 9th byte of data |
+| `setVBR`   | 0.`0 ` 1.`1 `  | Calculated using bit 0 of 10th byte of data |
+| `setVBRConstraint`   | 0.`0 ` 1.`1 ` | Calculated using bit 0 of 11th byte of data |
+| `forceChannel`   | 0.`OPUS_AUTO ` 1.`1 ` 2.`2 ` | Calculated using all bits of 12th byte of data |
+| `maxBandwidth`   | 0.`OPUS_BANDWIDTH_NARROWBAND ` 1.`OPUS_BANDWIDTH_MEDIUMBAND ` 2.`OPUS_BANDWIDTH_WIDEBAND ` 3.`OPUS_BANDWIDTH_SUPERWIDEBAND ` 4.`OPUS_BANDWIDTH_FULLBAND ` | Calculated using all bits of 13th byte of data |
+| `setInbandFec`   | 0.`0 ` 1.`1 ` | Calculated using bit 0 of 14th byte of data |
+| `pktLoss`   | 0.`0 ` 1.`1 ` 2.`2 ` 3.`5 `| Calculated using all bits of 15th byte of data |
+| `lsbDepth`   | 0.`8 ` 1.`24 ` | Calculated using bit 0 of 16th byte of data |
+| `setPredDisable`   | 0.`0 ` 1.`1 ` | Calculated using bit 0 of 17th byte of data |
+| `frameSizeEnum`   | 0.`OPUS_FRAMESIZE_2_5_MS ` 1.`OPUS_FRAMESIZE_5_MS ` 2.`OPUS_FRAMESIZE_10_MS ` 3.`OPUS_FRAMESIZE_20_MS ` 4.`OPUS_FRAMESIZE_40_MS ` 5.`OPUS_FRAMESIZE_60_MS ` 6.`OPUS_FRAMESIZE_80_MS ` 7.`OPUS_FRAMESIZE_100_MS ` 8.`OPUS_FRAMESIZE_120_MS ` | Calculated using all bits of 18th byte of data |
+
+
+##### Maximize utilization of input data
+The plugin feeds the entire input data to the codec. For buffer size which is not a multiple of mNumPcmBytesPerInputFrame, we will accumulate the input and keep it. Once the input is filled with expected number of bytes, we will send it to encoder. This ensures that the plugin tolerates any kind of input (empty, huge,
+malformed, etc) and doesnt `exit()` on any input and thereby increasing the chance of identifying vulnerabilities.
+
+## Build
+
+This describes steps to build opus_enc_fuzzer and opus_multistream_enc_fuzzer.
+
+## Android
+
+### Steps to build
+Build the fuzzer
+```
+  $ mm -j$(nproc) opus_enc_fuzzer
+  $ mm -j$(nproc) opus_multistream_enc_fuzzer
+```
+
+### Steps to run
+Create a directory CORPUS_DIR and copy some raw media files to that folder.
+Push this directory to device.
+
+To run on device
+```
+  $ adb sync data
+  $ adb shell /data/fuzz/arm64/opus_enc_fuzzer/opus_enc_fuzzer CORPUS_DIR
+  $ adb shell /data/fuzz/arm64/opus_multistream_enc_fuzzer/opus_multistream_enc_fuzzer CORPUS_DIR
+```
+To run on host
+```
+  $ $ANDROID_HOST_OUT/fuzz/x86_64/opus_enc_fuzzer/opus_enc_fuzzer CORPUS_DIR
+  $ $ANDROID_HOST_OUT/fuzz/x86_64/opus_multistream_enc_fuzzer/opus_multistream_enc_fuzzer CORPUS_DIR
+```
+
 ## References:
  * http://llvm.org/docs/LibFuzzer.html
  * https://github.com/google/oss-fuzz
diff --git a/fuzzer/opus_enc_fuzzer.cpp b/fuzzer/opus_enc_fuzzer.cpp
new file mode 100644
index 0000000..c2258fd
--- /dev/null
+++ b/fuzzer/opus_enc_fuzzer.cpp
@@ -0,0 +1,314 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *****************************************************************************
+ * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
+ */
+
+#include <algorithm>
+
+#include "opus.h"
+
+using namespace std;
+
+constexpr int kFrameDuration = 50;
+constexpr int kMaxPacket = 1500;
+constexpr int kMinBitRate = 500;
+constexpr int kMaxBitRate = 512000;
+
+constexpr opus_int32 kSampleRates[] = {8000, 12000, 16000, 24000, 48000};
+constexpr size_t kSampleRatesSize = size(kSampleRates);
+
+#ifndef MULTISTREAM
+constexpr int kChannels[] = {1, 2};
+constexpr size_t kChannelsSize = size(kChannels);
+#endif
+
+constexpr int kApplications[] = {OPUS_APPLICATION_VOIP, OPUS_APPLICATION_AUDIO,
+                                 OPUS_APPLICATION_RESTRICTED_LOWDELAY};
+constexpr size_t kApplicationsSize = size(kApplications);
+
+constexpr int kSignals[] = {OPUS_AUTO, OPUS_SIGNAL_VOICE, OPUS_SIGNAL_MUSIC};
+constexpr size_t kSignalsSize = size(kSignals);
+
+constexpr int kSetDTX[] = {0, 1};
+constexpr size_t kSetDTXSize = size(kSetDTX);
+
+constexpr int kSetVBR[] = {0, 1};
+constexpr size_t kSetVBRSize = size(kSetVBR);
+
+constexpr int kSetInbandFec[] = {0, 1};
+constexpr size_t kSetInbandFecSize = size(kSetInbandFec);
+
+constexpr int kSetVBRConstraint[] = {0, 1};
+constexpr size_t kSetVBRConstraintSize = size(kSetVBRConstraint);
+
+constexpr int kSetPredDisable[] = {0, 1};
+constexpr size_t kSetPredDisableSize = size(kSetPredDisable);
+
+constexpr int kComplexities[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+constexpr size_t kComplexitiesSize = size(kComplexities);
+
+constexpr int kForceChannels[] = {OPUS_AUTO, 1, 2};
+constexpr size_t kForceChannelsSize = size(kForceChannels);
+
+constexpr int kMaxBandwidths[] = {OPUS_BANDWIDTH_NARROWBAND, OPUS_BANDWIDTH_MEDIUMBAND,
+                                  OPUS_BANDWIDTH_WIDEBAND, OPUS_BANDWIDTH_SUPERWIDEBAND,
+                                  OPUS_BANDWIDTH_FULLBAND};
+constexpr size_t kMaxBandwidthsSize = size(kMaxBandwidths);
+
+constexpr int kPacketLossPerc[] = {0, 1, 2, 5};
+constexpr size_t kPacketLossPercSize = size(kPacketLossPerc);
+
+constexpr int kLsbDepths[] = {8, 24};
+constexpr size_t kLsbDepthsSize = size(kLsbDepths);
+
+constexpr int kFrameDurations[] = {
+    OPUS_FRAMESIZE_2_5_MS, OPUS_FRAMESIZE_5_MS,   OPUS_FRAMESIZE_10_MS,
+    OPUS_FRAMESIZE_20_MS,  OPUS_FRAMESIZE_40_MS,  OPUS_FRAMESIZE_60_MS,
+    OPUS_FRAMESIZE_80_MS,  OPUS_FRAMESIZE_100_MS, OPUS_FRAMESIZE_120_MS};
+constexpr size_t kFrameDurationsSize = size(kFrameDurations);
+
+#ifdef MULTISTREAM
+#include "opus_multistream.h"
+#define OPUS_ENC_DATA_TYPE OpusMSEncoder
+#define OPUS_ENC_ENCODE_API opus_multistream_encode
+#define OPUS_ENC_CTL_API opus_multistream_encoder_ctl
+#define OPUS_ENC_CREATE_API ms_opus_encoder_create
+#define OPUS_ENC_DESTROY_API opus_multistream_encoder_destroy
+static OpusMSEncoder* ms_opus_encoder_create(opus_int32 sampleRate, int channels, int application,
+                                             int* error) {
+    unsigned char* mapping = (unsigned char*)malloc(sizeof(unsigned char) * channels);
+    if (!mapping) {
+        *error = 1;
+        return nullptr;
+    }
+    for (unsigned char x = 0; x < channels; ++x) {
+        mapping[x] = x;
+    }
+    OpusMSEncoder* enc = opus_multistream_encoder_create(sampleRate, channels, 1, channels - 1,
+                                                         mapping, application, error);
+    free(mapping);
+    return enc;
+}
+#else
+#define OPUS_ENC_DATA_TYPE OpusEncoder
+#define OPUS_ENC_ENCODE_API opus_encode
+#define OPUS_ENC_CTL_API opus_encoder_ctl
+#define OPUS_ENC_CREATE_API opus_encoder_create
+#define OPUS_ENC_DESTROY_API opus_encoder_destroy
+#endif
+
+enum {
+    IDX_SAMPLE_RATE_INDEX = 0,
+    IDX_CHANNEL,
+    IDX_BIT_RATE_1,
+    IDX_BIT_RATE_2,
+    IDX_BIT_RATE_3,
+    IDX_COMPLEXITY,
+    IDX_APPLICATION,
+    IDX_SET_DTX,
+    IDX_SET_SIGNAL,
+    IDX_SET_VBR,
+    IDX_SET_VBR_CONSTRAINT,
+    IDX_FORCE_CHANNEL_INDEX,
+    IDX_SET_MAX_BANDWIDTH,
+    IDX_SET_INBAND_FEC,
+    IDX_SET_PACKET_LOSS_PERC,
+    IDX_SET_LSB_DEPTH,
+    IDX_SET_PREDICTION_DISABLED,
+    IDX_FRAME_ENUM,
+    IDX_LAST
+};
+
+template <typename type1, typename type2, typename type3>
+auto generateNumberInRangeFromData(type1 data, type2 min, type3 max) -> decltype(max) {
+    return (data % (1 + max - min)) + min;
+}
+
+class Codec {
+   public:
+    ~Codec() { deInitEncoder(); }
+    bool initEncoder(uint8_t** dataPtr, size_t* sizePtr);
+    void encodeFrames(const uint8_t* data, size_t size);
+    void deInitEncoder();
+
+   private:
+    OPUS_ENC_DATA_TYPE* mEncoder = nullptr;
+    int mChannels = 0;
+    int mNumSamplesPerFrame = 0;
+    size_t mFrameSize = 0;
+    size_t mNumPcmBytesPerInputFrame = 0;
+};
+
+int get_frame_size(int frameSizeEnum, int samplingRate) {
+    int frameSize = 0;
+    switch (frameSizeEnum) {
+        case OPUS_FRAMESIZE_2_5_MS:
+            frameSize = samplingRate / 400;
+            break;
+        case OPUS_FRAMESIZE_5_MS:
+            frameSize = samplingRate / 200;
+            break;
+        case OPUS_FRAMESIZE_10_MS:
+            frameSize = samplingRate / 100;
+            break;
+        case OPUS_FRAMESIZE_20_MS:
+            frameSize = samplingRate / 50;
+            break;
+        case OPUS_FRAMESIZE_40_MS:
+            frameSize = samplingRate / 25;
+            break;
+        case OPUS_FRAMESIZE_60_MS:
+            frameSize = 3 * samplingRate / 50;
+            break;
+        case OPUS_FRAMESIZE_80_MS:
+            frameSize = 4 * samplingRate / 50;
+            break;
+        case OPUS_FRAMESIZE_100_MS:
+            frameSize = 5 * samplingRate / 50;
+            break;
+        case OPUS_FRAMESIZE_120_MS:
+            frameSize = 6 * samplingRate / 50;
+            break;
+        default:
+            break;
+    }
+    return frameSize;
+}
+
+bool Codec::initEncoder(uint8_t** dataPtr, size_t* sizePtr) {
+    uint8_t* data = *dataPtr;
+
+    int sampleRateIndex = data[IDX_SAMPLE_RATE_INDEX] % kSampleRatesSize;
+    opus_int32 sampleRate = kSampleRates[sampleRateIndex];
+
+#ifdef MULTISTREAM
+    mChannels = generateNumberInRangeFromData(data[IDX_CHANNEL], 1, 255);
+#else
+    int channelIndex = data[IDX_CHANNEL] % kChannelsSize;
+    mChannels = kChannels[channelIndex];
+#endif
+
+    mNumSamplesPerFrame = sampleRate / kFrameDuration;
+    mNumPcmBytesPerInputFrame = mChannels * mNumSamplesPerFrame * sizeof(int16_t);
+
+    int application = kApplications[data[IDX_APPLICATION] % kApplicationsSize];
+    int err = 0;
+    mEncoder = OPUS_ENC_CREATE_API(sampleRate, mChannels, application, &err);
+    if (err) {
+        return false;
+    }
+    OPUS_ENC_CTL_API(mEncoder, OPUS_SET_APPLICATION(application));
+
+    int complexityIndex = data[IDX_COMPLEXITY] % kComplexitiesSize;
+    int complexity = kComplexities[complexityIndex];
+    OPUS_ENC_CTL_API(mEncoder, OPUS_SET_COMPLEXITY(complexity));
+
+    int setDTXIndex = data[IDX_SET_DTX] % kSetDTXSize;
+    int setDTX = kSetDTX[setDTXIndex];
+    OPUS_ENC_CTL_API(mEncoder, OPUS_SET_DTX(setDTX));
+
+    int signalIndex = data[IDX_SET_SIGNAL] % kSignalsSize;
+    int signal = kSignals[signalIndex];
+    OPUS_ENC_CTL_API(mEncoder, OPUS_SET_SIGNAL(signal));
+
+    int setVBRIndex = data[IDX_SET_VBR] % kSetVBRSize;
+    int setVBR = kSetVBR[setVBRIndex];
+    OPUS_ENC_CTL_API(mEncoder, OPUS_SET_VBR(setVBR));
+
+    int setVBRConstraintIndex = data[IDX_SET_VBR_CONSTRAINT] % kSetVBRConstraintSize;
+    int setVBRConstraint = kSetVBRConstraint[setVBRConstraintIndex];
+    OPUS_ENC_CTL_API(mEncoder, OPUS_SET_VBR_CONSTRAINT(setVBRConstraint));
+
+    // Clubbing 3 bytes of data to ensure bit rate in the range [kMinBitRate, kMaxBitRate]
+    uint32_t tempValue =
+        (data[IDX_BIT_RATE_1] << 16) | (data[IDX_BIT_RATE_2] << 8) | data[IDX_BIT_RATE_3];
+    uint32_t bitRate = generateNumberInRangeFromData(tempValue, kMinBitRate, kMaxBitRate);
+    OPUS_ENC_CTL_API(mEncoder, OPUS_SET_BITRATE(bitRate));
+
+    int forceChanneIndex = data[IDX_FORCE_CHANNEL_INDEX] % kForceChannelsSize;
+    int forceChannel = kForceChannels[forceChanneIndex];
+    forceChannel = min(forceChannel, mChannels);
+    OPUS_ENC_CTL_API(mEncoder, OPUS_SET_FORCE_CHANNELS(forceChannel));
+
+    int maxBandwidthIndex = data[IDX_SET_MAX_BANDWIDTH] % kMaxBandwidthsSize;
+    opus_int32 maxBandwidth = kMaxBandwidths[maxBandwidthIndex];
+    OPUS_ENC_CTL_API(mEncoder, OPUS_SET_MAX_BANDWIDTH(maxBandwidth));
+
+    int setInbandFecIndex = data[IDX_SET_INBAND_FEC] % kSetInbandFecSize;
+    int setInbandFec = kSetInbandFec[setInbandFecIndex];
+    OPUS_ENC_CTL_API(mEncoder, OPUS_SET_INBAND_FEC(setInbandFec));
+
+    int pktLossIndex = data[IDX_SET_PACKET_LOSS_PERC] % kPacketLossPercSize;
+    int pktLoss = kPacketLossPerc[pktLossIndex];
+    OPUS_ENC_CTL_API(mEncoder, OPUS_SET_PACKET_LOSS_PERC(pktLoss));
+
+    int lsbDepthIndex = data[IDX_SET_LSB_DEPTH] % kLsbDepthsSize;
+    int lsbDepth = kLsbDepths[lsbDepthIndex];
+    OPUS_ENC_CTL_API(mEncoder, OPUS_SET_LSB_DEPTH(lsbDepth));
+
+    int setPredDisableIndex = data[IDX_SET_PREDICTION_DISABLED] % kSetPredDisableSize;
+    int setPredDisable = kSetPredDisable[setPredDisableIndex];
+    OPUS_ENC_CTL_API(mEncoder, OPUS_SET_PREDICTION_DISABLED(setPredDisable));
+
+    int frameSizesEnumIndex = data[IDX_FRAME_ENUM] % kFrameDurationsSize;
+    int frameSizeEnum = kFrameDurations[frameSizesEnumIndex];
+    OPUS_ENC_CTL_API(mEncoder, OPUS_SET_EXPERT_FRAME_DURATION(frameSizeEnum));
+
+    mFrameSize = get_frame_size(frameSizeEnum, sampleRate);
+    if (mFrameSize == 0) {
+        return false;
+    }
+
+    // Not re-using the data which was used for configuration for encoding
+    *dataPtr += IDX_LAST;
+    *sizePtr -= IDX_LAST;
+    return true;
+}
+
+void Codec::encodeFrames(const uint8_t* data, size_t size) {
+    opus_int16* inputBuffer = (opus_int16*)data;
+    size = size / sizeof(opus_int16);
+    size_t offset = 0;
+    do {
+        size_t frameSize = mFrameSize / mChannels;
+        if (frameSize > (size - offset)) {
+            frameSize = size - offset;
+        }
+        unsigned char packet[kMaxPacket];
+        (void)OPUS_ENC_ENCODE_API(mEncoder, &inputBuffer[offset], frameSize, packet, kMaxPacket);
+        offset += mFrameSize * mChannels;
+    } while (offset < size);
+}
+
+void Codec::deInitEncoder() {
+    if (mEncoder) {
+        OPUS_ENC_DESTROY_API(mEncoder);
+        mEncoder = nullptr;
+    }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    if (size < IDX_LAST) {
+        return 0;
+    }
+    Codec encoder;
+    if (encoder.initEncoder(const_cast<uint8_t**>(&data), &size)) {
+        encoder.encodeFrames(data, size);
+    }
+    return 0;
+}