mirror of
https://github.com/bluenviron/mediamtx.git
synced 2026-01-26 21:39:16 -08:00
feat: add resolution and bitrate information to API responses
This commit is contained in:
parent
6acf9f1d2b
commit
79ee4c3db5
9 changed files with 234 additions and 0 deletions
|
|
@ -309,3 +309,12 @@ func (t *h264) ProcessRTPPacket( //nolint:dupl
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExtractH264Resolution extracts width and height from H264 SPS
|
||||
func ExtractH264Resolution(sps []byte) (int, int) {
|
||||
var s mch264.SPS
|
||||
if err := s.Unmarshal(sps); err != nil {
|
||||
return 0, 0
|
||||
}
|
||||
return s.Width(), s.Height()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -341,3 +341,12 @@ func (t *h265) ProcessRTPPacket( //nolint:dupl
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExtractH265Resolution extracts width and height from H265 SPS
|
||||
func ExtractH265Resolution(sps []byte) (int, int) {
|
||||
var s mch265.SPS
|
||||
if err := s.Unmarshal(sps); err != nil {
|
||||
return 0, 0
|
||||
}
|
||||
return s.Width(), s.Height()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,3 +108,28 @@ func (t *mpeg1Video) ProcessRTPPacket( //nolint:dupl
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
// ExtractMPEG1Resolution extracts width and height from MPEG-1/2 Video config
|
||||
func ExtractMPEG1Resolution(config []byte) (int, int) {
|
||||
// MPEG-1/2 Video sequence header parsing for resolution
|
||||
// Look for sequence header start code 0x00 0x00 0x01 0xB3
|
||||
if len(config) < 12 {
|
||||
return 0, 0
|
||||
}
|
||||
for i := 0; i < len(config)-4; i++ {
|
||||
if config[i] == 0x00 && config[i+1] == 0x00 && config[i+2] == 0x01 && config[i+3] == 0xB3 {
|
||||
// Sequence header starts after start code
|
||||
data := config[i+4:]
|
||||
if len(data) < 8 {
|
||||
continue
|
||||
}
|
||||
// horizontal_size_value: 12 bits
|
||||
width := (uint32(data[0]) << 4) | (uint32(data[1]) >> 4)
|
||||
// vertical_size_value: 12 bits
|
||||
height := ((uint32(data[1]) & 0x0F) << 8) | uint32(data[2])
|
||||
return int(width), int(height)
|
||||
}
|
||||
}
|
||||
return 0, 0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -157,3 +157,119 @@ func (t *mpeg4Video) ProcessRTPPacket( //nolint:dupl
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
// ExtractMPEG4Resolution extracts width and height from MPEG-4 Video config
|
||||
func ExtractMPEG4Resolution(config []byte) (int, int) {
|
||||
// MPEG-4 Video Object Layer (VOL) parsing for resolution
|
||||
// Look for VOL start code 0x00 0x00 0x01 0x20
|
||||
if len(config) < 20 {
|
||||
return 0, 0
|
||||
}
|
||||
for i := 0; i < len(config)-4; i++ {
|
||||
if config[i] == 0x00 && config[i+1] == 0x00 && config[i+2] == 0x01 && config[i+3] == 0x20 {
|
||||
// VOL header starts after start code
|
||||
data := config[i+4:]
|
||||
if len(data) < 10 {
|
||||
continue
|
||||
}
|
||||
// Skip vol_id (4 bits), random_accessible_vol (1), video_object_type_indication (8)
|
||||
// is_object_layer_identifier (1)
|
||||
bitPos := 0
|
||||
// vol_id: 4 bits
|
||||
bitPos += 4
|
||||
// random_accessible_vol: 1 bit
|
||||
bitPos += 1
|
||||
// video_object_type_indication: 8 bits
|
||||
bitPos += 8
|
||||
// is_object_layer_identifier: 1 bit
|
||||
isObjectLayer := getBit(data, bitPos)
|
||||
bitPos += 1
|
||||
if isObjectLayer {
|
||||
// video_object_layer_verid: 4 bits
|
||||
bitPos += 4
|
||||
// video_object_layer_priority: 3 bits
|
||||
bitPos += 3
|
||||
}
|
||||
// aspect_ratio_info: 4 bits
|
||||
aspectRatio := getBits(data, bitPos, 4)
|
||||
bitPos += 4
|
||||
if aspectRatio == 15 { // extended_PAR
|
||||
// par_width: 8 bits
|
||||
bitPos += 8
|
||||
// par_height: 8 bits
|
||||
bitPos += 8
|
||||
}
|
||||
// vol_control_parameters: 1 bit
|
||||
volControl := getBit(data, bitPos)
|
||||
bitPos += 1
|
||||
if volControl {
|
||||
// chroma_format: 2 bits
|
||||
bitPos += 2
|
||||
// low_delay: 1 bit
|
||||
bitPos += 1
|
||||
// vbv_parameters: 1 bit
|
||||
vbv := getBit(data, bitPos)
|
||||
bitPos += 1
|
||||
if vbv {
|
||||
// bit_rate: 15 bits
|
||||
bitPos += 15
|
||||
// buffer_size: 15 bits
|
||||
bitPos += 15
|
||||
// vbv_occupancy: 15 bits
|
||||
bitPos += 15
|
||||
}
|
||||
}
|
||||
// video_object_layer_shape: 2 bits
|
||||
shape := getBits(data, bitPos, 2)
|
||||
bitPos += 2
|
||||
if shape == 3 { // gray_scale
|
||||
// video_object_layer_shape_extension: 4 bits
|
||||
bitPos += 4
|
||||
}
|
||||
// marker_bit: 1 bit
|
||||
bitPos += 1
|
||||
// vop_time_increment_resolution: 16 bits
|
||||
bitPos += 16
|
||||
// marker_bit: 1 bit
|
||||
bitPos += 1
|
||||
// fixed_vop_rate: 1 bit
|
||||
fixedVop := getBit(data, bitPos)
|
||||
bitPos += 1
|
||||
if fixedVop {
|
||||
// fixed_vop_time_increment: variable bits
|
||||
// skip for now
|
||||
}
|
||||
// marker_bit: 1 bit
|
||||
bitPos += 1
|
||||
// video_object_layer_width: 13 bits
|
||||
width := getBits(data, bitPos, 13)
|
||||
bitPos += 13
|
||||
// marker_bit: 1 bit
|
||||
bitPos += 1
|
||||
// video_object_layer_height: 13 bits
|
||||
height := getBits(data, bitPos, 13)
|
||||
return int(width), int(height)
|
||||
}
|
||||
}
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
func getBit(data []byte, bitPos int) bool {
|
||||
byteIndex := bitPos / 8
|
||||
bitIndex := 7 - (bitPos % 8)
|
||||
if byteIndex >= len(data) {
|
||||
return false
|
||||
}
|
||||
return (data[byteIndex] & (1 << bitIndex)) != 0
|
||||
}
|
||||
|
||||
func getBits(data []byte, bitPos int, numBits int) uint32 {
|
||||
var val uint32
|
||||
for i := 0; i < numBits; i++ {
|
||||
if getBit(data, bitPos+i) {
|
||||
val |= 1 << (numBits - 1 - i)
|
||||
}
|
||||
}
|
||||
return val
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue