mirror of
https://github.com/v0l/zap-stream-core.git
synced 2025-06-21 06:00:45 +00:00
Audio codec libfdk_aac
This commit is contained in:
@ -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,
|
||||
|
@ -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
18
src/egress/http.rs
Normal 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(())
|
||||
}
|
@ -1 +1,2 @@
|
||||
pub mod hls;
|
||||
pub mod hls;
|
||||
pub mod http;
|
@ -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() {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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() {
|
||||
|
12
src/main.rs
12
src/main.rs
@ -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}");
|
||||
|
@ -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)?;
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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))?;
|
||||
|
136
src/variant.rs
136
src/variant.rs
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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(),
|
||||
}));
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user