| 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/public/cpp/application/connect.h" | 6 #include "mojo/public/cpp/application/connect.h" |
| 7 #include "mojo/services/media/common/cpp/timeline.h" | 7 #include "mojo/services/media/common/cpp/timeline.h" |
| 8 #include "services/media/factory_service/media_player_impl.h" | 8 #include "services/media/factory_service/media_player_impl.h" |
| 9 #include "services/media/framework/parts/reader.h" | 9 #include "services/media/framework/parts/reader.h" |
| 10 #include "services/media/framework/util/callback_joiner.h" | 10 #include "services/media/framework/util/callback_joiner.h" |
| (...skipping 13 matching lines...) Expand all Loading... |
| 24 MediaPlayerImpl::MediaPlayerImpl(InterfaceHandle<SeekingReader> reader, | 24 MediaPlayerImpl::MediaPlayerImpl(InterfaceHandle<SeekingReader> reader, |
| 25 InterfaceRequest<MediaPlayer> request, | 25 InterfaceRequest<MediaPlayer> request, |
| 26 MediaFactoryService* owner) | 26 MediaFactoryService* owner) |
| 27 : MediaFactoryService::Product<MediaPlayer>(this, request.Pass(), owner) { | 27 : MediaFactoryService::Product<MediaPlayer>(this, request.Pass(), owner) { |
| 28 DCHECK(reader); | 28 DCHECK(reader); |
| 29 | 29 |
| 30 status_publisher_.SetCallbackRunner([this](const GetStatusCallback& callback, | 30 status_publisher_.SetCallbackRunner([this](const GetStatusCallback& callback, |
| 31 uint64_t version) { | 31 uint64_t version) { |
| 32 MediaPlayerStatusPtr status = MediaPlayerStatus::New(); | 32 MediaPlayerStatusPtr status = MediaPlayerStatus::New(); |
| 33 status->timeline_transform = TimelineTransform::From(timeline_function_); | 33 status->timeline_transform = TimelineTransform::From(timeline_function_); |
| 34 status->end_of_stream = AllSinksAtEndOfStream(); | 34 status->end_of_stream = end_of_stream_; |
| 35 status->metadata = metadata_.Clone(); | 35 status->metadata = metadata_.Clone(); |
| 36 callback.Run(version, status.Pass()); | 36 callback.Run(version, status.Pass()); |
| 37 }); | 37 }); |
| 38 | 38 |
| 39 state_ = State::kWaiting; | 39 state_ = State::kWaiting; |
| 40 | 40 |
| 41 ConnectToService(app()->shell(), "mojo:media_factory", GetProxy(&factory_)); | 41 ConnectToService(app()->shell(), "mojo:media_factory", GetProxy(&factory_)); |
| 42 | 42 |
| 43 factory_->CreateDemux(reader.Pass(), GetProxy(&demux_)); | 43 factory_->CreateDemux(reader.Pass(), GetProxy(&demux_)); |
| 44 HandleDemuxMetadataUpdates(); |
| 44 | 45 |
| 45 HandleDemuxMetadataUpdates(); | 46 factory_->CreateTimelineController(GetProxy(&timeline_controller_)); |
| 47 timeline_controller_->GetControlSite(GetProxy(&timeline_control_site_)); |
| 48 timeline_control_site_->GetTimelineConsumer(GetProxy(&timeline_consumer_)); |
| 49 HandleTimelineControlSiteStatusUpdates(); |
| 46 | 50 |
| 47 demux_->Describe([this](mojo::Array<MediaTypePtr> stream_types) { | 51 demux_->Describe([this](mojo::Array<MediaTypePtr> stream_types) { |
| 48 // Populate streams_ and enable the streams we want. | 52 // Populate streams_ and enable the streams we want. |
| 49 std::shared_ptr<CallbackJoiner> callback_joiner = CallbackJoiner::Create(); | 53 std::shared_ptr<CallbackJoiner> callback_joiner = CallbackJoiner::Create(); |
| 50 | 54 |
| 51 for (MediaTypePtr& stream_type : stream_types) { | 55 for (MediaTypePtr& stream_type : stream_types) { |
| 52 streams_.push_back(std::unique_ptr<Stream>( | 56 streams_.push_back(std::unique_ptr<Stream>(new Stream())); |
| 53 new Stream(streams_.size(), stream_type.Pass()))); | |
| 54 Stream& stream = *streams_.back(); | 57 Stream& stream = *streams_.back(); |
| 55 switch (stream.media_type_->medium) { | 58 switch (stream_type->medium) { |
| 56 case MediaTypeMedium::AUDIO: | 59 case MediaTypeMedium::AUDIO: |
| 57 stream.enabled_ = true; | 60 PrepareStream(&stream, streams_.size() - 1, stream_type, |
| 58 PrepareStream(&stream, "mojo:audio_server", | 61 "mojo:audio_server", callback_joiner->NewCallback()); |
| 59 callback_joiner->NewCallback()); | |
| 60 break; | 62 break; |
| 61 case MediaTypeMedium::VIDEO: | 63 case MediaTypeMedium::VIDEO: |
| 62 stream.enabled_ = true; | |
| 63 // TODO(dalesat): Send video somewhere. | 64 // TODO(dalesat): Send video somewhere. |
| 64 PrepareStream(&stream, "nowhere", callback_joiner->NewCallback()); | 65 PrepareStream(&stream, streams_.size() - 1, stream_type, "nowhere", |
| 66 callback_joiner->NewCallback()); |
| 65 break; | 67 break; |
| 66 // TODO(dalesat): Enable other stream types. | 68 // TODO(dalesat): Enable other stream types. |
| 67 default: | 69 default: |
| 68 break; | 70 break; |
| 69 } | 71 } |
| 70 } | 72 } |
| 71 | 73 |
| 72 callback_joiner->WhenJoined([this]() { | 74 callback_joiner->WhenJoined([this]() { |
| 73 // The enabled streams are prepared. | 75 // The enabled streams are prepared. |
| 74 factory_.reset(); | 76 factory_.reset(); |
| 75 state_ = State::kPaused; | 77 state_ = State::kPaused; |
| 76 Update(); | 78 Update(); |
| 77 }); | 79 }); |
| 78 }); | 80 }); |
| 79 } | 81 } |
| 80 | 82 |
| 81 MediaPlayerImpl::~MediaPlayerImpl() {} | 83 MediaPlayerImpl::~MediaPlayerImpl() {} |
| 82 | 84 |
| 83 void MediaPlayerImpl::Update() { | 85 void MediaPlayerImpl::Update() { |
| 84 while (true) { | 86 while (true) { |
| 85 switch (state_) { | 87 switch (state_) { |
| 86 case State::kPaused: | 88 case State::kPaused: |
| 87 if (target_position_ != kUnspecifiedTime) { | 89 if (target_position_ != kUnspecifiedTime) { |
| 88 WhenPausedAndSeeking(); | 90 WhenPausedAndSeeking(); |
| 89 break; | 91 break; |
| 90 } | 92 } |
| 91 | 93 |
| 92 if (target_state_ == State::kPlaying) { | 94 if (target_state_ == State::kPlaying) { |
| 93 if (!flushed_) { | 95 if (!flushed_) { |
| 94 SetSinkTimelineTransforms(1, 1); | 96 SetTimelineTransform(1, 1); |
| 95 state_ = State::kWaiting; | 97 state_ = State::kWaiting; |
| 96 break; | 98 break; |
| 97 } | 99 } |
| 98 | 100 |
| 99 flushed_ = false; | 101 flushed_ = false; |
| 100 state_ = State::kWaiting; | 102 state_ = State::kWaiting; |
| 101 demux_->Prime([this]() { | 103 demux_->Prime([this]() { |
| 102 SetSinkTimelineTransforms(1, 1); | 104 SetTimelineTransform(1, 1); |
| 103 state_ = State::kWaiting; | 105 state_ = State::kWaiting; |
| 104 Update(); | 106 Update(); |
| 105 }); | 107 }); |
| 106 } | 108 } |
| 107 return; | 109 return; |
| 108 | 110 |
| 109 case State::kPlaying: | 111 case State::kPlaying: |
| 110 if (target_position_ != kUnspecifiedTime || | 112 if (target_position_ != kUnspecifiedTime || |
| 111 target_state_ == State::kPaused) { | 113 target_state_ == State::kPaused) { |
| 112 SetSinkTimelineTransforms(1, 0); | 114 SetTimelineTransform(1, 0); |
| 113 state_ = State::kWaiting; | 115 state_ = State::kWaiting; |
| 114 break; | 116 break; |
| 115 } | 117 } |
| 116 | 118 |
| 117 if (AllSinksAtEndOfStream()) { | 119 if (end_of_stream_) { |
| 118 target_state_ = State::kPaused; | 120 target_state_ = State::kPaused; |
| 119 state_ = State::kPaused; | 121 state_ = State::kPaused; |
| 120 break; | 122 break; |
| 121 } | 123 } |
| 122 return; | 124 return; |
| 123 | 125 |
| 124 case State::kWaiting: | 126 case State::kWaiting: |
| 125 return; | 127 return; |
| 126 } | 128 } |
| 127 } | 129 } |
| (...skipping 15 matching lines...) Expand all Loading... |
| 143 state_ = State::kWaiting; | 145 state_ = State::kWaiting; |
| 144 DCHECK(target_position_ != kUnspecifiedTime); | 146 DCHECK(target_position_ != kUnspecifiedTime); |
| 145 demux_->Seek(target_position_, [this]() { | 147 demux_->Seek(target_position_, [this]() { |
| 146 transform_subject_time_ = target_position_; | 148 transform_subject_time_ = target_position_; |
| 147 target_position_ = kUnspecifiedTime; | 149 target_position_ = kUnspecifiedTime; |
| 148 state_ = State::kPaused; | 150 state_ = State::kPaused; |
| 149 Update(); | 151 Update(); |
| 150 }); | 152 }); |
| 151 } | 153 } |
| 152 | 154 |
| 153 void MediaPlayerImpl::SetSinkTimelineTransforms(uint32_t reference_delta, | 155 void MediaPlayerImpl::SetTimelineTransform(uint32_t reference_delta, |
| 154 uint32_t subject_delta) { | 156 uint32_t subject_delta) { |
| 155 SetSinkTimelineTransforms( | 157 timeline_consumer_->SetTimelineTransform( |
| 156 transform_subject_time_, reference_delta, subject_delta, | 158 transform_subject_time_, reference_delta, subject_delta, |
| 157 Timeline::local_now() + kMinimumLeadTime, kUnspecifiedTime); | 159 Timeline::local_now() + kMinimumLeadTime, kUnspecifiedTime, |
| 158 } | 160 [this, subject_delta](bool completed) { |
| 161 RCHECK(state_ == State::kWaiting); |
| 159 | 162 |
| 160 void MediaPlayerImpl::SetSinkTimelineTransforms( | 163 if (subject_delta == 0) { |
| 161 int64_t subject_time, | 164 state_ = State::kPaused; |
| 162 uint32_t reference_delta, | 165 } else { |
| 163 uint32_t subject_delta, | 166 state_ = State::kPlaying; |
| 164 int64_t effective_reference_time, | 167 } |
| 165 int64_t effective_subject_time) { | |
| 166 std::shared_ptr<CallbackJoiner> callback_joiner = CallbackJoiner::Create(); | |
| 167 | 168 |
| 168 for (auto& stream : streams_) { | 169 Update(); |
| 169 if (stream->enabled_) { | 170 }); |
| 170 DCHECK(stream->timeline_consumer_); | |
| 171 callback_joiner->Spawn(); | |
| 172 stream->timeline_consumer_->SetTimelineTransform( | |
| 173 subject_time, reference_delta, subject_delta, | |
| 174 effective_reference_time, effective_subject_time, | |
| 175 [this, callback_joiner](bool completed) { | |
| 176 callback_joiner->Complete(); | |
| 177 }); | |
| 178 } | |
| 179 } | |
| 180 | |
| 181 transform_subject_time_ = kUnspecifiedTime; | |
| 182 | |
| 183 callback_joiner->WhenJoined([this, subject_delta]() { | |
| 184 RCHECK(state_ == State::kWaiting); | |
| 185 | |
| 186 if (subject_delta == 0) { | |
| 187 state_ = State::kPaused; | |
| 188 } else { | |
| 189 state_ = State::kPlaying; | |
| 190 } | |
| 191 | |
| 192 Update(); | |
| 193 }); | |
| 194 } | |
| 195 | |
| 196 bool MediaPlayerImpl::AllSinksAtEndOfStream() { | |
| 197 int result = false; | |
| 198 | |
| 199 for (auto& stream : streams_) { | |
| 200 if (stream->enabled_) { | |
| 201 result = stream->end_of_stream_; | |
| 202 if (!result) { | |
| 203 break; | |
| 204 } | |
| 205 } | |
| 206 } | |
| 207 | |
| 208 return result; | |
| 209 } | 171 } |
| 210 | 172 |
| 211 void MediaPlayerImpl::GetStatus(uint64_t version_last_seen, | 173 void MediaPlayerImpl::GetStatus(uint64_t version_last_seen, |
| 212 const GetStatusCallback& callback) { | 174 const GetStatusCallback& callback) { |
| 213 status_publisher_.Get(version_last_seen, callback); | 175 status_publisher_.Get(version_last_seen, callback); |
| 214 } | 176 } |
| 215 | 177 |
| 216 void MediaPlayerImpl::Play() { | 178 void MediaPlayerImpl::Play() { |
| 217 target_state_ = State::kPlaying; | 179 target_state_ = State::kPlaying; |
| 218 Update(); | 180 Update(); |
| 219 } | 181 } |
| 220 | 182 |
| 221 void MediaPlayerImpl::Pause() { | 183 void MediaPlayerImpl::Pause() { |
| 222 target_state_ = State::kPaused; | 184 target_state_ = State::kPaused; |
| 223 Update(); | 185 Update(); |
| 224 } | 186 } |
| 225 | 187 |
| 226 void MediaPlayerImpl::Seek(int64_t position) { | 188 void MediaPlayerImpl::Seek(int64_t position) { |
| 227 target_position_ = position; | 189 target_position_ = position; |
| 228 Update(); | 190 Update(); |
| 229 } | 191 } |
| 230 | 192 |
| 231 void MediaPlayerImpl::PrepareStream(Stream* stream, | 193 void MediaPlayerImpl::PrepareStream(Stream* stream, |
| 194 size_t index, |
| 195 const MediaTypePtr& input_media_type, |
| 232 const String& url, | 196 const String& url, |
| 233 const std::function<void()>& callback) { | 197 const std::function<void()>& callback) { |
| 234 DCHECK(factory_); | 198 DCHECK(factory_); |
| 235 | 199 |
| 236 demux_->GetProducer(stream->index_, GetProxy(&stream->encoded_producer_)); | 200 demux_->GetProducer(index, GetProxy(&stream->encoded_producer_)); |
| 237 | 201 |
| 238 if (stream->media_type_->encoding != MediaType::kAudioEncodingLpcm && | 202 if (input_media_type->encoding != MediaType::kAudioEncodingLpcm && |
| 239 stream->media_type_->encoding != MediaType::kVideoEncodingUncompressed) { | 203 input_media_type->encoding != MediaType::kVideoEncodingUncompressed) { |
| 240 std::shared_ptr<CallbackJoiner> callback_joiner = CallbackJoiner::Create(); | 204 std::shared_ptr<CallbackJoiner> callback_joiner = CallbackJoiner::Create(); |
| 241 | 205 |
| 242 // Compressed media. Insert a decoder in front of the sink. The sink would | 206 // Compressed media. Insert a decoder in front of the sink. The sink would |
| 243 // add its own internal decoder, but we want to test the decoder. | 207 // add its own internal decoder, but we want to test the decoder. |
| 244 factory_->CreateDecoder(stream->media_type_.Clone(), | 208 factory_->CreateDecoder(input_media_type.Clone(), |
| 245 GetProxy(&stream->decoder_)); | 209 GetProxy(&stream->decoder_)); |
| 246 | 210 |
| 247 MediaConsumerPtr decoder_consumer; | 211 MediaConsumerPtr decoder_consumer; |
| 248 stream->decoder_->GetConsumer(GetProxy(&decoder_consumer)); | 212 stream->decoder_->GetConsumer(GetProxy(&decoder_consumer)); |
| 249 | 213 |
| 250 callback_joiner->Spawn(); | 214 callback_joiner->Spawn(); |
| 251 stream->encoded_producer_->Connect(decoder_consumer.Pass(), | 215 stream->encoded_producer_->Connect(decoder_consumer.Pass(), |
| 252 [stream, callback_joiner]() { | 216 [stream, callback_joiner]() { |
| 253 stream->encoded_producer_.reset(); | 217 stream->encoded_producer_.reset(); |
| 254 callback_joiner->Complete(); | 218 callback_joiner->Complete(); |
| 255 }); | 219 }); |
| 256 | 220 |
| 257 callback_joiner->Spawn(); | 221 callback_joiner->Spawn(); |
| 258 stream->decoder_->GetOutputType( | 222 stream->decoder_->GetOutputType( |
| 259 [this, stream, url, callback_joiner](MediaTypePtr output_type) { | 223 [this, stream, url, callback_joiner](MediaTypePtr output_type) { |
| 260 stream->decoder_->GetProducer(GetProxy(&stream->decoded_producer_)); | 224 stream->decoder_->GetProducer(GetProxy(&stream->decoded_producer_)); |
| 261 CreateSink(stream, output_type, url, callback_joiner->NewCallback()); | 225 CreateSink(stream, output_type, url, callback_joiner->NewCallback()); |
| 262 callback_joiner->Complete(); | 226 callback_joiner->Complete(); |
| 263 }); | 227 }); |
| 264 | 228 |
| 265 callback_joiner->WhenJoined(callback); | 229 callback_joiner->WhenJoined(callback); |
| 266 } else { | 230 } else { |
| 267 // Uncompressed media. Connect the demux stream directly to the sink. This | 231 // Uncompressed media. Connect the demux stream directly to the sink. This |
| 268 // would work for compressed media as well (the sink would decode), but we | 232 // would work for compressed media as well (the sink would decode), but we |
| 269 // want to test the decoder. | 233 // want to test the decoder. |
| 270 stream->decoded_producer_ = stream->encoded_producer_.Pass(); | 234 stream->decoded_producer_ = stream->encoded_producer_.Pass(); |
| 271 CreateSink(stream, stream->media_type_, url, callback); | 235 CreateSink(stream, input_media_type, url, callback); |
| 272 } | 236 } |
| 273 } | 237 } |
| 274 | 238 |
| 275 void MediaPlayerImpl::CreateSink(Stream* stream, | 239 void MediaPlayerImpl::CreateSink(Stream* stream, |
| 276 const MediaTypePtr& input_media_type, | 240 const MediaTypePtr& input_media_type, |
| 277 const String& url, | 241 const String& url, |
| 278 const std::function<void()>& callback) { | 242 const std::function<void()>& callback) { |
| 279 DCHECK(input_media_type); | 243 DCHECK(input_media_type); |
| 280 DCHECK(stream->decoded_producer_); | 244 DCHECK(stream->decoded_producer_); |
| 281 DCHECK(factory_); | 245 DCHECK(factory_); |
| 282 | 246 |
| 283 factory_->CreateSink(url, input_media_type.Clone(), GetProxy(&stream->sink_)); | 247 factory_->CreateSink(url, input_media_type.Clone(), GetProxy(&stream->sink_)); |
| 284 stream->sink_->GetTimelineControlSite( | |
| 285 GetProxy(&stream->timeline_control_site_)); | |
| 286 | 248 |
| 287 HandleTimelineControlSiteStatusUpdates(stream); | 249 MediaTimelineControlSitePtr timeline_control_site; |
| 250 stream->sink_->GetTimelineControlSite(GetProxy(&timeline_control_site)); |
| 288 | 251 |
| 289 stream->timeline_control_site_->GetTimelineConsumer( | 252 timeline_controller_->AddControlSite(timeline_control_site.Pass()); |
| 290 GetProxy(&stream->timeline_consumer_)); | |
| 291 | 253 |
| 292 MediaConsumerPtr consumer; | 254 MediaConsumerPtr consumer; |
| 293 stream->sink_->GetConsumer(GetProxy(&consumer)); | 255 stream->sink_->GetConsumer(GetProxy(&consumer)); |
| 294 | 256 |
| 295 stream->decoded_producer_->Connect(consumer.Pass(), | 257 stream->decoded_producer_->Connect(consumer.Pass(), |
| 296 [this, callback, stream]() { | 258 [this, callback, stream]() { |
| 297 stream->decoded_producer_.reset(); | 259 stream->decoded_producer_.reset(); |
| 298 callback(); | 260 callback(); |
| 299 }); | 261 }); |
| 300 } | 262 } |
| 301 | 263 |
| 302 void MediaPlayerImpl::HandleDemuxMetadataUpdates(uint64_t version, | 264 void MediaPlayerImpl::HandleDemuxMetadataUpdates(uint64_t version, |
| 303 MediaMetadataPtr metadata) { | 265 MediaMetadataPtr metadata) { |
| 304 if (metadata) { | 266 if (metadata) { |
| 305 metadata_ = metadata.Pass(); | 267 metadata_ = metadata.Pass(); |
| 306 status_publisher_.SendUpdates(); | 268 status_publisher_.SendUpdates(); |
| 307 } | 269 } |
| 308 | 270 |
| 309 demux_->GetMetadata(version, | 271 demux_->GetMetadata(version, |
| 310 [this](uint64_t version, MediaMetadataPtr metadata) { | 272 [this](uint64_t version, MediaMetadataPtr metadata) { |
| 311 HandleDemuxMetadataUpdates(version, metadata.Pass()); | 273 HandleDemuxMetadataUpdates(version, metadata.Pass()); |
| 312 }); | 274 }); |
| 313 } | 275 } |
| 314 | 276 |
| 315 void MediaPlayerImpl::HandleTimelineControlSiteStatusUpdates( | 277 void MediaPlayerImpl::HandleTimelineControlSiteStatusUpdates( |
| 316 Stream* stream, | |
| 317 uint64_t version, | 278 uint64_t version, |
| 318 MediaTimelineControlSiteStatusPtr status) { | 279 MediaTimelineControlSiteStatusPtr status) { |
| 319 if (status) { | 280 if (status) { |
| 320 // TODO(dalesat): Why does one sink determine timeline_function_? | |
| 321 timeline_function_ = status->timeline_transform.To<TimelineFunction>(); | 281 timeline_function_ = status->timeline_transform.To<TimelineFunction>(); |
| 322 stream->end_of_stream_ = status->end_of_stream; | 282 end_of_stream_ = status->end_of_stream; |
| 323 status_publisher_.SendUpdates(); | 283 status_publisher_.SendUpdates(); |
| 324 Update(); | 284 Update(); |
| 325 } | 285 } |
| 326 | 286 |
| 327 stream->timeline_control_site_->GetStatus( | 287 timeline_control_site_->GetStatus( |
| 328 version, [this, stream](uint64_t version, | 288 version, |
| 329 MediaTimelineControlSiteStatusPtr status) { | 289 [this](uint64_t version, MediaTimelineControlSiteStatusPtr status) { |
| 330 HandleTimelineControlSiteStatusUpdates(stream, version, status.Pass()); | 290 HandleTimelineControlSiteStatusUpdates(version, status.Pass()); |
| 331 }); | 291 }); |
| 332 } | 292 } |
| 333 | 293 |
| 334 MediaPlayerImpl::Stream::Stream(size_t index, MediaTypePtr media_type) | 294 MediaPlayerImpl::Stream::Stream() {} |
| 335 : index_(index), media_type_(media_type.Pass()) {} | |
| 336 | 295 |
| 337 MediaPlayerImpl::Stream::~Stream() {} | 296 MediaPlayerImpl::Stream::~Stream() {} |
| 338 | 297 |
| 339 } // namespace media | 298 } // namespace media |
| 340 } // namespace mojo | 299 } // namespace mojo |
| OLD | NEW |