
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 (104)
-
Les tâches Cron régulières de la ferme
1er décembre 2010, parLa gestion de la ferme passe par l’exécution à intervalle régulier de plusieurs tâches répétitives dites Cron.
Le super Cron (gestion_mutu_super_cron)
Cette tâche, planifiée chaque minute, a pour simple effet d’appeler le Cron de l’ensemble des instances de la mutualisation régulièrement. Couplée avec un Cron système sur le site central de la mutualisation, cela permet de simplement générer des visites régulières sur les différents sites et éviter que les tâches des sites peu visités soient trop (...) -
Encoding and processing into web-friendly formats
13 avril 2011, parMediaSPIP automatically converts uploaded files to internet-compatible formats.
Video files are encoded in MP4, Ogv and WebM (supported by HTML5) and MP4 (supported by Flash).
Audio files are encoded in MP3 and Ogg (supported by HTML5) and MP3 (supported by Flash).
Where possible, text is analyzed in order to retrieve the data needed for search engine detection, and then exported as a series of image files.
All uploaded files are stored online in their original format, so you can (...) -
Formulaire personnalisable
21 juin 2013, parCette page présente les champs disponibles dans le formulaire de publication d’un média et il indique les différents champs qu’on peut ajouter. Formulaire de création d’un Media
Dans le cas d’un document de type média, les champs proposés par défaut sont : Texte Activer/Désactiver le forum ( on peut désactiver l’invite au commentaire pour chaque article ) Licence Ajout/suppression d’auteurs Tags
On peut modifier ce formulaire dans la partie :
Administration > Configuration des masques de formulaire. (...)
Sur d’autres sites (6057)
-
FFmpeg wrong output duration after av_seek_frame
18 septembre 2022, par GogogoI try to transcode a video and also cut it, but in the output file, I get the wrong duration for the file(video duration is correct). It happens when I seek the video, as an example, if I try cut from 60000 to 63000 ms I will get :
Format : WebM
Format version : Version 2
File size : 17.6 KiB
Duration : 1 min 4 s
Overall bit rate : 2 232 b/s
Writing application : Lavf59.31.100
Writing library : Lavf59.31.100


Video
ID : 1
Format : VP9
Codec ID : V_VP9
Duration : 2 s 961 ms
Width : 100 pixels
Height : 100 pixels
Display aspect ratio : 1.000
Frame rate mode : Constant
Frame rate : 24.000 FPS
Default : No
Forced : No


Here is my code, what I am doing wrong ?


namespace {
 
 constexpr auto maxDurationMs = 3000;
 constexpr auto maxFileSizeByte = 100000;
 
 struct StreamingParams {
 std::string output_extension;
 std::string muxer_opt_key;
 std::string muxer_opt_value;
 std::string video_codec;
 std::string codec_priv_key;
 std::string codec_priv_value;
 };
 
 struct StreamingContext {
 AVFormatContext* avfc = nullptr;
 AVCodec* video_avc = nullptr;
 AVStream* video_avs = nullptr;
 AVCodecContext* video_avcc = nullptr;
 int video_index = 0;
 std::string filename;
 ~StreamingContext() {}
 };
 
 struct StreamingContextDeleter {
 void operator()(StreamingContext* context) {
 if (context) {
 auto* avfc = &context->avfc;
 auto* avcc = &context->video_avcc;
 if (avfc)
 avformat_close_input(avfc);
 if (avcc)
 avcodec_free_context(avcc);
 if (context->avfc)
 avformat_free_context(context->avfc);
 }
 }
 };
 
 struct AVFrameDeleter {
 void operator()(AVFrame* frame) {
 if (frame)
 av_frame_free(&frame);
 }
 };
 
 struct AVPacketDeleter {
 void operator()(AVPacket* packet) {
 if (packet)
 av_packet_free(&packet);
 }
 };
 
 struct SwsContextDeleter {
 void operator()(SwsContext* context) {
 if (context)
 sws_freeContext(context);
 }
 };
 
 struct AVDictionaryDeleter {
 void operator()(AVDictionary* dictionary) {
 if (dictionary)
 av_dict_free(&dictionary);
 }
 };
 
 int fill_stream_info(AVStream* avs, AVCodec** avc, AVCodecContext** avcc) {
 *avc = const_cast(avcodec_find_decoder(avs->codecpar->codec_id));
 if (!*avc) return -1;

 *avcc = avcodec_alloc_context3(*avc);
 if (!*avcc) return -1;
 if (avcodec_parameters_to_context(*avcc, avs->codecpar) < 0) return -1;
 if (avcodec_open2(*avcc, *avc, nullptr) < 0) return -1;

 return 0;
 }
 
 int open_media(const char* in_filename, AVFormatContext** avfc) {
 *avfc = avformat_alloc_context();
 if (!*avfc) return -1;
 if (avformat_open_input(avfc, in_filename, nullptr, nullptr) != 0) return -1;
 if (avformat_find_stream_info(*avfc, nullptr) < 0) return -1;
 
 return 0;
 }
 
 int prepare_decoder(std::shared_ptr<streamingcontext> sc) {
 for (int i = 0; i < sc->avfc->nb_streams; i++) {
 if (sc->avfc->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
 sc->video_avs = sc->avfc->streams[i];
 sc->video_index = i;
 
 if (fill_stream_info(sc->video_avs, &sc->video_avc, &sc->video_avcc)) return -1;
 }
 }
 
 return 0;
 }
 
