Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8ca065e2 authored by Jennifer's avatar Jennifer
Browse files

Floss: replace libavc with |CodecClient|

In this patch, the third party codec is replaced with CodecClient, and
the dependency is also removed.

Bug: 294165109
Tag: #floss
Test: m - None
Change-Id: Ib968001eff309f4be9a6cbcc891430932bb5177b
parent e9fcc3a7
Loading
Loading
Loading
Loading
+6 −8
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ static_library("crypto_toolbox") {
  configs += [ "//bt/system:target_defaults" ]
}

# Nonstandard codecs are associated with 3P libs and must be contained in MMC
source_set("nonstandard_codecs") {
  if (defined(use.bt_nonstandard_codecs) && use.bt_nonstandard_codecs) {
    sources = [
@@ -44,19 +45,16 @@ source_set("nonstandard_codecs") {
      "//bt/system",
      "//bt/system/bta/include",
      "//bt/system/btif/include",
      "//bt/system/include",
      "//bt/system/internal_include",
      "//bt/system/stack",
      "//bt/system/stack/include",
      "//bt/system/utils/include",
    ]

    deps = [ "//bt/system/gd/rust/shim:init_flags_bridge_header" ]

    # b/289017207: external codec libs should not be directly linked
    libs = [
      # Following are for AAC using FFmpeg
      "avcodec",
      "avformat",
      "avutil",
    deps = [
      "//bt/system/gd/rust/shim:init_flags_bridge_header",
      "//bt/system/stack/mmc",
    ]

    configs += [
+40 −179
Original line number Diff line number Diff line
@@ -16,14 +16,6 @@

#define LOG_TAG "a2dp_aac_encoder"

extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/channel_layout.h>
#include <libavutil/common.h>
#include <libavutil/frame.h>
#include <libavutil/samplefmt.h>
}

#include <inttypes.h>
#include <stdio.h>
#include <string.h>
@@ -33,6 +25,8 @@ extern "C" {
#include "a2dp_aac.h"
#include "a2dp_aac_encoder.h"
#include "common/time_util.h"
#include "mmc/codec_client/codec_client.h"
#include "mmc/proto/mmc_config.pb.h"
#include "os/rand.h"
#include "osi/include/allocator.h"
#include "osi/include/log.h"
@@ -44,7 +38,6 @@ const int A2DP_AAC_MAX_LEN_REPR = 4;
const int A2DP_AAC_MAX_PREFIX_SIZE =
    AVDT_MEDIA_HDR_SIZE + A2DP_AAC_HEADER_LEN + A2DP_AAC_MAX_LEN_REPR;

// TODO(b/285999597): Run the APIs in a sandbox.
class FFmpegInterface {
 public:
  // Updates the context and configures codec parameters.
@@ -52,192 +45,61 @@ class FFmpegInterface {
  // Returns:
  //   The (fixed) input pcm frame size that the encoder accepts.
  //   Otherwise a negative errno on error.
  int prepare_context(int sample_rate, int channel_count, int bit_rate) {
    const AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
    if (!codec) {
      LOG_ERROR("%s: Codec not found", __func__);
      return -ENOENT;
    }

    if (!avctx) {
      avctx = avcodec_alloc_context3(codec);
      if (!avctx) {
        LOG_ERROR("%s: Cannot allocate context", __func__);
        return -EINVAL;
      }
    }

    if (channel_count == 1) {
      AVChannelLayout mono = AV_CHANNEL_LAYOUT_MONO;
      av_channel_layout_copy(&avctx->ch_layout, &mono);
    } else if (channel_count == 2) {
      AVChannelLayout stereo = AV_CHANNEL_LAYOUT_STEREO;
      av_channel_layout_copy(&avctx->ch_layout, &stereo);
    } else {
      LOG_ERROR("%s: Invalid number of channels %d", __func__, channel_count);
      return -EINVAL;
    }

    if (sample_rate != 44100 && sample_rate != 48000) {
      LOG_ERROR("%s: Unsupported sample rate %d", __func__, sample_rate);
      return -EINVAL;
    }

    avctx->sample_rate = sample_rate;
    avctx->bit_rate = bit_rate;
    avctx->bit_rate_tolerance = 0;
    avctx->sample_fmt = AV_SAMPLE_FMT_FLTP;

    int rc = avcodec_open2(avctx, codec, NULL);
  int prepare_context(int sample_rate, int channel_count, int bit_rate,
                      int bit_depth, int effective_frame_size) {
    clear_context();
    client = new mmc::CodecClient;

    mmc::AacEncoderParam param;
    param.set_sample_rate(sample_rate);
    param.set_channel_count(channel_count);
    param.set_bit_rate(bit_rate);
    param.set_bit_depth(bit_depth);
    param.set_effective_frame_size(effective_frame_size);

    mmc::ConfigParam config;
    *config.mutable_a2dp_aac_encoder_param() = param;

    int rc = client->init(config);
    if (rc < 0) {
      LOG_ERROR("%s: Could not open context: %d", __func__, rc);
      return -EINVAL;
      LOG_ERROR("%s: Init failed with error message, %s", __func__,
                strerror(-rc));
    }

    return avctx->frame_size;
    return rc;
  }

  void clear_context() {
    if (avctx) {
      avcodec_free_context(&avctx);
      avctx = NULL;
    if (client) {
      client->cleanup();
      delete client;
      client = nullptr;
    }
  }

  // Returns a negative errno if the encoded frame was not produced.
  // Otherwise returns the length of the encoded frame stored in `o_buf`.
  int encode_pcm(uint8_t* i_buf, int i_len, int bit_depth, uint8_t* o_buf) {
    int rc;

    AVFrame* frame = av_frame_alloc();

    frame->nb_samples = avctx->frame_size;
    frame->format = avctx->sample_fmt;
    frame->sample_rate = avctx->sample_rate;

    rc = av_channel_layout_copy(&frame->ch_layout, &avctx->ch_layout);
    if (rc < 0) {
      LOG_ERROR("%s: Failed to copy channel layout: %d", __func__, rc);
      av_frame_free(&frame);
  int encode_pcm(uint8_t* i_buf, int i_len, uint8_t* o_buf, int o_len) {
    if (i_buf == nullptr || o_buf == nullptr) {
      LOG_ERROR("%s: Buffer is null", __func__);
      return -EINVAL;
    }

    rc = av_frame_get_buffer(frame, 0);
    if (rc < 0) {
      LOG_ERROR("%s: Failed to get buffer for frame: %d", __func__, rc);
      av_frame_free(&frame);
      return -EIO;
    }

    rc = av_frame_make_writable(frame);
    if (rc < 0) {
      LOG_ERROR("%s: Failed to make frame writable: %d", __func__, rc);
      av_frame_free(&frame);
      return -EIO;
    }

    const int bytes_per_sample = bit_depth / 8;
    const float scaling_factor = (float)1 / (1 << (bit_depth - 1));

    uint8_t* buff = i_buf;
    float* data[] = {(float*)frame->data[0], (float*)frame->data[1]};

    auto read_pcm = [](uint8_t* buff, int nbits) -> int {
      int pcm = 0;

      switch (nbits) {
        case 16:
          pcm = *((int16_t*)buff);
          break;
        case 24:
          pcm = *buff | *(buff + 1) << 8 | *(buff + 2) << 16;
          pcm |= pcm & 0x00800000 ? 0xff000000 : 0;
          break;
        case 32:
          pcm = *((int32_t*)buff);
          break;
        default:
          ASSERT_LOG(false, "Attempting to read %d bits as bit depth", nbits);
      }

      return pcm;
    };

    for (int i = 0; i < i_len / bytes_per_sample; ++i) {
      *data[i & 1]++ = read_pcm(buff, bit_depth) * scaling_factor;
      buff += bytes_per_sample;
    if (!client) {
      LOG_ERROR("%s: CodecClient does not init", __func__);
      return -ENOENT;
    }

    AVPacket* pkt = av_packet_alloc();
    int rc = client->transcode(i_buf, i_len, o_buf, o_len);

    rc = avcodec_send_frame(avctx, frame);
    if (rc < 0) {
      LOG_ERROR("%s: Failed to send frame: %d", __func__, rc);
      av_frame_free(&frame);
      av_packet_free(&pkt);
      return -EIO;
      LOG_ERROR("%s: Encode failed with error message, %s", __func__,
                strerror(-rc));
    }

    rc = avcodec_receive_packet(avctx, pkt);
    if (rc < 0 && rc != -EAGAIN) {
      LOG_INFO("%s: Failed to receive packet: %d", __func__, rc);
      av_frame_free(&frame);
      av_packet_free(&pkt);
      return -EIO;
    }

    uint8_t* dst = o_buf;

    const uint8_t* header = avctx->sample_rate == 44100 ? A2DP_AAC_HEADER_44100
                                                        : A2DP_AAC_HEADER_48000;

    std::copy(header, header + A2DP_AAC_HEADER_LEN, dst);

    int written = A2DP_AAC_HEADER_LEN;
    dst += written;

    int cap = a2dp_aac_get_effective_frame_size();
    if (rc == -EAGAIN || cap < pkt->size + A2DP_AAC_MAX_PREFIX_SIZE) {
      if (rc != -EAGAIN) {
        LOG_WARN("Dropped pkt: size=%d, cap=%d", pkt->size, cap);
      }
      static uint8_t silent_frame[7] = {
          0x06, 0x21, 0x10, 0x04, 0x60, 0x8c, 0x1c,
      };
      std::copy(silent_frame, std::end(silent_frame), dst);
      dst += sizeof(silent_frame);
      written += sizeof(silent_frame);
    } else {
      int fsize = pkt->size;

      while (fsize >= 255) {
        *(dst++) = 0xff;
        fsize -= 255;
        ++written;
      }
      *(dst++) = fsize;
      ++written;

      std::copy(pkt->data, pkt->data + pkt->size, dst);
      written += pkt->size;
    }

    av_packet_unref(pkt);
    av_frame_free(&frame);
    av_packet_free(&pkt);

    return written;
    return rc;
  }

 private:
  static constexpr uint8_t A2DP_AAC_HEADER_44100[A2DP_AAC_HEADER_LEN] = {
      0x47, 0xfc, 0x00, 0x00, 0xb0, 0x90, 0x80, 0x03, 0x00,
  };
  static constexpr uint8_t A2DP_AAC_HEADER_48000[A2DP_AAC_HEADER_LEN] = {
      0x47, 0xfc, 0x00, 0x00, 0xb0, 0x8c, 0x80, 0x03, 0x00,
  };

  AVCodecContext* avctx;
  mmc::CodecClient* client = nullptr;
};

typedef struct {
@@ -314,8 +176,8 @@ void a2dp_aac_encoder_init(const tA2DP_ENCODER_INIT_PEER_PARAMS* p_peer_params,
  tA2DP_BITS_PER_SAMPLE bits_per_sample =
      a2dp_codec_config->getAudioBitsPerSample();

  int pcm_samples_per_frame =
      codec_intf.prepare_context(sample_rate, channel_count, bit_rate);
  int pcm_samples_per_frame = codec_intf.prepare_context(
      sample_rate, channel_count, bit_rate, bits_per_sample, mtu);

  if (pcm_samples_per_frame < 0) {
    LOG_ERROR("%s: Failed to prepare context: %d", __func__,
@@ -470,9 +332,8 @@ static void a2dp_aac_encode_frames(uint8_t nb_frame) {
    p_buf->layer_specific = 0;

    int written = codec_intf.encode_pcm(
        read_buffer, bytes_read,
        a2dp_aac_encoder_cb.feeding_params.bits_per_sample,
        (uint8_t*)(p_buf + 1) + p_buf->offset);
        read_buffer, bytes_read, (uint8_t*)(p_buf + 1) + p_buf->offset,
        BT_DEFAULT_BUFFER_SIZE - 1 - p_buf->offset);

    if (written < 0) {
      a2dp_aac_encoder_cb.stats.media_read_total_dropped_packets++;