houxiao
2016-12-26 993a7850d6cffb341fefabb68fbb97168c4a461c
rtsp server ok

git-svn-id: http://192.168.1.226/svn/proxy@34 454eff88-639b-444f-9e54-f578c98de674
12个文件已添加
6个文件已修改
1522 ■■■■ 已修改文件
RtspFace/FFmpegRTSPServer/FFmpegDecoder.cpp 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RtspFace/FFmpegRTSPServer/FFmpegDecoder.h 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RtspFace/FFmpegRTSPServer/FFmpegH264Encoder.cpp 291 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RtspFace/FFmpegRTSPServer/FFmpegH264Encoder.h 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RtspFace/FFmpegRTSPServer/FFmpegH264Source.cpp 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RtspFace/FFmpegRTSPServer/FFmpegH264Source.h 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RtspFace/FFmpegRTSPServer/IEncoder.h 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RtspFace/FFmpegRTSPServer/LiveRTSPServer.cpp 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RtspFace/FFmpegRTSPServer/LiveRTSPServer.h 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RtspFace/FFmpegRTSPServer/LiveServerMediaSubsession.cpp 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RtspFace/FFmpegRTSPServer/LiveServerMediaSubsession.h 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RtspFace/FFmpegRTSPServer/main.cpp 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RtspFace/PL_RTSPClient.cpp 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RtspFace/PL_RTSPClient.h 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RtspFace/PL_RTSPServer.cpp 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RtspFace/live555/testProgs/testRTSPClient.hpp 229 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RtspFace/main.cpp 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RtspFace/make.sh 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
RtspFace/FFmpegRTSPServer/FFmpegDecoder.cpp
New file
@@ -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);
    }
}
RtspFace/FFmpegRTSPServer/FFmpegDecoder.h
New file
@@ -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
RtspFace/FFmpegRTSPServer/FFmpegH264Encoder.cpp
New file
@@ -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;
    }
}
RtspFace/FFmpegRTSPServer/FFmpegH264Encoder.h
New file
@@ -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
RtspFace/FFmpegRTSPServer/FFmpegH264Source.cpp
New file
@@ -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);
    }
}
RtspFace/FFmpegRTSPServer/FFmpegH264Source.h
New file
@@ -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
RtspFace/FFmpegRTSPServer/IEncoder.h
New file
@@ -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
RtspFace/FFmpegRTSPServer/LiveRTSPServer.cpp
New file
@@ -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;
    }
}
RtspFace/FFmpegRTSPServer/LiveRTSPServer.h
New file
@@ -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
RtspFace/FFmpegRTSPServer/LiveServerMediaSubsession.cpp
New file
@@ -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);
    }
}
RtspFace/FFmpegRTSPServer/LiveServerMediaSubsession.h
New file
@@ -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
RtspFace/FFmpegRTSPServer/main.cpp
New file
@@ -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();
}
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)
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
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)
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;
}
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);
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