 int prepare_video_encoder(std::shared_ptr<streamingcontext> sc,
 AVCodecContext* decoder_ctx,
 AVRational input_framerate,
 const StreamingParams& sp) {
 sc->video_avs = avformat_new_stream(sc->avfc, nullptr);
 
 sc->video_avc = const_cast(
 avcodec_find_encoder_by_name(sp.video_codec.c_str()));
 if (!sc->video_avc) return -1;
 
 sc->video_avcc = avcodec_alloc_context3(sc->video_avc);
 if (!sc->video_avcc) return -1;
 
 av_opt_set(sc->video_avcc->priv_data, "preset", "fast", 0);

 sc->video_avcc->height = 100;
 sc->video_avcc->width = 100;
 sc->video_avcc->sample_aspect_ratio = decoder_ctx->sample_aspect_ratio;
 if (sc->video_avc->pix_fmts)
 sc->video_avcc->pix_fmt = sc->video_avc->pix_fmts[0];
 else
 sc->video_avcc->pix_fmt = decoder_ctx->pix_fmt;
 
 constexpr int64_t maxBitrate = maxFileSizeByte / (maxDurationMs / 1000.0) - 1;
 
 sc->video_avcc->bit_rate = maxBitrate;
 sc->video_avcc->rc_buffer_size = decoder_ctx->rc_buffer_size;
 sc->video_avcc->rc_max_rate = maxBitrate;
 sc->video_avcc->rc_min_rate = maxBitrate;
 sc->video_avcc->time_base = av_inv_q(input_framerate);
 sc->video_avs->time_base = sc->video_avcc->time_base;
 
 if (avcodec_open2(sc->video_avcc, sc->video_avc, nullptr) < 0) return -1;
 avcodec_parameters_from_context(sc->video_avs->codecpar, sc->video_avcc);
 
 return 0;
 }
 
 int encode_video(std::shared_ptr<streamingcontext> decoder,
 std::shared_ptr<streamingcontext> encoder,
 AVFrame* input_frame) {
 if (input_frame)
 input_frame->pict_type = AV_PICTURE_TYPE_NONE;
 
 AVPacket* output_packet = av_packet_alloc();
 if (!output_packet) return -1;
 
 int response = avcodec_send_frame(encoder->video_avcc, input_frame);
 
 while (response >= 0) {
 response = avcodec_receive_packet(encoder->video_avcc, output_packet);
 if (response == AVERROR(EAGAIN) || response == AVERROR_EOF) {
 break;
 } else if (response < 0) return -1;
 
 output_packet->stream_index = decoder->video_index;
 output_packet->duration = encoder->video_avs->time_base.den /
 encoder->video_avs->time_base.num /
 decoder->video_avs->avg_frame_rate.num *
 decoder->video_avs->avg_frame_rate.den;
 
 av_packet_rescale_ts(output_packet, decoder->video_avs->time_base,
 encoder->video_avs->time_base);
 
 response = av_interleaved_write_frame(encoder->avfc, output_packet);
 if (response != 0) return -1;
 }
 av_packet_unref(output_packet);
 av_packet_free(&output_packet);
 return 0;
 }
 
 int transcode_video(std::shared_ptr<streamingcontext> decoder,
 std::shared_ptr<streamingcontext> encoder,
 AVPacket* input_packet,
 AVFrame* input_frame) {
 int response = avcodec_send_packet(decoder->video_avcc, input_packet);
 if (response < 0) return response;

 
 while (response >= 0) {
 response = avcodec_receive_frame(decoder->video_avcc, input_frame);
 if (response == AVERROR(EAGAIN) || response == AVERROR_EOF) {
 break;
 } else if (response < 0) return response;
 
 if (response >= 0) {
 if (encode_video(decoder, encoder, input_frame)) return -1;
 }
 av_frame_unref(input_frame);
 }
 
 return 0;
 }
 
 } // namespace
 
 
 int VideoToGifConverter::convert(VideoProp input, QString output) {
 StreamingParams sp;
 sp.output_extension = ".webm";
 sp.video_codec = "libvpx-vp9";
 
 auto inputStd = input.path.toStdString();
 auto outputStd =
 (output + '/' + QUuid::createUuid().toString(QUuid::StringFormat::Id128))
 .toStdString() +
 sp.output_extension;
 
 auto decoder = std::shared_ptr<streamingcontext>(new StreamingContext,
 StreamingContextDeleter{});
 auto encoder = std::shared_ptr<streamingcontext>(new StreamingContext,
 StreamingContextDeleter{});
 
 encoder->filename = std::move(outputStd);
 decoder->filename = std::move(inputStd);
 
 if (open_media(decoder->filename.c_str(), &decoder->avfc))
 return -1;
 if (prepare_decoder(decoder))
 return -1;
 
 avformat_alloc_output_context2(&encoder->avfc, nullptr, nullptr,
 encoder->filename.c_str());
 if (!encoder->avfc) return -1;
 
 AVRational input_framerate =
 av_guess_frame_rate(decoder->avfc, decoder->video_avs, nullptr);
 prepare_video_encoder(encoder, decoder->video_avcc, input_framerate, sp);
 
 if (encoder->avfc->oformat->flags & AVFMT_GLOBALHEADER)
 encoder->avfc->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

 if (!(encoder->avfc->oformat->flags & AVFMT_NOFILE)) {
 if (avio_open(&encoder->avfc->pb, encoder->filename.c_str(),
 AVIO_FLAG_WRITE) < 0) return -1;
 }
 
 AVDictionary* muxer_opts = nullptr;
 
 if (!sp.muxer_opt_key.empty() && !sp.muxer_opt_value.empty()) {
 av_dict_set(&muxer_opts, sp.muxer_opt_key.c_str(),
 sp.muxer_opt_value.c_str(), 0);
 }
 
 if (avformat_write_header(encoder->avfc, &muxer_opts) < 0) return -1;
 
 auto inputFrame = std::unique_ptr(av_frame_alloc());
 if (!inputFrame) return -1;
 
 auto inputPacket =
 std::unique_ptr(av_packet_alloc());
 if (!inputPacket) return -1;
 
 auto** streams = decoder->avfc->streams;
 
 const auto fps = static_cast<double>(
 streams[inputPacket->stream_index]->avg_frame_rate.num) /
 streams[inputPacket->stream_index]->avg_frame_rate.den;
 const size_t beginFrame = input.beginPosMs * fps / 1000;
 const size_t endFrame = input.endPosMs * fps / 1000;
 const auto totalFrames = endFrame - beginFrame;
 
 size_t count = 0;
 
 int64_t startTime =
 av_rescale_q(input.beginPosMs * AV_TIME_BASE / 1000, {1, AV_TIME_BASE},
 decoder->video_avs->time_base);
 
 av_seek_frame(decoder->avfc, inputPacket->stream_index, startTime, 0);
 avcodec_flush_buffers(decoder->video_avcc);
 
 while (count < totalFrames &&
 av_read_frame(decoder->avfc, inputPacket.get()) >= 0) {
 if (streams[inputPacket->stream_index]->codecpar->codec_type ==
 AVMEDIA_TYPE_VIDEO) {
 if (transcode_video(decoder, encoder, inputPacket.get(), inputFrame.get())) {
 return -1;
 }
 ++count;
 }
 av_packet_unref(inputPacket.get());
 }
 
 if (encode_video(decoder, encoder, nullptr, nullptr)) return -1;
 
 av_write_trailer(encoder->avfc);
 
 return 0;
 }
</double></streamingcontext></streamingcontext></streamingcontext></streamingcontext></streamingcontext></streamingcontext></streamingcontext></streamingcontext>


-
Apostrophe issue with FFmpeg
1er mars 2024, par Rohan MolinilloI'm working on a company's project which is vue.js.
There are also code parts in php.
It uses FFmpeg to create a video from multiple videos.
On each video, there is a text type subtitle.
Each text is retrieved from a .txt file
But I have a problem with apostrophes.


If in the subtitle is stored like this ( I'm here ) in the txt file, on the video there will be written ( Im ).
The apostrophe is removed and the rest of the text too ( here ) will not be displayed.


I'm new to php and ffmpeg, I've been on this problem for almost 3 weeks.


I share the php code with you.


<?php

declare(strict_types=1);

array_shift($argv); // remove script name in $argv[0]

$parameters = array_reduce($argv, function ($carry, $arg) {
 $tokens = explode('=', $arg);
 $carry[$tokens[0]] = $tokens[1];
 return $carry;
}, []);

$projectPath = $parameters['projectPath'];
$musicPath = $parameters['musicPath'];

$fontPath = getcwd() . "/public/fonts/cobol/Cobol-Bold.ttf";
$logoPath = getcwd() . "/public/images/saintex.jpg";
$carnetLogoPath = getcwd() . "/public/images/CarnetTitre.jpg";

// Adding descriptions for each clip and fade in and fade out filters
$clipsToDescribe = glob("$projectPath/*.webm");
$clipFrameRate = (int) shell_exec("cd $projectPath && ffprobe -v error -select_streams v -of default=noprint_wrappers=1:nokey=1 -show_entries stream=r_frame_rate $clipsToDescribe[0]");
$clipFrameRate = $clipFrameRate > 60 ? 30 : $clipFrameRate;

foreach ($clipsToDescribe as $key => $clipToDescribe) {
 $clipIndex = $key + 1;
 $clipFrames = (int) shell_exec("cd $projectPath && ffprobe -v error -select_streams v:0 -count_packets -show_entries stream=nb_read_packets -of csv=p=0 $clipToDescribe");
 $clipDuration = (float) ($clipFrames / $clipFrameRate) - 0.5;
 file_put_contents("$projectPath/clip{$clipIndex}_desc.txt", addslashes($parameters["clip{$clipIndex}_desc"]));
 shell_exec("cd $projectPath && ffmpeg -i $clipToDescribe -vf 'drawtext=fontfile=$fontPath: textfile=clip{$clipIndex}_desc.txt: fontcolor=white: fontsize=46: box=1: boxcolor=black@0.5: boxborderw=5: x=(w-text_w)/2: y=(h-text_h-50): fix_bounds=true, fade=t=in:st=0:d=0.3,fade=t=out:st=$clipDuration:d=0.3' -b:v 3000K -b:a 192K clip{$clipIndex}.webm");
}
array_map('unlink', glob("$projectPath/*desc.txt"));

shell_exec("cd $projectPath && ffmpeg -t 2 -f lavfi -i color=c=black:s=1280x720 -r 30 blank.webm");
shell_exec("cd $projectPath && ffmpeg -i blank.webm -i $carnetLogoPath -filter_complex '[0:v][1:v] overlay=(main_w/2)-(overlay_w/2):(main_h/2)-(overlay_h/2)' -pix_fmt yuv420p -c:a copy logo.webm");



$workshop = $parameters["workshop_type"];
$title = $parameters["title"];
shell_exec("cd $projectPath && ffmpeg -f lavfi -i color=size=1280x720:duration=3:rate=30:color=black -vf 'drawtext=text=$workshop:fontfile=$fontPath:fontcolor=white:fontsize=46:x=(w-text_w)/2:y=(h-text_h)/2, drawtext=text=$title:fontfile=$fontPath:fontcolor=white:fontsize=46:x=(w-text_w)/2:y=((h-text_h)/2)+lh+5' opening.webm");
unlink("$projectPath/blank.webm");

$videosFile = "file 'logo.webm'\n";
$videosFile .= "file 'opening.webm'\n";{
 file_put_contents("$projectPath/project_desc.txt", $parameters["project_desc"]);
 shell_exec("cd $projectPath && ffmpeg -f lavfi -i color=size=1280x720:duration=3:rate=30:color=black -vf 'drawtext=fontfile=$fontPath:fontsize=46:fontcolor=white:x=(w-text_w)/2:y=(h-text_h)/2:textfile=project_desc.txt' project_desc.webm");
 unlink("$projectPath/project_desc.txt");
 $videosFile .= "file 'project_desc.webm'\n";
} 
if(array_key_exists("participants", $parameters)) {
 file_put_contents("$projectPath/participants.txt", "Participants :\n" . $parameters["participants"]);
 shell_exec("cd $projectPath && ffmpeg -f lavfi -i color=size=1280x720:duration=2:rate=30:color=black -vf 'drawtext=fontfile=$fontPath:fontsize=46:fontcolor=white:x=(w-text_w)/2:y=(h-text_h)/2:textfile=participants.txt' participants.webm");
 unlink("$projectPath/participants.txt");
}

$videos = glob("$projectPath/clip*.webm");
foreach($videos as $video) {
 if($video != "originalVideos") {
 $videosFile .= "file ". "'{$video}'\n";
 }
} 
if(array_key_exists("participants", $parameters)) {
 $videosFile .= "file '$projectPath/participants.webm'";
}
array_map('unlink', glob("$projectPath/*webm.txt"));
file_put_contents("$projectPath/videosFile.txt", $videosFile);
if($musicPath == "/_musics/") {
 echo(shell_exec("cd $projectPath && ffmpeg -f concat -safe 0 -i videosFile.txt -b:v 10000K -crf 20 -b:a 192K output.webm"));
} else {
 echo(shell_exec("cd $projectPath && ffmpeg -f concat -safe 0 -i videosFile.txt -b:v 10000K -crf 20 -b:a 192K assembled.webm && ffmpeg -i assembled.webm -i ../..$musicPath -filter_complex ' [1:0] apad ' -shortest -y -b:v 3000K -b:a 192K output.webm"));
}



I tried many things but each time there were errors.
I think I didn't implement the code properly.


I share you the error


[09:21:02] RECEIVED EVENT: videoRequest
{ Error: Command failed: php ./public/src/generate.php projectPath='/home/rohan/Documents/dodoc2/_projects/its-a-test' musicPath='/_musics/classic.mp3' clip1_name='' clip2_name='' clip3_name='' clip4_name='' clip5_name='' clip1_desc='It's a first test' clip2_desc='It's a second test' clip3_desc='It's a third test' clip4_desc='It's a fourth' clip5_desc='It's a last test' project_desc='' workshop_type='Atelier Robotique' title='It's a test' participants='Molinillo Rohan
'
PHP Warning: Undefined array key 1 in /home/rohan/carnet-numerique/public/src/generate.php on line 9
PHP Warning: Undefined array key 1 in /home/rohan/carnet-numerique/public/src/generate.php on line 9
PHP Warning: Undefined array key 1 in /home/rohan/carnet-numerique/public/src/generate.php on line 9
PHP Warning: Undefined array key 1 in /home/rohan/carnet-numerique/public/src/generate.php on line 9
PHP Warning: Undefined array key 1 in /home/rohan/carnet-numerique/public/src/generate.php on line 9
PHP Warning: Undefined array key 1 in /home/rohan/carnet-numerique/public/src/generate.php on line 9
ffmpeg version 5.1.1-1ubuntu2.1 Copyright (c) 2000-2022 the FFmpeg developers
 built with gcc 12 (Ubuntu 12.2.0-3ubuntu1)
 configuration: --prefix=/usr --extra-version=1ubuntu2.1 --toolchain=hardened --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu --arch=amd64 --enable-gpl --disable-stripping --enable-gnutls --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libcodec2 --enable-libdav1d --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libglslang --enable-libgme --enable-libgsm --enable-libjack --enable-libmp3lame --enable-libmysofa --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librabbitmq --enable-librist --enable-librubberband --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libsrt --enable-libssh --enable-libsvtav1 --enable-libtheora --enable-libtwolame --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzimg --enable-libzmq --enable-libzvbi --enable-lv2 --enable-omx --enable-openal --enable-opencl --enable-opengl --enable-sdl2 --disable-sndio --enable-pocketsphinx --enable-librsvg --enable-libmfx --enable-libdc1394 --enable-libdrm --enable-libiec61883 --enable-chromaprint --enable-frei0r --enable-libx264 --enable-libplacebo --enable-shared
 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, matroska,webm, from '/home/rohan/Documents/dodoc2/_projects/its-a-test/video-20230404-091933-682.webm':
 Metadata:
 encoder : QTmuxingAppLibWebM-0.0.1
 Duration: N/A, start: -0.001000, bitrate: N/A
 Stream #0:0(eng): Video: vp8, yuv420p(progressive), 1280x720, SAR 1:1 DAR 16:9, 1k tbr, 1k tbn (default)
 Stream #0:1(eng): Audio: opus, 48000 Hz, stereo, fltp (default)
Stream mapping:
 Stream #0:0 -> #0:0 (vp8 (native) -> vp9 (libvpx-vp9))
 Stream #0:1 -> #0:1 (opus (native) -> opus (libopus))
Press [q] to stop, [?] for help
[libvpx-vp9 @ 0x55952c183000] v1.12.0
Output #0, webm, to 'clip1.webm':
 Metadata:
 encoder : Lavf59.27.100
 Stream #0:0(eng): Video: vp9, yuv420p(tv, bt470bg/unknown/unknown, progressive), 1280x720 [SAR 1:1 DAR 16:9], q=2-31, 3000 kb/s, 1k fps, 1k tbn (default)
 Metadata:
 encoder : Lavc59.37.100 libvpx-vp9
 Side data:
 cpb: bitrate max/min/avg: 0/0/0 buffer size: 0 vbv_delay: N/A
 Stream #0:1(eng): Audio: opus, 48000 Hz, stereo, flt, 192 kb/s (default)
 Metadata:
 encoder : Lavc59.37.100 libopus
frame= 229 fps= 11 q=12.0 Lsize= 2786kB time=00:00:07.52 bitrate=3034.7kbits/s speed=0.377x 
video:2601kB audio:181kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.175036%
PHP Warning: Undefined array key "clip2_desc" in /home/rohan/carnet-numerique/public/src/generate.php on line 29
PHP Fatal error: Uncaught TypeError: addslashes(): Argument #1 ($string) must be of type string, null given in /home/rohan/carnet-numerique/public/src/generate.php:29
Stack trace:
#0 /home/rohan/carnet-numerique/public/src/generate.php(29): addslashes()
#1 {main}
 thrown in /home/rohan/carnet-numerique/public/src/generate.php on line 29

 at ChildProcess.exithandler (child_process.js:275:12)
 at emitTwo (events.js:126:13)
 at ChildProcess.emit (events.js:214:7)
 at maybeClose (internal/child_process.js:925:16)
 at Socket.stream.socket.on (internal/child_process.js:346:11)
 at emitOne (events.js:116:13)
 at Socket.emit (events.js:211:7)
 at Pipe._handle.close [as _onclose] (net.js:554:12)
 killed: false,
 code: 255,
 signal: null,
 cmd: 'php ./public/src/generate.php projectPath=\'/home/rohan/Documents/dodoc2/_projects/its-a-test\' musicPath=\'/_musics/classic.mp3\' clip1_name=\'\' clip2_name=\'\' clip3_name=\'\' clip4_name=\'\' clip5_name=\'\' clip1_desc=\'It\'s a first test\' clip2_desc=\'It\'s a second test\' clip3_desc=\'It\'s a third test\' clip4_desc=\'It\'s a fourth\' clip5_desc=\'It\'s a last test\' project_desc=\'\' workshop_type=\'Atelier Robotique\' title=\'It\'s a test\' participants=\'Molinillo Rohan\n\'' }



-
Discord 24/7 video stream self-bot crashes after a couple hours
21 juillet 2023, par angeloI've implemented this library to make a self-bot that streams videos from a local folder in a loop 24/7 (don't ask me why). I set up an ubuntu vps to run the bot and it works perfectly fine the first 2-3 hours, after that it gets more and more laggy until the server crashes.
pd : It's basically my first time using javascript and stole most of the code from this repo so don't bully me.


Here's the code :


import { Client, TextChannel, CustomStatus, ActivityOptions } from "discord.js-selfbot-v13";
import { command, streamLivestreamVideo, VoiceUdp, setStreamOpts, streamOpts } from "@dank074/discord-video-stream";
import config from "./config.json";
import fs from 'fs';
import path from 'path';

const client = new Client();

client.patchVoiceEvents(); //this is necessary to register event handlers

setStreamOpts(
 config.streamOpts.width,
 config.streamOpts.height,
 config.streamOpts.fps,
 config.streamOpts.bitrateKbps,
 config.streamOpts.hardware_acc
)

const prefix = '$';

const moviesFolder = config.movieFolder || './movies';

const movieFiles = fs.readdirSync(moviesFolder);
let movies = movieFiles.map(file => {
 const fileName = path.parse(file).name;
 // replace space with _
 return { name: fileName.replace(/ /g, ''), path: path.join(moviesFolder, file) };
});
let originalMovList = [...movies];
let movList = movies;
let shouldStop = false;

// print out all movies
console.log(`Available movies:\n${movies.map(m => m.name).join('\n')}`);

const status_idle = () => {
 return new CustomStatus()
 .setState('摸鱼进行中')
 .setEmoji('🐟')
}

const status_watch = (name) => {
 return new CustomStatus()
 .setState(`Playing ${name}...`)
 .setEmoji('📽')
}

