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_MinGW_64_bit-Debug/
|
||||
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);
|
||||
}
|
||||
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("打开摄像头");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 <thread>
|
||||
#include <queue>
|
||||
#include<iostream>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
|
|
|
@ -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
|
@ -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