
Recherche avancée
Médias (1)
-
GetID3 - Bloc informations de fichiers
9 avril 2013, par
Mis à jour : Mai 2013
Langue : français
Type : Image
Autres articles (25)
-
Publier sur MédiaSpip
13 juin 2013Puis-je poster des contenus à partir d’une tablette Ipad ?
Oui, si votre Médiaspip installé est à la version 0.2 ou supérieure. Contacter au besoin l’administrateur de votre MédiaSpip pour le savoir -
Les formats acceptés
28 janvier 2010, parLes commandes suivantes permettent d’avoir des informations sur les formats et codecs gérés par l’installation local de ffmpeg :
ffmpeg -codecs ffmpeg -formats
Les format videos acceptés en entrée
Cette liste est non exhaustive, elle met en exergue les principaux formats utilisés : h264 : H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 m4v : raw MPEG-4 video format flv : Flash Video (FLV) / Sorenson Spark / Sorenson H.263 Theora wmv :
Les formats vidéos de sortie possibles
Dans un premier temps on (...) -
Ajouter notes et légendes aux images
7 février 2011, parPour pouvoir ajouter notes et légendes aux images, la première étape est d’installer le plugin "Légendes".
Une fois le plugin activé, vous pouvez le configurer dans l’espace de configuration afin de modifier les droits de création / modification et de suppression des notes. Par défaut seuls les administrateurs du site peuvent ajouter des notes aux images.
Modification lors de l’ajout d’un média
Lors de l’ajout d’un média de type "image" un nouveau bouton apparait au dessus de la prévisualisation (...)
Sur d’autres sites (6158)
-
Converting a voice recording into an mp3
21 juillet 2023, par Raphael MFor 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.


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'".


"@ffmpeg/core": "^0.11.0",
"@ffmpeg/ffmpeg": "^0.11.6"



I tried to downgrade ffmpeg


"@ffmpeg/core": "^0.9.0",
"@ffmpeg/ffmpeg": "^0.9.8"



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".


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


Can you give me an example ?


Thanks


<template>
 <div class="left-panel">
 <header class="radial-blue">
 <div class="container">
 <h1 class="mb-30">Posez votre première question à nos thérapeutes</h1>
 <p><b>Attention</b>, vous disposez seulement de 2 messages. Veillez à les utiliser de manière judicieuse !</p>
 <div class="available-messages">
 <div class="item disabled">
 <span>Message 1</span>
 </div>
 <div class="item">
 <span>Message 2</span>
 </div>
 </div>
 </div>
 </header>
 </div>
 <div class="right-panel">
 <div class="messagerie bg-light">
 <messaging ref="messagingComponent"></messaging>
 <footer>
 <button type="button"><img src="http://stackoverflow.com/assets/backoffice/images/record-start.svg" style='max-width: 300px; max-height: 300px' /></button>
 <div class="loading-animation">
 <img src="http://stackoverflow.com/assets/backoffice/images/record-loading.svg" style='max-width: 300px; max-height: 300px' />
 </div>
 <button type="button"><img src="http://stackoverflow.com/assets/backoffice/images/record-stop.svg" style='max-width: 300px; max-height: 300px' /></button>
 <div class="textarea gradient text-dark">
 <textarea placeholder="Posez votre question"></textarea>
 </div>
 <div class="loading-text">Chargement de votre microphone en cours...</div>
 <div class="loading-text">Envoi de votre message en cours...</div>
 <div ref="visualizer"></div>
 <button type="button"><img src="http://stackoverflow.com/assets/backoffice/images/send.svg" style='max-width: 300px; max-height: 300px' /></button>
 <div>
 {{ formatTimer() }}
 </div>
 </footer>
 </div>
 </div>
</template>

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



-
Recording audio with MediaRecorder on iPhone with Safari and Chrome only 1 second long ? Mimetype and FFMPEG problem ?
9 mai 2023, par AvatarI am using MediaRecorder to record the Microphone audio on a website.


Javascript :


var blob;
var blob_url;
var stream;
var recorder;
var chunks;

var media = {
 tag: 'audio',
 type: 'audio/ogg',
 ext: '.ogg',
 gUM: {audio: true}
};

