capi.rs 14.4 KB
Newer Older
1
2
3
4
5
//! # C API for rav1e
//!
//! [rav1e](https://github.com/xiph/rav1e/) is an [AV1](https://aomediacodec.github.io/av1-spec/)
//! encoder written in [Rust](https://rust-lang.org)
//!
6
//! This is the C-compatible API
7

Luca Barbato's avatar
Luca Barbato committed
8
extern crate rav1e;
9
extern crate libc;
10
extern crate num_traits;
Luca Barbato's avatar
Luca Barbato committed
11
12
13
14
15

use std::slice;
use std::sync::Arc;

use std::ffi::CStr;
16
use std::ffi::CString;
Luca Barbato's avatar
Luca Barbato committed
17
18
19
use std::os::raw::c_char;
use std::os::raw::c_int;

20
21
22
use libc::size_t;
use libc::ptrdiff_t;

23
24
use num_traits::cast::FromPrimitive;

25
26
27
/// Raw video Frame
///
/// It can be allocated throught rav1e_frame_new(), populated using rav1e_frame_fill_plane()
28
/// and freed using rav1e_frame_unref().
29
pub struct Frame(Arc<rav1e::Frame<u16>>);
Luca Barbato's avatar
Luca Barbato committed
30

31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub enum EncoderStatus {
    /// Normal operation
    Success = 0,
    /// The encoder needs more data to produce an output Packet
    /// May be emitted by `Context::receive_packet`  when frame reordering is enabled.
    NeedMoreData,
    /// There are enough Frames queue
    /// May be emitted by `Context::send_frame` when the input queue is constrained
    EnoughData,
    /// The encoder already produced the number of frames requested
    /// May be emitted by `Context::receive_packet` after a flush request had been processed
    /// or the frame limit had been reached.
    LimitReached,
46
47
    /// A Frame had been encoded but not emitted yet
    Encoded,
48
49
50
51
52
53
54
55
56
57
58
59
    /// Generic fatal error
    Failure = -1,
}

impl From<Option<rav1e::EncoderStatus>> for EncoderStatus {
    fn from(status: Option<rav1e::EncoderStatus>) -> Self {
        match status {
            None => EncoderStatus::Success,
            Some(s) => match s {
                rav1e::EncoderStatus::NeedMoreData => EncoderStatus::NeedMoreData,
                rav1e::EncoderStatus::EnoughData => EncoderStatus::EnoughData,
                rav1e::EncoderStatus::LimitReached => EncoderStatus::LimitReached,
60
                rav1e::EncoderStatus::Encoded => EncoderStatus::Encoded,
61
62
63
64
65
                rav1e::EncoderStatus::Failure => EncoderStatus::Failure,
            }
        }
    }
}
66
67
68
69
70
71

/// Encoder configuration
///
/// Instantiate it using rav1e_config_default() and fine-tune it using
/// rav1e_config_parse().
///
72
/// Use rav1e_config_unref() to free its memory.
73
pub struct Config {
Luca Barbato's avatar
Luca Barbato committed
74
75
76
    cfg: rav1e::Config,
}

77
78
79
80
81
/// Encoder context
///
/// Contains the encoding state, it is created by rav1e_context_new() using an
/// Encoder configuration.
///
82
/// Use rav1e_context_unref() to free its memory.
83
pub struct Context {
84
    ctx: rav1e::Context<u16>,
85
    last_err: Option<rav1e::EncoderStatus>,
Luca Barbato's avatar
Luca Barbato committed
86
87
}

88
type FrameType = rav1e::FrameType;
Luca Barbato's avatar
Luca Barbato committed
89

90
91
92
93
/// Encoded Packet
///
/// The encoded packets are retrieved using rav1e_receive_packet().
///
94
/// Use rav1e_packet_unref() to free its memory.
Luca Barbato's avatar
Luca Barbato committed
95
#[repr(C)]
96
97
pub struct Packet {
    /// Encoded data buffer
Luca Barbato's avatar
Luca Barbato committed
98
    pub data: *const u8,
99
    /// Encoded data buffer size
100
    pub len: size_t,
101
    /// Frame sequence number
102
    pub number: u64,
103
104
    /// Frame type
    pub frame_type: FrameType,
Luca Barbato's avatar
Luca Barbato committed
105
106
}

107
type PixelRange=rav1e::PixelRange;
108
type ChromaSamplePosition=rav1e::ChromaSamplePosition;
109
type ChromaSampling=rav1e::ChromaSampling;
110
type MatrixCoefficients=rav1e::MatrixCoefficients;
Luca Barbato's avatar
Luca Barbato committed
111
type ColorPrimaries=rav1e::ColorPrimaries;
112
type TransferCharacteristics=rav1e::TransferCharacteristics;
113
type Rational=rav1e::Rational;
Luca Barbato's avatar
Luca Barbato committed
114
115

#[no_mangle]
116
pub unsafe extern "C" fn rav1e_config_default() -> *mut Config {
Luca Barbato's avatar
Luca Barbato committed
117
    let cfg = rav1e::Config {
118
        enc: rav1e::EncoderConfig::default(),
119
        threads: 0,
Luca Barbato's avatar
Luca Barbato committed
120
121
    };

122
    let c = Box::new(Config {
Luca Barbato's avatar
Luca Barbato committed
123
124
125
126
127
128
        cfg,
    });

    Box::into_raw(c)
}

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

/// Set pixel format of the stream.
///
/// Supported values for subsampling and chromapos are defined by the
/// enum types RaChromaSampling and RaChromaSamplePosition respectively.
/// Valid values for fullrange are 0 and 1.
///
/// Reuturns a negative value on error or 0.
#[no_mangle]
pub unsafe extern "C" fn rav1e_config_set_pixel_format(cfg: *mut Config,
                                                       bit_depth: u8,
                                                       subsampling: ChromaSampling,
                                                       chroma_pos: ChromaSamplePosition,
                                                       pixel_range: PixelRange
) -> c_int {
    if bit_depth != 8 && bit_depth != 10 && bit_depth != 12 {
        return -1
    }
    (*cfg).cfg.enc.bit_depth = bit_depth as usize;

    let subsampling_val = std::mem::transmute::<ChromaSampling, i32>(subsampling);
    if rav1e::ChromaSampling::from_i32(subsampling_val).is_none() {
        return -1
    }
    (*cfg).cfg.enc.chroma_sampling = subsampling;

    let chroma_pos_val = std::mem::transmute::<ChromaSamplePosition, i32>(chroma_pos);
    if rav1e::ChromaSamplePosition::from_i32(chroma_pos_val).is_none() {
        return -1
    }
    (*cfg).cfg.enc.chroma_sample_position = chroma_pos;

    let pixel_range_val = std::mem::transmute::<PixelRange, i32>(pixel_range);
    if rav1e::PixelRange::from_i32(pixel_range_val).is_none() {
        return -1;
    }
    (*cfg).cfg.enc.pixel_range = pixel_range;

    0
}

170
171
172
173
174
175
176
/// Set color properties of the stream.
///
/// Supported values are defined by the enum types
/// RaMatrixCoefficients, RaColorPrimaries, and RaTransferCharacteristics
/// respectively.
///
/// Return a negative value on error or 0.
Luca Barbato's avatar
Luca Barbato committed
177
178
179
180
#[no_mangle]
pub unsafe extern "C" fn rav1e_config_set_color_description(cfg: *mut Config,
                                                            matrix: MatrixCoefficients,
                                                            primaries: ColorPrimaries,
181
182
                                                            transfer: TransferCharacteristics
) -> c_int {
Luca Barbato's avatar
Luca Barbato committed
183
184
185
186
187
    (*cfg).cfg.enc.color_description = Some(rav1e::ColorDescription {
        matrix_coefficients: matrix,
        color_primaries: primaries,
        transfer_characteristics: transfer,
    });
188
189

    if (*cfg).cfg.enc.color_description.is_some() { 0 } else { -1 }
Luca Barbato's avatar
Luca Barbato committed
190
191
}

