Replace unstructured Task {} in MediaMixerOutput and StreamOutput
conformances with AsyncStream channels that preserve FIFO ordering.
The previous pattern created a new Task for each audio/video callback,
which provides no ordering guarantee when entering an actor's serial
executor. This caused adjacent frames to arrive out of order, resulting
in RTMPTimestamp.invalidSequence errors (silent frame drops) and
AVAssetWriter failures in StreamRecorder.
* feat: Add framerate to RTMP onMetaData
Adds framerate field to RTMP onMetaData for RTMP servers like
nginx-rtmp-module to correctly parse stream frame rate statistics.
The framerate value is synchronized from VideoCodec.expectedFrameRate
when MediaMixer.setFrameRate() is called.
* feat: Move expectedFrameRate to VideoCodecSettings and add framerate to RTMP metadata
- Move expectedFrameRate from VideoCodec to VideoCodecSettings
- Remove setExpectedFrameRate() method from OutgoingStream and StreamConvertible
- Add framerate field to RTMP onMetaData message
- Synchronize expectedFrameRate in MediaMixer.setFrameRate() and addOutput()
- Add helper methods for frame rate synchronization
- Make MediaMixer.defaultFrameRate public
* feat: Rename expectedFrameRate to defaultFrameRate and add framerate to RTMP metadata
- Rename VideoCodecSettings.expectedFrameRate to defaultFrameRate to represent
a different concept from MediaMixer.frameRate
- Remove synchronization code from MediaMixer.setFrameRate() and addOutput()
to allow independent frame rate management per stream
- Remove VideoCodec.expectedFrameRate and OutgoingStream.expectedFrameRate properties
- Add framerate field to RTMP onMetaData message for RTMP servers like
nginx-rtmp-module to correctly parse stream frame rate statistics
- Make MediaMixer.defaultFrameRate public
This change allows each stream to have its own independent defaultFrameRate
(e.g., Service A at 30 FPS, Service B at 60 FPS) while MediaMixer can output
at a different frame rate. The defaultFrameRate is used both for VideoToolbox
encoder configuration and RTMP metadata.
Breaking changes:
- VideoCodecSettings.expectedFrameRate renamed to defaultFrameRate
- MediaMixer.addOutput() no longer synchronizes frame rate (streams must
explicitly set defaultFrameRate via VideoCodecSettings init or setVideoSettings())