OSDN Git Service

[VM][FMTOWNS][MEMORY] Fix setup around memory banks by I/O 0404h and 0480h.
[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
10 #include "./csp_avio_basic.h"
11 #include "movie_saver.h"
12 #include "../osd.h"
13 #include "common.h"
14 #include "csp_logger.h"
15
16 #if (LIBAVCODEC_VERSION_MAJOR > 56)
17 #define AVCODEC_UPPER_V56
18 #endif
19
20 MOVIE_SAVER::MOVIE_SAVER(int width, int height, int fps, OSD *osd, config_t *cfg) : QThread(0)
21 {
22         buffer_size=8 * 1024 * 224;
23         max_rate=4000 * 1000;
24         min_rate=0;
25         _width = width;
26         _height = height;
27         rec_fps = fps;
28         
29         p_osd = osd;
30         p_config = cfg;
31         p_logger = osd->get_logger();
32         
33         recording = false;
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;
39         stream_format = NULL;
40         audio_codec = video_codec = NULL;
41         raw_options_list = NULL;
42         
43         memset(&video_st, 0x00, sizeof(video_st));
44         memset(&audio_st, 0x00, sizeof(audio_st));
45 #endif
46
47         output_context = NULL;
48         
49         audio_data_queue.clear();
50         video_data_queue.clear();
51         
52         do_reset_encoder_options();
53
54         //video_queue_mutex = new QMutex(QMutex::Recursive);
55         totalSrcFrame = 0;
56         totalDstFrame = 0;
57         totalAudioFrame = 0;
58
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;
64         left_frames = 0;
65         req_close = false;
66         req_stop = false;
67         bRunThread = false;
68         
69 }
70
71 MOVIE_SAVER::~MOVIE_SAVER()
72 {
73         req_close = true;
74         if(recording) do_close_main();
75 }
76
77 QString MOVIE_SAVER::get_avio_version()
78 {
79 #if defined(__LIBAVIO_VERSION)
80         return QString::fromUtf8(__LIBAVIO_VERSION);
81 #else
82         return QString::fromUtf8("\0");
83 #endif
84 }
85
86 QString MOVIE_SAVER::ts2str(int64_t ts)
87 {
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));
92 #else
93         return QString::fromUtf8("");
94 #endif
95 }                  
96
97 QString MOVIE_SAVER::ts2timestr(int64_t ts, void *timebase)
98 {
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));
104 #else
105         return QString::fromUtf8("");
106 #endif
107 }                  
108
109 QString MOVIE_SAVER::err2str(int errnum)
110 {
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));
115 #else
116         return QString::fromUtf8("");
117 #endif
118 }                  
119
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)
122 {
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;
127
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(),
135                    pkt->stream_index);
136 #endif  
137 }
138
139
140 void MOVIE_SAVER::enqueue_video(int frames, int width, int height, QImage *p)
141 {
142 #if defined(USE_MOVIE_SAVER)
143         if(!recording) return;
144         if(p == NULL) return;
145         if(req_stop) 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();
153 #endif   
154 }
155
156 bool MOVIE_SAVER::dequeue_video(uint32_t *p)
157 {
158         //if(!recording) return false;
159         if(p == NULL) return false;
160
161         VIDEO_DATA *pp = video_data_queue.dequeue();
162 #if defined(USE_MOVIE_SAVER)
163         if(pp == NULL) return false;
164         int y;
165         uint32_t *pq;
166         QImage *pp_i = &(pp->frame_data);
167         left_frames = pp->frames;
168         _width = pp->_width;
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));
175                 }
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);
178         }
179 #else
180         video_size = 0;
181 #endif   
182         if(pp != NULL) delete pp;
183         return true;
184 }
185
186 void MOVIE_SAVER::enqueue_audio(int16_t *p, int size)
187 {
188 #if defined(USE_MOVIE_SAVER)
189         if(!recording) return;
190         if(p == NULL) return;
191         if(req_stop) 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++;
196 #endif   
197 }
198
199 bool MOVIE_SAVER::dequeue_audio(int16_t *p)
200 {
201         //if(!recording) return false;
202         if(audio_data_queue.isEmpty()) return false;
203         if(p == NULL) return false;
204         
205         QByteArray *pp = audio_data_queue.dequeue();
206         int16_t *q = (int16_t *)pp->constData();
207         int32_t tmpd;
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++) {
214                 tmpd = q[i];
215                 tmpd <<= 4;
216                 tmpd = tmpd / 3;
217                 tmpd >>= 3;
218                 p[i] = tmpd;
219         }
220         //memcpy(p, pp->constData(), audio_size);
221         audio_count++;
222 #else
223         audio_size = 0;
224 #endif   
225         if(pp != NULL) delete pp;
226         return true;
227 }
228
229
230 void MOVIE_SAVER::run()
231 {
232         bRunThread = true;
233         //out_debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "MOVIE THREAD: Start");
234         int ret;
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;
239         bool a_f, v_f;
240         a_f = v_f = false;
241         volatile bool old_recording = false;
242         audio_remain = 0;
243         video_remain = 0;
244         audio_offset = 0;
245         audio_frame_offset = 0;
246         video_offset = 0;
247         n_encode_audio = 0;
248         n_encode_video = 0;
249         req_close = false;
250         req_stop = false;
251         left_frames = 0;
252         while(bRunThread) {
253                 if(recording) {
254                         if(!bRunThread) break;
255                         if(!old_recording) {
256                                 //n_encode_video = n_encode_audio = 1;
257                                 audio_remain = 0;
258                                 video_remain = 0;
259                                 audio_offset = 0;
260                                 audio_frame_offset = 0;
261                                 video_offset = 0;
262                                 video_count = 0;
263                                 audio_count = 0;
264                                 req_close = false;
265                                 req_stop = false;
266                                 left_frames = 0;
267                                 if(!do_open_main()) {
268                                         recording = false;
269                                         goto _next_turn;
270                                 }
271                                 out_debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "MOVIE/Saver: Start to recording.");
272                                 old_recording = true;
273                                 a_f = v_f = false;
274                         }
275                         if(audio_remain <= 0) {
276                                 a_f = audio_data_queue.isEmpty();
277                                 if(a_f) goto _video;
278                                 dequeue_audio(audio_frame_buf);
279                                 audio_remain = audio_size;
280                                 audio_offset = 0;
281                                 need_audio_transcode = true;
282                         }
283                 _video:
284                         {
285                                 v_f = video_data_queue.isEmpty();
286                                 if(v_f)
287                                         goto _write_frame;
288                                 if(left_frames <= 0) dequeue_video(video_frame_buf);
289                                 left_frames--;
290                                 video_remain = video_size;
291                                 video_offset = 0;
292                                 need_video_transcode = true;
293                         }
294                 _write_frame:
295                         int result_ts = 0;
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);
301                                 } else {
302                                         result_ts = 1; // AUDIO ONLY
303                                 }
304                         } else {
305                                 if(video_st.context != NULL) {
306                                         result_ts = -1; // VIDEO ONLY
307                                 }
308                         }
309 #else
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);
312 #endif
313                         if ((n_encode_video == 0) &&
314                                 ((n_encode_audio != 0) ||
315                                  (result_ts <= 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.");
320                                         goto _final;
321                                 }
322                         } else {
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.");
327                                         goto _final;
328                                 }
329                         }
330                         if (ret < 0 && ret != AVERROR_EOF) {
331                                 char errbuf[128];
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);
334                                 goto _final;
335                         }
336                         
337                         /* dump report by using the output first video and audio streams */
338                         //print_report(0, timer_start, cur_time);
339                         if(req_close) {
340                                 do_close_main();
341                                 need_video_transcode = need_audio_transcode = false;
342                                 old_recording = false;
343                         }
344                 }
345         _next_turn:
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;
350                         continue;
351                 }
352                 if(fps_wait >= tmp_wait) {
353                         this->msleep(tmp_wait);
354                         tmp_wait = 0;
355                 } else {
356                         this->msleep(fps_wait);
357                         tmp_wait -= fps_wait;
358                 }
359                 if(tmp_wait <= 0) {
360                         fps_wait = (int)((1000.0 / (double)rec_fps) / 4.0);
361                         //fps_wait = 10;
362                         tmp_wait = fps_wait;
363                 }
364                 if(req_close) {
365                         do_close_main();
366                         old_recording = false;
367                 }
368                 continue;
369         _final:
370                 req_close = true;
371                 do_close_main();
372                 old_recording = false;
373         }
374         out_debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "MOVIE: Exit thread.");
375         if(recording) {
376                 req_close = true;
377                 do_close_main();
378         }
379 }
380
381 bool MOVIE_SAVER::is_recording(void)
382 {
383         return recording;
384 }
385
386
387 void MOVIE_SAVER::do_exit()
388 {
389         bRunThread = false;
390         req_close = true;
391         req_stop = true;
392 }
393
394 void MOVIE_SAVER::do_set_record_fps(int fps)
395 {
396         if((fps > 0) && (fps <= 75)) rec_fps = fps;
397 }
398
399 void MOVIE_SAVER::do_set_width(int width)
400 {
401         _width = width;
402 }
403
404 void MOVIE_SAVER::do_set_height(int height)
405 {
406         _height = height;
407 }
408
409 void MOVIE_SAVER::do_clear_options_list(void)
410 {
411         encode_opt_keys.clear();
412         encode_options.clear();
413 }
414
415 void MOVIE_SAVER::do_add_option(QString key, QString value)
416 {
417         if(key.isEmpty()) return;
418         encode_opt_keys.append(key);
419         if(value.isEmpty()) {
420                 encode_options.append(QString::fromUtf8(""));
421         } else {
422                 encode_options.append(value);
423         }
424 }               
425
426 void MOVIE_SAVER::do_set_video_bitrate(int kbps)
427 {
428         if(kbps < 10) return;
429         video_bit_rate = kbps * 1000;
430 }
431
432 void MOVIE_SAVER::do_set_audio_bitrate(int kbps)
433 {
434         if(kbps < 8) return;
435         audio_bit_rate = kbps * 1000;
436 }
437
438 void MOVIE_SAVER::do_set_video_geometry(QSize geometry)
439 {
440         if(geometry.width() < 100) return;
441         if(geometry.height() < 80) return;
442         video_geometry = geometry;
443 }
444
445 void MOVIE_SAVER::do_set_video_threads(int threads)
446 {
447         video_encode_threads = threads;
448 }
449
450 void MOVIE_SAVER::do_reset_encoder_options(void)
451 {
452         encode_opt_keys.clear();
453         encode_options.clear();
454
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"));
462         // Dummy
463         do_add_option(QString::fromUtf8("qmax"), QString::fromUtf8("24"));
464 }
465