mirror of
https://github.com/v0l/route96.git
synced 2025-06-18 05:28:50 +00:00
feat: BUD-05
This commit is contained in:
@ -1,15 +1,27 @@
|
||||
use std::{ptr, slice};
|
||||
use std::collections::HashMap;
|
||||
use std::mem::transmute;
|
||||
use std::path::PathBuf;
|
||||
use std::{ptr, slice};
|
||||
|
||||
use anyhow::Error;
|
||||
use ffmpeg_sys_the_third::{AV_CODEC_FLAG_GLOBAL_HEADER, av_dump_format, av_find_best_stream, av_frame_alloc, av_frame_copy_props, av_frame_free, av_guess_format, av_interleaved_write_frame, av_packet_alloc, av_packet_free, av_packet_rescale_ts, av_packet_unref, AV_PROFILE_H264_HIGH, av_read_frame, av_write_trailer, AVCodec, avcodec_alloc_context3, avcodec_find_encoder, avcodec_free_context, avcodec_open2, avcodec_parameters_from_context, avcodec_parameters_to_context, avcodec_receive_frame, avcodec_receive_packet, avcodec_send_frame, avcodec_send_packet, AVCodecContext, AVCodecID, AVERROR, AVERROR_EOF, AVERROR_STREAM_NOT_FOUND, AVFMT_GLOBALHEADER, avformat_alloc_output_context2, avformat_close_input, avformat_find_stream_info, avformat_free_context, avformat_init_output, avformat_new_stream, avformat_open_input, avformat_write_header, AVFormatContext, AVIO_FLAG_WRITE, avio_open, AVMediaType, AVPacket, sws_freeContext, sws_getContext, sws_scale_frame, SwsContext};
|
||||
use ffmpeg_sys_the_third::AVMediaType::{AVMEDIA_TYPE_AUDIO, AVMEDIA_TYPE_VIDEO};
|
||||
use ffmpeg_sys_the_third::AVPixelFormat::{AV_PIX_FMT_RGBA, AV_PIX_FMT_YUV420P};
|
||||
use ffmpeg_sys_the_third::{
|
||||
av_dump_format, av_find_best_stream, av_frame_alloc, av_frame_copy_props, av_frame_free,
|
||||
av_guess_format, av_interleaved_write_frame, av_packet_alloc, av_packet_free,
|
||||
av_packet_rescale_ts, av_packet_unref, av_read_frame, av_write_trailer, avcodec_alloc_context3,
|
||||
avcodec_find_encoder, avcodec_free_context, avcodec_open2, avcodec_parameters_from_context,
|
||||
avcodec_parameters_to_context, avcodec_receive_frame, avcodec_receive_packet,
|
||||
avcodec_send_frame, avcodec_send_packet, avformat_alloc_output_context2, avformat_close_input,
|
||||
avformat_find_stream_info, avformat_free_context, avformat_init_output, avformat_new_stream,
|
||||
avformat_open_input, avformat_write_header, avio_open, sws_freeContext, sws_getContext,
|
||||
sws_scale_frame, AVCodec, AVCodecContext, AVCodecID, AVFormatContext, AVMediaType, AVPacket,
|
||||
SwsContext, AVERROR, AVERROR_EOF, AVERROR_STREAM_NOT_FOUND, AVFMT_GLOBALHEADER,
|
||||
AVIO_FLAG_WRITE, AV_CODEC_FLAG_GLOBAL_HEADER, AV_PROFILE_H264_HIGH,
|
||||
};
|
||||
use libc::EAGAIN;
|
||||
|
||||
use crate::processing::{FileProcessorResult, NewFileProcessorResult, resize_image};
|
||||
use crate::processing::{resize_image, FileProcessorResult, NewFileProcessorResult};
|
||||
|
||||
/// Image converter to WEBP
|
||||
pub struct WebpProcessor {
|
||||
@ -39,18 +51,26 @@ impl WebpProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn transcode_pkt(&mut self, pkt: *mut AVPacket, in_fmt: *mut AVFormatContext, out_fmt: *mut AVFormatContext) -> Result<(), Error> {
|
||||
unsafe fn transcode_pkt(
|
||||
&mut self,
|
||||
pkt: *mut AVPacket,
|
||||
in_fmt: *mut AVFormatContext,
|
||||
out_fmt: *mut AVFormatContext,
|
||||
) -> Result<(), Error> {
|
||||
let idx = (*pkt).stream_index as usize;
|
||||
let out_idx = match self.stream_map.get(&idx) {
|
||||
Some(i) => i,
|
||||
None => return Ok(())
|
||||
None => return Ok(()),
|
||||
};
|
||||
let in_stream = *(*in_fmt).streams.add(idx);
|
||||
let out_stream = *(*out_fmt).streams.add(*out_idx);
|
||||
av_packet_rescale_ts(pkt, (*in_stream).time_base, (*out_stream).time_base);
|
||||
|
||||
let dec_ctx = self.decoders.get_mut(&idx).expect("Missing decoder config");
|
||||
let enc_ctx = self.encoders.get_mut(out_idx).expect("Missing encoder config");
|
||||
let enc_ctx = self
|
||||
.encoders
|
||||
.get_mut(out_idx)
|
||||
.expect("Missing encoder config");
|
||||
|
||||
let ret = avcodec_send_packet(*dec_ctx, pkt);
|
||||
if ret < 0 {
|
||||
@ -76,16 +96,21 @@ impl WebpProcessor {
|
||||
}
|
||||
frame_out
|
||||
}
|
||||
None => frame
|
||||
None => frame,
|
||||
};
|
||||
|
||||
// take the first frame as "image"
|
||||
if (*(*out_stream).codecpar).codec_type == AVMEDIA_TYPE_VIDEO && self.image.is_none() {
|
||||
let mut dst_frame = resize_image(frame_out,
|
||||
(*frame_out).width as usize,
|
||||
(*frame_out).height as usize,
|
||||
AV_PIX_FMT_RGBA)?;
|
||||
let pic_slice = slice::from_raw_parts_mut((*dst_frame).data[0], ((*dst_frame).width * (*dst_frame).height * 4) as usize);
|
||||
let mut dst_frame = resize_image(
|
||||
frame_out,
|
||||
(*frame_out).width as usize,
|
||||
(*frame_out).height as usize,
|
||||
AV_PIX_FMT_RGBA,
|
||||
)?;
|
||||
let pic_slice = slice::from_raw_parts_mut(
|
||||
(*dst_frame).data[0],
|
||||
((*dst_frame).width * (*dst_frame).height * 4) as usize,
|
||||
);
|
||||
self.image = Some(pic_slice.to_vec());
|
||||
av_frame_free(&mut dst_frame);
|
||||
}
|
||||
@ -115,7 +140,11 @@ impl WebpProcessor {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn setup_decoder(&mut self, in_fmt: *mut AVFormatContext, av_type: AVMediaType) -> Result<i32, Error> {
|
||||
unsafe fn setup_decoder(
|
||||
&mut self,
|
||||
in_fmt: *mut AVFormatContext,
|
||||
av_type: AVMediaType,
|
||||
) -> Result<i32, Error> {
|
||||
let mut decoder: *const AVCodec = ptr::null_mut();
|
||||
let stream_idx = av_find_best_stream(in_fmt, av_type, -1, -1, &mut decoder, 0);
|
||||
if stream_idx == AVERROR_STREAM_NOT_FOUND {
|
||||
@ -141,13 +170,18 @@ impl WebpProcessor {
|
||||
Ok(stream_idx)
|
||||
}
|
||||
|
||||
unsafe fn setup_encoder(&mut self, in_fmt: *mut AVFormatContext, out_fmt: *mut AVFormatContext, in_idx: i32) -> Result<(), Error> {
|
||||
unsafe fn setup_encoder(
|
||||
&mut self,
|
||||
in_fmt: *mut AVFormatContext,
|
||||
out_fmt: *mut AVFormatContext,
|
||||
in_idx: i32,
|
||||
) -> Result<(), Error> {
|
||||
let in_stream = *(*in_fmt).streams.add(in_idx as usize);
|
||||
let stream_type = (*(*in_stream).codecpar).codec_type;
|
||||
let out_codec = match stream_type {
|
||||
AVMEDIA_TYPE_VIDEO => avcodec_find_encoder((*(*out_fmt).oformat).video_codec),
|
||||
AVMEDIA_TYPE_AUDIO => avcodec_find_encoder((*(*out_fmt).oformat).audio_codec),
|
||||
_ => ptr::null_mut()
|
||||
_ => ptr::null_mut(),
|
||||
};
|
||||
// not mapped ignore
|
||||
if out_codec.is_null() {
|
||||
@ -203,15 +237,21 @@ impl WebpProcessor {
|
||||
|
||||
let out_idx = (*stream).index as usize;
|
||||
// setup scaler if pix_fmt doesnt match
|
||||
if stream_type == AVMEDIA_TYPE_VIDEO &&
|
||||
(*(*in_stream).codecpar).format != (*(*stream).codecpar).format {
|
||||
let sws_ctx = sws_getContext((*(*in_stream).codecpar).width,
|
||||
(*(*in_stream).codecpar).height,
|
||||
transmute((*(*in_stream).codecpar).format),
|
||||
(*(*stream).codecpar).width,
|
||||
(*(*stream).codecpar).height,
|
||||
transmute((*(*stream).codecpar).format),
|
||||
0, ptr::null_mut(), ptr::null_mut(), ptr::null_mut());
|
||||
if stream_type == AVMEDIA_TYPE_VIDEO
|
||||
&& (*(*in_stream).codecpar).format != (*(*stream).codecpar).format
|
||||
{
|
||||
let sws_ctx = sws_getContext(
|
||||
(*(*in_stream).codecpar).width,
|
||||
(*(*in_stream).codecpar).height,
|
||||
transmute((*(*in_stream).codecpar).format),
|
||||
(*(*stream).codecpar).width,
|
||||
(*(*stream).codecpar).height,
|
||||
transmute((*(*stream).codecpar).format),
|
||||
0,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
);
|
||||
if sws_ctx.is_null() {
|
||||
return Err(Error::msg("Failed to create sws context"));
|
||||
}
|
||||
@ -262,16 +302,23 @@ impl WebpProcessor {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn process_file(mut self, in_file: PathBuf, mime_type: &str) -> Result<FileProcessorResult, Error> {
|
||||
pub fn process_file(
|
||||
mut self,
|
||||
in_file: PathBuf,
|
||||
mime_type: &str,
|
||||
) -> Result<FileProcessorResult, Error> {
|
||||
unsafe {
|
||||
let mut out_path = in_file.clone();
|
||||
out_path.set_extension("_compressed");
|
||||
|
||||
let mut dec_fmt: *mut AVFormatContext = ptr::null_mut();
|
||||
let ret = avformat_open_input(&mut dec_fmt,
|
||||
format!("{}\0", in_file.into_os_string().into_string().unwrap()).as_ptr() as *const libc::c_char,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut());
|
||||
let ret = avformat_open_input(
|
||||
&mut dec_fmt,
|
||||
format!("{}\0", in_file.into_os_string().into_string().unwrap()).as_ptr()
|
||||
as *const libc::c_char,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
);
|
||||
if ret < 0 {
|
||||
return Err(Error::msg("Failed to create input context"));
|
||||
}
|
||||
@ -284,23 +331,32 @@ impl WebpProcessor {
|
||||
let in_audio_stream = self.setup_decoder(dec_fmt, AVMEDIA_TYPE_AUDIO)?;
|
||||
|
||||
let out_format = if mime_type.starts_with("image/") {
|
||||
av_guess_format("webp\0".as_ptr() as *const libc::c_char,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut())
|
||||
av_guess_format(
|
||||
"webp\0".as_ptr() as *const libc::c_char,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
)
|
||||
} else if mime_type.starts_with("video/") {
|
||||
av_guess_format("matroska\0".as_ptr() as *const libc::c_char,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut())
|
||||
av_guess_format(
|
||||
"matroska\0".as_ptr() as *const libc::c_char,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
)
|
||||
} else {
|
||||
return Err(Error::msg("Mime type not supported"));
|
||||
};
|
||||
|
||||
let out_filename = format!("{}\0", out_path.clone().into_os_string().into_string().unwrap());
|
||||
let out_filename = format!(
|
||||
"{}\0",
|
||||
out_path.clone().into_os_string().into_string().unwrap()
|
||||
);
|
||||
let mut out_fmt: *mut AVFormatContext = ptr::null_mut();
|
||||
let ret = avformat_alloc_output_context2(&mut out_fmt,
|
||||
out_format,
|
||||
ptr::null_mut(),
|
||||
out_filename.as_ptr() as *const libc::c_char);
|
||||
let ret = avformat_alloc_output_context2(
|
||||
&mut out_fmt,
|
||||
out_format,
|
||||
ptr::null_mut(),
|
||||
out_filename.as_ptr() as *const libc::c_char,
|
||||
);
|
||||
if ret < 0 {
|
||||
return Err(Error::msg("Failed to create output context"));
|
||||
}
|
||||
@ -354,14 +410,13 @@ impl WebpProcessor {
|
||||
avformat_free_context(dec_fmt);
|
||||
avformat_free_context(out_fmt);
|
||||
|
||||
Ok(FileProcessorResult::NewFile(
|
||||
NewFileProcessorResult {
|
||||
result: out_path,
|
||||
mime_type: "image/webp".to_string(),
|
||||
width: self.width.unwrap_or(0),
|
||||
height: self.height.unwrap_or(0),
|
||||
image: self.image.unwrap_or_default(),
|
||||
}))
|
||||
Ok(FileProcessorResult::NewFile(NewFileProcessorResult {
|
||||
result: out_path,
|
||||
mime_type: "image/webp".to_string(),
|
||||
width: self.width.unwrap_or(0),
|
||||
height: self.height.unwrap_or(0),
|
||||
image: self.image.unwrap_or_default(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user