| 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/linear_transform.h" | 6 #include "mojo/services/media/common/cpp/timeline.h" |
| 7 #include "mojo/services/media/common/cpp/local_time.h" | 7 #include "mojo/services/media/common/cpp/timeline_function.h" |
| 8 #include "services/media/factory_service/media_sink_impl.h" | 8 #include "services/media/factory_service/media_sink_impl.h" |
| 9 #include "services/media/framework/util/conversion_pipeline_builder.h" | 9 #include "services/media/framework/util/conversion_pipeline_builder.h" |
| 10 #include "services/media/framework_mojo/mojo_type_conversions.h" | 10 #include "services/media/framework_mojo/mojo_type_conversions.h" |
| 11 | 11 |
| 12 namespace mojo { | 12 namespace mojo { |
| 13 namespace media { | 13 namespace media { |
| 14 | 14 |
| 15 // static | 15 // static |
| 16 std::shared_ptr<MediaSinkImpl> MediaSinkImpl::Create( | 16 std::shared_ptr<MediaSinkImpl> MediaSinkImpl::Create( |
| 17 const String& destination_url, | 17 const String& destination_url, |
| 18 MediaTypePtr media_type, | 18 MediaTypePtr media_type, |
| 19 InterfaceRequest<MediaSink> request, | 19 InterfaceRequest<MediaSink> request, |
| 20 MediaFactoryService* owner) { | 20 MediaFactoryService* owner) { |
| 21 return std::shared_ptr<MediaSinkImpl>(new MediaSinkImpl( | 21 return std::shared_ptr<MediaSinkImpl>(new MediaSinkImpl( |
| 22 destination_url, media_type.Pass(), request.Pass(), owner)); | 22 destination_url, media_type.Pass(), request.Pass(), owner)); |
| 23 } | 23 } |
| 24 | 24 |
| 25 MediaSinkImpl::MediaSinkImpl(const String& destination_url, | 25 MediaSinkImpl::MediaSinkImpl(const String& destination_url, |
| 26 MediaTypePtr media_type, | 26 MediaTypePtr media_type, |
| 27 InterfaceRequest<MediaSink> request, | 27 InterfaceRequest<MediaSink> request, |
| 28 MediaFactoryService* owner) | 28 MediaFactoryService* owner) |
| 29 : MediaFactoryService::Product<MediaSink>(this, request.Pass(), owner), | 29 : MediaFactoryService::Product<MediaSink>(this, request.Pass(), owner), |
| 30 consumer_(MojoConsumer::Create()), | 30 consumer_(MojoConsumer::Create()), |
| 31 producer_(MojoProducer::Create()) { | 31 producer_(MojoProducer::Create()) { |
| 32 DCHECK(destination_url); | 32 DCHECK(destination_url); |
| 33 DCHECK(media_type); | 33 DCHECK(media_type); |
| 34 | 34 |
| 35 status_publisher_.SetCallbackRunner( | 35 status_publisher_.SetCallbackRunner([this](const GetStatusCallback& callback, |
| 36 [this](const GetStatusCallback& callback, uint64_t version) { | 36 uint64_t version) { |
| 37 MediaSinkStatusPtr status = MediaSinkStatus::New(); | 37 MediaSinkStatusPtr status = MediaSinkStatus::New(); |
| 38 status->state = (producer_state_ == MediaState::PAUSED && rate_ != 0.0) | 38 status->state = (producer_state_ == MediaState::PAUSED && rate_ != 0.0) |
| 39 ? MediaState::PLAYING | 39 ? MediaState::PLAYING |
| 40 : producer_state_; | 40 : producer_state_; |
| 41 status->timeline_transform = status_transform_.Clone(); | 41 status->timeline_transform = TimelineTransform::From(timeline_function_); |
| 42 callback.Run(version, status.Pass()); | 42 callback.Run(version, status.Pass()); |
| 43 }); | 43 }); |
| 44 | 44 |
| 45 PartRef consumer_ref = graph_.Add(consumer_); | 45 PartRef consumer_ref = graph_.Add(consumer_); |
| 46 PartRef producer_ref = graph_.Add(producer_); | 46 PartRef producer_ref = graph_.Add(producer_); |
| 47 | 47 |
| 48 consumer_->SetPrimeRequestedCallback( | 48 consumer_->SetPrimeRequestedCallback( |
| 49 [this](const MediaConsumer::PrimeCallback& callback) { | 49 [this](const MediaConsumer::PrimeCallback& callback) { |
| 50 ready_.When([this, callback]() { | 50 ready_.When([this, callback]() { |
| 51 DCHECK(producer_); | 51 DCHECK(producer_); |
| 52 producer_->PrimeConnection(callback); | 52 producer_->PrimeConnection(callback); |
| 53 }); | 53 }); |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 105 if (!result) { | 105 if (!result) { |
| 106 // Failed to build conversion pipeline. | 106 // Failed to build conversion pipeline. |
| 107 producer_state_ = MediaState::FAULT; | 107 producer_state_ = MediaState::FAULT; |
| 108 status_publisher_.SendUpdates(); | 108 status_publisher_.SendUpdates(); |
| 109 return; | 109 return; |
| 110 } | 110 } |
| 111 | 111 |
| 112 graph_.ConnectOutputToPart(out, producer_ref); | 112 graph_.ConnectOutputToPart(out, producer_ref); |
| 113 | 113 |
| 114 if (producer_stream_type->medium() == StreamType::Medium::kAudio) { | 114 if (producer_stream_type->medium() == StreamType::Medium::kAudio) { |
| 115 frames_per_second_ = producer_stream_type->audio()->frames_per_second(); | 115 frames_per_ns_ = |
| 116 TimelineRate(producer_stream_type->audio()->frames_per_second(), |
| 117 Timeline::ns_from_seconds(1)); |
| 118 |
| 116 } else { | 119 } else { |
| 117 // Unsupported producer stream type. | 120 // Unsupported producer stream type. |
| 118 LOG(ERROR) << "unsupported producer stream type"; | 121 LOG(ERROR) << "unsupported producer stream type"; |
| 119 abort(); | 122 abort(); |
| 120 } | 123 } |
| 121 | 124 |
| 122 controller_->Configure( | 125 controller_->Configure( |
| 123 std::move(producer_stream_type), | 126 std::move(producer_stream_type), |
| 124 [this](MediaConsumerPtr consumer, RateControlPtr rate_control) { | 127 [this](MediaConsumerPtr consumer, |
| 128 MediaTimelineControlSitePtr timeline_control_site) { |
| 125 DCHECK(consumer); | 129 DCHECK(consumer); |
| 126 DCHECK(rate_control); | 130 DCHECK(timeline_control_site); |
| 127 rate_control_ = rate_control.Pass(); | 131 timeline_control_site->GetTimelineConsumer( |
| 132 GetProxy(&timeline_consumer_)); |
| 128 producer_->Connect(consumer.Pass(), [this]() { | 133 producer_->Connect(consumer.Pass(), [this]() { |
| 129 graph_.Prepare(); | 134 graph_.Prepare(); |
| 130 ready_.Occur(); | 135 ready_.Occur(); |
| 131 MaybeSetRate(); | 136 MaybeSetRate(); |
| 132 }); | 137 }); |
| 133 }); | 138 }); |
| 134 }); | 139 }); |
| 135 } | 140 } |
| 136 | 141 |
| 137 MediaSinkImpl::~MediaSinkImpl() {} | 142 MediaSinkImpl::~MediaSinkImpl() {} |
| (...skipping 15 matching lines...) Expand all Loading... |
| 153 void MediaSinkImpl::Pause() { | 158 void MediaSinkImpl::Pause() { |
| 154 target_rate_ = 0.0; | 159 target_rate_ = 0.0; |
| 155 MaybeSetRate(); | 160 MaybeSetRate(); |
| 156 } | 161 } |
| 157 | 162 |
| 158 void MediaSinkImpl::MaybeSetRate() { | 163 void MediaSinkImpl::MaybeSetRate() { |
| 159 if (producer_state_ < MediaState::PAUSED || rate_ == target_rate_) { | 164 if (producer_state_ < MediaState::PAUSED || rate_ == target_rate_) { |
| 160 return; | 165 return; |
| 161 } | 166 } |
| 162 | 167 |
| 163 if (!rate_control_) { | 168 if (!timeline_consumer_) { |
| 164 rate_ = target_rate_; | 169 rate_ = target_rate_; |
| 165 status_publisher_.SendUpdates(); | 170 status_publisher_.SendUpdates(); |
| 166 return; | 171 return; |
| 167 } | 172 } |
| 168 | 173 |
| 169 // Desired rate in frames per second. | 174 // TODO(dalesat): start_local_time and start_presentation_time should be |
| 170 LinearTransform::Ratio rate_frames_per_second( | 175 // supplied via the mojo interface. For now, start_local_time is hard-coded |
| 171 static_cast<uint32_t>(frames_per_second_ * target_rate_), 1); | 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. |
| 172 | 179 |
| 173 // Local time rate in seconds_per_tick. | |
| 174 LinearTransform::Ratio local_seconds_per_tick(LocalDuration::period::num, | |
| 175 LocalDuration::period::den); | |
| 176 | |
| 177 // Desired rate in frames per local tick. | |
| 178 LinearTransform::Ratio rate_frames_per_tick; | |
| 179 bool success = LinearTransform::Ratio::Compose( | |
| 180 local_seconds_per_tick, rate_frames_per_second, &rate_frames_per_tick); | |
| 181 DCHECK(success) | |
| 182 << "LinearTransform::Ratio::Compose reports loss of precision"; | |
| 183 | |
| 184 // TODO(dalesat): start_local_time should be supplied via the mojo interface. | |
| 185 // For now, it's hard-coded to be 30ms in the future. | |
| 186 // The local time when we want the rate to change. | 180 // The local time when we want the rate to change. |
| 187 int64_t start_local_time = | 181 int64_t start_local_time = Timeline::local_now() + Timeline::ns_from_ms(30); |
| 188 (LocalClock::now().time_since_epoch() + std::chrono::milliseconds(30)) | |
| 189 .count(); | |
| 190 | 182 |
| 191 // The media time corresponding to start_local_time. | 183 // The media time corresponding to start_local_time. |
| 192 int64_t start_media_time; | 184 int64_t start_presentation_time; |
| 193 if (flushed_ && producer_->GetFirstPtsSinceFlush() != Packet::kUnknownPts) { | 185 if (flushed_ && producer_->GetFirstPtsSinceFlush() != Packet::kUnknownPts) { |
| 194 // We're getting started initially or after a flush/prime, so the media | 186 // We're getting started initially or after a flush/prime, so the media |
| 195 // time corresponding to start_local_time should be the PTS of | 187 // time corresponding to start_local_time should be the PTS of |
| 196 // the first packet. | 188 // the first packet converted to ns (rather than frame) units. |
| 197 start_media_time = producer_->GetFirstPtsSinceFlush(); | 189 start_presentation_time = |
| 190 producer_->GetFirstPtsSinceFlush() / frames_per_ns_; |
| 198 } else { | 191 } else { |
| 199 // We're resuming, so the media time corresponding to start_local_time can | 192 // We're resuming, so the media time corresponding to start_local_time can |
| 200 // be calculated using the existing transform. | 193 // be calculated using the existing transform. |
| 201 success = | 194 start_presentation_time = timeline_function_(start_local_time); |
| 202 transform_.DoForwardTransform(start_local_time, &start_media_time); | |
| 203 DCHECK(success) | |
| 204 << "LinearTransform::DoForwardTransform reports loss of precision"; | |
| 205 } | 195 } |
| 206 | 196 |
| 207 flushed_ = false; | 197 flushed_ = false; |
| 208 | 198 |
| 209 // Update the transform. | 199 // Update the transform. |
| 210 transform_ = | 200 timeline_function_ = TimelineFunction( |
| 211 LinearTransform(start_local_time, rate_frames_per_tick, start_media_time); | 201 start_local_time, start_presentation_time, TimelineRate(target_rate_)); |
| 212 | 202 |
| 213 // Set the rate. | 203 // Set the rate. |
| 214 TimelineQuadPtr rate_quad = TimelineQuad::New(); | 204 timeline_consumer_->SetTimelineTransform( |
| 215 rate_quad->reference_offset = start_media_time; | 205 timeline_function_.subject_time(), timeline_function_.reference_delta(), |
| 216 rate_quad->target_offset = start_local_time; | 206 timeline_function_.subject_delta(), timeline_function_.reference_time(), |
| 217 rate_quad->reference_delta = rate_frames_per_tick.numerator; | 207 kUnspecifiedTime, [](bool completed) {}); |
| 218 rate_quad->target_delta = rate_frames_per_tick.denominator; | |
| 219 | |
| 220 rate_control_->SetCurrentQuad(rate_quad.Pass()); | |
| 221 | |
| 222 // Get the frame rate in frames per local tick. | |
| 223 LinearTransform::Ratio frame_rate_frames_per_second(frames_per_second_, 1); | |
| 224 LinearTransform::Ratio frame_rate_frames_per_tick; | |
| 225 success = LinearTransform::Ratio::Compose(local_seconds_per_tick, | |
| 226 frame_rate_frames_per_second, | |
| 227 &frame_rate_frames_per_tick); | |
| 228 DCHECK(success) | |
| 229 << "LinearTransform::Ratio::Compose reports loss of precision"; | |
| 230 | |
| 231 // Create a LinearTransform to translate from presentation units to | |
| 232 // local duration units. | |
| 233 LinearTransform local_to_presentation(0, frame_rate_frames_per_tick, 0); | |
| 234 | |
| 235 status_transform_ = TimelineTransform::New(); | |
| 236 status_transform_->quad = TimelineQuad::New(); | |
| 237 | |
| 238 // Translate the current transform quad so the presentation time units | |
| 239 // are the same as the local time units. | |
| 240 success = local_to_presentation.DoReverseTransform( | |
| 241 start_media_time, &status_transform_->quad->reference_offset); | |
| 242 DCHECK(success) | |
| 243 << "LinearTransform::DoReverseTransform reports loss of precision"; | |
| 244 status_transform_->quad->target_offset = start_local_time; | |
| 245 int64_t presentation_delta; | |
| 246 success = local_to_presentation.DoReverseTransform( | |
| 247 static_cast<int64_t>(rate_frames_per_tick.numerator), | |
| 248 &presentation_delta); | |
| 249 DCHECK(success) | |
| 250 << "LinearTransform::DoReverseTransform reports loss of precision"; | |
| 251 status_transform_->quad->reference_delta = | |
| 252 static_cast<uint32_t>(presentation_delta); | |
| 253 status_transform_->quad->target_delta = rate_frames_per_tick.denominator; | |
| 254 LinearTransform::Ratio::Reduce(&status_transform_->quad->reference_delta, | |
| 255 &status_transform_->quad->target_delta); | |
| 256 | 208 |
| 257 rate_ = target_rate_; | 209 rate_ = target_rate_; |
| 258 status_publisher_.SendUpdates(); | 210 status_publisher_.SendUpdates(); |
| 259 } | 211 } |
| 260 | 212 |
| 261 } // namespace media | 213 } // namespace media |
| 262 } // namespace mojo | 214 } // namespace mojo |
| OLD | NEW |