1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
media / muxers / webm_muxer.h [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MEDIA_MUXERS_WEBM_MUXER_H_
#define MEDIA_MUXERS_WEBM_MUXER_H_
#include <cstdint>
#include <memory>
#include <string>
#include "base/containers/circular_deque.h"
#include "media/muxers/muxer.h"
#include "third_party/libwebm/source/mkvmuxer.hpp"
namespace media {
class AudioParameters;
// Adapter class to manage a WebM container [1], a simplified version of a
// Matroska container [2], composed of an EBML header, and a single Segment
// including at least a Track Section and a number of SimpleBlocks each
// containing a single encoded video or audio frame. WebM container has no
// Trailer.
// Clients will push encoded VPx or AV1 video frames and Opus or PCM audio
// frames one by one via OnEncoded{Video|Audio}(). libwebm will eventually ping
// the WriteDataCB passed on constructor with the wrapped encoded data.
// WebmMuxer is designed for use on a single thread.
// [1] http://www.webmproject.org/docs/container/
// [2] http://www.matroska.org/technical/specs/index.html
class MEDIA_EXPORT WebmMuxer : public Muxer {
public:
// Defines an interface for delegates of WebmMuxer which should define how to
// implement the |mkvmuxer::IMkvWriter| APIs (e.g. whether to support
// non-seekable live mode writing, or seekable file mode writing).
class MEDIA_EXPORT Delegate : public mkvmuxer::IMkvWriter {
public:
Delegate();
~Delegate() override;
base::TimeTicks last_data_output_timestamp() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return last_data_output_timestamp_;
}
// Initializes the given |segment| according to the mode desired by the
// concrete implementation of this delegate.
virtual void InitSegment(mkvmuxer::Segment* segment) = 0;
// mkvmuxer::IMkvWriter:
mkvmuxer::int32 Write(const void* buf, mkvmuxer::uint32 len) final;
protected:
// Does the actual writing of |len| bytes from the given |buf| depending on
// the mode desired by the concrete implementation of this delegate.
// Returns 0 on success, -1 otherwise.
virtual mkvmuxer::int32 DoWrite(const void* buf, mkvmuxer::uint32 len) = 0;
SEQUENCE_CHECKER(sequence_checker_);
// The current writing position as set by libwebm.
base::CheckedNumeric<mkvmuxer::int64> position_
GUARDED_BY_CONTEXT(sequence_checker_) = 0;
// Last time data was written via Write().
base::TimeTicks last_data_output_timestamp_
GUARDED_BY_CONTEXT(sequence_checker_);
};
// `audio_codec` should coincide with whatever is sent in OnEncodedAudio(),
// If set, `max_data_output_interval` indicates the allowed maximum time for
// data output into the delegate provided frames are provided.
WebmMuxer(AudioCodec audio_codec,
bool has_video_,
bool has_audio_,
std::unique_ptr<Delegate> delegate,
std::optional<base::TimeDelta> max_data_output_interval);
WebmMuxer(const WebmMuxer&) = delete;
WebmMuxer& operator=(const WebmMuxer&) = delete;
~WebmMuxer() override;
// Drains and writes out all buffered frames and finalizes the segment.
// Returns true on success, false otherwise.
bool Flush() override;
bool PutFrame(EncodedFrame frame,
base::TimeDelta relative_timestamp) override;
void ForceOneLibWebmErrorForTesting() { force_one_libwebm_error_ = true; }
private:
friend class WebmMuxerTest;
// Methods for creating and adding video and audio tracks, called upon
// receiving the first frame of a given Track.
// AddVideoTrack adds |frame_size| and |frame_rate| to the Segment
// info, although individual frames passed to OnEncodedVideo() can have any
// frame size.
void AddVideoTrack(const gfx::Size& frame_size,
double frame_rate,
const std::optional<gfx::ColorSpace>& color_space);
void AddAudioTrack(const AudioParameters& params);
bool WriteWebmFrame(EncodedFrame frame, base::TimeDelta relative_timestamp);
// Forces data output from |segment_| on the next frame if recording video,
// and |min_data_output_interval_| was configured and has passed since the
// last received video frame.
void MaybeForceNewCluster();
// Audio codec configured on construction. Video codec is taken from first
// received frame.
const AudioCodec audio_codec_;
VideoCodec video_codec_ = VideoCodec::kUnknown;
// Caller-side identifiers to interact with |segment_|, initialised upon
// first frame arrival to Add{Video, Audio}Track().
uint8_t video_track_index_ = 0;
uint8_t audio_track_index_ = 0;
// TODO(ajose): Change these when support is added for multiple tracks.
// http://crbug.com/528523
const bool has_video_;
const bool has_audio_;
// Maximum interval between data output callbacks (given frames arriving).
// The muxer can hold on to audio frames almost indefinitely in the case video
// is recorded and video frames are temporarily not delivered. When this
// method is used, a new WebM cluster is forced when the next frame arrives
// |duration| after the last write.
// The maximum duration between forced clusters is internally limited to not
// go below 100 ms.
// TODO(crbug.com/40876732): consider if cluster output should be based on
// media timestamps.
base::TimeDelta max_data_output_interval_;
// Last timestamp written into the segment.
base::TimeDelta last_timestamp_written_;
std::unique_ptr<Delegate> delegate_;
// The MkvMuxer active element.
mkvmuxer::Segment segment_;
// Flag to force the next call to a |segment_| method to return false.
bool force_one_libwebm_error_ = false;
// Frames held until all track headers have been written.
base::circular_deque<std::tuple<EncodedFrame, base::TimeDelta>>
buffered_frames_;
SEQUENCE_CHECKER(sequence_checker_);
};
} // namespace media
#endif // MEDIA_MUXERS_WEBM_MUXER_H_