Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

提供一个为原画(10000)优先重试已有playurl的选项 #446

Open
gin3715 opened this issue Dec 24, 2022 · 8 comments
Open

提供一个为原画(10000)优先重试已有playurl的选项 #446

gin3715 opened this issue Dec 24, 2022 · 8 comments
Labels
area: Core enhancement New feature or request

Comments

@gin3715
Copy link

gin3715 commented Dec 24, 2022

Problem

由于B站会对原画画质进行二压,生成_bluray.flv?结尾的playurl,所以开播后重连会导致只能录制二压画质。

目前如果监测到断开,录播姬似乎会重新获取playurl并使用新的playurl,但如果重连发生在获取初始playurl的一个小时内(链接的过期时间),则一般可以直接继续用已有的playurl重连。这样就可以避免因为直播开始的时候的网络波动导致录不到原画,只能录制二压画质,一定程度上提高录制到原画画质的几率。(超出1个小时则同样只能获取二压画质)

Proposal

添加一个优先使用重试playurl的(高级)选项,当选项启用时:

缓存当前房间的playurl,并且当重试连接的时候

  1. 如果画质不为10000,或者当前已经是二压画质,则获取并使用新的playurl
  2. 如果已有的playurl已过期(检查UrlInfoItem["extra"]的query string中的expires=xxxxx参数),则获取并使用新的playurl
  3. 如果已有的playurl没有过期,则
    a. 如果新获取的playurl是以_bluray.flv?结尾的二压链接,则优先尝试用已有playurl重连,直至超出重试上限
    b. 如果新获取的playurl是live_B站UID_随机数.flv的真实原画链接,则使用新的playurl
@Genteure Genteure added enhancement New feature or request area: Core labels Dec 25, 2022
@xiaoyaoyaowww
Copy link

xiaoyaoyaowww commented Jan 12, 2023

由于B站会对原画画质进行二压,生成_bluray.flv?结尾的playurl,所以开播后重连会导致只能录制二压画质。

其实只要把live_B站UID_随机数_bluray.flv?中的_bluray去掉即可实现原生画质
一般情况下主播刚开播时是可以直接获取到无二压的画质就是这个原理(包括从网页看的观众,只要不切换画质都始终是真原画)
所以应该恳请开发者提供手动去除 _bluray 的选项吧?

另外,这个真原画必须要使用反代,要不然b站的省级CDN是不稳定的,录制的话大概率是会有画面缺失

@gin3715
Copy link
Author

gin3715 commented Jan 12, 2023

其实只要把live_B站UID_随机数_bluray.flv?中的_bluray去掉即可实现原生画质

playurl包括了sign=xxxxx校验的query string,去掉结尾的_bluray部分并不能直接获得可用的原画链接(会返回HTTP 403),这也是为什么录播姬的文档里说,对于会二压的直播间,需要开始直播后立刻开始录制。

这个真原画必须要使用反代,要不然b站的省级CDN是不稳定的,录制的话大概率是会有画面缺失

开启自动录制的录播姬,当前录制的就是这个原画画质,除非中途有网络波动导致重连,或是手动配置了录制其他画质。
另外这个“省级CDN”是相对于什么而言的?Akamai的CDN吗?

@Genteure
Copy link
Member

在用户脚本里加一个存数据的接口,具体的这个判断逻辑用脚本实现可以吗?

用户脚本文档:

大概的伪代码例子:

// 给录播姬的 javascript runtime 加一个类似 localStorage 的接口
const sharedState = {
    key() {
    },
    getItem(key) {
    },
    setItem(key, value) {
    },
    removeItem(key) {
    },
    clear() {
    },
}

recorderEvents = {
    onFetchStreamUrl({ roomid }) {
        let oldUrl = sharedState.getItem('playurl:room:' + roomid);
        if (oldUrl) {
            // 有旧的播放地址,检查是否过期
            const expire = 0 // TODO: 从 oldUrl 中解析出过期时间
            if (expire > Date.now()) {
                return oldUrl;
            } else {
                sharedState.removeItem('playurl:room:' + roomid);
            }
        }

        // 重新获取播放地址
        const resp = fetchSync(...);
        if (!resp.ok) {
            return null;
        }

        const playurl = "从 resp.body 里解析";

        // 检查是否是真原画并保存
        if (!(/_blueray/.test(playurl))) {
            sharedState.setItem('playurl:room:' + roomid, playurl);
        }

        return playurl;
    }
}

@gin3715
Copy link
Author

gin3715 commented Jan 13, 2023

在用户脚本里加一个存数据的接口,具体的这个判断逻辑用脚本实现可以吗?

感觉应该可以

另外如果用onTransformStreamUrl的话,脚本大概是这样的吗:

const getOldPlayurl = (uid) => {
    const oldUrl = sharedStorage.getItem('playurl:uid:' + uid);
    if (oldUrl) {
        const expires = new URL(oldUrl).searchParams.get('expires');
        if (Number(expires) > (Date.now()/1000) + 10) {
            return oldUrl;
        } else {
            console.debug('playurl expired');
            sharedStorage.removeItem('playurl:uid:' + uid);
        }
    }
    return null;
}

recorderEvents = {
    onTransformStreamUrl(originalUrl) {
        if (!/^https?:\/\/[^/]+\.bilivideo\.com/.test(originalUrl)) return originalUrl;
        console.debug('to transform url', originalUrl);
        try {
            const uid = /\/live-bvc\/\d+\/live_(\d+)_/.exec(originalUrl)[1];
            console.debug('parsed room owner uid', uid);
            const oldUrl = getOldPlayurl(uid);
            console.debug('obtained old playurl', oldUrl);
            let playurl;
            if (oldUrl && (/_bluray/.test(originalUrl))) {
                console.debug('reuse old url');
                playurl = oldUrl;
            } else {
                console.debug('keep current url');
                playurl = originalUrl;
            }

            if (!(/_bluray/.test(playurl))) {
                sharedStorage.setItem('playurl:uid:' + uid, playurl);
                console.debug('saving playurl', playurl);
            }
            return playurl;

        } catch (e) {
            console.error(e);
            return originalUrl;
        }
    }
}

@Genteure
Copy link
Member

Genteure commented Jan 14, 2023

加上了,可以在本仓库的 GitHub Actions 里下面 Artifacts 下载最新的 dev build

参考脚本: https://github.com/BililiveRecorder/recorder-scripting-template/blob/main/examples/reuse-old-url.js

API: https://github.com/BililiveRecorder/recorder-scripting-template/blob/6c5c1bef674dab8c4cf62fc1b880dc7bbb78a6a6/recorder.d.ts#L49-L58

@gin3715
Copy link
Author

gin3715 commented Jan 15, 2023

好的,辛苦了

@gin3715 gin3715 closed this as completed Jan 15, 2023
@komori-flag
Copy link

其实只要把live_B站UID_随机数_bluray.flv?中的_bluray去掉即可实现原生画质

playurl包括了校验的查询字符串,去掉结尾的部分并不能直接获得可用的原画链接(会返回HTTP 403),这也是为什么录播姬的文档里说,对于会二压的直播间,需要开始直播后立刻开始录制。sign=xxxxx``_bluray

这个真原画必须要使用反代,要不然b站的省级CDN是不稳定的,录制的话大概率是会有画面缺失

开启自动录制的录播姬,当前录制的就是这个原画画质,除非中途有网络波动导致重连,或是手动配置了录制其他画质。 另外这个“省级CDN”是相对于什么而言的?Akamai的CDN吗?

“省级CDN”是B站为了降低带宽成本而自建的服务器,其作用是用来分担“主CDN服务商”的带宽占用,一般是以“cn(中国简称)-gddg(省份城市缩写)-ct(服务器所使用的网络运营商简称)-01-01.bilivideo.com”的域名为主。

https://rec.danmuji.org/dev/bilibili-cdn/
这里是所搜集到的目前存在的B站直播CDN域名,里面的“B站视频云”就是他们所讨论到的“省级CDN”

@gin3715
Copy link
Author

gin3715 commented Jun 29, 2023

遇到了这样的一个情况:在主播重启推流后,原flv链接会反复发送重复数据,直至重复10次触发修复系统断开录制。此时必须更换新的flv链接才能获得重启的推流。而复用旧的flv链接会导致直至flv过期前,录播姬反复获取用旧flv链接获取结尾的重复片段,无法录制到重开的推流。
请问是否可能添加一个录制结束时的用户脚本接口,提供直播间的context,以及录制结束的原因,可以在触发重复而断开时,清除保存的flv链接?

onRecordingStop(roomContext, reason) => null

if (this.context.Actions.FirstOrDefault(x => x is PipelineDisconnectAction) is PipelineDisconnectAction disconnectAction)
{
this.logger.Information("修复系统断开录制:{Reason}", disconnectAction.Reason);
break;
}

@gin3715 gin3715 reopened this Jun 29, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: Core enhancement New feature or request
Development

No branches or pull requests

4 participants