#![allow(non_snake_case)]
#![allow(non_camel_case_types)] 
#![allow(unused_imports)]
#![allow(unused_variables)]
#![allow(dead_code)]
// #![allow(unused_braces)]
// https://github.com/zmwangx/rust-ffmpeg


use crate::prelude::*;

use std::{
    env,
    fs::{self, File}, 
    io::prelude::*, 
    os::unix::fs::MetadataExt, 
    path::{self,Path,PathBuf}, 
};
use ffmpeg_next::{
    // util::{channel_layout, chroma, color, dictionary, error, frame, log, mathematics, media, option, picture, rational, time}, 
    // codec::{audio_service, codec, discard, field_order, packet,   subtitle, threading, decoder, encoder}, 
    // filter,
    format::{ Pixel, Output }, 
    media::Type, 
    software::scaling::{self, flag::Flags},
    util::frame::video::Video,
    Rescale,rescale, //引入特征 
    
    codec, decoder, encoder,  format,  frame, log, media, picture, Dictionary, Packet, Rational,
};

use serde::{Serialize, Deserialize};
use lwleen_rpc::serde_json;
use data_encoding::BASE64;




#[gen_特征_derive(MOD特征_ffmpeg)]
impl 信令路由{
    async fn exec文本信令_ffmpeg(&self,函数名:&str, mut 参数数组:Vec<String>)-> anyhow_Result<(bool,Value,Vec<String>)>{
        let 信令路由ref = self;
        let 结果 = match 函数名{
            "ffmpeg_缩略图" =>{
                if 参数数组.len() < 1 {  return Err(anyhow_err!("ffmpeg_缩略图, 缺少参数:: <文件路径>   <图片大小>")); } 
                let 图片尺寸 = if 参数数组.len() >=2 {    Some(参数数组[1].parse::<u32>().unwrap_or(96))   } else { Some(96) } ;
                let 图片质量 = if 参数数组.len() >=3 {    参数数组[2].parse::<usize>().ok()  } else { None };
                json!(信令路由ref.ffmpeg_缩略图(参数数组.remove(0) ,图片尺寸,图片质量).await?)
            }
            "ffmpeg_截图到本地"=>{
                if 参数数组.len() < 1 {  return Err(anyhow_err!("ffmpeg_截图到本地, 缺少参数:: <输入路径> <输出路径> <图片大小> <图片质量> ")); }
                let 图片质量 = if 参数数组.len() >=4 {    参数数组[3].parse::<usize>().ok()   } else { None };
                let 图片尺寸 = if 参数数组.len() >=3 {    参数数组[2].parse::<u32>().ok()     } else { None };
                let 输出路径 = if 参数数组.len() >=2 && 参数数组[1] != "undefined" && 参数数组[1]!="null" {   Some(参数数组.remove(1))  } else { return Err(anyhow_err!("ffmpeg_截图到本地, 缺少参数:: <输出路径> ")); } ; 
                json!(信令路由ref.ffmpeg_截图保存到本地(参数数组.remove(0), 输出路径,图片尺寸, 图片质量 ).await?)
            }
            "ffmpeg_文件信息"=>{
                if 参数数组.len() < 1 {  return Err(anyhow_err!("ffmpeg_缩略图, 缺少参数:: <文件路径> ")); }  
                信令路由ref.ffmpeg_文件信息(参数数组.remove(0) ).await?
            },
            _=>{ return Ok((false, json!(""), 参数数组)); }
        };
        Ok((true, 结果, Vec::new()))
    }

    fn ffmpeg_文件信息<T:AsRef<str>>(&self,路径:T)-> impl 信令任务<Value>{
        core发布信令任务!(self, 信令表::ffmpeg_文件信息{ 路径:路径.as_ref().to_string() },  信令表::ffmpeg_文件信息_回复 => 信息 )          
    }
    //必须有输出路径
    fn ffmpeg_截图保存到本地<T:AsRef<str>>(&self,输入路径:T,输出路径:Option<T>,图片尺寸:Option<u32>,图片质量:Option<usize>)-> impl 信令任务<String>{ 
        core发布信令任务!(self, 信令表::ffmpeg_截图到本地{ 输入路径:输入路径.as_ref().to_string(), 输出路径:输出路径.map(|path|path.as_ref().to_string()), 图片尺寸, 图片质量 },  信令表::ffmpeg_截图到本地_回复 => 图片dataurl )
    }
    fn ffmpeg_缩略图<T:AsRef<str>>(&self,路径:T,图片尺寸:Option<u32>,图片质量:Option<usize>)-> impl 信令任务<String>{     
        core发布信令任务!(self, 信令表::ffmpeg_缩略图{ 路径:路径.as_ref().to_string(), 图片尺寸,图片质量  },  信令表::ffmpeg_缩略图_回复 => 图片dataurl )
    }
}


#[derive(Clone, Serialize, Deserialize)]//可选特征
enum 信令表{
    ffmpeg_缩略图{          路径:String, 图片尺寸:Option<u32>,图片质量:Option<usize> },
    ffmpeg_缩略图_回复{     图片dataurl:String   },
    ffmpeg_截图到本地{      输入路径:String,输出路径:Option<String>, 图片尺寸:Option<u32>, 图片质量:Option<usize> },
    ffmpeg_截图到本地_回复{  图片dataurl:String   },
    ffmpeg_文件信息{        路径:String },
    ffmpeg_文件信息_回复{    信息:Value },
}
impl信令特征!(信令表, 执行函数);




async fn 执行函数(信令路由:信令路由,id:信令ID,单个信令:信令表)->anyhow_Result<信令表>{
    match 单个信令{
        信令表::ffmpeg_缩略图{路径 , 图片尺寸,图片质量 }=>{
            let res =  信令路由.pool同步任务(move ||{  ffmpeg_缩略图(&路径,图片尺寸, 图片质量) }).await??;
            anyhow_Ok信令!(信令表::ffmpeg_缩略图_回复 { 图片dataurl:res })
        }
        信令表::ffmpeg_截图到本地{ 输入路径,输出路径, 图片尺寸,图片质量}=>{
            let res =  信令路由.pool同步任务(move ||{  ffmpeg_截图到本地(&输入路径,输出路径.as_deref(), 图片尺寸,图片质量) }).await??;
            anyhow_Ok信令!(信令表::ffmpeg_截图到本地_回复 { 图片dataurl:res })
        }
        信令表::ffmpeg_文件信息{ 路径 }=>{
            let res =  信令路由.pool同步任务(move ||{  视频转码器::应用_读取媒体文件信息(&路径) }).await??;
            anyhow_Ok信令!(信令表::ffmpeg_文件信息_回复 { 信息:res })
        }
        _ =>{ return anyhow_Err信令!("信令不存在 {id}"); }
    }
}





// ffmpeg -y -v error ${指定time} -i  "${视频路径}"  -frames:v 1 -f webp -vf "scale=-1:${大小}" pipe: | base64
pub fn ffmpeg_缩略图(input_path:&str, 图片尺寸:Option<u32>,图片质量:Option<usize>)->anyhow_Result<String>{
        let 路径对象 = Path::new(input_path);
        let 扩展名 = 路径对象.extension().unwrap_or_default().to_string_lossy().to_lowercase();
        if 扩展名 == "svg" {
            let 图片字符串 = fs::read(input_path)?;
            let base64_encoded = BASE64.encode(&图片字符串);
            return Ok(format!("data:image/svg+xml;base64,{}", base64_encoded));
        }
        
        let mut 视频模块 = 视频转码器::new(input_path, None)?; 
        let 最终dateurl = 视频模块.应用_截图保存到本地orDataurl(图片尺寸, None,图片质量)?;
        Ok(最终dateurl) 
}


pub fn ffmpeg_截图到本地(input_path:&str, output_path:Option<&str>, 图片尺寸:Option<u32>, 图片质量:Option<usize>)->anyhow_Result<String>{
    let mut 视频模块 = 视频转码器::new(input_path, output_path)?;  
    let 最终dateurl = 视频模块.应用_截图保存到本地orDataurl(图片尺寸,None, 图片质量)?;
    Ok(最终dateurl)
}




