1.添加部分新文件
1.1 给 DlgMain 对话框上添加两个按钮: 开始直播 按钮和 停止直播 按钮
1.2 本次直播项目中使用的是 FFMPEG 来处理视频,视频是通过 h264 进行编码的(进行编码的原因是每一帧图像较大,每秒想发送25帧图像,因此占用的网络资源较大,需要编码进行减小占用的网络资源)
1.3 本次直播项目中使用的是 WSAAPI 获取音频,并将音频进行 aac 编码
1.4 将图像与音频都进行编码之后将这两个编码为 mp4 文件,这其中涉及到 时间戳的对齐
1.5 将下载的文件放到项目中,将 dll 文件放到 Debug 目录下
1.6 添加一个新的筛选器 FFMPEG ,用来放 FFMPEG 相关的文件
1.7 添加库目录: …/lib;
1.8 添加附加依赖项,将下列语句添加到 附加依赖项 中
SDL2main.lib
SDL2.lib
avcodec.lib
avdevice.lib
avfilter.lib
swscale.lib
avformat.lib
avutil.lib
postproc.lib
swresample.lib
2.更改错误
2.1 新添加的文件需要 附加包含目录 : …/;
2.2 将项目的字符集更改为 Unicode (因为FFMPEG的默认编码形式为 Unicode)
2.3 将所有有关字符串操作的更改为 Unicode 相关的,将所有 strcpy_s 全部改为 WideCharToMultiByte ,将所有字符串全部改为 TCHAR
WideCharToMultiByte(CP_ACP, 0, m_name, -1, ssr.m_szName, sizeof(ssr.m_szName), 0, 0);
2.4 查看自己的摄像头的名称,在 Myffmpeg.cpp 中更改过来
void Myffmpeg::Factory(bool desk,bool camera,bool audio,bool microphone)
{
if(desk == true && m_pDesktop == NULL)
{
m_pDesktop = new MyCollectDesktop(this);
m_pDesktop->Initial();
}
if(audio == true && m_pAudio == NULL)
{
m_pAudio = new CCollectAudio(this);
m_pAudio->Initial();
}
if(camera == true && m_pCamera == NULL)
{
m_pCamera = new CCollectCamera(_T("video=Lenovo EasyCamera"));
m_pCamera->Start();
}
}
3.完成DlgMain对话框
3.1 在对话框上添加三个check控件: 摄像头、桌面、声卡;添加一个 设置参数 按钮;
3.2 给三个check控件添加三个变量,类型都是 value BOOL 型: m_b_camera 、 m_b_desktop 、 m_b_voice ;
3.3 在 DlgMain 类中添加一个 FFMPEG 类对象,用来获取摄像头数据: Myffmpeg m_ffmpeg ;
3.4 在 设置参数 按钮中,获取控件上选中的check信息,使用ffmpeg调用工厂模式;
- 1.我这里由于直接使用了新建对话框上面的按钮,因此这里的函数名为 OnBnClickedOk ,应该为 OnBnClickedButton 这种类型,请大家自己修改;
void CDlgMain::OnBnClickedOk()
{
UpdateData(TRUE);
m_ffmpeg.Factory(m_b_desktop, m_b_camera, m_b_voice);
}
3.5 添加一个 开始直播 按钮
- 1.在这个按钮中我们需要用 m_ffmpeg 对象来调用其中的 SetStart 函数;
void CDlgMain::OnBnClickedButton1()
{
m_ffmpeg.SetStart(m_b_desktop, m_b_camera, m_b_voice);
}
3.6 在项目目录下新建一个 TempFile 文件夹,用来存储生成的 mp4 文件
3.7 注释掉 MyCollectDesktop::CollectDesktop 中的 ((TCPKernel*)theApp.m_p_kernel)->SendFileData(szPath,szFileName) 这行代码,即可将摄像头显示到对话框上
4.完成 TCPKernel 类
4.1 给 TCPKernel 类添加一个 SendFileData 函数,用来发送文件信息,这个函数已经被写好了,直接使用(也需自己看一遍,理解一下)
bool TCPKernel::SendFileData(char* szpath, char* szFileName)
{
FILE *pFile = NULL;
fopen_s(&pFile,szpath,"rb");
if(pFile == NULL)
return false;
fseek(pFile,0,SEEK_END);
int nFileSize = ftell(pFile);
fseek(pFile,0,SEEK_SET);
STRU_UPLOADFILE su;
su.m_n_type = _DEF_PROTOCOL_STREAMINFO_RQ;
WideCharToMultiByte(CP_ACP,0,((CDlgMain*)theApp.m_pMainWnd)->m_name,-1,su.m_sz_name,sizeof(su.m_sz_name),0,0);
strcpy_s(su.m_sz_content,sizeof(su.m_sz_content),szFileName);
su.m_n_len = nFileSize;
SendData((char*)&su,sizeof(su));
while(1)
{
su.m_n_type = _DEF_PROTOCOL_STREAMCONNECT_RQ;
su.m_n_len = fread_s(su.m_sz_content,sizeof(su.m_sz_content),sizeof(char),sizeof(su.m_sz_content),pFile);
if(su.m_n_len==0)
{
break;
}
SendData((char*)&su,sizeof(su));
}
fclose(pFile);
return true;
}
4.2 在 PackDef.h 中添加一个结构体声明,这个结构体用来存放发送的文件的信息
#define MAX_CONTENTNUM 300
struct STRU_UPLOADFILE
{
PackType m_n_type;
char m_sz_name[DEF_SIZE];
char m_sz_content[MAX_CONTENTNUM];
int m_n_len;
}
4.3 将之前注释掉的 MyCollectDesktop::CollectDesktop 里的 ((TCPKernel*)theApp.m_p_kernel)->SendFileData(szPath,szFileName) 取消注释
5.继续 开始直播 按钮
5.1 完成按钮响应函数
- 1.首先使用 m_ffmpeg 对象调用 SetStart 函数(之前已写);
- 2.定义一个 开始传送 的结构体对象;
- 3.给这个结构体赋值;
- 4.使用 Kernel 进行发送数据包;
void CDlgMain::OnBnClickedButton1()
{
m_ffmpeg.SetStart(m_b_desktop, m_b_camera, m_b_voice);
STRU_STARTTRANSFER_RQ ssr;
ssr.m_nType = _DEF_PROTOCOL_STARTTRANSFER_RQ;
WideCharToMultiByte(CP_ACP, 0, m_name, -1, ssr.m_szName, sizeof(ssr.m_szName), 0, 0);
theApp.m_p_kernel->SendData((char*)&ssr, sizeof(ssr));
}
5.2 给服务器端添加四个函数: StartTransferRq 、 StreamInfoRq 、 StreamContentRq 、 SelectAuthor 函数;
bool TCPKernel::StartTransferRq(SOCKET sock,char* szbuf)
{
return true;
}
bool TCPKernel::StreamInfoRq(SOCKET sock,char* szbuf)
{
return true;
}
bool TCPKernel::StreamContentRq(SOCKET sock,char* szbuf)
{
return true;
}
bool TCPKernel::SelectAuthorRq(SOCKET sock,char* szbuf)
{
return true;
}