{"id":18112657,"url":"https://github.com/media-kit/media-kit","last_synced_at":"2026-01-11T13:34:51.589Z","repository":{"id":65317162,"uuid":"462770331","full_name":"media-kit/media-kit","owner":"media-kit","description":"A cross-platform video player \u0026 audio player for Flutter \u0026 Dart.","archived":false,"fork":false,"pushed_at":"2025-04-23T15:20:26.000Z","size":78473,"stargazers_count":1353,"open_issues_count":207,"forks_count":255,"subscribers_count":17,"default_branch":"main","last_synced_at":"2025-04-23T16:39:18.259Z","etag":null,"topics":["android","audio","audio-player","c","cpp","dart","flutter","hacktoberfest","ios","java","libmpv","linux","macos","media-player","obj-c","swift","video","video-player","web","windows"],"latest_commit_sha":null,"homepage":"https://github.com/media-kit/media-kit","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/media-kit.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":["alexmercerind"]}},"created_at":"2022-02-23T14:30:01.000Z","updated_at":"2025-04-23T15:20:14.000Z","dependencies_parsed_at":"2023-11-11T08:20:49.120Z","dependency_job_id":"20af1229-a293-4eff-8030-3ee9726781df","html_url":"https://github.com/media-kit/media-kit","commit_stats":null,"previous_names":["media-kit/media-kit","alexmercerind/media_kit","alexmercerind/libmpv.dart"],"tags_count":148,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/media-kit%2Fmedia-kit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/media-kit%2Fmedia-kit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/media-kit%2Fmedia-kit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/media-kit%2Fmedia-kit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/media-kit","download_url":"https://codeload.github.com/media-kit/media-kit/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251565769,"owners_count":21609978,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","audio","audio-player","c","cpp","dart","flutter","hacktoberfest","ios","java","libmpv","linux","macos","media-player","obj-c","swift","video","video-player","web","windows"],"created_at":"2024-11-01T02:00:42.539Z","updated_at":"2026-01-11T13:34:51.565Z","avatar_url":"https://github.com/media-kit.png","language":"Dart","funding_links":["https://github.com/sponsors/alexmercerind"],"categories":["Dart"],"sub_categories":[],"readme":"# [package:media_kit](https://github.com/media-kit/media-kit)\n\n#### A cross-platform video player \u0026 audio player for Flutter \u0026 Dart.\n\n[![](https://img.shields.io/discord/1079685977523617792?color=33cd57\u0026label=Discord\u0026logo=discord\u0026logoColor=discord)](https://discord.gg/h7qf2R9n57) [![Github Actions](https://github.com/media-kit/media-kit/actions/workflows/ci.yml/badge.svg)](https://github.com/media-kit/media-kit/actions/workflows/ci.yml)\n\n\u003chr\u003e\n\n\u003cstrong\u003eSponsored with 💖 by\u003c/strong\u003e\n\n\u003ca href=\"https://getstream.io/chat/sdk/flutter/?utm_source=alexmercerind_dart\u0026utm_medium=Github_Repo_Content_Ad\u0026utm_content=Developer\u0026utm_campaign=alexmercerind_December2022_FlutterSDK_klmh22\" target=\"_blank\"\u003e\n  \u003cpicture\u003e\n    \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"https://user-images.githubusercontent.com/28951144/204903234-4a64b63c-2fc2-4eef-be44-d287d27021e5.svg\"\u003e\n    \u003csource media=\"(prefers-color-scheme: light)\" srcset=\"https://user-images.githubusercontent.com/28951144/204903022-bbaa49ca-74c2-4a8f-a05d-af8314bfd2cc.svg\"\u003e\n    \u003cimg alt=\"Stream Chat\" width=\"200\" height=\"auto\" src=\"https://user-images.githubusercontent.com/28951144/204903022-bbaa49ca-74c2-4a8f-a05d-af8314bfd2cc.svg\"\u003e\n  \u003c/picture\u003e\n\u003c/a\u003e\n\u003cbr\u003e\u003c/br\u003e\n\u003cstrong\u003e\n  \u003ca href=\"https://getstream.io/chat/sdk/flutter/?utm_source=alexmercerind_dart\u0026utm_medium=Github_Repo_Content_Ad\u0026utm_content=Developer\u0026utm_campaign=alexmercerind_December2022_FlutterSDK_klmh22\" target=\"_blank\"\u003e\n  Try the Flutter Chat tutorial\n  \u003c/a\u003e\n\u003c/strong\u003e\n\n\u003cbr\u003e\u003c/br\u003e\n\n\u003ca href=\"https://ottomatic.io/\" target=\"_blank\"\u003e\n  \u003cpicture\u003e\n    \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"https://user-images.githubusercontent.com/28951144/228648854-e5d7c557-ee92-47b2-a037-17b447873e1c.svg\"\u003e\n    \u003csource media=\"(prefers-color-scheme: light)\" srcset=\"https://user-images.githubusercontent.com/28951144/228648844-f2a59ab1-12cd-4fee-bc8d-b2d332033c45.svg\"\u003e\n    \u003cimg alt=\"Stream Chat\" width=\"200\" height=\"auto\" src=\"https://user-images.githubusercontent.com/28951144/228648844-f2a59ab1-12cd-4fee-bc8d-b2d332033c45.svg\"\u003e\n  \u003c/picture\u003e\n\u003c/a\u003e\n\u003cbr\u003e\u003c/br\u003e\n\u003cstrong\u003e\n  \u003ca href=\"https://ottomatic.io/\" target=\"_blank\"\u003e\n  Clever Apps for Film Professionals\n  \u003c/a\u003e\n\u003c/strong\u003e\n\n## Installation\n\n[package:media_kit](https://github.com/media-kit/media-kit) is split into multiple packages to improve modularity \u0026 reduce bundle size.\n\n#### For apps that need video playback:\n\n```yaml\ndependencies:\n  media_kit: ^1.2.6 # Primary package.\n  media_kit_video: ^2.0.1 # For video rendering.\n  media_kit_libs_video: ^1.0.7 # Native video dependencies.\n```\n\n#### For apps that need audio playback:\n\n```yaml\ndependencies:\n  media_kit: ^1.2.6 # Primary package.\n  media_kit_libs_audio: ^1.0.7 # Native audio dependencies.\n```\n\n**Notes:**\n\n- The video libraries should be selected if both video \u0026 audio support is needed.\n- The `media_kit_libs_video` \u0026 `media_kit_libs_audio` packages should not be mixed.\n- The performance in [\"Release\" mode](https://docs.flutter.dev/testing/build-modes#release) is substantially higher than in [\"Debug\" mode](https://docs.flutter.dev/testing/build-modes#debug).\n- [Enable --split-per-abi](https://docs.flutter.dev/deployment/android#what-is-a-fat-apk) or [use app bundle (instead of APK)](https://docs.flutter.dev/deployment/android#when-should-i-build-app-bundles-versus-apks) on Android.\n\n## Platforms\n\n| Platform  | Video | Audio | Notes                              | Demo                                                                                                                        |\n| --------- | ----- | ----- | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |\n| Android   | ✅    | ✅    | Android 5.0 or above.              | [Download](https://github.com/media-kit/media-kit/releases/download/media_kit-v1.1.10/media_kit_test_android-arm64-v8a.apk) |\n| iOS       | ✅    | ✅    | iOS 9 or above.                    | [Download](https://github.com/media-kit/media-kit/releases/download/media_kit-v1.1.10/media_kit_test_ios_arm64.7z)          |\n| macOS     | ✅    | ✅    | macOS 10.9 or above.               | [Download](https://github.com/media-kit/media-kit/releases/download/media_kit-v1.1.10/media_kit_test_macos_universal.7z)    |\n| Windows   | ✅    | ✅    | Windows 7 or above.                | [Download](https://github.com/media-kit/media-kit/releases/download/media_kit-v1.1.10/media_kit_test_win32_x64.7z)          |\n| GNU/Linux | ✅    | ✅    | Any modern GNU/Linux distribution. | [Download](https://github.com/media-kit/media-kit/releases/download/media_kit-v1.1.10/media_kit_test_linux_x64.7z)          |\n| Web       | ✅    | ✅    | Any modern web browser.            | [Visit](https://media-kit.github.io/media-kit/)                                                                             |\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\n      Android\n    \u003c/td\u003e\n    \u003ctd\u003e\n      iOS\n    \u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\n      \u003cimg src=\"https://github.com/media-kit/media-kit/assets/28951144/cf93a1fd-e1d8-4d1c-8bd5-cc393cef1ce9\" height=\"400\" alt=\"Android\"\u003e\u003c/img\u003e\n      \u003cimg src=\"https://github.com/media-kit/media-kit/assets/28951144/aea1f480-51e2-452a-b53c-c0e27f71f0d8\" height=\"400\" alt=\"Android\"\u003e\u003c/img\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      \u003cimg src=\"https://github.com/media-kit/media-kit/assets/28951144/e8ce64cb-1ea9-4a3e-bc9c-db620abf88c9\" height=\"400\" alt=\"iOS\"\u003e\u003c/img\u003e\n      \u003cimg src=\"https://github.com/media-kit/media-kit/assets/28951144/d7159df2-1df1-46d3-84f8-238e2a66bfbc\" height=\"400\" alt=\"iOS\"\u003e\u003c/img\u003e\n    \u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\n      macOS\n    \u003c/td\u003e\n    \u003ctd\u003e\n      Windows\n    \u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\n      \u003cimg src=\"https://github.com/media-kit/media-kit/assets/28951144/fca8dbbf-4262-431f-a04a-f3aa6afb2911\" height=\"200\" alt=\"macOS\"\u003e\u003c/img\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      \u003cimg src=\"https://github.com/media-kit/media-kit/assets/28951144/742b0016-da58-42de-9880-ecaa0604c2b2\" height=\"200\" alt=\"Windows\"\u003e\u003c/img\u003e\n    \u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\n      GNU/Linux\n    \u003c/td\u003e\n    \u003ctd\u003e\n      Web\n    \u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\n      \u003cimg src=\"https://github.com/media-kit/media-kit/assets/28951144/8cd63750-6746-4c75-bc4e-cca5e4c61890\" height=\"200\" alt=\"GNU/Linux\"\u003e\u003c/img\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      \u003cimg src=\"https://github.com/media-kit/media-kit/assets/28951144/feb9fdf2-095f-43db-96af-f7782985238d\" height=\"200\" alt=\"Web\"\u003e\u003c/img\u003e\n    \u003c/td\u003e\n\u003c/table\u003e\n\n- ✅ Video playback\n- ✅ Audio playback\n- ✅ Cross platform\n- ✅ Wide format/codec support\n- ✅ Hardware/GPU acceleration\n- ✅ Playlist support with next/previous/jump/shuffle\n- ✅ Volume/Rate/Pitch change\n- ✅ Video/Audio/Subtitle track selection\n- ✅ External audio/subtitle track selection\n- ✅ HTTP headers\n- ✅ Video controls\n- ✅ Subtitle styling\n- ✅ Screenshot\n\n## TL;DR\n\nA quick usage example.\n\n```dart\nimport 'package:flutter/material.dart';\n\n// Make sure to add following packages to pubspec.yaml:\n// * media_kit\n// * media_kit_video\n// * media_kit_libs_video\nimport 'package:media_kit/media_kit.dart';                      // Provides [Player], [Media], [Playlist] etc.\nimport 'package:media_kit_video/media_kit_video.dart';          // Provides [VideoController] \u0026 [Video] etc.\n\nvoid main() {\n  WidgetsFlutterBinding.ensureInitialized();\n  // Necessary initialization for package:media_kit.\n  MediaKit.ensureInitialized();\n  runApp(\n    const MaterialApp(\n      home: MyScreen(),\n    ),\n  );\n}\n\nclass MyScreen extends StatefulWidget {\n  const MyScreen({Key? key}) : super(key: key);\n  @override\n  State\u003cMyScreen\u003e createState() =\u003e MyScreenState();\n}\n\nclass MyScreenState extends State\u003cMyScreen\u003e {\n  // Create a [Player] to control playback.\n  late final player = Player();\n  // Create a [VideoController] to handle video output from [Player].\n  late final controller = VideoController(player);\n\n  @override\n  void initState() {\n    super.initState();\n    // Play a [Media] or [Playlist].\n    player.open(Media('https://user-images.githubusercontent.com/28951144/229373695-22f88f13-d18f-4288-9bf1-c3e078d83722.mp4'));\n  }\n\n  @override\n  void dispose() {\n    player.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Center(\n      child: SizedBox(\n        width: MediaQuery.of(context).size.width,\n        height: MediaQuery.of(context).size.width * 9.0 / 16.0,\n        // Use [Video] widget to display video output.\n        child: Video(controller: controller),\n      ),\n    );\n  }\n}\n```\n\n**Note:** You may need to add required [permissions](#permissions) to your project (only if required).\n\n## Guide\n\nA usage guide for [package:media_kit](https://github.com/media-kit/media-kit).\n\n**Tip:** Use \u003ckbd\u003eCtrl\u003c/kbd\u003e + \u003ckbd\u003eF\u003c/kbd\u003e to quickly search for things.\n\n### Contents\n\n- [Initialization](#initialization)\n- [Create a `Player`](#create-a-player)\n- [Dispose a `Player`](#dispose-a-player)\n- [Open a `Media` or `Playlist`](#open-a-media-or-playlist)\n- [Play, pause or play/pause](#play-pause-or-playpause)\n- [Stop](#stop)\n- [Seek](#seek)\n- [Loop or repeat](#loop-or-repeat)\n- [Set volume, rate or pitch](#set-volume-rate-or-pitch)\n- [Handle playback events](#handle-playback-events)\n- [Shuffle the queue](#shuffle-the-queue)\n- [Use HTTP headers](#use-http-headers)\n- [Use `extras` to store additional data with `Media`](#use-extras-store-additional-data-with-media)\n- [Go to next, previous or any other position in queue](#go-to-next-previous-or-any-other-position-in-queue)\n- [Modify `Player`'s queue](#modify-players-queue)\n- [Select video, audio or subtitle track](#select-video-audio-or-subtitle-track)\n- [Select audio device](#select-audio-device)\n- [Display the video](#display-the-video)\n- [Capture screenshot](#capture-screenshot)\n- [Customize subtitles](#customize-subtitles)\n- [Load external subtitle track](#load-external-subtitle-track)\n- [Load external audio track](#load-external-audio-track)\n- [Video controls](#video-controls)\n- [Next steps](#next-steps)\n\n### Initialization\n\n`MediaKit.ensureInitialized` must be called before using the package:\n\n```dart\nvoid main() {\n  WidgetsFlutterBinding.ensureInitialized();\n  // Make sure to add the required packages to pubspec.yaml:\n  // * https://github.com/media-kit/media-kit#installation\n  // * https://pub.dev/packages/media_kit#installation\n  MediaKit.ensureInitialized();\n  runApp(const MyApp());\n}\n```\n\nThe method also has some optional arguments to customize the global behavior. To handle any initialization errors, this may be surrounded by `try`/`catch`.\n\n### Create a `Player`\n\nA `Player` instance is used to start \u0026 control the playback of a media source e.g. URL or file.\n\n```dart\nfinal Player player = Player();\n```\n\nAdditional options may be provided using the `configuration` argument in the constructor. In general situations, you will never require this.\n\n```dart\nfinal Player player = Player(\n  configuration: PlayerConfiguration(\n    // Supply your options:\n    title: 'My awesome package:media_kit application',\n    ready: () {\n      print('The initialization is complete.');\n    },\n  ),\n);\n```\n\n### Dispose a `Player`\n\nIt is extremely important to release the allocated resources back to the system:\n\n```dart\nawait player.dispose();\n```\n\n### Open a `Media` or `Playlist`\n\nA `Playable` can either be a `Media` or a `Playlist`.\n\n- `Media`: Single playback source (file or URL).\n- `Playlist`: Queue of playback sources (file or URL).\n\nUse the `Player.open` method to load \u0026 start playback.\n\n#### `Media`\n\n```dart\nfinal playable = Media('https://user-images.githubusercontent.com/28951144/229373695-22f88f13-d18f-4288-9bf1-c3e078d83722.mp4');\nawait player.open(playable);\n```\n\n#### `Playlist`\n\n```dart\nfinal playable = Playlist(\n  [\n    Media('https://user-images.githubusercontent.com/28951144/229373695-22f88f13-d18f-4288-9bf1-c3e078d83722.mp4'),\n    Media('https://user-images.githubusercontent.com/28951144/229373709-603a7a89-2105-4e1b-a5a5-a6c3567c9a59.mp4'),\n    Media('https://user-images.githubusercontent.com/28951144/229373716-76da0a4e-225a-44e4-9ee7-3e9006dbc3e3.mp4'),\n    Media('https://user-images.githubusercontent.com/28951144/229373718-86ce5e1d-d195-45d5-baa6-ef94041d0b90.mp4'),\n    Media('https://user-images.githubusercontent.com/28951144/229373720-14d69157-1a56-4a78-a2f4-d7a134d7c3e9.mp4'),\n  ],\n);\nawait player.open(playable);\n```\n\n**Notes:**\n\n1. By default, this will automatically start playing the playable. This may be disabled as follows:\n\n```dart\nawait player.open(\n  playable,\n  play: false,\n);\n```\n\n2. By default, the playlist will start at the index `0`. This may be changed as follows:\n\n```dart\nfinal playable = Playlist(\n  [\n    Media('https://user-images.githubusercontent.com/28951144/229373695-22f88f13-d18f-4288-9bf1-c3e078d83722.mp4'),\n    Media('https://user-images.githubusercontent.com/28951144/229373709-603a7a89-2105-4e1b-a5a5-a6c3567c9a59.mp4'),\n    Media('https://user-images.githubusercontent.com/28951144/229373716-76da0a4e-225a-44e4-9ee7-3e9006dbc3e3.mp4'),\n    Media('https://user-images.githubusercontent.com/28951144/229373718-86ce5e1d-d195-45d5-baa6-ef94041d0b90.mp4'),\n    Media('https://user-images.githubusercontent.com/28951144/229373720-14d69157-1a56-4a78-a2f4-d7a134d7c3e9.mp4'),\n  ],\n  // Declare the starting position.\n  index: 0,\n);\nawait player.open(playable);\n```\n\n### Play, pause or play/pause\n\nThe 3 methods are:\n\n```dart\nawait player.play();\n```\n\n```dart\nawait player.pause();\n```\n\n```dart\nawait player.playOrPause();\n```\n\n### Stop\n\nThe `stop` method may be used to stop the playback of currently opened `Media` or `Playlist`.\n\n```dart\nawait player.stop();\n```\n\nIt does not release allocated resources back to the system (unlike [`dispose`](#dispose-a-player)) \u0026 `Player` still stays usable.\n\n### Seek\n\nSupply the final position to `Player.seek` method as `Duration`:\n\n```dart\nawait player.seek(\n  const Duration(\n    minutes: 6,\n    seconds: 9,\n  ),\n);\n```\n\n### Loop or repeat\n\nThree `PlaylistMode`s are available:\n\n- `PlaylistMode.none`: End playback once end of the playlist is reached.\n- `PlaylistMode.single`: Indefinitely loop over the currently playing file in the playlist.\n- `PlaylistMode.loop`: Loop over the playlist \u0026 restart it from beginning once end is reached.\n\n```dart\nawait player.setPlaylistMode(PlaylistMode.single);\n```\n\n### Set volume, rate or pitch\n\n#### Set the volume\n\nThis controls the loudness of audio output. The maximum volume is `100.0`.\n\n```dart\nawait player.setVolume(50.0);\n```\n\n#### Set the rate\n\nThis controls the playback speed.\n\n```dart\nawait player.setRate(1.5);\n```\n\n#### Set the pitch\n\nThis controls the pitch of the audio output.\n\n```dart\nawait player.setPitch(1.2);\n```\n\n**Note:** This requires `pitch` argument to be `true` in `PlayerConfiguration`.\n\n### Handle playback events\n\nYou can access or subscribe to `Player`'s state changes.\n\nEvent handling is an extremely important part of media playback. It is used to show changes in the UI, handle errors, detect the occurrence of play/pause, end-of-file, position updates etc.\n\n- `Player.stream.*`: Provides access to `Player`'s state as [`Stream`](https://dart.dev/tutorials/language/streams)(s).\n- `Player.state.*`: Provides access to `Player`'s state directly (for instantaneous access).\n\nA typical example will be:\n\n```dart\nplayer.stream.playing.listen(\n  (bool playing) {\n    if (playing) {\n      // Playing.\n    } else {\n      // Paused.\n    }\n  },\n);\nplayer.stream.position.listen(\n  (Duration position) {\n    setState(() {\n      // Update UI.\n    });\n  },\n);\n```\n\nThe following state(s) are available as events:\n\n| Type                        | Name           | Description                                                                                              |\n| --------------------------- | -------------- | -------------------------------------------------------------------------------------------------------- |\n| `Stream\u003cPlaylist\u003e`          | `playlist`     | Currently opened media sources.                                                                          |\n| `Stream\u003cbool\u003e`              | `playing`      | Whether playing or not.                                                                                  |\n| `Stream\u003cbool\u003e`              | `completed`    | Whether end of currently playing media source has been reached.                                          |\n| `Stream\u003cDuration\u003e`          | `position`     | Current playback position.                                                                               |\n| `Stream\u003cDuration\u003e`          | `duration`     | Current playback duration.                                                                               |\n| `Stream\u003cdouble\u003e`            | `volume`       | Current volume.                                                                                          |\n| `Stream\u003cdouble\u003e`            | `rate`         | Current playback rate.                                                                                   |\n| `Stream\u003cdouble\u003e`            | `pitch`        | Current pitch.                                                                                           |\n| `Stream\u003cbool\u003e`              | `buffering`    | Whether buffering or not.                                                                                |\n| `Stream\u003cDuration\u003e`          | `buffer`       | Current buffer position. This indicates how much of the stream has been decoded \u0026 cached by the demuxer. |\n| `Stream\u003cPlaylistMode\u003e`      | `playlistMode` | Current playlist mode.                                                                                   |\n| `Stream\u003cbool\u003e`              | `shuffle`      | Whether playlist is shuffled or not.                                                                     |\n| `Stream\u003cAudioParams\u003e`       | `audioParams`  | Audio parameters of the currently playing media source e.g. sample rate, channels, etc.                  |\n| `Stream\u003cVideoParams\u003e`       | `videoParams`  | Video parameters of the currently playing media source e.g. width, height, rotation etc.                 |\n| `Stream\u003cdouble?\u003e`           | `audioBitrate` | Audio bitrate of the currently playing media source.                                                     |\n| `Stream\u003cAudioDevice\u003e`       | `audioDevice`  | Currently selected audio device.                                                                         |\n| `Stream\u003cList\u003cAudioDevice\u003e\u003e` | `audioDevices` | Currently available audio devices.                                                                       |\n| `Stream\u003cTrack\u003e`             | `track`        | Currently selected video, audio and subtitle track.                                                      |\n| `Stream\u003cTracks\u003e`            | `tracks`       | Currently available video, audio and subtitle tracks.                                                    |\n| `Stream\u003cint\u003e`               | `width`        | Currently playing video's width.                                                                         |\n| `Stream\u003cint\u003e`               | `height`       | Currently playing video's height.                                                                        |\n| `Stream\u003cint\u003e`               | `subtitle`     | Currently displayed subtitle.                                                                            |\n| `Stream\u003cPlayerLog\u003e`         | `log`          | Internal logs.                                                                                           |\n| `Stream\u003cString\u003e`            | `error`        | Error messages. This may be used to handle \u0026 display errors to the user.                                 |\n\n### Shuffle the queue\n\nYou may find the requirement to shuffle the `Playlist` you `open`'d in `Player`, like some music players do.\n\n```dart\nawait player.setShuffle(true);\n```\n\n**Note:** This option is reset upon the next `Player.open` call.\n\n### Use HTTP headers\n\nDeclare the `httpHeaders` argument in `Media` constructor. It takes the HTTP headers as `Map\u003cString, String\u003e`.\n\n```dart\nfinal playable = Media(\n  'https://user-images.githubusercontent.com/28951144/229373695-22f88f13-d18f-4288-9bf1-c3e078d83722.mp4',\n  httpHeaders: {\n    'Foo': 'Bar',\n    'Accept': '*/*',\n    'Range': 'bytes=0-',\n  },\n);\n```\n\n### Use `extras` to store additional data with `Media`\n\nThe `extras` argument may be utilized to store additional data with a `Media` in form of `Map\u003cString, dynamic\u003e`.\n\n```dart\nfinal playable = Media(\n  'https://user-images.githubusercontent.com/28951144/229373695-22f88f13-d18f-4288-9bf1-c3e078d83722.mp4',\n  extras: {\n    'track': '9',\n    'year': '2012',\n    'title': 'Courtesy Call',\n    'artist': 'Thousand Foot Krutch',\n    'album': 'The End Is Where We Begin',\n  },\n);\n```\n\n### Modify `Player`'s queue\n\nYou can add or remove (etc.) a `Media` in an already playing `Playlist`:\n\n#### Add\n\nAdd a new `Media` to the back of the queue:\n\n```dart\nawait player.add(Media('https://user-images.githubusercontent.com/28951144/229373695-22f88f13-d18f-4288-9bf1-c3e078d83722.mp4'));\n```\n\n#### Remove\n\nRemove any item from the queue:\n\n```dart\nawait player.remove(0);\n```\n\n#### Move\n\nMove any item in the queue from one position to another:\n\n```dart\nawait player.move(6, 9);\n```\n\n### Go to next, previous or any other position in queue\n\n#### Skip to the next queue item\n\n```dart\nawait player.next();\n```\n\n#### Skip to the previous queue item\n\n```dart\nawait player.previous();\n```\n\n#### Skip to any other queue item\n\n```dart\nawait player.jump(5);\n```\n\n### Select video, audio or subtitle track\n\nA media source may contain multiple video, audio or subtitle tracks e.g. for multiple languages. Available video, audio or subtitle tracks are notified through `Player`'s state. See [\"Handle playback events\" section](#handle-playback-events) for related information.\n\nBy default, video, audio \u0026 subtitle track is selected automatically _i.e._ `VideoTrack.auto()`, `AudioTrack.auto()` \u0026 `SubtitleTrack.auto()`.\n\n#### Automatic selection\n\n```dart\nawait player.setVideoTrack(VideoTrack.auto());\n\nawait player.setAudioTrack(AudioTrack.auto());\n\nawait player.setSubtitleTrack(SubtitleTrack.auto());\n```\n\n#### Disable track\n\nThis may be used to essentially disable video output, disable audio output or stop rendering of subtitles etc.\n\n```dart\nawait player.setVideoTrack(VideoTrack.no());\n\nawait player.setAudioTrack(AudioTrack.no());\n\nawait player.setSubtitleTrack(SubtitleTrack.no());\n```\n\n#### Select custom track\n\n- Retrieve currently available tracks:\n\n```dart\nList\u003cVideoTrack\u003e videos = player.state.tracks.video;\nList\u003cAudioTrack\u003e audios = player.state.tracks.audio;\nList\u003cSubtitleTrack\u003e subtitles = player.state.tracks.subtitle;\n\n// Get notified as [Stream]:\nplayer.stream.tracks.listen((event) {\n  List\u003cVideoTrack\u003e videos = event.video;\n  List\u003cAudioTrack\u003e audios = event.audio;\n  List\u003cSubtitleTrack\u003e subtitles = event.subtitle;\n});\n```\n\n- Select the track:\n\n```dart\nawait player.setVideoTrack(videos[0]);\nawait player.setAudioTrack(audios[1]);\nawait player.setSubtitleTrack(subtitles[2]);\n```\n\n- Get notified about currently selected track:\n\n```dart\nVideoTrack video = player.state.track.video;\nAudioTrack audio = player.state.track.audio;\nSubtitleTrack subtitle = player.state.track.subtitle;\n\n// Get notified as [Stream]:\nplayer.stream.track.listen((event) {\n  VideoTrack video = event.video;\n  AudioTrack audio = event.audio;\n  SubtitleTrack subtitle = event.subtitle;\n});\n```\n\n### Select audio device\n\nAvailable audio devices are notified through `Player`'s state. See [\"Handle playback events\" section](#handle-playback-events) for related information.\n\nBy default, audio device is selected automatically _i.e._ `AudioDevice.auto()`.\n\n#### Default selection\n\n```dart\nawait player.setAudioDevice(AudioDevice.auto());\n```\n\n#### Disable audio output\n\n```dart\nawait player.setAudioDevice(AudioDevice.no());\n```\n\n#### Select custom audio device\n\n- Retrieve currently available audio devices:\n\n```dart\nList\u003cAudioDevice\u003e devices = player.state.audioDevices;\n\n// Get notified as [Stream]:\nplayer.stream.audioDevices.listen((event) {\n  List\u003cAudioDevice\u003e devices = event;\n});\n```\n\n- Select the audio device:\n\n```dart\nawait player.setAudioDevice(devices[1]);\n```\n\n- Get notified about currently selected audio device:\n\n```dart\nAudioDevice device = player.state.audioDevice;\n\n// Get notified as [Stream]:\nplayer.stream.audioDevice.listen((event) {\n  AudioDevice device = event;\n});\n```\n\n### Display the video\n\nThe **existing [\"TL;DR example\"](#tldr) should provide you better idea**.\n\nFor displaying the video inside Flutter UI, you must:\n\n- Create `VideoController`\n  - Pass the `Player` you already have.\n- Create `Video` widget\n  - Pass the `VideoController` you already have.\n\nThe code is easier to understand:\n\n```dart\nclass _MyScreenState extends State\u003cMyScreen\u003e {\n  late final Player player = Player();\n  late final VideoController controller = VideoController(player);\n\n  @override\n  void dispose() {\n    player.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      body: Video(\n        controller: controller,\n      ),\n    );\n  }\n}\n```\n\nThe video playback uses [hardware acceleration](https://en.wikipedia.org/wiki/Hardware_acceleration) _i.e._ GPU by default.\n\nAdditional options may be provided using the `configuration` argument in the constructor. In general situations, you will never require this.\n\n```dart\nfinal VideoController player = VideoController(\n  player,\n  configuration: const VideoControllerConfiguration(\n    // Supply your options:\n    enableHardwareAcceleration: true,      // default: true\n    width: 640,                            // default: null\n    height: 480,                           // default: null\n    // The in-code comments is best place to know more about these options:\n    // https://github.com/media-kit/media-kit/blob/main/media_kit_video/lib/src/video_controller/video_controller.dart\n  ),\n);\n```\n\n### Capture screenshot\n\nThe `screenshot` method takes the snapshot of the current video frame \u0026 returns encoded image bytes as `Uint8List`.\n\n```dart\nfinal Uint8List? screenshot = await player.screenshot();\n```\n\nAdditionally `format` argument may be specified to change the encoding format. Following formats are supported:\n\n- `image/jpeg`: Returns a JPEG encoded image.\n- `image/png`: Returns a PNG encoded image.\n- `null`: Returns BGRA pixel buffer.\n\n### Customize subtitles\n\n`SubtitleViewConfiguration` can be passed to the `Video` widget for customizing the subtitles. The code is easier to understand:\n\nNotably, `TextStyle`, `TextAlign` \u0026 `EdgeInsetsGeometry` can be provided.\n\n```dart\nVideo(\n  controller: controller,\n  subtitleViewConfiguration: const SubtitleViewConfiguration(\n    style: TextStyle(\n      height: 1.4,\n      fontSize: 24.0,\n      letterSpacing: 0.0,\n      wordSpacing: 0.0,\n      color: Color(0xffffffff),\n      fontWeight: FontWeight.normal,\n      backgroundColor: Color(0xaa000000),\n    ),\n    textAlign: TextAlign.center,\n    padding: EdgeInsets.all(24.0),\n  ),\n);\n```\n\nhttps://user-images.githubusercontent.com/28951144/253067794-73b5ca5d-e90d-4892-bc09-2a80f05c9f0b.mp4\n\n### Load external subtitle track\n\nThe `SubtitleTrack.uri` constructor can be used to load external subtitle track **with URI** e.g. SRT, WebVTT etc. The code is easier to understand:\n\n```dart\nawait player.setSubtitleTrack(\n  SubtitleTrack.uri(\n    'https://www.iandevlin.com/html5test/webvtt/upc-video-subtitles-en.vtt',\n    title: 'English',\n    language: 'en',\n  ),\n);\n```\n\nThe `SubtitleTrack.data` constructor can be used to load external subtitle track **with data** e.g. SRT, WebVTT etc. The code is easier to understand:\n\n```dart\nplayer.setSubtitleTrack(\n  SubtitleTrack.data(\n    '''WEBVTT FILE\n\n1\n00:00:03.500 --\u003e 00:00:05.000 D:vertical A:start\nEveryone wants the most from life\n\n2\n00:00:06.000 --\u003e 00:00:09.000 A:start\nLike internet experiences that are rich \u003cb\u003eand\u003c/b\u003e entertaining\n\n3\n00:00:11.000 --\u003e 00:00:14.000 A:end\nPhone conversations where people truly \u003cc.highlight\u003econnect\u003c/c\u003e\n\n4\n00:00:14.500 --\u003e 00:00:18.000\nYour favourite TV programmes ready to watch at the touch of a button\n\n5\n00:00:19.000 --\u003e 00:00:24.000\nWhich is why we are bringing TV, internet and phone together in \u003cc.highlight\u003eone\u003c/c\u003e super package\n\n6\n00:00:24.500 --\u003e 00:00:26.000\n\u003cc.highlight\u003eOne\u003c/c\u003e simple way to get everything\n\n7\n00:00:26.500 --\u003e 00:00:27.500 L:12%\nUPC\n\n8\n00:00:28.000 --\u003e 00:00:30.000 L:75%\nSimply for \u003cu\u003eeveryone\u003c/u\u003e\n''',\n    title: 'English',\n    language: 'en',\n  ),\n);\n```\n\n### Load external audio track\n\nThe `AudioTrack.uri` constructor can be used to load external audio track **with URI**. The code is easier to understand:\n\n```dart\nawait player.setAudioTrack(\n  AudioTrack.uri(\n    'https://www.iandevlin.com/html5test/webvtt/v/upc-tobymanley.mp4',\n    title: 'English',\n    language: 'en',\n  ),\n);\n```\n\n### Video controls\n\n[`package:media_kit`](https://github.com/media-kit/media-kit) provides highly-customizable pre-built video controls for usage.\n\nApart from theming, layout can be customized, position of buttons can be modified, custom buttons can be created etc. Necessary features like fullscreen, keyboard shortcuts \u0026 swipe-based controls are also supported by default.\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\n      \u003ca href=\"#materialdesktopvideocontrols\"\u003e\u003ctt\u003eMaterialDesktopVideoControls\u003c/tt\u003e\u003c/a\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      \u003ca href=\"#materialvideocontrols\"\u003e\u003ctt\u003eMaterialVideoControls\u003c/tt\u003e\u003c/a\u003e\n    \u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\n      \u003cimg height=\"312\" src=\"https://user-images.githubusercontent.com/28951144/246606748-72557578-8be4-43c6-a3df-cb0aea99c879.jpg\"\u003e\n    \u003c/td\u003e\n    \u003ctd\u003e\n      \u003cimg height=\"312\" src=\"https://user-images.githubusercontent.com/28951144/246650427-a5bbabad-6f7b-4098-9325-ebe2a3068720.jpg\"\u003e\n    \u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n- `Video` widget provides `controls` argument to display \u0026 customize video controls.\n- By default, [`AdaptiveVideoControls`](#adaptivevideocontrols) are used.\n\n#### Types\n\n| Type                                                            | Description                                                                                                                            |\n| --------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |\n| [`AdaptiveVideoControls`](#adaptivevideocontrols)               | Selects [`MaterialVideoControls`](#materialvideocontrols), [`CupertinoVideoControls`](#cupertinovideocontrols) etc. based on platform. |\n| [`MaterialVideoControls`](#materialvideocontrols)               | [Material Design](https://material.io/) video controls.                                                                                |\n| [`MaterialDesktopVideoControls`](#materialdesktopvideocontrols) | [Material Design](https://material.io/) video controls for desktop.                                                                    |\n| [`CupertinoVideoControls`](#cupertinovideocontrols)             | [iOS-style](https://developer.apple.com/design/human-interface-guidelines/designing-for-ios) video controls.                           |\n| [`NoVideoControls`](#novideocontrols)                           | Disable video controls _i.e._ only render video output.                                                                                |\n| Custom                                                          | Provide custom `builder` for video controls.                                                                                           |\n\n#### Select existing video controls\n\nModify the `controls` argument. For advanced theming of existing video controls, see [theming \u0026 modifying video controls](#theming-\u0026-modifying-video-controls) section.\n\n```dart\nScaffold(\n  body: Video(\n    controller: controller,\n    // Select [MaterialVideoControls].\n    controls: MaterialVideoControls,\n  ),\n);\n```\n\n```dart\nScaffold(\n  body: Video(\n    controller: controller,\n    // Select [CupertinoVideoControls].\n    controls: CupertinoVideoControls,\n  ),\n);\n```\n\n#### Build custom video controls\n\nPass custom builder `Widget Function(BuildContext, VideoController)` as `controls` argument.\n\n```dart\nScaffold(\n  body: Video(\n    controller: controller,\n    // Provide custom builder for controls.\n    controls: (state) {\n      return Center(\n        child: IconButton(\n          onPressed: () {\n            state.widget.controller.player.playOrPause();\n          },\n          icon: StreamBuilder(\n            stream: state.widget.controller.player.stream.playing,\n            builder: (context, playing) =\u003e Icon(\n              playing.data == true ? Icons.pause : Icons.play_arrow,\n            ),\n          ),\n          // It's not necessary to use [StreamBuilder] or to use [Player] \u0026 [VideoController] from [state].\n          // [StreamSubscription]s can be made inside [initState] of this widget.\n        ),\n      );\n    },\n  ),\n);\n```\n\n#### Use \u0026 modify video controls\n\n##### `AdaptiveVideoControls`\n\n- Selects [`MaterialVideoControls`](#materialvideocontrols), [`CupertinoVideoControls`](#cupertinovideocontrols) etc. based on platform.\n- Theming:\n  - Theme the specific controls according to sections below.\n\n##### `MaterialVideoControls`\n\n- [Material Design](https://material.io/) video controls.\n- Theming:\n  - Use `MaterialVideoControlsTheme` widget.\n  - `Video` widget(s) in the `child` tree will follow the specified theme:\n\n```dart\n// Wrap [Video] widget with [MaterialVideoControlsTheme].\nMaterialVideoControlsTheme(\n  normal: MaterialVideoControlsThemeData(\n    // Modify theme options:\n    buttonBarButtonSize: 24.0,\n    buttonBarButtonColor: Colors.white,\n    // Modify top button bar:\n    topButtonBar: [\n      const Spacer(),\n      MaterialDesktopCustomButton(\n        onPressed: () {\n          debugPrint('Custom \"Settings\" button pressed.');\n        },\n        icon: const Icon(Icons.settings),\n      ),\n    ],\n  ),\n  fullscreen: const MaterialVideoControlsThemeData(\n    // Modify theme options:\n    displaySeekBar: false,\n    automaticallyImplySkipNextButton: false,\n    automaticallyImplySkipPreviousButton: false,\n  ),\n  child: Scaffold(\n    body: Video(\n      controller: controller,\n    ),\n  ),\n);\n```\n\n- Related widgets (may be used in `primaryButtonBar`, `topButtonBar` \u0026 `bottomButtonBar`):\n  - `MaterialPlayOrPauseButton`\n  - `MaterialSkipNextButton`\n  - `MaterialSkipPreviousButton`\n  - `MaterialFullscreenButton`\n  - `MaterialCustomButton`\n  - `MaterialPositionIndicator`\n\n##### `MaterialDesktopVideoControls`\n\n- [Material Design](https://material.io/) video controls for desktop.\n- Theming:\n  - Use `MaterialDesktopVideoControlsTheme` widget.\n  - `Video` widget(s) in the `child` tree will follow the specified theme:\n\n```dart\n// Wrap [Video] widget with [MaterialDesktopVideoControlsTheme].\nMaterialDesktopVideoControlsTheme(\n  normal: MaterialDesktopVideoControlsThemeData(\n    // Modify theme options:\n    seekBarThumbColor: Colors.blue,\n    seekBarPositionColor: Colors.blue,\n    toggleFullscreenOnDoublePress: false,\n    // Modify top button bar:\n    topButtonBar: [\n      const Spacer(),\n      MaterialDesktopCustomButton(\n        onPressed: () {\n          debugPrint('Custom \"Settings\" button pressed.');\n        },\n        icon: const Icon(Icons.settings),\n      ),\n    ],\n    // Modify bottom button bar:\n    bottomButtonBar: const [\n      Spacer(),\n      MaterialDesktopPlayOrPauseButton(),\n      Spacer(),\n    ],\n  ),\n  fullscreen: const MaterialDesktopVideoControlsThemeData(),\n  child: Scaffold(\n    body: Video(\n      controller: controller,\n    ),\n  ),\n);\n```\n\n- Related widgets (may be used in `primaryButtonBar`, `topButtonBar` \u0026 `bottomButtonBar`):\n  - `MaterialDesktopPlayOrPauseButton`\n  - `MaterialDesktopSkipNextButton`\n  - `MaterialDesktopSkipPreviousButton`\n  - `MaterialDesktopFullscreenButton`\n  - `MaterialDesktopCustomButton`\n  - `MaterialDesktopVolumeButton`\n  - `MaterialDesktopPositionIndicator`\n- Keyboard shortcuts may be modified using `keyboardShortcuts` argument. Default ones are listed below:\n\n| Shortcut                    | Action                |\n| --------------------------- | --------------------- |\n| Media Play Button           | Play                  |\n| Media Pause Button          | Pause                 |\n| Media Play/Pause Button     | Play/Pause            |\n| Media Next Track Button     | Skip Next             |\n| Media Previous Track Button | Skip Previous         |\n| Space                       | Play/Pause            |\n| J                           | Seek 10s Behind       |\n| I                           | Seek 10s Ahead        |\n| Arrow Left                  | Seek 2s Behind        |\n| Arrow Right                 | Seek 2s Ahead         |\n| Arrow Up                    | Increase Volume 5%    |\n| Arrow Down                  | Decrease Volume 5%    |\n| F                           | Enter/Exit Fullscreen |\n| Escape                      | Exit Fullscreen       |\n\n##### `CupertinoVideoControls`\n\n- [iOS-style](https://developer.apple.com/design/human-interface-guidelines/designing-for-ios) video controls.\n- Theming:\n  - Use `CupertinoVideoControlsTheme` widget.\n  - `Video` widget(s) in the `child` tree will follow the specified theme:\n\n```dart\n// Wrap [Video] widget with [CupertinoVideoControlsTheme].\nCupertinoVideoControlsTheme(\n  normal: const CupertinoVideoControlsThemeData(\n    // W.I.P.\n  ),\n  fullscreen: const CupertinoVideoControlsThemeData(\n    // W.I.P.\n  ),\n  child: Scaffold(\n    body: Video(\n      controller: controller,\n    ),\n  ),\n);\n```\n\n##### `NoVideoControls`\n\n- Disable video controls _i.e._ only render video output.\n- Theming:\n  - No theming applicable.\n\n### Next steps\n\nThis guide follows a tutorial-like structure \u0026 covers nearly all features that [package:media_kit](https://github.com/media-kit/media-kit) offers. However, it is _not complete_ by any means. You are free to improve this page \u0026 add more documentation, which newcomers may find helpful. The following places can help you learn more:\n\n- [API reference](https://pub.dev/documentation/media_kit/latest/media_kit/media_kit-library.html) can be helpful for diving into deeper specifics.\n- [source-code of the demo application](https://github.com/media-kit/media-kit/tree/main/media_kit_test/lib/tests) offers some complete code samples.\n- In-code comments \u0026 docstrings happen to be the most updated source of knowledge.\n\n## Goals\n\n[package:media_kit](https://github.com/media-kit/media-kit) is a library for Flutter \u0026 Dart which **provides video \u0026 audio playback**.\n\n- **Strong:** Supports _most_ video \u0026 audio codecs.\n- **Performant:**\n  - Handles multiple FHD videos flawlessly.\n  - Rendering is GPU-powered (hardware accelerated).\n  - 4K / 8K 60 FPS is supported.\n- **Stable:** Implementation is well-tested \u0026 used across number of intensive media playback related apps.\n- **Feature Proof:** A simple usage API while offering a large number of features to target multitude of apps.\n- **Modular:** Project is split into a number of packages for reducing bundle size.\n- **Cross Platform**: Implementation works on all platforms supported by Flutter \u0026 Dart:\n  - Android\n  - iOS\n  - macOS\n  - Windows\n  - GNU/Linux\n  - Web\n- **Flexible Architecture:**\n  - Major part of implementation (80%+) is in 100% Dart ([FFI](https://dart.dev/guides/libraries/c-interop)) \u0026 shared across platforms.\n    - Makes the behavior of library same \u0026 more predictable across platforms.\n    - Makes development \u0026 implementation of new features easier \u0026 faster.\n    - Avoids separate maintenance of native implementation for each platform.\n  - Only video embedding code is platform-specific \u0026 part of separate package.\n\nYou may see project's [architecture](https://github.com/media-kit/media-kit#architecture) \u0026 [implementation](https://github.com/media-kit/media-kit#implementation) details for further information.\n\nThe project aims to meet demands of the community, this includes:\n\n1. Holding accountability.\n2. Ensuring timely maintenance.\n\n## Supported Formats\n\nA wide variety of formats \u0026 codecs are supported. Complete list may be found below:\n\n\u003cdetails\u003e\n\n```\n3dostr          3DO STR\n4xm             4X Technologies\naa              Audible AA format files\naac             raw ADTS AAC (Advanced Audio Coding)\naax             CRI AAX\nac3             raw AC-3\nace             tri-Ace Audio Container\nacm             Interplay ACM\nact             ACT Voice file format\nadf             Artworx Data Format\nadp             ADP\nads             Sony PS2 ADS\nadx             CRI ADX\naea             MD STUDIO audio\nafc             AFC\naiff            Audio IFF\naix             CRI AIX\nalaw            PCM A-law\nalias_pix       Alias/Wavefront PIX image\nalp             LEGO Racers ALP\namr             3GPP AMR\namrnb           raw AMR-NB\namrwb           raw AMR-WB\nanm             Deluxe Paint Animation\napac            raw APAC\napc             CRYO APC\nape             Monkey's Audio\napm             Ubisoft Rayman 2 APM\napng            Animated Portable Network Graphics\naptx            raw aptX\naptx_hd         raw aptX HD\naqtitle         AQTitle subtitles\nargo_asf        Argonaut Games ASF\nargo_brp        Argonaut Games BRP\nargo_cvg        Argonaut Games CVG\nasf             ASF (Advanced / Active Streaming Format)\nasf_o           ASF (Advanced / Active Streaming Format)\nass             SSA (SubStation Alpha) subtitle\nast             AST (Audio Stream)\nau              Sun AU\nav1             AV1 Annex B\navi             AVI (Audio Video Interleaved)\navr             AVR (Audio Visual Research)\navs             Argonaut Games Creature Shock\navs2            raw AVS2-P2/IEEE1857.4\navs3            raw AVS3-P2/IEEE1857.10\nbethsoftvid     Bethesda Softworks VID\nbfi             Brute Force \u0026 Ignorance\nbfstm           BFSTM (Binary Cafe Stream)\nbin             Binary text\nbink            Bink\nbinka           Bink Audio\nbit             G.729 BIT file format\nbitpacked       Bitpacked\nbmp_pipe        piped bmp sequence\nbmv             Discworld II BMV\nboa             Black Ops Audio\nbonk            raw Bonk\nbrender_pix     BRender PIX image\nbrstm           BRSTM (Binary Revolution Stream)\nc93             Interplay C93\ncaf             Apple CAF (Core Audio Format)\ncavsvideo       raw Chinese AVS (Audio Video Standard)\ncdg             CD Graphics\ncdxl            Commodore CDXL video\ncine            Phantom Cine\ncodec2          codec2 .c2 demuxer\ncodec2raw       raw codec2 demuxer\nconcat          Virtual concatenation script\ncri_pipe        piped cri sequence\ndash            Dynamic Adaptive Streaming over HTTP\ndata            raw data\ndaud            D-Cinema audio\ndcstr           Sega DC STR\ndds_pipe        piped dds sequence\nderf            Xilam DERF\ndfa             Chronomaster DFA\ndfpwm           raw DFPWM1a\ndhav            Video DAV\ndirac           raw Dirac\ndnxhd           raw DNxHD (SMPTE VC-3)\ndpx_pipe        piped dpx sequence\ndsf             DSD Stream File (DSF)\ndshow           DirectShow capture\ndsicin          Delphine Software International CIN\ndss             Digital Speech Standard (DSS)\ndts             raw DTS\ndtshd           raw DTS-HD\ndv              DV (Digital Video)\ndvbsub          raw dvbsub\ndvbtxt          dvbtxt\ndxa             DXA\nea              Electronic Arts Multimedia\nea_cdata        Electronic Arts cdata\neac3            raw E-AC-3\nepaf            Ensoniq Paris Audio File\nexr_pipe        piped exr sequence\nf32be           PCM 32-bit floating-point big-endian\nf32le           PCM 32-bit floating-point little-endian\nf64be           PCM 64-bit floating-point big-endian\nf64le           PCM 64-bit floating-point little-endian\nffmetadata      FFmpeg metadata in text\nfilm_cpk        Sega FILM / CPK\nfilmstrip       Adobe Filmstrip\nfits            Flexible Image Transport System\nflac            raw FLAC\nflic            FLI/FLC/FLX animation\nflv             FLV (Flash Video)\nfrm             Megalux Frame\nfsb             FMOD Sample Bank\nfwse            Capcom's MT Framework sound\ng722            raw G.722\ng723_1          G.723.1\ng726            raw big-endian G.726 (\"left aligned\")\ng726le          raw little-endian G.726 (\"right aligned\")\ng729            G.729 raw format demuxer\ngdigrab         GDI API Windows frame grabber\ngdv             Gremlin Digital Video\ngem_pipe        piped gem sequence\ngenh            GENeric Header\ngif             CompuServe Graphics Interchange Format (GIF)\ngif_pipe        piped gif sequence\ngsm             raw GSM\ngxf             GXF (General eXchange Format)\nh261            raw H.261\nh263            raw H.263\nh264            raw H.264 video\nhca             CRI HCA\nhcom            Macintosh HCOM\nhdr_pipe        piped hdr sequence\nhevc            raw HEVC video\nhls             Apple HTTP Live Streaming\nhnm             Cryo HNM v4\nico             Microsoft Windows ICO\nidcin           id Cinematic\nidf             iCE Draw File\niff             IFF (Interchange File Format)\nifv             IFV CCTV DVR\nilbc            iLBC storage\nimage2          image2 sequence\nimage2pipe      piped image2 sequence\nimf             IMF (Interoperable Master Format)\ningenient       raw Ingenient MJPEG\nipmovie         Interplay MVE\nipu             raw IPU Video\nircam           Berkeley/IRCAM/CARL Sound Format\niss             Funcom ISS\niv8             IndigoVision 8000 video\nivf             On2 IVF\nivr             IVR (Internet Video Recording)\nj2k_pipe        piped j2k sequence\njacosub         JACOsub subtitle format\njpeg_pipe       piped jpeg sequence\njpegls_pipe     piped jpegls sequence\njpegxl_pipe     piped jpegxl sequence\njv              Bitmap Brothers JV\nkux             KUX (YouKu)\nkvag            Simon \u0026 Schuster Interactive VAG\nlaf             LAF (Limitless Audio Format)\nlavfi           Libavfilter virtual input device\nlive_flv        live RTMP FLV (Flash Video)\nlmlm4           raw lmlm4\nloas            LOAS AudioSyncStream\nlrc             LRC lyrics\nluodat          Video CCTV DAT\nlvf             LVF\nlxf             VR native stream (LXF)\nm4v             raw MPEG-4 video\nmatroska,webm   Matroska / WebM\nmca             MCA Audio Format\nmcc             MacCaption\nmgsts           Metal Gear Solid: The Twin Snakes\nmicrodvd        MicroDVD subtitle format\nmjpeg           raw MJPEG video\nmjpeg_2000      raw MJPEG 2000 video\nmlp             raw MLP\nmlv             Magic Lantern Video (MLV)\nmm              American Laser Games MM\nmmf             Yamaha SMAF\nmods            MobiClip MODS\nmoflex          MobiClip MOFLEX\nmov,mp4,m4a,3gp,3g2,mj2 QuickTime / MOV\nmp3             MP2/3 (MPEG audio layer 2/3)\nmpc             Musepack\nmpc8            Musepack SV8\nmpeg            MPEG-PS (MPEG-2 Program Stream)\nmpegts          MPEG-TS (MPEG-2 Transport Stream)\nmpegtsraw       raw MPEG-TS (MPEG-2 Transport Stream)\nmpegvideo       raw MPEG video\nmpjpeg          MIME multipart JPEG\nmpl2            MPL2 subtitles\nmpsub           MPlayer subtitles\nmsf             Sony PS3 MSF\nmsnwctcp        MSN TCP Webcam stream\nmsp             Microsoft Paint (MSP))\nmtaf            Konami PS2 MTAF\nmtv             MTV\nmulaw           PCM mu-law\nmusx            Eurocom MUSX\nmv              Silicon Graphics Movie\nmvi             Motion Pixels MVI\nmxf             MXF (Material eXchange Format)\nmxg             MxPEG clip\nnc              NC camera feed\nnistsphere      NIST SPeech HEader REsources\nnsp             Computerized Speech Lab NSP\nnsv             Nullsoft Streaming Video\nnut             NUT\nnuv             NuppelVideo\nobu             AV1 low overhead OBU\nogg             Ogg\noma             Sony OpenMG audio\npaf             Amazing Studio Packed Animation File\npam_pipe        piped pam sequence\npbm_pipe        piped pbm sequence\npcx_pipe        piped pcx sequence\npfm_pipe        piped pfm sequence\npgm_pipe        piped pgm sequence\npgmyuv_pipe     piped pgmyuv sequence\npgx_pipe        piped pgx sequence\nphm_pipe        piped phm sequence\nphotocd_pipe    piped photocd sequence\npictor_pipe     piped pictor sequence\npjs             PJS (Phoenix Japanimation Society) subtitles\npmp             Playstation Portable PMP\npng_pipe        piped png sequence\npp_bnk          Pro Pinball Series Soundbank\nppm_pipe        piped ppm sequence\npsd_pipe        piped psd sequence\npsxstr          Sony Playstation STR\npva             TechnoTrend PVA\npvf             PVF (Portable Voice Format)\nqcp             QCP\nqdraw_pipe      piped qdraw sequence\nqoi_pipe        piped qoi sequence\nr3d             REDCODE R3D\nrawvideo        raw video\nrealtext        RealText subtitle format\nredspark        RedSpark\nrka             RKA (RK Audio)\nrl2             RL2\nrm              RealMedia\nroq             id RoQ\nrpl             RPL / ARMovie\nrsd             GameCube RSD\nrso             Lego Mindstorms RSO\nrtp             RTP input\nrtsp            RTSP input\ns16be           PCM signed 16-bit big-endian\ns16le           PCM signed 16-bit little-endian\ns24be           PCM signed 24-bit big-endian\ns24le           PCM signed 24-bit little-endian\ns32be           PCM signed 32-bit big-endian\ns32le           PCM signed 32-bit little-endian\ns337m           SMPTE 337M\ns8              PCM signed 8-bit\nsami            SAMI subtitle format\nsap             SAP input\nsbc             raw SBC (low-complexity subband codec)\nsbg             SBaGen binaural beats script\nscc             Scenarist Closed Captions\nscd             Square Enix SCD\nsdns            Xbox SDNS\nsdp             SDP\nsdr2            SDR2\nsds             MIDI Sample Dump Standard\nsdx             Sample Dump eXchange\nser             SER (Simple uncompressed video format for astronomical capturing)\nsga             Digital Pictures SGA\nsgi_pipe        piped sgi sequence\nshn             raw Shorten\nsiff            Beam Software SIFF\nsimbiosis_imx   Simbiosis Interactive IMX\nsln             Asterisk raw pcm\nsmjpeg          Loki SDL MJPEG\nsmk             Smacker\nsmush           LucasArts Smush\nsol             Sierra SOL\nsox             SoX native\nspdif           IEC 61937 (compressed data in S/PDIF)\nsrt             SubRip subtitle\nstl             Spruce subtitle format\nsubviewer       SubViewer subtitle format\nsubviewer1      SubViewer v1 subtitle format\nsunrast_pipe    piped sunrast sequence\nsup             raw HDMV Presentation Graphic Stream subtitles\nsvag            Konami PS2 SVAG\nsvg_pipe        piped svg sequence\nsvs             Square SVS\nswf             SWF (ShockWave Flash)\ntak             raw TAK\ntedcaptions     TED Talks captions\nthp             THP\ntiertexseq      Tiertex Limited SEQ\ntiff_pipe       piped tiff sequence\ntmv             8088flex TMV\ntruehd          raw TrueHD\ntta             TTA (True Audio)\ntty             Tele-typewriter\ntxd             Renderware TeXture Dictionary\nty              TiVo TY Stream\nu16be           PCM unsigned 16-bit big-endian\nu16le           PCM unsigned 16-bit little-endian\nu24be           PCM unsigned 24-bit big-endian\nu24le           PCM unsigned 24-bit little-endian\nu32be           PCM unsigned 32-bit big-endian\nu32le           PCM unsigned 32-bit little-endian\nu8              PCM unsigned 8-bit\nv210            Uncompressed 4:2:2 10-bit\nv210x           Uncompressed 4:2:2 10-bit\nvag             Sony PS2 VAG\nvbn_pipe        piped vbn sequence\nvc1             raw VC-1\nvc1test         VC-1 test bitstream\nvfwcap          VfW video capture\nvidc            PCM Archimedes VIDC\nvividas         Vividas VIV\nvivo            Vivo\nvmd             Sierra VMD\nvobsub          VobSub subtitle format\nvoc             Creative Voice\nvpk             Sony PS2 VPK\nvplayer         VPlayer subtitles\nvqf             Nippon Telegraph and Telephone Corporation (NTT) TwinVQ\nw64             Sony Wave64\nwady            Marble WADY\nwav             WAV / WAVE (Waveform Audio)\nwavarc          Waveform Archiver\nwc3movie        Wing Commander III movie\nwebm_dash_manifest WebM DASH Manifest\nwebp_pipe       piped webp sequence\nwebvtt          WebVTT subtitle\nwsaud           Westwood Studios audio\nwsd             Wideband Single-bit Data (WSD)\nwsvqa           Westwood Studios VQA\nwtv             Windows Television (WTV)\nwv              WavPack\nwve             Psion 3 audio\nxa              Maxis XA\nxbin            eXtended BINary text (XBIN)\nxbm_pipe        piped xbm sequence\nxmd             Konami XMD\nxmv             Microsoft XMV\nxpm_pipe        piped xpm sequence\nxvag            Sony PS3 XVAG\nxwd_pipe        piped xwd sequence\nxwma            Microsoft xWMA\nyop             Psygnosis YOP\nyuv4mpegpipe    YUV4MPEG pipe\n```\n\n\u003c/details\u003e\n\n**Notes:**\n\n- The list contains the supported formats (\u0026 not containers).\n  - A video/audio format may be present in a number of containers.\n  - e.g. an MP4 file generally contains H264 video stream.\n- On the web, format support depends upon the web browser.\n  - It happens to be extremely limited as compared to native platforms.\n\n## Permissions\n\nYou may need to declare \u0026 request internet access or file-system permissions depending upon platform.\n\n### Android\n\nEdit `android/app/src/main/AndroidManifest.xml` to add the following permissions inside `\u003cmanifest\u003e` tag:\n\n```xml\n\u003cmanifest xmlns:android=\"http://schemas.android.com/apk/res/android\" package=\"com.example.app\"\u003e\n    \u003capplication\n      ...\n      /\u003e\n    \u003c/application\u003e\n    \u003c!--\n      Internet access permissions.\n      --\u003e\n    \u003cuses-permission android:name=\"android.permission.INTERNET\" /\u003e\n    \u003c!--\n      Media access permissions.\n      Android 13 or higher.\n      https://developer.android.com/about/versions/13/behavior-changes-13#granular-media-permissions\n      --\u003e\n    \u003cuses-permission android:name=\"android.permission.READ_MEDIA_AUDIO\" /\u003e\n    \u003cuses-permission android:name=\"android.permission.READ_MEDIA_VIDEO\" /\u003e\n    \u003c!--\n      Storage access permissions.\n      Android 12 or lower.\n      --\u003e\n    \u003cuses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\" /\u003e\n    \u003cuses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" /\u003e\n\u003c/manifest\u003e\n```\n\nUse [`package:permission_handler`](https://pub.dev/packages/permission_handler) to request access at runtime:\n\n```dart\nif (/* Android 13 or higher. */) {\n  // Video permissions.\n  if (await Permission.videos.isDenied || await Permission.videos.isPermanentlyDenied) {\n    final state = await Permission.videos.request();\n    if (!state.isGranted) {\n      await SystemNavigator.pop();\n    }\n  }\n  // Audio permissions.\n  if (await Permission.audio.isDenied || await Permission.audio.isPermanentlyDenied) {\n    final state = await Permission.audio.request();\n    if (!state.isGranted) {\n      await SystemNavigator.pop();\n    }\n  }\n} else {\n  if (await Permission.storage.isDenied || await Permission.storage.isPermanentlyDenied) {\n    final state = await Permission.storage.request();\n    if (!state.isGranted) {\n      await SystemNavigator.pop();\n    }\n  }\n}\n```\n\n### iOS\n\nEdit `ios/Runner/Info-Release.plist`, `ios/Runner/Info-Profile.plist`, `ios/Runner/Info-Debug.plist`:\n\n**Enable internet access**\n\n```xml\n\u003ckey\u003eNSAppTransportSecurity\u003c/key\u003e\n\u003cdict\u003e\n    \u003ckey\u003eNSAllowsArbitraryLoads\u003c/key\u003e\n    \u003ctrue/\u003e\n\u003c/dict\u003e\n```\n\n### Windows\n\nN/A\n\n### macOS\n\nEdit `macos/Runner/Release.entitlements` \u0026 `macos/Runner/DebugProfile.entitlements`:\n\n**Enable internet access**\n\n```xml\n\u003ckey\u003ecom.apple.security.network.client\u003c/key\u003e\n\u003ctrue/\u003e\n```\n\n**Disable sand-box to access files**\n\n```xml\n\u003ckey\u003ecom.apple.security.app-sandbox\u003c/key\u003e\n\u003cfalse/\u003e\n```\n\n### GNU/Linux\n\nN/A\n\n### Web\n\nN/A\n\n## Notes\n\n### Android\n\nN/A\n\n### iOS\n\nN/A\n\n### Windows\n\nN/A\n\n### macOS\n\nDuring the build phase, the following warnings are not critical and cannot be silenced:\n\n```log\n#import \"Headers/media_kit_video-Swift.h\"\n        ^\n/path/to/media_kit/media_kit_test/build/macos/Build/Products/Debug/media_kit_video/media_kit_video.framework/Headers/media_kit_video-Swift.h:270:31: warning: 'objc_ownership' only applies to Objective-C object or block pointer types; type here is 'CVPixelBufferRef' (aka 'struct __CVBuffer *')\n- (CVPixelBufferRef _Nullable __unsafe_unretained)copyPixelBuffer SWIFT_WARN_UNUSED_RESULT;\n```\n\n```log\n# 1 \"\u003ccommand line\u003e\" 1\n ^\n\u003ccommand line\u003e:20:9: warning: 'POD_CONFIGURATION_DEBUG' macro redefined\n#define POD_CONFIGURATION_DEBUG 1 DEBUG=1\n        ^\n#define POD_CONFIGURATION_DEBUG 1\n        ^\n```\n\n### GNU/Linux\n\n#### Install libmpv\n\nSystem shared libraries from distribution specific user-installed packages are used by-default. **This is how GNU/Linux works.** You can install these as follows:\n\n##### Ubuntu/Debian\n\n```bash\nsudo apt install libmpv-dev mpv\n```\n\n##### Packaging\n\nThere are other ways to bundle these within your app package e.g. within Snap or Flatpak. Few examples:\n\n- [Celluloid](https://github.com/celluloid-player/celluloid/blob/master/flatpak/io.github.celluloid_player.Celluloid.json)\n- [VidCutter](https://github.com/ozmartian/vidcutter/tree/master/_packaging)\n\n#### Utilize [mimalloc](https://github.com/microsoft/mimalloc)\n\nYou should consider replacing the default memory allocator with [mimalloc](https://github.com/microsoft/mimalloc) for [avoiding memory leaks](https://github.com/media-kit/media-kit/issues/68).\n\nThis is as simple as [adding one line to `linux/CMakeLists.txt`](https://github.com/media-kit/media-kit/blob/d02a97ce70b316207db024401fb99e3f4509a250/media_kit_test/linux/CMakeLists.txt#L92-L94):\n\n```cmake\ntarget_link_libraries(${BINARY_NAME} PRIVATE ${MIMALLOC_LIB})\n```\n\nIn case you prefer dynamic linking of mimalloc, you can additionally add the following line to your `linux/CMakeLists.txt`:\n\n```cmake\n# use dynamically linked mimalloc\nset(MIMALLOC_USE_STATIC_LIBS OFF)\n```\n\nIn this case, please ensure you install `libmimalloc-dev` at compile time and `libmimalloc2.0` as runtime dependencies.\n\n#### Ubuntu/Debian\n\n```bash\nsudo apt install libmimalloc-dev libmimalloc2.0\n```\n\n### Web\n\nOn the web, **libmpv is not used**. Video \u0026 audio playback is handled by embedding [HTML `\u003cvideo\u003e` element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video). The format support depends upon the web browser. It happens to be extremely limited as compared to native platforms.\n\n## Architecture\n\n### package:media_kit\n\n_Click on the zoom button on top-right or pinch inside._\n\n```mermaid\n%%{\n  init: {\n    'themeVariables': {\n      'fontFamily': 'BlinkMacSystemFont, Segoe UI, Noto Sans, Helvetica, Arial, Apple Color Emoji, Segoe UI Emoji'\n    }\n  }\n}%%\nclassDiagram\n\n  Player *-- PlatformPlayer\n  PlatformPlayer \u003c|-- NativePlayer\n  PlatformPlayer \u003c|-- WebPlayer\n  PlatformPlayer *-- PlayerState\n  PlatformPlayer *-- PlayerStream\n  PlatformPlayer o-- PlayerConfiguration\n\n  NativePlayer \u003c.. NativeLibrary\n  NativePlayer \u003c.. Initializer\n\n  Playable \u003c.. Media\n  Playable \u003c.. Playlist\n\n  class Initializer {\n    +create(path: String, callback: Function, options: Map\u003cString, String\u003e): Future\u003cPointer\u003cmpv_handle\u003e\u003e\n    +dispose(handle: Pointer\u003cmpv_handle\u003e)\n  }\n\n  class Playable {\n  }\n\n  class AudioDevice {\n  }\n\n  class Media {\n    +String uri\n    +dynamic extras\n  }\n\n  class Playlist {\n    +List\u003cMedia\u003e medias\n    +index index\n  }\n\n  class PlayerStream {\n    +Stream\u003cPlaylist\u003e playlist\n    +Stream\u003cbool\u003e playing\n    +Stream\u003cbool\u003e completed\n    +Stream\u003cDuration\u003e position\n    +Stream\u003cDuration\u003e duration\n    +Stream\u003cDuration\u003e buffer\n    +Stream\u003cdouble\u003e volume\n    +Stream\u003cdouble\u003e rate\n    +Stream\u003cdouble\u003e pitch\n    +Stream\u003cbool\u003e buffering\n    +Stream\u003cDuration\u003e buffer\n    +Stream\u003cAudioParams\u003e audioParams\n    +Stream\u003cVideoParams\u003e videoParams\n    +Stream\u003cdouble?\u003e audioBitrate\n    +Stream\u003cAudioDevice\u003e audioDevice\n    +Stream\u003cList\u003cAudioDevice\u003e\u003e audioDevices\n    +Stream\u003cTrack\u003e track\n    +Stream\u003cTracks\u003e tracks\n    +Stream\u003cint\u003e width\n    +Stream\u003cint\u003e height\n    +Stream\u003cList\u003cString\u003e\u003e subtitle\n    +Stream\u003cPlayerLog\u003e log\n    +Stream\u003cString\u003e error\n  }\n\n  class PlayerState {\n    +Playlist playlist\n    +bool playing\n    +bool completed\n    +Duration position\n    +Duration duration\n    +Duration buffer\n    +double volume\n    +double rate\n    +double pitch\n    +bool buffering\n    +Duration buffer\n    +AudioParams audioParams\n    +VideoParams videoParams\n    +double? audioBitrate\n    +AudioDevice audioDevice\n    +List\u003cAudioDevice audioDevices\n    +Track track\n    +Tracks tracks\n    +int width\n    +int height\n    +List\u003cString\u003e subtitle\n  }\n\n  class Player {\n    +PlatformPlayer? platform\n\n    +«get» PlayerState state\n    +«get» PlayerStream stream\n\n    +dispose()\n    +open(playable: Playable)\n    +play()\n    +stop()\n    +pause()\n    +playOrPause()\n    +add(media: Media)\n    +remove(index: int)\n    +next()\n    +previous()\n    +jump(index: int)\n    +move(from: int, to: int)\n    +seek(duration: Duration)\n    +setPlaylistMode(playlistMode: PlaylistMode)\n    +setVolume(volume: double)\n    +setRate(rate: double)\n    +setPitch(pitch: double)\n    +setShuffle(bool: double)\n    +setAudioDevice(device: AudioDevice)\n    +setVideoTrack(track: VideoTrack)\n    +setAudioTrack(track: AudioTrack)\n    +setSubtitleTrack(track: SubtitleTrack)\n    +screenshot(): Uint8List\n  }\n\n  class PlatformPlayer {\n    +PlayerState state\n    +PlayerStream stream\n    +PlayerConfiguration configuration\n\n    +dispose()*\n    +open(playable: Playable)*\n    +play()*\n    +stop()*\n    +pause()*\n    +playOrPause()*\n    +add(media: Media)*\n    +remove(index: int)*\n    +next()*\n    +previous()*\n    +jump(index: int)*\n    +move(from: int, to: int)*\n    +seek(duration: Duration)*\n    +setPlaylistMode(playlistMode: PlaylistMode)*\n    +setVolume(volume: double)*\n    +setRate(rate: double)*\n    +setPitch(pitch: double)*\n    +setShuffle(bool: double)*\n    +setAudioDevice(device: AudioDevice)*\n    +setVideoTrack(track: VideoTrack)*\n    +setAudioTrack(track: AudioTrack)*\n    +setSubtitleTrack(track: SubtitleTrack)*\n    +screenshot(): Uint8List*\n\n    +«get» handle: Future\u003cint\u003e*\n\n    #StreamController\u003cPlaylist\u003e playlistController\n    #StreamController\u003cbool\u003e playingController\n    #StreamController\u003cbool\u003e completedController\n    #StreamController\u003cDuration\u003e positionController\n    #StreamController\u003cDuration\u003e durationController\n    #StreamController\u003cDuration\u003e bufferController\n    #StreamController\u003cdouble\u003e volumeController\n    #StreamController\u003cdouble\u003e rateController\n    #StreamController\u003cdouble\u003e pitchController\n    #StreamController\u003cbool\u003e bufferingController\n    #StreamController\u003cPlayerLog\u003e logController\n    #StreamController\u003cPlayerError\u003e errorController\n    #StreamController\u003cAudioParams\u003e audioParamsController\n    #StreamController\u003cdouble?\u003e audioBitrateController\n    #StreamController\u003cAudioDevice\u003e audioDeviceController\n    #StreamController\u003cList\u003cAudioDevice\u003e\u003e audioDevicesController\n    #StreamController\u003cTrack\u003e trackController\n    #StreamController\u003cTracks\u003e tracksController\n    #StreamController\u003cint\u003e widthController\n    #StreamController\u003cint\u003e heightController\n  }\n\n  class NativePlayer {\n    +dispose()\n    +open(playable: Playable)\n    +play()\n    +stop()\n    +pause()\n    +playOrPause()\n    +add(media: Media)\n    +remove(index: int)\n    +next()\n    +previous()\n    +jump(index: int)\n    +move(from: int, to: int)\n    +seek(duration: Duration)\n    +setPlaylistMode(playlistMode: PlaylistMode)\n    +setVolume(volume: double)\n    +setRate(rate: double)\n    +setPitch(pitch: double)\n    +setShuffle(bool: double)\n    +setAudioDevice(device: AudioDevice)\n    +setVideoTrack(track: VideoTrack)\n    +setAudioTrack(track: AudioTrack)\n    +setSubtitleTrack(track: SubtitleTrack)\n    +screenshot(): Uint8List\n\n    +«get» handle: Future\u003cint\u003e\n  }\n\n  class WebPlayer {\n    +dispose()\n    +open(playable: Playable)\n    +play()\n    +stop()\n    +pause()\n    +playOrPause()\n    +add(media: Media)\n    +remove(index: int)\n    +next()\n    +previous()\n    +jump(index: int)\n    +move(from: int, to: int)\n    +seek(duration: Duration)\n    +setPlaylistMode(playlistMode: PlaylistMode)\n    +setVolume(volume: double)\n    +setRate(rate: double)\n    +setPitch(pitch: double)\n    +setShuffle(bool: double)\n    +setAudioDevice(device: AudioDevice)\n    +setVideoTrack(track: VideoTrack)\n    +setAudioTrack(track: AudioTrack)\n    +setSubtitleTrack(track: SubtitleTrack)\n    +screenshot(): Uint8List\n\n    +«get» handle: Future\u003cint\u003e\n  }\n\n  class NativeLibrary {\n    +find()$ String?\n  }\n```\n\n### package:media_kit_video\n\n_Click on the zoom button on top-right or pinch inside._\n\n#### Android\n\n```mermaid\n%%{\n  init: {\n    'themeVariables': {\n      'fontFamily': 'BlinkMacSystemFont, Segoe UI, Noto Sans, Helvetica, Arial, Apple Color Emoji, Segoe UI Emoji'\n    }\n  }\n}%%\nclassDiagram\n\n  MediaKitVideoPlugin \"1\" *-- \"1\" VideoOutputManager: Create VideoOutput(s) with VideoOutputManager for handle passed through platform channel\n  VideoOutputManager \"1\" *-- \"*\" VideoOutput: Create VideoOutput(s) to send back id \u0026 wid for render. Dispose to release.\n  VideoOutput \u003c.. MediaKitAndroidHelper: Create \u0026 dispose JNI global object reference to android.view.Surface (for --wid)\n\n  class MediaKitVideoPlugin {\n    -MethodChannel channel\n    -VideoOutputManager videoOutputManager\n  }\n\n  class VideoOutputManager {\n    -HashMap\u003cLong, VideoOutput\u003e videoOutputs\n    -TextureRegistry textureRegistryReference\n    -Object lock\n\n    +create(handle: long, textureUpdateCallback: TextureUpdateCallback)\n    +dispose(handle: long)\n    +setSurfaceSize(handle: long, width: int, height: int): long\n  }\n\n  class VideoOutput {\n    $Method newGlobalObjectRef\n    $Method deleteGlobalObjectRef\n    $Handler handler\n\n    -long id\n    -long wid\n\n    -TextureUpdateCallback textureUpdateCallback\n    -TextureRegistry.SurfaceProducer surfaceProducer\n\n    -long handle\n    -MethodChannel channelReference\n    -TextureRegistry textureRegistryReference\n\n    +dispose()\n    +setSurfaceSize(width: int, height: int)\n    +setSurfaceSize(width: int, height: int, force: boolean)\n    -setSurfaceTextureSize(width: int, height: int)\n    +onSurfaceCreated()\n    +onSurfaceDestroyed()\n\n    $newGlobalObjectRef(object: Object): long\n    $deleteGlobalObjectRef(ref: long)\n  }\n\n  class MediaKitAndroidHelper {\n    +newGlobalObjectRef(obj: Object): long\n    +deleteGlobalObjectRef(ref: long)\n    +setApplicationContext(context: Context)\n    +copyAssetToExternalFilesDir(assetName: String): String\n  }\n\n```\n\n#### iOS\n\n_TODO: documentation._\n\n#### macOS\n\n_TODO: documentation._\n\n#### Windows\n\n```mermaid\n%%{\n  init: {\n    'themeVariables': {\n      'fontFamily': 'BlinkMacSystemFont, Segoe UI, Noto Sans, Helvetica, Arial, Apple Color Emoji, Segoe UI Emoji'\n    }\n  }\n}%%\nclassDiagram\n\n  MediaKitVideoPlugin \"1\" *-- \"1\" VideoOutputManager: Create VideoOutput(s) with VideoOutputManager for handle passed through platform channel\n  VideoOutputManager \"1\" *-- \"*\" VideoOutput: Takes PluginRegistrarWindows as reference\n  VideoOutputManager \"1\" *-- \"1\" ThreadPool\n  VideoOutput \"*\" o-- \"1\" ThreadPool: Post creation, resize \u0026 render etc. tasks involving EGL to ensure synchronous EGL/ANGLE usage across multiple VideoOutput(s)\n  VideoOutput \"1\" *-- \"1\" ANGLESurfaceManager: Only for H/W accelerated rendering\n\n  class MediaKitVideoPlugin {\n    -flutter::PluginRegistrarWindows registrar_\n    -std::unique_ptr\u003cMethodChannel\u003e channel_\n    -std::unique_ptr\u003cVideoOutputManager\u003e video_output_manager_\n    -HandleMethodCall(method_call, result);\n  }\n\n  class ThreadPool {\n    +Post(function: std::function)\n  }\n\n  class VideoOutputManager {\n    +Create(handle: int, width: optional\u003cint\u003e, height: optional\u003cint\u003e, texture_update_callback: std::function)\n    +Dispose(handle: int)\n\n    -std::mutex mutex_\n    -std::unique_ptr\u003cThreadPool\u003e thread_pool_\n    -flutter::PluginRegistrarWindows registrar_\n    -std::unordered_map\u003cint64_t, std::unique_ptr\u003cVideoOutput\u003e\u003e video_outputs_\n  }\n\n  class VideoOutput {\n    +«get» texture_id: int64_t\n    +«get» width: int64_t\n    +«get» height: int64_t\n    -mpv_handle* handle_\n    -mpv_render_context* render_context_\n    -std::optional\u003cint64_t\u003e width_\n    -std::optional\u003cint64_t\u003e height_\n    -bool enable_hardware_acceleration_\n    -int64_t texture_id_\n    -flutter::PluginRegistrarWindows registrar_\n    -ThreadPool* thread_pool_ref_\n    -bool destroyed_\n    -std::mutex textures_mutex_\n    -std::unordered_map\u003cint64_t, std::unique_ptr\u003cflutter::TextureVariant\u003e\u003e texture_variants_\n    -std::unique_ptr\u003cANGLESurfaceManager\u003e surface_manager_ HW\n    -std::unordered_map\u003cint64_t, std::unique_ptr\u003cFlutterDesktopGpuSurfaceDescriptor\u003e\u003e textures_ HW\n    -std::unique_ptr\u003cuint8_t[]\u003e pixel_buffer_ SW\n    -std::unordered_map\u003cint64_t, std::unique_ptr\u003cFlutterDesktopPixelBuffer\u003e\u003e pixel_buffer_textures_ SW\n    -std::function texture_update_callback_\n\n    +SetTextureUpdateCallback(callback: std::function\u003cvoid(int64_t, int64_t, int64_t)\u003e)\n    +SetSize(width: std::optional\u003cint64_t\u003e, height: std::optional\u003cint64_t\u003e)\n    -NotifyRender()\n    -Render()\n    -CheckAndResize()\n    -Resize(required_width: int64_t, required_height: int64_t)\n    -GetVideoWidth(): int64_t\n    -GetVideoHeight(): int64_t\n  }\n\n  class ANGLESurfaceManager {\n    +«get» width: int32_t\n    +«get» height: int32_t\n    +«get» handle: HANDLE\n\n    +HandleResize(width: int32_t, height: int32_t)\n    +Draw(draw_callback: std::function\u003cvoid()\u003e)\n    +Read()\n    +MakeCurrent(value: bool)\n    -CreateEGLDisplay()\n    -SwapBuffers()\n    -Create()\n    -CleanUp(release_context: bool)\n    -CreateD3DTexture()\n    -CreateEGLDisplay()\n    -CreateAndBindEGLSurface()\n\n    -IDXGIAdapter* adapter_\n    -int32_t width_\n    -int32_t height_\n    -HANDLE internal_handle_\n    -HANDLE handle_\n    -HANDLE mutex_\n    -ID3D11Device* d3d_11_device_\n    -ID3D11DeviceContext* d3d_11_device_context_\n    -Microsoft::WRL::ComPtr\u003cID3D11Texture2D\u003e internal_d3d_11_texture_2D_\n    -Microsoft::WRL::ComPtr\u003cIDXGISwapChain\u003e d3d_11_texture_2D_\n    -EGLSurface surface_\n    -EGLDisplay display_\n    -EGLContext context_\n    -EGLConfig config_\n  }\n```\n\n#### GNU/Linux\n\n```mermaid\n%%{\n  init: {\n    'themeVariables': {\n      'fontFamily': 'BlinkMacSystemFont, Segoe UI, Noto Sans, Helvetica, Arial, Apple Color Emoji, Segoe UI Emoji'\n    }\n  }\n}%%\nclassDiagram\n\n  MediaKitVideoPlugin \"1\" *-- \"1\" VideoOutputManager: Create VideoOutput(s) with VideoOutputManager for handle passed through platform channel\n  VideoOutputManager \"1\" *-- \"*\" VideoOutput: Takes FlTextureRegistrar as reference\n  VideoOutput \"1\" *-- \"1\" TextureGL: For H/W rendering.\n  TextureGL \"1\" o-- \"1\" VideoOutput: Take VideoOutput as reference\n  VideoOutput \"1\" *-- \"1\" TextureSW: For S/W rendering.\n  TextureSW \"1\" o-- \"1\" VideoOutput: Take VideoOutput as reference\n  TextureGL \"1\" \u003c-- \"1\" FlTextureGL\n  TextureSW \"1\" \u003c-- \"1\" FlTexture\n\n  class MediaKitVideoPlugin {\n    -FlMethodChannel* channel\n    -VideoOutputManager* video_output_manager\n  }\n\n  class VideoOutputManager {\n    -GHashTable* video_outputs\n    -FlTextureRegistrar* texture_registrar\n    +video_output_manager_create(self: VideoOutputManager*, handle: gint64, width: gint64, height: gint64, texture_update_callback: TextureUpdateCallback, texture_update_callback_context: gpointer)\n    +video_output_manager_dispose(self: VideoOutputManager*, handle: gint64)\n  }\n\n  class VideoOutput {\n    -TextureGL* texture_gl\n    -GdkGLContext* context_gl\n    -mpv_handle* handle\n    -mpv_render_context* render_context\n    -gint64 width\n    -gint64 height\n    -TextureUpdateCallback texture_update_callback\n    -gpointer texture_update_callback_context\n    -FlTextureRegistrar* texture_registrar\n    +video_output_set_texture_update_callback(self: VideoOutput*, texture_update_callback: TextureUpdateCallback, texture_update_callback_context: gpointer)\n    +video_output_get_render_context(self: VideoOutput*): mpv_render_context*\n    +video_output_get_width(self: VideoOutput*): gint64\n    +video_output_get_height(self: VideoOutput*): gint64\n    +video_output_get_texture_id(self: VideoOutput*): gint64\n    +video_output_notify_texture_update(self: VideoOutput*);\n  }\n\n  class TextureGL {\n    -guint32 name\n    -guint32 fbo\n    -guint32 current_width\n    -guint32 current_height\n    -VideoOutput* video_output\n    texture_gl_populate_texture(texture: FlTextureGL*, target: guint32*, name: guint32*, width: guint32*, height: guint32*, error: GError**): gboolean\n  }\n\n  class TextureSW {\n    -guint32 current_width\n    -guint32 current_height\n    -VideoOutput* video_output\n    texture_sw_copy_pixels(texture: FlPixelBufferTexture*, buffer: const uint8_t**, width: uint32_t*, height: uint32_t*, error: GError**): gboolean\n  }\n```\n\n#### Web\n\n_TODO: documentation._\n\n## Implementation\n\n[libmpv](https://github.com/mpv-player/mpv/tree/master/libmpv) is used for leveraging audio \u0026 video playback. It _seems_ the best possible option since supports a wide variety of audio \u0026 video formats, provides hardware acceleration \u0026 bundle size is also minimal (select only required decoders etc. in FFmpeg/mpv).\n\nAnother major advantage is that large part of implementation (80%+) is shared across platforms using FFI. This makes the behavior of package very-very similar on all supported platforms \u0026 makes maintenance easier (since there is less code \u0026 most of it within Dart).\n\nAlternative backends may be implemented in future to meet certain demands (\u0026 project architecture makes it possible).\n\n### package:media_kit\n\n[package:media_kit](https://github.com/media-kit/media-kit) is entirely written in Dart. It uses dart:ffi to invoke native C API of libmpv through it's shared libraries. All the callback management, event-`Stream`s, other methods to control playback of audio/video are implemented in Dart with the help of FFI. Event management i.e. `position`, `duration`, `bitrate`, `audioParams` `Stream`s are important to render changes in the UI.\n\n~~A [big limitation with FFI in Dart SDK](https://github.com/dart-lang/sdk/issues/37022) has been that it does not support async callbacks from another thread. Learn more about this at: [dart/sdk#37022](https://github.com/dart-lang/sdk/issues/37022). Following situation will explain better:~~\n\n\u003e ~~If you pass a function pointer from Dart to C code, you can invoke it fine. But, as soon as you invoke it from some other thread on the native side, Dart VM will instantly crash. This feature is important because most events take place on a background thread.~~\n\n~~However, I could easily do this within Dart because [libmpv](https://github.com/mpv-player/mpv/tree/master/libmpv) offers an \"event polling\"-like way to listen to events. I got awesome idea to spawn a background [`Isolate`](https://api.flutter.dev/flutter/dart-isolate/Isolate-class.html), where I run the event-loop. I get the memory address of each event and forward it outside the [`Isolate`](https://api.flutter.dev/flutter/dart-isolate/Isolate-class.html) with the help of [`ReceivePort`](https://api.dart.dev/stable/2.18.6/dart-isolate/ReceivePort-class.html), where I finally interpret it using more FFI code. I have explained this in detail within [the in-code comments of initializer.dart, where I had to perform a lot more trickery to get this to work](https://github.com/media-kit/media-kit/blob/master/media_kit/lib/src/libmpv/core/initializer.dart).~~\n\n~~**Thus, invoking native methods \u0026 handling of events etc. could be done within 100% Dart using FFI.** This is enough for audio playback \u0026 supports both Flutter SDK \u0026 Dart VM. Although event handling works entirely within Dart. Later, it was discovered that going beyond certain number of simultaneous instances caused a deadlock ([dart-lang/sdk#51254](https://github.com/dart-lang/sdk/issues/51254) \u0026 [dart-lang/sdk#51261](https://github.com/dart-lang/sdk/issues/51261)), making UI entirely freezed along-side any other Dart code in execution. To deal with this, a new package [package:media_kit_native_event_loop](#packagemedia_kit_native_event_loop) is created. Adding [package:media_kit_native_event_loop](#packagemedia_kit_native_event_loop) to `pubspec.yaml` automatically resolves this issue without any chagnes to code!~~\n\n**Update:** The above issue is resolved in Dart SDK 3.1.0. [`NativeCallable`](https://api.flutter.dev/flutter/dart-ffi/NativeCallable-class.html) can now be used to make async C callbacks.\n\nHowever, no such \"event-polling\" like API is possible for video rendering. So, I best idea seemed to create a new package [`package:media_kit_video`](https://github.com/media-kit/media-kit) for specifically offering platform-specific video embedding implementation which internally handles Flutter's Texture Registry API \u0026 libmpv's OpenGL rendering API. This package only consumes the `mpv_handle*` (which can be shared as primitive `int` value easily) of the instance (created with [package:media_kit](https://github.com/media-kit/media-kit) through FFI) to setup a new viewport. Detailed implementation is discussed below.\n\n### package:media_kit_native_event_loop\n\n\u003e Platform specific threaded event handling for media_kit. Enables support for higher number of concurrent instances.\n\nThe package contains a minimal C++ implementation which spawns a detach-ed [`std::thread`](https://en.cppreference.com/w/cpp/thread/thread). This runs the `mpv_wait_event` loop \u0026 forwads the events using [`postCObject`](https://api.dart.dev/stable/2.19.6/dart-ffi/NativeApi/postCObject.html), [`SendPort`](https://api.dart.dev/stable/2.19.6/dart-isolate/SendPort-class.html) \u0026 [`ReceivePort`](https://api.dart.dev/stable/2.19.6/dart-isolate/ReceivePort-class.html) to Dart VM. Necessary mutex synchronization also takes place.\n\n[`Isolate`](https://api.flutter.dev/flutter/dart-isolate/Isolate-class.html) based event loop is avoided once this package is added to the project.\n\n### package:media_kit_video\n\n#### Android\n\nOn Android, [texture registry API](https://api.flutter.dev/javadoc/io/flutter/view/TextureRegistry.html) is based on [`android.graphics.SurfaceTexture`](https://developer.android.com/reference/android/graphics/SurfaceTexture.html?is-external=true).\n\n[libmpv](https://github.com/mpv-player/mpv/tree/master/libmpv) can render directly onto an [`android.view.Surface`](https://developer.android.com/reference/android/view/Surface) after setting [`--wid`](https://mpv.io/manual/stable/#options-wid). Creation of a new [`android.view.Surface`](https://developer.android.com/reference/android/view/Surface) requires reference to an existing [`android.graphics.SurfaceTexture`](https://developer.android.com/reference/android/graphics/SurfaceTexture.html?is-external=true), [which can be consumed from the texture entry created by Flutter itself](\u003chttps://api.flutter.dev/javadoc/io/flutter/view/TextureRegistry.SurfaceTextureEntry.html#surfaceTexture()\u003e).\n\nThis requires `--hwdec=mediacodec` for hardware decoding, along with `--vo=mediacodec_embed` and `--wid=(intptr_t)(*android.view.Surface)`.\n\nMore details may be found at: https://mpv.io/manual/stable/#video-output-drivers-mediacodec-embed\n\nObtaining a global reference pointer to a Java object ([`android.view.Surface`](https://developer.android.com/reference/android/view/Surface) in our case) requires JNI. For this, a custom shared library is used, you can find it's implementation at [media-kit/media-kit-android-helper](https://github.com/media-kit/media-kit-android-helper). Since compilation of this would require NDK (\u0026 make process tedious), pre-built shared libraries is bundled for each architecture at the time of development/build.\n\nSince the `package:media_kit` is a Dart package (which works independent of Flutter), accessing assets was a challenging part. The mentioned shared libraries generated by [media-kit/media-kit-android-helper](https://github.com/media-kit/media-kit-android-helper) helps to [access assets bundled inside Android APK from Dart](https://github.com/alexmercerind/MediaKitAndroidHelper/blob/220cf95958aceb7e3678ba524da812f212524537/app/src/main/cpp/native-lib.cpp#L26-L115) (using FFI, without depending on Flutter).\n\n#### iOS\n\niOS shares much of it's implementation with macOS. Only difference is that OpenGL ES is used instead of OpenGL.\n\n#### macOS\n\nOn macOS the current implementation is based on [libmpv](https://github.com/mpv-player/mpv/tree/master/libmpv) and can be summarized as follows:\n\n1. H/W video decoding: mpv option `hwdec` is set to `auto`, does not depend on a pixel buffer.\n2. OpenGL rendering to an OpenGL texture backed by a pixel buffer, which makes it interoperable with METAL ([CVPixelBuffer](https://developer.apple.com/documentation/corevideo/cvpixelbuffer-q2e))\n\n\u003c!--\n\nPossible improvements :\n- Render directly to METAL texture:\n  - Use ANGLE to not depend on the host OpenGL implementation, deprecated by Apple.\n  - Use a future METAL API natively developed by mpv.\n- Share the METAL texture between `media_kit_video` and Flutter, without using a pixel buffer.\n\n--\u003e\n\n#### Windows\n\n- [libmpv](https://github.com/mpv-player/mpv/tree/master/libmpv) gives access to C API for rendering hardware-accelerated video output using OpenGL.\n  - See:\n    - [render.h](https://github.com/mpv-player/mpv/blob/master/libmpv/render.h)\n    - [render_gl.h](https://github.com/mpv-player/mpv/blob/master/libmpv/render_gl.h)\n- Flutter recently added ability for Windows to [render Direct3D `ID3D11Texture2D` textures](https://github.com/flutter/engine/pull/26840).\n\nThe two APIs above are hardware accelerated i.e. GPU backed buffers are used. **This is performant approach, easily capable for rendering 4K 60 FPS videos**, rest depends on the hardware. Since [libmpv](https://github.com/mpv-player/mpv/tree/master/libmpv) API is OpenGL based \u0026 the Texture API in Flutter is Direct3D based, [ANGLE (Almost Native Graphics Layer Engine)](https://github.com/google/angle) is used for interop, which translates the OpenGL ES 2.0 calls into Direct3D.\n\nThis hardware-accelerated video output requires DirectX 11 or higher. Most Windows systems with either integrated or discrete GPUs should support this already. On systems where Direct3D fails to load due to missing graphics drivers or unsupported feature-level or DirectX version etc. a fallback pixel-buffer based software renderer is used. This means that video is rendered by CPU \u0026 every frame is copied back to the RAM. This will cause some redundant load on the CPU, result in decreased battery life \u0026 may not play higher resolution videos properly. However, it works well.\n\n\u003cdetails\u003e\n\n\u003csummary\u003e Windows 7 \u0026 8.x also work correctly. \u003c/summary\u003e\n\n![0](https://user-images.githubusercontent.com/28951144/212947036-4a2430d6-729e-47d7-a356-c8cc8534a1aa.jpg)\n![1](https://user-images.githubusercontent.com/28951144/212947046-cc8d441c-96f8-4437-9f59-b4613ca73f2a.jpg)\n\n\u003c/details\u003e\n\nYou may visit [experimentation repository](https://github.com/alexmercerind/flutter-windows-OpenGLES) to see a minimal example showing OpenGL ES usage in Flutter Windows.\n\n#### GNU/Linux\n\nOn Flutter Linux, [both OpenGL (H/W) \u0026 pixel buffer (S/W) APIs](https://github.com/flutter/engine/pull/24916) are available for rendering on [`Texture` widget](https://api.flutter.dev/flutter/widgets/Texture-class.html).\n\n#### Web\n\nVideo \u0026 audio playback is handled by embedding [HTML `\u003cvideo\u003e` element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video).\n\n## License\n\nCopyright © 2021 \u0026 onwards, Hitesh Kumar Saini \u003c\u003csaini123hitesh@gmail.com\u003e\u003e\n\nThis project \u0026 the work under this repository is governed by MIT license that can be found in the [LICENSE](./LICENSE) file.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmedia-kit%2Fmedia-kit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmedia-kit%2Fmedia-kit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmedia-kit%2Fmedia-kit/lists"}