
Recherche avancée
Médias (3)
-
The Slip - Artworks
26 septembre 2011, par
Mis à jour : Septembre 2011
Langue : English
Type : Texte
-
Podcasting Legal guide
16 mai 2011, par
Mis à jour : Mai 2011
Langue : English
Type : Texte
-
Creativecommons informational flyer
16 mai 2011, par
Mis à jour : Juillet 2013
Langue : English
Type : Texte
Autres articles (9)
-
Organiser par catégorie
17 mai 2013, parDans MédiaSPIP, une rubrique a 2 noms : catégorie et rubrique.
Les différents documents stockés dans MédiaSPIP peuvent être rangés dans différentes catégories. On peut créer une catégorie en cliquant sur "publier une catégorie" dans le menu publier en haut à droite ( après authentification ). Une catégorie peut être rangée dans une autre catégorie aussi ce qui fait qu’on peut construire une arborescence de catégories.
Lors de la publication prochaine d’un document, la nouvelle catégorie créée sera proposée (...) -
Les thèmes de MediaSpip
4 juin 20133 thèmes sont proposés à l’origine par MédiaSPIP. L’utilisateur MédiaSPIP peut rajouter des thèmes selon ses besoins.
Thèmes MediaSPIP
3 thèmes ont été développés au départ pour MediaSPIP : * SPIPeo : thème par défaut de MédiaSPIP. Il met en avant la présentation du site et les documents média les plus récents ( le type de tri peut être modifié - titre, popularité, date) . * Arscenic : il s’agit du thème utilisé sur le site officiel du projet, constitué notamment d’un bandeau rouge en début de page. La structure (...) -
Déploiements possibles
31 janvier 2010, parDeux types de déploiements sont envisageable dépendant de deux aspects : La méthode d’installation envisagée (en standalone ou en ferme) ; Le nombre d’encodages journaliers et la fréquentation envisagés ;
L’encodage de vidéos est un processus lourd consommant énormément de ressources système (CPU et RAM), il est nécessaire de prendre tout cela en considération. Ce système n’est donc possible que sur un ou plusieurs serveurs dédiés.
Version mono serveur
La version mono serveur consiste à n’utiliser qu’une (...)
Sur d’autres sites (2816)
-
C++ ffmpeg lib version 7.0 - noice in exported audio
2 septembre 2024, par Chris PI want to make a C++ lib named cppdub which will mimic the python module pydub.


One main function is to export the AudioSegment to a file with a specific format (example : mp3).


The code is :


