Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

关于动态加载mp4视频推流到rtmp,切换卡顿的问题 #120

Open
cowkeys opened this issue Jan 29, 2024 · 13 comments
Open

关于动态加载mp4视频推流到rtmp,切换卡顿的问题 #120

cowkeys opened this issue Jan 29, 2024 · 13 comments

Comments

@cowkeys
Copy link

cowkeys commented Jan 29, 2024

我看到您 最近提交的示例代码1b855b02fa0d0b53d2ca9d9a1382ce08914905bd,
我本人在做一个动态生成mp4视频,然后按顺序推流,相当于一边生成一边推流,我看我可以直接改一下接收的代码就能实现,但是感觉上这个做不到无缝?

@cowkeys
Copy link
Author

cowkeys commented Jan 29, 2024

还有 示例中的,time.Sleep(20 * time.Millisecond) 是否和帧率有关?

@cowkeys
Copy link
Author

cowkeys commented Jan 29, 2024

经过测试,我发现,1.mp4 切换 2.mp4 一直到5.mp4 过程中并没有卡顿。 目前遇到的问题是,当推流到2.mp4之后,会出现音画不同步的现象。这个如何处理呢

@yapingcat
Copy link
Owner

还有 示例中的,time.Sleep(20 * time.Millisecond) 是否和帧率有关?

如果可以,按照帧率推送

@yapingcat
Copy link
Owner

经过测试,我发现,1.mp4 切换 2.mp4 一直到5.mp4 过程中并没有卡顿。 目前遇到的问题是,当推流到2.mp4之后,会出现音画不同步的现象。这个如何处理呢

mp4文件提供一下?

@cowkeys
Copy link
Author

cowkeys commented Jan 29, 2024

可以 ,我先贴上代码,我一直在想是不是adjust方法的问题,但是不太了解这个纠正机制。

package main

