gpt4 book ai didi

ios - 通过 ffmpegwrapper 切割 MPEG-TS 文件?

转载 作者:IT王子 更新时间:2023-10-29 08:15:39 29 4
gpt4 key购买 nike

我的设备上有 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];
});
}

最佳答案

FFmpeg(在本例中为 libavformat/codec)API 非常接近地映射 ffmpeg.exe 命令行参数。要打开文件,请使用 avformat_open_input_file ().最后两个参数可以为 NULL。这会为您填充 AVFormatContext。现在您开始使用 av_read_frame() 阅读框架在一个循环中。 pkt.stream_index 会告诉你每个数据包属于哪个流,avformatcontext->streams[pkt.stream_index] 是附带的流信息,告诉你它使用什么编解码器,是否是视频/音频等。使用avformat_close()关闭。

对于 muxing,您使用相反的方法,参见 muxing了解详情。基本上是 allocate , avio_open2 , add streams对于输入文件中的每个现有流(基本上是 context->streams[]),avformat_write_header() , av_interleaved_write_frame()在一个循环中,av_write_trailer()关闭(和 free 最后分配的上下文)。

视频流的编码/解码是使用 libavcodec 完成的。对于从 muxer 获得的每个 AVPacket,使用 avcodec_decode_video2() .使用 avcodec_encode_video2()用于输出 AVFrame 的编码。请注意,两者都会引入延迟,因此对每个函数的前几次调用不会返回任何数据,您需要通过使用 NULL 输入数据调用每个函数来刷新缓存数据,以从中获取尾数据包/帧。 av_interleave_write_frame 将正确交错数据包,因此视频/音频流不会不同步(如:相同时间戳的视频数据包在 ts 文件中的音频数据包之后出现 MB)。

如果您需要更详细的 avcodec_decode_video2、avcodec_encode_video2、av_read_frame 或 av_interleaved_write_frame 示例,只需谷歌“$function example”,您就会看到展示如何正确使用它们的完整示例。对于 x264 编码,set some default parameters在调用 avcodec_open2 进行编码质量设置时在 AVCodecContext 中。在 C API 中,您可以使用 AVDictionary 执行此操作,例如:

AVDictionary opts = *NULL;
av_dict_set(&opts, "preset", "veryslow", 0);
// use either crf or b, not both! See the link above on H264 encoding options
av_dict_set_int(&opts, "b", 1000, 0);
av_dict_set_int(&opts, "crf", 10, 0);

[edit] 哦,我忘了一个部分,时间戳。每个 AVPacket 和 AVFrame 在其结构中都有一个 pts 变量,您可以使用它来决定是否在输出流中包含数据包/帧。所以对于音频,你会使用 AVPacket.pts来自 demuxing 步骤作为分隔符,对于视频,您将使用 AVFrame.pts从解码步骤作为分隔符。它们各自的文档会告诉您它们的单位。

[edit2] 我看到您在没有实际代码的情况下仍然遇到一些问题,所以这是一个真正的(工作中的)转码器,它可以重新编码视频并重新混合音频。它可能有大量的错误、泄漏并且缺乏适当的错误报告,它也不处理时间戳(我把它留给你作为练习),但它做了你要求的基本事情:

#include <stdio.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>

static AVFormatContext *inctx, *outctx;
#define MAX_STREAMS 16
static AVCodecContext *inavctx[MAX_STREAMS];
static AVCodecContext *outavctx[MAX_STREAMS];

static int openInputFile(const char *file) {
int res;

inctx = NULL;
res = avformat_open_input(& inctx, file, NULL, NULL);
if (res != 0)
return res;
res = avformat_find_stream_info(inctx, NULL);
if (res < 0)
return res;

return 0;
}

static void closeInputFile(void) {
int n;

for (n = 0; n < inctx->nb_streams; n++)
if (inavctx[n]) {
avcodec_close(inavctx[n]);
avcodec_free_context(&inavctx[n]);
}

avformat_close_input(&inctx);
}

static int openOutputFile(const char *file) {
int res, n;

outctx = avformat_alloc_context();
outctx->oformat = av_guess_format(NULL, file, NULL);
if ((res = avio_open2(&outctx->pb, file, AVIO_FLAG_WRITE, NULL, NULL)) < 0)
return res;

for (n = 0; n < inctx->nb_streams; n++) {
AVStream *inst = inctx->streams[n];
AVCodecContext *inc = inst->codec;

if (inc->codec_type == AVMEDIA_TYPE_VIDEO) {
// video decoder
inavctx[n] = avcodec_alloc_context3(inc->codec);
avcodec_copy_context(inavctx[n], inc);
if ((res = avcodec_open2(inavctx[n], avcodec_find_decoder(inc->codec_id), NULL)) < 0)
return res;

// video encoder
AVCodec *encoder = avcodec_find_encoder_by_name("libx264");
AVStream *outst = avformat_new_stream(outctx, encoder);
outst->codec->width = inavctx[n]->width;
outst->codec->height = inavctx[n]->height;
outst->codec->pix_fmt = inavctx[n]->pix_fmt;
AVDictionary *dict = NULL;
av_dict_set(&dict, "preset", "veryslow", 0);
av_dict_set_int(&dict, "crf", 10, 0);
outavctx[n] = avcodec_alloc_context3(encoder);
avcodec_copy_context(outavctx[n], outst->codec);
if ((res = avcodec_open2(outavctx[n], encoder, &dict)) < 0)
return res;
} else if (inc->codec_type == AVMEDIA_TYPE_AUDIO) {
avformat_new_stream(outctx, inc->codec);
inavctx[n] = outavctx[n] = NULL;
} else {
fprintf(stderr, "Don’t know what to do with stream %d\n", n);
return -1;
}
}

if ((res = avformat_write_header(outctx, NULL)) < 0)
return res;

return 0;
}

static void closeOutputFile(void) {
int n;

av_write_trailer(outctx);
for (n = 0; n < outctx->nb_streams; n++)
if (outctx->streams[n]->codec)
avcodec_close(outctx->streams[n]->codec);
avformat_free_context(outctx);
}

static int encodeFrame(int stream_index, AVFrame *frame, int *gotOutput) {
AVPacket outPacket;
int res;

av_init_packet(&outPacket);
if ((res = avcodec_encode_video2(outavctx[stream_index], &outPacket, frame, gotOutput)) < 0) {
fprintf(stderr, "Failed to encode frame\n");
return res;
}
if (*gotOutput) {
outPacket.stream_index = stream_index;
if ((res = av_interleaved_write_frame(outctx, &outPacket)) < 0) {
fprintf(stderr, "Failed to write packet\n");
return res;
}
}
av_free_packet(&outPacket);

return 0;
}

static int decodePacket(int stream_index, AVPacket *pkt, AVFrame *frame, int *frameFinished) {
int res;

if ((res = avcodec_decode_video2(inavctx[stream_index], frame,
frameFinished, pkt)) < 0) {
fprintf(stderr, "Failed to decode frame\n");
return res;
}
if (*frameFinished){
int hasOutput;

frame->pts = frame->pkt_pts;
return encodeFrame(stream_index, frame, &hasOutput);
} else {
return 0;
}
}

int main(int argc, char *argv[]) {
char *input = argv[1];
char *output = argv[2];
int res, n;

printf("Converting %s to %s\n", input, output);
av_register_all();
if ((res = openInputFile(input)) < 0) {
fprintf(stderr, "Failed to open input file %s\n", input);
return res;
}
if ((res = openOutputFile(output)) < 0) {
fprintf(stderr, "Failed to open output file %s\n", input);
return res;
}

AVFrame *frame = av_frame_alloc();
AVPacket inPacket;

av_init_packet(&inPacket);
while (av_read_frame(inctx, &inPacket) >= 0) {
if (inavctx[inPacket.stream_index] != NULL) {
int frameFinished;
if ((res = decodePacket(inPacket.stream_index, &inPacket, frame, &frameFinished)) < 0) {
return res;
}
} else {
if ((res = av_interleaved_write_frame(outctx, &inPacket)) < 0) {
fprintf(stderr, "Failed to write packet\n");
return res;
}
}
}

for (n = 0; n < inctx->nb_streams; n++) {
if (inavctx[n]) {
// flush decoder
int frameFinished;
do {
inPacket.data = NULL;
inPacket.size = 0;
if ((res = decodePacket(n, &inPacket, frame, &frameFinished)) < 0)
return res;
} while (frameFinished);

// flush encoder
int gotOutput;
do {
if ((res = encodeFrame(n, NULL, &gotOutput)) < 0)
return res;
} while (gotOutput);
}
}
av_free_packet(&inPacket);

closeInputFile();
closeOutputFile();

return 0;
}

关于ios - 通过 ffmpegwrapper 切割 MPEG-TS 文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34823545/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com