OSDN Git Service

[Qt][MOVIE_SAVER] Fix choppy sound.
[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         bitrate = 2000 * 1000;
19         _width = width;
20         _height = height;
21         rec_fps = fps;
22         p_osd = osd;
23         recording = false;
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
40         video_data_queue.clear();
41         video_width_queue.clear();
42         video_height_queue.clear();
43         
44         encode_opt_keys.clear();
45         encode_options.clear();
46
47         totalSrcFrame = 0;
48         totalDstFrame = 0;
49         totalAudioFrame = 0;
50         bRunThread = false;
51 }
52
53 MOVIE_SAVER::~MOVIE_SAVER()
54 {
55         if(recording) do_close();
56 }
57
58 QString MOVIE_SAVER::ts2str(int64_t ts)
59 {
60 #if defined(USE_LIBAV)  
61         char buffer[AV_TS_MAX_STRING_SIZE + 16];
62         memset(buffer, 0x00, sizeof(buffer));
63         return QString::fromLocal8Bit(av_ts_make_string(buffer, ts));
64 #else
65         return QString::fromUtf8("");
66 #endif
67 }                  
68
69 QString MOVIE_SAVER::ts2timestr(int64_t ts, void *timebase)
70 {
71 #if defined(USE_LIBAV)  
72         char buffer[AV_TS_MAX_STRING_SIZE + 16];
73         AVRational *tb = (AVRational *)timebase;
74         memset(buffer, 0x00, sizeof(buffer));
75         return QString::fromLocal8Bit(av_ts_make_time_string(buffer, ts, tb));
76 #else
77         return QString::fromUtf8("");
78 #endif
79 }                  
80
81 QString MOVIE_SAVER::err2str(int errnum)
82 {
83 #if defined(USE_LIBAV)  
84         char buffer[AV_TS_MAX_STRING_SIZE + 16];
85         memset(buffer, 0x00, sizeof(buffer));
86         return QString::fromLocal8Bit(av_make_error_string(buffer, sizeof(buffer), errnum));
87 #else
88         return QString::fromUtf8("");
89 #endif
90 }                  
91
92 //void MOVIE_SAVER::log_packet(const AVFormatContext *fmt_ctx, const AVPacket *pkt)
93 void MOVIE_SAVER::log_packet(const void *_fmt_ctx, const void *_pkt)
94 {
95 #if defined(USE_LIBAV)  
96         const AVFormatContext *fmt_ctx = (const AVFormatContext *)_fmt_ctx;
97         const AVPacket *pkt = (const AVPacket *)_pkt;
98     AVRational *time_base = &fmt_ctx->streams[pkt->stream_index]->time_base;
99
100     AGAR_DebugLog(AGAR_LOG_DEBUG, "pts:%s pts_time:%s dts:%s dts_time:%s duration:%s duration_time:%s stream_index:%d\n",
101            ts2str(pkt->pts).toLocal8Bit().constData(),
102                    ts2timestr(pkt->pts, (void *)time_base).toLocal8Bit().constData(),
103            ts2str(pkt->dts).toLocal8Bit().constData(),
104                    ts2timestr(pkt->dts, (void *)time_base).toLocal8Bit().constData(),
105            ts2str(pkt->duration).toLocal8Bit().constData(),
106                    ts2timestr(pkt->duration, (void *)time_base).toLocal8Bit().constData(),
107            pkt->stream_index);
108 #endif  
109 }
110
111
112 void MOVIE_SAVER::enqueue_video(QByteArray *p, int width, int height)
113 {
114 #if defined(USE_MOVIE_SAVER)
115         if(!recording) return;
116         if(p == NULL) return;
117         QByteArray *pp = new QByteArray(p->data(), p->size());
118         AGAR_DebugLog(AGAR_LOG_DEBUG, "Movie: Enqueue video data %d bytes", p->size());
119         video_data_queue.enqueue(pp);
120         //video_width_queue.enqueue(width);
121         //video_height_queue.enqueue(height);
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         QByteArray *pp = video_data_queue.dequeue();
131 #if defined(USE_MOVIE_SAVER)
132         if(pp == NULL) return false;
133         
134         video_size = pp->size();
135         if(video_size <= 0) return false;
136         memcpy(p, pp->data(), video_size);
137         //AGAR_DebugLog(AGAR_LOG_DEBUG, "Movie: Dequeue video data %d bytes", pp->size());
138         //_width = video_width_queue.dequeue();
139         //_height = video_height_queue.dequeue();
140 #else
141         video_size = 0;
142 #endif   
143         if(pp != NULL) delete pp;
144         
145         return true;
146 }
147
148 void MOVIE_SAVER::enqueue_audio(int16_t *p, int size)
149 {
150 #if defined(USE_MOVIE_SAVER)
151         if(!recording) return;
152         if(p == NULL) return;
153         QByteArray *pp = new QByteArray((const char *)p, size);
154         //AGAR_DebugLog(AGAR_LOG_DEBUG, "Movie: Enqueue audio data %d bytes", size);
155         audio_data_queue.enqueue(pp);
156 #endif   
157 }
158
159 bool MOVIE_SAVER::dequeue_audio(int16_t *p)
160 {
161         if(!recording) return false;
162         if(audio_data_queue.isEmpty()) return false;
163         if(p == NULL) return false;
164         QByteArray *pp = audio_data_queue.dequeue();
165 #if defined(USE_MOVIE_SAVER)
166         if(pp == NULL) return false;
167         //AGAR_DebugLog(AGAR_LOG_DEBUG, "Movie: Dequeue audio data %d bytes", pp->size());
168
169         audio_size = pp->size();
170         if(audio_size <= 0) return false;
171         memcpy(p, pp->constData(), audio_size);
172 #else
173         audio_size = 0;
174 #endif   
175         if(pp != NULL) delete pp;
176         return true;
177 }
178
179
180 void MOVIE_SAVER::run()
181 {
182         bRunThread = true;
183         //AGAR_DebugLog(AGAR_LOG_DEBUG, "MOVIE THREAD: Start");
184     int ret;
185     int got_packet;
186     int dst_nb_samples;
187
188         int fps_wait = (int)((1000.0 / p_osd->vm_frame_rate()) / 8.0);
189         int tmp_wait = fps_wait;
190         int ncount_audio = 0;
191         int ncount_video = 0;
192         bool audio_continue = false;
193         bool video_continue = false;
194         bool need_audio_transcode = false;
195         bool need_video_transcode = false;
196         int i;
197     int64_t total_packets_written = 0;
198         int encode_audio = 0;
199         int encode_video = 0;
200         bool old_recording = false;
201         audio_remain = 0;
202         video_remain = 0;
203         audio_offset = 0;
204         audio_frame_offset = 0;
205         video_offset = 0;
206         
207         while(bRunThread) {
208                 if(recording) {
209                         if(!bRunThread) break;
210                         if(old_recording != recording) {
211                                 AGAR_DebugLog(AGAR_LOG_DEBUG, "MOVIE/Saver: Start to recording.");
212                                 encode_video = encode_audio = 1;
213                                 audio_remain = 0;
214                                 video_remain = 0;
215                                 audio_offset = 0;
216                                 audio_frame_offset = 0;
217                                 video_offset = 0;
218                         }
219                         if(audio_remain <= 0) {
220                                 if(audio_data_queue.isEmpty()) goto _video;
221                                 dequeue_audio(audio_frame_buf);
222                                 audio_remain = audio_size;
223                                 audio_offset = 0;
224                                 need_audio_transcode = true;
225                         }
226                 _video:
227                         {
228                                 //if(video_data_queue.isEmpty() || video_width_queue.isEmpty()
229                                 //   || video_height_queue.isEmpty())
230                                 if(video_data_queue.isEmpty())
231                                         goto _write_frame;
232                                 dequeue_video(video_frame_buf);
233                                 video_remain = video_size;
234                                 video_offset = 0;
235                                 need_video_transcode = true;
236                         }
237                 _write_frame:
238                         if ((encode_video == 0) &&
239                                 ((encode_audio != 0) || av_compare_ts(video_st.next_pts, video_st.st->codec->time_base,
240                                                                                                                 audio_st.next_pts, audio_st.st->codec->time_base) <= 0)) {
241                                 encode_video = write_video_frame();
242                                 if(encode_video < 0) {
243                                         AGAR_DebugLog(AGAR_LOG_DEBUG, "MOVIE/Saver: Something wrong with encoding video.");
244                                         goto _final;
245                                 }
246                         }  else {
247                                 if(encode_audio == 0) {
248                                         encode_audio = write_audio_frame();
249                                         if(encode_audio < 0) {
250                                                 AGAR_DebugLog(AGAR_LOG_DEBUG, "MOVIE/Saver: Something wrong with encoding audio.");
251                                                 goto _final;
252                                         }
253                                 }
254                         }
255                         if (ret < 0 && ret != AVERROR_EOF) {
256                                 char errbuf[128];
257                                 av_strerror(ret, errbuf, sizeof(errbuf));
258                                 AGAR_DebugLog(AGAR_LOG_INFO, "Error while filtering: %s\n", (const char *)errbuf);
259                                 goto _final;
260                         }
261                         
262                         /* dump report by using the output first video and audio streams */
263                         //print_report(0, timer_start, cur_time);
264                 }
265         _next_turn:
266                 old_recording = recording;
267                 if(!bRunThread) break;
268                 if(need_video_transcode || need_audio_transcode) {
269                         need_video_transcode = need_audio_transcode = false;
270                         continue;
271                 }
272                 if(fps_wait >= tmp_wait) {
273                         this->msleep(tmp_wait);
274                         tmp_wait = 0;
275                 } else {
276                         this->msleep(fps_wait);
277                         tmp_wait -= fps_wait;
278                 }
279                 if(tmp_wait <= 0) {
280                         fps_wait = (int)((1000.0 / p_osd->vm_frame_rate()) / 8.0);
281                         tmp_wait = fps_wait;
282                 }
283                 old_recording = recording;
284                 continue;
285         _final:
286                 old_recording = recording;
287                 do_close();
288                 old_recording = false;
289         }
290         AGAR_DebugLog(AGAR_LOG_DEBUG, "MOVIE: Exit thread.");
291         if(recording) do_close();
292 }
293
294 bool MOVIE_SAVER::is_recording(void)
295 {
296         return recording;
297 }
298
299
300 void MOVIE_SAVER::do_exit()
301 {
302         bRunThread = false;
303 }
304
305 void MOVIE_SAVER::do_set_record_fps(int fps)
306 {
307         if((fps > 0) && (fps <= 60)) rec_fps = fps;
308 }
309
310 void MOVIE_SAVER::do_set_width(int width)
311 {
312         //printf("width = %d -> %d\n", _width, width);
313         _width = width;
314 }
315
316 void MOVIE_SAVER::do_set_height(int height)
317 {
318         //printf("height = %d -> %d\n", _height, height);
319         _height = height;
320 }
321
322 void MOVIE_SAVER::do_clear_options_list(void)
323 {
324         encode_opt_keys.clear();
325         encode_options.clear();
326 }
327
328 void MOVIE_SAVER::do_add_option(QString key, QString value)
329 {
330         if(key.isEmpty()) return;
331         encode_opt_keys.append(key);
332         if(value.isEmpty()) {
333                 encode_options.append(QString::fromUtf8(""));
334         } else {
335                 encode_options.append(value);
336         }
337 }