FFMPEG decode mp4, SDL play

#include <iostream>


//Refresh Event
#define SFM_REFRESH_EVENT     (SDL_USEREVENT + 1)
#define SFM_BREAK_EVENT       (SDL_USEREVENT + 2)

extern "C" {
    #include <libavformat/avformat.h>
    #include <libavcodec/avcodec.h>
    #include <libavutil/imgutils.h>
    #include <libswscale/swscale.h>
    #include <SDL.h>
}



void log_s(const char * msg, int d = -1123) {
    if (d == -1123) {
        printf("%s\n", msg);
    }
    else {
        printf("%s  %d \n", msg, d);
    }
}


int thread_exit = 0;
int thread_pause = 0;

int sfp_refresh_thread(void *opaque) {
    while (!thread_exit) {
        if (!thread_pause) {
            SDL_Event event;
            event.type = SFM_REFRESH_EVENT;
            SDL_PushEvent(&event);
        }
        SDL_Delay(10);
    }
    thread_exit = 0;
    thread_pause = 0;
    //Break
    SDL_Event event;
    event.type = SFM_BREAK_EVENT;
    SDL_PushEvent(&event);

    return 0;
}

int play(const char*  filePath) {
    AVFormatContext * pFmtCtx = NULL;
    AVCodecContext *pCodecCtx = NULL;
    AVFrame *pFrame = NULL;
    AVFrame *pFrameYUV = NULL;
    uint8_t *outBuffer = NULL;
    AVPacket *pPacket = NULL;
    SwsContext *pSwsCtx = NULL;

    //SDL
    int screen_w, screen_h;
    SDL_Window *screen=NULL;
    SDL_Renderer* sdlRenderer = NULL;
    SDL_Texture* sdlTexture = NULL;
    SDL_Thread *video_tid = NULL;
    SDL_Event event;


    //1. 初始化
    av_register_all();
    avformat_network_init();
    //2. AVFormatContext获取
    pFmtCtx = avformat_alloc_context();
    //3. 打开文件
    if (avformat_open_input(&pFmtCtx, filePath, NULL, NULL) != 0) {
        log_s("Couldn't open input stream.\n");
        return -1;
    }
    //4. 获取文件信息
    if (avformat_find_stream_info(pFmtCtx, NULL)<0) {
        log_s("Couldn't find stream information.");
        return -1;
    }
    //5. 获取视频的index
    int i = 0, videoIndex = -1;
    for (; i < pFmtCtx->nb_streams; i++) {
        if (pFmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoIndex = i;
            break;
        }
    }

    if (videoIndex == -1) {
        log_s("Didn't find a video stream.");
        return -1;
    }
    //6. 获取解码器并打开
    pCodecCtx = avcodec_alloc_context3(NULL);
    if (avcodec_parameters_to_context(pCodecCtx, pFmtCtx->streams[videoIndex]->codecpar) < 0) {
        log_s("Didn't parameters to contex.");
        return -1;
    }
    AVCodec *pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
    if (pCodec == NULL) {
        log_s("Codec not found.");
        return -1;
    }
    if (avcodec_open2(pCodecCtx, pCodec, NULL)<0) {//打开解码器
        log_s("Could not open codec.");
        return -1;
    }
    //7. 解码播放开始准备工作
    pFrame = av_frame_alloc();
    pFrameYUV = av_frame_alloc();

    //根据需要解码的类型,获取需要的buffer,不要忘记free
    outBuffer = (uint8_t *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 1) * sizeof(uint8_t));
    //根据指定的图像参数和提供的数组设置数据指针和行数 ,数据填充到对应的pFrameYUV里面
    av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize, outBuffer, AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 1);

    pPacket = av_packet_alloc();
    log_s("--------------- File Information ----------------");
    av_dump_format(pFmtCtx, 0, filePath, 0);
    log_s("-------------------------------------------------");
    //获取SwsContext
    pSwsCtx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
                                pCodecCtx->width, pCodecCtx->height,
                                    AV_PIX_FMT_YUV420P, NULL, NULL, NULL, NULL);
    //----------------------------------------------------------------------------------------------------------------
    // 8. SDL相关初始化
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
        log_s("Could not initialize SDL - ");
        log_s(SDL_GetError());
        return -1;
    }
    screen_w = pCodecCtx->width;
    screen_h = pCodecCtx->height;
    screen = SDL_CreateWindow("WS ffmpeg player", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
        pCodecCtx->width/2, pCodecCtx->height/2, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
    if (!screen) {
        log_s("SDL: could not create window - exiting");
        log_s(SDL_GetError());
        return -1;
    }

    sdlRenderer = SDL_CreateRenderer(screen, -1, 0);
    //IYUV: Y + U + V  (3 planes)
    //YV12: Y + V + U  (3 planes)
    sdlTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, pCodecCtx->width, pCodecCtx->height);



    video_tid = SDL_CreateThread(sfp_refresh_thread, NULL, NULL);
    //----------------------------------------------------------------------------------------------------------------


    int count = 0;

    //9.读取数据播放
    for (;;) {
        //Wait
        SDL_WaitEvent(&event);


        SDL_Rect sdlRect;
        sdlRect.x = 0;
        sdlRect.y = 0;
        sdlRect.w = screen_w;
        sdlRect.h = screen_h;

        if (event.type == SFM_REFRESH_EVENT) {
            if (av_read_frame(pFmtCtx, pPacket) == 0) {
                if (pPacket->stream_index == videoIndex) {

                    int ret = avcodec_send_packet(pCodecCtx, pPacket);
                    if(ret != 0){
                        log_s("Decode end or Error.");
                        break;
                    }
                    /*  avcodec_send_packet和avcodec_receive_frame调用关系并不一定是一对一的,
                        比如一些音频数据一个AVPacket中包含了1秒钟的音频,调用一次avcodec_send_packet之后,
                        可能需要调用25次 avcodec_receive_frame才能获取全部的解码音频数据   */
                    while(avcodec_receive_frame(pCodecCtx, pFrame) == 0)
                    {
                        //读取到一帧音频或者视频,处理解码数据
                        if (sws_scale(pSwsCtx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
                            pFrameYUV->data, pFrameYUV->linesize) == 0)
                        {
                            log_s("sws_scale!");
                            continue;
                        }

                        count++;

                        //SDL播放---------------------------
                        SDL_UpdateTexture(sdlTexture, NULL, pFrameYUV->data[0], pFrameYUV->linesize[0]);
                        SDL_RenderClear(sdlRenderer);
                        //SDL_RenderCopy( sdlRenderer, sdlTexture, &sdlRect, &sdlRect);
                        SDL_RenderCopy(sdlRenderer, sdlTexture, &sdlRect, NULL);
                        SDL_RenderPresent(sdlRenderer);
                        //SDL End-----------------------

                        log_s("Succeed to play frame!", count);
                    }

                }
            }else {
                //退出线程
                thread_exit = 1;
                av_packet_unref(pPacket);
            }
        }
        else if (event.type == SDL_KEYDOWN) {
            log_s("Pause");
            //Pause
            if (event.key.keysym.sym == SDLK_SPACE)
                thread_pause = !thread_pause;
        }
        else if (event.type == SDL_QUIT) {
            log_s("quit");
            thread_exit = 1;
            break;
        }
        else if (event.type == SFM_BREAK_EVENT) {
            log_s("break");
            break;
        }
    }
    //sdl退出
    SDL_Quit();

    //回收
    if (pSwsCtx != NULL) {
        sws_freeContext(pSwsCtx);
    }
    if (outBuffer != NULL) {
        av_free(outBuffer);
    }
    if (pFrameYUV != NULL) {
        av_frame_free(&pFrameYUV);
    }
    if (pFrame != NULL) {
        av_frame_free(&pFrame);
    }
    if (pCodecCtx != NULL) {
        avcodec_close(pCodecCtx);
    }
    if (pFmtCtx != NULL) {
        avformat_close_input(&pFmtCtx);
    }
}


int main()
{
    play("/home/lili/QLED.mp4");
    getchar();
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值