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/linear_transform.h" |
7 #include "mojo/services/media/common/cpp/local_time.h" | 7 #include "mojo/services/media/common/cpp/local_time.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/conversion_pipeline_builder.h" | 9 #include "services/media/framework/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, | 22 destination_url, media_type.Pass(), request.Pass(), owner)); |
23 media_type.Pass(), | |
24 request.Pass(), | |
25 owner)); | |
26 } | 23 } |
27 | 24 |
28 MediaSinkImpl::MediaSinkImpl( | 25 MediaSinkImpl::MediaSinkImpl(const String& destination_url, |
29 const String& destination_url, | 26 MediaTypePtr media_type, |
30 MediaTypePtr media_type, | 27 InterfaceRequest<MediaSink> request, |
31 InterfaceRequest<MediaSink> request, | 28 MediaFactoryService* owner) |
32 MediaFactoryService* owner) | |
33 : MediaFactoryService::Product(owner), | 29 : MediaFactoryService::Product(owner), |
34 binding_(this, request.Pass()), | 30 binding_(this, request.Pass()), |
35 consumer_(MojoConsumer::Create()), | 31 consumer_(MojoConsumer::Create()), |
36 producer_(MojoProducer::Create()) { | 32 producer_(MojoProducer::Create()) { |
37 DCHECK(destination_url); | 33 DCHECK(destination_url); |
38 DCHECK(media_type); | 34 DCHECK(media_type); |
39 | 35 |
40 // Go away when the client is no longer connected. | 36 // Go away when the client is no longer connected. |
41 binding_.set_connection_error_handler([this]() { | 37 binding_.set_connection_error_handler([this]() { ReleaseFromOwner(); }); |
42 ReleaseFromOwner(); | |
43 }); | |
44 | 38 |
45 // TODO(dalesat): Support file and network urls. | 39 // TODO(dalesat): Support file and network urls. |
46 // TODO(dalesat): Support mojo services in a reasonable way. | 40 // TODO(dalesat): Support mojo services in a reasonable way. |
47 | 41 |
48 PartRef consumer_ref = graph_.Add(consumer_); | 42 PartRef consumer_ref = graph_.Add(consumer_); |
49 PartRef producer_ref = graph_.Add(producer_); | 43 PartRef producer_ref = graph_.Add(producer_); |
50 | 44 |
51 consumer_->SetPrimeRequestedCallback( | 45 consumer_->SetPrimeRequestedCallback( |
52 [this](const MediaConsumer::PrimeCallback& callback) { | 46 [this](const MediaConsumer::PrimeCallback& callback) { |
53 ready_.When([this, callback]() { | 47 ready_.When([this, callback]() { |
54 DCHECK(producer_); | 48 DCHECK(producer_); |
55 producer_->PrimeConnection(callback); | 49 producer_->PrimeConnection(callback); |
56 }); | 50 }); |
57 }); | 51 }); |
58 consumer_->SetFlushRequestedCallback( | 52 consumer_->SetFlushRequestedCallback( |
59 [this, consumer_ref](const MediaConsumer::FlushCallback& callback) { | 53 [this, consumer_ref](const MediaConsumer::FlushCallback& callback) { |
60 ready_.When([this, consumer_ref, callback]() { | 54 ready_.When([this, consumer_ref, callback]() { |
61 DCHECK(producer_); | 55 DCHECK(producer_); |
62 graph_.FlushOutput(consumer_ref.output()); | 56 graph_.FlushOutput(consumer_ref.output()); |
63 producer_->FlushConnection(callback); | 57 producer_->FlushConnection(callback); |
64 flushed_ = true; | 58 flushed_ = true; |
65 }); | 59 }); |
66 }); | 60 }); |
67 | 61 |
68 producer_->SetStatusCallback( | 62 producer_->SetStatusCallback([this](MediaState state) { |
69 [this](MediaState state) { | 63 producer_state_ = state; |
70 producer_state_ = state; | 64 StatusUpdated(); |
71 StatusUpdated(); | 65 if (state == MediaState::ENDED) { |
72 if (state == MediaState::ENDED) { | 66 Pause(); |
73 Pause(); | 67 } |
74 } | 68 }); |
75 }); | |
76 | 69 |
77 if (destination_url != "mojo:audio_server") { | 70 if (destination_url != "mojo:audio_server") { |
78 LOG(ERROR) << "mojo:audio_server is the only supported destination"; | 71 LOG(ERROR) << "mojo:audio_server is the only supported destination"; |
79 if (binding_.is_bound()) { | 72 if (binding_.is_bound()) { |
80 binding_.Close(); | 73 binding_.Close(); |
81 } | 74 } |
82 return; | 75 return; |
83 } | 76 } |
84 | 77 |
85 // TODO(dalesat): Once we have c++14, get rid of this shared pointer hack. | 78 // TODO(dalesat): Once we have c++14, get rid of this shared pointer hack. |
86 std::shared_ptr<StreamType> | 79 std::shared_ptr<StreamType> captured_stream_type( |
87 captured_stream_type(Convert(media_type).release()); | 80 Convert(media_type).release()); |
88 | 81 |
89 // An AudioTrackController knows how to talk to an audio track, interrogating | 82 // An AudioTrackController knows how to talk to an audio track, interrogating |
90 // it for supported stream types and configuring it for the chosen stream | 83 // it for supported stream types and configuring it for the chosen stream |
91 // type. | 84 // type. |
92 controller_.reset(new AudioTrackController(destination_url, app())); | 85 controller_.reset(new AudioTrackController(destination_url, app())); |
93 | 86 |
94 controller_->GetSupportedMediaTypes( | 87 controller_->GetSupportedMediaTypes([this, consumer_ref, producer_ref, |
95 [this, consumer_ref, producer_ref, captured_stream_type] | 88 captured_stream_type]( |
96 (std::unique_ptr<std::vector<std::unique_ptr<StreamTypeSet>>> | 89 std::unique_ptr<std::vector<std::unique_ptr<StreamTypeSet>>> |
97 supported_stream_types) { | 90 supported_stream_types) { |
98 std::unique_ptr<StreamType> producer_stream_type; | 91 std::unique_ptr<StreamType> producer_stream_type; |
99 | 92 |
100 // Add transforms to the pipeline to convert from stream_type to a | 93 // Add transforms to the pipeline to convert from stream_type to a |
101 // type supported by the track. | 94 // type supported by the track. |
102 OutputRef out = consumer_ref.output(); | 95 OutputRef out = consumer_ref.output(); |
103 bool result = BuildConversionPipeline( | 96 bool result = |
104 *captured_stream_type, | 97 BuildConversionPipeline(*captured_stream_type, *supported_stream_types, |
105 *supported_stream_types, | 98 &graph_, &out, &producer_stream_type); |
106 &graph_, | 99 if (!result) { |
107 &out, | 100 // Failed to build conversion pipeline. |
108 &producer_stream_type); | 101 producer_state_ = MediaState::FAULT; |
109 if (!result) { | 102 StatusUpdated(); |
110 // Failed to build conversion pipeline. | 103 return; |
111 producer_state_ = MediaState::FAULT; | 104 } |
112 StatusUpdated(); | |
113 return; | |
114 } | |
115 | 105 |
116 graph_.ConnectOutputToPart(out, producer_ref); | 106 graph_.ConnectOutputToPart(out, producer_ref); |
117 | 107 |
118 switch (producer_stream_type->scheme()) { | 108 switch (producer_stream_type->scheme()) { |
119 case StreamType::Scheme::kLpcm: | 109 case StreamType::Scheme::kLpcm: |
120 frames_per_second_ = | 110 frames_per_second_ = producer_stream_type->lpcm()->frames_per_second(); |
121 producer_stream_type->lpcm()->frames_per_second(); | 111 break; |
122 break; | 112 case StreamType::Scheme::kCompressedAudio: |
123 case StreamType::Scheme::kCompressedAudio: | 113 frames_per_second_ = |
124 frames_per_second_ = | 114 producer_stream_type->compressed_audio()->frames_per_second(); |
125 producer_stream_type->compressed_audio()->frames_per_second(); | 115 break; |
126 break; | 116 default: |
127 default: | 117 // Unsupported producer stream type. |
128 // Unsupported producer stream type. | 118 producer_state_ = MediaState::FAULT; |
129 producer_state_ = MediaState::FAULT; | 119 StatusUpdated(); |
130 StatusUpdated(); | 120 return; |
131 return; | 121 } |
132 } | |
133 | 122 |
134 controller_->Configure( | 123 controller_->Configure( |
135 std::move(producer_stream_type), | 124 std::move(producer_stream_type), |
136 [this] | 125 [this](MediaConsumerPtr consumer, RateControlPtr rate_control) { |
137 (MediaConsumerPtr consumer, RateControlPtr rate_control) { | 126 DCHECK(consumer); |
138 DCHECK(consumer); | 127 DCHECK(rate_control); |
139 DCHECK(rate_control); | 128 rate_control_ = rate_control.Pass(); |
140 rate_control_ = rate_control.Pass(); | 129 producer_->Connect(consumer.Pass(), [this]() { |
141 producer_->Connect(consumer.Pass(), [this]() { | 130 graph_.Prepare(); |
142 graph_.Prepare(); | 131 ready_.Occur(); |
143 ready_.Occur(); | 132 MaybeSetRate(); |
144 MaybeSetRate(); | 133 }); |
145 }); | 134 }); |
146 }); | 135 }); |
147 }); | |
148 } | 136 } |
149 | 137 |
150 MediaSinkImpl::~MediaSinkImpl() {} | 138 MediaSinkImpl::~MediaSinkImpl() {} |
151 | 139 |
152 void MediaSinkImpl::GetClockDisposition( | 140 void MediaSinkImpl::GetClockDisposition( |
153 const GetClockDispositionCallback& callback) { | 141 const GetClockDispositionCallback& callback) { |
154 callback.Run(ClockDisposition::PREFER_MASTER); | 142 callback.Run(ClockDisposition::PREFER_MASTER); |
155 // TODO(dalesat): Varies by destination type. | 143 // TODO(dalesat): Varies by destination type. |
156 } | 144 } |
157 | 145 |
158 void MediaSinkImpl::GetMasterClock(InterfaceRequest<Clock> master_clock) { | 146 void MediaSinkImpl::GetMasterClock(InterfaceRequest<Clock> master_clock) { |
159 // TODO(dalesat): Produce master clock as appropriate. | 147 // TODO(dalesat): Produce master clock as appropriate. |
160 } | 148 } |
161 | 149 |
162 void MediaSinkImpl::SetMasterClock(InterfaceHandle<Clock> master_clock) { | 150 void MediaSinkImpl::SetMasterClock(InterfaceHandle<Clock> master_clock) { |
163 // TODO(dalesat): Accept master clock and arrange for synchronization. | 151 // TODO(dalesat): Accept master clock and arrange for synchronization. |
164 } | 152 } |
165 | 153 |
166 void MediaSinkImpl::GetConsumer(InterfaceRequest<MediaConsumer> consumer) { | 154 void MediaSinkImpl::GetConsumer(InterfaceRequest<MediaConsumer> consumer) { |
167 consumer_->AddBinding(consumer.Pass()); | 155 consumer_->AddBinding(consumer.Pass()); |
168 } | 156 } |
169 | 157 |
170 void MediaSinkImpl::GetStatus( | 158 void MediaSinkImpl::GetStatus(uint64_t version_last_seen, |
171 uint64_t version_last_seen, | 159 const GetStatusCallback& callback) { |
172 const GetStatusCallback& callback) { | |
173 if (version_last_seen < status_version_) { | 160 if (version_last_seen < status_version_) { |
174 RunStatusCallback(callback); | 161 RunStatusCallback(callback); |
175 } else { | 162 } else { |
176 pending_status_requests_.push_back(callback); | 163 pending_status_requests_.push_back(callback); |
177 } | 164 } |
178 } | 165 } |
179 | 166 |
180 void MediaSinkImpl::Play() { | 167 void MediaSinkImpl::Play() { |
181 target_rate_ = 1.0; | 168 target_rate_ = 1.0; |
182 MaybeSetRate(); | 169 MaybeSetRate(); |
183 } | 170 } |
184 | 171 |
185 void MediaSinkImpl::Pause() { | 172 void MediaSinkImpl::Pause() { |
186 target_rate_ = 0.0; | 173 target_rate_ = 0.0; |
187 MaybeSetRate(); | 174 MaybeSetRate(); |
188 } | 175 } |
189 | 176 |
190 void MediaSinkImpl::StatusUpdated() { | 177 void MediaSinkImpl::StatusUpdated() { |
191 ++status_version_; | 178 ++status_version_; |
192 while (!pending_status_requests_.empty()) { | 179 while (!pending_status_requests_.empty()) { |
193 RunStatusCallback(pending_status_requests_.front()); | 180 RunStatusCallback(pending_status_requests_.front()); |
194 pending_status_requests_.pop_front(); | 181 pending_status_requests_.pop_front(); |
195 } | 182 } |
196 } | 183 } |
197 | 184 |
198 void MediaSinkImpl::RunStatusCallback(const GetStatusCallback& callback) const { | 185 void MediaSinkImpl::RunStatusCallback(const GetStatusCallback& callback) const { |
199 MediaSinkStatusPtr status = MediaSinkStatus::New(); | 186 MediaSinkStatusPtr status = MediaSinkStatus::New(); |
200 status->state = (producer_state_ == MediaState::PAUSED && rate_ != 0.0) ? | 187 status->state = (producer_state_ == MediaState::PAUSED && rate_ != 0.0) |
201 MediaState::PLAYING : | 188 ? MediaState::PLAYING |
202 producer_state_; | 189 : producer_state_; |
203 status->timeline_transform = status_transform_.Clone(); | 190 status->timeline_transform = status_transform_.Clone(); |
204 callback.Run(status_version_, status.Pass()); | 191 callback.Run(status_version_, status.Pass()); |
205 } | 192 } |
206 | 193 |
207 void MediaSinkImpl::MaybeSetRate() { | 194 void MediaSinkImpl::MaybeSetRate() { |
208 if (producer_state_ < MediaState::PAUSED || rate_ == target_rate_) { | 195 if (producer_state_ < MediaState::PAUSED || rate_ == target_rate_) { |
209 return; | 196 return; |
210 } | 197 } |
211 | 198 |
212 DCHECK(rate_control_); | 199 DCHECK(rate_control_); |
213 | 200 |
214 // Desired rate in frames per second. | 201 // Desired rate in frames per second. |
215 LinearTransform::Ratio rate_frames_per_second( | 202 LinearTransform::Ratio rate_frames_per_second( |
216 static_cast<uint32_t>(frames_per_second_ * target_rate_), 1); | 203 static_cast<uint32_t>(frames_per_second_ * target_rate_), 1); |
217 | 204 |
218 // Local time rate in seconds_per_tick. | 205 // Local time rate in seconds_per_tick. |
219 LinearTransform::Ratio local_seconds_per_tick( | 206 LinearTransform::Ratio local_seconds_per_tick(LocalDuration::period::num, |
220 LocalDuration::period::num, | 207 LocalDuration::period::den); |
221 LocalDuration::period::den); | |
222 | 208 |
223 // Desired rate in frames per local tick. | 209 // Desired rate in frames per local tick. |
224 LinearTransform::Ratio rate_frames_per_tick; | 210 LinearTransform::Ratio rate_frames_per_tick; |
225 bool success = | 211 bool success = LinearTransform::Ratio::Compose( |
226 LinearTransform::Ratio::Compose( | 212 local_seconds_per_tick, rate_frames_per_second, &rate_frames_per_tick); |
227 local_seconds_per_tick, | |
228 rate_frames_per_second, | |
229 &rate_frames_per_tick); | |
230 DCHECK(success) | 213 DCHECK(success) |
231 << "LinearTransform::Ratio::Compose reports loss of precision"; | 214 << "LinearTransform::Ratio::Compose reports loss of precision"; |
232 | 215 |
233 // TODO(dalesat): start_local_time should be supplied via the mojo interface. | 216 // TODO(dalesat): start_local_time should be supplied via the mojo interface. |
234 // For now, it's hard-coded to be 30ms in the future. | 217 // For now, it's hard-coded to be 30ms in the future. |
235 // The local time when we want the rate to change. | 218 // The local time when we want the rate to change. |
236 int64_t start_local_time = | 219 int64_t start_local_time = |
237 (LocalClock::now().time_since_epoch() + std::chrono::milliseconds(30)). | 220 (LocalClock::now().time_since_epoch() + std::chrono::milliseconds(30)) |
238 count(); | 221 .count(); |
239 | 222 |
240 // The media time corresponding to start_local_time. | 223 // The media time corresponding to start_local_time. |
241 int64_t start_media_time; | 224 int64_t start_media_time; |
242 if (flushed_ && | 225 if (flushed_ && producer_->GetFirstPtsSinceFlush() != Packet::kUnknownPts) { |
243 producer_->GetFirstPtsSinceFlush() != Packet::kUnknownPts) { | |
244 // We're getting started initially or after a flush/prime, so the media | 226 // We're getting started initially or after a flush/prime, so the media |
245 // time corresponding to start_local_time should be the PTS of | 227 // time corresponding to start_local_time should be the PTS of |
246 // the first packet. | 228 // the first packet. |
247 start_media_time = producer_->GetFirstPtsSinceFlush(); | 229 start_media_time = producer_->GetFirstPtsSinceFlush(); |
248 } else { | 230 } else { |
249 // We're resuming, so the media time corresponding to start_local_time can | 231 // We're resuming, so the media time corresponding to start_local_time can |
250 // be calculated using the existing transform. | 232 // be calculated using the existing transform. |
251 success = | 233 success = |
252 transform_.DoForwardTransform(start_local_time, &start_media_time); | 234 transform_.DoForwardTransform(start_local_time, &start_media_time); |
253 DCHECK(success) | 235 DCHECK(success) |
(...skipping 11 matching lines...) Expand all Loading... |
265 rate_quad->reference_offset = start_media_time; | 247 rate_quad->reference_offset = start_media_time; |
266 rate_quad->target_offset = start_local_time; | 248 rate_quad->target_offset = start_local_time; |
267 rate_quad->reference_delta = rate_frames_per_tick.numerator; | 249 rate_quad->reference_delta = rate_frames_per_tick.numerator; |
268 rate_quad->target_delta = rate_frames_per_tick.denominator; | 250 rate_quad->target_delta = rate_frames_per_tick.denominator; |
269 | 251 |
270 rate_control_->SetCurrentQuad(rate_quad.Pass()); | 252 rate_control_->SetCurrentQuad(rate_quad.Pass()); |
271 | 253 |
272 // Get the frame rate in frames per local tick. | 254 // Get the frame rate in frames per local tick. |
273 LinearTransform::Ratio frame_rate_frames_per_second(frames_per_second_, 1); | 255 LinearTransform::Ratio frame_rate_frames_per_second(frames_per_second_, 1); |
274 LinearTransform::Ratio frame_rate_frames_per_tick; | 256 LinearTransform::Ratio frame_rate_frames_per_tick; |
275 success = LinearTransform::Ratio::Compose( | 257 success = LinearTransform::Ratio::Compose(local_seconds_per_tick, |
276 local_seconds_per_tick, | 258 frame_rate_frames_per_second, |
277 frame_rate_frames_per_second, | 259 &frame_rate_frames_per_tick); |
278 &frame_rate_frames_per_tick); | |
279 DCHECK(success) | 260 DCHECK(success) |
280 << "LinearTransform::Ratio::Compose reports loss of precision"; | 261 << "LinearTransform::Ratio::Compose reports loss of precision"; |
281 | 262 |
282 // Create a LinearTransform to translate from presentation units to | 263 // Create a LinearTransform to translate from presentation units to |
283 // local duration units. | 264 // local duration units. |
284 LinearTransform local_to_presentation(0, frame_rate_frames_per_tick, 0); | 265 LinearTransform local_to_presentation(0, frame_rate_frames_per_tick, 0); |
285 | 266 |
286 status_transform_ = TimelineTransform::New(); | 267 status_transform_ = TimelineTransform::New(); |
287 status_transform_->quad = TimelineQuad::New(); | 268 status_transform_->quad = TimelineQuad::New(); |
288 | 269 |
289 // Translate the current transform quad so the presentation time units | 270 // Translate the current transform quad so the presentation time units |
290 // are the same as the local time units. | 271 // are the same as the local time units. |
291 success = local_to_presentation.DoReverseTransform( | 272 success = local_to_presentation.DoReverseTransform( |
292 start_media_time, | 273 start_media_time, &status_transform_->quad->reference_offset); |
293 &status_transform_->quad->reference_offset); | |
294 DCHECK(success) | 274 DCHECK(success) |
295 << "LinearTransform::DoReverseTransform reports loss of precision"; | 275 << "LinearTransform::DoReverseTransform reports loss of precision"; |
296 status_transform_->quad->target_offset = start_local_time; | 276 status_transform_->quad->target_offset = start_local_time; |
297 int64_t presentation_delta; | 277 int64_t presentation_delta; |
298 success = local_to_presentation.DoReverseTransform( | 278 success = local_to_presentation.DoReverseTransform( |
299 static_cast<int64_t>(rate_frames_per_tick.numerator), | 279 static_cast<int64_t>(rate_frames_per_tick.numerator), |
300 &presentation_delta); | 280 &presentation_delta); |
301 DCHECK(success) | 281 DCHECK(success) |
302 << "LinearTransform::DoReverseTransform reports loss of precision"; | 282 << "LinearTransform::DoReverseTransform reports loss of precision"; |
303 status_transform_->quad->reference_delta = | 283 status_transform_->quad->reference_delta = |
304 static_cast<int32_t>(presentation_delta); | 284 static_cast<int32_t>(presentation_delta); |
305 status_transform_->quad->target_delta = rate_frames_per_tick.denominator; | 285 status_transform_->quad->target_delta = rate_frames_per_tick.denominator; |
306 LinearTransform::Ratio::Reduce( | 286 LinearTransform::Ratio::Reduce(&status_transform_->quad->reference_delta, |
307 &status_transform_->quad->reference_delta, | 287 &status_transform_->quad->target_delta); |
308 &status_transform_->quad->target_delta); | |
309 | 288 |
310 rate_ = target_rate_; | 289 rate_ = target_rate_; |
311 StatusUpdated(); | 290 StatusUpdated(); |
312 } | 291 } |
313 | 292 |
314 } // namespace media | 293 } // namespace media |
315 } // namespace mojo | 294 } // namespace mojo |
OLD | NEW |