Skip to content

Commit 59c8b4b

Browse files
committed
implemented bsf as a pluggable system
1 parent 1f0f8e9 commit 59c8b4b

File tree

3 files changed

+61
-76
lines changed

3 files changed

+61
-76
lines changed

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ crossbeam = "0.8"
2222
log = "0.4"
2323
env_logger = "0.11"
2424
parking_lot = "0.12"
25-
bitstream-io = "2.5"
2625

2726
[dependencies.ffmpeg-next]
2827
version = "7"

src/lib.rs

Lines changed: 47 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
use anyhow::bail;
22
use std::ffi::CString;
3-
use std::io::Cursor;
43
use std::path::Path;
54
use std::sync::Arc;
65
use std::thread::{spawn, JoinHandle};
76
use std::time::{Instant, SystemTime};
87

9-
use bitstream_io::{BigEndian, BitRead, BitReader};
108
use crossbeam::channel::{Receiver, Sender};
119
use derive_builder::Builder;
1210
use ffmpeg::util::frame::video::Video;
@@ -57,6 +55,27 @@ fn is_stream_key_framed(id: Id) -> Result<bool, String> {
5755
}
5856
}
5957

58+
#[pyclass]
59+
#[derive(Clone, Debug)]
60+
pub struct BsfFilter {
61+
codec: String,
62+
name: String,
63+
params: Vec<(String, String)>,
64+
}
65+
66+
#[pymethods]
67+
impl BsfFilter {
68+
#[new]
69+
#[pyo3(signature = (codec, name, params = vec![]))]
70+
fn new(codec: String, name: String, params: Vec<(String, String)>) -> Self {
71+
Self {
72+
codec,
73+
name,
74+
params,
75+
}
76+
}
77+
}
78+
6079
#[derive(Debug, Clone)]
6180
#[pyclass]
6281
pub struct VideoFrameEnvelope {
@@ -179,6 +198,7 @@ struct HandleParams {
179198
autoconvert_raw_formats_to_rgb24: bool,
180199
block_if_queue_full: bool,
181200
log_level: Arc<Mutex<Option<Level>>>,
201+
bsf_filters: Vec<BsfFilter>,
182202
}
183203

184204
struct BitStreamFilterContext {
@@ -324,36 +344,27 @@ fn handle(params: HandleParams) -> anyhow::Result<()> {
324344

325345
let mut video_filters = Vec::new();
326346

327-
match video_input.codec().id() {
328-
Id::H264 => {
329-
video_filters.push(init_bsf(
330-
"h264_mp4toannexb",
331-
&video_parameters,
332-
time_base,
333-
&[],
334-
)?);
335-
// video_filters.push(init_bsf(
336-
// "h264_metadata",
337-
// &video_parameters,
338-
// time_base,
339-
// &[("aud".into(), "insert".into())],
340-
// )?);
341-
}
342-
Id::HEVC | Id::H265 => {
343-
// video_filters.push(init_bsf(
344-
// "hevc_metadata",
345-
// &video_parameters,
346-
// time_base,
347-
// &[("aud".into(), "insert".into())],
348-
// )?);
349-
video_filters.push(init_bsf(
350-
"hevc_mp4toannexb",
351-
&video_parameters,
352-
time_base,
353-
&[],
354-
)?);
347+
let codec_name = video_input.codec().id().name();
348+
for f in &params.bsf_filters {
349+
if f.codec != codec_name {
350+
info!(
351+
"Skipping filter {} as it is not applicable to codec {}, must match {}",
352+
f.name, codec_name, f.codec
353+
);
354+
continue;
355355
}
356-
_ => {}
356+
357+
info!(
358+
"Initializing filter: {} with parameters {:?}",
359+
f.name, f.params
360+
);
361+
362+
video_filters.push(init_bsf(
363+
f.name.as_str(),
364+
&video_parameters,
365+
time_base,
366+
&f.params,
367+
)?);
357368
}
358369

359370
let mut video_decoder =
@@ -594,7 +605,8 @@ impl FFMpegSource {
594605
autoconvert_raw_formats_to_rgb24 = false,
595606
block_if_queue_full = false,
596607
init_timeout_ms = 10000,
597-
ffmpeg_log_level = FFmpegLogLevel::Info)
608+
ffmpeg_log_level = FFmpegLogLevel::Info,
609+
bsf_filters = vec![])
598610
)]
599611
pub fn new(
600612
uri: String,
@@ -605,6 +617,7 @@ impl FFMpegSource {
605617
block_if_queue_full: bool,
606618
init_timeout_ms: u64,
607619
ffmpeg_log_level: FFmpegLogLevel,
620+
bsf_filters: Vec<BsfFilter>,
608621
) -> PyResult<Self> {
609622
assert!(queue_len > 0, "Queue length must be a positive number");
610623

@@ -627,6 +640,7 @@ impl FFMpegSource {
627640
.autoconvert_raw_formats_to_rgb24(autoconvert_raw_formats_to_rgb24)
628641
.block_if_queue_full(block_if_queue_full)
629642
.log_level(log_level.clone())
643+
.bsf_filters(bsf_filters.clone())
630644
.build()
631645
.map_err(|e| {
632646
error!("Unable to create handle params. Error is: {:?}", e);
@@ -690,35 +704,6 @@ impl FFMpegSource {
690704
}
691705
}
692706

693-
// Function to decode Exp-Golomb codes from a slice and return a bit-string
694-
#[pyfunction]
695-
fn decode_exp_golomb(slice: &[u8]) -> String {
696-
let mut reader = BitReader::endian(Cursor::new(slice), BigEndian);
697-
let mut bit_string = String::new();
698-
699-
// Step 1: Count the number of leading zeros
700-
let mut leading_zeros = 0;
701-
while let Ok(bit) = reader.read_bit() {
702-
bit_string.push(if bit { '1' } else { '0' }); // Append bit to bit-string
703-
if !bit {
704-
leading_zeros += 1;
705-
} else {
706-
break; // Stop at the first '1'
707-
}
708-
}
709-
710-
// Step 2: Read the remainder of the code
711-
let mut code_value = 1; // The first '1' already encountered
712-
for _ in 0..leading_zeros {
713-
if let Ok(bit) = reader.read_bit() {
714-
bit_string.push(if bit { '1' } else { '0' }); // Append to bit-string
715-
code_value = (code_value << 1) | if bit { 1 } else { 0 };
716-
}
717-
}
718-
719-
bit_string
720-
}
721-
722707
#[pymodule]
723708
fn ffmpeg_input(_py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> {
724709
_ = env_logger::try_init_from_env("LOGLEVEL").map_err(|e| {
@@ -727,6 +712,6 @@ fn ffmpeg_input(_py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> {
727712
m.add_class::<VideoFrameEnvelope>()?;
728713
m.add_class::<FFMpegSource>()?;
729714
m.add_class::<FFmpegLogLevel>()?;
730-
m.add_function(wrap_pyfunction!(decode_exp_golomb, m)?)?;
715+
m.add_class::<BsfFilter>()?;
731716
Ok(())
732717
}

test.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@
22

33
os.environ["RUST_LOG"] = "info"
44

5-
from time import sleep
6-
7-
from ffmpeg_input import FFMpegSource, FFmpegLogLevel, decode_exp_golomb
5+
from ffmpeg_input import FFMpegSource, FFmpegLogLevel, BsfFilter
86

97

108
def bytes_to_bits_binary(byte_data):
@@ -14,14 +12,17 @@ def bytes_to_bits_binary(byte_data):
1412

1513
if __name__ == '__main__':
1614
# set env LOGLEVEL=info
17-
# file = "/home/ivan/Downloads/1_underground_supercut_reencode_bug_ab.mp4"
15+
file = "/home/ivan/Downloads/1_underground_supercut_reencode_bug.mp4"
1816
# file = "/home/ivan/Downloads/1_underground_supercut.mp4"
19-
file = "/home/ivan/Downloads/1_underground_supercut_reencode_bug_x265.mp4"
17+
# file = "/home/ivan/Downloads/1_underground_supercut_reencode_bug_x265.mp4"
2018
# file = "/home/ivan/Downloads/1_underground_supercut_reencode_bug_aud.mp4"
2119
s = FFMpegSource(file, params=[],
2220
queue_len=10, decode=False,
2321
block_if_queue_full=True,
24-
ffmpeg_log_level=FFmpegLogLevel.Info)
22+
ffmpeg_log_level=FFmpegLogLevel.Info,
23+
bsf_filters=[BsfFilter("h264", "h264_mp4toannexb"),
24+
BsfFilter("hevc", "hevc_mp4toannexb"),
25+
BsfFilter("h265", "hevc_mp4toannexb")])
2526
s.log_level = FFmpegLogLevel.Info
2627
# counter = 0
2728
while True:
@@ -36,13 +37,13 @@ def bytes_to_bits_binary(byte_data):
3637
payload = p.payload_as_bytes()
3738
print("Payload length:", len(payload))
3839
# print 1st 3 bytes of the payload
39-
bin_res = " ".join(format(x, '#010b')[2:] for x in payload[:16])
40-
first_hex_res = " ".join(format(x, '02x') for x in payload[:16])
41-
last_hex_res = " ".join(format(x, '02x') for x in payload[-4:])
42-
code = decode_exp_golomb(payload[:16])
43-
# int_val = int(code, 2)
44-
print("Payload bin start:", code)
45-
print("Payload hex start:", first_hex_res)
40+
# bin_res = " ".join(format(x, '#010b')[2:] for x in payload[:16])
41+
# first_hex_res = " ".join(format(x, '02x') for x in payload[:16])
42+
# last_hex_res = " ".join(format(x, '02x') for x in payload[-4:])
43+
# code = decode_exp_golomb(payload[:16])
44+
# # int_val = int(code, 2)
45+
# print("Payload bin start:", code)
46+
# print("Payload hex start:", first_hex_res)
4647
# if len(payload) - 4 > int_val:
4748
# first_hex_res = " ".join(format(x, '02x') for x in payload[4 + int_val:20 + int_val])
4849
# print("Payload hex start:", first_hex_res)

0 commit comments

Comments
 (0)