H264解析SPS获取宽高等信息

2023-11-09 05:32

本文主要是介绍H264解析SPS获取宽高等信息,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在做音视频开发的时候,存在不解码视频帧的前提下需要获取视频宽高、帧率等信息,而H.264中的SPS数据可为我们提供这些相关的信息。在此之前,我们需要对一些协议和算法有一定的初步了解,后文中有完整的代码展示。

H.264协议

我们在此不讲解H.264协议,但是我们需要了解NAL单元语法(NAL unit syntax)、序列参数集数据语法(Sequence parameter set data syntax)、视频可用参数语法(VUI parameters syntax)。具体可去ITU-T官网下载H.264文档,我查看的是T-REC-H.264-201704-I!!PDF-E.pdf。

Sequence parameter set data syntax

VUI parameters syntax


第一列:相关参数信息名称;

第二列:规定条带数据可以至多划分为三种条带数据类别(这个可以不用理解);

第三列中的u(n)表示:使用n位无符号整数表示,由n位bit换算得到,即从左到右读取n个bit位所表示的值;ue(v)表示:无符号指数哥伦布编码值;ue(v)表示:有符号指数哥伦布编码值。

u(n)代码实现:

  1. static UINT u(sps_bit_stream *bs, BYTE bitCount)
  2. {
  3. UINT val = 0;
  4. for (BYTE i=0; i<bitCount; i++) {
  5. val <<= 1;
  6. if (eof(bs)) {
  7. val = 0;
  8. break;
  9. } else if (bs->data[bs->index / 8] & (0x80 >> (bs->index % 8))) { //计算index所在的位是否为1
  10. val |= 1;
  11. }
  12. bs->index++;
  13. }
  14. return val;
  15. }

指数哥伦布编码

Golomb编码是一种无损的数据压缩方法,由数学家Solomon W.Golomb在1960年代发明。Golomb编码只能对非负整数进行编码,符号表中的符号出现的概率符合几何分布(Geometric Distribution)时,使用Golomb编码可以取得最优效果,也就是说Golomb编码比较适合小的数字比大的数字出现概率比较高的编码。它使用较短的码长编码较小的数字,较长的码长编码较大的数字。在此,我们只需要了解:无符号指数哥伦布编码ue(v)、有符号指数哥伦布编码se(v)。

无符号指数哥伦布编码(UE)

哥伦布编码的码字code_word由三部分组成:code_word = [M个0] + [1] + [Info]
其中,Info是一个携带信息的M位数据,每个哥伦布码的长度为(2M+1)位,每个码字都可由code_num产生。
根据码字code_word解码出code_num值的过程如下:
1. 首先读入M位以"1"为结尾的0;
2. 根据得到的M,读入接下来的M位Info数据;

3. 根据这个公式得到计算结果code_num = Info – 1 + 2M

代码实现:

  1. static UINT ue(sps_bit_stream *bs)
  2. {
  3. UINT zeroNum = 0;
  4. while (u(bs, 1) == 0 && !eof(bs) && zeroNum < 32) {
  5. zeroNum ++;
  6. }
  7. return (UINT)((1 << zeroNum) - 1 + u(bs, zeroNum));
  8. }

有符号指数哥伦布编码(SE)

有符号的指数哥伦布编码值是通过无符号的指数哥伦布编码的值通过换算得到的,其换算关系为:n = (-1)^(k+1) * ceil(k/2)。

代码实现:

  1. INT se(sps_bit_stream *bs)
  2. {
  3. INT ueVal = (INT)ue(bs);
  4. double k = ueVal;
  5. INT seVal = (INT)ceil(k / 2); //ceil:返回大于或者等于指定表达式的最小整数
  6. if (ueVal % 2 == 0) { //偶数取反,即(-1)^(k+1)
  7. seVal = -seVal;
  8. }
  9. return seVal;
  10. }

SPS参数分析

视频宽高(Width、Height)获取

从H.264协议文档中可以看出视频宽高涉及到的参数有:

