From 616789a33c889fbdb430ade2ff35d0a7acdba550 Mon Sep 17 00:00:00 2001 From: Michel Promonet Date: Sun, 25 Jan 2015 18:31:18 +0000 Subject: [PATCH 1/2] add a separate thread to read V4L2 device + change default behavior to mmap+thread --- CMakeLists.txt | 4 +++ README.md | 6 ++-- inc/V4l2DeviceSource.h | 9 ++++-- src/V4l2DeviceSource.cpp | 62 ++++++++++++++++++++++++++++++++++------ src/main.cpp | 17 ++++++----- 5 files changed, 78 insertions(+), 20 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b6d674e..678408a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,10 @@ include_directories("${PROJECT_BINARY_DIR}/v4l2wrapper/inc") aux_source_directory(v4l2wrapper/src SRC_FILES) add_executable(${PROJECT_NAME} ${SRC_FILES}) +#pthread +find_package (Threads) +target_link_libraries (${PROJECT_NAME} ${CMAKE_THREAD_LIBS_INIT}) + # LOG4CPP find_path(LOG4CPP_INCLUDE_DIR log4cpp/Category.hh) if (NOT LOG4CPP_INCLUDE_DIR) diff --git a/README.md b/README.md index 4f73d48..6cc94ae 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ This RTSP server works on Raspberry Pi using : Usage ----- - ./h264_v4l2_rtspserver [-v[v]][-m] [-P RTSP port][-P RTSP/HTTP port][-Q queueSize] [-M] [-W width] [-H height] [-F fps] [-O file] [device] + ./h264_v4l2_rtspserver [-v[v]][-m] [-P RTSP port][-P RTSP/HTTP port][-Q queueSize] [-M] [-t] [-W width] [-H height] [-F fps] [-O file] [device] -v : verbose -vv : very verbose -Q length: Number of frame queue (default 10) @@ -63,8 +63,10 @@ Usage -P port : RTSP port (default 8554) -H port : RTSP over HTTP port (default 0) V4L2 options : - -M : V4L2 capture using memory mapped buffers (default use read interface) + -M 0/1 : V4L2 capture 0:read interface /1:memory mapped buffers (default is 1) + -t 0/1 : V4L2 capture 0:read in live555 mainloop /1:in a thread (default is 1) -F fps : V4L2 capture framerate (default 25) -W width : V4L2 capture width (default 640) -H height: V4L2 capture height (default 480) device : V4L2 capture device (default /dev/video0) + diff --git a/inc/V4l2DeviceSource.h b/inc/V4l2DeviceSource.h index c4a16fa..3e8707f 100644 --- a/inc/V4l2DeviceSource.h +++ b/inc/V4l2DeviceSource.h @@ -64,18 +64,20 @@ class V4L2DeviceSource: public FramedSource }; public: - static V4L2DeviceSource* createNew(UsageEnvironment& env, V4L2DeviceParameters params, V4l2Capture * device, int outputFd, unsigned int queueSize, int verbose) ; + static V4L2DeviceSource* createNew(UsageEnvironment& env, V4L2DeviceParameters params, V4l2Capture * device, int outputFd, unsigned int queueSize, int verbose, bool useThread) ; std::string getAuxLine() { return m_auxLine; }; protected: - V4L2DeviceSource(UsageEnvironment& env, V4L2DeviceParameters params, V4l2Capture * device, int outputFd, unsigned int queueSize, int verbose); + V4L2DeviceSource(UsageEnvironment& env, V4L2DeviceParameters params, V4l2Capture * device, int outputFd, unsigned int queueSize, int verbose, bool useThread); virtual ~V4L2DeviceSource(); protected: + static void* threadStub(void* clientData) { return ((V4L2DeviceSource*) clientData)->thread();}; + void* thread(); static void deliverFrameStub(void* clientData) {((V4L2DeviceSource*) clientData)->deliverFrame();}; void deliverFrame(); static void incomingPacketHandlerStub(void* clientData, int mask) { ((V4L2DeviceSource*) clientData)->getNextFrame(); }; - void getNextFrame(); + int getNextFrame(); bool processConfigrationFrame(char * frame, int frameSize); void processFrame(char * frame, int &frameSize, const timeval &ref); void queueFrame(char * frame, int frameSize, const timeval &tv); @@ -95,6 +97,7 @@ class V4L2DeviceSource: public FramedSource V4l2Capture * m_device; unsigned int m_queueSize; int m_verbose; + pthread_t m_thid; }; #endif diff --git a/src/V4l2DeviceSource.cpp b/src/V4l2DeviceSource.cpp index 20c4e54..4f4653a 100644 --- a/src/V4l2DeviceSource.cpp +++ b/src/V4l2DeviceSource.cpp @@ -48,18 +48,18 @@ int V4L2DeviceSource::Stats::notify(int tv_sec, int framesize, int verbose) // --------------------------------- // V4L2 FramedSource // --------------------------------- -V4L2DeviceSource* V4L2DeviceSource::createNew(UsageEnvironment& env, V4L2DeviceParameters params, V4l2Capture * device, int outputFd, unsigned int queueSize, int verbose) +V4L2DeviceSource* V4L2DeviceSource::createNew(UsageEnvironment& env, V4L2DeviceParameters params, V4l2Capture * device, int outputFd, unsigned int queueSize, int verbose, bool useThread) { V4L2DeviceSource* source = NULL; if (device) { - source = new V4L2DeviceSource(env, params, device, outputFd, queueSize, verbose); + source = new V4L2DeviceSource(env, params, device, outputFd, queueSize, verbose, useThread); } return source; } // Constructor -V4L2DeviceSource::V4L2DeviceSource(UsageEnvironment& env, V4L2DeviceParameters params, V4l2Capture * device, int outputFd, unsigned int queueSize, int verbose) +V4L2DeviceSource::V4L2DeviceSource(UsageEnvironment& env, V4L2DeviceParameters params, V4l2Capture * device, int outputFd, unsigned int queueSize, int verbose, bool useThread) : FramedSource(env), m_params(params), m_in("in"), @@ -72,17 +72,61 @@ V4L2DeviceSource::V4L2DeviceSource(UsageEnvironment& env, V4L2DeviceParameters p m_eventTriggerId = envir().taskScheduler().createEventTrigger(V4L2DeviceSource::deliverFrameStub); if (m_device) { - envir().taskScheduler().turnOnBackgroundReadHandling( m_device->getFd(), V4L2DeviceSource::incomingPacketHandlerStub, this); + if (useThread) + { + pthread_create(&m_thid, NULL, threadStub, this); + } + else + { + envir().taskScheduler().turnOnBackgroundReadHandling( m_device->getFd(), V4L2DeviceSource::incomingPacketHandlerStub, this); + } } } // Destructor V4L2DeviceSource::~V4L2DeviceSource() -{ +{ envir().taskScheduler().deleteEventTrigger(m_eventTriggerId); m_device->captureStop(); + pthread_join(m_thid, NULL); } - + +// thread mainloop +void* V4L2DeviceSource::thread() +{ + int stop=0; + fd_set fdset; + FD_ZERO(&fdset); + timeval tv; + + envir() << "begin thread\n"; + while (!stop) + { + FD_SET(m_device->getFd(), &fdset); + tv.tv_sec=1; + tv.tv_usec=0; + int ret = select(m_device->getFd()+1, &fdset, NULL, NULL, &tv); + if (ret == 1) + { + if (FD_ISSET(m_device->getFd(), &fdset)) + { + if (this->getNextFrame() <= 0) + { + envir() << "error:" << strerror(errno) << "\n"; + stop=1; + } + } + } + else if (ret == -1) + { + envir() << "stop " << strerror(errno) << "\n"; + stop=1; + } + } + envir() << "end thread\n"; + return NULL; +} + // getting FrameSource callback void V4L2DeviceSource::doGetNextFrame() { @@ -149,9 +193,9 @@ void V4L2DeviceSource::deliverFrame() FramedSource::afterGetting(this); } } - + // FrameSource callback on read event -void V4L2DeviceSource::getNextFrame() +int V4L2DeviceSource::getNextFrame() { char* buffer = new char[m_device->getBufferSize()]; timeval ref; @@ -168,6 +212,7 @@ void V4L2DeviceSource::getNextFrame() { envir() << "V4L2DeviceSource::getNextFrame no data errno:" << errno << " " << strerror(errno) << "\n"; delete [] buffer; + handleClosure(this); } else { @@ -190,6 +235,7 @@ void V4L2DeviceSource::getNextFrame() delete [] buffer; } } + return frameSize; } bool V4L2DeviceSource::processConfigrationFrame(char * frame, int frameSize) diff --git a/src/main.cpp b/src/main.cpp index 83828c1..86c5509 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -140,7 +140,7 @@ int createOutput(const std::string & outputFile, int inputFd) } return outputFd; } - + // ----------------------------------------- // entry point // ----------------------------------------- @@ -162,13 +162,14 @@ int main(int argc, char** argv) bool multicast = false; int verbose = 0; std::string outputFile; - bool useMmap = false; + bool useMmap = true; std::string url = "unicast"; std::string murl = "multicast"; + bool useThread = true; // decode parameters int c = 0; - while ((c = getopt (argc, argv, "hW:H:Q:P:F:v::O:T:m:u:M")) != -1) + while ((c = getopt (argc, argv, "hW:H:Q:P:F:v::O:T:m:u:M:t:")) != -1) { switch (c) { @@ -181,13 +182,14 @@ int main(int argc, char** argv) case 'P': rtspPort = atoi(optarg); break; case 'T': rtspOverHTTPPort = atoi(optarg); break; case 'F': fps = atoi(optarg); break; - case 'M': useMmap = true; break; + case 'M': useMmap = atoi(optarg); break; + case 't': useThread = atoi(optarg); break; case 'u': url = optarg; break; case 'h': default: { - std::cout << argv[0] << " [-v[v]][-m] [-P RTSP port][-P RTSP/HTTP port][-Q queueSize] [-M] [-W width] [-H height] [-F fps] [-O file] [device]" << std::endl; + std::cout << argv[0] << " [-v[v]][-m] [-P RTSP port][-P RTSP/HTTP port][-Q queueSize] [-M] [-t] [-W width] [-H height] [-F fps] [-O file] [device]" << std::endl; std::cout << "\t -v : verbose" << std::endl; std::cout << "\t -vv : very verbose" << std::endl; std::cout << "\t -Q length: Number of frame queue (default "<< queueSize << ")" << std::endl; @@ -198,7 +200,8 @@ int main(int argc, char** argv) std::cout << "\t -P port : RTSP port (default "<< rtspPort << ")" << std::endl; std::cout << "\t -H port : RTSP over HTTP port (default "<< rtspOverHTTPPort << ")" << std::endl; std::cout << "\t V4L2 options :" << std::endl; - std::cout << "\t -M : V4L2 capture using memory mapped buffers (default use read interface)" << std::endl; + std::cout << "\t -M 0/1 : V4L2 capture 0:read interface /1:memory mapped buffers (default is 1)" << std::endl; + std::cout << "\t -t 0/1 : V4L2 capture 0:read in live555 mainloop /1:in a thread (default is 1)" << std::endl; std::cout << "\t -F fps : V4L2 capture framerate (default "<< fps << ")" << std::endl; std::cout << "\t -W width : V4L2 capture width (default "<< width << ")" << std::endl; std::cout << "\t -H height: V4L2 capture height (default "<< height << ")" << std::endl; @@ -241,7 +244,7 @@ int main(int argc, char** argv) int outputFd = createOutput(outputFile, videoCapture->getFd()); LOG(NOTICE) << "Start V4L2 Capture..." << dev_name; videoCapture->captureStart(); - V4L2DeviceSource* videoES = V4L2DeviceSource::createNew(*env, param, videoCapture, outputFd, queueSize, verbose); + V4L2DeviceSource* videoES = V4L2DeviceSource::createNew(*env, param, videoCapture, outputFd, queueSize, verbose, useThread); if (videoES == NULL) { LOG(FATAL) << "Unable to create source for device " << dev_name; From 888fff8e7e38311962d5b6112a11d91ccba42ba4 Mon Sep 17 00:00:00 2001 From: Michel Promonet Date: Sun, 25 Jan 2015 22:55:04 +0100 Subject: [PATCH 2/2] include git submodule stuff in CMakeLists.txt --- CMakeLists.txt | 24 ++++++++++++++++++++++-- README.md | 2 -- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 678408a..98e6bb9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,12 +8,32 @@ set(CMAKE_BUILD_TYPE DEBUG) set(CMAKE_C_FLAGS "-Wall") set(CMAKE_CXX_FLAGS "-Wall") +add_custom_target(git_update + COMMAND git submodule init + COMMAND git submodule update + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + ) + # define executable to build include_directories("${PROJECT_BINARY_DIR}/inc") aux_source_directory(src SRC_FILES) -include_directories("${PROJECT_BINARY_DIR}/v4l2wrapper/inc") -aux_source_directory(v4l2wrapper/src SRC_FILES) add_executable(${PROJECT_NAME} ${SRC_FILES}) +add_dependencies(${PROJECT_NAME} git_update) + +# v4l2wrapper +include_directories("${PROJECT_BINARY_DIR}/v4l2wrapper/inc") +set_source_files_properties(${PROJECT_BINARY_DIR}/v4l2wrapper/src/V4l2Capture.cpp PROPERTIES GENERATED 1) +set_source_files_properties(${PROJECT_BINARY_DIR}/v4l2wrapper/src/V4l2MmapCapture.cpp PROPERTIES GENERATED 1) +set_source_files_properties(${PROJECT_BINARY_DIR}/v4l2wrapper/src/V4l2ReadCapture.cpp PROPERTIES GENERATED 1) + +add_library(v4l2wrapper + STATIC + v4l2wrapper/src/V4l2Capture.cpp + v4l2wrapper/src/V4l2MmapCapture.cpp + v4l2wrapper/src/V4l2ReadCapture.cpp + ) +target_link_libraries(${PROJECT_NAME} v4l2wrapper) +add_dependencies(v4l2wrapper git_update) #pthread find_package (Threads) diff --git a/README.md b/README.md index 6cc94ae..8e14496 100644 --- a/README.md +++ b/README.md @@ -27,8 +27,6 @@ Dependencies Build ------- - git submodule init - git submodule update cmake . make