Skip to content
Permalink
 
 
Cannot retrieve contributors at this time
348 lines (294 sloc) 10.4 KB
/*
MIT License
Copyright 2016 Comcast Cable Communications Management, LLC
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
package psi
import (
"encoding/binary"
"fmt"
"strconv"
)
// Program Element Stream Descriptor Type.
const (
VIDEO_STREAM uint8 = 2 // 0000 0010 (0x02)
AUDIO_STREAM uint8 = 3 // 0000 0011 (0x03)
REGISTRATION uint8 = 5 // 0000 1000 (0x05)
CONDITIONAL_ACCESS uint8 = 9 // 0000 1001 (0x09)
LANGUAGE uint8 = 10 // 0000 1010 (0x0A)
SYSTEM_CLOCK uint8 = 11 // 0000 1011 (0x0B)
DOLBY_DIGITAL uint8 = 12 // 0000 1100 (0x0C)
COPYRIGHT uint8 = 13 // 0000 1101 (0x0D)
MAXIMUM_BITRATE uint8 = 14 // 0000 1110 (0x0E)
AVC_VIDEO uint8 = 40 // 0010 1000 (0x28)
STREAM_IDENTIFIER uint8 = 82 // 0101 0010 (0x52)
TTML_SUBTITLING uint8 = 127 // 0111 1111 (0x7F)
SCTE_ADAPTATION uint8 = 151 // 1001 0111 (0x97)
DOLBY_VISION uint8 = 176 // 1011 0000 (0xB0)
EBP uint8 = 233 // 1110 1001 (0xE9)
EC3 uint8 = 204 // 1100 1100 (0xCC)
)
// ISO_639 Audio service type
const (
AUDIO_UNDEFINED int = 0 // 0000 0000 (0x00)
AUDIO_DESCRIPTION int = 3 // 0000 0011 (0x03)
AUDIO_PRIMARY int = 128 // 1000 0000 (0x80)
)
// PmtDescriptor represents operations currently necessary on descriptors found in the PMT
type PmtDescriptor interface {
Tag() uint8
Format() string
IsIso639LanguageDescriptor() bool
IsMaximumBitrateDescriptor() bool
IsIFrameProfile() bool
IsEBPDescriptor() bool
DecodeMaximumBitRate() uint32
DecodeIso639LanguageCode() string
DecodeIso639AudioType() byte
IsDolbyATMOS() bool
IsDolbyVision() bool
DecodeDolbyVisionCodec(string) string
IsTTMLSubtitlingDescriptor() bool
DecodeTTMLIso639LanguageCode() string
DecodeTTMLSubtitlePurpose() uint
}
type pmtDescriptor struct {
tag uint8
data []byte
}
// NewPmtDescriptor creates a new PMTDescriptor with the provided tag and byte contents.
func NewPmtDescriptor(tag uint8, data []byte) PmtDescriptor {
descriptor := &pmtDescriptor{}
descriptor.tag = tag
descriptor.data = data
return descriptor
}
func (descriptor *pmtDescriptor) Tag() uint8 {
return descriptor.tag
}
func (descriptor *pmtDescriptor) String() string {
return descriptor.decode()
}
func (descriptor *pmtDescriptor) Format() string {
return fmt.Sprintf("[tag=%b, decoded=%s]\n", descriptor.tag, descriptor.decode())
}
func (descriptor *pmtDescriptor) decode() string {
switch descriptor.tag {
case LANGUAGE:
return fmt.Sprintf("ISO 639 Language (code=%s, audioType=%b)",
descriptor.DecodeIso639LanguageCode(), descriptor.DecodeIso639AudioType())
case MAXIMUM_BITRATE:
return fmt.Sprintf("Maximum Bit-Rate (%d)", descriptor.DecodeMaximumBitRate())
case VIDEO_STREAM:
return fmt.Sprintf("Video Stream (%d)", descriptor.tag)
case AUDIO_STREAM:
return fmt.Sprintf("Audio Stream (%d)", descriptor.tag)
case REGISTRATION:
return fmt.Sprintf("Registration (%d)", descriptor.tag)
case CONDITIONAL_ACCESS:
return fmt.Sprintf("Conditional Access (%d)", descriptor.tag)
case SYSTEM_CLOCK:
return fmt.Sprintf("System Clock (%d)", descriptor.tag)
case COPYRIGHT:
return fmt.Sprintf("Copyright (%d)", descriptor.tag)
case AVC_VIDEO:
return fmt.Sprintf("AVC Video (%d)", descriptor.tag)
case DOLBY_DIGITAL:
return fmt.Sprintf("Dolby Digital (%d)", descriptor.tag)
case SCTE_ADAPTATION:
return fmt.Sprintf("SCTE Adaptation (%d)", descriptor.tag)
case DOLBY_VISION:
return fmt.Sprintf("Dolby Vision (%d)", descriptor.tag)
case EBP:
return fmt.Sprintf("EBP (%d)", descriptor.tag)
case STREAM_IDENTIFIER:
return fmt.Sprintf("Stream Identifier (%d): %v", descriptor.tag, descriptor.data[0])
case TTML_SUBTITLING:
return fmt.Sprintf("TTML Subtitling (tagExt=0x%X, code=%s)",
descriptor.DecodeTTMLDescTagExtension(), descriptor.DecodeTTMLIso639LanguageCode())
}
return "unknown tag (" + strconv.Itoa(int(descriptor.tag)) + ")"
}
func (descriptor *pmtDescriptor) IsIso639LanguageDescriptor() bool {
return descriptor.tag == LANGUAGE
}
func (descriptor *pmtDescriptor) IsMaximumBitrateDescriptor() bool {
return descriptor.tag == MAXIMUM_BITRATE
}
func (descriptor *pmtDescriptor) IsEBPDescriptor() bool {
return descriptor.tag == EBP
}
// Return the decoded Maximum_bitrate in units of 50 bytes per second
func (descriptor *pmtDescriptor) DecodeMaximumBitRate() uint32 {
if descriptor.IsMaximumBitrateDescriptor() {
return uint32(descriptor.data[0]&0x1f)<<16 | uint32(descriptor.data[1])<<8 | uint32(descriptor.data[2])
}
return 0
}
func (descriptor *pmtDescriptor) DecodeIso639LanguageCode() string {
if LANGUAGE == descriptor.tag {
return string(descriptor.data[0:3])
}
return ""
}
func (descriptor *pmtDescriptor) DecodeIso639AudioType() byte {
return descriptor.data[3]
}
func (descriptor *pmtDescriptor) DecodeTTMLDescTagExtension() byte {
return descriptor.data[0]
}
func (descriptor *pmtDescriptor) IsTTMLSubtitlingDescriptor() bool {
return descriptor.tag == TTML_SUBTITLING
}
func (descriptor *pmtDescriptor) DecodeTTMLIso639LanguageCode() string {
if descriptor.tag == TTML_SUBTITLING {
return string(descriptor.data[1:4])
}
return ""
}
func (descriptor *pmtDescriptor) DecodeTTMLSubtitlePurpose() uint {
if descriptor.tag == TTML_SUBTITLING {
return uint(descriptor.data[4] >> 2) // First 6 bits of the 5th bytes
}
return 0xFF
}
// IsIFrameProfile determines from the PMT if the profile is an I-Frame only track
// or not. An I-Frame only track is defined to be true if and only if the
// 'EBP_distance' is equal to '1'. The 'EBP_distance' is found in the PMT EBP
// descriptor as defined on page 16-17 of OC-SP-EBP-I01-130018.pdf.
// https://www.teamccp.com/confluence/download/attachments/59024185/OC-SP-EBP-I01-130118.pdf?version=1&modificationDate=1378666671000&api=v2
func (descriptor *pmtDescriptor) IsIFrameProfile() bool {
if EBP == descriptor.tag && 0 < len(descriptor.data) {
offset := 0
num_partitions := uint8((descriptor.data[offset] & 0xF8) >> 3)
timescale_flag := 1 == uint8((descriptor.data[offset]&0x04)>>2)
offset++
var EBP_distance_width_minus_1 uint8
if timescale_flag {
return false
}
indx := uint8(0)
for indx < num_partitions {
indx++
EBP_data_explicit_flag := 1 == uint8((descriptor.data[offset]&0x80)>>7)
representation_id_flag := 1 == uint8((descriptor.data[offset]&0x04)>>6)
if EBP_data_explicit_flag {
offset++
if 0 == EBP_distance_width_minus_1 {
EBP_distance := uint8(descriptor.data[offset])
return 1 == EBP_distance
} else {
return false
}
} else {
offset += 2
}
if representation_id_flag {
offset += 8
}
}
return false
}
return false
}
// IsDolbyATMOS checks to see if the flag_ec3_extension_type_a flag is set
// The additional data added to the enhanced-AC3 descriptor in the additional_info_byte field section looks like this:
// flag_ec3_extension_type_reserved 7 bslbf
// flag_ec3_extension_type_a 1 bslbf
// A52-2015 Annex G Table G.1
func (descriptor *pmtDescriptor) IsDolbyATMOS() bool {
if descriptor.tag == EC3 && len(descriptor.data) >= 2 {
// reserved 1 bit '1'
bsid_flag := 1 == uint8((descriptor.data[0]&0x40)>>6) // 1 bit
mainid_flag := 1 == uint8((descriptor.data[0]&0x20)>>5) // 1 bit
asvc_flag := 1 == uint8((descriptor.data[0]&0x10)>>4) // 1 bit
// mixinfoexists := 1 == uint8((descriptor.data[0]&0x08)>>3) // 1 bit
substream1_flag := 1 == uint8((descriptor.data[0]&0x04)>>2) // 1 bit
substream2_flag := 1 == uint8((descriptor.data[0]&0x02)>>1) // 1 bit
substream3_flag := 1 == uint8(descriptor.data[0]&0x01) // 1 bit
// data[1] not needed: reserved 1, full_service_flag 1, audio_service_type 3, number_of_channels 3
language_flag := false
language_flag_2 := false
start := uint8(2)
if bsid_flag {
language_flag = 1 == uint8((descriptor.data[start]&0x80)>>7) // 1 bit
language_flag_2 = 1 == uint8((descriptor.data[start]&0x40)>>6) // 1 bit
start++
}
if mainid_flag {
start++
}
if asvc_flag {
start++
}
if substream1_flag {
start++
}
if substream2_flag {
start++
}
if substream3_flag {
start++
}
if language_flag {
start += 3
}
if language_flag_2 {
start += 3
}
if substream1_flag {
start += 3
}
if substream2_flag {
start += 3
}
if substream3_flag {
start += 3
}
for i := start; i < uint8(len(descriptor.data)); i++ {
if 0x01 == descriptor.data[i] {
return true
}
}
}
return false
}
// Check if the registration data is set to `DOVI`
// dolby-vision-bitstreams-in-mpeg-2-transport-stream-multiplex-v1.2, Section 3.1
func (descriptor *pmtDescriptor) IsDolbyVision() bool {
if descriptor.tag == REGISTRATION {
if len(descriptor.data) >= 4 {
return binary.BigEndian.Uint32(descriptor.data[:4]) == 0x444F5649
}
}
return false
}
// Parse the profile and level of dolby vision
// dolby-vision-bitstreams-in-mpeg-2-transport-stream-multiplex-v1.2, Section 3.3
func (descriptor *pmtDescriptor) DecodeDolbyVisionCodec(originalCodec string) string {
if descriptor.tag == DOLBY_VISION && len(descriptor.data) >= 4 {
num := binary.BigEndian.Uint16(descriptor.data[2:4])
dv_profile := uint8((num & 0xFE00) >> 9)
dv_level := uint8((num & 0xFC) >> 3)
// Hardcode to dvhe because this is MPEG2-TS which doesn't support h265
// The codec strings of dvc1 vs dvhe is an mp4 specific thing
// dolby-vision-streams-within-the-mpeg-dash-format-v2.0 see Table 2, section 4.2.3
baseCodec := "dvhe"
return fmt.Sprintf("%s.%02d.%02d", baseCodec, dv_profile, dv_level)
}
return ""
}