HLS with fragmented MP4

- Add EXT-X-MAP tag for init segment.
- Do not set output field on stream descriptor if not specified on
  command line. If it's set (internally) then it gets copied to
  MediaInfo that gets passed to the manifest generators.

b/36279481

Change-Id: I762c55b255699ec691817dc4806b0dee2f7504b8
This commit is contained in:
Rintaro Kuroiwa 2017-03-13 17:02:23 -07:00
parent cc04698460
commit 4891d9a6bf
8 changed files with 84 additions and 34 deletions

View File

@ -296,7 +296,7 @@ bool CreateRemuxJobs(const StreamDescriptorList& stream_descriptors,
remux_jobs->emplace_back(new RemuxJob(std::move(demuxer))); remux_jobs->emplace_back(new RemuxJob(std::move(demuxer)));
previous_input = stream_iter->input; previous_input = stream_iter->input;
// Skip setting up muxers if output is not needed. // Skip setting up muxers if output is not needed.
if (stream_iter->output.empty()) if (stream_iter->output.empty() && stream_iter->segment_template.empty())
continue; continue;
} }
DCHECK(!remux_jobs->empty()); DCHECK(!remux_jobs->empty());

View File

@ -187,12 +187,15 @@ bool InsertStreamDescriptor(const std::string& descriptor_string,
<< "' ignored. TS muxer does not support initialization " << "' ignored. TS muxer does not support initialization "
"segment generation."; "segment generation.";
} }
// For convenience, set descriptor.output to descriptor.segment_template. It
// is only used for flag checking in variuos places.
descriptor.output = descriptor.segment_template;
} }
if (!FLAGS_dump_stream_info && descriptor.output.empty()) { // For TS output, segment template is sufficient, and does not require an
// output entry.
const bool output_specified =
!descriptor.output.empty() ||
(descriptor.output_format == CONTAINER_MPEG2TS &&
!descriptor.segment_template.empty());
if (!FLAGS_dump_stream_info && !output_specified) {
LOG(ERROR) << "Stream output not specified."; LOG(ERROR) << "Stream output not specified.";
return false; return false;
} }

View File

@ -1,5 +1,5 @@
#EXTM3U #EXTM3U
#EXT-X-VERSION:5 #EXT-X-VERSION:6
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test> ## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
#EXT-X-TARGETDURATION:2 #EXT-X-TARGETDURATION:2
#EXT-X-PLAYLIST-TYPE:VOD #EXT-X-PLAYLIST-TYPE:VOD

View File

@ -1,5 +1,5 @@
#EXTM3U #EXTM3U
#EXT-X-VERSION:5 #EXT-X-VERSION:6
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test> ## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
#EXT-X-TARGETDURATION:2 #EXT-X-TARGETDURATION:2
#EXT-X-PLAYLIST-TYPE:VOD #EXT-X-PLAYLIST-TYPE:VOD

View File

@ -1,5 +1,5 @@
#EXTM3U #EXTM3U
#EXT-X-VERSION:5 #EXT-X-VERSION:6
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test> ## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
#EXT-X-TARGETDURATION:2 #EXT-X-TARGETDURATION:2
#EXT-X-PLAYLIST-TYPE:VOD #EXT-X-PLAYLIST-TYPE:VOD

View File

@ -1,5 +1,5 @@
#EXTM3U #EXTM3U
#EXT-X-VERSION:5 #EXT-X-VERSION:6
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test> ## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
#EXT-X-TARGETDURATION:2 #EXT-X-TARGETDURATION:2
#EXT-X-PLAYLIST-TYPE:VOD #EXT-X-PLAYLIST-TYPE:VOD

View File

