Direct answer
MediaCodec/BufferInfo and synchronous INFO_OUTPUT_FORMAT_CHANGED are API 16; AudioTimestamp.nanoTime is API 19; getOutputBuffer and asynchronous callbacks are API 21; KEY_PCM_ENCODING is API 24. Older synchronous paths use buffer arrays plus BufferInfo ranges. Pin the target API and decode path; do not assume later signals on earlier releases. A MediaCodec output BufferInfo carries offset and size for valid output bytes, flags, and presentationTimeUs in microseconds. That output timestamp is derived from the timestamp passed with its corresponding input buffer; it is media presentation time, not a capture clock. MediaExtractor.getSampleTime is also media PTS in microseconds. By contrast, AudioTimestamp.nanoTime is in nanoseconds and, for AudioRecord, depends on the requested monotonic or boottime clock base. Converting units does not create a shared origin or epoch. Store clock kind, unit, frame/sample association, and origin evidence separately; do not align timestamps because two fields look numerically close. For each output, capture its actual MediaFormat and buffer metadata before releasing the index. On API 21+, getOutputBuffer returns a ByteBuffer whose position and limit are set to valid data; with older array-based paths, validate BufferInfo offset and size against the selected buffer. Observe INFO_OUTPUT_FORMAT_CHANGED in synchronous mode or the API-21 callback in asynchronous mode. Record sample rate and channel count, and read PCM encoding only where the API provides it. A zero-sized output has no sample window and its PTS must be ignored. An EOS flag may accompany non-empty output: consume that valid byte range first, then mark the stream terminal. A returned format change starts a new format epoch. Keep requested and returned MediaFormat separate; record sample rate, channel count, and PCM encoding where available, otherwise mark unknown. Never derive PCM frames from compressed bytes or an earlier epoch. After a format change, recheck frame alignment and downstream representation. A MediaExtractor seek moves the extractor sample cursor; it does not by itself invalidate MediaCodec buffer indices. The application must define how the decoder consumes the post-seek input and whether its protocol flushes, reconfigures, or replaces the codec. Only the codec reset operation invalidates that codec generation. If the owner calls flush, invalidate pre-flush indices and queued associations, and begin a new application epoch only after a serialized transition establishes which outputs belong to it. Do not label an output callback as current merely because it arrived after the seek call. Flush needs different handling by processing mode. Android says flush immediately invalidates old input/output indices; asynchronous callbacks that were still outstanding may be delivered, but their indices are invalid and must be discarded. In asynchronous mode, call start after flush returns to resume callbacks; do not apply this extra start rule to the synchronous path. Serialize callback handling and seek/flush control through the codec owner, fence old work, and accept only buffers acquired in the new generation. If the owner cannot distinguish late events from new output safely, reset or replace the codec under a tested lifecycle instead of releasing an invalid or reused index. Derive a time window only for confirmed PCM. If output is interleaved PCM with known sample rate R, channel count C, and bytes per sample B, a frame occupies C × B bytes; size must divide evenly by that frame width before frameCount = size / (C × B) is meaningful. Under that explicit assumption, estimated end time in microseconds is startPtsUs + round(frameCount × 1,000,000 / R). Use checked arithmetic and record the rounding rule. Do not apply it to compressed frames, unknown planar packing, zero-size EOS, partial data, or a buffer whose format epoch is unknown. Fixtures should exercise the full owner protocol: API and driver identity, input PTS, output range, format changes, delayed output, zero-size EOS, non-empty EOS, extractor seek without codec reset, seek followed by flush, and a late asynchronous callback. Assert that old indices are discarded and new-generation buffers are not misattributed. Record PTS in microseconds and AudioTimestamp.nanoTime in nanoseconds with its selected timebase, but do not infer a conversion mapping without independent evidence. This guide defines a decoder-output method, not Cove integration, microphone synchronization, model compatibility, or device performance.
A decoder seek makes an old audio chunk appear current
An Android app decodes a local compressed recording and forwards PCM chunks to a later stage. After an extractor seek, a callback arrives while metadata from the first pass is still pending. Decoder PTS is in microseconds, while AudioTimestamp.nanoTime is in nanoseconds with a clock base; the app has not modeled codec flush, output format changes, or a verified mapping between them.
Decision
Represent each decoded output window with buffer-range evidence, effective format, presentation timestamp, flags, and an app-owned seek epoch; discard stale callbacks from earlier epochs.
Expected outcome
Downstream chunks are ordered or marked discontinuous according to measured decoder output, without fabricating a continuous capture-time timeline or claiming a model-quality result.
Evidence to collect
The claim becomes reviewable only when the following evidence is attached to the same artifact and test run:
- Output-format epochs and PCM properties
- Offset/size/EOS buffer fixtures
- Seek, flush, and PTS discontinuity records
A release claim is valid only for the recorded configuration and acceptance set. Generalization requires additional records.
Implementation workflow
- Declare clocks, API levels, and generations. Record media PTS in microseconds, AudioTimestamp.nanoTime in nanoseconds plus its clock base, and app processing time separately. BufferInfo is API 16; getOutputBuffer and async callbacks are API 21; KEY_PCM_ENCODING is API 24. Start a new decoder generation only after the owner performs a tested flush/reconfigure/reset protocol, not merely on MediaExtractor.seekTo.
- Snapshot actual format and buffer metadata. Capture the returned MediaFormat and its sample rate/channel count; read KEY_PCM_ENCODING only on supported APIs. On API 21+, getOutputBuffer position/limit identify valid data; on older synchronous paths use BufferInfo offset/size against the selected array buffer. Preserve PTS/flags before releasing the index.
- Validate output bytes and EOS ordering. Check the valid byte range against the current buffer. Ignore PTS for zero-size EOS. If EOS accompanies non-empty output, process that range before marking terminal. Derive frames only for confirmed PCM with aligned size and known channels/encoding.
- Fence a codec reset from late callbacks. MediaExtractor.seekTo alone moves the sample cursor; it does not invalidate codec indices. If the seek protocol flushes the codec, indices from that codec generation become invalid. In async mode, discard outstanding callbacks and call start after flush returns; serialize owner/callback handling so stale events are not stamped as new output.
- Prove units and ownership with fixtures. Record input/output PTS, byte ranges, format events, flags, clock units/bases, and codec generations for empty and non-empty EOS, extractor seek without flush, seek with flush, late async callback and format change. Preserve unknown mappings rather than deriving capture time.
Keep each transition observable. A failure should identify the stage, artifact, runtime, and recovery action without logging private user content.
Acceptance criteria
- Each decoded chunk is tied to the actual output format, valid buffer byte range, flags, PTS and application epoch.
- Frame-count or end-time calculations occur only for confirmed PCM layout, sample rate, channel count and aligned buffer size.
- MediaExtractor.seekTo alone does not invalidate codec indices; a flush/reset does. Async post-flush start and stale-callback discard are tested, and EOS bytes are consumed before terminal state.
- The guide never equates decoder PTS with AudioRecord origin, wall clock, Cove model input support, or a measured performance result.
Decision table
| Dimension | Choice or evidence | How to interpret it |
|---|---|---|
| Decoder output PTS | Retain `BufferInfo.presentationTimeUs` as a media timeline value. | It is derived from input presentation timestamps and is not automatically a capture-clock or wall-clock timestamp. |
| Application receive time | Record callback/dequeue time separately for diagnostics. | Useful for scheduling observations, but not a substitute for the media timestamp attached to decoded output. |
| AudioRecord timestamp | Keep AudioTimestamp.nanoTime, framePosition and MONOTONIC/BOOTTIME base explicit. | It is nanoseconds, while BufferInfo PTS is microseconds; unit conversion alone does not establish a shared clock origin. |
| Derived PCM window | Estimate endUs from PTS_us plus rounded frames × 1,000,000 / sampleRate. | Only for confirmed aligned PCM; invalid for zero-size EOS, compressed data, unknown packing, or stale generation. |
Failure patterns to prevent
- Interpreting later buffers with a prior format
- Treating PTS as AudioRecord clock time
- Counting zero-size EOS as samples
Also prevent silent fallback, unpinned artifacts, missing cancellation, and conclusions that combine unlike configurations. Store unsuccessful runs alongside successful ones.
Minimum reproducibility record
| Layer | Record |
|---|---|
| Device | Manufacturer, model, chipset, RAM class, operating-system build |
| Software | Application version and git commit |
| Model | Family, variant, revision, format, file length, hash, quantization |
| Runtime | Name, revision, requested backend, observed backend evidence |
| Workload | Fixture revision, input hash, prompt hash, output policy |
| Outcome | Completed, failed, cancelled, fallback, and privacy-safe diagnostics |
Release checklist
- ☐ The primary query is answered without an unsupported number.
- ☐ Every artifact and runtime is pinned.
- ☐ The representative task and failure policy are explicit.
- ☐ Lifecycle, cancellation, cleanup, and fallback are tested.
- ☐ User-content and network boundaries are documented.
- ☐ Result wording applies only to the recorded configuration.
- ☐ The page links to raw method or evidence when results are added.
Sources and related evidence
- Cove Travel voice translation
- Android API — MediaCodec
- Android API — MediaCodec.BufferInfo
- Android API — MediaFormat
- Android API — MediaExtractor
- Android API — AudioTimestamp nanoTime and timebase
Chinese deployment and troubleshooting content is organized in the .奇连 AI 端侧专题.
Frequently asked questions
Can I align AudioTimestamp.nanoTime with BufferInfo.presentationTimeUs by converting units?
No. AudioTimestamp.nanoTime is nanoseconds and names a MONOTONIC or BOOTTIME base for AudioRecord; BufferInfo PTS is media time in microseconds derived from input PTS. Dividing by 1000 changes units, not origin, clock base, or capture-to-media mapping.
How should a non-empty EOS output be handled?
The EOS flag can accompany a valid final data buffer. First consume its checked offset/size range, then mark the decoder stream terminal. A zero-size EOS has no sample window and its PTS is ignored.
Does extractor seek invalidate codec indices, and what follows async flush?
MediaExtractor.seekTo only moves the extractor sample cursor. If the codec owner also flushes, pre-flush indices become invalid; outstanding callbacks must be discarded, not relabeled as new. Android requires start after async flush returns to resume callbacks. Neither operation promises PTS resets to zero.