From 97fc9828f01826e99e83469875870bc7f1a477c5 Mon Sep 17 00:00:00 2001 From: Kongqun Yang Date: Wed, 28 Sep 2016 17:13:15 -0700 Subject: [PATCH] [WebM] Move index segment after init segment Cues used to be generated in the end of the file; if http range request is not supported, clients have to download the whole file to get to the cues. This CL updated TwoPassSingleSegmentSegmenter to write cues after writing webm header. This CL also updates libwebm dependency to latest. Closes #159 Change-Id: Ic73548e1b872e6b13a37627707e7d0ff3e556877 --- DEPS | 2 +- .../testdata/bear-320x240-opus-golden.webm | Bin 26604 -> 26604 bytes .../testdata/bear-320x240-vorbis-golden.webm | Bin 23999 -> 23999 bytes .../bear-320x240-vorbis-webm-golden.mpd | 2 +- .../testdata/bear-320x240-vp9-golden.webm | Bin 69545 -> 69546 bytes .../bear-320x240-vp9-opus-webm-golden.mpd | 6 +- .../bear-640x360-vp8-cenc-golden.webm | Bin 115275 -> 115275 bytes .../bear-640x360-vp8-cenc-webm-golden.mpd | 2 +- .../testdata/bear-640x360-vp8-golden.webm | Bin 114725 -> 114726 bytes .../testdata/bear-640x360-vp8-webm-golden.mpd | 4 +- .../bear-640x360-vp9-altref-dec-golden.webm | Bin 91692 -> 91693 bytes .../bear-640x360-vp9-altref-enc-golden.webm | Bin 92562 -> 92562 bytes .../testdata/bear-vp9-blockgroup-golden.webm | Bin 67362 -> 67363 bytes .../webm/encrypted_segmenter_unittest.cc | 59 +++++----- packager/media/formats/webm/seek_head.cc | 111 ++++++++---------- packager/media/formats/webm/seek_head.h | 29 +++-- .../formats/webm/single_segment_segmenter.h | 1 + .../webm/single_segment_segmenter_unittest.cc | 79 +++++-------- .../webm/two_pass_single_segment_segmenter.cc | 53 +++++++-- packager/media/formats/webm/webm.gyp | 1 + packager/media/formats/webm/webm_muxer.cc | 2 - packager/third_party/libwebm/libwebm.gyp | 25 +++- 22 files changed, 202 insertions(+), 174 deletions(-) diff --git a/DEPS b/DEPS index dc917af8b4..685ea75291 100644 --- a/DEPS +++ b/DEPS @@ -50,7 +50,7 @@ deps = { Var("chromium_git") + "/chromium/deps/icu@ef5c735307d0f86c7622f69620994c9468beba99", "src/packager/third_party/libwebm/src": - Var("chromium_git") + "/webm/libwebm@1ad314e297a43966605c4ef23a6442bb58e1d9be", + Var("chromium_git") + "/webm/libwebm@d6af52a1e688fade2e2d22b6d9b0c82f10d38e0b", "src/packager/third_party/modp_b64": Var("chromium_git") + "/chromium/src/third_party/modp_b64@aae60754fa997799e8037f5e8ca1f56d58df763d", #405651 diff --git a/packager/app/test/testdata/bear-320x240-opus-golden.webm b/packager/app/test/testdata/bear-320x240-opus-golden.webm index 3dc7e7e532b5d25aedf907ab135d49be6a62064a..ed155bc28b7daddb6cbc8424c8e137c4504b48c4 100644 GIT binary patch delta 96 zcmaEJp7G6j#tHI_EE5&XC1iqkX9uroVifk>-4ndJMc#Q^DTwPbG5?AoNN&yUp3RL6 k+uOf4GJOOJ@9y2)#QXxvRlNt|vR{C5J!WopW~@j90QTN2yZ`_I delta 96 zcmaEJp7G6j#tHI_OcNE%CFGsAl?JbAVifk>-4ndJMJ9N6Hjta%Ju&~v<`Tw=G((Wc ln%zB{8yU8@e{W>^2voYecXJc-3n*80Er`p00m}8r008$MEd>Ao diff --git a/packager/app/test/testdata/bear-320x240-vorbis-golden.webm b/packager/app/test/testdata/bear-320x240-vorbis-golden.webm index 57b0b1e60509c244ebae7c7139e871862d41792f..b764a53aa8e11caf689582011394f687d065bd07 100644 GIT binary patch delta 97 zcmdnLn{oec#tHI_EE5&XC1iqkX9uro65#jU-4ndJMc#Q^DTwQ^G5-R;AxLt~?w-w! l4BOkkH!^+%itp~-+{F9^%GF&2; output_audio.webm - + diff --git a/packager/app/test/testdata/bear-320x240-vp9-golden.webm b/packager/app/test/testdata/bear-320x240-vp9-golden.webm index 3b18d7666fb9a718f0af2d19b4dd2678e4537901..0618a8009c5c36b7ab4022af60daf5ce9c70db08 100644 GIT binary patch delta 125 zcmZ2EpJmm2mI?A=EKaSBOcKEj>5L3Oz{p=E=sUY)=R{K_Ntxi?*}-cXfBWw430~bI z@4T%vcuf5L3Oz{p=I=sUY)*F;k#DS78@rNL_&fBWw44PMVPy2qQFVLU_eRE#jlXyIZf;`X+ujc1 PH3fYFaoN8^xd+_<{981u diff --git a/packager/app/test/testdata/bear-320x240-vp9-opus-webm-golden.mpd b/packager/app/test/testdata/bear-320x240-vp9-opus-webm-golden.mpd index 3efd0d4cfd..39376b4a83 100644 --- a/packager/app/test/testdata/bear-320x240-vp9-opus-webm-golden.mpd +++ b/packager/app/test/testdata/bear-320x240-vp9-opus-webm-golden.mpd @@ -6,15 +6,15 @@ output_audio.webm - + - + output_video.webm - + diff --git a/packager/app/test/testdata/bear-640x360-vp8-cenc-golden.webm b/packager/app/test/testdata/bear-640x360-vp8-cenc-golden.webm index 6a90df191b484a4989d6830cf1be305fc82c68ed..91d57aa5fde18812c0b94729958e8b28913865d4 100644 GIT binary patch delta 109 zcmX@z!hX7ieS$nA%R~hc#$6Llm84~YcV`E$X<}6I-Q5$sx<%f3TPcVe_GSjd#G*$= zAnCQcdp0*pZEyeH$oLT`v%7b56ALqx`yzaI-{vOvH`_ZvvdxUi|C&P>w}&t?9z6g6 Dy_hiL delta 112 zcmX@z!hX7ieS$nA(?kUk#@!Q5m89jJx0MF3X<}6I-Q63!xv?%vH!EX+{u3)S6yo156*Z0`Wc JHZvxl0RUfAAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA== output_video.webm - + diff --git a/packager/app/test/testdata/bear-640x360-vp8-golden.webm b/packager/app/test/testdata/bear-640x360-vp8-golden.webm index d9f9436ac957d7ac5a15ead19c2bac03c6ed3700..5fa81b7d881eac4da266c5267cea04f5a04b3f69 100644 GIT binary patch delta 126 zcmZ45z`m@3eS*9gi&JYOlSFVsIwJ!RFz){*=sUY)=R{K_Ntxi?*}-cXKl<+O30~bI z@4T%vcufP0leQ#v^*!XdG@8%{J=I!ku XUeog{yZbgbvA@~g0pd0@CglPE3V%22 diff --git a/packager/app/test/testdata/bear-640x360-vp8-webm-golden.mpd b/packager/app/test/testdata/bear-640x360-vp8-webm-golden.mpd index c108e7f2b2..68a6b1d3ec 100644 --- a/packager/app/test/testdata/bear-640x360-vp8-webm-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-vp8-webm-golden.mpd @@ -3,9 +3,9 @@ - + output_video.webm - + diff --git a/packager/app/test/testdata/bear-640x360-vp9-altref-dec-golden.webm b/packager/app/test/testdata/bear-640x360-vp9-altref-dec-golden.webm index d5df056ea46e596ed8ab8aa21596716a840d5a32..ae1c97b480560a7cf9b2ed4846c214c2136a8eb0 100644 GIT binary patch delta 126 zcmZ2;hIQ>3)(P@rEKaSBOcKEj>5L3Oz?k}1(06vp&WWZRzl3rpgzoO!+{Auq Vdk09inUSBrS(kCUE+gZ)6ab*qHQN9H delta 123 zcmZ2`hIP#u)(P@rOirzhOcKEj>5L3Oz?k|+(06vpu8F2fQu5B*N`u!l{`KA68@#$j zCU|#t@S0}E)YETfOf1~f9M8Bto{{lfiXlj1&F=2ajSSn{zBe*{Z2Y^scXJc-%kAwT XUeg5S-F=&z*e`AG0CAfc`R)P$8@D%! diff --git a/packager/app/test/testdata/bear-640x360-vp9-altref-enc-golden.webm b/packager/app/test/testdata/bear-640x360-vp9-altref-enc-golden.webm index 06b7a345670d4fdc13750ccc8325a7789812b47c..6493afa80cdebc135acd8b9dc27bc0df7f8f75df 100644 GIT binary patch delta 108 zcmbPqnRU`-)(P^AEE5$(7K1wDZKWV?(wi9!6N}y% zfuz^&?%CYPu)Y0zBjZP)%-c}mCriszOcXw~_>K2*c-Pyrwni(?<-b|lZ zC_b_Cdh;a4<&zj0GmJn|Yj^i-Ze-Zr{=Jd$BT&!o-px(SFQMECCA<4JH?d#Z-T{(r IW)%1a0Pkcm!vFvP diff --git a/packager/app/test/testdata/bear-vp9-blockgroup-golden.webm b/packager/app/test/testdata/bear-vp9-blockgroup-golden.webm index 9ab11de17049dbc71feb1a8194089b1b63426842..aeda2e9b368f47be7a5d5b7ac124b0054823512b 100644 GIT binary patch delta 95 zcmZ3~$FjJOWrDmIi&JYOlSFVsIwJ!RFtU9X^qpO@bE2t|q)hPc?BF$xzkPT21g~z9 wcivVSyrzkf@6F7Kg%T4x77K&q`*!ziZe-Zr{=Jd$BT%kcn{m4~BjZz609t7y_5c6? delta 92 zcmZ47$Fiu8WrDmIlT&LWlSFVsIwJ!RFtU9T^qpO@Yoe)=l)Uq{(%?0XzkPT22Cr_B u3ErI@yr!9v?ZKNF6AO1W+c9ppV`O~FDg=_~-QB&pkzsq=_eRE#jlTirm?Xgf diff --git a/packager/media/formats/webm/encrypted_segmenter_unittest.cc b/packager/media/formats/webm/encrypted_segmenter_unittest.cc index 8af360d76f..b263f94d00 100644 --- a/packager/media/formats/webm/encrypted_segmenter_unittest.cc +++ b/packager/media/formats/webm/encrypted_segmenter_unittest.cc @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "packager/media/formats/webm/single_segment_segmenter.h" #include "packager/media/formats/webm/two_pass_single_segment_segmenter.h" #include @@ -39,16 +38,16 @@ const uint8_t kBasicSupportData[] = { 0x53, 0xac, 0x81, 0xb6, // ID: Seek, Payload Size: 12 0x4d, 0xbb, 0x8c, - // SeekID: binary(4) (Cluster) - 0x53, 0xab, 0x84, 0x1f, 0x43, 0xb6, 0x75, + // SeekID: binary(4) (Cues) + 0x53, 0xab, 0x84, 0x1c, 0x53, 0xbb, 0x6b, // SeekPosition: 279 0x53, 0xac, 0x82, 0x01, 0x17, // ID: Seek, Payload Size: 12 0x4d, 0xbb, 0x8c, - // SeekID: binary(4) (Cues) - 0x53, 0xab, 0x84, 0x1c, 0x53, 0xbb, 0x6b, - // SeekPosition: 398 - 0x53, 0xac, 0x82, 0x01, 0x8e, + // SeekID: binary(4) (Cluster) + 0x53, 0xab, 0x84, 0x1f, 0x43, 0xb6, 0x75, + // SeekPosition: 313 + 0x53, 0xac, 0x82, 0x01, 0x39, // ID: Void, Payload Size: 24 0xec, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -115,6 +114,28 @@ const uint8_t kBasicSupportData[] = { 0x54, 0xb0, 0x81, 0x64, // DisplayHeight: 100 0x54, 0xba, 0x81, 0x64, + // ID: Cues, Payload Size: 29 + 0x1c, 0x53, 0xbb, 0x6b, 0x9d, + // ID: CuePoint, Payload Size: 12 + 0xbb, 0x8c, + // CueTime: 0 + 0xb3, 0x81, 0x00, + // ID: CueTrackPositions, Payload Size: 7 + 0xb7, 0x87, + // CueTrack: 1 + 0xf7, 0x81, 0x01, + // CueClusterPosition: 313 + 0xf1, 0x82, 0x01, 0x39, + // ID: CuePoint, Payload Size: 13 + 0xbb, 0x8d, + // CueTime: 3000 + 0xb3, 0x82, 0x0b, 0xb8, + // ID: CueTrackPositions, Payload Size: 7 + 0xb7, 0x87, + // CueTrack: 1 + 0xf7, 0x81, 0x01, + // CueClusterPosition: 370 + 0xf1, 0x82, 0x01, 0x72, // ID: Cluster, Payload Size: 45 0x1f, 0x43, 0xb6, 0x75, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, // Timecode: 0 @@ -165,28 +186,6 @@ const uint8_t kBasicSupportData[] = { 0xbf, 0x38, 0x72, 0x20, 0xac, // BlockDuration: 1000 0x9b, 0x82, 0x03, 0xe8, - // ID: Cues, Payload Size: 29 - 0x1c, 0x53, 0xbb, 0x6b, 0x9d, - // ID: CuePoint, Payload Size: 12 - 0xbb, 0x8c, - // CueTime: 0 - 0xb3, 0x81, 0x00, - // ID: CueTrackPositions, Payload Size: 7 - 0xb7, 0x87, - // CueTrack: 1 - 0xf7, 0x81, 0x01, - // CueClusterPosition: 279 - 0xf1, 0x82, 0x01, 0x17, - // ID: CuePoint, Payload Size: 13 - 0xbb, 0x8d, - // CueTime: 3000 - 0xb3, 0x82, 0x0b, 0xb8, - // ID: CueTrackPositions, Payload Size: 7 - 0xb7, 0x87, - // CueTrack: 1 - 0xf7, 0x81, 0x01, - // CueClusterPosition: 336 - 0xf1, 0x82, 0x01, 0x50, }; } // namespace @@ -200,7 +199,7 @@ class EncrypedSegmenterTest : public SegmentTestBase { key_source_ = FixedKeySource::CreateFromHexStrings(kKeyId, kKey, kPsshData, kIv); ASSERT_NO_FATAL_FAILURE( - CreateAndInitializeSegmenter( + CreateAndInitializeSegmenter( options, info_.get(), key_source_.get(), &segmenter_)); } diff --git a/packager/media/formats/webm/seek_head.cc b/packager/media/formats/webm/seek_head.cc index 15f21ea1b5..7579bd9513 100644 --- a/packager/media/formats/webm/seek_head.cc +++ b/packager/media/formats/webm/seek_head.cc @@ -6,19 +6,23 @@ #include "packager/media/formats/webm/seek_head.h" +#include #include +#include "packager/base/logging.h" #include "packager/third_party/libwebm/src/mkvmuxerutil.hpp" #include "packager/third_party/libwebm/src/webmids.hpp" namespace shaka { namespace media { namespace { -const mkvmuxer::uint64 kElementIds[] = { - mkvmuxer::kMkvInfo, mkvmuxer::kMkvTracks, mkvmuxer::kMkvCluster, - mkvmuxer::kMkvCues, -}; -const int kElementIdCount = arraysize(kElementIds); + +// Cluster, Cues, Info, Tracks. +const size_t kElementIdCount = 4u; + +uint64_t EbmlMasterElementWithPayloadSize(mkvmuxer::MkvId id, uint64_t payload_size) { + return EbmlMasterElementSize(id, payload_size) + payload_size; +} uint64_t MaxSeekEntrySize() { const uint64_t max_entry_payload_size = @@ -27,57 +31,46 @@ uint64_t MaxSeekEntrySize() { static_cast(std::numeric_limits::max())) + EbmlElementSize(mkvmuxer::kMkvSeekPosition, std::numeric_limits::max()); - const uint64_t max_entry_size = - EbmlMasterElementSize(mkvmuxer::kMkvSeek, max_entry_payload_size) + - max_entry_payload_size; - - return max_entry_size; + return EbmlMasterElementWithPayloadSize(mkvmuxer::kMkvSeek, + max_entry_payload_size); } + } // namespace SeekHead::SeekHead() - : cluster_pos_(-1), - cues_pos_(-1), - info_pos_(-1), - tracks_pos_(-1), - wrote_void_(false) {} + : total_void_size_(EbmlMasterElementWithPayloadSize( + mkvmuxer::kMkvSeekHead, + kElementIdCount * MaxSeekEntrySize())) {} SeekHead::~SeekHead() {} bool SeekHead::Write(mkvmuxer::IMkvWriter* writer) { - std::vector element_sizes; - const uint64_t payload_size = GetPayloadSize(&element_sizes); - - if (payload_size == 0) { + std::vector seek_elements = CreateSeekElements(); + if (seek_elements.empty()) return true; + + uint64_t payload_size = 0; + for (const SeekHead::SeekElement& seek_element : seek_elements) { + payload_size += + seek_element.size + + EbmlMasterElementSize(mkvmuxer::kMkvSeek, seek_element.size); } const int64_t start_pos = writer->Position(); if (!WriteEbmlMasterElement(writer, mkvmuxer::kMkvSeekHead, payload_size)) return false; - const int64_t positions[] = {info_pos_, tracks_pos_, cluster_pos_, cues_pos_}; - for (int i = 0; i < kElementIdCount; ++i) { - if (element_sizes[i] == 0) - continue; - - const mkvmuxer::uint64 position = - static_cast(positions[i]); - if (!WriteEbmlMasterElement(writer, mkvmuxer::kMkvSeek, element_sizes[i]) || - !WriteEbmlElement(writer, mkvmuxer::kMkvSeekID, kElementIds[i]) || - !WriteEbmlElement(writer, mkvmuxer::kMkvSeekPosition, position)) + for (const SeekHead::SeekElement& element : seek_elements) { + if (!WriteEbmlMasterElement(writer, mkvmuxer::kMkvSeek, element.size) || + !WriteEbmlElement(writer, mkvmuxer::kMkvSeekID, element.id) || + !WriteEbmlElement(writer, mkvmuxer::kMkvSeekPosition, element.position)) return false; } // If we wrote void before, then fill in the extra with void. if (wrote_void_) { - const uint64_t max_payload_size = kElementIdCount * MaxSeekEntrySize(); - const uint64_t total_void_size = - EbmlMasterElementSize(mkvmuxer::kMkvSeekHead, max_payload_size) + - max_payload_size; - const uint64_t extra_void = - total_void_size - (writer->Position() - start_pos); + total_void_size_ - (writer->Position() - start_pos); if (!WriteVoidElement(writer, extra_void)) return false; } @@ -86,38 +79,36 @@ bool SeekHead::Write(mkvmuxer::IMkvWriter* writer) { } bool SeekHead::WriteVoid(mkvmuxer::IMkvWriter* writer) { - const uint64_t payload_size = kElementIdCount * MaxSeekEntrySize(); - const uint64_t total_size = - EbmlMasterElementSize(mkvmuxer::kMkvSeekHead, payload_size) + - payload_size; - - wrote_void_ = true; - const uint64_t written = WriteVoidElement(writer, total_size); + const uint64_t written = WriteVoidElement(writer, total_void_size_); if (!written) return false; - + wrote_void_ = true; return true; } -uint64_t SeekHead::GetPayloadSize(std::vector* data) { - const int64_t positions[] = {info_pos_, tracks_pos_, cluster_pos_, cues_pos_}; - uint64_t total_payload_size = 0; - data->resize(kElementIdCount); - for (int i = 0; i < kElementIdCount; ++i) { - if (positions[i] < 0) { - (*data)[i] = 0; - continue; - } +std::vector SeekHead::CreateSeekElements() { + std::vector seek_elements; + if (info_pos_ != 0) + seek_elements.emplace_back(mkvmuxer::kMkvInfo, info_pos_); + if (tracks_pos_ != 0) + seek_elements.emplace_back(mkvmuxer::kMkvTracks, tracks_pos_); + if (cues_pos_ != 0) + seek_elements.emplace_back(mkvmuxer::kMkvCues, cues_pos_); + if (cluster_pos_ != 0) + seek_elements.emplace_back(mkvmuxer::kMkvCluster, cluster_pos_); + DCHECK_LE(seek_elements.size(), kElementIdCount); - const mkvmuxer::uint64 position = - static_cast(positions[i]); - (*data)[i] = EbmlElementSize(mkvmuxer::kMkvSeekID, kElementIds[i]) + - EbmlElementSize(mkvmuxer::kMkvSeekPosition, position); - total_payload_size += - data->at(i) + EbmlMasterElementSize(mkvmuxer::kMkvSeek, data->at(i)); + std::sort(seek_elements.begin(), seek_elements.end(), + [](const SeekHead::SeekElement& left, + const SeekHead::SeekElement& right) { + return left.position < right.position; + }); + for (SeekHead::SeekElement& element : seek_elements) { + element.size = + EbmlElementSize(mkvmuxer::kMkvSeekID, element.id) + + EbmlElementSize(mkvmuxer::kMkvSeekPosition, element.position); } - - return total_payload_size; + return seek_elements; } } // namespace media diff --git a/packager/media/formats/webm/seek_head.h b/packager/media/formats/webm/seek_head.h index ccd5188d85..a4c3c019d6 100644 --- a/packager/media/formats/webm/seek_head.h +++ b/packager/media/formats/webm/seek_head.h @@ -10,7 +10,6 @@ #include #include -#include "base/macros.h" #include "packager/third_party/libwebm/src/mkvmuxer.hpp" namespace shaka { @@ -35,15 +34,29 @@ class SeekHead { void set_tracks_pos(uint64_t pos) { tracks_pos_ = pos; } private: - uint64_t GetPayloadSize(std::vector* data); + SeekHead(const SeekHead&) = delete; + SeekHead& operator=(const SeekHead&) = delete; - int64_t cluster_pos_; - int64_t cues_pos_; - int64_t info_pos_; - int64_t tracks_pos_; - bool wrote_void_; + struct SeekElement { + mkvmuxer::uint64 id; + mkvmuxer::uint64 position; + mkvmuxer::uint64 size; - DISALLOW_COPY_AND_ASSIGN(SeekHead); + SeekElement(uint64_t seek_id, uint64_t seek_position) + : id(seek_id), position(seek_position), size(0) {} + }; + + // Create seek element vector from positions. + std::vector CreateSeekElements(); + + // In practice, these positions, if set, will never be 0, so we use a zero + // value to denote that they are not set. + uint64_t cluster_pos_ = 0; + uint64_t cues_pos_ = 0; + uint64_t info_pos_ = 0; + uint64_t tracks_pos_ = 0; + bool wrote_void_ = false; + const uint64_t total_void_size_ = 0; }; } // namespace media diff --git a/packager/media/formats/webm/single_segment_segmenter.h b/packager/media/formats/webm/single_segment_segmenter.h index 1044a810d5..ff8efd54f2 100644 --- a/packager/media/formats/webm/single_segment_segmenter.h +++ b/packager/media/formats/webm/single_segment_segmenter.h @@ -36,6 +36,7 @@ class SingleSegmentSegmenter : public Segmenter { protected: MkvWriter* writer() { return writer_.get(); } + uint64_t init_end() { return init_end_; } void set_init_end(uint64_t end) { init_end_ = end; } void set_index_start(uint64_t start) { index_start_ = start; } void set_index_end(uint64_t end) { index_end_ = end; } diff --git a/packager/media/formats/webm/single_segment_segmenter_unittest.cc b/packager/media/formats/webm/single_segment_segmenter_unittest.cc index f24bda7630..7f6d01a219 100644 --- a/packager/media/formats/webm/single_segment_segmenter_unittest.cc +++ b/packager/media/formats/webm/single_segment_segmenter_unittest.cc @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "packager/media/formats/webm/single_segment_segmenter.h" #include "packager/media/formats/webm/two_pass_single_segment_segmenter.h" #include @@ -20,7 +19,7 @@ const uint8_t kBasicSupportData[] = { // ID: Segment, Payload Size: 343 0x18, 0x53, 0x80, 0x67, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x57, // ID: SeekHead, Payload Size: 57 - 0x11, 0x4d, 0x9b, 0x74, 0xb9, + 0x11, 0x4d, 0x9b, 0x74, 0xb8, // ID: Seek, Payload Size: 11 0x4d, 0xbb, 0x8b, // SeekID: binary(4) (Info) @@ -33,22 +32,22 @@ const uint8_t kBasicSupportData[] = { 0x53, 0xab, 0x84, 0x16, 0x54, 0xae, 0x6b, // SeekPosition: 182 0x53, 0xac, 0x81, 0xb6, + // ID: Seek, Payload Size: 12 + 0x4d, 0xbb, 0x8b, + // SeekID: binary(4) (Cues) + 0x53, 0xab, 0x84, 0x1c, 0x53, 0xbb, 0x6b, + // SeekPosition: 228 + 0x53, 0xac, 0x81, 0xe4, // ID: Seek, Payload Size: 11 0x4d, 0xbb, 0x8b, // SeekID: binary(4) (Cluster) 0x53, 0xab, 0x84, 0x1f, 0x43, 0xb6, 0x75, - // SeekPosition: 228 - 0x53, 0xac, 0x81, 0xe4, - // ID: Seek, Payload Size: 12 - 0x4d, 0xbb, 0x8c, - // SeekID: binary(4) (Cues) - 0x53, 0xab, 0x84, 0x1c, 0x53, 0xbb, 0x6b, - // SeekPosition: 325 - 0x53, 0xac, 0x82, 0x01, 0x45, - // ID: Void, Payload Size: 25 - 0xec, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // SeekPosition: 246 + 0x53, 0xac, 0x81, 0xf6, + // ID: Void, Payload Size: 26 + 0xec, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, // ID: Info, Payload Size: 88 0x15, 0x49, 0xa9, 0x66, 0xd8, // TimecodeScale: 1000000 @@ -89,6 +88,18 @@ const uint8_t kBasicSupportData[] = { 0x54, 0xb0, 0x81, 0x64, // DisplayHeight: 100 0x54, 0xba, 0x81, 0x64, + // ID: Cues, Payload Size: 13 + 0x1c, 0x53, 0xbb, 0x6b, 0x8d, + // ID: CuePoint, Payload Size: 11 + 0xbb, 0x8b, + // CueTime: 0 + 0xb3, 0x81, 0x00, + // ID: CueTrackPositions, Payload Size: 6 + 0xb7, 0x86, + // CueTrack: 1 + 0xf7, 0x81, 0x01, + // CueClusterPosition: 246 + 0xf1, 0x81, 0xf6, // ID: Cluster, Payload Size: 85 0x1f, 0x43, 0xb6, 0x75, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, // Timecode: 0 @@ -117,48 +128,26 @@ const uint8_t kBasicSupportData[] = { 0xa1, 0x89, 0x81, 0x0f, 0xa0, 0x00, 0xde, 0xad, 0xbe, 0xef, 0x00, // BlockDuration: 1000 0x9b, 0x82, 0x03, 0xe8, - // ID: Cues, Payload Size: 13 - 0x1c, 0x53, 0xbb, 0x6b, 0x8d, - // ID: CuePoint, Payload Size: 11 - 0xbb, 0x8b, - // CueTime: 0 - 0xb3, 0x81, 0x00, - // ID: CueTrackPositions, Payload Size: 6 - 0xb7, 0x86, - // CueTrack: 1 - 0xf7, 0x81, 0x01, - // CueClusterPosition: 228 - 0xf1, 0x81, 0xe4 }; } // namespace -// This is a parameterized test that tests both SingleSegmentSegmenter and -// TwoPassSingleSegmentSegmenter, since they should provide the exact same -// output. -class SingleSegmentSegmenterTest : public SegmentTestBase, - public ::testing::WithParamInterface { +class SingleSegmentSegmenterTest : public SegmentTestBase { public: SingleSegmentSegmenterTest() : info_(CreateVideoStreamInfo()) {} protected: void InitializeSegmenter(const MuxerOptions& options) { - if (!GetParam()) { - ASSERT_NO_FATAL_FAILURE( - CreateAndInitializeSegmenter( - options, info_.get(), NULL, &segmenter_)); - } else { - ASSERT_NO_FATAL_FAILURE( - CreateAndInitializeSegmenter( - options, info_.get(), NULL, &segmenter_)); - } + ASSERT_NO_FATAL_FAILURE( + CreateAndInitializeSegmenter( + options, info_.get(), NULL, &segmenter_)); } scoped_refptr info_; std::unique_ptr segmenter_; }; -TEST_P(SingleSegmentSegmenterTest, BasicSupport) { +TEST_F(SingleSegmentSegmenterTest, BasicSupport) { MuxerOptions options = CreateMuxerOptions(); ASSERT_NO_FATAL_FAILURE(InitializeSegmenter(options)); @@ -175,7 +164,7 @@ TEST_P(SingleSegmentSegmenterTest, BasicSupport) { ASSERT_FILE_ENDS_WITH(OutputFileName().c_str(), kBasicSupportData); } -TEST_P(SingleSegmentSegmenterTest, SplitsClustersOnSegmentDuration) { +TEST_F(SingleSegmentSegmenterTest, SplitsClustersOnSegmentDuration) { MuxerOptions options = CreateMuxerOptions(); options.segment_duration = 4.5; // seconds ASSERT_NO_FATAL_FAILURE(InitializeSegmenter(options)); @@ -196,7 +185,7 @@ TEST_P(SingleSegmentSegmenterTest, SplitsClustersOnSegmentDuration) { EXPECT_EQ(3, parser.GetFrameCountForCluster(1)); } -TEST_P(SingleSegmentSegmenterTest, IgnoresFragmentDuration) { +TEST_F(SingleSegmentSegmenterTest, IgnoresFragmentDuration) { MuxerOptions options = CreateMuxerOptions(); options.fragment_duration = 5; // seconds ASSERT_NO_FATAL_FAILURE(InitializeSegmenter(options)); @@ -216,7 +205,7 @@ TEST_P(SingleSegmentSegmenterTest, IgnoresFragmentDuration) { EXPECT_EQ(8, parser.GetFrameCountForCluster(0)); } -TEST_P(SingleSegmentSegmenterTest, RespectsSAPAlign) { +TEST_F(SingleSegmentSegmenterTest, RespectsSAPAlign) { MuxerOptions options = CreateMuxerOptions(); options.segment_duration = 3; // seconds options.segment_sap_aligned = true; @@ -242,9 +231,5 @@ TEST_P(SingleSegmentSegmenterTest, RespectsSAPAlign) { EXPECT_EQ(4, parser.GetFrameCountForCluster(1)); } -INSTANTIATE_TEST_CASE_P(TrueIsTwoPass, - SingleSegmentSegmenterTest, - ::testing::Bool()); - } // namespace media } // namespace shaka diff --git a/packager/media/formats/webm/two_pass_single_segment_segmenter.cc b/packager/media/formats/webm/two_pass_single_segment_segmenter.cc index a8166c4248..52a7c63758 100644 --- a/packager/media/formats/webm/two_pass_single_segment_segmenter.cc +++ b/packager/media/formats/webm/two_pass_single_segment_segmenter.cc @@ -28,6 +28,26 @@ namespace shaka { namespace media { namespace webm { namespace { +// Cues will be inserted before clusters. All clusters will be shifted down by +// the size of cues. However, cluster positions affect the size of cues. This +// function adjusts cues size iteratively until it is stable. +// Returns the size of updated Cues. +uint64_t UpdateCues(mkvmuxer::Cues* cues) { + uint64_t cues_size = cues->Size(); + uint64_t adjustment = cues_size; + while (adjustment != 0) { + for (int i = 0; i < cues->cue_entries_size(); ++i) { + mkvmuxer::CuePoint* cue = cues->GetCueByIndex(i); + cue->set_cluster_pos(cue->cluster_pos() + adjustment); + } + uint64_t new_cues_size = cues->Size(); + DCHECK_LE(cues_size, new_cues_size); + adjustment = new_cues_size - cues_size; + cues_size = new_cues_size; + } + return cues_size; +} + /// Create the temp file name using process/thread id and current time. std::string TempFileName(const MuxerOptions& options) { // TODO: Move to a common util function and remove other uses. @@ -93,17 +113,26 @@ Status TwoPassSingleSegmentSegmenter::DoFinalize() { if (!cluster()->Finalize()) return Status(error::FILE_FAILURE, "Error finalizing cluster."); - // Write the Cues to the end of the temp file. - uint64_t cues_pos = writer()->Position(); - set_index_start(cues_pos); - seek_head()->set_cues_pos(cues_pos - segment_payload_pos()); - if (!cues()->Write(writer())) - return Status(error::FILE_FAILURE, "Error writing Cues data."); + const uint64_t header_size = init_end() + 1; + const uint64_t cues_pos = header_size - segment_payload_pos(); + const uint64_t cues_size = UpdateCues(cues()); + seek_head()->set_cues_pos(cues_pos); + seek_head()->set_cluster_pos(cues_pos + cues_size); // Write the header to the real output file. - Status temp = WriteSegmentHeader(writer()->Position(), real_writer_.get()); + const uint64_t file_size = writer()->Position() + cues_size; + Status temp = WriteSegmentHeader(file_size, real_writer_.get()); if (!temp.ok()) return temp; + DCHECK_EQ(real_writer_->Position(), static_cast(header_size)); + + // Write the cues to the real output file. + set_index_start(real_writer_->Position()); + if (!cues()->Write(real_writer_.get())) + return Status(error::FILE_FAILURE, "Error writing Cues data."); + set_index_end(real_writer_->Position() - 1); + DCHECK_EQ(real_writer_->Position(), + static_cast(segment_payload_pos() + cues_pos + cues_size)); // Close the temp file and open it for reading. set_writer(std::unique_ptr()); @@ -113,14 +142,14 @@ Status TwoPassSingleSegmentSegmenter::DoFinalize() { return Status(error::FILE_FAILURE, "Error opening temp file."); // Skip the header that has already been written. - uint64_t header_size = real_writer_->Position(); if (!ReadSkip(temp_reader.get(), header_size)) return Status(error::FILE_FAILURE, "Error reading temp file."); // Copy the rest of the data over. if (!CopyFileWithClusterRewrite(temp_reader.get(), real_writer_.get(), - cluster()->Size())) + cluster()->Size())) { return Status(error::FILE_FAILURE, "Error copying temp file."); + } // Close and delete the temp file. temp_reader.reset(); @@ -128,8 +157,6 @@ Status TwoPassSingleSegmentSegmenter::DoFinalize() { LOG(WARNING) << "Unable to delete temporary file " << temp_file_name_; } - // The WebM index is at the end of the file. - set_index_end(real_writer_->file()->Size() - 1); return real_writer_->Close(); } @@ -175,9 +202,9 @@ bool TwoPassSingleSegmentSegmenter::CopyFileWithClusterRewrite( if (!ReadSkip(source, cluster_size_size)) return false; - // Copy the remaining data (i.e. Cues data). + // Copy the last cluster. return dest->WriteFromFile(source) == - static_cast(last_cluster_payload_size + cues()->Size()); + static_cast(last_cluster_payload_size); } } // namespace webm diff --git a/packager/media/formats/webm/webm.gyp b/packager/media/formats/webm/webm.gyp index 7557405d27..1aeb63c9ec 100644 --- a/packager/media/formats/webm/webm.gyp +++ b/packager/media/formats/webm/webm.gyp @@ -84,6 +84,7 @@ 'dependencies': [ '../../../testing/gtest.gyp:gtest', '../../../testing/gmock.gyp:gmock', + '../../../third_party/libwebm/libwebm.gyp:mkvmuxer', '../../file/file.gyp:file', '../../test/media_test.gyp:media_test_support', 'webm', diff --git a/packager/media/formats/webm/webm_muxer.cc b/packager/media/formats/webm/webm_muxer.cc index d4ac37db70..3f3d55e552 100644 --- a/packager/media/formats/webm/webm_muxer.cc +++ b/packager/media/formats/webm/webm_muxer.cc @@ -45,8 +45,6 @@ Status WebMMuxer::Initialize() { if (!options().single_segment) { segmenter_.reset(new MultiSegmentSegmenter(options())); - } else if (writer->Seekable()) { - segmenter_.reset(new SingleSegmentSegmenter(options())); } else { segmenter_.reset(new TwoPassSingleSegmentSegmenter(options())); } diff --git a/packager/third_party/libwebm/libwebm.gyp b/packager/third_party/libwebm/libwebm.gyp index 882a43023f..0d4bcabb48 100644 --- a/packager/third_party/libwebm/libwebm.gyp +++ b/packager/third_party/libwebm/libwebm.gyp @@ -12,13 +12,26 @@ 'target_name': 'mkvmuxer', 'type': 'static_library', 'sources': [ - 'src/mkvmuxer.cpp', - 'src/mkvmuxer.hpp', - 'src/mkvmuxerutil.cpp', - 'src/mkvmuxerutil.hpp', - 'src/mkvwriter.cpp', - 'src/mkvwriter.hpp', + 'src/common/webmids.h', + 'src/mkvmuxer/mkvmuxer.cc', + 'src/mkvmuxer/mkvmuxer.h', + 'src/mkvmuxer/mkvmuxertypes.h', + 'src/mkvmuxer/mkvmuxerutil.cc', + 'src/mkvmuxer/mkvmuxerutil.h', + 'src/mkvmuxer/mkvwriter.cc', + 'src/mkvmuxer/mkvwriter.h', + 'src/mkvmuxer.hpp' + 'src/mkvmuxerutil.hpp' + 'src/webmids.hpp' ], + 'include_dirs': [ + 'src', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + 'src', + ], + }, }, ], }