/*

time1=$(date +%s%N | cut -b1-13);     \
ffmpeg -y -v error  -an -i  "/home/lwl/Desktop/龙卷风.mp4"     "/home/lwl/Desktop/1_shell.gif" ;    \
echo "🔥用时：  "   $(($(date +%s%N | cut -b1-13) - time1)) 

*/
#[cfg(test)]
mod 测试生成_ffmpeg_mod { 
    use super::*;
    
    #[test]
    fn 测试_ffmpeg缩略图() {
        let 路径 = "/home/lwl/Videos/mv/半岛铁盒.mp4"; 
        let 记录时间ms = std::time::SystemTime::now();
        let data =  ffmpeg_缩略图(路径, Some(200),None).unwrap() ;
        
        eprintln!("{}\n🔥用时： {}", data,   std::time::SystemTime::now().duration_since(记录时间ms).unwrap().as_millis()  );

        // let info = ffmpeg_文件信息(路径);
        // eprintln!("{:#?}",info);   
    }
  
    
    #[test]
    fn 测试_ffmpeg_截图到本地() {
        let 路径 = "/home/lwl/Desktop/1_shell.webp";
        let 记录时间ms = std::time::SystemTime::now();
        let data =  ffmpeg_截图到本地(路径, Some("/home/lwl/Desktop/1_rust.webp"),None,None).unwrap();
        
        eprintln!("🔥用时： {}",   std::time::SystemTime::now().duration_since(记录时间ms).unwrap().as_millis()  );
 
    }
 

    #[test]
    fn 测试_ffmpeg_文件信息() {
        let 路径 = "/home/lwl/Desktop/test.jpg";  
        let info = 视频转码器::应用_读取媒体文件信息(路径);
        eprintln!("{:#?}",info);
    }
}


macro_rules! json文本{  ($res:expr) => { json!(format!("{:?}",$res))  };   }


pub struct 视频转码器{
    输入路径:String,
    输出路径:String, 
    原始输出路径:Option<String>,
    
    输入_ctx: ffmpeg_next::format::context::Input,
    输出_ctx: Option<ffmpeg_next::format::context::Output>,
  
    解码器: Option<decoder::Video>,
    编码器: Option<encoder::Video>,

    帧计数: usize,
    启动时间:std::time::SystemTime,
}

impl 视频转码器{
    fn new( 输入路径: &str, 输出路径: Option<&str>, ) -> anyhow_Result<Self> {
        ffmpeg_next::init().map_err(|err|anyhow_err!("ffmpeg_next::init [错误]: {err}"))?;
        let mut 语义化配置 = Dictionary::new();
        语义化配置.set("probesize","1000000");             // 减少初始探测大小(字节)
        // 语义化配置.set("analyzeduration", "1000000");      // 限制分析时长（微秒） 
        let 输入_ctx = ffmpeg_next::format::input_with_dictionary(输入路径,语义化配置).map_err(|err|anyhow_err!("ffmpeg_next::input [错误]: {err}"))?;
        
        let 格式化默认路径 = |字符串:&str|->String{
            let 路径扩展名 = std::path::Path::new(字符串).extension().unwrap_or_default().to_string_lossy().to_lowercase();
            let 无扩展名长度 = 字符串.len() - (if 路径扩展名.len()>0{ 路径扩展名.len()+1 }else{ 0 }) ;  
            format!("{}_out.{}", &字符串[..无扩展名长度], "webp" )
        };
        let 可用输出路径 = if let Some(字符串) = 输出路径{ if 字符串 != ""{ 字符串.to_string() }else{  格式化默认路径(输入路径) }  }  else{  格式化默认路径(输入路径)  };

        Ok(Self{
            输入路径:输入路径.to_string(),
            输出路径:可用输出路径,
            原始输出路径:输出路径.map(|字符串|{ 字符串.to_string() }),
            输入_ctx,
            输出_ctx:None, 
            解码器:None,
            编码器:None,
            帧计数: 0,
            启动时间: std::time::SystemTime::now(),
        })
    }
    
    fn 解析字符串配置<'b>(&self, 字符串: Option<String>) -> Dictionary<'b> {
        let mut dict = Dictionary::new();
        if let Some(s) = 字符串{
            for keyval in s.split_terminator(',') {
                let tokens: Vec<&str> = keyval.split('=').collect();
                match tokens[..] {
                    [key, val] => dict.set(key, val),
                    _ => return dict,
                }
            }
        }
        dict
    }
    
