Ceci est ma première question, donc j'espère que je l'ai fait correctement. Si non, s'il vous plaît laissez-moi savoir pour le réparer. J'essaie de convertir un fichier vidéo mp4 court (10 secondes) en un gif en utilisant des bibliothèques ffmpeg (je suis assez nouveau en utilisant ffmpeg). Le programme fonctionne plutôt bien en gif, mais parfois il se plante de manière aléatoire.Erreur de segmentation aléatoire avec avcodec_encode_video2()
Ceci est la version des bibliothèques ffmpeg j'utilise:
libavutil 54. 27.100
libavcodec 56. 41.100
libavformat 56. 36.100
libavdevice 56. 4.100
libavfilter 5. 16.101
libavresample 2. 1. 0
libswscale 3. 1.101
libswresample 1. 2.100
libpostproc 53. 3.100
J'utilise une vidéo de 1920x1080p, ainsi afin de générer le gif que je fais un format de pixel convertion, de AV_PIX_FMT_YUV420P
à AV_PIX_FMT_RGB8
avec un redimensionnement de la résolution initiale à 432x240p.
Voici le code:
int VideoManager::loadVideo(QString filename, bool showInfo)
{
if(avformat_open_input(&iFmtCtx, filename.toStdString().c_str(), 0, 0) < 0)
{
qDebug() << "Could not open input file " << filename;
closeInput();
return -1;
}
if (avformat_find_stream_info(iFmtCtx, 0) < 0)
{
qDebug() << "Failed to retrieve input stream information";
closeInput();
return -2;
}
videoStreamIndex = -1;
for(unsigned int i = 0; i < iFmtCtx->nb_streams; ++i)
if(iFmtCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
videoStreamIndex = i;
break;
}
if(videoStreamIndex == -1)
{
qDebug() << "Didn't find any video stream!";
closeInput();
return -3;
}
iCodecCtx = iFmtCtx->streams[videoStreamIndex]->codec;
iCodec = avcodec_find_decoder(iCodecCtx->codec_id);
if(iCodec == NULL) // Codec not found
{
qDebug() << "Codec not found!";
closeInput();
return -4;
}
if(avcodec_open2(iCodecCtx, iCodec, NULL) < 0)
{
qDebug() << "Could not open codec!";
closeInput();
return -1;
}
if(showInfo)
av_dump_format(iFmtCtx, 0, filename.toStdString().c_str(), 0);
return 0;
}
void VideoManager::generateGif(QString filename)
{
int ret, frameCount = 0;
AVPacket packet;
packet.data = NULL;
packet.size = 0;
AVFrame *frame = NULL;
unsigned int stream_index;
int got_frame;
gifHeight = iFmtCtx->streams[videoStreamIndex]->codec->height;
gifWidth = iFmtCtx->streams[videoStreamIndex]->codec->width;
if(gifHeight > MAX_GIF_HEIGHT || gifWidth > MAX_GIF_WIDTH)
{
if(gifHeight > gifWidth)
{
gifWidth = (float)gifWidth * ((float)MAX_GIF_HEIGHT/(float)gifHeight);
gifHeight = MAX_GIF_HEIGHT;
}
else
{
gifHeight = (float)gifHeight * ((float)MAX_GIF_WIDTH/(float)gifWidth);
gifWidth = MAX_GIF_WIDTH;
}
}
if(openOutputFile(filename.toStdString().c_str()) < 0)
{
qDebug() << "Error openning output file: " << filename;
return;
}
while (1) {
int ret = av_read_frame(iFmtCtx, &packet);
if (ret < 0)
{
if(ret != AVERROR_EOF)
qDebug() << "Error reading frame: " << ret;
break;
}
stream_index = packet.stream_index;
if(stream_index == videoStreamIndex)
{
frame = av_frame_alloc();
if (!frame) {
qDebug() << "Error allocating frame";
break;
}
av_packet_rescale_ts(&packet,
iFmtCtx->streams[stream_index]->time_base,
iFmtCtx->streams[stream_index]->codec->time_base);
ret = avcodec_decode_video2(iFmtCtx->streams[stream_index]->codec, frame,
&got_frame, &packet);
if (ret < 0) {
qDebug() << "Decoding failed";
break;
}
if(got_frame)
{
qDebug() << ++frameCount;
nframes++;
frame->pts = av_frame_get_best_effort_timestamp(frame);
////////////////////////////////////////////////////////////////////////////////
/// Pixel format convertion and resize
////////////////////////////////////////////////////////////////////////////////
uint8_t *out_buffer = NULL;
SwsContext *img_convert_ctx = NULL;
AVFrame *pFrameRGB = av_frame_alloc();
if(pFrameRGB == NULL)
{
qDebug() << "Error allocating frameRGB";
break;
}
AVPixelFormat pixFmt;
switch (iFmtCtx->streams[stream_index]->codec->pix_fmt)
{
case AV_PIX_FMT_YUVJ420P : pixFmt = AV_PIX_FMT_YUV420P; break;
case AV_PIX_FMT_YUVJ422P : pixFmt = AV_PIX_FMT_YUV422P; break;
case AV_PIX_FMT_YUVJ444P : pixFmt = AV_PIX_FMT_YUV444P; break;
case AV_PIX_FMT_YUVJ440P : pixFmt = AV_PIX_FMT_YUV440P; break;
default:
pixFmt = iFmtCtx->streams[stream_index]->codec->pix_fmt;
}
out_buffer = (uint8_t*)av_malloc(avpicture_get_size(AV_PIX_FMT_RGB8,
gifWidth,
gifHeight));
if(!out_buffer)
{
qDebug() << "Error alocatting out_buffer!";
}
avpicture_fill((AVPicture *)pFrameRGB, out_buffer, AV_PIX_FMT_RGB8,
gifWidth,
gifHeight);
img_convert_ctx = sws_getContext(iFmtCtx->streams[stream_index]->codec->width,
iFmtCtx->streams[stream_index]->codec->height,
pixFmt,
gifWidth,
gifHeight,
AV_PIX_FMT_RGB8,
SWS_ERROR_DIFFUSION, NULL, NULL, NULL);
if(!img_convert_ctx)
{
qDebug() << "error getting sws context";
}
sws_scale(img_convert_ctx, (const uint8_t* const*)frame->data,
frame->linesize, 0,
iFmtCtx->streams[stream_index]->codec->height,
pFrameRGB->data,
pFrameRGB->linesize);
pFrameRGB->format = AV_PIX_FMT_RGB8;
pFrameRGB->pts = frame->pts;
pFrameRGB->best_effort_timestamp = frame->best_effort_timestamp;
pFrameRGB->width = gifWidth;
pFrameRGB->height = gifHeight;
pFrameRGB->pkt_dts = frame->pkt_dts;
pFrameRGB->pkt_pts = frame->pkt_pts;
pFrameRGB->pkt_duration = frame->pkt_duration;
pFrameRGB->pkt_pos = frame->pkt_pos;
pFrameRGB->pkt_size = frame->pkt_size;
pFrameRGB->interlaced_frame = frame->interlaced_frame;
////////////////////////////////////////////////////////////////////////////////
ret = encodeAndWriteFrame(pFrameRGB, stream_index, NULL);
//av_frame_free(&frame);
//av_free(out_buffer);
//sws_freeContext(img_convert_ctx);
if (ret < 0)
{
qDebug() << "Error encoding and writting frame";
//av_free_packet(&packet);
closeOutput();
}
}
else {
//av_frame_free(&frame);
}
}
av_free_packet(&packet);
}
ret = flushEncoder(videoStreamIndex);
if (ret < 0)
{
qDebug() << "Flushing encoder failed";
}
av_write_trailer(oFmtCtx);
//av_free_packet(&packet);
//av_frame_free(&frame);
closeOutput();
}
void VideoManager::closeOutput()
{
if (oFmtCtx && oFmtCtx->nb_streams > 0 && oFmtCtx->streams[0] && oFmtCtx->streams[0]->codec)
avcodec_close(oFmtCtx->streams[0]->codec);
if (oFmtCtx && oFmt && !(oFmt->flags & AVFMT_NOFILE))
avio_closep(&oFmtCtx->pb);
avformat_free_context(oFmtCtx);
}
int VideoManager::openOutputFile(const char *filename)
{
AVStream *out_stream;
AVStream *in_stream;
AVCodecContext *dec_ctx, *enc_ctx;
AVCodec *encoder;
int ret;
oFmtCtx = NULL;
avformat_alloc_output_context2(&oFmtCtx, NULL, NULL, filename);
if (!oFmtCtx) {
qDebug() << "Could not create output context";
return AVERROR_UNKNOWN;
}
oFmt = oFmtCtx->oformat;
out_stream = avformat_new_stream(oFmtCtx, NULL);
if (!out_stream) {
qDebug() << "Failed allocating output stream";
return AVERROR_UNKNOWN;
}
in_stream = iFmtCtx->streams[videoStreamIndex];
dec_ctx = in_stream->codec;
enc_ctx = out_stream->codec;
encoder = avcodec_find_encoder(AV_CODEC_ID_GIF);
if (!encoder) {
qDebug() << "FATAL!: Necessary encoder not found";
return AVERROR_INVALIDDATA;
}
enc_ctx->height = gifHeight;
enc_ctx->width = gifWidth;
enc_ctx->sample_aspect_ratio = dec_ctx->sample_aspect_ratio;
enc_ctx->pix_fmt = AV_PIX_FMT_RGB8;
enc_ctx->time_base = dec_ctx->time_base;
ret = avcodec_open2(enc_ctx, encoder, NULL);
if (ret < 0) {
qDebug() << "Cannot open video encoder for gif";
return ret;
}
if (oFmt->flags & AVFMT_GLOBALHEADER)
enc_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER;
if (!(oFmt->flags & AVFMT_NOFILE)) {
ret = avio_open(&oFmtCtx->pb, filename, AVIO_FLAG_WRITE);
if (ret < 0) {
qDebug() << "Could not open output file " << filename;
return ret;
}
}
ret = avformat_write_header(oFmtCtx, NULL);
if (ret < 0) {
qDebug() << "Error occurred when opening output file";
return ret;
}
return 0;
}
int VideoManager::encodeAndWriteFrame(AVFrame *frame, unsigned int stream_index, int *got_frame) {
int ret;
int got_frame_local;
AVPacket enc_pkt;
if (!got_frame)
got_frame = &got_frame_local;
enc_pkt.data = NULL;
enc_pkt.size = 0;
av_init_packet(&enc_pkt);
ret = avcodec_encode_video2(oFmtCtx->streams[stream_index]->codec, &enc_pkt,
frame, got_frame);
//av_frame_free(&frame);
if (ret < 0)
return ret;
if (!(*got_frame))
return 0;
enc_pkt.stream_index = stream_index;
av_packet_rescale_ts(&enc_pkt,
oFmtCtx->streams[stream_index]->codec->time_base,
oFmtCtx->streams[stream_index]->time_base);
ret = av_interleaved_write_frame(oFmtCtx, &enc_pkt);
return ret;
}
int VideoManager::flushEncoder(unsigned int stream_index)
{
int ret;
int got_frame;
if (!(oFmtCtx->streams[stream_index]->codec->codec->capabilities &
CODEC_CAP_DELAY))
return 0;
while (1) {
ret = encodeAndWriteFrame(NULL, stream_index, &got_frame);
if (ret < 0)
break;
if (!got_frame)
return 0;
}
return ret;
}
Je sais qu'il ya beaucoup de fuites de mémoire. J'ai supprimé/commenté la plupart des fonctions libres intentionnalité parce que je pensais que c'était le problème.
J'utilise QtCreator, donc quand je déboguer les programmes c'est la sortie:
Level Function Line
0 av_image_copy 303
1 frame_copy_video 650
2 av_frame_copy 687
3 av_frame_ref 384
4 gif_encode_frame 307
5 avcodec_encode_video2 2191
6 VideoManager::encodeAndWriteFrame 813
7 VideoManager::generateGif 375
8 qMain 31
9 WinMain*16 112
10 main
J'ai vérifié s'il y a un cadre spécifique du crash du programme à, mais il est un cadre aléatoire aussi.
Une idée de ce que je fais mal? Toute aide serait très appréciée.
EDIT:
Après quelques jours de la douleur, la souffrance et je frustation décidé d'écrire tout le code à partir de zéro. Les deux fois j'ai commencé à partir de l'exemple this et l'ai modifié pour fonctionner comme je l'ai décrit précédemment. Et cela fonctionne parfaitement maintenant: D! La seule erreur que j'ai pu trouver dans l'ancien code (posté avant) est quand j'essaie d'accéder au flux vidéo dans le fichier de sortie que j'ai utilisé videoStreamIndex
, mais cet index provient du flux vidéo dans le fichier d'entrée. Parfois, il peut s'agir du même index et parfois non. Mais cela n'explique pas pourquoi il s'est écrasé au hasard. Si c'était la raison de l'accident, il devrait planter chaque fois que j'ai couru le code avec la même vidéo. Donc, probablement, il y a plus d'erreurs dans ce code. Notez que je n'ai pas testé si la correction de cette erreur dans le code ci-dessus résout réellement les problèmes de plantage.
C'est la documentation de la fonction de décodage et que l'appel est pour la fonction de codage. [Ici] (http://ffmpeg.org/doxygen/trunk/group__lavc__encoding.html#gaa2dc9e9ea2567ebb2801a08153c7306b) est la documentation de 'avcodec_encode_video2'. –
@ SebastiánArriagada Fils d'une arme à feu. Je mérite un bombardement de torpille rep pour cela. – user4581301