mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2025-09-02 01:33:47 +00:00
hlssink3/hlscmafsink: Add byte range tag information to hls-segment-added message
While at it, for tests, replace unreachable!() with panic!() and print the error message contents. Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/2439>
This commit is contained in:
parent
efbfab76bc
commit
a8684853d0
4 changed files with 70 additions and 60 deletions
|
@ -545,6 +545,8 @@ impl HlsBaseSink {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let (init_segment_br, segment_br) = self.byte_ranges(context, &segment);
|
||||||
|
|
||||||
context.playlist.add_segment(segment);
|
context.playlist.add_segment(segment);
|
||||||
|
|
||||||
if context.playlist.is_type_undefined() {
|
if context.playlist.is_type_undefined() {
|
||||||
|
@ -559,6 +561,12 @@ impl HlsBaseSink {
|
||||||
if let Some(ts) = timestamp {
|
if let Some(ts) = timestamp {
|
||||||
s = s.field("timestamp", ts.timestamp());
|
s = s.field("timestamp", ts.timestamp());
|
||||||
};
|
};
|
||||||
|
if let Some(br) = init_segment_br {
|
||||||
|
s = s.field("initialization-segment-byte-range", br);
|
||||||
|
}
|
||||||
|
if let Some(br) = segment_br {
|
||||||
|
s = s.field("segment-byte-range", br);
|
||||||
|
}
|
||||||
self.post_message(
|
self.post_message(
|
||||||
gst::message::Element::builder(s.build())
|
gst::message::Element::builder(s.build())
|
||||||
.src(&*self.obj())
|
.src(&*self.obj())
|
||||||
|
@ -690,4 +698,33 @@ impl HlsBaseSink {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.single_media_file.is_some()
|
settings.single_media_file.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn byte_ranges(
|
||||||
|
&self,
|
||||||
|
context: &PlaylistContext,
|
||||||
|
segment: &MediaSegment,
|
||||||
|
) -> (Option<gst::Structure>, Option<gst::Structure>) {
|
||||||
|
let mut init_segment_br = None;
|
||||||
|
let mut segment_br = None;
|
||||||
|
|
||||||
|
if context.single_media_file {
|
||||||
|
if let Some(ref map) = segment.map {
|
||||||
|
if let Some(br) = &map.byte_range {
|
||||||
|
let mut s = gst::Structure::new_empty("byte-range");
|
||||||
|
s.set("length", br.length);
|
||||||
|
s.set("offset", br.offset.unwrap());
|
||||||
|
init_segment_br = Some(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ref br) = segment.byte_range {
|
||||||
|
let mut s = gst::Structure::new_empty("byte-range");
|
||||||
|
s.set("length", br.length);
|
||||||
|
s.set("offset", br.offset.unwrap());
|
||||||
|
segment_br = Some(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(init_segment_br, segment_br)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,57 +6,30 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct ByteRange {
|
pub struct ByteRange {
|
||||||
length: u64,
|
length: u64,
|
||||||
offset: u64,
|
offset: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
pub fn get_byte_ranges(s: &gst::StructureRef) -> Vec<ByteRange> {
|
||||||
pub fn extract_map_byterange(input: &str) -> Option<ByteRange> {
|
let mut ranges = Vec::new();
|
||||||
input
|
|
||||||
.lines()
|
if let Ok(br) = s.get::<gst::Structure>("initialization-segment-byte-range") {
|
||||||
.find(|line| line.starts_with("#EXT-X-MAP:"))
|
let length = br.get::<u64>("length").unwrap();
|
||||||
.and_then(|line| {
|
let offset = br.get::<u64>("offset").unwrap();
|
||||||
line.find("BYTERANGE=\"")
|
assert!(length != 0);
|
||||||
.and_then(|start| {
|
ranges.push(ByteRange { length, offset })
|
||||||
let content_start = start + "BYTERANGE=\"".len();
|
|
||||||
line[content_start..]
|
|
||||||
.find('"')
|
|
||||||
.map(|end| &line[content_start..content_start + end])
|
|
||||||
})
|
|
||||||
.and_then(|byterange_str| {
|
|
||||||
let mut parts = byterange_str.split('@');
|
|
||||||
match (parts.next(), parts.next()) {
|
|
||||||
(Some(len_str), Some(off_str)) => {
|
|
||||||
let length = len_str.parse::<u64>().ok()?;
|
|
||||||
let offset = off_str.parse::<u64>().ok()?;
|
|
||||||
Some(ByteRange { length, offset })
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn extract_byteranges(input: &str) -> Vec<ByteRange> {
|
if let Ok(br) = s.get::<gst::Structure>("segment-byte-range") {
|
||||||
input
|
let length = br.get::<u64>("length").unwrap();
|
||||||
.split('\n')
|
let offset = br.get::<u64>("offset").unwrap();
|
||||||
.filter(|line| line.starts_with("#EXT-X-BYTERANGE:"))
|
assert!(length != 0);
|
||||||
.filter_map(|line| {
|
ranges.push(ByteRange { length, offset })
|
||||||
line.strip_prefix("#EXT-X-BYTERANGE:").and_then(|content| {
|
|
||||||
let mut parts = content.split('@');
|
|
||||||
match (parts.next(), parts.next()) {
|
|
||||||
(Some(len_str), Some(off_str)) => {
|
|
||||||
let length = len_str.parse::<u64>().ok()?;
|
|
||||||
let offset = off_str.parse::<u64>().ok()?;
|
|
||||||
Some(ByteRange { length, offset })
|
|
||||||
}
|
}
|
||||||
_ => None,
|
|
||||||
}
|
ranges
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn validate_byterange_sequence(ranges: &[ByteRange]) -> bool {
|
pub fn validate_byterange_sequence(ranges: &[ByteRange]) -> bool {
|
||||||
|
|
|
@ -159,6 +159,7 @@ fn test_hlscmafsink_video_with_single_media_file() -> Result<(), ()> {
|
||||||
|
|
||||||
pipeline.set_state(gst::State::Playing).unwrap();
|
pipeline.set_state(gst::State::Playing).unwrap();
|
||||||
|
|
||||||
|
let mut byte_ranges: Vec<ByteRange> = Vec::new();
|
||||||
let mut eos = false;
|
let mut eos = false;
|
||||||
let bus = pipeline.bus().unwrap();
|
let bus = pipeline.bus().unwrap();
|
||||||
while let Some(msg) = bus.timed_pop(gst::ClockTime::NONE) {
|
while let Some(msg) = bus.timed_pop(gst::ClockTime::NONE) {
|
||||||
|
@ -172,13 +173,16 @@ fn test_hlscmafsink_video_with_single_media_file() -> Result<(), ()> {
|
||||||
if let Some(structure) = msg.structure() {
|
if let Some(structure) = msg.structure() {
|
||||||
if structure.has_name("hls-segment-added") {
|
if structure.has_name("hls-segment-added") {
|
||||||
let location = structure.get::<String>("location").unwrap();
|
let location = structure.get::<String>("location").unwrap();
|
||||||
|
let byte_range = get_byte_ranges(structure);
|
||||||
|
byte_ranges.extend(byte_range);
|
||||||
|
|
||||||
hls_messages_sender
|
hls_messages_sender
|
||||||
.try_send(HlsSinkEvent::SegmentAddedMessage(location))
|
.try_send(HlsSinkEvent::SegmentAddedMessage(location))
|
||||||
.expect("Send segment added event");
|
.expect("Send segment added event");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MessageView::Error(..) => unreachable!(),
|
MessageView::Error(err) => panic!("{err}"),
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -196,17 +200,11 @@ fn test_hlscmafsink_video_with_single_media_file() -> Result<(), ()> {
|
||||||
actual_messages.push(event);
|
actual_messages.push(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
let contents = playlist_content.lock().unwrap();
|
eprintln!("Playlist byte ranges: {byte_ranges:?}");
|
||||||
|
|
||||||
let mut map_byte_range = Vec::new();
|
|
||||||
map_byte_range.push(extract_map_byterange(contents.as_str()).unwrap());
|
|
||||||
let byte_ranges = extract_byteranges(contents.as_str());
|
|
||||||
map_byte_range.extend(byte_ranges);
|
|
||||||
|
|
||||||
// We only validate the byte range and map tag. The actual value of
|
// We only validate the byte range and map tag. The actual value of
|
||||||
// byte range can differ from each run and hence we do not validate
|
// byte range can differ from each run and hence we do not validate
|
||||||
// the entire playlist.
|
// the entire playlist.
|
||||||
assert!(validate_byterange_sequence(&map_byte_range));
|
assert!(validate_byterange_sequence(&byte_ranges));
|
||||||
|
|
||||||
let expected_messages = {
|
let expected_messages = {
|
||||||
use self::HlsSinkEvent::*;
|
use self::HlsSinkEvent::*;
|
||||||
|
|
|
@ -14,7 +14,7 @@ use std::sync::LazyLock;
|
||||||
use std::sync::{mpsc, Arc, Mutex};
|
use std::sync::{mpsc, Arc, Mutex};
|
||||||
|
|
||||||
mod common;
|
mod common;
|
||||||
use crate::common::{extract_byteranges, validate_byterange_sequence};
|
use crate::common::{get_byte_ranges, validate_byterange_sequence, ByteRange};
|
||||||
|
|
||||||
static CAT: LazyLock<gst::DebugCategory> = LazyLock::new(|| {
|
static CAT: LazyLock<gst::DebugCategory> = LazyLock::new(|| {
|
||||||
gst::DebugCategory::new(
|
gst::DebugCategory::new(
|
||||||
|
@ -213,7 +213,7 @@ fn test_hlssink3_element_with_video_content() -> Result<(), ()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MessageView::Error(..) => unreachable!(),
|
MessageView::Error(err) => panic!("{err}"),
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -611,6 +611,7 @@ fn test_hlssink3_video_with_single_media_file() -> Result<(), ()> {
|
||||||
BUFFER_NB
|
BUFFER_NB
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let mut byte_ranges: Vec<ByteRange> = Vec::new();
|
||||||
let mut eos = false;
|
let mut eos = false;
|
||||||
let bus = pipeline.bus().unwrap();
|
let bus = pipeline.bus().unwrap();
|
||||||
while let Some(msg) = bus.timed_pop(gst::ClockTime::NONE) {
|
while let Some(msg) = bus.timed_pop(gst::ClockTime::NONE) {
|
||||||
|
@ -624,13 +625,16 @@ fn test_hlssink3_video_with_single_media_file() -> Result<(), ()> {
|
||||||
if let Some(structure) = msg.structure() {
|
if let Some(structure) = msg.structure() {
|
||||||
if structure.has_name("hls-segment-added") {
|
if structure.has_name("hls-segment-added") {
|
||||||
let location = structure.get::<String>("location").unwrap();
|
let location = structure.get::<String>("location").unwrap();
|
||||||
|
let byte_range = get_byte_ranges(structure);
|
||||||
|
byte_ranges.extend(byte_range);
|
||||||
|
|
||||||
hls_messages_sender
|
hls_messages_sender
|
||||||
.try_send(HlsSinkEvent::SegmentAddedMessage(location))
|
.try_send(HlsSinkEvent::SegmentAddedMessage(location))
|
||||||
.expect("Send segment added event");
|
.expect("Send segment added event");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MessageView::Error(..) => unreachable!(),
|
MessageView::Error(err) => panic!("{err}"),
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -648,9 +652,7 @@ fn test_hlssink3_video_with_single_media_file() -> Result<(), ()> {
|
||||||
actual_messages.push(event);
|
actual_messages.push(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
let contents = playlist_content.lock().unwrap();
|
eprintln!("Playlist byte ranges: {byte_ranges:?}");
|
||||||
|
|
||||||
let byte_ranges = extract_byteranges(contents.as_str());
|
|
||||||
// We only validate the byte range tag. The actual value of byte
|
// We only validate the byte range tag. The actual value of byte
|
||||||
// range can differ from each run and hence we do not validate
|
// range can differ from each run and hence we do not validate
|
||||||
// the entire playlist.
|
// the entire playlist.
|
||||||
|
|
Loading…
Reference in a new issue