    fn 最佳视频流(&self)->anyhow_Result<(decoder::Video, usize, Rational)>{
        let 最佳视频流  = self.输入_ctx.streams().best(ffmpeg_next::media::Type::Video).ok_or(ffmpeg_next::Error::StreamNotFound)?; 
        let 解码器 = ffmpeg_next::codec::context::Context::from_parameters(最佳视频流.parameters())?.decoder().video()?;
        let 最佳视频流index = 最佳视频流.index(); 
        let 最佳视频流time_base = 最佳视频流.time_base();
        Ok((解码器, 最佳视频流index, 最佳视频流time_base))
    }
    fn seek_跳到时间比例(&mut self, 跳转百分比: usize){  
        let 比例 = if 跳转百分比>=100{ 100 }else if 跳转百分比<=0{ 0 } else{ 跳转百分比 };
        let 视频时长 = self.输入_ctx.duration() as f64 / f64::from(ffmpeg_next::ffi::AV_TIME_BASE);   
        if 视频时长 > 1.0 {
            let 中间秒 = (视频时长 * 比例 as f64 / 100.0) as i64;                                                         
            let position = 中间秒.rescale((1, 1), ffmpeg_next::rescale::TIME_BASE);     //引入 ffmpeg_next::Rescale 特征
            self.输入_ctx.seek(position, ..position).ok();
        }
    }
    
    fn 解码_截图一帧(&mut self)->anyhow_Result<Video>{
        let (mut 解码器,最佳视频流index,最佳视频流time_base) = self.最佳视频流()?;
        解码器.flush();   // 必须刷新解码器缓冲
        let mut 截图帧 = Video::empty();
        for (stream, packet) in self.输入_ctx.packets() {
            if stream.index() == 最佳视频流index {
                解码器.send_packet(&packet).ok();    //须跟 send_eof
                // 解码器.send_eof().ok();           //可能卡住
                if let Ok(_) = 解码器.receive_frame(&mut 截图帧){   break;  }
            }
        };
        let 图片层数 = 截图帧.planes();    //这个 <=0  导致 panic 崩溃
        if 图片层数 <= 0{   return Err(anyhow_err!("截图帧的数据层为0    无法生成视频缩略图"));   }
        Ok(截图帧)
    }
    fn 解码_发送包(&mut self, packet: &Packet)->anyhow_Result<()>{ 
        let 解码器 = self.解码器.as_mut().ok_or(anyhow_err!("需要解码器"))?;   
        解码器.send_packet(packet)?;  //后面必须跟 结束eof         
        解码器.send_eof()?; Ok(()) 
    }
    fn 解码_接收和处理_单帧(&mut self)-> anyhow_Result<frame::Video>{
        let 解码器 = self.解码器.as_mut().ok_or(anyhow_err!("没有可用的 解码器"))?;   
        let mut 已解码帧 = frame::Video::empty(); 
        解码器.receive_frame(&mut 已解码帧)?;
        let timestamp = 已解码帧.timestamp();    // self.log_progress(f64::from(Rational(timestamp.unwrap_or(0) as i32, 1) * self.流time_base, ));
        已解码帧.set_pts(timestamp);
        已解码帧.set_kind(picture::Type::None); 
        self.帧计数 += 1;
        Ok(已解码帧)
    }
    
    
    fn 编码_发送帧(&mut self, frame: &frame::Video)->anyhow_Result<()>{     //后面必须跟 结束eof 
        let 编码器 = self.编码器.as_mut().ok_or(anyhow_err!("没有可用的编码器"))?;
        编码器.send_frame(frame)?; 
        编码器.send_eof()?;
        Ok(())  
    }
    fn 编码_接收和写入文件(&mut self, 流序号:usize)->anyhow_Result<()>{
        let 编码器 = self.编码器.as_mut().ok_or(anyhow_err!("没有可用的 编码器"))?;
        let octx = self.输出_ctx.as_mut().ok_or(anyhow_err!("没有可用的 输出octx"))?;
        
        let mut 已编码包 = Packet::empty();
        while 编码器.receive_packet(&mut 已编码包).is_ok() {
            已编码包.set_stream(流序号);
            // 已编码包.rescale_ts(输入视频流time_base, ost_time_base);  
            // 已编码包.set_position(-1);
            已编码包.write(octx )?;
        }
        Ok(()) 
    }

    
   
