| 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 |