react-native-alpha-video 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +903 -2
- package/THIRD_PARTY_NOTICES.md +30 -0
- package/android/av1/build.gradle +67 -0
- package/android/av1/consumer-rules.pro +4 -0
- package/android/av1/src/main/cpp/Av1Codec_jni.cpp +8 -0
- package/android/av1/src/main/cpp/CMakeLists.txt +34 -0
- package/android/av1/src/main/java/dev/mediacache/alphavideo/av1/Av1Codec.java +21 -0
- package/android/build.gradle +85 -0
- package/android/consumer-rules.pro +2 -0
- package/android/gradle.properties +1 -0
- package/android/publish.gradle +82 -0
- package/android/settings.gradle +5 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/cpp/CMakeLists.txt +59 -0
- package/android/src/main/java/dev/mediacache/alphavideo/AlphaVideoCodecs.java +73 -0
- package/android/src/main/java/dev/mediacache/alphavideo/AlphaVideoView.java +1966 -0
- package/android/src/main/java/dev/mediacache/alphavideo/BufferedDualStreamPlayer.java +978 -0
- package/android/src/main/java/dev/mediacache/alphavideo/Fetcher.java +37 -0
- package/android/src/main/java/dev/mediacache/alphavideo/HttpFetcher.java +156 -0
- package/android/src/main/java/dev/mediacache/alphavideo/MediaCodecColor.java +47 -0
- package/android/src/main/java/dev/mediacache/alphavideo/MediaCodecDecoders.java +135 -0
- package/android/src/main/java/dev/mediacache/alphavideo/RangeSourceLink.java +54 -0
- package/android/src/main/java/dev/mediacache/alphavideo/SourceReader.java +174 -0
- package/android/src/main/java/dev/mediacache/alphavideo/SurfaceTextureVideoDecoder.java +802 -0
- package/android/src/main/java/dev/mediacache/alphavideo/WebMAlphaPlayerNative.java +95 -0
- package/android/src/main/java/dev/mediacache/alphavideo/WebMFrame.java +54 -0
- package/android/src/main/java/dev/mediacache/alphavideo/WebMSource.java +160 -0
- package/android/src/rangesource/java/dev/mediacache/alphavideo/AppDataSource.java +50 -0
- package/android/vpx/build.gradle +67 -0
- package/android/vpx/consumer-rules.pro +4 -0
- package/android/vpx/src/main/cpp/CMakeLists.txt +34 -0
- package/android/vpx/src/main/cpp/VpxCodec_jni.cpp +8 -0
- package/android/vpx/src/main/java/dev/mediacache/alphavideo/vpx/VpxCodec.java +21 -0
- package/codecs/av1/Dav1dDecoderPlugin.cpp +157 -0
- package/codecs/av1/Dav1dDecoderPlugin.h +17 -0
- package/codecs/vpx/VpxDecoderPlugin.cpp +103 -0
- package/codecs/vpx/VpxDecoderPlugin.h +17 -0
- package/cpp/AlphaDemuxer.cpp +984 -0
- package/cpp/AlphaDemuxer.h +123 -0
- package/cpp/AlphaVideoDecoderPlugin.h +84 -0
- package/cpp/Av1SequenceHeader.cpp +175 -0
- package/cpp/Av1SequenceHeader.h +25 -0
- package/cpp/ByteSource.cpp +116 -0
- package/cpp/ByteSource.h +59 -0
- package/cpp/RangeSourceClient.h +40 -0
- package/cpp/VideoDecoder.cpp +157 -0
- package/cpp/VideoDecoder.h +83 -0
- package/cpp/WebMAlphaPlayer.cpp +1666 -0
- package/cpp/WebMAlphaPlayer.h +389 -0
- package/cpp/WebMAlphaPlayer_jni.cpp +1380 -0
- package/cpp/YuvToRgb.cpp +39 -0
- package/cpp/YuvToRgb.h +40 -0
- package/ios/Sources/AlphaVideo/AlphaVideoCodecs.mm +63 -0
- package/ios/Sources/AlphaVideo/AlphaVideoInternal.h +70 -0
- package/ios/Sources/AlphaVideo/AlphaVideoPlayer.mm +994 -0
- package/ios/Sources/AlphaVideo/AlphaVideoSource.mm +476 -0
- package/ios/Sources/AlphaVideo/AlphaVideoView.mm +374 -0
- package/ios/Sources/AlphaVideo/include/AlphaVideo.h +303 -0
- package/ios/Sources/AlphaVideoAV1/AlphaVideoAV1Codec.mm +16 -0
- package/ios/Sources/AlphaVideoAV1/include/AlphaVideoAV1.h +18 -0
- package/ios/Sources/AlphaVideoVPX/AlphaVideoVPXCodec.mm +16 -0
- package/ios/Sources/AlphaVideoVPX/include/AlphaVideoVPX.h +17 -0
- package/lib/commonjs/AlphaVideoView.js +187 -0
- package/lib/commonjs/AlphaVideoView.js.map +1 -0
- package/lib/commonjs/AlphaVideoViewNativeComponent.js +47 -0
- package/lib/commonjs/AlphaVideoViewNativeComponent.js.map +1 -0
- package/lib/commonjs/AlphaVideoViewNativeComponent.web.js +11 -0
- package/lib/commonjs/AlphaVideoViewNativeComponent.web.js.map +1 -0
- package/lib/commonjs/NativeAlphaVideoModule.js +10 -0
- package/lib/commonjs/NativeAlphaVideoModule.js.map +1 -0
- package/lib/commonjs/NativeAlphaVideoModule.web.js +9 -0
- package/lib/commonjs/NativeAlphaVideoModule.web.js.map +1 -0
- package/lib/commonjs/WebPlayer.js +12 -0
- package/lib/commonjs/WebPlayer.js.map +1 -0
- package/lib/commonjs/WebPlayer.web.js +92 -0
- package/lib/commonjs/WebPlayer.web.js.map +1 -0
- package/lib/commonjs/index.js +124 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/module/AlphaVideoView.js +187 -0
- package/lib/module/AlphaVideoView.js.map +1 -0
- package/lib/module/AlphaVideoViewNativeComponent.js +47 -0
- package/lib/module/AlphaVideoViewNativeComponent.js.map +1 -0
- package/lib/module/AlphaVideoViewNativeComponent.web.js +11 -0
- package/lib/module/AlphaVideoViewNativeComponent.web.js.map +1 -0
- package/lib/module/NativeAlphaVideoModule.js +10 -0
- package/lib/module/NativeAlphaVideoModule.js.map +1 -0
- package/lib/module/NativeAlphaVideoModule.web.js +9 -0
- package/lib/module/NativeAlphaVideoModule.web.js.map +1 -0
- package/lib/module/WebPlayer.js +12 -0
- package/lib/module/WebPlayer.js.map +1 -0
- package/lib/module/WebPlayer.web.js +92 -0
- package/lib/module/WebPlayer.web.js.map +1 -0
- package/lib/module/index.js +124 -0
- package/lib/module/index.js.map +1 -0
- package/lib/typescript/react-native/src/AlphaVideoView.d.ts +189 -0
- package/lib/typescript/react-native/src/AlphaVideoView.d.ts.map +1 -0
- package/lib/typescript/react-native/src/AlphaVideoViewNativeComponent.d.ts +109 -0
- package/lib/typescript/react-native/src/AlphaVideoViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/react-native/src/AlphaVideoViewNativeComponent.web.d.ts +5 -0
- package/lib/typescript/react-native/src/AlphaVideoViewNativeComponent.web.d.ts.map +1 -0
- package/lib/typescript/react-native/src/NativeAlphaVideoModule.d.ts +21 -0
- package/lib/typescript/react-native/src/NativeAlphaVideoModule.d.ts.map +1 -0
- package/lib/typescript/react-native/src/NativeAlphaVideoModule.web.d.ts +3 -0
- package/lib/typescript/react-native/src/NativeAlphaVideoModule.web.d.ts.map +1 -0
- package/lib/typescript/react-native/src/WebPlayer.d.ts +3 -0
- package/lib/typescript/react-native/src/WebPlayer.d.ts.map +1 -0
- package/lib/typescript/react-native/src/WebPlayer.web.d.ts +9 -0
- package/lib/typescript/react-native/src/WebPlayer.web.d.ts.map +1 -0
- package/lib/typescript/react-native/src/index.d.ts +77 -0
- package/lib/typescript/react-native/src/index.d.ts.map +1 -0
- package/licenses/dav1d-COPYING.txt +23 -0
- package/licenses/libvpx-LICENSE.txt +31 -0
- package/licenses/libvpx-PATENTS.txt +23 -0
- package/licenses/libwebm-LICENSE.txt +30 -0
- package/licenses/libwebm-PATENTS.txt +23 -0
- package/package.json +150 -4
- package/prebuilt/android/arm64-v8a/lib/libdav1d.a +0 -0
- package/prebuilt/android/arm64-v8a/lib/libvpx.a +0 -0
- package/prebuilt/android/armeabi-v7a/lib/libdav1d.a +0 -0
- package/prebuilt/android/armeabi-v7a/lib/libvpx.a +0 -0
- package/prebuilt/android/x86_64/lib/libdav1d.a +0 -0
- package/prebuilt/android/x86_64/lib/libvpx.a +0 -0
- package/prebuilt/include/dav1d/common.h +94 -0
- package/prebuilt/include/dav1d/data.h +117 -0
- package/prebuilt/include/dav1d/dav1d.h +329 -0
- package/prebuilt/include/dav1d/headers.h +440 -0
- package/prebuilt/include/dav1d/picture.h +157 -0
- package/prebuilt/include/dav1d/version.h +50 -0
- package/prebuilt/include/vpx/vp8.h +136 -0
- package/prebuilt/include/vpx/vp8dx.h +228 -0
- package/prebuilt/include/vpx/vpx_codec.h +475 -0
- package/prebuilt/include/vpx/vpx_decoder.h +365 -0
- package/prebuilt/include/vpx/vpx_frame_buffer.h +83 -0
- package/prebuilt/include/vpx/vpx_image.h +209 -0
- package/prebuilt/include/vpx/vpx_integer.h +40 -0
- package/prebuilt/ios/lib/libdav1d.a +0 -0
- package/prebuilt/ios/lib/libvpx.a +0 -0
- package/prebuilt/ios-simulator/include/dav1d/common.h +94 -0
- package/prebuilt/ios-simulator/include/dav1d/data.h +117 -0
- package/prebuilt/ios-simulator/include/dav1d/dav1d.h +329 -0
- package/prebuilt/ios-simulator/include/dav1d/headers.h +440 -0
- package/prebuilt/ios-simulator/include/dav1d/picture.h +157 -0
- package/prebuilt/ios-simulator/include/dav1d/version.h +50 -0
- package/prebuilt/ios-simulator/include/vpx/vp8.h +136 -0
- package/prebuilt/ios-simulator/include/vpx/vp8dx.h +228 -0
- package/prebuilt/ios-simulator/include/vpx/vpx_codec.h +475 -0
- package/prebuilt/ios-simulator/include/vpx/vpx_decoder.h +365 -0
- package/prebuilt/ios-simulator/include/vpx/vpx_frame_buffer.h +83 -0
- package/prebuilt/ios-simulator/include/vpx/vpx_image.h +209 -0
- package/prebuilt/ios-simulator/include/vpx/vpx_integer.h +40 -0
- package/prebuilt/ios-simulator/lib/libdav1d.a +0 -0
- package/prebuilt/ios-simulator/lib/libvpx.a +0 -0
- package/react-native/android/CMakeLists.txt +14 -0
- package/react-native/android/build.gradle +110 -0
- package/react-native/android/src/main/AndroidManifest.xml +1 -0
- package/react-native/android/src/main/java/dev/mediacache/alphavideo/reactnative/AlphaVideoModule.java +102 -0
- package/react-native/android/src/main/java/dev/mediacache/alphavideo/reactnative/AlphaVideoPackage.java +50 -0
- package/react-native/android/src/main/java/dev/mediacache/alphavideo/reactnative/AlphaVideoViewManager.java +317 -0
- package/react-native/android/src/newarch/java/dev/mediacache/alphavideo/reactnative/AlphaVideoModuleSpec.java +10 -0
- package/react-native/android/src/newarch/java/dev/mediacache/alphavideo/reactnative/AlphaVideoViewManagerSpec.java +23 -0
- package/react-native/android/src/norangesource/java/dev/mediacache/alphavideo/AppDataSource.java +15 -0
- package/react-native/android/src/oldarch/java/dev/mediacache/alphavideo/reactnative/AlphaVideoModuleSpec.java +30 -0
- package/react-native/android/src/oldarch/java/dev/mediacache/alphavideo/reactnative/AlphaVideoViewManagerSpec.java +61 -0
- package/react-native/ios/RNAlphaVideoViewComponentView.mm +173 -0
- package/react-native/ios/RNAlphaVideoViewManager.mm +147 -0
- package/react-native/src/AlphaVideoView.tsx +329 -0
- package/react-native/src/AlphaVideoViewNativeComponent.ts +132 -0
- package/react-native/src/AlphaVideoViewNativeComponent.web.ts +5 -0
- package/react-native/src/NativeAlphaVideoModule.ts +22 -0
- package/react-native/src/NativeAlphaVideoModule.web.ts +2 -0
- package/react-native/src/WebPlayer.tsx +7 -0
- package/react-native/src/WebPlayer.web.tsx +60 -0
- package/react-native/src/index.tsx +144 -0
- package/react-native-alpha-video.podspec +99 -0
- package/react-native.config.js +12 -0
- package/scripts/build_codecs_android.sh +246 -0
- package/scripts/build_codecs_ios.sh +31 -0
- package/scripts/build_codecs_ios_device.sh +210 -0
- package/scripts/build_codecs_ios_simulator.sh +256 -0
- package/scripts/build_wasm.sh +230 -0
- package/scripts/build_xcframework.sh +49 -0
- package/scripts/fetch-prebuilt.sh +48 -0
- package/scripts/package_release.sh +107 -0
- package/scripts/publish-android.sh +85 -0
- package/scripts/vendor_range_source.sh +47 -0
- package/third_party/libwebm/AUTHORS.TXT +5 -0
- package/third_party/libwebm/LICENSE.TXT +30 -0
- package/third_party/libwebm/PATENTS.TXT +23 -0
- package/third_party/libwebm/README.md +7 -0
- package/third_party/libwebm/webm_parser/include/webm/buffer_reader.h +138 -0
- package/third_party/libwebm/webm_parser/include/webm/callback.h +363 -0
- package/third_party/libwebm/webm_parser/include/webm/dom_types.h +1781 -0
- package/third_party/libwebm/webm_parser/include/webm/element.h +208 -0
- package/third_party/libwebm/webm_parser/include/webm/file_reader.h +103 -0
- package/third_party/libwebm/webm_parser/include/webm/id.h +1085 -0
- package/third_party/libwebm/webm_parser/include/webm/istream_reader.h +99 -0
- package/third_party/libwebm/webm_parser/include/webm/reader.h +94 -0
- package/third_party/libwebm/webm_parser/include/webm/status.h +166 -0
- package/third_party/libwebm/webm_parser/include/webm/webm_parser.h +133 -0
- package/third_party/libwebm/webm_parser/src/ancestory.cc +334 -0
- package/third_party/libwebm/webm_parser/src/ancestory.h +83 -0
- package/third_party/libwebm/webm_parser/src/audio_parser.h +84 -0
- package/third_party/libwebm/webm_parser/src/bit_utils.cc +26 -0
- package/third_party/libwebm/webm_parser/src/bit_utils.h +24 -0
- package/third_party/libwebm/webm_parser/src/block_additions_parser.h +30 -0
- package/third_party/libwebm/webm_parser/src/block_group_parser.h +71 -0
- package/third_party/libwebm/webm_parser/src/block_header_parser.cc +76 -0
- package/third_party/libwebm/webm_parser/src/block_header_parser.h +65 -0
- package/third_party/libwebm/webm_parser/src/block_more_parser.h +32 -0
- package/third_party/libwebm/webm_parser/src/block_parser.cc +285 -0
- package/third_party/libwebm/webm_parser/src/block_parser.h +126 -0
- package/third_party/libwebm/webm_parser/src/bool_parser.h +95 -0
- package/third_party/libwebm/webm_parser/src/buffer_reader.cc +110 -0
- package/third_party/libwebm/webm_parser/src/byte_parser.h +144 -0
- package/third_party/libwebm/webm_parser/src/callback.cc +155 -0
- package/third_party/libwebm/webm_parser/src/chapter_atom_parser.h +42 -0
- package/third_party/libwebm/webm_parser/src/chapter_display_parser.h +34 -0
- package/third_party/libwebm/webm_parser/src/chapters_parser.h +28 -0
- package/third_party/libwebm/webm_parser/src/cluster_parser.h +48 -0
- package/third_party/libwebm/webm_parser/src/colour_parser.h +55 -0
- package/third_party/libwebm/webm_parser/src/content_enc_aes_settings_parser.h +32 -0
- package/third_party/libwebm/webm_parser/src/content_encoding_parser.h +38 -0
- package/third_party/libwebm/webm_parser/src/content_encodings_parser.h +30 -0
- package/third_party/libwebm/webm_parser/src/content_encryption_parser.h +37 -0
- package/third_party/libwebm/webm_parser/src/cue_point_parser.h +38 -0
- package/third_party/libwebm/webm_parser/src/cue_track_positions_parser.h +39 -0
- package/third_party/libwebm/webm_parser/src/cues_parser.h +27 -0
- package/third_party/libwebm/webm_parser/src/date_parser.cc +72 -0
- package/third_party/libwebm/webm_parser/src/date_parser.h +64 -0
- package/third_party/libwebm/webm_parser/src/ebml_parser.h +48 -0
- package/third_party/libwebm/webm_parser/src/edition_entry_parser.h +35 -0
- package/third_party/libwebm/webm_parser/src/element_parser.h +102 -0
- package/third_party/libwebm/webm_parser/src/file_reader.cc +142 -0
- package/third_party/libwebm/webm_parser/src/float_parser.cc +77 -0
- package/third_party/libwebm/webm_parser/src/float_parser.h +65 -0
- package/third_party/libwebm/webm_parser/src/id_element_parser.cc +49 -0
- package/third_party/libwebm/webm_parser/src/id_element_parser.h +56 -0
- package/third_party/libwebm/webm_parser/src/id_parser.cc +75 -0
- package/third_party/libwebm/webm_parser/src/id_parser.h +45 -0
- package/third_party/libwebm/webm_parser/src/info_parser.h +44 -0
- package/third_party/libwebm/webm_parser/src/int_parser.h +121 -0
- package/third_party/libwebm/webm_parser/src/istream_reader.cc +133 -0
- package/third_party/libwebm/webm_parser/src/master_parser.cc +298 -0
- package/third_party/libwebm/webm_parser/src/master_parser.h +230 -0
- package/third_party/libwebm/webm_parser/src/master_value_parser.h +533 -0
- package/third_party/libwebm/webm_parser/src/mastering_metadata_parser.h +57 -0
- package/third_party/libwebm/webm_parser/src/parser.h +34 -0
- package/third_party/libwebm/webm_parser/src/parser_utils.cc +34 -0
- package/third_party/libwebm/webm_parser/src/parser_utils.h +64 -0
- package/third_party/libwebm/webm_parser/src/projection_parser.h +40 -0
- package/third_party/libwebm/webm_parser/src/recursive_parser.h +93 -0
- package/third_party/libwebm/webm_parser/src/seek_head_parser.h +27 -0
- package/third_party/libwebm/webm_parser/src/seek_parser.h +37 -0
- package/third_party/libwebm/webm_parser/src/segment_parser.cc +86 -0
- package/third_party/libwebm/webm_parser/src/segment_parser.h +53 -0
- package/third_party/libwebm/webm_parser/src/simple_tag_parser.h +37 -0
- package/third_party/libwebm/webm_parser/src/size_parser.cc +56 -0
- package/third_party/libwebm/webm_parser/src/size_parser.h +43 -0
- package/third_party/libwebm/webm_parser/src/skip_callback.h +63 -0
- package/third_party/libwebm/webm_parser/src/skip_parser.cc +59 -0
- package/third_party/libwebm/webm_parser/src/skip_parser.h +35 -0
- package/third_party/libwebm/webm_parser/src/slices_parser.h +30 -0
- package/third_party/libwebm/webm_parser/src/tag_parser.h +37 -0
- package/third_party/libwebm/webm_parser/src/tags_parser.h +27 -0
- package/third_party/libwebm/webm_parser/src/targets_parser.h +35 -0
- package/third_party/libwebm/webm_parser/src/time_slice_parser.h +30 -0
- package/third_party/libwebm/webm_parser/src/track_entry_parser.h +65 -0
- package/third_party/libwebm/webm_parser/src/tracks_parser.h +27 -0
- package/third_party/libwebm/webm_parser/src/unknown_parser.cc +49 -0
- package/third_party/libwebm/webm_parser/src/unknown_parser.h +38 -0
- package/third_party/libwebm/webm_parser/src/var_int_parser.cc +71 -0
- package/third_party/libwebm/webm_parser/src/var_int_parser.h +56 -0
- package/third_party/libwebm/webm_parser/src/video_parser.h +122 -0
- package/third_party/libwebm/webm_parser/src/virtual_block_parser.cc +72 -0
- package/third_party/libwebm/webm_parser/src/virtual_block_parser.h +69 -0
- package/third_party/libwebm/webm_parser/src/void_parser.cc +49 -0
- package/third_party/libwebm/webm_parser/src/void_parser.h +41 -0
- package/third_party/libwebm/webm_parser/src/webm_parser.cc +282 -0
- package/third_party/range-source/LICENSE +21 -0
- package/third_party/range-source/README.md +8 -0
- package/third_party/range-source/include/range_source.h +395 -0
- package/third_party/range-source/include/range_source_download.h +309 -0
|
@@ -0,0 +1,984 @@
|
|
|
1
|
+
#include "AlphaDemuxer.h"
|
|
2
|
+
|
|
3
|
+
#include "Av1SequenceHeader.h"
|
|
4
|
+
#include "ByteSource.h"
|
|
5
|
+
#include "YuvToRgb.h"
|
|
6
|
+
|
|
7
|
+
#include <webm/callback.h>
|
|
8
|
+
#include <webm/reader.h>
|
|
9
|
+
#include <webm/status.h>
|
|
10
|
+
#include <webm/webm_parser.h>
|
|
11
|
+
|
|
12
|
+
#include <algorithm>
|
|
13
|
+
#include <cmath>
|
|
14
|
+
#include <cstring>
|
|
15
|
+
#include <deque>
|
|
16
|
+
#include <limits>
|
|
17
|
+
#include <map>
|
|
18
|
+
#include <numeric>
|
|
19
|
+
#include <string>
|
|
20
|
+
|
|
21
|
+
namespace webm {
|
|
22
|
+
|
|
23
|
+
namespace {
|
|
24
|
+
|
|
25
|
+
constexpr int64_t kNone = std::numeric_limits<int64_t>::min();
|
|
26
|
+
constexpr uint64_t kNoPosition = std::numeric_limits<uint64_t>::max();
|
|
27
|
+
constexpr size_t kReadBufferSize = 64 * 1024;
|
|
28
|
+
constexpr uint64_t kMaxFrameSize = 64 * 1024 * 1024;
|
|
29
|
+
// Without a DefaultDuration, the frame rate comes from this many colour timestamps
|
|
30
|
+
constexpr size_t kFrameRateSamples = 32;
|
|
31
|
+
// Opening stops looking for the first alpha packet this far past the first cluster (FFmpeg's probesize)
|
|
32
|
+
constexpr uint64_t kProbeBytes = 5000000;
|
|
33
|
+
|
|
34
|
+
// webm_parser's Reader over a ByteSource. Buffered: the parser reads element headers a few bytes
|
|
35
|
+
// at a time. A source's kPending is the parser's kWouldBlock, from which it resumes.
|
|
36
|
+
class ByteSourceReader : public Reader {
|
|
37
|
+
public:
|
|
38
|
+
explicit ByteSourceReader(ByteSource* source) : source_(source), buffer_(new uint8_t[kReadBufferSize]) {}
|
|
39
|
+
|
|
40
|
+
Status Read(std::size_t numToRead, std::uint8_t* buffer, std::uint64_t* numActuallyRead) override {
|
|
41
|
+
*numActuallyRead = 0;
|
|
42
|
+
if (numToRead == 0) return Status(Status::kOkCompleted);
|
|
43
|
+
if (pos_ < bufferPos_ || pos_ >= bufferPos_ + bufferSize_) {
|
|
44
|
+
if (numToRead >= kReadBufferSize) {
|
|
45
|
+
return advance(source_->read(pos_, buffer, numToRead), numToRead, numActuallyRead);
|
|
46
|
+
}
|
|
47
|
+
const int64_t n = source_->read(pos_, buffer_.get(), kReadBufferSize);
|
|
48
|
+
if (n <= 0) return advance(n, numToRead, numActuallyRead);
|
|
49
|
+
bufferPos_ = pos_;
|
|
50
|
+
bufferSize_ = static_cast<uint64_t>(n);
|
|
51
|
+
}
|
|
52
|
+
const uint64_t n = std::min<uint64_t>(numToRead, bufferPos_ + bufferSize_ - pos_);
|
|
53
|
+
std::memcpy(buffer, buffer_.get() + (pos_ - bufferPos_), n);
|
|
54
|
+
return advance(static_cast<int64_t>(n), numToRead, numActuallyRead);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Moves without reading, but not past the end, so that the parser sees a truncated element
|
|
58
|
+
// as one.
|
|
59
|
+
Status Skip(std::uint64_t numToSkip, std::uint64_t* numActuallySkipped) override {
|
|
60
|
+
const int64_t size = source_->size();
|
|
61
|
+
uint64_t n = numToSkip;
|
|
62
|
+
if (size >= 0) n = std::min<uint64_t>(n, static_cast<uint64_t>(size) > pos_ ? size - pos_ : 0);
|
|
63
|
+
pos_ += n;
|
|
64
|
+
*numActuallySkipped = n;
|
|
65
|
+
if (n == numToSkip) return Status(Status::kOkCompleted);
|
|
66
|
+
return Status(n > 0 ? Status::kOkPartial : Status::kEndOfFile);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
std::uint64_t Position() const override { return pos_; }
|
|
70
|
+
|
|
71
|
+
void seek(uint64_t pos) {
|
|
72
|
+
pos_ = pos;
|
|
73
|
+
failed_ = false;
|
|
74
|
+
pending_ = false;
|
|
75
|
+
}
|
|
76
|
+
bool failed() const { return failed_; }
|
|
77
|
+
// Whether the last read found the source without the bytes yet
|
|
78
|
+
bool pending() const { return pending_; }
|
|
79
|
+
void clearPending() { pending_ = false; }
|
|
80
|
+
|
|
81
|
+
private:
|
|
82
|
+
Status advance(int64_t n, std::size_t wanted, std::uint64_t* numActuallyRead) {
|
|
83
|
+
if (n == ByteSource::kPending) {
|
|
84
|
+
pending_ = true;
|
|
85
|
+
return Status(Status::kWouldBlock);
|
|
86
|
+
}
|
|
87
|
+
if (n < 0) failed_ = true;
|
|
88
|
+
if (n <= 0) return Status(Status::kEndOfFile);
|
|
89
|
+
pos_ += static_cast<uint64_t>(n);
|
|
90
|
+
*numActuallyRead = static_cast<uint64_t>(n);
|
|
91
|
+
return Status(static_cast<std::size_t>(n) == wanted ? Status::kOkCompleted : Status::kOkPartial);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
ByteSource* source_;
|
|
95
|
+
std::unique_ptr<uint8_t[]> buffer_;
|
|
96
|
+
uint64_t bufferPos_ = 0;
|
|
97
|
+
uint64_t bufferSize_ = 0;
|
|
98
|
+
uint64_t pos_ = 0;
|
|
99
|
+
bool failed_ = false;
|
|
100
|
+
bool pending_ = false;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// a / d rounded to nearest, halves away from zero (FFmpeg's AV_ROUND_NEAR_INF), for d > 0.
|
|
104
|
+
int64_t divideRounded(int64_t a, int64_t d) {
|
|
105
|
+
return a >= 0 ? (a + d / 2) / d : -((-a + d / 2) / d);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// FFmpeg's av_reduce: the closest fraction to num/den whose terms are at most max. For positive
|
|
109
|
+
// num and den.
|
|
110
|
+
void reduceFraction(int64_t num, int64_t den, int64_t max, int64_t* outNum, int64_t* outDen) {
|
|
111
|
+
int64_t a0n = 0, a0d = 1, a1n = 1, a1d = 0;
|
|
112
|
+
const int64_t gcd = std::gcd(num, den);
|
|
113
|
+
if (gcd) {
|
|
114
|
+
num /= gcd;
|
|
115
|
+
den /= gcd;
|
|
116
|
+
}
|
|
117
|
+
if (num <= max && den <= max) {
|
|
118
|
+
a1n = num;
|
|
119
|
+
a1d = den;
|
|
120
|
+
den = 0;
|
|
121
|
+
}
|
|
122
|
+
while (den) {
|
|
123
|
+
int64_t x = num / den;
|
|
124
|
+
const int64_t nextDen = num - den * x;
|
|
125
|
+
const int64_t a2n = x * a1n + a0n;
|
|
126
|
+
const int64_t a2d = x * a1d + a0d;
|
|
127
|
+
if (a2n > max || a2d > max) {
|
|
128
|
+
if (a1n) x = (max - a0n) / a1n;
|
|
129
|
+
if (a1d) x = std::min(x, (max - a0d) / a1d);
|
|
130
|
+
if (den * (2 * x * a1d + a0d) > num * a1d) {
|
|
131
|
+
a1n = x * a1n + a0n;
|
|
132
|
+
a1d = x * a1d + a0d;
|
|
133
|
+
}
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
a0n = a1n;
|
|
137
|
+
a0d = a1d;
|
|
138
|
+
a1n = a2n;
|
|
139
|
+
a1d = a2d;
|
|
140
|
+
num = den;
|
|
141
|
+
den = nextDen;
|
|
142
|
+
}
|
|
143
|
+
*outNum = a1n;
|
|
144
|
+
*outDen = a1d;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// From frame timestamps, snapped to a common rate within 1% of it.
|
|
148
|
+
double estimateFrameRate(const std::vector<int64_t>& ptsUs) {
|
|
149
|
+
if (ptsUs.size() < 2) return 0;
|
|
150
|
+
const auto [first, last] = std::minmax_element(ptsUs.begin(), ptsUs.end());
|
|
151
|
+
if (*last <= *first) return 0;
|
|
152
|
+
const double fps = (ptsUs.size() - 1) * 1e6 / static_cast<double>(*last - *first);
|
|
153
|
+
static const double kCommonRates[] = {
|
|
154
|
+
24000.0 / 1001, 24, 25, 30000.0 / 1001, 30, 48, 50, 60000.0 / 1001, 60, 10, 12, 15, 120};
|
|
155
|
+
for (double rate : kCommonRates) {
|
|
156
|
+
if (std::fabs(fps - rate) < rate * 0.01) return rate;
|
|
157
|
+
}
|
|
158
|
+
return fps;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
struct BitReader {
|
|
162
|
+
const uint8_t* data;
|
|
163
|
+
int bitPos = 0;
|
|
164
|
+
int bits(int n) {
|
|
165
|
+
int v = 0;
|
|
166
|
+
for (; n > 0; n--, bitPos++) v = (v << 1) | ((data[bitPos >> 3] >> (7 - (bitPos & 7))) & 1);
|
|
167
|
+
return v;
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
struct VpxHeader {
|
|
172
|
+
int profile = 0;
|
|
173
|
+
int bitDepth = 8;
|
|
174
|
+
int colorSpace = kMatrixUnspecified;
|
|
175
|
+
int colorRange = kRangeLimited;
|
|
176
|
+
int width = 0;
|
|
177
|
+
int height = 0;
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
// A VP9 keyframe's uncompressed header, as FFmpeg's vp9 decoder reads it (VP9 spec 6.2).
|
|
181
|
+
bool parseVp9KeyframeHeader(const uint8_t* data, size_t size, VpxHeader* out) {
|
|
182
|
+
if (!data || size < 10) return false; // The fields read below fit in 10 bytes
|
|
183
|
+
BitReader r{data};
|
|
184
|
+
if (r.bits(2) != 2) return false; // frame_marker
|
|
185
|
+
int profile = r.bits(1);
|
|
186
|
+
profile |= r.bits(1) << 1;
|
|
187
|
+
if (profile == 3) r.bits(1);
|
|
188
|
+
if (r.bits(1)) return false; // show_existing_frame
|
|
189
|
+
if (r.bits(1)) return false; // frame_type: not a keyframe
|
|
190
|
+
r.bits(2); // show_frame, error_resilient_mode
|
|
191
|
+
if (r.bits(24) != 0x498342) return false; // sync code
|
|
192
|
+
|
|
193
|
+
out->profile = profile;
|
|
194
|
+
out->bitDepth = profile >= 2 ? (r.bits(1) ? 12 : 10) : 8;
|
|
195
|
+
static const int kColorSpaces[8] = {kMatrixUnspecified, kMatrixBt470bg, kMatrixBt709, kMatrixSmpte170m,
|
|
196
|
+
kMatrixSmpte240m, kMatrixBt2020Ncl, kMatrixReserved, kMatrixRgb};
|
|
197
|
+
out->colorSpace = kColorSpaces[r.bits(3)];
|
|
198
|
+
if (out->colorSpace != kMatrixRgb) {
|
|
199
|
+
out->colorRange = r.bits(1) ? kRangeFull : kRangeLimited;
|
|
200
|
+
if (profile & 1) r.bits(3); // subsampling_x, subsampling_y, reserved_zero
|
|
201
|
+
} else {
|
|
202
|
+
out->colorRange = kRangeFull;
|
|
203
|
+
if (profile & 1) r.bits(1); // reserved_zero
|
|
204
|
+
}
|
|
205
|
+
out->width = r.bits(16) + 1;
|
|
206
|
+
out->height = r.bits(16) + 1;
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// VP8's boolean decoder (RFC 6386 7.3), for the first bools of a frame's first partition
|
|
211
|
+
struct BoolDecoder {
|
|
212
|
+
const uint8_t* p;
|
|
213
|
+
const uint8_t* end;
|
|
214
|
+
uint32_t value = 0;
|
|
215
|
+
uint32_t range = 255;
|
|
216
|
+
int bitCount = 0;
|
|
217
|
+
|
|
218
|
+
BoolDecoder(const uint8_t* data, size_t size) : p(data + 2), end(data + size) {
|
|
219
|
+
value = (static_cast<uint32_t>(data[0]) << 8) | data[1];
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
int readBool(int probability) {
|
|
223
|
+
const uint32_t split = 1 + (((range - 1) * static_cast<uint32_t>(probability)) >> 8);
|
|
224
|
+
const uint32_t bigSplit = split << 8;
|
|
225
|
+
int bit = 0;
|
|
226
|
+
if (value >= bigSplit) {
|
|
227
|
+
bit = 1;
|
|
228
|
+
range -= split;
|
|
229
|
+
value -= bigSplit;
|
|
230
|
+
} else {
|
|
231
|
+
range = split;
|
|
232
|
+
}
|
|
233
|
+
while (range < 128) {
|
|
234
|
+
value <<= 1;
|
|
235
|
+
range <<= 1;
|
|
236
|
+
if (++bitCount == 8) {
|
|
237
|
+
bitCount = 0;
|
|
238
|
+
if (p < end) value |= *p++;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return bit;
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
// A VP8 keyframe's header, as FFmpeg's vp8 decoder reads it (RFC 6386 9.1-9.2). There is no
|
|
246
|
+
// matrix for VP8 but BT.601, which color_space 0 says, and clamping_type 1 says full range.
|
|
247
|
+
bool parseVp8KeyframeHeader(const uint8_t* data, size_t size, VpxHeader* out) {
|
|
248
|
+
if (!data || size < 12) return false; // The frame tag, start code, size and 2 bytes of bools
|
|
249
|
+
if (data[0] & 1) return false; // frame type: not a keyframe
|
|
250
|
+
if (data[3] != 0x9d || data[4] != 0x01 || data[5] != 0x2a) return false; // start code
|
|
251
|
+
BoolDecoder bools(data + 10, size - 10);
|
|
252
|
+
const int colorSpace = bools.readBool(128);
|
|
253
|
+
const int clampingType = bools.readBool(128);
|
|
254
|
+
|
|
255
|
+
out->profile = (data[0] >> 1) & 7; // version
|
|
256
|
+
out->bitDepth = 8;
|
|
257
|
+
out->colorSpace = colorSpace == 0 ? kMatrixBt470bg : kMatrixUnspecified;
|
|
258
|
+
out->colorRange = clampingType ? kRangeFull : kRangeLimited;
|
|
259
|
+
out->width = (data[6] | (data[7] << 8)) & 0x3fff;
|
|
260
|
+
out->height = (data[8] | (data[9] << 8)) & 0x3fff;
|
|
261
|
+
return true;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// The sequence header from av1C's configOBUs, as FFmpeg's libdav1d reads it, or else from the
|
|
265
|
+
// first packet.
|
|
266
|
+
bool parseAv1TrackHeader(const std::vector<uint8_t>& av1C, const std::vector<uint8_t>& firstPacket,
|
|
267
|
+
Av1SequenceHeader* out) {
|
|
268
|
+
if (av1C.size() > 4 && (av1C[0] & 0x80) && parseAv1SequenceHeader(av1C.data() + 4, av1C.size() - 4, out)) {
|
|
269
|
+
return true;
|
|
270
|
+
}
|
|
271
|
+
return !firstPacket.empty() && parseAv1SequenceHeader(firstPacket.data(), firstPacket.size(), out);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// The track's Colour element, as FFmpeg's Matroska demuxer takes it.
|
|
275
|
+
void readContainerColour(const TrackEntry& entry, AlphaDemuxer::StreamInfo* s) {
|
|
276
|
+
if (!entry.video.is_present() || !entry.video.value().colour.is_present()) return;
|
|
277
|
+
const Colour& colour = entry.video.value().colour.value();
|
|
278
|
+
const int matrix = static_cast<int>(colour.matrix_coefficients.value());
|
|
279
|
+
const int primaries = static_cast<int>(colour.primaries.value());
|
|
280
|
+
const int trc = static_cast<int>(colour.transfer_characteristics.value());
|
|
281
|
+
const int range = static_cast<int>(colour.range.value());
|
|
282
|
+
if (matrix != kMatrixReserved) s->colorSpace = matrix;
|
|
283
|
+
if (primaries != 0 && primaries != 3) s->colorPrimaries = primaries;
|
|
284
|
+
if (trc != 0 && trc != 3) s->colorTrc = trc;
|
|
285
|
+
if (range == kRangeLimited || range == kRangeFull) s->colorRange = range;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
} // namespace
|
|
289
|
+
|
|
290
|
+
bool isVp9Keyframe(const uint8_t* data, int size) {
|
|
291
|
+
if (!data || size < 1) return false;
|
|
292
|
+
const uint8_t b = data[0];
|
|
293
|
+
if ((b >> 6) != 2) return false; // frame_marker
|
|
294
|
+
const int profile = ((b >> 5) & 1) | (((b >> 4) & 1) << 1);
|
|
295
|
+
// profile 3 has a reserved bit before show_existing_frame
|
|
296
|
+
const int showExistingBit = profile == 3 ? 2 : 3;
|
|
297
|
+
if ((b >> showExistingBit) & 1) return false;
|
|
298
|
+
return ((b >> (showExistingBit - 1)) & 1) == 0; // frame_type 0 is a keyframe
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
bool isVp8Keyframe(const uint8_t* data, int size) {
|
|
302
|
+
return data && size >= 3 && (data[0] & 1) == 0; // frame type 0 is a keyframe
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Reads the file through webm_parser, which calls back per element. The callbacks pause it at the
|
|
307
|
+
* end of each block it returns packets for, so packets are read one block at a time.
|
|
308
|
+
*
|
|
309
|
+
* Seeking, like FFmpeg's: to the colour keyframe at or before the time, from an index of the
|
|
310
|
+
* keyframes read so far (complete up to where the file has been read through from the start)
|
|
311
|
+
* and the Cues, read when first needed. Past the last keyframe known, the index grows by reading
|
|
312
|
+
* on. Then every block before that keyframe's time is skipped.
|
|
313
|
+
*/
|
|
314
|
+
class AlphaDemuxer::Impl : public Callback {
|
|
315
|
+
public:
|
|
316
|
+
explicit Impl(std::unique_ptr<ByteSource> source)
|
|
317
|
+
: source_(std::move(source)), reader_(source_.get()) {}
|
|
318
|
+
|
|
319
|
+
AlphaDemuxer::Progress tryOpen();
|
|
320
|
+
const AlphaDemuxer::Info& info() const { return info_; }
|
|
321
|
+
bool readPacket(AlphaDemuxer::Packet* out);
|
|
322
|
+
bool failed() const { return reader_.failed(); }
|
|
323
|
+
bool pending() const { return pending_; }
|
|
324
|
+
void seekToStart();
|
|
325
|
+
void seekToTimeUs(int64_t timeUs);
|
|
326
|
+
|
|
327
|
+
Status OnElementBegin(const ElementMetadata& metadata, Action* action) override;
|
|
328
|
+
Status OnSegmentBegin(const ElementMetadata& metadata, Action* action) override;
|
|
329
|
+
Status OnSeek(const ElementMetadata& metadata, const Seek& seek) override;
|
|
330
|
+
Status OnInfo(const ElementMetadata& metadata, const webm::Info& info) override;
|
|
331
|
+
Status OnTrackEntry(const ElementMetadata& metadata, const TrackEntry& entry) override;
|
|
332
|
+
Status OnClusterBegin(const ElementMetadata& metadata, const Cluster& cluster, Action* action) override;
|
|
333
|
+
Status OnSimpleBlockBegin(const ElementMetadata& metadata, const SimpleBlock& block, Action* action) override;
|
|
334
|
+
Status OnSimpleBlockEnd(const ElementMetadata& metadata, const SimpleBlock& block) override;
|
|
335
|
+
Status OnBlockGroupBegin(const ElementMetadata& metadata, Action* action) override;
|
|
336
|
+
Status OnBlockBegin(const ElementMetadata& metadata, const Block& block, Action* action) override;
|
|
337
|
+
Status OnBlockGroupEnd(const ElementMetadata& metadata, const BlockGroup& group) override;
|
|
338
|
+
Status OnFrame(const FrameMetadata& metadata, Reader* reader, std::uint64_t* bytesRemaining) override;
|
|
339
|
+
Status OnCuePoint(const ElementMetadata& metadata, const CuePoint& cuePoint) override;
|
|
340
|
+
|
|
341
|
+
private:
|
|
342
|
+
// Reads only the Cues element, for its cue points.
|
|
343
|
+
class CuesCallback : public Callback {
|
|
344
|
+
public:
|
|
345
|
+
explicit CuesCallback(Impl* impl) : impl_(impl) {}
|
|
346
|
+
Status OnElementBegin(const ElementMetadata& metadata, Action* action) override;
|
|
347
|
+
Status OnCuePoint(const ElementMetadata&, const CuePoint& cuePoint) override {
|
|
348
|
+
impl_->addCuePoint(cuePoint);
|
|
349
|
+
return Status(Status::kOkCompleted);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
private:
|
|
353
|
+
Impl* impl_;
|
|
354
|
+
uint64_t end_ = 0;
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
enum class Mode {
|
|
358
|
+
kProbe, // Opening: keeps the first packets, for the bitstream's info
|
|
359
|
+
kPackets, // Queues packets
|
|
360
|
+
kScan, // Indexes keyframes up to scanUntil_, without reading frames
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
struct Track {
|
|
364
|
+
uint64_t number = 0; // 0 when there's no such track
|
|
365
|
+
AlphaDemuxer::Codec codec = AlphaDemuxer::VP9;
|
|
366
|
+
TrackEntry entry;
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
struct OwnedPacket {
|
|
370
|
+
int stream;
|
|
371
|
+
std::vector<uint8_t> data;
|
|
372
|
+
int64_t ptsUs;
|
|
373
|
+
int frameIndex;
|
|
374
|
+
bool key;
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
enum class Fed {
|
|
378
|
+
kPaused, // A callback paused the parser
|
|
379
|
+
kPending, // The source doesn't have the next bytes yet
|
|
380
|
+
kEnded, // The end of the file, or an error
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
Fed feed();
|
|
384
|
+
void restartAt(int64_t position);
|
|
385
|
+
bool probeComplete() const;
|
|
386
|
+
void buildInfo();
|
|
387
|
+
AlphaDemuxer::StreamInfo streamInfoOf(const Track& track, const std::vector<uint8_t>& firstPacket,
|
|
388
|
+
VpxHeader* vpx) const;
|
|
389
|
+
bool findKeyframe(int64_t ticks, std::map<int64_t, uint64_t>::const_iterator* out);
|
|
390
|
+
std::map<int64_t, uint64_t>::const_iterator keyframeAtOrBefore(int64_t ticks) const;
|
|
391
|
+
bool scan(uint64_t from, int64_t untilTicks);
|
|
392
|
+
bool loadCues();
|
|
393
|
+
void addCuePoint(const CuePoint& cuePoint);
|
|
394
|
+
|
|
395
|
+
void beginBlock(uint64_t trackNumber, int16_t timecode);
|
|
396
|
+
Status noteColourBlock();
|
|
397
|
+
Status finishBlock(const std::vector<uint8_t>* blockAdditional);
|
|
398
|
+
Status keepProbePackets(const std::vector<uint8_t>* blockAdditional);
|
|
399
|
+
|
|
400
|
+
int64_t ticksToUs(int64_t ticks) const;
|
|
401
|
+
int64_t usToTicks(int64_t us) const;
|
|
402
|
+
|
|
403
|
+
std::unique_ptr<ByteSource> source_;
|
|
404
|
+
ByteSourceReader reader_;
|
|
405
|
+
WebmParser parser_;
|
|
406
|
+
bool ended_ = false;
|
|
407
|
+
bool opened_ = false;
|
|
408
|
+
bool pending_ = false; // The last call stopped at a kPending read
|
|
409
|
+
Mode mode_ = Mode::kProbe;
|
|
410
|
+
AlphaDemuxer::Info info_;
|
|
411
|
+
|
|
412
|
+
// From the headers
|
|
413
|
+
int64_t segmentDataStart_ = -1;
|
|
414
|
+
int64_t cuesPosition_ = -1;
|
|
415
|
+
int64_t firstClusterPosition_ = -1;
|
|
416
|
+
uint64_t timecodeScale_ = 1000000; // Nanoseconds per tick
|
|
417
|
+
double durationTicks_ = 0;
|
|
418
|
+
Track colour_;
|
|
419
|
+
Track alpha_;
|
|
420
|
+
|
|
421
|
+
// The block being read
|
|
422
|
+
uint64_t clusterPosition_ = 0;
|
|
423
|
+
int64_t clusterTicks_ = 0;
|
|
424
|
+
int stream_ = -1; // 0 colour, 1 alpha, -1 neither
|
|
425
|
+
int64_t blockTicks_ = 0;
|
|
426
|
+
bool key_ = false;
|
|
427
|
+
bool discard_ = false; // Not returned; its frames are skipped
|
|
428
|
+
bool inBlockGroup_ = false;
|
|
429
|
+
std::vector<std::vector<uint8_t>> frames_;
|
|
430
|
+
uint64_t framePosition_ = kNoPosition;
|
|
431
|
+
// A block end paused the parser; the parser calls it again when it resumes
|
|
432
|
+
bool pausedAtBlockEnd_ = false;
|
|
433
|
+
|
|
434
|
+
// Opening
|
|
435
|
+
bool haveColourPacket_ = false;
|
|
436
|
+
bool haveAlphaPacket_ = false;
|
|
437
|
+
std::vector<uint8_t> firstColourPacket_;
|
|
438
|
+
std::vector<uint8_t> firstAlphaPacket_;
|
|
439
|
+
bool firstColourHasBlockAdditional_ = false;
|
|
440
|
+
int64_t startTicks_ = 0;
|
|
441
|
+
std::vector<int64_t> colourPtsSamples_;
|
|
442
|
+
|
|
443
|
+
// Reading
|
|
444
|
+
std::deque<OwnedPacket> queue_;
|
|
445
|
+
OwnedPacket current_{};
|
|
446
|
+
int colourFrameIndex_ = 0;
|
|
447
|
+
int alphaFrameIndex_ = 0;
|
|
448
|
+
int64_t skipBefore_ = kNone; // After a seek, blocks before this tick are skipped
|
|
449
|
+
bool colourNeedsKey_ = false; // After a seek, colour blocks are skipped until a keyframe
|
|
450
|
+
|
|
451
|
+
// Seeking: colour keyframes' ticks -> their clusters' positions
|
|
452
|
+
std::map<int64_t, uint64_t> keyframes_;
|
|
453
|
+
bool readingFromStart_ = true; // Every block since the first has been seen
|
|
454
|
+
int64_t indexedUpTo_ = kNone; // keyframes_ has every keyframe up to this tick
|
|
455
|
+
bool cuesLoaded_ = false;
|
|
456
|
+
int64_t scanUntil_ = 0;
|
|
457
|
+
bool scanStopped_ = false;
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
// --- Reading -------------------------------------------------------------------------------
|
|
461
|
+
|
|
462
|
+
int64_t AlphaDemuxer::Impl::ticksToUs(int64_t ticks) const {
|
|
463
|
+
if (timecodeScale_ % 1000 == 0) return ticks * static_cast<int64_t>(timecodeScale_ / 1000);
|
|
464
|
+
return std::llround(static_cast<double>(ticks) * static_cast<double>(timecodeScale_) / 1000.0);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
int64_t AlphaDemuxer::Impl::usToTicks(int64_t us) const {
|
|
468
|
+
if (timecodeScale_ % 1000 == 0) return divideRounded(us, static_cast<int64_t>(timecodeScale_ / 1000));
|
|
469
|
+
return std::llround(static_cast<double>(us) * 1000.0 / static_cast<double>(timecodeScale_));
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// Feeds the parser until a callback pauses it, the source has no more bytes yet, or the file
|
|
473
|
+
// ends or can't be read.
|
|
474
|
+
AlphaDemuxer::Impl::Fed AlphaDemuxer::Impl::feed() {
|
|
475
|
+
if (ended_) return Fed::kEnded;
|
|
476
|
+
reader_.clearPending();
|
|
477
|
+
const Status status = parser_.Feed(this, &reader_);
|
|
478
|
+
if (status.code == Status::kWouldBlock) return reader_.pending() ? Fed::kPending : Fed::kPaused;
|
|
479
|
+
ended_ = true;
|
|
480
|
+
// Read through from the start: every keyframe is indexed
|
|
481
|
+
if (status.completed_ok() && readingFromStart_) indexedUpTo_ = std::numeric_limits<int64_t>::max();
|
|
482
|
+
return Fed::kEnded;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
void AlphaDemuxer::Impl::restartAt(int64_t position) {
|
|
486
|
+
queue_.clear();
|
|
487
|
+
frames_.clear();
|
|
488
|
+
framePosition_ = kNoPosition;
|
|
489
|
+
pausedAtBlockEnd_ = false;
|
|
490
|
+
inBlockGroup_ = false;
|
|
491
|
+
readingFromStart_ = false;
|
|
492
|
+
skipBefore_ = kNone;
|
|
493
|
+
colourNeedsKey_ = false;
|
|
494
|
+
if (position < 0) {
|
|
495
|
+
ended_ = true;
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
reader_.seek(static_cast<uint64_t>(position));
|
|
499
|
+
parser_.DidSeek();
|
|
500
|
+
ended_ = false;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
bool AlphaDemuxer::Impl::readPacket(AlphaDemuxer::Packet* out) {
|
|
504
|
+
pending_ = false;
|
|
505
|
+
while (queue_.empty()) {
|
|
506
|
+
const Fed fed = feed();
|
|
507
|
+
if (fed == Fed::kPaused) continue;
|
|
508
|
+
pending_ = fed == Fed::kPending;
|
|
509
|
+
return false;
|
|
510
|
+
}
|
|
511
|
+
current_ = std::move(queue_.front());
|
|
512
|
+
queue_.pop_front();
|
|
513
|
+
out->stream = current_.stream;
|
|
514
|
+
out->data = current_.data.data();
|
|
515
|
+
out->size = static_cast<int>(current_.data.size());
|
|
516
|
+
out->ptsUs = current_.ptsUs;
|
|
517
|
+
out->frameIndex = current_.frameIndex;
|
|
518
|
+
out->key = current_.key;
|
|
519
|
+
return true;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// --- Opening -------------------------------------------------------------------------------
|
|
523
|
+
|
|
524
|
+
bool AlphaDemuxer::Impl::probeComplete() const {
|
|
525
|
+
if (!haveColourPacket_) return false;
|
|
526
|
+
if (reader_.Position() > static_cast<uint64_t>(firstClusterPosition_) + kProbeBytes) return true;
|
|
527
|
+
const bool needFrameRate = !colour_.entry.default_duration.is_present() ||
|
|
528
|
+
colour_.entry.default_duration.value() == 0;
|
|
529
|
+
return (!alpha_.number || haveAlphaPacket_) &&
|
|
530
|
+
(!needFrameRate || colourPtsSamples_.size() >= kFrameRateSamples);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
AlphaDemuxer::Progress AlphaDemuxer::Impl::tryOpen() {
|
|
534
|
+
if (opened_) return AlphaDemuxer::Progress::kDone;
|
|
535
|
+
// The parser pauses once the first packets are kept, or at the first cluster if there's no track
|
|
536
|
+
while (!probeComplete()) {
|
|
537
|
+
const Fed fed = feed();
|
|
538
|
+
if (fed == Fed::kPending) return AlphaDemuxer::Progress::kPending;
|
|
539
|
+
if (fed == Fed::kEnded || !colour_.number) break;
|
|
540
|
+
}
|
|
541
|
+
if (!colour_.number) return AlphaDemuxer::Progress::kFailed;
|
|
542
|
+
buildInfo();
|
|
543
|
+
firstColourPacket_ = {};
|
|
544
|
+
firstAlphaPacket_ = {};
|
|
545
|
+
colourPtsSamples_ = {};
|
|
546
|
+
mode_ = Mode::kPackets;
|
|
547
|
+
seekToStart();
|
|
548
|
+
opened_ = true;
|
|
549
|
+
return AlphaDemuxer::Progress::kDone;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
AlphaDemuxer::StreamInfo AlphaDemuxer::Impl::streamInfoOf(const Track& track, const std::vector<uint8_t>& firstPacket,
|
|
553
|
+
VpxHeader* vpx) const {
|
|
554
|
+
AlphaDemuxer::StreamInfo s;
|
|
555
|
+
s.codec = track.codec;
|
|
556
|
+
readContainerColour(track.entry, &s);
|
|
557
|
+
if (track.codec == AlphaDemuxer::VP9) {
|
|
558
|
+
// VP9's range and matrix are the bitstream's; primaries and transfer the container's
|
|
559
|
+
if (parseVp9KeyframeHeader(firstPacket.data(), firstPacket.size(), vpx)) {
|
|
560
|
+
s.profile = vpx->profile;
|
|
561
|
+
s.bitDepth = vpx->bitDepth;
|
|
562
|
+
s.colorSpace = vpx->colorSpace;
|
|
563
|
+
s.colorRange = vpx->colorRange;
|
|
564
|
+
}
|
|
565
|
+
return s;
|
|
566
|
+
}
|
|
567
|
+
if (track.codec == AlphaDemuxer::VP8) {
|
|
568
|
+
// VP8's range is the bitstream's, and its matrix unless color_space 1 leaves it open
|
|
569
|
+
if (parseVp8KeyframeHeader(firstPacket.data(), firstPacket.size(), vpx)) {
|
|
570
|
+
s.profile = vpx->profile;
|
|
571
|
+
s.bitDepth = vpx->bitDepth;
|
|
572
|
+
if (vpx->colorSpace != kMatrixUnspecified) s.colorSpace = vpx->colorSpace;
|
|
573
|
+
s.colorRange = vpx->colorRange;
|
|
574
|
+
}
|
|
575
|
+
return s;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
s.codecPrivate = track.entry.codec_private.value();
|
|
579
|
+
Av1SequenceHeader seq;
|
|
580
|
+
if (parseAv1TrackHeader(s.codecPrivate, firstPacket, &seq)) {
|
|
581
|
+
s.profile = seq.profile;
|
|
582
|
+
s.bitDepth = seq.bitDepth;
|
|
583
|
+
s.colorRange = seq.fullRange ? kRangeFull : kRangeLimited;
|
|
584
|
+
s.colorSpace = seq.matrixCoefficients;
|
|
585
|
+
s.colorPrimaries = seq.colorPrimaries;
|
|
586
|
+
s.colorTrc = seq.transferCharacteristics;
|
|
587
|
+
} else if (s.codecPrivate.size() >= 3) {
|
|
588
|
+
const uint8_t flags = s.codecPrivate[2];
|
|
589
|
+
s.bitDepth = (flags & 0x40) ? ((flags & 0x20) ? 12 : 10) : 8;
|
|
590
|
+
}
|
|
591
|
+
return s;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
void AlphaDemuxer::Impl::buildInfo() {
|
|
595
|
+
const TrackEntry& entry = colour_.entry;
|
|
596
|
+
if (entry.video.is_present()) {
|
|
597
|
+
info_.width = static_cast<int>(entry.video.value().pixel_width.value());
|
|
598
|
+
info_.height = static_cast<int>(entry.video.value().pixel_height.value());
|
|
599
|
+
}
|
|
600
|
+
VpxHeader vpx;
|
|
601
|
+
info_.color = streamInfoOf(colour_, firstColourPacket_, &vpx);
|
|
602
|
+
if (vpx.width > 0) {
|
|
603
|
+
info_.width = vpx.width;
|
|
604
|
+
info_.height = vpx.height;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
if (entry.default_duration.is_present() && entry.default_duration.value() > 0) {
|
|
608
|
+
int64_t num = 0, den = 0;
|
|
609
|
+
reduceFraction(1000000000, static_cast<int64_t>(entry.default_duration.value()), 30000, &num, &den);
|
|
610
|
+
if (den > 0) info_.frameRate = static_cast<double>(num) / static_cast<double>(den);
|
|
611
|
+
} else {
|
|
612
|
+
std::vector<int64_t> ptsUs;
|
|
613
|
+
for (int64_t ticks : colourPtsSamples_) ptsUs.push_back(ticksToUs(ticks));
|
|
614
|
+
if (const double fps = estimateFrameRate(ptsUs); fps > 0) info_.frameRate = fps;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
if (durationTicks_ > 0) {
|
|
618
|
+
info_.durationUs = static_cast<int64_t>(durationTicks_ * static_cast<double>(timecodeScale_) / 1000.0);
|
|
619
|
+
info_.totalFrames = static_cast<int>(std::lround(static_cast<double>(info_.durationUs) / 1e6 * info_.frameRate));
|
|
620
|
+
} else {
|
|
621
|
+
info_.totalFrames = 100;
|
|
622
|
+
}
|
|
623
|
+
info_.startUs = haveColourPacket_ ? ticksToUs(startTicks_) : 0;
|
|
624
|
+
|
|
625
|
+
if (alpha_.number) {
|
|
626
|
+
VpxHeader alphaVpx;
|
|
627
|
+
info_.alpha = streamInfoOf(alpha_, firstAlphaPacket_, &alphaVpx);
|
|
628
|
+
} else if ((colour_.codec == AlphaDemuxer::VP9 || colour_.codec == AlphaDemuxer::VP8) &&
|
|
629
|
+
firstColourHasBlockAdditional_) {
|
|
630
|
+
info_.alphaInBlockAdditional = true;
|
|
631
|
+
info_.alpha = info_.color;
|
|
632
|
+
// libvpx's alpha stream says limited range but holds alpha as-is, 0-255
|
|
633
|
+
info_.alpha.colorRange = kRangeFull;
|
|
634
|
+
}
|
|
635
|
+
info_.hasAlpha = alpha_.number != 0 || info_.alphaInBlockAdditional;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// --- Seeking -------------------------------------------------------------------------------
|
|
639
|
+
|
|
640
|
+
void AlphaDemuxer::Impl::seekToStart() {
|
|
641
|
+
restartAt(firstClusterPosition_);
|
|
642
|
+
readingFromStart_ = true;
|
|
643
|
+
colourFrameIndex_ = 0;
|
|
644
|
+
alphaFrameIndex_ = 0;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
void AlphaDemuxer::Impl::seekToTimeUs(int64_t timeUs) {
|
|
648
|
+
pending_ = false;
|
|
649
|
+
std::map<int64_t, uint64_t>::const_iterator keyframe;
|
|
650
|
+
const bool found = findKeyframe(usToTicks(timeUs), &keyframe);
|
|
651
|
+
if (pending_) return;
|
|
652
|
+
if (!found) {
|
|
653
|
+
restartAt(firstClusterPosition_);
|
|
654
|
+
readingFromStart_ = true;
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
const int64_t keyTicks = keyframe->first;
|
|
658
|
+
restartAt(static_cast<int64_t>(keyframe->second));
|
|
659
|
+
skipBefore_ = keyTicks;
|
|
660
|
+
colourNeedsKey_ = true;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
std::map<int64_t, uint64_t>::const_iterator AlphaDemuxer::Impl::keyframeAtOrBefore(int64_t ticks) const {
|
|
664
|
+
// Clamped to the first, as FFmpeg does
|
|
665
|
+
auto it = keyframes_.upper_bound(std::max(ticks, keyframes_.begin()->first));
|
|
666
|
+
return --it;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// False if no keyframe is known, or if reading stopped at a kPending read (pending_).
|
|
670
|
+
bool AlphaDemuxer::Impl::findKeyframe(int64_t ticks, std::map<int64_t, uint64_t>::const_iterator* out) {
|
|
671
|
+
const bool pastIndexed = indexedUpTo_ == kNone || ticks > indexedUpTo_;
|
|
672
|
+
if (pastIndexed && !cuesLoaded_ && !loadCues()) return false;
|
|
673
|
+
// Past the last keyframe known, there may be later ones that aren't
|
|
674
|
+
if (pastIndexed &&
|
|
675
|
+
(keyframes_.empty() || std::next(keyframeAtOrBefore(ticks)) == keyframes_.end())) {
|
|
676
|
+
const uint64_t from = keyframes_.empty() ? static_cast<uint64_t>(firstClusterPosition_)
|
|
677
|
+
: keyframeAtOrBefore(ticks)->second;
|
|
678
|
+
if (!scan(from, ticks)) return false;
|
|
679
|
+
}
|
|
680
|
+
if (keyframes_.empty()) return false;
|
|
681
|
+
*out = keyframeAtOrBefore(ticks);
|
|
682
|
+
return true;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// Reads on from the cluster at `from` until a colour block after `untilTicks`, indexing keyframes.
|
|
686
|
+
// False if it stopped at a kPending read; the keyframes found up to there are kept.
|
|
687
|
+
bool AlphaDemuxer::Impl::scan(uint64_t from, int64_t untilTicks) {
|
|
688
|
+
if (firstClusterPosition_ < 0) return true;
|
|
689
|
+
restartAt(static_cast<int64_t>(from));
|
|
690
|
+
mode_ = Mode::kScan;
|
|
691
|
+
scanUntil_ = untilTicks;
|
|
692
|
+
scanStopped_ = false;
|
|
693
|
+
Fed fed = Fed::kPaused;
|
|
694
|
+
while (!scanStopped_ && (fed = feed()) == Fed::kPaused) {}
|
|
695
|
+
mode_ = Mode::kPackets;
|
|
696
|
+
pending_ = fed == Fed::kPending;
|
|
697
|
+
return !pending_;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
// False if it stopped at a kPending read; the Cues are then read again next time.
|
|
701
|
+
bool AlphaDemuxer::Impl::loadCues() {
|
|
702
|
+
cuesLoaded_ = true;
|
|
703
|
+
if (cuesPosition_ < 0) return true;
|
|
704
|
+
ByteSourceReader reader(source_.get());
|
|
705
|
+
reader.seek(static_cast<uint64_t>(cuesPosition_));
|
|
706
|
+
WebmParser parser;
|
|
707
|
+
parser.DidSeek();
|
|
708
|
+
CuesCallback callback(this);
|
|
709
|
+
parser.Feed(&callback, &reader);
|
|
710
|
+
if (!reader.pending()) return true;
|
|
711
|
+
cuesLoaded_ = false;
|
|
712
|
+
pending_ = true;
|
|
713
|
+
return false;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
Status AlphaDemuxer::Impl::CuesCallback::OnElementBegin(const ElementMetadata& metadata, Action* action) {
|
|
717
|
+
if (end_ == 0) {
|
|
718
|
+
if (metadata.id != Id::kCues) return Status(Status::kWouldBlock);
|
|
719
|
+
end_ = metadata.size == kUnknownElementSize ? kNoPosition
|
|
720
|
+
: metadata.position + metadata.header_size + metadata.size;
|
|
721
|
+
} else if (metadata.position >= end_) {
|
|
722
|
+
return Status(Status::kWouldBlock);
|
|
723
|
+
}
|
|
724
|
+
*action = Action::kRead;
|
|
725
|
+
return Status(Status::kOkCompleted);
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
void AlphaDemuxer::Impl::addCuePoint(const CuePoint& cuePoint) {
|
|
729
|
+
if (!cuePoint.time.is_present() || segmentDataStart_ < 0) return;
|
|
730
|
+
for (const Element<CueTrackPositions>& positions : cuePoint.cue_track_positions) {
|
|
731
|
+
const CueTrackPositions& p = positions.value();
|
|
732
|
+
if (p.track.value() != colour_.number || !p.cluster_position.is_present()) continue;
|
|
733
|
+
keyframes_.emplace(static_cast<int64_t>(cuePoint.time.value()),
|
|
734
|
+
static_cast<uint64_t>(segmentDataStart_) + p.cluster_position.value());
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// --- Parser callbacks ----------------------------------------------------------------------
|
|
739
|
+
|
|
740
|
+
Status AlphaDemuxer::Impl::OnElementBegin(const ElementMetadata& metadata, Action* action) {
|
|
741
|
+
if (metadata.id == Id::kChapters || metadata.id == Id::kTags) {
|
|
742
|
+
*action = Action::kSkip;
|
|
743
|
+
} else {
|
|
744
|
+
if (metadata.id == Id::kCues) cuesLoaded_ = true;
|
|
745
|
+
*action = Action::kRead;
|
|
746
|
+
}
|
|
747
|
+
return Status(Status::kOkCompleted);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
Status AlphaDemuxer::Impl::OnSegmentBegin(const ElementMetadata& metadata, Action* action) {
|
|
751
|
+
if (segmentDataStart_ < 0) segmentDataStart_ = static_cast<int64_t>(metadata.position + metadata.header_size);
|
|
752
|
+
*action = Action::kRead;
|
|
753
|
+
return Status(Status::kOkCompleted);
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
Status AlphaDemuxer::Impl::OnSeek(const ElementMetadata&, const Seek& seek) {
|
|
757
|
+
if (seek.id.value() == Id::kCues && seek.position.is_present() && cuesPosition_ < 0 && segmentDataStart_ >= 0) {
|
|
758
|
+
cuesPosition_ = segmentDataStart_ + static_cast<int64_t>(seek.position.value());
|
|
759
|
+
}
|
|
760
|
+
return Status(Status::kOkCompleted);
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
Status AlphaDemuxer::Impl::OnInfo(const ElementMetadata&, const webm::Info& info) {
|
|
764
|
+
if (info.timecode_scale.value() > 0) timecodeScale_ = info.timecode_scale.value();
|
|
765
|
+
if (info.duration.is_present()) durationTicks_ = info.duration.value();
|
|
766
|
+
return Status(Status::kOkCompleted);
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
Status AlphaDemuxer::Impl::OnTrackEntry(const ElementMetadata&, const TrackEntry& entry) {
|
|
770
|
+
if (entry.track_type.value() != TrackType::kVideo) return Status(Status::kOkCompleted);
|
|
771
|
+
const std::string& codecId = entry.codec_id.value();
|
|
772
|
+
if (codecId != "V_VP9" && codecId != "V_AV1" && codecId != "V_VP8") return Status(Status::kOkCompleted);
|
|
773
|
+
Track* track = !colour_.number ? &colour_ : !alpha_.number ? &alpha_ : nullptr;
|
|
774
|
+
if (track && entry.track_number.value() > 0) {
|
|
775
|
+
track->number = entry.track_number.value();
|
|
776
|
+
track->codec = codecId == "V_AV1" ? AlphaDemuxer::AV1
|
|
777
|
+
: codecId == "V_VP8" ? AlphaDemuxer::VP8
|
|
778
|
+
: AlphaDemuxer::VP9;
|
|
779
|
+
track->entry = entry;
|
|
780
|
+
}
|
|
781
|
+
return Status(Status::kOkCompleted);
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
Status AlphaDemuxer::Impl::OnClusterBegin(const ElementMetadata& metadata, const Cluster& cluster, Action* action) {
|
|
785
|
+
if (firstClusterPosition_ < 0) {
|
|
786
|
+
firstClusterPosition_ = static_cast<int64_t>(metadata.position);
|
|
787
|
+
// No track to read: opening stops here
|
|
788
|
+
if (!colour_.number) return Status(Status::kWouldBlock);
|
|
789
|
+
}
|
|
790
|
+
clusterPosition_ = metadata.position;
|
|
791
|
+
clusterTicks_ = static_cast<int64_t>(cluster.timecode.value());
|
|
792
|
+
*action = Action::kRead;
|
|
793
|
+
return Status(Status::kOkCompleted);
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
void AlphaDemuxer::Impl::beginBlock(uint64_t trackNumber, int16_t timecode) {
|
|
797
|
+
stream_ = trackNumber == colour_.number ? 0 : (alpha_.number && trackNumber == alpha_.number) ? 1 : -1;
|
|
798
|
+
blockTicks_ = clusterTicks_ + timecode;
|
|
799
|
+
frames_.clear();
|
|
800
|
+
framePosition_ = kNoPosition;
|
|
801
|
+
discard_ = mode_ == Mode::kScan || stream_ < 0;
|
|
802
|
+
// Like FFmpeg, blocks of any track end the skip after a seek
|
|
803
|
+
if (mode_ != Mode::kScan && skipBefore_ != kNone) {
|
|
804
|
+
if (blockTicks_ < skipBefore_) {
|
|
805
|
+
discard_ = true;
|
|
806
|
+
} else {
|
|
807
|
+
skipBefore_ = kNone;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
Status AlphaDemuxer::Impl::OnSimpleBlockBegin(const ElementMetadata&, const SimpleBlock& block, Action* action) {
|
|
813
|
+
beginBlock(block.track_number, block.timecode);
|
|
814
|
+
key_ = block.is_key_frame;
|
|
815
|
+
*action = discard_ ? Action::kSkip : Action::kRead;
|
|
816
|
+
return stream_ == 0 ? noteColourBlock() : Status(Status::kOkCompleted);
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
Status AlphaDemuxer::Impl::OnSimpleBlockEnd(const ElementMetadata&, const SimpleBlock&) {
|
|
820
|
+
if (pausedAtBlockEnd_) {
|
|
821
|
+
pausedAtBlockEnd_ = false;
|
|
822
|
+
return Status(Status::kOkCompleted);
|
|
823
|
+
}
|
|
824
|
+
return finishBlock(nullptr);
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
Status AlphaDemuxer::Impl::OnBlockGroupBegin(const ElementMetadata&, Action* action) {
|
|
828
|
+
inBlockGroup_ = false;
|
|
829
|
+
*action = Action::kRead;
|
|
830
|
+
return Status(Status::kOkCompleted);
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
Status AlphaDemuxer::Impl::OnBlockBegin(const ElementMetadata&, const Block& block, Action* action) {
|
|
834
|
+
beginBlock(block.track_number, block.timecode);
|
|
835
|
+
inBlockGroup_ = true;
|
|
836
|
+
*action = discard_ ? Action::kSkip : Action::kRead;
|
|
837
|
+
return Status(Status::kOkCompleted);
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
Status AlphaDemuxer::Impl::OnBlockGroupEnd(const ElementMetadata&, const BlockGroup& group) {
|
|
841
|
+
if (pausedAtBlockEnd_) {
|
|
842
|
+
pausedAtBlockEnd_ = false;
|
|
843
|
+
return Status(Status::kOkCompleted);
|
|
844
|
+
}
|
|
845
|
+
if (!inBlockGroup_) return Status(Status::kOkCompleted);
|
|
846
|
+
inBlockGroup_ = false;
|
|
847
|
+
key_ = group.references.empty();
|
|
848
|
+
if (stream_ == 0) {
|
|
849
|
+
const Status status = noteColourBlock();
|
|
850
|
+
if (!status.completed_ok()) return status;
|
|
851
|
+
}
|
|
852
|
+
const std::vector<uint8_t>* blockAdditional = nullptr;
|
|
853
|
+
if (group.additions.is_present()) {
|
|
854
|
+
for (const Element<BlockMore>& more : group.additions.value().block_mores) {
|
|
855
|
+
if (more.value().id.value() == 1) {
|
|
856
|
+
blockAdditional = &more.value().data.value();
|
|
857
|
+
break;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
return finishBlock(blockAdditional);
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
Status AlphaDemuxer::Impl::OnFrame(const FrameMetadata& metadata, Reader* reader, std::uint64_t* bytesRemaining) {
|
|
865
|
+
if (discard_) return Skip(reader, bytesRemaining);
|
|
866
|
+
if (metadata.position != framePosition_) {
|
|
867
|
+
if (metadata.size > kMaxFrameSize) return Status(Status::kNotEnoughMemory);
|
|
868
|
+
framePosition_ = metadata.position;
|
|
869
|
+
frames_.emplace_back(static_cast<size_t>(metadata.size));
|
|
870
|
+
}
|
|
871
|
+
std::vector<uint8_t>& frame = frames_.back();
|
|
872
|
+
while (*bytesRemaining > 0) {
|
|
873
|
+
std::uint64_t n = 0;
|
|
874
|
+
const Status status = reader->Read(static_cast<size_t>(*bytesRemaining),
|
|
875
|
+
frame.data() + (frame.size() - *bytesRemaining), &n);
|
|
876
|
+
*bytesRemaining -= n;
|
|
877
|
+
if (!status.ok()) return status;
|
|
878
|
+
}
|
|
879
|
+
return Status(Status::kOkCompleted);
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
Status AlphaDemuxer::Impl::OnCuePoint(const ElementMetadata&, const CuePoint& cuePoint) {
|
|
883
|
+
addCuePoint(cuePoint);
|
|
884
|
+
return Status(Status::kOkCompleted);
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
// Indexes a colour block once its keyframe flag is known; stops a scan past its target.
|
|
888
|
+
Status AlphaDemuxer::Impl::noteColourBlock() {
|
|
889
|
+
if (key_) keyframes_.emplace(blockTicks_, clusterPosition_);
|
|
890
|
+
if (readingFromStart_ && (indexedUpTo_ == kNone || blockTicks_ > indexedUpTo_)) indexedUpTo_ = blockTicks_;
|
|
891
|
+
if (mode_ == Mode::kScan && blockTicks_ > scanUntil_) {
|
|
892
|
+
scanStopped_ = true;
|
|
893
|
+
return Status(Status::kWouldBlock);
|
|
894
|
+
}
|
|
895
|
+
return Status(Status::kOkCompleted);
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
// Turns the block just read into packets and pauses the parser, so that readPacket returns them.
|
|
899
|
+
Status AlphaDemuxer::Impl::finishBlock(const std::vector<uint8_t>* blockAdditional) {
|
|
900
|
+
if (discard_ || frames_.empty()) return Status(Status::kOkCompleted);
|
|
901
|
+
if (stream_ == 0 && colourNeedsKey_) {
|
|
902
|
+
if (!key_) return Status(Status::kOkCompleted);
|
|
903
|
+
colourNeedsKey_ = false;
|
|
904
|
+
}
|
|
905
|
+
if (mode_ == Mode::kProbe) return keepProbePackets(blockAdditional);
|
|
906
|
+
|
|
907
|
+
const int64_t ptsUs = ticksToUs(blockTicks_);
|
|
908
|
+
for (std::vector<uint8_t>& frame : frames_) {
|
|
909
|
+
if (stream_ == 1) {
|
|
910
|
+
queue_.push_back({1, std::move(frame), ptsUs, alphaFrameIndex_++, key_});
|
|
911
|
+
continue;
|
|
912
|
+
}
|
|
913
|
+
queue_.push_back({0, std::move(frame), ptsUs, colourFrameIndex_++, key_});
|
|
914
|
+
if (info_.alphaInBlockAdditional) {
|
|
915
|
+
// Empty when the block has no alpha
|
|
916
|
+
std::vector<uint8_t> alpha = blockAdditional ? *blockAdditional : std::vector<uint8_t>();
|
|
917
|
+
const int size = static_cast<int>(alpha.size());
|
|
918
|
+
const bool key = info_.color.codec == AlphaDemuxer::VP8 ? isVp8Keyframe(alpha.data(), size)
|
|
919
|
+
: isVp9Keyframe(alpha.data(), size);
|
|
920
|
+
queue_.push_back({1, std::move(alpha), ptsUs, alphaFrameIndex_++, key});
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
frames_.clear();
|
|
924
|
+
pausedAtBlockEnd_ = true;
|
|
925
|
+
return Status(Status::kWouldBlock);
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
Status AlphaDemuxer::Impl::keepProbePackets(const std::vector<uint8_t>* blockAdditional) {
|
|
929
|
+
if (stream_ == 0) {
|
|
930
|
+
if (!haveColourPacket_) {
|
|
931
|
+
haveColourPacket_ = true;
|
|
932
|
+
firstColourPacket_ = frames_.front();
|
|
933
|
+
firstColourHasBlockAdditional_ = blockAdditional && !blockAdditional->empty();
|
|
934
|
+
startTicks_ = blockTicks_;
|
|
935
|
+
}
|
|
936
|
+
if (colourPtsSamples_.size() < kFrameRateSamples) colourPtsSamples_.push_back(blockTicks_);
|
|
937
|
+
} else if (!haveAlphaPacket_) {
|
|
938
|
+
haveAlphaPacket_ = true;
|
|
939
|
+
firstAlphaPacket_ = frames_.front();
|
|
940
|
+
}
|
|
941
|
+
frames_.clear();
|
|
942
|
+
if (!probeComplete()) return Status(Status::kOkCompleted);
|
|
943
|
+
pausedAtBlockEnd_ = true;
|
|
944
|
+
return Status(Status::kWouldBlock);
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
// --- AlphaDemuxer --------------------------------------------------------------------------
|
|
948
|
+
|
|
949
|
+
AlphaDemuxer::AlphaDemuxer(std::unique_ptr<Impl> impl) : impl_(std::move(impl)) {}
|
|
950
|
+
AlphaDemuxer::~AlphaDemuxer() = default;
|
|
951
|
+
|
|
952
|
+
std::unique_ptr<AlphaDemuxer> AlphaDemuxer::open(std::unique_ptr<ByteSource> source) {
|
|
953
|
+
std::unique_ptr<AlphaDemuxer> demuxer = create(std::move(source));
|
|
954
|
+
if (!demuxer || demuxer->tryOpen() != Progress::kDone) return nullptr;
|
|
955
|
+
return demuxer;
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
std::unique_ptr<AlphaDemuxer> AlphaDemuxer::create(std::unique_ptr<ByteSource> source) {
|
|
959
|
+
if (!source) return nullptr;
|
|
960
|
+
return std::unique_ptr<AlphaDemuxer>(new AlphaDemuxer(std::make_unique<Impl>(std::move(source))));
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
std::vector<int> AlphaDemuxer::colorAlphaTracks(std::unique_ptr<ByteSource> source) {
|
|
964
|
+
std::vector<int> tracks;
|
|
965
|
+
const std::unique_ptr<AlphaDemuxer> demuxer = open(std::move(source));
|
|
966
|
+
if (!demuxer) return tracks;
|
|
967
|
+
const Info& info = demuxer->info();
|
|
968
|
+
tracks = {info.color.codec, info.color.profile};
|
|
969
|
+
if (info.hasAlpha) {
|
|
970
|
+
tracks.push_back(info.alpha.codec);
|
|
971
|
+
tracks.push_back(info.alpha.profile);
|
|
972
|
+
}
|
|
973
|
+
return tracks;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
AlphaDemuxer::Progress AlphaDemuxer::tryOpen() { return impl_->tryOpen(); }
|
|
977
|
+
const AlphaDemuxer::Info& AlphaDemuxer::info() const { return impl_->info(); }
|
|
978
|
+
bool AlphaDemuxer::readPacket(Packet* out) { return impl_->readPacket(out); }
|
|
979
|
+
bool AlphaDemuxer::failed() const { return impl_->failed(); }
|
|
980
|
+
bool AlphaDemuxer::pending() const { return impl_->pending(); }
|
|
981
|
+
void AlphaDemuxer::seekToStart() { impl_->seekToStart(); }
|
|
982
|
+
void AlphaDemuxer::seekToTimeUs(int64_t timeUs) { impl_->seekToTimeUs(timeUs); }
|
|
983
|
+
|
|
984
|
+
} // namespace webm
|