2017-01-23 22:51:00 +00:00
|
|
|
// Copyright 2017 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_BASE_MEDIA_HANDLER_H_
|
|
|
|
#define PACKAGER_MEDIA_BASE_MEDIA_HANDLER_H_
|
|
|
|
|
|
|
|
#include <map>
|
|
|
|
#include <memory>
|
|
|
|
#include <utility>
|
|
|
|
|
2017-02-02 18:28:29 +00:00
|
|
|
#include "packager/media/base/media_sample.h"
|
|
|
|
#include "packager/media/base/stream_info.h"
|
2017-06-09 15:30:16 +00:00
|
|
|
#include "packager/media/base/text_sample.h"
|
2017-06-29 22:23:53 +00:00
|
|
|
#include "packager/status.h"
|
2017-01-23 22:51:00 +00:00
|
|
|
|
|
|
|
namespace shaka {
|
|
|
|
namespace media {
|
|
|
|
|
2017-02-02 18:28:29 +00:00
|
|
|
enum class StreamDataType {
|
|
|
|
kUnknown,
|
|
|
|
kStreamInfo,
|
|
|
|
kMediaSample,
|
2017-06-09 15:30:16 +00:00
|
|
|
kTextSample,
|
2017-02-02 18:28:29 +00:00
|
|
|
kSegmentInfo,
|
2017-11-13 18:08:44 +00:00
|
|
|
kScte35Event,
|
2017-11-21 21:35:20 +00:00
|
|
|
kCueEvent,
|
2017-11-13 18:08:44 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Scte35Event represents cuepoint markers in input streams. It will be used
|
|
|
|
// to represent out of band cuepoint markers too.
|
|
|
|
struct Scte35Event {
|
|
|
|
std::string id;
|
|
|
|
// Segmentation type id from SCTE35 segmentation descriptor.
|
|
|
|
int type = 0;
|
2018-03-16 00:19:01 +00:00
|
|
|
double start_time_in_seconds = 0;
|
|
|
|
double duration_in_seconds = 0;
|
2017-11-13 18:08:44 +00:00
|
|
|
std::string cue_data;
|
2017-02-02 18:28:29 +00:00
|
|
|
};
|
|
|
|
|
2017-11-21 21:35:20 +00:00
|
|
|
enum class CueEventType { kCueIn, kCueOut, kCuePoint };
|
|
|
|
|
|
|
|
// In server-based model, Chunking Handler consolidates SCTE-35 events and
|
|
|
|
// generates CueEvent before an ad is about to be inserted.
|
|
|
|
struct CueEvent {
|
|
|
|
CueEventType type = CueEventType::kCuePoint;
|
2018-03-15 23:01:47 +00:00
|
|
|
double time_in_seconds;
|
2017-11-21 21:35:20 +00:00
|
|
|
std::string cue_data;
|
|
|
|
};
|
|
|
|
|
2017-02-02 18:28:29 +00:00
|
|
|
struct SegmentInfo {
|
|
|
|
bool is_subsegment = false;
|
|
|
|
bool is_encrypted = false;
|
Implement ChunkingHandler
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.
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
with each other - which is not efficient and often difficult.
Consistent Chunking Algorithm:
1. Find the consistent chunkable boundary
Let the timestamps for video frames be (t1, t2, t3, ...). Then a
consistent chunkable boundary is simply the first chunkable boundary after
(tk / N) != (tk-1 / N), where '/' denotes integer division, and N is the
intended chunk duration.
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.
Change-Id: Id3bad51ab14f311efdb8713b6cd36d36cf9e4639
2017-02-07 18:58:47 +00:00
|
|
|
int64_t start_timestamp = -1;
|
|
|
|
int64_t duration = 0;
|
2017-03-11 02:48:04 +00:00
|
|
|
// This is only available if key rotation is enabled. Note that we may have
|
|
|
|
// a |key_rotation_encryption_config| even if the segment is not encrypted,
|
|
|
|
// which is the case for clear lead.
|
|
|
|
std::shared_ptr<EncryptionConfig> key_rotation_encryption_config;
|
2017-02-02 18:28:29 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// TODO(kqyang): Should we use protobuf?
|
|
|
|
struct StreamData {
|
2017-03-17 19:45:28 +00:00
|
|
|
size_t stream_index = static_cast<size_t>(-1);
|
Implement ChunkingHandler
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.
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
with each other - which is not efficient and often difficult.
Consistent Chunking Algorithm:
1. Find the consistent chunkable boundary
Let the timestamps for video frames be (t1, t2, t3, ...). Then a
consistent chunkable boundary is simply the first chunkable boundary after
(tk / N) != (tk-1 / N), where '/' denotes integer division, and N is the
intended chunk duration.
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.
Change-Id: Id3bad51ab14f311efdb8713b6cd36d36cf9e4639
2017-02-07 18:58:47 +00:00
|
|
|
StreamDataType stream_data_type = StreamDataType::kUnknown;
|
2017-02-02 18:28:29 +00:00
|
|
|
|
2017-09-12 17:24:24 +00:00
|
|
|
std::shared_ptr<const StreamInfo> stream_info;
|
|
|
|
std::shared_ptr<const MediaSample> media_sample;
|
|
|
|
std::shared_ptr<const TextSample> text_sample;
|
|
|
|
std::shared_ptr<const SegmentInfo> segment_info;
|
2017-11-13 18:08:44 +00:00
|
|
|
std::shared_ptr<const Scte35Event> scte35_event;
|
2017-11-21 21:35:20 +00:00
|
|
|
std::shared_ptr<const CueEvent> cue_event;
|
2017-09-12 17:24:24 +00:00
|
|
|
|
|
|
|
static std::unique_ptr<StreamData> FromStreamInfo(
|
|
|
|
size_t stream_index, std::shared_ptr<const StreamInfo> stream_info) {
|
|
|
|
std::unique_ptr<StreamData> stream_data(new StreamData);
|
|
|
|
stream_data->stream_index = stream_index;
|
|
|
|
stream_data->stream_data_type = StreamDataType::kStreamInfo;
|
|
|
|
stream_data->stream_info = std::move(stream_info);
|
|
|
|
return stream_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::unique_ptr<StreamData> FromMediaSample(
|
|
|
|
size_t stream_index, std::shared_ptr<const MediaSample> media_sample) {
|
|
|
|
std::unique_ptr<StreamData> stream_data(new StreamData);
|
|
|
|
stream_data->stream_index = stream_index;
|
|
|
|
stream_data->stream_data_type = StreamDataType::kMediaSample;
|
|
|
|
stream_data->media_sample = std::move(media_sample);
|
|
|
|
return stream_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::unique_ptr<StreamData> FromTextSample(
|
|
|
|
size_t stream_index, std::shared_ptr<const TextSample> text_sample) {
|
|
|
|
std::unique_ptr<StreamData> stream_data(new StreamData);
|
|
|
|
stream_data->stream_index = stream_index;
|
|
|
|
stream_data->stream_data_type = StreamDataType::kTextSample;
|
|
|
|
stream_data->text_sample = std::move(text_sample);
|
|
|
|
return stream_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::unique_ptr<StreamData> FromSegmentInfo(
|
|
|
|
size_t stream_index, std::shared_ptr<const SegmentInfo> segment_info) {
|
|
|
|
std::unique_ptr<StreamData> stream_data(new StreamData);
|
|
|
|
stream_data->stream_index = stream_index;
|
|
|
|
stream_data->stream_data_type = StreamDataType::kSegmentInfo;
|
|
|
|
stream_data->segment_info = std::move(segment_info);
|
|
|
|
return stream_data;
|
|
|
|
}
|
2017-11-13 18:08:44 +00:00
|
|
|
|
|
|
|
static std::unique_ptr<StreamData> FromScte35Event(
|
|
|
|
size_t stream_index,
|
|
|
|
std::shared_ptr<const Scte35Event> scte35_event) {
|
|
|
|
std::unique_ptr<StreamData> stream_data(new StreamData);
|
|
|
|
stream_data->stream_index = stream_index;
|
|
|
|
stream_data->stream_data_type = StreamDataType::kScte35Event;
|
|
|
|
stream_data->scte35_event = std::move(scte35_event);
|
|
|
|
return stream_data;
|
|
|
|
}
|
2017-11-21 21:35:20 +00:00
|
|
|
|
|
|
|
static std::unique_ptr<StreamData> FromCueEvent(
|
|
|
|
size_t stream_index,
|
|
|
|
std::shared_ptr<const CueEvent> cue_event) {
|
|
|
|
std::unique_ptr<StreamData> stream_data(new StreamData);
|
|
|
|
stream_data->stream_index = stream_index;
|
|
|
|
stream_data->stream_data_type = StreamDataType::kCueEvent;
|
|
|
|
stream_data->cue_event = std::move(cue_event);
|
|
|
|
return stream_data;
|
|
|
|
}
|
2017-02-02 18:28:29 +00:00
|
|
|
};
|
|
|
|
|
2017-01-23 22:51:00 +00:00
|
|
|
/// MediaHandler is the base media processing unit. Media handlers transform
|
|
|
|
/// the input streams and propagate the outputs to downstream media handlers.
|
|
|
|
/// There are three different types of media handlers:
|
|
|
|
/// 1) Single input single output
|
|
|
|
/// This is the most basic handler. It only supports one input and one
|
|
|
|
/// output with both index as 0.
|
|
|
|
/// 2) Multiple inputs multiple outputs
|
|
|
|
/// The number of outputs must be equal to the number of inputs. The
|
|
|
|
/// output stream at a specific index comes from the input stream at the
|
|
|
|
/// same index. Different streams usually share a common resource, although
|
|
|
|
/// they may be independent. One example of this is encryptor handler.
|
|
|
|
/// 3) Single input multiple outputs
|
|
|
|
/// The input stream is splitted into multiple output streams. One example
|
|
|
|
/// of this is trick play handler.
|
|
|
|
/// Other types of media handlers are disallowed and not supported.
|
|
|
|
class MediaHandler {
|
|
|
|
public:
|
|
|
|
MediaHandler() = default;
|
|
|
|
virtual ~MediaHandler() = default;
|
|
|
|
|
|
|
|
/// Connect downstream handler at the specified output stream index.
|
2017-03-03 00:10:30 +00:00
|
|
|
Status SetHandler(size_t output_stream_index,
|
2017-01-23 22:51:00 +00:00
|
|
|
std::shared_ptr<MediaHandler> handler);
|
|
|
|
|
|
|
|
/// Connect downstream handler to the next availble output stream index.
|
|
|
|
Status AddHandler(std::shared_ptr<MediaHandler> handler) {
|
|
|
|
return SetHandler(next_output_stream_index_, handler);
|
|
|
|
}
|
|
|
|
|
2017-02-02 18:28:29 +00:00
|
|
|
/// Initialize the handler and downstream handlers. Note that it should be
|
|
|
|
/// called after setting up the graph before running the graph.
|
|
|
|
Status Initialize();
|
|
|
|
|
2017-03-21 23:14:46 +00:00
|
|
|
/// Validate if the handler is connected to its upstream handler.
|
|
|
|
bool IsConnected() { return num_input_streams_ > 0; }
|
|
|
|
|
2017-01-23 22:51:00 +00:00
|
|
|
protected:
|
2017-02-02 18:28:29 +00:00
|
|
|
/// Internal implementation of initialize. Note that it should only initialize
|
|
|
|
/// the MediaHandler itself. Downstream handlers are handled in Initialize().
|
|
|
|
virtual Status InitializeInternal() = 0;
|
2017-01-23 22:51:00 +00:00
|
|
|
|
|
|
|
/// Process the incoming stream data. Note that (1) stream_data.stream_index
|
|
|
|
/// should be the input stream index; (2) The implementation needs to call
|
|
|
|
/// DispatchXxx to dispatch the processed stream data to the downstream
|
|
|
|
/// handlers after finishing processing if needed.
|
|
|
|
virtual Status Process(std::unique_ptr<StreamData> stream_data) = 0;
|
|
|
|
|
2017-02-22 20:14:26 +00:00
|
|
|
/// Event handler for flush request at the specific input stream index.
|
2017-03-03 00:10:30 +00:00
|
|
|
virtual Status OnFlushRequest(size_t input_stream_index);
|
2017-01-23 22:51:00 +00:00
|
|
|
|
|
|
|
/// Validate if the stream at the specified index actually exists.
|
2017-03-03 00:10:30 +00:00
|
|
|
virtual bool ValidateOutputStreamIndex(size_t stream_index) const;
|
2017-01-23 22:51:00 +00:00
|
|
|
|
|
|
|
/// Dispatch the stream data to downstream handlers. Note that
|
|
|
|
/// stream_data.stream_index should be the output stream index.
|
|
|
|
Status Dispatch(std::unique_ptr<StreamData> stream_data);
|
|
|
|
|
|
|
|
/// Dispatch the stream info to downstream handlers.
|
2017-09-12 17:24:24 +00:00
|
|
|
Status DispatchStreamInfo(
|
|
|
|
size_t stream_index, std::shared_ptr<const StreamInfo> stream_info) {
|
|
|
|
return Dispatch(StreamData::FromStreamInfo(stream_index, stream_info));
|
2017-01-23 22:51:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Dispatch the media sample to downstream handlers.
|
2017-09-12 17:24:24 +00:00
|
|
|
Status DispatchMediaSample(
|
|
|
|
size_t stream_index, std::shared_ptr<const MediaSample> media_sample) {
|
|
|
|
return Dispatch(StreamData::FromMediaSample(stream_index, media_sample));
|
2017-06-09 15:30:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Dispatch the text sample to downsream handlers.
|
|
|
|
// DispatchTextSample should only be override for testing.
|
2017-09-12 17:24:24 +00:00
|
|
|
Status DispatchTextSample(
|
|
|
|
size_t stream_index, std::shared_ptr<const TextSample> text_sample) {
|
|
|
|
return Dispatch(StreamData::FromTextSample(stream_index, text_sample));
|
2017-01-23 22:51:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Dispatch the segment info to downstream handlers.
|
2017-09-12 17:24:24 +00:00
|
|
|
Status DispatchSegmentInfo(
|
|
|
|
size_t stream_index, std::shared_ptr<const SegmentInfo> segment_info) {
|
|
|
|
return Dispatch(StreamData::FromSegmentInfo(stream_index, segment_info));
|
2017-01-23 22:51:00 +00:00
|
|
|
}
|
|
|
|
|
2017-11-13 18:08:44 +00:00
|
|
|
/// Dispatch the scte35 event to downstream handlers.
|
|
|
|
Status DispatchScte35Event(size_t stream_index,
|
|
|
|
std::shared_ptr<const Scte35Event> scte35_event) {
|
|
|
|
return Dispatch(StreamData::FromScte35Event(stream_index, scte35_event));
|
|
|
|
}
|
|
|
|
|
2017-11-21 21:35:20 +00:00
|
|
|
/// Dispatch the cue event to downstream handlers.
|
|
|
|
Status DispatchCueEvent(size_t stream_index,
|
|
|
|
std::shared_ptr<const CueEvent> cue_event) {
|
|
|
|
return Dispatch(StreamData::FromCueEvent(stream_index, cue_event));
|
|
|
|
}
|
|
|
|
|
2017-02-22 20:14:26 +00:00
|
|
|
/// Flush the downstream connected at the specified output stream index.
|
2017-03-03 00:10:30 +00:00
|
|
|
Status FlushDownstream(size_t output_stream_index);
|
2017-02-22 20:14:26 +00:00
|
|
|
|
2017-03-21 23:14:46 +00:00
|
|
|
/// Flush all connected downstreams.
|
|
|
|
Status FlushAllDownstreams();
|
|
|
|
|
2017-04-28 23:29:02 +00:00
|
|
|
bool initialized() { return initialized_; }
|
2017-03-03 00:10:30 +00:00
|
|
|
size_t num_input_streams() const { return num_input_streams_; }
|
|
|
|
size_t next_output_stream_index() const { return next_output_stream_index_; }
|
|
|
|
const std::map<size_t, std::pair<std::shared_ptr<MediaHandler>, size_t>>&
|
2017-02-21 18:36:50 +00:00
|
|
|
output_handlers() {
|
|
|
|
return output_handlers_;
|
|
|
|
}
|
2017-01-23 22:51:00 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
MediaHandler(const MediaHandler&) = delete;
|
|
|
|
MediaHandler& operator=(const MediaHandler&) = delete;
|
|
|
|
|
2017-02-02 18:28:29 +00:00
|
|
|
bool initialized_ = false;
|
2017-01-23 22:51:00 +00:00
|
|
|
// Number of input streams.
|
2017-03-03 00:10:30 +00:00
|
|
|
size_t num_input_streams_ = 0;
|
2017-01-23 22:51:00 +00:00
|
|
|
// The next available output stream index, used by AddHandler.
|
2017-03-03 00:10:30 +00:00
|
|
|
size_t next_output_stream_index_ = 0;
|
2017-01-23 22:51:00 +00:00
|
|
|
// output stream index -> {output handler, output handler input stream index}
|
|
|
|
// map.
|
2017-03-03 00:10:30 +00:00
|
|
|
std::map<size_t, std::pair<std::shared_ptr<MediaHandler>, size_t>>
|
|
|
|
output_handlers_;
|
2017-01-23 22:51:00 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace media
|
|
|
|
} // namespace shaka
|
|
|
|
|
|
|
|
#endif // PACKAGER_MEDIA_BASE_MEDIA_HANDLER_H_
|