• ffmpeg学习(三)——ffmpeg+SDL2 实现简单播放器


    本篇实现基于ffmpeg动态库用测试程序播放本地文件和RTSP视频流。

    参考文章:http://blog.csdn.net/leixiaohua1020/article/details/8652605

                  http://blog.csdn.net/guanghua2_0beta/article/details/37578299

    创建工程,参考上一篇文章:http://www.cnblogs.com/wenjingu/p/3990071.html,注意:下载SDL2库的开发版,lib文件放到lib文件夹下,dll放到debug文件夹下。

    代码如下,在参考文章的基础上做了少量改动,主要是将ffmpeg老版本的部分函数在替换为2.4版本中的新函数。

    #include "stdafx.h"
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    #include <libavcodec/avcodec.h>
    #include <libavdevice/avdevice.h>
    #include <libavformat/avformat.h>
    #include <libavfilter/avfilter.h>
    #include <libavutil/avutil.h>
    #include <libswscale/swscale.h>
    #include <SDL2/SDL.h>
    
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <math.h>
    
    #ifdef __cplusplus
    }
    #endif
    
    int ffplayer()
    {
        AVFormatContext    *pFormatCtx;
        int                i, videoindex;
        AVCodecContext    *pCodecCtx;
        AVCodec            *pCodec;
        //char filepath[]="F:\Work\ffmpegdemo\Debug\Wildlife.wmv";
        char rtspUrl[] = "rtsp://admin:12345@192.168.10.76:554";
        av_register_all();//注册组件
        avformat_network_init();//支持网络流
        pFormatCtx = avformat_alloc_context();//初始化AVFormatContext
        if(avformat_open_input(&pFormatCtx,/*filepath*/rtspUrl,NULL,NULL)!=0){//打开文件或网络流
            printf("无法打开文件
    ");
            return -1;
        }
        if(avformat_find_stream_info(pFormatCtx, NULL)<0)//查找流信息
        {
            printf("Couldn't find stream information.
    ");
            return -1;
        }
        videoindex=-1;
        for(i=0; i<pFormatCtx->nb_streams; i++) //获取视频流ID
            if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)
            {
                videoindex=i;
                break;
            }
        if(videoindex==-1)
        {
            printf("Didn't find a video stream.
    ");
            return -1;
        }
        pCodecCtx=pFormatCtx->streams[videoindex]->codec;
        pCodec=avcodec_find_decoder(pCodecCtx->codec_id);//查找解码器
        if(pCodec==NULL)
        {
            printf("Codec not found.
    ");
            return -1;
        }
        if(avcodec_open2(pCodecCtx, pCodec, NULL)<0)//打开解码器
        {
            printf("Could not open codec.
    ");
            return -1;
        }
        AVFrame    *pFrame,*pFrameYUV;
        pFrame=avcodec_alloc_frame();//存储解码后AVFrame
        pFrameYUV=avcodec_alloc_frame();//存储转换后AVFrame
        uint8_t *out_buffer;
        out_buffer=new uint8_t[avpicture_get_size(PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height)];//分配AVFrame所需内存
        avpicture_fill((AVPicture *)pFrameYUV, out_buffer, PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);//填充AVFrame
    
        //------------SDL初始化--------
        if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {  
            printf( "Could not initialize SDL - %s
    ", SDL_GetError()); 
            return -1;
        } 
        SDL_Window *screen = SDL_CreateWindow("RTSP Client Demo",
                              SDL_WINDOWPOS_UNDEFINED,
                              SDL_WINDOWPOS_UNDEFINED,
                              pCodecCtx->width, pCodecCtx->height,
                              SDL_WINDOW_RESIZABLE/* SDL_WINDOW_HIDDEN*/| SDL_WINDOW_OPENGL);
        if(!screen) {  
            printf("SDL: could not set video mode - exiting
    ");  
            return -1;
        }
        SDL_Renderer* sdlRenderer = SDL_CreateRenderer(screen, -1, 0);  
        SDL_Texture* sdlTexture = SDL_CreateTexture(  
            sdlRenderer,  
            SDL_PIXELFORMAT_YV12,  
            SDL_TEXTUREACCESS_STREAMING,  
            pCodecCtx->width,  
            pCodecCtx->height); 
     
        SDL_Rect rect;
        //-----------------------------
        int ret, got_picture;
        static struct SwsContext *img_convert_ctx;
        int y_size = pCodecCtx->width * pCodecCtx->height;
    
        SDL_Event event; 
        AVPacket *packet=(AVPacket *)malloc(sizeof(AVPacket));//存储解码前数据包AVPacket
        av_new_packet(packet, y_size);
        //输出一下信息-----------------------------
        printf("文件信息-----------------------------------------
    ");
        //av_dump_format(pFormatCtx,0,filepath,0);
        printf("-------------------------------------------------
    ");
        //------------------------------
        while(av_read_frame(pFormatCtx, packet)>=0)//循环获取压缩数据包AVPacket
        {
            if(packet->stream_index==videoindex)
            {
                ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);//解码。输入为AVPacket,输出为AVFrame
                if(ret < 0)
                {
                    printf("解码错误
    ");
                    return -1;
                }
                if(got_picture)
                {
                    //像素格式转换。pFrame转换为pFrameYUV。
                    img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, 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显示--------
                    rect.x = 0;    
                    rect.y = 0;    
                    rect.w = pCodecCtx->width;    
                    rect.h = pCodecCtx->height;    
                    
                    SDL_UpdateTexture( sdlTexture, &rect, pFrameYUV->data[0], pFrameYUV->linesize[0] );  
                    SDL_RenderClear( sdlRenderer );  
                    SDL_RenderCopy( sdlRenderer, sdlTexture, &rect, &rect );  
                    SDL_RenderPresent( sdlRenderer );
                    //延时20ms
                    SDL_Delay(20);
                    //------------SDL-----------
                }
            }
            av_free_packet(packet);
            SDL_PollEvent(&event);  
            switch( event.type ) {  
                case SDL_QUIT:  
                    SDL_Quit();  
                    exit(0);  
                    break;  
                default:  
                    break;  
            }  
        }
    
        SDL_DestroyTexture(sdlTexture); 
        delete[] out_buffer;
        av_free(pFrameYUV);
        avcodec_close(pCodecCtx);
        avformat_close_input(&pFormatCtx);
    
        return 0;
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        ffplayer();
    
        return 0;
    }

    测试效果:

    播放rtsp网络流

    也可以播放本地文件,将rtspUrl替换为filepath即可。

  • 相关阅读:
    八、vue使用element-ui组件
    七、vue语法补充二(动态组件 & 异步组件、访问元素 & 组件、混入)
    oracle中使用sql语句生成10w条测试数据
    六、vue路由Vue Router
    五、vue状态管理模式vuex
    前端生态/工程化
    四、vue语法补充
    三、vue脚手架工具vue-cli的使用
    二、vue中组件的使用
    使用阿里云OSS上传文件
  • 原文地址:https://www.cnblogs.com/wenjingu/p/3990301.html
Copyright © 2020-2023  润新知