Skip to content

Stream and Channel Mapping Support#2015

Open
reinecke wants to merge 8 commits intoAcademySoftwareFoundation:mainfrom
reinecke:feature/stream_channel_mapping_support
Open

Stream and Channel Mapping Support#2015
reinecke wants to merge 8 commits intoAcademySoftwareFoundation:mainfrom
reinecke:feature/stream_channel_mapping_support

Conversation

@reinecke
Copy link
Copy Markdown
Collaborator

@reinecke reinecke commented Apr 4, 2026

Link the Issue(s) this Pull Request is related to.

If there is an associated issue, link it in the form:

Fixes #145

Summarize your change.

To for an explanation of the implementation, check out the docs.

This PR implements the proposal that was created to address #145 and add the ability to do stream/channel addressing and mapping from sources into the composition.

The C++ implementation of the mappings was built following the pattern of Clip.media_references.

Reference associated tests.

Unittests were added for the new features.

@reinecke
Copy link
Copy Markdown
Collaborator Author

reinecke commented Apr 4, 2026

@mrlimbic here is my first pass at stream/channel mapping. Please feel free to provide feedback here.

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Apr 4, 2026

Codecov Report

❌ Patch coverage is 91.91050% with 47 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.49%. Comparing base (6e13de4) to head (b3bae93).

Files with missing lines Patch % Lines
...entimelineio-bindings/otio_serializableObjects.cpp 80.00% 23 Missing ⚠️
src/opentimelineio/streamInfo.h 60.00% 2 Missing ⚠️
...timelineio/opentimelineio/algorithms/track_algo.py 0.00% 2 Missing ⚠️
...melineio/opentimelineio/schema/audio_mix_matrix.py 75.00% 2 Missing ⚠️
...neio/opentimelineio/schema/index_stream_address.py 75.00% 2 Missing ⚠️
...timelineio/opentimelineio/schema/stream_address.py 75.00% 2 Missing ⚠️
...neio/schema/stream_channel_index_stream_address.py 75.00% 2 Missing ⚠️
...pentimelineio/opentimelineio/schema/stream_info.py 75.00% 2 Missing ⚠️
...ntimelineio/opentimelineio/schema/stream_mapper.py 75.00% 2 Missing ⚠️
...imelineio/opentimelineio/schema/stream_selector.py 75.00% 2 Missing ⚠️
... and 4 more
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #2015      +/-   ##
==========================================
+ Coverage   85.14%   85.49%   +0.34%     
==========================================
  Files         181      205      +24     
  Lines       12780    13354     +574     
  Branches     1206     1217      +11     
