blob: d2bc4e4ef66da22f0a9b203c4451f39149c760be [file] [log] [blame]
// Copyright 2016 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 "base/logging.h"
#include "services/media/factory_service/media_player_impl.h"
#include "services/media/factory_service/watched.h"
#include "services/media/framework/parts/reader.h"
#include "url/gurl.h"
namespace mojo {
namespace media {
// static
std::shared_ptr<MediaPlayerImpl> MediaPlayerImpl::Create(
const String& origin_url,
InterfaceRequest<MediaPlayer> request,
MediaFactoryService* owner) {
return std::shared_ptr<MediaPlayerImpl>(
new MediaPlayerImpl(origin_url, request.Pass(), owner));
}
MediaPlayerImpl::MediaPlayerImpl(
const String& origin_url,
InterfaceRequest<MediaPlayer> request,
MediaFactoryService* owner)
: MediaFactoryService::Product(owner),
binding_(this, request.Pass()) {
DCHECK(origin_url);
target_state_.SetWithConsequences(MediaState::PAUSED);
target_position_.SetWithConsequences(kNotSeeking);
// Go away when the client is no longer connected.
binding_.set_connection_error_handler([this]() {
ReleaseFromOwner();
});
app()->ConnectToService("mojo:media_factory", &factory_);
factory_->CreateSource(
origin_url,
nullptr, // allowed_media_types
GetProxy(&source_));
HandleSourceStatusUpdates();
source_->GetStreams(
[this](mojo::Array<MediaSourceStreamDescriptorPtr> descriptors) {
// Populate streams_ and enable the streams we want.
std::vector<Event> stream_prepared_events;
for (MediaSourceStreamDescriptorPtr& descriptor : descriptors) {
streams_.push_back(std::unique_ptr<Stream>(new Stream()));
Stream& stream = *streams_.back();
stream.descriptor_ = descriptor.Pass();
switch (stream.descriptor_->media_type->scheme) {
case MediaTypeScheme::COMPRESSED_AUDIO:
case MediaTypeScheme::LPCM:
stream.enabled_ = true;
stream_prepared_events.push_back(
PrepareStream(streams_.back(), "mojo:audio_server"));
break;
// TODO(dalesat): Enable other stream types.
default:
break;
}
}
event_ = Event::All(stream_prepared_events).When([this]() {
// The enabled streams are prepared. Prepare the source.
factory_.reset();
source_->Prepare([this]() {
SetReportedMediaState(MediaState::PAUSED);
WhenPaused();
});
});
});
}
MediaPlayerImpl::~MediaPlayerImpl() {
event_.Cancel();
}
void MediaPlayerImpl::WhenPaused() {
Event seek_requested = target_position_.BecomesOtherThan(kNotSeeking);
Event play_requested = target_state_.Becomes(MediaState::PLAYING);
event_ = Event::First({ seek_requested, play_requested });
seek_requested.When([this]() {
WhenPausedAndSeeking();
});
play_requested.When([this]() {
flushed_ = false;
source_->Prime([this]() {
event_ = ChangeSinkStates(MediaState::PLAYING).When([this]() {
WhenPlaying();
});
});
});
}
void MediaPlayerImpl::WhenPlaying() {
SetReportedMediaState(MediaState::PLAYING);
Event seek_requested = target_position_.BecomesOtherThan(kNotSeeking);
Event pause_requested = target_state_.Becomes(MediaState::PAUSED);
Event sinks_ended = AllSinkStatesBecome(MediaState::ENDED);
event_ = Event::First({ seek_requested, pause_requested, sinks_ended });
seek_requested.When([this]() {
event_ = ChangeSinkStates(MediaState::PAUSED).When([this]() {
WhenPausedAndSeeking();
});
});
pause_requested.When([this]() {
event_ = ChangeSinkStates(MediaState::PAUSED).When([this]() {
SetReportedMediaState(MediaState::PAUSED);
WhenPaused();
});
});
sinks_ended.When([this]() {
target_state_.SetWithConsequences(MediaState::ENDED);
SetReportedMediaState(MediaState::ENDED);
WhenPaused();
});
}
void MediaPlayerImpl::WhenPausedAndSeeking() {
if (!flushed_) {
source_->Flush([this]() {
flushed_ = true;
WhenFlushedAndSeeking();
});
} else {
WhenFlushedAndSeeking();
}
}
void MediaPlayerImpl::WhenFlushedAndSeeking() {
source_->Seek(target_position_, [this]() {
target_position_.SetWithConsequences(kNotSeeking);
WhenPaused();
});
}
Event MediaPlayerImpl::ChangeSinkStates(MediaState media_state) {
for (auto& stream : streams_) {
if (stream->enabled_) {
if (media_state == MediaState::PAUSED) {
stream->sink_->Pause();
} else {
stream->sink_->Play();
}
}
}
return AllSinkStatesBecome(media_state);
}
Event MediaPlayerImpl::AllSinkStatesBecome(MediaState media_state) {
std::vector<Event> precursors;
for (auto& stream : streams_) {
if (stream->enabled_) {
precursors.push_back(stream->state_.Becomes(media_state));
}
}
return Event::All(precursors);
}
void MediaPlayerImpl::SetReportedMediaState(MediaState media_state) {
if (reported_media_state_ != media_state) {
reported_media_state_ = media_state;
StatusUpdated();
}
}
void MediaPlayerImpl::GetStatus(
uint64_t version_last_seen,
const GetStatusCallback& callback) {
if (version_last_seen < status_version_) {
RunStatusCallback(callback);
} else {
pending_status_requests_.push_back(callback);
}
}
void MediaPlayerImpl::Play() {
target_state_.SetWithConsequences(MediaState::PLAYING);
}
void MediaPlayerImpl::Pause() {
target_state_.SetWithConsequences(MediaState::PAUSED);
}
void MediaPlayerImpl::Seek(int64_t position) {
DCHECK(position != kNotSeeking);
target_position_.SetWithConsequences(position);
}
Event MediaPlayerImpl::PrepareStream(
const std::unique_ptr<Stream>& stream,
const String& url) {
DCHECK(factory_);
Event event = Event::Create();
source_->GetProducer(
stream->descriptor_->index,
GetProxy(&stream->encoded_producer_));
if (stream->descriptor_->media_type->scheme ==
MediaTypeScheme::COMPRESSED_AUDIO) {
// Compressed audio. Insert a decoder in front of the sink. The sink would
// add its own internal decoder, but we want to test the decoder.
factory_->CreateDecoder(
stream->descriptor_->media_type.Clone(),
GetProxy(&stream->decoder_));
MediaConsumerPtr decoder_consumer;
stream->decoder_->GetConsumer(GetProxy(&decoder_consumer));
Event connect_complete = Event::Create();
stream->encoded_producer_->Connect(
decoder_consumer.Pass(),
[&stream, connect_complete]() {
stream->encoded_producer_.reset();
connect_complete.Occur();
});
stream->decoder_->GetOutputType(
[this, &stream, url, event](MediaTypePtr output_type) {
stream->decoder_->GetProducer(GetProxy(&stream->decoded_producer_));
CreateSink(
stream,
output_type,
url,
event);
});
return Event::All({ connect_complete, event });
} else {
// Uncompressed audio. Connect the source stream directly to the sink. This
// would work for compressed audio as well (the sink would decode), but we
// want to test the decoder.
DCHECK(stream->descriptor_->media_type->scheme == MediaTypeScheme::LPCM);
stream->decoded_producer_ = stream->encoded_producer_.Pass();
CreateSink(
stream,
stream->descriptor_->media_type,
url,
event);
return event;
}
}
void MediaPlayerImpl::CreateSink(
const std::unique_ptr<Stream>& stream,
const MediaTypePtr& input_media_type,
const String& url,
Event event) {
DCHECK(input_media_type);
DCHECK(stream->decoded_producer_);
DCHECK(factory_);
factory_->CreateSink(
url,
input_media_type.Clone(),
GetProxy(&stream->sink_));
MediaConsumerPtr consumer;
stream->sink_->GetConsumer(GetProxy(&consumer));
stream->decoded_producer_->Connect(
consumer.Pass(),
[this, event, &stream]() {
stream->decoded_producer_.reset();
DCHECK(stream->state_ == MediaState::UNPREPARED);
DCHECK(reported_media_state_ == MediaState::UNPREPARED ||
reported_media_state_ == MediaState::FAULT);
stream->state_.SetWithConsequences(MediaState::PAUSED);
HandleSinkStatusUpdates(stream);
event.Occur();
});
}
void MediaPlayerImpl::StatusUpdated() {
++status_version_;
while (!pending_status_requests_.empty()) {
RunStatusCallback(pending_status_requests_.front());
pending_status_requests_.pop_front();
}
}
void MediaPlayerImpl::RunStatusCallback(const GetStatusCallback& callback)
const {
MediaPlayerStatusPtr status = MediaPlayerStatus::New();
status->state = reported_media_state_;
status->timeline_transform = transform_.Clone();
status->metadata = metadata_.Clone();
callback.Run(status_version_, status.Pass());
}
void MediaPlayerImpl::HandleSourceStatusUpdates(
uint64_t version,
MediaSourceStatusPtr status) {
if (status) {
metadata_ = status->metadata.Pass();
StatusUpdated();
}
source_->GetStatus(
version,
[this](uint64_t version, MediaSourceStatusPtr status) {
HandleSourceStatusUpdates(version, status.Pass());
});
}
void MediaPlayerImpl::HandleSinkStatusUpdates(
const std::unique_ptr<Stream>& stream,
uint64_t version,
MediaSinkStatusPtr status) {
if (status && status->state > MediaState::UNPREPARED) {
// We transition to PAUSED when Connect completes.
DCHECK(stream->state_ > MediaState::UNPREPARED);
stream->state_.SetWithConsequences(status->state);
transform_ = status->timeline_transform.Pass();
StatusUpdated();
}
stream->sink_->GetStatus(
version,
[this, &stream](uint64_t version, MediaSinkStatusPtr status) {
HandleSinkStatusUpdates(stream, version, status.Pass());
});
}
MediaPlayerImpl::Stream::Stream() {
state_.SetWithConsequences(MediaState::UNPREPARED);
}
MediaPlayerImpl::Stream::~Stream() {}
} // namespace media
} // namespace mojo