OSDN Git Service

[Qt][MOVIE] Recorder: Initial. This still not working.
[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
12 MOVIE_SAVER::MOVIE_SAVER(int width, int height, int fps, OSD *osd) : QThread(0)
13 {
14         buffer_size=8 * 1024 * 224;
15         max_rate=4000 * 1000;
16         min_rate=0;
17         bitrate = 2000 * 1000;
18         _width = width;
19         _height = height;
20         rec_fps = fps;
21         p_osd = osd;
22         recording = false;
23
24         audio_data_queue.clear();
25
26         video_data_queue.clear();
27         video_width_queue.clear();
28         video_height_queue.clear();
29         
30         totalSrcFrame = 0;
31         totalDstFrame = 0;
32         totalAudioFrame = 0;
33         bRunThread = false;
34 }
35
36 MOVIE_SAVER::~MOVIE_SAVER()
37 {
38         do_close();
39 }
40
41 void MOVIE_SAVER::enqueue_video(QByteArray *p, int width, int height)
42 {
43         if(!recording) return false;
44         if(p == NULL) return;
45         QByteArray *pp = new QByteArray(p->data(), p->size());
46         
47         video_data_queue.enqueue(pp);
48         video_width_queue.enqueue(width);
49         video_height_queue.enqueue(height);
50 }
51
52 bool MOVIE_SAVER::dequeue_video(uint32_t *p)
53 {
54         if(!recording) return false;
55         if(video_data_queue.isEmpty()) return false;
56         if(p == NULL) return false;
57
58         QByteArray *pp = video_data_queue.dequeue();
59         if((pp == NULL) || (video_size <= 0)) return false;
60         
61         video_size = pp->size();
62         memcpy(p, pp->data(), video_size);
63         delete pp;
64         
65         return true;
66 }
67
68 void MOVIE_SAVER::enqueue_audio(QByteArray *p)
69 {
70         if(!recording) return;
71         if(p == NULL) return;
72         QByteArray *pp = new QByteArray(p->data(), p->size());
73         audio_data_queue.enqueue(pp);
74 }
75
76 bool MOVIE_SAVER::dequeue_audio(int16_t *p)
77 {
78         if(!recording) return false;
79         if(audio_data_queue.isEmpty()) return false;
80         if(p == NULL) return false;
81
82         QByteArray *pp = audio_data_queue.dequeue();
83         if((pp == NULL) || (audio_size <= 0)) return false;
84
85         audio_size = pp->size();
86         memcpy(p, pp->data(), audio_size);
87         delete pp;
88         return true;
89 }
90
91 void MOVIE_SAVER::run()
92 {
93         bRunThread = true;
94         
95         int fps_wait = (int)((1000.0 / this->vm_frame_rate()) / 2.0);
96         int tmp_wait = fps_wait;
97         while(bRunThread) {
98                 if(recording) {
99                         if(!bRunThread) break;
100                         if(!recording) break;
101                         if(!audio_data_queue.isEmpty()) {
102                                 if(dequeue_audio(audio_frame)) {
103                                         AVPacket pkt;
104                                         uint64_t bytes = audio_size;
105                                         uint64_t us = (uint64_t)floor(((double)bytes * 1000000.0) / (double)audio_codec->sample_rate);
106                                         double samples = ((double)us / 1000000.0) * (double)audio_codec->sample_rate()   
107                                         int ret;
108                                         
109                                         if(bytes == 0) goto _video;
110                                         
111                                         av_init_packet(&pkt);
112                                         pkt.dts = pkt.pts = (int64_t)samples;
113                                         pkt.flags |= AV_PKT_FLAG_KEY;
114                                         pkt.data = (uint8_t *)audio_frame;
115                                         pkt.size = (uint32_t)bytes;
116                                         pkt.stream_index = 1;
117                                         ret = av_write_frame(output_context, &pkt);
118                                 
119                                         totalAudioFrame++;
120                                 }
121                         }
122                 _video:
123 #if 0 // ToDo                   
124                         if(!bRunThread) break;
125                         if(!recording) break;
126                         if(!video_data_queue.isEmpty() &&
127                            !video_height_queue.isEmpty() && !video_width_queue.isEmpty()) {
128                                 // Scale Video
129                                 if(dequeue_video(video_frame)) {
130                                         AVRational fps = {1, 1000000};
131                                         AVPacket pkt;
132                                         uint64_t bytes = video_size;
133                                         int i;
134                                         av_init_packet(&pkt);
135                                         // Call encoder
136                                         
137                                         // Call muxer
138                                         
139                                         totalSrcFrame++;
140                                         totalDstFrame++;
141                                 }
142                         }
143 #endif                  
144                 }
145                 if(fps_wait >= tmp_wait) {
146                         this->msleep(tmp_wait);
147                         tmp_wait = 0;
148                 } else {
149                         this->msleep(fps_wait);
150                         tmp_wait -= fps_wait;
151                 }
152                 if(tmp_wait <= 0) {
153                         fps_wait = (int)((1000.0 / this->vm_frame_rate()) / 2.0);
154                         tmp_wait = fps_wait;
155                 }
156         }
157 }
158
159 void MOVIE_SAVER::do_close()
160 {
161         if(output_context != NULL) {
162                 av_write_trailer(output_context);
163                 avio_close(output_context->pb);
164         }
165         if(audio_stream != NULL) {
166                 av_free(audio_stream);
167         }
168         if(video_stream != NULL) {
169                 av_free(video_stream);
170         }
171         audio_stream = NULL;
172         video_stream = NULL;
173
174         if(output_context != NULL) {
175                 av_free(output_context);
176                 output_context = NULL;
177         }
178         recording = false;
179
180         audio_data_queue.clear();
181
182         video_data_queue.clear();
183         video_width_queue.clear();
184         video_height_queue.clear();
185
186         // Message
187         AGAR_DebugLog(AGAR_LOG_DEBUG, "MOVIE: Close: Read:   Video %ll frames, Audio %ll frames", totalSrcFrame, totalAudioFrame);
188         AGAR_DebugLog(AGAR_LOG_DEBUG, "MOVIE: Close: Write:  Video %ll frames, Audio %ll frames", totalDstFrame, totalAudioFrame);
189         totalSrcFrame = 0;
190         totalDstFrame = 0;
191         totalAudioFrame = 0;
192 }
193
194 QString MOVIE_PLAYER::create_date_file_name(void)
195 {
196         QDateTime nowTime = QDateTime::currentDateTime();
197         QString tmps = nowTime.toString(QString::fromUtf8("yyyy-MM-dd_hh-mm-ss.zzz."));
198         return tmps;
199 }
200
201 bool MOVIE_SAVER::is_recording(void)
202 {
203         return recording;
204 }
205 static const AVRational time_base_15 = (AVRational){1001, 14485};
206 static const AVRational time_base_24 = (AVRational){1001, 23976};
207 static const AVRational time_base_25 = (AVRational){1001, 25025};
208 static const AVRational time_base_30 = (AVRational){1001, 29970};
209 static const AVRational time_base_60 = (AVRational){1001, 59940};
210
211 void MOVIE_SAVER::do_open(QString filename)
212 {
213         cur_time_t *t_time;
214         do_close();
215
216         _filename = filename;
217         format = av_guess_format("mp4", NULL, NULL);
218
219         if(format == NULL) {
220                 AGAR_DebugLog(AGAR_LOG_DEBUG, "AVC ERROR: Failed to initialize libavf");
221                 return;
222         }
223         
224         output_context = avformat_alloc_context();
225         if(output_context == NULL) {
226                 AGAR_DebugLog(AGAR_LOG_DEBUG, "AVC ERROR: Failed to get output_context");
227                 do_close();
228                 return;
229         }
230         
231         output_context->oformat = format;
232         snprintf(output_context->filename, 1000, "file://%s", filename.fromUtf8().constData());
233         AGAR_DebugLog(AGAR_LOG_DEBUG, "Start rec VIDEO: %s", output_context->filename);
234
235 #if 0 // Todo   
236         video_stream = av_new_stream(output_context, 0);
237         if(video_stream == NULL) {
238                 AGAR_DebugLog(AGAR_LOG_DEBUG, "AVC ERROR: Failed to open video stream");
239                 do_close();
240                 return;
241         }
242
243         get_host_time(t_time);
244         video_codec = video_stream->codec;
245         video_codec->gop_size = 15;
246         video_codec->max_b_frames = 8;
247         video_codec->has_b_frames = 1;
248         QString author = QString::fromUtf8("emu");
249         author = author + osd->get_vm_config_name();
250         QString date_str = QString::fromUtf8("Record from");
251         date_str = date_str + create_date_file_name();
252         
253         { // Video Start
254                 av_dict_set(&output_context->metadata, "title", date_str.fromUtf8().constData(), 0);
255                 av_dict_set(&output_context->metadata, "author", author.fromUtf8().constData(), 0);
256                 video_codec->has_b_frames=2; // let muxer know we may have bpyramid
257                 video_codec->codec_id = CODEC_ID_H264;
258                 video_codec->codec = &codec_real;
259                 memset(video_codec->codec, 0, sizeof(struct AVCodec));
260                 strcpy(video_codec->codec->name, "H264");
261                 {
262                         // Set basic rate
263                         video_codec->rc_buffer_size = buffer_size;
264                         video_codec->rc_max_rate = max_rate;
265                         video_codec->rc_min_rate = min_rate;
266                         video_codec->bit_rate = bitrate;
267                 }
268                 video_codec->codec_type = AVMEDIA_TYPE_VIDEO;
269                 video_codec->flags = CODEC_FLAG_QSCALE;
270                 video_codec->width = _width;
271                 video_codec->height = _height;
272         }
273
274         switch(rec_fps) {
275         case 15:
276                 video_codec->time_base = time_base_15;
277                 video_codec->bit_rate = bitrate / 2;
278                 video_codec->rc_max_rate = max_rate / 2;
279                 video_codec->rc_min_rate = min_rate / 2;
280                 break;
281         case 24:
282                 video_codec->time_base = time_base_24;
283                 break;
284         case 25:
285                 video_codec->time_base = time_base_25;
286                 break;
287         case 30:
288                 video_codec->time_base = time_base_30;
289                 break;
290         case 60:
291                 video_codec->time_base = time_base_60;
292                 video_codec->bit_rate = bitrate * 2;
293                 video_codec->rc_max_rate = max_rate * 2;
294                 video_codec->rc_min_rate = min_rate * 2;
295                 break;
296         default:
297                 time_base = (AVRational){1001, rec_fps * 1000};
298                 video_codec->time_base = time_base;
299                 video_codec->bit_rate = (int)((double)bitrate * 30.0 / (double)rec_fps);
300                 video_codec->rc_max_rate = (int)((double)max_rate * 30.0 / (double)rec_fps);
301                 video_codec->rc_min_rate = (int)((double)min_rate * 30.0 / (double)rec_fps);
302                 break;
303         }
304 #endif // ToDo
305         
306         //audio_stream = av_new_stream(output_context, 1);
307         audio_stream = av_new_stream(output_context, 0);
308         if(audio_stream == NULL) {
309                 AGAR_DebugLog(AGAR_LOG_DEBUG, "AVC ERROR: Failed to open audio stream");
310                 do_close();
311                 return;
312         }
313
314         // Temporally using libAV's AAC
315         audio_codec = audio_stream->codec;
316         audio_codec->frame_size = 1024; 
317         audio_codec->codec_id = CODEC_ID_AAC;
318         audio_codec->sample_rate = osd->get_sound_rate();
319
320         audio_codec->codec_type = AVMEDIA_TYPE_AUDIO;
321         
322         audio_codec->bit_rate = audio_codec->sample_rate * 8 * 2;
323         audio_codec->rc_buffer_size = audio_codec->sample_rate / 4; // 250 ms worth
324         audio_codec->channels = 2;
325
326         // Context
327         output_context->mux_rate = 100080 * 1000;
328         output_context->pre_load = AV_TIME_BASE / 10;
329         output_context->max_delay = 100 * 1000; // MAX 100ms delay;
330
331         if(avio_open(&(output_context->pb), filename.fromUtf8().constData(), AVIO_FLAG_WRITE) < 0) {
332                 AGAR_DebugLog(AGAR_LOG_DEBUG, "AVC ERROR: Failed to open file");
333                 do_close();
334                 return;
335         }               
336
337         av_dump_format(output_context, 0, filename.fromUtf8().constData(), 1);
338         AGAR_DebugLog(AGAR_LOG_DEBUG, "Successfully opened AVC stream.");
339                 
340         recording = true;
341 }
342
343 void MOVIE_SAVER::do_exit()
344 {
345         bRunThread = false;
346 }
347
348 void MOVIE_SAVER::do_set_record_fps(int fps)
349 {
350         if((fps > 0) && (fps <= 60)) rec_fps = fps;
351 }
352
353 void MOVIE_SAVER::do_set_width(int width)
354 {
355         _width = width;
356 }
357
358 void MOVIE_SAVER::do_set_height(int height)
359 {
360         _height = height;
361 }
362