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
8234a251
authored
Nov 17, 2015
by
Oliver Woodman
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Add H262 support for TS.
Built on top of
https://github.com/google/ExoPlayer/pull/915
.
parent
b1e42830
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
240 additions
and
0 deletions
library/src/main/java/com/google/android/exoplayer/extractor/ts/H262Reader.java
library/src/main/java/com/google/android/exoplayer/extractor/ts/TsExtractor.java
library/src/main/java/com/google/android/exoplayer/extractor/ts/H262Reader.java
0 → 100644
View file @
8234a251
/*
* 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
.
extractor
.
ts
;
import
com.google.android.exoplayer.C
;
import
com.google.android.exoplayer.MediaFormat
;
import
com.google.android.exoplayer.extractor.TrackOutput
;
import
com.google.android.exoplayer.util.MimeTypes
;
import
com.google.android.exoplayer.util.NalUnitUtil
;
import
com.google.android.exoplayer.util.ParsableByteArray
;
import
java.util.Arrays
;
import
java.util.Collections
;
/**
* Parses a continuous H262 byte stream and extracts individual frames.
*/
/* package */
final
class
H262Reader
extends
ElementaryStreamReader
{
private
static
final
int
START_PICTURE
=
0x00
;
private
static
final
int
START_SEQUENCE_HEADER
=
0xB3
;
private
static
final
int
START_EXTENSION
=
0xB5
;
private
static
final
int
START_GROUP
=
0xB8
;
// State that should not be reset on seek.
private
boolean
hasOutputFormat
;
// State that should be reset on seek.
private
final
boolean
[]
prefixFlags
;
private
final
CsdBuffer
csdBuffer
;
private
boolean
foundFirstFrameInGroup
;
private
long
totalBytesWritten
;
// Per sample state that gets reset at the start of each frame.
private
boolean
isKeyframe
;
private
long
framePosition
;
private
long
frameTimeUs
;
public
H262Reader
(
TrackOutput
output
)
{
super
(
output
);
prefixFlags
=
new
boolean
[
4
];
csdBuffer
=
new
CsdBuffer
(
128
);
}
@Override
public
void
seek
()
{
NalUnitUtil
.
clearPrefixFlags
(
prefixFlags
);
csdBuffer
.
reset
();
foundFirstFrameInGroup
=
false
;
totalBytesWritten
=
0
;
}
@Override
public
void
consume
(
ParsableByteArray
data
,
long
pesTimeUs
,
boolean
startOfPacket
)
{
while
(
data
.
bytesLeft
()
>
0
)
{
int
offset
=
data
.
getPosition
();
int
limit
=
data
.
limit
();
byte
[]
dataArray
=
data
.
data
;
// Append the data to the buffer.
totalBytesWritten
+=
data
.
bytesLeft
();
output
.
sampleData
(
data
,
data
.
bytesLeft
());
int
searchOffset
=
offset
;
while
(
true
)
{
int
startCodeOffset
=
NalUnitUtil
.
findNalUnit
(
dataArray
,
searchOffset
,
limit
,
prefixFlags
);
if
(
startCodeOffset
==
limit
)
{
// We've scanned to the end of the data without finding another start code.
if
(!
hasOutputFormat
)
{
csdBuffer
.
onData
(
dataArray
,
offset
,
limit
);
}
return
;
}
// We've found a start code with the following value.
int
startCodeValue
=
data
.
data
[
startCodeOffset
+
3
]
&
0xFF
;
if
(!
hasOutputFormat
)
{
// This is the number of bytes from the current offset to the start of the next start
// code. It may be negative if the start code started in the previously consumed data.
int
lengthToStartCode
=
startCodeOffset
-
offset
;
if
(
lengthToStartCode
>
0
)
{
csdBuffer
.
onData
(
dataArray
,
offset
,
startCodeOffset
);
}
// This is the number of bytes belonging to the next start code that have already been
// passed to csdDataTargetBuffer.
int
bytesAlreadyPassed
=
lengthToStartCode
<
0
?
-
lengthToStartCode
:
0
;
if
(
csdBuffer
.
onStartCode
(
startCodeValue
,
bytesAlreadyPassed
))
{
// The csd data is complete, so we can parse and output the media format.
output
.
format
(
parseMediaFormat
(
csdBuffer
));
hasOutputFormat
=
true
;
}
}
if
(
hasOutputFormat
&&
(
startCodeValue
==
START_GROUP
||
startCodeValue
==
START_PICTURE
))
{
int
bytesWrittenPastStartCode
=
limit
-
startCodeOffset
;
if
(
foundFirstFrameInGroup
)
{
int
flags
=
isKeyframe
?
C
.
SAMPLE_FLAG_SYNC
:
0
;
int
size
=
(
int
)
(
totalBytesWritten
-
framePosition
)
-
bytesWrittenPastStartCode
;
output
.
sampleMetadata
(
frameTimeUs
,
flags
,
size
,
bytesWrittenPastStartCode
,
null
);
isKeyframe
=
false
;
}
if
(
startCodeValue
==
START_GROUP
)
{
foundFirstFrameInGroup
=
false
;
isKeyframe
=
true
;
}
else
/* startCode == START_PICTURE */
{
foundFirstFrameInGroup
=
true
;
frameTimeUs
=
pesTimeUs
;
framePosition
=
totalBytesWritten
-
bytesWrittenPastStartCode
;
}
}
offset
=
startCodeOffset
;
searchOffset
=
offset
+
3
;
}
}
}
@Override
public
void
packetFinished
()
{
// Do nothing.
}
private
static
MediaFormat
parseMediaFormat
(
CsdBuffer
csdBuffer
)
{
byte
[]
csdData
=
Arrays
.
copyOf
(
csdBuffer
.
data
,
csdBuffer
.
length
);
int
firstByte
=
csdData
[
4
]
&
0xFF
;
int
secondByte
=
csdData
[
5
]
&
0xFF
;
int
thirdByte
=
csdData
[
6
]
&
0xFF
;
int
width
=
(
firstByte
<<
4
)
|
(
secondByte
>>
4
);
int
height
=
(
secondByte
&
0x0F
)
<<
8
|
thirdByte
;
float
pixelWidthHeightRatio
=
1
f
;
int
aspectRatioCode
=
(
csdData
[
7
]
&
0xF0
)
>>
4
;
switch
(
aspectRatioCode
)
{
case
2
:
pixelWidthHeightRatio
=
(
3
*
width
)
/
(
float
)
(
4
*
height
);
break
;
case
3
:
pixelWidthHeightRatio
=
(
9
*
width
)
/
(
float
)
(
16
*
height
);
break
;
case
4
:
pixelWidthHeightRatio
=
(
100
*
width
)
/
(
float
)
(
121
*
height
);
break
;
default
:
// Do nothing.
break
;
}
return
MediaFormat
.
createVideoFormat
(
null
,
MimeTypes
.
VIDEO_MPEG2
,
MediaFormat
.
NO_VALUE
,
MediaFormat
.
NO_VALUE
,
C
.
UNKNOWN_TIME_US
,
width
,
height
,
Collections
.
singletonList
(
csdData
),
MediaFormat
.
NO_VALUE
,
pixelWidthHeightRatio
);
}
private
static
final
class
CsdBuffer
{
private
boolean
isFilling
;
private
boolean
seenExtensionStartCode
;
public
int
length
;
public
byte
[]
data
;
public
CsdBuffer
(
int
initialCapacity
)
{
data
=
new
byte
[
initialCapacity
];
}
/**
* Resets the buffer, clearing any data that it holds.
*/
public
void
reset
()
{
isFilling
=
false
;
seenExtensionStartCode
=
false
;
length
=
0
;
}
/**
* Invoked when a start code is encountered in the stream.
*
* @param startCodeValue The start code value.
* @param bytesAlreadyPassed The number of bytes of the start code that have already been
* passed to {@link #onData(byte[], int, int)}, or 0.
* @return True if the csd data is now complete. False otherwise. If true is returned, neither
* this method or {@link #onData(byte[], int, int)} should be called again without an
* interleaving call to {@link #reset()}.
*/
public
boolean
onStartCode
(
int
startCodeValue
,
int
bytesAlreadyPassed
)
{
if
(
isFilling
)
{
if
(!
seenExtensionStartCode
&&
startCodeValue
==
START_EXTENSION
)
{
seenExtensionStartCode
=
true
;
}
else
{
length
-=
bytesAlreadyPassed
;
isFilling
=
false
;
return
true
;
}
}
else
if
(
startCodeValue
==
START_SEQUENCE_HEADER
)
{
isFilling
=
true
;
}
return
false
;
}
/**
* Invoked to pass stream data.
*
* @param newData Holds the data being passed.
* @param offset The offset of the data in {@code data}.
* @param limit The limit (exclusive) of the data in {@code data}.
*/
public
void
onData
(
byte
[]
newData
,
int
offset
,
int
limit
)
{
if
(!
isFilling
)
{
return
;
}
int
readLength
=
limit
-
offset
;
if
(
data
.
length
<
length
+
readLength
)
{
data
=
Arrays
.
copyOf
(
data
,
(
length
+
readLength
)
*
2
);
}
System
.
arraycopy
(
newData
,
offset
,
data
,
length
,
readLength
);
length
+=
readLength
;
}
}
}
library/src/main/java/com/google/android/exoplayer/extractor/ts/TsExtractor.java
View file @
8234a251
...
...
@@ -46,6 +46,7 @@ public final class TsExtractor implements Extractor {
private
static
final
int
TS_STREAM_TYPE_AAC
=
0x0F
;
private
static
final
int
TS_STREAM_TYPE_AC3
=
0x81
;
private
static
final
int
TS_STREAM_TYPE_E_AC3
=
0x87
;
private
static
final
int
TS_STREAM_TYPE_H262
=
0x02
;
private
static
final
int
TS_STREAM_TYPE_H264
=
0x1B
;
private
static
final
int
TS_STREAM_TYPE_H265
=
0x24
;
private
static
final
int
TS_STREAM_TYPE_ID3
=
0x15
;
...
...
@@ -313,6 +314,9 @@ public final class TsExtractor implements Extractor {
case
TS_STREAM_TYPE_AC3:
pesPayloadReader
=
new
Ac3Reader
(
output
.
track
(
streamType
));
break
;
case
TS_STREAM_TYPE_H262:
pesPayloadReader
=
new
H262Reader
(
output
.
track
(
TS_STREAM_TYPE_H262
));
break
;
case
TS_STREAM_TYPE_H264:
pesPayloadReader
=
new
H264Reader
(
output
.
track
(
TS_STREAM_TYPE_H264
),
new
SeiReader
(
output
.
track
(
TS_STREAM_TYPE_EIA608
)),
idrKeyframesOnly
);
...
...
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