2013-08-19 110 views
4

我们使用FFmpeg库git-ee94362 libavformat v55.2.100。 我们的目标是使用HLS将两个流(视频和音频)复合到M3U8播放列表中。 此外,我们希望每个TS段文件的持续时间恰好为3.0秒(帧速率为25 fps)。FFmpeg库:HLS的段持续时间完全不变

为了实现它,我们正在尝试设置几个选项和性能,即: - segment_time
- keyint_min - scenechange_threshold - gop_size - force_key_frames。

我们的代码看起来如下:

AVCodecContext *codec_ctx = NULL; 
AVFormatContext *ofmt_ctx = NULL; 

int ret = 0, gopSize = (int)(3.0 * 25); // 3 sec * 25 fps 

// ofmt_ctx and codec_ctx initialization and filling are OK, but: 
codec_ctx->time_base.num = 1; 
codec_ctx->time_base.den = 25 // fps 

// It seems, that the following three lines have no effect without explisit setting of the "hls_time" property 
codec_ctx->keyint_min = gopSize;  // in FFMpeg application, the corresponding option is "-keyint_min 3" 
codec_ctx->scenechange_threshold = 0; // in FFMpeg application, the corresponding option is "-sc_threshold 0" 
codec_ctx->gop_size = gopSize;   // in FFMpeg application, the corresponding option is "-g 3" 

ret = av_opt_set_double(ofmt_ctx, "hls_time", 3.0, AV_OPT_SEARCH_CHILDREN); 

// Any of the following lines causes "Option not found" error. 
ret = av_opt_set(codec_ctx->priv_data, "profile", "main", AV_OPT_SEARCH_CHILDREN); 
ret = av_opt_set(codec_ctx->priv_data, "preset", "ultrafast", AV_OPT_SEARCH_CHILDREN); 
ret = av_opt_get(ofmt_ctx, "segment_time", AV_OPT_SEARCH_CHILDREN, &str); 
ret = av_opt_set((ofmt_ctx, "segment_time", "3.0", AV_OPT_SEARCH_CHILDREN); 

无论如何,TS文件的持续时间是不同的,(约2-3秒),不完全是3.0秒。 我们的问题:解决问题的最佳方法是什么?

Andrey Mochenov。

+1

你能找到解决这个问题的办法吗? –

+0

你是否解决了这个问题? –

回答

0

您面临的主要问题可能是您的视频文件没有合适位置的关键帧。如果您只是从输入中复制流,那么这尤其是个问题。

FFmpeg取决于关键帧来计算何时“剪切”一个段。当你考虑它时是有意义的。您不能仅在两个关键帧之间进行切分,因为每个分段都需要完全自行运行。现在,有人可能会说,FFmpeg应该自己插入新的关键帧,然后,但这会太友好,不是这样;)

谢天谢地,您可以使用FFmpeg强制关键帧。无论是使用参数还是自己在代码中设置标志。你说你已经试过强制关键帧,但我认为你没有正确地做。

我的这个测试产生了相当好的结果。它只是命令行,很抱歉,但你似乎已经知道如何在代码中应用命令行参数,所以你应该没问题。 另外请注意,我不使用“hls_XXX”参数,因为a)我诚实地不相信他们和b)我认为它应该也适用于非HLS流。

ffmpeg -i inputFile.mov -force_key_frames "expr:gte(t,n_forced*10)" -strict -2 -c:a aac -c:v libx264 -f segment -segment_list_type m3u8 -segment_list_size 0 -segment_time 10.0 -segment_time_delta 0.1 -segment_list stream/test.m3u8 stream/test%02d.ts 

您可以查看force_key_frames命令的工作原理here

现在我用C++实现了上面的命令,并增加了一些内容。但没有“force_key_frames”,因为我在转码过程中手动设置了关键帧。下面是我做的:

AVDictionary* headerOptions(0); 
av_dict_set(&headerOptions, "segment_format", "mpegts", 0); 
av_dict_set(&headerOptions, "segment_list_type", "m3u8", 0); 
av_dict_set(&headerOptions, "segment_list", _playlistFileName.c_str(), 0); 
av_dict_set_int(&headerOptions, "segment_list_size", 0, 0); 
av_dict_set(&headerOptions, "segment_time_delta", TO_STRING(1.00).c_str(), 0); 
av_dict_set(&headerOptions, "segment_time", TO_STRING(_segmentDuration).c_str(), 0); 
av_dict_set_int(&headerOptions, "reference_stream", _videoStream->index, 0); 
av_dict_set(&headerOptions, "segment_list_flags", "cache+live", 0); 
avformat_write_header(_formatContext, &headerOptions); 

这里是导致M3U8:

#EXTM3U 
#EXT-X-VERSION:3 
#EXT-X-MEDIA-SEQUENCE:0 
#EXT-X-ALLOW-CACHE:YES 
#EXT-X-TARGETDURATION:11 
#EXTINF:10.083333, 
test00.ts 
#EXTINF:10.000000, 
test01.ts 
#EXTINF:10.000000, 
test02.ts 
#EXTINF:10.000000, 
test03.ts 
#EXTINF:10.000000, 
test04.ts 
#EXTINF:10.000000, 
test05.ts 
#EXTINF:0.083333, 
test06.ts 
#EXT-X-ENDLIST 

它不是完美的(第一部分是有点莫名其妙关),但我相信你不会比这更好的结果。

当然,最好的选择是在复制流时确保输入文件总是有正确的关键帧,但有时你无法控制你得到的文件。

旁注

当你在代码中使用的FFmpeg,总是试图你第一次使用CLI命令ffmpeg的代码做什么。如果你能以这种方式工作,你至少知道要在代码中设置什么参数。如果它使用命令行工具,你知道它必须以某种方式代码可能;)

0

你也可以尝试通过修改ffmpeg实现3秒段(大约)持续时间。正如@theSHEEP所指出的,ffmpeg在进行剪辑之前等待I帧到达。您可以通过强制它在“您的时间”进行剪辑而不是等待I帧来改变ffmpeg的这种行为。

ffmpeg/libavformat/segment.c, 
    795 static int seg_write_packet(AVFormatContext *s, AVPacket *pkt) 

    835  if (pkt->stream_index == seg->reference_stream_index && 
    836   pkt->flags & AV_PKT_FLAG_KEY && 
    837   seg->segment_frame_count > 0 && 
    838   (seg->cut_pending || seg->frame_count >= start_frame || 
    839   (pkt->pts != AV_NOPTS_VALUE && 
    840   av_compare_ts(pkt->pts, st->time_base, 
    841       end_pts-seg->time_delta, AV_TIME_BASE_Q) >= 0))) 

我会将第835行更改为841以符合我的要求。 (注释行836和尝试,并记住FFMPEG是LGPL)

HLS IETF草案版本建议:

服务器应该尝试源媒体在点除以个人的媒体段 支持有效的解码,例如在包 和关键帧边界

我会读它作为建议比要求。 ;)

相关问题