
Recherche avancée
Médias (1)
-
Bug de détection d’ogg
22 mars 2013, par
Mis à jour : Avril 2013
Langue : français
Type : Video
Autres articles (47)
-
La file d’attente de SPIPmotion
28 novembre 2010, parUne file d’attente stockée dans la base de donnée
Lors de son installation, SPIPmotion crée une nouvelle table dans la base de donnée intitulée spip_spipmotion_attentes.
Cette nouvelle table est constituée des champs suivants : id_spipmotion_attente, l’identifiant numérique unique de la tâche à traiter ; id_document, l’identifiant numérique du document original à encoder ; id_objet l’identifiant unique de l’objet auquel le document encodé devra être attaché automatiquement ; objet, le type d’objet auquel (...) -
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 -
Contribute to documentation
13 avril 2011Documentation is vital to the development of improved technical capabilities.
MediaSPIP welcomes documentation by users as well as developers - including : critique of existing features and functions articles contributed by developers, administrators, content producers and editors screenshots to illustrate the above translations of existing documentation into other languages
To contribute, register to the project users’ mailing (...)
Sur d’autres sites (7906)
-
AVPlayer currentTime not match with the displayed frame after many seeking. (HLS Stream)
26 août 2022, par D.BenceI have a m3u8 file which i want to use with HLS.
link to m3u8 file


My problem is that when i seek many times, the currentTime not match with the displayed frame. I experience that, the more seeking there is more difference. (it's like a constant +offset)


Our use case is very timing sensitive, we need to stop the videos at specific positions. For this we use the following code :


[_player addBoundaryTimeObserverForTimes:times
 queue:dispatch_get_main_queue()
 usingBlock:^{
 weakSelf.player.rate = 0.0;
 // The time is correct here, but the displayed frame is not sync with this time
 CMTimeShow(weakSelf.player.currentTime);
 }
] 



I read that AVURLAssetPreferPreciseDurationAndTimingKey can help, but the currentTime still wrong.


NSDictionary<nsstring> *options = @{AVURLAssetPreferPreciseDurationAndTimingKey:@YES};
AVURLAsset *urlAsset = [AVURLAsset URLAssetWithURL:url options:options];
AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:urlAsset];
</nsstring>


I also read that there is a problem with the m3u8 itself. (for example wrong GOP size.)


What is the reason for the incorrect currentTime, and how can i fix this ?


ffprobe m3u8 :


ffprobe https://mrkresz-content-bucket.s3.eu-central-1.amazonaws.com/applicationData/extended-path-videos/4b0046fd-0be7-4a07-bc7c-c1a0069116b1/released/50816bc9-6118-4dd1-a90e-fb72652bb06f-final-from-anonymized.m3u8
ffprobe version 5.1 Copyright (c) 2007-2022 the FFmpeg developers
 built with Apple clang version 13.1.6 (clang-1316.0.21.2.5)
 configuration: --prefix=/opt/homebrew/Cellar/ffmpeg/5.1 --enable-shared --enable-pthreads --enable-version3 --cc=clang --host-cflags= --host-ldflags= --enable-ffplay --enable-gnutls --enable-gpl --enable-libaom --enable-libbluray --enable-libdav1d --enable-libmp3lame --enable-libopus --enable-librav1e --enable-librist --enable-librubberband --enable-libsnappy --enable-libsrt --enable-libtesseract --enable-libtheora --enable-libvidstab --enable-libvmaf --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-lzma --enable-libfontconfig --enable-libfreetype --enable-frei0r --enable-libass --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libspeex --enable-libsoxr --enable-libzmq --enable-libzimg --disable-libjack --disable-indev=jack --enable-videotoolbox --enable-neon
 libavutil 57. 28.100 / 57. 28.100
 libavcodec 59. 37.100 / 59. 37.100
 libavformat 59. 27.100 / 59. 27.100
 libavdevice 59. 7.100 / 59. 7.100
 libavfilter 8. 44.100 / 8. 44.100
 libswscale 6. 7.100 / 6. 7.100
 libswresample 4. 7.100 / 4. 7.100
 libpostproc 56. 6.100 / 56. 6.100
