Recherche avancée

Médias (1)

Mot : - Tags -/portrait

Autres articles (71)

  • Personnaliser en ajoutant son logo, sa bannière ou son image de fond

    5 septembre 2013, par

    Certains thèmes prennent en compte trois éléments de personnalisation : l’ajout d’un logo ; l’ajout d’une bannière l’ajout d’une image de fond ;

  • Gestion des droits de création et d’édition des objets

    8 février 2011, par

    Par défaut, beaucoup de fonctionnalités sont limitées aux administrateurs mais restent configurables indépendamment pour modifier leur statut minimal d’utilisation notamment : la rédaction de contenus sur le site modifiables dans la gestion des templates de formulaires ; l’ajout de notes aux articles ; l’ajout de légendes et d’annotations sur les images ;

  • Ecrire une actualité

    21 juin 2013, par

    Présentez les changements dans votre MédiaSPIP ou les actualités de vos projets sur votre MédiaSPIP grâce à la rubrique actualités.
    Dans le thème par défaut spipeo de MédiaSPIP, les actualités sont affichées en bas de la page principale sous les éditoriaux.
    Vous pouvez personnaliser le formulaire de création d’une actualité.
    Formulaire de création d’une actualité Dans le cas d’un document de type actualité, les champs proposés par défaut sont : Date de publication ( personnaliser la date de publication ) (...)

Sur d’autres sites (11164)

  • Safari sends excessive HTTP range requests during HTML5 MOV playback

    28 mai, par Lucy

    I am currently developing a web application based on React and Next.js, and I use the react-player library to play user-uploaded videos stored on AWS S3. The supported upload video formats are mp4, mov, and mkv, with codecs including H.264 or H.265 (HEVC).

    


    Problem

    


    However, some H.264 videos in the mov format exhibit noticeably poor playback performance only in the Safari browser. The videos do not play smoothly and frequently experience stuttering or delays.

    


    How Safari Handles Range Requests (As I Understand It) :

    


      

    1. Safari first sends a normal GET request to check if the server supports Range requests. If the server responds with the header Accept-Ranges : bytes, Safari closes the connection.
    2. 


    3. Then, Safari sends a very small range request like Range : bytes=0-1 to confirm it receives a 206 Partial Content response.
    4. 


    5. Next, Safari requests some parts from the beginning and the end of the file to locate metadata such as the moov atom in MP4 files.
    6. 


    7. After that, actual streaming begins, but Safari does not request the entire file at once ; instead, it divides the needed parts into multiple small Range requests.
    8. 


    


    Example of Actual Problematic Requests

    


    Below are some of the Range requests Safari makes for the problematic .mov video (H.264 codec) :

    


    # Request - 1
Connection: keep-alive
Range: bytes=0-1

# Response
HTTP/1.1 206 Partial Content
Accept-Ranges: bytes
Content-Disposition: attachment;filename*=utf-8''test.mov
Content-Length: 2
Content-Range: bytes 0-1/102801747
Content-Type: video/quicktime

-------------------------------------
# Request - 2
Connection: Keep-Alive
Range: bytes=0-102801746

# Response
HTTP/1.1 206 Partial Content
Accept-Ranges: bytes
Content-Disposition: attachment;filename*=utf-8''test.mov
Content-Length: 102801747
Content-Range: bytes 0-102801746/102801747
Content-Type: video/quicktime

-------------------------------------
# Request - 3
Connection: Keep-Alive
Range: bytes=102760448-102801746

# Response - 3
HTTP/1.1 206 Partial Content
Accept-Ranges: bytes
Content-Disposition: attachment;filename*=utf-8''test.mov
Content-Length: 41299
Content-Range: bytes 102760448-102801746/102801747
Content-Type: video/quicktime

-------------------------------------
# Request - 4
Connection: keep-alive
Range: bytes=3014656-3080191 # 64KB

