avformat/movenc: creating producer reference time (PRFT) box
The producer reference time box supplies relative wall-clock times at which movie fragments, or files containing movie fragments (such as segments) were produced. The box is mainly useful in live streaming use cases. A media player can parse the box and utilize the time fields to measure and improve the latency during real time playout.
This commit is contained in:
committed by
Karthick Jeyapal
parent
f09635f2a2
commit
5717cd80dc
@@ -1344,6 +1344,16 @@ be negative. This enables the initial sample to have DTS/CTS of zero, and
|
|||||||
reduces the need for edit lists for some cases such as video tracks with
|
reduces the need for edit lists for some cases such as video tracks with
|
||||||
B-frames. Additionally, eases conformance with the DASH-IF interoperability
|
B-frames. Additionally, eases conformance with the DASH-IF interoperability
|
||||||
guidelines.
|
guidelines.
|
||||||
|
@item -write_prft
|
||||||
|
Write producer time reference box (PRFT) with a specified time source for the
|
||||||
|
NTP field in the PRFT box. Set value as @samp{wallclock} to specify timesource
|
||||||
|
as wallclock time and @samp{pts} to specify timesource as input packets' PTS
|
||||||
|
values.
|
||||||
|
|
||||||
|
Setting value to @samp{pts} is applicable only for a live encoding use case,
|
||||||
|
where PTS values are set as as wallclock time at the source. For example, an
|
||||||
|
encoding use case with decklink capture source where @option{video_pts} and
|
||||||
|
@option{audio_pts} are set to @samp{abs_wallclock}.
|
||||||
@end table
|
@end table
|
||||||
|
|
||||||
@subsection Example
|
@subsection Example
|
||||||
|
|||||||
@@ -98,6 +98,9 @@ static const AVOption options[] = {
|
|||||||
{ "encryption_kid", "The media encryption key identifier (hex)", offsetof(MOVMuxContext, encryption_kid), AV_OPT_TYPE_BINARY, .flags = AV_OPT_FLAG_ENCODING_PARAM },
|
{ "encryption_kid", "The media encryption key identifier (hex)", offsetof(MOVMuxContext, encryption_kid), AV_OPT_TYPE_BINARY, .flags = AV_OPT_FLAG_ENCODING_PARAM },
|
||||||
{ "use_stream_ids_as_track_ids", "use stream ids as track ids", offsetof(MOVMuxContext, use_stream_ids_as_track_ids), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM},
|
{ "use_stream_ids_as_track_ids", "use stream ids as track ids", offsetof(MOVMuxContext, use_stream_ids_as_track_ids), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM},
|
||||||
{ "write_tmcd", "force or disable writing tmcd", offsetof(MOVMuxContext, write_tmcd), AV_OPT_TYPE_BOOL, {.i64 = -1}, -1, 1, AV_OPT_FLAG_ENCODING_PARAM},
|
{ "write_tmcd", "force or disable writing tmcd", offsetof(MOVMuxContext, write_tmcd), AV_OPT_TYPE_BOOL, {.i64 = -1}, -1, 1, AV_OPT_FLAG_ENCODING_PARAM},
|
||||||
|
{ "write_prft", "Write producer reference time box with specified time source", offsetof(MOVMuxContext, write_prft), AV_OPT_TYPE_INT, {.i64 = MOV_PRFT_NONE}, 0, MOV_PRFT_NB-1, AV_OPT_FLAG_ENCODING_PARAM, "prft"},
|
||||||
|
{ "wallclock", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = MOV_PRFT_SRC_WALLCLOCK}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM, "prft"},
|
||||||
|
{ "pts", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = MOV_PRFT_SRC_PTS}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM, "prft"},
|
||||||
{ NULL },
|
{ NULL },
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -4514,6 +4517,49 @@ static int mov_write_sidx_tags(AVIOContext *pb, MOVMuxContext *mov,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mov_write_prft_tag(AVIOContext *pb, MOVMuxContext *mov, int tracks)
|
||||||
|
{
|
||||||
|
int64_t pos = avio_tell(pb), pts_us, ntp_ts;
|
||||||
|
MOVTrack *first_track;
|
||||||
|
|
||||||
|
/* PRFT should be associated with at most one track. So, choosing only the
|
||||||
|
* first track. */
|
||||||
|
if (tracks > 0)
|
||||||
|
return 0;
|
||||||
|
first_track = &(mov->tracks[0]);
|
||||||
|
|
||||||
|
if (!first_track->entry) {
|
||||||
|
av_log(mov->fc, AV_LOG_WARNING, "Unable to write PRFT, no entries in the track\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (first_track->cluster[0].pts == AV_NOPTS_VALUE) {
|
||||||
|
av_log(mov->fc, AV_LOG_WARNING, "Unable to write PRFT, first PTS is invalid\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mov->write_prft == MOV_PRFT_SRC_WALLCLOCK) {
|
||||||
|
ntp_ts = ff_get_formatted_ntp_time(ff_ntp_time());
|
||||||
|
} else if (mov->write_prft == MOV_PRFT_SRC_PTS) {
|
||||||
|
pts_us = av_rescale_q(first_track->cluster[0].pts,
|
||||||
|
first_track->st->time_base, AV_TIME_BASE_Q);
|
||||||
|
ntp_ts = ff_get_formatted_ntp_time(pts_us + NTP_OFFSET_US);
|
||||||
|
} else {
|
||||||
|
av_log(mov->fc, AV_LOG_WARNING, "Unsupported PRFT box configuration: %d\n",
|
||||||
|
mov->write_prft);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
avio_wb32(pb, 0); // Size place holder
|
||||||
|
ffio_wfourcc(pb, "prft"); // Type
|
||||||
|
avio_w8(pb, 1); // Version
|
||||||
|
avio_wb24(pb, 0); // Flags
|
||||||
|
avio_wb32(pb, first_track->track_id); // reference track ID
|
||||||
|
avio_wb64(pb, ntp_ts); // NTP time stamp
|
||||||
|
avio_wb64(pb, first_track->cluster[0].pts); //media time
|
||||||
|
return update_size(pb, pos);
|
||||||
|
}
|
||||||
|
|
||||||
static int mov_write_moof_tag(AVIOContext *pb, MOVMuxContext *mov, int tracks,
|
static int mov_write_moof_tag(AVIOContext *pb, MOVMuxContext *mov, int tracks,
|
||||||
int64_t mdat_size)
|
int64_t mdat_size)
|
||||||
{
|
{
|
||||||
@@ -4528,6 +4574,9 @@ static int mov_write_moof_tag(AVIOContext *pb, MOVMuxContext *mov, int tracks,
|
|||||||
if (mov->flags & FF_MOV_FLAG_DASH && !(mov->flags & FF_MOV_FLAG_GLOBAL_SIDX))
|
if (mov->flags & FF_MOV_FLAG_DASH && !(mov->flags & FF_MOV_FLAG_GLOBAL_SIDX))
|
||||||
mov_write_sidx_tags(pb, mov, tracks, moof_size + 8 + mdat_size);
|
mov_write_sidx_tags(pb, mov, tracks, moof_size + 8 + mdat_size);
|
||||||
|
|
||||||
|
if (mov->write_prft > MOV_PRFT_NONE && mov->write_prft < MOV_PRFT_NB)
|
||||||
|
mov_write_prft_tag(pb, mov, tracks);
|
||||||
|
|
||||||
if (mov->flags & FF_MOV_FLAG_GLOBAL_SIDX ||
|
if (mov->flags & FF_MOV_FLAG_GLOBAL_SIDX ||
|
||||||
!(mov->flags & FF_MOV_FLAG_SKIP_TRAILER) ||
|
!(mov->flags & FF_MOV_FLAG_SKIP_TRAILER) ||
|
||||||
mov->ism_lookahead) {
|
mov->ism_lookahead) {
|
||||||
@@ -5317,6 +5366,7 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt)
|
|||||||
trk->cluster[trk->entry].size = size;
|
trk->cluster[trk->entry].size = size;
|
||||||
trk->cluster[trk->entry].entries = samples_in_chunk;
|
trk->cluster[trk->entry].entries = samples_in_chunk;
|
||||||
trk->cluster[trk->entry].dts = pkt->dts;
|
trk->cluster[trk->entry].dts = pkt->dts;
|
||||||
|
trk->cluster[trk->entry].pts = pkt->pts;
|
||||||
if (!trk->entry && trk->start_dts != AV_NOPTS_VALUE) {
|
if (!trk->entry && trk->start_dts != AV_NOPTS_VALUE) {
|
||||||
if (!trk->frag_discont) {
|
if (!trk->frag_discont) {
|
||||||
/* First packet of a new fragment. We already wrote the duration
|
/* First packet of a new fragment. We already wrote the duration
|
||||||
|
|||||||
@@ -46,6 +46,7 @@
|
|||||||
typedef struct MOVIentry {
|
typedef struct MOVIentry {
|
||||||
uint64_t pos;
|
uint64_t pos;
|
||||||
int64_t dts;
|
int64_t dts;
|
||||||
|
int64_t pts;
|
||||||
unsigned int size;
|
unsigned int size;
|
||||||
unsigned int samples_in_chunk;
|
unsigned int samples_in_chunk;
|
||||||
unsigned int chunkNum; ///< Chunk number if the current entry is a chunk start otherwise 0
|
unsigned int chunkNum; ///< Chunk number if the current entry is a chunk start otherwise 0
|
||||||
@@ -169,6 +170,13 @@ typedef enum {
|
|||||||
MOV_ENC_CENC_AES_CTR,
|
MOV_ENC_CENC_AES_CTR,
|
||||||
} MOVEncryptionScheme;
|
} MOVEncryptionScheme;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
MOV_PRFT_NONE = 0,
|
||||||
|
MOV_PRFT_SRC_WALLCLOCK,
|
||||||
|
MOV_PRFT_SRC_PTS,
|
||||||
|
MOV_PRFT_NB
|
||||||
|
} MOVPrftBox;
|
||||||
|
|
||||||
typedef struct MOVMuxContext {
|
typedef struct MOVMuxContext {
|
||||||
const AVClass *av_class;
|
const AVClass *av_class;
|
||||||
int mode;
|
int mode;
|
||||||
@@ -224,6 +232,7 @@ typedef struct MOVMuxContext {
|
|||||||
int use_stream_ids_as_track_ids;
|
int use_stream_ids_as_track_ids;
|
||||||
int track_ids_ok;
|
int track_ids_ok;
|
||||||
int write_tmcd;
|
int write_tmcd;
|
||||||
|
MOVPrftBox write_prft;
|
||||||
} MOVMuxContext;
|
} MOVMuxContext;
|
||||||
|
|
||||||
#define FF_MOV_FLAG_RTP_HINT (1 << 0)
|
#define FF_MOV_FLAG_RTP_HINT (1 << 0)
|
||||||
|
|||||||
Reference in New Issue
Block a user