用ffmpeg+qt做点有趣的事(2)

来源:互联网 发布:足球数据统计app 编辑:程序博客网 时间:2024/05/16 10:09

首先来个简单的例子,使用FFMPEG打开视频文件,并解码保存成一张张的图片。

具体的步骤如下所示:


1.首先需要先初始化一下,使用如下函数:

1
av_register_all(); //初始化FFMPEG  调用了这个才能正常适用编码器和解码器



使用这个函数完成编码器和解码器的初始化,只有初始化了编码器和解码器才能正常使用,否则会在打开编解码器的时候失败。


2.接着需要分配一个AVFormatContext,FFMPEG所有的操作都要通过这个AVFormatContext来进行

1
AVFormatContext *pFormatCtx = avformat_alloc_context();


3.接着调用打开视频文件

这里文件名先不要使用中文,否则会打开失败,后期再讲解如何处理中文。

1
2
char *file_path = "E:in.mp4";
avformat_open_input(&pFormatCtx, file_path, NULL, NULL);


4.文件打开成功后就是查找文件中的视频流了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    ///循环查找视频中包含的流信息,直到找到视频类型的流    
    ///便将其记录下来 保存到videoStream变量中
    ///这里我们现在只处理视频流  音频流先不管他
    for (i = 0; i < pFormatCtx->nb_streams; i++) {
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoStream = i;
        }
    }
 
    ///如果videoStream为-1 说明没有找到视频流
    if (videoStream == -1) {
        printf("Didn't find a video stream.
");
        return -1;
    }


5.现在根据视频流  打开一个解码器来解码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    ///查找解码器    
    pCodecCtx = pFormatCtx->streams[videoStream]->codec;
    pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
 
    if (pCodec == NULL) {
        printf("Codec not found.
");
        return -1;
    }
 
    ///打开解码器
    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
        printf("Could not open codec.
");
        return -1;
    }


可以看出  我们可以直接根据查找到的视频流信息获取到解码器。

而且我们并不知道他实际用的是什么编码器。

这就是为什么一开始我们使用FFMPEG来操作,因为很多东西我们可以不关系。



6.现在开始读取视频了:

1
2
3
4
5
6
7
8
    int y_size = pCodecCtx->width * pCodecCtx->height;
    AVPacket *packet = (AVPacket *) malloc(sizeof(AVPacket)); //分配一个packet
    av_new_packet(packet, y_size); //分配packet的数据
 
    if (av_read_frame(pFormatCtx, packet) < 0)
    {
        break//这里认为视频读取完了
    }

可以看出 av_read_frame读取的是一帧视频,并存入一个AVPacket的结构中。



7.前面我们说过 视频里面的数据是经过编码压缩的,因此这里我们需要将其解码:

1
2
3
4
5
6
7
8
9
10
    if (packet->stream_index == videoStream) 
    {        
        ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture,packet);
 
        if (ret < 0) {
            printf("decode error.
");
            return -1;
        }
    }


8.基本上所有解码器解码之后得到的图像数据都是YUV420的格式,而这里我们需要将其保存成图片文件,因此需要将得到的YUV420数据转换成RGB格式,转换格式也是直接使用FFMPEG来完成:

1
2
3
4
5
6
    if (got_picture) {        
        sws_scale(img_convert_ctx,
                (uint8_t const const *) pFrame->data,
                pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data,
                pFrameRGB->linesize);
    }


至于YUV420和RGB图像格式的具体内容,这里不用去了解。这里只需要知道有这么个东西就行了,对我们使用FFMPEG转换没有影响。



9.得到RGB数据之后就是直接写入文件了:

1
    SaveFrame(pFrameRGB,     pCodecCtx->width,pCodecCtx->height,index++); //保存图片     if (index > 50) return 0; //这里我们就保存50张图片



至此读取视频解码保存成图片就写好了:

完整的代码如下:

extern "C"{
    #include "libavcodec/avcodec.h"
    #include "libavformat/avformat.h"
    #include "libavutil/pixfmt.h"
    #include "libswscale/swscale.h"
}
 
#include <stdio.h>
 
///现在我们需要做的是让SaveFrame函数能把RGB信息定稿到一个PPM格式的文件中。
///我们将生成一个简单的PPM格式文件,请相信,它是可以工作的。
void SaveFrame(AVFrame *pFrame, int width, int height,int index)
{
 
  FILE *pFile;
  char szFilename[32];
  int  y;
 
  // Open file
  sprintf(szFilename, "frame%d.ppm", index);
  pFile=fopen(szFilename, "wb");
 
  if(pFile==NULL)
    return;
 
  // Write header
  fprintf(pFile, "P6
%d %d
255
", 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);
 
}
 
 
int main(int argc, char *argv[])
{
    char *file_path = "E:in.mp4";
 
    AVFormatContext *pFormatCtx;
    AVCodecContext *pCodecCtx;
    AVCodec *pCodec;
    AVFrame *pFrame, *pFrameRGB;
    AVPacket *packet;
    uint8_t *out_buffer;
 
    static struct SwsContext *img_convert_ctx;
 
    int videoStream, i, numBytes;
    int ret, got_picture;
 
    av_register_all(); //初始化FFMPEG  调用了这个才能正常适用编码器和解码器
 
    //Allocate an AVFormatContext.
    pFormatCtx = avformat_alloc_context();
 
    if (avformat_open_input(&pFormatCtx, file_path, NULL, NULL) != 0) {
        printf("can't open the file. 
");
        return -1;
    }
 
    if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
        printf("Could't find stream infomation.
");
        return -1;
    }
 
    videoStream = -1;
 
    ///循环查找视频中包含的流信息,直到找到视频类型的流
    ///便将其记录下来 保存到videoStream变量中
    ///这里我们现在只处理视频流  音频流先不管他
    for (i = 0; i < pFormatCtx->nb_streams; i++) {
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoStream = i;
        }
    }
 
    ///如果videoStream为-1 说明没有找到视频流
    if (videoStream == -1) {
        printf("Didn't find a video stream.
");
        return -1;
    }
 
    ///查找解码器
    pCodecCtx = pFormatCtx->streams[videoStream]->codec;
    pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
 
    if (pCodec == NULL) {
        printf("Codec not found.
");
        return -1;
    }
 
    ///打开解码器
    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
        printf("Could not open codec.
");
        return -1;
    }
 
    pFrame = av_frame_alloc();
    pFrameRGB = av_frame_alloc();
 
    img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,
            pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,
            PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL);
 
    numBytes = avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,pCodecCtx->height);
 
    out_buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
    avpicture_fill((AVPicture *) pFrameRGB, out_buffer, PIX_FMT_RGB24,
            pCodecCtx->width, pCodecCtx->height);
 
    int y_size = pCodecCtx->width * pCodecCtx->height;
 
    packet = (AVPacket *) malloc(sizeof(AVPacket)); //分配一个packet
    av_new_packet(packet, y_size); //分配packet的数据
 
    av_dump_format(pFormatCtx, 0, file_path, 0); //输出视频信息
 
    int index = 0;
 
    while (1)
    {
        if (av_read_frame(pFormatCtx, packet) < 0)
        {
            break//这里认为视频读取完了
        }
 
        if (packet->stream_index == videoStream) {
            ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture,packet);
 
            if (ret < 0) {
                printf("decode error.
");
                return -1;
            }
 
            if (got_picture) {
                sws_scale(img_convert_ctx,
                        (uint8_t const const *) pFrame->data,
                        pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data,
                        pFrameRGB->linesize);
 
                SaveFrame(pFrameRGB, pCodecCtx->width,pCodecCtx->height,index++); //保存图片
                if (index > 50) return 0; //这里我们就保存50张图片
            }
        }
        av_free_packet(packet);
    }
    av_free(out_buffer);
    av_free(pFrameRGB);
    avcodec_close(pCodecCtx);
    avformat_close_input(&pFormatCtx);
 
    return 0;
}