2 * Common Source Code Project for Qt : movie saver.
3 * (C) 2016 K.Ohta <whatisthis.sowhat _at_ gmail.com>
5 * History: May 27, 2016 : Initial. This refer from avidemux 2.5.6 .
9 #include "movie_saver.h"
12 #include "csp_logger.h"
14 MOVIE_SAVER::MOVIE_SAVER(int width, int height, int fps, OSD *osd, config_t *cfg) : QThread(0)
16 buffer_size=8 * 1024 * 224;
25 p_logger = osd->get_logger();
28 audio_sample_rate = 48000;
29 #if defined(USE_MOVIE_SAVER)
30 memset(audio_frame_buf, 0x00, sizeof(audio_frame_buf));
31 memset(video_frame_buf, 0x00, sizeof(video_frame_buf));
32 output_context = NULL;
34 audio_codec = video_codec = NULL;
35 raw_options_list = NULL;
37 memset(&video_st, 0x00, sizeof(video_st));
38 memset(&audio_st, 0x00, sizeof(audio_st));
41 output_context = NULL;
43 audio_data_queue.clear();
44 video_data_queue.clear();
46 do_reset_encoder_options();
48 //video_queue_mutex = new QMutex(QMutex::Recursive);
53 video_bit_rate = 1500 * 1000;
54 audio_bit_rate = 160 * 1000;
55 video_geometry = QSize(640, 480);
56 video_encode_threads = 4;
57 // audio_enqueue_count = 0;
65 MOVIE_SAVER::~MOVIE_SAVER()
68 if(recording) do_close_main();
71 QString MOVIE_SAVER::get_avio_version()
73 #if defined(__LIBAVIO_VERSION)
74 return QString::fromUtf8(__LIBAVIO_VERSION);
76 return QString::fromUtf8("\0");
80 QString MOVIE_SAVER::ts2str(int64_t ts)
82 #if defined(USE_LIBAV)
83 char buffer[AV_TS_MAX_STRING_SIZE + 16];
84 memset(buffer, 0x00, sizeof(buffer));
85 return QString::fromLocal8Bit(av_ts_make_string(buffer, ts));
87 return QString::fromUtf8("");
91 QString MOVIE_SAVER::ts2timestr(int64_t ts, void *timebase)
93 #if defined(USE_LIBAV)
94 char buffer[AV_TS_MAX_STRING_SIZE + 16];
95 AVRational *tb = (AVRational *)timebase;
96 memset(buffer, 0x00, sizeof(buffer));
97 return QString::fromLocal8Bit(av_ts_make_time_string(buffer, ts, tb));
99 return QString::fromUtf8("");
103 QString MOVIE_SAVER::err2str(int errnum)
105 #if defined(USE_LIBAV)
106 char buffer[AV_TS_MAX_STRING_SIZE + 16];
107 memset(buffer, 0x00, sizeof(buffer));
108 return QString::fromLocal8Bit(av_make_error_string(buffer, sizeof(buffer), errnum));
110 return QString::fromUtf8("");
114 //void MOVIE_SAVER::log_packet(const AVFormatContext *fmt_ctx, const AVPacket *pkt)
115 void MOVIE_SAVER::log_packet(const void *_fmt_ctx, const void *_pkt)
117 #if defined(USE_LIBAV)
118 const AVFormatContext *fmt_ctx = (const AVFormatContext *)_fmt_ctx;
119 const AVPacket *pkt = (const AVPacket *)_pkt;
120 AVRational *time_base = &fmt_ctx->streams[pkt->stream_index]->time_base;
122 p_logger->debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "pts:%s pts_time:%s dts:%s dts_time:%s duration:%s duration_time:%s stream_index:%d\n",
123 ts2str(pkt->pts).toLocal8Bit().constData(),
124 ts2timestr(pkt->pts, (void *)time_base).toLocal8Bit().constData(),
125 ts2str(pkt->dts).toLocal8Bit().constData(),
126 ts2timestr(pkt->dts, (void *)time_base).toLocal8Bit().constData(),
127 ts2str(pkt->duration).toLocal8Bit().constData(),
128 ts2timestr(pkt->duration, (void *)time_base).toLocal8Bit().constData(),
134 void MOVIE_SAVER::enqueue_video(int frames, int width, int height, QImage *p)
136 #if defined(USE_MOVIE_SAVER)
137 if(!recording) return;
138 if(p == NULL) return;
140 if((width <= 0) || (height <= 0) || (frames <= 0)) return;
141 if((p->width() <= 0) || (p->height() <= 0)) return;
142 VIDEO_DATA *px = new VIDEO_DATA(frames, width, height, p);
143 //p_logger->debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "Movie: Enqueue video data %dx%d %d bytes %dx%d %d frames", width, height, p->byteCount(), p->width(), p->height(), frames);
144 //video_queue_mutex->lock();
145 video_data_queue.enqueue(px);
146 //video_queue_mutex->unlock();
150 bool MOVIE_SAVER::dequeue_video(uint32_t *p)
152 //if(!recording) return false;
153 if(p == NULL) return false;
155 VIDEO_DATA *pp = video_data_queue.dequeue();
156 #if defined(USE_MOVIE_SAVER)
157 if(pp == NULL) return false;
160 QImage *pp_i = &(pp->frame_data);
161 left_frames = pp->frames;
163 _height = pp->_height;
164 if((left_frames > 0) && (pp_i != NULL)){
165 for(y = 0; y < _height; y++) {
166 if(y >= pp_i->height()) break;
167 pq = (uint32_t *)(pp_i->constScanLine(y));
168 my_memcpy(&(p[_width * y]), pq, (((uint)_width * sizeof(uint32_t)) > pp_i->bytesPerLine()) ? pp_i->bytesPerLine() : _width * sizeof(uint32_t));
170 video_size = _width * y;
171 //p_logger->debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "Movie: Dequeue video data %d bytes", video_size);
176 if(pp != NULL) delete pp;
180 void MOVIE_SAVER::enqueue_audio(int16_t *p, int size)
182 #if defined(USE_MOVIE_SAVER)
183 if(!recording) return;
184 if(p == NULL) return;
186 QByteArray *pp = new QByteArray((const char *)p, size);
187 //p_logger->debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "Movie: Enqueue audio data %d bytes", size);
188 audio_data_queue.enqueue(pp);
189 // audio_enqueue_count++;
193 bool MOVIE_SAVER::dequeue_audio(int16_t *p)
195 //if(!recording) return false;
196 if(audio_data_queue.isEmpty()) return false;
197 if(p == NULL) return false;
199 QByteArray *pp = audio_data_queue.dequeue();
200 int16_t *q = (int16_t *)pp->constData();
202 #if defined(USE_MOVIE_SAVER)
203 if(pp == NULL) return false;
204 audio_size = pp->size();
205 if(audio_size <= 0) return false;
206 // I expect to use SIMD.
207 for(int i = 0; i < (int)(audio_size / sizeof(int16_t)); i++) {
214 //memcpy(p, pp->constData(), audio_size);
219 if(pp != NULL) delete pp;
224 void MOVIE_SAVER::run()
227 //p_logger->debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "MOVIE THREAD: Start");
229 int fps_wait = (int)((1000.0 / (double)rec_fps) / 4.0);
230 int tmp_wait = fps_wait;
231 bool need_audio_transcode = false;
232 bool need_video_transcode = false;
234 int64_t total_packets_written = 0;
237 volatile bool old_recording = false;
241 audio_frame_offset = 0;
250 if(!bRunThread) break;
252 //n_encode_video = n_encode_audio = 1;
256 audio_frame_offset = 0;
263 if(!do_open_main()) {
267 p_logger->debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "MOVIE/Saver: Start to recording.");
268 old_recording = true;
271 if(audio_remain <= 0) {
272 a_f = audio_data_queue.isEmpty();
274 dequeue_audio(audio_frame_buf);
275 audio_remain = audio_size;
277 need_audio_transcode = true;
281 v_f = video_data_queue.isEmpty();
284 if(left_frames <= 0) dequeue_video(video_frame_buf);
286 video_remain = video_size;
288 need_video_transcode = true;
291 if ((n_encode_video == 0) &&
292 ((n_encode_audio != 0) || av_compare_ts(video_st.next_pts, video_st.st->codec->time_base,
293 audio_st.next_pts, audio_st.st->codec->time_base) <= 0)) {
294 n_encode_video = write_video_frame();
295 if(n_encode_video < 0) {
296 p_logger->debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "MOVIE/Saver: Something wrong with encoding video.");
300 n_encode_audio = write_audio_frame();
301 if(n_encode_audio < 0) {
302 p_logger->debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "MOVIE/Saver: Something wrong with encoding audio.");
306 if (ret < 0 && ret != AVERROR_EOF) {
308 av_strerror(ret, errbuf, sizeof(errbuf));
309 p_logger->debug_log(CSP_LOG_INFO, CSP_LOG_TYPE_MOVIE_SAVER, "Error while filtering: %s\n", (const char *)errbuf);
313 /* dump report by using the output first video and audio streams */
314 //print_report(0, timer_start, cur_time);
317 need_video_transcode = need_audio_transcode = false;
318 old_recording = false;
322 //if(req_stop && a_f && v_f) req_close = true;
323 if(!bRunThread) break;
324 if(need_video_transcode || need_audio_transcode) {
325 need_video_transcode = need_audio_transcode = false;
328 if(fps_wait >= tmp_wait) {
329 this->msleep(tmp_wait);
332 this->msleep(fps_wait);
333 tmp_wait -= fps_wait;
336 fps_wait = (int)((1000.0 / (double)rec_fps) / 4.0);
342 old_recording = false;
348 old_recording = false;
350 p_logger->debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "MOVIE: Exit thread.");
357 bool MOVIE_SAVER::is_recording(void)
363 void MOVIE_SAVER::do_exit()
370 void MOVIE_SAVER::do_set_record_fps(int fps)
372 if((fps > 0) && (fps <= 75)) rec_fps = fps;
375 void MOVIE_SAVER::do_set_width(int width)
380 void MOVIE_SAVER::do_set_height(int height)
385 void MOVIE_SAVER::do_clear_options_list(void)
387 encode_opt_keys.clear();
388 encode_options.clear();
391 void MOVIE_SAVER::do_add_option(QString key, QString value)
393 if(key.isEmpty()) return;
394 encode_opt_keys.append(key);
395 if(value.isEmpty()) {
396 encode_options.append(QString::fromUtf8(""));
398 encode_options.append(value);
402 void MOVIE_SAVER::do_set_video_bitrate(int kbps)
404 if(kbps < 10) return;
405 video_bit_rate = kbps * 1000;
408 void MOVIE_SAVER::do_set_audio_bitrate(int kbps)
411 audio_bit_rate = kbps * 1000;
414 void MOVIE_SAVER::do_set_video_geometry(QSize geometry)
416 if(geometry.width() < 100) return;
417 if(geometry.height() < 80) return;
418 video_geometry = geometry;
421 void MOVIE_SAVER::do_set_video_threads(int threads)
423 video_encode_threads = threads;
426 void MOVIE_SAVER::do_reset_encoder_options(void)
428 encode_opt_keys.clear();
429 encode_options.clear();
431 do_add_option(QString::fromUtf8("c:v"), QString::fromUtf8("libx264"));
432 do_add_option(QString::fromUtf8("c:a"), QString::fromUtf8("aac"));
433 do_add_option(QString::fromUtf8("preset"), QString::fromUtf8("slow"));
434 do_add_option(QString::fromUtf8("tune"), QString::fromUtf8("grain"));
435 //do_add_option(QString::fromUtf8("crf"), QString::fromUtf8("20"));
436 do_add_option(QString::fromUtf8("qmin"), QString::fromUtf8("16"));
437 do_add_option(QString::fromUtf8("qmax"), QString::fromUtf8("24"));
439 do_add_option(QString::fromUtf8("qmax"), QString::fromUtf8("24"));