AudioSegment AudioSegment::from_file(const std::string& file_path, const std::string& format, const std::string& codec,
 const std::map& parameters, int start_second, int duration) {

 avformat_network_init();
 av_log_set_level(AV_LOG_ERROR); // Adjust logging level as needed

 AVFormatContext* format_ctx = nullptr;
 if (avformat_open_input(&format_ctx, file_path.c_str(), nullptr, nullptr) != 0) {
 std::cerr << "Error: Could not open audio file." << std::endl;
 return AudioSegment(); // Return an empty AudioSegment on failure
 }

 if (avformat_find_stream_info(format_ctx, nullptr) < 0) {
 std::cerr << "Error: Could not find stream information." << std::endl;
 avformat_close_input(&format_ctx);
 return AudioSegment();
 }

 int audio_stream_index = -1;
 for (unsigned int i = 0; i < format_ctx->nb_streams; i++) {
 if (format_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
 audio_stream_index = i;
 break;
 }
 }

 if (audio_stream_index == -1) {
 std::cerr << "Error: Could not find audio stream." << std::endl;
 avformat_close_input(&format_ctx);
 return AudioSegment();
 }

 AVCodecParameters* codec_par = format_ctx->streams[audio_stream_index]->codecpar;
 const AVCodec* my_codec = avcodec_find_decoder(codec_par->codec_id);
 AVCodecContext* codec_ctx = avcodec_alloc_context3(my_codec);

 if (avcodec_parameters_to_context(codec_ctx, codec_par) < 0) {
 std::cerr << "Error: Could not initialize codec context." << std::endl;
 avformat_close_input(&format_ctx);
 return AudioSegment();
 }

 if (avcodec_open2(codec_ctx, my_codec, nullptr) < 0) {
 std::cerr << "Error: Could not open codec." << std::endl;
 avcodec_free_context(&codec_ctx);
 avformat_close_input(&format_ctx);
 return AudioSegment();
 }

 SwrContext* swr_ctx = swr_alloc();
 if (!swr_ctx) {
 std::cerr << "Error: Could not allocate SwrContext." << std::endl;
 avcodec_free_context(&codec_ctx);
 avformat_close_input(&format_ctx);
 return AudioSegment();
 }

 av_opt_set_chlayout(swr_ctx, "in_chlayout", &codec_ctx->ch_layout, 0);
 av_opt_set_int(swr_ctx, "in_sample_rate", codec_ctx->sample_rate, 0);
 av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt", codec_ctx->sample_fmt, 0);

 AVChannelLayout dst_ch_layout;
 av_channel_layout_copy(&dst_ch_layout, &codec_ctx->ch_layout);
 av_channel_layout_uninit(&dst_ch_layout);
 av_channel_layout_default(&dst_ch_layout, 2);

 av_opt_set_chlayout(swr_ctx, "out_chlayout", &dst_ch_layout, 0);
 av_opt_set_int(swr_ctx, "out_sample_rate", 48000, 0);
 av_opt_set_sample_fmt(swr_ctx, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);

 if (swr_init(swr_ctx) < 0) {
 std::cerr << "Error: Failed to initialize the resampling context" << std::endl;
 swr_free(&swr_ctx);
 avcodec_free_context(&codec_ctx);
 avformat_close_input(&format_ctx);
 return AudioSegment();
 }

 AVPacket packet;
 AVFrame* frame = av_frame_alloc();
 if (!frame) {
 std::cerr << "Error: Could not allocate frame." << std::endl;
 swr_free(&swr_ctx);
 avcodec_free_context(&codec_ctx);
 avformat_close_input(&format_ctx);
 return AudioSegment();
 }

 std::vector<char> output;
 while (av_read_frame(format_ctx, &packet) >= 0) {
 if (packet.stream_index == audio_stream_index) {
 if (avcodec_send_packet(codec_ctx, &packet) == 0) {
 while (avcodec_receive_frame(codec_ctx, frame) == 0) {
 if (frame->pts != AV_NOPTS_VALUE) {
 frame->pts = av_rescale_q(frame->pts, codec_ctx->time_base, format_ctx->streams[audio_stream_index]->time_base);
 }

 uint8_t* output_buffer;
 int output_samples = av_rescale_rnd(
 swr_get_delay(swr_ctx, codec_ctx->sample_rate) + frame->nb_samples,
 48000, codec_ctx->sample_rate, AV_ROUND_UP);

 int output_buffer_size = av_samples_get_buffer_size(
 nullptr, 2, output_samples, AV_SAMPLE_FMT_S16, 1);

 output_buffer = (uint8_t*)av_malloc(output_buffer_size);

 if (output_buffer) {
 memset(output_buffer, 0, output_buffer_size); // Zero padding to avoid random noise
 int converted_samples = swr_convert(swr_ctx, &output_buffer, output_samples,
 (const uint8_t**)frame->extended_data, frame->nb_samples);

 if (converted_samples >= 0) {
 output.insert(output.end(), output_buffer, output_buffer + output_buffer_size);
 }
 else {
 std::cerr << "Error: Failed to convert audio samples." << std::endl;
 }

 av_free(output_buffer);
 }
 else {
 std::cerr << "Error: Could not allocate output buffer." << std::endl;
 }
 }
 }
 else {
 std::cerr << "Error: Failed to send packet to codec context." << std::endl;
 }
 }
 av_packet_unref(&packet);
 }

 av_frame_free(&frame);
 swr_free(&swr_ctx);
 avcodec_free_context(&codec_ctx);
 avformat_close_input(&format_ctx);

 std::map metadata = {
 {"sample_width", 2},
 {"frame_rate", 48000},
 {"channels", 2},
 {"frame_width", 4}
 };

 return AudioSegment(static_cast<const>(output.data()), output.size(), metadata);
}


