summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
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