Last active
March 19, 2022 20:13
-
-
Save annidy/3f0f1d790f01b9bd8acccc2e8381e6e3 to your computer and use it in GitHub Desktop.
my sps parser ext
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* h264_sps_parser.h | |
* | |
* Copyright (c) 2014 Zhou Quan <zhouqicy@gmail.com> | |
* | |
* This file is part of ijkPlayer. | |
* | |
* ijkPlayer is free software; you can redistribute it and/or | |
* modify it under the terms of the GNU Lesser General Public | |
* License as published by the Free Software Foundation; either | |
* version 2.1 of the License, or (at your option) any later version. | |
* | |
* ijkPlayer is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
* Lesser General Public License for more details. | |
* | |
* You should have received a copy of the GNU Lesser General Public | |
* License along with ijkPlayer; if not, write to the Free Software | |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
*/ | |
#ifndef IJKMediaPlayer_h264_sps_parser_h | |
#define IJKMediaPlayer_h264_sps_parser_h | |
#define AV_RB16(x) \ | |
((((const uint8_t*)(x))[0] << 8) | \ | |
((const uint8_t*)(x)) [1]) | |
#define AV_RB24(x) \ | |
((((const uint8_t*)(x))[0] << 16) | \ | |
(((const uint8_t*)(x))[1] << 8) | \ | |
((const uint8_t*)(x))[2]) | |
#define AV_RB32(x) \ | |
((((const uint8_t*)(x))[0] << 24) | \ | |
(((const uint8_t*)(x))[1] << 16) | \ | |
(((const uint8_t*)(x))[2] << 8) | \ | |
((const uint8_t*)(x))[3]) | |
/* NAL unit types */ | |
enum { | |
NAL_SLICE = 1, | |
NAL_DPA = 2, | |
NAL_DPB = 3, | |
NAL_DPC = 4, | |
NAL_IDR_SLICE = 5, | |
NAL_SEI = 6, | |
NAL_SPS = 7, | |
NAL_PPS = 8, | |
NAL_AUD = 9, | |
NAL_END_SEQUENCE = 10, | |
NAL_END_STREAM = 11, | |
NAL_FILLER_DATA = 12, | |
NAL_SPS_EXT = 13, | |
NAL_AUXILIARY_SLICE = 19, | |
NAL_FF_IGNORE = 0xff0f001, | |
}; | |
typedef struct | |
{ | |
const uint8_t *data; | |
const uint8_t *end; | |
int head; | |
uint64_t cache; | |
} nal_bitstream; | |
static void | |
nal_bs_init(nal_bitstream *bs, const uint8_t *data, size_t size) | |
{ | |
bs->data = data; | |
bs->end = data + size; | |
bs->head = 0; | |
// fill with something other than 0 to detect | |
// emulation prevention bytes | |
bs->cache = 0xffffffff; | |
} | |
static uint64_t | |
nal_bs_read(nal_bitstream *bs, int n) | |
{ | |
uint64_t res = 0; | |
int shift; | |
if (n == 0) | |
return res; | |
// fill up the cache if we need to | |
while (bs->head < n) { | |
uint8_t a_byte; | |
bool check_three_byte; | |
check_three_byte = true; | |
next_byte: | |
if (bs->data >= bs->end) { | |
// we're at the end, can't produce more than head number of bits | |
n = bs->head; | |
break; | |
} | |
// get the byte, this can be an emulation_prevention_three_byte that we need | |
// to ignore. | |
a_byte = *bs->data++; | |
if (check_three_byte && a_byte == 0x03 && ((bs->cache & 0xffff) == 0)) { | |
// next byte goes unconditionally to the cache, even if it's 0x03 | |
check_three_byte = false; | |
goto next_byte; | |
} | |
// shift bytes in cache, moving the head bits of the cache left | |
bs->cache = (bs->cache << 8) | a_byte; | |
bs->head += 8; | |
} | |
// bring the required bits down and truncate | |
if ((shift = bs->head - n) > 0) | |
res = bs->cache >> shift; | |
else | |
res = bs->cache; | |
// mask out required bits | |
if (n < 32) | |
res &= (1 << n) - 1; | |
bs->head = shift; | |
return res; | |
} | |
static bool | |
nal_bs_eos(nal_bitstream *bs) | |
{ | |
return (bs->data >= bs->end) && (bs->head == 0); | |
} | |
// read unsigned Exp-Golomb code | |
static int64_t | |
nal_bs_read_ue(nal_bitstream *bs) | |
{ | |
int i = 0; | |
while (nal_bs_read(bs, 1) == 0 && !nal_bs_eos(bs) && i < 32) | |
i++; | |
return ((1 << i) - 1 + nal_bs_read(bs, i)); | |
} | |
typedef struct | |
{ | |
uint64_t profile_idc; | |
uint64_t level_idc; | |
uint64_t sps_id; | |
uint64_t chroma_format_idc; | |
uint64_t separate_colour_plane_flag; | |
uint64_t bit_depth_luma_minus8; | |
uint64_t bit_depth_chroma_minus8; | |
uint64_t qpprime_y_zero_transform_bypass_flag; | |
uint64_t seq_scaling_matrix_present_flag; | |
uint64_t log2_max_frame_num_minus4; | |
uint64_t pic_order_cnt_type; | |
uint64_t log2_max_pic_order_cnt_lsb_minus4; | |
uint64_t max_num_ref_frames; | |
uint64_t gaps_in_frame_num_value_allowed_flag; | |
uint64_t pic_width_in_mbs_minus1; | |
uint64_t pic_height_in_map_units_minus1; | |
uint64_t frame_mbs_only_flag; | |
uint64_t mb_adaptive_frame_field_flag; | |
uint64_t direct_8x8_inference_flag; | |
uint64_t frame_cropping_flag; | |
uint64_t frame_crop_left_offset; | |
uint64_t frame_crop_right_offset; | |
uint64_t frame_crop_top_offset; | |
uint64_t frame_crop_bottom_offset; | |
} sps_info_struct; | |
static void parseh264_sps(uint8_t *sps, uint32_t sps_size, int *level, int *profile, bool *interlaced, int32_t *max_ref_frames, int *width, int *height) | |
{ | |
nal_bitstream bs; | |
sps_info_struct sps_info = {0}; | |
nal_bs_init(&bs, sps, sps_size); | |
sps_info.profile_idc = nal_bs_read(&bs, 8); | |
nal_bs_read(&bs, 1); // constraint_set0_flag | |
nal_bs_read(&bs, 1); // constraint_set1_flag | |
nal_bs_read(&bs, 1); // constraint_set2_flag | |
nal_bs_read(&bs, 1); // constraint_set3_flag | |
nal_bs_read(&bs, 4); // reserved | |
sps_info.level_idc = nal_bs_read(&bs, 8); | |
sps_info.sps_id = nal_bs_read_ue(&bs); | |
if (sps_info.profile_idc == 100 || | |
sps_info.profile_idc == 110 || | |
sps_info.profile_idc == 122 || | |
sps_info.profile_idc == 244 || | |
sps_info.profile_idc == 44 || | |
sps_info.profile_idc == 83 || | |
sps_info.profile_idc == 86) | |
{ | |
sps_info.chroma_format_idc = nal_bs_read_ue(&bs); | |
if (sps_info.chroma_format_idc == 3) | |
sps_info.separate_colour_plane_flag = nal_bs_read(&bs, 1); | |
sps_info.bit_depth_luma_minus8 = nal_bs_read_ue(&bs); | |
sps_info.bit_depth_chroma_minus8 = nal_bs_read_ue(&bs); | |
sps_info.qpprime_y_zero_transform_bypass_flag = nal_bs_read(&bs, 1); | |
sps_info.seq_scaling_matrix_present_flag = nal_bs_read (&bs, 1); | |
if (sps_info.seq_scaling_matrix_present_flag) | |
{ | |
/* TODO: unfinished */ | |
} | |
} | |
sps_info.log2_max_frame_num_minus4 = nal_bs_read_ue(&bs); | |
if (sps_info.log2_max_frame_num_minus4 > 12) { | |
// must be between 0 and 12 | |
// don't early return here - the bits we are using (profile/level/interlaced/ref frames) | |
// might still be valid - let the parser go on and pray. | |
//return; | |
} | |
sps_info.pic_order_cnt_type = nal_bs_read_ue(&bs); | |
if (sps_info.pic_order_cnt_type == 0) { | |
sps_info.log2_max_pic_order_cnt_lsb_minus4 = nal_bs_read_ue(&bs); | |
} | |
else if (sps_info.pic_order_cnt_type == 1) { // TODO: unfinished | |
/* | |
delta_pic_order_always_zero_flag = gst_nal_bs_read (bs, 1); | |
offset_for_non_ref_pic = gst_nal_bs_read_se (bs); | |
offset_for_top_to_bottom_field = gst_nal_bs_read_se (bs); | |
num_ref_frames_in_pic_order_cnt_cycle = gst_nal_bs_read_ue (bs); | |
for( i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++ ) | |
offset_for_ref_frame[i] = gst_nal_bs_read_se (bs); | |
*/ | |
} | |
sps_info.max_num_ref_frames = nal_bs_read_ue(&bs); | |
sps_info.gaps_in_frame_num_value_allowed_flag = nal_bs_read(&bs, 1); | |
sps_info.pic_width_in_mbs_minus1 = nal_bs_read_ue(&bs); | |
sps_info.pic_height_in_map_units_minus1 = nal_bs_read_ue(&bs); | |
sps_info.frame_mbs_only_flag = nal_bs_read(&bs, 1); | |
if (!sps_info.frame_mbs_only_flag) | |
sps_info.mb_adaptive_frame_field_flag = nal_bs_read(&bs, 1); | |
sps_info.direct_8x8_inference_flag = nal_bs_read(&bs, 1); | |
sps_info.frame_cropping_flag = nal_bs_read(&bs, 1); | |
if (sps_info.frame_cropping_flag) { | |
sps_info.frame_crop_left_offset = nal_bs_read_ue(&bs); | |
sps_info.frame_crop_right_offset = nal_bs_read_ue(&bs); | |
sps_info.frame_crop_top_offset = nal_bs_read_ue(&bs); | |
sps_info.frame_crop_bottom_offset = nal_bs_read_ue(&bs); | |
} | |
*level = (int)sps_info.level_idc; | |
*profile = (int)sps_info.profile_idc; | |
*interlaced = (int)!sps_info.frame_mbs_only_flag; | |
*max_ref_frames = (int)sps_info.max_num_ref_frames; | |
*width = (int)(sps_info.pic_width_in_mbs_minus1+1)*16; | |
*height = (int)(sps_info.pic_height_in_map_units_minus1+1)*16; | |
} | |
bool validate_avcC_spc(uint8_t *extradata, uint32_t extrasize, int32_t *max_ref_frames, int *level, int *profile) | |
{ | |
// check the avcC atom's sps for number of reference frames and | |
// bail if interlaced, VDA does not handle interlaced h264. | |
bool interlaced = true; | |
uint8_t *spc = extradata + 6; | |
uint32_t sps_size = AV_RB16(spc); | |
int width, height; | |
if (sps_size) | |
parseh264_sps(spc+3, sps_size-1, level, profile, &interlaced, max_ref_frames, &width, &height); | |
if (interlaced) | |
return false; | |
return true; | |
} | |
static inline int ff_get_nal_units_type(const uint8_t * const data) { | |
return data[4] & 0x1f; | |
} | |
static uint32_t bytesToInt(uint8_t* src) { | |
uint32_t value; | |
value = (uint32_t)((src[0] & 0xFF)<<24|(src[1]&0xFF)<<16|(src[2]&0xFF)<<8|(src[3]&0xFF)); | |
return value; | |
} | |
static bool ff_avpacket_is_idr(const AVPacket* pkt) { | |
int state = -1; | |
if (pkt->data && pkt->size >= 5) { | |
int offset = 0; | |
while (offset >= 0 && offset + 5 <= pkt->size) { | |
void* nal_start = pkt->data+offset; | |
state = ff_get_nal_units_type(nal_start); | |
if (state == NAL_IDR_SLICE) { | |
return true; | |
} | |
// ALOGI("offset %d \n", bytesToInt(nal_start)); | |
offset+=(bytesToInt(nal_start) + 4); | |
} | |
} | |
return false; | |
} | |
static bool ff_avpacket_is_key(const AVPacket* pkt) { | |
if (pkt->flags & AV_PKT_FLAG_KEY) { | |
return true; | |
} else { | |
return false; | |
} | |
} | |
static bool ff_avpacket_i_or_idr(const AVPacket* pkt,bool isIdr) { | |
if (isIdr == true) { | |
return ff_avpacket_is_idr(pkt); | |
} else { | |
return ff_avpacket_is_key(pkt); | |
} | |
} | |
static inline int next_nalu(uint8_t *buf, int size) { | |
int nalhead_pos=0; | |
while(nalhead_pos+4<size) | |
{ | |
//search for nal header | |
if(buf[nalhead_pos++] == 0x00 && | |
buf[nalhead_pos++] == 0x00) | |
{ | |
if(buf[nalhead_pos++] == 0x01) | |
goto gotnal_head; | |
else | |
{ | |
//cuz we have done an i++ before,so we need to roll back now | |
nalhead_pos--; | |
if(buf[nalhead_pos++] == 0x00 && | |
buf[nalhead_pos++] == 0x01) | |
goto gotnal_head; | |
else | |
continue; | |
} | |
} | |
else | |
continue; | |
//search for nal tail which is also the head of next nal | |
gotnal_head: | |
return nalhead_pos; | |
} | |
return size; | |
} | |
static bool ff_avpacket_get_sps_width_height(const AVPacket* pkt, int *width, int *height) { | |
if (!ff_avpacket_is_key(pkt)) { | |
return false; | |
} | |
int state = -1; | |
if (pkt->data && pkt->size >= 5) { | |
int offset = 0; | |
while (offset >= 0 && offset + 5 <= pkt->size) { | |
void* nal_start = pkt->data+offset; | |
state = ff_get_nal_units_type(nal_start); | |
if (state == NAL_SPS) { | |
uint8_t *extradata = (uint8_t *)nal_start; | |
uint8_t *sps = extradata + 5; | |
uint32_t sps_size = pkt->size-offset-5; | |
int level,profile,max_ref_frames; | |
bool interlaced; | |
parseh264_sps(sps, sps_size-1, &level, &profile, &interlaced, &max_ref_frames, width, height); | |
return true; | |
} | |
offset+=next_nalu(nal_start+4, pkt->size-offset-4); | |
} | |
} | |
return false; | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment