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

Commit 33266061 authored by Jeremy Wu's avatar Jeremy Wu Committed by Automerger Merge Worker
Browse files

Merge changes Ib968001e,I28a0ce41,I59c7c9e9 into main am: 06ad3e93

parents 68e2f1a8 06ad3e93
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++;
+6 −0
Original line number Diff line number Diff line
@@ -44,6 +44,9 @@ pkg_config("target_defaults") {
  } else {
    pkg_deps += [ "protobuf-lite" ]
  }
  if (!(defined(use.bt_nonstandard_codecs) && use.bt_nonstandard_codecs)) {
    defines = [ "EXCLUDE_NONSTANDARD_CODECS" ]
  }
}

install_config("install_dbus_config") {
@@ -81,6 +84,9 @@ source_set("libmmc") {
    "//bt/system/stack/mmc/proto:mmc_service_proto",
    "//bt/system/stack/mmc/codec_server:libcodec_server_hfp_lc3",
  ]
  if (defined(use.bt_nonstandard_codecs) && use.bt_nonstandard_codecs) {
    deps += [ "//bt/system/stack/mmc/codec_server:libcodec_server_a2dp_aac" ]
  }
}

executable("mmc_service") {
+25 −0
Original line number Diff line number Diff line
@@ -14,6 +14,31 @@
#  limitations under the License.
#

source_set("libcodec_server_a2dp_aac") {
  configs += [
    "//bt/system:target_defaults",
    "//bt/system/stack/mmc:target_defaults",
  ]
  include_dirs = [
    "//bt/system",
    "//bt/system/include",
    "//bt/system/internal_include",
    "//bt/system/stack",
    "//bt/system/stack/include",
  ]
  deps = [
    "//bt/system/gd/rust/shim:init_flags_bridge_header",
    "//bt/system/stack/mmc/proto:mmc_config_proto",
  ]
  sources = [ "a2dp_aac_mmc_encoder.cc" ]
  libs = [
    # Following are for AAC using FFmpeg
    "avcodec",
    "avformat",
    "avutil",
  ]
}

source_set("libcodec_server_hfp_lc3"){
  configs += [ "//bt/system/stack/mmc:target_defaults" ]
  include_dirs = [
+249 −0
Original line number Diff line number Diff line
/*
 * Copyright 2023 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.
 */

#include "mmc/codec_server/a2dp_aac_mmc_encoder.h"

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

#include <base/logging.h>
#include <errno.h>

#include "a2dp_aac.h"
#include "mmc/proto/mmc_config.pb.h"

namespace mmc {
namespace {

const int A2DP_AAC_HEADER_LEN = 9;
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;

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

A2dpAacEncoder::A2dpAacEncoder() : avctx_(nullptr) {}

A2dpAacEncoder::~A2dpAacEncoder() { cleanup(); }

int A2dpAacEncoder::init(ConfigParam config) {
  if (!config.has_a2dp_aac_encoder_param()) {
    LOG(ERROR) << "A2DP AAC Encoder params are not set";
    return -EINVAL;
  }

  const AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
  if (!codec) {
    LOG(ERROR) << "Codec not found";
    return -ENOENT;
  }

  if (!avctx_) {
    avctx_ = avcodec_alloc_context3(codec);
    if (!avctx_) {
      LOG(ERROR) << "Cannot allocate context";
      return -EINVAL;
    }
  }

  param_ = config.a2dp_aac_encoder_param();
  const int channel_count = param_.channel_count();
  const int sample_rate = param_.sample_rate();
  const int bit_rate = param_.bit_rate();

  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) << "Invalid number of channels: " << channel_count;
    return -EINVAL;
  }

  if (sample_rate != 44100 && sample_rate != 48000) {
    LOG(ERROR) << "Unsupported sample rate: " << 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);
  if (rc < 0) {
    LOG(ERROR) << "Could not open context: " << rc;
    return -EINVAL;
  }

  return avctx_->frame_size;
}

void A2dpAacEncoder::cleanup() {
  if (avctx_) {
    avcodec_free_context(&avctx_);
    avctx_ = nullptr;
  }
}

int A2dpAacEncoder::transcode(uint8_t* i_buf, int i_len, uint8_t* o_buf,
                              int o_len) {
  int rc;

  AVFrame* frame = av_frame_alloc();
  if (!frame) {
    LOG(ERROR) << "Could not alloc frame";
    return -ENOMEM;
  }

  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) << "Failed to copy channel layout: " << rc;
    av_frame_free(&frame);
    return -EINVAL;
  }

  rc = av_frame_get_buffer(frame, 0);
  if (rc < 0) {
    LOG(ERROR) << "Failed to get buffer for frame: " << rc;
    av_frame_free(&frame);
    return -EIO;
  }

  rc = av_frame_make_writable(frame);
  if (rc < 0) {
    LOG(ERROR) << "Failed to make frame writable: " << rc;
    av_frame_free(&frame);
    return -EIO;
  }

  const int bit_depth = param_.bit_depth();
  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:
        LOG_ASSERT(false) << "Attempting to read " << nbits
                          << " bits as bit depth";
    }

    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;
  }

  AVPacket* pkt = av_packet_alloc();
  if (!pkt) {
    LOG(ERROR) << "Could not alloc packet";
    return -ENOMEM;
  }

  rc = avcodec_send_frame(avctx_, frame);
  if (rc < 0) {
    LOG(ERROR) << "Failed to send frame: " << rc;
    av_frame_free(&frame);
    av_packet_free(&pkt);
    return -EIO;
  }

  rc = avcodec_receive_packet(avctx_, pkt);
  if (rc < 0 && rc != -EAGAIN) {
    LOG(ERROR) << "Failed to receive packet: " << 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 = param_.effective_frame_size();
  if (rc == -EAGAIN || cap < pkt->size + A2DP_AAC_MAX_PREFIX_SIZE) {
    if (rc != -EAGAIN) {
      LOG(WARNING) << "Dropped pkt: size=" << pkt->size << ", cap=" << 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;
}

}  // namespace mmc
Loading