WIP: Start converting H264 samples to Annex-B NALs
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
parent
7054fa61f2
commit
600029a918
2 changed files with 110 additions and 8 deletions
|
@ -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()
|
||||||
|
|
|
@ -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!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue