|
|
|
@@ -29,8 +29,30 @@ struct gxf_stream_info { |
|
|
|
int64_t last_field; |
|
|
|
AVRational frames_per_second; |
|
|
|
int32_t fields_per_frame; |
|
|
|
int64_t track_aux_data; |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
|
* @brief parse gxf timecode and add it to metadata |
|
|
|
*/ |
|
|
|
static int add_timecode_metadata(AVDictionary **pm, const char *key, uint32_t timecode, int fields_per_frame) |
|
|
|
{ |
|
|
|
char tmp[128]; |
|
|
|
int field = timecode & 0xff; |
|
|
|
int frame = fields_per_frame ? field / fields_per_frame : field; |
|
|
|
int second = (timecode >> 8) & 0xff; |
|
|
|
int minute = (timecode >> 16) & 0xff; |
|
|
|
int hour = (timecode >> 24) & 0x1f; |
|
|
|
int drop = (timecode >> 29) & 1; |
|
|
|
// bit 30: color_frame, unused |
|
|
|
// ignore invalid time code |
|
|
|
if (timecode >> 31) |
|
|
|
return 0; |
|
|
|
snprintf(tmp, sizeof(tmp), "%02d:%02d:%02d%c%02d", |
|
|
|
hour, minute, second, drop ? ';' : ':', frame); |
|
|
|
return av_dict_set(pm, key, tmp, 0); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* @brief parses a packet header, extracting type and length |
|
|
|
* @param pb AVIOContext to read header from |
|
|
|
@@ -210,6 +232,7 @@ static AVRational fps_umf2avr(uint32_t flags) { |
|
|
|
static void gxf_track_tags(AVIOContext *pb, int *len, struct gxf_stream_info *si) { |
|
|
|
si->frames_per_second = (AVRational){0, 0}; |
|
|
|
si->fields_per_frame = 0; |
|
|
|
si->track_aux_data = 0x80000000; |
|
|
|
while (*len >= 2) { |
|
|
|
GXFTrackTag tag = avio_r8(pb); |
|
|
|
int tlen = avio_r8(pb); |
|
|
|
@@ -223,7 +246,9 @@ static void gxf_track_tags(AVIOContext *pb, int *len, struct gxf_stream_info *si |
|
|
|
si->frames_per_second = fps_tag2avr(value); |
|
|
|
else if (tag == TRACK_FPF && (value == 1 || value == 2)) |
|
|
|
si->fields_per_frame = value; |
|
|
|
} else |
|
|
|
} else if (tlen == 8 && tag == TRACK_AUX) |
|
|
|
si->track_aux_data = avio_rl64(pb); |
|
|
|
else |
|
|
|
avio_skip(pb, tlen); |
|
|
|
} |
|
|
|
} |
|
|
|
@@ -301,8 +326,6 @@ static int gxf_header(AVFormatContext *s, AVFormatParameters *ap) { |
|
|
|
track_id = avio_r8(pb); |
|
|
|
track_len = avio_rb16(pb); |
|
|
|
len -= track_len; |
|
|
|
gxf_track_tags(pb, &track_len, si); |
|
|
|
avio_skip(pb, track_len); |
|
|
|
if (!(track_type & 0x80)) { |
|
|
|
av_log(s, AV_LOG_ERROR, "invalid track type %x\n", track_type); |
|
|
|
continue; |
|
|
|
@@ -313,6 +336,16 @@ static int gxf_header(AVFormatContext *s, AVFormatParameters *ap) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
track_id &= 0x3f; |
|
|
|
gxf_track_tags(pb, &track_len, si); |
|
|
|
// check for timecode tracks |
|
|
|
if (track_type == 7 || track_type == 8 || track_type == 24) { |
|
|
|
add_timecode_metadata(&s->metadata, "gxf_timecode", |
|
|
|
si->track_aux_data & 0xffffffff, |
|
|
|
si->fields_per_frame); |
|
|
|
|
|
|
|
} |
|
|
|
avio_skip(pb, track_len); |
|
|
|
|
|
|
|
idx = get_sindex(s, track_id, track_type); |
|
|
|
if (idx < 0) continue; |
|
|
|
st = s->streams[idx]; |
|
|
|
@@ -353,6 +386,15 @@ static int gxf_header(AVFormatContext *s, AVFormatParameters *ap) { |
|
|
|
main_timebase.num = fps.den; |
|
|
|
main_timebase.den = fps.num * 2; |
|
|
|
} |
|
|
|
|
|
|
|
if (len >= 0x18) { |
|
|
|
len -= 0x18; |
|
|
|
avio_skip(pb, 0x10); |
|
|
|
add_timecode_metadata(&s->metadata, "gxf_timecode_at_mark_in", |
|
|
|
avio_rl32(pb), si->fields_per_frame); |
|
|
|
add_timecode_metadata(&s->metadata, "gxf_timecode_at_mark_out", |
|
|
|
avio_rl32(pb), si->fields_per_frame); |
|
|
|
} |
|
|
|
} else |
|
|
|
av_log(s, AV_LOG_INFO, "UMF packet too short\n"); |
|
|
|
} else |
|
|
|
|