summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authormatthewjheaney@chromium.org <matthewjheaney@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-05-23 06:47:53 +0000
committermatthewjheaney@chromium.org <matthewjheaney@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-05-23 06:47:53 +0000
commit71537725fccd65a9c4c009d500276f63424a8710 (patch)
tree48793d06b87a67b4c6a75285db7d06f6ffa0ca66 /media
parent8e7d0b47e587935ff8172520b4778979db2a17a8 (diff)
downloadchromium_src-71537725fccd65a9c4c009d500276f63424a8710.zip
chromium_src-71537725fccd65a9c4c009d500276f63424a8710.tar.gz
chromium_src-71537725fccd65a9c4c009d500276f63424a8710.tar.bz2
Media Source dispatches inband text tracks
The Media Source (WebM) parser now detects the presence of inband text tracks. As frames of inband text (WebVTT cues) are found in the network stream, they are pushed up the media stack. BUG=230708 Review URL: https://chromiumcodereview.appspot.com/13419002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@201716 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/base/stream_parser.h11
-rw-r--r--media/base/text_track.h42
-rw-r--r--media/filters/chunk_demuxer.cc59
-rw-r--r--media/filters/chunk_demuxer.h10
-rw-r--r--media/filters/chunk_demuxer_unittest.cc11
-rw-r--r--media/filters/pipeline_integration_test.cc16
-rw-r--r--media/media.gyp4
-rw-r--r--media/mp4/mp4_stream_parser.cc2
-rw-r--r--media/mp4/mp4_stream_parser.h2
-rw-r--r--media/mp4/mp4_stream_parser_unittest.cc15
-rw-r--r--media/webm/webm_parser.h9
-rw-r--r--media/webm/webm_stream_parser.cc51
-rw-r--r--media/webm/webm_stream_parser.h10
-rw-r--r--media/webm/webm_tracks_parser.h1
-rw-r--r--media/webm/webm_webvtt_parser.cc78
-rw-r--r--media/webm/webm_webvtt_parser.h49
-rw-r--r--media/webm/webm_webvtt_parser_unittest.cc105
17 files changed, 462 insertions, 13 deletions
diff --git a/media/base/stream_parser.h b/media/base/stream_parser.h
index a4a8105..c006471a 100644
--- a/media/base/stream_parser.h
+++ b/media/base/stream_parser.h
@@ -14,6 +14,7 @@
#include "base/time.h"
#include "media/base/media_export.h"
#include "media/base/media_log.h"
+#include "media/base/text_track.h"
namespace media {
@@ -55,6 +56,14 @@ class MEDIA_EXPORT StreamParser {
// error should be signalled.
typedef base::Callback<bool(const BufferQueue&)> NewBuffersCB;
+ // New stream buffers of inband text have been parsed.
+ // First parameter - The text track to which these cues will be added.
+ // Second parameter - A queue of newly parsed buffers.
+ // Return value - True indicates that the buffers are accepted.
+ // False if something was wrong with the buffers and a parsing
+ // error should be signalled.
+ typedef base::Callback<bool(TextTrack*, const BufferQueue&)> NewTextBuffersCB;
+
// Signals the beginning of a new media segment.
// First parameter - The earliest timestamp of all the streams in the segment.
typedef base::Callback<void(base::TimeDelta)> NewMediaSegmentCB;
@@ -78,7 +87,9 @@ class MEDIA_EXPORT StreamParser {
const NewConfigCB& config_cb,
const NewBuffersCB& audio_cb,
const NewBuffersCB& video_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) = 0;
diff --git a/media/base/text_track.h b/media/base/text_track.h
new file mode 100644
index 0000000..b249e54
--- /dev/null
+++ b/media/base/text_track.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2012 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_BASE_TEXT_TRACK_H_
+#define MEDIA_BASE_TEXT_TRACK_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/time.h"
+
+namespace media {
+
+// Specifies the varieties of text tracks.
+enum TextKind {
+ kTextSubtitles,
+ kTextCaptions,
+ kTextDescriptions,
+ kTextMetadata,
+ kTextNone
+};
+
+class TextTrack {
+ public:
+ virtual ~TextTrack() {}
+ virtual void addWebVTTCue(const base::TimeDelta& start,
+ const base::TimeDelta& end,
+ const std::string& id,
+ const std::string& content,
+ const std::string& settings) = 0;
+};
+
+typedef base::Callback<scoped_ptr<TextTrack>
+ (TextKind kind,
+ const std::string& label,
+ const std::string& language)> AddTextTrackCB;
+
+} // namespace media
+
+#endif // MEDIA_BASE_TEXT_TRACK_H_
diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc
index c1232f6..df342cf 100644
--- a/media/filters/chunk_demuxer.cc
+++ b/media/filters/chunk_demuxer.cc
@@ -16,6 +16,7 @@
#include "media/base/stream_parser_buffer.h"
#include "media/base/video_decoder_config.h"
#include "media/filters/stream_parser_factory.h"
+#include "media/webm/webm_webvtt_parser.h"
using base::TimeDelta;
@@ -30,7 +31,9 @@ class SourceState {
const StreamParser::NewConfigCB& config_cb,
const StreamParser::NewBuffersCB& audio_cb,
const StreamParser::NewBuffersCB& video_cb,
+ const StreamParser::NewTextBuffersCB& text_cb,
const StreamParser::NeedKeyCB& need_key_cb,
+ const AddTextTrackCB& add_text_track_cb,
const StreamParser::NewMediaSegmentCB& new_segment_cb,
const LogCB& log_cb);
@@ -68,6 +71,15 @@ class SourceState {
bool OnBuffers(const StreamParser::NewBuffersCB& new_buffers_cb,
const StreamParser::BufferQueue& buffers);
+ // Called by the |stream_parser_| when new text buffers have been parsed. It
+ // applies |timestamp_offset_| to all buffers in |buffers| and then calls
+ // |new_buffers_cb| with the modified buffers.
+ // Returns true on a successful call. Returns false if an error occured while
+ // processing the buffers.
+ bool OnTextBuffers(const StreamParser::NewTextBuffersCB& new_buffers_cb,
+ TextTrack* text_track,
+ const StreamParser::BufferQueue& buffers);
+
// Helper function that adds |timestamp_offset_| to each buffer in |buffers|.
void AdjustBufferTimestamps(const StreamParser::BufferQueue& buffers);
@@ -92,7 +104,9 @@ void SourceState::Init(const StreamParser::InitCB& init_cb,
const StreamParser::NewConfigCB& config_cb,
const StreamParser::NewBuffersCB& audio_cb,
const StreamParser::NewBuffersCB& video_cb,
+ const StreamParser::NewTextBuffersCB& text_cb,
const StreamParser::NeedKeyCB& need_key_cb,
+ const AddTextTrackCB& add_text_track_cb,
const StreamParser::NewMediaSegmentCB& new_segment_cb,
const LogCB& log_cb) {
stream_parser_->Init(init_cb, config_cb,
@@ -100,7 +114,10 @@ void SourceState::Init(const StreamParser::InitCB& init_cb,
base::Unretained(this), audio_cb),
base::Bind(&SourceState::OnBuffers,
base::Unretained(this), video_cb),
+ base::Bind(&SourceState::OnTextBuffers,
+ base::Unretained(this), text_cb),
need_key_cb,
+ add_text_track_cb,
base::Bind(&SourceState::OnNewMediaSegment,
base::Unretained(this), new_segment_cb),
base::Bind(&SourceState::OnEndOfMediaSegment,
@@ -163,6 +180,18 @@ bool SourceState::OnBuffers(const StreamParser::NewBuffersCB& new_buffers_cb,
return new_buffers_cb.Run(buffers);
}
+bool SourceState::OnTextBuffers(
+ const StreamParser::NewTextBuffersCB& new_buffers_cb,
+ TextTrack* text_track,
+ const StreamParser::BufferQueue& buffers) {
+ if (new_buffers_cb.is_null())
+ return false;
+
+ AdjustBufferTimestamps(buffers);
+
+ return new_buffers_cb.Run(text_track, buffers);
+}
+
class ChunkDemuxerStream : public DemuxerStream {
public:
typedef std::deque<scoped_refptr<StreamParserBuffer> > BufferQueue;
@@ -545,11 +574,13 @@ bool ChunkDemuxerStream::GetNextBuffer_Locked(
ChunkDemuxer::ChunkDemuxer(const base::Closure& open_cb,
const NeedKeyCB& need_key_cb,
+ const AddTextTrackCB& add_text_track_cb,
const LogCB& log_cb)
: state_(WAITING_FOR_INIT),
host_(NULL),
open_cb_(open_cb),
need_key_cb_(need_key_cb),
+ add_text_track_cb_(add_text_track_cb),
log_cb_(log_cb),
duration_(kNoTimestamp()),
user_specified_duration_(-1) {
@@ -710,7 +741,9 @@ ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id,
has_audio, has_video),
audio_cb,
video_cb,
+ base::Bind(&ChunkDemuxer::OnTextBuffers, base::Unretained(this)),
base::Bind(&ChunkDemuxer::OnNeedKey, base::Unretained(this)),
+ add_text_track_cb_,
base::Bind(&ChunkDemuxer::OnNewMediaSegment, base::Unretained(this), id),
log_cb_);
@@ -1186,6 +1219,32 @@ bool ChunkDemuxer::OnVideoBuffers(const StreamParser::BufferQueue& buffers) {
return true;
}
+bool ChunkDemuxer::OnTextBuffers(
+ TextTrack* text_track,
+ const StreamParser::BufferQueue& buffers) {
+ lock_.AssertAcquired();
+ DCHECK_NE(state_, SHUTDOWN);
+
+ // TODO(matthewjheaney): IncreaseDurationIfNecessary
+
+ for (StreamParser::BufferQueue::const_iterator itr = buffers.begin();
+ itr != buffers.end(); ++itr) {
+ const StreamParserBuffer* const buffer = itr->get();
+ const base::TimeDelta start = buffer->GetTimestamp();
+ const base::TimeDelta end = start + buffer->GetDuration();
+
+ std::string id, settings, content;
+
+ WebMWebVTTParser::Parse(buffer->GetData(),
+ buffer->GetDataSize(),
+ &id, &settings, &content);
+
+ text_track->addWebVTTCue(start, end, id, content, settings);
+ }
+
+ return true;
+}
+
// TODO(acolwell): Remove bool from StreamParser::NeedKeyCB so that
// this method can be removed and need_key_cb_ can be passed directly
// to the parser.
diff --git a/media/filters/chunk_demuxer.h b/media/filters/chunk_demuxer.h
index a5fd6f0..249f22b 100644
--- a/media/filters/chunk_demuxer.h
+++ b/media/filters/chunk_demuxer.h
@@ -15,6 +15,7 @@
#include "media/base/demuxer.h"
#include "media/base/ranges.h"
#include "media/base/stream_parser.h"
+#include "media/base/text_track.h"
#include "media/filters/source_buffer_stream.h"
namespace media {
@@ -41,9 +42,13 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
// is ready to receive media data via AppenData().
// |need_key_cb| Run when the demuxer determines that an encryption key is
// needed to decrypt the content.
+ // |add_text_track_cb| Run when demuxer detects the presence of an inband
+ // text track.
// |log_cb| Run when parsing error messages need to be logged to the error
// console.
- ChunkDemuxer(const base::Closure& open_cb, const NeedKeyCB& need_key_cb,
+ ChunkDemuxer(const base::Closure& open_cb,
+ const NeedKeyCB& need_key_cb,
+ const AddTextTrackCB& add_text_track_cb,
const LogCB& log_cb);
virtual ~ChunkDemuxer();
@@ -134,6 +139,8 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
const VideoDecoderConfig& video_config);
bool OnAudioBuffers(const StreamParser::BufferQueue& buffers);
bool OnVideoBuffers(const StreamParser::BufferQueue& buffers);
+ bool OnTextBuffers(TextTrack* text_track,
+ const StreamParser::BufferQueue& buffers);
bool OnNeedKey(const std::string& type,
scoped_ptr<uint8[]> init_data,
int init_data_size);
@@ -175,6 +182,7 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
DemuxerHost* host_;
base::Closure open_cb_;
NeedKeyCB need_key_cb_;
+ AddTextTrackCB add_text_track_cb_;
// Callback used to report error strings that can help the web developer
// figure out what is wrong with the content.
LogCB log_cb_;
diff --git a/media/filters/chunk_demuxer_unittest.cc b/media/filters/chunk_demuxer_unittest.cc
index 8a1bc37..a71ffab 100644
--- a/media/filters/chunk_demuxer_unittest.cc
+++ b/media/filters/chunk_demuxer_unittest.cc
@@ -152,7 +152,10 @@ class ChunkDemuxerTest : public testing::Test {
base::Bind(&ChunkDemuxerTest::DemuxerOpened, base::Unretained(this));
ChunkDemuxer::NeedKeyCB need_key_cb =
base::Bind(&ChunkDemuxerTest::DemuxerNeedKey, base::Unretained(this));
- demuxer_.reset(new ChunkDemuxer(open_cb, need_key_cb, LogCB()));
+ AddTextTrackCB add_text_track_cb =
+ base::Bind(&ChunkDemuxerTest::OnTextTrack, base::Unretained(this));
+ demuxer_.reset(new ChunkDemuxer(open_cb, need_key_cb,
+ add_text_track_cb, LogCB()));
}
virtual ~ChunkDemuxerTest() {
@@ -775,6 +778,12 @@ class ChunkDemuxerTest : public testing::Test {
NeedKeyMock(type, init_data.get(), init_data_size);
}
+ scoped_ptr<TextTrack> OnTextTrack(TextKind kind,
+ const std::string& label,
+ const std::string& language) {
+ return scoped_ptr<TextTrack>();
+ }
+
base::MessageLoop message_loop_;
MockDemuxerHost host_;
diff --git a/media/filters/pipeline_integration_test.cc b/media/filters/pipeline_integration_test.cc
index ccbdd6d..db0ce12 100644
--- a/media/filters/pipeline_integration_test.cc
+++ b/media/filters/pipeline_integration_test.cc
@@ -238,6 +238,8 @@ class MockMediaSource {
base::Unretained(this)),
base::Bind(&MockMediaSource::DemuxerNeedKey,
base::Unretained(this)),
+ base::Bind(&MockMediaSource::OnTextTrack,
+ base::Unretained(this)),
LogCB())),
owned_chunk_demuxer_(chunk_demuxer_) {
@@ -325,6 +327,12 @@ class MockMediaSource {
std::string(), std::string(), type, init_data.Pass(), init_data_size);
}
+ scoped_ptr<TextTrack> OnTextTrack(TextKind kind,
+ const std::string& label,
+ const std::string& language) {
+ return scoped_ptr<TextTrack>();
+ }
+
private:
base::FilePath file_path_;
scoped_refptr<DecoderBuffer> file_data_;
@@ -947,4 +955,12 @@ TEST_F(PipelineIntegrationTest, BasicPlayback_VP8A_WebM) {
EXPECT_EQ(last_video_frame_format_, VideoFrame::YV12A);
}
+// Verify that VP8 video with inband text track can be played back.
+TEST_F(PipelineIntegrationTest, BasicPlayback_VP8_WebVTT_WebM) {
+ ASSERT_TRUE(Start(GetTestDataFilePath("bear-vp8-webvtt.webm"),
+ PIPELINE_OK));
+ Play();
+ ASSERT_TRUE(WaitUntilOnEnded());
+}
+
} // namespace media
diff --git a/media/media.gyp b/media/media.gyp
index 334f94e..966c979 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -289,6 +289,7 @@
'base/stream_parser.h',
'base/stream_parser_buffer.cc',
'base/stream_parser_buffer.h',
+ 'base/text_track.h',
'base/video_decoder.cc',
'base/video_decoder.h',
'base/video_decoder_config.cc',
@@ -445,6 +446,8 @@
'webm/webm_tracks_parser.h',
'webm/webm_video_client.cc',
'webm/webm_video_client.h',
+ 'webm/webm_webvtt_parser.cc',
+ 'webm/webm_webvtt_parser.h'
],
'direct_dependent_settings': {
'include_dirs': [
@@ -1004,6 +1007,7 @@
'webm/webm_content_encodings_client_unittest.cc',
'webm/webm_parser_unittest.cc',
'webm/webm_tracks_parser_unittest.cc',
+ 'webm/webm_webvtt_parser_unittest.cc',
],
'conditions': [
['arm_neon == 1', {
diff --git a/media/mp4/mp4_stream_parser.cc b/media/mp4/mp4_stream_parser.cc
index 5898615..77fa2b8 100644
--- a/media/mp4/mp4_stream_parser.cc
+++ b/media/mp4/mp4_stream_parser.cc
@@ -44,7 +44,9 @@ void MP4StreamParser::Init(const InitCB& init_cb,
const NewConfigCB& config_cb,
const NewBuffersCB& audio_cb,
const NewBuffersCB& video_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) {
diff --git a/media/mp4/mp4_stream_parser.h b/media/mp4/mp4_stream_parser.h
index 0fc65c6..7c9929a 100644
--- a/media/mp4/mp4_stream_parser.h
+++ b/media/mp4/mp4_stream_parser.h
@@ -31,7 +31,9 @@ class MEDIA_EXPORT MP4StreamParser : public StreamParser {
virtual void Init(const InitCB& init_cb, const NewConfigCB& config_cb,
const NewBuffersCB& audio_cb,
const NewBuffersCB& video_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;
diff --git a/media/mp4/mp4_stream_parser_unittest.cc b/media/mp4/mp4_stream_parser_unittest.cc
index 4105599..06cd976 100644
--- a/media/mp4/mp4_stream_parser_unittest.cc
+++ b/media/mp4/mp4_stream_parser_unittest.cc
@@ -82,6 +82,11 @@ class MP4StreamParserTest : public testing::Test {
return true;
}
+ bool NewTextBuffersF(TextTrack* text_track,
+ const StreamParser::BufferQueue& buffers) {
+ return true;
+ }
+
bool KeyNeededF(const std::string& type,
scoped_ptr<uint8[]> init_data, int init_data_size) {
DVLOG(1) << "KeyNeededF: " << init_data_size;
@@ -91,6 +96,13 @@ class MP4StreamParserTest : public testing::Test {
return true;
}
+ scoped_ptr<TextTrack> AddTextTrackF(
+ TextKind kind,
+ const std::string& label,
+ const std::string& language) {
+ return scoped_ptr<TextTrack>();
+ }
+
void NewSegmentF(TimeDelta start_dts) {
DVLOG(1) << "NewSegmentF: " << start_dts.InMilliseconds();
segment_start_ = start_dts;
@@ -106,7 +118,10 @@ class MP4StreamParserTest : public testing::Test {
base::Bind(&MP4StreamParserTest::NewConfigF, base::Unretained(this)),
base::Bind(&MP4StreamParserTest::NewBuffersF, base::Unretained(this)),
base::Bind(&MP4StreamParserTest::NewBuffersF, base::Unretained(this)),
+ base::Bind(&MP4StreamParserTest::NewTextBuffersF,
+ base::Unretained(this)),
base::Bind(&MP4StreamParserTest::KeyNeededF, base::Unretained(this)),
+ base::Bind(&MP4StreamParserTest::AddTextTrackF, base::Unretained(this)),
base::Bind(&MP4StreamParserTest::NewSegmentF, base::Unretained(this)),
base::Bind(&MP4StreamParserTest::EndOfSegmentF,
base::Unretained(this)),
diff --git a/media/webm/webm_parser.h b/media/webm/webm_parser.h
index 1fa71e7..68611a8 100644
--- a/media/webm/webm_parser.h
+++ b/media/webm/webm_parser.h
@@ -153,15 +153,6 @@ class MEDIA_EXPORT WebMListParser {
int MEDIA_EXPORT WebMParseElementHeader(const uint8* buf, int size,
int* id, int64* element_size);
-// Specifies the varieties of text tracks.
-enum TextKind {
- kTextNone,
- kTextSubtitles,
- kTextCaptions,
- kTextDescriptions,
- kTextMetadata
-};
-
} // namespace media
#endif // MEDIA_WEBM_WEBM_PARSER_H_
diff --git a/media/webm/webm_stream_parser.cc b/media/webm/webm_stream_parser.cc
index fb070a9..27a01a8 100644
--- a/media/webm/webm_stream_parser.cc
+++ b/media/webm/webm_stream_parser.cc
@@ -8,6 +8,7 @@
#include "base/callback.h"
#include "base/logging.h"
+#include "base/stl_util.h"
#include "media/webm/webm_cluster_parser.h"
#include "media/webm/webm_constants.h"
#include "media/webm/webm_content_encodings.h"
@@ -22,13 +23,17 @@ WebMStreamParser::WebMStreamParser()
waiting_for_buffers_(false) {
}
-WebMStreamParser::~WebMStreamParser() {}
+WebMStreamParser::~WebMStreamParser() {
+ STLDeleteValues(&text_track_map_);
+}
void WebMStreamParser::Init(const InitCB& init_cb,
const NewConfigCB& config_cb,
const NewBuffersCB& audio_cb,
const NewBuffersCB& video_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) {
@@ -37,6 +42,7 @@ void WebMStreamParser::Init(const InitCB& init_cb,
DCHECK(!init_cb.is_null());
DCHECK(!config_cb.is_null());
DCHECK(!audio_cb.is_null() || !video_cb.is_null());
+ DCHECK(!text_cb.is_null());
DCHECK(!need_key_cb.is_null());
DCHECK(!new_segment_cb.is_null());
DCHECK(!end_of_segment_cb.is_null());
@@ -46,7 +52,9 @@ void WebMStreamParser::Init(const InitCB& init_cb,
config_cb_ = config_cb;
audio_cb_ = audio_cb;
video_cb_ = video_cb;
+ text_cb_ = text_cb;
need_key_cb_ = need_key_cb;
+ add_text_track_cb_ = add_text_track_cb;
new_segment_cb_ = new_segment_cb;
end_of_segment_cb_ = end_of_segment_cb;
log_cb_ = log_cb;
@@ -198,11 +206,32 @@ int WebMStreamParser::ParseInfoAndTracks(const uint8* data, int size) {
return -1;
}
+ typedef WebMTracksParser::TextTracks TextTracks;
+ const TextTracks& text_tracks = tracks_parser.text_tracks();
+
+ for (TextTracks::const_iterator itr = text_tracks.begin();
+ itr != text_tracks.end(); ++itr) {
+ const WebMTracksParser::TextTrackInfo& text_track_info = itr->second;
+
+ // TODO(matthewjheaney): verify that WebVTT uses ISO 639-2 for lang
+ scoped_ptr<TextTrack> text_track =
+ add_text_track_cb_.Run(text_track_info.kind,
+ text_track_info.name,
+ text_track_info.language);
+
+ // Assume ownership of pointer, and cache the text track object, for use
+ // later when we have text track buffers. (The text track objects are
+ // deallocated in the dtor for this class.)
+
+ if (text_track)
+ text_track_map_.insert(std::make_pair(itr->first, text_track.release()));
+ }
+
cluster_parser_.reset(new WebMClusterParser(
info_parser.timecode_scale(),
tracks_parser.audio_track_num(),
tracks_parser.video_track_num(),
- tracks_parser.text_tracks(),
+ text_tracks,
tracks_parser.ignored_tracks(),
tracks_parser.audio_encryption_key_id(),
tracks_parser.video_encryption_key_id(),
@@ -268,6 +297,24 @@ int WebMStreamParser::ParseCluster(const uint8* data, int size) {
if (!video_buffers.empty() && !video_cb_.Run(video_buffers))
return -1;
+ WebMClusterParser::TextTrackIterator text_track_iter =
+ cluster_parser_->CreateTextTrackIterator();
+
+ int text_track_num;
+ const BufferQueue* text_buffers;
+
+ while (text_track_iter(&text_track_num, &text_buffers)) {
+ TextTrackMap::iterator find_result = text_track_map_.find(text_track_num);
+
+ if (find_result == text_track_map_.end())
+ continue;
+
+ TextTrack* const text_track = find_result->second;
+
+ if (!text_buffers->empty() && !text_cb_.Run(text_track, *text_buffers))
+ return -1;
+ }
+
if (cluster_ended)
end_of_segment_cb_.Run();
diff --git a/media/webm/webm_stream_parser.h b/media/webm/webm_stream_parser.h
index 7407542..75d3578 100644
--- a/media/webm/webm_stream_parser.h
+++ b/media/webm/webm_stream_parser.h
@@ -5,6 +5,8 @@
#ifndef MEDIA_WEBM_WEBM_STREAM_PARSER_H_
#define MEDIA_WEBM_WEBM_STREAM_PARSER_H_
+#include <map>
+
#include "base/callback_forward.h"
#include "base/memory/ref_counted.h"
#include "media/base/audio_decoder_config.h"
@@ -26,7 +28,9 @@ class WebMStreamParser : public StreamParser {
virtual void Init(const InitCB& init_cb, const NewConfigCB& config_cb,
const NewBuffersCB& audio_cb,
const NewBuffersCB& video_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;
@@ -70,7 +74,13 @@ class WebMStreamParser : public StreamParser {
NewConfigCB config_cb_;
NewBuffersCB audio_cb_;
NewBuffersCB video_cb_;
+ NewTextBuffersCB text_cb_;
NeedKeyCB need_key_cb_;
+ AddTextTrackCB add_text_track_cb_;
+
+ typedef std::map<int, TextTrack* > TextTrackMap;
+ TextTrackMap text_track_map_;
+
NewMediaSegmentCB new_segment_cb_;
base::Closure end_of_segment_cb_;
LogCB log_cb_;
diff --git a/media/webm/webm_tracks_parser.h b/media/webm/webm_tracks_parser.h
index d5be6ce..a7ec0e3 100644
--- a/media/webm/webm_tracks_parser.h
+++ b/media/webm/webm_tracks_parser.h
@@ -14,6 +14,7 @@
#include "base/memory/scoped_ptr.h"
#include "media/base/audio_decoder_config.h"
#include "media/base/media_log.h"
+#include "media/base/text_track.h"
#include "media/base/video_decoder_config.h"
#include "media/webm/webm_audio_client.h"
#include "media/webm/webm_content_encodings_client.h"
diff --git a/media/webm/webm_webvtt_parser.cc b/media/webm/webm_webvtt_parser.cc
new file mode 100644
index 0000000..d77bfbc
--- /dev/null
+++ b/media/webm/webm_webvtt_parser.cc
@@ -0,0 +1,78 @@
+// Copyright (c) 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.
+
+#include "media/webm/webm_webvtt_parser.h"
+
+namespace media {
+
+void WebMWebVTTParser::Parse(const uint8* payload, int payload_size,
+ std::string* id,
+ std::string* settings,
+ std::string* content) {
+ WebMWebVTTParser parser(payload, payload_size);
+ parser.Parse(id, settings, content);
+}
+
+WebMWebVTTParser::WebMWebVTTParser(const uint8* payload, int payload_size)
+ : ptr_(payload),
+ ptr_end_(payload + payload_size) {
+}
+
+void WebMWebVTTParser::Parse(std::string* id,
+ std::string* settings,
+ std::string* content) {
+ ParseLine(id);
+ ParseLine(settings);
+ content->assign(ptr_, ptr_end_);
+}
+
+bool WebMWebVTTParser::GetByte(uint8* byte) {
+ if (ptr_ >= ptr_end_)
+ return false; // indicates end-of-stream
+
+ *byte = *ptr_++;
+ return true;
+}
+
+void WebMWebVTTParser::UngetByte() {
+ --ptr_;
+}
+
+void WebMWebVTTParser::ParseLine(std::string* line) {
+ line->clear();
+
+ // Consume characters from the stream, until we reach end-of-line.
+
+ // The WebVTT spec states that lines may be terminated in any of the following
+ // three ways:
+ // LF
+ // CR
+ // CR LF
+
+ // The spec is here:
+ // http://wiki.webmproject.org/webm-metadata/temporal-metadata/webvtt-in-webm
+
+ enum {
+ kLF = '\x0A',
+ kCR = '\x0D'
+ };
+
+ for (;;) {
+ uint8 byte;
+
+ if (!GetByte(&byte) || byte == kLF)
+ return;
+
+ if (byte == kCR) {
+ if (GetByte(&byte) && byte != kLF)
+ UngetByte();
+
+ return;
+ }
+
+ line->push_back(byte);
+ }
+}
+
+} // namespace media
diff --git a/media/webm/webm_webvtt_parser.h b/media/webm/webm_webvtt_parser.h
new file mode 100644
index 0000000..a6aa316
--- /dev/null
+++ b/media/webm/webm_webvtt_parser.h
@@ -0,0 +1,49 @@
+// Copyright (c) 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_WEBM_WEBM_WEBVTT_PARSER_H_
+#define MEDIA_WEBM_WEBM_WEBVTT_PARSER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+class MEDIA_EXPORT WebMWebVTTParser {
+ public:
+ // Utility function to parse the WebVTT cue from a byte stream.
+ static void Parse(const uint8* payload, int payload_size,
+ std::string* id,
+ std::string* settings,
+ std::string* content);
+
+ private:
+ // The payload is the embedded WebVTT cue, stored in a WebM block.
+ // The parser treats this as a UTF-8 byte stream.
+ WebMWebVTTParser(const uint8* payload, int payload_size);
+
+ // Parse the cue identifier, settings, and content from the stream.
+ void Parse(std::string* id, std::string* settings, std::string* content);
+ // Remove a byte from the stream, advancing the stream pointer.
+ // Returns true if a character was returned; false means "end of stream".
+ bool GetByte(uint8* byte);
+
+ // Backup the stream pointer.
+ void UngetByte();
+
+ // Parse a line of text from the stream.
+ void ParseLine(std::string* line);
+
+ // Represents the portion of the stream that has not been consumed yet.
+ const uint8* ptr_;
+ const uint8* const ptr_end_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebMWebVTTParser);
+};
+
+} // namespace media
+
+#endif // MEDIA_WEBM_WEBM_WEBVTT_PARSER_H_
diff --git a/media/webm/webm_webvtt_parser_unittest.cc b/media/webm/webm_webvtt_parser_unittest.cc
new file mode 100644
index 0000000..6ef744e
--- /dev/null
+++ b/media/webm/webm_webvtt_parser_unittest.cc
@@ -0,0 +1,105 @@
+// Copyright (c) 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.
+
+#include "media/webm/webm_webvtt_parser.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::InSequence;
+
+namespace media {
+
+typedef std::vector<uint8> Cue;
+
+static Cue EncodeCue(const std::string& id,
+ const std::string& settings,
+ const std::string& content) {
+ const std::string result = id + '\n' + settings + '\n' + content;
+ const uint8* const buf = reinterpret_cast<const uint8*>(result.data());
+ return Cue(buf, buf + result.length());
+}
+
+static void DecodeCue(const Cue& cue,
+ std::string* id,
+ std::string* settings,
+ std::string* content) {
+ WebMWebVTTParser::Parse(&cue[0], static_cast<int>(cue.size()),
+ id, settings, content);
+}
+
+class WebMWebVTTParserTest : public testing::Test {
+ public:
+ WebMWebVTTParserTest() {}
+};
+
+TEST_F(WebMWebVTTParserTest, TestBlank) {
+ InSequence s;
+
+ const Cue cue = EncodeCue("", "", "Subtitle");
+ std::string id, settings, content;
+
+ DecodeCue(cue, &id, &settings, &content);
+ EXPECT_EQ(id, "");
+ EXPECT_EQ(settings, "");
+ EXPECT_EQ(content, "Subtitle");
+}
+
+TEST_F(WebMWebVTTParserTest, TestId) {
+ InSequence s;
+
+ for (int i = 1; i <= 9; ++i) {
+ const std::string idsrc(1, '0'+i);
+ const Cue cue = EncodeCue(idsrc, "", "Subtitle");
+ std::string id, settings, content;
+
+ DecodeCue(cue, &id, &settings, &content);
+ EXPECT_EQ(id, idsrc);
+ EXPECT_EQ(settings, "");
+ EXPECT_EQ(content, "Subtitle");
+ }
+}
+
+TEST_F(WebMWebVTTParserTest, TestSettings) {
+ InSequence s;
+
+ enum { kSettingsCount = 4 };
+ const char* const settings_str[kSettingsCount] = {
+ "vertical:lr",
+ "line:50%",
+ "position:42%",
+ "vertical:rl line:42% position:100%" };
+
+ for (int i = 0; i < kSettingsCount; ++i) {
+ const Cue cue = EncodeCue("", settings_str[i], "Subtitle");
+ std::string id, settings, content;
+
+ DecodeCue(cue, &id, &settings, &content);
+ EXPECT_EQ(id, "");
+ EXPECT_EQ(settings, settings_str[i]);
+ EXPECT_EQ(content, "Subtitle");
+ }
+}
+
+TEST_F(WebMWebVTTParserTest, TestContent) {
+ InSequence s;
+
+ enum { kContentCount = 4 };
+ const char* const content_str[kContentCount] = {
+ "Subtitle",
+ "Another Subtitle",
+ "Yet Another Subtitle",
+ "Another Subtitle\nSplit Across Two Lines" };
+
+ for (int i = 0; i < kContentCount; ++i) {
+ const Cue cue = EncodeCue("", "", content_str[i]);
+ std::string id, settings, content;
+
+ DecodeCue(cue, &id, &settings, &content);
+ EXPECT_EQ(id, "");
+ EXPECT_EQ(settings, "");
+ EXPECT_EQ(content, content_str[i]);
+ }
+}
+
+} // namespace media