OSDN Git Service

[SOUND][Qt][MOVIE_SAVER] WIP: Fixing appending unneeded sound data.
[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 %dx%d", pp->byteCount(), pp->width(), pp->height());
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         bool old_recording = false;
202         audio_remain = 0;
203         video_remain = 0;
204         audio_offset = 0;
205         audio_frame_offset = 0;
206         video_offset = 0;
207         n_encode_audio = 0;
208         n_encode_video = 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                                 n_encode_video = n_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())
232                                         goto _write_frame;
233                                 dequeue_video(video_frame_buf);
234                                 video_remain = video_size;
235                                 video_offset = 0;
236                                 need_video_transcode = true;
237                         }
238                 _write_frame:
239                         if ((n_encode_video == 0) &&
240                                 ((n_encode_audio != 0) || av_compare_ts(video_st.next_pts, video_st.st->codec->time_base,
241                                                                                                                 audio_st.next_pts, audio_st.st->codec->time_base) <= 0)) {
242                                 n_encode_video = write_video_frame();
243                                 if(n_encode_video < 0) {
244                                         AGAR_DebugLog(AGAR_LOG_DEBUG, "MOVIE/Saver: Something wrong with encoding video.");
245                                         goto _final;
246                                 }
247                         } else {
248                                 if(n_encode_audio == 0) {
249                                         n_encode_audio = write_audio_frame();
250                                         if(n_encode_audio < 0) {
251                                                 AGAR_DebugLog(AGAR_LOG_DEBUG, "MOVIE/Saver: Something wrong with encoding audio.");
252                                                 goto _final;
253                                         }
254                                 }
255                         }
256                         if (ret < 0 && ret != AVERROR_EOF) {
257                                 char errbuf[128];
258                                 av_strerror(ret, errbuf, sizeof(errbuf));
259                                 AGAR_DebugLog(AGAR_LOG_INFO, "Error while filtering: %s\n", (const char *)errbuf);
260                                 goto _final;
261                         }
262                         
263                         /* dump report by using the output first video and audio streams */
264                         //print_report(0, timer_start, cur_time);
265                 }
266         _next_turn:
267                 old_recording = recording;
268                 if(!bRunThread) break;
269                 if(need_video_transcode || need_audio_transcode) {
270                         need_video_transcode = need_audio_transcode = false;
271                         continue;
272                 }
273                 if(fps_wait >= tmp_wait) {
274                         this->msleep(tmp_wait);
275                         tmp_wait = 0;
276                 } else {
277                         this->msleep(fps_wait);
278                         tmp_wait -= fps_wait;
279                 }
280                 if(tmp_wait <= 0) {
281                         fps_wait = (int)((1000.0 / p_osd->vm_frame_rate()) / 8.0);
282                         //fps_wait = 10;
283                         tmp_wait = fps_wait;
284                 }
285                 old_recording = recording;
286                 continue;
287         _final:
288                 old_recording = recording;
289                 do_close();
290                 old_recording = false;
291         }
292         AGAR_DebugLog(AGAR_LOG_DEBUG, "MOVIE: Exit thread.");
293         if(recording) do_close();
294 }
295
296 bool MOVIE_SAVER::is_recording(void)
297 {
298         return recording;
299 }
300
301
302 void MOVIE_SAVER::do_exit()
303 {
304         bRunThread = false;
305 }
306
307 void MOVIE_SAVER::do_set_record_fps(int fps)
308 {
309         if((fps > 0) && (fps <= 75)) rec_fps = fps;
310 }
311
312 void MOVIE_SAVER::do_set_width(int width)
313 {
314         //printf("width = %d -> %d\n", _width, width);
315         _width = width;
316 }
317
318 void MOVIE_SAVER::do_set_height(int height)
319 {
320         //printf("height = %d -> %d\n", _height, height);
321         _height = height;
322 }
323
324 void MOVIE_SAVER::do_clear_options_list(void)
325 {
326         encode_opt_keys.clear();
327         encode_options.clear();
328         //do_add_option(QString::fromUtf8("c:v"), QString::fromUtf8("libx264"));
329         //do_add_option(QString::fromUtf8("c:a"), QString::fromUtf8("libfaac"));
330 }
331
332 void MOVIE_SAVER::do_add_option(QString key, QString value)
333 {
334         if(key.isEmpty()) return;
335         encode_opt_keys.append(key);
336         if(value.isEmpty()) {
337                 encode_options.append(QString::fromUtf8(""));
338         } else {
339                 encode_options.append(value);
340         }
341 }               
342
343 void MOVIE_SAVER::do_set_video_bitrate(int kbps)
344 {
345         if(kbps < 10) return;
346         video_bit_rate = kbps * 1000;
347 }
348
349 void MOVIE_SAVER::do_set_audio_bitrate(int kbps)
350 {
351         if(kbps < 8) return;
352         audio_bit_rate = kbps * 1000;
353 }
354
355 void MOVIE_SAVER::do_set_video_geometry(QSize geometry)
356 {
357         if(geometry.width() < 100) return;
358         if(geometry.height() < 80) return;
359         video_geometry = geometry;
360 }
361
362 void MOVIE_SAVER::do_set_video_threads(int threads)
363 {
364         video_encode_threads = threads;
365 }
366
367 void MOVIE_SAVER::do_reset_encoder_options(void)
368 {
369         encode_opt_keys.clear();
370         encode_options.clear();
371
372         do_add_option(QString::fromUtf8("c:v"), QString::fromUtf8("libx264"));
373         do_add_option(QString::fromUtf8("c:a"), QString::fromUtf8("libfaac"));
374         do_add_option(QString::fromUtf8("preset"), QString::fromUtf8("slow"));
375         do_add_option(QString::fromUtf8("tune"), QString::fromUtf8("grain"));
376         //do_add_option(QString::fromUtf8("crf"), QString::fromUtf8("20"));
377         do_add_option(QString::fromUtf8("qmin"), QString::fromUtf8("16"));
378         do_add_option(QString::fromUtf8("qmax"), QString::fromUtf8("24"));
379         // Dummy
380         do_add_option(QString::fromUtf8("qmax"), QString::fromUtf8("24"));
381 }
382