std::ofstream AudioSegment::export_segment(std::string& out_f,
 const std::string& format,
 const std::string& codec,
 const std::string& bitrate,
 const std::vector& parameters,
 const std::map& tags,
 const std::string& id3v2_version,
 const std::string& cover) {
 av_log_set_level(AV_LOG_DEBUG);
 AVCodecContext* codec_ctx = nullptr;
 AVFormatContext* format_ctx = nullptr;
 AVStream* stream = nullptr;
 AVFrame* frame = nullptr;
 AVPacket* pkt = nullptr;
 int ret;

 // Open output file
 std::ofstream out_file(out_f, std::ios::binary);
 if (!out_file) {
 throw std::runtime_error("Failed to open output file.");
 }

 // Initialize format context
 avformat_alloc_output_context2(&format_ctx, nullptr, format.c_str(), out_f.c_str());
 if (!format_ctx) {
 throw std::runtime_error("Could not allocate format context.");
 }

 // Find encoder
 const AVCodec* codec_ptr = avcodec_find_encoder_by_name(codec.c_str());
 if (!codec_ptr) {
 throw std::runtime_error("Codec not found.");
 }

 // Add stream
 stream = avformat_new_stream(format_ctx, codec_ptr);
 if (!stream) {
 throw std::runtime_error("Failed to create new stream.");
 }

 // Allocate codec context
 codec_ctx = avcodec_alloc_context3(codec_ptr);
 if (!codec_ctx) {
 throw std::runtime_error("Could not allocate audio codec context.");
 }

 // Set codec parameters
 codec_ctx->bit_rate = std::stoi(bitrate);
 codec_ctx->sample_fmt = codec_ptr->sample_fmts ? codec_ptr->sample_fmts[0] : AV_SAMPLE_FMT_FLTP;
 codec_ctx->sample_rate = frame_rate_;
 codec_ctx->ch_layout.nb_channels = this->get_channels();
 AVChannelLayout ch_layout_1;
 av_channel_layout_uninit(&ch_layout_1);
 av_channel_layout_default(&ch_layout_1, this->get_channels());
 codec_ctx->ch_layout = ch_layout_1;

 // Open codec
 ret = avcodec_open2(codec_ctx, codec_ptr, nullptr);
 if (ret < 0) {
 throw std::runtime_error("Could not open codec.");
 }

 // Initialize packet
 pkt = av_packet_alloc();
 if (!pkt) {
 throw std::runtime_error("Could not allocate AVPacket.");
 }

 // Initialize frame
 frame = av_frame_alloc();
 if (!frame) {
 throw std::runtime_error("Could not allocate AVFrame.");
 }

 frame->nb_samples = codec_ctx->frame_size;
 frame->format = codec_ctx->sample_fmt;
 frame->ch_layout = codec_ctx->ch_layout;
 frame->sample_rate = codec_ctx->sample_rate;

 // Allocate data buffer
 ret = av_frame_get_buffer(frame, 0);
 if (ret < 0) {
 throw std::runtime_error("Could not allocate audio data buffers.");
 }

 // Encode frames
 int samples_read = 0;
 while (samples_read < data_.size()) {
 ret = av_frame_make_writable(frame);
 if (ret < 0) {
 throw std::runtime_error("Frame not writable.");
 }

 // Determine the number of samples to copy into the frame
 int frame_size = std::min<int>(codec_ctx->frame_size, (data_.size() - samples_read) / frame_width_);
 int buffer_size = frame_size * frame_width_;

 // Clear the frame data to avoid artifacts from previous data
 std::memset(frame->data[0], 0, codec_ctx->frame_size * frame_width_);

 // Copy the actual audio data into the frame
 std::memcpy(frame->data[0], data_.data() + samples_read, buffer_size);
 samples_read += buffer_size;

 // If the frame is partially filled, pad the remaining part with zeros
 if (frame_size < codec_ctx->frame_size) {
 std::memset(frame->data[0] + buffer_size, 0, (codec_ctx->frame_size - frame_size) * frame_width_);
 }

 // Send the frame for encoding
 ret = avcodec_send_frame(codec_ctx, frame);
 if (ret < 0) {
 throw std::runtime_error("Error sending frame for encoding.");
 }

 // Receive and write packets
 while (ret >= 0) {
 ret = avcodec_receive_packet(codec_ctx, pkt);
 if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
 break;
 }
 else if (ret < 0) {
 throw std::runtime_error("Error encoding frame.");
 }

 out_file.write(reinterpret_cast(pkt->data), pkt->size);
 av_packet_unref(pkt);
 }
 }

 // **Explicitly flush the encoder**
 ret = avcodec_send_frame(codec_ctx, nullptr);
 if (ret < 0) {
 throw std::runtime_error("Error flushing the encoder.");
 }

 // Receive and write remaining packets after flushing
 while (ret >= 0) {
 ret = avcodec_receive_packet(codec_ctx, pkt);
 if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
 break;
 }
 else if (ret < 0) {
 throw std::runtime_error("Error encoding frame during flush.");
 }

 out_file.write(reinterpret_cast(pkt->data), pkt->size);
 av_packet_unref(pkt);
 }

 // Cleanup
 av_frame_free(&frame);
 av_packet_free(&pkt);
 avcodec_free_context(&codec_ctx);
 avformat_free_context(format_ctx);

 return out_file;
}
</int></const></char>


