parent
efc02236d5
commit
2bbc847754
@ -550,9 +550,9 @@ impl<W: AsyncWrite + AsyncSeek + Send + Unpin> Mp4Writer<W> {
|
|||||||
&frame.timestamp(),
|
&frame.timestamp(),
|
||||||
frame.data().remaining(),
|
frame.data().remaining(),
|
||||||
);
|
);
|
||||||
let sample_description_index = if let (Some(i), None) = (
|
let sample_description_index = if let (Some(i), true) = (
|
||||||
self.cur_video_params_sample_description_index,
|
self.cur_video_params_sample_description_index,
|
||||||
frame.new_parameters.as_ref(),
|
frame.has_new_parameters(),
|
||||||
) {
|
) {
|
||||||
// Use the most recent sample description index for most frames, without having to
|
// Use the most recent sample description index for most frames, without having to
|
||||||
// scan through self.video_sample_index.
|
// scan through self.video_sample_index.
|
||||||
|
@ -780,13 +780,12 @@ impl Stream {
|
|||||||
///
|
///
|
||||||
/// # With [`Demuxed`]
|
/// # With [`Demuxed`]
|
||||||
///
|
///
|
||||||
/// When using [`Demuxed`]'s frame-by-frame `futures::Stream` impl:
|
/// When using [`Demuxed`]'s frame-by-frame `futures::Stream` impl, `parameters` reflects
|
||||||
|
/// all parameters as of returned frames that have been returned from from `poll_next` via
|
||||||
|
/// `Poll::Ready`.
|
||||||
///
|
///
|
||||||
/// * After `poll_next` returns `Ready`, `parameters` reflects all parameter changes as of
|
/// It's guaranteed to *not* reflect any parameter changes in the upcoming frame, even after
|
||||||
/// returned frame.
|
/// a `Poll::Pending` return.
|
||||||
/// * After `poll_next` returns `Pending`, currently the parameters may or may not reflect
|
|
||||||
/// changes sent as part of the *next* frame that `poll_next` will return. (It's likely
|
|
||||||
/// that an upcoming Retina release will guarantee not.)
|
|
||||||
///
|
///
|
||||||
/// If there is no packet loss, parameters are generally available after the first frame is
|
/// If there is no packet loss, parameters are generally available after the first frame is
|
||||||
/// returned. In the case of H.264, [RFC 6184 section
|
/// returned. In the case of H.264, [RFC 6184 section
|
||||||
@ -2357,7 +2356,16 @@ impl futures::Stream for Demuxed {
|
|||||||
description,
|
description,
|
||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
// Note we're committed now to calling `pull` and returning
|
||||||
|
// `Ready` if it has a frame. This is because the
|
||||||
|
// `Stream::parameters` contract guarantees that changes in
|
||||||
|
// upcoming frames are *not* reflected. It's implemented by a
|
||||||
|
// call to `Depacketizer::pull`, which doesn't make a like
|
||||||
|
// guarantee about the state between `push` and `pull`. So we
|
||||||
|
// can't let our callers observe that state.
|
||||||
}
|
}
|
||||||
|
|
||||||
match depacketizer.pull(conn_ctx, stream_ctx) {
|
match depacketizer.pull(conn_ctx, stream_ctx) {
|
||||||
Ok(Some(item)) => {
|
Ok(Some(item)) => {
|
||||||
self.state = DemuxedState::Pulling(stream_id);
|
self.state = DemuxedState::Pulling(stream_id);
|
||||||
|
@ -496,31 +496,27 @@ impl Depacketizer {
|
|||||||
self.nals.clear();
|
self.nals.clear();
|
||||||
self.pieces.clear();
|
self.pieces.clear();
|
||||||
|
|
||||||
let new_parameters = match (
|
let has_new_parameters = match (
|
||||||
new_sps.as_deref(),
|
new_sps.as_deref(),
|
||||||
new_pps.as_deref(),
|
new_pps.as_deref(),
|
||||||
self.parameters.as_ref(),
|
self.parameters.as_ref(),
|
||||||
) {
|
) {
|
||||||
(Some(sps_nal), Some(pps_nal), _) => {
|
(Some(sps_nal), Some(pps_nal), _) => {
|
||||||
// TODO: could map this to a RtpPacketError more accurately.
|
// TODO: could map this to a RtpPacketError more accurately.
|
||||||
let ip = InternalParameters::parse_sps_and_pps(sps_nal, pps_nal)?;
|
self.parameters = Some(InternalParameters::parse_sps_and_pps(sps_nal, pps_nal)?);
|
||||||
let p = ip.generic_parameters.clone();
|
true
|
||||||
self.parameters = Some(ip);
|
|
||||||
Some(Box::new(p))
|
|
||||||
}
|
}
|
||||||
(Some(_), None, Some(old_ip)) | (None, Some(_), Some(old_ip)) => {
|
(Some(_), None, Some(old_ip)) | (None, Some(_), Some(old_ip)) => {
|
||||||
let sps_nal = new_sps.as_deref().unwrap_or(&old_ip.sps_nal);
|
let sps_nal = new_sps.as_deref().unwrap_or(&old_ip.sps_nal);
|
||||||
let pps_nal = new_pps.as_deref().unwrap_or(&old_ip.pps_nal);
|
let pps_nal = new_pps.as_deref().unwrap_or(&old_ip.pps_nal);
|
||||||
// TODO: as above, could map this to a RtpPacketError more accurately.
|
// TODO: as above, could map this to a RtpPacketError more accurately.
|
||||||
let ip = InternalParameters::parse_sps_and_pps(sps_nal, pps_nal)?;
|
self.parameters = Some(InternalParameters::parse_sps_and_pps(sps_nal, pps_nal)?);
|
||||||
let p = ip.generic_parameters.clone();
|
true
|
||||||
self.parameters = Some(ip);
|
|
||||||
Some(Box::new(p))
|
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => false,
|
||||||
};
|
};
|
||||||
Ok(VideoFrame {
|
Ok(VideoFrame {
|
||||||
new_parameters,
|
has_new_parameters,
|
||||||
loss: au.loss,
|
loss: au.loss,
|
||||||
start_ctx: au.start_ctx,
|
start_ctx: au.start_ctx,
|
||||||
end_ctx: au.end_ctx,
|
end_ctx: au.end_ctx,
|
||||||
@ -1429,9 +1425,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// After pull, new_parameters and parameters() both reflect the change.
|
// After pull, new_parameters and parameters() both reflect the change.
|
||||||
assert!(frame.new_parameters.is_some());
|
assert!(frame.has_new_parameters);
|
||||||
let p = frame.new_parameters.unwrap();
|
|
||||||
assert_eq!(p.pixel_dimensions(), (640, 480));
|
|
||||||
match d.parameters() {
|
match d.parameters() {
|
||||||
Some(crate::codec::Parameters::Video(v)) => {
|
Some(crate::codec::Parameters::Video(v)) => {
|
||||||
assert_eq!(v.pixel_dimensions(), (640, 480));
|
assert_eq!(v.pixel_dimensions(), (640, 480));
|
||||||
@ -1538,7 +1532,7 @@ mod tests {
|
|||||||
Some(CodecItem::VideoFrame(frame)) => frame,
|
Some(CodecItem::VideoFrame(frame)) => frame,
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
};
|
};
|
||||||
assert!(frame.new_parameters.is_some());
|
assert!(frame.has_new_parameters);
|
||||||
assert!(d.parameters().is_some());
|
assert!(d.parameters().is_some());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -324,16 +324,8 @@ pub struct VideoFrame {
|
|||||||
start_ctx: crate::PacketContext,
|
start_ctx: crate::PacketContext,
|
||||||
end_ctx: crate::PacketContext,
|
end_ctx: crate::PacketContext,
|
||||||
|
|
||||||
/// New video parameters.
|
has_new_parameters: bool,
|
||||||
///
|
|
||||||
/// Rarely populated and large, so boxed to reduce bloat.
|
|
||||||
//
|
|
||||||
/// To obtain the current parameters for the stream regardless of whether this frame set new
|
|
||||||
/// parameters, see [`crate::client::Stream::parameters`].
|
|
||||||
pub new_parameters: Option<Box<VideoParameters>>,
|
|
||||||
|
|
||||||
loss: u16,
|
loss: u16,
|
||||||
|
|
||||||
timestamp: crate::Timestamp,
|
timestamp: crate::Timestamp,
|
||||||
stream_id: usize,
|
stream_id: usize,
|
||||||
is_random_access_point: bool,
|
is_random_access_point: bool,
|
||||||
@ -347,6 +339,14 @@ impl VideoFrame {
|
|||||||
self.stream_id
|
self.stream_id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if this frame set new video parameters.
|
||||||
|
///
|
||||||
|
/// The parameters can be obtained via [`crate::client::Stream::parameters`].
|
||||||
|
#[inline]
|
||||||
|
pub fn has_new_parameters(&self) -> bool {
|
||||||
|
self.has_new_parameters
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the number of lost RTP packets before this video frame. See
|
/// Returns the number of lost RTP packets before this video frame. See
|
||||||
/// [`crate::rtp::ReceivedPacket::loss`].
|
/// [`crate::rtp::ReceivedPacket::loss`].
|
||||||
///
|
///
|
||||||
@ -418,7 +418,7 @@ impl std::fmt::Debug for VideoFrame {
|
|||||||
.field("start_ctx", &self.start_ctx)
|
.field("start_ctx", &self.start_ctx)
|
||||||
.field("end_ctx", &self.end_ctx)
|
.field("end_ctx", &self.end_ctx)
|
||||||
.field("loss", &self.loss)
|
.field("loss", &self.loss)
|
||||||
.field("new_parameters", &self.new_parameters)
|
.field("has_new_parameters", &self.has_new_parameters)
|
||||||
.field("is_random_access_point", &self.is_random_access_point)
|
.field("is_random_access_point", &self.is_random_access_point)
|
||||||
.field("is_disposable", &self.is_disposable)
|
.field("is_disposable", &self.is_disposable)
|
||||||
.field("data", &crate::hex::LimitedHex::new(&self.data, 64))
|
.field("data", &crate::hex::LimitedHex::new(&self.data, 64))
|
||||||
|
Loading…
Reference in New Issue
Block a user