Motown: Use new TimelineTransform and related definitions 1) Deleted rate_control.mojom and replaced with timelines.mojom and timeline_controller.mojom. 2) Removed redundant functionality from MediaTimelineController etc. 3) Deleted rate_control_base.* rate control implementation and replaced with timeline_control_site.* timeline control site implementation. 4) Moved mojo_publisher from factory_service to common so it can be used by timeline_control_site. 5) Updated many dependencies thereof. R=kulakowski@chromium.org Review URL: https://codereview.chromium.org/1986303002 .
diff --git a/examples/audio_play_test/BUILD.gn b/examples/audio_play_test/BUILD.gn index a710e28..bda93ca 100644 --- a/examples/audio_play_test/BUILD.gn +++ b/examples/audio_play_test/BUILD.gn
@@ -19,6 +19,7 @@ "//mojo/services/media/audio/interfaces", "//mojo/services/media/common/cpp", "//mojo/services/media/common/interfaces", + "//mojo/services/media/core/interfaces", "//mojo/services/network/interfaces", ] @@ -34,6 +35,7 @@ "//mojo/services/media/audio/interfaces", "//mojo/services/media/common/cpp", "//mojo/services/media/common/interfaces", + "//mojo/services/media/core/interfaces", ] sources = [
diff --git a/examples/audio_play_test/play_tone.cc b/examples/audio_play_test/play_tone.cc index 3a0ebc1..025bdbb 100644 --- a/examples/audio_play_test/play_tone.cc +++ b/examples/audio_play_test/play_tone.cc
@@ -16,7 +16,7 @@ #include "mojo/services/media/common/cpp/circular_buffer_media_pipe_adapter.h" #include "mojo/services/media/common/cpp/linear_transform.h" #include "mojo/services/media/common/cpp/local_time.h" -#include "mojo/services/media/common/interfaces/rate_control.mojom.h" +#include "mojo/services/media/common/interfaces/timelines.mojom.h" namespace mojo { namespace media { @@ -52,8 +52,8 @@ void OnConnectionError(const std::string& connection_name); AudioServerPtr audio_server_; - AudioTrackPtr audio_track_; - RateControlPtr rate_control_; + AudioTrackPtr audio_track_; + TimelineConsumerPtr timeline_consumer_; std::unique_ptr<CircularBufferMediaPipeAdapter> audio_pipe_; bool clock_started_ = false; @@ -64,7 +64,7 @@ }; void PlayToneApp::Quit() { - rate_control_.reset(); + timeline_consumer_.reset(); audio_pipe_.reset(); audio_track_.reset(); audio_server_.reset(); @@ -115,11 +115,12 @@ // TODO(johngro): do something useful with our capabilities description. sink_desc.reset(); - // Grab the rate control interface for our audio renderer. - audio_track_->GetRateControl(GetProxy(&rate_control_)); - rate_control_.set_connection_error_handler([this]() { - OnConnectionError("rate_control"); - }); + // Grab the timeline consumer interface for our audio renderer. + MediaTimelineControlSitePtr timeline_control_site; + audio_track_->GetTimelineControlSite(GetProxy(&timeline_control_site)); + timeline_control_site->GetTimelineConsumer(GetProxy(&timeline_consumer_)); + timeline_consumer_.set_connection_error_handler( + [this]() { OnConnectionError("timeline_consumer"); }); // Configure our sink for 16-bit 48KHz mono. AudioTrackConfigurationPtr cfg = AudioTrackConfiguration::New(); @@ -204,20 +205,11 @@ } if (!clock_started_) { - // In theory, this could be done at compile time using std::ratio, but - // std::ratio is prohibited. - LinearTransform::Ratio audio_rate(SAMP_FREQ, 1); - LinearTransform::Ratio local_time_rate(LocalDuration::period::num, - LocalDuration::period::den); - LinearTransform::Ratio rate; - bool success = LinearTransform::Ratio::Compose(local_time_rate, - audio_rate, - &rate); - MOJO_DCHECK(success); // assert that there was no loss of precision. + MOJO_LOG(INFO) << "Setting rate 1/1"; - MOJO_LOG(INFO) << "Setting rate " << rate; - - rate_control_->SetRate(rate.numerator, rate.denominator); + timeline_consumer_->SetTimelineTransform( + kUnspecifiedTime, 1, 1, kUnspecifiedTime, kUnspecifiedTime, + [](bool completed) {}); clock_started_ = true; } }
diff --git a/examples/audio_play_test/play_wav.cc b/examples/audio_play_test/play_wav.cc index d5dc2e1..f5f11ea 100644 --- a/examples/audio_play_test/play_wav.cc +++ b/examples/audio_play_test/play_wav.cc
@@ -128,7 +128,7 @@ AudioServerPtr audio_server_; AudioTrackPtr audio_track_; AudioPipePtr audio_pipe_; - RateControlPtr rate_control_; + TimelineConsumerPtr timeline_consumer_; AudioPacket audio_packet_; PacketCbk playout_complete_cbk_; NetworkServicePtr network_service_; @@ -196,7 +196,7 @@ url_loader_.reset(); network_service_.reset(); audio_pipe_.reset(); - rate_control_.reset(); + timeline_consumer_.reset(); audio_track_.reset(); audio_server_.reset(); } @@ -290,11 +290,12 @@ MediaConsumerPtr media_pipe; audio_track_->Configure(cfg.Pass(), GetProxy(&media_pipe)); - // Grab the rate control interface for our audio renderer. - audio_track_->GetRateControl(GetProxy(&rate_control_)); - rate_control_.set_connection_error_handler([this]() { - OnConnectionError("rate_control"); - }); + // Grab the timeline consumer interface for our audio renderer. + MediaTimelineControlSitePtr timeline_control_site; + audio_track_->GetTimelineControlSite(GetProxy(&timeline_control_site)); + timeline_control_site->GetTimelineConsumer(GetProxy(&timeline_consumer_)); + timeline_consumer_.set_connection_error_handler( + [this]() { OnConnectionError("timeline_consumer"); }); // Set up our media pipe helper, configure its callback and water marks to // kick off the playback process. @@ -501,8 +502,9 @@ } if (!clock_started_ && (audio_pipe_->AboveHiWater() || !payload_len_)) { - LocalTime sched = LocalClock::now() + local_time::from_msec(50); - rate_control_->SetRateAtTargetTime(1, 1, sched.time_since_epoch().count()); + timeline_consumer_->SetTimelineTransform( + kUnspecifiedTime, 1, 1, kUnspecifiedTime, kUnspecifiedTime, + [](bool completed) {}); clock_started_ = true; } }
diff --git a/examples/media_test/media_test.cc b/examples/media_test/media_test.cc index 3e78fe4..c1f0d19 100644 --- a/examples/media_test/media_test.cc +++ b/examples/media_test/media_test.cc
@@ -4,8 +4,8 @@ #include "examples/media_test/media_test.h" #include "mojo/public/cpp/application/connect.h" -#include "mojo/services/media/common/cpp/linear_transform.h" -#include "mojo/services/media/common/cpp/local_time.h" +#include "mojo/services/media/common/cpp/timeline.h" +#include "mojo/services/media/common/cpp/timeline_function.h" #include "mojo/services/media/control/interfaces/media_factory.mojom.h" namespace mojo { @@ -35,10 +35,8 @@ MediaTest::~MediaTest() {} int64_t MediaTest::position_ns() const { - // Apply the transform to the current time. - int64_t position; - transform_.DoForwardTransform(LocalClock::now().time_since_epoch().count(), - &position); + // Apply the timeline function to the current time. + int64_t position = timeline_function_(Timeline::local_now()); if (position < 0) { position = 0; @@ -62,15 +60,8 @@ previous_state_ = state_; state_ = status->state; - // Create a linear transform that translates local time to presentation - // time. Note that 'reference' here refers to the presentation time, and - // 'target' refers to the local time. if (status->timeline_transform) { - transform_ = - LinearTransform(status->timeline_transform->quad->target_offset, - status->timeline_transform->quad->reference_delta, - status->timeline_transform->quad->target_delta, - status->timeline_transform->quad->reference_offset); + timeline_function_ = status->timeline_transform.To<TimelineFunction>(); } metadata_ = status->metadata.Pass();
diff --git a/examples/media_test/media_test.h b/examples/media_test/media_test.h index 00ce340..da5473e 100644 --- a/examples/media_test/media_test.h +++ b/examples/media_test/media_test.h
@@ -7,7 +7,7 @@ #include "base/macros.h" #include "mojo/public/cpp/application/application_impl.h" -#include "mojo/services/media/common/cpp/linear_transform.h" +#include "mojo/services/media/common/cpp/timeline_function.h" #include "mojo/services/media/common/interfaces/rate_control.mojom.h" #include "mojo/services/media/control/interfaces/media_factory.mojom.h" #include "mojo/services/media/control/interfaces/media_player.mojom.h" @@ -63,7 +63,7 @@ MediaPlayerPtr media_player_; MediaState previous_state_ = MediaState::UNPREPARED; MediaState state_ = MediaState::UNPREPARED; - LinearTransform transform_ = LinearTransform(0, 0, 1, 0); + TimelineFunction timeline_function_; MediaMetadataPtr metadata_; UpdateCallback update_callback_;
diff --git a/mojo/dart/packages/mojo_services/BUILD.gn b/mojo/dart/packages/mojo_services/BUILD.gn index 80dc70c..28cd6fd 100644 --- a/mojo/dart/packages/mojo_services/BUILD.gn +++ b/mojo/dart/packages/mojo_services/BUILD.gn
@@ -63,7 +63,6 @@ "lib/mojo/media/media_transport.mojom.dart", "lib/mojo/media/media_type_converter.mojom.dart", "lib/mojo/media/media_types.mojom.dart", - "lib/mojo/media/rate_control.mojom.dart", "lib/mojo/media/timeline_controller.mojom.dart", "lib/mojo/native_viewport_event_dispatcher.mojom.dart", "lib/mojo/navigation.mojom.dart",
diff --git a/mojo/dart/packages/mojo_services/lib/mojo/media/audio_track.mojom.dart b/mojo/dart/packages/mojo_services/lib/mojo/media/audio_track.mojom.dart index f559530..df9cd3e 100644 --- a/mojo/dart/packages/mojo_services/lib/mojo/media/audio_track.mojom.dart +++ b/mojo/dart/packages/mojo_services/lib/mojo/media/audio_track.mojom.dart
@@ -9,7 +9,7 @@ import 'package:mojo/mojo/bindings/types/service_describer.mojom.dart' as service_describer; import 'package:mojo_services/mojo/media/media_transport.mojom.dart' as media_transport_mojom; import 'package:mojo_services/mojo/media/media_types.mojom.dart' as media_types_mojom; -import 'package:mojo_services/mojo/media/rate_control.mojom.dart' as rate_control_mojom; +import 'package:mojo_services/mojo/media/timeline_controller.mojom.dart' as timeline_controller_mojom; @@ -418,15 +418,15 @@ } -class _AudioTrackGetRateControlParams extends bindings.Struct { +class _AudioTrackGetTimelineControlSiteParams extends bindings.Struct { static const List<bindings.StructDataHeader> kVersions = const [ const bindings.StructDataHeader(16, 0) ]; - rate_control_mojom.RateControlInterfaceRequest rateControl = null; + Object timelineControlSite = null; - _AudioTrackGetRateControlParams() : super(kVersions.last.size); + _AudioTrackGetTimelineControlSiteParams() : super(kVersions.last.size); - static _AudioTrackGetRateControlParams deserialize(bindings.Message message) { + static _AudioTrackGetTimelineControlSiteParams deserialize(bindings.Message message) { var decoder = new bindings.Decoder(message); var result = decode(decoder); if (decoder.excessHandles != null) { @@ -435,11 +435,11 @@ return result; } - static _AudioTrackGetRateControlParams decode(bindings.Decoder decoder0) { + static _AudioTrackGetTimelineControlSiteParams decode(bindings.Decoder decoder0) { if (decoder0 == null) { return null; } - _AudioTrackGetRateControlParams result = new _AudioTrackGetRateControlParams(); + _AudioTrackGetTimelineControlSiteParams result = new _AudioTrackGetTimelineControlSiteParams(); var mainDataHeader = decoder0.decodeStructDataHeader(); if (mainDataHeader.version <= kVersions.last.version) { @@ -461,7 +461,7 @@ } if (mainDataHeader.version >= 0) { - result.rateControl = decoder0.decodeInterfaceRequest(8, false, rate_control_mojom.RateControlStub.newFromEndpoint); + result.timelineControlSite = decoder0.decodeInterfaceRequest(8, false, timeline_controller_mojom.MediaTimelineControlSiteStub.newFromEndpoint); } return result; } @@ -469,17 +469,17 @@ void encode(bindings.Encoder encoder) { var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last); try { - encoder0.encodeInterfaceRequest(rateControl, 8, false); + encoder0.encodeInterfaceRequest(timelineControlSite, 8, false); } on bindings.MojoCodecError catch(e) { e.message = "Error encountered while encoding field " - "rateControl of struct _AudioTrackGetRateControlParams: $e"; + "timelineControlSite of struct _AudioTrackGetTimelineControlSiteParams: $e"; rethrow; } } String toString() { - return "_AudioTrackGetRateControlParams(" - "rateControl: $rateControl" ")"; + return "_AudioTrackGetTimelineControlSiteParams(" + "timelineControlSite: $timelineControlSite" ")"; } Map toJson() { @@ -562,7 +562,7 @@ const int _audioTrackMethodDescribeName = 0; const int _audioTrackMethodConfigureName = 1; -const int _audioTrackMethodGetRateControlName = 2; +const int _audioTrackMethodGetTimelineControlSiteName = 2; const int _audioTrackMethodSetGainName = 3; class _AudioTrackServiceDescription implements service_describer.ServiceDescription { @@ -599,8 +599,8 @@ return p; } dynamic describe([Function responseFactory = null]); - void configure(AudioTrackConfiguration configuration, media_transport_mojom.MediaConsumerInterfaceRequest pipe); - void getRateControl(rate_control_mojom.RateControlInterfaceRequest rateControl); + void configure(AudioTrackConfiguration configuration, Object pipe); + void getTimelineControlSite(Object timelineControlSite); void setGain(double dbGain); static const double kMutedGain = -160.0; static const double kMaxGain = 20.0; @@ -719,15 +719,15 @@ ctrl.sendMessage(params, _audioTrackMethodConfigureName); } - void getRateControl(rate_control_mojom.RateControlInterfaceRequest rateControl) { + void getTimelineControlSite(Object timelineControlSite) { if (!ctrl.isBound) { ctrl.proxyError("The Proxy is closed."); return; } - var params = new _AudioTrackGetRateControlParams(); - params.rateControl = rateControl; + var params = new _AudioTrackGetTimelineControlSiteParams(); + params.timelineControlSite = timelineControlSite; ctrl.sendMessage(params, - _audioTrackMethodGetRateControlName); + _audioTrackMethodGetTimelineControlSiteName); } void setGain(double dbGain) { if (!ctrl.isBound) { @@ -804,10 +804,10 @@ message.payload); _impl.configure(params.configuration, params.pipe); break; - case _audioTrackMethodGetRateControlName: - var params = _AudioTrackGetRateControlParams.deserialize( + case _audioTrackMethodGetTimelineControlSiteName: + var params = _AudioTrackGetTimelineControlSiteParams.deserialize( message.payload); - _impl.getRateControl(params.rateControl); + _impl.getTimelineControlSite(params.timelineControlSite); break; case _audioTrackMethodSetGainName: var params = _AudioTrackSetGainParams.deserialize( @@ -878,8 +878,8 @@ void configure(AudioTrackConfiguration configuration, media_transport_mojom.MediaConsumerInterfaceRequest pipe) { return impl.configure(configuration, pipe); } - void getRateControl(rate_control_mojom.RateControlInterfaceRequest rateControl) { - return impl.getRateControl(rateControl); + void getTimelineControlSite(Object timelineControlSite) { + return impl.getTimelineControlSite(timelineControlSite); } void setGain(double dbGain) { return impl.setGain(dbGain);
diff --git a/mojo/dart/packages/mojo_services/lib/mojo/media/media_player.mojom.dart b/mojo/dart/packages/mojo_services/lib/mojo/media/media_player.mojom.dart index 5ab0c9e..d570d96 100644 --- a/mojo/dart/packages/mojo_services/lib/mojo/media/media_player.mojom.dart +++ b/mojo/dart/packages/mojo_services/lib/mojo/media/media_player.mojom.dart
@@ -9,7 +9,7 @@ import 'package:mojo/mojo/bindings/types/service_describer.mojom.dart' as service_describer; import 'package:mojo_services/mojo/media/media_metadata.mojom.dart' as media_metadata_mojom; import 'package:mojo_services/mojo/media/media_state.mojom.dart' as media_state_mojom; -import 'package:mojo_services/mojo/media/rate_control.mojom.dart' as rate_control_mojom; +import 'package:mojo_services/mojo/timelines.mojom.dart' as timelines_mojom; @@ -18,7 +18,7 @@ const bindings.StructDataHeader(32, 0) ]; media_state_mojom.MediaState state = null; - rate_control_mojom.TimelineTransform timelineTransform = null; + timelines_mojom.TimelineTransform timelineTransform = null; media_metadata_mojom.MediaMetadata metadata = null; MediaPlayerStatus() : super(kVersions.last.size); @@ -67,7 +67,7 @@ if (mainDataHeader.version >= 0) { var decoder1 = decoder0.decodePointer(16, true); - result.timelineTransform = rate_control_mojom.TimelineTransform.decode(decoder1); + result.timelineTransform = timelines_mojom.TimelineTransform.decode(decoder1); } if (mainDataHeader.version >= 0) {
diff --git a/mojo/dart/packages/mojo_services/lib/mojo/media/media_sink.mojom.dart b/mojo/dart/packages/mojo_services/lib/mojo/media/media_sink.mojom.dart index b21bfb3..6b73e31 100644 --- a/mojo/dart/packages/mojo_services/lib/mojo/media/media_sink.mojom.dart +++ b/mojo/dart/packages/mojo_services/lib/mojo/media/media_sink.mojom.dart
@@ -9,7 +9,7 @@ import 'package:mojo/mojo/bindings/types/service_describer.mojom.dart' as service_describer; import 'package:mojo_services/mojo/media/media_state.mojom.dart' as media_state_mojom; import 'package:mojo_services/mojo/media/media_transport.mojom.dart' as media_transport_mojom; -import 'package:mojo_services/mojo/media/rate_control.mojom.dart' as rate_control_mojom; +import 'package:mojo_services/mojo/timelines.mojom.dart' as timelines_mojom; @@ -18,7 +18,7 @@ const bindings.StructDataHeader(24, 0) ]; media_state_mojom.MediaState state = null; - rate_control_mojom.TimelineTransform timelineTransform = null; + timelines_mojom.TimelineTransform timelineTransform = null; MediaSinkStatus() : super(kVersions.last.size); @@ -66,7 +66,7 @@ if (mainDataHeader.version >= 0) { var decoder1 = decoder0.decodePointer(16, true); - result.timelineTransform = rate_control_mojom.TimelineTransform.decode(decoder1); + result.timelineTransform = timelines_mojom.TimelineTransform.decode(decoder1); } return result; }
diff --git a/mojo/dart/packages/mojo_services/lib/mojo/media/timeline_controller.mojom.dart b/mojo/dart/packages/mojo_services/lib/mojo/media/timeline_controller.mojom.dart index 4ab5371..8ea67ec 100644 --- a/mojo/dart/packages/mojo_services/lib/mojo/media/timeline_controller.mojom.dart +++ b/mojo/dart/packages/mojo_services/lib/mojo/media/timeline_controller.mojom.dart
@@ -11,100 +11,13 @@ -class MediaTimelineControllerStatus extends bindings.Struct { +class MediaTimelineControlSiteStatus extends bindings.Struct { static const List<bindings.StructDataHeader> kVersions = const [ const bindings.StructDataHeader(24, 0) ]; timelines_mojom.TimelineTransform timelineTransform = null; bool endOfStream = false; - MediaTimelineControllerStatus() : super(kVersions.last.size); - - static MediaTimelineControllerStatus deserialize(bindings.Message message) { - var decoder = new bindings.Decoder(message); - var result = decode(decoder); - if (decoder.excessHandles != null) { - decoder.excessHandles.forEach((h) => h.close()); - } - return result; - } - - static MediaTimelineControllerStatus decode(bindings.Decoder decoder0) { - if (decoder0 == null) { - return null; - } - MediaTimelineControllerStatus result = new MediaTimelineControllerStatus(); - - var mainDataHeader = decoder0.decodeStructDataHeader(); - if (mainDataHeader.version <= kVersions.last.version) { - // Scan in reverse order to optimize for more recent versions. - for (int i = kVersions.length - 1; i >= 0; --i) { - if (mainDataHeader.version >= kVersions[i].version) { - if (mainDataHeader.size == kVersions[i].size) { - // Found a match. - break; - } - throw new bindings.MojoCodecError( - 'Header size doesn\'t correspond to known version size.'); - } - } - } else if (mainDataHeader.size < kVersions.last.size) { - throw new bindings.MojoCodecError( - 'Message newer than the last known version cannot be shorter than ' - 'required by the last known version.'); - } - if (mainDataHeader.version >= 0) { - - var decoder1 = decoder0.decodePointer(8, false); - result.timelineTransform = timelines_mojom.TimelineTransform.decode(decoder1); - } - if (mainDataHeader.version >= 0) { - - result.endOfStream = decoder0.decodeBool(16, 0); - } - return result; - } - - void encode(bindings.Encoder encoder) { - var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last); - try { - encoder0.encodeStruct(timelineTransform, 8, false); - } on bindings.MojoCodecError catch(e) { - e.message = "Error encountered while encoding field " - "timelineTransform of struct MediaTimelineControllerStatus: $e"; - rethrow; - } - try { - encoder0.encodeBool(endOfStream, 16, 0); - } on bindings.MojoCodecError catch(e) { - e.message = "Error encountered while encoding field " - "endOfStream of struct MediaTimelineControllerStatus: $e"; - rethrow; - } - } - - String toString() { - return "MediaTimelineControllerStatus(" - "timelineTransform: $timelineTransform" ", " - "endOfStream: $endOfStream" ")"; - } - - Map toJson() { - Map map = new Map(); - map["timelineTransform"] = timelineTransform; - map["endOfStream"] = endOfStream; - return map; - } -} - - -class MediaTimelineControlSiteStatus extends bindings.Struct { - static const List<bindings.StructDataHeader> kVersions = const [ - const bindings.StructDataHeader(16, 0) - ]; - bool endOfStream = false; - bool starving = false; - MediaTimelineControlSiteStatus() : super(kVersions.last.size); static MediaTimelineControlSiteStatus deserialize(bindings.Message message) { @@ -142,11 +55,12 @@ } if (mainDataHeader.version >= 0) { - result.endOfStream = decoder0.decodeBool(8, 0); + var decoder1 = decoder0.decodePointer(8, false); + result.timelineTransform = timelines_mojom.TimelineTransform.decode(decoder1); } if (mainDataHeader.version >= 0) { - result.starving = decoder0.decodeBool(8, 1); + result.endOfStream = decoder0.decodeBool(16, 0); } return result; } @@ -154,31 +68,31 @@ void encode(bindings.Encoder encoder) { var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last); try { - encoder0.encodeBool(endOfStream, 8, 0); + encoder0.encodeStruct(timelineTransform, 8, false); } on bindings.MojoCodecError catch(e) { e.message = "Error encountered while encoding field " - "endOfStream of struct MediaTimelineControlSiteStatus: $e"; + "timelineTransform of struct MediaTimelineControlSiteStatus: $e"; rethrow; } try { - encoder0.encodeBool(starving, 8, 1); + encoder0.encodeBool(endOfStream, 16, 0); } on bindings.MojoCodecError catch(e) { e.message = "Error encountered while encoding field " - "starving of struct MediaTimelineControlSiteStatus: $e"; + "endOfStream of struct MediaTimelineControlSiteStatus: $e"; rethrow; } } String toString() { return "MediaTimelineControlSiteStatus(" - "endOfStream: $endOfStream" ", " - "starving: $starving" ")"; + "timelineTransform: $timelineTransform" ", " + "endOfStream: $endOfStream" ")"; } Map toJson() { Map map = new Map(); + map["timelineTransform"] = timelineTransform; map["endOfStream"] = endOfStream; - map["starving"] = starving; return map; } } @@ -255,365 +169,6 @@ } -class _MediaTimelineControllerGetStatusParams extends bindings.Struct { - static const List<bindings.StructDataHeader> kVersions = const [ - const bindings.StructDataHeader(16, 0) - ]; - int versionLastSeen = 0; - - _MediaTimelineControllerGetStatusParams() : super(kVersions.last.size); - - static _MediaTimelineControllerGetStatusParams deserialize(bindings.Message message) { - var decoder = new bindings.Decoder(message); - var result = decode(decoder); - if (decoder.excessHandles != null) { - decoder.excessHandles.forEach((h) => h.close()); - } - return result; - } - - static _MediaTimelineControllerGetStatusParams decode(bindings.Decoder decoder0) { - if (decoder0 == null) { - return null; - } - _MediaTimelineControllerGetStatusParams result = new _MediaTimelineControllerGetStatusParams(); - - var mainDataHeader = decoder0.decodeStructDataHeader(); - if (mainDataHeader.version <= kVersions.last.version) { - // Scan in reverse order to optimize for more recent versions. - for (int i = kVersions.length - 1; i >= 0; --i) { - if (mainDataHeader.version >= kVersions[i].version) { - if (mainDataHeader.size == kVersions[i].size) { - // Found a match. - break; - } - throw new bindings.MojoCodecError( - 'Header size doesn\'t correspond to known version size.'); - } - } - } else if (mainDataHeader.size < kVersions.last.size) { - throw new bindings.MojoCodecError( - 'Message newer than the last known version cannot be shorter than ' - 'required by the last known version.'); - } - if (mainDataHeader.version >= 0) { - - result.versionLastSeen = decoder0.decodeUint64(8); - } - return result; - } - - void encode(bindings.Encoder encoder) { - var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last); - try { - encoder0.encodeUint64(versionLastSeen, 8); - } on bindings.MojoCodecError catch(e) { - e.message = "Error encountered while encoding field " - "versionLastSeen of struct _MediaTimelineControllerGetStatusParams: $e"; - rethrow; - } - } - - String toString() { - return "_MediaTimelineControllerGetStatusParams(" - "versionLastSeen: $versionLastSeen" ")"; - } - - Map toJson() { - Map map = new Map(); - map["versionLastSeen"] = versionLastSeen; - return map; - } -} - - -class MediaTimelineControllerGetStatusResponseParams extends bindings.Struct { - static const List<bindings.StructDataHeader> kVersions = const [ - const bindings.StructDataHeader(24, 0) - ]; - int version = 0; - MediaTimelineControllerStatus status = null; - - MediaTimelineControllerGetStatusResponseParams() : super(kVersions.last.size); - - static MediaTimelineControllerGetStatusResponseParams deserialize(bindings.Message message) { - var decoder = new bindings.Decoder(message); - var result = decode(decoder); - if (decoder.excessHandles != null) { - decoder.excessHandles.forEach((h) => h.close()); - } - return result; - } - - static MediaTimelineControllerGetStatusResponseParams decode(bindings.Decoder decoder0) { - if (decoder0 == null) { - return null; - } - MediaTimelineControllerGetStatusResponseParams result = new MediaTimelineControllerGetStatusResponseParams(); - - var mainDataHeader = decoder0.decodeStructDataHeader(); - if (mainDataHeader.version <= kVersions.last.version) { - // Scan in reverse order to optimize for more recent versions. - for (int i = kVersions.length - 1; i >= 0; --i) { - if (mainDataHeader.version >= kVersions[i].version) { - if (mainDataHeader.size == kVersions[i].size) { - // Found a match. - break; - } - throw new bindings.MojoCodecError( - 'Header size doesn\'t correspond to known version size.'); - } - } - } else if (mainDataHeader.size < kVersions.last.size) { - throw new bindings.MojoCodecError( - 'Message newer than the last known version cannot be shorter than ' - 'required by the last known version.'); - } - if (mainDataHeader.version >= 0) { - - result.version = decoder0.decodeUint64(8); - } - if (mainDataHeader.version >= 0) { - - var decoder1 = decoder0.decodePointer(16, false); - result.status = MediaTimelineControllerStatus.decode(decoder1); - } - return result; - } - - void encode(bindings.Encoder encoder) { - var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last); - try { - encoder0.encodeUint64(version, 8); - } on bindings.MojoCodecError catch(e) { - e.message = "Error encountered while encoding field " - "version of struct MediaTimelineControllerGetStatusResponseParams: $e"; - rethrow; - } - try { - encoder0.encodeStruct(status, 16, false); - } on bindings.MojoCodecError catch(e) { - e.message = "Error encountered while encoding field " - "status of struct MediaTimelineControllerGetStatusResponseParams: $e"; - rethrow; - } - } - - String toString() { - return "MediaTimelineControllerGetStatusResponseParams(" - "version: $version" ", " - "status: $status" ")"; - } - - Map toJson() { - Map map = new Map(); - map["version"] = version; - map["status"] = status; - return map; - } -} - - -class _MediaTimelineControllerSetTimelineTransformParams extends bindings.Struct { - static const List<bindings.StructDataHeader> kVersions = const [ - const bindings.StructDataHeader(40, 0) - ]; - int subjectTime = 0; - int subjectDelta = 0; - int referenceDelta = 0; - int effectiveSubjectTime = 0; - int effectiveReferenceTime = 0; - - _MediaTimelineControllerSetTimelineTransformParams() : super(kVersions.last.size); - - static _MediaTimelineControllerSetTimelineTransformParams deserialize(bindings.Message message) { - var decoder = new bindings.Decoder(message); - var result = decode(decoder); - if (decoder.excessHandles != null) { - decoder.excessHandles.forEach((h) => h.close()); - } - return result; - } - - static _MediaTimelineControllerSetTimelineTransformParams decode(bindings.Decoder decoder0) { - if (decoder0 == null) { - return null; - } - _MediaTimelineControllerSetTimelineTransformParams result = new _MediaTimelineControllerSetTimelineTransformParams(); - - var mainDataHeader = decoder0.decodeStructDataHeader(); - if (mainDataHeader.version <= kVersions.last.version) { - // Scan in reverse order to optimize for more recent versions. - for (int i = kVersions.length - 1; i >= 0; --i) { - if (mainDataHeader.version >= kVersions[i].version) { - if (mainDataHeader.size == kVersions[i].size) { - // Found a match. - break; - } - throw new bindings.MojoCodecError( - 'Header size doesn\'t correspond to known version size.'); - } - } - } else if (mainDataHeader.size < kVersions.last.size) { - throw new bindings.MojoCodecError( - 'Message newer than the last known version cannot be shorter than ' - 'required by the last known version.'); - } - if (mainDataHeader.version >= 0) { - - result.subjectTime = decoder0.decodeInt64(8); - } - if (mainDataHeader.version >= 0) { - - result.subjectDelta = decoder0.decodeUint32(16); - } - if (mainDataHeader.version >= 0) { - - result.referenceDelta = decoder0.decodeUint32(20); - } - if (mainDataHeader.version >= 0) { - - result.effectiveSubjectTime = decoder0.decodeInt64(24); - } - if (mainDataHeader.version >= 0) { - - result.effectiveReferenceTime = decoder0.decodeInt64(32); - } - return result; - } - - void encode(bindings.Encoder encoder) { - var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last); - try { - encoder0.encodeInt64(subjectTime, 8); - } on bindings.MojoCodecError catch(e) { - e.message = "Error encountered while encoding field " - "subjectTime of struct _MediaTimelineControllerSetTimelineTransformParams: $e"; - rethrow; - } - try { - encoder0.encodeUint32(subjectDelta, 16); - } on bindings.MojoCodecError catch(e) { - e.message = "Error encountered while encoding field " - "subjectDelta of struct _MediaTimelineControllerSetTimelineTransformParams: $e"; - rethrow; - } - try { - encoder0.encodeUint32(referenceDelta, 20); - } on bindings.MojoCodecError catch(e) { - e.message = "Error encountered while encoding field " - "referenceDelta of struct _MediaTimelineControllerSetTimelineTransformParams: $e"; - rethrow; - } - try { - encoder0.encodeInt64(effectiveSubjectTime, 24); - } on bindings.MojoCodecError catch(e) { - e.message = "Error encountered while encoding field " - "effectiveSubjectTime of struct _MediaTimelineControllerSetTimelineTransformParams: $e"; - rethrow; - } - try { - encoder0.encodeInt64(effectiveReferenceTime, 32); - } on bindings.MojoCodecError catch(e) { - e.message = "Error encountered while encoding field " - "effectiveReferenceTime of struct _MediaTimelineControllerSetTimelineTransformParams: $e"; - rethrow; - } - } - - String toString() { - return "_MediaTimelineControllerSetTimelineTransformParams(" - "subjectTime: $subjectTime" ", " - "subjectDelta: $subjectDelta" ", " - "referenceDelta: $referenceDelta" ", " - "effectiveSubjectTime: $effectiveSubjectTime" ", " - "effectiveReferenceTime: $effectiveReferenceTime" ")"; - } - - Map toJson() { - Map map = new Map(); - map["subjectTime"] = subjectTime; - map["subjectDelta"] = subjectDelta; - map["referenceDelta"] = referenceDelta; - map["effectiveSubjectTime"] = effectiveSubjectTime; - map["effectiveReferenceTime"] = effectiveReferenceTime; - return map; - } -} - - -class MediaTimelineControllerSetTimelineTransformResponseParams extends bindings.Struct { - static const List<bindings.StructDataHeader> kVersions = const [ - const bindings.StructDataHeader(16, 0) - ]; - bool completed = false; - - MediaTimelineControllerSetTimelineTransformResponseParams() : super(kVersions.last.size); - - static MediaTimelineControllerSetTimelineTransformResponseParams deserialize(bindings.Message message) { - var decoder = new bindings.Decoder(message); - var result = decode(decoder); - if (decoder.excessHandles != null) { - decoder.excessHandles.forEach((h) => h.close()); - } - return result; - } - - static MediaTimelineControllerSetTimelineTransformResponseParams decode(bindings.Decoder decoder0) { - if (decoder0 == null) { - return null; - } - MediaTimelineControllerSetTimelineTransformResponseParams result = new MediaTimelineControllerSetTimelineTransformResponseParams(); - - var mainDataHeader = decoder0.decodeStructDataHeader(); - if (mainDataHeader.version <= kVersions.last.version) { - // Scan in reverse order to optimize for more recent versions. - for (int i = kVersions.length - 1; i >= 0; --i) { - if (mainDataHeader.version >= kVersions[i].version) { - if (mainDataHeader.size == kVersions[i].size) { - // Found a match. - break; - } - throw new bindings.MojoCodecError( - 'Header size doesn\'t correspond to known version size.'); - } - } - } else if (mainDataHeader.size < kVersions.last.size) { - throw new bindings.MojoCodecError( - 'Message newer than the last known version cannot be shorter than ' - 'required by the last known version.'); - } - if (mainDataHeader.version >= 0) { - - result.completed = decoder0.decodeBool(8, 0); - } - return result; - } - - void encode(bindings.Encoder encoder) { - var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last); - try { - encoder0.encodeBool(completed, 8, 0); - } on bindings.MojoCodecError catch(e) { - e.message = "Error encountered while encoding field " - "completed of struct MediaTimelineControllerSetTimelineTransformResponseParams: $e"; - rethrow; - } - } - - String toString() { - return "MediaTimelineControllerSetTimelineTransformResponseParams(" - "completed: $completed" ")"; - } - - Map toJson() { - Map map = new Map(); - map["completed"] = completed; - return map; - } -} - - class _MediaTimelineControllerGetControlSiteParams extends bindings.Struct { static const List<bindings.StructDataHeader> kVersions = const [ const bindings.StructDataHeader(16, 0) @@ -915,9 +470,7 @@ } const int _mediaTimelineControllerMethodAddControlSiteName = 0; -const int _mediaTimelineControllerMethodGetStatusName = 1; -const int _mediaTimelineControllerMethodSetTimelineTransformName = 2; -const int _mediaTimelineControllerMethodGetControlSiteName = 3; +const int _mediaTimelineControllerMethodGetControlSiteName = 1; class _MediaTimelineControllerServiceDescription implements service_describer.ServiceDescription { dynamic getTopLevelInterface([Function responseFactory]) => @@ -932,50 +485,8 @@ abstract class MediaTimelineController { static const String serviceName = null; - - static service_describer.ServiceDescription _cachedServiceDescription; - static service_describer.ServiceDescription get serviceDescription { - if (_cachedServiceDescription == null) { - _cachedServiceDescription = new _MediaTimelineControllerServiceDescription(); - } - return _cachedServiceDescription; - } - - static MediaTimelineControllerProxy connectToService( - bindings.ServiceConnector s, String url, [String serviceName]) { - MediaTimelineControllerProxy p = new MediaTimelineControllerProxy.unbound(); - String name = serviceName ?? MediaTimelineController.serviceName; - if ((name == null) || name.isEmpty) { - throw new core.MojoApiError( - "If an interface has no ServiceName, then one must be provided."); - } - s.connectToService(url, p, name); - return p; - } - void addControlSite(MediaTimelineControlSiteInterface controlSite); - dynamic getStatus(int versionLastSeen,[Function responseFactory = null]); - dynamic setTimelineTransform(int subjectTime,int subjectDelta,int referenceDelta,int effectiveSubjectTime,int effectiveReferenceTime,[Function responseFactory = null]); - void getControlSite(MediaTimelineControlSiteInterfaceRequest controlSite); - static const int kUnspecifiedTime = 9223372036854775807; - static const int kInitialStatus = 0; -} - -abstract class MediaTimelineControllerInterface - implements bindings.MojoInterface<MediaTimelineController>, - MediaTimelineController { - factory MediaTimelineControllerInterface([MediaTimelineController impl]) => - new MediaTimelineControllerStub.unbound(impl); - factory MediaTimelineControllerInterface.fromEndpoint( - core.MojoMessagePipeEndpoint endpoint, - [MediaTimelineController impl]) => - new MediaTimelineControllerStub.fromEndpoint(endpoint, impl); -} - -abstract class MediaTimelineControllerInterfaceRequest - implements bindings.MojoInterface<MediaTimelineController>, - MediaTimelineController { - factory MediaTimelineControllerInterfaceRequest() => - new MediaTimelineControllerProxy.unbound(); + void addControlSite(Object controlSite); + void getControlSite(Object controlSite); } class _MediaTimelineControllerProxyControl @@ -993,46 +504,6 @@ void handleResponse(bindings.ServiceMessage message) { switch (message.header.type) { - case _mediaTimelineControllerMethodGetStatusName: - var r = MediaTimelineControllerGetStatusResponseParams.deserialize( - message.payload); - if (!message.header.hasRequestId) { - proxyError("Expected a message with a valid request Id."); - return; - } - Completer c = completerMap[message.header.requestId]; - if (c == null) { - proxyError( - "Message had unknown request Id: ${message.header.requestId}"); - return; - } - completerMap.remove(message.header.requestId); - if (c.isCompleted) { - proxyError("Response completer already completed"); - return; - } - c.complete(r); - break; - case _mediaTimelineControllerMethodSetTimelineTransformName: - var r = MediaTimelineControllerSetTimelineTransformResponseParams.deserialize( - message.payload); - if (!message.header.hasRequestId) { - proxyError("Expected a message with a valid request Id."); - return; - } - Completer c = completerMap[message.header.requestId]; - if (c == null) { - proxyError( - "Message had unknown request Id: ${message.header.requestId}"); - return; - } - completerMap.remove(message.header.requestId); - if (c.isCompleted) { - proxyError("Response completer already completed"); - return; - } - c.complete(r); - break; default: proxyError("Unexpected message type: ${message.header.type}"); close(immediate: true); @@ -1084,29 +555,7 @@ ctrl.sendMessage(params, _mediaTimelineControllerMethodAddControlSiteName); } - dynamic getStatus(int versionLastSeen,[Function responseFactory = null]) { - var params = new _MediaTimelineControllerGetStatusParams(); - params.versionLastSeen = versionLastSeen; - return ctrl.sendMessageWithRequestId( - params, - _mediaTimelineControllerMethodGetStatusName, - -1, - bindings.MessageHeader.kMessageExpectsResponse); - } - dynamic setTimelineTransform(int subjectTime,int subjectDelta,int referenceDelta,int effectiveSubjectTime,int effectiveReferenceTime,[Function responseFactory = null]) { - var params = new _MediaTimelineControllerSetTimelineTransformParams(); - params.subjectTime = subjectTime; - params.subjectDelta = subjectDelta; - params.referenceDelta = referenceDelta; - params.effectiveSubjectTime = effectiveSubjectTime; - params.effectiveReferenceTime = effectiveReferenceTime; - return ctrl.sendMessageWithRequestId( - params, - _mediaTimelineControllerMethodSetTimelineTransformName, - -1, - bindings.MessageHeader.kMessageExpectsResponse); - } - void getControlSite(MediaTimelineControlSiteInterfaceRequest controlSite) { + void getControlSite(Object controlSite) { if (!ctrl.isBound) { ctrl.proxyError("The Proxy is closed."); return; @@ -1140,17 +589,6 @@ String get serviceName => MediaTimelineController.serviceName; - MediaTimelineControllerGetStatusResponseParams _mediaTimelineControllerGetStatusResponseParamsFactory(int version, MediaTimelineControllerStatus status) { - var result = new MediaTimelineControllerGetStatusResponseParams(); - result.version = version; - result.status = status; - return result; - } - MediaTimelineControllerSetTimelineTransformResponseParams _mediaTimelineControllerSetTimelineTransformResponseParamsFactory(bool completed) { - var result = new MediaTimelineControllerSetTimelineTransformResponseParams(); - result.completed = completed; - return result; - } dynamic handleMessage(bindings.ServiceMessage message) { if (bindings.ControlMessageHandler.isControlMessage(message)) { @@ -1167,50 +605,6 @@ message.payload); _impl.addControlSite(params.controlSite); break; - case _mediaTimelineControllerMethodGetStatusName: - var params = _MediaTimelineControllerGetStatusParams.deserialize( - message.payload); - var response = _impl.getStatus(params.versionLastSeen,_mediaTimelineControllerGetStatusResponseParamsFactory); - if (response is Future) { - return response.then((response) { - if (response != null) { - return buildResponseWithId( - response, - _mediaTimelineControllerMethodGetStatusName, - message.header.requestId, - bindings.MessageHeader.kMessageIsResponse); - } - }); - } else if (response != null) { - return buildResponseWithId( - response, - _mediaTimelineControllerMethodGetStatusName, - message.header.requestId, - bindings.MessageHeader.kMessageIsResponse); - } - break; - case _mediaTimelineControllerMethodSetTimelineTransformName: - var params = _MediaTimelineControllerSetTimelineTransformParams.deserialize( - message.payload); - var response = _impl.setTimelineTransform(params.subjectTime,params.subjectDelta,params.referenceDelta,params.effectiveSubjectTime,params.effectiveReferenceTime,_mediaTimelineControllerSetTimelineTransformResponseParamsFactory); - if (response is Future) { - return response.then((response) { - if (response != null) { - return buildResponseWithId( - response, - _mediaTimelineControllerMethodSetTimelineTransformName, - message.header.requestId, - bindings.MessageHeader.kMessageIsResponse); - } - }); - } else if (response != null) { - return buildResponseWithId( - response, - _mediaTimelineControllerMethodSetTimelineTransformName, - message.header.requestId, - bindings.MessageHeader.kMessageIsResponse); - } - break; case _mediaTimelineControllerMethodGetControlSiteName: var params = _MediaTimelineControllerGetControlSiteParams.deserialize( message.payload); @@ -1277,13 +671,7 @@ void addControlSite(MediaTimelineControlSiteInterface controlSite) { return impl.addControlSite(controlSite); } - dynamic getStatus(int versionLastSeen,[Function responseFactory = null]) { - return impl.getStatus(versionLastSeen,responseFactory); - } - dynamic setTimelineTransform(int subjectTime,int subjectDelta,int referenceDelta,int effectiveSubjectTime,int effectiveReferenceTime,[Function responseFactory = null]) { - return impl.setTimelineTransform(subjectTime,subjectDelta,referenceDelta,effectiveSubjectTime,effectiveReferenceTime,responseFactory); - } - void getControlSite(MediaTimelineControlSiteInterfaceRequest controlSite) { + void getControlSite(Object controlSite) { return impl.getControlSite(controlSite); } }
diff --git a/mojo/dart/packages/mojo_services/lib/mojo/timelines.mojom.dart b/mojo/dart/packages/mojo_services/lib/mojo/timelines.mojom.dart index 639bb64..6381061 100644 --- a/mojo/dart/packages/mojo_services/lib/mojo/timelines.mojom.dart +++ b/mojo/dart/packages/mojo_services/lib/mojo/timelines.mojom.dart
@@ -7,6 +7,7 @@ import 'package:mojo/bindings.dart' as bindings; import 'package:mojo/core.dart' as core; import 'package:mojo/mojo/bindings/types/service_describer.mojom.dart' as service_describer; +const int kUnspecifiedTime = 9223372036854775807; @@ -359,7 +360,6 @@ return p; } dynamic setTimelineTransform(int subjectTime,int referenceDelta,int subjectDelta,int effectiveReferenceTime,int effectiveSubjectTime,[Function responseFactory = null]); - static const int kUnspecifiedTime = 9223372036854775807; } abstract class TimelineConsumerInterface
diff --git a/mojo/services/media/audio/interfaces/audio_track.mojom b/mojo/services/media/audio/interfaces/audio_track.mojom index b9f251e..b5cf803 100644 --- a/mojo/services/media/audio/interfaces/audio_track.mojom +++ b/mojo/services/media/audio/interfaces/audio_track.mojom
@@ -8,7 +8,7 @@ import "mojo/services/media/common/interfaces/media_common.mojom"; import "mojo/services/media/common/interfaces/media_transport.mojom"; import "mojo/services/media/common/interfaces/media_types.mojom"; -import "mojo/services/media/common/interfaces/rate_control.mojom"; +import "mojo/services/media/core/interfaces/timeline_controller.mojom"; struct AudioTrackDescriptor { // The track supports the union of all these media type sets. @@ -50,8 +50,8 @@ // Set the configuration, receive a pipe to send data to in return. Configure(AudioTrackConfiguration configuration, MediaConsumer& pipe); - // Request the rate control interface for this AudioTrack - GetRateControl(RateControl& rate_control); + // Request the timeline control site for this AudioTrack + GetTimelineControlSite(MediaTimelineControlSite& timeline_control_site); // Sets the current gain/attenuation of the track, expressed in dB. Legal // values are in the range [-inf, 20.0]. Any value less than or equal to the
diff --git a/mojo/services/media/common/cpp/BUILD.gn b/mojo/services/media/common/cpp/BUILD.gn index c2f6b70..42272a0 100644 --- a/mojo/services/media/common/cpp/BUILD.gn +++ b/mojo/services/media/common/cpp/BUILD.gn
@@ -19,6 +19,7 @@ "mapped_shared_buffer.h", "shared_media_buffer_allocator.cc", "shared_media_buffer_allocator.h", + "timeline.h", "timeline_function.cc", "timeline_function.h", "timeline_rate.cc",
diff --git a/mojo/services/media/common/cpp/timeline.h b/mojo/services/media/common/cpp/timeline.h new file mode 100644 index 0000000..4fc3651 --- /dev/null +++ b/mojo/services/media/common/cpp/timeline.h
@@ -0,0 +1,53 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_SERVICES_MEDIA_COMMON_CPP_TIMELINE_H_ +#define MOJO_SERVICES_MEDIA_COMMON_CPP_TIMELINE_H_ + +#include <stdint.h> +#include <chrono> // NOLINT(build/c++11) + +// TODO(johngro): As we add support for other environments, extend this list. +#if defined(OS_POSIX) +#include "mojo/services/media/common/cpp/platform/posix/local_time.h" +#else +// TODO(johngro): consider adding a #warning or #info to inform the user that +// they are using the generic implementation of LocalTime, and really should get +// around to implementing proper platform support ASAP. +#include "mojo/services/media/common/cpp/platform/generic/local_time.h" +#endif + +namespace mojo { +namespace media { + +// Some helpful constants and static methods relating to timelines. +class Timeline { + public: + // Returns the current local time in nanoseconds since epoch. + static int64_t local_now() { + return local_time::Clock::now().time_since_epoch().count(); + } + + template <typename T> + static constexpr int64_t ns_from_seconds(T seconds) { + return static_cast<int64_t>(seconds * std::nano::den); + } + + template <typename T> + static constexpr int64_t ns_from_ms(T milliseconds) { + return static_cast<int64_t>(milliseconds * + (std::nano::den / std::milli::den)); + } + + template <typename T> + static constexpr int64_t ns_from_us(T microseconds) { + return static_cast<int64_t>(microseconds * + (std::nano::den / std::micro::den)); + } +}; + +} // namespace media +} // namespace mojo + +#endif // MOJO_SERVICES_MEDIA_COMMON_CPP_TIMELINE_H_
diff --git a/mojo/services/media/common/cpp/timeline_function.cc b/mojo/services/media/common/cpp/timeline_function.cc index 350fde6..f2b1ec1 100644 --- a/mojo/services/media/common/cpp/timeline_function.cc +++ b/mojo/services/media/common/cpp/timeline_function.cc
@@ -29,4 +29,25 @@ } } // namespace media + +TimelineTransformPtr +TypeConverter<TimelineTransformPtr, media::TimelineFunction>::Convert( + const media::TimelineFunction& input) { + TimelineTransformPtr result = TimelineTransform::New(); + result->reference_time = input.reference_time(); + result->subject_time = input.subject_time(); + result->reference_delta = input.reference_delta(); + result->subject_delta = input.subject_delta(); + return result; +} + +media::TimelineFunction +TypeConverter<media::TimelineFunction, TimelineTransformPtr>::Convert( + const TimelineTransformPtr& input) { + return input ? media::TimelineFunction( + input->reference_time, input->subject_time, + input->reference_delta, input->subject_delta) + : media::TimelineFunction(); +} + } // namespace mojo
diff --git a/mojo/services/media/common/cpp/timeline_function.h b/mojo/services/media/common/cpp/timeline_function.h index 67ac80a..075ee01 100644 --- a/mojo/services/media/common/cpp/timeline_function.h +++ b/mojo/services/media/common/cpp/timeline_function.h
@@ -5,8 +5,10 @@ #ifndef MOJO_SERVICES_MEDIA_COMMON_CPP_TIMELINE_FUNCTION_H_ #define MOJO_SERVICES_MEDIA_COMMON_CPP_TIMELINE_FUNCTION_H_ +#include "mojo/public/cpp/bindings/type_converter.h" #include "mojo/public/cpp/environment/logging.h" #include "mojo/services/media/common/cpp/timeline_rate.h" +#include "mojo/services/media/common/interfaces/timelines.mojom.h" namespace mojo { namespace media { @@ -128,6 +130,19 @@ } } // namespace media + +template <> +struct TypeConverter<TimelineTransformPtr, media::TimelineFunction> { + static TimelineTransformPtr Convert( + const media::TimelineFunction& input); +}; + +template <> +struct TypeConverter<media::TimelineFunction, TimelineTransformPtr> { + static media::TimelineFunction Convert( + const TimelineTransformPtr& input); +}; + } // namespace mojo #endif // MOJO_SERVICES_MEDIA_COMMON_CPP_TIMELINE_FUNCTION_H_
diff --git a/mojo/services/media/common/cpp/timeline_rate.h b/mojo/services/media/common/cpp/timeline_rate.h index 7aa0216..4718f1f 100644 --- a/mojo/services/media/common/cpp/timeline_rate.h +++ b/mojo/services/media/common/cpp/timeline_rate.h
@@ -62,6 +62,25 @@ explicit TimelineRate(uint32_t subject_delta) : subject_delta_(subject_delta), reference_delta_(1) {} + explicit TimelineRate(float rate_as_float) + : subject_delta_( + rate_as_float > 1.0f + ? kFloatFactor + : static_cast<uint32_t>(kFloatFactor * rate_as_float)), + reference_delta_( + rate_as_float > 1.0f + ? static_cast<uint32_t>(kFloatFactor / rate_as_float) + : kFloatFactor) { + // The expressions above are intended to provide good precision for + // 'reasonable' playback rate values (say in the range 0.0 to 4.0). The + // expressions always produce a ratio of kFloatFactor and a number smaller + // than kFloatFactor. kFloatFactor's value was chosen because floats have + // a 23-bit mantissa, and operations with a larger factor would sacrifice + // precision. + MOJO_DCHECK(rate_as_float >= 0.0f); + Reduce(&subject_delta_, &reference_delta_); + } + TimelineRate(uint32_t subject_delta, uint32_t reference_delta) : subject_delta_(subject_delta), reference_delta_(reference_delta) { MOJO_DCHECK(reference_delta != 0); @@ -84,6 +103,10 @@ uint32_t reference_delta() const { return reference_delta_; } private: + // A multiplier for float-to-TimelineRate conversions chosen because floats + // have a 23-bit mantissa. + static constexpr uint32_t kFloatFactor = 1ul << 23; + uint32_t subject_delta_; uint32_t reference_delta_; };
diff --git a/mojo/services/media/common/interfaces/BUILD.gn b/mojo/services/media/common/interfaces/BUILD.gn index 109f979..ffdb131 100644 --- a/mojo/services/media/common/interfaces/BUILD.gn +++ b/mojo/services/media/common/interfaces/BUILD.gn
@@ -12,7 +12,6 @@ "media_state.mojom", "media_transport.mojom", "media_types.mojom", - "rate_control.mojom", "timelines.mojom", ] }
diff --git a/mojo/services/media/common/interfaces/rate_control.mojom b/mojo/services/media/common/interfaces/rate_control.mojom deleted file mode 100644 index bd7dd92..0000000 --- a/mojo/services/media/common/interfaces/rate_control.mojom +++ /dev/null
@@ -1,102 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -[DartPackage="mojo_services"] -module mojo.media; - -// TimelineQuad -// TODO(dalesat): Rename reference -> presentation. -// TODO(dalesat): Rename target -> reference. -// -// A structure which holds the four numbers needed to define a linear -// relationship between the points in two different timelines. The relationship -// is expressed using 4 integers in order to facilitate compositions of multiple -// transformations (A->B can be composed with B->C to produce the transformation -// from A->C), and to minimize rounding and scaling errors when mapping points -// between timelines which do not have an integer scaling relationship between -// each other. -// -// These values are used to define the following functions which map from -// points from the reference timeline to the target timeline, and back again. -// -// Let r be a point in the reference timeline. -// Let t be a point in the target timeline timeline. -// Let ref and tgt be abbreviations for reference and target in equations below. -// -// Given that r and t represent the same instant in time (in a single frame of -// reference) -// -// t = f(r) = (((r - ref_offset) * tgt_delta) / ref_delta) + tgt_offset -// r = F(t) = (((t - tgt_offset) * ref_delta) / tgt_delta) + ref_offset -// -// See also... -// mojo/services/media/common/linear_transform.h -// -// no-format -struct TimelineQuad { - int64 reference_offset = 0; - int64 target_offset = 0; - uint32 reference_delta = 0; - uint32 target_delta = 1; -}; -// end-no-format - -// TimelineTransform -// TODO(dalesat): Rename reference -> presentation. -// TODO(dalesat): Rename target -> reference. -// -// A structure which holds both a timeline quad, and a pair of identifiers which -// define the specific timelines which are the reference and target timelines. -struct TimelineTransform { - // TODO: These constants should probably defined by a central time management - // service, not here. - const uint32 kLocalTimeID = 0xFFFFFFFF; - const uint32 kContextual = 0xFFFFFFFE; - - TimelineQuad quad; - uint32 reference_timeline_id = kContextual; - uint32 target_timeline_id = kLocalTimeID; -}; - -// RateControl -// -// An interface typically exposed by media renderers which allow producers of -// media to specify how the presentation time stamps of the media queued to the -// renderer relate to real time. Users may initialize the transformation with a -// specific Quad, change the rate immediately in a first order contiguous -// fashion, or schedule ranges in the rate at points in time on either the -// reference or target timelines. -interface RateControl { - // Get the current quad which describes the transformation between the - // reference and target timelines. - GetCurrentTransform() => (TimelineTransform trans); - - // Immediately, explicitly set the quad which describes the mapping from - // reference to target timeline. It is understood that this can cause - // discontinuities and should only be used in situations which are already - // fundamentally discontinuous (startup/seeking, for example) - SetCurrentQuad(TimelineQuad quad); - - // Configure the target timeline ID. Note, the reference timeline ID will - // always be contextual. - SetTargetTimelineID(uint32 id); - - // Immediately change the rate of the existing transformation in a fashion - // which is first order continuous with the current transformation. - SetRate(uint32 reference_delta, uint32 target_delta); - - // Schedule a first order continuous rate change at the specified reference - // time. - SetRateAtReferenceTime(uint32 reference_delta, - uint32 target_delta, - int64 reference_time); - - // Schedule a first order continuous rate change at the specified target time. - SetRateAtTargetTime(uint32 reference_delta, - uint32 target_delta, - int64 target_time); - - // Cancel any pending rate changes - CancelPendingChanges(); -};
diff --git a/mojo/services/media/common/interfaces/timelines.mojom b/mojo/services/media/common/interfaces/timelines.mojom index edee74d..39e4aef 100644 --- a/mojo/services/media/common/interfaces/timelines.mojom +++ b/mojo/services/media/common/interfaces/timelines.mojom
@@ -7,6 +7,9 @@ // TODO(dalesat): Move out of media to somewhere more generic. +// Used as a placefolder for unspecified time values. +const int64 kUnspecifiedTime = 0x7fffffffffffffff; + // Represents the relationship between and subject timeline and a reference // timeline. // @@ -37,8 +40,6 @@ // A push-mode consumer of timeline updates. interface TimelineConsumer { - const int64 kUnspecifiedTime = 0x7fffffffffffffff; - // Sets the timeline transform at the indicated effective time. Exactly one // of the effective_*_time values must be kUnspecifiedTime. // effective_subject_time can only be specified if the current subject_delta
diff --git a/mojo/services/media/control/interfaces/media_player.mojom b/mojo/services/media/control/interfaces/media_player.mojom index b612c17..49f4aa4 100644 --- a/mojo/services/media/control/interfaces/media_player.mojom +++ b/mojo/services/media/control/interfaces/media_player.mojom
@@ -7,7 +7,7 @@ import "mojo/services/media/common/interfaces/media_metadata.mojom"; import "mojo/services/media/common/interfaces/media_state.mojom"; -import "mojo/services/media/common/interfaces/rate_control.mojom"; +import "mojo/services/media/common/interfaces/timelines.mojom"; // Plays media. interface MediaPlayer { @@ -40,7 +40,7 @@ // Transform translating local time to presentation time. Reverse translation // (presentation time to local time) is only valid when media is playing. - TimelineTransform? timeline_transform; + mojo.TimelineTransform? timeline_transform; // Describes the media. MediaMetadata? metadata;
diff --git a/mojo/services/media/control/interfaces/media_sink.mojom b/mojo/services/media/control/interfaces/media_sink.mojom index 45268cc..ac6c311 100644 --- a/mojo/services/media/control/interfaces/media_sink.mojom +++ b/mojo/services/media/control/interfaces/media_sink.mojom
@@ -9,7 +9,7 @@ import "mojo/services/media/common/interfaces/media_state.mojom"; import "mojo/services/media/common/interfaces/media_transport.mojom"; import "mojo/services/media/common/interfaces/media_types.mojom"; -import "mojo/services/media/common/interfaces/rate_control.mojom"; +import "mojo/services/media/common/interfaces/timelines.mojom"; // TODO(dalesat): Define a media sink that multiplexes streams. @@ -44,5 +44,5 @@ // Transform translating local time to presentation time. Reverse translation // (presentation time to local time) is only valid when media is playing. - TimelineTransform? timeline_transform; + mojo.TimelineTransform? timeline_transform; };
diff --git a/mojo/services/media/core/interfaces/timeline_controller.mojom b/mojo/services/media/core/interfaces/timeline_controller.mojom index 895b5d5..b18bea0 100644 --- a/mojo/services/media/core/interfaces/timeline_controller.mojom +++ b/mojo/services/media/core/interfaces/timeline_controller.mojom
@@ -9,50 +9,13 @@ // Timing controller for a media graph. interface MediaTimelineController { - const int64 kUnspecifiedTime = 0x7fffffffffffffff; - const uint64 kInitialStatus = 0; - // Associates a control site with the controller. AddControlSite(MediaTimelineControlSite control_site); - // Gets the status. To get the status immediately, call - // GetStatus(kInitialStatus). To get updates thereafter, pass - // the version sent in the previous callback. - GetStatus(uint64 version_last_seen) - => (uint64 version, MediaTimelineControllerStatus status); - - // Sets the timeline transform at the indicated effective time. At least one - // of the effective_*_time values must be kUnspecifiedTime. If both are - // kUnspecifiedTime, the requested change is implemented as soon as possible. - // effective_subject_time can only be specified if the current subject_delta - // isn’t zero. reference_delta may not be zero. subject_time may be - // kUnspecifiedTime to indicate that the new transform subject_time should - // be inferred from the effective time. The reference time for the new - // transform (the reference time that will correspond to the specified or - // inferred subject_time) is always inferred from the effective time. The - // callback is called at the effective time or when a pending operation is - // cancelled due to a subsequent call, in which case the 'completed' value is - // false. - SetTimelineTransform( - int64 subject_time, - uint32 subject_delta, - uint32 reference_delta, - int64 effective_subject_time, - int64 effective_reference_time) => (bool completed); - // Gets a timeline control site interface for the controller. GetControlSite(MediaTimelineControlSite& control_site); }; -// Status returned by MediaTimelineController's GetStatus method. -struct MediaTimelineControllerStatus { - // Current timeline transform. - mojo.TimelineTransform timeline_transform; - - // Whether end of stream was encountered. - bool end_of_stream; -}; - // Media graph component controlled by a MediaTimelineController. interface MediaTimelineControlSite { const uint64 kInitialStatus = 0; @@ -69,9 +32,9 @@ // Status returned by MediaTimelineControlSite's GetStatus method. struct MediaTimelineControlSiteStatus { - // Whether end of stream was encountered. - bool end_of_stream; + // Current timeline transform. + mojo.TimelineTransform timeline_transform; - // Whether the site is starving. - bool starving; + // Indicates whether presentation has reached end-of-stream. + bool end_of_stream; };
diff --git a/services/media/audio/audio_track_impl.cc b/services/media/audio/audio_track_impl.cc index 7b9a8e0..87f3418 100644 --- a/services/media/audio/audio_track_impl.cc +++ b/services/media/audio/audio_track_impl.cc
@@ -7,6 +7,7 @@ #include "base/logging.h" #include "mojo/services/media/common/cpp/linear_transform.h" +#include "mojo/services/media/common/cpp/timeline.h" #include "services/media/audio/audio_output_manager.h" #include "services/media/audio/audio_server_impl.h" #include "services/media/audio/audio_track_impl.h" @@ -72,7 +73,7 @@ // for the service to destroy us. Run some DCHECK sanity checks and get out. if (!binding_.is_bound()) { DCHECK(!pipe_.IsInitialized()); - DCHECK(!rate_control_.is_bound()); + DCHECK(!timeline_control_site_.is_bound()); DCHECK(!outputs_.size()); return; } @@ -84,7 +85,7 @@ // reset all of our internal state and close any other client connections in // the process. pipe_.Reset(); - rate_control_.Reset(); + timeline_control_site_.Reset(); outputs_.clear(); DCHECK(owner_); @@ -193,6 +194,8 @@ return; } + frames_per_ns_ = + TimelineRate(cfg->frames_per_second, Timeline::ns_from_seconds(1)); // Figure out the rate we need to scale by in order to produce our fixed // point timestamps. @@ -262,10 +265,9 @@ owner_->GetOutputManager().SelectOutputsForTrack(strong_this); } -void AudioTrackImpl::GetRateControl(InterfaceRequest<RateControl> req) { - if (!rate_control_.Bind(req.Pass())) { - Shutdown(); - } +void AudioTrackImpl::GetTimelineControlSite( + InterfaceRequest<MediaTimelineControlSite> req) { + timeline_control_site_.Bind(req.Pass()); } void AudioTrackImpl::SetGain(float db_gain) { @@ -306,6 +308,23 @@ } } +void AudioTrackImpl::SnapshotRateTrans(LinearTransform* out, + uint32_t* generation) { + TimelineFunction timeline_function; + timeline_control_site_.SnapshotCurrentFunction( + Timeline::local_now(), &timeline_function, generation); + + // The control site works in ns units. We want the rate in frames per + // nanosecond, so we convert here. + TimelineRate rate_in_frames_per_ns = + timeline_function.rate() * frames_per_ns_; + + *out = LinearTransform(timeline_function.reference_time(), + rate_in_frames_per_ns.subject_delta(), + rate_in_frames_per_ns.reference_delta(), + timeline_function.subject_time() * frames_per_ns_); +} + void AudioTrackImpl::OnPacketReceived(AudioPipe::AudioPacketRefPtr packet) { DCHECK(packet); for (const auto& output : outputs_) {
diff --git a/services/media/audio/audio_track_impl.h b/services/media/audio/audio_track_impl.h index f282849..71ec3e1 100644 --- a/services/media/audio/audio_track_impl.h +++ b/services/media/audio/audio_track_impl.h
@@ -15,7 +15,7 @@ #include "mojo/services/media/common/cpp/linear_transform.h" #include "services/media/audio/audio_pipe.h" #include "services/media/audio/fwd_decls.h" -#include "services/media/common/rate_control_base.h" +#include "services/media/common/timeline_control_site.h" namespace mojo { namespace media { @@ -41,9 +41,7 @@ // Accessors used by AudioOutputs during mixing to access parameters which are // important for the mixing process. - void SnapshotRateTrans(LinearTransform* out, uint32_t* generation = nullptr) { - rate_control_.SnapshotCurrentTransform(out, generation); - } + void SnapshotRateTrans(LinearTransform* out, uint32_t* generation = nullptr); const LinearTransform::Ratio& FractionalFrameToMediaTimeRatio() const { return frame_to_media_ratio_; @@ -63,7 +61,8 @@ void Describe(const DescribeCallback& cbk) override; void Configure(AudioTrackConfigurationPtr configuration, InterfaceRequest<MediaConsumer> req) override; - void GetRateControl(InterfaceRequest<RateControl> req) override; + void GetTimelineControlSite(InterfaceRequest<MediaTimelineControlSite> req) + override; void SetGain(float db_gain) override; // Methods called by our AudioPipe. @@ -80,7 +79,8 @@ AudioServerImpl* owner_; Binding<AudioTrack> binding_; AudioPipe pipe_; - RateControlBase rate_control_; + TimelineControlSite timeline_control_site_; + TimelineRate frames_per_ns_; LinearTransform::Ratio frame_to_media_ratio_; uint32_t bytes_per_frame_ = 1; AudioMediaTypeDetailsPtr format_;
diff --git a/services/media/common/BUILD.gn b/services/media/common/BUILD.gn index bc1e1b2..51880fa 100644 --- a/services/media/common/BUILD.gn +++ b/services/media/common/BUILD.gn
@@ -10,13 +10,16 @@ sources = [ "media_pipe_base.cc", "media_pipe_base.h", - "rate_control_base.cc", + "mojo_publisher.h", + "timeline_control_site.cc", + "timeline_control_site.h", ] deps = [ "//base", "//mojo/services/media/common/cpp", "//mojo/services/media/common/interfaces", + "//mojo/services/media/core/interfaces", ] }
diff --git a/services/media/factory_service/mojo_publisher.h b/services/media/common/mojo_publisher.h similarity index 91% rename from services/media/factory_service/mojo_publisher.h rename to services/media/common/mojo_publisher.h index 0fb4e88..503ce61 100644 --- a/services/media/factory_service/mojo_publisher.h +++ b/services/media/common/mojo_publisher.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SERVICES_MEDIA_FRAMEWORK_MOJO_PUBLISHER_H_ -#define SERVICES_MEDIA_FRAMEWORK_MOJO_PUBLISHER_H_ +#ifndef SERVICES_MEDIA_COMMON_MOJO_PUBLISHER_H_ +#define SERVICES_MEDIA_COMMON_MOJO_PUBLISHER_H_ #include <functional> #include <vector> @@ -62,4 +62,4 @@ } // namespace media } // namespace mojo -#endif // SERVICES_MEDIA_FRAMEWORK_MOJO_MOJO_ALLOCATOR_H_ +#endif // SERVICES_MEDIA_COMMON_MOJO_PUBLISHER_H_
diff --git a/services/media/common/rate_control_base.cc b/services/media/common/rate_control_base.cc deleted file mode 100644 index c72d265..0000000 --- a/services/media/common/rate_control_base.cc +++ /dev/null
@@ -1,299 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/debug/stack_trace.h" -#include "base/logging.h" -#include "mojo/services/media/common/cpp/local_time.h" -#include "services/media/common/rate_control_base.h" - -namespace mojo { -namespace media { - -static inline int64_t LocalTimeNow() { - return LocalClock::now().time_since_epoch().count(); -} - -RateControlBase::RateControlBase() - : binding_(this) - , current_transform_(0, 1) { -} - -RateControlBase::~RateControlBase() { - Reset(); -} - -bool RateControlBase::Bind(InterfaceRequest<RateControl> request) { - Reset(); - - binding_.Bind(request.Pass()); - binding_.set_connection_error_handler([this]() -> void { - Reset(); - }); - - return true; -} - -void RateControlBase::SnapshotCurrentTransform(LinearTransform* out, - uint32_t* generation) { - DCHECK(out); - base::AutoLock lock(transform_lock_); - ApplyPendingChangesLocked(LocalTimeNow()); - *out = current_transform_; - if (generation) { - *generation = generation_; - } -} - -void RateControlBase::GetCurrentTransform( - const GetCurrentTransformCallback& cbk) { - TimelineTransformPtr ret(TimelineTransform::New()); - ret->quad = TimelineQuad::New(); - - LinearTransform trans; - SnapshotCurrentTransform(&trans); - ret->quad->target_offset = trans.a_zero; - ret->quad->reference_offset = trans.b_zero; - ret->quad->target_delta = trans.scale.denominator; - ret->quad->reference_delta = trans.scale.numerator; - ret->reference_timeline_id = TimelineTransform::kContextual; - ret->target_timeline_id = TimelineTransform::kLocalTimeID; - - cbk.Run(ret.Pass()); -} - -// TODO(johngro): implement or remove. Until we have the ability to query the -// clock in the target timeline (or at least, transform local time to the target -// timeline), we have no way to apply scheduled changes. -void RateControlBase::SetTargetTimelineID(uint32_t id) { - if (id != TimelineTransform::kLocalTimeID) { - LOG(ERROR) << "Unsupported target timeline id (" - << id << ") during SetTargetTimelineID"; - Reset(); - } -} - -void RateControlBase::SetCurrentQuad(TimelineQuadPtr quad) { - // A target delta of zero means that the transformation from the target - // timeline to the media timeline is singular. This is not permitted, log an - // error and close the connection if someone attempts to do this. - if (!quad->target_delta) { - OnIllegalRateChange(quad->reference_delta, quad->target_delta); - return; - } else { - base::AutoLock lock(transform_lock_); - - reference_pending_changes_.clear(); - target_pending_changes_.clear(); - - current_transform_.a_zero = quad->target_offset; - current_transform_.b_zero = quad->reference_offset; - - if (quad->reference_delta) { - current_transform_.scale = - LinearTransform::Ratio(quad->reference_delta, quad->target_delta); - } else { - current_transform_.scale.numerator = 0; - current_transform_.scale.denominator = 1; - } - - AdvanceGenerationLocked(); - } -} - -void RateControlBase::SetRate(uint32_t reference_delta, uint32_t target_delta) { - // Only rate changes with a non-zero target_delta are permitted. See comment - // in SetCurrentQuad. - if (!target_delta) { - OnIllegalRateChange(reference_delta, target_delta); - return; - } else { - base::AutoLock lock(transform_lock_); - - // Make sure we are up to date. - int64_t target_now = LocalTimeNow(); - ApplyPendingChangesLocked(target_now); - - DCHECK(current_transform_.scale.denominator); - int64_t reference_now; - if (!current_transform_.DoForwardTransform(target_now, &reference_now)) { - // TODO(johngro): we cannot apply this transformation because of - // overflow, so we are forced to skip it. Should we introduce a callback - // to allow the user to know that their transformation was skipped? - // Alternatively, should we log something about how the transformation was - // skipped? - return; - } - - current_transform_.a_zero = target_now; - current_transform_.b_zero = reference_now; - current_transform_.scale.numerator = reference_delta; - current_transform_.scale.denominator = target_delta; - - AdvanceGenerationLocked(); - } -} - -void RateControlBase::SetRateAtReferenceTime(uint32_t reference_delta, - uint32_t target_delta, - int64_t reference_time) { - // Only rate changes with a non-zero target_delta are permitted. See comment - // in SetCurrentQuad. - if (!target_delta) { - OnIllegalRateChange(reference_delta, target_delta); - return; - } else { - base::AutoLock lock(transform_lock_); - - // If the user tries to schedule a change which takes place before any - // already scheduled change, ignore it. - if (reference_pending_changes_.size() && - reference_pending_changes_.back().b_zero >= reference_time) { - return; - } - - reference_pending_changes_.emplace_back(0, - reference_delta, - target_delta, - reference_time); - } -} - -void RateControlBase::SetRateAtTargetTime(uint32_t reference_delta, - uint32_t target_delta, - int64_t target_time) { - // Only rate changes with a non-zero target_delta are permitted. See comment - // in SetCurrentQuad. - if (!target_delta) { - OnIllegalRateChange(reference_delta, target_delta); - return; - } else { - base::AutoLock lock(transform_lock_); - - // If the user tries to schedule a change which takes place before any - // already scheduled change, ignore it. - if (target_pending_changes_.size() && - target_pending_changes_.back().a_zero >= target_time) { - return; - } - - target_pending_changes_.emplace_back(target_time, - reference_delta, - target_delta, - 0); - } -} - -void RateControlBase::CancelPendingChanges() { - base::AutoLock lock(transform_lock_); - - reference_pending_changes_.clear(); - target_pending_changes_.clear(); -} - -void RateControlBase::ApplyPendingChangesLocked(int64_t target_now) { - bool advance_generation = false; - - do { - // Grab a pointer to the next pending target scheduled transform which is - // not in the future, if any. - int64_t target_age; - const LinearTransform* target_trans = nullptr; - if (target_pending_changes_.size() && - (target_now >= target_pending_changes_.front().a_zero)) { - target_trans = &target_pending_changes_.front(); - target_age = target_now - target_trans->a_zero; - } - - // Grab a pointer to the next pending reference scheduled transform which is - // not in the future, if any. - // - // TODO(johngro): Optimize this. When we have pending reference scheduled - // transformations, we don't have to compute this each and every time. We - // could just keep the time of the next reference scheduled change - // (expressed in target time) pre-computed, and only update it when the - // current transformation actually changes. - int64_t reference_age; - int64_t next_reference_change_target_time; - const LinearTransform* reference_trans = nullptr; - if (reference_pending_changes_.size()) { - if (current_transform_.DoReverseTransform( - reference_pending_changes_.front().b_zero, - &next_reference_change_target_time)) { - if (target_now >= next_reference_change_target_time) { - reference_age = target_now - next_reference_change_target_time; - reference_trans = &reference_pending_changes_.front(); - } - } - } - - if (target_trans && (!reference_trans || (reference_age <= target_age))) { - // If we have a target scheduled transform which should be applied, and we - // either have no reference scheduled transform which should be applied, - // or we have a reference scheduled transform which should be applied - // after the pending target scheduled transform, go ahead and apply the - // target transform. - // - // Note: if we cannot apply this transformation due to overflow, we have a - // serious problem. For now, we just purge the scheduled transformation - // and move on, but this is something which should never happen. We - // should probably signal an error up to the user somehow. - int64_t next_target_change_reference_time; - - if (current_transform_.DoForwardTransform( - target_trans->a_zero, - &next_target_change_reference_time)) { - current_transform_.a_zero = target_trans->a_zero; - current_transform_.b_zero = next_target_change_reference_time; - current_transform_.scale.numerator = target_trans->scale.numerator; - current_transform_.scale.denominator = target_trans->scale.denominator; - DCHECK(current_transform_.scale.denominator); - } - - advance_generation = true; - target_pending_changes_.pop_front(); - } else if (reference_trans) { - // We have a reference scheduled transformation which should be applied - // before any pending target scheduled transformation. Do so now. No - // need to compute the splice point for the function, we have already done - // so when determining if we should apply this transformation or not. - current_transform_.a_zero = next_reference_change_target_time; - current_transform_.b_zero = reference_trans->a_zero; - current_transform_.scale.numerator = reference_trans->scale.numerator; - current_transform_.scale.denominator = reference_trans->scale.denominator; - DCHECK(current_transform_.scale.denominator); - - advance_generation = true; - reference_pending_changes_.pop_front(); - } else { - // We have no transformations which need to be applied at the moment. We - // are done for now. - break; - } - } while (true); - - // If we have applied any changes, advance the transformation generation - if (advance_generation) { - AdvanceGenerationLocked(); - } -} - -void RateControlBase::OnIllegalRateChange(uint32_t numerator, - uint32_t denominator) { - LOG(ERROR) << "Illegal rate change requested (" - << numerator << "/" << denominator << ")"; - Reset(); -} - -void RateControlBase::Reset() { - CancelPendingChanges(); - SetRate(0, 1); - - if (binding_.is_bound()) { - binding_.set_connection_error_handler(mojo::Closure()); - binding_.Close(); - } -} - -} // namespace media -} // namespace mojo
diff --git a/services/media/common/rate_control_base.h b/services/media/common/rate_control_base.h deleted file mode 100644 index 95d55fa..0000000 --- a/services/media/common/rate_control_base.h +++ /dev/null
@@ -1,108 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICES_MEDIA_COMMON_RATE_CONTROL_BASE_H_ -#define SERVICES_MEDIA_COMMON_RATE_CONTROL_BASE_H_ - -#include <deque> - -#include "base/synchronization/lock.h" -#include "mojo/public/cpp/bindings/binding.h" -#include "mojo/services/media/common/cpp/linear_transform.h" -#include "mojo/services/media/common/interfaces/media_common.mojom.h" -#include "mojo/services/media/common/interfaces/rate_control.mojom.h" - -namespace mojo { -namespace media { - -class RateControlBase : public RateControl { - public: - // Default constructor and destructor - RateControlBase(); - ~RateControlBase() override; - - bool Bind(InterfaceRequest<RateControl> request); - bool is_bound() const { return binding_.is_bound(); } - - // Close any existing connections to clients, clear any pending rate changes - // and set the clock rate to 0/1. - void Reset(); - - // TODO(johngro): snapshotting the current transform requires an evaluation of - // all the pending timeline transformations. Currently, we allow users to - // schedule an arbitrary number of pending transformations. This could cause - // DoS hazards if a malicious (or just poorly written) application is - // schedules a ton of transformations, and then something like the mixer - // threads in the audio server is forced to collapse all of these - // transformations in order to mix then next set of outbound audio frames. - // - // A simple way to avoid this would be to allow the user to have only one - // pending transformation at any point in time. - // - // It would be nice to be able to simply return the transformation and use - // Rvalue references in calling code to access the temporary snapshot, but - // style does not permit us to use Rvalue references in such a way. - // - // Also; the way pending transformations get applied probably needs to be - // re-worked. Currently, when we snapshot, we collapse any pending - // transformations which should have occurred relative to LocalClock::now() - // into the current transformation. For both video and audio, however, we are - // always mixing/composing for a point in time in the near future, not for - // right now. We really want to given the mixer compositor a view of what the - // transformation is going to be at the mix/composition point, not what it is - // now. Additionally, since audio process many frames at a time, we need to - // give the audio mixer some knowledge of when we think the snapshotted - // transformation is going to change next. The audio mixer wants to mix up to - // that point, but not past it, and then fetch the new transformation before - // proceeding. - void SnapshotCurrentTransform(LinearTransform* out, - uint32_t* generation = nullptr); - - // RateControl interface - // - void GetCurrentTransform(const GetCurrentTransformCallback& cbk) override; - void SetTargetTimelineID(uint32_t id) override; - void SetCurrentQuad(TimelineQuadPtr quad) override; - void SetRate(uint32_t reference_delta, uint32_t target_delta) override; - void SetRateAtReferenceTime(uint32_t reference_delta, - uint32_t target_delta, - int64_t reference_time) override; - void SetRateAtTargetTime(uint32_t reference_delta, - uint32_t target_delta, - int64_t target_time) override; - void CancelPendingChanges() override; - - protected: - void ApplyPendingChangesLocked(int64_t target_now); - void AdvanceGenerationLocked() { - // bump the generation counter. Do not use the value 0. - while (!(++generation_)) {} - } - void OnIllegalRateChange(uint32_t numerator, uint32_t denominator); - - Binding<RateControl> binding_; - uint32_t target_timeline_id = TimelineTransform::kLocalTimeID; - - // Transformation state. - // - // Note: We use the LinearTransforms such that space A is the target timeline - // and space B is the reference timeline. Applying this convention, - // transforming from target to reference is the "forward" transformation and - // is always defined. Transforming from reference to target is the "reverse" - // transformation, and is only defined when we are not paused. - // <pedantic> - // OK; It is defined, but the equation has a singularity and the mapping is - // not 1-to-1. - // </pedantic>. - base::Lock transform_lock_; - LinearTransform current_transform_; - std::deque<LinearTransform> reference_pending_changes_; - std::deque<LinearTransform> target_pending_changes_; - uint32_t generation_ = 1; -}; - -} // namespace media -} // namespace mojo - -#endif // SERVICES_MEDIA_COMMON_RATE_CONTROL_BASE_H_
diff --git a/services/media/common/timeline_control_site.cc b/services/media/common/timeline_control_site.cc new file mode 100644 index 0000000..26dc07f --- /dev/null +++ b/services/media/common/timeline_control_site.cc
@@ -0,0 +1,193 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/bind.h" +#include "base/logging.h" +#include "base/message_loop/message_loop.h" +#include "mojo/services/media/common/cpp/timeline.h" +#include "services/media/common/timeline_control_site.h" + +namespace mojo { +namespace media { + +// For checking preconditions when handling mojo requests. +// Checks the condition, and, if it's false, resets and calls return. +#define RCHECK(condition) \ + if (!(condition)) { \ + LOG(ERROR) << "request precondition failed: " #condition "."; \ + ResetUnsafe(); \ + return; \ + } + +TimelineControlSite::TimelineControlSite() + : control_site_binding_(this), consumer_binding_(this) { + task_runner_ = base::MessageLoop::current()->task_runner(); + DCHECK(task_runner_); + + base::AutoLock lock(lock_); + ClearPendingTimelineFunctionUnsafe(false); + + status_publisher_.SetCallbackRunner( + [this](const GetStatusCallback& callback, uint64_t version) { + MediaTimelineControlSiteStatusPtr status; + { + base::AutoLock lock(lock_); + status = MediaTimelineControlSiteStatus::New(); + status->timeline_transform = + TimelineTransform::From(current_timeline_function_); + status->end_of_stream = false; // TODO(dalesat): Provide this. + } + callback.Run(version, status.Pass()); + }); +} + +TimelineControlSite::~TimelineControlSite() {} + +void TimelineControlSite::Bind( + InterfaceRequest<MediaTimelineControlSite> request) { + if (control_site_binding_.is_bound()) { + control_site_binding_.Close(); + } + + control_site_binding_.Bind(request.Pass()); +} + +void TimelineControlSite::Reset() { + if (control_site_binding_.is_bound()) { + control_site_binding_.Close(); + } + + if (consumer_binding_.is_bound()) { + consumer_binding_.Close(); + } + + { + base::AutoLock lock(lock_); + current_timeline_function_ = TimelineFunction(); + ClearPendingTimelineFunctionUnsafe(false); + generation_ = 1; + } + + status_publisher_.SendUpdates(); +} + +void TimelineControlSite::SnapshotCurrentFunction(int64_t reference_time, + TimelineFunction* out, + uint32_t* generation) { + DCHECK(out); + base::AutoLock lock(lock_); + ApplyPendingChangesUnsafe(reference_time); + *out = current_timeline_function_; + if (generation) { + *generation = generation_; + } +} + +void TimelineControlSite::GetStatus(uint64_t version_last_seen, + const GetStatusCallback& callback) { + status_publisher_.Get(version_last_seen, callback); +} + +void TimelineControlSite::GetTimelineConsumer( + InterfaceRequest<TimelineConsumer> timeline_consumer) { + if (consumer_binding_.is_bound()) { + consumer_binding_.Close(); + } + + consumer_binding_.Bind(timeline_consumer.Pass()); +} + +void TimelineControlSite::SetTimelineTransform( + int64_t subject_time, + uint32_t reference_delta, + uint32_t subject_delta, + int64_t effective_reference_time, + int64_t effective_subject_time, + const SetTimelineTransformCallback& callback) { + base::AutoLock lock(lock_); + + // At most one of the effective times must be specified. + RCHECK(effective_reference_time == kUnspecifiedTime || + effective_subject_time == kUnspecifiedTime); + // effective_subject_time can only be used if we're progressing already. + RCHECK(effective_subject_time == kUnspecifiedTime || + current_timeline_function_.subject_delta() != 0); + RCHECK(reference_delta != 0); + + if (effective_subject_time != kUnspecifiedTime) { + // Infer effective_reference_time from effective_subject_time. + effective_reference_time = + current_timeline_function_.ApplyInverse(effective_subject_time); + + if (subject_time == kUnspecifiedTime) { + // Infer subject_time from effective_subject_time. + subject_time = effective_subject_time; + } + } else { + if (effective_reference_time == kUnspecifiedTime) { + // Neither effective time was specified. Effective time is now. + effective_reference_time = Timeline::local_now(); + } + + if (subject_time == kUnspecifiedTime) { + // Infer subject_time from effective_reference_time. + subject_time = current_timeline_function_(effective_reference_time); + } + } + + // Eject any previous pending change. + ClearPendingTimelineFunctionUnsafe(false); + + // Queue up the new pending change. + pending_timeline_function_ = TimelineFunction( + effective_reference_time, subject_time, reference_delta, subject_delta); + + set_timeline_transform_callback_ = callback; +} + +void TimelineControlSite::ApplyPendingChangesUnsafe(int64_t reference_time) { + lock_.AssertAcquired(); + + if (!TimelineFunctionPendingUnsafe() || + pending_timeline_function_.reference_time() > reference_time) { + return; + } + + current_timeline_function_ = pending_timeline_function_; + ClearPendingTimelineFunctionUnsafe(true); + + ++generation_; + + task_runner_->PostTask( + FROM_HERE, base::Bind(&MojoPublisher<GetStatusCallback>::SendUpdates, + base::Unretained(&status_publisher_))); +} + +void TimelineControlSite::ClearPendingTimelineFunctionUnsafe(bool completed) { + lock_.AssertAcquired(); + + pending_timeline_function_ = + TimelineFunction(kUnspecifiedTime, kUnspecifiedTime, 1, 0); + if (!set_timeline_transform_callback_.is_null()) { + task_runner_->PostTask( + FROM_HERE, base::Bind(&TimelineControlSite::RunCallback, + set_timeline_transform_callback_, completed)); + set_timeline_transform_callback_.reset(); + } +} + +void TimelineControlSite::ResetUnsafe() { + lock_.AssertAcquired(); + task_runner_->PostTask(FROM_HERE, base::Bind(&TimelineControlSite::Reset, + base::Unretained(this))); +} + +// static +void TimelineControlSite::RunCallback(SetTimelineTransformCallback callback, + bool completed) { + callback.Run(completed); +} + +} // namespace media +} // namespace mojo
diff --git a/services/media/common/timeline_control_site.h b/services/media/common/timeline_control_site.h new file mode 100644 index 0000000..4b2506f --- /dev/null +++ b/services/media/common/timeline_control_site.h
@@ -0,0 +1,94 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_SERVICES_MEDIA_COMMON_TIMELINE_CONTROL_SITE_IMPL_H_ +#define MOJO_SERVICES_MEDIA_COMMON_TIMELINE_CONTROL_SITE_IMPL_H_ + +#include "base/single_thread_task_runner.h" +#include "base/synchronization/lock.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/services/media/common/cpp/timeline_function.h" +#include "mojo/services/media/core/interfaces/timeline_controller.mojom.h" +#include "services/media/common/mojo_publisher.h" + +namespace mojo { +namespace media { + +// MediaTimelineControlSite implementation. +class TimelineControlSite : public MediaTimelineControlSite, + public TimelineConsumer { + public: + TimelineControlSite(); + + ~TimelineControlSite() override; + + // Binds to the control site. If a binding exists already, it is closed. + void Bind(InterfaceRequest<MediaTimelineControlSite> request); + + // Determines whether the control site is currently bound. + bool is_bound() { return control_site_binding_.is_bound(); } + + // Unbinds from clients and resets to initial state. + void Reset(); + + // Get the TimelineFunction for the reference_time (which should be 'now', + // approximately). + void SnapshotCurrentFunction(int64_t reference_time, + TimelineFunction* out, + uint32_t* generation = nullptr); + + // MediaTimelineControlSite implementation. + void GetStatus(uint64_t version_last_seen, + const GetStatusCallback& callback) override; + + void GetTimelineConsumer( + InterfaceRequest<TimelineConsumer> timeline_consumer) override; + + // TimelineConsumer implementation. + void SetTimelineTransform( + int64_t subject_time, + uint32_t reference_delta, + uint32_t subject_delta, + int64_t effective_reference_time, + int64_t effective_subject_time, + const SetTimelineTransformCallback& callback) override; + + private: + // Applies pending_timeline_function_ if it's time to do so based on the + // given reference time. + void ApplyPendingChangesUnsafe(int64_t reference_time); + + // Clears the pending timeline function and calls its associated callback + // with the indicated completed status. + void ClearPendingTimelineFunctionUnsafe(bool completed); + + // Determines if an unrealized timeline function is currently pending. + bool TimelineFunctionPendingUnsafe() { + return pending_timeline_function_.reference_time() != kUnspecifiedTime; + } + + // Unbinds from clients and resets to initial state. + void ResetUnsafe(); + + static void RunCallback(SetTimelineTransformCallback callback, + bool completed); + + Binding<MediaTimelineControlSite> control_site_binding_; + Binding<TimelineConsumer> consumer_binding_; + MojoPublisher<GetStatusCallback> status_publisher_; + + base::Lock lock_; + // BEGIN fields synchronized using lock_. + scoped_refptr<base::SingleThreadTaskRunner> task_runner_; + TimelineFunction current_timeline_function_; + TimelineFunction pending_timeline_function_; + SetTimelineTransformCallback set_timeline_transform_callback_; + uint32_t generation_ = 1; + // END fields synchronized using lock_. +}; + +} // namespace media +} // namespace mojo + +#endif // MOJO_SERVICES_MEDIA_COMMON_TIMELINE_CONTROL_SITE_IMPL_H_
diff --git a/services/media/factory_service/BUILD.gn b/services/media/factory_service/BUILD.gn index 21a1e4b..12f60e1 100644 --- a/services/media/factory_service/BUILD.gn +++ b/services/media/factory_service/BUILD.gn
@@ -28,7 +28,6 @@ "media_sink_impl.h", "media_source_impl.cc", "media_source_impl.h", - "mojo_publisher.h", "network_reader_impl.cc", "network_reader_impl.h", ] @@ -45,6 +44,7 @@ "//mojo/services/media/control/interfaces", "//mojo/services/media/core/interfaces", "//mojo/services/network/interfaces", + "//services/media/common", "//services/media/framework", "//services/media/framework_create", "//services/media/framework_ffmpeg",
diff --git a/services/media/factory_service/audio_track_controller.cc b/services/media/factory_service/audio_track_controller.cc index e36e04b..3b85597 100644 --- a/services/media/factory_service/audio_track_controller.cc +++ b/services/media/factory_service/audio_track_controller.cc
@@ -45,10 +45,10 @@ MediaConsumerPtr consumer; audio_track_->Configure(config.Pass(), GetProxy(&consumer)); - RateControlPtr rate_control; - audio_track_->GetRateControl(GetProxy(&rate_control)); + MediaTimelineControlSitePtr timeline_control_site; + audio_track_->GetTimelineControlSite(GetProxy(&timeline_control_site)); - callback(consumer.Pass(), rate_control.Pass()); + callback(consumer.Pass(), timeline_control_site.Pass()); } } // namespace media
diff --git a/services/media/factory_service/audio_track_controller.h b/services/media/factory_service/audio_track_controller.h index 9e15485..6a60dc9 100644 --- a/services/media/factory_service/audio_track_controller.h +++ b/services/media/factory_service/audio_track_controller.h
@@ -19,7 +19,7 @@ using GetSupportedMediaTypesCallback = std::function<void( std::unique_ptr<std::vector<std::unique_ptr<StreamTypeSet>>>)>; using ConfigureCallback = - std::function<void(MediaConsumerPtr, RateControlPtr)>; + std::function<void(MediaConsumerPtr, MediaTimelineControlSitePtr)>; AudioTrackController(const String& url, ApplicationImpl* app);
diff --git a/services/media/factory_service/media_demux_impl.h b/services/media/factory_service/media_demux_impl.h index 0be2435..43d96bf 100644 --- a/services/media/factory_service/media_demux_impl.h +++ b/services/media/factory_service/media_demux_impl.h
@@ -13,8 +13,8 @@ #include "mojo/public/cpp/bindings/binding.h" #include "mojo/services/media/core/interfaces/media_demux.mojom.h" #include "mojo/services/media/core/interfaces/seeking_reader.mojom.h" +#include "services/media/common/mojo_publisher.h" #include "services/media/factory_service/factory_service.h" -#include "services/media/factory_service/mojo_publisher.h" #include "services/media/framework/graph.h" #include "services/media/framework/parts/demux.h" #include "services/media/framework/util/incident.h"
diff --git a/services/media/factory_service/media_player_impl.h b/services/media/factory_service/media_player_impl.h index 33a7595..0a09d9c 100644 --- a/services/media/factory_service/media_player_impl.h +++ b/services/media/factory_service/media_player_impl.h
@@ -13,8 +13,8 @@ #include "mojo/services/media/common/interfaces/media_transport.mojom.h" #include "mojo/services/media/control/interfaces/media_factory.mojom.h" #include "mojo/services/media/core/interfaces/seeking_reader.mojom.h" +#include "services/media/common/mojo_publisher.h" #include "services/media/factory_service/factory_service.h" -#include "services/media/factory_service/mojo_publisher.h" namespace mojo { namespace media {
diff --git a/services/media/factory_service/media_sink_impl.cc b/services/media/factory_service/media_sink_impl.cc index da4b7ef..1fec5bd 100644 --- a/services/media/factory_service/media_sink_impl.cc +++ b/services/media/factory_service/media_sink_impl.cc
@@ -3,8 +3,8 @@ // found in the LICENSE file. #include "base/logging.h" -#include "mojo/services/media/common/cpp/linear_transform.h" -#include "mojo/services/media/common/cpp/local_time.h" +#include "mojo/services/media/common/cpp/timeline.h" +#include "mojo/services/media/common/cpp/timeline_function.h" #include "services/media/factory_service/media_sink_impl.h" #include "services/media/framework/util/conversion_pipeline_builder.h" #include "services/media/framework_mojo/mojo_type_conversions.h" @@ -32,15 +32,15 @@ DCHECK(destination_url); DCHECK(media_type); - status_publisher_.SetCallbackRunner( - [this](const GetStatusCallback& callback, uint64_t version) { - MediaSinkStatusPtr status = MediaSinkStatus::New(); - status->state = (producer_state_ == MediaState::PAUSED && rate_ != 0.0) - ? MediaState::PLAYING - : producer_state_; - status->timeline_transform = status_transform_.Clone(); - callback.Run(version, status.Pass()); - }); + status_publisher_.SetCallbackRunner([this](const GetStatusCallback& callback, + uint64_t version) { + MediaSinkStatusPtr status = MediaSinkStatus::New(); + status->state = (producer_state_ == MediaState::PAUSED && rate_ != 0.0) + ? MediaState::PLAYING + : producer_state_; + status->timeline_transform = TimelineTransform::From(timeline_function_); + callback.Run(version, status.Pass()); + }); PartRef consumer_ref = graph_.Add(consumer_); PartRef producer_ref = graph_.Add(producer_); @@ -112,7 +112,10 @@ graph_.ConnectOutputToPart(out, producer_ref); if (producer_stream_type->medium() == StreamType::Medium::kAudio) { - frames_per_second_ = producer_stream_type->audio()->frames_per_second(); + frames_per_ns_ = + TimelineRate(producer_stream_type->audio()->frames_per_second(), + Timeline::ns_from_seconds(1)); + } else { // Unsupported producer stream type. LOG(ERROR) << "unsupported producer stream type"; @@ -121,10 +124,12 @@ controller_->Configure( std::move(producer_stream_type), - [this](MediaConsumerPtr consumer, RateControlPtr rate_control) { + [this](MediaConsumerPtr consumer, + MediaTimelineControlSitePtr timeline_control_site) { DCHECK(consumer); - DCHECK(rate_control); - rate_control_ = rate_control.Pass(); + DCHECK(timeline_control_site); + timeline_control_site->GetTimelineConsumer( + GetProxy(&timeline_consumer_)); producer_->Connect(consumer.Pass(), [this]() { graph_.Prepare(); ready_.Occur(); @@ -160,99 +165,46 @@ return; } - if (!rate_control_) { + if (!timeline_consumer_) { rate_ = target_rate_; status_publisher_.SendUpdates(); return; } - // Desired rate in frames per second. - LinearTransform::Ratio rate_frames_per_second( - static_cast<uint32_t>(frames_per_second_ * target_rate_), 1); + // TODO(dalesat): start_local_time and start_presentation_time should be + // supplied via the mojo interface. For now, start_local_time is hard-coded + // to be 30ms in the future, and start_presentation_time is grabbed from the + // first primed packet or is calculated from start_local_time based on the + // previous timeline function. - // Local time rate in seconds_per_tick. - LinearTransform::Ratio local_seconds_per_tick(LocalDuration::period::num, - LocalDuration::period::den); - - // Desired rate in frames per local tick. - LinearTransform::Ratio rate_frames_per_tick; - bool success = LinearTransform::Ratio::Compose( - local_seconds_per_tick, rate_frames_per_second, &rate_frames_per_tick); - DCHECK(success) - << "LinearTransform::Ratio::Compose reports loss of precision"; - - // TODO(dalesat): start_local_time should be supplied via the mojo interface. - // For now, it's hard-coded to be 30ms in the future. // The local time when we want the rate to change. - int64_t start_local_time = - (LocalClock::now().time_since_epoch() + std::chrono::milliseconds(30)) - .count(); + int64_t start_local_time = Timeline::local_now() + Timeline::ns_from_ms(30); // The media time corresponding to start_local_time. - int64_t start_media_time; + int64_t start_presentation_time; if (flushed_ && producer_->GetFirstPtsSinceFlush() != Packet::kUnknownPts) { // We're getting started initially or after a flush/prime, so the media // time corresponding to start_local_time should be the PTS of - // the first packet. - start_media_time = producer_->GetFirstPtsSinceFlush(); + // the first packet converted to ns (rather than frame) units. + start_presentation_time = + producer_->GetFirstPtsSinceFlush() / frames_per_ns_; } else { // We're resuming, so the media time corresponding to start_local_time can // be calculated using the existing transform. - success = - transform_.DoForwardTransform(start_local_time, &start_media_time); - DCHECK(success) - << "LinearTransform::DoForwardTransform reports loss of precision"; + start_presentation_time = timeline_function_(start_local_time); } flushed_ = false; // Update the transform. - transform_ = - LinearTransform(start_local_time, rate_frames_per_tick, start_media_time); + timeline_function_ = TimelineFunction( + start_local_time, start_presentation_time, TimelineRate(target_rate_)); // Set the rate. - TimelineQuadPtr rate_quad = TimelineQuad::New(); - rate_quad->reference_offset = start_media_time; - rate_quad->target_offset = start_local_time; - rate_quad->reference_delta = rate_frames_per_tick.numerator; - rate_quad->target_delta = rate_frames_per_tick.denominator; - - rate_control_->SetCurrentQuad(rate_quad.Pass()); - - // Get the frame rate in frames per local tick. - LinearTransform::Ratio frame_rate_frames_per_second(frames_per_second_, 1); - LinearTransform::Ratio frame_rate_frames_per_tick; - success = LinearTransform::Ratio::Compose(local_seconds_per_tick, - frame_rate_frames_per_second, - &frame_rate_frames_per_tick); - DCHECK(success) - << "LinearTransform::Ratio::Compose reports loss of precision"; - - // Create a LinearTransform to translate from presentation units to - // local duration units. - LinearTransform local_to_presentation(0, frame_rate_frames_per_tick, 0); - - status_transform_ = TimelineTransform::New(); - status_transform_->quad = TimelineQuad::New(); - - // Translate the current transform quad so the presentation time units - // are the same as the local time units. - success = local_to_presentation.DoReverseTransform( - start_media_time, &status_transform_->quad->reference_offset); - DCHECK(success) - << "LinearTransform::DoReverseTransform reports loss of precision"; - status_transform_->quad->target_offset = start_local_time; - int64_t presentation_delta; - success = local_to_presentation.DoReverseTransform( - static_cast<int64_t>(rate_frames_per_tick.numerator), - &presentation_delta); - DCHECK(success) - << "LinearTransform::DoReverseTransform reports loss of precision"; - status_transform_->quad->reference_delta = - static_cast<uint32_t>(presentation_delta); - status_transform_->quad->target_delta = rate_frames_per_tick.denominator; - LinearTransform::Ratio::Reduce(&status_transform_->quad->reference_delta, - &status_transform_->quad->target_delta); + timeline_consumer_->SetTimelineTransform( + timeline_function_.subject_time(), timeline_function_.reference_delta(), + timeline_function_.subject_delta(), timeline_function_.reference_time(), + kUnspecifiedTime, [](bool completed) {}); rate_ = target_rate_; status_publisher_.SendUpdates();
diff --git a/services/media/factory_service/media_sink_impl.h b/services/media/factory_service/media_sink_impl.h index 17db63a..f323f62 100644 --- a/services/media/factory_service/media_sink_impl.h +++ b/services/media/factory_service/media_sink_impl.h
@@ -9,11 +9,11 @@ #include "mojo/public/cpp/application/application_impl.h" #include "mojo/public/cpp/bindings/binding.h" -#include "mojo/services/media/common/cpp/linear_transform.h" +#include "mojo/services/media/common/cpp/timeline_function.h" #include "mojo/services/media/control/interfaces/media_sink.mojom.h" +#include "services/media/common/mojo_publisher.h" #include "services/media/factory_service/audio_track_controller.h" #include "services/media/factory_service/factory_service.h" -#include "services/media/factory_service/mojo_publisher.h" #include "services/media/framework/graph.h" #include "services/media/framework/parts/decoder.h" #include "services/media/framework/util/incident.h" @@ -61,13 +61,12 @@ std::shared_ptr<MojoConsumer> consumer_; std::shared_ptr<MojoProducer> producer_; std::unique_ptr<AudioTrackController> controller_; - RateControlPtr rate_control_; + TimelineConsumerPtr timeline_consumer_; float rate_ = 0.0f; float target_rate_ = 0.0f; MediaState producer_state_ = MediaState::UNPREPARED; - LinearTransform transform_ = LinearTransform(0, 0, 1, 0); - TimelineTransformPtr status_transform_; - uint32_t frames_per_second_ = 0u; + TimelineFunction timeline_function_; + TimelineRate frames_per_ns_; bool flushed_ = true; MojoPublisher<GetStatusCallback> status_publisher_; };
diff --git a/services/media/factory_service/media_source_impl.h b/services/media/factory_service/media_source_impl.h index 5fdd3ee..d056aa4 100644 --- a/services/media/factory_service/media_source_impl.h +++ b/services/media/factory_service/media_source_impl.h
@@ -11,8 +11,8 @@ #include "mojo/public/cpp/bindings/binding.h" #include "mojo/services/media/control/interfaces/media_source.mojom.h" #include "mojo/services/media/core/interfaces/seeking_reader.mojom.h" +#include "services/media/common/mojo_publisher.h" #include "services/media/factory_service/factory_service.h" -#include "services/media/factory_service/mojo_publisher.h" #include "services/media/framework/graph.h" #include "services/media/framework/parts/decoder.h" #include "services/media/framework/parts/demux.h"
diff --git a/services/media/framework_mojo/mojo_formatting.cc b/services/media/framework_mojo/mojo_formatting.cc index 3afefac..7dfbc86 100644 --- a/services/media/framework_mojo/mojo_formatting.cc +++ b/services/media/framework_mojo/mojo_formatting.cc
@@ -259,23 +259,6 @@ return os << outdent; } -std::ostream& operator<<(std::ostream& os, const TimelineQuadPtr& value) { - if (!value) { - return os << "<nullptr>" << std::endl; - } else { - os << std::endl; - } - - os << indent; - os << begl << "int64 reference_offset: " << value->reference_offset - << std::endl; - os << begl << "int64 target_offset: " << value->target_offset << std::endl; - os << begl << "int32 reference_delta: " << value->reference_delta - << std::endl; - os << begl << "uint32 target_delta: " << value->target_delta << std::endl; - return os << outdent; -} - std::ostream& operator<<(std::ostream& os, const TimelineTransformPtr& value) { if (!value) { return os << "<nullptr>" << std::endl; @@ -284,11 +267,12 @@ } os << indent; - os << begl << "TimelineQuad quad: " << value->quad; - os << begl << "uint32 reference_timeline_id: " << value->reference_timeline_id + os << begl << "int64 reference_time: " << value->reference_time << std::endl; - os << begl << "uint32 target_timeline_id: " << value->target_timeline_id + os << begl << "int64 subject_time: " << value->subject_time << std::endl; + os << begl << "uint32 reference_delta: " << value->reference_delta << std::endl; + os << begl << "uint32 subject_delta: " << value->subject_delta << std::endl; return os << outdent; }
diff --git a/services/media/framework_mojo/mojo_formatting.h b/services/media/framework_mojo/mojo_formatting.h index 507cd5c..020d47e 100644 --- a/services/media/framework_mojo/mojo_formatting.h +++ b/services/media/framework_mojo/mojo_formatting.h
@@ -8,7 +8,7 @@ #include "mojo/services/media/common/interfaces/media_common.mojom.h" #include "mojo/services/media/common/interfaces/media_transport.mojom.h" #include "mojo/services/media/common/interfaces/media_types.mojom.h" -#include "mojo/services/media/common/interfaces/rate_control.mojom.h" +#include "mojo/services/media/common/interfaces/timelines.mojom.h" #include "mojo/services/media/control/interfaces/media_source.mojom.h" #include "mojo/services/network/interfaces/network_service.mojom.h" #include "services/media/framework/util/formatting.h" @@ -50,7 +50,6 @@ const SubpictureMediaTypeSetDetailsPtr& value); std::ostream& operator<<(std::ostream& os, const MediaSourceStreamDescriptorPtr& value); -std::ostream& operator<<(std::ostream& os, const TimelineQuadPtr& value); std::ostream& operator<<(std::ostream& os, const TimelineTransformPtr& value); std::ostream& operator<<(std::ostream& os, const HttpHeaderPtr& value);