parent
e4be827a63
commit
499d658352
@ -20,10 +20,10 @@
|
|||||||
use anyhow::{anyhow, bail, Context, Error};
|
use anyhow::{anyhow, bail, Context, Error};
|
||||||
use bytes::{Buf, BufMut, BytesMut};
|
use bytes::{Buf, BufMut, BytesMut};
|
||||||
use futures::{Future, StreamExt};
|
use futures::{Future, StreamExt};
|
||||||
use log::{info, warn};
|
use log::{debug, info, warn};
|
||||||
use retina::{
|
use retina::{
|
||||||
client::Transport,
|
client::Transport,
|
||||||
codec::{AudioParameters, CodecItem, VideoParameters},
|
codec::{AudioParameters, CodecItem, Parameters, VideoParameters},
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::num::NonZeroU32;
|
use std::num::NonZeroU32;
|
||||||
@ -530,9 +530,17 @@ impl<W: AsyncWrite + AsyncSeek + Send + Unpin> Mp4Writer<W> {
|
|||||||
);
|
);
|
||||||
if let Some(p) = frame.new_parameters.take() {
|
if let Some(p) = frame.new_parameters.take() {
|
||||||
if self.video_trak.samples > 0 {
|
if self.video_trak.samples > 0 {
|
||||||
bail!("parameters change unimplemented. new parameters: {:#?}", p);
|
let old = self.video_params.as_ref().unwrap();
|
||||||
|
bail!(
|
||||||
|
"video parameters change unimplemented.\nold: {:#?}\nnew: {:#?}",
|
||||||
|
old,
|
||||||
|
p
|
||||||
|
);
|
||||||
}
|
}
|
||||||
self.video_params = Some(p);
|
self.video_params = Some(p);
|
||||||
|
} else if self.video_params.is_none() {
|
||||||
|
debug!("Discarding video frame received before parameters");
|
||||||
|
return Ok(());
|
||||||
}
|
}
|
||||||
let size = u32::try_from(frame.data().remaining())?;
|
let size = u32::try_from(frame.data().remaining())?;
|
||||||
self.video_trak.add_sample(
|
self.video_trak.add_sample(
|
||||||
@ -630,8 +638,8 @@ async fn copy<'a>(
|
|||||||
async fn write_mp4<'a>(
|
async fn write_mp4<'a>(
|
||||||
opts: &'a Opts,
|
opts: &'a Opts,
|
||||||
session: retina::client::Session<retina::client::Described>,
|
session: retina::client::Session<retina::client::Described>,
|
||||||
video_stream: Option<(usize, VideoParameters)>,
|
video_params: Option<Box<VideoParameters>>,
|
||||||
audio_stream: Option<(usize, AudioParameters)>,
|
audio_params: Option<Box<AudioParameters>>,
|
||||||
stop_signal: Pin<Box<dyn Future<Output = Result<(), std::io::Error>>>>,
|
stop_signal: Pin<Box<dyn Future<Output = Result<(), std::io::Error>>>>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let mut session = session
|
let mut session = session
|
||||||
@ -650,14 +658,7 @@ async fn write_mp4<'a>(
|
|||||||
tmp_filename.push(PARTIAL_SUFFIX); // OsString::push doesn't put in a '/', unlike PathBuf::.
|
tmp_filename.push(PARTIAL_SUFFIX); // OsString::push doesn't put in a '/', unlike PathBuf::.
|
||||||
let tmp_filename: PathBuf = tmp_filename.into();
|
let tmp_filename: PathBuf = tmp_filename.into();
|
||||||
let out = tokio::fs::File::create(&tmp_filename).await?;
|
let out = tokio::fs::File::create(&tmp_filename).await?;
|
||||||
let mut mp4 = Mp4Writer::new(
|
let mut mp4 = Mp4Writer::new(video_params, audio_params, opts.allow_loss, out).await?;
|
||||||
video_stream.map(|(_, p)| Box::new(p)),
|
|
||||||
audio_stream.map(|(_, p)| Box::new(p)),
|
|
||||||
opts.allow_loss,
|
|
||||||
out,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let result = copy(opts, &mut session, stop_signal, &mut mp4).await;
|
let result = copy(opts, &mut session, stop_signal, &mut mp4).await;
|
||||||
if let Err(e) = result {
|
if let Err(e) = result {
|
||||||
// Log errors about finishing, returning the original error.
|
// Log errors about finishing, returning the original error.
|
||||||
@ -705,38 +706,38 @@ pub async fn run(opts: Opts) -> Result<(), Error> {
|
|||||||
.teardown(opts.teardown),
|
.teardown(opts.teardown),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
let video_stream = if !opts.no_video {
|
let (video_stream_i, video_params) = if !opts.no_video {
|
||||||
let s = session
|
let s = session.streams().iter().enumerate().find_map(|(i, s)| {
|
||||||
.streams()
|
if s.media == "video" {
|
||||||
.iter()
|
if s.encoding_name == "h264" {
|
||||||
.enumerate()
|
log::info!("Using h264 video stream");
|
||||||
.find_map(|(i, s)| match s.parameters() {
|
return Some((
|
||||||
Some(retina::codec::Parameters::Video(v)) => {
|
i,
|
||||||
log::info!(
|
match s.parameters() {
|
||||||
"Using {} video stream (rfc 6381 codec {})",
|
Some(Parameters::Video(v)) => Some(Box::new(v)),
|
||||||
&s.encoding_name,
|
Some(_) => panic!("expected parameters to match stream type video"),
|
||||||
v.rfc6381_codec()
|
None => None,
|
||||||
);
|
},
|
||||||
Some((i, v.clone()))
|
));
|
||||||
}
|
}
|
||||||
_ if s.media == "video" => {
|
|
||||||
log::info!(
|
log::info!(
|
||||||
"Ignoring {} video stream because it's unsupported",
|
"Ignoring {} video stream because it's unsupported",
|
||||||
&s.encoding_name
|
&s.encoding_name
|
||||||
);
|
);
|
||||||
|
}
|
||||||
None
|
None
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
});
|
});
|
||||||
if s.is_none() {
|
if let Some((i, p)) = s {
|
||||||
|
(Some(i), p)
|
||||||
|
} else {
|
||||||
log::info!("No suitable video stream found");
|
log::info!("No suitable video stream found");
|
||||||
|
(None, None)
|
||||||
}
|
}
|
||||||
s
|
|
||||||
} else {
|
} else {
|
||||||
log::info!("Ignoring video streams (if any) because of --no-video");
|
log::info!("Ignoring video streams (if any) because of --no-video");
|
||||||
None
|
(None, None)
|
||||||
};
|
};
|
||||||
if let Some((i, _)) = video_stream {
|
if let Some(i) = video_stream_i {
|
||||||
session.setup(i).await?;
|
session.setup(i).await?;
|
||||||
}
|
}
|
||||||
let audio_stream = if !opts.no_audio {
|
let audio_stream = if !opts.no_audio {
|
||||||
@ -749,7 +750,7 @@ pub async fn run(opts: Opts) -> Result<(), Error> {
|
|||||||
// entry.
|
// entry.
|
||||||
Some(retina::codec::Parameters::Audio(a)) if a.sample_entry().is_some() => {
|
Some(retina::codec::Parameters::Audio(a)) if a.sample_entry().is_some() => {
|
||||||
log::info!("Using {} audio stream (rfc 6381 codec {})", &s.encoding_name, a.rfc6381_codec().unwrap());
|
log::info!("Using {} audio stream (rfc 6381 codec {})", &s.encoding_name, a.rfc6381_codec().unwrap());
|
||||||
Some((i, a.clone()))
|
Some((i, Box::new(a.clone())))
|
||||||
}
|
}
|
||||||
_ if s.media == "audio" => {
|
_ if s.media == "audio" => {
|
||||||
log::info!("Ignoring {} audio stream because it can't be placed into a .mp4 file without transcoding", &s.encoding_name);
|
log::info!("Ignoring {} audio stream because it can't be placed into a .mp4 file without transcoding", &s.encoding_name);
|
||||||
@ -768,10 +769,17 @@ pub async fn run(opts: Opts) -> Result<(), Error> {
|
|||||||
if let Some((i, _)) = audio_stream {
|
if let Some((i, _)) = audio_stream {
|
||||||
session.setup(i).await?;
|
session.setup(i).await?;
|
||||||
}
|
}
|
||||||
if video_stream.is_none() && audio_stream.is_none() {
|
if video_stream_i.is_none() && audio_stream.is_none() {
|
||||||
bail!("Exiting because no video or audio stream was selected; see info log messages above");
|
bail!("Exiting because no video or audio stream was selected; see info log messages above");
|
||||||
}
|
}
|
||||||
let result = write_mp4(&opts, session, video_stream, audio_stream, stop_signal).await;
|
let result = write_mp4(
|
||||||
|
&opts,
|
||||||
|
session,
|
||||||
|
video_params,
|
||||||
|
audio_stream.map(|(_i, p)| p),
|
||||||
|
stop_signal,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
// Session has now been dropped, on success or failure. A TEARDOWN should
|
// Session has now been dropped, on success or failure. A TEARDOWN should
|
||||||
// be pending if necessary. session_group.await_teardown() will wait for it.
|
// be pending if necessary. session_group.await_teardown() will wait for it.
|
||||||
|
Loading…
Reference in New Issue
Block a user