import (
	"fmt"
	"io"
	"net"
	"os"
	"time"

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

type TimestampAdjust struct {
	lastTimeStamp    int64
	adjust_timestamp int64
}

func newTimestampAdjust() *TimestampAdjust {
	return &TimestampAdjust{
		lastTimeStamp:    -1,
		adjust_timestamp: 0,
	}
}

// timestamp in millisecond
func (adjust *TimestampAdjust) adjust(timestamp int64) int64 {
	if adjust.lastTimeStamp == -1 {
		adjust.adjust_timestamp = timestamp
		adjust.lastTimeStamp = timestamp
		return adjust.adjust_timestamp
	}

	delta := timestamp - adjust.lastTimeStamp
	if delta < -1000 || delta > 1000 {
		adjust.adjust_timestamp = adjust.adjust_timestamp + 1
	} else {
		adjust.adjust_timestamp = adjust.adjust_timestamp + delta
	}
	adjust.lastTimeStamp = timestamp
	return adjust.adjust_timestamp
}

var video_pts_adjust *TimestampAdjust = newTimestampAdjust()
var video_dts_adjust *TimestampAdjust = newTimestampAdjust()
var audio_ts_adjust *TimestampAdjust = newTimestampAdjust()

// Will push the last file under mp4sPath to the specified rtmp server
func main() {
	var (
		mp4Path = "/Users/nicole/work/go/src/song/"    //like ./mp4/
		rtmpUrl = "rtmp://127.0.0.1:1935/live/test110" //like rtmp://127.0.0.1:1935/live/test110
	)

	c, err := net.Dial("tcp4", "127.0.0.1:1935") // like 127.0.0.1:1935
	if err != nil {
		fmt.Println("ininin", err)
	}
	cli := rtmp.NewRtmpClient(rtmp.WithComplexHandshake(),
		rtmp.WithComplexHandshakeSchema(rtmp.HANDSHAKE_COMPLEX_SCHEMA0),
		rtmp.WithEnablePublish())
	cli.OnError(func(code, describe string) {
		fmt.Printf("rtmp code:%s ,describe:%s\n", code, describe)
	})
	isReady := make(chan struct{})
	cli.OnStatus(func(code, level, describe string) {
		fmt.Printf("rtmp onstatus code:%s ,level %s ,describe:%s\n", code, level, describe)
	})
	cli.OnStateChange(func(newState rtmp.RtmpState) {
		if newState == rtmp.STATE_RTMP_PUBLISH_START {
			fmt.Println("ready for publish")
			close(isReady)
		}
	})
	cli.SetOutput(func(bytes []byte) error {
		_, err := c.Write(bytes)
		return err
	})
	go func() {
		<-isReady
		fmt.Println("start to read file")
		next := 1
		for {

			filees, err := os.ReadDir(mp4Path)
			if err != nil {
				fmt.Println("===============end")
				fmt.Println(err)
				return
			}

			for k, _ := range filees {
				nextName := fmt.Sprintf("s%v.mp4", next)
				if filees[k].Name() == nextName {
					fmt.Printf("----------found !!!! %v\n", nextName)
					PushRtmp(mp4Path+filees[k].Name(), cli)
					next++
				} else {
					// 直接循环推送。
					next = 1
				}

			}
		}
	}()

	cli.Start(rtmpUrl)
	buf := make([]byte, 4096)
	n := 0
	for err == nil {
		n, err = c.Read(buf)
		if err != nil {
			continue
		}
		fmt.Println("read byte", n)
		cli.Input(buf[:n])
	}
	fmt.Println(err)
}

func PushRtmp(fileName string, cli *rtmp.RtmpClient) {
	mp4File, err := os.Open(fileName)
	if err != nil {
		fmt.Printf("nilerr:%v\n", err)
		return
	}
	defer mp4File.Close()
	demuxer := mp4.CreateMp4Demuxer(mp4File)
	if infos, err := demuxer.ReadHead(); err != nil && err != io.EOF {
		fmt.Printf("err:%v\n", err)
	} else {
		fmt.Printf("infos: %+v\n", infos)
	}

	mp4info := demuxer.GetMp4Info()
	fmt.Printf("%+v\n", mp4info)

	// var video_pts_adjust *TimestampAdjust = newTimestampAdjust()
	// var video_dts_adjust *TimestampAdjust = newTimestampAdjust()
	// var audio_ts_adjust *TimestampAdjust = newTimestampAdjust()

	for {
		pkg, err := demuxer.ReadPacket()

		if err != nil {
			fmt.Println("demuxerr:%v\n", err)
			break
		}
		//fmt.Println("end:%v\n", err)
		if pkg.Cid == mp4.MP4_CODEC_H264 {
			time.Sleep(20 * time.Millisecond)
			pts := video_pts_adjust.adjust(int64(pkg.Pts))
			dts := video_dts_adjust.adjust(int64(pkg.Dts))
			cli.WriteVideo(codec.CODECID_VIDEO_H264, pkg.Data, uint32(pts), uint32(dts))

		} else if pkg.Cid == mp4.MP4_CODEC_AAC {
			pts := audio_ts_adjust.adjust(int64(pkg.Pts))
			cli.WriteAudio(codec.CODECID_AUDIO_AAC, pkg.Data, uint32(pts), uint32(pts))
		} else if pkg.Cid == mp4.MP4_CODEC_MP3 {
			pts := audio_ts_adjust.adjust(int64(pkg.Pts))
			cli.WriteAudio(codec.CODECID_AUDIO_MP3, pkg.Data, uint32(pts), uint32(pts))
		}

	}
}

@cowkeys
Copy link
Author

cowkeys commented Jan 29, 2024

经过测试,我发现,1.mp4 切换 2.mp4 一直到5.mp4 过程中并没有卡顿。 目前遇到的问题是,当推流到2.mp4之后,会出现音画不同步的现象。这个如何处理呢

mp4文件提供一下?

如以上我的代码和视频文件,关于示例代码我只改了 读取files那一部分,把文件下到本地的song目录,可以看一下,循环推流后,观看直播过了第一个之后就开始陆续就不同步了。

@yapingcat
Copy link
Owner

yapingcat commented Jan 29, 2024

视频可以用 https://cowkeys-file.oss-cn-beijing.aliyuncs.com/video/s1.mp4 https://cowkeys-file.oss-cn-beijing.aliyuncs.com/video/s2.mp4 https://cowkeys-file.oss-cn-beijing.aliyuncs.com/video/s3.mp4 https://cowkeys-file.oss-cn-beijing.aliyuncs.com/video/s4.mp4

这些视频文件,video和audio的duration 并不相等,有几百毫秒的差距,而且存在B 帧
很难简单地通过时间戳计算处理来应对这种情况。

@cowkeys
Copy link
Author

cowkeys commented Jan 29, 2024

视频可以用 https://cowkeys-file.oss-cn-beijing.aliyuncs.com/video/s1.mp4 https://cowkeys-file.oss-cn-beijing.aliyuncs.com/video/s2.mp4 https://cowkeys-file.oss-cn-beijing.aliyuncs.com/video/s3.mp4 https://cowkeys-file.oss-cn-beijing.aliyuncs.com/video/s4.mp4

这些视频文件,video和audio的duration 并不相等,有几百毫秒的差距,而且存在B 帧 很难简单地通过时间戳计算处理来应对这种情况。

谢谢,这几个视频是我从一个视频用ffmpeg直接切10秒做的一个demo,正常来讲,如果每一个视频 的video和audio ,duration相等的话,用这段代码是没有问题的吗

@yapingcat
Copy link
Owner

yapingcat commented Jan 29, 2024

视频可以用 https://cowkeys-file.oss-cn-beijing.aliyuncs.com/video/s1.mp4 https://cowkeys-file.oss-cn-beijing.aliyuncs.com/video/s2.mp4 https://cowkeys-file.oss-cn-beijing.aliyuncs.com/video/s3.mp4 https://cowkeys-file.oss-cn-beijing.aliyuncs.com/video/s4.mp4

这些视频文件,video和audio的duration 并不相等,有几百毫秒的差距,而且存在B 帧 很难简单地通过时间戳计算处理来应对这种情况。

谢谢,这几个视频是我从一个视频用ffmpeg直接切10秒做的一个demo,正常来讲,如果每一个视频 的video和audio ,duration相等的话,用这段代码是没有问题的吗

切片的时候,需要重新编码,不要编码B帧

@cowkeys
Copy link
Author

cowkeys commented Jan 30, 2024

视频可以用 https://cowkeys-file.oss-cn-beijing.aliyuncs.com/video/s1.mp4 https://cowkeys-file.oss-cn-beijing.aliyuncs.com/video/s2.mp4 https://cowkeys-file.oss-cn-beijing.aliyuncs.com/video/s3.mp4 https://cowkeys-file.oss-cn-beijing.aliyuncs.com/video/s4.mp4

这些视频文件,video和audio的duration 并不相等,有几百毫秒的差距,而且存在B 帧 很难简单地通过时间戳计算处理来应对这种情况。

谢谢,这几个视频是我从一个视频用ffmpeg直接切10秒做的一个demo,正常来讲,如果每一个视频 的video和audio ,duration相等的话,用这段代码是没有问题的吗

切片的时候,需要重新编码,不要编码B帧

好的。 我大概着了一些资料 ,用 重新编码视频 不要b帧可以使用这个命令吧? 我先试试
ffmpeg -i /home/song/s2.mp4 -c:v libx264 -bf 0 -c:a aac -strict experimental -f mp4 /home/song/news2.mp4

@cowkeys
Copy link
Author

cowkeys commented Jan 30, 2024

视频可以用 https://cowkeys-file.oss-cn-beijing.aliyuncs.com/video/s1.mp4 https://cowkeys-file.oss-cn-beijing.aliyuncs.com/video/s2.mp4 https://cowkeys-file.oss-cn-beijing.aliyuncs.com/video/s3.mp4 https://cowkeys-file.oss-cn-beijing.aliyuncs.com/video/s4.mp4

这些视频文件,video和audio的duration 并不相等,有几百毫秒的差距,而且存在B 帧 很难简单地通过时间戳计算处理来应对这种情况。

谢谢,这几个视频是我从一个视频用ffmpeg直接切10秒做的一个demo,正常来讲,如果每一个视频 的video和audio ,duration相等的话,用这段代码是没有问题的吗

切片的时候,需要重新编码,不要编码B帧

请问这个库里面有推流清晰度的选项吗

@yapingcat
Copy link
Owner

视频可以用 https://cowkeys-file.oss-cn-beijing.aliyuncs.com/video/s1.mp4 https://cowkeys-file.oss-cn-beijing.aliyuncs.com/video/s2.mp4 https://cowkeys-file.oss-cn-beijing.aliyuncs.com/video/s3.mp4 https://cowkeys-file.oss-cn-beijing.aliyuncs.com/video/s4.mp4

这些视频文件,video和audio的duration 并不相等,有几百毫秒的差距,而且存在B 帧 很难简单地通过时间戳计算处理来应对这种情况。

谢谢,这几个视频是我从一个视频用ffmpeg直接切10秒做的一个demo,正常来讲,如果每一个视频 的video和audio ,duration相等的话,用这段代码是没有问题的吗

切片的时候,需要重新编码,不要编码B帧

请问这个库里面有推流清晰度的选项吗

没有,这个库只是做文件容器格式的转换,
多种清晰度一般需要ffmpeg支持,使用ffmpeg编码出多种清晰度的流

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants