1 // Copyright 2015 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.
5 #include "media/capture/webm_muxer.h"
8 #include "media/base/limits.h"
9 #include "media/base/video_frame.h"
13 static double GetFrameRate(const scoped_refptr
<VideoFrame
>& video_frame
) {
14 const double kZeroFrameRate
= 0.0;
15 const double kDefaultFrameRate
= 30.0;
17 double frame_rate
= kDefaultFrameRate
;
18 if (!video_frame
->metadata()->GetDouble(
19 VideoFrameMetadata::FRAME_RATE
, &frame_rate
) ||
20 frame_rate
<= kZeroFrameRate
||
21 frame_rate
> media::limits::kMaxFramesPerSecond
) {
22 frame_rate
= kDefaultFrameRate
;
27 WebmMuxer::WebmMuxer(const WriteDataCB
& write_data_callback
)
29 write_data_callback_(write_data_callback
),
31 DCHECK(!write_data_callback_
.is_null());
32 // Creation is done on a different thread than main activities.
33 thread_checker_
.DetachFromThread();
36 WebmMuxer::~WebmMuxer() {
37 // No need to segment_.Finalize() since is not Seekable(), i.e. a live stream,
38 // but is good practice.
42 void WebmMuxer::OnEncodedVideo(const scoped_refptr
<VideoFrame
>& video_frame
,
43 const base::StringPiece
& encoded_data
,
44 base::TimeTicks timestamp
,
46 DVLOG(1) << __FUNCTION__
<< " - " << encoded_data
.size() << "B";
47 DCHECK(thread_checker_
.CalledOnValidThread());
49 // |track_index_|, cannot be zero (!), initialize WebmMuxer in that case.
50 // http://www.matroska.org/technical/specs/index.html#Tracks
51 AddVideoTrack(video_frame
->visible_rect().size(),
52 GetFrameRate(video_frame
));
53 first_frame_timestamp_
= timestamp
;
55 segment_
.AddFrame(reinterpret_cast<const uint8_t*>(encoded_data
.data()),
58 (timestamp
- first_frame_timestamp_
).InMicroseconds() *
59 base::Time::kNanosecondsPerMicrosecond
,
63 void WebmMuxer::AddVideoTrack(const gfx::Size
& frame_size
, double frame_rate
) {
64 DCHECK(thread_checker_
.CalledOnValidThread());
65 DCHECK_EQ(track_index_
, 0u) << "WebmMuxer can only be initialised once.";
68 segment_
.set_mode(mkvmuxer::Segment::kLive
);
69 segment_
.OutputCues(false);
71 mkvmuxer::SegmentInfo
* const info
= segment_
.GetSegmentInfo();
72 info
->set_writing_app("Chrome");
73 info
->set_muxing_app("Chrome");
76 segment_
.AddVideoTrack(frame_size
.width(), frame_size
.height(), 0);
77 DCHECK_GT(track_index_
, 0u);
79 mkvmuxer::VideoTrack
* const video_track
=
80 reinterpret_cast<mkvmuxer::VideoTrack
*>(
81 segment_
.GetTrackByNumber(track_index_
));
83 video_track
->set_codec_id(mkvmuxer::Tracks::kVp8CodecId
);
84 DCHECK_EQ(video_track
->crop_right(), 0ull);
85 DCHECK_EQ(video_track
->crop_left(), 0ull);
86 DCHECK_EQ(video_track
->crop_top(), 0ull);
87 DCHECK_EQ(video_track
->crop_bottom(), 0ull);
89 video_track
->set_frame_rate(frame_rate
);
90 video_track
->set_default_duration(base::Time::kNanosecondsPerSecond
/
92 // Segment's timestamps should be in milliseconds, DCHECK it. See
93 // http://www.webmproject.org/docs/container/#muxer-guidelines
94 DCHECK_EQ(segment_
.GetSegmentInfo()->timecode_scale(), 1000000ull);
97 mkvmuxer::int32
WebmMuxer::Write(const void* buf
, mkvmuxer::uint32 len
) {
98 DCHECK(thread_checker_
.CalledOnValidThread());
100 write_data_callback_
.Run(base::StringPiece(reinterpret_cast<const char*>(buf
),
106 mkvmuxer::int64
WebmMuxer::Position() const {
107 return position_
.ValueOrDie();
110 mkvmuxer::int32
WebmMuxer::Position(mkvmuxer::int64 position
) {
111 // The stream is not Seekable() so indicate we cannot set the position.
115 bool WebmMuxer::Seekable() const {
119 void WebmMuxer::ElementStartNotify(mkvmuxer::uint64 element_id
,
120 mkvmuxer::int64 position
) {
121 // This method gets pinged before items are sent to |write_data_callback_|.
122 DCHECK_GE(position
, position_
.ValueOrDefault(0))
123 << "Can't go back in a live WebM stream.";