-
Notifications
You must be signed in to change notification settings - Fork 64
Parse 'pssh' box for eme playback. #43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,7 +9,7 @@ | |
extern crate afl; | ||
|
||
extern crate byteorder; | ||
use byteorder::ReadBytesExt; | ||
use byteorder::{ReadBytesExt, WriteBytesExt}; | ||
use std::io::{Read, Take}; | ||
use std::io::Cursor; | ||
use std::cmp; | ||
|
@@ -294,13 +294,26 @@ pub struct MovieExtendsBox { | |
pub fragment_duration: Option<MediaScaledTime>, | ||
} | ||
|
||
pub type ByteData = Vec<u8>; | ||
|
||
#[derive(Debug, Default)] | ||
pub struct ProtectionSystemSpecificHeaderBox { | ||
pub system_id: ByteData, | ||
pub kid: Vec<ByteData>, | ||
pub data: ByteData, | ||
|
||
// The entire pssh box (include header) required by Gecko. | ||
pub box_content: ByteData, | ||
} | ||
|
||
/// Internal data structures. | ||
#[derive(Debug, Default)] | ||
pub struct MediaContext { | ||
pub timescale: Option<MediaTimeScale>, | ||
/// Tracks found in the file. | ||
pub tracks: Vec<Track>, | ||
pub mvex: Option<MovieExtendsBox>, | ||
pub psshs: Vec<ProtectionSystemSpecificHeaderBox> | ||
} | ||
|
||
impl MediaContext { | ||
|
@@ -601,13 +614,58 @@ fn read_moov<T: Read>(f: &mut BMFFBox<T>, context: &mut MediaContext) -> Result< | |
log!("{:?}", mvex); | ||
context.mvex = Some(mvex); | ||
} | ||
BoxType::ProtectionSystemSpecificHeaderBox => { | ||
let pssh = try!(read_pssh(&mut b)); | ||
log!("{:?}", pssh); | ||
context.psshs.push(pssh); | ||
} | ||
_ => try!(skip_box_content(&mut b)), | ||
}; | ||
check_parser_state!(b.content); | ||
} | ||
Ok(()) | ||
} | ||
|
||
fn read_pssh<T: Read>(src: &mut BMFFBox<T>) -> Result<ProtectionSystemSpecificHeaderBox> { | ||
let mut box_content = Vec::new(); | ||
try!(src.read_to_end(&mut box_content)); | ||
|
||
let (system_id, kid, data) = { | ||
let pssh = &mut Cursor::new(box_content.as_slice()); | ||
|
||
let (version, _) = try!(read_fullbox_extra(pssh)); | ||
|
||
let system_id = try!(read_buf(pssh, 16)); | ||
|
||
let mut kid: Vec<ByteData> = Vec::new(); | ||
if version > 0 { | ||
let mut count = try!(be_i32(pssh)); | ||
while count > 0 { | ||
let item = try!(read_buf(pssh, 16)); | ||
kid.push(item); | ||
count -= 1; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think |
||
} | ||
|
||
let data_size = try!(be_i32(pssh)) as usize; | ||
let data = try!(read_buf(pssh, data_size)); | ||
|
||
(system_id, kid, data) | ||
}; | ||
|
||
let mut pssh_box = Vec::new(); | ||
try!(write_be_u32(&mut pssh_box, src.head.size as u32)); | ||
pssh_box.append(&mut vec![b'p', b's', b's', b'h']); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can NB there's also |
||
pssh_box.append(&mut box_content); | ||
|
||
Ok(ProtectionSystemSpecificHeaderBox { | ||
system_id: system_id, | ||
kid: kid, | ||
data: data, | ||
box_content: pssh_box, | ||
}) | ||
} | ||
|
||
fn read_mvex<T: Read>(src: &mut BMFFBox<T>) -> Result<MovieExtendsBox> { | ||
let mut iter = src.box_iter(); | ||
let mut fragment_duration = None; | ||
|
@@ -1702,3 +1760,7 @@ fn be_u32<T: ReadBytesExt>(src: &mut T) -> Result<u32> { | |
fn be_u64<T: ReadBytesExt>(src: &mut T) -> Result<u64> { | ||
src.read_u64::<byteorder::BigEndian>().map_err(From::from) | ||
} | ||
|
||
fn write_be_u32<T: WriteBytesExt>(des: &mut T, num: u32) -> Result<()> { | ||
des.write_u32::<byteorder::BigEndian>(num).map_err(From::from) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,9 +35,11 @@ | |
// file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
|
||
extern crate mp4parse; | ||
extern crate byteorder; | ||
|
||
use std::io::Read; | ||
use std::collections::HashMap; | ||
use byteorder::WriteBytesExt; | ||
|
||
// Symbols we need from our rust api. | ||
use mp4parse::MediaContext; | ||
|
@@ -103,20 +105,33 @@ pub struct mp4parse_track_info { | |
} | ||
|
||
#[repr(C)] | ||
pub struct mp4parse_codec_specific_config { | ||
pub struct mp4parse_byte_data { | ||
pub length: u32, | ||
pub data: *const u8, | ||
} | ||
|
||
impl Default for mp4parse_codec_specific_config { | ||
impl Default for mp4parse_byte_data { | ||
fn default() -> Self { | ||
mp4parse_codec_specific_config { | ||
mp4parse_byte_data { | ||
length: 0, | ||
data: std::ptr::null_mut(), | ||
} | ||
} | ||
} | ||
|
||
impl mp4parse_byte_data { | ||
fn set_data(&mut self, data: &Vec<u8>) { | ||
self.length = data.len() as u32; | ||
self.data = data.as_ptr(); | ||
} | ||
} | ||
|
||
#[repr(C)] | ||
#[derive(Default)] | ||
pub struct mp4parse_pssh_info { | ||
pub data: mp4parse_byte_data, | ||
} | ||
|
||
#[derive(Default)] | ||
#[repr(C)] | ||
pub struct mp4parse_track_audio_info { | ||
|
@@ -126,7 +141,7 @@ pub struct mp4parse_track_audio_info { | |
// TODO(kinetik): | ||
// int32_t profile; | ||
// int32_t extended_profile; // check types | ||
codec_specific_config: mp4parse_codec_specific_config, | ||
codec_specific_config: mp4parse_byte_data, | ||
} | ||
|
||
#[repr(C)] | ||
|
@@ -154,6 +169,7 @@ struct Wrap { | |
io: mp4parse_io, | ||
poisoned: bool, | ||
opus_header: HashMap<u32, Vec<u8>>, | ||
pssh_data: Vec<u8>, | ||
} | ||
|
||
#[repr(C)] | ||
|
@@ -184,6 +200,10 @@ impl mp4parse_parser { | |
fn opus_header_mut(&mut self) -> &mut HashMap<u32, Vec<u8>> { | ||
&mut self.0.opus_header | ||
} | ||
|
||
fn pssh_data_mut(&mut self) -> &mut Vec<u8> { | ||
&mut self.0.pssh_data | ||
} | ||
} | ||
|
||
#[repr(C)] | ||
|
@@ -228,6 +248,7 @@ pub unsafe extern fn mp4parse_new(io: *const mp4parse_io) -> *mut mp4parse_parse | |
io: (*io).clone(), | ||
poisoned: false, | ||
opus_header: HashMap::new(), | ||
pssh_data: Vec::new(), | ||
})); | ||
Box::into_raw(parser) | ||
} | ||
|
@@ -529,6 +550,7 @@ pub unsafe extern fn mp4parse_get_track_video_info(parser: *mut mp4parse_parser, | |
MP4PARSE_OK | ||
} | ||
|
||
/// Fill the supplied `mp4parse_fragment_info` with metadata from fragmented file. | ||
#[no_mangle] | ||
pub unsafe extern fn mp4parse_get_fragment_info(parser: *mut mp4parse_parser, info: *mut mp4parse_fragment_info) -> mp4parse_error { | ||
if parser.is_null() || info.is_null() || (*parser).poisoned() { | ||
|
@@ -555,7 +577,7 @@ pub unsafe extern fn mp4parse_get_fragment_info(parser: *mut mp4parse_parser, in | |
MP4PARSE_OK | ||
} | ||
|
||
// A fragmented file needs mvex table and contains no data in stts, stsc, and stco boxes. | ||
/// A fragmented file needs mvex table and contains no data in stts, stsc, and stco boxes. | ||
#[no_mangle] | ||
pub unsafe extern fn mp4parse_is_fragmented(parser: *mut mp4parse_parser, track_id: u32, fragmented: *mut u8) -> mp4parse_error { | ||
if parser.is_null() || (*parser).poisoned() { | ||
|
@@ -581,6 +603,40 @@ pub unsafe extern fn mp4parse_is_fragmented(parser: *mut mp4parse_parser, track_ | |
MP4PARSE_OK | ||
} | ||
|
||
/// Get pssh system id and pssh box content for eme playback. | ||
#[no_mangle] | ||
pub unsafe extern fn mp4parse_get_pssh_info(parser: *mut mp4parse_parser, info: *mut mp4parse_pssh_info) -> mp4parse_error { | ||
if parser.is_null() || info.is_null() || (*parser).poisoned() { | ||
return MP4PARSE_ERROR_BADARG; | ||
} | ||
|
||
let context = (*parser).context_mut(); | ||
let pssh_data = (*parser).pssh_data_mut(); | ||
let info: &mut mp4parse_pssh_info = &mut *info; | ||
|
||
pssh_data.clear(); | ||
for pssh in &context.psshs { | ||
let mut data_len = Vec::new(); | ||
match data_len.write_u32::<byteorder::NativeEndian>(pssh.box_content.len() as u32) { | ||
Err(_) => { | ||
return MP4PARSE_ERROR_IO; | ||
}, | ||
_ => (), | ||
} | ||
// pssh data format in gecko: | ||
// system_id | ||
// pssh size (in native endian) | ||
// pssh box content (including header) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might be better to put this description in the function-level doc-comment above, since it's something a caller would need to know, not just an internal implementation detail. |
||
pssh_data.extend_from_slice(pssh.system_id.as_slice()); | ||
pssh_data.extend_from_slice(data_len.as_slice()); | ||
pssh_data.extend_from_slice(pssh.box_content.as_slice()); | ||
} | ||
|
||
info.data.set_data(&pssh_data); | ||
|
||
MP4PARSE_OK | ||
} | ||
|
||
#[cfg(test)] | ||
extern fn panic_read(_: *mut u8, _: usize, _: *mut std::os::raw::c_void) -> isize { | ||
panic!("panic_read shouldn't be called in these tests"); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You might save some allocations here if you use
Vec::with_capacity(src.head.size);
but it probably doesn't matter.