I have no run time error but i see this message in console :


[libmp3lame @ 000002d26b239ac0] Trying to remove 47 more samples than there are in the queue



I can play the exported mp3 file but there is background noise.


-
C++ ffmpeg lib version 7.0 - runtime error
1er septembre 2024, par Chris PI want to make a C++ lib named cppdub which will mimic the python module pydub.


One main function is to export the AudioSegment to a file with a specific format (example : mp3).


The code is :


void check_av_error(int error_code, const std::string& msg) {
 if (error_code < 0) {
 char errbuf[AV_ERROR_MAX_STRING_SIZE];
 av_strerror(error_code, errbuf, sizeof(errbuf));
 throw std::runtime_error(msg + ": " + errbuf);
 }
}

std::string av_err2str_(int errnum) {
 char buf[AV_ERROR_MAX_STRING_SIZE];
 av_strerror(errnum, buf, sizeof(buf));
 return std::string(buf);
}

void log_error(const std::string& msg) {
 std::cerr << "Error: " << msg << std::endl;
}

std::ofstream cppdub::AudioSegment::export_segment(
 std::string& out_f,
 const std::string& format,
 const std::string& codec,
 const std::string& bitrate,
 const std::vector& parameters,
 const std::map& tags,
 const std::string& id3v2_version,
 const std::string& cover) {

 av_log_set_level(AV_LOG_DEBUG);
 avformat_network_init();

 AVFormatContext* format_ctx = nullptr;
 int ret = avformat_alloc_output_context2(&format_ctx, nullptr, format.c_str(), out_f.c_str());
 check_av_error(ret, "Could not allocate format context");

 if (!(format_ctx->oformat->flags & AVFMT_NOFILE)) {
 ret = avio_open(&format_ctx->pb, out_f.c_str(), AVIO_FLAG_WRITE);
 check_av_error(ret, "Could not open output file");
 }

 AVStream* stream = avformat_new_stream(format_ctx, nullptr);
 if (!stream) {
 avformat_free_context(format_ctx);
 throw std::runtime_error("Could not allocate stream");
 }

 const AVCodec* codec_obj = avcodec_find_encoder_by_name(codec.c_str());
 if (!codec_obj) {
 avformat_free_context(format_ctx);
 throw std::runtime_error("Codec not found");
 }

 AVCodecContext* codec_ctx = avcodec_alloc_context3(codec_obj);
 if (!codec_ctx) {
 avformat_free_context(format_ctx);
 throw std::runtime_error("Could not allocate codec context");
 }

 codec_ctx->sample_rate = this->get_frame_rate();
 AVChannelLayout ch_layout_1;
 av_channel_layout_uninit(&ch_layout_1);
 av_channel_layout_default(&ch_layout_1, 2);
 codec_ctx->ch_layout = ch_layout_1; // Adjust based on your needs
 codec_ctx->bit_rate = std::stoi(bitrate);
 codec_ctx->sample_fmt = codec_obj->sample_fmts[0];

 if (format_ctx->oformat->flags & AVFMT_GLOBALHEADER) {
 codec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
 }

 ret = avcodec_open2(codec_ctx, codec_obj, nullptr);
 check_av_error(ret, "Could not open codec");

 stream->time_base = { 1, codec_ctx->sample_rate };
 ret = avcodec_parameters_from_context(stream->codecpar, codec_ctx);
 check_av_error(ret, "Could not set codec parameters");

 ret = avformat_write_header(format_ctx, nullptr);
 check_av_error(ret, "Error occurred when writing header");

 AVPacket pkt;
 av_init_packet(&pkt);
 pkt.data = nullptr;
 pkt.size = 0;

 int frame_size = av_samples_get_buffer_size(nullptr, codec_ctx->ch_layout.nb_channels,
 codec_ctx->frame_size, codec_ctx->sample_fmt, 0);
 check_av_error(frame_size, "Could not calculate frame size");

 AVFrame* frame = av_frame_alloc();
 if (!frame) {
 avcodec_free_context(&codec_ctx);
 avformat_free_context(format_ctx);
 throw std::runtime_error("Error allocating frame");
 }

 frame->format = codec_ctx->sample_fmt;
 frame->ch_layout = codec_ctx->ch_layout;
 frame->sample_rate = codec_ctx->sample_rate;
 frame->nb_samples = codec_ctx->frame_size;

 ret = av_frame_get_buffer(frame, 0);
 if (ret < 0) {
 av_frame_free(&frame);
 avcodec_free_context(&codec_ctx);
 avformat_free_context(format_ctx);
 throw std::runtime_error("Error allocating frame buffer: " + av_err2str_(ret));
 }

 size_t data_offset = 0;

 while (data_offset < this->raw_data().size()) {
 int samples_to_process = std::min(frame_size, static_cast<int>(this->raw_data().size()) - static_cast<int>(data_offset));

 // Fill the frame with audio data
 ret = avcodec_fill_audio_frame(frame, codec_ctx->ch_layout.nb_channels, codec_ctx->sample_fmt,
 reinterpret_cast<const>(this->raw_data().data()) + data_offset,
 samples_to_process, 0);
 if (ret < 0) {
 log_error("Error filling audio frame: " + av_err2str_(ret));
 av_frame_free(&frame);
 avcodec_free_context(&codec_ctx);
 avformat_free_context(format_ctx);
 throw std::runtime_error("Error filling audio frame: " + av_err2str_(ret));
 }

 data_offset += samples_to_process;

 ret = avcodec_send_frame(codec_ctx, frame);
 if (ret < 0) {
 log_error("Error sending frame for encoding: " + av_err2str_(ret));
 av_frame_free(&frame);
 avcodec_free_context(&codec_ctx);
 avformat_free_context(format_ctx);
 throw std::runtime_error("Error sending frame for encoding: " + av_err2str_(ret));
 }

 while (ret >= 0) {
 ret = avcodec_receive_packet(codec_ctx, &pkt);
 if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
 break;
 }
 check_av_error(ret, "Error receiving packet");

 pkt.stream_index = stream->index;

 ret = av_interleaved_write_frame(format_ctx, &pkt);
 check_av_error(ret, "Error writing encoded frame to output file");

 av_packet_unref(&pkt);
 }
 }

 // Flush the encoder
 avcodec_send_frame(codec_ctx, nullptr);
 while (avcodec_receive_packet(codec_ctx, &pkt) == 0) {
 pkt.stream_index = stream->index;
 av_interleaved_write_frame(format_ctx, &pkt);
 av_packet_unref(&pkt);
 }

 av_write_trailer(format_ctx);

 av_frame_free(&frame);
 avcodec_free_context(&codec_ctx);
 avformat_free_context(format_ctx);

 return std::ofstream(out_f, std::ios::binary);
}
</const></int></int>


