Commit 95fcf214 authored by vigneshv's avatar vigneshv Committed by Commit bot

media: Enable Opus support in Clank <video> and MSE

Opus audio codec is supported by the android platform starting from Lollipop.
This CL enables canPlayType() support for Opus on Clank and MSE playback of Opus
in Clank. This brings Opus feature parity with Desktop Chromium.

BUG=318436

Review URL: https://codereview.chromium.org/866573004

Cr-Commit-Position: refs/heads/master@{#313549}
parent 897bbc21
......@@ -30,6 +30,8 @@ IPC_STRUCT_TRAITS_BEGIN(media::DemuxerConfigs)
IPC_STRUCT_TRAITS_MEMBER(audio_sampling_rate)
IPC_STRUCT_TRAITS_MEMBER(is_audio_encrypted)
IPC_STRUCT_TRAITS_MEMBER(audio_extra_data)
IPC_STRUCT_TRAITS_MEMBER(audio_codec_delay_ns)
IPC_STRUCT_TRAITS_MEMBER(audio_seek_preroll_ns)
IPC_STRUCT_TRAITS_MEMBER(video_codec)
IPC_STRUCT_TRAITS_MEMBER(video_size)
......
......@@ -736,6 +736,13 @@ bool MediaSourceDelegate::GetDemuxerConfigFromStream(
configs->is_audio_encrypted = config.is_encrypted();
configs->audio_extra_data = std::vector<uint8>(
config.extra_data(), config.extra_data() + config.extra_data_size());
configs->audio_codec_delay_ns = static_cast<int64_t>(
config.codec_delay() *
(static_cast<double>(base::Time::kNanosecondsPerSecond) /
config.samples_per_second()));
configs->audio_seek_preroll_ns =
config.seek_preroll().InMicroseconds() *
base::Time::kNanosecondsPerMicrosecond;
return true;
}
if (!is_audio && video_stream_) {
......
......@@ -66,6 +66,8 @@ void AudioDecoderJob::SetDemuxerConfigs(const DemuxerConfigs& configs) {
config_sampling_rate_ = configs.audio_sampling_rate;
set_is_content_encrypted(configs.is_audio_encrypted);
audio_extra_data_ = configs.audio_extra_data;
audio_codec_delay_ns_ = configs.audio_codec_delay_ns;
audio_seek_preroll_ns_ = configs.audio_seek_preroll_ns;
bytes_per_frame_ = kBytesPerAudioOutputSample * num_channels_;
if (!media_codec_bridge_)
output_sampling_rate_ = config_sampling_rate_;
......@@ -142,7 +144,8 @@ bool AudioDecoderJob::CreateMediaCodecBridgeInternal() {
if (!(static_cast<AudioCodecBridge*>(media_codec_bridge_.get()))->Start(
audio_codec_, config_sampling_rate_, num_channels_, &audio_extra_data_[0],
audio_extra_data_.size(), true, GetMediaCrypto().obj())) {
audio_extra_data_.size(), audio_codec_delay_ns_, audio_seek_preroll_ns_,
true, GetMediaCrypto().obj())) {
media_codec_bridge_.reset();
return false;
}
......
......@@ -62,6 +62,8 @@ class AudioDecoderJob : public MediaDecoderJob {
int num_channels_;
int config_sampling_rate_;
std::vector<uint8> audio_extra_data_;
int64 audio_codec_delay_ns_;
int64 audio_seek_preroll_ns_;
double volume_;
int bytes_per_frame_;
......
......@@ -11,6 +11,8 @@ DemuxerConfigs::DemuxerConfigs()
audio_channels(0),
audio_sampling_rate(0),
is_audio_encrypted(false),
audio_codec_delay_ns(-1),
audio_seek_preroll_ns(-1),
video_codec(kUnknownVideoCodec),
is_video_encrypted(false) {}
......
......@@ -25,6 +25,8 @@ struct MEDIA_EXPORT DemuxerConfigs {
int audio_sampling_rate;
bool is_audio_encrypted;
std::vector<uint8> audio_extra_data;
int64 audio_codec_delay_ns;
int64 audio_seek_preroll_ns;
VideoCodec video_codec;
gfx::Size video_size;
......
......@@ -656,11 +656,16 @@ class MediaCodecBridge {
@CalledByNative
private static void setCodecSpecificData(MediaFormat format, int index, byte[] bytes) {
// Codec Specific Data is set in the MediaFormat as ByteBuffer entries with keys csd-0,
// csd-1, and so on. See: http://developer.android.com/reference/android/media/MediaCodec.html
// for details.
String name = null;
if (index == 0) {
name = "csd-0";
} else if (index == 1) {
name = "csd-1";
} else if (index == 2) {
name = "csd-2";
}
if (name != null) {
format.setByteBuffer(name, ByteBuffer.wrap(bytes));
......
......@@ -41,6 +41,8 @@ static const std::string AudioCodecToAndroidMimeType(const AudioCodec& codec) {
return "audio/mpeg";
case kCodecVorbis:
return "audio/vorbis";
case kCodecOpus:
return "audio/opus";
case kCodecAAC:
return "audio/mp4a-latm";
default:
......@@ -73,6 +75,8 @@ static const std::string CodecTypeToAndroidMimeType(const std::string& codec) {
return "video/x-vnd.on2.vp9";
if (codec == "vorbis")
return "audio/vorbis";
if (codec == "opus")
return "audio/opus";
return std::string();
}
......@@ -92,6 +96,8 @@ static const std::string AndroidMimeTypeToCodecType(const std::string& mime) {
return "mp3";
if (mime == "audio/vorbis")
return "vorbis";
if (mime == "audio/opus")
return "opus";
return std::string();
}
......@@ -525,6 +531,8 @@ bool AudioCodecBridge::Start(const AudioCodec& codec,
int channel_count,
const uint8* extra_data,
size_t extra_data_size,
int64 codec_delay_ns,
int64 seek_preroll_ns,
bool play_audio,
jobject media_crypto) {
JNIEnv* env = AttachCurrentThread();
......@@ -542,8 +550,10 @@ bool AudioCodecBridge::Start(const AudioCodec& codec,
env, j_mime.obj(), sample_rate, channel_count));
DCHECK(!j_format.is_null());
if (!ConfigureMediaFormat(j_format.obj(), codec, extra_data, extra_data_size))
if (!ConfigureMediaFormat(j_format.obj(), codec, extra_data, extra_data_size,
codec_delay_ns, seek_preroll_ns)) {
return false;
}
if (!Java_MediaCodecBridge_configureAudio(
env, media_codec(), j_format.obj(), media_crypto, 0, play_audio)) {
......@@ -556,8 +566,10 @@ bool AudioCodecBridge::Start(const AudioCodec& codec,
bool AudioCodecBridge::ConfigureMediaFormat(jobject j_format,
const AudioCodec& codec,
const uint8* extra_data,
size_t extra_data_size) {
if (extra_data_size == 0)
size_t extra_data_size,
int64 codec_delay_ns,
int64 seek_preroll_ns) {
if (extra_data_size == 0 && codec != kCodecOpus)
return true;
JNIEnv* env = AttachCurrentThread();
......@@ -648,6 +660,33 @@ bool AudioCodecBridge::ConfigureMediaFormat(jobject j_format,
Java_MediaCodecBridge_setFrameHasADTSHeader(env, j_format);
break;
}
case kCodecOpus: {
if (!extra_data || extra_data_size == 0 ||
codec_delay_ns < 0 || seek_preroll_ns < 0) {
LOG(ERROR) << "Invalid Opus Header";
return false;
}
// csd0 - Opus Header
ScopedJavaLocalRef<jbyteArray> csd0 =
base::android::ToJavaByteArray(env, extra_data, extra_data_size);
Java_MediaCodecBridge_setCodecSpecificData(env, j_format, 0, csd0.obj());
// csd1 - Codec Delay
ScopedJavaLocalRef<jbyteArray> csd1 =
base::android::ToJavaByteArray(
env, reinterpret_cast<const uint8*>(&codec_delay_ns),
sizeof(int64_t));
Java_MediaCodecBridge_setCodecSpecificData(env, j_format, 1, csd1.obj());
// csd2 - Seek Preroll
ScopedJavaLocalRef<jbyteArray> csd2 =
base::android::ToJavaByteArray(
env, reinterpret_cast<const uint8*>(&seek_preroll_ns),
sizeof(int64_t));
Java_MediaCodecBridge_setCodecSpecificData(env, j_format, 2, csd2.obj());
break;
}
default:
LOG(ERROR) << "Invalid header encountered for codec: "
<< AudioCodecToAndroidMimeType(codec);
......
......@@ -229,6 +229,7 @@ class AudioCodecBridge : public MediaCodecBridge {
// Start the audio codec bridge.
bool Start(const AudioCodec& codec, int sample_rate, int channel_count,
const uint8* extra_data, size_t extra_data_size,
int64 codec_delay_ns, int64 seek_preroll_ns,
bool play_audio, jobject media_crypto) WARN_UNUSED_RESULT;
// Play the output buffer. This call must be called after
......@@ -244,7 +245,8 @@ class AudioCodecBridge : public MediaCodecBridge {
// Configure the java MediaFormat object with the extra codec data passed in.
bool ConfigureMediaFormat(jobject j_format, const AudioCodec& codec,
const uint8* extra_data, size_t extra_data_size);
const uint8* extra_data, size_t extra_data_size,
int64 codec_delay_ns, int64 seek_preroll_ns);
};
class MEDIA_EXPORT VideoCodecBridge : public MediaCodecBridge {
......
......@@ -160,7 +160,8 @@ TEST(MediaCodecBridgeTest, DoNormal) {
scoped_ptr<media::AudioCodecBridge> media_codec;
media_codec.reset(AudioCodecBridge::Create(kCodecMP3));
ASSERT_TRUE(media_codec->Start(kCodecMP3, 44100, 2, NULL, 0, false, NULL));
ASSERT_TRUE(media_codec->Start(
kCodecMP3, 44100, 2, NULL, 0, 0, 0, false, NULL));
int input_buf_index = -1;
MediaCodecStatus status =
......@@ -229,12 +230,13 @@ TEST(MediaCodecBridgeTest, InvalidVorbisHeader) {
uint8 invalid_first_byte[] = { 0x00, 0xff, 0xff, 0xff, 0xff };
EXPECT_FALSE(media_codec->Start(
kCodecVorbis, 44100, 2, invalid_first_byte, sizeof(invalid_first_byte),
false, NULL));
0, 0, false, NULL));
// Size of the header does not match with the data we passed in.
uint8 invalid_size[] = { 0x02, 0x01, 0xff, 0x01, 0xff };
EXPECT_FALSE(media_codec->Start(
kCodecVorbis, 44100, 2, invalid_size, sizeof(invalid_size), false, NULL));
kCodecVorbis, 44100, 2, invalid_size, sizeof(invalid_size),
0, 0, false, NULL));
// Size of the header is too large.
size_t large_size = 8 * 1024 * 1024 + 2;
......@@ -244,10 +246,33 @@ TEST(MediaCodecBridgeTest, InvalidVorbisHeader) {
very_large_header[i] = 0xff;
very_large_header[large_size - 1] = 0xfe;
EXPECT_FALSE(media_codec->Start(
kCodecVorbis, 44100, 2, very_large_header, 0x80000000, false, NULL));
kCodecVorbis, 44100, 2, very_large_header, 0x80000000,
0, 0, false, NULL));
delete[] very_large_header;
}
TEST(MediaCodecBridgeTest, InvalidOpusHeader) {
SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
scoped_ptr<media::AudioCodecBridge> media_codec;
media_codec.reset(AudioCodecBridge::Create(kCodecOpus));
uint8 dummy_extra_data[] = { 0, 0 };
// Extra Data is NULL.
EXPECT_FALSE(media_codec->Start(
kCodecOpus, 48000, 2, NULL, 0, -1, 0, false, NULL));
// Codec Delay is < 0.
EXPECT_FALSE(media_codec->Start(
kCodecOpus, 48000, 2, dummy_extra_data, sizeof(dummy_extra_data),
-1, 0, false, NULL));
// Seek Preroll is < 0.
EXPECT_FALSE(media_codec->Start(
kCodecOpus, 48000, 2, dummy_extra_data, sizeof(dummy_extra_data),
0, -1, false, NULL));
}
TEST(MediaCodecBridgeTest, PresentationTimestampsDoNotDecrease) {
SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
......
......@@ -284,6 +284,11 @@ static bool VerifyCodec(
base::android::BuildInfo::GetInstance()->sdk_int() < 19) {
return false;
}
// Opus is only supported on Lollipop+ (API Level 21).
if (codec_info->tag == CodecInfo::HISTOGRAM_OPUS &&
base::android::BuildInfo::GetInstance()->sdk_int() < 21) {
return false;
}
#endif
if (video_codecs)
video_codecs->push_back(codec_info->tag);
......
......@@ -475,9 +475,8 @@ static bool IsCodecSupportedOnAndroid(MimeUtil::Codec codec) {
return base::android::BuildInfo::GetInstance()->sdk_int() >= 19;
case MimeUtil::OPUS:
// TODO(vigneshv): Change this similar to the VP9 check once Opus is
// supported on Android (http://crbug.com/318436).
return false;
// Opus is supported only in Lollipop+ (API Level 21).
return base::android::BuildInfo::GetInstance()->sdk_int() >= 21;
case MimeUtil::THEORA:
return false;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment