live_media_stream_sdk 0.4.1 copy "live_media_stream_sdk: ^0.4.1" to clipboard
live_media_stream_sdk: ^0.4.1 copied to clipboard

Self-hosted Flutter live streaming with native Opus and VP8/VP9 encoding, WebM output, advanced camera/audio controls, and any binary WebSocket server.

live_media_stream_sdk #

English · Türkçe

English #

A Flutter SDK for building low-cost, self-hosted live audio and video applications without requiring a dedicated media server, RTMP service, or WebRTC SFU.

live_media_stream_sdk covers the complete mobile side of a broadcast: camera and microphone capture, native encoding, packetization, WebM muxing, optional simultaneous recording, WebSocket delivery, playback, media inspection, audio routing, device selection, lifecycle, and live diagnostics. The same encoded packets can be broadcast and recorded at the same time; a second encoder is not started.

The SDK performs native Opus and VP8/VP9 encoding and produces either separate timestamped packets or one live WebM byte stream. Your backend can therefore be a regular binary-capable WebSocket server that authenticates connections and forwards frames to viewers or stores them. For a simple relay it does not need to decode, transcode, mix, or understand the codecs. The included transport handles binary framing, metadata, reconnect attempts, bounded backpressure, video-frame dropping under congestion, and keyframe resynchronization.

This makes the package suitable for private broadcasts, field reporting, education, internal company streams, intercom systems, event audio, remote observation, and applications that need to own their transport and infrastructure.

A specialized media server is not required for ingest. Your WebSocket backend and receiving clients must still implement authentication, room routing, access control, scaling, and playback/decoding appropriate to your application. The SDK does not provide a hosted relay service or viewer UI.

In practical terms, the package can publish microphone-only, camera-only, or synchronized camera + microphone; use the included WebSocket transport or a custom transport; save the same stream as a seekable .webm without double encoding; export/share the completed file; and play local files, HTTP(S), HLS/DASH, WebM, Ogg Opus, and SDK WebSocket streams.

Features #

Server and transport #

  • No RTMP server, WebRTC SFU, MCU, or hosted media SDK is required for ingest.
  • Works with a standard binary WebSocket endpoint.
  • Transport-independent API for SignalR, native sockets, message brokers, or application-specific transports.
  • One binary frame contains protocol version, JSON metadata, and raw media; payloads are not inflated with base64.
  • Room, sender, receiver, user, command, and custom header metadata.
  • Automatic reconnect attempts and a fresh keyframe request after reconnect.
  • Incoming JSON control messages and binary messages are exposed to the app.
  • Transport is optional: consume packets directly and send or store them yourself.

Camera and video #

  • Native camera capture and hardware-supported VP8/VP9 encoding.
  • Front and rear camera selection and live camera switching.
  • 480p, 720p, and 1080p profiles.
  • Configurable frame rate and video bitrate.
  • Runtime bitrate changes without restarting the stream.
  • Manual keyframe requests for viewer synchronization and reconnect recovery.
  • Audio-only, video-only, and combined audio/video operation.
  • Per-packet timestamps, sequence numbers, dimensions, codec, rotation, camera position, bitrate, and keyframe metadata.
  • Android and iOS Flutter texture preview with correct aspect ratio.
  • iOS thermal protection reduces FPS/bitrate under heat, temporarily pauses video at critical temperature, and restores it with a keyframe after cooling.

Microphone and audio #

  • Native Opus encoding with configurable bitrate.
  • Mono/stereo selection and 8, 12, 16, 24, or 48 kHz sample rates, subject to device support.
  • Built-in, wired headset, Bluetooth, and USB input discovery and selection.
  • Deterministic microphone priority helper for field devices.
  • Android software input gain from 0.0 to 8.0 with clipping protection.
  • Android device-dependent noise suppression, automatic gain control, and acoustic echo cancellation.
  • iOS preferred-input routing and voice-chat audio session mode when echo cancellation is requested.
  • Android voice activity detection with configurable silence threshold and hangover; silent frames can be skipped before encoding.
  • Live dB, peak, silence, clipping, audio-focus, and device-change events.

Output, lifecycle, and diagnostics #

  • Independent AudioPacket and VideoPacket output.
  • Ordered live WebmPacket output containing VP8/VP9 and Opus.
  • Native WebM recording that reuses encoded data without a second encoder.
  • Optional recording directory/name with safe defaults and collision handling.
  • Rich recording result for upload workflows: path, size, duration, media format, counters, timestamps, and errors.
  • Hybrid native player for platform-supported files/URLs/HLS plus the SDK's VP8/VP9 + Opus WebM fallback and WebM live streams.
  • Ready-made audio/video controls with seek, time, mute, volume, speed, adaptive sizing, fullscreen, and orientation.
  • Headless-friendly player state, timeline, capabilities, and custom Flutter control overlays for product-specific player experiences.
  • Pause/resume and clean asynchronous shutdown with final muxer flushing.
  • Android camera/microphone foreground service with customizable notification.
  • iOS background audio; camera automatically stops in the background and resumes with a keyframe in the foreground.
  • Live upload/encoding bitrate, FPS, CPU, memory, packet, dropped-frame, keyframe, audio-level, effect, and thermal statistics.

Platform matrix #

Capability Android iOS
Opus audio and VP8/VP9 video
Audio-only, video-only, audio/video
Separate packet and live WebM output
Front/rear camera and runtime video controls
Microphone discovery and selection
Native Flutter texture preview
Camera/microphone foreground service
Background audio with camera recovery
Thermal video protection
Native WebM recording and metadata
Native multi-format file/URL player
WebM byte-stream/WebSocket player
Swift Package Manager and CocoaPods

The iOS binary supports physical arm64 devices and both Apple Silicon (arm64) and Intel (x86_64) simulators. Camera capture is unavailable when the selected simulator has no camera input.

Codec and container support #

Encoding and decoding are different capabilities. The broadcast encoder is deliberately deterministic, while the player additionally uses the operating system's native media framework.

Operation Android iOS
Audio encode Opus Opus (bundled libopus)
Video encode VP8/VP9 through MediaCodec when a device encoder exists VP8/VP9 through bundled libvpx
Live/recording container WebM: Opus + VP8/VP9 WebM: Opus + VP8/VP9
SDK WebM decode VP8, VP9, Opus through Media3/device codecs VP8/VP9 through libvpx; Opus through libopus
Ogg audio decode Opus, Vorbis, FLAC where Media3/device support exists Opus through the SDK; other supported formats through AVFoundation
Common native audio decode AAC, MP3, Opus, Vorbis, FLAC, AMR, PCM/WAV, subject to the device AAC, HE-AAC, ALAC, MP3, PCM/WAV through AVFoundation; Opus through the SDK fallback
Common native video decode H.264/AVC, H.265/HEVC, VP8, VP9, AV1, MPEG-4, MPEG-2 when a device decoder exists H.264/AVC and H.265/HEVC through AVFoundation; VP8/VP9 through the SDK fallback
Containers/protocols MP4/M4V, Matroska/WebM, Ogg, MPEG-TS/PS, FLV, ADTS, AMR, FLAC, WAV, HLS, DASH MP4/M4V/MOV, M4A, MP3, WAV, HLS through AVFoundation; WebM and Ogg Opus through the SDK

This matrix describes the implemented playback paths, not a guarantee that every profile plays on every device. Native playback depends on OS version, hardware decoders, codec profile/level, bit depth, DRM, container correctness, and server range/adaptive-streaming behavior. Raw headerless Opus is not a normal media file; use Ogg Opus or WebM.

Installation #

dependencies:
  live_media_stream_sdk: ^0.4.1

Import the public API:

import 'package:live_media_stream_sdk/live_media_stream_sdk.dart';

The host application is responsible for requesting camera, microphone, and notification permissions before starting a stream.

Android #

The plugin manifest declares camera, microphone, foreground-service, and notification permissions. On Android 13 and later, request notification permission in the host app. Start a camera/microphone foreground service while the application is visible to satisfy Android's while-in-use restrictions.

iOS #

Add usage descriptions to the host application's Info.plist:

<key>NSCameraUsageDescription</key>
<string>Camera access is required for live video.</string>
<key>NSMicrophoneUsageDescription</key>
<string>Microphone access is required for live audio.</string>

To continue audio while the app is in the background, also add:

<key>UIBackgroundModes</key>
<array>
  <string>audio</string>
</array>

iOS suspends camera capture in the background. The plugin keeps audio active and restarts video with a keyframe when the application returns to the foreground.

The plugin supports both Swift Package Manager and CocoaPods. New Flutter projects can use SwiftPM without an additional integration step; CocoaPods is retained for projects that have not migrated yet.

Quick start #

final controller = LiveMediaStreamController();

await controller.initialize();

final packetSubscription = controller.packets.listen((packet) {
  // Store, forward, or inspect encoded media packets.
});

await controller.start(
  const LiveStreamConfiguration(
    mediaMode: StreamMediaMode.audioVideo,
    videoCodec: VideoCodec.vp8,
    audioCodec: AudioCodec.opus,
    resolution: StreamResolution.p480,
    framesPerSecond: 20,
    videoBitrate: 800000,
    audioBitrate: 64000,
  ),
);

// Later:
await controller.stop();
await packetSubscription.cancel();
await controller.dispose();

For long iOS sessions, VP8 at 480p, 15–20 FPS, and 600–900 Kbit/s is a good starting profile. The plugin automatically reduces video work under serious thermal pressure and temporarily switches to audio-only at the critical thermal state.

Usage guide #

The complete production sequence is:

  1. Request only the permissions needed by the selected media mode.
  2. Create and initialize one controller for the streaming screen.
  3. Subscribe to packets, events, and statistics before starting.
  4. Optionally create a transport and recording configuration.
  5. Start the stream and await every runtime control operation.
  6. Stop the stream and receive the finalized recording result.
  7. Export or share the recording when requested.
  8. Cancel subscriptions and dispose the controller.

1. Request permissions #

