如何从视频中间保存图像?

时间:2017-10-28 19:53:54

标签: ffmpeg

我需要为视频制作缩略图,以寻找25%的视频并保存图像。这就是我现在正在做的事情,但它只能保存黑色图像。

#include <stdio.h>

#include <libavformat/avformat.h>
#include <libavutil/dict.h>

int main (int argc, char **argv)
{

    av_register_all();

    AVFormatContext *pFormatCtx = avformat_alloc_context();

    int res;

    res = avformat_open_input(&pFormatCtx, "test.mp4", NULL, NULL);
    if (res) {
        return res;
    }


    avformat_find_stream_info(pFormatCtx, NULL);

    int64_t duration = pFormatCtx->duration;


    // Find the first video stream
    int videoStream=-1;
    for(int i=0; i<pFormatCtx->nb_streams; i++) {
        if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO) {
            videoStream=i;
            break;
        }
    }
    if(videoStream==-1) {
        return -1;
    }

    AVCodecContext *pCodecCtxOrig = NULL;

    // Get a pointer to the codec context for the video stream
    pCodecCtxOrig=pFormatCtx->streams[videoStream]->codec;


    AVCodec *pCodec = NULL;
    // Find the decoder for the video stream
    pCodec=avcodec_find_decoder(pCodecCtxOrig->codec_id);
    if(pCodec==NULL) {
        fprintf(stderr, "Unsupported codec!\n");
        return -1; // Codec not found
    }


    AVCodecContext *pCodecCtx = NULL;
    // Copy context
    pCodecCtx = avcodec_alloc_context3(pCodec);
    if(avcodec_copy_context(pCodecCtx, pCodecCtxOrig) != 0) {
        fprintf(stderr, "Couldn't copy codec context");
        return -1; // Error copying codec context
    }


    // Open codec
    if(avcodec_open2(pCodecCtx, pCodec, NULL)<0) {
        return -1; // Could not open codec
    }


    AVFrame *pFrame = NULL;

    pFrame=av_frame_alloc();

    AVFrame *pFrameRGB = NULL;

    pFrameRGB=av_frame_alloc();



    // Determine required buffer size and allocate buffer
    int numBytes=avpicture_get_size(AV_PIX_FMT_RGB24, pCodecCtx->width,
                                pCodecCtx->height);

    uint8_t *buffer = NULL;
    buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));


    // Assign appropriate parts of buffer to image planes in pFrameRGB
    // Note that pFrameRGB is an AVFrame, but AVFrame is a superset
    // of AVPicture
    res = avpicture_fill((AVPicture *)pFrameRGB, buffer, AV_PIX_FMT_RGB24,
                    pCodecCtx->width, pCodecCtx->height);
    if (res<0) {
        return;
    }



    // I've set this number randomly
    res = av_seek_frame(pFormatCtx, videoStream, 20.0, AVSEEK_FLAG_FRAME);
    if (res<0) {
        return;
    }



    AVPacket packet;
    while(1) {
        av_read_frame(pFormatCtx, &packet);
        if(packet.stream_index==videoStream) {
            int frameFinished;
            avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
            if(frameFinished) {
                SaveFrame(pFrameRGB, pCodecCtx->width,
                    pCodecCtx->height);
                break;
            }

        }
    }


    avformat_close_input(&pFormatCtx);
    return 0;
}



void SaveFrame(AVFrame *pFrame, int width, int height) {
  FILE *pFile;
  char szFilename[] = "frame.ppm";
  int  y;

  // Open file
  pFile=fopen(szFilename, "wb");
  if(pFile==NULL)
    return;

  // Write header
  fprintf(pFile, "P6\n%d %d\n255\n", width, height);

  // Write pixel data
  for(y=0; y<height; y++)
    fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile);

  // Close file
  fclose(pFile);
}

我正在关注本教程http://dranger.com/ffmpeg/tutorial01.html http://dranger.com/ffmpeg/tutorial07.html。它表示它已在2015年更新,但已经有一些关于弃用代码的警告,例如:pFormatCtx->streams[i]->codec

我有视频时长(以微秒为单位),但我不明白应该发送给av_seek_frame的内容。我可以以某种方式使用帧数来表示持续时间和寻找,而不是时间吗?

1 个答案:

答案 0 :(得分:0)

这是一篇关于av_seek_frame的文章:

此外,在使用avpicture_fill初始化pFrameRGB之后,永远不会将其设置为新值。

您可能只是改为:

 avcodec_decode_video2(pCodecCtx, pFrameRGB, &frameFinished, &packet);

此外,我不确定您当前的代码是否保证您甚至可以处理RGB流,您可能需要对其进行颜色转换......