Blog/Map transcript timestamps back to source media
OverviewAll posts
Tutorial

Map transcript timestamps back to source media

Map clip transcript timestamps to the original recording with a tested Python offset helper, explicit timeline origins, and limits for edited audio.

VTornado API team
Covered in this article
Define the timeline before adding an offset
Map a segment with bounded decimal inputs
Recognize when one offset is insufficient
6 min reading time
Published November 6, 2026
VProduct guides by Velys Software

A transcript produced from an excerpt usually describes positions inside that excerpt. To link a sentence back to the original recording, add the source position that corresponds to clip time zero to both ends of the sentence. This works only when the excerpt is continuous and plays at the original speed.

For example, if clip time zero corresponds to 120.250 seconds in the source, a segment at 2.500–5.750 seconds maps to 122.750–126.000 seconds. These are illustrative values, not measurements from a Tornado job. The useful part is preserving the relationship between the two timelines before a user clicks a transcript result.

Define the timeline before adding an offset

Store an explicit unit and origin for every timestamp. In this guide, all values are decimal seconds from the start of the relevant playable artifact. They are not wall-clock times, frame numbers, or raw container presentation timestamps.

Keep these fields in your application's own record:

FieldPurpose
Source artifact ID and revisionIdentify the exact recording being referenced
Clip artifact IDIdentify the file actually sent for transcription
Source origin in secondsSource position corresponding to clip time zero
Clip duration in secondsValidate the local transcript interval
Local segment start and endPreserve the transcription provider's original result
Transformation recordExplain trims, edits, or speed changes

These are suggested application fields, not Tornado API response fields. Keep the original local timestamps when you calculate source positions; replacing them makes later diagnosis harder.

A transcript provider may use different units or return timestamps relative to an already processed input. Normalize that contract first. If it returns milliseconds, convert them to seconds once at the boundary and record that conversion. Do not infer the unit from the size of a number.

Use the actual clip origin

The requested trim position is not automatically proof of where the delivered media begins. The FFmpeg seeking documentation explains that input seeking can land at an earlier seek point. Accurate seeking during transcoding discards the intervening segment, while stream copying can preserve it. This is a general editing caveat, not a claim about a particular Tornado job.

Establish the mapping using the transformation's documented behavior and inspect representative boundaries in the resulting artifact. Record the uncertainty if the start is not known precisely. Adding more decimal places cannot resolve an uncertain origin.

If the source player and audio stream have different zero points, reconcile those before generating clickable links. A source position must refer to the timeline the destination player actually uses.

Map a segment with bounded decimal inputs

This Python example accepts nonnegative decimal-second strings, with at most nine integer digits and six fractional digits. That deliberately bounded contract keeps arithmetic straightforward. It rejects floats, special values, negative times, reversed or empty intervals, and segments beyond the supplied clip duration.

import re
from decimal import Decimal, localcontext

_SECONDS = re.compile(r"[0-9]{1,9}(?:\.[0-9]{1,6})?", re.ASCII)


def seconds(value):
    if not isinstance(value, str) or not _SECONDS.fullmatch(value):
        raise ValueError("expected bounded nonnegative decimal seconds")
    return Decimal(value)


def map_segment(origin_s, duration_s, start_s, end_s):
    origin, duration, start, end = map(
        seconds, (origin_s, duration_s, start_s, end_s)
    )
    if not Decimal(0) <= start < end <= duration:
        raise ValueError("segment must fit inside the clip")
    with localcontext() as context:
        context.prec = 28
        return {
            "source_start_s": format(origin + start, "f"),
            "source_end_s": format(origin + end, "f"),
        }


print(map_segment("120.250", "30.000", "2.500", "5.750"))
# {'source_start_s': '122.750', 'source_end_s': '126.000'}

The Python decimal documentation describes exact decimal representation and configurable arithmetic precision. Passing decimal strings avoids importing a binary float's approximation. The input bounds here ensure these additions fit within the explicitly selected precision. This is a numeric policy, not a claim of microsecond transcription accuracy.

The example performs no media parsing, transcription request, seeking, or network call. Validate it with synthetic offsets and boundary cases, then separately check the correspondence against your actual audio and player. A correct addition does not prove a correct speech alignment.

Recognize when one offset is insufficient

Suppose an edited clip contains source seconds 100–110 followed immediately by source seconds 200–210. Clip second 5 maps to source second 105, but clip second 15 maps to source second 205. Adding 100 to every position would mislabel the second section.

Use a piecewise edit map when the input contains cuts or concatenation. Each continuous interval needs its own source identity and mapping. A transcript segment crossing a cut cannot always be represented by one continuous source interval; split or flag it for review instead of fabricating uninterrupted evidence.

Speed changes also require a scale factor. Removed silence introduces discontinuities. Overlapping transcription chunks can produce duplicate text near shared boundaries even when every offset is correct. Keep chunk identity and handle overlap separately; chronological sorting alone does not deduplicate speech.

For plain, continuous excerpts, reject unexpected edits rather than silently applying the simple helper. If you export captions, use the local timeline for playback of the clip and the mapped timeline for playback of the original. Our SRT timing validation guide covers interval validation before caption import.

Connect the mapping to your media workflow

In a Tornado workflow, retain the ingestion job ID and confirm the delivered artifact before passing it to your transcription service. The public job status reference documents retrieval of a job's status and delivery information. A delivery result is not a transcript or a guarantee that timestamps have been aligned to your source player.

Associate your mapping with the specific file sent downstream, alongside the job ID and transcription run ID. Keep durable artifact identity separate from a temporary access URL. The guide to audio output for transcription helps validate the input format before this handoff.

Before enabling transcript navigation, review a sentence near the beginning, one in the middle, and one near the end. Test a clip that starts after source time zero, and check the destination player against the same source revision. If the error grows across the clip, investigate rate changes or drift; if it jumps at a boundary, investigate edits. These checks help locate a mapping problem but do not certify every transcript segment.

Start with one recording you are authorized to process. Follow the first media workflow, preserve the artifact relationship, and add source-linked transcript navigation only after the local and source timelines agree.