1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "media/blink/websourcebuffer_impl.h"
6
7#include <limits>
8
9#include "base/bind.h"
10#include "base/callback.h"
11#include "base/callback_helpers.h"
12#include "base/float_util.h"
13#include "media/filters/chunk_demuxer.h"
14#include "third_party/WebKit/public/platform/WebSourceBufferClient.h"
15
16namespace media {
17
18static base::TimeDelta DoubleToTimeDelta(double time) {
19  DCHECK(!base::IsNaN(time));
20  DCHECK_NE(time, -std::numeric_limits<double>::infinity());
21
22  if (time == std::numeric_limits<double>::infinity())
23    return kInfiniteDuration();
24
25  // Don't use base::TimeDelta::Max() here, as we want the largest finite time
26  // delta.
27  base::TimeDelta max_time = base::TimeDelta::FromInternalValue(kint64max - 1);
28  double max_time_in_seconds = max_time.InSecondsF();
29
30  if (time >= max_time_in_seconds)
31    return max_time;
32
33  return base::TimeDelta::FromMicroseconds(
34      time * base::Time::kMicrosecondsPerSecond);
35}
36
37WebSourceBufferImpl::WebSourceBufferImpl(
38    const std::string& id, ChunkDemuxer* demuxer)
39    : id_(id),
40      demuxer_(demuxer),
41      client_(NULL),
42      append_window_end_(kInfiniteDuration()) {
43  DCHECK(demuxer_);
44}
45
46WebSourceBufferImpl::~WebSourceBufferImpl() {
47  DCHECK(!demuxer_) << "Object destroyed w/o removedFromMediaSource() call";
48  DCHECK(!client_);
49}
50
51void WebSourceBufferImpl::setClient(blink::WebSourceBufferClient* client) {
52  DCHECK(client);
53  DCHECK(!client_);
54  client_ = client;
55}
56
57bool WebSourceBufferImpl::setMode(WebSourceBuffer::AppendMode mode) {
58  if (demuxer_->IsParsingMediaSegment(id_))
59    return false;
60
61  switch (mode) {
62    case WebSourceBuffer::AppendModeSegments:
63      demuxer_->SetSequenceMode(id_, false);
64      return true;
65    case WebSourceBuffer::AppendModeSequence:
66      demuxer_->SetSequenceMode(id_, true);
67      return true;
68  }
69
70  NOTREACHED();
71  return false;
72}
73
74blink::WebTimeRanges WebSourceBufferImpl::buffered() {
75  Ranges<base::TimeDelta> ranges = demuxer_->GetBufferedRanges(id_);
76  blink::WebTimeRanges result(ranges.size());
77  for (size_t i = 0; i < ranges.size(); i++) {
78    result[i].start = ranges.start(i).InSecondsF();
79    result[i].end = ranges.end(i).InSecondsF();
80  }
81  return result;
82}
83
84void WebSourceBufferImpl::append(
85    const unsigned char* data,
86    unsigned length,
87    double* timestamp_offset) {
88  base::TimeDelta old_offset = timestamp_offset_;
89  demuxer_->AppendData(id_, data, length,
90                       append_window_start_, append_window_end_,
91                       &timestamp_offset_,
92                       base::Bind(&WebSourceBufferImpl::InitSegmentReceived,
93                                  base::Unretained(this)));
94
95  // Coded frame processing may update the timestamp offset. If the caller
96  // provides a non-NULL |timestamp_offset| and frame processing changes the
97  // timestamp offset, report the new offset to the caller. Do not update the
98  // caller's offset otherwise, to preserve any pre-existing value that may have
99  // more than microsecond precision.
100  if (timestamp_offset && old_offset != timestamp_offset_)
101    *timestamp_offset = timestamp_offset_.InSecondsF();
102}
103
104void WebSourceBufferImpl::abort() {
105  demuxer_->Abort(id_,
106                  append_window_start_, append_window_end_,
107                  &timestamp_offset_);
108
109  // TODO(wolenetz): abort should be able to modify the caller timestamp offset
110  // (just like WebSourceBufferImpl::append).
111  // See http://crbug.com/370229 for further details.
112}
113
114void WebSourceBufferImpl::remove(double start, double end) {
115  DCHECK_GE(start, 0);
116  DCHECK_GE(end, 0);
117  demuxer_->Remove(id_, DoubleToTimeDelta(start), DoubleToTimeDelta(end));
118}
119
120bool WebSourceBufferImpl::setTimestampOffset(double offset) {
121  if (demuxer_->IsParsingMediaSegment(id_))
122    return false;
123
124  timestamp_offset_ = DoubleToTimeDelta(offset);
125
126  // http://www.w3.org/TR/media-source/#widl-SourceBuffer-timestampOffset
127  // Step 6: If the mode attribute equals "sequence", then set the group start
128  // timestamp to new timestamp offset.
129  demuxer_->SetGroupStartTimestampIfInSequenceMode(id_, timestamp_offset_);
130  return true;
131}
132
133void WebSourceBufferImpl::setAppendWindowStart(double start) {
134  DCHECK_GE(start, 0);
135  append_window_start_ = DoubleToTimeDelta(start);
136}
137
138void WebSourceBufferImpl::setAppendWindowEnd(double end) {
139  DCHECK_GE(end, 0);
140  append_window_end_ = DoubleToTimeDelta(end);
141}
142
143void WebSourceBufferImpl::removedFromMediaSource() {
144  demuxer_->RemoveId(id_);
145  demuxer_ = NULL;
146  client_ = NULL;
147}
148
149void WebSourceBufferImpl::InitSegmentReceived() {
150  DVLOG(1) << __FUNCTION__;
151  client_->initializationSegmentReceived();
152}
153
154}  // namespace media
155