mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
FLV: Support set default has_av and disable guessing. v5.0.110 (#3311)
* FLV: Support set default has_av and disable guessing. v5.0.110 1. Support config default has_audio and has_video. 2. Support disable guessing has_audio or has_video. * FLV: Reset to false if start to guess has_av. * FLV: Add regression test for FLV header av metadata.
This commit is contained in:
parent
4551200e95
commit
a36cb57949
11 changed files with 624 additions and 84 deletions
75
trunk/3rdparty/srs-bench/srs/rtc_test.go
vendored
75
trunk/3rdparty/srs-bench/srs/rtc_test.go
vendored
|
@ -2022,71 +2022,44 @@ func TestRtcPublish_FlvPlay(t *testing.T) {
|
|||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-publishReady.Done():
|
||||
var url string = "http://127.0.0.1:8080" + *srsStream + "-" + streamSuffix + ".flv"
|
||||
logger.Tf(ctx, "Run play flv url=%v", url)
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||
if err != nil {
|
||||
logger.Tf(ctx, "New request for flv %v failed, err=%v", url, err)
|
||||
return
|
||||
}
|
||||
player := NewFLVPlayer()
|
||||
defer player.Close()
|
||||
|
||||
client := http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
logger.Tf(ctx, "Http get flv %v failed, err=%v", url, err)
|
||||
return
|
||||
}
|
||||
|
||||
var f flv.Demuxer
|
||||
if f, err = flv.NewDemuxer(resp.Body); err != nil {
|
||||
logger.Tf(ctx, "Create flv demuxer for %v failed, err=%v", url, err)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var hasVideo, hasAudio bool
|
||||
if _, hasVideo, hasAudio, err = f.ReadHeader(); err != nil {
|
||||
logger.Tf(ctx, "Flv demuxer read header failed, err=%v", err)
|
||||
return
|
||||
r3 = func() error {
|
||||
flvUrl := fmt.Sprintf("http://%v%v-%v.flv", *srsHttpServer, *srsStream, streamSuffix)
|
||||
if err := player.Play(ctx, flvUrl); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var nnVideo, nnAudio int
|
||||
var prevVideoTimestamp, prevAudioTimestamp int64
|
||||
|
||||
for {
|
||||
var tagType flv.TagType
|
||||
var tagSize, timestamp uint32
|
||||
if tagType, tagSize, timestamp, err = f.ReadTagHeader(); err != nil {
|
||||
logger.Tf(ctx, "Flv demuxer read tag header failed, err=%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
var tag []byte
|
||||
if tag, err = f.ReadTag(tagSize); err != nil {
|
||||
logger.Tf(ctx, "Flv demuxer read tag failed, err=%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
var hasVideo, hasAudio bool
|
||||
player.onRecvHeader = func(ha, hv bool) error {
|
||||
hasAudio, hasVideo = ha, hv
|
||||
return nil
|
||||
}
|
||||
player.onRecvTag = func(tagType flv.TagType, size, timestamp uint32, tag []byte) error {
|
||||
if tagType == flv.TagTypeAudio {
|
||||
nnAudio++
|
||||
prevAudioTimestamp = (int64)(timestamp)
|
||||
} else if tagType == flv.TagTypeVideo {
|
||||
nnVideo++
|
||||
prevVideoTimestamp = (int64)(timestamp)
|
||||
}
|
||||
logger.Tf(ctx, "got %v tag, %v %vms %vB", nnVideo+nnAudio, tagType, timestamp, len(tag))
|
||||
|
||||
audioPacketsOK, videoPacketsOK := !hasAudio || nnAudio >= 10, !hasVideo || nnVideo >= 10
|
||||
if audioPacketsOK && videoPacketsOK {
|
||||
avDiff := prevVideoTimestamp - prevAudioTimestamp
|
||||
logger.Tf(ctx, "Flv recv %v/%v audio, %v/%v video, avDiff=%v", hasAudio, nnAudio, hasVideo, nnVideo, avDiff)
|
||||
if audioPacketsOK, videoPacketsOK := !hasAudio || nnAudio >= 10, !hasVideo || nnVideo >= 10; audioPacketsOK && videoPacketsOK {
|
||||
logger.Tf(ctx, "Flv recv %v/%v audio, %v/%v video", hasAudio, nnAudio, hasVideo, nnVideo)
|
||||
cancel()
|
||||
break
|
||||
}
|
||||
|
||||
_ = tag
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if err := player.Consume(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}()
|
||||
}()
|
||||
}
|
||||
|
|
244
trunk/3rdparty/srs-bench/srs/rtmp_test.go
vendored
244
trunk/3rdparty/srs-bench/srs/rtmp_test.go
vendored
|
@ -24,6 +24,7 @@ import (
|
|||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/pkg/errors"
|
||||
"math/rand"
|
||||
"os"
|
||||
"sync"
|
||||
|
@ -393,3 +394,246 @@ func TestRtmpPublish_MultipleSequences_RtcPlay(t *testing.T) {
|
|||
t.Errorf("err %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRtmpPublish_FlvPlay(t *testing.T) {
|
||||
ctx := logger.WithContext(context.Background())
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Duration(*srsTimeout)*time.Millisecond)
|
||||
|
||||
var r0, r1 error
|
||||
err := func() error {
|
||||
publisher := NewRTMPPublisher()
|
||||
defer publisher.Close()
|
||||
|
||||
player := NewFLVPlayer()
|
||||
defer player.Close()
|
||||
|
||||
// Connect to RTMP URL.
|
||||
streamSuffix := fmt.Sprintf("rtmp-regression-%v-%v", os.Getpid(), rand.Int())
|
||||
rtmpUrl := fmt.Sprintf("rtmp://%v/live/%v", *srsServer, streamSuffix)
|
||||
flvUrl := fmt.Sprintf("http://%v/live/%v.flv", *srsHttpServer, streamSuffix)
|
||||
|
||||
if err := publisher.Publish(ctx, rtmpUrl); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := player.Play(ctx, flvUrl); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check packets.
|
||||
var wg sync.WaitGroup
|
||||
defer wg.Wait()
|
||||
|
||||
publisherReady, publisherReadyCancel := context.WithCancel(context.Background())
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
time.Sleep(30 * time.Millisecond) // Wait for publisher to push sequence header.
|
||||
publisherReadyCancel()
|
||||
}()
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
<-publisherReady.Done()
|
||||
|
||||
var nnPackets int
|
||||
player.onRecvHeader = func(hasAudio, hasVideo bool) error {
|
||||
return nil
|
||||
}
|
||||
player.onRecvTag = func(tp flv.TagType, size, ts uint32, tag []byte) error {
|
||||
logger.Tf(ctx, "got %v tag, %v %vms %vB", nnPackets, tp, ts, len(tag))
|
||||
if nnPackets += 1; nnPackets > 50 {
|
||||
cancel()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if r1 = player.Consume(ctx); r1 != nil {
|
||||
cancel()
|
||||
}
|
||||
}()
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
publisher.onSendPacket = func(m *rtmp.Message) error {
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
return nil
|
||||
}
|
||||
if r0 = publisher.Ingest(ctx, *srsPublishAvatar); r0 != nil {
|
||||
cancel()
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}()
|
||||
if err := filterTestError(ctx.Err(), err, r0, r1); err != nil {
|
||||
t.Errorf("err %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRtmpPublish_FlvPlayNoAudio(t *testing.T) {
|
||||
ctx := logger.WithContext(context.Background())
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Duration(*srsTimeout)*time.Millisecond)
|
||||
|
||||
var r0, r1 error
|
||||
err := func() error {
|
||||
publisher := NewRTMPPublisher()
|
||||
defer publisher.Close()
|
||||
|
||||
// Set publisher to drop audio.
|
||||
publisher.hasAudio = false
|
||||
|
||||
player := NewFLVPlayer()
|
||||
defer player.Close()
|
||||
|
||||
// Connect to RTMP URL.
|
||||
streamSuffix := fmt.Sprintf("rtmp-regression-%v-%v", os.Getpid(), rand.Int())
|
||||
rtmpUrl := fmt.Sprintf("rtmp://%v/live/%v", *srsServer, streamSuffix)
|
||||
flvUrl := fmt.Sprintf("http://%v/live/%v.flv", *srsHttpServer, streamSuffix)
|
||||
|
||||
if err := publisher.Publish(ctx, rtmpUrl); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := player.Play(ctx, flvUrl); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check packets.
|
||||
var wg sync.WaitGroup
|
||||
defer wg.Wait()
|
||||
|
||||
publisherReady, publisherReadyCancel := context.WithCancel(context.Background())
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
time.Sleep(30 * time.Millisecond) // Wait for publisher to push sequence header.
|
||||
publisherReadyCancel()
|
||||
}()
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
<-publisherReady.Done()
|
||||
|
||||
var nnPackets int
|
||||
player.onRecvHeader = func(hasAudio, hasVideo bool) error {
|
||||
return nil
|
||||
}
|
||||
player.onRecvTag = func(tp flv.TagType, size, ts uint32, tag []byte) error {
|
||||
if tp == flv.TagTypeAudio {
|
||||
return errors.New("should no audio")
|
||||
}
|
||||
logger.Tf(ctx, "got %v tag, %v %vms %vB", nnPackets, tp, ts, len(tag))
|
||||
if nnPackets += 1; nnPackets > 50 {
|
||||
cancel()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if r1 = player.Consume(ctx); r1 != nil {
|
||||
cancel()
|
||||
}
|
||||
}()
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
publisher.onSendPacket = func(m *rtmp.Message) error {
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
return nil
|
||||
}
|
||||
if r0 = publisher.Ingest(ctx, *srsPublishAvatar); r0 != nil {
|
||||
cancel()
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}()
|
||||
if err := filterTestError(ctx.Err(), err, r0, r1); err != nil {
|
||||
t.Errorf("err %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRtmpPublish_FlvPlayNoVideo(t *testing.T) {
|
||||
ctx := logger.WithContext(context.Background())
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Duration(*srsTimeout)*time.Millisecond)
|
||||
|
||||
var r0, r1 error
|
||||
err := func() error {
|
||||
publisher := NewRTMPPublisher()
|
||||
defer publisher.Close()
|
||||
|
||||
// Set publisher to drop video.
|
||||
publisher.hasVideo = false
|
||||
|
||||
player := NewFLVPlayer()
|
||||
defer player.Close()
|
||||
|
||||
// Connect to RTMP URL.
|
||||
streamSuffix := fmt.Sprintf("rtmp-regression-%v-%v", os.Getpid(), rand.Int())
|
||||
rtmpUrl := fmt.Sprintf("rtmp://%v/live/%v", *srsServer, streamSuffix)
|
||||
flvUrl := fmt.Sprintf("http://%v/live/%v.flv", *srsHttpServer, streamSuffix)
|
||||
|
||||
if err := publisher.Publish(ctx, rtmpUrl); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := player.Play(ctx, flvUrl); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check packets.
|
||||
var wg sync.WaitGroup
|
||||
defer wg.Wait()
|
||||
|
||||
publisherReady, publisherReadyCancel := context.WithCancel(context.Background())
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
time.Sleep(30 * time.Millisecond) // Wait for publisher to push sequence header.
|
||||
publisherReadyCancel()
|
||||
}()
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
<-publisherReady.Done()
|
||||
|
||||
var nnPackets int
|
||||
player.onRecvHeader = func(hasAudio, hasVideo bool) error {
|
||||
return nil
|
||||
}
|
||||
player.onRecvTag = func(tp flv.TagType, size, ts uint32, tag []byte) error {
|
||||
if tp == flv.TagTypeVideo {
|
||||
return errors.New("should no video")
|
||||
}
|
||||
logger.Tf(ctx, "got %v tag, %v %vms %vB", nnPackets, tp, ts, len(tag))
|
||||
if nnPackets += 1; nnPackets > 50 {
|
||||
cancel()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if r1 = player.Consume(ctx); r1 != nil {
|
||||
cancel()
|
||||
}
|
||||
}()
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
publisher.onSendPacket = func(m *rtmp.Message) error {
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
return nil
|
||||
}
|
||||
if r0 = publisher.Ingest(ctx, *srsPublishAvatar); r0 != nil {
|
||||
cancel()
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}()
|
||||
if err := filterTestError(ctx.Err(), err, r0, r1); err != nil {
|
||||
t.Errorf("err %+v", err)
|
||||
}
|
||||
}
|
||||
|
|
153
trunk/3rdparty/srs-bench/srs/util.go
vendored
153
trunk/3rdparty/srs-bench/srs/util.go
vendored
|
@ -34,6 +34,7 @@ import (
|
|||
"io"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
|
@ -65,6 +66,7 @@ var srsDTLSDropPackets *int
|
|||
|
||||
var srsSchema string
|
||||
var srsServer *string
|
||||
var srsHttpServer *string
|
||||
var srsStream *string
|
||||
var srsLiveStream *string
|
||||
var srsPublishAudio *string
|
||||
|
@ -75,7 +77,8 @@ var srsVnetClientIP *string
|
|||
|
||||
func prepareTest() (err error) {
|
||||
srsHttps = flag.Bool("srs-https", false, "Whther connect to HTTPS-API")
|
||||
srsServer = flag.String("srs-server", "127.0.0.1", "The RTC server to connect to")
|
||||
srsServer = flag.String("srs-server", "127.0.0.1", "The RTMP/RTC server to connect to")
|
||||
srsHttpServer = flag.String("srs-http-server", "127.0.0.1:8080", "The HTTP server to connect to")
|
||||
srsStream = flag.String("srs-stream", "/rtc/regression", "The RTC app/stream to play")
|
||||
srsLiveStream = flag.String("srs-live-stream", "/live/livestream", "The LIVE app/stream to play")
|
||||
srsLog = flag.Bool("srs-log", false, "Whether enable the detail log")
|
||||
|
@ -1445,6 +1448,10 @@ type RTMPPublisher struct {
|
|||
client *RTMPClient
|
||||
// Whether auto close transport when ingest done.
|
||||
closeTransportWhenIngestDone bool
|
||||
// Whether drop audio, set the hasAudio to false.
|
||||
hasAudio bool
|
||||
// Whether drop video, set the hasVideo to false.
|
||||
hasVideo bool
|
||||
|
||||
onSendPacket func(m *rtmp.Message) error
|
||||
}
|
||||
|
@ -1456,6 +1463,7 @@ func NewRTMPPublisher() *RTMPPublisher {
|
|||
|
||||
// By default, set to on.
|
||||
v.closeTransportWhenIngestDone = true
|
||||
v.hasAudio, v.hasVideo = true, true
|
||||
|
||||
return v
|
||||
}
|
||||
|
@ -1465,6 +1473,7 @@ func (v *RTMPPublisher) Close() error {
|
|||
}
|
||||
|
||||
func (v *RTMPPublisher) Publish(ctx context.Context, rtmpUrl string) error {
|
||||
logger.Tf(ctx, "Publish %v", rtmpUrl)
|
||||
return v.client.Publish(ctx, rtmpUrl)
|
||||
}
|
||||
|
||||
|
@ -1483,7 +1492,8 @@ func (v *RTMPPublisher) Ingest(ctx context.Context, flvInput string) error {
|
|||
}()
|
||||
|
||||
// Consume all packets.
|
||||
err := v.ingest(flvInput)
|
||||
logger.Tf(ctx, "Start to ingest %v", flvInput)
|
||||
err := v.ingest(ctx, flvInput)
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
|
@ -1493,7 +1503,7 @@ func (v *RTMPPublisher) Ingest(ctx context.Context, flvInput string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (v *RTMPPublisher) ingest(flvInput string) error {
|
||||
func (v *RTMPPublisher) ingest(ctx context.Context, flvInput string) error {
|
||||
p := v.client
|
||||
|
||||
fs, err := os.Open(flvInput)
|
||||
|
@ -1501,6 +1511,7 @@ func (v *RTMPPublisher) ingest(flvInput string) error {
|
|||
return err
|
||||
}
|
||||
defer fs.Close()
|
||||
logger.Tf(ctx, "Open input %v", flvInput)
|
||||
|
||||
demuxer, err := flv.NewDemuxer(fs)
|
||||
if err != nil {
|
||||
|
@ -1525,6 +1536,12 @@ func (v *RTMPPublisher) ingest(flvInput string) error {
|
|||
if tagType != flv.TagTypeVideo && tagType != flv.TagTypeAudio {
|
||||
continue
|
||||
}
|
||||
if !v.hasAudio && tagType == flv.TagTypeAudio {
|
||||
continue
|
||||
}
|
||||
if !v.hasVideo && tagType == flv.TagTypeVideo {
|
||||
continue
|
||||
}
|
||||
|
||||
m := rtmp.NewStreamMessage(p.streamID)
|
||||
m.MessageType = rtmp.MessageType(tagType)
|
||||
|
@ -1577,6 +1594,9 @@ func (v *RTMPPlayer) Consume(ctx context.Context) error {
|
|||
var wg sync.WaitGroup
|
||||
defer wg.Wait()
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
@ -1618,6 +1638,133 @@ func (v *RTMPPlayer) consume() error {
|
|||
}
|
||||
}
|
||||
|
||||
type FLVPlayer struct {
|
||||
flvUrl string
|
||||
client *http.Client
|
||||
resp *http.Response
|
||||
f flv.Demuxer
|
||||
|
||||
onRecvHeader func(hasAudio, hasVideo bool) error
|
||||
onRecvTag func(tp flv.TagType, size, ts uint32, tag []byte) error
|
||||
}
|
||||
|
||||
func NewFLVPlayer() *FLVPlayer {
|
||||
return &FLVPlayer{
|
||||
client: &http.Client{}, resp: nil, f: nil, onRecvHeader: nil, onRecvTag: nil,
|
||||
}
|
||||
}
|
||||
|
||||
func (v *FLVPlayer) Close() error {
|
||||
if v.f != nil {
|
||||
v.f.Close()
|
||||
}
|
||||
if v.resp != nil {
|
||||
v.resp.Body.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *FLVPlayer) Play(ctx context.Context, flvUrl string) error {
|
||||
v.flvUrl = flvUrl
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *FLVPlayer) Consume(ctx context.Context) error {
|
||||
// If ctx is cancelled, close the RTMP transport.
|
||||
var wg sync.WaitGroup
|
||||
defer wg.Wait()
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
<-ctx.Done()
|
||||
v.Close()
|
||||
}()
|
||||
|
||||
// Start to play.
|
||||
if err := v.play(ctx, v.flvUrl); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Consume all packets.
|
||||
err := v.consume(ctx)
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
if ctx.Err() == context.Canceled {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (v *FLVPlayer) play(ctx context.Context, flvUrl string) error {
|
||||
logger.Tf(ctx, "Run play flv url=%v", flvUrl)
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", flvUrl, nil)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "New request for flv %v failed, err=%v", flvUrl, err)
|
||||
}
|
||||
|
||||
resp, err := v.client.Do(req)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "Http get flv %v failed, err=%v", flvUrl, err)
|
||||
}
|
||||
logger.Tf(ctx, "Connected to %v", flvUrl)
|
||||
|
||||
if v.resp != nil {
|
||||
v.resp.Body.Close()
|
||||
}
|
||||
v.resp = resp
|
||||
|
||||
f, err := flv.NewDemuxer(resp.Body)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "Create flv demuxer for %v failed, err=%v", flvUrl, err)
|
||||
}
|
||||
|
||||
if v.f != nil {
|
||||
v.f.Close()
|
||||
}
|
||||
v.f = f
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *FLVPlayer) consume(ctx context.Context) (err error) {
|
||||
var hasVideo, hasAudio bool
|
||||
if _, hasVideo, hasAudio, err = v.f.ReadHeader(); err != nil {
|
||||
return errors.Wrapf(err, "Flv demuxer read header failed, err=%v", err)
|
||||
}
|
||||
logger.Tf(ctx, "Got audio=%v, video=%v", hasAudio, hasVideo)
|
||||
|
||||
if v.onRecvHeader != nil {
|
||||
if err := v.onRecvHeader(hasAudio, hasVideo); err != nil {
|
||||
return errors.Wrapf(err, "Callback FLV header audio=%v, video=%v", hasAudio, hasVideo)
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
var tagType flv.TagType
|
||||
var tagSize, timestamp uint32
|
||||
if tagType, tagSize, timestamp, err = v.f.ReadTagHeader(); err != nil {
|
||||
return errors.Wrapf(err, "Flv demuxer read tag header failed, err=%v", err)
|
||||
}
|
||||
|
||||
var tag []byte
|
||||
if tag, err = v.f.ReadTag(tagSize); err != nil {
|
||||
return errors.Wrapf(err, "Flv demuxer read tag failed, err=%v", err)
|
||||
}
|
||||
|
||||
if v.onRecvTag != nil {
|
||||
if err := v.onRecvTag(tagType, tagSize, timestamp, tag); err != nil {
|
||||
return errors.Wrapf(err, "Callback tag type=%v, size=%v, ts=%v, tag=%vB", tagType, tagSize, timestamp, len(tag))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func IsAvccrEquals(a, b *avc.AVCDecoderConfigurationRecord) bool {
|
||||
if a == nil || b == nil {
|
||||
return false
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue