Skip to content

Commit 07413a3

Browse files
committed
added example code
1 parent 75079dc commit 07413a3

File tree

7 files changed

+473
-1
lines changed

7 files changed

+473
-1
lines changed

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "ffmpeg-dev"
3-
version = "0.3.6"
3+
version = "0.3.7"
44
authors = ["colbyn <hello@colbyn.com>"]
55
edition = "2018"
66
keywords = ["video", "ffmpeg"]
@@ -9,6 +9,7 @@ license = "MIT"
99
homepage = "https://github.com/imager-io/ffmpeg-dev-rs"
1010
repository = "https://github.com/imager-io/ffmpeg-dev-rs"
1111
readme = "README.md"
12+
exclude = ["./assets"]
1213

1314
[dependencies]
1415
libc = "^0.2"

README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,28 @@ Hopefully one day the rust ecosystem will get a decent FFmpeg alternative for e.
7373
# Future
7474
It would be interesting to experiment with compiling FFmpeg to WebAssembly. Perhaps as an alternative to static linking, if a local version isn’t available it could link to a remote lib over the network.
7575

76+
# Examples
77+
78+
```shell
79+
$ cargo run --example h264_video_dec
80+
```
81+
82+
## Added as of this writing
83+
* `./examples/remux.rs`
84+
* `./examples/h264_video_dec.rs`
85+
86+
87+
# Miscellaneous Links:
88+
* [FFmpeg docs overview](https://ffmpeg.org/documentation.html)
89+
* [FFmpeg C API documentation](https://ffmpeg.org/doxygen/trunk/index.html)
90+
* [FFmpeg C Examples](https://github.com/FFmpeg/FFmpeg/tree/master/doc/examples) (pretty easy to convert to rust in my experience)
91+
* [Rust docs](https://docs.rs/ffmpeg-dev)
92+
93+
## Sample or Test Content
94+
95+
* [sintel_trailer-1080p.mp4](https://download.blender.org/durian/trailer/sintel_trailer-1080p.mp4)
96+
* `./assets/test/test.h264` - heavily compressed version of the video stream from `sintel_trailer-1080p.mp4`. This is a raw H264 encoded video binary.
97+
7698
<hr/>
7799

78100
Built for [Imager](https://imager.io) - Site performance tools for efficiently distributing media on the web.

assets/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
samples
2+
output

assets/test/test.h264

8.25 MB
Binary file not shown.

examples/h264_video_dec.rs

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
//! Decodes a raw H264 video binary into a `Vec` of YUV420P images.
2+
#![allow(unused)]
3+
use std::convert::AsRef;
4+
use std::path::{PathBuf, Path};
5+
use std::ffi::{CStr, CString};
6+
use std::os::raw::{c_char, c_int};
7+
use libc::{size_t, c_float, c_void};
8+
use ffmpeg_dev::sys::{
9+
self,
10+
AVFrame,
11+
AVDictionary,
12+
AVCodec,
13+
AVCodecContext,
14+
AVStream,
15+
AVPacket,
16+
AVFormatContext,
17+
AVOutputFormat,
18+
AVCodecParameters,
19+
AVCodecParserContext,
20+
AVMediaType,
21+
AVMediaType_AVMEDIA_TYPE_UNKNOWN as AVMEDIA_TYPE_UNKNOWN,
22+
AVMediaType_AVMEDIA_TYPE_VIDEO as AVMEDIA_TYPE_VIDEO,
23+
AVMediaType_AVMEDIA_TYPE_AUDIO as AVMEDIA_TYPE_AUDIO,
24+
AVMediaType_AVMEDIA_TYPE_DATA as AVMEDIA_TYPE_DATA,
25+
AVMediaType_AVMEDIA_TYPE_SUBTITLE as AVMEDIA_TYPE_SUBTITLE,
26+
AVMediaType_AVMEDIA_TYPE_ATTACHMENT as AVMEDIA_TYPE_ATTACHMENT,
27+
AVMediaType_AVMEDIA_TYPE_NB as AVMEDIA_TYPE_NB,
28+
AVFMT_NOFILE,
29+
AVIO_FLAG_WRITE,
30+
AVRounding_AV_ROUND_NEAR_INF as AV_ROUND_NEAR_INF,
31+
AVRounding_AV_ROUND_PASS_MINMAX as AV_ROUND_PASS_MINMAX,
32+
AVCodecID_AV_CODEC_ID_H264 as AV_CODEC_ID_H264,
33+
AV_INPUT_BUFFER_PADDING_SIZE,
34+
};
35+
36+
// TODO: Use defs from `ffmpeg_dev::extra::defs` over hardcoded values.
37+
pub const NOPTS_VALUE: i64 = -9223372036854775808;
38+
pub const AVERROR_EAGAIN: i32 = 35;
39+
40+
fn c_str(s: &str) -> CString {
41+
CString::new(s).expect("str to c str")
42+
}
43+
44+
pub struct RawYuv420p {
45+
pub width: u32,
46+
pub height: u32,
47+
pub bufsize: i32,
48+
pub linesize: [i32; 4],
49+
pub data: [*mut u8; 4],
50+
}
51+
52+
impl Drop for RawYuv420p {
53+
fn drop(&mut self) {
54+
unsafe {
55+
if !self.data[0].is_null() {
56+
sys::av_free(self.data[0] as *mut c_void);
57+
}
58+
};
59+
}
60+
}
61+
62+
63+
impl RawYuv420p {
64+
pub fn luma_size(&self) -> u32 {
65+
self.width * self.height
66+
}
67+
pub fn chroma_size(&self) -> u32 {
68+
self.width * self.height / 4
69+
}
70+
pub unsafe fn to_vec(&self) -> Vec<u8> {
71+
let mut output = Vec::<u8>::new();
72+
let ptr = self.data[0];
73+
for i in 0 .. self.bufsize as usize {
74+
let val = ptr.add(i);
75+
let val = *val;
76+
output.push(val);
77+
}
78+
output
79+
}
80+
pub unsafe fn save(&self, path: &str) {
81+
println!(
82+
"ffplay -s {}x{} -pix_fmt yuv420p {}",
83+
self.width,
84+
self.height,
85+
path,
86+
);
87+
std::fs::write(path, self.to_vec());
88+
}
89+
pub unsafe fn new(width: u32, height: u32) -> Self {
90+
use sys::{
91+
AVPixelFormat_AV_PIX_FMT_YUV420P as AV_PIX_FMT_YUV420P
92+
};
93+
let pix_fmt: sys::AVPixelFormat = AV_PIX_FMT_YUV420P;
94+
let mut linesize = [0i32; 4];
95+
let mut data = [
96+
std::ptr::null_mut(),
97+
std::ptr::null_mut(),
98+
std::ptr::null_mut(),
99+
std::ptr::null_mut(),
100+
];
101+
let bufsize = sys::av_image_alloc(
102+
data.as_mut_ptr(),
103+
linesize.as_mut_ptr(),
104+
width as i32,
105+
height as i32,
106+
pix_fmt,
107+
1,
108+
);
109+
RawYuv420p {
110+
width,
111+
height,
112+
bufsize,
113+
linesize,
114+
data,
115+
}
116+
}
117+
pub unsafe fn fill_from_frame(&mut self, frame: *mut AVFrame) {
118+
use sys::{
119+
AVPixelFormat_AV_PIX_FMT_YUV420P as AV_PIX_FMT_YUV420P
120+
};
121+
assert!(!frame.is_null());
122+
assert!((*frame).format == AV_PIX_FMT_YUV420P);
123+
sys::av_image_copy(
124+
self.data.as_mut_ptr(),
125+
self.linesize.as_mut_ptr(),
126+
(*frame).data.as_mut_ptr() as *mut *const u8,
127+
(*frame).linesize.as_ptr(),
128+
(*frame).format,
129+
(*frame).width,
130+
(*frame).height,
131+
);
132+
}
133+
}
134+
135+
136+
unsafe fn decode_h264_video(
137+
source: Vec<u8>,
138+
) -> Vec<RawYuv420p> {
139+
// HELPERS
140+
unsafe fn decode(
141+
dec_ctx: *mut AVCodecContext,
142+
frame: *mut AVFrame,
143+
pkt: *mut AVPacket,
144+
output: &mut Vec<RawYuv420p>,
145+
) {
146+
let mut buf = vec![0u8; 1024];
147+
let mut ret = sys::avcodec_send_packet(dec_ctx, pkt);
148+
assert!(ret >= 0);
149+
while ret >= 0 {
150+
ret = sys::avcodec_receive_frame(dec_ctx, frame);
151+
if (ret < 0) {
152+
return;
153+
}
154+
let done = {
155+
ret == ffmpeg_dev::extra::defs::averror(ffmpeg_dev::extra::defs::eagain()) ||
156+
ret == ffmpeg_dev::extra::defs::averror_eof()
157+
};
158+
if done {
159+
return;
160+
}
161+
assert!(ret >= 0);
162+
// WRITE DECODED FRAME
163+
let mut decoded = RawYuv420p::new((*frame).width as u32, (*frame).height as u32);
164+
decoded.fill_from_frame(frame);
165+
output.push(decoded);
166+
}
167+
}
168+
// I/O
169+
let mut f = source;
170+
// MISC
171+
const INBUF_SIZE: u32 = 4096;
172+
// SETUP AV STATE
173+
let mut codec: *mut AVCodec = sys::avcodec_find_decoder(AV_CODEC_ID_H264);
174+
assert!(!codec.is_null());
175+
let mut parser: *mut AVCodecParserContext = sys::av_parser_init((*codec).id as i32);
176+
let mut c: *mut AVCodecContext = sys::avcodec_alloc_context3(codec);
177+
assert!(!c.is_null());
178+
let mut frame: *mut AVFrame = sys::av_frame_alloc();
179+
let mut inbuf: Vec<u8> = vec![0; (INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE) as usize];
180+
let mut pkt: *mut AVPacket = sys::av_packet_alloc();
181+
assert!(!pkt.is_null());
182+
// OPEN
183+
assert!(sys::avcodec_open2(c, codec, std::ptr::null_mut()) >= 0);
184+
let mut output = Vec::<RawYuv420p>::new();
185+
let mut eof = false;
186+
while eof == false {
187+
let inbuf_size = {
188+
if f.len() < INBUF_SIZE as usize {
189+
f.len()
190+
} else {
191+
INBUF_SIZE as usize
192+
}
193+
};
194+
let inbuf = f
195+
.drain(0..inbuf_size)
196+
.collect::<Vec<u8>>();
197+
let mut inbuf_size = inbuf_size as isize;
198+
if inbuf.is_empty() {
199+
eof = true;
200+
break;
201+
}
202+
while inbuf_size > 0 {
203+
let ret = sys::av_parser_parse2(
204+
parser,
205+
c,
206+
&mut (*pkt).data,
207+
&mut (*pkt).size,
208+
inbuf.as_ptr(),
209+
inbuf.len() as i32,
210+
NOPTS_VALUE,
211+
NOPTS_VALUE,
212+
0,
213+
);
214+
assert!(ret >= 0);
215+
inbuf_size = inbuf_size - (ret as isize);
216+
if (*pkt).size > 0 {
217+
decode(c, frame, pkt, &mut output);
218+
}
219+
}
220+
}
221+
// FLUSH THE DECODER
222+
decode(c, frame, std::ptr::null_mut(), &mut output);
223+
// CLEANUP
224+
sys::av_parser_close(parser);
225+
sys::avcodec_free_context(&mut c);
226+
sys::av_frame_free(&mut frame);
227+
sys::av_packet_free(&mut pkt);
228+
// DONE
229+
output
230+
}
231+
232+
233+
///////////////////////////////////////////////////////////////////////////////
234+
// MAIN
235+
///////////////////////////////////////////////////////////////////////////////
236+
237+
fn main() {
238+
let path = "assets/test/test.h264";
239+
assert!(PathBuf::from(path).exists());
240+
let source = std::fs::read(path).expect("read source file");
241+
unsafe {
242+
decode_h264_video(source);
243+
};
244+
}
245+

0 commit comments

Comments
 (0)