diff options
Diffstat (limited to 'common/recorder.c')
-rw-r--r-- | common/recorder.c | 102 |
1 files changed, 65 insertions, 37 deletions
diff --git a/common/recorder.c b/common/recorder.c index 6b233d41bb..0cbc81d51c 100644 --- a/common/recorder.c +++ b/common/recorder.c @@ -23,13 +23,12 @@ #include "common/common.h" #include "common/global.h" #include "common/msg.h" +#include "demux/demux.h" #include "demux/packet.h" #include "demux/stheader.h" #include "recorder.h" -#define PTS_ADD(a, b) ((a) == MP_NOPTS_VALUE ? (a) : ((a) + (b))) - // Maximum number of packets we buffer at most to attempt to resync streams. // Essentially, this should be higher than the highest supported keyframe // interval. @@ -93,14 +92,18 @@ static int add_stream(struct mp_recorder *priv, struct sh_stream *sh) if (!avp) return -1; -#if LIBAVCODEC_VERSION_MICRO >= 100 + // Check if we get the same codec_id for the output format; + // otherwise clear it to have a chance at muxing + if (av_codec_get_id(priv->mux->oformat->codec_tag, + avp->codec_tag) != avp->codec_id) + avp->codec_tag = 0; + // We don't know the delay, so make something up. If the format requires // DTS, the result will probably be broken. FFmpeg provides nothing better // yet (unless you demux with libavformat, which contains tons of hacks // that try to determine a PTS). if (!sh->codec->lav_codecpar) avp->video_delay = 16; -#endif if (avp->codec_id == AV_CODEC_ID_NONE) return -1; @@ -117,7 +120,9 @@ static int add_stream(struct mp_recorder *priv, struct sh_stream *sh) 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); @@ -151,6 +156,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]; @@ -198,10 +232,10 @@ static void mux_packet(struct mp_recorder_sink *rst, struct demux_packet mpkt = *pkt; double diff = priv->rebase_ts - priv->base_ts; - mpkt.pts = PTS_ADD(mpkt.pts, diff); - mpkt.dts = PTS_ADD(mpkt.dts, diff); + mpkt.pts = MP_ADD_PTS(mpkt.pts, diff); + mpkt.dts = MP_ADD_PTS(mpkt.dts, diff); - rst->max_out_pts = MPMAX(rst->max_out_pts, pkt->pts); + 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); @@ -221,30 +255,19 @@ static void mux_packet(struct mp_recorder_sink *rst, MP_ERR(priv, "Failed writing packet.\n"); } -// 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 @@ -254,13 +277,13 @@ static void check_restart(struct mp_recorder *priv) if (priv->muxing) return; - double min_ts = INFINITY; + double min_ts = MP_NOPTS_VALUE; double rebase_ts = 0; for (int n = 0; n < priv->num_streams; n++) { struct mp_recorder_sink *rst = priv->streams[n]; int min_packets = rst->sh->type == STREAM_VIDEO ? QUEUE_MIN_PACKETS : 1; - rebase_ts = MPMAX(rebase_ts, rst->max_out_pts); + rebase_ts = MP_PTS_MAX(rebase_ts, rst->max_out_pts); if (rst->num_packets < min_packets) { if (!rst->proper_eof && rst->sh->type != STREAM_SUB) @@ -269,11 +292,11 @@ static void check_restart(struct mp_recorder *priv) } for (int i = 0; i < min_packets; i++) - min_ts = MPMIN(min_ts, rst->packets[i]->pts); + min_ts = MP_PTS_MIN(min_ts, rst->packets[i]->pts); } // Subtitle only stream (wait longer) or stream without any PTS (fuck it). - if (!isfinite(min_ts)) + if (min_ts == MP_NOPTS_VALUE) return; priv->rebase_ts = rebase_ts; @@ -295,9 +318,7 @@ 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]; - if (!rst->proper_eof) - continue; - mux_packets(rst, true); + mux_packets(rst); } if (av_write_trailer(priv->mux) < 0) @@ -318,24 +339,31 @@ 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); rst->discont = true; rst->proper_eof = false; } + flush_packets(priv); priv->muxing = false; priv->muxing_from_start = false; } // Get a stream for writing. The pointer is valid until mp_recorder is -// destroyed. The stream is the index referencing the stream passed to -// mp_recorder_create(). -struct mp_recorder_sink *mp_recorder_get_sink(struct mp_recorder *r, int stream) +// destroyed. The stream ptr. is the same as one passed to +// mp_recorder_create() (returns NULL if it wasn't). +struct mp_recorder_sink *mp_recorder_get_sink(struct mp_recorder *r, + struct sh_stream *stream) { - return stream >= 0 && stream < r->num_streams ? r->streams[stream] : NULL; + for (int n = 0; n < r->num_streams; n++) { + struct mp_recorder_sink *rst = r->streams[n]; + if (rst->sh == stream) + return rst; + } + return NULL; } // Pass a packet to the given stream. The function does not own the packet, but @@ -349,7 +377,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; } @@ -358,7 +386,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; } @@ -379,5 +407,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); } |