代码改变世界

[原]零基础学习视频解码之解码图像

2014-07-30 21:45  雪夜&流星  阅读(1826)  评论(4编辑  收藏  举报

  本文全部是在Ubuntu 14.04下使用Eclipse开发,本文假定你Eclipse已经安装好了CDT,以及安装好了FFmpeg和SDL。

  解码流程详见上一篇博客[原]零基础学习视频解码之FFMpeg中比较重要的函数以及数据结构

  如何安裝FFmpeg详见上一篇博客:[原]零基础学习视频解码之安装ffmpeg

  下图是解码流程图(全图):

    本文详解如何在Eclipse中使用使用C来完成一个解码的过程并且将其解码出来的YUV图片保存下来:

  一、新建一个C Project,添加相关库的支持:

   Project->Properties->C/C++ General->Path and Symbols->Libraries->Add->avcode、avutil、avformat、swscale

 二、在工程中引用相应的头文件:

   

/*
 ============================================================================
 Name        : VideoDecodeTutorial.c
 Author      : clarck
 Version     :
 Copyright   : Your copyright notice
 Description : Hello World in C, Ansi-style
 ============================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/pixfmt.h"
#include "libswscale/swscale.h"

int main(int argc, char* args[]) {
    puts("!!!Hello World!!!"); /* prints !!!Hello World!!! */
    return EXIT_SUCCESS;
}

  三、定义AVFormatContext、AVCodecContext、AVCodec

