Skip to content

Commit

Permalink
Add coil-video to support decoding video frames. (coil-kt#122)
Browse files Browse the repository at this point in the history
* Add coil-video to support decoding video frames.

* Documentation.

* Depend on updated Parameters API.

* Add README.

* Refactor extension functions.

* Guard.

* Use regular Dokka plugin.

* Update docs and sample.

* Formatting.

* Fix clashing name.

* Close the source.

* Optimize.

* Correctly close the source.

* Optimization.

* Remove operator.

* Move FakeBitmapPool out of sharedTest.

* Move sharedTest to coil-test module.

* Add shell test class.

* Add basic tests.

* Support allowRgb565.

* Support setting the video frame option.

* Fix check.

* Re-arrange.

* Re-arrange.

* WIP BufferedMediaDataSource rework.

* Convert VideoFrameDecoder to a Fetcher.

* Handle asset uris properly.

* Use extension function.

* Fix tests.

* Documentation.

* Documentation.

* Fix imports.

* Add to sample.

* WIP

* Ensure bitmap attributes.

* First pass.

* Fix Dokka.

* Docs.

* WIP.

* Correctly calculate isSampled.

* Clean up.

* Guard against hardware config.

* Rename asset.

* Docs.

* Rebase fix.

* Use extension function.

* Re-arrange.

* Re-arrange.

* Fix package.
  • Loading branch information
colinrtwhite authored Feb 7, 2020
1 parent 4d5312a commit 8c6108f
Show file tree
Hide file tree
Showing 64 changed files with 648 additions and 108 deletions.
13 changes: 11 additions & 2 deletions buildSrc/src/main/kotlin/Extensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import org.gradle.api.Project
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.dsl.DependencyHandler
import org.gradle.kotlin.dsl.kotlin
import org.gradle.kotlin.dsl.project
import kotlin.math.pow

val Project.minSdk: Int
Expand Down Expand Up @@ -59,7 +60,11 @@ fun DependencyHandler.androidTestImplementation(dependencyNotation: Any): Depend
return add("androidTestImplementation", dependencyNotation)
}