192
193
194
/// Set the content light level information for HDR10 streams.
///
/// Return a negative value on error or 0.
195
196
197
#[no_mangle]
pub unsafe extern "C" fn rav1e_config_set_content_light(cfg: *mut Config,
                                                        max_content_light_level: u16,
198
199
                                                        max_frame_average_light_level: u16
) -> c_int {
200
201
202
203
    (*cfg).cfg.enc.content_light = Some(rav1e::ContentLight {
        max_content_light_level,
        max_frame_average_light_level,
    });
204
205

    if (*cfg).cfg.enc.content_light.is_some() { 0 } else { -1 }
206
207
}

208
209
210
211
212
213
214
/// Set the mastering display information for HDR10 streams.
///
/// primaries and white_point arguments are RaPoint, containing 0.16 fixed point values.
/// max_luminance is a 24.8 fixed point value.
/// min_luminance is a 18.14 fixed point value.
///
/// Returns a negative value on error or 0.
215
216
217
218
219
#[no_mangle]
pub unsafe extern "C" fn rav1e_config_set_mastering_display(cfg: *mut Config,
                                                            primaries: [rav1e::Point; 3],
                                                            white_point: rav1e::Point,
                                                            max_luminance: u32,
220
221
                                                            min_luminance: u32
) -> c_int {
222
223
224
225
226
227
    (*cfg).cfg.enc.mastering_display = Some(rav1e::MasteringDisplay {
        primaries,
        white_point,
        max_luminance,
        min_luminance,
    });
228
229

    if (*cfg).cfg.enc.mastering_display.is_some() { 0 } else { -1 }
230
231
}

Luca Barbato's avatar
Luca Barbato committed
232
#[no_mangle]
233
pub unsafe extern "C" fn rav1e_config_unref(cfg: *mut Config) {
234
235
236
    if !cfg.is_null() {
        let _ = Box::from_raw(cfg);
    }
Luca Barbato's avatar
Luca Barbato committed
237
238
}

239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
unsafe fn option_match(
    cfg: *mut Config,
    key: *const c_char,
    value: *const c_char
) -> Result<(), ()> {
    let key = CStr::from_ptr(key).to_str().map_err(|_| ())?;
    let value = CStr::from_ptr(value).to_str().map_err(|_| ())?;
    let enc = &mut(*cfg).cfg.enc;

    match key {
        "width" => enc.width = value.parse().map_err(|_| ())?,
        "height" => enc.height = value.parse().map_err(|_| ())?,
        "speed" => enc.speed_settings = rav1e::SpeedSettings::from_preset(value.parse().map_err(|_| ())?),

        "threads" => (*cfg).cfg.threads = value.parse().map_err(|_| ())?,

Derek Buitenhuis's avatar
Derek Buitenhuis committed
255
256
257
        "tile_rows_log2" => enc.tile_rows_log2 = value.parse().map_err(|_| ())?,
        "tile_cols_log2" => enc.tile_cols_log2 = value.parse().map_err(|_| ())?,

258
259
        "tune" => enc.tune = value.parse().map_err(|_| ())?,
        "quantizer" => enc.quantizer = value.parse().map_err(|_| ())?,
Derek Buitenhuis's avatar
Derek Buitenhuis committed
260
        "bitrate" => enc.bitrate = value.parse().map_err(|_| ())?,
261
262
263
264
265
266
267
268
269
270
271

        "key_frame_interval" => enc.max_key_frame_interval = value.parse().map_err(|_| ())?,
        "min_key_frame_interval" => enc.min_key_frame_interval = value.parse().map_err(|_| ())?,
        "low_latency" => enc.low_latency = value.parse().map_err(|_| ())?,

        _ => return Err(())
    }

    Ok(())
}

272
/// Set a configuration parameter using its key and value as string.
273
274
275
276
277
278
///
/// Available keys and values
/// - "quantizer": 0-255, default 100
/// - "speed": 0-10, default 3
/// - "tune": "psnr"-"psychovisual", default "psnr"
///
279
/// Return a negative value on error or 0.
Luca Barbato's avatar
Luca Barbato committed
280
281
#[no_mangle]
pub unsafe extern "C" fn rav1e_config_parse(
282
    cfg: *mut Config,
Luca Barbato's avatar
Luca Barbato committed
283
284
285
    key: *const c_char,
    value: *const c_char,
) -> c_int {
286
    if option_match(cfg, key, value) == Ok(()) { 0 } else { -1 }
Luca Barbato's avatar
Luca Barbato committed
287
288
}

