FFmpeg转码重置了文件的开始时间

11

我使用一个片段器将我的 MPEG 2 Ts 文件分割成一系列媒体片段以进行 HTTP 直播流

每个片段的开始时间都紧跟在前一个之后(例如:片段的开始时间为 00:00、00:10、00:20、00:30...)

(在 Ubuntu 中)

问题是:

当我使用 ffmpeg 转码其中一个媒体片段(例如从 800k bps 转换为 200k bps)时,

转码后的媒体片段的开始时间会被重置为 0

例如:当我转码第三个片段时,

片段的开始时间将更改为:00:00、00:10、00:00、00:30...

导致我的播放器冻结,一旦播放转码后的媒体片段

有没有解决方案可以使转码后的媒体文件具有相同的开始时间?

我猜测是 ffmpeg 重置了片段的 PTS(演示时间戳),

但我不知道该如何解决...

以下是我的 ffmpeg 命令(转码为 250k bps)

============================

ffmpeg -y -i sample-03.ts -f mpegts -acodec libfaac -ar 48000 -ab 64k -vcodec libx264 -b 250k -flags +loop -cmp +chroma \
 -partitions +parti4x4+partp8x8+partb8x8 -subq 7 -trellis 0 -refs 0 -coder 0 -me_range 16 -keyint_min 25 \
 -sc_threshold 40 -i_qfactor 0.71 -maxrate 250k -bufsize 250k -rc_eq 'blurCplx^(1-qComp)' -qcomp 0.6 \
 -qmin 10 -qmax 51 -qdiff 4 -level 30 -aspect 320:240 -g 30 -async 2 sample.ts

============================

帮帮我!

谢谢!

5个回答

3

直接将H264编码的段进行数据包时间转移

我最终使用了FFmpeg libavformat/avcodec库来读取并直接转移数据包时间头。偏移时间以秒为单位指定。

unsigned int tsShift = offsetTime * 90000; // h264 defined sample rate is 90khz

并且在下面更进一步

do {
    double segmentTime;
    AVPacket packet;

    decodeDone = av_read_frame(pInFormatCtx, &packet);
    if (decodeDone < 0) {
        break;
    }

    if (av_dup_packet(&packet) < 0) {
        cout << "Could not duplicate packet" << endl;
        av_free_packet(&packet);
        break;
    }

    if (packet.stream_index == videoIndex && (packet.flags & AV_PKT_FLAG_KEY)) {
        segmentTime = (double)pVideoStream->pts.val * pVideoStream->time_base.num / pVideoStream->time_base.den;
    }
    else if (videoIndex < 0) {
        segmentTime = (double)pAudioStream->pts.val * pAudioStream->time_base.num / pAudioStream->time_base.den;
    }
    else {
        segmentTime = prevSegmentTime;
    }

    // cout << "before packet pts dts " << packet.pts << " " << packet.dts;
    packet.pts += tsShift;
    packet.dts += tsShift;
    // cout << " after packet pts dts " << packet.pts << " " << packet.dts << endl;


    ret = av_interleaved_write_frame(pOutFormatCtx, &packet);
    if (ret < 0) {
        cout << "Warning: Could not write frame of stream" << endl;
    }
    else if (ret > 0) {
        cout <<  "End of stream requested" << endl;
        av_free_packet(&packet);
        break;
    }

    av_free_packet(&packet);

} while (!decodeDone);

MPEGTS Shifter源码


迂回地调整视频流

但是时间差不完全符合我指定的要求

下面是具体步骤:

  1. 首先将原始TS文件转换为原始格式

    ffmpeg -i original.ts original.avi

  2. 应用setpts过滤器并转换为编码格式 (这会根据帧速率和所需的时间移位而有所不同)

    ffmpeg -i original.avi -filter:v 'setpts=240+PTS' -sameq -vcodec libx264 shift.mp4

  3. 对生成的shift.mp4进行分割

    ffmpeg -i shift.mp4 -qscale 0 -bsf:v h264_mp4toannexb -vcodec copy -an -map 0 -f segment -segment_time 10 -segment_format mpegts -y ./temp-%03d.ts

在我的情况下,最后一个片段文件“temp-001.ts”被时间移位了

问题:这种方法似乎过于复杂,仅仅是为了调整一些TS包时间,并且它导致了一个起始时间为10.5+,而不是精确的10秒的新TS文件所需的时间


原始建议未按照下面描述的方式工作

ffmpeg -itoffset prevTime (rest of ts gen args) | ffmpeg -ss prevTime -i _ -t 10 stuff.ts

prevTime是所有先前片段的持续时间。

第二个ffmpeg -ss调用不好,因为它使输出的mpegts文件相对于时间0(或者有时是1.4秒-可能是单个ts文件构造中的一个bug)。


2

IMO - 您有一系列序列化的片段,您想要将它们连接起来。

只要串联时段的序列顺序得到保留,就可以实现这一点。

处理每个片段条目的过程,以便它可以被连接起来....

getVideoRaw to its own file
getAudioRaw to its own file

当您将所有的segement分离到原始状态后,请执行以下操作...

concatenate video preserving serialized order so video segments remain correct order in videoConCatOUT.

concatenate the audio as above

然后将各自的concatOUT文件混合成一个单一的容器。

这可以通过脚本进行,并且可以按照ffmpeg FAQ中关于Concat的标准示例进行操作。

请参见“3.14.4”章节此处

请注意,“tail”命令和有关从除第一个片段输入外的所有片段输入中删除第1行的说明...


谢谢Robert,但拼接和重新分段目前是瓶颈。目标(至少对我来说)是在不同的进程和不同的系统上执行分割,然后从单个m3u8列表流回。 - Mark Essel
顺便说一句,我最终在下面的答案中编写了一些代码,以精确地实现我想要的操作(移动时间戳),请参见H264编码片段的直接数据包时间偏移。 - Mark Essel

1

看一下setpts过滤器。这应该可以让你对每个片段的PTS有足够的控制。


1

在分段之前,您应该进行转码。当您对单个片段进行转码时,每次都会创建一个新的ts流,并且ts时间数据不会被复制。


1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接