视频编码的常见参数基本概念 --bit rate / frame rate /sample rate等等

 

        码率:Bit Rate,指视频或音频文件在单位时间内使用的数据流量,该参数的单位通常是Kbps,也就是千比特每秒。通常2000kbps~3000kbps就已经足以将画质效果表现到极致了。码率参数与视频文件最终体积大小有直接性的关系。

  混合码率:Overall Bit Rate,指视频文件中视频和音频混合后的整体平均码率。一般描述一个视频文件的码率都是指OBR,如新浪播客允许的OBR上限为523Kbps。

  固定码率:Constant Bit Rate,指的是编码器的输出码率(或者解码器的输入码率)应该是固定制(常数)。CBR不适合高清晰度视频的编码,因为CBR将导致没有足够的码率应对复杂多变内容部分进行编码(从而导致画质下降),同时在简单的内容部分会浪费一些码率。

  可变码率:Variable Bit Rate,编码器的输出码率(或者解码器的输入码率)可以根据编码器的输入源信号的负责度自适应的调整,目的是达到保持输出质量保持不变而不是保持输出码率保持不变。VBR编码会消耗较多的计算时间,但可以更好的利用有限的存储空间:用比较多的码率对复杂度高的段进行编码,用比较少的码率对复杂度低的段进行编码。总之需要清晰度高且体积小的视频,选择VBR是明智的选择。

  平均码率:Average Bit Rate,指音频或视频的平均码率,可以简单的认为等于文件大小除以播放时间。在音频编码方面与CBR基本相同,会按照设定的目标码率进行编码。但当编码器认为“适当”的时候,会使用高于目标码率的数值来进行编码以保证更好的质量。

  帧率:Frame Rate,是用于测量画面显示帧数的量度。所谓的测量单位为每秒显示帧数(Frames per Second,缩写:FPS)。如电影的帧率一般是25fps和29.97fps,而第一人称射击游戏等要求画面极为顺畅的特殊场合,则需要30fps以上的效果,高于60fps就没有必要了。

  采样率:每秒从连续信号中提取并组成离散信号的采样个数,它用赫兹(Hz)来表示。一般音乐CD的采样率是44100Hz,所以视频编码中的音频采样率保持在这个级别就完全足够了,通常视频转换器也将这个采样率作为默认设置。

  Single Pass:在编码的时候只进行一次运算,直接生成经过编码的视频文件。

  Two Pass:需要运算两次,可以理解为先进行一次全局的计算,收集画面信息,并将这些信息记录到信息文件。第二次才根据采集的信息,正式进行压缩,生成压缩文件。

  Single pass模式编码较简单,编码速度较快,但是最终质量不如Two pass模式好,对于视频源本身画质就不佳的编码过程可以采用。Two pass通过第一次运算的信息采集,可以让需要高码率的运动画面可以分配更的码率来保证画面质量。而对于不包含太多运动信息的静态画面,则可以消减分配的码率。Twopass模式可以在影片容量与画面质量之间找到最佳平衡点。所以要求画面清晰的视频,肯定要选择Two Pass,只是编码速度惨不忍睹。

  封装格式:多媒体封装格式也称多媒体容器 (Multimedia Container),它不同于H.264、 AAC这类编码格式,它只是为多媒体编码提供了一个“外壳”,也就是所谓的视频格式。如MP4、AVI、MKV、FLV、WMA等。

  画面比例:Aspect Ratio,指视频画面宽和高的比例。常见的比例有16:9和4:3。电视媒体有严格的视频制式要求,视频比例和帧数都是固定的,而网络传播的视频比例则较为自由。一般DVD和BD电影的视频比例大多是宽屏或者超宽屏。在视频编码过程中一定要注意画面比例是否正确,不然就会出现画面拉伸变形。

  分辨率:指视频宽高的像素数值,单位为Px。通常视频分辨率的数值宽高比要等于画面比例,不然视频文件就会产生黑边。标准1080P的分辨率为1920×1080,帧率为60fps,也就是真高清。而最常见的网络传播的1080P高清片帧率通常为23.976 fps。

 

原文:http://blog.youkuaiyun.com/vblittleboy/article/details/6215592

