WIP: Start converting H264 samples to Annex-B NALs

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Antonio Scandurra 2022-08-31 18:02:05 +02:00
parent 7054fa61f2
commit 600029a918
2 changed files with 110 additions and 8 deletions

View file

@ -26,6 +26,7 @@ fn main() {
.allowlist_var("VTEncodeInfoFlags_.*") .allowlist_var("VTEncodeInfoFlags_.*")
.allowlist_var("kCMVideoCodecType_.*") .allowlist_var("kCMVideoCodecType_.*")
.allowlist_var("kCMTime.*") .allowlist_var("kCMTime.*")
.allowlist_var("kCMSampleAttachmentKey_.*")
.parse_callbacks(Box::new(bindgen::CargoCallbacks)) .parse_callbacks(Box::new(bindgen::CargoCallbacks))
.layout_tests(false) .layout_tests(false)
.generate() .generate()

View file

@ -208,7 +208,7 @@ pub mod core_media {
impl_CFTypeDescription, impl_TCFType, impl_CFTypeDescription, impl_TCFType,
string::CFString, string::CFString,
}; };
use std::ffi::c_void; use std::{ffi::c_void, ptr};
#[repr(C)] #[repr(C)]
pub struct __CMSampleBuffer(c_void); pub struct __CMSampleBuffer(c_void);
@ -261,6 +261,14 @@ pub mod core_media {
} }
} }
} }
pub fn format_description(&self) -> CMFormatDescription {
unsafe {
CMFormatDescription::wrap_under_get_rule(CMSampleBufferGetFormatDescription(
self.as_concrete_TypeRef(),
))
}
}
} }
#[link(name = "CoreMedia", kind = "framework")] #[link(name = "CoreMedia", kind = "framework")]
@ -276,6 +284,71 @@ pub mod core_media {
index: CMItemIndex, index: CMItemIndex,
timing_info_out: *mut CMSampleTimingInfo, timing_info_out: *mut CMSampleTimingInfo,
) -> OSStatus; ) -> OSStatus;
fn CMSampleBufferGetFormatDescription(buffer: CMSampleBufferRef) -> CMFormatDescriptionRef;
}
#[repr(C)]
pub struct __CMFormatDescription(c_void);
// The ref type must be a pointer to the underlying struct.
pub type CMFormatDescriptionRef = *const __CMFormatDescription;
declare_TCFType!(CMFormatDescription, CMFormatDescriptionRef);
impl_TCFType!(
CMFormatDescription,
CMFormatDescriptionRef,
CMFormatDescriptionGetTypeID
);
impl_CFTypeDescription!(CMFormatDescription);
impl CMFormatDescription {
pub fn h264_parameter_set_count(&self) -> usize {
unsafe {
let mut count = 0;
let result = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(
self.as_concrete_TypeRef(),
0,
ptr::null_mut(),
ptr::null_mut(),
&mut count,
ptr::null_mut(),
);
assert_eq!(result, 0);
count
}
}
pub fn h264_parameter_set_at_index(&self, index: usize) -> Result<&[u8]> {
unsafe {
let mut bytes = ptr::null();
let mut len = 0;
let result = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(
self.as_concrete_TypeRef(),
index,
&mut bytes,
&mut len,
ptr::null_mut(),
ptr::null_mut(),
);
if result == 0 {
Ok(std::slice::from_raw_parts(bytes, len))
} else {
Err(anyhow!("error getting parameter set, code: {}", result))
}
}
}
}
#[link(name = "CoreMedia", kind = "framework")]
extern "C" {
fn CMFormatDescriptionGetTypeID() -> CFTypeID;
fn CMVideoFormatDescriptionGetH264ParameterSetAtIndex(
video_desc: CMFormatDescriptionRef,
parameter_set_index: usize,
parameter_set_pointer_out: *mut *const u8,
parameter_set_size_out: *mut usize,
parameter_set_count_out: *mut usize,
NALUnitHeaderLengthOut: *mut isize,
) -> OSStatus;
} }
} }
@ -284,15 +357,17 @@ pub mod video_toolbox {
use super::*; use super::*;
use crate::{ use crate::{
core_media::{CMSampleBufferRef, CMTime, CMVideoCodecType}, core_media::{CMSampleBuffer, CMSampleBufferRef, CMTime, CMVideoCodecType},
core_video::CVImageBufferRef, core_video::CVImageBufferRef,
}; };
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use bindings::VTEncodeInfoFlags; use bindings::VTEncodeInfoFlags;
use core_foundation::{ use core_foundation::{
base::OSStatus, base::OSStatus,
dictionary::{CFDictionary, CFDictionaryRef, CFMutableDictionary}, dictionary::CFDictionaryRef,
mach_port::CFAllocatorRef, mach_port::CFAllocatorRef,
number::{CFBooleanGetValue, CFBooleanRef},
string::CFStringRef,
}; };
use std::ptr; use std::ptr;
@ -343,13 +418,39 @@ pub mod video_toolbox {
} }
} }
extern "C" fn output( unsafe extern "C" fn output(
outputCallbackRefCon: *mut c_void, output_callback_ref_con: *mut c_void,
sourceFrameRefCon: *mut c_void, source_frame_ref_con: *mut c_void,
status: OSStatus, status: OSStatus,
infoFlags: VTEncodeInfoFlags, info_flags: VTEncodeInfoFlags,
sampleBuffer: CMSampleBufferRef, sample_buffer: CMSampleBufferRef,
) { ) {
if status != 0 {
println!("error encoding frame, code: {}", status);
return;
}
let sample_buffer = CMSampleBuffer::wrap_under_get_rule(sample_buffer);
let mut is_iframe = false;
let attachments = sample_buffer.attachments();
if let Some(attachments) = attachments.first() {
is_iframe = attachments
.find(bindings::kCMSampleAttachmentKey_NotSync as CFStringRef)
.map_or(true, |not_sync| {
CFBooleanGetValue(*not_sync as CFBooleanRef)
});
}
const START_CODE: [u8; 4] = [0x00, 0x00, 0x00, 0x01];
if is_iframe {
let format_description = sample_buffer.format_description();
for ix in 0..format_description.h264_parameter_set_count() {
let parameter_set = format_description.h264_parameter_set_at_index(ix);
stream.extend(START_CODE);
stream.extend(parameter_set);
}
}
println!("YO!"); println!("YO!");
} }