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 .
10 #include "./csp_avio_basic.h"
11 #include "movie_saver.h"
14 #include "csp_logger.h"
16 #if (LIBAVCODEC_VERSION_MAJOR > 56)
17 #define AVCODEC_UPPER_V56
20 MOVIE_SAVER::MOVIE_SAVER(int width, int height, int fps, OSD *osd, config_t *cfg) : QThread(0)
22 buffer_size=8 * 1024 * 224;
31 p_logger = osd->get_logger();
34 audio_sample_rate = 48000;
35 #if defined(USE_MOVIE_SAVER)
36 memset(audio_frame_buf, 0x00, sizeof(audio_frame_buf));
37 memset(video_frame_buf, 0x00, sizeof(video_frame_buf));
38 output_context = NULL;
40 audio_codec = video_codec = NULL;
41 raw_options_list = NULL;
43 memset(&video_st, 0x00, sizeof(video_st));
44 memset(&audio_st, 0x00, sizeof(audio_st));
47 output_context = NULL;
49 audio_data_queue.clear();
50 video_data_queue.clear();
52 do_reset_encoder_options();
54 //video_queue_mutex = new QMutex(QMutex::Recursive);
59 video_bit_rate = 1500 * 1000;
60 audio_bit_rate = 160 * 1000;
61 video_geometry = QSize(640, 480);
62 video_encode_threads = 4;
63 // audio_enqueue_count = 0;
71 MOVIE_SAVER::~MOVIE_SAVER()
74 if(recording) do_close_main();
77 QString MOVIE_SAVER::get_avio_version()
79 #if defined(__LIBAVIO_VERSION)
80 return QString::fromUtf8(__LIBAVIO_VERSION);
82 return QString::fromUtf8("\0");
86 QString MOVIE_SAVER::ts2str(int64_t ts)
88 #if defined(USE_LIBAV)
89 char buffer[AV_TS_MAX_STRING_SIZE + 16];
90 memset(buffer, 0x00, sizeof(buffer));
91 return QString::fromLocal8Bit(av_ts_make_string(buffer, ts));
93 return QString::fromUtf8("");
97 QString MOVIE_SAVER::ts2timestr(int64_t ts, void *timebase)
99 #if defined(USE_LIBAV)
100 char buffer[AV_TS_MAX_STRING_SIZE + 16];
101 AVRational *tb = (AVRational *)timebase;
102 memset(buffer, 0x00, sizeof(buffer));
103 return QString::fromLocal8Bit(av_ts_make_time_string(buffer, ts, tb));
105 return QString::fromUtf8("");
109 QString MOVIE_SAVER::err2str(int errnum)
111 #if defined(USE_LIBAV)
112 char buffer[AV_TS_MAX_STRING_SIZE + 16];
113 memset(buffer, 0x00, sizeof(buffer));
114 return QString::fromLocal8Bit(av_make_error_string(buffer, sizeof(buffer), errnum));
116 return QString::fromUtf8("");
120 //void MOVIE_SAVER::log_packet(const AVFormatContext *fmt_ctx, const AVPacket *pkt)
121 void MOVIE_SAVER::log_packet(const void *_fmt_ctx, const void *_pkt)
123 #if defined(USE_LIBAV)
124 const AVFormatContext *fmt_ctx = (const AVFormatContext *)_fmt_ctx;
125 const AVPacket *pkt = (const AVPacket *)_pkt;
126 AVRational *time_base = &fmt_ctx->streams[pkt->stream_index]->time_base;
128 out_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",
129 ts2str(pkt->pts).toLocal8Bit().constData(),
130 ts2timestr(pkt->pts, (void *)time_base).toLocal8Bit().constData(),
131 ts2str(pkt->dts).toLocal8Bit().constData(),
132 ts2timestr(pkt->dts, (void *)time_base).toLocal8Bit().constData(),
133 ts2str(pkt->duration).toLocal8Bit().constData(),
134 ts2timestr(pkt->duration, (void *)time_base).toLocal8Bit().constData(),
140 void MOVIE_SAVER::enqueue_video(int frames, int width, int height, QImage *p)
142 #if defined(USE_MOVIE_SAVER)
143 if(!recording) return;
144 if(p == NULL) return;
146 if((width <= 0) || (height <= 0) || (frames <= 0)) return;
147 if((p->width() <= 0) || (p->height() <= 0)) return;
148 VIDEO_DATA *px = new VIDEO_DATA(frames, width, height, p);
149 //out_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);
150 //video_queue_mutex->lock();
151 video_data_queue.enqueue(px);
152 //video_queue_mutex->unlock();
156 bool MOVIE_SAVER::dequeue_video(uint32_t *p)
158 //if(!recording) return false;
159 if(p == NULL) return false;
161 VIDEO_DATA *pp = video_data_queue.dequeue();
162 #if defined(USE_MOVIE_SAVER)
163 if(pp == NULL) return false;
166 QImage *pp_i = &(pp->frame_data);
167 left_frames = pp->frames;
169 _height = pp->_height;
170 if((left_frames > 0) && (pp_i != NULL)){
171 for(y = 0; y < _height; y++) {
172 if(y >= pp_i->height()) break;
173 pq = (uint32_t *)(pp_i->constScanLine(y));
174 my_memcpy(&(p[_width * y]), pq, (((uint)_width * sizeof(uint32_t)) > pp_i->bytesPerLine()) ? pp_i->bytesPerLine() : _width * sizeof(uint32_t));
176 video_size = _width * y;
177 //out_debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "Movie: Dequeue video data %d bytes", video_size);
182 if(pp != NULL) delete pp;
186 void MOVIE_SAVER::enqueue_audio(int16_t *p, int size)
188 #if defined(USE_MOVIE_SAVER)
189 if(!recording) return;
190 if(p == NULL) return;
192 QByteArray *pp = new QByteArray((const char *)p, size);
193 //out_debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "Movie: Enqueue audio data %d bytes", size);
194 audio_data_queue.enqueue(pp);
195 // audio_enqueue_count++;
199 bool MOVIE_SAVER::dequeue_audio(int16_t *p)
201 //if(!recording) return false;
202 if(audio_data_queue.isEmpty()) return false;
203 if(p == NULL) return false;
205 QByteArray *pp = audio_data_queue.dequeue();
206 int16_t *q = (int16_t *)pp->constData();
208 #if defined(USE_MOVIE_SAVER)
209 if(pp == NULL) return false;
210 audio_size = pp->size();
211 if(audio_size <= 0) return false;
212 // I expect to use SIMD.
213 for(int i = 0; i < (int)(audio_size / sizeof(int16_t)); i++) {
220 //memcpy(p, pp->constData(), audio_size);
225 if(pp != NULL) delete pp;
230 void MOVIE_SAVER::run()
233 //out_debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "MOVIE THREAD: Start");
235 int fps_wait = (int)((1000.0 / (double)rec_fps) / 4.0);
236 int tmp_wait = fps_wait;
237 bool need_audio_transcode = false;
238 bool need_video_transcode = false;
241 volatile bool old_recording = false;
245 audio_frame_offset = 0;
254 if(!bRunThread) break;
256 //n_encode_video = n_encode_audio = 1;
260 audio_frame_offset = 0;
267 if(!do_open_main()) {
271 out_debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "MOVIE/Saver: Start to recording.");
272 old_recording = true;
275 if(audio_remain <= 0) {
276 a_f = audio_data_queue.isEmpty();
278 dequeue_audio(audio_frame_buf);
279 audio_remain = audio_size;
281 need_audio_transcode = true;
285 v_f = video_data_queue.isEmpty();
288 if(left_frames <= 0) dequeue_video(video_frame_buf);
290 video_remain = video_size;
292 need_video_transcode = true;
296 #ifdef AVCODEC_UPPER_V56
297 if(audio_st.context != NULL) {
298 if(video_st.context != NULL) {
299 result_ts = av_compare_ts(video_st.next_pts, video_st.context->time_base,
300 audio_st.next_pts, audio_st.context->time_base);
302 result_ts = 1; // AUDIO ONLY
305 if(video_st.context != NULL) {
306 result_ts = -1; // VIDEO ONLY
310 result_ts = av_compare_ts(video_st.next_pts, video_st.st->codec->time_base,
311 audio_st.next_pts, audio_st.st->codec->time_base);
313 if ((n_encode_video == 0) &&
314 ((n_encode_audio != 0) ||
316 n_encode_video = write_video_frame();
317 ret = n_encode_video;
318 if(n_encode_video < 0) {
319 out_debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "MOVIE/Saver: Something wrong with encoding video.");
323 n_encode_audio = write_audio_frame();
324 ret = n_encode_audio;
325 if(n_encode_audio < 0) {
326 out_debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "MOVIE/Saver: Something wrong with encoding audio.");
330 if (ret < 0 && ret != AVERROR_EOF) {
332 av_strerror(ret, errbuf, sizeof(errbuf));
333 out_debug_log(CSP_LOG_INFO, CSP_LOG_TYPE_MOVIE_SAVER, "Error while filtering: %s\n", (const char *)errbuf);
337 /* dump report by using the output first video and audio streams */
338 //print_report(0, timer_start, cur_time);
341 need_video_transcode = need_audio_transcode = false;
342 old_recording = false;
346 //if(req_stop && a_f && v_f) req_close = true;
347 if(!bRunThread) break;
348 if(need_video_transcode || need_audio_transcode) {
349 need_video_transcode = need_audio_transcode = false;
352 if(fps_wait >= tmp_wait) {
353 this->msleep(tmp_wait);
356 this->msleep(fps_wait);
357 tmp_wait -= fps_wait;
360 fps_wait = (int)((1000.0 / (double)rec_fps) / 4.0);
366 old_recording = false;
372 old_recording = false;
374 out_debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "MOVIE: Exit thread.");
381 bool MOVIE_SAVER::is_recording(void)
387 void MOVIE_SAVER::do_exit()
394 void MOVIE_SAVER::do_set_record_fps(int fps)
396 if((fps > 0) && (fps <= 75)) rec_fps = fps;
399 void MOVIE_SAVER::do_set_width(int width)
404 void MOVIE_SAVER::do_set_height(int height)
409 void MOVIE_SAVER::do_clear_options_list(void)
411 encode_opt_keys.clear();
412 encode_options.clear();
415 void MOVIE_SAVER::do_add_option(QString key, QString value)
417 if(key.isEmpty()) return;
418 encode_opt_keys.append(key);
419 if(value.isEmpty()) {
420 encode_options.append(QString::fromUtf8(""));
422 encode_options.append(value);
426 void MOVIE_SAVER::do_set_video_bitrate(int kbps)
428 if(kbps < 10) return;
429 video_bit_rate = kbps * 1000;
432 void MOVIE_SAVER::do_set_audio_bitrate(int kbps)
435 audio_bit_rate = kbps * 1000;
438 void MOVIE_SAVER::do_set_video_geometry(QSize geometry)
440 if(geometry.width() < 100) return;
441 if(geometry.height() < 80) return;
442 video_geometry = geometry;
445 void MOVIE_SAVER::do_set_video_threads(int threads)
447 video_encode_threads = threads;
450 void MOVIE_SAVER::do_reset_encoder_options(void)
452 encode_opt_keys.clear();
453 encode_options.clear();
455 do_add_option(QString::fromUtf8("c:v"), QString::fromUtf8("libx264"));
456 do_add_option(QString::fromUtf8("c:a"), QString::fromUtf8("aac"));
457 do_add_option(QString::fromUtf8("preset"), QString::fromUtf8("slow"));
458 do_add_option(QString::fromUtf8("tune"), QString::fromUtf8("grain"));
459 //do_add_option(QString::fromUtf8("crf"), QString::fromUtf8("20"));
460 do_add_option(QString::fromUtf8("qmin"), QString::fromUtf8("16"));
461 do_add_option(QString::fromUtf8("qmax"), QString::fromUtf8("24"));
463 do_add_option(QString::fromUtf8("qmax"), QString::fromUtf8("24"));