acgnhiki / blrec

Bilibili Live Streaming Recorder 哔哩哔哩直播录制
GNU General Public License v3.0
583 stars 40 forks source link

2.0.0B2录制HLS时仍然不能适应网络较差的情况 #217

Closed KNaiFen closed 10 months ago

KNaiFen commented 10 months ago

经常出现录了很大一个文件,但是转封装到途中遇见报错,导致剩余一大部分的内容全部被跳过的情况,尝试用-ignore_unknown-err_detect ignore_err让FFMPEG忽视错误也没用

附m3u8文件 20231218-223154-Key丶217-四驱兄弟一往无前.zip

附FFMPEG转封装时候的报错: ffmpeg -i E:\Downloads\Temp\Test\20231218-223154-Key丶217-四驱兄弟一往无前.m3u8 -c copy Output.mp4 ffmpeg version N-111424-ge6954fd087-20230712 Copyright (c) 2000-2023 the FFmpeg developers built with gcc 13.1.0 (crosstool-NG 1.25.0.196_227d99d) configuration: --prefix=/ffbuild/prefix --pkg-config-flags=--static --pkg-config=pkg-config --cross-prefix=x86_64-w64-mingw32- --arch=x86_64 --target-os=mingw32 --enable-gpl --enable-version3 --disable-debug --disable-w32threads --enable-pthreads --enable-iconv --enable-libxml2 --enable-zlib --enable-libfreetype --enable-libfribidi --enable-gmp --enable-lzma --enable-fontconfig --enable-libvorbis --enable-opencl --disable-libpulse --enable-libvmaf --disable-libxcb --disable-xlib --enable-amf --enable-libaom --enable-libaribb24 --enable-avisynth --enable-chromaprint --enable-libdav1d --enable-libdavs2 --disable-libfdk-aac --enable-ffnvcodec --enable-cuda-llvm --enable-frei0r --enable-libgme --enable-libkvazaar --enable-libass --enable-libbluray --enable-libjxl --enable-libmp3lame --enable-libopus --enable-librist --enable-libssh --enable-libtheora --enable-libvpx --enable-libwebp --enable-lv2 --enable-libvpl --enable-openal --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenh264 --enable-libopenjpeg --enable-libopenmpt --enable-librav1e --enable-librubberband --enable-schannel --enable-sdl2 --enable-libsoxr --enable-libsrt --enable-libsvtav1 --enable-libtwolame --enable-libuavs3d --disable-libdrm --disable-vaapi --enable-libvidstab --enable-vulkan --enable-libshaderc --enable-libplacebo --enable-libx264 --enable-libx265 --enable-libxavs2 --enable-libxvid --enable-libzimg --enable-libzvbi --extra-cflags=-DLIBTWOLAME_STATIC --extra-cxxflags= --extra-ldflags=-pthread --extra-ldexeflags= --extra-libs=-lgomp --extra-version=20230712 libavutil 58. 14.100 / 58. 14.100 libavcodec 60. 22.100 / 60. 22.100 libavformat 60. 10.100 / 60. 10.100 libavdevice 60. 2.101 / 60. 2.101 libavfilter 9. 8.102 / 9. 8.102 libswscale 7. 3.100 / 7. 3.100 libswresample 4. 11.100 / 4. 11.100 libpostproc 57. 2.100 / 57. 2.100 [hls @ 00000267b05b7b00] Skip ('#EXT-X-VERSION:7') [hls @ 00000267b05b7b00] Skip ('#EXT-X-DISCONTINUITY') Last message repeated 40 times [hls @ 00000267b05b7b00] Opening 'E:\Downloads\Temp\Test\20231218-223154-Key丶217-四驱兄弟,一往无前.m4s' for reading Last message repeated 1 times [h264 @ 00000267b2517200] co located POCs unavailable Input #0, hls, from 'E:\Downloads\Temp\Test\20231218-223154-Key丶217-四驱兄弟一往无前.m3u8': Duration: 01:36:18.12, start: 7102.615000, bitrate: 1 kb/s Program 0 Metadata: variant_bitrate : 0 Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, bt709), 2560x1440 [SAR 1:1 DAR 16:9], 0 kb/s, 60 fps, 60 tbr, 90k tbn (default) Metadata: variant_bitrate : 0 handler_name : VideoHandler vendor_id : [0][0][0][0] encoder : (libobs version 29.0.0) major_brand : isom minor_version : 1 compatible_brands: isommp42avc1dash Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 0 kb/s (default) Metadata: variant_bitrate : 0 handler_name : SoundHandler vendor_id : [0][0][0][0] Output #0, mp4, to 'Output.mp4': Metadata: encoder : Lavf60.10.100 Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, bt709), 2560x1440 [SAR 1:1 DAR 16:9], q=2-31, 0 kb/s, 60 fps, 60 tbr, 90k tbn (default) Metadata: variant_bitrate : 0 handler_name : VideoHandler vendor_id : [0][0][0][0] encoder : (libobs version 29.0.0) major_brand : isom minor_version : 1 compatible_brands: isommp42avc1dash Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 0 kb/s (default) Metadata: variant_bitrate : 0 handler_name : SoundHandler vendor_id : [0][0][0][0] Stream mapping: Stream #0:0 -> #0:0 (copy) Stream #0:1 -> #0:1 (copy) Press [q] to stop, [?] for help [hls @ 00000267b05b7b00] Opening 'E:\Downloads\Temp\Test\20231218-223154-Key丶217-四驱兄弟,一往无前.m4s' for reading Last message repeated 207 times [hls @ 00000267b05b7b00] Opening 'E:\Downloads\Temp\Test\20231218-223154-Key丶217-四驱兄弟,一往无前.m4s' for reading Last message repeated 40 times [mov,mp4,m4a,3gp,3g2,mj2 @ 00000267b05c1880] Found duplicated MOOV Atom. Skipped it [vist#0:0/h264 @ 00000267b264ee00] timestamp discontinuity (stream id=0): 48433333, new offset= -48433333 [mp4 @ 00000267b066ed00] Non-monotonous DTS in output stream 0:1; previous: 10714421, current: 10713550; changing to 10714422. This may result in incorrect timestamps in the output file. [hls @ 00000267b05b7b00] Opening 'E:\Downloads\Temp\Test\20231218-223154-Key丶217-四驱兄弟,一往无前.m4s' for reading Last message repeated 16 times [mov,mp4,m4a,3gp,3g2,mj2 @ 00000267b05c1880] Found duplicated MOOV Atom. Skipped it [vist#0:0/h264 @ 00000267b264ee00] timestamp discontinuity (stream id=0): 64750333, new offset= -113183666 [hls @ 00000267b05b7b00] Opening 'E:\Downloads\Temp\Test\20231218-223154-Key丶217-四驱兄弟,一往无前.m4s' for reading Last message repeated 157 times [hls @ 00000267b05b7b00] Opening 'E:\Downloads\Temp\Test\20231218-223154-Key丶217-四驱兄弟,一往无前.m4s' for reading Last message repeated 102 times [mov,mp4,m4a,3gp,3g2,mj2 @ 00000267b05c1880] Found duplicated MOOV Atom. Skipped it [vist#0:0/h264 @ 00000267b264ee00] timestamp discontinuity (stream id=0): 46949333, new offset= -160132999 [hls @ 00000267b05b7b00] Opening 'E:\Downloads\Temp\Test\20231218-223154-Key丶217-四驱兄弟,一往无前.m4s' for reading Last message repeated 21 times [NULL @ 00000267b05c1c40] Invalid NAL unit size (-2096901059 > 8653). [NULL @ 00000267b05c1c40] missing picture in access unit with size 8657 [NULL @ 00000267b05c1c40] Invalid NAL unit size (1607749757 > 10654). [NULL @ 00000267b05c1c40] missing picture in access unit with size 10658 [NULL @ 00000267b05c1c40] Invalid NAL unit size (-185673105 > 33699). [NULL @ 00000267b05c1c40] missing picture in access unit with size 33703 [NULL @ 00000267b05c1c40] Invalid NAL unit size (-1165502467 > 10433). [NULL @ 00000267b05c1c40] missing picture in access unit with size 10437 [NULL @ 00000267b05c1c40] Invalid NAL unit size (-526080001 > 8905). [NULL @ 00000267b05c1c40] missing picture in access unit with size 8909 [NULL @ 00000267b05c1c40] Invalid NAL unit size (-1646121952 > 45074). [NULL @ 00000267b05c1c40] missing picture in access unit with size 45078 [NULL @ 00000267b05c1c40] Invalid NAL unit size (1068210917 > 5731). [NULL @ 00000267b05c1c40] missing picture in access unit with size 5735 [NULL @ 00000267b05c1c40] Invalid NAL unit size (1127287948 > 6025). [NULL @ 00000267b05c1c40] missing picture in access unit with size 6029 [NULL @ 00000267b05c1c40] Invalid NAL unit size (2108500088 > 21069). [NULL @ 00000267b05c1c40] missing picture in access unit with size 21073 [NULL @ 00000267b05c1c40] Invalid NAL unit size (737729178 > 5006). [NULL @ 00000267b05c1c40] missing picture in access unit with size 5010 [NULL @ 00000267b05c1c40] Invalid NAL unit size (-658310373 > 11559). [NULL @ 00000267b05c1c40] missing picture in access unit with size 11563 [NULL @ 00000267b05c1c40] Invalid NAL unit size (-1261150402 > 37674). [NULL @ 00000267b05c1c40] missing picture in access unit with size 37678 [NULL @ 00000267b05c1c40] Invalid NAL unit size (545798410 > 8742). [NULL @ 00000267b05c1c40] missing picture in access unit with size 8746 [hls @ 00000267b05b7b00] Opening 'E:\Downloads\Temp\Test\20231218-223154-Key丶217-四驱兄弟,一往无前.m4s' for reading Last message repeated 5818 times [out#0/mp4 @ 00000267b0595780] video:500545kB audio:10135kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.182411% frame=30751 fps=3392 q=-1.0 Lsize= 511612kB time=00:08:35.48 bitrate=8130.4kbits/s speed=56.9x