289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
/// Set a configuration parameter using its key and value as integer.
///
/// Available keys and values are the same as rav1e_config_parse()
///
/// Return a negative value on error or 0.
#[no_mangle]
pub unsafe extern "C" fn rav1e_config_parse_int(
    cfg: *mut Config,
    key: *const c_char,
    value: c_int,
) -> c_int {
    let val = CString::new(value.to_string()).unwrap();
    if option_match(cfg, key, val.as_ptr()) == Ok(()) { 0 } else { -1 }
}

304
305
306
/// Generate a new encoding context from a populated encoder configuration
///
/// Multiple contexts can be generated through it.
Luca Barbato's avatar
Luca Barbato committed
307
#[no_mangle]
308
309
pub unsafe extern "C" fn rav1e_context_new(cfg: *const Config) -> *mut Context {
    let ctx = Context {
Luca Barbato's avatar
Luca Barbato committed
310
311
312
313
314
315
316
317
        ctx: (*cfg).cfg.new_context(),
        last_err: None,
    };

    Box::into_raw(Box::new(ctx))
}

#[no_mangle]
318
pub unsafe extern "C" fn rav1e_context_unref(ctx: *mut Context) {
319
320
321
    if !ctx.is_null() {
        let _ = Box::from_raw(ctx);
    }
Luca Barbato's avatar
Luca Barbato committed
322
323
}

324
325
326
327
/// Produce a new frame from the encoding context
///
/// It must be populated using rav1e_frame_fill_plane().
///
328
/// The frame is reference counted and must be released passing it to rav1e_frame_unref(),
329
/// see rav1e_send_frame().
Luca Barbato's avatar
Luca Barbato committed
330
#[no_mangle]
331
pub unsafe extern "C" fn rav1e_frame_new(ctx: *const Context) -> *mut Frame {
Luca Barbato's avatar
Luca Barbato committed
332
    let f = (*ctx).ctx.new_frame();
333
    let frame = Box::new(Frame(f));
Luca Barbato's avatar
Luca Barbato committed
334
335
336
337
338

    Box::into_raw(frame)
}

#[no_mangle]
339
pub unsafe extern "C" fn rav1e_frame_unref(frame: *mut Frame) {
340
341
342
    if !frame.is_null() {
        let _ = Box::from_raw(frame);
    }
Luca Barbato's avatar
Luca Barbato committed
343
344
}

345
346
347
348
/// Send the frame for encoding
///
/// The function increases the frame internal reference count and it can be passed multiple
/// times to different rav1e_send_frame().
349
350
351
352
353
///
/// Returns:
/// - `0` on success,
/// - `> 0` if the input queue is full
/// - `< 0` on unrecoverable failure
Luca Barbato's avatar
Luca Barbato committed
354
#[no_mangle]
355
pub unsafe extern "C" fn rav1e_send_frame(ctx: *mut Context, frame: *const Frame) -> EncoderStatus {
Luca Barbato's avatar
Luca Barbato committed
356
357
358
359
360
361
    let frame = if frame.is_null() {
        None
    } else {
        Some((*frame).0.clone())
    };

362
    let ret = (*ctx)
Luca Barbato's avatar
Luca Barbato committed
363
364
365
        .ctx
        .send_frame(frame)
        .map(|_v| {
366
            None
367
        }).unwrap_or_else(|e| {
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
            Some(e)
        });

    (*ctx).last_err = ret;

    ret.into()
}

/// Return the last encoder status
#[no_mangle]
pub unsafe extern "C" fn rav1e_last_status(ctx: *const Context) -> EncoderStatus {
    (*ctx).last_err.into()
}

