├── FFmpegDecoder.cpp ├── FFmpegDecoder.h ├── FFmpegH264Encoder.cpp ├── FFmpegH264Encoder.h ├── FFmpegH264Source.cpp ├── FFmpegH264Source.h ├── LiveRTSPServer.cpp ├── LiveRTSPServer.h ├── LiveServerMediaSubsession.cpp ├── LiveServerMediaSubsession.h └── main.cpp /FFmpegDecoder.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // FFmpegDecoder.cpp 3 | // FFmpegRTSPServer 4 | // 5 | // Created by Mina Saad on 9/22/15. 6 | // Copyright (c) 2015 Mina Saad. All rights reserved. 7 | // 8 | 9 | #include "FFmpegDecoder.h" 10 | 11 | namespace MESAI 12 | { 13 | FFmpegDecoder::FFmpegDecoder(std::string path) 14 | { 15 | this->path = path; 16 | } 17 | 18 | 19 | void FFmpegDecoder::intialize() 20 | { 21 | 22 | // Intialize FFmpeg enviroment 23 | av_register_all(); 24 | avdevice_register_all(); 25 | avcodec_register_all(); 26 | avformat_network_init(); 27 | 28 | const char *filenameSrc = path.c_str(); 29 | 30 | pFormatCtx = avformat_alloc_context(); 31 | 32 | AVCodec * pCodec; 33 | 34 | if(avformat_open_input(&pFormatCtx,filenameSrc,NULL,NULL) != 0) 35 | { 36 | //exception 37 | return; 38 | } 39 | 40 | if(avformat_find_stream_info(pFormatCtx, NULL) < 0) 41 | { 42 | //exception 43 | return; 44 | } 45 | 46 | av_dump_format(pFormatCtx, 0, filenameSrc, 0); 47 | 48 | videoStream = -1; 49 | 50 | for(int i=0; i < pFormatCtx->nb_streams; i++) 51 | { 52 | AVStream *st = pFormatCtx->streams[i]; 53 | enum AVMediaType type = st->codec->codec_type; 54 | if (videoStream == -1) 55 | if (avformat_match_stream_specifier(pFormatCtx, st, "vst") > 0) 56 | videoStream = i; 57 | } 58 | 59 | videoStream = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_VIDEO,videoStream, -1, NULL, 0); 60 | 61 | if(videoStream == -1) 62 | { 63 | //exception 64 | return; 65 | } 66 | 67 | pCodecCtx = pFormatCtx->streams[videoStream]->codec; 68 | 69 | pCodec =avcodec_find_decoder(pCodecCtx->codec_id); 70 | if(pCodec==NULL) 71 | { 72 | //exception 73 | return; 74 | } 75 | 76 | pCodecCtx->codec_id = pCodec->id; 77 | pCodecCtx->workaround_bugs = 1; 78 | 79 | if(avcodec_open2(pCodecCtx,pCodec,NULL) < 0) 80 | { 81 | //exception 82 | return; 83 | } 84 | 85 | pFrameRGB = av_frame_alloc(); 86 | AVPixelFormat pFormat = AV_PIX_FMT_BGR24; 87 | uint8_t *fbuffer; 88 | int numBytes; 89 | numBytes = avpicture_get_size(pFormat,pCodecCtx->width,pCodecCtx->height) ; //AV_PIX_FMT_RGB24 90 | fbuffer = (uint8_t *) av_malloc(numBytes*sizeof(uint8_t)); 91 | avpicture_fill((AVPicture *) pFrameRGB,fbuffer,pFormat,pCodecCtx->width,pCodecCtx->height); 92 | 93 | 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); 94 | 95 | height = pCodecCtx->height; 96 | width = pCodecCtx->width; 97 | bitrate =pCodecCtx->bit_rate; 98 | GOP = pCodecCtx->gop_size; 99 | frameRate = (int )pFormatCtx->streams[videoStream]->avg_frame_rate.num/pFormatCtx->streams[videoStream]->avg_frame_rate.den; 100 | 101 | 102 | } 103 | 104 | void FFmpegDecoder::setOnframeCallbackFunction(std::function func) 105 | { 106 | onFrame = func; 107 | } 108 | 109 | 110 | void FFmpegDecoder::playMedia() 111 | { 112 | AVPacket packet; 113 | AVFrame * pFrame; 114 | while((av_read_frame(pFormatCtx,&packet)>=0)) 115 | { 116 | if(packet.buf != NULL & packet.stream_index == videoStream) 117 | { 118 | pFrame = av_frame_alloc(); 119 | int frameFinished; 120 | int decode_ret = avcodec_decode_video2(pCodecCtx,pFrame,&frameFinished,&packet); 121 | av_free_packet(&packet); 122 | if(frameFinished) 123 | { 124 | sws_scale(img_convert_ctx, ((AVPicture*)pFrame)->data, ((AVPicture*)pFrame)->linesize, 0, pCodecCtx->height, ((AVPicture *)pFrameRGB)->data, ((AVPicture *)pFrameRGB)->linesize); 125 | onFrame(((AVPicture *)pFrameRGB)->data[0]); 126 | } 127 | av_frame_unref(pFrame); 128 | av_free(pFrame); 129 | } 130 | usleep(((double)(1.0/frameRate))*1000000); 131 | 132 | } 133 | av_free_packet(&packet); 134 | 135 | 136 | } 137 | 138 | void FFmpegDecoder::finalize() 139 | { 140 | sws_freeContext(img_convert_ctx); 141 | av_freep(&(pFrameRGB->data[0])); 142 | av_frame_unref(pFrameRGB); 143 | av_free(pFrameRGB); 144 | avcodec_close(pCodecCtx); 145 | avformat_close_input(&pFormatCtx); 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /FFmpegDecoder.h: -------------------------------------------------------------------------------- 1 | // 2 | // FFmpegDecoder.h 3 | // FFmpegRTSPServer 4 | // 5 | // Created by Mina Saad on 9/22/15. 6 | // Copyright (c) 2015 Mina Saad. All rights reserved. 7 | // 8 | 9 | #ifndef MESAI_FFmpegDecoder_H 10 | #define MESAI_FFmpegDecoder_H 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | extern "C" { 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | } 25 | 26 | 27 | 28 | namespace MESAI 29 | { 30 | class FFmpegDecoder 31 | { 32 | public: 33 | FFmpegDecoder(std::string); 34 | 35 | ~FFmpegDecoder(); 36 | 37 | void intialize(); 38 | 39 | void playMedia(); 40 | 41 | void finalize(); 42 | 43 | void setOnframeCallbackFunction(std::function func); 44 | 45 | int width; 46 | 47 | int height; 48 | 49 | int GOP; 50 | 51 | int frameRate; 52 | 53 | int bitrate; 54 | 55 | std::function onFrame; 56 | 57 | private: 58 | 59 | std::string path; 60 | 61 | AVCodecContext *pCodecCtx; 62 | 63 | AVFormatContext *pFormatCtx; 64 | 65 | AVFrame *pFrameRGB; 66 | 67 | struct SwsContext * img_convert_ctx; 68 | 69 | int videoStream; 70 | 71 | 72 | }; 73 | 74 | 75 | } 76 | 77 | #endif -------------------------------------------------------------------------------- /FFmpegH264Encoder.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // FFmpegH264Encoder.cpp 3 | // FFmpegRTSPServer 4 | // 5 | // Created by Mina Saad on 9/22/15. 6 | // Copyright (c) 2015 Mina Saad. All rights reserved. 7 | // 8 | 9 | #include "FFmpegH264Encoder.h" 10 | 11 | namespace MESAI 12 | { 13 | FFmpegH264Encoder::FFmpegH264Encoder() 14 | { 15 | pthread_mutex_init(&inqueue_mutex,NULL); 16 | pthread_mutex_init(&outqueue_mutex,NULL); 17 | 18 | } 19 | 20 | void FFmpegH264Encoder::setCallbackFunctionFrameIsReady(std::function func) 21 | { 22 | onFrame = func; 23 | } 24 | 25 | 26 | void FFmpegH264Encoder::SendNewFrame(uint8_t * RGBFrame) { 27 | pthread_mutex_lock(&inqueue_mutex); 28 | if(inqueue.size()<30) 29 | { 30 | inqueue.push(RGBFrame); 31 | } 32 | pthread_mutex_unlock(&inqueue_mutex); 33 | } 34 | 35 | void FFmpegH264Encoder::run() 36 | { 37 | while(true) 38 | { 39 | if(!inqueue.empty()) 40 | { 41 | uint8_t * frame; 42 | pthread_mutex_lock(&inqueue_mutex); 43 | frame = inqueue.front(); 44 | inqueue.pop(); 45 | pthread_mutex_unlock(&inqueue_mutex); 46 | if(frame != NULL) 47 | { 48 | WriteFrame(frame); 49 | } 50 | } 51 | } 52 | } 53 | 54 | void FFmpegH264Encoder::SetupCodec(const char *filename, int codec_id) 55 | { 56 | int ret; 57 | m_sws_flags = SWS_BICUBIC; 58 | m_frame_count=0; 59 | 60 | avcodec_register_all(); 61 | av_register_all(); 62 | 63 | avformat_alloc_output_context2(&m_oc, NULL, NULL, filename); 64 | 65 | if (!m_oc) { 66 | avformat_alloc_output_context2(&m_oc, NULL, "avi", filename); 67 | } 68 | 69 | if (!m_oc) { 70 | return; 71 | } 72 | 73 | m_fmt = m_oc->oformat; 74 | m_video_st = NULL; 75 | m_fmt->video_codec = (AVCodecID)codec_id; 76 | m_fmt->audio_codec = AV_CODEC_ID_NONE; 77 | 78 | AVStream *st; 79 | 80 | m_video_codec = avcodec_find_encoder(m_fmt->video_codec); 81 | if (!(m_video_codec)) { 82 | return; 83 | } 84 | 85 | st = avformat_new_stream(m_oc, m_video_codec); 86 | 87 | if (!st) { 88 | return; 89 | } 90 | 91 | st->id = m_oc->nb_streams-1; 92 | 93 | m_c = st->codec; 94 | 95 | m_c->codec_id = m_fmt->video_codec; 96 | m_c->bit_rate = m_AVIMOV_BPS; //Bits Per Second 97 | m_c->width = m_AVIMOV_WIDTH; //Note Resolution must be a multiple of 2!! 98 | m_c->height = m_AVIMOV_HEIGHT; //Note Resolution must be a multiple of 2!! 99 | m_c->time_base.den = m_AVIMOV_FPS; //Frames per second 100 | m_c->time_base.num = 1; 101 | m_c->gop_size = m_AVIMOV_GOB; // Intra frames per x P frames 102 | m_c->pix_fmt = AV_PIX_FMT_YUV420P;//Do not change this, H264 needs YUV format not RGB 103 | 104 | 105 | if (m_oc->oformat->flags & AVFMT_GLOBALHEADER) 106 | m_c->flags |= CODEC_FLAG_GLOBAL_HEADER; 107 | 108 | m_video_st=st; 109 | 110 | 111 | AVCodecContext *c = m_video_st->codec; 112 | 113 | ret = avcodec_open2(c, m_video_codec, NULL); 114 | if (ret < 0) { 115 | return; 116 | } 117 | 118 | //ret = avpicture_alloc(&m_dst_picture, c->pix_fmt, c->width, c->height); 119 | m_dst_picture = av_frame_alloc(); 120 | m_dst_picture->format = c->pix_fmt; 121 | m_dst_picture->data[0] = NULL; 122 | m_dst_picture->linesize[0] = -1; 123 | m_dst_picture->pts = 0; 124 | m_dst_picture->width = m_c->width; 125 | m_dst_picture->height = m_c->height; 126 | 127 | ret = av_image_alloc(m_dst_picture->data, m_dst_picture->linesize, c->width, c->height, (AVPixelFormat)m_dst_picture->format, 32); 128 | if (ret < 0) { 129 | return; 130 | } 131 | 132 | //ret = avpicture_alloc(&m_src_picture, AV_PIX_FMT_BGR24, c->width, c->height); 133 | m_src_picture = av_frame_alloc(); 134 | m_src_picture->format = c->pix_fmt; 135 | ret = av_image_alloc(m_src_picture->data, m_src_picture->linesize, c->width, c->height, AV_PIX_FMT_BGR24, 24); 136 | 137 | if (ret < 0) { 138 | return; 139 | } 140 | 141 | bufferSize = ret; 142 | 143 | av_dump_format(m_oc, 0, filename, 1); 144 | 145 | if (!(m_fmt->flags & AVFMT_NOFILE)) { 146 | ret = avio_open(&m_oc->pb, filename, AVIO_FLAG_WRITE); 147 | if (ret < 0) { 148 | return; 149 | } 150 | } 151 | 152 | ret = avformat_write_header(m_oc, NULL); 153 | 154 | if (ret < 0) { 155 | return; 156 | } 157 | 158 | sws_ctx = sws_getContext(c->width, c->height, AV_PIX_FMT_BGR24, 159 | c->width, c->height, AV_PIX_FMT_YUV420P, 160 | SWS_BICUBIC, NULL, NULL, NULL); 161 | if (!sws_ctx) { 162 | return; 163 | } 164 | } 165 | 166 | void FFmpegH264Encoder::WriteFrame(uint8_t * RGBFrame ) 167 | { 168 | 169 | memcpy(m_src_picture->data[0], RGBFrame, bufferSize); 170 | 171 | sws_scale(sws_ctx, 172 | m_src_picture->data, m_src_picture->linesize, 173 | 0, m_c->height, m_dst_picture->data, m_dst_picture->linesize); 174 | 175 | 176 | AVPacket pkt = { 0 }; 177 | int got_packet; 178 | av_init_packet(&pkt); 179 | 180 | int ret = 0; 181 | 182 | ret = avcodec_encode_video2(m_c, &pkt, m_dst_picture, &got_packet); 183 | 184 | if (ret < 0) { 185 | return; 186 | } 187 | 188 | if (!ret && got_packet && pkt.size) 189 | { 190 | pkt.stream_index = m_video_st->index; 191 | FrameStructure * frame = new FrameStructure(); 192 | frame->dataPointer = new uint8_t[pkt.size]; 193 | frame->dataSize = pkt.size-4; 194 | frame->frameID = m_frame_count; 195 | 196 | memcpy(frame->dataPointer,pkt.data+4,pkt.size-4); 197 | 198 | pthread_mutex_lock(&outqueue_mutex); 199 | 200 | if(outqueue.size()<30) 201 | { 202 | outqueue.push(frame); 203 | } 204 | else 205 | { 206 | delete frame; 207 | } 208 | 209 | pthread_mutex_unlock(&outqueue_mutex); 210 | 211 | } 212 | 213 | av_free_packet(&pkt); 214 | 215 | m_frame_count++; 216 | m_dst_picture->pts += av_rescale_q(1, m_video_st->codec->time_base, m_video_st->time_base); 217 | 218 | onFrame(); 219 | } 220 | 221 | void FFmpegH264Encoder::SetupVideo(std::string filename, int Width, int Height, int FPS, int GOB, int BitPerSecond) 222 | { 223 | m_filename = filename; 224 | m_AVIMOV_WIDTH=Width; //Movie width 225 | m_AVIMOV_HEIGHT=Height; //Movie height 226 | m_AVIMOV_FPS=FPS; //Movie frames per second 227 | m_AVIMOV_GOB=GOB; //I frames per no of P frames, see note below! 228 | m_AVIMOV_BPS=BitPerSecond; //Bits per second, if this is too low then movie will become garbled 229 | 230 | SetupCodec(m_filename.c_str(),AV_CODEC_ID_H264); 231 | } 232 | 233 | void FFmpegH264Encoder::CloseCodec() 234 | { 235 | 236 | av_write_trailer(m_oc); 237 | avcodec_close(m_video_st->codec); 238 | 239 | av_freep(&(m_dst_picture->data[0])); 240 | av_frame_unref(m_dst_picture); 241 | av_free(m_dst_picture); 242 | av_freep(&(m_src_picture->data[0])); 243 | av_frame_unref(m_src_picture); 244 | av_free(m_src_picture); 245 | 246 | if (!(m_fmt->flags & AVFMT_NOFILE)) 247 | avio_close(m_oc->pb); 248 | 249 | m_oc->pb = NULL; 250 | 251 | avformat_free_context(m_oc); 252 | sws_freeContext(sws_ctx); 253 | 254 | } 255 | 256 | void FFmpegH264Encoder::CloseVideo() 257 | { 258 | CloseCodec(); 259 | } 260 | 261 | char FFmpegH264Encoder::GetFrame(u_int8_t** FrameBuffer, unsigned int *FrameSize) 262 | { 263 | if(!outqueue.empty()) 264 | { 265 | FrameStructure * frame; 266 | frame = outqueue.front(); 267 | *FrameBuffer = (uint8_t*)frame->dataPointer; 268 | *FrameSize = frame->dataSize; 269 | return 1; 270 | } 271 | else 272 | { 273 | *FrameBuffer = 0; 274 | *FrameSize = 0; 275 | return 0; 276 | } 277 | } 278 | 279 | char FFmpegH264Encoder::ReleaseFrame() 280 | { 281 | pthread_mutex_lock(&outqueue_mutex); 282 | if(!outqueue.empty()) 283 | { 284 | FrameStructure * frame = outqueue.front(); 285 | outqueue.pop(); 286 | delete frame; 287 | } 288 | pthread_mutex_unlock(&outqueue_mutex); 289 | return 1; 290 | } 291 | } -------------------------------------------------------------------------------- /FFmpegH264Encoder.h: -------------------------------------------------------------------------------- 1 | // 2 | // FFmpegH264Encoder.h 3 | // FFmpegRTSPServer 4 | // 5 | // Created by Mina Saad on 9/22/15. 6 | // Copyright (c) 2015 Mina Saad. All rights reserved. 7 | // 8 | 9 | #ifndef MESAI_FFMPEGH264_ENCODER_H 10 | #define MESAI_FFMPEGH264_ENCODER_H 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | extern "C" { 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | } 32 | 33 | namespace MESAI 34 | { 35 | class FrameStructure { 36 | public: 37 | uint8_t * dataPointer; 38 | int dataSize; 39 | int frameID; 40 | ~FrameStructure() 41 | { 42 | delete dataPointer; 43 | } 44 | }; 45 | 46 | class FFmpegH264Encoder 47 | { 48 | public: 49 | FFmpegH264Encoder(); 50 | ~FFmpegH264Encoder(); 51 | 52 | void setCallbackFunctionFrameIsReady(std::function func); 53 | 54 | void SetupVideo(std::string filename, int Width, int Height, int FPS, int GOB, int BitPerSecond); 55 | void CloseVideo(); 56 | void SetupCodec(const char *filename, int codec_id); 57 | void CloseCodec(); 58 | 59 | 60 | void SendNewFrame(uint8_t * RGBFrame); 61 | void WriteFrame(uint8_t * RGBFrame); 62 | char ReleaseFrame(); 63 | 64 | void run(); 65 | char GetFrame(u_int8_t** FrameBuffer, unsigned int *FrameSize); 66 | 67 | private: 68 | 69 | 70 | std::queue inqueue; 71 | pthread_mutex_t inqueue_mutex; 72 | std::queue outqueue; 73 | pthread_mutex_t outqueue_mutex; 74 | 75 | 76 | int m_sws_flags; 77 | int m_AVIMOV_FPS; 78 | int m_AVIMOV_GOB; 79 | int m_AVIMOV_BPS; 80 | int m_frame_count; 81 | int m_AVIMOV_WIDTH; 82 | int m_AVIMOV_HEIGHT; 83 | std::string m_filename; 84 | 85 | double m_video_time; 86 | 87 | AVCodecContext *m_c; 88 | AVStream *m_video_st; 89 | AVOutputFormat *m_fmt; 90 | AVFormatContext *m_oc; 91 | AVCodec *m_video_codec; 92 | AVFrame * m_src_picture, * m_dst_picture; 93 | SwsContext *sws_ctx; 94 | int bufferSize; 95 | 96 | std::function onFrame; 97 | 98 | }; 99 | } 100 | #endif -------------------------------------------------------------------------------- /FFmpegH264Source.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // FFmpegH264Source.cpp 3 | // FFmpegRTSPServer 4 | // 5 | // Created by Mina Saad on 9/22/15. 6 | // Copyright (c) 2015 Mina Saad. All rights reserved. 7 | // 8 | 9 | #include "FFmpegH264Source.h" 10 | 11 | namespace MESAI 12 | { 13 | FFmpegH264Source * FFmpegH264Source::createNew(UsageEnvironment& env, FFmpegH264Encoder * E_Source) { 14 | return new FFmpegH264Source(env, E_Source); 15 | } 16 | 17 | FFmpegH264Source::FFmpegH264Source(UsageEnvironment& env, FFmpegH264Encoder * E_Source) : FramedSource(env), Encoding_Source(E_Source) 18 | { 19 | m_eventTriggerId = envir().taskScheduler().createEventTrigger(FFmpegH264Source::deliverFrameStub); 20 | std::function callback1 = std::bind(&FFmpegH264Source::onFrame,this); 21 | Encoding_Source -> setCallbackFunctionFrameIsReady(callback1); 22 | } 23 | 24 | FFmpegH264Source::~FFmpegH264Source() 25 | { 26 | 27 | } 28 | 29 | void FFmpegH264Source::doStopGettingFrames() 30 | { 31 | FramedSource::doStopGettingFrames(); 32 | } 33 | 34 | void FFmpegH264Source::onFrame() 35 | { 36 | envir().taskScheduler().triggerEvent(m_eventTriggerId, this); 37 | } 38 | 39 | void FFmpegH264Source::doGetNextFrame() 40 | { 41 | deliverFrame(); 42 | } 43 | 44 | void FFmpegH264Source::deliverFrame() 45 | { 46 | if (!isCurrentlyAwaitingData()) return; // we're not ready for the data yet 47 | 48 | static uint8_t* newFrameDataStart; 49 | static unsigned newFrameSize = 0; 50 | 51 | /* get the data frame from the Encoding thread.. */ 52 | if (Encoding_Source->GetFrame(&newFrameDataStart, &newFrameSize)){ 53 | if (newFrameDataStart!=NULL) { 54 | /* This should never happen, but check anyway.. */ 55 | if (newFrameSize > fMaxSize) { 56 | fFrameSize = fMaxSize; 57 | fNumTruncatedBytes = newFrameSize - fMaxSize; 58 | } else { 59 | fFrameSize = newFrameSize; 60 | } 61 | 62 | gettimeofday(&fPresentationTime, NULL); 63 | memcpy(fTo, newFrameDataStart, fFrameSize); 64 | 65 | //delete newFrameDataStart; 66 | //newFrameSize = 0; 67 | 68 | Encoding_Source->ReleaseFrame(); 69 | } 70 | else { 71 | fFrameSize=0; 72 | fTo=NULL; 73 | handleClosure(this); 74 | } 75 | }else 76 | { 77 | fFrameSize = 0; 78 | } 79 | 80 | if(fFrameSize>0) 81 | FramedSource::afterGetting(this); 82 | 83 | } 84 | } -------------------------------------------------------------------------------- /FFmpegH264Source.h: -------------------------------------------------------------------------------- 1 | // 2 | // FFmpegH264Source.h 3 | // FFmpegRTSPServer 4 | // 5 | // Created by Mina Saad on 9/22/15. 6 | // Copyright (c) 2015 Mina Saad. All rights reserved. 7 | // 8 | 9 | #ifndef MESAI_FFMPEGH264_SOURCE_HH 10 | #define MESAI_FFMPEGH264_SOURCE_HH 11 | 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "FFmpegH264Encoder.h" 18 | 19 | namespace MESAI 20 | { 21 | 22 | class FFmpegH264Source : public FramedSource { 23 | public: 24 | static FFmpegH264Source* createNew(UsageEnvironment& env, FFmpegH264Encoder * E_Source); 25 | FFmpegH264Source(UsageEnvironment& env, FFmpegH264Encoder * E_Source); 26 | ~FFmpegH264Source(); 27 | 28 | private: 29 | static void deliverFrameStub(void* clientData) {((FFmpegH264Source*) clientData)->deliverFrame();}; 30 | virtual void doGetNextFrame(); 31 | void deliverFrame(); 32 | virtual void doStopGettingFrames(); 33 | void onFrame(); 34 | 35 | 36 | private: 37 | FFmpegH264Encoder * Encoding_Source; 38 | EventTriggerId m_eventTriggerId; 39 | 40 | }; 41 | 42 | } 43 | #endif 44 | -------------------------------------------------------------------------------- /LiveRTSPServer.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // LiveRTSPServer.cpp 3 | // FFmpegRTSPServer 4 | // 5 | // Created by Mina Saad on 9/22/15. 6 | // Copyright (c) 2015 Mina Saad. All rights reserved. 7 | // 8 | 9 | #include "LiveRTSPServer.h" 10 | 11 | namespace MESAI 12 | { 13 | LiveRTSPServer::LiveRTSPServer( FFmpegH264Encoder * a_Encoder, int port, int httpPort ) 14 | : m_Encoder (a_Encoder), portNumber(port), httpTunnelingPort(httpPort) 15 | { 16 | quit = 0; 17 | } 18 | 19 | LiveRTSPServer::~LiveRTSPServer() 20 | { 21 | 22 | } 23 | 24 | void LiveRTSPServer::run() 25 | { 26 | TaskScheduler *scheduler; 27 | UsageEnvironment *env ; 28 | char RTSP_Address[1024]; 29 | RTSP_Address[0]=0x00; 30 | 31 | scheduler = BasicTaskScheduler::createNew(); 32 | env = BasicUsageEnvironment::createNew(*scheduler); 33 | 34 | UserAuthenticationDatabase* authDB = NULL; 35 | 36 | // if (m_Enable_Pass){ 37 | // authDB = new UserAuthenticationDatabase; 38 | // authDB->addUserRecord(UserN, PassW); 39 | // } 40 | 41 | OutPacketBuffer::maxSize = 2000000; 42 | RTSPServer* rtspServer = RTSPServer::createNew(*env, portNumber, authDB); 43 | 44 | if (rtspServer == NULL) 45 | { 46 | *env <<"LIVE555: Failed to create RTSP server: %s\n", env->getResultMsg(); 47 | } 48 | else { 49 | 50 | 51 | if(httpTunnelingPort) 52 | { 53 | rtspServer->setUpTunnelingOverHTTP(httpTunnelingPort); 54 | } 55 | 56 | char const* descriptionString = "MESAI Streaming Session"; 57 | 58 | FFmpegH264Source * source = FFmpegH264Source::createNew(*env,m_Encoder); 59 | StreamReplicator * inputDevice = StreamReplicator::createNew(*env, source, false); 60 | 61 | ServerMediaSession* sms = ServerMediaSession::createNew(*env, RTSP_Address, RTSP_Address, descriptionString); 62 | sms->addSubsession(MESAI::LiveServerMediaSubsession::createNew(*env, inputDevice)); 63 | rtspServer->addServerMediaSession(sms); 64 | 65 | char* url = rtspServer->rtspURL(sms); 66 | *env << "Play this stream using the URL \"" << url << "\"\n"; 67 | delete [] url; 68 | 69 | //signal(SIGNIT,sighandler); 70 | env->taskScheduler().doEventLoop(&quit); // does not return 71 | 72 | Medium::close(rtspServer); 73 | Medium::close(inputDevice); 74 | } 75 | 76 | env->reclaim(); 77 | delete scheduler; 78 | } 79 | } -------------------------------------------------------------------------------- /LiveRTSPServer.h: -------------------------------------------------------------------------------- 1 | // 2 | // LiveRTSPServer.h 3 | // FFmpegRTSPServer 4 | // 5 | // Created by Mina Saad on 9/22/15. 6 | // Copyright (c) 2015 Mina Saad. All rights reserved. 7 | // 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "LiveServerMediaSubsession.h" 14 | #include "FFmpegH264Source.h" 15 | #include "FFmpegH264Encoder.h" 16 | 17 | namespace MESAI { 18 | 19 | class LiveRTSPServer 20 | { 21 | public: 22 | 23 | LiveRTSPServer(FFmpegH264Encoder * a_Encoder, int port, int httpPort ); 24 | ~LiveRTSPServer(); 25 | void run(); 26 | 27 | private: 28 | int portNumber; 29 | int httpTunnelingPort; 30 | FFmpegH264Encoder * m_Encoder; 31 | char quit; 32 | 33 | }; 34 | } -------------------------------------------------------------------------------- /LiveServerMediaSubsession.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // LiveServerMediaSubsession.cpp 3 | // FFmpegRTSPServer 4 | // 5 | // Created by Mina Saad on 9/22/15. 6 | // Copyright (c) 2015 Mina Saad. All rights reserved. 7 | // 8 | 9 | #include "LiveServerMediaSubsession.h" 10 | 11 | namespace MESAI 12 | { 13 | LiveServerMediaSubsession * LiveServerMediaSubsession::createNew(UsageEnvironment& env, StreamReplicator* replicator) 14 | { 15 | return new LiveServerMediaSubsession(env,replicator); 16 | } 17 | 18 | FramedSource* LiveServerMediaSubsession::createNewStreamSource(unsigned clientSessionId, unsigned& estBitrate) 19 | { 20 | FramedSource* source = m_replicator->createStreamReplica(); 21 | return H264VideoStreamDiscreteFramer::createNew(envir(), source); 22 | } 23 | 24 | RTPSink* LiveServerMediaSubsession::createNewRTPSink(Groupsock* rtpGroupsock, unsigned char rtpPayloadTypeIfDynamic, FramedSource* inputSource) 25 | { 26 | return H264VideoRTPSink::createNew(envir(), rtpGroupsock,rtpPayloadTypeIfDynamic); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /LiveServerMediaSubsession.h: -------------------------------------------------------------------------------- 1 | // 2 | // LiveServerMediaSubsession.h 3 | // FFmpegRTSPServer 4 | // 5 | // Created by Mina Saad on 9/22/15. 6 | // Copyright (c) 2015 Mina Saad. All rights reserved. 7 | // 8 | 9 | #ifndef MESAI_Live_SERVER_MEDIA_SUBSESSION_HH 10 | #define MESAI_Live_SERVER_MEDIA_SUBSESSION_HH 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | namespace MESAI 21 | { 22 | 23 | class LiveServerMediaSubsession: public OnDemandServerMediaSubsession 24 | { 25 | public: 26 | static LiveServerMediaSubsession* createNew(UsageEnvironment& env, StreamReplicator* replicator); 27 | 28 | protected: 29 | LiveServerMediaSubsession(UsageEnvironment& env, StreamReplicator* replicator) 30 | : OnDemandServerMediaSubsession(env, False), m_replicator(replicator) {}; 31 | 32 | virtual FramedSource* createNewStreamSource(unsigned clientSessionId, unsigned& estBitrate); 33 | virtual RTPSink* createNewRTPSink(Groupsock* rtpGroupsock, unsigned char rtpPayloadTypeIfDynamic, FramedSource* inputSource); 34 | 35 | StreamReplicator * m_replicator; 36 | }; 37 | 38 | } 39 | #endif -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // main.cpp 3 | // FFmpegRTSPServer 4 | // 5 | // Created by Mina Saad on 9/22/15. 6 | // Copyright (c) 2015 Mina Saad. All rights reserved. 7 | // 8 | 9 | #include "LiveRTSPServer.h" 10 | #include "FFmpegH264Encoder.h" 11 | #include "FFmpegDecoder.h" 12 | 13 | MESAI::FFmpegH264Encoder * encoder; 14 | MESAI::LiveRTSPServer * server; 15 | MESAI::FFmpegDecoder * decoder; 16 | 17 | int UDPPort; 18 | int HTTPTunnelPort; 19 | pthread_t thread1; 20 | pthread_t thread2; 21 | 22 | 23 | void * runServer(void * server) 24 | { 25 | ((MESAI::LiveRTSPServer * ) server)->run(); 26 | pthread_exit(NULL); 27 | } 28 | 29 | void * runEncoder(void * encoder) 30 | { 31 | ((MESAI::FFmpegH264Encoder * ) encoder)->run(); 32 | pthread_exit(NULL); 33 | } 34 | 35 | void onFrame(uint8_t * data) 36 | { 37 | encoder->SendNewFrame(data); 38 | } 39 | 40 | int main(int argc, const char * argv[]) 41 | { 42 | if(argc==2) 43 | decoder = new MESAI::FFmpegDecoder(argv[1]); 44 | if(argc==3) 45 | UDPPort = atoi(argv[2]); 46 | if(argc==4) 47 | HTTPTunnelPort = atoi(argv[3]); 48 | decoder->intialize(); 49 | decoder->setOnframeCallbackFunction(onFrame); 50 | encoder = new MESAI::FFmpegH264Encoder(); 51 | encoder->SetupVideo("dummy.avi",decoder->width,decoder->height,decoder->frameRate,decoder->GOP,decoder->bitrate); 52 | server = new MESAI::LiveRTSPServer(encoder, UDPPort, HTTPTunnelPort); 53 | 54 | pthread_attr_t attr1; 55 | pthread_attr_init(&attr1); 56 | pthread_attr_setdetachstate(&attr1, PTHREAD_CREATE_DETACHED); 57 | int rc1 = pthread_create(&thread1, &attr1, runServer, server); 58 | 59 | if (rc1){ 60 | //exception 61 | return -1; 62 | } 63 | 64 | pthread_attr_t attr2; 65 | pthread_attr_init(&attr2); 66 | pthread_attr_setdetachstate(&attr2, PTHREAD_CREATE_DETACHED); 67 | int rc2 = pthread_create(&thread2, &attr2, runEncoder, encoder); 68 | 69 | if (rc2){ 70 | //exception 71 | return -1; 72 | } 73 | 74 | // Play Media Here 75 | decoder->playMedia(); 76 | decoder->finalize(); 77 | 78 | } 79 | --------------------------------------------------------------------------------