Monibuca / engine

Monibuca 核心引擎,包含流媒体核心转发逻辑,需要配合功能插件一起组合运行
MIT License
944 stars 200 forks source link

MemoryTS 输出的ts 文件 没有音频编码信息 #78

Open mars79668 opened 1 year ago

mars79668 commented 1 year ago

参考 hls 插件实现 ,使用memoryTs 输出的ts 文件没有音频编码信息,大致的代码如下: 直接使用hls 插件,输出的 ts 也无法获取 音视频信息,测试 拉流地址 http://huadonglive.starschinalive.com/stars/cjpd_stars-hd.m3u8?auth_key=1677980353-0-0-76a9f3c2dbaef16cf6b0f9d12b52eb5d

   func (hls *HLSWriter) OnEvent(event any) {
    switch v := event.(type) {
    case *track.Video:
        if hls.Audio != nil {
            hls.ts.WritePMTPacket(hls.Audio.CodecID, v.CodecID)
        } else {
            hls.ts.WritePMTPacket(0, v.CodecID)
        }
        hls.AddTrack(v)
    case *track.Audio:
        if hls.Video != nil {
            hls.ts.WritePMTPacket(v.CodecID, hls.Video.CodecID)
        } else {
            hls.ts.WritePMTPacket(v.CodecID, 0)
        }
        hls.AddTrack(v)
    case AudioFrame:
        pes := &mpegts.MpegtsPESFrame{
            Pid:                       mpegts.PID_AUDIO,
            IsKeyFrame:                false,
            ContinuityCounter:         hls.audio_cc,
            ProgramClockReferenceBase: uint64(v.DTS),
        }
        if err := hls.ts.WriteAudioFrame(v, pes); err != nil {
            hls.Error("WriteVideoFrame", zap.Error(err))
            return
        }
        hls.audio_cc = pes.ContinuityCounter
    case VideoFrame:
        if v.IFrame {
            // hls.ts.WriteTo(hFile)
            hls.frag(hls.Stream.Path, v.AbsTime, v)
        }
        pes := &mpegts.MpegtsPESFrame{
            Pid:                       mpegts.PID_VIDEO,
            IsKeyFrame:                v.IFrame,
            ContinuityCounter:         hls.video_cc,
            ProgramClockReferenceBase: uint64(v.DTS),
        }
        if err := hls.ts.WriteVideoFrame(v, pes); err != nil {
            hls.Error("WriteVideoFrame", zap.Error(err))
            return
        }
        hls.video_cc = pes.ContinuityCounter

    default:
        hls.Subscriber.OnEvent(event)
    }
}

ffprobe 信息如下

[mpegts @ 0x11fde40] PES packet size mismatch
    Last message repeated 5 times
[mpegts @ 0x11fde40] decoding for stream 0 failed
[mpegts @ 0x11fde40] decoding for stream 1 failed
[mpegts @ 0x11fde40] PES packet size mismatch
    Last message repeated 5 times
[mpegts @ 0x11fde40] Could not find codec parameters for stream 1 (Audio: aac ([15][0][0][0] / 0x000F), 0 channels, fltp): unspecified sample rate
Consider increasing the value for the 'analyzeduration' and 'probesize' options
Input #0, mpegts, from 'monibuca/output/hls/gtzy/cctv5P480/s15/cctv5-2303081101-18747-18765.ts':
  Duration: 00:00:00.27, start: 18765.786033, bitrate: 589 kb/s
  Program 1 
    Stream #0:0[0x101]: Video: h264 (High) ([27][0][0][0] / 0x001B), yuv420p(progressive), 640x480 [SAR 4:3 DAR 16:9], 30 fps, 30 tbr, 90k tbn, 60 tbc
    Stream #0:1[0x102]: Audio: aac ([15][0][0][0] / 0x000F), 0 channels, fltp
langhuihui commented 1 year ago
func (ts *MemoryTs) WriteTo(w io.Writer) (int64, error) {
    w.Write(mpegts.DefaultPATPacket)
    w.Write(ts.PMT)
    return ts.BLL.WriteTo(w)
}

注意写入的数据,PAT+PMT+PES

mars79668 commented 1 year ago

最新版本 hls 插件不做任何改动测试 hls拉流发布,flv 订阅 可以预览播放, hls 订阅 无法播放,没有编码信息 最新版本的engin 好像不稳定, flv 播放大约 2-3 分钟后就无法播放了,订阅收不到内容

我看之前版本的代码,音频在 AudioDeConf AudioPacketToPES 有处理写入编码信息,视频 VideoPacketToPES 也有使用 Track.DecoderConfiguration 处理写入视频编码信息, memoryts 代码好像没有找到写入 音视频编码的信息

langhuihui commented 1 year ago

hls拉流发布我抽空测一下,估计之前的改动影响到了这个

mars79668 commented 1 year ago

我提供的测试直播流,有我之前反馈的 ts 包乱序的问题,需要修正

package mpegts

import (
    "bytes"
    "errors"
    "io"
)

type mpegTsStreamStatus struct {
    pesPkt *MpegTsPESPacket
    frames int
}

func (s *MpegTsStream) Feed(ts io.Reader) (err error) {
    var reader bytes.Reader
    var lr io.LimitedReader
    lr.R = &reader
    var tsHeader MpegTsHeader
    tsData := make([]byte, TS_PACKET_SIZE)

    streamsStatus := make(map[uint16]*mpegTsStreamStatus)

    for {
        _, err = io.ReadFull(ts, tsData)
        if err == io.EOF {
            // 文件结尾 把最后面的数据发出去
            for _, ss := range streamsStatus {
                if ss.pesPkt != nil {
                    s.PESChan <- ss.pesPkt
                    ss.pesPkt = nil
                }
            }
            return nil
        } else if err != nil {
            return
        }
        reader.Reset(tsData)
        lr.N = TS_PACKET_SIZE
        if tsHeader, err = ReadTsHeader(&lr); err != nil {
            return
        }
        if tsHeader.SyncByte != 0x47 {
            return errors.New("sync byte error")
        }
        if tsHeader.Pid == PID_PAT {
            if s.PAT, err = ReadPAT(&lr); err != nil {
                return
            }
            continue
        }
        if len(s.PMT.Stream) == 0 {
            for _, v := range s.PAT.Program {
                if v.ProgramMapPID == tsHeader.Pid {
                    if s.PMT, err = ReadPMT(&lr); err != nil {
                        return
                    }
                }
                continue
            }
        }
        for _, v := range s.PMT.Stream {
            if v.ElementaryPID == tsHeader.Pid {
                ss := streamsStatus[v.ElementaryPID]
                if ss == nil {
                    ss = &mpegTsStreamStatus{}
                    streamsStatus[v.ElementaryPID] = ss
                }
                if tsHeader.PayloadUnitStartIndicator == 1 {
                    if ss.pesPkt != nil {
                        s.PESChan <- ss.pesPkt
                    }
                    ss.pesPkt = &MpegTsPESPacket{}
                    if ss.pesPkt.Header, err = ReadPESHeader(&lr); err != nil {
                        return
                    }
                }
                io.Copy(&ss.pesPkt.Payload, &lr)
            }
        }
    }
}