OSDN Git Service

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