Skip to content

Commit

Permalink
allow overriding pixel aspect ratio in sample box
Browse files Browse the repository at this point in the history
  • Loading branch information
scottlamb committed Aug 14, 2024
1 parent 0f6e81a commit d46acba
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 104 deletions.
9 changes: 5 additions & 4 deletions examples/client/src/mp4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,13 +382,14 @@ impl<W: AsyncWrite + AsyncSeek + Send + Unpin> Mp4Writer<W> {
buf.put_u32(0); // version
buf.put_u32(u32::try_from(self.video_params.len())?); // entry_count
for p in &self.video_params {
let e = p.sample_entry().ok_or_else(|| {
let e = p.sample_entry().build().map_err(|e| {
anyhow!(
"unable to produce VisualSampleEntry for {} stream",
p.rfc6381_codec()
"unable to produce VisualSampleEntry for {} stream: {}",
p.rfc6381_codec(),
e,
)
})?;
buf.extend_from_slice(e);
buf.extend_from_slice(&e);
}
});
self.video_trak.write_common_stbl_parts(buf)?;
Expand Down
4 changes: 2 additions & 2 deletions src/codec/aac.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ fn make_sample_entry(
// Write an MP4AudioSampleEntry (`mp4a`), as in ISO/IEC 14496-14 section 5.6.1.
// It's based on AudioSampleEntry, ISO/IEC 14496-12 section 12.2.3.2,
// in turn based on SampleEntry, ISO/IEC 14496-12 section 8.5.2.2.
write_mp4_box!(&mut buf, b"mp4a", {
write_mp4_box!(&mut buf, *b"mp4a", {
buf.extend_from_slice(&[
0, 0, 0, 0, // SampleEntry.reserved
0, 0, 0, 1, // SampleEntry.reserved, SampleEntry.data_reference_index (1)
Expand All @@ -209,7 +209,7 @@ fn make_sample_entry(
buf.put_u32(u32::from(sampling_frequency) << 16);

// Write the embedded ESDBox (`esds`), as in ISO/IEC 14496-14 section 5.6.1.
write_mp4_box!(&mut buf, b"esds", {
write_mp4_box!(&mut buf, *b"esds", {
buf.put_u32(0); // version

write_mpeg4_descriptor!(&mut buf, 0x03 /* ES_DescrTag */, {
Expand Down
30 changes: 11 additions & 19 deletions src/codec/h264.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ use h264_reader::nal::{NalHeader, UnitType};
use log::{debug, log_enabled, trace};

use crate::{
codec::write_visual_sample_entry_body,
rtp::{ReceivedPacket, ReceivedPacketBuilder},
Error, Timestamp,
};
Expand Down Expand Up @@ -706,22 +705,6 @@ impl<'a, R: h264_reader::rbsp::BitRead> h264_reader::rbsp::BitRead for TolerantB
}
}

/// Writes an `avc1` / `AVCSampleEntry` as in ISO/IEC 14496-15 section 5.4.2.1.
fn make_video_sample_entry(pixel_dimensions: (u32, u32), extra_data: &[u8]) -> Option<Vec<u8>> {
let pixel_dimensions = (
u16::try_from(pixel_dimensions.0).ok()?,
u16::try_from(pixel_dimensions.1).ok()?,
);
let mut buf = Vec::new();
write_mp4_box!(&mut buf, b"avc1", {
write_visual_sample_entry_body(&mut buf, pixel_dimensions);
write_mp4_box!(&mut buf, b"avcC", {
buf.extend_from_slice(extra_data);
});
});
Some(buf)
}

impl InternalParameters {
/// Parses metadata from the `format-specific-params` of a SDP `fmtp` media attribute.
fn parse_format_specific_params(format_specific_params: &str) -> Result<Self, String> {
Expand Down Expand Up @@ -800,6 +783,16 @@ impl InternalParameters {
let pixel_dimensions = sps
.pixel_dimensions()
.map_err(|e| format!("SPS has invalid pixel dimensions: {e:?}"))?;
let e = |_| {
format!(
"SPS has invalid pixel dimensions: {}x{} is too large",
pixel_dimensions.0, pixel_dimensions.1
)
};
let pixel_dimensions = (
u16::try_from(pixel_dimensions.0).map_err(e)?,
u16::try_from(pixel_dimensions.1).map_err(e)?,
);

// Create the AVCDecoderConfiguration, ISO/IEC 14496-15 section 5.2.4.1.
// The beginning of the AVCDecoderConfiguration takes a few values from
Expand Down Expand Up @@ -861,15 +854,14 @@ impl InternalParameters {
let avc_decoder_config = avc_decoder_config.freeze();
let sps_nal = avc_decoder_config.slice(sps_nal_start..sps_nal_end);
let pps_nal = avc_decoder_config.slice(pps_nal_start..pps_nal_end);
let sample_entry = make_video_sample_entry(pixel_dimensions, &avc_decoder_config);
Ok(InternalParameters {
generic_parameters: super::VideoParameters {
rfc6381_codec,
pixel_dimensions,
pixel_aspect_ratio,
frame_rate,
extra_data: avc_decoder_config,
sample_entry,
codec: super::VideoCodec::H264,
},
sps_nal,
pps_nal,
Expand Down
78 changes: 36 additions & 42 deletions src/codec/jpeg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
use bytes::{Buf, Bytes};

use crate::{codec::write_visual_sample_entry_body, rtp::ReceivedPacket, PacketContext, Timestamp};
use crate::{rtp::ReceivedPacket, PacketContext, Timestamp};

use super::{VideoFrame, VideoParameters};

Expand Down Expand Up @@ -446,12 +446,12 @@ impl Depacketizer {
start_ctx: ctx,
timestamp,
parameters: Some(VideoParameters {
pixel_dimensions: (u32::from(width), u32::from(height)),
pixel_dimensions: (width, height),
rfc6381_codec: "mp4v.6C".to_owned(),
pixel_aspect_ratio: None,
frame_rate: None,
extra_data: Bytes::new(),
sample_entry: Some(make_video_sample_entry(width, height)),
codec: super::VideoCodec::Jpeg,
}),
});
}
Expand Down Expand Up @@ -524,51 +524,45 @@ impl Depacketizer {
}
}

fn make_video_sample_entry(width: u16, height: u16) -> Vec<u8> {
let mut buf = Vec::new();

// Write an MP4VisualSampleEntry (`mp4v`), as in ISO/IEC 14496-14 section 5.6.1.
// It's based on VisualSampleEntry, ISO/IEC 14496-12 section 12.1.3.
// in turn based on SampleEntry, ISO/IEC 14496-12 section 8.5.2.2.
write_mp4_box!(&mut buf, b"mp4v", {
write_visual_sample_entry_body(&mut buf, (width, height));

// Write the embedded ESDBox (`esds`), as in ISO/IEC 14496-14 section 5.6.1.
write_mp4_box!(&mut buf, b"esds", {
buf.extend_from_slice(&0u32.to_be_bytes()[..]); // version
write_mpeg4_descriptor!(&mut buf, 0x03 /* ES_DescrTag */, {
// The ESDBox contains an ES_Descriptor, defined in ISO/IEC 14496-1 section 8.3.3.
// ISO/IEC 14496-14 section 3.1.2 has advice on how to set its
// fields within the scope of a .mp4 file.
/// Writes the embedded ESDBox (`esds`), as in ISO/IEC 14496-14 section 5.6.1.
///
/// This is actually entirely static, but we construct it at runtime with the
/// `write_mp4_box!` and `write_mpeg4_descriptor!` macros for readability.
#[cfg(feature = "unstable-sample-entry")]
pub(super) fn append_esds(buf: &mut Vec<u8>) {
write_mp4_box!(buf, *b"esds", {
buf.extend_from_slice(&0u32.to_be_bytes()[..]); // version
write_mpeg4_descriptor!(buf, 0x03 /* ES_DescrTag */, {
// The ESDBox contains an ES_Descriptor, defined in ISO/IEC 14496-1 section 8.3.3.
// ISO/IEC 14496-14 section 3.1.2 has advice on how to set its
// fields within the scope of a .mp4 file.
buf.extend_from_slice(&[
0, 0, // ES_ID=0
0x00, // streamDependenceFlag, URL_Flag, OCRStreamFlag, streamPriority.
]);

// DecoderConfigDescriptor, defined in ISO/IEC 14496-1 section 7.2.6.6.
write_mpeg4_descriptor!(buf, 0x04 /* DecoderConfigDescrTag */, {
buf.extend_from_slice(&[
0, 0, // ES_ID=0
0x00, // streamDependenceFlag, URL_Flag, OCRStreamFlag, streamPriority.
0x6C, // objectTypeIndication = Visual ISO/IEC 10918-1 (aka JPEG)
0x11, // streamType = visual, upstream = false, reserved = 1
// XXX: does any reader expect valid values here? They wouldn't be
// trivial to calculate ahead of time.
0x00, 0x00, 0x00, // bufferSizeDB
0x00, 0x00, 0x00, 0x00, // maxBitrate
0x00, 0x00, 0x00, 0x00, // avgBitrate
]);
// No DecoderSpecificInfo.
// DecoderSpecificInfo, 2 of them?
// No profileLevelIndicatorIndexDescr.
});

// DecoderConfigDescriptor, defined in ISO/IEC 14496-1 section 7.2.6.6.
write_mpeg4_descriptor!(&mut buf, 0x04 /* DecoderConfigDescrTag */, {
buf.extend_from_slice(&[
0x6C, // objectTypeIndication = Visual ISO/IEC 10918-1 (aka JPEG)
0x11, // streamType = visual, upstream = false, reserved = 1
// XXX: does any reader expect valid values here? They wouldn't be
// trivial to calculate ahead of time.
0x00, 0x00, 0x00, // bufferSizeDB
0x00, 0x00, 0x00, 0x00, // maxBitrate
0x00, 0x00, 0x00, 0x00, // avgBitrate
]);
// No DecoderSpecificInfo.
// DecoderSpecificInfo, 2 of them?
// No profileLevelIndicatorIndexDescr.
});

// SLConfigDescriptor, ISO/IEC 14496-1 section 7.3.2.3.1.
write_mpeg4_descriptor!(&mut buf, 0x06 /* SLConfigDescrTag */, {
buf.push(2); // predefined = reserved for use in MP4 files
});
// SLConfigDescriptor, ISO/IEC 14496-1 section 7.3.2.3.1.
write_mpeg4_descriptor!(buf, 0x06 /* SLConfigDescrTag */, {
buf.push(2); // predefined = reserved for use in MP4 files
});
});
});
buf
}

impl Default for Depacketizer {
Expand Down
Loading

0 comments on commit d46acba

Please sign in to comment.