diff options
Diffstat (limited to 'common/recorder.c')
-rw-r--r-- | common/recorder.c | 102 |
1 files changed, 67 insertions, 35 deletions
diff --git a/common/recorder.c b/common/recorder.c index aad9faf40c..42ae7f88d2 100644 --- a/common/recorder.c +++ b/common/recorder.c @@ -23,6 +23,7 @@ #include "common/common.h" #include "common/global.h" #include "common/msg.h" +#include "demux/demux.h" #include "demux/packet.h" #include "demux/stheader.h" @@ -63,6 +64,7 @@ struct mp_recorder_sink { struct mp_recorder *owner; struct sh_stream *sh; AVStream *av_stream; + AVPacket *avpkt; double max_out_pts; bool discont; bool proper_eof; @@ -73,23 +75,26 @@ struct mp_recorder_sink { static int add_stream(struct mp_recorder *priv, struct sh_stream *sh) { enum AVMediaType av_type = mp_to_av_stream_type(sh->type); + int ret = -1; + AVCodecParameters *avp = NULL; if (av_type == AVMEDIA_TYPE_UNKNOWN) - return -1; + goto done; struct mp_recorder_sink *rst = talloc(priv, struct mp_recorder_sink); *rst = (struct mp_recorder_sink) { .owner = priv, .sh = sh, .av_stream = avformat_new_stream(priv->mux, NULL), + .avpkt = av_packet_alloc(), .max_out_pts = MP_NOPTS_VALUE, }; - if (!rst->av_stream) - return -1; + if (!rst->av_stream || !rst->avpkt) + goto done; - AVCodecParameters *avp = mp_codec_params_to_av(sh->codec); + avp = mp_codec_params_to_av(sh->codec); if (!avp) - return -1; + goto done; // Check if we get the same codec_id for the output format; // otherwise clear it to have a chance at muxing @@ -105,21 +110,28 @@ static int add_stream(struct mp_recorder *priv, struct sh_stream *sh) avp->video_delay = 16; if (avp->codec_id == AV_CODEC_ID_NONE) - return -1; + goto done; if (avcodec_parameters_copy(rst->av_stream->codecpar, avp) < 0) - return -1; + goto done; + ret = 0; rst->av_stream->time_base = mp_get_codec_timebase(sh->codec); MP_TARRAY_APPEND(priv, priv->streams, priv->num_streams, rst); - return 0; + +done: + if (avp) + avcodec_parameters_free(&avp); + return ret; } struct mp_recorder *mp_recorder_create(struct mpv_global *global, const char *target_file, struct sh_stream **streams, - int num_streams) + int num_streams, + struct demux_attachment **attachments, + int num_attachments) { struct mp_recorder *priv = talloc_zero(NULL, struct mp_recorder); @@ -153,6 +165,35 @@ struct mp_recorder *mp_recorder_create(struct mpv_global *global, } } + if (!strcmp(priv->mux->oformat->name, "matroska")) { + // Only attach attachments (fonts) to matroska - mp4, nut, mpegts don't + // like them, and we find that out too late in the muxing process. + AVStream *a_stream = NULL; + for (int i = 0; i < num_attachments; ++i) { + a_stream = avformat_new_stream(priv->mux, NULL); + if (!a_stream) { + MP_ERR(priv, "Can't mux one of the attachments.\n"); + goto error; + } + struct demux_attachment *attachment = attachments[i]; + + a_stream->codecpar->codec_type = AVMEDIA_TYPE_ATTACHMENT; + + a_stream->codecpar->extradata = av_mallocz( + attachment->data_size + AV_INPUT_BUFFER_PADDING_SIZE + ); + if (!a_stream->codecpar->extradata) { + goto error; + } + memcpy(a_stream->codecpar->extradata, + attachment->data, attachment->data_size); + a_stream->codecpar->extradata_size = attachment->data_size; + + av_dict_set(&a_stream->metadata, "filename", attachment->name, 0); + av_dict_set(&a_stream->metadata, "mimetype", attachment->type, 0); + } + } + // Not sure how to write this in a "standard" way. It appears only mkv // and mp4 support this directly. char version[200]; @@ -205,15 +246,14 @@ static void mux_packet(struct mp_recorder_sink *rst, rst->max_out_pts = MP_PTS_MAX(rst->max_out_pts, pkt->pts); - AVPacket avpkt; - mp_set_av_packet(&avpkt, &mpkt, &rst->av_stream->time_base); + mp_set_av_packet(rst->avpkt, &mpkt, &rst->av_stream->time_base); - avpkt.stream_index = rst->av_stream->index; + rst->avpkt->stream_index = rst->av_stream->index; - if (avpkt.duration < 0 && rst->sh->type != STREAM_SUB) - avpkt.duration = 0; + if (rst->avpkt->duration < 0 && rst->sh->type != STREAM_SUB) + rst->avpkt->duration = 0; - AVPacket *new_packet = av_packet_clone(&avpkt); + AVPacket *new_packet = av_packet_clone(rst->avpkt); if (!new_packet) { MP_ERR(priv, "Failed to allocate packet.\n"); return; @@ -221,32 +261,23 @@ static void mux_packet(struct mp_recorder_sink *rst, if (av_interleaved_write_frame(priv->mux, new_packet) < 0) MP_ERR(priv, "Failed writing packet.\n"); + + av_packet_free(&new_packet); } -// Write all packets that currently can be written. -static void mux_packets(struct mp_recorder_sink *rst, bool force) +// Write all packets available in the stream queue +static void mux_packets(struct mp_recorder_sink *rst) { struct mp_recorder *priv = rst->owner; if (!priv->muxing || !rst->num_packets) return; - int safe_count = 0; for (int n = 0; n < rst->num_packets; n++) { - if (rst->packets[n]->keyframe) - safe_count = n; - } - if (force) - safe_count = rst->num_packets; - - for (int n = 0; n < safe_count; n++) { mux_packet(rst, rst->packets[n]); talloc_free(rst->packets[n]); } - // Remove packets[0..safe_count] - memmove(&rst->packets[0], &rst->packets[safe_count], - (rst->num_packets - safe_count) * sizeof(rst->packets[0])); - rst->num_packets -= safe_count; + rst->num_packets = 0; } // If there was a discontinuity, check whether we can resume muxing (and from @@ -297,7 +328,8 @@ void mp_recorder_destroy(struct mp_recorder *priv) if (priv->opened) { for (int n = 0; n < priv->num_streams; n++) { struct mp_recorder_sink *rst = priv->streams[n]; - mux_packets(rst, true); + mux_packets(rst); + mp_free_av_packet(&rst->avpkt); } if (av_write_trailer(priv->mux) < 0) @@ -318,15 +350,15 @@ void mp_recorder_destroy(struct mp_recorder *priv) // This is called on a seek, or when recording was started mid-stream. void mp_recorder_mark_discontinuity(struct mp_recorder *priv) { - flush_packets(priv); for (int n = 0; n < priv->num_streams; n++) { struct mp_recorder_sink *rst = priv->streams[n]; - mux_packets(rst, true); + mux_packets(rst); rst->discont = true; rst->proper_eof = false; } + flush_packets(priv); priv->muxing = false; priv->muxing_from_start = false; } @@ -356,7 +388,7 @@ void mp_recorder_feed_packet(struct mp_recorder_sink *rst, if (!pkt) { rst->proper_eof = true; check_restart(priv); - mux_packets(rst, false); + mux_packets(rst); return; } @@ -365,7 +397,7 @@ void mp_recorder_feed_packet(struct mp_recorder_sink *rst, // No, FFmpeg doesn't tell us which formats need DTS at all. // No, we can not shut up the FFmpeg warning, which will follow. MP_WARN(priv, "Source stream misses DTS on at least some packets!\n" - "If the target file format requires DTS, the written\n" + "If the target file format requires DTS, the written " "file will be invalid.\n"); priv->dts_warning = true; } @@ -386,5 +418,5 @@ void mp_recorder_feed_packet(struct mp_recorder_sink *rst, MP_TARRAY_APPEND(rst, rst->packets, rst->num_packets, pkt); check_restart(priv); - mux_packets(rst, false); + mux_packets(rst); } |