gomedia icon indicating copy to clipboard operation
gomedia copied to clipboard

Flv To mp4

Open hasnhasan opened this issue 2 years ago • 7 comments

Hello, The example in the file "example_covert_flv_to_mp4.go" creates and saves a new mp4 file.

I want to save it as a continuation of an existing mp4 file. Is this possible?

hasnhasan avatar Aug 30 '23 09:08 hasnhasan

you should use fragment mp4.

please refer to this example flv to fmp4

yapingcat avatar Aug 30 '23 16:08 yapingcat

I tried this way but without success.

I'm just trying to do this. If the test2_fmp4.mp4 file already exists (For example, there is a 1 minute video recording.) it should append to it and continue recording.


import (
	"encoding/binary"
	"fmt"
	"net"
	"os"
	"time"

	"github.com/notedit/rtmp/av"
	"github.com/notedit/rtmp/format/rtmp"
	"github.com/yapingcat/gomedia/go-codec"
	"github.com/yapingcat/gomedia/go-mp4"
)

func startRtmp() {

	lis, err := net.Listen("tcp", ":1935")
	if err != nil {
		panic(err)
	}

	s := rtmp.NewServer()

	s.LogEvent = func(c *rtmp.Conn, nc net.Conn, e int) {
		es := rtmp.EventString[e]
		fmt.Println(nc.LocalAddr(), nc.RemoteAddr(), es)
	}

	s.HandleConn = func(c *rtmp.Conn, nc net.Conn) {

		fmt.Println(c.URL.Path)
		if c.Publishing {
			mp4filename := "test2_fmp4.mp4"
			mp4file, err := os.OpenFile(mp4filename, os.O_CREATE|os.O_RDWR, 0666)
			if err != nil {
				fmt.Println(err)
				return
			}
			defer mp4file.Close()

			i := 0
			muxer, err := mp4.CreateMp4Muxer(mp4file, mp4.WithMp4Flag(mp4.MP4_FLAG_DASH))
			muxer.OnNewFragment(func(duration uint32, firstPts, firstDts uint64) {
				fmt.Println("on segment", duration)
				if i == 0 {
					initFile, _ := os.OpenFile("init.mp4", os.O_CREATE|os.O_RDWR, 0666)
					muxer.WriteInitSegment(initFile)
					initFile.Close()
				}
				i++
				muxer.ReBindWriter(mp4file)
			})

			if err != nil {
				fmt.Println(err)
				return
			}
			hasVideo := false
			hasAudio := false
			var vtid uint32
			var atid uint32
			defer muxer.WriteTrailer()
			var asc []byte
			for {
				nc.SetReadDeadline(time.Now().Add(time.Second))
				pkt, err := c.ReadPacket()
				if err != nil {
					fmt.Println(err)
					return
				}
				pts := pkt.CTime.Milliseconds() + pkt.Time.Milliseconds()
				dts := pkt.Time.Milliseconds()
				switch pkt.Type {
				case av.H264DecoderConfig:
					if !hasVideo {
						vtid = muxer.AddVideoTrack(mp4.MP4_CODEC_H264)
						hasVideo = true
					}
					tmpspss, tmpppss := codec.CovertExtradata(pkt.Data)
					//fmt.Println(len(tmpspss), len(tmpppss))
					pts := pkt.CTime + pkt.Time
					frame := make([]byte, 0, len(tmpspss[0])+len(tmpppss[0]))
					frame = append(frame, tmpspss[0]...)
					frame = append(frame, tmpppss[0]...)
					//codec.ShowPacketHexdump(tmpspss[0])
					//fmt.Println(codec.GetSPSIdWithStartCode(tmpspss[0]))
					muxer.Write(vtid, frame, uint64(pts), uint64(dts))
				case av.H264:
					for len(pkt.Data) > 0 {
						naluSize := binary.BigEndian.Uint32(pkt.Data[:4])
						//codec.ShowPacketHexdump(pkt.Data[:6])
						codec.CovertAVCCToAnnexB(pkt.Data)
						muxer.Write(vtid, pkt.Data[:4+naluSize], uint64(pts), uint64(dts))
						pkt.Data = pkt.Data[4+naluSize:]
					}
					//fmt.Println("h264 frame pts ", pts, "dts ", dts)
				case av.AACDecoderConfig:
					if !hasAudio {
						atid = muxer.AddAudioTrack(mp4.MP4_CODEC_AAC)
						hasAudio = true
					}

					asc = make([]byte, len(pkt.Data))
					copy(asc, pkt.Data)
				case av.AAC:
					adts, err := codec.ConvertASCToADTS(asc, len(pkt.Data)+7)
					if err != nil {
						return
					}
					//fmt.Println("aac frame pts ", pts, "dts ", dts)
					adts_frame := append(adts.Encode(), pkt.Data...)
					muxer.Write(atid, adts_frame, uint64(pts), uint64(dts))
				}
			}

		}
	}

	for {
		nc, err := lis.Accept()
		if err != nil {
			time.Sleep(time.Second)
			continue
		}
		go s.HandleNetConn(nc)
	}
}

func main() {

	startRtmp()
}

hasnhasan avatar Aug 30 '23 23:08 hasnhasan

https://github.com/yapingcat/gomedia/issues/57#issuecomment-1471481250

please according to this issue.

  1. create fmp4 muxer with memeory io handler,fmp4 binary data will be stored into memory buffer
  2. save data into fmp4 file
  3. reset memeory io handler

yapingcat avatar Aug 31 '23 05:08 yapingcat

The problem here is that I do not record the incoming broadcast continuously. I record it piece by piece. Sometimes during recording, the broadcast from RTMP is interrupted and comes again. When it comes, I try to make it continue from the file recorded until that section.

hasnhasan avatar Aug 31 '23 18:08 hasnhasan

The problem here is that I do not record the incoming broadcast continuously. I record it piece by piece. Sometimes during recording, the broadcast from RTMP is interrupted and comes again. When it comes, I try to make it continue from the file recorded until that section.

fmp4 satisfy your Scenario . you can append media data to the end of the fmp4 file,just like flv file

yapingcat avatar Sep 01 '23 05:09 yapingcat

I have the same "request". Currently when making very long recordings, as shown by @hasnhasan above, it will load all bytes into memory and then write at once. This might create OOM for very long recordings. I have seen implementations dumping the content directly in a file, and creating the appropriate headers at start, and trailer at end.

Not sure if fragmented mp4 is a good solution, as these will create an init and individual segment files, where as for some usecases you would like to have one large mp4 containing all data. Not sure if this is possible with the current implementation.

cedricve avatar Dec 05 '23 19:12 cedricve

Nevermind I just fixed my "usecase" by passing in a file writer, so nothing in memory is stored, but dumped directly to filesystem. @hasnhasan I believe this would fix your issue as well.

cedricve avatar Dec 05 '23 20:12 cedricve