• cocos2dx windows 播放mp4视频


    mp4解码过程主要通过ffmpeg实现,解码转出rgba格式后,通过CCSprite绘制出来。

    VideoPanel.h
    extern "C"
    {
    #include <libavcodec/avcodec.h>
    #include <libavdevice/avdevice.h>
    #include<libavfilter/avfilter.h>
    #include <libswscale/swscale.h>
    #include <libswresample/swresample.h>
    #include <libavformat/avformat.h>
    #include <libavutil/avutil.h>
    #include "libavutil/imgutils.h"
    };
    
    
    struct FrameData
    {
        void*    pData;
        int        iLen;
        int        iWidth;
        int        iHeight;
        int        iTime; //显示时间戳
        int        iIndex;
    
        FrameData(void*    _pData, int    _iLen, int _iWidth, int _iHeight, float _iTime, int _iIndex)
        {
            iLen = _iLen;
            iWidth = _iWidth;
            iHeight = _iHeight;
            iTime = _iTime;
            iIndex = _iIndex;
    
            pData = malloc(iLen);
            memcpy(pData, _pData, iLen);
        }
    };
    
    
    class VideoPanel : public Layer
    {
    public:
        VideoPanel();
        ~VideoPanel();
    
        static VideoPanel*        create();
        bool                    init();
        bool                    play(const std::string& strUrl);
        void                    pause();
    
        void                    resize(int iWidth, int iHeight);
    
        bool                    readFile(const std::string& strUrl);
        void                    run(float fDelay);
        void                    sliderEvent(Ref *pSender, ui::Slider::EventType type);
        void                    menuCallback(Ref* pSender);
        void                    closeCallback(Ref* pSender);
    
        void                    cacheFrameData();//用线程缓存帧数据
        void                    setTipKey(const std::string& strKey);
    
    private:
        Sprite*                m_pVideoSp;
    
        AVFormatContext*    m_pFormatContext;
        AVCodecContext*        m_pVideoCodecContext;
        AVCodecContext*        m_pAudioCodecContext;
    
        int                    m_iVideoIndex;
        int                    m_iAudioIndex;
    
        AVFrame*            m_pVideoFrame;
        AVFrame*            m_pVideoFrameYUV;
        AVFrame*            m_pAudioFrame;
    
        int                    m_iOutBufferSize;
        void*                m_pOutBuffer;
        SwsContext*            m_pVideoSwSContext;
        SwrContext*            m_pAudioSwrContext;
    
        int                    m_iOutChannelNum;//输出的声道个数
        void*                m_pOutAudioBuffer;
    
        AVPacket*            m_pPacket;
        bool                m_bPause;
        bool                m_bExitThread;
        AVSampleFormat        m_OutSampleFormat;//音频输出采样率格式
    
    
        vector<FrameData*>    m_vtFrameData;
        int                    m_iCurFrameIndex;
    
        int                    m_iTotalTime;//视频总时长
        int                    m_iTotalFrameNum;//视频总帧数
        AVRational            m_sTimeBase;//时间基
        int                    m_iFrameRate;//帧率
    
    };

    VideoPanel.cpp

    #include "VideoPanel.h"
    
    
    #define MAX_CACHE_FRAME_NUM 300        
    
    
    VideoPanel::VideoPanel()
        : m_pVideoSp(nullptr)
        , m_iCurFrameIndex(0)
    {
    }
    
    VideoPanel::~VideoPanel()
    {
        for (FrameData* pData : m_vtFrameData)
        {
            free(pData->pData);
            delete pData;
        }
    
        m_vtFrameData.clear();
    }
    
    VideoPanel* VideoPanel::create()
    {
        VideoPanel *pNode = new VideoPanel();
        if (pNode != nullptr && pNode->init())
        {
            pNode->autorelease();
        }
        else
        {
            CC_SAFE_DELETE(pNode);
        }
    
        return pNode;
    }
    
    bool VideoPanel::init()
    {
        RETURN_IF(!Layer::init(), false);
    
        return true;
    }
    
    bool VideoPanel::play(const std::string& strUrl)
    {
        if (!readFile(strUrl))
        {
            return false;
        }
    
        m_bPause = false;
    
        _scheduler->schedule(schedule_selector(VideoPanel::run), this, 1.0f / m_iFrameRate, false);
    
        m_bExitThread = false;
        thread t(&VideoPanel::cacheFrameData, this);
        t.join();
    
        return true;
    }
    
    void VideoPanel::pause()
    {
        m_bPause = true;
    }
    
    
    bool VideoPanel::readFile(const std::string& strUrl)
    {
        AVFormatContext* pContext = avformat_alloc_context();  // 获取文件信息上下文初始化
        m_pFormatContext = pContext;
    
        int iError = avformat_open_input(&m_pFormatContext, strUrl.c_str(), nullptr, nullptr);
        if (iError != 0)
        {
            LOG_ERROR("avformat_open_input fail");
            return false;
        }
    
        m_iVideoIndex = -1;
        m_iAudioIndex = -1;
    
        // 获取流的通道
        for (int iIndex = 0; iIndex < pContext->nb_streams; iIndex++)
        {
            if (pContext->streams[iIndex]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
            {
                m_iVideoIndex = iIndex;
                LOG_INFO("video index : %d", m_iVideoIndex);
            }
            if (pContext->streams[iIndex]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
            {
                m_iAudioIndex = iIndex;
                LOG_INFO("audio index : %d", m_iAudioIndex);
            }
        }
    
        if (m_iVideoIndex == -1)
        {
            LOG_INFO("Couldn't find a video stream");
            return false;
        }
    
        // 视频流处理
        if (m_iVideoIndex > -1)
        {
            //获取视频流中的编解码上下文
            AVCodecParameters* pCodecPar = pContext->streams[m_iVideoIndex]->codecpar;
    
            //根据编解码上下文中的编码id查找对应的解码
            AVCodec* pCodecVideo = avcodec_find_decoder(pCodecPar->codec_id);
            if (pCodecVideo == nullptr)
            {
                LOG_INFO("Couldn't find a video codec");
                return false;
            }
    
            m_pVideoCodecContext = avcodec_alloc_context3(pCodecVideo);
            //事实上codecpar包含了大部分解码器相关的信息,这里是直接从AVCodecParameters复制到AVCodecContext
            avcodec_parameters_to_context(m_pVideoCodecContext, pCodecPar);
            //av_codec_set_pkt_timebase(m_pVideoCodecContext, pContext->streams[m_iVideoIndex]->time_base);
    
            //打开编码器
            if (avcodec_open2(m_pVideoCodecContext, pCodecVideo, nullptr) < 0)
            {
                cout << "编码器无法打开" << endl;
                return -1;
            }
    
            //输出视频信息
            const char* pszFormat = pContext->iformat->name;
            m_iTotalTime = (pContext->duration) / 1000000;
            int iWidth = m_pVideoCodecContext->width;
            int iHeight = m_pVideoCodecContext->height;
            m_sTimeBase = pContext->streams[m_iVideoIndex]->time_base;
            m_iFrameRate = pContext->streams[m_iVideoIndex]->r_frame_rate.num;
            m_iTotalFrameNum = pContext->streams[m_iVideoIndex]->nb_frames;
            m_pProgressBar->setMaxPercent(m_iTotalTime);
    
            LOG_INFO("video format:%s, length:%d, width:%d, height:%d", pszFormat, m_iTotalTime, iWidth, iHeight);
    
            //准备读取
            //AVFrame用于存储解码后的像素数据(YUV)
            m_pVideoFrame = av_frame_alloc();//内存分配
            //YUV420
            m_pVideoFrameYUV = av_frame_alloc();
            //只有指定了AVFrame的像素格式、画面大小才能真正分配内存
            //缓冲区分配内存
            m_iOutBufferSize = av_image_get_buffer_size(AV_PIX_FMT_RGBA, iWidth, iHeight, 1);
    
            m_pOutBuffer = av_malloc(m_iOutBufferSize);
            //初始化缓冲区
            av_image_fill_arrays(m_pVideoFrameYUV->data, m_pVideoFrameYUV->linesize,
                (uint8_t*)m_pOutBuffer, AV_PIX_FMT_RGBA,
                iWidth, iHeight, 1);
            //用于转码(缩放)的参数,转之前的宽高,转之后的宽高,格式等
            m_pVideoSwSContext = sws_getContext(iWidth, iHeight, AV_PIX_FMT_YUV420P /*pCodecCtx->pix_fmt*/,
                iWidth, iHeight, AV_PIX_FMT_RGBA, SWS_BICUBIC, nullptr, nullptr, nullptr);
        }
    
        //缓冲区,开辟空间
        m_pPacket = (AVPacket*)av_malloc(sizeof(AVPacket));
    
        return true;
    }
    
    void VideoPanel::run(float fDelay)
    {
        RETURN_VOID_IF(m_bPause);
        RETURN_VOID_IF(m_iCurFrameIndex >= m_vtFrameData.size());
    
        FrameData* pFrameData = m_vtFrameData[m_iCurFrameIndex];
        int iWidth = pFrameData->iWidth;
        int iHeight = pFrameData->iHeight;
    
        if (m_pVideoSp == nullptr)
        {
            Image* image = new (std::nothrow) Image;
            image->initWithRawData((const unsigned char *)pFrameData->pData, pFrameData->iLen,
                iWidth, iHeight, 0, true);
    
            _director->getTextureCache()->removeTextureForKey("videoTexture");
            Texture2D* pTexture = _director->getTextureCache()->addImage(image, "videoTexture");
    
            m_pVideoSp = Sprite::createWithTexture(pTexture);
            m_pVideoSp->setAnchorPoint(Vec2::ZERO);
            m_pVideoSp->setPosition(Vec2(0, BOTTOM_HEIGHT));
            addChild(m_pVideoSp);
    
            if (iWidth < DEFAULT_WIDTH)
            {
                float fScale = (float)(DEFAULT_WIDTH / iWidth);
                m_pVideoSp->setScale(fScale);
    
                int width = iWidth * fScale;
                int height = iHeight * fScale;
                resize(width, height + BOTTOM_HEIGHT);
            }
            else
            {
                resize(iWidth, iHeight + BOTTOM_HEIGHT);
            }
    
            delete image;
        }
        else
        {
            m_pVideoSp->getTexture()->updateWithData(pFrameData->pData, 0, 0, iWidth, iHeight);
        }
    
    
        m_iCurFrameIndex++;
    }
    
    
    void VideoPanel::cacheFrameData()
    {
        AVStream* pVideoStream = m_pFormatContext->streams[m_iVideoIndex];
        int iIndex = 0;
    
        while (av_read_frame(m_pFormatContext, m_pPacket) >= 0 && !m_bExitThread)
        {
            int iRet = 0;
            if (m_pPacket->stream_index == m_iVideoIndex)
            {
                //解码一帧视频压缩数据,得到视频像素数据
                iRet = avcodec_send_packet(m_pVideoCodecContext, m_pPacket);
                //iRet = avcodec_decode_video2(m_pVideoCodecContext, m_pVideoFrame, &iCurFrame, m_pPacket);
                if (iRet != 0)
                {
                    LOG_ERROR("av send packet error");
                    return;
                }
    
                //读取到一帧音频或者视频
                if (avcodec_receive_frame(m_pVideoCodecContext, m_pVideoFrame) == 0) {
                    //AVFrame转为像素格式RGB24,宽高
                    sws_scale(m_pVideoSwSContext, m_pVideoFrame->data, m_pVideoFrame->linesize,
                        0, m_pVideoCodecContext->height, m_pVideoFrameYUV->data, m_pVideoFrameYUV->linesize);
    
                    int iWidth = m_pVideoCodecContext->width;
                    int iHeight = m_pVideoCodecContext->height;
    
                    FrameData* pFrameData = new FrameData(m_pOutBuffer, m_iOutBufferSize, iWidth, iHeight, 
                        m_pVideoFrame->pts * av_q2d(m_sTimeBase), iIndex++);
                    m_vtFrameData.push_back(pFrameData);
                }
            }
        }
        
        av_packet_unref(m_pPacket);
    }
    
    #endif

    调用方法:

    VideoPanel* player = VideoPanel::create();
    player->play("E:\cocosvideo.mp4");
    Director::getInstance()->getRunningScene()->addChild(player);
    player->show();

    最终加上进度条和按钮的效果:

    如需要支持有偿服务,加q:980550823

    转载请注明出处,from 博客园HemJohn

  • 相关阅读:
    QDUOJ 来自xjy的签到题(bfs+状压dp)
    HDU
    【原创+整理】线程同步之详解自旋锁
    【原创】浅说windows下的中断请求级IRQL
    【原创】驱动开发中Memory read error导致的蓝屏问题
    [转&精]IO_STACK_LOCATION与IRP的一点笔记
    【原创】《windows驱动开发技术详解》第4章实验总结二
    【原创】《windows驱动开发技术详解》第4章实验总结一
    【转载】LINUX 和 WINDOWS 内核的区别
    【原创】Windows服务管家婆之Service Control Manager
  • 原文地址:https://www.cnblogs.com/HemJohn/p/13050161.html
Copyright © 2020-2023  润新知