// ready event
client.on("ready", () => {
 if (client.user) {
 console.log(`--- ${client.user.tag} is ready ---`);
 client.user.setActivity(status_idle() as ActivityOptions)
 }
});

let streamStatus = {
 joined: false,
 joinsucc: false,
 playing: false,
 channelInfo: {
 guildId: '',
 channelId: '',
 cmdChannelId: ''
 },
 starttime: "00:00:00",
 timemark: '',
}

client.on('voiceStateUpdate', (oldState, newState) => {
 // when exit channel
 if (oldState.member?.user.id == client.user?.id) {
 if (oldState.channelId && !newState.channelId) {
 streamStatus.joined = false;
 streamStatus.joinsucc = false;
 streamStatus.playing = false;
 streamStatus.channelInfo = {
 guildId: '',
 channelId: '',
 cmdChannelId: streamStatus.channelInfo.cmdChannelId
 }
 client.user?.setActivity(status_idle() as ActivityOptions)
 }
 }
 // when join channel success
 if (newState.member?.user.id == client.user?.id) {
 if (newState.channelId && !oldState.channelId) {
 streamStatus.joined = true;
 if (newState.guild.id == streamStatus.channelInfo.guildId && newState.channelId == streamStatus.channelInfo.channelId) {
 streamStatus.joinsucc = true;
 }
 }
 }
})

