blob: 9cfc60e540f1d59a3482b180eff3437933ac9253 [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/event.h"
#include "services/media/factory_service/media_source_impl.h"
#include "services/media/framework/conversion_pipeline_builder.h"
#include "services/media/framework/formatting.h"
#include "services/media/framework/parts/reader.h"
#include "services/media/framework_mojo/mojo_type_conversions.h"
#include "url/gurl.h"
namespace mojo {
namespace media {
// static
std::shared_ptr<MediaSourceImpl> MediaSourceImpl::Create(
const String& origin_url,
const Array<MediaTypeSetPtr>& allowed_media_types,
InterfaceRequest<MediaSource> request,
MediaFactoryService* owner) {
return std::shared_ptr<MediaSourceImpl>(new MediaSourceImpl(
origin_url, allowed_media_types, request.Pass(), owner));
}
MediaSourceImpl::MediaSourceImpl(
const String& origin_url,
const Array<MediaTypeSetPtr>& allowed_media_types,
InterfaceRequest<MediaSource> request,
MediaFactoryService* owner)
: MediaFactoryService::Product(owner), binding_(this, request.Pass()) {
DCHECK(origin_url);
// Go away when the client is no longer connected.
binding_.set_connection_error_handler([this]() { ReleaseFromOwner(); });
GURL gurl = GURL(origin_url);
// TODO(dalesat): Support mojo urls for capture scenarios.
Result result = Reader::Create(gurl, &reader_);
if (result != Result::kOk) {
NOTREACHED() << "couldn't create reader: " << result;
state_ = MediaState::FAULT;
return;
}
result = Demux::Create(reader_, &demux_);
if (result != Result::kOk) {
NOTREACHED() << "couldn't create demux: " << result;
state_ = MediaState::FAULT;
return;
}
demux_part_ = graph_.Add(demux_);
auto demux_streams = demux_->streams();
for (auto demux_stream : demux_streams) {
streams_.push_back(std::unique_ptr<Stream>(new Stream(
demux_part_.output(demux_stream->index()), demux_stream->stream_type(),
Convert(allowed_media_types), &graph_)));
}
}
MediaSourceImpl::~MediaSourceImpl() {}
void MediaSourceImpl::GetStreams(const GetStreamsCallback& callback) {
auto result = Array<MediaSourceStreamDescriptorPtr>::New(streams_.size());
for (size_t i = 0; i < streams_.size(); i++) {
MediaSourceStreamDescriptorPtr descriptor =
MediaSourceStreamDescriptor::New();
descriptor->index = i;
descriptor->media_type = streams_[i]->media_type();
descriptor->original_media_type = streams_[i]->original_media_type();
result[i] = descriptor.Pass();
}
callback.Run(result.Pass());
}
void MediaSourceImpl::GetClockDisposition(
const GetClockDispositionCallback& callback) {
callback.Run(ClockDisposition::PASSIVE);
// TODO(dalesat): Varies by origin type.
}
void MediaSourceImpl::GetMasterClock(InterfaceRequest<Clock> master_clock) {
// TODO(dalesat): Produce master clock as appropriate.
}
void MediaSourceImpl::SetMasterClock(InterfaceHandle<Clock> master_clock) {
// TODO(dalesat): Is this needed?
}
void MediaSourceImpl::GetProducer(uint32_t stream_index,
InterfaceRequest<MediaProducer> producer) {
if (stream_index >= streams_.size()) {
return;
}
streams_[stream_index]->GetProducer(producer.Pass());
}
void MediaSourceImpl::GetPullModeProducer(
uint32_t stream_index,
InterfaceRequest<MediaPullModeProducer> producer) {
if (stream_index >= streams_.size()) {
return;
}
streams_[stream_index]->GetPullModeProducer(producer.Pass());
}
void MediaSourceImpl::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 MediaSourceImpl::Prepare(const PrepareCallback& callback) {
for (auto& stream : streams_) {
stream->EnsureSink();
}
graph_.Prepare();
state_ = MediaState::PAUSED;
callback.Run();
StatusUpdated();
}
void MediaSourceImpl::Prime(const PrimeCallback& callback) {
std::vector<Event> stream_primed_events;
for (auto& stream : streams_) {
Event stream_primed = Event::Create();
stream_primed_events.push_back(stream_primed);
stream->PrimeConnection(stream_primed);
}
Event all_streams_primed = Event::All(stream_primed_events);
// Capture all_streams_primed so it doesn't get deleted before it occurs.
all_streams_primed.When([callback, all_streams_primed]() { callback.Run(); });
}
void MediaSourceImpl::Flush(const FlushCallback& callback) {
graph_.FlushAllOutputs(demux_part_);
std::vector<Event> stream_flushed_events;
for (auto& stream : streams_) {
Event stream_flushed = Event::Create();
stream_flushed_events.push_back(stream_flushed);
stream->FlushConnection(stream_flushed);
}
Event all_streams_flushed = Event::All(stream_flushed_events);
// Capture all_streams_flushed so it doesn't get deleted before it occurs.
all_streams_flushed.When(
[callback, all_streams_flushed]() { callback.Run(); });
}
void MediaSourceImpl::Seek(int64_t position, const FlushCallback& callback) {
demux_->Seek(position);
callback.Run();
}
void MediaSourceImpl::StatusUpdated() {
++status_version_;
while (!pending_status_requests_.empty()) {
RunStatusCallback(pending_status_requests_.front());
pending_status_requests_.pop_front();
}
}
void MediaSourceImpl::RunStatusCallback(
const GetStatusCallback& callback) const {
MediaSourceStatusPtr status = MediaSourceStatus::New();
status->state = state_;
status->metadata = demux_ ? Convert(demux_->metadata()) : nullptr;
callback.Run(status_version_, status.Pass());
}
MediaSourceImpl::Stream::Stream(
OutputRef output,
std::unique_ptr<StreamType> stream_type,
const std::unique_ptr<std::vector<std::unique_ptr<StreamTypeSet>>>&
allowed_stream_types,
Graph* graph)
: original_stream_type_(std::move(stream_type)), graph_(graph) {
DCHECK(original_stream_type_);
DCHECK(graph);
output_ = output;
if (allowed_stream_types == nullptr) {
// No conversion requested.
stream_type_ = SafeClone(original_stream_type_);
} else if (!BuildConversionPipeline(*original_stream_type_,
*allowed_stream_types, graph, &output_,
&stream_type_)) {
// Can't convert to any allowed type.
stream_type_ = StreamType::Create(StreamType::Scheme::kNone);
}
}
MediaSourceImpl::Stream::~Stream() {}
MediaTypePtr MediaSourceImpl::Stream::media_type() const {
return Convert(stream_type_);
}
MediaTypePtr MediaSourceImpl::Stream::original_media_type() const {
return Convert(original_stream_type_);
}
void MediaSourceImpl::Stream::GetProducer(
InterfaceRequest<MediaProducer> producer) {
if (pull_mode_producer_) {
// Can't have both push mode and pull mode producers.
return;
}
if (!producer_) {
producer_ = MojoProducer::Create();
graph_->ConnectOutputToPart(output_, graph_->Add(producer_));
}
producer_->AddBinding(producer.Pass());
}
void MediaSourceImpl::Stream::GetPullModeProducer(
InterfaceRequest<MediaPullModeProducer> producer) {
if (producer_) {
// Can't have both push mode and pull mode producers.
return;
}
if (!pull_mode_producer_) {
pull_mode_producer_ = MojoPullModeProducer::Create();
graph_->ConnectOutputToPart(output_, graph_->Add(pull_mode_producer_));
}
pull_mode_producer_->AddBinding(producer.Pass());
}
void MediaSourceImpl::Stream::EnsureSink() {
if (producer_ == nullptr && pull_mode_producer_ == nullptr) {
null_sink_ = NullSink::Create();
graph_->ConnectOutputToPart(output_, graph_->Add(null_sink_));
}
}
void MediaSourceImpl::Stream::PrimeConnection(
const MojoProducer::PrimeConnectionCallback callback) {
if (producer_ != nullptr) {
producer_->PrimeConnection(callback);
}
}
void MediaSourceImpl::Stream::FlushConnection(
const MojoProducer::FlushConnectionCallback callback) {
if (producer_ != nullptr) {
producer_->FlushConnection(callback);
}
}
} // namespace media
} // namespace mojo