Recherche avancée

Médias (91)

Autres articles (67)

  • 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 ;

  • Dépôt de média et thèmes par FTP

    31 mai 2013, par

    L’outil MédiaSPIP traite aussi les média transférés par la voie FTP. Si vous préférez déposer par cette voie, récupérez les identifiants d’accès vers votre site MédiaSPIP et utilisez votre client FTP favori.
    Vous trouverez dès le départ les dossiers suivants dans votre espace FTP : config/ : dossier de configuration du site IMG/ : dossier des média déjà traités et en ligne sur le site local/ : répertoire cache du site web themes/ : les thèmes ou les feuilles de style personnalisées tmp/ : dossier de travail (...)

  • Keeping control of your media in your hands

    13 avril 2011, par

    The vocabulary used on this site and around MediaSPIP in general, aims to avoid reference to Web 2.0 and the companies that profit from media-sharing.
    While using MediaSPIP, you are invited to avoid using words like "Brand", "Cloud" and "Market".
    MediaSPIP is designed to facilitate the sharing of creative media online, while allowing authors to retain complete control of their work.
    MediaSPIP aims to be accessible to as many people as possible and development is based on expanding the (...)

Sur d’autres sites (9540)

  • How to setup my complex filter with zoompan and xfade for ffmpeg using Fluent-FFmpeg .complexFilter method ?

    20 août 2024, par Rémy Groleau

    i'm using fluent-ffmpeg with nodejs. My problem is the setup of my complex filter.

    


    const filters = Array.from({ length: imageCount - 1 }).map((_, i) => {
        const rndInt = Math.floor(Math.random() * 1000) + 1

        return {
            zoompanFilter1: {
                filter: 'zoompan',
                options: {
                    z: 'min(zoom+0.001,1.3)',
                    d: `${imageDuration * 60}`, 
                    x: `iw/2-iw*(1/2-${rndInt}/100)*iw/zoom`, 
                    y: `ih/2-ih*(1/2-${rndInt}/100)*ih/zoom`, 
                    s: '1080x1920', // Output size
                    fps: '60'
                },
                inputs: `[${i}:v]`,
                outputs: `zoomed${i}`
            },
            zoompanFilter2: {
                filter: 'zoompan',
                options: {
                    z: 'min(zoom+0.001,1.3)', 
                    d: `${imageDuration * 60}`, 
                    x: 'iw/2-iw*(1/2-33/100)*iw/zoom',
                    y: 'ih/2-ih*(1/2-33/100)*ih/zoom',
                    s: '1080x1920', // Output size
                    fps: '60'
                },
                inputs: [`${i + 1}:v`],
                outputs: `zoomed${i + 1}`
            },
            xfadeFilter: {
                filter: 'xfade',
                options: {
                    transition: 'fade', // Crossfade transition effect
                    duration: '0.5', // Duration of crossfade in seconds
                    offset: `${imageDuration - 1}` // Offset to start the crossfade
                },
                inputs: [`zoomed${i + 1}`, `zoomed${i}`],
                outputs: `crossfaded${i}`
            },
        };
    });


    


    This is my complete code :

    


    async function createVideo() {
    // Escape file paths for Windows
    const videoPath = path.resolve(__dirname+ '/output.mp4').replace(/\\/g, '\\\\');
    const audioPath = path.resolve(__dirname+ '/output.mp3').replace(/\\/g, '\\\\');
    const backgroundMusicPath = path.resolve(__dirname+ '/background-music.mp3').replace(/\\/g, '\\\\');

    const command = ffmpeg();

    const imagesDir = path.join(__dirname, 'images');
    const images = fs.readdirSync(imagesDir)
        .filter(file => /.(jpg|jpeg|png)$/i.test(file)) // Filter image files
        .sort() // Sort filenames to ensure the correct order
        .map(file => path.join(imagesDir, file));

        images.map((image) => command.input(image))

    const imageCount = images.length;
    const audioDuration = await getAudioDurationInSeconds(audioPath);
    const imageDuration =  Math.round(audioDuration / imageCount)

    const filters = Array.from({ length: imageCount - 1 }).map((_, i) => {
        const rndInt = Math.floor(Math.random() * 1000) + 1

        return {
            zoompanFilter1: {
                filter: 'zoompan',
                options: {
                    z: 'min(zoom+0.001,1.3)', // Reset zoom to 1.0
                    d: `${imageDuration * 60}`, // Duration of the zoom effect
                    x: `iw/2-iw*(1/2-${rndInt}/100)*iw/zoom`, // Center x
                    y: `ih/2-ih*(1/2-${rndInt}/100)*ih/zoom`, // Center y
                    s: '1080x1920', // Output size
                    fps: '60'
                },
                inputs: `[${i}:v]`,
                outputs: `zoomed${i}`
            },
            zoompanFilter2: {
                filter: 'zoompan',
                options: {
                    z: 'min(zoom+0.001,1.3)', // Reset zoom to 1.0
                    d: `${imageDuration * 60}`, // Duration of the zoom effect
                    x: 'iw/2-iw*(1/2-33/100)*iw/zoom', // Center x
                    y: 'ih/2-ih*(1/2-33/100)*ih/zoom', // Center y
                    s: '1080x1920', // Output size
                    fps: '60'
                },
                inputs: [`${i + 1}:v`],
                outputs: `zoomed${i + 1}`
            },
            xfadeFilter: {
                filter: 'xfade',
                options: {
                    transition: 'fade', // Crossfade transition effect
                    duration: '0.5', // Duration of crossfade in seconds
                    offset: `${imageDuration - 1}` // Offset to start the crossfade
                },
                inputs: [`zoomed${i + 1}`, `zoomed${i}`],
                outputs: `crossfaded${i}`
            },
        };
    });

    command
    .input(audioPath)
    .input(backgroundMusicPath)
    .outputOptions([
        '-pix_fmt', 'yuv420p',
        '-c:v', 'libx264',
        '-c:a', 'aac',
        '-y',
        '-t', `${audioDuration}`,
        '-r', '60',
        '-s', '1080x1920',
        '-preset', 'ultrafast',
        '-map', '[final_video]',
        '-map', '[mixed_audio]',
    ])
    .complexFilter([
        // Apply zoompan filters and xfade transitions
        ...filters.flatMap(({ zoompanFilter1, zoompanFilter2, xfadeFilter }) => [
            zoompanFilter1,
            zoompanFilter2,
            xfadeFilter,
        ]),
        {
            filter: 'concat',
            options: {
                n: imageCount - 1, // Number of videos to concatenate
                v: 1, // Video streams
                a: 0 // No audio streams
            },
            inputs: filters.map((_, i) => `crossfaded${i}`),
            outputs: 'video_sequence'
        },
        {
            filter: 'curves',
            options: 'preset=increase_contrast',
            inputs: 'video_sequence',
            outputs: 'curves'
        },
        {
            filter: 'subtitles',
            options: `./subtitles.ass:fontsdir=./fonts/:force_style='FontName=Montserrat Black Italic,FontSize=17,MarginL=10,MarginV=25,Alignment=10,Spacing=0.2,Outline=0.1,Shadow=1.5'`,
            inputs: '[curves]',
            outputs: 'final_video'
        },
        {
            filter: 'volume',
            options: 0.3,  // Adjust the volume to 25% of the original
            inputs: `${imageCount + 1}:a`,
            outputs: 'background_music_adjusted'
        },
        // Apply the amix filter to mix the two audio inputs
        {
            filter: 'amix',
            options: {
                inputs: 2,
                duration: 'first',
                dropout_transition: 0,
                weights: '1 0.25',
                normalize: 0
            },
            inputs: [`${imageCount}:a`, 'background_music_adjusted'],
            outputs: 'mixed_audio'
        },
      ])
    .save(videoPath)
    .on('progress', function(progress) {
        console.log('Processing: ' + progress.percent + '% done');
      })
    .on('end', function(stdout, stderr) {
        // emptyFolder(imagesDir)
        console.log('Transcoding succeeded !');
      }) 
    .on('error', function(err) {
        console.error('Une erreur s\'est produite :', err.message);
    });
}


    


    My problem is i see the 1 image then the transition and i see the 2 image. After that i'm suppose to see the 3 image but i see the 2 image.

    


    I don't understand what i'm doing wrong...

    


    i tried switching the inputs: [zoomed$i + 1, zoomed$i] to inputs: [zoomed$i, zoomed$i+1] but it just showed the next image and not a loop of consecutive images.

    


  • How to Correctly Implement ffmpeg Complex Filters in Node.js for Image Processing ?

    24 janvier 2024, par Luke

    Problem :

    


    I am trying to add filters and transitions between my image slideshow array, and am struggling to apply the proper filters. For example, I get errors like this :

    


    {&#xA;  "errorType": "Error",&#xA;  "errorMessage": "ffmpeg exited with code 234: Failed to set value &#x27;fade=type=in:start_time=0:duration=1,zoompan=z=zoom&#x2B;0.002:d=120:x=if(gte(zoom,1.2),x,x&#x2B;1):y=if(gte(zoom,1.2),y,y&#x2B;1)&#x27; for option &#x27;filter_complex&#x27;: Invalid argument\nError parsing global options: Invalid argument\n",&#xA;  "trace": [&#xA;    "Error: ffmpeg exited with code 234: Failed to set value &#x27;fade=type=in:start_time=0:duration=1,zoompan=z=zoom&#x2B;0.002:d=120:x=if(gte(zoom,1.2),x,x&#x2B;1):y=if(gte(zoom,1.2),y,y&#x2B;1)&#x27; for option &#x27;filter_complex&#x27;: Invalid argument",&#xA;    "Error parsing global options: Invalid argument",&#xA;    "",&#xA;    "    at ChildProcess.<anonymous> (/opt/nodejs/node_modules/fluent-ffmpeg/lib/processor.js:182:22)",&#xA;    "    at ChildProcess.emit (node:events:517:28)",&#xA;    "    at ChildProcess._handle.onexit (node:internal/child_process:292:12)"&#xA;  ]&#xA;}&#xA;</anonymous>

    &#xA;

    Lambda Function Code :

    &#xA;

     async function concat(bucketName, imageKeys) {&#xA;      const imageStreams = await Promise.all(&#xA;        imageKeys.map(async (key, i) => {&#xA;          const command = new GetObjectCommand({ Bucket: bucketName, Key: key });&#xA;          const response = await s3.send(command);&#xA;          // Define the temporary file path based on the index&#xA;          const tempFilePath = `/tmp/${i}.png`;&#xA;    &#xA;          // Write the image data to the temporary file&#xA;          await fs.writeFile(tempFilePath, response.Body);&#xA;    &#xA;          // Return the file path to be used later&#xA;          return tempFilePath;&#xA;        })&#xA;      );&#xA;    &#xA;      // Create a file list content with durations&#xA;      let fileContent = "";&#xA;      for (let i = 0; i &lt; imageStreams.length; i&#x2B;&#x2B;) {&#xA;        fileContent &#x2B;= `file &#x27;${imageStreams[i]}&#x27;\nduration 1\n`;&#xA;    &#xA;        // Check if it&#x27;s the last image, and if so, add it again&#xA;        if (i === imageStreams.length - 1) {&#xA;          fileContent &#x2B;= `file &#x27;${imageStreams[i]}&#x27;\nduration 1\n`;&#xA;        }&#xA;      }&#xA;    &#xA;      // Define the file path for the file list&#xA;      const fileListPath = "/tmp/file_list.txt";&#xA;    &#xA;      // Write the file list content to the file&#xA;      await fs.writeFile(fileListPath, fileContent);&#xA;    &#xA;      try {&#xA;        await fs.writeFile(fileListPath, fileContent);&#xA;      } catch (error) {&#xA;        console.error("Error writing file list:", error);&#xA;        throw error;&#xA;      }&#xA;    &#xA;      // Create a complex filter to add zooms and pans&#xA;      // Simplified filter example&#xA;  let complexFilter = [&#xA;    // Example of a fade transition&#xA;    {&#xA;      filter: &#x27;fade&#x27;,&#xA;      options: { type: &#x27;in&#x27;, start_time: 0, duration: 1 },&#xA;      inputs: &#x27;0:v&#x27;, // first video stream&#xA;      outputs: &#x27;fade0&#x27;&#xA;    },&#xA;    // Example of dynamic zoompan&#xA;    {&#xA;      filter: &#x27;zoompan&#x27;,&#xA;      options: {&#xA;        z: &#x27;zoom&#x2B;0.002&#x27;,&#xA;        d: 120, // duration for this image&#xA;        x: &#x27;if(gte(zoom,1.2),x,x&#x2B;1)&#x27;, // dynamic x position&#xA;        y: &#x27;if(gte(zoom,1.2),y,y&#x2B;1)&#x27; // dynamic y position&#xA;      },&#xA;      inputs: &#x27;fade0&#x27;,&#xA;      outputs: &#x27;zoom0&#x27;&#xA;    }&#xA;    // Continue adding filters for each image&#xA;  ];&#xA;&#xA;  let filterString = complexFilter&#xA;    .map(&#xA;      (f) =>&#xA;        `${f.filter}=${Object.entries(f.options)&#xA;          .map(([key, value]) => `${key}=${value}`)&#xA;          .join(":")}`&#xA;    )&#xA;    .join(",");&#xA;    &#xA;      let filterString = complexFilter&#xA;        .map(&#xA;          (f) =>&#xA;            `${f.filter}=${Object.entries(f.options)&#xA;              .map(([key, value]) => `${key}=${value}`)&#xA;              .join(":")}`&#xA;        )&#xA;        .join(",");&#xA;    &#xA;      console.log("Filter String:", filterString);&#xA;    &#xA;      return new Promise((resolve, reject) => {&#xA;        ffmpeg()&#xA;          .input(fileListPath)&#xA;          .complexFilter(filterString)&#xA;          .inputOptions(["-f concat", "-safe 0"])&#xA;          .outputOptions("-c copy")&#xA;          .outputOptions("-c:v libx264")&#xA;          .outputOptions("-pix_fmt yuv420p")&#xA;          .outputOptions("-r 30")&#xA;          .on("end", () => {&#xA;            resolve();&#xA;          })&#xA;          .on("error", (err) => {&#xA;            console.error("Error during video concatenation:", err);&#xA;            reject(err);&#xA;          })&#xA;          .saveToFile("/tmp/output.mp4");&#xA;      });&#xA;    }&#xA;

    &#xA;

    Filter String Console Log :

    &#xA;

    Filter String: fade=type=in:start_time=0:duration=1,zoompan=z=zoom&#x2B;0.002:d=120:x=if(gte(zoom,1.2),x,x&#x2B;1):y=if(gte(zoom,1.2),y,y&#x2B;1)&#xA;

    &#xA;

    Questions :

    &#xA;

      &#xA;
    1. What is the correct syntax for implementing complex filters like zoompan and fade in ffmpeg when used in a Node.js environment ?
    2. &#xA;

    3. How do I ensure the filters are applied correctly to each image in the sequence ?
    4. &#xA;

    5. Is there a better way to dynamically generate these filters based on the number of images or their content ?
    6. &#xA;

    &#xA;

    Any insights or examples of correctly implementing this would be greatly appreciated !

    &#xA;

  • GLtransition not working properly with 480p

    13 juin 2019, par Yukesh Aravind

    I have a problem with GL Transition while rendering video at 480p, GL EFFECT applies but not as it was but it works fine for 1080P and 720P, here is my command :

    ffmpeg -y -loop 1 -t 5 -i /srv/sample/text_images/textimage_bRq4E_1560330312.png -loop 1 -t 8 -i /srv/sample/text_images/textimage_mZPFr_1560330318.png -loop 1 -t 16 -i 'https://pixabay.com/get/52e3d14a4b5ab114a6df8c7ece2a3e7f083edbe35352704f742779.jpg' -loop 1 -t 18 -i 'https://pixabay.com/get/55e6d7424f52b114a6df8c7ece2a3e7f083edbe35352704f742779.jpg' -t 8 -i '/srv/sample/img/image.png' -filter_complex "[2:v]scale=w=2556:h=1440,zoompan=z='zoom+0.001':x='if(gte(zoom,1.5),x,x-1)':y='y':s=852x480:d=180,setsar=1,drawbox=color=black@0:width=iw:height=ih:t=fill[v2];[v2][0:v]overlay=x=(main_w-overlay_w)/2:y=(main_h-overlay_h)/2[v2];[4:v]fps=25,scale=25:25,setsar=1:1[v4];[v2][v4]overlay=x=10:y=(main_h-overlay_h)-10[v2];[3:v]scale=w=2556:h=1440,zoompan=z='zoom+0.001':x='if(gte(zoom,1.5),x,x-1)':y='y':s=852x480:d=240,setsar=1,drawsamplev/sample/fonts/Catamaran/Catamaran-Regular.ttf:text=pixabay.com:fontcolor=white:fontsize=20:x=60:y=(h-text_h)-10,drawbox=color=black@0:width=iw:height=ih:t=fill[v3];[v3][1:v]overlay=x=(main_w-overlay_w)/2:y=(main_h-overlay_h)/2[v3];[4:v]fps=25,scale=25:25,setsar=1:1[v4];[v3][v4]overlay=x=10:y=(main_h-overlay_h)-10[v3];[v2]split[s12][s22];[s12]trim=0:5:start=0:duration=5[s12];[s12]setpts=PTS-STARTPTS[s12];[s22]trim=5:6[s22];[s22]setpts=PTS-STARTPTS[s22];[v3]split[s13][s23];[s13]trim=0:7:start=0:duration=7[s13];[s13]setpts=PTS-STARTPTS[s13];[s23]trim=7:8[s23];[s23]setpts=PTS-STARTPTS[s23];[s22][s13]gltransition=duration=1:source=/filters/CircleCrop.glsl[vt0];[s12][vt0][s23]concat=n=3:v=1:a=0[tmp]" -map '[tmp]' -pix_fmt yuvj444p /srv/sample/rendered/video_h5DjO_1560330312.mp4

    what makes to fail, please let me to understand.

    I also attach the image, please give me some suggestions

    enter image description here