@ -32,6 +32,39 @@ uint32_t GetTimeScale(const MediaInfo& media_info) {
return 0u; return 0u;
} }
std::string CreatePlaylistHeader(
const std::string& init_segment_name,
uint32_t target_duration,
MediaPlaylist::MediaPlaylistType type) {
const std::string version = GetPackagerVersion();
std::string version_line;
if (!version.empty()) {
version_line =
base::StringPrintf("## Generated with %s version %s\n",
GetPackagerProjectUrl().c_str(), version.c_str());
}
// 6 is required for EXT-X-MAP without EXT-X-I-FRAMES-ONLY.
std::string header = base::StringPrintf(
"#EXTM3U\n"
"#EXT-X-VERSION:6\n"
"%s"
"#EXT-X-TARGETDURATION:%d\n",
version_line.c_str(), target_duration);
if (type == MediaPlaylist::MediaPlaylistType::kVod) {
header += "#EXT-X-PLAYLIST-TYPE:VOD\n";
}
// Put EXT-X-MAP at the end since the rest of the playlist is about the
// segment and key info.
if (!init_segment_name.empty()) {
header += "#EXT-X-MAP:URI=\"" + init_segment_name + "\"\n";
}
return header;
}
class SegmentInfoEntry : public HlsEntry { class SegmentInfoEntry : public HlsEntry {
public: public:
SegmentInfoEntry(const std::string& file_name, double duration); SegmentInfoEntry(const std::string& file_name, double duration);
@ -266,24 +299,8 @@ bool MediaPlaylist::WriteToFile(media::File* file) {
SetTargetDuration(ceil(GetLongestSegmentDuration())); SetTargetDuration(ceil(GetLongestSegmentDuration()));
} }
const std::string version = GetPackagerVersion(); std::string header = CreatePlaylistHeader(media_info_.init_segment_name(),
std::string version_line; target_duration_, type_);
if (!version.empty()) {
version_line =
base::StringPrintf("## Generated with %s version %s\n",
GetPackagerProjectUrl().c_str(), version.c_str());
}
// KEYFORMAT and KEYFORMATVERSIONS on EXT-X-KEY requires 5 or above.
std::string header = base::StringPrintf(
"#EXTM3U\n"
"#EXT-X-VERSION:5\n"
"%s"
"#EXT-X-TARGETDURATION:%d\n",
version_line.c_str(), target_duration_);
if (type_ == MediaPlaylistType::kVod) {
header += "#EXT-X-PLAYLIST-TYPE:VOD\n";
}
std::string body; std::string body;
if (!entries_.empty()) { if (!entries_.empty()) {

View File

@ -119,7 +119,7 @@ TEST_F(MediaPlaylistTest, WriteToFile) {
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_)); ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
const std::string kExpectedOutput = const std::string kExpectedOutput =
"#EXTM3U\n" "#EXTM3U\n"
"#EXT-X-VERSION:5\n" "#EXT-X-VERSION:6\n"
"## Generated with https://github.com/google/shaka-packager version " "## Generated with https://github.com/google/shaka-packager version "
"test\n" "test\n"
"#EXT-X-TARGETDURATION:0\n" "#EXT-X-TARGETDURATION:0\n"
@ -176,7 +176,7 @@ TEST_F(MediaPlaylistTest, SetTargetDuration) {
EXPECT_TRUE(media_playlist_.SetTargetDuration(20)); EXPECT_TRUE(media_playlist_.SetTargetDuration(20));
const std::string kExpectedOutput = const std::string kExpectedOutput =
"#EXTM3U\n" "#EXTM3U\n"
"#EXT-X-VERSION:5\n" "#EXT-X-VERSION:6\n"
"## Generated with https://github.com/google/shaka-packager version " "## Generated with https://github.com/google/shaka-packager version "
"test\n" "test\n"
"#EXT-X-TARGETDURATION:20\n" "#EXT-X-TARGETDURATION:20\n"
@ -204,7 +204,7 @@ TEST_F(MediaPlaylistTest, WriteToFileWithSegments) {
media_playlist_.AddSegment("file2.ts", 2700000, 5000000); media_playlist_.AddSegment("file2.ts", 2700000, 5000000);
const std::string kExpectedOutput = const std::string kExpectedOutput =
"#EXTM3U\n" "#EXTM3U\n"
"#EXT-X-VERSION:5\n" "#EXT-X-VERSION:6\n"
"## Generated with https://github.com/google/shaka-packager version " "## Generated with https://github.com/google/shaka-packager version "
"test\n" "test\n"
"#EXT-X-TARGETDURATION:30\n" "#EXT-X-TARGETDURATION:30\n"
@ -235,7 +235,7 @@ TEST_F(MediaPlaylistTest, WriteToFileWithEncryptionInfo) {
media_playlist_.AddSegment("file2.ts", 2700000, 5000000); media_playlist_.AddSegment("file2.ts", 2700000, 5000000);
const std::string kExpectedOutput = const std::string kExpectedOutput =
"#EXTM3U\n" "#EXTM3U\n"
"#EXT-X-VERSION:5\n" "#EXT-X-VERSION:6\n"
"## Generated with https://github.com/google/shaka-packager version " "## Generated with https://github.com/google/shaka-packager version "
"test\n" "test\n"
"#EXT-X-TARGETDURATION:30\n" "#EXT-X-TARGETDURATION:30\n"
@ -269,7 +269,7 @@ TEST_F(MediaPlaylistTest, WriteToFileWithEncryptionInfoEmptyIv) {
media_playlist_.AddSegment("file2.ts", 2700000, 5000000); media_playlist_.AddSegment("file2.ts", 2700000, 5000000);
const std::string kExpectedOutput = const std::string kExpectedOutput =
"#EXTM3U\n" "#EXTM3U\n"
"#EXT-X-VERSION:5\n" "#EXT-X-VERSION:6\n"
"## Generated with https://github.com/google/shaka-packager version " "## Generated with https://github.com/google/shaka-packager version "
"test\n" "test\n"
"#EXT-X-TARGETDURATION:30\n" "#EXT-X-TARGETDURATION:30\n"
@ -302,7 +302,7 @@ TEST_F(MediaPlaylistTest, WriteToFileWithClearLead) {
media_playlist_.AddSegment("file2.ts", 2700000, 5000000); media_playlist_.AddSegment("file2.ts", 2700000, 5000000);
const std::string kExpectedOutput = const std::string kExpectedOutput =
"#EXTM3U\n" "#EXTM3U\n"
"#EXT-X-VERSION:5\n" "#EXT-X-VERSION:6\n"
"## Generated with https://github.com/google/shaka-packager version test\n" "## Generated with https://github.com/google/shaka-packager version test\n"
"#EXT-X-TARGETDURATION:30\n" "#EXT-X-TARGETDURATION:30\n"
"#EXT-X-PLAYLIST-TYPE:VOD\n" "#EXT-X-PLAYLIST-TYPE:VOD\n"
@ -336,7 +336,7 @@ TEST_F(MediaPlaylistTest, RemoveOldestSegment) {
const std::string kExpectedOutput = const std::string kExpectedOutput =
"#EXTM3U\n" "#EXTM3U\n"
"#EXT-X-VERSION:5\n" "#EXT-X-VERSION:6\n"
"## Generated with https://github.com/google/shaka-packager version " "## Generated with https://github.com/google/shaka-packager version "
"test\n" "test\n"
"#EXT-X-TARGETDURATION:30\n" "#EXT-X-TARGETDURATION:30\n"
@ -370,5 +370,35 @@ TEST_F(MediaPlaylistTest, GetLanguage) {
EXPECT_EQ("apa", media_playlist_.GetLanguage()); // no short form exists EXPECT_EQ("apa", media_playlist_.GetLanguage()); // no short form exists
} }
TEST_F(MediaPlaylistTest, InitSegment) {
valid_video_media_info_.set_reference_time_scale(90000);
valid_video_media_info_.set_init_segment_name("init_segment.mp4");
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
// 10 seconds.
media_playlist_.AddSegment("file1.mp4", 900000, 1000000);
// 30 seconds.
media_playlist_.AddSegment("file2.mp4", 2700000, 5000000);
const std::string kExpectedOutput =
"#EXTM3U\n"
"#EXT-X-VERSION:6\n"
"## Generated with https://github.com/google/shaka-packager version test\n"
"#EXT-X-TARGETDURATION:30\n"
"#EXT-X-PLAYLIST-TYPE:VOD\n"
"#EXT-X-MAP:URI=\"init_segment.mp4\"\n"
"#EXTINF:10.000,\n"
"file1.mp4\n"
"#EXTINF:30.000,\n"
"file2.mp4\n"
"#EXT-X-ENDLIST\n";
MockFile file;
EXPECT_CALL(file,
Write(MatchesString(kExpectedOutput), kExpectedOutput.size()))
.WillOnce(ReturnArg<1>());
EXPECT_TRUE(media_playlist_.WriteToFile(&file));
}
} // namespace hls } // namespace hls
} // namespace shaka } // namespace shaka