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 |