cocos2dx windows 播放mp4视频
生活随笔
收集整理的這篇文章主要介紹了
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
總結
以上是生活随笔為你收集整理的cocos2dx windows 播放mp4视频的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 单片机小白学步系列(十五) 单片机程序下
- 下一篇: poll