├── LICENSE ├── Note. Colorspaces & bitdepth formats supported by x265 & ffmpeg.txt ├── Note. x265支持的色彩空间表以及ffmpeg支持的色彩空间.txt ├── READ-EN-AP.md ├── READ-ZH-CN.md ├── README.md ├── S1-4.png ├── S5n.png ├── bbenc-source ├── 1.「Environment setup 代码运行环境设置」.ps1 ├── 1.「Installation」Configure PSScript permission.txt ├── 1.「安装」设置PS脚本运行权限.txt ├── Step 2-5 in CHS │ ├── 2.「生成单任务模式待调用容器」.ps1 │ ├── 2.「生成大批量模式待调用容器」.ps1 │ ├── 3.「生成调用单任务编码的主控」.ps1 │ ├── 3.「生成调用大批量编码的主控」.ps1 │ └── 5.「生成基于ffmpeg的单任务封装命令模板」.ps1 └── Step 2-5 in ENG │ ├── 2.「Generate vessel for multiple encodes」.ps1 │ ├── 2.「Generate vessel for singular encode」.ps1 │ ├── 3.「Generate controller for multiple encodes」.ps1 │ ├── 3.「Generate controller for singular encode」.ps1 │ └── 5.「Generate multiplexing batch (ffmpeg)」.ps1 ├── bbenc-ttl1.png ├── bbenc-ttl2.png ├── bbenc-ttl3.png ├── bbenc-ttl4.png ├── bbenc-ttl5en.png ├── bbenc-ttl5zh.png └── pp_tip_qrcode.png /Note. Colorspaces & bitdepth formats supported by x265 & ffmpeg.txt: -------------------------------------------------------------------------------- 1 | Select the corresponding color space according to the -pix_fmt parameter measured by ffprobe 2 | The YUV for MPEG (Y4M) pipe scheme is used, so only yuv related options are provided here 3 | nv12, nv16 options x265 also support, but need to manually change to rawviddeo pipe scheme 4 | 5 | Option A - yuv420p8 6 | - Color Channels = 3, Bit Density = 8bit (#00+2^8=#FF) 7 | - Under ffmpeg -pix_fmt yuv420p, under x265 -input-csp i420 8 | - √ x264 x265 9 | 10 | Option B - yuv420p10 11 | - Color Channels = 3, Bit Density = 10bit (#00+2^10=#FF) 12 | - Under ffmpeg -pix_fmt yuv420p10le, under x265 -input-csp i420 13 | - √ x264 x265 14 | 15 | Option C - yuv420p12 16 | - Color Channels = 3, Bit Depth = 12bit (#00+2^12=#FF) 17 | - Under ffmpeg -pix_fmt yuv420p12le, under x265 -input-csp i420 18 | - √x265 19 | 20 | Option D - yuv422p8 21 | - Color Channels = 3, Bit Density = 8bit (#00+2^8=#FF) 22 | - under ffmpeg -pix_fmt yuv422p, under x265 -input-csp i422 23 | - √ x264 x265 24 | 25 | Option E - yuv422p10 26 | - Color Channels = 3, Bit Density = 10bit (#00+2^10=#FF) 27 | - under ffmpeg -pix_fmt yuv422p10le, under x265 -input-csp i422 28 | - √ x264 x265 29 | 30 | Option F - yuv422p12 31 | - Color Channels = 3, Bit Depth = 12bit (#00+2^12=#FF) 32 | - under ffmpeg -pix_fmt yuv422p12le, under x265 -input-csp i422 33 | - √x265 34 | 35 | Option G - yuv444p8 36 | - Color Channels = 3, Bit Density = 8bit (#00+2^8=#FF) 37 | - Under ffmpeg -pix_fmt yuv444p, under x265 -input-csp i444 38 | - √ x264 x265 39 | 40 | Option H - yuv444p10 41 | - Color Channels = 3, Bit Density = 10bit (#00+2^10=#FF) 42 | - Under ffmpeg -pix_fmt yuv444p10le, under x265 -input-csp i444 43 | - √ x264 x265 44 | 45 | Option I - yuv444p12 46 | - Color Channels = 3, Bit Depth = 12bit (#00+2^10=#FF) 47 | - Under ffmpeg -pix_fmt yuv444p12le, under x265 -input-csp i444 48 | - √x265 49 | 50 | Option J - yuv400 (black and white) 51 | - Color Channels = 1, Bit Density = 8bit (#00+2^8=#FF) 52 | - under ffmpeg -pix_fmt gray, under x265 -input-csp i400 53 | - Not compatible with HEVC Main/Main10 Profile 54 | - √ x264 x265 55 | 56 | Option K - yuv400 (black and white) 57 | - Color Channels = 1, Bit Depth = 10bit (#00+2^10=#FF) 58 | - under ffmpeg -pix_fmt gray10le, under x265 -input-csp i400 59 | - Not compatible with HEVC Main/Main10 Profile 60 | - √ x264 x265 61 | 62 | Option L - yuv400 (black and white) 63 | - Color Channels = 1, Bit Depth = 12bit (#00+2^12=#FF) 64 | - under ffmpeg -pix_fmt gray12le, under x265 -input-csp i400 65 | - Not compatible with HEVC Main/Main10 Profile 66 | - √x265 67 | 68 | Option M - nv12 (need to manually change to rawvideo pipe scheme) 69 | - Color Channels = 2, Bit Depth = 12bit (#00+2^12=#FF) 70 | - under ffmpeg -pix_fmt nv12, under x265 -input-csp nv12 71 | - √x265 72 | 73 | Option N - nv16 (need to manually change to rawvideo pipe scheme) 74 | - Color Channels = 2, Bit Depth = 16bit (#00+2^16=#FF) 75 | - under ffmpeg -pix_fmt nv16, under x265 -input-csp nv16 76 | - √x265 77 | 78 | ffmpeg -pix_fmts output (and -pix_fmt are two arguments): 79 | I.... = Supported Input format for conversion 80 | .O... = Supported Output format for conversion 81 | ..H.. = Hardware accelerated format 82 | ...P. = Paletted format 83 | ....B = Bitstream format 84 | FLAGS NAME NB_COMPONENTS BITS_PER_PIXEL BIT_DEPTHS 85 | ----- 86 | IO... yuv420p 3 12 8-8-8 87 | IO... yuyv422 3 16 8-8-8 88 | IO... rgb24 3 24 8-8-8 89 | IO... bgr24 3 24 8-8-8 90 | IO... yuv422p 3 16 8-8-8 91 | IO... yuv444p 3 24 8-8-8 92 | IO... yuv410p 3 9 8-8-8 93 | IO... yuv411p 3 12 8-8-8 94 | IO... gray 1 8 8 95 | IO..B monow 1 1 1 96 | IO..B monob 1 1 1 97 | I..P. pal8 1 8 8 98 | IO... yuvj420p 3 12 8-8-8 99 | IO... yuvj422p 3 16 8-8-8 100 | IO... yuvj444p 3 24 8-8-8 101 | IO... uyvy422 3 16 8-8-8 102 | ..... uyyvyy411 3 12 8-8-8 103 | IO... bgr8 3 8 3-3-2 104 | .O..B bgr4 3 4 1-2-1 105 | IO... bgr4_byte 3 4 1-2-1 106 | IO... rgb8 3 8 2-3-3 107 | .O..B rgb4 3 4 1-2-1 108 | IO... rgb4_byte 3 4 1-2-1 109 | IO... nv12 3 12 8-8-8 110 | IO... nv21 3 12 8-8-8 111 | IO... argb 4 32 8-8-8-8 112 | IO... rgba 4 32 8-8-8-8 113 | IO... abgr 4 32 8-8-8-8 114 | IO... bgra 4 32 8-8-8-8 115 | IO... gray16be 1 16 16 116 | IO... gray16le 1 16 16 117 | IO... yuv440p 3 16 8-8-8 118 | IO... yuvj440p 3 16 8-8-8 119 | IO... yuva420p 4 20 8-8-8-8 120 | IO... rgb48be 3 48 16-16-16 121 | IO... rgb48le 3 48 16-16-16 122 | IO... rgb565be 3 16 5-6-5 123 | IO... rgb565le 3 16 5-6-5 124 | IO... rgb555be 3 15 5-5-5 125 | IO... rgb555le 3 15 5-5-5 126 | IO... bgr565be 3 16 5-6-5 127 | IO... bgr565le 3 16 5-6-5 128 | IO... bgr555be 3 15 5-5-5 129 | IO... bgr555le 3 15 5-5-5 130 | ..H.. vaapi 0 0 0 131 | IO... yuv420p16le 3 24 16-16-16 132 | IO... yuv420p16be 3 24 16-16-16 133 | IO... yuv422p16le 3 32 16-16-16 134 | IO... yuv422p16be 3 32 16-16-16 135 | IO... yuv444p16le 3 48 16-16-16 136 | IO... yuv444p16be 3 48 16-16-16 137 | ..H.. dxva2_vld 0 0 0 138 | IO... rgb444le 3 12 4-4-4 139 | IO... rgb444be 3 12 4-4-4 140 | IO... bgr444le 3 12 4-4-4 141 | IO... bgr444be 3 12 4-4-4 142 | IO... ya8 2 16 8-8 143 | IO... bgr48be 3 48 16-16-16 144 | IO... bgr48le 3 48 16-16-16 145 | IO... yuv420p9be 3 13 9-9-9 146 | IO... yuv420p9le 3 13 9-9-9 147 | IO... yuv420p10be 3 15 10-10-10 148 | IO... yuv420p10le 3 15 10-10-10 149 | IO... yuv422p10be 3 20 10-10-10 150 | IO... yuv422p10le 3 20 10-10-10 151 | IO... yuv444p9be 3 27 9-9-9 152 | IO... yuv444p9le 3 27 9-9-9 153 | IO... yuv444p10be 3 30 10-10-10 154 | IO... yuv444p10le 3 30 10-10-10 155 | IO... yuv422p9be 3 18 9-9-9 156 | IO... yuv422p9le 3 18 9-9-9 157 | IO... gbrp 3 24 8-8-8 158 | IO... gbrp9be 3 27 9-9-9 159 | IO... gbrp9le 3 27 9-9-9 160 | IO... gbrp10be 3 30 10-10-10 161 | IO... gbrp10le 3 30 10-10-10 162 | IO... gbrp16be 3 48 16-16-16 163 | IO... gbrp16le 3 48 16-16-16 164 | IO... yuva422p 4 24 8-8-8-8 165 | IO... yuva444p 4 32 8-8-8-8 166 | IO... yuva420p9be 4 22 9-9-9-9 167 | IO... yuva420p9le 4 22 9-9-9-9 168 | IO... yuva422p9be 4 27 9-9-9-9 169 | IO... yuva422p9le 4 27 9-9-9-9 170 | IO... yuva444p9be 4 36 9-9-9-9 171 | IO... yuva444p9le 4 36 9-9-9-9 172 | IO... yuva420p10be 4 25 10-10-10-10 173 | IO... yuva420p10le 4 25 10-10-10-10 174 | IO... yuva422p10be 4 30 10-10-10-10 175 | IO... yuva422p10le 4 30 10-10-10-10 176 | IO... yuva444p10be 4 40 10-10-10-10 177 | IO... yuva444p10le 4 40 10-10-10-10 178 | IO... yuva420p16be 4 40 16-16-16-16 179 | IO... yuva420p16le 4 40 16-16-16-16 180 | IO... yuva422p16be 4 48 16-16-16-16 181 | IO... yuva422p16le 4 48 16-16-16-16 182 | IO... yuva444p16be 4 64 16-16-16-16 183 | IO... yuva444p16le 4 64 16-16-16-16 184 | ..H.. vdpau 0 0 0 185 | IO... xyz12le 3 36 12-12-12 186 | IO... xyz12be 3 36 12-12-12 187 | IO... nv16 3 16 8-8-8 188 | ..... nv20le 3 20 10-10-10 189 | ..... nv20be 3 20 10-10-10 190 | IO... rgba64be 4 64 16-16-16-16 191 | IO... rgba64le 4 64 16-16-16-16 192 | IO... bgra64be 4 64 16-16-16-16 193 | IO... bgra64le 4 64 16-16-16-16 194 | IO... yvyu422 3 16 8-8-8 195 | IO... ya16be 2 32 16-16 196 | IO... ya16le 2 32 16-16 197 | IO... gbrap 4 32 8-8-8-8 198 | IO... gbrap16be 4 64 16-16-16-16 199 | IO... gbrap16le 4 64 16-16-16-16 200 | ..H.. qsv 0 0 0 201 | ..H.. mmal 0 0 0 202 | ..H.. d3d11va_vld 0 0 0 203 | ..H.. cuda 0 0 0 204 | IO... 0rgb 3 24 8-8-8 205 | IO... rgb0 3 24 8-8-8 206 | IO... 0bgr 3 24 8-8-8 207 | IO... bgr0 3 24 8-8-8 208 | IO... yuv420p12be 3 18 12-12-12 209 | IO... yuv420p12le 3 18 12-12-12 210 | IO... yuv420p14be 3 21 14-14-14 211 | IO... yuv420p14le 3 21 14-14-14 212 | IO... yuv422p12be 3 24 12-12-12 213 | IO... yuv422p12le 3 24 12-12-12 214 | IO... yuv422p14be 3 28 14-14-14 215 | IO... yuv422p14le 3 28 14-14-14 216 | IO... yuv444p12be 3 36 12-12-12 217 | IO... yuv444p12le 3 36 12-12-12 218 | IO... yuv444p14be 3 42 14-14-14 219 | IO... yuv444p14le 3 42 14-14-14 220 | IO... gbrp12be 3 36 12-12-12 221 | IO... gbrp12le 3 36 12-12-12 222 | IO... gbrp14be 3 42 14-14-14 223 | IO... gbrp14le 3 42 14-14-14 224 | IO... yuvj411p 3 12 8-8-8 225 | I.... bayer_bggr8 3 8 2-4-2 226 | I.... bayer_rggb8 3 8 2-4-2 227 | I.... bayer_gbrg8 3 8 2-4-2 228 | I.... bayer_grbg8 3 8 2-4-2 229 | I.... bayer_bggr16le 3 16 4-8-4 230 | I.... bayer_bggr16be 3 16 4-8-4 231 | I.... bayer_rggb16le 3 16 4-8-4 232 | I.... bayer_rggb16be 3 16 4-8-4 233 | I.... bayer_gbrg16le 3 16 4-8-4 234 | I.... bayer_gbrg16be 3 16 4-8-4 235 | I.... bayer_grbg16le 3 16 4-8-4 236 | I.... bayer_grbg16be 3 16 4-8-4 237 | ..H.. xvmc 0 0 0 238 | IO... yuv440p10le 3 20 10-10-10 239 | IO... yuv440p10be 3 20 10-10-10 240 | IO... yuv440p12le 3 24 12-12-12 241 | IO... yuv440p12be 3 24 12-12-12 242 | IO... ayuv64le 4 64 16-16-16-16 243 | ..... ayuv64be 4 64 16-16-16-16 244 | ..H.. videotoolbox_vld 0 0 0 245 | IO... p010le 3 15 10-10-10 246 | IO... p010be 3 15 10-10-10 247 | IO... gbrap12be 4 48 12-12-12-12 248 | IO... gbrap12le 4 48 12-12-12-12 249 | IO... gbrap10be 4 40 10-10-10-10 250 | IO... gbrap10le 4 40 10-10-10-10 251 | ..H.. mediacodec 0 0 0 252 | IO... gray12be 1 12 12 253 | IO... gray12le 1 12 12 254 | IO... gray10be 1 10 10 255 | IO... gray10le 1 10 10 256 | IO... p016le 3 24 16-16-16 257 | IO... p016be 3 24 16-16-16 258 | ..H.. d3d11 0 0 0 259 | IO... gray9be 1 9 9 260 | IO... gray9le 1 9 9 261 | IO... gbrpf32be 3 96 32-32-32 262 | IO... gbrpf32le 3 96 32-32-32 263 | IO... gbrapf32be 4 128 32-32-32-32 264 | IO... gbrapf32le 4 128 32-32-32-32 265 | ..H.. drm_prime 0 0 0 266 | ..H.. opencl 0 0 0 267 | IO... gray14be 1 14 14 268 | IO... gray14le 1 14 14 269 | IO... grayf32be 1 32 32 270 | IO... grayf32le 1 32 32 271 | IO... yuva422p12be 4 36 12-12-12-12 272 | IO... yuva422p12le 4 36 12-12-12-12 273 | IO... yuva444p12be 4 48 12-12-12-12 274 | IO... yuva444p12le 4 48 12-12-12-12 275 | IO... nv24 3 24 8-8-8 276 | IO... nv42 3 24 8-8-8 277 | ..H.. vulkan 0 0 0 278 | ..... y210be 3 20 10-10-10 279 | IO... y210le 3 20 10-10-10 280 | IO... x2rgb10le 3 30 10-10-10 281 | ..... x2rgb10be 3 30 10-10-10 282 | IO... x2bgr10le 3 30 10-10-10 283 | ..... x2bgr10be 3 30 10-10-10 284 | IO... p210be 3 20 10-10-10 285 | IO... p210le 3 20 10-10-10 286 | IO... p410be 3 30 10-10-10 287 | IO... p410le 3 30 10-10-10 288 | IO... p216be 3 32 16-16-16 289 | IO... p216le 3 32 16-16-16 290 | IO... p416be 3 48 16-16-16 291 | IO... p416le 3 48 16-16-16 292 | IO... vuya 4 32 8-8-8-8 293 | I.... rgbaf16be 4 64 16-16-16-16 294 | I.... rgbaf16le 4 64 16-16-16-16 295 | IO... vuyx 3 24 8-8-8 296 | IO... p012le 3 18 12-12-12 297 | IO... p012be 3 18 12-12-12 298 | ..... y212be 3 24 12-12-12 299 | IO... y212le 3 24 12-12-12 300 | ....B xv30be 3 30 10-10-10 301 | IO... xv30le 3 30 10-10-10 302 | ..... xv36be 3 36 12-12-12 303 | IO... xv36le 3 36 12-12-12 304 | ..... rgbf32be 3 96 32-32-32 305 | ..... rgbf32le 3 96 32-32-32 306 | ..... rgbaf32be 4 128 32-32-32-32 307 | ..... rgbaf32le 4 128 32-32-32-32 -------------------------------------------------------------------------------- /Note. x265支持的色彩空间表以及ffmpeg支持的色彩空间.txt: -------------------------------------------------------------------------------- 1 | 根据ffprobe测出的-pix_fmt参数选择对应的色彩空间 2 | 使用了YUV for MPEG (Y4M) pipe方案, 所以此处只提供yuv相关选项 3 | nv12, nv16选项x265也支持, 但需要手动更改为rawviddeo pipe方案 4 | 5 | 选项A - yuv420p8 6 | - 色彩通道数 = 3, 位深密度 = 8bit (#00+2^8=#FF) 7 | - ffmpeg下-pix_fmt yuv420p, x265下--input-csp i420 8 | - √ x264 x265 9 | 10 | 选项B - yuv420p10 11 | - 色彩通道数 = 3, 位深密度 = 10bit (#00+2^10=#FF) 12 | - ffmpeg下-pix_fmt yuv420p10le, x265下--input-csp i420 13 | - √ x264 x265 14 | 15 | 选项C - yuv420p12 16 | - 色彩通道数 = 3, 位深密度 = 12bit (#00+2^12=#FF) 17 | - ffmpeg下-pix_fmt yuv420p12le, x265下--input-csp i420 18 | - √ x265 19 | 20 | 选项D - yuv422p8 21 | - 色彩通道数 = 3, 位深密度 = 8bit (#00+2^8=#FF) 22 | - ffmpeg下-pix_fmt yuv422p, x265下--input-csp i422 23 | - √ x264 x265 24 | 25 | 选项E - yuv422p10 26 | - 色彩通道数 = 3, 位深密度 = 10bit (#00+2^10=#FF) 27 | - ffmpeg下-pix_fmt yuv422p10le, x265下--input-csp i422 28 | - √ x264 x265 29 | 30 | 选项F - yuv422p12 31 | - 色彩通道数 = 3, 位深密度 = 12bit (#00+2^12=#FF) 32 | - ffmpeg下-pix_fmt yuv422p12le, x265下--input-csp i422 33 | - √ x265 34 | 35 | 选项G - yuv444p8 36 | - 色彩通道数 = 3, 位深密度 = 8bit (#00+2^8=#FF) 37 | - ffmpeg下-pix_fmt yuv444p, x265下--input-csp i444 38 | - √ x264 x265 39 | 40 | 选项H - yuv444p10 41 | - 色彩通道数 = 3, 位深密度 = 10bit (#00+2^10=#FF) 42 | - ffmpeg下-pix_fmt yuv444p10le, x265下--input-csp i444 43 | - √ x264 x265 44 | 45 | 选项I - yuv444p12 46 | - 色彩通道数 = 3, 位深密度 = 12bit (#00+2^10=#FF) 47 | - ffmpeg下-pix_fmt yuv444p12le, x265下--input-csp i444 48 | - √ x265 49 | 50 | 选项J - yuv400 (黑白) 51 | - 色彩通道数 = 1, 位深密度 = 8bit (#00+2^8=#FF) 52 | - ffmpeg下-pix_fmt gray, x265下--input-csp i400 53 | - 不兼容HEVC Main/Main10 Profile 54 | - √ x264 x265 55 | 56 | 选项K - yuv400 (黑白) 57 | - 色彩通道数 = 1, 位深密度 = 10bit (#00+2^10=#FF) 58 | - ffmpeg下-pix_fmt gray10le, x265下--input-csp i400 59 | - 不兼容HEVC Main/Main10 Profile 60 | - √ x264 x265 61 | 62 | 选项L - yuv400 (黑白) 63 | - 色彩通道数 = 1, 位深密度 = 12bit (#00+2^12=#FF) 64 | - ffmpeg下-pix_fmt gray12le, x265下--input-csp i400 65 | - 不兼容HEVC Main/Main10 Profile 66 | - √ x265 67 | 68 | 选项M - nv12 (需手动更改为rawvideo pipe方案) 69 | - 色彩通道数 = 2, 位深密度 = 12bit (#00+2^12=#FF) 70 | - ffmpeg下-pix_fmt nv12, x265下--input-csp nv12 71 | - √ x265 72 | 73 | 选项N - nv16 (需手动更改为rawvideo pipe方案) 74 | - 色彩通道数 = 2, 位深密度 = 16bit (#00+2^16=#FF) 75 | - ffmpeg下-pix_fmt nv16, x265下--input-csp nv16 76 | - √ x265 77 | 78 | ffmpeg -pix_fmts输出 (和-pix_fmt<色彩空间>是两个参数): 79 | I.... = Supported Input format for conversion 80 | .O... = Supported Output format for conversion 81 | ..H.. = Hardware accelerated format 82 | ...P. = Paletted format 83 | ....B = Bitstream format 84 | FLAGS NAME NB_COMPONENTS BITS_PER_PIXEL BIT_DEPTHS 85 | ----- 86 | IO... yuv420p 3 12 8-8-8 87 | IO... yuyv422 3 16 8-8-8 88 | IO... rgb24 3 24 8-8-8 89 | IO... bgr24 3 24 8-8-8 90 | IO... yuv422p 3 16 8-8-8 91 | IO... yuv444p 3 24 8-8-8 92 | IO... yuv410p 3 9 8-8-8 93 | IO... yuv411p 3 12 8-8-8 94 | IO... gray 1 8 8 95 | IO..B monow 1 1 1 96 | IO..B monob 1 1 1 97 | I..P. pal8 1 8 8 98 | IO... yuvj420p 3 12 8-8-8 99 | IO... yuvj422p 3 16 8-8-8 100 | IO... yuvj444p 3 24 8-8-8 101 | IO... uyvy422 3 16 8-8-8 102 | ..... uyyvyy411 3 12 8-8-8 103 | IO... bgr8 3 8 3-3-2 104 | .O..B bgr4 3 4 1-2-1 105 | IO... bgr4_byte 3 4 1-2-1 106 | IO... rgb8 3 8 2-3-3 107 | .O..B rgb4 3 4 1-2-1 108 | IO... rgb4_byte 3 4 1-2-1 109 | IO... nv12 3 12 8-8-8 110 | IO... nv21 3 12 8-8-8 111 | IO... argb 4 32 8-8-8-8 112 | IO... rgba 4 32 8-8-8-8 113 | IO... abgr 4 32 8-8-8-8 114 | IO... bgra 4 32 8-8-8-8 115 | IO... gray16be 1 16 16 116 | IO... gray16le 1 16 16 117 | IO... yuv440p 3 16 8-8-8 118 | IO... yuvj440p 3 16 8-8-8 119 | IO... yuva420p 4 20 8-8-8-8 120 | IO... rgb48be 3 48 16-16-16 121 | IO... rgb48le 3 48 16-16-16 122 | IO... rgb565be 3 16 5-6-5 123 | IO... rgb565le 3 16 5-6-5 124 | IO... rgb555be 3 15 5-5-5 125 | IO... rgb555le 3 15 5-5-5 126 | IO... bgr565be 3 16 5-6-5 127 | IO... bgr565le 3 16 5-6-5 128 | IO... bgr555be 3 15 5-5-5 129 | IO... bgr555le 3 15 5-5-5 130 | ..H.. vaapi 0 0 0 131 | IO... yuv420p16le 3 24 16-16-16 132 | IO... yuv420p16be 3 24 16-16-16 133 | IO... yuv422p16le 3 32 16-16-16 134 | IO... yuv422p16be 3 32 16-16-16 135 | IO... yuv444p16le 3 48 16-16-16 136 | IO... yuv444p16be 3 48 16-16-16 137 | ..H.. dxva2_vld 0 0 0 138 | IO... rgb444le 3 12 4-4-4 139 | IO... rgb444be 3 12 4-4-4 140 | IO... bgr444le 3 12 4-4-4 141 | IO... bgr444be 3 12 4-4-4 142 | IO... ya8 2 16 8-8 143 | IO... bgr48be 3 48 16-16-16 144 | IO... bgr48le 3 48 16-16-16 145 | IO... yuv420p9be 3 13 9-9-9 146 | IO... yuv420p9le 3 13 9-9-9 147 | IO... yuv420p10be 3 15 10-10-10 148 | IO... yuv420p10le 3 15 10-10-10 149 | IO... yuv422p10be 3 20 10-10-10 150 | IO... yuv422p10le 3 20 10-10-10 151 | IO... yuv444p9be 3 27 9-9-9 152 | IO... yuv444p9le 3 27 9-9-9 153 | IO... yuv444p10be 3 30 10-10-10 154 | IO... yuv444p10le 3 30 10-10-10 155 | IO... yuv422p9be 3 18 9-9-9 156 | IO... yuv422p9le 3 18 9-9-9 157 | IO... gbrp 3 24 8-8-8 158 | IO... gbrp9be 3 27 9-9-9 159 | IO... gbrp9le 3 27 9-9-9 160 | IO... gbrp10be 3 30 10-10-10 161 | IO... gbrp10le 3 30 10-10-10 162 | IO... gbrp16be 3 48 16-16-16 163 | IO... gbrp16le 3 48 16-16-16 164 | IO... yuva422p 4 24 8-8-8-8 165 | IO... yuva444p 4 32 8-8-8-8 166 | IO... yuva420p9be 4 22 9-9-9-9 167 | IO... yuva420p9le 4 22 9-9-9-9 168 | IO... yuva422p9be 4 27 9-9-9-9 169 | IO... yuva422p9le 4 27 9-9-9-9 170 | IO... yuva444p9be 4 36 9-9-9-9 171 | IO... yuva444p9le 4 36 9-9-9-9 172 | IO... yuva420p10be 4 25 10-10-10-10 173 | IO... yuva420p10le 4 25 10-10-10-10 174 | IO... yuva422p10be 4 30 10-10-10-10 175 | IO... yuva422p10le 4 30 10-10-10-10 176 | IO... yuva444p10be 4 40 10-10-10-10 177 | IO... yuva444p10le 4 40 10-10-10-10 178 | IO... yuva420p16be 4 40 16-16-16-16 179 | IO... yuva420p16le 4 40 16-16-16-16 180 | IO... yuva422p16be 4 48 16-16-16-16 181 | IO... yuva422p16le 4 48 16-16-16-16 182 | IO... yuva444p16be 4 64 16-16-16-16 183 | IO... yuva444p16le 4 64 16-16-16-16 184 | ..H.. vdpau 0 0 0 185 | IO... xyz12le 3 36 12-12-12 186 | IO... xyz12be 3 36 12-12-12 187 | IO... nv16 3 16 8-8-8 188 | ..... nv20le 3 20 10-10-10 189 | ..... nv20be 3 20 10-10-10 190 | IO... rgba64be 4 64 16-16-16-16 191 | IO... rgba64le 4 64 16-16-16-16 192 | IO... bgra64be 4 64 16-16-16-16 193 | IO... bgra64le 4 64 16-16-16-16 194 | IO... yvyu422 3 16 8-8-8 195 | IO... ya16be 2 32 16-16 196 | IO... ya16le 2 32 16-16 197 | IO... gbrap 4 32 8-8-8-8 198 | IO... gbrap16be 4 64 16-16-16-16 199 | IO... gbrap16le 4 64 16-16-16-16 200 | ..H.. qsv 0 0 0 201 | ..H.. mmal 0 0 0 202 | ..H.. d3d11va_vld 0 0 0 203 | ..H.. cuda 0 0 0 204 | IO... 0rgb 3 24 8-8-8 205 | IO... rgb0 3 24 8-8-8 206 | IO... 0bgr 3 24 8-8-8 207 | IO... bgr0 3 24 8-8-8 208 | IO... yuv420p12be 3 18 12-12-12 209 | IO... yuv420p12le 3 18 12-12-12 210 | IO... yuv420p14be 3 21 14-14-14 211 | IO... yuv420p14le 3 21 14-14-14 212 | IO... yuv422p12be 3 24 12-12-12 213 | IO... yuv422p12le 3 24 12-12-12 214 | IO... yuv422p14be 3 28 14-14-14 215 | IO... yuv422p14le 3 28 14-14-14 216 | IO... yuv444p12be 3 36 12-12-12 217 | IO... yuv444p12le 3 36 12-12-12 218 | IO... yuv444p14be 3 42 14-14-14 219 | IO... yuv444p14le 3 42 14-14-14 220 | IO... gbrp12be 3 36 12-12-12 221 | IO... gbrp12le 3 36 12-12-12 222 | IO... gbrp14be 3 42 14-14-14 223 | IO... gbrp14le 3 42 14-14-14 224 | IO... yuvj411p 3 12 8-8-8 225 | I.... bayer_bggr8 3 8 2-4-2 226 | I.... bayer_rggb8 3 8 2-4-2 227 | I.... bayer_gbrg8 3 8 2-4-2 228 | I.... bayer_grbg8 3 8 2-4-2 229 | I.... bayer_bggr16le 3 16 4-8-4 230 | I.... bayer_bggr16be 3 16 4-8-4 231 | I.... bayer_rggb16le 3 16 4-8-4 232 | I.... bayer_rggb16be 3 16 4-8-4 233 | I.... bayer_gbrg16le 3 16 4-8-4 234 | I.... bayer_gbrg16be 3 16 4-8-4 235 | I.... bayer_grbg16le 3 16 4-8-4 236 | I.... bayer_grbg16be 3 16 4-8-4 237 | ..H.. xvmc 0 0 0 238 | IO... yuv440p10le 3 20 10-10-10 239 | IO... yuv440p10be 3 20 10-10-10 240 | IO... yuv440p12le 3 24 12-12-12 241 | IO... yuv440p12be 3 24 12-12-12 242 | IO... ayuv64le 4 64 16-16-16-16 243 | ..... ayuv64be 4 64 16-16-16-16 244 | ..H.. videotoolbox_vld 0 0 0 245 | IO... p010le 3 15 10-10-10 246 | IO... p010be 3 15 10-10-10 247 | IO... gbrap12be 4 48 12-12-12-12 248 | IO... gbrap12le 4 48 12-12-12-12 249 | IO... gbrap10be 4 40 10-10-10-10 250 | IO... gbrap10le 4 40 10-10-10-10 251 | ..H.. mediacodec 0 0 0 252 | IO... gray12be 1 12 12 253 | IO... gray12le 1 12 12 254 | IO... gray10be 1 10 10 255 | IO... gray10le 1 10 10 256 | IO... p016le 3 24 16-16-16 257 | IO... p016be 3 24 16-16-16 258 | ..H.. d3d11 0 0 0 259 | IO... gray9be 1 9 9 260 | IO... gray9le 1 9 9 261 | IO... gbrpf32be 3 96 32-32-32 262 | IO... gbrpf32le 3 96 32-32-32 263 | IO... gbrapf32be 4 128 32-32-32-32 264 | IO... gbrapf32le 4 128 32-32-32-32 265 | ..H.. drm_prime 0 0 0 266 | ..H.. opencl 0 0 0 267 | IO... gray14be 1 14 14 268 | IO... gray14le 1 14 14 269 | IO... grayf32be 1 32 32 270 | IO... grayf32le 1 32 32 271 | IO... yuva422p12be 4 36 12-12-12-12 272 | IO... yuva422p12le 4 36 12-12-12-12 273 | IO... yuva444p12be 4 48 12-12-12-12 274 | IO... yuva444p12le 4 48 12-12-12-12 275 | IO... nv24 3 24 8-8-8 276 | IO... nv42 3 24 8-8-8 277 | ..H.. vulkan 0 0 0 278 | ..... y210be 3 20 10-10-10 279 | IO... y210le 3 20 10-10-10 280 | IO... x2rgb10le 3 30 10-10-10 281 | ..... x2rgb10be 3 30 10-10-10 282 | IO... x2bgr10le 3 30 10-10-10 283 | ..... x2bgr10be 3 30 10-10-10 284 | IO... p210be 3 20 10-10-10 285 | IO... p210le 3 20 10-10-10 286 | IO... p410be 3 30 10-10-10 287 | IO... p410le 3 30 10-10-10 288 | IO... p216be 3 32 16-16-16 289 | IO... p216le 3 32 16-16-16 290 | IO... p416be 3 48 16-16-16 291 | IO... p416le 3 48 16-16-16 292 | IO... vuya 4 32 8-8-8-8 293 | I.... rgbaf16be 4 64 16-16-16-16 294 | I.... rgbaf16le 4 64 16-16-16-16 295 | IO... vuyx 3 24 8-8-8 296 | IO... p012le 3 18 12-12-12 297 | IO... p012be 3 18 12-12-12 298 | ..... y212be 3 24 12-12-12 299 | IO... y212le 3 24 12-12-12 300 | ....B xv30be 3 30 10-10-10 301 | IO... xv30le 3 30 10-10-10 302 | ..... xv36be 3 36 12-12-12 303 | IO... xv36le 3 36 12-12-12 304 | ..... rgbf32be 3 96 32-32-32 305 | ..... rgbf32le 3 96 32-32-32 306 | ..... rgbaf32be 4 128 32-32-32-32 307 | ..... rgbaf32le 4 128 32-32-32-32 308 | 309 | 大批量压制+单任务工作流 -------------------------------------------------------------------------------- /READ-ZH-CN.md: -------------------------------------------------------------------------------- 1 | # 原创 PowerShell+CMD 多线路单文件 + 大批量压制 + 封装工具 2 | 3 | 囊括了分析,压制和封装的大型 CLI+GUI 压制辅助工具. (绝对不是因为等不来小丸工具箱更新而做的...) 4 | 5 | ## ☆环境得... 6 | 7 | - 有一两 ffprobe (一般下载 ffmpeg 后附带) 8 | - 多一斤 ffmpeg/Vapoursynth (vspipe)/Avisynth (avs2yuv)/Avisynth (avs2pipemod) 9 | - 含一克 x264/x265 10 | 11 | ## ★优势在... 12 | 13 | - 完全不依赖包括 Python 的编程语言,满足了入门视频剪辑用户一般都不会 Python 的要求 14 | - 自动为 ffmpeg,avs2yuv,x264,x265 填写色彩空间,位深,分辨率和帧率 15 | - 批量压制模式支持 15000 + 次独立的编码,每个压制的间隔都可以选择暂停和停止 16 | - 研发了固定 + 变化 (par-var) 的参数架构,稍加修改就可用于测试各种变量下的影响 17 | - 研发了编码 + 主控 (enc-ctrl) 批处理架构,解决了大批量压制下批处理结构变复杂的问题 18 | - 按代码要求,提供需要的信息即可,使用不需要教程 19 | - 由于不用编译软件,所以能直接编辑源码并测试效果 20 | - 压制部分支持 ffmpeg,vspipe,avs2yuv,avs2pipemod,svfi 及下游 x264,x265的 10 条线路,消灭了一大块学习成本 21 | 22 | ![S1-4.png](S1-4.png) 23 | 24 |

编码/压制工作流的线路图

25 | 26 | 27 | - 封装部分支持导入封装文件,特殊封装文件,视频流,音频流,字幕轨,字体轨,以及四种封装导出格式的多线并发网络 28 | 29 | ![S5n.png](S5n.png) 30 | 31 |

封装工作流的线路图

32 |

实际没有这么复杂,但逻辑上是支持的

33 | 34 | - 批处理能自行清理变量,不影响重复使用 35 | 36 | ----- 37 | 38 | ## ▲怎么用 39 | 1. Windows 11 下确保安装了对应〔文件名语言〕的语言包. 如要处理阿拉伯语的文件名就去`设置 --> 时间和语言 -->[左栏] 语言 --> 添加语言 --> 阿拉伯语`. Windows10 下不需要 40 | 2. 在设置 --> 更新和安全 --> 开发者选项中解除 PowerShell 的运行限制,如图: 41 | ![bbenc-ttl5zh.png](bbenc-ttl5zh.png) 42 | 3. 解压下载好的压缩包 43 | - 步骤2生成待调用的编码批处理零件,由步骤3生成的主控批处理组装和调用 44 | - 步骤5是专门处理视频封装的批处理生成工具,可以单独拎出来用 45 | - 大批量和单文件模式的区别在于一个针对多集视频开发,一个用针对单视频开发 46 | 4. 按大批量或单文件压制需求,照序号顺序右键点击"编辑"或"用PowerShell打开". 照说明文本的要求输入需要的信息,最后得到批处理 47 | ![bbenc-ttl1.png](bbenc-ttl1.png) 48 | ![bbenc-ttl2.png](bbenc-ttl2.png) 49 | 5. 集齐压制批处理,与其对应(大批量/单文件)的主控批处理后,双击主控批处理运行即可启动压制任务 50 | ![bbenc-ttl3.png](bbenc-ttl3.png) 51 | ![bbenc-ttl4.png](bbenc-ttl4.png) 52 |

大批量模式下的批处理运行截图(早期测试)


53 | 6. 最后, 运行封装用的.ps1脚本,按照提示导入构建一集视频所需的全部流/轨道,选择导出的封装格式即完成 54 | 55 | ## ★下载链接 56 | 皆同步更新, QQ群里有很高几率能得到问题答复
57 | 1: Github直链, 58 | 2: 谷歌盘, 59 | 3: 百度云, 提取码 hevc, 60 | 4: QQ群存档: 691892901
61 | 62 | 附录α: QAAC音频压缩教程Github
63 | 64 | 附录β: ffprobe视频探针教程Github
65 | 66 | 附录γ: x264,libx264,x265,libx265,libkvazzar,OBS 急用版压制教程Github
67 | 68 | δ: 下载ffmpeg, ffprobe: 官方Ottverse
69 | 70 | ε: 下载 x264/5: LigH (x265)、 71 | LigH (x264)、 72 | jpsdr (x264 tMod)、 73 | jpsdr (x265 auto-aq Mod)、 74 | Rigaya (x265)、 75 | Patman (x264 x265)、 76 | DJATOM (x265)、 77 | MeteorRain/7086 (x265) 78 |


79 | 80 | ## ☆打赏信息 81 | 82 | "免费即最贵" 的魔咒荼毒大地,让生灵涂炭;扫描下方二维码,用大数战胜罪恶与恐惧;保卫宇宙,守护文明,创造未来,意辉孟洺,赢得世界,锚定天理,金石萃编,葱烧鲤鱼 83 |

支持一下

84 | 85 | ----- 86 | 87 | ## ☆开发笔记 88 | 89 | **IDE** 90 | - 使用了notepad(记事本),VSCode,PowerShell ISE完成编写。 91 | - 由于内容较多,所以建议用VSCode,Sublime text等自带颜色标记能力的IDE来方便修改和开发(如移植到其他编程语言) 92 | - 建议使用PowerShell ISE来编辑和调试 93 | 94 | **PowerShell要求utf8+BOM文本编码** 95 | - 兼容PowerShell,PowerShell ISE,CMD批处理,以及UTF-8文件名4大天王之下只剩utf8+BOM这一种方案 96 | - PowerShell,PowerShell ISE完全不支持utf-8NoBOM 97 | - CMD不支持Unicode(UTF-16LE?) 98 | - PowerShell 5.1默认的utf-8编码选项是UTF-8BOM,且不原生支持导出UTF-8NoBOM; 99 | - 然而PowerShell 7要求用户用代码来运行.ps1脚本太过分,所以后期的开发完全回到PowerShell 5.1并实现了PowerShell 7中本来更容易实现的功能 100 | 101 | **PowerShell通过循环生成多行字符串** 102 | - 定义序列和字符串变量: $StrArray=@(); $MtlnString=\"\" 103 | - 循环中往序列累计字符串值,值尾加\`n: $StrArray+=\"some text \`n\" 104 | - 循环跑完后直接赋值到字符串变量: [string]$MtlnString=$StrArray 105 | - \`n实现了换行,但从第二行开始会于开头生成多余的空格,用-replace去掉: $MtlnString=MtlnString -replace " some", "some" 106 | 107 | **PowerShell生成自定义标题的表格** 108 | 109 | $新表格 = New-Object System.Data.DataTable 110 | $标题A= [System.Data.DataColumn]::new("自定义标题A") 111 | $标题B= [System.Data.DataColumn]::new("自定义标题B") 112 | $标题C= [System.Data.DataColumn]::new("自定义标题C") 113 | $新表格.Columns.Add($标题A); $新表格.Columns.Add($标题B); $新表格.Columns.Add($标题C) 114 | [void]$新表格.Rows.Add("行1内容A","行1内容B",$行1变量A); [void]$新表格.Rows.Add("行2内容A","行2内容B",$行2变量A) 115 | [void]$新表格.Rows.Add("行3内容A","行3内容B",$行3变量A); [void]$新表格.Rows.Add("行4内容A","行4内容B",$行4变量A) 116 | ($新表格 | Out-String).Trim() #1. Trim去掉空行, 2. pipe到Out-String以强制"$新表格"在其下方的Read-Host启动前发出 117 | 118 | **PowerShell在列表Array中添加坍缩和不坍缩的变量** 119 | 120 | $列表X=@(("$不坍缩变量A$不坍缩变量B"+'$坍缩变量A'), ("$不坍缩变量C$不坍缩变量D"+'$坍缩变量B')) 121 | #其中双引号下的变量不会坍缩,直接得到变量值形成固定的字符串 122 | #二级括号用于防止PowerShell混淆 `+` 和 `, `, 否则上面整个命令只会生成一个阵列项 123 | 124 | **PowerShell将列表Array中含坍缩变量的项扩张(expand)并打印出来** 125 | 126 | for ($_=0; $_ -lt $列表x.Length; $_++) { 127 | $ExecutionContext.InvokeCommand.ExpandString(($列表x | Out-String)) 128 | } #二级括号用于调整执行顺序, 即"将未扩张/Expand变量"先打印出来,然后再输入到变量扩张命令里 129 | 130 | **PowerShell检测文件名是否符合Windows命名规则的函数** 131 | 132 | Function namecheck([string]$inName) { 133 | $badChars = '[{0}]' -f [regex]::Escape(([IO.Path]::GetInvalidFileNameChars() -join '')) 134 | ForEach ($_ in $badChars) {if ($_ -match $inName) {return $false}} 135 | return $true 136 | } 137 | 138 | ----- 139 | 140 | ## ★更新信息 141 | **v1.2940** 142 | 146 | 147 | **v1.2910** 148 | 183 | 184 | **v1.2380** 185 | 228 | 229 | **v1.1840** 230 | 242 | 243 | **v1.1836** 244 | 256 | 257 | **v1.1789** 258 | 263 | 264 | **v1.1787** 265 | 272 | 273 | **v1.1769** 274 | 288 | 289 | **v1.1701** 290 | 299 | 300 | **v1.1640** 301 | 310 | 311 | **v1.1632** 312 | 323 | 324 | **正式 v1.1621** 325 | 331 | 332 | **半正式 v0.1619** 333 | 336 | 337 | 338 | **半正式 v0.1618** 339 | 343 | 344 | **内测 v0.1617** 345 | 348 | 349 | **内测 v0.1610** 350 | 365 | 366 | **内测 v0.1570** 367 | 384 | 385 | **内测 v0.1151** 386 | 391 | 392 | **内测 v0.1120** 393 | 399 | 400 | **内测 v0.390** 401 | 411 | 412 | **内测 v0.330** 413 | 425 | 426 | **内测 v0.190** 427 | 433 | 434 | **内测 v0.180** 435 | 444 | 445 | **更早** 446 | 452 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | --> README - 简中.md
2 | --> README - English.md 3 | -------------------------------------------------------------------------------- /S1-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iAvoe/Batch-batch-encoder/0c1b457b3c860e9e1243dcadaf8a1ad80ab3de64/S1-4.png -------------------------------------------------------------------------------- /S5n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iAvoe/Batch-batch-encoder/0c1b457b3c860e9e1243dcadaf8a1ad80ab3de64/S5n.png -------------------------------------------------------------------------------- /bbenc-source/1.「Environment setup 代码运行环境设置」.ps1: -------------------------------------------------------------------------------- 1 | cls #升级到管理员权限 2 | if (-Not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { 3 | if ([int](Get-CimInstance -Class Win32_OperatingSystem | Select-Object -ExpandProperty BuildNumber) -ge 6000) { 4 | Start-Process PowerShell -Verb RunAs -ArgumentList "-NoProfile -ExecutionPolicy Bypass -Command `"cd '$pwd'; & '$PSCommandPath';`""; 5 | Exit; 6 | } 7 | } 8 | Function testwritemodify ($inputPath) {$report="" 9 | $CurrentUserRights = ((Get-Acl $inputPath).Access | Select IdentityReference,AccessControlType,FileSystemRights | ?{$_.IdentityReference -match $env:USERNAME} | Format-List | Out-String).Trim() 10 | if ($CurrentUserRights.Contains("Modify")) {$report+="`r`n√ 用户 $env:USERNAME 拥有对 $inputPath 的一般读写权限"} else {$report+="`r`n× 用户 $env:USERNAME 没有对 $inputPath 的一般读写权限"} 11 | if ($CurrentUserRights.Contains("FullControl")) {$report+="`r`n√ 用户 $env:USERNAME 拥有对 $inputPath 的完全控制权限"} else {$report+="`r`n× 用户 $env:USERNAME 没有对 $inputPath 的完全控制权限"} 12 | return $report 13 | } 14 | 15 | Function loweruaclvl { 16 | Try{Set-ItemProperty -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System" -Name "ConsentPromptBehaviorAdmin" -Type DWord -Value 0 17 | Set-ItemProperty -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System" -Name "ConsentPromptBehaviorUser" -Type DWord -Value 0 18 | Set-ItemProperty -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System" -Name "PromptOnSecureDesktop" -Type DWord -Value 0 19 | Set-ItemProperty -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System" -Name "EnableLUA" -Type DWord -Value 0 20 | } Catch {return 1} 21 | return 0 22 | } 23 | 24 | Function raiseuaclvl { 25 | Try{Set-ItemProperty -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System" -Name "ConsentPromptBehaviorAdmin" -Type DWord -Value 5 26 | Set-ItemProperty -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System" -Name "ConsentPromptBehaviorUser" -Type DWord -Value 3 27 | Set-ItemProperty -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System" -Name "PromptOnSecureDesktop" -Type DWord -Value 1 28 | Set-ItemProperty -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System" -Name "EnableLUA" -Type DWord -Value 1 29 | } Catch {return 0} 30 | return 1 31 | } 32 | #检查用户PowerShell版本 33 | if ($PSVersionTable.PSVersion -lt 5.1) {Write-Warning "× PowerShell version is below 5.1, this script may not work - PowerShell版本低于5.1, 可能无法运行`r`n"} 34 | else {Write-Output "√ PowerShell version is 5.1 or higher - PowerShell版本为5.1或更高`r`n"} 35 | pause 36 | 37 | #检查用户权限是否正常 38 | Write-Output "`r`n检查当前用户于C盘根目录中的文件系统权限(仅用于排查故障)...`r`nInspecting current user's file system permission on C:\ (Debug only)..." 39 | $RootDirPerm=(testwritemodify -inputPath "C:\") 40 | Write-Output "检查当前用户于%USERPROFILE%中的文件系统权限(要求必须正常)...`r`nInspecting current user's file system permission on %USERPROFILE% (Has to be normal for this script)..." 41 | $profilePerm=testwritemodify -inputPath $env:USERPROFILE 42 | if ($profilePerm -notmatch "√") {Write-Warning "`r`n? 低权限: 用户没有C盘根目录的读写权限, 但不影响本脚本"} 43 | else {Write-Output "`r`n----------于C盘根目录的权限正常-----------"} 44 | $RootDirPerm 45 | if ($profilePerm -notmatch "√") {Write-Warning "`r`n× 系统损坏: 用户没有%USERPROFILE%的完全控制权限"} 46 | else {Write-Output "`r`n-----于%USERPROFILE%文件夹的权限正常------"} 47 | $profilePerm 48 | pause 49 | 50 | #检查并更改UAC 51 | Do {Switch (Read-Host "`r`n「User Access Control」由于每次运行PowerShell脚本都会弹出用户账户控制警告,选择: `r`n「User Access Control」Each time running PSscripts would panic UAC, select: `r`n[A: 关闭/Disable UAC | B: 不更改/Don't make changes | C: 恢复(公用电脑)/Restore UAC (public computers)]") { 52 | 53 | a {$UACops=1; Write-Output "`r`nLowering UAC level... `r`n正在降低用户账户控制通知级别..."; $uacc=loweruaclvl} #UAC_ON=1, UAC_OFF=0 54 | c {$UACops=3; Write-Output "`r`nRestoring UAC level...`r`n正在恢复用户账户控制通知级别..."; $uacc=raiseuaclvl} #UAC_ON=1, UAC_OFF=0 55 | b {$UACops=2; Write-Output "`r`nSkipped`r`n已跳过!"} 56 | default {$UACops=0; Write-Output " × Bad input, try again`r`n × 输入错误, 重试"} 57 | } 58 | } While ($UACops -eq 0) 59 | 60 | $UACreg=(Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System | Select ConsentPromptBehaviorAdmin,ConsentPromptBehaviorUser,PromptOnSecureDesktop,EnableLUA) 61 | if ($UACreg.EnableLUA -eq 1) {Write-Output "`r`nUAC is currently ON. `r`n用户账户控制通知目前已启用."} 62 | elseif ($UACreg.EnableLUA -eq 0) {Write-Output "`r`nUAC is currently OFF.`r`n用户账户控制通知目前已关闭."} 63 | 64 | if ($uacc -eq $UACreg.EnableLUA) {#确认用户账户控制返回结果$uacc与注册表符合. Cross referencing registry for UAC on/off status, to make sure $uacc works 65 | if (($UACops -eq 1) -and ($uacc -eq 1)) {Write-Warning "`r`n × Failed to lower UAC level. Please type UAC in Start menu, and lower the warning manually...`r`n关闭用户账户控制通知失败. 请在开始菜单输入UAC,然后手动降低警告阈限."} 66 | elseif (($UACops -eq 3) -and ($uacc -eq 0)) {Write-Warning "`r`n × Restore UAC level failed. Please type UAC in Start menu, and raise the warning manually...`r`n恢复用户账户控制通知失败. 请在开始菜单输入UAC,然后手动提高警告阈限."} 67 | } else {Write-Warning "`r`nNo operations were made, result check is skipped. `r`n未进行操作, 已跳过检测操作结果返回步骤."} 68 | pause 69 | 70 | #剩余的基本检查 71 | "Workstation name / 计算机名: "+$env:computername+", "+(Get-WmiObject -class Win32_OperatingSystem).Caption 72 | Write-Output "`r`n-------------Motherboard主板--------------" 73 | $MB = Get-WmiObject Win32_Baseboard | Select Status,Product,Manufacturer,Model,SerialNumber,Version 74 | "Name 名称: "+$MB.Product 75 | "Brand厂商: "+$MB.Manufacturer 76 | "Stat 状况: "+$MB.Status 77 | "Model型号: "+$MB.Model 78 | "S/N序列号: "+$MB.SerialNumber 79 | "Rev. 版本: "+$MB.Version 80 | Write-Output "`r`n-------------------BIOS-------------------" 81 | $BiosCond = Get-CimInstance Win32_BIOS | Select Status,Name,BIOSVersion,SMBIOSBIOSVersion,ReleaseDate 82 | "Name 名称: "+$BiosCond.Name 83 | "Stat 状况: "+$BiosCond.Status 84 | "Ver. 版本: "+$BiosCond.BIOSVersion 85 | "Reles.date 发布于: "+$BiosCond.ReleaseDate 86 | "Rng.ver. 现用版本: "+$BiosCond.SMBIOSBIOSVersion 87 | Write-Output "`r`n-------------Processor处理器--------------" 88 | $AllProcs = Get-CimInstance Win32_Processor | Select Availability,CurrentClockSpeed,MaxClockSpeed,Name,DeviceID,NumberOfCores,ThreadCount,LoadPercentage,VoltageCaps,VirtualizationFirmwareEnabled,L2CacheSize,L3CacheSize,SocketDesignation 89 | #Lopped for multi-node systems 为多路处理器系统准备的循环 90 | ForEach ($_ in $AllProcs) { 91 | #Distingulishi devices' status 区分设备运行与供电状态 92 | Switch ($_.Availability) { 93 | 2{[string]$prState = "Unknown 未知"} 94 | 3{[string]$prState = "Running/Full-Power/Normal 运行中/正常"} 95 | 4{[string]$prState = "Warning - 报警中"} 96 | 5{[string]$prState = "Under Test 处于测试状态"} 97 | 18{[string]$prState = "Paused - 暂停中"} 98 | 11{[string]$prState = "Not Installed - 未安装"} 99 | 12{[string]$prState = "Install Error - 安装错误"} 100 | 16{[string]$prState = "Powercycle - 重新启动中"} 101 | 17{[string]$prState = "Powersave: Warning - 省电模式: 警告"} 102 | 13{[string]$prState = "Powersave: Unknown - 省电模式: 未知"} 103 | 14{[string]$prState = "Powersave: LowPower - 省电模式: 节能"} 104 | 15{[string]$prState = "Powersave: Standby - 省电模式: 待机"} 105 | 20{[string]$prState = "Not Configured - 处理器缺少状态参数"} 106 | 21{[string]$prState = "Quiesced - 反馈被阻止"} 107 | Default{$prState = $_.Availability} 108 | } 109 | "Name 型号: "+$_.Name 110 | "Socket 插槽: "+$_.SocketDesignation 111 | "Status 状态: "+$prState 112 | "Cur.freq.当前频率: "+$_.CurrentClockSpeed+"Mhz" 113 | "Max.freq.最大频率: "+$_.MaxClockSpeed+"Mhz" 114 | "L2cache size 二缓: "+$_.L2CacheSize / 1024+"MB" 115 | "L3cache size 三缓: "+$_.L3CacheSize / 1024+"MB" 116 | "Voltage cap 最大电压: "+$_.VoltageCaps / 1000+"V" 117 | "Loads percent当前负载: "+$_.LoadPercentage+"%" 118 | "Virtualize on虚拟化开: "+$_.VirtualizationFirmwareEnabled 119 | } 120 | Write-Output "`r`n----------------Memory内存-----------------" 121 | 122 | $AllMemos = Get-WmiObject -Class Win32_PhysicalMemory | Select Manufacturer,ConfiguredVoltage,MaxVoltage,PartNumber,Speed,Tag,Capacity 123 | ForEach ($_ in $AllMemos) { 124 | 125 | #标题和居中计算 126 | [string]$heading = $_.Tag 127 | [int]$eCenter = $tCenter - $heading.Length/2 128 | [int]$gCenter = $eCenter/4 129 | 130 | #Calcualte memory size - 计算内存容量 131 | [int]$MemSize = $_.Capacity / 1GB 132 | Write-Output "`r`n------$heading------" 133 | "Size 容量: "+$MemSize+"GB" 134 | "Make 厂商: "+$_.Manufacturer 135 | "Model 型号: "+$_.PartNumber 136 | "Freq. 频率: "+$_.Speed+"MT/s(Mbps)" 137 | "Max. voltage 最大电压: "+$_.MaxVoltage / 1000+"V" 138 | "Defined volt.设定电压: "+$_.ConfiguredVoltage / 1000+"V" 139 | } 140 | Write-Output "`r`n----Debugging enc-options检查编码设定中----" 141 | $pme=$pool="" 142 | [int]$cores=(wmic cpu get NumberOfCores)[2] 143 | if ($cores -gt 21) {$pme="--pme"; Write-Output "√ Detecting processor's core count reaching 22, added x265 option: --pme`r`n√ 检测到处理器核心数达22, 已添加x265参数: --pme`r`n"} 144 | else {Write-Output "√ Detecting processor core counts are less than 22, evicted x265 option: --pme`r`n√ 检测到处理器核心数小于22, 已去除x265参数: --pme`r`n"} 145 | 146 | $procNodes=0 147 | $AllProcs=Get-CimInstance Win32_Processor | Select Availability 148 | ForEach ($_ in $AllProcs) {if ($_.Availability -eq 3) {$procNodes+=1}} 149 | if ($procNodes -eq 1) {Write-Output "√ Detected $procNodes installed processor, evited x265 option: --pools`r`n√ 检测到安装了 $procNodes 颗处理器, 已去除x265参数: --pools"} 150 | elseif ($procNodes -eq 2) {$pools="--pools +,-"} 151 | elseif ($procNodes -eq 4) {$pools="--pools +,-,-,-"} 152 | elseif ($procNodes -eq 6) {$pools="--pools +,-,-,-,-,-"} 153 | elseif ($procNodes -eq 8) {$pools="--pools +,-,-,-,-,-,-,-"} 154 | elseif ($procNodes -gt 8) {Write-Warning "? Detecting an unusal amount of installed processor nodes ($procNodes), please add option --pools manually`r`n? 检测到异常: 安装了超过8颗处理器($procNodes), 需手动填写--pools"} #Cannot use else, otherwise -eq 1 gets accounted for "unusual amount of comp nodes" 155 | if ($procNodes -gt 1) {Write-Output "√ Detected $procNodes installed processors, added x265 option: $pools`r`n√ 检测到安装了 $procNodes 颗处理器, 已添加x265参数: $pools"} 156 | 157 | Read-Host "? 如果参数--pme,--pools的代码没有报错,说明PowerShell状态正常,没有问题,可以运行步骤2`r`n? If --pme, --pools generating codeline didn't return with error, then PowerShell is running normally and ready for Step 2" -------------------------------------------------------------------------------- /bbenc-source/1.「Installation」Configure PSScript permission.txt: -------------------------------------------------------------------------------- 1 | 「Installation」 2 | - Start a PowerShell instance, execute Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope CurrentUser to remove policy restriction on running scripts. 3 | - Check whether the restriction is lifted with Get-ExecutionPolicy 4 | - Corporate/public workstations requires administrator priviledge to start PowerShell 5 | - If you don't own this PC, remember to set the restriction back-on with Set-ExecutionPolicy -ExecutionPolicy Restricted -Scope CurrentUser 6 | 7 | 8 | 「IDE」 9 | - This project was coded on Notepad, VSCode & PowerShell ISE. It's recommended to develop (e.g., port to another coding language) with integrated development environment (IDE) with colorcoding & comfy interface due to the sheer amount of content 10 | 11 | 「utf8+BOM text codec is required for PowerShell」 12 | - Under the 4 horsemen of PowerShell, PowerShell ISE, CMD/batch & UTF-8 filenames. Only utf8+BOM text codec could work it out. However the generated CMD batches are coded with UTF-8NoBOM 13 | - PowerShell,PowerShell ISE shraightup fail to decode UTF-8NoBOM 14 | - CMD doesn't support Unicode(UTF-16LE?) 15 | - PowerShell 5.1 does not natively support UTF-8NoBOM 16 | - however PowerShell 7 mandate users to run scripts with purely commandline, which is not acceptable, and caused the later shift to realize some of PS7's features in PowserShell 5.1 17 | 18 | 「Windows11 Language-pack requirement」 19 | - A Windows11 batch compatibility problem that has not yet been reproduced under Windows10 20 | - If path/filename contains any language that is not installed (e.g., Arabic, Japanese, Korean), batch will produce garbled text and fails miserably 21 | - To install a language pack, go to Settings-->Time & Language-->[Left pane]Languages-->Add a language 22 | - You don't need to set the newly installed language as the default displaying language 23 | - If you don't usually work on files with that foreign language, simply change the filename will do -------------------------------------------------------------------------------- /bbenc-source/1.「安装」设置PS脚本运行权限.txt: -------------------------------------------------------------------------------- 1 | 「安装」 2 | - 打开 PowerShell, 运行Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope CurrentUser以解除运行PS脚本的限制. 如果不知道是否已经开启则运行Get-ExecutionPolicy来检查 3 | - 添加到域的公司或公用电脑要以管理员权限运行 PowerShell 4 | - 如果不是自己的电脑,记得用完脚本后运行Set-ExecutionPolicy -ExecutionPolicy Restricted -Scope CurrentUser以改回原本的限制 5 | 6 | 「IDE」 7 | - 使用了notepad(记事本),VSCode,PowerShell ISE完成编写。 8 | - 由于内容较多,所以建议用VSCode,Sublime text等自带颜色标记能力的IDE来方便修改和开发(如移植到其他编程语言) 9 | - 建议使用PowerShell ISE来编辑和调试 10 | 11 | 「PowerShell要求使用utf8+BOM文本编码」 12 | - 兼容PowerShell,PowerShell ISE,CMD批处理,以及UTF-8文件名4大天王之下只剩utf8+BOM这一种方案 13 | - PowerShell,PowerShell ISE完全不支持utf-8NoBOM 14 | - CMD不支持Unicode(UTF-16LE?) 15 | - PowerShell 5.1默认的utf-8编码选项是UTF-8BOM,且不原生支持导出UTF-8NoBOM; 16 | - 然而PowerShell 7要求用户用代码来运行.ps1脚本太过分,所以后期的开发完全回到PowerShell 5.1并实现了PowerShell 7中本来更容易实现的功能 17 | 18 | 「Windows11 语言包需求」 19 | - Windows10未复现的Windows11兼容性问题 20 | - 如果文件与路径名中含有未安装的语言包(如阿拉伯语,日韩语)批处理将会乱码且无法正常运行 21 | - 安装语言包的方法是去设置-->时间和语言-->[左栏]语言-->添加语言. 不需要设为默认显示语言 22 | - 若不经常接触,也可以直接删改不兼容的文件名 -------------------------------------------------------------------------------- /bbenc-source/Step 2-5 in CHS/2.「生成单任务模式待调用容器」.ps1: -------------------------------------------------------------------------------- 1 | cls #开发人员的Github: https://github.com/iAvoe 2 | $mode="s" #单任务模式 3 | Function badinputwarning {Write-Warning "`r`n× 输入错误, 重试"} 4 | Function nosuchrouteerr {Write-Error "`r`n× 该线路不存在, 重试"} 5 | Function nonintinputerr {Write-Error "`r`n× 输入了非整数或空值"} 6 | Function tmpmuxreminder {return "x265线路下仅支持生成.hevc文件,若要封装为.mkv, 则受ffmpeg限制需要先封装为.mp4`r`n"} 7 | Function modeparamerror {Write-Error "`r`n× 崩溃: 变量`$mode损坏, 无法区分单任务和大批量模式"; pause; exit} 8 | Function modeimpextopserr{Write-Error "`r`n× 崩溃: `$mode, `$impOps或`$extOPS中的某个变量值无法识别"; pause; exit} 9 | Function skip {return "`r`n. 跳过"} 10 | Function namecheck([string]$inName) { 11 | $badChars = '[{0}]' -f [regex]::Escape(([IO.Path]::GetInvalidFileNameChars() -join '')) 12 | ForEach ($_ in $badChars) {if ($_ -match $inName) {return $false}} 13 | return $true 14 | } #检测文件名是否符合Windows命名规则,大批量版不需要 15 | 16 | Function whereisit($startPath='DESKTOP') { 17 | #启用System.Windows.Forms选择文件的GUI交互窗,通过SelectedPath将GUI交互窗锁定到桌面文件夹, 效果一般 18 | [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") 19 | Add-Type -AssemblyName System.Windows.Forms 20 | $startPath = New-Object System.Windows.Forms.OpenFileDialog -Property @{ InitialDirectory = [Environment]::GetFolderPath('DESKTOP') }#GUI交互窗锁定到桌面文件夹 21 | Do {$dInput = $startPath.ShowDialog()} While ($dInput -eq "Cancel") #1.打开选择文件的GUI交互窗, 通过重新打开选择窗来反取消用户的取消操作. 2. 窗口大, TopMost关 22 | return $startPath.FileName 23 | } 24 | 25 | Function whichlocation($startPath='DESKTOP') { 26 | #启用System.Windows.Forms选择文件夹的GUI交互窗 27 | Add-Type -AssemblyName System.Windows.Forms 28 | $startPath = New-Object System.Windows.Forms.FolderBrowserDialog -Property @{ Description="选择路径用的窗口. 拖拽边角可放大以便操作"; SelectedPath=[Environment]::GetFolderPath($startPath); RootFolder='MyComputer'; ShowNewFolderButton=$true } 29 | #打开选择文件的GUI交互窗, 用Do-While循环拦截误操作(取消/关闭选择窗) 30 | Do {$dInput = $startPath.ShowDialog((New-Object System.Windows.Forms.Form -Property @{TopMost=$true}))} While ($dInput -eq "Cancel") #2. 窗口小, TopMost开 31 | #由于选择根目录时路径变量含"\", 而文件夹时路径变量缺"\", 所以要自动判断并补上 32 | if (($startPath.SelectedPath.SubString($startPath.SelectedPath.Length-1) -eq "\") -eq $false) {$startPath.SelectedPath+="\"} 33 | return $startPath.SelectedPath 34 | } 35 | 36 | #「@MrNetTek」高DPI显示渲染模式的System.Windows.Forms 37 | Add-Type -TypeDefinition @' 38 | using System.Runtime.InteropServices; 39 | public class ProcessDPI { 40 | [DllImport("user32.dll", SetLastError=true)] 41 | public static extern bool SetProcessDPIAware(); 42 | } 43 | '@ 44 | $null = [ProcessDPI]::SetProcessDPIAware() 45 | 46 | Set-PSDebug -Strict 47 | Write-Output "ffmpeg -i [源] -an -f yuv4mpegpipe -strict unofficial - | x265.exe - --y4m --output`r`n" 48 | Write-Output "ffmpeg 缩放滤镜: -sws_flags <+full_chroma_int +full_chroma_inp +accurate_rnd>" 49 | Write-Output "ffmpeg .ass渲染: -filter_complex `"ass=`'F\:/字幕.ass`'`"" 50 | Write-Output "ffmpeg可变转恒定帧率: -vsync cfr`r`n" 51 | Write-Output "压制分场隔行视频 - x265: --tff/--bff; - x264: --interlaced`r`n" 52 | Write-Output "VSpipe [.vpy] --y4m - | x265.exe --y4m - --output" 53 | Write-Output "avs2yuv [.avs] -csp<串> -depth<整> - | x265.exe --input-res <串> --fps <整/小/分数> - --output" 54 | Write-Output "avs2pipemod [.avs] -y4mp | x265.exe --y4m - --output <<上游无`"-`">>`r`n" 55 | 56 | #「启动A-B」仅在大批量模式下用, 单文件模式下跳过 57 | 58 | #「启动C」定位导出主控文件用路径, 需要区分单任务和大批量模式 59 | Read-Host "将打开[导出待调用批处理]的路径选择窗. [Enter]继续" 60 | if ($mode -eq "s") {$bchExpPath = (whichlocation)+"enc_0S.bat"} 61 | elseif ($mode -eq "m") {$bchExpPath = (whichlocation)+'enc_$s.bat'} #大批量模式下, 使用单引号来防止变量$s在此处被激活 62 | else {modeparamerror} 63 | Write-Output "`r`n√ 选择的路径与导出文件名为 $bchExpPath" 64 | 65 | #「启动D」循环导入所有pipe上游,下游程序, 同时使用y4m pipe和ffprobe两者来实现冗余/fallback. 步骤2选择上游程序, 步骤3选择片源 66 | $fmpgPath=$vprsPath=$avsyPath=$avspPath=$svfiPath=$x265Path=$x264Path="" 67 | Do {Do { 68 | Switch (Read-Host "`r`n导入上游程序路径 [A: ffmpeg | B: vspipe | C: avs2yuv | D: avs2pipemod | E: SVFI], 重复选择会触发跳过") { 69 | a {if ($fmpgPath -eq "") {Write-Output "`r`nffmpeg------上游A线. 已打开[定位ffmpeg.exe]的选窗"; $fmpgPath=whereisit} else {skip}} 70 | b {if ($vprsPath -eq "") {Write-Output "`r`nvspipe------上游B线. 已打开[定位vspipe.exe]的选窗"; $vprsPath=whereisit} else {skip}} 71 | c {if ($avsyPath -eq "") {Write-Output "`r`navs2yuv-----上游C线. 已打开[定位avs2yuv.avs]的选窗"; $avsyPath=whereisit} else {skip}} 72 | d {if ($avspPath -eq "") {Write-Output "`r`navs2pipemod-上游D线. 已打开[定位avs2pipemod.exe]的选窗"; $avspPath=whereisit} else {skip}} 73 | e {if ($svfiPath -eq "") {Write-Output "`r`nsvfi--------上游E线. 已打开[定位one_line_shot_args.exe]的选窗";$svfiPath=whereisit} else {skip}} 74 | default {badinputwarning} 75 | } 76 | } While ($fmpgPath+$vprsPath+$avsyPath+$avspPath+$svfiPath -eq "") 77 | Do { 78 | Switch (Read-Host "`r`n导入下游程序路径 [A: x265/hevc | B: x264/avc], 重复选择会触发跳过") { 79 | a {if ($x265Path -eq "") {Write-Output "`r`nx265--------下游A线. 已打开[定位x265.exe]的选窗"; $x265Path=whereisit} else {skip}} 80 | b {if ($x264Path -eq "") {Write-Output "`r`nx264--------下游B线. 已打开[定位x264.exe]的选窗"; $x264Path=whereisit} else {skip}} 81 | default {badinputwarning} 82 | } 83 | } While ($x265Path+$x264Path -eq "") 84 | if ((Read-Host "`r`n√ [Enter]导入更多线路(推荐); 或[y][Enter]以进行下一步") -eq "y") {$impEND="y"} else {$impEND="n"} #用户选择是否完成导入操作并退出 85 | } While ($impEND -eq "n") 86 | #生成一张表来表示所有已知路线 87 | $updnTbl = New-Object System.Data.DataTable 88 | $availRts= [System.Data.DataColumn]::new("Routes") 89 | $upColumn= [System.Data.DataColumn]::new("\UNIX pipe upstream") 90 | $dnColumn= [System.Data.DataColumn]::new("\UNIX pipe downstream") 91 | $updnTbl.Columns.Add($availRts); $updnTbl.Columns.Add($upColumn); $updnTbl.Columns.Add($dnColumn) 92 | [void]$updnTbl.Rows.Add(" A:",$fmpgPath,$x265Path); [void]$updnTbl.Rows.Add(" B:",$vprsPath,$x264Path) 93 | [void]$updnTbl.Rows.Add(" C:",$avsyPath,""); [void]$updnTbl.Rows.Add(" D:",$avspPath,""); [void]$updnTbl.Rows.Add(" E:",$svfiPath,"") 94 | ($updnTbl | Out-String).Trim() #1. Trim去掉空行, 2. pipe到Out-String以强制$updnTbl在Read-Host启动前发出 95 | 96 | Read-Host "`r`n检查或确认所有导入的程序正确后按[Enter], 否则重运行此脚本" 97 | 98 | #「启动E」选择上下游线路, 通过impOPS, extOPS来判断注释掉剩余未选择的路线 99 | $impOPS=$extOPS="" 100 | Do {Switch (Read-Host "选择启用一条pipe上游线路 [A | B | C | D | E], 剩余线路会通过注释遮蔽掉") { 101 | a {if ($fmpgPath -ne "") {$impOPS="a"} else {nosuchrouteerr}} 102 | b {if ($vprsPath -ne "") {$impOPS="b"} else {nosuchrouteerr}} 103 | c {if ($avsyPath -ne "") {$impOPS="c"} else {nosuchrouteerr}} 104 | d {if ($avspPath -ne "") {$impOPS="d"} else {nosuchrouteerr}} 105 | e {if ($svfiPath -ne "") {$impOPS="e"} else {nosuchrouteerr}} 106 | default {badinputwarning} 107 | } 108 | if ($impOPS -ne "") {#未选择上游时, 通过if跳过本段代码回到选择上游的部分 109 | Switch (Read-Host "`r`n选择启用一条pipe下游线路 [A | B], 剩余线路会通过注释遮蔽掉") { 110 | a {if ($x265Path -ne "") {$extOPS="a"} else {nosuchrouteerr}} 111 | b {if ($x264Path -ne "") {$extOPS="b"} else {nosuchrouteerr}} 112 | default {badinputwarning} 113 | } 114 | } 115 | } While (($impOPS -eq "") -or ($extOPS -eq "")) 116 | 117 | #「启动F」调用impOPS, extOPS生成被选中线路的命令行 118 | $keyRoute=""; $sChar="AAA" #防止意外情况下的启动F, G出现变量空值错误, 所以提前给变量赋值 119 | Switch ($mode+$impOps+$extOPS) { 120 | saa {$keyRoute="$fmpgPath %ffmpegVarA% %ffmpegParA% - | $x265Path %x265ParA% %x265VarA%"} #ffmpeg+x265+single 121 | sab {$keyRoute="$fmpgPath %ffmpegVarA% %ffmpegParA% - | $x264Path %x264ParA% %x264VarA%"} #ffmpeg+x264+single 122 | sba {$keyRoute="$vprsPath %vspipeVarA% %vspipeParA% - | $x265Path %x265ParA% %x265VarA%"} #VSPipe+x265+single 123 | sbb {$keyRoute="$vprsPath %vspipeVarA% %vspipeParA% - | $x264Path %x264ParA% %x264VarA%"} #VSPipe+x264+single 124 | sca {$keyRoute="$avsyPath %avsyuvVarA% %avsyuvParA% - | $x265Path %x265ParA% %x265VarA%"} #AVSYUV+x265+single 125 | scb {$keyRoute="$avsyPath %avsyuvVarA% %avsyuvParA% - | $x264Path %x264ParA% %x264VarA%"} #AVSYUV+x264+single 126 | sda {$keyRoute="$avspPath %avsmodVarA% %avsmodParA% | $x265Path %x265ParA% %x265VarA%"} #AVSPmd+x265+single, 上游无"-" 127 | sdb {$keyRoute="$avspPath %avsmodVarA% %avsmodParA% | $x264Path %x264ParA% %x264VarA%"} #AVSPmd+x264+single, 上游无"-" 128 | sea {$keyRoute="$svfiPath %olsargVarA% %olsargParA% - | $x265Path %x265ParA% %x265VarA%"} #OLSARG+x265+single 129 | seb {$keyRoute="$svfiPath %olsargVarA% %olsargParA% - | $x264Path %x264ParA% %x264VarA%"} #OLSARG+x264+single 130 | maa {$keyRoute="$fmpgPath %ffmpegVarA% %ffmpegParA% - | $x265Path %x265ParA%"+' %x265Var$sChar%'} #ffmpeg+x265+multiple, 单引号防止$sChar被提前激活 131 | mab {$keyRoute="$fmpgPath %ffmpegVarA% %ffmpegParA% - | $x264Path %x264ParA%"+' %x264Var$sChar%'} #ffmpeg+x264+multiple 132 | mba {$keyRoute="$vprsPath %vspipeVarA% %vspipeParA% - | $x265Path %x265ParA%"+' %x265Var$sChar%'} #VSPipe+x265+multiple 133 | mbb {$keyRoute="$vprsPath %vspipeVarA% %vspipeParA% - | $x264Path %x264ParA%"+' %x264Var$sChar%'} #VSPipe+x264+multiple 134 | mca {$keyRoute="$avsyPath %avsyuvVarA% %avsyuvParA% - | $x265Path %x265ParA%"+' %x265Var$sChar%'} #AVSYUV+x265+multiple 135 | mcb {$keyRoute="$avsyPath %avsyuvVarA% %avsyuvParA% - | $x264Path %x264ParA%"+' %x264Var$sChar%'} #AVSYUV+x264+multiple 136 | mda {$keyRoute="$avspPath %avsmodVarA% %avsmodParA% | $x265Path %x265ParA%"+' %x265Var$sChar%'} #AVSPmd+x265+multiple, 上游无"-" 137 | mdb {$keyRoute="$avspPath %avsmodVarA% %avsmodParA% | $x264Path %x264ParA%"+' %x264Var$sChar%'} #AVSPmd+x264+multiple, 上游无"-" 138 | mea {$keyRoute="$svfiPath %olsargVarA% %olsargParA% - | $x265Path %x265ParA%"+' %x265Var$sChar%'} #OLSARG+x265+multiple 139 | meb {$keyRoute="$svfiPath %olsargVarA% %olsargParA% - | $x264Path %x264ParA%"+' %x264Var$sChar%'} #OLSARG+x264+multiple 140 | Default {modeimpextopserr} 141 | } 142 | #「启动G」将已知可用的上下游线路列举并进行排列组合, 以生成备选线路 143 | [array] $upPipeStr=@("$fmpgPath %ffmpegVarA% %ffmpegParA%", "$vprsPath %vspipeVarA% %vspipeParA%", "$avsyPath %avsyuvVarA% %avsyuvParA%", "$avspPath %avsmodVarA% %avsmodParA%","$svfiPath %olsargVarA% %olsargParA%") | Where-Object {$_.Length -gt 26} 144 | Switch ($mode) { #用字长过滤掉dnPipeStr中不存在的线路, 大批量版使用sChar变量所以原始字符串要比单文件版多一个字 145 | s {[array]$dnPipeStr=@("$x265Path %x265ParA% %x265VarA%", "$x264Path %x264ParA% %x264VarA%") | Where-Object {$_.Length -gt 22}} 146 | m {[array]$dnPipeStr=@(("$x265Path%x265ParA%"+' %x265Var$sChar%'), ("$x264Path %x264ParA%"+' %x264Var$sChar%')) | Where-Object {$_.Length -gt 23}} 147 | Default {modeparamerror} 148 | } 149 | [array]$altRoute=@() #注释符 + `$updnPipeStr值 = 备选线路. 生成所有的备选线路命令行 150 | for ($x=0; $x -lt ($upPipeStr.Length); $x++) {#上游/横向可能性的循环迭代 151 | for ($y=0; $y -lt ($dnPipeStr.Length); $y++) {#下游/纵向可能性的循环迭代 152 | if ($upPipeStr -notlike "avsmod") {$altRoute="REM "+$upPipeStr[$x]+" - | "+$dnPipeStr[$y]} #AVSPmd, 上游无"-" 153 | else {$altRoute="REM "+$upPipeStr[$x]+" | "+$dnPipeStr[$y]} #AVSPmd, 上游无"-" 154 | } 155 | } 156 | "√ 可用线路数量为: "+($altRoute.Count.ToString()) | Out-String #此时已得出主选线路`$keyRoute和备选线路`$altRoute 157 | 158 | if ($extOPS="a") {tmpmuxreminder} #选择x265下游时, 给出只能间接封装为.mkv的警告 159 | 160 | #「启动H.s」单任务封装模式下的文件输出功能 161 | #由于单纯激活$sChar变量后, Array只会生成换行失败的多行文本, 所以先pipe到Out-String变成多行字符串, 才能激活$sChar变量, 单文件版下无 162 | $utf8NoBOM=New-Object System.Text.UTF8Encoding $false #导出utf-8NoBOM文本编码hack 163 | Write-Output "`r`n... 正在生成enc_0S.bat`r`n" 164 | $enc_gen="REM 「标题」 165 | @echo. 166 | @echo -----------Starting encode 001----------- 167 | 168 | REM 「debug部分」正常使用时注释掉 169 | REM @echo %ffmpegParA% %ffmpegVarA% 170 | REM @echo %vspipeParA% %vspipeVarA% 171 | REM @echo %avsyuvParA% %avsyuvVarA% 172 | REM @echo %avsmodParA% %avsmodVarA% 173 | REM @echo %olsargParA% %olsargVarA% 174 | REM @echo %x265ParA% %x265VarA% 175 | REM @echo %x264ParA% %x264VarA% 176 | REM pause 177 | 178 | REM 「压制-选中线路」debug时注释掉 179 | REM Var被用于引用动态数据,如输入输出路径和根据源视频自动调整的部分参数值 180 | 181 | "+$ExecutionContext.InvokeCommand.ExpandString(($keyRoute | Out-String))+" 182 | 183 | REM 「压制-备选线路」下方命令去掉REM注释地复制-覆盖到上方命令以更换选中线路 184 | 185 | "+$ExecutionContext.InvokeCommand.ExpandString(($altRoute | Out-String))+" 186 | 187 | REM 「选择续y/暂n/止z」5秒后自动y, 除外字符被choice命令屏蔽, 暂停代表仍可继续. 188 | 189 | choice /C YNZ /T 5 /D Y /M `" Continue? (Sleep=5; Default: Y, Pause: N, Stop: Z)`" 190 | 191 | if %ERRORLEVEL%==3 cmd /k 192 | if %ERRORLEVEL%==2 pause 193 | if %ERRORLEVEL%==1 endlocal && exit /b" 194 | 195 | #Out-File -InputObject $enc_gen -FilePath $bchExpPath -Encoding utf8 196 | if ($mode -eq "m") {[IO.File]::WriteAllLines($ExecutionContext.InvokeCommand.ExpandString($bchExpPath), $enc_gen, $utf8NoBOM)}#大批量模式下激活$s变量 197 | elseif ($mode -eq "s") {[IO.File]::WriteAllLines($bchExpPath, $enc_gen, $utf8NoBOM)} 198 | else {modeparamerror} 199 | 200 | Write-Output "完成,只要线路中的程序不更新,步骤3生成的各种批处理(步骤4)就可以一直调用enc_0S.bat / enc_X.bat" 201 | pause -------------------------------------------------------------------------------- /bbenc-source/Step 2-5 in CHS/2.「生成大批量模式待调用容器」.ps1: -------------------------------------------------------------------------------- 1 | cls #开发人员的Github: https://github.com/iAvoe 2 | $mode="m" #大批量模式 3 | Function badinputwarning {Write-Warning "`r`n× 输入错误, 重试"} 4 | Function nosuchrouteerr {Write-Error "`r`n× 该线路不存在, 重试"} 5 | Function nonintinputerr {Write-Error "`r`n× 输入了非整数或空值"} 6 | Function tmpmuxreminder {return "x265线路下仅支持生成.hevc文件,若要封装为.mkv, 则受ffmpeg限制需要先封装为.mp4`r`n"} 7 | Function modeparamerror {Write-Error "`r`n× 崩溃: 变量`$mode损坏, 无法区分单任务和大批量模式"; pause; exit} 8 | Function modeimpextopserr{Write-Error "`r`n× 崩溃: `$mode, `$impOps或`$extOPS中的某个变量值无法识别"; pause; exit} 9 | Function skip {return "`r`n. 跳过"} 10 | Function namecheck([string]$inName) { 11 | $badChars = '[{0}]' -f [regex]::Escape(([IO.Path]::GetInvalidFileNameChars() -join '')) 12 | ForEach ($_ in $badChars) {if ($_ -match $inName) {return $false}} 13 | return $true 14 | } #检测文件名是否符合Windows命名规则,大批量版不需要 15 | 16 | Function whereisit($startPath='DESKTOP') { 17 | #启用System.Windows.Forms选择文件的GUI交互窗,通过SelectedPath将GUI交互窗锁定到桌面文件夹, 效果一般 18 | [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") 19 | Add-Type -AssemblyName System.Windows.Forms 20 | $startPath = New-Object System.Windows.Forms.OpenFileDialog -Property @{ InitialDirectory = [Environment]::GetFolderPath('DESKTOP') }#GUI交互窗锁定到桌面文件夹 21 | Do {$dInput = $startPath.ShowDialog()} While ($dInput -eq "Cancel") #1.打开选择文件的GUI交互窗, 通过重新打开选择窗来反取消用户的取消操作. 2. 窗口大, TopMost关 22 | return $startPath.FileName 23 | } 24 | 25 | Function whichlocation($startPath='DESKTOP') { 26 | #启用System.Windows.Forms选择文件夹的GUI交互窗 27 | Add-Type -AssemblyName System.Windows.Forms 28 | $startPath = New-Object System.Windows.Forms.FolderBrowserDialog -Property @{ Description="选择路径用的窗口. 拖拽边角可放大以便操作"; SelectedPath=[Environment]::GetFolderPath($startPath); RootFolder='MyComputer'; ShowNewFolderButton=$true } 29 | #打开选择文件的GUI交互窗, 用Do-While循环拦截误操作(取消/关闭选择窗) 30 | Do {$dInput = $startPath.ShowDialog((New-Object System.Windows.Forms.Form -Property @{TopMost=$true}))} While ($dInput -eq "Cancel") #2. 窗口小, TopMost开 31 | #由于选择根目录时路径变量含"\", 而文件夹时路径变量缺"\", 所以要自动判断并补上 32 | if (($startPath.SelectedPath.SubString($startPath.SelectedPath.Length-1) -eq "\") -eq $false) {$startPath.SelectedPath+="\"} 33 | return $startPath.SelectedPath 34 | } 35 | 36 | #「@MrNetTek」高DPI显示渲染模式的System.Windows.Forms 37 | Add-Type -TypeDefinition @' 38 | using System.Runtime.InteropServices; 39 | public class ProcessDPI { 40 | [DllImport("user32.dll", SetLastError=true)] 41 | public static extern bool SetProcessDPIAware(); 42 | } 43 | '@ 44 | $null = [ProcessDPI]::SetProcessDPIAware() 45 | 46 | Set-PSDebug -Strict 47 | Write-Output "ffmpeg -i [源] -an -f yuv4mpegpipe -strict unofficial - | x265.exe - --y4m --output`r`n" 48 | Write-Output "ffmpeg 缩放滤镜: -sws_flags <+full_chroma_int +full_chroma_inp +accurate_rnd>" 49 | Write-Output "ffmpeg .ass渲染: -filter_complex `"ass=`'F\:/字幕.ass`'`"" 50 | Write-Output "ffmpeg可变转恒定帧率: -vsync cfr`r`n" 51 | Write-Output "压制分场隔行视频 - x265: --tff/--bff; - x264: --interlaced`r`n" 52 | Write-Output "VSpipe [.vpy] --y4m - | x265.exe --y4m - --output" 53 | Write-Output "avs2yuv [.avs] -csp<串> -depth<整> - | x265.exe --input-res <串> --fps <整/小/分数> - --output" 54 | Write-Output "avs2pipemod [.avs] -y4mp | x265.exe --y4m - --output <<上游无`"-`">>`r`n" 55 | 56 | #「启动A」生成1~n个"enc_[序号].bat"单文件版不需要 57 | if ($mode -eq "m") { 58 | [array]$validChars='A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z' 59 | [int]$qty=0 #从0而非1开始数 60 | Do {[int]$qty = (Read-Host -Prompt "指定[生成压制批处理]的整数数量 (1~15625)") 61 | if ($qty -eq 0) {nonintinputerr} elseif ($qty -gt 15625) {Write-Error "× 编码次数超过15625"; pause; exit} 62 | } While ($qty -eq 0) 63 | #「启动B」选择是否在导出文件序号上补零, 由于int变量$qty得不到字长Length, 所以先转string再取值 64 | if ($qty -gt 9) {#个位数下关闭补零 65 | Do {[string]$leadCHK=""; [int]$ldZeros=0 66 | Switch (Read-Host "选择之前[y启用了 | n关闭了]导出压制文件名的[序号补0]. 如导出十位数文件时写作01, 02...") { 67 | y {$leadCHK="y"; Write-Output "√ 启用补零`r`n"; $ldZeros=$qty.ToString().Length} 68 | n {$leadCHK="n"; Write-Output "× 关闭补零`r`n"} 69 | default {badinputwarning} 70 | } 71 | } While ($leadCHK -eq "") 72 | [string]$zroStr="0"*$ldZeros #得到.ToString('000')所需的'000'部分, 如果关闭补零则$zroStr为0, 补零计算仍然存在但没有效果 73 | } else {[string]$zroStr="0"} 74 | } 75 | #「启动C」定位导出主控文件用路径, 需要区分单任务和大批量模式 76 | Read-Host "`r`n将打开[导出待调用批处理]的路径选择窗. [Enter]继续" 77 | if ($mode -eq "s") {$bchExpPath = (whichlocation)+"enc_0S.bat"} 78 | elseif ($mode -eq "m") {$bchExpPath = (whichlocation)+'enc_$s.bat'} #大批量模式下, 使用单引号来防止变量$s在此处被激活 79 | else {modeparamerror} 80 | Write-Output "`r`n√ 选择的路径与导出文件名为 $bchExpPath" 81 | 82 | #「启动D」循环导入所有pipe上游,下游程序, 同时使用y4m pipe和ffprobe两者来实现冗余/fallback. 步骤2选择上游程序, 步骤3选择片源 83 | $fmpgPath=$vprsPath=$avsyPath=$avspPath=$svfiPath=$x265Path=$x264Path="" 84 | Do {Do { 85 | Switch (Read-Host "`r`n导入上游程序路径 [A: ffmpeg | B: vspipe | C: avs2yuv | D: avs2pipemod | E: SVFI], 重复选择会触发跳过") { 86 | a {if ($fmpgPath -eq "") {Write-Output "`r`nffmpeg------上游A线. 已打开[定位ffmpeg.exe]的选窗"; $fmpgPath=whereisit} else {skip}} 87 | b {if ($vprsPath -eq "") {Write-Output "`r`nvspipe------上游B线. 已打开[定位vspipe.exe]的选窗"; $vprsPath=whereisit} else {skip}} 88 | c {if ($avsyPath -eq "") {Write-Output "`r`navs2yuv-----上游C线. 已打开[定位avs2yuv.avs]的选窗"; $avsyPath=whereisit} else {skip}} 89 | d {if ($avspPath -eq "") {Write-Output "`r`navs2pipemod-上游D线. 已打开[定位avs2pipemod.exe]的选窗"; $avspPath=whereisit} else {skip}} 90 | e {if ($svfiPath -eq "") {Write-Output "`r`nsvfi--------上游E线. 已打开[定位one_line_shot_args.exe]的选窗";$svfiPath=whereisit} else {skip}} 91 | default {badinputwarning} 92 | } 93 | } While ($fmpgPath+$vprsPath+$avsyPath+$avspPath+$svfiPath -eq "") 94 | Do { 95 | Switch (Read-Host "`r`n导入下游程序路径 [A: x265/hevc | B: x264/avc], 重复选择会触发跳过") { 96 | a {if ($x265Path -eq "") {Write-Output "`r`nx265--------下游A线. 已打开[定位x265.exe]的选窗"; $x265Path=whereisit} else {skip}} 97 | b {if ($x264Path -eq "") {Write-Output "`r`nx264--------下游B线. 已打开[定位x264.exe]的选窗"; $x264Path=whereisit} else {skip}} 98 | default {badinputwarning} 99 | } 100 | } While ($x265Path+$x264Path -eq "") 101 | if ((Read-Host "`r`n√ [Enter]导入更多线路(推荐); 或[y][Enter]以进行下一步") -eq "y") {$impEND="y"} else {$impEND="n"} #用户选择是否完成导入操作并退出 102 | } While ($impEND -eq "n") 103 | #生成一张表来表示所有已知路线 104 | $updnTbl = New-Object System.Data.DataTable 105 | $availRts= [System.Data.DataColumn]::new("Routes") 106 | $upColumn= [System.Data.DataColumn]::new("\UNIX Pipe Upstream") 107 | $dnColumn= [System.Data.DataColumn]::new("\UNIX Pipe Dnstream") 108 | $updnTbl.Columns.Add($availRts); $updnTbl.Columns.Add($upColumn); $updnTbl.Columns.Add($dnColumn) 109 | [void]$updnTbl.Rows.Add(" A:",$fmpgPath,$x265Path); [void]$updnTbl.Rows.Add(" B:",$vprsPath,$x264Path) 110 | [void]$updnTbl.Rows.Add(" C:",$avsyPath,""); [void]$updnTbl.Rows.Add(" D:",$avspPath,""); [void]$updnTbl.Rows.Add(" E:",$svfiPath,"") 111 | ($updnTbl | Out-String).Trim() #1. Trim去掉空行, 2. pipe到Out-String以强制$updnTbl在Read-Host启动前发出 112 | 113 | Read-Host "`r`n检查或确认所有导入的程序正确后按[Enter], 否则重运行此脚本" 114 | 115 | #「启动E」选择上下游线路, 通过impOPS, extOPS来判断注释掉剩余未选择的路线 116 | $impOPS=$extOPS="" 117 | Do {Switch (Read-Host "选择启用一条pipe上游线路 [A | B | C | D | E], 剩余线路会通过注释遮蔽掉") { 118 | a {if ($fmpgPath -ne "") {$impOPS="a"} else {nosuchrouteerr}} 119 | b {if ($vprsPath -ne "") {$impOPS="b"} else {nosuchrouteerr}} 120 | c {if ($avsyPath -ne "") {$impOPS="c"} else {nosuchrouteerr}} 121 | d {if ($avspPath -ne "") {$impOPS="d"} else {nosuchrouteerr}} 122 | e {if ($svfiPath -ne "") {$impOPS="e"} else {nosuchrouteerr}} 123 | default {badinputwarning} 124 | } 125 | if ($impOPS -ne "") {#未选择上游时, 通过if跳过本段代码回到选择上游的部分 126 | Switch (Read-Host "`r`n选择启用一条pipe下游线路 [A | B], 剩余线路会通过注释遮蔽掉") { 127 | a {if ($x265Path -ne "") {$extOPS="a"} else {nosuchrouteerr}} 128 | b {if ($x264Path -ne "") {$extOPS="b"} else {nosuchrouteerr}} 129 | default {badinputwarning} 130 | } 131 | } 132 | } While (($impOPS -eq "") -or ($extOPS -eq "")) 133 | 134 | #「启动F」调用impOPS, extOPS生成被选中线路的命令行 135 | $keyRoute=""; $sChar="AAA" #防止意外情况下的启动F, G出现变量空值错误, 所以提前给变量赋值 136 | Switch ($mode+$impOps+$extOPS) { 137 | saa {$keyRoute="$fmpgPath %ffmpegVarA% %ffmpegParA% - | $x265Path %x265ParA% %x265VarA%"} #ffmpeg+x265+single 138 | sab {$keyRoute="$fmpgPath %ffmpegVarA% %ffmpegParA% - | $x264Path %x264ParA% %x264VarA%"} #ffmpeg+x264+single 139 | sba {$keyRoute="$vprsPath %vspipeVarA% %vspipeParA% - | $x265Path %x265ParA% %x265VarA%"} #VSPipe+x265+single 140 | sbb {$keyRoute="$vprsPath %vspipeVarA% %vspipeParA% - | $x264Path %x264ParA% %x264VarA%"} #VSPipe+x264+single 141 | sca {$keyRoute="$avsyPath %avsyuvVarA% %avsyuvParA% - | $x265Path %x265ParA% %x265VarA%"} #AVSYUV+x265+single 142 | scb {$keyRoute="$avsyPath %avsyuvVarA% %avsyuvParA% - | $x264Path %x264ParA% %x264VarA%"} #AVSYUV+x264+single 143 | sda {$keyRoute="$avspPath %avsmodVarA% %avsmodParA% | $x265Path %x265ParA% %x265VarA%"} #AVSPmd+x265+single, 上游无"-" 144 | sdb {$keyRoute="$avspPath %avsmodVarA% %avsmodParA% | $x264Path %x264ParA% %x264VarA%"} #AVSPmd+x264+single, 上游无"-" 145 | sea {$keyRoute="$svfiPath %olsargVarA% %olsargParA% - | $x265Path %x265ParA% %x265VarA%"} #OLSARG+x265+single 146 | seb {$keyRoute="$svfiPath %olsargVarA% %olsargParA% - | $x264Path %x264ParA% %x264VarA%"} #OLSARG+x264+single 147 | maa {$keyRoute="$fmpgPath %ffmpegVarA% %ffmpegParA% - | $x265Path %x265ParA%"+' %x265Var$sChar%'} #ffmpeg+x265+multiple, 单引号防止$sChar被提前激活 148 | mab {$keyRoute="$fmpgPath %ffmpegVarA% %ffmpegParA% - | $x264Path %x264ParA%"+' %x264Var$sChar%'} #ffmpeg+x264+multiple 149 | mba {$keyRoute="$vprsPath %vspipeVarA% %vspipeParA% - | $x265Path %x265ParA%"+' %x265Var$sChar%'} #VSPipe+x265+multiple 150 | mbb {$keyRoute="$vprsPath %vspipeVarA% %vspipeParA% - | $x264Path %x264ParA%"+' %x264Var$sChar%'} #VSPipe+x264+multiple 151 | mca {$keyRoute="$avsyPath %avsyuvVarA% %avsyuvParA% - | $x265Path %x265ParA%"+' %x265Var$sChar%'} #AVSYUV+x265+multiple 152 | mcb {$keyRoute="$avsyPath %avsyuvVarA% %avsyuvParA% - | $x264Path %x264ParA%"+' %x264Var$sChar%'} #AVSYUV+x264+multiple 153 | mda {$keyRoute="$avspPath %avsmodVarA% %avsmodParA% | $x265Path %x265ParA%"+' %x265Var$sChar%'} #AVSPmd+x265+multiple, 上游无"-" 154 | mdb {$keyRoute="$avspPath %avsmodVarA% %avsmodParA% | $x264Path %x264ParA%"+' %x264Var$sChar%'} #AVSPmd+x264+multiple, 上游无"-" 155 | mea {$keyRoute="$svfiPath %olsargVarA% %olsargParA% - | $x265Path %x265ParA%"+' %x265Var$sChar%'} #OLSARG+x265+multiple 156 | meb {$keyRoute="$svfiPath %olsargVarA% %olsargParA% - | $x264Path %x264ParA%"+' %x264Var$sChar%'} #OLSARG+x264+multiple 157 | Default {modeimpextopserr} 158 | } 159 | #「启动G」将已知可用的上下游线路列举并进行排列组合, 以生成备选线路 160 | [array] $upPipeStr=@("$fmpgPath %ffmpegVarA% %ffmpegParA%", "$vprsPath %vspipeVarA% %vspipeParA%", "$avsyPath %avsyuvVarA% %avsyuvParA%", "$avspPath %avsmodVarA% %avsmodParA%","$svfiPath %olsargVarA% %olsargParA%") | Where-Object {$_.Length -gt 26} 161 | Switch ($mode) {#用字长过滤掉dnPipeStr中不存在的线路, 大批量版使用sChar变量所以原始字符串要比单文件版多一个字 162 | s {[array]$dnPipeStr=@( "$x265Path %x265ParA% %x265VarA%", "$x264Path %x264ParA% %x264VarA%") | Where-Object {$_.Length -gt 22}} 163 | m {[array]$dnPipeStr=@(("$x265Path%x265ParA%"+' %x265Var$sChar%'), ("$x264Path %x264ParA%"+' %x264Var$sChar%')) | Where-Object {$_.Length -gt 23}} #单引号防止$sChar被提前激活, 额外的()用于防止"+"和","在Array中出现混淆 164 | Default {modeparamerror} 165 | } 166 | [array]$altRoute=@() #注释符 + `$updnPipeStr值 = 备选线路. 生成所有的备选线路命令行 167 | for ($x=0; $x -lt ($upPipeStr.Length); $x++) {#上游/横向可能性的循环迭代 168 | for ($y=0; $y -lt ($dnPipeStr.Length); $y++) {#下游/纵向可能性的循环迭代 169 | if ($upPipeStr -notlike "avsmod") {$altRoute+="REM "+$upPipeStr[$x]+" - | "+$dnPipeStr[$y]} #AVSPmd, 上游无"-" 170 | else {$altRoute+="REM "+$upPipeStr[$x]+" | "+$dnPipeStr[$y]} #AVSPmd, 上游无"-" 171 | } 172 | } 173 | "√ 可用线路数量为: "+($altRoute.Count.ToString()) | Out-String #此时已得出主选线路`$keyRoute和备选线路`$altRoute 174 | 175 | if ($extOPS="a") {tmpmuxreminder} #选择x265下游时, 给出只能间接封装为.mkv的警告 176 | 177 | #「启动H.m」三维for循环轴通过$validChars[x]+$validChars[y]+$validChars[z], 用三个循环模拟了26进位制实现 178 | #x轴被填满后y轴+1并清除x, 当y轴填满后z轴+1并清除x和y 179 | [int]$x=[int]$y=[int]$z=0 180 | $utf8NoBOM=New-Object System.Text.UTF8Encoding $false #导出utf-8NoBOM文本编码hack 181 | 182 | #迭代开始, 当任意轴达到第27个字母时就进位, 高位轴+1, 低位轴归零. 因Switch占用而不能用临时变量$_. 算3位数26进制 183 | For ($s=0; $s -lt $qty; $s++) { 184 | #$x+=1 在开头注释掉, 因为+1要在生成文件名之后发生 185 | if ($x -gt 25) {$y+=1; $x=0} 186 | if ($y -gt 25) {$z+=1; $y=$x=0} 187 | $sChar=$validChars[$z]+$validChars[$y]+$validChars[$x] #循环中生成$sChar变量 188 | #由于删除了生成临时封装的功能, 故不再需要$serial变量而删除 189 | #循环中的keyRoute和altRoute在下方激活$sChar变量 190 | #由于单纯激活$sChar变量后, Array只会生成换行失败的多行文本, 所以先pipe到Out-String变成多行字符串, 才能激活$sChar变量, 单文件版下无 191 | $banner = "-----------Starting encode "+$sChar+"-----------" 192 | Write-Output " 正在生成enc_$s.bat" 193 | 194 | $enc_gen="REM 「标题」 195 | @echo. 196 | @echo "+$banner+" 197 | 198 | REM 「debug部分」正常使用时注释掉 199 | REM @echo %ffmpegParA% %ffmpegVarA% 200 | REM @echo %ffmpegVar"+$sChar+"% 201 | REM @echo %vspipeParA% %vspipeVarA% 202 | REM @echo %vspipeVar"+$sChar+"% 203 | REM @echo %avsyuvParA% %avsyuvVarA% 204 | REM @echo %avsyuvVar"+$sChar+"% 205 | REM @echo %avsmodParA% %avsmodVarA% 206 | REM @echo %avsmodVar"+$sChar+"% 207 | REM @echo %olsargParA% %olsargVarA% 208 | REM @echo %olsargVar"+$sChar+"% 209 | REM @echo %x265ParA% %x265VarA% 210 | REM @echo %x265Var"+$sChar+"% 211 | REM @echo %x264ParA% %x264VarA% 212 | REM @echo %x264Var"+$sChar+"% 213 | REM pause 214 | 215 | REM 「压制-主要线路」debug时注释掉 216 | REM Var被用于引用动态数据,如输入输出路径和根据源视频自动调整的部分参数值 217 | 218 | "+$ExecutionContext.InvokeCommand.ExpandString(($keyRoute | Out-String))+" 219 | 220 | REM 「压制-备选线路」下方命令去掉REM注释地复制-覆盖到上方命令以更换选中线路 221 | 222 | "+$ExecutionContext.InvokeCommand.ExpandString(($altRoute | Out-String))+" 223 | 224 | REM 「选择续y/暂n/止z」5秒后自动y, 除外字符被choice命令屏蔽, 暂停代表仍可继续. 225 | 226 | choice /C YNZ /T 5 /D Y /M `" Continue? (Sleep=5; Default: Y, Pause: N, Stop: Z)`" 227 | 228 | if %ERRORLEVEL%==3 cmd /k 229 | if %ERRORLEVEL%==2 pause 230 | if %ERRORLEVEL%==1 endlocal && exit /b" 231 | 232 | #Out-File -InputObject $enc_gen -FilePath $trueExpPath -Encoding utf8 233 | if ($mode -eq "m") {[IO.File]::WriteAllLines($ExecutionContext.InvokeCommand.ExpandString($bchExpPath), $enc_gen, $utf8NoBOM)}#大批量模式下激活$s变量 234 | elseif ($mode -eq "s") {[IO.File]::WriteAllLines($bchExpPath, $enc_gen, $utf8NoBOM)} 235 | else {modeparamerror} 236 | $x+=1 237 | }#关闭ForLoop 238 | 239 | Write-Output "`r`n完成,只要线路中的程序不更新,步骤3生成的各种批处理(步骤4)就可以一直调用enc_0S.bat / enc_X.bat" 240 | pause -------------------------------------------------------------------------------- /bbenc-source/Step 2-5 in CHS/3.「生成调用单任务编码的主控」.ps1: -------------------------------------------------------------------------------- 1 | cls #「启动-」大批量版生成的主控缺失文件名, 所以要提醒 2 | Read-Host "[单任务模式]大批量模式下, 生成的主控里没有导入用的文件名, 因此需要手动逐个填写导入文件名`r`nx264一般内置lavf, x265一般不带, 不内置lavf库的编码器需要通过ffmpeg等上游pipe端工具导入视频流,输出未封装的流. 按Enter继续" 3 | $mode="s" 4 | Function namecheck([string]$inName) { 5 | $badChars = '[{0}]' -f [regex]::Escape(([IO.Path]::GetInvalidFileNameChars() -join '')) 6 | ForEach ($_ in $badChars) {if ($_ -match $inName) {return $false}} 7 | return $true 8 | } #检测文件名是否符合Windows命名规则 9 | 10 | Function whereisit($startPath='DESKTOP') { 11 | #启用System.Windows.Forms选择文件的GUI交互窗,通过SelectedPath将GUI交互窗锁定到桌面文件夹, 效果一般 12 | [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") 13 | Add-Type -AssemblyName System.Windows.Forms 14 | $startPath = New-Object System.Windows.Forms.OpenFileDialog -Property @{ InitialDirectory = [Environment]::GetFolderPath('DESKTOP') }#GUI交互窗锁定到桌面文件夹 15 | Do {$dInput = $startPath.ShowDialog()} While ($dInput -eq "Cancel") #1.打开选择文件的GUI交互窗, 通过重新打开选择窗来反取消用户的取消操作. 2. 窗口大, TopMost关 16 | return $startPath.FileName 17 | } 18 | 19 | Function whichlocation($startPath='DESKTOP') { 20 | #启用System.Windows.Forms选择文件夹的GUI交互窗 21 | Add-Type -AssemblyName System.Windows.Forms 22 | $startPath = New-Object System.Windows.Forms.FolderBrowserDialog -Property @{ Description="选择路径用的窗口. 拖拽边角可放大以便操作"; SelectedPath=[Environment]::GetFolderPath($startPath); RootFolder='MyComputer'; ShowNewFolderButton=$true } 23 | #打开选择文件的GUI交互窗, 用Do-While循环拦截误操作(取消/关闭选择窗) 24 | Do {$dInput = $startPath.ShowDialog((New-Object System.Windows.Forms.Form -Property @{TopMost=$true}))} While ($dInput -eq "Cancel") #2. 窗口小, TopMost开 25 | #由于选择根目录时路径变量含"\", 而文件夹时路径变量缺"\", 所以要自动判断并补上 26 | if (($startPath.SelectedPath.SubString($startPath.SelectedPath.Length-1) -eq "\") -eq $false) {$startPath.SelectedPath+="\"} 27 | return $startPath.SelectedPath 28 | } 29 | 30 | Function setencoutputname ([string]$mode, [string]$switchOPS) { 31 | $DebugPreference="Continue" #function里不能用Write-Output/Host,或" "来输出交互信息, 所以用Write-Debug 32 | 33 | Switch ($switchOPS) { #函数中不支持「Switch + Readhost " $变量名 "」,所以把原本由Switch问的问题在进入函数前就要回答,只把答案导入进函数中 34 | a { Write-Debug "已打开[复制文件名]的选择窗" 35 | $vidEXP=whereisit 36 | $chkme=namecheck($vidEXP) 37 | $vidEXP=[io.path]::GetFileNameWithoutExtension($vidEXP) 38 | if ($mode -eq "m") {$vidEXP+='_$serial'} #!使用单引号防止$serial变量被激活 39 | Write-Debug "大批量模式下选项A会在末尾添加序号, 文件名尾会多出`"_`"`r`n" 40 | } b { 41 | if ($mode -eq "m") {#大批量模式用 42 | Do {$vidEXP=Read-Host "`r`n填写文件名(无后缀), 大批量模式下要于集数变化处填 `$serial, 并隔开`$serial后的英文字母, 两个方括号间要隔开. 如 [Zzz] Memories – `$serial (BDRip 1764x972 HEVC)" 43 | $chkme=namecheck($vidEXP) 44 | if (($vidEXP.Contains("`$serial") -eq $false) -or ($chkme -eq $false)) {Write-Warning "文件名中缺少变量`$serial, 输入了空值, 或拦截了不可用字符/ | \ < > : ? * `""} 45 | } While (($vidEXP.Contains("`$serial") -eq $false) -or ($chkme -eq $false)) 46 | } 47 | if ($mode -eq "s") {#单文件模式用 48 | Do {$vidEXP=Read-Host "`r`n填写文件名(无后缀), 两个方括号间要隔开. 如 [Zzz] Memories – 01 (BDRip 1764x972 HEVC)" 49 | $chkme=namecheck($vidEXP) 50 | if (($vidEXP.Contains("`$serial") -eq $true) -or ($chkme -eq $false)) {Write-Warning "单文件模式下文件名中含变量`$serial; 输入了空值; 或拦截了不可用字符/ | \ < > : ? * `""} 51 | } While (($vidEXP.Contains("`$serial") -eq $true) -or ($chkme -eq $false)) 52 | } 53 | #[string]$serial=($s).ToString($zroStr) #赋值示例. 用于下面的for循环(提供变量$s) 54 | #$vidEXP=$ExecutionContext.InvokeCommand.ExpandString($vidEXP) #下面的for循环中, 用户输入的变量只能通过Expand方法才能作为变量激活$serial 55 | } default {#相比于settmpoutputname, 此函数不存在空值输入,所以default状态下就是原始的$vidEXP文件名 56 | if ($mode -eq "m") {$vidEXP+='_$serial'} #!使用单引号防止$serial变量被激活 57 | } 58 | } 59 | Write-Debug "√ 写入了导出文件名 $vidEXP`r`n" 60 | return $vidEXP 61 | } 62 | 63 | Function hevcparwrapper { 64 | Param ([Parameter(Mandatory=$true)]$PICKops) 65 | Switch ($PICKops) { 66 | a {return "--tu-intra-depth 3 --tu-inter-depth 3 --limit-tu 1 --rdpenalty 1 --me umh --merange 48 --weightb--ref 3 --max-merge 3 --early-skip --no-open-gop --min-keyint 5 --fades --bframes 8 --b-adapt 2 --radl 3 --b-intra --constrained-intra --crf 21 --crqpoffs -3 --crqpoffs -1 --rdoq-level 2 --aq-mode 4 --aq-strength 0.8 --rd 3 --limit-modes --limit-refs 1 --rskip 3 --tskip-fast --rect --amp --psy-rd 1 --splitrd-skip --qp-adaptation-range 4 --limit-sao --sao-non-deblock --deblock 0:-1 --hash 2 --allow-non-conformance"} #generalPurpose 67 | b {return "--tu-intra-depth 4 --tu-inter-depth 4 --limit-tu 1 --me star --merange 48 --weightb --ref 3 --max-merge 4 --no-open-gop --min-keyint 3 --keyint 310 --fades --bframes 8 --b-adapt 2 --radl 3 --constrained-intra --b-intra --crf 21.8 --qpmin 8 --crqpoffs -3 --ipratio 1.2 --pbratio 1.5 --rdoq-level 2 --aq-mode 4 --qg-size 8 --rd 3 --limit-refs 0 --rskip 0 --rect --amp --psy-rd 1.6 --deblock 0:0 --limit-sao --sao-non-deblock --selective-sao 3 --hash 2 --allow-non-conformance"} #filmCustom 68 | c {return "--tu-intra-depth 4 --tu-inter-depth 4 --limit-tu 1 --me star --merange 48 --weightb --ref 3 --max-merge 4 --no-open-gop --min-keyint 3 --fades --bframes 8 --b-adapt 2 --radl 3 --constrained-intra --b-intra --crf 21.8 --qpmin 8 --crqpoffs -3 --ipratio 1.2 --pbratio 1.5 --rdoq-level 2 --aq-mode 4 --aq-strength 1 --qg-size 8 --rd 3 --limit-refs 0 --rskip 0 --rect --amp --psy-rd 1 --qp-adaptation-range 3 --deblock 0:-1 --limit-sao --sao-non-deblock --selective-sao 3 --hash 2 --allow-non-conformance"} #stockFootag 69 | d {return "--tu-intra-depth 4 --tu-inter-depth 4 --max-tu-size 16 --me umh --merange 48 --weightb --max-merge 4 --early-skip --ref 3 --no-open-gop --min-keyint 5 --fades --bframes 16 --b-adapt 2 --radl 3 --bframe-bias 20 --constrained-intra --b-intra --crf 22 --crqpoffs -4 --cbqpoffs -2 --ipratio 1.6 --pbratio 1.3 --cu-lossless --tskip --psy-rdoq 2.3 --rdoq-level 2 --hevc-aq --aq-strength 0.9 --qg-size 8 --rd 3 --limit-modes --limit-refs 1 --rskip 1 --rect --amp --psy-rd 1.5 --splitrd-skip --rdpenalty 2 --qp-adaptation-range 4 --deblock -1:0 --limit-sao --sao-non-deblock --hash 2 --allow-non-conformance --single-sei"} #animeFansubCustom 70 | e {return "--tu-intra-depth 4 --tu-inter-depth 4 --max-tu-size 4 --limit-tu 1 --me star --merange 52 --analyze-src-pics --weightb --max-merge 4 --ref 3 --no-open-gop --min-keyint 1 --fades --bframes 16 --b-adapt 2 --radl 2 --b-intra --crf 17 --crqpoffs -5 --cbqpoffs -2 --ipratio 1.67 --pbratio 1.33 --cu-lossless --psy-rdoq 2.5 --rdoq-level 2 --hevc-aq --aq-strength 1.4 --qg-size 8 --rd 5 --limit-refs 0 --rskip 0 --rect --amp --no-cutree --psy-rd 1.5 --rdpenalty 2 --qp-adaptation-range 5 --deblock -2:-2 --limit-sao --sao-non-deblock --selective-sao 1 --hash 2 --allow-non-conformance"} #animeBDRipColdwar 71 | } 72 | } 73 | 74 | Function avcparwrapper { 75 | Param ([Parameter(Mandatory=$true)]$PICKops) 76 | Switch ($PICKops) { 77 | a {return "--me umh --merange 48 --no-fast-pskip --direct auto --weightb --min-keyint 5 --bframes 12 --b-adapt 2 --ref 3 --crf 19 --qpmin 9 --chroma-qp-offset -2 --aq-mode 3 --aq-strength 0.9 --trellis 2 --deblock0:-1 --psy-rd 0.6:1.1"} #generalPurpose 78 | b {return "--me umh --merange 48 --no-fast-pskip --direct auto --weightb --min-keyint 1 --bframes 12 --b-adapt 2 --ref 3 --sliced-threads --crf 17 --tune grain --trellis 2"} #stockFootage 79 | } 80 | } 81 | 82 | Function x265submecalc{ # 24fps=3, 48fps=4, 60fps=5, ++=6 83 | Param ([Parameter(Mandatory=$true)]$CSVfps) 84 | if ((Invoke-Expression $CSVfps) -lt 25) {return "--subme 3"} 85 | elseif ((Invoke-Expression $CSVfps) -lt 49) {return "--subme 4"} 86 | elseif ((Invoke-Expression $CSVfps) -lt 61) {return "--subme 5"} 87 | else {return "--subme 6"} 88 | } 89 | 90 | Function keyintcalc{ # fps×9 91 | Param ([Parameter(Mandatory=$true)]$CSVfps) 92 | try {return "--keyint "+[math]::Round((Invoke-Expression $CSVfps)*9)} catch {return "--keyint 249"} #故意设定稀有值以同时方便debug和常用 93 | } 94 | 95 | Function poolscalc{ 96 | $allprocs=Get-CimInstance Win32_Processor | Select Availability 97 | $DebugPreference="Continue" #Cannot use Write-Output/Host or " " inside a Function as it would trigger a value return, modify Write-Debug instead 98 | [int]$procNodes=0 99 | ForEach ($_ in $allprocs) {if ($_.Availability -eq 3) {$procNodes+=1}} #只添加正常的处理器,否则未安装的槽也算 100 | if ($procNodes -gt 1) { 101 | if ($procNodes -eq 2) {return "--pools +,-"} 102 | elseif ($procNodes -eq 4) {return "--pools +,-,-,-"} 103 | elseif ($procNodes -eq 6) {return "--pools +,-,-,-,-,-"} 104 | elseif ($procNodes -eq 8) {return "--pools +,-,-,-,-,-,-,-"} 105 | elseif ($procNodes -gt 8) {Write-Debug "? 检测到安装了超过8颗处理器($procNodes), 需手动填写--pools"; return ""} #不能用else, 否则-eq 1也会被算进去 106 | } else {Write-Debug "√ 检测到安装了1颗处理器, 将不会填写--pools"; return ""} 107 | } 108 | 109 | Function framescalc{ 110 | Param ([Parameter(Mandatory=$true)]$fcountCSV, [Parameter(Mandatory=$false)]$fcountAUX) 111 | $DebugPreference="Continue" #Cannot use Write-Output/Host or " " inside a Function as it would trigger a value return, modify Write-Debug instead 112 | if ($fcountCSV -match "^\d+$") {Write-Debug "√ 检测到MPEGtag视频总帧数"; return "--frames "+$fcountCSV} 113 | elseif ($fcountAUX -match "^\d+$") {Write-Debug "√ 检测到MKV-tag视频总帧数"; return "--frames "+$fcountAUX} 114 | else {return ""} 115 | } 116 | 117 | #「@MrNetTek」高DPI显示渲染模式的System.Windows.Forms 118 | Add-Type -TypeDefinition @' 119 | using System.Runtime.InteropServices; 120 | public class ProcessDPI { 121 | [DllImport("user32.dll", SetLastError=true)] 122 | public static extern bool SetProcessDPIAware(); 123 | } 124 | '@ 125 | $null = [ProcessDPI]::SetProcessDPIAware() 126 | 127 | #「启动A」生成1~n个"enc_[序号].bat"单文件版不需要 128 | if ($mode -eq "m") { 129 | [array]$validChars='A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z' 130 | [int]$qty=0 #从0而非1开始数 131 | Do {[int]$qty = (Read-Host -Prompt "指定[之前生成压制批处理]的[整数数量]") 132 | if ($qty -eq 0) {"输入了非整数或空值"} elseif ($qty -gt 15625) {Write-Error "× 编码次数超过15625"; pause; exit} 133 | } While ($qty -eq 0) 134 | #「启动B」选择是否在导出文件序号上补零, 由于int变量$qty得不到字长Length, 所以先转string再取值 135 | if ($qty -gt 9) {#个位数下关闭补零 136 | Do {[string]$leadCHK=""; [int]$ldZeros=0 137 | Switch (Read-Host "选择之前[y启用了 | n关闭了]导出压制文件名的[序号补0]. 如导出十位数文件时写作01, 02...") { 138 | y {$leadCHK="y"; Write-Output "√ 启用补零`r`n"; $ldZeros=$qty.ToString().Length} 139 | n {$leadCHK="n"; Write-Output "× 关闭补零`r`n"} 140 | default {Write-Warning "`r`n× 输入错误, 重试"} 141 | } 142 | } While ($leadCHK -eq "") 143 | [string]$zroStr="0"*$ldZeros #得到.ToString('000')所需的'000'部分, 如果关闭补零则$zroStr为0, 补零计算仍然存在但没有效果 144 | } else {[string]$zroStr="0"} 145 | } 146 | #「启动C」定位导出主控文件用路径 147 | Read-Host "将打开[导出主控批处理]的路径选择窗. 按Enter继续" 148 | $exptPath = whichlocation 149 | Write-Output "√ 选择的路径为 $exptPath`r`n" 150 | 151 | #「启动D」定位导出压制结果用路径 152 | Read-Host "将打开[导出压制文件]的路径选择窗. 按Enter继续" 153 | $fileEXPpath = whichlocation 154 | Write-Output "选择的路径为 $fileEXPpath`r`n" 155 | 156 | #「启动E」导入原文件, 注意步骤2中已经导入了ffmpeg等工具的路径. 所以步骤3只导入源. 注意变量也为此而改了名 157 | Write-Output "参考[视频文件类型]https://en.wikipedia.org/wiki/Video_file_format`r`n由于步骤2已填写ffmpeg, vspipe, avs2yuv, avs2pipemod的所在路径, 所以步骤3中选择的是待压制文件`r`n" 158 | Do {$IMPchk=$vidIMP=$vpyIMP=$avsIMP=$apmIMP="" 159 | Switch (Read-Host "之前选择的pipe上游方案是[A: ffmpeg | B: vspipe | C: avs2yuv | D: avs2pipemod | E: SVFI (alpha)]") { 160 | a {$IMPchk="a"; Write-Output "`r`n选择了ffmpeg----视频源. 已打开[定位源]的文件选窗"; $vidIMP=whereisit} 161 | b {$IMPchk="b"; Write-Output "`r`n选择了vspipe----.vpy源. 已打开[定位源]的文件选窗"; $vpyIMP=whereisit} 162 | c {$IMPchk="c"; Write-Output "`r`n选择了avs2yuv---.avs源. 已打开[定位源]的文件选窗"; $avsIMP=whereisit} 163 | d {$IMPchk="d"; Write-Output "`r`n选了avs2pipemod-.avs源. 已打开[定位源]的文件选窗"; $apmIMP=whereisit} 164 | e {$IMPchk="e"; Write-Output "`r`n选了SVFI(alpha)-视频源. 已打开[定位源]的文件选窗"; $vidIMP=whereisit} 165 | default {Write-Warning "`r`n× 输入错误, 重试"} 166 | } 167 | if (($vidIMP+$vpyIMP+$avsIMP+$apmIMP).Contains(".exe")) {$IMPchk=""; Write-Error "`r`n× 该输入不是导入上游方案,而是要编码的源"} 168 | } While ($IMPchk -eq "") 169 | 170 | #「启动F1」整合并反馈选取的路径/文件 171 | $impEXTs=$vidIMP+$vpyIMP+$avsIMP+$apmIMP 172 | if ($mode -eq "m") {Write-Output "`r`n√ 选择的路径为 $impEXTm`r`n"} 173 | if ($mode -eq "s") {Write-Output "`r`n√ 选择的文件为 $impEXTs`r`n" 174 | if ($impEXTs -eq "") {Write-Error "× 没有导入任何文件"; pause; exit} 175 | else {#「启动F2」单文件模式下生成默认导出文件名, 而需获取文件名与后缀的方法. 由于变量污染问题摒弃了Get-ChildItem 176 | $impEXTs=[io.path]::GetExtension($impEXTs) 177 | $impFNM =[io.path]::GetFileNameWithoutExtension($impEXTs) 178 | } 179 | #「启动F3」avs2yuv, avs2pipemod线路检查文件后缀名是否正常 180 | if (($IMPchk -eq "d") -or ($IMPchk -eq "c")) { 181 | if ($impEXTs -ne ".avs") {Write-Warning "文件后缀名是 $impEXTs 而非 .avs`r`n"} #if选项用于防止ffmpeg线路下输入了空值$impEXTs 182 | } elseif ($IMPchk -eq "b") {#「启动F4」vspipe线路检查文件后缀名是否正常 183 | if ($impEXTs -ne ".vpy") {Write-Warning "文件后缀名是 $impEXTs 而非 .vpy`r`n"} #大批量模式下输入的是路径所以失效 184 | } #注: 导入路径: $impEXTm, 导入文件: $impEXTs, 导出路径: $fileEXPpath 185 | } 186 | #「启动G1」Avs2pipemod需要的文件 187 | if ($IMPchk -eq "d") { 188 | Read-Host "将为Avs2pipemod打开[选择avisynth.dll]的路径选择窗. 按Enter继续" 189 | $apmDLL=whereisit 190 | $DLLchk=(Get-ChildItem $apmDLL).Extension #检查文件后缀是否为.dll并报错 191 | if (($DLLchk -eq ".dll") -eq $false) {Write-Warning "文件后缀名是 $apmDLL 而非 .dll `r`n"} 192 | Write-Output "√ 已添加avs2pipemod参数: $apmDLL`r`n" 193 | } else { 194 | $apmDLL="X:\Somewhere\avisynth.dll" 195 | Write-Output "未选择Avs2pipemod线路, AVS动态链接库路径将临时设为 $apmDLL `r`n" 196 | } 197 | #「启动G2」SVFI需要的文件 198 | if ($IMPchk -eq "e") { 199 | Write-Warning "本程序会自动修改渲染配置ini文件中的target_fps值, 目的是将下游x264/5编码器设置x264Par, x265Par中的--fps设置统一起来`r`n但缺点是自定义的插帧设置会失效, 若需插帧则手动修改target_fps及x264/5Par设置的--fps参数." 200 | Read-Host "`r`n将为SVFI打开[自定渲染配置.ini]的路径选择窗.`r`nSteam发布端的路径如 X:\SteamLibrary\steamapps\common\SVFI\Configs\*.ini 按Enter继续" 201 | $olsINI=whereisit 202 | $INIchk=(Get-ChildItem $olsINI).Extension #检查文件后缀是否为.ini并报错 203 | if (($INIchk -eq ".ini") -eq $false) {Write-Warning "文件后缀名是 $olsINI 而非 .ini"} 204 | Write-Output "√ 已添加SVFI参数: $olsINI`r`n" 205 | } else { 206 | $olsINI="X:\Somewhere\SVFI-render-customize.ini" 207 | Write-Output "未选择SVFI线路, 配置文件路径将临时设为 $olsINI `r`n" 208 | } 209 | #「启动H」四种情况下需要专门导入视频给ffprobe检测: VS(1), AVS(2), 大批量模式(1) 210 | if (($mode -eq "m") -or (($IMPchk -ne "a") -and ($IMPchk -ne "e"))) { 211 | Do {$continue="n" 212 | Read-Host "`r`n将为ffprobe打开[送检用源视频]的文件选择窗, 因为大批量版下只会导入路径, 而单文件版下ffprobe无法检测.vpy和.avs; 按Enter继续..." 213 | $impEXTs=whereisit 214 | if ((Read-Host "[检查]输入的文件 $impEXTs 是否为视频 [Y: 确认操作 | N: 更换源]") -eq "y") {$continue="y"; Write-Output "继续"} else {Write-Output "重试"} 215 | } While ($continue -eq "n") 216 | } else {$impEXTs=$vidIMP} 217 | 218 | if ($impEXTs.Contains(".mov")) { 219 | $is_mov=$true; Write-Output "√ 导入视频 $impEXTs 的封装格式为MOV`r`n" 220 | } else { 221 | $is_mov=$false; Write-Output "√ 导入视频 $impEXTs 的封装格式非MOV`r`n" 222 | } 223 | 224 | #「启动I」定位ffprobe 225 | Read-Host "将打开[定位ffprobe.exe]的选择窗. 按Enter继续" 226 | $fprbPath=whereisit 227 | 228 | #「ffprobeA2」开始检测片源, 由于mkv有给不同视频流标注多种视频总帧数的tag功能, 导致封装视频的人会错标 NUMBER_OF_FRAMES 成 NUMBER_OF_FRAMES-eng (两者分别占据csv的第24,25号), 所以同时尝试读取两者, 缺少任意一值则X位中显示另一值, 所以只检测X位 229 | # 由于MOV封装格式下区别于MP4,MKV的stream_tags,所以全部无效 230 | #「ffprobeB2」用CSV读取模块映射array数据, 由于源文件没有所以添加目录A~F, 由于ffprobe生成的CSV不能直接导入进变量, 并且为方便debug(Remove-Item换成Notepad), 所以创建了中间文件 231 | # 由于stream作为标题会被写入CSV,所以自动忽略A项 232 | # 例: $parsProbe = "D:\ffprobe.exe -i `"F:\Asset\Video\BDRip私种\[Beatrice-Raws] Anne Happy [BDRip 1920x1080 x264 FLAC]\[Beatrice-Raws] Anne Happy 01 [BDRip 1920x1080 x264 FLAC].mkv`" -select_streams v:0 -v error -hide_banner -show_streams -show_entries stream=width,height,pix_fmt,avg_frame_rate,nb_frames,color_space,color_transfer,color_primaries:stream_tags=NUMBER_OF_FRAMES,NUMBER_OF_FRAMES-eng -of csv" 233 | # 例: $parsProbe = "D:\ffprobe.exe -i `"N:\SolLevante_HDR10_r2020_ST2084_UHD_24fps_1000nit.mov`" -select_streams v:0 -v error -hide_banner -show_streams -show_entries stream=width,height,pix_fmt,avg_frame_rate,nb_frames,color_space,color_transfer,color_primaries:stream_tags=NUMBER_OF_FRAMES,NUMBER_OF_FRAMES-eng -of csv" 234 | # Invoke-Expression $parsProbe > "$env:USERPROFILE\temp_v_info.csv" 235 | # Notepad "$env:USERPROFILE\temp_v_info.csv" 236 | Switch ($is_mov) { 237 | $true { 238 | [String]$parsProbe = $fprbPath+" -i `"$impEXTs`" -select_streams v:0 -v error -hide_banner -show_streams -show_entries stream=width,height,pix_fmt,avg_frame_rate,nb_frames,color_space,color_transfer,color_primaries -of csv" 239 | Invoke-Expression $parsProbe > "$env:USERPROFILE\temp_v_info_is_mov.csv" #由于多数Windows系统只有C盘, 所以临时生成CSV在C盘 240 | $ffprobeCSV = Import-Csv "$env:USERPROFILE\temp_v_info_is_mov.csv" -Header A,B,C,D,E,F,G,H,I 241 | } 242 | $false{ 243 | [String]$parsProbe = $fprbPath+" -i `"$impEXTs`" -select_streams v:0 -v error -hide_banner -show_streams -show_entries stream=width,height,pix_fmt,avg_frame_rate,nb_frames,color_space,color_transfer,color_primaries:stream_tags=NUMBER_OF_FRAMES,NUMBER_OF_FRAMES-eng -of csv" 244 | Invoke-Expression $parsProbe > "$env:USERPROFILE\temp_v_info.csv" #由于多数Windows系统只有C盘, 所以临时生成CSV在C盘 245 | $ffprobeCSV = Import-Csv "$env:USERPROFILE\temp_v_info.csv" -Header A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,AA 246 | } 247 | } 248 | if (Test-Path "$env:USERPROFILE\temp_v_info.csv") {Remove-Item "$env:USERPROFILE\temp_v_info.csv"} 249 | elseif (Test-Path "$env:USERPROFILE\temp_v_info_is_mov.csv") {Remove-Item "$env:USERPROFILE\temp_v_info_is_mov.csv"} 250 | 251 | #「ffprobeB3」根据视频帧数自动填写x265的--subme,H=第8个$ffprobeCSV序列值 252 | $x265subme=x265submecalc -CSVfps $ffprobeCSV.H 253 | Write-Output "√ 已添加x265参数: $x265subme" 254 | 255 | #「ffprobeB4」根据视频帧数自动填写x265, x264的--keyint 256 | $keyint=keyintcalc -CSVfps $ffprobeCSV.H 257 | Write-Output "√ 已添加x264/5参数: $keyint" 258 | 259 | $WxH="--input-res "+$ffprobeCSV.B+"x"+$ffprobeCSV.C+"" 260 | $color_mtx="--colormatrix "+$ffprobeCSV.F 261 | $trans_chrctr="--transfer "+$ffprobeCSV.G 262 | if ($ffprobeCSV.F -eq "unknown") {$avc_mtx="--colormatrix undef"} else {$avc_mtx=$color_mtx} #x264: ×--colormatrix unknown √--colormatrix undef 263 | if ($ffprobeCSV.G -eq "unknown") {$avc_tsf="--colormatrix undef"} else {$avc_tsf=$trans_chrctr} #x264: ×--transfer unknown √--transfer undef 264 | $fps="--fps "+$ffprobeCSV.H 265 | $fmpgfps="-r "+$ffprobeCSV.H 266 | Write-Output "√ 已添加x264参数: $fps $WxH`r`n√ 已添加x265参数: $color_mtx $trans_chrctr $fps $WxH`r`n√ 已添加ffmpeg参数: $fmpgfps`r`n" 267 | 268 | #「ffprobeC1」自动替换SVFI渲染配置文件的target_fps, 并导出新文件. 唯SVFI线路需要 269 | if ($IMPchk -eq "e") { 270 | $iniEXP="$env:USERPROFILE\bbenc_svfi_targetfps_mod_"+(Get-Date).ToString('yyyy.MM.dd.hh.mm.ss')+".ini" 271 | $olsfps="target_fps="+$ffprobeCSV.H 272 | $iniCxt=Get-Content $olsINI 273 | $iniTgt=$iniCxt | Select-String target_fps | Select-Object -ExpandProperty Line 274 | $iniCxt | ForEach-Object {$_ -replace $iniTgt,$olsfps}>$iniEXP 275 | Write-Output "√ 已将渲染配置文件 $olsINI 的target_fps行替换为 $olsfps,`r`n√ 新的渲染配置文件已导出为 $iniEXP" 276 | } else {$iniEXP=$olsINI} 277 | 278 | #「ffprobeC2」ffprobe获取视频总帧数并赋值到$x264/5VarA中, 唯单文件版可用 279 | if ($mode -eq "s") {$nbrFrames=framescalc -fcountCSV $ffprobeCSV.I -fcountAUX $ffprobeCSV.AA} 280 | if ($nbrFrames -ne "") {Write-Output "√ 已添加x264/5参数: $nbrFrames"} 281 | else {Write-Warning "× 总帧数的数据被删, 将留空x264/5参数--frames, 缺点是不再显示ETA(预计完成时间)"} 282 | 283 | #「ffprobeD1」获取色彩空间格式, 给ffmpeg, VapourSynth, AviSynth, AVS2PipeMod, x264和x265赋值 284 | [string]$avsCSP=[string]$avsD=[string]$encCSP=[string]$ffmpegCSP=[string]$encD=$null 285 | Do {Switch ($ffprobeCSV.D) { 286 | yuv420p {Write-Output "检测到源的色彩空间==[yuv420p 8bit ]"; $avsCSP="-csp i420"; $avsD="-depth 8"; $encCSP="--input-csp i420"; $encD="--input-depth 8"; $ffmpegCSP="-pix_fmt yuv420p"} 287 | yuv420p10le {Write-Output "检测到源的色彩空间==[yuv420p 10bit]"; $avsCSP="-csp i420"; $avsD="-depth 10"; $encCSP="--input-csp i420"; $encD="--input-depth 10"; $ffmpegCSP="-pix_fmt yuv420p10le"} 288 | yuv420p12le {Write-Output "仅x265支持的色彩空间[yuv420p 12bit]"; $avsCSP="-csp i420"; $avsD="-depth 12"; $encCSP="--input-csp i420"; $encD="--input-depth 12"; $ffmpegCSP="-pix_fmt yuv420p12le"} 289 | yuv422p {Write-Output "检测到源的色彩空间==[yuv422p 8bit ]"; $avsCSP="-csp i422"; $avsD="-depth 8"; $encCSP="--input-csp i422"; $encD="--input-depth 8"; $ffmpegCSP="-pix_fmt yuv422p"} 290 | yuv422p10le {Write-Output "检测到源的色彩空间==[yuv422p 10bit]"; $avsCSP="-csp i422"; $avsD="-depth 10"; $encCSP="--input-csp i422"; $encD="--input-depth 10"; $ffmpegCSP="-pix_fmt yuv422p10le"} 291 | yuv422p12le {Write-Output "仅x265支持的色彩空间[yuv422p 12bit]"; $avsCSP="-csp i422"; $avsD="-depth 12"; $encCSP="--input-csp i422"; $encD="--input-depth 12"; $ffmpegCSP="-pix_fmt yuv422p12le"} 292 | yuv444p {Write-Output "检测到源的色彩空间==[yuv444p 8bit ]"; $avsCSP="-csp i444"; $avsD="-depth 8"; $encCSP="--input-csp i444"; $encD="--input-depth 8"; $ffmpegCSP="-pix_fmt yuv444p"} 293 | yuv444p10le {Write-Output "检测到源的色彩空间==[yuv444p 10bit]"; $avsCSP="-csp i444"; $avsD="-depth 10"; $encCSP="--input-csp i444"; $encD="--input-depth 10"; $ffmpegCSP="-pix_fmt yuv444p10le"} 294 | yuv444p12le {Write-Output "仅x265支持的色彩空间[yuv444p 12bit]"; $avsCSP="-csp i444"; $avsD="-depth 12"; $encCSP="--input-csp i444"; $encD="--input-depth 12"; $ffmpegCSP="-pix_fmt yuv444p12le"} 295 | yuva444p10le{Write-Output "检测到源的色彩空间==[yuv444p 10bit]"; $avsCSP="-csp i444"; $avsD="-depth 10"; $encCSP="--input-csp i444"; $encD="--input-depth 10"; $ffmpegCSP="-pix_fmt yuv444p10le"} 296 | yuva444p12le{Write-Output "仅x265支持的色彩空间[yuv444p 12bit]"; $avsCSP="-csp i444"; $avsD="-depth 12"; $encCSP="--input-csp i444"; $encD="--input-depth 12"; $ffmpegCSP="-pix_fmt yuv444p12le"} 297 | gray {Write-Output "检测到源的色彩空间==[yuv400p 8bit ]"; $avsCSP="-csp i400"; $avsD="-depth 8"; $encCSP="--input-csp i400"; $encD="--input-depth 8"; $ffmpegCSP="-pix_fmt gray"} 298 | gray10le {Write-Output "检测到源的色彩空间==[yuv400p 10bit]"; $avsCSP="-csp i400"; $avsD="-depth 10"; $encCSP="--input-csp i400"; $encD="--input-depth 10"; $ffmpegCSP="-pix_fmt gray10le"} 299 | gray12le {Write-Output "仅x265支持的色彩空间[yuv400p 12bit]"; $avsCSP="-csp i400"; $avsD="-depth 12"; $encCSP="--input-csp i400"; $encD="--input-depth 12"; $ffmpegCSP="-pix_fmt gray12le"} 300 | nv12 {Write-Output "仅x265支持的色彩空间[ nv12 12bit ]"; $avsCSP="-csp AUTO"; $avsD="-depth 12"; $encCSP="--input-csp nv12"; $encD="--input-depth 12"; $ffmpegCSP="-pix_fmt nv12"} 301 | nv16 {Write-Output "仅x265支持的色彩空间[ nv16 16bit ]"; $avsCSP="-csp AUTO"; $avsD="-depth 16"; $encCSP="--input-csp nv16"; $encD="--input-depth 16"; $ffmpegCSP="-pix_fmt nv16"} 302 | default {Write-Warning "! 不兼容的色彩空间"($ffprobeCSV.D)} 303 | } 304 | } While ($ffmpegCSP -eq $null) 305 | if ($ffmpegCSP -ne $null) {Write-Output "√ 已添加ffmpeg参数: $ffmpegCSP`r`n√ 已添加avs2yuv参数: $avsCSP $avsD`r`n"} 306 | if ($avsCSP -eq "-csp AUTO") {Write-Warning "avs2yuv可能不兼容nv12/nv16色彩空间"} 307 | 308 | #「启动J」选择下游程序. x264或x265 309 | Do {$ENCops=$x265Path=$x264Path="" 310 | Switch (Read-Host "选择pipe下游程序 [A: x265/hevc | B: x264/avc]") { 311 | a {$ENCops="a"; Write-Output "`r`n选择了x265--A线路. 已打开[定位x265.exe]的选窗"; $x265Path=whereisit} 312 | b {$ENCops="b"; Write-Output "`r`n选择了x264--B线路. 已打开[定位x264.exe]的选窗"; $x264Path=whereisit} 313 | default {Write-Warning "`r`n× 输入错误, 重试"} 314 | } 315 | } While ($ENCops -eq "") 316 | $encEXT=$x265Path+$x264Path 317 | Write-Output "√ 选择了 $encEXT `r`n" 318 | 319 | #「启动K1」选择导出压制结果文件名的多种方式, 集数变量$serial于下方的循环中实现序号叠加, 单文件模式不需要集数变量 320 | $vidEXP=[io.path]::GetFileNameWithoutExtension($impEXTs) 321 | Do {$switchOPS="" 322 | $switchOPS=Read-Host "`r`n选择导出压制结果的文件名`r`n[A: 选择文件并拷贝 | B: 手动填写 | C: $vidEXP]" 323 | if (($switchOPS -ne "a") -and ($switchOPS -ne "b") -and ($switchOPS -ne "c")) {Write-Error "× 输入错误,重试"} 324 | } While (($switchOPS -ne "a") -and ($switchOPS -ne "b") -and ($switchOPS -ne "c")) 325 | 326 | if (($switchOPS -eq "a") -or ($switchOPS -eq "b")) {$vidEXP = setencoutputname($mode, $switchOPS)} 327 | else {Write-Output "√ 写入了导出文件名 $vidEXP`r`n"} 328 | 329 | #「启动K2」x264线路下,选择导出压制结果的后缀名(x265线路下默认.hevc) 330 | if ($ENCops -eq "b") {$vidFMT="" 331 | Do {Switch (Read-Host "「x264线路」选择导出压制结果的文件后缀名/格式`r`n[A: MKV | B: MP4 | C: FLV]`r`n") { 332 | a {$vidFMT=".mkv"} b {$vidFMT=".mp4"} c {$vidFMT=".flv"} Default {Write-Error "`r`n× 输入错误,重试"} 333 | } 334 | } While ($vidFMT -eq "") 335 | } elseif ($ENCops -eq "a") {$vidFMT=".hevc"} 336 | 337 | #「启动L, M」1: 根据选择x264/5来决定输出.hevc/.mp4. 2: x265下据cpu核心数量, 节点数量添加pme/pools 338 | if ($ENCops -eq "b") { 339 | Do {$PICKops=$x264ParWrap="" 340 | Switch (Read-Host "选择x264压制参数预设 [A: 高画质高压缩 | B: 剪辑素材存档]") { 341 | a {$x264ParWrap=avcparwrapper -PICKops "a"; Write-Output "`r`n√ 选择了高画质高压缩预设"} 342 | b {$x264ParWrap=avcparwrapper -PICKops "b"; Write-Output "`r`n√ 选择了剪辑素材存档预设"} 343 | default {Write-Warning "`r`n× 输入错误, 重试"} 344 | } 345 | } While ($x264ParWrap -eq "") 346 | Write-Output "√ 已定义x264压制参数: $x264ParWrap" 347 | } 348 | elseif ($ENCops -eq "a") { 349 | $pme=$pool="" 350 | $procNodes=0 351 | [int]$cores=(wmic cpu get NumberOfCores)[2] 352 | if ($cores -gt 21) {$pme="--pme"; Write-Output "`r`n√ 检测到处理器核心数达22, 已添加x265参数: --pme"} 353 | 354 | $pools=poolscalc 355 | if ($pools -ne "") {Write-Output "`r`n√ 已添加x265参数: $pools"} 356 | 357 | Do {$PICKops=$x265ParWrap="" 358 | Switch (Read-Host "`r`n选择x265压制参数预设 [A: 通用-自定义 | B: 高压-录像 | C: 剪辑素材存档 | D: 高压-动漫字幕组 | E: HEDT-动漫BDRip冷战]") { 359 | a {$x265ParWrap=hevcparwrapper -PICKops "a"; Write-Output "`r`n√ 选择了通用-自定义预设"} 360 | b {$x265ParWrap=hevcparwrapper -PICKops "b"; Write-Output "`r`n√ 选择了高压-录像预设"} 361 | c {$x265ParWrap=hevcparwrapper -PICKops "c"; Write-Output "`r`n√ 选择了剪辑素材存档预设"} 362 | d {$x265ParWrap=hevcparwrapper -PICKops "d"; Write-Output "`r`n√ 选择了高压-动漫字幕组预设"} 363 | e {$x265ParWrap=hevcparwrapper -PICKops "e"; Write-Output "`r`n√ 选择了HEDT-动漫BDRip冷战预设"} 364 | default {Write-Warning "`r`n× 输入错误, 重试"} 365 | } 366 | } While ($x265ParWrap -eq "") 367 | Write-Output "√ 已定义x265压制参数: $x265ParWrap" 368 | } 369 | 370 | #「启动N」如果使用了支持Film grain optimization的x264, 则开启 371 | #Do {$x264fgo=$FGOops="" 372 | # Switch (Read-Host "选择x264 [A: 是 | B: 否] 支持基于高频信号量的率失真优化策略 (--fgo参数/Film grain optimization)注: AVC标准外") { 373 | # a {$FGOops="A";Write-Output "`r`n修改率失真优化策略"; $x264fgo="--fgo 15"} 374 | # b {$FGOops="B";Write-Output "`r`n保持率失真优化策略"; $x264fgo=""} 375 | # default {Write-Warning "`r`n× 输入错误, 重试"} 376 | # } 377 | #} While ($FGOops -eq "") 378 | 379 | Set-PSDebug -Strict 380 | $utf8NoBOM=New-Object System.Text.UTF8Encoding $false #导出utf-8NoBOM文本编码用 381 | 382 | #注: 导入路径: $impEXTm, 导入文件: $impEXTs 导出路径: $fileEXPpath 383 | #「初始化」$ffmpegPar(固定参数变量)的末尾不带空格 384 | #「限制」$ffmpegPar-ameters不能写在输入文件命令(-i)前面, ffmpeg参数 "-hwaccel"不能写在输入文件后面. 导致了字符串重组的流程变复杂, 及更多字符串变量的参与 385 | $ffmpegParA="$ffmpegCSP $fmpgfps -loglevel 16 -y -hide_banner -an -f yuv4mpegpipe -strict unofficial" #步骤2已添加pipe参数"- | -", 所以此处省略 386 | $ffmpegParB="$ffmpegCSP $fmpgfps -loglevel 16 -y -hide_banner -c:v copy" #生成临时mp4封装来兼容ffmpeg封装mkv用 387 | $vspipeParA="--y4m" 388 | $avsyuvParA="$avsCSP $avsD" 389 | $avsmodParA="`"$apmDLL`" -y4mp" #注: avs2pipemod使用"| -"而非其他工具的"- | -"pipe参数(左侧无"-"). y4mp, y4mt, y4mb代表逐行, 上场优先隔行, 下场优先隔行. 为了降低代码复杂度所以不做隔行 390 | $olsargParA="-c `"$iniEXP`" --pipe-out" #注: svfi不支持y4m pipe格式 391 | 392 | #「初始化」x264/5固定参数 393 | if ($IMPchk -eq "e") { 394 | $x265y4m=$x264y4m=""; Write-Output "√ 由于SVFI不支持yuv for mpeg pipe格式, 所以x264, x265参数设定为使用raw pipe格式" 395 | } else { 396 | $x265y4m="--y4m" 397 | $x264y4m="--demuxer y4m" #x264,x265的书写格式不同 398 | } 399 | $x265ParA="$encD $x265subme $color_mtx $trans_chrctr $fps $WxH $encCSP $pme $pools $keyint $x265ParWrap $x265y4m -" 400 | $x264ParA="$encD $avc_mtx $avc_tsf $fps $WxH $encCSP $keyint $x264ParWrap $x264y4m -" 401 | $x265ParA=$x265ParA -replace " ", " " #由于某些情况下只能生成空的参数变量, 所以会导致双空格出现, 但保留也不影响运行 402 | $x264ParA=$x264ParA -replace " ", " " 403 | 404 | #「初始化」ffmpeg, vspipe, avs2yuv, avs2pipemod, one_line_shot_args变化参数, 大批量版需要单独计算每个视频的文件名所以不能直接赋值 405 | if ($mode -eq "s") { 406 | $ffmpegVarA=$vspipeVarA=$avsyuvVarA=$avsmodVarA=$olsargVarA="-i `"$impEXTs`"" #上游变化参数 407 | $x265VarA=$x264VarA="$nbrframes --output `"$fileEXPpath$vidEXP$vidFMT`"" #下游变化参数 408 | } 409 | 410 | #「生成ffmpeg, vspipe, avs2yuv, avspipemod主控批处理」 411 | $ctrl_gen=" 412 | chcp 65001 413 | REM 「兼容 UTF-8文件名」弃用ANSI文本编码格式 414 | REM 「要求 变量回收」set+endlocal, 在编码bat中停止也触发清理 415 | REM UTF-8文本编码, 关闭命令输入显示, 5秒倒数 416 | 417 | @echo off 418 | timeout 5 419 | setlocal 420 | 421 | REM 「非正常退出时」用taskkill /F /IM cmd.exe /T才能清理打开的批处理, 否则重复使用可会乱码 422 | 423 | @echo 「Non-std exits」cleanup with `"taskkill /F /IM cmd.exe /T`" is necessary to prevent residual variable's presence from previously ran sripts. 424 | @echo. && @echo --Starting multi-batch-enc workflow v2-- 425 | 426 | REM 「ffmpeg debug」删-loglevel 16 427 | REM 「-thread_queue_size过小」加-thread_queue_size<每核心内存带宽Kbps>, 但最好换ffmpeg 428 | REM 「ffmpeg, vspipe, avsyuv, avs2pipemod固定参数」 429 | REM 修改为批量编码时,需要确认视频格式(如-pix_fmt,-r)不变,否则应运行步骤3另建一个主控 430 | 431 | @set `"ffmpegParA="+$ffmpegParA+"`" 432 | @set `"ffmpegParB="+$ffmpegParB+"`" 433 | @set `"vspipeParA="+$vspipeParA+"`" 434 | @set `"avsyuvParA="+$avsyuvParA+"`" 435 | @set `"avsmodParA="+$avsmodParA+"`" 436 | @set `"olsargParA="+$olsargParA+"`" 437 | 438 | REM 「ffmpeg, vspipe, avsyuv, avs2pipemod变化参数」 439 | REM 可以通过在对应线路增加@set `"ffmpegVarX=-i `"X:\视频2.mp4`"`"的命令来进行批量编码 440 | 441 | @set `"ffmpegVarA=-hwaccel auto "+$ffmpegVarA+"`" 442 | @set `"vspipeVarA="+$vspipeVarA+"`" 443 | @set `"avsyuvVarA="+$avsyuvVarA+"`" 444 | @set `"avsmodVarA="+$avsmodVarA+"`" 445 | @set `"olsargVarA="+$olsargVarA+"`" 446 | 447 | REM 「x264-5固定参数」 448 | REM 可以通过增加@set `"x264ParX=...`"的命令来进行批量编码 449 | 450 | @set `"x265ParA="+$x265ParA+"`" 451 | @set `"x264ParA="+$x264ParA+"`" 452 | 453 | REM 「x264-5变化参数」测试时注释掉 454 | 455 | @set `"x265VarA="+$x265VarA+"`" 456 | @set `"x264VarA="+$x264VarA+"`" 457 | 458 | REM 「debug与测试」平时注释掉, 末尾不加空格 459 | 460 | REM @set `"x265VarA=--crf 23 ... --output ...`" 461 | REM @set `"x265VarB=--crf 26 ... --output ...`" 462 | REM @set `"x264VarA=--crf 23 ... --output ...`" 463 | REM @set `"x264VarA=--crf 26 ... --output ...`" 464 | 465 | REM 「编码部分」用注释或删除编码批处理跳过不需要的编码 466 | REM 可以通过在对应线路增加call enc_x.bat的命令来进行批量的编码 467 | 468 | call enc_0S.bat 469 | 470 | REM 「最后」保留命令输入行, 用/k而非-k可略过输出Windows build号 471 | 472 | endlocal 473 | cmd -k" 474 | 475 | if ($IMPchk -eq "a") {$exptPath+="4A.S.「编码主控」.bat" 476 | } elseif ($IMPchk -eq "b") {$exptPath+="4B.S.「编码主控」.bat" 477 | } elseif ($IMPchk -eq "c") {$exptPath+="4C.S.「编码主控」.bat" 478 | } elseif ($IMPchk -eq "d") {$exptPath+="4D.S.「编码主控」.bat"} 479 | 480 | Write-Output "`r`n正在生成 $exptPath" 481 | [System.IO.File]::WriteAllLines($exptPath, $ctrl_gen, $utf8NoBOM) #强制导出utf-8NoBOM编码 482 | Write-Output 完成 483 | pause -------------------------------------------------------------------------------- /bbenc-source/Step 2-5 in CHS/5.「生成基于ffmpeg的单任务封装命令模板」.ps1: -------------------------------------------------------------------------------- 1 | cls #开发人员的Github: https://github.com/iAvoe 2 | Read-Host "本程序无法手动选择导入封装中的特定轨道. 否则用以下命令导出所有内容后, 再运行本程序逐一导入:`r`n`"ffmpeg -dump_attachment:<视频v/音频a/字幕s/字体t> `"导出文件名`" -i 导入.mkv`"`r`n按Enter继续..." 3 | 4 | Function namecheck([string]$inName) { 5 | $badChars = '[{0}]' -f [regex]::Escape(([IO.Path]::GetInvalidFileNameChars() -join '')) 6 | ForEach ($_ in $badChars) {if ($_ -match $inName) {return $false}} 7 | return $true 8 | } #检测文件名是否符合Windows命名规则, 大批量版不需要 9 | 10 | Function whereisit($startPath='DESKTOP') { 11 | #「启动」启用System.Windows.Forms选择文件的GUI交互窗 12 | [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") 13 | Add-Type -AssemblyName System.Windows.Forms 14 | $startPath = New-Object System.Windows.Forms.OpenFileDialog -Property @{ InitialDirectory = [Environment]::GetFolderPath($startPath) } #GUI交互窗锁定到桌面文件夹 15 | Do {$dInput = $startPath.ShowDialog()} While ($dInput -eq "Cancel") #打开选择文件的GUI交互窗, 通过重新打开选择窗来反取消用户的取消操作 16 | return $startPath.FileName 17 | } 18 | 19 | Function whichlocation($startPath='DESKTOP') { 20 | #启用System.Windows.Forms选择文件夹的GUI交互窗, 通过SelectedPath将GUI交互窗锁定到桌面文件夹, 效果一般 21 | Add-Type -AssemblyName System.Windows.Forms 22 | $startPath = New-Object System.Windows.Forms.FolderBrowserDialog -Property @{ Description="选择路径用的窗口. 拖拽边角可放大以便操作"; SelectedPath=[Environment]::GetFolderPath($startPath); RootFolder='MyComputer'; ShowNewFolderButton=$true } 23 | #打开选择文件的GUI交互窗, 用Do-While循环拦截误操作(取消/关闭选择窗) 24 | Do {$dInput = $startPath.ShowDialog()} While ($dInput -eq "Cancel") 25 | #由于选择根目录时路径变量含"\", 而文件夹时路径变量缺"\", 所以要自动判断并补上 26 | if (($startPath.SelectedPath.SubString($startPath.SelectedPath.Length-1) -eq "\") -eq $false) {$startPath.SelectedPath+="\"} 27 | return $startPath.SelectedPath 28 | } 29 | 30 | 31 | #3合1大型函数, 分流了甲: 封装文件, 乙: 特殊封装, 丙: 单文件流. 甲-乙用ffprobe测, 丙用GetExtension测, 拦截无关的文件格式, 最终以多线并发的方法生成ffmpeg的-map ?:?, -c:? copy命令 32 | Function addcopy { 33 | Param ([Parameter(Mandatory=$true)]$fprbPath, [Parameter(Mandatory=$true)]$StrmPath, [Parameter(Mandatory=$true)]$mapQTY,[Parameter(Mandatory=$true)]$vcopy) 34 | $DebugPreference="Continue" #function里不能用Write-Output/Host,或" "来输出交互信息, 而是修改Write-Debug的运行逻辑属性实现交互 35 | $result=@() 36 | $mrc=$vrc=$arc=$src=$trc=[IO.Path]::GetExtension($StrmPath) #过滤掉文件名, 只保留后缀, 防止文件名中含匹配值造成误匹配, 由于Get-ChildItem查不出.hevc所以放弃 37 | #「检测」视频+音频+字幕+字体封装检测规程 38 | if (($mrc -match "mkv") -or ($mrc -match "mp4") -or ($mrc -match "mov") -or 39 | ($mrc -match "f4v") -or ($mrc -match "flv") -or ($mrc -match "avi") -or 40 | ($mrc -match "m3u") -or ($mrc -match "mxv")) { 41 | Write-Debug "? 可能性甲: 视音频字幕字体用的封装格式`r`n" 42 | #「ffprobe」读取封装文件multiplexed file中的音频. 不管导入文件是否封装 43 | #有的片子删了codec_tag_string, 所以用codec_name做冗余 44 | $VProbe=$fprbPath+" -i '$StrmPath' -select_streams v -v error -hide_banner -show_entries stream=codec_name,codec_tag_string,avg_frame_rate:disposition=:tags= -of csv" 45 | Invoke-Expression $VProbe > "C:\temp_mv_info.csv" #ffprobe生成的csv中: stream,, 46 | $AProbe=$fprbPath+" -i '$StrmPath' -select_streams a -v error -hide_banner -show_entries stream=codec_name,codec_tag_string:disposition=:tags= -of csv" 47 | Invoke-Expression $AProbe > "C:\temp_ma_info.csv" #导入的csv中: A=stream,B=,C= 48 | $SProbe=$fprbPath+" -i '$StrmPath' -select_streams s -v error -hide_banner -show_entries stream=codec_name,codec_tag_string:disposition=:tags= -of csv" 49 | Invoke-Expression $SProbe > "C:\temp_ms_info.csv" 50 | $SProbe=$fprbPath+" -i '$StrmPath' -select_streams t -v error -hide_banner -show_entries stream=codec_name,codec_tag_string:disposition=:tags= -of csv" 51 | Invoke-Expression $SProbe > "C:\temp_mt_info.csv" 52 | 53 | if (Test-Path "C:\temp_mv_info.csv") {$Vcsv=Import-Csv "C:\temp_mv_info.csv" -Header A,B,C,D; Remove-Item "C:\temp_mv_info.csv"} #ffmpeg要求输入帧数, 否则会阻止封装为mkv 54 | if (Test-Path "C:\temp_ma_info.csv") {$Acsv=Import-Csv "C:\temp_ma_info.csv" -Header A,B,C; Remove-Item "C:\temp_ma_info.csv"} 55 | if (Test-Path "C:\temp_ms_info.csv") {$Scsv=Import-Csv "C:\temp_ms_info.csv" -Header A,B,C; Remove-Item "C:\temp_ms_info.csv"} 56 | if (Test-Path "C:\temp_mt_info.csv") {$Tcsv=Import-Csv "C:\temp_mt_info.csv" -Header A,B,C; Remove-Item "C:\temp_mt_info.csv"} #防止删除不存在的文件导致报错 57 | 58 | if (($Vcsv.C -match "0") -or ($Vcsv.C -eq "")) {Write-Debug "甲! 视频元数据codec_tag_string遭删, 切到备用值codec_name"; $vrc=$Vcsv.B} else {$vrc=$Vcsv.C} #检测code_name_tag是否给出了定义范围内的格式 59 | $vfc=$Vcsv.D 60 | if (($Acsv.C -match "0") -or ($Acsv.C -eq "")) {Write-Debug "甲! 音频元数据codec_tag_string遭删, 切到备用值codec_name"; $arc=$Acsv.B} else {$arc=$Acsv.C} 61 | if (($Scsv.C -match "0") -or ($Scsv.C -eq "")) {Write-Debug "甲! 字幕元数据codec_tag_string遭删, 切到备用值codec_name"; $src=$Scsv.B} else {$src=$Scsv.C} 62 | if (($Tcsv.C -match "0") -or ($Tcsv.C -eq "")) {Write-Debug "甲! 字体元数据codec_tag_string遭删, 切到备用值codec_name"; $trc=$Tcsv.B} else {$trc=$Tcsv.C} 63 | #Write-Debug "vrc: $vrc " 64 | #Write-Debug $vrc.GetType() 65 | if (($vrc -eq "") -or ($vrc -eq $null)) {Write-Debug "`r`n甲× 视频流 $vrc 不存在"} 66 | else { 67 | if (($vrc -match "hevc") -or ($vrc -match "h265") -or ($vrc -match "avc") -or 68 | ($vrc -match "h264") -or ($vrc -match "cfhd") -or ($vrc -match "ap4x") -or 69 | ($vrc -match "apcn") -or ($vrc -match "hev1") -or ($vrc -match "vp09") -or 70 | ($vrc -match "vp9")) { 71 | Write-Debug "`r`n甲√可封装视频流: $vrc" 72 | if (($vrc -match "ap4x") -or ($vrc -match "apcn")) {Write-Warning "检测到ProRes 422 / 4444XQ视频流. 仅MOV/QTFF, MXF封装格式支持"} 73 | if (($vrc -match "vp09") -or ($vrc -match "vp9")) {Write-Warning "检测到VP9视频流. 有MKV, MP4, *OGG, WebM等封装格式支持"} 74 | } else {Write-Warning "`r`n甲? 不认识/大概能封装视频流: $vrc"} 75 | if ($vcopy -eq "y") {$result+="-r $vfc -c:v copy "} else {Write-Warning "首个 -c:v copy 以及 -r 命令已被写入, 屏蔽了重复写入"} 76 | } 77 | #Write-Debug "arc: $arc" 78 | #Write-Debug $arc.GetType() 79 | if (($arc -eq "") -or ($arc -eq $null)) {Write-Debug "`r`n甲× 音频流 $arc 不存在"} 80 | else { 81 | if (($arc -match "aac") -or ($arc -match "ogg") -or ($arc -match "alac") -or 82 | ($arc -match "dts") -or ($arc -match "mp3") -or ($arc -match "wma") -or 83 | ($arc -match "wav") -or ($arc -match "pcm") -or ($arc -match "lpcm") -or 84 | ($arc -match "flac") -or ($arc -match "ape") -or ($arc -match "alac")) { 85 | Write-Debug "`r`n甲√可封装音频流: $arc" 86 | if ($arc -match "ape") {Write-Error "甲× 没有封装格式支持MonkeysAudio/APE音频"; pause; exit} 87 | if ($arc -match "flac") {Write-Warning "检测到FLAC音频, MOV/QTFF, MXF封装格式不支持"} 88 | if ($arc -match "alac") {Write-Warning "检测到ALAC音频, MXF封装格式不支持"} 89 | } else {Write-Warning "`r`n甲? 不认识/大概能封装音频流: $arc"} 90 | $result+="-c:a copy " 91 | } 92 | #Write-Debug "src: $src" 93 | #Write-Debug $src.GetType() 94 | if (($src -eq "") -or ($src -eq $null)) {Write-Debug "`r`n甲× 字幕轨 $src 不存在"} 95 | else { 96 | if (($src -match "srt") -or ($src -match "ass") -or ($src -match "ssa")) { 97 | Write-Debug "`r`n甲√可封装字幕轨: $src" 98 | if (($src -match "ass") -or ($src -match "ssa")) {Write-Warning "检测到ASS/SSA字幕, 唯独MKV封装格式支持"} 99 | } else {Write-Warning "`r`n甲? 不认识/大概能封装字幕轨: $src"} 100 | $result+="-c:s copy " 101 | } 102 | #Write-Debug "trc: $trc" 103 | #Write-Debug $trc.GetType() 104 | if (($trc -eq "") -or ($trc -eq $null)) {Write-Debug "`r`n甲× 字体轨 $trc 不存在"} 105 | else { 106 | if (($trc -match "ttf") -or ($trc -match "ttc") -or ($trc -match "otf")) {Write-Warning "`r`n甲! .mp4和.mov封装格式不支持字体轨 $trc"} 107 | $result+="-c:t copy " 108 | } 109 | 110 | if ($result.Count -lt 1) {Write-Error "甲× 失败: 输入了空的视音频+字幕字体封装文件"; pause; exit} 111 | return $result 112 | }elseif( 113 | ($mrc -match "m4a") -or ($mrc -match "mka") -or ($mrc -match "mks")) { 114 | Write-Debug "? 可能性乙: 非视频用的封装格式`r`n" 115 | $AProbe=$fprbPath+" -i '$StrmPath' -select_streams a -v error -hide_banner -show_entries stream=codec_name,codec_tag_string:disposition=:tags= -of csv" 116 | $SProbe=$fprbPath+" -i '$StrmPath' -select_streams s -v error -hide_banner -show_entries stream=codec_name,codec_tag_string:disposition=:tags= -of csv" 117 | $SProbe=$fprbPath+" -i '$StrmPath' -select_streams t -v error -hide_banner -show_entries stream=codec_name,codec_tag_string:disposition=:tags= -of csv" 118 | Invoke-Expression $AProbe > "C:\temp_na_info.csv" 119 | Invoke-Expression $SProbe > "C:\temp_ns_info.csv" 120 | Invoke-Expression $SProbe > "C:\temp_nt_info.csv" 121 | if (Test-Path "C:\temp_na_info.csv") {$Acsv=Import-Csv "C:\temp_na_info.csv" -Header A,B,C; Remove-Item "C:\temp_na_info.csv"} 122 | if (Test-Path "C:\temp_ns_info.csv") {$Scsv=Import-Csv "C:\temp_ns_info.csv" -Header A,B,C; Remove-Item "C:\temp_ns_info.csv"} 123 | if (Test-Path "C:\temp_nt_info.csv") {$Tcsv=Import-Csv "C:\temp_nt_info.csv" -Header A,B,C; Remove-Item "C:\temp_nt_info.csv"} 124 | 125 | if (($Acsv.C -match "0") -or ($Acsv.C -eq "")) {Write-Debug "乙! 音频元数据codec_tag_string遭删, 切到备用值codec_name"; $arc=$Acsv.B} else {$arc=$Acsv.C} 126 | if (($Scsv.C -match "0") -or ($Scsv.C -eq "")) {Write-Debug "乙! 字幕元数据codec_tag_string遭删, 切到备用值codec_name"; $src=$Scsv.B} else {$src=$Scsv.C} 127 | if (($Tcsv.C -match "0") -or ($Tcsv.C -eq "")) {Write-Debug "乙! 字体元数据codec_tag_string遭删, 切到备用值codec_name"; $trc=$Tcsv.B} else {$trc=$Tcsv.C} 128 | #Write-Debug "arc: $arc" 129 | #Write-Debug $arc.GetType() 130 | if (($arc -eq "") -or ($arc -eq $null)) {Write-Debug "`r`n乙× 音频流 $arc 不存在"} 131 | else { 132 | if (($arc -match "aac") -or ($arc -match "ogg") -or ($arc -match "alac") -or 133 | ($arc -match "dts") -or ($arc -match "mp3") -or ($arc -match "wma") -or 134 | ($arc -match "wav") -or ($arc -match "pcm") -or ($arc -match "lpcm") -or 135 | ($arc -match "flac") -or ($arc -match "ape") -or ($arc -match "alac")) { 136 | Write-Debug "`r`n乙√可封装音频流: $arc" 137 | if ($arc -match "ape") {Write-Error "β× 没有封装格式支持MonkeysAudio/APE音频"; pause; exit} 138 | if ($arc -match "flac") {Write-Warning "检测到FLAC音频. MOV/QTFF, MXF封装格式不支持"} 139 | if ($arc -match "alac") {Write-Warning "检测到ALAC音频, MXF封装格式不支持"} 140 | } else {Write-Warning "`r`n乙? 不认识/大概能封装音频流: $arc"} 141 | $result+="-c:a copy " 142 | } 143 | #Write-Debug "src: $src" 144 | #Write-Debug $src.GetType() 145 | if (($src -eq "") -or ($src -eq $null)) {Write-Debug "`r`n乙× 字幕轨: $src 不存在"} 146 | else { 147 | if (($src -match "srt") -or ($src -match "ass") -or ($src -match "ssa")) { 148 | Write-Debug "`r`n乙√可封装字幕轨: $src" 149 | if (($src -match "ass") -or ($src -match "ssa")) {Write-Warning "检测到ASS/SSA字幕, 唯独MKV封装格式支持"} 150 | } else {Write-Warning "`r`n乙? 不认识/大概能封装字幕轨 $src"} 151 | $result+="-c:s copy " 152 | } 153 | #Write-Debug "trc: $trc" 154 | #Write-Debug $trc.GetType() 155 | if (($trc -eq "") -or ($trc -eq $null)) {Write-Debug "`r`n乙× 字体轨 $trc 不存在"} 156 | else { 157 | if (($trc -match "ttf") -or ($trc -match "ttc") -or ($trc -match "otf")) {Write-Warning "`r`n乙! .mp4和.mov封装格式不支持字体轨 $trc"} 158 | $result+="-c:t copy " 159 | } 160 | 161 | if ($result.Count -lt 1) {Write-Error "乙× 失败: 输入了空的音频-字幕封装文件"; pause} 162 | return $result[$mapQTY] 163 | }else { Write-Debug "? 可能性丙: 未封装的单文件`r`n" 164 | if (($vrc -match "hevc") -or ($vrc -match "h265") -or ($vrc -match "avc") -or 165 | ($vrc -match "h264") -or ($vrc -match "cfhd") -or ($vrc -match "ap4x") -or 166 | ($vrc -match "apcn") -or ($vrc -match "hev1") -or ($vrc -match "vp09") -or 167 | ($vrc -match "vp9")) { 168 | $VProbe=$fprbPath+" -i '$StrmPath' -select_streams v -v error -hide_banner -show_entries stream=codec_name,codec_tag_string,avg_frame_rate:disposition=:tags= -of csv" 169 | Invoke-Expression $VProbe > "C:\temp_ov_info.csv" #ffprobe生成的csv中: stream,, 170 | if (Test-Path "C:\temp_ov_info.csv") {$Vcsv=Import-Csv "C:\temp_ov_info.csv" -Header A,B,C,D; Remove-Item "C:\temp_ov_info.csv"} 171 | $vfc=$Vcsv.D #ffmpeg要求输入帧数, 否则会阻止封装为mkv 172 | if ($vcopy -eq "y") {$result+="-r $vfc -c:v copy "} else {Write-Warning "首个 -c:v copy 以及 -r 命令已被写入, 屏蔽了重复写入"} 173 | if ((($MUXops -match "mkv") -and ($vrc -match "hevc")) -or (($MUXops -match "mkv") -and ($vrc -match "h265"))) {Write-Error "丙× ffmpeg不准导入hevc/h265单文件流到mkv(无时间戳错误), 先用本程序封装成MP4再封装MKV"; pause; exit} 174 | if ((($MUXops -match "mkv") -and ($vrc -match "hevc")) -or (($MUXops -match "mkv") -and ($vrc -match "h265"))) {Write-Error "丙× ffmpeg不准导入avc/h264单文件流到mkv(无时间戳错误), 先用本程序封装成MP4再封装MKV"; pause; exit} 175 | Write-Debug "`r`n丙√ $MUXops 可以封装视频流: $vrc`r`n" 176 | if (($vrc -match "ap4x") -or ($vrc -match "apcn")) {Write-Warning "检测到ProRes 422 / 4444XQ视频流. 仅MOV/QTFF, MXF封装格式支持"} 177 | if (($vrc -match "vp09") -or ($vrc -match "vp9")) {Write-Warning "检测到VP9视频流. 有MKV, MP4, *OGG, WebM等封装格式支持"} 178 | } elseif( 179 | ($arc -match "aac") -or ($arc -match "ogg") -or ($arc -match "alac") -or 180 | ($arc -match "dts") -or ($arc -match "mp3") -or ($arc -match "wma") -or 181 | ($arc -match "wav") -or ($arc -match ".pcm") -or ($arc -match ".lpcm") -or 182 | ($arc -match "flac") -or ($arc -match "ape") -or ($arc -match "alac")) { 183 | $result+="-c:a copy " 184 | Write-Debug "`r`n丙√ $MUXops 可以封装音频流: $arc`r`n" 185 | if ($arc -match "ape") {Write-Error "β× 没有封装格式支持MonkeysAudio/APE音频"; pause; exit} 186 | if ($arc -match "flac") {Write-Warning "检测到FLAC音频. MOV/QTFF, MXF封装格式不支持"} 187 | if ($arc -match "alac") {Write-Warning "检测到ALAC音频, MXF封装格式不支持"} 188 | } elseif( 189 | ($src -match "srt") -or ($src -match "ass") -or ($src -match "ssa")) { 190 | $result+="-c:s copy " 191 | Write-Debug "`r`n丙√ $MUXops 可以封装字幕轨: $src`r`n" 192 | if (($src -match "ass") -or ($src -match "ssa")) {Write-Warning "检测到ASS/SSA字幕, 唯独MKV封装格式支持"} 193 | } elseif( 194 | ($trc -match "ttf") -or ($trc -match "ttc") -or ($trc -match "otf")) { 195 | $result+="-c:t copy " 196 | Write-Warning "`r`n丙! 仅.mkv格式支持封装字体轨 $trc`r`n" 197 | } else{Write-Error "`r`n丙× 无法写入ffmpeg命令, 因为输入的单文件流不在mkv/mp4/mov/f4v/flv/avi, m4a/mka/mks, hevc/avc/cfhd/ap4x/apcn/vp9, aac/ogg/alac/dts/mp3/wma/pcm/lpcm/flac, srt/ass/ssa, ttf/ttc/otf范围内`r`n"; pause} 198 | return $result 199 | } 200 | } 201 | 202 | #「@MrNetTek」高DPI显示渲染模式的System.Windows.Forms 203 | Add-Type -TypeDefinition @' 204 | using System.Runtime.InteropServices; 205 | public class ProcessDPI { 206 | [DllImport("user32.dll", SetLastError=true)] 207 | public static extern bool SetProcessDPIAware(); 208 | } 209 | '@ 210 | $null = [ProcessDPI]::SetProcessDPIAware() 211 | 212 | Write-Output "用mediainfo打开封装文件, 选`"view`"即检测封装中的音-字幕轨格式. 仅音轨可以用: ffprobe -i [源] -select_streams a -v error -hide_banner -show_streams -of ini > `"X:\桌面\1.txt`"`r`n" 213 | Write-Output "导出封装中特定aac音轨(.mp4中常见的音轨格式):`r`nffmpeg -i [源] -vn -c:a:0 copy `"X:\文件夹\导出音频1.aac`" `r`nffmpeg -i [源] -vn -c:a:1 copy `"X:\文件夹\导出音频2.aac`" `r`nffmpeg -i [源] -vn -c:a:2 copy `"X:\文件夹\导出音频3.aac`"`r`n" 214 | Write-Output "导出封装中所有字幕轨, 根据字幕格式写后缀名:`r`nffmpeg -i [源] -vn -an -c:s:0 copy `"X:\文件夹\导出字幕1.ass`" `r`nffmpeg -i [源] -vn -an -c:s:1 copy `"X:\文件夹\导出字幕2.ass`" `r`nffmpeg -i [源] -vn -an -c:s:2 copy `"X:\文件夹\导出字幕3.ass`"`r`n" 215 | Write-Output "导出封装中多轨道并重新封装1:`r`nffmpeg -i [源] -c:v copy -c:a:0 copy -c:a:1 copy -c:a:0 copy -c:s:0 copy`"X:\文件夹\导出1.mkv`"`r`n" 216 | Write-Output "导出封装中多轨道并重新封装2:`r`nffmpeg -i [源] -i [源2] -i [源3] -i [源4] -map 1:v -c:v copy -map 2:a -c:a copy -map 2:s -c:s copy -map 3:a -c:a copy -map 3:s -c:s copy -map 4:a -c:a copy -map 4:s -c:s copy `"X:\文件夹\导出2.mkv`" `r`n" 217 | Write-Output "为降低程序复杂度而限制了支持的文件类型:`r`nmkv/mp4/mov/f4v/flv/avi, m4a/mka/mks, hevc/avc/cfhd/ap4x/apcn, aac/ogg/alac/dts/mp3/wma/pcm/lpcm, srt/ass/ssa. ttf/ttc/otf`r`n" 218 | Set-PSDebug -Strict 219 | 220 | #「启动A」找到所有需要的路径 221 | Read-Host "将打开[导出封装批处理]的路径选择窗, 可能会在窗口底层弹出. 按Enter继续" 222 | $exptPath=whichlocation 223 | Read-Host "将打开[导出最终封装结果]的路径选择窗, 可能会在窗口底层弹出. 按Enter继续" 224 | $muxPath=whichlocation 225 | Read-Host "将打开[定位ffprobe.exe]的选择窗. 按Enter继续" 226 | $fprbPath=whereisit 227 | 228 | #「启动B」初始化用于临时储存一个导入命令, 以及集成多个导入命令的两个参数 229 | $StrmIMPAgg=$StrmParAgg=""#Agg=aggregate 230 | Write-Warning "只有第一项导入的视频文件会保留(-c:v copy)命令, 以确保仅输出一个视频流到封装中`r`n" 231 | $keepAdd=$vcopy="y" #循环导入判断, 以及写入首个-c:v copy命令后关闭写入窗口的判断值 232 | $mapQTY=0 #ffmpeg -map参数的循环累计值, 只在一开始导入时初始化 233 | 234 | #「启动C」循环调用addcopy函数生成封装导入命令 235 | Read-Host "将打开[定位待封装源]的循环选择窗. 按Enter继续" 236 | While ($keepAdd -eq "y") { 237 | $StrmPath=whereisit 238 | $addcopy=addcopy -fprbPath $fprbPath -StrmPath $StrmPath -mapQTY $mapQTY -vcopy $vcopy 239 | if ($addcopy -match "-c:v copy") {$vcopy="n"} #第一个源必须是视频, 然后通过$vcopy开关关闭"-c:v copy"命令的生成 240 | 241 | $StrmIMPAgg+=" -i `"$StrmPath`"" #由于ffmpeg要求多文件导入必须添加map参数, 而-map参数要求必须分割每个-c:? copy所做的参数顺序重构工作 242 | $StrmParAgg+="-map "+$mapQTY+" $addcopy" #随循环累计变多, 注意ffmpeg不支持单引号 243 | 244 | $mapQTY+=1 245 | Do {$keepAdd=Read-Host "`r`n√ 已生成ffmpeg导入参数: $StrmParAgg `r`n`r`n选择[n结束 | y继续]导入文件" 246 | if ((($keepAdd -eq "y") -eq $false) -and (($keepAdd -eq "n") -eq $false)) {Write-Warning "未输入y或n"} 247 | } While ((($keepAdd -eq "y") -eq $false) -and (($keepAdd -eq "n") -eq $false)) 248 | } 249 | 250 | $nameIn=[IO.Path]::GetFileNameWithoutExtension($StrmPath) #初始化导出封装结果用的文件名 251 | $nameIn+="_tempMux" 252 | Switch (Read-Host "`r`n 选择[A: 输入文件名 | B: $nameIn]") { 253 | a {Do {Do {$nameIn = (Read-Host -Prompt "`r`n输入[文件名](无后缀, 如 [Zzz] Memories – 01 (BDRip 1764x972 HEVC))") 254 | $nameCheck = namecheck($nameIn) 255 | if ($nameCheck -eq $false) {Write-Output "文件名违反Windows命名规则(含/ | \ < > : ? * `")"} 256 | elseif ($nameIn -eq 'y') {Write-Output "为防止误操作, 拦截了输出文件名: y"} 257 | } While ($nameCheck -eq $false) 258 | } While ($nameIn -eq 'y') 259 | Write-Output "√ 文件名符合Windows命名规则`r`n"} #关闭选项A 260 | b {Write-Output "选择了默认文件名"} 261 | default {Write-Output "选择了默认文件名(输入了空值)"} 262 | } 263 | 264 | #选择封装方案. 由于未来要添加更多导入方案, 所以导入变量会重复出现在多个选项里 265 | Do {$MUXops="" 266 | Switch (Read-Host "`r`n选择封装工具+格式:`r`n[A: ffmpeg+MP4 (视音频, 独轨字幕) | B: ffmpeg+MOV (视音频) | C: ffmpeg+MKV (视音字幕字体) | D: ffmpeg+MXF (视音字幕)]") { 267 | a {$MUXops=".mp4"; Write-Output "`r`n选择了MP4 - 线路A. 已打开[定位ffmpeg.exe]的选择窗`r`n"; $fmpgPath=whereisit} 268 | b {$MUXops=".mov"; Write-Output "`r`n选择了MOV - 线路B. 已打开[定位ffmpeg.exe]的选择窗`r`n"; $fmpgPath=whereisit} 269 | c {$MUXops=".mkv"; Write-Output "`r`n选择了MKV - 线路C. 已打开[定位ffmpeg.exe]的选择窗`r`n"; $fmpgPath=whereisit} 270 | d {$MUXops=".mxf"; Write-Output "`r`n选择了MXF - 线路D. 已打开[定位ffmpeg.exe]的选择窗`r`n"; $fmpgPath=whereisit} 271 | default {Write-Warning "输入错误, 重试"} 272 | } 273 | } While ($MUXops -eq "") 274 | Write-Output "√ 输入了 $fmpgPath`r`n" 275 | 276 | #D:\ffmpeg.exe -i "D:\视频.mkv" -i "D:\字幕.srt" -i "D:\视频.mp4" -map 0:v -r 25 -c:v copy -map 0:a -c:a copy -map 0:s -c:s copy -map 0:t -c:t copy -map 1:s -c:s copy -map 2:v -r 24000/1000 -c:v copy "D:\文件夹\输出.mkv" 277 | #$fmpgPath---" "-$StrmIMPAgg------------------------------------" "$StrmParAgg-------------------------------------------------------------------------------------------------------------------------------" "-$muxPath+$nameIn+$MUXops 278 | $mux_gen=$fmpgPath+$StrmIMPAgg+" "+$StrmParAgg+" "+"`""+$muxPath+$nameIn+$MUXops+"`"" #注: ffmpeg禁止命令中含单引号 279 | 280 | #拦截+处理与封装不匹配的命令 281 | if ((($MUXops -eq ".mp4") -or ($MUXops -eq ".mov") -or ($MUXops -eq ".mxf")) -and ($mux_gen -match "-c:t copy")) { 282 | Switch (Read-Host "选择了MP4/MOV/MXF封装, 但命令行中有拷贝字体-c:t copy的命令.`r`n选择[A: 删除命令 | B: 换MKV封装 | C: 跳过(不推荐)]") { 283 | a {$mux_gen -replace "-c:t copy", ""; Write-Output "已删除"} 284 | b {$MUXops=".mkv"; Write-Output "已更改"} 285 | c {Write-Output 跳过} 286 | } 287 | } 288 | 289 | if ((($MUXops -eq ".mp4") -or ($MUXops -eq ".mov")) -and ($mux_gen -match "-c:s copy")) { 290 | Switch (Read-Host "选择了MP4/MOV封装, 但命令行中有拷贝多轨字幕-c:s copy的命令.`r`n选择[A: 删除命令 | B: 换MKV封装 | C: 跳过(不推荐)] | D: 选择单轨字幕并转码为-c:s:0 mov_text(成功概率低, 如果有多个-c:s copy命令则需要手动去重)") { 291 | a {$mux_gen -replace "-c:s copy", ""; Write-Output "已删除"} 292 | b {$MUXops=".mkv"; Write-Output "已更改"} 293 | c {Write-Output 跳过} 294 | d {$mux_gen -replace "-c:s copy", "-c:s:0 mov_text"; Write-Output "已更改"} 295 | } 296 | } 297 | 298 | if (($mux_gen -match "-c:v copy") -eq $false) {Write-Warning "总结出的命令中不含-c:v copy. 封装结果将不含视频"} 299 | if (($mux_gen -match "-c:a copy") -eq $false) {Write-Warning "总结出的命令中不含-c:a copy. 封装结果将不含音频"} 300 | 301 | $utf8NoBOM=New-Object System.Text.UTF8Encoding $false #导出utf-8NoBOM文本编码hack 302 | $exptPath+="6S.「封装命令」.bat.txt" 303 | Write-Output "`r`n 正在生成 6S.「封装命令」.bat.txt`r`n" 304 | [System.IO.File]::WriteAllLines($exptPath, $mux_gen, $utf8NoBOM) #强制导出utf-8NoBOM编码 305 | 306 | Write-Output "封装命令会故意生成.txt以引导用户先打开并移除不需要的-c:v copy, -c:a copy, -c:s copy, -c:t copy等参数, 或改成-c:a:0 copy这样的定位参数来筛选特定流`r`n如果音频/字幕没有与视频对齐, 则在对应的-map -c两个命令之间添加-itoffset<+/-秒.毫秒>" 307 | pause -------------------------------------------------------------------------------- /bbenc-source/Step 2-5 in ENG/2.「Generate vessel for multiple encodes」.ps1: -------------------------------------------------------------------------------- 1 | cls #Dev's Github: https://github.com/iAvoe 2 | $mode="m" #Multiple encoding mode 3 | Function badinputwarning {Write-Warning "`r`n× Bad input, try again"} 4 | Function nosuchrouteerr {Write-Error "`r`n× No such route, try again"} 5 | Function nonintinputerr {Write-Error "`r`n× Input was not an integer"} 6 | Function tmpmuxreminder {return "x265 downstream supports .hevc output only. If you are multiplexing .mkv, then a .mp4 multiplexing is needed due to ffmpeg's restriction`r`n"} 7 | Function modeparamerror {Write-Error "`r`n× Crash: Variable `$mode broken, unable to distingulish operating mode"; pause; exit} 8 | Function modeimpextopserr{Write-Error "`r`n× Crash: `$mode, `$impOps or `$extOPS has an unidentifible or missing value"; pause; exit} 9 | Function skip {return "`r`n. Skipped"} 10 | Function namecheck([string]$inName) { 11 | $badChars = '[{0}]' -f [regex]::Escape(([IO.Path]::GetInvalidFileNameChars() -join '')) 12 | ForEach ($_ in $badChars) {if ($_ -match $inName) {return $false}} 13 | return $true 14 | } #Checking if input filename compliants to Windows file naming scheme, not needed in multiple encoding mode 15 | 16 | Function whereisit($startPath='DESKTOP') { 17 | #Opens a System.Windows.Forms GUI to pick a file 18 | [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") 19 | Add-Type -AssemblyName System.Windows.Forms 20 | $startPath = New-Object System.Windows.Forms.OpenFileDialog -Property @{ InitialDirectory = [Environment]::GetFolderPath('DESKTOP') }#Starting path set to Desktop 21 | Do {$dInput = $startPath.ShowDialog()} While ($dInput -eq "Cancel") #Opens a file selection window, re-open until receive inputs, 2.Window is big enough, TopMost OFF 22 | return $startPath.FileName 23 | } 24 | 25 | Function whichlocation($startPath='DESKTOP') { 26 | #Opens a System.Windows.Forms GUI to pick a folder/path/dir 27 | Add-Type -AssemblyName System.Windows.Forms 28 | $startPath = New-Object System.Windows.Forms.FolderBrowserDialog -Property @{ Description="Select a directory. Drag bottom corner to enlarge for convenience"; SelectedPath=[Environment]::GetFolderPath($startPath); RootFolder='MyComputer'; ShowNewFolderButton=$true } 29 | #Intercepting failed inputs (user presses close/cancel button) with Do-While looping 30 | Do {$dInput = $startPath.ShowDialog((New-Object System.Windows.Forms.Form -Property @{TopMost=$true}))} While ($dInput -eq "Cancel") #1. Opens a path selection window. 2.Window is too small, TopMost ON 31 | #Root directory always have a "\" in return, whereas a folder/path/dir doesn't. Therefore an if statement is used to add "\" when needed, but comment out under single-encode mode 32 | if (($startPath.SelectedPath.SubString($startPath.SelectedPath.Length-1) -eq "\") -eq $false) {$startPath.SelectedPath+="\"} 33 | return $startPath.SelectedPath 34 | } 35 | 36 | #「@MrNetTek」Use high-DPI rendering, to fix blurry System.Windows.Forms 37 | Add-Type -TypeDefinition @' 38 | using System.Runtime.InteropServices; 39 | public class ProcessDPI { 40 | [DllImport("user32.dll", SetLastError=true)] 41 | public static extern bool SetProcessDPIAware(); 42 | } 43 | '@ 44 | $null = [ProcessDPI]::SetProcessDPIAware() 45 | 46 | Set-PSDebug -Strict 47 | Write-Output "ffmpeg -i [input] -an -f yuv4mpegpipe -strict unofficial - | x265.exe - --y4m --output`r`n" 48 | Write-Output "ffmpeg video scaling: -sws_flags <+full_chroma_int +full_chroma_inp +accurate_rnd>" 49 | Write-Output "ffmpeg .ass rendring: -filter_complex `"ass=`'F\:/Subtitle.ass`'`"" 50 | Write-Output "ffmpeg convert from variable to constant framt rate: -vsync cfr`r`n" 51 | Write-Output "Encode interlaced source with x265: --tff/--bff; x264: --interlaced`r`n" 52 | Write-Output "VSpipe [.vpy] --y4m - | x265.exe --y4m - --output" 53 | Write-Output "avs2yuv [.avs] -csp -depth - | x265.exe --input-res --fps - --output" 54 | Write-Output "avs2pipemod [.avs] -y4mp | x265.exe --y4m - --output <>`r`n" 55 | 56 | #「Bootstrap A」Generate 1~n amount of "enc_[numbers].bat". Not needed in singular encode mode 57 | if ($mode -eq "m") { 58 | [array]$validChars='A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z' 59 | [int]$qty=0 #Start counting from 0 instead of 1 60 | Do {[int]$qty = (Read-Host -Prompt "Specify the amount of [generated encoding batches] (1~15625)") 61 | if ($qty -eq 0) {nonintinputerr} elseif ($qty -gt 15625) {Write-Error "× Greater than 15625 individual encodes"} 62 | } While (($qty -eq 0) -or ($qty -gt 15625)) 63 | #「Bootstrap B」Locate path to export batch files 64 | if ($qty -gt 9) {#Skip questionare for single digit $qtys 65 | Do {[string]$leadCHK=""; [int]$ldZeros=0 66 | Switch (Read-Host "Choose [y|n] to [add leading zeros] on exporting filename's episode counter. E.g., use 01, 02... for 2-digit episodes") { 67 | y {$leadCHK="y"; Write-Output "√ enable leading 0s`r`n"; $ldZeros=$qty.ToString().Length} 68 | n {$leadCHK="n"; Write-Output "× disable leading 0s`r`n"} 69 | default {badinputwarning} 70 | } 71 | } While ($leadCHK -eq "") 72 | [string]$zroStr="0"*$ldZeros #Gaining '000' protion for ".ToString('000')" method. $zroStr would be 0 if leading zero feature is deactivated, the calculation still haves but takes no effect 73 | } else {[string]$zroStr="0"} 74 | } 75 | #「Bootstrap C」Locate path to export batch files, distingulishing single & multiple encoding mode is needed 76 | Read-Host "`r`n[Enter] proceed open a window that locates [path for exporting batch files]..." 77 | if ($mode -eq "s") {$bchExpPath = (whichlocation)+"enc_0S.bat"} 78 | elseif ($mode -eq "m") {$bchExpPath = (whichlocation)+'enc_$s.bat'} #Under multiple encding mode, using single quote on var $s 79 | else {modeparamerror} 80 | Write-Output "`r`n√ Path & filename generated as: $bchExpPath" 81 | 82 | #「Bootstrap D」Loop importing pipe up-downstream programs 83 | $fmpgPath=$vprsPath=$avsyPath=$avspPath=$svfiPath=$x265Path=$x264Path="" 84 | Do {Do { 85 | Switch (Read-Host "`r`nImport upstream program [A: ffmpeg | B: vspipe | C: avs2yuv | D: avs2pipemod | E: SVFI], repeated selection will trigger a skip") { 86 | a {if ($fmpgPath -eq "") {Write-Output "`r`nffmpeg------up route A. Opening a window to [locate ffmpeg.exe]"; $fmpgPath=whereisit} else {skip}} 87 | b {if ($vprsPath -eq "") {Write-Output "`r`nvspipe------up route B. Opening a window to [locate vspipe.exe]"; $vprsPath=whereisit} else {skip}} 88 | c {if ($avsyPath -eq "") {Write-Output "`r`navs2yuv-----up route C. Opening a window to [locate avs2yuv.avs]"; $avsyPath=whereisit} else {skip}} 89 | d {if ($avspPath -eq "") {Write-Output "`r`navs2pipemod-up route D. Opening a window to [locate avs2pipemod.exe]"; $avspPath=whereisit} else {skip}} 90 | e {if ($svfiPath -eq "") {Write-Output "`r`nsvfi--------up route E. Opening a window to [locate one_line_shot_args.exe]";$svfiPath=whereisit} else {skip}} 91 | default {badinputwarning} 92 | } 93 | } While ($fmpgPath+$vprsPath+$avsyPath+$avspPath+$svfiPath -eq "") 94 | Do { 95 | Switch (Read-Host "`r`nImport downstream program [A: x265/hevc | B: x264/avc], repeated selection will trigger a skip") { 96 | a {if ($x265Path -eq "") {Write-Output "`r`nx265--------down route A. Opening a window to [locate x265.exe]"; $x265Path=whereisit} else {skip}} 97 | b {if ($x264Path -eq "") {Write-Output "`r`nx264--------down route B. Opening a window to [locate x264.exe]"; $x264Path=whereisit} else {skip}} 98 | default {badinputwarning} 99 | } 100 | } While ($x265Path+$x264Path -eq "") 101 | if ((Read-Host "`r`n√ [Enter] import more routes (recommneded); Or [y][Enter] to move on") -eq "y") {$impEND="y"} else {$impEND="n"} #User decides when to exit loop 102 | } While ($impEND -eq "n") 103 | #Generate a datatable to indicate imported programs 104 | $updnTbl = New-Object System.Data.DataTable 105 | $availRts= [System.Data.DataColumn]::new("Routes") 106 | $upColumn= [System.Data.DataColumn]::new("\UNIX Pipe Upstream") 107 | $dnColumn= [System.Data.DataColumn]::new("\UNIX Pipe Dnstream") 108 | $updnTbl.Columns.Add($availRts); $updnTbl.Columns.Add($upColumn); $updnTbl.Columns.Add($dnColumn) 109 | [void]$updnTbl.Rows.Add(" A:",$fmpgPath,$x265Path); [void]$updnTbl.Rows.Add(" B:",$vprsPath,$x264Path) 110 | [void]$updnTbl.Rows.Add(" C:",$avsyPath,""); [void]$updnTbl.Rows.Add(" D:",$avspPath,""); [void]$updnTbl.Rows.Add(" E:",$svfiPath,"") 111 | ($updnTbl | Out-String).Trim() #1. Trim used to trim out empty rows, 2. Piping to Out-String to force $updnTbl to return the result before Read-Host below gets executed 112 | 113 | Read-Host "`r`nCheck whether the correct programs are imported and [Enter] to proceed, restart otherwise" 114 | 115 | #「Bootstrap E」Choose up-downstream program for the encoding commandline, generates impOPS, extOPS variable for route selection parameter 116 | $impOPS=$extOPS="" 117 | Do {Switch (Read-Host "Choose an upstream program for encoding [A | B | C | D | E], the rest will be commented out in generated batch") { 118 | a {if ($fmpgPath -ne "") {$impOPS="a"} else {nosuchrouteerr}} 119 | b {if ($vprsPath -ne "") {$impOPS="b"} else {nosuchrouteerr}} 120 | c {if ($avsyPath -ne "") {$impOPS="c"} else {nosuchrouteerr}} 121 | d {if ($avspPath -ne "") {$impOPS="d"} else {nosuchrouteerr}} 122 | e {if ($svfiPath -ne "") {$impOPS="e"} else {nosuchrouteerr}} 123 | default {badinputwarning} 124 | } 125 | if ($impOPS -ne "") {#No-execusion when upstream input has failed, move to loop end and trigger a loopback instead 126 | Switch (Read-Host "`r`nChoose a downstream program for encoding [A | B], the rest will be commented out in generated batch") { 127 | a {if ($x265Path -ne "") {$extOPS="a"} else {nosuchrouteerr}} 128 | b {if ($x264Path -ne "") {$extOPS="b"} else {nosuchrouteerr}} 129 | default {badinputwarning} 130 | } 131 | } 132 | } While (($impOPS -eq "") -or ($extOPS -eq "")) 133 | 134 | #「Bootstrap F」Use impOPS, extOPS to generate the selected route as new variable keyRoute 135 | $keyRoute=""; $sChar="AAA" #An accident-proof measure where in bootstrap F, G expands $sChar too early, which may cause empty value error 136 | Switch ($mode+$impOps+$extOPS) { 137 | saa {$keyRoute="$fmpgPath %ffmpegVarA% %ffmpegParA% - | $x265Path %x265ParA% %x265VarA%"} #ffmpeg+x265+single 138 | sab {$keyRoute="$fmpgPath %ffmpegVarA% %ffmpegParA% - | $x264Path %x264ParA% %x264VarA%"} #ffmpeg+x264+single 139 | sba {$keyRoute="$vprsPath %vspipeVarA% %vspipeParA% - | $x265Path %x265ParA% %x265VarA%"} #VSPipe+x265+single 140 | sbb {$keyRoute="$vprsPath %vspipeVarA% %vspipeParA% - | $x264Path %x264ParA% %x264VarA%"} #VSPipe+x264+single 141 | sca {$keyRoute="$avsyPath %avsyuvVarA% %avsyuvParA% - | $x265Path %x265ParA% %x265VarA%"} #AVSYUV+x265+single 142 | scb {$keyRoute="$avsyPath %avsyuvVarA% %avsyuvParA% - | $x264Path %x264ParA% %x264VarA%"} #AVSYUV+x264+single 143 | sda {$keyRoute="$avspPath %avsmodVarA% %avsmodParA% | $x265Path %x265ParA% %x265VarA%"} #AVSPmd+x265+single, No "-" in AVSPipeMod upstream 144 | sdb {$keyRoute="$avspPath %avsmodVarA% %avsmodParA% | $x264Path %x264ParA% %x264VarA%"} #AVSPmd+x264+single, No "-" in AVSPipeMod upstream 145 | sea {$keyRoute="$svfiPath %olsargVarA% %olsargParA% - | $x265Path %x265ParA% %x265VarA%"} #OLSARG+x265+single 146 | seb {$keyRoute="$svfiPath %olsargVarA% %olsargParA% - | $x264Path %x264ParA% %x264VarA%"} #OLSARG+x264+single 147 | maa {$keyRoute="$fmpgPath %ffmpegVarA% %ffmpegParA% - | $x265Path %x265ParA%"+' %x265Var$sChar%'} #ffmpeg+x265+multiple, single quoting to yield expansion 148 | mab {$keyRoute="$fmpgPath %ffmpegVarA% %ffmpegParA% - | $x264Path %x264ParA%"+' %x264Var$sChar%'} #ffmpeg+x264+multiple 149 | mba {$keyRoute="$vprsPath %vspipeVarA% %vspipeParA% - | $x265Path %x265ParA%"+' %x265Var$sChar%'} #VSPipe+x265+multiple 150 | mbb {$keyRoute="$vprsPath %vspipeVarA% %vspipeParA% - | $x264Path %x264ParA%"+' %x264Var$sChar%'} #VSPipe+x264+multiple 151 | mca {$keyRoute="$avsyPath %avsyuvVarA% %avsyuvParA% - | $x265Path %x265ParA%"+' %x265Var$sChar%'} #AVSYUV+x265+multiple 152 | mcb {$keyRoute="$avsyPath %avsyuvVarA% %avsyuvParA% - | $x264Path %x264ParA%"+' %x264Var$sChar%'} #AVSYUV+x264+multiple 153 | mda {$keyRoute="$avspPath %avsmodVarA% %avsmodParA% | $x265Path %x265ParA%"+' %x265Var$sChar%'} #AVSPmd+x265+multiple, No "-" in AVSPipeMod upstream 154 | mdb {$keyRoute="$avspPath %avsmodVarA% %avsmodParA% | $x264Path %x264ParA%"+' %x264Var$sChar%'} #AVSPmd+x264+multiple, No "-" in AVSPipeMod upstream 155 | mea {$keyRoute="$svfiPath %olsargVarA% %olsargParA% - | $x265Path %x265ParA%"+' %x265Var$sChar%'} #OLSARG+x265+multiple 156 | meb {$keyRoute="$svfiPath %olsargVarA% %olsargParA% - | $x264Path %x264ParA%"+' %x264Var$sChar%'} #OLSARG+x264+multiple 157 | Default {modeimpextopserr} 158 | } 159 | #「Bootstrap G」Generate all possible possible upstream--downstream commandline layouts, which are the alternate routes 160 | [array] $upPipeStr=@("$fmpgPath %ffmpegVarA% %ffmpegParA%", "$vprsPath %vspipeVarA% %vspipeParA%", "$avsyPath %avsyuvVarA% %avsyuvParA%", "$avspPath %avsmodVarA% %avsmodParA%","$svfiPath %olsargVarA% %olsargParA%") | Where-Object {$_.Length -gt 26} 161 | Switch ($mode) {#Filter the non-existant route in dnPipeStr with length property, multile encoding mode has downstream commandline with $sChar variable which is 1 character less the nsingle encode mode, make use of the if split 162 | s {[array]$dnPipeStr=@( "$x265Path %x265ParA% %x265VarA%", "$x264Path %x264ParA% %x264VarA%") | Where-Object {$_.Length -gt 22}} 163 | m {[array]$dnPipeStr=@(("$x265Path%x265ParA%"+' %x265Var$sChar%'), ("$x264Path %x264ParA%"+' %x264Var$sChar%')) | Where-Object {$_.Length -gt 23}} #single quoting to yield expansion, extra () are used to prevent "+" & "," mixing up in Array 164 | Default {modeparamerror} 165 | } 166 | [array]$altRoute=@() #Commenting sign + `$updnPipeStr = altRoute. Thus generates all the alternate routes 167 | for ($x=0; $x -lt ($upPipeStr.Length); $x++) {#upstream/horizontal iteration of possibilities 168 | for ($y=0; $y -lt ($dnPipeStr.Length); $y++) {#downstream/vertical iteration of possibilities 169 | if ($upPipeStr -notlike "avsmod") {$altRoute+="REM "+$upPipeStr[$x]+" - | "+$dnPipeStr[$y]} #No "-" in AVSPipeMod upstream 170 | else {$altRoute+="REM "+$upPipeStr[$x]+" | "+$dnPipeStr[$y]} #No "-" in AVSPipeMod upstream 171 | } 172 | } 173 | "√ Number of usable/alternate routes are: "+($altRoute.Count.ToString()) | Out-String #Variable `$keyRoute & `$altRoute are ready, only differs in single & multiple mode where variable expansion is needed in multiple encoding mode 174 | 175 | if ($extOPS="a") {tmpmuxreminder} #Provide reminder for multiplexing to .mkv when selecting x265/hevc donwstream keyRoute 176 | 177 | #「Bootstrap H.m」3 dimension axis placed in for-loop realized with $validChars[x]+$validChars[y]+$validChars[z] 178 | #Simulated mathmatical carrying by: +1 to x-axis, +1 to y-axis & clear x after x-axis gets filled up; +1 to z-axis & clear x&y after filling up y-axis. 179 | [int]$x=[int]$y=[int]$z=0 180 | $utf8NoBOM=New-Object System.Text.UTF8Encoding $false #Enfore the output of UTB-8NoBom 181 | 182 | #Iteration begins, carry as any axis reaches letter 27. Switch occupies temp-variable $_ which cannot be used to initialize this loop. Counts as a 3-digit twenty-hexagonal 183 | For ($s=0; $s -lt $qty; $s++) { 184 | #$x+=1 is commented out at beginning as values are being parsed to filenames, therefore placed at the trailing of loop 185 | if ($x -gt 25) {$y+=1; $x=0} 186 | if ($y -gt 25) {$z+=1; $y=$x=0} 187 | [string]$sChar=$validChars[$z]+$validChars[$y]+$validChars[$x] #Start constructing variable $sChar in this loop 188 | #Due to the delection of (redundant) feature to generate temporary multiplexed container, variable $serial is no longer needed and therefore deleted 189 | #keyRoute & altRoute with collapsed $sChar will be expanded at lower printing statement 190 | #When expanding $sChar variable, Array would generate a multi-line text without line change, therefore a pipe to Out-String and then activatng the $sChar variable is needed, multiple encode only 191 | $banner = "-----------Starting encode "+$sChar+"-----------" 192 | Write-Output " Generating enc_$s.bat" 193 | 194 | $enc_gen="REM 「Title」 195 | @echo. 196 | @echo "+$banner+" 197 | 198 | REM 「Debug section」Comment out during normal usage 199 | REM @echo %ffmpegParA% %ffmpegVar"+$sChar+"% 200 | REM @echo %vspipeParA% %vspipeVar"+$sChar+"% 201 | REM @echo %avsyuvParA% %avsyuvVar"+$sChar+"% 202 | REM @echo %avsmodParA% %avsmodVar"+$sChar+"% 203 | REM @echo %olsargParA% %olsargVar"+$sChar+"% 204 | REM @echo %x265ParA% %x265Var"+$sChar+"% 205 | REM @echo %x264ParA% %x264Var"+$sChar+"% 206 | REM pause 207 | 208 | REM 「Encode-KeyRoutes」Comment out during debugging 209 | REM Var is used to specify dynamic values such as input-output, per-video encoding options 210 | 211 | "+$ExecutionContext.InvokeCommand.ExpandString(($keyRoute | Out-String))+" 212 | 213 | REM 「Encode-ALTRoutes」Copy and replace from lower to upper commandline wihtout REM commenting to change encoding programs 214 | 215 | "+$ExecutionContext.InvokeCommand.ExpandString(($altRoute | Out-String))+" 216 | 217 | REM Choose「y:Continue/n:Pause/z:END」Auto-continue after 5s, false input are blocked by choice statement, pause allows continue. 218 | 219 | choice /C YNZ /T 5 /D Y /M `" Continue? (Sleep=5; Default: Y, Pause: N, Stop: Z)`" 220 | 221 | if %ERRORLEVEL%==3 cmd /k 222 | if %ERRORLEVEL%==2 pause 223 | if %ERRORLEVEL%==1 endlocal && exit /b" 224 | 225 | #Out-File -InputObject $enc_gen -FilePath $bchExpPath -Encoding utf8 226 | if ($mode -eq "m") {[IO.File]::WriteAllLines($ExecutionContext.InvokeCommand.ExpandString($bchExpPath), $enc_gen, $utf8NoBOM)}#Expanding variable $s is needed in multiple encoding mode 227 | elseif ($mode -eq "s") {[IO.File]::WriteAllLines($bchExpPath, $enc_gen, $utf8NoBOM)} 228 | else {modeparamerror} 229 | $x+=1 230 | }#Closing For-loop 231 | 232 | Write-Output "Completed, as long as up-downstream program doesn't update, any controller batch generated by step 3 could always use enc_0S.bat / enc_X.bat" 233 | pause -------------------------------------------------------------------------------- /bbenc-source/Step 2-5 in ENG/2.「Generate vessel for singular encode」.ps1: -------------------------------------------------------------------------------- 1 | cls #Dev's Github: https://github.com/iAvoe 2 | $mode="s" #Signular encoding mode 3 | Function badinputwarning {Write-Warning "`r`n× Bad input, try again"} 4 | Function nosuchrouteerr {Write-Error "`r`n× No such route, try again"} 5 | Function nonintinputerr {Write-Error "`r`n× Input was not an integer"} 6 | Function tmpmuxreminder {return "x265 downstream supports .hevc output only. If you are multiplexing .mkv, then a .mp4 multiplexing is needed due to ffmpeg's restriction`r`n"} 7 | Function modeparamerror {Write-Error "`r`n× Crash: Variable `$mode broken, unable to distingulish operating mode"; pause; exit} 8 | Function modeimpextopserr{Write-Error "`r`n× Crash: `$mode, `$impOps or `$extOPS has an unidentifible or missing value"; pause; exit} 9 | Function skip {return "`r`n. Skipped"} 10 | Function namecheck([string]$inName) { 11 | $badChars = '[{0}]' -f [regex]::Escape(([IO.Path]::GetInvalidFileNameChars() -join '')) 12 | ForEach ($_ in $badChars) {if ($_ -match $inName) {return $false}} 13 | return $true 14 | } #Checking if input filename compliants to Windows file naming scheme, not needed in multiple encoding mode 15 | 16 | Function whereisit($startPath='DESKTOP') { 17 | #Opens a System.Windows.Forms GUI to pick a file 18 | [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") 19 | Add-Type -AssemblyName System.Windows.Forms 20 | $startPath = New-Object System.Windows.Forms.OpenFileDialog -Property @{ InitialDirectory = [Environment]::GetFolderPath('DESKTOP') }#Starting path set to Desktop 21 | Do {$dInput = $startPath.ShowDialog()} While ($dInput -eq "Cancel") #Opens a file selection window, re-open until receive inputs, 2.Window is big enough, TopMost OFF 22 | return $startPath.FileName 23 | } 24 | 25 | Function whichlocation($startPath='DESKTOP') { 26 | #Opens a System.Windows.Forms GUI to pick a folder/path/dir 27 | Add-Type -AssemblyName System.Windows.Forms 28 | $startPath = New-Object System.Windows.Forms.FolderBrowserDialog -Property @{ Description="Select a directory. Drag bottom corner to enlarge for convenience"; SelectedPath=[Environment]::GetFolderPath($startPath); RootFolder='MyComputer'; ShowNewFolderButton=$true } 29 | #Intercepting failed inputs (user presses close/cancel button) with Do-While looping 30 | Do {$dInput = $startPath.ShowDialog((New-Object System.Windows.Forms.Form -Property @{TopMost=$true}))} While ($dInput -eq "Cancel") #1. Opens a path selection window. 2.Window is too small, TopMost ON 31 | #Root directory always have a "\" in return, whereas a folder/path/dir doesn't. Therefore an if statement is used to add "\" when needed, but comment out under single-encode mode 32 | if (($startPath.SelectedPath.SubString($startPath.SelectedPath.Length-1) -eq "\") -eq $false) {$startPath.SelectedPath+="\"} 33 | return $startPath.SelectedPath 34 | } 35 | 36 | #「@MrNetTek」Use high-DPI rendering, to fix blurry System.Windows.Forms 37 | Add-Type -TypeDefinition @' 38 | using System.Runtime.InteropServices; 39 | public class ProcessDPI { 40 | [DllImport("user32.dll", SetLastError=true)] 41 | public static extern bool SetProcessDPIAware(); 42 | } 43 | '@ 44 | $null = [ProcessDPI]::SetProcessDPIAware() 45 | 46 | Set-PSDebug -Strict 47 | Write-Output "ffmpeg -i [input] -an -f yuv4mpegpipe -strict unofficial - | x265.exe - --y4m --output`r`n" 48 | Write-Output "ffmpeg video scaling: -sws_flags <+full_chroma_int +full_chroma_inp +accurate_rnd>" 49 | Write-Output "ffmpeg .ass rendring: -filter_complex `"ass=`'F\:/Subtitle.ass`'`"" 50 | Write-Output "ffmpeg convert from variable to constant framt rate: -vsync cfr`r`n" 51 | Write-Output "Encode interlaced source with x265: --tff/--bff; x264: --interlaced`r`n" 52 | Write-Output "VSpipe [.vpy] --y4m - | x265.exe --y4m - --output" 53 | Write-Output "avs2yuv [.avs] -csp -depth - | x265.exe --input-res --fps - --output" 54 | Write-Output "avs2pipemod [.avs] -y4mp | x265.exe --y4m - --output <>`r`n" 55 | 56 | #「Bootstrap A-B」Only needed in multiple encoding mode, skipped in sigular encoding mode 57 | 58 | #「Bootstrap C」Locate path to export batch files, distingulishing single & multiple encoding mode is needed 59 | Read-Host "`r`n[Enter] proceed open a window that locates [path for exporting batch files]..." 60 | if ($mode -eq "s") {$bchExpPath = (whichlocation)+"enc_0S.bat"} 61 | elseif ($mode -eq "m") {$bchExpPath = (whichlocation)+'enc_$s.bat'} #Under multiple encding mode, using single quote on var $s 62 | else {modeparamerror} 63 | Write-Output "`r`n√ Path & filename generated as: $bchExpPath" 64 | 65 | #「Bootstrap D」Loop importing pipe up-downstream programs 66 | $fmpgPath=$vprsPath=$avsyPath=$avspPath=$svfiPath=$x265Path=$x264Path="" 67 | Do {Do { 68 | Switch (Read-Host "`r`nImport upstream program [A: ffmpeg | B: vspipe | C: avs2yuv | D: avs2pipemod | E: SVFI], repeated selection will trigger a skip") { 69 | a {if ($fmpgPath -eq "") {Write-Output "`r`nffmpeg------up route A. Opening a window to [locate ffmpeg.exe]"; $fmpgPath=whereisit} else {skip}} 70 | b {if ($vprsPath -eq "") {Write-Output "`r`nvspipe------up route B. Opening a window to [locate vspipe.exe]"; $vprsPath=whereisit} else {skip}} 71 | c {if ($avsyPath -eq "") {Write-Output "`r`navs2yuv-----up route C. Opening a window to [locate avs2yuv.avs]"; $avsyPath=whereisit} else {skip}} 72 | d {if ($avspPath -eq "") {Write-Output "`r`navs2pipemod-up route D. Opening a window to [locate avs2pipemod.exe]"; $avspPath=whereisit} else {skip}} 73 | e {if ($svfiPath -eq "") {Write-Output "`r`nsvfi--------up route E. Opening a window to [locate one_line_shot_args.exe]";$svfiPath=whereisit} else {skip}} 74 | default {badinputwarning} 75 | } 76 | } While ($fmpgPath+$vprsPath+$avsyPath+$avspPath+$svfiPath -eq "") 77 | Do { 78 | Switch (Read-Host "`r`nImport downstream program [A: x265/hevc | B: x264/avc], repeated selection will trigger a skip") { 79 | a {if ($x265Path -eq "") {Write-Output "`r`nx265--------down route A. Opening a window to [locate x265.exe]"; $x265Path=whereisit} else {skip}} 80 | b {if ($x264Path -eq "") {Write-Output "`r`nx264--------down route B. Opening a window to [locate x264.exe]"; $x264Path=whereisit} else {skip}} 81 | default {badinputwarning} 82 | } 83 | } While ($x265Path+$x264Path -eq "") 84 | if ((Read-Host "`r`n√ [Enter] import more routes (recommneded); Or [y][Enter] to move on") -eq "y") {$impEND="y"} else {$impEND="n"} #User decides when to exit loop 85 | } While ($impEND -eq "n") 86 | #Generate a datatable to indicate imported programs 87 | $updnTbl = New-Object System.Data.DataTable 88 | $availRts= [System.Data.DataColumn]::new("Routes") 89 | $upColumn= [System.Data.DataColumn]::new("\UNIX Pipe Upstream") 90 | $dnColumn= [System.Data.DataColumn]::new("\UNIX Pipe Dnstream") 91 | $updnTbl.Columns.Add($availRts); $updnTbl.Columns.Add($upColumn); $updnTbl.Columns.Add($dnColumn) 92 | [void]$updnTbl.Rows.Add(" A:",$fmpgPath,$x265Path); [void]$updnTbl.Rows.Add(" B:",$vprsPath,$x264Path) 93 | [void]$updnTbl.Rows.Add(" C:",$avsyPath,""); [void]$updnTbl.Rows.Add(" D:",$avspPath,""); [void]$updnTbl.Rows.Add(" E:",$svfiPath,"") 94 | ($updnTbl | Out-String).Trim() #1. Trim used to trim out empty rows, 2. Piping to Out-String to force $updnTbl to return the result before Read-Host below gets executed 95 | 96 | Read-Host "`r`nCheck whether the correct programs are imported and [Enter] to proceed, restart otherwise" 97 | 98 | #「Bootstrap E」Choose up-downstream program for the encoding commandline, generates impOPS, extOPS variable for route selection parameter 99 | $impOPS=$extOPS="" 100 | Do {Switch (Read-Host "Choose an upstream program for encoding [A | B | C | D | E], the rest will be commented out in generated batch") { 101 | a {if ($fmpgPath -ne "") {$impOPS="a"} else {nosuchrouteerr}} 102 | b {if ($vprsPath -ne "") {$impOPS="b"} else {nosuchrouteerr}} 103 | c {if ($avsyPath -ne "") {$impOPS="c"} else {nosuchrouteerr}} 104 | d {if ($avspPath -ne "") {$impOPS="d"} else {nosuchrouteerr}} 105 | e {if ($svfiPath -ne "") {$impOPS="e"} else {nosuchrouteerr}} 106 | default {badinputwarning} 107 | } 108 | if ($impOPS -ne "") {#No-execusion when upstream input has failed, move to loop end and trigger a loopback instead 109 | Switch (Read-Host "`r`nChoose a downstream program for encoding [A | B], the rest will be commented out in generated batch") { 110 | a {if ($x265Path -ne "") {$extOPS="a"} else {nosuchrouteerr}} 111 | b {if ($x264Path -ne "") {$extOPS="b"} else {nosuchrouteerr}} 112 | default {badinputwarning} 113 | } 114 | } 115 | } While (($impOPS -eq "") -or ($extOPS -eq "")) 116 | 117 | #「Bootstrap F」Use impOPS, extOPS to generate the selected route as new variable keyRoute 118 | $keyRoute=""; $sChar="AAA" #An accident-proof measure where in bootstrap F, G expands $sChar too early, which may cause empty value error 119 | Switch ($mode+$impOps+$extOPS) { 120 | saa {$keyRoute="$fmpgPath %ffmpegVarA% %ffmpegParA% - | $x265Path %x265ParA% %x265VarA%"} #ffmpeg+x265+single 121 | sab {$keyRoute="$fmpgPath %ffmpegVarA% %ffmpegParA% - | $x264Path %x264ParA% %x264VarA%"} #ffmpeg+x264+single 122 | sba {$keyRoute="$vprsPath %vspipeVarA% %vspipeParA% - | $x265Path %x265ParA% %x265VarA%"} #VSPipe+x265+single 123 | sbb {$keyRoute="$vprsPath %vspipeVarA% %vspipeParA% - | $x264Path %x264ParA% %x264VarA%"} #VSPipe+x264+single 124 | sca {$keyRoute="$avsyPath %avsyuvVarA% %avsyuvParA% - | $x265Path %x265ParA% %x265VarA%"} #AVSYUV+x265+single 125 | scb {$keyRoute="$avsyPath %avsyuvVarA% %avsyuvParA% - | $x264Path %x264ParA% %x264VarA%"} #AVSYUV+x264+single 126 | sda {$keyRoute="$avspPath %avsmodVarA% %avsmodParA% | $x265Path %x265ParA% %x265VarA%"} #AVSPmd+x265+single, No "-" in AVSPipeMod upstream 127 | sdb {$keyRoute="$avspPath %avsmodVarA% %avsmodParA% | $x264Path %x264ParA% %x264VarA%"} #AVSPmd+x264+single, No "-" in AVSPipeMod upstream 128 | sea {$keyRoute="$svfiPath %olsargVarA% %olsargParA% - | $x265Path %x265ParA% %x265VarA%"} #OLSARG+x265+single 129 | seb {$keyRoute="$svfiPath %olsargVarA% %olsargParA% - | $x264Path %x264ParA% %x264VarA%"} #OLSARG+x264+single 130 | maa {$keyRoute="$fmpgPath %ffmpegVarA% %ffmpegParA% - | $x265Path %x265ParA%"+' %x265Var$sChar%'} #ffmpeg+x265+multiple, single quoting to yield expansion 131 | mab {$keyRoute="$fmpgPath %ffmpegVarA% %ffmpegParA% - | $x264Path %x264ParA%"+' %x264Var$sChar%'} #ffmpeg+x264+multiple 132 | mba {$keyRoute="$vprsPath %vspipeVarA% %vspipeParA% - | $x265Path %x265ParA%"+' %x265Var$sChar%'} #VSPipe+x265+multiple 133 | mbb {$keyRoute="$vprsPath %vspipeVarA% %vspipeParA% - | $x264Path %x264ParA%"+' %x264Var$sChar%'} #VSPipe+x264+multiple 134 | mca {$keyRoute="$avsyPath %avsyuvVarA% %avsyuvParA% - | $x265Path %x265ParA%"+' %x265Var$sChar%'} #AVSYUV+x265+multiple 135 | mcb {$keyRoute="$avsyPath %avsyuvVarA% %avsyuvParA% - | $x264Path %x264ParA%"+' %x264Var$sChar%'} #AVSYUV+x264+multiple 136 | mda {$keyRoute="$avspPath %avsmodVarA% %avsmodParA% | $x265Path %x265ParA%"+' %x265Var$sChar%'} #AVSPmd+x265+multiple, No "-" in AVSPipeMod upstream 137 | mdb {$keyRoute="$avspPath %avsmodVarA% %avsmodParA% | $x264Path %x264ParA%"+' %x264Var$sChar%'} #AVSPmd+x264+multiple, No "-" in AVSPipeMod upstream 138 | mea {$keyRoute="$svfiPath %olsargVarA% %olsargParA% - | $x265Path %x265ParA%"+' %x265Var$sChar%'} #OLSARG+x265+multiple 139 | meb {$keyRoute="$svfiPath %olsargVarA% %olsargParA% - | $x264Path %x264ParA%"+' %x264Var$sChar%'} #OLSARG+x264+multiple 140 | Default {modeimpextopserr} 141 | } 142 | #「Bootstrap G」Generate all possible possible upstream--downstream commandline layouts, which are the alternate routes 143 | [array] $upPipeStr=@("$fmpgPath %ffmpegVarA% %ffmpegParA%", "$vprsPath %vspipeVarA% %vspipeParA%", "$avsyPath %avsyuvVarA% %avsyuvParA%", "$avspPath %avsmodVarA% %avsmodParA%","$svfiPath %olsargVarA% %olsargParA%") | Where-Object {$_.Length -gt 26} 144 | Switch ($mode) {#Filter the non-existant route in dnPipeStr with length property, multile encoding mode has downstream commandline with $sChar variable which is 1 character less the nsingle encode mode, make use of the if split 145 | s {[array]$dnPipeStr=@( "$x265Path %x265ParA% %x265VarA%", "$x264Path %x264ParA% %x264VarA%") | Where-Object {$_.Length -gt 22}} 146 | m {[array]$dnPipeStr=@(("$x265Path%x265ParA%"+' %x265Var$sChar%'), ("$x264Path %x264ParA%"+' %x264Var$sChar%')) | Where-Object {$_.Length -gt 23}} #single quoting to yield expansion, extra () are used to prevent "+" & "," mixing up in Array 147 | Default {modeparamerror} 148 | } 149 | [array]$altRoute=@() #Commenting sign + `$updnPipeStr = altRoute. Thus generates all the alternate routes 150 | for ($x=0; $x -lt ($upPipeStr.Length); $x++) {#upstream/horizontal iteration of possibilities 151 | for ($y=0; $y -lt ($dnPipeStr.Length); $y++) {#downstream/vertical iteration of possibilities 152 | if ($upPipeStr -notlike "avsmod") {$altRoute+="REM "+$upPipeStr[$x]+" - | "+$dnPipeStr[$y]} #No "-" in AVSPipeMod upstream 153 | else {$altRoute+="REM "+$upPipeStr[$x]+" | "+$dnPipeStr[$y]} #No "-" in AVSPipeMod upstream 154 | } 155 | } 156 | "√ Number of usable/alternate routes are: "+($altRoute.Count.ToString()) | Out-String #Variable `$keyRoute & `$altRoute are ready, only differs in single & multiple mode where variable expansion is needed in multiple encoding mode 157 | 158 | if ($extOPS="a") {tmpmuxreminder} #Provide reminder for multiplexing to .mkv when selecting x265/hevc donwstream keyRoute 159 | 160 | #「Bootstrap H.s」Export batch file w/ utf-8NoBOM text codec 161 | #When expanding $sChar variable, Array would generate a multi-line text without line change, therefore a pipe to Out-String and then activatng the $sChar variable is needed, multiple encode only 162 | $utf8NoBOM=New-Object System.Text.UTF8Encoding $false #Enfore the output of UTB-8NoBom 163 | Write-Output "`r`n... Generating enc_0S.bat`r`n" 164 | $enc_gen="REM 「Title」 165 | @echo. 166 | @echo -------------Starting encode-------------- 167 | 168 | REM 「Debug」Comment out during normal usage 169 | REM @echo %ffmpegParA% %ffmpegVar"+$sChar+"% 170 | REM @echo %vspipeParA% %vspipeVar"+$sChar+"% 171 | REM @echo %avsyuvParA% %avsyuvVar"+$sChar+"% 172 | REM @echo %avsmodParA% %avsmodVar"+$sChar+"% 173 | REM @echo %olsargParA% %olsargVar"+$sChar+"% 174 | REM @echo %x265ParA% %x265Var"+$sChar+"% 175 | REM @echo %x264ParA% %x264Var"+$sChar+"% 176 | REM pause 177 | 178 | REM 「Encode-KeyRoutes」Comment out during debugging 179 | REM Var is used to specify dynamic values such as input-output, per-video encoding options 180 | 181 | "+$ExecutionContext.InvokeCommand.ExpandString(($keyRoute | Out-String))+" 182 | 183 | REM 「Encode-ALTRoutes」Copy and replace from lower to upper commandline wihtout REM commenting to change encoding programs 184 | 185 | "+$ExecutionContext.InvokeCommand.ExpandString(($altRoute | Out-String))+" 186 | 187 | REM Choose「y:Continue/n:Pause/z:END」Continuing after 5s sleep, false input are blocked by choice statement, pause allows continue. 188 | 189 | choice /C YNZ /T 5 /D Y /M `" Continue? (Sleep=5; Default: Y, Pause: N, Stop: Z)`" 190 | 191 | if %ERRORLEVEL%==3 cmd /k 192 | if %ERRORLEVEL%==2 pause 193 | if %ERRORLEVEL%==1 endlocal && exit /b" 194 | 195 | #Out-File -InputObject $enc_gen -FilePath $bchExpPath -Encoding utf8 196 | if ($mode -eq "m") {[IO.File]::WriteAllLines($ExecutionContext.InvokeCommand.ExpandString($bchExpPath), $enc_gen, $utf8NoBOM)}#Expanding variable $s is needed in multiple encoding mode 197 | elseif ($mode -eq "s") {[IO.File]::WriteAllLines($bchExpPath, $enc_gen, $utf8NoBOM)} 198 | else {modeparamerror} 199 | 200 | Write-Output "Completed, as long as up-downstream program doesn't update, any controller batch (step 4) generated by step 3 could always use enc_0S.bat / enc_X.bat" 201 | pause -------------------------------------------------------------------------------- /bbenc-source/Step 2-5 in ENG/5.「Generate multiplexing batch (ffmpeg)」.ps1: -------------------------------------------------------------------------------- 1 | cls #Dev's Github: https://github.com/iAvoe 2 | Read-Host "This program imports all tracks detected instead of manually selecting specific tracks to import under multi-track files; to import manually, use the follwing commandline to dump all streams inside, then run this program to import individually:`r`n`"ffmpeg -dump_attachment: `"outfile`" -i infile.mkv`"`r`nPress Enter to continue..." 3 | 4 | Function namecheck([string]$inName) { 5 | $badChars = '[{0}]' -f [regex]::Escape(([IO.Path]::GetInvalidFileNameChars() -join '')) 6 | ForEach ($_ in $badChars) {if ($_ -match $inName) {return $false}} 7 | return $true 8 | } #Checking if input filename compliants to Windows file naming scheme 9 | 10 | Function whereisit($startPath='DESKTOP') { 11 | #Opens a System.Windows.Forms GUI to pick a file 12 | [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") 13 | Add-Type -AssemblyName System.Windows.Forms 14 | $startPath = New-Object System.Windows.Forms.OpenFileDialog -Property @{ InitialDirectory = [Environment]::GetFolderPath($startPath) } #Starting path set to Desktop 15 | Do {$dInput = $startPath.ShowDialog()} While ($dInput -eq "Cancel") #Opens a file selection window, un-cancel cancelled user inputs (close/cancel button) by reopening selection window again 16 | return $startPath.FileName 17 | } 18 | 19 | Function whichlocation($startPath='DESKTOP') { 20 | #Opens a System.Windows.Forms GUI to pick a folder/path/dir 21 | Add-Type -AssemblyName System.Windows.Forms 22 | $startPath = New-Object System.Windows.Forms.FolderBrowserDialog -Property @{ Description="Select a directory. Drag bottom corner to enlarge for convenience"; SelectedPath=[Environment]::GetFolderPath($startPath); RootFolder='MyComputer'; ShowNewFolderButton=$true } 23 | #Intercepting failed inputs (user presses close/cancel button) with Do-While looping 24 | Do {$dInput = $startPath.ShowDialog()} While ($dInput -eq "Cancel") #Opens a path selection window 25 | #Root directory always have a "\" in return, whereas a folder/path/dir doesn't. Therefore an if statement is used to add "\" when needed, but comment out under single-encode mode 26 | if (($startPath.SelectedPath.SubString($startPath.SelectedPath.Length-1) -eq "\") -eq $false) {$startPath.SelectedPath+="\"} 27 | return $startPath.SelectedPath 28 | } 29 | 30 | #3-in-1 large function, diverted input file to α: multiplexed file, β: non-std multiplexed file, γ: demultiplexed stream. Detect α, β with ffprobe, γ with GetExtension. Stop on error for irrevelant file types;finally generate all the required ffmpeg -map ?:? & -c:? copy options in a multicasting fashion 31 | Function addcopy { 32 | Param ([Parameter(Mandatory=$true)]$fprbPath, [Parameter(Mandatory=$true)]$StrmPath, [Parameter(Mandatory=$true)]$mapQTY,[Parameter(Mandatory=$true)]$vcopy) 33 | $DebugPreference="Continue" #Cannot use Write-Output/Host or " " inside a function as it would trigger a value return, modify Write-Debug instead 34 | $result=@() 35 | $mrc=$vrc=$arc=$src=$trc=[IO.Path]::GetExtension($StrmPath) #Only get file extension, which prevents filename accidentally matches to detection statements. Get-ChildItem is incompetent for getting .hevc steams' extension, which is useless 36 | #「Detection」find out if mux includes video+audio+subtitle+font 37 | if (($mrc -match "mkv") -or ($mrc -match "mp4") -or ($mrc -match "mov") -or 38 | ($mrc -match "f4v") -or ($mrc -match "flv") -or ($mrc -match "avi") -or 39 | ($mrc -match "m3u") -or ($mrc -match "m3u8") -or ($mrc -match "mxv")) { 40 | Write-Debug "? Possibility α: Multiplex/encapsulation for video+audio+subtitle+font`r`n" 41 | #「ffprobe」detect a track types in a multiplexed file 42 | #Some file has deleted codec_tag_string, therefore using codec_name or fallback 43 | $VProbe=$fprbPath+" -i '$StrmPath' -select_streams v -v error -hide_banner -show_entries stream=codec_name,codec_tag_string,avg_frame_rate:disposition=:tags= -of csv" 44 | Invoke-Expression $VProbe > "C:\temp_mv_info.csv" #ffprobe generates a CSV with: stream,, 45 | $AProbe=$fprbPath+" -i '$StrmPath' -select_streams a -v error -hide_banner -show_entries stream=codec_name,codec_tag_string:disposition=:tags= -of csv" 46 | Invoke-Expression $AProbe > "C:\temp_ma_info.csv" #Imported CSV is: A=stream,B=,C= 47 | $SProbe=$fprbPath+" -i '$StrmPath' -select_streams s -v error -hide_banner -show_entries stream=codec_name,codec_tag_string:disposition=:tags= -of csv" 48 | Invoke-Expression $SProbe > "C:\temp_ms_info.csv" 49 | $SProbe=$fprbPath+" -i '$StrmPath' -select_streams t -v error -hide_banner -show_entries stream=codec_name,codec_tag_string:disposition=:tags= -of csv" 50 | Invoke-Expression $SProbe > "C:\temp_mt_info.csv" 51 | 52 | if (Test-Path "C:\temp_mv_info.csv") {$Vcsv=Import-Csv "C:\temp_mv_info.csv" -Header A,B,C,D; Remove-Item "C:\temp_mv_info.csv"} #ffmpeg demands total frame count, or refuses to write mkv encapsulation 53 | if (Test-Path "C:\temp_ma_info.csv") {$Acsv=Import-Csv "C:\temp_ma_info.csv" -Header A,B,C; Remove-Item "C:\temp_ma_info.csv"} 54 | if (Test-Path "C:\temp_ms_info.csv") {$Scsv=Import-Csv "C:\temp_ms_info.csv" -Header A,B,C; Remove-Item "C:\temp_ms_info.csv"} 55 | if (Test-Path "C:\temp_mt_info.csv") {$Tcsv=Import-Csv "C:\temp_mt_info.csv" -Header A,B,C; Remove-Item "C:\temp_mt_info.csv"} #Test-Path is to prevent Remove-Item stop on file-missing-error 56 | 57 | if (($Vcsv.C -match "0") -or ($Vcsv.C -eq "")) {Write-Debug "α! Missing video codec_tag_string, fallback to codec_name"; $vrc=$Vcsv.B} else {$vrc=$Vcsv.C} #Detect if code_name_tag data is valid 58 | $vfc=$Vcsv.D 59 | if (($Acsv.C -match "0") -or ($Acsv.C -eq "")) {Write-Debug "α! Missing audio codec_tag_string, fallback to codec_name"; $arc=$Acsv.B} else {$arc=$Acsv.C} 60 | if (($Scsv.C -match "0") -or ($Scsv.C -eq "")) {Write-Debug "α! Missing subtitle codec_tag_string, fallback to codec_name"; $src=$Scsv.B} else {$src=$Scsv.C} 61 | if (($Tcsv.C -match "0") -or ($Tcsv.C -eq "")) {Write-Debug "α! Missing font codec_tag_string, fallback to codec_name"; $trc=$Tcsv.B} else {$trc=$Tcsv.C} 62 | #Write-Debug "vrc: $vrc " 63 | #Write-Debug $vrc.GetType() 64 | if (($vrc -eq "") -or ($vrc -eq $null)) {Write-Debug "`r`nα× No video stream found on $vrc"} 65 | else { 66 | if (($vrc -match "hevc") -or ($vrc -match "h265") -or ($vrc -match "avc") -or 67 | ($vrc -match "h264") -or ($vrc -match "cfhd") -or ($vrc -match "ap4x") -or 68 | ($vrc -match "apcn") -or ($vrc -match "hev1") -or ($vrc -match "vp09") -or 69 | ($vrc -match "vp9")) { 70 | Write-Debug "`r`nα√ Video supports multiplexing: $vrc" 71 | if (($vrc -match "ap4x") -or ($vrc -match "apcn")) {Write-Warning "Detecting ProRes 422 / 4444XQ video streams. Supported by MOV/QTFF, MXF multiplexing container"} 72 | if (($vrc -match "vp09") -or ($vrc -match "vp9")) {Write-Warning "Detecting VP9 video stream. Supported by MKV, MP4, *OGG, WebM multiplexing container"} 73 | } else {Write-Warning "`r`nα? Video may supports multiplexing: $vrc"} 74 | if ($vcopy -eq "y") {$result+="-r $vfc -c:v copy "} else {Write-Warning "ffmpeg option -c:v copy & -r was written in previous import, preventing adding video stream for this run"} 75 | } 76 | #Write-Debug "arc: $arc" 77 | #Write-Debug $arc.GetType() 78 | if (($arc -eq "") -or ($arc -eq $null)) {Write-Debug "`r`nα× No audio stream found on $arc"} 79 | else { 80 | if (($arc -match "aac") -or ($arc -match "ogg") -or ($arc -match "alac") -or 81 | ($arc -match "dts") -or ($arc -match "mp3") -or ($arc -match "wma") -or 82 | ($arc -match "wav") -or ($arc -match "pcm") -or ($arc -match "lpcm") -or 83 | ($arc -match "flac") -or ($arc -match "ape") -or ($arc -match "alac")) { 84 | Write-Debug "`r`nα√ Audio supports multiplexing: $arc" 85 | if ($arc -match "ape") {Write-Error "α× No multiplex container available for MonkeysAudio/APE format"; pause; exit} 86 | if ($arc -match "flac") {Write-Warning "Detecting FLAC audio stream. Not supported by MOV/QTFF, MXF multiplexing container"} 87 | if ($arc -match "alac") {Write-Warning "Detecting ALAC audio stream, Not supported by MXF multiplexing container"} 88 | } else {Write-Warning "`r`nα? Audio may supports multiplexing: $arc"} 89 | $result+="-c:a copy " 90 | } 91 | #Write-Debug "src: $src" 92 | #Write-Debug $src.GetType() 93 | if (($src -eq "") -or ($src -eq $null)) {Write-Debug "`r`nα× No subtitle track found on $src"} 94 | else { 95 | if (($src -match "srt") -or ($src -match "ass") -or ($src -match "ssa")) { 96 | Write-Debug "`r`nα√ Subtitle supports multiplexing: $src" 97 | if (($src -match "ass") -or ($src -match "ssa")) {Write-Warning "Detecting ASS/SSA subtitle track, only supported by MKV"} 98 | } else {Write-Warning "`r`nα? Subtitle may supports multiplexing: $src"} 99 | $result+="-c:s copy " 100 | } 101 | #Write-Debug "trc: $trc" 102 | #Write-Debug $trc.GetType() 103 | if (($trc -eq "") -or ($trc -eq $null)) {Write-Debug "`r`nα× No font track found on $trc"} 104 | else { 105 | if (($trc -match "ttf") -or ($trc -match "ttc") -or ($trc -match "otf")) {Write-Warning "`r`nα! Only MKV supports multiplexing font $trc"} 106 | $result+="-c:t copy " 107 | } 108 | 109 | if ($result.Count -lt 1) {Write-Error "α× No stream found in multiplex container $StrmPath"; pause; exit} 110 | return $result 111 | }elseif( 112 | ($mrc -match "m4a") -or ($mrc -match "mka") -or ($mrc -match "mks")) { 113 | Write-Debug "? Possibility β: Multiplex file format made for non-video streams`r`n" 114 | $AProbe=$fprbPath+" -i '$StrmPath' -select_streams a -v error -hide_banner -show_entries stream=codec_name,codec_tag_string:disposition=:tags= -of csv" 115 | $SProbe=$fprbPath+" -i '$StrmPath' -select_streams s -v error -hide_banner -show_entries stream=codec_name,codec_tag_string:disposition=:tags= -of csv" 116 | $SProbe=$fprbPath+" -i '$StrmPath' -select_streams t -v error -hide_banner -show_entries stream=codec_name,codec_tag_string:disposition=:tags= -of csv" 117 | Invoke-Expression $AProbe > "C:\temp_na_info.csv" 118 | Invoke-Expression $SProbe > "C:\temp_ns_info.csv" 119 | Invoke-Expression $SProbe > "C:\temp_nt_info.csv" 120 | if (Test-Path "C:\temp_na_info.csv") {$Acsv=Import-Csv "C:\temp_na_info.csv" -Header A,B,C; Remove-Item "C:\temp_na_info.csv"} 121 | if (Test-Path "C:\temp_ns_info.csv") {$Scsv=Import-Csv "C:\temp_ns_info.csv" -Header A,B,C; Remove-Item "C:\temp_ns_info.csv"} 122 | if (Test-Path "C:\temp_nt_info.csv") {$Tcsv=Import-Csv "C:\temp_nt_info.csv" -Header A,B,C; Remove-Item "C:\temp_nt_info.csv"} 123 | 124 | if (($Acsv.C -match "0") -or ($Acsv.C -eq "")) {Write-Debug "β! Missing audio codec_tag_string, fallback to codec_name"; $arc=$Acsv.B} else {$arc=$Acsv.C} 125 | if (($Scsv.C -match "0") -or ($Scsv.C -eq "")) {Write-Debug "β! Missing subtitle codec_tag_string, fallback to codec_name"; $src=$Scsv.B} else {$src=$Scsv.C} 126 | if (($Tcsv.C -match "0") -or ($Tcsv.C -eq "")) {Write-Debug "β! Missing font codec_tag_string, fallback to codec_name"; $trc=$Tcsv.B} else {$trc=$Tcsv.C} 127 | #Write-Debug "arc: $arc" 128 | #Write-Debug $arc.GetType() 129 | if (($arc -eq "") -or ($arc -eq $null)) {Write-Debug "`r`nβ× No audio stream found on $arc"} 130 | else { 131 | if (($arc -match "aac") -or ($arc -match "ogg") -or ($arc -match "alac") -or 132 | ($arc -match "dts") -or ($arc -match "mp3") -or ($arc -match "wma") -or 133 | ($arc -match "wav") -or ($arc -match "pcm") -or ($arc -match "lpcm") -or 134 | ($arc -match "flac") -or ($arc -match "ape") -or ($arc -match "alac")) { 135 | Write-Debug "`r`nβ√ Audio supports multiplexing: $arc" 136 | if ($arc -match "ape") {Write-Error "β× No multiplex container available for MonkeysAudio/APE format"; pause; exit} 137 | if ($arc -match "flac") {Write-Warning "Detecting FLAC audio stream. Not supported by MOV/QTFF, MXF multiplexing container"} 138 | if ($arc -match "alac") {Write-Warning "Detecting ALAC audio stream, Not supported by MXF multiplexing container"} 139 | } else {Write-Warning "`r`nβ? Audio may supports multiplexing: $arc"} 140 | $result+="-c:a copy " 141 | } 142 | #Write-Debug "src: $src" 143 | #Write-Debug $src.GetType() 144 | if (($src -eq "") -or ($src -eq $null)) {Write-Debug "`r`nβ× No subtitle track found on $src"} 145 | else { 146 | if (($src -match "srt") -or ($src -match "ass") -or ($src -match "ssa")) { 147 | Write-Debug "`r`nα√ Subtitle supports multiplexing: $src" 148 | if (($src -match "ass") -or ($src -match "ssa")) {Write-Warning "Detecting ASS/SSA subtitle track, only supported by MKV"} 149 | } else {Write-Warning "`r`nα? Subtitle may supports multiplexing: $src"} 150 | $result+="-c:s copy " 151 | } 152 | #Write-Debug "trc: $trc" 153 | #Write-Debug $trc.GetType() 154 | if (($trc -eq "") -or ($trc -eq $null)) {Write-Debug "`r`nβ× No font track found on $trc"} 155 | else { 156 | if (($trc -match "ttf") -or ($trc -match "ttc") -or ($trc -match "otf")) {Write-Warning "`r`nα! Only MKV supports multiplexing font $trc"} 157 | $result+="-c:t copy " 158 | } 159 | 160 | if ($result.Count -lt 1) {Write-Error "β× No stream found in multiplex container $StrmPath"; pause; exit} 161 | return $result[$mapQTY] 162 | }else { Write-Debug "? Possibility γ: Raw streams`r`n" 163 | if (($vrc -match "hevc") -or ($vrc -match "h265") -or ($vrc -match "avc") -or 164 | ($vrc -match "h264") -or ($vrc -match "cfhd") -or ($vrc -match "ap4x") -or 165 | ($vrc -match "apcn") -or ($vrc -match "hev1") -or ($vrc -match "vp09") -or 166 | ($vrc -match "vp9")) { 167 | $VProbe=$fprbPath+" -i '$StrmPath' -select_streams v -v error -hide_banner -show_entries stream=codec_name,codec_tag_string,avg_frame_rate:disposition=:tags= -of csv" 168 | Invoke-Expression $VProbe > "C:\temp_ov_info.csv" #ffprobe generates a CSV with: stream,, 169 | if (Test-Path "C:\temp_ov_info.csv") {$Vcsv=Import-Csv "C:\temp_ov_info.csv" -Header A,B,C,D; Remove-Item "C:\temp_ov_info.csv"} 170 | $vfc=$Vcsv.D #ffmpeg demands total frame count, or refuses to write mkv encapsulation 171 | if ($vcopy -eq "y") {$result+="-r $vfc -c:v copy "} else {Write-Warning "ffmpeg option -c:v copy & -r was written in previous import, preventing adding video stream for this run"} 172 | if ((($MUXops -match "mkv") -and ($vrc -match "hevc")) -or (($MUXops -match "mkv") -and ($vrc -match "h265"))) {Write-Error "γ× ffmpeg refuses to multiplex raw hevc/h265 to mkv (missing pts), use this script to multiplex to mp4 first"; pause; exit} 173 | if ((($MUXops -match "mkv") -and ($vrc -match "hevc")) -or (($MUXops -match "mkv") -and ($vrc -match "h265"))) {Write-Error "γ× ffmpeg refuses to multiplex raw avc/h264 to mkv (missing pts), use this script to multiplex to mp4 first"; pause; exit} 174 | Write-Debug "`r`nγ√ $MUXops supports multiplexing video stream: $vrc`r`n" 175 | if (($vrc -match "ap4x") -or ($vrc -match "apcn")) {Write-Warning "Detecting ProRes 422 / 4444XQ video streams. Supported by MOV/QTFF, MXF multiplexing container"} 176 | if (($vrc -match "vp09") -or ($vrc -match "vp9")) {Write-Warning "Detecting VP9 video streams. Supported by MKV, MP4, *OGG, WebM multiplexing container"} 177 | } elseif( 178 | ($arc -match "aac") -or ($arc -match "ogg") -or ($arc -match "alac") -or 179 | ($arc -match "dts") -or ($arc -match "mp3") -or ($arc -match "wma") -or 180 | ($arc -match "wav") -or ($arc -match ".pcm") -or ($arc -match ".lpcm") -or 181 | ($arc -match "flac") -or ($arc -match "ape") -or ($arc -match "alac")) { 182 | $result+="-c:a copy " 183 | Write-Debug "`r`nγ√ $MUXops supports multiplexing audio stream: $arc`r`n" 184 | if ($arc -match "ape") {Write-Error "β× No multiplex container available for MonkeysAudio/APE format"; pause; exit} 185 | if ($arc -match "flac") {Write-Warning "Detecting FLAC audio stream. Not supported by MOV/QTFF, MXF multiplexing container"} 186 | if ($arc -match "alac") {Write-Warning "Detecting ALAC audio stream, Not supported by MXF multiplexing container"} 187 | } elseif( 188 | ($src -match "srt") -or ($src -match "ass") -or ($src -match "ssa")) { 189 | $result+="-c:s copy " 190 | if (($src -match "ass") -or ($src -match "ssa")) {Write-Warning "Detecting ASS/SSA subtitle track, only supported by MKV"} 191 | Write-Debug "`r`nγ√ $MUXops supports multiplexing subtitle track: $src`r`n" 192 | } elseif( 193 | ($trc -match "ttf") -or ($trc -match "ttc") -or ($trc -match "otf")) { 194 | $result+="-c:t copy " 195 | Write-Warning "`r`nγ! Only MKV supports multiplexing font $trc`r`n" 196 | } else{Write-Error "`r`nγ× Could not add ffmpeg option as single stream file input is out of supported range mkv/mp4/mov/f4v/flv/avi, m4a/mka/mks, hevc/avc/cfhd/ap4x/apcn, aac/ogg/alac/dts/mp3/wma/pcm/lpcm/ape, srt/ass/ssa`r`n"; pause} 197 | return $result 198 | } 199 | } 200 | 201 | #「@MrNetTek」Use high-DPI rendering, to fix blurry System.Windows.Forms 202 | Add-Type -TypeDefinition @' 203 | using System.Runtime.InteropServices; 204 | public class ProcessDPI { 205 | [DllImport("user32.dll", SetLastError=true)] 206 | public static extern bool SetProcessDPIAware(); 207 | } 208 | '@ 209 | $null = [ProcessDPI]::SetProcessDPIAware() 210 | 211 | Write-Output "To manually check all streams/tracks within a multiplex format, open w/ mediainfo & select`"view`", or check all of audio tracks w/: ffprobe -i [src] -select_streams a -v error -hide_banner -show_streams -of ini > `"X:\1.txt`"`r`n" 212 | Write-Output "To export all AAC audio tracks (commonly inside a .mp4 file): ffmpeg -i [src] -vn -c:a:0 copy `"X:\folder\audOut.aac`" `r`nffmpeg -i [src] -vn -c:a:0 copy `"X:\folder\audOut2.aac`" `r`nffmpeg -i [src] -vn -c:a:0 copy `"X:\folder\audOut3.aac`"`r`n" 213 | Write-Output "To export all subtitles (check mediainfo for proper file extension): ffmpeg -i [src] -vn -an -c:s:0 copy `"X:\folder\subOut1.ass`" `r`nffmpeg -i [src] -vn -an -c:s:0 copy `"X:\folder\subOut2.ass`" `r`nffmpeg -i [src] -vn -an -c:s:0 copy `"X:\folder\subOut3.ass`"`r`n" 214 | Write-Output "To re-multiplex streams/tracks from a multiplexed source:`r`nffmpeg -i [src] -c:v copy -c:a:0 copy -c:a:1 copy -c:a:0 copy -c:s:0 copy`"X:\folder\export1.mkv`"`r`n" 215 | Write-Output "To re-multiplex streams/tracks in many multiplexed sources:`r`nffmpeg -i [src] -i [src2] -i [src3] -i [src4] -map 1:v -c:v copy -map 2:a -c:a copy -map 2:s -c:s copy -map 3:a -c:a copy -map 3:s -c:s copy -map 4:a -c:a copy -map 4:s -c:s copy `"X:\文件夹\导出2.mkv`" `r`n" 216 | Write-Output "Note: this script works within a limited range of stream formats to reduce coding complexities`r`nmkv/mp4/mov/f4v/flv/avi, m4a/mka/mks, hevc/avc/cfhd/ap4x/apcn, aac/ogg/alac/dts/mp3/wma/pcm/lpcm, srt/ass/ssa. ttf/ttc/otf`r`n" 217 | Set-PSDebug -Strict 218 | 219 | #「Bootstrap」Locate all pathes needed 220 | Read-Host "Press Enter to open a selecting window to locate [Path to export multiplexing batch], it may pop up at rear of current window" 221 | $exptPath=whichlocation 222 | Read-Host "Press Enter to open a selecting window to locate [Path to export multiplexing result], it may pop up at rear of current window" 223 | $muxPath=whichlocation 224 | Read-Host "Press Enter to open a selecting window to locate [ffprobe.exe]" 225 | $fprbPath=whereisit 226 | 227 | #「Bootstrap」Initialize a variable to temporarily store ffmpeg import options, & another one to aggregate multiple instances from the former 228 | $StrmIMPAgg=$StrmParAgg="" 229 | Write-Warning "Only the 1st video stream imported in this script will get it's `"-c:v copy`" option generated, make sure to import the video wanted to multipelx first`r`n" 230 | $keepAdd=$vcopy="y" #initialize loop continuity variable, and decision to whether generate "-c:v copy" due to having the same value 231 | $mapQTY=0 #Initialize variable for "ffmpeg's -map" option, which requires to locate to apply operations to which imported file 232 | 233 | #「Bootstrap」Loop through addcopy function to enable multi-import works 234 | Read-Host "Press Enter to open a looped selecting window to locate [source files to multiplex]" 235 | While ($keepAdd -eq "y") { 236 | $StrmPath=whereisit 237 | $addcopy=addcopy -fprbPath $fprbPath -StrmPath $StrmPath -mapQTY $mapQTY -vcopy $vcopy 238 | if ($addcopy -match "-c:v copy") {$vcopy="n"} #The 1st video stream gets it's -c:v copy option, and then trigger kill-switch variable $vcopy 239 | 240 | $StrmIMPAgg+=" -i `"$StrmPath`"" #Due to ffmpeg demands -map option for multiple imports, and each -map option has to follow with operations like "-c:? copy" accordingly, reorganization work is needed 241 | $StrmParAgg+="-map "+$mapQTY+" $addcopy" #-map option's variable gets aggregated, note that ffmpeg prohibits the use of single quotes 242 | 243 | $mapQTY+=1 244 | Do {$keepAdd=Read-Host "`r`n√ Generated ffmpeg importing variable: $StrmParAgg `r`n`r`nChoose to [n: END | y: Continue] file importing tasks" 245 | if ((($keepAdd -eq "y") -eq $false) -and (($keepAdd -eq "n") -eq $false)) {Write-Warning "No option were selected"} 246 | } While ((($keepAdd -eq "y") -eq $false) -and (($keepAdd -eq "n") -eq $false)) 247 | } 248 | 249 | $nameIn=[IO.Path]::GetFileNameWithoutExtension($StrmPath) #Initialize the filename for exporting multiplexed files 250 | $nameIn+="_tempMux" 251 | Switch (Read-Host "`r`nChoose how to specify filename of encoding exports [A: Manually input | B: $nameIn]") { 252 | a {Do {Do {$nameIn = (Read-Host -Prompt "`r`nSpecify filename to export (w/out file extension). E.g., [Zzz] Memories – `$serial (BDRip 1764x972 HEVC)") 253 | $nameCheck = namecheck($nameIn) 254 | if ($nameCheck -eq $false) {Write-Warning "Detecting illegal characters / | \ < > : ? * `""} 255 | elseif ($nameIn -eq 'y') {Write-Warning "Preventing accidental input: y"} 256 | } While ($nameCheck -eq $false) 257 | } While ($nameIn -eq 'y') 258 | Write-Output "√ Filename is compliant to Windows OS`r`n"} #Close chose A 259 | b {Write-Output "Default filename was choosen"} 260 | default {Write-Output "Default filename was choosen(default option)"} 261 | } 262 | 263 | #Choose a multiplex format. This may get expanded in future with not just ffmpeg (and compatibility check gets improved), which causes $fmpgPath to be duplicated 264 | Do {$MUXops="" 265 | Switch (Read-Host "`r`nChoose a multiplexing tool+format:`r`n[A: ffmpeg+MP4 (video, audios) | B: ffmpeg+MOV (video, audio) | C: ffmpeg+MKV (video, audios, subtitles, fonts) | D: ffmpeg+MXF (video, audios, subtitles)]") { 266 | a {$MUXops=".mp4"; Write-Output "`r`nChoosed MP4 - route A. Opening a selection window to [locate ffmpeg]`r`n"; $fmpgPath=whereisit} 267 | b {$MUXops=".mov"; Write-Output "`r`nChoosed MOV - route B. Opening a selection window to [locate ffmpeg]`r`n"; $fmpgPath=whereisit} 268 | c {$MUXops=".mkv"; Write-Output "`r`nChoosed MKV - route C. Opening a selection window to [locate ffmpeg]`r`n"; $fmpgPath=whereisit} 269 | d {$MUXops=".mxf"; Write-Output "`r`nChoosed MXF - route D. Opening a selection window to [locate ffmpeg]`r`n"; $fmpgPath=whereisit} 270 | default {Write-Warning "Bad input, try again"} 271 | } 272 | } While ($MUXops -eq "") 273 | Write-Output "√ Selected $fmpgPath`r`n" 274 | 275 | #D:\ffmpeg.exe -i "D:\video&audio.mkv" -i "D:\sub1.srt" -i "D:\video2.mp4" -map 0:v -r 25 -c:v copy -map 0:a -c:a copy -map 0:s -c:s copy -map 0:t -c:t copy -map 1:s -c:s copy -map 2:v -r 24000/1000 -c:v copy "D:\folder\multiplex.mkv" 276 | #$fmpgPath---" "-$StrmIMPAgg---------------------------------------------" "$StrmParAgg-------------------------------------------------------------------------------------------------------------------------------" "-$muxPath+$nameIn+$MUXops 277 | $mux_gen=$fmpgPath+$StrmIMPAgg+" "+$StrmParAgg+" "+"`""+$muxPath+$nameIn+$MUXops+"`"" #Note: ffmpeg prohibits single quotes 278 | 279 | #Intercept+dealk with stream/track formats that are not compliant with select multiplexing container 280 | if ((($MUXops -eq ".mp4") -or ($MUXops -eq ".mov") -or ($MUXops -eq ".mxf")) -and ($mux_gen -match "-c:t copy")) { 281 | Switch (Read-Host "Export format was choosen as MP4/MOV/MXF, but detecting font track multiplexing command(s) `"-c:t copy`".`r`nChoose to [A: remove `"-c:t copy`" | B: replace format to MKV | C: Ignore (not recommended)]") { 282 | a {$mux_gen -replace "-c:t copy", ""; Write-Output "Deleted"} 283 | b {$MUXops=".mkv"; Write-Output "Format altered"} 284 | c {Write-Output "Ignored (default)"} 285 | } 286 | } 287 | 288 | if ((($MUXops -eq ".mp4") -or ($MUXops -eq ".mov")) -and ($mux_gen -match "-c:s copy")) { 289 | Switch (Read-Host "Export format was choosen as MP4/MOV, but detecting subtitle track multiplexing command(s) `"-c:s copy`".`r`nChoose to [A: remove `"-c:s copy`" | B: replace format to MKV | C: Ignore (not recommended) |`r`nD: Copy just 1 track (alter to -c:s:0 mov_text, low success rate, if there are multiple -c:s copy, then manual redundancy is needed)]") { 290 | a {$mux_gen -replace "-c:s copy", ""; Write-Output "Deleted"} 291 | b {$MUXops=".mkv"; Write-Output "Format altered"} 292 | c {Write-Output "Ignored (default)"} 293 | d {$mux_gen -replace "-c:s copy", "-c:s:0 mov_text"; Write-Output "Altered"} 294 | } 295 | } 296 | 297 | if (($mux_gen -match "-c:v copy") -eq $false) {Write-Warning "There are no `"-c:v copy`" option found in commandline, the resulting file is likely to have no video stream"} 298 | if (($mux_gen -match "-c:a copy") -eq $false) {Write-Warning "There are no `"-c:a copy`" option found in commandline, the resulting file is likely to have no audio stream"} 299 | 300 | $utf8NoBOM=New-Object System.Text.UTF8Encoding $false #Force exporting utf-8NoBOM text codec 301 | $exptPath+="6S.「Multiplexing」.bat.txt" 302 | Write-Output "`r`n Generating 6S.「Multiplexing」.bat.txt`r`n" 303 | [IO.File]::WriteAllLines($exptPath, $mux_gen, $utf8NoBOM) #Force exporting utf-8NoBOM text codec 304 | 305 | Write-Output "The file extension is purposfully set to .txt to drive users to open and check again, to edit -map, -c:v copy, -c:a copy, -c:s copy, -c:t copy options, or altering some options similar to -c:a:0 copy to filter undersired streams/tracks`r`nIf audio/subtitle aren't aligned, go to the corresponding -map -c:? copy and add -itoffset<+/-seconds.miliseconds>" 306 | pause -------------------------------------------------------------------------------- /bbenc-ttl1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iAvoe/Batch-batch-encoder/0c1b457b3c860e9e1243dcadaf8a1ad80ab3de64/bbenc-ttl1.png -------------------------------------------------------------------------------- /bbenc-ttl2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iAvoe/Batch-batch-encoder/0c1b457b3c860e9e1243dcadaf8a1ad80ab3de64/bbenc-ttl2.png -------------------------------------------------------------------------------- /bbenc-ttl3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iAvoe/Batch-batch-encoder/0c1b457b3c860e9e1243dcadaf8a1ad80ab3de64/bbenc-ttl3.png -------------------------------------------------------------------------------- /bbenc-ttl4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iAvoe/Batch-batch-encoder/0c1b457b3c860e9e1243dcadaf8a1ad80ab3de64/bbenc-ttl4.png -------------------------------------------------------------------------------- /bbenc-ttl5en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iAvoe/Batch-batch-encoder/0c1b457b3c860e9e1243dcadaf8a1ad80ab3de64/bbenc-ttl5en.png -------------------------------------------------------------------------------- /bbenc-ttl5zh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iAvoe/Batch-batch-encoder/0c1b457b3c860e9e1243dcadaf8a1ad80ab3de64/bbenc-ttl5zh.png -------------------------------------------------------------------------------- /pp_tip_qrcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iAvoe/Batch-batch-encoder/0c1b457b3c860e9e1243dcadaf8a1ad80ab3de64/pp_tip_qrcode.png --------------------------------------------------------------------------------