ios - Cutting MPEG-TS file via ffmpegwrapper? -
i have mpeg-ts files on device. cut fairly-exact time off start of files on-device.
using ffmpegwrapper base, i'm hoping achieve this.
i'm little lost on c api of ffmpeg, however. start?
i tried dropping packets prior start pts looking for, broke video stream.
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; }
how cut off part of start of input file without destroying video stream? when played back, want 2 cut segments run seamlessly together.
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
seems need. think re-encode needed video can start @ arbitrary point , not existing keyframe. if there's more efficient solution, that's great. if not, enough.
edit: here's attempt. i'm cobbling various pieces don't understand copied here. i'm leaving off "cutting" piece try , audio + video encoded written without layering complexity. exc_bad_access on avcodec_encode_video2(...)
- (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]; }); }
the ffmpeg (libavformat/codec, in case) api maps ffmpeg.exe commandline arguments pretty closely. open file, use avformat_open_input_file(). last 2 arguments can null. fills in avformatcontext you. start reading frames using av_read_frame() in loop. pkt.stream_index tell stream each packet belongs to, , avformatcontext->streams[pkt.stream_index] accompanying stream information, tells codec uses, whether it's video/audio, etc. use avformat_close() shut down.
for muxing, use inverse, see muxing details. it's allocate, avio_open2, add streams each existing stream in input file (basically context->streams[]), avformat_write_header(), av_interleaved_write_frame() in loop, av_write_trailer() shut down (and free allocated context in end).
encoding/decoding of video stream(s) done using libavcodec. each avpacket muxer, use avcodec_decode_video2(). use avcodec_encode_video2() encoding of output avframe. note both introduce delay first few calls each function not return data , need flush cached data calling each function null input data tail packets/frames out of it. av_interleave_write_frame interleave packets correctly video/audio stream not desync (as in: video packets of same timestamp occur mbs after audio packets in ts file).
if need more detailed examples avcodec_decode_video2, avcodec_encode_video2, av_read_frame or av_interleaved_write_frame, google "$function example" , you'll see full-fledged examples showing how use them correctly. x264 encoding, set default parameters in avcodeccontext when calling avcodec_open2 encoding quality settings. in c api, using avdictionary, e.g.:
avdictionary opts = *null; av_dict_set(&opts, "preset", "veryslow", 0); // use either crf or b, not both! see link above on h264 encoding options av_dict_set_int(&opts, "b", 1000, 0); av_dict_set_int(&opts, "crf", 10, 0);
[edit] oh forgot 1 part, timestamping. each avpacket , avframe has pts variable in struct, , can use decide whether include packet/frame in output stream. audio, you'd use avpacket.pts demuxing step delimiter, , video, you'd use avframe.pts decoding step delimited. respective documentation tells in unit are.
[edit2] see you're still having issues without actual code, here's real (working) transcoder re-codes video , re-muxes audio. has tons of bugs, leaks , lacks proper error reporting, doesn't deal timestamps (i'm leaving exercise), basic things asked for:
#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; (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; (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 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); (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 encode frame\n"); return res; } if (*gotoutput) { outpacket.stream_index = stream_index; if ((res = av_interleaved_write_frame(outctx, &outpacket)) < 0) { fprintf(stderr, "failed 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 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 %s\n", input, output); av_register_all(); if ((res = openinputfile(input)) < 0) { fprintf(stderr, "failed open input file %s\n", input); return res; } if ((res = openoutputfile(output)) < 0) { fprintf(stderr, "failed 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 write packet\n"); return res; } } } (n = 0; n < inctx->nb_streams; n++) { if (inavctx[n]) { // flush decoder int framefinished; { inpacket.data = null; inpacket.size = 0; if ((res = decodepacket(n, &inpacket, frame, &framefinished)) < 0) return res; } while (framefinished); // flush encoder int gotoutput; { if ((res = encodeframe(n, null, &gotoutput)) < 0) return res; } while (gotoutput); } } av_free_packet(&inpacket); closeinputfile(); closeoutputfile(); return 0; }
Comments
Post a Comment