From 5c303aeb9281a9dae0a6599b05f7613704fada58 Mon Sep 17 00:00:00 2001 From: Gee He <119773490+gehee@users.noreply.github.com> Date: Wed, 26 Jun 2024 22:55:49 -0400 Subject: [PATCH] DVR now has its own thread so it does not impact main video screen --- CMakeLists.txt | 0 fpvue_config.h | 2 +- main.cpp | 89 ++++++++++++++++++++++++++++++++++++++++---------- minimp4.h | 20 +++--------- 4 files changed, 77 insertions(+), 34 deletions(-) mode change 100755 => 100644 CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt old mode 100755 new mode 100644 diff --git a/fpvue_config.h b/fpvue_config.h index 2a55a74..5cdf908 100644 --- a/fpvue_config.h +++ b/fpvue_config.h @@ -1,2 +1,2 @@ #define fpvue_VERSION_MAJOR 0 -#define fpvue_VERSION_MINOR 13 +#define fpvue_VERSION_MINOR 13.1 diff --git a/main.cpp b/main.cpp index c09962e..f6d6737 100644 --- a/main.cpp +++ b/main.cpp @@ -15,6 +15,10 @@ #include #include #include +#include +#include +#include +#include #include #include @@ -74,6 +78,8 @@ pthread_mutex_t video_mutex; pthread_cond_t video_cond; int video_zpos = 1; +int video_framerate = -1; +int mp4_fragmentation_mode = 0; VideoCodec codec = VideoCodec::H265; FILE *dvr_file = NULL; @@ -171,7 +177,7 @@ void init_buffer(MppFrame frame) { // dvr setup if (dvr_file != NULL){ printf("setting up dvr and mux\n"); - mux = MP4E_open(0 /*sequential_mode*/, 0 /*fragmentation_mode*/, dvr_file, write_callback); + mux = MP4E_open(0 /*sequential_mode*/, mp4_fragmentation_mode, dvr_file, write_callback); if (MP4E_STATUS_OK != mp4_h26x_write_init(&mp4wr, mux, output_list->video_frm_width, output_list->video_frm_height, codec==VideoCodec::H265)) { printf("error: mp4_h26x_write_init failed\n"); @@ -349,6 +355,42 @@ void sig_handler(int signum) osd_thread_signal++; } +std::queue>> dvrQueue; +std::mutex mtx; +std::condition_variable cv; + +void *__DVR_THREAD__(void *param) { + while (true) { + std::unique_lock lock(mtx); + cv.wait(lock, [dvrQueue, signal_flag] { return !dvrQueue.empty() || signal_flag; }); + if (signal_flag && dvrQueue.empty()) { + break; + } + if (!dvrQueue.empty()) { + std::shared_ptr> frame = dvrQueue.front(); + dvrQueue.pop(); + lock.unlock(); + // Process the frame + auto res = mp4_h26x_write_nal(&mp4wr, frame->data(), frame->size(), 90000/video_framerate); + if (!(MP4E_STATUS_OK == res || MP4E_STATUS_BAD_ARGUMENTS == res)) { + printf("mp4_h26x_write_nal failed with error %d\n", res); + } + } + } + MP4E_close(mux); + mp4_h26x_write_close(&mp4wr); + fclose(dvr_file); + printf("DVR thread done.\n"); +} + +void enqueueDvrPacket(std::shared_ptr> frame) { + { + std::lock_guard lock(mtx); + dvrQueue.push(frame); + } + cv.notify_one(); +} + int decoder_stalled_count=0; bool feed_packet_to_decoder(MppPacket *packet,void* data_p,int data_len){ mpp_packet_set_data(packet, data_p); @@ -369,14 +411,6 @@ bool feed_packet_to_decoder(MppPacket *packet,void* data_p,int data_len){ } usleep(2 * 1000); } - - if (dvr_file!=NULL && mux != NULL){ - int res = mp4_h26x_write_nal(&mp4wr, (const unsigned char*)data_p, data_len, -1); - // if (MP4E_STATUS_OK != res) { - // // This is expected to fail until a keyframe arrives. - // //printf("mp4_h26x_write_nal failed with %d", res); - // } - } return true; } @@ -401,6 +435,8 @@ void read_gstreamerpipe_stream(MppPacket *packet, int gst_udp_port, const VideoC bytes_received = 0; } feed_packet_to_decoder(packet,frame->data(),frame->size()); + + enqueueDvrPacket(frame); }; receiver.start_receiving(cb); while (!signal_flag){ @@ -482,6 +518,10 @@ void printHelp() { "\n" " --dvr - Save the video feed (no osd) to the provided filename\n" "\n" + " --dvr-framerate - Force the dvr framerate fro smoother dvr\n" + "\n" + " --dvr-fmp4 - Save the video feed as a fragmented mp4\n" + "\n" " --screen-mode - Override default screen mode. ex:1920x1080@120\n" "\n", fpvue_VERSION_MAJOR , fpvue_VERSION_MINOR ); @@ -526,6 +566,16 @@ int main(int argc, char **argv) continue; } + __OnArgument("--dvr-framerate") { + video_framerate = atoi(__ArgValue); + continue; + } + + __OnArgument("--dvr-fmp4") { + mp4_fragmentation_mode = 1; + continue; + } + __OnArgument("--mavlink-port") { mavlink_port = atoi(__ArgValue); continue; @@ -591,6 +641,11 @@ int main(int argc, char **argv) __EndParseConsoleArguments__ + if (dvr_file != NULL && video_framerate < 0 ) { + printf("--dvr-framerate must be provided when dvr is enabled.\n"); + return 0; + } + printf("FPVue Rockchip %d.%d\n", fpvue_VERSION_MAJOR , fpvue_VERSION_MINOR); if (enable_osd == 0 ) { @@ -646,7 +701,10 @@ int main(int argc, char **argv) ret = pthread_cond_init(&video_cond, NULL); assert(!ret); - pthread_t tid_frame, tid_display, tid_osd, tid_mavlink; + pthread_t tid_frame, tid_display, tid_osd, tid_mavlink, tid_dvr; + if (dvr_file != NULL) { + ret = pthread_create(&tid_dvr, NULL, __DVR_THREAD__, NULL); + } ret = pthread_create(&tid_frame, NULL, __FRAME_THREAD__, NULL); assert(!ret); ret = pthread_create(&tid_display, NULL, __DISPLAY_THREAD__, NULL); @@ -666,13 +724,6 @@ int main(int argc, char **argv) ////////////////////////////////////////////// MAIN LOOP read_gstreamerpipe_stream((void**)packet, listen_port, codec); - // Close dvr file - if (dvr_file != NULL) { - MP4E_close(mux); - mp4_h26x_write_close(&mp4wr); - fclose(dvr_file); - } - ////////////////////////////////////////////// MPI CLEANUP ret = pthread_join(tid_frame, NULL); @@ -701,6 +752,10 @@ int main(int argc, char **argv) ret = pthread_join(tid_osd, NULL); assert(!ret); } + if (dvr_file != NULL ){ + ret = pthread_join(tid_dvr, NULL); + assert(!ret); + } ret = mpi.mpi->reset(mpi.ctx); assert(!ret); diff --git a/minimp4.h b/minimp4.h index da45839..420f63e 100644 --- a/minimp4.h +++ b/minimp4.h @@ -2322,11 +2322,10 @@ static int mp4_h265_write_nal(mp4_h26x_writer_t *h, const unsigned char *nal, in uint64_t last_ms = 0; -int mp4_h26x_write_nal(mp4_h26x_writer_t *h, const unsigned char *nal, int length, int duration) +int mp4_h26x_write_nal(mp4_h26x_writer_t *h, const unsigned char *nal, int length, int timeStamp90kHz_next) { const unsigned char *eof = nal + length; int payload_type, sizeof_nal, err = MP4E_STATUS_OK; - uint64_t cur_time = get_time_ms(); for (;;nal++) { #if MINIMP4_TRANSCODE_SPS_ID @@ -2337,11 +2336,7 @@ int mp4_h26x_write_nal(mp4_h26x_writer_t *h, const unsigned char *nal, int lengt break; if (h->is_hevc) { - if (duration==-1){ - int dur = cur_time - last_ms; - duration = (dur/1000.0)*90000.0; - } - ERR(mp4_h265_write_nal(h, nal, sizeof_nal, duration)); + ERR(mp4_h265_write_nal(h, nal, sizeof_nal, timeStamp90kHz_next)); continue; } payload_type = nal[0] & 31; @@ -2407,7 +2402,7 @@ int mp4_h26x_write_nal(mp4_h26x_writer_t *h, const unsigned char *nal, int lengt sample_kind = MP4E_SAMPLE_CONTINUATION; else if (payload_type == 5) sample_kind = MP4E_SAMPLE_RANDOM_ACCESS; - err = MP4E_put_sample(h->mux, h->mux_track_id, nal2, sizeof_nal, duration, sample_kind); + err = MP4E_put_sample(h->mux, h->mux_track_id, nal2, sizeof_nal, timeStamp90kHz_next, sample_kind); } break; } @@ -2451,11 +2446,7 @@ int mp4_h26x_write_nal(mp4_h26x_writer_t *h, const unsigned char *nal, int lengt sample_kind = MP4E_SAMPLE_CONTINUATION; else if (payload_type == 5) sample_kind = MP4E_SAMPLE_RANDOM_ACCESS; - if (duration==-1){ - int dur = cur_time - last_ms; - duration = (dur/1000)*90000; - } - err = MP4E_put_sample(h->mux, h->mux_track_id, tmp, 4 + sizeof_nal, duration, sample_kind); + err = MP4E_put_sample(h->mux, h->mux_track_id, tmp, 4 + sizeof_nal, timeStamp90kHz_next, sample_kind); free(tmp); } break; @@ -2464,9 +2455,6 @@ int mp4_h26x_write_nal(mp4_h26x_writer_t *h, const unsigned char *nal, int lengt if (err) break; } - if (payload_type != 9) { - last_ms = cur_time; - } return err; }