你希望将 `int parse_and_init_storage_data(const char* filename)` 函数用于**另一个线程中执行**,这是一个非常常见的需求(比如:主线程启动解码/处理线程,避免阻塞 UI 或其他任务)。 下面我会: 1. **修改函数使其适配线程函数签名(如 pthread)** 2. **封装参数传递方式** 3. **确保线程安全和资源管理** 4. **给出完整可运行示例** --- ## ✅ 目标 让 `parse_and_init_storage_data()` 在独立线程中运行,不阻塞主线程,并能正确初始化每一帧的 `STORAGE_DATA` 结构体。 --- ### ✅ 1. 线程函数的基本要求(以 POSIX pthread 为例) 线程函数必须满足如下原型: ```c void* thread_func(void *arg); ``` 所以我们不能直接把 `parse_and_init_storage_data()` 当作线程函数,但可以包装它。 --- ### ✅ 2. 定义线程参数结构 因为线程只能接收一个 `void*` 参数,我们需要封装文件名和其他可能需要的数据(比如回调函数、输出队列等)。 ```c typedef struct { const char *filename; void (*on_frame_ready)(STORAGE_DATA*, avdm_block_t*); // 回调函数 int channel; // 可选:通道号 } thread_arg_t; ``` --- ### ✅ 3. 修改为线程安全版本 ```c #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <unistd.h> // 假设这些类型已定义(与之前一致) typedef unsigned char U8; typedef unsigned int U32; typedef unsigned long long U64; #define TP_EVENT_TYPE_NUM 10 #define ALARM_DETECTION_MAX 10 typedef struct { int width; int height; int bitrate; } video_info_t; typedef struct { int sample_rate; int channels; } audio_info_t; typedef struct __avdm_block { time_t start_time; time_t end_time; U32 unitCnt; U8 *addr; U32 len; U64 pts; union { video_info_t video; audio_info_t audio; } unique_info; } avdm_block_t; typedef struct { U8 event_status[TP_EVENT_TYPE_NUM]; U8 det_status[ALARM_DETECTION_MAX]; int channel; int buf_size; U8 *buf; int buf_len; int frame_cnt; time_t start_time; time_t end_time; int pic_type; #ifdef VIDEO_AVBR_ENABLE U8 *i_frame_buf; #endif } STORAGE_DATA; // 全局函数声明 void init_storage_data(STORAGE_DATA *storage_data, avdm_block_t *block, int is_video); // 查找起始码函数 int find_start_code(const U8 *data, int offset, int len) { if (offset + 3 > len) return -1; if (offset + 4 <= len && data[offset] == 0x00 && data[offset+1] == 0x00 && data[offset+2] == 0x00 && data[offset+3] == 0x01) { return 4; } else if (data[offset] == 0x00 && data[offset+1] == 0x00 && data[offset+2] == 0x01) { return 3; } return 0; } // 模拟 init_storage_data 实现 void init_storage_data(STORAGE_DATA *storage_data, avdm_block_t *block, int is_video) { memset(storage_data, 0, sizeof(STORAGE_DATA)); storage_data->channel = block->unique_info.video.bitrate ? 0 : -1; // 示例赋值 storage_data->buf = block->addr; storage_data->buf_len = block->len; storage_data->frame_cnt = block->unitCnt; storage_data->start_time = block->start_time; storage_data->end_time = block->end_time; storage_data->pic_type = 0; memset(storage_data->event_status, 0, TP_EVENT_TYPE_NUM); memset(storage_data->det_status, 0, ALARM_DETECTION_MAX); } ``` --- ### ✅ 4. 线程安全版 `parse_and_init_storage_data` ```c void* parse_and_init_storage_data_thread(void *arg) { thread_arg_t *thread_arg = (thread_arg_t*)arg; const char *filename = thread_arg->filename; void (*on_frame_ready)(STORAGE_DATA*, avdm_block_t*) = thread_arg->on_frame_ready; int channel = thread_arg->channel; printf("Thread started: parsing file '%s' on channel %d\n", filename, channel); FILE *fp = fopen(filename, "rb"); if (!fp) { perror("Cannot open file"); free(thread_arg); // 注意:我们在线程内释放 arg return NULL; } fseek(fp, 0, SEEK_END); long file_len = ftell(fp); rewind(fp); U8 *buffer = (U8*)malloc(file_len); if (!buffer) { fclose(fp); free(thread_arg); return NULL; } fread(buffer, 1, file_len, fp); fclose(fp); int pos = 0; int frame_index = 0; const int fps = 30; time_t base_time = time(NULL); while (pos < file_len) { int sc_size = find_start_code(buffer, pos, file_len); if (sc_size <= 0) { pos++; continue; } int nal_start = pos + sc_size; int next_pos = -1; for (int i = nal_start; i < file_len; i++) { int sc = find_start_code(buffer, i, file_len); if (sc > 0) { next_pos = i; break; } } int nal_end = (next_pos == -1) ? file_len : next_pos; int nal_size = nal_end - nal_start; if (nal_size <= 0) break; U8 nal_header = buffer[nal_start]; int nal_type = (nal_header >> 1) & 0x3F; int is_idr = (nal_type == 19 || nal_type == 20 || nal_type == 21); int frame_size = sc_size + nal_size; U8 *frame_data = (U8*)malloc(frame_size); memcpy(frame_data, buffer + pos, frame_size); avdm_block_t block; memset(&block, 0, sizeof(avdm_block_t)); block.addr = frame_data; block.len = frame_size; block.unitCnt = 1; block.start_time = base_time + frame_index / fps; block.end_time = base_time + frame_index / fps; block.pts = frame_index * (1000000 / fps); block.unique_info.video.width = 1920; block.unique_info.video.height = 1080; block.unique_info.video.bitrate = 4096000; STORAGE_DATA storage_data; init_storage_data(&storage_data, &block, 1); printf("[Frame %d] Type: %s, Size: %u bytes, PTS: %llu\n", frame_index, is_idr ? "I-frame" : "P/B-frame", storage_data.buf_len, storage_data.start_time); // 调用用户回调函数处理这一帧(例如发送编码器、RTP打包等) if (on_frame_ready) { on_frame_ready(&storage_data, &block); } else { // 默认行为:模拟处理延迟 usleep(33000); // ~30fps } // 注意:free(frame_data),但 storage_data.buf 不要 deep copy 就需小心生命周期 free(frame_data); pos = next_pos; frame_index++; // 可加 usleep 控制播放速度 usleep(10000); // 小延时避免 CPU 占满 } free(buffer); free(thread_arg); // 由线程自己释放传入的参数内存 printf("Thread finished parsing file.\n"); return NULL; } ``` --- ### ✅ 5. 主函数中创建线程 ```c // 用户自定义处理每帧的回调函数 void on_frame_handler(STORAGE_DATA *data, avdm_block_t *block) { // 示例:打印信息或发送给编码器/RTP模块 printf("Callback: Frame processed, size=%d\n", data->buf_len); // 这里你可以: // - send_to_encoder(data); // - rtp_packetize_and_send(data); // - write_to_file(data); } int main() { pthread_t tid; thread_arg_t *arg = (thread_arg_t*)malloc(sizeof(thread_arg_t)); if (!arg) { perror("malloc failed"); return -1; } arg->filename = "video.h265"; // 输入文件 arg->on_frame_ready = on_frame_handler; // 回调函数 arg->channel = 0; // 通道编号 int ret = pthread_create(&tid, NULL, parse_and_init_storage_data_thread, arg); if (ret != 0) { perror("pthread_create failed"); free(arg); return -1; } printf("Main thread continues...\n"); // 等待线程结束(可选) pthread_join(tid, NULL); printf("Main: thread joined.\n"); return 0; } ``` --- ## ✅ 关键点说明 | 要点 | 说明 | |------|------| | ✅ 参数传递 | 使用 `malloc` 分配 `thread_arg_t`,在线程内释放 | | ✅ 内存安全 | 每帧动态分配 `frame_data`,处理完后立即 `free` | | ✅ 回调机制 | 通过函数指针将帧交给外部处理,解耦逻辑 | | ✅ 避免竞争 | 各帧数据独立分配,无共享状态 | | ⚠️ 生命周期 | 若回调函数异步使用 `storage_data.buf`,需深拷贝数据 | --- ## ✅ 如何避免 `free(frame_data)` 导致回调访问失效? 如果你的 `on_frame_ready()` 是异步使用的(比如放入队列由另一线程处理),就不能在当前线程 `free(frame_data)`。 ✅ 解决方案:深拷贝数据 ```c U8 *safe_copy = malloc(block.len); memcpy(safe_copy, block.addr, block.len); block.addr = safe_copy; // 然后把 block 和 storage_data 一起传出去,由消费者释放 ``` 或者使用智能指针/引用计数机制(C++ 更方便)。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值