OSDN Git Service

[BUILD] Set SOVERSION and GIT hash automatically.
[csp-qt/common_source_project-fm7.git] / source / src / qt / avio / movie_saver.cpp
1 /*
2  * Common Source Code Project for Qt : movie saver.
3  * (C) 2016 K.Ohta <whatisthis.sowhat _at_ gmail.com>
4  *  License: GPLv2
5  *  History: May 27, 2016 : Initial. This refer from avidemux 2.5.6 .
6  */
7
8 #include <QDateTime>
9 #include "movie_saver.h"
10 #include "../osd.h"
11 #include "common.h"
12 #include "csp_logger.h"
13
14 MOVIE_SAVER::MOVIE_SAVER(int width, int height, int fps, OSD *osd, config_t *cfg) : QThread(0)
15 {
16         buffer_size=8 * 1024 * 224;
17         max_rate=4000 * 1000;
18         min_rate=0;
19         _width = width;
20         _height = height;
21         rec_fps = fps;
22         
23         p_osd = osd;
24         p_config = cfg;
25         p_logger = osd->get_logger();
26         
27         recording = false;
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;
33         stream_format = NULL;
34         audio_codec = video_codec = NULL;
35         raw_options_list = NULL;
36         
37         memset(&video_st, 0x00, sizeof(video_st));
38         memset(&audio_st, 0x00, sizeof(audio_st));
39 #endif
40
41         output_context = NULL;
42         
43         audio_data_queue.clear();
44         video_data_queue.clear();
45         
46         do_reset_encoder_options();
47
48         //video_queue_mutex = new QMutex(QMutex::Recursive);
49         totalSrcFrame = 0;
50         totalDstFrame = 0;
51         totalAudioFrame = 0;
52
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;
58         left_frames = 0;
59         req_close = false;
60         req_stop = false;
61         bRunThread = false;
62         
63 }
64
65 MOVIE_SAVER::~MOVIE_SAVER()
66 {
67         req_close = true;
68         if(recording) do_close_main();
69 }
70
71 QString MOVIE_SAVER::get_avio_version()
72 {
73 #if defined(__LIBAVIO_VERSION)
74         return QString::fromUtf8(__LIBAVIO_VERSION);
75 #else
76         return QString::fromUtf8("\0");
77 #endif
78 }
79
80 QString MOVIE_SAVER::ts2str(int64_t ts)
81 {
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));
86 #else
87         return QString::fromUtf8("");
88 #endif
89 }                  
90
91 QString MOVIE_SAVER::ts2timestr(int64_t ts, void *timebase)
92 {
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));
98 #else
99         return QString::fromUtf8("");
100 #endif
101 }                  
102
103 QString MOVIE_SAVER::err2str(int errnum)
104 {
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));
109 #else
110         return QString::fromUtf8("");
111 #endif
112 }                  
113
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)
116 {
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;
121
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(),
129                    pkt->stream_index);
130 #endif  
131 }
132
133
134 void MOVIE_SAVER::enqueue_video(int frames, int width, int height, QImage *p)
135 {
136 #if defined(USE_MOVIE_SAVER)
137         if(!recording) return;
138         if(p == NULL) return;
139         if(req_stop) 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();
147 #endif   
148 }
149
150 bool MOVIE_SAVER::dequeue_video(uint32_t *p)
151 {
152         //if(!recording) return false;
153         if(p == NULL) return false;
154
155         VIDEO_DATA *pp = video_data_queue.dequeue();
156 #if defined(USE_MOVIE_SAVER)
157         if(pp == NULL) return false;
158         int y;
159         uint32_t *pq;
160         QImage *pp_i = &(pp->frame_data);
161         left_frames = pp->frames;
162         _width = pp->_width;
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));
169                 }
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);
172         }
173 #else
174         video_size = 0;
175 #endif   
176         if(pp != NULL) delete pp;
177         return true;
178 }
179
180 void MOVIE_SAVER::enqueue_audio(int16_t *p, int size)
181 {
182 #if defined(USE_MOVIE_SAVER)
183         if(!recording) return;
184         if(p == NULL) return;
185         if(req_stop) 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++;
190 #endif   
191 }
192
193 bool MOVIE_SAVER::dequeue_audio(int16_t *p)
194 {
195         //if(!recording) return false;
196         if(audio_data_queue.isEmpty()) return false;
197         if(p == NULL) return false;
198         
199         QByteArray *pp = audio_data_queue.dequeue();
200         int16_t *q = (int16_t *)pp->constData();
201         int32_t tmpd;
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++) {
208                 tmpd = q[i];
209                 tmpd <<= 4;
210                 tmpd = tmpd / 3;
211                 tmpd >>= 3;
212                 p[i] = tmpd;
213         }
214         //memcpy(p, pp->constData(), audio_size);
215         audio_count++;
216 #else
217         audio_size = 0;
218 #endif   
219         if(pp != NULL) delete pp;
220         return true;
221 }
222
223
224 void MOVIE_SAVER::run()
225 {
226         bRunThread = true;
227         //p_logger->debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "MOVIE THREAD: Start");
228         int ret;
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;
233         int i;
234         int64_t total_packets_written = 0;
235         bool a_f, v_f;
236         a_f = v_f = false;
237         volatile bool old_recording = false;
238         audio_remain = 0;
239         video_remain = 0;
240         audio_offset = 0;
241         audio_frame_offset = 0;
242         video_offset = 0;
243         n_encode_audio = 0;
244         n_encode_video = 0;
245         req_close = false;
246         req_stop = false;
247         left_frames = 0;
248         while(bRunThread) {
249                 if(recording) {
250                         if(!bRunThread) break;
251                         if(!old_recording) {
252                                 //n_encode_video = n_encode_audio = 1;
253                                 audio_remain = 0;
254                                 video_remain = 0;
255                                 audio_offset = 0;
256                                 audio_frame_offset = 0;
257                                 video_offset = 0;
258                                 video_count = 0;
259                                 audio_count = 0;
260                                 req_close = false;
261                                 req_stop = false;
262                                 left_frames = 0;
263                                 if(!do_open_main()) {
264                                         recording = false;
265                                         goto _next_turn;
266                                 }
267                                 p_logger->debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "MOVIE/Saver: Start to recording.");
268                                 old_recording = true;
269                                 a_f = v_f = false;
270                         }
271                         if(audio_remain <= 0) {
272                                 a_f = audio_data_queue.isEmpty();
273                                 if(a_f) goto _video;
274                                 dequeue_audio(audio_frame_buf);
275                                 audio_remain = audio_size;
276                                 audio_offset = 0;
277                                 need_audio_transcode = true;
278                         }
279                 _video:
280                         {
281                                 v_f = video_data_queue.isEmpty();
282                                 if(v_f)
283                                         goto _write_frame;
284                                 if(left_frames <= 0) dequeue_video(video_frame_buf);
285                                 left_frames--;
286                                 video_remain = video_size;
287                                 video_offset = 0;
288                                 need_video_transcode = true;
289                         }
290                 _write_frame:
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.");
297                                         goto _final;
298                                 }
299                         } else {
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.");
303                                         goto _final;
304                                 }
305                         }
306                         if (ret < 0 && ret != AVERROR_EOF) {
307                                 char errbuf[128];
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);
310                                 goto _final;
311                         }
312                         
313                         /* dump report by using the output first video and audio streams */
314                         //print_report(0, timer_start, cur_time);
315                         if(req_close) {
316                                 do_close_main();
317                                 need_video_transcode = need_audio_transcode = false;
318                                 old_recording = false;
319                         }
320                 }
321         _next_turn:
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;
326                         continue;
327                 }
328                 if(fps_wait >= tmp_wait) {
329                         this->msleep(tmp_wait);
330                         tmp_wait = 0;
331                 } else {
332                         this->msleep(fps_wait);
333                         tmp_wait -= fps_wait;
334                 }
335                 if(tmp_wait <= 0) {
336                         fps_wait = (int)((1000.0 / (double)rec_fps) / 4.0);
337                         //fps_wait = 10;
338                         tmp_wait = fps_wait;
339                 }
340                 if(req_close) {
341                         do_close_main();
342                         old_recording = false;
343                 }
344                 continue;
345         _final:
346                 req_close = true;
347                 do_close_main();
348                 old_recording = false;
349         }
350         p_logger->debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "MOVIE: Exit thread.");
351         if(recording) {
352                 req_close = true;
353                 do_close_main();
354         }
355 }
356
357 bool MOVIE_SAVER::is_recording(void)
358 {
359         return recording;
360 }
361
362
363 void MOVIE_SAVER::do_exit()
364 {
365         bRunThread = false;
366         req_close = true;
367         req_stop = true;
368 }
369
370 void MOVIE_SAVER::do_set_record_fps(int fps)
371 {
372         if((fps > 0) && (fps <= 75)) rec_fps = fps;
373 }
374
375 void MOVIE_SAVER::do_set_width(int width)
376 {
377         _width = width;
378 }
379
380 void MOVIE_SAVER::do_set_height(int height)
381 {
382         _height = height;
383 }
384
385 void MOVIE_SAVER::do_clear_options_list(void)
386 {
387         encode_opt_keys.clear();
388         encode_options.clear();
389 }
390
391 void MOVIE_SAVER::do_add_option(QString key, QString value)
392 {
393         if(key.isEmpty()) return;
394         encode_opt_keys.append(key);
395         if(value.isEmpty()) {
396                 encode_options.append(QString::fromUtf8(""));
397         } else {
398                 encode_options.append(value);
399         }
400 }               
401
402 void MOVIE_SAVER::do_set_video_bitrate(int kbps)
403 {
404         if(kbps < 10) return;
405         video_bit_rate = kbps * 1000;
406 }
407
408 void MOVIE_SAVER::do_set_audio_bitrate(int kbps)
409 {
410         if(kbps < 8) return;
411         audio_bit_rate = kbps * 1000;
412 }
413
414 void MOVIE_SAVER::do_set_video_geometry(QSize geometry)
415 {
416         if(geometry.width() < 100) return;
417         if(geometry.height() < 80) return;
418         video_geometry = geometry;
419 }
420
421 void MOVIE_SAVER::do_set_video_threads(int threads)
422 {
423         video_encode_threads = threads;
424 }
425
426 void MOVIE_SAVER::do_reset_encoder_options(void)
427 {
428         encode_opt_keys.clear();
429         encode_options.clear();
430
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"));
438         // Dummy
439         do_add_option(QString::fromUtf8("qmax"), QString::fromUtf8("24"));
440 }
441