238 const auto resources =
playback.resources();
239 if (!resources)
return std::unexpected(
"video presentation has no resource snapshot");
241 std::set<sequence::SequenceNodeId>
live;
244 for (
auto it = active_.begin();
it != active_.end();)
246 if (
live.contains(
it->first)) { ++
it;
continue; }
251 if (!stopped)
return std::unexpected(stopped.error());
254 it = active_.erase(
it);
257 states_.reserve(
intents.size());
258 std::vector<SequenceJumpNotice>
jumps;
262 if (
found == active_.end())
265 if (!file)
return std::unexpected(file.error());
270 created.presentation = std::make_shared<Presentation>();
280 if (!
entry.presentation)
281 return std::unexpected(
"cannot mix metadata-only and decoded video bridge sessions");
282 auto snapshot =
entry.presentation->snapshot();
284 return std::unexpected(snapshot.error);
285 if (!snapshot.metadata)
291 const auto&
metadata = *snapshot.metadata;
292 if (!
entry.runtime.open())
296 return std::unexpected(
"decoded video has invalid duration or frame cadence");
297 const auto duration = (1000000ULL *
metadata.frameRateDenominator +
299 const long double frames = std::ceil(
300 static_cast<long double>(
metadata.durationMicroseconds) *
301 metadata.frameRateNumerator / (1000000.0L *
metadata.frameRateDenominator));
302 if (!duration || duration > std::numeric_limits<std::uint32_t>::max() ||
304 return std::unexpected(
"decoded video timeline exceeds runtime range");
306 static_cast<std::uint32_t
>(duration),
static_cast<std::uint32_t
>(frames),
311 if (!bounds)
return std::unexpected(bounds.error());
314 const auto durationTicks = std::ceil(
static_cast<long double>(
317 return std::unexpected(
"decoded video duration exceeds sequence clock range");
318 entry.durationTicks =
static_cast<std::int32_t
>(durationTicks);
319 entry.clipDurationMicroseconds =
metadata.durationMicroseconds;
325 entry.clipDurationMicroseconds = std::min(
331 if (
entry.lastSequenceClock != std::numeric_limits<std::int32_t>::min() &&
335 if (!
frame)
return std::unexpected(
frame.error());
336 const auto cut =
entry.runtime.cutToFrame(
337 std::min(*
frame,
entry.runtime.status().numberOfFrames - 1));
338 if (!
cut)
return std::unexpected(
cut.error());
339 entry.requestedSeek =
static_cast<std::uint64_t
>(
entry.runtime.status().currentFrame) *
340 entry.runtime.metadata().microsecondsPerFrame;
341 entry.seekPending =
true;
343 if (
entry.seekPending)
347 entry.seekPending =
false;
351 node = node->parent ?
playback.runtime().inspect(node->parent) : std::nullopt)
352 paused = paused || node->paused;
353 const auto gain =
entry.presentation->setGain(
354 static_cast<float>(
intent.volume) / 100.0F);
355 if (!gain)
return std::unexpected(gain.error());
357 return std::unexpected(
358 "video audio source sample rate is unavailable for legacy pitch");
359 const auto pitch =
entry.presentation->setPitch(
361 if (!
pitch)
return std::unexpected(
pitch.error());
363 const auto clock =
entry.presentation->pump(
365 if (!clock)
return std::unexpected(clock.error());
366 const auto desired =
entry.runtime.frameAtElapsed(std::min(clock->elapsedMicroseconds,
367 entry.clipDurationMicroseconds > 0 ?
entry.clipDurationMicroseconds - 1 : 0));
369 if (!
entry.runtime.status().ended)
371 const auto fed =
entry.runtime.feedToFrame(
372 std::min(*
desired,
entry.runtime.status().numberOfFrames - 1));
373 if (!
fed)
return std::unexpected(
fed.error());
376 if (
auto event =
entry.runtime.takeJumpEvent())
379 if (event->alternativeTaken)
381 const auto timestamp =
static_cast<std::uint64_t
>(
event->jumpToFrame) *
382 entry.runtime.metadata().microsecondsPerFrame;
389 static_cast<std::uint64_t
>(
entry.runtime.status().currentFrame) *
390 entry.runtime.metadata().microsecondsPerFrame : std::min(clock->elapsedMicroseconds,
391 entry.clipDurationMicroseconds > 0 ?
entry.clipDurationMicroseconds - 1 : 0);
393 std::max(
entry.runtime.status().currentFrame, 0)) + 1U;
395 entry.runtime.metadata().microsecondsPerFrame - 1;
397 if (!
frame)
return std::unexpected(
frame.error());
398 if (
entry.options.enableVideo)
402 if (!transform)
return std::unexpected(transform.error());
403 const bool moved =
entry.surfaceVisible &&
404 (
entry.publishedTransform.values != transform->values ||
406 const std::size_t commands = !
entry.surfaceVisible && *
frame ? 1U :
407 moved ? (
entry.publishedPriority !=
intent.priority ? 2U : 1U) : 0
U;
409 return std::unexpected(
"sequence command queue cannot fit decoded video presentation");
413 if (!
image)
return std::unexpected(
image.error());
421 const auto updated =
playback.runtimeBitmaps().update(*
entry.surface, std::move(*
image));
422 if (!updated)
return std::unexpected(updated.error());
424 if (
entry.surface && (!
entry.surfaceVisible || moved))
429 if (!stopped)
return std::unexpected(stopped.error());
430 entry.surfaceVisible =
false;
436 entry.surfaceVisible =
true;
437 entry.publishedTransform = *transform;
441 snapshot =
entry.presentation->snapshot();
443 entry.presentation->videoDrained() && clock->audioDrained &&
444 clock->elapsedMicroseconds >=
metadata.durationMicroseconds;
446 (
entry.authoredClip && clock->elapsedMicroseconds >=
entry.clipDurationMicroseconds));
448 if (ended && (!
entry.runtime.status().ended ||
entry.loop))
454 const auto cut =
entry.runtime.cutToFrame(0);
455 if (!
cut)
return std::unexpected(
cut.error());
459 const auto fed =
entry.runtime.feedToFrame(
entry.runtime.status().numberOfFrames);
460 if (!
fed)
return std::unexpected(
fed.error());
469 if (
entry.surfaceVisible)
472 if (!stopped)
return std::unexpected(stopped.error());
475 entry.surface.reset();
476 entry.surfaceVisible =
false;
479 const auto mediaTicks =
static_cast<std::int32_t
>(std::min<long double>(
481 entry.durationTicks));
485 entry.lastSequenceClock =
intent.elapsedParentClock;
487 entry.boundingBox,
intent.worldTransform, clock->waitingForAudio,
488 true,
entry.surface, clock->consumedAudioBytes});
LegacyBitmapMetadata metadata
std::vector< std::byte > bytes