Repository: keijiro/KlakHap
Branch: master
Commit: 7212ffe5e402
Files: 187
Total size: 582.0 KB
Directory structure:
gitextract_73gfcozr/
├── .gitattributes
├── .gitignore
├── AGENTS.md
├── Assets/
│ ├── Demo/
│ │ ├── Hap.mat
│ │ ├── Hap.mat.meta
│ │ ├── Hap.renderTexture
│ │ ├── Hap.renderTexture.meta
│ │ ├── HapAlpha.mat
│ │ ├── HapAlpha.mat.meta
│ │ ├── HapQ.mat
│ │ ├── HapQ.mat.meta
│ │ ├── Manifest.asset
│ │ ├── Manifest.asset.meta
│ │ ├── ScriptingTest.cs
│ │ ├── ScriptingTest.cs.meta
│ │ ├── ScriptingTest.renderTexture
│ │ ├── ScriptingTest.renderTexture.meta
│ │ ├── SeekBarBind.cs
│ │ ├── SeekBarBind.cs.meta
│ │ ├── UITK/
│ │ │ ├── DefaultTheme.tss
│ │ │ ├── DefaultTheme.tss.meta
│ │ │ ├── Demo.uxml
│ │ │ ├── Demo.uxml.meta
│ │ │ ├── PanelSettings.asset
│ │ │ └── PanelSettings.asset.meta
│ │ ├── UITK.meta
│ │ ├── URP/
│ │ │ ├── DefaultRenderer.asset
│ │ │ ├── DefaultRenderer.asset.meta
│ │ │ ├── DefaultVolume.asset
│ │ │ ├── DefaultVolume.asset.meta
│ │ │ ├── GlobalSettings.asset
│ │ │ ├── GlobalSettings.asset.meta
│ │ │ ├── URP.asset
│ │ │ └── URP.asset.meta
│ │ └── URP.meta
│ ├── Demo.meta
│ ├── Demo.unity
│ ├── Demo.unity.meta
│ ├── StreamingAssets/
│ │ ├── Tests/
│ │ │ ├── Hap/
│ │ │ │ ├── 000001.png.meta
│ │ │ │ ├── 000002.png.meta
│ │ │ │ ├── 000003.png.meta
│ │ │ │ ├── 000004.png.meta
│ │ │ │ ├── 000005.png.meta
│ │ │ │ └── TestCards.mov.meta
│ │ │ ├── Hap.meta
│ │ │ ├── HapAlpha/
│ │ │ │ ├── 000001.png.meta
│ │ │ │ └── HapAlpha.mov.meta
│ │ │ ├── HapAlpha.meta
│ │ │ ├── HapQ/
│ │ │ │ ├── 000001.png.meta
│ │ │ │ ├── 000002.png.meta
│ │ │ │ ├── 000003.png.meta
│ │ │ │ ├── 000004.png.meta
│ │ │ │ ├── 000005.png.meta
│ │ │ │ └── TestCards.mov.meta
│ │ │ ├── HapQ.meta
│ │ │ ├── RGBCycle/
│ │ │ │ ├── RGBCycle24.mov.meta
│ │ │ │ ├── RGBCycle24000-1001.mov.meta
│ │ │ │ ├── RGBCycle25.mov.meta
│ │ │ │ ├── RGBCycle30.mov.meta
│ │ │ │ ├── RGBCycle30000-1001.mov.meta
│ │ │ │ ├── RGBCycle50.mov.meta
│ │ │ │ ├── RGBCycle60.mov.meta
│ │ │ │ └── RGBCycle60000-1001.mov.meta
│ │ │ ├── RGBCycle.meta
│ │ │ ├── 日本語/
│ │ │ │ └── Test.mov.meta
│ │ │ └── 日本語.meta
│ │ └── Tests.meta
│ ├── StreamingAssets.meta
│ ├── Tests/
│ │ ├── Runtime/
│ │ │ ├── HapAlphaPlaybackTest.cs
│ │ │ ├── HapAlphaPlaybackTest.cs.meta
│ │ │ ├── JapanesePathPlaybackTest.cs
│ │ │ ├── JapanesePathPlaybackTest.cs.meta
│ │ │ ├── Klak.Hap.Runtime.Tests.asmdef
│ │ │ ├── Klak.Hap.Runtime.Tests.asmdef.meta
│ │ │ ├── RgbCycleFrameMappingTest.cs
│ │ │ ├── RgbCycleFrameMappingTest.cs.meta
│ │ │ ├── TestCardsPlaybackTest.cs
│ │ │ └── TestCardsPlaybackTest.cs.meta
│ │ └── Runtime.meta
│ └── Tests.meta
├── CHANGELOG.md
├── LICENSE
├── Packages/
│ ├── jp.keijiro.klak.hap/
│ │ ├── CHANGELOG.md
│ │ ├── CHANGELOG.md.meta
│ │ ├── Editor/
│ │ │ ├── HapPlayerEditor.cs
│ │ │ ├── HapPlayerEditor.cs.meta
│ │ │ ├── Klak.Hap.Editor.asmdef
│ │ │ ├── Klak.Hap.Editor.asmdef.meta
│ │ │ ├── MaterialPropertySelector.cs
│ │ │ └── MaterialPropertySelector.cs.meta
│ │ ├── Editor.meta
│ │ ├── LICENSE
│ │ ├── LICENSE.meta
│ │ ├── Plugin/
│ │ │ ├── Linux/
│ │ │ │ └── libKlakHap.so.meta
│ │ │ ├── Linux.meta
│ │ │ ├── MacOS/
│ │ │ │ ├── KlakHap.bundle
│ │ │ │ └── KlakHap.bundle.meta
│ │ │ ├── MacOS.meta
│ │ │ ├── Windows/
│ │ │ │ └── KlakHap.dll.meta
│ │ │ └── Windows.meta
│ │ ├── Plugin.meta
│ │ ├── README.md
│ │ ├── README.md.meta
│ │ ├── Resources/
│ │ │ ├── Hap1.shader
│ │ │ ├── Hap1.shader.meta
│ │ │ ├── Hap5.shader
│ │ │ ├── Hap5.shader.meta
│ │ │ ├── HapY.shader
│ │ │ └── HapY.shader.meta
│ │ ├── Resources.meta
│ │ ├── Runtime/
│ │ │ ├── Common.cs
│ │ │ ├── Common.cs.meta
│ │ │ ├── HapPlayer.cs
│ │ │ ├── HapPlayer.cs.meta
│ │ │ ├── Internal/
│ │ │ │ ├── Decoder.cs
│ │ │ │ ├── Decoder.cs.meta
│ │ │ │ ├── Demuxer.cs
│ │ │ │ ├── Demuxer.cs.meta
│ │ │ │ ├── ReadBuffer.cs
│ │ │ │ ├── ReadBuffer.cs.meta
│ │ │ │ ├── StreamReader.cs
│ │ │ │ ├── StreamReader.cs.meta
│ │ │ │ ├── TextureUpdater.cs
│ │ │ │ ├── TextureUpdater.cs.meta
│ │ │ │ ├── Utility.cs
│ │ │ │ └── Utility.cs.meta
│ │ │ ├── Internal.meta
│ │ │ ├── Klak.Hap.asmdef
│ │ │ └── Klak.Hap.asmdef.meta
│ │ ├── Runtime.meta
│ │ ├── package.json
│ │ └── package.json.meta
│ ├── manifest.json
│ └── packages-lock.json
├── Plugin/
│ ├── Common.mk
│ ├── Hap/
│ │ ├── hap.c
│ │ └── hap.h
│ ├── MP4/
│ │ ├── mp4defs.h
│ │ ├── mp4demux.c
│ │ └── mp4demux.h
│ ├── Makefile.linux
│ ├── Makefile.macos
│ ├── Makefile.windows
│ ├── Snappy/
│ │ ├── snappy-c.cc
│ │ ├── snappy-c.h
│ │ ├── snappy-internal.h
│ │ ├── snappy-sinksource.cc
│ │ ├── snappy-sinksource.h
│ │ ├── snappy-stubs-internal.cc
│ │ ├── snappy-stubs-internal.h
│ │ ├── snappy-stubs-public.h
│ │ ├── snappy.cc
│ │ └── snappy.h
│ ├── Source/
│ │ ├── Decoder.h
│ │ ├── Demuxer.h
│ │ ├── KlakHap.cpp
│ │ └── ReadBuffer.h
│ ├── Unity/
│ │ ├── IUnityGraphics.h
│ │ ├── IUnityInterface.h
│ │ └── IUnityRenderingExtensions.h
│ └── build.macos.sh
├── ProjectSettings/
│ ├── AudioManager.asset
│ ├── ClusterInputManager.asset
│ ├── DynamicsManager.asset
│ ├── EditorBuildSettings.asset
│ ├── EditorSettings.asset
│ ├── GraphicsSettings.asset
│ ├── InputManager.asset
│ ├── MemorySettings.asset
│ ├── MultiplayerManager.asset
│ ├── NavMeshAreas.asset
│ ├── PackageManagerSettings.asset
│ ├── Packages/
│ │ └── com.unity.dedicated-server/
│ │ └── MultiplayerRolesSettings.asset
│ ├── Physics2DSettings.asset
│ ├── PresetManager.asset
│ ├── ProjectSettings.asset
│ ├── ProjectVersion.txt
│ ├── QualitySettings.asset
│ ├── SceneTemplateSettings.json
│ ├── ShaderGraphSettings.asset
│ ├── TagManager.asset
│ ├── TimeManager.asset
│ ├── URPProjectSettings.asset
│ ├── UnityConnectSettings.asset
│ ├── VFXManager.asset
│ └── VersionControlSettings.asset
└── README.md
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
* -text
*.cs text eol=lf diff=csharp
*.shader text eol=lf
*.cginc text eol=lf
*.hlsl text eol=lf
*.compute text eol=lf
*.meta text eol=lf
*.h text eol=lf
*.cpp text eol=lf
*.cc text eol=lf
*.c text eol=lf
*.plist text eol=lf
*.sln text eol=crlf
*.vcxproj text eol=crlf
*.vcxproj.filters text eol=crlf
================================================
FILE: .gitignore
================================================
# Windows
Thumbs.db
Desktop.ini
# macOS
.DS_Store
# Code Editors
/.idea
/.vscode
/*.csproj
/*.sln
*.swp
*.vcxproj.user
# Unity
/Library
/Logs
/Recordings
/Temp
/UIElementsSchema
/UserSettings
# Visual Studio
/Plugin/Windows/.vs
/Plugin/Windows/x64
# Xcode
xcuserdata
xcshareddata
/Plugin/build-*
/Plugin/MacOS/arm64
/Plugin/MacOS/x86_64
/Plugin/MacOS/*.bundle
/Plugin/*.bundle
/*.tgz
/Assets/StreamingAssets/*.mov
/Assets/StreamingAssets/*.mov.meta
================================================
FILE: AGENTS.md
================================================
This repository contains a UPM Unity package project. Refer to the following
document for the workflow:
https://raw.githubusercontent.com/keijiro/AgentWorkflows/refs/heads/main/upm-workflow.md
================================================
FILE: Assets/Demo/Hap.mat
================================================
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-8186347925687542841
MonoBehaviour:
m_ObjectHideFlags: 11
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion
version: 10
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Hap
m_Shader: {fileID: 4800000, guid: 650dd9526735d5b46b79224bc6e94025, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap:
RenderType: Opaque
disabledShaderPasses:
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BaseMap:
m_Texture: {fileID: 8400000, guid: a4f22813f74e84455bd3d9e41d54ced6, type: 2}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 8400000, guid: a4f22813f74e84455bd3d9e41d54ced6, type: 2}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _SpecGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AddPrecomputedVelocity: 0
- _AlphaClip: 0
- _AlphaToMask: 0
- _Blend: 0
- _BlendModePreserveSpecular: 1
- _BlendOp: 0
- _BumpScale: 1
- _ClearCoatMask: 0
- _ClearCoatSmoothness: 0
- _Cull: 2
- _Cutoff: 0.5
- _DetailAlbedoMapScale: 1
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _DstBlendAlpha: 0
- _EnvironmentReflections: 1
- _GlossMapScale: 0
- _Glossiness: 0
- _GlossyReflections: 0
- _Metallic: 0
- _OcclusionStrength: 1
- _Parallax: 0.005
- _QueueOffset: 0
- _ReceiveShadows: 1
- _SampleGI: 0
- _Smoothness: 0.5
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _SrcBlendAlpha: 1
- _Surface: 0
- _WorkflowMode: 1
- _XRMotionVectorsPass: 1
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 1, g: 1, b: 1, a: 1}
- _Color: {r: 1, g: 1, b: 1, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1
================================================
FILE: Assets/Demo/Hap.mat.meta
================================================
fileFormatVersion: 2
guid: e127fa53264f341caab6ab276ff39b77
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/Demo/Hap.renderTexture
================================================
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!84 &8400000
RenderTexture:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Hap
m_ImageContentsHash:
serializedVersion: 2
Hash: 00000000000000000000000000000000
m_IsAlphaChannelOptional: 0
serializedVersion: 6
m_Width: 1920
m_Height: 1080
m_AntiAliasing: 1
m_MipCount: -1
m_DepthStencilFormat: 0
m_ColorFormat: 4
m_MipMap: 0
m_GenerateMips: 1
m_SRGB: 1
m_UseDynamicScale: 0
m_UseDynamicScaleExplicit: 0
m_BindMS: 0
m_EnableCompatibleFormat: 1
m_EnableRandomWrite: 0
m_TextureSettings:
serializedVersion: 2
m_FilterMode: 2
m_Aniso: 8
m_MipBias: 0
m_WrapU: 1
m_WrapV: 1
m_WrapW: 1
m_Dimension: 2
m_VolumeDepth: 1
m_ShadowSamplingMode: 2
================================================
FILE: Assets/Demo/Hap.renderTexture.meta
================================================
fileFormatVersion: 2
guid: a4f22813f74e84455bd3d9e41d54ced6
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 8400000
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/Demo/HapAlpha.mat
================================================
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: HapAlpha
m_Shader: {fileID: 4800000, guid: 650dd9526735d5b46b79224bc6e94025, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords:
- _SURFACE_TYPE_TRANSPARENT
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: 3000
stringTagMap:
RenderType: Transparent
disabledShaderPasses:
- MOTIONVECTORS
- DepthOnly
- SHADOWCASTER
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BaseMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: -1}
m_Offset: {x: 0, y: 1}
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: -1}
m_Offset: {x: 0, y: 1}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _SpecGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AddPrecomputedVelocity: 0
- _AlphaClip: 0
- _AlphaToMask: 0
- _Blend: 0
- _BlendModePreserveSpecular: 1
- _BlendOp: 0
- _BumpScale: 1
- _ClearCoatMask: 0
- _ClearCoatSmoothness: 0
- _Cull: 2
- _Cutoff: 0.5
- _DetailAlbedoMapScale: 1
- _DetailNormalMapScale: 1
- _DstBlend: 10
- _DstBlendAlpha: 10
- _EnvironmentReflections: 1
- _GlossMapScale: 0
- _Glossiness: 0
- _GlossyReflections: 0
- _Metallic: 0
- _OcclusionStrength: 1
- _Parallax: 0.005
- _QueueOffset: 0
- _ReceiveShadows: 1
- _SampleGI: 0
- _Smoothness: 0.5
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 5
- _SrcBlendAlpha: 1
- _Surface: 1
- _WorkflowMode: 1
- _XRMotionVectorsPass: 1
- _ZWrite: 0
m_Colors:
- _BaseColor: {r: 1, g: 1, b: 1, a: 1}
- _Color: {r: 1, g: 1, b: 1, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1
--- !u!114 &4974996350567476565
MonoBehaviour:
m_ObjectHideFlags: 11
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion
version: 10
================================================
FILE: Assets/Demo/HapAlpha.mat.meta
================================================
fileFormatVersion: 2
guid: 6f1182baee9ac4863bd4c5ea5f7d536d
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/Demo/HapQ.mat
================================================
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-1545691033646063671
MonoBehaviour:
m_ObjectHideFlags: 11
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion
version: 10
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: HapQ
m_Shader: {fileID: 4800000, guid: c6a332340608cdd4f907eb4bc0c17f4a, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses:
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BaseMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _SpecGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AddPrecomputedVelocity: 0
- _AlphaClip: 0
- _AlphaToMask: 0
- _Blend: 0
- _BlendModePreserveSpecular: 1
- _BumpScale: 1
- _ClearCoatMask: 0
- _ClearCoatSmoothness: 0
- _Cull: 2
- _Cutoff: 0.5
- _DetailAlbedoMapScale: 1
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _DstBlendAlpha: 0
- _EnvironmentReflections: 1
- _GlossMapScale: 0
- _Glossiness: 0
- _GlossyReflections: 0
- _Metallic: 0
- _OcclusionStrength: 1
- _Parallax: 0.005
- _QueueOffset: 0
- _ReceiveShadows: 1
- _Smoothness: 0.5
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _SrcBlendAlpha: 1
- _Surface: 0
- _WorkflowMode: 1
- _XRMotionVectorsPass: 1
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 1, g: 1, b: 1, a: 1}
- _Color: {r: 1, g: 1, b: 1, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1
================================================
FILE: Assets/Demo/HapQ.mat.meta
================================================
fileFormatVersion: 2
guid: f4060b9dfe1254ee9bb9d4fad7ae4ebd
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/Demo/Manifest.asset
================================================
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5acc6266be6eb4130afc5134ec35fcec, type: 3}
m_Name: Manifest
m_EditorClassIdentifier: KlutterTools.Editor::KlutterTools.Downloader.Manifest
k__BackingField: 'Test video files are missing:'
k__BackingField:
- k__BackingField: https://huggingface.co/keijiro-tk/test-videos/resolve/main/Beeple-CleanRoom-Hap.mov
k__BackingField: Assets/StreamingAssets
- k__BackingField: https://huggingface.co/keijiro-tk/test-videos/resolve/main/Beeple-FiberOptical-Hap.mov
k__BackingField: Assets/StreamingAssets
- k__BackingField: https://huggingface.co/keijiro-tk/test-videos/resolve/main/Beeple-Hexxx-HapQ.mov
k__BackingField: Assets/StreamingAssets
- k__BackingField: https://huggingface.co/keijiro-tk/test-videos/resolve/main/TransparencyTest-HapAlpha.mov
k__BackingField: Assets/StreamingAssets
================================================
FILE: Assets/Demo/Manifest.asset.meta
================================================
fileFormatVersion: 2
guid: 3dac4bcd1e58c42f1b91f538480ebc79
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/Demo/ScriptingTest.cs
================================================
using UnityEngine;
using Klak.Hap;
public sealed class ScriptingTest : MonoBehaviour
{
[SerializeField] string _filename = "Test.mov";
[SerializeField] RenderTexture _destination = null;
[SerializeField] float _interval = 1;
[SerializeField] int _iteration = 10;
async void Start()
{
// Create a HAP player instance.
var player = gameObject.AddComponent();
// Open the specified HAP video file.
player.Open(_filename);
// Route playback to the destination render texture.
player.targetTexture = _destination;
// Step through playback changes.
for (var i = 0; i < _iteration; i++)
{
// Jump to a random timestamp after each interval.
await Awaitable.WaitForSecondsAsync(_interval);
player.time = Random.Range(0, (float)player.streamDuration - _interval);
}
// Clean up the player component.
Destroy(player);
}
}
================================================
FILE: Assets/Demo/ScriptingTest.cs.meta
================================================
fileFormatVersion: 2
guid: 2c91f3289fa694fe3ba2e1e6149477b8
================================================
FILE: Assets/Demo/ScriptingTest.renderTexture
================================================
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!84 &8400000
RenderTexture:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: ScriptingTest
m_ImageContentsHash:
serializedVersion: 2
Hash: 00000000000000000000000000000000
m_IsAlphaChannelOptional: 0
serializedVersion: 6
m_Width: 1920
m_Height: 1080
m_AntiAliasing: 1
m_MipCount: -1
m_DepthStencilFormat: 0
m_ColorFormat: 4
m_MipMap: 0
m_GenerateMips: 1
m_SRGB: 1
m_UseDynamicScale: 0
m_UseDynamicScaleExplicit: 0
m_BindMS: 0
m_EnableCompatibleFormat: 1
m_EnableRandomWrite: 0
m_TextureSettings:
serializedVersion: 2
m_FilterMode: 1
m_Aniso: 0
m_MipBias: 0
m_WrapU: 1
m_WrapV: 1
m_WrapW: 1
m_Dimension: 2
m_VolumeDepth: 1
m_ShadowSamplingMode: 2
================================================
FILE: Assets/Demo/ScriptingTest.renderTexture.meta
================================================
fileFormatVersion: 2
guid: 1bba849debc024a4f8251bd62cceac60
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 8400000
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/Demo/SeekBarBind.cs
================================================
using Unity.Properties;
using UnityEngine;
using UnityEngine.UIElements;
using Klak.Hap;
public sealed class SeekBarBind : MonoBehaviour
{
[SerializeField] UIDocument _ui = null;
HapPlayer _player;
[CreateProperty]
public float SeekPoint
{ get => _player.time;
set => _player.time = value; }
void Start()
{
_player = GetComponent();
var slider = _ui.rootVisualElement.Q("seek-bar");
slider.dataSource = this;
slider.highValue = (float)_player.streamDuration;
}
}
================================================
FILE: Assets/Demo/SeekBarBind.cs.meta
================================================
fileFormatVersion: 2
guid: 538bb1d7707cb473f8493b44c63286cc
================================================
FILE: Assets/Demo/UITK/DefaultTheme.tss
================================================
@import url("unity-theme://default");
================================================
FILE: Assets/Demo/UITK/DefaultTheme.tss.meta
================================================
fileFormatVersion: 2
guid: affeaa67622c24c3991a769c52b9c780
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 12388, guid: 0000000000000000e000000000000000, type: 0}
disableValidation: 0
unsupportedSelectorAction: 0
================================================
FILE: Assets/Demo/UITK/Demo.uxml
================================================
================================================
FILE: Assets/Demo/UITK/Demo.uxml.meta
================================================
fileFormatVersion: 2
guid: f9d1722b31b6d45a8b38e8167775d54d
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}
================================================
FILE: Assets/Demo/UITK/PanelSettings.asset
================================================
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 19101, guid: 0000000000000000e000000000000000, type: 0}
m_Name: PanelSettings
m_EditorClassIdentifier: UnityEngine.dll::UnityEngine.UIElements.PanelSettings
themeUss: {fileID: -4733365628477956816, guid: affeaa67622c24c3991a769c52b9c780, type: 3}
m_DisableNoThemeWarning: 0
m_TargetTexture: {fileID: 0}
m_RenderMode: 0
m_ColliderUpdateMode: 0
m_ColliderIsTrigger: 1
m_ScaleMode: 1
m_ReferenceSpritePixelsPerUnit: 100
m_PixelsPerUnit: 100
m_Scale: 1
m_ReferenceDpi: 96
m_FallbackDpi: 96
m_ReferenceResolution: {x: 1200, y: 800}
m_ScreenMatchMode: 0
m_Match: 0
m_SortingOrder: 0
m_TargetDisplay: 0
m_BindingLogLevel: 0
m_ClearDepthStencil: 1
m_ClearColor: 0
m_ColorClearValue: {r: 0, g: 0, b: 0, a: 0}
m_VertexBudget: 0
m_TextureSlotCount: 8
m_DynamicAtlasSettings:
m_MinAtlasSize: 64
m_MaxAtlasSize: 4096
m_MaxSubTextureSize: 64
m_ActiveFilters: -1
m_AtlasBlitShader: {fileID: 9101, guid: 0000000000000000f000000000000000, type: 0}
m_DefaultShader: {fileID: 9100, guid: 0000000000000000f000000000000000, type: 0}
m_RuntimeGaussianBlurShader: {fileID: 20300, guid: 0000000000000000f000000000000000, type: 0}
m_RuntimeColorEffectShader: {fileID: 20301, guid: 0000000000000000f000000000000000, type: 0}
m_SDFShader: {fileID: 19011, guid: 0000000000000000f000000000000000, type: 0}
m_BitmapShader: {fileID: 9001, guid: 0000000000000000f000000000000000, type: 0}
m_SpriteShader: {fileID: 19012, guid: 0000000000000000f000000000000000, type: 0}
m_ICUDataAsset: {fileID: 0}
forceGammaRendering: 0
textSettings: {fileID: 0}
================================================
FILE: Assets/Demo/UITK/PanelSettings.asset.meta
================================================
fileFormatVersion: 2
guid: 217443c668dc34029b583fa73e5c4dfc
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/Demo/UITK.meta
================================================
fileFormatVersion: 2
guid: 231a2176bf9354a2aaa754fd16e8e95e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/Demo/URP/DefaultRenderer.asset
================================================
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: de640fe3d0db1804a85f9fc8f5cadab6, type: 3}
m_Name: DefaultRenderer
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.UniversalRendererData
debugShaders:
debugReplacementPS: {fileID: 4800000, guid: cf852408f2e174538bcd9b7fda1c5ae7, type: 3}
hdrDebugViewPS: {fileID: 4800000, guid: 573620ae32aec764abd4d728906d2587, type: 3}
probeVolumeSamplingDebugComputeShader: {fileID: 7200000, guid: 53626a513ea68ce47b59dc1299fe3959, type: 3}
probeVolumeResources:
probeVolumeDebugShader: {fileID: 0}
probeVolumeFragmentationDebugShader: {fileID: 0}
probeVolumeOffsetDebugShader: {fileID: 0}
probeVolumeSamplingDebugShader: {fileID: 0}
probeSamplingDebugMesh: {fileID: 0}
probeSamplingDebugTexture: {fileID: 0}
probeVolumeBlendStatesCS: {fileID: 0}
m_RendererFeatures: []
m_RendererFeatureMap:
m_UseNativeRenderPass: 0
postProcessData: {fileID: 11400000, guid: 41439944d30ece34e96484bdb6645b55, type: 2}
m_AssetVersion: 3
m_PrepassLayerMask:
serializedVersion: 2
m_Bits: 4294967295
m_OpaqueLayerMask:
serializedVersion: 2
m_Bits: 4294967295
m_TransparentLayerMask:
serializedVersion: 2
m_Bits: 4294967295
m_DefaultStencilState:
overrideStencilState: 0
stencilReference: 0
stencilCompareFunction: 8
passOperation: 2
failOperation: 0
zFailOperation: 0
m_ShadowTransparentReceive: 1
m_RenderingMode: 0
m_DepthPrimingMode: 0
m_CopyDepthMode: 1
m_DepthAttachmentFormat: 0
m_DepthTextureFormat: 0
m_AccurateGbufferNormals: 0
m_IntermediateTextureMode: 1
================================================
FILE: Assets/Demo/URP/DefaultRenderer.asset.meta
================================================
fileFormatVersion: 2
guid: e707f25f3295f48408bbc74cefc67e0d
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/Demo/URP/DefaultVolume.asset
================================================
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-8324776220157134924
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5485954d14dfb9a4c8ead8edb0ded5b1, type: 3}
m_Name: LiftGammaGain
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.LiftGammaGain
active: 1
lift:
m_OverrideState: 1
m_Value: {x: 1, y: 1, z: 1, w: 0}
gamma:
m_OverrideState: 1
m_Value: {x: 1, y: 1, z: 1, w: 0}
gain:
m_OverrideState: 1
m_Value: {x: 1, y: 1, z: 1, w: 0}
--- !u!114 &-4854300222326862063
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 70afe9e12c7a7ed47911bb608a23a8ff, type: 3}
m_Name: SplitToning
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.SplitToning
active: 1
shadows:
m_OverrideState: 1
m_Value: {r: 0.5, g: 0.5, b: 0.5, a: 1}
highlights:
m_OverrideState: 1
m_Value: {r: 0.5, g: 0.5, b: 0.5, a: 1}
balance:
m_OverrideState: 1
m_Value: 0
--- !u!114 &-4422322214415224032
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 899c54efeace73346a0a16faa3afe726, type: 3}
m_Name: Vignette
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.Vignette
active: 1
color:
m_OverrideState: 1
m_Value: {r: 0, g: 0, b: 0, a: 1}
center:
m_OverrideState: 1
m_Value: {x: 0.5, y: 0.5}
intensity:
m_OverrideState: 1
m_Value: 0
smoothness:
m_OverrideState: 1
m_Value: 0.2
rounded:
m_OverrideState: 1
m_Value: 0
--- !u!114 &-4084026468743712817
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 81180773991d8724ab7f2d216912b564, type: 3}
m_Name: ChromaticAberration
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.ChromaticAberration
active: 1
intensity:
m_OverrideState: 1
m_Value: 0
--- !u!114 &-4056193433038019785
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fb60a22f311433c4c962b888d1393f88, type: 3}
m_Name: PaniniProjection
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.PaniniProjection
active: 1
distance:
m_OverrideState: 1
m_Value: 0
cropToFit:
m_OverrideState: 1
m_Value: 1
--- !u!114 &-1795136986949859044
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 06437c1ff663d574d9447842ba0a72e4, type: 3}
m_Name: ScreenSpaceLensFlare
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.ScreenSpaceLensFlare
active: 1
intensity:
m_OverrideState: 1
m_Value: 0
tintColor:
m_OverrideState: 1
m_Value: {r: 1, g: 1, b: 1, a: 1}
bloomMip:
m_OverrideState: 1
m_Value: 1
firstFlareIntensity:
m_OverrideState: 1
m_Value: 1
secondaryFlareIntensity:
m_OverrideState: 1
m_Value: 1
warpedFlareIntensity:
m_OverrideState: 1
m_Value: 1
warpedFlareScale:
m_OverrideState: 1
m_Value: {x: 1, y: 1}
samples:
m_OverrideState: 1
m_Value: 1
sampleDimmer:
m_OverrideState: 1
m_Value: 0.5
vignetteEffect:
m_OverrideState: 1
m_Value: 1
startingPosition:
m_OverrideState: 1
m_Value: 1.25
scale:
m_OverrideState: 1
m_Value: 1.5
streaksIntensity:
m_OverrideState: 1
m_Value: 0
streaksLength:
m_OverrideState: 1
m_Value: 0.5
streaksOrientation:
m_OverrideState: 1
m_Value: 0
streaksThreshold:
m_OverrideState: 1
m_Value: 0.25
resolution:
m_OverrideState: 1
m_Value: 4
chromaticAbberationIntensity:
m_OverrideState: 1
m_Value: 0.5
--- !u!114 &-598097793118719588
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: e021b4c809a781e468c2988c016ebbea, type: 3}
m_Name: ColorLookup
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.ColorLookup
active: 1
texture:
m_OverrideState: 1
m_Value: {fileID: 0}
dimension: 1
contribution:
m_OverrideState: 1
m_Value: 0
--- !u!114 &-491638309231600550
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 29fa0085f50d5e54f8144f766051a691, type: 3}
m_Name: FilmGrain
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.FilmGrain
active: 1
type:
m_OverrideState: 1
m_Value: 0
intensity:
m_OverrideState: 1
m_Value: 0
response:
m_OverrideState: 1
m_Value: 0.8
texture:
m_OverrideState: 1
m_Value: {fileID: 0}
--- !u!114 &-262398843821731637
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 0b2db86121404754db890f4c8dfe81b2, type: 3}
m_Name: Bloom
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.Bloom
active: 1
skipIterations:
m_OverrideState: 1
m_Value: 1
threshold:
m_OverrideState: 1
m_Value: 0.9
intensity:
m_OverrideState: 1
m_Value: 0
scatter:
m_OverrideState: 1
m_Value: 0.7
clamp:
m_OverrideState: 1
m_Value: 65472
tint:
m_OverrideState: 1
m_Value: {r: 1, g: 1, b: 1, a: 1}
highQualityFiltering:
m_OverrideState: 1
m_Value: 0
filter:
m_OverrideState: 1
m_Value: 0
downscale:
m_OverrideState: 1
m_Value: 0
maxIterations:
m_OverrideState: 1
m_Value: 6
dirtTexture:
m_OverrideState: 1
m_Value: {fileID: 0}
dimension: 1
dirtIntensity:
m_OverrideState: 1
m_Value: 0
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d7fd9488000d3734a9e00ee676215985, type: 3}
m_Name: DefaultVolume
m_EditorClassIdentifier: Unity.RenderPipelines.Core.Runtime::UnityEngine.Rendering.VolumeProfile
components:
- {fileID: 451328866785996911}
- {fileID: 8855959688280746896}
- {fileID: 4256406320919580142}
- {fileID: 2026760357700040227}
- {fileID: 8862907471677848668}
- {fileID: -4422322214415224032}
- {fileID: -8324776220157134924}
- {fileID: -1795136986949859044}
- {fileID: 3728775241584703705}
- {fileID: -4854300222326862063}
- {fileID: -262398843821731637}
- {fileID: 1801325758739867974}
- {fileID: -4056193433038019785}
- {fileID: -4084026468743712817}
- {fileID: 5078066985263128679}
- {fileID: -491638309231600550}
- {fileID: 1585749192924285193}
- {fileID: -598097793118719588}
- {fileID: 5892942527586337604}
--- !u!114 &451328866785996911
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 558a8e2b6826cf840aae193990ba9f2e, type: 3}
m_Name: ShadowsMidtonesHighlights
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.ShadowsMidtonesHighlights
active: 1
shadows:
m_OverrideState: 1
m_Value: {x: 1, y: 1, z: 1, w: 0}
midtones:
m_OverrideState: 1
m_Value: {x: 1, y: 1, z: 1, w: 0}
highlights:
m_OverrideState: 1
m_Value: {x: 1, y: 1, z: 1, w: 0}
shadowsStart:
m_OverrideState: 1
m_Value: 0
shadowsEnd:
m_OverrideState: 1
m_Value: 0.3
highlightsStart:
m_OverrideState: 1
m_Value: 0.55
highlightsEnd:
m_OverrideState: 1
m_Value: 1
--- !u!114 &1585749192924285193
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 221518ef91623a7438a71fef23660601, type: 3}
m_Name: WhiteBalance
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.WhiteBalance
active: 1
temperature:
m_OverrideState: 1
m_Value: 0
tint:
m_OverrideState: 1
m_Value: 0
--- !u!114 &1801325758739867974
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 66f335fb1ffd8684294ad653bf1c7564, type: 3}
m_Name: ColorAdjustments
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.ColorAdjustments
active: 1
postExposure:
m_OverrideState: 1
m_Value: 0
contrast:
m_OverrideState: 1
m_Value: 0
colorFilter:
m_OverrideState: 1
m_Value: {r: 1, g: 1, b: 1, a: 1}
hueShift:
m_OverrideState: 1
m_Value: 0
saturation:
m_OverrideState: 1
m_Value: 0
--- !u!114 &2026760357700040227
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c01700fd266d6914ababb731e09af2eb, type: 3}
m_Name: DepthOfField
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.DepthOfField
active: 1
mode:
m_OverrideState: 1
m_Value: 0
gaussianStart:
m_OverrideState: 1
m_Value: 10
gaussianEnd:
m_OverrideState: 1
m_Value: 30
gaussianMaxRadius:
m_OverrideState: 1
m_Value: 1
highQualitySampling:
m_OverrideState: 1
m_Value: 0
focusDistance:
m_OverrideState: 1
m_Value: 10
aperture:
m_OverrideState: 1
m_Value: 5.6
focalLength:
m_OverrideState: 1
m_Value: 50
bladeCount:
m_OverrideState: 1
m_Value: 5
bladeCurvature:
m_OverrideState: 1
m_Value: 1
bladeRotation:
m_OverrideState: 1
m_Value: 0
--- !u!114 &3728775241584703705
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c5e1dc532bcb41949b58bc4f2abfbb7e, type: 3}
m_Name: LensDistortion
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.LensDistortion
active: 1
intensity:
m_OverrideState: 1
m_Value: 0
xMultiplier:
m_OverrideState: 1
m_Value: 1
yMultiplier:
m_OverrideState: 1
m_Value: 1
center:
m_OverrideState: 1
m_Value: {x: 0.5, y: 0.5}
scale:
m_OverrideState: 1
m_Value: 1
--- !u!114 &4256406320919580142
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 3eb4b772797da9440885e8bd939e9560, type: 3}
m_Name: ColorCurves
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.ColorCurves
active: 1
master:
m_OverrideState: 1
m_Value:
k__BackingField: 2
m_Loop: 0
m_ZeroValue: 0
m_Range: 1
m_Curve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 1
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
- serializedVersion: 3
time: 1
value: 1
inSlope: 1
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
red:
m_OverrideState: 1
m_Value:
k__BackingField: 2
m_Loop: 0
m_ZeroValue: 0
m_Range: 1
m_Curve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 1
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
- serializedVersion: 3
time: 1
value: 1
inSlope: 1
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
green:
m_OverrideState: 1
m_Value:
k__BackingField: 2
m_Loop: 0
m_ZeroValue: 0
m_Range: 1
m_Curve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 1
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
- serializedVersion: 3
time: 1
value: 1
inSlope: 1
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
blue:
m_OverrideState: 1
m_Value:
k__BackingField: 2
m_Loop: 0
m_ZeroValue: 0
m_Range: 1
m_Curve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 1
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
- serializedVersion: 3
time: 1
value: 1
inSlope: 1
outSlope: 1
tangentMode: 0
weightedMode: 0
inWeight: 0
outWeight: 0
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
hueVsHue:
m_OverrideState: 1
m_Value:
k__BackingField: 0
m_Loop: 1
m_ZeroValue: 0.5
m_Range: 1
m_Curve:
serializedVersion: 2
m_Curve: []
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
hueVsSat:
m_OverrideState: 1
m_Value:
k__BackingField: 0
m_Loop: 1
m_ZeroValue: 0.5
m_Range: 1
m_Curve:
serializedVersion: 2
m_Curve: []
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
satVsSat:
m_OverrideState: 1
m_Value:
k__BackingField: 0
m_Loop: 0
m_ZeroValue: 0.5
m_Range: 1
m_Curve:
serializedVersion: 2
m_Curve: []
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
lumVsSat:
m_OverrideState: 1
m_Value:
k__BackingField: 0
m_Loop: 0
m_ZeroValue: 0.5
m_Range: 1
m_Curve:
serializedVersion: 2
m_Curve: []
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
--- !u!114 &5078066985263128679
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 97c23e3b12dc18c42a140437e53d3951, type: 3}
m_Name: Tonemapping
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.Tonemapping
active: 1
mode:
m_OverrideState: 1
m_Value: 0
neutralHDRRangeReductionMode:
m_OverrideState: 1
m_Value: 2
acesPreset:
m_OverrideState: 1
m_Value: 3
hueShiftAmount:
m_OverrideState: 1
m_Value: 0
detectPaperWhite:
m_OverrideState: 1
m_Value: 0
paperWhite:
m_OverrideState: 1
m_Value: 300
detectBrightnessLimits:
m_OverrideState: 1
m_Value: 1
minNits:
m_OverrideState: 1
m_Value: 0.005
maxNits:
m_OverrideState: 1
m_Value: 1000
--- !u!114 &5892942527586337604
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 6bd486065ce11414fa40e631affc4900, type: 3}
m_Name: ProbeVolumesOptions
m_EditorClassIdentifier: Unity.RenderPipelines.Core.Runtime::UnityEngine.Rendering.ProbeVolumesOptions
active: 1
normalBias:
m_OverrideState: 1
m_Value: 0.05
viewBias:
m_OverrideState: 1
m_Value: 0.1
scaleBiasWithMinProbeDistance:
m_OverrideState: 1
m_Value: 0
samplingNoise:
m_OverrideState: 1
m_Value: 0.1
animateSamplingNoise:
m_OverrideState: 1
m_Value: 1
leakReductionMode:
m_OverrideState: 1
m_Value: 2
minValidDotProductValue:
m_OverrideState: 1
m_Value: 0.1
occlusionOnlyReflectionNormalization:
m_OverrideState: 1
m_Value: 1
intensityMultiplier:
m_OverrideState: 1
m_Value: 1
skyOcclusionIntensityMultiplier:
m_OverrideState: 1
m_Value: 1
worldOffset:
m_OverrideState: 1
m_Value: {x: 0, y: 0, z: 0}
--- !u!114 &8855959688280746896
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: cdfbdbb87d3286943a057f7791b43141, type: 3}
m_Name: ChannelMixer
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.ChannelMixer
active: 1
redOutRedIn:
m_OverrideState: 1
m_Value: 100
redOutGreenIn:
m_OverrideState: 1
m_Value: 0
redOutBlueIn:
m_OverrideState: 1
m_Value: 0
greenOutRedIn:
m_OverrideState: 1
m_Value: 0
greenOutGreenIn:
m_OverrideState: 1
m_Value: 100
greenOutBlueIn:
m_OverrideState: 1
m_Value: 0
blueOutRedIn:
m_OverrideState: 1
m_Value: 0
blueOutGreenIn:
m_OverrideState: 1
m_Value: 0
blueOutBlueIn:
m_OverrideState: 1
m_Value: 100
--- !u!114 &8862907471677848668
MonoBehaviour:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: ccf1aba9553839d41ae37dd52e9ebcce, type: 3}
m_Name: MotionBlur
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.MotionBlur
active: 1
mode:
m_OverrideState: 1
m_Value: 0
quality:
m_OverrideState: 1
m_Value: 0
intensity:
m_OverrideState: 1
m_Value: 0
clamp:
m_OverrideState: 1
m_Value: 0.05
================================================
FILE: Assets/Demo/URP/DefaultVolume.asset.meta
================================================
fileFormatVersion: 2
guid: dc9bac492fb764e719f43f6fff2d42aa
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/Demo/URP/GlobalSettings.asset
================================================
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2ec995e51a6e251468d2a3fd8a686257, type: 3}
m_Name: GlobalSettings
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.UniversalRenderPipelineGlobalSettings
m_ShaderStrippingSetting:
m_Version: 0
m_ExportShaderVariants: 1
m_ShaderVariantLogLevel: 0
m_StripRuntimeDebugShaders: 1
m_URPShaderStrippingSetting:
m_Version: 0
m_StripUnusedPostProcessingVariants: 0
m_StripUnusedVariants: 1
m_StripScreenCoordOverrideVariants: 1
m_ShaderVariantLogLevel: 0
m_ExportShaderVariants: 1
m_StripDebugVariants: 1
m_StripUnusedPostProcessingVariants: 0
m_StripUnusedVariants: 1
m_StripScreenCoordOverrideVariants: 1
supportRuntimeDebugDisplay: 0
m_EnableRenderGraph: 0
m_Settings:
m_SettingsList:
m_List:
- rid: 8017221388498632982
- rid: 8017221388498632983
- rid: 8017221388498632984
- rid: 8017221388498632985
- rid: 8017221388498632986
- rid: 8017221388498632987
- rid: 8017221388498632988
- rid: 8017221388498632989
- rid: 8017221388498632990
- rid: 8017221388498632991
- rid: 8017221388498632992
- rid: 8017221388498632993
- rid: 8017221388498632994
- rid: 8017221388498632995
- rid: 8017221388498632996
- rid: 8017221388498632997
- rid: 8017221388498632998
- rid: 8017221388498632999
- rid: 8017221388498633000
- rid: 8017221388498633001
- rid: 8017221388498633002
- rid: 8017221388498633003
- rid: 8017221388498633004
- rid: 8017221388498633005
- rid: 8017221388498633006
- rid: 8017221388498633007
- rid: 8017221388498633008
- rid: 8017221388498633009
- rid: 8017221388498633010
- rid: 8017221388498633011
- rid: 8017221388498633012
- rid: 8017221388498633013
m_RuntimeSettings:
m_List: []
m_AssetVersion: 9
m_ObsoleteDefaultVolumeProfile: {fileID: 0}
m_RenderingLayerNames:
- Default
m_ValidRenderingLayers: 0
lightLayerName0:
lightLayerName1:
lightLayerName2:
lightLayerName3:
lightLayerName4:
lightLayerName5:
lightLayerName6:
lightLayerName7:
apvScenesData:
obsoleteSceneBounds:
m_Keys: []
m_Values: []
obsoleteHasProbeVolumes:
m_Keys: []
m_Values:
references:
version: 2
RefIds:
- rid: 8017221388498632982
type: {class: RayTracingRenderPipelineResources, ns: UnityEngine.Rendering.UnifiedRayTracing, asm: Unity.UnifiedRayTracing.Runtime}
data:
m_Version: 1
m_GeometryPoolKernels: {fileID: 7200000, guid: 98e3d58cae7210c4786f67f504c9e899, type: 3}
m_CopyBuffer: {fileID: 7200000, guid: 1b95b5dcf48d1914c9e1e7405c7660e3, type: 3}
m_CopyPositions: {fileID: 7200000, guid: 1ad53a96b58d3c3488dde4f14db1aaeb, type: 3}
m_BitHistogram: {fileID: 7200000, guid: 8670f7ce4b60cef43bed36148aa1b0a2, type: 3}
m_BlockReducePart: {fileID: 7200000, guid: 4e034cc8ea2635c4e9f063e5ddc7ea7a, type: 3}
m_BlockScan: {fileID: 7200000, guid: 4d6d5de35fa45ef4a92119397a045cc9, type: 3}
m_BuildHlbvh: {fileID: 7200000, guid: 2d70cd6be91bd7843a39a54b51c15b13, type: 3}
m_RestructureBvh: {fileID: 7200000, guid: 56641cb88dcb31a4398a4997ef7a7a8c, type: 3}
m_Scatter: {fileID: 7200000, guid: a2eaeefdac4637a44b734e85b7be9186, type: 3}
- rid: 8017221388498632983
type: {class: UniversalRenderPipelineEditorMaterials, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
data:
m_DefaultMaterial: {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2}
m_DefaultParticleMaterial: {fileID: 2100000, guid: e823cd5b5d27c0f4b8256e7c12ee3e6d, type: 2}
m_DefaultLineMaterial: {fileID: 2100000, guid: e823cd5b5d27c0f4b8256e7c12ee3e6d, type: 2}
m_DefaultTerrainMaterial: {fileID: 2100000, guid: 594ea882c5a793440b60ff72d896021e, type: 2}
m_DefaultDecalMaterial: {fileID: 2100000, guid: 31d0dcc6f2dd4e4408d18036a2c93862, type: 2}
m_DefaultSpriteMaterial: {fileID: 2100000, guid: 9dfc825aed78fcd4ba02077103263b40, type: 2}
- rid: 8017221388498632984
type: {class: PostProcessData/TextureResources, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
data:
blueNoise16LTex: []
filmGrainTex:
- {fileID: 2800000, guid: 654c582f7f8a5a14dbd7d119cbde215d, type: 3}
- {fileID: 2800000, guid: dd77ffd079630404e879388999033049, type: 3}
- {fileID: 2800000, guid: 1097e90e1306e26439701489f391a6c0, type: 3}
- {fileID: 2800000, guid: f0b67500f7fad3b4c9f2b13e8f41ba6e, type: 3}
- {fileID: 2800000, guid: 9930fb4528622b34687b00bbe6883de7, type: 3}
- {fileID: 2800000, guid: bd9e8c758250ef449a4b4bfaad7a2133, type: 3}
- {fileID: 2800000, guid: 510a2f57334933e4a8dbabe4c30204e4, type: 3}
- {fileID: 2800000, guid: b4db8180660810945bf8d55ab44352ad, type: 3}
- {fileID: 2800000, guid: fd2fd78b392986e42a12df2177d3b89c, type: 3}
- {fileID: 2800000, guid: 5cdee82a77d13994f83b8fdabed7c301, type: 3}
smaaAreaTex: {fileID: 2800000, guid: d1f1048909d55cd4fa1126ab998f617e, type: 3}
smaaSearchTex: {fileID: 2800000, guid: 51eee22c2a633ef4aada830eed57c3fd, type: 3}
m_TexturesResourcesVersion: 0
- rid: 8017221388498632985
type: {class: URPDefaultVolumeProfileSettings, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
data:
m_Version: 0
m_VolumeProfile: {fileID: 11400000, guid: dc9bac492fb764e719f43f6fff2d42aa, type: 2}
- rid: 8017221388498632986
type: {class: ScreenSpaceAmbientOcclusionDynamicResources, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
data:
m_BlueNoise256Textures:
- {fileID: 2800000, guid: 36f118343fc974119bee3d09e2111500, type: 3}
- {fileID: 2800000, guid: 4b7b083e6b6734e8bb2838b0b50a0bc8, type: 3}
- {fileID: 2800000, guid: c06cc21c692f94f5fb5206247191eeee, type: 3}
- {fileID: 2800000, guid: cb76dd40fa7654f9587f6a344f125c9a, type: 3}
- {fileID: 2800000, guid: e32226222ff144b24bf3a5a451de54bc, type: 3}
- {fileID: 2800000, guid: 3302065f671a8450b82c9ddf07426f3a, type: 3}
- {fileID: 2800000, guid: 56a77a3e8d64f47b6afe9e3c95cb57d5, type: 3}
m_Version: 0
- rid: 8017221388498632987
type: {class: RenderGraphSettings, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
data:
m_Version: 0
m_EnableRenderCompatibilityMode: 0
- rid: 8017221388498632988
type: {class: Renderer2DResources, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
data:
m_Version: 0
m_LightShader: {fileID: 4800000, guid: 3f6c848ca3d7bca4bbe846546ac701a1, type: 3}
m_ProjectedShadowShader: {fileID: 4800000, guid: ce09d4a80b88c5a4eb9768fab4f1ee00, type: 3}
m_SpriteShadowShader: {fileID: 4800000, guid: 44fc62292b65ab04eabcf310e799ccf6, type: 3}
m_SpriteUnshadowShader: {fileID: 4800000, guid: de02b375720b5c445afe83cd483bedf3, type: 3}
m_GeometryShadowShader: {fileID: 4800000, guid: 19349a0f9a7ed4c48a27445bcf92e5e1, type: 3}
m_GeometryUnshadowShader: {fileID: 4800000, guid: 77774d9009bb81447b048c907d4c6273, type: 3}
m_CopyDepthPS: {fileID: 4800000, guid: d6dae50ee9e1bfa4db75f19f99355220, type: 3}
m_DefaultLitMaterial: {fileID: 2100000, guid: a97c105638bdf8b4a8650670310a4cd3, type: 2}
m_DefaultUnlitMaterial: {fileID: 2100000, guid: 9dfc825aed78fcd4ba02077103263b40, type: 2}
m_DefaultMaskMaterial: {fileID: 2100000, guid: 15d0c3709176029428a0da2f8cecf0b5, type: 2}
m_DefaultMesh2DLitMaterial: {fileID: 2100000, guid: 9452ae1262a74094f8a68013fbcd1834, type: 2}
- rid: 8017221388498632989
type: {class: URPShaderStrippingSetting, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
data:
m_Version: 0
m_StripUnusedPostProcessingVariants: 0
m_StripUnusedVariants: 1
m_StripScreenCoordOverrideVariants: 1
- rid: 8017221388498632990
type: {class: UniversalRendererResources, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
data:
m_Version: 0
m_CopyDepthPS: {fileID: 4800000, guid: d6dae50ee9e1bfa4db75f19f99355220, type: 3}
m_CameraMotionVector: {fileID: 4800000, guid: c56b7e0d4c7cb484e959caeeedae9bbf, type: 3}
m_StencilDeferredPS: {fileID: 4800000, guid: e9155b26e1bc55942a41e518703fe304, type: 3}
m_ClusterDeferred: {fileID: 4800000, guid: 222cce62363a44a380c36bf03b392608, type: 3}
m_StencilDitherMaskSeedPS: {fileID: 4800000, guid: 8c3ee818f2efa514c889881ccb2e95a2, type: 3}
m_DBufferClear: {fileID: 4800000, guid: f056d8bd2a1c7e44e9729144b4c70395, type: 3}
- rid: 8017221388498632991
type: {class: UniversalRenderPipelineRuntimeShaders, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
data:
m_Version: 0
m_FallbackErrorShader: {fileID: 4800000, guid: e6e9a19c3678ded42a3bc431ebef7dbd, type: 3}
m_BlitHDROverlay: {fileID: 4800000, guid: a89bee29cffa951418fc1e2da94d1959, type: 3}
m_CoreBlitPS: {fileID: 4800000, guid: 93446b5c5339d4f00b85c159e1159b7c, type: 3}
m_CoreBlitColorAndDepthPS: {fileID: 4800000, guid: d104b2fc1ca6445babb8e90b0758136b, type: 3}
m_SamplingPS: {fileID: 4800000, guid: 04c410c9937594faa893a11dceb85f7e, type: 3}
m_TerrainDetailLit: {fileID: 4800000, guid: f6783ab646d374f94b199774402a5144, type: 3}
m_TerrainDetailGrassBillboard: {fileID: 4800000, guid: 29868e73b638e48ca99a19ea58c48d90, type: 3}
m_TerrainDetailGrass: {fileID: 4800000, guid: e507fdfead5ca47e8b9a768b51c291a1, type: 3}
- rid: 8017221388498632992
type: {class: OnTilePostProcessResource, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
data:
m_Version: 0
m_UberPostShader: {fileID: 4800000, guid: fe4f13c1004a07d4ea1e30bfd0326d9e, type: 3}
- rid: 8017221388498632993
type: {class: URPReflectionProbeSettings, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Universal.Runtime}
data:
version: 1
useReflectionProbeRotation: 1
- rid: 8017221388498632994
type: {class: UniversalRenderPipelineEditorShaders, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
data:
m_AutodeskInteractive: {fileID: 4800000, guid: 0e9d5a909a1f7e84882a534d0d11e49f, type: 3}
m_AutodeskInteractiveTransparent: {fileID: 4800000, guid: 5c81372d981403744adbdda4433c9c11, type: 3}
m_AutodeskInteractiveMasked: {fileID: 4800000, guid: 80aa867ac363ac043847b06ad71604cd, type: 3}
m_DefaultSpeedTree7Shader: {fileID: 4800000, guid: 0f4122b9a743b744abe2fb6a0a88868b, type: 3}
m_DefaultSpeedTree8Shader: {fileID: -6465566751694194690, guid: 9920c1f1781549a46ba081a2a15a16ec, type: 3}
m_DefaultSpeedTree9Shader: {fileID: -6465566751694194690, guid: cbd3e1cc4ae141c42a30e33b4d666a61, type: 3}
- rid: 8017221388498632995
type: {class: ScreenSpaceAmbientOcclusionPersistentResources, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
data:
m_Shader: {fileID: 4800000, guid: 0849e84e3d62649e8882e9d6f056a017, type: 3}
m_Version: 0
- rid: 8017221388498632996
type: {class: UniversalRenderPipelineDebugShaders, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
data:
m_DebugReplacementPS: {fileID: 4800000, guid: cf852408f2e174538bcd9b7fda1c5ae7, type: 3}
m_HdrDebugViewPS: {fileID: 4800000, guid: 573620ae32aec764abd4d728906d2587, type: 3}
m_ProbeVolumeSamplingDebugComputeShader: {fileID: 7200000, guid: 53626a513ea68ce47b59dc1299fe3959, type: 3}
- rid: 8017221388498632997
type: {class: UniversalRenderPipelineRuntimeXRResources, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
data:
m_xrOcclusionMeshPS: {fileID: 4800000, guid: 4431b1f1f743fbf4eb310a967890cbea, type: 3}
m_xrMirrorViewPS: {fileID: 4800000, guid: d5a307c014552314b9f560906d708772, type: 3}
m_xrMotionVector: {fileID: 4800000, guid: f89aac1e4f84468418fe30e611dff395, type: 3}
- rid: 8017221388498632998
type: {class: UniversalRenderPipelineEditorAssets, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
data:
m_DefaultSettingsVolumeProfile: {fileID: 11400000, guid: eda47df5b85f4f249abf7abd73db2cb2, type: 2}
- rid: 8017221388498632999
type: {class: PostProcessData/ShaderResources, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
data:
stopNanPS: {fileID: 4800000, guid: 1121bb4e615ca3c48b214e79e841e823, type: 3}
subpixelMorphologicalAntialiasingPS: {fileID: 4800000, guid: 63eaba0ebfb82cc43bde059b4a8c65f6, type: 3}
gaussianDepthOfFieldPS: {fileID: 4800000, guid: 5e7134d6e63e0bc47a1dd2669cedb379, type: 3}
bokehDepthOfFieldPS: {fileID: 4800000, guid: 2aed67ad60045d54ba3a00c91e2d2631, type: 3}
cameraMotionBlurPS: {fileID: 4800000, guid: 1edcd131364091c46a17cbff0b1de97a, type: 3}
paniniProjectionPS: {fileID: 4800000, guid: a15b78cf8ca26ca4fb2090293153c62c, type: 3}
lutBuilderLdrPS: {fileID: 4800000, guid: 65df88701913c224d95fc554db28381a, type: 3}
lutBuilderHdrPS: {fileID: 4800000, guid: ec9fec698a3456d4fb18cf8bacb7a2bc, type: 3}
bloomPS: {fileID: 4800000, guid: 5f1864addb451f54bae8c86d230f736e, type: 3}
temporalAntialiasingPS: {fileID: 4800000, guid: 9c70c1a35ff15f340b38ea84842358bf, type: 3}
LensFlareDataDrivenPS: {fileID: 4800000, guid: 6cda457ac28612740adb23da5d39ea92, type: 3}
LensFlareScreenSpacePS: {fileID: 4800000, guid: 701880fecb344ea4c9cd0db3407ab287, type: 3}
scalingSetupPS: {fileID: 4800000, guid: e8ee25143a34b8c4388709ea947055d1, type: 3}
easuPS: {fileID: 4800000, guid: 562b7ae4f629f144aa97780546fce7c6, type: 3}
uberPostPS: {fileID: 4800000, guid: e7857e9d0c934dc4f83f270f8447b006, type: 3}
finalPostPassPS: {fileID: 4800000, guid: c49e63ed1bbcb334780a3bd19dfed403, type: 3}
m_ShaderResourcesVersion: 0
- rid: 8017221388498633000
type: {class: UniversalRenderPipelineRuntimeTextures, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime}
data:
m_Version: 1
m_BlueNoise64LTex: {fileID: 2800000, guid: e3d24661c1e055f45a7560c033dbb837, type: 3}
m_BayerMatrixTex: {fileID: 2800000, guid: f9ee4ed84c1d10c49aabb9b210b0fc44, type: 3}
m_DebugFontTex: {fileID: 2800000, guid: 26a413214480ef144b2915d6ff4d0beb, type: 3}
- rid: 8017221388498633001
type: {class: GPUResidentDrawerResources, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.GPUDriven.Runtime}
data:
m_Version: 0
m_InstanceDataBufferCopyKernels: {fileID: 7200000, guid: f984aeb540ded8b4fbb8a2047ab5b2e2, type: 3}
m_InstanceDataBufferUploadKernels: {fileID: 7200000, guid: 53864816eb00f2343b60e1a2c5a262ef, type: 3}
m_TransformUpdaterKernels: {fileID: 7200000, guid: 2a567b9b2733f8d47a700c3c85bed75b, type: 3}
m_WindDataUpdaterKernels: {fileID: 7200000, guid: fde76746e4fd0ed418c224f6b4084114, type: 3}
m_OccluderDepthPyramidKernels: {fileID: 7200000, guid: 08b2b5fb307b0d249860612774a987da, type: 3}
m_InstanceOcclusionCullingKernels: {fileID: 7200000, guid: f6d223acabc2f974795a5a7864b50e6c, type: 3}
m_OcclusionCullingDebugKernels: {fileID: 7200000, guid: b23e766bcf50ca4438ef186b174557df, type: 3}
m_DebugOcclusionTestPS: {fileID: 4800000, guid: d3f0849180c2d0944bc71060693df100, type: 3}
m_DebugOccluderPS: {fileID: 4800000, guid: b3c92426a88625841ab15ca6a7917248, type: 3}
- rid: 8017221388498633002
type: {class: RenderGraphGlobalSettings, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime}
data:
m_version: 0
m_EnableCompilationCaching: 1
m_EnableValidityChecks: 1
- rid: 8017221388498633003
type: {class: VrsRenderPipelineRuntimeResources, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime}
data:
m_TextureComputeShader: {fileID: 7200000, guid: cacb30de6c40c7444bbc78cb0a81fd2a, type: 3}
m_VisualizationShader: {fileID: 4800000, guid: 620b55b8040a88d468e94abe55bed5ba, type: 3}
m_VisualizationLookupTable:
m_Data:
- {r: 0.785, g: 0.23, b: 0.2, a: 1}
- {r: 1, g: 0.8, b: 0.8, a: 1}
- {r: 0.4, g: 0.2, b: 0.2, a: 1}
- {r: 0.51, g: 0.8, b: 0.6, a: 1}
- {r: 0.6, g: 0.8, b: 1, a: 1}
- {r: 0.2, g: 0.4, b: 0.6, a: 1}
- {r: 0.8, g: 1, b: 0.8, a: 1}
- {r: 0.2, g: 0.4, b: 0.2, a: 1}
- {r: 0.125, g: 0.22, b: 0.36, a: 1}
m_ConversionLookupTable:
m_Data:
- {r: 0.785, g: 0.23, b: 0.2, a: 1}
- {r: 1, g: 0.8, b: 0.8, a: 1}
- {r: 0.4, g: 0.2, b: 0.2, a: 1}
- {r: 0.51, g: 0.8, b: 0.6, a: 1}
- {r: 0.6, g: 0.8, b: 1, a: 1}
- {r: 0.2, g: 0.4, b: 0.6, a: 1}
- {r: 0.8, g: 1, b: 0.8, a: 1}
- {r: 0.2, g: 0.4, b: 0.2, a: 1}
- {r: 0.125, g: 0.22, b: 0.36, a: 1}
- rid: 8017221388498633004
type: {class: RenderGraphUtilsResources, ns: UnityEngine.Rendering.RenderGraphModule.Util, asm: Unity.RenderPipelines.Core.Runtime}
data:
m_Version: 0
m_CoreCopyPS: {fileID: 4800000, guid: 12dc59547ea167a4ab435097dd0f9add, type: 3}
- rid: 8017221388498633005
type: {class: ProbeVolumeGlobalSettings, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime}
data:
m_Version: 1
m_ProbeVolumeDisableStreamingAssets: 0
- rid: 8017221388498633006
type: {class: RenderingDebuggerRuntimeResources, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime}
data:
m_version: 0
- rid: 8017221388498633007
type: {class: ShaderStrippingSetting, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime}
data:
m_Version: 0
m_ExportShaderVariants: 1
m_ShaderVariantLogLevel: 0
m_StripRuntimeDebugShaders: 1
- rid: 8017221388498633008
type: {class: LightmapSamplingSettings, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime}
data:
m_Version: 1
m_UseBicubicLightmapSampling: 0
- rid: 8017221388498633009
type: {class: ProbeVolumeBakingResources, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime}
data:
m_Version: 1
dilationShader: {fileID: 7200000, guid: 6bb382f7de370af41b775f54182e491d, type: 3}
subdivideSceneCS: {fileID: 7200000, guid: bb86f1f0af829fd45b2ebddda1245c22, type: 3}
voxelizeSceneShader: {fileID: 4800000, guid: c8b6a681c7b4e2e4785ffab093907f9e, type: 3}
traceVirtualOffsetCS: {fileID: -6772857160820960102, guid: ff2cbab5da58bf04d82c5f34037ed123, type: 3}
traceVirtualOffsetRT: {fileID: -5126288278712620388, guid: ff2cbab5da58bf04d82c5f34037ed123, type: 3}
skyOcclusionCS: {fileID: -6772857160820960102, guid: 5a2a534753fbdb44e96c3c78b5a6999d, type: 3}
skyOcclusionRT: {fileID: -5126288278712620388, guid: 5a2a534753fbdb44e96c3c78b5a6999d, type: 3}
renderingLayerCS: {fileID: -6772857160820960102, guid: 94a070d33e408384bafc1dea4a565df9, type: 3}
renderingLayerRT: {fileID: -5126288278712620388, guid: 94a070d33e408384bafc1dea4a565df9, type: 3}
- rid: 8017221388498633010
type: {class: ProbeVolumeRuntimeResources, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime}
data:
m_Version: 1
probeVolumeBlendStatesCS: {fileID: 7200000, guid: a3f7b8c99de28a94684cb1daebeccf5d, type: 3}
probeVolumeUploadDataCS: {fileID: 7200000, guid: 0951de5992461754fa73650732c4954c, type: 3}
probeVolumeUploadDataL2CS: {fileID: 7200000, guid: 6196f34ed825db14b81fb3eb0ea8d931, type: 3}
- rid: 8017221388498633011
type: {class: STP/RuntimeResources, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime}
data:
m_setupCS: {fileID: 7200000, guid: 33be2e9a5506b2843bdb2bdff9cad5e1, type: 3}
m_preTaaCS: {fileID: 7200000, guid: a679dba8ec4d9ce45884a270b0e22dda, type: 3}
m_taaCS: {fileID: 7200000, guid: 3923900e2b41b5e47bc25bfdcbcdc9e6, type: 3}
- rid: 8017221388498633012
type: {class: IncludeAdditionalRPAssets, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime}
data:
m_version: 0
m_IncludeReferencedInScenes: 0
m_IncludeAssetsByLabel: 0
m_LabelToInclude:
- rid: 8017221388498633013
type: {class: ProbeVolumeDebugResources, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime}
data:
m_Version: 1
probeVolumeDebugShader: {fileID: 4800000, guid: 3b21275fd12d65f49babb5286f040f2d, type: 3}
probeVolumeFragmentationDebugShader: {fileID: 4800000, guid: 3a80877c579b9144ebdcc6d923bca303, type: 3}
probeVolumeSamplingDebugShader: {fileID: 4800000, guid: bf54e6528c79a224e96346799064c393, type: 3}
probeVolumeOffsetDebugShader: {fileID: 4800000, guid: db8bd7436dc2c5f4c92655307d198381, type: 3}
probeSamplingDebugMesh: {fileID: -3555484719484374845, guid: 20be25aac4e22ee49a7db76fb3df6de2, type: 3}
numbersDisplayTex: {fileID: 2800000, guid: 73fe53b428c5b3440b7e87ee830b608a, type: 3}
================================================
FILE: Assets/Demo/URP/GlobalSettings.asset.meta
================================================
fileFormatVersion: 2
guid: a0e1abbc8745e464ab699ff4e71959f6
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/Demo/URP/URP.asset
================================================
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: bf2edee5c58d82540a51f03df9d42094, type: 3}
m_Name: URP
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.UniversalRenderPipelineAsset
k_AssetVersion: 13
k_AssetPreviousVersion: 13
m_RendererType: 1
m_RendererData: {fileID: 0}
m_RendererDataList:
- {fileID: 11400000, guid: e707f25f3295f48408bbc74cefc67e0d, type: 2}
m_DefaultRendererIndex: 0
m_RequireDepthTexture: 0
m_RequireOpaqueTexture: 0
m_OpaqueDownsampling: 1
m_SupportsTerrainHoles: 1
m_SupportsHDR: 1
m_HDRColorBufferPrecision: 0
m_MSAA: 1
m_RenderScale: 1
m_UpscalingFilter: 0
m_FsrOverrideSharpness: 0
m_FsrSharpness: 0.92
m_EnableLODCrossFade: 1
m_LODCrossFadeDitheringType: 1
m_ShEvalMode: 0
m_LightProbeSystem: 0
m_ProbeVolumeMemoryBudget: 1024
m_ProbeVolumeBlendingMemoryBudget: 256
m_SupportProbeVolumeGPUStreaming: 0
m_SupportProbeVolumeDiskStreaming: 0
m_SupportProbeVolumeScenarios: 0
m_SupportProbeVolumeScenarioBlending: 0
m_ProbeVolumeSHBands: 1
m_MainLightRenderingMode: 1
m_MainLightShadowsSupported: 1
m_MainLightShadowmapResolution: 2048
m_AdditionalLightsRenderingMode: 1
m_AdditionalLightsPerObjectLimit: 4
m_AdditionalLightShadowsSupported: 0
m_AdditionalLightsShadowmapResolution: 2048
m_AdditionalLightsShadowResolutionTierLow: 256
m_AdditionalLightsShadowResolutionTierMedium: 512
m_AdditionalLightsShadowResolutionTierHigh: 1024
m_ReflectionProbeBlending: 0
m_ReflectionProbeBoxProjection: 0
m_ReflectionProbeAtlas: 0
m_ShadowDistance: 50
m_ShadowCascadeCount: 1
m_Cascade2Split: 0.25
m_Cascade3Split: {x: 0.1, y: 0.3}
m_Cascade4Split: {x: 0.067, y: 0.2, z: 0.467}
m_CascadeBorder: 0.2
m_ShadowDepthBias: 1
m_ShadowNormalBias: 1
m_AnyShadowsSupported: 1
m_SoftShadowsSupported: 0
m_ConservativeEnclosingSphere: 1
m_NumIterationsEnclosingSphere: 64
m_SoftShadowQuality: 2
m_AdditionalLightsCookieResolution: 2048
m_AdditionalLightsCookieFormat: 3
m_UseSRPBatcher: 1
m_SupportsDynamicBatching: 0
m_MixedLightingSupported: 1
m_SupportsLightCookies: 1
m_SupportsLightLayers: 0
m_DebugLevel: 0
m_StoreActionsOptimization: 0
m_UseAdaptivePerformance: 1
m_ColorGradingMode: 0
m_ColorGradingLutSize: 32
m_AllowPostProcessAlphaOutput: 0
m_UseFastSRGBLinearConversion: 0
m_SupportDataDrivenLensFlare: 1
m_SupportScreenSpaceLensFlare: 1
m_GPUResidentDrawerMode: 0
m_SmallMeshScreenPercentage: 0
m_GPUResidentDrawerEnableOcclusionCullingInCameras: 0
m_ShadowType: 1
m_LocalShadowsSupported: 0
m_LocalShadowsAtlasResolution: 256
m_MaxPixelLights: 0
m_ShadowAtlasResolution: 256
m_VolumeFrameworkUpdateMode: 0
m_VolumeProfile: {fileID: 0}
apvScenesData:
obsoleteSceneBounds:
m_Keys: []
m_Values: []
obsoleteHasProbeVolumes:
m_Keys: []
m_Values:
m_PrefilteringModeMainLightShadows: 1
m_PrefilteringModeAdditionalLight: 4
m_PrefilteringModeAdditionalLightShadows: 1
m_PrefilterXRKeywords: 0
m_PrefilteringModeForwardPlus: 1
m_PrefilteringModeDeferredRendering: 1
m_PrefilteringModeScreenSpaceOcclusion: 1
m_PrefilterDebugKeywords: 0
m_PrefilterWriteRenderingLayers: 0
m_PrefilterHDROutput: 0
m_PrefilterAlphaOutput: 0
m_PrefilterSSAODepthNormals: 0
m_PrefilterSSAOSourceDepthLow: 0
m_PrefilterSSAOSourceDepthMedium: 0
m_PrefilterSSAOSourceDepthHigh: 0
m_PrefilterSSAOInterleaved: 0
m_PrefilterSSAOBlueNoise: 0
m_PrefilterSSAOSampleCountLow: 0
m_PrefilterSSAOSampleCountMedium: 0
m_PrefilterSSAOSampleCountHigh: 0
m_PrefilterDBufferMRT1: 0
m_PrefilterDBufferMRT2: 0
m_PrefilterDBufferMRT3: 0
m_PrefilterSoftShadowsQualityLow: 0
m_PrefilterSoftShadowsQualityMedium: 0
m_PrefilterSoftShadowsQualityHigh: 0
m_PrefilterSoftShadows: 0
m_PrefilterScreenCoord: 0
m_PrefilterScreenSpaceIrradiance: 0
m_PrefilterNativeRenderPass: 0
m_PrefilterUseLegacyLightmaps: 0
m_PrefilterBicubicLightmapSampling: 0
m_PrefilterReflectionProbeRotation: 0
m_PrefilterReflectionProbeBlending: 0
m_PrefilterReflectionProbeBoxProjection: 0
m_PrefilterReflectionProbeAtlas: 0
m_ShaderVariantLogLevel: 0
m_ShadowCascades: 0
m_Textures:
blueNoise64LTex: {fileID: 2800000, guid: e3d24661c1e055f45a7560c033dbb837, type: 3}
bayerMatrixTex: {fileID: 2800000, guid: f9ee4ed84c1d10c49aabb9b210b0fc44, type: 3}
================================================
FILE: Assets/Demo/URP/URP.asset.meta
================================================
fileFormatVersion: 2
guid: 0f2789a7f25c049d88040a322e46da7c
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/Demo/URP.meta
================================================
fileFormatVersion: 2
guid: dea8b7a5d656145a3aba10170830c74e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/Demo.meta
================================================
fileFormatVersion: 2
guid: 2c79d8564f0c64a0684cb4e548733c30
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/Demo.unity
================================================
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!29 &1
OcclusionCullingSettings:
m_ObjectHideFlags: 0
serializedVersion: 2
m_OcclusionBakeSettings:
smallestOccluder: 5
smallestHole: 0.25
backfaceThreshold: 100
m_SceneGUID: 00000000000000000000000000000000
m_OcclusionCullingData: {fileID: 0}
--- !u!104 &2
RenderSettings:
m_ObjectHideFlags: 0
serializedVersion: 10
m_Fog: 0
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
m_FogMode: 3
m_FogDensity: 0.01
m_LinearFogStart: 0
m_LinearFogEnd: 300
m_AmbientSkyColor: {r: 4.541206, g: 4.541206, b: 4.541206, a: 1}
m_AmbientEquatorColor: {r: 0.00073146477, g: 0.00073146477, b: 0.00073146477, a: 1}
m_AmbientGroundColor: {r: 0.26915348, g: 0.13803844, b: 0.6226415, a: 1}
m_AmbientIntensity: 1
m_AmbientMode: 1
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
m_SkyboxMaterial: {fileID: 0}
m_HaloStrength: 0.5
m_FlareStrength: 1
m_FlareFadeSpeed: 3
m_HaloTexture: {fileID: 0}
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
m_DefaultReflectionMode: 1
m_DefaultReflectionResolution: 128
m_ReflectionBounces: 1
m_ReflectionIntensity: 1
m_CustomReflection: {fileID: 0}
m_Sun: {fileID: 0}
m_UseRadianceAmbientProbe: 0
--- !u!157 &3
LightmapSettings:
m_ObjectHideFlags: 0
serializedVersion: 13
m_BakeOnSceneLoad: 0
m_GISettings:
serializedVersion: 2
m_BounceScale: 1
m_IndirectOutputScale: 1
m_AlbedoBoost: 1
m_EnvironmentLightingMode: 0
m_EnableBakedLightmaps: 1
m_EnableRealtimeLightmaps: 0
m_LightmapEditorSettings:
serializedVersion: 12
m_Resolution: 2
m_BakeResolution: 40
m_AtlasSize: 1024
m_AO: 0
m_AOMaxDistance: 1
m_CompAOExponent: 1
m_CompAOExponentDirect: 0
m_ExtractAmbientOcclusion: 0
m_Padding: 2
m_LightmapParameters: {fileID: 0}
m_LightmapsBakeMode: 1
m_TextureCompression: 1
m_ReflectionCompression: 2
m_MixedBakeMode: 2
m_BakeBackend: 2
m_PVRSampling: 1
m_PVRDirectSampleCount: 32
m_PVRSampleCount: 512
m_PVRBounces: 2
m_PVREnvironmentSampleCount: 256
m_PVREnvironmentReferencePointCount: 2048
m_PVRFilteringMode: 1
m_PVRDenoiserTypeDirect: 1
m_PVRDenoiserTypeIndirect: 1
m_PVRDenoiserTypeAO: 1
m_PVRFilterTypeDirect: 0
m_PVRFilterTypeIndirect: 0
m_PVRFilterTypeAO: 0
m_PVREnvironmentMIS: 1
m_PVRCulling: 1
m_PVRFilteringGaussRadiusDirect: 1
m_PVRFilteringGaussRadiusIndirect: 1
m_PVRFilteringGaussRadiusAO: 1
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
m_PVRFilteringAtrousPositionSigmaIndirect: 2
m_PVRFilteringAtrousPositionSigmaAO: 1
m_ExportTrainingData: 0
m_TrainingDataDestination: TrainingData
m_LightProbeSampleCountMultiplier: 4
m_LightingDataAsset: {fileID: 20201, guid: 0000000000000000f000000000000000, type: 0}
m_LightingSettings: {fileID: 0}
--- !u!196 &4
NavMeshSettings:
serializedVersion: 2
m_ObjectHideFlags: 0
m_BuildSettings:
serializedVersion: 3
agentTypeID: 0
agentRadius: 0.5
agentHeight: 2
agentSlope: 45
agentClimb: 0.4
ledgeDropHeight: 0
maxJumpAcrossDistance: 0
minRegionArea: 2
manualCellSize: 0
cellSize: 0.16666667
manualTileSize: 0
tileSize: 256
buildHeightMesh: 0
maxJobWorkers: 0
preserveTilesOutsideBounds: 0
debug:
m_Flags: 0
m_NavMeshData: {fileID: 0}
--- !u!1 &11714847
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 11714850}
- component: {fileID: 11714849}
- component: {fileID: 11714848}
m_Layer: 0
m_Name: Quad
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!23 &11714848
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 11714847}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_ForceMeshLod: -1
m_MeshLodSelectionBias: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: 6f1182baee9ac4863bd4c5ea5f7d536d, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_GlobalIlluminationMeshLod: 0
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!33 &11714849
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 11714847}
m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0}
--- !u!4 &11714850
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 11714847}
serializedVersion: 2
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 7.5510006, y: 4.247438, z: 4.247438}
m_ConstrainProportionsScale: 1
m_Children: []
m_Father: {fileID: 28633265}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &28633263
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 28633265}
- component: {fileID: 28633264}
m_Layer: 0
m_Name: Player (HAP Alpha)
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &28633264
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 28633263}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f3775962864755b43a790abf4d029c32, type: 3}
m_Name:
m_EditorClassIdentifier: Klak.Hap::Klak.Hap.HapPlayer
_pathMode: 0
_filePath: TransparencyTest-HapAlpha.mov
_time: 0
_speed: 1
_loop: 1
_targetTexture: {fileID: 0}
_targetRenderer: {fileID: 11714848}
_targetMaterialProperty: _BaseMap
--- !u!4 &28633265
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 28633263}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 11714850}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &201439697
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 201439700}
- component: {fileID: 201439699}
- component: {fileID: 201439698}
- component: {fileID: 201439702}
m_Layer: 0
m_Name: Cube
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!23 &201439698
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 201439697}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_ForceMeshLod: -1
m_MeshLodSelectionBias: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_GlobalIlluminationMeshLod: 0
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!33 &201439699
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 201439697}
m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
--- !u!4 &201439700
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 201439697}
serializedVersion: 2
m_LocalRotation: {x: 0.25000006, y: -0.06698733, z: 0.25000006, w: 0.9330127}
m_LocalPosition: {x: 1.6, y: 0, z: -4}
m_LocalScale: {x: 0.5, y: 0.5, z: 0.5}
m_ConstrainProportionsScale: 1
m_Children: []
m_Father: {fileID: 246925888}
m_LocalEulerAnglesHint: {x: 30, y: 0, z: 30}
--- !u!114 &201439702
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 201439697}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 99b3dd156c03a80f48913a83361c6d4c, type: 3}
m_Name:
m_EditorClassIdentifier: Klak.Motion::Klak.Motion.LinearMotion
velocity:
x: 0
y: 0
z: 0
angularVelocity:
x: -90
y: 0
z: 0
--- !u!1 &246925887
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 246925888}
- component: {fileID: 246925889}
- component: {fileID: 246925890}
m_Layer: 0
m_Name: Player (HAP)
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &246925888
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 246925887}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 327780397}
- {fileID: 201439700}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &246925889
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 246925887}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f3775962864755b43a790abf4d029c32, type: 3}
m_Name:
m_EditorClassIdentifier: Klak.Hap::Klak.Hap.HapPlayer
_pathMode: 0
_filePath: Beeple-FiberOptical-Hap.mov
_time: 0
_speed: 1
_loop: 1
_targetTexture: {fileID: 8400000, guid: a4f22813f74e84455bd3d9e41d54ced6, type: 2}
_targetRenderer: {fileID: 201439698}
_targetMaterialProperty: _BaseMap
--- !u!114 &246925890
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 246925887}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 538bb1d7707cb473f8493b44c63286cc, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::SeekBar
_ui: {fileID: 1940121732}
--- !u!1 &327780394
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 327780397}
- component: {fileID: 327780396}
- component: {fileID: 327780395}
m_Layer: 0
m_Name: Backdrop
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!23 &327780395
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 327780394}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_ForceMeshLod: -1
m_MeshLodSelectionBias: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: e127fa53264f341caab6ab276ff39b77, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_GlobalIlluminationMeshLod: 0
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!33 &327780396
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 327780394}
m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0}
--- !u!4 &327780397
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 327780394}
serializedVersion: 2
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 7.5510006, y: 4.247438, z: 4.247438}
m_ConstrainProportionsScale: 1
m_Children: []
m_Father: {fileID: 246925888}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &395240107
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 395240109}
- component: {fileID: 395240108}
m_Layer: 0
m_Name: Player (HAP Q)
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &395240108
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 395240107}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f3775962864755b43a790abf4d029c32, type: 3}
m_Name:
m_EditorClassIdentifier: Klak.Hap::Klak.Hap.HapPlayer
_pathMode: 0
_filePath: Beeple-Hexxx-HapQ.mov
_time: 0
_speed: 1
_loop: 1
_targetTexture: {fileID: 0}
_targetRenderer: {fileID: 2137533910}
_targetMaterialProperty: _MainTex
--- !u!4 &395240109
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 395240107}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 2137533909}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1585188254
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1585188257}
- component: {fileID: 1585188256}
- component: {fileID: 1585188255}
m_Layer: 0
m_Name: Main Camera
m_TagString: MainCamera
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &1585188255
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1585188254}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.UniversalAdditionalCameraData
m_RenderShadows: 0
m_RequiresDepthTextureOption: 2
m_RequiresOpaqueTextureOption: 2
m_CameraType: 0
m_Cameras: []
m_RendererIndex: -1
m_VolumeLayerMask:
serializedVersion: 2
m_Bits: 1
m_VolumeTrigger: {fileID: 0}
m_VolumeFrameworkUpdateModeOption: 2
m_RenderPostProcessing: 0
m_Antialiasing: 0
m_AntialiasingQuality: 2
m_StopNaN: 0
m_Dithering: 0
m_ClearDepth: 1
m_AllowXRRendering: 1
m_AllowHDROutput: 1
m_UseScreenCoordOverride: 0
m_ScreenSizeOverride: {x: 0, y: 0, z: 0, w: 0}
m_ScreenCoordScaleBias: {x: 0, y: 0, z: 0, w: 0}
m_RequiresDepthTexture: 0
m_RequiresColorTexture: 0
m_TaaSettings:
m_Quality: 3
m_FrameInfluence: 0.1
m_JitterScale: 1
m_MipBias: 0
m_VarianceClampScale: 0.9
m_ContrastAdaptiveSharpening: 0
m_Version: 2
--- !u!20 &1585188256
Camera:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1585188254}
m_Enabled: 1
serializedVersion: 2
m_ClearFlags: 2
m_BackGroundColor: {r: 0.07489321, g: 0.103773594, b: 0.09363752, a: 0}
m_projectionMatrixMode: 1
m_GateFitMode: 2
m_FOVAxisMode: 0
m_Iso: 200
m_ShutterSpeed: 0.005
m_Aperture: 16
m_FocusDistance: 10
m_FocalLength: 50
m_BladeCount: 5
m_Curvature: {x: 2, y: 11}
m_BarrelClipping: 0.25
m_Anamorphism: 0
m_SensorSize: {x: 36, y: 24}
m_LensShift: {x: 0, y: 0}
m_NormalizedViewPortRect:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
near clip plane: 1
far clip plane: 20
field of view: 24
orthographic: 0
orthographic size: 5
m_Depth: -1
m_CullingMask:
serializedVersion: 2
m_Bits: 4294967295
m_RenderingPath: -1
m_TargetTexture: {fileID: 0}
m_TargetDisplay: 0
m_TargetEye: 3
m_HDR: 1
m_AllowMSAA: 1
m_AllowDynamicResolution: 0
m_ForceIntoRT: 0
m_OcclusionCulling: 0
m_StereoConvergence: 10
m_StereoSeparation: 0.022
--- !u!4 &1585188257
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1585188254}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: -10}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1940121731
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1940121733}
- component: {fileID: 1940121732}
m_Layer: 0
m_Name: UI
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &1940121732
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1940121731}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 19102, guid: 0000000000000000e000000000000000, type: 0}
m_Name:
m_EditorClassIdentifier: UnityEngine.dll::UnityEngine.UIElements.UIDocument
m_PanelSettings: {fileID: 11400000, guid: 217443c668dc34029b583fa73e5c4dfc, type: 2}
m_ParentUI: {fileID: 0}
sourceAsset: {fileID: 9197481963319205126, guid: f9d1722b31b6d45a8b38e8167775d54d, type: 3}
m_SortingOrder: 0
m_Position: 0
m_WorldSpaceSizeMode: 1
m_WorldSpaceWidth: 1920
m_WorldSpaceHeight: 1080
m_PivotReferenceSize: 0
m_Pivot: 0
m_WorldSpaceCollider: {fileID: 0}
--- !u!4 &1940121733
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1940121731}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &2111630447
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 2111630449}
- component: {fileID: 2111630448}
m_Layer: 0
m_Name: Scripting Test
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &2111630448
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2111630447}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2c91f3289fa694fe3ba2e1e6149477b8, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::ScriptingTest
_filename: Beeple-CleanRoom-Hap.mov
_destination: {fileID: 8400000, guid: 1bba849debc024a4f8251bd62cceac60, type: 2}
_interval: 1
_iteration: 10
--- !u!4 &2111630449
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2111630447}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &2137533908
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 2137533909}
- component: {fileID: 2137533911}
- component: {fileID: 2137533910}
m_Layer: 0
m_Name: Quad
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &2137533909
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2137533908}
serializedVersion: 2
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: -2.75, y: 0, z: 0}
m_LocalScale: {x: 1.7777778, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 395240109}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!23 &2137533910
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2137533908}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_ForceMeshLod: -1
m_MeshLodSelectionBias: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: f4060b9dfe1254ee9bb9d4fad7ae4ebd, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_GlobalIlluminationMeshLod: 0
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!33 &2137533911
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2137533908}
m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0}
--- !u!1660057539 &9223372036854775807
SceneRoots:
m_ObjectHideFlags: 0
m_Roots:
- {fileID: 1940121733}
- {fileID: 1585188257}
- {fileID: 246925888}
- {fileID: 28633265}
- {fileID: 395240109}
- {fileID: 2111630449}
================================================
FILE: Assets/Demo.unity.meta
================================================
fileFormatVersion: 2
guid: 9d1e54e0ecdcb476597522d5c402dcdb
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/StreamingAssets/Tests/Hap/000001.png.meta
================================================
fileFormatVersion: 2
guid: f71b7a4c945804f1ca842cff0c10b46d
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/StreamingAssets/Tests/Hap/000002.png.meta
================================================
fileFormatVersion: 2
guid: ff68e82d75fdf47d8a5ad6b9c5fc99d3
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/StreamingAssets/Tests/Hap/000003.png.meta
================================================
fileFormatVersion: 2
guid: 0a2d50a92701046c4b2f72fb11f038b8
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/StreamingAssets/Tests/Hap/000004.png.meta
================================================
fileFormatVersion: 2
guid: ded47b6e71da14e1abb2ea48f2e60375
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/StreamingAssets/Tests/Hap/000005.png.meta
================================================
fileFormatVersion: 2
guid: 6861db0b44d0d4e78b701b38376b62c3
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/StreamingAssets/Tests/Hap/TestCards.mov.meta
================================================
fileFormatVersion: 2
guid: e3addd278331242d6b95bb6990e5216f
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/StreamingAssets/Tests/Hap.meta
================================================
fileFormatVersion: 2
guid: c4ca400c14640478aaf9bc0cec9b12c7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/StreamingAssets/Tests/HapAlpha/000001.png.meta
================================================
fileFormatVersion: 2
guid: 016f08f72c65549f69c9baa63b0e3ed0
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/StreamingAssets/Tests/HapAlpha/HapAlpha.mov.meta
================================================
fileFormatVersion: 2
guid: 9daa334d7e07e4a7092db093efe0efb8
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/StreamingAssets/Tests/HapAlpha.meta
================================================
fileFormatVersion: 2
guid: 362186a70fcc54855bcddda1af9f0e90
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/StreamingAssets/Tests/HapQ/000001.png.meta
================================================
fileFormatVersion: 2
guid: 1c1d2bb1e597e432391ee23db4b75d75
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/StreamingAssets/Tests/HapQ/000002.png.meta
================================================
fileFormatVersion: 2
guid: 469568f79f88b49f593d58b043aab26c
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/StreamingAssets/Tests/HapQ/000003.png.meta
================================================
fileFormatVersion: 2
guid: 6f8073f45b28847209cec3fa77e89e29
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/StreamingAssets/Tests/HapQ/000004.png.meta
================================================
fileFormatVersion: 2
guid: 554c0617cfb824ddfa28f0ea0ae8869c
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/StreamingAssets/Tests/HapQ/000005.png.meta
================================================
fileFormatVersion: 2
guid: 44d4725e9db92447ca83e803922e54f3
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/StreamingAssets/Tests/HapQ/TestCards.mov.meta
================================================
fileFormatVersion: 2
guid: 4ab52f88a57a94f50946db395a7e32c6
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/StreamingAssets/Tests/HapQ.meta
================================================
fileFormatVersion: 2
guid: 0a1a105c4ce2a40b28466bfc9f59f08f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/StreamingAssets/Tests/RGBCycle/RGBCycle24.mov.meta
================================================
fileFormatVersion: 2
guid: d17c63c5f7293493ca0b65bfc71c7808
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/StreamingAssets/Tests/RGBCycle/RGBCycle24000-1001.mov.meta
================================================
fileFormatVersion: 2
guid: 9b88a94eb79614c7b8be1396430b7fb9
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/StreamingAssets/Tests/RGBCycle/RGBCycle25.mov.meta
================================================
fileFormatVersion: 2
guid: 20f5482d0639347b4b0d62b7f553d61a
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/StreamingAssets/Tests/RGBCycle/RGBCycle30.mov.meta
================================================
fileFormatVersion: 2
guid: 04a711eb985854488a36cecaa629492c
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/StreamingAssets/Tests/RGBCycle/RGBCycle30000-1001.mov.meta
================================================
fileFormatVersion: 2
guid: 5b06b773e4b22470b9572d25f575eb91
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/StreamingAssets/Tests/RGBCycle/RGBCycle50.mov.meta
================================================
fileFormatVersion: 2
guid: 35a0a8d8451fe4dff943ae124f8fc02c
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/StreamingAssets/Tests/RGBCycle/RGBCycle60.mov.meta
================================================
fileFormatVersion: 2
guid: 1103893dcc48f48cbbf5f4681f02ccb4
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/StreamingAssets/Tests/RGBCycle/RGBCycle60000-1001.mov.meta
================================================
fileFormatVersion: 2
guid: 005f182f1b759493eadafadc1102eea9
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/StreamingAssets/Tests/RGBCycle.meta
================================================
fileFormatVersion: 2
guid: eaa1315fa4f1141c987260e3f3023bcf
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/StreamingAssets/Tests/日本語/Test.mov.meta
================================================
fileFormatVersion: 2
guid: a32999a4f135840ec9876594a54a0b6a
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/StreamingAssets/Tests/日本語.meta
================================================
fileFormatVersion: 2
guid: 7a8b3546c2bae4b729e75ccdedeab1d8
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/StreamingAssets/Tests.meta
================================================
fileFormatVersion: 2
guid: 026a9869dae424ee8b8609aa087fec1c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/StreamingAssets.meta
================================================
fileFormatVersion: 2
guid: de35ded2fd399421a88ca973172cc856
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/Tests/Runtime/HapAlphaPlaybackTest.cs
================================================
using System.Collections;
using System.IO;
using Klak.Hap;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
public sealed class HapAlphaPlaybackTest
{
const string MoviePath = "Tests/HapAlpha/HapAlpha.mov";
const string FramesDirName = "Tests/HapAlpha";
const string ExpectedFrame = "000001.png";
const float MatchThreshold = 0.99f;
const float ChannelTolerance = 0.08f;
const float AlphaTolerance = 0.08f;
const int SamplesPerAxis = 64;
const bool FlipExpectedY = true;
[UnityTest]
public IEnumerator HapAlphaMatchesExtractedFrame()
{
if (!Application.isPlaying)
{
Assert.Ignore("PlayMode only.");
yield break;
}
var go = new GameObject("HapAlphaTestPlayer");
try
{
var player = go.AddComponent();
player.Open(MoviePath, HapPlayer.PathMode.StreamingAssets);
player.loop = false;
player.speed = 0;
yield return null;
player.UpdateNow();
var pathInfo = $"Resolved path: {player.resolvedFilePath}";
Assert.That(player.isValid, Is.True, pathInfo);
Assert.That(player.frameCount, Is.GreaterThan(0), pathInfo);
Assert.That(player.frameWidth, Is.GreaterThan(0), pathInfo);
Assert.That(player.frameHeight, Is.GreaterThan(0), pathInfo);
Assert.That(player.texture, Is.Not.Null, pathInfo);
var duration = (float)player.streamDuration;
var dt = duration / player.frameCount;
player.time = dt * 0.1f;
player.UpdateNow();
yield return null;
yield return null;
var expected = LoadExpectedTexture(ExpectedFrame);
try
{
var ratio = ComputeMatchRatio(player.texture, expected);
var message = $"{pathInfo} Expected:{ExpectedFrame} Match:{ratio:0.000}";
Assert.That(ratio, Is.GreaterThanOrEqualTo(MatchThreshold), message);
}
finally
{
Object.Destroy(expected);
}
}
finally
{
Object.Destroy(go);
}
}
static Texture2D LoadExpectedTexture(string fileName)
{
var path = Path.Combine(Application.streamingAssetsPath, FramesDirName, fileName);
var data = File.ReadAllBytes(path);
var tex = new Texture2D(2, 2, TextureFormat.RGBA32, false);
tex.LoadImage(data);
return tex;
}
static float ComputeMatchRatio(Texture2D actual, Texture2D expected)
{
var width = Mathf.Min(actual.width, expected.width);
var height = Mathf.Min(actual.height, expected.height);
var stepX = Mathf.Max(1, width / SamplesPerAxis);
var stepY = Mathf.Max(1, height / SamplesPerAxis);
var match = 0;
var total = 0;
for (var y = stepY / 2; y < height; y += stepY)
for (var x = stepX / 2; x < width; x += stepX)
{
var a = actual.GetPixel(x, y);
var ey = FlipExpectedY ? height - 1 - y : y;
var e = expected.GetPixel(x, ey);
if (IsMatch(a, e)) match++;
total++;
}
return total > 0 ? (float)match / total : 0;
}
static bool IsMatch(Color actual, Color expected)
=> Mathf.Abs(actual.r - expected.r) <= ChannelTolerance
&& Mathf.Abs(actual.g - expected.g) <= ChannelTolerance
&& Mathf.Abs(actual.b - expected.b) <= ChannelTolerance
&& Mathf.Abs(actual.a - expected.a) <= AlphaTolerance;
}
================================================
FILE: Assets/Tests/Runtime/HapAlphaPlaybackTest.cs.meta
================================================
fileFormatVersion: 2
guid: 836849b85f39e4f2eb69dc82dd0bf0fe
================================================
FILE: Assets/Tests/Runtime/JapanesePathPlaybackTest.cs
================================================
using System.Collections;
using Klak.Hap;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
public sealed class JapanesePathPlaybackTest
{
const string TestPath = "Tests/日本語/Test.mov";
[UnityTest]
public IEnumerator CanPlayHapInJapanesePath()
{
if (!Application.isPlaying)
{
Assert.Ignore("PlayMode only.");
yield break;
}
var go = new GameObject("HapTestPlayer");
try
{
var player = go.AddComponent();
player.Open(TestPath, HapPlayer.PathMode.StreamingAssets);
for (var i = 0; i < 5; i++)
{
yield return null;
player.UpdateNow();
}
var pathInfo = $"Resolved path: {player.resolvedFilePath}";
Assert.That(player.isValid, Is.True, pathInfo);
Assert.That(player.frameCount, Is.GreaterThan(0), pathInfo);
Assert.That(player.frameWidth, Is.GreaterThan(0), pathInfo);
Assert.That(player.frameHeight, Is.GreaterThan(0), pathInfo);
Assert.That(player.texture, Is.Not.Null, pathInfo);
Assert.That(player.codecType, Is.Not.EqualTo(CodecType.Unsupported), pathInfo);
}
finally
{
Object.Destroy(go);
}
}
}
================================================
FILE: Assets/Tests/Runtime/JapanesePathPlaybackTest.cs.meta
================================================
fileFormatVersion: 2
guid: 0397f44a895774978874377ddf34f82c
================================================
FILE: Assets/Tests/Runtime/Klak.Hap.Runtime.Tests.asmdef
================================================
{
"name": "Klak.Hap.Runtime.Tests",
"rootNamespace": "",
"references": [
"Klak.Hap",
"UnityEngine.TestRunner"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}
================================================
FILE: Assets/Tests/Runtime/Klak.Hap.Runtime.Tests.asmdef.meta
================================================
fileFormatVersion: 2
guid: 1c2d58c8d70c843c7a4747274235802b
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/Tests/Runtime/RgbCycleFrameMappingTest.cs
================================================
using System.Collections;
using Klak.Hap;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
public sealed class RgbCycleFrameMappingTest
{
static readonly string[] TestPaths =
{
"Tests/RGBCycle/RGBCycle24.mov",
"Tests/RGBCycle/RGBCycle24000-1001.mov",
"Tests/RGBCycle/RGBCycle25.mov",
"Tests/RGBCycle/RGBCycle30.mov",
"Tests/RGBCycle/RGBCycle30000-1001.mov",
"Tests/RGBCycle/RGBCycle50.mov",
"Tests/RGBCycle/RGBCycle60.mov",
"Tests/RGBCycle/RGBCycle60000-1001.mov"
};
enum RgbExpected { Red, Green, Blue }
[UnityTest]
public IEnumerator RgbCycleFrameMappingIsCorrect()
{
if (!Application.isPlaying)
{
Assert.Ignore("PlayMode only.");
yield break;
}
foreach (var path in TestPaths)
foreach (var step in VerifyFile(path))
yield return step;
}
IEnumerable VerifyFile(string path)
{
var go = new GameObject("HapTestPlayer");
try
{
var player = go.AddComponent();
player.Open(path, HapPlayer.PathMode.StreamingAssets);
player.loop = false;
player.speed = 0;
yield return null;
player.UpdateNow();
var pathInfo = $"Resolved path: {player.resolvedFilePath}";
Assert.That(player.isValid, Is.True, pathInfo);
Assert.That(player.frameCount, Is.GreaterThan(0), pathInfo);
Assert.That(player.frameWidth, Is.GreaterThan(0), pathInfo);
Assert.That(player.frameHeight, Is.GreaterThan(0), pathInfo);
Assert.That(player.texture, Is.Not.Null, pathInfo);
var duration = (float)player.streamDuration;
var frameCount = player.frameCount;
var dt = duration / frameCount;
var maxFrames = Mathf.Min(frameCount, Mathf.FloorToInt(0.5f / dt) + 1);
var cx = player.frameWidth / 2;
var cy = player.frameHeight / 2;
for (var i = 0; i < maxFrames; i++)
{
player.time = i * dt + dt * 0.1f;
player.UpdateNow();
yield return null;
yield return null;
var actual = player.texture.GetPixel(cx, cy);
var expected = ExpectedForFrame(i);
AssertRgbColor(actual, expected, pathInfo, i);
yield return null;
yield return null;
}
}
finally
{
Object.Destroy(go);
}
}
static RgbExpected ExpectedForFrame(int frameIndex)
=> (RgbExpected)(frameIndex % 3);
static void AssertRgbColor(Color actual, RgbExpected expected, string pathInfo, int frameIndex)
{
var message = $"{pathInfo} Frame: {frameIndex} Color: {actual}";
if (expected == RgbExpected.Red)
{
Assert.That(actual.r, Is.GreaterThan(0.8f), message);
Assert.That(actual.g, Is.LessThan(0.2f), message);
Assert.That(actual.b, Is.LessThan(0.2f), message);
}
else if (expected == RgbExpected.Green)
{
Assert.That(actual.g, Is.GreaterThan(0.8f), message);
Assert.That(actual.r, Is.LessThan(0.2f), message);
Assert.That(actual.b, Is.LessThan(0.2f), message);
}
else
{
Assert.That(actual.b, Is.GreaterThan(0.8f), message);
Assert.That(actual.r, Is.LessThan(0.2f), message);
Assert.That(actual.g, Is.LessThan(0.2f), message);
}
}
}
================================================
FILE: Assets/Tests/Runtime/RgbCycleFrameMappingTest.cs.meta
================================================
fileFormatVersion: 2
guid: e48e4d437ac8346139c9ba9f277e7beb
================================================
FILE: Assets/Tests/Runtime/TestCardsPlaybackTest.cs
================================================
using System.Collections;
using System.IO;
using Klak.Hap;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
public sealed class TestCardsPlaybackTest
{
const string HapMoviePath = "Tests/Hap/TestCards.mov";
const string HapFramesDirName = "Tests/Hap";
const string HapQMoviePath = "Tests/HapQ/TestCards.mov";
const string HapQFramesDirName = "Tests/HapQ";
const float MatchThreshold = 0.99f;
const float ChannelTolerance = 0.08f;
static readonly string[] FramePngs =
{
"000001.png",
"000002.png",
"000003.png",
"000004.png",
"000005.png"
};
[UnityTest]
public IEnumerator TestCardsMatchPngFrames()
{
if (!Application.isPlaying)
{
Assert.Ignore("PlayMode only.");
yield break;
}
foreach (var step in VerifySet("HapTestPlayer", HapMoviePath, HapFramesDirName))
yield return step;
foreach (var step in VerifySet("HapQTestPlayer", HapQMoviePath, HapQFramesDirName))
yield return step;
}
IEnumerable VerifySet(string objectName, string moviePath, string framesDirName)
{
var go = new GameObject(objectName);
RenderTexture targetTexture = null;
Texture2D readbackTexture = null;
try
{
var player = go.AddComponent();
player.Open(moviePath, HapPlayer.PathMode.StreamingAssets);
player.loop = false;
player.speed = 0;
yield return null;
player.UpdateNow();
var pathInfo = $"Resolved path: {player.resolvedFilePath}";
Assert.That(player.isValid, Is.True, pathInfo);
Assert.That(player.frameCount, Is.GreaterThanOrEqualTo(FramePngs.Length), pathInfo);
Assert.That(player.frameWidth, Is.GreaterThan(0), pathInfo);
Assert.That(player.frameHeight, Is.GreaterThan(0), pathInfo);
Assert.That(player.texture, Is.Not.Null, pathInfo);
targetTexture = new RenderTexture(
player.frameWidth, player.frameHeight, 0, RenderTextureFormat.ARGB32
);
targetTexture.wrapMode = TextureWrapMode.Clamp;
player.targetTexture = targetTexture;
readbackTexture = new Texture2D(
player.frameWidth, player.frameHeight, TextureFormat.RGBA32, false
);
var duration = (float)player.streamDuration;
var frameCount = player.frameCount;
var dt = duration / frameCount;
for (var i = 0; i < FramePngs.Length; i++)
{
player.time = i * dt + dt * 0.1f;
player.UpdateNow();
yield return null;
yield return null;
ReadRenderTexture(targetTexture, readbackTexture);
var actualTexture = readbackTexture;
var expected = LoadExpectedTexture(framesDirName, FramePngs[i]);
try
{
var ratio = ComputeMatchRatio(actualTexture, expected);
var message = $"{pathInfo} Frame:{i} Expected:{FramePngs[i]} Match:{ratio:0.000}";
Assert.That(ratio, Is.GreaterThanOrEqualTo(MatchThreshold), message);
}
finally
{
Object.Destroy(expected);
}
yield return null;
yield return null;
}
}
finally
{
if (targetTexture != null)
targetTexture.Release();
Object.Destroy(targetTexture);
Object.Destroy(readbackTexture);
Object.Destroy(go);
}
}
static Texture2D LoadExpectedTexture(string framesDirName, string fileName)
{
var path = Path.Combine(Application.streamingAssetsPath, framesDirName, fileName);
var data = File.ReadAllBytes(path);
var tex = new Texture2D(2, 2, TextureFormat.RGBA32, false);
tex.LoadImage(data);
return tex;
}
static float ComputeMatchRatio(Texture2D actual, Texture2D expected)
{
var width = Mathf.Min(actual.width, expected.width);
var height = Mathf.Min(actual.height, expected.height);
var match = 0;
var total = width * height;
for (var y = 0; y < height; y++)
for (var x = 0; x < width; x++)
{
var a = actual.GetPixel(x, y);
var e = expected.GetPixel(x, y);
if (IsMatch(a, e)) match++;
}
return total > 0 ? (float)match / total : 0;
}
static bool IsMatch(Color actual, Color expected)
=> Mathf.Abs(actual.r - expected.r) <= ChannelTolerance
&& Mathf.Abs(actual.g - expected.g) <= ChannelTolerance
&& Mathf.Abs(actual.b - expected.b) <= ChannelTolerance;
static void ReadRenderTexture(RenderTexture source, Texture2D destination)
{
var previous = RenderTexture.active;
RenderTexture.active = source;
destination.ReadPixels(new Rect(0, 0, source.width, source.height), 0, 0);
destination.Apply();
RenderTexture.active = previous;
}
}
================================================
FILE: Assets/Tests/Runtime/TestCardsPlaybackTest.cs.meta
================================================
fileFormatVersion: 2
guid: 501079e0a007c4c9686d841e0bcb0b60
================================================
FILE: Assets/Tests/Runtime.meta
================================================
fileFormatVersion: 2
guid: 6f2caeec0887b4dd5a03360e87b42af7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Assets/Tests.meta
================================================
fileFormatVersion: 2
guid: c9392bf0e434b4e378744f7ed2695485
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: CHANGELOG.md
================================================
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [1.0.0] - 2026-02-05
### Added
- Added a Unity 6.3 demo scene updated for URP and UI Toolkit.
- Added runtime tests and a test data downloader for Hap, HapQ, and HapAlpha assets.
### Changed
- Updated HAP and Snappy source code and refreshed native plugin binaries.
- Migrated Windows builds to MinGW and refactored the native build pipelines and Makefiles.
### Fixed
- Fixed a Windows crash when paths contain Japanese characters. (PR #49)
- Fixed obsolete ShaderUtil API usage in the editor code.
## [0.1.20] - 2021-12-03
### Added
- Added `UpdateNow` for custom Timeline integration.
## [0.1.19] - 2021-06-05
### Added
- Added arm64 binary to the macOS plugin.
- Added no-delay mode.
- Added decoder thread control improvement.
### Changed
- Updated for Unity 2020.3.
- Moved package contexts into the Packages directory.
### Fixed
- Fixed rounding error issue.
- Fixed compilation error on Unity 2019.4.
================================================
FILE: LICENSE
================================================
License summary:
KlakHAP - MIT license
HAP codec - FreeBSD license
Snappy - BSD 3-clause license
MP4 demuxer - CC0 (public domain)
-------------------------------------------------------------------------------
KlakHAP
https://github.com/keijiro/KlakHap
-------------------------------------------------------------------------------
Copyright (c) 2019 Unity Technologies
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-------------------------------------------------------------------------------
HAP
https://github.com/Vidvox/hap
-------------------------------------------------------------------------------
Copyright (c) 2012-2013, Tom Butterworth and Vidvox LLC. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-------------------------------------------------------------------------------
Snappy
https://github.com/google/snappy
-------------------------------------------------------------------------------
Copyright 2011, Google Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-------------------------------------------------------------------------------
Minimalistic MP4 muxer & demuxer
https://github.com/aspt/mp4
-------------------------------------------------------------------------------
CC0 1.0 Universal
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator and
subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for the
purpose of contributing to a commons of creative, cultural and scientific
works ("Commons") that the public can reliably and without fear of later
claims of infringement build upon, modify, incorporate in other works, reuse
and redistribute as freely as possible in any form whatsoever and for any
purposes, including without limitation commercial purposes. These owners may
contribute to the Commons to promote the ideal of a free culture and the
further production of creative, cultural and scientific works, or to gain
reputation or greater distribution for their Work in part through the use and
efforts of others.
For these and/or other purposes and motivations, and without any expectation
of additional consideration or compensation, the person associating CC0 with a
Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
and publicly distribute the Work under its terms, with knowledge of his or her
Copyright and Related Rights in the Work and the meaning and intended legal
effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not limited
to, the following:
i. the right to reproduce, adapt, distribute, perform, display, communicate,
and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or likeness
depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data in
a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation thereof,
including any amended or successor version of such directive); and
vii. other similar, equivalent or corresponding rights throughout the world
based on applicable law or treaty, and any national implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention of,
applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
and Related Rights and associated claims and causes of action, whether now
known or unknown (including existing as well as future claims and causes of
action), in the Work (i) in all territories worldwide, (ii) for the maximum
duration provided by applicable law or treaty (including future time
extensions), (iii) in any current or future medium and for any number of
copies, and (iv) for any purpose whatsoever, including without limitation
commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
the Waiver for the benefit of each member of the public at large and to the
detriment of Affirmer's heirs and successors, fully intending that such Waiver
shall not be subject to revocation, rescission, cancellation, termination, or
any other legal or equitable action to disrupt the quiet enjoyment of the Work
by the public as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason be
judged legally invalid or ineffective under applicable law, then the Waiver
shall be preserved to the maximum extent permitted taking into account
Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
is so judged Affirmer hereby grants to each affected person a royalty-free,
non transferable, non sublicensable, non exclusive, irrevocable and
unconditional license to exercise Affirmer's Copyright and Related Rights in
the Work (i) in all territories worldwide, (ii) for the maximum duration
provided by applicable law or treaty (including future time extensions), (iii)
in any current or future medium and for any number of copies, and (iv) for any
purpose whatsoever, including without limitation commercial, advertising or
promotional purposes (the "License"). The License shall be deemed effective as
of the date CC0 was applied by Affirmer to the Work. Should any part of the
License for any reason be judged legally invalid or ineffective under
applicable law, such partial invalidity or ineffectiveness shall not
invalidate the remainder of the License, and in such case Affirmer hereby
affirms that he or she will not (i) exercise any of his or her remaining
Copyright and Related Rights in the Work or (ii) assert any associated claims
and causes of action with respect to the Work, in either case contrary to
Affirmer's express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or warranties
of any kind concerning the Work, express, implied, statutory or otherwise,
including without limitation warranties of title, merchantability, fitness
for a particular purpose, non infringement, or the absence of latent or
other defects, accuracy, or the present or absence of errors, whether or not
discoverable, all to the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without limitation
any person's Copyright and Related Rights in the Work. Further, Affirmer
disclaims responsibility for obtaining any necessary consents, permissions
or other rights required for any use of the Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to this
CC0 or use of the Work.
For more information, please see
================================================
FILE: Packages/jp.keijiro.klak.hap/CHANGELOG.md
================================================
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [1.0.0] - 2026-02-05
### Added
- Added a Unity 6.3 demo scene updated for URP and UI Toolkit.
- Added runtime tests and a test data downloader for Hap, HapQ, and HapAlpha assets.
### Changed
- Updated HAP and Snappy source code and refreshed native plugin binaries.
- Migrated Windows builds to MinGW and refactored the native build pipelines and Makefiles.
### Fixed
- Fixed a Windows crash when paths contain Japanese characters. (PR #49)
- Fixed obsolete ShaderUtil API usage in the editor code.
## [0.1.20] - 2021-12-03
### Added
- Added `UpdateNow` for custom Timeline integration.
## [0.1.19] - 2021-06-05
### Added
- Added arm64 binary to the macOS plugin.
- Added no-delay mode.
- Added decoder thread control improvement.
### Changed
- Updated for Unity 2020.3.
- Moved package contexts into the Packages directory.
### Fixed
- Fixed rounding error issue.
- Fixed compilation error on Unity 2019.4.
================================================
FILE: Packages/jp.keijiro.klak.hap/CHANGELOG.md.meta
================================================
fileFormatVersion: 2
guid: 1f2cacd9a85145aca9f81904d75ae53b
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Packages/jp.keijiro.klak.hap/Editor/HapPlayerEditor.cs
================================================
using UnityEngine;
using UnityEditor;
namespace Klak.Hap
{
[CanEditMultipleObjects]
[CustomEditor(typeof(HapPlayer))]
sealed class HapPlayerEditor : Editor
{
SerializedProperty _filePath;
SerializedProperty _pathMode;
SerializedProperty _time;
SerializedProperty _speed;
SerializedProperty _loop;
SerializedProperty _targetTexture;
SerializedProperty _targetRenderer;
SerializedProperty _targetMaterialProperty;
static class Labels
{
public static readonly GUIContent Property = new GUIContent("Property");
public static readonly GUIContent Select = new GUIContent("Select");
}
string _sourceInfo;
void ShowSourceInfo(HapPlayer player)
{
if (!player.enabled || !player.gameObject.activeInHierarchy) return;
if (!player.isValid)
{
EditorGUILayout.HelpBox(
"Failed to open file. " +
"Please specify a valid HAP-encoded .mov file.",
MessageType.Warning
);
return;
}
if (_sourceInfo == null)
_sourceInfo = string.Format(
"Codec: {0}\n" +
"Frame dimensions: {1} x {2}\n" +
"Stream duration: {3:0.00}\n" +
"Frame rate: {4:0.00}",
player.codecType,
player.frameWidth, player.frameHeight,
player.streamDuration,
player.frameCount / player.streamDuration
);
EditorGUILayout.HelpBox(_sourceInfo, MessageType.None);
}
void OnEnable()
{
_filePath = serializedObject.FindProperty("_filePath");
_pathMode = serializedObject.FindProperty("_pathMode");
_time = serializedObject.FindProperty("_time");
_speed = serializedObject.FindProperty("_speed");
_loop = serializedObject.FindProperty("_loop");
_targetTexture = serializedObject.FindProperty("_targetTexture");
_targetRenderer = serializedObject.FindProperty("_targetRenderer");
_targetMaterialProperty = serializedObject.FindProperty("_targetMaterialProperty");
}
public override void OnInspectorGUI()
{
var reload = false;
serializedObject.Update();
// Source infomation
if (!_filePath.hasMultipleDifferentValues &&
!_pathMode.hasMultipleDifferentValues &&
!string.IsNullOrEmpty(_filePath.stringValue))
{
ShowSourceInfo((HapPlayer)target);
}
// Source file
EditorGUI.BeginChangeCheck();
EditorGUILayout.DelayedTextField(_filePath);
EditorGUILayout.PropertyField(_pathMode);
reload = EditorGUI.EndChangeCheck();
// Playback control
EditorGUILayout.PropertyField(_time);
EditorGUILayout.PropertyField(_speed);
EditorGUILayout.PropertyField(_loop);
// Target texture/renderer
EditorGUILayout.PropertyField(_targetTexture);
EditorGUILayout.PropertyField(_targetRenderer);
EditorGUI.indentLevel++;
if (_targetRenderer.hasMultipleDifferentValues)
{
// Multiple renderers selected: Show a simple text field.
EditorGUILayout.PropertyField(_targetMaterialProperty, Labels.Property);
}
else if (_targetRenderer.objectReferenceValue != null)
{
// Single renderer: Show the material property selection dropdown.
MaterialPropertySelector.DropdownList(_targetRenderer, _targetMaterialProperty);
}
EditorGUI.indentLevel--;
serializedObject.ApplyModifiedProperties();
if (reload)
{
// This is a little bit scary hack but we can force a HapPlayer
// to reload a given video file by invoking OnDestroy.
foreach (HapPlayer hp in targets)
{
hp.SendMessage("OnDestroy");
hp.SendMessage("LateUpdate");
}
// Also the source information string should be refreshed.
_sourceInfo = null;
}
}
}
}
================================================
FILE: Packages/jp.keijiro.klak.hap/Editor/HapPlayerEditor.cs.meta
================================================
fileFormatVersion: 2
guid: 5afaabe3aee5cdb46a3846f74cc3a8a7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Packages/jp.keijiro.klak.hap/Editor/Klak.Hap.Editor.asmdef
================================================
{
"name": "Klak.Hap.Editor",
"references": [
"Klak.Hap"
],
"optionalUnityReferences": [],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": []
}
================================================
FILE: Packages/jp.keijiro.klak.hap/Editor/Klak.Hap.Editor.asmdef.meta
================================================
fileFormatVersion: 2
guid: 59c1346744fe3194291b617e412c56c4
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Packages/jp.keijiro.klak.hap/Editor/MaterialPropertySelector.cs
================================================
using UnityEngine;
using UnityEngine.Rendering;
using UnityEditor;
using System;
using System.Collections.Generic;
namespace Klak.Hap
{
static class MaterialPropertySelector
{
#region Public method
// Material property drop-down list
public static void DropdownList(
SerializedProperty rendererProperty,
SerializedProperty materialProperty
)
{
// Try retrieving the target shader.
var shader = RetrieveTargetShader(rendererProperty);
// Abandon the current value if it failed to get a shader.
if (shader == null)
{
materialProperty.stringValue = "";
return;
}
// Cache property names found in the target shader.
CachePropertyNames(shader);
// Abandon the current value if there is no property candidate.
if (_propertyNames.Length == 0)
{
materialProperty.stringValue = "";
return;
}
// Show the dropdown list.
var index = Array.IndexOf(_propertyNames, materialProperty.stringValue);
var newIndex = EditorGUILayout.Popup("Property", index, _propertyNames);
// Update the serialized property if the selection was changed.
if (index != newIndex) materialProperty.stringValue = _propertyNames[newIndex];
}
#endregion
#region Private members
static string[] _propertyNames; // Property name list
static Shader _cachedShader; // Shader used to cache the name list
// Retrieve a shader from a given renderer.
static Shader RetrieveTargetShader(SerializedProperty rendererProperty)
{
var renderer = rendererProperty.objectReferenceValue as Renderer;
if (renderer == null) return null;
var material = renderer.sharedMaterial;
if (material == null) return null;
return material.shader;
}
// Cache property names provided within a specified shader.
static void CachePropertyNames(Shader shader)
{
// Exit early when the shader is same to the cached one.
if (shader == _cachedShader) return;
var temp = new List();
var count = shader.GetPropertyCount();
for (var i = 0; i < count; i++)
{
var propType = shader.GetPropertyType(i);
if (propType == ShaderPropertyType.Texture)
temp.Add(shader.GetPropertyName(i));
}
_propertyNames = temp.ToArray();
_cachedShader = shader;
}
#endregion
}
}
================================================
FILE: Packages/jp.keijiro.klak.hap/Editor/MaterialPropertySelector.cs.meta
================================================
fileFormatVersion: 2
guid: 8ab866082e30404428d5298d084cc13a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Packages/jp.keijiro.klak.hap/Editor.meta
================================================
fileFormatVersion: 2
guid: 2a6f023ffc242a743a38d13e6cae3b66
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Packages/jp.keijiro.klak.hap/LICENSE
================================================
License summary:
KlakHAP - MIT license
HAP codec - FreeBSD license
Snappy - BSD 3-clause license
MP4 demuxer - CC0 (public domain)
-------------------------------------------------------------------------------
KlakHAP
https://github.com/keijiro/KlakHap
-------------------------------------------------------------------------------
Copyright (c) 2019 Unity Technologies
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-------------------------------------------------------------------------------
HAP
https://github.com/Vidvox/hap
-------------------------------------------------------------------------------
Copyright (c) 2012-2013, Tom Butterworth and Vidvox LLC. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-------------------------------------------------------------------------------
Snappy
https://github.com/google/snappy
-------------------------------------------------------------------------------
Copyright 2011, Google Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-------------------------------------------------------------------------------
Minimalistic MP4 muxer & demuxer
https://github.com/aspt/mp4
-------------------------------------------------------------------------------
CC0 1.0 Universal
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator and
subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for the
purpose of contributing to a commons of creative, cultural and scientific
works ("Commons") that the public can reliably and without fear of later
claims of infringement build upon, modify, incorporate in other works, reuse
and redistribute as freely as possible in any form whatsoever and for any
purposes, including without limitation commercial purposes. These owners may
contribute to the Commons to promote the ideal of a free culture and the
further production of creative, cultural and scientific works, or to gain
reputation or greater distribution for their Work in part through the use and
efforts of others.
For these and/or other purposes and motivations, and without any expectation
of additional consideration or compensation, the person associating CC0 with a
Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
and publicly distribute the Work under its terms, with knowledge of his or her
Copyright and Related Rights in the Work and the meaning and intended legal
effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not limited
to, the following:
i. the right to reproduce, adapt, distribute, perform, display, communicate,
and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or likeness
depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data in
a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation thereof,
including any amended or successor version of such directive); and
vii. other similar, equivalent or corresponding rights throughout the world
based on applicable law or treaty, and any national implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention of,
applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
and Related Rights and associated claims and causes of action, whether now
known or unknown (including existing as well as future claims and causes of
action), in the Work (i) in all territories worldwide, (ii) for the maximum
duration provided by applicable law or treaty (including future time
extensions), (iii) in any current or future medium and for any number of
copies, and (iv) for any purpose whatsoever, including without limitation
commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
the Waiver for the benefit of each member of the public at large and to the
detriment of Affirmer's heirs and successors, fully intending that such Waiver
shall not be subject to revocation, rescission, cancellation, termination, or
any other legal or equitable action to disrupt the quiet enjoyment of the Work
by the public as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason be
judged legally invalid or ineffective under applicable law, then the Waiver
shall be preserved to the maximum extent permitted taking into account
Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
is so judged Affirmer hereby grants to each affected person a royalty-free,
non transferable, non sublicensable, non exclusive, irrevocable and
unconditional license to exercise Affirmer's Copyright and Related Rights in
the Work (i) in all territories worldwide, (ii) for the maximum duration
provided by applicable law or treaty (including future time extensions), (iii)
in any current or future medium and for any number of copies, and (iv) for any
purpose whatsoever, including without limitation commercial, advertising or
promotional purposes (the "License"). The License shall be deemed effective as
of the date CC0 was applied by Affirmer to the Work. Should any part of the
License for any reason be judged legally invalid or ineffective under
applicable law, such partial invalidity or ineffectiveness shall not
invalidate the remainder of the License, and in such case Affirmer hereby
affirms that he or she will not (i) exercise any of his or her remaining
Copyright and Related Rights in the Work or (ii) assert any associated claims
and causes of action with respect to the Work, in either case contrary to
Affirmer's express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or warranties
of any kind concerning the Work, express, implied, statutory or otherwise,
including without limitation warranties of title, merchantability, fitness
for a particular purpose, non infringement, or the absence of latent or
other defects, accuracy, or the present or absence of errors, whether or not
discoverable, all to the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without limitation
any person's Copyright and Related Rights in the Work. Further, Affirmer
disclaims responsibility for obtaining any necessary consents, permissions
or other rights required for any use of the Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to this
CC0 or use of the Work.
For more information, please see
================================================
FILE: Packages/jp.keijiro.klak.hap/LICENSE.meta
================================================
fileFormatVersion: 2
guid: ee05d860d6934052af1f4d5fb541a384
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Packages/jp.keijiro.klak.hap/Plugin/Linux/libKlakHap.so.meta
================================================
fileFormatVersion: 2
guid: aefd4aee030ec9a56930540080f31258
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
platformData:
- first:
'': Any
second:
enabled: 0
settings:
Exclude Editor: 0
Exclude Linux: 1
Exclude Linux64: 0
Exclude LinuxUniversal: 0
Exclude OSXUniversal: 1
Exclude Win: 0
Exclude Win64: 0
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
CPU: x86_64
DefaultValueInitialized: true
OS: Linux
- first:
Facebook: Win
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Facebook: Win64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Linux
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Linux64
second:
enabled: 1
settings:
CPU: x86_64
- first:
Standalone: LinuxUniversal
second:
enabled: 1
settings:
CPU: x86_64
- first:
Standalone: OSXUniversal
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Win
second:
enabled: 1
settings:
CPU: AnyCPU
- first:
Standalone: Win64
second:
enabled: 1
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Packages/jp.keijiro.klak.hap/Plugin/Linux.meta
================================================
fileFormatVersion: 2
guid: 18114d790aea9f97e9b9f56ab458bc9d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Packages/jp.keijiro.klak.hap/Plugin/MacOS/KlakHap.bundle.meta
================================================
fileFormatVersion: 2
guid: 68eb9c44d2cd647b8a246c85c2516a6c
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
: Any
second:
enabled: 0
settings:
Exclude Editor: 0
Exclude Linux64: 1
Exclude OSXUniversal: 0
Exclude Win: 1
Exclude Win64: 1
Exclude iOS: 1
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: OSX
- first:
Standalone: Linux64
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: OSXUniversal
second:
enabled: 1
settings:
CPU: AnyCPU
- first:
Standalone: Win
second:
enabled: 0
settings:
CPU: x86
- first:
Standalone: Win64
second:
enabled: 0
settings:
CPU: x86_64
- first:
iPhone: iOS
second:
enabled: 0
settings:
AddToEmbeddedBinaries: false
CPU: AnyCPU
CompileFlags:
FrameworkDependencies:
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Packages/jp.keijiro.klak.hap/Plugin/MacOS.meta
================================================
fileFormatVersion: 2
guid: b3fc36a147f3a4fe19b3fe7371cfe554
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Packages/jp.keijiro.klak.hap/Plugin/Windows/KlakHap.dll.meta
================================================
fileFormatVersion: 2
guid: 3ab59e419166f214abf75fa29aac993e
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
platformData:
- first:
'': Any
second:
enabled: 0
settings:
Exclude Editor: 0
Exclude Linux: 0
Exclude Linux64: 0
Exclude LinuxUniversal: 0
Exclude OSXUniversal: 0
Exclude Win: 1
Exclude Win64: 0
- first:
Any:
second:
enabled: 1
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
CPU: x86_64
DefaultValueInitialized: true
OS: Windows
- first:
Facebook: Win
second:
enabled: 0
settings:
CPU: None
- first:
Facebook: Win64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Linux
second:
enabled: 1
settings:
CPU: x86
- first:
Standalone: Linux64
second:
enabled: 1
settings:
CPU: x86_64
- first:
Standalone: LinuxUniversal
second:
enabled: 1
settings:
CPU: AnyCPU
- first:
Standalone: OSXUniversal
second:
enabled: 1
settings:
CPU: AnyCPU
- first:
Standalone: Win
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Win64
second:
enabled: 1
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Packages/jp.keijiro.klak.hap/Plugin/Windows.meta
================================================
fileFormatVersion: 2
guid: dc34545ab496569488b8cd56973907d2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Packages/jp.keijiro.klak.hap/Plugin.meta
================================================
fileFormatVersion: 2
guid: 39f09b654953c8e45bc1d12d19cceaef
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Packages/jp.keijiro.klak.hap/README.md
================================================
# KlakHap

**KlakHap** is a Unity plugin for playing back video streams encoded with the
[HAP video codecs].
HAP is a fast and high-quality video codec often used in real-time interactive
applications. From the HAP Codecs website:
> The HAP codecs are designed to fit the needs of a variety of real-time video
> workflows where ultra high resolution video is needed such as live event
> production, set design, 360 video for gaming, projection mapping and creative
> coding.
KlakHap provides decoded frames as textures that you can use anywhere in
Unity's rendering pipeline: apply them to a material, present full-screen
video, animate a UI element, and more. Thanks to the efficient design and
implementation of the HAP codecs, it can adjust playback time and speed
dynamically without hiccups.
[HAP video codecs]: https://hap.video/
# System requirements
- Unity 2022.3 or later
Currently, KlakHap supports only 64-bit desktop platforms (Windows, macOS, and
Linux).
# Supported formats
KlakHap supports **HAP**, **HAP Alpha**, and **HAP Q**. **HAP Q Alpha** is not
supported.
KlakHap only supports the QuickTime File Format as a container, i.e., `.mov`
files.
# How to install
Install the KlakHap package (`jp.keijiro.klak.hap`) from the "Keijiro" scoped
registry in Package Manager. Follow [these instructions] to add the registry to
your project.
[these instructions]:
https://gist.github.com/keijiro/f8c7e8ff29bfe63d86b888901b82644c
# How to specify a video file
There are two ways to specify a video file in the plugin:
- **Streaming Assets Mode**: Put a video file in the [Streaming Assets]
directory and specify its file name.
- **Local File System Mode**: Put a video file somewhere in local drive and
specify its full path.
The former method is recommended when the video file ships with the
application. The latter method is useful when you need to play external
content.
[Streaming Assets]: https://docs.unity3d.com/Manual/StreamingAssets.html
# Hap Player component

**File Path** and **Path Mode** specify the source video file. See the previous
section for details.
**Time**, **Speed** and **Loop** are used to set the initial playback state.
You can also change these values during playback.
**Target Texture** stores decoded frames in a render texture. Note that this
allocates a small amount of GPU time for data transfer.
**Target Renderer** applies the decoded texture to a specific material
property. Although this is the most performant way to render video frames, it
requires a few extra steps to render correctly. Keep the following points in
mind:
- UV coordinate incompatibility: Decoded textures are upside down due to
differences in UV coordinate conventions between Unity and HAP. You can fix
this using a vertically inverted texture scale/offset. You can also use the
`Klak/Hap` shader for this purpose.
- Color space conversion for HAP Q: [YCoCg conversion] must be added to a
shader when using HAP Q. You can also use the `Klak/HAP Q` shader for this
purpose.
[YCoCg conversion]:
https://gist.github.com/dlublin/90f879cfe027ebf5792bdadf2c911bb5
# How to control playback
`HapPlayer` provides only a few properties and methods for controlling
playback. This is an intentional design choice; I avoid ambiguous methods like
`Play`, `Stop`, and `Pause`. Use the basic properties and methods instead.
- To jump to a specific point: Assign a time in seconds to `time`.
- To jump to a specific frame: Calculate the time in seconds using `frameCount`
and `streamDuration`, then assign it to `time`.
- To reverse the playback direction: Assign a negative value to `speed`.
- To pause: Assign `0` to `speed`.
- To resume: Assign `1` to `speed`.
- To stop: Assign `false` to `enabled`.
- To close the video file: Destroy the `HapPlayer` component.
- To open another video file: Call `AddComponent`, then call `Open`.
# Timeline support

The HAP Player component implements the [ITimeControl] interface, which allows
it to control playback time from a Control Track in a [Timeline]. You can
create a control track by dragging and dropping a HAP Player game object into
the Timeline Editor, or manually create a Control Track/Clip and set the
source game object.
[ITimeControl]: https://docs.unity3d.com/ScriptReference/Timeline.ITimeControl.html
[Timeline]: https://docs.unity3d.com/Manual/TimelineSection.html
# Platform differences (internal latency)
On Windows, KlakHap uses the [Custom Texture Update] feature to hide the
synchronization point in the background thread. It guarantees exact-frame
playback with minimal load on the main thread.
On macOS and Linux, the Custom Texture Update feature is unavailable for this
purpose[^1]. Instead, KlakHap delays synchronization to the next frame to avoid
main thread stalls. In other words, it guarantees exact-frame playback but adds
a single-frame latency.
You can turn off this behavior by adding `HAP_NO_DELAY` to the [Scripting
Define Symbols] in the project settings. This stalls the main thread for every
frame decoding. It significantly slows down the application but is useful when
exact frame matching is essential (e.g., [volumetric video playback] with
Alembic animation).
[Custom Texture Update]:
https://github.com/keijiro/TextureUpdateExample
[Scripting Define Symbols]:
https://docs.unity3d.com/Manual/CustomScriptingSymbols.html
[volumetric video playback]:
https://github.com/keijiro/Abcvfx
[^1]: The Custom Texture Update feature is available even on macOS/Linux but
doesn't support compressed texture formats, which are essential for HAP
decoding.
================================================
FILE: Packages/jp.keijiro.klak.hap/README.md.meta
================================================
fileFormatVersion: 2
guid: 2a0b411fba8e4ef48cbb46596fdc2de8
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Packages/jp.keijiro.klak.hap/Resources/Hap1.shader
================================================
Shader "Klak/HAP"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
}
CGINCLUDE
#include "UnityCG.cginc"
struct Attributes
{
float4 position : POSITION;
float2 texcoord : TEXCOORD;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct Varyings
{
float4 position : SV_Position;
float2 texcoord : TEXCOORD;
};
sampler2D _MainTex;
float4 _MainTex_ST;
Varyings Vertex(Attributes input)
{
UNITY_SETUP_INSTANCE_ID(input);
Varyings output;
output.position = UnityObjectToClipPos(input.position);
output.texcoord = TRANSFORM_TEX(input.texcoord, _MainTex);
output.texcoord.y = 1 - output.texcoord.y;
return output;
}
fixed4 Fragment(Varyings input) : SV_Target
{
return tex2D(_MainTex, input.texcoord);
}
ENDCG
SubShader
{
Tags { "RenderType"="Opaque" }
Pass
{
CGPROGRAM
#pragma vertex Vertex
#pragma fragment Fragment
#pragma multi_compile_instancing
ENDCG
}
}
}
================================================
FILE: Packages/jp.keijiro.klak.hap/Resources/Hap1.shader.meta
================================================
fileFormatVersion: 2
guid: e99f77a91d9883a4b890a1fc833b7158
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Packages/jp.keijiro.klak.hap/Resources/Hap5.shader
================================================
Shader "Klak/HAP Alpha"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
}
CGINCLUDE
#include "UnityCG.cginc"
struct Attributes
{
float4 position : POSITION;
float2 texcoord : TEXCOORD;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct Varyings
{
float4 position : SV_Position;
float2 texcoord : TEXCOORD;
};
sampler2D _MainTex;
float4 _MainTex_ST;
Varyings Vertex(Attributes input)
{
UNITY_SETUP_INSTANCE_ID(input);
Varyings output;
output.position = UnityObjectToClipPos(input.position);
output.texcoord = TRANSFORM_TEX(input.texcoord, _MainTex);
output.texcoord.y = 1 - output.texcoord.y;
return output;
}
fixed4 Fragment(Varyings input) : SV_Target
{
return tex2D(_MainTex, input.texcoord);
}
ENDCG
SubShader
{
Tags { "RenderType"="Transparent" "Queue"="Transparent" }
Pass
{
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex Vertex
#pragma fragment Fragment
#pragma multi_compile_instancing
ENDCG
}
}
}
================================================
FILE: Packages/jp.keijiro.klak.hap/Resources/Hap5.shader.meta
================================================
fileFormatVersion: 2
guid: 2fc3460268e46c64695de0f674e4e320
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Packages/jp.keijiro.klak.hap/Resources/HapY.shader
================================================
Shader "Klak/HAP Q"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
}
CGINCLUDE
#include "UnityCG.cginc"
struct Attributes
{
float4 position : POSITION;
float2 texcoord : TEXCOORD;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct Varyings
{
float4 position : SV_Position;
float2 texcoord : TEXCOORD;
};
sampler2D _MainTex;
float4 _MainTex_ST;
half3 CoCgSY2RGB(half4 i)
{
#if !defined(UNITY_COLORSPACE_GAMMA)
i.xyz = LinearToGammaSpace(i.xyz);
#endif
i.xy -= half2(0.50196078431373, 0.50196078431373);
half s = 1 / ((i.z * (255.0 / 8)) + 1);
half3 rgb = half3(i.x - i.y, i.y, -i.x - i.y) * s + i.w;
#if !defined(UNITY_COLORSPACE_GAMMA)
rgb = GammaToLinearSpace(rgb);
#endif
return rgb;
}
Varyings Vertex(Attributes input)
{
UNITY_SETUP_INSTANCE_ID(input);
Varyings output;
output.position = UnityObjectToClipPos(input.position);
output.texcoord = TRANSFORM_TEX(input.texcoord, _MainTex);
output.texcoord.y = 1 - output.texcoord.y;
return output;
}
fixed4 Fragment(Varyings input) : SV_Target
{
return fixed4(CoCgSY2RGB(tex2D(_MainTex, input.texcoord)), 1);
}
ENDCG
SubShader
{
Tags { "RenderType"="Opaque" }
Pass
{
CGPROGRAM
#pragma multi_compile _ UNITY_COLORSPACE_GAMMA
#pragma vertex Vertex
#pragma fragment Fragment
#pragma multi_compile_instancing
ENDCG
}
}
}
================================================
FILE: Packages/jp.keijiro.klak.hap/Resources/HapY.shader.meta
================================================
fileFormatVersion: 2
guid: c6a332340608cdd4f907eb4bc0c17f4a
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Packages/jp.keijiro.klak.hap/Resources.meta
================================================
fileFormatVersion: 2
guid: 1b69560a2379aa94b9fb309127d58020
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Packages/jp.keijiro.klak.hap/Runtime/Common.cs
================================================
namespace Klak.Hap
{
public enum CodecType { Unsupported, Hap, HapQ, HapAlpha }
}
================================================
FILE: Packages/jp.keijiro.klak.hap/Runtime/Common.cs.meta
================================================
fileFormatVersion: 2
guid: ab49f28befbab1341821f02280a3199f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Packages/jp.keijiro.klak.hap/Runtime/HapPlayer.cs
================================================
using UnityEngine;
using UnityEngine.Playables;
#if KLAKHAP_HAS_TIMELINE
using UnityEngine.Timeline;
#endif
namespace Klak.Hap
{
[ExecuteInEditMode, AddComponentMenu("Klak/HAP/HAP Player")]
#if KLAKHAP_HAS_TIMELINE
public sealed class HapPlayer : MonoBehaviour , ITimeControl, IPropertyPreview
#else
public sealed class HapPlayer : MonoBehaviour
#endif
{
#region Editable attributes
public enum PathMode { StreamingAssets, LocalFileSystem }
[SerializeField] PathMode _pathMode = PathMode.StreamingAssets;
[SerializeField] string _filePath = "";
[SerializeField] float _time = 0;
[SerializeField, Range(-10, 10)] float _speed = 1;
[SerializeField] bool _loop = true;
[SerializeField] RenderTexture _targetTexture = null;
[SerializeField] Renderer _targetRenderer = null;
[SerializeField] string _targetMaterialProperty = "_MainTex";
#endregion
#region Public properties
public float time {
get { return _time; }
set { _time = value; }
}
public float speed {
get { return _speed; }
set { _speed = value; }
}
public bool loop {
get { return _loop; }
set { _loop = value; }
}
public RenderTexture targetTexture {
get { return _targetTexture; }
set { _targetTexture = value; }
}
public Renderer targetRenderer {
get { return _targetRenderer; }
set { _targetRenderer = value; }
}
public string targetMaterialProperty {
get { return _targetMaterialProperty; }
set { _targetMaterialProperty = value; }
}
#endregion
#region Read-only properties
public bool isValid { get { return _demuxer != null; } }
public int frameWidth { get { return _demuxer?.Width ?? 0; } }
public int frameHeight { get { return _demuxer?.Height ?? 0; } }
public int frameCount { get { return _demuxer?.FrameCount ?? 0; } }
public double streamDuration { get { return _demuxer?.Duration ?? 0; } }
public CodecType codecType { get {
return Utility.DetermineCodecType(_demuxer?.VideoType ?? 0);
} }
public string resolvedFilePath { get {
if (_pathMode == PathMode.StreamingAssets)
return System.IO.Path.Combine(Application.streamingAssetsPath, _filePath);
else
return _filePath;
} }
public Texture2D texture { get { return _texture; } }
#endregion
#region Public methods
public void Open(string filePath, PathMode pathMode = PathMode.StreamingAssets)
{
if (_demuxer != null)
{
Debug.LogError("Stream has already been opened.");
return;
}
_filePath = filePath;
_pathMode = pathMode;
OpenInternal();
}
public void UpdateNow()
=> LateUpdate();
#endregion
#region Private members
Demuxer _demuxer;
StreamReader _stream;
Decoder _decoder;
Texture2D _texture;
TextureUpdater _updater;
float _storedTime;
float _storedSpeed;
void OpenInternal()
{
// Demuxer instantiation
_demuxer = new Demuxer(resolvedFilePath);
if (!_demuxer.IsValid)
{
if (Application.isPlaying)
{
// In play mode, show an error message, then disable itself
// to prevent spamming the console.
Debug.LogError("Failed to open stream (" + resolvedFilePath + ").");
enabled = false;
}
_demuxer.Dispose();
_demuxer = null;
return;
}
// Stream reader instantiation
_stream = new StreamReader(_demuxer, _time, _speed / 60);
(_storedTime, _storedSpeed) = (_time, _speed);
// Decoder instantiation
_decoder = new Decoder(
_stream, _demuxer.Width, _demuxer.Height, _demuxer.VideoType
);
// Texture initialization
_texture = new Texture2D(
_demuxer.Width, _demuxer.Height,
Utility.DetermineTextureFormat(_demuxer.VideoType), false
);
_texture.wrapMode = TextureWrapMode.Clamp;
_texture.hideFlags = HideFlags.DontSave;
_updater = new TextureUpdater(_texture, _decoder);
}
#endregion
#region External object updaters
Material _blitMaterial;
MaterialPropertyBlock _propertyBlock;
void UpdateTargetTexture()
{
if (_targetTexture == null) return;
// Material lazy initialization
if (_blitMaterial == null)
{
_blitMaterial = new Material(Utility.DetermineBlitShader(_demuxer.VideoType));
_blitMaterial.hideFlags = HideFlags.DontSave;
}
// Blit
Graphics.Blit(_texture, _targetTexture, _blitMaterial, 0);
}
void UpdateTargetRenderer()
{
if (_targetRenderer == null) return;
// Material property block lazy initialization
if (_propertyBlock == null)
_propertyBlock = new MaterialPropertyBlock();
// Read-modify-write
_targetRenderer.GetPropertyBlock(_propertyBlock);
_propertyBlock.SetTexture(_targetMaterialProperty, _texture);
_targetRenderer.SetPropertyBlock(_propertyBlock);
}
#endregion
#region ITimeControl implementation
bool _externalTime;
public void OnControlTimeStart()
{
_externalTime = true;
// In the external time mode, we can't know the actual playback
// speed but sure that it's positive (Control Track doesn't support
// reverse playback), so we assume that the speed is 1.0.
// Cons: Resync could happen every frame for high speed play back.
_speed = 1;
}
public void OnControlTimeStop()
{
_externalTime = false;
}
public void SetTime(double time)
{
_time = (float)time;
_speed = 1;
}
#endregion
#region IPropertyPreview implementation
#if KLAKHAP_HAS_TIMELINE
public void GatherProperties(PlayableDirector director, IPropertyCollector driver)
{
driver.AddFromName(gameObject, "_time");
}
#endif
#endregion
#region MonoBehaviour implementation
void OnDestroy()
{
if (_updater != null)
{
_updater.Dispose();
_updater = null;
}
if (_decoder != null)
{
_decoder.Dispose();
_decoder = null;
}
if (_stream != null)
{
_stream.Dispose();
_stream = null;
}
if (_demuxer != null)
{
_demuxer.Dispose();
_demuxer = null;
}
Utility.Destroy(_texture);
Utility.Destroy(_blitMaterial);
}
int _lastUpdateFrameCount = -1;
void LateUpdate()
{
// Double update check
if (Time.frameCount == _lastUpdateFrameCount) return;
_lastUpdateFrameCount = Time.frameCount;
// Lazy initialization of demuxer
if (_demuxer == null && !string.IsNullOrEmpty(_filePath))
OpenInternal();
// Do nothing if the demuxer hasn't been instantiated.
if (_demuxer == null) return;
var duration = (float)_demuxer.Duration;
// Check if _time is still in the same frame of _storedTime.
// Resync is needed when it went out of the frame.
var dt = duration / _demuxer.FrameCount;
var resync = _time < _storedTime || _time > _storedTime + dt;
// Check if the speed was externally modified.
if (_speed != _storedSpeed)
{
resync = true; // Resync to adapt to the new speed.
_storedSpeed = _speed;
}
// Time clamping
var t = _loop ? _time : Mathf.Clamp(_time, 0, duration - 1e-4f);
// Determine if background decoding is available.
// Resync shouldn't happen. Not preferable in edit mode.
var bgdec = !resync && Application.isPlaying;
// Restart the stream reader on resync.
if (resync) _stream.Restart(t, _speed / 60);
if (TextureUpdater.AsyncSupport)
{
// Asynchronous texture update supported:
// Decode a frame and request a texture update.
if (bgdec) _decoder.UpdateAsync(t); else _decoder.UpdateSync(t);
_updater.RequestAsyncUpdate();
}
#if !HAP_NO_DELAY
else if (bgdec)
{
// Synchronous texture update with background decoding:
// Update first, then start background decoding. This
// introduces a single frame delay but makes it possible to
// offload decoding load to a background thread.
_updater.UpdateNow();
_decoder.UpdateAsync(t);
}
#endif
else
{
// Synchronous decoding and texture update.
_decoder.UpdateSync(t);
_updater.UpdateNow();
}
// Update the stored time.
if (Application.isPlaying && !_externalTime)
_time += Time.deltaTime * _speed;
_storedTime = _time;
// External object updates
UpdateTargetRenderer();
UpdateTargetTexture();
}
#endregion
}
}
================================================
FILE: Packages/jp.keijiro.klak.hap/Runtime/HapPlayer.cs.meta
================================================
fileFormatVersion: 2
guid: f3775962864755b43a790abf4d029c32
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Packages/jp.keijiro.klak.hap/Runtime/Internal/Decoder.cs
================================================
using System;
using System.Runtime.InteropServices;
using System.Threading;
namespace Klak.Hap
{
internal sealed class Decoder : IDisposable
{
#region Initialization/finalization
public Decoder(StreamReader stream, int width, int height, int videoType)
{
_stream = stream;
// Plugin initialization
_plugin = KlakHap_CreateDecoder(width, height, videoType);
_id = ++_instantiationCount;
KlakHap_AssignDecoder(_id, _plugin);
// By default, start from the first frame.
_time = 0;
// Decoder thread startup
_resume.req = new AutoResetEvent(true);
_resume.ack = new AutoResetEvent(false);
_thread = new Thread(DecoderThread);
_thread.Start();
}
public void Dispose()
{
if (_thread != null)
{
_terminate = true;
_resume.req.Set();
_thread.Join();
_thread = null;
}
if (_plugin != IntPtr.Zero)
{
KlakHap_AssignDecoder(_id, IntPtr.Zero);
KlakHap_DestroyDecoder(_plugin);
_plugin = IntPtr.Zero;
}
}
#endregion
#region Public members
public uint CallbackID { get { return _id; } }
public int BufferSize { get {
return KlakHap_GetDecoderBufferSize(_plugin);
} }
public void UpdateSync(float time)
{
_time = time;
var buffer = _stream.Advance(_time);
if (buffer != null)
KlakHap_DecodeFrame(_plugin, buffer.PluginPointer);
}
public void UpdateAsync(float time)
{
_time = time;
_resume.req.Set();
_resume.ack.WaitOne();
}
public IntPtr LockBuffer()
{
return KlakHap_LockDecoderBuffer(_plugin);
}
public void UnlockBuffer()
{
KlakHap_UnlockDecoderBuffer(_plugin);
}
#endregion
#region Private members
static uint _instantiationCount;
IntPtr _plugin;
uint _id;
Thread _thread;
(AutoResetEvent req, AutoResetEvent ack) _resume;
bool _terminate;
StreamReader _stream;
float _time;
#endregion
#region Thread function
void DecoderThread()
{
while (true)
{
_resume.req.WaitOne();
_resume.ack.Set();
if (_terminate) break;
var buffer = _stream.Advance(_time);
if (buffer == null) continue;
KlakHap_DecodeFrame(_plugin, buffer.PluginPointer);
}
}
#endregion
#region Native plugin entry points
[DllImport("KlakHap")]
internal static extern IntPtr KlakHap_CreateDecoder(int width, int height, int typeID);
[DllImport("KlakHap")]
internal static extern void KlakHap_DestroyDecoder(IntPtr decoder);
[DllImport("KlakHap")]
internal static extern void KlakHap_AssignDecoder(uint id, IntPtr decoder);
[DllImport("KlakHap")]
internal static extern void KlakHap_DecodeFrame(IntPtr decoder, IntPtr input);
[DllImport("KlakHap")]
internal static extern IntPtr KlakHap_LockDecoderBuffer(IntPtr decoder);
[DllImport("KlakHap")]
internal static extern void KlakHap_UnlockDecoderBuffer(IntPtr decoder);
[DllImport("KlakHap")]
internal static extern int KlakHap_GetDecoderBufferSize(IntPtr decoder);
#endregion
}
}
================================================
FILE: Packages/jp.keijiro.klak.hap/Runtime/Internal/Decoder.cs.meta
================================================
fileFormatVersion: 2
guid: 6a95a95ecf0e26740a6507fe3474b8f8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Packages/jp.keijiro.klak.hap/Runtime/Internal/Demuxer.cs
================================================
using System;
using System.Runtime.InteropServices;
namespace Klak.Hap
{
internal sealed class Demuxer : IDisposable
{
#region Public properties
public bool IsValid { get { return _plugin != IntPtr.Zero; } }
public int Width { get { return _width; } }
public int Height { get { return _height; } }
public int VideoType { get { return _videoType; } }
public double Duration { get { return _duration; } }
public int FrameCount { get { return _frameCount; } }
#endregion
#region Initialization/finalization
public Demuxer(string filePath)
{
_plugin = KlakHap_OpenDemuxer(filePath);
if (KlakHap_DemuxerIsValid(_plugin) == 0)
{
// Instantiation failed; Close and stop.
KlakHap_CloseDemuxer(_plugin);
_plugin = IntPtr.Zero;
return;
}
// Video properties
_width = KlakHap_GetVideoWidth(_plugin);
_height = KlakHap_GetVideoHeight(_plugin);
_videoType = KlakHap_AnalyzeVideoType(_plugin);
_duration = KlakHap_GetDuration(_plugin);
_frameCount = KlakHap_CountFrames(_plugin);
}
public void Dispose()
{
if (_plugin != IntPtr.Zero)
{
KlakHap_CloseDemuxer(_plugin);
_plugin = IntPtr.Zero;
}
}
#endregion
#region Public methods
public void ReadFrame(ReadBuffer buffer, int index, float time)
{
KlakHap_ReadFrame(_plugin, index, buffer.PluginPointer);
buffer.Index = index;
buffer.Time = time;
}
#endregion
#region Private members
IntPtr _plugin;
int _width, _height, _videoType;
double _duration;
int _frameCount;
#endregion
#region Native plugin entry points
[DllImport("KlakHap", CharSet = CharSet.Ansi)]
internal static extern IntPtr KlakHap_OpenDemuxer([MarshalAs(UnmanagedType.LPUTF8Str)] string filepath);
[DllImport("KlakHap")]
internal static extern void KlakHap_CloseDemuxer(IntPtr demuxer);
[DllImport("KlakHap")]
internal static extern int KlakHap_DemuxerIsValid(IntPtr demuxer);
[DllImport("KlakHap")]
internal static extern int KlakHap_CountFrames(IntPtr demuxer);
[DllImport("KlakHap")]
internal static extern double KlakHap_GetDuration(IntPtr demuxer);
[DllImport("KlakHap")]
internal static extern int KlakHap_GetVideoWidth(IntPtr demuxer);
[DllImport("KlakHap")]
internal static extern int KlakHap_GetVideoHeight(IntPtr demuxer);
[DllImport("KlakHap")]
internal static extern int KlakHap_AnalyzeVideoType(IntPtr demuxer);
[DllImport("KlakHap")]
internal static extern void KlakHap_ReadFrame(IntPtr demuxer, int frameNumber, IntPtr buffer);
#endregion
}
}
================================================
FILE: Packages/jp.keijiro.klak.hap/Runtime/Internal/Demuxer.cs.meta
================================================
fileFormatVersion: 2
guid: 080b998a80cb20044adf8fa4a011a182
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Packages/jp.keijiro.klak.hap/Runtime/Internal/ReadBuffer.cs
================================================
using System;
using System.Runtime.InteropServices;
namespace Klak.Hap
{
internal sealed class ReadBuffer : IDisposable
{
#region Initialization/finalization
public IntPtr PluginPointer { get { return _plugin; } }
public int Index { get; set; }
public float Time { get; set; }
#endregion
#region Initialization/finalization
public ReadBuffer()
{
_plugin = KlakHap_CreateReadBuffer();
Index = Int32.MaxValue;
Time = Single.MaxValue;
}
public void Dispose()
{
KlakHap_DestroyReadBuffer(_plugin);
}
#endregion
#region Private members
IntPtr _plugin;
#endregion
#region Native plugin entry points
[DllImport("KlakHap")]
internal static extern IntPtr KlakHap_CreateReadBuffer();
[DllImport("KlakHap")]
internal static extern void KlakHap_DestroyReadBuffer(IntPtr buffer);
#endregion
}
}
================================================
FILE: Packages/jp.keijiro.klak.hap/Runtime/Internal/ReadBuffer.cs.meta
================================================
fileFormatVersion: 2
guid: 4e6e9144df4395d4fbff558262502c67
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Packages/jp.keijiro.klak.hap/Runtime/Internal/StreamReader.cs
================================================
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading;
namespace Klak.Hap
{
internal sealed class StreamReader : IDisposable
{
#region Public methods
public StreamReader(Demuxer demuxer, float time, float delta)
{
_demuxer = demuxer;
_leadQueue = new Queue();
_freeBuffers = new List();
// Initial buffer entry allocation
_freeBuffers.Add(new ReadBuffer());
_freeBuffers.Add(new ReadBuffer());
_freeBuffers.Add(new ReadBuffer());
_freeBuffers.Add(new ReadBuffer());
// Initial playback settings
_restart = (time, SafeDelta(delta));
// Reader thread startup
_updateEvent = new AutoResetEvent(true);
_readEvent = new AutoResetEvent(false);
_thread = new Thread(ReaderThread);
_thread.Start();
}
public void Dispose()
{
if (_thread != null)
{
_terminate = true;
_updateEvent.Set();
_thread.Join();
_thread = null;
}
if (_updateEvent != null)
{
_updateEvent.Dispose();
_updateEvent = null;
}
if (_readEvent != null)
{
_readEvent.Dispose();
_readEvent = null;
}
if (_current != null)
{
_current.Dispose();
_current = null;
}
if (_leadQueue != null)
{
foreach (var rb in _leadQueue) rb.Dispose();
_leadQueue.Clear();
_leadQueue = null;
}
if (_freeBuffers != null)
{
foreach (var rb in _freeBuffers) rb.Dispose();
_freeBuffers.Clear();
_freeBuffers = null;
}
}
public void Restart(float time, float delta)
{
// Restart request
lock (_restartLock) _restart = (time, SafeDelta(delta));
// Wait for reset/read on the reader thread.
_readEvent.Reset();
while (_restart != null)
{
_updateEvent.Set();
_readEvent.WaitOne();
}
}
public ReadBuffer Advance(float time)
{
// Add an epsilon-ish value to avoid rounding error.
time += 1e-6f;
var changed = false;
// There is no slow path in this function, so we prefer holding
// the queue lock for the entire function block rather than
// acquiring/releasing it for each operation.
lock (_queueLock)
{
// Scan the lead queue.
while (_leadQueue.Count > 0)
{
var peek = _leadQueue.Peek();
if (_current != null)
{
if (_current.Time <= peek.Time)
{
// Forward playback case:
// Break if it hasn't reached the next frame.
if (time < peek.Time) break;
}
else
{
// Reverse playback case:
// Break if it's still on the current frame.
if (_current.Time < time) break;
}
// Free the current frame before replacing it.
_freeBuffers.Add(_current);
}
_current = _leadQueue.Dequeue();
changed = true;
}
}
// Poke the reader thread.
_updateEvent.Set();
// Only returns a buffer object when the frame was changed.
return changed ? _current : null;
}
#endregion
#region Private members
// Assigned demuxer
Demuxer _demuxer;
// Thread and synchronization objects
Thread _thread;
AutoResetEvent _updateEvent;
AutoResetEvent _readEvent;
bool _terminate;
// Read buffer objects
ReadBuffer _current;
Queue _leadQueue;
List _freeBuffers;
readonly object _queueLock = new object();
// Restart request
(float, float)? _restart;
readonly object _restartLock = new object();
// Used to avoid too small delta time values.
float SafeDelta(float delta)
{
var min = (float)(_demuxer.Duration / _demuxer.FrameCount);
return Math.Max(Math.Abs(delta), min) * (delta < 0 ? -1 : 1);
}
#endregion
#region Thread function
void ReaderThread()
{
// Initial time settings from the restart request tuple
var (time, delta) = _restart.Value;
_restart = null;
// Stream attributes
var totalTime = _demuxer.Duration;
var totalFrames = _demuxer.FrameCount;
while (true)
{
// Synchronization with the parent thread
_updateEvent.WaitOne();
if (_terminate) break;
// Check if there is a restart request.
lock (_restartLock) if (_restart != null)
{
// Flush out the current contents of the lead queue.
lock (_queueLock) while (_leadQueue.Count > 0)
_freeBuffers.Add(_leadQueue.Dequeue());
// Apply the restart request.
(time, delta) = _restart.Value;
_restart = null;
}
// Time -> Frame count
// Rounding strategy: We don't prefer Math.Round because it can
// show a frame before the playhead reaches it (especially when
// using slow-mo). On the other hand, Math.Floor causes frame
// skipping due to rounding errors. To avoid these problems,
// we use the "adding a very-very small fractional frame"
// approach. 1/1000 might be safe and enough for all the cases.
var frameCount = (int)(time * totalFrames / totalTime + 1e-3f);
// Frame count -> Frame snapped time
var snappedTime = (float)(frameCount * totalTime / totalFrames);
// Frame count -> Wrapped frame number
var frameNumber = frameCount % totalFrames;
if (frameNumber < 0) frameNumber += totalFrames;
lock (_queueLock)
{
// Do nothing if there is no free buffer; It indicates that
// the lead queue is fully filled.
if (_freeBuffers.Count == 0) continue;
ReadBuffer buffer = null;
// Look for a free buffer that has the same frame number.
foreach (var temp in _freeBuffers)
{
if (temp.Index == frameNumber)
{
buffer = temp;
break;
}
}
if (buffer != null)
{
// Reuse the found buffer; Although we can use it
// without reading frame data, the time field should
// be updated to handle wrapping-around hits.
_freeBuffers.Remove(buffer);
buffer.Time = snappedTime;
}
else
{
// Allocate a buffer from the free buffer list.
buffer = _freeBuffers[_freeBuffers.Count - 1];
_freeBuffers.RemoveAt(_freeBuffers.Count - 1);
// Frame data read
_demuxer.ReadFrame(buffer, frameNumber, snappedTime);
}
// Push the buffer to the lead queue.
_leadQueue.Enqueue(buffer);
}
_readEvent.Set();
time += delta;
}
}
#endregion
}
}
================================================
FILE: Packages/jp.keijiro.klak.hap/Runtime/Internal/StreamReader.cs.meta
================================================
fileFormatVersion: 2
guid: 435d6c4db4ba1ea40b2289d7ef613a3c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Packages/jp.keijiro.klak.hap/Runtime/Internal/TextureUpdater.cs
================================================
using System;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.Rendering;
namespace Klak.Hap
{
internal sealed class TextureUpdater : IDisposable
{
#region Public properties
public static bool AsyncSupport { get {
return SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D11;
} }
#endregion
#region Public methods
public TextureUpdater(Texture2D texture, Decoder decoder)
{
_texture = texture;
_decoder = decoder;
if (AsyncSupport)
{
_command = new CommandBuffer();
_command.name = "Klak HAP";
_command.IssuePluginCustomTextureUpdateV2(
KlakHap_GetTextureUpdateCallback(),
texture, decoder.CallbackID
);
}
}
public void Dispose()
{
if (_command != null)
{
_command.Dispose();
_command = null;
}
}
public void UpdateNow()
{
_texture.LoadRawTextureData(
_decoder.LockBuffer(),
_decoder.BufferSize
);
_texture.Apply();
_decoder.UnlockBuffer();
}
public void RequestAsyncUpdate()
{
if (_command != null) Graphics.ExecuteCommandBuffer(_command);
}
#endregion
#region Private fields
Texture2D _texture;
Decoder _decoder;
CommandBuffer _command;
#endregion
#region Native plugin entry points
[DllImport("KlakHap")]
internal static extern IntPtr KlakHap_GetTextureUpdateCallback();
#endregion
}
}
================================================
FILE: Packages/jp.keijiro.klak.hap/Runtime/Internal/TextureUpdater.cs.meta
================================================
fileFormatVersion: 2
guid: f9e27f5a05bda2e4eab2f548840a9fb1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Packages/jp.keijiro.klak.hap/Runtime/Internal/Utility.cs
================================================
using UnityEngine;
namespace Klak.Hap
{
internal static class Utility
{
public static void Destroy(Object o)
{
if (o == null) return;
if (Application.isPlaying)
Object.Destroy(o);
else
Object.DestroyImmediate(o);
}
public static CodecType DetermineCodecType(int videoType)
{
switch (videoType & 0xf)
{
case 0xb: return CodecType.Hap;
case 0xe: return CodecType.HapAlpha;
case 0xf: return CodecType.HapQ;
}
return CodecType.Unsupported;
}
public static TextureFormat DetermineTextureFormat(int videoType)
{
switch (videoType & 0xf)
{
case 0xb: return TextureFormat.DXT1;
case 0xe: return TextureFormat.DXT5;
case 0xf: return TextureFormat.DXT5;
case 0xc: return TextureFormat.BC7;
case 0x1: return TextureFormat.BC4;
}
return TextureFormat.DXT1;
}
public static Shader DetermineBlitShader(int videoType)
{
if ((videoType & 0xf) == 0xf)
return Shader.Find("Klak/HAP Q");
else
return Shader.Find("Klak/HAP");
}
}
}
================================================
FILE: Packages/jp.keijiro.klak.hap/Runtime/Internal/Utility.cs.meta
================================================
fileFormatVersion: 2
guid: 970c4e0f94959ac4b9b108dc1a34306e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Packages/jp.keijiro.klak.hap/Runtime/Internal.meta
================================================
fileFormatVersion: 2
guid: d37984629e38a1c439a98815991ec09f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Packages/jp.keijiro.klak.hap/Runtime/Klak.Hap.asmdef
================================================
{
"name": "Klak.Hap",
"references": [
"GUID:f06555f75b070af458a003d92f9efb00"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [
{
"name": "com.unity.timeline",
"expression": "1.0.0",
"define": "KLAKHAP_HAS_TIMELINE"
}
],
"noEngineReferences": false
}
================================================
FILE: Packages/jp.keijiro.klak.hap/Runtime/Klak.Hap.asmdef.meta
================================================
fileFormatVersion: 2
guid: 63ee2b3a69c095740bf1e4ff4d11f294
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Packages/jp.keijiro.klak.hap/Runtime.meta
================================================
fileFormatVersion: 2
guid: 2d10b798fb99ee94981f7dbf19d8d6a9
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Packages/jp.keijiro.klak.hap/package.json
================================================
{
"name": "jp.keijiro.klak.hap",
"version": "1.0.0",
"displayName": "KlakHAP",
"description": "HAP video player",
"unity": "2022.3",
"author": "Keijiro Takahashi",
"dependencies": {},
"changelogUrl": "https://github.com/keijiro/KlakHap/blob/master/CHANGELOG.md",
"documentationUrl": "https://github.com/keijiro/KlakHap",
"licensesUrl": "https://github.com/keijiro/KlakHap/blob/master/LICENSE",
"license": "SEE LICENSE IN LICENSE",
"_upm": {
"changelog": "Added
- Added a Unity 6.3 demo scene updated for URP and UI Toolkit.
- Added runtime tests and a test data downloader for Hap, HapQ, and HapAlpha assets.
Changed
- Updated HAP and Snappy source code and refreshed native plugin binaries.
- Migrated Windows builds to MinGW and refactored the native build pipelines and Makefiles.
Fixed
- Fixed a Windows crash when paths contain Japanese characters. (PR #49)
- Fixed obsolete ShaderUtil API usage in the editor code."
}
}
================================================
FILE: Packages/jp.keijiro.klak.hap/package.json.meta
================================================
fileFormatVersion: 2
guid: 251c3b05c5e2c694da8fe8fe9b07eb5c
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: Packages/manifest.json
================================================
{
"dependencies": {
"com.unity.inputsystem": "1.18.0",
"com.unity.render-pipelines.universal": "17.3.0",
"com.unity.timeline": "1.8.10",
"jp.keijiro.klak.motion": "1.1.1",
"jp.keijiro.klutter-tools": "2.6.0",
"com.unity.modules.imageconversion": "1.0.0",
"com.unity.modules.uielements": "1.0.0"
},
"scopedRegistries": [
{
"name": "Keijiro",
"url": "https://registry.npmjs.com",
"scopes": [
"jp.keijiro"
]
}
]
}
================================================
FILE: Packages/packages-lock.json
================================================
{
"dependencies": {
"com.unity.burst": {
"version": "1.8.27",
"depth": 2,
"source": "registry",
"dependencies": {
"com.unity.mathematics": "1.2.1",
"com.unity.modules.jsonserialize": "1.0.0"
},
"url": "https://packages.unity.com"
},
"com.unity.collections": {
"version": "2.6.2",
"depth": 2,
"source": "registry",
"dependencies": {
"com.unity.burst": "1.8.23",
"com.unity.mathematics": "1.3.2",
"com.unity.test-framework": "1.4.6",
"com.unity.nuget.mono-cecil": "1.11.5",
"com.unity.test-framework.performance": "3.0.3"
},
"url": "https://packages.unity.com"
},
"com.unity.ext.nunit": {
"version": "2.0.5",
"depth": 4,
"source": "builtin",
"dependencies": {}
},
"com.unity.inputsystem": {
"version": "1.18.0",
"depth": 0,
"source": "registry",
"dependencies": {
"com.unity.modules.uielements": "1.0.0"
},
"url": "https://packages.unity.com"
},
"com.unity.mathematics": {
"version": "1.3.3",
"depth": 1,
"source": "registry",
"dependencies": {},
"url": "https://packages.unity.com"
},
"com.unity.nuget.mono-cecil": {
"version": "1.11.6",
"depth": 3,
"source": "registry",
"dependencies": {},
"url": "https://packages.unity.com"
},
"com.unity.render-pipelines.core": {
"version": "17.3.0",
"depth": 1,
"source": "builtin",
"dependencies": {
"com.unity.burst": "1.8.14",
"com.unity.mathematics": "1.3.2",
"com.unity.ugui": "2.0.0",
"com.unity.collections": "2.4.3",
"com.unity.modules.physics": "1.0.0",
"com.unity.modules.terrain": "1.0.0",
"com.unity.modules.jsonserialize": "1.0.0"
}
},
"com.unity.render-pipelines.universal": {
"version": "17.3.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.render-pipelines.core": "17.3.0",
"com.unity.shadergraph": "17.3.0",
"com.unity.render-pipelines.universal-config": "17.0.3"
}
},
"com.unity.render-pipelines.universal-config": {
"version": "17.0.3",
"depth": 1,
"source": "builtin",
"dependencies": {
"com.unity.render-pipelines.core": "17.0.3"
}
},
"com.unity.searcher": {
"version": "4.9.4",
"depth": 2,
"source": "registry",
"dependencies": {},
"url": "https://packages.unity.com"
},
"com.unity.shadergraph": {
"version": "17.3.0",
"depth": 1,
"source": "builtin",
"dependencies": {
"com.unity.render-pipelines.core": "17.3.0",
"com.unity.searcher": "4.9.3"
}
},
"com.unity.test-framework": {
"version": "1.6.0",
"depth": 3,
"source": "builtin",
"dependencies": {
"com.unity.ext.nunit": "2.0.3",
"com.unity.modules.imgui": "1.0.0",
"com.unity.modules.jsonserialize": "1.0.0"
}
},
"com.unity.test-framework.performance": {
"version": "3.2.0",
"depth": 3,
"source": "registry",
"dependencies": {
"com.unity.test-framework": "1.1.33",
"com.unity.modules.jsonserialize": "1.0.0"
},
"url": "https://packages.unity.com"
},
"com.unity.timeline": {
"version": "1.8.10",
"depth": 0,
"source": "registry",
"dependencies": {
"com.unity.modules.audio": "1.0.0",
"com.unity.modules.director": "1.0.0",
"com.unity.modules.animation": "1.0.0",
"com.unity.modules.particlesystem": "1.0.0"
},
"url": "https://packages.unity.com"
},
"com.unity.ugui": {
"version": "2.0.0",
"depth": 2,
"source": "builtin",
"dependencies": {
"com.unity.modules.ui": "1.0.0",
"com.unity.modules.imgui": "1.0.0"
}
},
"jp.keijiro.klak.hap": {
"version": "file:jp.keijiro.klak.hap",
"depth": 0,
"source": "embedded",
"dependencies": {}
},
"jp.keijiro.klak.motion": {
"version": "1.1.1",
"depth": 0,
"source": "registry",
"dependencies": {
"com.unity.mathematics": "1.2.1"
},
"url": "https://registry.npmjs.com"
},
"jp.keijiro.klutter-tools": {
"version": "2.6.0",
"depth": 0,
"source": "registry",
"dependencies": {},
"url": "https://registry.npmjs.com"
},
"com.unity.modules.animation": {
"version": "1.0.0",
"depth": 1,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.audio": {
"version": "1.0.0",
"depth": 1,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.director": {
"version": "1.0.0",
"depth": 1,
"source": "builtin",
"dependencies": {
"com.unity.modules.audio": "1.0.0",
"com.unity.modules.animation": "1.0.0"
}
},
"com.unity.modules.hierarchycore": {
"version": "1.0.0",
"depth": 1,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.imageconversion": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.imgui": {
"version": "1.0.0",
"depth": 1,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.jsonserialize": {
"version": "1.0.0",
"depth": 1,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.particlesystem": {
"version": "1.0.0",
"depth": 1,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.physics": {
"version": "1.0.0",
"depth": 1,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.terrain": {
"version": "1.0.0",
"depth": 2,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.ui": {
"version": "1.0.0",
"depth": 1,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.uielements": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.modules.ui": "1.0.0",
"com.unity.modules.imgui": "1.0.0",
"com.unity.modules.jsonserialize": "1.0.0",
"com.unity.modules.hierarchycore": "1.0.0",
"com.unity.modules.physics": "1.0.0"
}
}
}
}
================================================
FILE: Plugin/Common.mk
================================================
#
# File listings
#
PRODUCT = KlakHap
SRCS_C = Hap/hap.c MP4/mp4demux.c
vpath %.c Hap MP4
SRCS_CC = Snappy/snappy-c.cc \
Snappy/snappy-sinksource.cc \
Snappy/snappy-stubs-internal.cc \
Snappy/snappy.cc
vpath %.cc Snappy
SRCS_CPP = Source/KlakHap.cpp
vpath %.cpp Source
SRCS = $(SRCS_C) $(SRCS_CC) $(SRCS_CPP)
OBJ_DIR = build-$(PLATFORM)-$(ARCH)
#
# Intermediate/output files
#
OBJS_C = $(addprefix $(OBJ_DIR)/, $(notdir $(patsubst %.c, %.o, $(SRCS_C) )))
OBJS_CC = $(addprefix $(OBJ_DIR)/, $(notdir $(patsubst %.cc, %.o, $(SRCS_CC) )))
OBJS_CPP = $(addprefix $(OBJ_DIR)/, $(notdir $(patsubst %.cpp,%.o, $(SRCS_CPP))))
OBJS = $(OBJS_C) $(OBJS_CC) $(OBJS_CPP)
ifeq ($(TARGET_TYPE), dll)
TARGET = $(OBJ_DIR)/$(PRODUCT).$(TARGET_TYPE)
else
TARGET = $(OBJ_DIR)/lib$(PRODUCT).$(TARGET_TYPE)
endif
#
# Toolchain
#
ifndef AR
AR = ar
endif
ifeq ($(origin CC),default)
CC = clang
endif
ifeq ($(origin CXX),default)
CXX = clang++
endif
ifndef STRIP
STRIP = strip
endif
#
# Compiler/linker options
#
CPPFLAGS += -ISnappy -IHap -IMP4 -IUnity
CFLAGS += -O2 -Wall -Wextra -Wno-sign-compare -Wno-implicit-fallthrough
CXXFLAGS += -O2 -Wall -Wextra -Wno-unused-parameter -Wno-switch -Wno-unknown-pragmas -std=c++17
#
# Building rules
#
all: $(TARGET)
clean:
rm -f $(TARGET) $(OBJS)
copy: $(TARGET)
cp $(TARGET) ../Packages/jp.keijiro.klak.hap/Plugin/$(PLATFORM)
$(OBJ_DIR)/$(PRODUCT).dll: $(OBJS)
$(CXX) $(LDFLAGS) -o $@ $^ $(LIBS)
$(STRIP) $@
$(OBJ_DIR)/lib$(PRODUCT).dylib: $(OBJS)
$(CXX) $(LDFLAGS) -o $@ $^ $(LIBS)
$(OBJ_DIR)/lib$(PRODUCT).so: $(OBJS)
$(CXX) $(LDFLAGS) -o $@ $^ $(LIBS)
$(OBJ_DIR)/%.o: %.c | $(OBJ_DIR)
$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
$(OBJ_DIR)/%.o: %.cc | $(OBJ_DIR)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<
$(OBJ_DIR)/%.o: %.cpp | $(OBJ_DIR)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<
$(OBJ_DIR):
mkdir -p $(OBJ_DIR)
================================================
FILE: Plugin/Hap/hap.c
================================================
/*
hap.c
Copyright (c) 2011-2013, Tom Butterworth and Vidvox LLC. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "hap.h"
#include
#include
#include // For memcpy for uncompressed frames
#include "snappy-c.h"
#define kHapUInt24Max 0x00FFFFFF
/*
Hap Constants
First four bits represent the compressor
Second four bits represent the format
*/
#define kHapCompressorNone 0xA
#define kHapCompressorSnappy 0xB
#define kHapCompressorComplex 0xC
#define kHapFormatRGBDXT1 0xB
#define kHapFormatRGBADXT5 0xE
#define kHapFormatYCoCgDXT5 0xF
#define kHapFormatARGTC1 0x1
#define kHapFormatRGBABPTC 0xC
#define kHapFormatRGBBPTCUF 0x2
#define kHapFormatRGBBPTCSF 0x3
/*
Packed byte values for Hap
Format Compressor Byte Code
----------------------------------------------------
RGB_DXT1 None 0xAB
RGB_DXT1 Snappy 0xBB
RGB_DXT1 Complex 0xCB
RGBA_DXT5 None 0xAE
RGBA_DXT5 Snappy 0xBE
RGBA_DXT5 Complex 0xCE
YCoCg_DXT5 None 0xAF
YCoCg_DXT5 Snappy 0xBF
YCoCg_DXT5 Complex 0xCF
A_RGTC1 None 0xA1
A_RGTC1 Snappy 0xB1
A_RGTC1 Complex 0xC1
RGBA_BPTC_UNORM None 0xAC
RGBA_BPTC_UNORM Snappy 0xBC
RGBA_BPTC_UNORM Complex 0xCC
RGB_BPTC_UNSIGNED_FLOAT None 0xA2
RGB_BPTC_UNSIGNED_FLOAT Snappy 0xB2
RGB_BPTC_UNSIGNED_FLOAT Complex 0xC2
RGB_BPTC_SIGNED_FLOAT None 0xA3
RGB_BPTC_SIGNED_FLOAT Snappy 0xB3
RGB_BPTC_SIGNED_FLOAT Complex 0xC3
*/
/*
Hap Frame Section Types
*/
#define kHapSectionMultipleImages 0x0D
#define kHapSectionDecodeInstructionsContainer 0x01
#define kHapSectionChunkSecondStageCompressorTable 0x02
#define kHapSectionChunkSizeTable 0x03
#define kHapSectionChunkOffsetTable 0x04
/*
To decode we use a struct to store details of each chunk
*/
typedef struct HapChunkDecodeInfo {
unsigned int result;
unsigned int compressor;
const char *compressed_chunk_data;
size_t compressed_chunk_size;
char *uncompressed_chunk_data;
size_t uncompressed_chunk_size;
} HapChunkDecodeInfo;
// TODO: rename the defines we use for codes used in stored frames
// to better differentiate them from the enums used for the API
// These read and write little-endian values on big or little-endian architectures
static unsigned int hap_read_3_byte_uint(const void *buffer)
{
return (*(uint8_t *)buffer) + ((*(((uint8_t *)buffer) + 1)) << 8) + ((*(((uint8_t *)buffer) + 2)) << 16);
}
static void hap_write_3_byte_uint(void *buffer, unsigned int value)
{
*(uint8_t *)buffer = value & 0xFF;
*(((uint8_t *)buffer) + 1) = (value >> 8) & 0xFF;
*(((uint8_t *)buffer) + 2) = (value >> 16) & 0xFF;
}
static unsigned int hap_read_4_byte_uint(const void *buffer)
{
return (*(uint8_t *)buffer) + ((*(((uint8_t *)buffer) + 1)) << 8) + ((*(((uint8_t *)buffer) + 2)) << 16) + ((*(((uint8_t *)buffer) + 3)) << 24);
}
static void hap_write_4_byte_uint(const void *buffer, unsigned int value)
{
*(uint8_t *)buffer = value & 0xFF;
*(((uint8_t *)buffer) + 1) = (value >> 8) & 0xFF;
*(((uint8_t *)buffer) + 2) = (value >> 16) & 0xFF;
*(((uint8_t *)buffer) + 3) = (value >> 24) & 0xFF;
}
#define hap_top_4_bits(x) (((x) & 0xF0) >> 4)
#define hap_bottom_4_bits(x) ((x) & 0x0F)
#define hap_4_bit_packed_byte(top_bits, bottom_bits) (((top_bits) << 4) | ((bottom_bits) & 0x0F))
static int hap_read_section_header(const void *buffer, uint32_t buffer_length, uint32_t *out_header_length, uint32_t *out_section_length, unsigned int *out_section_type)
{
/*
Verify buffer is big enough to contain a four-byte header
*/
if (buffer_length < 4U)
{
return HapResult_Bad_Frame;
}
/*
The first three bytes are the length of the section (not including the header) or zero
if the length is stored in the last four bytes of an eight-byte header
*/
*out_section_length = hap_read_3_byte_uint(buffer);
/*
If the first three bytes are zero, the size is in the following four bytes
*/
if (*out_section_length == 0U)
{
/*
Verify buffer is big enough to contain an eight-byte header
*/
if (buffer_length < 8U)
{
return HapResult_Bad_Frame;
}
*out_section_length = hap_read_4_byte_uint(((uint8_t *)buffer) + 4U);
*out_header_length = 8U;
}
else
{
*out_header_length = 4U;
}
/*
The fourth byte stores the section type
*/
*out_section_type = *(((uint8_t *)buffer) + 3U);
/*
Verify the section does not extend beyond the buffer
*/
if (*out_header_length + *out_section_length > buffer_length)
{
return HapResult_Bad_Frame;
}
return HapResult_No_Error;
}
static void hap_write_section_header(void *buffer, size_t header_length, uint32_t section_length, unsigned int section_type)
{
/*
The first three bytes are the length of the section (not including the header) or zero
if using an eight-byte header
*/
if (header_length == 4U)
{
hap_write_3_byte_uint(buffer, (unsigned int)section_length);
}
else
{
/*
For an eight-byte header, the length is in the last four bytes
*/
hap_write_3_byte_uint(buffer, 0U);
hap_write_4_byte_uint(((uint8_t *)buffer) + 4U, section_length);
}
/*
The fourth byte stores the section type
*/
*(((uint8_t *)buffer) + 3) = section_type;
}
// Returns an API texture format constant or 0 if not recognised
static unsigned int hap_texture_format_constant_for_format_identifier(unsigned int identifier)
{
switch (identifier)
{
case kHapFormatRGBDXT1:
return HapTextureFormat_RGB_DXT1;
case kHapFormatRGBADXT5:
return HapTextureFormat_RGBA_DXT5;
case kHapFormatYCoCgDXT5:
return HapTextureFormat_YCoCg_DXT5;
case kHapFormatARGTC1:
return HapTextureFormat_A_RGTC1;
case kHapFormatRGBABPTC:
return HapTextureFormat_RGBA_BPTC_UNORM;
case kHapFormatRGBBPTCUF:
return HapTextureFormat_RGB_BPTC_UNSIGNED_FLOAT;
case kHapFormatRGBBPTCSF:
return HapTextureFormat_RGB_BPTC_SIGNED_FLOAT;
default:
return 0;
}
}
// Returns a frame identifier or 0 if not recognised
static unsigned int hap_texture_format_identifier_for_format_constant(unsigned int constant)
{
switch (constant)
{
case HapTextureFormat_RGB_DXT1:
return kHapFormatRGBDXT1;
case HapTextureFormat_RGBA_DXT5:
return kHapFormatRGBADXT5;
case HapTextureFormat_YCoCg_DXT5:
return kHapFormatYCoCgDXT5;
case HapTextureFormat_A_RGTC1:
return kHapFormatARGTC1;
case HapTextureFormat_RGBA_BPTC_UNORM:
return kHapFormatRGBABPTC;
case HapTextureFormat_RGB_BPTC_UNSIGNED_FLOAT:
return kHapFormatRGBBPTCUF;
case HapTextureFormat_RGB_BPTC_SIGNED_FLOAT:
return kHapFormatRGBBPTCSF;
default:
return 0;
}
}
// Returns the length of a decode instructions container of chunk_count chunks
// not including the section header
static size_t hap_decode_instructions_length(unsigned int chunk_count)
{
/*
Calculate the size of our Decode Instructions Section
= Second-Stage Compressor Table + Chunk Size Table + headers for both sections
= chunk_count + (4 * chunk_count) + 4 + 4
*/
size_t length = (5 * chunk_count) + 8;
return length;
}
static unsigned int hap_limited_chunk_count_for_frame(size_t input_bytes, unsigned int texture_format, unsigned int chunk_count)
{
// This is a hard limit due to the 4-byte headers we use for the decode instruction container
// (0xFFFFFF == count + (4 x count) + 20)
if (chunk_count > 3355431)
{
chunk_count = 3355431;
}
// Divide frame equally on DXT block boundries (8 or 16 bytes)
unsigned long dxt_block_count;
switch (texture_format) {
case HapTextureFormat_RGB_DXT1:
case HapTextureFormat_A_RGTC1:
dxt_block_count = input_bytes / 8;
break;
default:
dxt_block_count = input_bytes / 16;
}
while (dxt_block_count % chunk_count != 0) {
chunk_count--;
}
return chunk_count;
}
static size_t hap_max_encoded_length(size_t input_bytes, unsigned int texture_format, unsigned int compressor, unsigned int chunk_count)
{
size_t decode_instructions_length, max_compressed_length;
chunk_count = hap_limited_chunk_count_for_frame(input_bytes, texture_format, chunk_count);
decode_instructions_length = hap_decode_instructions_length(chunk_count);
if (compressor == HapCompressorSnappy)
{
size_t chunk_size = input_bytes / chunk_count;
max_compressed_length = snappy_max_compressed_length(chunk_size) * chunk_count;
}
else
{
max_compressed_length = input_bytes;
}
// top section header + decode instructions section header + decode instructions + compressed data
return max_compressed_length + 8U + decode_instructions_length + 4U;
}
unsigned long HapMaxEncodedLength(unsigned int count,
unsigned long *inputBytes,
unsigned int *textureFormats,
unsigned int *chunkCounts)
{
// Start with the length of a multiple-image section header
unsigned long total_length = 8;
// Return 0 for bad arguments
if (count == 0 || count > 2
|| inputBytes == NULL
|| textureFormats == NULL
|| chunkCounts == NULL)
{
return 0;
}
for (int i = 0; i < count; i++)
{
if (chunkCounts[i] == 0)
{
return 0;
}
// Assume snappy, the worst case
total_length += hap_max_encoded_length(inputBytes[i], textureFormats[i], HapCompressorSnappy, chunkCounts[i]);
}
return total_length;
}
static unsigned int hap_encode_texture(const void *inputBuffer, unsigned long inputBufferBytes, unsigned int textureFormat,
unsigned int compressor, unsigned int chunkCount, void *outputBuffer,
unsigned long outputBufferBytes, unsigned long *outputBufferBytesUsed)
{
size_t top_section_header_length;
size_t top_section_length;
unsigned int storedCompressor;
unsigned int storedFormat;
/*
Check arguments
*/
if (inputBuffer == NULL
|| inputBufferBytes == 0
|| (textureFormat != HapTextureFormat_RGB_DXT1
&& textureFormat != HapTextureFormat_RGBA_DXT5
&& textureFormat != HapTextureFormat_YCoCg_DXT5
&& textureFormat != HapTextureFormat_A_RGTC1
&& textureFormat != HapTextureFormat_RGBA_BPTC_UNORM
&& textureFormat != HapTextureFormat_RGB_BPTC_UNSIGNED_FLOAT
&& textureFormat != HapTextureFormat_RGB_BPTC_SIGNED_FLOAT
)
|| (compressor != HapCompressorNone
&& compressor != HapCompressorSnappy
)
|| outputBuffer == NULL
|| outputBufferBytesUsed == NULL
)
{
return HapResult_Bad_Arguments;
}
else if (outputBufferBytes < hap_max_encoded_length(inputBufferBytes, textureFormat, compressor, chunkCount))
{
return HapResult_Buffer_Too_Small;
}
/*
To store frames of length greater than can be expressed in three bytes, we use an eight byte header (the last four bytes are the
frame size). We don't know the compressed size until we have performed compression, but we know the worst-case size
(the uncompressed size), so choose header-length based on that.
A simpler encoder could always use the eight-byte header variation.
*/
if (inputBufferBytes > kHapUInt24Max)
{
top_section_header_length = 8U;
}
else
{
top_section_header_length = 4U;
}
if (compressor == HapCompressorSnappy)
{
/*
We attempt to chunk as requested, and if resulting frame is larger than it is uncompressed then
store frame uncompressed
*/
size_t decode_instructions_length;
size_t chunk_size, compress_buffer_remaining;
uint8_t *second_stage_compressor_table;
void *chunk_size_table;
char *compressed_data;
unsigned int i;
chunkCount = hap_limited_chunk_count_for_frame(inputBufferBytes, textureFormat, chunkCount);
decode_instructions_length = hap_decode_instructions_length(chunkCount);
// Check we have space for the Decode Instructions Container
if ((inputBufferBytes + decode_instructions_length + 4) > kHapUInt24Max)
{
top_section_header_length = 8U;
}
second_stage_compressor_table = ((uint8_t *)outputBuffer) + top_section_header_length + 4 + 4;
chunk_size_table = ((uint8_t *)outputBuffer) + top_section_header_length + 4 + 4 + chunkCount + 4;
chunk_size = inputBufferBytes / chunkCount;
// write the Decode Instructions section header
hap_write_section_header(((uint8_t *)outputBuffer) + top_section_header_length, 4U, decode_instructions_length, kHapSectionDecodeInstructionsContainer);
// write the Second Stage Compressor Table section header
hap_write_section_header(((uint8_t *)outputBuffer) + top_section_header_length + 4U, 4U, chunkCount, kHapSectionChunkSecondStageCompressorTable);
// write the Chunk Size Table section header
hap_write_section_header(((uint8_t *)outputBuffer) + top_section_header_length + 4U + 4U + chunkCount, 4U, chunkCount * 4U, kHapSectionChunkSizeTable);
compressed_data = (char *)(((uint8_t *)outputBuffer) + top_section_header_length + 4 + decode_instructions_length);
compress_buffer_remaining = outputBufferBytes - top_section_header_length - 4 - decode_instructions_length;
top_section_length = 4 + decode_instructions_length;
for (i = 0; i < chunkCount; i++) {
size_t chunk_packed_length = compress_buffer_remaining;
const char *chunk_input_start = (const char *)(((uint8_t *)inputBuffer) + (chunk_size * i));
if (compressor == HapCompressorSnappy)
{
snappy_status result = snappy_compress(chunk_input_start, chunk_size, (char *)compressed_data, &chunk_packed_length);
if (result != SNAPPY_OK)
{
return HapResult_Internal_Error;
}
}
if (compressor == HapCompressorNone || chunk_packed_length >= chunk_size)
{
// store the chunk uncompressed
memcpy(compressed_data, chunk_input_start, chunk_size);
chunk_packed_length = chunk_size;
second_stage_compressor_table[i] = kHapCompressorNone;
}
else
{
// ie we used snappy and saved some space
second_stage_compressor_table[i] = kHapCompressorSnappy;
}
hap_write_4_byte_uint(((uint8_t *)chunk_size_table) + (i * 4), chunk_packed_length);
compressed_data += chunk_packed_length;
top_section_length += chunk_packed_length;
compress_buffer_remaining -= chunk_packed_length;
}
if (top_section_length < inputBufferBytes + top_section_header_length)
{
// use the complex storage because snappy compression saved space
storedCompressor = kHapCompressorComplex;
}
else
{
// Signal to store the frame uncompressed
compressor = HapCompressorNone;
}
}
if (compressor == HapCompressorNone)
{
memcpy(((uint8_t *)outputBuffer) + top_section_header_length, inputBuffer, inputBufferBytes);
top_section_length = inputBufferBytes;
storedCompressor = kHapCompressorNone;
}
storedFormat = hap_texture_format_identifier_for_format_constant(textureFormat);
hap_write_section_header(outputBuffer, top_section_header_length, top_section_length, hap_4_bit_packed_byte(storedCompressor, storedFormat));
*outputBufferBytesUsed = top_section_length + top_section_header_length;
return HapResult_No_Error;
}
unsigned int HapEncode(unsigned int count,
const void **inputBuffers, unsigned long *inputBuffersBytes,
unsigned int *textureFormats,
unsigned int *compressors,
unsigned int *chunkCounts,
void *outputBuffer, unsigned long outputBufferBytes,
unsigned long *outputBufferBytesUsed)
{
size_t top_section_header_length;
size_t top_section_length;
unsigned long section_length;
if (count == 0 || count > 2 // A frame must contain one or two textures
|| inputBuffers == NULL
|| inputBuffersBytes == NULL
|| textureFormats == NULL
|| compressors == NULL
|| chunkCounts == NULL
|| outputBuffer == NULL
|| outputBufferBytes == 0
|| outputBufferBytesUsed == NULL)
{
return HapResult_Bad_Arguments;
}
for (int i = 0; i < count; i++)
{
if (chunkCounts[i] == 0)
{
return HapResult_Bad_Arguments;
}
}
if (count == 1)
{
// Encode without the multi-image layout
return hap_encode_texture(inputBuffers[0],
inputBuffersBytes[0],
textureFormats[0],
compressors[0],
chunkCounts[0],
outputBuffer,
outputBufferBytes,
outputBufferBytesUsed);
}
else if ((textureFormats[0] != HapTextureFormat_YCoCg_DXT5 && textureFormats[1] != HapTextureFormat_YCoCg_DXT5)
&& (textureFormats[0] != HapTextureFormat_A_RGTC1 && textureFormats[1] != HapTextureFormat_A_RGTC1))
{
/*
Permitted combinations:
HapTextureFormat_YCoCg_DXT5 + HapTextureFormat_A_RGTC1
*/
return HapResult_Bad_Arguments;
}
else
{
// Calculate the worst-case size for the top section and choose a header-length based on that
top_section_length = 0;
for (int i = 0; i < count; i++)
{
top_section_length += inputBuffersBytes[i] + hap_decode_instructions_length(chunkCounts[i]) + 4;
}
if (top_section_length > kHapUInt24Max)
{
top_section_header_length = 8U;
}
else
{
top_section_header_length = 4U;
}
// Encode each texture
top_section_length = 0;
for (int i = 0; i < count; i++)
{
void *section = ((uint8_t *)outputBuffer) + top_section_header_length + top_section_length;
unsigned int result = hap_encode_texture(inputBuffers[i],
inputBuffersBytes[i],
textureFormats[i],
compressors[i],
chunkCounts[i],
section,
outputBufferBytes - (top_section_header_length + top_section_length),
§ion_length);
if (result != HapResult_No_Error)
{
return result;
}
top_section_length += section_length;
}
hap_write_section_header(outputBuffer, top_section_header_length, top_section_length, kHapSectionMultipleImages);
*outputBufferBytesUsed = top_section_length + top_section_header_length;
return HapResult_No_Error;
}
}
static void hap_decode_chunk(HapChunkDecodeInfo chunks[], unsigned int index)
{
if (chunks)
{
if (chunks[index].compressor == kHapCompressorSnappy)
{
snappy_status snappy_result = snappy_uncompress(chunks[index].compressed_chunk_data,
chunks[index].compressed_chunk_size,
chunks[index].uncompressed_chunk_data,
&chunks[index].uncompressed_chunk_size);
switch (snappy_result)
{
case SNAPPY_INVALID_INPUT:
chunks[index].result = HapResult_Bad_Frame;
break;
case SNAPPY_OK:
chunks[index].result = HapResult_No_Error;
break;
default:
chunks[index].result = HapResult_Internal_Error;
break;
}
}
else if (chunks[index].compressor == kHapCompressorNone)
{
memcpy(chunks[index].uncompressed_chunk_data,
chunks[index].compressed_chunk_data,
chunks[index].compressed_chunk_size);
chunks[index].result = HapResult_No_Error;
}
else
{
chunks[index].result = HapResult_Bad_Frame;
}
}
}
static unsigned int hap_decode_header_complex_instructions(const void *texture_section, uint32_t texture_section_length, int * chunk_count,
const void **compressors, const void **chunk_sizes, const void **chunk_offsets, const char **frame_data){
int result = HapResult_No_Error;
const void *section_start;
uint32_t section_header_length;
uint32_t section_length;
unsigned int section_type;
size_t bytes_remaining = 0;
*compressors = NULL;
*chunk_sizes = NULL;
*chunk_offsets = NULL;
result = hap_read_section_header(texture_section, texture_section_length, §ion_header_length, §ion_length, §ion_type);
if (result == HapResult_No_Error && section_type != kHapSectionDecodeInstructionsContainer)
{
result = HapResult_Bad_Frame;
}
if (result != HapResult_No_Error)
{
return result;
}
/*
Frame data follows immediately after the Decode Instructions Container
*/
*frame_data = ((const char *)texture_section) + section_header_length + section_length;
/*
Step through the sections inside the Decode Instructions Container
*/
section_start = ((uint8_t *)texture_section) + section_header_length;
bytes_remaining = section_length;
while (bytes_remaining > 0) {
unsigned int section_chunk_count = 0;
result = hap_read_section_header(section_start, bytes_remaining, §ion_header_length, §ion_length, §ion_type);
if (result != HapResult_No_Error)
{
return result;
}
section_start = ((uint8_t *)section_start) + section_header_length;
switch (section_type) {
case kHapSectionChunkSecondStageCompressorTable:
*compressors = section_start;
section_chunk_count = section_length;
break;
case kHapSectionChunkSizeTable:
*chunk_sizes = section_start;
section_chunk_count = section_length / 4;
break;
case kHapSectionChunkOffsetTable:
*chunk_offsets = section_start;
section_chunk_count = section_length / 4;
break;
default:
// Ignore unrecognized sections
break;
}
/*
If we calculated a chunk count and already have one, make sure they match
*/
if (section_chunk_count != 0)
{
if ((*chunk_count) != 0 && section_chunk_count != (*chunk_count))
{
return HapResult_Bad_Frame;
}
*chunk_count = section_chunk_count;
}
section_start = ((uint8_t *)section_start) + section_length;
bytes_remaining -= section_header_length + section_length;
}
/*
The Chunk Second-Stage Compressor Table and Chunk Size Table are required
*/
if (*compressors == NULL || *chunk_sizes == NULL)
{
return HapResult_Bad_Frame;
}
return result;
}
unsigned int hap_decode_single_texture(const void *texture_section, uint32_t texture_section_length,
unsigned int texture_section_type,
HapDecodeCallback callback, void *info,
void *outputBuffer, unsigned long outputBufferBytes,
unsigned long *outputBufferBytesUsed,
unsigned int *outputBufferTextureFormat)
{
int result = HapResult_No_Error;
unsigned int textureFormat;
unsigned int compressor;
size_t bytesUsed = 0;
/*
One top-level section type describes texture-format and second-stage compression
Hap compressor/format constants can be unpacked by reading the top and bottom four bits.
*/
compressor = hap_top_4_bits(texture_section_type);
textureFormat = hap_bottom_4_bits(texture_section_type);
/*
Pass the texture format out
*/
*outputBufferTextureFormat = hap_texture_format_constant_for_format_identifier(textureFormat);
if (*outputBufferTextureFormat == 0)
{
return HapResult_Bad_Frame;
}
if (compressor == kHapCompressorComplex)
{
/*
The top-level section should contain a Decode Instructions Container followed by frame data
*/
int chunk_count = 0;
const void *compressors = NULL;
const void *chunk_sizes = NULL;
const void *chunk_offsets = NULL;
const char *frame_data = NULL;
result = hap_decode_header_complex_instructions(texture_section, texture_section_length, &chunk_count, &compressors, &chunk_sizes, &chunk_offsets, &frame_data);
if (result != HapResult_No_Error)
{
return result;
}
if (chunk_count > 0)
{
/*
Step through the chunks, storing information for their decompression
*/
HapChunkDecodeInfo *chunk_info = (HapChunkDecodeInfo *)malloc(sizeof(HapChunkDecodeInfo) * chunk_count);
size_t running_compressed_chunk_size = 0;
size_t running_uncompressed_chunk_size = 0;
int i;
if (chunk_info == NULL)
{
return HapResult_Internal_Error;
}
for (i = 0; i < chunk_count; i++) {
chunk_info[i].compressor = *(((uint8_t *)compressors) + i);
chunk_info[i].compressed_chunk_size = hap_read_4_byte_uint(((uint8_t *)chunk_sizes) + (i * 4));
if (chunk_offsets)
{
chunk_info[i].compressed_chunk_data = frame_data + hap_read_4_byte_uint(((uint8_t *)chunk_offsets) + (i * 4));
}
else
{
chunk_info[i].compressed_chunk_data = frame_data + running_compressed_chunk_size;
}
running_compressed_chunk_size += chunk_info[i].compressed_chunk_size;
if (chunk_info[i].compressor == kHapCompressorSnappy)
{
snappy_status snappy_result = snappy_uncompressed_length(chunk_info[i].compressed_chunk_data,
chunk_info[i].compressed_chunk_size,
&(chunk_info[i].uncompressed_chunk_size));
if (snappy_result != SNAPPY_OK)
{
switch (snappy_result)
{
case SNAPPY_INVALID_INPUT:
result = HapResult_Bad_Frame;
break;
default:
result = HapResult_Internal_Error;
break;
}
break;
}
}
else
{
chunk_info[i].uncompressed_chunk_size = chunk_info[i].compressed_chunk_size;
}
chunk_info[i].uncompressed_chunk_data = (char *)(((uint8_t *)outputBuffer) + running_uncompressed_chunk_size);
running_uncompressed_chunk_size += chunk_info[i].uncompressed_chunk_size;
}
if (result == HapResult_No_Error && running_uncompressed_chunk_size > outputBufferBytes)
{
result = HapResult_Buffer_Too_Small;
}
if (result == HapResult_No_Error)
{
/*
Perform decompression
*/
bytesUsed = running_uncompressed_chunk_size;
if (chunk_count == 1)
{
/*
We don't invoke the callback for one chunk, just decode it directly
*/
hap_decode_chunk(chunk_info, 0);
}
else
{
callback((HapDecodeWorkFunction)hap_decode_chunk, chunk_info, chunk_count, info);
}
/*
Check to see if we encountered any errors and report one of them
*/
for (i = 0; i < chunk_count; i++)
{
if (chunk_info[i].result != HapResult_No_Error)
{
result = chunk_info[i].result;
break;
}
}
}
free(chunk_info);
if (result != HapResult_No_Error)
{
return result;
}
}
}
else if (compressor == kHapCompressorSnappy)
{
/*
Only one section is present containing a single block of snappy-compressed texture data
*/
snappy_status snappy_result = snappy_uncompressed_length((const char *)texture_section, texture_section_length, &bytesUsed);
if (snappy_result != SNAPPY_OK)
{
return HapResult_Internal_Error;
}
if (bytesUsed > outputBufferBytes)
{
return HapResult_Buffer_Too_Small;
}
snappy_result = snappy_uncompress((const char *)texture_section, texture_section_length, (char *)outputBuffer, &bytesUsed);
if (snappy_result != SNAPPY_OK)
{
return HapResult_Internal_Error;
}
}
else if (compressor == kHapCompressorNone)
{
/*
Only one section is present containing a single block of uncompressed texture data
*/
bytesUsed = texture_section_length;
if (texture_section_length > outputBufferBytes)
{
return HapResult_Buffer_Too_Small;
}
memcpy(outputBuffer, texture_section, texture_section_length);
}
else
{
return HapResult_Bad_Frame;
}
/*
Fill out the remaining return value
*/
if (outputBufferBytesUsed != NULL)
{
*outputBufferBytesUsed = bytesUsed;
}
return HapResult_No_Error;
}
int hap_get_section_at_index(const void *input_buffer, uint32_t input_buffer_bytes,
unsigned int index,
const void **section, uint32_t *section_length, unsigned int *section_type)
{
int result;
uint32_t section_header_length;
result = hap_read_section_header(input_buffer, input_buffer_bytes, §ion_header_length, section_length, section_type);
if (result != HapResult_No_Error)
{
return result;
}
if (*section_type == kHapSectionMultipleImages)
{
/*
Step through until we find the section at index
*/
size_t offset = 0;
size_t top_section_length = *section_length;
input_buffer = ((uint8_t *)input_buffer) + section_header_length;
section_header_length = 0;
*section_length = 0;
for (int i = 0; i <= index; i++) {
offset += section_header_length + *section_length;
if (offset >= top_section_length)
{
return HapResult_Bad_Arguments;
}
result = hap_read_section_header(((uint8_t *)input_buffer) + offset,
top_section_length - offset,
§ion_header_length,
section_length,
section_type);
if (result != HapResult_No_Error)
{
return result;
}
}
offset += section_header_length;
*section = ((uint8_t *)input_buffer) + offset;
return HapResult_No_Error;
}
else if (index == 0)
{
/*
A single-texture frame with the texture as the top section.
*/
*section = ((uint8_t *)input_buffer) + section_header_length;
return HapResult_No_Error;
}
else
{
*section = NULL;
*section_length = 0;
*section_type = 0;
return HapResult_Bad_Arguments;
}
}
unsigned int HapDecode(const void *inputBuffer, unsigned long inputBufferBytes,
unsigned int index,
HapDecodeCallback callback, void *info,
void *outputBuffer, unsigned long outputBufferBytes,
unsigned long *outputBufferBytesUsed,
unsigned int *outputBufferTextureFormat)
{
int result = HapResult_No_Error;
const void *section;
uint32_t section_length;
unsigned int section_type;
/*
Check arguments
*/
if (inputBuffer == NULL
|| index > 1
|| callback == NULL
|| outputBuffer == NULL
|| outputBufferTextureFormat == NULL
)
{
return HapResult_Bad_Arguments;
}
/*
Locate the section at the given index, which will either be the top-level section in a single texture image, or one of the
sections inside a multi-image top-level section.
*/
result = hap_get_section_at_index(inputBuffer, inputBufferBytes, index, §ion, §ion_length, §ion_type);
if (result == HapResult_No_Error)
{
/*
Decode the located texture
*/
result = hap_decode_single_texture(section,
section_length,
section_type,
callback, info,
outputBuffer,
outputBufferBytes,
outputBufferBytesUsed,
outputBufferTextureFormat);
}
return result;
}
unsigned int HapGetFrameTextureCount(const void *inputBuffer, unsigned long inputBufferBytes, unsigned int *outputTextureCount)
{
int result;
uint32_t section_header_length;
uint32_t section_length;
unsigned int section_type;
result = hap_read_section_header(inputBuffer, inputBufferBytes, §ion_header_length, §ion_length, §ion_type);
if (result != HapResult_No_Error)
{
return result;
}
if (section_type == kHapSectionMultipleImages)
{
/*
Step through, counting sections
*/
uint32_t offset = section_header_length;
uint32_t top_section_length = section_length;
*outputTextureCount = 0;
while (offset < top_section_length) {
result = hap_read_section_header(((uint8_t *)inputBuffer) + offset,
inputBufferBytes - offset,
§ion_header_length,
§ion_length,
§ion_type);
if (result != HapResult_No_Error)
{
return result;
}
offset += section_header_length + section_length;
*outputTextureCount += 1;
}
return HapResult_No_Error;
}
else
{
/*
A single-texture frame with the texture as the top section.
*/
*outputTextureCount = 1;
return HapResult_No_Error;
}
}
unsigned int HapGetFrameTextureFormat(const void *inputBuffer, unsigned long inputBufferBytes, unsigned int index, unsigned int *outputBufferTextureFormat)
{
unsigned int result = HapResult_No_Error;
const void *section;
uint32_t section_length;
unsigned int section_type;
/*
Check arguments
*/
if (inputBuffer == NULL
|| index > 1
|| outputBufferTextureFormat == NULL
)
{
return HapResult_Bad_Arguments;
}
/*
Locate the section at the given index, which will either be the top-level section in a single texture image, or one of the
sections inside a multi-image top-level section.
*/
result = hap_get_section_at_index(inputBuffer, inputBufferBytes, index, §ion, §ion_length, §ion_type);
if (result == HapResult_No_Error)
{
/*
Pass the API enum value to match the constant out
*/
*outputBufferTextureFormat = hap_texture_format_constant_for_format_identifier(hap_bottom_4_bits(section_type));
/*
Check a valid format was present
*/
if (*outputBufferTextureFormat == 0)
{
result = HapResult_Bad_Frame;
}
}
return result;
}
unsigned int HapGetFrameTextureChunkCount(const void *inputBuffer, unsigned long inputBufferBytes, unsigned int index, int *chunk_count)
{
unsigned int result = HapResult_No_Error;
const void *section;
uint32_t section_length;
unsigned int section_type;
*chunk_count = 0;
/*
Check arguments
*/
if (inputBuffer == NULL
|| index > 1
)
{
return HapResult_Bad_Arguments;
}
/*
Locate the section at the given index, which will either be the top-level section in a single texture image, or one of the
sections inside a multi-image top-level section.
*/
result = hap_get_section_at_index(inputBuffer, inputBufferBytes, index, §ion, §ion_length, §ion_type);
if (result == HapResult_No_Error)
{
unsigned int compressor;
/*
One top-level section type describes texture-format and second-stage compression
Hap compressor/format constants can be unpacked by reading the top and bottom four bits.
*/
compressor = hap_top_4_bits(section_type);
if (compressor == kHapCompressorComplex)
{
/*
The top-level section should contain a Decode Instructions Container followed by frame data
*/
const void *compressors = NULL;
const void *chunk_sizes = NULL;
const void *chunk_offsets = NULL;
const char *frame_data = NULL;
result = hap_decode_header_complex_instructions(section, section_length, chunk_count, &compressors, &chunk_sizes, &chunk_offsets, &frame_data);
if (result != HapResult_No_Error)
{
return result;
}
}
else if ((compressor == kHapCompressorSnappy)||(compressor == kHapCompressorNone))
{
*chunk_count = 1;
}
else
{
return HapResult_Bad_Frame;
}
}
return result;
}
================================================
FILE: Plugin/Hap/hap.h
================================================
/*
hap.h
Copyright (c) 2011-2013, Tom Butterworth and Vidvox LLC. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef hap_h
#define hap_h
#ifdef __cplusplus
extern "C" {
#endif
/*
These match the constants defined by GL_EXT_texture_compression_s3tc,
GL_ARB_texture_compression_rgtc and GL_ARB_texture_compression_bptc
*/
enum HapTextureFormat {
HapTextureFormat_RGB_DXT1 = 0x83F0,
HapTextureFormat_RGBA_DXT5 = 0x83F3,
HapTextureFormat_YCoCg_DXT5 = 0x01,
HapTextureFormat_A_RGTC1 = 0x8DBB,
HapTextureFormat_RGBA_BPTC_UNORM = 0x8E8C,
HapTextureFormat_RGB_BPTC_UNSIGNED_FLOAT = 0x8E8F,
HapTextureFormat_RGB_BPTC_SIGNED_FLOAT = 0x8E8E,
};
enum HapCompressor {
HapCompressorNone,
HapCompressorSnappy
};
enum HapResult {
HapResult_No_Error = 0,
HapResult_Bad_Arguments,
HapResult_Buffer_Too_Small,
HapResult_Bad_Frame,
HapResult_Internal_Error
};
/*
See HapDecode for descriptions of these function types.
*/
typedef void (*HapDecodeWorkFunction)(void *p, unsigned int index);
typedef void (*HapDecodeCallback)(HapDecodeWorkFunction function, void *p, unsigned int count, void *info);
/*
Returns the maximum size of an output buffer for a frame composed of one or more textures, or returns 0 on error.
count is the number of textures (1 or 2) and matches the number of values in the array arguments
lengths is an array of input texture lengths in bytes
textureFormats is an array of HapTextureFormats
chunkCounts is an array of chunk counts (1 or more)
*/
unsigned long HapMaxEncodedLength(unsigned int count,
unsigned long *lengths,
unsigned int *textureFormats,
unsigned int *chunkCounts);
/*
Encodes one or multiple textures into one Hap frame, or returns an error.
Permitted multiple-texture combinations are:
HapTextureFormat_YCoCg_DXT5 + HapTextureFormat_A_RGTC1
Use HapMaxEncodedLength() to discover the minimal value for outputBufferBytes.
count is the number of textures (1 or 2) and matches the number of values in the array arguments
inputBuffers is an array of count pointers to texture data
inputBufferBytes is an array of texture data lengths in bytes
textureFormats is an array of HapTextureFormats
compressors is an array of HapCompressors
chunkCounts is an array of chunk counts to permit multithreaded decoding (1 or more)
outputBuffer is the destination buffer to receive the encoded frame
outputBufferBytes is the destination buffer's length in bytes
outputBufferBytesUsed will be set to the actual encoded length of the frame on return
*/
unsigned int HapEncode(unsigned int count,
const void **inputBuffers, unsigned long *inputBuffersBytes,
unsigned int *textureFormats,
unsigned int *compressors,
unsigned int *chunkCounts,
void *outputBuffer, unsigned long outputBufferBytes,
unsigned long *outputBufferBytesUsed);
/*
Decodes a texture from inputBuffer which is a Hap frame.
A frame may contain multiple textures which are to be combined to create the final image. Use HapGetFrameTextureCount()
to discover the number of textures in a frame, and then access each texture by incrementing the index argument to this
function.
If the frame permits multithreaded decoding, callback will be called once for you to invoke a platform-appropriate
mechanism to assign work to threads, and trigger that work by calling the function passed to your callback the number
of times indicated by the count argument, usually from a number of different threads. This callback must not return
until all the work has been completed.
void MyHapDecodeCallback(HapDecodeWorkFunction function, void *p, unsigned int count, void *info)
{
int i;
for (i = 0; i < count; i++) {
// Invoke your multithreading mechanism to cause this function to be called
// on a suitable number of threads.
function(p, i);
}
}
info is an argument for your own use to pass context to the callback.
If the frame does not permit multithreaded decoding, callback will not be called.
If outputBufferBytesUsed is not NULL then it will be set to the decoded length of the output buffer.
outputBufferTextureFormat must be non-NULL, and will be set to one of the HapTextureFormat constants.
*/
unsigned int HapDecode(const void *inputBuffer, unsigned long inputBufferBytes,
unsigned int index,
HapDecodeCallback callback, void *info,
void *outputBuffer, unsigned long outputBufferBytes,
unsigned long *outputBufferBytesUsed,
unsigned int *outputBufferTextureFormat);
/*
If this returns HapResult_No_Error then outputTextureCount is set to the count of textures in the frame.
*/
unsigned int HapGetFrameTextureCount(const void *inputBuffer, unsigned long inputBufferBytes, unsigned int *outputTextureCount);
/*
On return sets outputBufferTextureFormat to a HapTextureFormat constant describing the format of the texture at index in the frame.
*/
unsigned int HapGetFrameTextureFormat(const void *inputBuffer, unsigned long inputBufferBytes, unsigned int index, unsigned int *outputBufferTextureFormat);
/*
On return sets chunk_count to the chunk count value of the texture at index in the frame.
*/
unsigned int HapGetFrameTextureChunkCount(const void *inputBuffer, unsigned long inputBufferBytes, unsigned int index, int *chunk_count);
#ifdef __cplusplus
}
#endif
#endif
================================================
FILE: Plugin/MP4/mp4defs.h
================================================
/** 25.09.2018 @file
*
* Common MP4 definitions
*
* Acronyms:
* AVC = Advanced Video Coding (AKA H.264)
* AAC = Advanced Audio Coding
* OD = Object descriptor
* DSI = Decoder Specific Info (AAC element)
* LC = Low Complexity (AAC profile)
* SPS = Sequence Parameter Set (H.264 element)
* PPS = Picture Parameter Set (H.264 element)
*
* The MP4 file has several tracks. Each track contains a number of 'samples'
* (audio or video frames). Position and size of each sample in the track
* encoded in the track index.
*
*/
#ifndef mp4defs_H_INCLUDED
#define mp4defs_H_INCLUDED
/************************************************************************/
/* Some values of MP4X_track_t::object_type_indication */
/************************************************************************/
enum
{
// MPEG-4 AAC (all profiles)
MP4_OBJECT_TYPE_AUDIO_ISO_IEC_14496_3 = 0x40,
// MPEG-2 AAC, Main profile
MP4_OBJECT_TYPE_AUDIO_ISO_IEC_13818_7_MAIN_PROFILE = 0x66,
// MPEG-2 AAC, LC profile
MP4_OBJECT_TYPE_AUDIO_ISO_IEC_13818_7_LC_PROFILE = 0x67,
// MPEG-2 AAC, SSR profile
MP4_OBJECT_TYPE_AUDIO_ISO_IEC_13818_7_SSR_PROFILE = 0x68,
// H.264 (AVC) video
MP4_OBJECT_TYPE_AVC = 0x21,
// http://www.mp4ra.org/object.html 0xC0-E0 && 0xE2 - 0xFE are specified as "user private"
MP4_OBJECT_TYPE_USER_PRIVATE = 0xC0
};
/************************************************************************/
/* Some values of MP4X_track_t::handler_type */
/************************************************************************/
enum
{
// Video track : 'vide'
MP4_HANDLER_TYPE_VIDE = 0x76696465,
// Audio track : 'soun'
MP4_HANDLER_TYPE_SOUN = 0x736F756E,
// General MPEG-4 systems streams (without specific handler).
// Used for private stream, as suggested in http://www.mp4ra.org/handler.html
MP4_HANDLER_TYPE_GESM = 0x6765736D,
// Text comment track
MP4_HANDLER_TYPE_MDIR = 0x6d646972
};
/************************************************************************/
/* Complete box list (most of them not used here) */
/************************************************************************/
#define FOUR_CHAR_INT( a, b, c, d ) (((((((unsigned)(a)<<8)|(b))<<8)|(c))<<8)|(d))
enum
{
BOX_co64 = FOUR_CHAR_INT( 'c', 'o', '6', '4' ),//ChunkLargeOffsetAtomType
BOX_stco = FOUR_CHAR_INT( 's', 't', 'c', 'o' ),//ChunkOffsetAtomType
BOX_crhd = FOUR_CHAR_INT( 'c', 'r', 'h', 'd' ),//ClockReferenceMediaHeaderAtomType
BOX_ctts = FOUR_CHAR_INT( 'c', 't', 't', 's' ),//CompositionOffsetAtomType
BOX_cprt = FOUR_CHAR_INT( 'c', 'p', 'r', 't' ),//CopyrightAtomType
BOX_url_ = FOUR_CHAR_INT( 'u', 'r', 'l', ' ' ),//DataEntryURLAtomType
BOX_urn_ = FOUR_CHAR_INT( 'u', 'r', 'n', ' ' ),//DataEntryURNAtomType
BOX_dinf = FOUR_CHAR_INT( 'd', 'i', 'n', 'f' ),//DataInformationAtomType
BOX_dref = FOUR_CHAR_INT( 'd', 'r', 'e', 'f' ),//DataReferenceAtomType
BOX_stdp = FOUR_CHAR_INT( 's', 't', 'd', 'p' ),//DegradationPriorityAtomType
BOX_edts = FOUR_CHAR_INT( 'e', 'd', 't', 's' ),//EditAtomType
BOX_elst = FOUR_CHAR_INT( 'e', 'l', 's', 't' ),//EditListAtomType
BOX_uuid = FOUR_CHAR_INT( 'u', 'u', 'i', 'd' ),//ExtendedAtomType
BOX_free = FOUR_CHAR_INT( 'f', 'r', 'e', 'e' ),//FreeSpaceAtomType
BOX_hdlr = FOUR_CHAR_INT( 'h', 'd', 'l', 'r' ),//HandlerAtomType
BOX_hmhd = FOUR_CHAR_INT( 'h', 'm', 'h', 'd' ),//HintMediaHeaderAtomType
BOX_hint = FOUR_CHAR_INT( 'h', 'i', 'n', 't' ),//HintTrackReferenceAtomType
BOX_mdia = FOUR_CHAR_INT( 'm', 'd', 'i', 'a' ),//MediaAtomType
BOX_mdat = FOUR_CHAR_INT( 'm', 'd', 'a', 't' ),//MediaDataAtomType
BOX_mdhd = FOUR_CHAR_INT( 'm', 'd', 'h', 'd' ),//MediaHeaderAtomType
BOX_minf = FOUR_CHAR_INT( 'm', 'i', 'n', 'f' ),//MediaInformationAtomType
BOX_moov = FOUR_CHAR_INT( 'm', 'o', 'o', 'v' ),//MovieAtomType
BOX_mvhd = FOUR_CHAR_INT( 'm', 'v', 'h', 'd' ),//MovieHeaderAtomType
BOX_stsd = FOUR_CHAR_INT( 's', 't', 's', 'd' ),//SampleDescriptionAtomType
BOX_stsz = FOUR_CHAR_INT( 's', 't', 's', 'z' ),//SampleSizeAtomType
BOX_stz2 = FOUR_CHAR_INT( 's', 't', 'z', '2' ),//CompactSampleSizeAtomType
BOX_stbl = FOUR_CHAR_INT( 's', 't', 'b', 'l' ),//SampleTableAtomType
BOX_stsc = FOUR_CHAR_INT( 's', 't', 's', 'c' ),//SampleToChunkAtomType
BOX_stsh = FOUR_CHAR_INT( 's', 't', 's', 'h' ),//ShadowSyncAtomType
BOX_skip = FOUR_CHAR_INT( 's', 'k', 'i', 'p' ),//SkipAtomType
BOX_smhd = FOUR_CHAR_INT( 's', 'm', 'h', 'd' ),//SoundMediaHeaderAtomType
BOX_stss = FOUR_CHAR_INT( 's', 't', 's', 's' ),//SyncSampleAtomType
BOX_stts = FOUR_CHAR_INT( 's', 't', 't', 's' ),//TimeToSampleAtomType
BOX_trak = FOUR_CHAR_INT( 't', 'r', 'a', 'k' ),//TrackAtomType
BOX_tkhd = FOUR_CHAR_INT( 't', 'k', 'h', 'd' ),//TrackHeaderAtomType
BOX_tref = FOUR_CHAR_INT( 't', 'r', 'e', 'f' ),//TrackReferenceAtomType
BOX_udta = FOUR_CHAR_INT( 'u', 'd', 't', 'a' ),//UserDataAtomType
BOX_vmhd = FOUR_CHAR_INT( 'v', 'm', 'h', 'd' ),//VideoMediaHeaderAtomType
BOX_url = FOUR_CHAR_INT( 'u', 'r', 'l', ' ' ),
BOX_urn = FOUR_CHAR_INT( 'u', 'r', 'n', ' ' ),
BOX_gnrv = FOUR_CHAR_INT( 'g', 'n', 'r', 'v' ),//GenericVisualSampleEntryAtomType
BOX_gnra = FOUR_CHAR_INT( 'g', 'n', 'r', 'a' ),//GenericAudioSampleEntryAtomType
//V2 atoms
BOX_ftyp = FOUR_CHAR_INT( 'f', 't', 'y', 'p' ),//FileTypeAtomType
BOX_padb = FOUR_CHAR_INT( 'p', 'a', 'd', 'b' ),//PaddingBitsAtomType
//MP4 Atoms
BOX_sdhd = FOUR_CHAR_INT( 's', 'd', 'h', 'd' ),//SceneDescriptionMediaHeaderAtomType
BOX_dpnd = FOUR_CHAR_INT( 'd', 'p', 'n', 'd' ),//StreamDependenceAtomType
BOX_iods = FOUR_CHAR_INT( 'i', 'o', 'd', 's' ),//ObjectDescriptorAtomType
BOX_odhd = FOUR_CHAR_INT( 'o', 'd', 'h', 'd' ),//ObjectDescriptorMediaHeaderAtomType
BOX_mpod = FOUR_CHAR_INT( 'm', 'p', 'o', 'd' ),//ODTrackReferenceAtomType
BOX_nmhd = FOUR_CHAR_INT( 'n', 'm', 'h', 'd' ),//MPEGMediaHeaderAtomType
BOX_esds = FOUR_CHAR_INT( 'e', 's', 'd', 's' ),//ESDAtomType
BOX_sync = FOUR_CHAR_INT( 's', 'y', 'n', 'c' ),//OCRReferenceAtomType
BOX_ipir = FOUR_CHAR_INT( 'i', 'p', 'i', 'r' ),//IPIReferenceAtomType
BOX_mp4s = FOUR_CHAR_INT( 'm', 'p', '4', 's' ),//MPEGSampleEntryAtomType
BOX_mp4a = FOUR_CHAR_INT( 'm', 'p', '4', 'a' ),//MPEGAudioSampleEntryAtomType
BOX_mp4v = FOUR_CHAR_INT( 'm', 'p', '4', 'v' ),//MPEGVisualSampleEntryAtomType
// http://www.itscj.ipsj.or.jp/sc29/open/29view/29n7644t.doc
BOX_avc1 = FOUR_CHAR_INT( 'a', 'v', 'c', '1' ),
BOX_avc2 = FOUR_CHAR_INT( 'a', 'v', 'c', '2' ),
BOX_svc1 = FOUR_CHAR_INT( 's', 'v', 'c', '1' ),
BOX_avcC = FOUR_CHAR_INT( 'a', 'v', 'c', 'C' ),
BOX_svcC = FOUR_CHAR_INT( 's', 'v', 'c', 'C' ),
BOX_btrt = FOUR_CHAR_INT( 'b', 't', 'r', 't' ),
BOX_m4ds = FOUR_CHAR_INT( 'm', '4', 'd', 's' ),
BOX_seib = FOUR_CHAR_INT( 's', 'e', 'i', 'b' ),
//3GPP atoms
BOX_samr = FOUR_CHAR_INT( 's', 'a', 'm', 'r' ),//AMRSampleEntryAtomType
BOX_sawb = FOUR_CHAR_INT( 's', 'a', 'w', 'b' ),//WB_AMRSampleEntryAtomType
BOX_damr = FOUR_CHAR_INT( 'd', 'a', 'm', 'r' ),//AMRConfigAtomType
BOX_s263 = FOUR_CHAR_INT( 's', '2', '6', '3' ),//H263SampleEntryAtomType
BOX_d263 = FOUR_CHAR_INT( 'd', '2', '6', '3' ),//H263ConfigAtomType
//V2 atoms - Movie Fragments
BOX_mvex = FOUR_CHAR_INT( 'm', 'v', 'e', 'x' ),//MovieExtendsAtomType
BOX_trex = FOUR_CHAR_INT( 't', 'r', 'e', 'x' ),//TrackExtendsAtomType
BOX_moof = FOUR_CHAR_INT( 'm', 'o', 'o', 'f' ),//MovieFragmentAtomType
BOX_mfhd = FOUR_CHAR_INT( 'm', 'f', 'h', 'd' ),//MovieFragmentHeaderAtomType
BOX_traf = FOUR_CHAR_INT( 't', 'r', 'a', 'f' ),//TrackFragmentAtomType
BOX_tfhd = FOUR_CHAR_INT( 't', 'f', 'h', 'd' ),//TrackFragmentHeaderAtomType
BOX_trun = FOUR_CHAR_INT( 't', 'r', 'u', 'n' ),//TrackFragmentRunAtomType
// Object Descriptors (OD) data coding
// These takes only 1 byte; this implementation translate to
// + OD_BASE to keep API uniform and safe for string functions
OD_BASE = FOUR_CHAR_INT( '$', '$', '$', '0' ),//
OD_ESD = FOUR_CHAR_INT( '$', '$', '$', '3' ),//SDescriptor_Tag
OD_DCD = FOUR_CHAR_INT( '$', '$', '$', '4' ),//DecoderConfigDescriptor_Tag
OD_DSI = FOUR_CHAR_INT( '$', '$', '$', '5' ),//DecoderSpecificInfo_Tag
OD_SLC = FOUR_CHAR_INT( '$', '$', '$', '6' ),//SLConfigDescriptor_Tag
BOX_meta = FOUR_CHAR_INT( 'm', 'e', 't', 'a' ),
BOX_ilst = FOUR_CHAR_INT( 'i', 'l', 's', 't' ),
// Metagata tags, see http://atomicparsley.sourceforge.net/mpeg-4files.html
BOX_calb = FOUR_CHAR_INT( '\xa9', 'a', 'l', 'b'), // album
BOX_cart = FOUR_CHAR_INT( '\xa9', 'a', 'r', 't'), // artist
BOX_aART = FOUR_CHAR_INT( 'a', 'A', 'R', 'T' ), // album artist
BOX_ccmt = FOUR_CHAR_INT( '\xa9', 'c', 'm', 't'), // comment
BOX_cday = FOUR_CHAR_INT( '\xa9', 'd', 'a', 'y'), // year (as string)
BOX_cnam = FOUR_CHAR_INT( '\xa9', 'n', 'a', 'm'), // title
BOX_cgen = FOUR_CHAR_INT( '\xa9', 'g', 'e', 'n'), // custom genre (as string or as byte!)
BOX_trkn = FOUR_CHAR_INT( 't', 'r', 'k', 'n'), // track number (byte)
BOX_disk = FOUR_CHAR_INT( 'd', 'i', 's', 'k'), // disk number (byte)
BOX_cwrt = FOUR_CHAR_INT( '\xa9', 'w', 'r', 't'), // composer
BOX_ctoo = FOUR_CHAR_INT( '\xa9', 't', 'o', 'o'), // encoder
BOX_tmpo = FOUR_CHAR_INT( 't', 'm', 'p', 'o'), // bpm (byte)
BOX_cpil = FOUR_CHAR_INT( 'c', 'p', 'i', 'l'), // compilation (byte)
BOX_covr = FOUR_CHAR_INT( 'c', 'o', 'v', 'r'), // cover art (JPEG/PNG)
BOX_rtng = FOUR_CHAR_INT( 'r', 't', 'n', 'g'), // rating/advisory (byte)
BOX_cgrp = FOUR_CHAR_INT( '\xa9', 'g', 'r', 'p'), // grouping
BOX_stik = FOUR_CHAR_INT( 's', 't', 'i', 'k'), // stik (byte) 0 = Movie 1 = Normal 2 = Audiobook 5 = Whacked Bookmark 6 = Music Video 9 = Short Film 10 = TV Show 11 = Booklet 14 = Ringtone
BOX_pcst = FOUR_CHAR_INT( 'p', 'c', 's', 't'), // podcast (byte)
BOX_catg = FOUR_CHAR_INT( 'c', 'a', 't', 'g'), // category
BOX_keyw = FOUR_CHAR_INT( 'k', 'e', 'y', 'w'), // keyword
BOX_purl = FOUR_CHAR_INT( 'p', 'u', 'r', 'l'), // podcast URL (byte)
BOX_egid = FOUR_CHAR_INT( 'e', 'g', 'i', 'd'), // episode global unique ID (byte)
BOX_desc = FOUR_CHAR_INT( 'd', 'e', 's', 'c'), // description
BOX_clyr = FOUR_CHAR_INT( '\xa9', 'l', 'y', 'r'), // lyrics (may be > 255 bytes)
BOX_tven = FOUR_CHAR_INT( 't', 'v', 'e', 'n'), // tv episode number
BOX_tves = FOUR_CHAR_INT( 't', 'v', 'e', 's'), // tv episode (byte)
BOX_tvnn = FOUR_CHAR_INT( 't', 'v', 'n', 'n'), // tv network name
BOX_tvsh = FOUR_CHAR_INT( 't', 'v', 's', 'h'), // tv show name
BOX_tvsn = FOUR_CHAR_INT( 't', 'v', 's', 'n'), // tv season (byte)
BOX_purd = FOUR_CHAR_INT( 'p', 'u', 'r', 'd'), // purchase date
BOX_pgap = FOUR_CHAR_INT( 'p', 'g', 'a', 'p'), // Gapless Playback (byte)
//BOX_aart = FOUR_CHAR_INT( 'a', 'a', 'r', 't' ), // Album artist
BOX_cART = FOUR_CHAR_INT( '\xa9', 'A', 'R', 'T'), // artist
BOX_gnre = FOUR_CHAR_INT( 'g', 'n', 'r', 'e'),
// 3GPP metatags (http://cpansearch.perl.org/src/JHAR/MP4-Info-1.12/Info.pm)
BOX_auth = FOUR_CHAR_INT( 'a', 'u', 't', 'h'), // author
BOX_titl = FOUR_CHAR_INT( 't', 'i', 't', 'l'), // title
BOX_dscp = FOUR_CHAR_INT( 'd', 's', 'c', 'p'), // description
BOX_perf = FOUR_CHAR_INT( 'p', 'e', 'r', 'f'), // performer
BOX_mean = FOUR_CHAR_INT( 'm', 'e', 'a', 'n'), //
BOX_name = FOUR_CHAR_INT( 'n', 'a', 'm', 'e'), //
BOX_data = FOUR_CHAR_INT( 'd', 'a', 't', 'a'), //
// these from http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2008-September/053151.html
BOX_albm = FOUR_CHAR_INT( 'a', 'l', 'b', 'm'), // album
BOX_yrrc = FOUR_CHAR_INT( 'y', 'r', 'r', 'c'), // album
// Hap subtypes
BOX_Hap1 = FOUR_CHAR_INT( 'H', 'a', 'p', '1' ),
BOX_Hap5 = FOUR_CHAR_INT( 'H', 'a', 'p', '5' ),
BOX_HapY = FOUR_CHAR_INT( 'H', 'a', 'p', 'Y' ),
BOX_HapM = FOUR_CHAR_INT( 'H', 'a', 'p', 'M' ),
BOX_HapA = FOUR_CHAR_INT( 'H', 'a', 'p', 'A' )
};
#endif //mp4defs_H_INCLUDED
================================================
FILE: Plugin/MP4/mp4demux.c
================================================
/** 20.09.2009 ASP @file
*
*/
#include "mp4demux.h"
#include
#include
#include
#include // LONG_MAX
#include // struct stat
#include // fstat - for file size
/************************************************************************/
/* Build config */
/************************************************************************/
// Max chunks nesting level
#define MP4D_MAX_CHUNKS_DEPTH 64
// Debug trace
#ifndef MP4D_DEBUG_TRACE
# define MP4D_DEBUG_TRACE 0
#endif
#if MP4D_DEBUG_TRACE
# define MP4D_TRACE(x) printf x
#else
# define MP4D_TRACE(x)
#endif
// Box type: ATOM box, or 'Object Descriptor' box inside the atom.
typedef enum {BOX_ATOM, BOX_OD} mp4d_boxtype_t;
/************************************************************************/
/* File input (non-portable) stuff */
/************************************************************************/
/**
* Return 64-bit file size in most portable way
*/
static off_t mp4d_fsize(FILE * f)
{
struct stat st;
#ifdef _MSC_VER
if (fstat(_fileno(f), &st) == 0)
#else
if (fstat(fileno(f), &st) == 0)
#endif
{
return st.st_size;
}
return -1;
}
/**
* Read given number of bytes from the file
* Used to read box headers
*/
static unsigned mp4d_read(FILE * f, int nb, int * eof_flag)
{
uint32_t v = 0; int last_byte;
switch (nb)
{
case 4: v = (v << 8) | fgetc(f);
case 3: v = (v << 8) | fgetc(f);
case 2: v = (v << 8) | fgetc(f);
default:
case 1: v = (v << 8) | (last_byte = fgetc(f));
}
if (last_byte == EOF)
{
*eof_flag = 1;
}
return v;
}
/**
* Read given number of bytes, but no more than *payload_bytes specifies...
* Used to read box payload
*/
static uint32_t mp4d_read_payload(FILE * f, unsigned nb, mp4d_size_t * payload_bytes, int * eof_flag)
{
if (*payload_bytes < nb)
{
*eof_flag = 1;
nb = (int)*payload_bytes;
}
*payload_bytes -= nb;
return mp4d_read(f, nb, eof_flag);
}
/**
* Skips given number of bytes.
*/
static void mp4d_skip_bytes(FILE * f, mp4d_size_t skip, int * eof_flag)
{
while (skip > 0)
{
long lpos = (long)(skip < (mp4d_size_t)LONG_MAX ? skip : LONG_MAX);
if (fseek(f, lpos, SEEK_CUR))
{
*eof_flag = 1;
return;
}
skip -= lpos;
}
}
#define READ(n) mp4d_read_payload(f, n, &payload_bytes, &eof_flag)
#define SKIP(n) {mp4d_size_t t = payload_bytes < (n) ? payload_bytes : (n); mp4d_skip_bytes(f, t, &eof_flag); payload_bytes -= t;}
#define MP4D_MALLOC(p, size) p = malloc(size); if (!(p)) {MP4D_ERROR("out of memory");}
#define MP4D_REALLOC(p, size) {void * r = realloc(p, size); if (!(r)) {MP4D_ERROR("out of memory");} else p = r;};
/*
* On error: release resources, rewind the file.
*/
#define MP4D_RETURN_ERROR(mess) { \
MP4D_TRACE(("\nMP4 ERROR: " mess)); \
fseek(f, 0, SEEK_SET); \
MP4D__close(mp4); \
return 0; \
}
/*
* Any errors, occurred on top-level hierarchy is passed to exit check: 'if (!mp4->track_count) ... '
*/
#define MP4D_ERROR(mess) \
if (!depth) \
break; \
else \
MP4D_RETURN_ERROR(mess);
/************************************************************************/
/* Exported API functions */
/************************************************************************/
/**
* Parse given file as MP4 file. Allocate and store data indexes.
*/
int MP4D__open(MP4D_demux_t * mp4, FILE * f)
{
int depth = 0; // box stack size
// box stack
struct
{
// remaining bytes for box in the stack
mp4d_size_t bytes;
// kind of box children's: OD chunks handled in the same manner as name chunks
mp4d_boxtype_t format;
} stack[MP4D_MAX_CHUNKS_DEPTH];
off_t file_size = mp4d_fsize(f);
int eof_flag = 0;
unsigned i;
MP4D_track_t * tr = NULL;
int read_hdlr = 0;
#if MP4D_DEBUG_TRACE
// path of current element: List0/List1/... etc
uint32_t box_path[MP4D_MAX_CHUNKS_DEPTH];
#endif
if (!f || !mp4)
{
MP4D_TRACE(("\nERROR: invlaid arguments!"));
return 0;
}
if (fseek(f, 0, SEEK_SET)) // some platforms missing rewind()
{
return 0;
}
memset(mp4, 0, sizeof(MP4D_demux_t));
stack[0].format = BOX_ATOM; // start with atom box
stack[0].bytes = 0; // never accessed
do
{
// List of boxes, derived from 'FullBox'
// ~~~~~~~~~~~~~~~~~~~~~
// need read version field and check version for these boxes
static const struct
{
uint32_t name;
unsigned max_version;
unsigned use_track_flag;
} g_fullbox[] =
{
{BOX_mdhd, 1, 1},
{BOX_mvhd, 1, 0},
{BOX_hdlr, 0, 0},
{BOX_meta, 0, 0},
{BOX_stts, 0, 0},
{BOX_ctts, 0, 0},
{BOX_stz2, 0, 1},
{BOX_stsz, 0, 1},
{BOX_stsc, 0, 1},
{BOX_stco, 0, 1},
{BOX_co64, 0, 1},
{BOX_stsd, 0, 0},
{BOX_esds, 0, 1} // esds does not use track, but switches to OD mode. Check here, to avoid OD check
};
// List of boxes, which contains other boxes ('envelopes')
// Parser will descend down for boxes in this list, otherwise parsing will proceed to
// the next sibling box
// OD boxes handled in the same way as atom boxes...
static const struct
{
uint32_t name;
mp4d_boxtype_t type;
} g_envelope_box[] =
{
{BOX_esds, BOX_OD}, // TODO: BOX_esds can be used for both audio and video, but this code supports audio only!
{OD_ESD, BOX_OD},
{OD_DCD, BOX_OD},
{OD_DSI, BOX_OD},
{BOX_trak, BOX_ATOM},
{BOX_moov, BOX_ATOM},
{BOX_mdia, BOX_ATOM},
{BOX_tref, BOX_ATOM},
{BOX_minf, BOX_ATOM},
{BOX_dinf, BOX_ATOM},
{BOX_stbl, BOX_ATOM},
{BOX_stsd, BOX_ATOM},
{BOX_mp4a, BOX_ATOM},
{BOX_mp4s, BOX_ATOM},
{BOX_mp4v, BOX_ATOM},
{BOX_avc1, BOX_ATOM},
{BOX_udta, BOX_ATOM},
{BOX_meta, BOX_ATOM},
{BOX_ilst, BOX_ATOM}
};
uint32_t FullAtomVersionAndFlags = 0;
mp4d_size_t payload_bytes;
mp4d_size_t box_bytes;
uint32_t box_name;
unsigned char ** ptag = NULL;
int read_bytes = 0;
// Read header box type and it's length
if (stack[depth].format == BOX_ATOM)
{
box_bytes = mp4d_read(f, 4, &eof_flag);
if (eof_flag)
{
break; // normal exit
}
if (box_bytes >= 2 && box_bytes < 8)
{
MP4D_ERROR("invalid box size (broken file?)");
}
box_name = mp4d_read(f, 4, &eof_flag);
read_bytes = 8;
// Decode box size
if (box_bytes == 0 || // standard indication of 'till eof' size
box_bytes == (mp4d_size_t)0xFFFFFFFFU // some files uses non-standard 'till eof' signaling
)
{
box_bytes = ~(mp4d_size_t)0;
}
payload_bytes = box_bytes - 8;
if (box_bytes == 1) // 64-bit sizes
{
box_bytes = mp4d_read(f, 4, &eof_flag);
box_bytes <<= 32;
box_bytes |= mp4d_read(f, 4, &eof_flag);
if (box_bytes < 16)
{
MP4D_ERROR("invalid box size (broken file?)");
}
payload_bytes = box_bytes - 16;
}
// Read and check box version for some boxes
for (i = 0; i < sizeof(g_fullbox)/sizeof(g_fullbox[0]); i++)
{
if (box_name == g_fullbox[i].name)
{
FullAtomVersionAndFlags = READ(4);
read_bytes += 4;
if ((FullAtomVersionAndFlags >> 24) > g_fullbox[i].max_version)
{
MP4D_ERROR("unsupported box version!");
}
if (g_fullbox[i].use_track_flag && !tr)
{
MP4D_ERROR("broken file structure!");
}
}
}
}
else // stack[depth].format == BOX_OD
{
int val;
box_name = OD_BASE + mp4d_read(f, 1, &eof_flag); // 1-byte box type
read_bytes += 1;
if (eof_flag)
{
break;
}
payload_bytes = 0;
box_bytes = 1;
do
{
val = mp4d_read(f, 1, &eof_flag);
read_bytes += 1;
if (eof_flag)
{
MP4D_ERROR("premature EOF!");
}
payload_bytes = (payload_bytes << 7) | (val & 0x7F);
box_bytes++;
} while (val & 0x80);
box_bytes += payload_bytes;
}
#if MP4D_DEBUG_TRACE
box_path[depth] = (box_name >> 24) | (box_name << 24) | ((box_name >> 8) & 0x0000FF00) | ((box_name << 8) & 0x00FF0000);
MP4D_TRACE(("%2d %8d %.*s (%d bytes remains for sibilings) \n", depth, (int)box_bytes, depth*4, (char*)box_path, (int)stack[depth].bytes));
#endif
// Check that box size <= parent size
if (depth)
{
// Skip box with bad size
assert(box_bytes > 0);
if (box_bytes > stack[depth].bytes)
{
MP4D_TRACE(("Wrong %c%c%c%c box size: broken file?\n", (box_name >> 24)&255, (box_name >> 16)&255, (box_name >> 8)&255, box_name&255));
box_bytes = stack[depth].bytes;
box_name = 0;
payload_bytes = box_bytes - read_bytes;
}
stack[depth].bytes -= box_bytes;
}
// Read box header
switch(box_name)
{
case BOX_stz2: //ISO/IEC 14496-1 Page 38. Section 8.17.2 - Sample Size Box.
case BOX_stsz:
{
int carry_size = 0;
uint32_t sample_size = READ(4);
tr->sample_count = READ(4);
MP4D_MALLOC(tr->entry_size, tr->sample_count*4);
for (i = 0; i < tr->sample_count; i++)
{
if (box_name == BOX_stsz)
{
tr->entry_size[i] = (sample_size?sample_size:READ(4));
}
else
{
switch (sample_size & 0xFF)
{
case 16:
tr->entry_size[i] = READ(2);
break;
case 8:
tr->entry_size[i] = READ(1);
break;
case 4:
if (i&1)
{
tr->entry_size[i] = carry_size & 15;
}
else
{
carry_size = READ(1);
tr->entry_size[i] = (carry_size >> 4);
}
break;
}
}
}
}
break;
case BOX_stsc: //ISO/IEC 14496-12 Page 38. Section 8.18 - Sample To Chunk Box.
tr->sample_to_chunk_count = READ(4);
MP4D_MALLOC(tr->sample_to_chunk, tr->sample_to_chunk_count*sizeof(tr->sample_to_chunk[0]));
for (i = 0; i < tr->sample_to_chunk_count; i++)
{
tr->sample_to_chunk[i].first_chunk = READ(4);
tr->sample_to_chunk[i].samples_per_chunk = READ(4);
SKIP(4); // sample_description_index
}
break;
case BOX_stts:
{
unsigned count = READ(4);
unsigned j, k = 0, ts = 0, ts_count = count;
MP4D_MALLOC(tr->timestamp, ts_count*4);
MP4D_MALLOC(tr->duration, ts_count*4);
for (i = 0; i < count; i++)
{
unsigned sc = READ(4);
int d = READ(4);
MP4D_TRACE(("sample %8d count %8d duration %8d\n",i,sc,d));
if (k + sc > ts_count)
{
ts_count = k + sc;
MP4D_REALLOC(tr->timestamp, ts_count * sizeof(unsigned));
MP4D_REALLOC(tr->duration, ts_count * sizeof(unsigned));
}
for (j = 0; j < sc; j++)
{
tr->duration[k] = d;
tr->timestamp[k++] = ts;
ts += d;
}
}
}
break;
case BOX_ctts:
{
unsigned count = READ(4);
for (i = 0; i < count; i++)
{
int sc = READ(4);
int d = READ(4);
(void)sc;
(void)d;
MP4D_TRACE(("sample %8d count %8d decoding to composition offset %8d\n",i,sc,d));
}
}
break;
case BOX_stco: //ISO/IEC 14496-12 Page 39. Section 8.19 - Chunk Offset Box.
case BOX_co64:
tr->chunk_count = READ(4);
MP4D_MALLOC(tr->chunk_offset, tr->chunk_count*sizeof(mp4d_size_t));
for (i = 0; i < tr->chunk_count; i++)
{
tr->chunk_offset[i] = READ(4);
if (box_name == BOX_co64)
{
// 64-bit chunk_offset
tr->chunk_offset[i] <<= 32;
tr->chunk_offset[i] |= READ(4);
}
}
break;
case BOX_mvhd:
SKIP(((FullAtomVersionAndFlags >> 24) == 1) ? 8+8 : 4+4);
mp4->timescale = READ(4);
mp4->duration_hi = ((FullAtomVersionAndFlags >> 24) == 1) ? READ(4) : 0;
mp4->duration_lo = READ(4);
SKIP(4+2+2+4*2+4*9+4*6+4);
break;
case BOX_mdhd:
SKIP(((FullAtomVersionAndFlags >> 24) == 1) ? 8+8 : 4+4);
tr->timescale = READ(4);
tr->duration_hi = ((FullAtomVersionAndFlags >> 24) == 1) ? READ(4) : 0;
tr->duration_lo = READ(4);
{
int ISO_639_2_T = READ(2);
tr->language[2] = (ISO_639_2_T & 31) + 0x60;ISO_639_2_T >>= 5;
tr->language[1] = (ISO_639_2_T & 31) + 0x60;ISO_639_2_T >>= 5;
tr->language[0] = (ISO_639_2_T & 31) + 0x60;
}
// the rest of this box is skipped by default ...
break;
case BOX_mdia:
read_hdlr = 1;
break;
case BOX_minf:
read_hdlr = 0;
break;
case BOX_hdlr:
if (tr && read_hdlr) // When this box is within 'meta' box, the track may not be avaialable
{
SKIP(4); // pre_defined
tr->handler_type = READ(4);
}
// typically hdlr box does not contain any useful info.
// the rest of this box is skipped by default ...
break;
case BOX_btrt:
if (!tr)
{
MP4D_ERROR("broken file structure!");
}
SKIP(4+4);
tr->avg_bitrate_bps = READ(4);
break;
// Set pointer to tag to be read...
case BOX_calb: ptag = &mp4->tag.album; break;
case BOX_cART: ptag = &mp4->tag.artist; break;
case BOX_cnam: ptag = &mp4->tag.title; break;
case BOX_cday: ptag = &mp4->tag.year; break;
case BOX_ccmt: ptag = &mp4->tag.comment; break;
case BOX_cgen: ptag = &mp4->tag.genre; break;
case BOX_stsd:
SKIP(4); // entry_count, BOX_mp4a & BOX_mp4v boxes follows immediately
break;
case BOX_mp4s: // private stream
if (!tr)
{
MP4D_ERROR("broken file structure!");
}
SKIP(6*1+2/*Base SampleEntry*/);
break;
case BOX_mp4a:
if (!tr)
{
MP4D_ERROR("broken file structure!");
}
SKIP(6*1+2/*Base SampleEntry*/ + 4*2);
tr->SampleDescription.audio.channelcount = READ(2);
SKIP(2/*samplesize*/ + 2 + 2);
tr->SampleDescription.audio.samplerate_hz = READ(4) >> 16;
break;
// Hap subtypes
case BOX_Hap1:
case BOX_Hap5:
case BOX_HapY:
case BOX_HapM:
case BOX_HapA:
// vvvvvvvvvvvvv AVC support vvvvvvvvvvvvv
case BOX_avc1: // AVCSampleEntry extends VisualSampleEntry
// case BOX_avc2: - no test
// case BOX_svc1: - no test
case BOX_mp4v:
if (!tr)
{
MP4D_ERROR("broken file structure!");
}
SKIP(6*1 + 2/*Base SampleEntry*/ + 2 + 2 + 4*3);
tr->SampleDescription.video.width = READ(2);
tr->SampleDescription.video.height = READ(2);
// frame_count is always 1
// compressorname is rarely set..
SKIP(4 + 4 + 4 + 2/*frame_count*/ + 32/*compressorname*/ + 2 + 2);
// ^^^ end of VisualSampleEntry
// now follows for BOX_avc1:
// BOX_avcC
// BOX_btrt (optional)
// BOX_m4ds (optional)
// for BOX_mp4v:
// BOX_esds
break;
case BOX_avcC: // AVCDecoderConfigurationRecord()
// hack: AAC-specific DSI field reused (for it have same purpose as sps/pps)
// TODO: check this hack if BOX_esds co-exist with BOX_avcC
tr->object_type_indication = MP4_OBJECT_TYPE_AVC;
tr->dsi = malloc((size_t)box_bytes);
tr->dsi_bytes = (unsigned)box_bytes;
{
int spspps;
unsigned char * p = tr->dsi;
unsigned int configurationVersion = READ(1);
unsigned int AVCProfileIndication = READ(1);
unsigned int profile_compatibility = READ(1);
unsigned int AVCLevelIndication = READ(1);
//bit(6) reserved = 111111b;
unsigned int lengthSizeMinusOne = READ(1) & 3;
(void)configurationVersion;
(void)AVCProfileIndication;
(void)profile_compatibility;
(void)AVCLevelIndication;
(void)lengthSizeMinusOne;
for (spspps = 0; spspps < 2; spspps++)
{
unsigned int numOfSequenceParameterSets= READ(1);
if (!spspps)
{
numOfSequenceParameterSets &= 31; // clears 3 msb for SPS
}
*p++ = numOfSequenceParameterSets;
for (i=0; i< numOfSequenceParameterSets; i++) {
unsigned k, sequenceParameterSetLength = READ(2);
*p++ = sequenceParameterSetLength >> 8;
*p++ = sequenceParameterSetLength ;
for (k = 0; k < sequenceParameterSetLength ; k++)
{
*p++ = READ(1);
}
}
}
}
break;
// ^^^^^^^^^^^^^ AVC support ^^^^^^^^^^^^^
case OD_ESD:
{
unsigned flags = READ(3); // ES_ID(2) + flags(1)
if (flags & 0x80) // steamdependflag
{
SKIP(2); // dependsOnESID
}
if (flags & 0x40) // urlflag
{
unsigned bytecount = READ(1);
SKIP(bytecount); // skip URL
}
if (flags & 0x20) // ocrflag (was reserved in MPEG-4 v.1)
{
SKIP(2); // OCRESID
}
break;
}
case OD_DCD: //ISO/IEC 14496-1 Page 28. Section 8.6.5 - DecoderConfigDescriptor.
assert(tr); // ensured by g_fullbox[] check
tr->object_type_indication = READ(1);
tr->stream_type = READ(1) >> 2;
SKIP(3/*bufferSizeDB*/ + 4/*maxBitrate*/);
tr->avg_bitrate_bps = READ(4);
break;
case OD_DSI: //ISO/IEC 14496-1 Page 28. Section 8.6.5 - DecoderConfigDescriptor.
assert(tr); // ensured by g_fullbox[] check
if (!tr->dsi && payload_bytes)
{
MP4D_MALLOC(tr->dsi, (int)payload_bytes);
for (i = 0; i < payload_bytes; i++)
{
tr->dsi[i] = mp4d_read(f, 1, &eof_flag); // These bytes available due to check above
}
tr->dsi_bytes = i;
payload_bytes -= i;
break;
}
default:
MP4D_TRACE(("[%c%c%c%c] %d\n", box_name>>24, box_name>>16, box_name>>8, box_name, (int)payload_bytes));
}
// Read tag is tag pointer is set
if (ptag && !*ptag && payload_bytes > 16)
{
SKIP(4+4+4+4);
MP4D_MALLOC(*ptag, (unsigned)payload_bytes + 1);
for (i = 0; payload_bytes != 0; i++)
{
(*ptag)[i] = READ(1);
}
(*ptag)[i] = 0; // zero-terminated string
}
if (box_name == BOX_trak)
{
// New track found: allocate memory using realloc()
// Typically there are 1 audio track for AAC audio file,
// 4 tracks for movie file,
// 3-5 tracks for scalable audio (CELP+AAC)
// and up to 50 tracks for BSAC scalable audio
MP4D_REALLOC(mp4->track, (mp4->track_count + 1)*sizeof(MP4D_track_t));
tr = mp4->track + mp4->track_count++;
memset(tr, 0, sizeof(MP4D_track_t));
}
else if (box_name == BOX_meta)
{
tr = NULL; // Avoid update of 'hdlr' box, which may contains in the 'meta' box
}
// If this box is envelope, save it's size in box stack
for (i = 0; i < sizeof(g_envelope_box)/sizeof(g_envelope_box[0]); i++)
{
if (box_name == g_envelope_box[i].name)
{
if (++depth >= MP4D_MAX_CHUNKS_DEPTH)
{
MP4D_ERROR("too deep atoms nesting!");
}
stack[depth].bytes = payload_bytes;
stack[depth].format = g_envelope_box[i].type;
break;
}
}
// if box is not envelope, just skip it
if (i == sizeof(g_envelope_box)/sizeof(g_envelope_box[0]))
{
if (payload_bytes > file_size)
{
eof_flag = 1;
}
else
{
SKIP(payload_bytes);
}
}
// remove empty boxes from stack
// don't touch box with index 0 (which indicates whole file)
while (depth > 0 && !stack[depth].bytes)
{
depth--;
}
} while(!eof_flag);
if (!mp4->track_count)
{
MP4D_RETURN_ERROR("no tracks found");
}
fseek(f, 0, SEEK_SET);
return 1;
}
/**
* Find chunk, containing given sample.
* Returns chunk number, and first sample in this chunk.
*/
static int mp4d_sample_to_chunk(MP4D_track_t * tr, unsigned nsample, unsigned * nfirst_sample_in_chunk)
{
unsigned chunk_group = 0, nc;
unsigned sum = 0;
*nfirst_sample_in_chunk = 0;
if (tr->chunk_count <= 1)
{
return 0;
}
for (nc = 0; nc < tr->chunk_count; nc++)
{
if (chunk_group+1 < tr->sample_to_chunk_count // stuck at last entry till EOF
&& nc + 1 == // Chunks counted starting with '1'
tr->sample_to_chunk[chunk_group+1].first_chunk) // next group?
{
chunk_group++;
}
sum += tr->sample_to_chunk[chunk_group].samples_per_chunk;
if (nsample < sum)
{
return nc;
}
// TODO: this can be calculated once per file
*nfirst_sample_in_chunk = sum;
}
return -1;
}
/**
* Return position and size for given sample from given track.
*/
mp4d_size_t MP4D__frame_offset(const MP4D_demux_t * mp4, unsigned ntrack, unsigned nsample, unsigned * frame_bytes, unsigned * timestamp, unsigned * duration)
{
MP4D_track_t * tr = mp4->track + ntrack;
unsigned ns;
int nchunk = mp4d_sample_to_chunk(tr, nsample, &ns);
mp4d_size_t offset;
if (nchunk < 0)
{
*frame_bytes = 0;
return 0;
}
offset = tr->chunk_offset[nchunk];
for (;ns < nsample; ns++)
{
offset += tr->entry_size[ns];
}
*frame_bytes = tr->entry_size[ns];
if (timestamp)
{
*timestamp = tr->timestamp[ns];
}
if (duration)
{
*duration = tr->duration[ns];
}
return offset;
}
/**
* De-allocated memory
*/
void MP4D__close(MP4D_demux_t * mp4)
{
#define FREE(x) if (x) {free(x); x = NULL;}
while (mp4->track_count)
{
MP4D_track_t *tr = mp4->track + --mp4->track_count;
FREE(tr->entry_size);
FREE(tr->timestamp);
FREE(tr->duration);
FREE(tr->sample_to_chunk);
FREE(tr->chunk_offset);
FREE(tr->dsi);
}
FREE(mp4->track);
FREE(mp4->tag.title);
FREE(mp4->tag.artist);
FREE(mp4->tag.album);
FREE(mp4->tag.year);
FREE(mp4->tag.comment);
FREE(mp4->tag.genre);
}
/**
* skip given number of SPS/PPS in the list.
* return number of bytes skipped
*/
static int mp4d_skip_spspps(const unsigned char * p, int nbytes, int nskip)
{
int i, k = 0;
for (i = 0; i < nskip; i++)
{
unsigned segmbytes;
if (k > nbytes - 2)
{
return -1;
}
segmbytes = p[k]*256 + p[k+1];
k += 2 + segmbytes;
}
return k;
}
/**
* Read SPS/PPS with given index
*/
static const unsigned char * MP4D__read_spspps(const MP4D_demux_t * mp4, unsigned int ntrack, int pps_flag, int nsps, int * sps_bytes)
{
int sps_count, skip_bytes;
int bytepos = 0;
unsigned char * p = mp4->track[ntrack].dsi;
if (ntrack >= mp4->track_count)
{
return NULL;
}
if (mp4->track[ntrack].object_type_indication != MP4_OBJECT_TYPE_AVC)
{
return NULL; // SPS/PPS are specific for AVC format only
}
if (pps_flag)
{
// Skip all SPS
sps_count = p[bytepos++];
skip_bytes = mp4d_skip_spspps(p+bytepos, mp4->track[ntrack].dsi_bytes - bytepos, sps_count);
if (skip_bytes < 0)
{
return NULL;
}
bytepos += skip_bytes;
}
// Skip sps/pps before the given target
sps_count = p[bytepos++];
if (nsps >= sps_count)
{
return NULL;
}
skip_bytes = mp4d_skip_spspps(p+bytepos, mp4->track[ntrack].dsi_bytes - bytepos, nsps);
if (skip_bytes < 0)
{
return NULL;
}
bytepos += skip_bytes;
*sps_bytes = p[bytepos]*256 + p[bytepos+1];
return p + bytepos + 2;
}
/**
* Read SPS with given index
* Return pointer to internal mp4 memory, it must not be free()-ed
*/
const unsigned char * MP4D__read_sps(const MP4D_demux_t * mp4, unsigned int ntrack, int nsps, int * sps_bytes)
{
return MP4D__read_spspps(mp4, ntrack, 0, nsps, sps_bytes);
}
/**
* Read PPS with given index
* Return pointer to internal mp4 memory, it must not be free()-ed
*/
const unsigned char * MP4D__read_pps(const MP4D_demux_t * mp4, unsigned int ntrack, int npps, int * pps_bytes)
{
return MP4D__read_spspps(mp4, ntrack, 1, npps, pps_bytes);
}
/************************************************************************/
/* Purely informational part, may be removed for embedded applications */
/************************************************************************/
/**
* Decodes ISO/IEC 14496 MP4 stream type to ASCII string
*/
const char * MP4D__stream_type_to_ascii(int stream_type)
{
switch (stream_type)
{
case 0x00: return "Forbidden";
case 0x01: return "ObjectDescriptorStream";
case 0x02: return "ClockReferenceStream";
case 0x03: return "SceneDescriptionStream";
case 0x04: return "VisualStream";
case 0x05: return "AudioStream";
case 0x06: return "MPEG7Stream";
case 0x07: return "IPMPStream";
case 0x08: return "ObjectContentInfoStream";
case 0x09: return "MPEGJStream";
default:
if (stream_type >= 0x20 && stream_type <= 0x3F)
{
return "User private";
}
else
{
return "Reserved for ISO use";
}
}
}
/**
* Decodes ISO/IEC 14496 MP4 object type to ASCII string
*/
const char * MP4D__object_type_to_ascii(int object_type_indication)
{
switch (object_type_indication)
{
case 0x00: return "Forbidden";
case 0x01: return "Systems ISO/IEC 14496-1";
case 0x02: return "Systems ISO/IEC 14496-1";
case 0x20: return "Visual ISO/IEC 14496-2";
case 0x21: return "Visual ISO/IEC 14496-10";
case 0x22: return "Visual ISO/IEC 14496-10 Parameter Sets";
case 0x40: return "Audio ISO/IEC 14496-3";
case 0x60: return "Visual ISO/IEC 13818-2 Simple Profile";
case 0x61: return "Visual ISO/IEC 13818-2 Main Profile";
case 0x62: return "Visual ISO/IEC 13818-2 SNR Profile";
case 0x63: return "Visual ISO/IEC 13818-2 Spatial Profile";
case 0x64: return "Visual ISO/IEC 13818-2 High Profile";
case 0x65: return "Visual ISO/IEC 13818-2 422 Profile";
case 0x66: return "Audio ISO/IEC 13818-7 Main Profile";
case 0x67: return "Audio ISO/IEC 13818-7 LC Profile";
case 0x68: return "Audio ISO/IEC 13818-7 SSR Profile";
case 0x69: return "Audio ISO/IEC 13818-3";
case 0x6A: return "Visual ISO/IEC 11172-2";
case 0x6B: return "Audio ISO/IEC 11172-3";
case 0x6C: return "Visual ISO/IEC 10918-1";
case 0xFF: return "no object type specified";
default:
if (object_type_indication >= 0xC0 && object_type_indication <= 0xFE)
{
return "User private";
}
else
{
return "Reserved for ISO use";
}
}
}
#ifdef mp4demux_test
/******************************************************************************
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!! !!!!
!!!! !!!!!!!! !!!!!!!! !!!!!!! !!!!!!!! !!!!
!!!! !! !! !! !! !!!!
!!!! !! !! !! !! !!!!
!!!! !! !!!!!! !!!!!!! !! !!!!
!!!! !! !! !! !! !!!!
!!!! !! !! !! !! !!!!
!!!! !! !!!!!!!! !!!!!!! !! !!!!
!!!! !!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
******************************************************************************/
#include
#include
#include
#include
#include
#include
#include "mp4demux.h"
/**
* Print MP4 information to stdout.
*/
static void print_mp4_info(const MP4D_demux_t * mp4_demux)
{
unsigned i;
printf("\nMP4 FILE: %d tracks found. Movie time %.2f sec\n", mp4_demux->track_count, (4294967296.0*mp4_demux->duration_hi + mp4_demux->duration_lo) / mp4_demux->timescale);
printf("\nNo|type|lng| duration | bitrate| %-23s| Object type","Stream type");
for (i = 0; i < mp4_demux->track_count; i++)
{
MP4D_track_t * tr = mp4_demux->track + i;
printf("\n%2d|%c%c%c%c|%c%c%c|%7.2f s %6d frm| %7d|", i,
(tr->handler_type>>24), (tr->handler_type>>16), (tr->handler_type>>8), (tr->handler_type>>0),
tr->language[0],tr->language[1],tr->language[2],
(65536.0* 65536.0*tr->duration_hi + tr->duration_lo) / tr->timescale,
tr->sample_count,
tr->avg_bitrate_bps
);
printf(" %-23s|", MP4D__stream_type_to_ascii(tr->stream_type));
printf(" %-23s", MP4D__object_type_to_ascii(tr->object_type_indication));
if (tr->handler_type == MP4_HANDLER_TYPE_SOUN)
{
printf(" - %d ch %d hz", tr->SampleDescription.audio.channelcount, tr->SampleDescription.audio.samplerate_hz);
}
else if (tr->handler_type == MP4_HANDLER_TYPE_VIDE)
{
printf(" - %dx%d", tr->SampleDescription.video.width, tr->SampleDescription.video.height);
}
}
printf("\n");
}
/**
* Print MP4 file comment to stdout.
*/
static void print_comment(const MP4D_demux_t * mp4_demux)
{
#define STR_TAG(name) if (mp4_demux->tag.name) printf("%10s = %s\n", #name, mp4_demux->tag.name)
STR_TAG(title);
STR_TAG(artist);
STR_TAG(album);
STR_TAG(year);
STR_TAG(comment);
STR_TAG(genre);
}
/**
* Print SPS/PPS/DSI data in hex to stdout.
*/
static void print_dsi_data(const MP4D_demux_t * mp4_demux)
{
unsigned k, ntrack;
for (ntrack = 0; ntrack < mp4_demux->track_count; ntrack++)
{
MP4D_track_t *tr = mp4_demux->track + ntrack;
if (tr->dsi_bytes)
{
int i, k, sps_bytes, pps_bytes, sps_pps_found = 0;
for (i = 0; i < 256; i++)
{
const unsigned char *sps = MP4D__read_sps(mp4_demux, ntrack, i, &sps_bytes);
const unsigned char *pps = MP4D__read_pps(mp4_demux, ntrack, i, &pps_bytes);
if (sps && sps_bytes)
{
printf("%d SPS bytes found for track #%d:\n", sps_bytes, ntrack);
for (k = 0; k < sps_bytes; k++)
{
printf("%02x ", sps[k]);
}
printf("\n");
sps_pps_found = 1;
}
if (pps && pps_bytes)
{
printf("%d PPS bytes found for track #%d:\n", pps_bytes, ntrack);
for (k = 0; k < pps_bytes; k++)
{
printf("%02x ", pps[k]);
}
printf("\n");
sps_pps_found = 1;
}
}
if (!sps_pps_found)
{
printf("%d DSI bytes found for track #%d:\n", tr->dsi_bytes, ntrack);
for (k = 0; k < tr->dsi_bytes; k++)
{
printf("%02x ", tr->dsi[k]);
}
printf("\n");
}
}
}
}
/**
* Save AVC & audio tracks data to files
*/
static void save_track_data(const MP4D_demux_t * mp4_demux, FILE * mp4_file, unsigned ntrack)
{
unsigned i, frame_bytes, timestamp, duration;
unsigned avc_bytes_to_next_nal = 0;
MP4D_track_t *tr = mp4_demux->track + ntrack;
FILE * track_file;
char name[100];
const char * ext = (tr->object_type_indication == MP4_OBJECT_TYPE_AVC) ? "264" :
(tr->handler_type == MP4_HANDLER_TYPE_SOUN) ? "audio" :
(tr->handler_type == MP4_HANDLER_TYPE_VIDE) ? "video" : "data";
sprintf(name, "track%d.%s", ntrack, ext);
track_file = fopen(name,"wb");
for (i = 0; i < tr->sample_count; i++)
{
unsigned char * frame_mem;
mp4d_size_t frame_ofs = MP4D__frame_offset(mp4_demux, ntrack, i, &frame_bytes, ×tamp, &duration);
// print frame offset
//printf("%4d %06x %08d %d\n", i, (unsigned)ofs, duration, frame_bytes);
//printf("%4d %06x %08d %d\n", i, (unsigned)ofs, timestamp, frame_bytes);
// save payload
frame_mem = malloc(frame_bytes);
fseek(mp4_file, (long)frame_ofs, SEEK_SET);
fread(frame_mem, 1, frame_bytes, mp4_file);
if (mp4_demux->track[ntrack].object_type_indication == MP4_OBJECT_TYPE_AVC)
{
// replace 4-byte length field with start code
unsigned startcode = 0x01000000;
unsigned k = avc_bytes_to_next_nal;
while (k < frame_bytes - 4)
{
avc_bytes_to_next_nal = 4 + ((frame_mem[k] * 256 + frame_mem[k+1])*256 + frame_mem[k+2])*256 + frame_mem[k+3];
*(unsigned *)(frame_mem + k) = startcode;
k += avc_bytes_to_next_nal;
}
avc_bytes_to_next_nal = k - frame_bytes;
// Write SPS/PPS for 1st frame
if (!i)
{
const void * data;
int nps, bytes;
for (nps = 0; NULL != (data = MP4D__read_sps(mp4_demux, ntrack, nps, &bytes)); nps++)
{
fwrite(&startcode, 1, 4, track_file);
fwrite(data, 1, bytes, track_file);
}
for (nps = 0; NULL != (data = MP4D__read_pps(mp4_demux, ntrack, nps, &bytes)); nps++)
{
fwrite(&startcode, 1, 4, track_file);
fwrite(data, 1, bytes, track_file);
}
}
}
fwrite(frame_mem, 1, frame_bytes, track_file);
free(frame_mem);
}
fclose(track_file);
}
int main(int argc, char* argv[])
{
unsigned ntrack = 0;
MP4D_demux_t mp4_demux = {0,};
char* file_name = (argc>1)?argv[1]:"default_input.mp4";
FILE * mp4_file = fopen(file_name, "rb");
if (!mp4_file)
{
printf("\nERROR: can't open file %s for reading\n", file_name);
return 0;
}
if (!MP4D__open(&mp4_demux, mp4_file))
{
printf("\nERROR: can't parse %s \n", file_name);
return 0;
}
print_mp4_info(&mp4_demux);
for (ntrack = 0; ntrack < mp4_demux.track_count; ntrack++)
{
save_track_data(&mp4_demux, mp4_file, ntrack);
}
print_comment(&mp4_demux);
print_dsi_data(&mp4_demux);
MP4D__close(&mp4_demux);
fclose(mp4_file);
return 0;
}
// dmc mp4demux.c -Dmp4demux_test && del *.obj *.map
#endif // mp4demux_test
================================================
FILE: Plugin/MP4/mp4demux.h
================================================
/** 20.09.2009 @file
* ISO MP4 file parsing
*
* Portability note: this module uses:
* - Dynamic memory allocation (malloc(), realloc() and free()
* - Direct file access (fgetc(), fread() & fseek())
* - File size (fstat())
*
* This module provide functions to decode mp4 indexes, and retrieve
* file position and size for each sample in given track.
*
* Integration scenario example:
*
* // 1. Parse MP4 structure
* if (MP4D__open(mp4, file_handle))
* {
* // 2. Find tracks, supported by the application
* for (i = 0; i < mp4->track_count; i++)
* {
* if (supported(mp4->track[i].object_type_indication))
* {
* // 3. Initialize decoder from transparent 'Decoder Specific Info' data
* init_decoder(mp4->track[i].dsi);
*
* // 4. Read track data for each frame
* for (k = 0; k < mp4->track[i].sample_count; k++)
* {
* fseek(file_handle, MP4D__frame_offset(mp4, i, k, &frame_size, NULL, NULL), SEEK_SET);
* fread(buf, frame_size, 1, file_handle);
* decode(buf);
* }
* }
* }
* }
*
*/
#ifndef mp4demux_H_INCLUDED
#define mp4demux_H_INCLUDED
#include
#include "mp4defs.h"
#ifdef __cplusplus
extern "C" {
#endif //__cplusplus
/************************************************************************/
/* Portable 64-bit type definition */
/************************************************************************/
#if (defined(__GNUC__) && __GNUC__ >= 4) || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901)
# include // hope that all GCC compilers support this C99 extension
typedef uint64_t mp4d_size_t;
#else
# if defined (_MSC_VER)
typedef unsigned __int64 mp4d_size_t;
# else
typedef unsigned long long mp4d_size_t;
# endif
typedef unsigned int uint32_t;
#endif
typedef struct
{
unsigned first_chunk;
unsigned samples_per_chunk;
} MP4D_sample_to_chunk_t;
typedef struct
{
/************************************************************************/
/* mandatory public data */
/************************************************************************/
// How many 'samples' in the track
// The 'sample' is MP4 term, denoting audio or video frame
unsigned sample_count;
// Decoder-specific info (DSI) data
unsigned char * dsi;
// DSI data size
unsigned dsi_bytes;
// MP4 object type code
// case 0x00: return "Forbidden";
// case 0x01: return "Systems ISO/IEC 14496-1";
// case 0x02: return "Systems ISO/IEC 14496-1";
// case 0x20: return "Visual ISO/IEC 14496-2";
// case 0x40: return "Audio ISO/IEC 14496-3";
// case 0x60: return "Visual ISO/IEC 13818-2 Simple Profile";
// case 0x61: return "Visual ISO/IEC 13818-2 Main Profile";
// case 0x62: return "Visual ISO/IEC 13818-2 SNR Profile";
// case 0x63: return "Visual ISO/IEC 13818-2 Spatial Profile";
// case 0x64: return "Visual ISO/IEC 13818-2 High Profile";
// case 0x65: return "Visual ISO/IEC 13818-2 422 Profile";
// case 0x66: return "Audio ISO/IEC 13818-7 Main Profile";
// case 0x67: return "Audio ISO/IEC 13818-7 LC Profile";
// case 0x68: return "Audio ISO/IEC 13818-7 SSR Profile";
// case 0x69: return "Audio ISO/IEC 13818-3";
// case 0x6A: return "Visual ISO/IEC 11172-2";
// case 0x6B: return "Audio ISO/IEC 11172-3";
// case 0x6C: return "Visual ISO/IEC 10918-1";
unsigned object_type_indication;
/************************************************************************/
/* informational public data, not needed for decoding */
/************************************************************************/
// handler_type when present in a media box, is an integer containing one of
// the following values, or a value from a derived specification:
// 'vide' Video track
// 'soun' Audio track
// 'hint' Hint track
unsigned handler_type;
// Track duration: 64-bit value split into 2 variables
unsigned duration_hi;
unsigned duration_lo;
// duration scale: duration = timescale*seconds
unsigned timescale;
// Average bitrate, bits per second
unsigned avg_bitrate_bps;
// Track language: 3-char ISO 639-2T code: "und", "eng", "rus", "jpn" etc...
unsigned char language[4];
// MP4 stream type
// case 0x00: return "Forbidden";
// case 0x01: return "ObjectDescriptorStream";
// case 0x02: return "ClockReferenceStream";
// case 0x03: return "SceneDescriptionStream";
// case 0x04: return "VisualStream";
// case 0x05: return "AudioStream";
// case 0x06: return "MPEG7Stream";
// case 0x07: return "IPMPStream";
// case 0x08: return "ObjectContentInfoStream";
// case 0x09: return "MPEGJStream";
unsigned stream_type;
union
{
// for handler_type == 'soun' tracks
struct
{
unsigned channelcount;
unsigned samplerate_hz;
} audio;
// for handler_type == 'vide' tracks
struct
{
unsigned width;
unsigned height;
} video;
} SampleDescription;
/************************************************************************/
/* private data: MP4 indexes */
/************************************************************************/
unsigned *entry_size; // [sample_count]
unsigned *timestamp; // [sample_count]
unsigned *duration; // [sample_count]
unsigned sample_to_chunk_count;
MP4D_sample_to_chunk_t * sample_to_chunk; // [sample_to_chunk_count]
unsigned chunk_count;
mp4d_size_t * chunk_offset; // [chunk_count]
} MP4D_track_t;
typedef struct MP4D_demux_tag
{
/************************************************************************/
/* mandatory public data */
/************************************************************************/
// number of tracks in the movie
unsigned track_count;
// track data (public/private)
MP4D_track_t * track;
/************************************************************************/
/* informational public data, not needed for decoding */
/************************************************************************/
// Movie duration: 64-bit value split into 2 variables
unsigned duration_hi;
unsigned duration_lo;
// duration scale: duration = timescale*seconds
unsigned timescale;
// Metadata tag (optional)
// Tags provided 'as-is', without any re-encoding
struct
{
unsigned char *title;
unsigned char *artist;
unsigned char *album;
unsigned char *year;
unsigned char *comment;
unsigned char *genre;
} tag;
} MP4D_demux_t;
/**
* Parse given file as MP4 file. Allocate and store data indexes.
* return 1 on success, 0 on failure
* Given file rewind()'ed on return.
* The MP4 indexes may be stored at the end of file, so this
* function may parse all file, using fseek().
* It is guaranteed that function will read/seek the file sequentially,
* and will never jump back.
*/
int MP4D__open(MP4D_demux_t * mp4, FILE * f);
/**
* Return position and size for given sample from given track. The 'sample' is a
* MP4 term for 'frame'
*
* frame_bytes [OUT] - return coded frame size in bytes
* timestamp [OUT] - return frame timestamp (in mp4->timescale units)
* duration [OUT] - return frame duration (in mp4->timescale units)
*
* function return file offset for the frame
*/
mp4d_size_t MP4D__frame_offset(const MP4D_demux_t * mp4, unsigned int ntrack, unsigned int nsample, unsigned int * frame_bytes, unsigned * timestamp, unsigned * duration);
/**
* De-allocated memory
*/
void MP4D__close(MP4D_demux_t * mp4);
/**
* Helper functions to parse mp4.track[ntrack].dsi for H.264 SPS/PPS
* Return pointer to internal mp4 memory, it must not be free()-ed
*
* Example: process all SPS in MP4 file:
* while (sps = MP4D__read_sps(mp4, num_of_avc_track, sps_count, &sps_bytes))
* {
* process(sps, sps_bytes);
* sps_count++;
* }
*/
const unsigned char * MP4D__read_sps(const MP4D_demux_t * mp4, unsigned int ntrack, int nsps, int * sps_bytes);
const unsigned char * MP4D__read_pps(const MP4D_demux_t * mp4, unsigned int ntrack, int npps, int * pps_bytes);
/**
* Decode MP4D_track_t::stream_type to ASCII string
*/
const char * MP4D__stream_type_to_ascii(int stream_type);
/**
* Decode MP4D_track_t::object_type_indication to ASCII string
*/
const char * MP4D__object_type_to_ascii(int object_type_indication);
#ifdef __cplusplus
}
#endif //__cplusplus
#endif //mp4demux_H_INCLUDED
================================================
FILE: Plugin/Makefile.linux
================================================
PLATFORM = Linux
ARCH = x86_64
TARGET_TYPE = so
CFLAGS = -fPIC -ffunction-sections -fdata-sections
CXXFLAGS = -fPIC -ffunction-sections -fdata-sections
LDFLAGS = -shared -Wl,--gc-sections
include Common.mk
================================================
FILE: Plugin/Makefile.macos
================================================
PLATFORM = macOS
TARGET_TYPE = dylib
SDK_MIN_VER = 11.0
ARCH_OPT = -arch $(ARCH) -mmacosx-version-min=$(SDK_MIN_VER)
CFLAGS += $(ARCH_OPT)
CXXFLAGS += $(ARCH_OPT)
LDFLAGS += $(ARCH_OPT) -dynamiclib
include Common.mk
================================================
FILE: Plugin/Makefile.windows
================================================
PLATFORM = Windows
ARCH = x86_64
TARGET_TYPE = dll
TOOLCHAIN = $(ARCH)-w64-mingw32
CC = $(TOOLCHAIN)-gcc-posix
CXX = $(TOOLCHAIN)-g++-posix
STRIP = $(TOOLCHAIN)-strip
LDFLAGS = -shared
LIBS = -static -Wl,--subsystem,windows
include Common.mk
================================================
FILE: Plugin/Snappy/snappy-c.cc
================================================
// Copyright 2011 Martin Gieseking .
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "snappy.h"
#include "snappy-c.h"
extern "C" {
snappy_status snappy_compress(const char* input,
size_t input_length,
char* compressed,
size_t *compressed_length) {
if (*compressed_length < snappy_max_compressed_length(input_length)) {
return SNAPPY_BUFFER_TOO_SMALL;
}
snappy::RawCompress(input, input_length, compressed, compressed_length);
return SNAPPY_OK;
}
snappy_status snappy_uncompress(const char* compressed,
size_t compressed_length,
char* uncompressed,
size_t* uncompressed_length) {
size_t real_uncompressed_length;
if (!snappy::GetUncompressedLength(compressed,
compressed_length,
&real_uncompressed_length)) {
return SNAPPY_INVALID_INPUT;
}
if (*uncompressed_length < real_uncompressed_length) {
return SNAPPY_BUFFER_TOO_SMALL;
}
if (!snappy::RawUncompress(compressed, compressed_length, uncompressed)) {
return SNAPPY_INVALID_INPUT;
}
*uncompressed_length = real_uncompressed_length;
return SNAPPY_OK;
}
size_t snappy_max_compressed_length(size_t source_length) {
return snappy::MaxCompressedLength(source_length);
}
snappy_status snappy_uncompressed_length(const char *compressed,
size_t compressed_length,
size_t *result) {
if (snappy::GetUncompressedLength(compressed,
compressed_length,
result)) {
return SNAPPY_OK;
} else {
return SNAPPY_INVALID_INPUT;
}
}
snappy_status snappy_validate_compressed_buffer(const char *compressed,
size_t compressed_length) {
if (snappy::IsValidCompressedBuffer(compressed, compressed_length)) {
return SNAPPY_OK;
} else {
return SNAPPY_INVALID_INPUT;
}
}
} // extern "C"
================================================
FILE: Plugin/Snappy/snappy-c.h
================================================
/*
* Copyright 2011 Martin Gieseking .
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Plain C interface (a wrapper around the C++ implementation).
*/
#ifndef THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_C_H_
#define THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_C_H_
#ifdef __cplusplus
extern "C" {
#endif
#include
/*
* Return values; see the documentation for each function to know
* what each can return.
*/
typedef enum {
SNAPPY_OK = 0,
SNAPPY_INVALID_INPUT = 1,
SNAPPY_BUFFER_TOO_SMALL = 2
} snappy_status;
/*
* Takes the data stored in "input[0..input_length-1]" and stores
* it in the array pointed to by "compressed".
*
* signals the space available in "compressed".
* If it is not at least equal to "snappy_max_compressed_length(input_length)",
* SNAPPY_BUFFER_TOO_SMALL is returned. After successful compression,
* contains the true length of the compressed output,
* and SNAPPY_OK is returned.
*
* Example:
* size_t output_length = snappy_max_compressed_length(input_length);
* char* output = (char*)malloc(output_length);
* if (snappy_compress(input, input_length, output, &output_length)
* == SNAPPY_OK) {
* ... Process(output, output_length) ...
* }
* free(output);
*/
snappy_status snappy_compress(const char* input,
size_t input_length,
char* compressed,
size_t* compressed_length);
/*
* Given data in "compressed[0..compressed_length-1]" generated by
* calling the snappy_compress routine, this routine stores
* the uncompressed data to
* uncompressed[0..uncompressed_length-1].
* Returns failure (a value not equal to SNAPPY_OK) if the message
* is corrupted and could not be decrypted.
*
* signals the space available in "uncompressed".
* If it is not at least equal to the value returned by
* snappy_uncompressed_length for this stream, SNAPPY_BUFFER_TOO_SMALL
* is returned. After successful decompression,
* contains the true length of the decompressed output.
*
* Example:
* size_t output_length;
* if (snappy_uncompressed_length(input, input_length, &output_length)
* != SNAPPY_OK) {
* ... fail ...
* }
* char* output = (char*)malloc(output_length);
* if (snappy_uncompress(input, input_length, output, &output_length)
* == SNAPPY_OK) {
* ... Process(output, output_length) ...
* }
* free(output);
*/
snappy_status snappy_uncompress(const char* compressed,
size_t compressed_length,
char* uncompressed,
size_t* uncompressed_length);
/*
* Returns the maximal size of the compressed representation of
* input data that is "source_length" bytes in length.
*/
size_t snappy_max_compressed_length(size_t source_length);
/*
* REQUIRES: "compressed[]" was produced by snappy_compress()
* Returns SNAPPY_OK and stores the length of the uncompressed data in
* *result normally. Returns SNAPPY_INVALID_INPUT on parsing error.
* This operation takes O(1) time.
*/
snappy_status snappy_uncompressed_length(const char* compressed,
size_t compressed_length,
size_t* result);
/*
* Check if the contents of "compressed[]" can be uncompressed successfully.
* Does not return the uncompressed data; if so, returns SNAPPY_OK,
* or if not, returns SNAPPY_INVALID_INPUT.
* Takes time proportional to compressed_length, but is usually at least a
* factor of four faster than actual decompression.
*/
snappy_status snappy_validate_compressed_buffer(const char* compressed,
size_t compressed_length);
#ifdef __cplusplus
} // extern "C"
#endif
#endif /* THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_C_H_ */
================================================
FILE: Plugin/Snappy/snappy-internal.h
================================================
// Copyright 2008 Google Inc. All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Internals shared between the Snappy implementation and its unittest.
#ifndef THIRD_PARTY_SNAPPY_SNAPPY_INTERNAL_H_
#define THIRD_PARTY_SNAPPY_SNAPPY_INTERNAL_H_
#include
#include "snappy-stubs-internal.h"
#if SNAPPY_HAVE_SSSE3
// Please do not replace with or with headers that assume more
// advanced SSE versions without checking with all the OWNERS.
#include
#include
#endif
#if SNAPPY_HAVE_NEON
#include
#endif
#if SNAPPY_RVV_1 || SNAPPY_RVV_0_7
#define SNAPPY_HAVE_RVV 1
#include
#else
#define SNAPPY_HAVE_RVV 0
#endif
#ifdef SNAPPY_RVV_1
#define VSETVL_E8M2 __riscv_vsetvl_e8m2
#define VLE8_V_U8M2 __riscv_vle8_v_u8m2
#define VSE8_V_U8M2 __riscv_vse8_v_u8m2
#elif SNAPPY_RVV_0_7
#define VSETVL_E8M2 vsetvl_e8m2
#define VLE8_V_U8M2 vle8_v_u8m2
#define VSE8_V_U8M2 vse8_v_u8m2
#endif
#if SNAPPY_HAVE_SSSE3 || SNAPPY_HAVE_NEON
#define SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE 1
#else
#define SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE 0
#endif
namespace snappy {
namespace internal {
#if SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE
#if SNAPPY_HAVE_SSSE3
using V128 = __m128i;
#elif SNAPPY_HAVE_NEON
using V128 = uint8x16_t;
#endif
// Load 128 bits of integer data. `src` must be 16-byte aligned.
inline V128 V128_Load(const V128* src);
// Load 128 bits of integer data. `src` does not need to be aligned.
inline V128 V128_LoadU(const V128* src);
// Store 128 bits of integer data. `dst` does not need to be aligned.
inline void V128_StoreU(V128* dst, V128 val);
// Shuffle packed 8-bit integers using a shuffle mask.
// Each packed integer in the shuffle mask must be in [0,16).
inline V128 V128_Shuffle(V128 input, V128 shuffle_mask);
// Constructs V128 with 16 chars |c|.
inline V128 V128_DupChar(char c);
#if SNAPPY_HAVE_SSSE3
inline V128 V128_Load(const V128* src) { return _mm_load_si128(src); }
inline V128 V128_LoadU(const V128* src) { return _mm_loadu_si128(src); }
inline void V128_StoreU(V128* dst, V128 val) { _mm_storeu_si128(dst, val); }
inline V128 V128_Shuffle(V128 input, V128 shuffle_mask) {
return _mm_shuffle_epi8(input, shuffle_mask);
}
inline V128 V128_DupChar(char c) { return _mm_set1_epi8(c); }
#elif SNAPPY_HAVE_NEON
inline V128 V128_Load(const V128* src) {
return vld1q_u8(reinterpret_cast(src));
}
inline V128 V128_LoadU(const V128* src) {
return vld1q_u8(reinterpret_cast(src));
}
inline void V128_StoreU(V128* dst, V128 val) {
vst1q_u8(reinterpret_cast(dst), val);
}
inline V128 V128_Shuffle(V128 input, V128 shuffle_mask) {
assert(vminvq_u8(shuffle_mask) >= 0 && vmaxvq_u8(shuffle_mask) <= 15);
return vqtbl1q_u8(input, shuffle_mask);
}
inline V128 V128_DupChar(char c) { return vdupq_n_u8(c); }
#endif
#endif // SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE
// Working memory performs a single allocation to hold all scratch space
// required for compression.
class WorkingMemory {
public:
explicit WorkingMemory(size_t input_size);
~WorkingMemory();
// Allocates and clears a hash table using memory in "*this",
// stores the number of buckets in "*table_size" and returns a pointer to
// the base of the hash table.
uint16_t* GetHashTable(size_t fragment_size, int* table_size) const;
char* GetScratchInput() const { return input_; }
char* GetScratchOutput() const { return output_; }
private:
char* mem_; // the allocated memory, never nullptr
size_t size_; // the size of the allocated memory, never 0
uint16_t* table_; // the pointer to the hashtable
char* input_; // the pointer to the input scratch buffer
char* output_; // the pointer to the output scratch buffer
// No copying
WorkingMemory(const WorkingMemory&);
void operator=(const WorkingMemory&);
};
// Flat array compression that does not emit the "uncompressed length"
// prefix. Compresses "input" string to the "*op" buffer.
//
// REQUIRES: "input_length <= kBlockSize"
// REQUIRES: "op" points to an array of memory that is at least
// "MaxCompressedLength(input_length)" in size.
// REQUIRES: All elements in "table[0..table_size-1]" are initialized to zero.
// REQUIRES: "table_size" is a power of two
//
// Returns an "end" pointer into "op" buffer.
// "end - op" is the compressed size of "input".
char* CompressFragment(const char* input,
size_t input_length,
char* op,
uint16_t* table,
const int table_size);
// Find the largest n such that
//
// s1[0,n-1] == s2[0,n-1]
// and n <= (s2_limit - s2).
//
// Return make_pair(n, n < 8).
// Does not read *s2_limit or beyond.
// Does not read *(s1 + (s2_limit - s2)) or beyond.
// Requires that s2_limit >= s2.
//
// In addition populate *data with the next 5 bytes from the end of the match.
// This is only done if 8 bytes are available (s2_limit - s2 >= 8). The point is
// that on some arch's this can be done faster in this routine than subsequent
// loading from s2 + n.
//
// Separate implementation for 64-bit, little-endian cpus.
// riscv and little-endian cpu choose this routinue can be done faster too.
#if !SNAPPY_IS_BIG_ENDIAN && \
(defined(__x86_64__) || defined(_M_X64) || defined(ARCH_PPC) || \
defined(ARCH_ARM) || defined(__riscv))
static inline std::pair FindMatchLength(const char* s1,
const char* s2,
const char* s2_limit,
uint64_t* data) {
assert(s2_limit >= s2);
size_t matched = 0;
// This block isn't necessary for correctness; we could just start looping
// immediately. As an optimization though, it is useful. It creates some not
// uncommon code paths that determine, without extra effort, whether the match
// length is less than 8. In short, we are hoping to avoid a conditional
// branch, and perhaps get better code layout from the C++ compiler.
if (SNAPPY_PREDICT_TRUE(s2 <= s2_limit - 16)) {
uint64_t a1 = UNALIGNED_LOAD64(s1);
uint64_t a2 = UNALIGNED_LOAD64(s2);
if (SNAPPY_PREDICT_TRUE(a1 != a2)) {
// This code is critical for performance. The reason is that it determines
// how much to advance `ip` (s2). This obviously depends on both the loads
// from the `candidate` (s1) and `ip`. Furthermore the next `candidate`
// depends on the advanced `ip` calculated here through a load, hash and
// new candidate hash lookup (a lot of cycles). This makes s1 (ie.
// `candidate`) the variable that limits throughput. This is the reason we
// go through hoops to have this function update `data` for the next iter.
// The straightforward code would use *data, given by
//
// *data = UNALIGNED_LOAD64(s2 + matched_bytes) (Latency of 5 cycles),
//
// as input for the hash table lookup to find next candidate. However
// this forces the load on the data dependency chain of s1, because
// matched_bytes directly depends on s1. However matched_bytes is 0..7, so
// we can also calculate *data by
//
// *data = AlignRight(UNALIGNED_LOAD64(s2), UNALIGNED_LOAD64(s2 + 8),
// matched_bytes);
//
// The loads do not depend on s1 anymore and are thus off the bottleneck.
// The straightforward implementation on x86_64 would be to use
//
// shrd rax, rdx, cl (cl being matched_bytes * 8)
//
// unfortunately shrd with a variable shift has a 4 cycle latency. So this
// only wins 1 cycle. The BMI2 shrx instruction is a 1 cycle variable
// shift instruction but can only shift 64 bits. If we focus on just
// obtaining the least significant 4 bytes, we can obtain this by
//
// *data = ConditionalMove(matched_bytes < 4, UNALIGNED_LOAD64(s2),
// UNALIGNED_LOAD64(s2 + 4) >> ((matched_bytes & 3) * 8);
//
// Writen like above this is not a big win, the conditional move would be
// a cmp followed by a cmov (2 cycles) followed by a shift (1 cycle).
// However matched_bytes < 4 is equal to
// static_cast(xorval) != 0. Writen that way, the conditional
// move (2 cycles) can execute in parallel with FindLSBSetNonZero64
// (tzcnt), which takes 3 cycles.
uint64_t xorval = a1 ^ a2;
int shift = Bits::FindLSBSetNonZero64(xorval);
size_t matched_bytes = shift >> 3;
uint64_t a3 = UNALIGNED_LOAD64(s2 + 4);
#ifndef __x86_64__
a2 = static_cast(xorval) == 0 ? a3 : a2;
#else
// Ideally this would just be
//
// a2 = static_cast(xorval) == 0 ? a3 : a2;
//
// However clang correctly infers that the above statement participates on
// a critical data dependency chain and thus, unfortunately, refuses to
// use a conditional move (it's tuned to cut data dependencies). In this
// case there is a longer parallel chain anyway AND this will be fairly
// unpredictable.
asm("testl %k2, %k2\n\t"
"cmovzq %1, %0\n\t"
: "+r"(a2)
: "r"(a3), "r"(xorval)
: "cc");
#endif
*data = a2 >> (shift & (3 * 8));
return std::pair(matched_bytes, true);
} else {
matched = 8;
s2 += 8;
}
}
SNAPPY_PREFETCH(s1 + 64);
SNAPPY_PREFETCH(s2 + 64);
// Find out how long the match is. We loop over the data 64 bits at a
// time until we find a 64-bit block that doesn't match; then we find
// the first non-matching bit and use that to calculate the total
// length of the match.
while (SNAPPY_PREDICT_TRUE(s2 <= s2_limit - 16)) {
uint64_t a1 = UNALIGNED_LOAD64(s1 + matched);
uint64_t a2 = UNALIGNED_LOAD64(s2);
if (a1 == a2) {
s2 += 8;
matched += 8;
} else {
uint64_t xorval = a1 ^ a2;
int shift = Bits::FindLSBSetNonZero64(xorval);
size_t matched_bytes = shift >> 3;
uint64_t a3 = UNALIGNED_LOAD64(s2 + 4);
#ifndef __x86_64__
a2 = static_cast(xorval) == 0 ? a3 : a2;
#else
asm("testl %k2, %k2\n\t"
"cmovzq %1, %0\n\t"
: "+r"(a2)
: "r"(a3), "r"(xorval)
: "cc");
#endif
*data = a2 >> (shift & (3 * 8));
matched += matched_bytes;
assert(matched >= 8);
return std::pair(matched, false);
}
}
while (SNAPPY_PREDICT_TRUE(s2 < s2_limit)) {
if (s1[matched] == *s2) {
++s2;
++matched;
} else {
if (s2 <= s2_limit - 8) {
*data = UNALIGNED_LOAD64(s2);
}
return std::pair(matched, matched < 8);
}
}
return std::pair(matched, matched < 8);
}
#else
static inline std::pair FindMatchLength(const char* s1,
const char* s2,
const char* s2_limit,
uint64_t* data) {
// Implementation based on the x86-64 version, above.
assert(s2_limit >= s2);
int matched = 0;
while (s2 <= s2_limit - 4 &&
UNALIGNED_LOAD32(s2) == UNALIGNED_LOAD32(s1 + matched)) {
s2 += 4;
matched += 4;
}
if (LittleEndian::IsLittleEndian() && s2 <= s2_limit - 4) {
uint32_t x = UNALIGNED_LOAD32(s2) ^ UNALIGNED_LOAD32(s1 + matched);
int matching_bits = Bits::FindLSBSetNonZero(x);
matched += matching_bits >> 3;
s2 += matching_bits >> 3;
} else {
while ((s2 < s2_limit) && (s1[matched] == *s2)) {
++s2;
++matched;
}
}
if (s2 <= s2_limit - 8) *data = LittleEndian::Load64(s2);
return std::pair(matched, matched < 8);
}
#endif
static inline size_t FindMatchLengthPlain(const char* s1, const char* s2,
const char* s2_limit) {
// Implementation based on the x86-64 version, above.
assert(s2_limit >= s2);
int matched = 0;
while (s2 <= s2_limit - 8 &&
UNALIGNED_LOAD64(s2) == UNALIGNED_LOAD64(s1 + matched)) {
s2 += 8;
matched += 8;
}
if (LittleEndian::IsLittleEndian() && s2 <= s2_limit - 8) {
uint64_t x = UNALIGNED_LOAD64(s2) ^ UNALIGNED_LOAD64(s1 + matched);
int matching_bits = Bits::FindLSBSetNonZero64(x);
matched += matching_bits >> 3;
s2 += matching_bits >> 3;
} else {
while ((s2 < s2_limit) && (s1[matched] == *s2)) {
++s2;
++matched;
}
}
return matched;
}
// Lookup tables for decompression code. Give --snappy_dump_decompression_table
// to the unit test to recompute char_table.
enum {
LITERAL = 0,
COPY_1_BYTE_OFFSET = 1, // 3 bit length + 3 bits of offset in opcode
COPY_2_BYTE_OFFSET = 2,
COPY_4_BYTE_OFFSET = 3
};
static const int kMaximumTagLength = 5; // COPY_4_BYTE_OFFSET plus the actual offset.
// Data stored per entry in lookup table:
// Range Bits-used Description
// ------------------------------------
// 1..64 0..7 Literal/copy length encoded in opcode byte
// 0..7 8..10 Copy offset encoded in opcode byte / 256
// 0..4 11..13 Extra bytes after opcode
//
// We use eight bits for the length even though 7 would have sufficed
// because of efficiency reasons:
// (1) Extracting a byte is faster than a bit-field
// (2) It properly aligns copy offset so we do not need a <<8
static constexpr uint16_t char_table[256] = {
// clang-format off
0x0001, 0x0804, 0x1001, 0x2001, 0x0002, 0x0805, 0x1002, 0x2002,
0x0003, 0x0806, 0x1003, 0x2003, 0x0004, 0x0807, 0x1004, 0x2004,
0x0005, 0x0808, 0x1005, 0x2005, 0x0006, 0x0809, 0x1006, 0x2006,
0x0007, 0x080a, 0x1007, 0x2007, 0x0008, 0x080b, 0x1008, 0x2008,
0x0009, 0x0904, 0x1009, 0x2009, 0x000a, 0x0905, 0x100a, 0x200a,
0x000b, 0x0906, 0x100b, 0x200b, 0x000c, 0x0907, 0x100c, 0x200c,
0x000d, 0x0908, 0x100d, 0x200d, 0x000e, 0x0909, 0x100e, 0x200e,
0x000f, 0x090a, 0x100f, 0x200f, 0x0010, 0x090b, 0x1010, 0x2010,
0x0011, 0x0a04, 0x1011, 0x2011, 0x0012, 0x0a05, 0x1012, 0x2012,
0x0013, 0x0a06, 0x1013, 0x2013, 0x0014, 0x0a07, 0x1014, 0x2014,
0x0015, 0x0a08, 0x1015, 0x2015, 0x0016, 0x0a09, 0x1016, 0x2016,
0x0017, 0x0a0a, 0x1017, 0x2017, 0x0018, 0x0a0b, 0x1018, 0x2018,
0x0019, 0x0b04, 0x1019, 0x2019, 0x001a, 0x0b05, 0x101a, 0x201a,
0x001b, 0x0b06, 0x101b, 0x201b, 0x001c, 0x0b07, 0x101c, 0x201c,
0x001d, 0x0b08, 0x101d, 0x201d, 0x001e, 0x0b09, 0x101e, 0x201e,
0x001f, 0x0b0a, 0x101f, 0x201f, 0x0020, 0x0b0b, 0x1020, 0x2020,
0x0021, 0x0c04, 0x1021, 0x2021, 0x0022, 0x0c05, 0x1022, 0x2022,
0x0023, 0x0c06, 0x1023, 0x2023, 0x0024, 0x0c07, 0x1024, 0x2024,
0x0025, 0x0c08, 0x1025, 0x2025, 0x0026, 0x0c09, 0x1026, 0x2026,
0x0027, 0x0c0a, 0x1027, 0x2027, 0x0028, 0x0c0b, 0x1028, 0x2028,
0x0029, 0x0d04, 0x1029, 0x2029, 0x002a, 0x0d05, 0x102a, 0x202a,
0x002b, 0x0d06, 0x102b, 0x202b, 0x002c, 0x0d07, 0x102c, 0x202c,
0x002d, 0x0d08, 0x102d, 0x202d, 0x002e, 0x0d09, 0x102e, 0x202e,
0x002f, 0x0d0a, 0x102f, 0x202f, 0x0030, 0x0d0b, 0x1030, 0x2030,
0x0031, 0x0e04, 0x1031, 0x2031, 0x0032, 0x0e05, 0x1032, 0x2032,
0x0033, 0x0e06, 0x1033, 0x2033, 0x0034, 0x0e07, 0x1034, 0x2034,
0x0035, 0x0e08, 0x1035, 0x2035, 0x0036, 0x0e09, 0x1036, 0x2036,
0x0037, 0x0e0a, 0x1037, 0x2037, 0x0038, 0x0e0b, 0x1038, 0x2038,
0x0039, 0x0f04, 0x1039, 0x2039, 0x003a, 0x0f05, 0x103a, 0x203a,
0x003b, 0x0f06, 0x103b, 0x203b, 0x003c, 0x0f07, 0x103c, 0x203c,
0x0801, 0x0f08, 0x103d, 0x203d, 0x1001, 0x0f09, 0x103e, 0x203e,
0x1801, 0x0f0a, 0x103f, 0x203f, 0x2001, 0x0f0b, 0x1040, 0x2040,
// clang-format on
};
} // end namespace internal
} // end namespace snappy
#endif // THIRD_PARTY_SNAPPY_SNAPPY_INTERNAL_H_
================================================
FILE: Plugin/Snappy/snappy-sinksource.cc
================================================
// Copyright 2011 Google Inc. All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include
#include
#include "snappy-sinksource.h"
namespace snappy {
Source::~Source() = default;
Sink::~Sink() = default;
char* Sink::GetAppendBuffer(size_t length, char* scratch) {
// TODO: Switch to [[maybe_unused]] when we can assume C++17.
(void)length;
return scratch;
}
char* Sink::GetAppendBufferVariable(
size_t min_size, size_t desired_size_hint, char* scratch,
size_t scratch_size, size_t* allocated_size) {
// TODO: Switch to [[maybe_unused]] when we can assume C++17.
(void)min_size;
(void)desired_size_hint;
*allocated_size = scratch_size;
return scratch;
}
void Sink::AppendAndTakeOwnership(
char* bytes, size_t n,
void (*deleter)(void*, const char*, size_t),
void *deleter_arg) {
Append(bytes, n);
(*deleter)(deleter_arg, bytes, n);
}
ByteArraySource::~ByteArraySource() = default;
size_t ByteArraySource::Available() const { return left_; }
const char* ByteArraySource::Peek(size_t* len) {
*len = left_;
return ptr_;
}
void ByteArraySource::Skip(size_t n) {
left_ -= n;
ptr_ += n;
}
UncheckedByteArraySink::~UncheckedByteArraySink() { }
void UncheckedByteArraySink::Append(const char* data, size_t n) {
// Do no copying if the caller filled in the result of GetAppendBuffer()
if (data != dest_) {
std::memcpy(dest_, data, n);
}
dest_ += n;
}
char* UncheckedByteArraySink::GetAppendBuffer(size_t len, char* scratch) {
// TODO: Switch to [[maybe_unused]] when we can assume C++17.
(void)len;
(void)scratch;
return dest_;
}
void UncheckedByteArraySink::AppendAndTakeOwnership(
char* bytes, size_t n,
void (*deleter)(void*, const char*, size_t),
void *deleter_arg) {
if (bytes != dest_) {
std::memcpy(dest_, bytes, n);
(*deleter)(deleter_arg, bytes, n);
}
dest_ += n;
}
char* UncheckedByteArraySink::GetAppendBufferVariable(
size_t min_size, size_t desired_size_hint, char* scratch,
size_t scratch_size, size_t* allocated_size) {
// TODO: Switch to [[maybe_unused]] when we can assume C++17.
(void)min_size;
(void)scratch;
(void)scratch_size;
*allocated_size = desired_size_hint;
return dest_;
}
} // namespace snappy
================================================
FILE: Plugin/Snappy/snappy-sinksource.h
================================================
// Copyright 2011 Google Inc. All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef THIRD_PARTY_SNAPPY_SNAPPY_SINKSOURCE_H_
#define THIRD_PARTY_SNAPPY_SNAPPY_SINKSOURCE_H_
#include
namespace snappy {
// A Sink is an interface that consumes a sequence of bytes.
class Sink {
public:
Sink() { }
virtual ~Sink();
// Append "bytes[0,n-1]" to this.
virtual void Append(const char* bytes, size_t n) = 0;
// Returns a writable buffer of the specified length for appending.
// May return a pointer to the caller-owned scratch buffer which
// must have at least the indicated length. The returned buffer is
// only valid until the next operation on this Sink.
//
// After writing at most "length" bytes, call Append() with the
// pointer returned from this function and the number of bytes
// written. Many Append() implementations will avoid copying
// bytes if this function returned an internal buffer.
//
// If a non-scratch buffer is returned, the caller may only pass a
// prefix of it to Append(). That is, it is not correct to pass an
// interior pointer of the returned array to Append().
//
// The default implementation always returns the scratch buffer.
virtual char* GetAppendBuffer(size_t length, char* scratch);
// For higher performance, Sink implementations can provide custom
// AppendAndTakeOwnership() and GetAppendBufferVariable() methods.
// These methods can reduce the number of copies done during
// compression/decompression.
// Append "bytes[0,n-1] to the sink. Takes ownership of "bytes"
// and calls the deleter function as (*deleter)(deleter_arg, bytes, n)
// to free the buffer. deleter function must be non NULL.
//
// The default implementation just calls Append and frees "bytes".
// Other implementations may avoid a copy while appending the buffer.
virtual void AppendAndTakeOwnership(
char* bytes, size_t n, void (*deleter)(void*, const char*, size_t),
void *deleter_arg);
// Returns a writable buffer for appending and writes the buffer's capacity to
// *allocated_size. Guarantees *allocated_size >= min_size.
// May return a pointer to the caller-owned scratch buffer which must have
// scratch_size >= min_size.
//
// The returned buffer is only valid until the next operation
// on this ByteSink.
//
// After writing at most *allocated_size bytes, call Append() with the
// pointer returned from this function and the number of bytes written.
// Many Append() implementations will avoid copying bytes if this function
// returned an internal buffer.
//
// If the sink implementation allocates or reallocates an internal buffer,
// it should use the desired_size_hint if appropriate. If a caller cannot
// provide a reasonable guess at the desired capacity, it should set
// desired_size_hint = 0.
//
// If a non-scratch buffer is returned, the caller may only pass
// a prefix to it to Append(). That is, it is not correct to pass an
// interior pointer to Append().
//
// The default implementation always returns the scratch buffer.
virtual char* GetAppendBufferVariable(
size_t min_size, size_t desired_size_hint, char* scratch,
size_t scratch_size, size_t* allocated_size);
private:
// No copying
Sink(const Sink&);
void operator=(const Sink&);
};
// A Source is an interface that yields a sequence of bytes
class Source {
public:
Source() { }
virtual ~Source();
// Return the number of bytes left to read from the source
virtual size_t Available() const = 0;
// Peek at the next flat region of the source. Does not reposition
// the source. The returned region is empty iff Available()==0.
//
// Returns a pointer to the beginning of the region and store its
// length in *len.
//
// The returned region is valid until the next call to Skip() or
// until this object is destroyed, whichever occurs first.
//
// The returned region may be larger than Available() (for example
// if this ByteSource is a view on a substring of a larger source).
// The caller is responsible for ensuring that it only reads the
// Available() bytes.
virtual const char* Peek(size_t* len) = 0;
// Skip the next n bytes. Invalidates any buffer returned by
// a previous call to Peek().
// REQUIRES: Available() >= n
virtual void Skip(size_t n) = 0;
private:
// No copying
Source(const Source&);
void operator=(const Source&);
};
// A Source implementation that yields the contents of a flat array
class ByteArraySource : public Source {
public:
ByteArraySource(const char* p, size_t n) : ptr_(p), left_(n) { }
~ByteArraySource() override;
size_t Available() const override;
const char* Peek(size_t* len) override;
void Skip(size_t n) override;
private:
const char* ptr_;
size_t left_;
};
// A Sink implementation that writes to a flat array without any bound checks.
class UncheckedByteArraySink : public Sink {
public:
explicit UncheckedByteArraySink(char* dest) : dest_(dest) { }
~UncheckedByteArraySink() override;
void Append(const char* data, size_t n) override;
char* GetAppendBuffer(size_t len, char* scratch) override;
char* GetAppendBufferVariable(
size_t min_size, size_t desired_size_hint, char* scratch,
size_t scratch_size, size_t* allocated_size) override;
void AppendAndTakeOwnership(
char* bytes, size_t n, void (*deleter)(void*, const char*, size_t),
void *deleter_arg) override;
// Return the current output pointer so that a caller can see how
// many bytes were produced.
// Note: this is not a Sink method.
char* CurrentDestination() const { return dest_; }
private:
char* dest_;
};
} // namespace snappy
#endif // THIRD_PARTY_SNAPPY_SNAPPY_SINKSOURCE_H_
================================================
FILE: Plugin/Snappy/snappy-stubs-internal.cc
================================================
// Copyright 2011 Google Inc. All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include
#include
#include "snappy-stubs-internal.h"
namespace snappy {
void Varint::Append32(std::string* s, uint32_t value) {
char buf[Varint::kMax32];
const char* p = Varint::Encode32(buf, value);
s->append(buf, p - buf);
}
} // namespace snappy
================================================
FILE: Plugin/Snappy/snappy-stubs-internal.h
================================================
// Copyright 2011 Google Inc. All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Various stubs for the open-source version of Snappy.
#ifndef THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_INTERNAL_H_
#define THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_INTERNAL_H_
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include
#include
#include
#include
#include
#include
#if HAVE_SYS_MMAN_H
#include
#endif
#if HAVE_UNISTD_H
#include
#endif
#if defined(_MSC_VER)
#include
#endif // defined(_MSC_VER)
#ifndef __has_feature
#define __has_feature(x) 0
#endif
#if __has_feature(memory_sanitizer)
#include
#define SNAPPY_ANNOTATE_MEMORY_IS_INITIALIZED(address, size) \
__msan_unpoison((address), (size))
#else
#define SNAPPY_ANNOTATE_MEMORY_IS_INITIALIZED(address, size) /* empty */
#endif // __has_feature(memory_sanitizer)
#include "snappy-stubs-public.h"
// Used to enable 64-bit optimized versions of some routines.
#if defined(__PPC64__) || defined(__powerpc64__)
#define ARCH_PPC 1
#elif defined(__aarch64__) || defined(_M_ARM64)
#define ARCH_ARM 1
#endif
// Needed by OS X, among others.
#ifndef MAP_ANONYMOUS
#define MAP_ANONYMOUS MAP_ANON
#endif
// The size of an array, if known at compile-time.
// Will give unexpected results if used on a pointer.
// We undefine it first, since some compilers already have a definition.
#ifdef ARRAYSIZE
#undef ARRAYSIZE
#endif
#define ARRAYSIZE(a) int{sizeof(a) / sizeof(*(a))}
// Static prediction hints.
#if HAVE_BUILTIN_EXPECT
#define SNAPPY_PREDICT_FALSE(x) (__builtin_expect(x, 0))
#define SNAPPY_PREDICT_TRUE(x) (__builtin_expect(!!(x), 1))
#else
#define SNAPPY_PREDICT_FALSE(x) x
#define SNAPPY_PREDICT_TRUE(x) x
#endif // HAVE_BUILTIN_EXPECT
// Inlining hints.
#if HAVE_ATTRIBUTE_ALWAYS_INLINE
#define SNAPPY_ATTRIBUTE_ALWAYS_INLINE __attribute__((always_inline))
#else
#define SNAPPY_ATTRIBUTE_ALWAYS_INLINE
#endif // HAVE_ATTRIBUTE_ALWAYS_INLINE
#if HAVE_BUILTIN_PREFETCH
#define SNAPPY_PREFETCH(ptr) __builtin_prefetch(ptr, 0, 3)
#else
#define SNAPPY_PREFETCH(ptr) (void)(ptr)
#endif
// Stubbed version of ABSL_FLAG.
//
// In the open source version, flags can only be changed at compile time.
#define SNAPPY_FLAG(flag_type, flag_name, default_value, help) \
flag_type FLAGS_ ## flag_name = default_value
namespace snappy {
// Stubbed version of absl::GetFlag().
template
inline T GetFlag(T flag) { return flag; }
static const uint32_t kuint32max = std::numeric_limits::max();
static const int64_t kint64max = std::numeric_limits::max();
// Potentially unaligned loads and stores.
inline uint16_t UNALIGNED_LOAD16(const void *p) {
// Compiles to a single movzx/ldrh on clang/gcc/msvc.
uint16_t v;
std::memcpy(&v, p, sizeof(v));
return v;
}
inline uint32_t UNALIGNED_LOAD32(const void *p) {
// Compiles to a single mov/ldr on clang/gcc/msvc.
uint32_t v;
std::memcpy(&v, p, sizeof(v));
return v;
}
inline uint64_t UNALIGNED_LOAD64(const void *p) {
// Compiles to a single mov/ldr on clang/gcc/msvc.
uint64_t v;
std::memcpy(&v, p, sizeof(v));
return v;
}
inline void UNALIGNED_STORE16(void *p, uint16_t v) {
// Compiles to a single mov/strh on clang/gcc/msvc.
std::memcpy(p, &v, sizeof(v));
}
inline void UNALIGNED_STORE32(void *p, uint32_t v) {
// Compiles to a single mov/str on clang/gcc/msvc.
std::memcpy(p, &v, sizeof(v));
}
inline void UNALIGNED_STORE64(void *p, uint64_t v) {
// Compiles to a single mov/str on clang/gcc/msvc.
std::memcpy(p, &v, sizeof(v));
}
// Convert to little-endian storage, opposite of network format.
// Convert x from host to little endian: x = LittleEndian.FromHost(x);
// convert x from little endian to host: x = LittleEndian.ToHost(x);
//
// Store values into unaligned memory converting to little endian order:
// LittleEndian.Store16(p, x);
//
// Load unaligned values stored in little endian converting to host order:
// x = LittleEndian.Load16(p);
class LittleEndian {
public:
// Functions to do unaligned loads and stores in little-endian order.
static inline uint16_t Load16(const void *ptr) {
// Compiles to a single mov/str on recent clang and gcc.
#if SNAPPY_IS_BIG_ENDIAN
const uint8_t* const buffer = reinterpret_cast(ptr);
return (static_cast(buffer[0])) |
(static_cast(buffer[1]) << 8);
#else
// memcpy() turns into a single instruction early in the optimization
// pipeline (relatively to a series of byte accesses). So, using memcpy
// instead of byte accesses may lead to better decisions in more stages of
// the optimization pipeline.
uint16_t value;
std::memcpy(&value, ptr, 2);
return value;
#endif
}
static inline uint32_t Load32(const void *ptr) {
// Compiles to a single mov/str on recent clang and gcc.
#if SNAPPY_IS_BIG_ENDIAN
const uint8_t* const buffer = reinterpret_cast(ptr);
return (static_cast(buffer[0])) |
(static_cast(buffer[1]) << 8) |
(static_cast(buffer[2]) << 16) |
(static_cast(buffer[3]) << 24);
#else
// See Load16() for the rationale of using memcpy().
uint32_t value;
std::memcpy(&value, ptr, 4);
return value;
#endif
}
static inline uint64_t Load64(const void *ptr) {
// Compiles to a single mov/str on recent clang and gcc.
#if SNAPPY_IS_BIG_ENDIAN
const uint8_t* const buffer = reinterpret_cast(ptr);
return (static_cast(buffer[0])) |
(static_cast(buffer[1]) << 8) |
(static_cast(buffer[2]) << 16) |
(static_cast(buffer[3]) << 24) |
(static_cast(buffer[4]) << 32) |
(static_cast(buffer[5]) << 40) |
(static_cast(buffer[6]) << 48) |
(static_cast(buffer[7]) << 56);
#else
// See Load16() for the rationale of using memcpy().
uint64_t value;
std::memcpy(&value, ptr, 8);
return value;
#endif
}
static inline void Store16(void *dst, uint16_t value) {
// Compiles to a single mov/str on recent clang and gcc.
#if SNAPPY_IS_BIG_ENDIAN
uint8_t* const buffer = reinterpret_cast(dst);
buffer[0] = static_cast(value);
buffer[1] = static_cast(value >> 8);
#else
// See Load16() for the rationale of using memcpy().
std::memcpy(dst, &value, 2);
#endif
}
static void Store32(void *dst, uint32_t value) {
// Compiles to a single mov/str on recent clang and gcc.
#if SNAPPY_IS_BIG_ENDIAN
uint8_t* const buffer = reinterpret_cast(dst);
buffer[0] = static_cast(value);
buffer[1] = static_cast(value >> 8);
buffer[2] = static_cast(value >> 16);
buffer[3] = static_cast(value >> 24);
#else
// See Load16() for the rationale of using memcpy().
std::memcpy(dst, &value, 4);
#endif
}
static void Store64(void* dst, uint64_t value) {
// Compiles to a single mov/str on recent clang and gcc.
#if SNAPPY_IS_BIG_ENDIAN
uint8_t* const buffer = reinterpret_cast(dst);
buffer[0] = static_cast(value);
buffer[1] = static_cast(value >> 8);
buffer[2] = static_cast(value >> 16);
buffer[3] = static_cast(value >> 24);
buffer[4] = static_cast(value >> 32);
buffer[5] = static_cast(value >> 40);
buffer[6] = static_cast(value >> 48);
buffer[7] = static_cast(value >> 56);
#else
// See Load16() for the rationale of using memcpy().
std::memcpy(dst, &value, 8);
#endif
}
static inline constexpr bool IsLittleEndian() {
#if SNAPPY_IS_BIG_ENDIAN
return false;
#else
return true;
#endif // SNAPPY_IS_BIG_ENDIAN
}
};
// Some bit-manipulation functions.
class Bits {
public:
// Return floor(log2(n)) for positive integer n.
static int Log2FloorNonZero(uint32_t n);
// Return floor(log2(n)) for positive integer n. Returns -1 iff n == 0.
static int Log2Floor(uint32_t n);
// Return the first set least / most significant bit, 0-indexed. Returns an
// undefined value if n == 0. FindLSBSetNonZero() is similar to ffs() except
// that it's 0-indexed.
static int FindLSBSetNonZero(uint32_t n);
static int FindLSBSetNonZero64(uint64_t n);
private:
// No copying
Bits(const Bits&);
void operator=(const Bits&);
};
// In RISC-V, CLZ is supported by instructions from the ZBB bit-manipulation extension.
#if HAVE_BUILTIN_CTZ
inline int Bits::Log2FloorNonZero(uint32_t n) {
assert(n != 0);
// (31 ^ x) is equivalent to (31 - x) for x in [0, 31]. An easy proof
// represents subtraction in base 2 and observes that there's no carry.
//
// GCC and Clang represent __builtin_clz on x86 as 31 ^ _bit_scan_reverse(x).
// Using "31 ^" here instead of "31 -" allows the optimizer to strip the
// function body down to _bit_scan_reverse(x).
return 31 ^ __builtin_clz(n);
}
inline int Bits::Log2Floor(uint32_t n) {
return (n == 0) ? -1 : Bits::Log2FloorNonZero(n);
}
inline int Bits::FindLSBSetNonZero(uint32_t n) {
assert(n != 0);
return __builtin_ctz(n);
}
#elif defined(_MSC_VER)
inline int Bits::Log2FloorNonZero(uint32_t n) {
assert(n != 0);
// NOLINTNEXTLINE(runtime/int): The MSVC intrinsic demands unsigned long.
unsigned long where;
_BitScanReverse(&where, n);
return static_cast(where);
}
inline int Bits::Log2Floor(uint32_t n) {
// NOLINTNEXTLINE(runtime/int): The MSVC intrinsic demands unsigned long.
unsigned long where;
if (_BitScanReverse(&where, n))
return static_cast(where);
return -1;
}
inline int Bits::FindLSBSetNonZero(uint32_t n) {
assert(n != 0);
// NOLINTNEXTLINE(runtime/int): The MSVC intrinsic demands unsigned long.
unsigned long where;
if (_BitScanForward(&where, n))
return static_cast(where);
return 32;
}
#else // Portable versions.
inline int Bits::Log2FloorNonZero(uint32_t n) {
assert(n != 0);
int log = 0;
uint32_t value = n;
for (int i = 4; i >= 0; --i) {
int shift = (1 << i);
uint32_t x = value >> shift;
if (x != 0) {
value = x;
log += shift;
}
}
assert(value == 1);
return log;
}
inline int Bits::Log2Floor(uint32_t n) {
return (n == 0) ? -1 : Bits::Log2FloorNonZero(n);
}
inline int Bits::FindLSBSetNonZero(uint32_t n) {
assert(n != 0);
int rc = 31;
for (int i = 4, shift = 1 << 4; i >= 0; --i) {
const uint32_t x = n << shift;
if (x != 0) {
n = x;
rc -= shift;
}
shift >>= 1;
}
return rc;
}
#endif // End portable versions.
// In RISC-V, CLZ is supported by instructions from the ZBB bit-manipulation extension.
#if HAVE_BUILTIN_CTZ
inline int Bits::FindLSBSetNonZero64(uint64_t n) {
assert(n != 0);
return __builtin_ctzll(n);
}
#elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64))
// _BitScanForward64() is only available on x64 and ARM64.
inline int Bits::FindLSBSetNonZero64(uint64_t n) {
assert(n != 0);
// NOLINTNEXTLINE(runtime/int): The MSVC intrinsic demands unsigned long.
unsigned long where;
if (_BitScanForward64(&where, n))
return static_cast(where);
return 64;
}
#else // Portable version.
// FindLSBSetNonZero64() is defined in terms of FindLSBSetNonZero().
inline int Bits::FindLSBSetNonZero64(uint64_t n) {
assert(n != 0);
const uint32_t bottombits = static_cast(n);
if (bottombits == 0) {
// Bottom bits are zero, so scan the top bits.
return 32 + FindLSBSetNonZero(static_cast(n >> 32));
} else {
return FindLSBSetNonZero(bottombits);
}
}
#endif // HAVE_BUILTIN_CTZ
// Variable-length integer encoding.
class Varint {
public:
// Maximum lengths of varint encoding of uint32_t.
static const int kMax32 = 5;
// Attempts to parse a varint32 from a prefix of the bytes in [ptr,limit-1].
// Never reads a character at or beyond limit. If a valid/terminated varint32
// was found in the range, stores it in *OUTPUT and returns a pointer just
// past the last byte of the varint32. Else returns NULL. On success,
// "result <= limit".
static const char* Parse32WithLimit(const char* ptr, const char* limit,
uint32_t* OUTPUT);
// REQUIRES "ptr" points to a buffer of length sufficient to hold "v".
// EFFECTS Encodes "v" into "ptr" and returns a pointer to the
// byte just past the last encoded byte.
static char* Encode32(char* ptr, uint32_t v);
// EFFECTS Appends the varint representation of "value" to "*s".
static void Append32(std::string* s, uint32_t value);
};
inline const char* Varint::Parse32WithLimit(const char* p,
const char* l,
uint32_t* OUTPUT) {
const unsigned char* ptr = reinterpret_cast(p);
const unsigned char* limit = reinterpret_cast(l);
uint32_t b, result;
if (ptr >= limit) return NULL;
b = *(ptr++); result = b & 127; if (b < 128) goto done;
if (ptr >= limit) return NULL;
b = *(ptr++); result |= (b & 127) << 7; if (b < 128) goto done;
if (ptr >= limit) return NULL;
b = *(ptr++); result |= (b & 127) << 14; if (b < 128) goto done;
if (ptr >= limit) return NULL;
b = *(ptr++); result |= (b & 127) << 21; if (b < 128) goto done;
if (ptr >= limit) return NULL;
b = *(ptr++); result |= (b & 127) << 28; if (b < 16) goto done;
return NULL; // Value is too long to be a varint32
done:
*OUTPUT = result;
return reinterpret_cast(ptr);
}
inline char* Varint::Encode32(char* sptr, uint32_t v) {
// Operate on characters as unsigneds
uint8_t* ptr = reinterpret_cast(sptr);
static const uint8_t B = 128;
if (v < (1 << 7)) {
*(ptr++) = static_cast(v);
} else if (v < (1 << 14)) {
*(ptr++) = static_cast(v | B);
*(ptr++) = static_cast(v >> 7);
} else if (v < (1 << 21)) {
*(ptr++) = static_cast(v | B);
*(ptr++) = static_cast((v >> 7) | B);
*(ptr++) = static_cast(v >> 14);
} else if (v < (1 << 28)) {
*(ptr++) = static_cast(v | B);
*(ptr++) = static_cast((v >> 7) | B);
*(ptr++) = static_cast((v >> 14) | B);
*(ptr++) = static_cast(v >> 21);
} else {
*(ptr++) = static_cast(v | B);
*(ptr++) = static_cast((v>>7) | B);
*(ptr++) = static_cast((v>>14) | B);
*(ptr++) = static_cast((v>>21) | B);
*(ptr++) = static_cast(v >> 28);
}
return reinterpret_cast(ptr);
}
// If you know the internal layout of the std::string in use, you can
// replace this function with one that resizes the string without
// filling the new space with zeros (if applicable) --
// it will be non-portable but faster.
inline void STLStringResizeUninitialized(std::string* s, size_t new_size) {
s->resize(new_size);
}
// Return a mutable char* pointing to a string's internal buffer,
// which may not be null-terminated. Writing through this pointer will
// modify the string.
//
// string_as_array(&str)[i] is valid for 0 <= i < str.size() until the
// next call to a string method that invalidates iterators.
//
// As of 2006-04, there is no standard-blessed way of getting a
// mutable reference to a string's internal buffer. However, issue 530
// (http://www.open-std.org/JTC1/SC22/WG21/docs/lwg-defects.html#530)
// proposes this as the method. It will officially be part of the standard
// for C++0x. This should already work on all current implementations.
inline char* string_as_array(std::string* str) {
return str->empty() ? NULL : &*str->begin();
}
} // namespace snappy
#endif // THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_INTERNAL_H_
================================================
FILE: Plugin/Snappy/snappy-stubs-public.h
================================================
// Copyright 2011 Google Inc. All Rights Reserved.
// Author: sesse@google.com (Steinar H. Gunderson)
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Various type stubs for the open-source version of Snappy.
//
// This file cannot include config.h, as it is included from snappy.h,
// which is a public header. Instead, snappy-stubs-public.h is generated by
// from snappy-stubs-public.h.in at configure time.
#ifndef THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_
#define THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_
#include
#include
#include
#if 0 // HAVE_SYS_UIO_H
#include
#endif // HAVE_SYS_UIO_H
#define SNAPPY_MAJOR 1
#define SNAPPY_MINOR 1
#define SNAPPY_PATCHLEVEL 7
#define SNAPPY_VERSION \
((SNAPPY_MAJOR << 16) | (SNAPPY_MINOR << 8) | SNAPPY_PATCHLEVEL)
namespace snappy {
using int8 = std::int8_t;
using uint8 = std::uint8_t;
using int16 = std::int16_t;
using uint16 = std::uint16_t;
using int32 = std::int32_t;
using uint32 = std::uint32_t;
using int64 = std::int64_t;
using uint64 = std::uint64_t;
using string = std::string;
#if 1 // !HAVE_SYS_UIO_H
// Windows does not have an iovec type, yet the concept is universally useful.
// It is simple to define it ourselves, so we put it inside our own namespace.
struct iovec {
void* iov_base;
size_t iov_len;
};
#endif // !HAVE_SYS_UIO_H
} // namespace snappy
#endif // THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_
================================================
FILE: Plugin/Snappy/snappy.cc
================================================
// Copyright 2005 Google Inc. All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "snappy-internal.h"
#include "snappy-sinksource.h"
#include "snappy.h"
#if !defined(SNAPPY_HAVE_BMI2)
// __BMI2__ is defined by GCC and Clang. Visual Studio doesn't target BMI2
// specifically, but it does define __AVX2__ when AVX2 support is available.
// Fortunately, AVX2 was introduced in Haswell, just like BMI2.
//
// BMI2 is not defined as a subset of AVX2 (unlike SSSE3 and AVX above). So,
// GCC and Clang can build code with AVX2 enabled but BMI2 disabled, in which
// case issuing BMI2 instructions results in a compiler error.
#if defined(__BMI2__) || (defined(_MSC_VER) && defined(__AVX2__))
#define SNAPPY_HAVE_BMI2 1
#else
#define SNAPPY_HAVE_BMI2 0
#endif
#endif // !defined(SNAPPY_HAVE_BMI2)
#if !defined(SNAPPY_HAVE_X86_CRC32)
#if defined(__SSE4_2__)
#define SNAPPY_HAVE_X86_CRC32 1
#else
#define SNAPPY_HAVE_X86_CRC32 0
#endif
#endif // !defined(SNAPPY_HAVE_X86_CRC32)
#if !defined(SNAPPY_HAVE_NEON_CRC32)
#if SNAPPY_HAVE_NEON && defined(__ARM_FEATURE_CRC32)
#define SNAPPY_HAVE_NEON_CRC32 1
#else
#define SNAPPY_HAVE_NEON_CRC32 0
#endif
#endif // !defined(SNAPPY_HAVE_NEON_CRC32)
#if SNAPPY_HAVE_BMI2 || SNAPPY_HAVE_X86_CRC32
// Please do not replace with . or with headers that assume more
// advanced SSE versions without checking with all the OWNERS.
#include
#elif SNAPPY_HAVE_NEON_CRC32
#include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace snappy {
namespace {
// The amount of slop bytes writers are using for unconditional copies.
constexpr int kSlopBytes = 64;
using internal::char_table;
using internal::COPY_1_BYTE_OFFSET;
using internal::COPY_2_BYTE_OFFSET;
using internal::COPY_4_BYTE_OFFSET;
using internal::kMaximumTagLength;
using internal::LITERAL;
#if SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE
using internal::V128;
using internal::V128_Load;
using internal::V128_LoadU;
using internal::V128_Shuffle;
using internal::V128_StoreU;
using internal::V128_DupChar;
#endif
// We translate the information encoded in a tag through a lookup table to a
// format that requires fewer instructions to decode. Effectively we store
// the length minus the tag part of the offset. The lowest significant byte
// thus stores the length. While total length - offset is given by
// entry - ExtractOffset(type). The nice thing is that the subtraction
// immediately sets the flags for the necessary check that offset >= length.
// This folds the cmp with sub. We engineer the long literals and copy-4 to
// always fail this check, so their presence doesn't affect the fast path.
// To prevent literals from triggering the guard against offset < length (offset
// does not apply to literals) the table is giving them a spurious offset of
// 256.
inline constexpr int16_t MakeEntry(int16_t len, int16_t offset) {
return len - (offset << 8);
}
inline constexpr int16_t LengthMinusOffset(int data, int type) {
return type == 3 ? 0xFF // copy-4 (or type == 3)
: type == 2 ? MakeEntry(data + 1, 0) // copy-2
: type == 1 ? MakeEntry((data & 7) + 4, data >> 3) // copy-1
: data < 60 ? MakeEntry(data + 1, 1) // note spurious offset.
: 0xFF; // long literal
}
inline constexpr int16_t LengthMinusOffset(uint8_t tag) {
return LengthMinusOffset(tag >> 2, tag & 3);
}
template
struct index_sequence {};
template
struct make_index_sequence : make_index_sequence {};
template
struct make_index_sequence<0, Is...> : index_sequence {};
template
constexpr std::array MakeTable(index_sequence) {
return std::array{LengthMinusOffset(seq)...};
}
alignas(64) const std::array kLengthMinusOffset =
MakeTable(make_index_sequence<256>{});
// Given a table of uint16_t whose size is mask / 2 + 1, return a pointer to the
// relevant entry, if any, for the given bytes. Any hash function will do,
// but a good hash function reduces the number of collisions and thus yields
// better compression for compressible input.
//
// REQUIRES: mask is 2 * (table_size - 1), and table_size is a power of two.
inline uint16_t* TableEntry(uint16_t* table, uint32_t bytes, uint32_t mask) {
// Our choice is quicker-and-dirtier than the typical hash function;
// empirically, that seems beneficial. The upper bits of kMagic * bytes are a
// higher-quality hash than the lower bits, so when using kMagic * bytes we
// also shift right to get a higher-quality end result. There's no similar
// issue with a CRC because all of the output bits of a CRC are equally good
// "hashes." So, a CPU instruction for CRC, if available, tends to be a good
// choice.
#if SNAPPY_HAVE_NEON_CRC32
// We use mask as the second arg to the CRC function, as it's about to
// be used anyway; it'd be equally correct to use 0 or some constant.
// Mathematically, _mm_crc32_u32 (or similar) is a function of the
// xor of its arguments.
const uint32_t hash = __crc32cw(bytes, mask);
#elif SNAPPY_HAVE_X86_CRC32
const uint32_t hash = _mm_crc32_u32(bytes, mask);
#else
constexpr uint32_t kMagic = 0x1e35a7bd;
const uint32_t hash = (kMagic * bytes) >> (31 - kMaxHashTableBits);
#endif
return reinterpret_cast(reinterpret_cast(table) +
(hash & mask));
}
inline uint16_t* TableEntry4ByteMatch(uint16_t* table, uint32_t bytes,
uint32_t mask) {
constexpr uint32_t kMagic = 2654435761U;
const uint32_t hash = (kMagic * bytes) >> (32 - kMaxHashTableBits);
return reinterpret_cast(reinterpret_cast(table) +
(hash & mask));
}
inline uint16_t* TableEntry8ByteMatch(uint16_t* table, uint64_t bytes,
uint32_t mask) {
constexpr uint64_t kMagic = 58295818150454627ULL;
const uint32_t hash = (kMagic * bytes) >> (64 - kMaxHashTableBits);
return reinterpret_cast(reinterpret_cast(table) +
(hash & mask));
}
} // namespace
size_t MaxCompressedLength(size_t source_bytes) {
// Compressed data can be defined as:
// compressed := item* literal*
// item := literal* copy
//
// The trailing literal sequence has a space blowup of at most 62/60
// since a literal of length 60 needs one tag byte + one extra byte
// for length information.
//
// Item blowup is trickier to measure. Suppose the "copy" op copies
// 4 bytes of data. Because of a special check in the encoding code,
// we produce a 4-byte copy only if the offset is < 65536. Therefore
// the copy op takes 3 bytes to encode, and this type of item leads
// to at most the 62/60 blowup for representing literals.
//
// Suppose the "copy" op copies 5 bytes of data. If the offset is big
// enough, it will take 5 bytes to encode the copy op. Therefore the
// worst case here is a one-byte literal followed by a five-byte copy.
// I.e., 6 bytes of input turn into 7 bytes of "compressed" data.
//
// This last factor dominates the blowup, so the final estimate is:
return 32 + source_bytes + source_bytes / 6;
}
namespace {
void UnalignedCopy64(const void* src, void* dst) {
char tmp[8];
std::memcpy(tmp, src, 8);
std::memcpy(dst, tmp, 8);
}
void UnalignedCopy128(const void* src, void* dst) {
// std::memcpy() gets vectorized when the appropriate compiler options are
// used. For example, x86 compilers targeting SSE2+ will optimize to an SSE2
// load and store.
char tmp[16];
std::memcpy(tmp, src, 16);
std::memcpy(dst, tmp, 16);
}
template
inline void ConditionalUnalignedCopy128(const char* src, char* dst) {
if (use_16bytes_chunk) {
UnalignedCopy128(src, dst);
} else {
UnalignedCopy64(src, dst);
UnalignedCopy64(src + 8, dst + 8);
}
}
// Copy [src, src+(op_limit-op)) to [op, (op_limit-op)) a byte at a time. Used
// for handling COPY operations where the input and output regions may overlap.
// For example, suppose:
// src == "ab"
// op == src + 2
// op_limit == op + 20
// After IncrementalCopySlow(src, op, op_limit), the result will have eleven
// copies of "ab"
// ababababababababababab
// Note that this does not match the semantics of either std::memcpy() or
// std::memmove().
inline char* IncrementalCopySlow(const char* src, char* op,
char* const op_limit) {
// TODO: Remove pragma when LLVM is aware this
// function is only called in cold regions and when cold regions don't get
// vectorized or unrolled.
#ifdef __clang__
#pragma clang loop unroll(disable)
#endif
while (op < op_limit) {
*op++ = *src++;
}
return op_limit;
}
#if SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE
// Computes the bytes for shuffle control mask (please read comments on
// 'pattern_generation_masks' as well) for the given index_offset and
// pattern_size. For example, when the 'offset' is 6, it will generate a
// repeating pattern of size 6. So, the first 16 byte indexes will correspond to
// the pattern-bytes {0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3} and the
// next 16 byte indexes will correspond to the pattern-bytes {4, 5, 0, 1, 2, 3,
// 4, 5, 0, 1, 2, 3, 4, 5, 0, 1}. These byte index sequences are generated by
// calling MakePatternMaskBytes(0, 6, index_sequence<16>()) and
// MakePatternMaskBytes(16, 6, index_sequence<16>()) respectively.
template
inline constexpr std::array MakePatternMaskBytes(
int index_offset, int pattern_size, index_sequence) {
return {static_cast((index_offset + indexes) % pattern_size)...};
}
// Computes the shuffle control mask bytes array for given pattern-sizes and
// returns an array.
template
inline constexpr std::array,
sizeof...(pattern_sizes_minus_one)>
MakePatternMaskBytesTable(int index_offset,
index_sequence) {
return {
MakePatternMaskBytes(index_offset, pattern_sizes_minus_one + 1,
make_index_sequence*indexes=*/sizeof(V128)>())...};
}
// This is an array of shuffle control masks that can be used as the source
// operand for PSHUFB to permute the contents of the destination XMM register
// into a repeating byte pattern.
alignas(16) constexpr std::array,
16> pattern_generation_masks =
MakePatternMaskBytesTable(
/*index_offset=*/0,
/*pattern_sizes_minus_one=*/make_index_sequence<16>());
// Similar to 'pattern_generation_masks', this table is used to "rotate" the
// pattern so that we can copy the *next 16 bytes* consistent with the pattern.
// Basically, pattern_reshuffle_masks is a continuation of
// pattern_generation_masks. It follows that, pattern_reshuffle_masks is same as
// pattern_generation_masks for offsets 1, 2, 4, 8 and 16.
alignas(16) constexpr std::array,
16> pattern_reshuffle_masks =
MakePatternMaskBytesTable(
/*index_offset=*/16,
/*pattern_sizes_minus_one=*/make_index_sequence<16>());
SNAPPY_ATTRIBUTE_ALWAYS_INLINE
static inline V128 LoadPattern(const char* src, const size_t pattern_size) {
V128 generation_mask = V128_Load(reinterpret_cast(
pattern_generation_masks[pattern_size - 1].data()));
// Uninitialized bytes are masked out by the shuffle mask.
// TODO: remove annotation and macro defs once MSan is fixed.
SNAPPY_ANNOTATE_MEMORY_IS_INITIALIZED(src + pattern_size, 16 - pattern_size);
return V128_Shuffle(V128_LoadU(reinterpret_cast(src)),
generation_mask);
}
SNAPPY_ATTRIBUTE_ALWAYS_INLINE
static inline std::pair
LoadPatternAndReshuffleMask(const char* src, const size_t pattern_size) {
V128 pattern = LoadPattern(src, pattern_size);
// This mask will generate the next 16 bytes in-place. Doing so enables us to
// write data by at most 4 V128_StoreU.
//
// For example, suppose pattern is: abcdefabcdefabcd
// Shuffling with this mask will generate: efabcdefabcdefab
// Shuffling again will generate: cdefabcdefabcdef
V128 reshuffle_mask = V128_Load(reinterpret_cast(
pattern_reshuffle_masks[pattern_size - 1].data()));
return {pattern, reshuffle_mask};
}
#endif // SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE
// Fallback for when we need to copy while extending the pattern, for example
// copying 10 bytes from 3 positions back abc -> abcabcabcabca.
//
// REQUIRES: [dst - offset, dst + 64) is a valid address range.
SNAPPY_ATTRIBUTE_ALWAYS_INLINE
static inline bool Copy64BytesWithPatternExtension(char* dst, size_t offset) {
#if SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE
if (SNAPPY_PREDICT_TRUE(offset <= 16)) {
switch (offset) {
case 0:
return false;
case 1: {
// TODO: Ideally we should memset, move back once the
// codegen issues are fixed.
V128 pattern = V128_DupChar(dst[-1]);
for (int i = 0; i < 4; i++) {
V128_StoreU(reinterpret_cast(dst + 16 * i), pattern);
}
return true;
}
case 2:
case 4:
case 8:
case 16: {
V128 pattern = LoadPattern(dst - offset, offset);
for (int i = 0; i < 4; i++) {
V128_StoreU(reinterpret_cast(dst + 16 * i), pattern);
}
return true;
}
default: {
auto pattern_and_reshuffle_mask =
LoadPatternAndReshuffleMask(dst - offset, offset);
V128 pattern = pattern_and_reshuffle_mask.first;
V128 reshuffle_mask = pattern_and_reshuffle_mask.second;
for (int i = 0; i < 4; i++) {
V128_StoreU(reinterpret_cast(dst + 16 * i), pattern);
pattern = V128_Shuffle(pattern, reshuffle_mask);
}
return true;
}
}
}
#else
if (SNAPPY_PREDICT_TRUE(offset < 16)) {
if (SNAPPY_PREDICT_FALSE(offset == 0)) return false;
// Extend the pattern to the first 16 bytes.
// The simpler formulation of `dst[i - offset]` induces undefined behavior.
for (int i = 0; i < 16; i++) dst[i] = (dst - offset)[i];
// Find a multiple of pattern >= 16.
static std::array pattern_sizes = []() {
std::array res;
for (int i = 1; i < 16; i++) res[i] = (16 / i + 1) * i;
return res;
}();
offset = pattern_sizes[offset];
for (int i = 1; i < 4; i++) {
std::memcpy(dst + i * 16, dst + i * 16 - offset, 16);
}
return true;
}
#endif // SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE
// Very rare.
for (int i = 0; i < 4; i++) {
std::memcpy(dst + i * 16, dst + i * 16 - offset, 16);
}
return true;
}
// Copy [src, src+(op_limit-op)) to [op, op_limit) but faster than
// IncrementalCopySlow. buf_limit is the address past the end of the writable
// region of the buffer.
inline char* IncrementalCopy(const char* src, char* op, char* const op_limit,
char* const buf_limit) {
#if SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE
constexpr int big_pattern_size_lower_bound = 16;
#else
constexpr int big_pattern_size_lower_bound = 8;
#endif
// Terminology:
//
// slop = buf_limit - op
// pat = op - src
// len = op_limit - op
assert(src < op);
assert(op < op_limit);
assert(op_limit <= buf_limit);
// NOTE: The copy tags use 3 or 6 bits to store the copy length, so len <= 64.
assert(op_limit - op <= 64);
// NOTE: In practice the compressor always emits len >= 4, so it is ok to
// assume that to optimize this function, but this is not guaranteed by the
// compression format, so we have to also handle len < 4 in case the input
// does not satisfy these conditions.
size_t pattern_size = op - src;
// The cases are split into different branches to allow the branch predictor,
// FDO, and static prediction hints to work better. For each input we list the
// ratio of invocations that match each condition.
//
// input slop < 16 pat < 8 len > 16
// ------------------------------------------
// html|html4|cp 0% 1.01% 27.73%
// urls 0% 0.88% 14.79%
// jpg 0% 64.29% 7.14%
// pdf 0% 2.56% 58.06%
// txt[1-4] 0% 0.23% 0.97%
// pb 0% 0.96% 13.88%
// bin 0.01% 22.27% 41.17%
//
// It is very rare that we don't have enough slop for doing block copies. It
// is also rare that we need to expand a pattern. Small patterns are common
// for incompressible formats and for those we are plenty fast already.
// Lengths are normally not greater than 16 but they vary depending on the
// input. In general if we always predict len <= 16 it would be an ok
// prediction.
//
// In order to be fast we want a pattern >= 16 bytes (or 8 bytes in non-SSE)
// and an unrolled loop copying 1x 16 bytes (or 2x 8 bytes in non-SSE) at a
// time.
// Handle the uncommon case where pattern is less than 16 (or 8 in non-SSE)
// bytes.
if (pattern_size < big_pattern_size_lower_bound) {
#if SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE
// Load the first eight bytes into an 128-bit XMM register, then use PSHUFB
// to permute the register's contents in-place into a repeating sequence of
// the first "pattern_size" bytes.
// For example, suppose:
// src == "abc"
// op == op + 3
// After V128_Shuffle(), "pattern" will have five copies of "abc"
// followed by one byte of slop: abcabcabcabcabca.
//
// The non-SSE fallback implementation suffers from store-forwarding stalls
// because its loads and stores partly overlap. By expanding the pattern
// in-place, we avoid the penalty.
// Typically, the op_limit is the gating factor so try to simplify the loop
// based on that.
if (SNAPPY_PREDICT_TRUE(op_limit <= buf_limit - 15)) {
auto pattern_and_reshuffle_mask =
LoadPatternAndReshuffleMask(src, pattern_size);
V128 pattern = pattern_and_reshuffle_mask.first;
V128 reshuffle_mask = pattern_and_reshuffle_mask.second;
// There is at least one, and at most four 16-byte blocks. Writing four
// conditionals instead of a loop allows FDO to layout the code with
// respect to the actual probabilities of each length.
// TODO: Replace with loop with trip count hint.
V128_StoreU(reinterpret_cast(op), pattern);
if (op + 16 < op_limit) {
pattern = V128_Shuffle(pattern, reshuffle_mask);
V128_StoreU(reinterpret_cast(op + 16), pattern);
}
if (op + 32 < op_limit) {
pattern = V128_Shuffle(pattern, reshuffle_mask);
V128_StoreU(reinterpret_cast(op + 32), pattern);
}
if (op + 48 < op_limit) {
pattern = V128_Shuffle(pattern, reshuffle_mask);
V128_StoreU(reinterpret_cast(op + 48), pattern);
}
return op_limit;
}
char* const op_end = buf_limit - 15;
if (SNAPPY_PREDICT_TRUE(op < op_end)) {
auto pattern_and_reshuffle_mask =
LoadPatternAndReshuffleMask(src, pattern_size);
V128 pattern = pattern_and_reshuffle_mask.first;
V128 reshuffle_mask = pattern_and_reshuffle_mask.second;
// This code path is relatively cold however so we save code size
// by avoiding unrolling and vectorizing.
//
// TODO: Remove pragma when when cold regions don't get
// vectorized or unrolled.
#ifdef __clang__
#pragma clang loop unroll(disable)
#endif
do {
V128_StoreU(reinterpret_cast(op), pattern);
pattern = V128_Shuffle(pattern, reshuffle_mask);
op += 16;
} while (SNAPPY_PREDICT_TRUE(op < op_end));
}
return IncrementalCopySlow(op - pattern_size, op, op_limit);
#else // !SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE
// If plenty of buffer space remains, expand the pattern to at least 8
// bytes. The way the following loop is written, we need 8 bytes of buffer
// space if pattern_size >= 4, 11 bytes if pattern_size is 1 or 3, and 10
// bytes if pattern_size is 2. Precisely encoding that is probably not
// worthwhile; instead, invoke the slow path if we cannot write 11 bytes
// (because 11 are required in the worst case).
if (SNAPPY_PREDICT_TRUE(op <= buf_limit - 11)) {
while (pattern_size < 8) {
UnalignedCopy64(src, op);
op += pattern_size;
pattern_size *= 2;
}
if (SNAPPY_PREDICT_TRUE(op >= op_limit)) return op_limit;
} else {
return IncrementalCopySlow(src, op, op_limit);
}
#endif // SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE
}
assert(pattern_size >= big_pattern_size_lower_bound);
constexpr bool use_16bytes_chunk = big_pattern_size_lower_bound == 16;
// Copy 1x 16 bytes (or 2x 8 bytes in non-SSE) at a time. Because op - src can
// be < 16 in non-SSE, a single UnalignedCopy128 might overwrite data in op.
// UnalignedCopy64 is safe because expanding the pattern to at least 8 bytes
// guarantees that op - src >= 8.
//
// Typically, the op_limit is the gating factor so try to simplify the loop
// based on that.
if (SNAPPY_PREDICT_TRUE(op_limit <= buf_limit - 15)) {
// There is at least one, and at most four 16-byte blocks. Writing four
// conditionals instead of a loop allows FDO to layout the code with respect
// to the actual probabilities of each length.
// TODO: Replace with loop with trip count hint.
ConditionalUnalignedCopy128(src, op);
if (op + 16 < op_limit) {
ConditionalUnalignedCopy128(src + 16, op + 16);
}
if (op + 32 < op_limit) {
ConditionalUnalignedCopy128(src + 32, op + 32);
}
if (op + 48 < op_limit) {
ConditionalUnalignedCopy128(src + 48, op + 48);
}
return op_limit;
}
// Fall back to doing as much as we can with the available slop in the
// buffer. This code path is relatively cold however so we save code size by
// avoiding unrolling and vectorizing.
//
// TODO: Remove pragma when when cold regions don't get vectorized
// or unrolled.
#ifdef __clang__
#pragma clang loop unroll(disable)
#endif
for (char* op_end = buf_limit - 16; op < op_end; op += 16, src += 16) {
ConditionalUnalignedCopy128(src, op);
}
if (op >= op_limit) return op_limit;
// We only take this branch if we didn't have enough slop and we can do a
// single 8 byte copy.
if (SNAPPY_PREDICT_FALSE(op <= buf_limit - 8)) {
UnalignedCopy64(src, op);
src += 8;
op += 8;
}
return IncrementalCopySlow(src, op, op_limit);
}
} // namespace
template
static inline char* EmitLiteral(char* op, const char* literal, int len) {
// The vast majority of copies are below 16 bytes, for which a
// call to std::memcpy() is overkill. This fast path can sometimes
// copy up to 15 bytes too much, but that is okay in the
// main loop, since we have a bit to go on for both sides:
//
// - The input will always have kInputMarginBytes = 15 extra
// available bytes, as long as we're in the main loop, and
// if not, allow_fast_path = false.
// - The output will always have 32 spare bytes (see
// MaxCompressedLength).
assert(len > 0); // Zero-length literals are disallowed
int n = len - 1;
if (allow_fast_path && len <= 16) {
// Fits in tag byte
*op++ = LITERAL | (n << 2);
UnalignedCopy128(literal, op);
return op + len;
}
if (n < 60) {
// Fits in tag byte
*op++ = LITERAL | (n << 2);
} else {
int count = (Bits::Log2Floor(n) >> 3) + 1;
assert(count >= 1);
assert(count <= 4);
*op++ = LITERAL | ((59 + count) << 2);
// Encode in upcoming bytes.
// Write 4 bytes, though we may care about only 1 of them. The output buffer
// is guaranteed to have at least 3 more spaces left as 'len >= 61' holds
// here and there is a std::memcpy() of size 'len' below.
LittleEndian::Store32(op, n);
op += count;
}
// When allow_fast_path is true, we can overwrite up to 16 bytes.
if (allow_fast_path) {
char* destination = op;
const char* source = literal;
const char* end = destination + len;
do {
std::memcpy(destination, source, 16);
destination += 16;
source += 16;
} while (destination < end);
} else {
std::memcpy(op, literal, len);
}
return op + len;
}
template
static inline char* EmitCopyAtMost64(char* op, size_t offset, size_t len) {
assert(len <= 64);
assert(len >= 4);
assert(offset < 65536);
assert(len_less_than_12 == (len < 12));
if (len_less_than_12) {
uint32_t u = (len << 2) + (offset << 8);
uint32_t copy1 = COPY_1_BYTE_OFFSET - (4 << 2) + ((offset >> 3) & 0xe0);
uint32_t copy2 = COPY_2_BYTE_OFFSET - (1 << 2);
// It turns out that offset < 2048 is a difficult to predict branch.
// `perf record` shows this is the highest percentage of branch misses in
// benchmarks. This code produces branch free code, the data dependency
// chain that bottlenecks the throughput is so long that a few extra
// instructions are completely free (IPC << 6 because of data deps).
u += offset < 2048 ? copy1 : copy2;
LittleEndian::Store32(op, u);
op += offset < 2048 ? 2 : 3;
} else {
// Write 4 bytes, though we only care about 3 of them. The output buffer
// is required to have some slack, so the extra byte won't overrun it.
uint32_t u = COPY_2_BYTE_OFFSET + ((len - 1) << 2) + (offset << 8);
LittleEndian::Store32(op, u);
op += 3;
}
return op;
}
template
static inline char* EmitCopy(char* op, size_t offset, size_t len) {
assert(len_less_than_12 == (len < 12));
if (len_less_than_12) {
return EmitCopyAtMost64*len_less_than_12=*/true>(op, offset, len);
} else {
// A special case for len <= 64 might help, but so far measurements suggest
// it's in the noise.
// Emit 64 byte copies but make sure to keep at least four bytes reserved.
while (SNAPPY_PREDICT_FALSE(len >= 68)) {
op = EmitCopyAtMost64*len_less_than_12=*/false>(op, offset, 64);
len -= 64;
}
// One or two copies will now finish the job.
if (len > 64) {
op = EmitCopyAtMost64*len_less_than_12=*/false>(op, offset, 60);
len -= 60;
}
// Emit remainder.
if (len < 12) {
op = EmitCopyAtMost64*len_less_than_12=*/true>(op, offset, len);
} else {
op = EmitCopyAtMost64*len_less_than_12=*/false>(op, offset, len);
}
return op;
}
}
bool GetUncompressedLength(const char* start, size_t n, size_t* result) {
uint32_t v = 0;
const char* limit = start + n;
if (Varint::Parse32WithLimit(start, limit, &v) != NULL) {
*result = v;
return true;
} else {
return false;
}
}
namespace {
uint32_t CalculateTableSize(uint32_t input_size) {
static_assert(
kMaxHashTableSize >= kMinHashTableSize,
"kMaxHashTableSize should be greater or equal to kMinHashTableSize.");
if (input_size > kMaxHashTableSize) {
return kMaxHashTableSize;
}
if (input_size < kMinHashTableSize) {
return kMinHashTableSize;
}
// This is equivalent to Log2Ceiling(input_size), assuming input_size > 1.
// 2 << Log2Floor(x - 1) is equivalent to 1 << (1 + Log2Floor(x - 1)).
return 2u << Bits::Log2Floor(input_size - 1);
}
} // namespace
namespace internal {
WorkingMemory::WorkingMemory(size_t input_size) {
const size_t max_fragment_size = std::min(input_size, kBlockSize);
const size_t table_size = CalculateTableSize(max_fragment_size);
size_ = table_size * sizeof(*table_) + max_fragment_size +
MaxCompressedLength(max_fragment_size);
mem_ = std::allocator().allocate(size_);
table_ = reinterpret_cast(mem_);
input_ = mem_ + table_size * sizeof(*table_);
output_ = input_ + max_fragment_size;
}
WorkingMemory::~WorkingMemory() {
std::allocator().deallocate(mem_, size_);
}
uint16_t* WorkingMemory::GetHashTable(size_t fragment_size,
int* table_size) const {
const size_t htsize = CalculateTableSize(fragment_size);
memset(table_, 0, htsize * sizeof(*table_));
*table_size = htsize;
return table_;
}
} // end namespace internal
// Flat array compression that does not emit the "uncompressed length"
// prefix. Compresses "input" string to the "*op" buffer.
//
// REQUIRES: "input" is at most "kBlockSize" bytes long.
// REQUIRES: "op" points to an array of memory that is at least
// "MaxCompressedLength(input.size())" in size.
// REQUIRES: All elements in "table[0..table_size-1]" are initialized to zero.
// REQUIRES: "table_size" is a power of two
//
// Returns an "end" pointer into "op" buffer.
// "end - op" is the compressed size of "input".
namespace internal {
char* CompressFragment(const char* input, size_t input_size, char* op,
uint16_t* table, const int table_size) {
// "ip" is the input pointer, and "op" is the output pointer.
const char* ip = input;
assert(input_size <= kBlockSize);
assert((table_size & (table_size - 1)) == 0); // table must be power of two
const uint32_t mask = 2 * (table_size - 1);
const char* ip_end = input + input_size;
const char* base_ip = ip;
const size_t kInputMarginBytes = 15;
if (SNAPPY_PREDICT_TRUE(input_size >= kInputMarginBytes)) {
const char* ip_limit = input + input_size - kInputMarginBytes;
for (uint32_t preload = LittleEndian::Load32(ip + 1);;) {
// Bytes in [next_emit, ip) will be emitted as literal bytes. Or
// [next_emit, ip_end) after the main loop.
const char* next_emit = ip++;
uint64_t data = LittleEndian::Load64(ip);
// The body of this loop calls EmitLiteral once and then EmitCopy one or
// more times. (The exception is that when we're close to exhausting
// the input we goto emit_remainder.)
//
// In the first iteration of this loop we're just starting, so
// there's nothing to copy, so calling EmitLiteral once is
// necessary. And we only start a new iteration when the
// current iteration has determined that a call to EmitLiteral will
// precede the next call to EmitCopy (if any).
//
// Step 1: Scan forward in the input looking for a 4-byte-long match.
// If we get close to exhausting the input then goto emit_remainder.
//
// Heuristic match skipping: If 32 bytes are scanned with no matches
// found, start looking only at every other byte. If 32 more bytes are
// scanned (or skipped), look at every third byte, etc.. When a match is
// found, immediately go back to looking at every byte. This is a small
// loss (~5% performance, ~0.1% density) for compressible data due to more
// bookkeeping, but for non-compressible data (such as JPEG) it's a huge
// win since the compressor quickly "realizes" the data is incompressible
// and doesn't bother looking for matches everywhere.
//
// The "skip" variable keeps track of how many bytes there are since the
// last match; dividing it by 32 (ie. right-shifting by five) gives the
// number of bytes to move ahead for each iteration.
uint32_t skip = 32;
const char* candidate;
if (ip_limit - ip >= 16) {
auto delta = ip - base_ip;
for (int j = 0; j < 4; ++j) {
for (int k = 0; k < 4; ++k) {
int i = 4 * j + k;
// These for-loops are meant to be unrolled. So we can freely
// special case the first iteration to use the value already
// loaded in preload.
uint32_t dword = i == 0 ? preload : static_cast(data);
assert(dword == LittleEndian::Load32(ip + i));
uint16_t* table_entry = TableEntry(table, dword, mask);
candidate = base_ip + *table_entry;
assert(candidate >= base_ip);
assert(candidate < ip + i);
*table_entry = delta + i;
if (SNAPPY_PREDICT_FALSE(LittleEndian::Load32(candidate) == dword)) {
*op = LITERAL | (i << 2);
UnalignedCopy128(next_emit, op + 1);
ip += i;
op = op + i + 2;
goto emit_match;
}
data >>= 8;
}
data = LittleEndian::Load64(ip + 4 * j + 4);
}
ip += 16;
skip += 16;
}
while (true) {
assert(static_cast(data) == LittleEndian::Load32(ip));
uint16_t* table_entry = TableEntry(table, data, mask);
uint32_t bytes_between_hash_lookups = skip >> 5;
skip += bytes_between_hash_lookups;
const char* next_ip = ip + bytes_between_hash_lookups;
if (SNAPPY_PREDICT_FALSE(next_ip > ip_limit)) {
ip = next_emit;
goto emit_remainder;
}
candidate = base_ip + *table_entry;
assert(candidate >= base_ip);
assert(candidate < ip);
*table_entry = ip - base_ip;
if (SNAPPY_PREDICT_FALSE(static_cast(data) ==
LittleEndian::Load32(candidate))) {
break;
}
data = LittleEndian::Load32(next_ip);
ip = next_ip;
}
// Step 2: A 4-byte match has been found. We'll later see if more
// than 4 bytes match. But, prior to the match, input
// bytes [next_emit, ip) are unmatched. Emit them as "literal bytes."
assert(next_emit + 16 <= ip_end);
op = EmitLiteral*allow_fast_path=*/true>(op, next_emit, ip - next_emit);
// Step 3: Call EmitCopy, and then see if another EmitCopy could
// be our next move. Repeat until we find no match for the
// input immediately after what was consumed by the last EmitCopy call.
//
// If we exit this loop normally then we need to call EmitLiteral next,
// though we don't yet know how big the literal will be. We handle that
// by proceeding to the next iteration of the main loop. We also can exit
// this loop via goto if we get close to exhausting the input.
emit_match:
do {
// We have a 4-byte match at ip, and no need to emit any
// "literal bytes" prior to ip.
const char* base = ip;
std::pair p =
FindMatchLength(candidate + 4, ip + 4, ip_end, &data);
size_t matched = 4 + p.first;
ip += matched;
size_t offset = base - candidate;
assert(0 == memcmp(base, candidate, matched));
if (p.second) {
op = EmitCopy*len_less_than_12=*/true>(op, offset, matched);
} else {
op = EmitCopy*len_less_than_12=*/false>(op, offset, matched);
}
if (SNAPPY_PREDICT_FALSE(ip >= ip_limit)) {
goto emit_remainder;
}
// Expect 5 bytes to match
assert((data & 0xFFFFFFFFFF) ==
(LittleEndian::Load64(ip) & 0xFFFFFFFFFF));
// We are now looking for a 4-byte match again. We read
// table[Hash(ip, mask)] for that. To improve compression,
// we also update table[Hash(ip - 1, mask)] and table[Hash(ip, mask)].
*TableEntry(table, LittleEndian::Load32(ip - 1), mask) =
ip - base_ip - 1;
uint16_t* table_entry = TableEntry(table, data, mask);
candidate = base_ip + *table_entry;
*table_entry = ip - base_ip;
// Measurements on the benchmarks have shown the following probabilities
// for the loop to exit (ie. avg. number of iterations is reciprocal).
// BM_Flat/6 txt1 p = 0.3-0.4
// BM_Flat/7 txt2 p = 0.35
// BM_Flat/8 txt3 p = 0.3-0.4
// BM_Flat/9 txt3 p = 0.34-0.4
// BM_Flat/10 pb p = 0.4
// BM_Flat/11 gaviota p = 0.1
// BM_Flat/12 cp p = 0.5
// BM_Flat/13 c p = 0.3
} while (static_cast(data) == LittleEndian::Load32(candidate));
// Because the least significant 5 bytes matched, we can utilize data
// for the next iteration.
preload = data >> 8;
}
}
emit_remainder:
// Emit the remaining bytes as a literal
if (ip < ip_end) {
op = EmitLiteral*allow_fast_path=*/false>(op, ip, ip_end - ip);
}
return op;
}
char* CompressFragmentDoubleHash(const char* input, size_t input_size, char* op,
uint16_t* table, const int table_size,
uint16_t* table2, const int table_size2) {
(void)table_size2;
assert(table_size == table_size2);
// "ip" is the input pointer, and "op" is the output pointer.
const char* ip = input;
assert(input_size <= kBlockSize);
assert((table_size & (table_size - 1)) == 0); // table must be power of two
const uint32_t mask = 2 * (table_size - 1);
const char* ip_end = input + input_size;
const char* base_ip = ip;
const size_t kInputMarginBytes = 15;
if (SNAPPY_PREDICT_TRUE(input_size >= kInputMarginBytes)) {
const char* ip_limit = input + input_size - kInputMarginBytes;
for (;;) {
const char* next_emit = ip++;
uint64_t data = LittleEndian::Load64(ip);
uint32_t skip = 512;
const char* candidate;
uint32_t candidate_length;
while (true) {
assert(static_cast(data) == LittleEndian::Load32(ip));
uint16_t* table_entry2 = TableEntry8ByteMatch(table2, data, mask);
uint32_t bytes_between_hash_lookups = skip >> 9;
skip++;
const char* next_ip = ip + bytes_between_hash_lookups;
if (SNAPPY_PREDICT_FALSE(next_ip > ip_limit)) {
ip = next_emit;
goto emit_remainder;
}
candidate = base_ip + *table_entry2;
assert(candidate >= base_ip);
assert(candidate < ip);
*table_entry2 = ip - base_ip;
if (SNAPPY_PREDICT_FALSE(static_cast(data) ==
LittleEndian::Load32(candidate))) {
candidate_length =
FindMatchLengthPlain(candidate + 4, ip + 4, ip_end) + 4;
break;
}
uint16_t* table_entry = TableEntry4ByteMatch(table, data, mask);
candidate = base_ip + *table_entry;
assert(candidate >= base_ip);
assert(candidate < ip);
*table_entry = ip - base_ip;
if (SNAPPY_PREDICT_FALSE(static_cast(data) ==
LittleEndian::Load32(candidate))) {
candidate_length =
FindMatchLengthPlain(candidate + 4, ip + 4, ip_end) + 4;
table_entry2 =
TableEntry8ByteMatch(table2, LittleEndian::Load64(ip + 1), mask);
auto candidate2 = base_ip + *table_entry2;
size_t candidate_length2 =
FindMatchLengthPlain(candidate2, ip + 1, ip_end);
if (candidate_length2 > candidate_length) {
*table_entry2 = ip - base_ip;
candidate = candidate2;
candidate_length = candidate_length2;
++ip;
}
break;
}
data = LittleEndian::Load64(next_ip);
ip = next_ip;
}
// Backtrack to the point it matches fully.
while (ip > next_emit && candidate > base_ip &&
*(ip - 1) == *(candidate - 1)) {
--ip;
--candidate;
++candidate_length;
}
*TableEntry8ByteMatch(table2, LittleEndian::Load64(ip + 1), mask) =
ip - base_ip + 1;
*TableEntry8ByteMatch(table2, LittleEndian::Load64(ip + 2), mask) =
ip - base_ip + 2;
*TableEntry4ByteMatch(table, LittleEndian::Load32(ip + 1), mask) =
ip - base_ip + 1;
// Step 2: A 4-byte or 8-byte match has been found.
// We'll later see if more than 4 bytes match. But, prior to the match,
// input bytes [next_emit, ip) are unmatched. Emit them as
// "literal bytes."
assert(next_emit + 16 <= ip_end);
if (ip - next_emit > 0) {
op = EmitLiteral*allow_fast_path=*/true>(op, next_emit,
ip - next_emit);
}
// Step 3: Call EmitCopy, and then see if another EmitCopy could
// be our next move. Repeat until we find no match for the
// input immediately after what was consumed by the last EmitCopy call.
//
// If we exit this loop normally then we need to call EmitLiteral next,
// though we don't yet know how big the literal will be. We handle that
// by proceeding to the next iteration of the main loop. We also can exit
// this loop via goto if we get close to exhausting the input.
do {
// We have a 4-byte match at ip, and no need to emit any
// "literal bytes" prior to ip.
const char* base = ip;
ip += candidate_length;
size_t offset = base - candidate;
if (candidate_length < 12) {
op =
EmitCopy*len_less_than_12=*/true>(op, offset, candidate_length);
} else {
op = EmitCopy*len_less_than_12=*/false>(op, offset,
candidate_length);
}
if (SNAPPY_PREDICT_FALSE(ip >= ip_limit)) {
goto emit_remainder;
}
// We are now looking for a 4-byte match again. We read
// table[Hash(ip, mask)] for that. To improve compression,
// we also update several previous table entries.
if (ip - base_ip > 7) {
*TableEntry8ByteMatch(table2, LittleEndian::Load64(ip - 7), mask) =
ip - base_ip - 7;
*TableEntry8ByteMatch(table2, LittleEndian::Load64(ip - 4), mask) =
ip - base_ip - 4;
}
*TableEntry8ByteMatch(table2, LittleEndian::Load64(ip - 3), mask) =
ip - base_ip - 3;
*TableEntry8ByteMatch(table2, LittleEndian::Load64(ip - 2), mask) =
ip - base_ip - 2;
*TableEntry4ByteMatch(table, LittleEndian::Load32(ip - 2), mask) =
ip - base_ip - 2;
*TableEntry4ByteMatch(table, LittleEndian::Load32(ip - 1), mask) =
ip - base_ip - 1;
uint16_t* table_entry =
TableEntry8ByteMatch(table2, LittleEndian::Load64(ip), mask);
candidate = base_ip + *table_entry;
*table_entry = ip - base_ip;
if (LittleEndian::Load32(ip) == LittleEndian::Load32(candidate)) {
candidate_length =
FindMatchLengthPlain(candidate + 4, ip + 4, ip_end) + 4;
continue;
}
table_entry =
TableEntry4ByteMatch(table, LittleEndian::Load32(ip), mask);
candidate = base_ip + *table_entry;
*table_entry = ip - base_ip;
if (LittleEndian::Load32(ip) == LittleEndian::Load32(candidate)) {
candidate_length =
FindMatchLengthPlain(candidate + 4, ip + 4, ip_end) + 4;
continue;
}
break;
} while (true);
}
}
emit_remainder:
// Emit the remaining bytes as a literal
if (ip < ip_end) {
op = EmitLiteral*allow_fast_path=*/false>(op, ip, ip_end - ip);
}
return op;
}
} // end namespace internal
static inline void Report(int token, const char *algorithm, size_t
compressed_size, size_t uncompressed_size) {
// TODO: Switch to [[maybe_unused]] when we can assume C++17.
(void)token;
(void)algorithm;
(void)compressed_size;
(void)uncompressed_size;
}
// Signature of output types needed by decompression code.
// The decompression code is templatized on a type that obeys this
// signature so that we do not pay virtual function call overhead in
// the middle of a tight decompression loop.
//
// class DecompressionWriter {
// public:
// // Called before decompression
// void SetExpectedLength(size_t length);
//
// // For performance a writer may choose to donate the cursor variable to the
// // decompression function. The decompression will inject it in all its
// // function calls to the writer. Keeping the important output cursor as a
// // function local stack variable allows the compiler to keep it in
// // register, which greatly aids performance by avoiding loads and stores of
// // this variable in the fast path loop iterations.
// T GetOutputPtr() const;
//
// // At end of decompression the loop donates the ownership of the cursor
// // variable back to the writer by calling this function.
// void SetOutputPtr(T op);
//
// // Called after decompression
// bool CheckLength() const;
//
// // Called repeatedly during decompression
// // Each function get a pointer to the op (output pointer), that the writer
// // can use and update. Note it's important that these functions get fully
// // inlined so that no actual address of the local variable needs to be
// // taken.
// bool Append(const char* ip, size_t length, T* op);
// bool AppendFromSelf(uint32_t offset, size_t length, T* op);
//
// // The rules for how TryFastAppend differs from Append are somewhat
// // convoluted:
// //
// // - TryFastAppend is allowed to decline (return false) at any
// // time, for any reason -- just "return false" would be
// // a perfectly legal implementation of TryFastAppend.
// // The intention is for TryFastAppend to allow a fast path
// // in the common case of a small append.
// // - TryFastAppend is allowed to read up to bytes
// // from the input buffer, whereas Append is allowed to read
// // . However, if it returns true, it must leave
// // at least five (kMaximumTagLength) bytes in the input buffer
// // afterwards, so that there is always enough space to read the
// // next tag without checking for a refill.
// // - TryFastAppend must always return decline (return false)
// // if is 61 or more, as in this case the literal length is not
// // decoded fully. In practice, this should not be a big problem,
// // as it is unlikely that one would implement a fast path accepting
// // this much data.
// //
// bool TryFastAppend(const char* ip, size_t available, size_t length, T* op);
// };
static inline uint32_t ExtractLowBytes(const uint32_t& v, int n) {
assert(n >= 0);
assert(n <= 4);
#if SNAPPY_HAVE_BMI2
return _bzhi_u32(v, 8 * n);
#else
// This needs to be wider than uint32_t otherwise `mask << 32` will be
// undefined.
uint64_t mask = 0xffffffff;
return v & ~(mask << (8 * n));
#endif
}
static inline bool LeftShiftOverflows(uint8_t value, uint32_t shift) {
assert(shift < 32);
static const uint8_t masks[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe};
return (value & masks[shift]) != 0;
}
inline bool Copy64BytesWithPatternExtension(ptrdiff_t dst, size_t offset) {
// TODO: Switch to [[maybe_unused]] when we can assume C++17.
(void)dst;
return offset != 0;
}
// Copies between size bytes and 64 bytes from src to dest. size cannot exceed
// 64. More than size bytes, but never exceeding 64, might be copied if doing
// so gives better performance. [src, src + size) must not overlap with
// [dst, dst + size), but [src, src + 64) may overlap with [dst, dst + 64).
void MemCopy64(char* dst, const void* src, size_t size) {
// Always copy this many bytes. If that's below size then copy the full 64.
constexpr int kShortMemCopy = 32;
(void)kShortMemCopy;
assert(size <= 64);
assert(std::less_equal()(static_cast(src) + size,
dst) ||
std::less_equal()(dst + size, src));
// We know that src and dst are at least size bytes apart. However, because we
// might copy more than size bytes the copy still might overlap past size.
// E.g. if src and dst appear consecutively in memory (src + size >= dst).
// TODO: Investigate wider copies on other platforms.
#if defined(__x86_64__) && defined(__AVX__)
assert(kShortMemCopy <= 32);
__m256i data = _mm256_lddqu_si256(static_cast(src));
_mm256_storeu_si256(reinterpret_cast<__m256i *>(dst), data);
// Profiling shows that nearly all copies are short.
if (SNAPPY_PREDICT_FALSE(size > kShortMemCopy)) {
data = _mm256_lddqu_si256(static_cast(src) + 1);
_mm256_storeu_si256(reinterpret_cast<__m256i *>(dst) + 1, data);
}
// RVV acceleration available on RISC-V when compiled with -march=rv64gcv
#elif defined(__riscv) && SNAPPY_HAVE_RVV
// Cast pointers to the type we will operate on.
unsigned char* dst_ptr = reinterpret_cast(dst);
const unsigned char* src_ptr = reinterpret_cast(src);
size_t remaining_bytes = size;
// Loop as long as there are bytes remaining to be copied.
while (remaining_bytes > 0) {
// Set vector configuration: e8 (8-bit elements), m2 (LMUL=2).
// Use e8m2 configuration to maximize throughput.
size_t vl = VSETVL_E8M2(remaining_bytes);
// Load data from the current source pointer.
vuint8m2_t vec = VLE8_V_U8M2(src_ptr, vl);
// Store data to the current destination pointer.
VSE8_V_U8M2(dst_ptr, vec, vl);
// Update pointers and the remaining count.
src_ptr += vl;
dst_ptr += vl;
remaining_bytes -= vl;
}
#else
std::memmove(dst, src, kShortMemCopy);
// Profiling shows that nearly all copies are short.
if (SNAPPY_PREDICT_FALSE(size > kShortMemCopy)) {
std::memmove(dst + kShortMemCopy,
static_cast(src) + kShortMemCopy,
64 - kShortMemCopy);
}
#endif
}
void MemCopy64(ptrdiff_t dst, const void* src, size_t size) {
// TODO: Switch to [[maybe_unused]] when we can assume C++17.
(void)dst;
(void)src;
(void)size;
}
void ClearDeferred(const void** deferred_src, size_t* deferred_length,
uint8_t* safe_source) {
*deferred_src = safe_source;
*deferred_length = 0;
}
void DeferMemCopy(const void** deferred_src, size_t* deferred_length,
const void* src, size_t length) {
*deferred_src = src;
*deferred_length = length;
}
SNAPPY_ATTRIBUTE_ALWAYS_INLINE
inline size_t AdvanceToNextTagARMOptimized(const uint8_t** ip_p, size_t* tag) {
const uint8_t*& ip = *ip_p;
// This section is crucial for the throughput of the decompression loop.
// The latency of an iteration is fundamentally constrained by the
// following data chain on ip.
// ip -> c = Load(ip) -> delta1 = (c & 3) -> ip += delta1 or delta2
// delta2 = ((c >> 2) + 1) ip++
// This is different from X86 optimizations because ARM has conditional add
// instruction (csinc) and it removes several register moves.
const size_t tag_type = *tag & 3;
const bool is_literal = (tag_type == 0);
if (is_literal) {
size_t next_literal_tag = (*tag >> 2) + 1;
*tag = ip[next_literal_tag];
ip += next_literal_tag + 1;
} else {
*tag = ip[tag_type];
ip += tag_type + 1;
}
return tag_type;
}
SNAPPY_ATTRIBUTE_ALWAYS_INLINE
inline size_t AdvanceToNextTagX86Optimized(const uint8_t** ip_p, size_t* tag) {
const uint8_t*& ip = *ip_p;
// This section is crucial for the throughput of the decompression loop.
// The latency of an iteration is fundamentally constrained by the
// following data chain on ip.
// ip -> c = Load(ip) -> ip1 = ip + 1 + (c & 3) -> ip = ip1 or ip2
// ip2 = ip + 2 + (c >> 2)
// This amounts to 8 cycles.
// 5 (load) + 1 (c & 3) + 1 (lea ip1, [ip + (c & 3) + 1]) + 1 (cmov)
size_t literal_len = *tag >> 2;
size_t tag_type = *tag;
bool is_literal;
#if defined(__GCC_ASM_FLAG_OUTPUTS__) && defined(__x86_64__)
// TODO clang misses the fact that the (c & 3) already correctly
// sets the zero flag.
asm("and $3, %k[tag_type]\n\t"
: [tag_type] "+r"(tag_type), "=@ccz"(is_literal)
:: "cc");
#else
tag_type &= 3;
is_literal = (tag_type == 0);
#endif
// TODO
// This is code is subtle. Loading the values first and then cmov has less
// latency then cmov ip and then load. However clang would move the loads
// in an optimization phase, volatile prevents this transformation.
// Note that we have enough slop bytes (64) that the loads are always valid.
size_t tag_literal =
static_cast(ip)[1 + literal_len];
size_t tag_copy = static_cast(ip)[tag_type];
*tag = is_literal ? tag_literal : tag_copy;
const uint8_t* ip_copy = ip + 1 + tag_type;
const uint8_t* ip_literal = ip + 2 + literal_len;
ip = is_literal ? ip_literal : ip_copy;
#if defined(__GNUC__) && defined(__x86_64__)
// TODO Clang is "optimizing" zero-extension (a totally free
// operation) this means that after the cmov of tag, it emits another movzb
// tag, byte(tag). It really matters as it's on the core chain. This dummy
// asm, persuades clang to do the zero-extension at the load (it's automatic)
// removing the expensive movzb.
asm("" ::"r"(tag_copy));
#endif
return tag_type;
}
// Extract the offset for copy-1 and copy-2 returns 0 for literals or copy-4.
inline uint32_t ExtractOffset(uint32_t val, size_t tag_type) {
// For x86 non-static storage works better. For ARM static storage is better.
// TODO: Once the array is recognized as a register, improve the
// readability for x86.
#if defined(__x86_64__)
constexpr uint64_t kExtractMasksCombined = 0x0000FFFF00FF0000ull;
uint16_t result;
memcpy(&result,
reinterpret_cast(&kExtractMasksCombined) + 2 * tag_type,
sizeof(result));
return val & result;
#elif defined(__aarch64__)
constexpr uint64_t kExtractMasksCombined = 0x0000FFFF00FF0000ull;
return val & static_cast(
(kExtractMasksCombined >> (tag_type * 16)) & 0xFFFF);
#else
static constexpr uint32_t kExtractMasks[4] = {0, 0xFF, 0xFFFF, 0};
return val & kExtractMasks[tag_type];
#endif
};
// Core decompression loop, when there is enough data available.
// Decompresses the input buffer [ip, ip_limit) into the output buffer
// [op, op_limit_min_slop). Returning when either we are too close to the end
// of the input buffer, or we exceed op_limit_min_slop or when a exceptional
// tag is encountered (literal of length > 60) or a copy-4.
// Returns {ip, op} at the points it stopped decoding.
// TODO This function probably does not need to be inlined, as it
// should decode large chunks at a time. This allows runtime dispatch to
// implementations based on CPU capability (BMI2 / perhaps 32 / 64 byte memcpy).
template
std::pair DecompressBranchless(
const uint8_t* ip, const uint8_t* ip_limit, ptrdiff_t op, T op_base,
ptrdiff_t op_limit_min_slop) {
// If deferred_src is invalid point it here.
uint8_t safe_source[64];
const void* deferred_src;
size_t deferred_length;
ClearDeferred(&deferred_src, &deferred_length, safe_source);
// We unroll the inner loop twice so we need twice the spare room.
op_limit_min_slop -= kSlopBytes;
if (2 * (kSlopBytes + 1) < ip_limit - ip && op < op_limit_min_slop) {
const uint8_t* const ip_limit_min_slop = ip_limit - 2 * kSlopBytes - 1;
ip++;
// ip points just past the tag and we are touching at maximum kSlopBytes
// in an iteration.
size_t tag = ip[-1];
#if defined(__clang__) && defined(__aarch64__)
// Workaround for https://bugs.llvm.org/show_bug.cgi?id=51317
// when loading 1 byte, clang for aarch64 doesn't realize that it(ldrb)
// comes with free zero-extension, so clang generates another
// 'and xn, xm, 0xff' before it use that as the offset. This 'and' is
// redundant and can be removed by adding this dummy asm, which gives
// clang a hint that we're doing the zero-extension at the load.
asm("" ::"r"(tag));
#endif
do {
// The throughput is limited by instructions, unrolling the inner loop
// twice reduces the amount of instructions checking limits and also
// leads to reduced mov's.
SNAPPY_PREFETCH(ip + 128);
for (int i = 0; i < 2; i++) {
const uint8_t* old_ip = ip;
assert(tag == ip[-1]);
// For literals tag_type = 0, hence we will always obtain 0 from
// ExtractLowBytes. For literals offset will thus be kLiteralOffset.
ptrdiff_t len_minus_offset = kLengthMinusOffset[tag];
uint32_t next;
#if defined(__aarch64__)
size_t tag_type = AdvanceToNextTagARMOptimized(&ip, &tag);
// We never need more than 16 bits. Doing a Load16 allows the compiler
// to elide the masking operation in ExtractOffset.
next = LittleEndian::Load16(old_ip);
#else
size_t tag_type = AdvanceToNextTagX86Optimized(&ip, &tag);
next = LittleEndian::Load32(old_ip);
#endif
size_t len = len_minus_offset & 0xFF;
ptrdiff_t extracted = ExtractOffset(next, tag_type);
ptrdiff_t len_min_offset = len_minus_offset - extracted;
if (SNAPPY_PREDICT_FALSE(len_minus_offset > extracted)) {
if (SNAPPY_PREDICT_FALSE(len & 0x80)) {
// Exceptional case (long literal or copy 4).
// Actually doing the copy here is negatively impacting the main
// loop due to compiler incorrectly allocating a register for
// this fallback. Hence we just break.
break_loop:
ip = old_ip;
goto exit;
}
// Only copy-1 or copy-2 tags can get here.
assert(tag_type == 1 || tag_type == 2);
std::ptrdiff_t delta = (op + deferred_length) + len_min_offset - len;
// Guard against copies before the buffer start.
// Execute any deferred MemCopy since we write to dst here.
MemCopy64(op_base + op, deferred_src, deferred_length);
op += deferred_length;
ClearDeferred(&deferred_src, &deferred_length, safe_source);
if (SNAPPY_PREDICT_FALSE(delta < 0 ||
!Copy64BytesWithPatternExtension(
op_base + op, len - len_min_offset))) {
goto break_loop;
}
// We aren't deferring this copy so add length right away.
op += len;
continue;
}
std::ptrdiff_t delta = (op + deferred_length) + len_min_offset - len;
if (SNAPPY_PREDICT_FALSE(delta < 0)) {
// Due to the spurious offset in literals have this will trigger
// at the start of a block when op is still smaller than 256.
if (tag_type != 0) goto break_loop;
MemCopy64(op_base + op, deferred_src, deferred_length);
op += deferred_length;
DeferMemCopy(&deferred_src, &deferred_length, old_ip, len);
continue;
}
// For copies we need to copy from op_base + delta, for literals
// we need to copy from ip instead of from the stream.
const void* from =
tag_type ? reinterpret_cast(op_base + delta) : old_ip;
MemCopy64(op_base + op, deferred_src, deferred_length);
op += deferred_length;
DeferMemCopy(&deferred_src, &deferred_length, from, len);
}
} while (ip < ip_limit_min_slop &&
static_cast(op + deferred_length) < op_limit_min_slop);
exit:
ip--;
assert(ip <= ip_limit);
}
// If we deferred a copy then we can perform. If we are up to date then we
// might not have enough slop bytes and could run past the end.
if (deferred_length) {
MemCopy64(op_base + op, deferred_src, deferred_length);
op += deferred_length;
ClearDeferred(&deferred_src, &deferred_length, safe_source);
}
return {ip, op};
}
// Helper class for decompression
class SnappyDecompressor {
private:
Source* reader_; // Underlying source of bytes to decompress
const char* ip_; // Points to next buffered byte
const char* ip_limit_; // Points just past buffered bytes
// If ip < ip_limit_min_maxtaglen_ it's safe to read kMaxTagLength from
// buffer.
const char* ip_limit_min_maxtaglen_;
uint64_t peeked_; // Bytes peeked from reader (need to skip)
bool eof_; // Hit end of input without an error?
char scratch_[kMaximumTagLength]; // See RefillTag().
// Ensure that all of the tag metadata for the next tag is available
// in [ip_..ip_limit_-1]. Also ensures that [ip,ip+4] is readable even
// if (ip_limit_ - ip_ < 5).
//
// Returns true on success, false on error or end of input.
bool RefillTag();
void ResetLimit(const char* ip) {
ip_limit_min_maxtaglen_ =
ip_limit_ - std::min(ip_limit_ - ip, kMaximumTagLength - 1);
}
public:
explicit SnappyDecompressor(Source* reader)
: reader_(reader), ip_(NULL), ip_limit_(NULL), peeked_(0), eof_(false) {}
~SnappyDecompressor() {
// Advance past any bytes we peeked at from the reader
reader_->Skip(peeked_);
}
// Returns true iff we have hit the end of the input without an error.
bool eof() const { return eof_; }
// Read the uncompressed length stored at the start of the compressed data.
// On success, stores the length in *result and returns true.
// On failure, returns false.
bool ReadUncompressedLength(uint32_t* result) {
assert(ip_ == NULL); // Must not have read anything yet
// Length is encoded in 1..5 bytes
*result = 0;
uint32_t shift = 0;
while (true) {
if (shift >= 32) return false;
size_t n;
const char* ip = reader_->Peek(&n);
if (n == 0) return false;
const unsigned char c = *(reinterpret_cast(ip));
reader_->Skip(1);
uint32_t val = c & 0x7f;
if (LeftShiftOverflows(static_cast