Remove opaque pointers in pkts

This commit is contained in:
2024-08-29 11:46:57 +01:00
parent 759492d974
commit a64b54ba12
18 changed files with 250 additions and 396 deletions

View File

@ -4,7 +4,14 @@ use std::mem::transmute;
use std::ptr;
use anyhow::Error;
use ffmpeg_sys_next::{AV_CH_LAYOUT_STEREO, av_channel_layout_copy, av_dump_format, av_get_sample_fmt, av_interleaved_write_frame, av_opt_set, av_packet_clone, av_packet_copy_props, AVChannelLayout, AVChannelLayout__bindgen_ty_1, avcodec_find_encoder, avcodec_parameters_from_context, avcodec_parameters_to_context, AVCodecContext, avformat_alloc_output_context2, avformat_free_context, avformat_new_stream, avformat_write_header, AVFormatContext, AVPacket, AVRational};
use ffmpeg_sys_next::{
AV_CH_LAYOUT_STEREO, av_channel_layout_copy, av_dump_format, av_get_sample_fmt,
av_interleaved_write_frame, av_opt_set, av_packet_clone, av_packet_copy_props,
AVChannelLayout, AVChannelLayout__bindgen_ty_1, avcodec_find_encoder,
avcodec_parameters_from_context, avcodec_parameters_to_context, AVCodecContext, avformat_alloc_output_context2,
avformat_free_context, avformat_new_stream, avformat_write_header, AVFormatContext, AVPacket,
AVRational,
};
use ffmpeg_sys_next::AVChannelOrder::AV_CHANNEL_ORDER_NATIVE;
use ffmpeg_sys_next::AVColorSpace::AVCOL_SPC_BT709;
use ffmpeg_sys_next::AVMediaType::{AVMEDIA_TYPE_AUDIO, AVMEDIA_TYPE_VIDEO};
@ -16,10 +23,10 @@ use serde::{Deserialize, Serialize};
use tokio::sync::mpsc::UnboundedReceiver;
use uuid::Uuid;
use crate::egress::{EgressConfig, get_pkt_variant, map_variants_to_streams, update_pkt_for_muxer};
use crate::egress::{EgressConfig, map_variants_to_streams};
use crate::encode::dump_pkt_info;
use crate::pipeline::{PipelinePayload, PipelineProcessor};
use crate::utils::{get_ffmpeg_error_msg, id_ref_to_uuid};
use crate::pipeline::{AVPacketSource, PipelinePayload, PipelineProcessor};
use crate::utils::get_ffmpeg_error_msg;
use crate::variant::{VariantStream, VariantStreamType};
pub struct HlsEgress {
@ -150,9 +157,17 @@ impl HlsEgress {
Ok(())
}
unsafe fn process_pkt_internal(&mut self, pkt: *mut AVPacket) -> Result<(), Error> {
let variant = get_pkt_variant(&self.config.variants, pkt)?;
update_pkt_for_muxer(self.ctx, pkt, &variant);
unsafe fn process_pkt_internal(
&mut self,
pkt: *mut AVPacket,
src: &AVPacketSource,
) -> Result<(), Error> {
let variant = match src {
AVPacketSource::Encoder(v) => v,
_ => return Err(Error::msg(format!("Cannot mux packet from {:?}", src))),
};
(*pkt).stream_index = variant.dst_index() as libc::c_int;
//dump_pkt_info(pkt);
let ret = av_interleaved_write_frame(self.ctx, pkt);
if ret < 0 {
@ -161,20 +176,21 @@ impl HlsEgress {
Ok(())
}
unsafe fn process_pkt(&mut self, pkt: *mut AVPacket) -> Result<(), Error> {
let variant = get_pkt_variant(&self.config.variants, pkt)?;
if !self.stream_init.contains(&variant.dst_index()) {
let encoder_ctx = (*pkt).opaque as *mut AVCodecContext;
let out_stream = *(*self.ctx).streams.add(variant.dst_index());
avcodec_parameters_from_context((*out_stream).codecpar, encoder_ctx);
self.stream_init.insert(variant.dst_index());
}
unsafe fn process_pkt(
&mut self,
pkt: *mut AVPacket,
src: &AVPacketSource,
) -> Result<(), Error> {
let variant = match &src {
AVPacketSource::Encoder(v) => v,
_ => return Err(Error::msg(format!("Cannot mux packet from {:?}", src))),
};
if !self.init {
let pkt_clone = av_packet_clone(pkt);
av_packet_copy_props(pkt_clone, pkt);
self.packet_buffer.push_back(PipelinePayload::AvPacket(
"Buffered Muxer Packet".to_string(),
pkt_clone,
AVPacketSource::Muxer(variant.clone()),
));
}
@ -189,8 +205,8 @@ impl HlsEgress {
// push in pkts from buffer
while let Some(pkt) = self.packet_buffer.pop_front() {
match pkt {
PipelinePayload::AvPacket(_, pkt) => {
self.process_pkt_internal(pkt)?;
PipelinePayload::AvPacket(pkt, ref src) => {
self.process_pkt_internal(pkt, src)?;
}
_ => return Err(Error::msg("")),
}
@ -200,7 +216,7 @@ impl HlsEgress {
return Ok(());
}
self.process_pkt_internal(pkt)
self.process_pkt_internal(pkt, src)
}
}
@ -208,13 +224,20 @@ impl PipelineProcessor for HlsEgress {
fn process(&mut self) -> Result<(), Error> {
while let Ok(pkg) = self.chan_in.try_recv() {
match pkg {
PipelinePayload::AvPacket(_, pkt) => unsafe {
PipelinePayload::AvPacket(pkt, ref src) => unsafe {
self.process_pkt(pkt, src)?;
},
PipelinePayload::EncoderInfo(ref var, ctx) => unsafe {
if self.ctx.is_null() {
self.setup_muxer()?;
}
self.process_pkt(pkt)?;
if !self.stream_init.contains(&var.dst_index()) {
let out_stream = *(*self.ctx).streams.add(var.dst_index());
avcodec_parameters_from_context((*out_stream).codecpar, ctx);
self.stream_init.insert(var.dst_index());
}
},
_ => return Err(Error::msg("Payload not supported")),
_ => return Err(Error::msg(format!("Payload not supported: {:?}", pkg))),
}
}
Ok(())

View File

@ -2,15 +2,13 @@ use std::fmt::{Display, Formatter};
use std::ptr;
use anyhow::Error;
use ffmpeg_sys_next::{av_packet_rescale_ts, avformat_new_stream, AVFormatContext, AVPacket};
use ffmpeg_sys_next::{avformat_new_stream, AVFormatContext};
use serde::{Deserialize, Serialize};
use crate::utils::id_ref_to_uuid;
use crate::variant::{VariantStream, VariantStreamType};
pub mod hls;
pub mod http;
pub mod mpegts;
pub mod recorder;
#[derive(Clone, Debug, Serialize, Deserialize)]
@ -62,36 +60,4 @@ pub unsafe fn map_variants_to_streams(
}
}
Ok(())
}
/// Get variant of this packet
pub unsafe fn get_pkt_variant(
vars: &Vec<VariantStream>,
pkt: *mut AVPacket,
) -> Result<&VariantStream, Error> {
let variant_id = id_ref_to_uuid((*pkt).opaque_ref)?;
let variant = vars.iter().find(|v| v.id() == variant_id);
if variant.is_none() {
return Err(Error::msg(format!(
"No stream found with id={:?}",
variant_id
)));
}
Ok(variant.unwrap())
}
/// Update packet stream index to match muxer stream
pub unsafe fn update_pkt_for_muxer(
ctx: *mut AVFormatContext,
pkt: *mut AVPacket,
var: &VariantStream,
) {
let stream = *(*ctx).streams.add(var.dst_index());
let idx = (*stream).index;
if idx != (*pkt).stream_index {
(*pkt).stream_index = idx;
}
// match stream timebase in muxer
av_packet_rescale_ts(pkt, var.time_base(), (*stream).time_base);
(*pkt).time_base = (*stream).time_base;
}
}

View File

@ -1,118 +0,0 @@
use std::{fs, ptr};
use std::collections::HashSet;
use std::fmt::Display;
use anyhow::Error;
use ffmpeg_sys_next::{av_guess_format, av_interleaved_write_frame, av_strdup, avcodec_parameters_from_context, AVCodecContext, avformat_alloc_context, avformat_free_context, avformat_write_header, AVFormatContext, AVIO_FLAG_READ_WRITE, avio_open2, AVPacket};
use itertools::Itertools;
use tokio::sync::mpsc::UnboundedReceiver;
use uuid::Uuid;
use crate::egress::{EgressConfig, get_pkt_variant, map_variants_to_streams, update_pkt_for_muxer};
use crate::pipeline::{PipelinePayload, PipelineProcessor};
use crate::utils::get_ffmpeg_error_msg;
use crate::variant::VariantStreamType;
pub struct MPEGTSEgress {
id: Uuid,
config: EgressConfig,
ctx: *mut AVFormatContext,
chan_in: UnboundedReceiver<PipelinePayload>,
stream_init: HashSet<i32>,
}
unsafe impl Send for MPEGTSEgress {}
unsafe impl Sync for MPEGTSEgress {}
impl Drop for MPEGTSEgress {
fn drop(&mut self) {
unsafe {
avformat_free_context(self.ctx);
self.ctx = ptr::null_mut();
}
}
}
impl MPEGTSEgress {
pub fn new(
chan_in: UnboundedReceiver<PipelinePayload>,
id: Uuid,
config: EgressConfig,
) -> Self {
Self {
id,
config,
ctx: ptr::null_mut(),
chan_in,
stream_init: HashSet::new(),
}
}
unsafe fn setup_muxer(&mut self) -> Result<(), Error> {
let mut ctx = avformat_alloc_context();
if ctx.is_null() {
return Err(Error::msg("Failed to create muxer context"));
}
let base = format!("{}/{}", self.config.out_dir, self.id);
fs::create_dir_all(base.clone())?;
let ret = avio_open2(
&mut (*ctx).pb,
format!("{}/live.ts\0", base).as_ptr() as *const libc::c_char,
AVIO_FLAG_READ_WRITE,
ptr::null(),
ptr::null_mut(),
);
if ret < 0 {
return Err(Error::msg(get_ffmpeg_error_msg(ret)));
}
(*ctx).oformat = av_guess_format(
"mpegts\0".as_ptr() as *const libc::c_char,
ptr::null(),
ptr::null(),
);
if (*ctx).oformat.is_null() {
return Err(Error::msg("Output format not found"));
}
(*ctx).url = av_strdup(format!("{}/live.ts\0", base).as_ptr() as *const libc::c_char);
map_variants_to_streams(ctx, &mut self.config.variants)?;
let ret = avformat_write_header(ctx, ptr::null_mut());
if ret < 0 {
return Err(Error::msg(get_ffmpeg_error_msg(ret)));
}
self.ctx = ctx;
Ok(())
}
unsafe fn process_pkt(&mut self, pkt: *mut AVPacket) -> Result<(), Error> {
let variant = get_pkt_variant(&self.config.variants, pkt)?;
update_pkt_for_muxer(self.ctx, pkt, &variant);
let ret = av_interleaved_write_frame(self.ctx, pkt);
if ret < 0 {
return Err(Error::msg(get_ffmpeg_error_msg(ret)));
}
Ok(())
}
}
impl PipelineProcessor for MPEGTSEgress {
fn process(&mut self) -> Result<(), Error> {
while let Ok(pkg) = self.chan_in.try_recv() {
match pkg {
PipelinePayload::AvPacket(_, pkt) => unsafe {
if self.ctx.is_null() {
self.setup_muxer()?;
}
self.process_pkt(pkt)?;
},
_ => return Err(Error::msg("Payload not supported")),
}
}
Ok(())
}
}

View File

@ -1,19 +1,19 @@
use std::{fs, ptr};
use std::collections::HashSet;
use std::fmt::Display;
use std::{fs, ptr};
use anyhow::Error;
use ffmpeg_sys_next::{
av_dump_format, av_guess_format, av_interleaved_write_frame, av_strdup, avformat_alloc_context
, avformat_free_context,
AVFormatContext, AVIO_FLAG_READ_WRITE, avio_open2, AVPacket,
av_dump_format, av_guess_format, av_interleaved_write_frame, av_strdup, avformat_alloc_context,
avformat_free_context, avio_open2, AVFormatContext, AVPacket, AVIO_FLAG_READ_WRITE,
};
use tokio::sync::mpsc::UnboundedReceiver;
use uuid::Uuid;
use crate::egress::{EgressConfig, get_pkt_variant, map_variants_to_streams, update_pkt_for_muxer};
use crate::pipeline::{PipelinePayload, PipelineProcessor};
use crate::egress::{map_variants_to_streams, EgressConfig};
use crate::pipeline::{AVPacketSource, PipelinePayload, PipelineProcessor};
use crate::utils::get_ffmpeg_error_msg;
use crate::variant::VariantStreamType;
pub struct RecorderEgress {
id: Uuid,
@ -90,9 +90,16 @@ impl RecorderEgress {
Ok(())
}
unsafe fn process_pkt(&mut self, pkt: *mut AVPacket) -> Result<(), Error> {
let variant = get_pkt_variant(&self.config.variants, pkt)?;
update_pkt_for_muxer(self.ctx, pkt, &variant);
unsafe fn process_pkt(
&mut self,
pkt: *mut AVPacket,
src: &AVPacketSource,
) -> Result<(), Error> {
let variant = match src {
AVPacketSource::Encoder(v) => v,
_ => return Err(Error::msg(format!("Cannot mux packet from {:?}", src))),
};
(*pkt).stream_index = variant.dst_index() as libc::c_int;
let ret = av_interleaved_write_frame(self.ctx, pkt);
if ret < 0 {
@ -107,11 +114,11 @@ impl PipelineProcessor for RecorderEgress {
fn process(&mut self) -> Result<(), Error> {
while let Ok(pkg) = self.chan_in.try_recv() {
match pkg {
PipelinePayload::AvPacket(_, pkt) => unsafe {
PipelinePayload::AvPacket(pkt, ref src) => unsafe {
if self.ctx.is_null() {
self.setup_muxer()?;
}
self.process_pkt(pkt)?;
self.process_pkt(pkt, src)?;
},
_ => return Err(Error::msg("Payload not supported")),
}