blob: 5034b09c09d59e7828f5c10e7e1db74252803224 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/base/audio_codecs.h"
#include <ostream>
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
namespace media {
// These names come from src/third_party/ffmpeg/libavcodec/codec_desc.c
std::string GetCodecName(AudioCodec codec) {
switch (codec) {
case AudioCodec::kUnknown:
return "unknown";
case AudioCodec::kAAC:
return "aac";
case AudioCodec::kMP3:
return "mp3";
case AudioCodec::kPCM:
case AudioCodec::kPCM_S16BE:
case AudioCodec::kPCM_S24BE:
return "pcm";
case AudioCodec::kVorbis:
return "vorbis";
case AudioCodec::kFLAC:
return "flac";
case AudioCodec::kAMR_NB:
return "amr_nb";
case AudioCodec::kAMR_WB:
return "amr_wb";
case AudioCodec::kPCM_MULAW:
return "pcm_mulaw";
case AudioCodec::kGSM_MS:
return "gsm_ms";
case AudioCodec::kOpus:
return "opus";
case AudioCodec::kIAMF:
return "iamf";
case AudioCodec::kPCM_ALAW:
return "pcm_alaw";
case AudioCodec::kEAC3:
return "eac3";
case AudioCodec::kALAC:
return "alac";
case AudioCodec::kAC3:
return "ac3";
case AudioCodec::kMpegHAudio:
return "mpeg-h-audio";
}
}
std::string GetProfileName(AudioCodecProfile profile) {
switch (profile) {
case AudioCodecProfile::kUnknown:
return "unknown";
case AudioCodecProfile::kXHE_AAC:
return "xhe-aac";
case AudioCodecProfile::kIAMF_SIMPLE:
return "iamf-simple";
case AudioCodecProfile::kIAMF_BASE:
return "iamf-base";
}
}
AudioCodec StringToAudioCodec(const std::string& codec_id) {
if (codec_id == "aac")
return AudioCodec::kAAC;
if (codec_id == "ac-3" || codec_id == "mp4a.A5" || codec_id == "mp4a.a5")
return AudioCodec::kAC3;
if (codec_id == "ec-3" || codec_id == "mp4a.A6" || codec_id == "mp4a.a6")
return AudioCodec::kEAC3;
if (codec_id == "mp3" || codec_id == "mp4a.69" || codec_id == "mp4a.6B")
return AudioCodec::kMP3;
if (codec_id == "alac")
return AudioCodec::kALAC;
if (codec_id == "flac")
return AudioCodec::kFLAC;
if (base::StartsWith(codec_id, "mhm1.", base::CompareCase::SENSITIVE) ||
base::StartsWith(codec_id, "mha1.", base::CompareCase::SENSITIVE)) {
return AudioCodec::kMpegHAudio;
}
if (codec_id == "opus")
return AudioCodec::kOpus;
if (codec_id == "vorbis")
return AudioCodec::kVorbis;
if (base::StartsWith(codec_id, "mp4a.40.", base::CompareCase::SENSITIVE))
return AudioCodec::kAAC;
if (codec_id == "iamf" ||
base::StartsWith(codec_id, "iamf.", base::CompareCase::SENSITIVE)) {
return AudioCodec::kIAMF;
}
return AudioCodec::kUnknown;
}
std::ostream& operator<<(std::ostream& os, const AudioCodec& codec) {
return os << GetCodecName(codec);
}
#if BUILDFLAG(ENABLE_PLATFORM_IAMF_AUDIO)
bool ParseIamfCodecId(std::string_view codec_id,
uint8_t* primary_profilec,
uint8_t* additional_profilec) {
// Reference: Immersive Audio Model and Formats;
// v1.0.0
// 6.3. Codecs Parameter String
// (https://aomediacodec.github.io/iamf/v1.0.0.html#codecsparameter)
if (!(std::string(codec_id).find("iamf") == 0)) {
return false;
}
// For test purposes only, just "iamf" is acceptable.
if (codec_id == "iamf") {
return true;
}
constexpr int kMaxIamfCodecIdLength =
4 // FOURCC string "iamf".
+ 1 // delimiting period.
+ 3 // primary_profile as 3 digit string.
+ 1 // delimiting period.
+ 3 // additional_profile as 3 digit string.
+ 1 // delimiting period.
+ 9; // The remaining string is one of
// "opus", "mp4a.40.2", "flac", "ipcm".
if (codec_id.size() > kMaxIamfCodecIdLength) {
DVLOG(4) << __func__ << ": Codec id is too long (" << codec_id << ")";
return false;
}
std::vector<std::string> elem = base::SplitString(
std::string(codec_id), ".", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
if (elem.size() < 4) {
DVLOG(4) << __func__ << ": invalid IAMF codec id:" << codec_id;
return false;
}
DCHECK_EQ(elem[0], "iamf");
if (StringToAudioCodec(elem[0]) != AudioCodec::kIAMF) {
DVLOG(4) << __func__ << ": invalid IAMF codec id:" << codec_id;
return false;
}
// The primary profile string should be three digits, and should be between 0
// and 255 inclusive.
uint32_t primary_profile = 0;
if (elem[1].size() != 3 || !base::StringToUint(elem[1], &primary_profile) ||
primary_profile > 0xFF) {
DVLOG(4) << __func__ << ": invalid IAMF primary profile: " << elem[1];
return false;
}
// The additional profile string should be three digits, and should be between
// 0 and 255 inclusive.
uint32_t additional_profile = 0;
if (elem[2].size() != 3 ||
!base::StringToUint(elem[2], &additional_profile) ||
additional_profile > 0xFF) {
DVLOG(4) << __func__ << ": invalid IAMF additional profile: " << elem[2];
return false;
}
// The codec string should be one of "opus", "mp4a", "flac", or "ipcm".
std::string codec = base::ToLowerASCII(elem[3]);
if (codec.size() != 4 || ((codec != "opus") && (codec != "mp4a") &&
(codec != "flac") && (codec != "ipcm"))) {
DVLOG(4) << __func__ << ": invalid IAMF stream codec: " << elem[3];
return false;
}
if (codec == "mp4a") {
if (elem.size() != 6) {
DVLOG(4) << __func__
<< ": incorrect mp4a codec string syntax:" << codec_id;
return false;
}
// The fields following "mp4a" should be "40" and "2" to signal AAC-LC.
uint32_t object_type_indication = 0;
if (elem[4].size() != 2 ||
!base::HexStringToUInt(elem[4], &object_type_indication) ||
object_type_indication != 0x40) {
DVLOG(4) << __func__
<< ": invalid mp4a Object Type Indication:" << codec_id;
return false;
}
uint32_t audio_object_type = 0;
if (elem[5].size() != 1 ||
!base::HexStringToUInt(elem[5], &audio_object_type) ||
audio_object_type != 0x02) {
DVLOG(4) << __func__ << ": invalid mp4a Audio Object Type:" << codec_id;
return false;
}
}
if (primary_profilec) {
*primary_profilec = primary_profile;
}
if (additional_profilec) {
*additional_profilec = additional_profile;
}
return true;
}
#endif // BUILDFLAG(ENABLE_PLATFORM_IAMF_AUDIO)
} // namespace media