[hls @ 0x152f04280] Skip ('#EXT-X-VERSION:3')
[hls @ 0x152f04280] Skip ('#EXT-X-INDEPENDENT-SEGMENTS')
[hls @ 0x152f04280] Opening 'https://mrkresz-content-bucket.s3.eu-central-1.amazonaws.com/applicationData/extended-path-videos/4b0046fd-0be7-4a07-bc7c-c1a0069116b1/released/50816bc9-6118-4dd1-a90e-fb72652bb06f-final-from-anonymized1080p_6000000bitrate.m3u8' for reading
[hls @ 0x152f04280] Skip ('#EXT-X-VERSION:3')
[https @ 0x154045000] Opening 'https://mrkresz-content-bucket.s3.eu-central-1.amazonaws.com/applicationData/extended-path-videos/4b0046fd-0be7-4a07-bc7c-c1a0069116b1/released/50816bc9-6118-4dd1-a90e-fb72652bb06f-final-from-anonymized1080p_15000000bitrate.m3u8' for reading
[hls @ 0x152f04280] Skip ('#EXT-X-VERSION:3')
[https @ 0x154045000] Opening 'https://mrkresz-content-bucket.s3.eu-central-1.amazonaws.com/applicationData/extended-path-videos/4b0046fd-0be7-4a07-bc7c-c1a0069116b1/released/50816bc9-6118-4dd1-a90e-fb72652bb06f-final-from-anonymized1520p_33000000bitrate.m3u8' for reading
[hls @ 0x152f04280] Skip ('#EXT-X-VERSION:3')
[hls @ 0x152f04280] Opening 'https://mrkresz-content-bucket.s3.eu-central-1.amazonaws.com/applicationData/extended-path-videos/4b0046fd-0be7-4a07-bc7c-c1a0069116b1/released/50816bc9-6118-4dd1-a90e-fb72652bb06f-final-from-anonymized1080p_6000000bitrate_00001.ts' for reading
[hls @ 0x152f04280] Opening 'https://mrkresz-content-bucket.s3.eu-central-1.amazonaws.com/applicationData/extended-path-videos/4b0046fd-0be7-4a07-bc7c-c1a0069116b1/released/50816bc9-6118-4dd1-a90e-fb72652bb06f-final-from-anonymized1080p_6000000bitrate_00002.ts' for reading
[hls @ 0x152f04280] Opening 'https://mrkresz-content-bucket.s3.eu-central-1.amazonaws.com/applicationData/extended-path-videos/4b0046fd-0be7-4a07-bc7c-c1a0069116b1/released/50816bc9-6118-4dd1-a90e-fb72652bb06f-final-from-anonymized1080p_15000000bitrate_00001.ts' for reading
[hls @ 0x152f04280] Opening 'https://mrkresz-content-bucket.s3.eu-central-1.amazonaws.com/applicationData/extended-path-videos/4b0046fd-0be7-4a07-bc7c-c1a0069116b1/released/50816bc9-6118-4dd1-a90e-fb72652bb06f-final-from-anonymized1080p_15000000bitrate_00002.ts' for reading
[hls @ 0x152f04280] Opening 'https://mrkresz-content-bucket.s3.eu-central-1.amazonaws.com/applicationData/extended-path-videos/4b0046fd-0be7-4a07-bc7c-c1a0069116b1/released/50816bc9-6118-4dd1-a90e-fb72652bb06f-final-from-anonymized1520p_33000000bitrate_00001.ts' for reading
[hls @ 0x152f04280] Opening 'https://mrkresz-content-bucket.s3.eu-central-1.amazonaws.com/applicationData/extended-path-videos/4b0046fd-0be7-4a07-bc7c-c1a0069116b1/released/50816bc9-6118-4dd1-a90e-fb72652bb06f-final-from-anonymized1520p_33000000bitrate_00002.ts' for reading
Input #0, hls, from 'https://mrkresz-content-bucket.s3.eu-central-1.amazonaws.com/applicationData/extended-path-videos/4b0046fd-0be7-4a07-bc7c-c1a0069116b1/released/50816bc9-6118-4dd1-a90e-fb72652bb06f-final-from-anonymized.m3u8':
 Duration: 00:49:43.00, start: 2.033367, bitrate: 0 kb/s
 Program 0
 Metadata:
 variant_bitrate : 8133826
 Stream #0:0: Video: h264 (Main) ([27][0][0][0] / 0x001B), yuv420p, 1920x1080 [SAR 1:1 DAR 16:9], 29.97 fps, 29.97 tbr, 90k tbn
 Metadata:
 variant_bitrate : 8133826
 Program 1
 Metadata:
 variant_bitrate : 18870392
 Stream #0:1: Video: h264 (Main) ([27][0][0][0] / 0x001B), yuv420p, 1920x1080 [SAR 1:1 DAR 16:9], 29.97 fps, 29.97 tbr, 90k tbn
 Metadata:
 variant_bitrate : 18870392
 Program 2
 Metadata:
 variant_bitrate : 40776144
 Stream #0:2: Video: h264 (Main) ([27][0][0][0] / 0x001B), yuv420p, 2704x1520 [SAR 1:1 DAR 169:95], 29.97 fps, 29.97 tbr, 90k tbn
 Metadata:
 variant_bitrate : 40776144



