Skip to main content

bpi_rs/models/
stream.rs

1use bitflags::bitflags;
2use serde::{Deserialize, Serialize};
3use serde_with::DefaultOnError;
4use serde_with::serde_as;
5
6/// 视频清晰度标识
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum VideoQuality {
9    /// 240P 极速 (仅mp4方式支持)
10    P240 = 6,
11    /// 360P 流畅
12    P360 = 16,
13    /// 480P 清晰
14    P480 = 32,
15    /// 720P 高清 (web端默认值)
16    /// B站前端需要登录才能选择,但是直接发送请求可以不登录就拿到720P的取流地址
17    P720 = 64,
18    /// 720P60 高帧率
19    P720_60 = 74,
20    /// 1080P 高清
21    P1080 = 80,
22    /// 智能修复
23    /// 仅支持dash方式
24    /// 需要fnval&12240=12240
25    Smart = 100,
26    /// 1080P+ 高码率
27    P1080Plus = 112,
28    /// 1080P60 高帧率
29    P1080_60 = 116,
30    /// 4K 超清
31    P4K = 120,
32    /// HDR 真彩色
33    HDR = 125,
34    /// 杜比视界
35    DolbyVision = 126,
36    /// 8K 超高清
37    P8K = 127,
38}
39
40impl VideoQuality {
41    pub fn as_u32(self) -> u32 {
42        self as u32
43    }
44}
45
46bitflags! {
47    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
48    pub struct Fnval: u32 {
49        const FLV          = 0; // flv格式 仅H.264编码 0
50        const MP4          = 1; // mp4格式 仅H.264编码 1
51        const DASH         = 1 << 4;  // H.264编码或H.265编码 16
52
53        const HDR          = 1 << 6;  // HDR 视频 必须为dash格式 需要qn=125 64
54        const FOURK        = 1 << 7;  // 4K 分辨率 qn=120 128
55        const DOLBY_AUDIO  = 1 << 8;  // 杜比音频 256
56        const DOLBY_VISION = 1 << 9;  // 杜比视界 512
57        const EIGHTK       = 1 << 10; //  8K 分辨率 qn=127 1024
58        const AV1          = 1 << 11; // av1 编码  2048
59
60        const AI_FIX       = 12240; // 智能修复 会顶掉其他值 只能单独使用
61    }
62}
63impl Fnval {
64    pub fn is_fourk(&self) -> bool {
65        self.contains(Fnval::FOURK) || self.contains(Fnval::EIGHTK)
66    }
67}
68
69/// 视频编码代码
70#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71pub enum VideoCodec {
72    /// AVC编码
73    Avc = 7,
74    /// HEVC编码
75    Hevc = 12,
76    /// AV1编码
77    Av1 = 13,
78}
79
80impl VideoCodec {
81    pub fn as_u32(self) -> u32 {
82        self as u32
83    }
84}
85
86/// 视频伴音音质代码
87#[derive(Debug, Clone, Copy, PartialEq, Eq)]
88pub enum AudioQuality {
89    /// 64K
90    K64 = 30216,
91    /// 132K
92    K132 = 30232,
93    /// 192K
94    K192 = 30280,
95}
96
97impl AudioQuality {
98    pub fn as_u32(self) -> u32 {
99        self as u32
100    }
101}
102
103/// 通用视频流响应数据
104#[derive(Debug, Clone, Serialize, Deserialize)]
105pub struct VideoStreamData {
106    /// 当前分辨率代码
107    pub quality: u32,
108    /// 分辨率代码列表
109    pub accept_quality: Vec<u32>,
110    /// 支持的格式
111    pub accept_format: String,
112    /// 分辨率描述
113    pub accept_description: Vec<String>,
114    /// 当前格式
115    pub format: String,
116    /// 视频编码ID
117    pub video_codecid: u32,
118
119    /// FLV/MP4 直链(可选)
120    #[serde(default, rename = "durls")]
121    pub durl: Option<Vec<Durl>>,
122    /// DASH 流(可选)
123    #[serde(default)]
124    pub dash: Option<DashStreams>,
125    /// 是否已付费
126    pub has_paid: bool,
127    /// 支持的格式列表
128    pub support_formats: Vec<SupportFormat>,
129    /// 时长,毫秒
130    #[serde(default)]
131    pub timelength: Option<u64>,
132    /// fnval参数
133    #[serde(default)]
134    pub fnval: Option<u32>,
135    /// 是否为预览
136    #[serde(default)]
137    pub is_preview: Option<u32>,
138}
139
140impl VideoStreamData {
141    /// 获取最佳格式
142    pub fn best_format(&self) -> Option<&SupportFormat> {
143        self.support_formats.iter().max_by_key(|f| f.quality)
144    }
145
146    /// 获取最佳视频流
147    pub fn best_video(&self) -> Option<&DashTrack> {
148        self.dash.as_ref().and_then(|dash| {
149            dash.video.iter().max_by(|a, b| {
150                // 分辨率 > 带宽 > 编码格式
151                let res_a = a.width * a.height;
152                let res_b = b.width * b.height;
153
154                res_a
155                    .cmp(&res_b)
156                    .then_with(|| a.bandwidth.cmp(&b.bandwidth))
157                    .then_with(|| {
158                        let codec_priority = |c: &str| {
159                            if c.starts_with("hev1") || c.starts_with("hvc1") {
160                                1
161                            } else {
162                                0
163                            }
164                        };
165                        codec_priority(&a.codecs).cmp(&codec_priority(&b.codecs))
166                    })
167            })
168        })
169    }
170
171    /// 获取最佳音频流
172    pub fn best_audio(&self) -> Option<&DashTrack> {
173        self.dash.as_ref().and_then(|dash| {
174            dash.audio.iter().max_by(|a, b| {
175                a.bandwidth
176                    .cmp(&b.bandwidth)
177                    .then_with(|| a.size.cmp(&b.size))
178            })
179        })
180    }
181
182    /// 检查是否支持DASH格式
183    pub fn supports_dash(&self) -> bool {
184        self.dash.is_some()
185    }
186
187    /// 检查是否支持直链格式
188    pub fn supports_direct_url(&self) -> bool {
189        self.durl.is_some() && !self.durl.as_ref().unwrap().is_empty()
190    }
191
192    /// 获取视频时长(秒)
193    pub fn duration_seconds(&self) -> Option<u64> {
194        self.timelength.map(|ms| ms / 1000)
195    }
196}
197
198#[derive(Debug, Clone, Serialize, Deserialize)]
199pub struct Durl {
200    /// 单位 Byte
201    pub size: u64,
202    pub ahead: String,
203    /// 毫秒
204    pub length: u64,
205    pub vhead: String,
206    /// 备用 URL
207    pub backup_url: Vec<String>,
208    /// 视频流 URL(120 分钟有效)
209    pub url: String,
210    /// 分段序号
211    pub order: u32,
212}
213
214#[derive(Debug, Clone, Serialize, Deserialize)]
215pub struct SupportFormat {
216    pub display_desc: String,
217    pub format: String,
218    pub description: String,
219    pub quality: u32,
220    pub new_description: String,
221
222    pub superscript: String,
223
224    #[serde(default)]
225    // 课程分段不存在
226    pub codecs: Vec<String>,
227
228    pub attribute: Option<u32>,
229    pub has_preview: Option<bool>,
230    pub sub_description: Option<String>,
231    pub need_login: Option<bool>,
232    pub need_vip: Option<bool>,
233}
234
235#[derive(Debug, Clone, Serialize, Deserialize)]
236pub struct DashStreams {
237    pub duration: u64,
238    pub min_buffer_time: f64,
239    pub video: Vec<DashTrack>,
240    pub audio: Vec<DashTrack>,
241    pub dolby: Option<DashDolby>,
242    pub flac: Option<DashFlac>,
243}
244
245#[derive(Debug, Clone, Serialize, Deserialize)]
246pub struct DashFlac {
247    pub display_sample_rate: String,
248    pub audio: DashTrack,
249}
250
251#[derive(Debug, Clone, Serialize, Deserialize)]
252pub struct SegmentBase {
253    pub initialization: String,
254    pub index_range: String,
255}
256
257#[serde_as]
258#[derive(Debug, Clone, Serialize, Deserialize)]
259pub struct DashDolby {
260    #[serde_as(as = "DefaultOnError")]
261    pub r#type: u32,
262    pub audio: Vec<DashTrack>,
263}
264
265#[derive(Debug, Clone, Serialize, Deserialize)]
266pub struct DashTrack {
267    pub id: u32,
268    pub base_url: String,
269    pub backup_url: Vec<String>,
270    pub bandwidth: u32,
271    pub mime_type: String,
272    pub codecs: String,
273    pub width: u32,
274    pub height: u32,
275    pub frame_rate: String,
276    pub sar: String,
277    pub start_with_sap: u32,
278    pub segment_base: SegmentBase,
279    pub codecid: u32,
280    pub size: u64,
281    pub md5: Option<String>,
282}