Motown: Rename Ratio and LinearFunction classes
The new Ratio and LinearFunction classes are intended to help dealing
with timeline transformations. The names suggest that they implement the
mathematical concepts in a general way - this is not the case.
Furthermore, the generic names of their methods force a developer to
map between concepts, diluting the helpfulness of these helper classes.

This CL is strictly a rename...no functional changes were made. The
name mapping is:

from:                    to:

Ratio                    TimelineRate
    numerator                subject_delta
    denominator              reference_delta

LinearFunction           TimelineFunction
    domain_basis             reference_time
    range_basis              subject_time
    slope                    rate
    domain_delta             reference_delta
    range_delta              subject_delta

R=kulakowski@chromium.org

Review URL: https://codereview.chromium.org/1952673003 .
diff --git a/mojo/dart/packages/mojo_services/BUILD.gn b/mojo/dart/packages/mojo_services/BUILD.gn
index 140f817..2915f70 100644
--- a/mojo/dart/packages/mojo_services/BUILD.gn
+++ b/mojo/dart/packages/mojo_services/BUILD.gn
@@ -78,6 +78,7 @@
   "lib/mojo/tcp_server_socket.mojom.dart",
   "lib/mojo/terminal/terminal_client.mojom.dart",
   "lib/mojo/terminal/terminal.mojom.dart",
+  "lib/mojo/timelines.mojom.dart",
   "lib/mojo/udp_socket.mojom.dart",
   "lib/mojo/ui/input_connection.mojom.dart",
   "lib/mojo/ui/input_dispatcher.mojom.dart",
diff --git a/mojo/dart/packages/mojo_services/lib/mojo/timelines.mojom.dart b/mojo/dart/packages/mojo_services/lib/mojo/timelines.mojom.dart
new file mode 100644
index 0000000..1c97d38
--- /dev/null
+++ b/mojo/dart/packages/mojo_services/lib/mojo/timelines.mojom.dart
@@ -0,0 +1,564 @@
+// Copyright 2014 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.
+
+library timelines_mojom;
+import 'dart:async';
+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;
+
+
+
+class TimelineTransform extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(32, 0)
+  ];
+  int referenceTime = 0;
+  int subjectTime = 0;
+  int referenceDelta = 1;
+  int subjectDelta = 0;
+
+  TimelineTransform() : super(kVersions.last.size);
+
+  static TimelineTransform 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 TimelineTransform decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    TimelineTransform result = new TimelineTransform();
+
+    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.referenceTime = decoder0.decodeInt64(8);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.subjectTime = decoder0.decodeInt64(16);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.referenceDelta = decoder0.decodeUint32(24);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.subjectDelta = decoder0.decodeUint32(28);
+    }
+    return result;
+  }
+
+  void encode(bindings.Encoder encoder) {
+    var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last);
+    try {
+      encoder0.encodeInt64(referenceTime, 8);
+    } on bindings.MojoCodecError catch(e) {
+      e.message = "Error encountered while encoding field "
+          "referenceTime of struct TimelineTransform: $e";
+      rethrow;
+    }
+    try {
+      encoder0.encodeInt64(subjectTime, 16);
+    } on bindings.MojoCodecError catch(e) {
+      e.message = "Error encountered while encoding field "
+          "subjectTime of struct TimelineTransform: $e";
+      rethrow;
+    }
+    try {
+      encoder0.encodeUint32(referenceDelta, 24);
+    } on bindings.MojoCodecError catch(e) {
+      e.message = "Error encountered while encoding field "
+          "referenceDelta of struct TimelineTransform: $e";
+      rethrow;
+    }
+    try {
+      encoder0.encodeUint32(subjectDelta, 28);
+    } on bindings.MojoCodecError catch(e) {
+      e.message = "Error encountered while encoding field "
+          "subjectDelta of struct TimelineTransform: $e";
+      rethrow;
+    }
+  }
+
+  String toString() {
+    return "TimelineTransform("
+           "referenceTime: $referenceTime" ", "
+           "subjectTime: $subjectTime" ", "
+           "referenceDelta: $referenceDelta" ", "
+           "subjectDelta: $subjectDelta" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["referenceTime"] = referenceTime;
+    map["subjectTime"] = subjectTime;
+    map["referenceDelta"] = referenceDelta;
+    map["subjectDelta"] = subjectDelta;
+    return map;
+  }
+}
+
+
+class _TimelineConsumerSetTimelineTransformParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(40, 0)
+  ];
+  int subjectTime = 0;
+  int referenceDelta = 0;
+  int subjectDelta = 0;
+  int effectiveReferenceTime = 0;
+  int effectiveSubjectTime = 0;
+
+  _TimelineConsumerSetTimelineTransformParams() : super(kVersions.last.size);
+
+  static _TimelineConsumerSetTimelineTransformParams 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 _TimelineConsumerSetTimelineTransformParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    _TimelineConsumerSetTimelineTransformParams result = new _TimelineConsumerSetTimelineTransformParams();
+
+    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.referenceDelta = decoder0.decodeUint32(16);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.subjectDelta = decoder0.decodeUint32(20);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.effectiveReferenceTime = decoder0.decodeInt64(24);
+    }
+    if (mainDataHeader.version >= 0) {
+      
+      result.effectiveSubjectTime = 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 _TimelineConsumerSetTimelineTransformParams: $e";
+      rethrow;
+    }
+    try {
+      encoder0.encodeUint32(referenceDelta, 16);
+    } on bindings.MojoCodecError catch(e) {
+      e.message = "Error encountered while encoding field "
+          "referenceDelta of struct _TimelineConsumerSetTimelineTransformParams: $e";
+      rethrow;
+    }
+    try {
+      encoder0.encodeUint32(subjectDelta, 20);
+    } on bindings.MojoCodecError catch(e) {
+      e.message = "Error encountered while encoding field "
+          "subjectDelta of struct _TimelineConsumerSetTimelineTransformParams: $e";
+      rethrow;
+    }
+    try {
+      encoder0.encodeInt64(effectiveReferenceTime, 24);
+    } on bindings.MojoCodecError catch(e) {
+      e.message = "Error encountered while encoding field "
+          "effectiveReferenceTime of struct _TimelineConsumerSetTimelineTransformParams: $e";
+      rethrow;
+    }
+    try {
+      encoder0.encodeInt64(effectiveSubjectTime, 32);
+    } on bindings.MojoCodecError catch(e) {
+      e.message = "Error encountered while encoding field "
+          "effectiveSubjectTime of struct _TimelineConsumerSetTimelineTransformParams: $e";
+      rethrow;
+    }
+  }
+
+  String toString() {
+    return "_TimelineConsumerSetTimelineTransformParams("
+           "subjectTime: $subjectTime" ", "
+           "referenceDelta: $referenceDelta" ", "
+           "subjectDelta: $subjectDelta" ", "
+           "effectiveReferenceTime: $effectiveReferenceTime" ", "
+           "effectiveSubjectTime: $effectiveSubjectTime" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["subjectTime"] = subjectTime;
+    map["referenceDelta"] = referenceDelta;
+    map["subjectDelta"] = subjectDelta;
+    map["effectiveReferenceTime"] = effectiveReferenceTime;
+    map["effectiveSubjectTime"] = effectiveSubjectTime;
+    return map;
+  }
+}
+
+
+class TimelineConsumerSetTimelineTransformResponseParams extends bindings.Struct {
+  static const List<bindings.StructDataHeader> kVersions = const [
+    const bindings.StructDataHeader(16, 0)
+  ];
+  bool completed = false;
+
+  TimelineConsumerSetTimelineTransformResponseParams() : super(kVersions.last.size);
+
+  static TimelineConsumerSetTimelineTransformResponseParams 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 TimelineConsumerSetTimelineTransformResponseParams decode(bindings.Decoder decoder0) {
+    if (decoder0 == null) {
+      return null;
+    }
+    TimelineConsumerSetTimelineTransformResponseParams result = new TimelineConsumerSetTimelineTransformResponseParams();
+
+    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 TimelineConsumerSetTimelineTransformResponseParams: $e";
+      rethrow;
+    }
+  }
+
+  String toString() {
+    return "TimelineConsumerSetTimelineTransformResponseParams("
+           "completed: $completed" ")";
+  }
+
+  Map toJson() {
+    Map map = new Map();
+    map["completed"] = completed;
+    return map;
+  }
+}
+
+const int _timelineConsumerMethodSetTimelineTransformName = 0;
+
+class _TimelineConsumerServiceDescription implements service_describer.ServiceDescription {
+  dynamic getTopLevelInterface([Function responseFactory]) =>
+      responseFactory(null);
+
+  dynamic getTypeDefinition(String typeKey, [Function responseFactory]) =>
+      responseFactory(null);
+
+  dynamic getAllTypeDefinitions([Function responseFactory]) =>
+      responseFactory(null);
+}
+
+abstract class TimelineConsumer {
+  static const String serviceName = null;
+  dynamic setTimelineTransform(int subjectTime,int referenceDelta,int subjectDelta,int effectiveReferenceTime,int effectiveSubjectTime,[Function responseFactory = null]);
+  static const int kUnspecifiedTime = 9223372036854775807;
+}
+
+
+class _TimelineConsumerProxyImpl extends bindings.Proxy {
+  _TimelineConsumerProxyImpl.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) : super.fromEndpoint(endpoint);
+
+  _TimelineConsumerProxyImpl.fromHandle(core.MojoHandle handle) :
+      super.fromHandle(handle);
+
+  _TimelineConsumerProxyImpl.unbound() : super.unbound();
+
+  static _TimelineConsumerProxyImpl newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For _TimelineConsumerProxyImpl"));
+    return new _TimelineConsumerProxyImpl.fromEndpoint(endpoint);
+  }
+
+  service_describer.ServiceDescription get serviceDescription =>
+    new _TimelineConsumerServiceDescription();
+
+  void handleResponse(bindings.ServiceMessage message) {
+    switch (message.header.type) {
+      case _timelineConsumerMethodSetTimelineTransformName:
+        var r = TimelineConsumerSetTimelineTransformResponseParams.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);
+        break;
+    }
+  }
+
+  String toString() {
+    var superString = super.toString();
+    return "_TimelineConsumerProxyImpl($superString)";
+  }
+}
+
+
+class _TimelineConsumerProxyCalls implements TimelineConsumer {
+  _TimelineConsumerProxyImpl _proxyImpl;
+
+  _TimelineConsumerProxyCalls(this._proxyImpl);
+    dynamic setTimelineTransform(int subjectTime,int referenceDelta,int subjectDelta,int effectiveReferenceTime,int effectiveSubjectTime,[Function responseFactory = null]) {
+      var params = new _TimelineConsumerSetTimelineTransformParams();
+      params.subjectTime = subjectTime;
+      params.referenceDelta = referenceDelta;
+      params.subjectDelta = subjectDelta;
+      params.effectiveReferenceTime = effectiveReferenceTime;
+      params.effectiveSubjectTime = effectiveSubjectTime;
+      return _proxyImpl.sendMessageWithRequestId(
+          params,
+          _timelineConsumerMethodSetTimelineTransformName,
+          -1,
+          bindings.MessageHeader.kMessageExpectsResponse);
+    }
+}
+
+
+class TimelineConsumerProxy implements bindings.ProxyBase {
+  final bindings.Proxy impl;
+  TimelineConsumer ptr;
+
+  TimelineConsumerProxy(_TimelineConsumerProxyImpl proxyImpl) :
+      impl = proxyImpl,
+      ptr = new _TimelineConsumerProxyCalls(proxyImpl);
+
+  TimelineConsumerProxy.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) :
+      impl = new _TimelineConsumerProxyImpl.fromEndpoint(endpoint) {
+    ptr = new _TimelineConsumerProxyCalls(impl);
+  }
+
+  TimelineConsumerProxy.fromHandle(core.MojoHandle handle) :
+      impl = new _TimelineConsumerProxyImpl.fromHandle(handle) {
+    ptr = new _TimelineConsumerProxyCalls(impl);
+  }
+
+  TimelineConsumerProxy.unbound() :
+      impl = new _TimelineConsumerProxyImpl.unbound() {
+    ptr = new _TimelineConsumerProxyCalls(impl);
+  }
+
+  factory TimelineConsumerProxy.connectToService(
+      bindings.ServiceConnector s, String url, [String serviceName]) {
+    TimelineConsumerProxy p = new TimelineConsumerProxy.unbound();
+    s.connectToService(url, p, serviceName);
+    return p;
+  }
+
+  static TimelineConsumerProxy newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For TimelineConsumerProxy"));
+    return new TimelineConsumerProxy.fromEndpoint(endpoint);
+  }
+
+  String get serviceName => TimelineConsumer.serviceName;
+
+  Future close({bool immediate: false}) => impl.close(immediate: immediate);
+
+  Future responseOrError(Future f) => impl.responseOrError(f);
+
+  Future get errorFuture => impl.errorFuture;
+
+  int get version => impl.version;
+
+  Future<int> queryVersion() => impl.queryVersion();
+
+  void requireVersion(int requiredVersion) {
+    impl.requireVersion(requiredVersion);
+  }
+
+  String toString() {
+    return "TimelineConsumerProxy($impl)";
+  }
+}
+
+
+class TimelineConsumerStub extends bindings.Stub {
+  TimelineConsumer _impl = null;
+
+  TimelineConsumerStub.fromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint, [this._impl])
+      : super.fromEndpoint(endpoint);
+
+  TimelineConsumerStub.fromHandle(core.MojoHandle handle, [this._impl])
+      : super.fromHandle(handle);
+
+  TimelineConsumerStub.unbound() : super.unbound();
+
+  static TimelineConsumerStub newFromEndpoint(
+      core.MojoMessagePipeEndpoint endpoint) {
+    assert(endpoint.setDescription("For TimelineConsumerStub"));
+    return new TimelineConsumerStub.fromEndpoint(endpoint);
+  }
+
+
+  TimelineConsumerSetTimelineTransformResponseParams _timelineConsumerSetTimelineTransformResponseParamsFactory(bool completed) {
+    var result = new TimelineConsumerSetTimelineTransformResponseParams();
+    result.completed = completed;
+    return result;
+  }
+
+  dynamic handleMessage(bindings.ServiceMessage message) {
+    if (bindings.ControlMessageHandler.isControlMessage(message)) {
+      return bindings.ControlMessageHandler.handleMessage(this,
+                                                          0,
+                                                          message);
+    }
+    assert(_impl != null);
+    switch (message.header.type) {
+      case _timelineConsumerMethodSetTimelineTransformName:
+        var params = _TimelineConsumerSetTimelineTransformParams.deserialize(
+            message.payload);
+        var response = _impl.setTimelineTransform(params.subjectTime,params.referenceDelta,params.subjectDelta,params.effectiveReferenceTime,params.effectiveSubjectTime,_timelineConsumerSetTimelineTransformResponseParamsFactory);
+        if (response is Future) {
+          return response.then((response) {
+            if (response != null) {
+              return buildResponseWithId(
+                  response,
+                  _timelineConsumerMethodSetTimelineTransformName,
+                  message.header.requestId,
+                  bindings.MessageHeader.kMessageIsResponse);
+            }
+          });
+        } else if (response != null) {
+          return buildResponseWithId(
+              response,
+              _timelineConsumerMethodSetTimelineTransformName,
+              message.header.requestId,
+              bindings.MessageHeader.kMessageIsResponse);
+        }
+        break;
+      default:
+        throw new bindings.MojoCodecError("Unexpected message name");
+        break;
+    }
+    return null;
+  }
+
+  TimelineConsumer get impl => _impl;
+  set impl(TimelineConsumer d) {
+    assert(_impl == null);
+    _impl = d;
+  }
+
+  String toString() {
+    var superString = super.toString();
+    return "TimelineConsumerStub($superString)";
+  }
+
+  int get version => 0;
+
+  static service_describer.ServiceDescription _cachedServiceDescription;
+  static service_describer.ServiceDescription get serviceDescription {
+    if (_cachedServiceDescription == null) {
+      _cachedServiceDescription = new _TimelineConsumerServiceDescription();
+    }
+    return _cachedServiceDescription;
+  }
+}
+
+
+
diff --git a/mojo/services/media/common/cpp/BUILD.gn b/mojo/services/media/common/cpp/BUILD.gn
index e46af00..c2f6b70 100644
--- a/mojo/services/media/common/cpp/BUILD.gn
+++ b/mojo/services/media/common/cpp/BUILD.gn
@@ -12,17 +12,17 @@
     "circular_buffer_media_pipe_adapter.h",
     "fifo_allocator.cc",
     "fifo_allocator.h",