# Response - 4
HTTP/1.1 206 Partial Content
Accept-Ranges: bytes
Content-Disposition: attachment;filename*=utf-8''IMG_7929.mov
Content-Length: 65536
Content-Range: bytes 3014656-3080191/102801747
Content-Type: video/quicktime


    


    Safari continues to send hundreds of similar small Range requests repeatedly, gradually downloading the file.

    


    This causes increased network load, which ultimately leads to video stuttering or playback delays.

    


    Example of another .mov file that works properly :

    


    In contrast, another .mov video using the same format and codec (H.264) sends Range requests over much larger byte ranges, downloading about 30MB of data at once. In this case, the video plays smoothly.

    


    # Request 
Connection: Keep-Alive
Range: bytes=1310534-673918954 # Request : 672MB, Actual Downloaded : 32MB

# Response
Accept-Ranges: bytes
Content-Disposition: attachment;filename*=utf-8''mov-example-video-download-4k-uhd-3840x2160.mov
Content-Length: 672608421
Content-Range: bytes 1310534-673918954/673918955
Content-Type: video/quicktime


    


    Question

    


    In Safari, for certain video files, the browser repeatedly closes the connection after receiving only a few kilobytes per request and immediately sends the next request. As a result, dozens or even hundreds of small range requests occur consecutively. This causes increased connection overhead and latency, and although the web server can deliver data at sufficient speed, the player does not receive data in time, leading to poor and stuttering video playback.

    


      

    1. What could be the reason Safari repeatedly makes these very small requests for certain files ?
    2. 


    3. Could the internal structure of the video file (such as the moov atom) influence this request pattern ?
    4. 


    


    f you have any similar experience or advice, please share.

    


    What I'ved tried

    


      

    • Using ffmpeg to move the moov atom to the beginning of the file with -movflags faststart does not resolve the issue — the same problem persists.
    • 


    • Converting the exact same video to MP4 format and testing it results in normal playback without issues.
    • 


    


    Test Environment

    


      

    • Macbook pro 16
    • 


    • mac OS - Sequoia 15.5
    • 


    • Safari - Latest
    • 


    


    File information

    


    Due to company policy, I am unable to share the problematic video files directly. However, I am attaching the file information obtained via ffprobe for your reference.

    


    ffprobe -v error -show_format -show_streams -print_format json test.mov

