From 993a7850d6cffb341fefabb68fbb97168c4a461c Mon Sep 17 00:00:00 2001 From: houxiao <houxiao@454eff88-639b-444f-9e54-f578c98de674> Date: 星期一, 26 十二月 2016 16:27:16 +0800 Subject: [PATCH] rtsp server ok --- RtspFace/FFmpegRTSPServer/LiveServerMediaSubsession.cpp | 29 + RtspFace/main.cpp | 9 RtspFace/FFmpegRTSPServer/main.cpp | 78 +++ RtspFace/FFmpegRTSPServer/FFmpegH264Encoder.h | 102 +++ RtspFace/FFmpegRTSPServer/FFmpegH264Source.cpp | 84 +++ RtspFace/FFmpegRTSPServer/LiveRTSPServer.h | 39 + RtspFace/FFmpegRTSPServer/FFmpegDecoder.cpp | 148 +++++ RtspFace/PL_RTSPClient.cpp | 15 RtspFace/FFmpegRTSPServer/LiveRTSPServer.cpp | 79 +++ RtspFace/make.sh | 14 RtspFace/PL_RTSPServer.cpp | 219 ++----- RtspFace/FFmpegRTSPServer/FFmpegH264Encoder.cpp | 291 +++++++++++ RtspFace/FFmpegRTSPServer/IEncoder.h | 19 RtspFace/FFmpegRTSPServer/FFmpegDecoder.h | 77 ++ RtspFace/live555/testProgs/testRTSPClient.hpp | 229 ++++--- RtspFace/FFmpegRTSPServer/LiveServerMediaSubsession.h | 39 + RtspFace/PL_RTSPClient.h | 8 RtspFace/FFmpegRTSPServer/FFmpegH264Source.h | 43 + 18 files changed, 1,258 insertions(+), 264 deletions(-) diff --git a/RtspFace/FFmpegRTSPServer/FFmpegDecoder.cpp b/RtspFace/FFmpegRTSPServer/FFmpegDecoder.cpp new file mode 100644 index 0000000..dbe1feb --- /dev/null +++ b/RtspFace/FFmpegRTSPServer/FFmpegDecoder.cpp @@ -0,0 +1,148 @@ +// +// FFmpegDecoder.cpp +// FFmpegRTSPServer +// +// Created by Mina Saad on 9/22/15. +// Copyright (c) 2015 Mina Saad. All rights reserved. +// + +#include "FFmpegDecoder.h" + +namespace MESAI +{ + FFmpegDecoder::FFmpegDecoder(std::string path) + { + this->path = path; + } + + + void FFmpegDecoder::intialize() + { + + // Intialize FFmpeg enviroment + av_register_all(); + avdevice_register_all(); + avcodec_register_all(); + avformat_network_init(); + + const char *filenameSrc = path.c_str(); + + pFormatCtx = avformat_alloc_context(); + + AVCodec * pCodec; + + if(avformat_open_input(&pFormatCtx,filenameSrc,NULL,NULL) != 0) + { + //exception + return; + } + + if(avformat_find_stream_info(pFormatCtx, NULL) < 0) + { + //exception + return; + } + + av_dump_format(pFormatCtx, 0, filenameSrc, 0); + + videoStream = -1; + + for(int i=0; i < pFormatCtx->nb_streams; i++) + { + AVStream *st = pFormatCtx->streams[i]; + enum AVMediaType type = st->codec->codec_type; + if (videoStream == -1) + if (avformat_match_stream_specifier(pFormatCtx, st, "vst") > 0) + videoStream = i; + } + + videoStream = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_VIDEO,videoStream, -1, NULL, 0); + + if(videoStream == -1) + { + //exception + return; + } + + pCodecCtx = pFormatCtx->streams[videoStream]->codec; + + pCodec =avcodec_find_decoder(pCodecCtx->codec_id); + if(pCodec==NULL) + { + //exception + return; + } + + pCodecCtx->codec_id = pCodec->id; + pCodecCtx->workaround_bugs = 1; + + if(avcodec_open2(pCodecCtx,pCodec,NULL) < 0) + { + //exception + return; + } + + pFrameRGB = av_frame_alloc(); + AVPixelFormat pFormat = AV_PIX_FMT_BGR24; + uint8_t *fbuffer; + int numBytes; + numBytes = avpicture_get_size(pFormat,pCodecCtx->width,pCodecCtx->height) ; //AV_PIX_FMT_RGB24 + fbuffer = (uint8_t *) av_malloc(numBytes*sizeof(uint8_t)); + avpicture_fill((AVPicture *) pFrameRGB,fbuffer,pFormat,pCodecCtx->width,pCodecCtx->height); + + img_convert_ctx = sws_getCachedContext(NULL,pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_BGR24, SWS_BICUBIC, NULL, NULL,NULL); + + height = pCodecCtx->height; + width = pCodecCtx->width; + bitrate =pCodecCtx->bit_rate; + GOP = pCodecCtx->gop_size; + frameRate = (int )pFormatCtx->streams[videoStream]->avg_frame_rate.num/pFormatCtx->streams[videoStream]->avg_frame_rate.den; + + + } + + void FFmpegDecoder::setOnframeCallbackFunction(std::function<void(uint8_t *)> func) + { + onFrame = func; + } + + + void FFmpegDecoder::playMedia() + { + AVPacket packet; + AVFrame * pFrame; + while((av_read_frame(pFormatCtx,&packet)>=0)) + { + if(packet.buf != NULL & packet.stream_index == videoStream) + { + pFrame = av_frame_alloc(); + int frameFinished; + int decode_ret = avcodec_decode_video2(pCodecCtx,pFrame,&frameFinished,&packet); + av_free_packet(&packet); + if(frameFinished) + { + sws_scale(img_convert_ctx, ((AVPicture*)pFrame)->data, ((AVPicture*)pFrame)->linesize, 0, pCodecCtx->height, ((AVPicture *)pFrameRGB)->data, ((AVPicture *)pFrameRGB)->linesize); + onFrame(((AVPicture *)pFrameRGB)->data[0]); + } + av_frame_unref(pFrame); + av_free(pFrame); + } + usleep(((double)(1.0/frameRate))*1000000); + + } + av_free_packet(&packet); + + + } + + void FFmpegDecoder::finalize() + { + sws_freeContext(img_convert_ctx); + av_freep(&(pFrameRGB->data[0])); + av_frame_unref(pFrameRGB); + av_free(pFrameRGB); + avcodec_close(pCodecCtx); + avformat_close_input(&pFormatCtx); + } + +} diff --git a/RtspFace/FFmpegRTSPServer/FFmpegDecoder.h b/RtspFace/FFmpegRTSPServer/FFmpegDecoder.h new file mode 100644 index 0000000..86dfc87 --- /dev/null +++ b/RtspFace/FFmpegRTSPServer/FFmpegDecoder.h @@ -0,0 +1,77 @@ +// +// FFmpegDecoder.h +// FFmpegRTSPServer +// +// Created by Mina Saad on 9/22/15. +// Copyright (c) 2015 Mina Saad. All rights reserved. +// + +#ifndef MESAI_FFmpegDecoder_H +#define MESAI_FFmpegDecoder_H + +#include <iostream> +#include <string> +#include <pthread.h> +#include <functional> +#include <unistd.h> + +extern "C" { +#include <libavutil/mathematics.h> +#include <libavutil/imgutils.h> +#include <libswscale/swscale.h> +#include <libavutil/pixdesc.h> +#include <libavdevice/avdevice.h> +} + + + +namespace MESAI +{ + class FFmpegDecoder + { + public: + FFmpegDecoder(std::string); + + ~FFmpegDecoder(); + + void intialize(); + + void playMedia(); + + void finalize(); + + void setOnframeCallbackFunction(std::function<void(uint8_t *)> func); + + int width; + + int height; + + int GOP; + + int frameRate; + + int bitrate; + + std::function<void(uint8_t *)> onFrame; + + private: + + std::string path; + + AVCodecContext *pCodecCtx; + + AVFormatContext *pFormatCtx; + + AVFrame *pFrameRGB; + + struct SwsContext * img_convert_ctx; + + int videoStream; + + + }; + + +} + +#endif \ No newline at end of file diff --git a/RtspFace/FFmpegRTSPServer/FFmpegH264Encoder.cpp b/RtspFace/FFmpegRTSPServer/FFmpegH264Encoder.cpp new file mode 100644 index 0000000..0b38140 --- /dev/null +++ b/RtspFace/FFmpegRTSPServer/FFmpegH264Encoder.cpp @@ -0,0 +1,291 @@ +// +// FFmpegH264Encoder.cpp +// FFmpegRTSPServer +// +// Created by Mina Saad on 9/22/15. +// Copyright (c) 2015 Mina Saad. All rights reserved. +// + +#include "FFmpegH264Encoder.h" + +namespace MESAI +{ + FFmpegH264Encoder::FFmpegH264Encoder() + { + pthread_mutex_init(&inqueue_mutex,NULL); + pthread_mutex_init(&outqueue_mutex,NULL); + + } + + void FFmpegH264Encoder::setCallbackFunctionFrameIsReady(std::function<void()> func) + { + onFrame = func; + } + + + void FFmpegH264Encoder::SendNewFrame(uint8_t * RGBFrame) { + pthread_mutex_lock(&inqueue_mutex); + if(inqueue.size()<30) + { + inqueue.push(RGBFrame); + } + pthread_mutex_unlock(&inqueue_mutex); + } + + void FFmpegH264Encoder::run() + { + while(true) + { + if(!inqueue.empty()) + { + uint8_t * frame; + pthread_mutex_lock(&inqueue_mutex); + frame = inqueue.front(); + inqueue.pop(); + pthread_mutex_unlock(&inqueue_mutex); + if(frame != NULL) + { + WriteFrame(frame); + } + } + } + } + + void FFmpegH264Encoder::SetupCodec(const char *filename, int codec_id) + { + int ret; + m_sws_flags = SWS_BICUBIC; + m_frame_count=0; + + avcodec_register_all(); + av_register_all(); + + avformat_alloc_output_context2(&m_oc, NULL, NULL, filename); + + if (!m_oc) { + avformat_alloc_output_context2(&m_oc, NULL, "avi", filename); + } + + if (!m_oc) { + return; + } + + m_fmt = m_oc->oformat; + m_video_st = NULL; + m_fmt->video_codec = (AVCodecID)codec_id; + m_fmt->audio_codec = AV_CODEC_ID_NONE; + + AVStream *st; + + m_video_codec = avcodec_find_encoder(m_fmt->video_codec); + if (!(m_video_codec)) { + return; + } + + st = avformat_new_stream(m_oc, m_video_codec); + + if (!st) { + return; + } + + st->id = m_oc->nb_streams-1; + + m_c = st->codec; + + m_c->codec_id = m_fmt->video_codec; + m_c->bit_rate = m_AVIMOV_BPS; //Bits Per Second + m_c->width = m_AVIMOV_WIDTH; //Note Resolution must be a multiple of 2!! + m_c->height = m_AVIMOV_HEIGHT; //Note Resolution must be a multiple of 2!! + m_c->time_base.den = m_AVIMOV_FPS; //Frames per second + m_c->time_base.num = 1; + m_c->gop_size = m_AVIMOV_GOB; // Intra frames per x P frames + m_c->pix_fmt = AV_PIX_FMT_YUV420P;//Do not change this, H264 needs YUV format not RGB + + + if (m_oc->oformat->flags & AVFMT_GLOBALHEADER) + m_c->flags |= CODEC_FLAG_GLOBAL_HEADER; + + m_video_st=st; + + + AVCodecContext *c = m_video_st->codec; + + ret = avcodec_open2(c, m_video_codec, NULL); + if (ret < 0) { + return; + } + + //ret = avpicture_alloc(&m_dst_picture, c->pix_fmt, c->width, c->height); + m_dst_picture = av_frame_alloc(); + m_dst_picture->format = c->pix_fmt; + m_dst_picture->data[0] = NULL; + m_dst_picture->linesize[0] = -1; + m_dst_picture->pts = 0; + m_dst_picture->width = m_c->width; + m_dst_picture->height = m_c->height; + + ret = av_image_alloc(m_dst_picture->data, m_dst_picture->linesize, c->width, c->height, (AVPixelFormat)m_dst_picture->format, 32); + if (ret < 0) { + return; + } + + //ret = avpicture_alloc(&m_src_picture, AV_PIX_FMT_BGR24, c->width, c->height); + m_src_picture = av_frame_alloc(); + m_src_picture->format = c->pix_fmt; + ret = av_image_alloc(m_src_picture->data, m_src_picture->linesize, c->width, c->height, AV_PIX_FMT_BGR24, 24); + + if (ret < 0) { + return; + } + + bufferSize = ret; + + av_dump_format(m_oc, 0, filename, 1); + + if (!(m_fmt->flags & AVFMT_NOFILE)) { + ret = avio_open(&m_oc->pb, filename, AVIO_FLAG_WRITE); + if (ret < 0) { + return; + } + } + + ret = avformat_write_header(m_oc, NULL); + + if (ret < 0) { + return; + } + + sws_ctx = sws_getContext(c->width, c->height, AV_PIX_FMT_BGR24, + c->width, c->height, AV_PIX_FMT_YUV420P, + SWS_BICUBIC, NULL, NULL, NULL); + if (!sws_ctx) { + return; + } + } + + void FFmpegH264Encoder::WriteFrame(uint8_t * RGBFrame ) + { + + memcpy(m_src_picture->data[0], RGBFrame, bufferSize); + + sws_scale(sws_ctx, + m_src_picture->data, m_src_picture->linesize, + 0, m_c->height, m_dst_picture->data, m_dst_picture->linesize); + + + AVPacket pkt = { 0 }; + int got_packet; + av_init_packet(&pkt); + + int ret = 0; + + ret = avcodec_encode_video2(m_c, &pkt, m_dst_picture, &got_packet); + + if (ret < 0) { + return; + } + + if (!ret && got_packet && pkt.size) + { + pkt.stream_index = m_video_st->index; + FrameStructure * frame = new FrameStructure(); + frame->dataPointer = new uint8_t[pkt.size]; + frame->dataSize = pkt.size-4; + frame->frameID = m_frame_count; + + memcpy(frame->dataPointer,pkt.data+4,pkt.size-4); + + pthread_mutex_lock(&outqueue_mutex); + + if(outqueue.size()<30) + { + outqueue.push(frame); + } + else + { + delete frame; + } + + pthread_mutex_unlock(&outqueue_mutex); + + } + + av_free_packet(&pkt); + + m_frame_count++; + m_dst_picture->pts += av_rescale_q(1, m_video_st->codec->time_base, m_video_st->time_base); + + onFrame(); + } + + void FFmpegH264Encoder::SetupVideo(std::string filename, int Width, int Height, int FPS, int GOB, int BitPerSecond) + { + m_filename = filename; + m_AVIMOV_WIDTH=Width; //Movie width + m_AVIMOV_HEIGHT=Height; //Movie height + m_AVIMOV_FPS=FPS; //Movie frames per second + m_AVIMOV_GOB=GOB; //I frames per no of P frames, see note below! + m_AVIMOV_BPS=BitPerSecond; //Bits per second, if this is too low then movie will become garbled + + SetupCodec(m_filename.c_str(),AV_CODEC_ID_H264); + } + + void FFmpegH264Encoder::CloseCodec() + { + + av_write_trailer(m_oc); + avcodec_close(m_video_st->codec); + + av_freep(&(m_dst_picture->data[0])); + av_frame_unref(m_dst_picture); + av_free(m_dst_picture); + av_freep(&(m_src_picture->data[0])); + av_frame_unref(m_src_picture); + av_free(m_src_picture); + + if (!(m_fmt->flags & AVFMT_NOFILE)) + avio_close(m_oc->pb); + + m_oc->pb = NULL; + + avformat_free_context(m_oc); + sws_freeContext(sws_ctx); + + } + + void FFmpegH264Encoder::CloseVideo() + { + CloseCodec(); + } + + char FFmpegH264Encoder::GetFrame(u_int8_t** FrameBuffer, unsigned int *FrameSize) + { + if(!outqueue.empty()) + { + FrameStructure * frame; + frame = outqueue.front(); + *FrameBuffer = (uint8_t*)frame->dataPointer; + *FrameSize = frame->dataSize; + return 1; + } + else + { + *FrameBuffer = 0; + *FrameSize = 0; + return 0; + } + } + + char FFmpegH264Encoder::ReleaseFrame() + { + pthread_mutex_lock(&outqueue_mutex); + if(!outqueue.empty()) + { + FrameStructure * frame = outqueue.front(); + outqueue.pop(); + delete frame; + } + pthread_mutex_unlock(&outqueue_mutex); + return 1; + } +} \ No newline at end of file diff --git a/RtspFace/FFmpegRTSPServer/FFmpegH264Encoder.h b/RtspFace/FFmpegRTSPServer/FFmpegH264Encoder.h new file mode 100644 index 0000000..31e345d --- /dev/null +++ b/RtspFace/FFmpegRTSPServer/FFmpegH264Encoder.h @@ -0,0 +1,102 @@ +// +// FFmpegH264Encoder.h +// FFmpegRTSPServer +// +// Created by Mina Saad on 9/22/15. +// Copyright (c) 2015 Mina Saad. All rights reserved. +// + +#ifndef MESAI_FFMPEGH264_ENCODER_H +#define MESAI_FFMPEGH264_ENCODER_H + +#include <string> +#include <queue> +#include <pthread.h> +#include <functional> + +#include "IEncoder.h" + +extern "C" { + + #include <stdlib.h> + #include <stdio.h> + #include <string.h> + #include <math.h> + #include <libavutil/opt.h> + #include <libavutil/mathematics.h> + #include <libavformat/avformat.h> + #include <libswscale/swscale.h> + #include <libswresample/swresample.h> + #include <libavutil/imgutils.h> + #include <libavcodec/avcodec.h> + +} + +namespace MESAI +{ + class FrameStructure { + public: + uint8_t * dataPointer; + int dataSize; + int frameID; + ~FrameStructure() + { + delete dataPointer; + } + }; + + class FFmpegH264Encoder : public IEncoder + { + public: + FFmpegH264Encoder(); + ~FFmpegH264Encoder(); + + virtual void setCallbackFunctionFrameIsReady(std::function<void()> func); + + void SetupVideo(std::string filename, int Width, int Height, int FPS, int GOB, int BitPerSecond); + void CloseVideo(); + void SetupCodec(const char *filename, int codec_id); + void CloseCodec(); + + + void SendNewFrame(uint8_t * RGBFrame); + void WriteFrame(uint8_t * RGBFrame); + virtual char ReleaseFrame(); + + void run(); + virtual char GetFrame(u_int8_t** FrameBuffer, unsigned int *FrameSize); + + private: + + + std::queue<uint8_t*> inqueue; + pthread_mutex_t inqueue_mutex; + std::queue<FrameStructure *> outqueue; + pthread_mutex_t outqueue_mutex; + + + int m_sws_flags; + int m_AVIMOV_FPS; + int m_AVIMOV_GOB; + int m_AVIMOV_BPS; + int m_frame_count; + int m_AVIMOV_WIDTH; + int m_AVIMOV_HEIGHT; + std::string m_filename; + + double m_video_time; + + AVCodecContext *m_c; + AVStream *m_video_st; + AVOutputFormat *m_fmt; + AVFormatContext *m_oc; + AVCodec *m_video_codec; + AVFrame * m_src_picture, * m_dst_picture; + SwsContext *sws_ctx; + int bufferSize; + + std::function<void()> onFrame; + + }; +} +#endif \ No newline at end of file diff --git a/RtspFace/FFmpegRTSPServer/FFmpegH264Source.cpp b/RtspFace/FFmpegRTSPServer/FFmpegH264Source.cpp new file mode 100644 index 0000000..c2d5ab5 --- /dev/null +++ b/RtspFace/FFmpegRTSPServer/FFmpegH264Source.cpp @@ -0,0 +1,84 @@ +// +// FFmpegH264Source.cpp +// FFmpegRTSPServer +// +// Created by Mina Saad on 9/22/15. +// Copyright (c) 2015 Mina Saad. All rights reserved. +// + +#include "FFmpegH264Source.h" + +namespace MESAI +{ + FFmpegH264Source * FFmpegH264Source::createNew(UsageEnvironment& env, IEncoder * E_Source) { + return new FFmpegH264Source(env, E_Source); + } + + FFmpegH264Source::FFmpegH264Source(UsageEnvironment& env, IEncoder * E_Source) : FramedSource(env), Encoding_Source(E_Source) + { + m_eventTriggerId = envir().taskScheduler().createEventTrigger(FFmpegH264Source::deliverFrameStub); + std::function<void()> callback1 = std::bind(&FFmpegH264Source::onFrame,this); + Encoding_Source -> setCallbackFunctionFrameIsReady(callback1); + } + + FFmpegH264Source::~FFmpegH264Source() + { + + } + + void FFmpegH264Source::doStopGettingFrames() + { + FramedSource::doStopGettingFrames(); + } + + void FFmpegH264Source::onFrame() + { + envir().taskScheduler().triggerEvent(m_eventTriggerId, this); + } + + void FFmpegH264Source::doGetNextFrame() + { + deliverFrame(); + } + + void FFmpegH264Source::deliverFrame() + { + if (!isCurrentlyAwaitingData()) return; // we're not ready for the data yet + + static uint8_t* newFrameDataStart; + static unsigned newFrameSize = 0; + + /* get the data frame from the Encoding thread.. */ + if (Encoding_Source->GetFrame(&newFrameDataStart, &newFrameSize)){ + if (newFrameDataStart!=NULL) { + /* This should never happen, but check anyway.. */ + if (newFrameSize > fMaxSize) { + fFrameSize = fMaxSize; + fNumTruncatedBytes = newFrameSize - fMaxSize; + } else { + fFrameSize = newFrameSize; + } + + gettimeofday(&fPresentationTime, NULL); + memcpy(fTo, newFrameDataStart, fFrameSize); + + //delete newFrameDataStart; + //newFrameSize = 0; + + Encoding_Source->ReleaseFrame(); + } + else { + fFrameSize=0; + fTo=NULL; + handleClosure(this); + } + }else + { + fFrameSize = 0; + } + + if(fFrameSize>0) + FramedSource::afterGetting(this); + + } +} \ No newline at end of file diff --git a/RtspFace/FFmpegRTSPServer/FFmpegH264Source.h b/RtspFace/FFmpegRTSPServer/FFmpegH264Source.h new file mode 100644 index 0000000..8b7a660 --- /dev/null +++ b/RtspFace/FFmpegRTSPServer/FFmpegH264Source.h @@ -0,0 +1,43 @@ +// +// FFmpegH264Source.h +// FFmpegRTSPServer +// +// Created by Mina Saad on 9/22/15. +// Copyright (c) 2015 Mina Saad. All rights reserved. +// + +#ifndef MESAI_FFMPEGH264_SOURCE_HH +#define MESAI_FFMPEGH264_SOURCE_HH + + +#include <functional> +#include <FramedSource.hh> +#include <UsageEnvironment.hh> +#include <Groupsock.hh> +#include "IEncoder.h" + +namespace MESAI +{ + + class FFmpegH264Source : public FramedSource { + public: + static FFmpegH264Source* createNew(UsageEnvironment& env, IEncoder * E_Source); + FFmpegH264Source(UsageEnvironment& env, IEncoder * E_Source); + ~FFmpegH264Source(); + + private: + static void deliverFrameStub(void* clientData) {((FFmpegH264Source*) clientData)->deliverFrame();}; + virtual void doGetNextFrame(); + void deliverFrame(); + virtual void doStopGettingFrames(); + void onFrame(); + + + private: + IEncoder * Encoding_Source; + EventTriggerId m_eventTriggerId; + + }; + +} +#endif diff --git a/RtspFace/FFmpegRTSPServer/IEncoder.h b/RtspFace/FFmpegRTSPServer/IEncoder.h new file mode 100644 index 0000000..f71cb72 --- /dev/null +++ b/RtspFace/FFmpegRTSPServer/IEncoder.h @@ -0,0 +1,19 @@ +#ifndef MESAI_I_ENCODER_H +#define MESAI_I_ENCODER_H + +#include <functional> + +namespace MESAI +{ + class IEncoder + { + public: + IEncoder() { } + virtual ~IEncoder() { } + virtual void setCallbackFunctionFrameIsReady(std::function<void()> func) = 0; + virtual char GetFrame(u_int8_t** FrameBuffer, unsigned int *FrameSize) = 0; + virtual char ReleaseFrame() = 0; + }; +} + +#endif diff --git a/RtspFace/FFmpegRTSPServer/LiveRTSPServer.cpp b/RtspFace/FFmpegRTSPServer/LiveRTSPServer.cpp new file mode 100644 index 0000000..2f6790a --- /dev/null +++ b/RtspFace/FFmpegRTSPServer/LiveRTSPServer.cpp @@ -0,0 +1,79 @@ +// +// LiveRTSPServer.cpp +// FFmpegRTSPServer +// +// Created by Mina Saad on 9/22/15. +// Copyright (c) 2015 Mina Saad. All rights reserved. +// + +#include "LiveRTSPServer.h" + +namespace MESAI +{ + LiveRTSPServer::LiveRTSPServer( IEncoder * a_Encoder, int port, int httpPort ) + : m_Encoder (a_Encoder), portNumber(port), httpTunnelingPort(httpPort) + { + quit = 0; + } + + LiveRTSPServer::~LiveRTSPServer() + { + + } + + void LiveRTSPServer::run() + { + TaskScheduler *scheduler; + UsageEnvironment *env ; + char RTSP_Address[1024]; + RTSP_Address[0]=0x00; + + scheduler = BasicTaskScheduler::createNew(); + env = BasicUsageEnvironment::createNew(*scheduler); + + UserAuthenticationDatabase* authDB = NULL; + + // if (m_Enable_Pass){ + // authDB = new UserAuthenticationDatabase; + // authDB->addUserRecord(UserN, PassW); + // } + + OutPacketBuffer::maxSize = 2000000; + RTSPServer* rtspServer = RTSPServer::createNew(*env, portNumber, authDB); + + if (rtspServer == NULL) + { + *env <<"LIVE555: Failed to create RTSP server: %s\n", env->getResultMsg(); + } + else { + + + if(httpTunnelingPort) + { + rtspServer->setUpTunnelingOverHTTP(httpTunnelingPort); + } + + char const* descriptionString = "MESAI Streaming Session"; + + FFmpegH264Source * source = FFmpegH264Source::createNew(*env,m_Encoder); + StreamReplicator * inputDevice = StreamReplicator::createNew(*env, source, false); + + ServerMediaSession* sms = ServerMediaSession::createNew(*env, RTSP_Address, RTSP_Address, descriptionString); + sms->addSubsession(MESAI::LiveServerMediaSubsession::createNew(*env, inputDevice)); + rtspServer->addServerMediaSession(sms); + + char* url = rtspServer->rtspURL(sms); + *env << "Play this stream using the URL \"" << url << "\"\n"; + delete [] url; + + //signal(SIGNIT,sighandler); + env->taskScheduler().doEventLoop(&quit); // does not return + + Medium::close(rtspServer); + Medium::close(inputDevice); + } + + env->reclaim(); + delete scheduler; + } +} \ No newline at end of file diff --git a/RtspFace/FFmpegRTSPServer/LiveRTSPServer.h b/RtspFace/FFmpegRTSPServer/LiveRTSPServer.h new file mode 100644 index 0000000..e1f3eed --- /dev/null +++ b/RtspFace/FFmpegRTSPServer/LiveRTSPServer.h @@ -0,0 +1,39 @@ +// +// LiveRTSPServer.h +// FFmpegRTSPServer +// +// Created by Mina Saad on 9/22/15. +// Copyright (c) 2015 Mina Saad. All rights reserved. +// + +#ifndef MESAI_LIVE_RTSP_SERVER_HH +#define MESAI_LIVE_RTSP_SERVER_HH + +#include <UsageEnvironment.hh> +#include <BasicUsageEnvironment.hh> +#include <GroupsockHelper.hh> +#include <liveMedia.hh> +#include "LiveServerMediaSubsession.h" +#include "FFmpegH264Source.h" +#include "IEncoder.h" + +namespace MESAI { + + class LiveRTSPServer + { + public: + + LiveRTSPServer(IEncoder * a_Encoder, int port, int httpPort ); + ~LiveRTSPServer(); + void run(); + + private: + int portNumber; + int httpTunnelingPort; + IEncoder * m_Encoder; + char quit; + + }; +} + +#endif diff --git a/RtspFace/FFmpegRTSPServer/LiveServerMediaSubsession.cpp b/RtspFace/FFmpegRTSPServer/LiveServerMediaSubsession.cpp new file mode 100644 index 0000000..6d16082 --- /dev/null +++ b/RtspFace/FFmpegRTSPServer/LiveServerMediaSubsession.cpp @@ -0,0 +1,29 @@ +// +// LiveServerMediaSubsession.cpp +// FFmpegRTSPServer +// +// Created by Mina Saad on 9/22/15. +// Copyright (c) 2015 Mina Saad. All rights reserved. +// + +#include "LiveServerMediaSubsession.h" + +namespace MESAI +{ + LiveServerMediaSubsession * LiveServerMediaSubsession::createNew(UsageEnvironment& env, StreamReplicator* replicator) + { + return new LiveServerMediaSubsession(env,replicator); + } + + FramedSource* LiveServerMediaSubsession::createNewStreamSource(unsigned clientSessionId, unsigned& estBitrate) + { + FramedSource* source = m_replicator->createStreamReplica(); + return H264VideoStreamDiscreteFramer::createNew(envir(), source); + } + + RTPSink* LiveServerMediaSubsession::createNewRTPSink(Groupsock* rtpGroupsock, unsigned char rtpPayloadTypeIfDynamic, FramedSource* inputSource) + { + return H264VideoRTPSink::createNew(envir(), rtpGroupsock,rtpPayloadTypeIfDynamic); + } + +} diff --git a/RtspFace/FFmpegRTSPServer/LiveServerMediaSubsession.h b/RtspFace/FFmpegRTSPServer/LiveServerMediaSubsession.h new file mode 100644 index 0000000..22bfbc5 --- /dev/null +++ b/RtspFace/FFmpegRTSPServer/LiveServerMediaSubsession.h @@ -0,0 +1,39 @@ +// +// LiveServerMediaSubsession.h +// FFmpegRTSPServer +// +// Created by Mina Saad on 9/22/15. +// Copyright (c) 2015 Mina Saad. All rights reserved. +// + +#ifndef MESAI_Live_SERVER_MEDIA_SUBSESSION_HH +#define MESAI_Live_SERVER_MEDIA_SUBSESSION_HH + +#include <OnDemandServerMediaSubsession.hh> +#include <StreamReplicator.hh> +#include <H264VideoRTPSink.hh> +#include <H264VideoStreamFramer.hh> +#include <H264VideoStreamDiscreteFramer.hh> +#include <UsageEnvironment.hh> +#include <Groupsock.hh> + +namespace MESAI +{ + + class LiveServerMediaSubsession: public OnDemandServerMediaSubsession + { + public: + static LiveServerMediaSubsession* createNew(UsageEnvironment& env, StreamReplicator* replicator); + + protected: + LiveServerMediaSubsession(UsageEnvironment& env, StreamReplicator* replicator) + : OnDemandServerMediaSubsession(env, False), m_replicator(replicator) {}; + + virtual FramedSource* createNewStreamSource(unsigned clientSessionId, unsigned& estBitrate); + virtual RTPSink* createNewRTPSink(Groupsock* rtpGroupsock, unsigned char rtpPayloadTypeIfDynamic, FramedSource* inputSource); + + StreamReplicator * m_replicator; + }; + +} +#endif \ No newline at end of file diff --git a/RtspFace/FFmpegRTSPServer/main.cpp b/RtspFace/FFmpegRTSPServer/main.cpp new file mode 100644 index 0000000..38d22fc --- /dev/null +++ b/RtspFace/FFmpegRTSPServer/main.cpp @@ -0,0 +1,78 @@ +// +// main.cpp +// FFmpegRTSPServer +// +// Created by Mina Saad on 9/22/15. +// Copyright (c) 2015 Mina Saad. All rights reserved. +// + +#include "LiveRTSPServer.h" +#include "FFmpegH264Encoder.h" +#include "FFmpegDecoder.h" + +MESAI::FFmpegH264Encoder * encoder; +MESAI::LiveRTSPServer * server; +MESAI::FFmpegDecoder * decoder; + +int UDPPort; +int HTTPTunnelPort; +pthread_t thread1; +pthread_t thread2; + + +void * runServer(void * server) +{ + ((MESAI::LiveRTSPServer * ) server)->run(); + pthread_exit(NULL); +} + +void * runEncoder(void * encoder) +{ + ((MESAI::FFmpegH264Encoder * ) encoder)->run(); + pthread_exit(NULL); +} + +void onFrame(uint8_t * data) +{ + encoder->SendNewFrame(data); +} + +int test_main(int argc, const char * argv[]) +{ + if(argc==2) + decoder = new MESAI::FFmpegDecoder(argv[1]); + if(argc==3) + UDPPort = atoi(argv[2]); + if(argc==4) + HTTPTunnelPort = atoi(argv[3]); + decoder->intialize(); + decoder->setOnframeCallbackFunction(onFrame); + encoder = new MESAI::FFmpegH264Encoder(); + encoder->SetupVideo("dummy.avi",decoder->width,decoder->height,decoder->frameRate,decoder->GOP,decoder->bitrate); + server = new MESAI::LiveRTSPServer(encoder, UDPPort, HTTPTunnelPort); + + pthread_attr_t attr1; + pthread_attr_init(&attr1); + pthread_attr_setdetachstate(&attr1, PTHREAD_CREATE_DETACHED); + int rc1 = pthread_create(&thread1, &attr1, runServer, server); + + if (rc1){ + //exception + return -1; + } + + pthread_attr_t attr2; + pthread_attr_init(&attr2); + pthread_attr_setdetachstate(&attr2, PTHREAD_CREATE_DETACHED); + int rc2 = pthread_create(&thread2, &attr2, runEncoder, encoder); + + if (rc2){ + //exception + return -1; + } + + // Play Media Here + decoder->playMedia(); + decoder->finalize(); + +} diff --git a/RtspFace/PL_RTSPClient.cpp b/RtspFace/PL_RTSPClient.cpp index b26b2c1..41dad6a 100644 --- a/RtspFace/PL_RTSPClient.cpp +++ b/RtspFace/PL_RTSPClient.cpp @@ -5,11 +5,11 @@ void rtsp_client_fmtp_callback(void* arg, const char* val); void rtsp_client_frame_callback(void* arg, uint8_t* buffer, size_t buffSize); void rtsp_client_continue_callback(void* arg); +//struct RTSPConfig; #include "live555/testProgs/testRTSPClient.hpp" struct RTSPClient_Internal { - PL_RTSPClient* client; RTSPConfig rtspConfig; pthread_t live_daemon_thid; char eventLoopWatchVariable; @@ -21,7 +21,7 @@ size_t lastBuffSize; RTSPClient_Internal() : - client(nullptr), rtspConfig(), live_daemon_thid(0), + rtspConfig(), live_daemon_thid(0), eventLoopWatchVariable(0), live_daemon_running(false), frame_mutex(new pthread_mutex_t), continue_mutex(new pthread_mutex_t), lastBuffer(nullptr), lastBuffSize(0) @@ -49,9 +49,8 @@ void reset() { - client = nullptr; - rtspConfig.progName = ""; - rtspConfig.rtspURL = ""; + RTSPConfig _rtspConfig; + rtspConfig = _rtspConfig; live_daemon_thid = 0; eventLoopWatchVariable = 0; live_daemon_running = false; @@ -81,7 +80,7 @@ } }; -void* live_daemon_thd(void* arg) +static void* live_daemon_thd(void* arg) { RTSPClient_Internal* in = (RTSPClient_Internal*)arg; @@ -90,7 +89,7 @@ usage(*env, in->rtspConfig.progName.c_str()); - openURL(*env, in->client, in->rtspConfig.progName.c_str(), in->rtspConfig.rtspURL.c_str()); + openURL(*env, in->rtspConfig); in->live_daemon_running = true; env->taskScheduler().doEventLoop(&(in->eventLoopWatchVariable)); @@ -120,8 +119,8 @@ const RTSPConfig* config = reinterpret_cast<const RTSPConfig*>(args); RTSPClient_Internal* in = (RTSPClient_Internal*)internal; in->reset(); - in->client = this; in->rtspConfig = *config; + in->rtspConfig.args = this; int ret = pthread_mutex_lock(in->frame_mutex); if(ret != 0) diff --git a/RtspFace/PL_RTSPClient.h b/RtspFace/PL_RTSPClient.h index b5367fb..e464586 100644 --- a/RtspFace/PL_RTSPClient.h +++ b/RtspFace/PL_RTSPClient.h @@ -8,8 +8,14 @@ { std::string progName; std::string rtspURL; + bool aux; // frame data start with 0x00000001 + int verbosityLevel; + int tunnelOverHTTPPortNum; // portNumBits + void* args; - RTSPConfig() : progName(), rtspURL() { } + RTSPConfig() : + progName(), rtspURL() ,aux(true), verbosityLevel(1), tunnelOverHTTPPortNum(0), args(nullptr) + { } }; class PL_RTSPClient : public PipeLineElem diff --git a/RtspFace/PL_RTSPServer.cpp b/RtspFace/PL_RTSPServer.cpp index 5ea97c2..feb25ac 100644 --- a/RtspFace/PL_RTSPServer.cpp +++ b/RtspFace/PL_RTSPServer.cpp @@ -3,7 +3,12 @@ #include <liveMedia.hh> #include <BasicUsageEnvironment.hh> -class MyH264FramedSource; +#include "FFmpegRTSPServer/IEncoder.h" +#include "FFmpegRTSPServer/LiveRTSPServer.h" +#include "FFmpegRTSPServer/FFmpegH264Source.h" +#include "FFmpegRTSPServer/LiveServerMediaSubsession.h" + +class MyEncoderStub; struct RTSPServer_Internal { @@ -15,31 +20,13 @@ pthread_mutex_t* frame_mutex; bool live_daemon_running; - UsageEnvironment* env; + MESAI::LiveRTSPServer * server; + MyEncoderStub * encoderStub; - // To make the second and subsequent client for each stream reuse the same - // input stream as the first client (rather than playing the file from the - // start for each client), change the following "False" to "True": - Boolean reuseFirstSource; - - // To stream *only* MPEG-1 or 2 video "I" frames - // (e.g., to reduce network bandwidth), - // change the following "False" to "True": - Boolean iFramesOnly; - - UserAuthenticationDatabase* authDB; - - RTSPServer* rtspServer;//#todo delete - - char descriptionString[1024]; - - MyH264FramedSource* pMyH264FramedSource; - RTSPServer_Internal() : buffer(nullptr), buffSize(0), - payError(true), live_daemon_thid(0), frame_mutex(nullptr), live_daemon_running(false), - env(nullptr), reuseFirstSource(False), iFramesOnly(False), authDB(nullptr), - rtspServer(nullptr) + payError(true), live_daemon_thid(0), frame_mutex(new pthread_mutex_t), live_daemon_running(false), + server(nullptr), encoderStub(nullptr) { pthread_mutex_init(frame_mutex, NULL); } @@ -74,102 +61,76 @@ live_daemon_thid = 0; live_daemon_running = false; - env = nullptr; - reuseFirstSource = False; - iFramesOnly = False; - authDB = nullptr; - rtspServer = nullptr; - - strcpy(descriptionString, "Session streamed by \"testOnDemandRTSPServer\""); - - pMyH264FramedSource = nullptr; + server = nullptr; + encoderStub = nullptr; } }; - -class MyH264FramedSource : public FramedSource +class MyEncoderStub : public MESAI::IEncoder { public: - static MyH264FramedSource* createNew(UsageEnvironment& _env, RTSPServer_Internal& _in) + MyEncoderStub(RTSPServer_Internal& _in) : in(_in) { - return new MyH264FramedSource(_env, _in); } - // deliver frame to the sink - bool deliverFrame() + virtual ~MyEncoderStub() { - int ret = false; - if (isCurrentlyAwaitingData()) + } + + virtual void setCallbackFunctionFrameIsReady(std::function<void()> func) + { + onFrame = func; + } + + virtual char GetFrame(u_int8_t** FrameBuffer, unsigned int *FrameSize) + { + if (in.buffer != nullptr && in.buffSize > 0) { - fDurationInMicroseconds = 0; - fFrameSize = 0; + *FrameBuffer = in.buffer; + *FrameSize = in.buffSize; + + printf("send frame size=%u\n", in.buffSize); - if (in.buffSize > fMaxSize) - { - fFrameSize = fMaxSize; - fNumTruncatedBytes = in.buffSize - fMaxSize; - } - else - { - fFrameSize = in.buffSize; - } + in.buffer = nullptr; + in.buffSize = 0; - if (fFrameSize > 0) - { - memcpy(fTo, in.buffer, fFrameSize); - - int ret = pthread_mutex_unlock(in.frame_mutex); - if(ret != 0) - { - printf("pthread_mutex_unlock frame_mutex: %s/n", strerror(ret)); - return false; - } - - ret = true; - } + return 1; } - - return ret; - } - -protected: - MyH264FramedSource(UsageEnvironment& _env, RTSPServer_Internal& _in) : - FramedSource(_env), env(_env), in(_in) - { + else + { + ReleaseFrame(); + return 0; + } } - virtual ~MyH264FramedSource() + virtual char ReleaseFrame() { + int ret = pthread_mutex_unlock(in.frame_mutex); + if(ret != 0) + { + printf("pthread_mutex_unlock frame_mutex: %s/n", strerror(ret)); + return 0; + } + + return 1; } - // overide FramedSource - virtual void doGetNextFrame() + void deliverFrame() { - printf("MyH264FramedSource::doGetNextFrame\n"); - + // write frame buffer of RTSPServer_Internal::buffer + onFrame(); + int ret = pthread_mutex_lock(in.frame_mutex); if(ret != 0) { printf("pthread_mutex_lock frame_mutex: %s/n", strerror(ret)); return; } + } - // deliverFrame - //if (fFrameSize > 0) - //{ - // send Frame to the consumer - FramedSource::afterGetting(this); - //} - } - - virtual void doStopGettingFrames() - { - FramedSource::doStopGettingFrames(); - } - private: - UsageEnvironment& env; RTSPServer_Internal& in; + std::function<void()> onFrame; }; PipeLineElem* create_PL_RTSPServer() @@ -184,60 +145,20 @@ PL_RTSPServer::~PL_RTSPServer() { delete (RTSPServer_Internal*)internal; - internal= nullptr; + internal = nullptr; } -void* live_daemon_thd(void* arg) +static void* live_daemon_thd(void* arg) { RTSPServer_Internal* in = (RTSPServer_Internal*)arg; + + MyEncoderStub encoder(*in); + in->encoderStub = &encoder; + in->server = new MESAI::LiveRTSPServer(&encoder, 8554, 8080); - // Begin by setting up our usage environment: - TaskScheduler* scheduler = BasicTaskScheduler::createNew(); - in->env = BasicUsageEnvironment::createNew(*scheduler); - -#ifdef ACCESS_CONTROL - // To implement client access control to the RTSP server, do the following: - in->authDB = new UserAuthenticationDatabase; - in->authDB->addUserRecord("username1", "password1"); // replace these with real strings - // Repeat the above with each <username>, <password> that you wish to allow - // access to the server. -#endif - - // Create the RTSP server: - in->rtspServer = RTSPServer::createNew(*(in->env), 8554, in->authDB); - if (in->rtspServer == NULL) - { - *(in->env) << "Failed to create RTSP server: " << in->env->getResultMsg() << "\n"; - return nullptr; - } - - // Set up each of the possible streams that can be served by the - // RTSP server. Each such stream is implemented using a - // "ServerMediaSession" object, plus one or more - // "ServerMediaSubsession" objects for each audio/video substream. - - char const* streamName = "plH264Encoder"; - ServerMediaSession* sms = ServerMediaSession::createNew(*(in->env), streamName, streamName, in->descriptionString); - in->pMyH264FramedSource = MyH264FramedSource::createNew(*(in->env), *in); - sms->addSubsession(in->pMyH264FramedSource); - in->rtspServer->addServerMediaSession(sms); - - // announceStream - char* url = in->rtspServer->rtspURL(sms); - *(in->env) << "Play this stream using the URL " << url << "\n"; - delete[] url; - - // Also, attempt to create a HTTP server for RTSP-over-HTTP tunneling. - // Try first with the default HTTP port (80), and then with the alternative HTTP - // port numbers (8000 and 8080). - - if (in->rtspServer->setUpTunnelingOverHTTP(80)) - *(in->env) << "\n(We use port " << in->rtspServer->httpServerPortNum() << " for optional RTSP-over-HTTP tunneling.)\n"; - else - *(in->env) << "\n(RTSP-over-HTTP tunneling is not available.)\n"; - in->live_daemon_running = true; - in->env->taskScheduler().doEventLoop(); // does not return + in->server->run(); // does not return + in->encoderStub = nullptr; in->live_daemon_running = false; } @@ -245,15 +166,8 @@ { RTSPServer_Internal* in = (RTSPServer_Internal*)internal; in->reset(); - - int ret = pthread_mutex_lock(in->frame_mutex); - if(ret != 0) - { - printf("pthread_mutex_lock frame_mutex: %s/n", strerror(ret)); - return false; - } - - ret = pthread_create(&(in->live_daemon_thid), NULL, live_daemon_thd, in); + + int ret = pthread_create(&(in->live_daemon_thid), NULL, live_daemon_thd, in); if(ret != 0) { printf("pthread_create: %s/n", strerror(ret)); @@ -274,10 +188,17 @@ { RTSPServer_Internal* in = (RTSPServer_Internal*)internal; + if (pm.buffer == nullptr || pm.buffSize <= 0) + return false; + in->buffer = pm.buffer; in->buffSize = pm.buffSize; - return in->pMyH264FramedSource->deliverFrame(); + if (in->encoderStub == nullptr) + return false; + + in->encoderStub->deliverFrame(); + return true; } bool PL_RTSPServer::gain(PipeMaterial& pm) diff --git a/RtspFace/live555/testProgs/testRTSPClient.hpp b/RtspFace/live555/testProgs/testRTSPClient.hpp index bf37d55..f7f5c4f 100644 --- a/RtspFace/live555/testProgs/testRTSPClient.hpp +++ b/RtspFace/live555/testProgs/testRTSPClient.hpp @@ -25,6 +25,19 @@ #include <iostream> +#define RTSP_CLIENT_VERBOSITY_LEVEL 1 // by default, print verbose output from each "RTSPClient" + +// By default, we request that the server stream its data using RTP/UDP. +// If, instead, you want to request that the server stream via RTP-over-TCP, change the following to True: +#define REQUEST_STREAMING_OVER_TCP False + +// Even though we're not going to be doing anything with the incoming data, we still need to receive it. +// Define the size of the buffer that we'll use: +#define DUMMY_SINK_RECEIVE_BUFFER_SIZE 1920*1080*3 + +// If you don't want to see debugging output for each received frame, then comment out the following line: +#define DEBUG_PRINT_EACH_RECEIVED_FRAME 1 + // Forward function definitions: // RTSP 'response handlers': @@ -39,7 +52,7 @@ // called at the end of a stream's expected duration (if the stream has not already signaled its end using a RTCP "BYE") // The main streaming routine (for each "rtsp://" URL): -void openURL(UsageEnvironment& env, void* args, char const* progName, char const* rtspURL); +void openURL(UsageEnvironment& env, const RTSPConfig& _rtspConfig); // Used to iterate through each stream's 'subsessions', setting up each one: void setupNextSubsession(RTSPClient* rtspClient); @@ -75,9 +88,18 @@ return 1; } + RTSPConfig rtspConfig; + rtspConfig.progName = argv[0]; + rtspConfig.rtspURL = ""; + rtspConfig.aux = false; + rtspConfig.verbosityLevel = RTSP_CLIENT_VERBOSITY_LEVEL; + rtspConfig.tunnelOverHTTPPortNum = 0; + rtspConfig.args = nullptr; + // There are argc-1 URLs: argv[1] through argv[argc-1]. Open and start streaming each one: for (int i = 1; i <= argc-1; ++i) { - openURL(*env, NULL, argv[0], argv[i]); + rtspConfig.rtspURL = argv[i]; + openURL(*env, rtspConfig); } // All subsequent activity takes place within the event loop: @@ -117,20 +139,16 @@ class ourRTSPClient: public RTSPClient { public: - static ourRTSPClient* createNew(UsageEnvironment& env, char const* rtspURL, - int verbosityLevel = 0, - char const* applicationName = NULL, - portNumBits tunnelOverHTTPPortNum = 0); + static ourRTSPClient* createNew(UsageEnvironment& env, const RTSPConfig& _rtspConfig); protected: - ourRTSPClient(UsageEnvironment& env, char const* rtspURL, - int verbosityLevel, char const* applicationName, portNumBits tunnelOverHTTPPortNum); + ourRTSPClient(UsageEnvironment& env, const RTSPConfig& _rtspConfig); // called only by createNew(); virtual ~ourRTSPClient(); public: StreamClientState scs; - void* args; + const RTSPConfig& rtspConfig; }; // Define a data sink (a subclass of "MediaSink") to receive the data for each subsession (i.e., each audio or video 'substream'). @@ -142,12 +160,12 @@ { public: static DummySink* createNew(UsageEnvironment& env, - void* _args, + const RTSPConfig& _rtspConfig, MediaSubsession& subsession, // identifies the kind of data that's being received char const* streamId = NULL); // identifies the stream itself (optional) private: - DummySink(UsageEnvironment& env, void* _args, MediaSubsession& subsession, char const* streamId); + DummySink(UsageEnvironment& env, const RTSPConfig& _rtspConfig, MediaSubsession& subsession, char const* streamId); // called only by "createNew()" virtual ~DummySink(); @@ -159,7 +177,7 @@ struct timeval presentationTime, unsigned durationInMicroseconds); public: - void* args; + const RTSPConfig& rtspConfig; private: // redefined virtual functions: @@ -171,74 +189,75 @@ char* fStreamId; }; -#define RTSP_CLIENT_VERBOSITY_LEVEL 1 // by default, print verbose output from each "RTSPClient" - static unsigned rtspClientCount = 0; // Counts how many streams (i.e., "RTSPClient"s) are currently in use. -void openURL(UsageEnvironment& env, void* args, char const* progName, char const* rtspURL) { - // Begin by creating a "RTSPClient" object. Note that there is a separate "RTSPClient" object for each stream that we wish - // to receive (even if more than stream uses the same "rtsp://" URL). - RTSPClient* rtspClient = ourRTSPClient::createNew(env, rtspURL, RTSP_CLIENT_VERBOSITY_LEVEL, progName); - if (rtspClient == NULL) { - env << "Failed to create a RTSP client for URL \"" << rtspURL << "\": " << env.getResultMsg() << "\n"; - return; - } - - ((ourRTSPClient*)rtspClient)->args = args; +void openURL(UsageEnvironment& env, const RTSPConfig& _rtspConfig) +{ + // Begin by creating a "RTSPClient" object. Note that there is a separate "RTSPClient" object for each stream that we wish + // to receive (even if more than stream uses the same "rtsp://" URL). + RTSPClient* rtspClient = ourRTSPClient::createNew(env, _rtspConfig); + if (rtspClient == NULL) + { + env << "Failed to create a RTSP client for URL \"" << _rtspConfig.rtspURL.c_str() << "\": " << env.getResultMsg() << "\n"; + return; + } - ++rtspClientCount; + ++rtspClientCount; - // Next, send a RTSP "DESCRIBE" command, to get a SDP description for the stream. - // Note that this command - like all RTSP commands - is sent asynchronously; we do not block, waiting for a response. - // Instead, the following function call returns immediately, and we handle the RTSP response later, from within the event loop: - rtspClient->sendDescribeCommand(continueAfterDESCRIBE); + // Next, send a RTSP "DESCRIBE" command, to get a SDP description for the stream. + // Note that this command - like all RTSP commands - is sent asynchronously; we do not block, waiting for a response. + // Instead, the following function call returns immediately, and we handle the RTSP response later, from within the event loop: + rtspClient->sendDescribeCommand(continueAfterDESCRIBE); } // Implementation of the RTSP 'response handlers': -void continueAfterDESCRIBE(RTSPClient* rtspClient, int resultCode, char* resultString) { - do { - UsageEnvironment& env = rtspClient->envir(); // alias - StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs; // alias +void continueAfterDESCRIBE(RTSPClient* rtspClient, int resultCode, char* resultString) +{ + do + { + UsageEnvironment& env = rtspClient->envir(); // alias + StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs; // alias - if (resultCode != 0) { - env << *rtspClient << "Failed to get a SDP description: " << resultString << "\n"; - delete[] resultString; - break; - } + if (resultCode != 0) + { + env << *rtspClient << "Failed to get a SDP description: " << resultString << "\n"; + delete[] resultString; + break; + } - char* const sdpDescription = resultString; - env << *rtspClient << "Got a SDP description:\n" << sdpDescription << "\n"; + char* const sdpDescription = resultString; + env << *rtspClient << "Got a SDP description:\n" << sdpDescription << "\n"; - // Create a media session object from this SDP description: - scs.session = MediaSession::createNew(env, sdpDescription); - delete[] sdpDescription; // because we don't need it anymore - if (scs.session == NULL) { - env << *rtspClient << "Failed to create a MediaSession object from the SDP description: " << env.getResultMsg() << "\n"; - break; - } else if (!scs.session->hasSubsessions()) { - env << *rtspClient << "This session has no media subsessions (i.e., no \"m=\" lines)\n"; - break; - } + // Create a media session object from this SDP description: + scs.session = MediaSession::createNew(env, sdpDescription); + delete[] sdpDescription; // because we don't need it anymore + if (scs.session == NULL) + { + env << *rtspClient << "Failed to create a MediaSession object from the SDP description: " << env.getResultMsg() << "\n"; + break; + } + else if (!scs.session->hasSubsessions()) + { + env << *rtspClient << "This session has no media subsessions (i.e., no \"m=\" lines)\n"; + break; + } - // Then, create and set up our data source objects for the session. We do this by iterating over the session's 'subsessions', - // calling "MediaSubsession::initiate()", and then sending a RTSP "SETUP" command, on each one. - // (Each 'subsession' will have its own data source.) - scs.iter = new MediaSubsessionIterator(*scs.session); - setupNextSubsession(rtspClient); - return; - } while (0); + // Then, create and set up our data source objects for the session. We do this by iterating over the session's 'subsessions', + // calling "MediaSubsession::initiate()", and then sending a RTSP "SETUP" command, on each one. + // (Each 'subsession' will have its own data source.) + scs.iter = new MediaSubsessionIterator(*scs.session); + setupNextSubsession(rtspClient); + return; + } while (0); - // An unrecoverable error occurred with this stream. - shutdownStream(rtspClient); + // An unrecoverable error occurred with this stream. + shutdownStream(rtspClient); } -// By default, we request that the server stream its data using RTP/UDP. -// If, instead, you want to request that the server stream via RTP-over-TCP, change the following to True: -#define REQUEST_STREAMING_OVER_TCP False - -void setupNextSubsession(RTSPClient* rtspClient) { +void setupNextSubsession(RTSPClient* rtspClient) +{ UsageEnvironment& env = rtspClient->envir(); // alias StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs; // alias @@ -294,9 +313,8 @@ // (This will prepare the data sink to receive data; the actual flow of data from the client won't start happening until later, // after we've sent a RTSP "PLAY" command.) - DummySink* mySink; - scs.subsession->sink = mySink = DummySink::createNew(env, ((ourRTSPClient*)rtspClient)->args, - *scs.subsession, rtspClient->url()); + scs.subsession->sink = DummySink::createNew(env, ((ourRTSPClient*)rtspClient)->rtspConfig, + *scs.subsession, rtspClient->url()); // perhaps use your own custom "MediaSink" subclass instead if (scs.subsession->sink == NULL) { env << *rtspClient << "Failed to create a data sink for the \"" << *scs.subsession @@ -446,15 +464,14 @@ // Implementation of "ourRTSPClient": -ourRTSPClient* ourRTSPClient::createNew(UsageEnvironment& env, char const* rtspURL, - int verbosityLevel, char const* applicationName, portNumBits tunnelOverHTTPPortNum) { - return new ourRTSPClient(env, rtspURL, verbosityLevel, applicationName, tunnelOverHTTPPortNum); +ourRTSPClient* ourRTSPClient::createNew(UsageEnvironment& env, const RTSPConfig& _rtspConfig) +{ + return new ourRTSPClient(env, _rtspConfig); } -ourRTSPClient::ourRTSPClient(UsageEnvironment& env, char const* rtspURL, - int verbosityLevel, char const* applicationName, portNumBits tunnelOverHTTPPortNum) - : RTSPClient(env,rtspURL, verbosityLevel, applicationName, tunnelOverHTTPPortNum, -1), - args(nullptr) +ourRTSPClient::ourRTSPClient(UsageEnvironment& env, const RTSPConfig& _rtspConfig) + : RTSPClient(env, _rtspConfig.rtspURL.c_str(), _rtspConfig.verbosityLevel, _rtspConfig.progName.c_str(), + _rtspConfig.tunnelOverHTTPPortNum, -1), rtspConfig(_rtspConfig) { } @@ -481,30 +498,29 @@ // Implementation of "DummySink": -// Even though we're not going to be doing anything with the incoming data, we still need to receive it. -// Define the size of the buffer that we'll use: -#define DUMMY_SINK_RECEIVE_BUFFER_SIZE 1920*1080*3 - -DummySink* DummySink::createNew(UsageEnvironment& env, void* _args, MediaSubsession& subsession, char const* streamId) +DummySink* DummySink::createNew(UsageEnvironment& env, const RTSPConfig& _rtspConfig, MediaSubsession& subsession, char const* streamId) { - return new DummySink(env, _args, subsession, streamId); + return new DummySink(env, _rtspConfig, subsession, streamId); } -DummySink::DummySink(UsageEnvironment& env, void* _args, MediaSubsession& subsession, char const* streamId) - : MediaSink(env), args(_args), fSubsession(subsession) +DummySink::DummySink(UsageEnvironment& env, const RTSPConfig& _rtspConfig, MediaSubsession& subsession, char const* streamId) + : MediaSink(env), rtspConfig(_rtspConfig), fSubsession(subsession) { fStreamId = strDup(streamId); fReceiveBuffer = new u_int8_t[DUMMY_SINK_RECEIVE_BUFFER_SIZE]; // ffmpeg need AUX header - fReceiveBuffer[0]=0x00; fReceiveBuffer[1]=0x00; fReceiveBuffer[2]=0x00; fReceiveBuffer[3]=0x01; + if (rtspConfig.aux) + { + fReceiveBuffer[0]=0x00; fReceiveBuffer[1]=0x00; fReceiveBuffer[2]=0x00; fReceiveBuffer[3]=0x01; + } //parse sdp const char* strSDP = fSubsession.savedSDPLines(); - rtsp_client_sdp_callback(args, strSDP); + rtsp_client_sdp_callback(rtspConfig.args, strSDP); const char* strFmtp = fSubsession.fmtp_spropparametersets(); - rtsp_client_fmtp_callback(args, strFmtp); + rtsp_client_fmtp_callback(rtspConfig.args, strFmtp); //std::cout << strFmtp << std::endl; } @@ -514,17 +530,20 @@ } void DummySink::afterGettingFrame(void* clientData, unsigned frameSize, unsigned numTruncatedBytes, - struct timeval presentationTime, unsigned durationInMicroseconds) { - DummySink* sink = (DummySink*)clientData; + struct timeval presentationTime, unsigned durationInMicroseconds) +{ + DummySink* sink = (DummySink*)clientData; - if (frameSize > 0) - rtsp_client_frame_callback(sink->args, sink->fReceiveBuffer, frameSize + 4); + if (frameSize > 0) + { + unsigned s = frameSize; + if (sink->rtspConfig.aux) + s += 4; + rtsp_client_frame_callback(sink->rtspConfig.args, sink->fReceiveBuffer, s); + } - sink->afterGettingFrame(frameSize, numTruncatedBytes, presentationTime, durationInMicroseconds); + sink->afterGettingFrame(frameSize, numTruncatedBytes, presentationTime, durationInMicroseconds); } - -// If you don't want to see debugging output for each received frame, then comment out the following line: -#define DEBUG_PRINT_EACH_RECEIVED_FRAME 1 void DummySink::afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes, struct timeval presentationTime, unsigned /*durationInMicroseconds*/) { @@ -549,14 +568,20 @@ continuePlaying(); } -Boolean DummySink::continuePlaying() { - if (fSource == NULL) return False; // sanity check (should not happen) +Boolean DummySink::continuePlaying() +{ + if (fSource == NULL) return False; // sanity check (should not happen) - rtsp_client_continue_callback(args); - - // Request the next frame of data from our input source. "afterGettingFrame()" will get called later, when it arrives: - fSource->getNextFrame(fReceiveBuffer + 4, DUMMY_SINK_RECEIVE_BUFFER_SIZE, - afterGettingFrame, this, - onSourceClosure, this); - return True; + rtsp_client_continue_callback(rtspConfig.args); + + u_int8_t* b = fReceiveBuffer; + if (rtspConfig.aux) + b += 4; + + // Request the next frame of data from our input source. "afterGettingFrame()" will get called later, when it arrives: + fSource->getNextFrame(b, DUMMY_SINK_RECEIVE_BUFFER_SIZE, + afterGettingFrame, this, + onSourceClosure, this); + + return True; } diff --git a/RtspFace/main.cpp b/RtspFace/main.cpp index c7e3c2b..ea5505d 100644 --- a/RtspFace/main.cpp +++ b/RtspFace/main.cpp @@ -14,6 +14,7 @@ PipeLine pipeLine; pipeLine.register_elem_creator("PL_RTSPClient", create_PL_RTSPClient); + pipeLine.register_elem_creator("PL_RTSPServer", create_PL_RTSPServer); pipeLine.register_elem_creator("PL_H264Decoder", create_PL_H264Decoder); pipeLine.register_elem_creator("PL_AVFrameYUV420", create_PL_AVFrameYUV420); pipeLine.register_elem_creator("PL_H264Encoder", create_PL_H264Encoder); @@ -22,6 +23,10 @@ RTSPConfig rtspConfig; rtspConfig.progName = argv[0]; rtspConfig.rtspURL = argv[1]; + rtspConfig.aux = false; // ffmpeg need aux + rtspConfig.verbosityLevel = 1; + rtspConfig.tunnelOverHTTPPortNum = 0; + rtspConfig.args = nullptr; bool ret = rtspClient->init(&rtspConfig); if (!ret) { @@ -29,8 +34,8 @@ exit(EXIT_FAILURE); } - PL_H264Decoder* h264Decoder = (PL_H264Decoder*)pipeLine.push_elem("PL_H264Decoder"); - h264Decoder->init(nullptr); + //PL_H264Decoder* h264Decoder = (PL_H264Decoder*)pipeLine.push_elem("PL_H264Decoder"); + //h264Decoder->init(nullptr); //PL_AVFrameYUV420* avFrameYUV420 = (PL_AVFrameYUV420*)pipeLine.push_elem("PL_AVFrameYUV420"); //avFrameYUV420->init(nullptr); diff --git a/RtspFace/make.sh b/RtspFace/make.sh index bd54b93..6a122db 100644 --- a/RtspFace/make.sh +++ b/RtspFace/make.sh @@ -18,6 +18,9 @@ LIBYUV_INC="-I$LIBYUV_BASE/include" LIBYUV_LIB="-L$LIBYUV_BASE -lyuv" +FFMPEGRTSPSERVER_BASE=./FFmpegRTSPServer +FFMPEGRTSPSERVER_OBJ="FFmpegH264Source.o LiveRTSPServer.o LiveServerMediaSubsession.o" + CPPFLAGS+="-pthread $LIVEMEDIA_INC $FFMPEG_INC $LIBBASE64_INC $LIBYUV_INC" LDFLAGS+="-pthread $LIVEMEDIA_LIB $FFMPEG_LIB $LIBBASE64_LIB $LIBYUV_LIB $LIBX264_LIB" @@ -34,9 +37,16 @@ g++ -g -c -std=c++11 PL_AVFrameYUV420.cpp $CFLAGS $CPPFLAGS g++ -g -c -std=c++11 PL_AVFrameBGRA.cpp $CFLAGS $CPPFLAGS g++ -g -c -std=c++11 PipeLine.cpp $CFLAGS $CPPFLAGS + +g++ -g -c -std=c++11 $FFMPEGRTSPSERVER_BASE/LiveRTSPServer.cpp $CFLAGS $CPPFLAGS +g++ -g -c -std=c++11 $FFMPEGRTSPSERVER_BASE/FFmpegH264Source.cpp $CFLAGS $CPPFLAGS +g++ -g -c -std=c++11 $FFMPEGRTSPSERVER_BASE/LiveServerMediaSubsession.cpp $CFLAGS $CPPFLAGS + g++ -g -std=c++11 \ - main.o PL_RTSPClient.o PL_RTSPServer.o PL_H264Decoder.o PL_H264Encoder.o PL_AVFrameYUV420.o PL_AVFrameBGRA.o PipeLine.o \ + main.o PipeLine.o \ + PL_RTSPClient.o PL_H264Decoder.o PL_H264Encoder.o PL_AVFrameYUV420.o PL_AVFrameBGRA.o \ + $FFMPEGRTSPSERVER_OBJ PL_RTSPServer.o \ $LDFLAGS -o rtsp_face #export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$LIBX264_BASE/lib:$FFMPEG_BASE/lib -#./rtsp_face rtsp://admin:admin12345@192.168.1.63:554/h264/ch1/main/av_stream +#./rtsp_face rtsp://admin:admin12345@192.168.1.64:554/h264/ch1/main/av_stream -- Gitblit v1.8.0