ffprobe original mp4 :


ffprobe https://mrkresz-content-bucket.s3.eu-central-1.amazonaws.com/applicationData/extended-path-videos/4b0046fd-0be7-4a07-bc7c-c1a0069116b1/finalized/50816bc9-6118-4dd1-a90e-fb72652bb06f-final-from-anonymized.mp4
ffprobe version 5.1 Copyright (c) 2007-2022 the FFmpeg developers
 built with Apple clang version 13.1.6 (clang-1316.0.21.2.5)
 configuration: --prefix=/opt/homebrew/Cellar/ffmpeg/5.1 --enable-shared --enable-pthreads --enable-version3 --cc=clang --host-cflags= --host-ldflags= --enable-ffplay --enable-gnutls --enable-gpl --enable-libaom --enable-libbluray --enable-libdav1d --enable-libmp3lame --enable-libopus --enable-librav1e --enable-librist --enable-librubberband --enable-libsnappy --enable-libsrt --enable-libtesseract --enable-libtheora --enable-libvidstab --enable-libvmaf --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-lzma --enable-libfontconfig --enable-libfreetype --enable-frei0r --enable-libass --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libspeex --enable-libsoxr --enable-libzmq --enable-libzimg --disable-libjack --disable-indev=jack --enable-videotoolbox --enable-neon
 libavutil 57. 28.100 / 57. 28.100
 libavcodec 59. 37.100 / 59. 37.100
 libavformat 59. 27.100 / 59. 27.100
 libavdevice 59. 7.100 / 59. 7.100
 libavfilter 8. 44.100 / 8. 44.100
 libswscale 6. 7.100 / 6. 7.100
 libswresample 4. 7.100 / 4. 7.100
 libpostproc 56. 6.100 / 56. 6.100
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'https://mrkresz-content-bucket.s3.eu-central-1.amazonaws.com/applicationData/extended-path-videos/4b0046fd-0be7-4a07-bc7c-c1a0069116b1/finalized/50816bc9-6118-4dd1-a90e-fb72652bb06f-final-from-anonymized.mp4':
 Metadata:
 major_brand : isom
 minor_version : 512
 compatible_brands: isomiso2avc1mp41
 encoder : Lavf58.76.100
 Duration: 00:49:42.81, start: 0.000000, bitrate: 36142 kb/s
 Stream #0:0[0x1](und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(progressive), 2486x1398, 36138 kb/s, 29.97 fps, 29.97 tbr, 30k tbn (default)
 Metadata:
 handler_name : VideoHandler
 vendor_id : [0][0][0][0]



-
Discord.py repository example : bot can join voice channel, shows voice activity (green ring), but no sound is produced ?
19 juin 2023, par Jared RobertsonI copied an example from the discord.py repository to test out a bot with music capabilities. Though the code enables YouTube playback, I am only concerned with local audio playback. See code here : https://github.com/Rapptz/discord.py/blob/v2.3.0/examples/basic_voice.py. I tested this code as is with only the Discord token portion updated (from constants.py). See code below :


# This example requires the 'message_content' privileged intent to function.

#my constants.py with API keys, tokens, etc. stored
import constants

import asyncio

import discord
import youtube_dl

from discord.ext import commands

# Suppress noise about console usage from errors
youtube_dl.utils.bug_reports_message = lambda: ''


ytdl_format_options = {
 'format': 'bestaudio/best',
 'outtmpl': '%(extractor)s-%(id)s-%(title)s.%(ext)s',
 'restrictfilenames': True,
 'noplaylist': True,
 'nocheckcertificate': True,
 'ignoreerrors': False,
 'logtostderr': False,
 'quiet': True,
 'no_warnings': True,
 'default_search': 'auto',
 'source_address': '0.0.0.0', # bind to ipv4 since ipv6 addresses cause issues sometimes
}

ffmpeg_options = {
 'options': '-vn',
}

ytdl = youtube_dl.YoutubeDL(ytdl_format_options)


class YTDLSource(discord.PCMVolumeTransformer):
 def __init__(self, source, *, data, volume=0.5):
 super().__init__(source, volume)

 self.data = data

 self.title = data.get('title')
 self.url = data.get('url')

 @classmethod
 async def from_url(cls, url, *, loop=None, stream=False):
 loop = loop or asyncio.get_event_loop()
 data = await loop.run_in_executor(None, lambda: ytdl.extract_info(url, download=not stream))

 if 'entries' in data:
 # take first item from a playlist
 data = data['entries'][0]

 filename = data['url'] if stream else ytdl.prepare_filename(data)
 return cls(discord.FFmpegPCMAudio(filename, **ffmpeg_options), data=data)


