From 66b82f87dde36293b7723a2953e237bc61f18bfe Mon Sep 17 00:00:00 2001 From: Rintaro Kuroiwa Date: Mon, 28 Mar 2016 00:52:58 -0700 Subject: [PATCH] Add HlsNotifyMuxerListener - A MuxerListener implementation that uses HlsNotifier. Issue #85 Change-Id: I4c94c025be112d2bd02a78123da3f275e09c3a06 --- .../media/event/hls_notify_muxer_listener.cc | 80 ++++++++++ .../media/event/hls_notify_muxer_listener.h | 68 +++++++++ .../hls_notify_muxer_listener_unittest.cc | 142 ++++++++++++++++++ packager/media/event/media_event.gyp | 3 + 4 files changed, 293 insertions(+) create mode 100644 packager/media/event/hls_notify_muxer_listener.cc create mode 100644 packager/media/event/hls_notify_muxer_listener.h create mode 100644 packager/media/event/hls_notify_muxer_listener_unittest.cc diff --git a/packager/media/event/hls_notify_muxer_listener.cc b/packager/media/event/hls_notify_muxer_listener.cc new file mode 100644 index 0000000000..88104913a9 --- /dev/null +++ b/packager/media/event/hls_notify_muxer_listener.cc @@ -0,0 +1,80 @@ +// Copyright 2016 Google Inc. 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/hls_notify_muxer_listener.h" + +#include "packager/base/logging.h" +#include "packager/base/memory/scoped_ptr.h" +#include "packager/hls/base/hls_notifier.h" +#include "packager/media/base/muxer_options.h" +#include "packager/media/base/protection_system_specific_info.h" +#include "packager/media/event/muxer_listener_internal.h" + +namespace edash_packager { +namespace media { + +HlsNotifyMuxerListener::HlsNotifyMuxerListener(const std::string& playlist_name, + hls::HlsNotifier* hls_notifier) + : playlist_name_(playlist_name), hls_notifier_(hls_notifier) { + DCHECK(hls_notifier); +} + +HlsNotifyMuxerListener::~HlsNotifyMuxerListener() {} + +void HlsNotifyMuxerListener::OnEncryptionInfoReady( + bool is_initial_encryption_info, + const std::vector& key_id, + const std::vector& iv, + const std::vector& key_system_infos) { + for (const ProtectionSystemSpecificInfo& info : key_system_infos) { + const bool result = hls_notifier_->NotifyEncryptionUpdate( + stream_id_, key_id, info.system_id(), iv, info.pssh_data()); + LOG_IF(WARNING, !result) << "Failed to add encryption info."; + } +} + +void HlsNotifyMuxerListener::OnMediaStart(const MuxerOptions& muxer_options, + const StreamInfo& stream_info, + uint32_t time_scale, + ContainerType container_type) { + MediaInfo media_info; + if (!internal::GenerateMediaInfo(muxer_options, stream_info, time_scale, + container_type, &media_info)) { + LOG(ERROR) << "Failed to generate MediaInfo from input."; + return; + } + const bool result = hls_notifier_->NotifyNewStream( + media_info, playlist_name_, muxer_options.hls_name, + muxer_options.hls_group_id, &stream_id_); + LOG_IF(WARNING, !result) << "Failed to notify new stream."; +} + +void HlsNotifyMuxerListener::OnSampleDurationReady(uint32_t sample_duration) { +} + +void HlsNotifyMuxerListener::OnMediaEnd(bool has_init_range, + uint64_t init_range_start, + uint64_t init_range_end, + bool has_index_range, + uint64_t index_range_start, + uint64_t index_range_end, + float duration_seconds, + uint64_t file_size) { + const bool result = hls_notifier_->Flush(); + LOG_IF(WARNING, !result) << "Failed to flush."; +} + +void HlsNotifyMuxerListener::OnNewSegment(const std::string& file_name, + uint64_t start_time, + uint64_t duration, + uint64_t segment_file_size) { + const bool result = hls_notifier_->NotifyNewSegment( + stream_id_, file_name, start_time, duration, segment_file_size); + LOG_IF(WARNING, !result) << "Failed to add new segment."; +} + +} // namespace media +} // namespace edash_packager diff --git a/packager/media/event/hls_notify_muxer_listener.h b/packager/media/event/hls_notify_muxer_listener.h new file mode 100644 index 0000000000..9a7d10e13a --- /dev/null +++ b/packager/media/event/hls_notify_muxer_listener.h @@ -0,0 +1,68 @@ +// Copyright 2016 Google Inc. 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_HLS_NOTIFY_MUXER_LISTENER_H_ +#define PACKAGER_MEDIA_EVENT_HLS_NOTIFY_MUXER_LISTENER_H_ + +#include + +#include "packager/base/macros.h" +#include "packager/media/event/muxer_listener.h" + +namespace edash_packager { + +namespace hls { +class HlsNotifier; +} // namespace hls + +namespace media { + +/// MuxerListener that uses HlsNotifier. +class HlsNotifyMuxerListener : public MuxerListener { + public: + /// @param playlist_name is the name of the playlist for the muxer's stream. + /// @param hls_notifier used by this listener. Ownership does not transfer. + HlsNotifyMuxerListener(const std::string& playlist_name, + hls::HlsNotifier* hls_notifier); + ~HlsNotifyMuxerListener() override; + + /// @name MuxerListener implementation overrides. + /// @{ + void OnEncryptionInfoReady(bool is_initial_encryption_info, + const std::vector& key_id, + const std::vector& iv, + const std::vector& + key_system_info) override; + void OnMediaStart(const MuxerOptions& muxer_options, + const StreamInfo& stream_info, + uint32_t time_scale, + ContainerType container_type) override; + void OnSampleDurationReady(uint32_t sample_duration) override; + void OnMediaEnd(bool has_init_range, + uint64_t init_range_start, + uint64_t init_range_end, + bool has_index_range, + uint64_t index_range_start, + uint64_t index_range_end, + float duration_seconds, + uint64_t file_size) override; + void OnNewSegment(const std::string& file_name, + uint64_t start_time, + uint64_t duration, + uint64_t segment_file_size) override; + /// @} + + private: + const std::string playlist_name_; + hls::HlsNotifier* const hls_notifier_; + uint32_t stream_id_ = 0; + + DISALLOW_COPY_AND_ASSIGN(HlsNotifyMuxerListener); +}; + +} // namespace media +} // namespace edash_packager +#endif // PACKAGER_MEDIA_EVENT_HLS_NOTIFY_MUXER_LISTENER_H_ diff --git a/packager/media/event/hls_notify_muxer_listener_unittest.cc b/packager/media/event/hls_notify_muxer_listener_unittest.cc new file mode 100644 index 0000000000..f3d509a2f1 --- /dev/null +++ b/packager/media/event/hls_notify_muxer_listener_unittest.cc @@ -0,0 +1,142 @@ +// Copyright 2016 Google Inc. 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 +#include + +#include "packager/hls/base/hls_notifier.h" +#include "packager/media/base/muxer_options.h" +#include "packager/media/base/protection_system_specific_info.h" +#include "packager/media/event/hls_notify_muxer_listener.h" +#include "packager/media/event/muxer_listener_test_helper.h" + +namespace edash_packager { +namespace media { + +using ::testing::Return; +using ::testing::StrEq; +using ::testing::_; + +namespace { + +class MockHlsNotifier : public hls::HlsNotifier { + public: + MockHlsNotifier() + : HlsNotifier(hls::HlsNotifier::HlsProfile::kOnDemandProfile) {} + + MOCK_METHOD0(Init, bool()); + MOCK_METHOD5(NotifyNewStream, + bool(const MediaInfo& media_info, + const std::string& playlist_name, + const std::string& name, + const std::string& group_id, + uint32_t* stream_id)); + MOCK_METHOD5(NotifyNewSegment, + bool(uint32_t stream_id, + const std::string& segment_name, + uint64_t start_time, + uint64_t duration, + uint64_t size)); + MOCK_METHOD5( + NotifyEncryptionUpdate, + bool(uint32_t stream_id, + const std::vector& key_id, + const std::vector& system_id, + const std::vector& iv, + const std::vector& protection_system_specific_data)); + MOCK_METHOD0(Flush, bool()); +}; + +// Doesn't really matter what the values are as long as it is a system ID (16 +// bytes). +const uint8_t kAnySystemId[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, +}; + +const uint8_t kAnyData[] = { + 0xFF, 0x78, 0xAA, 0x6B, +}; + +// This value doesn't really affect the test, it's not used by the +// implementation. +const bool kInitialEncryptionInfo = true; + +const char kDefaultPlaylistName[] = "default_playlist.m3u8"; + +} // namespace + +class HlsNotifyMuxerListenerTest : public ::testing::Test { + protected: + HlsNotifyMuxerListenerTest() + : listener_(kDefaultPlaylistName, &mock_notifier_) {} + + MockHlsNotifier mock_notifier_; + HlsNotifyMuxerListener listener_; +}; + +TEST_F(HlsNotifyMuxerListenerTest, OnEncryptionInfoReady) { + ProtectionSystemSpecificInfo info; + std::vector system_id(kAnySystemId, + kAnySystemId + arraysize(kAnySystemId)); + info.set_system_id(system_id.data(), system_id.size()); + std::vector pssh_data(kAnyData, kAnyData + arraysize(kAnyData)); + info.set_pssh_data(pssh_data); + + std::vector key_id(16, 0x05); + std::vector key_system_infos; + key_system_infos.push_back(info); + + std::vector iv(16, 0x54); + + EXPECT_CALL(mock_notifier_, + NotifyEncryptionUpdate(_, key_id, system_id, iv, pssh_data)) + .WillOnce(Return(true)); + listener_.OnEncryptionInfoReady(kInitialEncryptionInfo, key_id, iv, + key_system_infos); +} + +TEST_F(HlsNotifyMuxerListenerTest, OnMediaStart) { + MuxerOptions muxer_options; + muxer_options.hls_name = "Name"; + muxer_options.hls_group_id = "GroupID"; + SetDefaultMuxerOptionsValues(&muxer_options); + VideoStreamInfoParameters video_params = GetDefaultVideoStreamInfoParams(); + scoped_refptr video_stream_info = + CreateVideoStreamInfo(video_params); + + EXPECT_CALL(mock_notifier_, + NotifyNewStream(_, StrEq(kDefaultPlaylistName), StrEq("Name"), + StrEq("GroupID"), _)) + .WillOnce(Return(true)); + + listener_.OnMediaStart(muxer_options, *video_stream_info, 90000, + MuxerListener::kContainerMpeg2ts); +} + +TEST_F(HlsNotifyMuxerListenerTest, OnSampleDurationReady) { + listener_.OnSampleDurationReady(2340); +} + +TEST_F(HlsNotifyMuxerListenerTest, OnMediaEnd) { + EXPECT_CALL(mock_notifier_, Flush()).WillOnce(Return(true)); + // None of these values matter, they are not used. + listener_.OnMediaEnd(false, 0, 0, false, 0, 0, 0, 0); +} + +TEST_F(HlsNotifyMuxerListenerTest, OnNewSegment) { + const uint64_t kStartTime = 19283; + const uint64_t kDuration = 98028; + const uint64_t kFileSize = 756739; + EXPECT_CALL(mock_notifier_, + NotifyNewSegment(_, StrEq("new_segment_name10.ts"), kStartTime, + kDuration, kFileSize)); + listener_.OnNewSegment("new_segment_name10.ts", kStartTime, kDuration, + kFileSize); +} + +} // namespace media +} // namespace edash_packager diff --git a/packager/media/event/media_event.gyp b/packager/media/event/media_event.gyp index c17ff7b343..5a7f99285d 100644 --- a/packager/media/event/media_event.gyp +++ b/packager/media/event/media_event.gyp @@ -13,6 +13,8 @@ 'target_name': 'media_event', 'type': '<(component)', 'sources': [ + 'hls_notify_muxer_listener.cc', + 'hls_notify_muxer_listener.h', 'mpd_notify_muxer_listener.cc', 'mpd_notify_muxer_listener.h', 'muxer_listener.h', @@ -34,6 +36,7 @@ 'target_name': 'media_event_unittest', 'type': '<(gtest_target_type)', 'sources': [ + 'hls_notify_muxer_listener_unittest.cc', 'mpd_notify_muxer_listener_unittest.cc', 'muxer_listener_test_helper.cc', 'muxer_listener_test_helper.h',