OSDN Git Service

1e3a6f35025ad05599203c46defd9467e4249670
[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 "csp_logger.h"
12
13 MOVIE_SAVER::MOVIE_SAVER(int width, int height, int fps, OSD *osd, config_t *cfg) : QThread(0)
14 {
15         buffer_size=8 * 1024 * 224;
16         max_rate=4000 * 1000;
17         min_rate=0;
18         _width = width;
19         _height = height;
20         rec_fps = fps;
21         p_osd = osd;
22         p_config = cfg;
23         
24         recording = false;
25         audio_sample_rate = 48000;
26 #if defined(USE_MOVIE_SAVER)
27         memset(audio_frame_buf, 0x00, sizeof(audio_frame_buf));
28         memset(video_frame_buf, 0x00, sizeof(video_frame_buf));
29         output_context = NULL;
30         stream_format = NULL;
31         audio_codec = video_codec = NULL;
32         raw_options_list = NULL;
33         
34         memset(&video_st, 0x00, sizeof(video_st));
35         memset(&audio_st, 0x00, sizeof(audio_st));
36 #endif
37
38         output_context = NULL;
39         
40         audio_data_queue.clear();
41         video_data_queue.clear();
42         
43         do_reset_encoder_options();
44
45         //video_queue_mutex = new QMutex(QMutex::Recursive);
46         totalSrcFrame = 0;
47         totalDstFrame = 0;
48         totalAudioFrame = 0;
49
50         video_bit_rate = 1500 * 1000;
51         audio_bit_rate = 160 * 1000;
52         video_geometry = QSize(640, 480);
53         video_encode_threads = 4;
54 //      audio_enqueue_count = 0;
55         left_frames = 0;
56         req_close = false;
57         req_stop = false;
58         bRunThread = false;
59         
60 }
61
62 MOVIE_SAVER::~MOVIE_SAVER()
63 {
64         if(recording) do_close_main();
65         //if(video_queue_mutex) delete video_queue_mutex;
66
67 }
68
69 QString MOVIE_SAVER::ts2str(int64_t ts)
70 {
71 #if defined(USE_LIBAV)  
72         char buffer[AV_TS_MAX_STRING_SIZE + 16];
73         memset(buffer, 0x00, sizeof(buffer));
74         return QString::fromLocal8Bit(av_ts_make_string(buffer, ts));
75 #else
76         return QString::fromUtf8("");
77 #endif
78 }                  
79
80 QString MOVIE_SAVER::ts2timestr(int64_t ts, void *timebase)
81 {
82 #if defined(USE_LIBAV)  
83         char buffer[AV_TS_MAX_STRING_SIZE + 16];
84         AVRational *tb = (AVRational *)timebase;
85         memset(buffer, 0x00, sizeof(buffer));
86         return QString::fromLocal8Bit(av_ts_make_time_string(buffer, ts, tb));
87 #else
88         return QString::fromUtf8("");
89 #endif
90 }                  
91
92 QString MOVIE_SAVER::err2str(int errnum)
93 {
94 #if defined(USE_LIBAV)  
95         char buffer[AV_TS_MAX_STRING_SIZE + 16];
96         memset(buffer, 0x00, sizeof(buffer));
97         return QString::fromLocal8Bit(av_make_error_string(buffer, sizeof(buffer), errnum));
98 #else
99         return QString::fromUtf8("");
100 #endif
101 }                  
102
103 //void MOVIE_SAVER::log_packet(const AVFormatContext *fmt_ctx, const AVPacket *pkt)
104 void MOVIE_SAVER::log_packet(const void *_fmt_ctx, const void *_pkt)
105 {
106 #if defined(USE_LIBAV)  
107         const AVFormatContext *fmt_ctx = (const AVFormatContext *)_fmt_ctx;
108         const AVPacket *pkt = (const AVPacket *)_pkt;
109         AVRational *time_base = &fmt_ctx->streams[pkt->stream_index]->time_base;
110
111         csp_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",
112                    ts2str(pkt->pts).toLocal8Bit().constData(),
113                    ts2timestr(pkt->pts, (void *)time_base).toLocal8Bit().constData(),
114                    ts2str(pkt->dts).toLocal8Bit().constData(),
115                    ts2timestr(pkt->dts, (void *)time_base).toLocal8Bit().constData(),
116                    ts2str(pkt->duration).toLocal8Bit().constData(),
117                    ts2timestr(pkt->duration, (void *)time_base).toLocal8Bit().constData(),
118                    pkt->stream_index);
119 #endif  
120 }
121
122
123 void MOVIE_SAVER::enqueue_video(int frames, int width, int height, QImage *p)
124 {
125 #if defined(USE_MOVIE_SAVER)
126         if(!recording) return;
127         if(p == NULL) return;
128         if(req_stop) return;
129         uint32_t *pq;
130         VIDEO_DATA *px = new VIDEO_DATA(frames, width, height, p);
131         //csp_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);
132         //video_queue_mutex->lock();
133         video_data_queue.enqueue(px);
134         //video_queue_mutex->unlock();
135 #endif   
136 }
137
138 bool MOVIE_SAVER::dequeue_video(uint32_t *p)
139 {
140         //if(!recording) return false;
141         if(p == NULL) return false;
142
143         VIDEO_DATA *pp = video_data_queue.dequeue();
144 #if defined(USE_MOVIE_SAVER)
145         if(pp == NULL) return false;
146         int y;
147         int x;
148         uint32_t *pq;
149         QImage *pp_i = &(pp->frame_data);
150         left_frames = pp->frames;
151         _width = pp->_width;
152         _height = pp->_height;
153         if((left_frames > 0) && (pp_i != NULL)){ 
154                 for(y = 0; y < _height; y++) {
155                         if(y >= pp_i->height()) break;
156                         pq = (uint32_t *)(pp_i->constScanLine(y));
157                         memcpy(&(p[_width * y]), pq, ((_width * sizeof(uint32_t)) > pp_i->bytesPerLine()) ? pp_i->bytesPerLine() : _width * sizeof(uint32_t));
158                 }
159                 video_size = _width * y;
160                 //csp_logger->debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "Movie: Dequeue video data %d bytes", pp->byteCount());
161         }
162 #else
163         video_size = 0;
164 #endif   
165         if(pp != NULL) delete pp;
166         return true;
167 }
168
169 void MOVIE_SAVER::enqueue_audio(int16_t *p, int size)
170 {
171 #if defined(USE_MOVIE_SAVER)
172         if(!recording) return;
173         if(p == NULL) return;
174         if(req_stop) return;
175         QByteArray *pp = new QByteArray((const char *)p, size);
176         //csp_logger->debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "Movie: Enqueue audio data %d bytes", size);
177         audio_data_queue.enqueue(pp);
178 //      audio_enqueue_count++;
179 #endif   
180 }
181
182 bool MOVIE_SAVER::dequeue_audio(int16_t *p)
183 {
184         //if(!recording) return false;
185         if(audio_data_queue.isEmpty()) return false;
186         if(p == NULL) return false;
187         
188         QByteArray *pp = audio_data_queue.dequeue();
189         int16_t *q = (int16_t *)pp->constData();
190         int32_t tmpd;
191 #if defined(USE_MOVIE_SAVER)
192         if(pp == NULL) return false;
193         audio_size = pp->size();
194         if(audio_size <= 0) return false;
195         // I expect to use SIMD.
196         for(int i = 0; i < audio_size / sizeof(int16_t); i++) {
197                 tmpd = q[i];
198                 tmpd <<= 4;
199                 tmpd = tmpd / 3;
200                 tmpd >>= 3;
201                 p[i] = tmpd;
202         }
203         //memcpy(p, pp->constData(), audio_size);
204         audio_count++;
205         //csp_logger->debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "Movie: Dequeue audio data %d bytes", pp->size());
206 #else
207         audio_size = 0;
208 #endif   
209         if(pp != NULL) delete pp;
210         return true;
211 }
212
213
214 void MOVIE_SAVER::run()
215 {
216         bRunThread = true;
217         //csp_logger->debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "MOVIE THREAD: Start");
218         int ret;
219         int fps_wait = (int)((1000.0 / (double)rec_fps) / 4.0);
220         int tmp_wait = fps_wait;
221         bool need_audio_transcode = false;
222         bool need_video_transcode = false;
223         int i;
224         int64_t total_packets_written = 0;
225         bool a_f, v_f;
226         a_f = v_f = false;
227         volatile bool old_recording = false;
228         audio_remain = 0;
229         video_remain = 0;
230         audio_offset = 0;
231         audio_frame_offset = 0;
232         video_offset = 0;
233         n_encode_audio = 0;
234         n_encode_video = 0;
235         req_close = false;
236         req_stop = false;
237         left_frames = 0;
238         while(bRunThread) {
239                 if(recording) {
240                         if(!bRunThread) break;
241                         if(!old_recording) {
242                                 //n_encode_video = n_encode_audio = 1;
243                                 audio_remain = 0;
244                                 video_remain = 0;
245                                 audio_offset = 0;
246                                 audio_frame_offset = 0;
247                                 video_offset = 0;
248                                 video_count = 0;
249                                 audio_count = 0;
250                                 req_close = false;
251                                 req_stop = false;
252                                 left_frames = 0;
253                                 if(!do_open_main()) {
254                                         recording = false;
255                                         goto _next_turn;
256                                 }
257                                 csp_logger->debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "MOVIE/Saver: Start to recording.");
258                                 old_recording = true;
259                                 a_f = v_f = false;
260                         }
261                         if(audio_remain <= 0) {
262                                 a_f = audio_data_queue.isEmpty();
263                                 if(a_f) goto _video;
264                                 dequeue_audio(audio_frame_buf);
265                                 audio_remain = audio_size;
266                                 audio_offset = 0;
267                                 need_audio_transcode = true;
268                         }
269                 _video:
270                         {
271                                 v_f = video_data_queue.isEmpty();
272                                 if(v_f)
273                                         goto _write_frame;
274                                 if(left_frames <= 0) dequeue_video(video_frame_buf);
275                                 left_frames--;
276                                 video_remain = video_size;
277                                 video_offset = 0;
278                                 need_video_transcode = true;
279                         }
280                 _write_frame:
281                         if ((n_encode_video == 0) &&
282                                 ((n_encode_audio != 0) || av_compare_ts(video_st.next_pts, video_st.st->codec->time_base,
283                                                                                                                 audio_st.next_pts, audio_st.st->codec->time_base) <= 0)) {
284                                 n_encode_video = write_video_frame();
285                                 if(n_encode_video < 0) {
286                                         csp_logger->debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "MOVIE/Saver: Something wrong with encoding video.");
287                                         goto _final;
288                                 }
289                         } else {
290                                 n_encode_audio = write_audio_frame();
291                                 if(n_encode_audio < 0) {
292                                         csp_logger->debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "MOVIE/Saver: Something wrong with encoding audio.");
293                                         goto _final;
294                                 }
295                         }
296                         if (ret < 0 && ret != AVERROR_EOF) {
297                                 char errbuf[128];
298                                 av_strerror(ret, errbuf, sizeof(errbuf));
299                                 csp_logger->debug_log(CSP_LOG_INFO, CSP_LOG_TYPE_MOVIE_SAVER, "Error while filtering: %s\n", (const char *)errbuf);
300                                 goto _final;
301                         }
302                         
303                         /* dump report by using the output first video and audio streams */
304                         //print_report(0, timer_start, cur_time);
305                         if(req_close) {
306                                 do_close_main();
307                                 need_video_transcode = need_audio_transcode = false;
308                                 old_recording = false;
309                         }
310                 }
311         _next_turn:
312                 //if(req_stop && a_f && v_f) req_close = true;
313                 if(!bRunThread) break;
314                 if(need_video_transcode || need_audio_transcode) {
315                         need_video_transcode = need_audio_transcode = false;
316                         continue;
317                 }
318                 if(fps_wait >= tmp_wait) {
319                         this->msleep(tmp_wait);
320                         tmp_wait = 0;
321                 } else {
322                         this->msleep(fps_wait);
323                         tmp_wait -= fps_wait;
324                 }
325                 if(tmp_wait <= 0) {
326                         fps_wait = (int)((1000.0 / (double)rec_fps) / 4.0);
327                         //fps_wait = 10;
328                         tmp_wait = fps_wait;
329                 }
330                 if(req_close) {
331                         do_close_main();
332                         old_recording = false;
333                 }
334                 continue;
335         _final:
336                 req_close = true;
337                 do_close_main();
338                 old_recording = false;
339         }
340         csp_logger->debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "MOVIE: Exit thread.");
341         if(recording) {
342                 req_close = true;
343                 do_close_main();
344         }
345 }
346
347 bool MOVIE_SAVER::is_recording(void)
348 {
349         return recording;
350 }
351
352
353 void MOVIE_SAVER::do_exit()
354 {
355         bRunThread = false;
356         req_close = true;
357         req_stop = true;
358 }
359
360 void MOVIE_SAVER::do_set_record_fps(int fps)
361 {
362         if((fps > 0) && (fps <= 75)) rec_fps = fps;
363 }
364
365 void MOVIE_SAVER::do_set_width(int width)
366 {
367         _width = width;
368 }
369
370 void MOVIE_SAVER::do_set_height(int height)
371 {
372         _height = height;
373 }
374
375 void MOVIE_SAVER::do_clear_options_list(void)
376 {
377         encode_opt_keys.clear();
378         encode_options.clear();
379 }
380
381 void MOVIE_SAVER::do_add_option(QString key, QString value)
382 {
383         if(key.isEmpty()) return;
384         encode_opt_keys.append(key);
385         if(value.isEmpty()) {
386                 encode_options.append(QString::fromUtf8(""));
387         } else {
388                 encode_options.append(value);
389         }
390 }               
391
392 void MOVIE_SAVER::do_set_video_bitrate(int kbps)
393 {
394         if(kbps < 10) return;
395         video_bit_rate = kbps * 1000;
396 }
397
398 void MOVIE_SAVER::do_set_audio_bitrate(int kbps)
399 {
400         if(kbps < 8) return;
401         audio_bit_rate = kbps * 1000;
402 }
403
404 void MOVIE_SAVER::do_set_video_geometry(QSize geometry)
405 {
406         if(geometry.width() < 100) return;
407         if(geometry.height() < 80) return;
408         video_geometry = geometry;
409 }
410
411 void MOVIE_SAVER::do_set_video_threads(int threads)
412 {
413         video_encode_threads = threads;
414 }
415
416 void MOVIE_SAVER::do_reset_encoder_options(void)
417 {
418         encode_opt_keys.clear();
419         encode_options.clear();
420
421         do_add_option(QString::fromUtf8("c:v"), QString::fromUtf8("libx264"));
422         do_add_option(QString::fromUtf8("c:a"), QString::fromUtf8("aac"));
423         do_add_option(QString::fromUtf8("preset"), QString::fromUtf8("slow"));
424         do_add_option(QString::fromUtf8("tune"), QString::fromUtf8("grain"));
425         //do_add_option(QString::fromUtf8("crf"), QString::fromUtf8("20"));
426         do_add_option(QString::fromUtf8("qmin"), QString::fromUtf8("16"));
427         do_add_option(QString::fromUtf8("qmax"), QString::fromUtf8("24"));
428         // Dummy
429         do_add_option(QString::fromUtf8("qmax"), QString::fromUtf8("24"));
430 }
431