LinuxC/C++编程基础(17) 视频关键帧长度和宽度的解析
2012-12-05 15:47
851 查看
一.问题描述,如下:
在视频开发时,在视频播放之前,通常得确定该视频的原始长度和宽度,以这样的长度和宽度,才能得到最佳的视觉效果。
否则,长度或者宽度过大,就会造成失真,过小,则显然不必要。而视频的长度和宽度通常是通过相关算法变换后,存储
于视频的关键帧,因此,需要把它们解析出来,如下图给出了一些视频帧的数据:
比如,在网页YY的开发中,http://yy.com/#31499/81208043,其中的视频播放效果如下:
其中那美女的视频的长度和宽度,就需要解析出来,否则影响播放效果
转载请注明出处:山水间博客,/article/2317650.html
二.视频关键帧的解析,即sps-parse.cpp的实现,如下:
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
typedef uint8_t byte, uint8, uint8_bitmask;
typedef uint16_t uint16, uint16_be, uint16_le;
typedef int16_t sint16, sint16_be, sint16_le;
typedef uint32_t uint32, uint32_be, uint32_le;
typedef int32_t sint32, sint32_be, sint32_le;
typedef struct __uint24 {
uint8 b[3];
} uint24, uint24_be, uint24_le;
typedef struct __bit_buffer {
byte * start;
size_t size;
byte * current;
uint8 read_bits;
} bit_buffer;
static void skip_bits(bit_buffer * bb, size_t nbits) {
bb->current = bb->current + ((nbits + bb->read_bits) / 8);
bb->read_bits = (uint8)((bb->read_bits + nbits) % 8);
}
/**
* 函数static uint8 get_bit(bit_buffer* bb)的功能是读取bb的某个字节的每一位,然后返回,实现很简单,这里略去。
*/
/**
* 函数static uint32 get_bits(bit_buffer* bb,size_t nbits)的功能相当于连续读取bb的nbits位,若nbits是8的整数倍,则相当于读取几个字节
*由uint32可知,nbits的取值范围为0~32,实现很简单,这里略去
*/
转载请注明出处:山水间博客,/article/2317650.html
static uint32 exp_golomb_ue(bit_buffer * bb) {
uint8 bit, significant_bits;
significant_bits = 0;
bit = get_bit(bb);
while (bit == 0) {
significant_bits++;
bit = get_bit(bb);
}
return (1 << significant_bits) + get_bits(bb, significant_bits) - 1;
}
static sint32 exp_golomb_se(bit_buffer * bb) {
sint32 ret;
ret = exp_golomb_ue(bb);
if ((ret & 0x1) == 0) {
return -(ret >> 1);
}
else {
return (ret + 1) >> 1;
}
}
static void parse_scaling_list(uint32 size, bit_buffer * bb) {
uint32 last_scale, next_scale, i;
sint32 delta_scale;
last_scale = 8;
next_scale = 8;
for (i = 0; i < size; i++) {
if (next_scale != 0) {
delta_scale = exp_golomb_se(bb);
next_scale = (last_scale + delta_scale + 256) % 256;
}
if (next_scale != 0) {
last_scale = next_scale;
}
}
}
/**
Parses a SPS NALU to retrieve video width and height
*/
static void parse_sps(byte * sps, size_t sps_size, uint32 * width, uint32 * height) {
bit_buffer bb;
uint32 profile, pic_order_cnt_type, width_in_mbs, height_in_map_units;
uint32 i, size, left, right, top, bottom;
uint8 frame_mbs_only_flag;
bb.start = sps;
bb.size = sps_size;
bb.current = sps;
bb.read_bits = 0;
/* skip first byte, since we already know we're parsing a SPS */
skip_bits(&bb, 8);
/* get profile */
profile = get_bits(&bb, 8);
/* skip 4 bits + 4 zeroed bits + 8 bits = 32 bits = 4 bytes */
skip_bits(&bb, 16);
/* read sps id, first exp-golomb encoded value */
exp_golomb_ue(&bb);
if (profile == 100 || profile == 110 || profile == 122 || profile == 144) {
/* chroma format idx */
if (exp_golomb_ue(&bb) == 3) {
skip_bits(&bb, 1);
}
/* bit depth luma minus8 */
exp_golomb_ue(&bb);
/* bit depth chroma minus8 */
exp_golomb_ue(&bb);
/* Qpprime Y Zero Transform Bypass flag */
skip_bits(&bb, 1);
/* Seq Scaling Matrix Present Flag */
if (get_bit(&bb)) {
for (i = 0; i < 8; i++) {
/* Seq Scaling List Present Flag */
if (get_bit(&bb)) {
parse_scaling_list(i < 6 ? 16 : 64, &bb);
}
}
}
}
/* log2_max_frame_num_minus4 */
exp_golomb_ue(&bb);
/* pic_order_cnt_type */
pic_order_cnt_type = exp_golomb_ue(&bb);
if (pic_order_cnt_type == 0) {
/* log2_max_pic_order_cnt_lsb_minus4 */
exp_golomb_ue(&bb);
}
else if (pic_order_cnt_type == 1) {
/* delta_pic_order_always_zero_flag */
skip_bits(&bb, 1);
/* offset_for_non_ref_pic */
exp_golomb_se(&bb);
/* offset_for_top_to_bottom_field */
exp_golomb_se(&bb);
size = exp_golomb_ue(&bb);
for (i = 0; i < size; i++) {
/* offset_for_ref_frame */
exp_golomb_se(&bb);
}
}
/* num_ref_frames */
exp_golomb_ue(&bb);
/* gaps_in_frame_num_value_allowed_flag */
skip_bits(&bb, 1);
/* pic_width_in_mbs */
width_in_mbs = exp_golomb_ue(&bb) + 1;
/* pic_height_in_map_units */
height_in_map_units = exp_golomb_ue(&bb) + 1;
/* frame_mbs_only_flag */
frame_mbs_only_flag = get_bit(&bb);
if (!frame_mbs_only_flag) {
/* mb_adaptive_frame_field */
skip_bits(&bb, 1);
}
/* direct_8x8_inference_flag */
skip_bits(&bb, 1);
/* frame_cropping */
left = right = top = bottom = 0;
if (get_bit(&bb)) {
left = exp_golomb_ue(&bb) * 2;
right = exp_golomb_ue(&bb) * 2;
top = exp_golomb_ue(&bb) * 2;
bottom = exp_golomb_ue(&bb) * 2;
if (!frame_mbs_only_flag) {
top *= 2;
bottom *= 2;
}
}
/* width */
*width = width_in_mbs * 16 - (left + right);
/* height */
*height = height_in_map_units * 16 - (top + bottom);
if (!frame_mbs_only_flag) {
*height *= 2;
}
}
转载请注明出处:山水间博客,/article/2317650.html
三.main函数实现,如下:
int main() {
FILE *fp = fopen("./pkt", "rb");//pkt即上图中给出的视频数据
int n = 0;
byte c = 0;
byte buf[256] = {0};
uint32 width, height;
fseek(fp, 0x0a, SEEK_SET);//set the offset 0x0a from the begining of the file
fread(&c, 1, 1, fp);
n |= (c << 8);
fread(&c, 1, 1, fp);
n |= c;
printf("sps len: 0x%x\n", n);
fread(buf, 1, n, fp);
parse_sps(buf, n, &width, &height);
printf("video width: %d\n", width);
printf("video height: %d\n", height);
return 0;
}
说明:代码简洁明了,该注意的地方,已经在注释中点出来了,这里不再赘述
四.运行结果,如下:
说明:由图可知,在视频关键帧中,视频的长度和宽度可以通过0x18个字节,即24个字节解析出来,具体哪24个字节呢?
从main函数里可以知道,是从第一行的第13个字节(即0x67)开始的24个字节,具体不再赘述
转载请注明出处:山水间博客,/article/2317650.html
在视频开发时,在视频播放之前,通常得确定该视频的原始长度和宽度,以这样的长度和宽度,才能得到最佳的视觉效果。
否则,长度或者宽度过大,就会造成失真,过小,则显然不必要。而视频的长度和宽度通常是通过相关算法变换后,存储
于视频的关键帧,因此,需要把它们解析出来,如下图给出了一些视频帧的数据:
比如,在网页YY的开发中,http://yy.com/#31499/81208043,其中的视频播放效果如下:
其中那美女的视频的长度和宽度,就需要解析出来,否则影响播放效果
转载请注明出处:山水间博客,/article/2317650.html
二.视频关键帧的解析,即sps-parse.cpp的实现,如下:
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
typedef uint8_t byte, uint8, uint8_bitmask;
typedef uint16_t uint16, uint16_be, uint16_le;
typedef int16_t sint16, sint16_be, sint16_le;
typedef uint32_t uint32, uint32_be, uint32_le;
typedef int32_t sint32, sint32_be, sint32_le;
typedef struct __uint24 {
uint8 b[3];
} uint24, uint24_be, uint24_le;
typedef struct __bit_buffer {
byte * start;
size_t size;
byte * current;
uint8 read_bits;
} bit_buffer;
static void skip_bits(bit_buffer * bb, size_t nbits) {
bb->current = bb->current + ((nbits + bb->read_bits) / 8);
bb->read_bits = (uint8)((bb->read_bits + nbits) % 8);
}
/**
* 函数static uint8 get_bit(bit_buffer* bb)的功能是读取bb的某个字节的每一位,然后返回,实现很简单,这里略去。
*/
/**
* 函数static uint32 get_bits(bit_buffer* bb,size_t nbits)的功能相当于连续读取bb的nbits位,若nbits是8的整数倍,则相当于读取几个字节
*由uint32可知,nbits的取值范围为0~32,实现很简单,这里略去
*/
转载请注明出处:山水间博客,/article/2317650.html
static uint32 exp_golomb_ue(bit_buffer * bb) {
uint8 bit, significant_bits;
significant_bits = 0;
bit = get_bit(bb);
while (bit == 0) {
significant_bits++;
bit = get_bit(bb);
}
return (1 << significant_bits) + get_bits(bb, significant_bits) - 1;
}
static sint32 exp_golomb_se(bit_buffer * bb) {
sint32 ret;
ret = exp_golomb_ue(bb);
if ((ret & 0x1) == 0) {
return -(ret >> 1);
}
else {
return (ret + 1) >> 1;
}
}
static void parse_scaling_list(uint32 size, bit_buffer * bb) {
uint32 last_scale, next_scale, i;
sint32 delta_scale;
last_scale = 8;
next_scale = 8;
for (i = 0; i < size; i++) {
if (next_scale != 0) {
delta_scale = exp_golomb_se(bb);
next_scale = (last_scale + delta_scale + 256) % 256;
}
if (next_scale != 0) {
last_scale = next_scale;
}
}
}
/**
Parses a SPS NALU to retrieve video width and height
*/
static void parse_sps(byte * sps, size_t sps_size, uint32 * width, uint32 * height) {
bit_buffer bb;
uint32 profile, pic_order_cnt_type, width_in_mbs, height_in_map_units;
uint32 i, size, left, right, top, bottom;
uint8 frame_mbs_only_flag;
bb.start = sps;
bb.size = sps_size;
bb.current = sps;
bb.read_bits = 0;
/* skip first byte, since we already know we're parsing a SPS */
skip_bits(&bb, 8);
/* get profile */
profile = get_bits(&bb, 8);
/* skip 4 bits + 4 zeroed bits + 8 bits = 32 bits = 4 bytes */
skip_bits(&bb, 16);
/* read sps id, first exp-golomb encoded value */
exp_golomb_ue(&bb);
if (profile == 100 || profile == 110 || profile == 122 || profile == 144) {
/* chroma format idx */
if (exp_golomb_ue(&bb) == 3) {
skip_bits(&bb, 1);
}
/* bit depth luma minus8 */
exp_golomb_ue(&bb);
/* bit depth chroma minus8 */
exp_golomb_ue(&bb);
/* Qpprime Y Zero Transform Bypass flag */
skip_bits(&bb, 1);
/* Seq Scaling Matrix Present Flag */
if (get_bit(&bb)) {
for (i = 0; i < 8; i++) {
/* Seq Scaling List Present Flag */
if (get_bit(&bb)) {
parse_scaling_list(i < 6 ? 16 : 64, &bb);
}
}
}
}
/* log2_max_frame_num_minus4 */
exp_golomb_ue(&bb);
/* pic_order_cnt_type */
pic_order_cnt_type = exp_golomb_ue(&bb);
if (pic_order_cnt_type == 0) {
/* log2_max_pic_order_cnt_lsb_minus4 */
exp_golomb_ue(&bb);
}
else if (pic_order_cnt_type == 1) {
/* delta_pic_order_always_zero_flag */
skip_bits(&bb, 1);
/* offset_for_non_ref_pic */
exp_golomb_se(&bb);
/* offset_for_top_to_bottom_field */
exp_golomb_se(&bb);
size = exp_golomb_ue(&bb);
for (i = 0; i < size; i++) {
/* offset_for_ref_frame */
exp_golomb_se(&bb);
}
}
/* num_ref_frames */
exp_golomb_ue(&bb);
/* gaps_in_frame_num_value_allowed_flag */
skip_bits(&bb, 1);
/* pic_width_in_mbs */
width_in_mbs = exp_golomb_ue(&bb) + 1;
/* pic_height_in_map_units */
height_in_map_units = exp_golomb_ue(&bb) + 1;
/* frame_mbs_only_flag */
frame_mbs_only_flag = get_bit(&bb);
if (!frame_mbs_only_flag) {
/* mb_adaptive_frame_field */
skip_bits(&bb, 1);
}
/* direct_8x8_inference_flag */
skip_bits(&bb, 1);
/* frame_cropping */
left = right = top = bottom = 0;
if (get_bit(&bb)) {
left = exp_golomb_ue(&bb) * 2;
right = exp_golomb_ue(&bb) * 2;
top = exp_golomb_ue(&bb) * 2;
bottom = exp_golomb_ue(&bb) * 2;
if (!frame_mbs_only_flag) {
top *= 2;
bottom *= 2;
}
}
/* width */
*width = width_in_mbs * 16 - (left + right);
/* height */
*height = height_in_map_units * 16 - (top + bottom);
if (!frame_mbs_only_flag) {
*height *= 2;
}
}
转载请注明出处:山水间博客,/article/2317650.html
三.main函数实现,如下:
int main() {
FILE *fp = fopen("./pkt", "rb");//pkt即上图中给出的视频数据
int n = 0;
byte c = 0;
byte buf[256] = {0};
uint32 width, height;
fseek(fp, 0x0a, SEEK_SET);//set the offset 0x0a from the begining of the file
fread(&c, 1, 1, fp);
n |= (c << 8);
fread(&c, 1, 1, fp);
n |= c;
printf("sps len: 0x%x\n", n);
fread(buf, 1, n, fp);
parse_sps(buf, n, &width, &height);
printf("video width: %d\n", width);
printf("video height: %d\n", height);
return 0;
}
说明:代码简洁明了,该注意的地方,已经在注释中点出来了,这里不再赘述
四.运行结果,如下:
说明:由图可知,在视频关键帧中,视频的长度和宽度可以通过0x18个字节,即24个字节解析出来,具体哪24个字节呢?
从main函数里可以知道,是从第一行的第13个字节(即0x67)开始的24个字节,具体不再赘述
转载请注明出处:山水间博客,/article/2317650.html
相关文章推荐
- LinuxC/C++编程基础(18) 视频关键帧长度和宽度的解析(续)
- LinuxC/C++编程基础(33) 视频包缓存的实现(续1)
- LinuxC/C++编程基础(30) 视频包缓存的实现
- LinuxC/C++编程基础(8) 基于条件变量实现生产者与消费者的实例
- Linux下使用Vim进行C/C++ 编程基础知识详解
- LinuxC/C++编程基础(11) 基于深度优先搜索查找迷宫路线
- LinuxC/C++编程基础(37) Cumulus::BinaryReader的实现
- LinuxC/C++编程基础(2) 基于boost::any构建数据容器
- LinuxC/C++编程基础(14) asio定时器的用法
- Linux下基于C/C++的Socket编程基础
- LinuxC/C++编程基础(26) “心跳”信息的处理
- 第二篇 深入解析高性能服务器编程 第5章 Linux 网络编程基础API
- LinuxC/C++编程基础(29) syslog-ng配置实例
- C/C++程序员基础:Linux C高级编程基本知识点
- LinuxC/C++编程基础(10) quicksort的简单实现
- Linux下基于C/C++的Socket编程基础
- LinuxC/C++编程基础(15) boost同步socket处理
- LinuxC/C++编程基础(4) 基于泛型实现vector
- LinuxC/C++编程基础(5) 排序二叉树的实现