diff --git a/docs/source/tutorials/live.rst b/docs/source/tutorials/live.rst index f14f4ff100..9d6b5050dc 100644 --- a/docs/source/tutorials/live.rst +++ b/docs/source/tutorials/live.rst @@ -41,6 +41,24 @@ Here are some examples. `preserved_segments_outside_live_window` option in :doc:`/options/dash_options` or :doc:`/options/hls_options` for details. +.. note:: + + Shaka Packager ensures all segments referenced in DASH manifest / HLS + playlists are available, by updating the manifest / playlists only after a + segment is completed. + + However, if content is not served directly from packaging output location, + extra care must be taken outside of packager to avoid updating manifest / + playlists without updating media segments. + + Here is an example flow that avoids potential race condition. The following + steps should be done SERIALLY AND IN ORDER in every sync loop when uploading + manifest / playlists and media segments to content server: + + 1. Upload manifest / playlists under different names + 2. Upload / Sync media segments + 3. Rename uploaded manifest / playlists back to the original names + .. include:: /options/udp_file_options.rst .. include:: /options/segment_template_formatting.rst diff --git a/packager/app/test/packager_test.py b/packager/app/test/packager_test.py index de02ee0539..9f08dd55f9 100755 --- a/packager/app/test/packager_test.py +++ b/packager/app/test/packager_test.py @@ -1241,6 +1241,15 @@ class PackagerFunctionalTest(PackagerAppTest): 'http://foo.bar/check_me_for_the_date_header')) self._CheckTestResults('live-profile') + def testPackageLiveProfileWithWebM(self): + self.assertPackageSuccess( + self._GetStreams( + ['audio', 'video'], + segmented=True, + output_format='webm', + test_files=['bear-640x360.webm']), self._GetFlags()) + self._CheckTestResults('live-profile-with-webm') + def testPackageLiveStaticProfile(self): self.assertPackageSuccess( self._GetStreams(['audio', 'video'], segmented=True), diff --git a/packager/app/test/testdata/live-profile-with-webm/bear-640x360-audio-1.webm b/packager/app/test/testdata/live-profile-with-webm/bear-640x360-audio-1.webm new file mode 100644 index 0000000000..0fa8079896 Binary files /dev/null and b/packager/app/test/testdata/live-profile-with-webm/bear-640x360-audio-1.webm differ diff --git a/packager/app/test/testdata/live-profile-with-webm/bear-640x360-audio-2.webm b/packager/app/test/testdata/live-profile-with-webm/bear-640x360-audio-2.webm new file mode 100644 index 0000000000..7ec8e8ca6a Binary files /dev/null and b/packager/app/test/testdata/live-profile-with-webm/bear-640x360-audio-2.webm differ diff --git a/packager/app/test/testdata/live-profile-with-webm/bear-640x360-audio-3.webm b/packager/app/test/testdata/live-profile-with-webm/bear-640x360-audio-3.webm new file mode 100644 index 0000000000..96bc3f6e4c Binary files /dev/null and b/packager/app/test/testdata/live-profile-with-webm/bear-640x360-audio-3.webm differ diff --git a/packager/app/test/testdata/live-profile-with-webm/bear-640x360-audio-init.webm b/packager/app/test/testdata/live-profile-with-webm/bear-640x360-audio-init.webm new file mode 100644 index 0000000000..0476414efd Binary files /dev/null and b/packager/app/test/testdata/live-profile-with-webm/bear-640x360-audio-init.webm differ diff --git a/packager/app/test/testdata/live-profile-with-webm/bear-640x360-video-1.webm b/packager/app/test/testdata/live-profile-with-webm/bear-640x360-video-1.webm new file mode 100644 index 0000000000..3fa0a83605 Binary files /dev/null and b/packager/app/test/testdata/live-profile-with-webm/bear-640x360-video-1.webm differ diff --git a/packager/app/test/testdata/live-profile-with-webm/bear-640x360-video-2.webm b/packager/app/test/testdata/live-profile-with-webm/bear-640x360-video-2.webm new file mode 100644 index 0000000000..de55eba0e8 Binary files /dev/null and b/packager/app/test/testdata/live-profile-with-webm/bear-640x360-video-2.webm differ diff --git a/packager/app/test/testdata/live-profile-with-webm/bear-640x360-video-3.webm b/packager/app/test/testdata/live-profile-with-webm/bear-640x360-video-3.webm new file mode 100644 index 0000000000..15bb824c7b Binary files /dev/null and b/packager/app/test/testdata/live-profile-with-webm/bear-640x360-video-3.webm differ diff --git a/packager/app/test/testdata/live-profile-with-webm/bear-640x360-video-init.webm b/packager/app/test/testdata/live-profile-with-webm/bear-640x360-video-init.webm new file mode 100644 index 0000000000..f343639b9a Binary files /dev/null and b/packager/app/test/testdata/live-profile-with-webm/bear-640x360-video-init.webm differ diff --git a/packager/app/test/testdata/live-profile-with-webm/output.mpd b/packager/app/test/testdata/live-profile-with-webm/output.mpd new file mode 100644 index 0000000000..a970d599c3 --- /dev/null +++ b/packager/app/test/testdata/live-profile-with-webm/output.mpd @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packager/media/formats/mp4/multi_segment_segmenter.cc b/packager/media/formats/mp4/multi_segment_segmenter.cc index 41d4ff723d..cf257012c4 100644 --- a/packager/media/formats/mp4/multi_segment_segmenter.cc +++ b/packager/media/formats/mp4/multi_segment_segmenter.cc @@ -18,6 +18,7 @@ #include "packager/media/event/muxer_listener.h" #include "packager/media/formats/mp4/box_definitions.h" #include "packager/media/formats/mp4/key_frame_info.h" +#include "packager/status_macros.h" namespace shaka { namespace media { @@ -61,10 +62,9 @@ Status MultiSegmentSegmenter::DoInitialize() { Status MultiSegmentSegmenter::DoFinalize() { // Update init segment with media duration set. - Status status = WriteInitSegment(); - if (status.ok()) - SetComplete(); - return status; + RETURN_IF_ERROR(WriteInitSegment()); + SetComplete(); + return Status::OK; } Status MultiSegmentSegmenter::DoFinalizeSegment() { @@ -180,21 +180,25 @@ Status MultiSegmentSegmenter::WriteSegment() { const size_t segment_size = segment_header_size + fragment_buffer()->Size(); DCHECK_NE(segment_size, 0u); - Status status = buffer->WriteToFile(file.get()); - if (status.ok()) { - if (muxer_listener()) { - for (const KeyFrameInfo& key_frame_info : key_frame_infos()) { - muxer_listener()->OnKeyFrame( - key_frame_info.timestamp, - segment_header_size + key_frame_info.start_byte_offset, - key_frame_info.size); - } + RETURN_IF_ERROR(buffer->WriteToFile(file.get())); + if (muxer_listener()) { + for (const KeyFrameInfo& key_frame_info : key_frame_infos()) { + muxer_listener()->OnKeyFrame( + key_frame_info.timestamp, + segment_header_size + key_frame_info.start_byte_offset, + key_frame_info.size); } - status = fragment_buffer()->WriteToFile(file.get()); } + RETURN_IF_ERROR(fragment_buffer()->WriteToFile(file.get())); - if (!status.ok()) - return status; + // Close the file, which also does flushing, to make sure the file is written + // before manifest is updated. + if (!file.release()->Close()) { + return Status( + error::FILE_FAILURE, + "Cannot close file " + file_name + + ", possibly file permission issue or running out of disk space."); + } uint64_t segment_duration = 0; // ISO/IEC 23009-1:2012: the value shall be identical to sum of the the diff --git a/packager/media/formats/webm/multi_segment_segmenter.cc b/packager/media/formats/webm/multi_segment_segmenter.cc index 8d9cd398fc..4a2f24bae2 100644 --- a/packager/media/formats/webm/multi_segment_segmenter.cc +++ b/packager/media/formats/webm/multi_segment_segmenter.cc @@ -10,6 +10,7 @@ #include "packager/media/base/muxer_util.h" #include "packager/media/base/stream_info.h" #include "packager/media/event/muxer_listener.h" +#include "packager/status_macros.h" #include "packager/third_party/libwebm/src/mkvmuxer.hpp" namespace shaka { @@ -25,17 +26,21 @@ Status MultiSegmentSegmenter::FinalizeSegment(uint64_t start_timestamp, uint64_t duration_timestamp, bool is_subsegment) { CHECK(cluster()); - Status status = Segmenter::FinalizeSegment(start_timestamp, - duration_timestamp, is_subsegment); - if (!status.ok()) - return status; + RETURN_IF_ERROR(Segmenter::FinalizeSegment( + start_timestamp, duration_timestamp, is_subsegment)); if (!cluster()->Finalize()) return Status(error::FILE_FAILURE, "Error finalizing segment."); + if (!is_subsegment) { + const std::string segment_name = writer_->file()->file_name(); + // Close the file, which also does flushing, to make sure the file is + // written before manifest is updated. + RETURN_IF_ERROR(writer_->Close()); + if (muxer_listener()) { const uint64_t size = cluster()->Size(); - muxer_listener()->OnNewSegment(writer_->file()->file_name(), - start_timestamp, duration_timestamp, size); + muxer_listener()->OnNewSegment(segment_name, start_timestamp, + duration_timestamp, size); } VLOG(1) << "WEBM file '" << writer_->file()->file_name() << "' finalized."; } @@ -66,7 +71,7 @@ Status MultiSegmentSegmenter::DoInitialize() { } Status MultiSegmentSegmenter::DoFinalize() { - return writer_->Close(); + return Status::OK; } Status MultiSegmentSegmenter::NewSegment(uint64_t start_timestamp,