@@ -12,7 +12,7 @@ use serde::{Deserialize, Serialize};
1212use tracing:: warn;
1313use vapoursynth:: format:: PresetFormat ;
1414
15- use crate :: { into_array, into_vec, ClipInfo , InputPixelFormat } ;
15+ use crate :: { into_array, into_vec, ClipInfo , ColorRange , InputPixelFormat } ;
1616
1717#[ inline]
1818pub fn compose_ffmpeg_pipe < S : Into < String > > (
@@ -47,6 +47,7 @@ struct FfProbeStreamInfo {
4747 pub width : u32 ,
4848 pub height : u32 ,
4949 pub pix_fmt : String ,
50+ pub color_range : Option < String > ,
5051 pub color_transfer : Option < String > ,
5152 pub avg_frame_rate : String ,
5253 pub nb_frames : Option < String > ,
@@ -62,7 +63,7 @@ pub fn get_clip_info(source: &Path) -> anyhow::Result<ClipInfo> {
6263 . arg ( "-print_format" )
6364 . arg ( "json" )
6465 . arg ( "-show_entries" )
65- . arg ( "stream=width,height,pix_fmt,avg_frame_rate,nb_frames,color_transfer" )
66+ . arg ( "stream=width,height,pix_fmt,avg_frame_rate,nb_frames,color_range, color_transfer" )
6667 . arg ( source)
6768 . output ( ) ?
6869 . stdout ;
@@ -71,24 +72,49 @@ pub fn get_clip_info(source: &Path) -> anyhow::Result<ClipInfo> {
7172 . streams
7273 . first ( )
7374 . ok_or_else ( || anyhow:: anyhow!( "no video streams found in source file" ) ) ?;
75+ let format = FFPixelFormat :: from_str ( & stream_info. pix_fmt ) ?;
76+ let color_range = stream_info. color_range . as_deref ( ) . map_or_else (
77+ || infer_color_range_from_pix_fmt ( format) ,
78+ parse_ffprobe_color_range,
79+ ) ;
7480
7581 Ok ( ClipInfo {
76- format_info : InputPixelFormat :: FFmpeg {
77- format : FFPixelFormat :: from_str ( & stream_info . pix_fmt ) ? ,
82+ format_info : InputPixelFormat :: FFmpeg {
83+ format,
7884 } ,
79- frame_rate : parse_frame_rate ( & stream_info. avg_frame_rate ) ?,
80- resolution : ( stream_info. width , stream_info. height ) ,
85+ frame_rate : parse_frame_rate ( & stream_info. avg_frame_rate ) ?,
86+ resolution : ( stream_info. width , stream_info. height ) ,
87+ color_range,
8188 transfer_characteristics : match stream_info. color_transfer . as_deref ( ) {
8289 Some ( "smpte2084" ) => av1_grain:: TransferFunction :: SMPTE2084 ,
8390 _ => av1_grain:: TransferFunction :: BT1886 ,
8491 } ,
85- num_frames : match stream_info. nb_frames . as_deref ( ) . map ( str:: parse) {
92+ num_frames : match stream_info. nb_frames . as_deref ( ) . map ( str:: parse) {
8693 Some ( Ok ( nb_frames) ) => nb_frames,
8794 _ => get_num_frames ( source) ?,
8895 } ,
8996 } )
9097}
9198
99+ #[ inline]
100+ const fn infer_color_range_from_pix_fmt ( pix_fmt : FFPixelFormat ) -> Option < ColorRange > {
101+ match pix_fmt {
102+ FFPixelFormat :: YUVJ420P | FFPixelFormat :: YUVJ422P | FFPixelFormat :: YUVJ444P => {
103+ Some ( ColorRange :: Full )
104+ } ,
105+ _ => None ,
106+ }
107+ }
108+
109+ #[ inline]
110+ fn parse_ffprobe_color_range ( color_range : & str ) -> Option < ColorRange > {
111+ match color_range {
112+ "pc" | "jpeg" | "full" => Some ( ColorRange :: Full ) ,
113+ "tv" | "mpeg" | "limited" => Some ( ColorRange :: Limited ) ,
114+ _ => None ,
115+ }
116+ }
117+
92118/// Get frame count using FFmpeg
93119#[ inline]
94120pub fn get_num_frames ( source : & Path ) -> anyhow:: Result < usize > {
@@ -461,3 +487,39 @@ impl FromStr for FFPixelFormat {
461487 } )
462488 }
463489}
490+
491+ #[ cfg( test) ]
492+ mod tests {
493+ use super :: * ;
494+
495+ #[ test]
496+ fn parse_ffprobe_color_range_aliases ( ) {
497+ assert_eq ! ( parse_ffprobe_color_range( "pc" ) , Some ( ColorRange :: Full ) ) ;
498+ assert_eq ! ( parse_ffprobe_color_range( "jpeg" ) , Some ( ColorRange :: Full ) ) ;
499+ assert_eq ! ( parse_ffprobe_color_range( "full" ) , Some ( ColorRange :: Full ) ) ;
500+ assert_eq ! ( parse_ffprobe_color_range( "tv" ) , Some ( ColorRange :: Limited ) ) ;
501+ assert_eq ! ( parse_ffprobe_color_range( "mpeg" ) , Some ( ColorRange :: Limited ) ) ;
502+ assert_eq ! (
503+ parse_ffprobe_color_range( "limited" ) ,
504+ Some ( ColorRange :: Limited )
505+ ) ;
506+ assert_eq ! ( parse_ffprobe_color_range( "unknown" ) , None ) ;
507+ }
508+
509+ #[ test]
510+ fn infer_color_range_from_legacy_jpeg_formats ( ) {
511+ assert_eq ! (
512+ infer_color_range_from_pix_fmt( FFPixelFormat :: YUVJ420P ) ,
513+ Some ( ColorRange :: Full )
514+ ) ;
515+ assert_eq ! (
516+ infer_color_range_from_pix_fmt( FFPixelFormat :: YUVJ422P ) ,
517+ Some ( ColorRange :: Full )
518+ ) ;
519+ assert_eq ! (
520+ infer_color_range_from_pix_fmt( FFPixelFormat :: YUVJ444P ) ,
521+ Some ( ColorRange :: Full )
522+ ) ;
523+ assert_eq ! ( infer_color_range_from_pix_fmt( FFPixelFormat :: YUV420P ) , None ) ;
524+ }
525+ }
0 commit comments