    pub fn 帧缩放器(截图帧:&frame::Video , 尺寸:Option<u32>, 编码像素格式:Pixel) -> anyhow_Result<frame::Video>{
            let mut 计算宽 = 截图帧.width();
            let mut 计算高 = 截图帧.height();
            if let Some(size) = 尺寸{
                let 原始宽 = 计算宽; let 原始高 = 计算高;
                if 原始宽==0 || 原始高==0 { return  Err(anyhow_err!("帧缩放器错误: 帧宽高为0")); }
                计算宽 = if size % 2 == 0 {size} else {size+1};      //必须是偶数
                计算高 = 计算宽 * 原始高 / 原始宽;
            }
            
            let mut 缩放器  = ffmpeg_next::software::scaling::Context::get(
                截图帧.format(),  截图帧.width(),  截图帧.height(), 
                编码像素格式,
                计算宽, 计算高,
                Flags::BICUBIC | Flags::DIRECT_BGR | Flags::ACCURATE_RND
            )?;   //  Flags::LANCZOS  更适合对图像质量有极致要求  
            let mut 新帧 = frame::Video::empty();
            缩放器.run( 截图帧, &mut 新帧)?;
            Ok(新帧)
    }
   

    //有原始地址  就保存到本地, 否则输出 dataurl
    pub fn 应用_截图保存到本地orDataurl(&mut self, 截图尺寸:Option<u32>,跳转时间比例:Option<usize>, 图片质量:Option<usize>)->anyhow_Result<String>{
        let mut 输出_ctx = None;
        if let Some(_) = self.原始输出路径 {   输出_ctx =  Some(format::output(&self.输出路径)?); }
        
        //-------准备 猜测格式
        let mut 猜测编码格式 = codec::Id::WEBP;   //默认
        if let Some(out_ctx) = &输出_ctx{  
            猜测编码格式 =  out_ctx.format().codec(&self.输出路径, media::Type::Video);
        };
        let 编码内核 = encoder::find(猜测编码格式).ok_or(anyhow_err!("find 传入的编码器类型 不可用"))?;
        let 像素格式 = match 猜测编码格式{
            codec::Id::AV1   =>{  ffmpeg_next::format::Pixel::YUV420P       }
            codec::Id::WEBP  =>{  ffmpeg_next::format::Pixel::YUVA420P      }
            codec::Id::PNG   =>{  ffmpeg_next::format::Pixel::RGBA          }
            codec::Id::MJPEG =>{  ffmpeg_next::format::Pixel::YUVJ444P      }
            codec::Id::BMP   =>{  ffmpeg_next::format::Pixel::BGRA          }
            _ => {  编码内核.video()?.formats().ok_or(anyhow_err!("找不到 像素格式"))?.next().unwrap_or(ffmpeg_next::format::Pixel::YUV420P)   }
        };
        // eprintln!("{:?} ->  {:?} {:?}" ,&self.输出路径, &猜测编码格式, &像素格式);
        //--------开始解码
        self.seek_跳到时间比例(跳转时间比例.unwrap_or(50));
        let mut 截图帧 = self.解码_截图一帧()?;
        if 截图帧.format() != 像素格式 {  截图帧 = Self::帧缩放器(&截图帧, 截图尺寸, 像素格式)?;  };    //💖💖💖必须转像素格式
        
        //--------开始编码
        let mut enc_ctx = codec::context::Context::new().encoder().video()?; 
        enc_ctx.set_format(像素格式);                          // BGRA  YUV420P    YUVA420P
        enc_ctx.set_height(截图帧.height());
        enc_ctx.set_width(截图帧.width());
        enc_ctx.set_time_base((1, 25));                       //单位s   设置帧率 (1, 25) 表示每个时间单位是 1/25 秒。    time_base 是一个分数，表示时间的基本单位，通常写成 1/分母 的形式。
        enc_ctx.set_quality(图片质量.unwrap_or(75) * 118);  //webp默认75
        enc_ctx.set_compression(Some(4));                    //webp默认4  
        match 猜测编码格式{
            codec::Id::AV1 =>{  }
            codec::Id::WEBP=>{ 
            }
            _ =>{}
        }
        let mut 编码器options = Dictionary::default();
        编码器options.set("介绍", "Li文件浏览器");
        编码器options.set("vframes", "1");           //单帧的图片
        编码器options.set("lossless", "0");          //无损的  0关闭
        // 编码器options.set("crf", "18");              // libx264 / libx265（H.264 / H.265 视频编码器）  范围一般为 18-28  18 = 最高质量
        // 编码器options.set("preset", "default");              //使用预设 ---- 会覆盖上面的配置
        let mut 视频编码器 = enc_ctx.open_as_with(编码内核,编码器options)?;
        视频编码器.send_frame(&截图帧)?;                 //💖💖💖 后面必须跟   发送结束eof
        视频编码器.send_eof()?;
        let mut 最终图pkt = ffmpeg_next::packet::Packet::empty();
        视频编码器.receive_packet(&mut 最终图pkt).map_err(|err|anyhow_err!("webp视频编码器 无法接收packet -->  {err}"))?;
        
        if let Some(mut out_ctx) = 输出_ctx {
            // 保存图片到本地
            let mut 全局metadata = Dictionary::default();
            全局metadata.set("介绍", "Li文件浏览器");
            全局metadata.set("vframes", "1");             //单帧的图片

            let mut 输出流 =  out_ctx.add_stream(编码内核)?;
            输出流.set_parameters(&视频编码器);              //💖💖💖 必不可少  设置流信息        输出流.set_metadata(语义化配置.clone());
            输出流.set_metadata(全局metadata.clone());
            输出流.set_rate((1, 25));
            out_ctx.set_metadata(全局metadata);
            out_ctx.write_header()?;                       //💖💖💖 必不可少  写入文件头部信息（如格式、流参数 
    
            最终图pkt.set_pts(Some(0));                     // Presentation Time Stamp  该帧的显示时间
            最终图pkt.set_dts(Some(0));                     // Decoding Time Stamp 
            最终图pkt.set_stream(0);
            最终图pkt.set_position(0);
            最终图pkt.set_duration(0);
            最终图pkt.write_interleaved( &mut out_ctx )?; // 发送编码后的数据包（Packet）到输出文件
            out_ctx.write_trailer()?;                      // 最后调用 write_trailer() 写入文件尾部元数据（如索引、时长）
            // eprintln!("用时🔥🔥🔥： {}",   std::time::SystemTime::now().duration_since(self.启动时间).unwrap().as_millis()  );
            // format::context::output::dump(&out_ctx, 0, Some(&self.输出路径));  //输出调试信息
            Ok("".to_string())
        }else{
            // 生成DataURL
            let 图片数据 = 最终图pkt.data().ok_or(anyhow_err!("无法解析编码后 packet 的webp图片"))?;
            let 最终字符串 = BASE64.encode(图片数据);

            if 最终字符串==""{ return  Err(anyhow_err!("webp 转 BASE64 错误")); }
            Ok(format!("data:image/webp;base64,{}", 最终字符串))
        }
    }