Sodapopoo commented 10 months ago

这个问题我熟,这是m3u8里面某一段m4s的片段出问题了,所以之后都跳过了,以前1.13版本的原始模式合成的时候有编号,在M3U8里把ffmpeg出问题的那两排给去掉就可以继续合成了。但是现在都保存为一个文件,合成的时候每个片段也没有不一样的编号,看ffmpeg的信息看不出来出问题是哪一段啊。所以,目前这个文件只能先剪切一大段来合成,再逐步缩小范围定位出问题的片段了

建议按EXT-X-MAP:URI 来分段,第一段EXT-X-MAP:URI保存为1,第二段EXT-X-MAP:URI保存为2 然后合成出来就很容易分析出有问题的是哪段了。

这个m3u8好像就第一到第二段有问题,而且也有可能是第一段是真原画,第二段是二压导致的问题。不过这个可能性很小,即使衔接有问题,但我用beta2一个多月了也没出现这种跳过一大段的问题,倒是以前1.13的原始模式很常见

Genteure commented 10 months ago

有部分切片没有下载完整导致的解析出错。写了个脚本对着发的 m3u8 文件跑了一下,把下面这些切片从 m3u8 文件里移除应该就行了。

61944801.m4s
预期大小: 1096903
实际大小: 524288

61945814.m4s
预期大小: 1052949
实际大小: 1048576

