ffmpeg编程(一)

这篇主要讲如何以路径的方式打开一个视频

int main() {
 AVFormatContext *pFormatCtx;
 int i, videoStream;
 AVCodecContext *pCodecCtx;
 AVCodec *pCodec;
 AVFrame *pFrame;
 AVFrame *pFrameRGB;
 AVPacket packet;
 int frameFinished = NULL;
 int numBytes;
 uint8_t *buffer;
 struct SwsContext *pSwsCtx;

AVFormatContext是输入输出信息的容器,需要注意的是其中两个成员:

 struct AVInputFormat * AVIFormat;  //数据输入格式
 struct AVOutputFormat * AVIFormat; //数据输出格式

这两个成员不能同时赋值,简单来说 AVFormatContext 不能作为输入容器又作为输出容器。

i是保存帧的,videoStream找到一个视频流并且记录该数组的坐标
AVCodecContext动态的记录一个解码器的上下文信息

AVCodec:ffmpeg中编解码器是由链表结构管理的,链表的第一个节点是在文件libavcodec/util.c中声明的:

static AVCodec *first_avcodec = NULL;

对于编码器、与解码器的操作都是在围绕AVCodec该链表执行的。

AVFrame用来保存数据缓存的对像

AVPacket主要记录音视频数据帧,时钟信息和压缩数据首地址,大小等信息。

SwsContext视频分辩率、色彩空间变换时所需要的上下文句柄。

av_register_all();
 //视频的路径
 const char *filename = "/root/Desktop/test.flv";
 if (av_open_input_file(&pFormatCtx, filename, NULL, 0, NULL) != 0)
  return -1; // Couldn't open file
 if (av_find_stream_info(pFormatCtx) < 0)
  return -1; // Couldn't find stream information

 // Dump information about file onto standard error
 dump_format(pFormatCtx, 0, filename, 0);

av_register_all();函数通过执行libvcodec/allcodes.c文件里的avcodec_register_all()来初始化所有encoder/decoder。

av_open_input_file(&pFormatCtx, filename, NULL, 0, NULL)函数读取文件的头部并且把信息保存到我们给的AVFormatContext结构体中。最后三个参数用来指定特殊的文件格式,缓冲大小和格式参数,但如果把它们设置为空NULL或者0,libavformat将自动检测这些参数。

av_open_input_file 只是检测了文件的头部,所以接着 av_find_stream_info(pFormatCtx) 函数负责检查在文件中的流的信息。

那么头文件和文件中的流信息都没有问题的话,接下来dump_format(pFormatCtx, 0, filename, 0)函数负责为pFormatCtx->streams填充上正确的信息。
 videoStream = -1;
 for (i = 0; i < pFormatCtx->nb_streams; i++)
  if (pFormatCtx->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
   videoStream = i;
   break;
  }
 if (videoStream == -1)
  return -1; // Didn't find a video stream

现在pFormatCtx->streams仅仅是一组大小为pFormatCtx->nb_streams的指针,所以让我们先跳过它直到我们找到一个视频流并且记录该数组的坐标。

pCodecCtx = pFormatCtx->streams[videoStream]->codec;

得到编解码器上下文信息。

  pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
    if (pCodec == NULL) {
        fprintf(stderr, "Unsupported codec!\n");
        return -1; // Codec not found
    }

得到了编码器的上下文信息之后,但是AVCodecContext 包含了流中所使用的关于编解码器的所有信息,现在我们有了一个指向他的指针。所以必需要找到真正的编解码器avcodec_find_decoder(pCodecCtx->codec_id)函数就是做的这件事情。

    if (avcodec_open(pCodecCtx, pCodec) < 0)
        return -1; // Could not open codec

avcodec_open(pCodecCtx, pCodec)函数负责打开pCodec解码器。

 // Allocate video frame
    pFrame = avcodec_alloc_frame();

    // Allocate an AVFrame structure
    pFrameRGB = avcodec_alloc_frame();
    if (pFrameRGB == NULL)
        return -1;

avcodec_alloc_frame()函数负责申请内存,注意:初始化的时候AVFrame中的元素data,linesize均为空。未指向任何内存数据

    // Determine required buffer size and allocate buffer
    numBytes = avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,
            pCodecCtx->height);
    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
    avpicture_fill((AVPicture *) pFrameRGB, buffer, PIX_FMT_RGB24,
    pCodecCtx->width, pCodecCtx->height);

函数avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,pCodecCtx->height);负责计算AVCodecContext缓冲区的大小和申请空间。

函数av_malloc(numBytes * sizeof(uint8_t))申请内存对齐(但不保证内存泄漏)。

函数avpicture_fill((AVPicture *) pFrameRGB, buffer, PIX_FMT_RGB24,pCodecCtx->width, pCodecCtx->height);把帧和pFrameRGB关联起来。

  // Read frames and save first five frames to disk
    i = 0;
    while (av_read_frame(pFormatCtx, &packet) >= 0) {
        // Is this a packet from the video stream?
        if (packet.stream_index == videoStream) {

            // Allocate video frame
            pFrame = avcodec_alloc_frame();
            int w = pCodecCtx->width;
            int h = pCodecCtx->height;
            // Decode video frame
            avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
            pSwsCtx = sws_getContext(w, h, pCodecCtx->pix_fmt, w, h,
                    PIX_FMT_RGB565, SWS_POINT, NULL, NULL, NULL);
            // Did we get a video frame?

            if (frameFinished) {
                // Convert the image from its native format to RGB
                sws_scale(pSwsCtx, pFrame->data, pFrame->linesize, 0,
                        pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
                // Save the frame to disk

                ++i;
                printf("%d\n", i);
                SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height, i);
            }
        }

函数av_read_frame(pFormatCtx, &packet)读取一个包并且把它保存到AVPacket结构体中。

函数avcodec_decode_video2()把包转换为帧。

函数sws_getContext(w, h, pCodecCtx->pix_fmt, w, h, PIX_FMT_RGB565, SWS_POINT, NULL, NULL, NULL);负责得到视频分辩率、色彩空间变换时所需要的上下文句柄。

函数sws_scale(pSwsCtx, pFrame->data, pFrame->linesize, 0,pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);把RGB格式转换成image。

void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame) {
    FILE *pFile;
    const char *szFilename = "/root/Desktop/ffmpeg";
    int y;

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

    // Write header
    //printf("P6\n%d %d\n255\n", width, height);
    printf("bbbbbbbbbb\n");
    // Write pixel data
    for (y = 0; y < height; y++)
        fwrite(pFrame->data[0] + y * pFrame->linesize[0], sizeof(char),
                width * 3, pFile);

    // Close file
    fclose(pFile);
} 

把图片保存成文件,这里就不再叙述了。

// Free the packet that was allocated by av_read_frame
        av_free_packet(&packet);
    }

    // Free the RGB image
    av_free(buffer);
    av_free(pFrameRGB);

    // Free the YUV frame
    av_free(pFrame);

    // Close the codec
    avcodec_close(pCodecCtx);

    // Close the video file
    av_close_input_file(pFormatCtx);

    return 0;
}

释放内存。

c文件下载

http://download.csdn.net/detail/wenwei19861106/4219809

posted on 2012-04-12 09:05  南瓜饼  阅读(6772)  评论(1编辑  收藏  举报

导航