/*
 ============================================================================
 Name        : VideoDecodeTutorial.c
 Author      : clarck
 Version     :
 Copyright   : Your copyright notice
 Description : Hello World in C, Ansi-style
 ============================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/pixfmt.h"
#include "libswscale/swscale.h"

int main(int argc, char* args[]) {
    AVFormatContext *pFormatCtx;
    AVCodecContext *pCodecCtx;
    AVCodec *pCodec;
    int videoStream, i;

    return EXIT_SUCCESS;
}

  四、注册所有格式以及初始化AVFormatContext

/*
 ============================================================================
 Name        : VideoDecodeTutorial.c
 Author      : clarck
 Version     :
 Copyright   : Your copyright notice
 Description : Hello World in C, Ansi-style
 ============================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/pixfmt.h"
#include "libswscale/swscale.h"

int main(int argc, char* args[]) {
    AVFormatContext *pFormatCtx;
    AVCodecContext *pCodecCtx;
    AVCodec *pCodec;
    int videoStream, i;

    av_register_all();
    pFormatCtx = avformat_alloc_context();

    return EXIT_SUCCESS;
}

  五、打开一个视频文件,获取视频文件的流信息, 获取初始的视频流

/*
 ============================================================================
 Name        : VideoDecodeTutorial.c
 Author      : clarck
 Version     :
 Copyright   : Your copyright notice
 Description : Hello World in C, Ansi-style
 ============================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/pixfmt.h"
#include "libswscale/swscale.h"

int main(int argc, char* args[]) {
    AVFormatContext *pFormatCtx;
    AVCodecContext *pCodecCtx;
    AVCodec *pCodec;
    int videoStream, i;

    char filePath[] = "./src/a.mp4";

    av_register_all();
    pFormatCtx = avformat_alloc_context();

    if (avformat_open_input(&pFormatCtx, filePath, NULL, NULL) != 0) {
        printf("can't open the file. \n");
        return EXIT_FAILURE;
    }

    if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
        printf("Could't find stream infomation.\n");
        return EXIT_FAILURE;
    }

    videoStream = 1;
    for (i = 0; i < pFormatCtx->nb_streams; i++) {
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoStream = i;
        }
    }

    if (videoStream == -1) {
        printf("Didn't find a video stream.\n");
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

  六、获得视频流编码内容,获取视频编码格式,用一个编码格式打开一个编码文件

/*
 ============================================================================
 Name        : VideoDecodeTutorial.c
 Author      : clarck
 Version     :
 Copyright   : Your copyright notice
 Description : Hello World in C, Ansi-style
 ============================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/pixfmt.h"
#include "libswscale/swscale.h"

int main(int argc, char* args[]) {
    AVFormatContext *pFormatCtx;
    AVCodecContext *pCodecCtx;
    AVCodec *pCodec;
    int videoStream, i;

    char filePath[] = "./src/a.mp4";

    av_register_all();
    pFormatCtx = avformat_alloc_context();

    if (avformat_open_input(&pFormatCtx, filePath, NULL, NULL) != 0) {
        printf("can't open the file. \n");
        return EXIT_FAILURE;
    }

    if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
        printf("Could't find stream infomation.\n");
        return EXIT_FAILURE;
    }

    videoStream = 1;
    for (i = 0; i < pFormatCtx->nb_streams; i++) {
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoStream = i;
        }
    }

    if (videoStream == -1) {
        printf("Didn't find a video stream.\n");
        return EXIT_FAILURE;
    }

    pCodecCtx = pFormatCtx->streams[videoStream]->codec;
    pCodec = avcodec_find_decoder(pCodecCtx->codec_id);

    if (pCodec == NULL) {
        printf("Codec not found.\n");
        return EXIT_FAILURE;
    }

    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
        printf("Could not open codec.\n");
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

  七、申请内存用来作为图片文件(AVPicture)缓冲

/*
 ============================================================================
 Name        : VideoDecodeTutorial.c
 Author      : clarck
 Version     :
 Copyright   : Your copyright notice
 Description : Hello World in C, Ansi-style
 ============================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/pixfmt.h"
#include "libswscale/swscale.h"

int main(int argc, char* args[]) {
    AVFormatContext *pFormatCtx;
    AVCodecContext *pCodecCtx;
    AVCodec *pCodec;
    AVFrame *pFrame, *pFrameYUV;
    uint8_t *out_buffer;

    int videoStream, i, numBytes;

    char filePath[] = "./src/a.mp4";

    av_register_all();
    pFormatCtx = avformat_alloc_context();

    if (avformat_open_input(&pFormatCtx, filePath, NULL, NULL) != 0) {
        printf("can't open the file. \n");
        return EXIT_FAILURE;
    }

    if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
        printf("Could't find stream infomation.\n");
        return EXIT_FAILURE;
    }

    videoStream = 1;
    for (i = 0; i < pFormatCtx->nb_streams; i++) {
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoStream = i;
        }
    }

    if (videoStream == -1) {
        printf("Didn't find a video stream.\n");
        return EXIT_FAILURE;
    }

    pCodecCtx = pFormatCtx->streams[videoStream]->codec;
    pCodec = avcodec_find_decoder(pCodecCtx->codec_id);

    if (pCodec == NULL) {
        printf("Codec not found.\n");
        return EXIT_FAILURE;
    }

    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
        printf("Could not open codec.\n");
        return EXIT_FAILURE;
    }

    pFrame = av_frame_alloc();
    pFrameYUV = av_frame_alloc();

    numBytes = avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width,
            pCodecCtx->height);
    out_buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
    avpicture_fill((AVPicture *) pFrameYUV, out_buffer, AV_PIX_FMT_YUV420P,
            pCodecCtx->width, pCodecCtx->height);

    return EXIT_SUCCESS;
}

  八、为AVPacket申请内存

/*
 ============================================================================
 Name        : VideoDecodeTutorial.c
 Author      : clarck
 Version     :
 Copyright   : Your copyright notice
 Description : Hello World in C, Ansi-style
 ============================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/pixfmt.h"
#include "libswscale/swscale.h"

int main(int argc, char* args[]) {
    AVFormatContext *pFormatCtx;
    AVCodecContext *pCodecCtx;
    AVCodec *pCodec;
    AVFrame *pFrame, *pFrameYUV;
    AVPacket *packet;
    uint8_t *out_buffer;int videoStream, i, numBytes;
    int ret, got_picture;

    char filePath[] = "./src/a.mp4";

    av_register_all();
    pFormatCtx = avformat_alloc_context();

    if (avformat_open_input(&pFormatCtx, filePath, NULL, NULL) != 0) {
        printf("can't open the file. \n");
        return EXIT_FAILURE;
    }

    if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
        printf("Could't find stream infomation.\n");
        return EXIT_FAILURE;
    }

    videoStream = 1;
    for (i = 0; i < pFormatCtx->nb_streams; i++) {
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoStream = i;
        }
    }

    if (videoStream == -1) {
        printf("Didn't find a video stream.\n");
        return EXIT_FAILURE;
    }

    pCodecCtx = pFormatCtx->streams[videoStream]->codec;
    pCodec = avcodec_find_decoder(pCodecCtx->codec_id);

    if (pCodec == NULL) {
        printf("Codec not found.\n");
        return EXIT_FAILURE;
    }

    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
        printf("Could not open codec.\n");
        return EXIT_FAILURE;
    }

    pFrame = av_frame_alloc();
    pFrameYUV = av_frame_alloc();

    numBytes = avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width,
            pCodecCtx->height);
    out_buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
    avpicture_fill((AVPicture *) pFrameYUV, out_buffer, AV_PIX_FMT_YUV420P,
            pCodecCtx->width, pCodecCtx->height);

    int y_size = pCodecCtx->width * pCodecCtx->height;

    packet = (AVPacket *) malloc(sizeof(AVPacket));
    av_new_packet(packet, y_size);

    av_dump_format(pFormatCtx, 0, filePath, 0);

    return EXIT_SUCCESS;
}

  九、从AVFrame中读取AVPacket,解码video得到YUV图像AVPicture

/*
 ============================================================================
 Name        : VideoDecodeTutorial.c
 Author      : clarck
 Version     :
 Copyright   : Your copyright notice
 Description : Hello World in C, Ansi-style
 ============================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/pixfmt.h"
#include "libswscale/swscale.h"

int main(int argc, char* args[]) {
    AVFormatContext *pFormatCtx;
    AVCodecContext *pCodecCtx;
    AVCodec *pCodec;
    AVFrame *pFrame, *pFrameYUV;
    AVPacket *packet;
    uint8_t *out_buffer;

    int videoStream, i, numBytes;
    int ret, got_picture;

    char filePath[] = "./src/a.mp4";

    av_register_all();
    pFormatCtx = avformat_alloc_context();

    if (avformat_open_input(&pFormatCtx, filePath, NULL, NULL) != 0) {
        printf("can't open the file. \n");
        return EXIT_FAILURE;
    }

    if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
        printf("Could't find stream infomation.\n");
        return EXIT_FAILURE;
    }

    videoStream = 1;
    for (i = 0; i < pFormatCtx->nb_streams; i++) {
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoStream = i;
        }
    }

    if (videoStream == -1) {
        printf("Didn't find a video stream.\n");
        return EXIT_FAILURE;
    }

    pCodecCtx = pFormatCtx->streams[videoStream]->codec;
    pCodec = avcodec_find_decoder(pCodecCtx->codec_id);

    if (pCodec == NULL) {
        printf("Codec not found.\n");
        return EXIT_FAILURE;
    }

    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
        printf("Could not open codec.\n");
        return EXIT_FAILURE;
    }

    pFrame = av_frame_alloc();
    pFrameYUV = av_frame_alloc();

    numBytes = avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width,
            pCodecCtx->height);
    out_buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
    avpicture_fill((AVPicture *) pFrameYUV, out_buffer, AV_PIX_FMT_YUV420P,
            pCodecCtx->width, pCodecCtx->height);

    int y_size = pCodecCtx->width * pCodecCtx->height;

    packet = (AVPacket *) malloc(sizeof(AVPacket));
    av_new_packet(packet, y_size);

    av_dump_format(pFormatCtx, 0, filePath, 0);

    while (av_read_frame(pFormatCtx, packet) >= 0) {
        if (packet->stream_index == videoStream) {
            ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture,
                    packet);

            if (ret < 0) {
                printf("decode error.\n");
                return EXIT_FAILURE;
            }

            if (got_picture) {
                //TODO 保存解码出来的图片
            }
        }
        av_free_packet(packet);
    }

    av_free(out_buffer);
    av_free(pFrameYUV);
    avcodec_close(pCodecCtx);
    avformat_close_input(&pFormatCtx);

    return EXIT_SUCCESS;
}

   十、利用sws_scale將得到的YUV转换成Image data

/*
 ============================================================================
 Name        : VideoDecodeTutorial.c
 Author      : clarck
 Version     :
 Copyright   : Your copyright notice
 Description : Hello World in C, Ansi-style
 ============================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/pixfmt.h"
#include "libswscale/swscale.h"

int main(int argc, char* args[]) {
    AVFormatContext *pFormatCtx;
    AVCodecContext *pCodecCtx;
    AVCodec *pCodec;
    AVFrame *pFrame, *pFrameYUV;
    AVPacket *packet;
    uint8_t *out_buffer;

    static struct SwsContext *img_convert_ctx;

    int videoStream, i, numBytes;
    int ret, got_picture;

    char filePath[] = "./src/a.mp4";

    av_register_all();
    pFormatCtx = avformat_alloc_context();

    if (avformat_open_input(&pFormatCtx, filePath, NULL, NULL) != 0) {
        printf("can't open the file. \n");
        return EXIT_FAILURE;
    }

    if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
        printf("Could't find stream infomation.\n");
        return EXIT_FAILURE;
    }

    videoStream = 1;
    for (i = 0; i < pFormatCtx->nb_streams; i++) {
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoStream = i;
        }
    }

    if (videoStream == -1) {
        printf("Didn't find a video stream.\n");
        return EXIT_FAILURE;
    }

    pCodecCtx = pFormatCtx->streams[videoStream]->codec;
    pCodec = avcodec_find_decoder(pCodecCtx->codec_id);

    if (pCodec == NULL) {
        printf("Codec not found.\n");
        return EXIT_FAILURE;
    }

    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
        printf("Could not open codec.\n");
        return EXIT_FAILURE;
    }

    pFrame = av_frame_alloc();
    pFrameYUV = av_frame_alloc();

    numBytes = avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width,
            pCodecCtx->height);
    out_buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
    avpicture_fill((AVPicture *) pFrameYUV, out_buffer, AV_PIX_FMT_YUV420P,
            pCodecCtx->width, pCodecCtx->height);

    int y_size = pCodecCtx->width * pCodecCtx->height;

    packet = (AVPacket *) malloc(sizeof(AVPacket));
    av_new_packet(packet, y_size);

    av_dump_format(pFormatCtx, 0, filePath, 0);

    while (av_read_frame(pFormatCtx, packet) >= 0) {
        if (packet->stream_index == videoStream) {
            ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture,
                    packet);

            if (ret < 0) {
                printf("decode error.\n");
                return EXIT_FAILURE;
            }

            if (got_picture) {
                img_convert_ctx = sws_getContext(pCodecCtx->width,
                        pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width,
                        pCodecCtx->height, AV_PIX_FMT_YUV420P,
                        SWS_BICUBIC, NULL, NULL, NULL);
                sws_scale(img_convert_ctx,
                        (const uint8_t* const *) pFrame->data, pFrame->linesize,
                        0, pCodecCtx->height, pFrameYUV->data,
                        pFrameYUV->linesize);
                sws_freeContext(img_convert_ctx);
                //TODO 保存解码出来的图片
            }
        }
        av_free_packet(packet);
    }

    av_free(out_buffer);
    av_free(pFrameYUV);
    avcodec_close(pCodecCtx);
    avformat_close_input(&pFormatCtx);

    return EXIT_SUCCESS;
}

  十一、将解码的data保存为.ppm

/*
 ============================================================================
 Name        : VideoDecodeTutorial.c
 Author      : clarck
 Version     :
 Copyright   : Your copyright notice
 Description : Hello World in C, Ansi-style
 ============================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/pixfmt.h"
#include "libswscale/swscale.h"

void SaveFrame(AVFrame *pFrame, int width, int height, int i) {
    FILE * pFile;
    char szFilename[32];
    int y;

    sprintf(szFilename, "frame%d.ppm", i);

    pFile = fopen(szFilename, "wb");

    if (pFile == NULL) {
        printf("pFile is null");
        return;
    }

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

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

    fclose(pFile);
}

int main(int argc, char* args[]) {
    AVFormatContext *pFormatCtx;
    AVCodecContext *pCodecCtx;
    AVCodec *pCodec;
    AVFrame *pFrame, *pFrameYUV;
    AVPacket *packet;
    uint8_t *out_buffer;

    static struct SwsContext *img_convert_ctx;

    int videoStream, i, numBytes;
    int ret, got_picture;

    char filePath[] = "./src/a.mp4";

    av_register_all();
    pFormatCtx = avformat_alloc_context();

    if (avformat_open_input(&pFormatCtx, filePath, NULL, NULL) != 0) {
        printf("can't open the file. \n");
        return EXIT_FAILURE;
    }

    if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
        printf("Could't find stream infomation.\n");
        return EXIT_FAILURE;
    }

    videoStream = 1;
    for (i = 0; i < pFormatCtx->nb_streams; i++) {
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoStream = i;
        }
    }

    if (videoStream == -1) {
        printf("Didn't find a video stream.\n");
        return EXIT_FAILURE;
    }

    pCodecCtx = pFormatCtx->streams[videoStream]->codec;
    pCodec = avcodec_find_decoder(pCodecCtx->codec_id);

    if (pCodec == NULL) {
        printf("Codec not found.\n");
        return EXIT_FAILURE;
    }

    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
        printf("Could not open codec.\n");
        return EXIT_FAILURE;
    }

    pFrame = av_frame_alloc();
    pFrameYUV = av_frame_alloc();

    numBytes = avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width,
            pCodecCtx->height);
    out_buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
    avpicture_fill((AVPicture *) pFrameYUV, out_buffer, AV_PIX_FMT_YUV420P,
            pCodecCtx->width, pCodecCtx->height);

    int y_size = pCodecCtx->width * pCodecCtx->height;

    packet = (AVPacket *) malloc(sizeof(AVPacket));
    av_new_packet(packet, y_size);

    av_dump_format(pFormatCtx, 0, filePath, 0);

    while (av_read_frame(pFormatCtx, packet) >= 0) {
        if (packet->stream_index == videoStream) {
            ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture,
                    packet);

            if (ret < 0) {
                printf("decode error.\n");
                return EXIT_FAILURE;
            }

            if (got_picture) {
                img_convert_ctx = sws_getContext(pCodecCtx->width,
                        pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width,
                        pCodecCtx->height, AV_PIX_FMT_YUV420P,
                        SWS_BICUBIC, NULL, NULL, NULL);
                sws_scale(img_convert_ctx,
                        (const uint8_t* const *) pFrame->data, pFrame->linesize,
                        0, pCodecCtx->height, pFrameYUV->data,
                        pFrameYUV->linesize);
                sws_freeContext(img_convert_ctx);

                if (i < 20) {
                    SaveFrame(pFrameYUV, pCodecCtx->width, pCodecCtx->height, ++i);
                }
            }
        }
        av_free_packet(packet);
    }

    av_free(out_buffer);
    av_free(pFrameYUV);
    avcodec_close(pCodecCtx);
    avformat_close_input(&pFormatCtx);

    return EXIT_SUCCESS;
}

  十二、将解码的数据使用SDL输出展现, 首先添加SDL库支持

     Project->Properties->C/C++ General->Path and Symbols->Libraries->Add->SDL

/*
 ============================================================================
 Name        : VideoDecodeTutorial.c
 Author      : clarck
 Version     :
 Copyright   : Your copyright notice
 Description : Hello World in C, Ansi-style
 ============================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include "SDL/SDL.h"
#include "SDL/SDL_thread.h"
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/pixfmt.h"
#include "libswscale/swscale.h"

void SaveFrame(AVFrame *pFrame, int width, int height, int i) {
    FILE * pFile;
    char szFilename[32];
    int y;

    sprintf(szFilename, "frame%d.ppm", i);

    pFile = fopen(szFilename, "wb");

    if (pFile == NULL) {
        printf("pFile is null");
        return;
    }

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

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

    fclose(pFile);
}

int main(int argc, char* args[]) {
    AVFormatContext *pFormatCtx;
    AVCodecContext *pCodecCtx;
    AVCodec *pCodec;
    AVFrame *pFrame, *pFrameYUV;
    AVPacket *packet;
    uint8_t *out_buffer;

    static struct SwsContext *img_convert_ctx;

    int videoStream, i, numBytes;
    int ret, got_picture;

    char filePath[] = "./src/a.mp4";

    av_register_all();
    pFormatCtx = avformat_alloc_context();

    if (avformat_open_input(&pFormatCtx, filePath, NULL, NULL) != 0) {
        printf("can't open the file. \n");
        return EXIT_FAILURE;
    }

    if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
        printf("Could't find stream infomation.\n");
        return EXIT_FAILURE;
    }

    videoStream = 1;
    for (i = 0; i < pFormatCtx->nb_streams; i++) {
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoStream = i;
        }
    }

    if (videoStream == -1) {
        printf("Didn't find a video stream.\n");
        return EXIT_FAILURE;
    }

    pCodecCtx = pFormatCtx->streams[videoStream]->codec;
    pCodec = avcodec_find_decoder(pCodecCtx->codec_id);

    if (pCodec == NULL) {
        printf("Codec not found.\n");
        return EXIT_FAILURE;
    }

    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
        printf("Could not open codec.\n");
        return EXIT_FAILURE;
    }

    pFrame = av_frame_alloc();
    pFrameYUV = av_frame_alloc();

    numBytes = avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width,
            pCodecCtx->height);
    out_buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
    avpicture_fill((AVPicture *) pFrameYUV, out_buffer, AV_PIX_FMT_YUV420P,
            pCodecCtx->width, pCodecCtx->height);

    //==============SDL==================//
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
        printf("Could not initialize SDL - %s \n", SDL_GetError());
        exit(1);
    }

    SDL_Surface *screen;
    screen = SDL_SetVideoMode(pCodecCtx->width, pCodecCtx->height, 0, 0);
    if (!screen) {
        printf("SDL:could not set video mode - exiting\n");
        exit(1);
    }

    SDL_Overlay *bmp;
    bmp = SDL_CreateYUVOverlay(pCodecCtx->width, pCodecCtx->height,
    SDL_YV12_OVERLAY, screen);
    SDL_Rect rect;
    //===================================//

    int y_size = pCodecCtx->width * pCodecCtx->height;

    packet = (AVPacket *) malloc(sizeof(AVPacket));
    av_new_packet(packet, y_size);

    av_dump_format(pFormatCtx, 0, filePath, 0);

    while (av_read_frame(pFormatCtx, packet) >= 0) {
        if (packet->stream_index == videoStream) {
            ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture,
                    packet);

            if (ret < 0) {
                printf("decode error.\n");
                return EXIT_FAILURE;
            }

            if (got_picture) {
                SDL_LockYUVOverlay(bmp);
                pFrameYUV->data[0] = bmp->pixels[0];
                pFrameYUV->data[1] = bmp->pixels[2];
                pFrameYUV->data[2] = bmp->pixels[1];
                pFrameYUV->linesize[0] = bmp->pitches[0];
                pFrameYUV->linesize[1] = bmp->pitches[2];
                pFrameYUV->linesize[2] = bmp->pitches[1];

                img_convert_ctx = sws_getContext(pCodecCtx->width,
                        pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width,
                        pCodecCtx->height, AV_PIX_FMT_YUV420P,
                        SWS_BICUBIC, NULL, NULL, NULL);
                sws_scale(img_convert_ctx,
                        (const uint8_t* const *) pFrame->data, pFrame->linesize,
                        0, pCodecCtx->height, pFrameYUV->data,
                        pFrameYUV->linesize);
                sws_freeContext(img_convert_ctx);

                SDL_UnlockYUVOverlay(bmp);
                rect.x = 0;
                rect.y = 0;
                rect.w = pCodecCtx->width;
                rect.h = pCodecCtx->height;
                SDL_DisplayYUVOverlay(bmp, &rect);
                //delay 400ms
                SDL_Delay(400);
            }
        }
        av_free_packet(packet);
    }

    av_free(out_buffer);
    av_free(pFrameYUV);
    avcodec_close(pCodecCtx);
    avformat_close_input(&pFormatCtx);

    return EXIT_SUCCESS;
}