-    "linear_function.cc",
-    "linear_function.h",
     "linear_transform.cc",
     "linear_transform.h",
     "local_time.h",
     "mapped_shared_buffer.cc",
     "mapped_shared_buffer.h",
-    "ratio.cc",
-    "ratio.h",
     "shared_media_buffer_allocator.cc",
     "shared_media_buffer_allocator.h",
+    "timeline_function.cc",
+    "timeline_function.h",
+    "timeline_rate.cc",
+    "timeline_rate.h",
   ]
 
   if (is_posix) {
diff --git a/mojo/services/media/common/cpp/linear_function.cc b/mojo/services/media/common/cpp/linear_function.cc
deleted file mode 100644
index ec662e7..0000000
--- a/mojo/services/media/common/cpp/linear_function.cc
+++ /dev/null
@@ -1,31 +0,0 @@
-// 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 <limits>
-#include <utility>
-
-#include "mojo/public/cpp/environment/logging.h"
-#include "mojo/services/media/common/cpp/linear_function.h"
-
-namespace mojo {
-namespace media {
-
-// static
-int64_t LinearFunction::Apply(int64_t domain_basis,
-                              int64_t range_basis,
-                              const Ratio& slope,  // range_delta / domain_delta
-                              int64_t domain_input) {
-  return slope.Scale(domain_input - domain_basis) + range_basis;
-}
-
-// static
-LinearFunction LinearFunction::Compose(const LinearFunction& bc,
-                                       const LinearFunction& ab,
-                                       bool exact) {
-  return LinearFunction(ab.domain_basis(), bc.Apply(ab.range_basis()),
-                        Ratio::Product(ab.slope(), bc.slope(), exact));
-}
-
-}  // namespace media
-}  // namespace mojo
diff --git a/mojo/services/media/common/cpp/linear_function.h b/mojo/services/media/common/cpp/linear_function.h
deleted file mode 100644
index be66bb3..0000000
--- a/mojo/services/media/common/cpp/linear_function.h
+++ /dev/null
@@ -1,123 +0,0 @@
-// 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_LINEAR_FUNCTION_H_
-#define MOJO_SERVICES_MEDIA_COMMON_CPP_LINEAR_FUNCTION_H_
-
-#include "mojo/public/cpp/environment/logging.h"
-#include "mojo/services/media/common/cpp/ratio.h"
-
-namespace mojo {
-namespace media {
-
-// TODO(dalesat): Consider always allowing inexact results.
-
-// A linear function from int64_t to int64_t with non-negative slope. The
-// representation is in point-slope form. The point is represented as two
-// int64_t values (domain_basis, range_basis), and the slope is represented as
-// the ratio of two uint32_t values (range_delta / domain_delta). 'Domain'
-// refers to the input space, and 'range' refers to the output space.
-struct LinearFunction {
-  // Applies a linear function.
-  static int64_t Apply(int64_t domain_basis,
-                       int64_t range_basis,
-                       const Ratio& slope,  // range_delta / domain_delta
-                       int64_t domain_input);
-
-  // Applies the inverse of a linear function.
-  static int64_t ApplyInverse(int64_t domain_basis,
-                              int64_t range_basis,
-                              const Ratio& slope,  // range_delta / domain_delta
-                              int64_t range_input) {
-    MOJO_DCHECK(slope.denominator() != 0u);
-    return Apply(range_basis, domain_basis, slope.Inverse(), range_input);
-  }
-
-  // Composes two linear functions B->C and A->B producing A->C. If exact is
-  // true, DCHECKs on loss of precision.
-  static LinearFunction Compose(const LinearFunction& bc,
-                                const LinearFunction& ab,
-                                bool exact = true);
-
-  LinearFunction() : domain_basis_(0), range_basis_(0) {}
-
-  LinearFunction(int64_t domain_basis,
-                 int64_t range_basis,
-                 uint32_t domain_delta,
-                 uint32_t range_delta)
-      : domain_basis_(domain_basis),
-        range_basis_(range_basis),
-        slope_(range_delta, domain_delta) {}
-
-  LinearFunction(int64_t domain_basis,
-                 int64_t range_basis,
-                 const Ratio& slope)  // range_delta / domain_delta
-      : domain_basis_(domain_basis),
-        range_basis_(range_basis),
-        slope_(slope) {}
-
-  explicit LinearFunction(const Ratio& slope)  // range_delta / domain_delta
-      : domain_basis_(0),
-        range_basis_(0),
-        slope_(slope) {}
-
-  // Applies the function. Returns Ratio::kOverflow on overflow.
-  int64_t Apply(int64_t domain_input) const {
-    return Apply(domain_basis_, range_basis_, slope_, domain_input);
-  }
-
-  // Applies the inverse of the function. Returns Ratio::kOverflow on overflow.
-  int64_t ApplyInverse(int64_t range_input) const {
-    MOJO_DCHECK(slope_.denominator() != 0u);
-    return ApplyInverse(domain_basis_, range_basis_, slope_, range_input);
-  }
-
-  // Applies the function.  Returns Ratio::kOverflow on overflow.
-  int64_t operator()(int64_t domain_input) const { return Apply(domain_input); }
-
-  // Returns a linear function that is the inverse if this linear function.
-  LinearFunction Inverse() const {
-    MOJO_DCHECK(slope_.denominator() != 0u);
-    return LinearFunction(range_basis_, domain_basis_, slope_.Inverse());
-  }
-
-  int64_t domain_basis() const { return domain_basis_; }
-
-  int64_t range_basis() const { return range_basis_; }
-
-  const Ratio& slope() const { return slope_; }
-
-  uint32_t domain_delta() const { return slope_.denominator(); }
-
-  uint32_t range_delta() const { return slope_.numerator(); }
-
-  int64_t domain_basis_;
-  int64_t range_basis_;
-  Ratio slope_;  // range_delta / domain_delta
-};
-
-// Tests two linear functions for equality. Equality requires equal basis
-// values.
-inline bool operator==(const LinearFunction& a, const LinearFunction& b) {
-  return a.domain_basis() == b.domain_basis() &&
-         a.range_basis() == b.range_basis() && a.slope() == b.slope();
-}
-
-// Tests two linear functions for inequality. Equality requires equal basis
-// values.
-inline bool operator!=(const LinearFunction& a, const LinearFunction& b) {
-  return !(a == b);
-}
-
-// Composes two linear functions B->C and A->B producing A->C. DCHECKs on
-// loss of precision.
-inline LinearFunction operator*(const LinearFunction& bc,
-                                const LinearFunction& ab) {
-  return LinearFunction::Compose(bc, ab);
-}
-
-}  // namespace media
-}  // namespace mojo
-
-#endif  // MOJO_SERVICES_MEDIA_COMMON_CPP_LINEAR_FUNCTION_H_
diff --git a/mojo/services/media/common/cpp/ratio.cc b/mojo/services/media/common/cpp/ratio.cc
deleted file mode 100644
index 56a3f51..0000000
--- a/mojo/services/media/common/cpp/ratio.cc
+++ /dev/null
@@ -1,228 +0,0 @@
-// 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 <limits>
-#include <utility>
-
-#include "mojo/public/cpp/environment/logging.h"
-#include "mojo/services/media/common/cpp/ratio.h"
-
-namespace mojo {
-namespace media {
-
-namespace {
-
-// Calculates the greatest common denominator (factor) of two values.
-template <typename T>
-T BinaryGcd(T a, T b) {
-  if (a == 0) {
-    return b;
-  }
-
-  if (b == 0) {
-    return a;
-  }
-
-  // Remove and count the common factors of 2.
-  uint8_t twos;
-  for (twos = 0; ((a | b) & 1) == 0; ++twos) {
-    a >>= 1;
-    b >>= 1;
-  }
-
-  // Get rid of the non-common factors of 2 in a. a is non-zero, so this
-  // terminates.
-  while ((a & 1) == 0) {
-    a >>= 1;
-  }
-
-  do {
-    // Get rid of the non-common factors of 2 in b. b is non-zero, so this
-    // terminates.
-    while ((b & 1) == 0) {
-      b >>= 1;
-    }
-
-    // Apply the Euclid subtraction method.
-    if (a > b) {
-      std::swap(a, b);
-    }
-
-    b = b - a;
-  } while (b != 0);
-
-  // Multiply in the common factors of two.
-  return a << twos;
-}
-
-// Reduces the ration of *numerator and *denominator.
-template <typename T>
-void ReduceRatio(T* numerator, T* denominator) {
-  MOJO_DCHECK(numerator != nullptr);
-  MOJO_DCHECK(denominator != nullptr);
-  MOJO_DCHECK(*denominator != 0);
-
-  T gcd = BinaryGcd(*numerator, *denominator);
-
-  if (gcd == 0) {
-    *denominator = 1;
-    return;
-  }
-
-  if (gcd == 1) {
-    return;
-  }
-
-  *numerator = *numerator / gcd;
-  *denominator = *denominator / gcd;
-}
-
-template void ReduceRatio<uint64_t>(uint64_t* numerator, uint64_t* denominator);
-template void ReduceRatio<uint32_t>(uint32_t* numerator, uint32_t* denominator);
-
-// Scales a uint64_t value by the ratio of two uint32_t values. If round_up is
-// true, the result is rounded up rather than down. overflow is set to indicate
-// overflow.
-uint64_t ScaleUInt64(uint64_t value,
-                     uint32_t numerator,
-                     uint32_t denominator,
-                     bool round_up,
-                     bool* overflow) {
-  MOJO_DCHECK(denominator != 0u);
-  MOJO_DCHECK(overflow != nullptr);
-
-  constexpr uint64_t kLow32Bits = 0xffffffffu;
-  constexpr uint64_t kHigh32Bits = kLow32Bits << 32u;
-
-  // high and low are the product of the numerator and the high and low halves
-  // (respectively) of value.
-  uint64_t high = numerator * (value >> 32u);
-  uint64_t low = numerator * (value & kLow32Bits);
-  // Ignoring overflow and remainder, the result we want is:
-  // ((high << 32) + low) / denominator.
-
-  // Move the high end of low into the low end of high.
-  high += low >> 32u;
-  low = low & kLow32Bits;
-  // Ignoring overflow and remainder, the result we want is still:
-  // ((high << 32) + low) / denominator.
-
-  // When we divide high by denominator, there'll be a remainder. Make
-  // that the high end of low, which is currently all zeroes.
-  low |= (high % denominator) << 32u;
-
-  // Determine if we need to round up when we're done:
-  round_up = round_up && (low % denominator) != 0;
-
-  // Do the division.
-  high /= denominator;
-  low /= denominator;
-
-  // If high's top 32 bits aren't all zero, we have overflow.
-  if (high & kHigh32Bits) {
-    *overflow = true;
-    return 0;
-  }
-
-  uint64_t result = (high << 32u) | low;
-  if (round_up) {
-    if (result == std::numeric_limits<int64_t>::max()) {
-      *overflow = true;
-      return 0;
-    }
-    ++result;
-  }
-
-  *overflow = false;
-  return result;
-}
-
-}  // namespace
-
-// static
-void Ratio::Reduce(uint32_t* numerator, uint32_t* denominator) {
-  ReduceRatio(numerator, denominator);
-}
-
-// static
-void Ratio::Product(uint32_t a_numerator,
-                    uint32_t a_denominator,
-                    uint32_t b_numerator,
-                    uint32_t b_denominator,
-                    uint32_t* product_numerator,
-                    uint32_t* product_denominator,
-                    bool exact) {
-  MOJO_DCHECK(a_denominator != 0);
-  MOJO_DCHECK(b_denominator != 0);
-  MOJO_DCHECK(product_numerator != nullptr);
-  MOJO_DCHECK(product_denominator != nullptr);
-
-  uint64_t numerator = static_cast<uint64_t>(a_numerator) * b_numerator;
-  uint64_t denominator = static_cast<uint64_t>(a_denominator) * b_denominator;
-
-  ReduceRatio(&numerator, &denominator);
-
-  if (numerator > std::numeric_limits<uint32_t>::max() ||
-      denominator > std::numeric_limits<uint32_t>::max()) {
-    MOJO_DCHECK(!exact);
-
-    do {
-      numerator >>= 1;
-      denominator >>= 1;
-    } while (numerator > std::numeric_limits<uint32_t>::max() ||
-             denominator > std::numeric_limits<uint32_t>::max());
-
-    if (denominator == 0) {
-      // Product is larger than we can represent. Return the largest value we
-      // can represent.
-      *product_numerator = std::numeric_limits<uint32_t>::max();
-      *product_denominator = 1;
-      return;
-    }
-  }
-
-  *product_numerator = static_cast<uint32_t>(numerator);
-  *product_denominator = static_cast<uint32_t>(denominator);
-}
-
-// static
-int64_t Ratio::Scale(int64_t value, uint32_t numerator, uint32_t denominator) {
-  static constexpr uint64_t abs_of_min_int64 =
-      static_cast<uint64_t>(std::numeric_limits<int64_t>::max()) + 1;
-
-  MOJO_DCHECK(denominator != 0u);
-
-  bool overflow;
-
-  uint64_t abs_result;
-
-  if (value >= 0) {
-    abs_result = ScaleUInt64(static_cast<uint64_t>(value), numerator,
-                             denominator, false, &overflow);
-  } else if (value == std::numeric_limits<int64_t>::min()) {
-    abs_result = ScaleUInt64(abs_of_min_int64, numerator, denominator,
-                             true, &overflow);
-  } else {
-    abs_result = ScaleUInt64(static_cast<uint64_t>(-value), numerator,
-                             denominator, true, &overflow);
-  }
-
-  if (overflow) {
-    return Ratio::kOverflow;
-  }
-
-  // Make sure we won't overflow when we cast to int64_t.
-  if (abs_result > static_cast<uint64_t>(std::numeric_limits<int64_t>::max())) {
-    if (value < 0 && abs_result == abs_of_min_int64) {
-      return std::numeric_limits<int64_t>::min();
-    }
-    return Ratio::kOverflow;
-  }
-
-  return value >= 0 ? static_cast<int64_t>(abs_result)
-                    : -static_cast<int64_t>(abs_result);
-}
-
-}  // namespace media
-}  // namespace mojo
diff --git a/mojo/services/media/common/cpp/ratio.h b/mojo/services/media/common/cpp/ratio.h
deleted file mode 100644
index 0755c6e..0000000
--- a/mojo/services/media/common/cpp/ratio.h
+++ /dev/null
@@ -1,118 +0,0 @@
-// 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_RATIO_H_
-#define MOJO_SERVICES_MEDIA_COMMON_CPP_RATIO_H_
-
-#include <stdint.h>
-
-#include <limits>
-
-#include "mojo/public/cpp/environment/logging.h"
-
-namespace mojo {
-namespace media {
-
-// TODO(dalesat): Consider always allowing inexact results.
-
-// Expresses a non-negative rational number as the ratio between two uint32_t
-// values.
-struct Ratio {
-  // Used to indicate overflow of scaling operations.
-  static constexpr int64_t kOverflow = std::numeric_limits<int64_t>::max();
-
-  // Reduces the ratio of *numerator and *denominator.
-  static void Reduce(uint32_t* numerator, uint32_t* denominator);
-
-  // Produces the product of the ratios. If exact is true, DCHECKs on loss of
-  // precision.
-  static void Product(uint32_t a_numerator,
-                      uint32_t a_denominator,
-                      uint32_t b_numerator,
-                      uint32_t b_denominator,
-                      uint32_t* product_numerator,
-                      uint32_t* product_denominator,
-                      bool exact = true);
-
-  // Produces the product of the ratios and the int64_t as an int64_t. Returns
-  // kOverflow on overflow.
-  static int64_t Scale(int64_t value, uint32_t numerator, uint32_t denominator);
-
-  // Returns the product of the ratios. If exact is true, DCHECKs on loss of
-  // precision.
-  static Ratio Product(const Ratio& a, const Ratio& b, bool exact = true) {
-    uint32_t result_numerator;
-    uint32_t result_denominator;
-    Product(a.numerator(), a.denominator(), b.numerator(), b.denominator(),
-            &result_numerator, &result_denominator, exact);
-    return Ratio(result_numerator, result_denominator);
-  }
-
-  Ratio() : numerator_(0), denominator_(1) {}
-
-  explicit Ratio(uint32_t numerator) : numerator_(numerator), denominator_(1) {}
-
-  Ratio(uint32_t numerator, uint32_t denominator)
-      : numerator_(numerator), denominator_(denominator) {
-    MOJO_DCHECK(denominator != 0);
-    Reduce(&numerator_, &denominator_);
-  }
-
-  // Returns the inverse of the ratio. DCHECKs if the numerator of this ratio
-  // is zero.
-  Ratio Inverse() const {
-    MOJO_DCHECK(numerator_ != 0);
-    return Ratio(denominator_, numerator_);
-  }
-
-  // Scales the value by this ratio. Returns kOverflow on overflow.
-  int64_t Scale(int64_t value) const {
-    return Scale(value, numerator_, denominator_);
-  }
-
-  uint32_t numerator() const { return numerator_; }
-  uint32_t denominator() const { return denominator_; }
-
- private:
-  uint32_t numerator_;
-  uint32_t denominator_;
-};
-
-// Tests two ratios for equality.
-inline bool operator==(const Ratio& a, const Ratio& b) {
-  return a.numerator() == b.numerator() && a.denominator() == b.denominator();
-}
-
-// Tests two ratios for inequality.
-inline bool operator!=(const Ratio& a, const Ratio& b) {
-  return !(a == b);
-}
-
-// Returns the product of the two ratios. DCHECKs on loss of precision.
-inline Ratio operator*(const Ratio& a, const Ratio& b) {
-  return Ratio::Product(a, b);
-}
-
-// Returns the product of the ratio and the int64_t. Returns kOverflow on
-// overflow.
-inline int64_t operator*(const Ratio& a, int64_t b) {
-  return a.Scale(b);
-}
-
-// Returns the product of the ratio and the int64_t. Returns kOverflow on
-// overflow.
-inline int64_t operator*(int64_t a, const Ratio& b) {
-  return b.Scale(a);
-}
-
-// Returns the the int64_t divided by the ratio. Returns kOverflow on
-// overflow.
-inline int64_t operator/(int64_t a, const Ratio& b) {
-  return b.Inverse().Scale(a);
-}
-
-}  // namespace media
-}  // namespace mojo
-
-#endif  // MOJO_SERVICES_MEDIA_COMMON_CPP_LINEAR_TRANSFORM_H_
diff --git a/mojo/services/media/common/cpp/timeline_function.cc b/mojo/services/media/common/cpp/timeline_function.cc
new file mode 100644
index 0000000..350fde6
--- /dev/null
+++ b/mojo/services/media/common/cpp/timeline_function.cc
@@ -0,0 +1,32 @@
+// 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 <limits>
+#include <utility>
+
+#include "mojo/public/cpp/environment/logging.h"
+#include "mojo/services/media/common/cpp/timeline_function.h"
+
+namespace mojo {
+namespace media {
+
+// static
+int64_t TimelineFunction::Apply(
+    int64_t reference_time,
+    int64_t subject_time,
+    const TimelineRate& rate,  // subject_delta / reference_delta
+    int64_t reference_input) {
+  return rate.Scale(reference_input - reference_time) + subject_time;
+}
+
+// static
+TimelineFunction TimelineFunction::Compose(const TimelineFunction& bc,
+                                           const TimelineFunction& ab,
+                                           bool exact) {
+  return TimelineFunction(ab.reference_time(), bc.Apply(ab.subject_time()),
+                          TimelineRate::Product(ab.rate(), bc.rate(), exact));
+}
+
+}  // namespace media
+}  // namespace mojo
diff --git a/mojo/services/media/common/cpp/timeline_function.h b/mojo/services/media/common/cpp/timeline_function.h
new file mode 100644
index 0000000..67ac80a
--- /dev/null
+++ b/mojo/services/media/common/cpp/timeline_function.h
@@ -0,0 +1,133 @@
+// 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_FUNCTION_H_
+#define MOJO_SERVICES_MEDIA_COMMON_CPP_TIMELINE_FUNCTION_H_
+
+#include "mojo/public/cpp/environment/logging.h"
+#include "mojo/services/media/common/cpp/timeline_rate.h"
+
+namespace mojo {
+namespace media {
+
+// TODO(dalesat): Consider always allowing inexact results.
+
+// A linear function from int64_t to int64_t with non-negative slope that
+// translates reference timeline values into subject timeline values (the
+// 'subject' being the timeline that's represented by the function). The
+// representation is in point-slope form. The point is represented as two
+// int64_t time values (reference_time, subject_time), and the slope (rate) is
+// represented as a TimelineRate, the ratio of two uint32_t values
+// (subject_delta / reference_delta).
+class TimelineFunction {
+ public:
+  // Applies a timeline function.
+  static int64_t Apply(
+      int64_t reference_time,
+      int64_t subject_time,
+      const TimelineRate& rate,  // subject_delta / reference_delta
+      int64_t reference_input);
+
+  // Applies the inverse of a timeline function.
+  static int64_t ApplyInverse(
+      int64_t reference_time,
+      int64_t subject_time,
+      const TimelineRate& rate,  // subject_delta / reference_delta
+      int64_t subject_input) {
+    MOJO_DCHECK(rate.reference_delta() != 0u);
+    return Apply(subject_time, reference_time, rate.Inverse(), subject_input);
+  }
+
+  // Composes two timeline functions B->C and A->B producing A->C. If exact is
+  // true, DCHECKs on loss of precision.
+  static TimelineFunction Compose(const TimelineFunction& bc,
+                                  const TimelineFunction& ab,
+                                  bool exact = true);
+
+  TimelineFunction() : reference_time_(0), subject_time_(0) {}
+
+  TimelineFunction(int64_t reference_time,
+                   int64_t subject_time,
+                   uint32_t reference_delta,
+                   uint32_t subject_delta)
+      : reference_time_(reference_time),
+        subject_time_(subject_time),
+        rate_(subject_delta, reference_delta) {}
+
+  TimelineFunction(int64_t reference_time,
+                   int64_t subject_time,
+                   const TimelineRate& rate)  // subject_delta / reference_delta
+      : reference_time_(reference_time),
+        subject_time_(subject_time),
+        rate_(rate) {}
+
+  explicit TimelineFunction(
+      const TimelineRate& rate)  // subject_delta / reference_delta
+      : reference_time_(0),
+        subject_time_(0),
+        rate_(rate) {}
+
+  // Applies the function. Returns TimelineRate::kOverflow on overflow.
+  int64_t Apply(int64_t reference_input) const {
+    return Apply(reference_time_, subject_time_, rate_, reference_input);
+  }
+
+  // Applies the inverse of the function. Returns TimelineRate::kOverflow on
+  // overflow.
+  int64_t ApplyInverse(int64_t subject_input) const {
+    MOJO_DCHECK(rate_.reference_delta() != 0u);
+    return ApplyInverse(reference_time_, subject_time_, rate_, subject_input);
+  }
+
+  // Applies the function.  Returns TimelineRate::kOverflow on overflow.
+  int64_t operator()(int64_t reference_input) const {
+    return Apply(reference_input);
+  }
+
+  // Returns a timeline function that is the inverse if this timeline function.
+  TimelineFunction Inverse() const {
+    MOJO_DCHECK(rate_.reference_delta() != 0u);
+    return TimelineFunction(subject_time_, reference_time_, rate_.Inverse());
+  }
+
+  int64_t reference_time() const { return reference_time_; }
+
+  int64_t subject_time() const { return subject_time_; }
+
+  const TimelineRate& rate() const { return rate_; }
+
+  uint32_t reference_delta() const { return rate_.reference_delta(); }
+
+  uint32_t subject_delta() const { return rate_.subject_delta(); }
+
+ private:
+  int64_t reference_time_;
+  int64_t subject_time_;
+  TimelineRate rate_;  // subject_delta / reference_delta
+};
+
+// Tests two timeline functions for equality. Equality requires equal basis
+// values.
+inline bool operator==(const TimelineFunction& a, const TimelineFunction& b) {
+  return a.reference_time() == b.reference_time() &&
+         a.subject_time() == b.subject_time() && a.rate() == b.rate();
+}
+
+// Tests two timeline functions for inequality. Equality requires equal basis
+// values.
+inline bool operator!=(const TimelineFunction& a, const TimelineFunction& b) {
+  return !(a == b);
+}
+
+// Composes two timeline functions B->C and A->B producing A->C. DCHECKs on
+// loss of precision.
+inline TimelineFunction operator*(const TimelineFunction& bc,
+                                  const TimelineFunction& ab) {
+  return TimelineFunction::Compose(bc, ab);
+}
+
+}  // namespace media
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_MEDIA_COMMON_CPP_TIMELINE_FUNCTION_H_
diff --git a/mojo/services/media/common/cpp/timeline_rate.cc b/mojo/services/media/common/cpp/timeline_rate.cc
new file mode 100644
index 0000000..a450e10
--- /dev/null
+++ b/mojo/services/media/common/cpp/timeline_rate.cc
@@ -0,0 +1,233 @@
+// 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 <limits>
+#include <utility>
+
+#include "mojo/public/cpp/environment/logging.h"
+#include "mojo/services/media/common/cpp/timeline_rate.h"
+
+namespace mojo {
+namespace media {
+
+namespace {
+
+// Calculates the greatest common denominator (factor) of two values.
+template <typename T>
+T BinaryGcd(T a, T b) {
+  if (a == 0) {
+    return b;
+  }
+
+  if (b == 0) {
+    return a;
+  }
+
+  // Remove and count the common factors of 2.
+  uint8_t twos;
+  for (twos = 0; ((a | b) & 1) == 0; ++twos) {
+    a >>= 1;
+    b >>= 1;
+  }
+
+  // Get rid of the non-common factors of 2 in a. a is non-zero, so this
+  // terminates.
+  while ((a & 1) == 0) {
+    a >>= 1;
+  }
+
+  do {
+    // Get rid of the non-common factors of 2 in b. b is non-zero, so this
+    // terminates.
+    while ((b & 1) == 0) {
+      b >>= 1;
+    }
+
+    // Apply the Euclid subtraction method.
+    if (a > b) {
+      std::swap(a, b);
+    }
+
+    b = b - a;
+  } while (b != 0);
+
+  // Multiply in the common factors of two.
+  return a << twos;
+}
+
+// Reduces the ratio of *numerator and *denominator.
+template <typename T>
+void ReduceRatio(T* numerator, T* denominator) {
+  MOJO_DCHECK(numerator != nullptr);
+  MOJO_DCHECK(denominator != nullptr);
+  MOJO_DCHECK(*denominator != 0);
+
+  T gcd = BinaryGcd(*numerator, *denominator);
+
+  if (gcd == 0) {
+    *denominator = 1;
+    return;
+  }
+
+  if (gcd == 1) {
+    return;
+  }
+
+  *numerator = *numerator / gcd;
+  *denominator = *denominator / gcd;
+}
+
+template void ReduceRatio<uint64_t>(uint64_t* numerator, uint64_t* denominator);
+template void ReduceRatio<uint32_t>(uint32_t* numerator, uint32_t* denominator);
+
+// Scales a uint64_t value by the ratio of two uint32_t values. If round_up is
+// true, the result is rounded up rather than down. overflow is set to indicate
+// overflow.
+uint64_t ScaleUInt64(uint64_t value,
+                     uint32_t subject_delta,
+                     uint32_t reference_delta,
+                     bool round_up,
+                     bool* overflow) {
+  MOJO_DCHECK(reference_delta != 0u);
+  MOJO_DCHECK(overflow != nullptr);
+
+  constexpr uint64_t kLow32Bits = 0xffffffffu;
+  constexpr uint64_t kHigh32Bits = kLow32Bits << 32u;
+
+  // high and low are the product of the subject_delta and the high and low
+  // halves
+  // (respectively) of value.
+  uint64_t high = subject_delta * (value >> 32u);
+  uint64_t low = subject_delta * (value & kLow32Bits);
+  // Ignoring overflow and remainder, the result we want is:
+  // ((high << 32) + low) / reference_delta.
+
+  // Move the high end of low into the low end of high.
+  high += low >> 32u;
+  low = low & kLow32Bits;
+  // Ignoring overflow and remainder, the result we want is still:
+  // ((high << 32) + low) / reference_delta.
+
+  // When we divide high by reference_delta, there'll be a remainder. Make
+  // that the high end of low, which is currently all zeroes.
+  low |= (high % reference_delta) << 32u;
+
+  // Determine if we need to round up when we're done:
+  round_up = round_up && (low % reference_delta) != 0;
+
+  // Do the division.
+  high /= reference_delta;
+  low /= reference_delta;
+
+  // If high's top 32 bits aren't all zero, we have overflow.
+  if (high & kHigh32Bits) {
+    *overflow = true;
+    return 0;
+  }
+
+  uint64_t result = (high << 32u) | low;
+  if (round_up) {
+    if (result == std::numeric_limits<int64_t>::max()) {
+      *overflow = true;
+      return 0;
+    }
+    ++result;
+  }
+
+  *overflow = false;
+  return result;
+}
+
+}  // namespace
+
+// static
+void TimelineRate::Reduce(uint32_t* subject_delta, uint32_t* reference_delta) {
+  ReduceRatio(subject_delta, reference_delta);
+}
+
+// static
+void TimelineRate::Product(uint32_t a_subject_delta,
+                           uint32_t a_reference_delta,
+                           uint32_t b_subject_delta,
+                           uint32_t b_reference_delta,
+                           uint32_t* product_subject_delta,
+                           uint32_t* product_reference_delta,
+                           bool exact) {
+  MOJO_DCHECK(a_reference_delta != 0);
+  MOJO_DCHECK(b_reference_delta != 0);
+  MOJO_DCHECK(product_subject_delta != nullptr);
+  MOJO_DCHECK(product_reference_delta != nullptr);
+
+  uint64_t subject_delta =
+      static_cast<uint64_t>(a_subject_delta) * b_subject_delta;
+  uint64_t reference_delta =
+      static_cast<uint64_t>(a_reference_delta) * b_reference_delta;
+
+  ReduceRatio(&subject_delta, &reference_delta);
+
+  if (subject_delta > std::numeric_limits<uint32_t>::max() ||
+      reference_delta > std::numeric_limits<uint32_t>::max()) {
+    MOJO_DCHECK(!exact);
+
+    do {
+      subject_delta >>= 1;
+      reference_delta >>= 1;
+    } while (subject_delta > std::numeric_limits<uint32_t>::max() ||
+             reference_delta > std::numeric_limits<uint32_t>::max());
+
+    if (reference_delta == 0) {
+      // Product is larger than we can represent. Return the largest value we
+      // can represent.
+      *product_subject_delta = std::numeric_limits<uint32_t>::max();
+      *product_reference_delta = 1;
+      return;
+    }
+  }
+
+  *product_subject_delta = static_cast<uint32_t>(subject_delta);
+  *product_reference_delta = static_cast<uint32_t>(reference_delta);
+}
+
+// static
+int64_t TimelineRate::Scale(int64_t value,
+                            uint32_t subject_delta,
+                            uint32_t reference_delta) {
+  static constexpr uint64_t abs_of_min_int64 =
+      static_cast<uint64_t>(std::numeric_limits<int64_t>::max()) + 1;
+
+  MOJO_DCHECK(reference_delta != 0u);
+
+  bool overflow;
+
+  uint64_t abs_result;
+
+  if (value >= 0) {
+    abs_result = ScaleUInt64(static_cast<uint64_t>(value), subject_delta,
+                             reference_delta, false, &overflow);
+  } else if (value == std::numeric_limits<int64_t>::min()) {
+    abs_result = ScaleUInt64(abs_of_min_int64, subject_delta, reference_delta,
+                             true, &overflow);
+  } else {
+    abs_result = ScaleUInt64(static_cast<uint64_t>(-value), subject_delta,
+                             reference_delta, true, &overflow);
+  }
+
+  if (overflow) {
+    return TimelineRate::kOverflow;
+  }
+
+  // Make sure we won't overflow when we cast to int64_t.
+  if (abs_result > static_cast<uint64_t>(std::numeric_limits<int64_t>::max())) {
+    if (value < 0 && abs_result == abs_of_min_int64) {
+      return std::numeric_limits<int64_t>::min();
+    }
+    return TimelineRate::kOverflow;
+  }
+
+  return value >= 0 ? static_cast<int64_t>(abs_result)
+                    : -static_cast<int64_t>(abs_result);
+}
+
+}  // namespace media
+}  // namespace mojo
diff --git a/mojo/services/media/common/cpp/timeline_rate.h b/mojo/services/media/common/cpp/timeline_rate.h
new file mode 100644
index 0000000..7aa0216
--- /dev/null
+++ b/mojo/services/media/common/cpp/timeline_rate.h
@@ -0,0 +1,128 @@
+// 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_RATE_H_
+#define MOJO_SERVICES_MEDIA_COMMON_CPP_TIMELINE_RATE_H_
+
+#include <stdint.h>
+
+#include <limits>
+
+#include "mojo/public/cpp/environment/logging.h"
+
+namespace mojo {
+namespace media {
+
+// TODO(dalesat): Consider always allowing inexact results.
+
+// Expresses the relative rate of a timeline as the ratio between two uint32_t
+// values subject_delta / reference_delta. "subject" refers to the timeline
+// whose rate is being represented, and "reference" refers to the timeline
+// relative to which the rate is expressed.
+class TimelineRate {
+ public:
+  // Used to indicate overflow of scaling operations.
+  static constexpr int64_t kOverflow = std::numeric_limits<int64_t>::max();
+
+  // Reduces the ratio of *subject_delta and *reference_delta.
+  static void Reduce(uint32_t* subject_delta, uint32_t* reference_delta);
+
+  // Produces the product of the rates. If exact is true, DCHECKs on loss of
+  // precision.
+  static void Product(uint32_t a_subject_delta,
+                      uint32_t a_reference_delta,
+                      uint32_t b_subject_delta,
+                      uint32_t b_reference_delta,
+                      uint32_t* product_subject_delta,
+                      uint32_t* product_reference_delta,
+                      bool exact = true);
+
+  // Produces the product of the rates and the int64_t as an int64_t. Returns
+  // kOverflow on overflow.
+  static int64_t Scale(int64_t value,
+                       uint32_t subject_delta,
+                       uint32_t reference_delta);
+
+  // Returns the product of the rates. If exact is true, DCHECKs on loss of
+  // precision.
+  static TimelineRate Product(const TimelineRate& a,
+                              const TimelineRate& b,
+                              bool exact = true) {
+    uint32_t result_subject_delta;
+    uint32_t result_reference_delta;
+    Product(a.subject_delta(), a.reference_delta(), b.subject_delta(),
+            b.reference_delta(), &result_subject_delta, &result_reference_delta,
+            exact);
+    return TimelineRate(result_subject_delta, result_reference_delta);
+  }
+
+  TimelineRate() : subject_delta_(0), reference_delta_(1) {}
+
+  explicit TimelineRate(uint32_t subject_delta)
+      : subject_delta_(subject_delta), reference_delta_(1) {}
+
+  TimelineRate(uint32_t subject_delta, uint32_t reference_delta)
+      : subject_delta_(subject_delta), reference_delta_(reference_delta) {
+    MOJO_DCHECK(reference_delta != 0);
+    Reduce(&subject_delta_, &reference_delta_);
+  }
+
+  // Returns the inverse of the rate. DCHECKs if the subject_delta of this
+  // rate is zero.
+  TimelineRate Inverse() const {
+    MOJO_DCHECK(subject_delta_ != 0);
+    return TimelineRate(reference_delta_, subject_delta_);
+  }
+
+  // Scales the value by this rate. Returns kOverflow on overflow.
+  int64_t Scale(int64_t value) const {
+    return Scale(value, subject_delta_, reference_delta_);
+  }
+
+  uint32_t subject_delta() const { return subject_delta_; }
+  uint32_t reference_delta() const { return reference_delta_; }
+
+ private:
+  uint32_t subject_delta_;
+  uint32_t reference_delta_;
+};
+
+// Tests two rates for equality.
+inline bool operator==(const TimelineRate& a, const TimelineRate& b) {
+  return a.subject_delta() == b.subject_delta() &&
+         a.reference_delta() == b.reference_delta();
+}
+
+// Tests two rates for inequality.
+inline bool operator!=(const TimelineRate& a, const TimelineRate& b) {
+  return !(a == b);
+}
+
+// Returns the product of the two rates. DCHECKs on loss of precision.
+inline TimelineRate operator*(const TimelineRate& a, const TimelineRate& b) {
+  return TimelineRate::Product(a, b);
+}
+
+// Returns the product of the rate and the int64_t. Returns kOverflow on
+// overflow.
+inline int64_t operator*(const TimelineRate& a, int64_t b) {
+  return a.Scale(b);
+}
+
+// Returns the product of the rate and the int64_t. Returns kOverflow on
+// overflow.
+inline int64_t operator*(int64_t a, const TimelineRate& b) {
+  return b.Scale(a);
+}
+
+// Returns the the int64_t divided by the rate. Returns kOverflow on
+// overflow.
+inline int64_t operator/(int64_t a, const TimelineRate& b) {
+  return b.Inverse().Scale(a);
+}
+
+}  // namespace media
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_MEDIA_COMMON_CPP_TIMELINE_RATE_H_
diff --git a/mojo/services/media/common/interfaces/timelines.mojom b/mojo/services/media/common/interfaces/timelines.mojom
new file mode 100644
index 0000000..f79697d
--- /dev/null
+++ b/mojo/services/media/common/interfaces/timelines.mojom
@@ -0,0 +1,57 @@
+// 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.
+
+[DartPackage="mojo_services"]
+module mojo;
+
+// TODO(dalesat): Move out of media to somewhere more generic.
+
+// Represents the relationship between and subject timeline and a reference
+// timeline.
+//
+// To translate a reference timeline value r to the subject timeline, apply
+// the following formula:
+//
+//   (r - reference_time) * subject_delta / reference_delta + subject_time
+//
+// To translate a subject timeline value s to the reference timeline, apply
+// this formula provided subject_delta isn't zero:
+//
+//   (s - subject_time) * reference_delta / subject_delta + reference_time
+//
+struct TimelineTransform {
+  // A value from the reference timeline that correlates to timeline_time.
+  int64 reference_time = 0;
+
+  // A value from the subject timeline that correlates to reference_time.
+  int64 subject_time = 0;
+
+  // The change in the reference timeline corresponding to timeline_delta.
+  // Cannnot be zero.
+  uint32 reference_delta = 1;
+
+  // The change in the subject timeline corresponding to reference_delta.
+  uint32 subject_delta = 0;
+};
+
+// 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
+  // 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 new transform reference_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 reference_delta,
+      uint32 subject_delta,
+      int64 effective_reference_time,
+      int64 effective_subject_time) => (bool completed);
+};
diff --git a/services/media/common/BUILD.gn b/services/media/common/BUILD.gn
index dc8e8b6..bc1e1b2 100644
--- a/services/media/common/BUILD.gn
+++ b/services/media/common/BUILD.gn
@@ -26,9 +26,9 @@
   testonly = true
 
   sources = [
-    "test/linear_function_test.cc",
-    "test/ratio_test.cc",
     "test/test_base.h",
+    "test/timeline_function_test.cc",
+    "test/timeline_rate_test.cc",
   ]
 
   deps = [
diff --git a/services/media/common/test/linear_function_test.cc b/services/media/common/test/linear_function_test.cc
deleted file mode 100644
index b2ab53d..0000000
--- a/services/media/common/test/linear_function_test.cc
+++ /dev/null
@@ -1,226 +0,0 @@
-// 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 "mojo/services/media/common/cpp/linear_function.h"
-#include "services/media/common/test/test_base.h"
-
-namespace mojo {
-namespace media {
-namespace {
-
-class LinearFunctionTest : public TestBase {
- public:
-  // Verifies that a LinearFunction instantiated in three different ways with
-  // the given arguments has the expected properties.
-  void VerifyBasics(int64_t domain_basis,
-                    int64_t range_basis,
-                    uint32_t domain_delta,
-                    uint32_t range_delta) {
-    LinearFunction under_test_1(domain_basis, range_basis, domain_delta,
-                                range_delta);
-    VerifyBasics(under_test_1, domain_basis, range_basis, domain_delta,
-                 range_delta);
-
-    LinearFunction under_test_2(domain_basis, range_basis,
-                                Ratio(range_delta, domain_delta));
-    VerifyBasics(under_test_2, domain_basis, range_basis, domain_delta,
-                 range_delta);
-
-    LinearFunction under_test_3(Ratio(range_delta, domain_delta));
-    VerifyBasics(under_test_3, 0, 0, domain_delta, range_delta);
-
-    EXPECT_EQ(under_test_1, under_test_1);
-    EXPECT_EQ(under_test_1, under_test_2);
-    EXPECT_EQ(under_test_2, under_test_1);
-    EXPECT_EQ(under_test_2, under_test_2);
-
-    if (domain_basis == 0 && range_basis == 0) {
-      EXPECT_EQ(under_test_1, under_test_3);
-      EXPECT_EQ(under_test_2, under_test_3);
-      EXPECT_EQ(under_test_3, under_test_1);
-      EXPECT_EQ(under_test_3, under_test_2);
-    } else {
-      EXPECT_NE(under_test_1, under_test_3);
-      EXPECT_NE(under_test_2, under_test_3);
-      EXPECT_NE(under_test_3, under_test_1);
-      EXPECT_NE(under_test_3, under_test_2);
-    }
-  }
-
-  // Verifies that the given LinearFunction instantiated with the given
-  // arguments has the expected properties.
-  void VerifyBasics(const LinearFunction& under_test,
-                    int64_t domain_basis,
-                    int64_t range_basis,
-                    uint32_t domain_delta,
-                    uint32_t range_delta) {
-    Ratio::Reduce(&range_delta, &domain_delta);
-    EXPECT_EQ(domain_basis, under_test.domain_basis());
-    EXPECT_EQ(range_basis, under_test.range_basis());
-    EXPECT_EQ(domain_delta, under_test.domain_delta());
-    EXPECT_EQ(range_delta, under_test.range_delta());
-    EXPECT_EQ(domain_delta, under_test.slope().denominator());
-    EXPECT_EQ(range_delta, under_test.slope().numerator());
-  }
-
-  // Verifies that the inverse of a LinearFunction instantiated in three
-  // different ways with the given arguments has the expected properties.
-  void VerifyInverse(int64_t domain_basis,
-                     int64_t range_basis,
-                     uint32_t domain_delta,
-                     uint32_t range_delta) {
-    LinearFunction under_test_1(domain_basis, range_basis, domain_delta,
-                                range_delta);
-    VerifyBasics(under_test_1.Inverse(), range_basis, domain_basis, range_delta,
-                 domain_delta);
-
-    LinearFunction under_test_2(domain_basis, range_basis,
-                                Ratio(range_delta, domain_delta));
-    VerifyBasics(under_test_2.Inverse(), range_basis, domain_basis, range_delta,
-                 domain_delta);
-
-    LinearFunction under_test_3(Ratio(range_delta, domain_delta));
-    VerifyBasics(under_test_3.Inverse(), 0, 0, range_delta, domain_delta);
-  }
-
-  // Verifies that LinearFunction::Apply, in its various forms, works as
-  // expected for the given arguments.
-  void VerifyApply(int64_t domain_basis,
-                   int64_t range_basis,
-                   uint32_t domain_delta,
-                   uint32_t range_delta,
-                   int64_t domain_input,
-                   int64_t expected_result) {
-    // Verify the static method.
-    EXPECT_EQ(
-        expected_result,
-        LinearFunction::Apply(domain_basis, range_basis,
-                              Ratio(range_delta, domain_delta), domain_input));
-
-    // Verify the instance method.
-    LinearFunction under_test(domain_basis, range_basis, domain_delta,
-                              range_delta);
-    EXPECT_EQ(expected_result, under_test.Apply(domain_input));
-
-    // Verify the operator.
-    EXPECT_EQ(expected_result, under_test(domain_input));
-  }
-
-  // Verifies that LinearFunction::ApplyInverse, in its various forms, works as
-  // expected for the given arguments.
-  void VerifyApplyInverse(int64_t domain_basis,
-                          int64_t range_basis,
-                          uint32_t domain_delta,
-                          uint32_t range_delta,
-                          int64_t range_input,
-                          int64_t expected_result) {
-    // Verify the static method.
-    EXPECT_EQ(expected_result,
-              LinearFunction::ApplyInverse(domain_basis, range_basis,
-                                           Ratio(range_delta, domain_delta),
-                                           range_input));
-
-    // Verify the instance method.
-    LinearFunction under_test(domain_basis, range_basis, domain_delta,
-                              range_delta);
-    EXPECT_EQ(expected_result, under_test.ApplyInverse(range_input));
-  }
-
-  // Verifies that LinearFunction::Compose works as expected with the given
-  // inputs.
-  void VerifyCompose(const LinearFunction& a,
-                     const LinearFunction& b,
-                     bool exact,
-                     const LinearFunction& expected_result) {
-    // Verify the static method.
-    EXPECT_EQ(expected_result, LinearFunction::Compose(a, b, exact));
-  }
-};
-
-// Tests LinearFunction basics for various instantiation arguments.
-TEST_F(LinearFunctionTest, Basics) {
-  VerifyBasics(0, 0, 1, 0);
-  VerifyBasics(0, 0, 1, 1);
-  VerifyBasics(1, 1, 10, 10);
-  VerifyBasics(1234, 5678, 4321, 8765);
-  VerifyBasics(-1234, 5678, 4321, 8765);
-  VerifyBasics(-1234, -5678, 4321, 8765);
-  VerifyBasics(1234, -5678, 4321, 8765);
-}
-
-// Tests LinearFunction::Inverse.
-TEST_F(LinearFunctionTest, Inverse) {
-  VerifyInverse(0, 0, 1, 1);
-  VerifyInverse(1, 1, 10, 10);
-  VerifyInverse(1234, 5678, 4321, 8765);
-  VerifyInverse(-1234, 5678, 4321, 8765);
-  VerifyInverse(-1234, -5678, 4321, 8765);
-  VerifyInverse(1234, -5678, 4321, 8765);
-}
-
-// Tests LinearFunction::Apply in its variations.
-TEST_F(LinearFunctionTest, Apply) {
-  VerifyApply(0, 0, 1, 0, 0, 0);
-  VerifyApply(0, 0, 1, 0, 1000, 0);
-  VerifyApply(0, 1234, 1, 0, 0, 1234);
-  VerifyApply(0, 1234, 1, 0, 1000, 1234);
-  VerifyApply(0, 1234, 1, 0, -1000, 1234);
-  VerifyApply(0, -1234, 1, 0, 0, -1234);
-  VerifyApply(0, -1234, 1, 0, 1000, -1234);
-  VerifyApply(0, -1234, 1, 0, -1000, -1234);
-  VerifyApply(0, 0, 1, 1, 0, 0);
-  VerifyApply(0, 0, 1, 1, 1000, 1000);
-  VerifyApply(0, 1234, 1, 1, 0, 1234);
-  VerifyApply(0, 1234, 1, 1, 1000, 2234);
-  VerifyApply(0, 1234, 1, 1, -1000, 234);
-  VerifyApply(0, -1234, 1, 1, 0, -1234);
-  VerifyApply(0, -1234, 1, 1, 1000, -234);
-  VerifyApply(0, -1234, 1, 1, -1000, -2234);
-  VerifyApply(10, 0, 1, 0, 0, 0);
-  VerifyApply(10, 0, 1, 1, 0, -10);
-  VerifyApply(-10, 0, 1, 0, 0, 0);
-  VerifyApply(-10, 0, 1, 1, 0, 10);
-  VerifyApply(0, 1234, 2, 1, 0, 1234);
-  VerifyApply(0, 1234, 2, 1, 1234, 1234 + 1234 / 2);
-  VerifyApply(0, 1234, 1, 2, 1234, 1234 + 1234 * 2);
-}
-
-// Tests LinearFunction::Apply in its variations.
-TEST_F(LinearFunctionTest, ApplyInverse) {
-  VerifyApplyInverse(0, 0, 1, 1, 0, 0);
-  VerifyApplyInverse(0, 0, 1, 1, 1000, 1000);
-  VerifyApplyInverse(0, 1234, 1, 1, 1234, 0);
-  VerifyApplyInverse(0, 1234, 1, 1, 2234, 1000);
-  VerifyApplyInverse(0, 1234, 1, 1, 234, -1000);
-  VerifyApplyInverse(0, -1234, 1, 1, -1234, 0);
-  VerifyApplyInverse(0, -1234, 1, 1, -234, 1000);
-  VerifyApplyInverse(0, -1234, 1, 1, -2234, -1000);
-  VerifyApplyInverse(10, 0, 1, 1, -10, 0);
-  VerifyApplyInverse(-10, 0, 1, 1, 10, 0);
-  VerifyApplyInverse(0, 1234, 2, 1, 1234, 0);
-  VerifyApplyInverse(0, 1234, 2, 1, 1234 + 1234 / 2, 1234);
-  VerifyApplyInverse(0, 1234, 1, 2, 1234 + 1234 * 2, 1234);
-}
-
-// Tests LinearFunction::Compose.
-TEST_F(LinearFunctionTest, Compose) {
-  VerifyCompose(LinearFunction(0, 0, 1, 0), LinearFunction(0, 0, 1, 0), true,
-                LinearFunction(0, 0, 1, 0));
-  VerifyCompose(LinearFunction(0, 0, 1, 1), LinearFunction(0, 0, 1, 1), true,
-                LinearFunction(0, 0, 1, 1));
-  VerifyCompose(LinearFunction(1, 0, 1, 1), LinearFunction(0, 0, 1, 1), true,
-                LinearFunction(0, -1, 1, 1));
-  VerifyCompose(LinearFunction(10, 10, 1, 1), LinearFunction(0, 0, 1, 1), true,
-                LinearFunction(0, 0, 1, 1));
-  VerifyCompose(LinearFunction(0, 0, 1, 2), LinearFunction(0, 0, 1, 2), true,
-                LinearFunction(0, 0, 1, 4));
-  VerifyCompose(LinearFunction(0, 0, 2, 1), LinearFunction(0, 0, 2, 1), true,
-                LinearFunction(0, 0, 4, 1));
-  VerifyCompose(LinearFunction(0, 0, 2, 1), LinearFunction(0, 0, 1, 2), true,
-                LinearFunction(0, 0, 1, 1));
-}
-
-}  // namespace
-}  // namespace media
-}  // namespace mojo
diff --git a/services/media/common/test/ratio_test.cc b/services/media/common/test/ratio_test.cc
deleted file mode 100644
index 8a0ce3e..0000000
--- a/services/media/common/test/ratio_test.cc
+++ /dev/null
@@ -1,181 +0,0 @@
-// 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 <limits>
-
-#include "mojo/services/media/common/cpp/ratio.h"
-#include "services/media/common/test/test_base.h"
-
-namespace mojo {
-namespace media {
-namespace {
-
-class RatioTest : public TestBase {
-  static uint32_t gcd(uint32_t a, uint32_t b) {
-    while (b != 0) {
-      uint32_t t = a;
-      a = b;
-      b = t % b;
-    }
-    return a;
-  }
-
- public:
-  // Verifies Ratio::Reduce and the constructor, ensuring that the ratio
-  // numerator * common_factor / denominator * common_factor is reduced to
-  // numerator / denominator. numerator and denominator need to be relatively
-  // prime for this to work.
-  void VerifyReduce(uint32_t numerator,
-                    uint32_t denominator,
-                    uint32_t common_factor) {
-    // Make sure numerator and denominator are relatively prime.
-    EXPECT_EQ(1u, gcd(numerator, denominator));
-
-    uint32_t test_numerator = numerator * common_factor;
-    uint32_t test_denominator = denominator * common_factor;
-
-    // Make sure the constructor reduces.
-    Ratio ratio(test_numerator, test_denominator);
-    EXPECT_EQ(numerator, ratio.numerator());
-    EXPECT_EQ(denominator, ratio.denominator());
-
-    // Test the static method.
-    Ratio::Reduce(&test_numerator, &test_denominator);
-    EXPECT_EQ(numerator, test_numerator);
-    EXPECT_EQ(denominator, test_denominator);
-  }
-
-  // Verifies the Ratio::Scale methods by scaling value by numerator /
-  // denominator and verifying the result.
-  void VerifyScale(int64_t value,
-                   uint32_t numerator,
-                   uint32_t denominator,
-                   int64_t result) {
-    // Test the instance method.
-    EXPECT_EQ(result, Ratio(numerator, denominator).Scale(value));
-
-    // Test the static method.
-    EXPECT_EQ(result, Ratio::Scale(value, numerator, denominator));
-
-    // Test the operators.
-    EXPECT_EQ(result, value * Ratio(numerator, denominator));
-    EXPECT_EQ(result, Ratio(numerator, denominator) * value);
-    if (numerator != 0) {
-      EXPECT_EQ(result, value / Ratio(denominator, numerator));
-    }
-  }
-
-  // Verifies the Ratio::Product methods by multiplying the given a and b
-  // ratios and checking the result against the expected ratio.
-  void VerifyProduct(uint32_t a_numerator,
-                     uint32_t a_denominator,
-                     uint32_t b_numerator,
-                     uint32_t b_denominator,
-                     uint32_t expected_numerator,
-                     uint32_t expected_denominator,
-                     bool exact) {
-    // Test the first static method.
-    uint32_t actual_numerator;
-    uint32_t actual_denominator;
-    Ratio::Product(a_numerator, a_denominator, b_numerator, b_denominator,
-                   &actual_numerator, &actual_denominator, exact);
-    EXPECT_EQ(expected_numerator, actual_numerator);
-    EXPECT_EQ(expected_denominator, actual_denominator);
-
-    // Test the second static method.
-    EXPECT_EQ(Ratio(expected_numerator, expected_denominator),
-              Ratio::Product(Ratio(a_numerator, a_denominator),
-                             Ratio(b_numerator, b_denominator), exact));
-
-    // Test the operator
-    if (exact) {
-      EXPECT_EQ(Ratio(expected_numerator, expected_denominator),
-                Ratio(a_numerator, a_denominator) *
-                    Ratio(b_numerator, b_denominator));
-    }
-  }
-
-  // Verifies the Ration::Inverse method using the given ratio.
-  void VerifyInverse(uint32_t numerator, uint32_t denominator) {
-    Ratio ratio(numerator, denominator);
-    Ratio inverse(ratio.Inverse());
-    EXPECT_EQ(ratio.denominator(), inverse.numerator());
-    EXPECT_EQ(ratio.numerator(), inverse.denominator());
-  }
-};
-
-// Tests Ratio::Reduce and that the Ratio constructor reduces.
-TEST_F(RatioTest, Reduce) {
-  VerifyReduce(0, 1, 1);
-  VerifyReduce(1, 1, 1);
-  VerifyReduce(1234, 1, 1);
-  VerifyReduce(1, 1234, 14);
-  VerifyReduce(1, 1, 1234);
-  VerifyReduce(10, 1, 1234);
-  VerifyReduce(1, 10, 1234);
-  VerifyReduce(49, 81, 1);
-  VerifyReduce(49, 81, 10);
-  VerifyReduce(49, 81, 100);
-  VerifyReduce(1, 8, 65536);
-  VerifyReduce(8, 1, 65536);
-}
-
-// Tests Ratio::Scale, static, instance and operator versions.
-TEST_F(RatioTest, Scale) {
-  const int64_t int64_min = std::numeric_limits<int64_t>::min();
-  VerifyScale(0, 0, 1, 0);
-  VerifyScale(1, 0, 1, 0);
-  VerifyScale(0, 1, 1, 0);
-  VerifyScale(1, 1, 1, 1);
-  VerifyScale(1, 2, 1, 2);
-  VerifyScale(1, 1, 2, 0);
-  VerifyScale(-1, 1, 2, -1);
-  VerifyScale(1000, 1, 2, 500);
-  VerifyScale(1001, 1, 2, 500);
-  VerifyScale(-1000, 1, 2, -500);
-  VerifyScale(-1001, 1, 2, -501);
-  VerifyScale(1000, 2, 1, 2000);
-  VerifyScale(1001, 2, 1, 2002);
-  VerifyScale(-1000, 2, 1, -2000);
-  VerifyScale(-1001, 2, 1, -2002);
-  VerifyScale(1ll << 32, 1, 1, 1ll << 32);
-  VerifyScale(1ll << 32, 1, 2, 1ll << 31);
-  VerifyScale(1ll << 32, 2, 1, 1ll << 33);
-  VerifyScale(1234ll << 30, 1, 1, 1234ll << 30);
-  VerifyScale(1234ll << 30, 1, 2, 1234ll << 29);
-  VerifyScale(1234ll << 30, 2, 1, 1234ll << 31);
-  VerifyScale(1234ll << 30, 1 << 31, 1, Ratio::kOverflow);
-  VerifyScale(1234ll << 30, 1ll << 31, (1ll << 31) - 2,
-              (1234ll << 30) + 1234ll);
-  VerifyScale(int64_min, 1, 1, int64_min);
-  VerifyScale(int64_min, 1, 2, int64_min / 2);
-  VerifyScale(int64_min / 2, 2, 1, int64_min);
-  VerifyScale(int64_min, 1000001, 1000000, Ratio::kOverflow);
-}
-
-// Tests Ratio::Product, static and operator versions.
-TEST_F(RatioTest, Product) {
-  VerifyProduct(0, 1, 0, 1, 0, 1, true);
-  VerifyProduct(1, 1, 1, 1, 1, 1, true);
-  VerifyProduct(10, 1, 1, 10, 1, 1, true);
-  VerifyProduct(4321, 1234, 617, 4321, 1, 2, true);
-  VerifyProduct(1234, 4321, 4321, 617, 2, 1, true);
-  VerifyProduct(1ll << 31, (1ll << 31) - 1, (1ll << 31) - 1, 1ll << 31, 1, 1,
-                true);
-  VerifyProduct(1ll << 31, (1ll << 31) - 1, (1ll << 31) - 2, 1ll << 31,
-                0x7ffffffe, 0x7fffffff, false);
-}
-
-// Tests Ratio::Inverse.
-TEST_F(RatioTest, Inverse) {
-  VerifyInverse(1, 1);
-  VerifyInverse(2, 1);
-  VerifyInverse(1, 2);
-  VerifyInverse(1000000, 1234);
-  VerifyInverse(1234, 1000000);
-}
-
-}  // namespace
-}  // namespace media
-}  // namespace mojo
diff --git a/services/media/common/test/timeline_function_test.cc b/services/media/common/test/timeline_function_test.cc
new file mode 100644
index 0000000..8d3adb0
--- /dev/null
+++ b/services/media/common/test/timeline_function_test.cc
@@ -0,0 +1,227 @@
+// 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 "mojo/services/media/common/cpp/timeline_function.h"
+#include "services/media/common/test/test_base.h"
+
+namespace mojo {
+namespace media {
+namespace {
+
+class TimelineFunctionTest : public TestBase {
+ public:
+  // Verifies that a TimelineFunction instantiated in three different ways with
+  // the given arguments has the expected properties.
+  void VerifyBasics(int64_t reference_time,
+                    int64_t subject_time,
+                    uint32_t reference_delta,
+                    uint32_t subject_delta) {
+    TimelineFunction under_test_1(reference_time, subject_time, reference_delta,
+                                  subject_delta);
+    VerifyBasics(under_test_1, reference_time, subject_time, reference_delta,
+                 subject_delta);
+
+    TimelineFunction under_test_2(reference_time, subject_time,
+                                  TimelineRate(subject_delta, reference_delta));
+    VerifyBasics(under_test_2, reference_time, subject_time, reference_delta,
+                 subject_delta);
+
+    TimelineFunction under_test_3(TimelineRate(subject_delta, reference_delta));
+    VerifyBasics(under_test_3, 0, 0, reference_delta, subject_delta);
+
+    EXPECT_EQ(under_test_1, under_test_1);
+    EXPECT_EQ(under_test_1, under_test_2);
+    EXPECT_EQ(under_test_2, under_test_1);
+    EXPECT_EQ(under_test_2, under_test_2);
+
+    if (reference_time == 0 && subject_time == 0) {
+      EXPECT_EQ(under_test_1, under_test_3);
+      EXPECT_EQ(under_test_2, under_test_3);
+      EXPECT_EQ(under_test_3, under_test_1);
+      EXPECT_EQ(under_test_3, under_test_2);
+    } else {
+      EXPECT_NE(under_test_1, under_test_3);
+      EXPECT_NE(under_test_2, under_test_3);
+      EXPECT_NE(under_test_3, under_test_1);
+      EXPECT_NE(under_test_3, under_test_2);
+    }
+  }
+
+  // Verifies that the given TimelineFunction instantiated with the given
+  // arguments has the expected properties.
+  void VerifyBasics(const TimelineFunction& under_test,
+                    int64_t reference_time,
+                    int64_t subject_time,
+                    uint32_t reference_delta,
+                    uint32_t subject_delta) {
+    TimelineRate::Reduce(&subject_delta, &reference_delta);
+    EXPECT_EQ(reference_time, under_test.reference_time());
+    EXPECT_EQ(subject_time, under_test.subject_time());
+    EXPECT_EQ(reference_delta, under_test.reference_delta());
+    EXPECT_EQ(subject_delta, under_test.subject_delta());
+    EXPECT_EQ(reference_delta, under_test.rate().reference_delta());
+    EXPECT_EQ(subject_delta, under_test.rate().subject_delta());
+  }
+
+  // Verifies that the inverse of a TimelineFunction instantiated in three
+  // different ways with the given arguments has the expected properties.
+  void VerifyInverse(int64_t reference_time,
+                     int64_t subject_time,
+                     uint32_t reference_delta,
+                     uint32_t subject_delta) {
+    TimelineFunction under_test_1(reference_time, subject_time, reference_delta,
+                                  subject_delta);
+    VerifyBasics(under_test_1.Inverse(), subject_time, reference_time,
+                 subject_delta, reference_delta);
+
+    TimelineFunction under_test_2(reference_time, subject_time,
+                                  TimelineRate(subject_delta, reference_delta));
+    VerifyBasics(under_test_2.Inverse(), subject_time, reference_time,
+                 subject_delta, reference_delta);
+
+    TimelineFunction under_test_3(TimelineRate(subject_delta, reference_delta));
+    VerifyBasics(under_test_3.Inverse(), 0, 0, subject_delta, reference_delta);
+  }
+
+  // Verifies that TimelineFunction::Apply, in its various forms, works as
+  // expected for the given arguments.
+  void VerifyApply(int64_t reference_time,
+                   int64_t subject_time,
+                   uint32_t reference_delta,
+                   uint32_t subject_delta,
+                   int64_t reference_input,
+                   int64_t expected_result) {
+    // Verify the static method.
+    EXPECT_EQ(expected_result, TimelineFunction::Apply(
+                                   reference_time, subject_time,
+                                   TimelineRate(subject_delta, reference_delta),
+                                   reference_input));
+
+    // Verify the instance method.
+    TimelineFunction under_test(reference_time, subject_time, reference_delta,
+                                subject_delta);
+    EXPECT_EQ(expected_result, under_test.Apply(reference_input));
+
+    // Verify the operator.
+    EXPECT_EQ(expected_result, under_test(reference_input));
+  }
+
+  // Verifies that TimelineFunction::ApplyInverse, in its various forms, works
+  // as
+  // expected for the given arguments.
+  void VerifyApplyInverse(int64_t reference_time,
+                          int64_t subject_time,
+                          uint32_t reference_delta,
+                          uint32_t subject_delta,
+                          int64_t subject_input,
+                          int64_t expected_result) {
+    // Verify the static method.
+    EXPECT_EQ(expected_result,
+              TimelineFunction::ApplyInverse(
+                  reference_time, subject_time,
+                  TimelineRate(subject_delta, reference_delta), subject_input));
+
+    // Verify the instance method.
+    TimelineFunction under_test(reference_time, subject_time, reference_delta,
+                                subject_delta);
+    EXPECT_EQ(expected_result, under_test.ApplyInverse(subject_input));
+  }
+
+  // Verifies that TimelineFunction::Compose works as expected with the given
+  // inputs.
+  void VerifyCompose(const TimelineFunction& a,
+                     const TimelineFunction& b,
+                     bool exact,
+                     const TimelineFunction& expected_result) {
+    // Verify the static method.
+    EXPECT_EQ(expected_result, TimelineFunction::Compose(a, b, exact));
+  }
+};
+
+// Tests TimelineFunction basics for various instantiation arguments.
+TEST_F(TimelineFunctionTest, Basics) {
+  VerifyBasics(0, 0, 1, 0);
+  VerifyBasics(0, 0, 1, 1);
+  VerifyBasics(1, 1, 10, 10);
+  VerifyBasics(1234, 5678, 4321, 8765);
+  VerifyBasics(-1234, 5678, 4321, 8765);
+  VerifyBasics(-1234, -5678, 4321, 8765);
+  VerifyBasics(1234, -5678, 4321, 8765);
+}
+
+// Tests TimelineFunction::Inverse.
+TEST_F(TimelineFunctionTest, Inverse) {
+  VerifyInverse(0, 0, 1, 1);
+  VerifyInverse(1, 1, 10, 10);
+  VerifyInverse(1234, 5678, 4321, 8765);
+  VerifyInverse(-1234, 5678, 4321, 8765);
+  VerifyInverse(-1234, -5678, 4321, 8765);
+  VerifyInverse(1234, -5678, 4321, 8765);
+}
+
+// Tests TimelineFunction::Apply in its variations.
+TEST_F(TimelineFunctionTest, Apply) {
+  VerifyApply(0, 0, 1, 0, 0, 0);
+  VerifyApply(0, 0, 1, 0, 1000, 0);
+  VerifyApply(0, 1234, 1, 0, 0, 1234);
+  VerifyApply(0, 1234, 1, 0, 1000, 1234);
+  VerifyApply(0, 1234, 1, 0, -1000, 1234);
+  VerifyApply(0, -1234, 1, 0, 0, -1234);
+  VerifyApply(0, -1234, 1, 0, 1000, -1234);
+  VerifyApply(0, -1234, 1, 0, -1000, -1234);
+  VerifyApply(0, 0, 1, 1, 0, 0);
+  VerifyApply(0, 0, 1, 1, 1000, 1000);
+  VerifyApply(0, 1234, 1, 1, 0, 1234);
+  VerifyApply(0, 1234, 1, 1, 1000, 2234);
+  VerifyApply(0, 1234, 1, 1, -1000, 234);
+  VerifyApply(0, -1234, 1, 1, 0, -1234);
+  VerifyApply(0, -1234, 1, 1, 1000, -234);
+  VerifyApply(0, -1234, 1, 1, -1000, -2234);
+  VerifyApply(10, 0, 1, 0, 0, 0);
+  VerifyApply(10, 0, 1, 1, 0, -10);
+  VerifyApply(-10, 0, 1, 0, 0, 0);
+  VerifyApply(-10, 0, 1, 1, 0, 10);
+  VerifyApply(0, 1234, 2, 1, 0, 1234);
+  VerifyApply(0, 1234, 2, 1, 1234, 1234 + 1234 / 2);
+  VerifyApply(0, 1234, 1, 2, 1234, 1234 + 1234 * 2);
+}
+
+// Tests TimelineFunction::Apply in its variations.
+TEST_F(TimelineFunctionTest, ApplyInverse) {
+  VerifyApplyInverse(0, 0, 1, 1, 0, 0);
+  VerifyApplyInverse(0, 0, 1, 1, 1000, 1000);
+  VerifyApplyInverse(0, 1234, 1, 1, 1234, 0);
+  VerifyApplyInverse(0, 1234, 1, 1, 2234, 1000);
+  VerifyApplyInverse(0, 1234, 1, 1, 234, -1000);
+  VerifyApplyInverse(0, -1234, 1, 1, -1234, 0);
+  VerifyApplyInverse(0, -1234, 1, 1, -234, 1000);
+  VerifyApplyInverse(0, -1234, 1, 1, -2234, -1000);
+  VerifyApplyInverse(10, 0, 1, 1, -10, 0);
+  VerifyApplyInverse(-10, 0, 1, 1, 10, 0);
+  VerifyApplyInverse(0, 1234, 2, 1, 1234, 0);
+  VerifyApplyInverse(0, 1234, 2, 1, 1234 + 1234 / 2, 1234);
+  VerifyApplyInverse(0, 1234, 1, 2, 1234 + 1234 * 2, 1234);
+}
+
+// Tests TimelineFunction::Compose.
+TEST_F(TimelineFunctionTest, Compose) {
+  VerifyCompose(TimelineFunction(0, 0, 1, 0), TimelineFunction(0, 0, 1, 0),
+                true, TimelineFunction(0, 0, 1, 0));
+  VerifyCompose(TimelineFunction(0, 0, 1, 1), TimelineFunction(0, 0, 1, 1),
+                true, TimelineFunction(0, 0, 1, 1));
+  VerifyCompose(TimelineFunction(1, 0, 1, 1), TimelineFunction(0, 0, 1, 1),
+                true, TimelineFunction(0, -1, 1, 1));
+  VerifyCompose(TimelineFunction(10, 10, 1, 1), TimelineFunction(0, 0, 1, 1),
+                true, TimelineFunction(0, 0, 1, 1));
+  VerifyCompose(TimelineFunction(0, 0, 1, 2), TimelineFunction(0, 0, 1, 2),
+                true, TimelineFunction(0, 0, 1, 4));
+  VerifyCompose(TimelineFunction(0, 0, 2, 1), TimelineFunction(0, 0, 2, 1),
+                true, TimelineFunction(0, 0, 4, 1));
+  VerifyCompose(TimelineFunction(0, 0, 2, 1), TimelineFunction(0, 0, 1, 2),
+                true, TimelineFunction(0, 0, 1, 1));
+}
+
+}  // namespace
+}  // namespace media
+}  // namespace mojo
diff --git a/services/media/common/test/timeline_rate_test.cc b/services/media/common/test/timeline_rate_test.cc
new file mode 100644
index 0000000..d7080a0
--- /dev/null
+++ b/services/media/common/test/timeline_rate_test.cc
@@ -0,0 +1,186 @@
+// 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 <limits>
+
+#include "mojo/services/media/common/cpp/timeline_rate.h"
+#include "services/media/common/test/test_base.h"
+
+namespace mojo {
+namespace media {
+namespace {
+
+class TimelineRateTest : public TestBase {
+  static uint32_t gcd(uint32_t a, uint32_t b) {
+    while (b != 0) {
+      uint32_t t = a;
+      a = b;
+      b = t % b;
+    }
+    return a;
+  }
+
+ public:
+  // Verifies TimelineRate::Reduce and the constructor, ensuring that the ratio
+  // subject_delta * common_factor / reference_delta * common_factor is reduced
+  // to subject_delta / reference_delta. subject_delta and reference_delta need
+  // to be relatively prime for this to work.
+  void VerifyReduce(uint32_t subject_delta,
+                    uint32_t reference_delta,
+                    uint32_t common_factor) {
+    // Make sure subject_delta and reference_delta are relatively prime.
+    EXPECT_EQ(1u, gcd(subject_delta, reference_delta));
+
+    uint32_t test_subject_delta = subject_delta * common_factor;
+    uint32_t test_reference_delta = reference_delta * common_factor;
+
+    // Make sure the constructor reduces.
+    TimelineRate rate(test_subject_delta, test_reference_delta);
+    EXPECT_EQ(subject_delta, rate.subject_delta());
+    EXPECT_EQ(reference_delta, rate.reference_delta());
+
+    // Test the static method.
+    TimelineRate::Reduce(&test_subject_delta, &test_reference_delta);
+    EXPECT_EQ(subject_delta, test_subject_delta);
+    EXPECT_EQ(reference_delta, test_reference_delta);
+  }
+
+  // Verifies the TimelineRate::Scale methods by scaling value by subject_delta
+  // /
+  // reference_delta and verifying the result.
+  void VerifyScale(int64_t value,
+                   uint32_t subject_delta,
+                   uint32_t reference_delta,
+                   int64_t result) {
+    // Test the instance method.
+    EXPECT_EQ(result,
+              TimelineRate(subject_delta, reference_delta).Scale(value));
+
+    // Test the static method.
+    EXPECT_EQ(result,
+              TimelineRate::Scale(value, subject_delta, reference_delta));
+
+    // Test the operators.
+    EXPECT_EQ(result, value * TimelineRate(subject_delta, reference_delta));
+    EXPECT_EQ(result, TimelineRate(subject_delta, reference_delta) * value);
+    if (subject_delta != 0) {
+      EXPECT_EQ(result, value / TimelineRate(reference_delta, subject_delta));
+    }
+  }
+
+  // Verifies the TimelineRate::Product methods by multiplying the given a and b
+  // rates and checking the result against the expected rate.
+  void VerifyProduct(uint32_t a_subject_delta,
+                     uint32_t a_reference_delta,
+                     uint32_t b_subject_delta,
+                     uint32_t b_reference_delta,
+                     uint32_t expected_subject_delta,
+                     uint32_t expected_reference_delta,
+                     bool exact) {
+    // Test the first static method.
+    uint32_t actual_subject_delta;
+    uint32_t actual_reference_delta;
+    TimelineRate::Product(a_subject_delta, a_reference_delta, b_subject_delta,
+                          b_reference_delta, &actual_subject_delta,
+                          &actual_reference_delta, exact);
+    EXPECT_EQ(expected_subject_delta, actual_subject_delta);
+    EXPECT_EQ(expected_reference_delta, actual_reference_delta);
+
+    // Test the second static method.
+    EXPECT_EQ(TimelineRate(expected_subject_delta, expected_reference_delta),
+              TimelineRate::Product(
+                  TimelineRate(a_subject_delta, a_reference_delta),
+                  TimelineRate(b_subject_delta, b_reference_delta), exact));
+
+    // Test the operator
+    if (exact) {
+      EXPECT_EQ(TimelineRate(expected_subject_delta, expected_reference_delta),
+                TimelineRate(a_subject_delta, a_reference_delta) *
+                    TimelineRate(b_subject_delta, b_reference_delta));
+    }
+  }
+
+  // Verifies the TimelineRaten::Inverse method using the given rate.
+  void VerifyInverse(uint32_t subject_delta, uint32_t reference_delta) {
+    TimelineRate rate(subject_delta, reference_delta);
+    TimelineRate inverse(rate.Inverse());
+    EXPECT_EQ(rate.reference_delta(), inverse.subject_delta());
+    EXPECT_EQ(rate.subject_delta(), inverse.reference_delta());
+  }
+};
+
+// Tests TimelineRate::Reduce and that the TimelineRate constructor reduces.
+TEST_F(TimelineRateTest, Reduce) {
+  VerifyReduce(0, 1, 1);
+  VerifyReduce(1, 1, 1);
+  VerifyReduce(1234, 1, 1);
+  VerifyReduce(1, 1234, 14);
+  VerifyReduce(1, 1, 1234);
+  VerifyReduce(10, 1, 1234);
+  VerifyReduce(1, 10, 1234);
+  VerifyReduce(49, 81, 1);
+  VerifyReduce(49, 81, 10);
+  VerifyReduce(49, 81, 100);
+  VerifyReduce(1, 8, 65536);
+  VerifyReduce(8, 1, 65536);
+}
+
+// Tests TimelineRate::Scale, static, instance and operator versions.
+TEST_F(TimelineRateTest, Scale) {
+  const int64_t int64_min = std::numeric_limits<int64_t>::min();
+  VerifyScale(0, 0, 1, 0);
+  VerifyScale(1, 0, 1, 0);
+  VerifyScale(0, 1, 1, 0);
+  VerifyScale(1, 1, 1, 1);
+  VerifyScale(1, 2, 1, 2);
+  VerifyScale(1, 1, 2, 0);
+  VerifyScale(-1, 1, 2, -1);
+  VerifyScale(1000, 1, 2, 500);
+  VerifyScale(1001, 1, 2, 500);
+  VerifyScale(-1000, 1, 2, -500);
+  VerifyScale(-1001, 1, 2, -501);
+  VerifyScale(1000, 2, 1, 2000);
+  VerifyScale(1001, 2, 1, 2002);
+  VerifyScale(-1000, 2, 1, -2000);
+  VerifyScale(-1001, 2, 1, -2002);
+  VerifyScale(1ll << 32, 1, 1, 1ll << 32);
+  VerifyScale(1ll << 32, 1, 2, 1ll << 31);
+  VerifyScale(1ll << 32, 2, 1, 1ll << 33);
+  VerifyScale(1234ll << 30, 1, 1, 1234ll << 30);
+  VerifyScale(1234ll << 30, 1, 2, 1234ll << 29);
+  VerifyScale(1234ll << 30, 2, 1, 1234ll << 31);
+  VerifyScale(1234ll << 30, 1 << 31, 1, TimelineRate::kOverflow);
+  VerifyScale(1234ll << 30, 1ll << 31, (1ll << 31) - 2,
+              (1234ll << 30) + 1234ll);
+  VerifyScale(int64_min, 1, 1, int64_min);
+  VerifyScale(int64_min, 1, 2, int64_min / 2);
+  VerifyScale(int64_min / 2, 2, 1, int64_min);
+  VerifyScale(int64_min, 1000001, 1000000, TimelineRate::kOverflow);
+}
+
+// Tests TimelineRate::Product, static and operator versions.
+TEST_F(TimelineRateTest, Product) {
+  VerifyProduct(0, 1, 0, 1, 0, 1, true);
+  VerifyProduct(1, 1, 1, 1, 1, 1, true);
+  VerifyProduct(10, 1, 1, 10, 1, 1, true);
+  VerifyProduct(4321, 1234, 617, 4321, 1, 2, true);
+  VerifyProduct(1234, 4321, 4321, 617, 2, 1, true);
+  VerifyProduct(1ll << 31, (1ll << 31) - 1, (1ll << 31) - 1, 1ll << 31, 1, 1,
+                true);
+  VerifyProduct(1ll << 31, (1ll << 31) - 1, (1ll << 31) - 2, 1ll << 31,
+                0x7ffffffe, 0x7fffffff, false);
+}
+
+// Tests TimelineRate::Inverse.
+TEST_F(TimelineRateTest, Inverse) {
+  VerifyInverse(1, 1);
+  VerifyInverse(2, 1);
+  VerifyInverse(1, 2);
+  VerifyInverse(1000000, 1234);
+  VerifyInverse(1234, 1000000);
+}
+
+}  // namespace
+}  // namespace media
+}  // namespace mojo