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"
11 #include "agar_logger.h"
13 MOVIE_SAVER::MOVIE_SAVER(int width, int height, int fps, OSD *osd) : QThread(0)
15 buffer_size=8 * 1024 * 224;
23 audio_sample_rate = 48000;
24 #if defined(USE_MOVIE_SAVER)
25 memset(audio_frame_buf, 0x00, sizeof(audio_frame_buf));
26 memset(video_frame_buf, 0x00, sizeof(video_frame_buf));
27 output_context = NULL;
29 audio_codec = video_codec = NULL;
30 raw_options_list = NULL;
32 memset(&video_st, 0x00, sizeof(video_st));
33 memset(&audio_st, 0x00, sizeof(audio_st));
36 output_context = NULL;
38 audio_data_queue.clear();
39 video_data_queue.clear();
41 do_reset_encoder_options();
46 video_bit_rate = 1500 * 1000;
47 audio_bit_rate = 160 * 1000;
48 video_geometry = QSize(640, 480);
49 video_encode_threads = 4;
54 MOVIE_SAVER::~MOVIE_SAVER()
56 if(recording) do_close();
59 QString MOVIE_SAVER::ts2str(int64_t ts)
61 #if defined(USE_LIBAV)
62 char buffer[AV_TS_MAX_STRING_SIZE + 16];
63 memset(buffer, 0x00, sizeof(buffer));
64 return QString::fromLocal8Bit(av_ts_make_string(buffer, ts));
66 return QString::fromUtf8("");
70 QString MOVIE_SAVER::ts2timestr(int64_t ts, void *timebase)
72 #if defined(USE_LIBAV)
73 char buffer[AV_TS_MAX_STRING_SIZE + 16];
74 AVRational *tb = (AVRational *)timebase;
75 memset(buffer, 0x00, sizeof(buffer));
76 return QString::fromLocal8Bit(av_ts_make_time_string(buffer, ts, tb));
78 return QString::fromUtf8("");
82 QString MOVIE_SAVER::err2str(int errnum)
84 #if defined(USE_LIBAV)
85 char buffer[AV_TS_MAX_STRING_SIZE + 16];
86 memset(buffer, 0x00, sizeof(buffer));
87 return QString::fromLocal8Bit(av_make_error_string(buffer, sizeof(buffer), errnum));
89 return QString::fromUtf8("");
93 //void MOVIE_SAVER::log_packet(const AVFormatContext *fmt_ctx, const AVPacket *pkt)
94 void MOVIE_SAVER::log_packet(const void *_fmt_ctx, const void *_pkt)
96 #if defined(USE_LIBAV)
97 const AVFormatContext *fmt_ctx = (const AVFormatContext *)_fmt_ctx;
98 const AVPacket *pkt = (const AVPacket *)_pkt;
99 AVRational *time_base = &fmt_ctx->streams[pkt->stream_index]->time_base;
101 AGAR_DebugLog(AGAR_LOG_DEBUG, "pts:%s pts_time:%s dts:%s dts_time:%s duration:%s duration_time:%s stream_index:%d\n",
102 ts2str(pkt->pts).toLocal8Bit().constData(),
103 ts2timestr(pkt->pts, (void *)time_base).toLocal8Bit().constData(),
104 ts2str(pkt->dts).toLocal8Bit().constData(),
105 ts2timestr(pkt->dts, (void *)time_base).toLocal8Bit().constData(),
106 ts2str(pkt->duration).toLocal8Bit().constData(),
107 ts2timestr(pkt->duration, (void *)time_base).toLocal8Bit().constData(),
113 void MOVIE_SAVER::enqueue_video(QImage *p)
115 #if defined(USE_MOVIE_SAVER)
116 if(!recording) return;
117 if(p == NULL) return;
119 QImage *pp = new QImage(*p);
120 //AGAR_DebugLog(AGAR_LOG_DEBUG, "Movie: Enqueue video data %d bytes %dx%d", pp->byteCount(), pp->width(), pp->height());
121 video_data_queue.enqueue(pp);
125 bool MOVIE_SAVER::dequeue_video(uint32_t *p)
127 //if(!recording) return false;
128 if(p == NULL) return false;
130 QImage *pp = video_data_queue.dequeue();
131 #if defined(USE_MOVIE_SAVER)
132 if(pp == NULL) return false;
136 for(y = 0; y < _height; y++) {
137 if(y >= pp->height()) break;
138 pq = (uint32_t *)(pp->constScanLine(y));
139 memcpy(&(p[_width * y]), pq, ((_width * sizeof(uint32_t)) > pp->bytesPerLine()) ? pp->bytesPerLine() : _width * sizeof(uint32_t));
141 video_size = _width * (y + 1);
142 //AGAR_DebugLog(AGAR_LOG_DEBUG, "Movie: Dequeue video data %d bytes", pp->size());
146 if(pp != NULL) delete pp;
151 void MOVIE_SAVER::enqueue_audio(int16_t *p, int size)
153 #if defined(USE_MOVIE_SAVER)
154 if(!recording) return;
155 if(p == NULL) return;
156 QByteArray *pp = new QByteArray((const char *)p, size);
157 //AGAR_DebugLog(AGAR_LOG_DEBUG, "Movie: Enqueue audio data %d bytes", size);
158 audio_data_queue.enqueue(pp);
162 bool MOVIE_SAVER::dequeue_audio(int16_t *p)
164 //if(!recording) return false;
165 if(audio_data_queue.isEmpty()) return false;
166 if(p == NULL) return false;
167 QByteArray *pp = audio_data_queue.dequeue();
168 #if defined(USE_MOVIE_SAVER)
169 if(pp == NULL) return false;
170 //AGAR_DebugLog(AGAR_LOG_DEBUG, "Movie: Dequeue audio data %d bytes", pp->size());
172 audio_size = pp->size();
173 if(audio_size <= 0) return false;
174 memcpy(p, pp->constData(), audio_size);
178 if(pp != NULL) delete pp;
183 void MOVIE_SAVER::run()
186 //AGAR_DebugLog(AGAR_LOG_DEBUG, "MOVIE THREAD: Start");
191 int fps_wait = (int)((1000.0 / p_osd->vm_frame_rate()) / 8.0);
192 int tmp_wait = fps_wait;
193 int ncount_audio = 0;
194 int ncount_video = 0;
195 bool audio_continue = false;
196 bool video_continue = false;
197 bool need_audio_transcode = false;
198 bool need_video_transcode = false;
200 int64_t total_packets_written = 0;
201 bool old_recording = false;
205 audio_frame_offset = 0;
212 if(!bRunThread) break;
213 if(old_recording != recording) {
214 AGAR_DebugLog(AGAR_LOG_DEBUG, "MOVIE/Saver: Start to recording.");
215 n_encode_video = n_encode_audio = 1;
219 audio_frame_offset = 0;
222 if(audio_remain <= 0) {
223 if(audio_data_queue.isEmpty()) goto _video;
224 dequeue_audio(audio_frame_buf);
225 audio_remain = audio_size;
227 need_audio_transcode = true;
231 if(video_data_queue.isEmpty())
233 dequeue_video(video_frame_buf);
234 video_remain = video_size;
236 need_video_transcode = true;
239 if ((n_encode_video == 0) &&
240 ((n_encode_audio != 0) || av_compare_ts(video_st.next_pts, video_st.st->codec->time_base,
241 audio_st.next_pts, audio_st.st->codec->time_base) <= 0)) {
242 n_encode_video = write_video_frame();
243 if(n_encode_video < 0) {
244 AGAR_DebugLog(AGAR_LOG_DEBUG, "MOVIE/Saver: Something wrong with encoding video.");
248 if(n_encode_audio == 0) {
249 n_encode_audio = write_audio_frame();
250 if(n_encode_audio < 0) {
251 AGAR_DebugLog(AGAR_LOG_DEBUG, "MOVIE/Saver: Something wrong with encoding audio.");
256 if (ret < 0 && ret != AVERROR_EOF) {
258 av_strerror(ret, errbuf, sizeof(errbuf));
259 AGAR_DebugLog(AGAR_LOG_INFO, "Error while filtering: %s\n", (const char *)errbuf);
263 /* dump report by using the output first video and audio streams */
264 //print_report(0, timer_start, cur_time);
267 old_recording = recording;
268 if(!bRunThread) break;
269 if(need_video_transcode || need_audio_transcode) {
270 need_video_transcode = need_audio_transcode = false;
273 if(fps_wait >= tmp_wait) {
274 this->msleep(tmp_wait);
277 this->msleep(fps_wait);
278 tmp_wait -= fps_wait;
281 fps_wait = (int)((1000.0 / p_osd->vm_frame_rate()) / 8.0);
285 old_recording = recording;
288 old_recording = recording;
290 old_recording = false;
292 AGAR_DebugLog(AGAR_LOG_DEBUG, "MOVIE: Exit thread.");
293 if(recording) do_close();
296 bool MOVIE_SAVER::is_recording(void)
302 void MOVIE_SAVER::do_exit()
307 void MOVIE_SAVER::do_set_record_fps(int fps)
309 if((fps > 0) && (fps <= 75)) rec_fps = fps;
312 void MOVIE_SAVER::do_set_width(int width)
314 //printf("width = %d -> %d\n", _width, width);
318 void MOVIE_SAVER::do_set_height(int height)
320 //printf("height = %d -> %d\n", _height, height);
324 void MOVIE_SAVER::do_clear_options_list(void)
326 encode_opt_keys.clear();
327 encode_options.clear();
328 //do_add_option(QString::fromUtf8("c:v"), QString::fromUtf8("libx264"));
329 //do_add_option(QString::fromUtf8("c:a"), QString::fromUtf8("libfaac"));
332 void MOVIE_SAVER::do_add_option(QString key, QString value)
334 if(key.isEmpty()) return;
335 encode_opt_keys.append(key);
336 if(value.isEmpty()) {
337 encode_options.append(QString::fromUtf8(""));
339 encode_options.append(value);
343 void MOVIE_SAVER::do_set_video_bitrate(int kbps)
345 if(kbps < 10) return;
346 video_bit_rate = kbps * 1000;
349 void MOVIE_SAVER::do_set_audio_bitrate(int kbps)
352 audio_bit_rate = kbps * 1000;
355 void MOVIE_SAVER::do_set_video_geometry(QSize geometry)
357 if(geometry.width() < 100) return;
358 if(geometry.height() < 80) return;
359 video_geometry = geometry;
362 void MOVIE_SAVER::do_set_video_threads(int threads)
364 video_encode_threads = threads;
367 void MOVIE_SAVER::do_reset_encoder_options(void)
369 encode_opt_keys.clear();
370 encode_options.clear();
372 do_add_option(QString::fromUtf8("c:v"), QString::fromUtf8("libx264"));
373 do_add_option(QString::fromUtf8("c:a"), QString::fromUtf8("libfaac"));
374 do_add_option(QString::fromUtf8("preset"), QString::fromUtf8("slow"));
375 do_add_option(QString::fromUtf8("tune"), QString::fromUtf8("grain"));
376 //do_add_option(QString::fromUtf8("crf"), QString::fromUtf8("20"));
377 do_add_option(QString::fromUtf8("qmin"), QString::fromUtf8("16"));
378 do_add_option(QString::fromUtf8("qmax"), QString::fromUtf8("24"));
380 do_add_option(QString::fromUtf8("qmax"), QString::fromUtf8("24"));