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
5e4a35fa
authored
Mar 13, 2015
by
Andrew Lewis
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Add Mp4SampleExtractor, for reading unfragmented MP4 files.
parent
6d8c4dd4
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
468 additions
and
4 deletions
demo/src/main/java/com/google/android/exoplayer/demo/DemoUtil.java
demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java
demo/src/main/java/com/google/android/exoplayer/demo/Samples.java
demo/src/main/java/com/google/android/exoplayer/demo/player/Mp4RendererBuilder.java
library/src/main/java/com/google/android/exoplayer/source/Mp4SampleExtractor.java
library/src/main/java/com/google/android/exoplayer/upstream/BufferedNonBlockingInputStream.java
library/src/main/java/com/google/android/exoplayer/upstream/DataSourceStream.java
library/src/test/java/com/google/android/exoplayer/source/Mp4SampleExtractorTest.java
library/src/test/java/com/google/android/exoplayer/upstream/BufferedNonBlockingInputStreamTest.java
demo/src/main/java/com/google/android/exoplayer/demo/DemoUtil.java
View file @
5e4a35fa
...
@@ -48,6 +48,7 @@ public class DemoUtil {
...
@@ -48,6 +48,7 @@ public class DemoUtil {
public
static
final
int
TYPE_SS
=
1
;
public
static
final
int
TYPE_SS
=
1
;
public
static
final
int
TYPE_OTHER
=
2
;
public
static
final
int
TYPE_OTHER
=
2
;
public
static
final
int
TYPE_HLS
=
3
;
public
static
final
int
TYPE_HLS
=
3
;
public
static
final
int
TYPE_MP4
=
4
;
private
static
final
CookieManager
defaultCookieManager
;
private
static
final
CookieManager
defaultCookieManager
;
...
...
demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java
View file @
5e4a35fa
...
@@ -24,6 +24,7 @@ import com.google.android.exoplayer.demo.player.DefaultRendererBuilder;
...
@@ -24,6 +24,7 @@ import com.google.android.exoplayer.demo.player.DefaultRendererBuilder;
import
com.google.android.exoplayer.demo.player.DemoPlayer
;
import
com.google.android.exoplayer.demo.player.DemoPlayer
;
import
com.google.android.exoplayer.demo.player.DemoPlayer.RendererBuilder
;
import
com.google.android.exoplayer.demo.player.DemoPlayer.RendererBuilder
;
import
com.google.android.exoplayer.demo.player.HlsRendererBuilder
;
import
com.google.android.exoplayer.demo.player.HlsRendererBuilder
;
import
com.google.android.exoplayer.demo.player.Mp4RendererBuilder
;
import
com.google.android.exoplayer.demo.player.SmoothStreamingRendererBuilder
;
import
com.google.android.exoplayer.demo.player.SmoothStreamingRendererBuilder
;
import
com.google.android.exoplayer.demo.player.UnsupportedDrmException
;
import
com.google.android.exoplayer.demo.player.UnsupportedDrmException
;
import
com.google.android.exoplayer.metadata.GeobMetadata
;
import
com.google.android.exoplayer.metadata.GeobMetadata
;
...
@@ -215,6 +216,8 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
...
@@ -215,6 +216,8 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
new
WidevineTestMediaDrmCallback
(
contentId
),
debugTextView
,
audioCapabilities
);
new
WidevineTestMediaDrmCallback
(
contentId
),
debugTextView
,
audioCapabilities
);
case
DemoUtil
.
TYPE_HLS
:
case
DemoUtil
.
TYPE_HLS
:
return
new
HlsRendererBuilder
(
userAgent
,
contentUri
.
toString
());
return
new
HlsRendererBuilder
(
userAgent
,
contentUri
.
toString
());
case
DemoUtil
.
TYPE_MP4
:
return
new
Mp4RendererBuilder
(
contentUri
,
debugTextView
);
default
:
default
:
return
new
DefaultRendererBuilder
(
this
,
contentUri
,
debugTextView
);
return
new
DefaultRendererBuilder
(
this
,
contentUri
,
debugTextView
);
}
}
...
...
demo/src/main/java/com/google/android/exoplayer/demo/Samples.java
View file @
5e4a35fa
...
@@ -135,6 +135,12 @@ import java.util.Locale;
...
@@ -135,6 +135,12 @@ import java.util.Locale;
new
Sample
(
"Apple AAC 10s"
,
"https://devimages.apple.com.edgekey.net/"
new
Sample
(
"Apple AAC 10s"
,
"https://devimages.apple.com.edgekey.net/"
+
"streaming/examples/bipbop_4x3/gear0/fileSequence0.aac"
,
+
"streaming/examples/bipbop_4x3/gear0/fileSequence0.aac"
,
DemoUtil
.
TYPE_OTHER
),
DemoUtil
.
TYPE_OTHER
),
new
Sample
(
"Big Buck Bunny (MP4)"
,
"http://redirector.c.youtube.com/videoplayback?id=604ed5ce52eda7ee&itag=22&source=youtube"
+
"&sparams=ip,ipbits,expire&ip=0.0.0.0&ipbits=0&expire=19000000000&signature="
+
"2E853B992F6CAB9D28CA3BEBD84A6F26709A8A55.94344B0D8BA83A7417AAD24DACC8C71A9A878ECE"
+
"&key=ik0"
,
DemoUtil
.
TYPE_MP4
),
};
};
private
Samples
()
{}
private
Samples
()
{}
...
...
demo/src/main/java/com/google/android/exoplayer/demo/player/Mp4RendererBuilder.java
0 → 100644
View file @
5e4a35fa
/*
* Copyright (C) 2014 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
.
exoplayer
.
demo
.
player
;
import
com.google.android.exoplayer.MediaCodecAudioTrackRenderer
;
import
com.google.android.exoplayer.MediaCodecVideoTrackRenderer
;
import
com.google.android.exoplayer.TrackRenderer
;
import
com.google.android.exoplayer.demo.player.DemoPlayer.RendererBuilder
;
import
com.google.android.exoplayer.demo.player.DemoPlayer.RendererBuilderCallback
;
import
com.google.android.exoplayer.source.DefaultSampleSource
;
import
com.google.android.exoplayer.source.Mp4SampleExtractor
;
import
com.google.android.exoplayer.upstream.DataSpec
;
import
com.google.android.exoplayer.upstream.UriDataSource
;
import
android.media.MediaCodec
;
import
android.net.Uri
;
import
android.widget.TextView
;
/**
* A {@link RendererBuilder} for streams that can be read using {@link Mp4SampleExtractor}.
*/
public
class
Mp4RendererBuilder
implements
RendererBuilder
{
private
final
Uri
uri
;
private
final
TextView
debugTextView
;
public
Mp4RendererBuilder
(
Uri
uri
,
TextView
debugTextView
)
{
this
.
uri
=
uri
;
this
.
debugTextView
=
debugTextView
;
}
@Override
public
void
buildRenderers
(
DemoPlayer
player
,
RendererBuilderCallback
callback
)
{
// Build the video and audio renderers.
DefaultSampleSource
sampleSource
=
new
DefaultSampleSource
(
new
Mp4SampleExtractor
(
new
UriDataSource
(
"exoplayer"
,
null
),
new
DataSpec
(
uri
)),
2
);
MediaCodecVideoTrackRenderer
videoRenderer
=
new
MediaCodecVideoTrackRenderer
(
sampleSource
,
null
,
true
,
MediaCodec
.
VIDEO_SCALING_MODE_SCALE_TO_FIT
,
5000
,
null
,
player
.
getMainHandler
(),
player
,
50
);
MediaCodecAudioTrackRenderer
audioRenderer
=
new
MediaCodecAudioTrackRenderer
(
sampleSource
,
null
,
true
,
player
.
getMainHandler
(),
player
);
// Build the debug renderer.
TrackRenderer
debugRenderer
=
debugTextView
!=
null
?
new
DebugTrackRenderer
(
debugTextView
,
videoRenderer
)
:
null
;
// Invoke the callback.
TrackRenderer
[]
renderers
=
new
TrackRenderer
[
DemoPlayer
.
RENDERER_COUNT
];
renderers
[
DemoPlayer
.
TYPE_VIDEO
]
=
videoRenderer
;
renderers
[
DemoPlayer
.
TYPE_AUDIO
]
=
audioRenderer
;
renderers
[
DemoPlayer
.
TYPE_DEBUG
]
=
debugRenderer
;
callback
.
onRenderers
(
null
,
null
,
renderers
);
}
}
library/src/main/java/com/google/android/exoplayer/source/Mp4SampleExtractor.java
0 → 100644
View file @
5e4a35fa
This diff is collapsed.
Click to expand it.
library/src/main/java/com/google/android/exoplayer/upstream/BufferedNonBlockingInputStream.java
0 → 100644
View file @
5e4a35fa
/*
* Copyright (C) 2014 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
.
exoplayer
.
upstream
;
import
com.google.android.exoplayer.util.Assertions
;
import
java.nio.ByteBuffer
;
/**
* Input stream with non-blocking reading/skipping that also stores read/skipped data in a buffer.
* Call {@link #mark} to discard any buffered data before the current reading position. Call
* {@link #returnToMark} to move the current reading position back to the marked position, which is
* initially the start of the input stream.
*/
public
final
class
BufferedNonBlockingInputStream
implements
NonBlockingInputStream
{
private
final
NonBlockingInputStream
inputStream
;
private
final
byte
[]
bufferedBytes
;
private
long
inputStreamPosition
;
private
int
readPosition
;
private
int
writePosition
;
/**
* Wraps the specified {@code nonBlockingInputStream} for buffered reading using a buffer of size
* {@code bufferSize} bytes.
*/
public
BufferedNonBlockingInputStream
(
NonBlockingInputStream
nonBlockingInputStream
,
int
bufferSize
)
{
inputStream
=
Assertions
.
checkNotNull
(
nonBlockingInputStream
);
bufferedBytes
=
new
byte
[
bufferSize
];
}
@Override
public
int
skip
(
int
length
)
{
return
consumeStream
(
null
,
null
,
0
,
length
);
}
@Override
public
int
read
(
byte
[]
buffer
,
int
offset
,
int
length
)
{
return
consumeStream
(
null
,
buffer
,
offset
,
length
);
}
@Override
public
int
read
(
ByteBuffer
buffer
,
int
length
)
{
return
consumeStream
(
buffer
,
null
,
0
,
length
);
}
@Override
public
long
getAvailableByteCount
()
{
// The amount that can be read from the input stream is limited by how much can be buffered.
return
(
writePosition
-
readPosition
)
+
Math
.
min
(
inputStream
.
getAvailableByteCount
(),
bufferedBytes
.
length
-
writePosition
);
}
@Override
public
boolean
isEndOfStream
()
{
return
writePosition
==
readPosition
&&
inputStream
.
isEndOfStream
();
}
@Override
public
void
close
()
{
inputStream
.
close
();
inputStreamPosition
=
-
1
;
}
/** Returns the current position in the stream. */
public
long
getReadPosition
()
{
return
inputStreamPosition
-
(
writePosition
-
readPosition
);
}
/**
* Moves the mark to be at the current position. Any data before the current position is
* discarded. After calling this method, calling {@link #returnToMark} will move the reading
* position back to the mark position.
*/
public
void
mark
()
{
System
.
arraycopy
(
bufferedBytes
,
readPosition
,
bufferedBytes
,
0
,
writePosition
-
readPosition
);
writePosition
-=
readPosition
;
readPosition
=
0
;
}
/** Moves the current position back to the mark position. */
public
void
returnToMark
()
{
readPosition
=
0
;
}
/**
* Reads or skips data from the input stream. If {@code byteBuffer} is non-{@code null}, reads
* {@code length} bytes into {@code byteBuffer} (other arguments are ignored). If
* {@code byteArray} is non-{@code null}, reads {@code length} bytes into {@code byteArray} at
* {@code offset} (other arguments are ignored). Otherwise, skips {@code length} bytes.
*
* @param byteBuffer {@link ByteBuffer} to read into, or {@code null} to read into
* {@code byteArray} or skip.
* @param byteArray Byte array to read into, or {@code null} to read into {@code byteBuffer} or
* skip.
* @param offset Offset in {@code byteArray} to write to, if it is non-{@code null}.
* @param length Number of bytes to read or skip.
* @return The number of bytes consumed, or -1 if nothing was consumed and the end of stream was
* reached.
*/
private
int
consumeStream
(
ByteBuffer
byteBuffer
,
byte
[]
byteArray
,
int
offset
,
int
length
)
{
// If necessary, reduce length so that we do not need to write past the end of the array.
int
pendingBytes
=
writePosition
-
readPosition
;
length
=
Math
.
min
(
length
,
bufferedBytes
.
length
-
writePosition
+
pendingBytes
);
// If reading past the end of buffered data, request more and populate the buffer.
int
streamBytesRead
=
0
;
if
(
length
-
pendingBytes
>
0
)
{
streamBytesRead
=
inputStream
.
read
(
bufferedBytes
,
writePosition
,
length
-
pendingBytes
);
if
(
streamBytesRead
>
0
)
{
inputStreamPosition
+=
streamBytesRead
;
writePosition
+=
streamBytesRead
;
pendingBytes
+=
streamBytesRead
;
}
}
// Signal the end of the stream if nothing more will be read.
if
(
streamBytesRead
==
-
1
&&
pendingBytes
==
0
)
{
return
-
1
;
}
// Fill the buffer using buffered data if reading, or just skip otherwise.
length
=
Math
.
min
(
pendingBytes
,
length
);
if
(
byteBuffer
!=
null
)
{
byteBuffer
.
put
(
bufferedBytes
,
readPosition
,
length
);
}
else
if
(
byteArray
!=
null
)
{
System
.
arraycopy
(
bufferedBytes
,
readPosition
,
byteArray
,
offset
,
length
);
}
readPosition
+=
length
;
return
length
;
}
}
library/src/main/java/com/google/android/exoplayer/upstream/DataSourceStream.java
View file @
5e4a35fa
...
@@ -47,6 +47,10 @@ public final class DataSourceStream implements Loadable, NonBlockingInputStream
...
@@ -47,6 +47,10 @@ public final class DataSourceStream implements Loadable, NonBlockingInputStream
private
final
Allocator
allocator
;
private
final
Allocator
allocator
;
private
final
ReadHead
readHead
;
private
final
ReadHead
readHead
;
/** Whether {@link #allocation}'s capacity is fixed. If true, the allocation is not resized. */
private
final
boolean
isAllocationFixedSize
;
private
final
int
allocationSize
;
private
Allocation
allocation
;
private
Allocation
allocation
;
private
volatile
boolean
loadCanceled
;
private
volatile
boolean
loadCanceled
;
...
@@ -58,6 +62,9 @@ public final class DataSourceStream implements Loadable, NonBlockingInputStream
...
@@ -58,6 +62,9 @@ public final class DataSourceStream implements Loadable, NonBlockingInputStream
private
int
writeFragmentRemainingLength
;
private
int
writeFragmentRemainingLength
;
/**
/**
* Constructs an instance whose allocation grows to contain all of the data specified by the
* {@code dataSpec}.
*
* @param dataSource The source from which the data should be loaded.
* @param dataSource The source from which the data should be loaded.
* @param dataSpec Defines the data to be loaded. {@code dataSpec.length} must not exceed
* @param dataSpec Defines the data to be loaded. {@code dataSpec.length} must not exceed
* {@link Integer#MAX_VALUE}. If {@code dataSpec.length == C.LENGTH_UNBOUNDED} then
* {@link Integer#MAX_VALUE}. If {@code dataSpec.length == C.LENGTH_UNBOUNDED} then
...
@@ -72,12 +79,48 @@ public final class DataSourceStream implements Loadable, NonBlockingInputStream
...
@@ -72,12 +79,48 @@ public final class DataSourceStream implements Loadable, NonBlockingInputStream
this
.
allocator
=
allocator
;
this
.
allocator
=
allocator
;
resolvedLength
=
C
.
LENGTH_UNBOUNDED
;
resolvedLength
=
C
.
LENGTH_UNBOUNDED
;
readHead
=
new
ReadHead
();
readHead
=
new
ReadHead
();
isAllocationFixedSize
=
false
;
allocationSize
=
0
;
}
/**
* Constructs an instance whose allocation is of a fixed size, which may be smaller than the data
* specified by the {@code dataSpec}.
* <p>
* The allocation size determines how far ahead loading can proceed relative to the current
* reading position.
*
* @param dataSource The source form which the data should be loaded.
* @param dataSpec Defines the data to be loaded.
* @param allocator Used to obtain an {@link Allocation} for holding the data.
* @param allocationSize The minimum size for a fixed-size allocation that will hold the data
* loaded from {@code dataSource}.
*/
public
DataSourceStream
(
DataSource
dataSource
,
DataSpec
dataSpec
,
Allocator
allocator
,
int
allocationSize
)
{
Assertions
.
checkState
(
dataSpec
.
length
<=
Integer
.
MAX_VALUE
);
this
.
dataSource
=
dataSource
;
this
.
dataSpec
=
dataSpec
;
this
.
allocator
=
allocator
;
this
.
allocationSize
=
allocationSize
;
resolvedLength
=
C
.
LENGTH_UNBOUNDED
;
readHead
=
new
ReadHead
();
isAllocationFixedSize
=
true
;
}
}
/**
/**
* Resets the read position to the start of the data.
* Resets the read position to the start of the data.
*
* @throws UnsupportedOperationException Thrown if the allocation size is fixed.
*/
*/
public
void
resetReadPosition
()
{
public
void
resetReadPosition
()
{
if
(
isAllocationFixedSize
)
{
throw
new
UnsupportedOperationException
(
"The read position cannot be reset when using a fixed allocation"
);
}
readHead
.
reset
();
readHead
.
reset
();
}
}
...
@@ -176,7 +219,12 @@ public final class DataSourceStream implements Loadable, NonBlockingInputStream
...
@@ -176,7 +219,12 @@ public final class DataSourceStream implements Loadable, NonBlockingInputStream
byte
[][]
buffers
=
allocation
.
getBuffers
();
byte
[][]
buffers
=
allocation
.
getBuffers
();
while
(
bytesRead
<
bytesToRead
)
{
while
(
bytesRead
<
bytesToRead
)
{
if
(
readHead
.
fragmentRemaining
==
0
)
{
if
(
readHead
.
fragmentRemaining
==
0
)
{
if
(
readHead
.
fragmentIndex
==
buffers
.
length
-
1
)
{
Assertions
.
checkState
(
isAllocationFixedSize
);
readHead
.
fragmentIndex
=
0
;
}
else
{
readHead
.
fragmentIndex
++;
readHead
.
fragmentIndex
++;
}
readHead
.
fragmentOffset
=
allocation
.
getFragmentOffset
(
readHead
.
fragmentIndex
);
readHead
.
fragmentOffset
=
allocation
.
getFragmentOffset
(
readHead
.
fragmentIndex
);
readHead
.
fragmentRemaining
=
allocation
.
getFragmentLength
(
readHead
.
fragmentIndex
);
readHead
.
fragmentRemaining
=
allocation
.
getFragmentLength
(
readHead
.
fragmentIndex
);
}
}
...
@@ -194,6 +242,13 @@ public final class DataSourceStream implements Loadable, NonBlockingInputStream
...
@@ -194,6 +242,13 @@ public final class DataSourceStream implements Loadable, NonBlockingInputStream
readHead
.
fragmentRemaining
-=
bufferReadLength
;
readHead
.
fragmentRemaining
-=
bufferReadLength
;
}
}
if
(
isAllocationFixedSize
)
{
synchronized
(
readHead
)
{
// Notify load() of the updated position so it can resume.
readHead
.
notify
();
}
}
return
bytesRead
;
return
bytesRead
;
}
}
...
@@ -210,6 +265,7 @@ public final class DataSourceStream implements Loadable, NonBlockingInputStream
...
@@ -210,6 +265,7 @@ public final class DataSourceStream implements Loadable, NonBlockingInputStream
}
}
@Override
@Override
@SuppressWarnings
(
"NonAtomicVolatileUpdate"
)
public
void
load
()
throws
IOException
,
InterruptedException
{
public
void
load
()
throws
IOException
,
InterruptedException
{
if
(
loadCanceled
||
isLoadFinished
())
{
if
(
loadCanceled
||
isLoadFinished
())
{
// The load was canceled, or is already complete.
// The load was canceled, or is already complete.
...
@@ -221,7 +277,7 @@ public final class DataSourceStream implements Loadable, NonBlockingInputStream
...
@@ -221,7 +277,7 @@ public final class DataSourceStream implements Loadable, NonBlockingInputStream
if
(
loadPosition
==
0
&&
resolvedLength
==
C
.
LENGTH_UNBOUNDED
)
{
if
(
loadPosition
==
0
&&
resolvedLength
==
C
.
LENGTH_UNBOUNDED
)
{
loadDataSpec
=
dataSpec
;
loadDataSpec
=
dataSpec
;
long
resolvedLength
=
dataSource
.
open
(
loadDataSpec
);
long
resolvedLength
=
dataSource
.
open
(
loadDataSpec
);
if
(
resolvedLength
>
Integer
.
MAX_VALUE
)
{
if
(
!
isAllocationFixedSize
&&
resolvedLength
>
Integer
.
MAX_VALUE
)
{
throw
new
DataSourceStreamLoadException
(
throw
new
DataSourceStreamLoadException
(
new
UnexpectedLengthException
(
dataSpec
.
length
,
resolvedLength
));
new
UnexpectedLengthException
(
dataSpec
.
length
,
resolvedLength
));
}
}
...
@@ -235,10 +291,14 @@ public final class DataSourceStream implements Loadable, NonBlockingInputStream
...
@@ -235,10 +291,14 @@ public final class DataSourceStream implements Loadable, NonBlockingInputStream
}
}
if
(
allocation
==
null
)
{
if
(
allocation
==
null
)
{
if
(
isAllocationFixedSize
)
{
allocation
=
allocator
.
allocate
(
allocationSize
);
}
else
{
int
initialAllocationSize
=
resolvedLength
!=
C
.
LENGTH_UNBOUNDED
int
initialAllocationSize
=
resolvedLength
!=
C
.
LENGTH_UNBOUNDED
?
(
int
)
resolvedLength
:
CHUNKED_ALLOCATION_INCREMENT
;
?
(
int
)
resolvedLength
:
CHUNKED_ALLOCATION_INCREMENT
;
allocation
=
allocator
.
allocate
(
initialAllocationSize
);
allocation
=
allocator
.
allocate
(
initialAllocationSize
);
}
}
}
int
allocationCapacity
=
allocation
.
capacity
();
int
allocationCapacity
=
allocation
.
capacity
();
if
(
loadPosition
==
0
)
{
if
(
loadPosition
==
0
)
{
...
@@ -253,19 +313,26 @@ public final class DataSourceStream implements Loadable, NonBlockingInputStream
...
@@ -253,19 +313,26 @@ public final class DataSourceStream implements Loadable, NonBlockingInputStream
if
(
Thread
.
interrupted
())
{
if
(
Thread
.
interrupted
())
{
throw
new
InterruptedException
();
throw
new
InterruptedException
();
}
}
read
=
dataSource
.
read
(
buffers
[
writeFragmentIndex
],
writeFragmentOffset
,
writeFragmentRemainingLength
);
int
bytesToWrite
=
getBytesToWrite
();
read
=
dataSource
.
read
(
buffers
[
writeFragmentIndex
],
writeFragmentOffset
,
bytesToWrite
);
if
(
read
>
0
)
{
if
(
read
>
0
)
{
loadPosition
+=
read
;
loadPosition
+=
read
;
writeFragmentOffset
+=
read
;
writeFragmentOffset
+=
read
;
writeFragmentRemainingLength
-=
read
;
writeFragmentRemainingLength
-=
read
;
if
(
writeFragmentRemainingLength
==
0
&&
maybeMoreToLoad
())
{
if
(
writeFragmentRemainingLength
==
0
&&
maybeMoreToLoad
())
{
writeFragmentIndex
++;
writeFragmentIndex
++;
if
(
loadPosition
==
allocationCapacity
)
{
if
(
writeFragmentIndex
==
buffers
.
length
)
{
if
(
isAllocationFixedSize
)
{
// Wrap back to the first fragment.
writeFragmentIndex
=
0
;
}
else
{
// Grow the allocation.
allocation
.
ensureCapacity
(
allocationCapacity
+
CHUNKED_ALLOCATION_INCREMENT
);
allocation
.
ensureCapacity
(
allocationCapacity
+
CHUNKED_ALLOCATION_INCREMENT
);
allocationCapacity
=
allocation
.
capacity
();
allocationCapacity
=
allocation
.
capacity
();
buffers
=
allocation
.
getBuffers
();
buffers
=
allocation
.
getBuffers
();
}
}
}
writeFragmentOffset
=
allocation
.
getFragmentOffset
(
writeFragmentIndex
);
writeFragmentOffset
=
allocation
.
getFragmentOffset
(
writeFragmentIndex
);
writeFragmentRemainingLength
=
allocation
.
getFragmentLength
(
writeFragmentIndex
);
writeFragmentRemainingLength
=
allocation
.
getFragmentLength
(
writeFragmentIndex
);
}
}
...
@@ -281,6 +348,25 @@ public final class DataSourceStream implements Loadable, NonBlockingInputStream
...
@@ -281,6 +348,25 @@ public final class DataSourceStream implements Loadable, NonBlockingInputStream
}
}
}
}
/**
* Returns the number of bytes that can be written to the current fragment, blocking until the
* reader has consumed data if the allocation has a fixed size and is full.
*/
private
int
getBytesToWrite
()
throws
InterruptedException
{
if
(!
isAllocationFixedSize
)
{
return
writeFragmentRemainingLength
;
}
synchronized
(
readHead
)
{
while
(
loadPosition
==
readHead
.
position
+
allocation
.
capacity
())
{
readHead
.
wait
();
}
}
return
Math
.
min
(
writeFragmentRemainingLength
,
allocation
.
capacity
()
-
(
int
)
(
loadPosition
-
readHead
.
position
));
}
private
boolean
maybeMoreToLoad
()
{
private
boolean
maybeMoreToLoad
()
{
return
resolvedLength
==
C
.
LENGTH_UNBOUNDED
||
loadPosition
<
resolvedLength
;
return
resolvedLength
==
C
.
LENGTH_UNBOUNDED
||
loadPosition
<
resolvedLength
;
}
}
...
...
library/src/test/java/com/google/android/exoplayer/source/Mp4SampleExtractorTest.java
0 → 100644
View file @
5e4a35fa
This diff is collapsed.
Click to expand it.
library/src/test/java/com/google/android/exoplayer/upstream/BufferedNonBlockingInputStreamTest.java
0 → 100644
View file @
5e4a35fa
/*
* Copyright (C) 2014 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
.
exoplayer
.
upstream
;
import
static
org
.
mockito
.
Matchers
.
any
;
import
static
org
.
mockito
.
Matchers
.
anyInt
;
import
static
org
.
mockito
.
Matchers
.
eq
;
import
static
org
.
mockito
.
Mockito
.
verify
;
import
static
org
.
mockito
.
Mockito
.
when
;
import
com.google.android.exoplayer.SampleSource
;
import
junit.framework.TestCase
;
import
org.mockito.Mock
;
import
org.mockito.MockitoAnnotations
;
import
org.mockito.invocation.InvocationOnMock
;
import
org.mockito.stubbing.Answer
;
import
java.util.Arrays
;
/**
* Tests for {@link BufferedNonBlockingInputStream}.
*/
public
class
BufferedNonBlockingInputStreamTest
extends
TestCase
{
private
static
final
int
BUFFER_SIZE_BYTES
=
16
;
@Mock
private
NonBlockingInputStream
mockInputStream
;
private
BufferedNonBlockingInputStream
bufferedInputStream
;
@Override
public
void
setUp
()
{
MockitoAnnotations
.
initMocks
(
this
);
bufferedInputStream
=
new
BufferedNonBlockingInputStream
(
mockInputStream
,
BUFFER_SIZE_BYTES
);
}
public
void
testSkipClipsCountToBufferSizeWhenMarkSet
()
{
// When marking and skipping more than the buffer size
bufferedInputStream
.
mark
();
bufferedInputStream
.
skip
(
BUFFER_SIZE_BYTES
+
1
);
// Then BUFFER_SIZE_BYTES are read.
verify
(
mockInputStream
).
read
((
byte
[])
any
(),
eq
(
0
),
eq
(
BUFFER_SIZE_BYTES
));
}
public
void
testSkipResetSkipUsesBufferedData
()
{
// Given a buffered input stream that has already read BUFFER_SIZE_BYTES
stubInputStreamForReadingBytes
();
bufferedInputStream
.
mark
();
bufferedInputStream
.
skip
(
BUFFER_SIZE_BYTES
);
verify
(
mockInputStream
).
read
((
byte
[])
any
(),
eq
(
0
),
eq
(
BUFFER_SIZE_BYTES
));
// When resetting and reading the same amount, no extra data are read.
bufferedInputStream
.
returnToMark
();
bufferedInputStream
.
skip
(
BUFFER_SIZE_BYTES
);
verify
(
mockInputStream
).
read
((
byte
[])
any
(),
eq
(
0
),
eq
(
BUFFER_SIZE_BYTES
));
}
public
void
testReturnsEndOfStreamAfterBufferedData
()
{
// Given a buffered input stream that has read 1 byte (to end-of-stream) and has been reset
stubInputStreamForReadingBytes
();
bufferedInputStream
.
mark
();
bufferedInputStream
.
skip
(
1
);
stubInputStreamForReadingEndOfStream
();
bufferedInputStream
.
returnToMark
();
// When skipping, first 1 byte is returned, then end-of-stream.
assertEquals
(
1
,
bufferedInputStream
.
skip
(
1
));
assertEquals
(
SampleSource
.
END_OF_STREAM
,
bufferedInputStream
.
skip
(
1
));
}
public
void
testReadAtOffset
()
{
// Given a mock input stream that provide non-zero data
stubInputStreamForReadingBytes
();
// When reading a byte at offset 1
byte
[]
bytes
=
new
byte
[
2
];
bufferedInputStream
.
mark
();
bufferedInputStream
.
read
(
bytes
,
1
,
1
);
// Then only the second byte is set.
assertTrue
(
Arrays
.
equals
(
new
byte
[]
{(
byte
)
0
,
(
byte
)
0xFF
},
bytes
));
}
public
void
testSkipAfterMark
()
{
// Given a mock input stream that provides non-zero data, with three bytes read
stubInputStreamForReadingBytes
();
bufferedInputStream
.
skip
(
1
);
bufferedInputStream
.
mark
();
bufferedInputStream
.
skip
(
2
);
bufferedInputStream
.
returnToMark
();
// Then it is possible to skip one byte after the mark and read two bytes.
assertEquals
(
1
,
bufferedInputStream
.
skip
(
1
));
assertEquals
(
2
,
bufferedInputStream
.
read
(
new
byte
[
2
],
0
,
2
));
verify
(
mockInputStream
).
read
((
byte
[])
any
(),
eq
(
0
),
eq
(
1
));
verify
(
mockInputStream
).
read
((
byte
[])
any
(),
eq
(
0
),
eq
(
2
));
verify
(
mockInputStream
).
read
((
byte
[])
any
(),
eq
(
2
),
eq
(
1
));
}
/** Stubs the input stream to read 0xFF for all requests. */
private
void
stubInputStreamForReadingBytes
()
{
when
(
mockInputStream
.
read
((
byte
[])
any
(),
anyInt
(),
anyInt
())).
thenAnswer
(
new
Answer
<
Integer
>()
{
@Override
public
Integer
answer
(
InvocationOnMock
invocation
)
throws
Throwable
{
byte
[]
bytes
=
(
byte
[])
invocation
.
getArguments
()[
0
];
int
offset
=
(
int
)
invocation
.
getArguments
()[
1
];
int
length
=
(
int
)
invocation
.
getArguments
()[
2
];
for
(
int
i
=
0
;
i
<
length
;
i
++)
{
bytes
[
i
+
offset
]
=
(
byte
)
0xFF
;
}
return
length
;
}
});
when
(
mockInputStream
.
skip
(
anyInt
())).
thenAnswer
(
new
Answer
<
Integer
>()
{
@Override
public
Integer
answer
(
InvocationOnMock
invocation
)
throws
Throwable
{
return
(
int
)
invocation
.
getArguments
()[
0
];
}
});
}
/** Stubs the input stream to read end-of-stream for all requests. */
private
void
stubInputStreamForReadingEndOfStream
()
{
when
(
mockInputStream
.
read
((
byte
[])
any
(),
anyInt
(),
anyInt
()))
.
thenReturn
(
SampleSource
.
END_OF_STREAM
);
}
}
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