github.com/cnotch/ipchub@v1.1.0/av/format/sdp/parsemeta.go (about)

     1  // Copyright (c) 2019,CAOHONGJU All rights reserved.
     2  // Use of this source code is governed by a MIT-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package sdp
     6  
     7  import (
     8  	"encoding/base64"
     9  	"encoding/hex"
    10  	"strings"
    11  
    12  	"github.com/cnotch/ipchub/av/codec"
    13  	"github.com/cnotch/ipchub/av/codec/aac"
    14  	"github.com/cnotch/ipchub/av/codec/h264"
    15  	"github.com/cnotch/ipchub/av/codec/hevc"
    16  	"github.com/cnotch/ipchub/utils/scan"
    17  	"github.com/pixelbender/go-sdp/sdp"
    18  )
    19  
    20  func ParseMetadata(rawsdp string, video *codec.VideoMeta, audio *codec.AudioMeta) error {
    21  	sdp, err := sdp.ParseString(rawsdp)
    22  	if err != nil {
    23  		return err
    24  	}
    25  
    26  	for _, media := range sdp.Media {
    27  		switch media.Type {
    28  		case "video":
    29  			video.Codec = media.Format[0].Name
    30  			if video.Codec != "" {
    31  				for _, bw := range media.Bandwidth {
    32  					if bw.Type == "AS" {
    33  						video.DataRate = float64(bw.Value)
    34  					}
    35  				}
    36  				parseVideoMeta(media.Format[0], video)
    37  			}
    38  
    39  		case "audio":
    40  			audio.Codec = media.Format[0].Name
    41  			if audio.Codec == "MPEG4-GENERIC" {
    42  				audio.Codec = "AAC"
    43  			}
    44  
    45  			if audio.Codec != "" {
    46  				for _, bw := range media.Bandwidth {
    47  					if bw.Type == "AS" {
    48  						audio.DataRate = float64(bw.Value)
    49  					}
    50  				}
    51  				parseAudioMeta(media.Format[0], audio)
    52  			}
    53  		}
    54  	}
    55  	return nil
    56  }
    57  
    58  func parseAudioMeta(m *sdp.Format, audio *codec.AudioMeta) {
    59  	audio.SampleSize = 16
    60  	audio.Channels = 2
    61  	audio.SampleRate = 44100
    62  	if m.ClockRate > 0 {
    63  		audio.SampleRate = m.ClockRate
    64  	}
    65  	if m.Channels > 0 {
    66  		audio.Channels = m.Channels
    67  	}
    68  
    69  	// parse AAC config
    70  	if len(m.Params) == 0 {
    71  		return
    72  	}
    73  	if audio.Codec == "AAC" {
    74  		for _, p := range m.Params {
    75  			i := strings.Index(p, "config=")
    76  			if i < 0 {
    77  				continue
    78  			}
    79  			p = p[i+len("config="):]
    80  
    81  			endi := strings.IndexByte(p, ';')
    82  			if endi > -1 {
    83  				p = p[:endi]
    84  			}
    85  
    86  			var config []byte
    87  			var err error
    88  			if config, err = hex.DecodeString(p); err != nil {
    89  				config = aac.Encode2BytesASC(2,
    90  					byte(aac.SamplingIndex(audio.SampleRate)),
    91  					byte(audio.Channels))
    92  			}
    93  
    94  			// audio.SetParameterSet(aac.ParameterSetConfig, config)
    95  			audio.Sps = config
    96  			_ = aac.MetadataIsReady(audio)
    97  			break
    98  		}
    99  	}
   100  
   101  }
   102  
   103  func parseVideoMeta(m *sdp.Format, video *codec.VideoMeta) {
   104  	if m.ClockRate > 0 {
   105  		video.ClockRate = m.ClockRate
   106  	}
   107  
   108  	if len(m.Params) == 0 {
   109  		return
   110  	}
   111  	switch video.Codec {
   112  	case "h264", "H264":
   113  		video.Codec = "H264"
   114  		for _, p := range m.Params {
   115  			i := strings.Index(p, "sprop-parameter-sets=")
   116  			if i < 0 {
   117  				continue
   118  			}
   119  			p = p[i+len("sprop-parameter-sets="):]
   120  
   121  			endi := strings.IndexByte(p, ';')
   122  			if endi > -1 {
   123  				p = p[:endi]
   124  			}
   125  			parseH264SpsPps(p, video)
   126  			break
   127  		}
   128  	case "h265", "H265", "hevc", "HEVC":
   129  		video.Codec = "H265"
   130  		for _, p := range m.Params {
   131  			i := strings.Index(p, "sprop-")
   132  			if i < 0 {
   133  				continue
   134  			}
   135  			parseH265VpsSpsPps(p[i:], video)
   136  			break
   137  		}
   138  	}
   139  }
   140  
   141  func parseH264SpsPps(s string, video *codec.VideoMeta) {
   142  	ppsStr, spsStr, ok := scan.Comma.Scan(s)
   143  	if !ok {
   144  		return
   145  	}
   146  
   147  	sps, err := base64.StdEncoding.DecodeString(spsStr)
   148  	if err == nil {
   149  		// video.SetParameterSet(h264.ParameterSetSps, sps)
   150  		video.Sps = sps
   151  	}
   152  
   153  	pps, err := base64.StdEncoding.DecodeString(ppsStr)
   154  	if err == nil {
   155  		// video.SetParameterSet(h264.ParameterSetPps, pps)
   156  		video.Pps = pps
   157  	}
   158  
   159  	_ = h264.MetadataIsReady(video)
   160  }
   161  
   162  func parseH265VpsSpsPps(s string, video *codec.VideoMeta) {
   163  	var advance, token string
   164  	continueScan := true
   165  	advance = s
   166  	for continueScan {
   167  		advance, token, continueScan = scan.Semicolon.Scan(advance)
   168  		name, value, ok := scan.EqualPair.Scan(token)
   169  		if ok {
   170  			var ps *[]byte
   171  			var err error
   172  			switch name {
   173  			case "sprop-vps":
   174  				ps = &video.Vps
   175  			case "sprop-sps":
   176  				ps = &video.Sps
   177  			case "sprop-pps":
   178  				ps = &video.Pps
   179  			}
   180  			if ps == nil {
   181  				continue
   182  			}
   183  
   184  			if *ps, err = base64.StdEncoding.DecodeString(value); err != nil {
   185  				return
   186  			}
   187  		}
   188  	}
   189  
   190  	_ = hevc.MetadataIsReady(video)
   191  }