navigator.mediaDevices.getUserMedia(media.gUM).then(_stream => 
{
 stream = _stream;

 recorder = new MediaRecorder(stream);

 recorder.ondataavailable = e => 
 {
 // push data to chunks
 chunks.push(e.data);

 // recording has been stopped
 if(recorder.state == 'inactive') 
 {
 // audio data available
 blob = new Blob(chunks, {type: media.type });
 blob_url = URL.createObjectURL(blob);
 
 // send data to server
 uploadfile_audio();
 }
 };

 if(typeof(recorder)=='undefined')
 {
 alert('No microphone access');
 return;
 }

 chunks = [];
 recorder.start();
}


// when stop button is clicked
recorder.stop();
stream.getTracks().forEach( track => track.stop() );



The audio stream (ogg format) is sent to the server.


Since iPad/iPhone do not play ogg files, the recording file is converted to "mp3" using FFMPEG.


This file is stored on the server.



This works on Windows and MAC (Chrome and Safari), also on iPad (Safari) but not properly on iPhone (Chrome/Safari). Version : iPhone iOS 15.1.


On iPhone the recording file is only
0:01 min
in length. Size is always17277
Bytes.

What could be the issue ? (I cannot debug because I don't have a Mac.)


Does the stream get interrupted ? Is the recording stopped after 1 second ?


Update 1 :


I have checked the incoming filesize of the browser-recorded file serverside. It seems to be coming in properly, because there are different sizes such as 184 kB.


My guess is now that FFMPEG cannot handle the incoming file correctly. Which might have the wrong mimetype set in Javascript with
type: 'audio/ogg',
. Is another format needed ?

The conversion code serverside :


PHP


$mp3file = shell_exec("ffmpeg -i ".$file_locationtmp." -vn -ar 44100 -ac 2 -b:a 128k ".$file_locationtmp.".mp3");



I would need to find out the audio recording format used by iPhone but I couldn't.


I tried to find the supporting mimetypes using https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/isTypeSupported - however, it shows that NO mimetypes are supported on iPhone (neither in Chrome nor Safari).


Update 2 :


I used
ffprobe
to get the recording file information. It saysStream #0:0(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, mono, fltp, 2234 kb/s (default)


Update 3 :


It seems to be a problem with FFMPEG. See my new question How to convert AAC/MP4A to MP3 using FFMPEG in full length ? Audio file gets cut off after 1 second


-
How to save audio chunks from client to ffmpeg readable file ?
22 septembre 2023, par LuckOverflowI am live recording audio data from a TS React front-end and need to send it to the server, where it can be saved to a file so that ffmpeg can mix it. The front-end saves the mic data to a blob with type "mimeType : "audio/webm ; codecs=opus" when printed in the browser terminal. I send the exact object that I printed to the server, where logging it indicates it is a, or was passed as a, "Buffer" object.


I have tried saving that Buffer as a webm file, but when I pass that file as an input to ffmpeg ffprobe, I get the error "Format matroska,webm detected only with a low score of 1..." and "EBML header parsing failed.." "Invalid data found when processing input." I have tried several other formats to no success.


I need a way to transform this Buffer object to an audio file that can be mixed by ffmpeg. When I am finished, I also need to be able to do the reverse operation to send it in the same format to another client for playback, which is currently working.


Code that records and sends the audio (TS React) :




const startRecording = async function () {
 inputStream = await navigator.mediaDevices.getUserMedia({ audio: true });
 
 mediaRecorder.current = new MediaRecorder(inputStream, { mimeType: "audio/webm; codecs=opus" });

 mediaRecorder.current.ondataavailable = e => {
 console.log(e.data)
 if (e.data.size > 0) {
 socket.emit("recording", e.data);
 console.log("Audio data recorded. Transmitting to server via socketio...");
 }
 };

 mediaRecorder.current.start(1000);
 };




Code that receives and tries to save the Buffer to a file (JS Node.js) :




socket.on("recording", (chunk) => {
 console_log("Audio chunk recieved. Transmitting to frontend...");
 socket.broadcast.emit('listening', chunk);

 fs.writeFileSync('out.webm', chunk.toString());
 if (counter > 3) {
 console.log("Trying ffmpeg...");

 ffmpegInstance
 .input('out.webm')
 .complexFilter([
 {
 filter: 'amix'
 }])
 .save('./Music/FFMPEGSTREAM.mp3');
 }

 counter++;
 });



fluent-ffmpeg interface package is includued in the server code, but I have been using ffmpeg in the terminal (Pop OS) to debug. The goal is to save the file to a ram disk and use fluent ffmpeg to mix before sending to a different client for playback. Currently I am just trying to save it to disk and get ffmpeg command line to work on it.


Update :
Problem was that the chunk I was analyzing didn't have the header info. MediaRecorder encodes, then slices it up, not slices it up into your specified time slot and encodes. I have not found a good solution to this. Saving the file, without toString I believe, results in a playable webm when the header is properly included.