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.
4 
5 #ifndef MEDIA_MUXERS_WEBM_MUXER_H_
6 #define MEDIA_MUXERS_WEBM_MUXER_H_
7 
8 #include <stdint.h>
9 
10 #include <memory>
11 
12 #include "base/callback.h"
13 #include "base/containers/circular_deque.h"
14 #include "base/macros.h"
15 #include "base/numerics/safe_math.h"
16 #include "base/sequence_checker.h"
17 #include "base/strings/string_piece.h"
18 #include "base/time/time.h"
19 #include "base/timer/elapsed_timer.h"
20 #include "media/base/audio_codecs.h"
21 #include "media/base/media_export.h"
22 #include "media/base/video_codecs.h"
23 #include "media/base/video_color_space.h"
24 #include "third_party/libwebm/source/mkvmuxer.hpp"
25 #include "ui/gfx/geometry/size.h"
26 
27 namespace gfx {
28 class Size;
29 }  // namespace gfx
30 
31 namespace media {
32 
33 class VideoFrame;
34 class AudioParameters;
35 
36 // Adapter class to manage a WebM container [1], a simplified version of a
37 // Matroska container [2], composed of an EBML header, and a single Segment
38 // including at least a Track Section and a number of SimpleBlocks each
39 // containing a single encoded video or audio frame. WebM container has no
40 // Trailer.
41 // Clients will push encoded VPx or AV1 video frames and Opus or PCM audio
42 // frames one by one via OnEncoded{Video|Audio}(). libwebm will eventually ping
43 // the WriteDataCB passed on contructor with the wrapped encoded data.
44 // WebmMuxer is designed for use on a single thread.
45 // [1] http://www.webmproject.org/docs/container/
46 // [2] http://www.matroska.org/technical/specs/index.html
47 class MEDIA_EXPORT WebmMuxer : public mkvmuxer::IMkvWriter {
48  public:
49   // Callback to be called when WebmMuxer is ready to write a chunk of data,
50   // either any file header or a SingleBlock.
51   using WriteDataCB = base::RepeatingCallback<void(base::StringPiece)>;
52 
53   // Container for the parameters that muxer uses that is extracted from
54   // media::VideoFrame.
55   struct MEDIA_EXPORT VideoParameters {
56     VideoParameters(scoped_refptr<media::VideoFrame> frame);
57     VideoParameters(gfx::Size visible_rect_size,
58                     double frame_rate,
59                     VideoCodec codec,
60                     base::Optional<gfx::ColorSpace> color_space);
61     VideoParameters(const VideoParameters&);
62     ~VideoParameters();
63     gfx::Size visible_rect_size;
64     double frame_rate;
65     VideoCodec codec;
66     base::Optional<gfx::ColorSpace> color_space;
67   };
68 
69   // |audio_codec| should coincide with whatever is sent in OnEncodedAudio(),
70   WebmMuxer(AudioCodec audio_codec,
71             bool has_video_,
72             bool has_audio_,
73             const WriteDataCB& write_data_callback);
74   ~WebmMuxer() override;
75 
76   // Functions to add video and audio frames with |encoded_data.data()|
77   // to WebM Segment. Either one returns true on success.
78   // |encoded_alpha| represents the encode output of alpha channel when
79   // available, can be nullptr otherwise.
80   bool OnEncodedVideo(const VideoParameters& params,
81                       std::string encoded_data,
82                       std::string encoded_alpha,
83                       base::TimeTicks timestamp,
84                       bool is_key_frame);
85   bool OnEncodedAudio(const media::AudioParameters& params,
86                       std::string encoded_data,
87                       base::TimeTicks timestamp);
88 
89   void Pause();
90   void Resume();
91 
92   // Drains and writes out all buffered frames and finalizes the segment.
93   // Returns true on success, false otherwise.
94   bool Flush();
95 
ForceOneLibWebmErrorForTesting()96   void ForceOneLibWebmErrorForTesting() { force_one_libwebm_error_ = true; }
97 
98  private:
99   friend class WebmMuxerTest;
100 
101   // Methods for creating and adding video and audio tracks, called upon
102   // receiving the first frame of a given Track.
103   // AddVideoTrack adds |frame_size| and |frame_rate| to the Segment
104   // info, although individual frames passed to OnEncodedVideo() can have any
105   // frame size.
106   void AddVideoTrack(const gfx::Size& frame_size,
107                      double frame_rate,
108                      const base::Optional<gfx::ColorSpace>& color_space);
109   void AddAudioTrack(const media::AudioParameters& params);
110 
111   // IMkvWriter interface.
112   mkvmuxer::int32 Write(const void* buf, mkvmuxer::uint32 len) override;
113   mkvmuxer::int64 Position() const override;
114   mkvmuxer::int32 Position(mkvmuxer::int64 position) override;
115   bool Seekable() const override;
116   void ElementStartNotify(mkvmuxer::uint64 element_id,
117                           mkvmuxer::int64 position) override;
118 
119   // Adds all currently buffered frames to the mkvmuxer in timestamp order,
120   // until the queues are depleted.
121   void FlushQueues();
122   // Flushes out frames to the mkvmuxer while ensuring monotonically increasing
123   // timestamps as per the WebM specification,
124   // https://www.webmproject.org/docs/container/. Returns true on success and
125   // false on mkvmuxer failure.
126   //
127   // Note that frames may still be around in the queues after this call. The
128   // method stops flushing when timestamp monotonicity can't be guaranteed
129   // anymore.
130   bool PartiallyFlushQueues();
131   // Flushes out the next frame in timestamp order from the queues. Returns true
132   // on success and false on mkvmuxer failure.
133   //
134   // Note: it's assumed that at least one video or audio frame is queued.
135   bool FlushNextFrame();
136   // Calculates a monotonically increasing timestamp from an input |timestamp|
137   // and a pointer to a previously stored |last_timestamp| by taking the maximum
138   // of |timestamp| and *|last_timestamp|. Updates *|last_timestamp| if
139   // |timestamp| is greater.
140   base::TimeTicks UpdateLastTimestampMonotonically(
141       base::TimeTicks timestamp,
142       base::TimeTicks* last_timestamp);
143 
144   // Audio codec configured on construction. Video codec is taken from first
145   // received frame.
146   const AudioCodec audio_codec_;
147   VideoCodec video_codec_;
148 
149   // Caller-side identifiers to interact with |segment_|, initialised upon
150   // first frame arrival to Add{Video, Audio}Track().
151   uint8_t video_track_index_;
152   uint8_t audio_track_index_;
153 
154   // Origin of times for frame timestamps.
155   base::TimeTicks first_frame_timestamp_video_;
156   base::TimeTicks last_frame_timestamp_video_;
157   base::TimeTicks first_frame_timestamp_audio_;
158   base::TimeTicks last_frame_timestamp_audio_;
159 
160   // Variables to measure and accumulate, respectively, the time in pause state.
161   std::unique_ptr<base::ElapsedTimer> elapsed_time_in_pause_;
162   base::TimeDelta total_time_in_pause_;
163 
164   // TODO(ajose): Change these when support is added for multiple tracks.
165   // http://crbug.com/528523
166   const bool has_video_;
167   const bool has_audio_;
168 
169   // Callback to dump written data as being called by libwebm.
170   const WriteDataCB write_data_callback_;
171 
172   // Rolling counter of the position in bytes of the written goo.
173   base::CheckedNumeric<mkvmuxer::int64> position_;
174 
175   // The MkvMuxer active element.
176   mkvmuxer::Segment segment_;
177   // Flag to force the next call to a |segment_| method to return false.
178   bool force_one_libwebm_error_;
179 
180   struct EncodedFrame {
181     std::string data;
182     std::string alpha_data;
183     base::TimeDelta
184         relative_timestamp;  // relative to first_frame_timestamp_xxx_
185     bool is_keyframe;
186   };
187 
188   // The following two queues hold frames to ensure that monotonically
189   // increasing timestamps are stored in the resulting webm file without
190   // modifying the timestamps.
191   base::circular_deque<EncodedFrame> audio_frames_;
192   // If muxing audio and video, this queue holds frames until the first audio
193   // frame appears.
194   base::circular_deque<EncodedFrame> video_frames_;
195 
196   SEQUENCE_CHECKER(sequence_checker_);
197 
198   DISALLOW_COPY_AND_ASSIGN(WebmMuxer);
199 };
200 
201 }  // namespace media
202 
203 #endif  // MEDIA_MUXERS_WEBM_MUXER_H_
204