美文网首页
结合ffmpeg用SDL播放YUV实现简易播放器

结合ffmpeg用SDL播放YUV实现简易播放器

作者: 曾大稳丶 | 来源:发表于2018-06-20 14:50 被阅读0次

    通过解码之后得到的yuv视频数据我们直接可以进行播放,本篇使用SDL来实现视频播放。

    SDL环境配置:https://blog.csdn.net/furzoom/article/details/53992124

    SDL视频播放基础教程: https://blog.csdn.net/xuyankuanrong/article/details/77574152

    有了上一篇文章的解码的基础,代码看着就很轻松了。

    代码上写的很详细了,具体看代码

    
    
    #include "stdafx.h"
    #include <iostream>
    
    // LNK2019  无法解析的外部符号 SDL_main,该符号在函数 main_utf8 中被引用
    #define SDL_MAIN_HANDLED 
    
    //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 <libsdl/SDL.h>
    }
    
    
    
    void log_s(const char * msg, int d = -1123) {
        if (d == -1123) {
            printf_s("%s\n", msg);
        }
        else {
            printf_s("%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_Rect sdlRect;
        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_OPENGL);
        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);
    
        sdlRect.x = 0;
        sdlRect.y = 0;
        sdlRect.w = screen_w;
        sdlRect.h = screen_h;
    
        video_tid = SDL_CreateThread(sfp_refresh_thread, NULL, NULL);
        //----------------------------------------------------------------------------------------------------------------
    
    
        int count = 0;
    
        //9.读取数据播放
        for (;;) {
            //Wait
            SDL_WaitEvent(&event);
            if (event.type == SFM_REFRESH_EVENT) {
                if (av_read_frame(pFmtCtx, pPacket) == 0) {
                    if (pPacket->stream_index == videoIndex) {
                        if (avcodec_send_packet(pCodecCtx, pPacket) != 0) {//解码一帧压缩数据
                            log_s("Decode end or Error.");
                            break;
                        }
                        else {//处理解码数据
                            avcodec_receive_frame(pCodecCtx, pFrame);
    
                            if (sws_scale(pSwsCtx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
                                pFrameYUV->data, pFrameYUV->linesize) == 0) {
                                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("F:/视频资源/gxsp.mp4");
        getchar();
        return 0;
    }
    
    
    
    
    
    

    参考链接:https://blog.csdn.net/king1425/article/details/71171142

    相关文章

      网友评论

          本文标题:结合ffmpeg用SDL播放YUV实现简易播放器

          本文链接:https://www.haomeiwen.com/subject/dxkxyftx.html