class Music(commands.Cog):
 def __init__(self, bot):
 self.bot = bot

 @commands.command()
 async def join(self, ctx, *, channel: discord.VoiceChannel):
 """Joins a voice channel"""

 if ctx.voice_client is not None:
 return await ctx.voice_client.move_to(channel)

 await channel.connect()

 @commands.command()
 async def play(self, ctx, *, query):
 """Plays a file from the local filesystem"""

 source = discord.PCMVolumeTransformer(discord.FFmpegPCMAudio(query))
 ctx.voice_client.play(source, after=lambda e: print(f'Player error: {e}') if e else None)

 await ctx.send(f'Now playing: {query}')

 @commands.command()
 async def yt(self, ctx, *, url):
 """Plays from a url (almost anything youtube_dl supports)"""

 async with ctx.typing():
 player = await YTDLSource.from_url(url, loop=self.bot.loop)
 ctx.voice_client.play(player, after=lambda e: print(f'Player error: {e}') if e else None)

 await ctx.send(f'Now playing: {player.title}')

 @commands.command()
 async def stream(self, ctx, *, url):
 """Streams from a url (same as yt, but doesn't predownload)"""

 async with ctx.typing():
 player = await YTDLSource.from_url(url, loop=self.bot.loop, stream=True)
 ctx.voice_client.play(player, after=lambda e: print(f'Player error: {e}') if e else None)

 await ctx.send(f'Now playing: {player.title}')

 @commands.command()
 async def volume(self, ctx, volume: int):
 """Changes the player's volume"""

 if ctx.voice_client is None:
 return await ctx.send("Not connected to a voice channel.")

 ctx.voice_client.source.volume = volume / 100
 await ctx.send(f"Changed volume to {volume}%")

 @commands.command()
 async def stop(self, ctx):
 """Stops and disconnects the bot from voice"""

 await ctx.voice_client.disconnect()

 @play.before_invoke
 @yt.before_invoke
 @stream.before_invoke
 async def ensure_voice(self, ctx):
 if ctx.voice_client is None:
 if ctx.author.voice:
 await ctx.author.voice.channel.connect()
 else:
 await ctx.send("You are not connected to a voice channel.")
 raise commands.CommandError("Author not connected to a voice channel.")
 elif ctx.voice_client.is_playing():
 ctx.voice_client.stop()


intents = discord.Intents.default()
intents.message_content = True

bot = commands.Bot(
 command_prefix=commands.when_mentioned_or("!"),
 description='Relatively simple music bot example',
 intents=intents,
)


@bot.event
async def on_ready():
 print(f'Logged in as {bot.user} (ID: {bot.user.id})')
 print('------')


async def main():
 async with bot:
 await bot.add_cog(Music(bot))
 #referenced my discord token in constants.py
 await bot.start(constants.discord_token)


asyncio.run(main())



This code should cause the Discord bot to join the voice channel and play a local audio file when the message " !play query" is entered into the Discord text channel, with query in my case being "test.mp3." When " !play test.mp3" is entered, the bot does join the voice channel and appears to generate voice activity, however no sound is heard. No errors are thrown in the output. The bot simply continues on silently.


Here's what I've checked and tried :


- 

-
I am in the Discord voice channel. The code will alert that user is not in the voice channel if a user attempts to summon the bot without being in a voice channel.


-
FFmpeg is installed and added to PATH. I even stored a copy of the .exe in the project folder.


-
I've tried specifying the full path of both FFmpeg (adding the "executable=" arg to the play function) and the audio file (stored at C :/test.mp3 and a copy in the project folder).


-
All libraries up to date.


-
Reviewed discord.py docs (https://discordpy.readthedocs.io/en/stable/api.html#voice-related) and played around with opuslib (docs say not necessary on Windows, which I'm on) and FFmpegOpusAudio, but results were the same.


-
Referenced numerous StackOverflow threads, including every one suggested when I typed the title of this post. Tried each suggestion individually and in various combinations when possible. See a few below :
Discord.py music_cog, bot joins channel but plays no sound
Discord.py Music Bot doesn’t play music
Discord.py Bot Not Playing music


-
Double checked my sound is on and volume turned up. My sound is working I can hear the Discord notification when the bot joins the voice channel.


-
Issue is the same if I try to play a YouTube file with !yt command. Bot joins channel fine but no sound is produced.




















That's everything I can think of at the moment. There seem to be many posts regarding this exact topic and also variations of it. I've been unable to find a clear and consistent answer, nor one the works for me. I am willing to try anything at this point as it is obviously possible, but for whatever reason success eludes me. Thank you in advance for any assistance you are willing to offer.


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