|
|
|
@ -35,6 +35,7 @@ |
|
|
|
|
#include <math.h> |
|
|
|
|
#include <time.h> |
|
|
|
|
|
|
|
|
|
#include "libavutil/opt.h" |
|
|
|
|
#include "libavutil/random_seed.h" |
|
|
|
|
#include "libavcodec/bytestream.h" |
|
|
|
|
#include "libavcodec/timecode.h" |
|
|
|
@ -172,6 +173,7 @@ static const MXFContainerEssenceEntry mxf_essence_container_uls[] = { |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
typedef struct MXFContext { |
|
|
|
|
AVClass *av_class; |
|
|
|
|
int64_t footer_partition_offset; |
|
|
|
|
int essence_container_count; |
|
|
|
|
AVRational time_base; |
|
|
|
@ -185,10 +187,9 @@ typedef struct MXFContext { |
|
|
|
|
unsigned body_partitions_count; |
|
|
|
|
int last_key_index; ///< index of last key frame
|
|
|
|
|
uint64_t duration; |
|
|
|
|
struct ff_timecode tc; |
|
|
|
|
AVStream *timecode_track; |
|
|
|
|
int timecode_base; ///< rounded time code base (25 or 30)
|
|
|
|
|
int timecode_start; ///< frame number computed from mpeg-2 gop header timecode
|
|
|
|
|
int timecode_drop_frame; ///< time code use drop frame method frop mpeg-2 essence gop header
|
|
|
|
|
int edit_unit_byte_count; ///< fixed edit unit byte count
|
|
|
|
|
uint64_t body_offset; |
|
|
|
|
uint32_t instance_number; |
|
|
|
@ -666,7 +667,7 @@ static void mxf_write_timecode_component(AVFormatContext *s, AVStream *st, enum |
|
|
|
|
|
|
|
|
|
// Start Time Code
|
|
|
|
|
mxf_write_local_tag(pb, 8, 0x1501); |
|
|
|
|
avio_wb64(pb, mxf->timecode_start); |
|
|
|
|
avio_wb64(pb, mxf->tc.start); |
|
|
|
|
|
|
|
|
|
// Rounded Time Code Base
|
|
|
|
|
mxf_write_local_tag(pb, 2, 0x1502); |
|
|
|
@ -674,7 +675,7 @@ static void mxf_write_timecode_component(AVFormatContext *s, AVStream *st, enum |
|
|
|
|
|
|
|
|
|
// Drop Frame
|
|
|
|
|
mxf_write_local_tag(pb, 1, 0x1503); |
|
|
|
|
avio_w8(pb, mxf->timecode_drop_frame); |
|
|
|
|
avio_w8(pb, mxf->tc.drop); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void mxf_write_structural_component(AVFormatContext *s, AVStream *st, enum MXFMetadataSetType type) |
|
|
|
@ -1310,7 +1311,6 @@ static int mxf_parse_mpeg2_frame(AVFormatContext *s, AVStream *st, |
|
|
|
|
AVPacket *pkt, MXFIndexEntry *e) |
|
|
|
|
{ |
|
|
|
|
MXFStreamContext *sc = st->priv_data; |
|
|
|
|
MXFContext *mxf = s->priv_data; |
|
|
|
|
uint32_t c = -1; |
|
|
|
|
int i; |
|
|
|
|
|
|
|
|
@ -1330,21 +1330,6 @@ static int mxf_parse_mpeg2_frame(AVFormatContext *s, AVStream *st, |
|
|
|
|
if (e->flags & 0x40) // sequence header present
|
|
|
|
|
e->flags |= 0x80; // random access
|
|
|
|
|
} |
|
|
|
|
if (!mxf->header_written) { |
|
|
|
|
unsigned hours = (pkt->data[i+1]>>2) & 0x1f; |
|
|
|
|
unsigned minutes = ((pkt->data[i+1] & 0x03) << 4) | (pkt->data[i+2]>>4); |
|
|
|
|
unsigned seconds = ((pkt->data[i+2] & 0x07) << 3) | (pkt->data[i+3]>>5); |
|
|
|
|
unsigned frames = ((pkt->data[i+3] & 0x1f) << 1) | (pkt->data[i+4]>>7); |
|
|
|
|
mxf->timecode_drop_frame = !!(pkt->data[i+1] & 0x80); |
|
|
|
|
mxf->timecode_start = (hours*3600 + minutes*60 + seconds) * |
|
|
|
|
mxf->timecode_base + frames; |
|
|
|
|
if (mxf->timecode_drop_frame) { |
|
|
|
|
unsigned tminutes = 60 * hours + minutes; |
|
|
|
|
mxf->timecode_start -= 2 * (tminutes - tminutes / 10); |
|
|
|
|
} |
|
|
|
|
av_log(s, AV_LOG_DEBUG, "frame %d %d:%d:%d%c%d\n", mxf->timecode_start, |
|
|
|
|
hours, minutes, seconds, mxf->timecode_drop_frame ? ';':':', frames); |
|
|
|
|
} |
|
|
|
|
} else if (c == 0x1b3) { // seq
|
|
|
|
|
e->flags |= 0x40; |
|
|
|
|
switch ((pkt->data[i+4]>>4) & 0xf) { |
|
|
|
@ -1440,6 +1425,12 @@ static int mxf_write_header(AVFormatContext *s) |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
av_set_pts_info(st, 64, mxf->time_base.num, mxf->time_base.den); |
|
|
|
|
if (mxf->tc.str) { |
|
|
|
|
mxf->tc.rate.num = mxf->time_base.den; |
|
|
|
|
mxf->tc.rate.den = mxf->time_base.num; |
|
|
|
|
if (ff_init_smtpe_timecode(s, &mxf->tc) < 0) |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
if (s->oformat == &ff_mxf_d10_muxer) { |
|
|
|
|
if (st->codec->bit_rate == 50000000) |
|
|
|
|
if (mxf->time_base.den == 25) sc->index = 3; |
|
|
|
@ -1554,7 +1545,7 @@ static void mxf_write_system_item(AVFormatContext *s) |
|
|
|
|
unsigned frame; |
|
|
|
|
uint32_t time_code; |
|
|
|
|
|
|
|
|
|
frame = mxf->timecode_start + mxf->last_indexed_edit_unit + mxf->edit_units_count; |
|
|
|
|
frame = mxf->tc.start + mxf->last_indexed_edit_unit + mxf->edit_units_count; |
|
|
|
|
|
|
|
|
|
// write system metadata pack
|
|
|
|
|
avio_write(pb, system_metadata_pack_key, 16); |
|
|
|
@ -1576,7 +1567,7 @@ static void mxf_write_system_item(AVFormatContext *s) |
|
|
|
|
|
|
|
|
|
avio_w8(pb, 0x81); // SMPTE 12M time code
|
|
|
|
|
time_code = ff_framenum_to_smtpe_timecode(frame, mxf->timecode_base, |
|
|
|
|
mxf->timecode_drop_frame); |
|
|
|
|
mxf->tc.drop); |
|
|
|
|
avio_wb32(pb, time_code); |
|
|
|
|
avio_wb32(pb, 0); // binary group data
|
|
|
|
|
avio_wb64(pb, 0); |
|
|
|
@ -1873,6 +1864,16 @@ static int mxf_interleave(AVFormatContext *s, AVPacket *out, AVPacket *pkt, int |
|
|
|
|
mxf_interleave_get_packet, mxf_compare_timestamps); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static const AVClass class = { |
|
|
|
|
.class_name = "mxf", |
|
|
|
|
.item_name = av_default_item_name, |
|
|
|
|
.version = LIBAVUTIL_VERSION_INT, |
|
|
|
|
.option = (const AVOption[]){ |
|
|
|
|
{TIMECODE_OPT(MXFContext, AV_OPT_FLAG_ENCODING_PARAM)}, |
|
|
|
|
{NULL} |
|
|
|
|
}, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
AVOutputFormat ff_mxf_muxer = { |
|
|
|
|
.name = "mxf", |
|
|
|
|
.long_name = NULL_IF_CONFIG_SMALL("Material eXchange Format"), |
|
|
|
@ -1886,6 +1887,7 @@ AVOutputFormat ff_mxf_muxer = { |
|
|
|
|
.write_trailer = mxf_write_footer, |
|
|
|
|
.flags = AVFMT_NOTIMESTAMPS, |
|
|
|
|
.interleave_packet = mxf_interleave, |
|
|
|
|
.priv_class = &class, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
AVOutputFormat ff_mxf_d10_muxer = { |
|
|
|
@ -1900,4 +1902,5 @@ AVOutputFormat ff_mxf_d10_muxer = { |
|
|
|
|
.write_trailer = mxf_write_footer, |
|
|
|
|
.flags = AVFMT_NOTIMESTAMPS, |
|
|
|
|
.interleave_packet = mxf_interleave, |
|
|
|
|
.priv_class = &class, |
|
|
|
|
}; |
|
|
|
|