在 Linux 中用 FFMPEG 覆盖 TS 流文件
Overwriting TS Stream File with FFMPEG in Linux
我正在尝试将 rtmp 流转换为 m3u8 流。为了达到这个目的,我使用了 FFMPEG。现在,转换和下载没有问题。但是,它会写入很多 .ts 文件,例如 channel0000.ts、channel0001.ts、channel0002.ts。每 10 秒创建 1 个 ts 文件。在这一点上,我想要一个 ts 文件。换句话说,我需要覆盖,因为我不想只存储最后 10 秒所需的所有流。当我尝试写入同一个文件时,出现此错误:
Invalid segment filename template 'channel.ts'
Could not write header for output file #0 (incorrect codec parameters ?): Invalid argumentStream mapping:
Stream #0:0 -> #0:0 (copy)
Stream #0:1 -> #0:1 (copy)
Last message repeated 1 times
这是我的 FFMPEG 命令。
ffmpeg -loglevel quiet -i rtmp://example -c:v libx264 -profile:v baseline -level 3.1 -c:a aac -strict experimental -f mpegts - | ffmpeg -i - -c copy -map 0 -f segment -segment_list channel.m3u8 -segment_format mpegts -segment_time 10 channel%04d.ts
有什么建议吗?
在 FFMPEG 文档中,我找到了“segment_wrap”选项。添加此选项时,文件将循环写入。在我的例子中,我添加了“-segment_wrap 1”命令部分,它现在只写一个文件。
我遇到了类似的问题,我的解决方案是使用 -hls_segment_filename pipe:1
选项(因此我只是输出 .ts 文件而不是 .m3u8 文件).
在我的管道上,我写了一段代码来检测 .ts 文件的 header,它看起来像字节 [71, 64, 17, 1X, 0, 66, 240, 34, 0, 1, 193, 0, 0, 255, 1, 255, 0, 1, 252, 128, 17, 72](我推荐 GHex 来检查你的.ts 文件,如果你想要更多的 header 来区分)。然后我做了一段代码,沿着这条线切割(警告,这里的第四个字节可以改变它的值,但它是唯一的),并重新组合文件。
我认为您的应用程序唯一需要做的就是为文件内容使用 queue。如果你想要十秒,而你的文件大约是 1 秒,你可以使用长度为 10.queue 的 queue。
我的代码是在 go 中(因为我的整个项目都是),所以这里有一些 go 中的代码可能会有所帮助:
type tsFile struct {
contents []byte
finished bool
child *tsFile
}
func in(a int, array int[]) bool{
for _, b := range list {
if b == a {
return true
}
}
return false
}
func CutAdd(curFile *tsFile, pipedOut []bytes){
header := [...]bytes{} // insert here the header you want
unNeededIndex := [...]int{3} // insert here the bytes indexes that change between files
cur_header_pointer := 0
header_cache := make([]byte, 0, len(header))
for i, b := range(pipedOut){
if header[cur_header_pointer] == b || in(cur_header_pointer, unNeededIndex){
header_cache = append(header_cache, b)
cur_header_pointer ++
if cur_header_pointer == len(header){
curFile.finished = true
curFile.child = &tsFile{contents : header_cache}
CutAdd(curFile.child, pipedOut[i:])
return
}
} else {
if cur_header_pointer != 0 {
for _, cached_b := range(header_cache){
curFile.contents = append(curFile.contents, cached_b) // we store what we thought was a header
}
cur_header_pointer = 0
}
curFile.contents = append(curFile.contents, b) // we store the byte
}
}
}
有点简陋,但对我有用(也可能有错误,我当时没有放实际代码,但你应该大致了解我的意思)
我正在尝试将 rtmp 流转换为 m3u8 流。为了达到这个目的,我使用了 FFMPEG。现在,转换和下载没有问题。但是,它会写入很多 .ts 文件,例如 channel0000.ts、channel0001.ts、channel0002.ts。每 10 秒创建 1 个 ts 文件。在这一点上,我想要一个 ts 文件。换句话说,我需要覆盖,因为我不想只存储最后 10 秒所需的所有流。当我尝试写入同一个文件时,出现此错误:
Invalid segment filename template 'channel.ts'
Could not write header for output file #0 (incorrect codec parameters ?): Invalid argumentStream mapping:
Stream #0:0 -> #0:0 (copy)
Stream #0:1 -> #0:1 (copy)
Last message repeated 1 times
这是我的 FFMPEG 命令。
ffmpeg -loglevel quiet -i rtmp://example -c:v libx264 -profile:v baseline -level 3.1 -c:a aac -strict experimental -f mpegts - | ffmpeg -i - -c copy -map 0 -f segment -segment_list channel.m3u8 -segment_format mpegts -segment_time 10 channel%04d.ts
有什么建议吗?
在 FFMPEG 文档中,我找到了“segment_wrap”选项。添加此选项时,文件将循环写入。在我的例子中,我添加了“-segment_wrap 1”命令部分,它现在只写一个文件。
我遇到了类似的问题,我的解决方案是使用 -hls_segment_filename pipe:1
选项(因此我只是输出 .ts 文件而不是 .m3u8 文件).
在我的管道上,我写了一段代码来检测 .ts 文件的 header,它看起来像字节 [71, 64, 17, 1X, 0, 66, 240, 34, 0, 1, 193, 0, 0, 255, 1, 255, 0, 1, 252, 128, 17, 72](我推荐 GHex 来检查你的.ts 文件,如果你想要更多的 header 来区分)。然后我做了一段代码,沿着这条线切割(警告,这里的第四个字节可以改变它的值,但它是唯一的),并重新组合文件。
我认为您的应用程序唯一需要做的就是为文件内容使用 queue。如果你想要十秒,而你的文件大约是 1 秒,你可以使用长度为 10.queue 的 queue。
我的代码是在 go 中(因为我的整个项目都是),所以这里有一些 go 中的代码可能会有所帮助:
type tsFile struct {
contents []byte
finished bool
child *tsFile
}
func in(a int, array int[]) bool{
for _, b := range list {
if b == a {
return true
}
}
return false
}
func CutAdd(curFile *tsFile, pipedOut []bytes){
header := [...]bytes{} // insert here the header you want
unNeededIndex := [...]int{3} // insert here the bytes indexes that change between files
cur_header_pointer := 0
header_cache := make([]byte, 0, len(header))
for i, b := range(pipedOut){
if header[cur_header_pointer] == b || in(cur_header_pointer, unNeededIndex){
header_cache = append(header_cache, b)
cur_header_pointer ++
if cur_header_pointer == len(header){
curFile.finished = true
curFile.child = &tsFile{contents : header_cache}
CutAdd(curFile.child, pipedOut[i:])
return
}
} else {
if cur_header_pointer != 0 {
for _, cached_b := range(header_cache){
curFile.contents = append(curFile.contents, cached_b) // we store what we thought was a header
}
cur_header_pointer = 0
}
curFile.contents = append(curFile.contents, b) // we store the byte
}
}
}
有点简陋,但对我有用(也可能有错误,我当时没有放实际代码,但你应该大致了解我的意思)