Recherche avancée

Médias (17)

Mot : - Tags -/wired

Autres articles (100)

  • ANNEXE : Les plugins utilisés spécifiquement pour la ferme

    5 mars 2010, par

    Le site central/maître de la ferme a besoin d’utiliser plusieurs plugins supplémentaires vis à vis des canaux pour son bon fonctionnement. le plugin Gestion de la mutualisation ; le plugin inscription3 pour gérer les inscriptions et les demandes de création d’instance de mutualisation dès l’inscription des utilisateurs ; le plugin verifier qui fournit une API de vérification des champs (utilisé par inscription3) ; le plugin champs extras v2 nécessité par inscription3 (...)

  • Problèmes fréquents

    10 mars 2010, par

    PHP et safe_mode activé
    Une des principales sources de problèmes relève de la configuration de PHP et notamment de l’activation du safe_mode
    La solution consiterait à soit désactiver le safe_mode soit placer le script dans un répertoire accessible par apache pour le site

  • Support de tous types de médias

    10 avril 2011

    Contrairement à beaucoup de logiciels et autres plate-formes modernes de partage de documents, MediaSPIP a l’ambition de gérer un maximum de formats de documents différents qu’ils soient de type : images (png, gif, jpg, bmp et autres...) ; audio (MP3, Ogg, Wav et autres...) ; vidéo (Avi, MP4, Ogv, mpg, mov, wmv et autres...) ; contenu textuel, code ou autres (open office, microsoft office (tableur, présentation), web (html, css), LaTeX, Google Earth) (...)

Sur d’autres sites (11057)

  • Are there any alternatives to SharedArrayBuffer, or methods for video editing in a web browser ?

    26 juillet 2023, par Govinda Regmi

    I'm working on a web-based video editing application using ffmeg that heavily relies on SharedArrayBuffer. Unfortunately, I've encountered a roadblock with the "Cross-Origin-Embedder-Policy : require-corp | credentialless" and "Cross-Origin-Opener-Policy : same-origin" headers. While these headers allow the usage of SharedArrayBuffer, they restrict other essential features, such as rendering images from an s3 bucket and script of TinyMce text editor.

    


    I am trying to achieve
video editor like this

    


    I am using "next" : "12.1.6" and
