PC客户端程序添加rtmp pusher
parent
67836fb226
commit
03a7bb2120
|
@ -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_MSVC2017_64bit-Debug/
|
||||||
client/qt_gl_render/build-yuvgl-Desktop_Qt_5_14_0_MinGW_64_bit-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/libs/
|
||||||
|
client/qt_gl_render/yuvgl/third/
|
||||||
|
|
|
@ -24,8 +24,8 @@ void MainWindow::on_pushButton_clicked(){
|
||||||
mPlayerWidget = new CPlayWidget(nullptr);
|
mPlayerWidget = new CPlayWidget(nullptr);
|
||||||
}
|
}
|
||||||
if(!m_bCameraOpen){
|
if(!m_bCameraOpen){
|
||||||
ui->widget->SetDataType(CPlayWidget::IMG_TYPE::TYPE_RGB32);
|
mPlayerWidget->SetDataType(CPlayWidget::IMG_TYPE::TYPE_RGB32);
|
||||||
ui->widget->SetImgSize(640,480);
|
mPlayerWidget->SetImgSize(640,480);
|
||||||
|
|
||||||
qDebug()<<ui->comboBox->currentText().size()<<ui->comboBox->currentText();
|
qDebug()<<ui->comboBox->currentText().size()<<ui->comboBox->currentText();
|
||||||
wchar_t *opencamera = new wchar_t[ui->comboBox->currentText().size()];
|
wchar_t *opencamera = new wchar_t[ui->comboBox->currentText().size()];
|
||||||
|
@ -34,11 +34,11 @@ void MainWindow::on_pushButton_clicked(){
|
||||||
if(nullptr == mCamera){
|
if(nullptr == mCamera){
|
||||||
this->mCamera = new Camera(ss);
|
this->mCamera = new Camera(ss);
|
||||||
}
|
}
|
||||||
this->mCamera->SetObserver(ui->widget);
|
this->mCamera->SetObserver(mPlayerWidget);
|
||||||
qDebug()<<ui->comboBox->currentText();
|
qDebug()<<ui->comboBox->currentText();
|
||||||
ui->pushButton->setText("关闭摄像头");
|
ui->pushButton->setText("关闭摄像头");
|
||||||
m_bCameraOpen = true;
|
m_bCameraOpen = true;
|
||||||
ui->widget->show();
|
mPlayerWidget->show();
|
||||||
}else{
|
}else{
|
||||||
m_bCameraOpen = false;
|
m_bCameraOpen = false;
|
||||||
ui->pushButton->setText("打开摄像头");
|
ui->pushButton->setText("打开摄像头");
|
||||||
|
|
|
@ -403,7 +403,6 @@ HRESULT STDMETHODCALLTYPE Camera::SampleGrabberCallback::SampleCB(double Time, I
|
||||||
|
|
||||||
HRESULT STDMETHODCALLTYPE Camera::SampleGrabberCallback::BufferCB(double Time, BYTE * pBuffer, long BufferLen)
|
HRESULT STDMETHODCALLTYPE Camera::SampleGrabberCallback::BufferCB(double Time, BYTE * pBuffer, long BufferLen)
|
||||||
{
|
{
|
||||||
#define DEBUG_CAMERA
|
|
||||||
#ifdef DEBUG_CAMERA
|
#ifdef DEBUG_CAMERA
|
||||||
static FILE *p = fopen("camera_test.yuv","wb+");
|
static FILE *p = fopen("camera_test.yuv","wb+");
|
||||||
fwrite(pBuffer,BufferLen,1,p);
|
fwrite(pBuffer,BufferLen,1,p);
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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 == 1,CodecID == 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
#include<iostream>
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
|
@ -32,6 +32,9 @@ SOURCES += \
|
||||||
cplaywidget.cpp \
|
cplaywidget.cpp \
|
||||||
media/AACAudioCoder.cpp \
|
media/AACAudioCoder.cpp \
|
||||||
media/CameraCapture.cpp \
|
media/CameraCapture.cpp \
|
||||||
|
media/RtmpPuller.cpp \
|
||||||
|
media/RtmpPuller2.cpp \
|
||||||
|
media/RtmpPusher.cpp \
|
||||||
media/imgutil.cpp \
|
media/imgutil.cpp \
|
||||||
utils/Base64.cpp \
|
utils/Base64.cpp \
|
||||||
utils/Debuger.cpp \
|
utils/Debuger.cpp \
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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.
Loading…
Reference in New Issue