Skip to content

Commit

Permalink
Migrate MediaCodecBridge to jni_generator
Browse files Browse the repository at this point in the history
b/390481510
  • Loading branch information
haozheng-cobalt committed Jan 22, 2025
1 parent 73018f4 commit c4d1201
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 46 deletions.
5 changes: 4 additions & 1 deletion cobalt/android/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ jinja_template("cobalt_manifest") {
}

generate_jni("jni_headers") {
sources = [ "apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java" ]
sources = [
"apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java",
"apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java",
]
}

generate_jni("content_shell_jni_headers") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,15 @@
import java.nio.ByteOrder;
import java.util.Locale;
import java.util.Optional;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
// import org.chromium.base.annotations.NativeMethods;


// /** A wrapper of the MediaCodec class. */

@JNINamespace("starboard::android::shared")

/** A wrapper of the MediaCodec class. */
@SuppressWarnings("unused")
@UsedByNative
class MediaCodecBridge {
// After a flush(), dequeueOutputBuffer() can often produce empty presentation timestamps
// for several frames. As a result, the player may find that the time does not increase
Expand Down Expand Up @@ -143,41 +148,37 @@ public void onNewFrame(long presentationTimeUs) {
private FrameRateEstimator mFrameRateEstimator = null;
private BitrateAdjustmentTypes mBitrateAdjustmentType = BitrateAdjustmentTypes.NO_ADJUSTMENT;

@SuppressWarnings("unused")
@UsedByNative
public static class DequeueInputResult {
private int mStatus;
private int mIndex;

@SuppressWarnings("unused")
@UsedByNative
@CalledByNative("DequeueInputResult")
private DequeueInputResult() {
mStatus = MediaCodecStatus.ERROR;
mIndex = -1;
}

@SuppressWarnings("unused")
@UsedByNative
@CalledByNative("DequeueInputResult")
private DequeueInputResult(int status, int index) {
mStatus = status;
mIndex = index;
}

@SuppressWarnings("unused")
@UsedByNative
@CalledByNative("DequeueInputResult")
private int status() {
return mStatus;
}

@SuppressWarnings("unused")
@UsedByNative
@CalledByNative("DequeueInputResult")
private int index() {
return mIndex;
}
}

@SuppressWarnings("unused")
@UsedByNative
private static class DequeueOutputResult {
private int mStatus;
private int mIndex;
Expand All @@ -187,7 +188,7 @@ private static class DequeueOutputResult {
private int mNumBytes;

@SuppressWarnings("unused")
@UsedByNative
@CalledByNative("DequeueOutputResult")
private DequeueOutputResult() {
mStatus = MediaCodecStatus.ERROR;
mIndex = -1;
Expand All @@ -198,7 +199,7 @@ private DequeueOutputResult() {
}

@SuppressWarnings("unused")
@UsedByNative
@CalledByNative("DequeueOutputResult")
private DequeueOutputResult(
int status,
int index,
Expand All @@ -215,53 +216,51 @@ private DequeueOutputResult(
}

@SuppressWarnings("unused")
@UsedByNative
@CalledByNative("DequeueOutputResult")
private int status() {
return mStatus;
}

@SuppressWarnings("unused")
@UsedByNative
@CalledByNative("DequeueOutputResult")
private int index() {
return mIndex;
}

@SuppressWarnings("unused")
@UsedByNative
@CalledByNative("DequeueOutputResult")
private int flags() {
return mFlags;
}

@SuppressWarnings("unused")
@UsedByNative
@CalledByNative("DequeueOutputResult")
private int offset() {
return mOffset;
}

@SuppressWarnings("unused")
@UsedByNative
@CalledByNative("DequeueOutputResult")
private long presentationTimeMicroseconds() {
return mPresentationTimeMicroseconds;
}

@SuppressWarnings("unused")
@UsedByNative
@CalledByNative("DequeueOutputResult")
private int numBytes() {
return mNumBytes;
}
}

/** A wrapper around a MediaFormat. */
@SuppressWarnings("unused")
@UsedByNative
private static class GetOutputFormatResult {
private int mStatus;
// May be null if mStatus is not MediaCodecStatus.OK.
private MediaFormat mFormat;
private Optional<Boolean> mFormatHasCropValues = Optional.empty();

@SuppressWarnings("unused")
@UsedByNative
@CalledByNative("GetOutputFormatResult")
private GetOutputFormatResult() {
mStatus = MediaCodecStatus.ERROR;
mFormat = null;
Expand All @@ -280,59 +279,59 @@ private boolean formatHasCropValues() {
}

@SuppressWarnings("unused")
@UsedByNative
@CalledByNative("GetOutputFormatResult")
private int status() {
return mStatus;
}

@SuppressWarnings("unused")
@UsedByNative
@CalledByNative("GetOutputFormatResult")
private int textureWidth() {
return (mFormat != null && mFormat.containsKey(MediaFormat.KEY_WIDTH))
? mFormat.getInteger(MediaFormat.KEY_WIDTH)
: 0;
}

@SuppressWarnings("unused")
@UsedByNative
@CalledByNative("GetOutputFormatResult")
private int textureHeight() {
return (mFormat != null && mFormat.containsKey(MediaFormat.KEY_HEIGHT))
? mFormat.getInteger(MediaFormat.KEY_HEIGHT)
: 0;
}

@SuppressWarnings("unused")
@UsedByNative
@CalledByNative("GetOutputFormatResult")
private int cropLeft() {
return formatHasCropValues() ? mFormat.getInteger(KEY_CROP_LEFT) : -1;
}

@SuppressWarnings("unused")
@UsedByNative
@CalledByNative("GetOutputFormatResult")
private int cropTop() {
return formatHasCropValues() ? mFormat.getInteger(KEY_CROP_TOP) : -1;
}

@SuppressWarnings("unused")
@UsedByNative
@CalledByNative("GetOutputFormatResult")
private int cropRight() {
return formatHasCropValues() ? mFormat.getInteger(KEY_CROP_RIGHT) : -1;
}

@SuppressWarnings("unused")
@UsedByNative
@CalledByNative("GetOutputFormatResult")
private int cropBottom() {
return formatHasCropValues() ? mFormat.getInteger(KEY_CROP_BOTTOM) : -1;
}

@SuppressWarnings("unused")
@UsedByNative
@CalledByNative("GetOutputFormatResult")
private int sampleRate() {
return mFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE);
}

@SuppressWarnings("unused")
@UsedByNative
@CalledByNative("GetOutputFormatResult")
private int channelCount() {
return mFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
}
Expand Down
42 changes: 30 additions & 12 deletions starboard/android/shared/media_codec_bridge.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,16 @@
#include "starboard/android/shared/media_codec_bridge_eradicator.h"
#include "starboard/common/string.h"

// Must come after all headers that specialize FromJniType() / ToJniType().
#include "cobalt/android/jni_headers/MediaCodecBridge_jni.h"

namespace starboard {
namespace android {
namespace shared {

// TODO: (cobalt b/372559388) Update namespace to jni_zero.
using base::android::AttachCurrentThread;

namespace {

// See
Expand Down Expand Up @@ -506,7 +512,7 @@ jint MediaCodecBridge::Flush() {
}

FrameSize MediaCodecBridge::GetOutputSize() {
JniEnvExt* env = JniEnvExt::Get();
JNIEnv* env = AttachCurrentThread();
env->CallVoidMethodOrAbort(
j_media_codec_bridge_, "getOutputFormat",
"(Ldev/cobalt/media/MediaCodecBridge$GetOutputFormatResult;)V",
Expand All @@ -528,22 +534,34 @@ FrameSize MediaCodecBridge::GetOutputSize() {

AudioOutputFormatResult MediaCodecBridge::GetAudioOutputFormat() {
JniEnvExt* env = JniEnvExt::Get();
env->CallVoidMethodOrAbort(
j_media_codec_bridge_, "getOutputFormat",
"(Ldev/cobalt/media/MediaCodecBridge$GetOutputFormatResult;)V",
j_reused_get_output_format_result_);

jint status = env->CallIntMethodOrAbort(j_reused_get_output_format_result_,
"status", "()I");
JAVA_MediaCodecBridge_getOutputFormat(env, j_media_codec_bridge_,
j_reused_get_output_format_result_);

jint status = JAVA_GetOutputFormatResult_status(
env, j_reused_get_output_format_result_);
// env->CallVoidMethodOrAbort(
// j_media_codec_bridge_, "getOutputFormat",
// "(Ldev/cobalt/media/MediaCodecBridge$GetOutputFormatResult;)V",
// j_reused_get_output_format_result_);

// jint status = env->CallIntMethodOrAbort(j_reused_get_output_format_result_,
// "status", "()I");
if (status == MEDIA_CODEC_ERROR) {
return {status, 0, 0};
}

return {status,
env->CallIntMethodOrAbort(j_reused_get_output_format_result_,
"sampleRate", "()I"),
env->CallIntMethodOrAbort(j_reused_get_output_format_result_,
"channelCount", "()I")};
jint sample_rate = JAVA_GetOutputFormatResult_sampleRate(
env, j_reused_get_output_format_result_);
jint channel_count = JAVA_GetOutputFormatResult_channelCount(
env, j_reused_get_output_format_result_);

// return {status,
// env->CallIntMethodOrAbort(j_reused_get_output_format_result_,
// "sampleRate", "()I"),
// env->CallIntMethodOrAbort(j_reused_get_output_format_result_,
// "channelCount", "()I")};
return {status, sample_rate, channel_count};
}

void MediaCodecBridge::OnMediaCodecError(bool is_recoverable,
Expand Down
8 changes: 6 additions & 2 deletions starboard/android/shared/media_codec_bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <memory>
#include <string>

#include "base/android/scoped_java_ref.h"
#include "starboard/android/shared/jni_env_ext.h"
#include "starboard/android/shared/jni_utils.h"
#include "starboard/android/shared/media_common.h"
Expand Down Expand Up @@ -223,14 +224,17 @@ class MediaCodecBridge {
void Initialize(jobject j_media_codec_bridge);

Handler* handler_ = NULL;
jobject j_media_codec_bridge_ = NULL;

// The Java MediaCodecBridge instance.
base::android::ScopedJavaGlobalRef<jobject> j_media_codec_bridge_ = NULL;

// Profiling and allocation tracking has identified this area to be hot,
// and, capable of enough to cause GC times to raise high enough to impact
// playback. We mitigate this by reusing these output objects between calls
// to |DequeueInputBuffer|, |DequeueOutputBuffer|, and
// |GetOutputDimensions|.
jobject j_reused_get_output_format_result_ = NULL;
base::android::ScopedJavaGlobalRef<jobject>
j_reused_get_output_format_result_ = NULL;

MediaCodecBridge(const MediaCodecBridge&) = delete;
void operator=(const MediaCodecBridge&) = delete;
Expand Down

0 comments on commit c4d1201

Please sign in to comment.