Skip to content

Commit

Permalink
Add TrackFileResource EntryPoint, Duration and Repeat parameters supp…
Browse files Browse the repository at this point in the history
…ort (#32) (#29)
  • Loading branch information
valnoel authored Jul 15, 2021
2 parents 2422678 + de96d11 commit 9e8926f
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 30 deletions.
2 changes: 2 additions & 0 deletions libavformat/imf_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
#include <libxml/tree.h>

#define UUID_FORMAT "urn:uuid:%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
#define AVRATIONAL_FORMAT "%d/%d"
#define AVRATIONAL_ARG(rational) rational.num, rational.den

/**
* IMF Asset locator
Expand Down
94 changes: 64 additions & 30 deletions libavformat/imfdec.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,13 @@ typedef struct IMFVirtualTrackPlaybackCtx {
// Track index in playlist
int32_t index;
// Time counters
int64_t current_timestamp;
int64_t duration;
AVRational current_timestamp;
AVRational duration;
// Resources
unsigned int resource_count;
IMFVirtualTrackResourcePlaybackCtx **resources;
// Decoding cursors
IMFVirtualTrackResourcePlaybackCtx *current_resource;
uint32_t current_resource_index;
int64_t last_pts;
} IMFVirtualTrackPlaybackCtx;

Expand Down Expand Up @@ -185,9 +185,9 @@ static int parse_assetmap(AVFormatContext *s, const char *url, AVIOContext *in)
AVDictionary *opts = NULL;
xmlDoc *doc = NULL;

int close_in = 0;
int ret = 0;
int64_t filesize = 0;
int close_in;
int ret;
int64_t filesize;

const char *base_url = av_dirname(strdup(url));
if (c->asset_locator_map == NULL) {
Expand Down Expand Up @@ -253,6 +253,7 @@ static IMFAssetLocator *find_asset_map_locator(IMFAssetLocatorMap *asset_map, UU

static int open_track_resource_context(AVFormatContext *s, IMFVirtualTrackResourcePlaybackCtx *track_resource) {
int ret = 0;
int64_t entry_point;

if (!track_resource->ctx) {
track_resource->ctx = avformat_alloc_context();
Expand All @@ -275,6 +276,22 @@ static int open_track_resource_context(AVFormatContext *s, IMFVirtualTrackResour
goto cleanup;
}

// Compare the source timebase to the resource edit rate, considering the first stream of the source file
if (av_cmp_q(track_resource->ctx->streams[0]->time_base, av_inv_q(track_resource->resource->base.edit_rate))) {
av_log(s, AV_LOG_WARNING, "Incoherent source stream timebase %d/%d regarding resource edit rate: %d/%d", track_resource->ctx->streams[0]->time_base.num, track_resource->ctx->streams[0]->time_base.den, track_resource->resource->base.edit_rate.den, track_resource->resource->base.edit_rate.num);
}

entry_point = (int64_t)track_resource->resource->base.entry_point * track_resource->resource->base.edit_rate.den * AV_TIME_BASE / track_resource->resource->base.edit_rate.num;

if (entry_point) {
av_log(s, AV_LOG_DEBUG, "Seek at resource %s entry point: %ld\n", track_resource->locator->absolute_uri, track_resource->resource->base.entry_point);
ret = avformat_seek_file(track_resource->ctx, -1, entry_point, entry_point, entry_point, 0);
if (ret < 0) {
av_log(s, AV_LOG_ERROR, "Could not seek at %ld on %s: %s\n", entry_point, track_resource->locator->absolute_uri, av_err2str(ret));
goto cleanup;
}
}

return ret;
cleanup:
avformat_free_context(track_resource->ctx);
Expand All @@ -286,7 +303,7 @@ static int open_track_file_resource(AVFormatContext *s, IMFTrackFileResource *tr
IMFVirtualTrackResourcePlaybackCtx *track_resource;
IMFAssetLocator *asset_locator;

int ret = 0;
int ret;

if (!(asset_locator = find_asset_map_locator(c->asset_locator_map, track_file_resource->track_file_uuid))) {
av_log(s, AV_LOG_ERROR, "Could not find asset locator for UUID: " UUID_FORMAT "\n", UID_ARG(track_file_resource->track_file_uuid));
Expand All @@ -303,9 +320,11 @@ static int open_track_file_resource(AVFormatContext *s, IMFTrackFileResource *tr
return ret;
}

track->resources = av_realloc(track->resources, track->resource_count + 1 * sizeof(IMFVirtualTrackResourcePlaybackCtx));
track->resources[track->resource_count++] = track_resource;
track->duration += track_resource->ctx->duration;
for (int repetition = 0; repetition < track_file_resource->base.repeat_count; ++repetition) {
track->resources = av_realloc(track->resources, (track->resource_count + 1) * sizeof(IMFVirtualTrackResourcePlaybackCtx));
track->resources[track->resource_count++] = track_resource;
track->duration = av_add_q(track->duration, av_make_q((int)track_resource->resource->base.duration * track_resource->resource->base.edit_rate.den, track_resource->resource->base.edit_rate.num));
}

return ret;
}
Expand All @@ -317,6 +336,7 @@ static int open_virtual_track(AVFormatContext *s, IMFTrackFileVirtualTrack *virt

track = av_mallocz(sizeof(IMFVirtualTrackPlaybackCtx));
track->index = track_index;
track->duration = av_make_q(0, INT32_MAX);

for (int i = 0; i < virtual_track->resource_count; i++) {
av_log(s, AV_LOG_DEBUG, "Open stream from file " UUID_FORMAT ", stream %d\n", UID_ARG(virtual_track->resources[i].track_file_uuid), i);
Expand All @@ -326,7 +346,9 @@ static int open_virtual_track(AVFormatContext *s, IMFTrackFileVirtualTrack *virt
}
}

c->tracks = av_realloc(c->tracks, c->track_count + 1 * sizeof(IMFVirtualTrackPlaybackCtx));
track->current_timestamp = av_make_q(0, track->duration.den);

c->tracks = av_realloc(c->tracks, (c->track_count + 1) * sizeof(IMFVirtualTrackPlaybackCtx));
c->tracks[c->track_count++] = track;

return ret;
Expand All @@ -344,6 +366,7 @@ static void imf_virtual_track_playback_context_free(IMFVirtualTrackPlaybackCtx *

if (track->resources[i]->ctx && track->resources[i]->ctx->iformat) {
avformat_free_context(track->resources[i]->ctx);
track->resources[i]->ctx = NULL;
}
}
}
Expand All @@ -370,6 +393,7 @@ static int set_context_streams_from_tracks(AVFormatContext *s) {
break;
}
avpriv_set_pts_info(asset_stream, first_resource_stream->pts_wrap_bits, first_resource_stream->time_base.num, first_resource_stream->time_base.den);
asset_stream->duration = (int64_t)av_q2d(av_mul_q(c->tracks[i]->duration, av_inv_q(asset_stream->time_base)));
}

return ret;
Expand Down Expand Up @@ -446,42 +470,41 @@ static IMFVirtualTrackPlaybackCtx *get_next_track_with_minimum_timestamp(AVForma
IMFContext *c = s->priv_data;
IMFVirtualTrackPlaybackCtx *track;

int64_t minimum_timestamp = INT64_MAX;
AVRational minimum_timestamp = av_make_q(INT32_MAX, 1);
for (int i = 0; i < c->track_count; ++i) {
av_log(s, AV_LOG_DEBUG, "Compare track %p timestamp %ld to minimum %ld (over duration: %ld)\n", c->tracks[i], c->tracks[i]->current_timestamp, minimum_timestamp, c->tracks[i]->duration);
if (c->tracks[i]->current_timestamp < minimum_timestamp) {
av_log(s, AV_LOG_DEBUG, "Compare track %d timestamp " AVRATIONAL_FORMAT " to minimum " AVRATIONAL_FORMAT " (over duration: " AVRATIONAL_FORMAT ")\n", i, AVRATIONAL_ARG(c->tracks[i]->current_timestamp), AVRATIONAL_ARG(minimum_timestamp), AVRATIONAL_ARG(c->tracks[i]->duration));
if (av_cmp_q(c->tracks[i]->current_timestamp, minimum_timestamp) < 0) {
track = c->tracks[i];
minimum_timestamp = track->current_timestamp;
}
}

av_log(s, AV_LOG_DEBUG, "Found next track to read: %d (timestamp: %ld / %ld)\n", track->index, track->current_timestamp, minimum_timestamp);
av_log(s, AV_LOG_DEBUG, "Found next track to read: %d (timestamp: %lf / %lf)\n", track->index, av_q2d(track->current_timestamp), av_q2d(minimum_timestamp));
return track;
}

static IMFVirtualTrackResourcePlaybackCtx *get_resource_context_for_timestamp(AVFormatContext *s, IMFVirtualTrackPlaybackCtx *track) {
unsigned long cumulated_duration = 0;
unsigned long edit_unit_duration;
AVRational edit_unit_duration = av_inv_q(track->resources[0]->resource->base.edit_rate);
AVRational cumulated_duration = av_make_q(0, edit_unit_duration.den);

av_log(s, AV_LOG_DEBUG, "Looking for track %d resource for timestamp = %ld / %ld\n", track->index, track->current_timestamp, track->duration);
av_log(s, AV_LOG_DEBUG, "Looking for track %d resource for timestamp = %lf / %lf\n", track->index, av_q2d(track->current_timestamp), av_q2d(track->duration));
for (int i = 0; i < track->resource_count; ++i) {
edit_unit_duration = track->resources[i]->resource->base.edit_rate.den * AV_TIME_BASE / track->resources[i]->resource->base.edit_rate.num;
cumulated_duration += track->resources[i]->resource->base.duration * track->resources[i]->resource->base.edit_rate.den * AV_TIME_BASE / track->resources[i]->resource->base.edit_rate.num;
cumulated_duration = av_add_q(cumulated_duration, av_make_q((int)track->resources[i]->resource->base.duration * edit_unit_duration.num, edit_unit_duration.den));

if (track->current_timestamp + edit_unit_duration <= cumulated_duration) {
av_log(s, AV_LOG_DEBUG, "Found resource %d in track %d to read for timestamp %ld (on cumulated=%ld): entry=%ld, duration=%lu, editrate=%d/%d | edit_unit_duration=%ld\n", i, track->index, track->current_timestamp, cumulated_duration, track->resources[i]->resource->base.entry_point, track->resources[i]->resource->base.duration, track->resources[i]->resource->base.edit_rate.num, track->resources[i]->resource->base.edit_rate.den, edit_unit_duration);
if (av_cmp_q(av_add_q(track->current_timestamp, edit_unit_duration), cumulated_duration) <= 0) {
av_log(s, AV_LOG_DEBUG, "Found resource %d in track %d to read for timestamp %lf (on cumulated=%lf): entry=%ld, duration=%lu, editrate=" AVRATIONAL_FORMAT " | edit_unit_duration=%lf\n", i, track->index, av_q2d(track->current_timestamp), av_q2d(cumulated_duration), track->resources[i]->resource->base.entry_point, track->resources[i]->resource->base.duration, AVRATIONAL_ARG(track->resources[i]->resource->base.edit_rate), av_q2d(edit_unit_duration));

if (track->current_resource != track->resources[i]) {
if (track->current_resource_index != i) {
av_log(s, AV_LOG_DEBUG, "Switch resource on track %d: re-open context\n", track->index);
if (track->current_resource != NULL) {
avformat_close_input(&track->current_resource->ctx);
if (track->resources[track->current_resource_index] != NULL) {
avformat_close_input(&track->resources[track->current_resource_index]->ctx);
}
if (open_track_resource_context(s, track->resources[i]) != 0) {
return NULL;
}
track->current_resource = track->resources[i];
track->current_resource_index = i;
}
return track->current_resource;
return track->resources[track->current_resource_index];
}
}
return NULL;
Expand All @@ -491,31 +514,42 @@ static int ff_imf_read_packet(AVFormatContext *s, AVPacket *pkt) {
IMFContext *c = s->priv_data;

IMFVirtualTrackResourcePlaybackCtx *resource_to_read = NULL;
AVRational edit_unit_duration;
int ret = 0;

IMFVirtualTrackPlaybackCtx *track_to_read = get_next_track_with_minimum_timestamp(s);

if (track_to_read->current_timestamp == track_to_read->duration) {
if (av_cmp_q(track_to_read->current_timestamp, track_to_read->duration) == 0) {
return AVERROR_EOF;
}

resource_to_read = get_resource_context_for_timestamp(s, track_to_read);

if (!resource_to_read) {
edit_unit_duration = av_inv_q(track_to_read->resources[track_to_read->current_resource_index]->resource->base.edit_rate);
if (av_cmp_q(av_add_q(track_to_read->current_timestamp, edit_unit_duration), track_to_read->duration) > 0) {
return AVERROR_EOF;
}

av_log(s, AV_LOG_ERROR, "Could not find IMF track resource to read\n");
return AVERROR_STREAM_NOT_FOUND;
}

while (!ff_check_interrupt(c->interrupt_callback) && !ret) {
ret = av_read_frame(resource_to_read->ctx, pkt);
av_log(s, AV_LOG_DEBUG, "Got packet: pts=%ld, dts=%ld, duration=%ld, stream_index=%d, pos=%ld,\n", pkt->pts, pkt->dts, pkt->duration, pkt->stream_index, pkt->pos);
av_log(s, AV_LOG_DEBUG, "Got packet: pts=%ld, dts=%ld, duration=%ld, stream_index=%d, pos=%ld\n", pkt->pts, pkt->dts, pkt->duration, pkt->stream_index, pkt->pos);
if (ret >= 0) {
// Update packet info from track
if (pkt->dts < s->streams[track_to_read->index]->cur_dts && track_to_read->last_pts > 0) {
pkt->dts = s->streams[track_to_read->index]->cur_dts;
}

pkt->pts = track_to_read->last_pts;
pkt->dts = pkt->dts - (int64_t)track_to_read->resources[track_to_read->current_resource_index]->resource->base.entry_point;
pkt->stream_index = track_to_read->index;

// Update track cursors
track_to_read->current_timestamp += av_rescale(pkt->duration, (int64_t)resource_to_read->ctx->streams[0]->time_base.num * AV_TIME_BASE, resource_to_read->ctx->streams[0]->time_base.den);
track_to_read->current_timestamp = av_add_q(track_to_read->current_timestamp, av_make_q((int)pkt->duration * resource_to_read->ctx->streams[0]->time_base.num, resource_to_read->ctx->streams[0]->time_base.den));
track_to_read->last_pts += pkt->duration;

return 0;
Expand Down

0 comments on commit 9e8926f

Please sign in to comment.