Commit 9cd3dd70 authored by vigneshv's avatar vigneshv Committed by Commit bot

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

Re-landing the CL reverted here:
https://codereview.chromium.org/866573004

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
TBR=jschuh,rsleevi

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

Cr-Commit-Position: refs/heads/master@{#315132}
parent 1fb332dc
......@@ -31,12 +31,12 @@ const char kPropMaybe[] = "";
const char kOggVideoProbably[] = "probably";
const char kOggVideoMaybe[] = "maybe";
const char kTheoraProbably[] = "probably";
const char kOpusProbably[] = "probably";
const char kOggOpusProbably[] = "probably";
#else
const char kOggVideoProbably[] = "";
const char kOggVideoMaybe[] = "";
const char kTheoraProbably[] = "";
const char kOpusProbably[] = "";
const char kOggOpusProbably[] = "";
#endif // !OS_ANDROID
namespace content {
......@@ -261,13 +261,18 @@ IN_PROC_BROWSER_TEST_F(MediaCanPlayTypeTest, CodecSupportTest_wav) {
}
IN_PROC_BROWSER_TEST_F(MediaCanPlayTypeTest, CodecSupportTest_webm) {
// On Android, VP9 is supported only on KitKat and above (API level 19).
// On Android, VP9 is supported only on KitKat and above (API level 19) and
// Opus is supported only on Lollipop and above (API level 21).
std::string VP9Probably = "probably";
std::string VP9AndOpusProbably = "probably";
std::string OpusProbably = "probably";
#if defined(OS_ANDROID)
VP9AndOpusProbably = "";
if (base::android::BuildInfo::GetInstance()->sdk_int() < 19)
VP9Probably = "";
if (base::android::BuildInfo::GetInstance()->sdk_int() < 21) {
OpusProbably = "";
VP9AndOpusProbably = "";
}
#endif
EXPECT_EQ(kMaybe, CanPlay("'video/webm'"));
......@@ -275,8 +280,8 @@ IN_PROC_BROWSER_TEST_F(MediaCanPlayTypeTest, CodecSupportTest_webm) {
EXPECT_EQ(kProbably, CanPlay("'video/webm; codecs=\"vp8.0\"'"));
EXPECT_EQ(kProbably, CanPlay("'video/webm; codecs=\"vp8, vorbis\"'"));
EXPECT_EQ(kProbably, CanPlay("'video/webm; codecs=\"vp8.0, vorbis\"'"));
EXPECT_EQ(kOpusProbably, CanPlay("'video/webm; codecs=\"vp8, opus\"'"));
EXPECT_EQ(kOpusProbably, CanPlay("'video/webm; codecs=\"vp8.0, opus\"'"));
EXPECT_EQ(OpusProbably, CanPlay("'video/webm; codecs=\"vp8, opus\"'"));
EXPECT_EQ(OpusProbably, CanPlay("'video/webm; codecs=\"vp8.0, opus\"'"));
EXPECT_EQ(VP9Probably, CanPlay("'video/webm; codecs=\"vp9\"'"));
EXPECT_EQ(VP9Probably, CanPlay("'video/webm; codecs=\"vp9.0\"'"));
......@@ -293,8 +298,8 @@ IN_PROC_BROWSER_TEST_F(MediaCanPlayTypeTest, CodecSupportTest_webm) {
EXPECT_EQ(kMaybe, CanPlay("'audio/webm'"));
EXPECT_EQ(kProbably, CanPlay("'audio/webm; codecs=\"vorbis\"'"));
EXPECT_EQ(kOpusProbably, CanPlay("'audio/webm; codecs=\"opus\"'"));
EXPECT_EQ(kOpusProbably, CanPlay("'audio/webm; codecs=\"opus, vorbis\"'"));
EXPECT_EQ(OpusProbably, CanPlay("'audio/webm; codecs=\"opus\"'"));
EXPECT_EQ(OpusProbably, CanPlay("'audio/webm; codecs=\"opus, vorbis\"'"));
EXPECT_EQ(kNot, CanPlay("'audio/webm; codecs=\"vp8\"'"));
EXPECT_EQ(kNot, CanPlay("'audio/webm; codecs=\"vp8.0\"'"));
......@@ -327,8 +332,8 @@ IN_PROC_BROWSER_TEST_F(MediaCanPlayTypeTest, CodecSupportTest_ogg) {
EXPECT_EQ(kMaybe, CanPlay("'audio/ogg'"));
EXPECT_EQ(kProbably, CanPlay("'audio/ogg; codecs=\"vorbis\"'"));
EXPECT_EQ(kOpusProbably, CanPlay("'audio/ogg; codecs=\"opus\"'"));
EXPECT_EQ(kOpusProbably, CanPlay("'audio/ogg; codecs=\"vorbis, opus\"'"));
EXPECT_EQ(kOggOpusProbably, CanPlay("'audio/ogg; codecs=\"opus\"'"));
EXPECT_EQ(kOggOpusProbably, CanPlay("'audio/ogg; codecs=\"vorbis, opus\"'"));
EXPECT_EQ(kNot, CanPlay("'audio/ogg; codecs=\"theora\"'"));
EXPECT_EQ(kNot, CanPlay("'audio/ogg; codecs=\"theora, opus\"'"));
......@@ -339,12 +344,12 @@ IN_PROC_BROWSER_TEST_F(MediaCanPlayTypeTest, CodecSupportTest_ogg) {
EXPECT_EQ(kMaybe, CanPlay("'application/ogg'"));
EXPECT_EQ(kProbably, CanPlay("'application/ogg; codecs=\"vorbis\"'"));
EXPECT_EQ(kTheoraProbably, CanPlay("'application/ogg; codecs=\"theora\"'"));
EXPECT_EQ(kOpusProbably, CanPlay("'application/ogg; codecs=\"opus\"'"));
EXPECT_EQ(kOggOpusProbably, CanPlay("'application/ogg; codecs=\"opus\"'"));
EXPECT_EQ(kTheoraProbably,
CanPlay("'application/ogg; codecs=\"theora, vorbis\"'"));
EXPECT_EQ(kTheoraProbably,
CanPlay("'application/ogg; codecs=\"theora, opus\"'"));
EXPECT_EQ(kOpusProbably,
EXPECT_EQ(kOggOpusProbably,
CanPlay("'application/ogg; codecs=\"opus, vorbis\"'"));
TestOGGUnacceptableCombinations("application/ogg");
......
......@@ -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)
......
......@@ -737,6 +737,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_;
......@@ -143,7 +145,8 @@ MediaDecoderJob::MediaDecoderJobStatus
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 STATUS_FAILURE;
}
......
......@@ -61,6 +61,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,23 @@ class MediaCodecBridge {
@CalledByNative
private static void setCodecSpecificData(MediaFormat format, int index, byte[] bytes) {
String name = null;
if (index == 0) {
name = "csd-0";
} else if (index == 1) {
name = "csd-1";
// 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;
switch (index) {
case 0:
name = "csd-0";
break;
case 1:
name = "csd-1";
break;
case 2:
name = "csd-2";
break;
default:
name = null;
break;
}
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);
......
......@@ -476,9 +476,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