
Recherche avancée
Autres articles (53)
-
Gestion des droits de création et d’édition des objets
8 février 2011, parPar défaut, beaucoup de fonctionnalités sont limitées aux administrateurs mais restent configurables indépendamment pour modifier leur statut minimal d’utilisation notamment : la rédaction de contenus sur le site modifiables dans la gestion des templates de formulaires ; l’ajout de notes aux articles ; l’ajout de légendes et d’annotations sur les images ;
-
Les images
15 mai 2013 -
Supporting all media types
13 avril 2011, parUnlike most software and media-sharing platforms, MediaSPIP aims to manage as many different media types as possible. The following are just a few examples from an ever-expanding list of supported formats : images : png, gif, jpg, bmp and more audio : MP3, Ogg, Wav and more video : AVI, MP4, OGV, mpg, mov, wmv and more text, code and other data : OpenOffice, Microsoft Office (Word, PowerPoint, Excel), web (html, CSS), LaTeX, Google Earth and (...)
Sur d’autres sites (4868)
-
Trying to cancel execution and delete file using ffmpeg C API
6 mars 2020, par VuwoxThe code below is a class that handle the conversion of multiples images, through
add_frame()
method, into a GIF withencode()
. It also use a filter to generate and apply the palette. The usage is like this :Code call example
std::unique_ptr gif_obj = nullptr;
try
{
gif_obj = std::make_unique({1000,1000}, 12, "C:/out.gif",
"format=pix_fmts=rgb24,split [a][b];[a]palettegen[p];[b][p]paletteuse");
// Example: a simple vector of images (usually process internally)
for(auto img : image_vector)
gif_obj->add_frame(img);
// Once all frame were added, encode the final GIF with the filter applied.
gif_obj->encode();
}
catch(const std::exception& e)
{
// An error occured! We must close FFMPEG properly and delete the created file.
gif_obj->cancel();
}I have the following issue. If the code for any reason throw an exception, I call
ffmpeg->cancel()
and it supposes to delete the GIF file on disk. But this is never working, I assume there is a lock on the file or something like that. So here are my question :What is the proper way to close/free ffmpeg object in order to remove the file afterward ?
Full class code below
Header
// C++ Standard includes
#include <memory>
#include <string>
#include <vector>
// 3rd Party incldues
#ifdef __cplusplus
extern "C" {
#include "libavformat/avformat.h"
#include "libavfilter/avfilter.h"
#include "libavutil/opt.h"
#include "libavfilter/buffersrc.h"
#include "libavfilter/buffersink.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
}
#endif
#define FFMPEG_MSG_LEN 2000
namespace px
{
namespace GIF
{
class FFMPEG
{
public:
FFMPEG(const px::Point2D<int>& dim,
const int framerate,
const std::string& filename,
const std::string& filter_cmd);
~FFMPEG();
void add_frame(pxImage * const img);
void encode();
void cancel();
private:
void init_filters(); // Init everything that needed to filter the input frame.
void init_muxer(); // The muxer that creates the output file.
void muxing_one_frame(AVFrame* frame);
void release();
int _ret = 0; // status code from FFMPEG.
char _err_msg[FFMPEG_MSG_LEN]; // Error message buffer.
int m_width = 0; // The width that all futur images must have to be accepted.
int m_height = 0; // The height that all futur images must have to be accepted.
int m_framerate = 0; // GIF Framerate.
std::string m_filename = ""; // The GIF filename (on cache?)
std::string m_filter_desc = ""; // The FFMPEG filter to apply over the frames.
bool as_frame = false;
AVFrame* picture_rgb24 = nullptr; // Temporary frame that will hold the pxImage in an RGB24 format (NOTE: TOP-LEFT origin)
AVFormatContext* ofmt_ctx = nullptr; // ouput format context associated to the
AVCodecContext* o_codec_ctx = nullptr; // output codec for the GIF
AVFilterGraph* filter_graph = nullptr; // filter graph associate with the string we want to execute
AVFilterContext* buffersrc_ctx = nullptr; // The buffer that will store all the frames in one place for the palette generation.
AVFilterContext* buffersink_ctx = nullptr; // The buffer that will store the result afterward (once the palette are used).
int64_t m_pts_increment = 0;
};
};
};
</int></vector></string></memory>ctor
px::GIF::FFMPEG::FFMPEG(const px::Point2D<int>& dim,
const int framerate,
const std::string& filename,
const std::string& filter_cmd) :
m_width(dim.x()),
m_height(dim.y()),
m_framerate(framerate),
m_filename(filename),
m_filter_desc(filter_cmd)
{
#if !_DEBUG
av_log_set_level(AV_LOG_QUIET); // Set the FFMPEG log to quiet to avoid too much logs.
#endif
// Allocate the temporary buffer that hold the ffmpeg image (pxImage to AVFrame conversion).
picture_rgb24 = av_frame_alloc();
picture_rgb24->pts = 0;
picture_rgb24->data[0] = NULL;
picture_rgb24->linesize[0] = -1;
picture_rgb24->format = AV_PIX_FMT_RGB24;
picture_rgb24->height = m_height;
picture_rgb24->width = m_width;
if ((_ret = av_image_alloc(picture_rgb24->data, picture_rgb24->linesize, m_width, m_height, (AVPixelFormat)picture_rgb24->format, 24)) < 0)
throw px::GIF::Error("Failed to allocate the AVFrame for pxImage conversion with error: " +
std::string(av_make_error_string(_err_msg, FFMPEG_MSG_LEN, _ret)),
"GIF::FFMPEG CTOR");
//printf("allocated picture of size %d, linesize %d %d %d %d\n", _ret, picture_rgb24->linesize[0], picture_rgb24->linesize[1], picture_rgb24->linesize[2], picture_rgb24->linesize[3]);
init_muxer(); // Prepare the GIF encoder (open it on disk).
init_filters(); // Prepare the filter that will be applied over the frame.
// Instead of hardcoder {1,100} which is the GIF tbn, we collect it from its stream.
// This will avoid future problem if the codec change in ffmpeg.
if (ofmt_ctx && ofmt_ctx->nb_streams > 0)
m_pts_increment = av_rescale_q(1, { 1, m_framerate }, ofmt_ctx->streams[0]->time_base);
else
m_pts_increment = av_rescale_q(1, { 1, m_framerate }, { 1, 100 });
}
</int>FFMPEG Initialization (Filter and muxer)
void px::GIF::FFMPEG::init_filters()
{
const AVFilter* buffersrc = avfilter_get_by_name("buffer");
const AVFilter* buffersink = avfilter_get_by_name("buffersink");
AVRational time_base = { 1, m_framerate };
AVRational aspect_pixel = { 1, 1 };
AVFilterInOut* inputs = avfilter_inout_alloc();
AVFilterInOut* outputs = avfilter_inout_alloc();
filter_graph = avfilter_graph_alloc();
try
{
if (!outputs || !inputs || !filter_graph)
throw px::GIF::Error("Failed to 'init_filters' could not allocated the graph/filters.", "GIF::FFMPEG init_filters");
char args[512];
snprintf(args, sizeof(args),
"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
m_width, m_height,
picture_rgb24->format,
time_base.num, time_base.den,
aspect_pixel.num, aspect_pixel.den);
if (avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in", args, nullptr, filter_graph) < 0)
throw px::GIF::Error("Failed to create the 'source buffer' in init_filer method.", "GIF::FFMPEG init_filters");
if (avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out", nullptr, nullptr, filter_graph) < 0)
throw px::GIF::Error("Failed to create the 'sink buffer' in init_filer method.", "GIF::FFMPEG init_filters");
// GIF has possible output of PAL8.
enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_PAL8, AV_PIX_FMT_NONE };
if (av_opt_set_int_list(buffersink_ctx, "pix_fmts", pix_fmts, AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN) < 0)
throw px::GIF::Error("Failed to set the output pixel format.", "GIF::FFMPEG init_filters");
outputs->name = av_strdup("in");
outputs->filter_ctx = buffersrc_ctx;
outputs->pad_idx = 0;
outputs->next = nullptr;
inputs->name = av_strdup("out");
inputs->filter_ctx = buffersink_ctx;
inputs->pad_idx = 0;
inputs->next = nullptr;
// GIF has possible output of PAL8.
if (avfilter_graph_parse_ptr(filter_graph, m_filter_desc.c_str(), &inputs, &outputs, nullptr) < 0)
throw px::GIF::Error("Failed to parse the filter graph (bad string!).", "GIF::FFMPEG init_filters");
if (avfilter_graph_config(filter_graph, nullptr) < 0)
throw px::GIF::Error("Failed to configure the filter graph (bad string!).", "GIF::FFMPEG init_filters");
avfilter_inout_free(&inputs);
avfilter_inout_free(&outputs);
}
catch (const std::exception& e)
{
// Catch exception to delete element.
avfilter_inout_free(&inputs);
avfilter_inout_free(&outputs);
throw e; // re-throuw
}
}
void px::GIF::FFMPEG::init_muxer()
{
AVOutputFormat* o_fmt = av_guess_format("gif", m_filename.c_str(), "video/gif");
if ((_ret = avformat_alloc_output_context2(&ofmt_ctx, o_fmt, "gif", m_filename.c_str())) < 0)
throw px::GIF::Error(std::string(av_make_error_string(_err_msg, FFMPEG_MSG_LEN, _ret)) + " allocate output format.", "GIF::FFMPEG init_muxer");
AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_GIF);
if (!codec) throw px::GIF::Error("Could to find the 'GIF' codec.", "GIF::FFMPEG init_muxer");
#if 0
const AVPixelFormat* p = codec->pix_fmts;
while (p != NULL && *p != AV_PIX_FMT_NONE) {
printf("supported pix fmt: %s\n", av_get_pix_fmt_name(*p));
++p;
}
#endif
AVStream* stream = avformat_new_stream(ofmt_ctx, codec);
AVCodecParameters* codec_paramters = stream->codecpar;
codec_paramters->codec_tag = 0;
codec_paramters->codec_id = codec->id;
codec_paramters->codec_type = AVMEDIA_TYPE_VIDEO;
codec_paramters->width = m_width;
codec_paramters->height = m_height;
codec_paramters->format = AV_PIX_FMT_PAL8;
o_codec_ctx = avcodec_alloc_context3(codec);
avcodec_parameters_to_context(o_codec_ctx, codec_paramters);
o_codec_ctx->time_base = { 1, m_framerate };
if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
o_codec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
if ((_ret = avcodec_open2(o_codec_ctx, codec, NULL)) < 0)
throw px::GIF::Error(std::string(av_make_error_string(_err_msg, FFMPEG_MSG_LEN, _ret)) + " open output codec.", "GIF::FFMPEG init_muxer");
if ((_ret = avio_open(&ofmt_ctx->pb, m_filename.c_str(), AVIO_FLAG_WRITE)) < 0)
throw px::GIF::Error(std::string(av_make_error_string(_err_msg, FFMPEG_MSG_LEN, _ret)) + " avio open error.", "GIF::FFMPEG init_muxer");
if ((_ret = avformat_write_header(ofmt_ctx, NULL)) < 0)
throw px::GIF::Error(std::string(av_make_error_string(_err_msg, FFMPEG_MSG_LEN, _ret)) + " write GIF header", "GIF::FFMPEG init_muxer");
#if _DEBUG
// This print the stream/output format.
av_dump_format(ofmt_ctx, -1, m_filename.c_str(), 1);
#endif
}Add frame (usually in a loop)
void px::GIF::FFMPEG::add_frame(pxImage * const img)
{
if (img->getImageType() != PXT_BYTE || img->getNChannels() != 4)
throw px::GIF::Error("Failed to 'add_frame' since image is not PXT_BYTE and 4-channels.", "GIF::FFMPEG add_frame");
if (img->getWidth() != m_width || img->getHeight() != m_height)
throw px::GIF::Error("Failed to 'add_frame' since the size is not same to other inputs.", "GIF::FFMPEG add_frame");
const int pitch = picture_rgb24->linesize[0];
auto px_ptr = getImageAccessor(img);
for (int y = 0; y < m_height; y++)
{
const int px_row = img->getOrigin() == ORIGIN_BOT_LEFT ? m_height - y - 1 : y;
for (int x = 0; x < m_width; x++)
{
const int idx = y * pitch + 3 * x;
picture_rgb24->data[0][idx] = px_ptr[px_row][x].ch[PX_RE];
picture_rgb24->data[0][idx + 1] = px_ptr[px_row][x].ch[PX_GR];
picture_rgb24->data[0][idx + 2] = px_ptr[px_row][x].ch[PX_BL];
}
}
// palettegen need a whole stream, just add frame to buffer.
if ((_ret = av_buffersrc_add_frame_flags(buffersrc_ctx, picture_rgb24, AV_BUFFERSRC_FLAG_KEEP_REF)) < 0)
throw px::GIF::Error("Failed to 'add_frame' to global buffer with error: " +
std::string(av_make_error_string(_err_msg, FFMPEG_MSG_LEN, _ret)),
"GIF::FFMPEG add_frame");
// Increment the FPS of the picture for the next add-up to the buffer.
picture_rgb24->pts += m_pts_increment;
as_frame = true;
}Encoder (final step)
void px::GIF::FFMPEG::encode()
{
if (!as_frame)
throw px::GIF::Error("Please 'add_frame' before running the Encoding().", "GIF::FFMPEG encode");
// end of buffer
if ((_ret = av_buffersrc_add_frame_flags(buffersrc_ctx, nullptr, AV_BUFFERSRC_FLAG_KEEP_REF)) < 0)
throw px::GIF::Error("error add frame to buffer source: " + std::string(av_make_error_string(_err_msg, FFMPEG_MSG_LEN, _ret)), "GIF::FFMPEG encode");
do {
AVFrame* filter_frame = av_frame_alloc();
_ret = av_buffersink_get_frame(buffersink_ctx, filter_frame);
if (_ret == AVERROR(EAGAIN) || _ret == AVERROR_EOF) {
av_frame_unref(filter_frame);
break;
}
// write the filter frame to output file
muxing_one_frame(filter_frame);
av_frame_unref(filter_frame);
} while (_ret >= 0);
av_write_trailer(ofmt_ctx);
}
void px::GIF::FFMPEG::muxing_one_frame(AVFrame* frame)
{
int ret = avcodec_send_frame(o_codec_ctx, frame);
AVPacket *pkt = av_packet_alloc();
av_init_packet(pkt);
while (ret >= 0) {
ret = avcodec_receive_packet(o_codec_ctx, pkt);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
}
av_write_frame(ofmt_ctx, pkt);
}
av_packet_unref(pkt);
}DTOR, Release and Cancel
px::GIF::FFMPEG::~FFMPEG()
{
release();
}
void px::GIF::FFMPEG::release()
{
// Muxer stuffs
if (ofmt_ctx != nullptr) avformat_free_context(ofmt_ctx);
if (o_codec_ctx != nullptr) avcodec_close(o_codec_ctx);
if (o_codec_ctx != nullptr) avcodec_free_context(&o_codec_ctx);
ofmt_ctx = nullptr;
o_codec_ctx = nullptr;
// Filter stuffs
if (buffersrc_ctx != nullptr) avfilter_free(buffersrc_ctx);
if (buffersink_ctx != nullptr) avfilter_free(buffersink_ctx);
if (filter_graph != nullptr) avfilter_graph_free(&filter_graph);
buffersrc_ctx = nullptr;
buffersink_ctx = nullptr;
filter_graph = nullptr;
// Conversion image.
if (picture_rgb24 != nullptr) av_frame_free(&picture_rgb24);
picture_rgb24 = nullptr;
}
void px::GIF::FFMPEG::cancel()
{
// In-case of failure we must close ffmpeg and exit.
av_write_trailer(ofmt_ctx);
// Release and close all elements.
release();
// Delete the file on disk.
if (remove(m_filename.c_str()) != 0)
PX_LOG0(PX_LOGLEVEL_ERROR, "GIF::FFMPEG - On 'cancel' failed to remove the file.");
} -
record rtsp stream to file(muxing)
11 avril 2014, par user3521863AVFormatContext *g_oc = NULL;
AVStream *g_in_audio_st, *g_in_video_st;
AVStream *g_out_audio_st, *g_out_video_st;
int audio_pts = 0, video_pts = 0, audio_dts = 0, video_dts = 0;
int last_video_pts = 0;
AVPacket outpkt, *av_pkt;
// initialize video codec
static void init_video_codec(AVFormatContext *context) {
LOGI(1, "enter init_video_codec");
AVFormatContext *in_format_ctx = NULL;
AVCodecContext *avcodec_ctx = NULL;
int fps = 0;
if(context->streams[1]->r_frame_rate.num != AV_NOPTS_VALUE &&
context->streams[1]->r_frame_rate.den != 0)
fps = context->streams[1]->r_frame_rate.num / context->streams[1]->r_frame_rate.den;
else
fps = 25;
g_out_video_st = avformat_new_stream(g_oc, context->streams[1]->codec->codec);
LOGI(1, "video avformat_new_stream");
if( g_out_video_st == NULL ) {
LOGE(1, "Fail to Allocate Output Video Stream");
return ;
}
else {
LOGI(1, "Allocated Video Stream");
if( avcodec_copy_context(g_out_video_st->codec, context->streams[1]->codec) != 0 ) {
LOGE(1, "Failed to video Copy Context");
return ;
}
else {
LOGI(1, "Success to video Copy Context");
// how to setting video stream parameter?
g_out_video_st->sample_aspect_ratio.den = g_in_video_st->codec->sample_aspect_ratio.den;
g_out_video_st->sample_aspect_ratio.num = g_in_video_st->codec->sample_aspect_ratio.num;
g_out_video_st->codec->codec_id = g_in_video_st->codec->codec->id;
g_out_video_st->codec->time_base.num = 1;
g_out_video_st->codec->time_base.den = fps * (g_in_video_st->codec->ticks_per_frame);
g_out_video_st->time_base.num = 1;
g_out_video_st->time_base.den = 1000;
g_out_video_st->r_frame_rate.num = fps;
g_out_video_st->r_frame_rate.den = 1;
g_out_video_st->avg_frame_rate.den = 1;
g_out_video_st->avg_frame_rate.num = fps;
g_out_video_st->codec->width = g_frame_width;
g_out_video_st->codec->height = g_frame_height;
g_out_video_st->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
}
}
LOGI(1, "end video init");
}
// initialize audio codec
static void init_audio_codec(AVFormatContext *context) {
LOGI(1, "enter init_audio_codec");
AVFormatContext *in_format_ctx = NULL;
AVCodecContext *avcodec_ctx = NULL;
g_out_audio_st = avformat_new_stream(g_oc, context->streams[0]->codec->codec);
LOGI(1, "audio avformat_new_stream");
if( avcodec_copy_context(g_out_audio_st->codec, context->streams[0]->codec) != 0 ) {
LOGE(1, "Failed to Copy audio Context");
return ;
}
else {
LOGI(1, "Success to Copy audio Context");
// how to setting video stream parameter?
g_out_audio_st->codec->codec_id = g_in_audio_st->codec->codec_id;
g_out_audio_st->codec->codec_tag = 0;
g_out_audio_st->pts = g_in_audio_st->pts;
g_out_audio_st->time_base.num = g_in_audio_st->time_base.num;
g_out_audio_st->time_base.den = g_in_audio_st->time_base.den;
g_out_audio_st->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
}
LOGI(1, "end init audio");
}
// write video stream
static void write_video_stream(AVPacket *pkt) {
av_pkt = NULL;
av_pkt = pkt;
if( pkt == NULL || sizeof(*pkt) == 0 )
return;
av_rescale_q(av_pkt->pts, g_in_video_st->time_base, g_in_video_st->codec->time_base);
av_rescale_q(av_pkt->dts, g_in_video_st->time_base, g_in_video_st->codec->time_base);
av_init_packet(&outpkt);
if( av_pkt->pts != AV_NOPTS_VALUE ) {
if( last_video_pts == video_pts ) {
video_pts++;
last_video_pts = video_pts;
}
outpkt.pts = video_pts;
}
else {
outpkt.pts = AV_NOPTS_VALUE;
}
if( av_pkt->dts == AV_NOPTS_VALUE )
outpkt.dts = AV_NOPTS_VALUE;
else
outpkt.dts = video_pts;
outpkt.data = av_pkt->data;
outpkt.size = av_pkt->size;
outpkt.stream_index = av_pkt->stream_index;
outpkt.flags |= AV_PKT_FLAG_KEY;
last_video_pts = video_pts;
if(av_interleaved_write_frame(g_oc, &outpkt) < 0) {
// if(av_write_frame(g_oc, &outpkt) < 0) {
LOGE(1, "Failed Video Write");
}
else {
g_out_video_st->codec->frame_number++;
}
if( !&outpkt || sizeof(outpkt) == 0 )
return;
if( !av_pkt || sizeof(*av_pkt) == 0 )
return;
av_free_packet(&outpkt);
}
// write audio stream
static void write_audio_stream(AVPacket *pkt) {
av_pkt = NULL;
av_pkt = pkt;
if( pkt == NULL || sizeof(*pkt) == 0 )
return;
av_rescale_q(av_pkt->pts, g_in_audio_st->time_base, g_in_audio_st->codec->time_base);
av_rescale_q(av_pkt->dts, g_in_audio_st->time_base, g_in_audio_st->codec->time_base);
av_init_packet(&outpkt);
if(av_pkt->pts != AV_NOPTS_VALUE)
outpkt.pts = audio_pts;
else
outpkt.pts = AV_NOPTS_VALUE;
if(av_pkt->dts == AV_NOPTS_VALUE)
outpkt.dts = AV_NOPTS_VALUE;
else {
outpkt.dts = audio_pts;
if( outpkt.pts >= outpkt.dts)
outpkt.dts = outpkt.pts;
if(outpkt.dts == audio_dts)
outpkt.dts++;
if(outpkt.pts < outpkt.dts) {
outpkt.pts = outpkt.dts;
audio_pts = outpkt.pts;
}
outpkt.data = av_pkt->data;
outpkt.size = av_pkt->size;
outpkt.stream_index = av_pkt->stream_index;
outpkt.flags |= AV_PKT_FLAG_KEY;
video_pts = audio_pts;
audio_pts++;
if( av_interleaved_write_frame(g_oc, &outpkt) < 0 ) {
// if( av_write_frame(g_oc, &outpkt) < 0 ) {
LOGE(1, "Failed Audio Write");
}
else {
g_out_audio_st->codec->frame_number++;
}
if( !&outpkt || sizeof(outpkt) == 0 )
return;
if( !av_pkt || sizeof(*av_pkt) == 0 )
return;
av_free_packet(&outpkt);
}
}here result : recorded file
here full source : player.cI want to record rtsp stream to file on playing
i try tested video and audio streams while changing the parameters
but this result file does not match sync between video and audio
i try search about ffmpeg but almost command run or video recording was only.
please advice me. -
FFmpeg -segment with a lot of time codes
24 février 2020, par GrenightI’m making program that splits videos for running parallel encodes.
My problem is that with putting all segments into command like i can hit a limit of maximum characters in windows console. Approximately 600, while 1-2 hour movie can easily be 1000-2000 scenes.
Is there a way to use .csv file for segment so i don’t need to put all time codes to command line ? Or is there another ways ?
Here is example of command line that is used in program :
timecodes is where all time codes passed.cmd = f'{self.FFMPEG} -i {video} -map_metadata 0 -an -f segment -segment_times {timecodes} -c copy -avoid_negative_ts 1 {self.temp_dir / "split" / "%04d.mkv"}