client.on('messageCreate', async (message) => {
 if (message.author.bot) return; // ignore bots
 if (message.author.id == client.user?.id) return; // ignore self
 if (!config.commandChannels.includes(message.channel.id)) return; // ignore non-command channels
 if (!message.content.startsWith(prefix)) return; // ignore non-commands

 const args = message.content.slice(prefix.length).trim().split(/ +/); // split command and arguments
 if (args.length == 0) return;

 const user_cmd = args.shift()!.toLowerCase();

 if (config.commandChannels.includes(message.channel.id)) {
 switch (user_cmd) {
 case 'play':
 playCommand(args, message);
 break;
 case 'stop':
 stopCommand(message);
 break;
 case 'playtime':
 playtimeCommand(message);
 break;
 case 'pause':
 pauseCommand(message);
 break;
 case 'resume':
 resumeCommand(message);
 break;
 case 'list':
 listCommand(message);
 break;
 case 'status':
 statusCommand(message);
 break;
 case 'refresh':
 refreshCommand(message);
 break;
 case 'help':
 helpCommand(message);
 break;
 case 'playall':
 playAllCommand(args, message);
 break;
 case 'stream':
 streamCommand(args, message);
 break;
 case 'shuffle':
 shuffleCommand();
 break;
 case 'skip':
 //skip cmd
 break;
 default:
 message.reply('Invalid command');
 }
 }
});

client.login("TOKEN_HERE");

let lastPrint = "";

async function playAllCommand(args, message) {
 if (streamStatus.joined) {
 message.reply("Already joined");
 return;
 }

 // args = [guildId]/[channelId]
 if (args.length === 0) {
 message.reply("Missing voice channel");
 return;
 }

 // process args
 const [guildId, channelId] = args.shift()!.split("/");
 if (!guildId || !channelId) {
 message.reply("Invalid voice channel");
 return;
 }

 await client.joinVoice(guildId, channelId);
 streamStatus.joined = true;
 streamStatus.playing = false;
 streamStatus.starttime = "00:00:00";
 streamStatus.channelInfo = {
 guildId: guildId,
 channelId: channelId,
 cmdChannelId: message.channel.id,
 };

 const streamUdpConn = await client.createStream();

 streamUdpConn.voiceConnection.setSpeaking(true);
 streamUdpConn.voiceConnection.setVideoStatus(true);

 playAllVideos(streamUdpConn); // Start playing videos

 // Keep the stream open

 streamStatus.joined = false;
 streamStatus.joinsucc = false;
 streamStatus.playing = false;
 lastPrint = "";
 streamStatus.channelInfo = {
 guildId: "",
 channelId: "",
 cmdChannelId: "",
 };
}

async function playAllVideos(udpConn: VoiceUdp) {

 console.log("Started playing video");

 udpConn.voiceConnection.setSpeaking(true);
 udpConn.voiceConnection.setVideoStatus(true);

 try {
 let index = 0;

 while (true) {
 if (shouldStop) {
 break; // For the stop command
 }

 if (index >= movies.length) {
 // Reset the loop
 index = 0;
 }

 const movie = movList[index];

 if (!movie) {
 console.log("Movie not found");
 index++;
 continue;
 }

 let options = {};
 options["-ss"] = "00:00:00";

 console.log(`Playing ${movie.name}...`);

 try {
 let videoStream = streamLivestreamVideo(movie.path, udpConn);
 command?.on('progress', (msg) => {
 // print timemark if it passed 10 second sionce last print, becareful when it pass 0
 if (streamStatus.timemark) {
 if (lastPrint != "") {
 let last = lastPrint.split(':');
 let now = msg.timemark.split(':');
 // turn to seconds
 let s = parseInt(now[2]) + parseInt(now[1]) * 60 + parseInt(now[0]) * 3600;
 let l = parseInt(last[2]) + parseInt(last[1]) * 60 + parseInt(last[0]) * 3600;
 if (s - l >= 10) {
 console.log(`Timemark: ${msg.timemark}`);
 lastPrint = msg.timemark;
 }
 } else {
 console.log(`Timemark: ${msg.timemark}`);
 lastPrint = msg.timemark;
 }
 }
 streamStatus.timemark = msg.timemark;
 });
 const res = await videoStream;
 console.log("Finished playing video " + res);
 } catch (e) {
 console.log(e);
 }

 index++; // Pasar a la siguiente película
 }
 } finally {
 udpConn.voiceConnection.setSpeaking(false);
 udpConn.voiceConnection.setVideoStatus(false);
 }

 command?.kill("SIGINT");
 // send message to channel, not reply
 (client.channels.cache.get(streamStatus.channelInfo.cmdChannelId) as TextChannel).send('Finished playing video, timemark is ' + streamStatus.timemark);
 client.leaveVoice();
 client.user?.setActivity(status_idle() as ActivityOptions)
 streamStatus.joined = false;
 streamStatus.joinsucc = false;
 streamStatus.playing = false;
 lastPrint = ""
 streamStatus.channelInfo = {
 guildId: '',
 channelId: '',
 cmdChannelId: ''
 };
}

