Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- use ffmpeg::{
- decoder, encoder,
- error::EAGAIN,
- format::context::{Input, Output},
- media::Type,
- packet::{Mut, Ref},
- util::frame::video::Video,
- Codec, Packet, Rational,
- };
- use ffmpeg_next as ffmpeg;
- use ffmpeg_sys_next::{
- av_calloc, av_dump_format, av_frame_alloc, av_frame_unref, av_guess_format,
- av_interleaved_write_frame, av_malloc, av_mallocz, av_packet_alloc,
- av_packet_unref, av_read_frame, av_write_trailer, avcodec_alloc_context3,
- avcodec_find_decoder, avcodec_find_encoder, avcodec_open2,
- avcodec_parameters_from_context, avcodec_parameters_to_context,
- avcodec_receive_frame, avcodec_receive_packet, avcodec_send_frame,
- avcodec_send_packet, avformat_alloc_context,
- avformat_alloc_output_context2, avformat_free_context,
- avformat_init_output, avformat_new_stream, avformat_open_input,
- avformat_write_header, avio_alloc_context, AVCodecContext,
- AVCodecParameters, AVFormatContext, AVMediaType, AVERROR, AVERROR_EOF,
- AVFMT_FLAG_CUSTOM_IO, AVSEEK_SIZE, SEEK_CUR, SEEK_SET,
- };
- use core::panic;
- use std::{
- ffi::{c_int, c_uchar, c_void, CString},
- fs::File,
- io::{Read, Seek},
- mem::{forget, size_of},
- ptr,
- };
- const MPEG_TS_PACKET_SIZE: usize = 188;
- struct Wrapper<T> {
- inner: T,
- }
- extern "C" fn read(
- opaque: *mut c_void,
- buf: *mut u8,
- buf_size: c_int,
- ) -> c_int {
- let input: Box<Wrapper<Box<dyn Read>>> =
- unsafe { Box::from_raw(opaque as *mut Wrapper<Box<dyn Read>>) };
- let input = Box::leak(input);
- let mut buffer =
- unsafe { std::slice::from_raw_parts_mut(buf, buf_size as usize) };
- let mut read: c_int = 0;
- loop {
- match (&mut input.inner).read(&mut buffer) {
- Ok(0) => break,
- Ok(n) => {
- read += n as c_int;
- buffer = &mut buffer[n..];
- }
- Err(err) => {
- if let Some(code) = err.raw_os_error() {
- return code;
- }
- }
- }
- if buffer.is_empty() {
- break;
- }
- }
- if read == 0 {
- return AVERROR_EOF;
- }
- return read;
- }
- extern "C" fn write(
- opaque: *mut c_void,
- buf: *mut u8,
- buf_size: c_int,
- ) -> c_int {
- let output: Box<Wrapper<Box<dyn SeekWrite>>> =
- unsafe { Box::from_raw(opaque as *mut Wrapper<Box<dyn SeekWrite>>) };
- let output = Box::leak(output);
- let mut written: c_int = 0;
- let mut buffer =
- unsafe { std::slice::from_raw_parts(buf, buf_size as usize) };
- loop {
- match output.inner.write(buffer) {
- Ok(n) => {
- written += n as c_int;
- buffer = &buffer[n..];
- }
- Err(err) => {
- if let Some(code) = err.raw_os_error() {
- return code;
- }
- }
- }
- if buffer.is_empty() {
- break;
- }
- }
- return written;
- }
- unsafe extern "C" fn seek(
- opaque: *mut c_void,
- offset: i64,
- whence: c_int,
- ) -> i64 {
- let output: Box<Wrapper<Box<dyn SeekWrite>>> =
- unsafe { Box::from_raw(opaque as *mut Wrapper<Box<dyn SeekWrite>>) };
- let output = Box::leak(output);
- match whence {
- AVSEEK_SIZE => -1,
- SEEK_SET => {
- match output.inner.seek(std::io::SeekFrom::Start(offset as u64)) {
- Ok(n) => n as i64,
- Err(err) => {
- err.raw_os_error().map(Into::into).unwrap_or(-1)
- },
- }
- }
- SEEK_CUR => {
- match output.inner.seek(std::io::SeekFrom::Current(offset)) {
- Ok(n) => n as i64,
- Err(err) => {
- err.raw_os_error().map(Into::into).unwrap_or(-1)
- },
- }
- }
- SEEK_END => {
- match output.inner.seek(std::io::SeekFrom::End(offset)) {
- Ok(n) => return n as i64,
- Err(err) => {
- err.raw_os_error().map(Into::into).unwrap_or(-1)
- },
- }
- }
- _ => { -1 }
- }
- }
- use std::io::Write;
- fn pgm_save(frame: &Video, filename: &str) {
- let mut file = std::fs::OpenOptions::new()
- .create(true)
- .truncate(true)
- .write(true)
- .open(filename)
- .unwrap();
- write!(file, "P5\n{} {}\n{}\n", frame.width(), frame.height(), 255)
- .unwrap();
- let wrap = frame.stride(0);
- for i in 0..frame.height() {
- file.write(&frame.data(0)[i as usize * wrap..][..wrap])
- .unwrap();
- }
- drop(file);
- }
- fn process(
- internal_buffer: *mut c_void,
- internal_buffer_size: c_int,
- internal_buffer_out: *mut c_void,
- internal_buffer_out_size: c_int,
- input: *mut c_void,
- output: *mut c_void,
- ) -> Result<(), ffmpeg::Error> {
- let mut read_format_context: *mut AVFormatContext =
- unsafe { avformat_alloc_context() };
- let read_io_context = unsafe {
- avio_alloc_context(
- internal_buffer as *mut c_uchar,
- internal_buffer_size, // internal buffer and its size
- 0, // write flag (1=true, 0=false)
- input, // user data, will be passed to our callback functions
- Some(read),
- None,
- None,
- )
- };
- unsafe {
- (*read_format_context).pb = read_io_context;
- (*read_format_context).flags |= AVFMT_FLAG_CUSTOM_IO;
- }
- unsafe {
- avformat_open_input(
- &mut read_format_context,
- ptr::null_mut(),
- ptr::null_mut(),
- ptr::null_mut(),
- );
- }
- let mut ictx = unsafe { Input::wrap(read_format_context) };
- let input = ictx
- .streams()
- .best(Type::Video)
- .ok_or(ffmpeg::Error::StreamNotFound)
- .expect("stream not found");
- let mut decoder =
- ffmpeg::codec::context::Context::from_parameters(input.parameters())?
- .decoder()
- .video()?;
- let mut ofmt_ctx = ptr::null_mut();
- let fmt = &CString::new("mp4").unwrap();
- unsafe {
- let ret = avformat_alloc_output_context2(
- &mut ofmt_ctx,
- ptr::null_mut(),
- fmt.as_ptr(),
- ptr::null(),
- );
- if ret != 0 {
- panic!("can't allocate output context {ret}");
- }
- // let encoder = avcodec_find_encoder(ffmpeg_sys_next::AVCodecID::AV_CODEC_ID_H264);
- // avformat_new_stream(*ofmt_ctx, encoder);
- }
- let out_stream = unsafe { avformat_new_stream(ofmt_ctx, ptr::null()) };
- if out_stream.is_null() {
- panic!("failed to allocate output stream");
- }
- let encoder = unsafe {
- avcodec_find_encoder(ffmpeg_sys_next::AVCodecID::AV_CODEC_ID_H264)
- };
- if encoder.is_null() {
- panic!("h264 encoder not found");
- }
- let enc_ctx = unsafe { avcodec_alloc_context3(encoder) };
- if enc_ctx.is_null() {
- panic!("failed to allocate encoder context");
- }
- unsafe {
- (*enc_ctx).width = decoder.width() as i32;
- (*enc_ctx).height = decoder.height() as i32;
- (*enc_ctx).sample_aspect_ratio = decoder.aspect_ratio().into();
- (*enc_ctx).pix_fmt = *(*encoder).pix_fmts;
- (*enc_ctx).time_base = Rational::new(1001, 30000).into();
- (*enc_ctx).framerate = Rational::new(30000, 1001).into();
- }
- let ret = unsafe { avcodec_open2(enc_ctx, encoder, ptr::null_mut()) };
- if ret < 0 {
- panic!("failed to open video encoder for stream");
- }
- let ret = unsafe {
- avcodec_parameters_from_context((*out_stream).codecpar, enc_ctx)
- };
- if ret != 0 {
- panic!("error when creating parameters from context {ret}");
- }
- unsafe {
- (*out_stream).time_base = (*enc_ctx).time_base;
- (*out_stream).duration = input.duration();
- (*out_stream).avg_frame_rate = Rational::new(30000, 1001).into();
- (*out_stream)
- }
- let write_io_context = unsafe {
- avio_alloc_context(
- internal_buffer_out as *mut c_uchar,
- internal_buffer_out_size, // internal buffer and its size
- 1, // write flag (1=true, 0=false)
- output, // user data, will be passed to our callback functions
- None,
- Some(write),
- Some(seek),
- )
- };
- unsafe {
- (*ofmt_ctx).pb = write_io_context;
- (*ofmt_ctx).flags |= AVFMT_FLAG_CUSTOM_IO;
- }
- let ret = unsafe { avformat_write_header(ofmt_ctx, ptr::null_mut()) };
- if ret < 0 {
- panic!("error when writing header {ret}")
- };
- let video_stream_index = input.index();
- let mut idx = 0;
- //output.write_header()?;
- for (stream, packet) in ictx.packets() {
- if stream.index() == video_stream_index {
- decoder.send_packet(&packet)?;
- process_frames(&mut decoder, enc_ctx, ofmt_ctx, &mut idx)?;
- }
- }
- decoder.send_eof()?;
- let ret = unsafe { avcodec_send_frame(enc_ctx, ptr::null()) };
- if ret < 0 {
- panic!("can't flush encoder {ret}")
- }
- let mut encoded = Packet::empty();
- let ret = unsafe { avcodec_receive_packet(enc_ctx, encoded.as_mut_ptr()) };
- dbg!(ret);
- let ret =
- unsafe { av_interleaved_write_frame(ofmt_ctx, encoded.as_mut_ptr()) };
- dbg!(ret);
- let ret = unsafe { av_write_trailer(ofmt_ctx) };
- dbg!(ret);
- unsafe { avformat_free_context(ofmt_ctx) };
- //encoder.send_eof()?;
- Ok(())
- }
- fn process_frames(
- decoder: &mut decoder::Video,
- encoder: *mut AVCodecContext,
- //encoder: &mut encoder::video::Video,
- output: *mut AVFormatContext,
- // output: &mut Output,
- idx: &mut usize,
- ) -> Result<(), ffmpeg::Error> {
- let mut frame = Video::empty();
- let mut i = 0;
- loop {
- dbg!(*idx);
- match decoder.receive_frame(&mut frame) {
- Ok(_) => {
- i += 1;
- pgm_save(&frame, &format!("frame-{idx}.pgm"));
- *idx += 1;
- let mut encoded = Packet::empty();
- let ret =
- unsafe { avcodec_send_frame(encoder, frame.as_ptr()) };
- if ret < 0 {
- panic!("error sending frame {ret}");
- }
- let ret = unsafe {
- avcodec_receive_packet(encoder, encoded.as_mut_ptr())
- };
- if ret == AVERROR(EAGAIN) || ret == AVERROR_EOF {
- break;
- }
- if ret < 0 {
- panic!("error sending packet {ret}");
- }
- let raw = encoded.as_mut_ptr();
- (unsafe { *raw }).duration = i;
- let ret = unsafe {
- av_interleaved_write_frame(output, encoded.as_mut_ptr())
- };
- if ret < 0 {
- panic!("error writing frame {ret}");
- }
- }
- Err(ffmpeg::Error::Eof) => {
- dbg!("eof");
- break;
- }
- Err(ffmpeg::Error::Other { errno: EAGAIN }) => {
- dbg!("again");
- break;
- }
- other => {
- dbg!(other);
- return other;
- }
- }
- }
- Ok(())
- }
- trait SeekWrite: Seek + Write {}
- impl<T: Seek + Write> SeekWrite for T {}
- fn main() {
- let mut args = std::env::args().skip(1);
- let input_filename = "out.mkv"; // args.next().unwrap();
- let output_filename = "out.mp4"; // args.next().unwrap();
- let input = std::fs::OpenOptions::new()
- .read(true)
- .open(input_filename)
- .unwrap();
- let output = std::fs::OpenOptions::new()
- .write(true)
- .create(true)
- .open(output_filename)
- .unwrap();
- let buffer_size = MPEG_TS_PACKET_SIZE;
- let internal_buffer_for_demux = unsafe { av_malloc(buffer_size) };
- let internal_buffer_for_output = unsafe { av_malloc(1024) };
- let inner: Box<dyn Read> = Box::new(input);
- let input_ptr = Box::into_raw(Box::new(Wrapper { inner })) as *mut c_void;
- let inner_: Box<dyn SeekWrite> = Box::new(output);
- let output_ptr =
- Box::into_raw(Box::new(Wrapper { inner: inner_ })) as *mut c_void;
- process(
- internal_buffer_for_demux,
- buffer_size as i32,
- internal_buffer_for_output,
- 1024 as i32,
- input_ptr,
- output_ptr,
- )
- .unwrap();
- println!("done");
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement