diff --git a/packager/app/test/packager_test.py b/packager/app/test/packager_test.py
index d306e94257..2273b49ad9 100755
--- a/packager/app/test/packager_test.py
+++ b/packager/app/test/packager_test.py
@@ -806,8 +806,8 @@ class PackagerFunctionalTest(PackagerAppTest):
self.assertPackageSuccess(
self._GetStreams(['audio', 'video']),
self._GetFlags(encryption=True, ad_cues='1.5'))
- self._DiffGold(self.output[0], 'bear-640x360-a-cenc-golden.mp4')
- self._DiffGold(self.output[1], 'bear-640x360-v-cenc-golden.mp4')
+ self._DiffGold(self.output[0], 'bear-640x360-a-cenc-ad_cues-golden.mp4')
+ self._DiffGold(self.output[1], 'bear-640x360-v-cenc-ad_cues-golden.mp4')
self._DiffGold(self.mpd_output, 'bear-640x360-av-cenc-ad_cues-golden.mpd')
self._VerifyDecryption(self.output[0], 'bear-640x360-a-demuxed-golden.mp4')
self._VerifyDecryption(self.output[1], 'bear-640x360-v-golden.mp4')
@@ -1088,10 +1088,10 @@ class PackagerFunctionalTest(PackagerAppTest):
self.assertPackageSuccess(
self._GetStreams(['audio', 'video'], hls=True),
self._GetFlags(encryption=True, output_hls=True, ad_cues='1.5'))
- self._DiffGold(self.output[0], 'bear-640x360-a-cenc-golden.mp4')
- self._DiffGold(self.output[1], 'bear-640x360-v-cenc-golden.mp4')
+ self._DiffGold(self.output[0], 'bear-640x360-a-cenc-ad_cues-golden.mp4')
+ self._DiffGold(self.output[1], 'bear-640x360-v-cenc-ad_cues-golden.mp4')
self._DiffGold(self.hls_master_playlist_output,
- 'bear-640x360-av-mp4-master-cenc-golden.m3u8')
+ 'bear-640x360-av-mp4-master-cenc-ad_cues-golden.m3u8')
self._DiffGold(
os.path.join(self.tmp_dir, 'audio.m3u8'),
'bear-640x360-a-mp4-cenc-ad_cues-golden.m3u8')
@@ -1146,8 +1146,8 @@ class PackagerFunctionalTest(PackagerAppTest):
self.assertPackageSuccess(
self._GetStreams(['audio', 'video'], live=True),
self._GetFlags(generate_static_mpd=True, ad_cues='1.5'))
- self._DiffLiveGold(self.output[0], 'bear-640x360-a-live-golden')
- self._DiffLiveGold(self.output[1], 'bear-640x360-v-live-golden')
+ self._DiffLiveGold(self.output[0], 'bear-640x360-a-live-ad_cues-golden')
+ self._DiffLiveGold(self.output[1], 'bear-640x360-v-live-ad_cues-golden')
self._DiffGold(self.mpd_output,
'bear-640x360-av-live-static-ad_cues-golden.mpd')
diff --git a/packager/app/test/testdata/bear-320x240-opus-cenc-golden.mp4 b/packager/app/test/testdata/bear-320x240-opus-cenc-golden.mp4
index cdda8b532a..646ae1c329 100644
Binary files a/packager/app/test/testdata/bear-320x240-opus-cenc-golden.mp4 and b/packager/app/test/testdata/bear-320x240-opus-cenc-golden.mp4 differ
diff --git a/packager/app/test/testdata/bear-320x240-opus-golden.mp4 b/packager/app/test/testdata/bear-320x240-opus-golden.mp4
index 8af1a3a2bf..54a091c00c 100644
Binary files a/packager/app/test/testdata/bear-320x240-opus-golden.mp4 and b/packager/app/test/testdata/bear-320x240-opus-golden.mp4 differ
diff --git a/packager/app/test/testdata/bear-320x240-opus-golden.webm b/packager/app/test/testdata/bear-320x240-opus-golden.webm
index 08a035417c..ed155bc28b 100644
Binary files a/packager/app/test/testdata/bear-320x240-opus-golden.webm and b/packager/app/test/testdata/bear-320x240-opus-golden.webm differ
diff --git a/packager/app/test/testdata/bear-320x240-opus-vp9-cenc-golden.mpd b/packager/app/test/testdata/bear-320x240-opus-vp9-cenc-golden.mpd
index d488094f94..8963d219a2 100644
--- a/packager/app/test/testdata/bear-320x240-opus-vp9-cenc-golden.mpd
+++ b/packager/app/test/testdata/bear-320x240-opus-vp9-cenc-golden.mpd
@@ -7,7 +7,7 @@
AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA==
-
+
output_audio.mp4
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 7cf286a043..efeacb525d 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
@@ -3,7 +3,7 @@
-
+
output_audio.webm
diff --git a/packager/app/test/testdata/bear-640x360-a-cbc1-golden.mp4 b/packager/app/test/testdata/bear-640x360-a-cbc1-golden.mp4
index 5760ac2b58..afd156b3f3 100644
Binary files a/packager/app/test/testdata/bear-640x360-a-cbc1-golden.mp4 and b/packager/app/test/testdata/bear-640x360-a-cbc1-golden.mp4 differ
diff --git a/packager/app/test/testdata/bear-640x360-a-cbcs-golden.mp4 b/packager/app/test/testdata/bear-640x360-a-cbcs-golden.mp4
index 07afa6a80c..64128ea454 100644
Binary files a/packager/app/test/testdata/bear-640x360-a-cbcs-golden.mp4 and b/packager/app/test/testdata/bear-640x360-a-cbcs-golden.mp4 differ
diff --git a/packager/app/test/testdata/bear-640x360-a-cenc-ad_cues-golden.mp4 b/packager/app/test/testdata/bear-640x360-a-cenc-ad_cues-golden.mp4
new file mode 100644
index 0000000000..6df0ec5ab9
Binary files /dev/null and b/packager/app/test/testdata/bear-640x360-a-cenc-ad_cues-golden.mp4 differ
diff --git a/packager/app/test/testdata/bear-640x360-a-cenc-golden.mp4 b/packager/app/test/testdata/bear-640x360-a-cenc-golden.mp4
index 2b40464893..46e073eefe 100644
Binary files a/packager/app/test/testdata/bear-640x360-a-cenc-golden.mp4 and b/packager/app/test/testdata/bear-640x360-a-cenc-golden.mp4 differ
diff --git a/packager/app/test/testdata/bear-640x360-a-cenc-golden.mp4.media_info b/packager/app/test/testdata/bear-640x360-a-cenc-golden.mp4.media_info
index 7f119685c9..3244f9075d 100644
--- a/packager/app/test/testdata/bear-640x360-a-cenc-golden.mp4.media_info
+++ b/packager/app/test/testdata/bear-640x360-a-cenc-golden.mp4.media_info
@@ -1,4 +1,4 @@
-bandwidth: 129185
+bandwidth: 129162
audio_info {
codec: "mp4a.40.2"
sampling_frequency: 44100
diff --git a/packager/app/test/testdata/bear-640x360-a-cenc-no-clear-lead-golden.mp4 b/packager/app/test/testdata/bear-640x360-a-cenc-no-clear-lead-golden.mp4
index 08c44d8285..fd6baf5aa9 100644
Binary files a/packager/app/test/testdata/bear-640x360-a-cenc-no-clear-lead-golden.mp4 and b/packager/app/test/testdata/bear-640x360-a-cenc-no-clear-lead-golden.mp4 differ
diff --git a/packager/app/test/testdata/bear-640x360-a-cenc-no-pssh-golden.mp4 b/packager/app/test/testdata/bear-640x360-a-cenc-no-pssh-golden.mp4
index 318a7f278a..8da4e3efaf 100644
Binary files a/packager/app/test/testdata/bear-640x360-a-cenc-no-pssh-golden.mp4 and b/packager/app/test/testdata/bear-640x360-a-cenc-no-pssh-golden.mp4 differ
diff --git a/packager/app/test/testdata/bear-640x360-a-cens-golden.mp4 b/packager/app/test/testdata/bear-640x360-a-cens-golden.mp4
index 36c049aa3a..e3e3ee5d8a 100644
Binary files a/packager/app/test/testdata/bear-640x360-a-cens-golden.mp4 and b/packager/app/test/testdata/bear-640x360-a-cens-golden.mp4 differ
diff --git a/packager/app/test/testdata/bear-640x360-a-enc-golden-1.ts b/packager/app/test/testdata/bear-640x360-a-enc-golden-1.ts
index 17d20c42c9..c492a868c3 100644
Binary files a/packager/app/test/testdata/bear-640x360-a-enc-golden-1.ts and b/packager/app/test/testdata/bear-640x360-a-enc-golden-1.ts differ
diff --git a/packager/app/test/testdata/bear-640x360-a-enc-golden-2.ts b/packager/app/test/testdata/bear-640x360-a-enc-golden-2.ts
index 3c3e210dac..b7dbc50b73 100644
Binary files a/packager/app/test/testdata/bear-640x360-a-enc-golden-2.ts and b/packager/app/test/testdata/bear-640x360-a-enc-golden-2.ts differ
diff --git a/packager/app/test/testdata/bear-640x360-a-enc-golden-3.ts b/packager/app/test/testdata/bear-640x360-a-enc-golden-3.ts
index ac61fde335..fafb892ed8 100644
Binary files a/packager/app/test/testdata/bear-640x360-a-enc-golden-3.ts and b/packager/app/test/testdata/bear-640x360-a-enc-golden-3.ts differ
diff --git a/packager/app/test/testdata/bear-640x360-a-enc-golden.m3u8 b/packager/app/test/testdata/bear-640x360-a-enc-golden.m3u8
index 9c67fa201d..a33db9a33d 100644
--- a/packager/app/test/testdata/bear-640x360-a-enc-golden.m3u8
+++ b/packager/app/test/testdata/bear-640x360-a-enc-golden.m3u8
@@ -3,12 +3,12 @@
## Generated with https://github.com/google/shaka-packager version --
#EXT-X-TARGETDURATION:2
#EXT-X-PLAYLIST-TYPE:VOD
-#EXTINF:0.952,
+#EXTINF:0.975,
output_audio-1.ts
#EXT-X-DISCONTINUITY
#EXT-X-KEY:METHOD=SAMPLE-AES,URI="data:text/plain;base64,MTIzNDU2Nzg5MDEyMzQ1Ng==",IV=0x3334353637383930,KEYFORMAT="identity"
#EXTINF:0.998,
output_audio-2.ts
-#EXTINF:0.813,
+#EXTINF:0.789,
output_audio-3.ts
#EXT-X-ENDLIST
diff --git a/packager/app/test/testdata/bear-640x360-a-enc-rotation-golden-1.ts b/packager/app/test/testdata/bear-640x360-a-enc-rotation-golden-1.ts
index 17d20c42c9..c492a868c3 100644
Binary files a/packager/app/test/testdata/bear-640x360-a-enc-rotation-golden-1.ts and b/packager/app/test/testdata/bear-640x360-a-enc-rotation-golden-1.ts differ
diff --git a/packager/app/test/testdata/bear-640x360-a-enc-rotation-golden-2.ts b/packager/app/test/testdata/bear-640x360-a-enc-rotation-golden-2.ts
index 3c3e210dac..2edc20505b 100644
Binary files a/packager/app/test/testdata/bear-640x360-a-enc-rotation-golden-2.ts and b/packager/app/test/testdata/bear-640x360-a-enc-rotation-golden-2.ts differ
diff --git a/packager/app/test/testdata/bear-640x360-a-enc-rotation-golden-3.ts b/packager/app/test/testdata/bear-640x360-a-enc-rotation-golden-3.ts
index 1b21a7eb22..3f18799587 100644
Binary files a/packager/app/test/testdata/bear-640x360-a-enc-rotation-golden-3.ts and b/packager/app/test/testdata/bear-640x360-a-enc-rotation-golden-3.ts differ
diff --git a/packager/app/test/testdata/bear-640x360-a-event-golden.m3u8 b/packager/app/test/testdata/bear-640x360-a-event-golden.m3u8
index ae07db12d1..cb2d47ca5d 100644
--- a/packager/app/test/testdata/bear-640x360-a-event-golden.m3u8
+++ b/packager/app/test/testdata/bear-640x360-a-event-golden.m3u8
@@ -3,9 +3,9 @@
## Generated with https://github.com/google/shaka-packager version --
#EXT-X-TARGETDURATION:2
#EXT-X-PLAYLIST-TYPE:EVENT
-#EXTINF:0.952,
+#EXTINF:0.975,
output_audio-1.ts
#EXTINF:0.998,
output_audio-2.ts
-#EXTINF:0.813,
+#EXTINF:0.789,
output_audio-3.ts
diff --git a/packager/app/test/testdata/bear-640x360-a-fairplay-enc-golden.m3u8 b/packager/app/test/testdata/bear-640x360-a-fairplay-enc-golden.m3u8
index 166a8f4174..124c292eab 100644
--- a/packager/app/test/testdata/bear-640x360-a-fairplay-enc-golden.m3u8
+++ b/packager/app/test/testdata/bear-640x360-a-fairplay-enc-golden.m3u8
@@ -3,12 +3,12 @@
## Generated with https://github.com/google/shaka-packager version --
#EXT-X-TARGETDURATION:2
#EXT-X-PLAYLIST-TYPE:VOD
-#EXTINF:0.952,
+#EXTINF:0.975,
output_audio-1.ts
#EXT-X-DISCONTINUITY
#EXT-X-KEY:METHOD=SAMPLE-AES,URI="skd://www.license.com/getkey?KeyId=31323334-3536-3738-3930-313233343536",KEYFORMATVERSIONS="1",KEYFORMAT="com.apple.streamingkeydelivery"
#EXTINF:0.998,
output_audio-2.ts
-#EXTINF:0.813,
+#EXTINF:0.789,
output_audio-3.ts
#EXT-X-ENDLIST
diff --git a/packager/app/test/testdata/bear-640x360-a-golden-1.ts b/packager/app/test/testdata/bear-640x360-a-golden-1.ts
index 17d20c42c9..c492a868c3 100644
Binary files a/packager/app/test/testdata/bear-640x360-a-golden-1.ts and b/packager/app/test/testdata/bear-640x360-a-golden-1.ts differ
diff --git a/packager/app/test/testdata/bear-640x360-a-golden-2.ts b/packager/app/test/testdata/bear-640x360-a-golden-2.ts
index 977aa81f5a..5f386328e3 100644
Binary files a/packager/app/test/testdata/bear-640x360-a-golden-2.ts and b/packager/app/test/testdata/bear-640x360-a-golden-2.ts differ
diff --git a/packager/app/test/testdata/bear-640x360-a-golden-3.ts b/packager/app/test/testdata/bear-640x360-a-golden-3.ts
index 926ef4c075..fcfe141d34 100644
Binary files a/packager/app/test/testdata/bear-640x360-a-golden-3.ts and b/packager/app/test/testdata/bear-640x360-a-golden-3.ts differ
diff --git a/packager/app/test/testdata/bear-640x360-a-golden.m3u8 b/packager/app/test/testdata/bear-640x360-a-golden.m3u8
index d46157a0c8..0209c9b3f2 100644
--- a/packager/app/test/testdata/bear-640x360-a-golden.m3u8
+++ b/packager/app/test/testdata/bear-640x360-a-golden.m3u8
@@ -3,10 +3,10 @@
## Generated with https://github.com/google/shaka-packager version --
#EXT-X-TARGETDURATION:2
#EXT-X-PLAYLIST-TYPE:VOD
-#EXTINF:0.952,
+#EXTINF:0.975,
output_audio-1.ts
#EXTINF:0.998,
output_audio-2.ts
-#EXTINF:0.813,
+#EXTINF:0.789,
output_audio-3.ts
#EXT-X-ENDLIST
diff --git a/packager/app/test/testdata/bear-640x360-a-golden.mp4 b/packager/app/test/testdata/bear-640x360-a-golden.mp4
index 363c933a1a..d58886f965 100644
Binary files a/packager/app/test/testdata/bear-640x360-a-golden.mp4 and b/packager/app/test/testdata/bear-640x360-a-golden.mp4 differ
diff --git a/packager/app/test/testdata/bear-640x360-a-live-ad_cues-golden-1.m4s b/packager/app/test/testdata/bear-640x360-a-live-ad_cues-golden-1.m4s
new file mode 100644
index 0000000000..c4b4ac03d9
Binary files /dev/null and b/packager/app/test/testdata/bear-640x360-a-live-ad_cues-golden-1.m4s differ
diff --git a/packager/app/test/testdata/bear-640x360-a-live-ad_cues-golden-2.m4s b/packager/app/test/testdata/bear-640x360-a-live-ad_cues-golden-2.m4s
new file mode 100644
index 0000000000..8504387c99
Binary files /dev/null and b/packager/app/test/testdata/bear-640x360-a-live-ad_cues-golden-2.m4s differ
diff --git a/packager/app/test/testdata/bear-640x360-a-live-ad_cues-golden-3.m4s b/packager/app/test/testdata/bear-640x360-a-live-ad_cues-golden-3.m4s
new file mode 100644
index 0000000000..692408ca70
Binary files /dev/null and b/packager/app/test/testdata/bear-640x360-a-live-ad_cues-golden-3.m4s differ
diff --git a/packager/app/test/testdata/bear-640x360-a-live-ad_cues-golden-init.mp4 b/packager/app/test/testdata/bear-640x360-a-live-ad_cues-golden-init.mp4
new file mode 100644
index 0000000000..f725c309e7
Binary files /dev/null and b/packager/app/test/testdata/bear-640x360-a-live-ad_cues-golden-init.mp4 differ
diff --git a/packager/app/test/testdata/bear-640x360-a-live-cenc-golden-1.m4s b/packager/app/test/testdata/bear-640x360-a-live-cenc-golden-1.m4s
index 26e069ee9e..f336d41dd2 100644
Binary files a/packager/app/test/testdata/bear-640x360-a-live-cenc-golden-1.m4s and b/packager/app/test/testdata/bear-640x360-a-live-cenc-golden-1.m4s differ
diff --git a/packager/app/test/testdata/bear-640x360-a-live-cenc-golden-2.m4s b/packager/app/test/testdata/bear-640x360-a-live-cenc-golden-2.m4s
index e7e2c85278..e285304cbf 100644
Binary files a/packager/app/test/testdata/bear-640x360-a-live-cenc-golden-2.m4s and b/packager/app/test/testdata/bear-640x360-a-live-cenc-golden-2.m4s differ
diff --git a/packager/app/test/testdata/bear-640x360-a-live-cenc-golden-3.m4s b/packager/app/test/testdata/bear-640x360-a-live-cenc-golden-3.m4s
index 4243aa6447..f197652913 100644
Binary files a/packager/app/test/testdata/bear-640x360-a-live-cenc-golden-3.m4s and b/packager/app/test/testdata/bear-640x360-a-live-cenc-golden-3.m4s differ
diff --git a/packager/app/test/testdata/bear-640x360-a-live-cenc-rotation-golden-1.m4s b/packager/app/test/testdata/bear-640x360-a-live-cenc-rotation-golden-1.m4s
index bf8580ceb3..50e3c1afc8 100644
Binary files a/packager/app/test/testdata/bear-640x360-a-live-cenc-rotation-golden-1.m4s and b/packager/app/test/testdata/bear-640x360-a-live-cenc-rotation-golden-1.m4s differ
diff --git a/packager/app/test/testdata/bear-640x360-a-live-cenc-rotation-golden-2.m4s b/packager/app/test/testdata/bear-640x360-a-live-cenc-rotation-golden-2.m4s
index 846a7fb92e..b48c322560 100644
Binary files a/packager/app/test/testdata/bear-640x360-a-live-cenc-rotation-golden-2.m4s and b/packager/app/test/testdata/bear-640x360-a-live-cenc-rotation-golden-2.m4s differ
diff --git a/packager/app/test/testdata/bear-640x360-a-live-cenc-rotation-golden-3.m4s b/packager/app/test/testdata/bear-640x360-a-live-cenc-rotation-golden-3.m4s
index 3354221854..dd5d4b9f09 100644
Binary files a/packager/app/test/testdata/bear-640x360-a-live-cenc-rotation-golden-3.m4s and b/packager/app/test/testdata/bear-640x360-a-live-cenc-rotation-golden-3.m4s differ
diff --git a/packager/app/test/testdata/bear-640x360-a-live-cenc-rotation-no-pssh-golden-1.m4s b/packager/app/test/testdata/bear-640x360-a-live-cenc-rotation-no-pssh-golden-1.m4s
index 26e069ee9e..f336d41dd2 100644
Binary files a/packager/app/test/testdata/bear-640x360-a-live-cenc-rotation-no-pssh-golden-1.m4s and b/packager/app/test/testdata/bear-640x360-a-live-cenc-rotation-no-pssh-golden-1.m4s differ
diff --git a/packager/app/test/testdata/bear-640x360-a-live-cenc-rotation-no-pssh-golden-2.m4s b/packager/app/test/testdata/bear-640x360-a-live-cenc-rotation-no-pssh-golden-2.m4s
index 087c95234d..39468e7e43 100644
Binary files a/packager/app/test/testdata/bear-640x360-a-live-cenc-rotation-no-pssh-golden-2.m4s and b/packager/app/test/testdata/bear-640x360-a-live-cenc-rotation-no-pssh-golden-2.m4s differ
diff --git a/packager/app/test/testdata/bear-640x360-a-live-cenc-rotation-no-pssh-golden-3.m4s b/packager/app/test/testdata/bear-640x360-a-live-cenc-rotation-no-pssh-golden-3.m4s
index a15ea991f8..e179f1c7e3 100644
Binary files a/packager/app/test/testdata/bear-640x360-a-live-cenc-rotation-no-pssh-golden-3.m4s and b/packager/app/test/testdata/bear-640x360-a-live-cenc-rotation-no-pssh-golden-3.m4s differ
diff --git a/packager/app/test/testdata/bear-640x360-a-live-enc-rotation-golden.m3u8 b/packager/app/test/testdata/bear-640x360-a-live-enc-rotation-golden.m3u8
index 916ab207fc..8ecbea21ff 100644
--- a/packager/app/test/testdata/bear-640x360-a-live-enc-rotation-golden.m3u8
+++ b/packager/app/test/testdata/bear-640x360-a-live-enc-rotation-golden.m3u8
@@ -4,9 +4,9 @@
#EXT-X-TARGETDURATION:2
#EXT-X-MEDIA-SEQUENCE:1
#EXT-X-DISCONTINUITY-SEQUENCE:1
-#EXT-X-KEY:METHOD=SAMPLE-AES,URI="data:text/plain;base64,MTIzNDU2Nzg5MDEyMzQ1Ng==",IV=0x3334353637383930,KEYFORMAT="identity"
+#EXT-X-KEY:METHOD=SAMPLE-AES,URI="data:text/plain;base64,MjM0NTY3ODkwMTIzNDU2MQ==",IV=0x3334353637383930,KEYFORMAT="identity"
#EXTINF:0.998,
output_audio-2.ts
-#EXT-X-KEY:METHOD=SAMPLE-AES,URI="data:text/plain;base64,MjM0NTY3ODkwMTIzNDU2MQ==",IV=0x3334353637383930,KEYFORMAT="identity"
-#EXTINF:0.813,
+#EXT-X-KEY:METHOD=SAMPLE-AES,URI="data:text/plain;base64,MzQ1Njc4OTAxMjM0NTYxMg==",IV=0x3334353637383930,KEYFORMAT="identity"
+#EXTINF:0.789,
output_audio-3.ts
diff --git a/packager/app/test/testdata/bear-640x360-a-live-golden-1.m4s b/packager/app/test/testdata/bear-640x360-a-live-golden-1.m4s
index f37ed8a13c..c4b4ac03d9 100644
Binary files a/packager/app/test/testdata/bear-640x360-a-live-golden-1.m4s and b/packager/app/test/testdata/bear-640x360-a-live-golden-1.m4s differ
diff --git a/packager/app/test/testdata/bear-640x360-a-live-golden-2.m4s b/packager/app/test/testdata/bear-640x360-a-live-golden-2.m4s
index bf0257dff3..8504387c99 100644
Binary files a/packager/app/test/testdata/bear-640x360-a-live-golden-2.m4s and b/packager/app/test/testdata/bear-640x360-a-live-golden-2.m4s differ
diff --git a/packager/app/test/testdata/bear-640x360-a-live-golden-3.m4s b/packager/app/test/testdata/bear-640x360-a-live-golden-3.m4s
index 4efd91c5dd..99af31e503 100644
Binary files a/packager/app/test/testdata/bear-640x360-a-live-golden-3.m4s and b/packager/app/test/testdata/bear-640x360-a-live-golden-3.m4s differ
diff --git a/packager/app/test/testdata/bear-640x360-a-live-golden.m3u8 b/packager/app/test/testdata/bear-640x360-a-live-golden.m3u8
index 086a1dad8a..c2993e32b6 100644
--- a/packager/app/test/testdata/bear-640x360-a-live-golden.m3u8
+++ b/packager/app/test/testdata/bear-640x360-a-live-golden.m3u8
@@ -5,5 +5,5 @@
#EXT-X-MEDIA-SEQUENCE:1
#EXTINF:0.998,
output_audio-2.ts
-#EXTINF:0.813,
+#EXTINF:0.789,
output_audio-3.ts
diff --git a/packager/app/test/testdata/bear-640x360-a-mp4-cenc-ad_cues-golden.m3u8 b/packager/app/test/testdata/bear-640x360-a-mp4-cenc-ad_cues-golden.m3u8
index 7bf3defe68..4c282ea363 100644
--- a/packager/app/test/testdata/bear-640x360-a-mp4-cenc-ad_cues-golden.m3u8
+++ b/packager/app/test/testdata/bear-640x360-a-mp4-cenc-ad_cues-golden.m3u8
@@ -5,14 +5,17 @@
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-MAP:URI="output_audio.mp4",BYTERANGE="967@0"
#EXT-X-KEY:METHOD=SAMPLE-AES-CTR,URI="data:text/plain;base64,MTIzNDU2Nzg5MDEyMzQ1Ng==",KEYFORMAT="identity"
-#EXTINF:0.998,
-#EXT-X-BYTERANGE:16279@1035
+#EXTINF:1.022,
+#EXT-X-BYTERANGE:16655@1047
output_audio.mp4
#EXTINF:0.998,
-#EXT-X-BYTERANGE:16674
+#EXT-X-BYTERANGE:16650
+output_audio.mp4
+#EXTINF:0.046,
+#EXT-X-BYTERANGE:1014
output_audio.mp4
#EXT-X-PLACEMENT-OPPORTUNITY
-#EXTINF:0.766,
-#EXT-X-BYTERANGE:10632
+#EXTINF:0.697,
+#EXT-X-BYTERANGE:9415
output_audio.mp4
#EXT-X-ENDLIST
diff --git a/packager/app/test/testdata/bear-640x360-a-mp4-cenc-golden.m3u8 b/packager/app/test/testdata/bear-640x360-a-mp4-cenc-golden.m3u8
index a1b9dcb25b..17206aab4b 100644
--- a/packager/app/test/testdata/bear-640x360-a-mp4-cenc-golden.m3u8
+++ b/packager/app/test/testdata/bear-640x360-a-mp4-cenc-golden.m3u8
@@ -5,13 +5,13 @@
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-MAP:URI="output_audio.mp4",BYTERANGE="967@0"
#EXT-X-KEY:METHOD=SAMPLE-AES-CTR,URI="data:text/plain;base64,MTIzNDU2Nzg5MDEyMzQ1Ng==",KEYFORMAT="identity"
-#EXTINF:0.998,
-#EXT-X-BYTERANGE:16279@1035
+#EXTINF:1.022,
+#EXT-X-BYTERANGE:16655@1035
output_audio.mp4
#EXTINF:0.998,
-#EXT-X-BYTERANGE:16674
+#EXT-X-BYTERANGE:16650
output_audio.mp4
-#EXTINF:0.766,
-#EXT-X-BYTERANGE:10632
+#EXTINF:0.743,
+#EXT-X-BYTERANGE:10272
output_audio.mp4
#EXT-X-ENDLIST
diff --git a/packager/app/test/testdata/bear-640x360-a-mp4-golden.m3u8 b/packager/app/test/testdata/bear-640x360-a-mp4-golden.m3u8
index d011a2c514..c9719d6f6f 100644
--- a/packager/app/test/testdata/bear-640x360-a-mp4-golden.m3u8
+++ b/packager/app/test/testdata/bear-640x360-a-mp4-golden.m3u8
@@ -4,10 +4,10 @@
#EXT-X-TARGETDURATION:2
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-MAP:URI="audio-init.mp4"
-#EXTINF:0.998,
+#EXTINF:1.022,
audio-1.m4s
#EXTINF:0.998,
audio-2.m4s
-#EXTINF:0.766,
+#EXTINF:0.743,
audio-3.m4s
#EXT-X-ENDLIST
diff --git a/packager/app/test/testdata/bear-640x360-a-por-BR-golden.mp4 b/packager/app/test/testdata/bear-640x360-a-por-BR-golden.mp4
index 090a83e729..a68f12768a 100644
Binary files a/packager/app/test/testdata/bear-640x360-a-por-BR-golden.mp4 and b/packager/app/test/testdata/bear-640x360-a-por-BR-golden.mp4 differ
diff --git a/packager/app/test/testdata/bear-640x360-a-por-golden.mp4 b/packager/app/test/testdata/bear-640x360-a-por-golden.mp4
index 090a83e729..a68f12768a 100644
Binary files a/packager/app/test/testdata/bear-640x360-a-por-golden.mp4 and b/packager/app/test/testdata/bear-640x360-a-por-golden.mp4 differ
diff --git a/packager/app/test/testdata/bear-640x360-ac3-enc-golden-2.ts b/packager/app/test/testdata/bear-640x360-ac3-enc-golden-2.ts
index f76f3c8362..49834de310 100644
Binary files a/packager/app/test/testdata/bear-640x360-ac3-enc-golden-2.ts and b/packager/app/test/testdata/bear-640x360-ac3-enc-golden-2.ts differ
diff --git a/packager/app/test/testdata/bear-640x360-ac3-enc-golden-3.ts b/packager/app/test/testdata/bear-640x360-ac3-enc-golden-3.ts
index 0b71ff9387..39a1002c18 100644
Binary files a/packager/app/test/testdata/bear-640x360-ac3-enc-golden-3.ts and b/packager/app/test/testdata/bear-640x360-ac3-enc-golden-3.ts differ
diff --git a/packager/app/test/testdata/bear-640x360-ac3-enc-golden.m3u8 b/packager/app/test/testdata/bear-640x360-ac3-enc-golden.m3u8
index 95f63083b0..74c89011d4 100644
--- a/packager/app/test/testdata/bear-640x360-ac3-enc-golden.m3u8
+++ b/packager/app/test/testdata/bear-640x360-ac3-enc-golden.m3u8
@@ -7,8 +7,8 @@
output_audio-1.ts
#EXT-X-DISCONTINUITY
#EXT-X-KEY:METHOD=SAMPLE-AES,URI="data:text/plain;base64,MTIzNDU2Nzg5MDEyMzQ1Ng==",IV=0x3334353637383930,KEYFORMAT="identity"
-#EXTINF:0.975,
+#EXTINF:1.010,
output_audio-2.ts
-#EXTINF:0.836,
+#EXTINF:0.801,
output_audio-3.ts
#EXT-X-ENDLIST
diff --git a/packager/app/test/testdata/bear-640x360-ac3-from-ts-golden.mp4 b/packager/app/test/testdata/bear-640x360-ac3-from-ts-golden.mp4
index 48a4618685..3c4ec7a4b0 100644
Binary files a/packager/app/test/testdata/bear-640x360-ac3-from-ts-golden.mp4 and b/packager/app/test/testdata/bear-640x360-ac3-from-ts-golden.mp4 differ
diff --git a/packager/app/test/testdata/bear-640x360-ac3-golden-2.ts b/packager/app/test/testdata/bear-640x360-ac3-golden-2.ts
index 1ef30535e1..b5803febbf 100644
Binary files a/packager/app/test/testdata/bear-640x360-ac3-golden-2.ts and b/packager/app/test/testdata/bear-640x360-ac3-golden-2.ts differ
diff --git a/packager/app/test/testdata/bear-640x360-ac3-golden-3.ts b/packager/app/test/testdata/bear-640x360-ac3-golden-3.ts
index 7808415253..a8de767844 100644
Binary files a/packager/app/test/testdata/bear-640x360-ac3-golden-3.ts and b/packager/app/test/testdata/bear-640x360-ac3-golden-3.ts differ
diff --git a/packager/app/test/testdata/bear-640x360-ac3-golden.m3u8 b/packager/app/test/testdata/bear-640x360-ac3-golden.m3u8
index f236f95de1..0a45f41b03 100644
--- a/packager/app/test/testdata/bear-640x360-ac3-golden.m3u8
+++ b/packager/app/test/testdata/bear-640x360-ac3-golden.m3u8
@@ -5,8 +5,8 @@
#EXT-X-PLAYLIST-TYPE:VOD
#EXTINF:0.975,
output_audio-1.ts
-#EXTINF:0.975,
+#EXTINF:1.010,
output_audio-2.ts
-#EXTINF:0.836,
+#EXTINF:0.801,
output_audio-3.ts
#EXT-X-ENDLIST
diff --git a/packager/app/test/testdata/bear-640x360-ac3-ts-to-mp4-golden.m3u8 b/packager/app/test/testdata/bear-640x360-ac3-ts-to-mp4-golden.m3u8
index b8a4080daa..98e8e1b9b7 100644
--- a/packager/app/test/testdata/bear-640x360-ac3-ts-to-mp4-golden.m3u8
+++ b/packager/app/test/testdata/bear-640x360-ac3-ts-to-mp4-golden.m3u8
@@ -7,10 +7,10 @@
#EXTINF:0.975,
#EXT-X-BYTERANGE:23728@794
output_audio.mp4
-#EXTINF:0.975,
-#EXT-X-BYTERANGE:23730
+#EXTINF:1.010,
+#EXT-X-BYTERANGE:24574
output_audio.mp4
-#EXTINF:0.836,
-#EXT-X-BYTERANGE:20354
+#EXTINF:0.801,
+#EXT-X-BYTERANGE:19510
output_audio.mp4
#EXT-X-ENDLIST
diff --git a/packager/app/test/testdata/bear-640x360-av-ac3-master-golden.m3u8 b/packager/app/test/testdata/bear-640x360-av-ac3-master-golden.m3u8
index 5b874572c1..4c2c40cb16 100644
--- a/packager/app/test/testdata/bear-640x360-av-ac3-master-golden.m3u8
+++ b/packager/app/test/testdata/bear-640x360-av-ac3-master-golden.m3u8
@@ -3,5 +3,5 @@
#EXT-X-MEDIA:TYPE=AUDIO,URI="audio.m3u8",GROUP-ID="default-audio-group",NAME="stream_0",AUTOSELECT=YES,CHANNELS="2"
-#EXT-X-STREAM-INF:BANDWIDTH=1242703,CODECS="avc1.64001e,ac-3",RESOLUTION=640x360,AUDIO="default-audio-group"
+#EXT-X-STREAM-INF:BANDWIDTH=1242861,CODECS="avc1.64001e,ac-3",RESOLUTION=640x360,AUDIO="default-audio-group"
video.m3u8
diff --git a/packager/app/test/testdata/bear-640x360-av-ac3-ts-to-mp4-master-golden.m3u8 b/packager/app/test/testdata/bear-640x360-av-ac3-ts-to-mp4-master-golden.m3u8
index f788d95f5d..b14c4199ab 100644
--- a/packager/app/test/testdata/bear-640x360-av-ac3-ts-to-mp4-master-golden.m3u8
+++ b/packager/app/test/testdata/bear-640x360-av-ac3-ts-to-mp4-master-golden.m3u8
@@ -3,5 +3,5 @@
#EXT-X-MEDIA:TYPE=AUDIO,URI="audio.m3u8",GROUP-ID="default-audio-group",NAME="stream_0",AUTOSELECT=YES,CHANNELS="2"
-#EXT-X-STREAM-INF:BANDWIDTH=1168277,CODECS="avc1.64001e,ac-3",RESOLUTION=640x360,AUDIO="default-audio-group"
+#EXT-X-STREAM-INF:BANDWIDTH=1168319,CODECS="avc1.64001e,ac-3",RESOLUTION=640x360,AUDIO="default-audio-group"
video.m3u8
diff --git a/packager/app/test/testdata/bear-640x360-av-cbc1-golden.mpd b/packager/app/test/testdata/bear-640x360-av-cbc1-golden.mpd
index ded974833f..af910e8eed 100644
--- a/packager/app/test/testdata/bear-640x360-av-cbc1-golden.mpd
+++ b/packager/app/test/testdata/bear-640x360-av-cbc1-golden.mpd
@@ -19,7 +19,7 @@
AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA==
-
+
output_audio.mp4
diff --git a/packager/app/test/testdata/bear-640x360-av-cenc-ad_cues-golden.mpd b/packager/app/test/testdata/bear-640x360-av-cenc-ad_cues-golden.mpd
index fd13d80881..719a8e2c38 100644
--- a/packager/app/test/testdata/bear-640x360-av-cenc-ad_cues-golden.mpd
+++ b/packager/app/test/testdata/bear-640x360-av-cenc-ad_cues-golden.mpd
@@ -2,57 +2,57 @@
-
+
AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA==
-
+
+
+ output_audio.mp4
+
+
+
+
+
+
+
+
+ AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA==
+
+
output_video.mp4
-
+
+
+
AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA==
-
+
output_audio.mp4
-
+
-
-
-
+
AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA==
-
+
output_video.mp4
-
-
-
- AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA==
-
-
-
- output_audio.mp4
-
-
-
-
-
diff --git a/packager/app/test/testdata/bear-640x360-av-cenc-golden.mpd b/packager/app/test/testdata/bear-640x360-av-cenc-golden.mpd
index 5f0b7c2d4c..45915bf2a6 100644
--- a/packager/app/test/testdata/bear-640x360-av-cenc-golden.mpd
+++ b/packager/app/test/testdata/bear-640x360-av-cenc-golden.mpd
@@ -19,7 +19,7 @@
AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA==
-
+
output_audio.mp4
diff --git a/packager/app/test/testdata/bear-640x360-av-cenc-no-pssh-golden.mpd b/packager/app/test/testdata/bear-640x360-av-cenc-no-pssh-golden.mpd
index 8a8f5fe910..c9e3e7458d 100644
--- a/packager/app/test/testdata/bear-640x360-av-cenc-no-pssh-golden.mpd
+++ b/packager/app/test/testdata/bear-640x360-av-cenc-no-pssh-golden.mpd
@@ -19,7 +19,7 @@
AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA==
-
+
output_audio.mp4
diff --git a/packager/app/test/testdata/bear-640x360-av-cenc-non-iop-golden.mpd b/packager/app/test/testdata/bear-640x360-av-cenc-non-iop-golden.mpd
index ea6273a9a6..2f3145c34c 100644
--- a/packager/app/test/testdata/bear-640x360-av-cenc-non-iop-golden.mpd
+++ b/packager/app/test/testdata/bear-640x360-av-cenc-non-iop-golden.mpd
@@ -15,7 +15,7 @@
-
+
diff --git a/packager/app/test/testdata/bear-640x360-av-cens-golden.mpd b/packager/app/test/testdata/bear-640x360-av-cens-golden.mpd
index fa83b269e1..4f363d82ba 100644
--- a/packager/app/test/testdata/bear-640x360-av-cens-golden.mpd
+++ b/packager/app/test/testdata/bear-640x360-av-cens-golden.mpd
@@ -19,7 +19,7 @@
AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA==
-
+
output_audio.mp4
diff --git a/packager/app/test/testdata/bear-640x360-av-live-cenc-golden.mpd b/packager/app/test/testdata/bear-640x360-av-live-cenc-golden.mpd
index 7c994c222e..b904b4dde3 100644
--- a/packager/app/test/testdata/bear-640x360-av-live-cenc-golden.mpd
+++ b/packager/app/test/testdata/bear-640x360-av-live-cenc-golden.mpd
@@ -21,12 +21,13 @@
AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA==
-
+
-
-
+
+
+
diff --git a/packager/app/test/testdata/bear-640x360-av-live-cenc-non-iop-golden.mpd b/packager/app/test/testdata/bear-640x360-av-live-cenc-non-iop-golden.mpd
index 2ff1091ee3..7358bb9452 100644
--- a/packager/app/test/testdata/bear-640x360-av-live-cenc-non-iop-golden.mpd
+++ b/packager/app/test/testdata/bear-640x360-av-live-cenc-non-iop-golden.mpd
@@ -17,7 +17,7 @@
-
+
@@ -25,8 +25,9 @@
-
-
+
+
+
diff --git a/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-golden.mpd b/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-golden.mpd
index 47ca735044..e9c4348da4 100644
--- a/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-golden.mpd
+++ b/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-golden.mpd
@@ -17,12 +17,13 @@
-
+
-
-
+
+
+
diff --git a/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-no-pssh-golden.mpd b/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-no-pssh-golden.mpd
index 779863417c..9265f2e191 100644
--- a/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-no-pssh-golden.mpd
+++ b/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-no-pssh-golden.mpd
@@ -17,12 +17,13 @@
-
+
-
-
+
+
+
diff --git a/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-non-iop-golden.mpd b/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-non-iop-golden.mpd
index 90fa72cdd1..81eb4d90d5 100644
--- a/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-non-iop-golden.mpd
+++ b/packager/app/test/testdata/bear-640x360-av-live-cenc-rotation-non-iop-golden.mpd
@@ -15,14 +15,15 @@
-
+
-
-
+
+
+
diff --git a/packager/app/test/testdata/bear-640x360-av-live-golden.mpd b/packager/app/test/testdata/bear-640x360-av-live-golden.mpd
index 3bba3a7577..4faf8e66e8 100644
--- a/packager/app/test/testdata/bear-640x360-av-live-golden.mpd
+++ b/packager/app/test/testdata/bear-640x360-av-live-golden.mpd
@@ -13,12 +13,13 @@
-
+
-
-
+
+
+
diff --git a/packager/app/test/testdata/bear-640x360-av-live-static-ad_cues-golden.mpd b/packager/app/test/testdata/bear-640x360-av-live-static-ad_cues-golden.mpd
index 9d299d7fa4..2a43303cfd 100644
--- a/packager/app/test/testdata/bear-640x360-av-live-static-ad_cues-golden.mpd
+++ b/packager/app/test/testdata/bear-640x360-av-live-static-ad_cues-golden.mpd
@@ -12,27 +12,19 @@
-
+
-
+
+
+
-
-
-
-
-
-
-
-
-
-
@@ -42,5 +34,15 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/packager/app/test/testdata/bear-640x360-av-live-static-golden.mpd b/packager/app/test/testdata/bear-640x360-av-live-static-golden.mpd
index 53e77e5258..b29a49ff9a 100644
--- a/packager/app/test/testdata/bear-640x360-av-live-static-golden.mpd
+++ b/packager/app/test/testdata/bear-640x360-av-live-static-golden.mpd
@@ -13,12 +13,13 @@
-
+
-
-
+
+
+
diff --git a/packager/app/test/testdata/bear-640x360-av-mp4-master-cenc-ad_cues-golden.m3u8 b/packager/app/test/testdata/bear-640x360-av-mp4-master-cenc-ad_cues-golden.m3u8
new file mode 100644
index 0000000000..bc086b1b12
--- /dev/null
+++ b/packager/app/test/testdata/bear-640x360-av-mp4-master-cenc-ad_cues-golden.m3u8
@@ -0,0 +1,7 @@
+#EXTM3U
+## Generated with https://github.com/google/shaka-packager version --
+
+#EXT-X-MEDIA:TYPE=AUDIO,URI="audio.m3u8",GROUP-ID="default-audio-group",NAME="stream_0",AUTOSELECT=YES,CHANNELS="2"
+
+#EXT-X-STREAM-INF:BANDWIDTH=1152419,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
+video.m3u8
diff --git a/packager/app/test/testdata/bear-640x360-av-mp4-master-cenc-golden.m3u8 b/packager/app/test/testdata/bear-640x360-av-mp4-master-cenc-golden.m3u8
index b3f0c6f629..5178491030 100644
--- a/packager/app/test/testdata/bear-640x360-av-mp4-master-cenc-golden.m3u8
+++ b/packager/app/test/testdata/bear-640x360-av-mp4-master-cenc-golden.m3u8
@@ -3,5 +3,5 @@
#EXT-X-MEDIA:TYPE=AUDIO,URI="audio.m3u8",GROUP-ID="default-audio-group",NAME="stream_0",AUTOSELECT=YES,CHANNELS="2"
-#EXT-X-STREAM-INF:BANDWIDTH=1111340,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
+#EXT-X-STREAM-INF:BANDWIDTH=1111147,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
video.m3u8
diff --git a/packager/app/test/testdata/bear-640x360-av-mp4-master-golden.m3u8 b/packager/app/test/testdata/bear-640x360-av-mp4-master-golden.m3u8
index 7ce0555ec8..f4f5b1180c 100644
--- a/packager/app/test/testdata/bear-640x360-av-mp4-master-golden.m3u8
+++ b/packager/app/test/testdata/bear-640x360-av-mp4-master-golden.m3u8
@@ -3,5 +3,5 @@
#EXT-X-MEDIA:TYPE=AUDIO,URI="audio/audio.m3u8",GROUP-ID="default-audio-group",NAME="stream_0",AUTOSELECT=YES,CHANNELS="2"
-#EXT-X-STREAM-INF:BANDWIDTH=1105163,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
+#EXT-X-STREAM-INF:BANDWIDTH=1105129,CODECS="avc1.64001e,mp4a.40.2",RESOLUTION=640x360,AUDIO="default-audio-group"
video/video.m3u8
diff --git a/packager/app/test/testdata/bear-640x360-av-trick-1-cenc-golden.mpd b/packager/app/test/testdata/bear-640x360-av-trick-1-cenc-golden.mpd
index 9c7d256e7b..3980468707 100644
--- a/packager/app/test/testdata/bear-640x360-av-trick-1-cenc-golden.mpd
+++ b/packager/app/test/testdata/bear-640x360-av-trick-1-cenc-golden.mpd
@@ -32,7 +32,7 @@
AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA==
-
+
output_audio.mp4
diff --git a/packager/app/test/testdata/bear-640x360-av-trick-1-trick-2-cenc-golden.mpd b/packager/app/test/testdata/bear-640x360-av-trick-1-trick-2-cenc-golden.mpd
index 711c66824b..d0dda4b1cd 100644
--- a/packager/app/test/testdata/bear-640x360-av-trick-1-trick-2-cenc-golden.mpd
+++ b/packager/app/test/testdata/bear-640x360-av-trick-1-trick-2-cenc-golden.mpd
@@ -38,7 +38,7 @@
AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA==
-
+
output_audio.mp4
diff --git a/packager/app/test/testdata/bear-640x360-ec3-a-cenc-golden.mp4 b/packager/app/test/testdata/bear-640x360-ec3-a-cenc-golden.mp4
index 564ad7770b..3c04593b4a 100644
Binary files a/packager/app/test/testdata/bear-640x360-ec3-a-cenc-golden.mp4 and b/packager/app/test/testdata/bear-640x360-ec3-a-cenc-golden.mp4 differ
diff --git a/packager/app/test/testdata/bear-640x360-ec3-a-mp4-cenc-golden.m3u8 b/packager/app/test/testdata/bear-640x360-ec3-a-mp4-cenc-golden.m3u8
index 031fab01da..da52180f58 100644
--- a/packager/app/test/testdata/bear-640x360-ec3-a-mp4-cenc-golden.m3u8
+++ b/packager/app/test/testdata/bear-640x360-ec3-a-mp4-cenc-golden.m3u8
@@ -8,10 +8,10 @@
#EXTINF:1.010,
#EXT-X-BYTERANGE:24460@977
output_audio.mp4
-#EXTINF:0.975,
-#EXT-X-BYTERANGE:23899
+#EXTINF:1.010,
+#EXT-X-BYTERANGE:24747
output_audio.mp4
-#EXTINF:0.766,
-#EXT-X-BYTERANGE:18811
+#EXTINF:0.731,
+#EXT-X-BYTERANGE:17963
output_audio.mp4
#EXT-X-ENDLIST
diff --git a/packager/app/test/testdata/bear-640x360-ec3-av-mp4-master-cenc-golden.m3u8 b/packager/app/test/testdata/bear-640x360-ec3-av-mp4-master-cenc-golden.m3u8
index b7bec5bcf8..b79a840ecd 100644
--- a/packager/app/test/testdata/bear-640x360-ec3-av-mp4-master-cenc-golden.m3u8
+++ b/packager/app/test/testdata/bear-640x360-ec3-av-mp4-master-cenc-golden.m3u8
@@ -3,5 +3,5 @@
#EXT-X-MEDIA:TYPE=AUDIO,URI="audio.m3u8",GROUP-ID="default-audio-group",NAME="stream_0",AUTOSELECT=YES,CHANNELS="2"
-#EXT-X-STREAM-INF:BANDWIDTH=1174135,CODECS="avc1.64001e,ec-3",RESOLUTION=640x360,AUDIO="default-audio-group"
+#EXT-X-STREAM-INF:BANDWIDTH=1174212,CODECS="avc1.64001e,ec-3",RESOLUTION=640x360,AUDIO="default-audio-group"
video.m3u8
diff --git a/packager/app/test/testdata/bear-640x360-v-cenc-ad_cues-golden.mp4 b/packager/app/test/testdata/bear-640x360-v-cenc-ad_cues-golden.mp4
new file mode 100644
index 0000000000..43bfd90f1f
Binary files /dev/null and b/packager/app/test/testdata/bear-640x360-v-cenc-ad_cues-golden.mp4 differ
diff --git a/packager/app/test/testdata/bear-640x360-v-live-ad_cues-golden-1.m4s b/packager/app/test/testdata/bear-640x360-v-live-ad_cues-golden-1.m4s
new file mode 100644
index 0000000000..e8a21a613c
Binary files /dev/null and b/packager/app/test/testdata/bear-640x360-v-live-ad_cues-golden-1.m4s differ
diff --git a/packager/app/test/testdata/bear-640x360-v-live-ad_cues-golden-2.m4s b/packager/app/test/testdata/bear-640x360-v-live-ad_cues-golden-2.m4s
new file mode 100644
index 0000000000..311f93260e
Binary files /dev/null and b/packager/app/test/testdata/bear-640x360-v-live-ad_cues-golden-2.m4s differ
diff --git a/packager/app/test/testdata/bear-640x360-v-live-ad_cues-golden-3.m4s b/packager/app/test/testdata/bear-640x360-v-live-ad_cues-golden-3.m4s
new file mode 100644
index 0000000000..71e371e17a
Binary files /dev/null and b/packager/app/test/testdata/bear-640x360-v-live-ad_cues-golden-3.m4s differ
diff --git a/packager/app/test/testdata/bear-640x360-v-live-ad_cues-golden-init.mp4 b/packager/app/test/testdata/bear-640x360-v-live-ad_cues-golden-init.mp4
new file mode 100644
index 0000000000..5ed12ec053
Binary files /dev/null and b/packager/app/test/testdata/bear-640x360-v-live-ad_cues-golden-init.mp4 differ
diff --git a/packager/media/base/media_handler.cc b/packager/media/base/media_handler.cc
index 7d16d87877..e26bce3745 100644
--- a/packager/media/base/media_handler.cc
+++ b/packager/media/base/media_handler.cc
@@ -49,7 +49,7 @@ bool MediaHandler::ValidateOutputStreamIndex(size_t stream_index) const {
return stream_index < num_input_streams_;
}
-Status MediaHandler::Dispatch(std::unique_ptr stream_data) {
+Status MediaHandler::Dispatch(std::unique_ptr stream_data) const {
size_t output_stream_index = stream_data->stream_index;
auto handler_it = output_handlers_.find(output_stream_index);
if (handler_it == output_handlers_.end()) {
diff --git a/packager/media/base/media_handler.h b/packager/media/base/media_handler.h
index e4875a006e..a01693fe63 100644
--- a/packager/media/base/media_handler.h
+++ b/packager/media/base/media_handler.h
@@ -185,43 +185,54 @@ class MediaHandler {
/// Dispatch the stream data to downstream handlers. Note that
/// stream_data.stream_index should be the output stream index.
- Status Dispatch(std::unique_ptr stream_data);
+ Status Dispatch(std::unique_ptr stream_data) const;
/// Dispatch the stream info to downstream handlers.
Status DispatchStreamInfo(
- size_t stream_index, std::shared_ptr stream_info) {
- return Dispatch(StreamData::FromStreamInfo(stream_index, stream_info));
+ size_t stream_index,
+ std::shared_ptr stream_info) const {
+ return Dispatch(
+ StreamData::FromStreamInfo(stream_index, std::move(stream_info)));
}
/// Dispatch the media sample to downstream handlers.
Status DispatchMediaSample(
- size_t stream_index, std::shared_ptr media_sample) {
- return Dispatch(StreamData::FromMediaSample(stream_index, media_sample));
+ size_t stream_index,
+ std::shared_ptr media_sample) const {
+ return Dispatch(
+ StreamData::FromMediaSample(stream_index, std::move(media_sample)));
}
/// Dispatch the text sample to downsream handlers.
// DispatchTextSample should only be override for testing.
Status DispatchTextSample(
- size_t stream_index, std::shared_ptr text_sample) {
- return Dispatch(StreamData::FromTextSample(stream_index, text_sample));
+ size_t stream_index,
+ std::shared_ptr text_sample) const {
+ return Dispatch(
+ StreamData::FromTextSample(stream_index, std::move(text_sample)));
}
/// Dispatch the segment info to downstream handlers.
Status DispatchSegmentInfo(
- size_t stream_index, std::shared_ptr segment_info) {
- return Dispatch(StreamData::FromSegmentInfo(stream_index, segment_info));
+ size_t stream_index,
+ std::shared_ptr segment_info) const {
+ return Dispatch(
+ StreamData::FromSegmentInfo(stream_index, std::move(segment_info)));
}
/// Dispatch the scte35 event to downstream handlers.
- Status DispatchScte35Event(size_t stream_index,
- std::shared_ptr scte35_event) {
- return Dispatch(StreamData::FromScte35Event(stream_index, scte35_event));
+ Status DispatchScte35Event(
+ size_t stream_index,
+ std::shared_ptr scte35_event) const {
+ return Dispatch(
+ StreamData::FromScte35Event(stream_index, std::move(scte35_event)));
}
/// Dispatch the cue event to downstream handlers.
Status DispatchCueEvent(size_t stream_index,
- std::shared_ptr cue_event) {
- return Dispatch(StreamData::FromCueEvent(stream_index, cue_event));
+ std::shared_ptr cue_event) const {
+ return Dispatch(
+ StreamData::FromCueEvent(stream_index, std::move(cue_event)));
}
/// Flush the downstream connected at the specified output stream index.
diff --git a/packager/media/chunking/chunking_handler.cc b/packager/media/chunking/chunking_handler.cc
index bbc08c4674..6c1c5578de 100644
--- a/packager/media/chunking/chunking_handler.cc
+++ b/packager/media/chunking/chunking_handler.cc
@@ -9,46 +9,38 @@
#include
#include "packager/base/logging.h"
-#include "packager/base/threading/platform_thread.h"
#include "packager/media/base/media_sample.h"
namespace shaka {
namespace media {
namespace {
-int64_t kThreadIdUnset = -1;
+const size_t kStreamIndex = 0;
} // namespace
ChunkingHandler::ChunkingHandler(const ChunkingParams& chunking_params)
- : chunking_params_(chunking_params),
- thread_id_(kThreadIdUnset),
- media_sample_comparator_(this),
- cached_media_sample_stream_data_(media_sample_comparator_) {
+ : chunking_params_(chunking_params) {
CHECK_NE(chunking_params.segment_duration_in_seconds, 0u);
}
-ChunkingHandler::~ChunkingHandler() {}
-
Status ChunkingHandler::InitializeInternal() {
- segment_info_.resize(num_input_streams());
- subsegment_info_.resize(num_input_streams());
- time_scales_.resize(num_input_streams());
- last_sample_end_timestamps_.resize(num_input_streams());
- num_cached_samples_.resize(num_input_streams());
+ if (num_input_streams() != 1 || next_output_stream_index() != 1) {
+ return Status(error::INVALID_ARGUMENT,
+ "Expects exactly one input and one output.");
+ }
return Status::OK;
}
Status ChunkingHandler::Process(std::unique_ptr stream_data) {
switch (stream_data->stream_data_type) {
case StreamDataType::kStreamInfo:
- return OnStreamInfo(stream_data->stream_index, stream_data->stream_info);
- case StreamDataType::kScte35Event:
- return OnScte35Event(stream_data->stream_index,
- stream_data->scte35_event);
+ return OnStreamInfo(std::move(stream_data->stream_info));
+ case StreamDataType::kCueEvent:
+ return OnCueEvent(std::move(stream_data->cue_event));
case StreamDataType::kSegmentInfo:
VLOG(3) << "Droppping existing segment info.";
return Status::OK;
case StreamDataType::kMediaSample:
- return OnMediaSample(std::move(stream_data));
+ return OnMediaSample(std::move(stream_data->media_sample));
default:
VLOG(3) << "Stream data type "
<< static_cast(stream_data->stream_data_type) << " ignored.";
@@ -57,289 +49,103 @@ Status ChunkingHandler::Process(std::unique_ptr stream_data) {
}
Status ChunkingHandler::OnFlushRequest(size_t input_stream_index) {
- // Process all cached samples.
- while (!cached_media_sample_stream_data_.empty()) {
- Status status =
- ProcessMediaSampleStreamData(*cached_media_sample_stream_data_.top());
- if (!status.ok())
- return status;
- --num_cached_samples_[cached_media_sample_stream_data_.top()->stream_index];
- cached_media_sample_stream_data_.pop();
- }
- if (segment_info_[input_stream_index]) {
- auto& segment_info = segment_info_[input_stream_index];
- if (segment_info->start_timestamp != -1) {
- segment_info->duration = last_sample_end_timestamps_[input_stream_index] -
- segment_info->start_timestamp;
- Status status =
- DispatchSegmentInfo(input_stream_index, std::move(segment_info));
- if (!status.ok())
- return status;
- }
- }
- const size_t output_stream_index = input_stream_index;
- return FlushDownstream(output_stream_index);
+ Status status = EndSegmentIfStarted();
+ if (!status.ok())
+ return status;
+ return FlushDownstream(kStreamIndex);
}
-Status ChunkingHandler::OnStreamInfo(uint64_t stream_index,
- std::shared_ptr info) {
- // Make sure the inputs come from the same thread.
- const int64_t thread_id =
- static_cast(base::PlatformThread::CurrentId());
-
- int64_t expected = kThreadIdUnset;
- if (!thread_id_.compare_exchange_strong(expected, thread_id) &&
- expected != thread_id) {
- return Status(error::CHUNKING_ERROR,
- "Inputs should come from the same thread.");
- }
-
- const auto time_scale = info->time_scale();
- time_scales_[stream_index] = time_scale;
-
- // The video stream is treated as the main stream. If there is only one
- // stream, it is the main stream.
- const bool is_main_stream =
- main_stream_index_ == kInvalidStreamIndex &&
- (info->stream_type() == kStreamVideo || num_input_streams() == 1);
- if (is_main_stream) {
- main_stream_index_ = stream_index;
- segment_duration_ =
- chunking_params_.segment_duration_in_seconds * time_scale;
- subsegment_duration_ =
- chunking_params_.subsegment_duration_in_seconds * time_scale;
- } else if (info->stream_type() == kStreamVideo) {
- return Status(error::CHUNKING_ERROR,
- "Only one video stream is allowed per chunking handler.");
- }
-
- return DispatchStreamInfo(stream_index, std::move(info));
+Status ChunkingHandler::OnStreamInfo(std::shared_ptr info) {
+ time_scale_ = info->time_scale();
+ segment_duration_ =
+ chunking_params_.segment_duration_in_seconds * time_scale_;
+ subsegment_duration_ =
+ chunking_params_.subsegment_duration_in_seconds * time_scale_;
+ return DispatchStreamInfo(kStreamIndex, std::move(info));
}
-Status ChunkingHandler::OnScte35Event(
- uint64_t stream_index,
- std::shared_ptr event) {
- if (stream_index == main_stream_index_) {
- scte35_events_.push(std::move(event));
- } else {
- VLOG(3) << "Dropping scte35 event from non main stream.";
- }
-
- return Status::OK;
+Status ChunkingHandler::OnCueEvent(std::shared_ptr event) {
+ Status status = EndSegmentIfStarted();
+ if (!status.ok())
+ return status;
+ // Force start new segment after cue event.
+ segment_start_time_ = base::nullopt;
+ return DispatchCueEvent(kStreamIndex, std::move(event));
}
-Status ChunkingHandler::OnMediaSample(std::unique_ptr stream_data) {
- DCHECK_EQ(StreamDataType::kMediaSample, stream_data->stream_data_type);
+Status ChunkingHandler::OnMediaSample(
+ std::shared_ptr sample) {
+ DCHECK_NE(time_scale_, 0u) << "kStreamInfo should arrive before kMediaSample";
- const size_t stream_index = stream_data->stream_index;
- DCHECK_NE(time_scales_[stream_index], 0u)
- << "kStreamInfo should arrive before kMediaSample";
-
- if (stream_index != main_stream_index_ &&
- !stream_data->media_sample->is_key_frame()) {
- return Status(error::CHUNKING_ERROR,
- "All non video samples should be key frames.");
- }
- // The streams are expected to be roughly synchronized, so we don't expect
- // to see a lot of samples from one stream but no samples from another
- // stream.
- // The value is kind of arbitrary here. For a 24fps video, it is ~40s.
- const size_t kMaxCachedSamplesPerStream = 1000u;
- if (num_cached_samples_[stream_index] >= kMaxCachedSamplesPerStream) {
- LOG(ERROR) << "Streams are not synchronized:";
- for (size_t i = 0; i < num_cached_samples_.size(); ++i)
- LOG(ERROR) << " [Stream " << i << "] " << num_cached_samples_[i];
- return Status(error::CHUNKING_ERROR, "Streams are not synchronized.");
- }
-
- cached_media_sample_stream_data_.push(std::move(stream_data));
- ++num_cached_samples_[stream_index];
-
- // If we have cached samples from every stream, the first sample in
- // |cached_media_samples_stream_data_| is guaranteed to be the earliest
- // sample. Extract and process that sample.
- if (std::all_of(num_cached_samples_.begin(), num_cached_samples_.end(),
- [](size_t num_samples) { return num_samples > 0; })) {
- while (true) {
- const size_t top_stream_index =
- cached_media_sample_stream_data_.top()->stream_index;
- Status status =
- ProcessMediaSampleStreamData(*cached_media_sample_stream_data_.top());
- if (!status.ok())
- return status;
- cached_media_sample_stream_data_.pop();
- if (--num_cached_samples_[top_stream_index] == 0)
- break;
- }
- }
- return Status::OK;
-}
-
-Status ChunkingHandler::ProcessMainMediaSample(const MediaSample* sample) {
- const bool is_key_frame = sample->is_key_frame();
const int64_t timestamp = sample->dts();
- const int64_t time_scale = time_scales_[main_stream_index_];
- const double dts_in_seconds = static_cast(sample->dts()) / time_scale;
- // Check if we need to terminate the current (sub)segment.
- bool new_segment = false;
- bool new_subsegment = false;
- std::shared_ptr cue_event;
- if (is_key_frame || !chunking_params_.segment_sap_aligned) {
+ bool started_new_segment = false;
+ const bool can_start_new_segment =
+ sample->is_key_frame() || !chunking_params_.segment_sap_aligned;
+ if (can_start_new_segment) {
const int64_t segment_index = timestamp / segment_duration_;
- if (segment_index != current_segment_index_) {
+ if (!segment_start_time_ || segment_index != current_segment_index_) {
current_segment_index_ = segment_index;
// Reset subsegment index.
current_subsegment_index_ = 0;
- new_segment = true;
- }
- // We use 'while' instead of 'if' to make sure to pop off multiple SCTE35
- // events that may be very close to each other.
- while (!scte35_events_.empty() &&
- scte35_events_.top()->start_time_in_seconds <= dts_in_seconds) {
- // For simplicity, don't change |current_segment_index_|.
- current_subsegment_index_ = 0;
- new_segment = true;
- cue_event = std::make_shared();
- cue_event->time_in_seconds =
- static_cast(sample->pts()) / time_scale;
- cue_event->cue_data = scte35_events_.top()->cue_data;
- LOG(INFO) << "Chunked at " << dts_in_seconds << " seconds for Ad Cue.";
-
- scte35_events_.pop();
+ Status status = EndSegmentIfStarted();
+ if (!status.ok())
+ return status;
+ segment_start_time_ = timestamp;
+ subsegment_start_time_ = timestamp;
+ started_new_segment = true;
}
}
- if (!new_segment && subsegment_duration_ > 0 &&
- (is_key_frame || !chunking_params_.subsegment_sap_aligned)) {
- const int64_t subsegment_index =
- (timestamp - segment_info_[main_stream_index_]->start_timestamp) /
- subsegment_duration_;
- if (subsegment_index != current_subsegment_index_) {
- current_subsegment_index_ = subsegment_index;
- new_subsegment = true;
+ if (!started_new_segment && IsSubsegmentEnabled()) {
+ const bool can_start_new_subsegment =
+ sample->is_key_frame() || !chunking_params_.subsegment_sap_aligned;
+ if (can_start_new_subsegment) {
+ const int64_t subsegment_index =
+ (timestamp - segment_start_time_.value()) / subsegment_duration_;
+ if (subsegment_index != current_subsegment_index_) {
+ current_subsegment_index_ = subsegment_index;
+
+ Status status = EndSubsegmentIfStarted();
+ if (!status.ok())
+ return status;
+ subsegment_start_time_ = timestamp;
+ }
}
}
- Status status;
- if (new_segment) {
- status.Update(DispatchSegmentInfoForAllStreams());
- segment_info_[main_stream_index_]->start_timestamp = timestamp;
-
- if (cue_event)
- status.Update(DispatchCueEventForAllStreams(std::move(cue_event)));
- }
- if (subsegment_duration_ > 0 && (new_segment || new_subsegment)) {
- status.Update(DispatchSubsegmentInfoForAllStreams());
- subsegment_info_[main_stream_index_]->start_timestamp = timestamp;
- }
- return status;
-}
-
-Status ChunkingHandler::ProcessMediaSampleStreamData(
- const StreamData& media_sample_stream_data) {
- const size_t stream_index = media_sample_stream_data.stream_index;
- const auto sample = std::move(media_sample_stream_data.media_sample);
-
- if (stream_index == main_stream_index_) {
- Status status = ProcessMainMediaSample(sample.get());
- if (!status.ok())
- return status;
- }
-
- VLOG(3) << "Stream index: " << stream_index << " "
- << "Sample ts: " << sample->dts() << " "
- << " duration: " << sample->duration()
- << " scale: " << time_scales_[stream_index] << "\n"
- << " scale: " << time_scales_[main_stream_index_]
- << (segment_info_[stream_index] ? " dispatch " : " discard ");
+ VLOG(3) << "Sample ts: " << timestamp << " "
+ << " duration: " << sample->duration() << " scale: " << time_scale_
+ << (segment_start_time_ ? " dispatch " : " discard ");
// Discard samples before segment start. If the segment has started,
- // |segment_info_[stream_index]| won't be null.
- if (!segment_info_[stream_index])
+ // |segment_start_time_| won't be null.
+ if (!segment_start_time_)
return Status::OK;
- if (segment_info_[stream_index]->start_timestamp == -1)
- segment_info_[stream_index]->start_timestamp = sample->dts();
- if (subsegment_info_[stream_index] &&
- subsegment_info_[stream_index]->start_timestamp == -1) {
- subsegment_info_[stream_index]->start_timestamp = sample->dts();
- }
- last_sample_end_timestamps_[stream_index] =
- sample->dts() + sample->duration();
- return DispatchMediaSample(stream_index, std::move(sample));
+ last_sample_end_timestamp_ = timestamp + sample->duration();
+ return DispatchMediaSample(kStreamIndex, std::move(sample));
}
-Status ChunkingHandler::DispatchSegmentInfoForAllStreams() {
- Status status;
- for (size_t i = 0; i < segment_info_.size() && status.ok(); ++i) {
- if (segment_info_[i] && segment_info_[i]->start_timestamp != -1) {
- segment_info_[i]->duration =
- last_sample_end_timestamps_[i] - segment_info_[i]->start_timestamp;
- status.Update(DispatchSegmentInfo(i, std::move(segment_info_[i])));
- }
- segment_info_[i].reset(new SegmentInfo);
- subsegment_info_[i].reset();
- }
- return status;
+Status ChunkingHandler::EndSegmentIfStarted() const {
+ if (!segment_start_time_)
+ return Status::OK;
+
+ auto segment_info = std::make_shared();
+ segment_info->start_timestamp = segment_start_time_.value();
+ segment_info->duration =
+ last_sample_end_timestamp_ - segment_start_time_.value();
+ return DispatchSegmentInfo(kStreamIndex, std::move(segment_info));
}
-Status ChunkingHandler::DispatchSubsegmentInfoForAllStreams() {
- Status status;
- for (size_t i = 0; i < subsegment_info_.size() && status.ok(); ++i) {
- if (subsegment_info_[i] && subsegment_info_[i]->start_timestamp != -1) {
- subsegment_info_[i]->duration =
- last_sample_end_timestamps_[i] - subsegment_info_[i]->start_timestamp;
- status.Update(DispatchSegmentInfo(i, std::move(subsegment_info_[i])));
- }
- subsegment_info_[i].reset(new SegmentInfo);
- subsegment_info_[i]->is_subsegment = true;
- }
- return status;
-}
+Status ChunkingHandler::EndSubsegmentIfStarted() const {
+ if (!subsegment_start_time_)
+ return Status::OK;
-Status ChunkingHandler::DispatchCueEventForAllStreams(
- std::shared_ptr cue_event) {
- Status status;
- for (size_t i = 0; i < segment_info_.size() && status.ok(); ++i) {
- status.Update(DispatchCueEvent(i, cue_event));
- }
- return status;
-}
-
-ChunkingHandler::MediaSampleTimestampGreater::MediaSampleTimestampGreater(
- const ChunkingHandler* const chunking_handler)
- : chunking_handler_(chunking_handler) {}
-
-bool ChunkingHandler::MediaSampleTimestampGreater::operator()(
- const std::unique_ptr& lhs,
- const std::unique_ptr& rhs) const {
- DCHECK(lhs);
- DCHECK(rhs);
- return GetSampleTimeInSeconds(*lhs) > GetSampleTimeInSeconds(*rhs);
-}
-
-double ChunkingHandler::MediaSampleTimestampGreater::GetSampleTimeInSeconds(
- const StreamData& media_sample_stream_data) const {
- const size_t stream_index = media_sample_stream_data.stream_index;
- const auto& sample = media_sample_stream_data.media_sample;
- DCHECK(sample);
- // Order main samples by left boundary and non main samples by mid-point. This
- // ensures non main samples are properly chunked, i.e. if the portion of the
- // sample in the next chunk is bigger than the portion of the sample in the
- // previous chunk, the sample is placed in the next chunk.
- const uint64_t timestamp =
- stream_index == chunking_handler_->main_stream_index_
- ? sample->dts()
- : (sample->dts() + sample->duration() / 2);
- return static_cast(timestamp) /
- chunking_handler_->time_scales_[stream_index];
-}
-
-bool ChunkingHandler::Scte35EventTimestampGreater::operator()(
- const std::shared_ptr& lhs,
- const std::shared_ptr& rhs) const {
- DCHECK(lhs);
- DCHECK(rhs);
- return lhs->start_time_in_seconds > rhs->start_time_in_seconds;
+ auto subsegment_info = std::make_shared();
+ subsegment_info->start_timestamp = subsegment_start_time_.value();
+ subsegment_info->duration =
+ last_sample_end_timestamp_ - subsegment_start_time_.value();
+ subsegment_info->is_subsegment = true;
+ return DispatchSegmentInfo(kStreamIndex, std::move(subsegment_info));
}
} // namespace media
diff --git a/packager/media/chunking/chunking_handler.h b/packager/media/chunking/chunking_handler.h
index fdc8b54f08..d59e917bfb 100644
--- a/packager/media/chunking/chunking_handler.h
+++ b/packager/media/chunking/chunking_handler.h
@@ -11,6 +11,7 @@
#include
#include "packager/base/logging.h"
+#include "packager/base/optional.h"
#include "packager/media/base/media_handler.h"
#include "packager/media/public/chunking_params.h"
@@ -19,9 +20,7 @@ namespace media {
/// ChunkingHandler splits the samples into segments / subsegments based on the
/// specified chunking params.
-/// This handler is a multi-in multi-out handler. If more than one input is
-/// provided, there should be one and only one video stream; also, all inputs
-/// should come from the same thread and are synchronized.
+/// This handler is a one-in one-out handler.
/// There can be multiple chunking handler running in different threads or even
/// different processes, we use the "consistent chunking algorithm" to make sure
/// the chunks in different streams are aligned without explicit communcating
@@ -36,17 +35,11 @@ namespace media {
/// 2. Chunk only at the consistent chunkable boundary
///
/// This algorithm will make sure the chunks from different video streams are
-/// aligned if they have aligned GoPs. However, this algorithm will only work
-/// for video streams. To be able to chunk non video streams at similar
-/// positions as video streams, ChunkingHandler is designed to accept one video
-/// input and multiple non video inputs, the non video inputs are chunked when
-/// the video input is chunked. If the inputs are synchronized - which is true
-/// if the inputs come from the same demuxer, the video and non video chunks
-/// are aligned.
+/// aligned if they have aligned GoPs.
class ChunkingHandler : public MediaHandler {
public:
explicit ChunkingHandler(const ChunkingParams& chunking_params);
- ~ChunkingHandler() override;
+ ~ChunkingHandler() override = default;
protected:
/// @name MediaHandler implementation overrides.
@@ -62,84 +55,34 @@ class ChunkingHandler : public MediaHandler {
ChunkingHandler(const ChunkingHandler&) = delete;
ChunkingHandler& operator=(const ChunkingHandler&) = delete;
- Status OnStreamInfo(uint64_t stream_index,
- std::shared_ptr info);
- Status OnScte35Event(uint64_t stream_index,
- std::shared_ptr event);
+ Status OnStreamInfo(std::shared_ptr info);
+ Status OnCueEvent(std::shared_ptr event);
+ Status OnMediaSample(std::shared_ptr sample);
- Status OnMediaSample(std::unique_ptr stream_data);
+ Status EndSegmentIfStarted() const;
+ Status EndSubsegmentIfStarted() const;
- // Processes main media sample and apply chunking if needed.
- Status ProcessMainMediaSample(const MediaSample* sample);
-
- // Processes and dispatches media sample.
- Status ProcessMediaSampleStreamData(const StreamData& media_data);
-
- // The (sub)segments are aligned and dispatched together.
- Status DispatchSegmentInfoForAllStreams();
- Status DispatchSubsegmentInfoForAllStreams();
- Status DispatchCueEventForAllStreams(std::shared_ptr cue_event);
+ bool IsSubsegmentEnabled() {
+ return subsegment_duration_ > 0 &&
+ subsegment_duration_ != segment_duration_;
+ }
const ChunkingParams chunking_params_;
- // The inputs are expected to come from the same thread.
- std::atomic thread_id_;
-
- // The video stream is the main stream; if there is only one stream, it is the
- // main stream. The chunking is based on the main stream.
- const size_t kInvalidStreamIndex = static_cast(-1);
- size_t main_stream_index_ = kInvalidStreamIndex;
- // Segment and subsegment duration in main stream's time scale.
+ // Segment and subsegment duration in stream's time scale.
int64_t segment_duration_ = 0;
int64_t subsegment_duration_ = 0;
- class MediaSampleTimestampGreater {
- public:
- explicit MediaSampleTimestampGreater(
- const ChunkingHandler* const chunking_handler);
-
- // Comparison operator. Used by |media_samples_| priority queue below to
- // sort the media samples.
- bool operator()(const std::unique_ptr& lhs,
- const std::unique_ptr& rhs) const;
-
- private:
- double GetSampleTimeInSeconds(
- const StreamData& media_sample_stream_data) const;
-
- const ChunkingHandler* const chunking_handler_ = nullptr;
- };
- MediaSampleTimestampGreater media_sample_comparator_;
- // Caches media samples and sort the samples.
- std::priority_queue,
- std::vector>,
- MediaSampleTimestampGreater>
- cached_media_sample_stream_data_;
- // Tracks number of cached samples in input streams.
- std::vector num_cached_samples_;
-
// Current segment index, useful to determine where to do chunking.
int64_t current_segment_index_ = -1;
// Current subsegment index, useful to determine where to do chunking.
int64_t current_subsegment_index_ = -1;
- std::vector> segment_info_;
- std::vector> subsegment_info_;
- std::vector time_scales_;
+ base::Optional segment_start_time_;
+ base::Optional subsegment_start_time_;
+ uint32_t time_scale_ = 0;
// The end timestamp of the last dispatched sample.
- std::vector last_sample_end_timestamps_;
-
- struct Scte35EventTimestampGreater {
- bool operator()(const std::shared_ptr& lhs,
- const std::shared_ptr& rhs) const;
- };
- // Captures all incoming SCTE35 events to identify chunking points. Events
- // will be removed from this queue one at a time as soon as the correct
- // chunking point is identified in the incoming samples.
- std::priority_queue,
- std::vector>,
- Scte35EventTimestampGreater>
- scte35_events_;
+ int64_t last_sample_end_timestamp_ = 0;
};
} // namespace media
diff --git a/packager/media/chunking/chunking_handler_unittest.cc b/packager/media/chunking/chunking_handler_unittest.cc
index d02b02c8e9..c8085a272a 100644
--- a/packager/media/chunking/chunking_handler_unittest.cc
+++ b/packager/media/chunking/chunking_handler_unittest.cc
@@ -18,12 +18,10 @@ using ::testing::IsEmpty;
namespace shaka {
namespace media {
namespace {
-const size_t kStreamIndex0 = 0;
-const size_t kStreamIndex1 = 1;
+const size_t kStreamIndex = 0;
const uint32_t kTimeScale0 = 800;
const uint32_t kTimeScale1 = 1000;
-const int64_t kDuration0 = 200;
-const int64_t kDuration1 = 300;
+const int64_t kDuration = 300;
const bool kKeyFrame = true;
const bool kIsSubsegment = true;
const bool kEncrypted = true;
@@ -57,34 +55,34 @@ TEST_F(ChunkingHandlerTest, AudioNoSubsegmentsThenFlush) {
SetUpChunkingHandler(1, chunking_params);
ASSERT_OK(Process(StreamData::FromStreamInfo(
- kStreamIndex0, GetAudioStreamInfo(kTimeScale0))));
+ kStreamIndex, GetAudioStreamInfo(kTimeScale0))));
EXPECT_THAT(
GetOutputStreamDataVector(),
- ElementsAre(IsStreamInfo(kStreamIndex0, kTimeScale0, !kEncrypted)));
+ ElementsAre(IsStreamInfo(kStreamIndex, kTimeScale0, !kEncrypted)));
for (int i = 0; i < 5; ++i) {
ClearOutputStreamDataVector();
ASSERT_OK(Process(StreamData::FromMediaSample(
- kStreamIndex0, GetMediaSample(i * kDuration1, kDuration1, kKeyFrame))));
+ kStreamIndex, GetMediaSample(i * kDuration, kDuration, kKeyFrame))));
// One output stream_data except when i == 3, which also has SegmentInfo.
if (i == 3) {
EXPECT_THAT(GetOutputStreamDataVector(),
- ElementsAre(IsSegmentInfo(kStreamIndex0, 0, kDuration1 * 3,
+ ElementsAre(IsSegmentInfo(kStreamIndex, 0, kDuration * 3,
!kIsSubsegment, !kEncrypted),
- IsMediaSample(kStreamIndex0, i * kDuration1,
- kDuration1, !kEncrypted)));
+ IsMediaSample(kStreamIndex, i * kDuration,
+ kDuration, !kEncrypted)));
} else {
EXPECT_THAT(GetOutputStreamDataVector(),
- ElementsAre(IsMediaSample(kStreamIndex0, i * kDuration1,
- kDuration1, !kEncrypted)));
+ ElementsAre(IsMediaSample(kStreamIndex, i * kDuration,
+ kDuration, !kEncrypted)));
}
}
ClearOutputStreamDataVector();
- ASSERT_OK(OnFlushRequest(kStreamIndex0));
+ ASSERT_OK(OnFlushRequest(kStreamIndex));
EXPECT_THAT(
GetOutputStreamDataVector(),
- ElementsAre(IsSegmentInfo(kStreamIndex0, kDuration1 * 3, kDuration1 * 2,
+ ElementsAre(IsSegmentInfo(kStreamIndex, kDuration * 3, kDuration * 2,
!kIsSubsegment, !kEncrypted)));
}
@@ -95,25 +93,24 @@ TEST_F(ChunkingHandlerTest, AudioWithSubsegments) {
SetUpChunkingHandler(1, chunking_params);
ASSERT_OK(Process(StreamData::FromStreamInfo(
- kStreamIndex0, GetAudioStreamInfo(kTimeScale0))));
+ kStreamIndex, GetAudioStreamInfo(kTimeScale0))));
for (int i = 0; i < 5; ++i) {
ASSERT_OK(Process(StreamData::FromMediaSample(
- kStreamIndex0, GetMediaSample(i * kDuration1, kDuration1, kKeyFrame))));
+ kStreamIndex, GetMediaSample(i * kDuration, kDuration, kKeyFrame))));
}
EXPECT_THAT(
GetOutputStreamDataVector(),
ElementsAre(
- IsStreamInfo(kStreamIndex0, kTimeScale0, !kEncrypted),
- IsMediaSample(kStreamIndex0, 0, kDuration1, !kEncrypted),
- IsMediaSample(kStreamIndex0, kDuration1, kDuration1, !kEncrypted),
- IsSegmentInfo(kStreamIndex0, 0, kDuration1 * 2, kIsSubsegment,
+ IsStreamInfo(kStreamIndex, kTimeScale0, !kEncrypted),
+ IsMediaSample(kStreamIndex, 0, kDuration, !kEncrypted),
+ IsMediaSample(kStreamIndex, kDuration, kDuration, !kEncrypted),
+ IsSegmentInfo(kStreamIndex, 0, kDuration * 2, kIsSubsegment,
!kEncrypted),
- IsMediaSample(kStreamIndex0, 2 * kDuration1, kDuration1, !kEncrypted),
- IsSegmentInfo(kStreamIndex0, 0, kDuration1 * 3, !kIsSubsegment,
+ IsMediaSample(kStreamIndex, 2 * kDuration, kDuration, !kEncrypted),
+ IsSegmentInfo(kStreamIndex, 0, kDuration * 3, !kIsSubsegment,
!kEncrypted),
- IsMediaSample(kStreamIndex0, 3 * kDuration1, kDuration1, !kEncrypted),
- IsMediaSample(kStreamIndex0, 4 * kDuration1, kDuration1,
- !kEncrypted)));
+ IsMediaSample(kStreamIndex, 3 * kDuration, kDuration, !kEncrypted),
+ IsMediaSample(kStreamIndex, 4 * kDuration, kDuration, !kEncrypted)));
}
TEST_F(ChunkingHandlerTest, VideoAndSubsegmentAndNonzeroStart) {
@@ -123,193 +120,79 @@ TEST_F(ChunkingHandlerTest, VideoAndSubsegmentAndNonzeroStart) {
SetUpChunkingHandler(1, chunking_params);
ASSERT_OK(Process(StreamData::FromStreamInfo(
- kStreamIndex0, GetVideoStreamInfo(kTimeScale1))));
+ kStreamIndex, GetVideoStreamInfo(kTimeScale1))));
const int64_t kVideoStartTimestamp = 12345;
for (int i = 0; i < 6; ++i) {
// Alternate key frame.
const bool is_key_frame = (i % 2) == 1;
ASSERT_OK(Process(StreamData::FromMediaSample(
- kStreamIndex0, GetMediaSample(kVideoStartTimestamp + i * kDuration1,
- kDuration1, is_key_frame))));
+ kStreamIndex, GetMediaSample(kVideoStartTimestamp + i * kDuration,
+ kDuration, is_key_frame))));
}
EXPECT_THAT(
GetOutputStreamDataVector(),
ElementsAre(
- IsStreamInfo(kStreamIndex0, kTimeScale1, !kEncrypted),
+ IsStreamInfo(kStreamIndex, kTimeScale1, !kEncrypted),
// The first samples @ kStartTimestamp is discarded - not key frame.
- IsMediaSample(kStreamIndex0, kVideoStartTimestamp + kDuration1,
- kDuration1, !kEncrypted),
- IsMediaSample(kStreamIndex0, kVideoStartTimestamp + kDuration1 * 2,
- kDuration1, !kEncrypted),
+ IsMediaSample(kStreamIndex, kVideoStartTimestamp + kDuration,
+ kDuration, !kEncrypted),
+ IsMediaSample(kStreamIndex, kVideoStartTimestamp + kDuration * 2,
+ kDuration, !kEncrypted),
// The next segment boundary 13245 / 1000 != 12645 / 1000.
- IsSegmentInfo(kStreamIndex0, kVideoStartTimestamp + kDuration1,
- kDuration1 * 2, !kIsSubsegment, !kEncrypted),
- IsMediaSample(kStreamIndex0, kVideoStartTimestamp + kDuration1 * 3,
- kDuration1, !kEncrypted),
- IsMediaSample(kStreamIndex0, kVideoStartTimestamp + kDuration1 * 4,
- kDuration1, !kEncrypted),
- // The subsegment has duration kDuration1 * 2 since it can only
+ IsSegmentInfo(kStreamIndex, kVideoStartTimestamp + kDuration,
+ kDuration * 2, !kIsSubsegment, !kEncrypted),
+ IsMediaSample(kStreamIndex, kVideoStartTimestamp + kDuration * 3,
+ kDuration, !kEncrypted),
+ IsMediaSample(kStreamIndex, kVideoStartTimestamp + kDuration * 4,
+ kDuration, !kEncrypted),
+ // The subsegment has duration kDuration * 2 since it can only
// terminate before key frame.
- IsSegmentInfo(kStreamIndex0, kVideoStartTimestamp + kDuration1 * 3,
- kDuration1 * 2, kIsSubsegment, !kEncrypted),
- IsMediaSample(kStreamIndex0, kVideoStartTimestamp + kDuration1 * 5,
- kDuration1, !kEncrypted)));
+ IsSegmentInfo(kStreamIndex, kVideoStartTimestamp + kDuration * 3,
+ kDuration * 2, kIsSubsegment, !kEncrypted),
+ IsMediaSample(kStreamIndex, kVideoStartTimestamp + kDuration * 5,
+ kDuration, !kEncrypted)));
}
-TEST_F(ChunkingHandlerTest, AudioAndVideo) {
- ChunkingParams chunking_params;
- chunking_params.segment_duration_in_seconds = 1;
- chunking_params.subsegment_duration_in_seconds = 0.3;
- SetUpChunkingHandler(2, chunking_params);
-
- ASSERT_OK(Process(StreamData::FromStreamInfo(
- kStreamIndex0, GetAudioStreamInfo(kTimeScale0))));
- ASSERT_OK(Process(StreamData::FromStreamInfo(
- kStreamIndex1, GetVideoStreamInfo(kTimeScale1))));
- EXPECT_THAT(
- GetOutputStreamDataVector(),
- ElementsAre(IsStreamInfo(kStreamIndex0, kTimeScale0, !kEncrypted),
- IsStreamInfo(kStreamIndex1, kTimeScale1, !kEncrypted)));
- ClearOutputStreamDataVector();
-
- // Equivalent to 12345 in video timescale.
- const int64_t kAudioStartTimestamp = 9876;
- const int64_t kVideoStartTimestamp = 12345;
- // Burst of audio and video samples. They will be properly ordered.
- for (int i = 0; i < 5; ++i) {
- ASSERT_OK(Process(StreamData::FromMediaSample(
- kStreamIndex0, GetMediaSample(kAudioStartTimestamp + kDuration0 * i,
- kDuration0, true))));
- }
- for (int i = 0; i < 5; ++i) {
- // Alternate key frame.
- const bool is_key_frame = (i % 2) == 1;
- ASSERT_OK(Process(StreamData::FromMediaSample(
- kStreamIndex1, GetMediaSample(kVideoStartTimestamp + kDuration1 * i,
- kDuration1, is_key_frame))));
- }
-
- EXPECT_THAT(
- GetOutputStreamDataVector(),
- ElementsAre(
- // The first samples @ kStartTimestamp is discarded - not key frame.
- IsMediaSample(kStreamIndex1, kVideoStartTimestamp + kDuration1,
- kDuration1, !kEncrypted),
- IsMediaSample(kStreamIndex0, kAudioStartTimestamp + kDuration0,
- kDuration0, !kEncrypted),
- IsMediaSample(kStreamIndex1, kVideoStartTimestamp + kDuration1 * 2,
- kDuration1, !kEncrypted),
- IsMediaSample(kStreamIndex0, kAudioStartTimestamp + kDuration0 * 2,
- kDuration0, !kEncrypted),
- IsMediaSample(kStreamIndex0, kAudioStartTimestamp + kDuration0 * 3,
- kDuration0, !kEncrypted),
- // The audio segment is terminated together with video stream.
- IsSegmentInfo(kStreamIndex0, kAudioStartTimestamp + kDuration0,
- kDuration0 * 3, !kIsSubsegment, !kEncrypted),
- // The next segment boundary 13245 / 1000 != 12645 / 1000.
- IsSegmentInfo(kStreamIndex1, kVideoStartTimestamp + kDuration1,
- kDuration1 * 2, !kIsSubsegment, !kEncrypted),
- IsMediaSample(kStreamIndex1, kVideoStartTimestamp + kDuration1 * 3,
- kDuration1, !kEncrypted),
- IsMediaSample(kStreamIndex0, kAudioStartTimestamp + kDuration0 * 4,
- kDuration0, !kEncrypted)));
- ClearOutputStreamDataVector();
-
- // The side comments below show the equivalent timestamp in video timescale.
- // The audio and video are made ~aligned.
- ASSERT_OK(Process(StreamData::FromMediaSample(
- kStreamIndex0,
- GetMediaSample(kAudioStartTimestamp + kDuration0 * 5, kDuration0,
- true)))); // 13595
- ASSERT_OK(Process(StreamData::FromMediaSample(
- kStreamIndex1,
- GetMediaSample(kVideoStartTimestamp + kDuration1 * 5, kDuration1,
- true)))); // 13845
- ASSERT_OK(Process(StreamData::FromMediaSample(
- kStreamIndex0,
- GetMediaSample(kAudioStartTimestamp + kDuration0 * 6, kDuration0,
- true)))); // 13845
- // This expectation are separated from the expectation above because
- // ElementsAre supports at most 10 elements.
- EXPECT_THAT(
- GetOutputStreamDataVector(),
- ElementsAre(
- IsMediaSample(kStreamIndex1, kVideoStartTimestamp + kDuration1 * 4,
- kDuration1, !kEncrypted),
- IsMediaSample(kStreamIndex0, kAudioStartTimestamp + kDuration0 * 5,
- kDuration0, !kEncrypted),
- // Audio is terminated along with video below.
- IsSegmentInfo(kStreamIndex0, kAudioStartTimestamp + kDuration0 * 4,
- kDuration0 * 2, kIsSubsegment, !kEncrypted),
- // The subsegment has duration kDuration1 * 2 since it can only
- // terminate before key frame.
- IsSegmentInfo(kStreamIndex1, kVideoStartTimestamp + kDuration1 * 3,
- kDuration1 * 2, kIsSubsegment, !kEncrypted),
- IsMediaSample(kStreamIndex1, kVideoStartTimestamp + kDuration1 * 5,
- kDuration1, !kEncrypted)));
-
- ClearOutputStreamDataVector();
- ASSERT_OK(OnFlushRequest(kStreamIndex0));
- EXPECT_THAT(
- GetOutputStreamDataVector(),
- ElementsAre(
- IsMediaSample(kStreamIndex0, kAudioStartTimestamp + kDuration0 * 6,
- kDuration0, !kEncrypted),
- IsSegmentInfo(kStreamIndex0, kAudioStartTimestamp + kDuration0 * 4,
- kDuration0 * 3, !kIsSubsegment, !kEncrypted)));
-
- ClearOutputStreamDataVector();
- ASSERT_OK(OnFlushRequest(kStreamIndex1));
- EXPECT_THAT(GetOutputStreamDataVector(),
- ElementsAre(IsSegmentInfo(
- kStreamIndex1, kVideoStartTimestamp + kDuration1 * 3,
- kDuration1 * 3, !kIsSubsegment, !kEncrypted)));
-
- // Flush again will do nothing.
- ClearOutputStreamDataVector();
- ASSERT_OK(OnFlushRequest(kStreamIndex0));
- ASSERT_OK(OnFlushRequest(kStreamIndex1));
- EXPECT_THAT(GetOutputStreamDataVector(), IsEmpty());
-}
-
-TEST_F(ChunkingHandlerTest, Scte35Event) {
+TEST_F(ChunkingHandlerTest, CueEvent) {
ChunkingParams chunking_params;
chunking_params.segment_duration_in_seconds = 1;
chunking_params.subsegment_duration_in_seconds = 0.5;
SetUpChunkingHandler(1, chunking_params);
ASSERT_OK(Process(StreamData::FromStreamInfo(
- kStreamIndex0, GetVideoStreamInfo(kTimeScale1))));
+ kStreamIndex, GetVideoStreamInfo(kTimeScale1))));
const int64_t kVideoStartTimestamp = 12345;
- const double kScte35TimeInSeconds =
- static_cast(kVideoStartTimestamp + kDuration1) / kTimeScale1;
+ const double kCueTimeInSeconds =
+ static_cast(kVideoStartTimestamp + kDuration) / kTimeScale1;
- auto scte35_event = std::make_shared();
- scte35_event->start_time_in_seconds = kScte35TimeInSeconds;
- ASSERT_OK(Process(StreamData::FromScte35Event(kStreamIndex0, scte35_event)));
+ auto cue_event = std::make_shared();
+ cue_event->time_in_seconds = kCueTimeInSeconds;
for (int i = 0; i < 3; ++i) {
const bool is_key_frame = true;
ASSERT_OK(Process(StreamData::FromMediaSample(
- kStreamIndex0, GetMediaSample(kVideoStartTimestamp + i * kDuration1,
- kDuration1, is_key_frame))));
+ kStreamIndex, GetMediaSample(kVideoStartTimestamp + i * kDuration,
+ kDuration, is_key_frame))));
+ if (i == 0) {
+ ASSERT_OK(Process(StreamData::FromCueEvent(kStreamIndex, cue_event)));
+ }
}
EXPECT_THAT(
GetOutputStreamDataVector(),
ElementsAre(
- IsStreamInfo(kStreamIndex0, kTimeScale1, !kEncrypted),
- IsMediaSample(kStreamIndex0, kVideoStartTimestamp, kDuration1,
+ IsStreamInfo(kStreamIndex, kTimeScale1, !kEncrypted),
+ IsMediaSample(kStreamIndex, kVideoStartTimestamp, kDuration,
!kEncrypted),
// A new segment is created due to the existance of Cue.
- IsSegmentInfo(kStreamIndex0, kVideoStartTimestamp, kDuration1,
+ IsSegmentInfo(kStreamIndex, kVideoStartTimestamp, kDuration,
!kIsSubsegment, !kEncrypted),
- IsCueEvent(kStreamIndex0, kScte35TimeInSeconds),
- IsMediaSample(kStreamIndex0, kVideoStartTimestamp + kDuration1 * 1,
- kDuration1, !kEncrypted),
- IsMediaSample(kStreamIndex0, kVideoStartTimestamp + kDuration1 * 2,
- kDuration1, !kEncrypted)));
+ IsCueEvent(kStreamIndex, kCueTimeInSeconds),
+ IsMediaSample(kStreamIndex, kVideoStartTimestamp + kDuration * 1,
+ kDuration, !kEncrypted),
+ IsMediaSample(kStreamIndex, kVideoStartTimestamp + kDuration * 2,
+ kDuration, !kEncrypted)));
}
} // namespace media
diff --git a/packager/media/chunking/cue_alignment_handler.cc b/packager/media/chunking/cue_alignment_handler.cc
index fb53c0da69..26b7c5d73d 100644
--- a/packager/media/chunking/cue_alignment_handler.cc
+++ b/packager/media/chunking/cue_alignment_handler.cc
@@ -20,7 +20,18 @@ double TimeInSeconds(const StreamInfo& info, const StreamData& data) {
switch (data.stream_data_type) {
case StreamDataType::kMediaSample:
time_scale = info.time_scale();
- scaled_time = data.media_sample->pts();
+ if (info.stream_type() == kStreamAudio) {
+ // Return the start time for video and mid-point for audio, so that for
+ // an audio sample, if the portion of the sample after the cue point is
+ // bigger than the portion of the sample before the cue point, the
+ // sample is placed after the cue.
+ // It does not matter for text samples as text samples will be cut at
+ // cue point.
+ scaled_time =
+ data.media_sample->pts() + data.media_sample->duration() / 2;
+ } else {
+ scaled_time = data.media_sample->pts();
+ }
break;
case StreamDataType::kTextSample:
// Text is always in MS but the stream info time scale is 0.
@@ -80,7 +91,7 @@ Status CueAlignmentHandler::OnFlushRequest(size_t stream_index) {
if (!stream_state.samples.empty()) {
LOG(WARNING) << "Unexpected data seen on stream " << i;
while (!stream_state.samples.empty()) {
- Status status(Dispatch(std::move(stream_state.samples.front())));
+ Status status = Dispatch(std::move(stream_state.samples.front()));
if (!status.ok())
return status;
stream_state.samples.pop_front();
@@ -141,7 +152,7 @@ Status CueAlignmentHandler::OnSample(std::unique_ptr sample) {
// Accept the sample. This will output it if it comes before the hint point or
// will cache it if it comes after the hint point.
- Status status(AcceptSample(std::move(sample), &stream_state));
+ Status status = AcceptSample(std::move(sample), &stream_state);
if (!status.ok()) {
return status;
}
@@ -180,6 +191,8 @@ Status CueAlignmentHandler::UseNewSyncPoint(
// No stream should be so out of sync with the others that they would
// still be working on an old cue.
if (stream_state.cue) {
+ // TODO(kqyang): Could this happen for text when there are no text samples
+ // between the two cues?
LOG(ERROR) << "Found two cue events that are too close together. One at "
<< stream_state.cue->time_in_seconds << " and the other at "
<< new_sync->time_in_seconds;
diff --git a/packager/packager.cc b/packager/packager.cc
index 6afad69d72..2afa7b7a58 100644
--- a/packager/packager.cc
+++ b/packager/packager.cc
@@ -33,6 +33,7 @@
#include "packager/media/base/muxer_options.h"
#include "packager/media/base/muxer_util.h"
#include "packager/media/chunking/chunking_handler.h"
+#include "packager/media/chunking/cue_alignment_handler.h"
#include "packager/media/crypto/encryption_handler.h"
#include "packager/media/demuxer/demuxer.h"
#include "packager/media/event/muxer_listener_factory.h"
@@ -55,6 +56,7 @@ namespace shaka {
using media::Demuxer;
using media::KeySource;
using media::MuxerOptions;
+using media::SyncPointQueue;
namespace media {
namespace {
@@ -375,8 +377,15 @@ std::shared_ptr CreateEncryptionHandler(
Status CreateMp4ToMp4TextJob(const StreamDescriptor& stream,
const PackagingParams& packaging_params,
std::unique_ptr muxer_listener,
+ SyncPointQueue* sync_points,
MuxerFactory* muxer_factory,
std::shared_ptr* root) {
+ // TODO(kqyang): This need to be integrated back to media pipeline since we
+ // may want to get not only text streams from the demuxer, in which case, the
+ // same demuxer should be used to get all streams instead of having a demuxer
+ // specifically for text.
+ // TODO(kqyang): Support Cue Alignment if |sync_points| is not null.
+
Status status;
std::shared_ptr demuxer;
@@ -385,14 +394,15 @@ Status CreateMp4ToMp4TextJob(const StreamDescriptor& stream,
demuxer->SetLanguageOverride(stream.stream_selector, stream.language);
}
- std::shared_ptr chunker(
- new ChunkingHandler(packaging_params.chunking_params));
+ auto chunker =
+ std::make_shared(packaging_params.chunking_params);
std::shared_ptr muxer =
muxer_factory->CreateMuxer(GetOutputFormat(stream), stream);
muxer->SetMuxerListener(std::move(muxer_listener));
status.Update(chunker->AddHandler(std::move(muxer)));
- status.Update(demuxer->SetHandler(stream.stream_selector, chunker));
+ status.Update(
+ demuxer->SetHandler(stream.stream_selector, std::move(chunker)));
return status;
}
@@ -400,7 +410,10 @@ Status CreateMp4ToMp4TextJob(const StreamDescriptor& stream,
Status CreateHlsTextJob(const StreamDescriptor& stream,
const PackagingParams& packaging_params,
std::unique_ptr muxer_listener,
+ SyncPointQueue* sync_points,
JobManager* job_manager) {
+ // TODO(kqyang): Support Cue Alignment if |sync_points| is not null.
+
DCHECK(muxer_listener);
DCHECK(job_manager);
@@ -421,9 +434,8 @@ Status CreateHlsTextJob(const StreamDescriptor& stream,
MuxerOptions muxer_options = CreateMuxerOptions(stream, packaging_params);
muxer_options.bandwidth = stream.bandwidth ? stream.bandwidth : 256;
- std::shared_ptr output(
- new WebVttSegmentedOutputHandler(muxer_options,
- std::move(muxer_listener)));
+ auto output = std::make_shared(
+ muxer_options, std::move(muxer_listener));
std::unique_ptr reader;
Status open_status = FileReader::Open(stream.input, &reader);
@@ -431,10 +443,9 @@ Status CreateHlsTextJob(const StreamDescriptor& stream,
return open_status;
}
- std::shared_ptr parser(
- new WebVttParser(std::move(reader), stream.language));
- std::shared_ptr segmenter(
- new WebVttSegmenter(segment_length_in_ms));
+ auto parser =
+ std::make_shared(std::move(reader), stream.language);
+ auto segmenter = std::make_shared(segment_length_in_ms);
// Build in reverse to allow us to move the pointers.
Status status;
@@ -451,8 +462,11 @@ Status CreateHlsTextJob(const StreamDescriptor& stream,
Status CreateWebVttToMp4TextJob(const StreamDescriptor& stream,
const PackagingParams& packaging_params,
std::unique_ptr muxer_listener,
+ SyncPointQueue* sync_points,
MuxerFactory* muxer_factory,
std::shared_ptr* root) {
+ // TODO(kqyang): Support Cue Alignment if |sync_points| is not null.
+
Status status;
std::unique_ptr reader;
status = FileReader::Open(stream.input, &reader);
@@ -461,11 +475,11 @@ Status CreateWebVttToMp4TextJob(const StreamDescriptor& stream,
return status;
}
- std::shared_ptr parser(
- new WebVttParser(std::move(reader), stream.language));
- std::shared_ptr text_to_mp4(new WebVttToMp4Handler);
- std::shared_ptr chunker(
- new ChunkingHandler(packaging_params.chunking_params));
+ auto parser =
+ std::make_shared(std::move(reader), stream.language);
+ auto text_to_mp4 = std::make_shared();
+ auto chunker =
+ std::make_shared(packaging_params.chunking_params);
std::shared_ptr muxer =
muxer_factory->CreateMuxer(GetOutputFormat(stream), stream);
muxer->SetMuxerListener(std::move(muxer_listener));
@@ -482,6 +496,7 @@ Status CreateWebVttToMp4TextJob(const StreamDescriptor& stream,
Status CreateTextJobs(
const std::vector>& streams,
const PackagingParams& packaging_params,
+ SyncPointQueue* sync_points,
MuxerListenerFactory* muxer_listener_factory,
MuxerFactory* muxer_factory,
MpdNotifier* mpd_notifier,
@@ -498,15 +513,15 @@ Status CreateTextJobs(
switch (DetermineContainerFromFileName(stream.input)) {
case CONTAINER_WEBVTT:
- status.Update(CreateWebVttToMp4TextJob(stream, packaging_params,
- std::move(muxer_listener),
- muxer_factory, &root));
+ status.Update(CreateWebVttToMp4TextJob(
+ stream, packaging_params, std::move(muxer_listener), sync_points,
+ muxer_factory, &root));
break;
case CONTAINER_MOV:
- status.Update(CreateMp4ToMp4TextJob(stream, packaging_params,
- std::move(muxer_listener),
- muxer_factory, &root));
+ status.Update(CreateMp4ToMp4TextJob(
+ stream, packaging_params, std::move(muxer_listener), sync_points,
+ muxer_factory, &root));
break;
default:
@@ -543,8 +558,9 @@ Status CreateTextJobs(
// If we are outputting to HLS, then create the HLS test pipeline that
// will create segmented text output.
if (hls_listener) {
- Status status = CreateHlsTextJob(stream, packaging_params,
- std::move(hls_listener), job_manager);
+ Status status =
+ CreateHlsTextJob(stream, packaging_params, std::move(hls_listener),
+ sync_points, job_manager);
if (!status.ok()) {
return status;
}
@@ -592,6 +608,7 @@ Status CreateAudioVideoJobs(
const std::vector>& streams,
const PackagingParams& packaging_params,
KeySource* encryption_key_source,
+ SyncPointQueue* sync_points,
MuxerListenerFactory* muxer_listener_factory,
MuxerFactory* muxer_factory,
JobManager* job_manager) {
@@ -601,14 +618,14 @@ Status CreateAudioVideoJobs(
// Demuxers are shared among all streams with the same input.
std::shared_ptr demuxer;
- // Chunkers can be shared among all streams with the same input (except for
- // WVM files), which allows samples from the same input to be synced when
- // doing chunking.
- std::shared_ptr chunker;
- bool is_wvm_file = false;
+ // When |sync_points| is not null, there should be one CueAlignmentHandler per
+ // input. All CueAlignmentHandler shares the same |sync_points|, which allows
+ // sync points / cues to be aligned across streams, whether they are from the
+ // same input or not.
+ std::shared_ptr cue_aligner;
// Replicators are shared among all streams with the same input and stream
// selector.
- std::shared_ptr replicator;
+ std::shared_ptr replicator;
std::string previous_input;
std::string previous_selector;
@@ -624,15 +641,8 @@ Status CreateAudioVideoJobs(
job_manager->Add("RemuxJob", demuxer);
- // Share chunkers among all streams with the same input except for WVM
- // file, which may contain multiple video files and the samples may not be
- // interleaved either.
- is_wvm_file =
- DetermineContainerFromFileName(stream.input) == CONTAINER_WVM;
- if (!is_wvm_file) {
- chunker =
- std::make_shared(packaging_params.chunking_params);
- }
+ if (sync_points)
+ cue_aligner = std::make_shared(sync_points);
}
if (!stream.language.empty()) {
@@ -651,16 +661,8 @@ Status CreateAudioVideoJobs(
}
if (new_stream) {
- std::shared_ptr ad_cue_generator;
- if (!packaging_params.ad_cue_generator_params.cue_points.empty()) {
- ad_cue_generator = std::make_shared(
- packaging_params.ad_cue_generator_params);
- }
-
- if (is_wvm_file) {
- chunker =
- std::make_shared(packaging_params.chunking_params);
- }
+ auto chunker =
+ std::make_shared(packaging_params.chunking_params);
std::shared_ptr encryptor = CreateEncryptionHandler(
packaging_params, stream, encryption_key_source);
@@ -668,10 +670,9 @@ Status CreateAudioVideoJobs(
replicator = std::make_shared();
Status status;
- if (ad_cue_generator) {
- status.Update(
- demuxer->SetHandler(stream.stream_selector, ad_cue_generator));
- status.Update(ad_cue_generator->AddHandler(chunker));
+ if (cue_aligner) {
+ status.Update(demuxer->SetHandler(stream.stream_selector, cue_aligner));
+ status.Update(cue_aligner->AddHandler(chunker));
} else {
status.Update(demuxer->SetHandler(stream.stream_selector, chunker));
}
@@ -729,6 +730,7 @@ Status CreateAllJobs(const std::vector& stream_descriptors,
const PackagingParams& packaging_params,
MpdNotifier* mpd_notifier,
KeySource* encryption_key_source,
+ SyncPointQueue* sync_points,
MuxerListenerFactory* muxer_listener_factory,
MuxerFactory* muxer_factory,
JobManager* job_manager) {
@@ -758,11 +760,11 @@ Status CreateAllJobs(const std::vector& stream_descriptors,
media::StreamDescriptorCompareFn);
Status status;
- status.Update(CreateTextJobs(text_streams, packaging_params,
+ status.Update(CreateTextJobs(text_streams, packaging_params, sync_points,
muxer_listener_factory, muxer_factory,
mpd_notifier, job_manager));
status.Update(CreateAudioVideoJobs(
- audio_video_streams, packaging_params, encryption_key_source,
+ audio_video_streams, packaging_params, encryption_key_source, sync_points,
muxer_listener_factory, muxer_factory, job_manager));
if (!status.ok()) {
@@ -783,6 +785,7 @@ struct Packager::PackagerInternal {
std::unique_ptr encryption_key_source;
std::unique_ptr mpd_notifier;
std::unique_ptr hls_notifier;
+ std::unique_ptr sync_points;
BufferCallbackParams buffer_callback_params;
media::JobManager job_manager;
};
@@ -854,6 +857,11 @@ Status Packager::Initialize(
internal->hls_notifier.reset(new hls::SimpleHlsNotifier(hls_params));
}
+ if (!packaging_params.ad_cue_generator_params.cue_points.empty()) {
+ internal->sync_points.reset(
+ new SyncPointQueue(packaging_params.ad_cue_generator_params));
+ }
+
std::vector streams_for_jobs;
for (const StreamDescriptor& descriptor : stream_descriptors) {
@@ -896,8 +904,8 @@ Status Packager::Initialize(
Status status = media::CreateAllJobs(
streams_for_jobs, packaging_params, internal->mpd_notifier.get(),
- internal->encryption_key_source.get(), &muxer_listener_factory,
- &muxer_factory, &internal->job_manager);
+ internal->encryption_key_source.get(), internal->sync_points.get(),
+ &muxer_listener_factory, &muxer_factory, &internal->job_manager);
if (!status.ok()) {
return status;