The runtime error is :


Exception thrown at 0x00007FF945137C9B (avcodec-61.dll) in cppdub_test.exe : 0xC0000005 : Access violation reading location 0x0000024CBCD25080.


for line :


ret = avcodec_send_frame(codec_ctx, frame);



Call stack :


avcodec-61.dll!00007ff945137c9b() Unknown
 avcodec-61.dll!00007ff9451381bb() Unknown
 avcodec-61.dll!00007ff945139679() Unknown
 avcodec-61.dll!00007ff94371521d() Unknown
 avcodec-61.dll!00007ff9434a80c2() Unknown
 avcodec-61.dll!00007ff9434a84a6() Unknown
 avcodec-61.dll!00007ff9434a8749() Unknown
> cppdub_test.exe!cppdub::AudioSegment::export_segment(std::string & out_f, const std::string & format, const std::string & codec, const std::string & bitrate, const std::vector> & parameters, const std::map,std::allocator>> & tags, const std::string & id3v2_version, const std::string & cover) Line 572 C++
 cppdub_test.exe!main() Line 33 C++
 [External Code] 




Autos :


+ this 0x000000d3a08ff690 {data_="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0... ...} cppdub::AudioSegment *
+ bitrate "128000" const std::string &
+ ch_layout_1 {order=AV_CHANNEL_ORDER_NATIVE (1) nb_channels=2 u={mask=3 map=0x0000000000000003 {id=??? name=... opaque=...} } ...} AVChannelLayout
+ codec "libmp3lame" const std::string &
+ codec_ctx 0x0000024cbc78c240 {av_class=avcodec-61.dll!0x00007ff94789c760 {class_name=0x00007ff94789c740 "AVCodecContext" ...} ...} AVCodecContext *
+ codec_obj avcodec-61.dll!0x00007ff9477fa4c0 (load symbols for additional information) {name=0x00007ff9477fa47c "libmp3lame" ...} const AVCodec *
+ cover "" const std::string &
 data_offset 9216 unsigned __int64
+ format "mp3" const std::string &
+ format_ctx 0x0000024cbc788a40 {av_class=avformat-61.dll!0x00007ff99eb09fe0 {class_name=0x00007ff99eb09fc0 "AVFormatContext" ...} ...} AVFormatContext *
+ frame 0x0000024cbc787380 {data=0x0000024cbc787380 {0x0000024cbcd25080 <error reading="reading" characters="characters" of="of">, ...} ...} AVFrame *
 frame_size 9216 int
+ id3v2_version "4" const std::string &
+ out_f "ha-ha-ha.mp3" std::string &
+ parameters { size=0 } const std::vector> &
+ pkt {buf=0x0000000000000000 <null> pts=-9223372036854775808 dts=-9223372036854775808 ...} AVPacket
 ret 9216 int
 samples_to_process 9216 int
+ stream 0x0000024cbc789bc0 {av_class=avformat-61.dll!0x00007ff99eb09840 {class_name=0x00007ff99eb09820 "AVStream" ...} ...} AVStream *
+ tags { size=0 } const std::map,std::allocator>> &
</null></error>


-
How to optimize this code that gets video frame as image
14 août 2024, par TSRI am quite new to mp4 file. But here is my working attempt to extract image frame given video url and a timestamp.


In reality the input url is an 8K 10hours 200GB video, so I can't download it all, I can't load it to memory, and this is an API call so it has to be fast.


Is there anything else I can optimize ?


My doubts :


- 

-
This line
ffprobe -v error -select_streams v:0 -show_entries packet=pos,size,dts_time -read_intervals ${timestamp}%+5 -of csv=p=0 "${url}"
I chose this clingy 5s, in which case would this fail ?

-
Same line, I don't know what's going on under the hood of this
ffprobe
command, but I tested it with the big 8K video and it seems to complete fast. So is it safe to assume that the entire 200GB was not downloaded ? An explanation of how thisffprobe
command work would be appreciated

-
Based on trial and error, I concluded that the intervals returned is parsable by
ffmpeg
only if the first frame until the timestamp is included. If I include only that the single frame interval,ffmpeg
says it is an invalid file. (Makes sense cuz I don't think I'll get an image from a 4byte data.) However, how can I be sure that I am selecting the least number of intervals.

-
Worse bottleneck : The function
extractFrame
takes 6seconds on the big video. It seems to read the entire video segment fetched (by the preceding subrange step). I couldn't find a way to jump to the last frame without computing. Is this how MP4 work ? I read somewhere that MP4 computes the current frame based on the previous. If that is true, does it mean there is no way to compute a specific frame without reading everything since the last keyframe ?

-
Finally, this
ffmpeg
line is fishy (I got it from SO Extract final frame from a video using ffmpeg) it says that it ovewrites the output at everyframe . Does it mean it is writing to the disk every time ? I experience severe degradation in performance when I used.png
instead of.jpg
. This tells me that the image is computed every
frame. Can we compute only the final image at the very end ?













Here is the working code to optimize.


import path from "path";
import axios from "axios";
import ffmpeg from "fluent-ffmpeg";
import fs from "fs";
import {promisify} from 'util';
import {exec} from 'child_process';

const execPromise = promisify(exec);


// URL of the video and desired timestamp (in seconds)

const videoUrl = 'https://raw.githubusercontent.com/tolotrasamuel/assets/main/videoplayback.mp4';

console.log(videoUrl);
const timestamp = 30; // Example: 30 seconds into the video


// Function to get the byte range using ffprobe
const getByteRangeForTimestamp = async (url, timestamp) => {
 // Use ffprobe to get the offset and size of the frame at the given timestamp
 const command = `ffprobe -v error -select_streams v:0 -show_entries packet=pos,size,dts_time -read_intervals ${timestamp}%+5 -of csv=p=0 "${url}"`;
 console.log('Running command:', command);
 const {stdout} = await execPromise(command);


 // Parse the output
 const timeStamps = stdout.trim().split("\n");
 const frames = timeStamps.map(ts => {
 const [dts_time, size, offset] = ts.split(',');
 const timeInt = parseFloat(dts_time);
 const offsetInt = parseInt(offset);
 const sizeInt = parseInt(size);
 return {dts_time: timeInt, size: sizeInt, offset: offsetInt};
 })

 if (frames.length === 0) {
 throw new Error('No frames found in the specified interval');
 }

 let closest;


 let i = 0
 while (i < frames.length) {
 if (i === frames.length) {
 throw new Error('No frames found in the specified 5s interval');
 }
 if (frames[i].dts_time >= timestamp) {
 const oldDiff = Math.abs(closest.dts_time - timestamp);
 const newDiff = Math.abs(frames[i].dts_time - timestamp);
 if (newDiff < oldDiff) {
 closest = frames[i];
 }
 break;
 }
 closest = frames[i];
 i++;
 }

 // I experimented with this, but it seems that the first frame is always at the beginning of a valid atom
 // anything after that will make the video unplayable
 const startByte = frames[0].offset;
 const endByte = closest.offset + closest.size - 1;

 const diff = Math.abs(closest.dts_time - timestamp);
 const size = endByte - startByte + 1;
 console.log("Start: ", startByte, "End: ", endByte, "Diff: ", diff, "Timestamp: ", timestamp, "Closest: ", closest.dts_time, "Size to fetch: ", size)


 const startTime = closest.dts_time - frames[0].dts_time;
 return {startByte, endByte, startTime};
};

// Download the specific segment
const downloadSegment = async (url, startByte, endByte, outputPath) => {
 console.log(`Downloading bytes ${startByte}-${endByte}...`);
 const response = await axios.get(url, {
 responseType: 'arraybuffer',
 headers: {
 Range: `bytes=${startByte}-${endByte}`,
 },
 });

 console.log('Segment downloaded!', response.data.length, "Written to: ", outputPath);
 fs.writeFileSync(outputPath, response.data);
};

// Extract frame from the segment
const extractFrameRaw = async (videoPath, timestamp, outputFramePath) => {


 const command = `ffmpeg -sseof -3 -i ${videoPath} -update 1 -q:v 1 ${outputFramePath} -y`;
 console.log('Running command:', command);
 const startTime = new Date().getTime();
 await execPromise(command);
 const endTime = new Date().getTime();
 console.log('Processing time:', endTime - startTime, 'ms');
 console.log('Frame extracted to:', outputFramePath);
};
const extractFrame = (videoPath, timestamp, outputFramePath) => {
 ffmpeg(videoPath)
 .inputOptions(['-sseof -5']) // Seeks to 3 seconds before the end of the video
 .outputOptions([
 '-update 1', // Continuously update the output file with new frames
 '-q:v 1' // Set the highest JPEG quality
 ])
 .output(outputFramePath) // Set the output file path

 // log
 .on('start', function (commandLine) {
 console.log('Spawned Ffmpeg with command: ' + commandLine);
 })
 .on('progress', function (progress) {
 console.log('Processing: ' + progress.timemark + '% done', progress, 'frame: ', progress.frames);
 })
 .on('end', function () {
 console.log('Processing finished !');
 })
 .on('error', function (err, stdout, stderr) {
 console.error('Error:', err);
 console.error('ffmpeg stderr:', stderr);
 })
 .run();
};


const __dirname = path.resolve();

// Main function to orchestrate the process
(async () => {
 try {
 // ffmpeg.setFfmpegPath('/usr/local/bin/ffmpeg');
 const {startByte, endByte, startTime} = await getByteRangeForTimestamp(videoUrl, timestamp);
 const tmpVideoPath = path.resolve(__dirname, 'temp_video.mp4');
 const outputFramePath = path.resolve(__dirname, `frame_${timestamp}.jpg`);

 await downloadSegment(videoUrl, startByte, endByte, tmpVideoPath);
 await extractFrame(tmpVideoPath, startTime, outputFramePath);
 } catch (err) {
 console.error('Error:', err);
 }
})();



-