PC客户端程序添加rtmp pusher

master
DESKTOP-4RNDQIC\29019 2020-05-29 01:16:10 +08:00
parent 67836fb226
commit 03a7bb2120
11 changed files with 2350 additions and 5 deletions

1
.gitignore vendored
View File

@ -22,3 +22,4 @@ client/qt_gl_render/build-yuvgl-Desktop_Qt_5_14_0_MinGW_32_bit-Release/
client/qt_gl_render/build-yuvgl-Desktop_Qt_5_14_0_MSVC2017_64bit-Debug/
client/qt_gl_render/build-yuvgl-Desktop_Qt_5_14_0_MinGW_64_bit-Debug/
client/qt_gl_render/yuvgl/third/libs/
client/qt_gl_render/yuvgl/third/

View File

@ -24,8 +24,8 @@ void MainWindow::on_pushButton_clicked(){
mPlayerWidget = new CPlayWidget(nullptr);
}
if(!m_bCameraOpen){
ui->widget->SetDataType(CPlayWidget::IMG_TYPE::TYPE_RGB32);
ui->widget->SetImgSize(640,480);
mPlayerWidget->SetDataType(CPlayWidget::IMG_TYPE::TYPE_RGB32);
mPlayerWidget->SetImgSize(640,480);
qDebug()<<ui->comboBox->currentText().size()<<ui->comboBox->currentText();
wchar_t *opencamera = new wchar_t[ui->comboBox->currentText().size()];
@ -34,11 +34,11 @@ void MainWindow::on_pushButton_clicked(){
if(nullptr == mCamera){
this->mCamera = new Camera(ss);
}
this->mCamera->SetObserver(ui->widget);
this->mCamera->SetObserver(mPlayerWidget);
qDebug()<<ui->comboBox->currentText();
ui->pushButton->setText("关闭摄像头");
m_bCameraOpen = true;
ui->widget->show();
mPlayerWidget->show();
}else{
m_bCameraOpen = false;
ui->pushButton->setText("打开摄像头");

View File

@ -403,7 +403,6 @@ HRESULT STDMETHODCALLTYPE Camera::SampleGrabberCallback::SampleCB(double Time, I
HRESULT STDMETHODCALLTYPE Camera::SampleGrabberCallback::BufferCB(double Time, BYTE * pBuffer, long BufferLen)
{
#define DEBUG_CAMERA
#ifdef DEBUG_CAMERA
static FILE *p = fopen("camera_test.yuv","wb+");
fwrite(pBuffer,BufferLen,1,p);

View File

@ -0,0 +1,132 @@
#include "RtmpPuller.h"
RtmpPuller::RtmpPuller()
:mFrameIndex(0), mAudioIndex(-1),mVideoIndex(-1)
, mAudioStream(nullptr),mVideoStream(nullptr)
{
av_register_all();
//Network
avformat_network_init();
//Input
}
int RtmpPuller::ConnectServer(const char *p)
{
int ret = 0;
if ((ret = avformat_open_input(&mIfmtCtx, p, 0, 0)) < 0) {
printf("Could not open input file.");
return -1;
}
if ((ret = avformat_find_stream_info(mIfmtCtx, 0)) < 0) {
printf("Failed to retrieve input stream information");
return -1;
}
for (int i = 0; i < mIfmtCtx->nb_streams; i++) {
if (mIfmtCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
mVideoIndex = i;
}
if (mIfmtCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
mAudioIndex = i;
}
}
if(mAudioIndex > -1)
this->mAudioStream = mIfmtCtx->streams[mAudioIndex];
if(mVideoIndex > -1)
this->mVideoStream = mIfmtCtx->streams[mVideoIndex];
av_dump_format(mIfmtCtx, 0, p, 0);
mH264bsfc = av_bitstream_filter_init("h264_mp4toannexb");
mStatus = RUNNING;
if((mAudioIndex == -1 ) &&(mVideoIndex == -1))
mStatus = NOSOURCE;
return 0;
}
int ThreadPull(RtmpPuller*p) {
while (p->Status() == RtmpPuller::CAP_STATUS::RUNNING) {
p->PullData();
}
return 0;
}
int RtmpPuller::StartPull()
{
this->mThread = new std::thread(ThreadPull, this);
this->mThread->get_id();
mStatus = RUNNING;
return 0;
}
int RtmpPuller::PullData()
{
static int drop = 0;
AVStream *in_stream;
//Get an AVPacket
int ret = av_read_frame(mIfmtCtx, &pkt);
if (ret < 0)
return -1;
in_stream = mIfmtCtx->streams[pkt.stream_index];
/* copy packet */
//Convert PTS/DTS
pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, in_stream->time_base,
(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, in_stream->time_base,
(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, in_stream->time_base);
pkt.pos = -1;
//Print to Screen
if (drop < 100) {
drop++;
goto end;
}
if (pkt.stream_index == mVideoIndex) {
printf("Receive %8d video frames from input URL\n", mFrameIndex);
mFrameIndex++;
av_bitstream_filter_filter(mH264bsfc, in_stream->codec, NULL,
&pkt.data, &pkt.size, pkt.data, pkt.size, 0);
if (mObserver.size() > 0) {
for (auto itr = this->mObserver.begin(); itr != mObserver.end(); itr++) {
RtmpPullObserver *p = (RtmpPullObserver *)*itr;
if (p->mObserverType == RtmpPullObserver::Observer_Video) {
p->OnRtmpFrame(pkt.data, pkt.size);
}
}
}
}
if (pkt.stream_index == mAudioIndex) {
if (mObserver.size() > 0) {
for (auto itr = this->mObserver.begin(); itr != mObserver.end(); itr++) {
RtmpPullObserver *p = (RtmpPullObserver *)*itr;
if (p->mObserverType == RtmpPullObserver::Observer_Audio) {
p->OnRtmpFrame(pkt.data, pkt.size);
}
}
}
}
end:
//printf("%02x %02x %02x %02x %02x\r\n", pkt.data[0], pkt.data[1], pkt.data[2], pkt.data[3], pkt.data[4]);
av_free_packet(&pkt);
}
int RtmpPuller::SetObserver(RtmpPullObserver *p)
{
if (nullptr == p)
return -1;
mMux.lock();
for (auto itr = this->mObserver.begin(); itr != mObserver.end(); itr++) {
if (p == *itr) return 0;
}
this->mObserver.push_back(p);
mMux.unlock();
return 0;
}
RtmpPuller::CAP_STATUS RtmpPuller::Status()
{
return this->mStatus;
}
AVStream * RtmpPuller::AudioStream()
{
return this->mAudioStream;
}

View File

@ -0,0 +1,251 @@
#include "RtmpPuller2.h"
#include "Debuger.h"
RtmpPuller2::RtmpPuller2()
{
mAccBuffer = new uint8_t[3000];
}
RtmpPuller2::~RtmpPuller2()
{
}
int ThreadPull(RtmpPuller2*p) {
while (p->Status() == RtmpPuller2::CAP_STATUS::RUNNING) {
p->PullData();
Sleep(10);
}
return 0;
}
// 关闭拉流
int RtmpPuller2::StopPull()
{
mStatus = STOP;
this->mThread->join();
RTMP_Close(mRtmp);
RTMP_Free(mRtmp);
return 0;
}
int RtmpPuller2::StartPull()
{
if(this->mStatus == CONNECTED) {
mStatus = RUNNING;
this->mThread = new std::thread(ThreadPull, this);
this->mThread->get_id();
}
else {
}
return 0;
}
FILE *fp = nullptr;
int RtmpPuller2::PullData()
{
RTMPPacket packet = { 0 };
// Parse rtmp stream to h264 and aac
uint8_t nalu_header[4] = { 0x00, 0x00, 0x00, 0x01 };
int ret = RTMP_ReadPacket(mRtmp, &packet);
if (ret < 0)
return ret;
if (nullptr == fp) {
fp = fopen("src.aac", "wb");
}
if (RTMPPacket_IsReady(&packet)) {
// Process packet, eg: set chunk size, set bw, ...
RTMP_ClientPacket(mRtmp, &packet);
if (packet.m_packetType == RTMP_PACKET_TYPE_VIDEO) {
bool keyframe = 0x17 == packet.m_body[0] ? true : false;
bool sequence = 0x00 == packet.m_body[1];
printf("keyframe=%s, sequence=%s\n", keyframe ? "true" : "false", sequence ? "true" : "false");
// SPS/PPS sequence
if (sequence) {
uint32_t offset = 10;
uint32_t sps_num = packet.m_body[offset++] & 0x1f;
for (int i = 0; i < sps_num; i++) {
uint8_t ch0 = packet.m_body[offset];
uint8_t ch1 = packet.m_body[offset + 1];
uint32_t sps_len = ((ch0 << 8) | ch1);
offset += 2;
packet.m_body[offset - 1] = 0x01;
packet.m_body[offset - 2] = 0x00;
packet.m_body[offset - 3] = 0x00;
packet.m_body[offset - 4] = 0x00;
if (mObserver.size() > 0) {
for (auto itr = this->mObserver.begin(); itr != mObserver.end(); itr++) {
RtmpPullObserver *p = (RtmpPullObserver *)*itr;
if (p->mObserverType == RtmpPullObserver::Observer_Video) {
p->OnRtmpFrame(packet.m_body + offset - 4, sps_len + 4);
}
}
}
// Write sps data
//fwrite(nalu_header, sizeof(uint8_t), 4, _file_ptr);
//fwrite(packet.m_body + offset, sizeof(uint8_t), sps_len, _file_ptr);
offset += sps_len;
}
uint32_t pps_num = packet.m_body[offset++] & 0x1f;
for (int i = 0; i < pps_num; i++) {
uint8_t ch0 = packet.m_body[offset];
uint8_t ch1 = packet.m_body[offset + 1];
uint32_t pps_len = ((ch0 << 8) | ch1);
offset += 2;
packet.m_body[offset - 1] = 0x01;
packet.m_body[offset - 2] = 0x00;
packet.m_body[offset - 3] = 0x00;
packet.m_body[offset - 4] = 0x00;
if (mObserver.size() > 0) {
for (auto itr = this->mObserver.begin(); itr != mObserver.end(); itr++) {
RtmpPullObserver *p = (RtmpPullObserver *)*itr;
if (p->mObserverType == RtmpPullObserver::Observer_Video) {
p->OnRtmpFrame(packet.m_body + offset - 4, pps_len + 4);
}
}
}
// Write pps data
offset += pps_len;
}
}
// Nalu frames
else {
uint32_t offset = 5;
uint8_t ch0 = packet.m_body[offset];
uint8_t ch1 = packet.m_body[offset + 1];
uint8_t ch2 = packet.m_body[offset + 2];
uint8_t ch3 = packet.m_body[offset + 3];
uint32_t data_len = ((ch0 << 24) | (ch1 << 16) | (ch2 << 8) | ch3);
offset += 4;
packet.m_body[offset - 1] = 0x01;
packet.m_body[offset - 2] = 0x00;
packet.m_body[offset - 3] = 0x00;
packet.m_body[offset - 4] = 0x00;
if (mObserver.size() > 0) {
for (auto itr = this->mObserver.begin(); itr != mObserver.end(); itr++) {
RtmpPullObserver *p = (RtmpPullObserver *)*itr;
if (p->mObserverType == RtmpPullObserver::Observer_Video) {
p->OnRtmpFrame(packet.m_body + offset - 4, data_len + 4);
}
}
}
// Write nalu data(already started with '0x00,0x00,0x00,0x01')
//fwrite(nalu_header, sizeof(uint8_t), 4, _file_ptr);
offset += data_len;
}
}
else if (packet.m_packetType == RTMP_PACKET_TYPE_AUDIO) {
bool sequence = 0x00 == packet.m_body[1];
printf("sequence=%s\n", sequence ? "true" : "false");
// AAC sequence
if (sequence) {
uint8_t format = (packet.m_body[0] & 0xf0) >> 4;
uint8_t samplerate = (packet.m_body[0] & 0x0c) >> 2;
uint8_t sampledepth = (packet.m_body[0] & 0x02) >> 1;
uint8_t type = packet.m_body[0] & 0x01;
// sequence = packet.m_body[1];
// AAC(AudioSpecificConfig)
if (format == 10) {
ch0 = packet.m_body[2];
ch1 = packet.m_body[3];
config = ((ch0 << 8) | ch1);
object_type = (config & 0xF800) >> 11;
sample_frequency_index = (config & 0x0780) >> 7;
channels = (config & 0x78) >> 3;
frame_length_flag = (config & 0x04) >> 2;
depend_on_core_coder = (config & 0x02) >> 1;
extension_flag = config & 0x01;
}
// Speex(Fix data here, so no need to parse...)
else if (format == 11) {
// 16 KHz, mono, 16bit/sample
type = 0;
sampledepth = 1;
samplerate = 4;
}
}
// Audio frames
else {
// ADTS(7 bytes) + AAC data
uint32_t data_len = packet.m_nBodySize - 2 + 7;
uint8_t adts[7];
adts[0] = 0xff;
adts[1] = 0xf1;
adts[2] = ((object_type - 1) << 6) | (sample_frequency_index << 2)
| (channels >> 2);
adts[3] = ((channels & 3) << 6) + (data_len >> 11);
adts[4] = (data_len & 0x7FF) >> 3;
adts[5] = ((data_len & 7) << 5) + 0x1F;
adts[6] = 0xfc;
// Write audio frames
fwrite(adts, sizeof(uint8_t), 7, fp);
fwrite(packet.m_body + 2, sizeof(uint8_t), packet.m_nBodySize - 2, fp);
fflush(fp);
memcpy(mAccBuffer, adts, 7);
memcpy(mAccBuffer + 7, packet.m_body + 2, packet.m_nBodySize - 2);
if (mObserver.size() > 0) {
for (auto itr = this->mObserver.begin(); itr != mObserver.end(); itr++) {
RtmpPullObserver *p = (RtmpPullObserver *)*itr;
if (p->mObserverType == RtmpPullObserver::Observer_Audio) {
p->OnRtmpFrame(mAccBuffer, packet.m_nBodySize - 2 + 7);
}
}
}
}
}
RTMPPacket_Free(&packet);
}
return 0;
}
int RtmpPuller2::SetObserver(RtmpPuller2::RtmpPullObserver *p)
{
if (nullptr == p)
return -1;
mMux.lock();
for (auto itr = this->mObserver.begin(); itr != mObserver.end(); itr++) {
if (p == *itr) return 0;
}
this->mObserver.push_back(p);
mMux.unlock();
return 0;
}
RtmpPuller2::CAP_STATUS RtmpPuller2::Status()
{
return mStatus;
}
int RtmpPuller2::ConnectServer(string url)
{
mRtmp = RTMP_Alloc();
RTMP_Init(mRtmp);
if (RTMP_SetupURL(mRtmp, (char*)url.c_str()) == FALSE)
{
RTMP_Free(mRtmp);
mStatus = FAIL;
return -1;
}
/*连接服务器*/
if (RTMP_Connect(mRtmp, NULL) == FALSE)
{
RTMP_Free(mRtmp);
mStatus = FAIL;
return -1;
}
/*连接流*/
if (RTMP_ConnectStream(mRtmp, 0) == FALSE)
{
RTMP_Close(mRtmp);
RTMP_Free(mRtmp);
mStatus = FAIL;
return -1;
}
mStatus = CONNECTED;
return 0;
}

View File

@ -0,0 +1,570 @@
#include "RtmpPusher.h"
/**
* winsock
*
* @1 ,
*/
int InitSockets()
{
#ifdef WIN32
WORD version;
WSADATA wsaData;
version = MAKEWORD(1, 1);
return (WSAStartup(version, &wsaData) == 0);
#else
return TRUE;
#endif
}
bool RtmpPusher::IfConnect()
{
return mIfConnected;
}
int RtmpPusher::RTMP264_Connect(const char* url)
{
InitSockets();
m_pRtmp = RTMP_Alloc();
RTMP_Init(m_pRtmp);
/*设置URL*/
if (RTMP_SetupURL(m_pRtmp, (char*)url) == FALSE)
{
RTMP_Free(m_pRtmp);
return -1;
}
/*设置可写,即发布流,这个函数必须在连接前使用,否则无效*/
RTMP_EnableWrite(m_pRtmp);
/*连接服务器*/
if (RTMP_Connect(m_pRtmp, NULL) == FALSE)
{
RTMP_Free(m_pRtmp);
return -1;
}
/*连接流*/
if (RTMP_ConnectStream(m_pRtmp, 0) == FALSE)
{
RTMP_Close(m_pRtmp);
RTMP_Free(m_pRtmp);
return -1;
}
this->mUrl = string(url);
this->mIfConnected = true;
return 0;
}
/**
* winsock
*
* @0 ,
*/
inline void CleanupSockets()
{
#ifdef WIN32
WSACleanup();
#endif
}
void RtmpPusher::RTMP264_Close()
{
mMux.lock();
if (m_pRtmp)
{
RTMP_Close(m_pRtmp);
RTMP_Free(m_pRtmp);
m_pRtmp = NULL;
}
mMux.unlock();
CleanupSockets();
}
RTMPPacket* gPacket = nullptr;
int RtmpPusher::SendPacket(unsigned int nPacketType, unsigned char * data,
unsigned int size, unsigned int nTimestamp)
{
static bool once = true;
/*分配包内存和初始化,len为包体长度*/
if(nullptr == gPacket)
gPacket = (RTMPPacket *)malloc(640*720*3 + size);
memset(gPacket, 0, RTMP_HEAD_SIZE);
/*包体内存*/
gPacket->m_body = (char *)gPacket + RTMP_HEAD_SIZE;
gPacket->m_nBodySize = size;
memcpy(gPacket->m_body, data, size);
gPacket->m_hasAbsTimestamp = 0;
gPacket->m_packetType = nPacketType; /*此处为类型有两种一种是音频,一种是视频*/
gPacket->m_nInfoField2 = m_pRtmp->m_stream_id;
gPacket->m_nChannel = 0x04;
gPacket->m_headerType = RTMP_PACKET_SIZE_LARGE;
if (RTMP_PACKET_TYPE_AUDIO == nPacketType && size != 4)
{
gPacket->m_headerType = RTMP_PACKET_SIZE_MEDIUM;
}
gPacket->m_nTimeStamp = nTimestamp;
/*发送*/
int nRet = 0;
if (RTMP_IsConnected(m_pRtmp))
{
nRet = RTMP_SendPacket(m_pRtmp, gPacket, FALSE); /*TRUE为放进发送队列,FALSE是不放进发送队列,直接发送*/
}
else {
if (once) {
once = false;
std::cout<<"rtmp 服务器断开 at "<<__FILE__<<" line "<<__LINE__;
}
}
/*释放内存*/
//free(gPacket);
return nRet;
}
int RtmpPusher::SendVideoPacket(unsigned int nPacketType,
unsigned char * data, unsigned int size, unsigned int nTimestamp)
{
RTMPPacket* packet;
/*分配包内存和初始化,len为包体长度*/
packet = (RTMPPacket *)malloc(RTMP_HEAD_SIZE + size);
memset(packet, 0, RTMP_HEAD_SIZE);
/*包体内存*/
packet->m_body = (char *)packet + RTMP_HEAD_SIZE;
packet->m_nBodySize = size;
memcpy(packet->m_body, data, size);
packet->m_hasAbsTimestamp = 0;
packet->m_packetType = nPacketType; /*此处为类型有两种一种是音频,一种是视频*/
packet->m_nInfoField2 = m_pRtmp->m_stream_id;
packet->m_nChannel = 0x04;
packet->m_headerType = RTMP_PACKET_SIZE_LARGE;
if (RTMP_PACKET_TYPE_AUDIO == nPacketType && size != 4)
{
packet->m_headerType = RTMP_PACKET_SIZE_MEDIUM;
}
packet->m_nTimeStamp = nTimestamp;
/*发送*/
int nRet = 0;
if (RTMP_IsConnected(m_pRtmp))
{
nRet = RTMP_SendPacket(m_pRtmp, packet, TRUE); /*TRUE为放进发送队列,FALSE是不放进发送队列,直接发送*/
}
/*释放内存*/
free(packet);
return 0;
}
RtmpPusher::RtmpPusher()
:mThread(nullptr),
mIfConnected(false)
{
}
RtmpPusher::~RtmpPusher()
{
if (m_pRtmp)
{
RTMP_Close(m_pRtmp);
RTMP_Free(m_pRtmp);
m_pRtmp = NULL;
}
CleanupSockets();
}
void H264RtmpPuser::OnAudioEncode(const void * frameaddress, uint32_t framelen,uint16_t pts)
{
uint8_t *pack = (uint8_t*)malloc(framelen);
memcpy(pack, frameaddress, framelen);
mMux.lock();
Buffer buf;
buf.buf = (uint8_t *)pack;
buf.len = framelen;
buf.type = PAYLOAD_TYPE_AUDIO;
this->mPack.push(buf);
mMux.unlock();
this->mAudioPts = pts;
}
H264RtmpPuser::H264RtmpPuser()
{
this->metaData.Pps = nullptr;
this->metaData.Sps = nullptr;
this->metaData.nPpsLen = 0;
this->metaData.nSpsLen = 0;
mFirtACC = false;
}
int H264RtmpPuser::sortAndSendNal(uint8_t * data, int len)
{
int i = 0;
uint8_t * nalhead = nullptr;
uint8_t * naltail = nullptr;
uint32_t size = 0;
static int timestamp = 0;
timestamp += 1000 / 25;
if (nullptr == data) {
return -1;
}
while (i < len)
{
// sps pps p frame
if ((data[i] == 0x00) && (data[i + 1] == 0x00)
&& ((data[i + 2] == 0x00) && (data[i + 3] == 0x01) || (data[i + 2] == 0x01))) {
if ((nalhead == nullptr) && (i == 0) ) {
if ((data[i + 3] == 0x01) && (data[i + 4] == 0x41)) { //p 帧直接发
nalhead = data;
naltail = data + (len);
size = naltail - nalhead;
this->SendH264Packet(nalhead, size, 0, timestamp);
return 0;
}
//sps 帧进行解包
if ((data[i + 3] == 0x01) && (data[i + 4] == 0x67)) { // sps or pps or sei
nalhead = data;
i += 1;
}
//sei
if ((data[i + 2] == 0x01) && (data[i + 3] == 0x06)) {
i += 1;
}
}
else {
// i frame
if ((data[i + 2] == 0x01) && (data[i + 3] == 0x65)) {
naltail = data + i;
size = naltail - nalhead;
this->SendH264Packet(nalhead, size, 0, timestamp);
nalhead = data + i;
naltail = data + (len);
size = naltail - nalhead;
this->SendH264Packet(nalhead, size, 0, timestamp);
return 0;
}
//pps
if ((data[i + 3] == 0x01) && (data[i + 4] == 0x68)) { // sps or pps or sei
naltail = data + i;
size = naltail - nalhead;
this->SendH264Packet(nalhead, size, 0, timestamp);
nalhead = data + i;
i += 3;
}//sps
if ((data[i + 3] == 0x01) && (data[i + 4] == 0x67)) { // sps or pps or sei
nalhead = data + i;
i += 3;
}
//sei
if ((data[i + 3] == 0x01) && (data[i + 4] == 0x06)) { // sps or pps or sei
naltail = data + i;
size = naltail - nalhead;
this->SendH264Packet(nalhead, size, 0, timestamp);
nalhead = data + i;
i += 3;
}
// sps pps or sei
}
// 跳过00 00 00 00 01的情况
}
i++;
}
return 0;
}
// 视频同步包详细结构请见https://blog.csdn.net/liwf616/article/details/51596373
int H264RtmpPuser::SendVideoSpsPps(unsigned char * pps,
int pps_len, unsigned char * sps,
int sps_len,unsigned int nTimeStamp)
{
RTMPPacket * packet = NULL;//rtmp包结构
unsigned char * body = NULL;
int i;
packet = (RTMPPacket *)malloc(RTMP_HEAD_SIZE + 1024);
//RTMPPacket_Reset(packet);//重置packet状态
memset(packet, 0, RTMP_HEAD_SIZE + 1024);
packet->m_body = (char *)packet + RTMP_HEAD_SIZE;
body = (unsigned char *)packet->m_body;
i = 0;
// FrameType == 1CodecID == 7
body[i++] = 0x17;
//AVCPacketType
body[i++] = 0x00;
//CompositionTime
body[i++] = 0x00;
body[i++] = 0x00;
body[i++] = 0x00;
/*AVCDecoderConfigurationRecord*/
body[i++] = 0x01;
body[i++] = sps[1];
body[i++] = sps[2];
body[i++] = sps[3];
body[i++] = 0xff;
/*sps*/
body[i++] = 0xe1;
body[i++] = (sps_len >> 8) & 0xff;
body[i++] = sps_len & 0xff;
memcpy(&body[i], sps, sps_len);
i += sps_len;
/*pps*/
body[i++] = 0x01;
body[i++] = (pps_len >> 8) & 0xff;
body[i++] = (pps_len) & 0xff;
memcpy(&body[i], pps, pps_len);
i += pps_len;
packet->m_packetType = RTMP_PACKET_TYPE_VIDEO;
packet->m_nBodySize = i;
packet->m_nChannel = 0x04;
packet->m_nTimeStamp = nTimeStamp;
packet->m_hasAbsTimestamp = 0;
packet->m_headerType = RTMP_PACKET_SIZE_MEDIUM;
packet->m_nInfoField2 = m_pRtmp->m_stream_id;
/*调用发送接口*/
int nRet = RTMP_SendPacket(m_pRtmp, packet, TRUE);
free(packet); //释放内存
return nRet;
}
int H264RtmpPuser::SendAudioData(unsigned char * dat,
unsigned int size, unsigned int nTimeStamp)
{
return 0;
}
int H264RtmpPuser::SendH264Packet(unsigned char * data,
unsigned int size, int bIsKeyFrame, unsigned int nTimeStamp)
{
if(data == NULL){
return false;
}
unsigned int nal_type = 0;
// 小帧应该是PPS或者SPS
if ((data[0] != 0x00) || (data[1] != 0x00)
|| ((data[2] != 0x00)&&data[2]!= 0x01)) {
return false;
}
//Debuger::Debug(L"%02x %02x %02x %02x %02x %02d\r\n",
// data[0],data[1],data[2],data[3],data[4],size);
if (data[2] == 0x01) {
nal_type = data[3];
}
if (data[3] == 0x01) {
nal_type = data[4];
}
switch (nal_type)
{
case 0x67: //just update sps and pps
if (NULL == metaData.Sps)
metaData.Sps = (unsigned char *)malloc(size - 4);
h264_decode_sps(data + 4, size - 4, metaData.nWidth, metaData.nHeight, metaData.nFrameRate);
metaData.nSpsLen = size - 4;
memcpy(this->metaData.Sps, data + 4, size - 4);
break;
case 0x68: //just update sps and pps
this->metaData.nPpsLen = size - 4;
if (NULL == metaData.Pps) metaData.Pps = (unsigned char *)malloc(size - 4);
memcpy(this->metaData.Pps, data + 4, size - 4);
break;
case 0x41: //p frame
this->sendDataPackH264(data + 4, size - 4, 0, nTimeStamp);
break;
case 0x65: //i frame
this->sendDataPackH264(data + 3, size - 3, 1, nTimeStamp);
break;
case 0x06:
size = size;
//this->sendDataPack(data + 4, size - 4, 0, nTimeStamp);
break;
default:
break;
}
}
unsigned char *gBody = nullptr;
int H264RtmpPuser::sendDataPackH264(unsigned char * data,
unsigned int size, int bIsKeyFrame, unsigned int nTimeStamp)
{
if (gBody == nullptr) {
gBody = new unsigned char[640*720*3 + 9];
}
if (size < 0) {
gBody = gBody;
}
memset(gBody, 0, size + 9);
int i = 0;
if (1 == bIsKeyFrame) {
gBody[i++] = 0x17;// 1:Iframe 7:AVC
gBody[i++] = 0x01;// AVC NALU
gBody[i++] = 0x00;
gBody[i++] = 0x00;
gBody[i++] = 0x00;
// NALU size
gBody[i++] = size >> 24 & 0xff;
gBody[i++] = size >> 16 & 0xff;
gBody[i++] = size >> 8 & 0xff;
gBody[i++] = size & 0xff;
// NALU data
memcpy(&gBody[i], data, size);
if(metaData.Sps != nullptr)
SendVideoSpsPps(metaData.Pps, metaData.nPpsLen, metaData.Sps,
metaData.nSpsLen, nTimeStamp);
}
else {
gBody[i++] = 0x27;// 2:Pframe 7:AVC
gBody[i++] = 0x01;// AVC NALU
gBody[i++] = 0x00;
gBody[i++] = 0x00;
gBody[i++] = 0x00;
// NALU size
gBody[i++] = size >> 24 & 0xff;
gBody[i++] = size >> 16 & 0xff;
gBody[i++] = size >> 8 & 0xff;
gBody[i++] = size & 0xff;
// NALU data
memcpy(&gBody[i], data, size);
}
int bRet = SendPacket(RTMP_PACKET_TYPE_VIDEO, gBody, i + size, nTimeStamp);
return bRet;
}
int H264RtmpPuser::SendAudioSync(int audioType,
int sampleIndex, int channel, unsigned int nTimeStamp)
{
RTMPPacket * packet = NULL;//rtmp包结构
unsigned char * body = NULL;
int i;
packet = (RTMPPacket *)malloc(RTMP_HEAD_SIZE + 1024);
//RTMPPacket_Reset(packet);//重置packet状态
memset(packet, 0, RTMP_HEAD_SIZE + 1024);
packet->m_body = (char *)packet + RTMP_HEAD_SIZE;
body = (unsigned char *)packet->m_body;
body[0] = 0xaf;
body[1] = 0x00;
uint16_t audioSpecConf = 0;
audioSpecConf |= ((2 << 11) & 0xf800); //2: AACLC
audioSpecConf |= ((4 << 7) & 0x0780); //4: 44khz
audioSpecConf |= ((2 << 3) & 0x78); //4: 2:stero
audioSpecConf |= 0 & 0x07; //4: 0 padding
body[2] = (audioSpecConf >> 8) & 0xff;
body[3] = audioSpecConf & 0xff;
packet->m_packetType = RTMP_PACKET_TYPE_AUDIO;
packet->m_nBodySize = 4;
packet->m_nChannel = 0x04;
packet->m_nTimeStamp = nTimeStamp;
packet->m_hasAbsTimestamp = 0;
packet->m_headerType = RTMP_PACKET_SIZE_MEDIUM;
packet->m_nInfoField2 = m_pRtmp->m_stream_id;
/*调用发送接口*/
int nRet = RTMP_SendPacket(m_pRtmp, packet, TRUE);
free(packet); //释放内存
return nRet;
}
int H264RtmpPuser::sendDataPackAAC(unsigned char * data,
unsigned int size, unsigned int nTimeStamp)
{
unsigned char *gBody = nullptr;
static int timestamp = 0;
timestamp += 20;
if (!mFirtACC) {
SendAudioSync(2,4,4, timestamp);
mFirtACC = 1;
}
gBody = (unsigned char*)malloc(size + 2);
gBody[0] = 0xAF;
gBody[1] = 0x01; //aac raw data
memcpy(gBody + 2, data + 7, size - 7);
int bRet = SendPacket(RTMP_PACKET_TYPE_AUDIO, gBody,
size - 7 + 2, timestamp);
free(gBody);
return 0;
}
void H264RtmpPuser::OnGetCodeFrame(uint8_t * data, int len)
{
static int timetamp = 0;
timetamp += this->mTick;
uint8_t *pack = (uint8_t*)malloc(len);
memcpy(pack, data, len);
mMux.lock();
Buffer buf;
buf.buf = pack;
buf.len = len;
buf.type = PAYLOAD_TYPE_VIDEO;
this->mPack.push(buf);
mMux.unlock();
}
void H264RtmpPuser::ProcessSend()
{
while (this->mIfStart) {
int len = mPack.size();
if (!mPack.empty()) {
mMux.lock();
Buffer buf = mPack.front();
mPack.pop();
mMux.unlock();
//如果是视频帧
if (buf.type == PAYLOAD_TYPE_VIDEO) {
this->sortAndSendNal(buf.buf, buf.len);
}// 如果是音频帧
if (buf.type == PAYLOAD_TYPE_AUDIO) {
this->sendDataPackAAC(buf.buf, buf.len, this->mAudioPts);
}
free(buf.buf);
}
msleep(10);
}
}
int ThreadEncode(H264RtmpPuser * p)
{
Debuger::Debug(L"thread started\r\n");
if (nullptr == p)
return -1;
p->ProcessSend();
return 0;
}
int H264RtmpPuser::StartPush()
{
mIfStart = true;
this->mThread = new std::thread(ThreadEncode,this);
mThreadId = this->mThread->get_id();
return 0;
}
int H264RtmpPuser::StopPush()
{
mIfConnected = false;
mIfStart = false;
if(mThread != nullptr)
this->mThread->join();
this->RTMP264_Close();
return 0;
}

View File

@ -10,6 +10,7 @@
#include <mutex>
#include <thread>
#include <queue>
#include<iostream>
#ifdef WIN32
#include <windows.h>

View File

@ -32,6 +32,9 @@ SOURCES += \
cplaywidget.cpp \
media/AACAudioCoder.cpp \
media/CameraCapture.cpp \
media/RtmpPuller.cpp \
media/RtmpPuller2.cpp \
media/RtmpPusher.cpp \
media/imgutil.cpp \
utils/Base64.cpp \
utils/Debuger.cpp \

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,97 @@
OPTIONS rtsp://192.168.6.68 RTSP/1.0
CSeq: 2
User-Agent: LibVLC/2.1.5 (LIVE555 Streaming Media v2014.05.27)
RTSP/1.0 200 OK
CSeq: 2
Public: OPTIONS, DESCRIBE, PLAY, PAUSE, SETUP, TEARDOWN, SET_PARAMETER, GET_PARAMETER
Date: Fri, Dec 25 2015 14:08:41 GMT
DESCRIBE rtsp://192.168.6.68 RTSP/1.0
CSeq: 3
User-Agent: LibVLC/2.1.5 (LIVE555 Streaming Media v2014.05.27)
Accept: application/sdp
RTSP/1.0 200 OK
CSeq: 3
Content-Type: application/sdp
Content-Base: rtsp://192.168.6.68/
Content-Length: 679
v=0
o=- 1451052521156494 1451052521156494 IN IP4 192.168.6.68
s=Media Presentation
e=NONE
b=AS:5100
t=0 0
a=control:rtsp://192.168.6.68/
m=video 0 RTP/AVP 96
c=IN IP4 0.0.0.0
b=AS:5000
a=recvonly
a=x-dimensions:1280,960
a=control:rtsp://192.168.6.68/trackID=1
a=rtpmap:96 H264/90000
a=fmtp:96 profile-level-id=420029; packetization-mode=1; sprop-parameter-sets=Z2QAIK2EAQwgCGEAQwgCGEAQwgCEK1AoA803AQEBAg==,aO48sA==
m=audio 0 RTP/AVP 0
c=IN IP4 0.0.0.0
b=AS:50
a=recvonly
a=control:rtsp://192.168.6.68/trackID=2
a=rtpmap:0 PCMU/8000
a=Media_header:MEDIAINFO=494D4B48010100000400010010710110401F000000FA000000000000000000000000000000000000;
a=appversion:1.0
SETUP rtsp://192.168.6.68/trackID=1 RTSP/1.0
CSeq: 4
User-Agent: LibVLC/2.1.5 (LIVE555 Streaming Media v2014.05.27)
Transport: RTP/AVP;unicast;client_port=56296-56297
RTSP/1.0 200 OK
CSeq: 4
Session: 283812775;timeout=60
Transport: RTP/AVP;unicast;client_port=56296-56297;server_port=8236-8237;ssrc=2d02fa0f;mode="play"
Date: Fri, Dec 25 2015 14:08:41 GMT
SETUP rtsp://192.168.6.68/trackID=2 RTSP/1.0
CSeq: 5
User-Agent: LibVLC/2.1.5 (LIVE555 Streaming Media v2014.05.27)
Transport: RTP/AVP;unicast;client_port=56298-56299
Session: 283812775
RTSP/1.0 200 OK
CSeq: 5
Session: 283812775;timeout=60
Transport: RTP/AVP;unicast;client_port=56298-56299;server_port=8234-8235;ssrc=1266baad;mode="play"
Date: Fri, Dec 25 2015 14:08:41 GMT
PLAY rtsp://192.168.6.68/ RTSP/1.0
CSeq: 6
User-Agent: LibVLC/2.1.5 (LIVE555 Streaming Media v2014.05.27)
Session: 283812775
Range: npt=0.000-
RTSP/1.0 200 OK
CSeq: 6
Session: 283812775
RTP-Info: url=rtsp://192.168.6.68/trackID=1;seq=16387;rtptime=1724325370,url=rtsp://192.168.6.68/trackID=2;seq=30619;rtptime=153273528
Date: Fri, Dec 25 2015 14:08:41 GMT
GET_PARAMETER rtsp://192.168.6.68/ RTSP/1.0
CSeq: 7
User-Agent: LibVLC/2.1.5 (LIVE555 Streaming Media v2014.05.27)
Session: 283812775
RTSP/1.0 200 OK
CSeq: 7
Date: Fri, Dec 25 2015 14:08:41 GMT
TEARDOWN rtsp://192.168.6.68/ RTSP/1.0
CSeq: 8
User-Agent: LibVLC/2.1.5 (LIVE555 Streaming Media v2014.05.27)
Session: 283812775
RTSP/1.0 200 OK
CSeq: 8
Session: 283812775
Date: Fri, Dec 25 2015 14:08:49 GMT

Binary file not shown.