Hi,
By now, the mxfenc.c works without exceptions, but there are still some
bugs:
run the cmd:
./ffmpeg -i video.mpg test1.mxf
it will create test1.mxf successfully.
then run some test on it:
1)test1.mxf can be played by GomPlayer and KMPlayer, but can not be played
by VLC :(
2)I use freemxf lib to dump the test1.mxf and get some errors (see
attachment test1.txt).
in order to further debug, I have some questions:
1) how to debug it with mxf.c?
I debug the code by remote control tools on another pc,
run ./ffplay_g test1.mxf, it gives some SDL errors.
Any other command to run mxf demuxer?
2) I added av_log() debug information, and set #define DEBUG on the top of
the file,
but can not find where debug information output, something wrong?
thanks.
--
Best wishes~
Test Program for MXFLib
Partition at 0x00000000 is for BodySID 0x0000
ClosedCompleteHeader
MajorVersion = 1
MinorVersion = 2
KAGSize = 1
ThisPartition = 0
PreviousPartition = 0
FooterPartition = 640231
HeaderByteCount = 1289
IndexByteCount = 0
IndexSID = 0
BodyOffset = 0
BodySID = 0
OperationalPattern = 06 0e 2b 34 04 01 01 01 0d 01 02 01 01 01 01 00
EssenceContainers
EssenceContainer = 06 0e 2b 34 04 01 01 02 0d 01 03 01 02 04 60 01
ERROR: KLV length is 18434 but available data size is only 1270 after reading
0x00000013 of header metadata at 0x0000008c in "test1.mxf"
ERROR: Primer not an integer number of multiples of 18 bytes!
ERROR: Malformed vector header in Primer - number of entries is 70, vector
header claims 32
ERROR: Less than 16-bytes of header metadata available after reading 0x00000507
bytes at 0x00000582 in file "test1.mxf"
Header Metadata:
No index table in this partition
Partition at 0x0009c4e7 is for BodySID 0x0001
CompleteFooter
MajorVersion = 1
MinorVersion = 2
KAGSize = 1
ThisPartition = 640231
PreviousPartition = 0
FooterPartition = 640231
HeaderByteCount = 0
IndexByteCount = 0
IndexSID = 0
BodyOffset = 0
BodySID = 1
OperationalPattern = 06 0e 2b 34 04 01 01 01 0d 01 02 01 01 01 01 00
EssenceContainers
EssenceContainer = 06 0e 2b 34 04 01 01 02 0d 01 03 01 02 04 60 01
No header metadata in this partition
No index table in this partition
Scanned RIP
BodySID 0x0000 is at 0x00000000 type ClosedCompleteHeader
BodySID 0x0001 is at 0x0009c4e7 type CompleteFooter
/*
* MXF muxer
* Copyright (c) 2008 GUCAS, Zhentan Feng<spyfeng at gmail dot com>
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* References
* SMPTE 336M KLV Data Encoding Protocol Using Key-Length-Value
* SMPTE 377M MXF File Format Specifications
* SMPTE 379M MXF Generic Container
* SMPTE 381M Mapping MPEG Streams into the MXF Generic Container
* SMPTE RP210: SMPTE Metadata Dictionary
* SMPTE RP224: Registry of SMPTE Universal Labels
*/
#define DEBUG
#include "libavutil/random.h"
#include "avformat.h"
#include "libavcodec/bytestream.h"
typedef uint8_t UID[16];
typedef uint8_t UMID[32];
enum MXFMetadataSetType {
MaterialPackage,
SourcePackage,
};
typedef struct {
UID key;
offset_t offset;
uint64_t length;
} KLVPacket;
typedef struct {
UID uid;
unsigned matching_len;
enum CodecID id;
} MXFCodecUL;
typedef struct {
int local_tag;
UID uid;
} MXFLocalTagPair;
typedef struct {
UID uid;
enum CodecType type;
} MXFDataDefinitionUL;
typedef struct {
UID uid;
enum CodecID type;
} MXFEssenceElementKey;
typedef struct {
UID *identification;
UID *content_storage;
UID *package;
UID *track;
UID **sequence;
UID **structural_component;
} MXFReferenceContext;
typedef struct MXFContext {
UMID top_src_package_uid;
int64_t header_byte_count;
int64_t header_start;
int64_t header_byte_count_offset;
int64_t header_footer_partition_offset;
AVRandomState random_state;
MXFReferenceContext *reference;
char *track_number_sign;
UID *track_essence_element_key;
int type_num;
const MXFCodecUL *video_container_ul;
const MXFCodecUL *audio_container_ul;
} MXFContext;
static const uint8_t umid_base[] = {0x06, 0x0a, 0x2b, 0x34, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x0f, 0x00, 0x13, 0x00, 0x00, 0x00};
/* complete key */
static const uint8_t op1a_ul[] = { 0x06, 0x0e, 0x2b, 0x34, 0x04,
0x01, 0x01, 0x01, 0x0d, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x00 };
static const uint8_t header_partition_key[] = { 0x06, 0x0e, 0x2b,
0x34, 0x02, 0x05, 0x01, 0x01, 0x0d, 0x01, 0x02, 0x01, 0x01, 0x02, 0x04, 0x00 };
// ClosedComplete
static const uint8_t footer_partition_key[] = {0x06, 0x0e, 0x2b, 0x34, 0x02,
0x05, 0x01, 0x01, 0x0d, 0x01, 0x02, 0x01, 0x01, 0x04, 0x04, 0x00}; //
ClosedComplete
static const uint8_t primer_pack_key[] = {
0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x05,0x01,0x00
};
static const MXFEssenceElementKey mxf_essence_element_key[] = {
{ {
0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x01,0x0d,0x01,0x03,0x01,0x15,0x01,0x05,0x00},
CODEC_ID_MPEG2VIDEO},
{ {
0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x01,0x0d,0x01,0x03,0x01,0x16,0x01,0x01,0x00},
CODEC_ID_PCM_S16LE},
{ {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
CODEC_ID_NONE},
};
/* partial key for header metadata */
static const uint8_t header_metadata_key[] =
{0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01};
/* SMPTE RP224 http://www.smpte-ra.org/mdd/index.html */
static const MXFDataDefinitionUL mxf_data_definition_uls[] = {
{ {
0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x01,0x03,0x02,0x02,0x01,0x00,0x00,0x00
}, CODEC_TYPE_VIDEO },
{ {
0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x01,0x03,0x02,0x02,0x02,0x00,0x00,0x00
}, CODEC_TYPE_AUDIO },
{ {
0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x05,0x01,0x03,0x02,0x02,0x02,0x02,0x00,0x00
}, CODEC_TYPE_AUDIO },
{ {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
}, CODEC_TYPE_DATA },
};
static const MXFCodecUL mxf_picture_essence_container_uls[] = {
{ {
0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x02,0x0D,0x01,0x03,0x01,0x02,0x04,0x60,0x01
}, 14, CODEC_ID_MPEG2VIDEO }, /* MPEG-ES Frame wrapped */
// { {
0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x02,0x41,0x01
}, 14, CODEC_ID_DVVIDEO }, /* DV 625 25mbps */
{ {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
}, 0, CODEC_ID_NONE },
};
static const MXFCodecUL mxf_sound_essence_container_uls[] = {
{ {
0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x06,0x01,0x00
}, 14, CODEC_ID_PCM_S16LE }, /* BWF Frame wrapped */
// { {
0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x02,0x0D,0x01,0x03,0x01,0x02,0x04,0x40,0x01
}, 14, CODEC_ID_MP2 }, /* MPEG-ES Frame wrapped, 0x40 ??? stream id */
// { {
0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x01,0x01
}, 14, CODEC_ID_PCM_S16LE }, /* D-10 Mapping 50Mbps PAL Extended Template */
{ {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
}, 0, CODEC_ID_NONE },
};
/* SMPTE RP210 http://www.smpte-ra.org/mdd/index.html */
static const MXFLocalTagPair mxf_local_tag_batch[] = {
// preface set
{ 0x3C0A,
{0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x01,0x01,0x15,0x02,0x00,0x00,0x00,0x00}},
/* Instance UID */
{ 0x3B02,
{0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x07,0x02,0x01,0x10,0x02,0x04,0x00,0x00}},
/* Last Modified Date */
{ 0x3B05,
{0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x03,0x01,0x02,0x01,0x05,0x00,0x00,0x00}},
/* Version */
{ 0x3B06,
{0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x04,0x06,0x04,0x00,0x00}},
/* Identifications reference */
{ 0x3B03,
{0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x04,0x02,0x01,0x00,0x00}},
/* Content Storage reference */
{ 0x3B09,
{0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x01,0x02,0x02,0x03,0x00,0x00,0x00,0x00}},
/* Operational Pattern UL */
{ 0x3B0A,
{0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x01,0x02,0x02,0x10,0x02,0x01,0x00,0x00}},
/* Essence Containers UL batch */
{ 0x3B0B,
{0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x01,0x02,0x02,0x10,0x02,0x02,0x00,0x00}},
/* DM Schemes UL batch */
// Identification
{ 0x3C09,
{0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x05,0x20,0x07,0x01,0x01,0x00,0x00,0x00}},
/* This Generation UID */
{ 0x3C01,
{0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x05,0x20,0x07,0x01,0x02,0x01,0x00,0x00}},
/* Company Name */
{ 0x3C02,
{0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x05,0x20,0x07,0x01,0x03,0x01,0x00,0x00}},
/* Product Name */
{ 0x3C04,
{0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x05,0x20,0x07,0x01,0x04,0x00,0x00,0x00}},
/* Version String */
{ 0x3C05,
{0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x05,0x20,0x07,0x01,0x07,0x00,0x00,0x00}},
/* Product ID */
{ 0x3C06,
{0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x07,0x02,0x01,0x10,0x02,0x03,0x00,0x00}},
/* Modification Date */
// Content Storage
{ 0x1901,
{0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x04,0x05,0x01,0x00,0x00}},
/* Package strong reference batch */
// Essence Container Data
{ 0x2701,
{0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x06,0x01,0x00,0x00,0x00}},
/* Linked Package UID */
{ 0x3F07,
{0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x04,0x01,0x03,0x04,0x04,0x00,0x00,0x00,0x00}},
/* BodySID */
// Package
{ 0x4401,
{0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x01,0x01,0x15,0x10,0x00,0x00,0x00,0x00}},
/* Package UID */
{ 0x4405,
{0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x07,0x02,0x01,0x10,0x01,0x03,0x00,0x00}},
/* Package Creation Date */
{ 0x4404,
{0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x07,0x02,0x01,0x10,0x02,0x05,0x00,0x00}},
/* Package Modified Date */
{ 0x4403,
{0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x04,0x06,0x05,0x00,0x00}},
/* Tracks Strong reference array */
// Track
{ 0x4801,
{0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x01,0x07,0x01,0x01,0x00,0x00,0x00,0x00}},
/* Track ID */
{ 0x4804,
{0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x04,0x01,0x03,0x00,0x00}},
/* Track Numberr */
{ 0x4B01,
{0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x05,0x30,0x04,0x05,0x00,0x00,0x00,0x00}},
/* Edit Rate */
{ 0x4B02,
{0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x07,0x02,0x01,0x03,0x01,0x03,0x00,0x00}},
/* Origin */
{ 0x4803,
{0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x04,0x02,0x04,0x00,0x00}},
/* Sequence reference */
// Sequence
{ 0x0201,
{0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x04,0x07,0x01,0x00,0x00,0x00,0x00,0x00}},
/* Data Definition UL */
{ 0x0202,
{0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x07,0x02,0x02,0x01,0x01,0x03,0x00,0x00}},
/* Duration */
{ 0x1001,
{0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x04,0x06,0x09,0x00,0x00}},
/* Structural Components reference array */
// Source Clip
{ 0x1201,
{0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x07,0x02,0x01,0x03,0x01,0x0A,0x00,0x00}},
/* Start position */
{ 0x1101,
{0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x03,0x01,0x00,0x00,0x00}},
/* SourcePackageID */
{ 0x1102,
{0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x06,0x01,0x01,0x03,0x02,0x00,0x00,0x00}},
/* SourceTrackID */
};
#define PRINT_KEY(pc, s, x) dprintf(pc, "%s %02X %02X %02X %02X %02X %02X %02X
%02X %02X %02X %02X %02X %02X %02X %02X %02X\n", s, \
(x)[0], (x)[1], (x)[2], (x)[3], (x)[4], (x)[5],
(x)[6], (x)[7], (x)[8], (x)[9], (x)[10], (x)[11], (x)[12], (x)[13], (x)[14],
(x)[15])
static void mxf_generate_uuid(AVFormatContext *s, UID uuid)
{
MXFContext *mxf = s->priv_data;
int rand_num, i;
for (i = 0; i < 16; i++) {
rand_num = av_random(&mxf->random_state);
rand_num = rand_num & 0x00ff;
// the 7th byte is version according to ISO 11578
if (i == 6) {
rand_num &= 0x0f;
rand_num |= 0x40;
}
// the 8th byte is variant for current use according to ISO 11578
if (i == 8) {
rand_num &= 0x3f;
rand_num |= 0x80;
}
uuid[i] = rand_num;
}
}
static void mxf_generate_umid(AVFormatContext *s, UMID umid)
{
memcpy(umid, umid_base, 16);
mxf_generate_uuid(s, umid + 16);
}
static int mxf_generate_reference(AVFormatContext *s, UID **refs, int ref_count)
{
int i;
UID *p;
*refs = av_mallocz(ref_count * sizeof(UID));
if (!refs)
return -1;
p = *refs;
for (i = 0; i < ref_count; i++) {
mxf_generate_uuid(s, *p);
p += 16;
}
p = 0;
return 0;
}
static int klv_encode_ber_length(ByteIOContext *pb, uint64_t len)
{
// Determine the best BER size
int size = 0, i;
uint64_t tmp = len;
if (len < 128) {
//short form
size = 1;
put_byte(pb, len);
return size;
}
while (tmp) {
tmp >>= 8;
size ++;
}
// long form
put_byte(pb, 0x80 + size);
i = size;
while(i) {
put_byte(pb, len & 0xff);
len >>= 8;
i--;
}
return size;
}
static int mxf_write_primer_pack(AVFormatContext *s)
{
ByteIOContext *pb = s->pb;
const MXFLocalTagPair *local_tag_batch;
int i,local_tag_number = 0;
local_tag_number = sizeof(mxf_local_tag_batch) / sizeof(MXFLocalTagPair);
put_buffer(pb, primer_pack_key, 16);
klv_encode_ber_length(pb, local_tag_number * 18 + 8);
put_be32(pb, local_tag_number); // local_tag num
put_be32(pb, 18); // item size, always 18 according to the specs
for (local_tag_batch = mxf_local_tag_batch; i < local_tag_number;
local_tag_batch++, i++) {
put_be16(pb, local_tag_batch->local_tag);
put_buffer(pb, local_tag_batch->uid, 16);
}
return 0;
}
static int mxf_write_local_tag(ByteIOContext *pb, int value_size, int tag)
{
put_be16(pb, tag);
put_be16(pb, value_size);
return 0;
}
static int mxf_write_reference(ByteIOContext *pb, int ref_count, UID *value)
{
put_be32(pb, ref_count);
put_be32(pb, 16);
put_buffer(pb, *value, 16 * ref_count);
return 0;
}
static int utf8len(const uint8_t *b){
int len=0;
int val;
while(*b){
GET_UTF8(val, *b++, return -1;)
len++;
}
return len;
}
static void mxf_free(AVFormatContext *s)
{
MXFContext *mxf = s->priv_data;
int i;
av_freep(&mxf->reference->identification);
av_freep(&mxf->reference->content_storage);
av_freep(&mxf->reference->package);
av_freep(&mxf->reference->track);
for (i = 0; i < s->nb_streams; i++) {
av_freep(&mxf->reference->sequence[i]);
av_freep(&mxf->reference->structural_component[i]);
}
av_freep(&mxf->reference->sequence);
av_freep(&mxf->reference->structural_component);
av_freep(&mxf->reference);
av_freep(&mxf->track_essence_element_key);
av_freep(&mxf->track_number_sign);
}
static const MXFDataDefinitionUL *mxf_get_data_definition_ul(const
MXFDataDefinitionUL *uls, enum CodecType type)
{
while (uls->type != CODEC_TYPE_DATA) {
if (type == uls->type)
break;
uls ++;
}
return uls;
}
static int mxf_write_preface(AVFormatContext *s, KLVPacket *klv)
{
MXFContext *mxf = s->priv_data;
MXFReferenceContext *refs = mxf->reference;
UID uid;
ByteIOContext *pb = s->pb;
klv->key[13] = 0x01;
klv->key[14] = 0x2f;
klv->key[15] = 0x00;
put_buffer(pb, klv->key, 16);
klv_encode_ber_length(pb, 146);
// write preface set uid
mxf_generate_uuid(s, uid);
mxf_write_local_tag(pb, 16, 0x3C0A);
put_buffer(pb, uid, 16);
#ifdef DEBUG
PRINT_KEY(s, "preface uid", uid);
#endif
// write create date as unknown
mxf_write_local_tag(pb, 8, 0x3B02);
put_be64(pb, 0);
// write version
mxf_write_local_tag(pb, 2, 0x3B05);
put_be16(pb, 1);
// write identification_refs
if (mxf_generate_reference(s, &refs->identification, 1) < 0)
return -1;
mxf_write_local_tag(pb, 16 + 8, 0x3B06);
mxf_write_reference(pb, 1, refs->identification);
// write content_storage_refs
if (mxf_generate_reference(s, &refs->content_storage, 1) < 0)
return -1;
mxf_write_local_tag(pb, 16, 0x3B03);
put_buffer(pb, *refs->content_storage, 16);
mxf_write_local_tag(pb, 16, 0x3B09);
put_buffer(pb, op1a_ul, 16);
// write essence_container_refs
mxf_write_local_tag(pb, 8 + 16 * mxf->type_num, 0x3B0A);
put_be32(pb,mxf->type_num);
put_be32(pb,16);
if (mxf->video_container_ul != 0)
put_buffer(pb, mxf->video_container_ul->uid, 16);
if (mxf->audio_container_ul != 0)
put_buffer(pb, mxf->audio_container_ul->uid, 16);
// write dm_scheme_refs
mxf_write_local_tag(pb, 8, 0x3B0B);
put_be64(pb, 0);
return 0;
}
static int mxf_write_identification(AVFormatContext *s, KLVPacket *klv)
{
MXFContext *mxf = s->priv_data;
MXFReferenceContext *refs = mxf->reference;
ByteIOContext *pb = s->pb;
UID uid;
int length, company_name_len, product_name_len, version_string_len;
klv->key[13] = 0x01;
klv->key[14] = 0x30;
klv->key[15] = 0x00;
put_buffer(pb, klv->key, 16);
company_name_len = utf8len("FFmpeg") + 1;
product_name_len = utf8len("OP1a Muxer") + 1;
version_string_len = utf8len("version 0.0.1") + 1;
length = 84 + company_name_len + product_name_len + version_string_len;
klv_encode_ber_length(pb, length);
// write uid
mxf_write_local_tag(pb, 16, 0x3C0A);
put_buffer(pb, *refs->identification, 16);
// write generation uid
mxf_generate_uuid(s, uid);
mxf_write_local_tag(pb, 16, 0x3C09);
put_buffer(pb, uid, 16);
mxf_write_local_tag(pb, company_name_len, 0x3C01);
put_buffer(pb, "FFmpeg", company_name_len);
mxf_write_local_tag(pb, product_name_len, 0x3C02);
put_buffer(pb, "OP1a Muxer", product_name_len);
mxf_write_local_tag(pb, version_string_len, 0x3C04);
put_buffer(pb, "version 0.0.1", version_string_len);
// write product uid
mxf_generate_uuid(s, uid);
mxf_write_local_tag(pb, 16, 0x3C05);
put_buffer(pb, uid, 16);
// write modified date
mxf_write_local_tag(pb, 8, 0x3C06);
put_be64(pb, 0);
return 0;
}
static int mxf_write_content_storage(AVFormatContext *s, KLVPacket *klv)
{
MXFContext *mxf = s->priv_data;
MXFReferenceContext *refs = mxf->reference;
ByteIOContext *pb = s->pb;
klv->key[13] = 0x01;
klv->key[14] = 0x18;
klv->key[15] = 0x00;
put_buffer(pb, klv->key, 16);
klv_encode_ber_length(pb, 64);
// write uid
mxf_write_local_tag(pb, 16, 0x3C0A);
put_buffer(pb, *refs->content_storage, 16);
// write package reference
if (mxf_generate_reference(s, &refs->package, 2) < 0)
return -1;
mxf_write_local_tag(pb, 16 * 2 + 8, 0x1901);
mxf_write_reference(pb, 2, refs->package);
return 0;
}
static int mxf_write_package(AVFormatContext *s, KLVPacket *klv, enum
MXFMetadataSetType type)
{
MXFContext *mxf = s->priv_data;
MXFReferenceContext *refs = mxf->reference;
ByteIOContext *pb = s->pb;
UMID umid;
UID *ref;
klv->key[13] = 0x01;
klv->key[14] = type == MaterialPackage ? 0x36 : 0x37;
klv->key[15] = 0x00;
put_buffer(pb, klv->key, 16);
klv_encode_ber_length(pb, 92 + 16 * s->nb_streams);
// write uid
ref = &refs->package[type == SourcePackage];
mxf_write_local_tag(pb, 16, 0x3C0A);
put_buffer(pb, *ref, 16);
// write package umid
mxf_write_local_tag(pb, 32, 0x4401);
if (type == MaterialPackage) {
mxf_generate_umid(s, umid);
put_buffer(pb, umid, 32);
} else {
put_buffer(pb, mxf->top_src_package_uid, 32);
}
// write create date
mxf_write_local_tag(pb, 8, 0x4405);
put_be64(pb, 0);
// write modified date
mxf_write_local_tag(pb, 8, 0x4404);
put_be64(pb, 0);
// write track refs
if (mxf_generate_reference(s, &refs->track, s->nb_streams) < 0)
return -1;
mxf_write_local_tag(pb, s->nb_streams * 16 + 8, 0x4403);
mxf_write_reference(pb, s->nb_streams, refs->track);
// every track have 1 sequence and 1 structural componet, malloc memory for
the refs pointer
refs->sequence = av_mallocz(s->nb_streams * sizeof(*refs->sequence));
if (!refs->sequence)
return -1;
refs->structural_component = av_mallocz(s->nb_streams *
sizeof(*refs->structural_component));
if (!refs->structural_component)
return -1;
// malloc memory for track number sign
if (type == SourcePackage) {
mxf->track_number_sign =
av_mallocz(sizeof(mxf_essence_element_key)/sizeof(MXFEssenceElementKey));
if (!mxf->track_number_sign)
return -1;
}
// malloc memory for essence element key of each track
mxf->track_essence_element_key = av_mallocz(s->nb_streams * sizeof(UID));
if (!mxf->track_essence_element_key)
return -1;
return 0;
}
static int mxf_write_track(AVFormatContext *s, KLVPacket *klv, int
stream_index, enum MXFMetadataSetType type)
{
MXFContext *mxf = s->priv_data;
MXFReferenceContext *refs = mxf->reference;
ByteIOContext *pb = s->pb;
AVStream *st;
const MXFEssenceElementKey *element;
int i = 0;
klv->key[13] = 0x01;
klv->key[14] = 0x3b;
klv->key[15] = 0x00;
put_buffer(pb, klv->key, 16);
klv_encode_ber_length(pb, 80);
st = s->streams[stream_index];
// set pts information
if (st->codec->codec_type == CODEC_TYPE_VIDEO) {
av_set_pts_info(st, 64, 1, st->codec->time_base.den);
} else if (st->codec->codec_type == CODEC_TYPE_AUDIO) {
av_set_pts_info(st, 64, 1, st->codec->sample_rate);
}
// write track uid
mxf_write_local_tag(pb, 16, 0x3C0A);
put_buffer(pb, refs->track[stream_index], 16);
// write track id
mxf_write_local_tag(pb, 4, 0x4801);
put_be32(pb, stream_index + 1);
if (type != MaterialPackage) {
for (element = mxf_essence_element_key; element->type != CODEC_ID_NONE;
element++) {
if (st->codec->codec_id== element->type) {
// write track number
mxf_write_local_tag(pb, 4, 0x4804);
put_buffer(pb, element->uid + 12, 3);
put_byte(pb, element->uid[15] + mxf->track_number_sign[i]);
mxf->track_number_sign[i] ++;
// set essence_element key
memcpy(mxf->track_essence_element_key[stream_index],
element->uid, 16);
break;
}
i++;
}
} else {
put_be32(pb, 0); // track number of material package is 0
}
mxf_write_local_tag(pb, 8, 0x4B01);
put_be32(pb, st->time_base.num);
put_be32(pb, st->time_base.den);
// write origin
mxf_write_local_tag(pb, 8, 0x4B02);
put_be64(pb, 0);
// write sequence refs
if (mxf_generate_reference(s, &refs->sequence[stream_index], 1) < 0)
return -1;
mxf_write_local_tag(pb, 16, 0x4803);
put_buffer(pb, *refs->sequence[stream_index], 16);
return 0;
}
static int mxf_write_sequence(AVFormatContext *s, KLVPacket *klv, int
stream_index)
{
MXFContext *mxf = s->priv_data;
MXFReferenceContext *refs = mxf->reference;
ByteIOContext *pb = s->pb;
AVStream *st;
const MXFDataDefinitionUL * data_def_ul;
klv->key[13] = 0x01;
klv->key[14] = 0x0f;
klv->key[15] = 0x00;
put_buffer(pb, klv->key, 16);
klv_encode_ber_length(pb, 80);
st = s->streams[stream_index];
mxf_write_local_tag(pb, 16, 0x3C0A);
put_buffer(pb, *refs->sequence[stream_index], 16);
// find data define uls
data_def_ul = mxf_get_data_definition_ul(mxf_data_definition_uls,
st->codec->codec_type);
mxf_write_local_tag(pb, 16, 0x0201);
put_buffer(pb, data_def_ul->uid, 16);
mxf_write_local_tag(pb, 8, 0x0202);
put_be32(pb, st->duration);
// write structural component
if (mxf_generate_reference(s, &refs->structural_component[stream_index], 1)
< 0)
return -1;
mxf_write_local_tag(pb, 16 + 8, 0x1001);
mxf_write_reference(pb, 1, refs->structural_component[stream_index]);
return 0;
}
static int mxf_write_structural_component(AVFormatContext *s, KLVPacket *klv,
int stream_index, enum MXFMetadataSetType type)
{
MXFContext *mxf = s->priv_data;
MXFReferenceContext *refs = mxf->reference;
ByteIOContext *pb = s->pb;
AVStream *st;
const MXFDataDefinitionUL * data_def_ul;
int i;
klv->key[13] = 0x01;
klv->key[14] = 0x11;
klv->key[15] = 0x00;
put_buffer(pb, klv->key, 16);
klv_encode_ber_length(pb, 90);
st = s->streams[stream_index];
// write uid
mxf_write_local_tag(pb, 16, 0x3C0A);
put_buffer(pb, *refs->structural_component[stream_index], 16);
data_def_ul = mxf_get_data_definition_ul(mxf_data_definition_uls,
st->codec->codec_type);
mxf_write_local_tag(pb, 16, 0x0201);
put_buffer(pb, data_def_ul->uid, 16);
// write start_position
mxf_write_local_tag(pb, 8, 0x1201);
put_be64(pb, 0);
// write duration
mxf_write_local_tag(pb, 8, 0x0202);
put_be64(pb, st->duration);
if (type == SourcePackage) {
// write source package uid, end of the reference
mxf_write_local_tag(pb, 32, 0x1101);
for (i = 0; i < 4; i++) {
put_be64(pb, 0);
}
// write source track id
mxf_write_local_tag(pb, 4, 0x1102);
put_be64(pb, 0);
} else {
mxf_write_local_tag(pb, 32, 0x1101);
put_buffer(pb, mxf->top_src_package_uid, 32);
mxf_write_local_tag(pb, 4, 0x1102);
put_be64(pb, stream_index + 1);
}
return 0;
}
static int mxf_build_structural_metadata(AVFormatContext *s, KLVPacket* klv,
enum MXFMetadataSetType type)
{
int i;
if (mxf_write_package(s, klv, type) < 0)
return -1;
for (i = 0;i < s->nb_streams; i++) {
if (mxf_write_track(s, klv, i, type) < 0)
return -1;
if (mxf_write_sequence(s, klv, i) < 0)
return -1;
if (mxf_write_structural_component(s, klv, i, type) < 0)
return -1;
}
return 0;
}
static int mxf_write_header_metadata_sets(AVFormatContext *s)
{
KLVPacket klv;
memcpy(klv.key, header_metadata_key, 13);
if (mxf_write_preface(s, &klv) < 0)
return -1;
if (mxf_write_identification(s,&klv) < 0)
return -1;
if (mxf_write_content_storage(s, &klv) < 0)
return -1;
if (mxf_build_structural_metadata(s, &klv, MaterialPackage) < 0)
return -1;
if (mxf_build_structural_metadata(s, &klv, SourcePackage) < 0)
return -1;
return 0;
}
static const MXFCodecUL *mxf_get_essence_container_ul(const MXFCodecUL *uls,
enum CodecID type)
{
while (uls->id != CODEC_ID_NONE) {
if (uls->id == type)
break;
uls++;
}
return uls;
}
static void mxf_set_essence_number(AVFormatContext *s)
{
MXFContext *mxf = s->priv_data;
AVStream *st;
int i, video_type = 0, audio_type = 0;
for (i = 0; i < s->nb_streams; i++) {
st = s->streams[i];
if (!video_type && st->codec->codec_type == CODEC_TYPE_VIDEO) {
mxf->video_container_ul =
mxf_get_essence_container_ul(mxf_picture_essence_container_uls,
st->codec->codec_id);
video_type++;
}
if (!audio_type && st->codec->codec_type == CODEC_TYPE_AUDIO) {
mxf->audio_container_ul =
mxf_get_essence_container_ul(mxf_sound_essence_container_uls,
st->codec->codec_id);
audio_type++;
}
if (video_type && audio_type)
break;
}
mxf->type_num = video_type + audio_type;
}
static void mxf_write_partition(AVFormatContext *s, int64_t this_partition, int
bodysid, const uint8_t *key)
{
MXFContext *mxf = s->priv_data;
ByteIOContext *pb = s->pb;
// write klv
put_buffer(pb, key, 16);
klv_encode_ber_length(pb, 88 + 16 * mxf->type_num);
// write partition value
put_be16(pb, 1); // majorVersion
put_be16(pb, 2); // minorVersion
put_be32(pb, 1); // kagSize
put_be64(pb, this_partition); // thisPartition
put_be64(pb, 0); // previousPartition
// set offset
if (!this_partition)
mxf->header_footer_partition_offset = url_ftell(pb);
put_be64(pb, this_partition); // footerPartition,update later
// set offset
if (!this_partition)
mxf->header_byte_count_offset = url_ftell(pb);
put_be64(pb, 0); // headerByteCount, update later
// no indexTable
put_be64(pb, 0); // indexByteCount
put_be32(pb, 0); // indexSID
put_be64(pb, 0); // bodyOffset
put_be32(pb, bodysid); // bodySID
put_buffer(pb, op1a_ul, 16); // operational pattern
// essence container
put_be32(pb,mxf->type_num);
put_be32(pb,16);
if (mxf->video_container_ul != 0)
put_buffer(pb, mxf->video_container_ul->uid, 16);
if (mxf->audio_container_ul != 0)
put_buffer(pb, mxf->audio_container_ul->uid, 16);
}
static int mux_write_header(AVFormatContext *s)
{
MXFContext *mxf = s->priv_data;
ByteIOContext *pb = s->pb;
int64_t header_metadata_start;
av_init_random(0xbeefdead, &mxf->random_state);
// intial MXFReferenceContext
mxf->reference = av_mallocz(sizeof(MXFReferenceContext));
if (!mxf->reference)
goto fail;
// mark the header start position, for some fields update later
mxf->header_start = url_ftell(pb);
// calculate the numner of essence container type
mxf_set_essence_number(s);
mxf_write_partition(s, 0, 0, header_partition_key);
// generate Source Package Set UMID for op1a
// will be used by
material_package->source_track->sequence->structual_component->source_package_id
mxf_generate_umid(s, mxf->top_src_package_uid);
// mark the start of the headermetadata and calculate metadata size
header_metadata_start = url_ftell(s->pb);
mxf_write_primer_pack(s);
if (mxf_write_header_metadata_sets(s) < 0)
goto fail;
mxf->header_byte_count = url_ftell(s->pb) - header_metadata_start;
put_flush_packet(pb);
return 0;
fail:
mxf_free(s);
return -1;
}
static int mux_write_packet(AVFormatContext *s, AVPacket *pkt)
{
MXFContext *mxf = s->priv_data;
ByteIOContext *pb = s->pb;
put_buffer(pb, mxf->track_essence_element_key[pkt->stream_index], 16); //
write key
klv_encode_ber_length(pb, pkt->size); // write length
put_buffer(pb, pkt->data, pkt->size); // write value
put_flush_packet(pb);
return 0;
}
static int mxf_update_header_partition(AVFormatContext *s, int64_t
footer_partition_offset)
{
MXFContext *mxf = s->priv_data;
ByteIOContext *pb = s->pb;
url_fseek(pb, mxf->header_byte_count_offset, SEEK_SET);
put_be64(pb, mxf->header_byte_count);
put_flush_packet(pb);
url_fseek(pb, mxf->header_footer_partition_offset, SEEK_SET);
put_be64(pb, footer_partition_offset);
put_flush_packet(pb);
return 0;
}
static int mux_write_footer(AVFormatContext *s)
{
MXFContext *mxf = s->priv_data;
ByteIOContext *pb = s->pb;
int64_t this_partition = url_ftell(pb) - mxf->header_start;
mxf_write_partition(s, this_partition, 1, footer_partition_key);
put_flush_packet(pb);
mxf_update_header_partition(s, this_partition);
mxf_free(s);
return 0;
}
AVOutputFormat mxf_muxer = {
"mxf",
NULL_IF_CONFIG_SMALL("Material eXchange Format"),
NULL,
"mxf",
sizeof(MXFContext),
CODEC_ID_PCM_S16LE,
CODEC_ID_MPEG2VIDEO,
mux_write_header,
mux_write_packet,
mux_write_footer,
};
_______________________________________________
FFmpeg-soc mailing list
[email protected]
https://lists.mplayerhq.hu/mailman/listinfo/ffmpeg-soc