diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/utils/ffmpeg.c | 192 | ||||
| -rw-r--r-- | src/utils/ffmpeg.h | 2 | 
2 files changed, 187 insertions, 7 deletions
| diff --git a/src/utils/ffmpeg.c b/src/utils/ffmpeg.c index e4d9e44..694400c 100644 --- a/src/utils/ffmpeg.c +++ b/src/utils/ffmpeg.c @@ -9,6 +9,25 @@  #include "../logger.h"  #include "ffmpeg.h" +#ifdef DEBUG +static void log_packet(const AVFormatContext *fmt_ctx, const AVPacket *pkt, +                       const char *tag) { +  AVRational *time_base = &fmt_ctx->streams[pkt->stream_index]->time_base; + +  printf("%s: pts:%s pts_time:%s dts:%s dts_time:%s duration:%s " +         "duration_time:%s stream_index:%d\n", +         tag, av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, time_base), +         av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, time_base), +         av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, time_base), +         pkt->stream_index); +} +#else +static void log_packet(const AVFormatContext *fmt_ctx, const AVPacket *pkt, +                       const char *tag) { +  return; +} +#endif +  int merge_av(const char *videofn, const char *audiofn, const char *outfn) {    AVFormatContext *input1_format_context = NULL, *input2_format_context = NULL,                    *output_format_context = NULL; @@ -18,6 +37,16 @@ int merge_av(const char *videofn, const char *audiofn, const char *outfn) {    int *streams_list = NULL;    int number_of_streams = 0; +  AVDictionary *opts = NULL; +  /* Support fragmented MP4 +   * https://developer.mozilla.org/en-US/docs/Web/API/Media_Source_Extensions_API/Transcoding_assets_for_MSE +   */ +  av_dict_set(&opts, "movflags", "frag_keyframe+empty_moov+default_base_moof", +              0); +  /* Enable all protocols support */ +  av_dict_set(&opts, "protocol_whitelist", +              "file,crypto,data,http,https,tcp,tls", 0); +    if ((ret = avformat_open_input(&input1_format_context, videofn, NULL, NULL)) <        0) {      append_log("Could not open input file '%s'\n", videofn); @@ -112,12 +141,7 @@ int merge_av(const char *videofn, const char *audiofn, const char *outfn) {        goto end;      }    } -  AVDictionary *opts = NULL; -  /* Support fragmented MP4 -   * https://developer.mozilla.org/en-US/docs/Web/API/Media_Source_Extensions_API/Transcoding_assets_for_MSE -   */ -  av_dict_set(&opts, "movflags", "frag_keyframe+empty_moov+default_base_moof", -              0); +    ret = avformat_write_header(output_format_context, &opts);    if (ret < 0) {      append_log("Error occurred when opening output file\n"); @@ -128,7 +152,7 @@ int merge_av(const char *videofn, const char *audiofn, const char *outfn) {      ret = av_read_frame(input1_format_context, &packet);      if (ret < 0)        break; -    in_stream = input1_format_context->streams[packet.stream_index]; +    in_stream = input1_format_context->streams[stream_index];      if (packet.stream_index >= number_of_streams ||          streams_list[packet.stream_index] < 0) {        av_packet_unref(&packet); @@ -137,6 +161,7 @@ int merge_av(const char *videofn, const char *audiofn, const char *outfn) {      packet.stream_index = streams_list[packet.stream_index];      out_stream = output_format_context->streams[packet.stream_index];      /* copy packet */ +    log_packet(input1_format_context, &packet, "in");      packet.pts = av_rescale_q_rnd(packet.pts, in_stream->time_base,                                    out_stream->time_base,                                    AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); @@ -146,6 +171,7 @@ int merge_av(const char *videofn, const char *audiofn, const char *outfn) {      packet.duration = av_rescale_q(packet.duration, in_stream->time_base,                                     out_stream->time_base);      packet.pos = -1; +    log_packet(output_format_context, &packet, "out");      ret = av_interleaved_write_frame(output_format_context, &packet);      if (ret < 0) { @@ -170,6 +196,7 @@ int merge_av(const char *videofn, const char *audiofn, const char *outfn) {          streams_list[packet.stream_index + input1_format_context->nb_streams];      out_stream = output_format_context->streams[packet.stream_index];      /* copy packet */ +    log_packet(input2_format_context, &packet, "in");      packet.pts = av_rescale_q_rnd(packet.pts, in_stream->time_base,                                    out_stream->time_base,                                    AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); @@ -179,6 +206,7 @@ int merge_av(const char *videofn, const char *audiofn, const char *outfn) {      packet.duration = av_rescale_q(packet.duration, in_stream->time_base,                                     out_stream->time_base);      packet.pos = -1; +    log_packet(output_format_context, &packet, "out");      ret = av_interleaved_write_frame(output_format_context, &packet);      if (ret < 0) { @@ -211,3 +239,153 @@ end:    }    return 0;  } + +int remux(const char *in_filename, const char *out_filename) { +  const AVOutputFormat *ofmt = NULL; +  AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL; +  AVPacket *pkt = NULL; +  int ret, i; +  int stream_index = 0; +  int *stream_mapping = NULL; +  int stream_mapping_size = 0; + +  pkt = av_packet_alloc(); +  if (!pkt) { +    fprintf(stderr, "Could not allocate AVPacket\n"); +    return 1; +  } + +  AVDictionary *opts = NULL; +  av_dict_set(&opts, "protocol_whitelist", +              "concat,file,http,https,tcp,tls,crypto", 0); + +  if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, &opts)) < 0) { +    fprintf(stderr, "Could not open input file '%s'\n", in_filename); +    goto end; +  } + +  if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) { +    fprintf(stderr, "Failed to retrieve input stream information.\n"); +    goto end; +  } + +  av_dump_format(ifmt_ctx, 0, in_filename, 0); + +  avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename); +  if (!ofmt_ctx) { +    fprintf(stderr, "Could not create output context.\n"); +    ret = AVERROR_UNKNOWN; +    goto end; +  } + +  stream_mapping_size = ifmt_ctx->nb_streams; +  stream_mapping = av_calloc(stream_mapping_size, sizeof(*stream_mapping)); +  if (!stream_mapping) { +    ret = AVERROR(ENOMEM); +    goto end; +  } + +  ofmt = ofmt_ctx->oformat; + +  for (i = 0; i < ifmt_ctx->nb_streams; i++) { +    AVStream *out_stream; +    AVStream *in_stream = ifmt_ctx->streams[i]; +    AVCodecParameters *in_codecpar = in_stream->codecpar; + +    if (in_codecpar->codec_type != AVMEDIA_TYPE_AUDIO && +        in_codecpar->codec_type != AVMEDIA_TYPE_VIDEO && +        in_codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) { +      stream_mapping[i] = -1; +      continue; +    } + +    stream_mapping[i] = stream_index++; + +    out_stream = avformat_new_stream(ofmt_ctx, NULL); +    if (!out_stream) { +      fprintf(stderr, "Failed allocating output stream\n"); +      ret = AVERROR_UNKNOWN; +      goto end; +    } + +    ret = avcodec_parameters_copy(out_stream->codecpar, in_codecpar); +    if (ret < 0) { +      fprintf(stderr, "Failed to copy codec parameters\n"); +      goto end; +    } +    out_stream->codecpar->codec_tag = 0; +  } +  av_dump_format(ofmt_ctx, 0, out_filename, 1); + +  if (!(ofmt->flags & AVFMT_NOFILE)) { +    ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE); +    if (ret < 0) { +      fprintf(stderr, "Could not open output file '%s'", out_filename); +      goto end; +    } +  } + +  ret = avformat_write_header(ofmt_ctx, &opts); +  if (ret < 0) { +    fprintf(stderr, "Error occurred when opening output file\n"); +    goto end; +  } + +  while (1) { +    AVStream *in_stream, *out_stream; + +    ret = av_read_frame(ifmt_ctx, pkt); +    if (ret < 0) +      break; + +    in_stream = ifmt_ctx->streams[pkt->stream_index]; +    if (pkt->stream_index >= stream_mapping_size || +        stream_mapping[pkt->stream_index] < 0) { +      av_packet_unref(pkt); +      continue; +    } + +    pkt->stream_index = stream_mapping[pkt->stream_index]; +    out_stream = ofmt_ctx->streams[pkt->stream_index]; +    log_packet(ifmt_ctx, pkt, "in"); + +    /* copy packet */ +    av_packet_rescale_ts(pkt, in_stream->time_base, out_stream->time_base); +    pkt->pos = -1; +    log_packet(ofmt_ctx, pkt, "out"); + +    ret = av_interleaved_write_frame(ofmt_ctx, pkt); +    /* pkt is now blank (av_interleaved_write_frame() takes ownership of +     * its contents and resets pkt), so that no unreferencing is necessary. +     * This would be different if one used av_write_frame(). */ +    if (ret < 0) { +      fprintf(stderr, "Error muxing packet\n"); +      break; +    } +  } + +  av_write_trailer(ofmt_ctx); +end: +  av_packet_free(&pkt); + +  avformat_close_input(&ifmt_ctx); + +  /* close output */ +  if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE)) +    avio_closep(&ofmt_ctx->pb); +  avformat_free_context(ofmt_ctx); + +  av_freep(&stream_mapping); + +  if (ret < 0 && ret != AVERROR_EOF) { +    fprintf(stderr, "Error occurred: %s\n", av_err2str(ret)); +    return 1; +  } + +  // Delete seperate files +  if (remove(in_filename) != 0) { +    append_log("Error deleting partial file %s\n", in_filename); +  } + +  return 0; +} diff --git a/src/utils/ffmpeg.h b/src/utils/ffmpeg.h index 91d79df..1ca949d 100644 --- a/src/utils/ffmpeg.h +++ b/src/utils/ffmpeg.h @@ -3,4 +3,6 @@  int merge_av(const char *videofn, const char *audiofn, const char *outfn); +int remux(const char *inputfn, const char *outfn); +  #endif | 
