| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/logging.h" | 5 #include "base/logging.h" |
| 6 #include "mojo/services/media/common/cpp/timeline.h" | |
| 7 #include "mojo/services/media/common/cpp/timeline_function.h" | |
| 8 #include "services/media/factory_service/media_sink_impl.h" | 6 #include "services/media/factory_service/media_sink_impl.h" |
| 9 #include "services/media/framework/util/conversion_pipeline_builder.h" | 7 #include "services/media/framework/util/conversion_pipeline_builder.h" |
| 10 #include "services/media/framework_mojo/mojo_type_conversions.h" | 8 #include "services/media/framework_mojo/mojo_type_conversions.h" |
| 11 | 9 |
| 12 namespace mojo { | 10 namespace mojo { |
| 13 namespace media { | 11 namespace media { |
| 14 | 12 |
| 15 // static | 13 // static |
| 16 std::shared_ptr<MediaSinkImpl> MediaSinkImpl::Create( | 14 std::shared_ptr<MediaSinkImpl> MediaSinkImpl::Create( |
| 17 const String& destination_url, | 15 const String& destination_url, |
| 18 MediaTypePtr media_type, | 16 MediaTypePtr media_type, |
| 19 InterfaceRequest<MediaSink> request, | 17 InterfaceRequest<MediaSink> request, |
| 20 MediaFactoryService* owner) { | 18 MediaFactoryService* owner) { |
| 21 return std::shared_ptr<MediaSinkImpl>(new MediaSinkImpl( | 19 return std::shared_ptr<MediaSinkImpl>(new MediaSinkImpl( |
| 22 destination_url, media_type.Pass(), request.Pass(), owner)); | 20 destination_url, media_type.Pass(), request.Pass(), owner)); |
| 23 } | 21 } |
| 24 | 22 |
| 25 MediaSinkImpl::MediaSinkImpl(const String& destination_url, | 23 MediaSinkImpl::MediaSinkImpl(const String& destination_url, |
| 26 MediaTypePtr media_type, | 24 MediaTypePtr media_type, |
| 27 InterfaceRequest<MediaSink> request, | 25 InterfaceRequest<MediaSink> request, |
| 28 MediaFactoryService* owner) | 26 MediaFactoryService* owner) |
| 29 : MediaFactoryService::Product<MediaSink>(this, request.Pass(), owner), | 27 : MediaFactoryService::Product<MediaSink>(this, request.Pass(), owner), |
| 30 consumer_(MojoConsumer::Create()), | 28 consumer_(MojoConsumer::Create()), |
| 31 producer_(MojoProducer::Create()) { | 29 producer_(MojoProducer::Create()) { |
| 32 DCHECK(destination_url); | 30 DCHECK(destination_url); |
| 33 DCHECK(media_type); | 31 DCHECK(media_type); |
| 34 | 32 |
| 35 status_publisher_.SetCallbackRunner([this](const GetStatusCallback& callback, | |
| 36 uint64_t version) { | |
| 37 MediaSinkStatusPtr status = MediaSinkStatus::New(); | |
| 38 status->state = (producer_state_ == MediaState::PAUSED && rate_ != 0.0) | |
| 39 ? MediaState::PLAYING | |
| 40 : producer_state_; | |
| 41 status->timeline_transform = TimelineTransform::From(timeline_function_); | |
| 42 callback.Run(version, status.Pass()); | |
| 43 }); | |
| 44 | |
| 45 PartRef consumer_ref = graph_.Add(consumer_); | 33 PartRef consumer_ref = graph_.Add(consumer_); |
| 46 PartRef producer_ref = graph_.Add(producer_); | 34 PartRef producer_ref = graph_.Add(producer_); |
| 47 | 35 |
| 48 consumer_->SetPrimeRequestedCallback( | 36 consumer_->SetPrimeRequestedCallback( |
| 49 [this](const MediaConsumer::PrimeCallback& callback) { | 37 [this](const MediaConsumer::PrimeCallback& callback) { |
| 50 ready_.When([this, callback]() { | 38 ready_.When([this, callback]() { |
| 51 DCHECK(producer_); | 39 DCHECK(producer_); |
| 52 producer_->PrimeConnection(callback); | 40 producer_->PrimeConnection(callback); |
| 53 }); | 41 }); |
| 54 }); | 42 }); |
| 55 consumer_->SetFlushRequestedCallback( | 43 consumer_->SetFlushRequestedCallback( |
| 56 [this, consumer_ref](const MediaConsumer::FlushCallback& callback) { | 44 [this, consumer_ref](const MediaConsumer::FlushCallback& callback) { |
| 57 ready_.When([this, consumer_ref, callback]() { | 45 ready_.When([this, consumer_ref, callback]() { |
| 58 DCHECK(producer_); | 46 DCHECK(producer_); |
| 59 graph_.FlushOutput(consumer_ref.output()); | 47 graph_.FlushOutput(consumer_ref.output()); |
| 60 producer_->FlushConnection(callback); | 48 producer_->FlushConnection(callback); |
| 61 flushed_ = true; | |
| 62 }); | 49 }); |
| 63 }); | 50 }); |
| 64 | 51 |
| 65 producer_->SetStatusCallback([this](MediaState state) { | |
| 66 producer_state_ = state; | |
| 67 status_publisher_.SendUpdates(); | |
| 68 if (state == MediaState::ENDED) { | |
| 69 Pause(); | |
| 70 } | |
| 71 }); | |
| 72 | |
| 73 // TODO(dalesat): Temporary, remove. | 52 // TODO(dalesat): Temporary, remove. |
| 74 if (destination_url == "nowhere") { | 53 if (destination_url == "nowhere") { |
| 75 // Throwing away the content. | 54 // Throwing away the content. |
| 76 graph_.ConnectParts(consumer_ref, producer_ref); | 55 graph_.ConnectParts(consumer_ref, producer_ref); |
| 77 graph_.Prepare(); | 56 graph_.Prepare(); |
| 78 ready_.Occur(); | 57 ready_.Occur(); |
| 79 return; | 58 return; |
| 80 } | 59 } |
| 81 | 60 |
| 82 RCHECK(destination_url == "mojo:audio_server"); | 61 RCHECK(destination_url == "mojo:audio_server"); |
| (...skipping 14 matching lines...) Expand all Loading... |
| 97 std::unique_ptr<StreamType> producer_stream_type; | 76 std::unique_ptr<StreamType> producer_stream_type; |
| 98 | 77 |
| 99 // Add transforms to the pipeline to convert from stream_type to a | 78 // Add transforms to the pipeline to convert from stream_type to a |
| 100 // type supported by the track. | 79 // type supported by the track. |
| 101 OutputRef out = consumer_ref.output(); | 80 OutputRef out = consumer_ref.output(); |
| 102 bool result = | 81 bool result = |
| 103 BuildConversionPipeline(*captured_stream_type, *supported_stream_types, | 82 BuildConversionPipeline(*captured_stream_type, *supported_stream_types, |
| 104 &graph_, &out, &producer_stream_type); | 83 &graph_, &out, &producer_stream_type); |
| 105 if (!result) { | 84 if (!result) { |
| 106 // Failed to build conversion pipeline. | 85 // Failed to build conversion pipeline. |
| 107 producer_state_ = MediaState::FAULT; | 86 LOG(WARNING) << "failed to build conversion pipeline"; |
| 108 status_publisher_.SendUpdates(); | 87 // TODO(dalesat): Add problem reporting. |
| 109 return; | 88 return; |
| 110 } | 89 } |
| 111 | 90 |
| 112 graph_.ConnectOutputToPart(out, producer_ref); | 91 graph_.ConnectOutputToPart(out, producer_ref); |
| 113 | 92 |
| 114 if (producer_stream_type->medium() == StreamType::Medium::kAudio) { | 93 controller_->Configure(std::move(producer_stream_type), |
| 115 frames_per_ns_ = | 94 [this](MediaConsumerPtr consumer) { |
| 116 TimelineRate(producer_stream_type->audio()->frames_per_second(), | 95 DCHECK(consumer); |
| 117 Timeline::ns_from_seconds(1)); | 96 producer_->Connect(consumer.Pass(), [this]() { |
| 118 | 97 graph_.Prepare(); |
| 119 } else { | 98 ready_.Occur(); |
| 120 // Unsupported producer stream type. | 99 }); |
| 121 LOG(ERROR) << "unsupported producer stream type"; | 100 }); |
| 122 abort(); | |
| 123 } | |
| 124 | |
| 125 controller_->Configure( | |
| 126 std::move(producer_stream_type), | |
| 127 [this](MediaConsumerPtr consumer, | |
| 128 MediaTimelineControlSitePtr timeline_control_site) { | |
| 129 DCHECK(consumer); | |
| 130 DCHECK(timeline_control_site); | |
| 131 timeline_control_site->GetTimelineConsumer( | |
| 132 GetProxy(&timeline_consumer_)); | |
| 133 producer_->Connect(consumer.Pass(), [this]() { | |
| 134 graph_.Prepare(); | |
| 135 ready_.Occur(); | |
| 136 MaybeSetRate(); | |
| 137 }); | |
| 138 }); | |
| 139 }); | 101 }); |
| 140 } | 102 } |
| 141 | 103 |
| 142 MediaSinkImpl::~MediaSinkImpl() {} | 104 MediaSinkImpl::~MediaSinkImpl() {} |
| 143 | 105 |
| 144 void MediaSinkImpl::GetConsumer(InterfaceRequest<MediaConsumer> consumer) { | 106 void MediaSinkImpl::GetConsumer(InterfaceRequest<MediaConsumer> consumer) { |
| 145 consumer_->AddBinding(consumer.Pass()); | 107 consumer_->AddBinding(consumer.Pass()); |
| 146 } | 108 } |
| 147 | 109 |
| 148 void MediaSinkImpl::GetStatus(uint64_t version_last_seen, | 110 void MediaSinkImpl::GetTimelineControlSite( |
| 149 const GetStatusCallback& callback) { | 111 InterfaceRequest<MediaTimelineControlSite> req) { |
| 150 status_publisher_.Get(version_last_seen, callback); | 112 if (!controller_) { |
| 151 } | 113 LOG(ERROR) << "GetTimelineControlSite not implemented for 'nowhere' case"; |
| 152 | 114 abort(); |
| 153 void MediaSinkImpl::Play() { | |
| 154 target_rate_ = 1.0; | |
| 155 MaybeSetRate(); | |
| 156 } | |
| 157 | |
| 158 void MediaSinkImpl::Pause() { | |
| 159 target_rate_ = 0.0; | |
| 160 MaybeSetRate(); | |
| 161 } | |
| 162 | |
| 163 void MediaSinkImpl::MaybeSetRate() { | |
| 164 if (producer_state_ < MediaState::PAUSED || rate_ == target_rate_) { | |
| 165 return; | |
| 166 } | 115 } |
| 167 | 116 controller_->GetTimelineControlSite(req.Pass()); |
| 168 if (!timeline_consumer_) { | |
| 169 rate_ = target_rate_; | |
| 170 status_publisher_.SendUpdates(); | |
| 171 return; | |
| 172 } | |
| 173 | |
| 174 // TODO(dalesat): start_local_time and start_presentation_time should be | |
| 175 // supplied via the mojo interface. For now, start_local_time is hard-coded | |
| 176 // to be 30ms in the future, and start_presentation_time is grabbed from the | |
| 177 // first primed packet or is calculated from start_local_time based on the | |
| 178 // previous timeline function. | |
| 179 | |
| 180 // The local time when we want the rate to change. | |
| 181 int64_t start_local_time = Timeline::local_now() + Timeline::ns_from_ms(30); | |
| 182 | |
| 183 // The media time corresponding to start_local_time. | |
| 184 int64_t start_presentation_time; | |
| 185 if (flushed_ && producer_->GetFirstPtsSinceFlush() != Packet::kUnknownPts) { | |
| 186 // We're getting started initially or after a flush/prime, so the media | |
| 187 // time corresponding to start_local_time should be the PTS of | |
| 188 // the first packet converted to ns (rather than frame) units. | |
| 189 start_presentation_time = | |
| 190 producer_->GetFirstPtsSinceFlush() / frames_per_ns_; | |
| 191 } else { | |
| 192 // We're resuming, so the media time corresponding to start_local_time can | |
| 193 // be calculated using the existing transform. | |
| 194 start_presentation_time = timeline_function_(start_local_time); | |
| 195 } | |
| 196 | |
| 197 flushed_ = false; | |
| 198 | |
| 199 // Update the transform. | |
| 200 timeline_function_ = TimelineFunction( | |
| 201 start_local_time, start_presentation_time, TimelineRate(target_rate_)); | |
| 202 | |
| 203 // Set the rate. | |
| 204 timeline_consumer_->SetTimelineTransform( | |
| 205 timeline_function_.subject_time(), timeline_function_.reference_delta(), | |
| 206 timeline_function_.subject_delta(), timeline_function_.reference_time(), | |
| 207 kUnspecifiedTime, [](bool completed) {}); | |
| 208 | |
| 209 rate_ = target_rate_; | |
| 210 status_publisher_.SendUpdates(); | |
| 211 } | 117 } |
| 212 | 118 |
| 213 } // namespace media | 119 } // namespace media |
| 214 } // namespace mojo | 120 } // namespace mojo |
| OLD | NEW |