| // Copyright 2013 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 "media/blink/websourcebuffer_impl.h" |
| |
| #include <stdint.h> |
| |
| #include <cmath> |
| #include <limits> |
| |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "base/callback_helpers.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "media/base/media_tracks.h" |
| #include "media/base/timestamp_constants.h" |
| #include "media/filters/chunk_demuxer.h" |
| #include "third_party/WebKit/public/platform/WebMediaPlayer.h" |
| #include "third_party/WebKit/public/platform/WebSourceBufferClient.h" |
| |
| namespace media { |
| |
| static base::TimeDelta DoubleToTimeDelta(double time) { |
| DCHECK(!std::isnan(time)); |
| DCHECK_NE(time, -std::numeric_limits<double>::infinity()); |
| |
| if (time == std::numeric_limits<double>::infinity()) |
| return kInfiniteDuration; |
| |
| // Don't use base::TimeDelta::Max() here, as we want the largest finite time |
| // delta. |
| base::TimeDelta max_time = base::TimeDelta::FromInternalValue( |
| std::numeric_limits<int64_t>::max() - 1); |
| double max_time_in_seconds = max_time.InSecondsF(); |
| |
| if (time >= max_time_in_seconds) |
| return max_time; |
| |
| return base::TimeDelta::FromMicroseconds( |
| time * base::Time::kMicrosecondsPerSecond); |
| } |
| |
| WebSourceBufferImpl::WebSourceBufferImpl(const std::string& id, |
| ChunkDemuxer* demuxer) |
| : id_(id), |
| demuxer_(demuxer), |
| client_(NULL), |
| append_window_end_(kInfiniteDuration) { |
| DCHECK(demuxer_); |
| demuxer_->SetTracksWatcher( |
| id, base::Bind(&WebSourceBufferImpl::InitSegmentReceived, |
| base::Unretained(this))); |
| } |
| |
| WebSourceBufferImpl::~WebSourceBufferImpl() { |
| DCHECK(!demuxer_) << "Object destroyed w/o removedFromMediaSource() call"; |
| DCHECK(!client_); |
| } |
| |
| void WebSourceBufferImpl::setClient(blink::WebSourceBufferClient* client) { |
| DCHECK(client); |
| DCHECK(!client_); |
| client_ = client; |
| } |
| |
| bool WebSourceBufferImpl::setMode(WebSourceBuffer::AppendMode mode) { |
| if (demuxer_->IsParsingMediaSegment(id_)) |
| return false; |
| |
| switch (mode) { |
| case WebSourceBuffer::AppendModeSegments: |
| demuxer_->SetSequenceMode(id_, false); |
| return true; |
| case WebSourceBuffer::AppendModeSequence: |
| demuxer_->SetSequenceMode(id_, true); |
| return true; |
| } |
| |
| NOTREACHED(); |
| return false; |
| } |
| |
| blink::WebTimeRanges WebSourceBufferImpl::buffered() { |
| Ranges<base::TimeDelta> ranges = demuxer_->GetBufferedRanges(id_); |
| blink::WebTimeRanges result(ranges.size()); |
| for (size_t i = 0; i < ranges.size(); i++) { |
| result[i].start = ranges.start(i).InSecondsF(); |
| result[i].end = ranges.end(i).InSecondsF(); |
| } |
| return result; |
| } |
| |
| double WebSourceBufferImpl::highestPresentationTimestamp() { |
| return demuxer_->GetHighestPresentationTimestamp(id_).InSecondsF(); |
| } |
| |
| bool WebSourceBufferImpl::evictCodedFrames(double currentPlaybackTime, |
| size_t newDataSize) { |
| return demuxer_->EvictCodedFrames( |
| id_, |
| base::TimeDelta::FromSecondsD(currentPlaybackTime), |
| newDataSize); |
| } |
| |
| bool WebSourceBufferImpl::append(const unsigned char* data, |
| unsigned length, |
| double* timestamp_offset) { |
| base::TimeDelta old_offset = timestamp_offset_; |
| bool success = demuxer_->AppendData(id_, data, length, append_window_start_, |
| append_window_end_, ×tamp_offset_); |
| |
| // Coded frame processing may update the timestamp offset. If the caller |
| // provides a non-NULL |timestamp_offset| and frame processing changes the |
| // timestamp offset, report the new offset to the caller. Do not update the |
| // caller's offset otherwise, to preserve any pre-existing value that may have |
| // more than microsecond precision. |
| if (timestamp_offset && old_offset != timestamp_offset_) |
| *timestamp_offset = timestamp_offset_.InSecondsF(); |
| |
| return success; |
| } |
| |
| void WebSourceBufferImpl::resetParserState() { |
| demuxer_->ResetParserState(id_, |
| append_window_start_, append_window_end_, |
| ×tamp_offset_); |
| |
| // TODO(wolenetz): resetParserState should be able to modify the caller |
| // timestamp offset (just like WebSourceBufferImpl::append). |
| // See http://crbug.com/370229 for further details. |
| } |
| |
| void WebSourceBufferImpl::remove(double start, double end) { |
| DCHECK_GE(start, 0); |
| DCHECK_GE(end, 0); |
| demuxer_->Remove(id_, DoubleToTimeDelta(start), DoubleToTimeDelta(end)); |
| } |
| |
| bool WebSourceBufferImpl::setTimestampOffset(double offset) { |
| if (demuxer_->IsParsingMediaSegment(id_)) |
| return false; |
| |
| timestamp_offset_ = DoubleToTimeDelta(offset); |
| |
| // http://www.w3.org/TR/media-source/#widl-SourceBuffer-timestampOffset |
| // Step 6: If the mode attribute equals "sequence", then set the group start |
| // timestamp to new timestamp offset. |
| demuxer_->SetGroupStartTimestampIfInSequenceMode(id_, timestamp_offset_); |
| return true; |
| } |
| |
| void WebSourceBufferImpl::setAppendWindowStart(double start) { |
| DCHECK_GE(start, 0); |
| append_window_start_ = DoubleToTimeDelta(start); |
| } |
| |
| void WebSourceBufferImpl::setAppendWindowEnd(double end) { |
| DCHECK_GE(end, 0); |
| append_window_end_ = DoubleToTimeDelta(end); |
| } |
| |
| void WebSourceBufferImpl::removedFromMediaSource() { |
| demuxer_->RemoveId(id_); |
| demuxer_ = NULL; |
| client_ = NULL; |
| } |
| |
| blink::WebMediaPlayer::TrackType mediaTrackTypeToBlink(MediaTrack::Type type) { |
| switch (type) { |
| case MediaTrack::Audio: |
| return blink::WebMediaPlayer::AudioTrack; |
| case MediaTrack::Text: |
| return blink::WebMediaPlayer::TextTrack; |
| case MediaTrack::Video: |
| return blink::WebMediaPlayer::VideoTrack; |
| } |
| NOTREACHED(); |
| return blink::WebMediaPlayer::AudioTrack; |
| } |
| |
| void WebSourceBufferImpl::InitSegmentReceived( |
| std::unique_ptr<MediaTracks> tracks) { |
| DCHECK(tracks.get()); |
| DVLOG(1) << __func__ << " tracks=" << tracks->tracks().size(); |
| |
| std::vector<blink::WebSourceBufferClient::MediaTrackInfo> trackInfoVector; |
| for (const auto& track : tracks->tracks()) { |
| blink::WebSourceBufferClient::MediaTrackInfo trackInfo; |
| trackInfo.trackType = mediaTrackTypeToBlink(track->type()); |
| trackInfo.id = blink::WebString::fromUTF8(track->id()); |
| trackInfo.byteStreamTrackID = blink::WebString::fromUTF8( |
| base::UintToString(track->bytestream_track_id())); |
| trackInfo.kind = blink::WebString::fromUTF8(track->kind()); |
| trackInfo.label = blink::WebString::fromUTF8(track->label()); |
| trackInfo.language = blink::WebString::fromUTF8(track->language()); |
| trackInfoVector.push_back(trackInfo); |
| } |
| |
| client_->initializationSegmentReceived(trackInfoVector); |
| } |
| |
| } // namespace media |