Support Dolby Vision backward compatible profiles
Added Dolby Vision backward compatible signalling, i.e. for Dolby Vision profile 8, both base codec without Dolby Vision and HDR codec with Dolby Vision are signalled. This is achieved by using a new MuxerListener implementation MultiCodecMuxerListener, which wraps multiple child MuxerListeners and is able to delegate to the child MuxerListeners based on the codecs in the stream. Closes #341. Change-Id: I1967bb1ed503087cdd011c364e5fb5647d516ca4
This commit is contained in:
parent
888be2d1ee
commit
b900565a0f
|
@ -1236,6 +1236,15 @@ class PackagerFunctionalTest(PackagerAppTest):
|
||||||
self.assertPackageSuccess(streams, flags)
|
self.assertPackageSuccess(streams, flags)
|
||||||
self._CheckTestResults('dolby-vision-profile-5-with-encryption')
|
self._CheckTestResults('dolby-vision-profile-5-with-encryption')
|
||||||
|
|
||||||
|
def testDolbyVisionProfile8WithEncryption(self):
|
||||||
|
streams = [
|
||||||
|
self._GetStream('video', test_file='sparks_dovi_8.mp4')
|
||||||
|
]
|
||||||
|
flags = self._GetFlags(encryption=True, output_dash=True, output_hls=True)
|
||||||
|
|
||||||
|
self.assertPackageSuccess(streams, flags)
|
||||||
|
self._CheckTestResults('dolby-vision-profile-8-with-encryption')
|
||||||
|
|
||||||
def testVp8Mp4WithEncryption(self):
|
def testVp8Mp4WithEncryption(self):
|
||||||
streams = [
|
streams = [
|
||||||
self._GetStream('video',
|
self._GetStream('video',
|
||||||
|
|
7
packager/app/test/testdata/dolby-vision-profile-8-with-encryption/output.m3u8
vendored
Normal file
7
packager/app/test/testdata/dolby-vision-profile-8-with-encryption/output.m3u8
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#EXTM3U
|
||||||
|
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
|
||||||
|
|
||||||
|
#EXT-X-STREAM-INF:BANDWIDTH=818598,AVERAGE-BANDWIDTH=755328,CODECS="hvc1.2.4.L90.90",RESOLUTION=640x360,FRAME-RATE=59.940,VIDEO-RANGE=PQ
|
||||||
|
stream_0.m3u8
|
||||||
|
#EXT-X-STREAM-INF:BANDWIDTH=818598,AVERAGE-BANDWIDTH=755328,CODECS="dvh1.08.01",RESOLUTION=640x360,FRAME-RATE=59.940,VIDEO-RANGE=PQ
|
||||||
|
stream_0.m3u8
|
30
packager/app/test/testdata/dolby-vision-profile-8-with-encryption/output.mpd
vendored
Normal file
30
packager/app/test/testdata/dolby-vision-profile-8-with-encryption/output.mpd
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>-->
|
||||||
|
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" minBufferTime="PT2S" type="static" mediaPresentationDuration="PT6.022683143615723S">
|
||||||
|
<Period id="0">
|
||||||
|
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="60000/1001" subsegmentAlignment="true" par="16:9">
|
||||||
|
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/>
|
||||||
|
<ContentProtection schemeIdUri="urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b">
|
||||||
|
<cenc:pssh>AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA==</cenc:pssh>
|
||||||
|
</ContentProtection>
|
||||||
|
<Representation id="0" bandwidth="818598" codecs="hvc1.2.4.L90.90" mimeType="video/mp4" sar="1:1">
|
||||||
|
<BaseURL>sparks_dovi_8-video.mp4</BaseURL>
|
||||||
|
<SegmentBase indexRange="1355-1434" timescale="60000">
|
||||||
|
<Initialization range="0-1354"/>
|
||||||
|
</SegmentBase>
|
||||||
|
</Representation>
|
||||||
|
</AdaptationSet>
|
||||||
|
<AdaptationSet id="1" contentType="video" width="640" height="360" frameRate="60000/1001" subsegmentAlignment="true" par="16:9">
|
||||||
|
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/>
|
||||||
|
<ContentProtection schemeIdUri="urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b">
|
||||||
|
<cenc:pssh>AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA==</cenc:pssh>
|
||||||
|
</ContentProtection>
|
||||||
|
<Representation id="1" bandwidth="818598" codecs="dvh1.08.01" mimeType="video/mp4" sar="1:1">
|
||||||
|
<BaseURL>sparks_dovi_8-video.mp4</BaseURL>
|
||||||
|
<SegmentBase indexRange="1355-1434" timescale="60000">
|
||||||
|
<Initialization range="0-1354"/>
|
||||||
|
</SegmentBase>
|
||||||
|
</Representation>
|
||||||
|
</AdaptationSet>
|
||||||
|
</Period>
|
||||||
|
</MPD>
|
BIN
packager/app/test/testdata/dolby-vision-profile-8-with-encryption/sparks_dovi_8-video.mp4
vendored
Normal file
BIN
packager/app/test/testdata/dolby-vision-profile-8-with-encryption/sparks_dovi_8-video.mp4
vendored
Normal file
Binary file not shown.
20
packager/app/test/testdata/dolby-vision-profile-8-with-encryption/stream_0.m3u8
vendored
Normal file
20
packager/app/test/testdata/dolby-vision-profile-8-with-encryption/stream_0.m3u8
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#EXTM3U
|
||||||
|
#EXT-X-VERSION:6
|
||||||
|
## Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>
|
||||||
|
#EXT-X-TARGETDURATION:3
|
||||||
|
#EXT-X-PLAYLIST-TYPE:VOD
|
||||||
|
#EXT-X-MAP:URI="sparks_dovi_8-video.mp4",BYTERANGE="1355@0"
|
||||||
|
#EXT-X-KEY:METHOD=SAMPLE-AES-CTR,URI="data:text/plain;base64,MTIzNDU2Nzg5MDEyMzQ1Ng==",KEYFORMAT="identity"
|
||||||
|
#EXTINF:2.002,
|
||||||
|
#EXT-X-BYTERANGE:172013@1435
|
||||||
|
sparks_dovi_8-video.mp4
|
||||||
|
#EXTINF:2.002,
|
||||||
|
#EXT-X-BYTERANGE:189474
|
||||||
|
sparks_dovi_8-video.mp4
|
||||||
|
#EXTINF:2.002,
|
||||||
|
#EXT-X-BYTERANGE:204854
|
||||||
|
sparks_dovi_8-video.mp4
|
||||||
|
#EXTINF:0.017,
|
||||||
|
#EXT-X-BYTERANGE:2296
|
||||||
|
sparks_dovi_8-video.mp4
|
||||||
|
#EXT-X-ENDLIST
|
|
@ -7,20 +7,24 @@
|
||||||
#ifndef PACKAGER_MEDIA_EVENT_COMBINED_MUXER_LISTENER_H_
|
#ifndef PACKAGER_MEDIA_EVENT_COMBINED_MUXER_LISTENER_H_
|
||||||
#define PACKAGER_MEDIA_EVENT_COMBINED_MUXER_LISTENER_H_
|
#define PACKAGER_MEDIA_EVENT_COMBINED_MUXER_LISTENER_H_
|
||||||
|
|
||||||
#include <list>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "packager/media/event/muxer_listener.h"
|
#include "packager/media/event/muxer_listener.h"
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
|
||||||
|
/// This class supports a group of MuxerListeners. All events are forwarded to
|
||||||
|
/// every individual MuxerListeners contained in this CombinedMuxerListener.
|
||||||
class CombinedMuxerListener : public MuxerListener {
|
class CombinedMuxerListener : public MuxerListener {
|
||||||
public:
|
public:
|
||||||
CombinedMuxerListener() = default;
|
CombinedMuxerListener() = default;
|
||||||
|
|
||||||
void AddListener(std::unique_ptr<MuxerListener> listener);
|
void AddListener(std::unique_ptr<MuxerListener> listener);
|
||||||
|
|
||||||
|
/// @name MuxerListener implementation overrides.
|
||||||
|
/// @{
|
||||||
void OnEncryptionInfoReady(bool is_initial_encryption_info,
|
void OnEncryptionInfoReady(bool is_initial_encryption_info,
|
||||||
FourCC protection_scheme,
|
FourCC protection_scheme,
|
||||||
const std::vector<uint8_t>& key_id,
|
const std::vector<uint8_t>& key_id,
|
||||||
|
@ -41,11 +45,29 @@ class CombinedMuxerListener : public MuxerListener {
|
||||||
uint64_t segment_file_size) override;
|
uint64_t segment_file_size) override;
|
||||||
void OnKeyFrame(int64_t timestamp, uint64_t start_byte_offset, uint64_t size);
|
void OnKeyFrame(int64_t timestamp, uint64_t start_byte_offset, uint64_t size);
|
||||||
void OnCueEvent(int64_t timestamp, const std::string& cue_data) override;
|
void OnCueEvent(int64_t timestamp, const std::string& cue_data) override;
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// Limit the number of children MuxerListeners. It can only be used to reduce
|
||||||
|
/// the number of children.
|
||||||
|
/// @num is the number to set to. It is a no-op if `num` is equal or greater
|
||||||
|
/// than the existing number of children MuxerListeners.
|
||||||
|
void LimitNumOfMuxerListners(size_t num) {
|
||||||
|
if (num < muxer_listeners_.size())
|
||||||
|
muxer_listeners_.resize(num);
|
||||||
|
}
|
||||||
|
/// @return MuxerListener at the specified index or nullptr if the index is
|
||||||
|
/// out of range.
|
||||||
|
MuxerListener* MuxerListenerAt(size_t index) {
|
||||||
|
return (index < muxer_listeners_.size()) ? muxer_listeners_[index].get()
|
||||||
|
: nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::list<std::unique_ptr<MuxerListener>> muxer_listeners_;
|
CombinedMuxerListener(const CombinedMuxerListener&) = delete;
|
||||||
|
CombinedMuxerListener& operator=(const CombinedMuxerListener&) = delete;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(CombinedMuxerListener);
|
std::vector<std::unique_ptr<MuxerListener>> muxer_listeners_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
|
|
|
@ -20,6 +20,8 @@
|
||||||
'hls_notify_muxer_listener.h',
|
'hls_notify_muxer_listener.h',
|
||||||
'mpd_notify_muxer_listener.cc',
|
'mpd_notify_muxer_listener.cc',
|
||||||
'mpd_notify_muxer_listener.h',
|
'mpd_notify_muxer_listener.h',
|
||||||
|
'multi_codec_muxer_listener.cc',
|
||||||
|
'multi_codec_muxer_listener.h',
|
||||||
'muxer_listener.h',
|
'muxer_listener.h',
|
||||||
'muxer_listener_factory.cc',
|
'muxer_listener_factory.cc',
|
||||||
'muxer_listener_factory.h',
|
'muxer_listener_factory.h',
|
||||||
|
@ -56,6 +58,7 @@
|
||||||
'hls_notify_muxer_listener_unittest.cc',
|
'hls_notify_muxer_listener_unittest.cc',
|
||||||
'muxer_listener_internal_unittest.cc',
|
'muxer_listener_internal_unittest.cc',
|
||||||
'mpd_notify_muxer_listener_unittest.cc',
|
'mpd_notify_muxer_listener_unittest.cc',
|
||||||
|
'multi_codec_muxer_listener_unittest.cc',
|
||||||
'muxer_listener_test_helper.cc',
|
'muxer_listener_test_helper.cc',
|
||||||
'muxer_listener_test_helper.h',
|
'muxer_listener_test_helper.h',
|
||||||
'vod_media_info_dump_muxer_listener_unittest.cc',
|
'vod_media_info_dump_muxer_listener_unittest.cc',
|
||||||
|
@ -70,6 +73,7 @@
|
||||||
'../../third_party/protobuf/protobuf.gyp:protobuf_full_do_not_use',
|
'../../third_party/protobuf/protobuf.gyp:protobuf_full_do_not_use',
|
||||||
'../test/media_test.gyp:run_tests_with_atexit_manager',
|
'../test/media_test.gyp:run_tests_with_atexit_manager',
|
||||||
'media_event',
|
'media_event',
|
||||||
|
'mock_muxer_listener',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
// Copyright 2019 Google LLC. All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file or at
|
||||||
|
// https://developers.google.com/open-source/licenses/bsd
|
||||||
|
|
||||||
|
#include "packager/media/event/multi_codec_muxer_listener.h"
|
||||||
|
|
||||||
|
#include "packager/base/logging.h"
|
||||||
|
#include "packager/base/strings/string_split.h"
|
||||||
|
#include "packager/media/base/stream_info.h"
|
||||||
|
|
||||||
|
namespace shaka {
|
||||||
|
namespace media {
|
||||||
|
|
||||||
|
void MultiCodecMuxerListener::OnMediaStart(const MuxerOptions& muxer_options,
|
||||||
|
const StreamInfo& stream_info,
|
||||||
|
uint32_t time_scale,
|
||||||
|
ContainerType container_type) {
|
||||||
|
size_t num_codecs = 0;
|
||||||
|
for (const std::string& codec_string :
|
||||||
|
base::SplitString(stream_info.codec_string(), ";", base::KEEP_WHITESPACE,
|
||||||
|
base::SPLIT_WANT_NONEMPTY)) {
|
||||||
|
std::unique_ptr<StreamInfo> current_stream_info = stream_info.Clone();
|
||||||
|
current_stream_info->set_codec_string(codec_string);
|
||||||
|
MuxerListener* current_muxer_listener = MuxerListenerAt(num_codecs++);
|
||||||
|
if (!current_muxer_listener) {
|
||||||
|
LOG(WARNING) << "'" << codec_string << "' is not handled.";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
current_muxer_listener->OnMediaStart(muxer_options, *current_stream_info,
|
||||||
|
time_scale, container_type);
|
||||||
|
}
|
||||||
|
// We only need |num_codecs| MuxerListeners.
|
||||||
|
LimitNumOfMuxerListners(num_codecs);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace media
|
||||||
|
} // namespace shaka
|
|
@ -0,0 +1,42 @@
|
||||||
|
// Copyright 2019 Google LLC. All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file or at
|
||||||
|
// https://developers.google.com/open-source/licenses/bsd
|
||||||
|
|
||||||
|
#ifndef PACKAGER_MEDIA_EVENT_MULTI_CODEC_MUXER_LISTENER_H_
|
||||||
|
#define PACKAGER_MEDIA_EVENT_MULTI_CODEC_MUXER_LISTENER_H_
|
||||||
|
|
||||||
|
#include "packager/media/event/combined_muxer_listener.h"
|
||||||
|
|
||||||
|
namespace shaka {
|
||||||
|
namespace media {
|
||||||
|
|
||||||
|
/// MultiCodecMuxerListener is a variant of CombinedMuxerListener. It is
|
||||||
|
/// designed to handle the case that a stream can be signalled in multiple
|
||||||
|
/// different codecs. Like a normal CombinedMuxerListener, it contains multiple
|
||||||
|
/// child MuxerListeners, with one child per codec. If there are more child
|
||||||
|
/// MuxerListeners than the number of codecs, the extra child MuxerListeners are
|
||||||
|
/// removed; on the other hand, if there are more codecs than the number of
|
||||||
|
/// child MuxerListeners, the extra codecs are not handled.
|
||||||
|
class MultiCodecMuxerListener : public CombinedMuxerListener {
|
||||||
|
public:
|
||||||
|
MultiCodecMuxerListener() = default;
|
||||||
|
|
||||||
|
/// @name CombinedMuxerListener implementation overrides.
|
||||||
|
/// @{
|
||||||
|
void OnMediaStart(const MuxerOptions& muxer_options,
|
||||||
|
const StreamInfo& stream_info,
|
||||||
|
uint32_t time_scale,
|
||||||
|
ContainerType container_type) override;
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
private:
|
||||||
|
MultiCodecMuxerListener(const MultiCodecMuxerListener&) = delete;
|
||||||
|
MultiCodecMuxerListener& operator=(const MultiCodecMuxerListener&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace media
|
||||||
|
} // namespace shaka
|
||||||
|
|
||||||
|
#endif // PACKAGER_MEDIA_EVENT_MULTI_CODEC_MUXER_LISTENER_H_
|
|
@ -0,0 +1,127 @@
|
||||||
|
// Copyright 2019 Google LLC. All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file or at
|
||||||
|
// https://developers.google.com/open-source/licenses/bsd
|
||||||
|
|
||||||
|
#include "packager/media/event/multi_codec_muxer_listener.h"
|
||||||
|
|
||||||
|
#include <gmock/gmock.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "packager/media/base/muxer_options.h"
|
||||||
|
#include "packager/media/event/mock_muxer_listener.h"
|
||||||
|
#include "packager/media/event/muxer_listener_test_helper.h"
|
||||||
|
|
||||||
|
namespace shaka {
|
||||||
|
namespace media {
|
||||||
|
|
||||||
|
using ::testing::_;
|
||||||
|
using ::testing::Property;
|
||||||
|
using ::testing::StrEq;
|
||||||
|
using ::testing::StrictMock;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
const uint64_t kSegmentStartTime = 19283;
|
||||||
|
const uint64_t kSegmentDuration = 98028;
|
||||||
|
const uint64_t kSegmentSize = 756739;
|
||||||
|
const uint32_t kTimescale = 90000;
|
||||||
|
MuxerListener::ContainerType kContainer = MuxerListener::kContainerMpeg2ts;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
class MultiCodecMuxerListenerTest : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
MultiCodecMuxerListenerTest() {
|
||||||
|
std::unique_ptr<StrictMock<MockMuxerListener>> listener_1(
|
||||||
|
new StrictMock<MockMuxerListener>);
|
||||||
|
listener_for_first_codec_ = listener_1.get();
|
||||||
|
std::unique_ptr<StrictMock<MockMuxerListener>> listener_2(
|
||||||
|
new StrictMock<MockMuxerListener>);
|
||||||
|
listener_for_second_codec_ = listener_2.get();
|
||||||
|
multi_codec_listener_.AddListener(std::move(listener_1));
|
||||||
|
multi_codec_listener_.AddListener(std::move(listener_2));
|
||||||
|
|
||||||
|
muxer_options_.segment_template = "$Number$.ts";
|
||||||
|
|
||||||
|
VideoStreamInfoParameters video_params = GetDefaultVideoStreamInfoParams();
|
||||||
|
video_stream_info_ = CreateVideoStreamInfo(video_params);
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiCodecMuxerListener multi_codec_listener_;
|
||||||
|
StrictMock<MockMuxerListener>* listener_for_first_codec_;
|
||||||
|
StrictMock<MockMuxerListener>* listener_for_second_codec_;
|
||||||
|
MuxerOptions muxer_options_;
|
||||||
|
std::shared_ptr<StreamInfo> video_stream_info_;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(MultiCodecMuxerListenerTest, OnMediaStartSingleCodec) {
|
||||||
|
video_stream_info_->set_codec_string("codec_1");
|
||||||
|
|
||||||
|
EXPECT_CALL(
|
||||||
|
*listener_for_first_codec_,
|
||||||
|
OnMediaStart(_, Property(&StreamInfo::codec_string, StrEq("codec_1")),
|
||||||
|
kTimescale, kContainer))
|
||||||
|
.Times(1);
|
||||||
|
|
||||||
|
multi_codec_listener_.OnMediaStart(muxer_options_, *video_stream_info_,
|
||||||
|
kTimescale, kContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(MultiCodecMuxerListenerTest, OnNewSegmentAfterOnMediaStartSingleCodec) {
|
||||||
|
video_stream_info_->set_codec_string("codec_1");
|
||||||
|
|
||||||
|
EXPECT_CALL(*listener_for_first_codec_, OnMediaStart(_, _, _, _)).Times(1);
|
||||||
|
|
||||||
|
multi_codec_listener_.OnMediaStart(muxer_options_, *video_stream_info_,
|
||||||
|
kTimescale, kContainer);
|
||||||
|
|
||||||
|
EXPECT_CALL(*listener_for_first_codec_,
|
||||||
|
OnNewSegment(StrEq("new_segment_name10.ts"), kSegmentStartTime,
|
||||||
|
kSegmentDuration, kSegmentSize));
|
||||||
|
|
||||||
|
multi_codec_listener_.OnNewSegment("new_segment_name10.ts", kSegmentStartTime,
|
||||||
|
kSegmentDuration, kSegmentSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(MultiCodecMuxerListenerTest, OnMediaStartTwoCodecs) {
|
||||||
|
video_stream_info_->set_codec_string("codec_1;codec_2");
|
||||||
|
|
||||||
|
EXPECT_CALL(
|
||||||
|
*listener_for_first_codec_,
|
||||||
|
OnMediaStart(_, Property(&StreamInfo::codec_string, StrEq("codec_1")),
|
||||||
|
kTimescale, kContainer))
|
||||||
|
.Times(1);
|
||||||
|
EXPECT_CALL(
|
||||||
|
*listener_for_second_codec_,
|
||||||
|
OnMediaStart(_, Property(&StreamInfo::codec_string, StrEq("codec_2")),
|
||||||
|
kTimescale, kContainer))
|
||||||
|
.Times(1);
|
||||||
|
|
||||||
|
multi_codec_listener_.OnMediaStart(muxer_options_, *video_stream_info_,
|
||||||
|
kTimescale, kContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(MultiCodecMuxerListenerTest, OnNewSegmentAfterOnMediaStartTwoCodecs) {
|
||||||
|
video_stream_info_->set_codec_string("codec_1;codec_2");
|
||||||
|
|
||||||
|
EXPECT_CALL(*listener_for_first_codec_, OnMediaStart(_, _, _, _)).Times(1);
|
||||||
|
EXPECT_CALL(*listener_for_second_codec_, OnMediaStart(_, _, _, _)).Times(1);
|
||||||
|
|
||||||
|
multi_codec_listener_.OnMediaStart(muxer_options_, *video_stream_info_,
|
||||||
|
kTimescale, kContainer);
|
||||||
|
|
||||||
|
EXPECT_CALL(*listener_for_first_codec_,
|
||||||
|
OnNewSegment(StrEq("new_segment_name10.ts"), kSegmentStartTime,
|
||||||
|
kSegmentDuration, kSegmentSize));
|
||||||
|
EXPECT_CALL(*listener_for_second_codec_,
|
||||||
|
OnNewSegment(StrEq("new_segment_name10.ts"), kSegmentStartTime,
|
||||||
|
kSegmentDuration, kSegmentSize));
|
||||||
|
|
||||||
|
multi_codec_listener_.OnNewSegment("new_segment_name10.ts", kSegmentStartTime,
|
||||||
|
kSegmentDuration, kSegmentSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace media
|
||||||
|
} // namespace shaka
|
|
@ -6,12 +6,15 @@
|
||||||
|
|
||||||
#include "packager/media/event/muxer_listener_factory.h"
|
#include "packager/media/event/muxer_listener_factory.h"
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
|
||||||
#include "packager/base/memory/ptr_util.h"
|
#include "packager/base/memory/ptr_util.h"
|
||||||
#include "packager/base/strings/stringprintf.h"
|
#include "packager/base/strings/stringprintf.h"
|
||||||
#include "packager/hls/base/hls_notifier.h"
|
#include "packager/hls/base/hls_notifier.h"
|
||||||
#include "packager/media/event/combined_muxer_listener.h"
|
#include "packager/media/event/combined_muxer_listener.h"
|
||||||
#include "packager/media/event/hls_notify_muxer_listener.h"
|
#include "packager/media/event/hls_notify_muxer_listener.h"
|
||||||
#include "packager/media/event/mpd_notify_muxer_listener.h"
|
#include "packager/media/event/mpd_notify_muxer_listener.h"
|
||||||
|
#include "packager/media/event/multi_codec_muxer_listener.h"
|
||||||
#include "packager/media/event/muxer_listener.h"
|
#include "packager/media/event/muxer_listener.h"
|
||||||
#include "packager/media/event/vod_media_info_dump_muxer_listener.h"
|
#include "packager/media/event/vod_media_info_dump_muxer_listener.h"
|
||||||
#include "packager/mpd/base/mpd_notifier.h"
|
#include "packager/mpd/base/mpd_notifier.h"
|
||||||
|
@ -87,25 +90,36 @@ std::unique_ptr<MuxerListener> MuxerListenerFactory::CreateListener(
|
||||||
const StreamData& stream) {
|
const StreamData& stream) {
|
||||||
const int stream_index = stream_index_++;
|
const int stream_index = stream_index_++;
|
||||||
|
|
||||||
std::unique_ptr<CombinedMuxerListener> combined_listener(
|
// Use a MultiCodecMuxerListener to handle possible DolbyVision profile 8
|
||||||
new CombinedMuxerListener);
|
// stream which can be signalled as two different codecs.
|
||||||
|
std::unique_ptr<MultiCodecMuxerListener> multi_codec_listener(
|
||||||
if (output_media_info_) {
|
new MultiCodecMuxerListener);
|
||||||
combined_listener->AddListener(
|
// Creates two child MuxerListeners. Both are used if the stream is a
|
||||||
CreateMediaInfoDumpListenerInternal(stream.media_info_output));
|
// multi-codec stream (e.g. DolbyVision proifile 8); otherwise the second
|
||||||
}
|
// child is ignored. Right now the only use case is DolbyVision profile 8
|
||||||
if (mpd_notifier_) {
|
// which contains two codecs.
|
||||||
combined_listener->AddListener(
|
for (int i = 0; i < 2; i++) {
|
||||||
CreateMpdListenerInternal(stream, mpd_notifier_));
|
std::unique_ptr<CombinedMuxerListener> combined_listener(
|
||||||
}
|
new CombinedMuxerListener);
|
||||||
if (hls_notifier_) {
|
if (output_media_info_) {
|
||||||
for (auto& listener :
|
combined_listener->AddListener(
|
||||||
CreateHlsListenersInternal(stream, stream_index, hls_notifier_)) {
|
CreateMediaInfoDumpListenerInternal(stream.media_info_output));
|
||||||
combined_listener->AddListener(std::move(listener));
|
|
||||||
}
|
}
|
||||||
|
if (mpd_notifier_) {
|
||||||
|
combined_listener->AddListener(
|
||||||
|
CreateMpdListenerInternal(stream, mpd_notifier_));
|
||||||
|
}
|
||||||
|
if (hls_notifier_) {
|
||||||
|
for (auto& listener :
|
||||||
|
CreateHlsListenersInternal(stream, stream_index, hls_notifier_)) {
|
||||||
|
combined_listener->AddListener(std::move(listener));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
multi_codec_listener->AddListener(std::move(combined_listener));
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::move(combined_listener);
|
return std::move(multi_codec_listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<MuxerListener> MuxerListenerFactory::CreateHlsListener(
|
std::unique_ptr<MuxerListener> MuxerListenerFactory::CreateHlsListener(
|
||||||
|
|
|
@ -139,12 +139,26 @@ bool UpdateCodecStringForDolbyVision(
|
||||||
"configuration record.";
|
"configuration record.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (actual_format == FOURCC_dvh1 || actual_format == FOURCC_dvhe) {
|
switch (actual_format) {
|
||||||
// Non-Backward compatibility mode. Replace the code string with
|
case FOURCC_dvh1:
|
||||||
// Dolby Vision only.
|
case FOURCC_dvhe:
|
||||||
*codec_string = dovi_config.GetCodecString(actual_format);
|
// Non-Backward compatibility mode. Replace the code string with
|
||||||
} else {
|
// Dolby Vision only.
|
||||||
// TODO(kqyang): Support backward compatible signaling.
|
*codec_string = dovi_config.GetCodecString(actual_format);
|
||||||
|
break;
|
||||||
|
case FOURCC_hev1:
|
||||||
|
// Backward compatibility mode. Two codecs are signalled: base codec
|
||||||
|
// without Dolby Vision and HDR with Dolby Vision.
|
||||||
|
*codec_string += ";" + dovi_config.GetCodecString(FOURCC_dvhe);
|
||||||
|
break;
|
||||||
|
case FOURCC_hvc1:
|
||||||
|
// See above.
|
||||||
|
*codec_string += ";" + dovi_config.GetCodecString(FOURCC_dvh1);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG(ERROR) << "Unsupported format with extra codec "
|
||||||
|
<< FourCCToString(actual_format);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -629,6 +643,7 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
|
||||||
transfer_characteristics = hevc_config.transfer_characteristics();
|
transfer_characteristics = hevc_config.transfer_characteristics();
|
||||||
|
|
||||||
if (!entry.extra_codec_configs.empty()) {
|
if (!entry.extra_codec_configs.empty()) {
|
||||||
|
// |extra_codec_configs| is present only for Dolby Vision.
|
||||||
if (!UpdateCodecStringForDolbyVision(
|
if (!UpdateCodecStringForDolbyVision(
|
||||||
actual_format, entry.extra_codec_configs, &codec_string)) {
|
actual_format, entry.extra_codec_configs, &codec_string)) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -111,3 +111,4 @@ sintel-1024x436.mp4 - First 6 seconds of Sintel stream.
|
||||||
// First 6 seconds of Sparks, generated from Netflix open content:
|
// First 6 seconds of Sparks, generated from Netflix open content:
|
||||||
// http://download.opencontent.netflix.com/?prefix=TechblogAssets/Sparks/Sparks_DolbyVision_P3D65_PQ_5994fps_4096x2160_LtRt_IMF_20170214/
|
// http://download.opencontent.netflix.com/?prefix=TechblogAssets/Sparks/Sparks_DolbyVision_P3D65_PQ_5994fps_4096x2160_LtRt_IMF_20170214/
|
||||||
sparks_dovi_5.mp4 - Dolby Vision profile 5
|
sparks_dovi_5.mp4 - Dolby Vision profile 5
|
||||||
|
sparks_dovi_8.mp4 - Dolby Vision profile 8
|
||||||
|
|
Binary file not shown.
Loading…
Reference in New Issue