Couper le file MPEG-TS via ffmpegwrapper?

J'ai des files MPEG-TS sur l'appareil. Je voudrais couper un time assez précis sur le début des files sur l'appareil.

En utilisant FFmpegWrapper comme base, j'espère y parvenir.

Je suis un peu perdu sur l'API C de ffmpeg, cependant. Où est-ce que je commence?

J'ai essayé de laisser tomber tous les packages avant de commencer PTS que je cherchais, mais cela a brisé le stream video.

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; } 

Comment couper une partie du début du file d'input sans détruire le stream video? Lorsque vous jouez à l'envers, je veux que 2 segments coupés se déroulent parfaitement set.

 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 

Semble être ce dont j'ai besoin. Je pense que le reencoding est nécessaire pour que la video puisse commencer à n'importe quel point arbitraire et non à une image-key existante. S'il y a une solution plus efficace, c'est génial. Sinon, c'est assez bon.

EDIT: Voici ma tentative. Je suis en train de bricoler différentes pièces que je ne comprends pas parfaitement copiées d' ici . Je suis en train d'abandonner la pièce "découpée" pour l'instant pour essayer d'get un enencoding audio + video sans superposition de couches. J'obtiens EXC_BAD_ACCESS sur avcodec_encode_video2(...)

 - (void)convertInputPath:(NSSsortingng *)inputPath outputPath:(NSSsortingng *)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]; }); } 

L'API FFmpeg (libavformat / codec, dans ce cas) mappe les arguments de la command line ffmpeg.exe de très près. Pour ouvrir un file, utilisez avformat_open_input_file (). Les deux derniers arguments peuvent être NULL. Cela remplit le AVFormatContext pour vous. Maintenant vous commencez à lire les frameworks en utilisant av_read_frame () dans une boucle. Pkt.stream_index vous indiquera à quel stream appartient chaque package, et avformatcontext-> streams [pkt.stream_index] est l'information de stream qui l'accompagne, qui vous indique quel codec il utilise, qu'il s'agisse de video / audio, etc. Utilisez avformat_close () éteindre.

Pour le multiplexage, vous utilisez l'inverse, voir Muxing pour plus de détails. Fondamentalement, c'est allouer , avio_open2 , append des stream pour chaque stream existant dans le file d'input (essentiellement context-> streams []), avformat_write_header () , av_interleaved_write_frame () dans une boucle, av_write_trailer () pour fermer (et libérer le context alloué dans la fin).

L'enencoding / déencoding du (des) stream video est effectué en utilisant libavcodec. Pour chaque AVPacket du muxer, utilisez avcodec_decode_video2 () . Utilisez avcodec_encode_video2 () pour l'enencoding de la sortie AVFrame. Notez que les deux introduiront un timeout de sorte que les premiers appels à chaque fonction ne renverront aucune donnée et que vous devez vider datatables mises en cache en appelant chaque fonction avec des données d'input NULL pour extraire les packages / frameworks de queue. av_interleave_write_frame va entrelacer correctement les packages pour que le stream video / audio ne se désynchronise pas (comme dans: les packages video du même horodatage produisent des MB après les packages audio dans le file ts).

Si vous avez besoin d'exemples plus détaillés pour avcodec_decode_video2, avcodec_encode_video2, av_read_frame ou av_interleaved_write_frame, juste Google "$ exemple de fonction" et vous verrez des exemples complets montrant comment les utiliser correctement. Pour l'enencoding x264, définissez des parameters par défaut dans AVCodecContext lorsque vous appelez avcodec_open2 pour coder les parameters de qualité. Dans l'API C, vous faites cela en utilisant AVDictionary , par exemple:

 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); 

[modifier] Oh j'ai oublié une partie, l'horodatage. Chaque AVPacket et AVFrame a une variable pts dans sa structure, et vous pouvez l'utiliser pour décider d'inclure ou non le package / frame dans le stream de sortie. Donc, pour l'audio, vous utiliserez AVPacket.pts de l'étape de démultiplexage comme délimiteur, et pour la video, vous utiliserez AVFrame.pts de l'étape de déencoding comme délimité. Leur documentation respective vous indique dans quelle unité ils sont.

[edit2] Je vois que vous avez encore des problèmes sans code réel, alors voici un vrai transcodeur (qui fonctionne) qui recode les videos et les re-muxes audio. Il a probablement des tonnes de bugs, des fuites et manque de rapports d'erreurs, il ne traite pas non plus des horodatages (je vous laisse comme un exercice), mais il fait les choses de base que vous avez demandé:

 #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; } 

Découvrez la réponse acceptée de cette question.

En résumé, vous pouvez utiliser:

 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 

Juste pour reference, la réponse acceptée de cette question est:

Comment split et joindre des files en utilisant ffmpeg tout en conservant toutes les pistes audio?

Comme vous l'avez découvert, une copy bitstream sélectionnera une seule piste (audio), conformément à la documentation de spécification de stream :

Par défaut, ffmpeg inclut un seul stream de chaque type (video, audio, sous-titre) présent dans les files d'input et les ajoute à chaque file de sortie. Il choisit le «meilleur» de chacun selon les critères suivants: pour la video, c'est le stream avec la plus haute résolution, pour l'audio, c'est le stream avec le plus de canaux, pour les sous-titres, c'est le premier stream de sous-titres. Dans le cas où plusieurs stream du même type ont la même fréquence, le stream ayant l'indice le plus bas est choisi.

Pour sélectionner toutes les pistes audio:

 ffmpeg -i InputFile.ts-c copy -ss 00:12:34.567 -t 00:34:56.789 -map 0:v -map 0:a FirstFile.ts 

Pour sélectionner la troisième piste audio:

 ffmpeg -i InputFile.ts -c copy -ss 00:12:34.567 -t 00:34:56.789 -map 0:v -map 0:a:2 FirstFile.ts 

Vous pouvez en savoir plus sur et voir d'autres exemples de sélection de stream dans la section des options avancées de la documentation de ffmpeg .

Je combinerais -vcodec copy -acodec copy de votre command d'origine en -c copy comme ci-dessus pour la compacité de l'expression.

Divisé:

Donc, en combinant ceux avec ce que vous voulez atteindre dans les deux files en termes de fractionnement pour une ré-inscription ultérieure:

 ffmpeg -i InputOne.ts -ss 00:02:00.0 -c copy -map 0:v -map 0:a OutputOne.ts ffmpeg -i InputTwo.ts -c copy -t 00:03:05.0 -map 0:v -map 0:a OutputTwo.ts 

te donnera:

  • OutputOne.ts , qui est tout après les deux premières minutes du premier file d'input
  • OutputTwo.ts , qui est les 3 premières minutes et 5 secondes du deuxième file d'input

Joindre:

ffmpeg prend en charge la concaténation de files sans ré-enencoding, décrite en détail dans sa documentation de concaténation .

Créez votre list de files à joindre (par exemple join.txt ):

 file '/path/to/files/OutputOne.ts' file '/path/to/files/OutputTwo.ts' 

Ensuite, votre command ffmpeg peut utiliser le concat demuxer :

  ffmpeg -f concat -i join.txt -c copy FinalOutput.ts 

Puisque vous travaillez avec des stream de transport .ts ( .ts ), vous devriez aussi pouvoir utiliser le protocole concat:

 ffmpeg -i "concat:OutputOne.ts|OutputTwo.ts" -c copy -bsf:a aac_adtstoasc output.mp4 

Par exemple sur la page de concatation ci-dessus. Je vais vous laisser le soin d'expérimenter.