pic_width_in_mbs_minus1加1指定以宏块(16*16)为单位的每个解码图像的宽度,即PicWidthInSamplesL = (pic_width_in_mbs_minus1 + 1) * 16 ;


pic_height_in_map_units_minus1:加1指定解码帧或场中的一个切片组的高度,即PicSizeInMapUnits = PicWidthInMbs * (pic_height_in_map_units_minus1 + 1) ;


frame_mbs_only_flag:等于0指明了视频序列的编码图象可能是编码场或编码帧,等于1指明了每个编码视频序列的编码图像都是只含帧宏块的编码帧,即FrameHeightInMbs = ( 2 − frame_mbs_only_flag ) * PicHeightInMapUnits ;

frame_cropping_flag:等于0表明不存在帧剪切偏移量,等于1表明在sps中下一个使用的帧剪切偏移量参数,即需要使用到frame_crop_left_offset、frame_crop_right_offset、frame_crop_top_offset、frame_crop_bottom_offset四个参数;

frame_crop_left_offset、frame_crop_right_offset、frame_crop_top_offset、frame_crop_bottom_offset:左右上下帧裁剪偏移量,需要根据chroma format计算实际的视频宽高;


chroma_format_idc:亮度取样对应的色度取样值,包括0~3,不存在时默认为1(即YUV420);


根据上述参数的定义说明,计算视频分辨率存在多种情况:1、需要判断是否包含宏片的编码帧或场;2、需要判断视频帧是否被裁剪;3、需要判断采样类型。因此代码大致如下:

  1. UINT pic_width_in_mbs_minus1 = ue(&bs); //第36位开始
  2. UINT pic_height_in_map_units_minus1 = ue(&bs); //47
  3. UINT frame_mbs_only_flag = u(&bs, 1);
  4. info->width = (INT)(pic_width_in_mbs_minus1 + 1) * 16;
  5. info->height = (INT)(2 - frame_mbs_only_flag) * (pic_height_in_map_units_minus1 + 1) * 16;
  6. if (!frame_mbs_only_flag) {
  7. u(&bs, 1); //mb_adaptive_frame_field_flag
  8. }
  9. u(&bs, 1); //direct_8x8_inference_flag
  10. UINT frame_cropping_flag = u(&bs, 1);
  11. if (frame_cropping_flag) {
  12. UINT frame_crop_left_offset = ue(&bs);
  13. UINT frame_crop_right_offset = ue(&bs);
  14. UINT frame_crop_top_offset = ue(&bs);
  15. UINT frame_crop_bottom_offset= ue(&bs);
  16. //See 6.2 Source, decoded, and output picture formats
  17. INT crop_unit_x = 1;
  18. INT crop_unit_y = 2 - frame_mbs_only_flag; //monochrome or 4:4:4
  19. if (chroma_format_idc == 1) { //4:2:0
  20. crop_unit_x = 2;
  21. crop_unit_y = 2 * (2 - frame_mbs_only_flag);
  22. } else if (chroma_format_idc == 2) { //4:2:2
  23. crop_unit_x = 2;
  24. crop_unit_y = 2 - frame_mbs_only_flag;
  25. }
  26. info->width -= crop_unit_x * (frame_crop_left_offset + frame_crop_right_offset);
  27. info->height -= crop_unit_y * (frame_crop_top_offset + frame_crop_bottom_offset);
  28. }

视频帧率(FPS)获取

视频帧率信息在SPS的VUI parameters syntax中,需要根据time_scale、fixed_frame_rate_flag计算得到:fps = time_scale / num_units_in_tick。但是需要判断参数timing_info_present_flag是否存在,若不存在表示FPS在信息流中无法获取。同时还存在另外一种情况:fixed_frame_rate_flag为1时,两个连续图像的HDR输出时间频率为单位,获取的fps是实际的2倍。

  1. UINT timing_info_present_flag = u(bs, 1);
  2. if (timing_info_present_flag) {
  3. UINT num_units_in_tick = u(bs, 32);
  4. UINT time_scale = u(bs, 32);
  5. UINT fixed_frame_rate_flag = u(bs, 1);
  6. info->fps = (UINT)((float)time_scale / (float)num_units_in_tick);
  7. if (fixed_frame_rate_flag) {
  8. info->fps = info->fps/2;
  9. }
  10. }