fun DependencyHandler.addTestDependencies(kotlinVersion: String) {
fun DependencyHandler.addTestDependencies(kotlinVersion: String, includeTestProject: Boolean = true) {
if (includeTestProject) {
testImplementation(project(":coil-test"))
}

testImplementation(kotlin("test-junit", kotlinVersion))
testImplementation(Library.KOTLINX_COROUTINES_TEST)

Expand All @@ -72,7 +77,11 @@ fun DependencyHandler.addTestDependencies(kotlinVersion: String) {
testImplementation(Library.ROBOLECTRIC)
}

fun DependencyHandler.addAndroidTestDependencies(kotlinVersion: String) {
fun DependencyHandler.addAndroidTestDependencies(kotlinVersion: String, includeTestProject: Boolean = true) {
if (includeTestProject) {
androidTestImplementation(project(":coil-test"))
}

androidTestImplementation(kotlin("test-junit", kotlinVersion))

androidTestImplementation(Library.ANDROIDX_TEST_CORE)
Expand Down
10 changes: 0 additions & 10 deletions coil-base/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,6 @@ android {
targetSdkVersion(project.targetSdk)
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
sourceSets {
getByName("test").apply {
assets.srcDirs("src/sharedTest/assets")
java.srcDirs("src/sharedTest/java")
}
getByName("androidTest").apply {
assets.srcDirs("src/sharedTest/assets")
java.srcDirs("src/sharedTest/java")
}
}
libraryVariants.all {
generateBuildConfigProvider?.configure { enabled = false }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import coil.util.Utils
import coil.util.createMockWebServer
import coil.util.createOptions
import coil.util.getDrawableCompat
import coil.util.size
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.suspendCancellableCoroutine
Expand Down Expand Up @@ -303,7 +304,7 @@ class RealImageLoaderIntegrationTest {

val resultDrawable = result.drawable
assertTrue(resultDrawable is BitmapDrawable)
assertTrue(resultDrawable.bitmap.run { width == size.width && height == size.height })
assertEquals(resultDrawable.bitmap.size, size)
}

@Test
Expand Down Expand Up @@ -387,7 +388,7 @@ class RealImageLoaderIntegrationTest {

val drawable = imageView.drawable
assertTrue(drawable is BitmapDrawable)
assertEquals(expectedSize, drawable.bitmap.run { PixelSize(width, height) })
assertEquals(expectedSize, drawable.bitmap.size)
}

private fun testGet(data: Any, expectedSize: PixelSize = PixelSize(100, 125)) {
Expand All @@ -398,7 +399,7 @@ class RealImageLoaderIntegrationTest {
}

assertTrue(drawable is BitmapDrawable)
assertEquals(expectedSize, drawable.bitmap.run { PixelSize(width, height) })
assertEquals(expectedSize, drawable.bitmap.size)
}

private fun copyNormalImageAssetToCacheDir(): File {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import coil.size.Scale
import coil.size.Size
import coil.util.createOptions
import coil.util.isSimilarTo
import coil.util.size
import kotlinx.coroutines.runBlocking
import okio.buffer
import okio.source
Expand Down Expand Up @@ -49,7 +50,7 @@ class BitmapFactoryDecoderTest {
assertEquals("closed", exception.message)
assertTrue(isSampled)
assertTrue(drawable is BitmapDrawable)
assertEquals(PixelSize(100, 125), drawable.bitmap.run { PixelSize(width, height) })
assertEquals(PixelSize(100, 125), drawable.bitmap.size)
assertEquals(drawable.bitmap.config, Bitmap.Config.ARGB_8888)
}

Expand Down
21 changes: 5 additions & 16 deletions coil-base/src/androidTest/java/coil/fetch/FileFetcherTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,11 @@ import android.content.Context
import androidx.test.core.app.ApplicationProvider
import coil.bitmappool.BitmapPool
import coil.size.PixelSize
import coil.util.copyAssetToFile
import coil.util.createOptions
import kotlinx.coroutines.runBlocking
import okio.buffer
import okio.sink
import okio.source
import org.junit.Before
import org.junit.Test
import java.io.File
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotEquals
Expand All @@ -32,16 +29,13 @@ class FileFetcherTest {

@Test
fun basic() {
// Copy the asset to filesDir.
val source = context.assets.open("normal.jpg").source()
val file = File(context.filesDir.absolutePath + File.separator + "normal.jpg")
val sink = file.sink().buffer()
source.use { sink.use { sink.writeAll(source) } }
val file = context.copyAssetToFile("normal.jpg")

assertTrue(fetcher.handles(file))

val size = PixelSize(100, 100)
val result = runBlocking {
fetcher.fetch(pool, file, PixelSize(100, 100), createOptions())
fetcher.fetch(pool, file, size, createOptions())
}

assertTrue(result is SourceResult)
Expand All @@ -51,12 +45,7 @@ class FileFetcherTest {

@Test
fun fileCacheKeyWithLastModified() {
val file = File(context.filesDir.absolutePath + File.separator + "file.jpg")

// Copy the asset to filesDir.
val source = context.assets.open("normal.jpg").source()
val sink = file.sink().buffer()
source.use { sink.use { sink.writeAll(source) } }
val file = context.copyAssetToFile("normal.jpg")

file.setLastModified(1234L)
val firstKey = fetcher.key(file)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package coil.transform

import android.content.Context
import android.graphics.BitmapFactory
import androidx.test.core.app.ApplicationProvider
import coil.bitmappool.BitmapPool
import coil.size.OriginalSize
import coil.util.decodeBitmapAsset
import coil.util.isSimilarTo
import kotlinx.coroutines.runBlocking
import org.junit.Before
Expand All @@ -26,8 +26,8 @@ class CircleCropTransformationTest {

@Test
fun basic() {
val input = BitmapFactory.decodeStream(context.assets.open("normal_small.jpg"))
val expected = BitmapFactory.decodeStream(context.assets.open("normal_small_circle.png"))
val input = context.decodeBitmapAsset("normal_small.jpg")
val expected = context.decodeBitmapAsset("normal_small_circle.png")

val actual = runBlocking {
transformation.transform(pool, input, OriginalSize)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package coil.transform

import android.content.Context
import android.graphics.BitmapFactory
import androidx.test.core.app.ApplicationProvider
import coil.bitmappool.BitmapPool
import coil.size.OriginalSize
import coil.util.decodeBitmapAsset
import coil.util.getPixels
import coil.util.isSimilarTo
import kotlinx.coroutines.runBlocking
Expand All @@ -27,8 +27,8 @@ class GrayscaleTransformationTest {

@Test
fun basic() {
val input = BitmapFactory.decodeStream(context.assets.open("normal.jpg"))
val expected = BitmapFactory.decodeStream(context.assets.open("normal_grayscale.jpg"))
val input = context.decodeBitmapAsset("normal.jpg")
val expected = context.decodeBitmapAsset("normal_grayscale.jpg")

val actual = runBlocking {
transformation.transform(pool, input, OriginalSize)
Expand Down
18 changes: 11 additions & 7 deletions coil-base/src/test/java/coil/RealImageLoaderBasicTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,15 @@ import coil.size.SizeResolver
import coil.transform.Transformation
import coil.util.createBitmap
import coil.util.createGetRequest
import coil.util.createImageLoader
import coil.util.createLoadRequest
import coil.util.decodeBitmapAsset
import coil.util.error
import coil.util.toDrawable
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.runBlocking
import okhttp3.OkHttpClient
import org.junit.After
import org.junit.Before
import org.junit.Test
Expand Down Expand Up @@ -64,11 +65,14 @@ class RealImageLoaderBasicTest {
bitmapPool = BitmapPool(Long.MAX_VALUE)
referenceCounter = BitmapReferenceCounter(bitmapPool)
memoryCache = MemoryCache(referenceCounter, Int.MAX_VALUE)
imageLoader = createImageLoader(
context = context,
bitmapPool = bitmapPool,
referenceCounter = referenceCounter,
memoryCache = memoryCache
imageLoader = RealImageLoader(
context,
DefaultRequestOptions(),
bitmapPool,
referenceCounter,
memoryCache,
OkHttpClient(),
ComponentRegistry()
)
}

Expand Down Expand Up @@ -483,7 +487,7 @@ class RealImageLoaderBasicTest {
@Suppress("SameParameterValue")
private fun decodeAssetAndAddToMemoryCache(key: String, fileName: String): Bitmap {
val options = BitmapFactory.Options().apply { inPreferredConfig = Bitmap.Config.HARDWARE }
val bitmap = checkNotNull(BitmapFactory.decodeStream(context.assets.open(fileName), null, options))
val bitmap = context.decodeBitmapAsset(fileName, options)
assertEquals(Bitmap.Config.HARDWARE, bitmap.config)
memoryCache.set(key, bitmap, false)
return bitmap
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL
import android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW
import android.content.ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
import android.graphics.Bitmap
import coil.bitmappool.strategy.FakeBitmapPoolStrategy
import coil.util.DEFAULT_BITMAP_SIZE
import coil.util.createBitmap
import org.junit.Before
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package coil.bitmappool
package coil.bitmappool.strategy

import android.graphics.Bitmap
import coil.bitmappool.strategy.BitmapPoolStrategy
import java.util.ArrayDeque

class FakeBitmapPoolStrategy : BitmapPoolStrategy {
Expand All @@ -16,7 +15,7 @@ class FakeBitmapPoolStrategy : BitmapPoolStrategy {
bitmaps += bitmap
}

override operator fun get(width: Int, height: Int, config: Bitmap.Config): Bitmap? {
override fun get(width: Int, height: Int, config: Bitmap.Config): Bitmap? {
return if (bitmaps.isEmpty()) null else bitmaps.removeLast()
}

Expand Down
2 changes: 1 addition & 1 deletion coil-base/src/test/java/coil/lifecycle/FakeLifecycle.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class FakeLifecycle : Lifecycle() {
}

override fun removeObserver(observer: LifecycleObserver) {
observers.remove(observer)
observers -= observer
}

override fun getCurrentState() = state
Expand Down
2 changes: 1 addition & 1 deletion coil-gif/src/main/java/coil/decode/ImageDecoderDecoder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class ImageDecoderDecoder : Decoder {
var isSampled = false

// Work around https://issuetracker.google.com/issues/139371066 by copying the source to a temp file.
source.use { tempFile.sink().use { source.readAll(it) } }
source.use { tempFile.sink().use(source::readAll) }
val decoderSource = ImageDecoder.createSource(tempFile)

val baseDrawable = decoderSource.decodeDrawable { info, _ ->
Expand Down
1 change: 1 addition & 0 deletions coil-sample/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ dependencies {
implementation(project(":coil-default"))
implementation(project(":coil-gif"))
implementation(project(":coil-svg"))
implementation(project(":coil-video"))

implementation(kotlin("stdlib", KotlinCompilerVersion.VERSION))

Expand Down
Binary file added coil-sample/src/main/assets/video.mp4
Binary file not shown.
7 changes: 7 additions & 0 deletions coil-sample/src/main/java/coil/sample/Application.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import coil.ImageLoader
import coil.decode.GifDecoder
import coil.decode.ImageDecoderDecoder
import coil.decode.SvgDecoder
import coil.fetch.VideoFrameFileFetcher
import coil.fetch.VideoFrameUriFetcher
import coil.util.CoilLogger
import okhttp3.Cache
import okhttp3.OkHttpClient
Expand All @@ -28,6 +30,11 @@ class Application : MultiDexApplication() {
availableMemoryPercentage(0.5) // Use 50% of the application's available memory.
crossfade(true) // Show a short crossfade when loading images from network or disk into an ImageView.
componentRegistry {
// Fetchers
add(VideoFrameFileFetcher(applicationContext))
add(VideoFrameUriFetcher(applicationContext))

// Decoders
if (SDK_INT >= P) {
add(ImageDecoderDecoder())
} else {
Expand Down
3 changes: 2 additions & 1 deletion coil-sample/src/main/java/coil/sample/AssetType.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ package coil.sample
enum class AssetType(val fileName: String) {
JPG("jpgs.json"),
GIF("gifs.json"),
SVG("svgs.json")
SVG("svgs.json"),
MP4("video.mp4")
}
8 changes: 8 additions & 0 deletions coil-sample/src/main/java/coil/sample/Extensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@ package coil.sample

import android.app.Activity
import android.content.Context
import android.graphics.Color
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.ColorInt
import androidx.annotation.IdRes
import androidx.annotation.LayoutRes
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import coil.size.PixelSize
import kotlin.random.Random

inline fun <reified V : View> ViewGroup.inflate(@LayoutRes layoutRes: Int, attachToRoot: Boolean = false): V {
return LayoutInflater.from(context).inflate(layoutRes, this, attachToRoot) as V
Expand All @@ -35,3 +38,8 @@ fun Context.getDisplaySize(): PixelSize {
fun Int.dp(context: Context): Float {
return this * context.resources.displayMetrics.density
}

@ColorInt
fun randomColor(): Int {
return Color.argb(128, Random.nextInt(256), Random.nextInt(256), Random.nextInt(256))
}
6 changes: 4 additions & 2 deletions coil-sample/src/main/java/coil/sample/Image.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package coil.sample

import androidx.annotation.ColorInt
import androidx.annotation.Px
import coil.request.Parameters

data class Image(
val url: String,
val uri: String,
@ColorInt val color: Int,
@Px val width: Int,
@Px val height: Int
@Px val height: Int,
val parameters: Parameters = Parameters.EMPTY
)
5 changes: 3 additions & 2 deletions coil-sample/src/main/java/coil/sample/ImageListAdapter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ class ImageListAdapter(
width = columnWidth
}

load(item.url) {
load(item.uri) {
parameters(item.parameters)
placeholder(ColorDrawable(item.color))
}

Expand All @@ -53,7 +54,7 @@ class ImageListAdapter(
}

private object Callback : DiffUtil.ItemCallback<Image>() {
override fun areItemsTheSame(old: Image, new: Image) = old.url == new.url
override fun areItemsTheSame(old: Image, new: Image) = old.uri == new.uri
override fun areContentsTheSame(old: Image, new: Image) = old == new
}
}
4 changes: 3 additions & 1 deletion coil-sample/src/main/java/coil/sample/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ class MainActivity : AppCompatActivity() {
is Screen.Detail -> {
list.isVisible = false
detail.isVisible = true
detail.load(screen.image.url)
detail.load(screen.image.uri) {
parameters(screen.image.parameters)
}
}
}
}
Expand Down
Loading

0 comments on commit 8c6108f

Please sign in to comment.