/// Return a string matching the EncooderStatus variant.
#[no_mangle]
pub unsafe extern "C" fn rav1e_status_to_str(status: EncoderStatus) -> *mut c_char {
    let status = format!("{:?}", status);
    let cptr = CString::new(status).unwrap().as_ptr();

    libc::strdup(cptr)
Luca Barbato's avatar
Luca Barbato committed
389
390
}

391
/// Receive encoded data
392
393
394
395
396
///
/// Returns:
/// - `0` on success
/// - `> 0` if additional frame data is required
/// - `< 0` on unrecoverable failure
Luca Barbato's avatar
Luca Barbato committed
397
398
#[no_mangle]
pub unsafe extern "C" fn rav1e_receive_packet(
399
400
    ctx: *mut Context,
    pkt: *mut *mut Packet,
401
402
) -> EncoderStatus {
    let ret = (*ctx)
Luca Barbato's avatar
Luca Barbato committed
403
404
405
        .ctx
        .receive_packet()
        .map(|p| {
406
407
408
            let rav1e::Packet { data, number, frame_type, .. } = p;
            let len  = data.len();
            let data = Box::into_raw(data.into_boxed_slice()) as *const u8;
409
            let packet = Packet {
410
411
412
413
                data,
                len,
                number,
                frame_type,
Luca Barbato's avatar
Luca Barbato committed
414
415
            };
            *pkt = Box::into_raw(Box::new(packet));
416
            None
417
        }).unwrap_or_else(|e| {
418
419
420
421
422
423
            Some(e)
        });

    (*ctx).last_err = ret;

    ret.into()
Luca Barbato's avatar
Luca Barbato committed
424
425
426
}

#[no_mangle]
427
pub unsafe extern fn rav1e_packet_unref(pkt: *mut Packet) {
428
    if !pkt.is_null() {
429
430
        let pkt = Box::from_raw(pkt);
        let _ = Box::from_raw(pkt.data as *mut u8);
431
    }
Luca Barbato's avatar
Luca Barbato committed
432
433
}

434
435
436
/// Produce a sequence header matching the current encoding context
///
/// Its format is compatible with the AV1 Matroska and ISOBMFF specification.
437
438
///
/// Use rav1e_container_sequence_header_unref() to free it.
439
#[no_mangle]
440
pub unsafe extern fn rav1e_container_sequence_header(ctx: *mut Context, buf_size: *mut size_t) -> *mut u8 {
441
442
    let buf = (*ctx).ctx.container_sequence_header();

443
    *buf_size = buf.len();
444
445
446
    Box::into_raw(buf.into_boxed_slice()) as *mut u8
}

447
448
#[no_mangle]
pub unsafe extern fn rav1e_container_sequence_header_unref(sequence: *mut u8) {
449
450
451
    if !sequence.is_null() {
        let _ = Box::from_raw(sequence);
    }
452
453
}

454
455
/// Fill a frame plane
///
456
/// Currently the frame contains 3 planes, the first is luminance followed by
457
/// chrominance.
458
459
///
/// The data is copied and this function has to be called for each plane.
460
461
462
463
464
465
466
///
/// frame: A frame provided by rav1e_frame_new()
/// plane: The index of the plane starting from 0
/// data: The data to be copied
/// data_len: Lenght of the buffer
/// stride: Plane line in bytes, including padding
/// bytewidth: Number of bytes per component, either 1 or 2
Luca Barbato's avatar
Luca Barbato committed
467
468
#[no_mangle]
pub unsafe extern "C" fn rav1e_frame_fill_plane(
469
    frame: *mut Frame,
470
    plane: c_int,
Luca Barbato's avatar
Luca Barbato committed
471
    data: *const u8,
472
473
474
    data_len: size_t,
    stride: ptrdiff_t,
    bytewidth: c_int,
Luca Barbato's avatar
Luca Barbato committed
475
) {
476
477
    let f = &mut (*frame).0;
    let input = Arc::make_mut(f);
478
    let data_slice = slice::from_raw_parts(data, data_len as usize);
Luca Barbato's avatar
Luca Barbato committed
479

480
    input.planes[plane as usize].copy_from_raw_u8(data_slice, stride as usize, bytewidth as usize);
Luca Barbato's avatar
Luca Barbato committed
481
}