OSDN Git Service

[Qt][MOVIE_SAVER] Add configure dialog.
[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 "agar_logger.h"
12
13 MOVIE_SAVER::MOVIE_SAVER(int width, int height, int fps, OSD *osd) : 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         recording = false;
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;
28         stream_format = NULL;
29         audio_codec = video_codec = NULL;
30         raw_options_list = NULL;
31         
32         memset(&video_st, 0x00, sizeof(video_st));
33         memset(&audio_st, 0x00, sizeof(audio_st));
34 #endif
35
36         output_context = NULL;
37         
38         audio_data_queue.clear();
39         video_data_queue.clear();
40         
41         do_reset_encoder_options();
42         totalSrcFrame = 0;
43         totalDstFrame = 0;
44         totalAudioFrame = 0;
45
46         video_bit_rate = 1500 * 1000;
47         audio_bit_rate = 160 * 1000;
48         video_geometry = QSize(640, 480);
49         video_encode_threads = 4;
50
51         bRunThread = false;
52 }
53
54 MOVIE_SAVER::~MOVIE_SAVER()
55 {
56         if(recording) do_close();
57 }
58
59 QString MOVIE_SAVER::ts2str(int64_t ts)
60 {
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));
65 #else
66         return QString::fromUtf8("");
67 #endif
68 }                  
69
70 QString MOVIE_SAVER::ts2timestr(int64_t ts, void *timebase)
71 {
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));
77 #else
78         return QString::fromUtf8("");
79 #endif
80 }                  
81
82 QString MOVIE_SAVER::err2str(int errnum)
83 {
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));
88 #else
89         return QString::fromUtf8("");
90 #endif
91 }                  
92
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)
95 {
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;
100
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(),
108            pkt->stream_index);
109 #endif  
110 }
111
112
113 void MOVIE_SAVER::enqueue_video(QImage *p)
114 {
115 #if defined(USE_MOVIE_SAVER)
116         if(!recording) return;
117         if(p == NULL) return;
118         uint32_t *pq;
119         QImage *pp = new QImage(*p);
120         //AGAR_DebugLog(AGAR_LOG_DEBUG, "Movie: Enqueue video data %d bytes", p->byteCount());
121         video_data_queue.enqueue(pp);
122 #endif   
123 }
124
125 bool MOVIE_SAVER::dequeue_video(uint32_t *p)
126 {
127         if(!recording) return false;
128         if(p == NULL) return false;
129
130         QImage *pp = video_data_queue.dequeue();
131 #if defined(USE_MOVIE_SAVER)
132         if(pp == NULL) return false;
133         int y;
134         int x;
135         uint32_t *pq;
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));
140         }
141         video_size = _width * (y + 1);
142         //AGAR_DebugLog(AGAR_LOG_DEBUG, "Movie: Dequeue video data %d bytes", pp->size());
143 #else
144         video_size = 0;
145 #endif   
146         if(pp != NULL) delete pp;
147         
148         return true;
149 }
150
151 void MOVIE_SAVER::enqueue_audio(int16_t *p, int size)
152 {
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);
159 #endif   
160 }
161
162 bool MOVIE_SAVER::dequeue_audio(int16_t *p)
163 {
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());
171
172         audio_size = pp->size();
173         if(audio_size <= 0) return false;
174         memcpy(p, pp->constData(), audio_size);
175 #else
176         audio_size = 0;
177 #endif   
178         if(pp != NULL) delete pp;
179         return true;
180 }
181
182
183 void MOVIE_SAVER::run()
184 {
185         bRunThread = true;
186         //AGAR_DebugLog(AGAR_LOG_DEBUG, "MOVIE THREAD: Start");
187     int ret;
188     int got_packet;
189     int dst_nb_samples;
190
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;
199         int i;
200     int64_t total_packets_written = 0;
201         int encode_audio = 0;
202         int encode_video = 0;
203         bool old_recording = false;
204         audio_remain = 0;
205         video_remain = 0;
206         audio_offset = 0;
207         audio_frame_offset = 0;
208         video_offset = 0;
209         
210         while(bRunThread) {
211                 if(recording) {
212                         if(!bRunThread) break;
213                         if(old_recording != recording) {
214                                 AGAR_DebugLog(AGAR_LOG_DEBUG, "MOVIE/Saver: Start to recording.");
215                                 encode_video = encode_audio = 1;
216                                 audio_remain = 0;
217                                 video_remain = 0;
218                                 audio_offset = 0;
219                                 audio_frame_offset = 0;
220                                 video_offset = 0;
221                         }
222                         if(audio_remain <= 0) {
223                                 if(audio_data_queue.isEmpty()) goto _video;
224                                 dequeue_audio(audio_frame_buf);
225                                 audio_remain = audio_size;
226                                 audio_offset = 0;
227                                 need_audio_transcode = true;
228                         }
229                 _video:
230                         {
231                                 //if(video_data_queue.isEmpty() || video_width_queue.isEmpty()
232                                 //   || video_height_queue.isEmpty())
233                                 if(video_data_queue.isEmpty())
234                                         goto _write_frame;
235                                 dequeue_video(video_frame_buf);
236                                 video_remain = video_size;
237                                 video_offset = 0;
238                                 need_video_transcode = true;
239                         }
240                 _write_frame:
241                         if ((encode_video == 0) &&
242                                 ((encode_audio != 0) || av_compare_ts(video_st.next_pts, video_st.st->codec->time_base,
243                                                                                                                 audio_st.next_pts, audio_st.st->codec->time_base) <= 0)) {
244                                 encode_video = write_video_frame();
245                                 if(encode_video < 0) {
246                                         AGAR_DebugLog(AGAR_LOG_DEBUG, "MOVIE/Saver: Something wrong with encoding video.");
247                                         goto _final;
248                                 }
249                         }  else {
250                                 if(encode_audio == 0) {
251                                         encode_audio = write_audio_frame();
252                                         if(encode_audio < 0) {
253                                                 AGAR_DebugLog(AGAR_LOG_DEBUG, "MOVIE/Saver: Something wrong with encoding audio.");
254                                                 goto _final;
255                                         }
256                                 }
257                         }
258                         if (ret < 0 && ret != AVERROR_EOF) {
259                                 char errbuf[128];
260                                 av_strerror(ret, errbuf, sizeof(errbuf));
261                                 AGAR_DebugLog(AGAR_LOG_INFO, "Error while filtering: %s\n", (const char *)errbuf);
262                                 goto _final;
263                         }
264                         
265                         /* dump report by using the output first video and audio streams */
266                         //print_report(0, timer_start, cur_time);
267                 }
268         _next_turn:
269                 old_recording = recording;
270                 if(!bRunThread) break;
271                 if(need_video_transcode || need_audio_transcode) {
272                         need_video_transcode = need_audio_transcode = false;
273                         continue;
274                 }
275                 if(fps_wait >= tmp_wait) {
276                         this->msleep(tmp_wait);
277                         tmp_wait = 0;
278                 } else {
279                         this->msleep(fps_wait);
280                         tmp_wait -= fps_wait;
281                 }
282                 if(tmp_wait <= 0) {
283                         fps_wait = (int)((1000.0 / p_osd->vm_frame_rate()) / 8.0);
284                         tmp_wait = fps_wait;
285                 }
286                 old_recording = recording;
287                 continue;
288         _final:
289                 old_recording = recording;
290                 do_close();
291                 old_recording = false;
292         }
293         AGAR_DebugLog(AGAR_LOG_DEBUG, "MOVIE: Exit thread.");
294         if(recording) do_close();
295 }
296
297 bool MOVIE_SAVER::is_recording(void)
298 {
299         return recording;
300 }
301
302
303 void MOVIE_SAVER::do_exit()
304 {
305         bRunThread = false;
306 }
307
308 void MOVIE_SAVER::do_set_record_fps(int fps)
309 {
310         if((fps > 0) && (fps <= 60)) rec_fps = fps;
311 }
312
313 void MOVIE_SAVER::do_set_width(int width)
314 {
315         //printf("width = %d -> %d\n", _width, width);
316         _width = width;
317 }
318
319 void MOVIE_SAVER::do_set_height(int height)
320 {
321         //printf("height = %d -> %d\n", _height, height);
322         _height = height;
323 }
324
325 void MOVIE_SAVER::do_clear_options_list(void)
326 {
327         encode_opt_keys.clear();
328         encode_options.clear();
329         //do_add_option(QString::fromUtf8("c:v"), QString::fromUtf8("libx264"));
330         //do_add_option(QString::fromUtf8("c:a"), QString::fromUtf8("libfaac"));
331 }
332
333 void MOVIE_SAVER::do_add_option(QString key, QString value)
334 {
335         if(key.isEmpty()) return;
336         encode_opt_keys.append(key);
337         if(value.isEmpty()) {
338                 encode_options.append(QString::fromUtf8(""));
339         } else {
340                 encode_options.append(value);
341         }
342 }               
343
344 void MOVIE_SAVER::do_set_video_bitrate(int kbps)
345 {
346         if(kbps < 10) return;
347         video_bit_rate = kbps * 1000;
348 }
349
350 void MOVIE_SAVER::do_set_audio_bitrate(int kbps)
351 {
352         if(kbps < 8) return;
353         audio_bit_rate = kbps * 1000;
354 }
355
356 void MOVIE_SAVER::do_set_video_geometry(QSize geometry)
357 {
358         if(geometry.width() < 100) return;
359         if(geometry.height() < 80) return;
360         video_geometry = geometry;
361 }
362
363 void MOVIE_SAVER::do_set_video_threads(int threads)
364 {
365         video_encode_threads = threads;
366 }
367
368 void MOVIE_SAVER::do_reset_encoder_options(void)
369 {
370         encode_opt_keys.clear();
371         encode_options.clear();
372
373         do_add_option(QString::fromUtf8("c:v"), QString::fromUtf8("libx264"));
374         do_add_option(QString::fromUtf8("c:a"), QString::fromUtf8("libfaac"));
375         do_add_option(QString::fromUtf8("preset"), QString::fromUtf8("slow"));
376         do_add_option(QString::fromUtf8("tune"), QString::fromUtf8("grain"));
377         //do_add_option(QString::fromUtf8("crf"), QString::fromUtf8("20"));
378         do_add_option(QString::fromUtf8("qmin"), QString::fromUtf8("16"));
379         do_add_option(QString::fromUtf8("qmax"), QString::fromUtf8("24"));
380         // Dummy
381         do_add_option(QString::fromUtf8("qmax"), QString::fromUtf8("24"));
382 }
383