use std::os::raw::c_int;
use std::ptr;
use crate::{
AVCodec, AVCodecContext, AVCodecID, AVCodecParameters, AVDictionary, AVFrame, AVPacket,
avcodec_alloc_context3 as ffi_avcodec_alloc_context3,
avcodec_find_decoder as ffi_avcodec_find_decoder,
avcodec_find_decoder_by_name as ffi_avcodec_find_decoder_by_name,
avcodec_find_encoder as ffi_avcodec_find_encoder,
avcodec_find_encoder_by_name as ffi_avcodec_find_encoder_by_name,
avcodec_flush_buffers as ffi_avcodec_flush_buffers,
avcodec_free_context as ffi_avcodec_free_context, avcodec_open2 as ffi_avcodec_open2,
avcodec_parameters_from_context as ffi_avcodec_parameters_from_context,
avcodec_parameters_to_context as ffi_avcodec_parameters_to_context,
avcodec_receive_frame as ffi_avcodec_receive_frame,
avcodec_receive_packet as ffi_avcodec_receive_packet,
avcodec_send_frame as ffi_avcodec_send_frame, avcodec_send_packet as ffi_avcodec_send_packet,
ensure_initialized,
};
pub unsafe fn find_decoder(codec_id: AVCodecID) -> Option<*const AVCodec> {
ensure_initialized();
let codec = ffi_avcodec_find_decoder(codec_id);
if codec.is_null() { None } else { Some(codec) }
}
pub unsafe fn find_decoder_by_name(name: *const i8) -> Option<*const AVCodec> {
ensure_initialized();
if name.is_null() {
return None;
}
let codec = ffi_avcodec_find_decoder_by_name(name);
if codec.is_null() { None } else { Some(codec) }
}
pub unsafe fn find_encoder(codec_id: AVCodecID) -> Option<*const AVCodec> {
ensure_initialized();
let codec = ffi_avcodec_find_encoder(codec_id);
if codec.is_null() { None } else { Some(codec) }
}
pub unsafe fn find_encoder_by_name(name: *const i8) -> Option<*const AVCodec> {
ensure_initialized();
if name.is_null() {
return None;
}
let codec = ffi_avcodec_find_encoder_by_name(name);
if codec.is_null() { None } else { Some(codec) }
}
pub unsafe fn alloc_context3(codec: *const AVCodec) -> Result<*mut AVCodecContext, c_int> {
ensure_initialized();
let ctx = ffi_avcodec_alloc_context3(codec);
if ctx.is_null() {
Err(crate::error_codes::ENOMEM)
} else {
Ok(ctx)
}
}
pub unsafe fn free_context(ctx: *mut *mut AVCodecContext) {
if !ctx.is_null() && !(*ctx).is_null() {
ffi_avcodec_free_context(ctx);
}
}
pub unsafe fn parameters_from_context(
par: *mut AVCodecParameters,
ctx: *const AVCodecContext,
) -> Result<(), c_int> {
if par.is_null() || ctx.is_null() {
return Err(crate::error_codes::EINVAL);
}
let ret = ffi_avcodec_parameters_from_context(par, ctx);
if ret < 0 { Err(ret) } else { Ok(()) }
}
pub unsafe fn parameters_to_context(
ctx: *mut AVCodecContext,
par: *const AVCodecParameters,
) -> Result<(), c_int> {
if ctx.is_null() || par.is_null() {
return Err(crate::error_codes::EINVAL);
}
let ret = ffi_avcodec_parameters_to_context(ctx, par);
if ret < 0 { Err(ret) } else { Ok(()) }
}
pub unsafe fn open2(
ctx: *mut AVCodecContext,
codec: *const AVCodec,
options: *mut *mut AVDictionary,
) -> Result<(), c_int> {
if ctx.is_null() {
return Err(crate::error_codes::EINVAL);
}
let ret = ffi_avcodec_open2(ctx, codec, options);
if ret < 0 { Err(ret) } else { Ok(()) }
}
pub unsafe fn send_packet(ctx: *mut AVCodecContext, pkt: *const AVPacket) -> Result<(), c_int> {
if ctx.is_null() {
return Err(crate::error_codes::EINVAL);
}
let ret = ffi_avcodec_send_packet(ctx, pkt);
if ret < 0 { Err(ret) } else { Ok(()) }
}
pub unsafe fn receive_frame(ctx: *mut AVCodecContext, frame: *mut AVFrame) -> Result<(), c_int> {
if ctx.is_null() || frame.is_null() {
return Err(crate::error_codes::EINVAL);
}
let ret = ffi_avcodec_receive_frame(ctx, frame);
if ret < 0 { Err(ret) } else { Ok(()) }
}
pub unsafe fn send_frame(ctx: *mut AVCodecContext, frame: *const AVFrame) -> Result<(), c_int> {
if ctx.is_null() {
return Err(crate::error_codes::EINVAL);
}
let ret = ffi_avcodec_send_frame(ctx, frame);
if ret < 0 { Err(ret) } else { Ok(()) }
}
pub unsafe fn receive_packet(ctx: *mut AVCodecContext, pkt: *mut AVPacket) -> Result<(), c_int> {
if ctx.is_null() || pkt.is_null() {
return Err(crate::error_codes::EINVAL);
}
let ret = ffi_avcodec_receive_packet(ctx, pkt);
if ret < 0 { Err(ret) } else { Ok(()) }
}
pub unsafe fn flush_buffers(ctx: *mut AVCodecContext) {
if !ctx.is_null() {
ffi_avcodec_flush_buffers(ctx);
}
}
pub mod hw_config_flags {
pub const HW_DEVICE_CTX: i32 = 1;
pub const HW_FRAMES_REF: i32 = 2;
pub const INTERNAL: i32 = 4;
pub const AD_HOC: i32 = 8;
}
pub mod codec_caps {
pub const EXPERIMENTAL: u32 = 1 << 9;
pub const HARDWARE: u32 = 1 << 18;
pub const HYBRID: u32 = 1 << 19;
pub const VARIABLE_FRAME_SIZE: u32 = 1 << 16;
pub const AVOID_PROBING: u32 = 1 << 17;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::AVCodecID_AV_CODEC_ID_H264;
use std::ffi::CString;
#[test]
fn test_find_decoder_h264() {
unsafe {
let codec = find_decoder(AVCodecID_AV_CODEC_ID_H264);
assert!(codec.is_some(), "H.264 decoder should be found");
}
}
#[test]
fn test_find_decoder_invalid() {
unsafe {
let codec = find_decoder(999_999);
assert!(codec.is_none());
}
}
#[test]
fn test_find_decoder_by_name() {
unsafe {
let name = CString::new("h264").expect("CString creation failed");
let codec = find_decoder_by_name(name.as_ptr());
assert!(codec.is_some(), "H.264 decoder should be found by name");
}
}
#[test]
fn test_find_decoder_by_name_null() {
unsafe {
let codec = find_decoder_by_name(ptr::null());
assert!(codec.is_none());
}
}
#[test]
fn test_find_encoder_h264() {
unsafe {
let _codec = find_encoder(AVCodecID_AV_CODEC_ID_H264);
}
}
#[test]
fn test_find_encoder_by_name_null() {
unsafe {
let codec = find_encoder_by_name(ptr::null());
assert!(codec.is_none());
}
}
#[test]
fn test_alloc_and_free_context() {
unsafe {
let ctx_result = alloc_context3(ptr::null());
assert!(ctx_result.is_ok(), "Context allocation should succeed");
let mut ctx = ctx_result.unwrap();
assert!(!ctx.is_null());
free_context(&mut ctx);
assert!(ctx.is_null(), "Context should be null after free");
}
}
#[test]
fn test_free_context_null_safety() {
unsafe {
free_context(ptr::null_mut());
let mut null_ctx: *mut AVCodecContext = ptr::null_mut();
free_context(&mut null_ctx);
}
}
#[test]
fn test_parameters_to_context_null() {
unsafe {
let result = parameters_to_context(ptr::null_mut(), ptr::null());
assert!(result.is_err());
assert_eq!(result.unwrap_err(), crate::error_codes::EINVAL);
}
}
#[test]
fn test_open2_null() {
unsafe {
let result = open2(ptr::null_mut(), ptr::null(), ptr::null_mut());
assert!(result.is_err());
assert_eq!(result.unwrap_err(), crate::error_codes::EINVAL);
}
}
#[test]
fn test_send_packet_null() {
unsafe {
let result = send_packet(ptr::null_mut(), ptr::null());
assert!(result.is_err());
assert_eq!(result.unwrap_err(), crate::error_codes::EINVAL);
}
}
#[test]
fn test_receive_frame_null() {
unsafe {
let result = receive_frame(ptr::null_mut(), ptr::null_mut());
assert!(result.is_err());
assert_eq!(result.unwrap_err(), crate::error_codes::EINVAL);
}
}
#[test]
fn test_send_frame_null() {
unsafe {
let result = send_frame(ptr::null_mut(), ptr::null());
assert!(result.is_err());
assert_eq!(result.unwrap_err(), crate::error_codes::EINVAL);
}
}
#[test]
fn test_receive_packet_null() {
unsafe {
let result = receive_packet(ptr::null_mut(), ptr::null_mut());
assert!(result.is_err());
assert_eq!(result.unwrap_err(), crate::error_codes::EINVAL);
}
}
#[test]
fn test_flush_buffers_null() {
unsafe {
flush_buffers(ptr::null_mut());
}
}
#[test]
fn test_hw_config_flags() {
assert_eq!(hw_config_flags::HW_DEVICE_CTX, 1);
assert_eq!(hw_config_flags::HW_FRAMES_REF, 2);
assert_eq!(hw_config_flags::INTERNAL, 4);
assert_eq!(hw_config_flags::AD_HOC, 8);
}
#[test]
fn test_codec_caps() {
assert!(codec_caps::EXPERIMENTAL.is_power_of_two());
assert!(codec_caps::HARDWARE.is_power_of_two());
assert!(codec_caps::HYBRID.is_power_of_two());
assert!(codec_caps::VARIABLE_FRAME_SIZE.is_power_of_two());
assert!(codec_caps::AVOID_PROBING.is_power_of_two());
}
}