==========================================
+ Hits        10882    11417     +535     
- Misses       1715     1753      +38     
- Partials      183      184       +1     
Flag Coverage Δ
py-unittests 85.49% <91.91%> (+0.34%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/opentimelineio/audioMixMatrix.cpp 100.00% <100.00%> (ø)
src/opentimelineio/indexStreamAddress.cpp 100.00% <100.00%> (ø)
src/opentimelineio/indexStreamAddress.h 100.00% <100.00%> (ø)
src/opentimelineio/mediaReference.cpp 100.00% <100.00%> (ø)
src/opentimelineio/mediaReference.h 100.00% <ø> (ø)
src/opentimelineio/streamAddress.cpp 100.00% <100.00%> (ø)
...opentimelineio/streamChannelIndexStreamAddress.cpp 100.00% <100.00%> (ø)
...c/opentimelineio/streamChannelIndexStreamAddress.h 100.00% <100.00%> (ø)
src/opentimelineio/streamInfo.cpp 100.00% <100.00%> (ø)
src/opentimelineio/streamMapper.cpp 100.00% <100.00%> (ø)
... and 22 more

... and 3 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 6e13de4...b3bae93. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

reinecke added 8 commits April 7, 2026 17:49
Signed-off-by: Eric Reinecke <ereinecke@netflix.com>
Signed-off-by: Eric Reinecke <ereinecke@netflix.com>
Signed-off-by: Eric Reinecke <ereinecke@netflix.com>
Signed-off-by: Eric Reinecke <ereinecke@netflix.com>
Signed-off-by: Eric Reinecke <ereinecke@netflix.com>
Signed-off-by: Eric Reinecke <ereinecke@netflix.com>
Signed-off-by: Eric Reinecke <ereinecke@netflix.com>
Signed-off-by: Eric Reinecke <ereinecke@netflix.com>
@reinecke reinecke force-pushed the feature/stream_channel_mapping_support branch from 0d6c0cf to b3bae93 Compare April 7, 2026 22:50
Copy link
Copy Markdown
Contributor

@darbyjohnston darbyjohnston left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall the C++ code looks fine, it seems to follow all of our conventions, I just left a few minor comments. Thanks for including the Doxygen markup and export macros.

I only skimmed the Python code, it would be good to get someone with more Python experience to take a look.

At some point it might be nice to add C++ tests, but for now the Python tests exercise the functionality.


## Overview

OTIO's stream and channel mapping support allows media references to describe the individual streams they contain (video eyes, audio channels, etc.) and for timeline items to select or remix those streams via effects. The system has three layers: **addressing** a stream within a container, **describing** a stream's semantic role, and **selecting or mixing** streams on a clip.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't seen "video eyes" before, is that a convention?

2. **Additional streams** SHOULD prefix an `Identifier` value to form a unique key within the media reference.

```python
# A music stem alongside the audio mix
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought this was a typo ('stem' -> 'stream'), but I guess stem is an audio term?

| Class | Use when |
|---|---|
| `IndexStreamAddress` | The container uses a single integer index per stream (e.g. WAV channel index, ffmpeg stream index) |
| `StreamChannelIndexStreamAddress` | The container organises media into tracks, each with one or more channels (e.g. MP4/MOV, MXF) |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we simplify this class name a bit, maybe ChannelStreamAddress?

/// @brief This struct provides the AudioMixMatrix schema.
struct Schema
{
static char constexpr name[] = "AudioMixMatrix";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I like the explicit type here vs. the static auto constexpr name we have in the rest of the headers. :)

OTIO_API explicit IndexStreamAddress(int64_t index = 0);

/// @brief Return the stream index.
OTIO_API int64_t index() const noexcept { return _index; }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if the export macros are needed on inline functions, we should double check that.


using Parent = SerializableObject;

OTIO_API StreamAddress();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could use = default here to save a bit of code since the constructor is empty. Just a suggestion though, I'm not sure we use default anywhere else yet. At some point we could also do a separate pass over the code replacing empty ctors/dtors with default.

/// @brief An effect that selects specific named output streams from an item.
///
/// Use this to select a stereo view, specific audio channels, etc.
/// The clip will expose these streams out with the same naming.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor, but I thought the wording on this was a little confusing:

The clip will expose these streams out with the same naming.

Maybe:

The stream names match the names within the clip.


using Parent = StreamAddress;

OTIO_API explicit StringStreamAddress(std::string const& address = std::string());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor, but we could add a Doxygen comment here for completeness:

/// @brief Create a new StringStreamAddress.

from . import (
track_algo
)
from . import track_algo
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the changes in timeline_algo.py and track_algo.py are just formatting?


Stream effects are `Effect` subclasses that attach to a clip's `effects` list and transform which streams are visible downstream and under what names. They operate on the stream identifiers declared in `available_streams`.

### StreamSelector
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious if we need the StreamSelector class, it seems like you could do the same thing with StreamMapper?

@darbyjohnston
Copy link
Copy Markdown
Contributor

Just to continue the discussion from today's OTIO meeting:

If multiple media references expose different stream information, how is that handled by the stream effects?

For example, say you have a clip with two media references, one with 5.1 audio and the other with mono:

  • Clip
    • Media Reference: "5.1"
      • Stream Info: "left"
      • Stream Info: "surround_center_front"
      • ...
    • Media Reference: "mono"
      • Stream Info: "monaural"

How would you use an AudioMixMatrix effect to make the clip stereo? Either use one effect with all of the streams:

  • AudioMixMatrix
    • "left"
      • "left", 1.0
      • "surround_center_front", 0.707
      • ...
      • "monaural", 1.0
    • "right"
      • "right", 1.0
      • "surround_center_front", 0.707
      • ...
      • "monaural", 1.0

Or use multiple AudioMixMatrix effects?

  • AudioMixMatrix
    • "left"
      • "left", 1.0
      • "surround_center_front", 0.707
      • ...
    • "right"
      • "right", 1.0
      • "surround_center_front", 0.707
      • ...
  • AudioMixMatrix
    • "left"
      • "monaural", 1.0
    • "right"
      • "monaural", 1.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Media Reference Stream/Channel Specification support

3 participants