Skip to content

Commit ecff40e

Browse files
author
alfredoyang
committed
Parse 'pssh' box for eme playback.
1 parent cfd956a commit ecff40e

File tree

6 files changed

+194
-42
lines changed

6 files changed

+194
-42
lines changed

mp4parse/src/boxes.rs

+37-36
Original file line numberDiff line numberDiff line change
@@ -23,40 +23,41 @@ macro_rules! box_database {
2323
}
2424

2525
box_database!(
26-
FileTypeBox 0x66747970, // "ftyp"
27-
MovieBox 0x6d6f6f76, // "moov"
28-
MovieHeaderBox 0x6d766864, // "mvhd"
29-
TrackBox 0x7472616b, // "trak"
30-
TrackHeaderBox 0x746b6864, // "tkhd"
31-
EditBox 0x65647473, // "edts"
32-
MediaBox 0x6d646961, // "mdia"
33-
EditListBox 0x656c7374, // "elst"
34-
MediaHeaderBox 0x6d646864, // "mdhd"
35-
HandlerBox 0x68646c72, // "hdlr"
36-
MediaInformationBox 0x6d696e66, // "minf"
37-
SampleTableBox 0x7374626c, // "stbl"
38-
SampleDescriptionBox 0x73747364, // "stsd"
39-
TimeToSampleBox 0x73747473, // "stts"
40-
SampleToChunkBox 0x73747363, // "stsc"
41-
SampleSizeBox 0x7374737a, // "stsz"
42-
ChunkOffsetBox 0x7374636f, // "stco"
43-
ChunkLargeOffsetBox 0x636f3634, // "co64"
44-
SyncSampleBox 0x73747373, // "stss"
45-
AVCSampleEntry 0x61766331, // "avc1"
46-
AVC3SampleEntry 0x61766333, // "avc3" - Need to check official name in spec.
47-
AVCConfigurationBox 0x61766343, // "avcC"
48-
MP4AudioSampleEntry 0x6d703461, // "mp4a"
49-
ESDBox 0x65736473, // "esds"
50-
VP8SampleEntry 0x76703038, // "vp08"
51-
VP9SampleEntry 0x76703039, // "vp09"
52-
VPCodecConfigurationBox 0x76706343, // "vpcC"
53-
FLACSampleEntry 0x664c6143, // "fLaC"
54-
FLACSpecificBox 0x64664c61, // "dfLa"
55-
OpusSampleEntry 0x4f707573, // "Opus"
56-
OpusSpecificBox 0x644f7073, // "dOps"
57-
ProtectedVisualSampleEntry 0x656e6376, // "encv" - Need to check official name in spec.
58-
ProtectedAudioSampleEntry 0x656e6361, // "enca" - Need to check official name in spec.
59-
MovieExtendsBox 0x6d766578, // "mvex"
60-
MovieExtendsHeaderBox 0x6d656864, // "mehd"
61-
QTWaveAtom 0x77617665, // "wave" - quicktime atom
26+
FileTypeBox 0x66747970, // "ftyp"
27+
MovieBox 0x6d6f6f76, // "moov"
28+
MovieHeaderBox 0x6d766864, // "mvhd"
29+
TrackBox 0x7472616b, // "trak"
30+
TrackHeaderBox 0x746b6864, // "tkhd"
31+
EditBox 0x65647473, // "edts"
32+
MediaBox 0x6d646961, // "mdia"
33+
EditListBox 0x656c7374, // "elst"
34+
MediaHeaderBox 0x6d646864, // "mdhd"
35+
HandlerBox 0x68646c72, // "hdlr"
36+
MediaInformationBox 0x6d696e66, // "minf"
37+
SampleTableBox 0x7374626c, // "stbl"
38+
SampleDescriptionBox 0x73747364, // "stsd"
39+
TimeToSampleBox 0x73747473, // "stts"
40+
SampleToChunkBox 0x73747363, // "stsc"
41+
SampleSizeBox 0x7374737a, // "stsz"
42+
ChunkOffsetBox 0x7374636f, // "stco"
43+
ChunkLargeOffsetBox 0x636f3634, // "co64"
44+
SyncSampleBox 0x73747373, // "stss"
45+
AVCSampleEntry 0x61766331, // "avc1"
46+
AVC3SampleEntry 0x61766333, // "avc3" - Need to check official name in spec.
47+
AVCConfigurationBox 0x61766343, // "avcC"
48+
MP4AudioSampleEntry 0x6d703461, // "mp4a"
49+
ESDBox 0x65736473, // "esds"
50+
VP8SampleEntry 0x76703038, // "vp08"
51+
VP9SampleEntry 0x76703039, // "vp09"
52+
VPCodecConfigurationBox 0x76706343, // "vpcC"
53+
FLACSampleEntry 0x664c6143, // "fLaC"
54+
FLACSpecificBox 0x64664c61, // "dfLa"
55+
OpusSampleEntry 0x4f707573, // "Opus"
56+
OpusSpecificBox 0x644f7073, // "dOps"
57+
ProtectedVisualSampleEntry 0x656e6376, // "encv" - Need to check official name in spec.
58+
ProtectedAudioSampleEntry 0x656e6361, // "enca" - Need to check official name in spec.
59+
MovieExtendsBox 0x6d766578, // "mvex"
60+
MovieExtendsHeaderBox 0x6d656864, // "mehd"
61+
QTWaveAtom 0x77617665, // "wave" - quicktime atom
62+
ProtectionSystemSpecificHeaderBox 0x70737368, // "pssh"
6263
);