    pub fn 应用_读取媒体文件信息(input_path:impl AsRef<str>)-> anyhow_Result<Value>{
        let mut 输出文本 = json!({
            "路径": input_path.as_ref(),  
        });
        
        ffmpeg_next::init().map_err(|err|anyhow_err!("ffmpeg_next::init [错误]: {err}"))?;
        match ffmpeg_next::format::input(input_path.as_ref()){
            Err(err)  => {  输出文本["err"] = json文本!(format!("ffmpeg_next::input [错误]: {err}")); }
            Ok(输入_ctx) => {
                let 视频时长s =  输入_ctx.duration() as f64 / f64::from(ffmpeg_next::ffi::AV_TIME_BASE);
                输出文本["视频时长"] = json!(format!("{}  ({:.3} 秒)", TIME时间::time_毫秒转24小时(1000*视频时长s as u128), 视频时长s));
                if let Some(stream) = 输入_ctx.streams().best(ffmpeg_next::media::Type::Video) {    输出文本["最佳视频流"] = json!(stream.index());  }
                if let Some(stream) = 输入_ctx.streams().best(ffmpeg_next::media::Type::Audio) {    输出文本["最佳音频流"] = json!(stream.index());  }
                if let Some(stream) = 输入_ctx.streams().best(ffmpeg_next::media::Type::Subtitle) { 输出文本["最佳字幕流"] = json!(stream.index());   }
                for (k, v) in 输入_ctx.metadata().iter() {
                    let key = String::from_utf8_lossy(k.as_bytes()).to_string(); 
                    输出文本["metadata"][key] = json!(String::from_utf8_lossy(v.as_bytes()));
                }
                for stream in 输入_ctx.streams() {
                    let 流索引 = &format!("流 {}",stream.index()); 
                    for (k, v) in stream.metadata().iter() { 
                        let key = String::from_utf8_lossy(k.as_bytes()).to_string(); 
                        输出文本[流索引]["metadata"][key] = json!(String::from_utf8_lossy(v.as_bytes()));
                    }
                    let codec = ffmpeg_next::codec::context::Context::from_parameters(stream.parameters())?;
                    输出文本[流索引]["stream index"] = json!(stream.index());
                    输出文本[流索引]["codec id 编码格式"] = json文本!(codec.id());
                    输出文本[流索引]["codec medium"] = json文本!(codec.medium());
                    
                    let 视频时长s =  stream.duration() as f64 * f64::from(stream.time_base());
                    输出文本[流索引]["duration 视频时长"] = json!(format!("{}  ({:.3} 秒)", TIME时间::time_毫秒转24小时(1000*视频时长s as u128), 视频时长s));
                    输出文本[流索引]["duration (stream timebase)"] = json!(stream.duration());  
                    输出文本[流索引]["rate"] = json文本!(stream.rate());
                    输出文本[流索引]["frames 帧数"] = json!(stream.frames());
    
                    输出文本[流索引]["time_base 秒"] = json文本!(stream.time_base());
                    输出文本[流索引]["start_time"] = json!(stream.start_time()); 
                    输出文本[流索引]["disposition"] = json文本!(stream.disposition());
                    输出文本[流索引]["discard"] = json文本!(stream.discard());
                    if codec.medium() == ffmpeg_next::media::Type::Video {
                        if let Ok(video) = codec.decoder().video() {
                            输出文本[流索引]["视频流"]["bit_rate 码流"] = json!(video.bit_rate());
                            输出文本[流索引]["视频流"]["video.format 像素格式"] = json文本!(video.format());
                            输出文本[流索引]["视频流"]["video.width"] = json!(video.width());
                            输出文本[流索引]["视频流"]["video.height"] = json!(video.height());
                            输出文本[流索引]["视频流"]["video.aspect_ratio"] = json文本!(video.aspect_ratio());
                            输出文本[流索引]["视频流"]["max_rate"] = json!(video.max_bit_rate());
                            输出文本[流索引]["视频流"]["delay"] = json!(video.delay());
    
                            输出文本[流索引]["视频流"]["video.color_space"] = json文本!(video.color_space());
                            输出文本[流索引]["视频流"]["video.color_range"] = json文本!(video.color_range());
                            输出文本[流索引]["视频流"]["video.color_primaries"] = json文本!(video.color_primaries());
                            输出文本[流索引]["视频流"]["video.color_transfer_characteristic"] = json文本!(video.color_transfer_characteristic());
                            输出文本[流索引]["视频流"]["video.chroma_location"] = json文本!(video.chroma_location());
                            输出文本[流索引]["视频流"]["video.has_b_frames"] = json!(video.has_b_frames());
                            输出文本[流索引]["视频流"]["video.references"] = json!(video.references());
                            输出文本[流索引]["视频流"]["video.intra_dc_precision"] = json!(video.intra_dc_precision()); 
                        }
                    }
                    else if codec.medium() == ffmpeg_next::media::Type::Audio {
                        if let Ok(audio) = codec.decoder().audio() {
                            输出文本[流索引]["音频流"]["bit_rate 码流"] = json!(audio.bit_rate());
                            输出文本[流索引]["音频流"]["audio.rate 采样率"] = json!(audio.rate()); 
                            输出文本[流索引]["音频流"]["audio.channels 声道"] = json!(audio.channels()); 
                            输出文本[流索引]["音频流"]["audio.format"] = json文本!(audio.format()); 
                            输出文本[流索引]["音频流"]["audio.frames"] = json!(audio.frames());
                            输出文本[流索引]["音频流"]["max_rate"] = json!(audio.max_bit_rate());
                            输出文本[流索引]["音频流"]["delay"] = json!(audio.delay()); 
                            
                            输出文本[流索引]["音频流"]["audio.align"] = json!(audio.align()); 
                            输出文本[流索引]["音频流"]["audio.channel_layout"] = json文本!(audio.channel_layout()); 
                        }
                    }
                }
            } 
        };
        Ok(输出文本)
        
        // UnwindSafe 是一个标记 trait，用于表示一个类型在 panic 发生时是“安全”的。它的主要作用是确保某些类型在 catch_unwind 中不会因为 panic 而导致未定义行为。
        // let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
        //     执行查询()
        // })); 
        // match result {
        //     Ok(查询结果) => { 查询结果 }
        //     Err(_) => { return Err(anyhow_err!("ffmpeg_文件信息 崩溃panic"));  }
        // }
    }
}


 























