Skip to content

Commit ad76a89

Browse files
committed
Improve android media player, also fix #2101
a. handle cbcr offset properly b. handle output dim properly
1 parent 5851603 commit ad76a89

File tree

3 files changed

+60
-12
lines changed

3 files changed

+60
-12
lines changed

core/media/AndroidMediaEngine.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ JNIEXPORT void JNICALL Java_org_axmol_lib_AxmolMediaEngine_nativeHandleVideoSamp
4444
int outputY,
4545
int videoX,
4646
int videoY,
47+
int cbcrOffset,
4748
int rotation,
4849
int videoPF)
4950
{
@@ -53,7 +54,7 @@ JNIEXPORT void JNICALL Java_org_axmol_lib_AxmolMediaEngine_nativeHandleVideoSamp
5354

5455
auto sampleData = static_cast<uint8_t*>(env->GetDirectBufferAddress(sampleBuffer));
5556

56-
mediaEngine->handleVideoSample(sampleData, sampleLen, outputX, outputY, videoX, videoY, rotation, videoPF);
57+
mediaEngine->handleVideoSample(sampleData, sampleLen, outputX, outputY, videoX, videoY, cbcrOffset, rotation, videoPF);
5758
}
5859

5960
JNIEXPORT void JNICALL Java_org_axmol_lib_AxmolMediaEngine_nativeSetDuration(JNIEnv* env,
@@ -162,7 +163,7 @@ bool AndroidMediaEngine::transferVideoFrame()
162163

163164
auto& buffer = _frameBuffer2;
164165

165-
ax::MEVideoFrame frame{buffer.data(), buffer.data() + _outputDim.x * _outputDim.y, buffer.size(),
166+
ax::MEVideoFrame frame{buffer.data(), buffer.data() + _cbcrOffset, buffer.size(),
166167
ax::MEVideoPixelDesc{static_cast<ax::MEVideoPixelFormat>(_videoPF), _outputDim}, _videoDim};
167168
frame._vpd._rotation = _videoRotation;
168169
assert(static_cast<int>(frame._dataLen) >= frame._vpd._dim.x * frame._vpd._dim.y * 3 / 2);
@@ -180,13 +181,15 @@ void AndroidMediaEngine::handleVideoSample(const uint8_t* buf,
180181
int outputY,
181182
int videoX,
182183
int videoY,
184+
int cbcrOffset,
183185
int rotation,
184186
int videoPF)
185187
{
186188
std::unique_lock<std::mutex> lck(_frameBuffer1Mtx);
187189
_frameBuffer1.assign(buf, buf + len);
188190
_outputDim.set(outputX, outputY);
189191
_videoDim.set(videoX, videoY);
192+
_cbcrOffset = cbcrOffset;
190193
_videoRotation = rotation;
191194
_videoPF = videoPF;
192195
}

core/media/AndroidMediaEngine.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class AndroidMediaEngine : public MediaEngine
6060
MEMediaState getState() const override;
6161
bool transferVideoFrame() override;
6262

63-
void handleVideoSample(const uint8_t* buf, size_t len, int outputX, int outputY, int videoX, int videoY, int rotation, int videoPF);
63+
void handleVideoSample(const uint8_t* buf, size_t len, int outputX, int outputY, int videoX, int videoY, int cbcrOffset, int rotation, int videoPF);
6464
void updateCurrentTime(double currentTime) { _currentTime = currentTime; }
6565
void updateDuration(double duration) { _duration = duration; }
6666

@@ -71,6 +71,7 @@ class AndroidMediaEngine : public MediaEngine
7171

7272
MEIntPoint _outputDim;
7373
MEIntPoint _videoDim;
74+
int _cbcrOffset{0};
7475
int _videoRotation{0};
7576
int _videoPF{-1};
7677

core/platform/android/java/src/org/axmol/lib/AxmolMediaEngine.java

Lines changed: 53 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -105,12 +105,14 @@ public class AxmolMediaEngine extends DefaultRenderersFactory implements Player.
105105
private AtomicInteger mState = new AtomicInteger(STATE_CLOSED);
106106
Point mOutputDim = new Point(); // The output dim match with buffer
107107
Point mVideoDim = new Point(); // The video dim (validate image dim)
108+
String mSampleMimeType = null;
108109
private int mVideoPF = -1;
109110
private int mVideoRotation = 0;
111+
private int mCbcrOffset = 0;
110112

111113
/** ------ native methods ------- */
112114
public static native void nativeHandleEvent(long nativeObj, int arg1);
113-
public static native void nativeHandleVideoSample(long nativeObj, ByteBuffer sampleData, int sampleLen, int outputX, int outputY, int videoX, int videoY, int rotation, int videoPF);
115+
public static native void nativeHandleVideoSample(long nativeObj, ByteBuffer sampleData, int sampleLen, int outputX, int outputY, int videoX, int videoY, int cbcrOffset, int rotation, int videoPF);
114116
public static native void nativeSetDuration(long nativeObj, double duration);
115117
public static native void nativeSetCurrentTime(long nativeObj, double currentTime);
116118

@@ -304,16 +306,18 @@ public void onVideoFrameAboutToBeRendered(
304306
Format format,
305307
@Nullable MediaFormat mediaFormat) {
306308
if (mOutputFormat != mediaFormat) {
309+
mSampleMimeType = format.sampleMimeType; // video/hevc, video/avc
307310
mOutputFormat = mediaFormat;
308-
updateVideoMeta();
311+
handleVideoMetaChanged();
309312
}
310313
}
311314

312-
/** update video informations */
313-
private void updateVideoMeta() {
315+
/** handle video informations changed */
316+
private void handleVideoMetaChanged() {
314317
MediaFormat format = mOutputFormat;
315318
if(format != null) {
316-
// String mimeType = format.getString(MediaFormat.KEY_MIME); // "video/raw"
319+
// String mimeType = format.getString(MediaFormat.KEY_MIME); // =="video/raw"
320+
317321
// Note: some android 11 and older devices not response desired color format(NV12), instead will be YUV420P aka I420
318322
// refer: https://github.com/axmolengine/axmol/issues/2049
319323
Integer colorFormat = format.getInteger(MediaFormat.KEY_COLOR_FORMAT);
@@ -329,30 +333,70 @@ private void updateVideoMeta() {
329333
Log.w(TAG, String.format("Unsupported color format: %d, video render may incorrect!", colorFormat));
330334
}
331335

336+
// output dim
332337
mOutputDim.x = format.getInteger(MediaFormat.KEY_WIDTH);
338+
mOutputDim.y = format.getInteger(MediaFormat.KEY_HEIGHT);
339+
340+
int stride = 0, sliceHeight = 0;
341+
if (format.containsKey(MediaFormat.KEY_STRIDE)) {
342+
stride = format.getInteger(MediaFormat.KEY_STRIDE);
343+
}
344+
if (format.containsKey(MediaFormat.KEY_SLICE_HEIGHT)) {
345+
sliceHeight = format.getInteger(MediaFormat.KEY_SLICE_HEIGHT);
346+
}
347+
Log.d(TAG, String.format("Frame stride and slice height: %dx%d", stride, sliceHeight);
348+
stride = Math.max(mOutputDim.x, stride);
349+
sliceHeight = Math.max(mOutputDim.y, sliceHeight);
350+
351+
/* Notes
352+
* 1. About desired frame size bytes
353+
* a. stride > mOutputDim.x: means all frame bytes should pass to GPU(shader), and
354+
* desired frame size bytes is: stride * sliceHeight * 3 / 2
355+
* b. stride == mOutputDim.x: means we need discard Y plane aligned extra data, and
356+
* desired frame size bytes is: stride * sliceHeight + (mOutputDim.x / 2) * (mOutputDim.y / 2) * 2
357+
* 2. About video frame size alignment
358+
* a. many devices may align 2, the sliceHeight == mOutputDim.y and stride == mOutputDim.x
359+
* b. H264: align 16 for both width and height
360+
* HEVC/H265: align 32 for both width and height
361+
* 3. The cbcrOffset should be always stride * sliceHeight
362+
* refer: https://github.com/axmolengine/axmol/issues/2101
363+
*/
364+
mCbcrOffset = stride * sliceHeight;
365+
int frameSizeBytes = 0;
366+
if (stride > mOutputDim.x) {
367+
mOutputDim.x = stride;
368+
mOutputDim.y = sliceHeight;
369+
frameSizeBytes = mCbcrOffset * 3 / 2;
370+
} else frameSizeBytes = mCbcrOffset + mOutputDim.x / 2 * mOutputDim.y;
371+
372+
// video dim
333373
if (format.containsKey(MediaFormat.KEY_CROP_LEFT)
334374
&& format.containsKey(MediaFormat.KEY_CROP_RIGHT)) {
335375
mVideoDim.x = format.getInteger(MediaFormat.KEY_CROP_RIGHT) + 1
336376
- format.getInteger(MediaFormat.KEY_CROP_LEFT);
337377
} else
338378
mVideoDim.x = mOutputDim.x;
339379

340-
mOutputDim.y = format.getInteger(MediaFormat.KEY_HEIGHT);
341380
if (format.containsKey(MediaFormat.KEY_CROP_TOP)
342381
&& format.containsKey(MediaFormat.KEY_CROP_BOTTOM)) {
343382
mVideoDim.y = format.getInteger(MediaFormat.KEY_CROP_BOTTOM) + 1
344383
- format.getInteger(MediaFormat.KEY_CROP_TOP);
345384
} else
346385
mVideoDim.y = mOutputDim.y;
347386

387+
// video rotation
348388
if (format.containsKey(MediaFormat.KEY_ROTATION)) {
349389
mVideoRotation = format.getInteger(MediaFormat.KEY_ROTATION);
350390
}
391+
392+
Log.d(TAG, String.format("Input format:%s, outputDim:%dx%d, videoDim:%dx%d, cbcrOffset:%d, frameSizeBytes:%d", mSampleMimeType,
393+
mOutputDim.x, mOutputDim.y,
394+
mVideoDim.x, mVideoDim.y,
395+
mCbcrOffset, frameSizeBytes));
351396
}
352397
}
353398

354399
/** handler or listener methods */
355-
356400
@Override
357401
public void processVideoFrame(MediaCodecAdapter codec, int index, long presentationTimeUs) {
358402
if (mState.get() != STATE_PLAYING) {
@@ -362,7 +406,7 @@ public void processVideoFrame(MediaCodecAdapter codec, int index, long presentat
362406
}
363407

364408
ByteBuffer tmpBuffer = codec.getOutputBuffer(index);
365-
nativeHandleVideoSample(mNativeObj, tmpBuffer, tmpBuffer.remaining(), mOutputDim.x, mOutputDim.y, mVideoDim.x, mVideoDim.y, mVideoRotation, mVideoPF);
409+
nativeHandleVideoSample(mNativeObj, tmpBuffer, tmpBuffer.remaining(), mOutputDim.x, mOutputDim.y, mVideoDim.x, mVideoDim.y, mCbcrOffset, mVideoRotation, mVideoPF);
366410

367411
AxmolEngine.getActivity().runOnUiThread(() -> {
368412
if (mPlayer != null) {
@@ -437,7 +481,7 @@ public void onVideoSizeChanged(VideoSize videoSize) {
437481
Log.d(TAG, String.format("[Individual]onVideoSizeChanged: (%d,%d)", videoSize.width, videoSize.height));
438482

439483
if(mPlayer != null)
440-
updateVideoMeta();
484+
handleVideoMetaChanged();
441485
}
442486

443487
@Override

0 commit comments

Comments
 (0)