mp4parse/src/lib.rs

+63-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
extern crate afl;
1010

1111
extern crate byteorder;
12-
use byteorder::ReadBytesExt;
12+
use byteorder::{ReadBytesExt, WriteBytesExt};
1313
use std::io::{Read, Take};
1414
use std::io::Cursor;
1515
use std::cmp;
@@ -294,13 +294,26 @@ pub struct MovieExtendsBox {
294294
pub fragment_duration: Option<MediaScaledTime>,
295295
}
296296

297+
pub type ByteData = Vec<u8>;
298+
299+
#[derive(Debug, Default)]
300+
pub struct ProtectionSystemSpecificHeaderBox {
301+
pub system_id: ByteData,
302+
pub kid: Vec<ByteData>,
303+
pub data: ByteData,
304+
305+
// The entire pssh box (include header) required by Gecko.
306+
pub box_content: ByteData,
307+
}
308+
297309
/// Internal data structures.
298310
#[derive(Debug, Default)]
299311
pub struct MediaContext {
300312
pub timescale: Option<MediaTimeScale>,
301313
/// Tracks found in the file.
302314
pub tracks: Vec<Track>,
303315
pub mvex: Option<MovieExtendsBox>,
316+
pub psshs: Vec<ProtectionSystemSpecificHeaderBox>
304317
}
305318

306319
impl MediaContext {
@@ -601,13 +614,58 @@ fn read_moov<T: Read>(f: &mut BMFFBox<T>, context: &mut MediaContext) -> Result<
601614
log!("{:?}", mvex);
602615
context.mvex = Some(mvex);
603616
}
617+
BoxType::ProtectionSystemSpecificHeaderBox => {
618+
let pssh = try!(read_pssh(&mut b));
619+
log!("{:?}", pssh);
620+
context.psshs.push(pssh);
621+
}
604622
_ => try!(skip_box_content(&mut b)),
605623
};
606624
check_parser_state!(b.content);
607625
}
608626
Ok(())
609627
}
610628

629+
fn read_pssh<T: Read>(src: &mut BMFFBox<T>) -> Result<ProtectionSystemSpecificHeaderBox> {
630+
let mut box_content = Vec::new();
631+
try!(src.read_to_end(&mut box_content));
632+
633+
let (system_id, kid, data) = {
634+
let pssh = &mut Cursor::new(box_content.as_slice());
635+
636+
let (version, _) = try!(read_fullbox_extra(pssh));
637+
638+
let system_id = try!(read_buf(pssh, 16));
639+
640+
let mut kid: Vec<ByteData> = Vec::new();
641+
if version > 0 {
642+
let mut count = try!(be_i32(pssh));
643+
while count > 0 {
644+
let item = try!(read_buf(pssh, 16));
645+
kid.push(item);
646+
count -= 1;
647+
}
648+
}
649+
650+
let data_size = try!(be_i32(pssh)) as usize;
651+
let data = try!(read_buf(pssh, data_size));
652+
653+
(system_id, kid, data)
654+
};
655+
656+
let mut pssh_box = Vec::new();
657+
try!(write_be_u32(&mut pssh_box, src.head.size as u32));
658+
pssh_box.append(&mut vec![b'p', b's', b's', b'h']);
659+
pssh_box.append(&mut box_content);
660+
661+
Ok(ProtectionSystemSpecificHeaderBox {
662+
system_id: system_id,
663+
kid: kid,
664+
data: data,
665+
box_content: pssh_box,
666+
})
667+
}
668+
611669
fn read_mvex<T: Read>(src: &mut BMFFBox<T>) -> Result<MovieExtendsBox> {
612670
let mut iter = src.box_iter();
613671
let mut fragment_duration = None;
@@ -1702,3 +1760,7 @@ fn be_u32<T: ReadBytesExt>(src: &mut T) -> Result<u32> {
17021760
fn be_u64<T: ReadBytesExt>(src: &mut T) -> Result<u64> {
17031761
src.read_u64::<byteorder::BigEndian>().map_err(From::from)
17041762
}
1763+
1764+
fn write_be_u32<T: WriteBytesExt>(des: &mut T, num: u32) -> Result<()> {
1765+
des.write_u32::<byteorder::BigEndian>(num).map_err(From::from)
1766+
}
Binary file not shown.

mp4parse/tests/public.rs

+32
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,35 @@ fn public_api() {
9595
}
9696
}
9797
}
98+
99+
#[test]
100+
fn public_cenc() {
101+
let mut fd = File::open("tests/bipbop_480wp_1001kbps-cenc-video-key1-init.mp4").expect("Unknown file");
102+
let mut buf = Vec::new();
103+
fd.read_to_end(&mut buf).expect("File error");
104+
105+
let mut c = Cursor::new(&buf);
106+
let mut context = mp4::MediaContext::new();
107+
mp4::read_mp4(&mut c, &mut context).expect("read_mp4 failed");
108+
for track in context.tracks {
109+
assert_eq!(track.codec_type, mp4::CodecType::EncryptedVideo);
110+
}
111+
112+
let system_id = vec![0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b];
113+
114+
let kid = vec![0x7e, 0x57, 0x1d, 0x03, 0x7e, 0x57, 0x1d, 0x03, 0x7e, 0x57, 0x1d, 0x03, 0x7e, 0x57, 0x1d, 0x11];
115+
116+
let pssh_box = vec![0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68, 0x01, 0x00, 0x00, 0x00, 0x10, 0x77,
117+
0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b, 0x00, 0x00,
118+
0x00, 0x01, 0x7e, 0x57, 0x1d, 0x03, 0x7e, 0x57, 0x1d, 0x03, 0x7e, 0x57, 0x1d, 0x03, 0x7e, 0x57,
119+
0x1d, 0x11, 0x00, 0x00, 0x00, 0x00];
120+
121+
for pssh in context.psshs {
122+
assert_eq!(pssh.system_id, system_id);
123+
for kid_id in pssh.kid {
124+
assert_eq!(kid_id, kid);
125+
}
126+
assert_eq!(pssh.data.len(), 0);
127+
assert_eq!(pssh.box_content, pssh_box);
128+
}
129+
}

