-
Notifications
You must be signed in to change notification settings - Fork 662
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[coil-video] Support MediaDataSource implementations (#1795)
* Implement issue coil#1790 * Address PR feedback * Update public API file * Set Timeout.NONE and remove ExperimentalCoilApi
- Loading branch information
1 parent
b904ac9
commit a031511
Showing
7 changed files
with
246 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
38 changes: 38 additions & 0 deletions
38
coil-video/src/androidTest/java/coil/FileMediaDataSource.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package coil | ||
|
||
import android.media.MediaDataSource | ||
import android.os.Build | ||
import androidx.annotation.RequiresApi | ||
import java.io.File | ||
import java.io.RandomAccessFile | ||
|
||
@RequiresApi(Build.VERSION_CODES.M) | ||
class FileMediaDataSource(private val file: File) : MediaDataSource() { | ||
|
||
private var randomAccessFile: RandomAccessFile? = null | ||
|
||
override fun readAt(position: Long, buffer: ByteArray?, offset: Int, size: Int): Int { | ||
synchronized(file) { | ||
if (randomAccessFile == null) { | ||
randomAccessFile = RandomAccessFile(file, "r") | ||
} | ||
|
||
if (position >= getSize()) { | ||
// indicates EOF | ||
return -1 | ||
} | ||
|
||
val sizeToRead = minOf(size, (getSize() - position).toInt()) | ||
randomAccessFile!!.seek(position) | ||
return randomAccessFile!!.read(buffer, offset, sizeToRead) | ||
} | ||
} | ||
|
||
override fun getSize(): Long { | ||
return file.length() | ||
} | ||
|
||
override fun close() { | ||
randomAccessFile?.close() | ||
} | ||
} |
36 changes: 36 additions & 0 deletions
36
coil-video/src/androidTest/java/coil/decode/MediaDataSourceOkioSourceTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package coil.decode | ||
|
||
import android.content.Context | ||
import android.os.Build.VERSION.SDK_INT | ||
import androidx.test.core.app.ApplicationProvider | ||
import coil.FileMediaDataSource | ||
import coil.fetch.MediaDataSourceFetcher | ||
import coil.util.assumeTrue | ||
import coil.util.copyAssetToFile | ||
import kotlinx.coroutines.test.runTest | ||
import okio.buffer | ||
import org.junit.Assert.assertArrayEquals | ||
import org.junit.Before | ||
import org.junit.Test | ||
|
||
class MediaDataSourceOkioSourceTest { | ||
|
||
private lateinit var context: Context | ||
|
||
@Before | ||
fun before() { | ||
context = ApplicationProvider.getApplicationContext() | ||
} | ||
|
||
@Test | ||
fun mediaDataSourceOkioSource() = runTest { | ||
assumeTrue(SDK_INT >= 23) | ||
val file = context.copyAssetToFile("video_frame_1.jpg") | ||
|
||
val expected = file.readBytes() | ||
val source = MediaDataSourceFetcher.MediaDataSourceOkioSource(FileMediaDataSource(file)) | ||
val actual = source.buffer().readByteArray() | ||
|
||
assertArrayEquals(expected, actual) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
45 changes: 45 additions & 0 deletions
45
coil-video/src/androidTest/java/coil/fetch/MediaDataSourceFetcherTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package coil.fetch | ||
|
||
import android.content.Context | ||
import android.os.Build | ||
import androidx.test.core.app.ApplicationProvider | ||
import coil.FileMediaDataSource | ||
import coil.ImageLoader | ||
import coil.request.Options | ||
import coil.util.assumeTrue | ||
import coil.util.copyAssetToFile | ||
import kotlin.test.assertEquals | ||
import kotlin.test.assertIs | ||
import kotlin.test.assertTrue | ||
import kotlinx.coroutines.test.runTest | ||
import org.junit.Before | ||
import org.junit.Test | ||
|
||
class MediaDataSourceFetcherTest { | ||
|
||
private lateinit var context: Context | ||
private lateinit var fetcherFactory: MediaDataSourceFetcher.Factory | ||
|
||
@Before | ||
fun before() { | ||
context = ApplicationProvider.getApplicationContext() | ||
fetcherFactory = MediaDataSourceFetcher.Factory() | ||
} | ||
|
||
@Test | ||
fun basic() = runTest { | ||
assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) | ||
val file = context.copyAssetToFile("video.mp4") | ||
|
||
val dataSource = FileMediaDataSource(file) | ||
val fetcher = assertIs<MediaDataSourceFetcher>( | ||
fetcherFactory.create(dataSource, Options(context), ImageLoader(context)), | ||
) | ||
|
||
val result = fetcher.fetch() | ||
|
||
assertTrue(result is SourceResult) | ||
assertEquals(null, result.mimeType) | ||
assertIs<MediaDataSourceFetcher.MediaSourceMetadata>(result.source.metadata) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
78 changes: 78 additions & 0 deletions
78
coil-video/src/main/java/coil/fetch/MediaDataSourceFetcher.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package coil.fetch | ||
|
||
import android.media.MediaDataSource | ||
import android.os.Build | ||
import androidx.annotation.RequiresApi | ||
import coil.ImageLoader | ||
import coil.decode.DataSource | ||
import coil.decode.ImageSource | ||
import coil.request.Options | ||
import okio.Buffer | ||
import okio.Source | ||
import okio.Timeout | ||
import okio.buffer | ||
|
||
@RequiresApi(Build.VERSION_CODES.M) | ||
class MediaDataSourceFetcher( | ||
private val data: MediaDataSource, | ||
private val options: Options, | ||
) : Fetcher { | ||
|
||
override suspend fun fetch(): FetchResult { | ||
val imageSource = ImageSource( | ||
source = MediaDataSourceOkioSource(data).buffer(), | ||
context = options.context, | ||
metadata = MediaSourceMetadata(data), | ||
) | ||
|
||
return SourceResult( | ||
source = imageSource, | ||
mimeType = null, | ||
dataSource = DataSource.DISK | ||
) | ||
} | ||
|
||
class Factory : Fetcher.Factory<MediaDataSource> { | ||
|
||
override fun create( | ||
data: MediaDataSource, | ||
options: Options, | ||
imageLoader: ImageLoader, | ||
): Fetcher { | ||
return MediaDataSourceFetcher(data, options) | ||
} | ||
} | ||
|
||
internal class MediaDataSourceOkioSource(private val mediaDataSource: MediaDataSource) : Source { | ||
|
||
private var size = mediaDataSource.size | ||
private var position: Long = 0L | ||
|
||
override fun read(sink: Buffer, byteCount: Long): Long { | ||
if (position >= size) { | ||
// indicates EOF | ||
return -1 | ||
} | ||
|
||
val sizeToRead = minOf(byteCount, size - position) | ||
val byteArray = ByteArray(sizeToRead.toInt()) | ||
val readBytes = mediaDataSource.readAt(position, byteArray, 0, byteArray.size) | ||
|
||
position += readBytes | ||
sink.write(byteArray, 0, readBytes) | ||
|
||
return readBytes.toLong() | ||
} | ||
|
||
override fun timeout(): Timeout { | ||
return Timeout.NONE | ||
} | ||
|
||
override fun close() { | ||
mediaDataSource.close() | ||
} | ||
} | ||
|
||
@RequiresApi(Build.VERSION_CODES.M) | ||
class MediaSourceMetadata(val mediaDataSource: MediaDataSource) : ImageSource.Metadata() | ||
} |