@@ -20,43 +20,32 @@ class FFmpegCommandBuilder {
2020 final ua = headers? ['user-agent' ] ?? defaultUserAgent;
2121 final headerStr = _buildHeader (headers);
2222 final normalizedOutputPath = '$outputDir ${Platform .pathSeparator }%Y%m%d_%H%M%S.ts' ;
23+ final rwTimeoutMicro = (rwTimeout * 1000000 ).clamp (0 , 2147483647 );
24+
2325 final args = < String > [
2426 '-y' ,
2527 '-hide_banner' ,
2628 '-loglevel' , 'info' ,
27- // 允许的协议白名单,确保直播流能正常加载
2829 '-protocol_whitelist' , 'httpproxy,udp,rtp,tcp,tls,data,file,http,https,crypto' ,
29-
30- // --- 重连与网络优化 ---
3130 '-reconnect' , '1' ,
3231 '-reconnect_streamed' , '1' ,
3332 '-reconnect_delay_max' , '2' ,
3433 '-reconnect_at_eof' , '1' ,
35- '-rw_timeout' , '${ rwTimeout * 1000000 }' , // 转化为微秒
34+ '-rw_timeout' , rwTimeoutMicro. toString (),
3635 '-max_delay' , '5000000' ,
3736 '-thread_queue_size' , threadQueueSize.toString (),
38-
39- // --- 身份伪装 ---
40- '-user_agent' , _quote (ua),
41- if (headerStr.isNotEmpty) ...['-headers' , _quote (headerStr)], // 关键:headers必须包裹在引号内
42- // --- 输入 ---
43- '-i' , _quote (url),
44-
45- // --- 轨道处理 ---
46- // 使用 copy 模式避免 CPU 占用过高
37+ '-user_agent' , ua,
38+ if (headerStr.isNotEmpty) ...['-headers' , _quote (headerStr)], //关键:headers
39+ '-i' , url,
4740 '-map' , preferBestStream ? '0:v:0' : '0:v' ,
4841 '-map' , preferBestStream ? '0:a:0' : '0:a' ,
4942 '-c' , 'copy' ,
50-
51- // --- 分段逻辑 ---
5243 '-f' , 'segment' ,
5344 '-segment_format' , 'mpegts' ,
5445 '-segment_time' , segmentTime.toString (),
5546 '-reset_timestamps' , '1' ,
5647 '-strftime' , '1' ,
57-
58- // 输出路径 (使用 .ts 格式以防断流导致文件损坏)
59- _quote (normalizedOutputPath),
48+ normalizedOutputPath,
6049 ];
6150
6251 return args.join (' ' );
@@ -68,8 +57,6 @@ class FFmpegCommandBuilder {
6857 .where ((e) => e.key.toLowerCase () != 'user-agent' )
6958 .map ((e) => '${e .key }: ${e .value }' )
7059 .join ('\r\n ' );
71-
72- // FFmpeg 要求 headers 末尾也必须有换行符
7360 return lines.isEmpty ? '' : '$lines \r\n ' ;
7461 }
7562}
0 commit comments