用mfc编写
// CGPlayerDemoDlg.h : 头文件 // #pragma once #include "CGPlayer-SDL.h" using namespace ChunGen::Client::Player; // CCGPlayerDemoDlg 对话框 class CCGPlayerDemoDlg : public CDialogEx { // 构造 public: CCGPlayerDemoDlg(CWnd* pParent = NULL); // 标准构造函数 // 对话框数据 enum { IDD = IDD_CGPLAYERDEMO_DIALOG }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 // 实现 protected: HICON m_hIcon; // 生成的消息映射函数 virtual BOOL OnInitDialog(); afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); DECLARE_MESSAGE_MAP() public: afx_msg void OnBnClickedStartBut(); afx_msg void OnBnClickedStopBut(); private: CGPlayerSdl *cgPlayer; bool isPlay; HWND handle; };CGPlayerDemoDlg.h
#include "stdafx.h" #include "CGPlayerDemo.h" #include "CGPlayerDemoDlg.h" #include "afxdialogex.h" #ifdef _DEBUG #define new DEBUG_NEW #endif // 用于应用程序“关于”菜单项的 CAboutDlg 对话框 class CAboutDlg : public CDialogEx { public: CAboutDlg(); // 对话框数据 enum { IDD = IDD_ABOUTBOX }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 // 实现 protected: DECLARE_MESSAGE_MAP() }; CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD) { } void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); } BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx) END_MESSAGE_MAP() // CCGPlayerDemoDlg 对话框 CCGPlayerDemoDlg::CCGPlayerDemoDlg(CWnd* pParent /*=NULL*/) : CDialogEx(CCGPlayerDemoDlg::IDD, pParent) { m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); } void CCGPlayerDemoDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); } BEGIN_MESSAGE_MAP(CCGPlayerDemoDlg, CDialogEx) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDC_START_BUT, &CCGPlayerDemoDlg::OnBnClickedStartBut) ON_BN_CLICKED(IDC_STOP_BUT, &CCGPlayerDemoDlg::OnBnClickedStopBut) END_MESSAGE_MAP() // CCGPlayerDemoDlg 消息处理程序 BOOL CCGPlayerDemoDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // 将“关于...”菜单项添加到系统菜单中。 // IDM_ABOUTBOX 必须在系统命令范围内。 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { BOOL bNameValid; CString strAboutMenu; bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX); ASSERT(bNameValid); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } // 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动 // 执行此操作 SetIcon(m_hIcon, TRUE); // 设置大图标 SetIcon(m_hIcon, FALSE); // 设置小图标 // TODO: 在此添加额外的初始化代码 cgPlayer = nullptr; isPlay = false; CString str("rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov"); //CString str("d://test.mp4"); GetDlgItem(IDC_URL)->SetWindowText(str); return TRUE; // 除非将焦点设置到控件,否则返回 TRUE } void CCGPlayerDemoDlg::OnSysCommand(UINT nID, LPARAM lParam) { if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); } else { CDialogEx::OnSysCommand(nID, lParam); } } // 如果向对话框添加最小化按钮,则需要下面的代码 // 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序, // 这将由框架自动完成。 void CCGPlayerDemoDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // 用于绘制的设备上下文 SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0); // 使图标在工作区矩形中居中 int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // 绘制图标 dc.DrawIcon(x, y, m_hIcon); } else { CDialogEx::OnPaint(); } } //当用户拖动最小化窗口时系统调用此函数取得光标 //显示。 HCURSOR CCGPlayerDemoDlg::OnQueryDragIcon() { return static_cast<HCURSOR>(m_hIcon); } void CCGPlayerDemoDlg::OnBnClickedStartBut() { // TODO: 在此添加控件通知处理程序代码 if (!isPlay) { handle = GetDlgItem(IDC_PICTURE)->GetSafeHwnd(); cgPlayer = new CGPlayerSdl(); cgPlayer->Init(); CString strUrl; GetDlgItem(IDC_URL)->GetWindowText(strUrl); std::string url = (CStringA)strUrl; bool ret = cgPlayer->Open(url); if (ret < 0) { delete cgPlayer; cgPlayer = nullptr; return; } cgPlayer->Play(handle); isPlay = true; } } void CCGPlayerDemoDlg::OnBnClickedStopBut() { // TODO: 在此添加控件通知处理程序代码 if (isPlay) { if (cgPlayer) { cgPlayer->Close(); delete cgPlayer; cgPlayer = nullptr; isPlay = false; } } }CCGPlayerDemoDlg
/* Copyright [c] 2018-2028 www.chungen90.com Allrights Reserved*/ #ifndef CG_PLAYER_SDL_H #define CG_PLAYER_SDL_H #include <string> #include <windows.h> namespace ChunGen{ namespace Client {namespace Player { class SdlPlayer; class CGPlayerSdl { public: CGPlayerSdl(); void Init(); int Open(std::string inputUrl); void Play(HWND handle); void Close(); private: SdlPlayer* sdlPlayer; }; }}} #endifCGPlayer-SDL.h
/* Copyright [c] 2018-2028 www.chungen90.com Allrights Reserved*/ #include "CGPlayer-SDL.h" #include "SdlPlayer.h" using namespace ChunGen::Client::Player; CGPlayerSdl::CGPlayerSdl() { sdlPlayer = nullptr; } void CGPlayerSdl::Init() { if(!sdlPlayer) { sdlPlayer = new SdlPlayer(); sdlPlayer->Init(); } } int CGPlayerSdl::Open(std::string inputUrl) { int ret = -1; if(sdlPlayer) { ret = sdlPlayer->OpenInput(inputUrl); } return ret; } void CGPlayerSdl::Play(HWND handle) { if(sdlPlayer) { sdlPlayer->Play(handle); } } void CGPlayerSdl::Close() { if(sdlPlayer) { sdlPlayer->Close(); delete sdlPlayer; sdlPlayer = nullptr; } }CGPlayer-SDL.cpp
/* Copyright [c] 2018-2028 www.chungen90.com Allrights Reserved*/ #ifndef SDL_PLAYER_H #define SDL_PLAYER_H #include <string> #include "pch.h" #include <memory> #include "CGSdlRender.h" #include <thread> namespace ChunGen{ namespace Client {namespace Player { enum CGPlayState { PClose = 0, POpen, PStart }; class SdlPlayer { public: SdlPlayer() :inputContext(nullptr) ,lastReadPacktTime(av_gettime()) ,timeout(10) ,sdlRender(nullptr) ,videoIndex(-1) ,audioIndex(-1) ,playState(CGPlayState::PClose) ,swr(nullptr) { } void Init() { av_register_all(); avfilter_register_all(); avformat_network_init(); av_log_set_level(AV_LOG_ERROR); } int OpenInput(std::string inputUrl) //rtsp://admin:admin12345@192.168.1.64:554/h264/ch1/main/av_stream { inputContext = avformat_alloc_context(); lastReadPacktTime = av_gettime(); inputContext->interrupt_callback.opaque = this; inputContext->interrupt_callback.callback = interrupt_cb; int ret = avformat_open_input(&inputContext, inputUrl.c_str(), nullptr,nullptr); if(ret < 0) { av_log(NULL, AV_LOG_ERROR, "Input file open input failed\n"); return ret; } ret = avformat_find_stream_info(inputContext,nullptr); if(ret < 0) { av_log(NULL, AV_LOG_ERROR, "Find input file stream inform failed\n"); } else { for(int i = 0; i < inputContext->nb_streams; i++) { if(inputContext->streams[i]->codec->codec_type == AVMediaType::AVMEDIA_TYPE_VIDEO) { videoIndex = i; } else if(inputContext->streams[i]->codec->codec_type == AVMediaType::AVMEDIA_TYPE_AUDIO) { audioIndex = i; } } playState = CGPlayState::POpen; av_log(NULL, AV_LOG_FATAL, "Open input file %s success\n",inputUrl.c_str()); } return ret; } int Play(HWND handle) { int ret = -1; if(playState != CGPlayState::POpen) ret; sdlRender =std::make_shared<CGSDLRender>(); if(videoIndex >= 0) { ret = initVideoDecodeContext(); if(ret < 0) return ret; sdlRender->InitVideo(handle,0); sdlRender->CreateVideoSurface(inputContext->streams[videoIndex]->codec->width, inputContext->streams[videoIndex]->codec->height); } if(audioIndex >= 0) { ret = initAudioDecodeContext(); if(ret < 0) return ret; sdlRender->InitAudio(inputContext->streams[audioIndex]->codec->sample_rate,inputContext->streams[audioIndex]->codec->channels); } playState = CGPlayState::PStart; task.swap(std::thread([=]{ AVFrame * videoFrame = av_frame_alloc(); AVFrame * audioFrame = av_frame_alloc(); while(playState == CGPlayState::PStart) { auto packet = readPacketFromSource(); if(packet) { if(packet->stream_index == videoIndex) { if(videoDecode(packet.get(),videoFrame)) { playVideo(videoFrame); } } else if(packet->stream_index == audioIndex) { if(audioDecode(packet.get(),videoFrame)) { playAudio(videoFrame); } } } } av_frame_free(&videoFrame); av_frame_free(&audioFrame); })); return ret; } void Close() { playState = CGPlayState::PClose; if(sdlRender) { sdlRender->Close(); sdlRender = nullptr; } if(task.joinable()) { task.join(); } if(inputContext) { for(int i = 0 ; i < inputContext->nb_streams; i++) { AVCodecContext *codecContext = inputContext->streams[i]->codec; avcodec_close(codecContext); } avformat_close_input(&inputContext); inputContext = nullptr; } if(swr) { swr_free(&swr); swr = nullptr; } } private: static int interrupt_cb(void *ctx) { SdlPlayer* sdlPlayer = (SdlPlayer* )ctx; if(av_gettime() - sdlPlayer->lastReadPacktTime > sdlPlayer->timeout *1000 *1000) { return -1; } return 0; } std::shared_ptr<AVPacket> readPacketFromSource() { std::shared_ptr<AVPacket> packet(static_cast<AVPacket*>(av_malloc(sizeof(AVPacket))), [&](AVPacket *p) { av_packet_free(&p); av_freep(&p);}); av_init_packet(packet.get()); lastReadPacktTime = av_gettime(); int ret = av_read_frame(inputContext, packet.get()); if(ret >= 0) { return packet; } else { return nullptr; } } int initVideoDecodeContext() { auto codecId = inputContext->streams[videoIndex]->codec->codec_id; auto codec = avcodec_find_decoder(codecId); if (!codec) { return -1; } int ret = avcodec_open2(inputContext->streams[videoIndex]->codec, codec, NULL); return ret; } bool videoDecode(AVPacket* packet, AVFrame *frame) { int gotFrame = 0; auto hr = avcodec_decode_video2(inputContext->streams[videoIndex]->codec, frame, &gotFrame, packet); if (hr >= 0 && gotFrame != 0) { return true; } return false; } int initAudioDecodeContext() { int ret = -1; auto codecId = inputContext->streams[audioIndex]->codec->codec_id; auto codec = avcodec_find_decoder(codecId); if (!codec) { return ret; } ret = avcodec_open2(inputContext->streams[audioIndex]->codec, codec, NULL); if(ret < 0) return ret; if(inputContext->streams[audioIndex]->codec->sample_fmt != AV_SAMPLE_FMT_S16) { swr = swr_alloc(); av_opt_set_int(swr, "in_channel_layout", inputContext->streams[audioIndex]->codec->channel_layout, 0); av_opt_set_int(swr, "out_channel_layout", inputContext->streams[audioIndex]->codec->channel_layout, 0); av_opt_set_int(swr, "in_sample_rate", inputContext->streams[audioIndex]->codec->sample_rate, 0); av_opt_set_int(swr, "out_sample_rate", inputContext->streams[audioIndex]->codec->sample_rate, 0); av_opt_set_sample_fmt(swr, "in_sample_fmt", inputContext->streams[audioIndex]->codec->sample_fmt, 0); av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0); swr_init(swr); } return ret; } bool audioDecode(AVPacket* packet, AVFrame *frame) { int gotFrame = 0; auto hr = avcodec_decode_audio4(inputContext->streams[audioIndex]->codec, frame, &gotFrame, packet); if (hr >= 0 && gotFrame != 0) { return true; } return false; } void playVideo(AVFrame* frame) { sdlRender->Display(frame->data,frame->linesize); } void playAudio(AVFrame* frame) { if(swr) { int dstNbChannels = 1; int srcNbSamples = frame->nb_samples; int srcRate =inputContext->streams[audioIndex]->codec->sample_rate; int dstRate = inputContext->streams[audioIndex]->codec->sample_rate; int dstNbSamples = av_rescale_rnd(srcNbSamples, dstRate, srcRate, AV_ROUND_UP); AVSampleFormat dst_sample_fmt = AV_SAMPLE_FMT_S16; uint8_t** dst_data = nullptr; int dstLinesize; dstNbChannels = av_get_channel_layout_nb_channels(inputContext->streams[0]->codec->channel_layout); dstNbChannels = dstNbChannels > 0 ? dstNbChannels : 1; int ret = av_samples_alloc_array_and_samples(&dst_data, &dstLinesize, dstNbChannels, dstNbSamples, dst_sample_fmt, 0); ret = swr_convert(swr, dst_data, dstNbSamples, (const uint8_t **)frame->data, srcNbSamples); sdlRender->PlaySamples((BYTE*)dst_data[0], dstLinesize); if (dst_data) { av_freep((void*)&dst_data[0]); } av_freep(&dst_data); } else { sdlRender->PlaySamples(frame->data[0], frame->linesize[0]); } } private: AVFormatContext *inputContext; int64_t lastReadPacktTime ; int timeout; std::shared_ptr<CGSDLRender> sdlRender; std::thread task; int videoIndex; int audioIndex; CGPlayState playState; SwrContext* swr; }; }}}; #endifSdlPlayer.h
/* Copyright [c] 2018-2028 www.chungen90.com Allrights Reserved*/ #ifndef CG_SDL_RENDER_H #define CG_SDL_RENDER_H #include <SDL.h> #include <windows.h> #include <mutex> namespace ChunGen { namespace Client { namespace Player { class CGBlockRingBuffer { public: CGBlockRingBuffer() :buffer(nullptr) ,bufferSize(0) ,writeIndex(0) ,readIndex(0) ,bytesCanRead(0) { } void Init(int buffSize) { buffer = new uint8_t [buffSize]; if(!buffer) return; this->bufferSize = buffSize; } void Write(uint8_t *src, int size) { if(!src ||size == 0) return ; std::lock_guard<std::mutex> lk(lock); int bytesCanWrite = size < bufferSize - bytesCanRead ? size : (bufferSize - bytesCanRead); if(bytesCanWrite <= bufferSize - writeIndex) { memcpy(buffer + writeIndex,src,bytesCanWrite); writeIndex += bytesCanWrite; if(writeIndex == bufferSize) { writeIndex = 0; } } else { int room = bufferSize - writeIndex; memcpy(buffer + writeIndex, src,room); int left = bytesCanWrite - room; memcpy(buffer, src + room, left); writeIndex = left; } bytesCanRead += bytesCanWrite; } int Read(uint8_t *dst, int size) { if(!dst || size == 0) return 0; std::lock_guard<std::mutex> lk(lock); int bytesRead = size < bytesCanRead ? size : bytesCanRead; if(bytesRead <= bufferSize - readIndex) { memcpy(dst,buffer + readIndex,bytesRead); readIndex += bytesRead; if(readIndex == bufferSize) readIndex = 0; } else { int bytesHead = bufferSize - readIndex; memcpy(dst, buffer + readIndex,bytesHead); int bytesTail = bytesRead - bytesHead; memcpy(dst + readIndex,buffer, bytesTail); readIndex = bytesTail; } bytesCanRead -= bytesRead; return bytesRead; } private: uint8_t *buffer; int bufferSize; int readIndex; int writeIndex; int bytesCanRead; std::mutex lock; }; class CGSDLRender { public: CGSDLRender(); virtual ~CGSDLRender(void); int InitVideo(HWND hDisplayWindow, int adapterId = 0); int CreateVideoSurface(int width, int height); int Display(BYTE** data, int* linesize); int InitAudio(int samples,int channels); int PlaySamples(BYTE* buffer, int size) ; void Close(); private: SDL_Window *sdlWindow; SDL_Renderer *sdlRender; SDL_Texture *sdlTexture; int width; int height; CGBlockRingBuffer *blockingBuffer; //CircularBuffer *blockingBuffer; private: }; }}} #endifCGSdlRender.h
/* Copyright [c] 2018-2028 www.chungen90.com Allrights Reserved*/ #include "pch.h" #include "CGSDLRender.h" using namespace ChunGen::Client::Player; void callback(void *userdata, Uint8 *stream, int len) { if(userdata != nullptr) { CGBlockRingBuffer* blockingBuffer = (CGBlockRingBuffer*)userdata; auto len1 = 0; while (len > 0) { len1 = blockingBuffer->Read((uint8_t*)stream + len1,len); len = len - len1; } } } CGSDLRender::CGSDLRender() :blockingBuffer(nullptr) { } CGSDLRender::~CGSDLRender() { } int CGSDLRender::InitVideo(HWND hDisplayWindow, int adapterId) { SDL_Init(SDL_INIT_VIDEO); sdlWindow = SDL_CreateWindowFrom(hDisplayWindow); sdlRender = SDL_CreateRenderer(sdlWindow, -1, SDL_RendererFlags::SDL_RENDERER_ACCELERATED); return S_OK; } int CGSDLRender::CreateVideoSurface(int width, int height) { this->width = width; this->height = height; sdlTexture = SDL_CreateTexture(sdlRender, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING, width, height); return S_OK; } int CGSDLRender::Display(BYTE** data, int* linesize) { void* piexels = nullptr; int pitch; int ret = SDL_LockTexture(sdlTexture, NULL, &piexels, &pitch); if(ret < 0) return ret; uint8_t* yuv[3] = { (uint8_t*)piexels,(uint8_t*)piexels + pitch * height, (uint8_t*)piexels + pitch * height + ((pitch >> 1) * (height >> 1)) }; for (int i = 0; i < height; i++) { memcpy(yuv[0] + i * pitch, data[0] + i * linesize[0], linesize[0]); if (i % 2 == 0) { memcpy(yuv[1] + (i >> 1) * (pitch >> 1), data[2] + (i >> 1) * linesize[2], linesize[2]); memcpy(yuv[2] + (i >> 1) * (pitch >> 1), data[1] + (i >> 1) * linesize[1], linesize[1]); } } SDL_UnlockTexture(sdlTexture); SDL_RenderClear(sdlRender); SDL_RenderCopy(sdlRender, sdlTexture, NULL, NULL); SDL_RenderPresent(sdlRender); return S_OK; } void CGSDLRender::Close() { SDL_CloseAudio(); if(blockingBuffer) { delete blockingBuffer; } } int CGSDLRender::InitAudio(int samples,int channels) { SDL_Init(SDL_INIT_AUDIO); blockingBuffer = new CGBlockRingBuffer(); blockingBuffer->Init(4 * 1024); SDL_AudioSpec wanted; wanted.freq = samples; wanted.format = AUDIO_S16SYS; wanted.channels = channels > 2 ? 2 : channels; wanted.silence = 0; wanted.samples = 1024; wanted.callback = callback; wanted.userdata = this->blockingBuffer; // devid_out = SDL_OpenAudioDevice(NULL, SDL_FALSE, &wanted, &spec, SDL_AUDIO_ALLOW_ANY_CHANGE); auto r = (SDL_OpenAudio(&wanted,NULL)); SDL_PauseAudio(0); return r; } int CGSDLRender::PlaySamples(BYTE* buffer, int size) { this->blockingBuffer->Write(buffer,size); //SDL_QueueAudio(1,buffer,size); return S_OK; }CGSdlRender.cpp
#ifndef PCH_H #define PCH_H extern "C" { #include "libavutil/opt.h" #include "libavutil/channel_layout.h" #include "libavutil/common.h" #include "libavutil/imgutils.h" #include "libavutil/mathematics.h" #include "libavutil/samplefmt.h" #include "libavutil/time.h" #include "libavutil/fifo.h" #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libavformat/avio.h" //#include "libavfilter/avfiltergraph.h" #include "libavfilter/avfilter.h" #include "libavfilter/buffersink.h" #include "libavfilter/buffersrc.h" #include "libswscale/swscale.h" #include "libswresample/swresample.h" } #endifpch.h