/* 

 // {   use image;
        //     let rgba_data = 截图帧.data(0);       //RGBA
        //     let mut 最终图片  = Vec::new();
        //     let encoder = image::codecs::webp::WebPEncoder::new_lossless(&mut 最终图片);
        //     let image_buffer= image::ImageBuffer::<image::Rgba<u8>, _>::from_raw(截图帧.width(), 截图帧.height(), rgba_data)            // let img = image::load_from_memory(data)?;  
        //                             .ok_or(anyhow_err!("rgba格式帧 无法转  ImageBuffer  "))?; 
        //     image_buffer.write_with_encoder(encoder)?; 
        //     最终dateurl = BASE64.encode(&最终图片); 
        // }


//速度很慢
pub fn 图片缩略图(input_path:&str, size:u32)->anyhow_Result<String>{  
    use image;
    let 路径对象 = Path::new(input_path);
    let 扩展名 = 路径对象.extension().unwrap_or_default().to_str().unwrap_or_default().to_string().to_lowercase();
    if 扩展名 == "svg" {
        let 图片字符串 = fs::read(input_path)?;
        let base64_encoded = BASE64.encode(&图片字符串);
        return Ok(format!("data:image/svg+xml;base64,{}", base64_encoded));
    }

    let 二进制文件 = fs::read(&input_path)?; 
    let input图片 = image::load_from_memory(&二进制文件)?;    //速度很慢
    // let input图片 = reader.decode()?;
    let img = input图片.thumbnail(size, size);

    let mut 最终图片  = Vec::new();
    let encoder = image::codecs::webp::WebPEncoder::new_lossless(&mut 最终图片);
    img.write_with_encoder(encoder)?;
    let base64_encoded = BASE64.encode(&最终图片);
    
    if base64_encoded!=""{  Ok(format!("data:image/webp;base64,{}", base64_encoded)) }
    else{ Err(anyhow_err!("格式转webp错误")) }

}


     pub fn 应用_截图转webp格式dataurl(&mut self, size:u32, 跳转时间比例:usize)->anyhow_Result<String>{
        self.seek_跳到时间比例(跳转时间比例);
        let mut 截图帧 = self.解码_截图一帧().map_err(|err|anyhow_err!("解码_截图一帧 错误-->  {err}"))?;
        截图帧 = Self::帧缩放器(&截图帧, Some(size), Pixel::YUVA420P)?;

        // 配置 avif 编码器
        let codec = codec::encoder::find(codec::Id::WEBP).ok_or(anyhow_err!("找不到 WebP 解码器"))?;
        let mut enc_ctx = codec::context::Context::new_with_codec(codec).encoder().video()?; 
        enc_ctx.set_format(ffmpeg_next::format::Pixel::YUVA420P);     // BGRA  YUV420P    YUVA420P
        enc_ctx.set_height(截图帧.height());
        enc_ctx.set_width(截图帧.width());
        enc_ctx.set_time_base((1, 25));                        // 设置帧率      (1, 25) 表示每个时间单位是 1/25 秒。 
        // enc_ctx.set_compression(Some(0));                   //webp默认4
        // enc_ctx.set_quality(75);                            //webp默认75
        
        let mut 语义化配置 = Dictionary::new();
        语义化配置.set("介绍", "Li文件浏览器");
        语义化配置.set("lossless", "0");              //无损的  0关闭
        // 语义化配置.set("preset", "default");        //使用预设 ---- 会覆盖上面的配置

        let mut 视频编码器 = enc_ctx.open_with(语义化配置)?;
        let mut 最终图pkt = ffmpeg_next::packet::Packet::empty();  
        视频编码器.send_frame(&截图帧)?;       //💖💖💖 后面必须跟   发送结束eof
        视频编码器.send_eof()?;
        视频编码器.receive_packet(&mut 最终图pkt).map_err(|err|anyhow_err!("webp视频编码器 无法接收packet -->  {err}"))?;
        
        // 生成DataURL
        let 图片数据 = 最终图pkt.data().ok_or(anyhow_err!("无法解析编码后 packet 的webp图片"))?;
        let 最终字符串 = BASE64.encode(图片数据);

        if 最终字符串==""{ return  Err(anyhow_err!("webp 转 BASE64 错误")); }
        Ok(format!("data:image/webp;base64,{}", 最终字符串))
    }

*/


