Commit 37e3ddb2 authored by wolenetz's avatar wolenetz Committed by Commit bot

MSE: Notify Blink SourceBuffer on init segment received

As the second of a three-sided set of change:
This change lays the groundwork for Blink to own the app-visible pieces
of the MSE initialization segment received algorithm. It lets
SourceState notify WebSourceBufferImpl upon parsing an initialization
segment. WSBI then notifies its WebSourceBufferClient.
Updated unit tests are included.

R=acolwell@chromium.org
TEST=No media_unittest regression
BUG=249428,397243

Review URL: https://codereview.chromium.org/547223003

Cr-Commit-Position: refs/heads/master@{#294722}
parent 069fc881
...@@ -6,8 +6,12 @@ ...@@ -6,8 +6,12 @@
#include <limits> #include <limits>
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/float_util.h" #include "base/float_util.h"
#include "media/filters/chunk_demuxer.h" #include "media/filters/chunk_demuxer.h"
#include "third_party/WebKit/public/platform/WebSourceBufferClient.h"
namespace media { namespace media {
...@@ -34,12 +38,20 @@ WebSourceBufferImpl::WebSourceBufferImpl( ...@@ -34,12 +38,20 @@ WebSourceBufferImpl::WebSourceBufferImpl(
const std::string& id, ChunkDemuxer* demuxer) const std::string& id, ChunkDemuxer* demuxer)
: id_(id), : id_(id),
demuxer_(demuxer), demuxer_(demuxer),
client_(NULL),
append_window_end_(kInfiniteDuration()) { append_window_end_(kInfiniteDuration()) {
DCHECK(demuxer_); DCHECK(demuxer_);
} }
WebSourceBufferImpl::~WebSourceBufferImpl() { WebSourceBufferImpl::~WebSourceBufferImpl() {
DCHECK(!demuxer_) << "Object destroyed w/o removedFromMediaSource() call"; DCHECK(!demuxer_) << "Object destroyed w/o removedFromMediaSource() call";
DCHECK(!client_);
}
void WebSourceBufferImpl::setClient(blink::WebSourceBufferClient* client) {
DCHECK(client);
DCHECK(!client_);
client_ = client;
} }
bool WebSourceBufferImpl::setMode(WebSourceBuffer::AppendMode mode) { bool WebSourceBufferImpl::setMode(WebSourceBuffer::AppendMode mode) {
...@@ -76,7 +88,9 @@ void WebSourceBufferImpl::append( ...@@ -76,7 +88,9 @@ void WebSourceBufferImpl::append(
base::TimeDelta old_offset = timestamp_offset_; base::TimeDelta old_offset = timestamp_offset_;
demuxer_->AppendData(id_, data, length, demuxer_->AppendData(id_, data, length,
append_window_start_, append_window_end_, append_window_start_, append_window_end_,
&timestamp_offset_); &timestamp_offset_,
base::Bind(&WebSourceBufferImpl::InitSegmentReceived,
base::Unretained(this)));
// Coded frame processing may update the timestamp offset. If the caller // Coded frame processing may update the timestamp offset. If the caller
// provides a non-NULL |timestamp_offset| and frame processing changes the // provides a non-NULL |timestamp_offset| and frame processing changes the
...@@ -129,6 +143,12 @@ void WebSourceBufferImpl::setAppendWindowEnd(double end) { ...@@ -129,6 +143,12 @@ void WebSourceBufferImpl::setAppendWindowEnd(double end) {
void WebSourceBufferImpl::removedFromMediaSource() { void WebSourceBufferImpl::removedFromMediaSource() {
demuxer_->RemoveId(id_); demuxer_->RemoveId(id_);
demuxer_ = NULL; demuxer_ = NULL;
client_ = NULL;
}
void WebSourceBufferImpl::InitSegmentReceived() {
DVLOG(1) << __FUNCTION__;
client_->initializationSegmentReceived();
} }
} // namespace media } // namespace media
...@@ -21,6 +21,7 @@ class WebSourceBufferImpl : public blink::WebSourceBuffer { ...@@ -21,6 +21,7 @@ class WebSourceBufferImpl : public blink::WebSourceBuffer {
virtual ~WebSourceBufferImpl(); virtual ~WebSourceBufferImpl();
// blink::WebSourceBuffer implementation. // blink::WebSourceBuffer implementation.
virtual void setClient(blink::WebSourceBufferClient* client);
virtual bool setMode(AppendMode mode); virtual bool setMode(AppendMode mode);
virtual blink::WebTimeRanges buffered(); virtual blink::WebTimeRanges buffered();
virtual void append( virtual void append(
...@@ -35,9 +36,15 @@ class WebSourceBufferImpl : public blink::WebSourceBuffer { ...@@ -35,9 +36,15 @@ class WebSourceBufferImpl : public blink::WebSourceBuffer {
virtual void removedFromMediaSource(); virtual void removedFromMediaSource();
private: private:
// Demuxer callback handler to process an initialization segment received
// during an append() call.
void InitSegmentReceived();
std::string id_; std::string id_;
ChunkDemuxer* demuxer_; // Owned by WebMediaPlayerImpl. ChunkDemuxer* demuxer_; // Owned by WebMediaPlayerImpl.
blink::WebSourceBufferClient* client_;
// Controls the offset applied to timestamps when processing appended media // Controls the offset applied to timestamps when processing appended media
// segments. It is initially 0, which indicates that no offset is being // segments. It is initially 0, which indicates that no offset is being
// applied. Both setTimestampOffset() and append() may update this value. // applied. Both setTimestampOffset() and append() may update this value.
......
...@@ -90,6 +90,8 @@ class SourceState { ...@@ -90,6 +90,8 @@ class SourceState {
typedef base::Callback<ChunkDemuxerStream*( typedef base::Callback<ChunkDemuxerStream*(
DemuxerStream::Type)> CreateDemuxerStreamCB; DemuxerStream::Type)> CreateDemuxerStreamCB;
typedef ChunkDemuxer::InitSegmentReceivedCB InitSegmentReceivedCB;
typedef base::Callback<void( typedef base::Callback<void(
ChunkDemuxerStream*, const TextTrackConfig&)> NewTextTrackCB; ChunkDemuxerStream*, const TextTrackConfig&)> NewTextTrackCB;
...@@ -111,11 +113,14 @@ class SourceState { ...@@ -111,11 +113,14 @@ class SourceState {
// error occurred. |*timestamp_offset| is used and possibly updated by the // error occurred. |*timestamp_offset| is used and possibly updated by the
// append. |append_window_start| and |append_window_end| correspond to the MSE // append. |append_window_start| and |append_window_end| correspond to the MSE
// spec's similarly named source buffer attributes that are used in coded // spec's similarly named source buffer attributes that are used in coded
// frame processing. // frame processing. |init_segment_received_cb| is run for each new fully
bool Append(const uint8* data, size_t length, // parsed initialization segment.
bool Append(const uint8* data,
size_t length,
TimeDelta append_window_start, TimeDelta append_window_start,
TimeDelta append_window_end, TimeDelta append_window_end,
TimeDelta* timestamp_offset); TimeDelta* timestamp_offset,
const InitSegmentReceivedCB& init_segment_received_cb);
// Aborts the current append sequence and resets the parser. // Aborts the current append sequence and resets the parser.
void Abort(TimeDelta append_window_start, void Abort(TimeDelta append_window_start,
...@@ -232,6 +237,13 @@ class SourceState { ...@@ -232,6 +237,13 @@ class SourceState {
LogCB log_cb_; LogCB log_cb_;
StreamParser::InitCB init_cb_; StreamParser::InitCB init_cb_;
// During Append(), OnNewConfigs() will trigger the initialization segment
// received algorithm. This callback is only non-NULL during the lifetime of
// an Append() call. Note, the MSE spec explicitly disallows this algorithm
// during an Abort(), since Abort() is allowed only to emit coded frames, and
// only if the parser is PARSING_MEDIA_SEGMENT (not an INIT segment).
InitSegmentReceivedCB init_segment_received_cb_;
// Indicates that timestampOffset should be updated automatically during // Indicates that timestampOffset should be updated automatically during
// OnNewBuffers() based on the earliest end timestamp of the buffers provided. // OnNewBuffers() based on the earliest end timestamp of the buffers provided.
// TODO(wolenetz): Refactor this function while integrating April 29, 2014 // TODO(wolenetz): Refactor this function while integrating April 29, 2014
...@@ -300,20 +312,27 @@ void SourceState::SetGroupStartTimestampIfInSequenceMode( ...@@ -300,20 +312,27 @@ void SourceState::SetGroupStartTimestampIfInSequenceMode(
frame_processor_->SetGroupStartTimestampIfInSequenceMode(timestamp_offset); frame_processor_->SetGroupStartTimestampIfInSequenceMode(timestamp_offset);
} }
bool SourceState::Append(const uint8* data, size_t length, bool SourceState::Append(
TimeDelta append_window_start, const uint8* data,
TimeDelta append_window_end, size_t length,
TimeDelta* timestamp_offset) { TimeDelta append_window_start,
TimeDelta append_window_end,
TimeDelta* timestamp_offset,
const InitSegmentReceivedCB& init_segment_received_cb) {
DCHECK(timestamp_offset); DCHECK(timestamp_offset);
DCHECK(!timestamp_offset_during_append_); DCHECK(!timestamp_offset_during_append_);
DCHECK(!init_segment_received_cb.is_null());
DCHECK(init_segment_received_cb_.is_null());
append_window_start_during_append_ = append_window_start; append_window_start_during_append_ = append_window_start;
append_window_end_during_append_ = append_window_end; append_window_end_during_append_ = append_window_end;
timestamp_offset_during_append_ = timestamp_offset; timestamp_offset_during_append_ = timestamp_offset;
init_segment_received_cb_= init_segment_received_cb;
// TODO(wolenetz/acolwell): Curry and pass a NewBuffersCB here bound with // TODO(wolenetz/acolwell): Curry and pass a NewBuffersCB here bound with
// append window and timestamp offset pointer. See http://crbug.com/351454. // append window and timestamp offset pointer. See http://crbug.com/351454.
bool err = stream_parser_->Parse(data, length); bool err = stream_parser_->Parse(data, length);
timestamp_offset_during_append_ = NULL; timestamp_offset_during_append_ = NULL;
init_segment_received_cb_.Reset();
return err; return err;
} }
...@@ -534,6 +553,7 @@ bool SourceState::OnNewConfigs( ...@@ -534,6 +553,7 @@ bool SourceState::OnNewConfigs(
DVLOG(1) << "OnNewConfigs(" << allow_audio << ", " << allow_video DVLOG(1) << "OnNewConfigs(" << allow_audio << ", " << allow_video
<< ", " << audio_config.IsValidConfig() << ", " << audio_config.IsValidConfig()
<< ", " << video_config.IsValidConfig() << ")"; << ", " << video_config.IsValidConfig() << ")";
DCHECK(!init_segment_received_cb_.is_null());
if (!audio_config.IsValidConfig() && !video_config.IsValidConfig()) { if (!audio_config.IsValidConfig() && !video_config.IsValidConfig()) {
DVLOG(1) << "OnNewConfigs() : Audio & video config are not valid!"; DVLOG(1) << "OnNewConfigs() : Audio & video config are not valid!";
...@@ -676,6 +696,9 @@ bool SourceState::OnNewConfigs( ...@@ -676,6 +696,9 @@ bool SourceState::OnNewConfigs(
frame_processor_->SetAllTrackBuffersNeedRandomAccessPoint(); frame_processor_->SetAllTrackBuffersNeedRandomAccessPoint();
DVLOG(1) << "OnNewConfigs() : " << (success ? "success" : "failed"); DVLOG(1) << "OnNewConfigs() : " << (success ? "success" : "failed");
if (success)
init_segment_received_cb_.Run();
return success; return success;
} }
...@@ -1238,15 +1261,19 @@ Ranges<TimeDelta> ChunkDemuxer::GetBufferedRanges(const std::string& id) const { ...@@ -1238,15 +1261,19 @@ Ranges<TimeDelta> ChunkDemuxer::GetBufferedRanges(const std::string& id) const {
return itr->second->GetBufferedRanges(duration_, state_ == ENDED); return itr->second->GetBufferedRanges(duration_, state_ == ENDED);
} }
void ChunkDemuxer::AppendData(const std::string& id, void ChunkDemuxer::AppendData(
const uint8* data, size_t length, const std::string& id,
TimeDelta append_window_start, const uint8* data,
TimeDelta append_window_end, size_t length,
TimeDelta* timestamp_offset) { TimeDelta append_window_start,
TimeDelta append_window_end,
TimeDelta* timestamp_offset,
const InitSegmentReceivedCB& init_segment_received_cb) {
DVLOG(1) << "AppendData(" << id << ", " << length << ")"; DVLOG(1) << "AppendData(" << id << ", " << length << ")";
DCHECK(!id.empty()); DCHECK(!id.empty());
DCHECK(timestamp_offset); DCHECK(timestamp_offset);
DCHECK(!init_segment_received_cb.is_null());
Ranges<TimeDelta> ranges; Ranges<TimeDelta> ranges;
...@@ -1269,7 +1296,8 @@ void ChunkDemuxer::AppendData(const std::string& id, ...@@ -1269,7 +1296,8 @@ void ChunkDemuxer::AppendData(const std::string& id,
if (!source_state_map_[id]->Append(data, length, if (!source_state_map_[id]->Append(data, length,
append_window_start, append_window_start,
append_window_end, append_window_end,
timestamp_offset)) { timestamp_offset,
init_segment_received_cb)) {
ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN); ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN);
return; return;
} }
...@@ -1280,7 +1308,8 @@ void ChunkDemuxer::AppendData(const std::string& id, ...@@ -1280,7 +1308,8 @@ void ChunkDemuxer::AppendData(const std::string& id,
if (!source_state_map_[id]->Append(data, length, if (!source_state_map_[id]->Append(data, length,
append_window_start, append_window_start,
append_window_end, append_window_end,
timestamp_offset)) { timestamp_offset,
init_segment_received_cb)) {
ReportError_Locked(PIPELINE_ERROR_DECODE); ReportError_Locked(PIPELINE_ERROR_DECODE);
return; return;
} }
......
...@@ -137,6 +137,8 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { ...@@ -137,6 +137,8 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
kReachedIdLimit, // Reached ID limit. We can't handle any more IDs. kReachedIdLimit, // Reached ID limit. We can't handle any more IDs.
}; };
typedef base::Closure InitSegmentReceivedCB;
// |open_cb| Run when Initialize() is called to signal that the demuxer // |open_cb| Run when Initialize() is called to signal that the demuxer
// is ready to receive media data via AppenData(). // is ready to receive media data via AppenData().
// |need_key_cb| Run when the demuxer determines that an encryption key is // |need_key_cb| Run when the demuxer determines that an encryption key is
...@@ -211,10 +213,13 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { ...@@ -211,10 +213,13 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
// |append_window_start| and |append_window_end| correspond to the MSE spec's // |append_window_start| and |append_window_end| correspond to the MSE spec's
// similarly named source buffer attributes that are used in coded frame // similarly named source buffer attributes that are used in coded frame
// processing. // processing.
// |init_segment_received_cb| is run for each newly successfully parsed
// initialization segment.
void AppendData(const std::string& id, const uint8* data, size_t length, void AppendData(const std::string& id, const uint8* data, size_t length,
base::TimeDelta append_window_start, base::TimeDelta append_window_start,
base::TimeDelta append_window_end, base::TimeDelta append_window_end,
base::TimeDelta* timestamp_offset); base::TimeDelta* timestamp_offset,
const InitSegmentReceivedCB& init_segment_received_cb);
// Aborts parsing the current segment and reset the parser to a state where // Aborts parsing the current segment and reset the parser to a state where
// it can accept a new segment. // it can accept a new segment.
......
This diff is collapsed.
...@@ -18,9 +18,11 @@ ...@@ -18,9 +18,11 @@
#include "media/cdm/json_web_key.h" #include "media/cdm/json_web_key.h"
#include "media/filters/chunk_demuxer.h" #include "media/filters/chunk_demuxer.h"
#include "media/filters/renderer_impl.h" #include "media/filters/renderer_impl.h"
#include "testing/gmock/include/gmock/gmock.h"
using testing::_; using testing::_;
using testing::AnyNumber; using testing::AnyNumber;
using testing::AtLeast;
using testing::AtMost; using testing::AtMost;
using testing::SaveArg; using testing::SaveArg;
...@@ -432,7 +434,9 @@ class MockMediaSource { ...@@ -432,7 +434,9 @@ class MockMediaSource {
chunk_demuxer_->AppendData( chunk_demuxer_->AppendData(
kSourceId, file_data_->data() + current_position_, size, kSourceId, file_data_->data() + current_position_, size,
base::TimeDelta(), kInfiniteDuration(), &last_timestamp_offset_); base::TimeDelta(), kInfiniteDuration(), &last_timestamp_offset_,
base::Bind(&MockMediaSource::InitSegmentReceived,
base::Unretained(this)));
current_position_ += size; current_position_ += size;
} }
...@@ -442,7 +446,9 @@ class MockMediaSource { ...@@ -442,7 +446,9 @@ class MockMediaSource {
CHECK(!chunk_demuxer_->IsParsingMediaSegment(kSourceId)); CHECK(!chunk_demuxer_->IsParsingMediaSegment(kSourceId));
chunk_demuxer_->AppendData(kSourceId, pData, size, chunk_demuxer_->AppendData(kSourceId, pData, size,
base::TimeDelta(), kInfiniteDuration(), base::TimeDelta(), kInfiniteDuration(),
&timestamp_offset); &timestamp_offset,
base::Bind(&MockMediaSource::InitSegmentReceived,
base::Unretained(this)));
last_timestamp_offset_ = timestamp_offset; last_timestamp_offset_ = timestamp_offset;
} }
...@@ -457,7 +463,9 @@ class MockMediaSource { ...@@ -457,7 +463,9 @@ class MockMediaSource {
size, size,
append_window_start, append_window_start,
append_window_end, append_window_end,
&timestamp_offset); &timestamp_offset,
base::Bind(&MockMediaSource::InitSegmentReceived,
base::Unretained(this)));
last_timestamp_offset_ = timestamp_offset; last_timestamp_offset_ = timestamp_offset;
} }
...@@ -519,6 +527,8 @@ class MockMediaSource { ...@@ -519,6 +527,8 @@ class MockMediaSource {
return last_timestamp_offset_; return last_timestamp_offset_;
} }
MOCK_METHOD0(InitSegmentReceived, void(void));
private: private:
base::FilePath file_path_; base::FilePath file_path_;
scoped_refptr<DecoderBuffer> file_data_; scoped_refptr<DecoderBuffer> file_data_;
...@@ -536,6 +546,7 @@ class PipelineIntegrationTest ...@@ -536,6 +546,7 @@ class PipelineIntegrationTest
public PipelineIntegrationTestBase { public PipelineIntegrationTestBase {
public: public:
void StartPipelineWithMediaSource(MockMediaSource* source) { void StartPipelineWithMediaSource(MockMediaSource* source) {
EXPECT_CALL(*source, InitSegmentReceived()).Times(AtLeast(1));
EXPECT_CALL(*this, OnMetadata(_)) EXPECT_CALL(*this, OnMetadata(_))
.Times(AtMost(1)) .Times(AtMost(1))
.WillRepeatedly(SaveArg<0>(&metadata_)); .WillRepeatedly(SaveArg<0>(&metadata_));
......
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