Audio codec libfdk_aac

This commit is contained in:
2024-03-27 16:33:43 +00:00
parent 298bfd994b
commit 9c4969cf95
16 changed files with 549 additions and 127 deletions

View File

@ -7,6 +7,7 @@ use ffmpeg_sys_next::{
avcodec_find_decoder, avcodec_free_context, avcodec_open2, avcodec_parameters_to_context,
avcodec_receive_frame, avcodec_send_packet, AVCodecContext, AVERROR, AVERROR_EOF, AVPacket, AVStream,
};
use ffmpeg_sys_next::AVPictureType::AV_PICTURE_TYPE_NONE;
use tokio::sync::broadcast;
use tokio::sync::mpsc::UnboundedReceiver;
@ -99,7 +100,9 @@ impl Decoder {
}
return Err(Error::msg(format!("Failed to decode {}", ret)));
}
(*frame).time_base = (*stream).time_base;
// reset picture type, not to confuse the encoder
(*frame).pict_type = AV_PICTURE_TYPE_NONE;
(*frame).opaque = stream as *mut libc::c_void;
self.chan_out.send(PipelinePayload::AvFrame(
"Decoder frame".to_owned(),
frame,

View File

@ -4,7 +4,7 @@ use std::mem::transmute;
use std::ptr;
use anyhow::Error;
use ffmpeg_sys_next::{AV_CH_LAYOUT_STEREO, av_dump_format, av_get_sample_fmt, av_interleaved_write_frame, av_opt_set, AVChannelLayout, AVChannelLayout__bindgen_ty_1, avcodec_parameters_from_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_dump_format, av_get_sample_fmt, av_interleaved_write_frame, av_opt_set, AVChannelLayout, AVChannelLayout__bindgen_ty_1, avcodec_find_encoder, avcodec_parameters_from_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};
@ -145,7 +145,6 @@ impl HlsEgress {
);
for var in &mut self.config.variants {
let tb = var.time_base();
match var {
VariantStream::Video(vs) => {
let stream = avformat_new_stream(ctx, ptr::null());
@ -155,22 +154,8 @@ impl HlsEgress {
// overwrite dst_index to match output stream
vs.dst_index = (*stream).index as usize;
(*stream).time_base = tb;
let params = (*stream).codecpar;
(*params).height = vs.height as libc::c_int;
(*params).width = vs.width as libc::c_int;
(*params).codec_id = transmute(vs.codec as i32);
(*params).codec_type = AVMEDIA_TYPE_VIDEO;
(*params).format = AV_PIX_FMT_YUV420P as i32;
(*params).framerate = AVRational {
num: 1,
den: vs.fps as libc::c_int,
};
(*params).bit_rate = vs.bitrate as i64;
(*params).color_space = AVCOL_SPC_BT709;
(*params).level = vs.level as libc::c_int;
(*params).profile = vs.profile as libc::c_int;
vs.to_stream(stream);
vs.to_codec_params((*stream).codecpar);
}
VariantStream::Audio(va) => {
let stream = avformat_new_stream(ctx, ptr::null());
@ -180,25 +165,8 @@ impl HlsEgress {
// overwrite dst_index to match output stream
va.dst_index = (*stream).index as usize;
(*stream).time_base = tb;
let params = (*stream).codecpar;
(*params).codec_id = transmute(va.codec as i32);
(*params).codec_type = AVMEDIA_TYPE_AUDIO;
(*params).format = av_get_sample_fmt(
format!("{}\0", va.sample_fmt).as_ptr() as *const libc::c_char
) as libc::c_int;
(*params).bit_rate = va.bitrate as i64;
(*params).sample_rate = va.sample_rate as libc::c_int;
(*params).ch_layout = AVChannelLayout {
order: AV_CHANNEL_ORDER_NATIVE,
nb_channels: 2,
u: AVChannelLayout__bindgen_ty_1 {
mask: AV_CH_LAYOUT_STEREO,
},
opaque: ptr::null_mut(),
};
va.to_stream(stream);
va.to_codec_params((*stream).codecpar);
}
}
}

18
src/egress/http.rs Normal file
View File

@ -0,0 +1,18 @@
use std::net::SocketAddr;
use anyhow::Error;
use warp::{cors, Filter};
use crate::settings::Settings;
pub async fn listen_out_dir(addr: String, settings: Settings) -> Result<(), Error> {
let addr: SocketAddr = addr.parse()?;
let cors = cors().allow_any_origin().allow_methods(vec!["GET"]);
let warp_out = warp::get()
.and(warp::fs::dir(settings.output_dir.clone()))
.with(cors);
warp::serve(warp_out).run(addr).await;
Ok(())
}

View File

@ -1 +1,2 @@
pub mod hls;
pub mod hls;
pub mod http;

View File

@ -5,18 +5,16 @@ use anyhow::Error;
use ffmpeg_sys_next::{
av_audio_fifo_alloc, av_audio_fifo_free, av_audio_fifo_read, av_audio_fifo_realloc,
av_audio_fifo_size, av_audio_fifo_write, av_buffer_ref, av_buffer_unref,
AV_CH_LAYOUT_STEREO, av_channel_layout_copy, av_frame_alloc, av_frame_free, av_frame_get_buffer,
av_freep, av_get_sample_fmt, av_packet_alloc, av_packet_free,
av_packet_rescale_ts, av_samples_alloc_array_and_samples, AVAudioFifo,
AVBufferRef, AVChannelLayout, AVChannelLayout__bindgen_ty_1, AVCodec,
avcodec_alloc_context3, avcodec_find_encoder, avcodec_free_context, avcodec_open2, avcodec_receive_packet, avcodec_send_frame,
AVCodecContext, AVERROR, AVFrame, swr_alloc_set_opts2, swr_convert, swr_free,
swr_init, SwrContext,
av_channel_layout_copy, av_frame_alloc, av_frame_free, av_frame_get_buffer, av_freep,
av_packet_alloc, av_packet_free, av_samples_alloc_array_and_samples, AVAudioFifo,
AVBufferRef, AVCodec, avcodec_alloc_context3, avcodec_free_context,
avcodec_open2, avcodec_receive_packet, avcodec_send_frame, AVCodecContext, AVERROR, AVFrame, swr_alloc_set_opts2,
swr_convert, swr_free, swr_init, SwrContext,
};
use ffmpeg_sys_next::AVChannelOrder::AV_CHANNEL_ORDER_NATIVE;
use libc::EAGAIN;
use tokio::sync::mpsc::UnboundedSender;
use crate::encode::set_encoded_pkt_timing;
use crate::ipc::Rx;
use crate::pipeline::{PipelinePayload, PipelineProcessor};
use crate::utils::{audio_variant_id_ref, get_ffmpeg_error_msg, id_ref_to_uuid};
@ -49,8 +47,8 @@ impl<T> Drop for AudioEncoder<T> {
}
impl<TRecv> AudioEncoder<TRecv>
where
TRecv: Rx<PipelinePayload>,
where
TRecv: Rx<PipelinePayload>,
{
pub fn new(
chan_in: TRecv,
@ -72,8 +70,7 @@ impl<TRecv> AudioEncoder<TRecv>
unsafe fn setup_encoder(&mut self, frame: *mut AVFrame) -> Result<(), Error> {
if self.ctx.is_null() {
let codec = self.variant.codec;
let encoder = avcodec_find_encoder(transmute(codec as i32));
let encoder = self.variant.get_codec();
if encoder.is_null() {
return Err(Error::msg("Encoder not found"));
}
@ -83,20 +80,7 @@ impl<TRecv> AudioEncoder<TRecv>
return Err(Error::msg("Failed to allocate encoder context"));
}
(*ctx).time_base = self.variant.time_base();
(*ctx).sample_fmt = av_get_sample_fmt(
format!("{}\0", self.variant.sample_fmt).as_ptr() as *const libc::c_char,
);
(*ctx).bit_rate = self.variant.bitrate as i64;
(*ctx).sample_rate = self.variant.sample_rate as libc::c_int;
(*ctx).ch_layout = AVChannelLayout {
order: AV_CHANNEL_ORDER_NATIVE,
nb_channels: 2,
u: AVChannelLayout__bindgen_ty_1 {
mask: AV_CH_LAYOUT_STEREO,
},
opaque: ptr::null_mut(),
};
self.variant.to_codec_context(ctx);
// setup audio FIFO
let fifo = av_audio_fifo_alloc((*ctx).sample_fmt, 2, 1);
@ -220,13 +204,17 @@ impl<TRecv> AudioEncoder<TRecv>
assert_eq!(var_id, self.variant.id);
self.setup_encoder(frame)?;
if !self.process_audio_frame(frame)? {
return Ok(());
}
// read audio from FIFO
let frame = self.get_fifo_frame()?;
let fifo_frame = self.get_fifo_frame()?;
// copy pointer to input stream
(*fifo_frame).opaque = (*frame).opaque;
let frame = fifo_frame;
let mut ret = avcodec_send_frame(self.ctx, frame);
if ret < 0 && ret != AVERROR(EAGAIN) {
return Err(Error::msg(get_ffmpeg_error_msg(ret)));
@ -243,9 +231,7 @@ impl<TRecv> AudioEncoder<TRecv>
return Err(Error::msg(get_ffmpeg_error_msg(ret)));
}
(*pkt).time_base = (*self.ctx).time_base;
(*pkt).duration = (*frame).duration;
av_packet_rescale_ts(pkt, (*frame).time_base, (*self.ctx).time_base);
set_encoded_pkt_timing(self.ctx, pkt, frame);
(*pkt).opaque = self.ctx as *mut libc::c_void;
(*pkt).opaque_ref = av_buffer_ref(self.var_id_ref);
self.chan_out
@ -257,8 +243,8 @@ impl<TRecv> AudioEncoder<TRecv>
}
impl<TRecv> PipelineProcessor for AudioEncoder<TRecv>
where
TRecv: Rx<PipelinePayload>,
where
TRecv: Rx<PipelinePayload>,
{
fn process(&mut self) -> Result<(), Error> {
while let Ok(pkg) = self.chan_in.try_recv_next() {

View File

@ -1,2 +1,22 @@
use ffmpeg_sys_next::{av_packet_rescale_ts, AVCodecContext, AVFrame, AVPacket, AVStream};
use ffmpeg_sys_next::AVMediaType::AVMEDIA_TYPE_VIDEO;
pub mod audio;
pub mod video;
pub mod video;
/// Set packet details based on decoded frame
pub unsafe fn set_encoded_pkt_timing(
ctx: *mut AVCodecContext,
pkt: *mut AVPacket,
in_frame: *mut AVFrame,
) {
assert!(!(*in_frame).opaque.is_null());
let in_stream = (*in_frame).opaque as *mut AVStream;
let tb = (*ctx).time_base;
(*pkt).stream_index = (*in_stream).index;
if (*ctx).codec_type == AVMEDIA_TYPE_VIDEO {
(*pkt).duration = tb.den as i64 / tb.num as i64 / (*in_stream).avg_frame_rate.num as i64
* (*in_stream).avg_frame_rate.den as i64;
}
av_packet_rescale_ts(pkt, (*in_stream).time_base, (*ctx).time_base);
}

View File

@ -3,14 +3,13 @@ use std::ptr;
use anyhow::Error;
use ffmpeg_sys_next::{
av_buffer_ref, av_opt_set, av_packet_alloc, av_packet_free, av_packet_rescale_ts,
AVBufferRef, AVCodec, avcodec_alloc_context3, avcodec_find_encoder,
avcodec_open2, avcodec_receive_packet, avcodec_send_frame, AVCodecContext, AVERROR, AVFrame, AVRational,
av_buffer_ref, av_packet_alloc, av_packet_free, av_packet_rescale_ts, avcodec_alloc_context3,
avcodec_find_encoder, avcodec_open2, avcodec_receive_packet, avcodec_send_frame, AVBufferRef,
AVCodec, AVCodecContext, AVFrame, AVStream, AVERROR,
};
use ffmpeg_sys_next::AVColorSpace::AVCOL_SPC_BT709;
use ffmpeg_sys_next::AVPixelFormat::AV_PIX_FMT_YUV420P;
use libc::EAGAIN;
use tokio::sync::mpsc::UnboundedSender;
use crate::encode::set_encoded_pkt_timing;
use crate::ipc::Rx;
use crate::pipeline::{PipelinePayload, PipelineProcessor};
@ -31,8 +30,8 @@ unsafe impl<T> Send for VideoEncoder<T> {}
unsafe impl<T> Sync for VideoEncoder<T> {}
impl<TRecv> VideoEncoder<TRecv>
where
TRecv: Rx<PipelinePayload>,
where
TRecv: Rx<PipelinePayload>,
{
pub fn new(
chan_in: TRecv,
@ -63,28 +62,7 @@ impl<TRecv> VideoEncoder<TRecv>
return Err(Error::msg("Failed to allocate encoder context"));
}
(*ctx).time_base = self.variant.time_base();
(*ctx).bit_rate = self.variant.bitrate as i64;
(*ctx).width = (*frame).width;
(*ctx).height = (*frame).height;
(*ctx).level = self.variant.level as libc::c_int;
(*ctx).profile = self.variant.profile as libc::c_int;
(*ctx).framerate = AVRational {
num: 1,
den: self.variant.fps as libc::c_int,
};
let key_frames = self.variant.fps * self.variant.keyframe_interval;
(*ctx).gop_size = key_frames as libc::c_int;
(*ctx).max_b_frames = 1;
(*ctx).pix_fmt = AV_PIX_FMT_YUV420P;
(*ctx).colorspace = AVCOL_SPC_BT709;
av_opt_set(
(*ctx).priv_data,
"preset\0".as_ptr() as *const libc::c_char,
"fast\0".as_ptr() as *const libc::c_char,
0,
);
self.variant.to_codec_context(ctx);
let ret = avcodec_open2(ctx, encoder, ptr::null_mut());
if ret < 0 {
@ -119,9 +97,7 @@ impl<TRecv> VideoEncoder<TRecv>
return Err(Error::msg(get_ffmpeg_error_msg(ret)));
}
(*pkt).time_base = (*self.ctx).time_base;
(*pkt).duration = (*frame).duration;
av_packet_rescale_ts(pkt, (*frame).time_base, (*self.ctx).time_base);
set_encoded_pkt_timing(self.ctx, pkt, frame);
(*pkt).opaque = self.ctx as *mut libc::c_void;
(*pkt).opaque_ref = av_buffer_ref(self.var_id_ref);
self.chan_out
@ -133,8 +109,8 @@ impl<TRecv> VideoEncoder<TRecv>
}
impl<TRecv> PipelineProcessor for VideoEncoder<TRecv>
where
TRecv: Rx<PipelinePayload>,
where
TRecv: Rx<PipelinePayload>,
{
fn process(&mut self) -> Result<(), Error> {
while let Ok(pkg) = self.chan_in.try_recv_next() {

View File

@ -4,6 +4,7 @@ use config::Config;
use log::{error, info};
use url::Url;
use crate::egress::http::listen_out_dir;
use crate::pipeline::builder::PipelineBuilder;
use crate::settings::Settings;
use crate::webhook::Webhook;
@ -14,14 +15,14 @@ mod egress;
mod encode;
mod fraction;
mod ingress;
mod ipc;
mod pipeline;
mod scale;
mod settings;
mod tag_frame;
mod utils;
mod variant;
mod webhook;
mod ipc;
mod tag_frame;
/// Test: ffmpeg -re -f lavfi -i testsrc -g 2 -r 30 -pix_fmt yuv420p -s 1280x720 -c:v h264 -b:v 2000k -f mpegts srt://localhost:3333
#[tokio::main]
@ -48,7 +49,7 @@ async fn main() -> anyhow::Result<()> {
let webhook = Webhook::new(settings.clone());
let builder = PipelineBuilder::new(webhook);
let mut listeners = vec![];
for e in settings.endpoints {
for e in &settings.endpoints {
let u: Url = e.parse()?;
let addr = format!("{}:{}", u.host_str().unwrap(), u.port().unwrap());
match u.scheme() {
@ -59,6 +60,11 @@ async fn main() -> anyhow::Result<()> {
}
}
}
listeners.push(tokio::spawn(listen_out_dir(
"0.0.0.0:8080".to_owned(),
settings.clone(),
)));
for handle in listeners {
if let Err(e) = handle.await {
error!("{e}");

View File

@ -2,7 +2,7 @@ use std::ops::Add;
use std::time::{Duration, Instant};
use anyhow::Error;
use log::info;
use log::{info, warn};
use tokio::sync::broadcast;
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver};
@ -59,7 +59,7 @@ impl PipelineRunner {
}
pub fn run(&mut self) -> Result<(), Error> {
if let Some(info) = &self.stream_info {
/*if let Some(info) = &self.stream_info {
if let Some(v_stream) = info
.channels
.iter()
@ -73,7 +73,7 @@ impl PipelineRunner {
std::thread::sleep(poll_sleep);
}
}
}
}*/
if let Some(cfg) = self.demuxer.process()? {
self.configure_pipeline(cfg)?;
}

View File

@ -57,7 +57,7 @@ impl Scaler {
let ctx = sws_getContext(
(*frame).width,
(*frame).height,
dst_fmt,
transmute((*frame).format),
self.variant.width as libc::c_int,
self.variant.height as libc::c_int,
dst_fmt,
@ -83,6 +83,7 @@ impl Scaler {
return Err(Error::msg(get_ffmpeg_error_msg(ret)));
}
(*dst_frame).opaque = (*frame).opaque;
(*dst_frame).opaque_ref = av_buffer_ref(self.var_id_ref);
self.chan_out.send(PipelinePayload::AvFrame(

View File

@ -48,6 +48,7 @@ impl<TRecv> PipelineProcessor for TagFrame<TRecv>
if idx == self.variant.src_index() {
let new_frame = av_frame_clone(frm);
av_frame_copy_props(new_frame, frm);
(*new_frame).opaque = (*frm).opaque;
(*new_frame).opaque_ref = av_buffer_ref(self.var_id_ref);
self.chan_out
.send(PipelinePayload::AvFrame(tag.clone(), new_frame, idx))?;

View File

@ -1,8 +1,18 @@
use std::ffi::CStr;
use std::fmt::{Display, Formatter};
use std::mem::transmute;
use std::ptr;
use ffmpeg_sys_next::{avcodec_get_name, AVRational};
use ffmpeg_sys_next::AVChannelOrder::AV_CHANNEL_ORDER_NATIVE;
use ffmpeg_sys_next::AVCodecID::{AV_CODEC_ID_AAC, AV_CODEC_ID_H264};
use ffmpeg_sys_next::AVColorSpace::AVCOL_SPC_BT709;
use ffmpeg_sys_next::AVPixelFormat::AV_PIX_FMT_YUV420P;
use ffmpeg_sys_next::{
av_get_sample_fmt, av_opt_set, avcodec_find_encoder, avcodec_find_encoder_by_name,
avcodec_get_name, AVChannelLayout, AVChannelLayout__bindgen_ty_1, AVCodec, AVCodecContext,
AVCodecParameters, AVRational, AVStream, AV_CH_LAYOUT_STEREO,
};
use ffmpeg_sys_next::AVColorRange::{AVCOL_RANGE_JPEG, AVCOL_RANGE_MPEG};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
@ -162,6 +172,77 @@ impl VideoVariant {
den: 90_000,
}
}
pub fn get_codec(&self) -> *const AVCodec {
unsafe { avcodec_find_encoder(transmute(self.codec as u32)) }
}
pub unsafe fn to_codec_context(&self, ctx: *mut AVCodecContext) {
let codec = self.get_codec();
(*ctx).codec_id = (*codec).id;
(*ctx).codec_type = (*codec).type_;
(*ctx).time_base = self.time_base();
(*ctx).bit_rate = self.bitrate as i64;
(*ctx).width = self.width as libc::c_int;
(*ctx).height = self.height as libc::c_int;
(*ctx).level = self.level as libc::c_int;
(*ctx).profile = self.profile as libc::c_int;
(*ctx).framerate = AVRational {
num: self.fps as libc::c_int,
den: 1,
};
let key_frames = self.fps * self.keyframe_interval;
(*ctx).gop_size = key_frames as libc::c_int;
(*ctx).keyint_min = key_frames as libc::c_int;
(*ctx).max_b_frames = 1;
(*ctx).pix_fmt = AV_PIX_FMT_YUV420P;
(*ctx).colorspace = AVCOL_SPC_BT709;
(*ctx).color_range = AVCOL_RANGE_MPEG;
if (*codec).id == AV_CODEC_ID_H264 {
av_opt_set(
(*ctx).priv_data,
"preset\0".as_ptr() as *const libc::c_char,
"fast\0".as_ptr() as *const libc::c_char,
0,
);
av_opt_set(
(*ctx).priv_data,
"tune\0".as_ptr() as *const libc::c_char,
"zerolatency\0".as_ptr() as *const libc::c_char,
0,
);
}
}
pub unsafe fn to_codec_params(&self, params: *mut AVCodecParameters) {
let codec = self.get_codec();
(*params).codec_id = (*codec).id;
(*params).codec_type = (*codec).type_;
(*params).height = self.height as libc::c_int;
(*params).width = self.width as libc::c_int;
(*params).format = AV_PIX_FMT_YUV420P as i32;
(*params).framerate = AVRational {
num: self.fps as libc::c_int,
den: 1,
};
(*params).bit_rate = self.bitrate as i64;
(*params).color_space = AVCOL_SPC_BT709;
(*params).level = self.level as libc::c_int;
(*params).profile = self.profile as libc::c_int;
}
pub unsafe fn to_stream(&self, stream: *mut AVStream) {
(*stream).time_base = self.time_base();
(*stream).avg_frame_rate = AVRational {
num: self.fps as libc::c_int,
den: 1,
};
(*stream).r_frame_rate = AVRational {
num: self.fps as libc::c_int,
den: 1,
};
}
}
impl AudioVariant {
@ -171,4 +252,57 @@ impl AudioVariant {
den: self.sample_rate as libc::c_int,
}
}
pub fn get_codec(&self) -> *const AVCodec {
unsafe {
if self.codec == AV_CODEC_ID_AAC as usize {
avcodec_find_encoder_by_name("libfdk_aac\0".as_ptr() as *const libc::c_char)
} else {
avcodec_find_encoder(transmute(self.codec as u32))
}
}
}
pub unsafe fn to_codec_context(&self, ctx: *mut AVCodecContext) {
let codec = self.get_codec();
(*ctx).codec_id = (*codec).id;
(*ctx).codec_type = (*codec).type_;
(*ctx).time_base = self.time_base();
(*ctx).sample_fmt =
av_get_sample_fmt(format!("{}\0", self.sample_fmt).as_ptr() as *const libc::c_char);
(*ctx).bit_rate = self.bitrate as i64;
(*ctx).sample_rate = self.sample_rate as libc::c_int;
(*ctx).ch_layout = self.channel_layout();
}
pub unsafe fn to_codec_params(&self, params: *mut AVCodecParameters) {
let codec = self.get_codec();
(*params).codec_id = (*codec).id;
(*params).codec_type = (*codec).type_;
(*params).format =
av_get_sample_fmt(format!("{}\0", self.sample_fmt).as_ptr() as *const libc::c_char)
as libc::c_int;
(*params).bit_rate = self.bitrate as i64;
(*params).sample_rate = self.sample_rate as libc::c_int;
(*params).ch_layout = self.channel_layout();
}
pub unsafe fn to_stream(&self, stream: *mut AVStream) {
(*stream).time_base = self.time_base();
(*stream).r_frame_rate = AVRational {
num: (*stream).time_base.den,
den: (*stream).time_base.num,
};
}
pub fn channel_layout(&self) -> AVChannelLayout {
AVChannelLayout {
order: AV_CHANNEL_ORDER_NATIVE,
nb_channels: 2,
u: AVChannelLayout__bindgen_ty_1 {
mask: AV_CH_LAYOUT_STEREO,
},
opaque: ptr::null_mut(),
}
}
}

View File

@ -1,3 +1,4 @@
use ffmpeg_sys_next::AVCodecID::{AV_CODEC_ID_AAC, AV_CODEC_ID_AAC_LATM};
use uuid::Uuid;
use crate::demux::info::{DemuxStreamInfo, StreamChannelType};
@ -62,7 +63,7 @@ impl Webhook {
codec: 86018,
channels: 2,
sample_rate: 44_100,
sample_fmt: "fltp".to_owned(),
sample_fmt: "s16".to_owned(),
}));
vars.push(VariantStream::Audio(AudioVariant {
id: Uuid::new_v4(),
@ -72,7 +73,7 @@ impl Webhook {
codec: 86018,
channels: 2,
sample_rate: 44_100,
sample_fmt: "fltp".to_owned(),
sample_fmt: "s16".to_owned(),
}));
}