mirror of
https://github.com/euzu/tuliprox.git
synced 2026-10-25 01:02:29 +02:00
@@ -1,12 +1,10 @@
|
||||
use super::{
|
||||
clock::{
|
||||
add_pts_dts_offset, decode_pcr_at_location, ticks_90khz_to_rounded_millis, ts_chunk_packet_count,
|
||||
TS_PACKET_SIZE,
|
||||
},
|
||||
clock::{add_pts_dts_offset, decode_pcr_at_location, ticks_90khz_to_rounded_millis, ts_chunk_packet_count},
|
||||
layout::{build_hls_finite_ts_layout, find_ts_alignment, finite_hls_presentation_duration},
|
||||
profile::timestamp_profile_from_finite_layout,
|
||||
HlsTsTimestampProfile, TransportStreamBuffer,
|
||||
};
|
||||
use crate::packet::TS_PACKET_SIZE;
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use futures::task::AtomicWaker;
|
||||
use sha2::{Digest, Sha256};
|
||||
@@ -200,10 +198,10 @@ impl TransportStreamBuffer {
|
||||
let packet_layout =
|
||||
self.finite_hls_layout.as_ref().ok().and_then(|layout| layout.packets.get(current_pos)).copied();
|
||||
let packet_has_payload = packet_layout
|
||||
.map_or_else(|| matches!((packet[3] >> 4) & 0b11, 0b01 | 0b11), |layout| layout.has_payload);
|
||||
.map_or_else(|| crate::packet::TsPacket(packet).has_payload(), |layout| layout.has_payload);
|
||||
|
||||
// O(1) PID lookup — PID is at most 13 bits (0–8191).
|
||||
let pid = (u16::from(packet[1] & 0x1F) << 8) | u16::from(packet[2]);
|
||||
let pid = crate::packet::ts_packet_pid(packet);
|
||||
let entry = &mut self.cc_entries[pid as usize];
|
||||
// Normalize payload continuity per PID to a clean local sequence.
|
||||
let (counter, discontinuity_sent) = entry.get_or_insert((0, false));
|
||||
|
||||
@@ -36,9 +36,7 @@ pub(super) fn add_pcr_offset_27mhz(timestamp_27mhz: u64, offset_27mhz: u64) -> u
|
||||
((u128::from(timestamp_27mhz) + u128::from(offset_27mhz)) % u128::from(MAX_PCR)) as u64
|
||||
}
|
||||
|
||||
pub(super) const TS_PACKET_SIZE: usize = 188;
|
||||
|
||||
pub(super) const SYNC_BYTE: u8 = 0x47;
|
||||
use crate::packet::{TS_PACKET_SIZE, TS_SYNC_BYTE};
|
||||
|
||||
pub(super) const PACKET_COUNT: usize = 7;
|
||||
|
||||
@@ -58,11 +56,6 @@ pub(super) fn ts_chunk_packet_count() -> usize {
|
||||
*PACKET_COUNT_OVERRIDE
|
||||
}
|
||||
|
||||
pub(super) const ADAPTATION_FIELD_FLAG_PCR: u8 = 0x10;
|
||||
|
||||
// PCR flag bit in adaptation field flags
|
||||
pub(super) const NULL_PID: u16 = 0x1FFF;
|
||||
|
||||
pub(super) const HLS_TS_MAX_PENDING_PES_HEADERS: usize = 64;
|
||||
|
||||
pub(super) const HLS_TS_TIMESTAMP_HEADER_BYTES: usize = 19;
|
||||
@@ -97,7 +90,7 @@ pub(super) fn encode_timestamp(ts: u64) -> [u8; 5] {
|
||||
|
||||
/// Decode PCR from 6 bytes (adaptation field) into 42-bit PCR base + 9-bit extension as u64
|
||||
#[inline]
|
||||
pub(super) fn decode_pcr(pcr_bytes: &[u8]) -> u64 {
|
||||
pub(crate) fn decode_pcr(pcr_bytes: &[u8]) -> u64 {
|
||||
let pcr_base = (u64::from(pcr_bytes[0]) << 25)
|
||||
| ((u64::from(pcr_bytes[1])) << 17)
|
||||
| ((u64::from(pcr_bytes[2])) << 9)
|
||||
@@ -113,21 +106,18 @@ pub(super) enum HlsTsTimestampKind {
|
||||
Pcr,
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn ts_packet_pid(packet: &[u8]) -> u16 { (u16::from(packet[1] & 0x1F) << 8) | u16::from(packet[2]) }
|
||||
|
||||
pub(super) fn same_finite_ts_packet_layout(source: &[u8], prepared: &[u8]) -> bool {
|
||||
if source.len() != TS_PACKET_SIZE
|
||||
|| prepared.len() != TS_PACKET_SIZE
|
||||
|| source[0] != SYNC_BYTE
|
||||
|| prepared[0] != SYNC_BYTE
|
||||
|| source[0] != TS_SYNC_BYTE
|
||||
|| prepared[0] != TS_SYNC_BYTE
|
||||
|| source[1] & 0x7F != prepared[1] & 0x7F
|
||||
|| source[2] != prepared[2]
|
||||
|| source[3] & 0xF0 != prepared[3] & 0xF0
|
||||
{
|
||||
return false;
|
||||
}
|
||||
let adaptation_field_control = (source[3] >> 4) & 0b11;
|
||||
let adaptation_field_control = crate::packet::TsPacket(source).adaptation_field_control();
|
||||
!matches!(adaptation_field_control, 0b10 | 0b11) || source[4] == prepared[4]
|
||||
}
|
||||
|
||||
@@ -137,7 +127,7 @@ pub(super) fn append_finite_discontinuity_packet(following_packet: &[u8], output
|
||||
let start = output.len();
|
||||
output.resize(start.saturating_add(TS_PACKET_SIZE), 0xFF);
|
||||
let marker = &mut output[start..start + TS_PACKET_SIZE];
|
||||
marker[0] = SYNC_BYTE;
|
||||
marker[0] = TS_SYNC_BYTE;
|
||||
marker[1] = following_packet[1] & 0x1F;
|
||||
marker[2] = following_packet[2];
|
||||
// FFmpeg retains this adaptation-only marker as the per-PID CC baseline.
|
||||
|
||||
@@ -1,28 +1,17 @@
|
||||
use super::{
|
||||
clock::{decode_pcr_at_location, decode_timestamp_at_location, MAX_PTS_DTS, SYNC_BYTE, TS_PACKET_SIZE},
|
||||
clock::{decode_pcr_at_location, decode_timestamp_at_location, MAX_PTS_DTS},
|
||||
pes::{inspect_hls_ts_packet, HlsFiniteTsLayoutError},
|
||||
HlsFiniteTsLayout, HlsFiniteTsPacketLayout, HlsPesHeaderAssembler, HlsTsTimestampFieldKind,
|
||||
};
|
||||
use crate::packet::TS_PACKET_SIZE;
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
/// Finds TS alignment by checking for 0x47 sync byte every 188 bytes
|
||||
/// Finds TS alignment by checking for 0x47 sync byte every 188 bytes, five packets in a row.
|
||||
pub(super) fn find_ts_alignment(buf: &[u8]) -> Option<usize> {
|
||||
for offset in 0..TS_PACKET_SIZE {
|
||||
let mut valid = true;
|
||||
for i in 0..5 {
|
||||
if buf.get(offset + i * TS_PACKET_SIZE) != Some(&SYNC_BYTE) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if valid {
|
||||
return Some(offset);
|
||||
}
|
||||
}
|
||||
None
|
||||
crate::packet::find_sync(buf, 0..TS_PACKET_SIZE, 5, false)
|
||||
}
|
||||
|
||||
pub(super) fn build_hls_finite_ts_layout(buffer: &[u8]) -> Result<HlsFiniteTsLayout, HlsFiniteTsLayoutError> {
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
use super::{
|
||||
clock::{
|
||||
ts_packet_pid, ADAPTATION_FIELD_FLAG_PCR, HLS_TS_MAX_PENDING_PES_HEADERS, HLS_TS_TIMESTAMP_HEADER_BYTES,
|
||||
PES_DTS_OFFSET, PES_PTS_OFFSET, SYNC_BYTE, TS_PACKET_SIZE,
|
||||
},
|
||||
clock::{HLS_TS_MAX_PENDING_PES_HEADERS, HLS_TS_TIMESTAMP_HEADER_BYTES, PES_DTS_OFFSET, PES_PTS_OFFSET},
|
||||
HlsCompletedPesTimestamps, HlsCompletedTimestampField, HlsPendingPesHeader, HlsPesHeaderAssembler,
|
||||
HlsTsPacketEvidence,
|
||||
};
|
||||
use crate::packet::{ts_packet_pid, ADAPTATION_FIELD_FLAG_PCR, TS_PACKET_SIZE, TS_SYNC_BYTE};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
@@ -77,11 +75,11 @@ pub(super) fn inspect_hls_ts_packet(
|
||||
packet: &[u8],
|
||||
packet_start: usize,
|
||||
) -> Result<HlsTsPacketEvidence, HlsFiniteTsLayoutError> {
|
||||
if packet.len() != TS_PACKET_SIZE || packet[0] != SYNC_BYTE || packet[1] & 0x80 != 0 || packet[3] & 0xC0 != 0 {
|
||||
if packet.len() != TS_PACKET_SIZE || packet[0] != TS_SYNC_BYTE || packet[1] & 0x80 != 0 || packet[3] & 0xC0 != 0 {
|
||||
return Err(HlsFiniteTsLayoutError::InvalidTransportPacket);
|
||||
}
|
||||
let pid = ts_packet_pid(packet);
|
||||
let adaptation_field_control = (packet[3] >> 4) & 0b11;
|
||||
let adaptation_field_control = crate::packet::TsPacket(packet).adaptation_field_control();
|
||||
let continuity_counter = packet[3] & 0x0F;
|
||||
let (payload_offset, adaptation_length) = match adaptation_field_control {
|
||||
0b01 => (Some(4), None),
|
||||
|
||||
@@ -2,11 +2,12 @@ use super::{
|
||||
clock::{
|
||||
add_pts_dts_offset, decode_pcr, decode_pcr_at_location, decode_timestamp, decode_timestamp_at_location,
|
||||
forward_clock_distance_90khz, HlsTsTimestampKind, HLS_TS_PROFILE_MIN_TOLERANCE_TICKS_90KHZ,
|
||||
HLS_TS_SPLICE_MIN_GAP_TICKS_90KHZ, MAX_PTS_DTS, TS_PACKET_SIZE,
|
||||
HLS_TS_SPLICE_MIN_GAP_TICKS_90KHZ, MAX_PTS_DTS,
|
||||
},
|
||||
pes::inspect_hls_ts_packet,
|
||||
HlsFiniteTsLayout, HlsPesHeaderAssembler, HlsTsTimestampProfileAccumulator, HlsTsTimestampProfileScanner,
|
||||
};
|
||||
use crate::packet::TS_PACKET_SIZE;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub struct HlsTsTimestampProfile {
|
||||
|
||||
@@ -2,11 +2,12 @@ use super::{
|
||||
clock::{
|
||||
add_pcr_offset_27mhz, add_pts_dts_offset, append_finite_discontinuity_packet, decode_pcr_at_location,
|
||||
decode_timestamp, encode_pcr, encode_timestamp, gather_timestamp_bytes, pcr_offset_27mhz,
|
||||
same_finite_ts_packet_layout, ts_packet_pid, ADAPTATION_FIELD_FLAG_PCR, NULL_PID, SYNC_BYTE, TS_PACKET_SIZE,
|
||||
same_finite_ts_packet_layout,
|
||||
},
|
||||
pes::HlsFiniteTsLayoutError,
|
||||
HlsFiniteTsLayout, HlsFiniteTsPacketLayout, TransportStreamBuffer,
|
||||
};
|
||||
use crate::packet::{ts_packet_pid, NULL_PID, TS_PACKET_SIZE, TS_SYNC_BYTE};
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use std::task::Waker;
|
||||
|
||||
@@ -179,20 +180,12 @@ impl TransportStreamBuffer {
|
||||
out.resize(start + TS_PACKET_SIZE, 0xFF);
|
||||
let pkt = &mut out[start..start + TS_PACKET_SIZE];
|
||||
|
||||
pkt[0] = SYNC_BYTE;
|
||||
pkt[0] = TS_SYNC_BYTE;
|
||||
pkt[1] = new_packet[1] & 0x1F;
|
||||
pkt[2] = new_packet[2];
|
||||
|
||||
// Check if the current packet has a PCR (need at least 7 adaptation bytes: 1 flag + 6 PCR).
|
||||
let new_pkt_has_pcr = {
|
||||
let afc = (new_packet[3] >> 4) & 0b11;
|
||||
if afc == 2 || afc == 3 {
|
||||
let adaptation_len = new_packet[4] as usize;
|
||||
adaptation_len >= 7 && (new_packet[5] & ADAPTATION_FIELD_FLAG_PCR) != 0
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
let new_pkt_has_pcr = crate::packet::TsPacket(new_packet).has_pcr();
|
||||
|
||||
// AFC=2 (Adaptation Only), Scrambling=00 (Unscrambled), CC=cc
|
||||
pkt[3] = 0x20 | (cc & 0x0F);
|
||||
|
||||
@@ -2,18 +2,18 @@ use super::{
|
||||
clock::{
|
||||
append_finite_discontinuity_packet, decode_pcr, decode_pcr_at_location, decode_timestamp,
|
||||
decode_timestamp_at_location, encode_pcr, encode_timestamp, forward_clock_distance_90khz,
|
||||
gather_timestamp_bytes, pcr_offset_27mhz, ts_packet_pid, ADAPTATION_FIELD_FLAG_PCR,
|
||||
HLS_TS_SPLICE_MIN_GAP_TICKS_90KHZ, MAX_PTS_DTS, NULL_PID, SYNC_BYTE, TS_PACKET_SIZE,
|
||||
gather_timestamp_bytes, pcr_offset_27mhz, HLS_TS_SPLICE_MIN_GAP_TICKS_90KHZ, MAX_PTS_DTS,
|
||||
},
|
||||
layout::build_hls_finite_ts_layout,
|
||||
pes::inspect_hls_ts_packet,
|
||||
*,
|
||||
};
|
||||
use crate::packet::{ts_packet_pid, ADAPTATION_FIELD_FLAG_PCR, NULL_PID, TS_PACKET_SIZE, TS_SYNC_BYTE};
|
||||
use bytes::BytesMut;
|
||||
|
||||
fn build_pts_dts_payload_packet(pid: u16, cc: u8, pts: u64, dts: u64) -> [u8; TS_PACKET_SIZE] {
|
||||
let mut packet = [0xFF_u8; TS_PACKET_SIZE];
|
||||
packet[0] = SYNC_BYTE;
|
||||
packet[0] = TS_SYNC_BYTE;
|
||||
packet[1] = 0x40 | ((pid >> 8) as u8 & 0x1F); // PUSI + PID high bits
|
||||
packet[2] = (pid & 0xFF) as u8;
|
||||
packet[3] = 0x10 | (cc & 0x0F); // payload only
|
||||
@@ -42,7 +42,7 @@ fn build_pts_dts_payload_packet(pid: u16, cc: u8, pts: u64, dts: u64) -> [u8; TS
|
||||
|
||||
fn build_pts_only_payload_packet(pid: u16, cc: u8, pts: u64) -> [u8; TS_PACKET_SIZE] {
|
||||
let mut packet = [0xFF_u8; TS_PACKET_SIZE];
|
||||
packet[0] = SYNC_BYTE;
|
||||
packet[0] = TS_SYNC_BYTE;
|
||||
packet[1] = 0x40 | ((pid >> 8) as u8 & 0x1F);
|
||||
packet[2] = (pid & 0xFF) as u8;
|
||||
packet[3] = 0x10 | (cc & 0x0F);
|
||||
@@ -86,7 +86,7 @@ fn build_split_pes_header_packets(
|
||||
assert!((1..=184).contains(&payload_length));
|
||||
let mut packet = [0xFF_u8; TS_PACKET_SIZE];
|
||||
let packet_index = u8::try_from(packet_index).expect("test packet index fits");
|
||||
packet[0] = SYNC_BYTE;
|
||||
packet[0] = TS_SYNC_BYTE;
|
||||
packet[1] = ((pid >> 8) as u8 & 0x1F) | (u8::from(packet_index == 0) * 0x40);
|
||||
packet[2] = (pid & 0xFF) as u8;
|
||||
packet[3] = if payload_length == 184 {
|
||||
@@ -114,7 +114,7 @@ fn build_split_pes_header_packets(
|
||||
|
||||
fn build_pts_dts_pcr_packet(pid: u16, cc: u8, pts: u64, dts: u64, pcr_90khz: u64) -> [u8; TS_PACKET_SIZE] {
|
||||
let mut packet = [0xFF_u8; TS_PACKET_SIZE];
|
||||
packet[0] = SYNC_BYTE;
|
||||
packet[0] = TS_SYNC_BYTE;
|
||||
packet[1] = 0x40 | ((pid >> 8) as u8 & 0x1F);
|
||||
packet[2] = (pid & 0xFF) as u8;
|
||||
packet[3] = 0x30 | (cc & 0x0F);
|
||||
@@ -170,7 +170,7 @@ fn packet_timestamps(packet: &[u8]) -> (u64, u64, u64) {
|
||||
|
||||
fn build_adaptation_only_packet(pid: u16, cc: u8) -> [u8; TS_PACKET_SIZE] {
|
||||
let mut packet = [0xFF_u8; TS_PACKET_SIZE];
|
||||
packet[0] = SYNC_BYTE;
|
||||
packet[0] = TS_SYNC_BYTE;
|
||||
packet[1] = (pid >> 8) as u8 & 0x1F;
|
||||
packet[2] = (pid & 0xFF) as u8;
|
||||
packet[3] = 0x20 | (cc & 0x0F); // adaptation only
|
||||
@@ -188,7 +188,7 @@ fn build_pcr_only_packet(pid: u16, cc: u8, pcr_90khz: u64) -> [u8; TS_PACKET_SIZ
|
||||
|
||||
fn build_payload_packet(pid: u16, cc: u8) -> [u8; TS_PACKET_SIZE] {
|
||||
let mut packet = [0xFF_u8; TS_PACKET_SIZE];
|
||||
packet[0] = SYNC_BYTE;
|
||||
packet[0] = TS_SYNC_BYTE;
|
||||
packet[1] = (pid >> 8) as u8 & 0x1F;
|
||||
packet[2] = (pid & 0xFF) as u8;
|
||||
packet[3] = 0x10 | (cc & 0x0F);
|
||||
|
||||
Reference in New Issue
Block a user