function shuffleArray(array) {
 for (let i = array.length - 1; i > 0; i--) {
 const j = Math.floor(Math.random() * (i + 1));
 [array[i], array[j]] = [array[j], array[i]];
 }
}

function shuffleCommand() {
 shuffleArray(movList);
}

async function playCommand(args, message) {
 if (streamStatus.joined) {
 message.reply('Already joined');
 return;
 }

 // args = [guildId]/[channelId]
 if (args.length == 0) {
 message.reply('Missing voice channel');
 return;
 }

 // process args
 const [guildId, channelId] = args.shift()!.split('/');
 if (!guildId || !channelId) {
 message.reply('Invalid voice channel');
 return;
 }

 // get movie name and find movie file
 let moviename = args.shift()
 let movie = movies.find(m => m.name == moviename);

 if (!movie) {
 message.reply('Movie not found');
 return;
 }

 // get start time from args "hh:mm:ss"
 let startTime = args.shift();
 let options = {}
 // check if start time is valid
 if (startTime) {
 let time = startTime.split(':');
 if (time.length != 3) {
 message.reply('Invalid start time');
 return;
 }
 let h = parseInt(time[0]);
 let m = parseInt(time[1]);
 let s = parseInt(time[2]);
 if (isNaN(h) || isNaN(m) || isNaN(s)) {
 message.reply('Invalid start time');
 return;
 }
 startTime = `${h}:${m}:${s}`;
 options['-ss'] = startTime;
 console.log("Start time: " + startTime);
 }

 await client.joinVoice(guildId, channelId);
 streamStatus.joined = true;
 streamStatus.playing = false;
 streamStatus.starttime = startTime ? startTime : "00:00:00";
 streamStatus.channelInfo = {
 guildId: guildId,
 channelId: channelId,
 cmdChannelId: message.channel.id
 }
 const streamUdpConn = await client.createStream();
 playVideo(movie.path, streamUdpConn, options);
 message.reply('Playing ' + (startTime ? ` from ${startTime} ` : '') + moviename + '...');
 client.user?.setActivity(status_watch(moviename) as ActivityOptions);
}

function stopCommand(message) {
 client.leaveVoice()
 streamStatus.joined = false;
 streamStatus.joinsucc = false;
 streamStatus.playing = false;
 streamStatus.channelInfo = {
 guildId: '',
 channelId: '',
 cmdChannelId: streamStatus.channelInfo.cmdChannelId
 }
 // use sigquit??
 command?.kill("SIGINT");
 // msg
 message.reply('Stopped playing');
 shouldStop = true;
 movList = [...originalMovList];
}

function playtimeCommand(message) {
 // streamStatus.starttime + streamStatus.timemark
 // starttime is hh:mm:ss, timemark is hh:mm:ss.000
 let start = streamStatus.starttime.split(':');
 let mark = streamStatus.timemark.split(':');
 let h = parseInt(start[0]) + parseInt(mark[0]);
 let m = parseInt(start[1]) + parseInt(mark[1]);
 let s = parseInt(start[2]) + parseInt(mark[2]);
 if (s >= 60) {
 m += 1;
 s -= 60;
 }
 if (m >= 60) {
 h += 1;
 m -= 60;
 }
 message.reply(`Play time: ${h}:${m}:${s}`);
}

function pauseCommand(message) {
 if (!streamStatus.playing) {
 command?.kill("SIGSTOP");
 message.reply('Paused');
 streamStatus.playing = false;
 } else {
 message.reply('Not playing');
 }
}

function resumeCommand(message) {
 if (!streamStatus.playing) {
 command?.kill("SIGCONT");
 message.reply('Resumed');
 streamStatus.playing = true;
 } else {
 message.reply('Not playing');
 }
}

function listCommand(message) {
 message.reply(`Available movies:\n${movies.map(m => m.name).join('\n')}`);
}

function statusCommand(message) {
 message.reply(`Joined: ${streamStatus.joined}\nJoin success: ${streamStatus.joinsucc}\nPlaying: ${streamStatus.playing}\nChannel: ${streamStatus.channelInfo.guildId}/${streamStatus.channelInfo.channelId}\nTimemark: ${streamStatus.timemark}\nStart time: ${streamStatus.starttime}`);
}