I tried to implement ffmeg like this :

    


    import { useEffect, useState } from "react";&#xA;&#xA;import { useDebounce } from "use-debounce";&#xA;import { createFFmpeg, fetchFile } from "@ffmpeg/ffmpeg";&#xA;&#xA;import styles from "../videoEditor.module.scss";&#xA;import RangeInput from "../range-input/RangeInput";&#xA;import * as helpers from "../../../../utils/videoHelpers";&#xA;&#xA;const FF = createFFmpeg({&#xA;    log: true,&#xA;    corePath: "https://unpkg.com/@ffmpeg/core@0.10.0/dist/ffmpeg-core.js",&#xA;});&#xA;&#xA;(async function () {&#xA;    await FF.load();&#xA;})();&#xA;&#xA;export const VideoTrimmer = ({&#xA;    videoFile,&#xA;    trimmedVideoFile,&#xA;    isConfirmClicked,&#xA;    setTrimmedVideoFile,&#xA;    onConfirmClickHandler,&#xA;}) => {&#xA;    const [URL, setURL] = useState([]);&#xA;    const [thumbNails, setThumbNails] = useState([]);&#xA;    const [videoMeta, setVideoMeta] = useState(null);&#xA;    const [inputVideoFile, setInputVideoFile] = useState(null);&#xA;    const [thumbnailIsProcessing, setThumbnailIsProcessing] = useState(false);&#xA;&#xA;    const [rStart, setRstart] = useState(0);&#xA;    const [debouncedRstart] = useDebounce(rStart, 500);&#xA;&#xA;    const [rEnd, setRend] = useState(10);&#xA;    const [debouncedRend] = useDebounce(rEnd, 500);&#xA;&#xA;    const handleLoadedData = async (e) => {&#xA;        const el = e.target;&#xA;        const meta = {&#xA;            name: inputVideoFile.name,&#xA;            duration: el.duration,&#xA;            videoWidth: 50,&#xA;            videoHeight: 50,&#xA;        };&#xA;        setVideoMeta(meta);&#xA;        const thumbNails = await getThumbnails(meta);&#xA;        setThumbNails(thumbNails);&#xA;    };&#xA;&#xA;    const getThumbnails = async ({ duration }) => {&#xA;        if (!FF.isLoaded()) await FF.load();&#xA;        setThumbnailIsProcessing(true);&#xA;        let MAX_NUMBER_OF_IMAGES = 15;&#xA;        let NUMBER_OF_IMAGES = duration &lt; MAX_NUMBER_OF_IMAGES ? duration : 15;&#xA;        let offset =&#xA;            duration === MAX_NUMBER_OF_IMAGES ? 1 : duration / NUMBER_OF_IMAGES;&#xA;&#xA;        const arrayOfImageURIs = [];&#xA;        FF.FS("writeFile", inputVideoFile.name, await fetchFile(inputVideoFile));&#xA;&#xA;        for (let i = 0; i &lt; NUMBER_OF_IMAGES; i&#x2B;&#x2B;) {&#xA;            let startTimeInSecs = helpers.toTimeString(Math.round(i * offset));&#xA;&#xA;            try {&#xA;                await FF.run(&#xA;                    "-ss",&#xA;                    startTimeInSecs,&#xA;                    "-i",&#xA;                    inputVideoFile.name,&#xA;                    "-t",&#xA;                    "00:00:1.000",&#xA;                    "-vf",&#xA;                    `scale=150:-1`,&#xA;                    `img${i}.png`,&#xA;                );&#xA;                const data = FF.FS("readFile", `img${i}.png`);&#xA;&#xA;                let blob = new Blob([data.buffer], { type: "image/png" });&#xA;                let dataURI = await helpers.readFileAsBase64(blob);&#xA;                FF.FS("unlink", `img${i}.png`);&#xA;                arrayOfImageURIs.push(dataURI);&#xA;            } catch (error) {&#xA;                // console.log({ message: error });&#xA;            }&#xA;        }&#xA;        setThumbnailIsProcessing(false);&#xA;&#xA;        return arrayOfImageURIs;&#xA;    };&#xA;    const handleTrim = async () => {&#xA;        // setTrimIsProcessing(true);&#xA;        let startTime = ((rStart / 100) * videoMeta.duration).toFixed(2);&#xA;        let offset = ((rEnd / 100) * videoMeta.duration - startTime).toFixed(2);&#xA;        try {&#xA;            FF.FS("writeFile", inputVideoFile.name, await fetchFile(inputVideoFile));&#xA;            await FF.run(&#xA;                "-ss",&#xA;                helpers.toTimeString(startTime),&#xA;                "-i",&#xA;                inputVideoFile.name,&#xA;                "-t",&#xA;                helpers.toTimeString(offset),&#xA;                "-c",&#xA;                "copy",&#xA;                "ping.mp4",&#xA;            );&#xA;            const data = FF.FS("readFile", "ping.mp4");&#xA;            const dataURL = await helpers.readFileAsBase64(&#xA;                new Blob([data.buffer], { type: "video/mp4" }),&#xA;            );&#xA;&#xA;            setTrimmedVideoFile(dataURL);&#xA;        } catch (error) {&#xA;            // console.log(error);&#xA;        } finally {&#xA;            // setTrimIsProcessing(false);&#xA;        }&#xA;    };&#xA;&#xA;    const handleRangeChange = (type, event) => {&#xA;        const limit = parseInt((120 / videoMeta.duration) * 100);&#xA;        if (type === "start") {&#xA;            if (rEnd - rStart > limit) {&#xA;                setRend(parseInt(event.target.value) &#x2B; limit);&#xA;                setRstart(parseInt(event.target.value));&#xA;            } else {&#xA;                setRstart(parseInt(event.target.value));&#xA;            }&#xA;        } else if (type === "end") {&#xA;            if (rEnd - rStart > limit) {&#xA;                setRstart(parseInt(event.target.value) - limit);&#xA;                setRend(parseInt(event.target.value));&#xA;            } else {&#xA;                setRend(parseInt(event.target.value));&#xA;            }&#xA;        }&#xA;    };&#xA;&#xA;    useEffect(() => {&#xA;        if (videoMeta?.duration > 120) {&#xA;            const limit = parseInt((120 / videoMeta.duration) * 100);&#xA;            setRend(limit);&#xA;        }&#xA;    }, [videoMeta?.duration]);&#xA;&#xA;    useEffect(() => {&#xA;        const videoFormData = new FormData();&#xA;        if (videoFile) {&#xA;            videoFormData.append("file", videoFile);&#xA;            const handleChange = async () => {&#xA;                setInputVideoFile(videoFile);&#xA;                setURL(await helpers.readFileAsBase64(videoFile));&#xA;            };&#xA;            handleChange();&#xA;        }&#xA;    }, []);&#xA;&#xA;    useEffect(() => {&#xA;        if (videoMeta) {&#xA;            onConfirmClickHandler(handleTrim);&#xA;        }&#xA;    }, [isConfirmClicked]);&#xA;&#xA;    useEffect(() => {&#xA;        if (debouncedRend == rEnd &amp;&amp; debouncedRstart == rStart &amp;&amp; videoMeta) {&#xA;            handleTrim();&#xA;        }&#xA;    }, [debouncedRend, debouncedRstart, videoMeta]);&#xA;&#xA;    return (&#xA;        &lt;>&#xA;            <article classname="grid_txt_2">&#xA;                &#xA;                    {trimmedVideoFile ? (&#xA;                        &#xA;                    ) : (&#xA;                        &#xA;                    )}&#xA;                &#xA;            </article>&#xA;            &#xA;        >&#xA;    );&#xA;};&#xA;

    &#xA;

    next.config.js

    &#xA;

    const nextConfig = {&#xA;    async headers() {&#xA;        return [&#xA;            {&#xA;                source: "/(.*)",&#xA;                headers: [&#xA;                    { key: "Cross-Origin-Opener-Policy", value: "same-origin" },&#xA;                    { key: "Cross-Origin-Embedder-Policy", value: "credentialless" },&#xA;                ],&#xA;            },&#xA;        ];&#xA;    },&#xA;    &#xA;};&#xA;

    &#xA;

    This works seamlessly in Chrome and Edge, but it encounter issues (SharedArrayBuffer is not defined) in Firefox and Safari. How can we ensure it functions impeccably across all major browsers ?

    &#xA;

    When utilizing key : "Cross-Origin-Embedder-Policy", value : "require-corp" , I encounter an error while fetching images/scripts from cross-origin sources, resulting in "net::ERR_BLOCKED_BY_RESPONSE.NotSameOriginAfterDefaultedToSameOriginByCoep 200 (OK)". Cany you suggest me how can I resolve this issue ?

    &#xA;

  • Converting a voice recording into an mp3

    21 juillet 2023, par Raphael M

    For a vue.js messaging project, I'm using the wavesurfer.js library to record voice messages. However Google chrome gives me an audio/webm blob and Safari gives me an audio/mp4 blob.

    &#xA;

    I'm trying to find a solution to transcode the blob into audio/mp3. I've tried several methods, including ffmpeg. However, ffmpeg gives me an error when compiling "npm run dev" : "Can't resolve '/node_modules/@ffmpeg/core/dist/ffmpeg-core.js'".

    &#xA;

    "@ffmpeg/core": "^0.11.0",&#xA;"@ffmpeg/ffmpeg": "^0.11.6"&#xA;

    &#xA;

    I tried to downgrade ffmpeg

    &#xA;

    "@ffmpeg/core": "^0.9.0",&#xA;"@ffmpeg/ffmpeg": "^0.9.8"&#xA;

    &#xA;

    I no longer get the error message when compiling, but when I want to convert my audio stream, the console displays a problem with SharedBuffer : "Uncaught (in promise) ReferenceError : SharedArrayBuffer is not defined".

    &#xA;

    Here's my complete code below.&#xA;Is there a reliable way of transcoding the audio stream into mp3 ?

    &#xA;

    Can you give me an example ?

    &#xA;

    Thanks

    &#xA;

    <template>&#xA;  <div class="left-panel">&#xA;    <header class="radial-blue">&#xA;      <div class="container">&#xA;        <h1 class="mb-30">Posez votre premi&#xE8;re question &#xE0; nos th&#xE9;rapeutes</h1>&#xA;        <p><b>Attention</b>, vous disposez seulement de 2 messages. Veillez &#xE0; les utiliser de mani&#xE8;re judicieuse !</p>&#xA;        <div class="available-messages">&#xA;          <div class="item disabled">&#xA;            <span>Message 1</span>&#xA;          </div>&#xA;          <div class="item">&#xA;            <span>Message 2</span>&#xA;          </div>&#xA;        </div>&#xA;      </div>&#xA;    </header>&#xA;  </div>&#xA;  <div class="right-panel">&#xA;    <div class="messagerie bg-light">&#xA;      <messaging ref="messagingComponent"></messaging>&#xA;      <footer>&#xA;        <button type="button"><img src="http://stackoverflow.com/assets/backoffice/images/record-start.svg" style='max-width: 300px; max-height: 300px' /></button>&#xA;        <div class="loading-animation">&#xA;          <img src="http://stackoverflow.com/assets/backoffice/images/record-loading.svg" style='max-width: 300px; max-height: 300px' />&#xA;        </div>&#xA;        <button type="button"><img src="http://stackoverflow.com/assets/backoffice/images/record-stop.svg" style='max-width: 300px; max-height: 300px' /></button>&#xA;        <div class="textarea gradient text-dark">&#xA;          <textarea placeholder="Posez votre question"></textarea>&#xA;        </div>&#xA;        <div class="loading-text">Chargement de votre microphone en cours...</div>&#xA;        <div class="loading-text">Envoi de votre message en cours...</div>&#xA;        <div ref="visualizer"></div>&#xA;        <button type="button"><img src="http://stackoverflow.com/assets/backoffice/images/send.svg" style='max-width: 300px; max-height: 300px' /></button>&#xA;        <div>&#xA;          {{ formatTimer() }}&#xA;        </div>&#xA;      </footer>&#xA;    </div>&#xA;  </div>&#xA;</template>&#xA;&#xA;<code class="echappe-js">&lt;script&gt;&amp;#xA;import Messaging from &quot;./Messaging.vue&quot;;&amp;#xA;import { createFFmpeg, fetchFile } from &amp;#x27;@ffmpeg/ffmpeg&amp;#x27;;&amp;#xA;&amp;#xA;export default {&amp;#xA;  data() {&amp;#xA;    return {&amp;#xA;      isMicrophoneLoading: false,&amp;#xA;      isSubmitLoading: false,&amp;#xA;      isMobile: false,&amp;#xA;      isMessagerie: false,&amp;#xA;      isRecording: false,&amp;#xA;      audioUrl: &amp;#x27;&amp;#x27;,&amp;#xA;      messageText: &amp;#x27;&amp;#x27;,&amp;#xA;      message:null,&amp;#xA;      wavesurfer: null,&amp;#xA;      access:(this.isMobile?&amp;#x27;denied&amp;#x27;:&amp;#x27;granted&amp;#x27;),&amp;#xA;      maxMinutes: 5,&amp;#xA;      orangeTimer: 3,&amp;#xA;      redTimer: 4,&amp;#xA;      timer: 0,&amp;#xA;      timerInterval: null,&amp;#xA;      ffmpeg: null,&amp;#xA;    };&amp;#xA;  },&amp;#xA;  components: {&amp;#xA;    Messaging,&amp;#xA;  },&amp;#xA;  mounted() {&amp;#xA;    this.checkScreenSize();&amp;#xA;    window.addEventListener(&amp;#x27;resize&amp;#x27;, this.checkScreenSize);&amp;#xA;&amp;#xA;    if(!this.isMobile)&amp;#xA;    {&amp;#xA;      this.$moment.locale(&amp;#x27;fr&amp;#x27;);&amp;#xA;      window.addEventListener(&amp;#x27;beforeunload&amp;#x27;, (event) =&gt; {&amp;#xA;        if (this.isMessagerie) {&amp;#xA;          event.preventDefault();&amp;#xA;          event.returnValue = &amp;#x27;&amp;#x27;;&amp;#xA;        }&amp;#xA;      });&amp;#xA;&amp;#xA;      this.initializeWaveSurfer();&amp;#xA;    }&amp;#xA;  },&amp;#xA;  beforeUnmount() {&amp;#xA;    window.removeEventListener(&amp;#x27;resize&amp;#x27;, this.checkScreenSize);&amp;#xA;  },&amp;#xA;  methods: {&amp;#xA;    checkScreenSize() {&amp;#xA;      this.isMobile = window.innerWidth &lt; 1200;&amp;#xA;&amp;#xA;      const windowHeight = window.innerHeight;&amp;#xA;      const navbarHeight = this.$navbarHeight;&amp;#xA;      let padding = parseInt(navbarHeight &amp;#x2B;181);&amp;#xA;&amp;#xA;      const messageListHeight = windowHeight - padding;&amp;#xA;      this.$refs.messagingComponent.$refs.messageList.style.height = messageListHeight &amp;#x2B; &amp;#x27;px&amp;#x27;;&amp;#xA;    },&amp;#xA;    showMessagerie() {&amp;#xA;      this.isMessagerie = true;&amp;#xA;      this.$refs.messagingComponent.scrollToBottom();&amp;#xA;    },&amp;#xA;    checkMicrophoneAccess() {&amp;#xA;      if (navigator.mediaDevices &amp;amp;&amp;amp; navigator.mediaDevices.getUserMedia) {&amp;#xA;&amp;#xA;        return navigator.mediaDevices.getUserMedia({audio: true})&amp;#xA;            .then(function (stream) {&amp;#xA;              stream.getTracks().forEach(function (track) {&amp;#xA;                track.stop();&amp;#xA;              });&amp;#xA;              return true;&amp;#xA;            })&amp;#xA;            .catch(function (error) {&amp;#xA;              console.error(&amp;#x27;Erreur lors de la demande d\&amp;#x27;acc&amp;#xE8;s au microphone:&amp;#x27;, error);&amp;#xA;              return false;&amp;#xA;            });&amp;#xA;      } else {&amp;#xA;        console.error(&amp;#x27;getUserMedia n\&amp;#x27;est pas support&amp;#xE9; par votre navigateur.&amp;#x27;);&amp;#xA;        return false;&amp;#xA;      }&amp;#xA;    },&amp;#xA;    initializeWaveSurfer() {&amp;#xA;      this.wavesurfer = this.$wavesurfer.create({&amp;#xA;        container: &amp;#x27;#visualizer&amp;#x27;,&amp;#xA;        barWidth: 3,&amp;#xA;        barHeight: 1.5,&amp;#xA;        height: 46,&amp;#xA;        responsive: true,&amp;#xA;        waveColor: &amp;#x27;rgba(108,115,202,0.3)&amp;#x27;,&amp;#xA;        progressColor: &amp;#x27;rgba(108,115,202,1)&amp;#x27;,&amp;#xA;        cursorColor: &amp;#x27;transparent&amp;#x27;&amp;#xA;      });&amp;#xA;&amp;#xA;      this.record = this.wavesurfer.registerPlugin(this.$recordPlugin.create());&amp;#xA;    },&amp;#xA;    startRecording() {&amp;#xA;      const _this = this;&amp;#xA;      this.isMicrophoneLoading = true;&amp;#xA;&amp;#xA;      setTimeout(() =&gt;&amp;#xA;      {&amp;#xA;        _this.checkMicrophoneAccess().then(function (accessible)&amp;#xA;        {&amp;#xA;          if (accessible) {&amp;#xA;            _this.record.startRecording();&amp;#xA;&amp;#xA;            _this.record.once(&amp;#x27;startRecording&amp;#x27;, () =&gt; {&amp;#xA;              _this.isMicrophoneLoading = false;&amp;#xA;              _this.isRecording = true;&amp;#xA;              _this.updateChildMessage( &amp;#x27;server&amp;#x27;, &amp;#x27;Allez-y ! Vous pouvez enregistrer votre message audio maintenant. La dur&amp;#xE9;e maximale autoris&amp;#xE9;e pour votre enregistrement est de 5 minutes.&amp;#x27;, &amp;#x27;text&amp;#x27;, &amp;#x27;&amp;#x27;, &amp;#x27;Message automatique&amp;#x27;);&amp;#xA;              _this.startTimer();&amp;#xA;            });&amp;#xA;          } else {&amp;#xA;            _this.isRecording = false;&amp;#xA;            _this.isMicrophoneLoading = false;&amp;#xA;            _this.$swal.fire({&amp;#xA;              title: &amp;#x27;Microphone non d&amp;#xE9;tect&amp;#xE9;&amp;#x27;,&amp;#xA;              html: &amp;#x27;&lt;p&gt;Le microphone de votre appareil est inaccessible ou l\&amp;#x27;acc&amp;#xE8;s a &amp;#xE9;t&amp;#xE9; refus&amp;#xE9;.&lt;/p&gt;&lt;p&gt;Merci de v&amp;#xE9;rifier les param&amp;#xE8;tres de votre navigateur afin de v&amp;#xE9;rifier les autorisations de votre microphone.&lt;/p&gt;&amp;#x27;,&amp;#xA;              footer: &amp;#x27;&lt;a href='http://stackoverflow.com/contact'&gt;Vous avez besoin d\&amp;#x27;aide ?&lt;/a&gt;&amp;#x27;,&amp;#xA;            });&amp;#xA;          }&amp;#xA;        });&amp;#xA;      }, 100);&amp;#xA;    },&amp;#xA;    stopRecording() {&amp;#xA;      this.stopTimer();&amp;#xA;      this.isRecording = false;&amp;#xA;      this.isSubmitLoading = true;&amp;#xA;      this.record.stopRecording();&amp;#xA;&amp;#xA;      this.record.once(&amp;#x27;stopRecording&amp;#x27;, () =&gt; {&amp;#xA;        const blobUrl = this.record.getRecordedUrl();&amp;#xA;        fetch(blobUrl).then(response =&gt; response.blob()).then(blob =&gt; {&amp;#xA;          this.uploadAudio(blob);&amp;#xA;        });&amp;#xA;      });&amp;#xA;    },&amp;#xA;    startTimer() {&amp;#xA;      this.timerInterval = setInterval(() =&gt; {&amp;#xA;        this.timer&amp;#x2B;&amp;#x2B;;&amp;#xA;        if (this.timer === this.maxMinutes * 60) {&amp;#xA;          this.stopRecording();&amp;#xA;        }&amp;#xA;      }, 1000);&amp;#xA;    },&amp;#xA;    stopTimer() {&amp;#xA;      clearInterval(this.timerInterval);&amp;#xA;      this.timer = 0;&amp;#xA;    },&amp;#xA;    formatTimer() {&amp;#xA;      const minutes = Math.floor(this.timer / 60);&amp;#xA;      const seconds = this.timer % 60;&amp;#xA;      const formattedMinutes = minutes &lt; 10 ? `0${minutes}` : minutes;&amp;#xA;      const formattedSeconds = seconds &lt; 10 ? `0${seconds}` : seconds;&amp;#xA;      return `${formattedMinutes}:${formattedSeconds}`;&amp;#xA;    },&amp;#xA;    async uploadAudio(blob)&amp;#xA;    {&amp;#xA;      const format = blob.type === &amp;#x27;audio/webm&amp;#x27; ? &amp;#x27;webm&amp;#x27; : &amp;#x27;mp4&amp;#x27;;&amp;#xA;&amp;#xA;      // Convert the blob to MP3&amp;#xA;      const mp3Blob = await this.convertToMp3(blob, format);&amp;#xA;&amp;#xA;      const s3 = new this.$AWS.S3({&amp;#xA;        accessKeyId: &amp;#x27;xxx&amp;#x27;,&amp;#xA;        secretAccessKey: &amp;#x27;xxx&amp;#x27;,&amp;#xA;        region: &amp;#x27;eu-west-1&amp;#x27;&amp;#xA;      });&amp;#xA;&amp;#xA;      var currentDate = new Date();&amp;#xA;      var filename = currentDate.getDate().toString() &amp;#x2B; &amp;#x27;-&amp;#x27; &amp;#x2B; currentDate.getMonth().toString() &amp;#x2B; &amp;#x27;-&amp;#x27; &amp;#x2B; currentDate.getFullYear().toString() &amp;#x2B; &amp;#x27;--&amp;#x27; &amp;#x2B; currentDate.getHours().toString() &amp;#x2B; &amp;#x27;-&amp;#x27; &amp;#x2B; currentDate.getMinutes().toString() &amp;#x2B; &amp;#x27;.mp4&amp;#x27;;&amp;#xA;&amp;#xA;      const params = {&amp;#xA;        Bucket: &amp;#x27;xxx/audio&amp;#x27;,&amp;#xA;        Key: filename,&amp;#xA;        Body: mp3Blob,&amp;#xA;        ACL: &amp;#x27;public-read&amp;#x27;,&amp;#xA;        ContentType: &amp;#x27;audio/mp3&amp;#x27;&amp;#xA;      }&amp;#xA;&amp;#xA;      s3.upload(params, (err, data) =&gt; {&amp;#xA;        if (err) {&amp;#xA;          console.error(&amp;#x27;Error uploading audio:&amp;#x27;, err)&amp;#xA;        } else {&amp;#xA;          const currentDate = this.$moment();&amp;#xA;          const timestamp = currentDate.format(&amp;#x27;dddd DD MMMM YYYY HH:mm&amp;#x27;);&amp;#xA;&amp;#xA;          this.updateChildMessage( &amp;#x27;client&amp;#x27;, &amp;#x27;&amp;#x27;, &amp;#x27;audio&amp;#x27;, mp3Blob, timestamp);&amp;#xA;          this.isSubmitLoading = false;&amp;#xA;        }&amp;#xA;      });&amp;#xA;    },&amp;#xA;    async convertToMp3(blob, format) {&amp;#xA;      const ffmpeg = createFFmpeg({ log: true });&amp;#xA;      await ffmpeg.load();&amp;#xA;&amp;#xA;      const inputPath = &amp;#x27;input.&amp;#x27; &amp;#x2B; format;&amp;#xA;      const outputPath = &amp;#x27;output.mp3&amp;#x27;;&amp;#xA;&amp;#xA;      ffmpeg.FS(&amp;#x27;writeFile&amp;#x27;, inputPath, await fetchFile(blob));&amp;#xA;&amp;#xA;      await ffmpeg.run(&amp;#x27;-i&amp;#x27;, inputPath, &amp;#x27;-acodec&amp;#x27;, &amp;#x27;libmp3lame&amp;#x27;, outputPath);&amp;#xA;&amp;#xA;      const mp3Data = ffmpeg.FS(&amp;#x27;readFile&amp;#x27;, outputPath);&amp;#xA;      const mp3Blob = new Blob([mp3Data.buffer], { type: &amp;#x27;audio/mp3&amp;#x27; });&amp;#xA;&amp;#xA;      ffmpeg.FS(&amp;#x27;unlink&amp;#x27;, inputPath);&amp;#xA;      ffmpeg.FS(&amp;#x27;unlink&amp;#x27;, outputPath);&amp;#xA;&amp;#xA;      return mp3Blob;&amp;#xA;    },&amp;#xA;    sendMessage() {&amp;#xA;      this.isSubmitLoading = true;&amp;#xA;      if (this.messageText.trim() !== &amp;#x27;&amp;#x27;) {&amp;#xA;        const emmet = &amp;#x27;client&amp;#x27;;&amp;#xA;        const text = this.escapeHTML(this.messageText)&amp;#xA;            .replace(/\n/g, &amp;#x27;&lt;br&gt;&amp;#x27;);&amp;#xA;&amp;#xA;        const currentDate = this.$moment();&amp;#xA;        const timestamp = currentDate.format(&amp;#x27;dddd DD MMMM YYYY HH:mm&amp;#x27;);&amp;#xA;&amp;#xA;        this.$nextTick(() =&gt; {&amp;#xA;          this.messageText = &amp;#x27;&amp;#x27;;&amp;#xA;&amp;#xA;          const textarea = document.getElementById(&amp;#x27;messageTextarea&amp;#x27;);&amp;#xA;          if (textarea) {&amp;#xA;            textarea.scrollTop = 0;&amp;#xA;            textarea.scrollLeft = 0;&amp;#xA;          }&amp;#xA;        });&amp;#xA;&amp;#xA;        this.updateChildMessage(emmet, text, &amp;#x27;text&amp;#x27;, &amp;#x27;&amp;#x27;, timestamp);&amp;#xA;        this.isSubmitLoading = false;&amp;#xA;      }&amp;#xA;    },&amp;#xA;    escapeHTML(text) {&amp;#xA;      const map = {&amp;#xA;        &amp;#x27;&amp;amp;&amp;#x27;: &amp;#x27;&amp;amp;amp;&amp;#x27;,&amp;#xA;        &amp;#x27;&lt;&amp;#x27;: &amp;#x27;&amp;amp;lt;&amp;#x27;,&amp;#xA;        &amp;#x27;&gt;&amp;#x27;: &amp;#x27;&amp;amp;gt;&amp;#x27;,&amp;#xA;        &amp;#x27;&quot;&amp;#x27;: &amp;#x27;&amp;amp;quot;&amp;#x27;,&amp;#xA;        &quot;&amp;#x27;&quot;: &amp;#x27;&amp;amp;#039;&amp;#x27;,&amp;#xA;        &quot;`&quot;: &amp;#x27;&amp;amp;#x60;&amp;#x27;,&amp;#xA;        &quot;/&quot;: &amp;#x27;&amp;amp;#x2F;&amp;#x27;&amp;#xA;      };&amp;#xA;      return text.replace(/[&amp;amp;&lt;&gt;&quot;&amp;#x27;`/]/g, (match) =&gt; map[match]);&amp;#xA;    },&amp;#xA;    updateChildMessage(emmet, text, type, blob, timestamp) {&amp;#xA;      const newMessage = {&amp;#xA;        id: this.$refs.messagingComponent.lastMessageId &amp;#x2B; 1,&amp;#xA;        emmet: emmet,&amp;#xA;        text: text,&amp;#xA;        type: type,&amp;#xA;        blob: blob,&amp;#xA;        timestamp: timestamp&amp;#xA;      };&amp;#xA;&amp;#xA;      this.$refs.messagingComponent.updateMessages(newMessage);&amp;#xA;    }&amp;#xA;  },&amp;#xA;};&amp;#xA;&lt;/script&gt;&#xA;

    &#xA;

  • Is it feasible to create FFmpegFrameGrabber one by one for single FFmpegFrameRecorder and maintain video stream keep alive ?

    12 juillet 2023, par zhoutian

    The reason why I ask these question is I got byte [] of container data(name is dhav) one by one and I need to push that data continuously to RTMP to play。

    &#xA;

    What's the current progress I made ?

    &#xA;

    For now ,I can push data to RTMP and play RTMP by VLC just for few seconds,then the RTMP stream is end .

    &#xA;

    because the grabber created by inputstream only contain a few of the data come from ByteBuffer ,when that inputstream is end, the RTMP is closed.

    &#xA;

    synchronized (buffer) {&#xA;                                buffer.flip();&#xA;                                byte[] bytes = new byte[buffer.remaining()];&#xA;                                buffer.get(bytes);&#xA;                                buffer.clear();&#xA;                                isByteBufferFull[0] = false;&#xA;                                try {&#xA;                                    grabAndPush(bytes, SRS_PUSH_ADDRESS);&#xA;                                } catch (Exception e) {&#xA;                                    //throw new RuntimeException(e);&#xA;                                }&#xA;&#xA;                            }&#xA;

    &#xA;

    private static synchronized void grabAndPush(byte[] bytes, String pushAddress) throws Exception {&#xA;        avutil.av_log_set_level(avutil.AV_LOG_INFO);&#xA;        FFmpegLogCallback.set();&#xA;&#xA;        FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(new ByteArrayInputStream(bytes));&#xA;...&#xA;}&#xA;

    &#xA;

    So can anyone tell me how to keep the RTMP aways alive by FFmpegFrameGrabber and FFmpegFrameRecorder when the source data come from one by one.&#xA;very appreciate 😃

    &#xA;

    this is my code :

    &#xA;

    import lombok.extern.slf4j.Slf4j;&#xA;import org.bytedeco.ffmpeg.avcodec.AVCodecParameters;&#xA;import org.bytedeco.ffmpeg.avformat.AVFormatContext;&#xA;import org.bytedeco.ffmpeg.avformat.AVStream;&#xA;import org.bytedeco.ffmpeg.global.avcodec;&#xA;import org.bytedeco.ffmpeg.global.avutil;&#xA;import org.bytedeco.javacv.FFmpegFrameGrabber;&#xA;import org.bytedeco.javacv.FFmpegFrameRecorder;&#xA;import org.bytedeco.javacv.FFmpegLogCallback;&#xA;import org.bytedeco.javacv.Frame;&#xA;import org.jfjy.ch2ji.ecctv.dh.api.ApiService;&#xA;import org.jfjy.ch2ji.ecctv.dh.callback.RealPlayCallback;&#xA;&#xA;import java.io.ByteArrayInputStream;&#xA;import java.nio.ByteBuffer;&#xA;import java.util.concurrent.ExecutorService;&#xA;import java.util.concurrent.Executors;&#xA;&#xA;@Slf4j&#xA;public class GetBytes2PushRTMPNew2 {&#xA;&#xA;    private static final String SRS_PUSH_ADDRESS = "rtmp://127.0.0.1:1935/live/livestream";&#xA;&#xA;    static int BUFFER_CAPACITY = 1 * 1024 * 1024;&#xA;&#xA;    public static void main(String[] args) throws Exception {&#xA;        FFmpegLogCallback.set();&#xA;        ApiService apiService = new ApiService();&#xA;        Long login = apiService.login("10.3.0.54", 8801, "admin", "xxxx");&#xA;        ByteBuffer buffer = ByteBuffer.allocate(BUFFER_CAPACITY);&#xA;        final boolean[] isByteBufferFull = {false};&#xA;        apiService.startRealPlay(new RealPlayCallback() {&#xA;            @Override&#xA;            public void apply(Long aLong, Integer integer, byte[] bytes) {&#xA;                try {&#xA;                    //push data to bytebuffer&#xA;                    synchronized (buffer) {&#xA;                        if (buffer.remaining() > bytes.length) {&#xA;                            buffer.put(bytes);&#xA;                        } else {&#xA;                            isByteBufferFull[0] = true;&#xA;                        }&#xA;                    }&#xA;                } catch (Exception e) {&#xA;                    throw new RuntimeException(e);&#xA;                }&#xA;            }&#xA;        }, 0, 0);&#xA;&#xA;        ExecutorService executorService = Executors.newFixedThreadPool(1);&#xA;        executorService.execute(new Runnable() {&#xA;            @Override&#xA;            public void run() {&#xA;                while (true) {&#xA;                    //get data from bytebuffer when buffer is full&#xA;                    synchronized (isByteBufferFull) {&#xA;                        if (isByteBufferFull[0]) {&#xA;                            synchronized (buffer) {&#xA;                                buffer.flip();&#xA;                                byte[] bytes = new byte[buffer.remaining()];&#xA;                                buffer.get(bytes);&#xA;                                buffer.clear();&#xA;                                isByteBufferFull[0] = false;&#xA;                                try {&#xA;                                    //using grabber and recorder to push RTMP&#xA;                                    grabAndPush(bytes, SRS_PUSH_ADDRESS);&#xA;                                } catch (Exception e) {&#xA;                                    //throw new RuntimeException(e);&#xA;                                }&#xA;&#xA;                            }&#xA;                        }&#xA;                    }&#xA;                    try {&#xA;                        Thread.sleep(500);&#xA;                    } catch (InterruptedException e) {&#xA;                        throw new RuntimeException(e);&#xA;                    }&#xA;                }&#xA;&#xA;            }&#xA;        });&#xA;        while (true) {&#xA;&#xA;        }&#xA;    }&#xA;&#xA;    private static synchronized void grabAndPush(byte[] bytes, String pushAddress) throws Exception {&#xA;        avutil.av_log_set_level(avutil.AV_LOG_INFO);&#xA;        FFmpegLogCallback.set();&#xA;&#xA;        FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(new ByteArrayInputStream(bytes));&#xA;&#xA;&#xA;        grabber.setFormat("dhav");&#xA;        grabber.start();&#xA;&#xA;        AVFormatContext avFormatContext = grabber.getFormatContext();&#xA;&#xA;        int streamNum = avFormatContext.nb_streams();&#xA;&#xA;        if (streamNum &lt; 1) {&#xA;            log.error("no media!");&#xA;            return;&#xA;        }&#xA;&#xA;        int frameRate = (int) grabber.getVideoFrameRate();&#xA;        if (0 == frameRate) {&#xA;            frameRate = 15;&#xA;        }&#xA;        log.info("frameRate[{}],duration[{}]秒,nb_streams[{}]",&#xA;                frameRate,&#xA;                avFormatContext.duration() / 1000000,&#xA;                avFormatContext.nb_streams());&#xA;&#xA;        for (int i = 0; i &lt; streamNum; i&#x2B;&#x2B;) {&#xA;            AVStream avStream = avFormatContext.streams(i);&#xA;            AVCodecParameters avCodecParameters = avStream.codecpar();&#xA;            log.info("stream index[{}],codec type[{}],codec ID[{}]", i, avCodecParameters.codec_type(), avCodecParameters.codec_id());&#xA;        }&#xA;&#xA;        int frameWidth = grabber.getImageWidth();&#xA;        int frameHeight = grabber.getImageHeight();&#xA;        int audioChannels = grabber.getAudioChannels();&#xA;&#xA;        log.info("frameWidth[{}],frameHeight[{}],audioChannels[{}]",&#xA;                frameWidth,&#xA;                frameHeight,&#xA;                audioChannels);&#xA;&#xA;        FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(pushAddress,&#xA;                frameWidth,&#xA;                frameHeight,&#xA;                audioChannels);&#xA;&#xA;        recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);&#xA;        recorder.setInterleaved(true);&#xA;&#xA;        recorder.setFormat("flv");&#xA;&#xA;        recorder.setFrameRate(frameRate);&#xA;&#xA;        recorder.setGopSize(frameRate);&#xA;&#xA;        recorder.setAudioChannels(grabber.getAudioChannels());&#xA;&#xA;&#xA;        recorder.start();&#xA;&#xA;&#xA;        Frame frame;&#xA;&#xA;&#xA;        log.info("start push");&#xA;&#xA;        int videoFrameNum = 0;&#xA;        int audioFrameNum = 0;&#xA;        int dataFrameNum = 0;&#xA;&#xA;        int interVal = 1000 / frameRate;&#xA;        interVal /= 8;&#xA;&#xA;        while (null != (frame = grabber.grab())) {&#xA;&#xA;            if (null != frame.image) {&#xA;                videoFrameNum&#x2B;&#x2B;;&#xA;            }&#xA;&#xA;            if (null != frame.samples) {&#xA;                audioFrameNum&#x2B;&#x2B;;&#xA;            }&#xA;&#xA;            if (null != frame.data) {&#xA;                dataFrameNum&#x2B;&#x2B;;&#xA;            }&#xA;&#xA;            recorder.record(frame);&#xA;&#xA;            Thread.sleep(interVal);&#xA;        }&#xA;&#xA;        log.info("push complete,videoFrameNum[{}],audioFrameNum[{}],dataFrameNum[{}]",&#xA;                videoFrameNum,&#xA;                audioFrameNum,&#xA;                dataFrameNum);&#xA;&#xA;        recorder.close();&#xA;        grabber.close();&#xA;    }&#xA;&#xA;&#xA;}&#xA;

    &#xA;