diff --git a/include/stream_srt_source.h b/include/stream_srt_source.h index 2a1e35732490b9be04265f8b99aa6090414065d6..be9233bade57b4d6b3eaf1e08dbfdcc86debd2c2 100644 --- a/include/stream_srt_source.h +++ b/include/stream_srt_source.h @@ -12,6 +12,7 @@ extern "C" #include <libavutil/timestamp.h> #include <libavutil/opt.h> #include <libswresample/swresample.h> + #include <libswscale/swscale.h> } #include "portaudio.h" @@ -36,7 +37,9 @@ typedef struct{ AVStream *stream; AVCodecContext *encoder_context; AVFrame *frame; + AVFrame *tmp_frame; int64_t next_pts; + struct SwsContext *resampler; // device data int fd; TVideoBuffer *buffers; @@ -49,6 +52,7 @@ typedef struct{ unsigned int height; float framerate; unsigned int format; + AVPixelFormat av_format; // device data unsigned char device[32]; // encoder @@ -103,7 +107,9 @@ typedef struct{ }TAudioConfig; typedef struct{ + bool video_enable; TVideoConfig video; + bool audio_enable; TAudioConfig audio; unsigned char srt_ip[15]; unsigned int srt_port; @@ -157,7 +163,7 @@ class CStreamSrtSource static int audioCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData); static void *capture_audio_thread(void *param); public: - CStreamSrtSource(); + CStreamSrtSource(const std::string &name); void load_config(const std::string &filename); void load_config(TStreamConfig &stream_config); void save_config(const std::string &filename); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5d3c64c4fdc6f4b1d7e2b84c578e8d87498ad2e7..cff9b9a0937b22cf6c73bcb7192e946ac0cfbd7e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -13,10 +13,11 @@ set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/opt/vc/lib/pkgconfig/") pkg_search_module(AVCODEC REQUIRED libavcodec) pkg_search_module(AVDEVICE REQUIRED libavdevice) pkg_search_module(AVFILTER REQUIRED libavfilter) -pkg_search_module(AVAVFORMAT REQUIRED libavformat) +pkg_search_module(AVFORMAT REQUIRED libavformat) pkg_search_module(AVUTIL REQUIRED libavutil) pkg_search_module(SWRESAMPLE REQUIRED libswresample) -pkg_search_module(MMAL REQUIRED mmal) +pkg_search_module(SWSCALE REQUIRED libswscale) +pkg_search_module(MMAL mmal) pkg_search_module(PORTAUDIO REQUIRED portaudiocpp) find_package(iriutils REQUIRED) @@ -29,16 +30,19 @@ include_directories(${AVFILTER_INCLUDE_DIRS}) include_directories(${AVFORMAT_INCLUDE_DIRS}) include_directories(${AVUTIL_INCLUDE_DIRS}) include_directories(${SWRESAMPLE_INCLUDE_DIRS}) +include_directories(${SWSCALE_INCLUDE_DIRS}) include_directories(${MMAL_INCLUDE_DIRS}) include_directories(${PORTAUDIO_INCLUDE_DIRS}) include_directories(${iriutils_INCLUDE_DIR}) +message(${AVCODEC_LIBRARY_DIRS}) link_directories(${AVCODEC_LIBRARY_DIRS}) link_directories(${AVDEVICE_LIBRARY_DIRS}) link_directories(${AVFILTER_LIBRARY_DIRS}) link_directories(${AVFORMAT_LIBRARY_DIRS}) link_directories(${AVUTIL_LIBRARY_DIRS}) link_directories(${SWRESAMPLE_LIBRARY_DIRS}) +link_directories(${SWSCALE_LIBRARY_DIRS}) link_directories(${MMAL_LIBRARY_DIRS}) link_directories(${PORTAUDIO_LIBRARY_DIRS}) @@ -57,6 +61,7 @@ target_link_libraries(${PROJECT_NAME} ${AVFILTER_LIBRARIES}) target_link_libraries(${PROJECT_NAME} ${AVFORMAT_LIBRARIES}) target_link_libraries(${PROJECT_NAME} ${AVUTIL_LIBRARIES}) target_link_libraries(${PROJECT_NAME} ${SWRESAMPLE_LIBRARIES}) +target_link_libraries(${PROJECT_NAME} ${SWSCALE_LIBRARIES}) target_link_libraries(${PROJECT_NAME} ${MMAL_LIBRARIES}) target_link_libraries(${PROJECT_NAME} ${PORTAUDIO_LIBRARIES}) @@ -66,6 +71,7 @@ target_link_libraries(${PROJECT_NAME} ${AVFILTER_LDFLAGS_OTHER}) target_link_libraries(${PROJECT_NAME} ${AVFORMAT_LDFLAGS_OTHER}) target_link_libraries(${PROJECT_NAME} ${AVUTIL_LDFLAGS_OTHER}) target_link_libraries(${PROJECT_NAME} ${SWRESAMPLE_LDFLAGS_OTHER}) +target_link_libraries(${PROJECT_NAME} ${SWSCALE_LDFLAGS_OTHER}) #ADD_DEPENDENCIES(${PROJECT_NAME} my_example_library_target) ADD_DEPENDENCIES(${PROJECT_NAME} xsd_files_gen) diff --git a/src/examples/stream_srt_source_test.cpp b/src/examples/stream_srt_source_test.cpp index dc87f211b6ec90096a1c9016472c38899e2d2ede..5da2c4cae44462abbd6a363dc593602491b7aa29 100644 --- a/src/examples/stream_srt_source_test.cpp +++ b/src/examples/stream_srt_source_test.cpp @@ -7,9 +7,15 @@ int main(int argc, char **argv) { try{ - CStreamSrtSource stream; + CStreamSrtSource stream("stream"); - stream.load_config("../src/xml/stream_config.xml"); + if(argc!=2) + { + std::cout << "Invalid numbe of arguments:" << std::endl; + std::cout << " " << argv[0] << " <XML configuration file>" << std::endl; + return 0; + } + stream.load_config(std::string(argv[1])); stream.start(); sleep(1000000); stream.stop(); diff --git a/src/stream_srt_source.cpp b/src/stream_srt_source.cpp index a010667d57f1f926acbde060bd9c50fbe40a3b22..d71311f53de9c458b7ae11205dfcfa72050e61bd 100644 --- a/src/stream_srt_source.cpp +++ b/src/stream_srt_source.cpp @@ -14,6 +14,7 @@ #include <sstream> #include <iostream> #include <fstream> +#include <chrono> #undef av_err2str #define av_err2str(errnum) av_make_error_string((char*)__builtin_alloca(AV_ERROR_MAX_STRING_SIZE), AV_ERROR_MAX_STRING_SIZE, errnum) @@ -24,13 +25,14 @@ #undef av_ts2timestr #define av_ts2timestr(ts,tb) av_ts_make_time_string((char*)__builtin_alloca(AV_TS_MAX_STRING_SIZE), ts, tb) -CStreamSrtSource::CStreamSrtSource() +CStreamSrtSource::CStreamSrtSource(const std::string &name) { this->output_context=NULL; this->video_data.codec=NULL; this->video_data.stream=NULL; this->video_data.encoder_context=NULL; this->video_data.frame=NULL; + this->video_data.resampler=NULL; this->video_data.next_pts=0; this->video_data.fd=-1; this->video_data.buffers=NULL; @@ -49,27 +51,27 @@ CStreamSrtSource::CStreamSrtSource() this->audio_data.buffer_read=0; // initialize events this->event_server=CEventServer::instance(); - this->finish_main_thread_event_id="stream_srt_source_finish_main_thread_event"; + this->finish_main_thread_event_id=name+"_finish_main_thread_event"; this->event_server->create_event(this->finish_main_thread_event_id); - this->finish_video_thread_event_id="stream_srt_source_finish_video_thread_event"; + this->finish_video_thread_event_id=name+"_finish_video_thread_event"; this->event_server->create_event(this->finish_video_thread_event_id); - this->finish_audio_thread_event_id="stream_srt_source_finish_audio_thread_event"; + this->finish_audio_thread_event_id=name+"_finish_audio_thread_event"; this->event_server->create_event(this->finish_audio_thread_event_id); - this->new_audio_buffer_event_id="stream_srt_source_new_audio_buffer_event"; + this->new_audio_buffer_event_id=name+"_new_audio_buffer_event"; this->event_server->create_event(this->new_audio_buffer_event_id); - this->audio_failure_event_id="stream_srt_source_audio_failure_event"; + this->audio_failure_event_id=name+"_audio_failure_event"; this->event_server->create_event(this->audio_failure_event_id); - this->video_failure_event_id="stream_srt_source_video_failure_event"; + this->video_failure_event_id=name+"_video_failure_event"; this->event_server->create_event(this->video_failure_event_id); // initialize threads this->thread_server=CThreadServer::instance(); - this->main_thread_id="stream_srt_source_main_thread"; + this->main_thread_id=name+"_main_thread"; this->thread_server->create_thread(this->main_thread_id); this->thread_server->attach_thread(this->main_thread_id,this->main_thread,this); - this->capture_video_thread_id="stream_srt_source_capture_video_thread"; + this->capture_video_thread_id=name+"_capture_video_thread"; this->thread_server->create_thread(this->capture_video_thread_id); this->thread_server->attach_thread(this->capture_video_thread_id,this->capture_video_thread,this); - this->capture_audio_thread_id="stream_srt_source_capture_audio_thread"; + this->capture_audio_thread_id=name+"_capture_audio_thread"; this->thread_server->create_thread(this->capture_audio_thread_id); this->thread_server->attach_thread(this->capture_audio_thread_id,this->capture_audio_thread,this); } @@ -218,88 +220,136 @@ void CStreamSrtSource::open_video(void) { int ret; - // create a new ouptut stream - this->video_data.stream=avformat_new_stream(this->output_context, NULL); - if(this->video_data.stream==NULL) - throw CException(_HERE_,"Impossible to create a new video stream"); - // configure the stream - this->video_data.stream->time_base=(AVRational){1,(int)this->config.video.framerate}; - this->video_data.stream->id=this->output_context->nb_streams-1; - // try to locate the Hardware accelerated h264 encoder - if(this->config.video.format!=V4L2_PIX_FMT_H264) + if(this->config.video_enable) { - this->video_data.codec=avcodec_find_encoder_by_name("h264_omx"); - if(this->video_data.codec==NULL) + // create a new ouptut stream + this->video_data.stream=avformat_new_stream(this->output_context, NULL); + if(this->video_data.stream==NULL) + throw CException(_HERE_,"Impossible to create a new video stream"); + // configure the stream + this->video_data.stream->time_base=(AVRational){1,(int)this->config.video.framerate}; + this->video_data.stream->id=this->output_context->nb_streams-1; + // try to locate the Hardware accelerated h264 encoder + if(this->config.video.format!=V4L2_PIX_FMT_H264) { - this->video_data.codec=avcodec_find_encoder_by_name("libx264"); + this->video_data.codec=avcodec_find_encoder_by_name("h264_omx"); if(this->video_data.codec==NULL) - throw CException(_HERE_,"Impossible to find an h264 encoder"); - } - // try to alloc a context for the encoder - this->video_data.encoder_context=avcodec_alloc_context3(this->video_data.codec); - if(this->video_data.encoder_context==NULL) - { - this->close_video(); - throw CException(_HERE_,"Impossible to allocate a context for the video encoder"); - } - // configure the codec - this->video_data.encoder_context->bit_rate=this->config.video.bitrate; - this->video_data.encoder_context->width=this->config.video.width; - this->video_data.encoder_context->height=this->config.video.height; - this->video_data.encoder_context->time_base=(AVRational){1,(int)this->config.video.framerate}; - this->video_data.encoder_context->gop_size=this->config.video.i_frame_period; - this->video_data.encoder_context->thread_count=1; - this->video_data.encoder_context->max_b_frames=0; - this->video_data.encoder_context->pix_fmt=AV_PIX_FMT_YUV420P; - this->video_data.encoder_context->level=this->config.video.av_profile; - this->video_data.encoder_context->flags|=AV_CODEC_FLAG_GLOBAL_HEADER; - this->video_data.encoder_context->flags2|=AV_CODEC_FLAG2_LOCAL_HEADER; - - // open codec - ret=avcodec_open2(this->video_data.encoder_context,this->video_data.codec,NULL); - if(ret<0) - { - this->close_video(); - throw CException(_HERE_,"Impossible to open video codec"); - } - // allocate frame - this->video_data.frame=av_frame_alloc(); - if(this->video_data.frame==NULL) - { - this->close_video(); - throw CException(_HERE_,"Impossible to allocate a video frame"); + { + this->video_data.codec=avcodec_find_encoder_by_name("libx264"); + if(this->video_data.codec==NULL) + throw CException(_HERE_,"Impossible to find an h264 encoder"); + } + // try to alloc a context for the encoder + this->video_data.encoder_context=avcodec_alloc_context3(this->video_data.codec); + if(this->video_data.encoder_context==NULL) + { + this->close_video(); + throw CException(_HERE_,"Impossible to allocate a context for the video encoder"); + } + // configure the codec + this->video_data.encoder_context->bit_rate=this->config.video.bitrate; + this->video_data.encoder_context->width=this->config.video.width; + this->video_data.encoder_context->height=this->config.video.height; + this->video_data.encoder_context->time_base=(AVRational){1,(int)this->config.video.framerate}; + this->video_data.encoder_context->gop_size=this->config.video.i_frame_period; + this->video_data.encoder_context->thread_count=1; + this->video_data.encoder_context->max_b_frames=0; + this->video_data.encoder_context->pix_fmt=AV_PIX_FMT_YUV420P; + this->video_data.encoder_context->profile=this->config.video.av_profile; + this->video_data.encoder_context->flags|=AV_CODEC_FLAG_GLOBAL_HEADER; + this->video_data.encoder_context->flags2|=AV_CODEC_FLAG2_LOCAL_HEADER; + + av_opt_set(this->video_data.encoder_context->priv_data, "preset", "ultrafast", 0); + av_opt_set(this->video_data.encoder_context->priv_data, "tune", "zerolatency", 0); + + // open codec + ret=avcodec_open2(this->video_data.encoder_context,this->video_data.codec,NULL); + if(ret<0) + { + this->close_video(); + throw CException(_HERE_,"Impossible to open video codec"); + } + // allocate frame + this->video_data.frame=av_frame_alloc(); + if(this->video_data.frame==NULL) + { + this->close_video(); + throw CException(_HERE_,"Impossible to allocate a video frame"); + } + this->video_data.frame->format=AV_PIX_FMT_YUV420P; + this->video_data.frame->width=this->config.video.width; + this->video_data.frame->height=this->config.video.height; + ret=av_frame_get_buffer(this->video_data.frame, 0); + if(ret<0) + { + this->close_video(); + throw CException(_HERE_,"Impossible to allocate a video frame data"); + } + // copy codec parameters to the stream + ret=avcodec_parameters_from_context(this->video_data.stream->codecpar,this->video_data.encoder_context); + if(ret<0) + { + this->close_video(); + throw CException(_HERE_,"Impossible to copy video codec data to the stream"); + } + if(this->config.video.av_format!=AV_PIX_FMT_YUV420P) + { + this->video_data.resampler=sws_getContext(this->config.video.width,this->config.video.height, + this->config.video.av_format, + this->config.video.width,this->config.video.height, + AV_PIX_FMT_YUV420P, + SWS_BICUBIC, NULL, NULL, NULL); + if(this->video_data.resampler==NULL) + { + this->close_video(); + throw CException(_HERE_,"Impossible to allocate resampler"); + } + + this->video_data.tmp_frame=av_frame_alloc(); + if(this->video_data.tmp_frame==NULL) + { + this->close_video(); + throw CException(_HERE_,"Impossible to allocate an video frame"); + } + this->video_data.tmp_frame->format=this->config.video.av_format; + this->video_data.tmp_frame->width=this->config.video.width; + this->video_data.tmp_frame->height=this->config.video.height; + ret=av_frame_get_buffer(this->video_data.tmp_frame, 0); + if(ret<0) + { + this->close_video(); + throw CException(_HERE_,"Impossible to allocate a video frame data"); + } + } } - this->video_data.frame->format=AV_PIX_FMT_YUV420P; - this->video_data.frame->width=this->config.video.width; - this->video_data.frame->height=this->config.video.height; - ret=av_frame_get_buffer(this->video_data.frame, 0); - if(ret<0) - { - this->close_video(); - throw CException(_HERE_,"Impossible to allocate a video frame data"); - } - // copy codec parameters to the stream - ret=avcodec_parameters_from_context(this->video_data.stream->codecpar,this->video_data.encoder_context); - if(ret<0) - { - this->close_video(); - throw CException(_HERE_,"Impossible to copy video codec data to the stream"); - } + this->video_data.next_pts=0; } - this->video_data.next_pts=0; } void CStreamSrtSource::close_video(void) { - if(this->video_data.encoder_context!=NULL) - { - avcodec_free_context(&this->video_data.encoder_context); - this->video_data.encoder_context=NULL; - } - if(this->video_data.frame!=NULL) + if(this->config.video_enable) { - av_frame_free(&this->video_data.frame); - this->video_data.frame=NULL; + if(this->video_data.encoder_context!=NULL) + { + avcodec_free_context(&this->video_data.encoder_context); + this->video_data.encoder_context=NULL; + } + if(this->video_data.frame!=NULL) + { + av_frame_free(&this->video_data.frame); + this->video_data.frame=NULL; + } + if(this->video_data.tmp_frame!=NULL) + { + av_frame_free(&this->video_data.tmp_frame); + this->video_data.tmp_frame=NULL; + } + if(this->video_data.resampler!=NULL) + { + sws_freeContext(this->video_data.resampler); + this->video_data.resampler=NULL; + } } } @@ -313,289 +363,301 @@ void CStreamSrtSource::open_video_device(void) struct v4l2_crop crop; struct v4l2_format fmt; - // check if the device exists - if(stat((char *)this->config.video.device, &st)==-1) - throw CStreamSrtSourceVideoDeviceException(_HERE_,"Video device not available",errno,strerror(errno)); - - // open the device - this->video_data.fd = ::open((char *)this->config.video.device, O_RDWR | O_NONBLOCK, 0); - if(this->video_data.fd==-1) - throw CStreamSrtSourceVideoDeviceException(_HERE_,"Impossible to open video device",errno,strerror(errno)); - - // query capabilities - if(this->xioctl(this->video_data.fd,VIDIOC_QUERYCAP,&cap)==-1) - { - if(errno==EINVAL) - throw CException(_HERE_,"Video device is not an V4L2 device"); - else - throw CStreamSrtSourceVideoDeviceException(_HERE_,"Impossible to query device capavilities",errno,strerror(errno)); - } - if(!(cap.capabilities&V4L2_CAP_VIDEO_CAPTURE)) - throw CException(_HERE_,"Video device is not a capture device"); - if(!(cap.capabilities&V4L2_CAP_STREAMING)) - throw CException(_HERE_,"Video device does not support streaming i/o"); - - /* Set default rectangle */ - memset(&cropcap,0,sizeof(cropcap)); - cropcap.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; - this->xioctl(this->video_data.fd,VIDIOC_CROPCAP,&cropcap); - crop.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; - crop.c=cropcap.defrect; /* reset to default */ - this->xioctl(this->video_data.fd,VIDIOC_S_CROP,&crop); - - // set video parameters - memset(&parm,0,sizeof(parm)); - parm.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; - parm.parm.capture.timeperframe.numerator=1; - parm.parm.capture.timeperframe.denominator=(int)this->config.video.framerate; - if(this->xioctl(this->video_data.fd,VIDIOC_S_PARM,&parm)==-1) + if(this->config.video_enable) { - this->close_video_device(); - throw CStreamSrtSourceVideoDeviceException(_HERE_,"Impossible to set parameters",errno,strerror(errno)); - } + // check if the device exists + if(stat((char *)this->config.video.device, &st)==-1) + throw CStreamSrtSourceVideoDeviceException(_HERE_,"Video device not available",errno,strerror(errno)); - // set format - memset(&fmt,0,sizeof(fmt)); - fmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; - fmt.fmt.pix.width=this->config.video.width; - fmt.fmt.pix.height=this->config.video.height; - fmt.fmt.pix.pixelformat=this->config.video.format; - if(this->xioctl(this->video_data.fd,VIDIOC_S_FMT,&fmt)==-1) - { - this->close_video_device(); - throw CStreamSrtSourceVideoDeviceException(_HERE_,"Impossible to set format",errno,strerror(errno)); - } - /* setup hardawre encoder */ - if(this->config.video.format==V4L2_PIX_FMT_H264) - { - struct v4l2_ext_controls ecs1; - struct v4l2_ext_control ec1; - - // set desired bitrate - memset(&ecs1, 0, sizeof(ecs1)); - memset(&ec1, 0, sizeof(ec1)); - ec1.id = V4L2_CID_MPEG_VIDEO_BITRATE; - ec1.value = this->config.video.bitrate; - ec1.size = 0; - ecs1.controls = &ec1; - ecs1.count = 1; - ecs1.ctrl_class = V4L2_CTRL_CLASS_MPEG; - if(this->xioctl(this->video_data.fd,VIDIOC_S_EXT_CTRLS,&ecs1)==-1) + // open the device + this->video_data.fd = ::open((char *)this->config.video.device, O_RDWR | O_NONBLOCK, 0); + if(this->video_data.fd==-1) + throw CStreamSrtSourceVideoDeviceException(_HERE_,"Impossible to open video device",errno,strerror(errno)); + + // query capabilities + if(this->xioctl(this->video_data.fd,VIDIOC_QUERYCAP,&cap)==-1) { - this->close_video_device(); - throw CStreamSrtSourceVideoDeviceException(_HERE_,"Impossible to set H264 bitrate",errno,strerror(errno)); + if(errno==EINVAL) + throw CException(_HERE_,"Video device is not an V4L2 device"); + else + throw CStreamSrtSourceVideoDeviceException(_HERE_,"Impossible to query device capavilities",errno,strerror(errno)); } - - // set the constant or variable bitrate - memset(&ecs1, 0, sizeof(ecs1)); - memset(&ec1, 0, sizeof(ec1)); - ec1.id = V4L2_CID_MPEG_VIDEO_BITRATE_MODE; - if(this->config.video.variable_bitrate) - ec1.value = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR; - else - ec1.value = V4L2_MPEG_VIDEO_BITRATE_MODE_CBR; - ec1.size = 0; - ecs1.controls = &ec1; - ecs1.count = 1; - ecs1.ctrl_class = V4L2_CTRL_CLASS_MPEG; - if(this->xioctl(this->video_data.fd,VIDIOC_S_EXT_CTRLS,&ecs1)==-1) - { + if(!(cap.capabilities&V4L2_CAP_VIDEO_CAPTURE)) + throw CException(_HERE_,"Video device is not a capture device"); + if(!(cap.capabilities&V4L2_CAP_STREAMING)) + throw CException(_HERE_,"Video device does not support streaming i/o"); + + /* Set default rectangle */ + memset(&cropcap,0,sizeof(cropcap)); + cropcap.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; + this->xioctl(this->video_data.fd,VIDIOC_CROPCAP,&cropcap); + crop.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; + crop.c=cropcap.defrect; /* reset to default */ + this->xioctl(this->video_data.fd,VIDIOC_S_CROP,&crop); + + // set video parameters + memset(&parm,0,sizeof(parm)); + parm.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; + parm.parm.capture.timeperframe.numerator=1; + parm.parm.capture.timeperframe.denominator=(int)this->config.video.framerate; + if(this->xioctl(this->video_data.fd,VIDIOC_S_PARM,&parm)==-1) + { this->close_video_device(); - throw CStreamSrtSourceVideoDeviceException(_HERE_,"Impossible to set H264 variable bitrate",errno,strerror(errno)); + throw CStreamSrtSourceVideoDeviceException(_HERE_,"Impossible to set parameters",errno,strerror(errno)); } - // set the H264 profile - memset(&ecs1, 0, sizeof(ecs1)); - memset(&ec1, 0, sizeof(ec1)); - ec1.id = V4L2_CID_MPEG_VIDEO_H264_PROFILE; - ec1.value = this->config.video.v4l2_profile; - ec1.size = 0; - ecs1.controls = &ec1; - ecs1.count = 1; - ecs1.ctrl_class = V4L2_CTRL_CLASS_MPEG; - if(this->xioctl(this->video_data.fd,VIDIOC_S_EXT_CTRLS,&ecs1)==-1) - { + // set format + memset(&fmt,0,sizeof(fmt)); + fmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; + fmt.fmt.pix.width=this->config.video.width; + fmt.fmt.pix.height=this->config.video.height; + fmt.fmt.pix.pixelformat=this->config.video.format; + if(this->xioctl(this->video_data.fd,VIDIOC_S_FMT,&fmt)==-1) + { this->close_video_device(); - throw CStreamSrtSourceVideoDeviceException(_HERE_,"Impossible to set H264 profile",errno,strerror(errno)); + throw CStreamSrtSourceVideoDeviceException(_HERE_,"Impossible to set format",errno,strerror(errno)); } + /* setup hardawre encoder */ + if(this->config.video.format==V4L2_PIX_FMT_H264) + { + struct v4l2_ext_controls ecs1; + struct v4l2_ext_control ec1; + + // set desired bitrate + memset(&ecs1, 0, sizeof(ecs1)); + memset(&ec1, 0, sizeof(ec1)); + ec1.id = V4L2_CID_MPEG_VIDEO_BITRATE; + ec1.value = this->config.video.bitrate; + ec1.size = 0; + ecs1.controls = &ec1; + ecs1.count = 1; + ecs1.ctrl_class = V4L2_CTRL_CLASS_MPEG; + if(this->xioctl(this->video_data.fd,VIDIOC_S_EXT_CTRLS,&ecs1)==-1) + { + this->close_video_device(); + throw CStreamSrtSourceVideoDeviceException(_HERE_,"Impossible to set H264 bitrate",errno,strerror(errno)); + } - // set the I frame period - memset(&ecs1, 0, sizeof(ecs1)); - memset(&ec1, 0, sizeof(ec1)); - ec1.id = V4L2_CID_MPEG_VIDEO_H264_I_PERIOD; - ec1.value = this->config.video.i_frame_period; - ec1.size = 0; - ecs1.controls = &ec1; - ecs1.count = 1; - ecs1.ctrl_class = V4L2_CTRL_CLASS_MPEG; - if(this->xioctl(this->video_data.fd,VIDIOC_S_EXT_CTRLS,&ecs1)==-1) - { - this->close_video_device(); - throw CStreamSrtSourceVideoDeviceException(_HERE_,"Impossible to set H264 I frame period",errno,strerror(errno)); - } + // set the constant or variable bitrate + memset(&ecs1, 0, sizeof(ecs1)); + memset(&ec1, 0, sizeof(ec1)); + ec1.id = V4L2_CID_MPEG_VIDEO_BITRATE_MODE; + if(this->config.video.variable_bitrate) + ec1.value = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR; + else + ec1.value = V4L2_MPEG_VIDEO_BITRATE_MODE_CBR; + ec1.size = 0; + ecs1.controls = &ec1; + ecs1.count = 1; + ecs1.ctrl_class = V4L2_CTRL_CLASS_MPEG; + if(this->xioctl(this->video_data.fd,VIDIOC_S_EXT_CTRLS,&ecs1)==-1) + { + this->close_video_device(); + throw CStreamSrtSourceVideoDeviceException(_HERE_,"Impossible to set H264 variable bitrate",errno,strerror(errno)); + } - // set the H264 level - memset(&ecs1, 0, sizeof(ecs1)); - memset(&ec1, 0, sizeof(ec1)); - ec1.id = V4L2_CID_MPEG_VIDEO_H264_LEVEL; - ec1.value = this->config.video.level; - ec1.size = 0; - ecs1.controls = &ec1; - ecs1.count = 1; - ecs1.ctrl_class = V4L2_CTRL_CLASS_MPEG; - if(this->xioctl(this->video_data.fd,VIDIOC_S_EXT_CTRLS,&ecs1)==-1) - { - this->close_video_device(); - throw CStreamSrtSourceVideoDeviceException(_HERE_,"Impossible to set H264 level",errno,strerror(errno)); - } + // set the H264 profile + memset(&ecs1, 0, sizeof(ecs1)); + memset(&ec1, 0, sizeof(ec1)); + ec1.id = V4L2_CID_MPEG_VIDEO_H264_PROFILE; + ec1.value = this->config.video.v4l2_profile; + ec1.size = 0; + ecs1.controls = &ec1; + ecs1.count = 1; + ecs1.ctrl_class = V4L2_CTRL_CLASS_MPEG; + if(this->xioctl(this->video_data.fd,VIDIOC_S_EXT_CTRLS,&ecs1)==-1) + { + this->close_video_device(); + throw CStreamSrtSourceVideoDeviceException(_HERE_,"Impossible to set H264 profile",errno,strerror(errno)); + } - // enable repeting the header sequence - memset(&ecs1, 0, sizeof(ecs1)); - memset(&ec1, 0, sizeof(ec1)); - ec1.id = V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER; - ec1.value = 1; - ec1.size = 0; - ecs1.controls = &ec1; - ecs1.count = 1; - ecs1.ctrl_class = V4L2_CTRL_CLASS_MPEG; - if(this->xioctl(this->video_data.fd,VIDIOC_S_EXT_CTRLS,&ecs1)==-1) - { - this->close_video_device(); - throw CStreamSrtSourceVideoDeviceException(_HERE_,"Impossible to enable repeating the header sequence",errno,strerror(errno)); + // set the I frame period + memset(&ecs1, 0, sizeof(ecs1)); + memset(&ec1, 0, sizeof(ec1)); + ec1.id = V4L2_CID_MPEG_VIDEO_H264_I_PERIOD; + ec1.value = this->config.video.i_frame_period; + ec1.size = 0; + ecs1.controls = &ec1; + ecs1.count = 1; + ecs1.ctrl_class = V4L2_CTRL_CLASS_MPEG; + if(this->xioctl(this->video_data.fd,VIDIOC_S_EXT_CTRLS,&ecs1)==-1) + { + this->close_video_device(); + throw CStreamSrtSourceVideoDeviceException(_HERE_,"Impossible to set H264 I frame period",errno,strerror(errno)); + } + + // set the H264 level + memset(&ecs1, 0, sizeof(ecs1)); + memset(&ec1, 0, sizeof(ec1)); + ec1.id = V4L2_CID_MPEG_VIDEO_H264_LEVEL; + ec1.value = this->config.video.level; + ec1.size = 0; + ecs1.controls = &ec1; + ecs1.count = 1; + ecs1.ctrl_class = V4L2_CTRL_CLASS_MPEG; + if(this->xioctl(this->video_data.fd,VIDIOC_S_EXT_CTRLS,&ecs1)==-1) + { + this->close_video_device(); + throw CStreamSrtSourceVideoDeviceException(_HERE_,"Impossible to set H264 level",errno,strerror(errno)); + } + + // enable repeting the header sequence + memset(&ecs1, 0, sizeof(ecs1)); + memset(&ec1, 0, sizeof(ec1)); + ec1.id = V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER; + ec1.value = 1; + ec1.size = 0; + ecs1.controls = &ec1; + ecs1.count = 1; + ecs1.ctrl_class = V4L2_CTRL_CLASS_MPEG; + if(this->xioctl(this->video_data.fd,VIDIOC_S_EXT_CTRLS,&ecs1)==-1) + { + this->close_video_device(); + throw CStreamSrtSourceVideoDeviceException(_HERE_,"Impossible to enable repeating the header sequence",errno,strerror(errno)); + } } - } - // request DMA buffers - memset(&req,0,sizeof(req)); - req.count=NUM_VIDEO_BUFFERS; - req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; - req.memory=V4L2_MEMORY_MMAP; - if(this->xioctl(this->video_data.fd,VIDIOC_REQBUFS,&req)==-1) - { - if(errno==EINVAL) + // request DMA buffers + memset(&req,0,sizeof(req)); + req.count=NUM_VIDEO_BUFFERS; + req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; + req.memory=V4L2_MEMORY_MMAP; + if(this->xioctl(this->video_data.fd,VIDIOC_REQBUFS,&req)==-1) { - this->close_video_device(); - throw CException(_HERE_,"Video device does not support memory mapping"); + if(errno==EINVAL) + { + this->close_video_device(); + throw CException(_HERE_,"Video device does not support memory mapping"); + } + else + { + this->close_video_device(); + throw CStreamSrtSourceVideoDeviceException(_HERE_,"Impossible to request buffers",errno,strerror(errno)); + } } - else + if(req.count<2) { this->close_video_device(); - throw CStreamSrtSourceVideoDeviceException(_HERE_,"Impossible to request buffers",errno,strerror(errno)); + throw CException(_HERE_,"Insifficient buffer memory for video device"); } - } - if(req.count<2) - { - this->close_video_device(); - throw CException(_HERE_,"Insifficient buffer memory for video device"); - } - this->video_data.buffers=(TVideoBuffer *)calloc(req.count,sizeof(*this->video_data.buffers)); - if(!this->video_data.buffers) - { - this->close_video_device(); - throw CException(_HERE_,"Out of memory"); - } - for(unsigned int i=0;i<req.count;++i) - { - struct v4l2_buffer buf; - - memset(&buf,0,sizeof(buf)); - buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory=V4L2_MEMORY_MMAP; - buf.index=i; - if(this->xioctl(this->video_data.fd,VIDIOC_QUERYBUF,&buf)==-1) + this->video_data.buffers=(TVideoBuffer *)calloc(req.count,sizeof(*this->video_data.buffers)); + if(!this->video_data.buffers) { this->close_video_device(); - throw CStreamSrtSourceVideoDeviceException(_HERE_,"Impossible to query buffer",errno,strerror(errno)); + throw CException(_HERE_,"Out of memory"); } - this->video_data.buffers[i].length=buf.length; - this->video_data.buffers[i].start = mmap(NULL /* start anywhere */, + for(unsigned int i=0;i<req.count;++i) + { + struct v4l2_buffer buf; + + memset(&buf,0,sizeof(buf)); + buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory=V4L2_MEMORY_MMAP; + buf.index=i; + if(this->xioctl(this->video_data.fd,VIDIOC_QUERYBUF,&buf)==-1) + { + this->close_video_device(); + throw CStreamSrtSourceVideoDeviceException(_HERE_,"Impossible to query buffer",errno,strerror(errno)); + } + this->video_data.buffers[i].length=buf.length; + this->video_data.buffers[i].start = mmap(NULL /* start anywhere */, buf.length, PROT_READ | PROT_WRITE /* required */, MAP_SHARED /* recommended */, this->video_data.fd, buf.m.offset); - if(this->video_data.buffers[i].start==MAP_FAILED) - { - this->close_video_device(); - throw CStreamSrtSourceVideoDeviceException(_HERE_,"MMAP error",errno,strerror(errno)); + if(this->video_data.buffers[i].start==MAP_FAILED) + { + this->close_video_device(); + throw CStreamSrtSourceVideoDeviceException(_HERE_,"MMAP error",errno,strerror(errno)); + } } + this->video_data.num_buffers=req.count; } - this->video_data.num_buffers=req.count; } void CStreamSrtSource::start_video_device(void) { enum v4l2_buf_type type; - for(unsigned int i=0;i<this->video_data.num_buffers;++i) + if(this->config.video_enable) { - struct v4l2_buffer buf; - memset(&buf,0,sizeof(buf)); - buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory=V4L2_MEMORY_MMAP; - buf.index=i; - if(this->xioctl(this->video_data.fd,VIDIOC_QBUF,&buf)==-1) + for(unsigned int i=0;i<this->video_data.num_buffers;++i) + { + struct v4l2_buffer buf; + memset(&buf,0,sizeof(buf)); + buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory=V4L2_MEMORY_MMAP; + buf.index=i; + if(this->xioctl(this->video_data.fd,VIDIOC_QBUF,&buf)==-1) + { + this->close_video_device(); + throw CStreamSrtSourceVideoDeviceException(_HERE_,"Impossible to query buffer",errno,strerror(errno)); + } + } + type=V4L2_BUF_TYPE_VIDEO_CAPTURE; + if(this->xioctl(this->video_data.fd,VIDIOC_STREAMON,&type)==-1) { this->close_video_device(); - throw CStreamSrtSourceVideoDeviceException(_HERE_,"Impossible to query buffer",errno,strerror(errno)); + throw CStreamSrtSourceVideoDeviceException(_HERE_,"Impossible to start video device",errno,strerror(errno)); } + // start thread + if(this->thread_server->get_thread_state(this->capture_video_thread_id)==attached) + this->thread_server->start_thread(this->capture_video_thread_id); } - type=V4L2_BUF_TYPE_VIDEO_CAPTURE; - if(this->xioctl(this->video_data.fd,VIDIOC_STREAMON,&type)==-1) - { - this->close_video_device(); - throw CStreamSrtSourceVideoDeviceException(_HERE_,"Impossible to start video device",errno,strerror(errno)); - } - // start thread - if(this->thread_server->get_thread_state(this->capture_video_thread_id)==attached) - this->thread_server->start_thread(this->capture_video_thread_id); } void CStreamSrtSource::stop_video_device(void) { enum v4l2_buf_type type; - // stop thread - if(this->thread_server->get_thread_state(this->capture_video_thread_id)==starting || - this->thread_server->get_thread_state(this->capture_video_thread_id)==active) - { - this->event_server->set_event(this->finish_video_thread_event_id); - this->thread_server->end_thread(this->capture_video_thread_id); - this->event_server->reset_event(this->finish_video_thread_event_id); - } - // stop the video device - if(this->video_data.fd!=-1) + if(this->config.video_enable) { - type=V4L2_BUF_TYPE_VIDEO_CAPTURE; - if(this->xioctl(this->video_data.fd,VIDIOC_STREAMOFF,&type)==-1) + // stop thread + if(this->thread_server->get_thread_state(this->capture_video_thread_id)==starting || + this->thread_server->get_thread_state(this->capture_video_thread_id)==active) { - this->close_video_device(); - throw CStreamSrtSourceVideoDeviceException(_HERE_,"Impossible to stop video device",errno,strerror(errno)); + this->event_server->set_event(this->finish_video_thread_event_id); + this->thread_server->end_thread(this->capture_video_thread_id); + this->event_server->reset_event(this->finish_video_thread_event_id); + } + // stop the video device + if(this->video_data.fd!=-1) + { + type=V4L2_BUF_TYPE_VIDEO_CAPTURE; + if(this->xioctl(this->video_data.fd,VIDIOC_STREAMOFF,&type)==-1) + { + this->close_video_device(); + throw CStreamSrtSourceVideoDeviceException(_HERE_,"Impossible to stop video device",errno,strerror(errno)); + } } } } void CStreamSrtSource::close_video_device(void) { - // free buffers - if(this->video_data.buffers!=NULL) + if(this->config.video_enable) { - for(unsigned int i=0;i<this->video_data.num_buffers;++i) + // free buffers + if(this->video_data.buffers!=NULL) { - if(munmap(this->video_data.buffers[i].start,this->video_data.buffers[i].length)==-1) + for(unsigned int i=0;i<this->video_data.num_buffers;++i) { - this->close_video_device(); - throw CStreamSrtSourceVideoDeviceException(_HERE_,"Impossible to free buffer memory",errno,strerror(errno)); + if(munmap(this->video_data.buffers[i].start,this->video_data.buffers[i].length)==-1) + { + this->close_video_device(); + throw CStreamSrtSourceVideoDeviceException(_HERE_,"Impossible to free buffer memory",errno,strerror(errno)); + } } + free(this->video_data.buffers); + this->video_data.buffers=NULL; + this->video_data.num_buffers=0; + } + // close device + if(this->video_data.fd!=-1) + { + if(::close(this->video_data.fd)==-1) + throw CStreamSrtSourceVideoDeviceException(_HERE_,"Video device not available",errno,strerror(errno)); + this->video_data.fd=-1; } - free(this->video_data.buffers); - this->video_data.buffers=NULL; - this->video_data.num_buffers=0; - } - // close device - if(this->video_data.fd!=-1) - { - if(::close(this->video_data.fd)==-1) - throw CStreamSrtSourceVideoDeviceException(_HERE_,"Video device not available",errno,strerror(errno)); - this->video_data.fd=-1; } } @@ -619,6 +681,8 @@ void *CStreamSrtSource::capture_video_thread(void *param) bool end=false; AVPacket pkt; fd_set fds; + std::chrono::time_point<std::chrono::system_clock> start_time=std::chrono::system_clock::now(),end_time; + std::chrono::duration<double> elapsed; pkt.data=NULL; while(!end) @@ -642,6 +706,9 @@ void *CStreamSrtSource::capture_video_thread(void *param) std::cout << "select timeout" << std::endl; else { + end_time=std::chrono::system_clock::now(); + elapsed=end_time-start_time; + start_time=end_time; //capture frame; memset(&buf,0,sizeof(buf)); buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; @@ -652,14 +719,24 @@ void *CStreamSrtSource::capture_video_thread(void *param) { av_init_packet(&pkt); pkt.dts=AV_NOPTS_VALUE; - if(stream->config.video.format==V4L2_PIX_FMT_YUV420) + if(stream->config.video.format!=V4L2_PIX_FMT_H264) { if(av_frame_make_writable(stream->video_data.frame)<0) std::cout << "Impossible to make video frame writable" << std::endl; - // copy data to frame - memcpy(stream->video_data.frame->data[0],stream->video_data.buffers[buf.index].start,stream->video_data.y_size); - memcpy(stream->video_data.frame->data[1],&((char *)stream->video_data.buffers[buf.index].start)[stream->video_data.y_size],stream->video_data.uv_size); - memcpy(stream->video_data.frame->data[2],&((char *)stream->video_data.buffers[buf.index].start)[stream->video_data.y_size+stream->video_data.uv_size],stream->video_data.uv_size); + if(stream->config.video.format!=V4L2_PIX_FMT_YUV420) + { + if(av_frame_make_writable(stream->video_data.tmp_frame)<0) + std::cout << "Impossible to make video frame writable" << std::endl; + memcpy(stream->video_data.tmp_frame->data[0],stream->video_data.buffers[buf.index].start,buf.bytesused); + sws_scale(stream->video_data.resampler,stream->video_data.tmp_frame->data,stream->video_data.tmp_frame->linesize,0,stream->config.video.height,stream->video_data.frame->data,stream->video_data.frame->linesize); + } + else + { + // copy data to frame + memcpy(stream->video_data.frame->data[0],stream->video_data.buffers[buf.index].start,stream->video_data.y_size); + memcpy(stream->video_data.frame->data[1],&((char *)stream->video_data.buffers[buf.index].start)[stream->video_data.y_size],stream->video_data.uv_size); + memcpy(stream->video_data.frame->data[2],&((char *)stream->video_data.buffers[buf.index].start)[stream->video_data.y_size+stream->video_data.uv_size],stream->video_data.uv_size); + } stream->video_data.frame->pts=stream->video_data.next_pts++; // call the external video callback if(stream->video_callback) @@ -667,7 +744,9 @@ void *CStreamSrtSource::capture_video_thread(void *param) ret=avcodec_encode_video2(stream->video_data.encoder_context,&pkt,stream->video_data.frame,&got_packet); if(ret<0) std::cout << "Error encoding video frame: " << av_err2str(ret) << std::endl; - av_packet_rescale_ts(&pkt,stream->video_data.encoder_context->time_base,stream->video_data.stream->time_base); + //av_packet_rescale_ts(&pkt,stream->video_data.encoder_context->time_base,stream->video_data.stream->time_base); + stream->video_data.frame->pts=stream->video_data.next_pts++; + av_packet_rescale_ts(&pkt,(AVRational){1,1.0/elapsed.count()},(AVRational){1,1.0/elapsed.count()}); } else { @@ -694,7 +773,7 @@ void *CStreamSrtSource::capture_video_thread(void *param) std::cout << "Impossible to stream video frame: " << av_err2str(ret) << std::endl; } stream->access.exit(); - if(stream->config.video.format!=V4L2_PIX_FMT_YUV420) + if(stream->config.video.format==V4L2_PIX_FMT_H264) delete[] pkt.data; } if(stream->xioctl(stream->video_data.fd,VIDIOC_QBUF,&buf)==-1) @@ -714,193 +793,198 @@ void CStreamSrtSource::open_audio(void) int ret,i; bool format_changed=false; - // try to locate the Hardware accelerated h264 encoder - this->audio_data.codec=avcodec_find_encoder(AV_CODEC_ID_AAC); - if(this->audio_data.codec==NULL) - throw CException(_HERE_,"Impossible to find an AAC encoder"); - // create a new ouptut stream - this->audio_data.stream=avformat_new_stream(this->output_context, NULL); - if(this->audio_data.stream==NULL) - throw CException(_HERE_,"Impossible to create a new audio stream"); - // configure the stream - this->audio_data.stream->time_base=(AVRational){ 1, this->config.audio.sample_rate}; - this->audio_data.stream->id=this->output_context->nb_streams-1; - // try to alloc a context for the encoder - this->audio_data.encoder_context=avcodec_alloc_context3(this->audio_data.codec); - if(this->audio_data.encoder_context==NULL) - { - this->close_audio(); - throw CException(_HERE_,"Impossible to allocate a context for the audio encoder"); - } - // configure the codec - if(this->audio_data.codec->sample_fmts) + if(this->config.audio_enable) { - this->audio_data.encoder_context->sample_fmt=AV_SAMPLE_FMT_NONE; - for(i=0;this->audio_data.codec->sample_fmts[i];i++) + // try to locate the Hardware accelerated h264 encoder + this->audio_data.codec=avcodec_find_encoder(AV_CODEC_ID_AAC); + if(this->audio_data.codec==NULL) + throw CException(_HERE_,"Impossible to find an AAC encoder"); + // create a new ouptut stream + this->audio_data.stream=avformat_new_stream(this->output_context, NULL); + if(this->audio_data.stream==NULL) + throw CException(_HERE_,"Impossible to create a new audio stream"); + // configure the stream + this->audio_data.stream->time_base=(AVRational){ 1, this->config.audio.sample_rate}; + this->audio_data.stream->id=this->output_context->nb_streams-1; + // try to alloc a context for the encoder + this->audio_data.encoder_context=avcodec_alloc_context3(this->audio_data.codec); + if(this->audio_data.encoder_context==NULL) + { + this->close_audio(); + throw CException(_HERE_,"Impossible to allocate a context for the audio encoder"); + } + // configure the codec + if(this->audio_data.codec->sample_fmts) { - if(this->audio_data.codec->sample_fmts[i]==this->audio_data.av_sample_format) + this->audio_data.encoder_context->sample_fmt=AV_SAMPLE_FMT_NONE; + for(i=0;this->audio_data.codec->sample_fmts[i];i++) { - this->audio_data.encoder_context->sample_fmt=this->audio_data.av_sample_format; - break; + if(this->audio_data.codec->sample_fmts[i]==this->audio_data.av_sample_format) + { + this->audio_data.encoder_context->sample_fmt=this->audio_data.av_sample_format; + break; + } } - } - if(this->audio_data.encoder_context->sample_fmt==AV_SAMPLE_FMT_NONE) - { - this->audio_data.encoder_context->sample_fmt=this->audio_data.codec->sample_fmts[0]; - std::cout << "Audio sample format not supported, using " << this->audio_data.encoder_context->sample_fmt << std::endl; - format_changed=true; - } - } - else - this->audio_data.encoder_context->sample_fmt=this->audio_data.av_sample_format; - this->audio_data.encoder_context->bit_rate=this->config.audio.bitrate; - if(this->audio_data.codec->supported_samplerates) - { - this->audio_data.encoder_context->sample_rate=0; - for(i=0;this->audio_data.codec->supported_samplerates[i];i++) - { - if(this->audio_data.codec->supported_samplerates[i]==this->config.audio.sample_rate) + if(this->audio_data.encoder_context->sample_fmt==AV_SAMPLE_FMT_NONE) { - this->audio_data.encoder_context->sample_rate = this->config.audio.sample_rate; - break; + this->audio_data.encoder_context->sample_fmt=this->audio_data.codec->sample_fmts[0]; + std::cout << "Audio sample format not supported, using " << this->audio_data.encoder_context->sample_fmt << std::endl; + format_changed=true; } } - if(this->audio_data.encoder_context->sample_rate==0) - { - this->audio_data.encoder_context->sample_rate=this->audio_data.codec->supported_samplerates[0]; - this->config.audio.sample_rate=this->audio_data.codec->supported_samplerates[0]; - std::cout << "Audio sample rate not supported, using " << this->audio_data.encoder_context->sample_rate << std::endl; - } - } - else - this->audio_data.encoder_context->sample_rate = this->config.audio.sample_rate; - this->audio_data.encoder_context->channels=this->config.audio.num_channels; - if(this->audio_data.codec->channel_layouts) - { - this->audio_data.encoder_context->channel_layout=0; - for(i=0;this->audio_data.codec->channel_layouts[i];i++) + else + this->audio_data.encoder_context->sample_fmt=this->audio_data.av_sample_format; + this->audio_data.encoder_context->bit_rate=this->config.audio.bitrate; + if(this->audio_data.codec->supported_samplerates) { - if(this->audio_data.codec->channel_layouts[i]==this->audio_data.av_channels_layout) + this->audio_data.encoder_context->sample_rate=0; + for(i=0;this->audio_data.codec->supported_samplerates[i];i++) { - this->audio_data.encoder_context->channel_layout=this->audio_data.av_channels_layout; - break; + if(this->audio_data.codec->supported_samplerates[i]==this->config.audio.sample_rate) + { + this->audio_data.encoder_context->sample_rate = this->config.audio.sample_rate; + break; + } + } + if(this->audio_data.encoder_context->sample_rate==0) + { + this->audio_data.encoder_context->sample_rate=this->audio_data.codec->supported_samplerates[0]; + this->config.audio.sample_rate=this->audio_data.codec->supported_samplerates[0]; + std::cout << "Audio sample rate not supported, using " << this->audio_data.encoder_context->sample_rate << std::endl; } } - if(this->audio_data.encoder_context->channel_layout==0) + else + this->audio_data.encoder_context->sample_rate = this->config.audio.sample_rate; + this->audio_data.encoder_context->channels=this->config.audio.num_channels; + if(this->audio_data.codec->channel_layouts) { - this->audio_data.encoder_context->channel_layout = this->audio_data.codec->channel_layouts[0]; - this->audio_data.av_channels_layout=this->audio_data.codec->supported_samplerates[0]; - std::cout << "Audio channel layout not supported, using " << this->audio_data.encoder_context->channel_layout << std::endl; + this->audio_data.encoder_context->channel_layout=0; + for(i=0;this->audio_data.codec->channel_layouts[i];i++) + { + if(this->audio_data.codec->channel_layouts[i]==this->audio_data.av_channels_layout) + { + this->audio_data.encoder_context->channel_layout=this->audio_data.av_channels_layout; + break; + } + } + if(this->audio_data.encoder_context->channel_layout==0) + { + this->audio_data.encoder_context->channel_layout = this->audio_data.codec->channel_layouts[0]; + this->audio_data.av_channels_layout=this->audio_data.codec->supported_samplerates[0]; + std::cout << "Audio channel layout not supported, using " << this->audio_data.encoder_context->channel_layout << std::endl; + } } - } - else - this->audio_data.encoder_context->channel_layout=this->audio_data.av_channels_layout; -// this->audio_data.encoder_context->flags|=AV_CODEC_FLAG_GLOBAL_HEADER; + else + this->audio_data.encoder_context->channel_layout=this->audio_data.av_channels_layout; - // open codec - ret=avcodec_open2(this->audio_data.encoder_context,this->audio_data.codec,NULL); - if(ret<0) - { - this->close_audio(); - throw CException(_HERE_,"Impossible to open audio codec"); - } - if(this->audio_data.encoder_context->codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE) - this->audio_data.nb_samples = 1024; - else - this->audio_data.nb_samples = this->audio_data.encoder_context->frame_size; - // create a resampler if necessary - if(format_changed) - { - this->audio_data.resampler=swr_alloc(); - if(this->audio_data.resampler==NULL) + // open codec + ret=avcodec_open2(this->audio_data.encoder_context,this->audio_data.codec,NULL); + if(ret<0) { this->close_audio(); - throw CException(_HERE_,"Impossible to allocate resampler"); + throw CException(_HERE_,"Impossible to open audio codec"); } - /* set options */ - av_opt_set_int(this->audio_data.resampler,"in_channel_count",this->audio_data.encoder_context->channels,0); - av_opt_set_int(this->audio_data.resampler,"in_sample_rate",this->audio_data.encoder_context->sample_rate,0); - av_opt_set_sample_fmt(this->audio_data.resampler,"in_sample_fmt",this->audio_data.av_sample_format, 0); - av_opt_set_int(this->audio_data.resampler,"out_channel_count",this->audio_data.encoder_context->channels,0); - av_opt_set_int(this->audio_data.resampler,"out_sample_rate",this->audio_data.encoder_context->sample_rate,0); - av_opt_set_sample_fmt(this->audio_data.resampler,"out_sample_fmt",this->audio_data.encoder_context->sample_fmt,0); + if(this->audio_data.encoder_context->codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE) + this->audio_data.nb_samples = 1024; + else + this->audio_data.nb_samples = this->audio_data.encoder_context->frame_size; + // create a resampler if necessary + if(format_changed) + { + this->audio_data.resampler=swr_alloc(); + if(this->audio_data.resampler==NULL) + { + this->close_audio(); + throw CException(_HERE_,"Impossible to allocate resampler"); + } + /* set options */ + av_opt_set_int(this->audio_data.resampler,"in_channel_count",this->audio_data.encoder_context->channels,0); + av_opt_set_int(this->audio_data.resampler,"in_sample_rate",this->audio_data.encoder_context->sample_rate,0); + av_opt_set_sample_fmt(this->audio_data.resampler,"in_sample_fmt",this->audio_data.av_sample_format, 0); + av_opt_set_int(this->audio_data.resampler,"out_channel_count",this->audio_data.encoder_context->channels,0); + av_opt_set_int(this->audio_data.resampler,"out_sample_rate",this->audio_data.encoder_context->sample_rate,0); + av_opt_set_sample_fmt(this->audio_data.resampler,"out_sample_fmt",this->audio_data.encoder_context->sample_fmt,0); - /* initialize the resampling context */ - if((ret=swr_init(this->audio_data.resampler))<0) + /* initialize the resampling context */ + if((ret=swr_init(this->audio_data.resampler))<0) + { + this->close_audio(); + throw CException(_HERE_,"Impossible to initialize resampler"); + } + this->audio_data.tmp_frame=av_frame_alloc(); + if(this->audio_data.tmp_frame==NULL) + { + this->close_audio(); + throw CException(_HERE_,"Impossible to allocate an audio frame"); + } + this->audio_data.tmp_frame->format=this->audio_data.av_sample_format; + this->audio_data.tmp_frame->channel_layout=this->audio_data.av_channels_layout; + this->audio_data.tmp_frame->sample_rate=this->config.audio.sample_rate; + this->audio_data.tmp_frame->nb_samples=this->audio_data.nb_samples; + ret=av_frame_get_buffer(this->audio_data.tmp_frame, 0); + if(ret<0) + { + this->close_audio(); + throw CException(_HERE_,"Impossible to allocate an audio frame data"); + } + this->audio_data.av_sample_format=this->audio_data.encoder_context->sample_fmt; + } + else { - this->close_audio(); - throw CException(_HERE_,"Impossible to initialize resampler"); + this->audio_data.resampler=NULL; + this->audio_data.tmp_frame=NULL; } - this->audio_data.tmp_frame=av_frame_alloc(); - if(this->audio_data.tmp_frame==NULL) + // allocate frame + this->audio_data.frame=av_frame_alloc(); + if(this->audio_data.frame==NULL) { this->close_audio(); throw CException(_HERE_,"Impossible to allocate an audio frame"); } - this->audio_data.tmp_frame->format=this->audio_data.av_sample_format; - this->audio_data.tmp_frame->channel_layout=this->audio_data.av_channels_layout; - this->audio_data.tmp_frame->sample_rate=this->config.audio.sample_rate; - this->audio_data.tmp_frame->nb_samples=this->audio_data.nb_samples; - ret=av_frame_get_buffer(this->audio_data.tmp_frame, 0); + this->audio_data.frame->format=this->audio_data.encoder_context->sample_fmt; + this->audio_data.frame->channel_layout=this->audio_data.av_channels_layout; + this->audio_data.frame->sample_rate=this->config.audio.sample_rate; + this->audio_data.frame->nb_samples=this->audio_data.nb_samples; + ret=av_frame_get_buffer(this->audio_data.frame, 0); if(ret<0) { this->close_audio(); throw CException(_HERE_,"Impossible to allocate an audio frame data"); - } - this->audio_data.av_sample_format=this->audio_data.encoder_context->sample_fmt; - } - else - { - this->audio_data.resampler=NULL; - this->audio_data.tmp_frame=NULL; - } - // allocate frame - this->audio_data.frame=av_frame_alloc(); - if(this->audio_data.frame==NULL) - { - this->close_audio(); - throw CException(_HERE_,"Impossible to allocate an audio frame"); + } + // copy codec parameters to the stream + ret=avcodec_parameters_from_context(this->audio_data.stream->codecpar,this->audio_data.encoder_context); + if(ret<0) + { + this->close_audio(); + throw CException(_HERE_,"Impossible to copy audio codec data to the stream"); + } } - this->audio_data.frame->format=this->audio_data.encoder_context->sample_fmt; - this->audio_data.frame->channel_layout=this->audio_data.av_channels_layout; - this->audio_data.frame->sample_rate=this->config.audio.sample_rate; - this->audio_data.frame->nb_samples=this->audio_data.nb_samples; - ret=av_frame_get_buffer(this->audio_data.frame, 0); - if(ret<0) - { - this->close_audio(); - throw CException(_HERE_,"Impossible to allocate an audio frame data"); - } - // copy codec parameters to the stream - ret=avcodec_parameters_from_context(this->audio_data.stream->codecpar,this->audio_data.encoder_context); - if(ret<0) - { - this->close_audio(); - throw CException(_HERE_,"Impossible to copy audio codec data to the stream"); - } } void CStreamSrtSource::close_audio(void) { - if(this->audio_data.encoder_context!=NULL) - { - avcodec_free_context(&this->audio_data.encoder_context); - this->audio_data.encoder_context=NULL; - } - if(this->audio_data.frame!=NULL) - { - av_frame_free(&this->audio_data.frame); - this->audio_data.frame=NULL; - } - if(this->audio_data.tmp_frame!=NULL) + if(this->config.audio_enable) { - av_frame_free(&this->audio_data.tmp_frame); - this->audio_data.tmp_frame=NULL; - } - if(this->audio_data.resampler!=NULL) - { - swr_free(&this->audio_data.resampler); - this->audio_data.resampler=NULL; + if(this->audio_data.encoder_context!=NULL) + { + avcodec_free_context(&this->audio_data.encoder_context); + this->audio_data.encoder_context=NULL; + } + if(this->audio_data.frame!=NULL) + { + av_frame_free(&this->audio_data.frame); + this->audio_data.frame=NULL; + } + if(this->audio_data.tmp_frame!=NULL) + { + av_frame_free(&this->audio_data.tmp_frame); + this->audio_data.tmp_frame=NULL; + } + if(this->audio_data.resampler!=NULL) + { + swr_free(&this->audio_data.resampler); + this->audio_data.resampler=NULL; + } } } @@ -912,108 +996,117 @@ void CStreamSrtSource::open_audio_device(void) std::string device_name; PaStreamParameters audio_params; - // reset any pending events - if(this->event_server->event_is_set(this->new_audio_buffer_event_id)) + if(this->config.audio_enable) { - this->event_server->reset_event(this->new_audio_buffer_event_id); - std::cout << "resetting audio event" << std::endl; - } - err=Pa_Initialize(); - if(err!=paNoError) - throw CStreamSrtSourceAudioDeviceException(_HERE_,"Impossible to initialize PortAudio",err,Pa_GetErrorText(err)); - num_devices=Pa_GetDeviceCount(); - for(PaDeviceIndex i=0;i<num_devices;i++) - { - device_info=Pa_GetDeviceInfo(i); - device_name=std::string(device_info->name); - if(device_name.find((char *)this->config.audio.device)!=std::string::npos)// device found + // reset any pending events + if(this->event_server->event_is_set(this->new_audio_buffer_event_id)) { - audio_params.device=i; - audio_params.channelCount=this->config.audio.num_channels; - audio_params.sampleFormat=this->audio_data.pa_sample_format; - audio_params.hostApiSpecificStreamInfo=NULL; - audio_params.suggestedLatency=0.0; - err = Pa_OpenStream(&this->audio_data.audio_stream, - &audio_params, - &audio_params, - this->config.audio.sample_rate, - this->audio_data.nb_samples, /* frames per buffer */ - 0, - audioCallback, - this); - if(err!=paNoError) - { - this->close_audio_device(); - throw CStreamSrtSourceAudioDeviceException(_HERE_,"Impossible to open audio device",err,Pa_GetErrorText(err)); - } - // initialize buffers - if(this->audio_data.buffers!=NULL) + this->event_server->reset_event(this->new_audio_buffer_event_id); + std::cout << "resetting audio event" << std::endl; + } + err=Pa_Initialize(); + if(err!=paNoError) + throw CStreamSrtSourceAudioDeviceException(_HERE_,"Impossible to initialize PortAudio",err,Pa_GetErrorText(err)); + num_devices=Pa_GetDeviceCount(); + for(PaDeviceIndex i=0;i<num_devices;i++) + { + device_info=Pa_GetDeviceInfo(i); + device_name=std::string(device_info->name); + if(device_name.find((char *)this->config.audio.device)!=std::string::npos)// device found { - for(unsigned int i=0;i<NUM_AUDIO_BUFFERS;i++) + audio_params.device=i; + audio_params.channelCount=this->config.audio.num_channels; + audio_params.sampleFormat=this->audio_data.pa_sample_format; + audio_params.hostApiSpecificStreamInfo=NULL; + audio_params.suggestedLatency=0.0; + err = Pa_OpenStream(&this->audio_data.audio_stream, + &audio_params, + &audio_params, + this->config.audio.sample_rate, + this->audio_data.nb_samples, /* frames per buffer */ + 0, + audioCallback, + this); + if(err!=paNoError) + { + this->close_audio_device(); + throw CStreamSrtSourceAudioDeviceException(_HERE_,"Impossible to open audio device",err,Pa_GetErrorText(err)); + } + // initialize buffers + if(this->audio_data.buffers!=NULL) { - if(this->audio_data.buffers[i].data!=NULL) + for(unsigned int i=0;i<NUM_AUDIO_BUFFERS;i++) { - for(int j=0;j<this->config.audio.num_channels;j++) + if(this->audio_data.buffers[i].data!=NULL) { - delete[] this->audio_data.buffers[i].data[j]; - this->audio_data.buffers[i].data[j]=NULL; + for(int j=0;j<this->config.audio.num_channels;j++) + { + delete[] this->audio_data.buffers[i].data[j]; + this->audio_data.buffers[i].data[j]=NULL; + } + delete[] this->audio_data.buffers[i].data; + audio_data.buffers[i].data=NULL; } - delete[] this->audio_data.buffers[i].data; - audio_data.buffers[i].data=NULL; } + delete[] this->audio_data.buffers; + this->audio_data.buffers=NULL; } - delete[] this->audio_data.buffers; - this->audio_data.buffers=NULL; - } - this->audio_data.buffers=new TAudioBuffer[NUM_AUDIO_BUFFERS]; - for(unsigned int i=0;i<NUM_AUDIO_BUFFERS;i++) - { - audio_data.buffers[i].data=new unsigned char *[this->config.audio.num_channels]; - for(int j=0;j<this->config.audio.num_channels;j++) - audio_data.buffers[i].data[j]=new unsigned char[this->audio_data.nb_samples*this->audio_data.sample_size]; - audio_data.buffers[i].frame_time=0; - audio_data.buffers[i].frame_pts=0; - audio_data.buffers[i].num_bytes=0; + this->audio_data.buffers=new TAudioBuffer[NUM_AUDIO_BUFFERS]; + for(unsigned int i=0;i<NUM_AUDIO_BUFFERS;i++) + { + audio_data.buffers[i].data=new unsigned char *[this->config.audio.num_channels]; + for(int j=0;j<this->config.audio.num_channels;j++) + audio_data.buffers[i].data[j]=new unsigned char[this->audio_data.nb_samples*this->audio_data.sample_size]; + audio_data.buffers[i].frame_time=0; + audio_data.buffers[i].frame_pts=0; + audio_data.buffers[i].num_bytes=0; + } + this->audio_data.buffer_write=0; + this->audio_data.buffer_read=0; } - this->audio_data.buffer_write=0; - this->audio_data.buffer_read=0; + return; } - return; + throw CException(_HERE_,"Audio device not available"); } - throw CException(_HERE_,"Audio device not available"); } void CStreamSrtSource::start_audio_device(void) { PaError err; - err=Pa_StartStream(this->audio_data.audio_stream); - if(err!=paNoError) - throw CStreamSrtSourceAudioDeviceException(_HERE_,"Impossible to start audio device",err,Pa_GetErrorText(err)); - // start thread - if(this->thread_server->get_thread_state(this->capture_audio_thread_id)==attached) - this->thread_server->start_thread(this->capture_audio_thread_id); - this->audio_data.next_pts=0; - this->audio_data.start_time=std::chrono::high_resolution_clock::now(); + if(this->config.audio_enable) + { + err=Pa_StartStream(this->audio_data.audio_stream); + if(err!=paNoError) + throw CStreamSrtSourceAudioDeviceException(_HERE_,"Impossible to start audio device",err,Pa_GetErrorText(err)); + // start thread + if(this->thread_server->get_thread_state(this->capture_audio_thread_id)==attached) + this->thread_server->start_thread(this->capture_audio_thread_id); + this->audio_data.next_pts=0; + this->audio_data.start_time=std::chrono::high_resolution_clock::now(); + } } void CStreamSrtSource::stop_audio_device(void) { PaError err; - // stop thread - if(this->thread_server->get_thread_state(this->capture_audio_thread_id)==starting || - this->thread_server->get_thread_state(this->capture_audio_thread_id)==active) + if(this->config.audio_enable) { - this->event_server->set_event(this->finish_audio_thread_event_id); - this->thread_server->end_thread(this->capture_audio_thread_id); - this->event_server->reset_event(this->finish_audio_thread_event_id); - } - if(this->audio_data.audio_stream!=NULL) - { - err=Pa_StopStream(this->audio_data.audio_stream); - if(err!=paNoError) - throw CStreamSrtSourceAudioDeviceException(_HERE_,"Impossible to stop audio device",err,Pa_GetErrorText(err)); + // stop thread + if(this->thread_server->get_thread_state(this->capture_audio_thread_id)==starting || + this->thread_server->get_thread_state(this->capture_audio_thread_id)==active) + { + this->event_server->set_event(this->finish_audio_thread_event_id); + this->thread_server->end_thread(this->capture_audio_thread_id); + this->event_server->reset_event(this->finish_audio_thread_event_id); + } + if(this->audio_data.audio_stream!=NULL) + { + err=Pa_StopStream(this->audio_data.audio_stream); + if(err!=paNoError) + throw CStreamSrtSourceAudioDeviceException(_HERE_,"Impossible to stop audio device",err,Pa_GetErrorText(err)); + } } } @@ -1021,35 +1114,38 @@ void CStreamSrtSource::close_audio_device(void) { PaError err; - if(this->audio_data.audio_stream!=NULL) + if(this->config.audio_enable) { - err=Pa_CloseStream(this->audio_data.audio_stream); - if(err!=paNoError) - throw CStreamSrtSourceAudioDeviceException(_HERE_,"Impossible to close audio device",err,Pa_GetErrorText(err)); - this->audio_data.audio_stream=NULL; - } - Pa_Terminate(); - // delete buffers - if(this->audio_data.buffers!=NULL) - { - for(unsigned int i=0;i<NUM_AUDIO_BUFFERS;i++) + if(this->audio_data.audio_stream!=NULL) + { + err=Pa_CloseStream(this->audio_data.audio_stream); + if(err!=paNoError) + throw CStreamSrtSourceAudioDeviceException(_HERE_,"Impossible to close audio device",err,Pa_GetErrorText(err)); + this->audio_data.audio_stream=NULL; + } + Pa_Terminate(); + // delete buffers + if(this->audio_data.buffers!=NULL) { - if(this->audio_data.buffers[i].data!=NULL) + for(unsigned int i=0;i<NUM_AUDIO_BUFFERS;i++) { - for(int j=0;j<this->config.audio.num_channels;j++) + if(this->audio_data.buffers[i].data!=NULL) { - delete[] this->audio_data.buffers[i].data[j]; - this->audio_data.buffers[i].data[j]=NULL; + for(int j=0;j<this->config.audio.num_channels;j++) + { + delete[] this->audio_data.buffers[i].data[j]; + this->audio_data.buffers[i].data[j]=NULL; + } + delete[] audio_data.buffers[i].data; + audio_data.buffers[i].data=NULL; } - delete[] audio_data.buffers[i].data; - audio_data.buffers[i].data=NULL; } + delete[] this->audio_data.buffers; + this->audio_data.buffers=NULL; } - delete[] this->audio_data.buffers; - this->audio_data.buffers=NULL; + this->audio_data.buffer_write=0; + this->audio_data.buffer_read=0; } - this->audio_data.buffer_write=0; - this->audio_data.buffer_read=0; } int CStreamSrtSource::audioCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData) @@ -1169,70 +1265,82 @@ void *CStreamSrtSource::capture_audio_thread(void *param) void CStreamSrtSource::load_config(const std::string &filename) { CStreamSrtSource::read_config(filename,this->config); - this->video_data.y_size=this->config.video.width*this->config.video.height; - this->video_data.uv_size=this->config.video.width*this->config.video.height/4; - if(this->config.audio.num_channels==1) - this->audio_data.av_channels_layout=AV_CH_LAYOUT_MONO; - else if(this->config.audio.num_channels==2) - this->audio_data.av_channels_layout=AV_CH_LAYOUT_STEREO; - if(this->config.audio.format==paUInt8) + if(this->config.video_enable) { - this->audio_data.pa_sample_format=paUInt8; - this->audio_data.av_sample_format=AV_SAMPLE_FMT_U8P; - this->audio_data.sample_size=sizeof(unsigned char); + this->video_data.y_size=this->config.video.width*this->config.video.height; + this->video_data.uv_size=this->config.video.width*this->config.video.height/4; } - else if(this->config.audio.format==paInt16) + if(this->config.audio_enable) { - this->audio_data.pa_sample_format=paInt16; - this->audio_data.av_sample_format=AV_SAMPLE_FMT_S16P; - this->audio_data.sample_size=sizeof(short int); - } - else if(this->config.audio.format==paInt32) - { - this->audio_data.pa_sample_format=paInt32; - this->audio_data.av_sample_format=AV_SAMPLE_FMT_S32P; - this->audio_data.sample_size=sizeof(int); - } - else if(this->config.audio.format==paFloat32) - { - this->audio_data.pa_sample_format=paFloat32; - this->audio_data.av_sample_format=AV_SAMPLE_FMT_FLTP; - this->audio_data.sample_size=sizeof(float); + if(this->config.audio.num_channels==1) + this->audio_data.av_channels_layout=AV_CH_LAYOUT_MONO; + else if(this->config.audio.num_channels==2) + this->audio_data.av_channels_layout=AV_CH_LAYOUT_STEREO; + if(this->config.audio.format==paUInt8) + { + this->audio_data.pa_sample_format=paUInt8; + this->audio_data.av_sample_format=AV_SAMPLE_FMT_U8P; + this->audio_data.sample_size=sizeof(unsigned char); + } + else if(this->config.audio.format==paInt16) + { + this->audio_data.pa_sample_format=paInt16; + this->audio_data.av_sample_format=AV_SAMPLE_FMT_S16P; + this->audio_data.sample_size=sizeof(short int); + } + else if(this->config.audio.format==paInt32) + { + this->audio_data.pa_sample_format=paInt32; + this->audio_data.av_sample_format=AV_SAMPLE_FMT_S32P; + this->audio_data.sample_size=sizeof(int); + } + else if(this->config.audio.format==paFloat32) + { + this->audio_data.pa_sample_format=paFloat32; + this->audio_data.av_sample_format=AV_SAMPLE_FMT_FLTP; + this->audio_data.sample_size=sizeof(float); + } } } void CStreamSrtSource::load_config(TStreamConfig &strm_config) { memcpy(&this->config,&strm_config,sizeof(TStreamConfig)); - this->video_data.y_size=this->config.video.width*this->config.video.height; - this->video_data.uv_size=this->config.video.width*this->config.video.height/4; - if(this->config.audio.num_channels==1) - this->audio_data.av_channels_layout=AV_CH_LAYOUT_MONO; - else if(this->config.audio.num_channels==2) - this->audio_data.av_channels_layout=AV_CH_LAYOUT_STEREO; - if(this->config.audio.format==paUInt8) - { - this->audio_data.pa_sample_format=paUInt8; - this->audio_data.av_sample_format=AV_SAMPLE_FMT_U8P; - this->audio_data.sample_size=sizeof(unsigned char); - } - else if(this->config.audio.format==paInt16) - { - this->audio_data.pa_sample_format=paInt16; - this->audio_data.av_sample_format=AV_SAMPLE_FMT_S16P; - this->audio_data.sample_size=sizeof(short int); - } - else if(this->config.audio.format==paInt32) + if(this->config.video_enable) { - this->audio_data.pa_sample_format=paInt32; - this->audio_data.av_sample_format=AV_SAMPLE_FMT_S32P; - this->audio_data.sample_size=sizeof(int); + this->video_data.y_size=this->config.video.width*this->config.video.height; + this->video_data.uv_size=this->config.video.width*this->config.video.height/4; } - else if(this->config.audio.format==paFloat32) + if(this->config.audio_enable) { - this->audio_data.pa_sample_format=paFloat32; - this->audio_data.av_sample_format=AV_SAMPLE_FMT_FLTP; - this->audio_data.sample_size=sizeof(float); + if(this->config.audio.num_channels==1) + this->audio_data.av_channels_layout=AV_CH_LAYOUT_MONO; + else if(this->config.audio.num_channels==2) + this->audio_data.av_channels_layout=AV_CH_LAYOUT_STEREO; + if(this->config.audio.format==paUInt8) + { + this->audio_data.pa_sample_format=paUInt8; + this->audio_data.av_sample_format=AV_SAMPLE_FMT_U8P; + this->audio_data.sample_size=sizeof(unsigned char); + } + else if(this->config.audio.format==paInt16) + { + this->audio_data.pa_sample_format=paInt16; + this->audio_data.av_sample_format=AV_SAMPLE_FMT_S16P; + this->audio_data.sample_size=sizeof(short int); + } + else if(this->config.audio.format==paInt32) + { + this->audio_data.pa_sample_format=paInt32; + this->audio_data.av_sample_format=AV_SAMPLE_FMT_S32P; + this->audio_data.sample_size=sizeof(int); + } + else if(this->config.audio.format==paFloat32) + { + this->audio_data.pa_sample_format=paFloat32; + this->audio_data.av_sample_format=AV_SAMPLE_FMT_FLTP; + this->audio_data.sample_size=sizeof(float); + } } } @@ -1249,8 +1357,9 @@ void CStreamSrtSource::set_video_callback(video_callback_t video_callback) void CStreamSrtSource::start(void) { // start thread - if(this->thread_server->get_thread_state(this->main_thread_id)==attached) - this->thread_server->start_thread(this->main_thread_id); + if(this->config.video_enable || this->config.audio_enable) + if(this->thread_server->get_thread_state(this->main_thread_id)==attached) + this->thread_server->start_thread(this->main_thread_id); } void CStreamSrtSource::stop(void) @@ -1276,91 +1385,111 @@ void CStreamSrtSource::read_config(const std::string &filename,TStreamConfig &st try{ std::unique_ptr<stream_config_t> cfg(stream_config(filename.c_str(), xml_schema::flags::dont_validate)); // load the video configuration - std::copy(cfg->video_config().video_device().begin(),cfg->video_config().video_device().end(),strm_config.video.device); - strm_config.video.device[cfg->video_config().video_device().size()]='\0'; - strm_config.video.width=cfg->video_config().width(); - strm_config.video.height=cfg->video_config().height(); - strm_config.video.framerate=cfg->video_config().framerate(); - if(cfg->video_config().format().compare("YUV420")==0) - strm_config.video.format=V4L2_PIX_FMT_YUV420; - else if(cfg->video_config().format().compare("H264")==0) - strm_config.video.format=V4L2_PIX_FMT_H264; - else - throw CException(_HERE_,"Video format not supported"); - // video encoder configuration - strm_config.video.bitrate=cfg->video_config().encoder().bitrate(); - strm_config.video.variable_bitrate=cfg->video_config().encoder().variable_bitrate(); - strm_config.video.i_frame_period=cfg->video_config().encoder().i_frame_period(); - if(cfg->video_config().encoder().level().compare("1")==0) - strm_config.video.level=V4L2_MPEG_VIDEO_H264_LEVEL_1_0; - else if(cfg->video_config().encoder().level().compare("1b")==0) - strm_config.video.level=V4L2_MPEG_VIDEO_H264_LEVEL_1B; - else if(cfg->video_config().encoder().level().compare("1.1")==0) - strm_config.video.level=V4L2_MPEG_VIDEO_H264_LEVEL_1_1; - else if(cfg->video_config().encoder().level().compare("1.2")==0) - strm_config.video.level=V4L2_MPEG_VIDEO_H264_LEVEL_1_2; - else if(cfg->video_config().encoder().level().compare("1.3")==0) - strm_config.video.level=V4L2_MPEG_VIDEO_H264_LEVEL_1_3; - else if(cfg->video_config().encoder().level().compare("2")==0) - strm_config.video.level=V4L2_MPEG_VIDEO_H264_LEVEL_2_0; - else if(cfg->video_config().encoder().level().compare("2.1")==0) - strm_config.video.level=V4L2_MPEG_VIDEO_H264_LEVEL_2_1; - else if(cfg->video_config().encoder().level().compare("2.2")==0) - strm_config.video.level=V4L2_MPEG_VIDEO_H264_LEVEL_2_2; - else if(cfg->video_config().encoder().level().compare("3")==0) - strm_config.video.level=V4L2_MPEG_VIDEO_H264_LEVEL_3_0; - else if(cfg->video_config().encoder().level().compare("3.1")==0) - strm_config.video.level=V4L2_MPEG_VIDEO_H264_LEVEL_3_1; - else if(cfg->video_config().encoder().level().compare("3.2")==0) - strm_config.video.level=V4L2_MPEG_VIDEO_H264_LEVEL_3_2; - else if(cfg->video_config().encoder().level().compare("4")==0) - strm_config.video.level=V4L2_MPEG_VIDEO_H264_LEVEL_4_0; - else - throw CException(_HERE_,"H264 level not supported"); - if(cfg->video_config().encoder().profile().compare("baseline")==0) + if(cfg->video_enable()) { - strm_config.video.v4l2_profile=V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; - strm_config.video.av_profile=FF_PROFILE_H264_BASELINE; - } - else if(cfg->video_config().encoder().profile().compare("const_baseline")==0) - { - strm_config.video.v4l2_profile=V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE; - strm_config.video.av_profile=FF_PROFILE_H264_CONSTRAINED_BASELINE; - } - else if(cfg->video_config().encoder().profile().compare("main")==0) - { - strm_config.video.v4l2_profile=V4L2_MPEG_VIDEO_H264_PROFILE_MAIN; - strm_config.video.av_profile=FF_PROFILE_H264_MAIN; + strm_config.video_enable=true; + std::copy(cfg->video_config().video_device().begin(),cfg->video_config().video_device().end(),strm_config.video.device); + strm_config.video.device[cfg->video_config().video_device().size()]='\0'; + strm_config.video.width=cfg->video_config().width(); + strm_config.video.height=cfg->video_config().height(); + strm_config.video.framerate=cfg->video_config().framerate(); + if(cfg->video_config().format().compare("YUV420")==0) + { + strm_config.video.format=V4L2_PIX_FMT_YUV420; + strm_config.video.av_format=AV_PIX_FMT_YUV420P; + } + else if(cfg->video_config().format().compare("RGB24")==0) + { + strm_config.video.format=V4L2_PIX_FMT_RGB24; + strm_config.video.av_format=AV_PIX_FMT_RGB24; + } + else if(cfg->video_config().format().compare("H264")==0) + strm_config.video.format=V4L2_PIX_FMT_H264; + else + throw CException(_HERE_,"Video format not supported"); + // video encoder configuration + strm_config.video.bitrate=cfg->video_config().encoder().bitrate(); + strm_config.video.variable_bitrate=cfg->video_config().encoder().variable_bitrate(); + strm_config.video.i_frame_period=cfg->video_config().encoder().i_frame_period(); + if(cfg->video_config().encoder().level().compare("1")==0) + strm_config.video.level=V4L2_MPEG_VIDEO_H264_LEVEL_1_0; + else if(cfg->video_config().encoder().level().compare("1b")==0) + strm_config.video.level=V4L2_MPEG_VIDEO_H264_LEVEL_1B; + else if(cfg->video_config().encoder().level().compare("1.1")==0) + strm_config.video.level=V4L2_MPEG_VIDEO_H264_LEVEL_1_1; + else if(cfg->video_config().encoder().level().compare("1.2")==0) + strm_config.video.level=V4L2_MPEG_VIDEO_H264_LEVEL_1_2; + else if(cfg->video_config().encoder().level().compare("1.3")==0) + strm_config.video.level=V4L2_MPEG_VIDEO_H264_LEVEL_1_3; + else if(cfg->video_config().encoder().level().compare("2")==0) + strm_config.video.level=V4L2_MPEG_VIDEO_H264_LEVEL_2_0; + else if(cfg->video_config().encoder().level().compare("2.1")==0) + strm_config.video.level=V4L2_MPEG_VIDEO_H264_LEVEL_2_1; + else if(cfg->video_config().encoder().level().compare("2.2")==0) + strm_config.video.level=V4L2_MPEG_VIDEO_H264_LEVEL_2_2; + else if(cfg->video_config().encoder().level().compare("3")==0) + strm_config.video.level=V4L2_MPEG_VIDEO_H264_LEVEL_3_0; + else if(cfg->video_config().encoder().level().compare("3.1")==0) + strm_config.video.level=V4L2_MPEG_VIDEO_H264_LEVEL_3_1; + else if(cfg->video_config().encoder().level().compare("3.2")==0) + strm_config.video.level=V4L2_MPEG_VIDEO_H264_LEVEL_3_2; + else if(cfg->video_config().encoder().level().compare("4")==0) + strm_config.video.level=V4L2_MPEG_VIDEO_H264_LEVEL_4_0; + else + throw CException(_HERE_,"H264 level not supported"); + if(cfg->video_config().encoder().profile().compare("baseline")==0) + { + strm_config.video.v4l2_profile=V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; + strm_config.video.av_profile=FF_PROFILE_H264_BASELINE; + } + else if(cfg->video_config().encoder().profile().compare("const_baseline")==0) + { + strm_config.video.v4l2_profile=V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE; + strm_config.video.av_profile=FF_PROFILE_H264_CONSTRAINED_BASELINE; + } + else if(cfg->video_config().encoder().profile().compare("main")==0) + { + strm_config.video.v4l2_profile=V4L2_MPEG_VIDEO_H264_PROFILE_MAIN; + strm_config.video.av_profile=FF_PROFILE_H264_MAIN; + } + else if(cfg->video_config().encoder().profile().compare("high")==0) + { + strm_config.video.v4l2_profile=V4L2_MPEG_VIDEO_H264_PROFILE_HIGH; + strm_config.video.av_profile=FF_PROFILE_H264_HIGH; + } + else + throw CException(_HERE_,"H264 v4l2_profile not supported"); } - else if(cfg->video_config().encoder().profile().compare("high")==0) + else + strm_config.video_enable=false; + if(cfg->audio_enable()) { - strm_config.video.v4l2_profile=V4L2_MPEG_VIDEO_H264_PROFILE_HIGH; - strm_config.video.av_profile=FF_PROFILE_H264_HIGH; + strm_config.audio_enable=true; + // load audio configuration + std::copy(cfg->audio_config().audio_device().begin(),cfg->audio_config().audio_device().end(),strm_config.audio.device); + strm_config.audio.device[cfg->audio_config().audio_device().size()]='\0'; + strm_config.audio.num_channels=cfg->audio_config().num_channels(); + if(cfg->audio_config().num_channels()==1) + strm_config.audio.num_channels=1; + else if(cfg->audio_config().num_channels()==2) + strm_config.audio.num_channels=2; + else + throw CException(_HERE_,"Audio channels not supported"); + strm_config.audio.sample_rate=cfg->audio_config().sample_rate(); + if(cfg->audio_config().sample_format()=="U8") + strm_config.audio.format=paUInt8; + else if(cfg->audio_config().sample_format()=="S16") + strm_config.audio.format=paInt16; + else if(cfg->audio_config().sample_format()=="S32") + strm_config.audio.format=paInt32; + else if(cfg->audio_config().sample_format()=="F32") + strm_config.audio.format=paFloat32; + else + throw CException(_HERE_,"Audio format not supported"); + strm_config.audio.bitrate=cfg->audio_config().bitrate(); } else - throw CException(_HERE_,"H264 v4l2_profile not supported"); - // load audio configuration - std::copy(cfg->audio_config().audio_device().begin(),cfg->audio_config().audio_device().end(),strm_config.audio.device); - strm_config.audio.device[cfg->audio_config().audio_device().size()]='\0'; - strm_config.audio.num_channels=cfg->audio_config().num_channels(); - if(cfg->audio_config().num_channels()==1) - strm_config.audio.num_channels=1; - else if(cfg->audio_config().num_channels()==2) - strm_config.audio.num_channels=2; - else - throw CException(_HERE_,"Audio channels not supported"); - strm_config.audio.sample_rate=cfg->audio_config().sample_rate(); - if(cfg->audio_config().sample_format()=="U8") - strm_config.audio.format=paUInt8; - else if(cfg->audio_config().sample_format()=="S16") - strm_config.audio.format=paInt16; - else if(cfg->audio_config().sample_format()=="S32") - strm_config.audio.format=paInt32; - else if(cfg->audio_config().sample_format()=="F32") - strm_config.audio.format=paFloat32; - else - throw CException(_HERE_,"Audio format not supported"); - strm_config.audio.bitrate=cfg->audio_config().bitrate(); + strm_config.audio_enable=false; // load the SRT configuration if(cfg->srt_ip().present()) { @@ -1379,7 +1508,6 @@ void CStreamSrtSource::read_config(const std::string &filename,TStreamConfig &st } else throw CException(_HERE_,"The configuration file does not exist"); - } void CStreamSrtSource::write_config(const std::string &filename,TStreamConfig &strm_config) @@ -1426,6 +1554,8 @@ void CStreamSrtSource::write_config(const std::string &filename,TStreamConfig &s if(strm_config.video.format==V4L2_PIX_FMT_YUV420) video_format="YUV420"; + else if(strm_config.video.format==V4L2_PIX_FMT_RGB24) + video_format="RGB24"; else if(strm_config.video.format==V4L2_PIX_FMT_H264) video_format="H264"; std::cout << strm_config.video.format << "," << video_format << std::endl; @@ -1441,7 +1571,7 @@ void CStreamSrtSource::write_config(const std::string &filename,TStreamConfig &s audio_format="F32"; audio_config_t audio_config((char *)strm_config.audio.device,strm_config.audio.num_channels,strm_config.audio.sample_rate,audio_format,strm_config.audio.bitrate); - stream_config_t config(video_config,audio_config,strm_config.srt_port); + stream_config_t config(strm_config.video_enable,video_config,strm_config.audio_enable,audio_config,strm_config.srt_port); config.srt_ip(""); try{ diff --git a/src/xml/stream_config.xml b/src/xml/stream1_config.xml similarity index 86% rename from src/xml/stream_config.xml rename to src/xml/stream1_config.xml index c2ca482c3f8a88978f018a37903671caf33c0d9a..3ff549ab51f5fac527c00b53a70c4c9f5fd3b404 100644 --- a/src/xml/stream_config.xml +++ b/src/xml/stream1_config.xml @@ -1,11 +1,12 @@ <?xml version="1.0" encoding="UTF-8" standalone="no" ?> <stream_config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="stream_config_cfg_file.xsd"> + <video_enable>true</video_enable> <video_config> <video_device>/dev/video0</video_device> <width>1280</width> - <height>720</height> + <height>960</height> <framerate>30</framerate> - <format>YUV420</format> + <format>RGB24</format> <encoder> <bitrate>6000000</bitrate> <variable_bitrate>true</variable_bitrate> @@ -14,6 +15,7 @@ <profile>high</profile> </encoder> </video_config> + <audio_enable>false</audio_enable> <audio_config> <audio_device>hw:0,0</audio_device> <num_channels>2</num_channels> diff --git a/src/xml/stream2_config.xml b/src/xml/stream2_config.xml new file mode 100644 index 0000000000000000000000000000000000000000..66570efd247b719709bf48b41b81d8951a03f48d --- /dev/null +++ b/src/xml/stream2_config.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no" ?> +<stream_config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="stream_config_cfg_file.xsd"> + <video_enable>true</video_enable> + <video_config> + <video_device>/dev/video1</video_device> + <width>1024</width> + <height>768</height> + <framerate>30</framerate> + <format>RGB24</format> + <encoder> + <bitrate>6000000</bitrate> + <variable_bitrate>true</variable_bitrate> + <i_frame_period>10</i_frame_period> + <level>4</level> + <profile>high</profile> + </encoder> + </video_config> + <audio_enable>false</audio_enable> + <audio_config> + <audio_device>hw:0,0</audio_device> + <num_channels>2</num_channels> + <sample_rate>48000</sample_rate> + <sample_format>S32</sample_format> + <bitrate>32000</bitrate> + </audio_config> + <srt_ip/> + <srt_port>9999</srt_port> +</stream_config> diff --git a/src/xml/stream_config_cfg_file.xsd b/src/xml/stream_config_cfg_file.xsd index 23802ae45111d2cfa9cef271e2b19ce94cc74cc4..ba6b97114dfdac82df06f673bdf9b83ba7e589e9 100644 --- a/src/xml/stream_config_cfg_file.xsd +++ b/src/xml/stream_config_cfg_file.xsd @@ -57,8 +57,12 @@ <xsd:complexType name="stream_config_t"> <xsd:sequence> + <xsd:element name="video_enable" type="xsd:boolean"> + </xsd:element> <xsd:element name="video_config" type="video_config_t"> </xsd:element> + <xsd:element name="audio_enable" type="xsd:boolean"> + </xsd:element> <xsd:element name="audio_config" type="audio_config_t"> </xsd:element> <xsd:element name="srt_ip" type="IPv4Address" minOccurs="0">