diff options
Diffstat (limited to 'media')
-rw-r--r-- | media/base/stream_parser.h | 11 | ||||
-rw-r--r-- | media/base/text_track.h | 42 | ||||
-rw-r--r-- | media/filters/chunk_demuxer.cc | 59 | ||||
-rw-r--r-- | media/filters/chunk_demuxer.h | 10 | ||||
-rw-r--r-- | media/filters/chunk_demuxer_unittest.cc | 11 | ||||
-rw-r--r-- | media/filters/pipeline_integration_test.cc | 16 | ||||
-rw-r--r-- | media/media.gyp | 4 | ||||
-rw-r--r-- | media/mp4/mp4_stream_parser.cc | 2 | ||||
-rw-r--r-- | media/mp4/mp4_stream_parser.h | 2 | ||||
-rw-r--r-- | media/mp4/mp4_stream_parser_unittest.cc | 15 | ||||
-rw-r--r-- | media/webm/webm_parser.h | 9 | ||||
-rw-r--r-- | media/webm/webm_stream_parser.cc | 51 | ||||
-rw-r--r-- | media/webm/webm_stream_parser.h | 10 | ||||
-rw-r--r-- | media/webm/webm_tracks_parser.h | 1 | ||||
-rw-r--r-- | media/webm/webm_webvtt_parser.cc | 78 | ||||
-rw-r--r-- | media/webm/webm_webvtt_parser.h | 49 | ||||
-rw-r--r-- | media/webm/webm_webvtt_parser_unittest.cc | 105 |
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 |