SPS解析代码

H264ParseSPS.h

//
//  H264ParseSPS.h
//
//  Created by lzj<lizhijian_21@163.com> on 2018/7/6.
//  Copyright © 2018年 LZJ. All rights reserved.
//

#ifndef H264ParseSPS_h
#define H264ParseSPS_h
#ifdef __cplusplus
extern “C”
{
#endif
#include <stdio.h>

typedef struct
{

unsigned int profile_idc;
unsigned int level_idc;

<span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> width;
<span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> height;
<span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> fps;       <span class="hljs-comment">//SPS中可能不包含FPS信息</span>

} sps_info_struct;

/**
解析SPS数据信息

@param data SPS数据内容,需要Nal类型为0x7数据的开始(比如:67 42 00 28 ab 40 22 01 e3 cb cd c0 80 80 a9 02)
@param dataSize SPS数据的长度
@param info SPS解析之后的信息数据结构体
@return success:1,fail:0

*/
int h264_parse_sps(const unsigned char *data, unsigned int dataSize, sps_info_struct *info);

#ifdef __cplusplus
}
#endif
#endif /* H264ParseSPS_h */

H264ParseSPS.c

  1. //
  2. // H264ParseSPS.c
  3. //
  4. // Created by lzj<lizhijian_21@163.com> on 2018/7/6.
  5. // Copyright © 2018年 LZJ. All rights reserved.
  6. //
  7. // See https://www.itu.int/rec/T-REC-H.264-201610-S
  8. //
  9. #include “H264ParseSPS.h”
  10. #include <string.h>
  11. #include <stdlib.h>
  12. #include <math.h>
  13. typedef unsigned char BYTE;
  14. typedef int INT;
  15. typedef unsigned int UINT;
  16. typedef struct
  17. {
  18. const BYTE *data; //sps数据
  19. UINT size; //sps数据大小
  20. UINT index; //当前计算位所在的位置标记
  21. } sps_bit_stream;
  22. /
  23. 移除H264的NAL防竞争码(0x03)
  24. @param data sps数据
  25. @param dataSize sps数据大小
  26. */
  27. static void del_emulation_prevention(BYTE *data, UINT *dataSize)
  28. {
  29. UINT dataSizeTemp = *dataSize;
  30. for (UINT i=0, j=0; i<(dataSizeTemp-2); i++) {
  31. int val = (data[i]^0x0) + (data[i+1]^0x0) + (data[i+2]^0x3); //检测是否是竞争码
  32. if (val == 0) {
  33. for (j=i+2; j<dataSizeTemp-1; j++) { //移除竞争码
  34. data[j] = data[j+1];
  35. }
  36. (*dataSize)–; //data size 减1
  37. }
  38. }
  39. }
  40. static void sps_bs_init(sps_bit_stream *bs, const BYTE *data, UINT size)
  41. {
  42. if (bs) {
  43. bs->data = data;
  44. bs->size = size;
  45. bs->index = 0;
  46. }
  47. }
  48. /
  49. 是否已经到数据流最后
  50. @param bs sps_bit_stream数据
  51. @return 1:yes,0:no
  52. */
  53. static INT eof(sps_bit_stream *bs)
  54. {
  55. return (bs->index >= bs->size * 8); //位偏移已经超出数据
  56. }
  57. /
  58. 读取从起始位开始的BitCount个位所表示的值
  59. @param bs sps_bit_stream数据
  60. @param bitCount bit位个数(从低到高)
  61. @return value
  62. */
  63. static UINT u(sps_bit_stream *bs, BYTE bitCount)
  64. {
  65. UINT val = 0;
  66. for (BYTE i=0; i<bitCount; i++) {
  67. val <<= 1;
  68. if (eof(bs)) {
  69. val = 0;
  70. break;
  71. } else if (bs->data[bs->index / 8] & (0x80 >> (bs->index % 8))) { //计算index所在的位是否为1
  72. val |= 1;
  73. }
  74. bs->index++; //递增当前起始位(表示该位已经被计算,在后面的计算过程中不需要再次去计算所在的起始位索引,缺点:后面每个bit位都需要去位移)
  75. }
  76. return val;
  77. }
  78. /
  79. 读取无符号哥伦布编码值(UE)
  80. #2^LeadingZeroBits - 1 + (xxx)
  81. @param bs sps_bit_stream数据
  82. @return value
  83. */
  84. static UINT ue(sps_bit_stream *bs)
  85. {
  86. UINT zeroNum = 0;
  87. while (u(bs, 1) == 0 && !eof(bs) && zeroNum < 32) {
  88. zeroNum ++;
  89. }
  90. return (UINT)((1 << zeroNum) - 1 + u(bs, zeroNum));
  91. }
  92. /
  93. 读取有符号哥伦布编码值(SE)
  94. #(-1)^(k+1) * Ceil(k/2)
  95. @param bs sps_bit_stream数据
  96. @return value
  97. */
  98. INT se(sps_bit_stream *bs)
  99. {
  100. INT ueVal = (INT)ue(bs);
  101. double k = ueVal;
  102. INT seVal = (INT)ceil(k / 2); //ceil:返回大于或者等于指定表达式的最小整数
  103. if (ueVal % 2 == 0) { //偶数取反,即(-1)^(k+1)
  104. seVal = -seVal;
  105. }
  106. return seVal;
  107. }
  108. /
  109. 视频可用性信息(Video usability information)解析
  110. @param bs sps_bit_stream数据
  111. @param info sps解析之后的信息数据及结构体
  112. @see E.1.1 VUI parameters syntax
  113. */
  114. void vui_para_parse(sps_bit_stream *bs, sps_info_struct *info)
  115. {
  116. UINT aspect_ratio_info_present_flag = u(bs, 1);
  117. if (aspect_ratio_info_present_flag) {
  118. UINT aspect_ratio_idc = u(bs, 8);
  119. if (aspect_ratio_idc == 255) { //Extended_SAR
  120. u(bs, 16); //sar_width
  121. u(bs, 16); //sar_height
  122. }
  123. }
  124. UINT overscan_info_present_flag = u(bs, 1);
  125. if (overscan_info_present_flag) {
  126. u(bs, 1); //overscan_appropriate_flag
  127. }
  128. UINT video_signal_type_present_flag = u(bs, 1);
  129. if (video_signal_type_present_flag) {
  130. u(bs, 3); //video_format
  131. u(bs, 1); //video_full_range_flag
  132. UINT colour_description_present_flag = u(bs, 1);
  133. if (colour_description_present_flag) {
  134. u(bs, 8); //colour_primaries
  135. u(bs, 8); //transfer_characteristics
  136. u(bs, 8); //matrix_coefficients
  137. }
  138. }
  139. UINT chroma_loc_info_present_flag = u(bs, 1);
  140. if (chroma_loc_info_present_flag) {
  141. ue(bs); //chroma_sample_loc_type_top_field
  142. ue(bs); //chroma_sample_loc_type_bottom_field
  143. }
  144. UINT timing_info_present_flag = u(bs, 1);
  145. if (timing_info_present_flag) {
  146. UINT num_units_in_tick = u(bs, 32);
  147. UINT time_scale = u(bs, 32);
  148. UINT fixed_frame_rate_flag = u(bs, 1);
  149. info->fps = (UINT)((float)time_scale / (float)num_units_in_tick);
  150. if (fixed_frame_rate_flag) {
  151. info->fps = info->fps/2;
  152. }
  153. }
  154. UINT nal_hrd_parameters_present_flag = u(bs, 1);
  155. if (nal_hrd_parameters_present_flag) {
  156. //hrd_parameters() //see E.1.2 HRD parameters syntax
  157. }
  158. //后面代码需要hrd_parameters()函数接口实现才有用
  159. UINT vcl_hrd_parameters_present_flag = u(bs, 1);
  160. if (vcl_hrd_parameters_present_flag) {
  161. //hrd_parameters()
  162. }
  163. if (nal_hrd_parameters_present_flag || vcl_hrd_parameters_present_flag) {
  164. u(bs, 1); //low_delay_hrd_flag
  165. }
  166. u(bs, 1); //pic_struct_present_flag
  167. UINT bitstream_restriction_flag = u(bs, 1);
  168. if (bitstream_restriction_flag) {
  169. u(bs, 1); //motion_vectors_over_pic_boundaries_flag
  170. ue(bs); //max_bytes_per_pic_denom
  171. ue(bs); //max_bits_per_mb_denom
  172. ue(bs); //log2_max_mv_length_horizontal
  173. ue(bs); //log2_max_mv_length_vertical
  174. ue(bs); //max_num_reorder_frames
  175. ue(bs); //max_dec_frame_buffering
  176. }
  177. }
  178. //See 7.3.1 NAL unit syntax
  179. //See 7.3.2.1.1 Sequence parameter set data syntax
  180. INT h264_parse_sps(const BYTE *data, UINT dataSize, sps_info_struct *info)
  181. {
  182. if (!data || dataSize <= 0 || !info) return 0;
  183. INT ret = 0;
  184. BYTE *dataBuf = malloc(dataSize);
  185. memcpy(dataBuf, data, dataSize); //重新拷贝一份数据,防止移除竞争码时对原数据造成影响
  186. del_emulation_prevention(dataBuf, &dataSize);
  187. sps_bit_stream bs = {0};
  188. sps_bs_init(&bs, dataBuf, dataSize); //初始化SPS数据流结构体
  189. u(&bs, 1); //forbidden_zero_bit
  190. u(&bs, 2); //nal_ref_idc
  191. UINT nal_unit_type = u(&bs, 5);
  192. if (nal_unit_type == 0x7) { //Nal SPS Flag
  193. info->profile_idc = u(&bs, 8);
  194. u(&bs, 1); //constraint_set0_flag
  195. u(&bs, 1); //constraint_set1_flag
  196. u(&bs, 1); //constraint_set2_flag
  197. u(&bs, 1); //constraint_set3_flag
  198. u(&bs, 1); //constraint_set4_flag
  199. u(&bs, 1); //constraint_set4_flag
  200. u(&bs, 2); //reserved_zero_2bits
  201. info->level_idc = u(&bs, 8);
  202. ue(&bs); //seq_parameter_set_id
  203. UINT chroma_format_idc = 1; //摄像机出图大部分格式是4:2:0
  204. if (info->profile_idc == 100 || info->profile_idc == 110 || info->profile_idc == 122 ||
  205. info->profile_idc == 244 || info->profile_idc == 44 || info->profile_idc == 83 ||
  206. info->profile_idc == 86 || info->profile_idc == 118 || info->profile_idc == 128 ||
  207. info->profile_idc == 138 || info->profile_idc == 139 || info->profile_idc == 134 || info->profile_idc == 135) {
  208. chroma_format_idc = ue(&bs);
  209. if (chroma_format_idc == 3) {
  210. u(&bs, 1); //separate_colour_plane_flag
  211. }
  212. ue(&bs); //bit_depth_luma_minus8
  213. ue(&bs); //bit_depth_chroma_minus8
  214. u(&bs, 1); //qpprime_y_zero_transform_bypass_flag
  215. UINT seq_scaling_matrix_present_flag = u(&bs, 1);
  216. if (seq_scaling_matrix_present_flag) {
  217. UINT seq_scaling_list_present_flag[8] = {0};
  218. for (INT i=0; i<((chroma_format_idc != 3)?8:12); i++) {
  219. seq_scaling_list_present_flag[i] = u(&bs, 1);
  220. if (seq_scaling_list_present_flag[i]) {
  221. if (i < 6) { //scaling_list(ScalingList4x4[i], 16, UseDefaultScalingMatrix4x4Flag[i])
  222. } else { //scaling_list(ScalingList8x8[i − 6], 64, UseDefaultScalingMatrix8x8Flag[i − 6] )
  223. }
  224. }
  225. }
  226. }
  227. }
  228. ue(&bs); //log2_max_frame_num_minus4
  229. UINT pic_order_cnt_type = ue(&bs);
  230. if (pic_order_cnt_type == 0) {
  231. ue(&bs); //log2_max_pic_order_cnt_lsb_minus4
  232. } else if (pic_order_cnt_type == 1) {
  233. u(&bs, 1); //delta_pic_order_always_zero_flag
  234. se(&bs); //offset_for_non_ref_pic
  235. se(&bs); //offset_for_top_to_bottom_field
  236. UINT num_ref_frames_in_pic_order_cnt_cycle = ue(&bs);
  237. INT *offset_for_ref_frame = (INT )malloc((UINT)num_ref_frames_in_pic_order_cnt_cycle * sizeof(INT));
  238. for (INT i = 0; i<num_ref_frames_in_pic_order_cnt_cycle; i++) {
  239. offset_for_ref_frame[i] = se(&bs);
  240. }
  241. free(offset_for_ref_frame);
  242. }
  243. ue(&bs); //max_num_ref_frames
  244. u(&bs, 1); //gaps_in_frame_num_value_allowed_flag
  245. UINT pic_width_in_mbs_minus1 = ue(&bs); //第36位开始
  246. UINT pic_height_in_map_units_minus1 = ue(&bs); //47
  247. UINT frame_mbs_only_flag = u(&bs, 1);
  248. info->width = (INT)(pic_width_in_mbs_minus1 + 1) * 16;
  249. info->height = (INT)(2 - frame_mbs_only_flag) * (pic_height_in_map_units_minus1 + 1) * 16;
  250. if (!frame_mbs_only_flag) {
  251. u(&bs, 1); //mb_adaptive_frame_field_flag
  252. }
  253. u(&bs, 1); //direct_8x8_inference_flag
  254. UINT frame_cropping_flag = u(&bs, 1);
  255. if (frame_cropping_flag) {
  256. UINT frame_crop_left_offset = ue(&bs);
  257. UINT frame_crop_right_offset = ue(&bs);
  258. UINT frame_crop_top_offset = ue(&bs);
  259. UINT frame_crop_bottom_offset= ue(&bs);
  260. //See 6.2 Source, decoded, and output picture formats
  261. INT crop_unit_x = 1;
  262. INT crop_unit_y = 2 - frame_mbs_only_flag; //monochrome or 4:4:4
  263. if (chroma_format_idc == 1) { //4:2:0
  264. crop_unit_x = 2;
  265. crop_unit_y = 2 * (2 - frame_mbs_only_flag);
  266. } else if (chroma_format_idc == 2) { //4:2:2
  267. crop_unit_x = 2;
  268. crop_unit_y = 2 - frame_mbs_only_flag;
  269. }
  270. info->width -= crop_unit_x * (frame_crop_left_offset + frame_crop_right_offset);
  271. info->height -= crop_unit_y * (frame_crop_top_offset + frame_crop_bottom_offset);
  272. }
  273. UINT vui_parameters_present_flag = u(&bs, 1);
  274. if (vui_parameters_present_flag) {
  275. vui_para_parse(&bs, info);
  276. }
  277. ret = 1;
  278. }
  279. free(dataBuf);
  280. return ret;
  281. }