Request the permissions required by the selected mode before calling start():

  • audioOnly: microphone
  • videoOnly: camera
  • audioVideo: camera and microphone
  • Android 13+: notifications when the foreground-service notification is used

Permission prompts belong to the host application. Use a permission package of your choice or your existing native permission flow.

2. Create and initialize the controller #

Keep one controller for the lifetime of the streaming screen:

late final LiveMediaStreamController controller;

Future<void> initializeStreaming() async {
  controller = LiveMediaStreamController();
  await controller.initialize();
}

3. Listen to output and diagnostics #

Subscribe before starting so the first packets and events are not missed:

final packetSubscription = controller.packets.listen((packet) {
  // AudioPacket, VideoPacket, or WebmPacket.
});

final eventSubscription = controller.events.listen((event) {
  // Camera, audio, connection, state, and error events.
});

final statisticsSubscription = controller.statistics.listen((statistics) {
  debugPrint(
    'upload=${statistics.uploadBitsPerSecond}, '
    'cpu=${statistics.cpuUsage}, '
    'thermal=${statistics.thermalState}',
  );
});

4. Configure and start the stream #

final configuration = LiveStreamConfiguration(
  mediaMode: StreamMediaMode.audioVideo,
  outputMode: StreamOutputMode.separateAudioVideo,
  cameraPosition: CameraPosition.front,
  resolution: StreamResolution.p480,
  framesPerSecond: 20,
  videoBitrate: 800000,
  audioBitrate: 64000,
  audioProcessing: const AudioProcessingConfiguration(
    noiseSuppression: true,
    automaticGainControl: true,
    echoCancellation: true,
  ),
);

await controller.start(configuration);

Use separateAudioVideo when your server accepts independent audio and video packets. Use webm when the receiver expects one playable WebM byte stream.

transport is optional. Without one, encoded output is still available from controller.packets, allowing the application to store or send it itself.

5. Control an active stream #

await controller.switchCamera(CameraPosition.back);
await controller.setVideoBitrate(600000);
await controller.requestVideoKeyFrame();

await controller.pause();
await controller.resume();

Only call video controls while video is enabled. Await each operation before starting another state change.

6. Stop and clean up #

await controller.stop();

await packetSubscription.cancel();
await eventSubscription.cancel();
await statisticsSubscription.cancel();

await controller.dispose();

Always await stop() before closing your socket or leaving the streaming screen. This allows buffered packets and final WebM bytes to be delivered.

When recording is enabled, stop() returns the finalized file:

final stopResult = await controller.stop();
final recording = stopResult.recording;

if (recording != null && recording.isCompleted) {
  debugPrint(
    '${recording.filePath} • ${recording.fileSizeKilobytes} KB '
    '• ${recording.duration}',
  );

  // Native Files/Save picker; null means that the user cancelled.
  final exportedUri = await controller.exportRecording(recording);

  // Android Sharesheet / iOS share sheet: WhatsApp, AirDrop, etc.
  await controller.shareRecording(recording);
}

The muxer first finalizes the recording in app-scoped storage. The native export picker then lets the user select Documents, Downloads, iCloud Drive, or another provider. The file name is optional and .webm is appended automatically.

How the SDK fits into your system #

Camera + microphone
        │
        ▼
Native capture → Opus + VP8/VP9 encoding
        │
        ├── controller.packets ──→ your transport / WebSocket relay
        │
        ├── native WebM recorder ──→ uploadable .webm file
        │
        └── Flutter texture ──→ local camera preview

Media file / URL / WebSocket WebM stream
        │
        ▼
Native player → video texture + audio output → Flutter controls

Encoding and decoding run natively. The WebSocket server does not need to decode, transcode, mix, or understand WebM when it only forwards binary frames. This keeps the ingest architecture small, but it also means that authentication, authorization, rooms, slow-client handling, persistence, monitoring, and horizontal scaling remain backend responsibilities.

Complete configuration reference #

LiveStreamConfiguration #

Property Default Meaning
transport null Optional delivery layer. With null, encoded packets are available only from controller.packets; useful for local recording and a custom backend.
socketMessageBuilder SDK builder Converts packets to your application’s socket envelope and metadata.
mediaMode audioVideo audioOnly, videoOnly, or audioVideo; also determines required permissions.
videoCodec vp8 VP8 or VP9. VP8 is the safest compatibility and heat starting point.
audioCodec opus Native Opus audio encoding.
resolution p720 p480, p720, or p1080. Higher values increase heat, bitrate, and battery use.
outputMode separateAudioVideo Emits independent encoded packets or ordered live WebM chunks.
cameraPosition back Initial front or rear camera.
qualityMode automatic Chooses the SDK quality strategy. Automatic mode permits thermal adaptation on iOS.
framesPerSecond 30 Requested video FPS. Use 15–20 for long, heat-sensitive broadcasts.
videoBitrate 1500000 Video bits per second. Can be changed during a stream.
audioBitrate 64000 Opus bits per second. 32–64 Kbit/s is a practical voice range.
audioProcessing defaults Input device, gain, channel/sample rate, AGC, AEC, noise suppression, and VAD.
recording null Enables native WebM recording when provided and enabled.
enableForegroundService true Keeps Android capture represented by the required foreground notification.
foregroundNotificationTitle/Text localized defaults Android notification content.
overlays empty Metadata/configuration for supported media overlays.

AudioProcessingConfiguration #

Property Default Meaning
automaticGainControl true Requests native AGC where the device supports it.
echoCancellation true Requests AEC/voice-chat routing; actual availability is device-dependent.
noiseSuppression true Requests native noise suppression.
voiceActivityDetection false Detects silence; Android can skip silent frames before encoding.
inputDevice default input Select a value returned by getAudioInputDevices().
gain 1.0 Android software gain from 0.0 to 8.0; watch clipping events above 1.0.
channelMode mono Mono or stereo, subject to route support.
sampleRate 48000 8, 12, 16, 24, or 48 kHz. Opus commonly performs best at 48 kHz.
silenceThresholdDb -50 VAD level below which input is considered silent.
vadHangoverMilliseconds 250 Keeps speech active briefly after the level falls below the threshold.

Native audio effects are requests, not guarantees. Read noiseSuppressorEnabled, automaticGainControlEnabled, and echoCancelerEnabled from live statistics to learn what the current device actually enabled.

Controller operations and when to use them #

API Use
initialize() Once before accessing devices or starting the first stream.
start(configuration) Starts capture, encoders, optional transport, recorder, and foreground lifecycle.
pause() / resume() Temporarily suspend and continue the active publishing session.
stop() Flushes encoders/muxer and returns LiveStreamStopResult; always await it.
dispose() Releases channels and controller resources when the screen/service is permanently finished.
getAudioInputDevices() Lists built-in, wired, Bluetooth, and USB inputs.
switchCamera(position) Changes front/rear camera during video streaming.
setVideoBitrate(value) Adapts bandwidth/heat without restarting.
requestVideoKeyFrame() Resynchronizes new viewers or viewers recovering after reconnect.
getVideoTextureId() Obtains the local native camera preview texture where supported.

Subscribe to packets, events, and statistics before start() so the first data is not missed. Cancel those subscriptions after stop() and before disposing the owning screen.

Choosing an output and server design #

  • Choose separateAudioVideo when your protocol routes audio and video independently or needs packet-level metadata.
  • Choose webm when receivers want an ordered, directly demuxable live WebM byte stream.
  • Enable recording independently of either output. Recording reuses encoded data and does not require network transport.
  • Keep transport: null for offline recording or when your app consumes controller.packets and sends them through an existing SignalR, broker, or native socket layer.
  • Use WebSocketLiveMediaTransport when a normal binary WebSocket relay is sufficient.

SDK-framed WebSocket messages contain a protocol header, JSON metadata, and the raw binary media payload. Do not base64 the payload. Preserve WebM chunk order, apply backpressure, and request/send a fresh keyframe after a viewer joins or reconnects.

Error handling and production rules #

try {
  await controller.start(configuration);
} on PlatformException catch (error) {
  // Permission, camera, microphone, encoder, path, or native lifecycle error.
  log('${error.code}: ${error.message}');
}

final events = controller.events.listen((event) {
  if (event is LiveStreamError) {
    log('${event.code}: ${event.message}');
  }
});
  • Never start two sessions with the same controller.
  • Disable UI configuration controls while a session is starting or active.
  • Await every lifecycle operation; do not call start/stop concurrently.
  • Use writable app-scoped paths unless the user explicitly grants access to another directory.
  • Use wss://, authenticate the connection, authorize room membership, limit payload/rate, and never trust client metadata.
  • Monitor dropped frames, CPU, memory, thermal state, upload bitrate, and reconnects in production.
  • A WebSocket relay is deliberately not a transcoder, DVR, CDN, SFU, or access control service. Add those components only if your product needs them.

WebSocket transport #

The SDK is transport-independent. Use WebSocketLiveMediaTransport or implement LiveMediaTransport for SignalR, a native socket, or another protocol:

final transport = WebSocketLiveMediaTransport(
  uri: Uri.parse('wss://your-server.example/live'),
);

await controller.start(
  LiveStreamConfiguration(
    transport: transport,
    socketMessageBuilder: (packet) => LiveSocketMessage.fromMediaPacket(
      packet,
      action: 'media',
      command: 'publish',
      groupName: 'room-42',
      senderConnectionId: 'connection-id',
      userId: 15,
      userType: 'publisher',
    ),
  ),
);

The included transport sends JSON metadata and the raw binary payload in one binary frame, without base64 expansion.

Minimal WebSocket relay #

The server does not have to decode the media when its only job is to relay frames. For example, this minimal Node.js server broadcasts each incoming binary frame to the other connections:

npm install ws
import { WebSocketServer } from 'ws';

const server = new WebSocketServer({ port: 8080 });

server.on('connection', (sender) => {
  sender.on('message', (data, isBinary) => {
    for (const viewer of server.clients) {
      if (viewer !== sender && viewer.readyState === 1) {
        viewer.send(data, { binary: isBinary });
      }
    }
  });
});

