cohere-transcribe 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +21 -0
- data/LICENSE.txt +201 -0
- data/NOTICE +5 -0
- data/README.md +265 -0
- data/THIRD_PARTY_NOTICES.md +67 -0
- data/exe/cohere-transcribe +8 -0
- data/exe/cohere-transcribe-doctor +8 -0
- data/ext/cohere_transcribe_native/CMakeLists.txt +262 -0
- data/ext/cohere_transcribe_native/README.md +26 -0
- data/ext/cohere_transcribe_native/audio_abi.cpp +1416 -0
- data/ext/cohere_transcribe_native/audio_exports.macos +5 -0
- data/ext/cohere_transcribe_native/audio_exports.map +10 -0
- data/ext/cohere_transcribe_native/cohere_abi.cpp +1209 -0
- data/ext/cohere_transcribe_native/exports.macos +40 -0
- data/ext/cohere_transcribe_native/exports.map +45 -0
- data/ext/cohere_transcribe_native/extconf.rb +89 -0
- data/ext/cohere_transcribe_native/test/abi_smoke.rb +232 -0
- data/ext/cohere_transcribe_native/test/audio_matrix_smoke.cpp +121 -0
- data/ext/cohere_transcribe_native/test/audio_reliability_smoke.cpp +198 -0
- data/ext/cohere_transcribe_native/test/native_abi_reliability_smoke.cpp +186 -0
- data/ext/cohere_transcribe_native/test/native_batch_projection_probe.rb +81 -0
- data/ext/cohere_transcribe_native/test/native_cancellation_smoke.cpp +194 -0
- data/lib/cohere/transcribe/alignment/ATTRIBUTION.md +23 -0
- data/lib/cohere/transcribe/alignment/LICENSE.ctc-forced-aligner +407 -0
- data/lib/cohere/transcribe/alignment/LICENSE.torchaudio +25 -0
- data/lib/cohere/transcribe/alignment/LICENSE.uroman +26 -0
- data/lib/cohere/transcribe/alignment/aligner.rb +476 -0
- data/lib/cohere/transcribe/alignment/ctc.rb +224 -0
- data/lib/cohere/transcribe/alignment/text.rb +237 -0
- data/lib/cohere/transcribe/alignment/uroman_data.rb +4950 -0
- data/lib/cohere/transcribe/api.rb +173 -0
- data/lib/cohere/transcribe/asr/batching.rb +472 -0
- data/lib/cohere/transcribe/asr/failure_policy.rb +112 -0
- data/lib/cohere/transcribe/asr/native.rb +676 -0
- data/lib/cohere/transcribe/audio/ATTRIBUTION.md +8 -0
- data/lib/cohere/transcribe/audio/LICENSE.auditok +21 -0
- data/lib/cohere/transcribe/audio/decoder.rb +315 -0
- data/lib/cohere/transcribe/audio/ffmpeg_native.rb +248 -0
- data/lib/cohere/transcribe/audio/segmentation.rb +260 -0
- data/lib/cohere/transcribe/cli.rb +727 -0
- data/lib/cohere/transcribe/configuration.rb +282 -0
- data/lib/cohere/transcribe/constants.rb +14 -0
- data/lib/cohere/transcribe/dense_converter.rb +548 -0
- data/lib/cohere/transcribe/doctor.rb +576 -0
- data/lib/cohere/transcribe/errors.rb +57 -0
- data/lib/cohere/transcribe/gguf_writer.rb +268 -0
- data/lib/cohere/transcribe/hub.rb +436 -0
- data/lib/cohere/transcribe/input.rb +110 -0
- data/lib/cohere/transcribe/licenses/crispasr.txt +21 -0
- data/lib/cohere/transcribe/loader.rb +128 -0
- data/lib/cohere/transcribe/model_identity.rb +440 -0
- data/lib/cohere/transcribe/output/publication.rb +1118 -0
- data/lib/cohere/transcribe/output/rendering.rb +105 -0
- data/lib/cohere/transcribe/output/timing.rb +86 -0
- data/lib/cohere/transcribe/python_text.rb +70 -0
- data/lib/cohere/transcribe/pytorch_checkpoint.rb +1180 -0
- data/lib/cohere/transcribe/runtime/engine.rb +1676 -0
- data/lib/cohere/transcribe/runtime/model_provider.rb +390 -0
- data/lib/cohere/transcribe/runtime/precision.rb +57 -0
- data/lib/cohere/transcribe/runtime/preparation.rb +215 -0
- data/lib/cohere/transcribe/runtime/resources.rb +165 -0
- data/lib/cohere/transcribe/runtime/word_pipeline.rb +364 -0
- data/lib/cohere/transcribe/safetensors.rb +579 -0
- data/lib/cohere/transcribe/state/checkpoint.rb +224 -0
- data/lib/cohere/transcribe/state/contracts.rb +141 -0
- data/lib/cohere/transcribe/state/io.rb +727 -0
- data/lib/cohere/transcribe/state/locking.rb +211 -0
- data/lib/cohere/transcribe/state/manifest.rb +155 -0
- data/lib/cohere/transcribe/state.rb +7 -0
- data/lib/cohere/transcribe/types.rb +535 -0
- data/lib/cohere/transcribe/vad/ATTRIBUTION.md +14 -0
- data/lib/cohere/transcribe/vad/LICENSE.faster-whisper +21 -0
- data/lib/cohere/transcribe/vad/LICENSE.silero-vad +21 -0
- data/lib/cohere/transcribe/vad/silero.rb +344 -0
- data/lib/cohere/transcribe/vad/silero_vad_v6.onnx +0 -0
- data/lib/cohere/transcribe/vad/timestamps.rb +219 -0
- data/lib/cohere/transcribe/version.rb +7 -0
- data/lib/cohere/transcribe.rb +26 -0
- data/sig/cohere/transcribe.rbs +250 -0
- data/vendor/crispasr/AUTHORS +510 -0
- data/vendor/crispasr/LICENSE +21 -0
- data/vendor/crispasr/UPSTREAM.md +9 -0
- data/vendor/crispasr/VERSION +1 -0
- data/vendor/crispasr/ggml/AUTHORS +335 -0
- data/vendor/crispasr/ggml/CMakeLists.txt +512 -0
- data/vendor/crispasr/ggml/LICENSE +21 -0
- data/vendor/crispasr/ggml/README.md +49 -0
- data/vendor/crispasr/ggml/cmake/FindNCCL.cmake +36 -0
- data/vendor/crispasr/ggml/cmake/GitVars.cmake +22 -0
- data/vendor/crispasr/ggml/cmake/common.cmake +50 -0
- data/vendor/crispasr/ggml/cmake/ggml-config.cmake.in +191 -0
- data/vendor/crispasr/ggml/ggml.pc.in +10 -0
- data/vendor/crispasr/ggml/include/ggml-alloc.h +85 -0
- data/vendor/crispasr/ggml/include/ggml-backend.h +431 -0
- data/vendor/crispasr/ggml/include/ggml-blas.h +25 -0
- data/vendor/crispasr/ggml/include/ggml-cann.h +123 -0
- data/vendor/crispasr/ggml/include/ggml-cpp.h +39 -0
- data/vendor/crispasr/ggml/include/ggml-cpu.h +151 -0
- data/vendor/crispasr/ggml/include/ggml-cuda.h +50 -0
- data/vendor/crispasr/ggml/include/ggml-hexagon.h +19 -0
- data/vendor/crispasr/ggml/include/ggml-metal.h +61 -0
- data/vendor/crispasr/ggml/include/ggml-opencl.h +26 -0
- data/vendor/crispasr/ggml/include/ggml-openvino.h +37 -0
- data/vendor/crispasr/ggml/include/ggml-opt.h +256 -0
- data/vendor/crispasr/ggml/include/ggml-rpc.h +35 -0
- data/vendor/crispasr/ggml/include/ggml-sycl.h +49 -0
- data/vendor/crispasr/ggml/include/ggml-virtgpu.h +14 -0
- data/vendor/crispasr/ggml/include/ggml-vulkan.h +29 -0
- data/vendor/crispasr/ggml/include/ggml-webgpu.h +19 -0
- data/vendor/crispasr/ggml/include/ggml-zdnn.h +17 -0
- data/vendor/crispasr/ggml/include/ggml-zendnn.h +22 -0
- data/vendor/crispasr/ggml/include/ggml.h +2887 -0
- data/vendor/crispasr/ggml/include/gguf.h +204 -0
- data/vendor/crispasr/ggml/src/CMakeLists.txt +493 -0
- data/vendor/crispasr/ggml/src/ggml-alloc.c +1323 -0
- data/vendor/crispasr/ggml/src/ggml-backend-dl.cpp +48 -0
- data/vendor/crispasr/ggml/src/ggml-backend-dl.h +44 -0
- data/vendor/crispasr/ggml/src/ggml-backend-impl.h +275 -0
- data/vendor/crispasr/ggml/src/ggml-backend-meta.cpp +2145 -0
- data/vendor/crispasr/ggml/src/ggml-backend-reg.cpp +586 -0
- data/vendor/crispasr/ggml/src/ggml-backend.cpp +2437 -0
- data/vendor/crispasr/ggml/src/ggml-common.h +1900 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/CMakeLists.txt +718 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/amx/amx.cpp +249 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/amx/amx.h +8 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/amx/common.h +115 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/amx/mmq.cpp +2512 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/amx/mmq.h +10 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/arch/arm/cpu-feats.cpp +98 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/arch/arm/quants.c +4244 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/arch/arm/repack.cpp +5156 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/arch/loongarch/quants.c +2158 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/arch/powerpc/cpu-feats.cpp +82 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/arch/powerpc/quants.c +2304 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/arch/riscv/cpu-feats.cpp +38 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/arch/riscv/quants.c +4455 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/arch/riscv/repack.cpp +1703 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/arch/s390/cpu-feats.cpp +50 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/arch/s390/quants.c +1465 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/arch/wasm/quants.c +1220 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/arch/x86/cpu-feats.cpp +327 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/arch/x86/quants.c +3970 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/arch/x86/repack.cpp +6407 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/arch-fallback.h +349 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/binary-ops.cpp +154 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/binary-ops.h +16 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/cmake/FindSIMD.cmake +100 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/common.h +95 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/ggml-cpu-impl.h +539 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/ggml-cpu.c +3791 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/ggml-cpu.cpp +703 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/hbm.cpp +55 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/hbm.h +8 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/kleidiai/kernels.cpp +939 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/kleidiai/kernels.h +90 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/kleidiai/kleidiai.cpp +1513 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/kleidiai/kleidiai.h +17 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/llamafile/sgemm.cpp +4051 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/llamafile/sgemm.h +25 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/ops.cpp +11662 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/ops.h +121 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/quants.c +1288 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/quants.h +103 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/repack.cpp +4836 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/repack.h +245 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/simd-gemm.h +226 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/simd-mappings.h +1329 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/spacemit/ime.cpp +1025 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/spacemit/ime.h +13 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/spacemit/ime1_kernels.cpp +3196 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/spacemit/ime_kernels.h +26 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/traits.cpp +36 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/traits.h +38 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/unary-ops.cpp +336 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/unary-ops.h +35 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/vec.cpp +681 -0
- data/vendor/crispasr/ggml/src/ggml-cpu/vec.h +1606 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/CMakeLists.txt +272 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/acc.cu +61 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/acc.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/add-id.cu +58 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/add-id.cuh +3 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/arange.cu +34 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/arange.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/argmax.cu +91 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/argmax.cuh +3 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/argsort.cu +265 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/argsort.cuh +19 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/binbcast.cu +534 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/binbcast.cuh +12 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/clamp.cu +45 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/clamp.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/col2im-1d.cu +81 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/col2im-1d.cuh +3 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/common.cuh +1489 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/concat.cu +204 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/concat.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/conv-transpose-1d.cu +97 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/conv-transpose-1d.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/conv2d-dw.cu +161 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/conv2d-dw.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/conv2d-transpose.cu +115 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/conv2d-transpose.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/conv2d.cu +166 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/conv2d.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/convert.cu +892 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/convert.cuh +66 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/count-equal.cu +64 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/count-equal.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/cp-async.cuh +57 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/cpy-utils.cuh +217 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/cpy.cu +581 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/cpy.cuh +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/cross-entropy-loss.cu +177 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/cross-entropy-loss.cuh +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/cumsum.cu +307 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/cumsum.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/dequantize.cuh +99 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/diag.cu +77 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/diag.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/diagmask.cu +40 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/diagmask.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/fattn-common.cuh +1212 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/fattn-mma-f16.cuh +1860 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/fattn-tile.cu +57 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/fattn-tile.cuh +1309 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/fattn-vec.cuh +600 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/fattn-wmma-f16.cu +696 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/fattn-wmma-f16.cuh +51 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/fattn.cu +620 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/fattn.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/fill.cu +37 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/fill.cuh +3 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/gated_delta_net.cu +273 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/gated_delta_net.cuh +4 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/getrows.cu +332 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/getrows.cuh +15 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/ggml-cuda.cu +5580 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/gla.cu +93 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/gla.cuh +3 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/im2col.cu +274 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/im2col.cuh +6 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/mean.cu +75 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/mean.cuh +3 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/mma.cuh +1333 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/mmf.cu +191 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/mmf.cuh +908 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/mmid.cu +164 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/mmid.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/mmq.cu +372 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/mmq.cuh +4175 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/mmvf.cu +862 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/mmvf.cuh +14 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/mmvq.cu +1161 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/mmvq.cuh +16 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/norm.cu +756 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/norm.cuh +20 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/opt-step-adamw.cu +78 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/opt-step-adamw.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/opt-step-sgd.cu +49 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/opt-step-sgd.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/out-prod.cu +68 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/out-prod.cuh +3 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/pad.cu +106 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/pad.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/pad_reflect_1d.cu +91 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/pad_reflect_1d.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/pool2d.cu +94 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/pool2d.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/quantize.cu +443 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/quantize.cuh +41 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/reduce_rows.cuh +39 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/roll.cu +67 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/roll.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/rope.cu +665 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/rope.cuh +9 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/scale.cu +34 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/scale.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/set-rows.cu +330 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/set-rows.cuh +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/set.cu +39 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/set.cuh +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/softcap.cu +34 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/softcap.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/softmax.cu +472 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/softmax.cuh +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/solve_tri.cu +275 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/solve_tri.cuh +3 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/ssm-conv.cu +197 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/ssm-conv.cuh +3 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/ssm-scan.cu +342 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/ssm-scan.cuh +3 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/sum.cu +41 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/sum.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/sumrows.cu +43 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/sumrows.cuh +4 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_1-ncols2_16.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_1-ncols2_32.cu +6 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_1-ncols2_8.cu +11 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_16-ncols2_1.cu +10 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_16-ncols2_2.cu +10 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_16-ncols2_4.cu +12 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_2-ncols2_16.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_2-ncols2_32.cu +6 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_2-ncols2_4.cu +12 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_2-ncols2_8.cu +11 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_32-ncols2_1.cu +10 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_32-ncols2_2.cu +10 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_4-ncols2_16.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_4-ncols2_2.cu +10 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_4-ncols2_4.cu +12 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_4-ncols2_8.cu +11 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_64-ncols2_1.cu +10 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_8-ncols2_1.cu +10 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_8-ncols2_2.cu +10 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_8-ncols2_4.cu +12 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-mma-f16-instance-ncols1_8-ncols2_8.cu +11 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq112-dv112.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq128-dv128.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq256-dv256.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq320-dv256.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq40-dv40.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq512-dv512.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq576-dv512.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq64-dv64.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq72-dv72.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq80-dv80.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq96-dv96.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-bf16-bf16.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-bf16-f16.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-bf16-q4_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-bf16-q4_1.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-bf16-q5_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-bf16-q5_1.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-bf16-q8_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-f16-bf16.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-f16-f16.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-f16-q4_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-f16-q4_1.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-f16-q5_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-f16-q5_1.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-f16-q8_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_0-bf16.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_0-f16.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_0-q4_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_0-q4_1.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_0-q5_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_0-q5_1.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_0-q8_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_1-bf16.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_1-f16.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_1-q4_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_1-q4_1.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_1-q5_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_1-q5_1.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q4_1-q8_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_0-bf16.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_0-f16.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_0-q4_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_0-q4_1.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_0-q5_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_0-q5_1.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_0-q8_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_1-bf16.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_1-f16.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_1-q4_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_1-q4_1.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_1-q5_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_1-q5_1.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q5_1-q8_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q8_0-bf16.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q8_0-f16.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q8_0-q4_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q8_0-q4_1.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q8_0-q5_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q8_0-q5_1.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/fattn-vec-instance-q8_0-q8_0.cu +7 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_1.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_10.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_11.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_12.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_13.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_14.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_15.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_16.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_2.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_3.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_4.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_5.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_6.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_7.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_8.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmf-instance-ncols_9.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-iq1_s.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-iq2_s.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-iq2_xs.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-iq2_xxs.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-iq3_s.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-iq3_xxs.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-iq4_nl.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-iq4_xs.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-mxfp4.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-nvfp4.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-q1_0.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-q2_k.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-q3_k.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-q4_0.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-q4_1.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-q4_k.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-q5_0.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-q5_1.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-q5_k.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-q6_k.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/template-instances/mmq-instance-q8_0.cu +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/top-k.cu +95 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/top-k.cuh +3 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/topk-moe.cu +415 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/topk-moe.cuh +27 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/tri.cu +136 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/tri.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/tsembd.cu +47 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/tsembd.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/unary.cu +644 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/unary.cuh +116 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/upscale.cu +293 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/upscale.cuh +5 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/vecdotq.cuh +1317 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/vendors/cuda.h +28 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/vendors/hip.h +306 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/vendors/musa.h +148 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/wkv.cu +199 -0
- data/vendor/crispasr/ggml/src/ggml-cuda/wkv.cuh +7 -0
- data/vendor/crispasr/ggml/src/ggml-impl.h +783 -0
- data/vendor/crispasr/ggml/src/ggml-metal/CMakeLists.txt +124 -0
- data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal-common.cpp +457 -0
- data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal-common.h +52 -0
- data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal-context.h +41 -0
- data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal-context.m +778 -0
- data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal-device.cpp +2117 -0
- data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal-device.h +299 -0
- data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal-device.m +2093 -0
- data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal-impl.h +1267 -0
- data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal-ops.cpp +5023 -0
- data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal-ops.h +111 -0
- data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal.cpp +954 -0
- data/vendor/crispasr/ggml/src/ggml-metal/ggml-metal.metal +11756 -0
- data/vendor/crispasr/ggml/src/ggml-opt.cpp +1094 -0
- data/vendor/crispasr/ggml/src/ggml-quants.c +5491 -0
- data/vendor/crispasr/ggml/src/ggml-quants.h +112 -0
- data/vendor/crispasr/ggml/src/ggml-threading.cpp +12 -0
- data/vendor/crispasr/ggml/src/ggml-threading.h +14 -0
- data/vendor/crispasr/ggml/src/ggml.c +7925 -0
- data/vendor/crispasr/ggml/src/ggml.cpp +26 -0
- data/vendor/crispasr/ggml/src/gguf.cpp +1556 -0
- data/vendor/crispasr/src/cohere-arch.h +137 -0
- data/vendor/crispasr/src/cohere.cpp +5642 -0
- data/vendor/crispasr/src/cohere.h +327 -0
- data/vendor/crispasr/src/cohere_batch_planner.h +82 -0
- data/vendor/crispasr/src/cohere_chunking.h +64 -0
- data/vendor/crispasr/src/cohere_decoder_batch_layout.h +60 -0
- data/vendor/crispasr/src/cohere_encoder_padded_layout.h +27 -0
- data/vendor/crispasr/src/cohere_frontend.cpp +189 -0
- data/vendor/crispasr/src/cohere_frontend.h +31 -0
- data/vendor/crispasr/src/cohere_ragged_controller.h +149 -0
- data/vendor/crispasr/src/cohere_token_renderer.h +181 -0
- data/vendor/crispasr/src/core/attention.h +924 -0
- data/vendor/crispasr/src/core/audio_chunking.h +97 -0
- data/vendor/crispasr/src/core/beam_decode.h +486 -0
- data/vendor/crispasr/src/core/cpu_ops.h +135 -0
- data/vendor/crispasr/src/core/gguf_loader.cpp +1021 -0
- data/vendor/crispasr/src/core/gguf_loader.h +216 -0
- data/vendor/crispasr/src/core/gpu_backend_pref.h +119 -0
- data/vendor/crispasr/src/core/mel.cpp +519 -0
- data/vendor/crispasr/src/core/mel.h +265 -0
- data/vendor/crispasr/src/core/ngram_loop_fix.h +173 -0
- data/vendor/crispasr/src/core/repetition_loop_guard.h +54 -0
- data/vendor/crispasr/src/crispasr_imatrix.cpp +255 -0
- data/vendor/crispasr/src/crispasr_imatrix.h +38 -0
- metadata +596 -0
|
@@ -0,0 +1,1416 @@
|
|
|
1
|
+
// Subprocess-free audio decoding through FFmpeg's public shared-library ABI.
|
|
2
|
+
//
|
|
3
|
+
// The source gem intentionally does not require FFmpeg development headers:
|
|
4
|
+
// Linux distributions commonly install the runtime libraries with the ffmpeg
|
|
5
|
+
// executable but omit the -dev packages. This adapter uses only stable public
|
|
6
|
+
// functions and the small, documented prefixes of AVFormatContext, AVPacket,
|
|
7
|
+
// AVFrame, AVStream, and AVCodecParameters needed for decoding. Public field
|
|
8
|
+
// offsets that changed at an FFmpeg major boundary are selected only after all
|
|
9
|
+
// four runtime library versions have been verified as a compatible tuple.
|
|
10
|
+
|
|
11
|
+
#include <algorithm>
|
|
12
|
+
#include <atomic>
|
|
13
|
+
#include <cerrno>
|
|
14
|
+
#include <chrono>
|
|
15
|
+
#include <cmath>
|
|
16
|
+
#include <cstddef>
|
|
17
|
+
#include <cstdint>
|
|
18
|
+
#include <cstdio>
|
|
19
|
+
#include <cstdlib>
|
|
20
|
+
#include <cstring>
|
|
21
|
+
#include <exception>
|
|
22
|
+
#include <limits>
|
|
23
|
+
#include <mutex>
|
|
24
|
+
#include <new>
|
|
25
|
+
#include <string>
|
|
26
|
+
#include <utility>
|
|
27
|
+
#include <vector>
|
|
28
|
+
|
|
29
|
+
#if defined(_WIN32)
|
|
30
|
+
#include <windows.h>
|
|
31
|
+
#else
|
|
32
|
+
#include <dlfcn.h>
|
|
33
|
+
#endif
|
|
34
|
+
|
|
35
|
+
#if defined(_WIN32)
|
|
36
|
+
#define COHERE_AUDIO_EXPORT extern "C" __declspec(dllexport)
|
|
37
|
+
#else
|
|
38
|
+
#define COHERE_AUDIO_EXPORT extern "C" __attribute__((visibility("default")))
|
|
39
|
+
#endif
|
|
40
|
+
|
|
41
|
+
namespace {
|
|
42
|
+
|
|
43
|
+
struct AVFormatContext;
|
|
44
|
+
struct AVIOContext;
|
|
45
|
+
struct AVCodecContext;
|
|
46
|
+
struct AVCodec;
|
|
47
|
+
struct AVCodecParameters;
|
|
48
|
+
struct AVPacket;
|
|
49
|
+
struct AVFrame;
|
|
50
|
+
struct SwrContext;
|
|
51
|
+
|
|
52
|
+
struct AVIOInterruptCB {
|
|
53
|
+
int (*callback)(void*);
|
|
54
|
+
void* opaque;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
struct AVChannelLayout {
|
|
58
|
+
int order;
|
|
59
|
+
int nb_channels;
|
|
60
|
+
union {
|
|
61
|
+
std::uint64_t mask;
|
|
62
|
+
void* map;
|
|
63
|
+
} details;
|
|
64
|
+
void* opaque;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// This prefix is unchanged across the supported libavformat major versions.
|
|
68
|
+
struct FormatContextPrefix {
|
|
69
|
+
const void* av_class;
|
|
70
|
+
const void* input_format;
|
|
71
|
+
const void* output_format;
|
|
72
|
+
void* private_data;
|
|
73
|
+
void* io_context;
|
|
74
|
+
int context_flags;
|
|
75
|
+
unsigned int stream_count;
|
|
76
|
+
void** streams;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// The fields through stream_index retain these offsets in FFmpeg 4 through 8.
|
|
80
|
+
struct PacketPrefix {
|
|
81
|
+
void* buffer;
|
|
82
|
+
std::int64_t pts;
|
|
83
|
+
std::int64_t dts;
|
|
84
|
+
std::uint8_t* data;
|
|
85
|
+
int size;
|
|
86
|
+
int stream_index;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// The fields through format retain these offsets in FFmpeg 4 through 8.
|
|
90
|
+
struct FramePrefix {
|
|
91
|
+
std::uint8_t* data[8];
|
|
92
|
+
int line_size[8];
|
|
93
|
+
std::uint8_t** extended_data;
|
|
94
|
+
int width;
|
|
95
|
+
int height;
|
|
96
|
+
int sample_count;
|
|
97
|
+
int format;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
struct Rational {
|
|
101
|
+
int numerator;
|
|
102
|
+
int denominator;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
static_assert(sizeof(void*) == 8, "cohere_audio currently targets 64-bit Ruby runtimes");
|
|
106
|
+
static_assert(offsetof(FormatContextPrefix, streams) == 48);
|
|
107
|
+
static_assert(offsetof(PacketPrefix, stream_index) == 36);
|
|
108
|
+
static_assert(offsetof(FramePrefix, extended_data) == 96);
|
|
109
|
+
static_assert(offsetof(FramePrefix, sample_count) == 112);
|
|
110
|
+
static_assert(offsetof(FramePrefix, format) == 116);
|
|
111
|
+
static_assert(sizeof(AVChannelLayout) == 24);
|
|
112
|
+
static_assert(sizeof(Rational) == 8);
|
|
113
|
+
|
|
114
|
+
constexpr int kAudioMediaType = 1;
|
|
115
|
+
constexpr int kFloatSampleFormat = 3;
|
|
116
|
+
constexpr int kAvioFlagRead = 1;
|
|
117
|
+
constexpr std::int64_t kMonoChannelMask = 0x4;
|
|
118
|
+
constexpr int kAvErrorEof = -541478725;
|
|
119
|
+
constexpr int kCancelledStatus = 6;
|
|
120
|
+
constexpr int kTimeoutStatus = 7;
|
|
121
|
+
constexpr std::uint64_t kMaximumScratchSamples = 16ULL * 1024ULL * 1024ULL;
|
|
122
|
+
constexpr std::chrono::seconds kDecodeTimeout(3600);
|
|
123
|
+
constexpr std::chrono::seconds kDurationProbeTimeout(30);
|
|
124
|
+
|
|
125
|
+
std::atomic<std::uint64_t> cancellation_generation{0};
|
|
126
|
+
|
|
127
|
+
class OperationDeadline {
|
|
128
|
+
public:
|
|
129
|
+
OperationDeadline(std::chrono::seconds timeout, const char* operation)
|
|
130
|
+
: generation_(cancellation_generation.load(std::memory_order_acquire)),
|
|
131
|
+
deadline_(std::chrono::steady_clock::now() + timeout),
|
|
132
|
+
timeout_(timeout),
|
|
133
|
+
operation_(operation) {}
|
|
134
|
+
|
|
135
|
+
int check(std::string& error) const {
|
|
136
|
+
if (cancellation_generation.load(std::memory_order_acquire) != generation_) {
|
|
137
|
+
error = std::string(operation_) + " was cancelled";
|
|
138
|
+
return kCancelledStatus;
|
|
139
|
+
}
|
|
140
|
+
if (std::chrono::steady_clock::now() >= deadline_) {
|
|
141
|
+
error = std::string(operation_) + " exceeded the " +
|
|
142
|
+
std::to_string(timeout_.count()) + "s timeout";
|
|
143
|
+
return kTimeoutStatus;
|
|
144
|
+
}
|
|
145
|
+
return 0;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
bool interrupted() const noexcept {
|
|
149
|
+
return cancellation_generation.load(std::memory_order_acquire) != generation_ ||
|
|
150
|
+
std::chrono::steady_clock::now() >= deadline_;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private:
|
|
154
|
+
std::uint64_t generation_;
|
|
155
|
+
std::chrono::steady_clock::time_point deadline_;
|
|
156
|
+
std::chrono::seconds timeout_;
|
|
157
|
+
const char* operation_;
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
using AvformatVersion = unsigned int (*)();
|
|
161
|
+
using AvcodecVersion = unsigned int (*)();
|
|
162
|
+
using AvutilVersion = unsigned int (*)();
|
|
163
|
+
using SwresampleVersion = unsigned int (*)();
|
|
164
|
+
using AvformatAllocContext = AVFormatContext* (*)();
|
|
165
|
+
using AvformatFreeContext = void (*)(AVFormatContext*);
|
|
166
|
+
using AvformatOpenInput = int (*)(AVFormatContext**, const char*, const void*, void**);
|
|
167
|
+
using AvformatFindStreamInfo = int (*)(AVFormatContext*, void**);
|
|
168
|
+
using AvReadFrame = int (*)(AVFormatContext*, AVPacket*);
|
|
169
|
+
using AvformatCloseInput = void (*)(AVFormatContext**);
|
|
170
|
+
using AvioOpen2 = int (*)(AVIOContext**, const char*, int, const AVIOInterruptCB*, void**);
|
|
171
|
+
using AvioClosep = int (*)(AVIOContext**);
|
|
172
|
+
using AvcodecFindDecoder = const AVCodec* (*)(int);
|
|
173
|
+
using AvcodecAllocContext3 = AVCodecContext* (*)(const AVCodec*);
|
|
174
|
+
using AvcodecParametersToContext = int (*)(AVCodecContext*, const AVCodecParameters*);
|
|
175
|
+
using AvcodecOpen2 = int (*)(AVCodecContext*, const AVCodec*, void**);
|
|
176
|
+
using AvcodecSendPacket = int (*)(AVCodecContext*, const AVPacket*);
|
|
177
|
+
using AvcodecReceiveFrame = int (*)(AVCodecContext*, AVFrame*);
|
|
178
|
+
using AvcodecFreeContext = void (*)(AVCodecContext**);
|
|
179
|
+
using AvPacketAlloc = AVPacket* (*)();
|
|
180
|
+
using AvPacketUnref = void (*)(AVPacket*);
|
|
181
|
+
using AvPacketFree = void (*)(AVPacket**);
|
|
182
|
+
using AvFrameAlloc = AVFrame* (*)();
|
|
183
|
+
using AvFrameUnref = void (*)(AVFrame*);
|
|
184
|
+
using AvFrameFree = void (*)(AVFrame**);
|
|
185
|
+
using AvStrerror = int (*)(int, char*, std::size_t);
|
|
186
|
+
using AvLogGetLevel = int (*)();
|
|
187
|
+
using AvLogSetLevel = void (*)(int);
|
|
188
|
+
using AvChannelLayoutDefault = void (*)(AVChannelLayout*, int);
|
|
189
|
+
using AvChannelLayoutCheck = int (*)(const AVChannelLayout*);
|
|
190
|
+
using AvChannelLayoutCompare = int (*)(const AVChannelLayout*, const AVChannelLayout*);
|
|
191
|
+
using AvChannelLayoutCopy = int (*)(AVChannelLayout*, const AVChannelLayout*);
|
|
192
|
+
using AvChannelLayoutUninit = void (*)(AVChannelLayout*);
|
|
193
|
+
using AvGetDefaultChannelLayout = std::int64_t (*)(int);
|
|
194
|
+
using SwrAllocSetOpts2 = int (*)(SwrContext**, const AVChannelLayout*, int, int,
|
|
195
|
+
const AVChannelLayout*, int, int, int, void*);
|
|
196
|
+
using SwrAllocSetOpts = SwrContext* (*)(SwrContext*, std::int64_t, int, int,
|
|
197
|
+
std::int64_t, int, int, int, void*);
|
|
198
|
+
using SwrInit = int (*)(SwrContext*);
|
|
199
|
+
using SwrConvert = int (*)(SwrContext*, std::uint8_t**, int,
|
|
200
|
+
const std::uint8_t**, int);
|
|
201
|
+
using SwrGetDelay = std::int64_t (*)(SwrContext*, std::int64_t);
|
|
202
|
+
using SwrFree = void (*)(SwrContext**);
|
|
203
|
+
|
|
204
|
+
struct LibraryTuple {
|
|
205
|
+
int format_major;
|
|
206
|
+
int codec_major;
|
|
207
|
+
int util_major;
|
|
208
|
+
int resample_major;
|
|
209
|
+
const char* format_name;
|
|
210
|
+
const char* codec_name;
|
|
211
|
+
const char* util_name;
|
|
212
|
+
const char* resample_name;
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
#if defined(__APPLE__)
|
|
216
|
+
constexpr LibraryTuple kLibraryTuples[] = {
|
|
217
|
+
{62, 62, 60, 6, "libavformat.62.dylib", "libavcodec.62.dylib", "libavutil.60.dylib", "libswresample.6.dylib"},
|
|
218
|
+
{61, 61, 59, 5, "libavformat.61.dylib", "libavcodec.61.dylib", "libavutil.59.dylib", "libswresample.5.dylib"},
|
|
219
|
+
{60, 60, 58, 4, "libavformat.60.dylib", "libavcodec.60.dylib", "libavutil.58.dylib", "libswresample.4.dylib"},
|
|
220
|
+
{59, 59, 57, 4, "libavformat.59.dylib", "libavcodec.59.dylib", "libavutil.57.dylib", "libswresample.4.dylib"},
|
|
221
|
+
{58, 58, 56, 3, "libavformat.58.dylib", "libavcodec.58.dylib", "libavutil.56.dylib", "libswresample.3.dylib"},
|
|
222
|
+
{0, 0, 0, 0, "libavformat.dylib", "libavcodec.dylib", "libavutil.dylib", "libswresample.dylib"},
|
|
223
|
+
};
|
|
224
|
+
#else
|
|
225
|
+
constexpr LibraryTuple kLibraryTuples[] = {
|
|
226
|
+
{62, 62, 60, 6, "libavformat.so.62", "libavcodec.so.62", "libavutil.so.60", "libswresample.so.6"},
|
|
227
|
+
{61, 61, 59, 5, "libavformat.so.61", "libavcodec.so.61", "libavutil.so.59", "libswresample.so.5"},
|
|
228
|
+
{60, 60, 58, 4, "libavformat.so.60", "libavcodec.so.60", "libavutil.so.58", "libswresample.so.4"},
|
|
229
|
+
{59, 59, 57, 4, "libavformat.so.59", "libavcodec.so.59", "libavutil.so.57", "libswresample.so.4"},
|
|
230
|
+
{58, 58, 56, 3, "libavformat.so.58", "libavcodec.so.58", "libavutil.so.56", "libswresample.so.3"},
|
|
231
|
+
{0, 0, 0, 0, "libavformat.so", "libavcodec.so", "libavutil.so", "libswresample.so"},
|
|
232
|
+
};
|
|
233
|
+
#endif
|
|
234
|
+
|
|
235
|
+
void set_message(char* destination, std::size_t capacity, const std::string& message) {
|
|
236
|
+
if (!destination || capacity == 0)
|
|
237
|
+
return;
|
|
238
|
+
const std::size_t length = std::min(capacity - 1, message.size());
|
|
239
|
+
if (length > 0)
|
|
240
|
+
std::memcpy(destination, message.data(), length);
|
|
241
|
+
destination[length] = '\0';
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
unsigned int major_version(unsigned int packed) {
|
|
245
|
+
return packed >> 16;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
#if defined(_WIN32)
|
|
249
|
+
using LibraryHandle = HMODULE;
|
|
250
|
+
|
|
251
|
+
LibraryHandle open_library(const char* name) {
|
|
252
|
+
return LoadLibraryA(name);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
void close_library(LibraryHandle handle) {
|
|
256
|
+
if (handle)
|
|
257
|
+
FreeLibrary(handle);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
void* load_symbol(LibraryHandle handle, const char* name) {
|
|
261
|
+
return reinterpret_cast<void*>(GetProcAddress(handle, name));
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
#else
|
|
265
|
+
using LibraryHandle = void*;
|
|
266
|
+
|
|
267
|
+
LibraryHandle open_library(const char* name) {
|
|
268
|
+
LibraryHandle handle = dlopen(name, RTLD_NOW | RTLD_LOCAL);
|
|
269
|
+
#if defined(__APPLE__)
|
|
270
|
+
if (handle || std::strchr(name, '/'))
|
|
271
|
+
return handle;
|
|
272
|
+
|
|
273
|
+
if (const char* homebrew_prefix = std::getenv("HOMEBREW_PREFIX")) {
|
|
274
|
+
if (*homebrew_prefix) {
|
|
275
|
+
const std::string path = std::string(homebrew_prefix) + "/opt/ffmpeg/lib/" + name;
|
|
276
|
+
handle = dlopen(path.c_str(), RTLD_NOW | RTLD_LOCAL);
|
|
277
|
+
if (handle)
|
|
278
|
+
return handle;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
constexpr const char* kLibraryDirectories[] = {
|
|
283
|
+
"/opt/homebrew/opt/ffmpeg/lib/",
|
|
284
|
+
"/usr/local/opt/ffmpeg/lib/",
|
|
285
|
+
"/opt/homebrew/lib/",
|
|
286
|
+
"/usr/local/lib/",
|
|
287
|
+
"/opt/local/lib/",
|
|
288
|
+
};
|
|
289
|
+
for (const char* directory : kLibraryDirectories) {
|
|
290
|
+
const std::string path = std::string(directory) + name;
|
|
291
|
+
handle = dlopen(path.c_str(), RTLD_NOW | RTLD_LOCAL);
|
|
292
|
+
if (handle)
|
|
293
|
+
return handle;
|
|
294
|
+
}
|
|
295
|
+
#endif
|
|
296
|
+
return handle;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
void close_library(LibraryHandle handle) {
|
|
300
|
+
if (handle)
|
|
301
|
+
dlclose(handle);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
void* load_symbol(LibraryHandle handle, const char* name) {
|
|
305
|
+
dlerror();
|
|
306
|
+
return dlsym(handle, name);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
#endif
|
|
310
|
+
|
|
311
|
+
template <typename Function>
|
|
312
|
+
Function symbol(LibraryHandle handle, const char* name) {
|
|
313
|
+
return reinterpret_cast<Function>(load_symbol(handle, name));
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
class Runtime {
|
|
317
|
+
public:
|
|
318
|
+
Runtime() {
|
|
319
|
+
load();
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
~Runtime() = default;
|
|
323
|
+
Runtime(const Runtime&) = delete;
|
|
324
|
+
Runtime& operator=(const Runtime&) = delete;
|
|
325
|
+
|
|
326
|
+
bool available = false;
|
|
327
|
+
std::string diagnostic;
|
|
328
|
+
int format_major = 0;
|
|
329
|
+
int codec_major = 0;
|
|
330
|
+
int util_major = 0;
|
|
331
|
+
int resample_major = 0;
|
|
332
|
+
|
|
333
|
+
AvformatOpenInput avformat_open_input = nullptr;
|
|
334
|
+
AvformatAllocContext avformat_alloc_context = nullptr;
|
|
335
|
+
AvformatFreeContext avformat_free_context = nullptr;
|
|
336
|
+
AvformatFindStreamInfo avformat_find_stream_info = nullptr;
|
|
337
|
+
AvReadFrame av_read_frame = nullptr;
|
|
338
|
+
AvformatCloseInput avformat_close_input = nullptr;
|
|
339
|
+
AvioOpen2 avio_open2 = nullptr;
|
|
340
|
+
AvioClosep avio_closep = nullptr;
|
|
341
|
+
AvcodecFindDecoder avcodec_find_decoder = nullptr;
|
|
342
|
+
AvcodecAllocContext3 avcodec_alloc_context3 = nullptr;
|
|
343
|
+
AvcodecParametersToContext avcodec_parameters_to_context = nullptr;
|
|
344
|
+
AvcodecOpen2 avcodec_open2 = nullptr;
|
|
345
|
+
AvcodecSendPacket avcodec_send_packet = nullptr;
|
|
346
|
+
AvcodecReceiveFrame avcodec_receive_frame = nullptr;
|
|
347
|
+
AvcodecFreeContext avcodec_free_context = nullptr;
|
|
348
|
+
AvPacketAlloc av_packet_alloc = nullptr;
|
|
349
|
+
AvPacketUnref av_packet_unref = nullptr;
|
|
350
|
+
AvPacketFree av_packet_free = nullptr;
|
|
351
|
+
AvFrameAlloc av_frame_alloc = nullptr;
|
|
352
|
+
AvFrameUnref av_frame_unref = nullptr;
|
|
353
|
+
AvFrameFree av_frame_free = nullptr;
|
|
354
|
+
AvStrerror av_strerror = nullptr;
|
|
355
|
+
AvLogGetLevel av_log_get_level = nullptr;
|
|
356
|
+
AvLogSetLevel av_log_set_level = nullptr;
|
|
357
|
+
AvChannelLayoutDefault av_channel_layout_default = nullptr;
|
|
358
|
+
AvChannelLayoutCheck av_channel_layout_check = nullptr;
|
|
359
|
+
AvChannelLayoutCompare av_channel_layout_compare = nullptr;
|
|
360
|
+
AvChannelLayoutCopy av_channel_layout_copy = nullptr;
|
|
361
|
+
AvChannelLayoutUninit av_channel_layout_uninit = nullptr;
|
|
362
|
+
AvGetDefaultChannelLayout av_get_default_channel_layout = nullptr;
|
|
363
|
+
SwrAllocSetOpts2 swr_alloc_set_opts2 = nullptr;
|
|
364
|
+
SwrAllocSetOpts swr_alloc_set_opts = nullptr;
|
|
365
|
+
SwrInit swr_init = nullptr;
|
|
366
|
+
SwrConvert swr_convert = nullptr;
|
|
367
|
+
SwrGetDelay swr_get_delay = nullptr;
|
|
368
|
+
SwrFree swr_free = nullptr;
|
|
369
|
+
|
|
370
|
+
std::string error_text(int code) const {
|
|
371
|
+
char buffer[256] = {};
|
|
372
|
+
if (av_strerror && av_strerror(code, buffer, sizeof(buffer)) == 0)
|
|
373
|
+
return buffer;
|
|
374
|
+
return "FFmpeg error " + std::to_string(code);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
private:
|
|
378
|
+
std::vector<LibraryHandle> handles_;
|
|
379
|
+
|
|
380
|
+
bool supported_versions(int format, int codec, int util, int resample) const {
|
|
381
|
+
if (format < 58 || format > 62 || codec != format)
|
|
382
|
+
return false;
|
|
383
|
+
const int expected_util = format - 2;
|
|
384
|
+
const int expected_resample = format == 58 ? 3 : (format <= 60 ? 4 : format - 56);
|
|
385
|
+
return util == expected_util && resample == expected_resample;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
bool bind_required() {
|
|
389
|
+
#define BIND_FROM(member, handle, name) \
|
|
390
|
+
do { \
|
|
391
|
+
member = symbol<decltype(member)>(handle, name); \
|
|
392
|
+
if (!member) { \
|
|
393
|
+
diagnostic = std::string("FFmpeg runtime is missing ") + name; \
|
|
394
|
+
return false; \
|
|
395
|
+
} \
|
|
396
|
+
} while (false)
|
|
397
|
+
|
|
398
|
+
const auto format = handles_[0];
|
|
399
|
+
const auto codec = handles_[1];
|
|
400
|
+
const auto util = handles_[2];
|
|
401
|
+
const auto resample = handles_[3];
|
|
402
|
+
BIND_FROM(avformat_alloc_context, format, "avformat_alloc_context");
|
|
403
|
+
BIND_FROM(avformat_free_context, format, "avformat_free_context");
|
|
404
|
+
BIND_FROM(avformat_open_input, format, "avformat_open_input");
|
|
405
|
+
BIND_FROM(avformat_find_stream_info, format, "avformat_find_stream_info");
|
|
406
|
+
BIND_FROM(av_read_frame, format, "av_read_frame");
|
|
407
|
+
BIND_FROM(avformat_close_input, format, "avformat_close_input");
|
|
408
|
+
BIND_FROM(avio_open2, format, "avio_open2");
|
|
409
|
+
BIND_FROM(avio_closep, format, "avio_closep");
|
|
410
|
+
BIND_FROM(avcodec_find_decoder, codec, "avcodec_find_decoder");
|
|
411
|
+
BIND_FROM(avcodec_alloc_context3, codec, "avcodec_alloc_context3");
|
|
412
|
+
BIND_FROM(avcodec_parameters_to_context, codec, "avcodec_parameters_to_context");
|
|
413
|
+
BIND_FROM(avcodec_open2, codec, "avcodec_open2");
|
|
414
|
+
BIND_FROM(avcodec_send_packet, codec, "avcodec_send_packet");
|
|
415
|
+
BIND_FROM(avcodec_receive_frame, codec, "avcodec_receive_frame");
|
|
416
|
+
BIND_FROM(avcodec_free_context, codec, "avcodec_free_context");
|
|
417
|
+
BIND_FROM(av_packet_alloc, codec, "av_packet_alloc");
|
|
418
|
+
BIND_FROM(av_packet_unref, codec, "av_packet_unref");
|
|
419
|
+
BIND_FROM(av_packet_free, codec, "av_packet_free");
|
|
420
|
+
BIND_FROM(av_frame_alloc, util, "av_frame_alloc");
|
|
421
|
+
BIND_FROM(av_frame_unref, util, "av_frame_unref");
|
|
422
|
+
BIND_FROM(av_frame_free, util, "av_frame_free");
|
|
423
|
+
BIND_FROM(av_strerror, util, "av_strerror");
|
|
424
|
+
BIND_FROM(av_log_get_level, util, "av_log_get_level");
|
|
425
|
+
BIND_FROM(av_log_set_level, util, "av_log_set_level");
|
|
426
|
+
BIND_FROM(swr_init, resample, "swr_init");
|
|
427
|
+
BIND_FROM(swr_convert, resample, "swr_convert");
|
|
428
|
+
BIND_FROM(swr_get_delay, resample, "swr_get_delay");
|
|
429
|
+
BIND_FROM(swr_free, resample, "swr_free");
|
|
430
|
+
|
|
431
|
+
if (util_major >= 57) {
|
|
432
|
+
BIND_FROM(av_channel_layout_default, util, "av_channel_layout_default");
|
|
433
|
+
BIND_FROM(av_channel_layout_check, util, "av_channel_layout_check");
|
|
434
|
+
BIND_FROM(av_channel_layout_compare, util, "av_channel_layout_compare");
|
|
435
|
+
BIND_FROM(av_channel_layout_copy, util, "av_channel_layout_copy");
|
|
436
|
+
BIND_FROM(av_channel_layout_uninit, util, "av_channel_layout_uninit");
|
|
437
|
+
BIND_FROM(swr_alloc_set_opts2, resample, "swr_alloc_set_opts2");
|
|
438
|
+
} else {
|
|
439
|
+
BIND_FROM(av_get_default_channel_layout, util, "av_get_default_channel_layout");
|
|
440
|
+
BIND_FROM(swr_alloc_set_opts, resample, "swr_alloc_set_opts");
|
|
441
|
+
}
|
|
442
|
+
#undef BIND_FROM
|
|
443
|
+
return true;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
bool try_tuple(const LibraryTuple& tuple) {
|
|
447
|
+
std::vector<LibraryHandle> opened;
|
|
448
|
+
for (const char* name : {tuple.format_name, tuple.codec_name, tuple.util_name, tuple.resample_name}) {
|
|
449
|
+
LibraryHandle handle = open_library(name);
|
|
450
|
+
if (!handle) {
|
|
451
|
+
for (auto iterator = opened.rbegin(); iterator != opened.rend(); ++iterator)
|
|
452
|
+
close_library(*iterator);
|
|
453
|
+
return false;
|
|
454
|
+
}
|
|
455
|
+
opened.push_back(handle);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
const auto format_version = symbol<AvformatVersion>(opened[0], "avformat_version");
|
|
459
|
+
const auto codec_version = symbol<AvcodecVersion>(opened[1], "avcodec_version");
|
|
460
|
+
const auto util_version = symbol<AvutilVersion>(opened[2], "avutil_version");
|
|
461
|
+
const auto resample_version = symbol<SwresampleVersion>(opened[3], "swresample_version");
|
|
462
|
+
if (!format_version || !codec_version || !util_version || !resample_version) {
|
|
463
|
+
for (auto iterator = opened.rbegin(); iterator != opened.rend(); ++iterator)
|
|
464
|
+
close_library(*iterator);
|
|
465
|
+
return false;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
const int actual_format = static_cast<int>(major_version(format_version()));
|
|
469
|
+
const int actual_codec = static_cast<int>(major_version(codec_version()));
|
|
470
|
+
const int actual_util = static_cast<int>(major_version(util_version()));
|
|
471
|
+
const int actual_resample = static_cast<int>(major_version(resample_version()));
|
|
472
|
+
const bool expected = tuple.format_major == 0 ||
|
|
473
|
+
(actual_format == tuple.format_major && actual_codec == tuple.codec_major &&
|
|
474
|
+
actual_util == tuple.util_major && actual_resample == tuple.resample_major);
|
|
475
|
+
if (!expected || !supported_versions(actual_format, actual_codec, actual_util, actual_resample)) {
|
|
476
|
+
for (auto iterator = opened.rbegin(); iterator != opened.rend(); ++iterator)
|
|
477
|
+
close_library(*iterator);
|
|
478
|
+
return false;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
handles_ = std::move(opened);
|
|
482
|
+
format_major = actual_format;
|
|
483
|
+
codec_major = actual_codec;
|
|
484
|
+
util_major = actual_util;
|
|
485
|
+
resample_major = actual_resample;
|
|
486
|
+
if (!bind_required()) {
|
|
487
|
+
for (auto iterator = handles_.rbegin(); iterator != handles_.rend(); ++iterator)
|
|
488
|
+
close_library(*iterator);
|
|
489
|
+
handles_.clear();
|
|
490
|
+
return false;
|
|
491
|
+
}
|
|
492
|
+
return true;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
void load() {
|
|
496
|
+
const char* explicit_format = std::getenv("COHERE_TRANSCRIBE_AVFORMAT_LIBRARY");
|
|
497
|
+
const char* explicit_codec = std::getenv("COHERE_TRANSCRIBE_AVCODEC_LIBRARY");
|
|
498
|
+
const char* explicit_util = std::getenv("COHERE_TRANSCRIBE_AVUTIL_LIBRARY");
|
|
499
|
+
const char* explicit_resample = std::getenv("COHERE_TRANSCRIBE_SWRESAMPLE_LIBRARY");
|
|
500
|
+
const bool any_explicit = explicit_format || explicit_codec || explicit_util || explicit_resample;
|
|
501
|
+
if (any_explicit) {
|
|
502
|
+
if (!explicit_format || !explicit_codec || !explicit_util || !explicit_resample) {
|
|
503
|
+
diagnostic = "all four COHERE_TRANSCRIBE_AV* library overrides must be set together";
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
const LibraryTuple explicit_tuple = {
|
|
507
|
+
0, 0, 0, 0, explicit_format, explicit_codec, explicit_util, explicit_resample
|
|
508
|
+
};
|
|
509
|
+
if (!try_tuple(explicit_tuple)) {
|
|
510
|
+
diagnostic = "the explicit FFmpeg shared-library tuple is unavailable or incompatible";
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
} else {
|
|
514
|
+
bool loaded = false;
|
|
515
|
+
for (const auto& tuple : kLibraryTuples) {
|
|
516
|
+
if (try_tuple(tuple)) {
|
|
517
|
+
loaded = true;
|
|
518
|
+
break;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
if (!loaded) {
|
|
522
|
+
diagnostic = "no compatible FFmpeg 4-8 shared-library tuple was found";
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
available = true;
|
|
528
|
+
diagnostic = "FFmpeg ABI avformat " + std::to_string(format_major) +
|
|
529
|
+
", avcodec " + std::to_string(codec_major) +
|
|
530
|
+
", avutil " + std::to_string(util_major) +
|
|
531
|
+
", swresample " + std::to_string(resample_major);
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
Runtime& runtime() {
|
|
536
|
+
static Runtime instance;
|
|
537
|
+
return instance;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// FFmpeg logging is process-global. A reference-counted guard temporarily
|
|
541
|
+
// matches the reference CLI's captured stderr behavior without serializing
|
|
542
|
+
// parallel decodes or permanently changing the embedding application's level.
|
|
543
|
+
class ErrorLogGuard {
|
|
544
|
+
public:
|
|
545
|
+
explicit ErrorLogGuard(Runtime& runtime) : api_(runtime) {
|
|
546
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
547
|
+
if (active_++ == 0) {
|
|
548
|
+
saved_level_ = api_.av_log_get_level();
|
|
549
|
+
if (saved_level_ > -8)
|
|
550
|
+
api_.av_log_set_level(-8);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
~ErrorLogGuard() {
|
|
555
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
556
|
+
if (--active_ == 0 && saved_level_ > -8)
|
|
557
|
+
api_.av_log_set_level(saved_level_);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
ErrorLogGuard(const ErrorLogGuard&) = delete;
|
|
561
|
+
ErrorLogGuard& operator=(const ErrorLogGuard&) = delete;
|
|
562
|
+
|
|
563
|
+
private:
|
|
564
|
+
Runtime& api_;
|
|
565
|
+
static std::mutex mutex_;
|
|
566
|
+
static int active_;
|
|
567
|
+
static int saved_level_;
|
|
568
|
+
};
|
|
569
|
+
|
|
570
|
+
std::mutex ErrorLogGuard::mutex_;
|
|
571
|
+
int ErrorLogGuard::active_ = 0;
|
|
572
|
+
int ErrorLogGuard::saved_level_ = -8;
|
|
573
|
+
|
|
574
|
+
std::size_t stream_parameters_offset(int format_major) {
|
|
575
|
+
return format_major >= 60 ? 16 : 208;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
std::size_t stream_time_base_offset(int format_major) {
|
|
579
|
+
// The public AVStream layouts place time_base at byte 24 in 58-59 and
|
|
580
|
+
// byte 32 after codecpar moved to the prefix in 60-62.
|
|
581
|
+
return format_major >= 60 ? 32 : 24;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
std::size_t format_start_time_offset(int format_major) {
|
|
585
|
+
// AVFormatContext retained its public prefix but changed the fields between
|
|
586
|
+
// streams and url at the FFmpeg 5 and 7 major boundaries.
|
|
587
|
+
if (format_major == 58)
|
|
588
|
+
return 1088; // legacy filename[1024], then url
|
|
589
|
+
if (format_major <= 60)
|
|
590
|
+
return 64; // url
|
|
591
|
+
return 96; // stream groups, chapters, then url
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
std::size_t frame_sample_rate_offset(int util_major) {
|
|
595
|
+
switch (util_major) {
|
|
596
|
+
case 56: return 272;
|
|
597
|
+
case 57:
|
|
598
|
+
case 58: return 208;
|
|
599
|
+
case 59: return 192;
|
|
600
|
+
case 60: return 180;
|
|
601
|
+
default: return 0;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
std::size_t frame_channel_layout_offset(int util_major) {
|
|
606
|
+
switch (util_major) {
|
|
607
|
+
case 57:
|
|
608
|
+
case 58: return 448;
|
|
609
|
+
case 59: return 408;
|
|
610
|
+
case 60: return 384;
|
|
611
|
+
default: return 0;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
template <typename Value>
|
|
616
|
+
Value field_at(const void* object, std::size_t offset) {
|
|
617
|
+
Value value{};
|
|
618
|
+
std::memcpy(&value, static_cast<const std::uint8_t*>(object) + offset, sizeof(value));
|
|
619
|
+
return value;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
AVCodecParameters* parameters_for_stream(Runtime& api, AVFormatContext* context, int stream_index) {
|
|
623
|
+
const auto* prefix = reinterpret_cast<const FormatContextPrefix*>(context);
|
|
624
|
+
if (!prefix || stream_index < 0 || static_cast<unsigned int>(stream_index) >= prefix->stream_count ||
|
|
625
|
+
!prefix->streams || !prefix->streams[stream_index]) {
|
|
626
|
+
return nullptr;
|
|
627
|
+
}
|
|
628
|
+
return field_at<AVCodecParameters*>(
|
|
629
|
+
prefix->streams[stream_index], stream_parameters_offset(api.format_major));
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
int first_audio_stream(Runtime& api, AVFormatContext* context, AVCodecParameters** parameters) {
|
|
633
|
+
const auto* prefix = reinterpret_cast<const FormatContextPrefix*>(context);
|
|
634
|
+
if (!prefix || !prefix->streams || prefix->stream_count > 1'000'000)
|
|
635
|
+
return -1;
|
|
636
|
+
for (unsigned int index = 0; index < prefix->stream_count; ++index) {
|
|
637
|
+
AVCodecParameters* candidate = parameters_for_stream(api, context, static_cast<int>(index));
|
|
638
|
+
if (!candidate)
|
|
639
|
+
continue;
|
|
640
|
+
const int media_type = field_at<int>(candidate, 0);
|
|
641
|
+
if (media_type == kAudioMediaType) {
|
|
642
|
+
*parameters = candidate;
|
|
643
|
+
return static_cast<int>(index);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
return -1;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
double duration_for_stream(Runtime& api, AVFormatContext* context, int stream_index) {
|
|
650
|
+
const auto* prefix = reinterpret_cast<const FormatContextPrefix*>(context);
|
|
651
|
+
if (!prefix || !prefix->streams || stream_index < 0 ||
|
|
652
|
+
static_cast<unsigned int>(stream_index) >= prefix->stream_count ||
|
|
653
|
+
!prefix->streams[stream_index]) {
|
|
654
|
+
return -1.0;
|
|
655
|
+
}
|
|
656
|
+
const void* stream = prefix->streams[stream_index];
|
|
657
|
+
const std::size_t time_base_offset = stream_time_base_offset(api.format_major);
|
|
658
|
+
const Rational time_base = field_at<Rational>(stream, time_base_offset);
|
|
659
|
+
const std::int64_t raw_duration = field_at<std::int64_t>(stream, time_base_offset + 16);
|
|
660
|
+
if (raw_duration < 0 || time_base.numerator <= 0 || time_base.denominator <= 0)
|
|
661
|
+
return -1.0;
|
|
662
|
+
const double seconds = static_cast<double>(raw_duration) *
|
|
663
|
+
static_cast<double>(time_base.numerator) / static_cast<double>(time_base.denominator);
|
|
664
|
+
return std::isfinite(seconds) && seconds >= 0.0 ? seconds : -1.0;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
double duration_for_format(Runtime& api, AVFormatContext* context) {
|
|
668
|
+
const std::size_t start_offset = format_start_time_offset(api.format_major);
|
|
669
|
+
const std::int64_t raw_start = field_at<std::int64_t>(context, start_offset);
|
|
670
|
+
const std::int64_t raw_duration = field_at<std::int64_t>(context, start_offset + 8);
|
|
671
|
+
if (raw_duration < 0)
|
|
672
|
+
return -1.0;
|
|
673
|
+
|
|
674
|
+
double duration = static_cast<double>(raw_duration) / 1'000'000.0;
|
|
675
|
+
const double start = raw_start < 0 ? -1.0 : static_cast<double>(raw_start) / 1'000'000.0;
|
|
676
|
+
// Match the reference ffprobe fallback: some containers expose an
|
|
677
|
+
// absolute end timestamp as format duration when start_time is positive.
|
|
678
|
+
if (start > 0.0 && duration > start)
|
|
679
|
+
duration -= start;
|
|
680
|
+
return std::isfinite(duration) && duration >= 0.0 ? duration : -1.0;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
int interrupt_io(void* opaque) noexcept {
|
|
684
|
+
const auto* deadline = static_cast<const OperationDeadline*>(opaque);
|
|
685
|
+
return deadline && deadline->interrupted() ? 1 : 0;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// Pre-open the input through avio_open2 so FFmpeg receives a stable public
|
|
689
|
+
// AVIOInterruptCB without writing the version-dependent interrupt_callback
|
|
690
|
+
// field deep inside AVFormatContext. avformat_open_input detects the supplied
|
|
691
|
+
// pb and marks it as custom I/O; consequently we retain and close it ourselves.
|
|
692
|
+
struct DemuxState {
|
|
693
|
+
explicit DemuxState(Runtime& runtime) : api(runtime) {}
|
|
694
|
+
|
|
695
|
+
~DemuxState() {
|
|
696
|
+
if (format_opened && format)
|
|
697
|
+
api.avformat_close_input(&format);
|
|
698
|
+
else if (format)
|
|
699
|
+
api.avformat_free_context(format);
|
|
700
|
+
if (io)
|
|
701
|
+
api.avio_closep(&io);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
DemuxState(const DemuxState&) = delete;
|
|
705
|
+
DemuxState& operator=(const DemuxState&) = delete;
|
|
706
|
+
|
|
707
|
+
Runtime& api;
|
|
708
|
+
AVFormatContext* format = nullptr;
|
|
709
|
+
AVIOContext* io = nullptr;
|
|
710
|
+
bool format_opened = false;
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
int open_input(DemuxState& state, const char* path,
|
|
714
|
+
const OperationDeadline& deadline, std::string& error) {
|
|
715
|
+
int interrupted = deadline.check(error);
|
|
716
|
+
if (interrupted != 0)
|
|
717
|
+
return interrupted;
|
|
718
|
+
|
|
719
|
+
state.format = state.api.avformat_alloc_context();
|
|
720
|
+
if (!state.format) {
|
|
721
|
+
error = "could not allocate the FFmpeg input context";
|
|
722
|
+
return 5;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
const AVIOInterruptCB interrupt_callback = {interrupt_io, const_cast<OperationDeadline*>(&deadline)};
|
|
726
|
+
int result = state.api.avio_open2(
|
|
727
|
+
&state.io, path, kAvioFlagRead, &interrupt_callback, nullptr);
|
|
728
|
+
interrupted = deadline.check(error);
|
|
729
|
+
if (interrupted != 0)
|
|
730
|
+
return interrupted;
|
|
731
|
+
if (result < 0) {
|
|
732
|
+
error = "FFmpeg could not open the input: " + state.api.error_text(result);
|
|
733
|
+
return 3;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
std::memcpy(
|
|
737
|
+
static_cast<std::uint8_t*>(static_cast<void*>(state.format)) +
|
|
738
|
+
offsetof(FormatContextPrefix, io_context),
|
|
739
|
+
&state.io,
|
|
740
|
+
sizeof(state.io));
|
|
741
|
+
result = state.api.avformat_open_input(&state.format, path, nullptr, nullptr);
|
|
742
|
+
state.format_opened = result >= 0;
|
|
743
|
+
interrupted = deadline.check(error);
|
|
744
|
+
if (interrupted != 0)
|
|
745
|
+
return interrupted;
|
|
746
|
+
if (result < 0) {
|
|
747
|
+
error = "FFmpeg could not open the input: " + state.api.error_text(result);
|
|
748
|
+
return 3;
|
|
749
|
+
}
|
|
750
|
+
return 0;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
using DurationProbeState = DemuxState;
|
|
754
|
+
|
|
755
|
+
int probe_duration_file(const char* path, double* output_duration, std::string& error) {
|
|
756
|
+
Runtime& api = runtime();
|
|
757
|
+
if (!api.available) {
|
|
758
|
+
error = api.diagnostic;
|
|
759
|
+
return 1;
|
|
760
|
+
}
|
|
761
|
+
if (!path || path[0] == '\0' || !output_duration) {
|
|
762
|
+
error = "invalid native FFmpeg duration-probe arguments";
|
|
763
|
+
return 2;
|
|
764
|
+
}
|
|
765
|
+
*output_duration = -1.0;
|
|
766
|
+
|
|
767
|
+
OperationDeadline deadline(kDurationProbeTimeout, "FFmpeg duration probe");
|
|
768
|
+
ErrorLogGuard log_guard(api);
|
|
769
|
+
DurationProbeState state(api);
|
|
770
|
+
int interrupted = open_input(state, path, deadline, error);
|
|
771
|
+
if (interrupted != 0)
|
|
772
|
+
return interrupted;
|
|
773
|
+
const int result = api.avformat_find_stream_info(state.format, nullptr);
|
|
774
|
+
interrupted = deadline.check(error);
|
|
775
|
+
if (interrupted != 0)
|
|
776
|
+
return interrupted;
|
|
777
|
+
if (result < 0) {
|
|
778
|
+
error = "FFmpeg could not inspect the input streams: " + api.error_text(result);
|
|
779
|
+
return 3;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
AVCodecParameters* parameters = nullptr;
|
|
783
|
+
const int stream_index = first_audio_stream(api, state.format, ¶meters);
|
|
784
|
+
if (stream_index < 0 || !parameters) {
|
|
785
|
+
error = "FFmpeg input has no audio stream";
|
|
786
|
+
return 3;
|
|
787
|
+
}
|
|
788
|
+
double duration = duration_for_stream(api, state.format, stream_index);
|
|
789
|
+
if (duration < 0.0)
|
|
790
|
+
duration = duration_for_format(api, state.format);
|
|
791
|
+
*output_duration = duration;
|
|
792
|
+
return 0;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
class OutputBuffer {
|
|
796
|
+
public:
|
|
797
|
+
explicit OutputBuffer(std::uint64_t maximum_bytes)
|
|
798
|
+
: limited_(maximum_bytes != 0), maximum_samples_(maximum_bytes / sizeof(float)) {}
|
|
799
|
+
|
|
800
|
+
~OutputBuffer() {
|
|
801
|
+
std::free(data_);
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
OutputBuffer(const OutputBuffer&) = delete;
|
|
805
|
+
OutputBuffer& operator=(const OutputBuffer&) = delete;
|
|
806
|
+
|
|
807
|
+
int append(const float* samples, std::uint64_t count, std::string& error) {
|
|
808
|
+
if (count == 0)
|
|
809
|
+
return 0;
|
|
810
|
+
if (!samples || count > std::numeric_limits<std::uint64_t>::max() - size_) {
|
|
811
|
+
error = "decoded audio sample count overflowed";
|
|
812
|
+
return 3;
|
|
813
|
+
}
|
|
814
|
+
const std::uint64_t wanted = size_ + count;
|
|
815
|
+
if (limited_ && wanted > maximum_samples_) {
|
|
816
|
+
error = "FFmpeg output exceeded the configured decoded-audio memory limit";
|
|
817
|
+
return 4;
|
|
818
|
+
}
|
|
819
|
+
if (wanted > std::numeric_limits<std::size_t>::max() / sizeof(float)) {
|
|
820
|
+
error = "decoded audio is too large for this process";
|
|
821
|
+
return 5;
|
|
822
|
+
}
|
|
823
|
+
if (wanted > capacity_) {
|
|
824
|
+
const std::uint64_t maximum_capacity =
|
|
825
|
+
std::numeric_limits<std::size_t>::max() / sizeof(float);
|
|
826
|
+
std::uint64_t next = capacity_ == 0 ? 4096 : capacity_;
|
|
827
|
+
while (next < wanted && next <= maximum_capacity / 2)
|
|
828
|
+
next *= 2;
|
|
829
|
+
if (next < wanted)
|
|
830
|
+
next = wanted;
|
|
831
|
+
if (limited_)
|
|
832
|
+
next = std::min(next, maximum_samples_);
|
|
833
|
+
next = std::min(next, maximum_capacity);
|
|
834
|
+
void* replacement = std::realloc(data_, static_cast<std::size_t>(next) * sizeof(float));
|
|
835
|
+
if (!replacement) {
|
|
836
|
+
error = "could not allocate decoded audio";
|
|
837
|
+
return 5;
|
|
838
|
+
}
|
|
839
|
+
data_ = static_cast<float*>(replacement);
|
|
840
|
+
capacity_ = next;
|
|
841
|
+
}
|
|
842
|
+
std::memcpy(data_ + size_, samples, static_cast<std::size_t>(count) * sizeof(float));
|
|
843
|
+
size_ = wanted;
|
|
844
|
+
return 0;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
float* release() {
|
|
848
|
+
float* result = data_;
|
|
849
|
+
data_ = nullptr;
|
|
850
|
+
capacity_ = 0;
|
|
851
|
+
size_ = 0;
|
|
852
|
+
return result;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
std::uint64_t size() const {
|
|
856
|
+
return size_;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
private:
|
|
860
|
+
float* data_ = nullptr;
|
|
861
|
+
std::uint64_t size_ = 0;
|
|
862
|
+
std::uint64_t capacity_ = 0;
|
|
863
|
+
bool limited_ = false;
|
|
864
|
+
std::uint64_t maximum_samples_ = 0;
|
|
865
|
+
};
|
|
866
|
+
|
|
867
|
+
struct DecodeState : DemuxState {
|
|
868
|
+
explicit DecodeState(Runtime& runtime) : DemuxState(runtime) {}
|
|
869
|
+
|
|
870
|
+
~DecodeState() {
|
|
871
|
+
if (input_layout_owned && api.av_channel_layout_uninit)
|
|
872
|
+
api.av_channel_layout_uninit(&input_layout);
|
|
873
|
+
if (resampler)
|
|
874
|
+
api.swr_free(&resampler);
|
|
875
|
+
if (frame)
|
|
876
|
+
api.av_frame_free(&frame);
|
|
877
|
+
if (packet)
|
|
878
|
+
api.av_packet_free(&packet);
|
|
879
|
+
if (codec)
|
|
880
|
+
api.avcodec_free_context(&codec);
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
AVCodecContext* codec = nullptr;
|
|
884
|
+
AVPacket* packet = nullptr;
|
|
885
|
+
AVFrame* frame = nullptr;
|
|
886
|
+
SwrContext* resampler = nullptr;
|
|
887
|
+
int stream_index = -1;
|
|
888
|
+
int target_rate = 0;
|
|
889
|
+
int input_rate = 0;
|
|
890
|
+
int input_format = -1;
|
|
891
|
+
int input_channels = 0;
|
|
892
|
+
std::int64_t old_input_layout = 0;
|
|
893
|
+
AVChannelLayout input_layout{};
|
|
894
|
+
bool input_layout_owned = false;
|
|
895
|
+
const OperationDeadline* deadline = nullptr;
|
|
896
|
+
};
|
|
897
|
+
|
|
898
|
+
int frame_sample_rate(const DecodeState& state) {
|
|
899
|
+
const std::size_t offset = frame_sample_rate_offset(state.api.util_major);
|
|
900
|
+
return offset == 0 ? 0 : field_at<int>(state.frame, offset);
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
const AVChannelLayout* new_frame_layout(const DecodeState& state) {
|
|
904
|
+
const std::size_t offset = frame_channel_layout_offset(state.api.util_major);
|
|
905
|
+
if (offset == 0)
|
|
906
|
+
return nullptr;
|
|
907
|
+
return reinterpret_cast<const AVChannelLayout*>(
|
|
908
|
+
static_cast<const std::uint8_t*>(static_cast<const void*>(state.frame)) + offset);
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
int initialize_resampler(DecodeState& state, std::string& error) {
|
|
912
|
+
const auto* prefix = reinterpret_cast<const FramePrefix*>(state.frame);
|
|
913
|
+
state.input_format = prefix->format;
|
|
914
|
+
state.input_rate = frame_sample_rate(state);
|
|
915
|
+
if (state.input_format < 0 || state.input_rate <= 0 || state.input_rate > 1'000'000) {
|
|
916
|
+
error = "FFmpeg returned invalid decoded audio format metadata";
|
|
917
|
+
return 3;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
int result = 0;
|
|
921
|
+
if (state.api.util_major >= 57) {
|
|
922
|
+
const AVChannelLayout* source_layout = new_frame_layout(state);
|
|
923
|
+
AVChannelLayout fallback{};
|
|
924
|
+
bool fallback_initialized = false;
|
|
925
|
+
if (!source_layout || state.api.av_channel_layout_check(source_layout) != 1 ||
|
|
926
|
+
source_layout->nb_channels <= 0 || source_layout->nb_channels > 256) {
|
|
927
|
+
const int channels = source_layout ? source_layout->nb_channels : 0;
|
|
928
|
+
if (channels <= 0 || channels > 256) {
|
|
929
|
+
error = "FFmpeg returned an invalid decoded channel layout";
|
|
930
|
+
return 3;
|
|
931
|
+
}
|
|
932
|
+
state.api.av_channel_layout_default(&fallback, channels);
|
|
933
|
+
source_layout = &fallback;
|
|
934
|
+
fallback_initialized = true;
|
|
935
|
+
}
|
|
936
|
+
state.input_channels = source_layout->nb_channels;
|
|
937
|
+
if (state.api.av_channel_layout_copy(&state.input_layout, source_layout) < 0) {
|
|
938
|
+
if (fallback_initialized)
|
|
939
|
+
state.api.av_channel_layout_uninit(&fallback);
|
|
940
|
+
error = "could not retain the FFmpeg input channel layout";
|
|
941
|
+
return 5;
|
|
942
|
+
}
|
|
943
|
+
state.input_layout_owned = true;
|
|
944
|
+
|
|
945
|
+
AVChannelLayout output_layout{};
|
|
946
|
+
state.api.av_channel_layout_default(&output_layout, 1);
|
|
947
|
+
result = state.api.swr_alloc_set_opts2(
|
|
948
|
+
&state.resampler,
|
|
949
|
+
&output_layout,
|
|
950
|
+
kFloatSampleFormat,
|
|
951
|
+
state.target_rate,
|
|
952
|
+
source_layout,
|
|
953
|
+
state.input_format,
|
|
954
|
+
state.input_rate,
|
|
955
|
+
0,
|
|
956
|
+
nullptr);
|
|
957
|
+
state.api.av_channel_layout_uninit(&output_layout);
|
|
958
|
+
if (fallback_initialized)
|
|
959
|
+
state.api.av_channel_layout_uninit(&fallback);
|
|
960
|
+
} else {
|
|
961
|
+
state.old_input_layout = field_at<std::int64_t>(state.frame, 280);
|
|
962
|
+
state.input_channels = field_at<int>(state.frame, 444);
|
|
963
|
+
if (state.input_channels <= 0 || state.input_channels > 256) {
|
|
964
|
+
error = "FFmpeg returned an invalid decoded channel count";
|
|
965
|
+
return 3;
|
|
966
|
+
}
|
|
967
|
+
if (state.old_input_layout == 0)
|
|
968
|
+
state.old_input_layout = state.api.av_get_default_channel_layout(state.input_channels);
|
|
969
|
+
if (state.old_input_layout == 0) {
|
|
970
|
+
error = "FFmpeg could not determine the decoded channel layout";
|
|
971
|
+
return 3;
|
|
972
|
+
}
|
|
973
|
+
state.resampler = state.api.swr_alloc_set_opts(
|
|
974
|
+
nullptr,
|
|
975
|
+
kMonoChannelMask,
|
|
976
|
+
kFloatSampleFormat,
|
|
977
|
+
state.target_rate,
|
|
978
|
+
state.old_input_layout,
|
|
979
|
+
state.input_format,
|
|
980
|
+
state.input_rate,
|
|
981
|
+
0,
|
|
982
|
+
nullptr);
|
|
983
|
+
result = state.resampler ? 0 : -1;
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
if (result < 0 || !state.resampler) {
|
|
987
|
+
error = "could not configure FFmpeg audio resampling";
|
|
988
|
+
return 3;
|
|
989
|
+
}
|
|
990
|
+
result = state.api.swr_init(state.resampler);
|
|
991
|
+
if (result < 0) {
|
|
992
|
+
error = "could not initialize FFmpeg audio resampling: " + state.api.error_text(result);
|
|
993
|
+
return 3;
|
|
994
|
+
}
|
|
995
|
+
return 0;
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
bool frame_matches_resampler(const DecodeState& state) {
|
|
999
|
+
const auto* prefix = reinterpret_cast<const FramePrefix*>(state.frame);
|
|
1000
|
+
if (prefix->format != state.input_format || frame_sample_rate(state) != state.input_rate)
|
|
1001
|
+
return false;
|
|
1002
|
+
if (state.api.util_major >= 57) {
|
|
1003
|
+
const AVChannelLayout* layout = new_frame_layout(state);
|
|
1004
|
+
if (layout && state.api.av_channel_layout_check(layout) == 1)
|
|
1005
|
+
return state.api.av_channel_layout_compare(layout, &state.input_layout) == 0;
|
|
1006
|
+
return layout && layout->nb_channels == state.input_channels;
|
|
1007
|
+
}
|
|
1008
|
+
const int channels = field_at<int>(state.frame, 444);
|
|
1009
|
+
std::int64_t layout = field_at<std::int64_t>(state.frame, 280);
|
|
1010
|
+
if (layout == 0 && channels > 0)
|
|
1011
|
+
layout = state.api.av_get_default_channel_layout(channels);
|
|
1012
|
+
return channels == state.input_channels && layout == state.old_input_layout;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
int flush_resampler(DecodeState& state, OutputBuffer& output, std::string& error);
|
|
1016
|
+
|
|
1017
|
+
int check_decode_deadline(const DecodeState& state, std::string& error) {
|
|
1018
|
+
return state.deadline ? state.deadline->check(error) : 0;
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
void release_resampler_configuration(DecodeState& state) {
|
|
1022
|
+
if (state.input_layout_owned) {
|
|
1023
|
+
state.api.av_channel_layout_uninit(&state.input_layout);
|
|
1024
|
+
state.input_layout = {};
|
|
1025
|
+
state.input_layout_owned = false;
|
|
1026
|
+
}
|
|
1027
|
+
if (state.resampler)
|
|
1028
|
+
state.api.swr_free(&state.resampler);
|
|
1029
|
+
state.input_rate = 0;
|
|
1030
|
+
state.input_format = -1;
|
|
1031
|
+
state.input_channels = 0;
|
|
1032
|
+
state.old_input_layout = 0;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
std::uint64_t output_capacity(std::int64_t delayed, int input_samples,
|
|
1036
|
+
int input_rate, int target_rate) {
|
|
1037
|
+
if (delayed < 0 || input_samples < 0 || input_rate <= 0 || target_rate <= 0)
|
|
1038
|
+
return 0;
|
|
1039
|
+
const auto total = static_cast<std::uint64_t>(delayed) +
|
|
1040
|
+
static_cast<unsigned int>(input_samples);
|
|
1041
|
+
const auto denominator = static_cast<unsigned int>(input_rate);
|
|
1042
|
+
const auto multiplier = static_cast<unsigned int>(target_rate);
|
|
1043
|
+
const std::uint64_t quotient = total / denominator;
|
|
1044
|
+
const std::uint64_t remainder = total % denominator;
|
|
1045
|
+
if (quotient > std::numeric_limits<std::uint64_t>::max() / multiplier)
|
|
1046
|
+
return std::numeric_limits<std::uint64_t>::max();
|
|
1047
|
+
const std::uint64_t whole = quotient * multiplier;
|
|
1048
|
+
const std::uint64_t remainder_numerator = remainder * multiplier;
|
|
1049
|
+
const std::uint64_t fractional =
|
|
1050
|
+
(remainder_numerator + denominator - 1) / denominator;
|
|
1051
|
+
if (whole > std::numeric_limits<std::uint64_t>::max() - fractional)
|
|
1052
|
+
return std::numeric_limits<std::uint64_t>::max();
|
|
1053
|
+
return whole + fractional;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
int append_decoded_frame(DecodeState& state, OutputBuffer& output, std::string& error) {
|
|
1057
|
+
int interrupted = check_decode_deadline(state, error);
|
|
1058
|
+
if (interrupted != 0)
|
|
1059
|
+
return interrupted;
|
|
1060
|
+
const auto* frame = reinterpret_cast<const FramePrefix*>(state.frame);
|
|
1061
|
+
if (frame->sample_count < 0 || !frame->extended_data) {
|
|
1062
|
+
error = "FFmpeg returned an invalid decoded audio frame";
|
|
1063
|
+
return 3;
|
|
1064
|
+
}
|
|
1065
|
+
if (!state.resampler) {
|
|
1066
|
+
const int status = initialize_resampler(state, error);
|
|
1067
|
+
if (status != 0)
|
|
1068
|
+
return status;
|
|
1069
|
+
} else if (!frame_matches_resampler(state)) {
|
|
1070
|
+
int status = flush_resampler(state, output, error);
|
|
1071
|
+
if (status != 0)
|
|
1072
|
+
return status;
|
|
1073
|
+
release_resampler_configuration(state);
|
|
1074
|
+
status = initialize_resampler(state, error);
|
|
1075
|
+
if (status != 0)
|
|
1076
|
+
return status;
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
const std::int64_t delayed = state.api.swr_get_delay(state.resampler, state.input_rate);
|
|
1080
|
+
const std::uint64_t capacity = output_capacity(
|
|
1081
|
+
delayed, frame->sample_count, state.input_rate, state.target_rate);
|
|
1082
|
+
if (capacity == 0 && frame->sample_count > 0) {
|
|
1083
|
+
error = "FFmpeg returned an invalid resampling delay";
|
|
1084
|
+
return 3;
|
|
1085
|
+
}
|
|
1086
|
+
if (capacity > kMaximumScratchSamples || capacity > static_cast<std::uint64_t>(std::numeric_limits<int>::max())) {
|
|
1087
|
+
error = "FFmpeg returned an unreasonably large decoded audio frame";
|
|
1088
|
+
return 3;
|
|
1089
|
+
}
|
|
1090
|
+
std::vector<float> scratch(static_cast<std::size_t>(capacity));
|
|
1091
|
+
std::uint8_t* planes[] = {
|
|
1092
|
+
capacity == 0 ? nullptr : reinterpret_cast<std::uint8_t*>(scratch.data())
|
|
1093
|
+
};
|
|
1094
|
+
const int converted = state.api.swr_convert(
|
|
1095
|
+
state.resampler,
|
|
1096
|
+
planes,
|
|
1097
|
+
static_cast<int>(capacity),
|
|
1098
|
+
const_cast<const std::uint8_t**>(frame->extended_data),
|
|
1099
|
+
frame->sample_count);
|
|
1100
|
+
interrupted = check_decode_deadline(state, error);
|
|
1101
|
+
if (interrupted != 0)
|
|
1102
|
+
return interrupted;
|
|
1103
|
+
if (converted < 0) {
|
|
1104
|
+
error = "FFmpeg audio resampling failed: " + state.api.error_text(converted);
|
|
1105
|
+
return 3;
|
|
1106
|
+
}
|
|
1107
|
+
return output.append(scratch.data(), static_cast<std::uint64_t>(converted), error);
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
int flush_resampler(DecodeState& state, OutputBuffer& output, std::string& error) {
|
|
1111
|
+
if (!state.resampler)
|
|
1112
|
+
return 0;
|
|
1113
|
+
for (int iteration = 0; iteration < 1024; ++iteration) {
|
|
1114
|
+
int interrupted = check_decode_deadline(state, error);
|
|
1115
|
+
if (interrupted != 0)
|
|
1116
|
+
return interrupted;
|
|
1117
|
+
const std::int64_t delayed = state.api.swr_get_delay(state.resampler, state.input_rate);
|
|
1118
|
+
if (delayed <= 0)
|
|
1119
|
+
return 0;
|
|
1120
|
+
const std::uint64_t capacity = output_capacity(
|
|
1121
|
+
delayed, 0, state.input_rate, state.target_rate);
|
|
1122
|
+
if (capacity == 0 || capacity > kMaximumScratchSamples ||
|
|
1123
|
+
capacity > static_cast<std::uint64_t>(std::numeric_limits<int>::max())) {
|
|
1124
|
+
error = "FFmpeg returned an invalid final resampling delay";
|
|
1125
|
+
return 3;
|
|
1126
|
+
}
|
|
1127
|
+
std::vector<float> scratch(static_cast<std::size_t>(capacity));
|
|
1128
|
+
std::uint8_t* planes[] = {reinterpret_cast<std::uint8_t*>(scratch.data())};
|
|
1129
|
+
const int converted = state.api.swr_convert(
|
|
1130
|
+
state.resampler, planes, static_cast<int>(capacity), nullptr, 0);
|
|
1131
|
+
interrupted = check_decode_deadline(state, error);
|
|
1132
|
+
if (interrupted != 0)
|
|
1133
|
+
return interrupted;
|
|
1134
|
+
if (converted < 0) {
|
|
1135
|
+
error = "FFmpeg final audio resampling failed: " + state.api.error_text(converted);
|
|
1136
|
+
return 3;
|
|
1137
|
+
}
|
|
1138
|
+
if (converted == 0)
|
|
1139
|
+
return 0;
|
|
1140
|
+
const int status = output.append(scratch.data(), static_cast<std::uint64_t>(converted), error);
|
|
1141
|
+
if (status != 0)
|
|
1142
|
+
return status;
|
|
1143
|
+
}
|
|
1144
|
+
error = "FFmpeg audio resampling did not finish";
|
|
1145
|
+
return 3;
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
int decode_file(const char* path, int target_rate, std::uint64_t maximum_bytes,
|
|
1149
|
+
float** output_samples, std::int64_t* output_count, std::string& error) {
|
|
1150
|
+
Runtime& api = runtime();
|
|
1151
|
+
if (!api.available) {
|
|
1152
|
+
error = api.diagnostic;
|
|
1153
|
+
return 1;
|
|
1154
|
+
}
|
|
1155
|
+
if (!path || path[0] == '\0' || !output_samples || !output_count ||
|
|
1156
|
+
target_rate <= 0 || target_rate > 1'000'000) {
|
|
1157
|
+
error = "invalid native FFmpeg decode arguments";
|
|
1158
|
+
return 2;
|
|
1159
|
+
}
|
|
1160
|
+
*output_samples = nullptr;
|
|
1161
|
+
*output_count = 0;
|
|
1162
|
+
|
|
1163
|
+
OperationDeadline deadline(kDecodeTimeout, "FFmpeg audio decode");
|
|
1164
|
+
ErrorLogGuard log_guard(api);
|
|
1165
|
+
DecodeState state(api);
|
|
1166
|
+
state.target_rate = target_rate;
|
|
1167
|
+
state.deadline = &deadline;
|
|
1168
|
+
int interrupted = open_input(state, path, deadline, error);
|
|
1169
|
+
if (interrupted != 0)
|
|
1170
|
+
return interrupted;
|
|
1171
|
+
int result = api.avformat_find_stream_info(state.format, nullptr);
|
|
1172
|
+
interrupted = deadline.check(error);
|
|
1173
|
+
if (interrupted != 0)
|
|
1174
|
+
return interrupted;
|
|
1175
|
+
if (result < 0) {
|
|
1176
|
+
error = "FFmpeg could not inspect the input streams: " + api.error_text(result);
|
|
1177
|
+
return 3;
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
AVCodecParameters* parameters = nullptr;
|
|
1181
|
+
state.stream_index = first_audio_stream(api, state.format, ¶meters);
|
|
1182
|
+
if (state.stream_index < 0 || !parameters) {
|
|
1183
|
+
error = "FFmpeg input has no audio stream";
|
|
1184
|
+
return 3;
|
|
1185
|
+
}
|
|
1186
|
+
const int codec_identifier = field_at<int>(parameters, sizeof(int));
|
|
1187
|
+
const AVCodec* decoder = api.avcodec_find_decoder(codec_identifier);
|
|
1188
|
+
if (!decoder) {
|
|
1189
|
+
error = "FFmpeg has no decoder for the first audio stream";
|
|
1190
|
+
return 3;
|
|
1191
|
+
}
|
|
1192
|
+
state.codec = api.avcodec_alloc_context3(decoder);
|
|
1193
|
+
if (!state.codec) {
|
|
1194
|
+
error = "could not allocate the FFmpeg decoder context";
|
|
1195
|
+
return 5;
|
|
1196
|
+
}
|
|
1197
|
+
result = api.avcodec_parameters_to_context(state.codec, parameters);
|
|
1198
|
+
interrupted = deadline.check(error);
|
|
1199
|
+
if (interrupted != 0)
|
|
1200
|
+
return interrupted;
|
|
1201
|
+
if (result < 0) {
|
|
1202
|
+
error = "could not configure the FFmpeg decoder: " + api.error_text(result);
|
|
1203
|
+
return 3;
|
|
1204
|
+
}
|
|
1205
|
+
result = api.avcodec_open2(state.codec, decoder, nullptr);
|
|
1206
|
+
interrupted = deadline.check(error);
|
|
1207
|
+
if (interrupted != 0)
|
|
1208
|
+
return interrupted;
|
|
1209
|
+
if (result < 0) {
|
|
1210
|
+
error = "could not open the FFmpeg audio decoder: " + api.error_text(result);
|
|
1211
|
+
return 3;
|
|
1212
|
+
}
|
|
1213
|
+
state.packet = api.av_packet_alloc();
|
|
1214
|
+
state.frame = api.av_frame_alloc();
|
|
1215
|
+
if (!state.packet || !state.frame) {
|
|
1216
|
+
error = "could not allocate FFmpeg packet/frame state";
|
|
1217
|
+
return 5;
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
OutputBuffer output(maximum_bytes);
|
|
1221
|
+
auto receive_frames = [&]() -> int {
|
|
1222
|
+
while (true) {
|
|
1223
|
+
const int cancellation_status = deadline.check(error);
|
|
1224
|
+
if (cancellation_status != 0)
|
|
1225
|
+
return cancellation_status;
|
|
1226
|
+
const int receive_result = api.avcodec_receive_frame(state.codec, state.frame);
|
|
1227
|
+
const int post_receive_status = deadline.check(error);
|
|
1228
|
+
if (post_receive_status != 0) {
|
|
1229
|
+
if (receive_result >= 0)
|
|
1230
|
+
api.av_frame_unref(state.frame);
|
|
1231
|
+
return post_receive_status;
|
|
1232
|
+
}
|
|
1233
|
+
if (receive_result == -EAGAIN || receive_result == kAvErrorEof)
|
|
1234
|
+
return 0;
|
|
1235
|
+
if (receive_result < 0) {
|
|
1236
|
+
error = "FFmpeg audio decoding failed: " + api.error_text(receive_result);
|
|
1237
|
+
return 3;
|
|
1238
|
+
}
|
|
1239
|
+
const int append_result = append_decoded_frame(state, output, error);
|
|
1240
|
+
api.av_frame_unref(state.frame);
|
|
1241
|
+
if (append_result != 0)
|
|
1242
|
+
return append_result;
|
|
1243
|
+
}
|
|
1244
|
+
};
|
|
1245
|
+
|
|
1246
|
+
while ((result = api.av_read_frame(state.format, state.packet)) >= 0) {
|
|
1247
|
+
interrupted = deadline.check(error);
|
|
1248
|
+
if (interrupted != 0) {
|
|
1249
|
+
api.av_packet_unref(state.packet);
|
|
1250
|
+
return interrupted;
|
|
1251
|
+
}
|
|
1252
|
+
const auto* packet = reinterpret_cast<const PacketPrefix*>(state.packet);
|
|
1253
|
+
int packet_status = 0;
|
|
1254
|
+
if (packet->stream_index == state.stream_index) {
|
|
1255
|
+
while (true) {
|
|
1256
|
+
interrupted = deadline.check(error);
|
|
1257
|
+
if (interrupted != 0) {
|
|
1258
|
+
packet_status = interrupted;
|
|
1259
|
+
break;
|
|
1260
|
+
}
|
|
1261
|
+
const int send_result = api.avcodec_send_packet(state.codec, state.packet);
|
|
1262
|
+
interrupted = deadline.check(error);
|
|
1263
|
+
if (interrupted != 0) {
|
|
1264
|
+
packet_status = interrupted;
|
|
1265
|
+
break;
|
|
1266
|
+
}
|
|
1267
|
+
if (send_result == -EAGAIN) {
|
|
1268
|
+
packet_status = receive_frames();
|
|
1269
|
+
if (packet_status != 0)
|
|
1270
|
+
break;
|
|
1271
|
+
continue;
|
|
1272
|
+
}
|
|
1273
|
+
if (send_result < 0) {
|
|
1274
|
+
error = "FFmpeg rejected an audio packet: " + api.error_text(send_result);
|
|
1275
|
+
packet_status = 3;
|
|
1276
|
+
} else {
|
|
1277
|
+
packet_status = receive_frames();
|
|
1278
|
+
}
|
|
1279
|
+
break;
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
api.av_packet_unref(state.packet);
|
|
1283
|
+
if (packet_status != 0)
|
|
1284
|
+
return packet_status;
|
|
1285
|
+
}
|
|
1286
|
+
interrupted = deadline.check(error);
|
|
1287
|
+
if (interrupted != 0)
|
|
1288
|
+
return interrupted;
|
|
1289
|
+
if (result != kAvErrorEof) {
|
|
1290
|
+
error = "FFmpeg failed while reading the input: " + api.error_text(result);
|
|
1291
|
+
return 3;
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
while (true) {
|
|
1295
|
+
result = api.avcodec_send_packet(state.codec, nullptr);
|
|
1296
|
+
interrupted = deadline.check(error);
|
|
1297
|
+
if (interrupted != 0)
|
|
1298
|
+
return interrupted;
|
|
1299
|
+
if (result != -EAGAIN)
|
|
1300
|
+
break;
|
|
1301
|
+
result = receive_frames();
|
|
1302
|
+
if (result != 0)
|
|
1303
|
+
return result;
|
|
1304
|
+
}
|
|
1305
|
+
if (result < 0 && result != kAvErrorEof) {
|
|
1306
|
+
error = "FFmpeg could not flush the audio decoder: " + api.error_text(result);
|
|
1307
|
+
return 3;
|
|
1308
|
+
}
|
|
1309
|
+
result = receive_frames();
|
|
1310
|
+
if (result != 0)
|
|
1311
|
+
return result;
|
|
1312
|
+
result = flush_resampler(state, output, error);
|
|
1313
|
+
if (result != 0)
|
|
1314
|
+
return result;
|
|
1315
|
+
if (output.size() > static_cast<std::uint64_t>(std::numeric_limits<std::int64_t>::max())) {
|
|
1316
|
+
error = "decoded audio is too large for the Ruby ABI";
|
|
1317
|
+
return 5;
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
*output_count = static_cast<std::int64_t>(output.size());
|
|
1321
|
+
*output_samples = output.release();
|
|
1322
|
+
return 0;
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
} // namespace
|
|
1326
|
+
|
|
1327
|
+
COHERE_AUDIO_EXPORT int cohere_audio_ffmpeg_probe(char* diagnostic, std::size_t capacity) {
|
|
1328
|
+
try {
|
|
1329
|
+
Runtime& api = runtime();
|
|
1330
|
+
set_message(diagnostic, capacity, api.diagnostic);
|
|
1331
|
+
return api.available ? 0 : 1;
|
|
1332
|
+
} catch (const std::exception& exception) {
|
|
1333
|
+
set_message(diagnostic, capacity, exception.what());
|
|
1334
|
+
return 1;
|
|
1335
|
+
} catch (...) {
|
|
1336
|
+
set_message(diagnostic, capacity, "unknown error while loading the FFmpeg ABI");
|
|
1337
|
+
return 1;
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
COHERE_AUDIO_EXPORT int cohere_audio_ffmpeg_decode(
|
|
1342
|
+
const char* path,
|
|
1343
|
+
int target_rate,
|
|
1344
|
+
std::uint64_t maximum_bytes,
|
|
1345
|
+
float** output_samples,
|
|
1346
|
+
std::int64_t* output_count,
|
|
1347
|
+
char* diagnostic,
|
|
1348
|
+
std::size_t capacity) {
|
|
1349
|
+
// Make the ownership contract deterministic on every return path,
|
|
1350
|
+
// including argument validation and runtime-loader failures.
|
|
1351
|
+
if (output_samples)
|
|
1352
|
+
*output_samples = nullptr;
|
|
1353
|
+
if (output_count)
|
|
1354
|
+
*output_count = 0;
|
|
1355
|
+
try {
|
|
1356
|
+
std::string error;
|
|
1357
|
+
const int result = decode_file(
|
|
1358
|
+
path, target_rate, maximum_bytes, output_samples, output_count, error);
|
|
1359
|
+
set_message(diagnostic, capacity, result == 0 ? runtime().diagnostic : error);
|
|
1360
|
+
return result;
|
|
1361
|
+
} catch (const std::bad_alloc&) {
|
|
1362
|
+
if (output_samples)
|
|
1363
|
+
*output_samples = nullptr;
|
|
1364
|
+
if (output_count)
|
|
1365
|
+
*output_count = 0;
|
|
1366
|
+
set_message(diagnostic, capacity, "could not allocate native audio decode memory");
|
|
1367
|
+
return 5;
|
|
1368
|
+
} catch (const std::exception& exception) {
|
|
1369
|
+
if (output_samples)
|
|
1370
|
+
*output_samples = nullptr;
|
|
1371
|
+
if (output_count)
|
|
1372
|
+
*output_count = 0;
|
|
1373
|
+
set_message(diagnostic, capacity, exception.what());
|
|
1374
|
+
return 3;
|
|
1375
|
+
} catch (...) {
|
|
1376
|
+
if (output_samples)
|
|
1377
|
+
*output_samples = nullptr;
|
|
1378
|
+
if (output_count)
|
|
1379
|
+
*output_count = 0;
|
|
1380
|
+
set_message(diagnostic, capacity, "unknown native FFmpeg decode failure");
|
|
1381
|
+
return 3;
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
COHERE_AUDIO_EXPORT int cohere_audio_ffmpeg_duration(
|
|
1386
|
+
const char* path,
|
|
1387
|
+
double* output_duration,
|
|
1388
|
+
char* diagnostic,
|
|
1389
|
+
std::size_t capacity) {
|
|
1390
|
+
if (output_duration)
|
|
1391
|
+
*output_duration = -1.0;
|
|
1392
|
+
try {
|
|
1393
|
+
std::string error;
|
|
1394
|
+
const int result = probe_duration_file(path, output_duration, error);
|
|
1395
|
+
set_message(diagnostic, capacity, result == 0 ? runtime().diagnostic : error);
|
|
1396
|
+
return result;
|
|
1397
|
+
} catch (const std::exception& exception) {
|
|
1398
|
+
if (output_duration)
|
|
1399
|
+
*output_duration = -1.0;
|
|
1400
|
+
set_message(diagnostic, capacity, exception.what());
|
|
1401
|
+
return 3;
|
|
1402
|
+
} catch (...) {
|
|
1403
|
+
if (output_duration)
|
|
1404
|
+
*output_duration = -1.0;
|
|
1405
|
+
set_message(diagnostic, capacity, "unknown native FFmpeg duration-probe failure");
|
|
1406
|
+
return 3;
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
COHERE_AUDIO_EXPORT void cohere_audio_ffmpeg_cancel() {
|
|
1411
|
+
cancellation_generation.fetch_add(1, std::memory_order_acq_rel);
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
COHERE_AUDIO_EXPORT void cohere_audio_ffmpeg_free(void* samples) {
|
|
1415
|
+
std::free(samples);
|
|
1416
|
+
}
|