webm_muxer.cc 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  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. #include "media/muxers/webm_muxer.h"
  5. #include <algorithm>
  6. #include <memory>
  7. #include "base/bind.h"
  8. #include "base/check.h"
  9. #include "base/logging.h"
  10. #include "base/sequence_checker.h"
  11. #include "base/time/time.h"
  12. #include "base/time/time_override.h"
  13. #include "media/base/audio_parameters.h"
  14. #include "media/base/limits.h"
  15. #include "media/base/video_frame.h"
  16. #include "media/formats/common/opus_constants.h"
  17. namespace media {
  18. namespace {
  19. namespace av1 {
  20. // CodecPrivate for AV1. See
  21. // https://github.com/ietf-wg-cellar/matroska-specification/blob/master/codec/av1.md.
  22. constexpr int high_bitdepth = 0;
  23. constexpr int twelve_bit = 0;
  24. constexpr int monochrome = 0;
  25. constexpr int initial_presentation_delay_present = 0;
  26. constexpr int initial_presentation_delay_minus_one = 0;
  27. constexpr int chroma_sample_position = 0;
  28. constexpr int seq_profile = 0; // Main
  29. constexpr int seq_level_idx_0 = 9; // level 4.1 ~1920x1080@60fps
  30. constexpr int seq_tier_0 = 0;
  31. constexpr int chroma_subsampling_x = 1;
  32. constexpr int chroma_subsampling_y = 1;
  33. constexpr uint8_t codec_private[4] = {
  34. 255, //
  35. (seq_profile << 5) | seq_level_idx_0, //
  36. (seq_tier_0 << 7) | //
  37. (high_bitdepth << 6) | //
  38. (twelve_bit << 5) | //
  39. (monochrome << 4) | //
  40. (chroma_subsampling_x << 3) | //
  41. (chroma_subsampling_y << 2) | //
  42. chroma_sample_position, //
  43. (initial_presentation_delay_present << 4) | //
  44. initial_presentation_delay_minus_one //
  45. };
  46. } // namespace av1
  47. // Force new clusters at a maximum rate of 10 Hz.
  48. constexpr base::TimeDelta kMinimumForcedClusterDuration =
  49. base::Milliseconds(100);
  50. void WriteOpusHeader(const media::AudioParameters& params, uint8_t* header) {
  51. // See https://wiki.xiph.org/OggOpus#ID_Header.
  52. // Set magic signature.
  53. std::string label = "OpusHead";
  54. memcpy(header + OPUS_EXTRADATA_LABEL_OFFSET, label.c_str(), label.size());
  55. // Set Opus version.
  56. header[OPUS_EXTRADATA_VERSION_OFFSET] = 1;
  57. // Set channel count.
  58. DCHECK_LE(params.channels(), 2);
  59. header[OPUS_EXTRADATA_CHANNELS_OFFSET] = params.channels();
  60. // Set pre-skip
  61. uint16_t skip = 0;
  62. memcpy(header + OPUS_EXTRADATA_SKIP_SAMPLES_OFFSET, &skip, sizeof(uint16_t));
  63. // Set original input sample rate in Hz.
  64. uint32_t sample_rate = params.sample_rate();
  65. memcpy(header + OPUS_EXTRADATA_SAMPLE_RATE_OFFSET, &sample_rate,
  66. sizeof(uint32_t));
  67. // Set output gain in dB.
  68. uint16_t gain = 0;
  69. memcpy(header + OPUS_EXTRADATA_GAIN_OFFSET, &gain, 2);
  70. header[OPUS_EXTRADATA_CHANNEL_MAPPING_OFFSET] = 0;
  71. }
  72. static double GetFrameRate(const WebmMuxer::VideoParameters& params) {
  73. const double kZeroFrameRate = 0.0;
  74. const double kDefaultFrameRate = 30.0;
  75. double frame_rate = params.frame_rate;
  76. if (frame_rate <= kZeroFrameRate ||
  77. frame_rate > media::limits::kMaxFramesPerSecond) {
  78. frame_rate = kDefaultFrameRate;
  79. }
  80. return frame_rate;
  81. }
  82. static const char kH264CodecId[] = "V_MPEG4/ISO/AVC";
  83. static const char kPcmCodecId[] = "A_PCM/FLOAT/IEEE";
  84. static const char* MkvCodeIcForMediaVideoCodecId(VideoCodec video_codec) {
  85. switch (video_codec) {
  86. case VideoCodec::kVP8:
  87. return mkvmuxer::Tracks::kVp8CodecId;
  88. case VideoCodec::kVP9:
  89. return mkvmuxer::Tracks::kVp9CodecId;
  90. case VideoCodec::kAV1:
  91. return mkvmuxer::Tracks::kAv1CodecId;
  92. case VideoCodec::kH264:
  93. return kH264CodecId;
  94. default:
  95. NOTREACHED() << "Unsupported codec " << GetCodecName(video_codec);
  96. return "";
  97. }
  98. }
  99. absl::optional<mkvmuxer::Colour> ColorFromColorSpace(
  100. const gfx::ColorSpace& color) {
  101. using mkvmuxer::Colour;
  102. using MatrixID = gfx::ColorSpace::MatrixID;
  103. using RangeID = gfx::ColorSpace::RangeID;
  104. using TransferID = gfx::ColorSpace::TransferID;
  105. using PrimaryID = gfx::ColorSpace::PrimaryID;
  106. Colour colour;
  107. int matrix_coefficients;
  108. switch (color.GetMatrixID()) {
  109. case MatrixID::BT709:
  110. matrix_coefficients = Colour::kBt709;
  111. break;
  112. case MatrixID::BT2020_NCL:
  113. matrix_coefficients = Colour::kBt2020NonConstantLuminance;
  114. break;
  115. default:
  116. return absl::nullopt;
  117. }
  118. colour.set_matrix_coefficients(matrix_coefficients);
  119. int range;
  120. switch (color.GetRangeID()) {
  121. case RangeID::LIMITED:
  122. range = Colour::kBroadcastRange;
  123. break;
  124. case RangeID::FULL:
  125. range = Colour::kFullRange;
  126. break;
  127. default:
  128. return absl::nullopt;
  129. }
  130. colour.set_range(range);
  131. int transfer_characteristics;
  132. switch (color.GetTransferID()) {
  133. case TransferID::BT709:
  134. transfer_characteristics = Colour::kIturBt709Tc;
  135. break;
  136. case TransferID::SRGB:
  137. transfer_characteristics = Colour::kIec6196621;
  138. break;
  139. case TransferID::PQ:
  140. transfer_characteristics = Colour::kSmpteSt2084;
  141. break;
  142. default:
  143. return absl::nullopt;
  144. }
  145. colour.set_transfer_characteristics(transfer_characteristics);
  146. int primaries;
  147. switch (color.GetPrimaryID()) {
  148. case PrimaryID::BT709:
  149. primaries = Colour::kIturBt709P;
  150. break;
  151. case PrimaryID::BT2020:
  152. primaries = Colour::kIturBt2020;
  153. break;
  154. default:
  155. return absl::nullopt;
  156. }
  157. colour.set_primaries(primaries);
  158. return colour;
  159. }
  160. } // anonymous namespace
  161. // -----------------------------------------------------------------------------
  162. // WebmMuxer::Delegate:
  163. WebmMuxer::Delegate::Delegate() {
  164. // Creation can be done on a different sequence than main activities.
  165. DETACH_FROM_SEQUENCE(sequence_checker_);
  166. }
  167. WebmMuxer::Delegate::~Delegate() = default;
  168. mkvmuxer::int32 WebmMuxer::Delegate::Write(const void* buf,
  169. mkvmuxer::uint32 len) {
  170. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  171. DVLOG(2) << __func__ << " len " << len;
  172. DCHECK(buf);
  173. last_data_output_timestamp_ = base::TimeTicks::Now();
  174. const auto result = DoWrite(buf, len);
  175. position_ += len;
  176. return result;
  177. }
  178. // -----------------------------------------------------------------------------
  179. // WebmMuxer::VideoParameters:
  180. WebmMuxer::VideoParameters::VideoParameters(
  181. scoped_refptr<media::VideoFrame> frame)
  182. : visible_rect_size(frame->visible_rect().size()),
  183. frame_rate(frame->metadata().frame_rate.value_or(0.0)),
  184. codec(VideoCodec::kUnknown),
  185. color_space(frame->ColorSpace()) {}
  186. WebmMuxer::VideoParameters::VideoParameters(
  187. gfx::Size visible_rect_size,
  188. double frame_rate,
  189. VideoCodec codec,
  190. absl::optional<gfx::ColorSpace> color_space)
  191. : visible_rect_size(visible_rect_size),
  192. frame_rate(frame_rate),
  193. codec(codec),
  194. color_space(color_space) {}
  195. WebmMuxer::VideoParameters::VideoParameters(const VideoParameters&) = default;
  196. WebmMuxer::VideoParameters::~VideoParameters() = default;
  197. // -----------------------------------------------------------------------------
  198. // WebmMuxer:
  199. WebmMuxer::WebmMuxer(AudioCodec audio_codec,
  200. bool has_video,
  201. bool has_audio,
  202. std::unique_ptr<Delegate> delegate)
  203. : audio_codec_(audio_codec),
  204. video_codec_(VideoCodec::kUnknown),
  205. video_track_index_(0),
  206. audio_track_index_(0),
  207. has_video_(has_video),
  208. has_audio_(has_audio),
  209. delegate_(std::move(delegate)),
  210. force_one_libwebm_error_(false) {
  211. DCHECK(has_video_ || has_audio_);
  212. DCHECK(delegate_);
  213. DCHECK(audio_codec == AudioCodec::kOpus || audio_codec == AudioCodec::kPCM)
  214. << " Unsupported audio codec: " << GetCodecName(audio_codec);
  215. delegate_->InitSegment(&segment_);
  216. mkvmuxer::SegmentInfo* const info = segment_.GetSegmentInfo();
  217. info->set_writing_app("Chrome");
  218. info->set_muxing_app("Chrome");
  219. // Creation can be done on a different sequence than main activities.
  220. DETACH_FROM_SEQUENCE(sequence_checker_);
  221. }
  222. WebmMuxer::~WebmMuxer() {
  223. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  224. Flush();
  225. }
  226. void WebmMuxer::SetMaximumDurationToForceDataOutput(base::TimeDelta interval) {
  227. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  228. max_data_output_interval_ = std::max(interval, kMinimumForcedClusterDuration);
  229. }
  230. bool WebmMuxer::OnEncodedVideo(const VideoParameters& params,
  231. std::string encoded_data,
  232. std::string encoded_alpha,
  233. base::TimeTicks timestamp,
  234. bool is_key_frame) {
  235. DVLOG(2) << __func__ << " - " << encoded_data.size() << "B";
  236. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  237. DCHECK(params.codec == VideoCodec::kVP8 || params.codec == VideoCodec::kVP9 ||
  238. params.codec == VideoCodec::kH264 || params.codec == VideoCodec::kAV1)
  239. << " Unsupported video codec: " << GetCodecName(params.codec);
  240. DCHECK(video_codec_ == VideoCodec::kUnknown || video_codec_ == params.codec)
  241. << "Unsupported: codec switched, to: " << GetCodecName(params.codec);
  242. if (encoded_data.size() == 0u) {
  243. DLOG(WARNING) << __func__ << ": zero size encoded frame, skipping";
  244. // Some encoders give sporadic zero-size data, see https://crbug.com/716451.
  245. return true;
  246. }
  247. if (!video_track_index_) {
  248. // |track_index_|, cannot be zero (!), initialize WebmMuxer in that case.
  249. // http://www.matroska.org/technical/specs/index.html#Tracks
  250. video_codec_ = params.codec;
  251. AddVideoTrack(params.visible_rect_size, GetFrameRate(params),
  252. params.color_space);
  253. if (first_frame_timestamp_video_.is_null()) {
  254. // Compensate for time in pause spent before the first frame.
  255. first_frame_timestamp_video_ = timestamp - total_time_in_pause_;
  256. last_frame_timestamp_video_ = first_frame_timestamp_video_;
  257. }
  258. // Add codec private for AV1.
  259. if (params.codec == VideoCodec::kAV1 &&
  260. !segment_.GetTrackByNumber(video_track_index_)
  261. ->SetCodecPrivate(av1::codec_private, sizeof(av1::codec_private)))
  262. LOG(ERROR) << __func__ << " failed to set CodecPrivate for AV1.";
  263. }
  264. // TODO(ajose): Support multiple tracks: http://crbug.com/528523
  265. if (has_audio_ && !audio_track_index_) {
  266. DVLOG(1) << __func__ << ": delaying until audio track ready.";
  267. if (is_key_frame) // Upon Key frame reception, empty the encoded queue.
  268. video_frames_.clear();
  269. }
  270. const base::TimeTicks recorded_timestamp =
  271. UpdateLastTimestampMonotonically(timestamp, &last_frame_timestamp_video_);
  272. video_frames_.push_back(EncodedFrame{
  273. std::move(encoded_data), std::move(encoded_alpha),
  274. recorded_timestamp - first_frame_timestamp_video_, is_key_frame});
  275. return PartiallyFlushQueues();
  276. }
  277. bool WebmMuxer::OnEncodedAudio(const media::AudioParameters& params,
  278. std::string encoded_data,
  279. base::TimeTicks timestamp) {
  280. DVLOG(2) << __func__ << " - " << encoded_data.size() << "B";
  281. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  282. MaybeForceNewCluster();
  283. if (!audio_track_index_) {
  284. AddAudioTrack(params);
  285. if (first_frame_timestamp_audio_.is_null()) {
  286. // Compensate for time in pause spent before the first frame.
  287. first_frame_timestamp_audio_ = timestamp - total_time_in_pause_;
  288. last_frame_timestamp_audio_ = first_frame_timestamp_audio_;
  289. }
  290. }
  291. const base::TimeTicks recorded_timestamp =
  292. UpdateLastTimestampMonotonically(timestamp, &last_frame_timestamp_audio_);
  293. audio_frames_.push_back(
  294. EncodedFrame{encoded_data, std::string(),
  295. recorded_timestamp - first_frame_timestamp_audio_,
  296. /*is_keyframe=*/true});
  297. return PartiallyFlushQueues();
  298. }
  299. void WebmMuxer::SetLiveAndEnabled(bool track_live_and_enabled, bool is_video) {
  300. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  301. bool& written_track_live_and_enabled =
  302. is_video ? video_track_live_and_enabled_ : audio_track_live_and_enabled_;
  303. if (written_track_live_and_enabled != track_live_and_enabled) {
  304. DVLOG(1) << __func__ << (is_video ? " video " : " audio ")
  305. << "track live-and-enabled changed to " << track_live_and_enabled;
  306. }
  307. written_track_live_and_enabled = track_live_and_enabled;
  308. }
  309. void WebmMuxer::Pause() {
  310. DVLOG(1) << __func__;
  311. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  312. if (!elapsed_time_in_pause_)
  313. elapsed_time_in_pause_ = std::make_unique<base::ElapsedTimer>();
  314. }
  315. void WebmMuxer::Resume() {
  316. DVLOG(1) << __func__;
  317. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  318. if (elapsed_time_in_pause_) {
  319. total_time_in_pause_ += elapsed_time_in_pause_->Elapsed();
  320. elapsed_time_in_pause_.reset();
  321. }
  322. }
  323. bool WebmMuxer::Flush() {
  324. // Depending on the |delegate_|, it can be either non-seekable (i.e. a live
  325. // stream), or seekable (file mode). So calling |segment_.Finalize()| here is
  326. // needed.
  327. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  328. FlushQueues();
  329. return segment_.Finalize();
  330. }
  331. void WebmMuxer::AddVideoTrack(
  332. const gfx::Size& frame_size,
  333. double frame_rate,
  334. const absl::optional<gfx::ColorSpace>& color_space) {
  335. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  336. DCHECK_EQ(0u, video_track_index_)
  337. << "WebmMuxer can only be initialized once.";
  338. video_track_index_ =
  339. segment_.AddVideoTrack(frame_size.width(), frame_size.height(), 0);
  340. if (video_track_index_ <= 0) { // See https://crbug.com/616391.
  341. NOTREACHED() << "Error adding video track";
  342. return;
  343. }
  344. mkvmuxer::VideoTrack* const video_track =
  345. reinterpret_cast<mkvmuxer::VideoTrack*>(
  346. segment_.GetTrackByNumber(video_track_index_));
  347. if (color_space) {
  348. auto colour = ColorFromColorSpace(*color_space);
  349. if (colour)
  350. video_track->SetColour(*colour);
  351. }
  352. DCHECK(video_track);
  353. video_track->set_codec_id(MkvCodeIcForMediaVideoCodecId(video_codec_));
  354. DCHECK_EQ(0ull, video_track->crop_right());
  355. DCHECK_EQ(0ull, video_track->crop_left());
  356. DCHECK_EQ(0ull, video_track->crop_top());
  357. DCHECK_EQ(0ull, video_track->crop_bottom());
  358. DCHECK_EQ(0.0f, video_track->frame_rate());
  359. // Segment's timestamps should be in milliseconds, DCHECK it. See
  360. // http://www.webmproject.org/docs/container/#muxer-guidelines
  361. DCHECK_EQ(1000000ull, segment_.GetSegmentInfo()->timecode_scale());
  362. // Set alpha channel parameters for only VPX (crbug.com/711825).
  363. if (video_codec_ == VideoCodec::kH264)
  364. return;
  365. video_track->SetAlphaMode(mkvmuxer::VideoTrack::kAlpha);
  366. // Alpha channel, if present, is stored in a BlockAdditional next to the
  367. // associated opaque Block, see
  368. // https://matroska.org/technical/specs/index.html#BlockAdditional.
  369. // This follows Method 1 for VP8 encoding of A-channel described on
  370. // http://wiki.webmproject.org/alpha-channel.
  371. video_track->set_max_block_additional_id(1);
  372. }
  373. void WebmMuxer::AddAudioTrack(const media::AudioParameters& params) {
  374. DVLOG(1) << __func__ << " " << params.AsHumanReadableString();
  375. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  376. DCHECK_EQ(0u, audio_track_index_)
  377. << "WebmMuxer audio can only be initialised once.";
  378. audio_track_index_ =
  379. segment_.AddAudioTrack(params.sample_rate(), params.channels(), 0);
  380. if (audio_track_index_ <= 0) { // See https://crbug.com/616391.
  381. NOTREACHED() << "Error adding audio track";
  382. return;
  383. }
  384. mkvmuxer::AudioTrack* const audio_track =
  385. reinterpret_cast<mkvmuxer::AudioTrack*>(
  386. segment_.GetTrackByNumber(audio_track_index_));
  387. DCHECK(audio_track);
  388. DCHECK_EQ(params.sample_rate(), audio_track->sample_rate());
  389. DCHECK_EQ(params.channels(), static_cast<int>(audio_track->channels()));
  390. DCHECK_LE(params.channels(), 2)
  391. << "Only 1 or 2 channels supported, requested " << params.channels();
  392. // Audio data is always pcm_f32le.
  393. audio_track->set_bit_depth(32u);
  394. if (audio_codec_ == AudioCodec::kOpus) {
  395. audio_track->set_codec_id(mkvmuxer::Tracks::kOpusCodecId);
  396. uint8_t opus_header[OPUS_EXTRADATA_SIZE];
  397. WriteOpusHeader(params, opus_header);
  398. if (!audio_track->SetCodecPrivate(opus_header, OPUS_EXTRADATA_SIZE))
  399. LOG(ERROR) << __func__ << ": failed to set opus header.";
  400. // Segment's timestamps should be in milliseconds, DCHECK it. See
  401. // http://www.webmproject.org/docs/container/#muxer-guidelines
  402. DCHECK_EQ(1000000ull, segment_.GetSegmentInfo()->timecode_scale());
  403. } else if (audio_codec_ == AudioCodec::kPCM) {
  404. audio_track->set_codec_id(kPcmCodecId);
  405. }
  406. }
  407. void WebmMuxer::FlushQueues() {
  408. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  409. while ((!video_frames_.empty() || !audio_frames_.empty()) &&
  410. FlushNextFrame()) {
  411. }
  412. }
  413. bool WebmMuxer::PartiallyFlushQueues() {
  414. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  415. // Punt writing until all tracks have been created.
  416. if ((has_audio_ && !audio_track_index_) ||
  417. (has_video_ && !video_track_index_)) {
  418. return true;
  419. }
  420. bool result = true;
  421. // We strictly sort by timestamp unless a track is not live-and-enabled. In
  422. // that case we relax this and allow drainage of the live-and-enabled leg.
  423. while ((!has_video_ || !video_frames_.empty() ||
  424. !video_track_live_and_enabled_) &&
  425. (!has_audio_ || !audio_frames_.empty() ||
  426. !audio_track_live_and_enabled_) &&
  427. result) {
  428. if (video_frames_.empty() && audio_frames_.empty())
  429. return true;
  430. result = FlushNextFrame();
  431. }
  432. return result;
  433. }
  434. bool WebmMuxer::FlushNextFrame() {
  435. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  436. base::TimeDelta min_timestamp = base::TimeDelta::Max();
  437. base::circular_deque<EncodedFrame>* queue = &video_frames_;
  438. uint8_t track_index = video_track_index_;
  439. if (!video_frames_.empty())
  440. min_timestamp = video_frames_.front().relative_timestamp;
  441. if (!audio_frames_.empty() &&
  442. audio_frames_.front().relative_timestamp < min_timestamp) {
  443. queue = &audio_frames_;
  444. track_index = audio_track_index_;
  445. }
  446. EncodedFrame frame = std::move(queue->front());
  447. queue->pop_front();
  448. // The logic tracking live-and-enabled that temporarily relaxes the strict
  449. // timestamp sorting allows for draining a track's queue completely in the
  450. // presence of the other track being muted. When the muted track becomes
  451. // live-and-enabled again the sorting recommences. However, tracks get encoded
  452. // data before live-and-enabled transitions to true. This can lead to us
  453. // emitting non-monotonic timestamps to the muxer, which results in an error
  454. // return. Fix this by enforcing monotonicity by rewriting timestamps.
  455. base::TimeDelta relative_timestamp = frame.relative_timestamp;
  456. DLOG_IF(WARNING, relative_timestamp < last_timestamp_written_)
  457. << "Enforced a monotonically increasing timestamp. Last written "
  458. << last_timestamp_written_ << " new " << relative_timestamp;
  459. relative_timestamp = std::max(relative_timestamp, last_timestamp_written_);
  460. last_timestamp_written_ = relative_timestamp;
  461. auto recorded_timestamp = relative_timestamp.InMicroseconds() *
  462. base::Time::kNanosecondsPerMicrosecond;
  463. if (force_one_libwebm_error_) {
  464. DVLOG(1) << "Forcing a libwebm error";
  465. force_one_libwebm_error_ = false;
  466. return false;
  467. }
  468. DCHECK(frame.data.data());
  469. bool result =
  470. frame.alpha_data.empty()
  471. ? segment_.AddFrame(
  472. reinterpret_cast<const uint8_t*>(frame.data.data()),
  473. frame.data.size(), track_index, recorded_timestamp,
  474. frame.is_keyframe)
  475. : segment_.AddFrameWithAdditional(
  476. reinterpret_cast<const uint8_t*>(frame.data.data()),
  477. frame.data.size(),
  478. reinterpret_cast<const uint8_t*>(frame.alpha_data.data()),
  479. frame.alpha_data.size(), 1 /* add_id */, track_index,
  480. recorded_timestamp, frame.is_keyframe);
  481. return result;
  482. }
  483. base::TimeTicks WebmMuxer::UpdateLastTimestampMonotonically(
  484. base::TimeTicks timestamp,
  485. base::TimeTicks* last_timestamp) {
  486. base::TimeTicks compensated_timestamp = timestamp - total_time_in_pause_;
  487. // In theory, time increases monotonically. In practice, it does not.
  488. // See http://crbug/618407.
  489. DLOG_IF(WARNING, compensated_timestamp < *last_timestamp)
  490. << "Encountered a non-monotonically increasing timestamp. Was: "
  491. << *last_timestamp << ", compensated: " << compensated_timestamp
  492. << ", uncompensated: " << timestamp;
  493. *last_timestamp = std::max(*last_timestamp, compensated_timestamp);
  494. return *last_timestamp;
  495. }
  496. void WebmMuxer::MaybeForceNewCluster() {
  497. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  498. if (!has_video_ || max_data_output_interval_.is_zero() ||
  499. delegate_->last_data_output_timestamp().is_null()) {
  500. return;
  501. }
  502. if (base::TimeTicks::Now() - delegate_->last_data_output_timestamp() >=
  503. max_data_output_interval_) {
  504. segment_.ForceNewClusterOnNextFrame();
  505. }
  506. }
  507. } // namespace media