function refreshCommand(message) {
 // refresh movie list
 const movieFiles = fs.readdirSync(moviesFolder);
 movies = movieFiles.map(file => {
 const fileName = path.parse(file).name;
 // replace space with _
 return { name: fileName.replace(/ /g, ''), path: path.join(moviesFolder, file) };
 });
 message.reply('Movie list refreshed ' + movies.length + ' movies found.\n' + movies.map(m => m.name).join('\n'));
}

function helpCommand(message) {
 // reply all commands here
 message.reply('Available commands:\nplay [guildId]/[channelId] [movie] [start time]\nstop\nlist\nstatus\nrefresh\nplaytime\npause\nresume\nhelp');
}

async function playVideo(video: string, udpConn: VoiceUdp, options: any) {
 console.log("Started playing video");

 udpConn.voiceConnection.setSpeaking(true);
 udpConn.voiceConnection.setVideoStatus(true);
 try {
 let videoStream = streamLivestreamVideo(video, udpConn);
 command?.on('progress', (msg) => {
 // print timemark if it passed 10 second sionce last print, becareful when it pass 0
 if (streamStatus.timemark) {
 if (lastPrint != "") {
 let last = lastPrint.split(':');
 let now = msg.timemark.split(':');
 // turn to seconds
 let s = parseInt(now[2]) + parseInt(now[1]) * 60 + parseInt(now[0]) * 3600;
 let l = parseInt(last[2]) + parseInt(last[1]) * 60 + parseInt(last[0]) * 3600;
 if (s - l >= 10) {
 console.log(`Timemark: ${msg.timemark}`);
 lastPrint = msg.timemark;
 }
 } else {
 console.log(`Timemark: ${msg.timemark}`);
 lastPrint = msg.timemark;
 }
 }
 streamStatus.timemark = msg.timemark;
 });
 const res = await videoStream;
 console.log("Finished playing video " + res);
 } catch (e) {
 console.log(e);
 } finally {
 udpConn.voiceConnection.setSpeaking(false);
 udpConn.voiceConnection.setVideoStatus(false);
 }
 command?.kill("SIGINT");
 // send message to channel, not reply
 (client.channels.cache.get(streamStatus.channelInfo.cmdChannelId) as TextChannel).send('Finished playing video, timemark is ' + streamStatus.timemark);
 client.leaveVoice();
 client.user?.setActivity(status_idle() as ActivityOptions)
 streamStatus.joined = false;
 streamStatus.joinsucc = false;
 streamStatus.playing = false;
 lastPrint = ""
 streamStatus.channelInfo = {
 guildId: '',
 channelId: '',
 cmdChannelId: ''
 }
}

async function streamCommand(args, message) {

 if (streamStatus.joined) {
 message.reply('Already joined');
 return;
 }

 // args = [guildId]/[channelId]
 if (args.length == 0) {
 message.reply('Missing voice channel');
 return;
 }

 // process args
 const [guildId, channelId] = args.shift()!.split('/');
 if (!guildId || !channelId) {
 message.reply('Invalid voice channel');
 return;
 }

 let url = args.shift()
 let options = {}

 await client.joinVoice(guildId, channelId);
 streamStatus.joined = true;
 streamStatus.playing = false;
 //streamStatus.starttime = startTime ? startTime : "00:00:00";
 streamStatus.channelInfo = {
 guildId: guildId,
 channelId: channelId,
 cmdChannelId: message.channel.id
 }
 const streamUdpConn = await client.createStream();
 playStream(url, streamUdpConn, options);
 message.reply('Playing url');
 client.user?.setActivity(status_watch('livestream') as ActivityOptions);
}

async function playStream(video: string, udpConn: VoiceUdp, options: any) {
 console.log("Started playing video");

 udpConn.voiceConnection.setSpeaking(true);
 udpConn.voiceConnection.setVideoStatus(true);

 try {
 console.log("Trying to stream url");
 const res = await streamLivestreamVideo(video, udpConn);
 console.log("Finished streaming url");
 } catch (e) {
 console.log(e);
 } finally {
 udpConn.voiceConnection.setSpeaking(false);
 udpConn.voiceConnection.setVideoStatus(false);
 }

 command?.kill("SIGINT");
 client.leaveVoice();
 client.user?.setActivity(status_idle() as ActivityOptions)
 streamStatus.joined = false;
 streamStatus.joinsucc = false;
 streamStatus.playing = false;
 streamStatus.channelInfo = {
 guildId: '',
 channelId: '',
 cmdChannelId: ''
 }

}

// run server if enabled in config
if (config.server.enabled) {
 // run server.js
 require('./server');
}




I've tried running the code with the nocache package, setting up a cron job to clean the cache every 5 minutes, unifying functions in the code, but nothigns works.
I think that the problem has to do with certain process that never really ends after one video finishes playing, probably ffmpeg. I don't know whether is my code, my vps or the library the problem.


I wanted the bot to stay in the voice channel streaming my videos 24/7 (no interruptions), I don't know how to prevent it from getting laggy after a while.


This is the config.json file just in case you wanna test the code and can't find it


{
 "token": "DCTOKEN",
 "videoChannels": ["ID", "OTHERID"],
 "commandChannels": ["ID", "OTHERID"],
 "adminIds": ["ID"],
 "movieFolder": "./movies/",
 "previewCache": "/tmp/preview-cache",
 "streamOpts": {
 "width": 1280,
 "height": 720,
 "fps": 30,
 "bitrateKbps": 3000,
 "hardware_acc": true
 },
 "server": {
 "enabled": false,
 "port": 8080
 }
}