添加 app/flvfile2es 用于从flv文件中抽取h264 es流并写入文件

pull/200/head
q191201771 6 years ago
parent 2f6d036083
commit 03208d5a55

5
.gitignore vendored

@ -2,11 +2,12 @@
/coverage.txt
/TODO.md
/conf/self.conf.json
/lal
/lal_linux
/pkg/hls/
/demo/
/bin/
/.idea/
/.trash/
/logs/
/lal
/lal_linux

@ -0,0 +1,131 @@
package main
import (
"encoding/hex"
"flag"
"github.com/q191201771/lal/pkg/avc"
"github.com/q191201771/lal/pkg/httpflv"
"github.com/q191201771/lal/pkg/util/bele"
"github.com/q191201771/lal/pkg/util/errors"
"github.com/q191201771/lal/pkg/util/log"
"io"
"os"
)
var adtsHeader = make([]byte, 7)
func captureAVC(w io.Writer, payload []byte) {
// sps pps
if payload[0] == 0x17 && payload[1] == 0x00 {
sps, pps, err := avc.ParseAVCSeqHeader(payload)
errors.PanicIfErrorOccur(err)
_, _ = w.Write(avc.NaluStartCode)
_, _ = w.Write(sps)
_, _ = w.Write(avc.NaluStartCode)
_, _ = w.Write(pps)
return
}
// payload中可能存在多个nalu
// 先跳过前面type的2字节以及composition time的3字节
for i := 5; i != len(payload); {
naluLen := int(bele.BEUint32(payload[i:]))
i += 4
//naluUintType := payload[i] & 0x1f
//log.Debugf("naluLen:%d t:%d %s\n", naluLen, naluUintType, avc.NaluUintTypeMapping[naluUintType])
_, _ = w.Write(avc.NaluStartCode)
_, _ = w.Write(payload[i:i+naluLen])
i += naluLen
break
}
return
}
// TODO chef: mv to pkg
func captureAAC(w io.Writer, payload []byte) {
soundFormat := payload[0] >> 4
soundRate := (payload[0] >> 2) & 0x03
soundSize := (payload[0] >> 1) & 0x01
soundType := payload[0] & 0x01
if payload[1] == 0 {
audioObjectType := (payload[2] >> 3) & 0x1f // 5bit 编码结构类型
samplingFrequencyIndex := ((payload[2] & 0x07) << 1) | (payload[3] >> 7) // 4bit 音频采样率索引值
channelConfig := (payload[3] >> 3) & 0x0f // 4bit 音频输出声道
// syncword 12bit
// ID 1bit
// layer 2
// protection_absent 1bit
// profile 2bit
// sampling_frequency_index 4bit
// private_bit 1bit
// channel_configuration 3bit
// origin_copy 1bit
// home 1bit
adtsHeader[0] = 0xff // 8bit syncword 高8bit
adtsHeader[1] = 0xf0 // 4bit syncword 低4bit
// 1bit ID 0 for MPEG-4, 1 for MPEG-2
// 2bit layer 0
adtsHeader[1] |= 1 // 1bit protection absent
adtsHeader[2] = (audioObjectType - 1) << 6
log.Debugf(hex.Dump(payload[:4]))
log.Debugf("%d %d %d %d\n", soundFormat, soundRate, soundSize, soundType)
log.Debugf("%d %d %d", audioObjectType, samplingFrequencyIndex, channelConfig)
}
}
func main() {
var err error
flvFileName, aacFileName, avcFileName := parseFlag()
var ffr httpflv.FlvFileReader
err = ffr.Open(flvFileName)
errors.PanicIfErrorOccur(err)
defer ffr.Dispose()
log.Infof("open flv file succ.")
afp, err := os.Create(aacFileName)
errors.PanicIfErrorOccur(err)
defer afp.Close()
log.Infof("open es aac file succ.")
vfp, err := os.Create(avcFileName)
errors.PanicIfErrorOccur(err)
defer vfp.Close()
log.Infof("open es h264 file succ.")
_, err = ffr.ReadFlvHeader()
errors.PanicIfErrorOccur(err)
for {
tag, err := ffr.ReadTag()
if err == io.EOF {
log.Infof("EOF.")
break
}
errors.PanicIfErrorOccur(err)
payload := tag.Payload()
switch tag.Header.T {
case httpflv.TagTypeAudio:
captureAAC(afp, payload)
case httpflv.TagTypeVideo:
captureAVC(vfp, payload)
}
}
}
func parseFlag() (string, string, string) {
flv := flag.String("i", "", "specify flv file")
aac := flag.String("a", "", "specify es aac file")
avc := flag.String("v", "", "specify es h264 file")
flag.Parse()
if *flv == "" || *avc == "" || *aac == "" {
flag.Usage()
os.Exit(1)
}
return *flv, *aac, *avc
}