代码中部分未用到的参数并没有获取并赋值,比如:u(&bs, 1);      //forbidden_zero_bit。如需获取相应在这里插入代码片的参数,只需将注释后面对应的变量值进行赋值即可,如:UINT forbidden_zero_bit = u(&bs, 1);

h264_parse_sps接口中sps数据需要第一个字节以Nal类型为0x7作为开始,比如:67 42 00 28 ab 40 22 01 e3 cb cd c0 80 80 a9 02,解析得到的宽高为10801920,fps为0。

这篇关于H264解析SPS获取宽高等信息的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/374237

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

业务中14个需要进行A/B测试的时刻[信息图]

在本指南中,我们将全面了解有关 A/B测试 的所有内容。 我们将介绍不同类型的A/B测试,如何有效地规划和启动测试,如何评估测试是否成功,您应该关注哪些指标,多年来我们发现的常见错误等等。 什么是A/B测试? A/B测试(有时称为“分割测试”)是一种实验类型,其中您创建两种或多种内容变体——如登录页面、电子邮件或广告——并将它们显示给不同的受众群体,以查看哪一种效果最好。 本质上,A/B测

【北交大信息所AI-Max2】使用方法

BJTU信息所集群AI_MAX2使用方法 使用的前提是预约到相应的算力卡,拥有登录权限的账号密码,一般为导师组共用一个。 有浏览器、ssh工具就可以。 1.新建集群Terminal 浏览器登陆10.126.62.75 (如果是1集群把75改成66) 交互式开发 执行器选Terminal 密码随便设一个(需记住) 工作空间:私有数据、全部文件 加速器选GeForce_RTX_2080_Ti

OWASP十大安全漏洞解析

OWASP(开放式Web应用程序安全项目)发布的“十大安全漏洞”列表是Web应用程序安全领域的权威指南,它总结了Web应用程序中最常见、最危险的安全隐患。以下是对OWASP十大安全漏洞的详细解析: 1. 注入漏洞(Injection) 描述:攻击者通过在应用程序的输入数据中插入恶意代码,从而控制应用程序的行为。常见的注入类型包括SQL注入、OS命令注入、LDAP注入等。 影响:可能导致数据泄

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

Spring 源码解读:自定义实现Bean定义的注册与解析

引言 在Spring框架中,Bean的注册与解析是整个依赖注入流程的核心步骤。通过Bean定义,Spring容器知道如何创建、配置和管理每个Bean实例。本篇文章将通过实现一个简化版的Bean定义注册与解析机制,帮助你理解Spring框架背后的设计逻辑。我们还将对比Spring中的BeanDefinition和BeanDefinitionRegistry,以全面掌握Bean注册和解析的核心原理。

CSP 2023 提高级第一轮 CSP-S 2023初试题 完善程序第二题解析 未完

一、题目阅读 (最大值之和)给定整数序列 a0,⋯,an−1,求该序列所有非空连续子序列的最大值之和。上述参数满足 1≤n≤105 和 1≤ai≤108。 一个序列的非空连续子序列可以用两个下标 ll 和 rr(其中0≤l≤r<n0≤l≤r<n)表示,对应的序列为 al,al+1,⋯,ar​。两个非空连续子序列不同,当且仅当下标不同。 例如,当原序列为 [1,2,1,2] 时,要计算子序列 [

多线程解析报表

假如有这样一个需求,当我们需要解析一个Excel里多个sheet的数据时,可以考虑使用多线程,每个线程解析一个sheet里的数据,等到所有的sheet都解析完之后,程序需要提示解析完成。 Way1 join import java.time.LocalTime;public class Main {public static void main(String[] args) thro

ZooKeeper 中的 Curator 框架解析

Apache ZooKeeper 是一个为分布式应用提供一致性服务的软件。它提供了诸如配置管理、分布式同步、组服务等功能。在使用 ZooKeeper 时,Curator 是一个非常流行的客户端库,它简化了 ZooKeeper 的使用,提供了高级的抽象和丰富的工具。本文将详细介绍 Curator 框架,包括它的设计哲学、核心组件以及如何使用 Curator 来简化 ZooKeeper 的操作。 1