{"id":13610319,"url":"https://github.com/jordond/transformerKt","last_synced_at":"2025-04-12T22:33:26.598Z","repository":{"id":168342155,"uuid":"644045485","full_name":"jordond/transformerKt","owner":"jordond","description":"🔧 A Kotlin coroutine wrapper around Media3's Transformer API.","archived":false,"fork":false,"pushed_at":"2024-10-31T18:17:06.000Z","size":1058,"stargazers_count":22,"open_issues_count":13,"forks_count":2,"subscribers_count":4,"default_branch":"main","last_synced_at":"2024-10-31T19:21:41.913Z","etag":null,"topics":["android","coroutines","coroutines-android","jetpack","kotlin","kotlin-coroutines","media3","mediacodec","transcode","transformer"],"latest_commit_sha":null,"homepage":"http://docs.transformerkt.dev/","language":"Kotlin","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/jordond.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"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}},"created_at":"2023-05-22T17:39:09.000Z","updated_at":"2024-10-31T11:10:49.000Z","dependencies_parsed_at":"2023-10-13T10:00:55.930Z","dependency_job_id":"2827f636-4235-4170-816a-4e52b3d90501","html_url":"https://github.com/jordond/transformerKt","commit_stats":null,"previous_names":["jordond/transformerkt"],"tags_count":26,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jordond%2FtransformerKt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jordond%2FtransformerKt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jordond%2FtransformerKt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jordond%2FtransformerKt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jordond","download_url":"https://codeload.github.com/jordond/transformerKt/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223549180,"owners_count":17163607,"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","coroutines","coroutines-android","jetpack","kotlin","kotlin-coroutines","media3","mediacodec","transcode","transformer"],"created_at":"2024-08-01T19:01:43.573Z","updated_at":"2025-04-12T22:33:26.585Z","avatar_url":"https://github.com/jordond.png","language":"Kotlin","funding_links":[],"categories":["Kotlin"],"sub_categories":[],"readme":"## Project status\n\nThis project is no longer maintained, please see [#261](https://github.com/jordond/transformerKt/issues/261).\n\n\u003cp align=\"center\"\u003e \n   \u003cimg height=\"200\" src=\"art/logo-text.png\"/\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n    \u003cimg src=\"https://img.shields.io/maven-central/v/dev.jordond.transformerkt/transformerkt\"\u003e\n    \u003ca href=\"https://github.com/jordond/transformerKt/actions/workflows/ci.yml\"\u003e\u003cimg src=\"https://github.com/jordond/transformerKt/actions/workflows/ci.yml/badge.svg\"\u003e\u003c/img\u003e\u003c/a\u003e\n    \u003ca href=\"https://developer.android.com/jetpack/androidx/releases/media3#1.6.0-alpha03-alpha01\"\u003e\u003cimg src=\"https://img.shields.io/badge/media3-1.6.0-alpha03-alpha01?color=brightgreen\" /\u003e\u003c/a\u003e\n    \u003cimg src=\"https://img.shields.io/github/license/jordond/transformerkt\" /\u003e   \n\u003c/p\u003e\n\nTransformerKt is a Kotlin coroutine wrapper library\naround [media3.transformer](https://developer.android.com/guide/topics/media/transformer):\n\n\u003e Transformer is an API for editing media, including converting between formats (transcoding),\n\u003e applying changes like trimming a clip from a longer video, cropping a portion of the video frame,\n\u003e applying custom effects, and other editing operations\n\nYou can view the TransformerKt KDocs at [docs.transformerkt.dev](https://docs.transformerkt.dev)\n\n- Using `media3.transformer` [version `1.6.0-alpha03-alpha01`](https://github.com/androidx/media/releases)\n\n## Table of Contents\n\n- [Motivation](#motivation)\n- [Getting Started](#getting-started)\n- [Usage](#usage)\n- [Applying Effects](#applying-effects)\n- [Composition](#composition)\n- [Demo App](#demo-app)\n- [License](#license)\n\n## Motivation\n\nThe `media3.transformer` API is Java based and therefore relies on callbacks to notify the caller of\nthe result of an operation. This library wraps the API in a Kotlin coroutine based API to make it\neasier to use. It exposes the `Transformer` API as either a `suspend` function or a `Flow`.\n\nThis library also includes some helpful extension functions to make it easier to use the API.\nSee [Usage](#usage) for more information.\n\n**Note:** Due to the way `Transformer` works, the coroutines must be launched on\nthe `Dispatchers.Main` thread, otherwise the API will throw an `IllegalStateException`. Since it\nrelies on the current thread to contain a `Looper`. While it is launched on the\nmain-thread, `Transformer` delegates all the heavy lifting off of the main thread.\nSee [the docs](https://developer.android.com/guide/topics/media/transformer/getting-started#threading)\nfor more information.\n\n## Getting Started\n\nAdd the dependency to your app level `build.gradle.kts` file:\n\n```kotlin\ndependencies {\n    implementation(\"dev.jordond.transformerkt:transformerkt:3.7.0-alpha01\")\n}\n```\n\nOr using Version Catalogs:\n\n```toml\n[versions]\ntransformerkt = \"3.7.0-alpha01\"\n\n[libraries]\ntransformerkt = { group = \"dev.jordond.transformerkt\", name = \"transformerkt\", version.ref = \"transformerkt\" }\n\n```\n\n## Usage\n\nFirst you should familiarize yourself with\nthe [Transformer Docs](https://developer.android.com/guide/topics/media/transformer).\n\n### Inputs\n\nThen you need an input video or image file. `TransformerKt` supports the following inputs:\n\n- [MediaItem](https://developer.android.com/reference/androidx/media3/common/MediaItem).\n- [EditedMediaItem](https://github.com/androidx/media/blob/0fce8f416b54124605c1ed8aa72af98c94602834/libraries/transformer/src/main/java/androidx/media3/transformer/EditedMediaItem.java).\n- A `Uri` pointing to somewhere on the device.\n- A `File` object pointing to a file in the _app's_ sand-boxed storage.\n    - Warning: Getting a `File` object to a file outside of the app's storage will probably cause a\n      permission error.\n\nNow that you have your input sorted, there are two ways to consume this library.\n\n### Extension functions\n\nA few extension functions have been added to the `Transformer` instance.\n\n- `suspend fun Transformer.start(): TransformerStatus.Finished`\n- `fun Transformer.start(): Flow\u003cTransformerStatus\u003e`\n\nThere are overloads for each of the supported inputs. For example:\n\n```kotlin\nsuspend fun transform(context: Context, input: Uri) {\n    val output = File(context.filesDir, \"output.mp4\")\n    val transformer = TransformerKt.build(context) {\n        setVideoMimeType(MimeTypes.VIDEO_H264)\n    }\n    val result = transformer.start(input, output) { progress -\u003e\n        // Update UI progress\n    }\n    when (result) {\n        is TransformerStatus.Failure -\u003e TODO()\n        is TransformerStatus.Success -\u003e TODO()\n    }\n}\n```\n\nOr you can use the `Flow` version instead:\n\n```kotlin\nfun transform(context: Context, input: Uri) {\n    val output = File(context.filesDir, \"output.mp4\")\n    val transformer = Transformer.build(context) { setVideoMimeType(MimeTypes.VIDEO_H264) }\n    transformer.start(input, output).collect { status -\u003e\n        when (status) {\n            is TransformerStatus.Progress -\u003e TODO()\n            is TransformerStatus.Success -\u003e TODO()\n            is TransformerStatus.Failure -\u003e TODO()\n        }\n    }\n}\n```\n\n## Applying Effects\n\nStarting with Media3 `1.1.0-alpha01` the `Transformer` library changed the way you apply effects.\nInstead of applying the effects to the `Transformer.Builder` you now create a `EditedMediaItem` and\napply the affects there.\n\nTo make that API a bit easier, an extension function `.edited {}` has been added\nto `MediaItem.Builder`:\n\n```kotlin\nval editedMediaItem = MediaItem.Builder()\n    .setUri(Uri.parse(\"https://example.com/video.mp4\"))\n    .setMediaId(\"Foo\")\n    .edited {\n        setRemoveAudio(true)\n    }\n\nval result = TransformerKt.build(context).start(editedMediaItem, File(\"output.mp4\"))\n```\n\nOr directly from a [MediaItem] instance:\n\n```kotlin\nval editedMediaItem = MediaItem\n    .fromUri(Uri.parse(\"https://example.com/video.mp4\"))\n    .edited {\n        setRemoveAudio(true)\n    }\n\nval result = TransformerKt.build(context).start(editedMediaItem, File(\"output.mp4\"))\n```\n\n## Composition\n\nTransformer now supports `Composition` which allows you to combine multiple inputs into a single\noutput. You can apply effects to the whole composition or on a per input basis:\n\n```kotlin\ndata class MyComplexItem(val tag: String, val uri: Uri, val startMs: Long, val endMs: Long)\n\nval items: List\u003cUri\u003e\nval complexItems: List\u003cMyComplexItem\u003e\nval endCredits: File\nval audioOverlay: File\nval composition = compositionOf {\n    // Apply effects to the whole composition\n    effects {\n        resolution(1920, 1080, LayoutScale.Fit)\n    }\n\n    // Create a sequence of inputs\n    sequenceOf {\n        items(items) { uri -\u003e\n            effects {\n                bitmapOverlay(context, R.drawable.watermark) {\n                    setScale(.2f, .2f)\n                    setOverlayFrameAnchor(.8f, .8f)\n                }\n            }\n        }\n\n        items(\n            items = complexItems,\n            selector = { it.uri },\n            configure = { complexItem -\u003e\n                // Configure the MediaItem instance\n                setTag(complexItem.tag)\n                setClippingConfiguration(complexItem.startMs, complexItem.endMs)\n            },\n        ) { complexItem -\u003e\n            setRemoveAudio(true)\n\n            effects {\n                speed(2f)\n                brightness(0.5f)\n            }\n        }\n\n        item(endCredits)\n    }\n\n    sequenceOf(isLooping = true) {\n        item(audioOverlay)\n    }\n}\n```\n\nCheckout\nthe [`TransformerRepo.kt`](demo/src/main/java/dev/transformerkt/demo/transformer/TransformerRepo.kt)\nfile for more examples.\n\n## Included Effects\n\nCurrently only one custom effect is included in this library. It is the `AudioProcessor`\ncalled `VolumeChangeProcessor`. It allows you to change the volume of the audio track in the video.\n\n```kotlin\nval volumeChange = volumeChangeEffect(inputChannels = 2, volume = 0.5f)\n// ... Add to your EditedMediaItemSequence\n```\n\nIf you provide a `VolumeChangeProcessor` you are able to customize the volume based on the elapsed\ntime in the output video:\n\n```kotlin\nval volumeChange = volumeChangeEffect(inputChannels = 2) { elapsedMs -\u003e\n    when {\n        elapsedMs \u003c 1000 -\u003e 0f\n        elapsedMs \u003c 2000 -\u003e 0.5f\n        else -\u003e 1f\n    }\n}\n```\n\nFor convenience there is a `fadeAudioOutEffect` that will fade the audio out over a given duration:\n\n```kotlin\nval volumeChange = fadeAudioOutEffect(\n    totalDurationUs = 10_000_000,\n    inputChannels = 2,\n    initialVolume = 0.8f,\n    finalVolume = 0.3f,\n    fadeDurationUs = 2_000_000,\n)\n```\n\nThis effect will fade the audio from `0.8f` to `0.3f` over the course of `2_000_000` microseconds\n(2 seconds) from the end of the video.\n\n## Demo App\n\nA demo app is included in the `demo` module. It is a simple app that allows you to select a HDR\nvideo and convert it do a SDR video.\n\nTo run the demo app you can follow these steps:\n\n```shell\ngit clone git@github.com:jordond/transformerkt.git transformerkt\ncd transformerkt\n./gradlew assembleRelease\n```\n\nThen install the `demo/build/outputs/apk/release/demo-release.apk` file on your device.\n\n## License\n\nSee [LICENSE](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjordond%2FtransformerKt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjordond%2FtransformerKt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjordond%2FtransformerKt/lists"}