@ -4,6 +4,7 @@ import (
"flag"
"github.com/q191201771/lal/pkg/httpflv"
"github.com/q191201771/lal/pkg/rtmp"
"github.com/q191201771/lal/pkg/util/errors"
"github.com/q191201771/lal/pkg/util/log"
"os"
"time"
@ -19,17 +20,17 @@ func main() {
var ffr httpflv.FlvFileReader
err := ffr.Open(flvFileName)
panicIfErr(err)
errors.PanicIfErrorOccur(err)
defer ffr.Dispose()
log.Infof("open succ.")
flvHeader, err := ffr.ReadFlvHeader()
panicIfErr(err)
errors.PanicIfErrorOccur(err)
log.Infof("read flv header succ. %v", flvHeader)
ps := rtmp.NewPushSession(5000)
err = ps.Push(rtmpPushURL)
panicIfErr(err)
errors.PanicIfErrorOccur(err)
log.Infof("push succ.")
var prevTS uint32
@ -41,7 +42,7 @@ func main() {
//for i := 0; i < 1000*1000; i++ {
for {
tag, err := ffr.ReadTag()
panicIfErr(err)
errors.PanicIfErrorOccur(err)
//log.Infof("tag: %+v %v", tag.Header, tag.Raw[11:])
//log.Infof("tag: %+v %d", tag.Header, len(tag.Raw))
@ -87,7 +88,7 @@ func main() {
// 第一个包直接发送
if prevTS == 0 {
err = ps.TmpWrite(chunks)
panicIfErr(err)
errors.PanicIfErrorOccur(err)
prevTS = tag.Header.Timestamp
continue
}
@ -95,7 +96,7 @@ func main() {
// 相等或回退了直接发送
if tag.Header.Timestamp <= prevTS {
err = ps.TmpWrite(chunks)
panicIfErr(err)
errors.PanicIfErrorOccur(err)
prevTS = tag.Header.Timestamp
continue
}
@ -106,7 +107,7 @@ func main() {
// 跳跃超过了30秒直接发送
if diff > 30000 {
err = ps.TmpWrite(chunks)
panicIfErr(err)
errors.PanicIfErrorOccur(err)
prevTS = tag.Header.Timestamp
continue
}
@ -114,7 +115,7 @@ func main() {
// 睡眠后发送,睡眠时长为时间戳间隔
time.Sleep(time.Duration(diff) * time.Millisecond)
err = ps.TmpWrite(chunks)
panicIfErr(err)
errors.PanicIfErrorOccur(err)
prevTS = tag.Header.Timestamp
continue
}
@ -123,12 +124,6 @@ func main() {
}
}
func panicIfErr(err error) {
if err != nil {
panic(err)
}
}
func parseFlag() (string, string) {
i := flag.String("i", "", "specify flv file")
o := flag.String("o", "", "specify rtmp push url")

@ -3,6 +3,7 @@ package main
import (
"flag"
"github.com/q191201771/lal/pkg/httpflv"
"github.com/q191201771/lal/pkg/util/errors"
"github.com/q191201771/lal/pkg/util/log"
"io"
"os"
@ -37,21 +38,21 @@ func main() {
var ffr httpflv.FlvFileReader
err = ffr.Open(inFileName)
panicIfErr(err)
errors.PanicIfErrorOccur(err)
defer ffr.Dispose()
log.Infof("open input flv file succ.")
var ffw httpflv.FlvFileWriter
err = ffw.Open(outFileName)
panicIfErr(err)
errors.PanicIfErrorOccur(err)
defer ffw.Dispose()
log.Infof("open output flv file succ.")
flvHeader, err := ffr.ReadFlvHeader()
panicIfErr(err)
errors.PanicIfErrorOccur(err)
err = ffw.WriteRaw(flvHeader)
panicIfErr(err)
errors.PanicIfErrorOccur(err)
//for i:=0; i < 10; i++{
for {
@ -60,19 +61,13 @@ func main() {
log.Infof("EOF.")
break
}
panicIfErr(err)
errors.PanicIfErrorOccur(err)
//log.Infof("> hook. %+v", tag)
hookTag(tag)
//log.Infof("< hook. %+v", tag)
err = ffw.WriteRaw(tag.Raw)
panicIfErr(err)
}
}
func panicIfErr(err error) {
if err != nil {
panic(err)
errors.PanicIfErrorOccur(err)
}
}

@ -0,0 +1 @@
package aac

@ -1,45 +1,62 @@
package avc
import (
"errors"
"github.com/q191201771/lal/pkg/util/bele"
)
var avcErr = errors.New("avc: fxxk")
var NaluStartCode = []byte{0x0, 0x0, 0x0, 0x1}
var NaluUintTypeMapping = map[uint8]string{
1:"SLICE",
5:"IDR",
6:"SEI",
7:"SPS",
8:"PPS",
9:"AUD",
}
// H.264-AVC-ISO_IEC_14496-15.pdf
// 5.2.4 Decoder configuration information
// <buf> body of tag
//func parseAVCSeqHeader(buf []byte) (sps, pps []byte, err error) {
// // TODO chef: check if read out of <buf> range
//
// if buf[0] != AVCKey || buf[1] != isAVCKeySeqHeader || buf[2] != 0 || buf[3] != 0 || buf[4] != 0 {
// log.Error("parse avc seq header failed.")
// err = httpFlvErr
// return
// }
//
// //configurationVersion := buf[5]
// //avcProfileIndication := buf[6]
// //profileCompatibility := buf[7]
// //avcLevelIndication := buf[8]
// //lengthSizeMinusOne := buf[9] & 0x03
//
// index := 10
//
// numOfSPS := int(buf[index] & 0x1F)
// index++
// // TODO chef: if the situation of multi sps exist?
// // only take the last one.
// for i := 0; i < numOfSPS; i++ {
// lenOfSPS := int(bele.BEUint16(buf[index:]))
// index += 2
// sps = append(sps, buf[index:index+lenOfSPS]...)
// index += lenOfSPS
// }
//
// numOfPPS := int(buf[index] & 0x1F)
// index++
// for i := 0; i < numOfPPS; i++ {
// lenOfPPS := int(bele.BEUint16(buf[index:]))
// index += 2
// pps = append(pps, buf[index:index+lenOfPPS]...)
// index += lenOfPPS
// }
//
// return
//}
func ParseAVCSeqHeader(buf []byte) (sps, pps []byte, err error) {
// TODO chef: check if read out of <buf> range
if buf[0] != 0x17 || buf[1] != 0x00 || buf[2] != 0 || buf[3] != 0 || buf[4] != 0 {
err = avcErr
return
}
//configurationVersion := buf[5]
//avcProfileIndication := buf[6]
//profileCompatibility := buf[7]
//avcLevelIndication := buf[8]
//lengthSizeMinusOne := buf[9] & 0x03
index := 10
numOfSPS := int(buf[index] & 0x1F)
index++
// TODO chef: if the situation of multi sps exist?
// only take the last one.
for i := 0; i < numOfSPS; i++ {
lenOfSPS := int(bele.BEUint16(buf[index:]))
index += 2
sps = append(sps, buf[index:index+lenOfSPS]...)
index += lenOfSPS
}
numOfPPS := int(buf[index] & 0x1F)
index++
for i := 0; i < numOfPPS; i++ {
lenOfPPS := int(bele.BEUint16(buf[index:]))
index += 2
pps = append(pps, buf[index:index+lenOfPPS]...)
index += lenOfPPS
}
return
}

@ -45,14 +45,14 @@ const (
type TagHeader struct {
T uint8 // type
DataSize uint32
DataSize uint32 // body大小不包含header和prev tag size字段
Timestamp uint32
StreamID uint32 // always 0
}
type Tag struct {
Header TagHeader
Raw []byte
Raw []byte // 结构为 (11字节的 tag header) + (body) + (4字节的 prev tag size)
}
func (tag *Tag) IsMetadata() bool {
@ -71,6 +71,10 @@ func (tag *Tag) IsAACSeqHeader() bool {
return tag.Header.T == TagTypeAudio && tag.Raw[TagHeaderSize]>>4 == SoundFormatAAC && tag.Raw[TagHeaderSize+1] == AACPacketTypeSeqHeader
}
func (tag *Tag) Payload() []byte {
return tag.Raw[11:len(tag.Raw)-4]
}
func IsMetadata(tag []byte) bool {
return tag[0] == TagTypeMetadata
}
@ -101,6 +105,13 @@ func PackHTTPFlvTag(t uint8, timestamp int, in []byte) []byte {
return out
}
func ModTagTimestamp(tag *Tag, timestamp uint32) {
tag.Header.Timestamp = timestamp
bele.BEPutUint24(tag.Raw[4:], timestamp&0xffffff)
tag.Raw[7] = byte(timestamp >> 24)
}
func readTagHeader(rd io.Reader) (h TagHeader, rawHeader []byte, err error) {
rawHeader = make([]byte, TagHeaderSize)
if _, err = io.ReadAtLeast(rd, rawHeader, TagHeaderSize); err != nil {
@ -113,13 +124,6 @@ func readTagHeader(rd io.Reader) (h TagHeader, rawHeader []byte, err error) {
return
}
func ModTagTimestamp(tag *Tag, timestamp uint32) {
tag.Header.Timestamp = timestamp
bele.BEPutUint24(tag.Raw[4:], timestamp&0xffffff)
tag.Raw[7] = byte(timestamp >> 24)
}
func (tag *Tag) cloneTag() *Tag {
res := &Tag{}
res.Header = tag.Header

@ -0,0 +1,7 @@
package errors
func PanicIfErrorOccur(err error) {
if err != nil {
panic(err)
}
}
Loading…
Cancel
Save