This is intentionally a minimal architecture example. Add TLS (wss://), authentication, rooms, authorization, rate limits, backpressure, monitoring, and horizontal fan-out before production use. Receiving applications can use WebSocketLiveMediaTransport.decode() to recover metadata and payload.

WebM output #

await controller.start(
  const LiveStreamConfiguration(
    outputMode: StreamOutputMode.webm,
    videoCodec: VideoCodec.vp8,
    audioCodec: AudioCodec.opus,
  ),
);

In WebM mode, controller.packets emits ordered WebmPacket chunks. Concatenate them without reordering. Always await controller.stop() so the final muxer bytes are emitted before disconnecting the transport.

Native WebM recording #

Recording uses the same encoded Opus and VP8/VP9 packets as the live stream, so enabling it does not start a second camera, microphone, or encoder. It works with audio-only, video-only, and combined sessions.

await controller.start(
  const LiveStreamConfiguration(
    mediaMode: StreamMediaMode.audioVideo,
    recording: RecordingConfiguration(
      // Both values are optional.
      directoryPath: '/a/writable/directory',
      fileName: 'training-session.webm',
    ),
  ),
);

final stopResult = await controller.stop();
final recording = stopResult.recording;

if (recording?.isSuccessful == true) {
  print(recording!.filePath);          // Upload/share this file.
  print(recording.fileSizeBytes);
  print(recording.fileSizeKilobytes);
  print(recording.duration);
  print('${recording.width}x${recording.height}');
}

While recording, LiveStreamStatistics.isRecording and recordingDuration provide the live elapsed time. After stop(), the authoritative finalized duration remains available as MediaRecordingResult.duration.

The recording is finalized in app-scoped storage first. Then let the user pick a visible destination or open the native share sheet:

final recording = (await controller.stop()).recording;
if (recording?.isSuccessful == true) {
  // Android: system Save/Storage Access Framework.
  // iOS: Files picker (On My iPhone, iCloud Drive, providers, etc.).
  final exportedUri = await controller.exportRecording(recording!);

  // WhatsApp, Telegram, AirDrop, Drive, Mail and other installed targets.
  await controller.shareRecording(recording);
}

Users provide only a file name; .webm is appended automatically. Direct arbitrary filesystem paths remain available for advanced app-controlled storage, but user-facing applications should prefer the system picker because Android scoped storage and the iOS sandbox do not expose app paths as normal shared folders.

If directoryPath is omitted, Android uses the application's external files directory (Android/data/<package>/files) and iOS uses its Documents directory. If fileName is omitted, a unique .webm name is generated. An existing file is never replaced unless overwriteExisting is explicitly enabled. MediaRecordingResult also reports codecs, bitrates, sample rate, channel count, FPS, packet/frame/drop counts, keyframes, rotation, timestamps, and any recording error.

Recording result reference #

controller.stop() returns LiveStreamStopResult. Its recording value is null when recording was not requested. Otherwise inspect:

Field Meaning
status / isSuccessful Completed or failed final state.
filePath, directoryPath, fileName Resolved upload/share destination.
fileSizeBytes, fileSizeKilobytes, fileSizeMegabytes Exact and derived file sizes.
duration, startedAt, endedAt Media duration and UTC session timestamps.
mediaMode, hasAudio, hasVideo Recorded stream composition.
audioCodec, videoCodec, bitrates Codec and configured rate metadata.
sampleRate, channels Audio format.
width, height, framesPerSecond, rotationDegrees, aspectRatio Video/display format.
packet/frame/drop/keyframe counters Diagnostics useful before upload.
errorCode, errorMessage Native recording failure details.

Do not upload until stop() completes. A path returned while capture is still running may refer to a file whose muxer has not been finalized.

Recording and socket delivery are independent pipelines. A slow socket cannot block native file writing: transport work is scheduled fairly with a bounded queue, audio/keyframe priority, and keyframe recovery after disposable video frames are dropped. If an ordered WebM socket cannot be reduced safely, socket delivery stops with a transport_backpressure event while recording continues.

Audio and video playback #

The same controller plays platform-supported local media files and HTTP(S) URLs. WebM is supported on both platforms even where the system player does not support it. Raw byte streams and WebSocket live streams use the SDK's ordered WebM live protocol:

final player = LiveMediaPlayerController();

// Local file: MP4, MOV, M4A, MP3, Ogg Opus (.opus), WebM, etc.
await player.open(
  const MediaFileSource('/path/to/video.mp4'),
  autoplay: true,
);

// URL:
await player.open(
  MediaUrlSource.parse(
    'example.com/master.m3u8',
    headers: const {'Authorization': 'Bearer token'},
  ),
);

// A WebSocket may send raw WebM binary chunks or the SDK binary frame format.
await player.open(
  WebSocketMediaSource(Uri.parse('wss://example.com/watch/room-42')),
  autoplay: true,
);

MediaUrlSource.parse accepts user-entered addresses with or without a scheme. It adds https:// when the scheme is omitted and preserves an explicit http:// or https://. The MediaUrlSource(Uri(...)) constructor is also normalized by the controller before opening.

Use the ready-made video widget in a screen:

LiveMediaVideoPlayer(
  controller: player,
  // When omitted, the video's own 4:3, 16:9, 9:16, etc. ratio is used.
  scale: 0.9,
  fit: BoxFit.contain,
  fullscreenConfiguration: const FullscreenConfiguration(
    orientation: FullscreenOrientation.landscape,
  ),
)

Fully custom controls #

The rendering surface, media state, and controls are independent. There are three integration levels:

  • Use LiveMediaVideoPlayer as-is for the SDK control overlay.
  • Pass controlsBuilder to replace that overlay with application-owned controls. Custom controls remain visible and own their gestures; the SDK does not auto-hide them or consume surface taps.
  • Use LiveMediaVideoSurface directly for a completely external layout. Set showControls: false on LiveMediaVideoPlayer when no overlay is wanted.

LiveMediaPlayerController is a ValueListenable<LiveMediaPlayerState> and also exposes a broadcast states stream. A custom progress bar can therefore rebuild without depending on ready-made widgets:

LiveMediaVideoPlayer(
  controller: player,
  controlsBuilder: (context, controller, state) {
    final timeline = state.timeline;
    return Align(
      alignment: Alignment.bottomCenter,
      child: Slider(
        value: (timeline.progress ?? 0) * 100,
        max: 100,
        onChanged: state.capabilities.canSeek && timeline.hasDuration
            ? (value) => controller.seekTo(
                timeline.duration * (value / 100),
              )
            : null,
      ),
    );
  },
)

Use state.timeline (also available as controller.timeline) for position, buffered position, duration, live/seekability flags, optional seekable bounds, and a progress value when duration is finite. progress is null for a live or otherwise unbounded timeline; do not represent those sources with a fake fixed duration.

Use state.capabilities (also controller.capabilities) before displaying an action. It exposes canPlay, canPause, canSeek, canSetVolume, canMute, canChangePlaybackSpeed, canChangeAudioOutput, and canGoLive. The controller provides play, pause, stop, seek/seekTo, seekBy, togglePlayback, setVolume, setMuted, setPlaybackSpeed/ setPlaybackRate, setAudioOutput, and goLive.

For completed seekable audio or video, play() automatically seeks to the start before resuming. This applies equally to the SDK controls and a custom play button; live sources retain their existing behavior.

For an external layout, render LiveMediaVideoSurface(controller: player) and listen with ValueListenableBuilder<LiveMediaPlayerState> or player.states. This supports custom overlays such as a branded progress bar, chapter markers, large accessibility controls, a live-edge button, playback analytics, or an entirely different audio/video experience.

Alternatively set width and/or height instead of scale. The video widget includes play/pause, seekable progress, elapsed/total time, mute, volume, 0.25x–3x speed, fullscreen, and portrait/landscape support. For audio-only media use LiveMediaAudioPlayer(controller: player), which provides the relevant controls without a video surface. Live WebSocket sources are not seekable; recorded files and seekable URLs expose progress and seeking.

Select the physical audio route after opening a source:

await player.setAudioOutput(PlayerAudioOutput.speaker);
await player.setAudioOutput(PlayerAudioOutput.receiver);
await player.setAudioOutput(PlayerAudioOutput.systemDefault);

Receiver routing requires a phone with a built-in earpiece. Wired, Bluetooth, AirPlay, and other external routes remain under the operating system's route policy when systemDefault is selected.

Call player.dispose() when the owning screen is disposed. The current native backend supports one active playback session per Flutter engine.

Format selection and native fallback #

The player does not reject a file merely because it is not WebM:

Source Android iOS
Local file / HTTP(S) URL Media3 extractors plus device decoders AVPlayer/AVFoundation
HLS Media3 HLS AVPlayer
MPEG-DASH Media3 DASH Device/AVFoundation support; HLS is the recommended Apple adaptive format
WebM VP8/VP9 + Opus file/URL Media3 SDK WebM demuxer + libvpx + Opus
Ogg Opus .opus / .ogg / .oga file/URL Media3 SDK Ogg demuxer + Opus
WebSocket / byte stream SDK WebM live protocol SDK WebM live protocol

On iOS, the SDK reads the Opus channel mapping from OpusHead. Multichannel WebM/Ogg Opus sources, including 5.1 media, use the libopus multistream decoder and are downmixed to stereo for playback.

Typical native file support includes MP4/M4V/MOV with H.264/HEVC and AAC, M4A/AAC, MP3, and WAV/PCM, subject to OS version, device decoder capability, profile, level, bit depth, DRM, and the actual container/track combination. Android Media3 additionally recognizes formats such as Matroska/WebM, Ogg, FLV, MPEG-TS, HLS, and DASH when the contained codec is available.

An .opus file is expected to contain Opus packets in an Ogg container, as defined for normal Ogg Opus files. Headerless raw Opus packets are not a self-describing, seekable media file and must first be placed in Ogg or WebM.

An extension is only a hint. The native backend inspects the media tracks and reports the discovered container/codec. A valid container can still fail when its codec profile is unsupported, encrypted/DRM content lacks a license session, the server does not support required range/adaptive requests, or the file is incomplete.

WebSocket is intentionally narrower: arbitrary chunks of a normal MP4/MOV file do not automatically form a playable live stream. The current SDK live protocol transports ordered WebM chunks. Supporting fMP4 or MPEG-TS over a custom WebSocket requires an explicit stream type and a matching incremental parser.

Player controller reference #

API/source Meaning
MediaFileSource(path) App-readable local media file; native backend is selected automatically.
MediaUrlSource(uri, headers:) HTTP(S) media or adaptive manifest; a missing scheme is normalized to HTTPS.
MediaUrlSource.parse(text, headers:) Recommended for text input; accepts example.com/media.mp3 without requiring https://.
WebSocketMediaSource(uri, protocols:) Raw WebM chunks or SDK-framed WebM binary messages.
MediaByteStreamSource(stream) Ordered WebM bytes produced by application code.
open(source, autoplay:) Closes the previous source, prepares native decoding, and returns discovered MediaInfo.
play(), pause(), stop() Playback lifecycle.
seek(position) Recorded/seekable sources only.
seekTo(position), seekBy(offset) Custom-control aliases for absolute and relative seeking.
togglePlayback() Plays or pauses based on the current state.
setVolume(0…1), setMuted() Audio output controls.
setPlaybackSpeed(0.25…3), setPlaybackRate() Audio and video playback rate.
goLive() Returns a live source to its latest buffered position.
timeline, capabilities, states Timeline snapshot, available actions, and a stream of state updates for custom controls.
LiveMediaVideoSurface Video texture only; use it to compose a fully external player layout.
LiveMediaVideoPlayer.controlsBuilder Replaces the default in-video control overlay.

controller.state reports opening, ready, playing, paused, buffering, completed, failed, and disposed states along with position, buffered position, duration, volume, speed, texture ID, metadata, and error details.

Sizing rules for LiveMediaVideoPlayer:

  • With no size arguments it follows the discovered video aspect ratio.
  • width only derives height from the ratio; height only derives width.
  • width and height together fit the player into that exact design box.
  • scale scales relative to the available parent width.
  • Do not combine scale with explicit width/height.
  • BoxFit.contain preserves the whole frame; cover fills and crops.
  • Fullscreen orientation can be automatic, portrait, landscape, or all.

Camera preview #

final textureId = await controller.getVideoTextureId();

Widget preview = textureId == null
    ? const SizedBox.shrink()
    : LiveMediaPreview(textureId: textureId);

The same public texture preview API is available on Android and iOS. Request the texture ID once after controller initialization; frames begin when a video stream starts and stop when the stream ends.

Audio processing and statistics #

AudioProcessingConfiguration controls gain, input selection, channel mode, sample rate, echo cancellation, noise suppression, automatic gain control, voice activity detection, and silence thresholds. See AUDIO_FEATURES.md for platform-specific details.

Listen to controller.events and controller.statistics for audio levels, stream state, encoded/upload bitrates, CPU use, active audio effects, and iOS thermal state.

Example #

The example application is an interactive learning lab. It supports local capture without a server, every stream/audio/video setting, native recording configuration, rich completion metadata, live diagnostics, and playback of the last recording, another file, an HTTP(S) URL, or a WebSocket stream.

Run it on a physical device:

cd example
flutter pub get
flutter run

Start with WebSocket delivery disabled to verify capture and recording. Then enable it in the example UI, enter an IP address or WebSocket URL, and test your relay. The example normalizes local IP addresses to ws://, domains to wss://, and demonstrates simultaneous recording and socket delivery.

License #

This package is available under the MIT License. The bundled iOS libvpx and libopus binaries have separate terms documented in THIRD_PARTY_NOTICES.md.


Türkçe #

Özel bir media server, RTMP servisi veya WebRTC SFU kurmak zorunda kalmadan düşük maliyetli ve tamamen size ait canlı ses/video uygulamaları geliştirmenizi sağlayan bir Flutter SDK'sıdır.

live_media_stream_sdk, yayının mobil taraftaki bütün zincirini kapsar: kamera/mikrofon yakalama, native encode, paketleme, WebM mux, yayınla eşzamanlı kayıt, WebSocket aktarımı, player/decode, medya bilgisi okuma, ses çıkış yönlendirmesi, cihaz seçimi, yaşam döngüsü ve canlı ölçümler. Yayına gönderilen encode edilmiş paketler aynı anda kayda yazılır; ikinci encoder çalıştırılmaz.

SDK, Opus sesi ve VP8/VP9 videoyu native olarak encode ederek ayrı timestamp'li paketler veya tek bir canlı WebM byte akışı üretir. Backend tarafında binary frame kabul eden normal bir WebSocket sunucusu yeterlidir. Basit relay senaryosunda sunucunun codec çözmesi, transcode etmesi veya sesi/görüntüyü işlemesi gerekmez. Hazır taşıyıcı; binary frame formatını, metadata'yı, yeniden bağlantıyı, sınırlı kuyruk/backpressure yönetimini, yoğunlukta video karesi düşürmeyi ve keyframe ile yeniden senkronizasyonu yönetir.

Özel yayınlar, saha haberciliği, eğitim, şirket içi yayınlar, interkom sistemleri, etkinlik sesi, uzaktan gözlem ve kendi altyapısına sahip olmak isteyen uygulamalar için uygundur.

Yayın verisini almak için özel bir media server gerekmez. Ancak WebSocket sunucunuz ve izleyici uygulamalarınız; kimlik doğrulama, oda yönlendirme, erişim kontrolü, ölçekleme ve oynatma/decode işlemlerini kendi ihtiyacınıza göre gerçekleştirmelidir. SDK hazır bir bulut relay servisi veya izleyici arayüzü sunmaz.

Paket; yalnız ses, yalnız video veya senkron ses + video yayınlamayı; hazır ya da özel transport kullanmayı; aynı yayını ikinci kez encode etmeden .webm olarak kaydetmeyi; dosyayı dışa aktarıp paylaşmayı; yerel dosya, HTTP(S), HLS/DASH, WebM, Ogg Opus ve SDK WebSocket yayınlarını oynatmayı sağlar.

Özellikler #

Sunucu ve taşıma katmanı

  • Yayın verisini almak için RTMP server, WebRTC SFU/MCU veya ücretli bir medya SDK'sı gerekmez.
  • Standart, binary mesaj destekleyen herhangi bir WebSocket endpoint'iyle çalışır.
  • SignalR, native socket, mesaj kuyruğu veya özel protokoller için transport-bağımsız API sunar.
  • Her binary frame; protokol sürümü, JSON metadata ve ham medya payload'ını içerir. Base64 kullanılmadığı için veri gereksiz büyümez.
  • Oda/grup, gönderen, alıcı, kullanıcı, komut ve uygulamaya özel header metadata'sı taşınabilir.
  • Bağlantı koptuğunda otomatik yeniden bağlanma denenir; bağlantı geldiğinde izleyicileri tekrar senkronlamak için yeni keyframe istenir.
  • Sunucudan gelen JSON kontrol mesajları ve binary mesajlar uygulamaya aktarılır.
  • Hazır transport'u kullanmak zorunlu değildir; paketleri doğrudan dinleyip kendi bağlantınızla gönderebilir veya kaydedebilirsiniz.

Kamera ve video

  • Native kamera yakalama ve cihazın desteklediği donanım hızlandırmalı VP8/VP9 kodlama.
  • Ön/arka kamera seçimi ve yayın sırasında kamera değiştirme.
  • 480p, 720p ve 1080p çözünürlük profilleri.
  • Ayarlanabilir FPS ve video bitrate.
  • Yayını kapatmadan çalışma anında bitrate değiştirme.
  • İzleyici senkronizasyonu ve yeniden bağlantı için elle keyframe isteme.
  • Yalnızca ses, yalnızca video veya birlikte ses/video yayını.
  • Her video paketinde sıra numarası, timestamp, çözünürlük, codec, dönüş açısı, kamera yönü, bitrate ve keyframe bilgisi.
  • Android ve iOS'ta doğru en-boy oranıyla Flutter texture kamera önizlemesi.
  • iOS termal koruma: cihaz ısındığında FPS/bitrate düşürme, kritik sıcaklıkta videoyu geçici durdurma ve soğuyunca keyframe ile otomatik devam etme.

Mikrofon ve ses

  • Native Opus kodlama ve ayarlanabilir ses bitrate.
  • Cihaz desteğine bağlı mono/stereo ile 8, 12, 16, 24 veya 48 kHz örnekleme.
  • Dahili mikrofon, kablolu kulaklık, Bluetooth ve USB girişlerini listeleme ve seçme.
  • Saha cihazları için USB → kablolu → Bluetooth → dahili mikrofon şeklinde değiştirilebilir öncelikli seçim yardımcısı.
  • Android'de 0.0–8.0 aralığında yazılımsal gain ve clipping koruması.
  • Android'de cihaz desteğine bağlı gürültü azaltma, otomatik kazanç ve akustik yankı giderme.
  • iOS'ta tercih edilen giriş cihazına yönlendirme ve yankı giderme istendiğinde voice-chat ses oturumu.
  • Android'de ayarlanabilir sessizlik eşiği ve hangover süresiyle VAD; sessiz frame'ler kodlama öncesinde atlanabilir.
  • Canlı dB, peak, sessizlik, clipping, audio focus ve cihaz değişikliği olayları.

Çıktı, yaşam döngüsü ve ölçümler

  • Bağımsız AudioPacket ve VideoPacket çıkışı.
  • VP8/VP9 ve Opus içeren sıralı canlı WebmPacket çıkışı.
  • İkinci encoder açmadan mevcut encode verisini kullanan native WebM kayıt.
  • Opsiyonel kayıt yolu/adı, güvenli varsayılanlar ve isim çakışması yönetimi.
  • Upload için dosya yolu, boyut, süre, format, sayaç, zaman ve hata metadata'sı.
  • Platformun desteklediği dosya/URL/HLS formatları, WebM fallback ve WebM canlı akış için hibrit native player.
  • Seek, süre, mute, volume, hız, otomatik boyut, fullscreen ve yön kontrolleri.
  • Ürüne özel player deneyimleri için headless uyumlu state, timeline, capabilities ve özelleştirilebilir Flutter kontrol katmanı.
  • Duraklatma/devam ettirme ve muxer'ın son verilerini gönderen güvenli kapatma.
  • Android'de metni değiştirilebilir kamera/mikrofon foreground service bildirimi.
  • iOS'ta arka planda ses; kamera arka planda durur ve öne dönünce keyframe ile otomatik açılır.
  • Upload/kodlama bitrate, FPS, CPU, bellek, paket, düşen frame, keyframe, ses seviyesi, etkin ses efektleri ve termal durum istatistikleri.

Platform karşılaştırması

Özellik Android iOS
Opus ses ve VP8/VP9 video
Yalnızca ses, yalnızca video, ses/video
Ayrı paket ve canlı WebM çıkışı
Ön/arka kamera ve çalışma anında video kontrolü
Mikrofon listeleme ve seçme
Native Flutter texture önizlemesi
Kamera/mikrofon foreground service
Arka plan sesi ve kamera geri yükleme
Termal video koruması
Native WebM kayıt ve metadata
Native çok-formatlı dosya/URL player
WebM byte-stream/WebSocket player
Swift Package Manager ve CocoaPods

iOS ikilisi fiziksel arm64 cihazları, Apple Silicon (arm64) ve Intel (x86_64) simülatörleri destekler. Seçilen simülatörde kamera girişi yoksa kamera yayını kullanılamaz.

Codec ve container desteği

Encode ve decode iki ayrı yetenektir. Yayın encoder’ı kontrollü bir format üretir; player bunlara ek olarak işletim sisteminin native medya altyapısını kullanır.

İşlem Android iOS
Ses encode Opus Opus (paketle gelen libopus)
Video encode Cihaz encoder’ı varsa MediaCodec ile VP8/VP9 Paketle gelen libvpx ile VP8/VP9
Canlı/kayıt container’ı WebM: Opus + VP8/VP9 WebM: Opus + VP8/VP9
SDK WebM decode Media3/cihaz codec’i ile VP8, VP9, Opus libvpx ile VP8/VP9; libopus ile Opus
Ogg ses decode Media3/cihaz desteğine göre Opus, Vorbis, FLAC SDK ile Opus; diğer desteklenenler AVFoundation ile
Yaygın native ses decode Cihaz desteğine göre AAC, MP3, Opus, Vorbis, FLAC, AMR, PCM/WAV AVFoundation ile AAC, HE-AAC, ALAC, MP3, PCM/WAV; SDK fallback ile Opus
Yaygın native video decode Uyumlu cihaz decoder’ı varsa H.264/AVC, H.265/HEVC, VP8, VP9, AV1, MPEG-4, MPEG-2 AVFoundation ile H.264/AVC, H.265/HEVC; SDK fallback ile VP8/VP9
Container/protokol MP4/M4V, Matroska/WebM, Ogg, MPEG-TS/PS, FLV, ADTS, AMR, FLAC, WAV, HLS, DASH MP4/M4V/MOV, M4A, MP3, WAV, HLS; SDK ile WebM ve Ogg Opus

Bu tablo uygulanan oynatma yollarını gösterir; her codec profilinin her cihazda çalışacağı garantisi değildir. Native destek işletim sistemi sürümüne, donanım decoder’ına, codec profile/level değerine, bit derinliğine, DRM'e, container yapısına ve sunucunun range/adaptive isteklerine bağlıdır. Container içermeyen ham Opus normal medya dosyası değildir; Ogg Opus veya WebM kullanılmalıdır.

Kurulum #

pubspec.yaml dosyanıza paketi ekleyin:

dependencies:
  live_media_stream_sdk: ^0.4.1

Ardından genel API'yi içe aktarın:

import 'package:live_media_stream_sdk/live_media_stream_sdk.dart';

Yayın başlatılmadan önce kamera, mikrofon ve gerekiyorsa bildirim izinlerini istemek host uygulamanın sorumluluğundadır.

Android izinleri

Eklenti manifest dosyası kamera, mikrofon, foreground service ve bildirim izinlerini tanımlar. Android 13 ve sonrasında bildirim iznini uygulamanızdan istemelisiniz. Android'in kullanım-anı kısıtlamaları nedeniyle kamera veya mikrofon kullanan foreground service, uygulama görünür durumdayken başlatılmalıdır.

iOS izinleri

Host uygulamanın Info.plist dosyasına kullanım açıklamalarını ekleyin:

<key>NSCameraUsageDescription</key>
<string>Canlı video için kamera erişimi gereklidir.</string>
<key>NSMicrophoneUsageDescription</key>
<string>Canlı ses için mikrofon erişimi gereklidir.</string>

Uygulama arka plandayken ses yayınını sürdürmek için ayrıca şunu ekleyin:

<key>UIBackgroundModes</key>
<array>
  <string>audio</string>
</array>

iOS arka planda kamera yakalamayı durdurur. Eklenti ses yayınını sürdürür; uygulama tekrar öne geldiğinde kamerayı yeniden başlatır ve ilk video karesini keyframe olarak üretir.

Eklenti hem Swift Package Manager hem CocoaPods destekler. Yeni Flutter projeleri ek işlem yapmadan SwiftPM kullanabilir; henüz geçiş yapmamış uygulamalar için CocoaPods desteği korunur.

Hızlı başlangıç #

Yukarıdaki Quick start örneği doğrudan kullanılabilir. Temel akış şöyledir:

  1. LiveMediaStreamController oluşturun ve initialize() çağrısını bekleyin.
  2. Üretilen veriler için packets, olaylar için events, ölçümler için statistics akışını dinleyin.
  3. Bir LiveStreamConfiguration ile start() çağrısını yapın.
  4. Yayını bitirirken sırasıyla stop() ve dispose() çağrılarını mutlaka await edin.

Uzun iOS yayınları için VP8, 480p, 15–20 FPS ve 600–900 Kbit/s iyi bir başlangıç profilidir. Cihaz ciddi ölçüde ısındığında eklenti video yükünü otomatik azaltır. Termal durum kritik seviyeye ulaşırsa geçici olarak yalnızca ses yayınına geçer; cihaz soğuduğunda kamerayı yeniden başlatır.

Ayrıntılı kullanım #

Üretim ortamındaki tam sıra şöyledir:

  1. Seçilen medya modu için gereken izinleri alın.
  2. Ekran boyunca yaşayacak tek controller oluşturup initialize edin.
  3. Yayından önce paket, olay ve istatistik stream’lerine abone olun.
  4. Gerekiyorsa WebSocket transport ve kayıt ayarlarını oluşturun.
  5. Yayını başlatın; bütün kontrol işlemlerini await edin.
  6. Yayını durdurup tamamlanmış kayıt sonucunu alın.
  7. Kullanıcı isterse kaydı dosyalara aktarın veya paylaşın.
  8. Abonelikleri kapatıp controller’ı dispose edin.

1. Gerekli izinleri alın

start() çağrısından önce seçilen yayın modunun izinlerini kullanıcıdan alın:

  • audioOnly: mikrofon
  • videoOnly: kamera
  • audioVideo: kamera ve mikrofon
  • Android 13 ve sonrası: foreground service bildirimi kullanılacaksa bildirim

İzin isteme ekranları SDK tarafından açılmaz; bu işlem host uygulamaya aittir. Tercih ettiğiniz bir Flutter izin paketini veya mevcut native izin akışınızı kullanabilirsiniz.

2. Controller'ı oluşturun

Yayın ekranı boyunca aynı controller nesnesini saklayın:

late final LiveMediaStreamController controller;

Future<void> yayiniHazirla() async {
  controller = LiveMediaStreamController();
  await controller.initialize();
}

3. Paketleri, olayları ve istatistikleri dinleyin

İlk verileri kaçırmamak için yayın başlamadan önce stream'lere abone olun:

final paketAboneligi = controller.packets.listen((packet) {
  // AudioPacket, VideoPacket veya WebmPacket gelir.
});

final olayAboneligi = controller.events.listen((event) {
  // Kamera, ses, bağlantı, durum ve hata olayları gelir.
});

final istatistikAboneligi = controller.statistics.listen((statistics) {
  debugPrint(
    'upload=${statistics.uploadBitsPerSecond}, '
    'cpu=${statistics.cpuUsage}, '
    'termal=${statistics.thermalState}',
  );
});

4. Yayını yapılandırıp başlatın

final ayarlar = LiveStreamConfiguration(
  mediaMode: StreamMediaMode.audioVideo,
  outputMode: StreamOutputMode.separateAudioVideo,
  cameraPosition: CameraPosition.front,
  resolution: StreamResolution.p480,
  framesPerSecond: 20,
  videoBitrate: 800000,
  audioBitrate: 64000,
  audioProcessing: const AudioProcessingConfiguration(
    noiseSuppression: true,
    automaticGainControl: true,
    echoCancellation: true,
  ),
);

await controller.start(ayarlar);

Sunucunuz ses ve video paketlerini ayrı kabul ediyorsa separateAudioVideo, oynatılabilir tek bir byte akışı bekliyorsa webm çıkışını seçin.

transport vermek zorunlu değildir. Transport verilmezse kodlanmış veriler yine controller.packets üzerinden gelir; uygulama bu verileri kendisi kaydedebilir veya kendi bağlantısıyla gönderebilir.

5. Aktif yayını kontrol edin

await controller.switchCamera(CameraPosition.back);
await controller.setVideoBitrate(600000);
await controller.requestVideoKeyFrame();

await controller.pause();
await controller.resume();

Video komutlarını yalnızca video açıkken çağırın. Bir durum değiştirme işlemi bitmeden diğerini başlatmamak için tüm çağrıları await edin.

6. Yayını güvenli biçimde kapatın

await controller.stop();

await paketAboneligi.cancel();
await olayAboneligi.cancel();
await istatistikAboneligi.cancel();

await controller.dispose();

Socket bağlantısını kapatmadan veya yayın ekranından çıkmadan önce stop() çağrısını mutlaka await edin. Böylece bekleyen paketler ve WebM akışının son baytları kaybolmaz.

Kayıt açıksa stop() tamamlanan dosyanın bütün bilgilerini döndürür:

final durdurmaSonucu = await controller.stop();
final kayit = durdurmaSonucu.recording;

if (kayit != null && kayit.isCompleted) {
  debugPrint(
    '${kayit.filePath} • ${kayit.fileSizeKilobytes} KB '
    '• ${kayit.duration}',
  );

  // Native Dosyalar/Kaydet ekranı; kullanıcı vazgeçerse null döner.
  final hedefUri = await controller.exportRecording(kayit);

  // Android/iOS paylaşım ekranı: WhatsApp, AirDrop vb.
  await controller.shareRecording(kayit);
}

Dosya, mux işleminin güvenli tamamlanabilmesi için önce uygulamaya özel alanda oluşturulur. exportRecording ile Documents, Downloads, iCloud Drive veya başka bir sağlayıcı seçilir. Dosya adı opsiyoneldir; .webm otomatik eklenir.

SDK sisteminizde nasıl konumlanır? #

Kamera + mikrofon
        │
        ▼
Native yakalama → Opus + VP8/VP9 kodlama
        │
        ├── controller.packets ──→ transport / WebSocket relay
        │
        ├── native WebM kayıt ──→ upload edilebilir .webm
        │
        └── Flutter texture ──→ yerel kamera önizlemesi

Medya dosyası / URL / WebSocket WebM akışı
        │
        ▼
Native player → video texture + ses çıkışı → Flutter kontrolleri

Encode ve decode işlemleri cihazın native katmanında yapılır. WebSocket sunucusu yalnızca binary frame’leri aktaracaksa WebM’i açmak, decode etmek, transcode etmek veya ses/video karıştırmak zorunda değildir. Kimlik doğrulama, oda yetkisi, yavaş istemci kontrolü, saklama, izleme ve yatay ölçekleme ise uygulamanın backend sorumluluklarıdır.

Tüm yayın ayarları #

LiveStreamConfiguration

Alan Varsayılan Açıklama
transport null Gönderim katmanı. Null olduğunda paketler yalnız controller.packets üzerinden alınır; yerel kayıt için idealdir.
socketMessageBuilder SDK builder Paketleri uygulamanın socket metadata ve mesaj biçimine dönüştürür.
mediaMode audioVideo audioOnly, videoOnly veya audioVideo; istenecek izinleri de belirler.
videoCodec vp8 VP8 veya VP9. Uyumluluk ve ısınma açısından VP8 güvenli başlangıçtır.
audioCodec opus Native Opus ses kodlama.
resolution p720 480p, 720p veya 1080p. Çözünürlük arttıkça ısı, pil ve bitrate ihtiyacı artar.
outputMode separateAudioVideo Bağımsız encode paketleri veya sıralı canlı WebM parçaları.
cameraPosition back Başlangıçta ön veya arka kamera.
qualityMode automatic Kalite stratejisi; otomatik mod iOS termal uyarlamasına izin verir.
framesPerSecond 30 İstenen FPS. Uzun yayınlarda 15–20 daha serindir.
videoBitrate 1500000 Video bit/s; yayın sırasında değiştirilebilir.
audioBitrate 64000 Opus bit/s; konuşma için 32–64 Kbit/s iyi başlangıçtır.
audioProcessing varsayılanlar Mikrofon, gain, kanal/sample rate, AGC, AEC, NS ve VAD.
recording null Verildiğinde native WebM dosya kaydını açar.
enableForegroundService true Android’de zorunlu foreground bildirim yaşam döngüsünü yönetir.
bildirim başlığı/açıklaması Türkçe varsayılan Android foreground service bildirim metni.
overlays boş Desteklenen medya overlay yapılandırması.

AudioProcessingConfiguration

Alan Varsayılan Açıklama
automaticGainControl true Cihaz destekliyorsa native otomatik kazanç ister.
echoCancellation true AEC/voice-chat yönlendirmesi ister; cihaz desteği değişebilir.
noiseSuppression true Native gürültü azaltma ister.
voiceActivityDetection false Sessizliği algılar; Android sessiz frame’leri encode öncesi atabilir.
inputDevice varsayılan getAudioInputDevices() listesinden mikrofon seçer.
gain 1.0 Android yazılımsal gain: 0.0–8.0; 1 üzerindeyken clipping izlenmelidir.
channelMode mono Rota destekliyorsa mono veya stereo.
sampleRate 48000 8, 12, 16, 24 veya 48 kHz.
silenceThresholdDb -50 Bunun altındaki giriş VAD tarafından sessiz kabul edilir.
vadHangoverMilliseconds 250 Seviye düşünce konuşmayı kısa süre daha aktif tutar.

Ses efektleri garanti değil, istektir. Cihazın gerçekten açtığı efektleri canlı istatistiklerdeki noiseSuppressorEnabled, automaticGainControlEnabled ve echoCancelerEnabled alanlarından okuyun.

Controller işlemleri #

API Ne zaman kullanılır?
initialize() Cihazları okumadan ve ilk yayından önce bir kez.
start(configuration) Capture, encoder, transport, kayıt ve foreground yaşam döngüsünü başlatır.
pause() / resume() Aktif yayını geçici durdurur/devam ettirir.
stop() Encoder ve muxer’ı flush eder, LiveStreamStopResult döndürür. Mutlaka await edin.
dispose() Ekran/servis tamamen kapanırken kanal kaynaklarını bırakır.
getAudioInputDevices() Dahili, kablolu, Bluetooth ve USB mikrofonları listeler.
switchCamera() Video yayınında ön/arka kamerayı değiştirir.
setVideoBitrate() Yayını kesmeden bant genişliği ve ısıyı ayarlar.
requestVideoKeyFrame() Yeni veya yeniden bağlanan izleyiciyi senkronlar.
getVideoTextureId() Desteklenen platformda yerel kamera preview texture’ını verir.

İlk verileri kaçırmamak için packets, events ve statistics aboneliklerini start() öncesinde açın. Ekranı kapatırken önce stop(), sonra abonelik iptalleri ve en son dispose() çağırın.

Hangi çıkış biçimini seçmeliyim? #

  • Protokol ses/video paketlerini ayrı yönetecekse separateAudioVideo.
  • İzleyici doğrudan demux edilebilir sıralı byte akışı istiyorsa webm.
  • Ağ olmasa da kayıt istiyorsanız transport: null ve recording.
  • SignalR, broker veya mevcut native socket’iniz varsa controller.packets üzerinden kendi LiveMediaTransport katmanınızı kullanın.
  • Normal binary WebSocket relay yeterliyse WebSocketLiveMediaTransport kullanın.

WebSocket payload’ını base64’e çevirmeyin. WebM parça sırasını koruyun, backpressure uygulayın ve yeni/yeniden bağlanan izleyici için keyframe isteyin.

Hata yönetimi ve üretim kuralları #

try {
  await controller.start(ayarlar);
} on PlatformException catch (hata) {
  log('${hata.code}: ${hata.message}');
}

final olaylar = controller.events.listen((olay) {
  if (olay is LiveStreamError) {
    log('${olay.code}: ${olay.message}');
  }
});
  • Aynı controller ile eşzamanlı iki yayın başlatmayın.
  • Başlatma/yayın sırasında yapılandırma kontrollerini kilitleyin.
  • Lifecycle çağrılarını eşzamanlı yapmayın; hepsini await edin.
  • Kullanıcı başka klasöre erişim vermediyse app-scoped dosya yolu kullanın.
  • wss://, kimlik doğrulama, oda yetkisi, payload/rate sınırı kullanın.
  • Düşen kare, CPU, bellek, termal durum, upload bitrate ve reconnect değerlerini üretimde izleyin.
  • WebSocket relay; transcoder, DVR, CDN, SFU veya yetkilendirme servisi değildir. Ürününüz ihtiyaç duyuyorsa bunları ayrıca kurun.

Gelişmiş kullanım örnekleri #

Harici mikrofonu otomatik seçerek kaliteli ses yayını

Bağlı mikrofonları listeleyip USB, kablolu kulaklık, Bluetooth ve dahili mikrofon sırasıyla en uygun girişi seçebilirsiniz:

final mikrofonlar = await controller.getAudioInputDevices();
final mikrofon = AudioInputDeviceSelector.select(mikrofonlar);

await controller.start(
  LiveStreamConfiguration(
    mediaMode: StreamMediaMode.audioOnly,
    audioCodec: AudioCodec.opus,
    audioBitrate: 64000,
    audioProcessing: AudioProcessingConfiguration(
      inputDevice: mikrofon,
      channelMode: AudioChannelMode.mono,
      sampleRate: 48000,
      gain: 1.25,
      noiseSuppression: true,
      automaticGainControl: true,
      echoCancellation: true,
      voiceActivityDetection: true,
      silenceThresholdDb: -50,
      vadHangoverMilliseconds: 250,
    ),
  ),
);

Android'de VAD açıkken eşik altında kalan sessiz frame'ler kodlanmayarak bant genişliği ve işlemci kullanımı azaltılabilir. Native ses efektlerinin gerçekten etkin olup olmadığını LiveStreamStatistics içindeki noiseSuppressorEnabled, automaticGainControlEnabled ve echoCancelerEnabled alanlarından kontrol edin; destek cihazdan cihaza değişir.

Isınmayı gözeten kamera profili

Uzun süreli yayınlar için 480p ve 20 FPS ile başlayıp ağ veya cihaz durumuna göre bitrate'i yayın sırasında değiştirebilirsiniz:

await controller.start(
  const LiveStreamConfiguration(
    mediaMode: StreamMediaMode.audioVideo,
    videoCodec: VideoCodec.vp8,
    resolution: StreamResolution.p480,
    cameraPosition: CameraPosition.back,
    framesPerSecond: 20,
    videoBitrate: 800000,
    audioBitrate: 64000,
    qualityMode: QualityMode.automatic,
  ),
);

// Ağ zayıfladığında yayını kesmeden düşürün.
await controller.setVideoBitrate(550000);

// Kamerayı değiştirip izleyicilere yeni bir tam kare gönderin.
await controller.switchCamera(CameraPosition.front);
await controller.requestVideoKeyFrame();

iOS'ta termal koruma ayrıca otomatik çalışır. Güncel durumu statistics.thermalState ve statistics.thermalLimited alanlarından izleyebilirsiniz.

Native WebM kayıt

Kayıt sırasında ikinci bir kamera, mikrofon veya encoder açılmaz. Canlı yayında üretilen Opus ve VP8/VP9 verileri aynı anda WebM dosyasına yazılır. Yalnız ses, yalnız video ve sesli video modlarının tamamında kullanılabilir:

await controller.start(
  const LiveStreamConfiguration(
    mediaMode: StreamMediaMode.audioVideo,
    recording: RecordingConfiguration(
      // İki değer de isteğe bağlıdır.
      directoryPath: '/uygulamanin/yazabildigi/klasor',
      fileName: 'egitim-yayini.webm',
    ),
  ),
);

final durdurmaSonucu = await controller.stop();
final kayit = durdurmaSonucu.recording;

if (kayit?.isSuccessful == true) {
  print(kayit!.filePath); // Upload veya paylaşım için kullanın.
  print(kayit.fileSizeBytes);
  print(kayit.fileSizeKilobytes);
  print(kayit.duration);
  print('${kayit.width}x${kayit.height}');
}

Kayıt sürerken LiveStreamStatistics.isRecording ve recordingDuration alanları canlı geçen süreyi verir. stop() tamamlandığında kesin medya süresi MediaRecordingResult.duration alanındadır.

Kayıt önce uygulamanın güvenli alanında finalize edilir. Ardından kullanıcıya görünür bir konum seçtirilebilir veya native paylaşım ekranı açılabilir:

final kayit = (await controller.stop()).recording;
if (kayit?.isSuccessful == true) {
  // Android'de sistem Kaydet/SAF, iOS'ta Files seçicisi açılır.
  final hedef = await controller.exportRecording(kayit!);

  // WhatsApp, Telegram, AirDrop, Drive, Mail vb.
  await controller.shareRecording(kayit);
}

Kullanıcı yalnız dosya adını yazar; .webm otomatik eklenir. Uygulama tarafından yönetilen ileri seviye senaryolarda doğrudan klasör yolu hâlâ verilebilir; son kullanıcı arayüzünde Android scoped storage ve iOS sandbox kurallarına uygun sistem seçicisi tercih edilmelidir.

Klasör verilmezse Android Android/data/<paket_adı>/files, iOS ise uygulamanın Documents klasörünü kullanır. Dosya adı verilmezse benzersiz bir .webm adı üretilir. Aynı isimli dosya, overwriteExisting: true açıkça seçilmedikçe ezilmez. Dönen MediaRecordingResult; tam dosya yolu, bayt/KB/MB boyutu, süre, codec ve bitrate değerleri, örnekleme hızı, kanal sayısı, çözünürlük, FPS, paket/kare/ düşen kare/keyframe sayıları, dönüş bilgisi, başlangıç-bitiş zamanı ve hata bilgisini içerir.

controller.stop() bir LiveStreamStopResult döndürür. Kayıt istenmediyse recording null olur. Kayıt varsa şu alanlar upload öncesi ihtiyaç duyulan bilgilerin tamamını verir:

Alan Anlamı
status, isSuccessful Kayıt başarıyla finalize edildi mi?
filePath, directoryPath, fileName Upload/paylaşım için çözülmüş dosya adresi.
byte, KB ve MB boyutları Kesin ve yardımcı dosya boyutları.
duration, startedAt, endedAt Medya süresi ve UTC başlangıç/bitiş zamanları.
mediaMode, hasAudio, hasVideo Dosyanın medya içeriği.
codec ve bitrate alanları Encode biçimi ve yapılandırılan hızlar.
sampleRate, channels Ses formatı.
çözünürlük, FPS, dönüş, aspect ratio Video/görüntüleme formatı.
paket, kare, düşen kare, keyframe sayaçları Kayıt kalite tanılaması.
errorCode, errorMessage Native kayıt hatası.

stop() tamamlanmadan dosyayı upload etmeyin; muxer henüz finalize edilmemiş olabilir.

Kayıt ve socket gönderimi birbirinden bağımsız hatlardır. Yavaş socket native dosya yazımını bekletemez: sınırlı gönderim kuyruğu ses ve keyframe'e öncelik verir, atılabilir video karelerinden sonra yeni keyframe ister. Sıralı WebM socket akışı güvenle küçültülemiyorsa transport_backpressure olayı üretilerek socket gönderimi durur, fakat yerel kayıt devam eder.

Audio ve video player

Tek player controller; platformun desteklediği cihaz dosyalarını ve HTTP(S) adreslerini oynatır. Sistem player’ının açamadığı WebM için SDK’nın özel libvpx/Opus hattı kullanılır. Byte stream ve WebSocket canlı yayınları SDK’nın sıralı WebM canlı protokolünü kullanır:

final oynatici = LiveMediaPlayerController();

await oynatici.open(
  const MediaFileSource('/dosya/yolu/video.mp4'),
  autoplay: true,
);

// URL için:
await oynatici.open(
  MediaUrlSource.parse('ornek.com/master.m3u8'),
);

// Ham WebM parçaları veya SDK binary frame'leri gönderen WebSocket için:
await oynatici.open(
  WebSocketMediaSource(Uri.parse('wss://ornek.com/izle/oda-42')),
  autoplay: true,
);

MediaUrlSource.parse, kullanıcının yazdığı adreste protokol yoksa otomatik olarak https:// ekler; açıkça yazılan http:// veya https:// değerini korur. MediaUrlSource(Uri(...)) kullanıldığında da controller, kaynak açılmadan önce eksik protokolü HTTPS olarak tamamlar.

Video arayüzü:

LiveMediaVideoPlayer(
  controller: oynatici,
  // Boş bırakılırsa videonun 4:3, 16:9, 9:16 vb. oranı kullanılır.
  scale: 0.9,
  fit: BoxFit.contain,
  fullscreenConfiguration: const FullscreenConfiguration(
    orientation: FullscreenOrientation.landscape,
  ),
)

scale yerine width ve/veya height verilebilir. Video player; oynat/duraklat, progress bar üzerinden ileri-geri sarma, izlenen/toplam süre, ses, sessize alma, 0.25x–3x hız, tam ekran ve yatay/dikey görüntüleme kontrollerini içerir. Yalnız ses için LiveMediaAudioPlayer(controller: oynatici) kullanılır. Canlı WebSocket yayını ileri-geri sarılamaz; kayıtlı ve seek edilebilir kaynaklarda progress ve sarma açıktır.

Tam özelleştirilebilir kontroller

Video yüzeyi, medya state'i ve kontrol arayüzü birbirinden ayrıdır. Üç kullanım seviyesi vardır:

  • Hazır kontrol arayüzü için LiveMediaVideoPlayer doğrudan kullanılır.
  • controlsBuilder verilirse SDK kontrol katmanı, uygulamanın kendi Flutter widget'larıyla değiştirilir. Bu kontroller görünür kalır ve gesture'larını kendisi yönetir; SDK otomatik gizleme yapmaz veya yüzey dokunuşlarını yutmaz.
  • Tamamen harici yerleşim için LiveMediaVideoSurface doğrudan kullanılır. Hiç overlay istenmiyorsa LiveMediaVideoPlayer içinde showControls: false kullanılabilir.

LiveMediaPlayerController, ValueListenable<LiveMediaPlayerState>'dir ve ayrıca broadcast states akışını sunar. Özel progress bar örneği:

LiveMediaVideoPlayer(
  controller: oynatici,
  controlsBuilder: (context, controller, state) {
    final zaman = state.timeline;
    return Align(
      alignment: Alignment.bottomCenter,
      child: Slider(
        value: (zaman.progress ?? 0) * 100,
        max: 100,
        onChanged: state.capabilities.canSeek && zaman.hasDuration
            ? (deger) => controller.seekTo(
                zaman.duration * (deger / 100),
              )
            : null,
      ),
    );
  },
)

state.timeline (veya oynatici.timeline); konum, buffer konumu, süre, canlı/seek bilgisi, platform sağlarsa seek edilebilir sınırlar ve süre sonluysa progress değerini verir. Canlı ya da sınırı olmayan timeline'da progress null olur; bu kaynaklara yapay sabit süre verilmemelidir.

state.capabilities (veya oynatici.capabilities), hangi butonların o an anlamlı olduğunu bildirir: canPlay, canPause, canSeek, canSetVolume, canMute, canChangePlaybackSpeed, canChangeAudioOutput ve canGoLive. Controller'da play, pause, stop, seek/seekTo, seekBy, togglePlayback, setVolume, setMuted, setPlaybackSpeed/ setPlaybackRate, setAudioOutput ve goLive bulunur.

Seek edilebilir ses veya video tamamlandıktan sonra play(), oynatmaya başlamadan önce otomatik olarak başa sarar. Bu davranış hem SDK kontrollerinde hem de uygulamanın kendi play düğmesinde aynıdır; canlı kaynaklar mevcut davranışını korur.

Harici düzen için LiveMediaVideoSurface(controller: oynatici) çizilip ValueListenableBuilder<LiveMediaPlayerState> veya oynatici.states dinlenebilir. Böylece markalı progress bar, bölüm işaretleri, büyük erişilebilir butonlar, canlıya dön düğmesi, izleme analitiği veya tamamen farklı bir player deneyimi oluşturulabilir.

Kaynak açıldıktan sonra ses çıkışı seçilebilir:

await oynatici.setAudioOutput(PlayerAudioOutput.speaker);       // Hoparlör
await oynatici.setAudioOutput(PlayerAudioOutput.receiver);      // Ahize
await oynatici.setAudioOutput(PlayerAudioOutput.systemDefault); // Sistem

Ahize seçimi yalnız dahili telefon ahizesi bulunan cihazlarda kullanılabilir. systemDefault seçildiğinde kablolu, Bluetooth, AirPlay ve diğer harici çıkışların yönetimi işletim sistemine bırakılır.

Ekran kapatılırken oynatici.dispose() çağrılmalıdır. Mevcut native altyapıda her Flutter engine için aynı anda bir aktif player oturumu desteklenir.

Format seçimi ve native fallback

Player, dosya WebM olmadığı için kaynağı reddetmez:

Kaynak Android iOS
Yerel dosya / HTTP(S) URL Media3 extractor + cihaz decoder’ı AVPlayer/AVFoundation
HLS Media3 HLS AVPlayer
MPEG-DASH Media3 DASH Cihaz/AVFoundation desteğine bağlı; Apple için HLS önerilir
WebM VP8/VP9 + Opus dosya/URL Media3 SDK WebM demuxer + libvpx + Opus
Ogg Opus .opus / .ogg / .oga dosya/URL Media3 SDK Ogg demuxer + Opus
WebSocket / byte stream SDK WebM canlı protokolü SDK WebM canlı protokolü

iOS'ta SDK, Opus kanal eşlemesini OpusHead içinden okur. 5.1 dahil çok kanallı WebM/Ogg Opus kaynakları libopus multistream decoder ile çözülür ve oynatma için stereo çıkışa indirgenir.

Native tarafta yaygın olarak MP4/M4V/MOV içinde H.264/HEVC + AAC, M4A/AAC, MP3 ve WAV/PCM oynatılır. Gerçek destek; işletim sistemi, cihaz decoder’ı, codec profile/level, bit derinliği, DRM ve container-track birleşimine bağlıdır. Android Media3 ayrıca iç codec mevcutsa Matroska/WebM, Ogg, FLV, MPEG-TS, HLS ve DASH gibi formatları tanır.

.opus dosyasının standart Ogg container içinde Opus paketleri taşıması beklenir. Herhangi bir container ve başlık içermeyen ham Opus paketleri, kendi başına tanımlanabilir ve seek edilebilir bir medya dosyası değildir; önce Ogg veya WebM içine mux edilmelidir.

Dosya uzantısı yalnız ipucudur. Native backend medya track’lerini inceler ve bulunan gerçek container/codec bilgisini döndürür. Container geçerli olsa bile codec profili desteklenmiyorsa, DRM lisansı yoksa, sunucu gerekli range/adaptive isteklerini karşılamıyorsa veya dosya eksikse oynatma başarısız olabilir.

WebSocket daha dar kapsamlıdır: normal MP4/MOV dosyasının rastgele parçaları kendiliğinden canlı akış olmaz. Mevcut SDK canlı protokolü sıralı WebM parçaları taşır. WebSocket üzerinden fMP4 veya MPEG-TS için açık stream türü ve ona uygun incremental parser gerekir.

Player kaynakları ve işlemleri:

API Kullanımı
MediaFileSource(path) Uygulamanın okuyabildiği medya dosyası; native backend otomatik seçilir.
MediaUrlSource(uri, headers:) HTTP(S) medya/adaptive manifest; eksik protokol otomatik HTTPS yapılır.
MediaUrlSource.parse(metin, headers:) Metin girişi için önerilir; ornek.com/ses.mp3 adresini https:// yazmadan kabul eder.
WebSocketMediaSource(uri, protocols:) Ham WebM parçaları veya SDK-framed binary mesajlar.
MediaByteStreamSource(stream) Uygulamanın ürettiği sıralı WebM byte stream.
open(source, autoplay:) Önceki kaynağı kapatır, native decoder’ı hazırlar ve MediaInfo döndürür.
play, pause, stop Oynatma yaşam döngüsü.
seek Yalnız kayıtlı/seek edilebilir kaynak.
seekTo, seekBy Özel kontroller için mutlak ve göreli sarma yardımcıları.
togglePlayback() Mevcut duruma göre oynatır veya duraklatır.
volume/mute 0–1 ses ve sessiz kontrolü.
setPlaybackSpeed, setPlaybackRate 0.25–3.0 oynatma hızı.
goLive() Canlı kaynağın en güncel tampon konumuna döner.
timeline, capabilities, states Özel kontroller için timeline, kullanılabilir aksiyonlar ve state akışı.
LiveMediaVideoSurface Yalnız video texture'ı; tamamen harici player düzeni için.
LiveMediaVideoPlayer.controlsBuilder Varsayılan video içi kontrol katmanını değiştirir.

oynatici.state; opening, ready, playing, paused, buffering, completed, failed ve disposed durumlarıyla birlikte konum, buffer, süre, ses, hız, texture, metadata ve hata bilgisini verir.

Video boyutlandırma kuralları:

  • Hiç ölçü verilmezse videonun bulunan 4:3, 16:9, 9:16 vb. oranı kullanılır.
  • Yalnız width verilirse yükseklik, yalnız height verilirse genişlik orandan hesaplanır.
  • İkisi birlikte verilirse player o tasarım kutusuna yerleşir.
  • scale, parent genişliğine göre oransal büyüklük verir.
  • scale ile width/height aynı anda kullanılmaz.
  • BoxFit.contain tüm kareyi korur; cover alanı doldurup kenarları kırpabilir.
  • Tam ekran yönü automatic, portrait, landscape veya all seçilebilir.

WebSocket ve özel taşıma katmanı #

SDK herhangi bir sunucu protokolüne bağımlı değildir. WebSocketLiveMediaTransport kullanabilir veya SignalR, native WebSocket ya da kendi protokolünüz için LiveMediaTransport arayüzünü uygulayabilirsiniz. Yukarıdaki WebSocket transport örneğinde bağlantı ve mesaj metadata'sı gösterilmiştir.

Pakete dahil WebSocket taşıyıcısı, JSON metadata ile ham binary medya verisini tek bir binary frame içinde gönderir. Base64 kullanılmadığı için medya verisi gereksiz yere büyümez.

Basit WebSocket sunucusu nasıl olur?

Sunucu yalnızca yayın verisini diğer bağlantılara aktaracaksa codec çözmesine veya mux işlemi yapmasına gerek yoktur. Yukarıdaki Minimal WebSocket relay örneği, gelen binary frame'leri diğer istemcilere olduğu gibi ileten çok küçük bir Node.js sunucusu gösterir.

Bu örnek yalnızca mimariyi anlatır. Gerçek kullanımda wss://, kimlik doğrulama, odalar, yetkilendirme, hız sınırı, yavaş istemci kontrolü, izleme ve yatay ölçekleme eklenmelidir. Alıcı Flutter uygulaması WebSocketLiveMediaTransport.decode() ile metadata ve ham payload'ı ayırabilir.

WebM çıkışı #

outputMode: StreamOutputMode.webm seçildiğinde controller.packets, sıralı WebmPacket parçaları üretir. Bu parçaları sıralarını değiştirmeden birleştirin. Muxer'ın son baytları bağlantı kapanmadan gönderilebilsin diye yayını durdururken her zaman await controller.stop() kullanın.

Kamera önizlemesi #

Controller initialize edildikten sonra controller.getVideoTextureId() ile texture kimliğini alın ve LiveMediaPreview(textureId: textureId) bileşenine verin. Aynı genel API Android ve iOS'ta çalışır; video yayını başladığında kareler görünür, yayın durduğunda texture üretimi durur.

Ses işleme ve istatistikler #

AudioProcessingConfiguration üzerinden gain, giriş cihazı, kanal modu, örnekleme hızı, yankı giderme, gürültü azaltma, otomatik kazanç, ses etkinliği algılama ve sessizlik eşiği ayarlanabilir. Platform ayrıntıları için AUDIO_FEATURES.md dosyasına bakın.

controller.events ve controller.statistics; ses seviyesi, yayın durumu, kodlama/yükleme bitrate değerleri, CPU kullanımı, etkin ses efektleri ve iOS termal durumu gibi bilgileri sağlar.

Örnek uygulama #

example uygulaması etkileşimli bir öğrenme laboratuvarıdır. Sunucu olmadan yerel capture, bütün yayın/ses/video ayarları, native kayıt yolu ve adı, zengin kayıt sonucu, canlı istatistikler ve son kayıt/dosya/URL/WebSocket player senaryolarını tek ekranda öğretir.

cd example
flutter pub get
flutter run

Önce arayüzde WebSocket’i kapalı tutarak capture ve kaydı doğrulayın. Sonra Örnek arayüzden WebSocket gönderimini açın, IP adresini veya WebSocket URL’sini girin ve relay sunucunuzu test edin. Örnek, yerel IP adresini otomatik ws://, alan adını wss:// yapar; kayıt ile socket gönderiminin aynı anda çalışmasını da gösterir.

Lisans #

Paket MIT Lisansı ile sunulur. iOS paketine dahil edilen libvpx ve libopus ikililerinin ayrı koşulları THIRD_PARTY_NOTICES.md dosyasında belirtilmiştir.

0
likes
0
points
682
downloads

Publisher

unverified uploader

Weekly Downloads

Self-hosted Flutter live streaming with native Opus and VP8/VP9 encoding, WebM output, advanced camera/audio controls, and any binary WebSocket server.

Repository (GitHub)
View/report issues

Topics

#streaming #audio #video #webm #websocket

License

unknown (license)

Dependencies

flutter, plugin_platform_interface, web_socket_channel

More

Packages that depend on live_media_stream_sdk

Packages that implement live_media_stream_sdk