Commit d5368090 authored by acolwell@chromium.org's avatar acolwell@chromium.org

Implement experimental MP3 support for Media Source API.

BUG=280550
TEST=PipelineIntegrationTest.MediaSource_MP3

Review URL: https://chromiumcodereview.appspot.com/23454006

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@221471 0039d316-1c4b-4281-b951-d872f2087c98
parent 51b87118
......@@ -900,6 +900,7 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer(
switches::kEnableGPUClientLogging,
switches::kEnableGpuClientTracing,
switches::kEnableGpuBenchmarking,
switches::kEnableMP3StreamParser,
switches::kEnableMemoryBenchmarking,
switches::kEnableOverlayScrollbars,
switches::kEnableSkiaBenchmarking,
......
include_rules = [
"+gpu",
"+jni",
"+net/http",
"+third_party/ffmpeg",
"+third_party/libvpx",
"+third_party/opus",
......
......@@ -25,6 +25,9 @@ const char kVideoThreads[] = "video-threads";
const char kOverrideEncryptedMediaCanPlayType[] =
"override-encrypted-media-canplaytype";
// Enables MP3 stream parser for Media Source Extensions.
const char kEnableMP3StreamParser[] = "enable-mp3-stream-parser";
#if defined(GOOGLE_TV)
// Use external video surface for video with more than or equal pixels to
// specified value. For example, value of 0 will enable external video surface
......
......@@ -24,6 +24,8 @@ MEDIA_EXPORT extern const char kVideoThreads[];
MEDIA_EXPORT extern const char kOverrideEncryptedMediaCanPlayType[];
MEDIA_EXPORT extern const char kEnableMP3StreamParser[];
#if defined(GOOGLE_TV)
MEDIA_EXPORT extern const char kUseExternalVideoSurfaceThresholdInPixels[];
#endif
......
......@@ -39,6 +39,8 @@ void TestSuiteNoAtExit::Initialize() {
// Run this here instead of main() to ensure an AtExitManager is already
// present.
media::InitializeMediaLibraryForTesting();
CommandLine* cmd_line = CommandLine::ForCurrentProcess();
cmd_line->AppendSwitch(switches::kEnableMP3StreamParser);
}
int main(int argc, char** argv) {
......
......@@ -11,6 +11,7 @@
#include "build/build_config.h"
#include "media/base/decoder_buffer.h"
#include "media/base/media_keys.h"
#include "media/base/media_switches.h"
#include "media/base/test_data_util.h"
#include "media/cdm/aes_decryptor.h"
#include "media/filters/chunk_demuxer.h"
......@@ -33,6 +34,7 @@ static const char kMP4Video[] = "video/mp4; codecs=\"avc1.4D4041\"";
static const char kMP4Audio[] = "audio/mp4; codecs=\"mp4a.40.2\"";
static const char kMP4AudioType[] = "audio/mp4";
static const char kMP4VideoType[] = "video/mp4";
static const char kMP3[] = "audio/mpeg";
// Key used to encrypt test files.
static const uint8 kSecretKey[] = {
......@@ -284,13 +286,29 @@ class MockMediaSource {
}
void DemuxerOpenedTask() {
// This code assumes that |mimetype_| is one of the following forms.
// 1. audio/mpeg
// 2. video/webm;codec="vorbis,vp8".
size_t semicolon = mimetype_.find(";");
std::string type = mimetype_.substr(0, semicolon);
size_t quote1 = mimetype_.find("\"");
size_t quote2 = mimetype_.find("\"", quote1 + 1);
std::string codecStr = mimetype_.substr(quote1 + 1, quote2 - quote1 - 1);
std::string type = mimetype_;
std::vector<std::string> codecs;
Tokenize(codecStr, ",", &codecs);
if (semicolon != std::string::npos) {
type = mimetype_.substr(0, semicolon);
size_t codecs_param_start = mimetype_.find("codecs=\"", semicolon);
CHECK_NE(codecs_param_start, std::string::npos);
codecs_param_start += 8; // Skip over the codecs=".
size_t codecs_param_end = mimetype_.find("\"", codecs_param_start);
CHECK_NE(codecs_param_end, std::string::npos);
std::string codecs_param =
mimetype_.substr(codecs_param_start,
codecs_param_end - codecs_param_start);
Tokenize(codecs_param, ",", &codecs);
}
CHECK_EQ(chunk_demuxer_->AddId(kSourceId, type, codecs), ChunkDemuxer::kOk);
AppendData(initial_append_size_);
......@@ -627,6 +645,27 @@ TEST_F(PipelineIntegrationTest,
}
#if defined(USE_PROPRIETARY_CODECS)
TEST_F(PipelineIntegrationTest, MediaSource_MP3) {
MockMediaSource source("sfx.mp3", kMP3, kAppendWholeFile);
StartPipelineWithMediaSource(&source);
source.EndOfStream();
Play();
EXPECT_TRUE(WaitUntilOnEnded());
}
TEST_F(PipelineIntegrationTest, MediaSource_MP3_Icecast) {
MockMediaSource source("icy_sfx.mp3", kMP3, kAppendWholeFile);
StartPipelineWithMediaSource(&source);
source.EndOfStream();
Play();
EXPECT_TRUE(WaitUntilOnEnded());
}
TEST_F(PipelineIntegrationTest, MediaSource_ConfigChange_MP4) {
MockMediaSource source("bear-640x360-av_frag.mp4", kMP4, kAppendWholeFile);
StartPipelineWithMediaSource(&source);
......
......@@ -10,6 +10,7 @@
#include "base/strings/string_util.h"
#include "media/base/media_log.h"
#include "media/base/media_switches.h"
#include "media/mp3/mp3_stream_parser.h"
#include "media/webm/webm_stream_parser.h"
#if defined(USE_PROPRIETARY_CODECS)
......@@ -28,6 +29,8 @@ struct CodecInfo {
AUDIO,
VIDEO
};
// Update tools/metrics/histograms/histograms.xml if new values are added.
enum HistogramTag {
HISTOGRAM_UNKNOWN,
HISTOGRAM_VP8,
......@@ -37,6 +40,7 @@ struct CodecInfo {
HISTOGRAM_MPEG2AAC,
HISTOGRAM_MPEG4AAC,
HISTOGRAM_EAC3,
HISTOGRAM_MP3,
HISTOGRAM_MAX // Must be the last entry.
};
......@@ -151,6 +155,7 @@ static const CodecInfo* kAudioMP4Codecs[] = {
static StreamParser* BuildMP4Parser(
const std::vector<std::string>& codecs, const LogCB& log_cb) {
std::set<int> audio_object_types;
bool has_sbr = false;
#if defined(ENABLE_EAC3_PLAYBACK)
bool enable_eac3 = CommandLine::ForCurrentProcess()->HasSwitch(
......@@ -179,12 +184,28 @@ static StreamParser* BuildMP4Parser(
return new mp4::MP4StreamParser(audio_object_types, has_sbr);
}
static const CodecInfo kMP3CodecInfo = { NULL, CodecInfo::AUDIO, NULL,
CodecInfo::HISTOGRAM_MP3 };
static const CodecInfo* kAudioMP3Codecs[] = {
&kMP3CodecInfo,
NULL
};
static StreamParser* BuildMP3Parser(
const std::vector<std::string>& codecs, const LogCB& log_cb) {
return new MP3StreamParser();
}
#endif
static const SupportedTypeInfo kSupportedTypeInfo[] = {
{ "video/webm", &BuildWebMParser, kVideoWebMCodecs },
{ "audio/webm", &BuildWebMParser, kAudioWebMCodecs },
#if defined(USE_PROPRIETARY_CODECS)
{ "audio/mpeg", &BuildMP3Parser, kAudioMP3Codecs },
{ "video/mp4", &BuildMP4Parser, kVideoMP4Codecs },
{ "audio/mp4", &BuildMP4Parser, kAudioMP4Codecs },
#endif
......@@ -212,6 +233,7 @@ static bool VerifyCodec(
return false;
}
#endif
if (audio_codecs)
audio_codecs->push_back(codec_info->tag);
return true;
......@@ -253,8 +275,26 @@ static bool CheckTypeAndCodecs(
for (size_t i = 0; i < arraysize(kSupportedTypeInfo); ++i) {
const SupportedTypeInfo& type_info = kSupportedTypeInfo[i];
if (type == type_info.type) {
if (codecs.empty()) {
#if defined(USE_PROPRIETARY_CODECS)
if (type_info.codecs == kAudioMP3Codecs &&
!CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableMP3StreamParser)) {
DVLOG(1) << "MP3StreamParser is not enabled.";
return false;
}
#endif
const CodecInfo* codec_info = type_info.codecs[0];
if (codec_info && !codec_info->pattern &&
VerifyCodec(codec_info, audio_codecs, video_codecs)) {
if (factory_function)
*factory_function = type_info.factory_function;
return true;
}
if (codecs.size() == 0u) {
MEDIA_LOG(log_cb) << "A codecs parameter must be provided for '"
<< type << "'";
return false;
......@@ -275,6 +315,7 @@ static bool CheckTypeAndCodecs(
break; // Since only 1 pattern will match, no need to check others.
}
}
if (!found_codec) {
MEDIA_LOG(log_cb) << "Codec '" << codec_id
<< "' is not supported for '" << type << "'";
......
......@@ -32,7 +32,7 @@ class MEDIA_EXPORT StreamParserFactory {
// |has_video| is true if a video codec was specified.
// Returns NULL otherwise. The values of |has_audio| and |has_video| are
// undefined.
static scoped_ptr<media::StreamParser> Create(
static scoped_ptr<StreamParser> Create(
const std::string& type, const std::vector<std::string>& codecs,
const LogCB& log_cb, bool* has_audio, bool* has_video);
};
......
......@@ -41,6 +41,7 @@
'dependencies': [
'../base/base.gyp:base',
'../crypto/crypto.gyp:crypto',
'../net/net.gyp:net',
'../skia/skia.gyp:skia',
'../third_party/opus/opus.gyp:opus',
'../ui/ui.gyp:ui',
......@@ -382,6 +383,8 @@
'midi/midi_manager_mac.h',
'midi/midi_port_info.cc',
'midi/midi_port_info.h',
'mp3/mp3_stream_parser.cc',
'mp3/mp3_stream_parser.h',
'video/capture/android/video_capture_device_android.cc',
'video/capture/android/video_capture_device_android.h',
'video/capture/fake_video_capture_device.cc',
......
This diff is collapsed.
// Copyright 2013 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.
#ifndef MEDIA_MP3_MP3_STREAM_PARSER_H_
#define MEDIA_MP3_MP3_STREAM_PARSER_H_
#include <set>
#include <vector>
#include "base/basictypes.h"
#include "base/callback.h"
#include "media/base/audio_decoder_config.h"
#include "media/base/audio_timestamp_helper.h"
#include "media/base/byte_queue.h"
#include "media/base/media_export.h"
#include "media/base/stream_parser.h"
namespace media {
class BitReader;
class MEDIA_EXPORT MP3StreamParser : public StreamParser {
public:
MP3StreamParser();
virtual ~MP3StreamParser();
// StreamParser implementation.
virtual void Init(const InitCB& init_cb, const NewConfigCB& config_cb,
const NewBuffersCB& new_buffers_cb,
const NewTextBuffersCB& text_cb,
const NeedKeyCB& need_key_cb,
const AddTextTrackCB& add_text_track_cb,
const NewMediaSegmentCB& new_segment_cb,
const base::Closure& end_of_segment_cb,
const LogCB& log_cb) OVERRIDE;
virtual void Flush() OVERRIDE;
virtual bool Parse(const uint8* buf, int size) OVERRIDE;
private:
enum State {
UNINITIALIZED,
INITIALIZED,
PARSE_ERROR
};
State state_;
InitCB init_cb_;
NewConfigCB config_cb_;
NewBuffersCB new_buffers_cb_;
NewMediaSegmentCB new_segment_cb_;
base::Closure end_of_segment_cb_;
LogCB log_cb_;
ByteQueue queue_;
AudioDecoderConfig config_;
scoped_ptr<AudioTimestampHelper> timestamp_helper_;
bool in_media_segment_;
void ChangeState(State state);
// Parsing functions for various byte stream elements.
// |data| & |size| describe the data available for parsing.
// These functions are expected to consume an entire frame/header.
// It should only return a value greater than 0 when |data| has
// enough bytes to successfully parse & consume the entire element.
//
// |frame_size| - Required parameter that is set to the size of the frame, in
// bytes, including the frame header if the function returns a value > 0.
// |sample_rate| - Optional parameter that is set to the sample rate
// of the frame if this function returns a value > 0.
// |channel_layout| - Optional parameter that is set to the channel_layout
// of the frame if this function returns a value > 0.
// |sample_count| - Optional parameter that is set to the number of samples
// in the frame if this function returns a value > 0.
//
// |sample_rate|, |channel_layout|, |sample_count| may be NULL if the caller
// is not interested in receiving these values from the frame header.
//
// Returns:
// > 0 : The number of bytes parsed.
// 0 : If more data is needed to parse the entire element.
// < 0 : An error was encountered during parsing.
int ParseFrameHeader(const uint8* data, int size,
int* frame_size,
int* sample_rate,
ChannelLayout* channel_layout,
int* sample_count) const;
int ParseMP3Frame(const uint8* data, int size);
int ParseIcecastHeader(const uint8* data, int size);
int ParseID3v1(const uint8* data, int size);
int ParseID3v2(const uint8* data, int size);
// Parses an ID3v2 "sync safe" integer.
// |reader| - A BitReader to read from.
// |value| - Set to the integer value read, if true is returned.
//
// Returns true if the integer was successfully parsed and |value|
// was set.
// Returns false if an error was encountered. The state of |value| is
// undefined when false is returned.
bool ParseSyncSafeInt(BitReader* reader, int32* value);
// Scans |data| for the next valid start code.
// Returns:
// > 0 : The number of bytes that should be skipped to reach the
// next start code..
// 0 : If a valid start code was not found and more data is needed.
// < 0 : An error was encountered during parsing.
int FindNextValidStartCode(const uint8* data, int size) const;
DISALLOW_COPY_AND_ASSIGN(MP3StreamParser);
};
} // namespace media
#endif // MEDIA_MP3_MP3_STREAM_PARSER_H_
......@@ -22606,6 +22606,7 @@ other types of suffix sets.
<int value="5" label="MPEG2 AAC"/>
<int value="6" label="MPEG4 AAC"/>
<int value="7" label="EAC3"/>
<int value="8" label="MP3"/>
</enum>
<enum name="NavigationScheme" type="int">
......
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