mp4parse_capi/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ exclude = [
2121
build = "build.rs"
2222

2323
[dependencies]
24+
byteorder = "0.5.0"
2425
"mp4parse" = {version = "0.6.0", path = "../mp4parse"}
2526

2627
[build-dependencies]

mp4parse_capi/src/lib.rs

+61-5
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,11 @@
3535
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
3636

3737
extern crate mp4parse;
38+
extern crate byteorder;
3839

3940
use std::io::Read;
4041
use std::collections::HashMap;
42+
use byteorder::WriteBytesExt;
4143

4244
// Symbols we need from our rust api.
4345
use mp4parse::MediaContext;
@@ -103,20 +105,33 @@ pub struct mp4parse_track_info {
103105
}
104106

105107
#[repr(C)]
106-
pub struct mp4parse_codec_specific_config {
108+
pub struct mp4parse_byte_data {
107109
pub length: u32,
108110
pub data: *const u8,
109111
}
110112

111-
impl Default for mp4parse_codec_specific_config {
113+
impl Default for mp4parse_byte_data {
112114
fn default() -> Self {
113-
mp4parse_codec_specific_config {
115+
mp4parse_byte_data {
114116
length: 0,
115117
data: std::ptr::null_mut(),
116118
}
117119
}
118120
}
119121

122+
impl mp4parse_byte_data {
123+
fn set_data(&mut self, data: &Vec<u8>) {
124+
self.length = data.len() as u32;
125+
self.data = data.as_ptr();
126+
}
127+
}
128+
129+
#[repr(C)]
130+
#[derive(Default)]
131+
pub struct mp4parse_pssh_info {
132+
pub data: mp4parse_byte_data,
133+
}
134+
120135
#[derive(Default)]
121136
#[repr(C)]
122137
pub struct mp4parse_track_audio_info {
@@ -126,7 +141,7 @@ pub struct mp4parse_track_audio_info {
126141
// TODO(kinetik):
127142
// int32_t profile;
128143
// int32_t extended_profile; // check types
129-
codec_specific_config: mp4parse_codec_specific_config,
144+
codec_specific_config: mp4parse_byte_data,
130145
}
131146

132147
#[repr(C)]
@@ -154,6 +169,7 @@ struct Wrap {
154169
io: mp4parse_io,
155170
poisoned: bool,
156171
opus_header: HashMap<u32, Vec<u8>>,
172+
pssh_data: Vec<u8>,
157173
}
158174

159175
#[repr(C)]
@@ -184,6 +200,10 @@ impl mp4parse_parser {
184200
fn opus_header_mut(&mut self) -> &mut HashMap<u32, Vec<u8>> {
185201
&mut self.0.opus_header
186202
}
203+
204+
fn pssh_data_mut(&mut self) -> &mut Vec<u8> {
205+
&mut self.0.pssh_data
206+
}
187207
}
188208

189209
#[repr(C)]
@@ -228,6 +248,7 @@ pub unsafe extern fn mp4parse_new(io: *const mp4parse_io) -> *mut mp4parse_parse
228248
io: (*io).clone(),
229249
poisoned: false,
230250
opus_header: HashMap::new(),
251+
pssh_data: Vec::new(),
231252
}));
232253
Box::into_raw(parser)
233254
}
@@ -529,6 +550,7 @@ pub unsafe extern fn mp4parse_get_track_video_info(parser: *mut mp4parse_parser,
529550
MP4PARSE_OK
530551
}
531552

553+
/// Fill the supplied `mp4parse_fragment_info` with metadata from fragmented file.
532554
#[no_mangle]
533555
pub unsafe extern fn mp4parse_get_fragment_info(parser: *mut mp4parse_parser, info: *mut mp4parse_fragment_info) -> mp4parse_error {
534556
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
555577
MP4PARSE_OK
556578
}
557579

558-
// A fragmented file needs mvex table and contains no data in stts, stsc, and stco boxes.
580+
/// A fragmented file needs mvex table and contains no data in stts, stsc, and stco boxes.
559581
#[no_mangle]
560582
pub unsafe extern fn mp4parse_is_fragmented(parser: *mut mp4parse_parser, track_id: u32, fragmented: *mut u8) -> mp4parse_error {
561583
if parser.is_null() || (*parser).poisoned() {
@@ -581,6 +603,40 @@ pub unsafe extern fn mp4parse_is_fragmented(parser: *mut mp4parse_parser, track_
581603
MP4PARSE_OK
582604
}
583605

606+
/// Get pssh system id and pssh box content for eme playback.
607+
#[no_mangle]
608+
pub unsafe extern fn mp4parse_get_pssh_info(parser: *mut mp4parse_parser, info: *mut mp4parse_pssh_info) -> mp4parse_error {
609+
if parser.is_null() || info.is_null() || (*parser).poisoned() {
610+
return MP4PARSE_ERROR_BADARG;
611+
}
612+
613+
let context = (*parser).context_mut();
614+
let pssh_data = (*parser).pssh_data_mut();
615+
let info: &mut mp4parse_pssh_info = &mut *info;
616+
617+
pssh_data.clear();
618+
for pssh in &context.psshs {
619+
let mut data_len = Vec::new();
620+
match data_len.write_u32::<byteorder::NativeEndian>(pssh.box_content.len() as u32) {
621+
Err(_) => {
622+
return MP4PARSE_ERROR_IO;
623+
},
624+
_ => (),
625+
}
626+
// pssh data format in gecko:
627+
// system_id
628+
// pssh size (in native endian)
629+
// pssh box content (including header)
630+
pssh_data.extend_from_slice(pssh.system_id.as_slice());
631+
pssh_data.extend_from_slice(data_len.as_slice());
632+
pssh_data.extend_from_slice(pssh.box_content.as_slice());
633+
}
634+
635+
info.data.set_data(&pssh_data);
636+
637+
MP4PARSE_OK
638+
}
639+
584640
#[cfg(test)]
585641
extern fn panic_read(_: *mut u8, _: usize, _: *mut std::os::raw::c_void) -> isize {
586642
panic!("panic_read shouldn't be called in these tests");

0 commit comments

Comments
 (0)