Skip to content

Commit

Permalink
Make MIME codec parsing safer by accounting for container. (#1313)
Browse files Browse the repository at this point in the history
For the WAV container, "1" is used as the codec value to specify PCM:
https://datatracker.ietf.org/doc/html/rfc2361
To be safe, we should only interpret "1" as PCM for WAV containers.

Test: Added unit tests for AAC, AC3, EAC3, Opus, Vorbis, MP3, FLAC, and
PCM audio codecs.

Bug: b/296479020
  • Loading branch information
antoniori-eng authored Nov 3, 2023
1 parent 859b512 commit 36ddc12
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 4 deletions.
7 changes: 5 additions & 2 deletions starboard/shared/starboard/media/codec_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ bool VideoConfig::operator!=(const VideoConfig& that) const {
return !(*this == that);
}

SbMediaAudioCodec GetAudioCodecFromString(const char* codec) {
SbMediaAudioCodec GetAudioCodecFromString(const char* codec,
const char* subtype) {
#if SB_API_VERSION < 15
const bool kCheckAc3Audio = kSbHasAc3Audio;
#else
Expand Down Expand Up @@ -105,7 +106,9 @@ SbMediaAudioCodec GetAudioCodecFromString(const char* codec) {
}
// For WAV, the "codecs" parameter of a MIME type refers to the WAVE format
// ID, where 1 represents PCM: https://datatracker.ietf.org/doc/html/rfc2361
if (strcmp(codec, "1") == 0) {
const bool is_wav =
strcmp(subtype, "wav") == 0 || strcmp(subtype, "wave") == 0;
if (is_wav && strcmp(codec, "1") == 0) {
return kSbMediaAudioCodecPcm;
}
#endif // SB_API_VERSION >= 14
Expand Down
6 changes: 5 additions & 1 deletion starboard/shared/starboard/media/codec_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,11 @@ class VideoConfig {
optional<AvcParameterSets> avc_parameter_sets_;
};

SbMediaAudioCodec GetAudioCodecFromString(const char* codec);
// Attempts to determine an SbMediaAudioCodec from |codec|, returning
// kSbMediaAudioCodecNone if no match is found. |subtype| may be checked in
// cases of ambiguous codec strings.
SbMediaAudioCodec GetAudioCodecFromString(const char* codec,
const char* subtype);

} // namespace media
} // namespace starboard
Expand Down
55 changes: 55 additions & 0 deletions starboard/shared/starboard/media/codec_util_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include <vector>

#include "starboard/media.h"
#include "starboard/shared/starboard/media/avc_util.h"
#include "testing/gtest/include/gtest/gtest.h"

Expand Down Expand Up @@ -214,6 +215,60 @@ TEST(VideoConfigTest, H264VsVp9) {
EXPECT_FALSE(config_h264 == config_vp9);
}

TEST(CodecUtilTest, ParsesAacCodecs) {
EXPECT_EQ(GetAudioCodecFromString("mp4a.40.2", ""), kSbMediaAudioCodecAac);
EXPECT_EQ(GetAudioCodecFromString("mp4a.40.5", ""), kSbMediaAudioCodecAac);
}

#if SB_API_VERSION < 15
const bool kCheckAc3Audio = kSbHasAc3Audio;
#else
const bool kCheckAc3Audio = true;
#endif // SB_API_VERSION < 15

TEST(CodecUtilTest, ParsesAc3CodecIfEnabled) {
EXPECT_EQ(GetAudioCodecFromString("ac-3", ""),
kCheckAc3Audio ? kSbMediaAudioCodecAc3 : kSbMediaAudioCodecNone);
}

TEST(CodecUtilTest, ParsesEac3CodecIfEnabled) {
EXPECT_EQ(GetAudioCodecFromString("ec-3", ""),
kCheckAc3Audio ? kSbMediaAudioCodecEac3 : kSbMediaAudioCodecNone);
}

TEST(CodecUtilTest, ParsesOpusCodec) {
EXPECT_EQ(GetAudioCodecFromString("opus", ""), kSbMediaAudioCodecOpus);
}

TEST(CodecUtilTest, ParsesVorbisCodec) {
EXPECT_EQ(GetAudioCodecFromString("vorbis", ""), kSbMediaAudioCodecVorbis);
}

#if SB_API_VERSION >= 14
TEST(CodecUtilTest, ParsesMp3Codecs) {
EXPECT_EQ(GetAudioCodecFromString("mp3", ""), kSbMediaAudioCodecMp3);
EXPECT_EQ(GetAudioCodecFromString("mp4a.69", ""), kSbMediaAudioCodecMp3);
EXPECT_EQ(GetAudioCodecFromString("mp4a.6B", ""), kSbMediaAudioCodecMp3);
}

TEST(CodecUtilTest, ParsesFlacCodec) {
EXPECT_EQ(GetAudioCodecFromString("flac", ""), kSbMediaAudioCodecFlac);
}

TEST(CodecUtilTest, ParsesPcmCodecForWav) {
EXPECT_EQ(GetAudioCodecFromString("1", "wav"), kSbMediaAudioCodecPcm);
EXPECT_EQ(GetAudioCodecFromString("1", "wave"), kSbMediaAudioCodecPcm);
}

TEST(CodecUtilTest, DoesNotParse1AsPcmForNonWavSubtypes) {
EXPECT_EQ(GetAudioCodecFromString("1", ""), kSbMediaAudioCodecNone);
EXPECT_EQ(GetAudioCodecFromString("1", "mp4"), kSbMediaAudioCodecNone);
EXPECT_EQ(GetAudioCodecFromString("1", "mp3"), kSbMediaAudioCodecNone);
EXPECT_EQ(GetAudioCodecFromString("1", "mpeg"), kSbMediaAudioCodecNone);
EXPECT_EQ(GetAudioCodecFromString("1", "webm"), kSbMediaAudioCodecNone);
}
#endif // SB_API_VERSION >= 14

} // namespace
} // namespace media
} // namespace starboard
Expand Down
1 change: 1 addition & 0 deletions starboard/shared/starboard/media/media_tests.gni
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ media_tests_sources = [
"//starboard/shared/starboard/media/media_util_test.cc",
"//starboard/shared/starboard/media/mime_type_test.cc",
"//starboard/shared/starboard/media/mime_util_test.cc",
"//starboard/shared/starboard/media/parsed_mime_info_test.cc",
"//starboard/shared/starboard/media/video_capabilities_test.cc",
"//starboard/shared/starboard/media/vp9_util_test.cc",
]
3 changes: 2 additions & 1 deletion starboard/shared/starboard/media/parsed_mime_info.cc
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ bool ParsedMimeInfo::ParseAudioInfo(const std::string& codec) {
SB_DCHECK(mime_type_.is_valid());
SB_DCHECK(!has_audio_info());

SbMediaAudioCodec audio_codec = GetAudioCodecFromString(codec.c_str());
SbMediaAudioCodec audio_codec =
GetAudioCodecFromString(codec.c_str(), mime_type_.subtype().c_str());
if (audio_codec == kSbMediaAudioCodecNone) {
return false;
}
Expand Down
98 changes: 98 additions & 0 deletions starboard/shared/starboard/media/parsed_mime_info_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright 2023 The Cobalt Authors. All Rights Reserved.
//
// 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 "starboard/shared/starboard/media/parsed_mime_info.h"

#include "starboard/media.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace starboard {
namespace shared {
namespace starboard {
namespace media {
namespace {

#if SB_API_VERSION < 15
const bool kCheckAc3Audio = kSbHasAc3Audio;
#else
const bool kCheckAc3Audio = true;
#endif // SB_API_VERSION < 15

TEST(ParsedMimeInfoTest, ParsesAacLowComplexityCodec) {
ParsedMimeInfo mime_info("audio/mp4; codecs=\"mp4a.40.2\"");
ASSERT_TRUE(mime_info.has_audio_info());
EXPECT_EQ(mime_info.audio_info().codec, kSbMediaAudioCodecAac);
}

TEST(ParsedMimeInfoTest, ParsesAacHighEfficiencyCodec) {
ParsedMimeInfo mime_info("audio/mp4; codecs=\"mp4a.40.5\"");
ASSERT_TRUE(mime_info.has_audio_info());
EXPECT_EQ(mime_info.audio_info().codec, kSbMediaAudioCodecAac);
}

TEST(ParsedMimeInfoTest, ParsesAc3Codec) {
ParsedMimeInfo mime_info("audio/mp4; codecs=\"ac-3\"");
ASSERT_EQ(mime_info.has_audio_info(), kCheckAc3Audio);

if (kCheckAc3Audio) {
EXPECT_EQ(mime_info.audio_info().codec, kSbMediaAudioCodecAc3);
}
}

TEST(ParsedMimeInfoTest, ParsesEac3Codec) {
ParsedMimeInfo mime_info("audio/mp4; codecs=\"ec-3\"");
ASSERT_EQ(mime_info.has_audio_info(), kCheckAc3Audio);

if (kCheckAc3Audio) {
EXPECT_EQ(mime_info.audio_info().codec, kSbMediaAudioCodecEac3);
}
}

TEST(ParsedMimeInfoTest, ParsesOpusCodec) {
ParsedMimeInfo mime_info("audio/webm; codecs=\"opus\"");
ASSERT_TRUE(mime_info.has_audio_info());
EXPECT_EQ(mime_info.audio_info().codec, kSbMediaAudioCodecOpus);
}

TEST(ParsedMimeInfoTest, ParsesVorbisCodec) {
ParsedMimeInfo mime_info("audio/webm; codecs=\"vorbis\"");
ASSERT_TRUE(mime_info.has_audio_info());
EXPECT_EQ(mime_info.audio_info().codec, kSbMediaAudioCodecVorbis);
}

#if SB_API_VERSION >= 14
TEST(ParsedMimeInfoTest, ParsesMp3Codec) {
ParsedMimeInfo mime_info("audio/mpeg; codecs=\"mp3\"");
ASSERT_TRUE(mime_info.has_audio_info());
EXPECT_EQ(mime_info.audio_info().codec, kSbMediaAudioCodecMp3);
}

TEST(ParsedMimeInfoTest, ParsesFlacCodec) {
ParsedMimeInfo mime_info("audio/ogg; codecs=\"flac\"");
ASSERT_TRUE(mime_info.has_audio_info());
EXPECT_EQ(mime_info.audio_info().codec, kSbMediaAudioCodecFlac);
}

TEST(ParsedMimeInfoTest, ParsesPcmCodec) {
ParsedMimeInfo mime_info("audio/wav; codecs=\"1\"");
ASSERT_TRUE(mime_info.has_audio_info());
EXPECT_EQ(mime_info.audio_info().codec, kSbMediaAudioCodecPcm);
}
#endif // SB_API_VERSION >= 14

} // namespace
} // namespace media
} // namespace starboard
} // namespace shared
} // namespace starboard

0 comments on commit 36ddc12

Please sign in to comment.