Skip to content

Commit ecd141a

Browse files
committed
[media] Add BUFFER_FLAG_DECODE_ONLY experiment
This PR adds the experiment of using the MediaCodec flag BUFFER_FLAG_DECODE_ONLY (introduced in Android 34) for resource improvement during playback. This experiment is currently hard-coded to be false, but when the Starboard-Feature Extension is added, a follow up PR will be submitted to make this a proper feature experiment. To get this experiment to work, we now make MediaCodecBridge keep track of seekToTime in playbacks with a Seek() function. Bug: 423927047
1 parent e62ee66 commit ecd141a

File tree

8 files changed

+57
-3
lines changed

8 files changed

+57
-3
lines changed

cobalt/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,14 @@ class MediaCodecBridge {
6868
private boolean mFlushed;
6969
private long mLastPresentationTimeUs;
7070
private double mPlaybackRate = 1.0;
71+
private long mSeekToTime = 0;
7172
private int mFps = 30;
7273
private final boolean mIsTunnelingPlayback;
7374

75+
// TODO: (b/423927047) Once the Starboard Extension for Chromium Features
76+
// lands, replace this macro with a starboard features.
77+
private boolean mBufferFlagDecodeOnlyExperiment = false;
78+
7479
private MediaCodec.OnFrameRenderedListener mFrameRendererListener;
7580
private MediaCodec.OnFirstTunnelFrameReadyListener mFirstTunnelFrameReadyListener;
7681

@@ -397,6 +402,12 @@ public static boolean isFrameRenderedCallbackEnabled() {
397402
return Build.VERSION.SDK_INT >= 34;
398403
}
399404

405+
private boolean isDecodeOnly(long presentationTimeUs) {
406+
// Starting with Android 14, we can use BUFFER_FLAG_DECODE_ONLY to explicitly skip video frames
407+
// before the seek time so that they won't be rendered.
408+
return Build.VERSION.SDK_INT >= 34 && (mBufferFlagDecodeOnlyExperiment) && (presentationTimeUs < mSeekToTime);
409+
}
410+
400411
@CalledByNative
401412
public static void createVideoMediaCodecBridge(
402413
long nativeMediaCodecBridge,
@@ -652,6 +663,11 @@ private void setPlaybackRate(double playbackRate) {
652663
}
653664
}
654665

666+
@CalledByNative
667+
private void seek(long seekToTime) {
668+
mSeekToTime = seekToTime;
669+
}
670+
655671
private void updateOperatingRate() {
656672
// We needn't set operation rate if playback rate is 0 or less.
657673
if (Double.compare(mPlaybackRate, 0.0) <= 0) {
@@ -739,6 +755,9 @@ private int queueInputBuffer(
739755
int index, int offset, int size, long presentationTimeUs, int flags) {
740756
resetLastPresentationTimeIfNeeded(presentationTimeUs);
741757
try {
758+
if (isDecodeOnly(presentationTimeUs)) {
759+
flags |= MediaCodec.BUFFER_FLAG_DECODE_ONLY;
760+
}
742761
mMediaCodec.get().queueInputBuffer(index, offset, size, presentationTimeUs, flags);
743762
} catch (Exception e) {
744763
Log.e(TAG, "Failed to queue input buffer", e);
@@ -773,7 +792,11 @@ private int queueSecureInputBuffer(
773792
return MediaCodecStatus.ERROR;
774793
}
775794

776-
mMediaCodec.get().queueSecureInputBuffer(index, offset, cryptoInfo, presentationTimeUs, 0);
795+
int flags = 0;
796+
if (isDecodeOnly(presentationTimeUs)) {
797+
flags |= MediaCodec.BUFFER_FLAG_DECODE_ONLY;
798+
}
799+
mMediaCodec.get().queueSecureInputBuffer(index, offset, cryptoInfo, presentationTimeUs, flags);
777800
} catch (MediaCodec.CryptoException e) {
778801
int errorCode = e.getErrorCode();
779802
if (errorCode == MediaCodec.CryptoException.ERROR_NO_KEY) {

starboard/android/shared/media_codec_bridge.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,11 @@ void MediaCodecBridge::SetPlaybackRate(double playback_rate) {
388388
playback_rate);
389389
}
390390

391+
void MediaCodecBridge::Seek(int64_t seek_to_time) {
392+
JNIEnv* env = AttachCurrentThread();
393+
Java_MediaCodecBridge_seek(env, j_media_codec_bridge_, seek_to_time);
394+
}
395+
391396
bool MediaCodecBridge::Restart() {
392397
JNIEnv* env = AttachCurrentThread();
393398
return Java_MediaCodecBridge_restart(env, j_media_codec_bridge_) == JNI_TRUE;

starboard/android/shared/media_codec_bridge.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ class MediaCodecBridge {
203203
void ReleaseOutputBufferAtTimestamp(jint index, jlong render_timestamp_ns);
204204

205205
void SetPlaybackRate(double playback_rate);
206+
void Seek(int64_t seek_to_time);
206207
bool Restart();
207208
jint Flush();
208209
void Stop();

starboard/android/shared/media_decoder.cc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,15 @@ void MediaDecoder::SetPlaybackRate(double playback_rate) {
231231
media_codec_bridge_->SetPlaybackRate(playback_rate);
232232
}
233233

234+
void MediaDecoder::Seek(int64_t seek_to_time) {
235+
SB_DCHECK(media_type_ == kSbMediaTypeVideo);
236+
SB_DCHECK(media_codec_bridge_);
237+
if (!media_codec_bridge_) {
238+
return;
239+
}
240+
media_codec_bridge_->Seek(seek_to_time);
241+
}
242+
234243
// static
235244
void* MediaDecoder::DecoderThreadEntryPoint(void* context) {
236245
SB_DCHECK(context);

starboard/android/shared/media_decoder.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ class MediaDecoder final
109109

110110
void SetPlaybackRate(double playback_rate);
111111

112+
void Seek(int64_t seek_to_time);
113+
112114
size_t GetNumberOfPendingInputs() const {
113115
return number_of_pending_inputs_.load();
114116
}

starboard/android/shared/video_decoder.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,7 @@ bool VideoDecoder::InitializeCodec(const VideoStreamInfo& video_stream_info,
758758
std::bind(&VideoDecoder::ReportError, this, _1, _2));
759759
}
760760
media_decoder_->SetPlaybackRate(playback_rate_);
761+
media_decoder_->Seek(seek_to_time_);
761762

762763
if (video_stream_info.codec == kSbMediaVideoCodecAv1) {
763764
SB_DCHECK(!pending_input_buffers_.empty());
@@ -1267,6 +1268,16 @@ void VideoDecoder::ReportError(SbPlayerError error,
12671268
error_cb_(kSbPlayerErrorDecode, error_message);
12681269
}
12691270

1271+
void VideoDecoder::Seek(int64_t seek_to_time) {
1272+
seek_to_time_ = seek_to_time;
1273+
if (video_frame_tracker_) {
1274+
video_frame_tracker_->Seek(seek_to_time_);
1275+
}
1276+
if (media_decoder_) {
1277+
media_decoder_->Seek(seek_to_time_);
1278+
}
1279+
}
1280+
12701281
} // namespace starboard::android::shared
12711282

12721283
extern "C" SB_EXPORT_PLATFORM void

starboard/android/shared/video_decoder.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ class VideoDecoder
9898
void UpdateDecodeTargetSizeAndContentRegion_Locked();
9999
void SetPlaybackRate(double playback_rate);
100100

101+
void Seek(int64_t seek_to_time);
102+
101103
void OnNewTextureAvailable();
102104

103105
bool is_decoder_created() const { return media_decoder_ != NULL; }
@@ -209,6 +211,7 @@ class VideoDecoder
209211
bool end_of_stream_written_ = false;
210212
volatile int64_t first_buffer_timestamp_; // microseconds
211213
std::atomic_bool has_new_texture_available_{false};
214+
int64_t seek_to_time_ = 0;
212215

213216
// Use |owns_video_surface_| only on decoder thread, to avoid unnecessary
214217
// invocation of ReleaseVideoSurface(), though ReleaseVideoSurface() would

starboard/android/shared/video_render_algorithm.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,8 @@ void VideoRenderAlgorithm::Render(
118118
}
119119

120120
void VideoRenderAlgorithm::Seek(int64_t seek_to_time) {
121-
if (frame_tracker_) {
122-
frame_tracker_->Seek(seek_to_time);
121+
if (video_decoder_) {
122+
video_decoder_->Seek(seek_to_time);
123123
}
124124
}
125125

0 commit comments

Comments
 (0)