summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMole Shang <[email protected]>2023-08-09 22:39:07 +0800
committerMole Shang <[email protected]>2023-08-09 22:39:07 +0800
commitf7ccf916d9755700c655499afb0c8c636be07242 (patch)
tree36e604db3b99cdddaf3267944fccc06e79a1647d
parente910743450ae6ca3ec2a65e0e415f4a5d19ff3f9 (diff)
downloadhinata-f7ccf916d9755700c655499afb0c8c636be07242.tar.gz
hinata-f7ccf916d9755700c655499afb0c8c636be07242.tar.bz2
hinata-f7ccf916d9755700c655499afb0c8c636be07242.zip
utils/ffmpeg: support remux
-rw-r--r--src/utils/ffmpeg.c192
-rw-r--r--src/utils/ffmpeg.h2
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