{
    "streams": [
        {
            "index": 0,
            "codec_name": "h264",
            "codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10",
            "profile": "High",
            "codec_type": "video",
            "codec_tag_string": "avc1",
            "codec_tag": "0x31637661",
            "width": 3840,
            "height": 2160,
            "coded_width": 3840,
            "coded_height": 2160,
            "closed_captions": 0,
            "film_grain": 0,
            "has_b_frames": 0,
            "pix_fmt": "yuv420p",
            "level": 51,
            "color_range": "tv",
            "color_space": "bt709",
            "color_transfer": "bt709",
            "color_primaries": "bt709",
            "chroma_location": "left",
            "field_order": "progressive",
            "refs": 1,
            "is_avc": "true",
            "nal_length_size": "4",
            "id": "0x1",
            "r_frame_rate": "30000/1001",
            "avg_frame_rate": "18200/607",
            "time_base": "1/600",
            "start_pts": 0,
            "start_time": "0.000000",
            "duration_ts": 10925,
            "duration": "18.208333",
            "bit_rate": "44900923",
            "bits_per_raw_sample": "8",
            "nb_frames": "546",
            "extradata_size": 158,
            "disposition": {
                "default": 1,
                "dub": 0,
                "original": 0,
                "comment": 0,
                "lyrics": 0,
                "karaoke": 0,
                "forced": 0,
                "hearing_impaired": 0,
                "visual_impaired": 0,
                "clean_effects": 0,
                "attached_pic": 0,
                "timed_thumbnails": 0,
                "non_diegetic": 0,
                "captions": 0,
                "descriptions": 0,
                "metadata": 0,
                "dependent": 0,
                "still_image": 0,
                "multilayer": 0
            },
            "tags": {
                "creation_time": "",
                "language": "und",
                "handler_name": "Core Media Video",
                "vendor_id": "[0][0][0][0]",
                "encoder": "H.264"
            }
        },
        {
            "index": 1,
            "codec_name": "aac",
            "codec_long_name": "AAC (Advanced Audio Coding)",
            "profile": "LC",
            "codec_type": "audio",
            "codec_tag_string": "mp4a",
            "codec_tag": "0x6134706d",
            "sample_fmt": "fltp",
            "sample_rate": "44100",
            "channels": 2,
            "channel_layout": "stereo",
            "bits_per_sample": 0,
            "initial_padding": 0,
            "id": "0x2",
            "r_frame_rate": "0/0",
            "avg_frame_rate": "0/0",
            "time_base": "1/44100",
            "start_pts": 0,
            "start_time": "0.000000",
            "duration_ts": 802988,
            "duration": "18.208345",
            "bit_rate": "187218",
            "nb_frames": "787",
            "extradata_size": 2,
            "disposition": {
                "default": 1,
                "dub": 0,
                "original": 0,
                "comment": 0,
                "lyrics": 0,
                "karaoke": 0,
                "forced": 0,
                "hearing_impaired": 0,
                "visual_impaired": 0,
                "clean_effects": 0,
                "attached_pic": 0,
                "timed_thumbnails": 0,
                "non_diegetic": 0,
                "captions": 0,
                "descriptions": 0,
                "metadata": 0,
                "dependent": 0,
                "still_image": 0,
                "multilayer": 0
            },
            "tags": {
                "creation_time": "",
                "language": "und",
                "handler_name": "Core Media Audio",
                "vendor_id": "[0][0][0][0]"
            }
        },
        {
            "index": 2,
            "codec_type": "data",
            "codec_tag_string": "mebx",
            "codec_tag": "0x7862656d",
            "id": "0x3",
            "r_frame_rate": "0/0",
            "avg_frame_rate": "0/0",
            "time_base": "1/600",
            "start_pts": 0,
            "start_time": "0.000000",
            "duration_ts": 10925,
            "duration": "18.208333",
            "bit_rate": "4",
            "nb_frames": "1",
            "disposition": {
                "default": 1,
                "dub": 0,
                "original": 0,
                "comment": 0,
                "lyrics": 0,
                "karaoke": 0,
                "forced": 0,
                "hearing_impaired": 0,
                "visual_impaired": 0,
                "clean_effects": 0,
                "attached_pic": 0,
                "timed_thumbnails": 0,
                "non_diegetic": 0,
                "captions": 0,
                "descriptions": 0,
                "metadata": 0,
                "dependent": 0,
                "still_image": 0,
                "multilayer": 0
            },
            "tags": {
                "creation_time": "",
                "language": "und",
                "handler_name": "Core Media Metadata"
            }
        },
        {
            "index": 3,
            "codec_type": "data",
            "codec_tag_string": "mebx",
            "codec_tag": "0x7862656d",
            "id": "0x4",
            "r_frame_rate": "0/0",
            "avg_frame_rate": "0/0",
            "time_base": "1/600",
            "start_pts": 0,
            "start_time": "0.000000",
            "duration_ts": 10925,
            "duration": "18.208333",
            "bit_rate": "6756",
            "nb_frames": "80",
            "disposition": {
                "default": 1,
                "dub": 0,
                "original": 0,
                "comment": 0,
                "lyrics": 0,
                "karaoke": 0,
                "forced": 0,
                "hearing_impaired": 0,
                "visual_impaired": 0,
                "clean_effects": 0,
                "attached_pic": 0,
                "timed_thumbnails": 0,
                "non_diegetic": 0,
                "captions": 0,
                "descriptions": 0,
                "metadata": 0,
                "dependent": 0,
                "still_image": 0,
                "multilayer": 0
            },
            "tags": {
                "creation_time": "",
                "language": "und",
                "handler_name": "Core Media Metadata"
            }
        },
        {
            "index": 4,
            "codec_type": "data",
            "codec_tag_string": "mebx",
            "codec_tag": "0x7862656d",
            "id": "0x5",
            "r_frame_rate": "0/0",
            "avg_frame_rate": "0/0",
            "time_base": "1/600",
            "start_pts": 0,
            "start_time": "0.000000",
            "duration_ts": 10925,
            "duration": "18.208333",
            "bit_rate": "50764",
            "nb_frames": "546",
            "disposition": {
                "default": 1,
                "dub": 0,
                "original": 0,
                "comment": 0,
                "lyrics": 0,
                "karaoke": 0,
                "forced": 0,
                "hearing_impaired": 0,
                "visual_impaired": 0,
                "clean_effects": 0,
                "attached_pic": 0,
                "timed_thumbnails": 0,
                "non_diegetic": 0,
                "captions": 0,
                "descriptions": 0,
                "metadata": 0,
                "dependent": 0,
                "still_image": 0,
                "multilayer": 0
            },
            "tags": {
                "creation_time": "",
                "language": "und",
                "handler_name": "Core Media Metadata"
            }
        },
        {
            "index": 5,
            "codec_type": "data",
            "codec_tag_string": "mebx",
            "codec_tag": "0x7862656d",
            "id": "0x6",
            "r_frame_rate": "0/0",
            "avg_frame_rate": "0/0",
            "time_base": "1/600",
            "start_pts": 0,
            "start_time": "0.000000",
            "duration_ts": 10925,
            "duration": "18.208333",
            "bit_rate": "19",
            "nb_frames": "1",
            "disposition": {
                "default": 1,
                "dub": 0,
                "original": 0,
                "comment": 0,
                "lyrics": 0,
                "karaoke": 0,
                "forced": 0,
                "hearing_impaired": 0,
                "visual_impaired": 0,
                "clean_effects": 0,
                "attached_pic": 0,
                "timed_thumbnails": 0,
                "non_diegetic": 0,
                "captions": 0,
                "descriptions": 0,
                "metadata": 0,
                "dependent": 0,
                "still_image": 0,
                "multilayer": 0
            },
            "tags": {
                "creation_time": "",
                "language": "und",
                "handler_name": "Core Media Metadata"
            }
        }
    ],
    "format": {
        "filename": "test.mov",
        "nb_streams": 6,
        "nb_programs": 0,
        "nb_stream_groups": 0,
        "format_name": "mov,mp4,m4a,3gp,3g2,mj2",
        "format_long_name": "QuickTime / MOV",
        "start_time": "0.000000",
        "duration": "18.208345",
        "size": "102801747",
        "bit_rate": "45166871",
        "probe_score": 100,
        "tags": {
            "major_brand": "qt  ",
            "minor_version": "0",
            "compatible_brands": "qt  ",
            "creation_time": "",
            "com.apple.quicktime.location.accuracy.horizontal": "76.200233",
            "com.apple.quicktime.full-frame-rate-playback-intent": "0",
            "com.apple.quicktime.location.ISO6709": "",
            "com.apple.quicktime.make": "Apple",
            "com.apple.quicktime.model": "iPhone 15 Pro",
            "com.apple.quicktime.software": "18.4.1",
            "com.apple.quicktime.creationdate": ""
        }
    }
}


    


  • avformat/iamf : fix setting channel layout for Scalable layers

    17 juin, par James Almer
    avformat/iamf : fix setting channel layout for Scalable layers
    

    The way streams are coded in an IAMF struct follows a scalable model where the
    channel layouts for each layer may not match the channel order our API can
    represent in a Native order layout.

    For example, an audio element may have six coded streams in the form of two
    stereo streams, followed by two mono streams, and then by another two stereo
    streams, for a total of 10 channels, and define for them four scalable layers
    with loudspeaker_layout values "Stereo", "5.1ch", "5.1.2ch", and "5.1.4ch".
    The first layer references the first stream, and each following layer will
    reference all previous streams plus extra ones.
    In this case, the "5.1ch" layer will reference four streams (the first two
    stereo and the two mono) to encompass six channels, which does not match out
    native layout 5.1(side) given that FC and LFE come after FL+FR but before
    SL+SR, and here, they are at the end.

    For this reason, we need to build Custom order layouts that properly represent
    what we're exporting.


    Before :

    Stream group #0:0[0x12c] : IAMF Audio Element :
    Layer 0 : stereo
    Stream #0:0[0x0] : Audio : opus, 48000 Hz, stereo, fltp (default)
    Layer 1 : 5.1(side)
    Stream #0:0[0x0] : Audio : opus, 48000 Hz, stereo, fltp (default)
    Stream #0:1[0x1] : Audio : opus, 48000 Hz, stereo, fltp (dependent)
    Stream #0:2[0x2] : Audio : opus, 48000 Hz, mono, fltp (dependent)
    Stream #0:3[0x3] : Audio : opus, 48000 Hz, mono, fltp (dependent)
    Layer 2 : 5.1.2
    Stream #0:0[0x0] : Audio : opus, 48000 Hz, stereo, fltp (default)
    Stream #0:1[0x1] : Audio : opus, 48000 Hz, stereo, fltp (dependent)
    Stream #0:2[0x2] : Audio : opus, 48000 Hz, mono, fltp (dependent)
    Stream #0:3[0x3] : Audio : opus, 48000 Hz, mono, fltp (dependent)
    Stream #0:4[0x4] : Audio : opus, 48000 Hz, stereo, fltp (dependent)
    Layer 3 : 5.1.4
    Stream #0:0[0x0] : Audio : opus, 48000 Hz, stereo, fltp (default)
    Stream #0:1[0x1] : Audio : opus, 48000 Hz, stereo, fltp (dependent)
    Stream #0:2[0x2] : Audio : opus, 48000 Hz, mono, fltp (dependent)
    Stream #0:3[0x3] : Audio : opus, 48000 Hz, mono, fltp (dependent)
    Stream #0:4[0x4] : Audio : opus, 48000 Hz, stereo, fltp (dependent)
    Stream #0:5[0x5] : Audio : opus, 48000 Hz, stereo, fltp (dependent)


    AFter :

    Stream group #0:0[0x12c] : IAMF Audio Element :
    Layer 0 : stereo
    Stream #0:0[0x0] : Audio : opus, 48000 Hz, stereo, fltp (default)
    Layer 1 : 6 channels (FL+FR+SL+SR+FC+LFE)
    Stream #0:0[0x0] : Audio : opus, 48000 Hz, stereo, fltp (default)
    Stream #0:1[0x1] : Audio : opus, 48000 Hz, stereo, fltp (dependent)
    Stream #0:2[0x2] : Audio : opus, 48000 Hz, mono, fltp (dependent)
    Stream #0:3[0x3] : Audio : opus, 48000 Hz, mono, fltp (dependent)
    Layer 2 : 8 channels (FL+FR+SL+SR+FC+LFE+TFL+TFR)
    Stream #0:0[0x0] : Audio : opus, 48000 Hz, stereo, fltp (default)
    Stream #0:1[0x1] : Audio : opus, 48000 Hz, stereo, fltp (dependent)
    Stream #0:2[0x2] : Audio : opus, 48000 Hz, mono, fltp (dependent)
    Stream #0:3[0x3] : Audio : opus, 48000 Hz, mono, fltp (dependent)
    Stream #0:4[0x4] : Audio : opus, 48000 Hz, stereo, fltp (dependent)
    Layer 3 : 10 channels (FL+FR+SL+SR+FC+LFE+TFL+TFR+TBL+TBR)
    Stream #0:0[0x0] : Audio : opus, 48000 Hz, stereo, fltp (default)
    Stream #0:1[0x1] : Audio : opus, 48000 Hz, stereo, fltp (dependent)
    Stream #0:2[0x2] : Audio : opus, 48000 Hz, mono, fltp (dependent)
    Stream #0:3[0x3] : Audio : opus, 48000 Hz, mono, fltp (dependent)
    Stream #0:4[0x4] : Audio : opus, 48000 Hz, stereo, fltp (dependent)
    Stream #0:5[0x5] : Audio : opus, 48000 Hz, stereo, fltp (dependent)

    Signed-off-by : James Almer <jamrial@gmail.com>

    • [DH] libavformat/iamf_parse.c
    • [DH] libavformat/iamf_writer.c
    • [DH] libavformat/iamfdec.c
    • [DH] tests/ref/fate/iamf-5_1-copy
    • [DH] tests/ref/fate/iamf-5_1-demux
    • [DH] tests/ref/fate/iamf-5_1_4
    • [DH] tests/ref/fate/iamf-7_1_4
    • [DH] tests/ref/fate/iamf-9_1_6
    • [DH] tests/ref/fate/mov-mp4-iamf-5_1_4
    • [DH] tests/ref/fate/mov-mp4-iamf-7_1_4-video-first
    • [DH] tests/ref/fate/mov-mp4-iamf-7_1_4-video-last
  • Flutter Android release crash with ffmpeg_kit_flutter_new : Bad JNI version returned from JNI_OnLoad in libffmpegkit_abidetect.so (debug works) [closed]

    20 août, par Muhammad Rakha Almasah

    Problem&#xA;My Flutter app crashes only in release when the FFmpegKit plugin is registered. The app runs fine in debug on the same devices. The crash happens before my UI shows up, during plugin registration.

    &#xA;

    Error (release logcat)

    &#xA;

    On Samsung SM-A105G (Android 11, API 30, 32-bit device) :

    &#xA;

    E/AndroidRuntime: FATAL EXCEPTION: main&#xA;java.lang.Error: FFmpegKit failed to start on brand: samsung, model: SM-A105G, device: a10, api level: 30,&#xA;abis: armeabi-v7a armeabi, 32bit abis: armeabi-v7a armeabi, 64bit abis: .&#xA;    at com.antonkarpenko.ffmpegkit.AbiDetect.<clinit>(SourceFile:3)&#xA;    at com.antonkarpenko.ffmpegkit.FFmpegKitConfig.<clinit>(SourceFile:16)&#xA;    ...&#xA;Caused by: java.lang.UnsatisfiedLinkError: Bad JNI version returned from JNI_OnLoad in&#xA;"/data/app/.../lib/arm/libffmpegkit_abidetect.so": 0&#xA;    at java.lang.Runtime.loadLibrary0(Runtime.java:1087)&#xA;    at java.lang.System.loadLibrary(System.java:1664)&#xA;&#xA;</clinit></clinit>

    &#xA;

    On Redmi 24094RAD4G (Android 15/35, 64-bit) :

    &#xA;

    java.lang.Error: FFmpegKit failed to start on brand: Redmi, model: 24094RAD4G, device: citrine, api level: 35,&#xA;abis: arm64-v8a armeabi-v7a armeabi, 32bit abis: armeabi-v7a armeabi, 64bit abis: arm64-v8a.&#xA;...&#xA;Caused by: java.lang.UnsatisfiedLinkError: Bad JNI version returned from JNI_OnLoad in&#xA;"/data/app/.../split_config.arm64_v8a.apk!/lib/arm64-v8a/libffmpegkit_abidetect.so": 0&#xA;&#xA;

    &#xA;

    What’s strange&#xA;In debug the plugin loads and prints :

    &#xA;

    I/ffmpeg-kit: Loaded ffmpeg-kit-full-gpl-arm-v7a-neon-6.0-20250810.&#xA;

    &#xA;

    and the app works normally.

    &#xA;

    Environment

    &#xA;

    Flutter (stable), Android

    &#xA;

    compileSdk = 35, minSdk = 24, targetSdk = 35

    &#xA;

    NDK I tried :

    &#xA;

    26.1.10909125 (recommended by many plugins)

    &#xA;

    25.2.9519653

    &#xA;

    Devices that crash in release :

    &#xA;

    Samsung SM-A105G (32-bit only, API 30)

    &#xA;

    Redmi 24094RAD4G (arm64-v8a capable, API 35)

    &#xA;

    Dependencies (relevant)

    &#xA;

    I mainly need FFmpeg through a video editor workflow.

    &#xA;

    Tried these FFmpeg packages :

    &#xA;

    ffmpeg_kit_flutter_new 3.2.0 → release crash

    &#xA;

    ffmpeg_kit_flutter_new 1.6.1 → release crash

    &#xA;

    (for testing) the discontinued ffmpeg_kit_flutter 6.0.3 → gradle couldn’t resolve com.arthenica:ffmpeg-kit-https:6.0-2 in release

    &#xA;

    Other media deps : video_editor (git), video_thumbnail (git), video_player, etc

    &#xA;

    App config&#xA;android/app/build.gradle

    &#xA;

    plugins {&#xA;    id "com.android.application"&#xA;    id "kotlin-android"&#xA;    id "dev.flutter.flutter-gradle-plugin"&#xA;}&#xA;&#xA;def localProperties = new Properties()&#xA;def localPropertiesFile = rootProject.file(&#x27;local.properties&#x27;)&#xA;if (localPropertiesFile.exists()) {&#xA;    localPropertiesFile.withReader(&#x27;UTF-8&#x27;) { reader ->&#xA;        localProperties.load(reader)&#xA;    }&#xA;}&#xA;&#xA;def keystoreProperties = new Properties()&#xA;def keystorePropertiesFile = rootProject.file(&#x27;key.properties&#x27;)&#xA;if (keystorePropertiesFile.exists()) {&#xA;    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))&#xA;}&#xA;&#xA;&#xA;def flutterVersionCode = localProperties.getProperty(&#x27;flutter.versionCode&#x27;)&#xA;if (flutterVersionCode == null) {&#xA;    flutterVersionCode = &#x27;1&#x27;&#xA;}&#xA;&#xA;def flutterVersionName = localProperties.getProperty(&#x27;flutter.versionName&#x27;)&#xA;if (flutterVersionName == null) {&#xA;    flutterVersionName = &#x27;1.0&#x27;&#xA;}&#xA;&#xA;android {&#xA;    namespace "id.fitacademy.app"&#xA;    compileSdk = 35&#xA;    ndkVersion = "26.1.10909125"&#xA;&#xA;    compileOptions {&#xA;        sourceCompatibility JavaVersion.VERSION_1_8&#xA;        targetCompatibility JavaVersion.VERSION_1_8&#xA;    }&#xA;&#xA;    kotlinOptions {&#xA;        jvmTarget = JavaVersion.VERSION_1_8&#xA;    }&#xA;&#xA;    sourceSets {&#xA;        main.java.srcDirs &#x2B;= &#x27;src/main/kotlin&#x27;&#xA;    }&#xA;&#xA;    defaultConfig {&#xA;        applicationId "id.fitacademy.app"&#xA;        minSdkVersion 24&#xA;        targetSdkVersion 35&#xA;        versionCode flutterVersionCode.toInteger()&#xA;        versionName flutterVersionName&#xA;        multiDexEnabled true&#xA;        ndk { abiFilters "armeabi-v7a" }&#xA;    }&#xA;&#xA;    packagingOptions {&#xA;        jniLibs { useLegacyPackaging true }   // muat .so via ekstraksi&#xA;        // pickFirsts &#x2B;= [&#x27;**/libc&#x2B;&#x2B;_shared.so&#x27;]  // aktifkan hanya kalau ada konflik duplikat&#xA;    }&#xA;&#xA;    signingConfigs {&#xA;       release {&#xA;           keyAlias keystoreProperties[&#x27;keyAlias&#x27;]&#xA;           keyPassword keystoreProperties[&#x27;keyPassword&#x27;]&#xA;           storeFile keystoreProperties[&#x27;storeFile&#x27;] ? file(keystoreProperties[&#x27;storeFile&#x27;]) : null&#xA;           storePassword keystoreProperties[&#x27;storePassword&#x27;]&#xA;       }&#xA;        debug {&#xA;            keyAlias keystoreProperties[&#x27;keyAlias&#x27;]&#xA;            keyPassword keystoreProperties[&#x27;keyPassword&#x27;]&#xA;            storeFile keystoreProperties[&#x27;storeFile&#x27;] ? file(keystoreProperties[&#x27;storeFile&#x27;]) : null&#xA;            storePassword keystoreProperties[&#x27;storePassword&#x27;]&#xA;        }&#xA;   }&#xA;   buildTypes {&#xA;       release {&#xA;           signingConfig signingConfigs.release&#xA;       }&#xA;        debug {&#xA;            signingConfig signingConfigs.debug&#xA;        }&#xA;   }&#xA;}&#xA;&#xA;flutter {&#xA;    source &#x27;../..&#x27;&#xA;}&#xA;&#xA;

    &#xA;

    AndroidManifest.xml

    &#xA;

    <manifest>&#xA;&#xA;    &#xA;    &#xA;    &#xA;    &#xA;    &#xA;    &#xA;    &#xA;    &#xA;    &#xA;    &#xA;    &#xA;&#xA;    <queries>&#xA;        <package></package>&#xA;        <package></package>&#xA;        <package></package>&#xA;        <package></package>&#xA;        <package></package>&#xA;        <package></package>&#xA;        <package></package>&#xA;&#xA;        <provider></provider> &#xA;        <provider></provider> &#xA;&#xA;        <intent>&#xA;            <action></action>&#xA;            <data></data>&#xA;        </intent>&#xA;    </queries>&#xA;&#xA;    &#xA;        &#xA;            &#xA;            &#xA;            &#xA;                <action></action>&#xA;                <category></category>&#xA;            &#xA;&#xA;            &#xA;            &#xA;            &#xA;                <action></action>&#xA;                <category></category>&#xA;                <category></category>&#xA;                <data></data>&#xA;                <data></data>&#xA;                <data></data>&#xA;                <data></data>&#xA;            &#xA;        &#xA;&#xA;        &#xA;        &#xA;&#xA;&#xA;        &#xA;        &#xA;&#xA;        <provider>&#xA;            &#xA;        </provider>&#xA;&#xA;        &#xA;        &#xA;    &#xA;</manifest>&#xA;

    &#xA;

    APK / AAB inspection

    &#xA;

    jar tf app-release.apk | findstr ffmpegkit_abidetect shows the library is packaged, e.g. :

    &#xA;

    lib/arm/libffmpegkit_abidetect.so (on 32-bit build)

    &#xA;

    lib/arm64-v8a/libffmpegkit_abidetect.so (on 64-bit)

    &#xA;

    So the .so is present in the release artifact.

    &#xA;

    What I tried

    &#xA;

    Switch NDK 25.2 ↔ 26.1 and align with plugins.

    &#xA;

    Add android:extractNativeLibs="true" in the manifest.

    &#xA;

    packagingOptions jniLibs useLegacyPackaging true .

    &#xA;

    Build both APK and AAB, and also flutter run —release -d .

    &#xA;

    With and without abiFilters.

    &#xA;

    Full flutter clean, delete Gradle caches, rebuild.

    &#xA;

    Verified .env files and other assets are included (unrelated but checked).

    &#xA;

    The crash always happens at plugin startup in release with :&#xA;Bad JNI version returned from JNI_OnLoad in ... libffmpegkit_abidetect.so : 0.

    &#xA;