#pragma once
#include "VideoSourceServer/IVideoSourceServer.h"
#include "ConformRtmp/IRtmpConform.h"
#include "Buffer.h"
#include "TVCritSec.h"
#include "DrawVideoGDI.h"
#include "BitmapInfoHelpFunc.h"
#include "WaveFormatExHelpFun.h"
#include "ListboxInsertString.h"
#include "FormatString.h"
#include "StreamNumDef.h"
#include "StreamDataUnpackerUnpress.h"
#include "LogTool.h"
#include <atlconv.h>
#include <string>
#include <queue>
using namespace std;
#define TIMER_ID_PROCESS_MSG (100)
#define TIMER_ID_DRAW_VIDEO (101)
#define TIMER_ID_CONNECT_FMS (102)
#define TIMER_ID_UPDATA_INFO (103)
#define TIME_PROCESS_MSG (200)
#define TIME_DRAW_VIDEO (80)
#define TIME_CONNECT_FMS (1000)
#define TIME_UPDATA_INFO (1000)
class CMainDlg
: public CDialogImpl<CMainDlg>
, public CMessageFilter
, public IStreamCapNotfiy
, public IRtmpClientListen
{
private:
//
//视频数据
//
CTVCritSec m_lockBufVideo; //CTVAutoLock lockBufVideo(&m_lockBufVideo);
CBuffer m_bufVideo; //视频数据
int m_iVideoWidth;
int m_iVideoHeight;
bool m_bHaveVideo; //有新的视频数据,用于描画
//
//消息显示
//
CListboxInsertString m_listboxInsertStr;
CTVCritSec m_lockQueueStr; //CTVAutoLock lockQueueStr(&m_lockQueueStr);
queue<wstring> m_queueStr; //字符串消息队列,用于消息的显示
//
//音视频参数
//
WAVEFORMATEX m_audioFormat;
BITMAPINFO m_videoFormat;
UINT m_uFramePerSec;
wstring m_strVideoDevName;
wstring m_strAudioDevName;
DWORD m_dwVideoEncoderBitRate;
DWORD m_dwAudioEncoderBitRate;
double m_dVolum;
bool m_bMute;
wstring m_strCmdshow;
IStreamCap* m_pStreamCap;
//
//虚拟视频数据发送端
//
IVideoSourceServer* m_videoSourceServer;
//
//fms连接
//
//PCONN_INFO m_rtmpConInfo;
CTVCritSec m_lockFlash;
string m_strFmsAddr; //fms地址
//
//是否已经启动
//
bool m_bStart;
//
//控件变量
//
CComboBox m_comboxVideoDev; //视频设备
CComboBox m_comboxAudioDev; //音频设备
CTrackBarCtrl m_trackBarVolum; //音量
//
//信息显示
//
int m_iAudioAvgBitrate;
int m_iCountPerSecAudio;
int m_iVideoAvgBitrate;
int m_iCountPerSecVideo;
bool m_bNeedUpdataInfoShow;
//
//时间戳计算
//
__int64 m_rtLastVideoSampleTime;
__int64 m_rtLastAudioSampleTime;
bool m_bFirstSample; //是否是第一帧数据
public:
enum { IDD = IDD_DIALOG };
CMainDlg(
wstring &strCmdshow,
int iVideoWidth,
int iVideoHeight,
int iFps,
int iVideoBitrate,
int iAudioBitrate,
wstring &strVideoDev,
wstring &strAudioDev,
int iAudioSampleRate,
wstring &strRtmpAddr
)
: m_bHaveVideo(false)
, m_iVideoWidth(0)
, m_iVideoHeight(0)
, m_pStreamCap(NULL)
, m_uFramePerSec(iFps)
, m_strVideoDevName(strVideoDev)
, m_strAudioDevName(strAudioDev)
, m_dwVideoEncoderBitRate(iVideoBitrate)
, m_dwAudioEncoderBitRate(iAudioBitrate)
, m_dVolum(100.0)
, m_bMute(false)
, m_videoSourceServer( CreateVideoSourceServer() )
, m_strFmsAddr( "" )
//, m_rtmpConInfo(NULL)
, m_bStart(false)
, m_iAudioAvgBitrate(0)
, m_iCountPerSecAudio(0)
, m_iVideoAvgBitrate(0)
, m_iCountPerSecVideo(0)
, m_bNeedUpdataInfoShow(false)
, m_strCmdshow(strCmdshow)
, m_rtLastVideoSampleTime(0)
, m_rtLastAudioSampleTime(0)
, m_bFirstSample(true)
{
SetBitmapInfo(m_videoFormat, iVideoWidth, iVideoHeight, 24);
SetWaveFormat(m_audioFormat, iAudioSampleRate);
USES_CONVERSION;
m_strFmsAddr = W2A( strRtmpAddr.c_str() );
// CLogTool::m_sAppName="MediaCapAssistForFlash";
// CLogTool::WriteLog(LOG_LEVEL_DEBUGGING,__FILE__,__LINE__,"MediaCapAssistForFlash start record...");
}
~CMainDlg()
{
if (m_videoSourceServer)
{
m_videoSourceServer->Release();
m_videoSourceServer = NULL;
}
}
virtual BOOL PreTranslateMessage(MSG* pMsg)
{
return ::IsDialogMessage(m_hWnd, pMsg);
}
BEGIN_MSG_MAP(CMainDlg)
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
MESSAGE_HANDLER(WM_TIMER, OnTimer)
MESSAGE_HANDLER(WM_PAINT, OnPaint)
COMMAND_ID_HANDLER(IDOK, OnOK)
COMMAND_ID_HANDLER(IDCANCEL, OnCancel)
COMMAND_ID_HANDLER(IDC_BUTTON_START, OnStart)
COMMAND_ID_HANDLER(IDC_BUTTON_STOP, OnStop)
COMMAND_ID_HANDLER(IDC_CHECK_MUTE, OnMute)
MESSAGE_HANDLER(WM_HSCROLL, OnHScroll)
END_MSG_MAP()
LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
USES_CONVERSION;
// center the dialog on the screen
CenterWindow();
// register object for message filtering
CMessageLoop* pLoop = _Module.GetMessageLoop();
pLoop->AddMessageFilter(this);
m_listboxInsertStr.SetListboxHWND( this->GetDlgItem(IDC_LIST_INFO).m_hWnd );
this->SetTimer(TIMER_ID_DRAW_VIDEO, TIME_DRAW_VIDEO, NULL);
this->SetTimer(TIMER_ID_PROCESS_MSG, TIME_PROCESS_MSG, NULL);
this->SetTimer(TIMER_ID_UPDATA_INFO, TIME_UPDATA_INFO, NULL);
this->SetTimer(TIMER_ID_CONNECT_FMS, TIME_CONNECT_FMS, NULL);
IDeviceList* pDeviceList = CreateDeviceList(NULL);
//
//视频输入设备
//
m_comboxVideoDev.Attach( GetDlgItem(IDC_COMBO_VIDEO_INPUT_DEVICE).m_hWnd );
vector<wstring>* pVecString = pDeviceList->GetDeviceList(DeviceCategory_VideoInput);
for (vector<wstring>::size_type i = 0; i < pVecString->size(); i++)
{
m_comboxVideoDev.InsertString( m_comboxVideoDev.GetCount(), pVecString->at(i).c_str() );
}
this->SetDlgItemText( IDC_COMBO_VIDEO_INPUT_DEVICE, m_strVideoDevName.c_str() );
//
//音频输入设备
//
m_comboxAudioDev.Attach( GetDlgItem(IDC_COMBO_AUDIO_INPUT_DEVICE).m_hWnd );
pVecString = pDeviceList->GetDeviceList(DeviceCategory_AudioInput);
for (vector<wstring>::size_type i = 0; i < pVecString->size(); i++)
{
m_comboxAudioDev.InsertString( m_comboxAudioDev.GetCount(), pVecString->at(i).c_str() );
}
this->SetDlgItemText( IDC_COMBO_AUDIO_INPUT_DEVICE, m_strAudioDevName.c_str() );
if (pDeviceList)
{
pDeviceList->Release();
pDeviceList = NULL;
}
this->SetDlgItemInt(IDC_EDIT_AUDIO_SAMPLERATE, m_audioFormat.nSamplesPerSec);
this->SetDlgItemInt(IDC_EDIT_VIDEO_WIDTH, m_videoFormat.bmiHeader.biWidth);
this->SetDlgItemInt(IDC_EDIT_VIDEO_HEIGHT, m_videoFormat.bmiHeader.biHeight);
this->SetDlgItemInt(IDC_EDIT_VIDEO_BITRATE, m_dwVideoEncoderBitRate);
this->SetDlgItemInt(IDC_EDIT_AUDIO_BITRATE, m_dwAudioEncoderBitRate);
this->SetDlgItemInt(IDC_EDIT_VIDEO_FPS, m_uFramePerSec);
this->CheckDlgButton(IDC_CHECK_MUTE, m_bMute ? TRUE : FALSE);
m_trackBarVolum.Attach( GetDlgItem(IDC_SLIDER_VOLUM).m_hWnd );
m_trackBarVolum.SetRange(0, 100);
m_trackBarVolum.SetPos( int(m_dVolum) );
this->SetDlgItemText(IDC_EDIT_FMS_ADDR, A2W(m_strFmsAddr.c_str()));
if ( m_strCmdshow == L"hide" )
{
this->Start();
}
return TRUE;
}
LRESULT OnOK(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
return 0;
}
LRESULT OnCancel(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
CloseDialog(wID);
return 0;
}
void CloseDialog(int nVal)
{
this->KillTimer(TIMER_ID_DRAW_VIDEO);
this->KillTimer(TIMER_ID_PROCESS_MSG);
this->KillTimer(TIMER_ID_UPDATA_INFO);
this->KillTimer(TIMER_ID_CONNECT_FMS);
Sleep(1000);
Stop();
DestroyWindow();
::PostQuitMessage(nVal);
}
LRESULT OnStart(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
UpdataPara();
Start();
return 0;
}
LRESULT OnStop(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
Stop();
return 0;
}
LRESULT OnMute(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
m_bMute = this->IsDlgButtonChecked(IDC_CHECK_MUTE) > 0;
if (m_pStreamCap)
{
m_pStreamCap->SetMute(m_bMute);
}
return 0;
}
LRESULT OnHScroll(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
if ( SB_THUMBPOSITION == LOWORD(wParam) )
{
m_dVolum = double( m_trackBarVolum.GetPos() );
if (m_pStreamCap)
{
m_pStreamCap->SetVolum(m_dVolum);
}
}
return 0;
}
LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
//
//消息处理
//
if (TIMER_ID_DRAW_VIDEO == wParam)
{
//画视频图像
if (m_bHaveVideo)
{
CTVAutoLock lockBufVideo(&m_lockBufVideo);
if (m_bufVideo.m_pBuf)
{
CDrawVideoGDI::DrawVideo(m_hWnd, IDC_STATIC_VIDEO, m_iVideoWidth, m_iVideoHeight, 24, m_bufVideo.m_pBuf, m_bufVideo.m_iActualDataLen);
m_bHaveVideo = false;
}
}
}
else if ( TIMER_ID_PROCESS_MSG == wParam )
{
CTVAutoLock lockQueueStr(&m_lockQueueStr);
while (m_queueStr.size() > 0)
{
this->m_listboxInsertStr.Insert( 0, m_queueStr.front().c_str() );
m_queueStr.pop();
}
}
else if ( TIMER_ID_CONNECT_FMS == wParam )
{
if (initCrtmp()==NULL)
{
wchar_t strData[1024] = {0};
GetDlgItemText(IDC_EDIT_FMS_ADDR, strData, 1024);
USES_CONVERSION;
char *svrPath = W2A( strData );
initCrtmp()->RegistListen(this);
initCrtmp()->Rtmp_Connect(svrPath);
}
}
else if ( TIMER_ID_UPDATA_INFO == wParam )
{
if (m_bNeedUpdataInfoShow)
{
this->SetDlgItemText(IDC_STATIC_BITRATE_INFO, CFormatWString(L"AudioBitrate: %d, AudioCount: %d/r/nVideoBitrate: %d, VideoCount: %d",
m_iAudioAvgBitrate, m_iCountPerSecAudio, m_iVideoAvgBitrate, m_iCountPerSecVideo) );
m_bNeedUpdataInfoShow = false;
}
}
return 0;
}
LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
//m_bHaveVideo = true;
bHandled = FALSE;
return 0;
}
void InsertStringToListbox(const wchar_t* pstr)
{
CTVAutoLock lockQueueStr(&m_lockQueueStr);
m_queueStr.push( wstring(pstr) );
}
//更新参数
void UpdataPara()
{
USES_CONVERSION;
ATL::CString strDevice;
if ( CB_ERR != m_comboxVideoDev.GetCurSel() )
{
m_comboxVideoDev.GetWindowText(strDevice);
m_strVideoDevName = strDevice;
}
if ( CB_ERR != m_comboxAudioDev.GetCurSel() )
{
m_comboxAudioDev.GetWindowText(strDevice);
m_strAudioDevName = strDevice;
}
UINT uWidth = this->GetDlgItemInt(IDC_EDIT_VIDEO_WIDTH);
UINT uHeight = this->GetDlgItemInt(IDC_EDIT_VIDEO_HEIGHT);
SetBitmapInfo(m_videoFormat, uWidth, uHeight, 24);
UINT uSamplePerSec = this->GetDlgItemInt(IDC_EDIT_AUDIO_SAMPLERATE);
SetWaveFormat(m_audioFormat, uSamplePerSec);
m_dwVideoEncoderBitRate = this->GetDlgItemInt(IDC_EDIT_VIDEO_BITRATE);
m_dwAudioEncoderBitRate = this->GetDlgItemInt(IDC_EDIT_AUDIO_BITRATE);
m_uFramePerSec = this->GetDlgItemInt(IDC_EDIT_VIDEO_FPS);
m_bMute = this->IsDlgButtonChecked(IDC_CHECK_MUTE) > 0;
m_dVolum = double( m_trackBarVolum.GetPos() );
wchar_t strAddr[1024] = {0};
GetDlgItemText(IDC_EDIT_FMS_ADDR, strAddr, 1024);
m_strFmsAddr = W2A( strAddr );
}
public:
void Start()
{
if (m_bStart)
{
return;
}
//
//虚拟视频数据发送启动
//
if (m_videoSourceServer)
{
m_videoSourceServer->Start();
}
//
//flash数据发送启动
//
{
CTVAutoLock lockFlash(&m_lockFlash);
wchar_t strData[1024] = {0};
GetDlgItemText(IDC_EDIT_FMS_ADDR, strData, 1024);
USES_CONVERSION;
char *svrPath = W2A( strData );
/******(2010-12-12)******************************************************************/
/*
更换了2个函数的位置
*/
/******(2010-12-12)******************************************************************/
initCrtmp()->RegistListen(this);
initCrtmp()->Rtmp_Connect(svrPath);
// m_rtmpConInfo = RtmpSDK_CreateConnInfo();
// if (m_rtmpConInfo)
// {
// ConnectFms();
// }
}
//
//时间
//
m_rtLastVideoSampleTime = 0;
m_rtLastAudioSampleTime = 0;
m_bFirstSample = true;
//
//启动捕获
//
wchar_t* pVideoEnc = L"ADVideo Encoder";
wchar_t* pAudioEnc = L"ADAudio Encoder";
char* pMediaTypeData = NULL;
DWORD dwMediaTypeDataLen = 0;
assert(NULL == m_pStreamCap);
m_pStreamCap = CreateStreamCap(pMediaTypeData, dwMediaTypeDataLen, 0, this,
m_audioFormat, m_videoFormat, m_uFramePerSec,
const_cast<wchar_t*>(m_strAudioDevName.c_str()), const_cast<wchar_t*>(m_strVideoDevName.c_str()), //L"Kkyoo Video Source", //
pVideoEnc, m_dwVideoEncoderBitRate,
pAudioEnc, m_dwAudioEncoderBitRate, TRUE, m_dVolum, m_bMute, 0, 0);
if (m_pStreamCap)
{
m_pStreamCap->Start();
}
m_bStart = true;
}
void Stop()
{
if (!m_bStart)
{
return;
}
if (m_pStreamCap)
{
m_pStreamCap->Stop();
m_pStreamCap->Release();
m_pStreamCap = NULL;
}
if (m_videoSourceServer)
{
m_videoSourceServer->Stop();
}
{
CTVAutoLock lockFlash(&m_lockFlash);
initCrtmp()->UnRegistListen(this);
initCrtmp()->Rtmp_ReleaseConn();
// if (m_rtmpConInfo)
// {
// RtmpSDK_ReleaseConnInfo(m_rtmpConInfo);
// m_rtmpConInfo = NULL;
// InsertStringToListbox( L"fms 断开连接!" );
// }
this->KillTimer(TIMER_ID_CONNECT_FMS);
}
m_bStart = false;
}
// void ConnectFms()
// {
// if ( 1 == RtmpSDK_Connect(m_rtmpConInfo, m_strFmsAddr.c_str()) )
// {
// InsertStringToListbox( L"fms连接成功!" );
// }
// else
// {
// InsertStringToListbox( L"fms连接失败!" );
// }
//
// if ( 1 == RtmpSDK_StreamControl(m_rtmpConInfo,StreamLIVE) )
// {
// InsertStringToListbox( L"fms StreamLIVE成功!" );
// }
// else
// {
// InsertStringToListbox( L"fms StreamLIVE失败!" );
// }
// }
public:
//
//IStreamCapNotfiy
//
virtual void OnStreamCapStatus(int iID, STREAMCAPSTATUS_LEVEL level, STREAMCAPSTATUS status, HRESULT errCode, WCHAR* szStatusString)
{
InsertStringToListbox( CFormatWString(L"StreamCap %s", szStatusString) );
}
virtual void OnStreamCapStart(int iID, int iFramesPerSec, int iWidth, int iHeight)
{
{
InsertStringToListbox( CFormatWString(L"StreamCap 开始--帧率%d, 宽%d, 高%d", iFramesPerSec, iWidth, iHeight) );
}
}
virtual void OnStreamCapStop(int iID){}
virtual void OnStreamCapUncompressedAudioData(int iID, IMediaSample *pSample, void* pBuf, int iLen, WAVEFORMATEX* pWft){}
virtual void OnStreamCapUncompressedVideoData(int iID, IMediaSample *pSample, void* pBuf, int iLen, int iWidth, int iHeight)
{
if (m_videoSourceServer)
{
BITMAPINFO format;
SetBitmapInfo(format, iWidth, iHeight, 24);
m_videoSourceServer->SetVideoFormat(format);
m_videoSourceServer->SetVideoData(pBuf, iLen);
}
{
CTVAutoLock lockBufVideo(&m_lockBufVideo);
m_bufVideo.CheckBufSize( iLen );
if (m_bufVideo.m_pBuf)
{
memcpy(m_bufVideo.m_pBuf, pBuf, iLen);
m_bufVideo.m_iActualDataLen = iLen;
m_iVideoWidth = iWidth;
m_iVideoHeight = iHeight;
m_bHaveVideo = true;
}
}
}
virtual void OnCompressedData(int iID, char* pData, unsigned int nSize)
{
WORD wStreamNum = 0;
bool bDiscontinuty = false;
bool bPreroll = false;
bool bSyncPoint = false;
REFERENCE_TIME cnsSampleTime = 0;
REFERENCE_TIME cnsSampleDuration = 0;
int iSubSeq = 0; //当前流的包序
unsigned int nMediaSize = 0;
unsigned char* pMediaData = NULL;
//
//解包包头
//
bool bReadSuc = CStreamDataUnpackerUnpress::ReadPakcet(wStreamNum, bDiscontinuty, bPreroll, bSyncPoint, cnsSampleTime, cnsSampleDuration,
iSubSeq, nMediaSize, pMediaData, (char*)pData, (int)nSize);
if (bReadSuc)
{
CTVAutoLock lockFlash(&m_lockFlash);
// if (m_rtmpConInfo)
// {
if (m_bFirstSample)
{
m_rtLastVideoSampleTime = cnsSampleTime;
m_rtLastAudioSampleTime = cnsSampleTime;
m_bFirstSample = false;
}
if (AUDIO_STREAM_NUM == wStreamNum && nMediaSize > 0)
{
initCrtmp()->Rtmp_SendStream((char*)pMediaData, nMediaSize, AUDIO_DATA, 0);
}
if (VIDEO_STREAM_NUM == wStreamNum && nMediaSize > 0)
{
//
//计算时间差
//
DWORD dwTimeStamp = 0;
__int64 timeStamp = (cnsSampleTime - m_rtLastVideoSampleTime) / 10000;
if (timeStamp < 0)
{
timeStamp = 10;
}
dwTimeStamp = DWORD(timeStamp);
m_rtLastVideoSampleTime = cnsSampleTime;
initCrtmp()->Rtmp_SendStream((char*)pMediaData, nMediaSize, VIDEO_DATA, dwTimeStamp);
}
// }
}
}
virtual void OnStreamCapAudioStatistics(int iAudioAvgBitrate, int iCountPerSec)
{
m_iAudioAvgBitrate = iAudioAvgBitrate;
m_iCountPerSecAudio = iCountPerSec;
m_bNeedUpdataInfoShow = true;
}
virtual void OnStreamCapVideoStatistics(int iVideoAvgBitrate, int iCountPerSec)
{
m_iVideoAvgBitrate = iVideoAvgBitrate;
m_iCountPerSecVideo = iCountPerSec;
m_bNeedUpdataInfoShow = true;
}
virtual void OnStreamCapStatistics(int iAvgBitrate, int iCountPerSec){}
};
如上所示的一个实例
在初始化的时候LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)里面设置this->SetTimer(TIMER_ID_CONNECT_FMS, TIME_CONNECT_FMS, NULL);
以连接rtmp连接服务器为例,在这里设置了消息,在OnTimer里面有对应的消息映射,对相应的消息进行不同的消息处理,我们这里的这个消息是
else if ( TIMER_ID_CONNECT_FMS == wParam )
{
if (initCrtmp()==NULL)
{
wchar_t strData[1024] = {0};
GetDlgItemText(IDC_EDIT_FMS_ADDR, strData, 1024);
USES_CONVERSION;
char *svrPath = W2A( strData );
initCrtmp()->RegistListen(this);
initCrtmp()->Rtmp_Connect(svrPath);
}
}
在OnTimer中每隔1000毫秒的话执行一次
在这里的意思是假如连接断开的话重新连接服务器
最后在停止Stop或者void CloseDialog(int nVal)中将 this->KillTimer(TIMER_ID_CONNECT_FMS);kill这条消息。