【发布时间】:2016-04-21 18:48:07
【问题描述】:
我的设备上有 MPEG-TS 文件。我想缩短设备上文件的开始时间。
以FFmpegWrapper 为基础,我希望能够实现这一目标。
不过,我对 ffmpeg 的 C API 有点迷茫。我从哪里开始?
我尝试在我正在寻找的开始 PTS 之前丢弃所有数据包,但这破坏了视频流。
packet->pts = av_rescale_q(packet->pts, inputStream.stream->time_base, outputStream.stream->time_base);
packet->dts = av_rescale_q(packet->dts, inputStream.stream->time_base, outputStream.stream->time_base);
if(startPts == 0){
startPts = packet->pts;
}
if(packet->pts < cutTimeStartPts + startPts){
av_free_packet(packet);
continue;
}
如何在不破坏视频流的情况下切断输入文件的部分开头?背靠背播放时,我希望 2 个剪辑片段无缝地一起运行。
ffmpeg -i time.ts -c:v libx264 -c:a copy -ss $CUT_POINT -map 0 -y after.ts
ffmpeg -i time.ts -c:v libx264 -c:a copy -to $CUT_POINT -map 0 -y before.ts
似乎是我需要的。我认为需要重新编码,以便视频可以从任意点开始,而不是现有的关键帧。如果有更有效的解决方案,那就太好了。如果没有,这就足够了。
编辑:这是我的尝试。我正在拼凑从here复制的各种我不完全理解的部分。我暂时放弃“剪辑”部分,尝试在没有分层复杂性的情况下编写音频+视频编码。我在avcodec_encode_video2(...) 得到 EXC_BAD_ACCESS
- (void)convertInputPath:(NSString *)inputPath outputPath:(NSString *)outputPath
options:(NSDictionary *)options progressBlock:(FFmpegWrapperProgressBlock)progressBlock
completionBlock:(FFmpegWrapperCompletionBlock)completionBlock {
dispatch_async(conversionQueue, ^{
FFInputFile *inputFile = nil;
FFOutputFile *outputFile = nil;
NSError *error = nil;
inputFile = [[FFInputFile alloc] initWithPath:inputPath options:options];
outputFile = [[FFOutputFile alloc] initWithPath:outputPath options:options];
[self setupDirectStreamCopyFromInputFile:inputFile outputFile:outputFile];
if (![outputFile openFileForWritingWithError:&error]) {
[self finishWithSuccess:NO error:error completionBlock:completionBlock];
return;
}
if (![outputFile writeHeaderWithError:&error]) {
[self finishWithSuccess:NO error:error completionBlock:completionBlock];
return;
}
AVRational default_timebase;
default_timebase.num = 1;
default_timebase.den = AV_TIME_BASE;
FFStream *outputVideoStream = outputFile.streams[0];
FFStream *inputVideoStream = inputFile.streams[0];
AVFrame *frame;
AVPacket inPacket, outPacket;
frame = avcodec_alloc_frame();
av_init_packet(&inPacket);
while (av_read_frame(inputFile.formatContext, &inPacket) >= 0) {
if (inPacket.stream_index == 0) {
int frameFinished;
avcodec_decode_video2(inputVideoStream.stream->codec, frame, &frameFinished, &inPacket);
// if (frameFinished && frame->pkt_pts >= starttime_int64 && frame->pkt_pts <= endtime_int64) {
if (frameFinished){
av_init_packet(&outPacket);
int output;
avcodec_encode_video2(outputVideoStream.stream->codec, &outPacket, frame, &output);
if (output) {
if (av_write_frame(outputFile.formatContext, &outPacket) != 0) {
fprintf(stderr, "convert(): error while writing video frame\n");
[self finishWithSuccess:NO error:nil completionBlock:completionBlock];
}
}
av_free_packet(&outPacket);
}
if (frame->pkt_pts > endtime_int64) {
break;
}
}
}
av_free_packet(&inPacket);
if (![outputFile writeTrailerWithError:&error]) {
[self finishWithSuccess:NO error:error completionBlock:completionBlock];
return;
}
[self finishWithSuccess:YES error:nil completionBlock:completionBlock];
});
}
【问题讨论】:
-
所以现在我要添加一个答案。如果没有更有效的答案,我希望我能得到赏金,
-
你应该只剪切随机访问点(I 帧),这样你就可以搜索最近的 PTS,在那里你还可以找到一个随机访问指示符(我认为它被称为 RAP 标志,你应该寻找它在标题中)如果您想每隔一帧剪切,那么您只能通过对视频进行转码来做到这一点
-
@StefanKendall,是我的回答好吧。你觉得还有什么需要补充的吗?另外,如果您找到更有效的方法,请随时通知我。
-
@StefanKendall,另外,我删除了我提到链接的评论,因为我在答案中添加了链接。
-
@Stefan Kendall,您应该将
-ss移动到-i之前,否则 ffmpeg 将解码所有帧然后丢弃,而不是寻求 inpoint。
标签: ios objective-c c ffmpeg