2 * Common Source Code Project for Qt : movie saver.
3 * (C) 2016 K.Ohta <whatisthis.sowhat _at_ gmail.com>
5 * History: May 27, 2016 : Initial. This refer from avidemux 2.5.6 .
9 #include "movie_saver.h"
11 #include "agar_logger.h"
13 MOVIE_SAVER::MOVIE_SAVER(int width, int height, int fps, OSD *osd) : QThread(0)
15 buffer_size=8 * 1024 * 224;
18 bitrate = 2000 * 1000;
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;
29 audio_codec = video_codec = NULL;
30 raw_options_list = NULL;
32 memset(&video_st, 0x00, sizeof(video_st));
33 memset(&audio_st, 0x00, sizeof(audio_st));
36 output_context = NULL;
38 audio_data_queue.clear();
40 video_data_queue.clear();
41 video_width_queue.clear();
42 video_height_queue.clear();
44 encode_opt_keys.clear();
45 encode_options.clear();
53 MOVIE_SAVER::~MOVIE_SAVER()
55 if(recording) do_close();
58 QString MOVIE_SAVER::ts2str(int64_t ts)
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));
65 return QString::fromUtf8("");
69 QString MOVIE_SAVER::ts2timestr(int64_t ts, void *timebase)
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));
77 return QString::fromUtf8("");
81 QString MOVIE_SAVER::err2str(int errnum)
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));
88 return QString::fromUtf8("");
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)
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;
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(),
112 void MOVIE_SAVER::enqueue_video(QByteArray *p, int width, int height)
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);
125 bool MOVIE_SAVER::dequeue_video(uint32_t *p)
127 if(!recording) return false;
128 if(p == NULL) return false;
130 QByteArray *pp = video_data_queue.dequeue();
131 #if defined(USE_MOVIE_SAVER)
132 if(pp == NULL) return false;
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();
143 if(pp != NULL) delete pp;
148 void MOVIE_SAVER::enqueue_audio(int16_t *p, int size)
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);
159 bool MOVIE_SAVER::dequeue_audio(int16_t *p)
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());
169 audio_size = pp->size();
170 if(audio_size <= 0) return false;
171 memcpy(p, pp->constData(), audio_size);
175 if(pp != NULL) delete pp;
180 void MOVIE_SAVER::run()
183 //AGAR_DebugLog(AGAR_LOG_DEBUG, "MOVIE THREAD: Start");
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;
197 int64_t total_packets_written = 0;
198 int encode_audio = 0;
199 int encode_video = 0;
200 bool old_recording = false;
204 audio_frame_offset = 0;
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;
216 audio_frame_offset = 0;
219 if(audio_remain <= 0) {
220 if(audio_data_queue.isEmpty()) goto _video;
221 dequeue_audio(audio_frame_buf);
222 audio_remain = audio_size;
224 need_audio_transcode = true;
228 //if(video_data_queue.isEmpty() || video_width_queue.isEmpty()
229 // || video_height_queue.isEmpty())
230 if(video_data_queue.isEmpty())
232 dequeue_video(video_frame_buf);
233 video_remain = video_size;
235 need_video_transcode = true;
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.");
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.");
255 if (ret < 0 && ret != AVERROR_EOF) {
257 av_strerror(ret, errbuf, sizeof(errbuf));
258 AGAR_DebugLog(AGAR_LOG_INFO, "Error while filtering: %s\n", (const char *)errbuf);
262 /* dump report by using the output first video and audio streams */
263 //print_report(0, timer_start, cur_time);
266 old_recording = recording;
267 if(!bRunThread) break;
268 if(need_video_transcode || need_audio_transcode) {
269 need_video_transcode = need_audio_transcode = false;
272 if(fps_wait >= tmp_wait) {
273 this->msleep(tmp_wait);
276 this->msleep(fps_wait);
277 tmp_wait -= fps_wait;
280 fps_wait = (int)((1000.0 / p_osd->vm_frame_rate()) / 8.0);
283 old_recording = recording;
286 old_recording = recording;
288 old_recording = false;
290 AGAR_DebugLog(AGAR_LOG_DEBUG, "MOVIE: Exit thread.");
291 if(recording) do_close();
294 bool MOVIE_SAVER::is_recording(void)
300 void MOVIE_SAVER::do_exit()
305 void MOVIE_SAVER::do_set_record_fps(int fps)
307 if((fps > 0) && (fps <= 60)) rec_fps = fps;
310 void MOVIE_SAVER::do_set_width(int width)
312 //printf("width = %d -> %d\n", _width, width);
316 void MOVIE_SAVER::do_set_height(int height)
318 //printf("height = %d -> %d\n", _height, height);
322 void MOVIE_SAVER::do_clear_options_list(void)
324 encode_opt_keys.clear();
325 encode_options.clear();
328 void MOVIE_SAVER::do_add_option(QString key, QString value)
330 if(key.isEmpty()) return;
331 encode_opt_keys.append(key);
332 if(value.isEmpty()) {
333 encode_options.append(QString::fromUtf8(""));
335 encode_options.append(value);