Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
SDK
/
exoplayer
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Settings
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
f2c06fff
authored
Feb 27, 2023
by
tofunmi
Committed by
tonihei
Mar 01, 2023
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Create DataSourceBitmapLoader
PiperOrigin-RevId: 512659747
parent
d3639771
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
339 additions
and
0 deletions
library/datasource/src/androidTest/java/com/google/android/exoplayer2/upstream/DataSourceBitmapLoaderTest.java
library/datasource/src/main/java/com/google/android/exoplayer2/upstream/DataSourceBitmapLoader.java
library/datasource/src/androidTest/java/com/google/android/exoplayer2/upstream/DataSourceBitmapLoaderTest.java
0 → 100644
View file @
f2c06fff
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
com
.
google
.
android
.
exoplayer2
.
upstream
;
import
static
com
.
google
.
common
.
truth
.
Truth
.
assertThat
;
import
static
org
.
junit
.
Assert
.
assertThrows
;
import
android.graphics.Bitmap
;
import
android.graphics.BitmapFactory
;
import
android.net.Uri
;
import
androidx.test.core.app.ApplicationProvider
;
import
androidx.test.ext.junit.runners.AndroidJUnit4
;
import
com.google.android.exoplayer2.MediaMetadata
;
import
com.google.android.exoplayer2.testutil.TestUtil
;
import
com.google.common.util.concurrent.ListenableFuture
;
import
com.google.common.util.concurrent.MoreExecutors
;
import
java.io.File
;
import
java.nio.file.Files
;
import
java.nio.file.Paths
;
import
java.util.concurrent.ExecutionException
;
import
okhttp3.mockwebserver.MockResponse
;
import
okhttp3.mockwebserver.MockWebServer
;
import
okio.Buffer
;
import
org.junit.Before
;
import
org.junit.Rule
;
import
org.junit.Test
;
import
org.junit.function.ThrowingRunnable
;
import
org.junit.rules.TemporaryFolder
;
import
org.junit.runner.RunWith
;
/**
* Tests for {@link DataSourceBitmapLoader}.
*
* <p>This test needs to run as an androidTest because robolectric's BitmapFactory is not fully
* functional.
*/
@RunWith
(
AndroidJUnit4
.
class
)
public
class
DataSourceBitmapLoaderTest
{
@Rule
public
final
TemporaryFolder
tempFolder
=
new
TemporaryFolder
();
private
static
final
String
TEST_IMAGE_PATH
=
"media/jpeg/non-motion-photo-shortened.jpg"
;
private
DataSource
.
Factory
dataSourceFactory
;
@Before
public
void
setUp
()
{
dataSourceFactory
=
new
DefaultDataSource
.
Factory
(
ApplicationProvider
.
getApplicationContext
());
}
@Test
public
void
decodeBitmap_withValidData_loadsCorrectData
()
throws
Exception
{
DataSourceBitmapLoader
bitmapLoader
=
new
DataSourceBitmapLoader
(
MoreExecutors
.
newDirectExecutorService
(),
dataSourceFactory
);
byte
[]
imageData
=
TestUtil
.
getByteArray
(
ApplicationProvider
.
getApplicationContext
(),
TEST_IMAGE_PATH
);
Bitmap
bitmap
=
bitmapLoader
.
decodeBitmap
(
imageData
).
get
();
assertThat
(
bitmap
.
sameAs
(
BitmapFactory
.
decodeByteArray
(
imageData
,
/* offset= */
0
,
imageData
.
length
)))
.
isTrue
();
}
@Test
public
void
decodeBitmap_withInvalidData_throws
()
{
DataSourceBitmapLoader
bitmapLoader
=
new
DataSourceBitmapLoader
(
MoreExecutors
.
newDirectExecutorService
(),
dataSourceFactory
);
ListenableFuture
<
Bitmap
>
future
=
bitmapLoader
.
decodeBitmap
(
new
byte
[
0
]);
assertException
(
future:
:
get
,
IllegalArgumentException
.
class
,
/* messagePart= */
"Could not decode image data"
);
}
@Test
public
void
loadBitmap_withHttpUri_loadsCorrectData
()
throws
Exception
{
DataSourceBitmapLoader
bitmapLoader
=
new
DataSourceBitmapLoader
(
MoreExecutors
.
newDirectExecutorService
(),
dataSourceFactory
);
MockWebServer
mockWebServer
=
new
MockWebServer
();
byte
[]
imageData
=
TestUtil
.
getByteArray
(
ApplicationProvider
.
getApplicationContext
(),
TEST_IMAGE_PATH
);
Buffer
responseBody
=
new
Buffer
().
write
(
imageData
);
mockWebServer
.
enqueue
(
new
MockResponse
().
setResponseCode
(
200
).
setBody
(
responseBody
));
Bitmap
bitmap
=
bitmapLoader
.
loadBitmap
(
Uri
.
parse
(
mockWebServer
.
url
(
"test_path"
).
toString
())).
get
();
assertThat
(
bitmap
.
sameAs
(
BitmapFactory
.
decodeByteArray
(
imageData
,
/* offset= */
0
,
imageData
.
length
)))
.
isTrue
();
}
@Test
public
void
loadBitmap_httpUriAndServerError_throws
()
{
DataSourceBitmapLoader
bitmapLoader
=
new
DataSourceBitmapLoader
(
MoreExecutors
.
newDirectExecutorService
(),
dataSourceFactory
);
MockWebServer
mockWebServer
=
new
MockWebServer
();
mockWebServer
.
enqueue
(
new
MockResponse
().
setResponseCode
(
404
));
ListenableFuture
<
Bitmap
>
future
=
bitmapLoader
.
loadBitmap
(
Uri
.
parse
(
mockWebServer
.
url
(
"test_path"
).
toString
()));
assertException
(
future:
:
get
,
HttpDataSource
.
InvalidResponseCodeException
.
class
,
/* messagePart= */
"404"
);
}
@Test
public
void
loadBitmap_assetUri_loadsCorrectData
()
throws
Exception
{
DataSourceBitmapLoader
bitmapLoader
=
new
DataSourceBitmapLoader
(
MoreExecutors
.
newDirectExecutorService
(),
dataSourceFactory
);
byte
[]
imageData
=
TestUtil
.
getByteArray
(
ApplicationProvider
.
getApplicationContext
(),
TEST_IMAGE_PATH
);
Bitmap
bitmap
=
bitmapLoader
.
loadBitmap
(
Uri
.
parse
(
"asset:///"
+
TEST_IMAGE_PATH
)).
get
();
assertThat
(
bitmap
.
sameAs
(
BitmapFactory
.
decodeByteArray
(
imageData
,
/* offset= */
0
,
imageData
.
length
)))
.
isTrue
();
}
@Test
public
void
loadBitmap_assetUriWithAssetNotExisting_throws
()
throws
Exception
{
DataSourceBitmapLoader
bitmapLoader
=
new
DataSourceBitmapLoader
(
MoreExecutors
.
newDirectExecutorService
(),
dataSourceFactory
);
assertException
(
()
->
bitmapLoader
.
loadBitmap
(
Uri
.
parse
(
"asset:///not_valid/path/image.bmp"
)).
get
(),
AssetDataSource
.
AssetDataSourceException
.
class
,
/* messagePart= */
""
);
}
@Test
public
void
loadBitmap_withFileUri_loadsCorrectData
()
throws
Exception
{
byte
[]
imageData
=
TestUtil
.
getByteArray
(
ApplicationProvider
.
getApplicationContext
(),
TEST_IMAGE_PATH
);
File
file
=
tempFolder
.
newFile
();
Files
.
write
(
Paths
.
get
(
file
.
getAbsolutePath
()),
imageData
);
Uri
uri
=
Uri
.
fromFile
(
file
);
DataSourceBitmapLoader
bitmapLoader
=
new
DataSourceBitmapLoader
(
MoreExecutors
.
newDirectExecutorService
(),
dataSourceFactory
);
Bitmap
bitmap
=
bitmapLoader
.
loadBitmap
(
uri
).
get
();
assertThat
(
bitmap
.
sameAs
(
BitmapFactory
.
decodeByteArray
(
imageData
,
/* offset= */
0
,
imageData
.
length
)))
.
isTrue
();
}
@Test
public
void
loadBitmap_fileUriWithFileNotExisting_throws
()
throws
Exception
{
DataSourceBitmapLoader
bitmapLoader
=
new
DataSourceBitmapLoader
(
MoreExecutors
.
newDirectExecutorService
(),
dataSourceFactory
);
assertException
(
()
->
bitmapLoader
.
loadBitmap
(
Uri
.
parse
(
"file:///not_valid/path/image.bmp"
)).
get
(),
FileDataSource
.
FileDataSourceException
.
class
,
/* messagePart= */
"No such file or directory"
);
}
@Test
public
void
loadBitmapFromMetadata_withArtworkDataAndArtworkUriSet_decodeFromArtworkData
()
throws
Exception
{
byte
[]
imageData
=
TestUtil
.
getByteArray
(
ApplicationProvider
.
getApplicationContext
(),
TEST_IMAGE_PATH
);
MockWebServer
mockWebServer
=
new
MockWebServer
();
Uri
uri
=
Uri
.
parse
(
mockWebServer
.
url
(
"test_path"
).
toString
());
MediaMetadata
metadata
=
new
MediaMetadata
.
Builder
()
.
setArtworkData
(
imageData
,
MediaMetadata
.
PICTURE_TYPE_FRONT_COVER
)
.
setArtworkUri
(
uri
)
.
build
();
DataSourceBitmapLoader
bitmapLoader
=
new
DataSourceBitmapLoader
(
MoreExecutors
.
newDirectExecutorService
(),
dataSourceFactory
);
Bitmap
bitmap
=
bitmapLoader
.
loadBitmapFromMetadata
(
metadata
).
get
();
assertThat
(
bitmap
.
sameAs
(
BitmapFactory
.
decodeByteArray
(
imageData
,
/* offset= */
0
,
imageData
.
length
)))
.
isTrue
();
assertThat
(
mockWebServer
.
getRequestCount
()).
isEqualTo
(
0
);
}
@Test
public
void
loadBitmapFromMetadata_withArtworkUriSet_loadFromArtworkUri
()
throws
Exception
{
byte
[]
imageData
=
TestUtil
.
getByteArray
(
ApplicationProvider
.
getApplicationContext
(),
TEST_IMAGE_PATH
);
MockWebServer
mockWebServer
=
new
MockWebServer
();
Buffer
responseBody
=
new
Buffer
().
write
(
imageData
);
mockWebServer
.
enqueue
(
new
MockResponse
().
setResponseCode
(
200
).
setBody
(
responseBody
));
Uri
uri
=
Uri
.
parse
(
mockWebServer
.
url
(
"test_path"
).
toString
());
MediaMetadata
metadata
=
new
MediaMetadata
.
Builder
().
setArtworkUri
(
uri
).
build
();
DataSourceBitmapLoader
bitmapLoader
=
new
DataSourceBitmapLoader
(
MoreExecutors
.
newDirectExecutorService
(),
dataSourceFactory
);
Bitmap
bitmap
=
bitmapLoader
.
loadBitmapFromMetadata
(
metadata
).
get
();
assertThat
(
bitmap
.
sameAs
(
BitmapFactory
.
decodeByteArray
(
imageData
,
/* offset= */
0
,
imageData
.
length
)))
.
isTrue
();
assertThat
(
mockWebServer
.
getRequestCount
()).
isEqualTo
(
1
);
}
@Test
public
void
loadBitmapFromMetadata_withArtworkDataAndArtworkUriUnset_returnNull
()
throws
Exception
{
MediaMetadata
metadata
=
new
MediaMetadata
.
Builder
().
build
();
DataSourceBitmapLoader
bitmapLoader
=
new
DataSourceBitmapLoader
(
MoreExecutors
.
newDirectExecutorService
(),
dataSourceFactory
);
ListenableFuture
<
Bitmap
>
bitmapFuture
=
bitmapLoader
.
loadBitmapFromMetadata
(
metadata
);
assertThat
(
bitmapFuture
).
isNull
();
}
private
static
void
assertException
(
ThrowingRunnable
runnable
,
Class
<?
extends
Exception
>
clazz
,
String
messagePart
)
{
ExecutionException
executionException
=
assertThrows
(
ExecutionException
.
class
,
runnable
);
assertThat
(
executionException
).
hasCauseThat
().
isInstanceOf
(
clazz
);
assertThat
(
executionException
).
hasMessageThat
().
contains
(
messagePart
);
}
}
library/datasource/src/main/java/com/google/android/exoplayer2/upstream/DataSourceBitmapLoader.java
0 → 100644
View file @
f2c06fff
/*
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
com
.
google
.
android
.
exoplayer2
.
upstream
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkArgument
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkStateNotNull
;
import
android.content.Context
;
import
android.graphics.Bitmap
;
import
android.graphics.BitmapFactory
;
import
android.net.Uri
;
import
androidx.annotation.Nullable
;
import
com.google.android.exoplayer2.util.BitmapLoader
;
import
com.google.common.base.Supplier
;
import
com.google.common.base.Suppliers
;
import
com.google.common.util.concurrent.ListenableFuture
;
import
com.google.common.util.concurrent.ListeningExecutorService
;
import
com.google.common.util.concurrent.MoreExecutors
;
import
java.io.IOException
;
import
java.util.concurrent.Executors
;
/**
* A {@link BitmapLoader} implementation that uses a {@link DataSource} to support fetching images
* from URIs.
*
* <p>Loading tasks are delegated to a {@link ListeningExecutorService} defined during construction.
* If no executor service is passed, all tasks are delegated to a single-thread executor service
* that is shared between instances of this class.
*/
public
final
class
DataSourceBitmapLoader
implements
BitmapLoader
{
public
static
final
Supplier
<
ListeningExecutorService
>
DEFAULT_EXECUTOR_SERVICE
=
Suppliers
.
memoize
(
()
->
MoreExecutors
.
listeningDecorator
(
Executors
.
newSingleThreadExecutor
()));
private
final
ListeningExecutorService
listeningExecutorService
;
private
final
DataSource
.
Factory
dataSourceFactory
;
/**
* Creates an instance that uses a {@link DefaultHttpDataSource} for image loading and delegates
* loading tasks to a {@link Executors#newSingleThreadExecutor()}.
*/
public
DataSourceBitmapLoader
(
Context
context
)
{
this
(
checkStateNotNull
(
DEFAULT_EXECUTOR_SERVICE
.
get
()),
new
DefaultDataSource
.
Factory
(
context
));
}
/**
* Creates an instance that delegates loading tasks to the {@link ListeningExecutorService}.
*
* @param listeningExecutorService The {@link ListeningExecutorService}.
* @param dataSourceFactory The {@link DataSource.Factory} that creates the {@link DataSource}
* used to load the image.
*/
public
DataSourceBitmapLoader
(
ListeningExecutorService
listeningExecutorService
,
DataSource
.
Factory
dataSourceFactory
)
{
this
.
listeningExecutorService
=
listeningExecutorService
;
this
.
dataSourceFactory
=
dataSourceFactory
;
}
@Override
public
ListenableFuture
<
Bitmap
>
decodeBitmap
(
byte
[]
data
)
{
return
listeningExecutorService
.
submit
(()
->
decode
(
data
));
}
/** Loads an image from a {@link Uri}. */
@Override
public
ListenableFuture
<
Bitmap
>
loadBitmap
(
Uri
uri
)
{
return
listeningExecutorService
.
submit
(()
->
load
(
dataSourceFactory
.
createDataSource
(),
uri
));
}
private
static
Bitmap
decode
(
byte
[]
data
)
{
@Nullable
Bitmap
bitmap
=
BitmapFactory
.
decodeByteArray
(
data
,
/* offset= */
0
,
data
.
length
);
checkArgument
(
bitmap
!=
null
,
"Could not decode image data"
);
return
bitmap
;
}
private
static
Bitmap
load
(
DataSource
dataSource
,
Uri
uri
)
throws
IOException
{
DataSpec
dataSpec
=
new
DataSpec
(
uri
);
dataSource
.
open
(
dataSpec
);
byte
[]
readData
=
DataSourceUtil
.
readToEnd
(
dataSource
);
return
decode
(
readData
);
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment