2015-08-06 2 views
0

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.

Répondre

0

Je pense que vous pourriez avoir vos paramètres mélangés. According to what I'm reading from the documentation prototype de avcodec_decode_video2 ressemble:

int avcodec_decode_video2 (AVCodecContext * avctx, 
          AVFrame * picture, 
          int * got_picture_ptr, 
          const AVPacket * avpkt) 

Et est appelée avec:

ret = avcodec_encode_video2(oFmtCtx->streams[stream_index]->codec, // Dunno. 
          &enc_pkt, //AVPacket * should be AVFrame * 
          frame, //AVFrame * Should be int * 
          got_frame); // int * should be AVPacket * 
+0

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'. –

+0

@ SebastiánArriagada Fils d'une arme à feu. Je mérite un bombardement de torpille rep pour cela. – user4581301