├── .github └── workflows │ └── ci.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── build.rs ├── examples └── slice │ ├── bear.mp4 │ ├── main.rs │ └── output │ ├── frame-1.pgm │ ├── frame-2.pgm │ ├── frame-3.pgm │ ├── frame-4.pgm │ ├── frame-5.pgm │ ├── frame-6.pgm │ └── frame-7.pgm └── src ├── README.md ├── avutil ├── _avutil.rs ├── common.rs ├── error.rs ├── mod.rs ├── pixfmt.rs └── rational.rs ├── binding.rs └── lib.rs /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # We can do more optimization here, e.g. job `rust_clippy_check` compiles 2 | # FFmpeg, while `build_and_test` also compiles FFmpeg, that takes a lot of time. 3 | name: CI 4 | 5 | on: 6 | push: 7 | branches: [ master ] 8 | pull_request: 9 | branches: [ master ] 10 | 11 | env: 12 | CARGO_TERM_COLOR: always 13 | 14 | jobs: 15 | rustfmt_check: 16 | strategy: 17 | matrix: 18 | os: [macos-latest, windows-latest, ubuntu-latest] 19 | fail-fast: false 20 | runs-on: ${{ matrix.os }} 21 | steps: 22 | - uses: actions/checkout@v4 23 | - uses: dtolnay/rust-toolchain@stable 24 | with: 25 | toolchain: stable 26 | components: rustfmt 27 | - run: cargo fmt --all -- --check 28 | 29 | rust_clippy_check_ubuntu: 30 | runs-on: ubuntu-latest 31 | steps: 32 | - uses: actions/checkout@v4 33 | - uses: dtolnay/rust-toolchain@stable 34 | with: 35 | toolchain: stable 36 | components: clippy 37 | 38 | # https://trac.ffmpeg.org/wiki/CompilationGuide/Ubuntu 39 | - name: Install FFmpegBuildTools 40 | run: | 41 | sudo apt-get update -qq && sudo apt-get -y install \ 42 | autoconf \ 43 | automake \ 44 | build-essential \ 45 | cmake \ 46 | git-core \ 47 | libass-dev \ 48 | libbz2-dev \ 49 | libfreetype6-dev \ 50 | libgnutls28-dev \ 51 | libsdl2-dev \ 52 | libtool \ 53 | libva-dev \ 54 | libvdpau-dev \ 55 | libvorbis-dev \ 56 | libxcb1-dev \ 57 | libxcb-shm0-dev \ 58 | libxcb-xfixes0-dev \ 59 | pkg-config \ 60 | texinfo \ 61 | wget \ 62 | yasm \ 63 | zlib1g-dev \ 64 | nasm \ 65 | libx264-dev \ 66 | libx265-dev \ 67 | libnuma-dev \ 68 | libvpx-dev \ 69 | libfdk-aac-dev \ 70 | libmp3lame-dev \ 71 | libopus-dev 72 | 73 | - run: | 74 | git clone https://github.com/ffmpeg/ffmpeg --depth 1 --single-branch --branch release/7.1 75 | cd ffmpeg 76 | mkdir build 77 | cd build 78 | ../configure --prefix=${PWD}/build 79 | make -j$(nproc) 80 | make install 81 | cp -r ./build ~/ffmpeg_build 82 | cd ../.. 83 | 84 | - run: | 85 | FFMPEG_INCLUDE_DIR=${HOME}/ffmpeg_build/include \ 86 | FFMPEG_PKG_CONFIG_PATH=${HOME}/ffmpeg_build/lib/pkgconfig \ 87 | cargo clippy -- -D warnings 88 | 89 | rust_clippy_check_windows: 90 | runs-on: windows-latest 91 | steps: 92 | - uses: actions/checkout@v4 93 | - uses: dtolnay/rust-toolchain@stable 94 | with: 95 | toolchain: stable 96 | components: clippy 97 | 98 | # Using this since it's used by clang-sys's CI 99 | - name: Install LLVM and Clang 100 | uses: KyleMayes/install-llvm-action@v1 101 | with: 102 | version: "10.0" 103 | directory: ${{ github.workspace }}/clang 104 | 105 | - name: Cache vcpkg 106 | id: cache 107 | uses: actions/cache@v3 108 | with: 109 | path: | 110 | ${{ github.workspace }}/vcpkg 111 | key: vcpkg-${{ runner.os }}-x64-windows-static-md 112 | 113 | - name: Build vcpkg 114 | if: steps.cache.outputs.cache-hit != 'true' 115 | run: | 116 | git clone https://github.com/microsoft/vcpkg --depth 1 117 | ./vcpkg/bootstrap-vcpkg.bat 118 | 119 | - name: Install Dependencies 120 | run: | 121 | ./vcpkg/vcpkg.exe install ffmpeg:x64-windows-static-md 122 | 123 | - name: Clippy check 124 | env: 125 | VCPKG_ROOT: ${{ github.workspace }}/vcpkg 126 | LIBCLANG_PATH: ${{ github.workspace }}/clang/lib 127 | LLVM_CONFIG_PATH: ${{ github.workspace }}/clang/bin/llvm-config 128 | run: cargo clippy --features link_vcpkg_ffmpeg -- -D warnings 129 | 130 | build_static_and_test_ubuntu_with_system_ffmpeg: 131 | runs-on: ubuntu-latest 132 | steps: 133 | - uses: actions/checkout@v4 134 | - uses: dtolnay/rust-toolchain@stable 135 | with: 136 | toolchain: stable 137 | components: clippy 138 | 139 | 140 | # https://trac.ffmpeg.org/wiki/CompilationGuide/Ubuntu 141 | - name: Install FFmpegBuildTools 142 | run: | 143 | sudo apt-get update -qq && sudo apt-get -y install \ 144 | autoconf \ 145 | automake \ 146 | build-essential \ 147 | cmake \ 148 | git-core \ 149 | libass-dev \ 150 | libfreetype6-dev \ 151 | libgnutls28-dev \ 152 | libsdl2-dev \ 153 | libtool \ 154 | libva-dev \ 155 | libvdpau-dev \ 156 | libvorbis-dev \ 157 | libxcb1-dev \ 158 | libxcb-shm0-dev \ 159 | libxcb-xfixes0-dev \ 160 | pkg-config \ 161 | texinfo \ 162 | wget \ 163 | yasm \ 164 | zlib1g-dev \ 165 | nasm \ 166 | libx264-dev \ 167 | libx265-dev \ 168 | libnuma-dev \ 169 | libvpx-dev \ 170 | libfdk-aac-dev \ 171 | libmp3lame-dev \ 172 | libopus-dev \ 173 | libraw1394-dev \ 174 | libdc1394-dev \ 175 | libavc1394-dev \ 176 | libiec61883-dev \ 177 | libjack-dev \ 178 | libfaad-dev \ 179 | libgsm1-dev \ 180 | libzmq3-dev \ 181 | libssh-dev \ 182 | libbluray-dev \ 183 | libopenmpt-dev \ 184 | ocl-icd-opencl-dev \ 185 | libogg-dev \ 186 | libspeex-dev \ 187 | libtheora-dev \ 188 | flite1-dev \ 189 | libchromaprint-dev \ 190 | libopenal-dev \ 191 | libcdio-dev \ 192 | libcaca-dev \ 193 | libpocketsphinx-dev \ 194 | libsphinxbase-dev \ 195 | libbs2b-dev \ 196 | liblilv-dev \ 197 | libsratom-dev \ 198 | libsord-dev \ 199 | libserd-dev \ 200 | librubberband-dev \ 201 | libsamplerate0-dev \ 202 | libmysofa-dev \ 203 | libvidstab-dev \ 204 | libzimg-dev \ 205 | libgme-dev \ 206 | librabbitmq-dev \ 207 | libdav1d-dev \ 208 | libzvbi-dev \ 209 | libsnappy-dev \ 210 | libaom-dev \ 211 | libcodec2-dev \ 212 | libshine-dev \ 213 | libtwolame-dev \ 214 | libwebp-dev \ 215 | libxvidcore-dev \ 216 | libsoxr-dev \ 217 | libcdio-paranoia-dev \ 218 | libcdio-cdda-dev \ 219 | libsrt-gnutls-dev \ 220 | libmfx-dev \ 221 | libvorbis-dev 222 | 223 | - name: Install System FFmpeg 224 | run: | 225 | sudo apt-get update -qq && sudo apt-get -y install \ 226 | libavcodec-dev \ 227 | libavdevice-dev \ 228 | libavfilter-dev \ 229 | libavformat-dev \ 230 | libavutil-dev \ 231 | libpostproc-dev \ 232 | libswresample-dev \ 233 | libswscale-dev 234 | - name: Clippy check 235 | run: cargo clippy --features link_system_ffmpeg -- -D warnings 236 | 237 | # Currently disable until system ffmpeg upgrades to FFmpeg 7.0 238 | # - name: Run Slice Example 239 | # run: cargo run --example slice --features link_system_ffmpeg 240 | 241 | - name: Check test result correctness 242 | run: | 243 | if [[ -z "$(git status --porcelain)" ]]; then 244 | echo "0" 245 | else 246 | echo "1" 247 | fi 248 | 249 | build_static_and_test_ubuntu: 250 | runs-on: ubuntu-latest 251 | steps: 252 | - uses: actions/checkout@v4 253 | - uses: dtolnay/rust-toolchain@stable 254 | with: 255 | toolchain: stable 256 | 257 | # https://trac.ffmpeg.org/wiki/CompilationGuide/Ubuntu 258 | - name: Install FFmpegBuildTools 259 | run: | 260 | sudo apt-get update -qq && sudo apt-get -y install \ 261 | autoconf \ 262 | automake \ 263 | build-essential \ 264 | cmake \ 265 | git-core \ 266 | libass-dev \ 267 | libfreetype6-dev \ 268 | libgnutls28-dev \ 269 | libsdl2-dev \ 270 | libtool \ 271 | libva-dev \ 272 | libvdpau-dev \ 273 | libvorbis-dev \ 274 | libxcb1-dev \ 275 | libxcb-shm0-dev \ 276 | libxcb-xfixes0-dev \ 277 | pkg-config \ 278 | texinfo \ 279 | wget \ 280 | yasm \ 281 | zlib1g-dev 282 | sudo apt-get -y install nasm 283 | sudo apt-get -y install libx264-dev 284 | sudo apt-get -y install libx265-dev libnuma-dev 285 | sudo apt-get -y install libvpx-dev 286 | sudo apt-get -y install libfdk-aac-dev 287 | sudo apt-get -y install libmp3lame-dev 288 | sudo apt-get -y install libopus-dev 289 | 290 | # Disable exr,phm to workaround "multiple definition of 'ff_init_half2float_tables'" 291 | # Ref: `https://github.com/larksuite/rsmpeg/pull/98#issuecomment-1467511193` 292 | - run: | 293 | git clone https://github.com/ffmpeg/ffmpeg --depth 1 --single-branch --branch release/7.1 294 | cd ffmpeg 295 | mkdir build 296 | cd build 297 | ../configure --disable-decoder=exr,phm --prefix=${PWD}/build 298 | make -j$(nproc) 299 | make install 300 | cp -r ./build ~/ffmpeg_build 301 | cd ../.. 302 | 303 | - name: Binding Test 304 | run: | 305 | FFMPEG_INCLUDE_DIR=${HOME}/ffmpeg_build/include \ 306 | FFMPEG_PKG_CONFIG_PATH=${HOME}/ffmpeg_build/lib/pkgconfig \ 307 | cargo test --verbose 308 | 309 | - name: Run Slice Example 310 | run: | 311 | FFMPEG_INCLUDE_DIR=${HOME}/ffmpeg_build/include \ 312 | FFMPEG_PKG_CONFIG_PATH=${HOME}/ffmpeg_build/lib/pkgconfig \ 313 | cargo run --example slice 314 | 315 | - name: Check test result correctness 316 | run: | 317 | if [[ -z "$(git status --porcelain)" ]]; then 318 | echo "0" 319 | else 320 | echo "1" 321 | fi 322 | 323 | build_with_vcpkg_ffmpeg_windows: 324 | runs-on: windows-latest 325 | strategy: 326 | matrix: 327 | config: 328 | - target: "x86_64-pc-windows-msvc" 329 | vcpkg_triplet: "x64-windows-static" 330 | rustflags: "-Ctarget-feature=+crt-static" 331 | - target: "x86_64-pc-windows-msvc" 332 | vcpkg_triplet: "x64-windows-static-md" 333 | - target: "x86_64-pc-windows-msvc" 334 | vcpkg_triplet: "x64-windows" 335 | dynamic: true 336 | 337 | - target: "i686-pc-windows-msvc" 338 | vcpkg_triplet: "x86-windows-static" 339 | rustflags: "-Ctarget-feature=+crt-static" 340 | - target: "i686-pc-windows-msvc" 341 | vcpkg_triplet: "x86-windows-static-md" 342 | - target: "i686-pc-windows-msvc" 343 | vcpkg_triplet: "x86-windows" 344 | dynamic: true 345 | fail-fast: false 346 | steps: 347 | - uses: actions/checkout@v4 348 | - uses: dtolnay/rust-toolchain@stable 349 | with: 350 | toolchain: stable 351 | targets: ${{ matrix.config.target }} 352 | 353 | # Using this since it's used by clang-sys's CI 354 | - name: Install LLVM and Clang 355 | uses: KyleMayes/install-llvm-action@v1 356 | with: 357 | version: "10.0" 358 | directory: ${{ github.workspace }}/clang 359 | 360 | - name: Cache vcpkg 361 | id: cache 362 | uses: actions/cache@v3 363 | with: 364 | path: | 365 | ${{ github.workspace }}/vcpkg 366 | key: vcpkg-${{ runner.os }}-${{ matrix.config.vcpkg_triplet }} 367 | 368 | - name: Build vcpkg 369 | if: steps.cache.outputs.cache-hit != 'true' 370 | run: | 371 | git clone https://github.com/microsoft/vcpkg --depth 1 372 | ./vcpkg/bootstrap-vcpkg.bat 373 | 374 | - name: Install Dependencies 375 | run: | 376 | ./vcpkg/vcpkg install ffmpeg:${{ matrix.config.vcpkg_triplet }} 377 | 378 | - name: Set env 379 | shell: bash 380 | run: | 381 | if [ '${{ matrix.config.dynamic }}' != '' ]; then 382 | echo "VCPKGRS_DYNAMIC=1" >> $GITHUB_ENV 383 | fi 384 | 385 | - name: Binding build 386 | shell: bash 387 | env: 388 | VCPKG_ROOT: ${{ github.workspace }}/vcpkg 389 | RUSTFLAGS: ${{ matrix.config.rustflags }} 390 | VCPKG_DEFAULT_TRIPLET: ${{ matrix.config.vcpkg_triplet }} 391 | LIBCLANG_PATH: ${{ github.workspace }}/clang/lib 392 | LLVM_CONFIG_PATH: ${{ github.workspace }}/clang/bin/llvm-config 393 | run: cargo build --features link_vcpkg_ffmpeg --target ${{ matrix.config.target }} --verbose 394 | 395 | build_dynamic_and_test_ubuntu: 396 | runs-on: ubuntu-latest 397 | steps: 398 | # https://trac.ffmpeg.org/wiki/CompilationGuide/Ubuntu 399 | - name: Install FFmpegBuildTools 400 | run: | 401 | sudo apt-get update -qq && sudo apt-get -y install \ 402 | autoconf \ 403 | automake \ 404 | build-essential \ 405 | cmake \ 406 | git-core \ 407 | libass-dev \ 408 | libfreetype6-dev \ 409 | libgnutls28-dev \ 410 | libsdl2-dev \ 411 | libbz2-dev \ 412 | libtool \ 413 | libva-dev \ 414 | libvdpau-dev \ 415 | libvorbis-dev \ 416 | libxcb1-dev \ 417 | libxcb-shm0-dev \ 418 | libxcb-xfixes0-dev \ 419 | pkg-config \ 420 | texinfo \ 421 | wget \ 422 | yasm \ 423 | zlib1g-dev \ 424 | nasm \ 425 | libdrm-dev \ 426 | libx264-dev \ 427 | libx265-dev \ 428 | libnuma-dev \ 429 | libvpx-dev \ 430 | libfdk-aac-dev \ 431 | libmp3lame-dev \ 432 | libopus-dev 433 | 434 | - name: Build x264 435 | # Since github actions use 2 core cpu 436 | run: | 437 | git clone https://github.com/mirror/x264 --depth 1 438 | cd x264 439 | mkdir build 440 | cd build 441 | ../configure \ 442 | --prefix=${PWD}/build \ 443 | --disable-cli \ 444 | --enable-static \ 445 | --enable-strip \ 446 | --enable-pic \ 447 | --disable-asm 448 | make -j$(nproc) 449 | make install 450 | cp -r ./build ~/x264_prebuilt 451 | cd ../.. 452 | 453 | - name: Build FFmpeg dylib 454 | run: | 455 | git clone https://github.com/ffmpeg/ffmpeg --depth 1 --single-branch --branch release/7.1 456 | cd ffmpeg 457 | mkdir build 458 | cd build 459 | ../configure --help 460 | PKG_CONFIG_PATH="${HOME}/x264_prebuilt/lib/pkgconfig" ../configure \ 461 | --prefix=${PWD}/build \ 462 | --enable-small \ 463 | --disable-debug \ 464 | --disable-programs \ 465 | --disable-postproc \ 466 | --disable-doc \ 467 | --disable-hwaccels \ 468 | --disable-parsers \ 469 | --disable-bsfs \ 470 | --disable-x86asm \ 471 | --disable-indevs \ 472 | --disable-outdevs \ 473 | --disable-devices \ 474 | --disable-decoders \ 475 | --enable-decoder=aac,ac3,flac,mp3,hevc,vp8,mpeg4,h263,h264,ass,srt,text \ 476 | --disable-encoders \ 477 | --enable-encoder=aac,ac3,flac,libx264,ass,ssa,srt,text,mpeg4 \ 478 | --disable-filters \ 479 | --enable-filter=scale \ 480 | --enable-gpl \ 481 | --enable-libx264 \ 482 | --enable-pic \ 483 | --disable-vaapi \ 484 | --disable-libdrm \ 485 | --disable-vdpau \ 486 | --disable-xlib 487 | make -j$(nproc) 488 | make install 489 | cd build/lib/ 490 | cp ~/x264_prebuilt/lib/libx264.a . 491 | gcc -shared -o libffmpeg.so \ 492 | -Wl,--whole-archive *.a -Wl,--no-whole-archive \ 493 | -Wl,-Bsymbolic \ 494 | -lz \ 495 | -lm \ 496 | -lbz2 497 | cd ../.. 498 | cp -r ./build ~/ffmpeg_prebuilt 499 | cd ../.. 500 | 501 | - uses: actions/checkout@v4 502 | - uses: dtolnay/rust-toolchain@stable 503 | with: 504 | toolchain: stable 505 | - env: 506 | FFMPEG_INCLUDE_DIR: ${{ github.workspace }}/ffmpeg_prebuilt/include 507 | FFMPEG_DLL_PATH: ${{ github.workspace }}/ffmpeg_prebuilt/lib/libffmpeg.so 508 | LD_LIBRARY_PATH: ${{ github.workspace }}/ffmpeg_prebuilt/lib/ 509 | run: | 510 | cp -r ~/ffmpeg_prebuilt ${{ github.workspace }}/ffmpeg_prebuilt 511 | cargo build --example slice 512 | cargo run --example slice 513 | 514 | build_dynamic_and_test_macos: 515 | runs-on: macos-latest 516 | steps: 517 | - name: Build x264 518 | run: | 519 | git clone https://github.com/mirror/x264 --depth 1 520 | cd x264 521 | mkdir build 522 | cd build 523 | ../configure \ 524 | --prefix=${PWD}/build \ 525 | --disable-cli \ 526 | --enable-static \ 527 | --enable-strip \ 528 | --enable-pic \ 529 | --disable-asm \ 530 | --extra-cflags=-mmacosx-version-min=10.9 531 | make -j$(nproc) 532 | make install 533 | cp -r ./build ~/x264_prebuilt 534 | cd ../.. 535 | 536 | - name: Build FFmpeg dylib 537 | run: | 538 | git clone https://github.com/ffmpeg/ffmpeg --depth 1 --single-branch --branch release/7.1 539 | cd ffmpeg 540 | mkdir build 541 | cd build 542 | ../configure --help 543 | CFLAGS="-mmacosx-version-min=10.9" PKG_CONFIG_PATH="${HOME}/x264_prebuilt/lib/pkgconfig" ../configure \ 544 | --prefix=${PWD}/build \ 545 | --enable-small \ 546 | --disable-debug \ 547 | --disable-programs \ 548 | --disable-postproc \ 549 | --disable-doc \ 550 | --disable-hwaccels \ 551 | --disable-parsers \ 552 | --disable-bsfs \ 553 | --disable-x86asm \ 554 | --disable-indevs \ 555 | --disable-outdevs \ 556 | --disable-devices \ 557 | --disable-decoders \ 558 | --enable-decoder=aac,ac3,flac,mp3,hevc,vp8,mpeg4,h263,h264,ass,srt,text \ 559 | --disable-encoders \ 560 | --enable-encoder=aac,ac3,flac,libx264,ass,ssa,srt,text,mpeg4 \ 561 | --disable-filters \ 562 | --enable-filter=scale \ 563 | --enable-gpl \ 564 | --enable-libx264 \ 565 | --enable-pic 566 | make -j$(sysctl -n hw.logicalcpu) 567 | make install 568 | cd build/lib/ 569 | cp ~/x264_prebuilt/lib/libx264.a . 570 | gcc -shared -o libffmpeg.dylib \ 571 | -framework CoreFoundation \ 572 | -framework CoreMedia \ 573 | -framework CoreVideo \ 574 | -framework Security \ 575 | -framework VideoToolbox \ 576 | -lbz2 \ 577 | -liconv \ 578 | -lz \ 579 | -Wl,-all_load *.a \ 580 | -mmacosx-version-min=10.9 581 | cd ../.. 582 | cp -r ./build ~/ffmpeg_prebuilt 583 | cd ../.. 584 | 585 | - uses: actions/checkout@v4 586 | - uses: dtolnay/rust-toolchain@stable 587 | with: 588 | toolchain: stable 589 | - env: 590 | FFMPEG_INCLUDE_DIR: ${{ github.workspace }}/ffmpeg_prebuilt/include 591 | FFMPEG_DLL_PATH: ${{ github.workspace }}/ffmpeg_prebuilt/lib/libffmpeg.dylib 592 | run: | 593 | cp -r ~/ffmpeg_prebuilt ${{ github.workspace }}/ffmpeg_prebuilt 594 | cargo build --example slice 595 | cp ${{ github.workspace }}/ffmpeg_prebuilt/lib/libffmpeg.dylib . 596 | cargo run --example slice 597 | 598 | build_dynamic_and_test_windows_pre: 599 | runs-on: ubuntu-latest 600 | steps: 601 | - name: Install deps 602 | run: | 603 | sudo apt-get update -qq && sudo apt-get install -y mingw-w64 604 | - name: Build x264 605 | run: | 606 | git clone https://github.com/mirror/x264 --depth 1 607 | cd x264 608 | mkdir build 609 | cd build 610 | ../configure \ 611 | --prefix=${PWD}/build \ 612 | --disable-cli \ 613 | --enable-static \ 614 | --enable-strip \ 615 | --enable-pic \ 616 | --disable-asm \ 617 | --host=i686-w64-mingw32 \ 618 | --cross-prefix=i686-w64-mingw32- 619 | make -j$(nproc) 620 | make install 621 | cp -r ./build ~/x264_prebuilt_cross 622 | cd ../.. 623 | - name: Build FFmpeg 624 | run: | 625 | git clone https://github.com/ffmpeg/ffmpeg --depth 1 --single-branch --branch release/7.1 626 | cd ffmpeg 627 | mkdir build 628 | cd build 629 | PKG_CONFIG_PATH="${HOME}/x264_prebuilt_cross/lib/pkgconfig" ../configure \ 630 | --prefix=${PWD}/build \ 631 | --enable-small \ 632 | --disable-debug \ 633 | --disable-programs \ 634 | --disable-postproc \ 635 | --disable-doc \ 636 | --disable-hwaccels \ 637 | --disable-parsers \ 638 | --disable-bsfs \ 639 | --disable-x86asm \ 640 | --disable-indevs \ 641 | --disable-outdevs \ 642 | --disable-devices \ 643 | --disable-decoders \ 644 | --enable-decoder=aac,ac3,flac,mp3,hevc,vp8,mpeg4,h263,h264,ass,srt,text \ 645 | --disable-encoders \ 646 | --enable-encoder=aac,ac3,flac,libx264,ass,ssa,srt,text,mpeg4 \ 647 | --disable-filters \ 648 | --enable-filter=scale \ 649 | --enable-gpl \ 650 | --enable-libx264 \ 651 | --enable-pic \ 652 | --arch=x86 \ 653 | --target-os=mingw32 \ 654 | --cross-prefix=i686-w64-mingw32- \ 655 | --pkg-config=pkg-config 656 | make -j$(nproc) 657 | make install 658 | cd build/lib/ 659 | cp ~/x264_prebuilt_cross/lib/libx264.a . 660 | i686-w64-mingw32-gcc -shared -o libffmpeg.dll \ 661 | -Wl,--out-implib,libffmpeg.lib \ 662 | -Wl,--whole-archive *.a -Wl,--no-whole-archive \ 663 | -lgdi32 \ 664 | -lpsapi \ 665 | -lole32 \ 666 | -lstrmiids \ 667 | -luuid \ 668 | -loleaut32 \ 669 | -lshlwapi \ 670 | -luser32 \ 671 | -lws2_32 \ 672 | -lvfw32 \ 673 | -luser32 \ 674 | -lsecur32 \ 675 | -lbcrypt \ 676 | -lm \ 677 | -lpsapi \ 678 | -ladvapi32 \ 679 | -lshell32 \ 680 | -lole32 \ 681 | -static-libgcc \ 682 | -static-libstdc++ 683 | cd ../.. 684 | 685 | - name: Upload Cross Compiled FFmpeg for Windows 686 | uses: actions/upload-artifact@v4 687 | with: 688 | name: cross_ffmpeg 689 | path: ffmpeg/build/build 690 | 691 | build_dynamic_and_test_windows: 692 | runs-on: windows-latest 693 | needs: build_dynamic_and_test_windows_pre 694 | steps: 695 | - uses: actions/checkout@v4 696 | - uses: dtolnay/rust-toolchain@stable 697 | with: 698 | toolchain: stable 699 | targets: i686-pc-windows-msvc 700 | 701 | - name: Download Cross Compiled FFmpeg for Windows 702 | uses: actions/download-artifact@v4 703 | with: 704 | name: cross_ffmpeg 705 | path: ${{ github.workspace }}/ffmpeg_prebuilt_cross 706 | 707 | # Using this since it's used by clang-sys's CI 708 | - name: Install LLVM and Clang 709 | uses: KyleMayes/install-llvm-action@v1 710 | with: 711 | version: "10.0" 712 | directory: ${{ github.workspace }}/clang 713 | 714 | - env: 715 | FFMPEG_INCLUDE_DIR: ${{ github.workspace }}/ffmpeg_prebuilt_cross/include 716 | FFMPEG_DLL_PATH: ${{ github.workspace }}/ffmpeg_prebuilt_cross/lib/libffmpeg.dll 717 | LIBCLANG_PATH: ${{ github.workspace }}/clang/lib 718 | LLVM_CONFIG_PATH: ${{ github.workspace }}/clang/bin/llvm-config 719 | run: | 720 | cargo build --target i686-pc-windows-msvc --example slice 721 | copy ${{ github.workspace }}/ffmpeg_prebuilt_cross/lib/libffmpeg.dll . 722 | cargo run --target i686-pc-windows-msvc --example slice 723 | 724 | # Check if correct documentation can be generated by docs.rs 725 | docs_rs_check: 726 | runs-on: ubuntu-latest 727 | steps: 728 | - uses: actions/checkout@v4 729 | - uses: dtolnay/rust-toolchain@stable 730 | with: 731 | toolchain: stable 732 | 733 | # https://trac.ffmpeg.org/wiki/CompilationGuide/Ubuntu 734 | - name: Install FFmpegBuildTools 735 | run: | 736 | sudo apt-get update -qq && sudo apt-get -y install \ 737 | autoconf \ 738 | automake \ 739 | build-essential \ 740 | cmake \ 741 | git-core \ 742 | libass-dev \ 743 | libfreetype6-dev \ 744 | libgnutls28-dev \ 745 | libsdl2-dev \ 746 | libtool \ 747 | libva-dev \ 748 | libvdpau-dev \ 749 | libvorbis-dev \ 750 | libxcb1-dev \ 751 | libxcb-shm0-dev \ 752 | libxcb-xfixes0-dev \ 753 | pkg-config \ 754 | texinfo \ 755 | wget \ 756 | yasm \ 757 | zlib1g-dev \ 758 | nasm \ 759 | libx264-dev \ 760 | libx265-dev libnuma-dev \ 761 | libvpx-dev \ 762 | libfdk-aac-dev \ 763 | libmp3lame-dev \ 764 | libopus-dev 765 | 766 | - name: Set env 767 | run: echo "DOCS_RS=1" >> $GITHUB_ENV 768 | - name: Binding Build 769 | run: cargo build --verbose 770 | - name: Document Generation 771 | run: cargo doc --verbose 772 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | /.vscode 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rusty_ffmpeg" 3 | version = "0.16.3+ffmpeg.7.1" 4 | authors = ["ldm0 "] 5 | edition = "2021" 6 | description = "A library that provides Rust bindings for FFmpeg" 7 | documentation = "https://docs.rs/rusty_ffmpeg" 8 | readme = "README.md" 9 | homepage = "https://github.com/CCExtractor/rusty_ffmpeg/" 10 | repository = "https://github.com/CCExtractor/rusty_ffmpeg/" 11 | license = "MIT" 12 | 13 | keywords = ["ffmpeg", "ffi", "binding", "video", "audio"] 14 | categories = ["external-ffi-bindings", "multimedia"] 15 | 16 | build = "build.rs" 17 | links = "ffmpeg" 18 | exclude = [".github"] 19 | 20 | # Doc test of generated binding is non-sense https://github.com/rust-lang/cargo/issues/3720 21 | [lib] 22 | doctest = false 23 | 24 | [dependencies] 25 | 26 | [build-dependencies] 27 | bindgen = "0.71" 28 | camino = "1.1" 29 | once_cell = "1.12" 30 | vcpkg = { version = "0.2", optional = true } 31 | 32 | [target.'cfg(not(windows))'.build-dependencies] 33 | pkg-config = "0.3" 34 | 35 | [features] 36 | # Probe and link FFmpeg with pkg-config 37 | link_system_ffmpeg = [] 38 | # Probe and link FFmpeg with vcpkg 39 | link_vcpkg_ffmpeg = ["vcpkg"] 40 | # FFmpeg 5.* support 41 | ffmpeg5 = [] 42 | # FFmpeg 6.* support 43 | ffmpeg6 = [] 44 | # FFmpeg 7.* support 45 | ffmpeg7 = [] 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 2 | 3 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 4 | 5 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rusty FFmpeg 2 | 3 | [![Doc](https://docs.rs/rusty_ffmpeg/badge.svg?style=flat-square)](https://docs.rs/rusty_ffmpeg) 4 | [![Crates.io](https://img.shields.io/crates/v/rusty_ffmpeg)](https://crates.io/crates/rusty_ffmpeg) 5 | [![Downloads](https://img.shields.io/crates/d/rusty_ffmpeg)](https://lib.rs/crates/rusty_ffmpeg) 6 | [![CI](https://github.com/CCExtractor/rusty_ffmpeg/workflows/CI/badge.svg?branch=master&style=flat-square)](https://github.com/CCExtractor/rusty_ffmpeg/actions) 7 | 8 | Cross platform FFI bindings for FFmpeg internal libraries. This is a crate that: 9 | 10 | 1. Links FFmpeg libraries for you. 11 | 2. Generates Rust binding for FFmpeg libraries. 12 | 13 | ## Getting started: 14 | 15 | To use this crate, you need to set several environment variables. 16 | 17 | ### The simplest usage: 18 | 19 | #### Linux, macOS..(*nix) 20 | 21 | If you have FFmpeg installed with package manager, import `rusty_ffmpeg` with feature `link_system_ffmpeg`. Then it should work. 22 | 23 | If you built FFmpeg from source, set `FFMPEG_PKG_CONFIG_PATH` to the path of the generated FFmpeg `pkg-config` directory. Then it should work. 24 | 25 | #### Windows 26 | 27 | `rusty_ffmpeg` can link FFmpeg using `vcpkg`: 28 | 1. Install [`vcpkg`](https://github.com/microsoft/vcpkg), check [documentation of the vcpkg *crate*](https://docs.rs/vcpkg) for the environment variables to set. 29 | 2. Import `rusty_ffmpeg` with feature `link_vcpkg_ffmpeg`, Then it should work. 30 | 31 | ### Fine-grained usage: 32 | 33 | You need to set several environment variables for both the linking and binding generating procedures. 34 | 35 | #### To link prebuilt libraries: 36 | 37 | 1. Dynamic linking with pre-built dylib: Set `FFMPEG_DLL_PATH` to the path of `dll` or `so` files. (Windows: Put corresponding `.lib` file next to the `.dll` file.) 38 | 39 | 2. Static linking with pre-built staticlib: Set `FFMPEG_LIBS_DIR` to the path of FFmpeg pre-built libs directory. 40 | 41 | #### To generate bindings: 42 | 43 | 1. Compile-time binding generation([requires the `Clang` dylib](https://github.com/KyleMayes/clang-sys/blob/c9ae24a7a218e73e1eccd320174349eef5a3bd1a/build.rs#L23)): Set `FFMPEG_INCLUDE_DIR` to the path of the header files for binding generation. 44 | 45 | 2. Use your prebuilt binding: Set `FFMPEG_BINDING_PATH` to the pre-built binding file. The pre-built binding is usually copied from the `OUT_DIR` of the compile-time binding generation, using it will prevent the need to regenerate the same binding file repeatedly. 46 | 47 | ### Linking FFmpeg installed by package manager on (*nix) 48 | 49 | You can link FFmpeg libraries installed by package manager by enabling feature `link_system_ffmpeg` (which uses pkg-config underneath). 50 | 51 | ### Linking FFmpeg installed by vcpkg 52 | 53 | You can link FFmpeg libraries installed by vcpkg by enabling feature `link_vcpkg_ffmpeg` on Windows, macOS, and Linux. 54 | 55 | ### Use a specific FFmpeg version 56 | 57 | - Do nothing when you are using FFmpeg `4.*` 58 | - Enable `ffmpeg5` feature when you are using FFmpeg `5.*` 59 | - Enable `ffmpeg6` feature when you are using FFmpeg `6.*` 60 | - Enable `ffmpeg7` feature when you are using FFmpeg `7.*` 61 | 62 | ## Attention 63 | 64 | FFI is not that easy, especially when you are dealing with a big old C project. Don't get discouraged if you encounter some problems. The CI check already has some typical ffmpeg compilation and use cases for you to check. File an issue if you still have any problems. 65 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use bindgen::RustTarget; 2 | use bindgen::{callbacks, Bindings}; 3 | use camino::Utf8Path as Path; 4 | use camino::Utf8PathBuf as PathBuf; 5 | use once_cell::sync::Lazy; 6 | use std::{collections::HashSet, env, fs}; 7 | 8 | /// All the libs that FFmpeg has 9 | static LIBS: Lazy<[&str; 7]> = Lazy::new(|| { 10 | [ 11 | "avcodec", 12 | "avdevice", 13 | "avfilter", 14 | "avformat", 15 | "avutil", 16 | "swresample", 17 | "swscale", 18 | ] 19 | }); 20 | 21 | /// Whitelist of the headers we want to generate bindings 22 | static HEADERS: Lazy> = Lazy::new(|| { 23 | [ 24 | "libavcodec/ac3_parser.h", 25 | "libavcodec/adts_parser.h", 26 | "libavcodec/avcodec.h", 27 | "libavcodec/avdct.h", 28 | "libavcodec/avfft.h", 29 | "libavcodec/bsf.h", 30 | "libavcodec/codec.h", 31 | "libavcodec/codec_desc.h", 32 | "libavcodec/codec_id.h", 33 | "libavcodec/codec_par.h", 34 | // "libavcodec/d3d11va.h", 35 | "libavcodec/defs.h", 36 | "libavcodec/dirac.h", 37 | "libavcodec/dv_profile.h", 38 | // "libavcodec/dxva2.h", 39 | "libavcodec/jni.h", 40 | "libavcodec/mediacodec.h", 41 | "libavcodec/packet.h", 42 | // "libavcodec/qsv.h", 43 | // "libavcodec/vdpau.h", 44 | "libavcodec/version.h", 45 | "libavcodec/version_major.h", 46 | // "libavcodec/videotoolbox.h", 47 | "libavcodec/vorbis_parser.h", 48 | // "libavcodec/xvmc.h", 49 | "libavdevice/avdevice.h", 50 | "libavdevice/version.h", 51 | "libavdevice/version_major.h", 52 | "libavfilter/avfilter.h", 53 | "libavfilter/buffersink.h", 54 | "libavfilter/buffersrc.h", 55 | "libavfilter/version.h", 56 | "libavfilter/version_major.h", 57 | "libavformat/avformat.h", 58 | "libavformat/avio.h", 59 | "libavformat/version.h", 60 | "libavformat/version_major.h", 61 | "libavutil/adler32.h", 62 | "libavutil/aes.h", 63 | "libavutil/aes_ctr.h", 64 | "libavutil/ambient_viewing_environment.h", 65 | "libavutil/attributes.h", 66 | "libavutil/audio_fifo.h", 67 | "libavutil/avassert.h", 68 | "libavutil/avconfig.h", 69 | "libavutil/avstring.h", 70 | "libavutil/avutil.h", 71 | "libavutil/base64.h", 72 | "libavutil/blowfish.h", 73 | "libavutil/bprint.h", 74 | "libavutil/bswap.h", 75 | "libavutil/buffer.h", 76 | "libavutil/camellia.h", 77 | "libavutil/cast5.h", 78 | "libavutil/channel_layout.h", 79 | "libavutil/common.h", 80 | "libavutil/cpu.h", 81 | "libavutil/crc.h", 82 | "libavutil/csp.h", 83 | "libavutil/des.h", 84 | "libavutil/detection_bbox.h", 85 | "libavutil/dict.h", 86 | "libavutil/display.h", 87 | "libavutil/dovi_meta.h", 88 | "libavutil/downmix_info.h", 89 | "libavutil/encryption_info.h", 90 | "libavutil/error.h", 91 | "libavutil/eval.h", 92 | "libavutil/executor.h", 93 | "libavutil/ffversion.h", 94 | "libavutil/fifo.h", 95 | "libavutil/file.h", 96 | "libavutil/film_grain_params.h", 97 | "libavutil/frame.h", 98 | "libavutil/hash.h", 99 | "libavutil/hdr_dynamic_metadata.h", 100 | "libavutil/hdr_dynamic_vivid_metadata.h", 101 | "libavutil/hmac.h", 102 | "libavutil/hwcontext.h", 103 | // "libavutil/hwcontext_cuda.h", 104 | // "libavutil/hwcontext_d3d11va.h", 105 | // "libavutil/hwcontext_drm.h", 106 | // "libavutil/hwcontext_dxva2.h", 107 | // "libavutil/hwcontext_mediacodec.h", 108 | // "libavutil/hwcontext_opencl.h", 109 | // "libavutil/hwcontext_qsv.h", 110 | // "libavutil/hwcontext_vaapi.h", 111 | // "libavutil/hwcontext_vdpau.h", 112 | // "libavutil/hwcontext_videotoolbox.h", 113 | // "libavutil/hwcontext_vulkan.h", 114 | "libavutil/imgutils.h", 115 | "libavutil/intfloat.h", 116 | "libavutil/intreadwrite.h", 117 | "libavutil/lfg.h", 118 | "libavutil/log.h", 119 | "libavutil/lzo.h", 120 | "libavutil/macros.h", 121 | "libavutil/mastering_display_metadata.h", 122 | "libavutil/mathematics.h", 123 | "libavutil/md5.h", 124 | "libavutil/mem.h", 125 | "libavutil/motion_vector.h", 126 | "libavutil/murmur3.h", 127 | "libavutil/opt.h", 128 | "libavutil/parseutils.h", 129 | "libavutil/pixdesc.h", 130 | "libavutil/pixelutils.h", 131 | "libavutil/pixfmt.h", 132 | "libavutil/random_seed.h", 133 | "libavutil/rational.h", 134 | "libavutil/rc4.h", 135 | "libavutil/replaygain.h", 136 | "libavutil/ripemd.h", 137 | "libavutil/samplefmt.h", 138 | "libavutil/sha.h", 139 | "libavutil/sha512.h", 140 | "libavutil/spherical.h", 141 | "libavutil/stereo3d.h", 142 | "libavutil/tea.h", 143 | "libavutil/threadmessage.h", 144 | "libavutil/time.h", 145 | "libavutil/timecode.h", 146 | "libavutil/timestamp.h", 147 | "libavutil/tree.h", 148 | "libavutil/twofish.h", 149 | "libavutil/tx.h", 150 | "libavutil/uuid.h", 151 | "libavutil/version.h", 152 | "libavutil/video_enc_params.h", 153 | "libavutil/video_hint.h", 154 | "libavutil/xtea.h", 155 | "libswresample/swresample.h", 156 | "libswresample/version.h", 157 | "libswresample/version_major.h", 158 | "libswscale/swscale.h", 159 | "libswscale/version.h", 160 | "libswscale/version_major.h", 161 | ] 162 | .into_iter() 163 | .map(|x| Path::new(x).into_iter().collect()) 164 | .collect() 165 | }); 166 | 167 | /// Filter out all symbols in the HashSet, and for others things it will act 168 | /// exactly the same as `CargoCallback`. 169 | #[derive(Debug)] 170 | struct FilterCargoCallbacks { 171 | emitted_macro: HashSet<&'static str>, 172 | } 173 | 174 | impl FilterCargoCallbacks { 175 | fn new(set: HashSet<&'static str>) -> Self { 176 | Self { emitted_macro: set } 177 | } 178 | } 179 | 180 | impl callbacks::ParseCallbacks for FilterCargoCallbacks { 181 | fn will_parse_macro(&self, name: &str) -> callbacks::MacroParsingBehavior { 182 | if self.emitted_macro.contains(name) { 183 | callbacks::MacroParsingBehavior::Ignore 184 | } else { 185 | callbacks::MacroParsingBehavior::Default 186 | } 187 | } 188 | } 189 | 190 | fn use_prebuilt_binding(from: &Path, to: &Path) { 191 | fs::copy(from, to).expect("Prebuilt binding file failed to be copied."); 192 | } 193 | 194 | fn generate_bindings(ffmpeg_include_dir: &Path, headers: &[PathBuf]) -> Bindings { 195 | if !Path::new(ffmpeg_include_dir).exists() { 196 | panic!( 197 | "FFmpeg include dir: `{:?}` doesn't exits", 198 | ffmpeg_include_dir 199 | ); 200 | } 201 | // Because of the strange `FP_*` in `math.h` https://github.com/rust-lang/rust-bindgen/issues/687 202 | let filter_callback = FilterCargoCallbacks::new( 203 | [ 204 | "FP_NAN", 205 | "FP_INFINITE", 206 | "FP_ZERO", 207 | "FP_SUBNORMAL", 208 | "FP_NORMAL", 209 | ] 210 | .into_iter() 211 | .collect(), 212 | ); 213 | 214 | // Bindgen on all avaiable headers 215 | headers 216 | .iter() 217 | .map(|header| ffmpeg_include_dir.join(header)) 218 | .filter(|path| { 219 | let exists = Path::new(&path).exists(); 220 | if !exists { 221 | eprintln!("Header path `{:?}` not found.", path); 222 | } 223 | exists 224 | }) 225 | .fold( 226 | { 227 | bindgen::builder() 228 | // Force impl Debug if possible(for `AVCodecParameters`) 229 | .impl_debug(true) 230 | .rust_target(RustTarget::stable(68, 0).ok().unwrap()) 231 | .parse_callbacks(Box::new(filter_callback)) 232 | // Add clang path, for `#include` header finding in bindgen process. 233 | .clang_arg(format!("-I{}", ffmpeg_include_dir)) 234 | // Workaround: https://github.com/rust-lang/rust-bindgen/issues/2159 235 | .blocklist_type("__mingw_ldbl_type_t") 236 | // Stop bindgen from prefixing enums 237 | .prepend_enum_name(false) 238 | }, 239 | |builder, header| builder.header(header), 240 | ) 241 | .generate() 242 | .expect("Binding generation failed.") 243 | } 244 | 245 | fn static_linking_with_libs_dir(library_names: &[&str], ffmpeg_libs_dir: &Path) { 246 | println!("cargo:rustc-link-search=native={}", ffmpeg_libs_dir); 247 | for library_name in library_names { 248 | println!("cargo:rustc-link-lib=static={}", library_name); 249 | } 250 | } 251 | 252 | #[allow(dead_code)] 253 | pub struct EnvVars { 254 | docs_rs: Option, 255 | out_dir: Option, 256 | ffmpeg_include_dir: Option, 257 | ffmpeg_dll_path: Option, 258 | ffmpeg_pkg_config_path: Option, 259 | ffmpeg_libs_dir: Option, 260 | ffmpeg_binding_path: Option, 261 | } 262 | 263 | impl EnvVars { 264 | fn init() -> Self { 265 | println!("cargo:rerun-if-env-changed=DOCS_RS"); 266 | println!("cargo:rerun-if-env-changed=OUT_DIR"); 267 | println!("cargo:rerun-if-env-changed=FFMPEG_INCLUDE_DIR"); 268 | println!("cargo:rerun-if-env-changed=FFMPEG_DLL_PATH"); 269 | println!("cargo:rerun-if-env-changed=FFMPEG_PKG_CONFIG_PATH"); 270 | println!("cargo:rerun-if-env-changed=FFMPEG_LIBS_DIR"); 271 | println!("cargo:rerun-if-env-changed=FFMPEG_BINDING_PATH"); 272 | Self { 273 | docs_rs: env::var("DOCS_RS").ok(), 274 | out_dir: env::var("OUT_DIR").ok().map(remove_verbatim), 275 | ffmpeg_include_dir: env::var("FFMPEG_INCLUDE_DIR").ok().map(remove_verbatim), 276 | ffmpeg_dll_path: env::var("FFMPEG_DLL_PATH").ok().map(remove_verbatim), 277 | ffmpeg_pkg_config_path: env::var("FFMPEG_PKG_CONFIG_PATH").ok().map(remove_verbatim), 278 | ffmpeg_libs_dir: env::var("FFMPEG_LIBS_DIR").ok().map(remove_verbatim), 279 | ffmpeg_binding_path: env::var("FFMPEG_BINDING_PATH").ok().map(remove_verbatim), 280 | } 281 | } 282 | } 283 | 284 | /// clang doesn't support -I{verbatim path} on windows, so we need to remove it if possible. 285 | fn remove_verbatim(path: String) -> PathBuf { 286 | let path = if let Some(path) = path.strip_prefix(r#"\\?\"#) { 287 | path.to_string() 288 | } else { 289 | path 290 | }; 291 | PathBuf::from(path) 292 | } 293 | 294 | #[cfg(not(target_os = "windows"))] 295 | mod pkg_config_linking { 296 | use super::*; 297 | 298 | /// Returns error when some library are missing. Otherwise, returns the paths of the libraries. 299 | /// 300 | /// Note: no side effect if this function errors. 301 | pub fn linking_with_pkg_config( 302 | library_names: &[&str], 303 | ) -> Result, pkg_config::Error> { 304 | // dry run for library linking 305 | for libname in library_names { 306 | pkg_config::Config::new() 307 | .cargo_metadata(false) 308 | .env_metadata(false) 309 | .print_system_libs(false) 310 | .print_system_cflags(false) 311 | .probe(&format!("lib{}", libname))?; 312 | } 313 | 314 | // real linking 315 | let mut paths = HashSet::new(); 316 | for libname in library_names { 317 | let new_paths = pkg_config::Config::new() 318 | .probe(&format!("lib{}", libname)) 319 | .unwrap_or_else(|_| panic!("{} not found!", libname)) 320 | .include_paths; 321 | for new_path in new_paths { 322 | let new_path = new_path.to_str().unwrap().to_string(); 323 | paths.insert(new_path); 324 | } 325 | } 326 | Ok(paths.into_iter().map(PathBuf::from).collect()) 327 | } 328 | } 329 | 330 | #[cfg(feature = "link_vcpkg_ffmpeg")] 331 | mod vcpkg_linking { 332 | use super::*; 333 | 334 | fn linking_with_vcpkg( 335 | _env_vars: &EnvVars, 336 | _library_names: &[&str], 337 | ) -> Result, vcpkg::Error> { 338 | Ok(vcpkg::Config::new() 339 | .find_package("ffmpeg")? 340 | .include_paths 341 | .into_iter() 342 | .map(|x| PathBuf::from_path_buf(x).unwrap()) 343 | .collect()) 344 | } 345 | 346 | pub fn linking_with_vcpkg_and_bindgen( 347 | env_vars: &EnvVars, 348 | output_binding_path: &Path, 349 | ) -> Result<(), vcpkg::Error> { 350 | let include_paths = linking_with_vcpkg(env_vars, &*LIBS)?; 351 | if let Some(ffmpeg_binding_path) = env_vars.ffmpeg_binding_path.as_ref() { 352 | use_prebuilt_binding(ffmpeg_binding_path, output_binding_path); 353 | } else { 354 | generate_bindings(&include_paths[0], &HEADERS) 355 | .write_to_file(output_binding_path) 356 | .expect("Cannot write binding to file."); 357 | } 358 | Ok(()) 359 | } 360 | } 361 | 362 | fn dynamic_linking(env_vars: &EnvVars) { 363 | let ffmpeg_dll_path = env_vars.ffmpeg_dll_path.as_ref().unwrap(); 364 | 365 | let output_binding_path = &env_vars.out_dir.as_ref().unwrap().join("binding.rs"); 366 | 367 | // Extract dll name and the dir the dll is in. 368 | let (ffmpeg_dll_name, ffmpeg_dll_dir) = { 369 | let mut ffmpeg_dll_path = PathBuf::from(ffmpeg_dll_path); 370 | // Without extension. 371 | let ffmpeg_dll_filename = ffmpeg_dll_path.file_stem().unwrap(); 372 | let ffmpeg_dll_name = if cfg!(target_os = "windows") { 373 | ffmpeg_dll_filename 374 | } else { 375 | ffmpeg_dll_filename.trim_start_matches("lib") 376 | } 377 | .to_string(); 378 | // Remove file name. 379 | ffmpeg_dll_path.pop(); 380 | let ffmpeg_dll_path = ffmpeg_dll_path.to_string(); 381 | (ffmpeg_dll_name, ffmpeg_dll_path) 382 | }; 383 | 384 | println!("cargo:rustc-link-lib=dylib={}", ffmpeg_dll_name); 385 | println!("cargo:rustc-link-search=native={}", ffmpeg_dll_dir); 386 | 387 | if let Some(ffmpeg_binding_path) = env_vars.ffmpeg_binding_path.as_ref() { 388 | use_prebuilt_binding(ffmpeg_binding_path, output_binding_path); 389 | } else if let Some(ffmpeg_include_dir) = env_vars.ffmpeg_include_dir.as_ref() { 390 | generate_bindings(ffmpeg_include_dir, &HEADERS) 391 | // Is it correct to generate binding to one file? :-/ 392 | .write_to_file(output_binding_path) 393 | .expect("Cannot write binding to file."); 394 | } else { 395 | panic!("No binding generation method is set!"); 396 | } 397 | } 398 | 399 | fn static_linking(env_vars: &EnvVars) { 400 | let output_binding_path = &env_vars.out_dir.as_ref().unwrap().join("binding.rs"); 401 | 402 | #[cfg(not(target_os = "windows"))] 403 | { 404 | fn static_linking_with_pkg_config_and_bindgen( 405 | env_vars: &EnvVars, 406 | output_binding_path: &Path, 407 | ) -> Result<(), pkg_config::Error> { 408 | // Probe libraries(enable emitting cargo metadata) 409 | let include_paths = pkg_config_linking::linking_with_pkg_config(&*LIBS)?; 410 | if let Some(ffmpeg_binding_path) = env_vars.ffmpeg_binding_path.as_ref() { 411 | use_prebuilt_binding(ffmpeg_binding_path, output_binding_path); 412 | } else if let Some(ffmpeg_include_dir) = env_vars.ffmpeg_include_dir.as_ref() { 413 | // If use ffmpeg_pkg_config_path with ffmpeg_include_dir, prefer using the user given dir rather than pkg_config_path. 414 | generate_bindings(ffmpeg_include_dir, &HEADERS) 415 | .write_to_file(output_binding_path) 416 | .expect("Cannot write binding to file."); 417 | } else { 418 | generate_bindings(&include_paths[0], &HEADERS) 419 | .write_to_file(output_binding_path) 420 | .expect("Cannot write binding to file."); 421 | } 422 | Ok(()) 423 | } 424 | // Hint: set PKG_CONFIG_PATH to some placeholder value will let pkg_config probing system library. 425 | if let Some(ffmpeg_pkg_config_path) = env_vars.ffmpeg_pkg_config_path.as_ref() { 426 | if !Path::new(ffmpeg_pkg_config_path).exists() { 427 | panic!( 428 | "error: FFMPEG_PKG_CONFIG_PATH is set to `{}`, which does not exist.", 429 | ffmpeg_pkg_config_path 430 | ); 431 | } 432 | env::set_var("PKG_CONFIG_PATH", ffmpeg_pkg_config_path); 433 | static_linking_with_pkg_config_and_bindgen(env_vars, output_binding_path) 434 | .expect("Static linking with pkg-config failed."); 435 | } else if let Some(ffmpeg_libs_dir) = env_vars.ffmpeg_libs_dir.as_ref() { 436 | static_linking_with_libs_dir(&*LIBS, ffmpeg_libs_dir); 437 | if let Some(ffmpeg_binding_path) = env_vars.ffmpeg_binding_path.as_ref() { 438 | use_prebuilt_binding(ffmpeg_binding_path, output_binding_path); 439 | } else if let Some(ffmpeg_include_dir) = env_vars.ffmpeg_include_dir.as_ref() { 440 | generate_bindings(ffmpeg_include_dir, &HEADERS) 441 | .write_to_file(output_binding_path) 442 | .expect("Cannot write binding to file."); 443 | } else { 444 | panic!("No binding generation method is set!"); 445 | } 446 | } else { 447 | #[cfg(not(any(feature = "link_system_ffmpeg", feature = "link_vcpkg_ffmpeg")))] 448 | panic!( 449 | " 450 | !!!!!!! rusty_ffmpeg: No linking method set! 451 | Use `FFMPEG_PKG_CONFIG_PATH` or `FFMPEG_LIBS_DIR` if you have prebuilt FFmpeg libraries. 452 | Enable `link_system_ffmpeg` feature if you want to link ffmpeg libraries installed in system path(which can be probed by pkg-config). 453 | Enable `link_vcpkg_ffmpeg` feature if you want to link ffmpeg libraries installed by vcpkg. 454 | " 455 | ); 456 | #[cfg(any(feature = "link_system_ffmpeg", feature = "link_vcpkg_ffmpeg"))] 457 | { 458 | let mut success = false; 459 | let mut error = String::new(); 460 | #[cfg(feature = "link_system_ffmpeg")] 461 | if !success { 462 | if let Err(e) = 463 | static_linking_with_pkg_config_and_bindgen(env_vars, output_binding_path) 464 | { 465 | error.push('\n'); 466 | error.push_str(&format!("Link system FFmpeg failed: {:?}", e)); 467 | } else { 468 | println!("Link system FFmpeg succeeded."); 469 | success = true; 470 | } 471 | } 472 | #[cfg(feature = "link_vcpkg_ffmpeg")] 473 | if !success { 474 | if let Err(e) = 475 | vcpkg_linking::linking_with_vcpkg_and_bindgen(env_vars, output_binding_path) 476 | { 477 | error.push('\n'); 478 | error.push_str(&format!("Link vcpkg FFmpeg failed: {:?}", e)); 479 | } else { 480 | println!("Link vcpkg FFmpeg succeeded."); 481 | success = true; 482 | } 483 | } 484 | if !success { 485 | panic!("FFmpeg linking trial failed: {}", error); 486 | } 487 | } 488 | } 489 | } 490 | #[cfg(target_os = "windows")] 491 | { 492 | if let Some(ffmpeg_libs_dir) = env_vars.ffmpeg_libs_dir.as_ref() { 493 | static_linking_with_libs_dir(&*LIBS, ffmpeg_libs_dir); 494 | if let Some(ffmpeg_binding_path) = env_vars.ffmpeg_binding_path.as_ref() { 495 | use_prebuilt_binding(ffmpeg_binding_path, output_binding_path); 496 | } else if let Some(ffmpeg_include_dir) = env_vars.ffmpeg_include_dir.as_ref() { 497 | generate_bindings(ffmpeg_include_dir, &HEADERS) 498 | .write_to_file(output_binding_path) 499 | .expect("Cannot write binding to file."); 500 | } else { 501 | panic!("No binding generation method is set!"); 502 | } 503 | } else { 504 | #[cfg(feature = "link_vcpkg_ffmpeg")] 505 | vcpkg_linking::linking_with_vcpkg_and_bindgen(env_vars, output_binding_path) 506 | .expect("Linking FFmpeg with vcpkg failed."); 507 | #[cfg(not(feature = "link_vcpkg_ffmpeg"))] 508 | panic!( 509 | " 510 | !!!!!!! rusty_ffmpeg: No linking method set! 511 | Use FFMPEG_LIBS_DIR if you have prebuilt FFmpeg libraries. 512 | Enable `link_vcpkg_ffmpeg` feature if you want to link ffmpeg provided by vcpkg. 513 | " 514 | ); 515 | } 516 | } 517 | } 518 | 519 | fn docs_rs_linking(env_vars: &EnvVars) { 520 | // If it's a documentation generation from docs.rs, just copy the bindings 521 | // generated locally to `OUT_DIR`. We do this because the building 522 | // environment of docs.rs doesn't have an network connection, so we cannot 523 | // git clone the FFmpeg. And they also have a limitation on crate's size: 524 | // 10MB, which is not enough to fit in FFmpeg source files. So the only 525 | // thing we can do is copying the locally generated binding files to the 526 | // `OUT_DIR`. 527 | let binding_file_path = &env_vars.out_dir.as_ref().unwrap().join("binding.rs"); 528 | use_prebuilt_binding(Path::new("src/binding.rs"), binding_file_path); 529 | } 530 | 531 | fn main() { 532 | let env_vars = EnvVars::init(); 533 | if env_vars.docs_rs.is_some() { 534 | docs_rs_linking(&env_vars); 535 | } else if env_vars.ffmpeg_dll_path.is_some() { 536 | dynamic_linking(&env_vars); 537 | } else { 538 | // fallback to static linking 539 | static_linking(&env_vars); 540 | } 541 | } 542 | -------------------------------------------------------------------------------- /examples/slice/bear.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/rusty_ffmpeg/bdabfd9afac6f7a9258457fc7739c93a0495a856/examples/slice/bear.mp4 -------------------------------------------------------------------------------- /examples/slice/main.rs: -------------------------------------------------------------------------------- 1 | //! Port from Original code: https://github.com/leandromoreira/ffmpeg-libav-tutorial/blob/master/0_hello_world.c 2 | 3 | use rusty_ffmpeg::ffi; 4 | 5 | use std::{ 6 | ffi::{CStr, CString}, 7 | fs::File, 8 | io::Write, 9 | ptr, slice, 10 | }; 11 | 12 | // This should only be used with FFmpeg 7 13 | fn main() { 14 | let filepath: CString = CString::new("./examples/slice/bear.mp4").unwrap(); 15 | 16 | println!("initializing all the containers, codecs and protocols."); 17 | 18 | let mut format_context_ptr = unsafe { ffi::avformat_alloc_context() }; 19 | if format_context_ptr.is_null() { 20 | panic!("ERROR could not allocate memory for Format Context"); 21 | } 22 | 23 | println!( 24 | "opening the input file ({}) and loading format (container) header", 25 | filepath.to_str().unwrap() 26 | ); 27 | 28 | if unsafe { 29 | ffi::avformat_open_input( 30 | &mut format_context_ptr, 31 | filepath.as_ptr(), 32 | ptr::null_mut(), 33 | ptr::null_mut(), 34 | ) 35 | } != 0 36 | { 37 | panic!("ERROR could not open the file"); 38 | } 39 | 40 | let format_context = unsafe { format_context_ptr.as_mut() }.unwrap(); 41 | 42 | let format_name = unsafe { CStr::from_ptr((*format_context.iformat).name) } 43 | .to_str() 44 | .unwrap(); 45 | 46 | println!( 47 | "format {}, duration {} us, bit_rate {}", 48 | format_name, format_context.duration, format_context.bit_rate 49 | ); 50 | 51 | println!("finding stream info from format"); 52 | 53 | if unsafe { ffi::avformat_find_stream_info(format_context, ptr::null_mut()) } < 0 { 54 | panic!("ERROR could not get the stream info"); 55 | } 56 | 57 | let mut codec_ptr: *const ffi::AVCodec = ptr::null_mut(); 58 | let mut codec_parameters_ptr: *const ffi::AVCodecParameters = ptr::null_mut(); 59 | let mut video_stream_index = None; 60 | 61 | let streams = unsafe { 62 | slice::from_raw_parts(format_context.streams, format_context.nb_streams as usize) 63 | }; 64 | 65 | for (i, stream) in streams 66 | .iter() 67 | .map(|stream| unsafe { stream.as_ref() }.unwrap()) 68 | .enumerate() 69 | { 70 | println!( 71 | "AVStream->time_base before open coded {}/{}", 72 | stream.time_base.num, stream.time_base.den 73 | ); 74 | println!( 75 | "AVStream->r_frame_rate before open coded {}/{}", 76 | stream.r_frame_rate.num, stream.r_frame_rate.den 77 | ); 78 | println!("AVStream->start_time {}", stream.start_time); 79 | println!("AVStream->duration {}", stream.duration); 80 | println!("finding the proper decoder (CODEC)"); 81 | 82 | let local_codec_params = unsafe { stream.codecpar.as_ref() }.unwrap(); 83 | let local_codec = 84 | unsafe { ffi::avcodec_find_decoder(local_codec_params.codec_id).as_ref() } 85 | .expect("ERROR unsupported codec!"); 86 | 87 | match local_codec_params.codec_type { 88 | ffi::AVMEDIA_TYPE_VIDEO => { 89 | if video_stream_index.is_none() { 90 | video_stream_index = Some(i); 91 | codec_ptr = local_codec; 92 | codec_parameters_ptr = local_codec_params; 93 | } 94 | 95 | println!( 96 | "Video Codec: resolution {} x {}", 97 | local_codec_params.width, local_codec_params.height 98 | ); 99 | } 100 | ffi::AVMEDIA_TYPE_AUDIO => { 101 | println!( 102 | "Audio Codec: {} channels, sample rate {}", 103 | local_codec_params.ch_layout.nb_channels, local_codec_params.sample_rate 104 | ); 105 | } 106 | _ => {} 107 | }; 108 | 109 | let codec_name = unsafe { CStr::from_ptr(local_codec.name) } 110 | .to_str() 111 | .unwrap(); 112 | 113 | println!( 114 | "\tCodec {} ID {} bit_rate {}", 115 | codec_name, local_codec.id, local_codec_params.bit_rate 116 | ); 117 | } 118 | let codec_context = unsafe { ffi::avcodec_alloc_context3(codec_ptr).as_mut() } 119 | .expect("failed to allocated memory for AVCodecContext"); 120 | 121 | if unsafe { ffi::avcodec_parameters_to_context(codec_context, codec_parameters_ptr) } < 0 { 122 | panic!("failed to copy codec params to codec context"); 123 | } 124 | 125 | if unsafe { ffi::avcodec_open2(codec_context, codec_ptr, ptr::null_mut()) } < 0 { 126 | panic!("failed to open codec through avcodec_open2"); 127 | } 128 | 129 | let frame = 130 | unsafe { ffi::av_frame_alloc().as_mut() }.expect("failed to allocated memory for AVFrame"); 131 | let packet = unsafe { ffi::av_packet_alloc().as_mut() } 132 | .expect("failed to allocated memory for AVPacket"); 133 | 134 | let mut packets_waiting = 8; 135 | 136 | while unsafe { ffi::av_read_frame(format_context, packet) } >= 0 { 137 | if video_stream_index == Some(packet.stream_index as usize) { 138 | println!("AVPacket->pts {}", packet.pts); 139 | decode_packet(packet, codec_context, frame).unwrap(); 140 | packets_waiting -= 1; 141 | if packets_waiting <= 0 { 142 | break; 143 | } 144 | } 145 | unsafe { ffi::av_packet_unref(packet) }; 146 | } 147 | 148 | println!("releasing all the resources"); 149 | 150 | unsafe { 151 | ffi::avformat_close_input(&mut (format_context as *mut _)); 152 | ffi::av_packet_free(&mut (packet as *mut _)); 153 | ffi::av_frame_free(&mut (frame as *mut _)); 154 | ffi::avcodec_free_context(&mut (codec_context as *mut _)); 155 | } 156 | } 157 | 158 | fn decode_packet( 159 | packet: &ffi::AVPacket, 160 | codec_context: &mut ffi::AVCodecContext, 161 | frame: &mut ffi::AVFrame, 162 | ) -> Result<(), String> { 163 | let mut response = unsafe { ffi::avcodec_send_packet(codec_context, packet) }; 164 | 165 | if response < 0 { 166 | return Err(String::from("Error while sending a packet to the decoder.")); 167 | } 168 | 169 | while response >= 0 { 170 | response = unsafe { ffi::avcodec_receive_frame(codec_context, frame) }; 171 | if response == ffi::AVERROR(ffi::EAGAIN) || response == ffi::AVERROR_EOF { 172 | break; 173 | } else if response < 0 { 174 | return Err(String::from( 175 | "Error while receiving a frame from the decoder.", 176 | )); 177 | } else { 178 | println!( 179 | "Frame {} (type={}, size={} bytes) pts {} key_frame {}", 180 | codec_context.frame_num, 181 | unsafe { ffi::av_get_picture_type_char(frame.pict_type) }, 182 | frame.pkt_size, 183 | frame.pts, 184 | frame.key_frame, 185 | ); 186 | 187 | let frame_filename = format!( 188 | "./examples/slice/output/frame-{}.pgm", 189 | codec_context.frame_num 190 | ); 191 | let width = frame.width as usize; 192 | let height = frame.height as usize; 193 | let wrap = frame.linesize[0] as usize; 194 | let data = unsafe { slice::from_raw_parts(frame.data[0], wrap * height) }; 195 | save_gray_frame(data, wrap, width, height, frame_filename).unwrap(); 196 | } 197 | } 198 | Ok(()) 199 | } 200 | 201 | fn save_gray_frame( 202 | buf: &[u8], 203 | wrap: usize, 204 | xsize: usize, 205 | ysize: usize, 206 | filename: String, 207 | ) -> Result<(), std::io::Error> { 208 | let mut file = File::create(filename)?; 209 | let data = format!("P5\n{} {}\n{}\n", xsize, ysize, 255); 210 | file.write_all(data.as_bytes())?; 211 | 212 | for i in 0..ysize { 213 | file.write_all(&buf[i * wrap..(i * wrap + xsize)])?; 214 | } 215 | Ok(()) 216 | } 217 | -------------------------------------------------------------------------------- /examples/slice/output/frame-1.pgm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/rusty_ffmpeg/bdabfd9afac6f7a9258457fc7739c93a0495a856/examples/slice/output/frame-1.pgm -------------------------------------------------------------------------------- /examples/slice/output/frame-2.pgm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/rusty_ffmpeg/bdabfd9afac6f7a9258457fc7739c93a0495a856/examples/slice/output/frame-2.pgm -------------------------------------------------------------------------------- /examples/slice/output/frame-3.pgm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/rusty_ffmpeg/bdabfd9afac6f7a9258457fc7739c93a0495a856/examples/slice/output/frame-3.pgm -------------------------------------------------------------------------------- /examples/slice/output/frame-4.pgm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/rusty_ffmpeg/bdabfd9afac6f7a9258457fc7739c93a0495a856/examples/slice/output/frame-4.pgm -------------------------------------------------------------------------------- /examples/slice/output/frame-5.pgm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/rusty_ffmpeg/bdabfd9afac6f7a9258457fc7739c93a0495a856/examples/slice/output/frame-5.pgm -------------------------------------------------------------------------------- /examples/slice/output/frame-6.pgm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/rusty_ffmpeg/bdabfd9afac6f7a9258457fc7739c93a0495a856/examples/slice/output/frame-6.pgm -------------------------------------------------------------------------------- /examples/slice/output/frame-7.pgm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCExtractor/rusty_ffmpeg/bdabfd9afac6f7a9258457fc7739c93a0495a856/examples/slice/output/frame-7.pgm -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | The `binding.rs` is only used for document generation from docs.rs, which should be updated on crate's version changing. -------------------------------------------------------------------------------- /src/avutil/_avutil.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi::{AVRational, AV_TIME_BASE}; 2 | use std::ffi::c_int; 3 | 4 | pub const AV_NOPTS_VALUE: i64 = 0x8000000000000000u64 as i64; 5 | pub const AV_TIME_BASE_Q: AVRational = AVRational { 6 | num: 1, 7 | den: AV_TIME_BASE as c_int, 8 | }; 9 | -------------------------------------------------------------------------------- /src/avutil/common.rs: -------------------------------------------------------------------------------- 1 | #[allow(non_snake_case)] 2 | pub const fn MKBETAG(a: u8, b: u8, c: u8, d: u8) -> u32 { 3 | (d as u32) | ((c as u32) << 8) | ((b as u32) << 16) | ((a as u32) << 24) 4 | } 5 | 6 | #[allow(non_snake_case)] 7 | pub const fn MKTAG(a: u8, b: u8, c: u8, d: u8) -> u32 { 8 | (a as u32) | ((b as u32) << 8) | ((c as u32) << 16) | ((d as u32) << 24) 9 | } 10 | -------------------------------------------------------------------------------- /src/avutil/error.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::c_char; 2 | use std::ffi::c_int; 3 | use std::ffi::CStr; 4 | use super::common::MKTAG; 5 | use crate::ffi; 6 | 7 | #[allow(non_snake_case)] 8 | pub const fn AVERROR(e: u32) -> c_int { 9 | -(e as c_int) 10 | } 11 | 12 | #[allow(non_snake_case)] 13 | pub const fn AVUNERROR(e: u32) -> c_int { 14 | -(e as c_int) 15 | } 16 | 17 | macro_rules! FFERRTAG { 18 | ($a:expr, $b:expr, $c:expr, $d:expr) => 19 | (-(MKTAG($a, $b, $c, $d) as c_int)) 20 | } 21 | 22 | pub const AVERROR_BSF_NOT_FOUND: c_int = FFERRTAG!(0xF8, b'B', b'S', b'F'); 23 | pub const AVERROR_BUG: c_int = FFERRTAG!(b'B', b'U', b'G', b'!'); 24 | pub const AVERROR_BUFFER_TOO_SMALL: c_int = FFERRTAG!(b'B', b'U', b'F', b'S'); 25 | pub const AVERROR_DECODER_NOT_FOUND: c_int = FFERRTAG!(0xF8, b'D', b'E', b'C'); 26 | pub const AVERROR_DEMUXER_NOT_FOUND: c_int = FFERRTAG!(0xF8, b'D', b'E', b'M'); 27 | pub const AVERROR_ENCODER_NOT_FOUND: c_int = FFERRTAG!(0xF8, b'E', b'N', b'C'); 28 | pub const AVERROR_EOF: c_int = FFERRTAG!(b'E', b'O', b'F', b' '); 29 | pub const AVERROR_EXIT: c_int = FFERRTAG!(b'E', b'X', b'I', b'T'); 30 | pub const AVERROR_EXTERNAL: c_int = FFERRTAG!(b'E', b'X', b'T', b' '); 31 | pub const AVERROR_FILTER_NOT_FOUND: c_int = FFERRTAG!(0xF8, b'F', b'I', b'L'); 32 | pub const AVERROR_INVALIDDATA: c_int = FFERRTAG!(b'I', b'N', b'D', b'A'); 33 | pub const AVERROR_MUXER_NOT_FOUND: c_int = FFERRTAG!(0xF8, b'M', b'U', b'X'); 34 | pub const AVERROR_OPTION_NOT_FOUND: c_int = FFERRTAG!(0xF8, b'O', b'P', b'T'); 35 | pub const AVERROR_PATCHWELCOME: c_int = FFERRTAG!(b'P', b'A', b'W', b'E'); 36 | pub const AVERROR_PROTOCOL_NOT_FOUND: c_int = FFERRTAG!(0xF8, b'P', b'R', b'O'); 37 | 38 | pub const AVERROR_STREAM_NOT_FOUND: c_int = FFERRTAG!(0xF8, b'S', b'T', b'R'); 39 | 40 | pub const AVERROR_BUG2: c_int = FFERRTAG!(b'B', b'U', b'G', b' '); 41 | pub const AVERROR_UNKNOWN: c_int = FFERRTAG!(b'U', b'N', b'K', b'N'); 42 | 43 | 44 | pub const AVERROR_HTTP_BAD_REQUEST: c_int = FFERRTAG!(0xF8, b'4', b'0', b'0'); 45 | pub const AVERROR_HTTP_UNAUTHORIZED: c_int = FFERRTAG!(0xF8, b'4', b'0', b'1'); 46 | pub const AVERROR_HTTP_FORBIDDEN: c_int = FFERRTAG!(0xF8, b'4', b'0', b'3'); 47 | pub const AVERROR_HTTP_NOT_FOUND: c_int = FFERRTAG!(0xF8, b'4', b'0', b'4'); 48 | pub const AVERROR_HTTP_OTHER_4XX: c_int = FFERRTAG!(0xF8, b'4', b'X', b'X'); 49 | pub const AVERROR_HTTP_SERVER_ERROR: c_int = FFERRTAG!(0xF8, b'5', b'X', b'X'); 50 | 51 | pub const AV_ERROR_MAX_STRING_SIZE: usize = 64; 52 | 53 | /// This function should not be called before the horsemen are ready. 54 | /// Fill the provided buffer with a string containing an error string 55 | /// corresponding to the AVERROR code errnum. 56 | /// 57 | /// @param errbuf a buffer 58 | /// @param errbuf_size size in bytes of errbuf 59 | /// @param errnum error code to describe 60 | /// @return the buffer in input, filled with the error description 61 | /// @see av_strerror() 62 | /// 63 | /// # Safety 64 | /// Safety requirements is the same as the av_strerror()` 65 | pub unsafe fn av_make_error_string( 66 | errbuf: *mut c_char, 67 | errbuf_size: usize, 68 | errnum: c_int 69 | ) -> *mut c_char { 70 | ffi::av_strerror(errnum, errbuf, errbuf_size); 71 | errbuf 72 | } 73 | 74 | pub fn av_err2str( 75 | errnum: c_int 76 | ) -> String { 77 | let mut errbuf = [0u8; AV_ERROR_MAX_STRING_SIZE]; 78 | let errbuf_ptr = errbuf.as_mut_ptr() as _; 79 | unsafe { av_make_error_string(errbuf_ptr, AV_ERROR_MAX_STRING_SIZE, errnum); } 80 | unsafe { CStr::from_ptr(errbuf_ptr) }.to_string_lossy().into() 81 | } 82 | 83 | #[cfg(test)] 84 | mod test { 85 | use super::*; 86 | 87 | #[test] 88 | fn test_err2str() { 89 | assert_eq!(&av_err2str(AVERROR(ffi::EINVAL)), "Invalid argument"); 90 | assert_eq!(&av_err2str(AVERROR(ffi::EAGAIN)), "Resource temporarily unavailable"); 91 | assert_eq!(&av_err2str(AVERROR(ffi::ENOMEM)), "Cannot allocate memory"); 92 | assert_eq!(&av_err2str(AVERROR_EOF), "End of file"); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/avutil/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod _avutil; 2 | pub mod common; 3 | #[rustfmt::skip] 4 | pub mod error; 5 | #[rustfmt::skip] 6 | pub mod pixfmt; 7 | pub mod rational; 8 | -------------------------------------------------------------------------------- /src/avutil/pixfmt.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_upper_case_globals)] 2 | 3 | use crate::ffi::*; 4 | 5 | macro_rules! AV_PIX_FMT_NE { 6 | ($def: ident, $be: ident, $le: ident) => { 7 | #[cfg(target_endian = "big")] 8 | pub const $def: AVPixelFormat = $be; 9 | 10 | #[cfg(target_endian = "little")] 11 | pub const $def: AVPixelFormat = $le; 12 | }; 13 | } 14 | 15 | AV_PIX_FMT_NE!(AV_PIX_FMT_RGB32, AV_PIX_FMT_ARGB, AV_PIX_FMT_BGRA); 16 | AV_PIX_FMT_NE!(AV_PIX_FMT_RGB32_1, AV_PIX_FMT_RGBA, AV_PIX_FMT_ABGR); 17 | AV_PIX_FMT_NE!(AV_PIX_FMT_BGR32, AV_PIX_FMT_ABGR, AV_PIX_FMT_RGBA); 18 | AV_PIX_FMT_NE!(AV_PIX_FMT_BGR32_1, AV_PIX_FMT_BGRA, AV_PIX_FMT_ARGB); 19 | AV_PIX_FMT_NE!(AV_PIX_FMT_0RGB32, AV_PIX_FMT_0RGB, AV_PIX_FMT_BGR0); 20 | AV_PIX_FMT_NE!(AV_PIX_FMT_0BGR32, AV_PIX_FMT_0BGR, AV_PIX_FMT_RGB0); 21 | 22 | AV_PIX_FMT_NE!(AV_PIX_FMT_GRAY9, AV_PIX_FMT_GRAY9BE, AV_PIX_FMT_GRAY9LE); 23 | AV_PIX_FMT_NE!(AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY10BE, AV_PIX_FMT_GRAY10LE); 24 | AV_PIX_FMT_NE!(AV_PIX_FMT_GRAY12, AV_PIX_FMT_GRAY12BE, AV_PIX_FMT_GRAY12LE); 25 | AV_PIX_FMT_NE!(AV_PIX_FMT_GRAY16, AV_PIX_FMT_GRAY16BE, AV_PIX_FMT_GRAY16LE); 26 | AV_PIX_FMT_NE!(AV_PIX_FMT_YA16, AV_PIX_FMT_YA16BE, AV_PIX_FMT_YA16LE); 27 | AV_PIX_FMT_NE!(AV_PIX_FMT_RGB48, AV_PIX_FMT_RGB48BE, AV_PIX_FMT_RGB48LE); 28 | AV_PIX_FMT_NE!(AV_PIX_FMT_RGB565, AV_PIX_FMT_RGB565BE, AV_PIX_FMT_RGB565LE); 29 | AV_PIX_FMT_NE!(AV_PIX_FMT_RGB555, AV_PIX_FMT_RGB555BE, AV_PIX_FMT_RGB555LE); 30 | AV_PIX_FMT_NE!(AV_PIX_FMT_RGB444, AV_PIX_FMT_RGB444BE, AV_PIX_FMT_RGB444LE); 31 | AV_PIX_FMT_NE!(AV_PIX_FMT_RGBA64, AV_PIX_FMT_RGBA64BE, AV_PIX_FMT_RGBA64LE); 32 | AV_PIX_FMT_NE!(AV_PIX_FMT_BGR48, AV_PIX_FMT_BGR48BE, AV_PIX_FMT_BGR48LE); 33 | AV_PIX_FMT_NE!(AV_PIX_FMT_BGR565, AV_PIX_FMT_BGR565BE, AV_PIX_FMT_BGR565LE); 34 | AV_PIX_FMT_NE!(AV_PIX_FMT_BGR555, AV_PIX_FMT_BGR555BE, AV_PIX_FMT_BGR555LE); 35 | AV_PIX_FMT_NE!(AV_PIX_FMT_BGR444, AV_PIX_FMT_BGR444BE, AV_PIX_FMT_BGR444LE); 36 | AV_PIX_FMT_NE!(AV_PIX_FMT_BGRA64, AV_PIX_FMT_BGRA64BE, AV_PIX_FMT_BGRA64LE); 37 | 38 | AV_PIX_FMT_NE!(AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV420P9BE , AV_PIX_FMT_YUV420P9LE); 39 | AV_PIX_FMT_NE!(AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV422P9BE , AV_PIX_FMT_YUV422P9LE); 40 | AV_PIX_FMT_NE!(AV_PIX_FMT_YUV444P9, AV_PIX_FMT_YUV444P9BE , AV_PIX_FMT_YUV444P9LE); 41 | AV_PIX_FMT_NE!(AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV420P10BE, AV_PIX_FMT_YUV420P10LE); 42 | AV_PIX_FMT_NE!(AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV422P10BE, AV_PIX_FMT_YUV422P10LE); 43 | AV_PIX_FMT_NE!(AV_PIX_FMT_YUV440P10, AV_PIX_FMT_YUV440P10BE, AV_PIX_FMT_YUV440P10LE); 44 | AV_PIX_FMT_NE!(AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV444P10BE, AV_PIX_FMT_YUV444P10LE); 45 | AV_PIX_FMT_NE!(AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV420P12BE, AV_PIX_FMT_YUV420P12LE); 46 | AV_PIX_FMT_NE!(AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV422P12BE, AV_PIX_FMT_YUV422P12LE); 47 | AV_PIX_FMT_NE!(AV_PIX_FMT_YUV440P12, AV_PIX_FMT_YUV440P12BE, AV_PIX_FMT_YUV440P12LE); 48 | AV_PIX_FMT_NE!(AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV444P12BE, AV_PIX_FMT_YUV444P12LE); 49 | AV_PIX_FMT_NE!(AV_PIX_FMT_YUV420P14, AV_PIX_FMT_YUV420P14BE, AV_PIX_FMT_YUV420P14LE); 50 | AV_PIX_FMT_NE!(AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV422P14BE, AV_PIX_FMT_YUV422P14LE); 51 | AV_PIX_FMT_NE!(AV_PIX_FMT_YUV444P14, AV_PIX_FMT_YUV444P14BE, AV_PIX_FMT_YUV444P14LE); 52 | AV_PIX_FMT_NE!(AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV420P16BE, AV_PIX_FMT_YUV420P16LE); 53 | AV_PIX_FMT_NE!(AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV422P16BE, AV_PIX_FMT_YUV422P16LE); 54 | AV_PIX_FMT_NE!(AV_PIX_FMT_YUV444P16, AV_PIX_FMT_YUV444P16BE, AV_PIX_FMT_YUV444P16LE); 55 | 56 | AV_PIX_FMT_NE!(AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP9BE , AV_PIX_FMT_GBRP9LE); 57 | AV_PIX_FMT_NE!(AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRP10BE, AV_PIX_FMT_GBRP10LE); 58 | AV_PIX_FMT_NE!(AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP12BE, AV_PIX_FMT_GBRP12LE); 59 | AV_PIX_FMT_NE!(AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP14BE, AV_PIX_FMT_GBRP14LE); 60 | AV_PIX_FMT_NE!(AV_PIX_FMT_GBRP16, AV_PIX_FMT_GBRP16BE, AV_PIX_FMT_GBRP16LE); 61 | AV_PIX_FMT_NE!(AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP10BE, AV_PIX_FMT_GBRAP10LE); 62 | AV_PIX_FMT_NE!(AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP12BE, AV_PIX_FMT_GBRAP12LE); 63 | #[cfg(feature = "ffmpeg7")] 64 | AV_PIX_FMT_NE!(AV_PIX_FMT_GBRAP14, AV_PIX_FMT_GBRAP14BE, AV_PIX_FMT_GBRAP14LE); 65 | AV_PIX_FMT_NE!(AV_PIX_FMT_GBRAP16, AV_PIX_FMT_GBRAP16BE, AV_PIX_FMT_GBRAP16LE); 66 | 67 | AV_PIX_FMT_NE!(AV_PIX_FMT_BAYER_BGGR16, AV_PIX_FMT_BAYER_BGGR16BE, AV_PIX_FMT_BAYER_BGGR16LE); 68 | AV_PIX_FMT_NE!(AV_PIX_FMT_BAYER_RGGB16, AV_PIX_FMT_BAYER_RGGB16BE, AV_PIX_FMT_BAYER_RGGB16LE); 69 | AV_PIX_FMT_NE!(AV_PIX_FMT_BAYER_GBRG16, AV_PIX_FMT_BAYER_GBRG16BE, AV_PIX_FMT_BAYER_GBRG16LE); 70 | AV_PIX_FMT_NE!(AV_PIX_FMT_BAYER_GRBG16, AV_PIX_FMT_BAYER_GRBG16BE, AV_PIX_FMT_BAYER_GRBG16LE); 71 | 72 | AV_PIX_FMT_NE!(AV_PIX_FMT_GBRPF32, AV_PIX_FMT_GBRPF32BE, AV_PIX_FMT_GBRPF32LE); 73 | AV_PIX_FMT_NE!(AV_PIX_FMT_GBRAPF32, AV_PIX_FMT_GBRAPF32BE, AV_PIX_FMT_GBRAPF32LE); 74 | 75 | AV_PIX_FMT_NE!(AV_PIX_FMT_GRAYF32, AV_PIX_FMT_GRAYF32BE, AV_PIX_FMT_GRAYF32LE); 76 | 77 | AV_PIX_FMT_NE!(AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA420P9BE , AV_PIX_FMT_YUVA420P9LE); 78 | AV_PIX_FMT_NE!(AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA422P9BE , AV_PIX_FMT_YUVA422P9LE); 79 | AV_PIX_FMT_NE!(AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_YUVA444P9BE , AV_PIX_FMT_YUVA444P9LE); 80 | AV_PIX_FMT_NE!(AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA420P10BE, AV_PIX_FMT_YUVA420P10LE); 81 | AV_PIX_FMT_NE!(AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA422P10BE, AV_PIX_FMT_YUVA422P10LE); 82 | AV_PIX_FMT_NE!(AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA444P10BE, AV_PIX_FMT_YUVA444P10LE); 83 | AV_PIX_FMT_NE!(AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA420P16BE, AV_PIX_FMT_YUVA420P16LE); 84 | AV_PIX_FMT_NE!(AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA422P16BE, AV_PIX_FMT_YUVA422P16LE); 85 | AV_PIX_FMT_NE!(AV_PIX_FMT_YUVA444P16, AV_PIX_FMT_YUVA444P16BE, AV_PIX_FMT_YUVA444P16LE); 86 | 87 | AV_PIX_FMT_NE!(AV_PIX_FMT_XYZ12, AV_PIX_FMT_XYZ12BE, AV_PIX_FMT_XYZ12LE); 88 | AV_PIX_FMT_NE!(AV_PIX_FMT_NV20, AV_PIX_FMT_NV20BE, AV_PIX_FMT_NV20LE); 89 | AV_PIX_FMT_NE!(AV_PIX_FMT_AYUV64, AV_PIX_FMT_AYUV64BE, AV_PIX_FMT_AYUV64LE); 90 | AV_PIX_FMT_NE!(AV_PIX_FMT_P010, AV_PIX_FMT_P010BE, AV_PIX_FMT_P010LE); 91 | #[cfg(any(feature = "ffmpeg6", feature = "ffmpeg7"))] 92 | AV_PIX_FMT_NE!(AV_PIX_FMT_P012, AV_PIX_FMT_P012BE, AV_PIX_FMT_P012LE); 93 | AV_PIX_FMT_NE!(AV_PIX_FMT_P016, AV_PIX_FMT_P016BE, AV_PIX_FMT_P016LE); 94 | 95 | #[cfg(any(feature = "ffmpeg5", feature = "ffmpeg6", feature = "ffmpeg7"))] 96 | AV_PIX_FMT_NE!(AV_PIX_FMT_Y210, AV_PIX_FMT_Y210BE, AV_PIX_FMT_Y210LE); 97 | #[cfg(any(feature = "ffmpeg6", feature = "ffmpeg7"))] 98 | AV_PIX_FMT_NE!(AV_PIX_FMT_Y212, AV_PIX_FMT_Y212BE, AV_PIX_FMT_Y212LE); 99 | #[cfg(any(feature = "ffmpeg6", feature = "ffmpeg7"))] 100 | AV_PIX_FMT_NE!(AV_PIX_FMT_XV30, AV_PIX_FMT_XV30BE, AV_PIX_FMT_XV30LE); 101 | #[cfg(any(feature = "ffmpeg6", feature = "ffmpeg7"))] 102 | AV_PIX_FMT_NE!(AV_PIX_FMT_XV36, AV_PIX_FMT_XV36BE, AV_PIX_FMT_XV36LE); 103 | #[cfg(any(feature = "ffmpeg5", feature = "ffmpeg6", feature = "ffmpeg7"))] 104 | AV_PIX_FMT_NE!(AV_PIX_FMT_X2RGB10, AV_PIX_FMT_X2RGB10BE, AV_PIX_FMT_X2RGB10LE); 105 | #[cfg(any(feature = "ffmpeg5", feature = "ffmpeg6", feature = "ffmpeg7"))] 106 | AV_PIX_FMT_NE!(AV_PIX_FMT_X2BGR10, AV_PIX_FMT_X2BGR10BE, AV_PIX_FMT_X2BGR10LE); 107 | 108 | #[cfg(any(feature = "ffmpeg5", feature = "ffmpeg6", feature = "ffmpeg7"))] 109 | AV_PIX_FMT_NE!(AV_PIX_FMT_P210, AV_PIX_FMT_P210BE, AV_PIX_FMT_P210LE); 110 | #[cfg(any(feature = "ffmpeg5", feature = "ffmpeg6", feature = "ffmpeg7"))] 111 | AV_PIX_FMT_NE!(AV_PIX_FMT_P410, AV_PIX_FMT_P410BE, AV_PIX_FMT_P410LE); 112 | #[cfg(feature = "ffmpeg7")] 113 | AV_PIX_FMT_NE!(AV_PIX_FMT_P212, AV_PIX_FMT_P212BE, AV_PIX_FMT_P212LE); 114 | #[cfg(feature = "ffmpeg7")] 115 | AV_PIX_FMT_NE!(AV_PIX_FMT_P412, AV_PIX_FMT_P412BE, AV_PIX_FMT_P412LE); 116 | #[cfg(any(feature = "ffmpeg5", feature = "ffmpeg6", feature = "ffmpeg7"))] 117 | AV_PIX_FMT_NE!(AV_PIX_FMT_P216, AV_PIX_FMT_P216BE, AV_PIX_FMT_P216LE); 118 | #[cfg(any(feature = "ffmpeg5", feature = "ffmpeg6", feature = "ffmpeg7"))] 119 | AV_PIX_FMT_NE!(AV_PIX_FMT_P416, AV_PIX_FMT_P416BE, AV_PIX_FMT_P416LE); 120 | 121 | #[cfg(any(feature = "ffmpeg6", feature = "ffmpeg7"))] 122 | AV_PIX_FMT_NE!(AV_PIX_FMT_RGBAF16, AV_PIX_FMT_RGBAF16BE, AV_PIX_FMT_RGBAF16LE); 123 | 124 | #[cfg(any(feature = "ffmpeg6", feature = "ffmpeg7"))] 125 | AV_PIX_FMT_NE!(AV_PIX_FMT_RGBF32, AV_PIX_FMT_RGBF32BE, AV_PIX_FMT_RGBF32LE); 126 | #[cfg(any(feature = "ffmpeg6", feature = "ffmpeg7"))] 127 | AV_PIX_FMT_NE!(AV_PIX_FMT_RGBAF32, AV_PIX_FMT_RGBAF32BE, AV_PIX_FMT_RGBAF32LE); 128 | -------------------------------------------------------------------------------- /src/avutil/rational.rs: -------------------------------------------------------------------------------- 1 | /// Bindgen is unable to generate static inline functions since they don't exist 2 | /// in linked library. So we need this. 3 | /// Ref: https://github.com/rust-lang/rust-bindgen/issues/1344 4 | use crate::ffi::AVRational; 5 | use std::ffi::{c_double, c_int}; 6 | 7 | /// Create an AVRational. 8 | /// 9 | /// Useful for compilers that do not support compound literals. 10 | /// 11 | /// @note The return value is not reduced. 12 | /// @see av_reduce() 13 | pub const fn av_make_q(num: c_int, den: c_int) -> AVRational { 14 | AVRational { num, den } 15 | } 16 | 17 | /// Compare two rationals. 18 | /// 19 | /// @param a First rational 20 | /// @param b Second rational 21 | /// 22 | /// @return One of the following values: 23 | /// - 0 if `a == b` 24 | /// - 1 if `a > b` 25 | /// - -1 if `a < b` 26 | /// - `INT_MIN` if one of the values is of the form `0 / 0` 27 | pub fn av_cmp_q(a: AVRational, b: AVRational) -> c_int { 28 | let tmp = i64::from(a.num) * i64::from(b.den) - i64::from(b.num) * i64::from(a.den); 29 | 30 | if tmp != 0 { 31 | (((tmp ^ i64::from(a.den) ^ i64::from(b.den)) >> 63) | 1) as c_int 32 | } else if b.den != 0 && a.den != 0 { 33 | 0 34 | } else if a.num != 0 && b.num != 0 { 35 | (a.num >> 31) - (b.num >> 31) 36 | } else { 37 | c_int::MIN 38 | } 39 | } 40 | 41 | /// Convert an AVRational to a `double`. 42 | /// @param a AVRational to convert 43 | /// @return `a` in floating-point form 44 | /// @see av_d2q() 45 | pub fn av_q2d(a: AVRational) -> c_double { 46 | c_double::from(a.num) / c_double::from(a.den) 47 | } 48 | 49 | /// Invert a rational. 50 | /// @param q value 51 | /// @return 1 / q 52 | pub const fn av_inv_q(q: AVRational) -> AVRational { 53 | AVRational { 54 | num: q.den, 55 | den: q.num, 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | mod avutil; 2 | 3 | #[allow( 4 | non_snake_case, 5 | non_camel_case_types, 6 | non_upper_case_globals, 7 | improper_ctypes, 8 | clippy::all 9 | )] 10 | pub mod ffi { 11 | pub use crate::avutil::{_avutil::*, common::*, error::*, pixfmt::*, rational::*}; 12 | include!(concat!(env!("OUT_DIR"), "/binding.rs")); 13 | } 14 | --------------------------------------------------------------------------------