#![warn(missing_docs)]
#![allow(clippy::module_name_repetitions)]
#![allow(clippy::missing_errors_doc)]
#![allow(clippy::missing_panics_doc)]
pub mod audio_track;
pub mod bandwidth_estimator;
pub mod bitrate_calc;
pub mod byte_range;
pub mod cmaf;
pub mod cmaf_byterange;
pub mod config;
pub mod content_boundary;
pub mod dash;
pub mod dash_event_stream;
pub mod drm_info;
pub mod drm_packager;
pub mod encryption;
pub mod encryption_info;
pub mod error;
pub mod hls;
pub mod hls_interstitial;
pub mod isobmff_writer;
pub mod keyframe_alignment;
pub mod ladder;
pub mod low_latency;
pub mod manifest;
pub mod manifest_builder;
pub mod manifest_update;
pub mod metadata_cache;
pub mod multivariant;
pub mod multivariant_builder;
pub mod output;
pub mod packaging_config;
pub mod packaging_optimizer;
pub mod parallel_packager;
pub mod playlist_generator;
pub mod pre_roll;
pub mod pssh;
pub mod scene_segmenter;
pub mod segment;
pub mod segment_index;
pub mod segment_list;
pub mod segment_naming;
pub mod segment_timeline;
pub mod segment_validator;
pub mod ssai;
pub mod streaming_output;
pub mod subtitle_track;
pub mod thumbnail_track;
pub mod timed_metadata;
pub mod variant_stream;
pub use config::{
BitrateEntry, BitrateLadder, EncryptionConfig, EncryptionMethod, OutputConfig, PackagerConfig,
PackagingFormat, SegmentConfig, SegmentFormat,
};
pub use dash::{DashPackager, DashPackagerBuilder};
pub use error::{PackagerError, PackagerResult};
pub use hls::{HlsPackager, HlsPackagerBuilder};
pub use ladder::{BitrateLadderGenerator, LadderGenerator, LadderRung, SourceAnalysis, SourceInfo};
pub use multivariant_builder::{
DashAdaptationSetBuilder, HlsMultivariantBuilder, MultivariantPlaylistBuilder,
};
pub use variant_stream::{StreamCodec, VariantSet, VariantStream};
pub struct Packager {
config: PackagerConfig,
}
impl Packager {
pub fn new(config: PackagerConfig) -> PackagerResult<Self> {
config.validate()?;
Ok(Self { config })
}
pub async fn package_hls(&self, input: &str, output: &str) -> PackagerResult<()> {
let mut config = self.config.clone();
config.output.directory = output.into();
let mut hls_packager = HlsPackager::new(config)?;
hls_packager.package(input).await?;
Ok(())
}
pub async fn package_dash(&self, input: &str, output: &str) -> PackagerResult<()> {
let mut config = self.config.clone();
config.output.directory = output.into();
let mut dash_packager = DashPackager::new(config)?;
dash_packager.package(input).await?;
Ok(())
}
pub async fn package_both(&self, input: &str, output_base: &str) -> PackagerResult<()> {
use std::path::PathBuf;
let hls_output = PathBuf::from(output_base).join("hls");
let dash_output = PathBuf::from(output_base).join("dash");
let hls_str = hls_output.to_str().ok_or_else(|| {
PackagerError::InvalidConfig(format!(
"HLS output path contains invalid UTF-8: {}",
hls_output.display()
))
})?;
let dash_str = dash_output.to_str().ok_or_else(|| {
PackagerError::InvalidConfig(format!(
"DASH output path contains invalid UTF-8: {}",
dash_output.display()
))
})?;
self.package_hls(input, hls_str).await?;
self.package_dash(input, dash_str).await?;
Ok(())
}
#[must_use]
pub fn config(&self) -> &PackagerConfig {
&self.config
}
}
pub struct PackagerBuilder {
config: PackagerConfig,
}
impl PackagerBuilder {
#[must_use]
pub fn new() -> Self {
Self {
config: PackagerConfig::default(),
}
}
#[must_use]
pub fn format(mut self, format: PackagingFormat) -> Self {
self.config.format = format;
self
}
#[must_use]
pub fn ladder(mut self, ladder: BitrateLadder) -> Self {
self.config.ladder = ladder;
self
}
#[must_use]
pub fn segment_config(mut self, segment: SegmentConfig) -> Self {
self.config.segment = segment;
self
}
#[must_use]
pub fn encryption(mut self, encryption: EncryptionConfig) -> Self {
self.config.encryption = encryption;
self
}
#[must_use]
pub fn output(mut self, output: OutputConfig) -> Self {
self.config.output = output;
self
}
#[must_use]
pub fn low_latency(mut self, enabled: bool) -> Self {
self.config.low_latency = enabled;
self
}
#[must_use]
pub fn manifest_versioning(mut self, enabled: bool) -> Self {
self.config.manifest_versioning = enabled;
self
}
pub fn build(self) -> PackagerResult<Packager> {
Packager::new(self.config)
}
}
impl Default for PackagerBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_packager_creation() {
let config = PackagerConfig::default();
let packager = Packager::new(config);
assert!(packager.is_ok());
}
#[test]
fn test_packager_builder() {
let packager = PackagerBuilder::new()
.format(PackagingFormat::HlsFmp4)
.low_latency(true)
.build();
assert!(packager.is_ok());
let p = packager.expect("should succeed in test");
assert_eq!(p.config.format, PackagingFormat::HlsFmp4);
assert!(p.config.low_latency);
}
#[test]
fn test_bitrate_ladder_validation() {
let mut ladder = BitrateLadder::new();
ladder.add_entry(BitrateEntry::new(1_000_000, 1280, 720, "av1"));
let config = PackagerConfig::new().with_ladder(ladder);
assert!(config.validate().is_ok());
}
#[test]
fn test_package_both_valid_paths() {
let config = PackagerConfig::default();
let packager = Packager::new(config).expect("should succeed in test");
assert_eq!(packager.config().format, PackagingFormat::HlsFmp4);
}
#[test]
fn test_packager_builder_with_encryption() {
let mut enc = EncryptionConfig::default();
enc.method = EncryptionMethod::None;
let packager = PackagerBuilder::new().encryption(enc).build();
assert!(packager.is_ok());
}
}