OSDN Git Service

[VM][FMTOWNS][MEMORY] Fix setup around memory banks by I/O 0404h and 0480h.
[csp-qt/common_source_project-fm7.git] / source / src / qt / avio / movie_saver_fileio.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
10 #include "./csp_avio_basic.h"
11 #include "movie_saver.h"
12 #include "../osd.h"
13 #include "csp_logger.h"
14
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include <math.h>
19 #if defined(USE_LIBAV)
20 extern "C" {
21         #include <libavutil/avassert.h>
22         #include <libavutil/channel_layout.h>
23         #include <libavutil/opt.h>
24         #include <libavutil/mathematics.h>
25         #include <libavutil/timestamp.h>
26         #include <libavformat/avformat.h>
27         #include <libswscale/swscale.h>
28         #include <libswresample/swresample.h>
29 }
30         #if (LIBAVCODEC_VERSION_MAJOR > 56)
31         #define AVCODEC_UPPER_V56
32         #endif
33 #endif
34
35 //int MOVIE_SAVER::write_frame(AVFormatContext *fmt_ctx, const AVRational *time_base, AVStream *st, AVPacket *pkt)
36 int MOVIE_SAVER::write_frame(void *_fmt_ctx, const void *_time_base, void *_st, void *_pkt)
37 {
38 #if defined(USE_LIBAV)
39         AVFormatContext *fmt_ctx = (AVFormatContext *)_fmt_ctx;
40         const AVRational *time_base = (const AVRational *)_time_base;
41         AVStream *st = (AVStream *)_st;
42         AVPacket *pkt = (AVPacket *)_pkt;
43         /* rescale output packet timestamp values from codec to stream timebase */
44         av_packet_rescale_ts(pkt, *time_base, st->time_base);
45         pkt->stream_index = st->index;
46
47         /* Write the compressed frame to the media file. */
48         //log_packet(fmt_ctx, pkt);
49         return av_interleaved_write_frame(fmt_ctx, pkt);
50 #else
51         return 0;
52 #endif
53 }
54
55 /* Add an output stream. */
56 bool MOVIE_SAVER::add_stream(void *_ost, void *_oc,
57                                            void **_codec,
58                                            uint64_t _codec_id)
59 {
60 #if defined(USE_LIBAV)
61         AVCodecContext *c;
62         OutputStream *ost = (OutputStream *)_ost;
63         AVFormatContext *oc = (AVFormatContext *)_oc;
64         AVCodec **codec = (AVCodec **)_codec;
65         enum AVCodecID codec_id = (enum AVCodecID)_codec_id;
66         /* find the encoder */
67         *codec = (AVCodec *)avcodec_find_encoder(codec_id);
68         if (!(*codec)) {
69                 out_debug_log(CSP_LOG_INFO, CSP_LOG_TYPE_MOVIE_SAVER, "Could not find encoder for '%s'\n",
70                                 (const char *)avcodec_get_name(codec_id));
71                 return false;
72         }
73
74         ost->st = avformat_new_stream(oc, *codec);
75         if (!ost->st) {
76                 out_debug_log(CSP_LOG_INFO, CSP_LOG_TYPE_MOVIE_SAVER, "Could not allocate stream\n");
77                 return false;
78         }
79         ost->st->id = oc->nb_streams-1;
80 #ifdef AVCODEC_UPPER_V56
81         ost->context = avcodec_alloc_context3(*codec);
82         if (ost->context == NULL) {
83                 out_debug_log(CSP_LOG_INFO, CSP_LOG_TYPE_MOVIE_SAVER, "Failed to allocate context for encoding: \n");
84                 return false;
85         }
86 #endif
87 #ifdef AVCODEC_UPPER_V56
88         AVCodecParameters  *cp = ost->st->codecpar;
89         AVRational ratio;
90         ratio.num = 1;
91         ratio.den = 1;
92         switch ((*codec)->type) {
93         case AVMEDIA_TYPE_AUDIO:
94                 cp->codec_type = AVMEDIA_TYPE_AUDIO;
95                 cp->codec_id = codec_id;
96                 cp->format = (*codec)->sample_fmts ? (*codec)->sample_fmts[0] : AV_SAMPLE_FMT_FLTP;
97                 cp->bit_rate = audio_bit_rate;
98                 cp->channel_layout = AV_CH_LAYOUT_STEREO;
99                 cp->channels = 2;
100                 cp->sample_rate = audio_sample_rate;
101                 cp->frame_size = 1024;
102                 break;
103         case AVMEDIA_TYPE_VIDEO:
104                 cp->codec_type = AVMEDIA_TYPE_VIDEO;
105                 cp->codec_id = codec_id;
106                 cp->format = STREAM_PIX_FMT;
107                 cp->bit_rate = video_bit_rate;
108                 cp->width       = video_geometry.width();
109                 cp->height   = video_geometry.height();
110 //              cp->profile = ;
111 //              cp->level = ;
112                 cp->sample_aspect_ratio = ratio;
113                 cp->field_order = AV_FIELD_PROGRESSIVE;
114                 break;
115         default:
116                 break;
117         }
118         avcodec_parameters_to_context(ost->context, cp);
119         c = ost->context;
120 #else
121         c = ost->st->codec;
122 #endif
123         switch ((*codec)->type) {
124         case AVMEDIA_TYPE_AUDIO:
125                 setup_audio(c, (void **)codec);
126                 ost->st->time_base = (AVRational){ 1, c->sample_rate };
127                 c->time_base = (AVRational){ 1, c->sample_rate };
128                 break;
129         case AVMEDIA_TYPE_VIDEO:
130                 c->codec_id = codec_id;
131                 c->bit_rate = video_bit_rate;
132                 // See:
133                 // https://libav.org/avconv.html#Video-Options
134                 /* Resolution must be a multiple of two. */
135                 c->width        = video_geometry.width();
136                 c->height   = video_geometry.height();
137                 c->thread_count  = video_encode_threads;
138                 
139                 /* timebase: This is the fundamental unit of time (in seconds) in terms
140                  * of which frame timestamps are represented. For fixed-fps content,
141                  * timebase should be 1/framerate and timestamp increments should be
142                  * identical to 1. */
143                 ost->st->time_base = (AVRational){ 1, rec_fps};
144                 c->time_base       = ost->st->time_base;
145
146                 //c->gop_size     = rec_fps; /* emit one intra frame every one second */
147                 c->pix_fmt         = STREAM_PIX_FMT;
148                 #ifndef AVCODEC_UPPER_V56
149                 if (c->codec_id == AV_CODEC_ID_MPEG2VIDEO) {
150                         /* just for testing, we also add B frames */
151                         c->max_b_frames = 2;
152                         c->gop_size  = rec_fps; /* emit one intra frame every one second */
153                 }
154                 if (c->codec_id == AV_CODEC_ID_MPEG4) {
155                         setup_mpeg4((void *)c);
156                 }
157                 if (c->codec_id == AV_CODEC_ID_MPEG1VIDEO) {
158                         /* Needed to avoid using macroblocks in which some coeffs overflow.
159                          * This does not happen with normal video, it just happens here as
160                          * the motion of the chroma plane does not match the luma plane. */
161                         c->mb_decision = 2;
162                 }
163                 if (c->codec_id == AV_CODEC_ID_H264) {
164                         setup_h264((void *)c);
165                 }
166                 #else
167                 {
168                         int __type = CSP_AVIO_BASIC::csp_avio_get_codec_type((_TCHAR*)avcodec_get_name(c->codec_id));
169                         switch(__type) {
170                         case CSP_AVIO_BASIC::TYPE_MPEG1:
171                                 c->mb_decision = 2;
172                                 break;
173                         /* just for testing, we also add B frames */
174                         case CSP_AVIO_BASIC::TYPE_MPEG2:
175                                 c->max_b_frames = 2;
176                                 c->gop_size  = rec_fps; /* emit one intra frame every one second */
177                                 break;
178                         case CSP_AVIO_BASIC::TYPE_MPEG4:
179                                 setup_mpeg4((void *)c);
180                                 break;
181                         case CSP_AVIO_BASIC::TYPE_H264:
182                         case CSP_AVIO_BASIC::TYPE_LIBX264:
183                                 setup_h264((void *)c);
184                                 break;
185                         default:
186                                 break;
187                         }
188                 }
189                 #endif
190         break;
191
192         default:
193                 break;
194         }
195
196         /* Some formats want stream headers to be separate. */
197         if (oc->oformat->flags & AVFMT_GLOBALHEADER)
198                 c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
199         return true;
200 #else
201         return false;
202 #endif  
203 }
204
205
206
207 //void MOVIE_SAVER::close_stream(AVFormatContext *oc, OutputStream *ost)
208 void MOVIE_SAVER::close_stream(void *_oc, void *_ost)
209 {
210 #if defined(USE_LIBAV)
211         OutputStream *ost = (OutputStream *)_ost;
212         #ifdef AVCODEC_UPPER_V56
213         avcodec_close(ost->context);
214         while(avcodec_is_open(ost->context) != 0) { this->msleep(5);}
215         #else
216         avcodec_close(ost->st->codec);
217         while(avcodec_is_open(ost->st->codec) != 0) { this->msleep(5);}
218         #endif
219         av_frame_free(&ost->frame);
220         av_frame_free(&ost->tmp_frame);
221         sws_freeContext(ost->sws_ctx);
222         swr_free(&ost->swr_ctx);
223         #ifdef AVCODEC_UPPER_V56
224         avcodec_free_context(&(ost->context));
225         #endif
226 #endif  
227 }
228
229 void MOVIE_SAVER::do_open(QString filename, int _fps, int _sample_rate)
230 {
231         if(recording || req_close || req_stop) return;
232         if(filename.isEmpty()) return;
233         if(_fps <= 0) return;
234         if(_sample_rate <= 10) return;
235         do_set_record_fps(_fps);
236         audio_sample_rate = _sample_rate;
237         _filename = filename;
238         recording = true;
239 }
240
241 bool MOVIE_SAVER::do_open_main()
242 {
243 #if defined(USE_LIBAV)
244         if(req_close) return false;
245         if(req_stop) return false;
246         if(_filename.isEmpty()) return false;
247         if(rec_fps <= 0) return false;
248         if(audio_sample_rate <= 10) return false;
249         AVOutputFormat *fmt;
250         AVFormatContext *oc;
251         int ret;
252         have_video = 0, have_audio = 0;
253          
254         raw_options_list = NULL;
255         //video_st = { 0 };
256         //audio_st = { 0 };
257         memset(&video_st, 0x00, sizeof(video_st));
258         memset(&audio_st, 0x00, sizeof(audio_st));
259         
260         /* Initialize libavcodec, and register all codecs and formats. */
261 #ifndef AVCODEC_UPPER_V56
262         av_register_all();
263 #endif
264
265         {
266                 QString value;
267                 do_clear_options_list();
268                 do_add_option(QString::fromUtf8("c:v"), QString::fromUtf8("mpeg4"));
269                 //do_add_option(QString::fromUtf8("c:a"), QString::fromUtf8("aac"));
270                 //do_add_option(QString::fromUtf8("c:v"), QString::fromUtf8("theora"));
271                 do_add_option(QString::fromUtf8("c:a"), QString::fromUtf8("vorbis"));
272                 
273                 video_encode_threads = p_config->video_threads;
274                 video_geometry = QSize(p_config->video_width, p_config->video_height);
275                 audio_bit_rate = p_config->audio_bitrate * 1000;
276                 
277         }
278
279         for(int i = 0; i < encode_options.size(); i++) {
280                 if(encode_opt_keys.size() <= i) break;
281                 av_dict_set(&raw_options_list, encode_opt_keys.takeAt(i).toUtf8().constData(),
282                                         encode_options.takeAt(i).toUtf8().constData(), 0);
283         }
284
285         /* allocate the output media context */
286         avformat_alloc_output_context2(&oc, NULL, NULL, _filename.toLocal8Bit().constData());
287         if (!oc) {
288 //              printf("Could not reduce output format from file extension: using MPEG.\n");
289                 avformat_alloc_output_context2(&oc, NULL, "mpeg", _filename.toLocal8Bit().constData());
290         }
291         if (!oc)
292                 return false;
293
294         fmt = (AVOutputFormat *)(oc->oformat);
295         switch(p_config->video_codec_type) {
296         case VIDEO_CODEC_MPEG4:
297                 fmt->video_codec = AV_CODEC_ID_MPEG4;
298                 break;
299         case VIDEO_CODEC_H264:
300                 fmt->video_codec = AV_CODEC_ID_H264;
301                 break;
302         }
303         switch(p_config->audio_codec_type) {
304         case AUDIO_CODEC_MP3:
305                 fmt->audio_codec = AV_CODEC_ID_MP3;
306                 break;
307         case AUDIO_CODEC_AAC:
308                 fmt->audio_codec = AV_CODEC_ID_AAC;
309                 break;
310         case AUDIO_CODEC_VORBIS:
311                 fmt->audio_codec = AV_CODEC_ID_VORBIS;
312                 break;
313         }               
314         /* Add the audio and video streams using the default format codecs
315          * and initialize the codecs. */
316         if (fmt->video_codec != AV_CODEC_ID_NONE) {
317                 
318                 if(!add_stream((void *)&video_st, (void *)oc, (void **)&video_codec, (uint64_t)fmt->video_codec)) goto _err_final;
319                 have_video = 1;
320                 encode_video = 1;
321         }
322         if (fmt->audio_codec != AV_CODEC_ID_NONE) {
323                 if(!add_stream((void *)&audio_st, (void *)oc, (void **)&audio_codec, (uint64_t)fmt->audio_codec)) goto _err_final;
324                 have_audio = 1;
325                 encode_audio = 1;
326         }
327         output_context = oc;
328         stream_format  = fmt;
329         /* Now that all the parameters are set, we can open the audio and
330          * video codecs and allocate the necessary encode buffers. */
331         if (have_video)
332                 open_video();
333
334         if (have_audio)
335                 open_audio();
336         
337         av_dump_format(oc, 0, _filename.toLocal8Bit().constData(), 1);
338
339         /* open the output file, if needed */
340         if (!(fmt->flags & AVFMT_NOFILE)) {
341                 ret = avio_open(&oc->pb, _filename.toLocal8Bit().constData(), AVIO_FLAG_WRITE);
342                 if (ret < 0) {
343                         out_debug_log(CSP_LOG_INFO, CSP_LOG_TYPE_MOVIE_SAVER,
344                                                                   "Could not open '%s': %s\n", _filename.toLocal8Bit().constData(),
345                                         err2str(ret).toLocal8Bit().constData());
346                         goto _err_final;
347                 } else {
348                         out_debug_log(CSP_LOG_INFO, CSP_LOG_TYPE_MOVIE_SAVER,
349                                                                   "Success to Open file: '%s\n", _filename.toLocal8Bit().constData());
350                 }                       
351         }
352         totalSrcFrame = 0;
353         totalDstFrame = 0;
354         totalAudioFrame = 0;
355 //      audio_enqueue_count = 0;
356
357         /* Write the stream header, if any. */
358         ret = avformat_write_header(oc, &raw_options_list);
359         if (ret < 0) {
360                 out_debug_log(CSP_LOG_INFO, CSP_LOG_TYPE_MOVIE_SAVER,
361                                                           "Error occurred when opening output file: %s\n",
362                                 err2str(ret).toLocal8Bit().constData());
363                 goto _err_final;
364         }
365         emit sig_set_state_saving_movie(true);
366         return true;
367 _err_final:
368         avformat_free_context(oc);
369         oc = NULL;
370         output_context = NULL;
371         stream_format  = NULL;
372         emit sig_set_state_saving_movie(false);
373         return false;
374 #else
375         emit sig_set_state_saving_movie(true);
376         return true;
377 #endif  
378 }
379
380 void MOVIE_SAVER::do_close()
381 {
382         req_close = true;
383         req_stop = true;
384 }
385
386 void MOVIE_SAVER::do_close_main()
387 {
388         if(!req_close) return;
389 #if defined(USE_LIBAV)
390         if(output_context != NULL) {
391                 AVFormatContext *oc = output_context;
392                 AVOutputFormat *fmt = (AVOutputFormat *)(oc->oformat);
393                 //AVPacket pkt = {0};
394                 //AVCodecContext *c = video_st.st->codec;
395                 bool a_f, v_f;
396                 //bool need_video_transcode, need_audio_transcode;
397                 bool need_audio_transcode;
398                 a_f = v_f = false;
399                 //need_video_transcode = need_audio_transcode = false;
400                 need_audio_transcode = false;
401                 /* Close each codec. */
402                 //if(totalSrcFrame != totalDstFrame) {
403                 while(!a_f || !v_f) {
404                         if(audio_remain <= 0) {
405                                 a_f = audio_data_queue.isEmpty();
406                                 if(!a_f) {
407                                         dequeue_audio(audio_frame_buf);
408                                         audio_remain = audio_size;
409                                         audio_offset = 0;
410                                 }
411                         }
412                         {
413                                 v_f = video_data_queue.isEmpty();
414                                 if(!v_f) {
415                                         if(left_frames <= 0) dequeue_video(video_frame_buf);
416                                         left_frames--;
417                                         video_remain = video_size;
418                                         video_offset = 0;
419                                 }
420                         }
421                         int result = 0;
422 #ifdef AVCODEC_UPPER_V56
423                         if(audio_st.context != NULL) {
424                                 if(video_st.context != NULL) {
425                                         result = av_compare_ts(video_st.next_pts, video_st.context->time_base,
426                                                                                    audio_st.next_pts, audio_st.context->time_base);
427                                 } else {
428                                         result = 1;
429                                 }
430                         } else {
431                                 result = -1;
432                         }
433 #else
434                         result = av_compare_ts(video_st.next_pts, video_st.st->codec->time_base,
435                                                                    audio_st.next_pts, audio_st.st->codec->time_base);
436 #endif
437                         if ((n_encode_video == 0) &&
438                                 ((n_encode_audio != 0) || (result <= 0))) {
439                                 n_encode_video = write_video_frame();
440                                 if(n_encode_video < 0) break;
441                         } else {
442                                 n_encode_audio = write_audio_frame();
443                                 if(n_encode_audio < 0) break;
444                         }
445                 }
446                 if (have_video) {
447                         close_stream(oc, &video_st);
448                 }
449                 if (have_audio) {
450                         close_stream(oc, &audio_st);
451                 }
452                 have_video = have_audio = 0;
453
454                 av_write_trailer(oc);
455
456                 if (!(fmt->flags & AVFMT_NOFILE))
457                         avio_closep(&oc->pb);
458
459                 /* free the stream */
460                 avformat_free_context(output_context);
461                 output_context = NULL;
462                 stream_format = NULL;
463                 audio_codec = video_codec = NULL;
464                 raw_options_list = NULL;
465
466                 memset(&video_st, 0x00, sizeof(video_st));
467                 memset(&audio_st, 0x00, sizeof(audio_st));
468         }
469         if(raw_options_list != NULL) {
470                 av_dict_free(&raw_options_list);
471                 raw_options_list = NULL;
472         }
473 #endif   // defined(USE_LIBAV)
474         memset(audio_frame_buf, 0x00, sizeof(audio_frame_buf));
475         memset(video_frame_buf, 0x00, sizeof(video_frame_buf));
476
477         uint64_t leftq_a, leftq_v;
478         leftq_a = leftq_v = 0;
479         while(!audio_data_queue.isEmpty()) {
480                 QByteArray *pp = audio_data_queue.dequeue();
481                 if(pp != NULL) {
482                         leftq_a++;
483                         delete pp;
484                 }
485         }
486         audio_data_queue.clear();
487
488         while(!video_data_queue.isEmpty()) {
489                 VIDEO_DATA *pp = video_data_queue.dequeue();
490                 if(pp != NULL) {
491                         if(pp->frames >= 0) leftq_v += pp->frames;
492                         delete pp;
493                 }
494         }
495         video_data_queue.clear();
496
497         out_debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER,
498                                                   "Close: Left:  Video %lld frames, Audio %lld frames",
499                                   leftq_v,
500                                   leftq_a
501         );
502         // Message
503         out_debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER,
504                                   "MOVIE: Close: Write:  Video %lld frames, Audio %lld frames", totalDstFrame, totalAudioFrame);
505         out_debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER,
506                                                   "MOVIE: Close: Dequeue:  Video %lld frames, Audio %lld frames", totalDstFrame, audio_count);
507         totalSrcFrame = 0;
508         totalDstFrame = 0;
509         totalAudioFrame = 0;
510         //audio_enqueue_count = 0;
511         audio_remain = 0;
512         video_remain = 0;
513         audio_offset = 0;
514         audio_frame_offset = 0;
515         video_offset = 0;
516         video_count = 0;
517         audio_count = 0;
518         
519         req_close = false;
520         req_stop = false;
521         recording = false;
522         
523         emit sig_set_state_saving_movie(false);
524 }
525
526
527 QString MOVIE_SAVER::create_date_file_name(void)
528 {
529         QDateTime nowTime = QDateTime::currentDateTime();
530         QString tmps = nowTime.toString(QString::fromUtf8("yyyy-MM-dd_hh-mm-ss.zzz."));
531         return tmps;
532 }
533