From 23e4236227bc3b85a8422ef43f2158152452097a Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 18 Feb 2020 10:29:19 +0000 Subject: [PATCH 01/59] Add missing IntDef to switch PiperOrigin-RevId: 295692163 --- .../main/java/com/google/android/exoplayer2/audio/WavUtil.java | 1 + 1 file changed, 1 insertion(+) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/audio/WavUtil.java b/library/core/src/main/java/com/google/android/exoplayer2/audio/WavUtil.java index dff81021de7..208989124a1 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/audio/WavUtil.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/audio/WavUtil.java @@ -61,6 +61,7 @@ public static int getTypeForPcmEncoding(@C.PcmEncoding int pcmEncoding) { return TYPE_PCM; case C.ENCODING_PCM_FLOAT: return TYPE_FLOAT; + case C.ENCODING_PCM_16BIT_BIG_ENDIAN: // Not TYPE_PCM, because TYPE_PCM is little endian. case C.ENCODING_INVALID: case Format.NO_VALUE: default: From ed1eade980677008eb8710b338bb1549a37df137 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Tue, 18 Feb 2020 17:00:38 +0000 Subject: [PATCH 02/59] Update stale comment in TrimmingAudioProcessor The part about leaving the pending trim start byte count unmodified if the processor was just configured is not correct. PiperOrigin-RevId: 295745371 --- .../exoplayer2/audio/TrimmingAudioProcessor.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/audio/TrimmingAudioProcessor.java b/library/core/src/main/java/com/google/android/exoplayer2/audio/TrimmingAudioProcessor.java index 9437e4ac26c..8d84325d932 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/audio/TrimmingAudioProcessor.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/audio/TrimmingAudioProcessor.java @@ -155,15 +155,16 @@ protected void onQueueEndOfStream() { @Override protected void onFlush() { if (reconfigurationPending) { + // This is the initial flush after reconfiguration. Prepare to trim bytes from the start/end. reconfigurationPending = false; endBuffer = new byte[trimEndFrames * inputAudioFormat.bytesPerFrame]; pendingTrimStartBytes = trimStartFrames * inputAudioFormat.bytesPerFrame; } else { - // Audio processors are flushed after initial configuration, so we leave the pending trim - // start byte count unmodified if the processor was just configured. Otherwise we (possibly - // incorrectly) assume that this is a seek to a non-zero position. We should instead check the - // timestamp of the first input buffer queued after flushing to decide whether to trim (see - // also [Internal: b/77292509]). + // This is a flush during playback (after the initial flush). We assume this was caused by a + // seek to a non-zero position and clear pending start bytes. This assumption may be wrong (we + // may be seeking to zero), but playing data that should have been trimmed shouldn't be + // noticeable after a seek. Ideally we would check the timestamp of the first input buffer + // queued after flushing to decide whether to trim (see also [Internal: b/77292509]). pendingTrimStartBytes = 0; } endBufferSize = 0; From 1818921a20b673f1d45f70659e8f27d4b0dc885b Mon Sep 17 00:00:00 2001 From: ibaker Date: Wed, 19 Feb 2020 11:09:53 +0000 Subject: [PATCH 03/59] Catch-and-log all subtitle decode errors issue:#6885 PiperOrigin-RevId: 295931197 --- RELEASENOTES.md | 6 +++ .../exoplayer2/decoder/SimpleDecoder.java | 7 +++- .../android/exoplayer2/text/TextRenderer.java | 40 ++++++++++++++----- 3 files changed, 40 insertions(+), 13 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index e23bf592a51..847746d425e 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,5 +1,11 @@ # Release notes # +### 2.11.4 (not yet released) ### + +* Text: Catch-and-log all fatal exceptions in `TextRenderer` instead of + re-throwing, allowing playback to continue even if subtitles fail + ([#6885](https://github.com/google/ExoPlayer/issues/6885)). + ### 2.11.3 (2020-02-19) ### * SmoothStreaming: Fix regression that broke playback in 2.11.2 diff --git a/library/core/src/main/java/com/google/android/exoplayer2/decoder/SimpleDecoder.java b/library/core/src/main/java/com/google/android/exoplayer2/decoder/SimpleDecoder.java index 03aabecb0e7..4eef1ea32de 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/decoder/SimpleDecoder.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/decoder/SimpleDecoder.java @@ -149,6 +149,7 @@ public final void flush() { while (!queuedOutputBuffers.isEmpty()) { queuedOutputBuffers.removeFirst().release(); } + exception = null; } } @@ -225,6 +226,7 @@ private boolean decode() throws InterruptedException { if (inputBuffer.isDecodeOnly()) { outputBuffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY); } + @Nullable E exception; try { exception = decode(inputBuffer, outputBuffer, resetDecoder); } catch (RuntimeException e) { @@ -238,8 +240,9 @@ private boolean decode() throws InterruptedException { exception = createUnexpectedDecodeException(e); } if (exception != null) { - // Memory barrier to ensure that the decoder exception is visible from the playback thread. - synchronized (lock) {} + synchronized (lock) { + this.exception = exception; + } return false; } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/TextRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/text/TextRenderer.java index 058b1c4526b..46c26db1228 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/TextRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/TextRenderer.java @@ -23,11 +23,11 @@ import androidx.annotation.Nullable; import com.google.android.exoplayer2.BaseRenderer; import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.FormatHolder; import com.google.android.exoplayer2.RendererCapabilities; import com.google.android.exoplayer2.util.Assertions; +import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.Util; import java.lang.annotation.Documented; @@ -45,6 +45,8 @@ */ public final class TextRenderer extends BaseRenderer implements Callback { + private static final String TAG = "TextRenderer"; + @Documented @Retention(RetentionPolicy.SOURCE) @IntDef({ @@ -143,19 +145,13 @@ protected void onStreamChanged(Format[] formats, long offsetUs) { @Override protected void onPositionReset(long positionUs, boolean joining) { - clearOutput(); inputStreamEnded = false; outputStreamEnded = false; - if (decoderReplacementState != REPLACEMENT_STATE_NONE) { - replaceDecoder(); - } else { - releaseBuffers(); - decoder.flush(); - } + resetOutputAndDecoder(); } @Override - public void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { + public void render(long positionUs, long elapsedRealtimeUs) { if (outputStreamEnded) { return; } @@ -165,7 +161,8 @@ public void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackEx try { nextSubtitle = decoder.dequeueOutputBuffer(); } catch (SubtitleDecoderException e) { - throw createRendererException(e, streamFormat); + handleDecoderError(e); + return; } } @@ -247,7 +244,8 @@ public void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackEx } } } catch (SubtitleDecoderException e) { - throw createRendererException(e, streamFormat); + handleDecoderError(e); + return; } } @@ -329,4 +327,24 @@ private void invokeUpdateOutputInternal(List cues) { output.onCues(cues); } + /** + * Called when {@link #decoder} throws an exception, so it can be logged and playback can + * continue. + * + *

Logs {@code e} and resets state to allow decoding the next sample. + */ + private void handleDecoderError(SubtitleDecoderException e) { + Log.e(TAG, "Subtitle decoding failed. streamFormat=" + streamFormat, e); + resetOutputAndDecoder(); + } + + private void resetOutputAndDecoder() { + clearOutput(); + if (decoderReplacementState != REPLACEMENT_STATE_NONE) { + replaceDecoder(); + } else { + releaseBuffers(); + decoder.flush(); + } + } } From 11635191a63e5ba1e1c0bf5b046faeb1b6cfb9a6 Mon Sep 17 00:00:00 2001 From: olly Date: Fri, 7 Feb 2020 00:57:15 +0000 Subject: [PATCH 04/59] Switch to new libgav1 frame buffer callback API. The new code in Libgav1GetFrameBuffer is copied from libgav1/src/frame_buffer_callback_adaptor.cc. It may become libgav1 utility functions available to libgav1 clients in the future. The Libgav1FrameBuffer struct in the old frame buffer callback API is defined as follows: typedef struct Libgav1FrameBuffer { uint8_t* data[3]; size_t size[3]; void* private_data; } Libgav1FrameBuffer; Copy these three fields to the JniFrameBuffer class as private data members and add the RawBuffer() and Id() getter methods. The existing AlignTo16 function is replaced by the copied Align template function. PiperOrigin-RevId: 293709205 --- extensions/av1/src/main/jni/gav1_jni.cc | 182 +++++++++++++++++++----- 1 file changed, 146 insertions(+), 36 deletions(-) diff --git a/extensions/av1/src/main/jni/gav1_jni.cc b/extensions/av1/src/main/jni/gav1_jni.cc index 9ac3ea5cd28..29ef3f0ec41 100644 --- a/extensions/av1/src/main/jni/gav1_jni.cc +++ b/extensions/av1/src/main/jni/gav1_jni.cc @@ -27,6 +27,7 @@ #endif // CPU_FEATURES_COMPILED_ANY_ARM_NEON #include +#include #include #include // NOLINT #include @@ -121,15 +122,13 @@ const char* GetJniErrorMessage(JniStatusCode error_code) { } } -// Manages Libgav1FrameBuffer and reference information. +// Manages frame buffer and reference information. class JniFrameBuffer { public: - explicit JniFrameBuffer(int id) : id_(id), reference_count_(0) { - gav1_frame_buffer_.private_data = &id_; - } + explicit JniFrameBuffer(int id) : id_(id), reference_count_(0) {} ~JniFrameBuffer() { for (int plane_index = kPlaneY; plane_index < kMaxPlanes; plane_index++) { - delete[] gav1_frame_buffer_.data[plane_index]; + delete[] raw_buffer_[plane_index]; } } @@ -160,9 +159,8 @@ class JniFrameBuffer { void RemoveReference() { reference_count_--; } bool InUse() const { return reference_count_ != 0; } - const Libgav1FrameBuffer& GetGav1FrameBuffer() const { - return gav1_frame_buffer_; - } + uint8_t* RawBuffer(int plane_index) const { return raw_buffer_[plane_index]; } + int Id() const { return id_; } // Attempts to reallocate data planes if the existing ones don't have enough // capacity. Returns true if the allocation was successful or wasn't needed, @@ -172,15 +170,14 @@ class JniFrameBuffer { for (int plane_index = kPlaneY; plane_index < kMaxPlanes; plane_index++) { const int min_size = (plane_index == kPlaneY) ? y_plane_min_size : uv_plane_min_size; - if (gav1_frame_buffer_.size[plane_index] >= min_size) continue; - delete[] gav1_frame_buffer_.data[plane_index]; - gav1_frame_buffer_.data[plane_index] = - new (std::nothrow) uint8_t[min_size]; - if (!gav1_frame_buffer_.data[plane_index]) { - gav1_frame_buffer_.size[plane_index] = 0; + if (raw_buffer_size_[plane_index] >= min_size) continue; + delete[] raw_buffer_[plane_index]; + raw_buffer_[plane_index] = new (std::nothrow) uint8_t[min_size]; + if (!raw_buffer_[plane_index]) { + raw_buffer_size_[plane_index] = 0; return false; } - gav1_frame_buffer_.size[plane_index] = min_size; + raw_buffer_size_[plane_index] = min_size; } return true; } @@ -190,9 +187,12 @@ class JniFrameBuffer { uint8_t* plane_[kMaxPlanes]; int displayed_width_[kMaxPlanes]; int displayed_height_[kMaxPlanes]; - int id_; + const int id_; int reference_count_; - Libgav1FrameBuffer gav1_frame_buffer_ = {}; + // Pointers to the raw buffers allocated for the data planes. + uint8_t* raw_buffer_[kMaxPlanes] = {}; + // Sizes of the raw buffers in bytes. + size_t raw_buffer_size_[kMaxPlanes] = {}; }; // Manages frame buffers used by libgav1 decoder and ExoPlayer. @@ -210,7 +210,7 @@ class JniBufferManager { } JniStatusCode GetBuffer(size_t y_plane_min_size, size_t uv_plane_min_size, - Libgav1FrameBuffer* frame_buffer) { + JniFrameBuffer** jni_buffer) { std::lock_guard lock(mutex_); JniFrameBuffer* output_buffer; @@ -230,7 +230,7 @@ class JniBufferManager { } output_buffer->AddReference(); - *frame_buffer = output_buffer->GetGav1FrameBuffer(); + *jni_buffer = output_buffer; return kJniStatusOk; } @@ -316,33 +316,142 @@ struct JniContext { JniStatusCode jni_status_code = kJniStatusOk; }; -int Libgav1GetFrameBuffer(void* private_data, size_t y_plane_min_size, - size_t uv_plane_min_size, - Libgav1FrameBuffer* frame_buffer) { - JniContext* const context = reinterpret_cast(private_data); +// Aligns |value| to the desired |alignment|. |alignment| must be a power of 2. +template +constexpr T Align(T value, T alignment) { + const T alignment_mask = alignment - 1; + return (value + alignment_mask) & ~alignment_mask; +} + +// Aligns |addr| to the desired |alignment|. |alignment| must be a power of 2. +uint8_t* AlignAddr(uint8_t* const addr, const size_t alignment) { + const auto value = reinterpret_cast(addr); + return reinterpret_cast(Align(value, alignment)); +} + +// Libgav1 frame buffer callbacks return 0 on success, -1 on failure. + +int Libgav1OnFrameBufferSizeChanged(void* /*callback_private_data*/, + int /*bitdepth*/, + libgav1::ImageFormat /*image_format*/, + int /*width*/, int /*height*/, + int /*left_border*/, int /*right_border*/, + int /*top_border*/, int /*bottom_border*/, + int /*stride_alignment*/) { + // The libgav1 decoder calls this callback to provide information on the + // subsequent frames in the video. JniBufferManager ignores this information. + return 0; +} + +int Libgav1GetFrameBuffer(void* callback_private_data, int bitdepth, + libgav1::ImageFormat image_format, int width, + int height, int left_border, int right_border, + int top_border, int bottom_border, + int stride_alignment, + libgav1::FrameBuffer2* frame_buffer) { + bool is_monochrome = false; + int8_t subsampling_x = 1; + int8_t subsampling_y = 1; + switch (image_format) { + case libgav1::kImageFormatYuv420: + break; + case libgav1::kImageFormatYuv422: + subsampling_y = 0; + break; + case libgav1::kImageFormatYuv444: + subsampling_x = subsampling_y = 0; + break; + default: + // image_format is libgav1::kImageFormatMonochrome400. (AV1 has only four + // image formats, hardcoded in the spec). + is_monochrome = true; + break; + } + + // Calculate y_stride (in bytes). It is padded to a multiple of + // |stride_alignment| bytes. + int y_stride = width + left_border + right_border; + if (bitdepth > 8) y_stride *= sizeof(uint16_t); + y_stride = Align(y_stride, stride_alignment); + // Size of the Y plane in bytes. + const uint64_t y_plane_size = + (height + top_border + bottom_border) * static_cast(y_stride) + + (stride_alignment - 1); + + const int uv_width = is_monochrome ? 0 : width >> subsampling_x; + const int uv_height = is_monochrome ? 0 : height >> subsampling_y; + const int uv_left_border = is_monochrome ? 0 : left_border >> subsampling_x; + const int uv_right_border = is_monochrome ? 0 : right_border >> subsampling_x; + const int uv_top_border = is_monochrome ? 0 : top_border >> subsampling_y; + const int uv_bottom_border = + is_monochrome ? 0 : bottom_border >> subsampling_y; + + // Calculate uv_stride (in bytes). It is padded to a multiple of + // |stride_alignment| bytes. + int uv_stride = uv_width + uv_left_border + uv_right_border; + if (bitdepth > 8) uv_stride *= sizeof(uint16_t); + uv_stride = Align(uv_stride, stride_alignment); + // Size of the U or V plane in bytes. + const uint64_t uv_plane_size = + is_monochrome ? 0 + : (uv_height + uv_top_border + uv_bottom_border) * + static_cast(uv_stride) + + (stride_alignment - 1); + + // Check if it is safe to cast y_plane_size and uv_plane_size to size_t. + if (y_plane_size > SIZE_MAX || uv_plane_size > SIZE_MAX) { + return -1; + } + + JniContext* const context = + reinterpret_cast(callback_private_data); + JniFrameBuffer* jni_buffer; context->jni_status_code = context->buffer_manager.GetBuffer( - y_plane_min_size, uv_plane_min_size, frame_buffer); + static_cast(y_plane_size), static_cast(uv_plane_size), + &jni_buffer); if (context->jni_status_code != kJniStatusOk) { LOGE("%s", GetJniErrorMessage(context->jni_status_code)); return -1; } + + uint8_t* const y_buffer = jni_buffer->RawBuffer(0); + uint8_t* const u_buffer = !is_monochrome ? jni_buffer->RawBuffer(1) : nullptr; + uint8_t* const v_buffer = !is_monochrome ? jni_buffer->RawBuffer(2) : nullptr; + + int left_border_bytes = left_border; + int uv_left_border_bytes = uv_left_border; + if (bitdepth > 8) { + left_border_bytes *= sizeof(uint16_t); + uv_left_border_bytes *= sizeof(uint16_t); + } + frame_buffer->plane[0] = AlignAddr( + y_buffer + (top_border * y_stride) + left_border_bytes, stride_alignment); + frame_buffer->plane[1] = + AlignAddr(u_buffer + (uv_top_border * uv_stride) + uv_left_border_bytes, + stride_alignment); + frame_buffer->plane[2] = + AlignAddr(v_buffer + (uv_top_border * uv_stride) + uv_left_border_bytes, + stride_alignment); + + frame_buffer->stride[0] = y_stride; + frame_buffer->stride[1] = frame_buffer->stride[2] = uv_stride; + + frame_buffer->private_data = reinterpret_cast(jni_buffer->Id()); + return 0; } -int Libgav1ReleaseFrameBuffer(void* private_data, - Libgav1FrameBuffer* frame_buffer) { - JniContext* const context = reinterpret_cast(private_data); - const int buffer_id = *reinterpret_cast(frame_buffer->private_data); +void Libgav1ReleaseFrameBuffer(void* callback_private_data, + void* buffer_private_data) { + JniContext* const context = + reinterpret_cast(callback_private_data); + const int buffer_id = reinterpret_cast(buffer_private_data); context->jni_status_code = context->buffer_manager.ReleaseBuffer(buffer_id); if (context->jni_status_code != kJniStatusOk) { LOGE("%s", GetJniErrorMessage(context->jni_status_code)); - return -1; } - return 0; } -constexpr int AlignTo16(int value) { return (value + 15) & (~15); } - void CopyPlane(const uint8_t* source, int source_stride, uint8_t* destination, int destination_stride, int width, int height) { while (height--) { @@ -508,8 +617,9 @@ DECODER_FUNC(jlong, gav1Init, jint threads) { libgav1::DecoderSettings settings; settings.threads = threads; - settings.get = Libgav1GetFrameBuffer; - settings.release = Libgav1ReleaseFrameBuffer; + settings.on_frame_buffer_size_changed = Libgav1OnFrameBufferSizeChanged; + settings.get_frame_buffer = Libgav1GetFrameBuffer; + settings.release_frame_buffer = Libgav1ReleaseFrameBuffer; settings.callback_private_data = context; context->libgav1_status_code = context->decoder.Init(&settings); @@ -619,7 +729,7 @@ DECODER_FUNC(jint, gav1GetFrame, jlong jContext, jobject jOutputBuffer, } const int buffer_id = - *reinterpret_cast(decoder_buffer->buffer_private_data); + reinterpret_cast(decoder_buffer->buffer_private_data); context->buffer_manager.AddBufferReference(buffer_id); JniFrameBuffer* const jni_buffer = context->buffer_manager.GetBuffer(buffer_id); @@ -680,7 +790,7 @@ DECODER_FUNC(jint, gav1RenderFrame, jlong jContext, jobject jSurface, const int32_t native_window_buffer_uv_height = (native_window_buffer.height + 1) / 2; const int native_window_buffer_uv_stride = - AlignTo16(native_window_buffer.stride / 2); + Align(native_window_buffer.stride / 2, 16); // TODO(b/140606738): Handle monochrome videos. From 0acc95cfbbb9c164c7321d7be98d425430245907 Mon Sep 17 00:00:00 2001 From: olly Date: Fri, 7 Feb 2020 15:51:27 +0000 Subject: [PATCH 05/59] Cast void* to JniContext* using static_cast. static_cast is more appropriate than reinrerpret_cast for casting from void* to JniContext*. See Section 7.2.1 (page 173) in The C++ Programming Language, 4th Edition and https://stackoverflow.com/questions/310451/should-i-use-static-cast-or-reinterpret-cast-when-casting-a-void-to-whatever PiperOrigin-RevId: 293812940 --- extensions/av1/src/main/jni/gav1_jni.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/extensions/av1/src/main/jni/gav1_jni.cc b/extensions/av1/src/main/jni/gav1_jni.cc index 29ef3f0ec41..8ba4413fea6 100644 --- a/extensions/av1/src/main/jni/gav1_jni.cc +++ b/extensions/av1/src/main/jni/gav1_jni.cc @@ -403,8 +403,7 @@ int Libgav1GetFrameBuffer(void* callback_private_data, int bitdepth, return -1; } - JniContext* const context = - reinterpret_cast(callback_private_data); + JniContext* const context = static_cast(callback_private_data); JniFrameBuffer* jni_buffer; context->jni_status_code = context->buffer_manager.GetBuffer( static_cast(y_plane_size), static_cast(uv_plane_size), @@ -443,8 +442,7 @@ int Libgav1GetFrameBuffer(void* callback_private_data, int bitdepth, void Libgav1ReleaseFrameBuffer(void* callback_private_data, void* buffer_private_data) { - JniContext* const context = - reinterpret_cast(callback_private_data); + JniContext* const context = static_cast(callback_private_data); const int buffer_id = reinterpret_cast(buffer_private_data); context->jni_status_code = context->buffer_manager.ReleaseBuffer(buffer_id); if (context->jni_status_code != kJniStatusOk) { From b73f6b6ef06bd2c2af6e490bf3a47cf2cebab864 Mon Sep 17 00:00:00 2001 From: olly Date: Fri, 14 Feb 2020 21:16:57 +0000 Subject: [PATCH 06/59] gav1_jni: fix pointer->int conversion warnings fixes: gav1_jni.cc:446:25: error: cast from pointer to smaller type 'int' loses information const int buffer_id = reinterpret_cast(buffer_private_data); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ gav1_jni.cc:730:9: error: cast from pointer to smaller type 'int' loses information reinterpret_cast(decoder_buffer->buffer_private_data); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ after https://github.com/google/ExoPlayer/commit/0915998add5918214fa0282a69b50a159168a6d5 PiperOrigin-RevId: 295211245 --- extensions/av1/src/main/jni/gav1_jni.cc | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/extensions/av1/src/main/jni/gav1_jni.cc b/extensions/av1/src/main/jni/gav1_jni.cc index 8ba4413fea6..30c88693df7 100644 --- a/extensions/av1/src/main/jni/gav1_jni.cc +++ b/extensions/av1/src/main/jni/gav1_jni.cc @@ -27,6 +27,8 @@ #endif // CPU_FEATURES_COMPILED_ANY_ARM_NEON #include +#include +#include #include #include #include // NOLINT @@ -443,8 +445,10 @@ int Libgav1GetFrameBuffer(void* callback_private_data, int bitdepth, void Libgav1ReleaseFrameBuffer(void* callback_private_data, void* buffer_private_data) { JniContext* const context = static_cast(callback_private_data); - const int buffer_id = reinterpret_cast(buffer_private_data); - context->jni_status_code = context->buffer_manager.ReleaseBuffer(buffer_id); + const intptr_t buffer_id = reinterpret_cast(buffer_private_data); + assert(buffer_id <= INT_MAX); + context->jni_status_code = + context->buffer_manager.ReleaseBuffer(static_cast(buffer_id)); if (context->jni_status_code != kJniStatusOk) { LOGE("%s", GetJniErrorMessage(context->jni_status_code)); } @@ -726,11 +730,12 @@ DECODER_FUNC(jint, gav1GetFrame, jlong jContext, jobject jOutputBuffer, return kStatusError; } - const int buffer_id = - reinterpret_cast(decoder_buffer->buffer_private_data); - context->buffer_manager.AddBufferReference(buffer_id); + const intptr_t buffer_id = + reinterpret_cast(decoder_buffer->buffer_private_data); + assert(buffer_id <= INT_MAX); + context->buffer_manager.AddBufferReference(static_cast(buffer_id)); JniFrameBuffer* const jni_buffer = - context->buffer_manager.GetBuffer(buffer_id); + context->buffer_manager.GetBuffer(static_cast(buffer_id)); jni_buffer->SetFrameData(*decoder_buffer); env->CallVoidMethod(jOutputBuffer, context->init_for_private_frame_method, decoder_buffer->displayed_width[kPlaneY], @@ -739,7 +744,8 @@ DECODER_FUNC(jint, gav1GetFrame, jlong jContext, jobject jOutputBuffer, // Exception is thrown in Java when returning from the native call. return kStatusError; } - env->SetIntField(jOutputBuffer, context->decoder_private_field, buffer_id); + env->SetIntField(jOutputBuffer, context->decoder_private_field, + static_cast(buffer_id)); } return kStatusOk; From 766b383d274ebbf9ebe387b0abe8df9dc323f8eb Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 17 Feb 2020 11:00:56 +0000 Subject: [PATCH 07/59] Use libgav1 frame buffer callback helper functions Libgav1 recently added the ComputeFrameBufferInfo() and SetFrameBuffer() helper functions for writing frame buffer callbacks. Using them simplifies the Libgav1GetFrameBuffer() function. Also resurrect the AlignTo16() function. PiperOrigin-RevId: 295548330 --- extensions/av1/src/main/jni/gav1_jni.cc | 111 ++++-------------------- 1 file changed, 18 insertions(+), 93 deletions(-) diff --git a/extensions/av1/src/main/jni/gav1_jni.cc b/extensions/av1/src/main/jni/gav1_jni.cc index 30c88693df7..4ce614a3d97 100644 --- a/extensions/av1/src/main/jni/gav1_jni.cc +++ b/extensions/av1/src/main/jni/gav1_jni.cc @@ -318,19 +318,6 @@ struct JniContext { JniStatusCode jni_status_code = kJniStatusOk; }; -// Aligns |value| to the desired |alignment|. |alignment| must be a power of 2. -template -constexpr T Align(T value, T alignment) { - const T alignment_mask = alignment - 1; - return (value + alignment_mask) & ~alignment_mask; -} - -// Aligns |addr| to the desired |alignment|. |alignment| must be a power of 2. -uint8_t* AlignAddr(uint8_t* const addr, const size_t alignment) { - const auto value = reinterpret_cast(addr); - return reinterpret_cast(Align(value, alignment)); -} - // Libgav1 frame buffer callbacks return 0 on success, -1 on failure. int Libgav1OnFrameBufferSizeChanged(void* /*callback_private_data*/, @@ -351,95 +338,31 @@ int Libgav1GetFrameBuffer(void* callback_private_data, int bitdepth, int top_border, int bottom_border, int stride_alignment, libgav1::FrameBuffer2* frame_buffer) { - bool is_monochrome = false; - int8_t subsampling_x = 1; - int8_t subsampling_y = 1; - switch (image_format) { - case libgav1::kImageFormatYuv420: - break; - case libgav1::kImageFormatYuv422: - subsampling_y = 0; - break; - case libgav1::kImageFormatYuv444: - subsampling_x = subsampling_y = 0; - break; - default: - // image_format is libgav1::kImageFormatMonochrome400. (AV1 has only four - // image formats, hardcoded in the spec). - is_monochrome = true; - break; - } - - // Calculate y_stride (in bytes). It is padded to a multiple of - // |stride_alignment| bytes. - int y_stride = width + left_border + right_border; - if (bitdepth > 8) y_stride *= sizeof(uint16_t); - y_stride = Align(y_stride, stride_alignment); - // Size of the Y plane in bytes. - const uint64_t y_plane_size = - (height + top_border + bottom_border) * static_cast(y_stride) + - (stride_alignment - 1); - - const int uv_width = is_monochrome ? 0 : width >> subsampling_x; - const int uv_height = is_monochrome ? 0 : height >> subsampling_y; - const int uv_left_border = is_monochrome ? 0 : left_border >> subsampling_x; - const int uv_right_border = is_monochrome ? 0 : right_border >> subsampling_x; - const int uv_top_border = is_monochrome ? 0 : top_border >> subsampling_y; - const int uv_bottom_border = - is_monochrome ? 0 : bottom_border >> subsampling_y; - - // Calculate uv_stride (in bytes). It is padded to a multiple of - // |stride_alignment| bytes. - int uv_stride = uv_width + uv_left_border + uv_right_border; - if (bitdepth > 8) uv_stride *= sizeof(uint16_t); - uv_stride = Align(uv_stride, stride_alignment); - // Size of the U or V plane in bytes. - const uint64_t uv_plane_size = - is_monochrome ? 0 - : (uv_height + uv_top_border + uv_bottom_border) * - static_cast(uv_stride) + - (stride_alignment - 1); - - // Check if it is safe to cast y_plane_size and uv_plane_size to size_t. - if (y_plane_size > SIZE_MAX || uv_plane_size > SIZE_MAX) { - return -1; - } + libgav1::FrameBufferInfo info; + Libgav1StatusCode status = libgav1::ComputeFrameBufferInfo( + bitdepth, image_format, width, height, left_border, right_border, + top_border, bottom_border, stride_alignment, &info); + if (status != kLibgav1StatusOk) return -1; JniContext* const context = static_cast(callback_private_data); JniFrameBuffer* jni_buffer; context->jni_status_code = context->buffer_manager.GetBuffer( - static_cast(y_plane_size), static_cast(uv_plane_size), - &jni_buffer); + info.y_buffer_size, info.uv_buffer_size, &jni_buffer); if (context->jni_status_code != kJniStatusOk) { LOGE("%s", GetJniErrorMessage(context->jni_status_code)); return -1; } uint8_t* const y_buffer = jni_buffer->RawBuffer(0); - uint8_t* const u_buffer = !is_monochrome ? jni_buffer->RawBuffer(1) : nullptr; - uint8_t* const v_buffer = !is_monochrome ? jni_buffer->RawBuffer(2) : nullptr; - - int left_border_bytes = left_border; - int uv_left_border_bytes = uv_left_border; - if (bitdepth > 8) { - left_border_bytes *= sizeof(uint16_t); - uv_left_border_bytes *= sizeof(uint16_t); - } - frame_buffer->plane[0] = AlignAddr( - y_buffer + (top_border * y_stride) + left_border_bytes, stride_alignment); - frame_buffer->plane[1] = - AlignAddr(u_buffer + (uv_top_border * uv_stride) + uv_left_border_bytes, - stride_alignment); - frame_buffer->plane[2] = - AlignAddr(v_buffer + (uv_top_border * uv_stride) + uv_left_border_bytes, - stride_alignment); - - frame_buffer->stride[0] = y_stride; - frame_buffer->stride[1] = frame_buffer->stride[2] = uv_stride; - - frame_buffer->private_data = reinterpret_cast(jni_buffer->Id()); - - return 0; + uint8_t* const u_buffer = + (info.uv_buffer_size != 0) ? jni_buffer->RawBuffer(1) : nullptr; + uint8_t* const v_buffer = + (info.uv_buffer_size != 0) ? jni_buffer->RawBuffer(2) : nullptr; + + status = libgav1::SetFrameBuffer(&info, y_buffer, u_buffer, v_buffer, + reinterpret_cast(jni_buffer->Id()), + frame_buffer); + return (status == kLibgav1StatusOk) ? 0 : -1; } void Libgav1ReleaseFrameBuffer(void* callback_private_data, @@ -454,6 +377,8 @@ void Libgav1ReleaseFrameBuffer(void* callback_private_data, } } +constexpr int AlignTo16(int value) { return (value + 15) & (~15); } + void CopyPlane(const uint8_t* source, int source_stride, uint8_t* destination, int destination_stride, int width, int height) { while (height--) { @@ -794,7 +719,7 @@ DECODER_FUNC(jint, gav1RenderFrame, jlong jContext, jobject jSurface, const int32_t native_window_buffer_uv_height = (native_window_buffer.height + 1) / 2; const int native_window_buffer_uv_stride = - Align(native_window_buffer.stride / 2, 16); + AlignTo16(native_window_buffer.stride / 2); // TODO(b/140606738): Handle monochrome videos. From 75bd4ebc1800e5960e8badfc0e7e18d1e97821a3 Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 17 Feb 2020 11:22:38 +0000 Subject: [PATCH 08/59] Use &id_ as buffer_private_data for libgav1. This avoids the issue of whether it is defined behaviour to cast an arbitrary int (or even intptr_t) value to a void* pointer. This is the original approach used before commit 0915998add5918214fa0282a69b50a159168a6d5. PiperOrigin-RevId: 295552115 --- extensions/av1/src/main/jni/gav1_jni.cc | 34 ++++++++++++------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/extensions/av1/src/main/jni/gav1_jni.cc b/extensions/av1/src/main/jni/gav1_jni.cc index 4ce614a3d97..5be4f5b923b 100644 --- a/extensions/av1/src/main/jni/gav1_jni.cc +++ b/extensions/av1/src/main/jni/gav1_jni.cc @@ -27,8 +27,6 @@ #endif // CPU_FEATURES_COMPILED_ANY_ARM_NEON #include -#include -#include #include #include #include // NOLINT @@ -134,6 +132,12 @@ class JniFrameBuffer { } } + // Not copyable or movable. + JniFrameBuffer(const JniFrameBuffer&) = delete; + JniFrameBuffer(JniFrameBuffer&&) = delete; + JniFrameBuffer& operator=(const JniFrameBuffer&) = delete; + JniFrameBuffer& operator=(JniFrameBuffer&&) = delete; + void SetFrameData(const libgav1::DecoderBuffer& decoder_buffer) { for (int plane_index = kPlaneY; plane_index < decoder_buffer.NumPlanes(); plane_index++) { @@ -162,7 +166,7 @@ class JniFrameBuffer { bool InUse() const { return reference_count_ != 0; } uint8_t* RawBuffer(int plane_index) const { return raw_buffer_[plane_index]; } - int Id() const { return id_; } + void* BufferPrivateData() const { return const_cast(&id_); } // Attempts to reallocate data planes if the existing ones don't have enough // capacity. Returns true if the allocation was successful or wasn't needed, @@ -359,19 +363,17 @@ int Libgav1GetFrameBuffer(void* callback_private_data, int bitdepth, uint8_t* const v_buffer = (info.uv_buffer_size != 0) ? jni_buffer->RawBuffer(2) : nullptr; - status = libgav1::SetFrameBuffer(&info, y_buffer, u_buffer, v_buffer, - reinterpret_cast(jni_buffer->Id()), - frame_buffer); + status = + libgav1::SetFrameBuffer(&info, y_buffer, u_buffer, v_buffer, + jni_buffer->BufferPrivateData(), frame_buffer); return (status == kLibgav1StatusOk) ? 0 : -1; } void Libgav1ReleaseFrameBuffer(void* callback_private_data, void* buffer_private_data) { JniContext* const context = static_cast(callback_private_data); - const intptr_t buffer_id = reinterpret_cast(buffer_private_data); - assert(buffer_id <= INT_MAX); - context->jni_status_code = - context->buffer_manager.ReleaseBuffer(static_cast(buffer_id)); + const int buffer_id = *static_cast(buffer_private_data); + context->jni_status_code = context->buffer_manager.ReleaseBuffer(buffer_id); if (context->jni_status_code != kJniStatusOk) { LOGE("%s", GetJniErrorMessage(context->jni_status_code)); } @@ -655,12 +657,11 @@ DECODER_FUNC(jint, gav1GetFrame, jlong jContext, jobject jOutputBuffer, return kStatusError; } - const intptr_t buffer_id = - reinterpret_cast(decoder_buffer->buffer_private_data); - assert(buffer_id <= INT_MAX); - context->buffer_manager.AddBufferReference(static_cast(buffer_id)); + const int buffer_id = + *static_cast(decoder_buffer->buffer_private_data); + context->buffer_manager.AddBufferReference(buffer_id); JniFrameBuffer* const jni_buffer = - context->buffer_manager.GetBuffer(static_cast(buffer_id)); + context->buffer_manager.GetBuffer(buffer_id); jni_buffer->SetFrameData(*decoder_buffer); env->CallVoidMethod(jOutputBuffer, context->init_for_private_frame_method, decoder_buffer->displayed_width[kPlaneY], @@ -669,8 +670,7 @@ DECODER_FUNC(jint, gav1GetFrame, jlong jContext, jobject jOutputBuffer, // Exception is thrown in Java when returning from the native call. return kStatusError; } - env->SetIntField(jOutputBuffer, context->decoder_private_field, - static_cast(buffer_id)); + env->SetIntField(jOutputBuffer, context->decoder_private_field, buffer_id); } return kStatusOk; From 908a76e8515073c721eeffb1205b5799af9f2efa Mon Sep 17 00:00:00 2001 From: olly Date: Wed, 19 Feb 2020 16:09:10 +0000 Subject: [PATCH 09/59] Change libgav1's frame buffer callback API. 1. Have frame buffer callbacks return Libgav1StatusCode instead of int. The 0 (success), -1 (failure) return value convention is less obvious. Note: The callers of frame buffer callbacks, BufferPool::OnFrameBufferSizeChanged() and YuvBuffer::Realloc(), currently return bool, so more work is needed to propagate the frame buffer callbacks' Libgav1StatusCode return value to the Decoder API. 2. Allow the FrameBufferSizeChangedCallback to be omitted if the frame buffer size information is not useful to the application. 3. Remove the old (version 1) frame buffer callback API. Remove the frame buffer callback adaptor. frame_buffer2.h is renamed frame_buffer.h. Libgav1FrameBuffer2 is renamed Libgav1FrameBuffer. GetFrameBufferCallback2 and ReleaseFrameBufferCallback2 are renamed GetFrameBufferCallback and ReleaseFrameBufferCallback. PiperOrigin-RevId: 295971183 --- extensions/av1/src/main/jni/gav1_jni.cc | 38 +++++++------------------ 1 file changed, 11 insertions(+), 27 deletions(-) diff --git a/extensions/av1/src/main/jni/gav1_jni.cc b/extensions/av1/src/main/jni/gav1_jni.cc index 5be4f5b923b..e3fb890c326 100644 --- a/extensions/av1/src/main/jni/gav1_jni.cc +++ b/extensions/av1/src/main/jni/gav1_jni.cc @@ -322,31 +322,18 @@ struct JniContext { JniStatusCode jni_status_code = kJniStatusOk; }; -// Libgav1 frame buffer callbacks return 0 on success, -1 on failure. - -int Libgav1OnFrameBufferSizeChanged(void* /*callback_private_data*/, - int /*bitdepth*/, - libgav1::ImageFormat /*image_format*/, - int /*width*/, int /*height*/, - int /*left_border*/, int /*right_border*/, - int /*top_border*/, int /*bottom_border*/, - int /*stride_alignment*/) { - // The libgav1 decoder calls this callback to provide information on the - // subsequent frames in the video. JniBufferManager ignores this information. - return 0; -} - -int Libgav1GetFrameBuffer(void* callback_private_data, int bitdepth, - libgav1::ImageFormat image_format, int width, - int height, int left_border, int right_border, - int top_border, int bottom_border, - int stride_alignment, - libgav1::FrameBuffer2* frame_buffer) { +Libgav1StatusCode Libgav1GetFrameBuffer(void* callback_private_data, + int bitdepth, + libgav1::ImageFormat image_format, + int width, int height, int left_border, + int right_border, int top_border, + int bottom_border, int stride_alignment, + libgav1::FrameBuffer* frame_buffer) { libgav1::FrameBufferInfo info; Libgav1StatusCode status = libgav1::ComputeFrameBufferInfo( bitdepth, image_format, width, height, left_border, right_border, top_border, bottom_border, stride_alignment, &info); - if (status != kLibgav1StatusOk) return -1; + if (status != kLibgav1StatusOk) return status; JniContext* const context = static_cast(callback_private_data); JniFrameBuffer* jni_buffer; @@ -354,7 +341,7 @@ int Libgav1GetFrameBuffer(void* callback_private_data, int bitdepth, info.y_buffer_size, info.uv_buffer_size, &jni_buffer); if (context->jni_status_code != kJniStatusOk) { LOGE("%s", GetJniErrorMessage(context->jni_status_code)); - return -1; + return kLibgav1StatusOutOfMemory; } uint8_t* const y_buffer = jni_buffer->RawBuffer(0); @@ -363,10 +350,8 @@ int Libgav1GetFrameBuffer(void* callback_private_data, int bitdepth, uint8_t* const v_buffer = (info.uv_buffer_size != 0) ? jni_buffer->RawBuffer(2) : nullptr; - status = - libgav1::SetFrameBuffer(&info, y_buffer, u_buffer, v_buffer, - jni_buffer->BufferPrivateData(), frame_buffer); - return (status == kLibgav1StatusOk) ? 0 : -1; + return libgav1::SetFrameBuffer(&info, y_buffer, u_buffer, v_buffer, + jni_buffer->BufferPrivateData(), frame_buffer); } void Libgav1ReleaseFrameBuffer(void* callback_private_data, @@ -546,7 +531,6 @@ DECODER_FUNC(jlong, gav1Init, jint threads) { libgav1::DecoderSettings settings; settings.threads = threads; - settings.on_frame_buffer_size_changed = Libgav1OnFrameBufferSizeChanged; settings.get_frame_buffer = Libgav1GetFrameBuffer; settings.release_frame_buffer = Libgav1ReleaseFrameBuffer; settings.callback_private_data = context; From 8591e69b6a40162ffc801a1569549ca826606cc5 Mon Sep 17 00:00:00 2001 From: Oliver Woodman Date: Mon, 30 Mar 2020 13:46:24 +0100 Subject: [PATCH 10/59] Remove duplicate SCTE-35 format and add sample to TsExtractorTest It's not clear why we're currently outputting the format in both init() and consume() - it seems likely that this was accidentally introduced in when we started outputting the format in consume() but didn't remove it from init(). --- .../exoplayer2/extractor/ts/SpliceInfoSectionReader.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/SpliceInfoSectionReader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/SpliceInfoSectionReader.java index 27838d4c257..ab674396076 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/SpliceInfoSectionReader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/SpliceInfoSectionReader.java @@ -38,8 +38,6 @@ public void init(TimestampAdjuster timestampAdjuster, ExtractorOutput extractorO this.timestampAdjuster = timestampAdjuster; idGenerator.generateNewId(); output = extractorOutput.track(idGenerator.getTrackId(), C.TRACK_TYPE_METADATA); - output.format(Format.createSampleFormat(idGenerator.getFormatId(), MimeTypes.APPLICATION_SCTE35, - null, Format.NO_VALUE, null)); } @Override From 265670cf93fa9d688df4db55a296214ad9630501 Mon Sep 17 00:00:00 2001 From: vigneshv Date: Thu, 20 Feb 2020 20:52:37 +0000 Subject: [PATCH 11/59] Add ReleaseInputBuffer callback The release_input_buffer callback will be called when the library is done consuming an "input buffer". The buffer passed into EnqueueFrame must be kept valid until this callback is called. If frame parallel is false, then this callback can be nullptr (in this case the buffer has to be kept valid until the next call to DequeueFrame). If frame parallel is true, this callback cannot be nullptr. PiperOrigin-RevId: 296276083 --- extensions/av1/src/main/jni/gav1_jni.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions/av1/src/main/jni/gav1_jni.cc b/extensions/av1/src/main/jni/gav1_jni.cc index e3fb890c326..e0cef86d227 100644 --- a/extensions/av1/src/main/jni/gav1_jni.cc +++ b/extensions/av1/src/main/jni/gav1_jni.cc @@ -567,7 +567,8 @@ DECODER_FUNC(jint, gav1Decode, jlong jContext, jobject encodedData, const uint8_t* const buffer = reinterpret_cast( env->GetDirectBufferAddress(encodedData)); context->libgav1_status_code = - context->decoder.EnqueueFrame(buffer, length, /*user_private_data=*/0); + context->decoder.EnqueueFrame(buffer, length, /*user_private_data=*/0, + /*buffer_private_data=*/nullptr); if (context->libgav1_status_code != kLibgav1StatusOk) { return kStatusError; } From c3f9f0e5fec9735d36dcc83afc8faf03325121d7 Mon Sep 17 00:00:00 2001 From: Oliver Woodman Date: Tue, 25 Feb 2020 21:24:27 +0000 Subject: [PATCH 12/59] Merge pull request #7010 from dbrain:fix_nullability PiperOrigin-RevId: 297187116 --- .../exoplayer2/offline/DownloadHelper.java | 26 +++++++++---------- .../exoplayer2/offline/DownloadService.java | 12 ++++++--- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadHelper.java b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadHelper.java index d176b1905c6..6707c1e4960 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadHelper.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadHelper.java @@ -245,8 +245,8 @@ public static DownloadHelper forDash( * @param dataSourceFactory A {@link DataSource.Factory} used to load the manifest. * @param renderersFactory A {@link RenderersFactory} creating the renderers for which tracks are * selected. - * @param drmSessionManager An optional {@link DrmSessionManager} used by the renderers created by - * {@code renderersFactory}. + * @param drmSessionManager An optional {@link DrmSessionManager}. Used to help determine which + * tracks can be selected. * @param trackSelectorParameters {@link DefaultTrackSelector.Parameters} for selecting tracks for * downloading. * @return A {@link DownloadHelper} for DASH streams. @@ -315,8 +315,8 @@ public static DownloadHelper forHls( * @param dataSourceFactory A {@link DataSource.Factory} used to load the playlist. * @param renderersFactory A {@link RenderersFactory} creating the renderers for which tracks are * selected. - * @param drmSessionManager An optional {@link DrmSessionManager} used by the renderers created by - * {@code renderersFactory}. + * @param drmSessionManager An optional {@link DrmSessionManager}. Used to help determine which + * tracks can be selected. * @param trackSelectorParameters {@link DefaultTrackSelector.Parameters} for selecting tracks for * downloading. * @return A {@link DownloadHelper} for HLS streams. @@ -385,8 +385,8 @@ public static DownloadHelper forSmoothStreaming( * @param dataSourceFactory A {@link DataSource.Factory} used to load the manifest. * @param renderersFactory A {@link RenderersFactory} creating the renderers for which tracks are * selected. - * @param drmSessionManager An optional {@link DrmSessionManager} used by the renderers created by - * {@code renderersFactory}. + * @param drmSessionManager An optional {@link DrmSessionManager}. Used to help determine which + * tracks can be selected. * @param trackSelectorParameters {@link DefaultTrackSelector.Parameters} for selecting tracks for * downloading. * @return A {@link DownloadHelper} for SmoothStreaming streams. @@ -414,27 +414,27 @@ public static DownloadHelper forSmoothStreaming( /** * Equivalent to {@link #createMediaSource(DownloadRequest, Factory, DrmSessionManager) - * createMediaSource(downloadRequest, dataSourceFactory, - * DrmSessionManager.getDummyDrmSessionManager())}. + * createMediaSource(downloadRequest, dataSourceFactory, null)}. */ public static MediaSource createMediaSource( DownloadRequest downloadRequest, DataSource.Factory dataSourceFactory) { - return createMediaSource( - downloadRequest, dataSourceFactory, DrmSessionManager.getDummyDrmSessionManager()); + return createMediaSource(downloadRequest, dataSourceFactory, /* drmSessionManager= */ null); } /** - * Utility method to create a MediaSource which only contains the tracks defined in {@code + * Utility method to create a {@link MediaSource} that only exposes the tracks defined in {@code * downloadRequest}. * * @param downloadRequest A {@link DownloadRequest}. * @param dataSourceFactory A factory for {@link DataSource}s to read the media. - * @return A MediaSource which only contains the tracks defined in {@code downloadRequest}. + * @param drmSessionManager An optional {@link DrmSessionManager} to be passed to the {@link + * MediaSource}. + * @return A {@link MediaSource} that only exposes the tracks defined in {@code downloadRequest}. */ public static MediaSource createMediaSource( DownloadRequest downloadRequest, DataSource.Factory dataSourceFactory, - DrmSessionManager drmSessionManager) { + @Nullable DrmSessionManager drmSessionManager) { @Nullable Constructor constructor; switch (downloadRequest.type) { case DownloadRequest.TYPE_DASH: diff --git a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadService.java b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadService.java index b1ab5ac7c62..819478b80e6 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadService.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadService.java @@ -595,7 +595,7 @@ public void onCreate() { } @Override - public int onStartCommand(Intent intent, int flags, int startId) { + public int onStartCommand(@Nullable Intent intent, int flags, int startId) { lastStartId = startId; taskRemoved = false; @Nullable String intentAction = null; @@ -617,7 +617,9 @@ public int onStartCommand(Intent intent, int flags, int startId) { // Do nothing. break; case ACTION_ADD_DOWNLOAD: - @Nullable DownloadRequest downloadRequest = intent.getParcelableExtra(KEY_DOWNLOAD_REQUEST); + @Nullable + DownloadRequest downloadRequest = + Assertions.checkNotNull(intent).getParcelableExtra(KEY_DOWNLOAD_REQUEST); if (downloadRequest == null) { Log.e(TAG, "Ignored ADD_DOWNLOAD: Missing " + KEY_DOWNLOAD_REQUEST + " extra"); } else { @@ -642,7 +644,7 @@ public int onStartCommand(Intent intent, int flags, int startId) { downloadManager.pauseDownloads(); break; case ACTION_SET_STOP_REASON: - if (!intent.hasExtra(KEY_STOP_REASON)) { + if (!Assertions.checkNotNull(intent).hasExtra(KEY_STOP_REASON)) { Log.e(TAG, "Ignored SET_STOP_REASON: Missing " + KEY_STOP_REASON + " extra"); } else { int stopReason = intent.getIntExtra(KEY_STOP_REASON, /* defaultValue= */ 0); @@ -650,7 +652,9 @@ public int onStartCommand(Intent intent, int flags, int startId) { } break; case ACTION_SET_REQUIREMENTS: - @Nullable Requirements requirements = intent.getParcelableExtra(KEY_REQUIREMENTS); + @Nullable + Requirements requirements = + Assertions.checkNotNull(intent).getParcelableExtra(KEY_REQUIREMENTS); if (requirements == null) { Log.e(TAG, "Ignored SET_REQUIREMENTS: Missing " + KEY_REQUIREMENTS + " extra"); } else { From e2a6775ea44935c8fe78fc49f6698989e29c5454 Mon Sep 17 00:00:00 2001 From: Oliver Woodman Date: Tue, 10 Mar 2020 10:17:13 +0000 Subject: [PATCH 13/59] Merge pull request #6999 from xufuji456:dev-v2 PiperOrigin-RevId: 298544278 --- .../exoplayer2/ext/ffmpeg/FfmpegLibrary.java | 2 +- extensions/ffmpeg/src/main/jni/Android.mk | 7 +---- .../ffmpeg/src/main/jni/build_ffmpeg.sh | 2 +- extensions/ffmpeg/src/main/jni/ffmpeg_jni.cc | 29 +++++++++---------- 4 files changed, 17 insertions(+), 23 deletions(-) diff --git a/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegLibrary.java b/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegLibrary.java index 46398512630..dc72e12e65b 100644 --- a/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegLibrary.java +++ b/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegLibrary.java @@ -33,7 +33,7 @@ public final class FfmpegLibrary { private static final String TAG = "FfmpegLibrary"; private static final LibraryLoader LOADER = - new LibraryLoader("avutil", "avresample", "swresample", "avcodec", "ffmpeg"); + new LibraryLoader("avutil", "swresample", "avcodec", "ffmpeg"); private FfmpegLibrary() {} diff --git a/extensions/ffmpeg/src/main/jni/Android.mk b/extensions/ffmpeg/src/main/jni/Android.mk index 22a4edcdae2..bcaf12cd119 100644 --- a/extensions/ffmpeg/src/main/jni/Android.mk +++ b/extensions/ffmpeg/src/main/jni/Android.mk @@ -21,11 +21,6 @@ LOCAL_MODULE := libavcodec LOCAL_SRC_FILES := ffmpeg/android-libs/$(TARGET_ARCH_ABI)/$(LOCAL_MODULE).so include $(PREBUILT_SHARED_LIBRARY) -include $(CLEAR_VARS) -LOCAL_MODULE := libavresample -LOCAL_SRC_FILES := ffmpeg/android-libs/$(TARGET_ARCH_ABI)/$(LOCAL_MODULE).so -include $(PREBUILT_SHARED_LIBRARY) - include $(CLEAR_VARS) LOCAL_MODULE := libswresample LOCAL_SRC_FILES := ffmpeg/android-libs/$(TARGET_ARCH_ABI)/$(LOCAL_MODULE).so @@ -40,6 +35,6 @@ include $(CLEAR_VARS) LOCAL_MODULE := ffmpeg LOCAL_SRC_FILES := ffmpeg_jni.cc LOCAL_C_INCLUDES := ffmpeg -LOCAL_SHARED_LIBRARIES := libavcodec libavresample libswresample libavutil +LOCAL_SHARED_LIBRARIES := libavcodec libswresample libavutil LOCAL_LDLIBS := -Lffmpeg/android-libs/$(TARGET_ARCH_ABI) -llog include $(BUILD_SHARED_LIBRARY) diff --git a/extensions/ffmpeg/src/main/jni/build_ffmpeg.sh b/extensions/ffmpeg/src/main/jni/build_ffmpeg.sh index a76fa0e5897..d6db5fc1722 100755 --- a/extensions/ffmpeg/src/main/jni/build_ffmpeg.sh +++ b/extensions/ffmpeg/src/main/jni/build_ffmpeg.sh @@ -32,7 +32,7 @@ COMMON_OPTIONS=" --disable-postproc --disable-avfilter --disable-symver - --enable-avresample + --disable-avresample --enable-swresample " TOOLCHAIN_PREFIX="${NDK_PATH}/toolchains/llvm/prebuilt/${HOST_PLATFORM}/bin" diff --git a/extensions/ffmpeg/src/main/jni/ffmpeg_jni.cc b/extensions/ffmpeg/src/main/jni/ffmpeg_jni.cc index dcd4560e4ab..400039af893 100644 --- a/extensions/ffmpeg/src/main/jni/ffmpeg_jni.cc +++ b/extensions/ffmpeg/src/main/jni/ffmpeg_jni.cc @@ -26,10 +26,10 @@ extern "C" { #include #endif #include -#include #include #include #include +#include } #define LOG_TAG "ffmpeg_jni" @@ -289,11 +289,11 @@ int decodePacket(AVCodecContext *context, AVPacket *packet, int sampleCount = frame->nb_samples; int dataSize = av_samples_get_buffer_size(NULL, channelCount, sampleCount, sampleFormat, 1); - AVAudioResampleContext *resampleContext; + SwrContext *resampleContext; if (context->opaque) { - resampleContext = (AVAudioResampleContext *) context->opaque; + resampleContext = (SwrContext *)context->opaque; } else { - resampleContext = avresample_alloc_context(); + resampleContext = swr_alloc(); av_opt_set_int(resampleContext, "in_channel_layout", channelLayout, 0); av_opt_set_int(resampleContext, "out_channel_layout", channelLayout, 0); av_opt_set_int(resampleContext, "in_sample_rate", sampleRate, 0); @@ -302,9 +302,9 @@ int decodePacket(AVCodecContext *context, AVPacket *packet, // The output format is always the requested format. av_opt_set_int(resampleContext, "out_sample_fmt", context->request_sample_fmt, 0); - result = avresample_open(resampleContext); + result = swr_init(resampleContext); if (result < 0) { - logError("avresample_open", result); + logError("swr_init", result); av_frame_free(&frame); return -1; } @@ -312,7 +312,7 @@ int decodePacket(AVCodecContext *context, AVPacket *packet, } int inSampleSize = av_get_bytes_per_sample(sampleFormat); int outSampleSize = av_get_bytes_per_sample(context->request_sample_fmt); - int outSamples = avresample_get_out_samples(resampleContext, sampleCount); + int outSamples = swr_get_out_samples(resampleContext, sampleCount); int bufferOutSize = outSampleSize * channelCount * outSamples; if (outSize + bufferOutSize > outputSize) { LOGE("Output buffer size (%d) too small for output data (%d).", @@ -320,15 +320,14 @@ int decodePacket(AVCodecContext *context, AVPacket *packet, av_frame_free(&frame); return -1; } - result = avresample_convert(resampleContext, &outputBuffer, bufferOutSize, - outSamples, frame->data, frame->linesize[0], - sampleCount); + result = swr_convert(resampleContext, &outputBuffer, bufferOutSize, + (const uint8_t **)frame->data, frame->nb_samples); av_frame_free(&frame); if (result < 0) { - logError("avresample_convert", result); + logError("swr_convert", result); return result; } - int available = avresample_available(resampleContext); + int available = swr_get_out_samples(resampleContext, 0); if (available != 0) { LOGE("Expected no samples remaining after resampling, but found %d.", available); @@ -351,9 +350,9 @@ void releaseContext(AVCodecContext *context) { if (!context) { return; } - AVAudioResampleContext *resampleContext; - if ((resampleContext = (AVAudioResampleContext *) context->opaque)) { - avresample_free(&resampleContext); + SwrContext *swrContext; + if ((swrContext = (SwrContext *)context->opaque)) { + swr_free(&swrContext); context->opaque = NULL; } avcodec_free_context(&context); From 4e6383aeae900fb7504035610020b38dfe3c33ab Mon Sep 17 00:00:00 2001 From: Oliver Woodman Date: Tue, 10 Mar 2020 10:20:37 +0000 Subject: [PATCH 14/59] Merge pull request #7051 from Cizor:dev-v2 PiperOrigin-RevId: 299357049 --- .../google/android/exoplayer2/video/MediaCodecVideoRenderer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java index 4e72a1b3d73..25dc09be816 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java @@ -1000,6 +1000,7 @@ protected void onProcessedTunneledBuffer(long presentationTimeUs) { processOutputFormat(getCodec(), format.width, format.height); } maybeNotifyVideoSizeChanged(); + decoderCounters.renderedOutputBufferCount++; maybeNotifyRenderedFirstFrame(); onProcessedOutputBuffer(presentationTimeUs); } From 40d5db04607e8a91449608a08c8c25fa6ee3eedb Mon Sep 17 00:00:00 2001 From: krocard Date: Mon, 9 Mar 2020 16:43:38 +0000 Subject: [PATCH 15/59] Add support for x86_64 for the ffmpeg extension Requested by https://github.com/google/ExoPlayer/issues/7058. Additionally move one of the common option in COMMON_OPTIONS. PiperOrigin-RevId: 299862479 --- RELEASENOTES.md | 1 + extensions/ffmpeg/src/main/jni/build_ffmpeg.sh | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 847746d425e..bc862102d15 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -5,6 +5,7 @@ * Text: Catch-and-log all fatal exceptions in `TextRenderer` instead of re-throwing, allowing playback to continue even if subtitles fail ([#6885](https://github.com/google/ExoPlayer/issues/6885)). +* FFmpeg extension: Add support for x86_64. ### 2.11.3 (2020-02-19) ### diff --git a/extensions/ffmpeg/src/main/jni/build_ffmpeg.sh b/extensions/ffmpeg/src/main/jni/build_ffmpeg.sh index d6db5fc1722..b7d8f0eb7fe 100755 --- a/extensions/ffmpeg/src/main/jni/build_ffmpeg.sh +++ b/extensions/ffmpeg/src/main/jni/build_ffmpeg.sh @@ -34,6 +34,7 @@ COMMON_OPTIONS=" --disable-symver --disable-avresample --enable-swresample + --extra-ldexeflags=-pie " TOOLCHAIN_PREFIX="${NDK_PATH}/toolchains/llvm/prebuilt/${HOST_PLATFORM}/bin" for decoder in "${ENABLED_DECODERS[@]}" @@ -53,7 +54,6 @@ git checkout release/4.2 --strip="${TOOLCHAIN_PREFIX}/arm-linux-androideabi-strip" \ --extra-cflags="-march=armv7-a -mfloat-abi=softfp" \ --extra-ldflags="-Wl,--fix-cortex-a8" \ - --extra-ldexeflags=-pie \ ${COMMON_OPTIONS} make -j4 make install-libs @@ -65,7 +65,6 @@ make clean --cross-prefix="${TOOLCHAIN_PREFIX}/aarch64-linux-android21-" \ --nm="${TOOLCHAIN_PREFIX}/aarch64-linux-android-nm" \ --strip="${TOOLCHAIN_PREFIX}/aarch64-linux-android-strip" \ - --extra-ldexeflags=-pie \ ${COMMON_OPTIONS} make -j4 make install-libs @@ -77,7 +76,18 @@ make clean --cross-prefix="${TOOLCHAIN_PREFIX}/i686-linux-android16-" \ --nm="${TOOLCHAIN_PREFIX}/i686-linux-android-nm" \ --strip="${TOOLCHAIN_PREFIX}/i686-linux-android-strip" \ - --extra-ldexeflags=-pie \ + --disable-asm \ + ${COMMON_OPTIONS} +make -j4 +make install-libs +make clean +./configure \ + --libdir=android-libs/x86_64 \ + --arch=x86_64 \ + --cpu=x86_64 \ + --cross-prefix="${TOOLCHAIN_PREFIX}/x86_64-linux-android16-" \ + --nm="${TOOLCHAIN_PREFIX}/x86_64-linux-android-nm" \ + --strip="${TOOLCHAIN_PREFIX}/x86_64-linux-android-strip" \ --disable-asm \ ${COMMON_OPTIONS} make -j4 From dca68b2198066da57633947c05e1ad7915cba3fa Mon Sep 17 00:00:00 2001 From: Oliver Woodman Date: Wed, 11 Mar 2020 16:27:47 +0000 Subject: [PATCH 16/59] Merge pull request #7064 from davibe:enhancement/6907 PiperOrigin-RevId: 300330109 --- RELEASENOTES.md | 2 ++ .../source/dash/DashMediaSource.java | 21 +++++++++++-------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index bc862102d15..543a7f83ef6 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -5,6 +5,8 @@ * Text: Catch-and-log all fatal exceptions in `TextRenderer` instead of re-throwing, allowing playback to continue even if subtitles fail ([#6885](https://github.com/google/ExoPlayer/issues/6885)). +* DASH: Update the manifest URI to avoid repeated HTTP redirects + ([#6907](https://github.com/google/ExoPlayer/issues/6907)). * FFmpeg extension: Add support for x86_64. ### 2.11.3 (2020-02-19) ### diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java index dfcd62b8b10..dcd4b15caed 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java @@ -807,15 +807,18 @@ protected void releaseSourceInternal() { manifestLoadPending &= manifest.dynamic; manifestLoadStartTimestampMs = elapsedRealtimeMs - loadDurationMs; manifestLoadEndTimestampMs = elapsedRealtimeMs; - if (manifest.location != null) { - synchronized (manifestUriLock) { - // This condition checks that replaceManifestUri wasn't called between the start and end of - // this load. If it was, we ignore the manifest location and prefer the manual replacement. - @SuppressWarnings("ReferenceEquality") - boolean isSameUriInstance = loadable.dataSpec.uri == manifestUri; - if (isSameUriInstance) { - manifestUri = manifest.location; - } + + synchronized (manifestUriLock) { + // Checks whether replaceManifestUri(Uri) was called to manually replace the URI between the + // start and end of this load. If it was then isSameUriInstance evaluates to false, and we + // prefer the manual replacement to one derived from the previous request. + @SuppressWarnings("ReferenceEquality") + boolean isSameUriInstance = loadable.dataSpec.uri == manifestUri; + if (isSameUriInstance) { + // Replace the manifest URI with one specified by a manifest Location element (if present), + // or with the final (possibly redirected) URI. This follows the recommendation in + // DASH-IF-IOP 4.3, section 3.2.15.3. See: https://dashif.org/docs/DASH-IF-IOP-v4.3.pdf. + manifestUri = manifest.location != null ? manifest.location : loadable.getUri(); } } From b2849fde3d6cab091fcf10608ef93a26870fc45e Mon Sep 17 00:00:00 2001 From: Oliver Woodman Date: Wed, 11 Mar 2020 16:27:36 +0000 Subject: [PATCH 17/59] Merge pull request #7057 from Chimerapps:dash_assetidentifier PiperOrigin-RevId: 300313753 --- RELEASENOTES.md | 6 +++-- .../dash/manifest/DashManifestParser.java | 20 ++++++++++++----- .../source/dash/manifest/Period.java | 22 ++++++++++++++++++- 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 543a7f83ef6..98de3a6255c 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -5,8 +5,10 @@ * Text: Catch-and-log all fatal exceptions in `TextRenderer` instead of re-throwing, allowing playback to continue even if subtitles fail ([#6885](https://github.com/google/ExoPlayer/issues/6885)). -* DASH: Update the manifest URI to avoid repeated HTTP redirects - ([#6907](https://github.com/google/ExoPlayer/issues/6907)). +* DASH: + * Update the manifest URI to avoid repeated HTTP redirects + ([#6907](https://github.com/google/ExoPlayer/issues/6907)). + * Parse period `AssetIdentifier` elements. * FFmpeg extension: Add support for x86_64. ### 2.11.3 (2020-02-19) ### diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java index b107be4794d..95129d68c45 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java @@ -222,10 +222,11 @@ protected UtcTimingElement buildUtcTimingElement(String schemeIdUri, String valu protected Pair parsePeriod(XmlPullParser xpp, String baseUrl, long defaultStartMs) throws XmlPullParserException, IOException { - String id = xpp.getAttributeValue(null, "id"); + @Nullable String id = xpp.getAttributeValue(null, "id"); long startMs = parseDuration(xpp, "start", defaultStartMs); long durationMs = parseDuration(xpp, "duration", C.TIME_UNSET); - SegmentBase segmentBase = null; + @Nullable SegmentBase segmentBase = null; + @Nullable Descriptor assetIdentifier = null; List adaptationSets = new ArrayList<>(); List eventStreams = new ArrayList<>(); boolean seenFirstBaseUrl = false; @@ -246,17 +247,24 @@ protected Pair parsePeriod(XmlPullParser xpp, String baseUrl, long segmentBase = parseSegmentList(xpp, null, durationMs); } else if (XmlPullParserUtil.isStartTag(xpp, "SegmentTemplate")) { segmentBase = parseSegmentTemplate(xpp, null, Collections.emptyList(), durationMs); + } else if (XmlPullParserUtil.isStartTag(xpp, "AssetIdentifier")) { + assetIdentifier = parseDescriptor(xpp, "AssetIdentifier"); } else { maybeSkipTag(xpp); } } while (!XmlPullParserUtil.isEndTag(xpp, "Period")); - return Pair.create(buildPeriod(id, startMs, adaptationSets, eventStreams), durationMs); + return Pair.create( + buildPeriod(id, startMs, adaptationSets, eventStreams, assetIdentifier), durationMs); } - protected Period buildPeriod(String id, long startMs, List adaptationSets, - List eventStreams) { - return new Period(id, startMs, adaptationSets, eventStreams); + protected Period buildPeriod( + @Nullable String id, + long startMs, + List adaptationSets, + List eventStreams, + @Nullable Descriptor assetIdentifier) { + return new Period(id, startMs, adaptationSets, eventStreams, assetIdentifier); } // AdaptationSet parsing. diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/Period.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/Period.java index 18614ca4b01..b472aed50cf 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/Period.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/Period.java @@ -45,13 +45,16 @@ public class Period { */ public final List eventStreams; + /** The asset identifier for this period, if one exists */ + @Nullable public final Descriptor assetIdentifier; + /** * @param id The period identifier. May be null. * @param startMs The start time of the period in milliseconds. * @param adaptationSets The adaptation sets belonging to the period. */ public Period(@Nullable String id, long startMs, List adaptationSets) { - this(id, startMs, adaptationSets, Collections.emptyList()); + this(id, startMs, adaptationSets, Collections.emptyList(), /* assetIdentifier= */ null); } /** @@ -62,10 +65,27 @@ public Period(@Nullable String id, long startMs, List adaptationS */ public Period(@Nullable String id, long startMs, List adaptationSets, List eventStreams) { + this(id, startMs, adaptationSets, eventStreams, /* assetIdentifier= */ null); + } + + /** + * @param id The period identifier. May be null. + * @param startMs The start time of the period in milliseconds. + * @param adaptationSets The adaptation sets belonging to the period. + * @param eventStreams The {@link EventStream}s belonging to the period. + * @param assetIdentifier The asset identifier for this period + */ + public Period( + @Nullable String id, + long startMs, + List adaptationSets, + List eventStreams, + @Nullable Descriptor assetIdentifier) { this.id = id; this.startMs = startMs; this.adaptationSets = Collections.unmodifiableList(adaptationSets); this.eventStreams = Collections.unmodifiableList(eventStreams); + this.assetIdentifier = assetIdentifier; } /** From 4750785f5abe64a3f7c71ca88a4db24a65765c15 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Fri, 13 Mar 2020 09:04:00 +0000 Subject: [PATCH 18/59] Add option for sensor rotation in 360 playbacks Issue: #6761 PiperOrigin-RevId: 300715682 --- RELEASENOTES.md | 3 ++ .../android/exoplayer2/ui/PlayerView.java | 27 ++++++++++++++ .../ui/spherical/SphericalGLSurfaceView.java | 35 +++++++++++++++---- library/ui/src/main/res/values/attrs.xml | 2 +- 4 files changed, 59 insertions(+), 8 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 98de3a6255c..c9b986f7314 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -5,6 +5,9 @@ * Text: Catch-and-log all fatal exceptions in `TextRenderer` instead of re-throwing, allowing playback to continue even if subtitles fail ([#6885](https://github.com/google/ExoPlayer/issues/6885)). +* UI: Add an option to set whether to use the orientation sensor for rotation + in spherical playbacks + ([#6761](https://github.com/google/ExoPlayer/issues/6761)). * DASH: * Update the manifest URI to avoid repeated HTTP redirects ([#6907](https://github.com/google/ExoPlayer/issues/6907)). diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerView.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerView.java index 03168643cf3..2eae9c1dde2 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerView.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerView.java @@ -143,6 +143,12 @@ *

  • Corresponding method: None *
  • Default: {@code surface_view} * + *
  • {@code use_sensor_rotation} - Whether to use the orientation sensor for rotation + * during spherical playbacks (if available). + *
      + *
    • Corresponding method: {@link #setUseSensorRotation(boolean)} + *
    • Default: {@code true} + *
    *
  • {@code shutter_background_color} - The background color of the {@code exo_shutter} * view. *