Skip to content

feat: add vui_parameter parsing. #81

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
220 changes: 220 additions & 0 deletions codec/h264parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ const (
NALU_AUD = 9
)

const (
EXTENDED_SAR = 255
)

func IsDataNALU(b []byte) bool {
typ := b[0] & 0x1f
return typ >= 1 && typ <= 5
Expand Down Expand Up @@ -301,6 +305,9 @@ type SPSInfo struct {
CropRight uint
CropTop uint
CropBottom uint
UnitsInTick uint
TimeScale uint
FixedRate uint

Width uint
Height uint
Expand All @@ -309,6 +316,7 @@ type SPSInfo struct {
func ParseSPS(data []byte) (self SPSInfo, err error) {
r := &bits.GolombBitReader{R: bytes.NewReader(data)}


if _, err = r.ReadBits(8); err != nil {
return
}
Expand Down Expand Up @@ -494,6 +502,218 @@ func ParseSPS(data []byte) (self SPSInfo, err error) {

self.Width = (self.MbWidth * 16) - self.CropLeft*2 - self.CropRight*2
self.Height = ((2 - frame_mbs_only_flag) * self.MbHeight * 16) - self.CropTop*2 - self.CropBottom*2

var vui_parameters_present_flag uint
if vui_parameters_present_flag, err = r.ReadBit(); err != nil {
return
}

if (vui_parameters_present_flag != 0) {
/*
aspect_ratio_info_present_flag equal to 1 specifies that
aspect_ratio_idc is present. aspect_ratio_info_present_flag
equal to 0 specifies that aspect_ratio_idc is not present.
*/
var aspect_ratio_info_present_flag uint
if aspect_ratio_info_present_flag, err = r.ReadBit(); err != nil {
return
}

if aspect_ratio_info_present_flag != 0 {
/*
aspect_ratio_idc specifies the value of the sample aspect
ratio of the luma samples. Table E-1 shows the meaning of
the code. When aspect_ratio_idc indicates Extended_SAR, the
sample aspect ratio is represented by sar_width and
sar_height. When the aspect_ratio_idc syntax element is not
present, aspect_ratio_idc value shall be inferred to be
equal to 0.
*/
var aspect_ratio_idc uint
if aspect_ratio_idc, err = r.ReadBits(8); err != nil {
return
}

switch aspect_ratio_idc {
case 0:
// Unspecified
break;
case 1:
// 1:1
/*
1280x720 16:9 frame without overscan
1920x1080 16:9 frame without overscan (cropped from 1920x1088)
640x480 4:3 frame without overscan
*/
break;
case 2:
// 12:11
/*
720x576 4:3 frame with horizontal overscan
352x288 4:3 frame without overscan
*/
break;
case 3:
// 10:11
/*
720x480 4:3 frame with horizontal overscan
352x240 4:3 frame without overscan
*/
break;
case 4:
// 16:11
/*
720x576 16:9 frame with horizontal overscan
540x576 4:3 frame with horizontal overscan
*/
break;
case 5:
// 40:33
/*
720x480 16:9 frame with horizontal overscan
540x480 4:3 frame with horizontal overscan
*/
break;
case 6:
// 24:11
/*
352x576 4:3 frame without overscan
540x576 16:9 frame with horizontal overscan
*/
break;
case 7:
// 20:11
/*
352x480 4:3 frame without overscan
480x480 16:9 frame with horizontal overscan
*/
break;
case 8:
// 32:11
/*
352x576 16:9 frame without overscan
*/
break;
case 9:
// 80:33
/*
352x480 16:9 frame without overscan
*/
break;
case 10:
// 18:11
/*
480x576 4:3 frame with horizontal overscan
*/
break;
case 11:
// 15:11
/*
480x480 4:3 frame with horizontal overscan
*/
break;
case 12:
// 64:33
/*
540x576 16:9 frame with horizontal overscan
*/
break;
case 13:
// 160:99
/*
540x576 16:9 frame with horizontal overscan
*/
break;
case EXTENDED_SAR:
if _, err = r.ReadBits(16); err != nil {
return
}
if _, err = r.ReadBits(16); err != nil {
return
}
break;
}
}

var overscan_info_present_flag uint
if overscan_info_present_flag, err = r.ReadBit(); err != nil {
return
}

if overscan_info_present_flag != 0 {
//overscan_appropriate_flag
if _, err = r.ReadBit(); err != nil {
return
}
}

var video_signal_type_present_flag uint
if video_signal_type_present_flag, err = r.ReadBit(); err != nil {
return
}

if video_signal_type_present_flag != 0 { //video_signal_type_present_flag
if _, err = r.ReadBits(3); err != nil {//video_format
return
}

if _, err = r.ReadBit(); err != nil {//video_full_range_flag
return
}

var colour_description_present_flag uint
if colour_description_present_flag, err = r.ReadBit(); err != nil {
return
}

if colour_description_present_flag != 0 {
if _, err = r.ReadBits(8); err != nil {// colour_primaries
return
}

if _, err = r.ReadBits(8); err != nil {// transfer_characteristics
return
}

if _, err = r.ReadBits(8); err != nil {// matrix_coefficients
return
}
}
}

var chroma_loc_info_present_flag uint
if chroma_loc_info_present_flag, err = r.ReadBit(); err != nil {
return
}

if chroma_loc_info_present_flag != 0 {

r.ReadExponentialGolombCode() //chroma_sample_loc_type_top_field ue(v)
r.ReadExponentialGolombCode() //chroma_sample_loc_type_bottom_field ue(v)
}

var timing_info_present_flag uint
if timing_info_present_flag, err = r.ReadBit(); err != nil {
return
}

if timing_info_present_flag != 0 {
//num_units_in_tick
if self.UnitsInTick, err = r.ReadBits(32); err != nil {
return
}

//time_scale
if self.TimeScale, err = r.ReadBits(32); err != nil {
return
}

//fixed_frame_rate_flag
if self.FixedRate, err = r.ReadBit(); err != nil {
return
}
}
}

return
}
Expand Down
45 changes: 43 additions & 2 deletions examples/open_probe_file/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,32 @@ import (
"github.com/nareix/joy4/av"
"github.com/nareix/joy4/av/avutil"
"github.com/nareix/joy4/format"
"github.com/nareix/joy4/codec/h264parser"
)

func init() {
format.RegisterAll()
}

func frameRate(info h264parser.SPSInfo) float64 {
var num uint64
var fps float64

num = 500 * uint64(info.TimeScale) /* 1000 * 0.5 */

if info.UnitsInTick != 0 {
fps = (float64(num) / float64(info.UnitsInTick)) / 1000
}

return fps;
}

func main() {
file, _ := avutil.Open("projectindex.flv")
file, _ := avutil.Open("/tmp/sintel.flv")

if file == nil {
fmt.Println("could not open input file")
}
streams, _ := file.Streams()
for _, stream := range streams {
if stream.Type().IsAudio() {
Expand All @@ -25,12 +42,36 @@ func main() {
}
}

for i := 0; i < 10; i++ {
for i := 0; i < 1000; i++ {
var pkt av.Packet
var err error
if pkt, err = file.ReadPacket(); err != nil {
break
}

// Split out the NAL units
nals, _ := h264parser.SplitNALUs(pkt.Data)
for _, nalUnit := range nals {

if len(nalUnit) == 0 {
continue
}

// Get the type, check for a SPS header
typ := nalUnit[0] & 0x1f
if typ == 7 {
// Try to parse out the SPS header.
if info, err := h264parser.ParseSPS(nalUnit); err == nil {
fmt.Println("SPSInfo",
frameRate(info),
info.UnitsInTick,
info.TimeScale,
info.FixedRate,
info.Width)
}
}
}

fmt.Println("pkt", i, streams[pkt.Idx].Type(), "len", len(pkt.Data), "keyframe", pkt.IsKeyFrame)
}

Expand Down
23 changes: 23 additions & 0 deletions utils/bits/golomb_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,33 @@ type GolombBitReader struct {
R io.Reader
buf [1]byte
left byte
prev_two_bytes uint
emulation_prevention_bytes uint
}

func (self *GolombBitReader) ReadBit() (res uint, err error) {
if self.left == 0 {
if _, err = self.R.Read(self.buf[:]); err != nil {
return
}
/*
// Emulation prevention three-byte detection.
// If a sequence of 0x000003 is found, skip (ignore) the last byte (0x03).
*/
if self.buf[0] == 0x03 && (self.prev_two_bytes & 0xffff) == 0 {
// Detected 0x000003, skip last byte.
if _, err = self.R.Read(self.buf[:]); err != nil {
return
}

self.emulation_prevention_bytes++
/*
// Need another full three bytes before we can detect the sequence again.
*/
self.prev_two_bytes = 0xffff
}
self.left = 8
self.prev_two_bytes = (self.prev_two_bytes << 8) | uint(self.buf[0])
}
self.left--
res = uint(self.buf[0]>>self.left) & 1
Expand Down Expand Up @@ -63,3 +82,7 @@ func (self *GolombBitReader) ReadSE() (res uint, err error) {
}
return
}

func (self *GolombBitReader) NumEmulationPreventionBytesRead() uint {
return self.emulation_prevention_bytes
}