61946557.m4s
预期大小: 1039794
实际大小: 524288

61946890.m4s
预期大小: 1126384
实际大小: 524288

61947421.m4s
预期大小: 1216022
实际大小: 524288

61948885.m4s
预期大小: 953567
实际大小: 524288

61950165.m4s
预期大小: 998320
实际大小: 524288

61950754.m4s
预期大小: 1026585
实际大小: 524288

61952160.m4s
预期大小: 908610
实际大小: 524288
KNaiFen commented 10 months ago

@Genteure 的处理办法太牛啦,已经成功转出好的MP4了,录播有救啦! QQ截图20231219200819

Sodapopoo commented 10 months ago

@Genteure 求问,只用m3u8分析怎么办到的? 还有分析m3u8的脚本能不能共享一下,我也想要一个,求

检测出有问题的片段疑似有点多了,一般即使有损坏也只会损坏一个片段,删去那一个片段就可以了

ffmpeg提示的信息:

[hls @ 00000267b05b7b00] Opening 'E:\Downloads\Temp\Test\20231218-223154-Key丶217-四驱兄弟,一往无前.m4s' for reading
Last message repeated 21 times

如果合成的片段有单独的编号的话,报错的那个片段前一个就是报错的片段

Genteure commented 10 months ago
#EXTINF:1,10bcc7|21a44cea|61944801.m4s
#EXT-X-BYTERANGE:524288@523950637
20231218-223154-Key丶217-四驱兄弟,一往无前.m4s
#EXTINF:1,10bcc7|21a44cea|61944801.m4s
#EXTINF:切片长度,切片标题(任意字符内容)

切片标题里 10bcc7|21a44cea|61944801.m4s

#EXT-X-BYTERANGE:524288@523950637
#EXT-X-BYTERANGE:length@offset

把标题里服务器提供的文件长度读出来和实际录到的文件长度对比一下就有了。奶粉他用 python 自己写了一个加到他自己后处理流程里了。

最根本的解决方法是在 blrec 里就对下载下来的数据做校验,校验不通过的切片判定为下载失败,然后重试或者按缺少处理。

我昨天在 QQ 上让奶粉给我看一下下载出问题的切片里面内容是啥,他还没发。

我用的脚本(用 copilot 写的,逻辑写的比较简单):

import { readFile } from 'fs/promises';

interface RawSegment {
    extinf: string
    byteRange: string
    uri: string
}

async function readLines() {
    const data = await readFile('./20231218-223154-Key丶217-四驱兄弟一往无前.m3u8', 'utf-8');
    const lines = data.split('\n');
    return lines;
}

function parseSegments(lines: string[]): RawSegment[] {
    const segments: RawSegment[] = [];
    let current: RawSegment = { extinf: '', byteRange: '', uri: '' };
    for (const line of lines) {
        if (line.startsWith('#EXTINF:')) {
            current.extinf = line;
        } else if (line.startsWith('#EXT-X-BYTERANGE:')) {
            current.byteRange = line;
        } else if (!line.startsWith('#')) {
            current.uri = line;
            segments.push(current);
            current = { extinf: '', byteRange: '', uri: '' };
        }
    }
    return segments;
}

interface Segment {
    duration: string
    expectedSize: number
    crc32: string
    originalFilename: string
    length: number
    offset: string
    uri: string
}

function parseRawSegment(rawSegment: RawSegment): Segment {
    /*
    #EXTINF:1,10d3a6|9c0cdd5|61944082.m4s
    #EXT-X-BYTERANGE:1102758@939969
    20231218-223154-Key丶217-四驱兄弟,一往无前.m4s
    */

    const duration = rawSegment.extinf.split(':')[1].split(',')[0];
    const [origLengthStr, crc32, originalFilename] = rawSegment.extinf.split(',')[1].split('|');
    const [length, offset] = rawSegment.byteRange.replace('#EXT-X-BYTERANGE:', '').split('@');
    const expectedSize = parseInt(origLengthStr, 16);
    const uri = rawSegment.uri;

    return {
        duration,
        expectedSize,
        crc32,
        originalFilename,
        length: parseInt(length),
        offset,
        uri
    };
}

async function main() {
    const lines = await readLines();
    console.log(lines.length);
    const rawSegments = parseSegments(lines);
    console.log(rawSegments.length);
    const segments = rawSegments.map(parseRawSegment)
    console.log(segments.length);

    const mismatchedSegments = segments.filter(segment => segment.length !== segment.expectedSize);
    console.log(mismatchedSegments.length);
    console.log(mismatchedSegments);

    for (const segment of mismatchedSegments) {
        console.log(segment.originalFilename)
        console.log(`预期大小: ${segment.expectedSize}`);
        console.log(`实际大小: ${segment.length}`);
        // console.log(`BYTERANGE    : ${segment.offset}`);
        console.log();
    }
}

main();

保存成 .ts 文件然后 npx tsm ./run.ts 运行。

Genteure commented 10 months ago

或者解析一下文件里结构是否符合 MP4 的标准也可以。只是简单检测文件是否完整的话写起来很简单,只需要读长度、seek、读长度、seek直到文件结尾,然后看是否刚好是文件的最后一个字节就ok。如果长度超过文件结尾了就说明文件不完整。