summaryrefslogtreecommitdiffstats
path: root/common/recorder.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/recorder.c')
-rw-r--r--common/recorder.c102
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);
}