forked from CCExtractor/rusty_ffmpeg
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.rs
More file actions
215 lines (182 loc) · 6.99 KB
/
main.rs
File metadata and controls
215 lines (182 loc) · 6.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
//! Port from Original code: https://github.com/leandromoreira/ffmpeg-libav-tutorial/blob/master/0_hello_world.c
use rusty_ffmpeg::ffi;
use std::{
ffi::{CStr, CString},
fs::File,
io::Write,
ptr, slice,
};
// This should only be used with FFmpeg 7
fn main() {
let filepath: CString = CString::new("./examples/slice/bear.mp4").unwrap();
println!("initializing all the containers, codecs and protocols.");
let mut format_context_ptr = unsafe { ffi::avformat_alloc_context() };
if format_context_ptr.is_null() {
panic!("ERROR could not allocate memory for Format Context");
}
println!(
"opening the input file ({}) and loading format (container) header",
filepath.to_str().unwrap()
);
if unsafe {
ffi::avformat_open_input(
&mut format_context_ptr,
filepath.as_ptr(),
ptr::null_mut(),
ptr::null_mut(),
)
} != 0
{
panic!("ERROR could not open the file");
}
let format_context = unsafe { format_context_ptr.as_mut() }.unwrap();
let format_name = unsafe { CStr::from_ptr((*format_context.iformat).name) }
.to_str()
.unwrap();
println!(
"format {}, duration {} us, bit_rate {}",
format_name, format_context.duration, format_context.bit_rate
);
println!("finding stream info from format");
if unsafe { ffi::avformat_find_stream_info(format_context, ptr::null_mut()) } < 0 {
panic!("ERROR could not get the stream info");
}
let mut codec_ptr: *const ffi::AVCodec = ptr::null_mut();
let mut codec_parameters_ptr: *const ffi::AVCodecParameters = ptr::null_mut();
let mut video_stream_index = None;
let streams = unsafe {
slice::from_raw_parts(format_context.streams, format_context.nb_streams as usize)
};
for (i, stream) in streams
.iter()
.map(|stream| unsafe { stream.as_ref() }.unwrap())
.enumerate()
{
println!(
"AVStream->time_base before open coded {}/{}",
stream.time_base.num, stream.time_base.den
);
println!(
"AVStream->r_frame_rate before open coded {}/{}",
stream.r_frame_rate.num, stream.r_frame_rate.den
);
println!("AVStream->start_time {}", stream.start_time);
println!("AVStream->duration {}", stream.duration);
println!("finding the proper decoder (CODEC)");
let local_codec_params = unsafe { stream.codecpar.as_ref() }.unwrap();
let local_codec =
unsafe { ffi::avcodec_find_decoder(local_codec_params.codec_id).as_ref() }
.expect("ERROR unsupported codec!");
match local_codec_params.codec_type {
ffi::AVMEDIA_TYPE_VIDEO => {
if video_stream_index.is_none() {
video_stream_index = Some(i);
codec_ptr = local_codec;
codec_parameters_ptr = local_codec_params;
}
println!(
"Video Codec: resolution {} x {}",
local_codec_params.width, local_codec_params.height
);
}
ffi::AVMEDIA_TYPE_AUDIO => {
println!(
"Audio Codec: {} channels, sample rate {}",
local_codec_params.ch_layout.nb_channels, local_codec_params.sample_rate
);
}
_ => {}
};
let codec_name = unsafe { CStr::from_ptr(local_codec.name) }
.to_str()
.unwrap();
println!(
"\tCodec {} ID {} bit_rate {}",
codec_name, local_codec.id, local_codec_params.bit_rate
);
}
let codec_context = unsafe { ffi::avcodec_alloc_context3(codec_ptr).as_mut() }
.expect("failed to allocated memory for AVCodecContext");
if unsafe { ffi::avcodec_parameters_to_context(codec_context, codec_parameters_ptr) } < 0 {
panic!("failed to copy codec params to codec context");
}
if unsafe { ffi::avcodec_open2(codec_context, codec_ptr, ptr::null_mut()) } < 0 {
panic!("failed to open codec through avcodec_open2");
}
let frame =
unsafe { ffi::av_frame_alloc().as_mut() }.expect("failed to allocated memory for AVFrame");
let packet = unsafe { ffi::av_packet_alloc().as_mut() }
.expect("failed to allocated memory for AVPacket");
let mut packets_waiting = 8;
while unsafe { ffi::av_read_frame(format_context, packet) } >= 0 {
if video_stream_index == Some(packet.stream_index as usize) {
println!("AVPacket->pts {}", packet.pts);
decode_packet(packet, codec_context, frame).unwrap();
packets_waiting -= 1;
if packets_waiting <= 0 {
break;
}
}
unsafe { ffi::av_packet_unref(packet) };
}
println!("releasing all the resources");
unsafe {
ffi::avformat_close_input(&mut (format_context as *mut _));
ffi::av_packet_free(&mut (packet as *mut _));
ffi::av_frame_free(&mut (frame as *mut _));
ffi::avcodec_free_context(&mut (codec_context as *mut _));
}
}
fn decode_packet(
packet: &ffi::AVPacket,
codec_context: &mut ffi::AVCodecContext,
frame: &mut ffi::AVFrame,
) -> Result<(), String> {
let mut response = unsafe { ffi::avcodec_send_packet(codec_context, packet) };
if response < 0 {
return Err(String::from("Error while sending a packet to the decoder."));
}
while response >= 0 {
response = unsafe { ffi::avcodec_receive_frame(codec_context, frame) };
if response == ffi::AVERROR(ffi::EAGAIN) || response == ffi::AVERROR_EOF {
break;
} else if response < 0 {
return Err(String::from(
"Error while receiving a frame from the decoder.",
));
} else {
println!(
"Frame {} (type={}, size={} bytes) pts {}",
codec_context.frame_num,
unsafe { ffi::av_get_picture_type_char(frame.pict_type) },
frame.linesize[0] * frame.height,
frame.pts,
);
let frame_filename = format!(
"./examples/slice/output/frame-{}.pgm",
codec_context.frame_num
);
let width = frame.width as usize;
let height = frame.height as usize;
let wrap = frame.linesize[0] as usize;
let data = unsafe { slice::from_raw_parts(frame.data[0], wrap * height) };
save_gray_frame(data, wrap, width, height, frame_filename).unwrap();
}
}
Ok(())
}
fn save_gray_frame(
buf: &[u8],
wrap: usize,
xsize: usize,
ysize: usize,
filename: String,
) -> Result<(), std::io::Error> {
let mut file = File::create(filename)?;
let data = format!("P5\n{} {}\n{}\n", xsize, ysize, 255);
file.write_all(data.as_bytes())?;
for i in 0..ysize {
file.write_all(&buf[i * wrap..(i * wrap + xsize)])?;
}
Ok(())
}