zoukankan      html  css  js  c++  java
  • [原]零基础学习视频解码之解码图像

      本文全部是在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. 
    ");
            return EXIT_FAILURE;
        }
    
        if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
            printf("Could't find stream infomation.
    ");
            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.
    ");
            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. 
    ");
            return EXIT_FAILURE;
        }
    
        if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
            printf("Could't find stream infomation.
    ");
            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.
    ");
            return EXIT_FAILURE;
        }
    
        pCodecCtx = pFormatCtx->streams[videoStream]->codec;
        pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
    
        if (pCodec == NULL) {
            printf("Codec not found.
    ");
            return EXIT_FAILURE;
        }
    
        if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
            printf("Could not open codec.
    ");
            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. 
    ");
            return EXIT_FAILURE;
        }
    
        if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
            printf("Could't find stream infomation.
    ");
            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.
    ");
            return EXIT_FAILURE;
        }
    
        pCodecCtx = pFormatCtx->streams[videoStream]->codec;
        pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
    
        if (pCodec == NULL) {
            printf("Codec not found.
    ");
            return EXIT_FAILURE;
        }
    
        if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
            printf("Could not open codec.
    ");
            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. 
    ");
            return EXIT_FAILURE;
        }
    
        if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
            printf("Could't find stream infomation.
    ");
            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.
    ");
            return EXIT_FAILURE;
        }
    
        pCodecCtx = pFormatCtx->streams[videoStream]->codec;
        pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
    
        if (pCodec == NULL) {
            printf("Codec not found.
    ");
            return EXIT_FAILURE;
        }
    
        if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
            printf("Could not open codec.
    ");
            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. 
    ");
            return EXIT_FAILURE;
        }
    
        if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
            printf("Could't find stream infomation.
    ");
            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.
    ");
            return EXIT_FAILURE;
        }
    
        pCodecCtx = pFormatCtx->streams[videoStream]->codec;
        pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
    
        if (pCodec == NULL) {
            printf("Codec not found.
    ");
            return EXIT_FAILURE;
        }
    
        if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
            printf("Could not open codec.
    ");
            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.
    ");
                    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. 
    ");
            return EXIT_FAILURE;
        }
    
        if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
            printf("Could't find stream infomation.
    ");
            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.
    ");
            return EXIT_FAILURE;
        }
    
        pCodecCtx = pFormatCtx->streams[videoStream]->codec;
        pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
    
        if (pCodec == NULL) {
            printf("Codec not found.
    ");
            return EXIT_FAILURE;
        }
    
        if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
            printf("Could not open codec.
    ");
            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.
    ");
                    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
    %d %d
    255
    ", 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. 
    ");
            return EXIT_FAILURE;
        }
    
        if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
            printf("Could't find stream infomation.
    ");
            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.
    ");
            return EXIT_FAILURE;
        }
    
        pCodecCtx = pFormatCtx->streams[videoStream]->codec;
        pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
    
        if (pCodec == NULL) {
            printf("Codec not found.
    ");
            return EXIT_FAILURE;
        }
    
        if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
            printf("Could not open codec.
    ");
            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.
    ");
                    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
    %d %d
    255
    ", 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. 
    ");
            return EXIT_FAILURE;
        }
    
        if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
            printf("Could't find stream infomation.
    ");
            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.
    ");
            return EXIT_FAILURE;
        }
    
        pCodecCtx = pFormatCtx->streams[videoStream]->codec;
        pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
    
        if (pCodec == NULL) {
            printf("Codec not found.
    ");
            return EXIT_FAILURE;
        }
    
        if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
            printf("Could not open codec.
    ");
            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 
    ", 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
    ");
            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.
    ");
                    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;
    }

       

        

  • 相关阅读:
    手把手教你学Git
    服务器上Mysql的安装与配置
    python 5
    python 4
    python 3
    python 2
    区分命令行模式和Python交互模式
    CUDA编程模型之内存管理
    多目标优化算法-NSGA2
    C# ListView 如何添加列标头
  • 原文地址:https://www.cnblogs.com/tanlon/p/3879208.html
Copyright © 2011-2022 走看看