History

Merge remote-tracking branch 'tjoppen/proper_mxf_track_linking'

* tjoppen/proper_mxf_track_linking: mxfdec: Don't parse slices or DeltaEntryArrays mxfdec: Remove dead/useless code mxfdec: Hybrid demuxing/seeking solution mxfdec: Add mxf_edit_unit_absolute_offset() mxfdec: Replace zero IndexDurations with st->duration mxfdec: Add "fake" index to MXFIndexTable to assist seeking mxfdec: Add MXFIndexTables mxfdec: Move mxf_read_packet*() near the bottom of the file

Michael Niedermayer committed 14 years ago

Commit a9ef21b2 parents 1c73391 0e764cf2 files · +356 −354Browse files

Unified · Split

2 files
  1. libavformat/mxfdec.c +338 −336
  2. tests/ref/seek/lavf_mxf_d10 +18 −18
libavformat/mxfdec.c+338−336
@@ -148,17 +148,12 @@ typedef struct { · 147 unmodified lines
148 int edit_unit_byte_count;148 int edit_unit_byte_count;
149 int index_sid;149 int index_sid;
150 int body_sid;150 int body_sid;
151 int slice_count;
152 AVRational index_edit_rate;151 AVRational index_edit_rate;
153 uint64_t index_start_position;152 uint64_t index_start_position;
154 uint64_t index_duration;153 uint64_t index_duration;
155 int *slice;
156 int *element_delta;
157 int nb_delta_entries;
158 int8_t *temporal_offset_entries;154 int8_t *temporal_offset_entries;
159 int *flag_entries;155 int *flag_entries;
160 uint64_t *stream_offset_entries;156 uint64_t *stream_offset_entries;
161 uint32_t **slice_offset_entries;
162 int nb_index_entries;157 int nb_index_entries;
163} MXFIndexTableSegment;158} MXFIndexTableSegment;
164 159
@@ -177,6 +172,18 @@ typedef struct { · 12 unmodified lines
177 enum MXFMetadataSetType type;172 enum MXFMetadataSetType type;
178} MXFMetadataSet;173} MXFMetadataSet;
179 174
175/* decoded index table */
176typedef struct {
177 int index_sid;
178 int body_sid;
179 int nb_ptses; /* number of PTSes or total duration of index */
180 int64_t first_dts; /* DTS = EditUnit + first_dts */
181 int64_t *ptses; /* maps EditUnit -> PTS */
182 int nb_segments;
183 MXFIndexTableSegment **segments; /* sorted by IndexStartPosition */
184 AVIndexEntry *fake_index; /* used for calling ff_index_search_timestamp() */
185} MXFIndexTable;
186
180typedef struct {187typedef struct {
181 MXFPartition *partitions;188 MXFPartition *partitions;
182 unsigned partitions_count;189 unsigned partitions_count;
@@ -198,12 +205,8 @@ typedef struct { · 15 unmodified lines
198 int64_t last_forward_tell;205 int64_t last_forward_tell;
199 int last_forward_partition;206 int last_forward_partition;
200 int current_edit_unit;207 int current_edit_unit;
201 int current_stream;208 int nb_index_tables;
202 int d10;209 MXFIndexTable *index_tables;
203 int broken_index;
204 int64_t first_dts; /* DTS = EditUnit + first_dts */
205 int64_t *ptses; /* maps EditUnit -> PTS */
206 int nb_ptses;
207} MXFContext;210} MXFContext;
208 211
209enum MXFWrappingScheme {212enum MXFWrappingScheme {
@@ -380,117 +383,6 @@ static int mxf_decrypt_triplet(AVFormatContext *s, AVPacket *pkt, KLVPacket *klv · 170 unmodified lines
380 return 0;383 return 0;
381}384}
382 385
383static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt)
384{
385 KLVPacket klv;
386
387 while (!url_feof(s->pb)) {
388 if (klv_read_packet(&klv, s->pb) < 0)
389 return -1;
390 PRINT_KEY(s, "read packet", klv.key);
391 av_dlog(s, "size %"PRIu64" offset %#"PRIx64"\n", klv.length, klv.offset);
392 if (IS_KLV_KEY(klv.key, mxf_encrypted_triplet_key)) {
393 int res = mxf_decrypt_triplet(s, pkt, &klv);
394 if (res < 0) {
395 av_log(s, AV_LOG_ERROR, "invalid encoded triplet\n");
396 return -1;
397 }
398 return 0;
399 }
400 if (IS_KLV_KEY(klv.key, mxf_essence_element_key) ||
401 IS_KLV_KEY(klv.key, mxf_avid_essence_element_key)) {
402 int index = mxf_get_stream_index(s, &klv);
403 if (index < 0) {
404 av_log(s, AV_LOG_ERROR, "error getting stream index %d\n", AV_RB32(klv.key+12));
405 goto skip;
406 }
407 if (s->streams[index]->discard == AVDISCARD_ALL)
408 goto skip;
409 /* check for 8 channels AES3 element */
410 if (klv.key[12] == 0x06 && klv.key[13] == 0x01 && klv.key[14] == 0x10) {
411 if (mxf_get_d10_aes3_packet(s->pb, s->streams[index], pkt, klv.length) < 0) {
412 av_log(s, AV_LOG_ERROR, "error reading D-10 aes3 frame\n");
413 return -1;
414 }
415 } else {
416 int ret = av_get_packet(s->pb, pkt, klv.length);
417 if (ret < 0)
418 return ret;
419 }
420 pkt->stream_index = index;
421 pkt->pos = klv.offset;
422 return 0;
423 } else
424 skip:
425 avio_skip(s->pb, klv.length);
426 }
427 return AVERROR_EOF;
428}
429
430static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt)
431{
432 MXFContext *mxf = s->priv_data;
433 AVIndexEntry *e;
434 int ret;
435 int64_t ret64;
436 KLVPacket klv;
437 AVStream *st;
438
439 /* TODO: better logic for this?
440 * only files that lack all index segments prior to the essence need this */
441 if (!s->pb->seekable && mxf->op != OPAtom || mxf->d10 || mxf->broken_index)
442 return mxf_read_packet_old(s, pkt);
443
444 if (mxf->current_stream >= s->nb_streams) {
445 mxf->current_edit_unit++;
446 mxf->current_stream = 0;
447 }
448
449 st = s->streams[mxf->current_stream];
450
451 if (mxf->current_edit_unit >= st->nb_index_entries)
452 return AVERROR_EOF;
453
454 e = &st->index_entries[mxf->current_edit_unit];
455
456 if ((ret64 = avio_seek(s->pb, e->pos, SEEK_SET)) < 0)
457 return ret64;
458
459 if (mxf->op == OPAtom) {
460 /* OPAtom - no KL, just essence */
461 if ((ret = av_get_packet(s->pb, pkt, e->size)) != e->size)
462 return ret < 0 ? ret : AVERROR_EOF;
463 } else {
464 /* read KL, read L bytes of essence */
465 if ((ret = klv_read_packet(&klv, s->pb)) < 0)
466 return ret;
467
468 /* untested, but looks OK */
469 if (IS_KLV_KEY(klv.key, mxf_encrypted_triplet_key)) {
470 int res = mxf_decrypt_triplet(s, pkt, &klv);
471 if (res < 0) {
472 av_log(s, AV_LOG_ERROR, "invalid encoded triplet\n");
473 return -1;
474 }
475 return 0;
476 }
477
478 if ((ret = av_get_packet(s->pb, pkt, klv.length)) != klv.length)
479 return ret < 0 ? ret : AVERROR_EOF;
480
481 pkt->pos = e->pos;
482 }
483
484 if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && mxf->ptses && mxf->current_edit_unit < mxf->nb_ptses) {
485 pkt->dts = mxf->current_edit_unit + mxf->first_dts;
486 pkt->pts = mxf->ptses[mxf->current_edit_unit];
487 }
488
489 pkt->stream_index = mxf->current_stream++;
490
491 return 0;
492}
493
494static int mxf_read_primer_pack(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset)386static int mxf_read_primer_pack(void *arg, AVIOContext *pb, int tag, int size, UID uid, int64_t klv_offset)
495{387{
496 MXFContext *mxf = arg;388 MXFContext *mxf = arg;
@@ -768,25 +660,6 @@ static int mxf_read_source_package(void *arg, AVIOContext *pb, int tag, int size · 271 unmodified lines
768 return 0;660 return 0;
769}661}
770 662
771static int mxf_read_delta_entry_array(AVIOContext *pb, MXFIndexTableSegment *segment)
772{
773 int i, length;
774
775 segment->nb_delta_entries = avio_rb32(pb);
776 length = avio_rb32(pb);
777
778 if (!(segment->slice = av_calloc(segment->nb_delta_entries, sizeof(*segment->slice))) ||
779 !(segment->element_delta = av_calloc(segment->nb_delta_entries, sizeof(*segment->element_delta))))
780 return AVERROR(ENOMEM);
781
782 for (i = 0; i < segment->nb_delta_entries; i++) {
783 avio_r8(pb); /* PosTableIndex */
784 segment->slice[i] = avio_r8(pb);
785 segment->element_delta[i] = avio_rb32(pb);
786 }
787 return 0;
788}
789
790static int mxf_read_index_entry_array(AVIOContext *pb, MXFIndexTableSegment *segment)663static int mxf_read_index_entry_array(AVIOContext *pb, MXFIndexTableSegment *segment)
791{664{
792 int i, j, length;665 int i, j, length;
@@ -799,24 +672,12 @@ static int mxf_read_index_entry_array(AVIOContext *pb, MXFIndexTableSegment *seg · 6 unmodified lines
799 !(segment->stream_offset_entries = av_calloc(segment->nb_index_entries, sizeof(*segment->stream_offset_entries))))672 !(segment->stream_offset_entries = av_calloc(segment->nb_index_entries, sizeof(*segment->stream_offset_entries))))
800 return AVERROR(ENOMEM);673 return AVERROR(ENOMEM);
801 674
802 if (segment->slice_count &&
803 !(segment->slice_offset_entries = av_calloc(segment->nb_index_entries, sizeof(*segment->slice_offset_entries))))
804 return AVERROR(ENOMEM);
805
806 for (i = 0; i < segment->nb_index_entries; i++) {675 for (i = 0; i < segment->nb_index_entries; i++) {
807 segment->temporal_offset_entries[i] = avio_r8(pb);676 segment->temporal_offset_entries[i] = avio_r8(pb);
808 avio_r8(pb); /* KeyFrameOffset */677 avio_r8(pb); /* KeyFrameOffset */
809 segment->flag_entries[i] = avio_r8(pb);678 segment->flag_entries[i] = avio_r8(pb);
810 segment->stream_offset_entries[i] = avio_rb64(pb);679 segment->stream_offset_entries[i] = avio_rb64(pb);
811 if (segment->slice_count) {680 avio_skip(pb, length - 11);
812 if (!(segment->slice_offset_entries[i] = av_calloc(segment->slice_count, sizeof(**segment->slice_offset_entries))))
813 return AVERROR(ENOMEM);
814
815 for (j = 0; j < segment->slice_count; j++)
816 segment->slice_offset_entries[i][j] = avio_rb32(pb);
817 }
818
819 avio_skip(pb, length - 11 - 4 * segment->slice_count);
820 }681 }
821 return 0;682 return 0;
822}683}
@@ -837,13 +698,6 @@ static int mxf_read_index_table_segment(void *arg, AVIOContext *pb, int tag, int · 14 unmodified lines
837 segment->body_sid = avio_rb32(pb);698 segment->body_sid = avio_rb32(pb);
838 av_dlog(NULL, "BodySID %d\n", segment->body_sid);699 av_dlog(NULL, "BodySID %d\n", segment->body_sid);
839 break;700 break;
840 case 0x3F08:
841 segment->slice_count = avio_r8(pb);
842 av_dlog(NULL, "SliceCount %d\n", segment->slice_count);
843 break;
844 case 0x3F09:
845 av_dlog(NULL, "DeltaEntryArray found\n");
846 return mxf_read_delta_entry_array(pb, segment);
847 case 0x3F0A:701 case 0x3F0A:
848 av_dlog(NULL, "IndexEntryArray found\n");702 av_dlog(NULL, "IndexEntryArray found\n");
849 return mxf_read_index_entry_array(pb, segment);703 return mxf_read_index_entry_array(pb, segment);
@@ -1001,8 +855,6 @@ static const MXFCodecUL mxf_sound_essence_container_uls[] = { · 151 unmodified lines
1001 { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, CODEC_ID_NONE },855 { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, CODEC_ID_NONE },
1002};856};
1003 857
1004static UID mxf_d10_ul = { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x01,0x01 };
1005
1006static int mxf_get_sorted_table_segments(MXFContext *mxf, int *nb_sorted_segments, MXFIndexTableSegment ***sorted_segments)858static int mxf_get_sorted_table_segments(MXFContext *mxf, int *nb_sorted_segments, MXFIndexTableSegment ***sorted_segments)
1007{859{
1008 int i, j, nb_segments = 0;860 int i, j, nb_segments = 0;
@@ -1089,9 +941,9 @@ static int mxf_absolute_bodysid_offset(MXFContext *mxf, int body_sid, int64_t of · 80 unmodified lines
1089}941}
1090 942
1091/**943/**
1092 * Returns the length of the essence container with given BodySID, or zero if unknown944 * Returns the end position of the essence container with given BodySID, or zero if unknown
1093 */945 */
1094static int64_t mxf_essence_container_length(MXFContext *mxf, int body_sid)946static int64_t mxf_essence_container_end(MXFContext *mxf, int body_sid)
1095{947{
1096 int x;948 int x;
1097 int64_t ret = 0;949 int64_t ret = 0;
@@ -1105,167 +957,90 @@ static int64_t mxf_essence_container_length(MXFContext *mxf, int body_sid) · 7 unmodified lines
1105 if (!p->essence_length)957 if (!p->essence_length)
1106 return 0;958 return 0;
1107 959
1108 ret += p->essence_length;960 ret = p->essence_offset + p->essence_length;
1109 }961 }
1110 962
1111 return ret;963 return ret;
1112}964}
1113 965
1114static int mxf_parse_index(MXFContext *mxf, int track_id, AVStream *st, MXFIndexTableSegment **sorted_segments, int nb_sorted_segments)966/* EditUnit -> absolute offset */
967static int mxf_edit_unit_absolute_offset(MXFContext *mxf, MXFIndexTable *index_table, int64_t edit_unit, int64_t *edit_unit_out, int64_t *offset_out, int nag)
1115{968{
1116 int64_t accumulated_offset = 0;969 int i;
1117 int j, k, l, ret;970 int offset_temp = 0;
1118 int n_delta = track_id - 1; /* TrackID = 1-based stream index */
1119
1120 if (track_id < 1) {
1121 av_log(mxf->fc, AV_LOG_ERROR, "TrackID not positive: %i\n", track_id);
1122 return AVERROR_INVALIDDATA;
1123 }
1124
1125 for (j = 0; j < nb_sorted_segments; j++) {
1126 int duration, sample_duration = 1, last_sample_size = 0;
1127 int64_t segment_size;
1128 MXFIndexTableSegment *tableseg = sorted_segments[j];
1129 int index_delta = 1, last_size_unknown = 0;
1130 int64_t last_pos = 0;
1131 971
1132 /* reset accumulated_offset on BodySID change */972 for (i = 0; i < index_table->nb_segments; i++) {
1133 if (j > 0 && tableseg->body_sid != sorted_segments[j-1]->body_sid)973 MXFIndexTableSegment *s = index_table->segments[i];
1134 accumulated_offset = 0;
1135 974
1136 if (tableseg->nb_index_entries == 2 * tableseg->index_duration + 1) {975 edit_unit = FFMAX(edit_unit, s->index_start_position); /* clamp if trying to seek before start */
1137 /* Avid index - duplicate entries and total size as last entry */
1138 index_delta = 2;
1139 }
1140 976
1141 if (n_delta >= tableseg->nb_delta_entries && st->index != 0)977 if (edit_unit < s->index_start_position + s->index_duration) {
1142 continue;978 int64_t index = edit_unit - s->index_start_position;
1143 duration = tableseg->index_duration > 0 ? tableseg->index_duration :
1144 st->duration - st->nb_index_entries;
1145 segment_size = tableseg->edit_unit_byte_count * duration;
1146 /* check small EditUnitByteCount for audio */
1147 if (tableseg->edit_unit_byte_count && tableseg->edit_unit_byte_count < 32
1148 && !tableseg->index_duration) {
1149 /* duration might be prime relative to the new sample_duration,
1150 * which means we need to handle the last frame differently */
1151 sample_duration = 8192;
1152 last_sample_size = (duration % sample_duration) * tableseg->edit_unit_byte_count;
1153 tableseg->edit_unit_byte_count *= sample_duration;
1154 duration /= sample_duration;
1155 if (last_sample_size) duration++;
1156 }
1157 979
1158 if (duration <= 0) {980 if (s->edit_unit_byte_count)
1159 av_log(mxf->fc, AV_LOG_WARNING, "0 duration in index for stream %i\n", st->index);981 offset_temp += s->edit_unit_byte_count * index;
1160 mxf->broken_index = 1;982 else if (s->nb_index_entries) {
1161 return 0;983 if (s->nb_index_entries == 2 * s->index_duration + 1)
1162 }984 index *= 2; /* Avid index */
1163 985
1164 for (k = l = 0; k < duration; k++, l += index_delta) {986 if (index < 0 || index > s->nb_index_entries) {
1165 int64_t pos;987 av_log(mxf->fc, AV_LOG_ERROR, "IndexSID %i segment at %"PRId64" IndexEntryArray too small\n",
1166 int size, flags = 0;988 index_table->index_sid, s->index_start_position);
1167 989 return AVERROR_INVALIDDATA;
1168 if (l < tableseg->nb_index_entries) {
1169 pos = tableseg->stream_offset_entries[l];
1170 if (n_delta < tableseg->nb_delta_entries) {
1171 if (n_delta < tableseg->nb_delta_entries - 1) {
1172 size =
1173 tableseg->slice_offset_entries[l][tableseg->slice[n_delta+1]-1] +
1174 tableseg->element_delta[n_delta+1] -
1175 tableseg->element_delta[n_delta];
1176 if (tableseg->slice[n_delta] > 0)
1177 size -= tableseg->slice_offset_entries[l][tableseg->slice[n_delta]-1];
1178 } else if (l < tableseg->nb_index_entries - 1) {
1179 size = tableseg->stream_offset_entries[l+1] -
1180 tableseg->stream_offset_entries[l] -
1181 tableseg->slice_offset_entries[l][tableseg->slice[tableseg->nb_delta_entries-1]-1] -
1182 tableseg->element_delta[tableseg->nb_delta_entries-1];
1183 } else
1184 size = 0;
1185 if (tableseg->slice[n_delta] > 0)
1186 pos += tableseg->slice_offset_entries[l][tableseg->slice[n_delta]-1];
1187 pos += tableseg->element_delta[n_delta];
1188 } else
1189 size = 0;
1190 flags = !(tableseg->flag_entries[l] & 0x30) ? AVINDEX_KEYFRAME : 0;
1191 } else {
1192 pos = (int64_t)k * tableseg->edit_unit_byte_count + accumulated_offset;
1193 if (n_delta < tableseg->nb_delta_entries - 1)
1194 size = tableseg->element_delta[n_delta+1] - tableseg->element_delta[n_delta];
1195 else {
1196 /* use smaller size for last sample if we should */
1197 if (last_sample_size && k == duration - 1)
1198 size = last_sample_size;
1199 else
1200 size = tableseg->edit_unit_byte_count;
1201 if (tableseg->nb_delta_entries)
1202 size -= tableseg->element_delta[tableseg->nb_delta_entries-1];
1203 }990 }
1204 if (n_delta < tableseg->nb_delta_entries)
1205 pos += tableseg->element_delta[n_delta];
1206 flags = AVINDEX_KEYFRAME;
1207 }
1208
1209 if (last_size_unknown)
1210 st->index_entries[st->nb_index_entries-1].size = pos - last_pos;
1211 991
1212 last_size_unknown = size == 0;992 offset_temp = s->stream_offset_entries[index];
1213 last_pos = pos;993 } else {
1214 994 av_log(mxf->fc, AV_LOG_ERROR, "IndexSID %i segment at %"PRId64" missing EditUnitByteCount and IndexEntryArray\n",
1215 if (mxf_absolute_bodysid_offset(mxf, tableseg->body_sid, pos, &pos) < 0) {995 index_table->index_sid, s->index_start_position);
1216 /* probably partial file - no point going further for this stream */996 return AVERROR_INVALIDDATA;
1217 break;
1218 }997 }
1219 998
1220 av_dlog(mxf->fc, "Stream %d IndexEntry %d TrackID %d Offset %"PRIx64" Timestamp %"PRId64"\n",999 if (edit_unit_out)
1221 st->index, st->nb_index_entries, track_id, pos, sample_duration * st->nb_index_entries);1000 *edit_unit_out = edit_unit;
1222
1223 if ((ret = av_add_index_entry(st, pos, sample_duration * st->nb_index_entries, size, 0, flags)) < 0)
1224 return ret;
1225 }
1226 1001
1227 if (last_size_unknown) {1002 return mxf_absolute_bodysid_offset(mxf, index_table->body_sid, offset_temp, offset_out);
1228 int64_t ecl = mxf_essence_container_length(mxf, tableseg->body_sid);1003 } else {
1229 1004 /* EditUnitByteCount == 0 for VBR indexes, which is fine since they use explicit StreamOffsets */
1230 if (ecl > 0)1005 offset_temp += s->edit_unit_byte_count * s->index_duration;
1231 st->index_entries[st->nb_index_entries-1].size = ecl - last_pos;
1232 }1006 }
1233
1234 accumulated_offset += segment_size;
1235 }1007 }
1236 1008
1237 return 0;1009 if (nag)
1010 av_log(mxf->fc, AV_LOG_ERROR, "failed to map EditUnit %"PRId64" in IndexSID %i to an offset\n", edit_unit, index_table->index_sid);
1011
1012 return AVERROR_INVALIDDATA;
1238}1013}
1239 1014
1240static int mxf_compute_ptses(MXFContext *mxf, MXFIndexTableSegment **sorted_segments, int nb_sorted_segments)1015static int mxf_compute_ptses_fake_index(MXFContext *mxf, MXFIndexTable *index_table)
1241{1016{
1242 int ret, i, j, x;1017 int i, j, x;
1243 int8_t max_temporal_offset = -128;1018 int8_t max_temporal_offset = -128;
1244 1019
1245 /* first compute how many entries we have */1020 /* first compute how many entries we have */
1246 for (i = 0; i < nb_sorted_segments; i++) {1021 for (i = 0; i < index_table->nb_segments; i++) {
1247 MXFIndexTableSegment *s = sorted_segments[i];1022 MXFIndexTableSegment *s = index_table->segments[i];
1248 1023
1249 if (!s->nb_index_entries)1024 if (!s->nb_index_entries)
1250 return 0; /* no TemporalOffsets */1025 return 0; /* no TemporalOffsets */
1251 1026
1252 if (s->nb_index_entries == 2 * s->index_duration + 1)1027 index_table->nb_ptses += s->index_duration;
1253 mxf->nb_ptses += s->index_duration; /* Avid index */
1254 else
1255 mxf->nb_ptses += s->nb_index_entries;
1256 }1028 }
1257 1029
1258 /* paranoid check */1030 /* paranoid check */
1259 if (mxf->nb_ptses <= 0)1031 if (index_table->nb_ptses <= 0)
1260 return 0;1032 return 0;
1261 1033
1262 if (!(mxf->ptses = av_calloc(mxf->nb_ptses, sizeof(int64_t))))1034 if (!(index_table->ptses = av_calloc(index_table->nb_ptses, sizeof(int64_t))) ||
1035 !(index_table->fake_index = av_calloc(index_table->nb_ptses, sizeof(AVIndexEntry)))) {
1036 av_freep(&index_table->ptses);
1263 return AVERROR(ENOMEM);1037 return AVERROR(ENOMEM);
1038 }
1264 1039
1265 /* we may have a few bad TemporalOffsets1040 /* we may have a few bad TemporalOffsets
1266 * make sure the corresponding PTSes don't have the bogus value 0 */1041 * make sure the corresponding PTSes don't have the bogus value 0 */
1267 for (x = 0; x < mxf->nb_ptses; x++)1042 for (x = 0; x < index_table->nb_ptses; x++)
1268 mxf->ptses[x] = AV_NOPTS_VALUE;1043 index_table->ptses[x] = AV_NOPTS_VALUE;
1269 1044
1270 /**1045 /**
1271 * We have this:1046 * We have this:
@@ -1294,8 +1069,8 @@ static int mxf_compute_ptses(MXFContext *mxf, MXFIndexTableSegment **sorted_segm · 22 unmodified lines
1294 * then settings mxf->first_dts = -max(TemporalOffset[x]).1069 * then settings mxf->first_dts = -max(TemporalOffset[x]).
1295 * The latter makes DTS <= PTS.1070 * The latter makes DTS <= PTS.
1296 */1071 */
1297 for (i = x = 0; i < nb_sorted_segments; i++) {1072 for (i = x = 0; i < index_table->nb_segments; i++) {
1298 MXFIndexTableSegment *s = sorted_segments[i];1073 MXFIndexTableSegment *s = index_table->segments[i];
1299 int index_delta = 1;1074 int index_delta = 1;
1300 1075
1301 if (s->nb_index_entries == 2 * s->index_duration + 1)1076 if (s->nb_index_entries == 2 * s->index_duration + 1)
@@ -1305,29 +1080,121 @@ static int mxf_compute_ptses(MXFContext *mxf, MXFIndexTableSegment **sorted_segm · 3 unmodified lines
1305 int offset = s->temporal_offset_entries[j] / index_delta;1080 int offset = s->temporal_offset_entries[j] / index_delta;
1306 int index = x + offset;1081 int index = x + offset;
1307 1082
1308 if (index < 0 || index >= mxf->nb_ptses) {1083 index_table->fake_index[x].timestamp = x;
1084 index_table->fake_index[x].flags = !(s->flag_entries[j] & 0x30) ? AVINDEX_KEYFRAME : 0;
1085
1086 if (index < 0 || index >= index_table->nb_ptses) {
1309 av_log(mxf->fc, AV_LOG_ERROR,1087 av_log(mxf->fc, AV_LOG_ERROR,
1310 "index entry %i + TemporalOffset %i = %i, which is out of bounds\n",1088 "index entry %i + TemporalOffset %i = %i, which is out of bounds\n",
1311 x, offset, index);1089 x, offset, index);
1312 continue;1090 continue;
1313 }1091 }
1314 1092
1315 mxf->ptses[index] = x;1093 index_table->ptses[index] = x;
1316 max_temporal_offset = FFMAX(max_temporal_offset, offset);1094 max_temporal_offset = FFMAX(max_temporal_offset, offset);
1317 }1095 }
1318 }1096 }
1319 1097
1320 mxf->first_dts = -max_temporal_offset;1098 index_table->first_dts = -max_temporal_offset;
1321 1099
1322 return 0;1100 return 0;
1323}1101}
1324 1102
1103/**
1104 * Sorts and collects index table segments into index tables.
1105 * Also computes PTSes if possible.
1106 */
1107static int mxf_compute_index_tables(MXFContext *mxf)
1108{
1109 int i, j, k, ret, nb_sorted_segments;
1110 MXFIndexTableSegment **sorted_segments = NULL;
1111
1112 if ((ret = mxf_get_sorted_table_segments(mxf, &nb_sorted_segments, &sorted_segments)) ||
1113 nb_sorted_segments <= 0) {
1114 av_log(mxf->fc, AV_LOG_WARNING, "broken or empty index\n");
1115 return 0;
1116 }
1117
1118 /* sanity check and count unique BodySIDs/IndexSIDs */
1119 for (i = 0; i < nb_sorted_segments; i++) {
1120 if (i == 0 || sorted_segments[i-1]->index_sid != sorted_segments[i]->index_sid)
1121 mxf->nb_index_tables++;
1122 else if (sorted_segments[i-1]->body_sid != sorted_segments[i]->body_sid) {
1123 av_log(mxf->fc, AV_LOG_ERROR, "found inconsistent BodySID\n");
1124 ret = AVERROR_INVALIDDATA;
1125 goto finish_decoding_index;
1126 }
1127 }
1128
1129 if (!(mxf->index_tables = av_calloc(mxf->nb_index_tables, sizeof(MXFIndexTable)))) {
1130 av_log(mxf->fc, AV_LOG_ERROR, "failed to allocate index tables\n");
1131 ret = AVERROR(ENOMEM);
1132 goto finish_decoding_index;
1133 }
1134
1135 /* distribute sorted segments to index tables */
1136 for (i = j = 0; i < nb_sorted_segments; i++) {
1137 if (i != 0 && sorted_segments[i-1]->index_sid != sorted_segments[i]->index_sid) {
1138 /* next IndexSID */
1139 j++;
1140 }
1141
1142 mxf->index_tables[j].nb_segments++;
1143 }
1144
1145 for (i = j = 0; j < mxf->nb_index_tables; i += mxf->index_tables[j++].nb_segments) {
1146 MXFIndexTable *t = &mxf->index_tables[j];
1147
1148 if (!(t->segments = av_calloc(t->nb_segments, sizeof(MXFIndexTableSegment*)))) {
1149 av_log(mxf->fc, AV_LOG_ERROR, "failed to allocate IndexTableSegment pointer array\n");
1150 ret = AVERROR(ENOMEM);
1151 goto finish_decoding_index;
1152 }
1153
1154 if (sorted_segments[i]->index_start_position)
1155 av_log(mxf->fc, AV_LOG_WARNING, "IndexSID %i starts at EditUnit %"PRId64" - seeking may not work as expected\n",
1156 sorted_segments[i]->index_sid, sorted_segments[i]->index_start_position);
1157
1158 memcpy(t->segments, &sorted_segments[i], t->nb_segments * sizeof(MXFIndexTableSegment*));
1159 t->index_sid = sorted_segments[i]->index_sid;
1160 t->body_sid = sorted_segments[i]->body_sid;
1161
1162 if ((ret = mxf_compute_ptses_fake_index(mxf, t)) < 0)
1163 goto finish_decoding_index;
1164
1165 /* fix zero IndexDurations */
1166 for (k = 0; k < t->nb_segments; k++) {
1167 if (t->segments[k]->index_duration)
1168 continue;
1169
1170 if (t->nb_segments > 1)
1171 av_log(mxf->fc, AV_LOG_WARNING, "IndexSID %i segment %i has zero IndexDuration and there's more than one segment\n",
1172 t->index_sid, k);
1173
1174 if (mxf->fc->nb_streams <= 0) {
1175 av_log(mxf->fc, AV_LOG_WARNING, "no streams?\n");
1176 break;
1177 }
1178
1179 /* assume the first stream's duration is reasonable
1180 * leave index_duration = 0 on further segments in case we have any (unlikely)
1181 */
1182 t->segments[k]->index_duration = mxf->fc->streams[0]->duration;
1183 break;
1184 }
1185 }
1186
1187 ret = 0;
1188finish_decoding_index:
1189 av_free(sorted_segments);
1190 return ret;
1191}
1192
1325static int mxf_parse_structural_metadata(MXFContext *mxf)1193static int mxf_parse_structural_metadata(MXFContext *mxf)
1326{1194{
1327 MXFPackage *material_package = NULL;1195 MXFPackage *material_package = NULL;
1328 MXFPackage *temp_package = NULL;1196 MXFPackage *temp_package = NULL;
1329 int i, j, k, ret, nb_sorted_segments;1197 int i, j, k, ret;
1330 MXFIndexTableSegment **sorted_segments = NULL;
1331 1198
1332 av_dlog(mxf->fc, "metadata sets count %d\n", mxf->metadata_sets_count);1199 av_dlog(mxf->fc, "metadata sets count %d\n", mxf->metadata_sets_count);
1333 /* TODO: handle multiple material packages (OP3x) */1200 /* TODO: handle multiple material packages (OP3x) */
@@ -1340,12 +1207,6 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) · 6 unmodified lines
1340 return -1;1207 return -1;
1341 }1208 }
1342 1209
1343 if ((ret = mxf_get_sorted_table_segments(mxf, &nb_sorted_segments, &sorted_segments)) ||
1344 nb_sorted_segments <= 0) {
1345 av_log(mxf->fc, AV_LOG_WARNING, "broken or empty index\n");
1346 mxf->broken_index = 1;
1347 }
1348
1349 for (i = 0; i < material_package->tracks_count; i++) {1210 for (i = 0; i < material_package->tracks_count; i++) {
1350 MXFPackage *source_package = NULL;1211 MXFPackage *source_package = NULL;
1351 MXFTrack *material_track = NULL;1212 MXFTrack *material_track = NULL;
@@ -1476,10 +1337,6 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) · 124 unmodified lines
1476 }1337 }
1477 }1338 }
1478 1339
1479 /* HACK: revert to the old demuxing/seeking scode for D-10 for now */
1480 if (mxf_match_uid(essence_container_ul, mxf_d10_ul, 14))
1481 mxf->d10 = 1;
1482
1483 /* TODO: drop PictureEssenceCoding and SoundEssenceCompression, only check EssenceContainer */1340 /* TODO: drop PictureEssenceCoding and SoundEssenceCompression, only check EssenceContainer */
1484 codec_ul = mxf_get_codec_ul(ff_mxf_codec_uls, &descriptor->essence_codec_ul);1341 codec_ul = mxf_get_codec_ul(ff_mxf_codec_uls, &descriptor->essence_codec_ul);
1485 st->codec->codec_id = codec_ul->id;1342 st->codec->codec_id = codec_ul->id;
@@ -1522,16 +1379,10 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) · 36 unmodified lines
1522 /* TODO: decode timestamps */1379 /* TODO: decode timestamps */
1523 st->need_parsing = AVSTREAM_PARSE_TIMESTAMPS;1380 st->need_parsing = AVSTREAM_PARSE_TIMESTAMPS;
1524 }1381 }
1525
1526 if (!mxf->broken_index &&
1527 (ret = mxf_parse_index(mxf, material_track->track_id, st, sorted_segments, nb_sorted_segments)))
1528 goto fail_and_free;
1529 }1382 }
1530 1383
1531 ret = mxf_compute_ptses(mxf, sorted_segments, nb_sorted_segments);1384 ret = 0;
1532
1533fail_and_free:1385fail_and_free:
1534 av_free(sorted_segments);
1535 return ret;1386 return ret;
1536}1387}
1537 1388
@@ -1728,6 +1579,7 @@ static int mxf_read_header(AVFormatContext *s, AVFormatParameters *ap) · 190 unmodified lines
1728 MXFContext *mxf = s->priv_data;1579 MXFContext *mxf = s->priv_data;
1729 KLVPacket klv;1580 KLVPacket klv;
1730 int64_t essence_offset = 0;1581 int64_t essence_offset = 0;
1582 int ret;
1731 1583
1732 mxf->last_forward_tell = INT64_MAX;1584 mxf->last_forward_tell = INT64_MAX;
1733 1585
@@ -1823,7 +1675,153 @@ static int mxf_read_header(AVFormatContext *s, AVFormatParameters *ap) · 89 unmodified lines
1823 1675
1824 mxf_compute_essence_containers(mxf);1676 mxf_compute_essence_containers(mxf);
1825 1677
1826 return mxf_parse_structural_metadata(mxf);1678 /* we need to do this before computing the index tables
1679 * to be able to fill in zero IndexDurations with st->duration */
1680 if ((ret = mxf_parse_structural_metadata(mxf)) < 0)
1681 return ret;
1682
1683 if ((ret = mxf_compute_index_tables(mxf)) < 0)
1684 return ret;
1685
1686 if (mxf->nb_index_tables > 1) {
1687 /* TODO: look up which IndexSID to use via EssenceContainerData */
1688 av_log(mxf->fc, AV_LOG_INFO, "got %i index tables - only the first one (IndexSID %i) will be used\n",
1689 mxf->nb_index_tables, mxf->index_tables[0].index_sid);
1690 } else if (mxf->nb_index_tables == 0 && mxf->op == OPAtom) {
1691 av_log(mxf->fc, AV_LOG_ERROR, "cannot demux OPAtom without an index\n");
1692 return AVERROR_INVALIDDATA;
1693 }
1694
1695 return 0;
1696}
1697
1698/**
1699 * Computes DTS and PTS for the given video packet based on its offset.
1700 */
1701static void mxf_packet_timestamps(MXFContext *mxf, AVPacket *pkt)
1702{
1703 int64_t next_ofs;
1704 MXFIndexTable *t = &mxf->index_tables[0];
1705
1706 /* find mxf->current_edit_unit so that the next edit unit starts ahead of pkt->pos */
1707 for (;;) {
1708 if (mxf_edit_unit_absolute_offset(mxf, t, mxf->current_edit_unit + 1, NULL, &next_ofs, 0) < 0)
1709 break;
1710
1711 if (next_ofs > pkt->pos)
1712 break;
1713
1714 mxf->current_edit_unit++;
1715 }
1716
1717 if (mxf->current_edit_unit >= t->nb_ptses)
1718 return;
1719
1720 pkt->dts = mxf->current_edit_unit + t->first_dts;
1721 pkt->pts = t->ptses[mxf->current_edit_unit];
1722}
1723
1724static int mxf_read_packet_old(AVFormatContext *s, AVPacket *pkt)
1725{
1726 KLVPacket klv;
1727
1728 while (!url_feof(s->pb)) {
1729 if (klv_read_packet(&klv, s->pb) < 0)
1730 return -1;
1731 PRINT_KEY(s, "read packet", klv.key);
1732 av_dlog(s, "size %"PRIu64" offset %#"PRIx64"\n", klv.length, klv.offset);
1733 if (IS_KLV_KEY(klv.key, mxf_encrypted_triplet_key)) {
1734 int res = mxf_decrypt_triplet(s, pkt, &klv);
1735 if (res < 0) {
1736 av_log(s, AV_LOG_ERROR, "invalid encoded triplet\n");
1737 return -1;
1738 }
1739 return 0;
1740 }
1741 if (IS_KLV_KEY(klv.key, mxf_essence_element_key) ||
1742 IS_KLV_KEY(klv.key, mxf_avid_essence_element_key)) {
1743 int index = mxf_get_stream_index(s, &klv);
1744 if (index < 0) {
1745 av_log(s, AV_LOG_ERROR, "error getting stream index %d\n", AV_RB32(klv.key+12));
1746 goto skip;
1747 }
1748 if (s->streams[index]->discard == AVDISCARD_ALL)
1749 goto skip;
1750 /* check for 8 channels AES3 element */
1751 if (klv.key[12] == 0x06 && klv.key[13] == 0x01 && klv.key[14] == 0x10) {
1752 if (mxf_get_d10_aes3_packet(s->pb, s->streams[index], pkt, klv.length) < 0) {
1753 av_log(s, AV_LOG_ERROR, "error reading D-10 aes3 frame\n");
1754 return -1;
1755 }
1756 } else {
1757 int ret = av_get_packet(s->pb, pkt, klv.length);
1758 if (ret < 0)
1759 return ret;
1760 }
1761 pkt->stream_index = index;
1762 pkt->pos = klv.offset;
1763
1764 if (s->streams[index]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
1765 mxf_packet_timestamps(s->priv_data, pkt); /* offset -> EditUnit -> DTS/PTS */
1766
1767 return 0;
1768 } else
1769 skip:
1770 avio_skip(s->pb, klv.length);
1771 }
1772 return AVERROR_EOF;
1773}
1774
1775static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt)
1776{
1777 MXFContext *mxf = s->priv_data;
1778 int ret, size;
1779 int64_t ret64, pos, next_pos;
1780 AVStream *st;
1781 MXFIndexTable *t;
1782
1783 if (mxf->op != OPAtom)
1784 return mxf_read_packet_old(s, pkt);
1785
1786 /* OPAtom - clip wrapped demuxing */
1787 st = s->streams[0];
1788 t = &mxf->index_tables[0];
1789
1790 if (mxf->current_edit_unit >= st->duration)
1791 return AVERROR_EOF;
1792
1793 if ((ret = mxf_edit_unit_absolute_offset(mxf, t, mxf->current_edit_unit, NULL, &pos, 1)) < 0)
1794 return ret;
1795
1796 /* compute size by finding the next edit unit or the end of the essence container
1797 * not pretty, but it works */
1798 if ((ret = mxf_edit_unit_absolute_offset(mxf, t, mxf->current_edit_unit + 1, NULL, &next_pos, 0)) < 0 &&
1799 (next_pos = mxf_essence_container_end(mxf, t->body_sid)) <= 0) {
1800 av_log(s, AV_LOG_ERROR, "unable to compute the size of the last packet\n");
1801 return AVERROR_INVALIDDATA;
1802 }
1803
1804 if ((size = next_pos - pos) <= 0) {
1805 av_log(s, AV_LOG_ERROR, "bad size: %i\n", size);
1806 return AVERROR_INVALIDDATA;
1807 }
1808
1809 if ((ret64 = avio_seek(s->pb, pos, SEEK_SET)) < 0)
1810 return ret64;
1811
1812 if ((ret = av_get_packet(s->pb, pkt, size)) != size)
1813 return ret < 0 ? ret : AVERROR_EOF;
1814
1815 if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && t->ptses &&
1816 mxf->current_edit_unit >= 0 && mxf->current_edit_unit < t->nb_ptses) {
1817 pkt->dts = mxf->current_edit_unit + t->first_dts;
1818 pkt->pts = t->ptses[mxf->current_edit_unit];
1819 }
1820
1821 pkt->stream_index = 0;
1822 mxf->current_edit_unit++;
1823
1824 return 0;
1827}1825}
1828 1826
1829static int mxf_read_close(AVFormatContext *s)1827static int mxf_read_close(AVFormatContext *s)
@@ -1851,15 +1849,9 @@ static int mxf_read_close(AVFormatContext *s) · 21 unmodified lines
1851 break;1849 break;
1852 case IndexTableSegment:1850 case IndexTableSegment:
1853 seg = (MXFIndexTableSegment *)mxf->metadata_sets[i];1851 seg = (MXFIndexTableSegment *)mxf->metadata_sets[i];
1854 if (seg->slice_count)
1855 for (j = 0; j < seg->nb_index_entries; j++)
1856 av_freep(&seg->slice_offset_entries[j]);
1857 av_freep(&seg->slice);
1858 av_freep(&seg->element_delta);
1859 av_freep(&seg->temporal_offset_entries);1852 av_freep(&seg->temporal_offset_entries);
1860 av_freep(&seg->flag_entries);1853 av_freep(&seg->flag_entries);
1861 av_freep(&seg->stream_offset_entries);1854 av_freep(&seg->stream_offset_entries);
1862 av_freep(&seg->slice_offset_entries);
1863 break;1855 break;
1864 default:1856 default:
1865 break;1857 break;
@@ -1870,7 +1862,13 @@ static int mxf_read_close(AVFormatContext *s) · 4 unmodified lines
1870 av_freep(&mxf->metadata_sets);1862 av_freep(&mxf->metadata_sets);
1871 av_freep(&mxf->aesc);1863 av_freep(&mxf->aesc);
1872 av_freep(&mxf->local_tags);1864 av_freep(&mxf->local_tags);
1873 av_freep(&mxf->ptses);1865
1866 for (i = 0; i < mxf->nb_index_tables; i++) {
1867 av_freep(&mxf->index_tables[i].segments);
1868 av_freep(&mxf->index_tables[i].fake_index);
1869 }
1870 av_freep(&mxf->index_tables);
1871
1874 return 0;1872 return 0;
1875}1873}
1876 1874
@@ -1898,9 +1896,10 @@ static int mxf_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti · 21 unmodified lines
1898 int64_t seconds;1896 int64_t seconds;
1899 MXFContext* mxf = s->priv_data;1897 MXFContext* mxf = s->priv_data;
1900 int64_t seekpos;1898 int64_t seekpos;
1901 int index;1899 int ret;
1900 MXFIndexTable *t;
1902 1901
1903 if (mxf->d10) {1902 if (mxf->index_tables <= 0) {
1904 if (!s->bit_rate)1903 if (!s->bit_rate)
1905 return -1;1904 return -1;
1906 if (sample_time < 0)1905 if (sample_time < 0)
@@ -1910,24 +1909,27 @@ static int mxf_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti · 3 unmodified lines
1910 return -1;1909 return -1;
1911 ff_update_cur_dts(s, st, sample_time);1910 ff_update_cur_dts(s, st, sample_time);
1912 } else {1911 } else {
1913 if (st->nb_index_entries <= 0)1912 t = &mxf->index_tables[0];
1914 return -1;
1915 1913
1916 index = av_index_search_timestamp(st, sample_time, flags);1914 /* clamp above zero, else ff_index_search_timestamp() returns negative
1915 * this also means we allow seeking before the start */
1916 sample_time = FFMAX(sample_time, 0);
1917 1917
1918 av_dlog(s, "stream %d, timestamp %"PRId64", sample %d\n", st->index, sample_time, index);1918 if (t->fake_index) {
1919 1919 /* behave as if we have a proper index */
1920 if (index < 0) {1920 if ((sample_time = ff_index_search_timestamp(t->fake_index, t->nb_ptses, sample_time, flags)) < 0)
1921 if (sample_time < st->index_entries[0].timestamp)1921 return sample_time;
1922 index = 0;1922 } else {
1923 else1923 /* no IndexEntryArray (one or more CBR segments)
1924 return -1;1924 * make sure we don't seek past the end */
1925 sample_time = FFMIN(sample_time, st->duration - 1);
1925 }1926 }
1926 1927
1927 seekpos = st->index_entries[index].pos;1928 if ((ret = mxf_edit_unit_absolute_offset(mxf, t, sample_time, &sample_time, &seekpos, 1)) << 0)
1928 av_update_cur_dts(s, st, st->index_entries[index].timestamp);1929 return ret;
1929 mxf->current_edit_unit = st->index_entries[index].timestamp;1930
1930 mxf->current_stream = 0;1931 av_update_cur_dts(s, st, sample_time);
1932 mxf->current_edit_unit = sample_time;
1931 avio_seek(s->pb, seekpos, SEEK_SET);1933 avio_seek(s->pb, seekpos, SEEK_SET);
1932 }1934 }
1933 return 0;1935 return 0;
tests/ref/seek/lavf_mxf_d10+18−18
@@ -2,52 +2,52 @@ ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 6144 size:150000 · 1 unmodified line
2ret: 0 st:-1 flags:0 ts:-1.0000002ret: 0 st:-1 flags:0 ts:-1.000000
3ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 6144 size:1500003ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 6144 size:150000
4ret: 0 st:-1 flags:1 ts: 1.8941674ret: 0 st:-1 flags:1 ts: 1.894167
5ret:-15ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000
6ret: 0 st: 0 flags:0 ts: 0.8000006ret: 0 st: 0 flags:0 ts: 0.800000
7ret:-17ret: 0 st: 0 flags:1 dts: 0.800000 pts: 0.800000 pos:4265984 size:150000
8ret: 0 st: 0 flags:1 ts:-0.3200008ret: 0 st: 0 flags:1 ts:-0.320000
9ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 6144 size:1500009ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 6144 size:150000
10ret: 0 st: 1 flags:0 ts: 2.56000010ret: 0 st: 1 flags:0 ts: 2.560000
11ret:-111ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000
12ret: 0 st: 1 flags:1 ts: 1.48000012ret: 0 st: 1 flags:1 ts: 1.480000
13ret:-113ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000
14ret: 0 st:-1 flags:0 ts: 0.36500214ret: 0 st:-1 flags:0 ts: 0.365002
15ret: 0 st: 0 flags:1 dts: 0.360000 pts: 0.360000 pos: 6144 size:15000015ret: 0 st: 0 flags:1 dts: 0.360000 pts: 0.360000 pos:1923072 size:150000
16ret: 0 st:-1 flags:1 ts:-0.74083116ret: 0 st:-1 flags:1 ts:-0.740831
17ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 6144 size:15000017ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 6144 size:150000
18ret: 0 st: 0 flags:0 ts: 2.16000018ret: 0 st: 0 flags:0 ts: 2.160000
19ret:-119ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000
20ret: 0 st: 0 flags:1 ts: 1.04000020ret: 0 st: 0 flags:1 ts: 1.040000
21ret:-121ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000
22ret: 0 st: 1 flags:0 ts:-0.04000022ret: 0 st: 1 flags:0 ts:-0.040000
23ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 6144 size:15000023ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 6144 size:150000
24ret: 0 st: 1 flags:1 ts: 2.84000024ret: 0 st: 1 flags:1 ts: 2.840000
25ret:-125ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000
26ret: 0 st:-1 flags:0 ts: 1.73000426ret: 0 st:-1 flags:0 ts: 1.730004
27ret:-127ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000
28ret: 0 st:-1 flags:1 ts: 0.62417128ret: 0 st:-1 flags:1 ts: 0.624171
29ret:-129ret: 0 st: 0 flags:1 dts: 0.640000 pts: 0.640000 pos:3414016 size:150000
30ret: 0 st: 0 flags:0 ts:-0.48000030ret: 0 st: 0 flags:0 ts:-0.480000
31ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 6144 size:15000031ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 6144 size:150000
32ret: 0 st: 0 flags:1 ts: 2.40000032ret: 0 st: 0 flags:1 ts: 2.400000
33ret:-133ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000
34ret: 0 st: 1 flags:0 ts: 1.32000034ret: 0 st: 1 flags:0 ts: 1.320000
35ret:-135ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000
36ret: 0 st: 1 flags:1 ts: 0.20000036ret: 0 st: 1 flags:1 ts: 0.200000
37ret: 0 st: 0 flags:1 dts: 0.200000 pts: 0.200000 pos: 6144 size:15000037ret: 0 st: 0 flags:1 dts: 0.200000 pts: 0.200000 pos:1071104 size:150000
38ret: 0 st:-1 flags:0 ts:-0.90499438ret: 0 st:-1 flags:0 ts:-0.904994
39ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 6144 size:15000039ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 6144 size:150000
40ret: 0 st:-1 flags:1 ts: 1.98917340ret: 0 st:-1 flags:1 ts: 1.989173
41ret:-141ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000
42ret: 0 st: 0 flags:0 ts: 0.88000042ret: 0 st: 0 flags:0 ts: 0.880000
43ret:-143ret: 0 st: 0 flags:1 dts: 0.880000 pts: 0.880000 pos:4691968 size:150000
44ret: 0 st: 0 flags:1 ts:-0.24000044ret: 0 st: 0 flags:1 ts:-0.240000
45ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 6144 size:15000045ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 6144 size:150000
46ret: 0 st: 1 flags:0 ts: 2.68000046ret: 0 st: 1 flags:0 ts: 2.680000
47ret:-147ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000
48ret: 0 st: 1 flags:1 ts: 1.56000048ret: 0 st: 1 flags:1 ts: 1.560000
49ret:-149ret: 0 st: 0 flags:1 dts: 0.960000 pts: 0.960000 pos:5117952 size:150000
50ret: 0 st:-1 flags:0 ts: 0.46000850ret: 0 st:-1 flags:0 ts: 0.460008
51ret: 0 st: 0 flags:1 dts: 0.480000 pts: 0.480000 pos: 6144 size:15000051ret: 0 st: 0 flags:1 dts: 0.480000 pts: 0.480000 pos:2562048 size:150000
52ret: 0 st:-1 flags:1 ts:-0.64582552ret: 0 st:-1 flags:1 ts:-0.645825
53ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 6144 size:15000053ret: 0 st: 0 flags:1 dts: 0.000000 pts: 0.000000 pos: 6144 size:150000

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t