OSDN Git Service

[BUILD] Set SOVERSION and GIT hash automatically.
[csp-qt/common_source_project-fm7.git] / source / src / qt / avio / movie_saver.cpp
index 38a10e5..d50d8a7 100644 (file)
@@ -8,19 +8,24 @@
 #include <QDateTime>
 #include "movie_saver.h"
 #include "../osd.h"
-#include "agar_logger.h"
+#include "common.h"
+#include "csp_logger.h"
 
-MOVIE_SAVER::MOVIE_SAVER(int width, int height, int fps, OSD *osd) : QThread(0)
+MOVIE_SAVER::MOVIE_SAVER(int width, int height, int fps, OSD *osd, config_t *cfg) : QThread(0)
 {
        buffer_size=8 * 1024 * 224;
        max_rate=4000 * 1000;
        min_rate=0;
-       bitrate = 2000 * 1000;
        _width = width;
        _height = height;
        rec_fps = fps;
+       
        p_osd = osd;
+       p_config = cfg;
+       p_logger = osd->get_logger();
+       
        recording = false;
+       audio_sample_rate = 48000;
 #if defined(USE_MOVIE_SAVER)
        memset(audio_frame_buf, 0x00, sizeof(audio_frame_buf));
        memset(video_frame_buf, 0x00, sizeof(video_frame_buf));
@@ -36,23 +41,40 @@ MOVIE_SAVER::MOVIE_SAVER(int width, int height, int fps, OSD *osd) : QThread(0)
        output_context = NULL;
        
        audio_data_queue.clear();
-
        video_data_queue.clear();
-       video_width_queue.clear();
-       video_height_queue.clear();
        
-       encode_opt_keys.clear();
-       encode_options.clear();
+       do_reset_encoder_options();
 
+       //video_queue_mutex = new QMutex(QMutex::Recursive);
        totalSrcFrame = 0;
        totalDstFrame = 0;
        totalAudioFrame = 0;
+
+       video_bit_rate = 1500 * 1000;
+       audio_bit_rate = 160 * 1000;
+       video_geometry = QSize(640, 480);
+       video_encode_threads = 4;
+//     audio_enqueue_count = 0;
+       left_frames = 0;
+       req_close = false;
+       req_stop = false;
        bRunThread = false;
+       
 }
 
 MOVIE_SAVER::~MOVIE_SAVER()
 {
-       if(recording) do_close();
+       req_close = true;
+       if(recording) do_close_main();
+}
+
+QString MOVIE_SAVER::get_avio_version()
+{
+#if defined(__LIBAVIO_VERSION)
+       return QString::fromUtf8(__LIBAVIO_VERSION);
+#else
+       return QString::fromUtf8("\0");
+#endif
 }
 
 QString MOVIE_SAVER::ts2str(int64_t ts)
@@ -95,53 +117,63 @@ void MOVIE_SAVER::log_packet(const void *_fmt_ctx, const void *_pkt)
 #if defined(USE_LIBAV) 
        const AVFormatContext *fmt_ctx = (const AVFormatContext *)_fmt_ctx;
        const AVPacket *pkt = (const AVPacket *)_pkt;
-    AVRational *time_base = &fmt_ctx->streams[pkt->stream_index]->time_base;
+       AVRational *time_base = &fmt_ctx->streams[pkt->stream_index]->time_base;
 
-    AGAR_DebugLog(AGAR_LOG_DEBUG, "pts:%s pts_time:%s dts:%s dts_time:%s duration:%s duration_time:%s stream_index:%d\n",
-           ts2str(pkt->pts).toLocal8Bit().constData(),
+       p_logger->debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "pts:%s pts_time:%s dts:%s dts_time:%s duration:%s duration_time:%s stream_index:%d\n",
+                  ts2str(pkt->pts).toLocal8Bit().constData(),
                   ts2timestr(pkt->pts, (void *)time_base).toLocal8Bit().constData(),
-           ts2str(pkt->dts).toLocal8Bit().constData(),
+                  ts2str(pkt->dts).toLocal8Bit().constData(),
                   ts2timestr(pkt->dts, (void *)time_base).toLocal8Bit().constData(),
-           ts2str(pkt->duration).toLocal8Bit().constData(),
+                  ts2str(pkt->duration).toLocal8Bit().constData(),
                   ts2timestr(pkt->duration, (void *)time_base).toLocal8Bit().constData(),
-           pkt->stream_index);
+                  pkt->stream_index);
 #endif 
 }
 
 
-void MOVIE_SAVER::enqueue_video(QByteArray *p, int width, int height)
+void MOVIE_SAVER::enqueue_video(int frames, int width, int height, QImage *p)
 {
 #if defined(USE_MOVIE_SAVER)
        if(!recording) return;
        if(p == NULL) return;
-       QByteArray *pp = new QByteArray(p->data(), p->size());
-       AGAR_DebugLog(AGAR_LOG_DEBUG, "Movie: Enqueue video data %d bytes", p->size());
-       video_data_queue.enqueue(pp);
-       //video_width_queue.enqueue(width);
-       //video_height_queue.enqueue(height);
+       if(req_stop) return;
+       if((width  <= 0) || (height <= 0) || (frames <= 0)) return;
+       if((p->width() <= 0) || (p->height() <= 0)) return;
+       VIDEO_DATA *px = new VIDEO_DATA(frames, width, height, p);
+       //p_logger->debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "Movie: Enqueue video data %dx%d %d bytes %dx%d %d frames", width, height, p->byteCount(), p->width(), p->height(), frames);
+       //video_queue_mutex->lock();
+       video_data_queue.enqueue(px);
+       //video_queue_mutex->unlock();
 #endif   
 }
 
 bool MOVIE_SAVER::dequeue_video(uint32_t *p)
 {
-       if(!recording) return false;
+       //if(!recording) return false;
        if(p == NULL) return false;
 
-       QByteArray *pp = video_data_queue.dequeue();
+       VIDEO_DATA *pp = video_data_queue.dequeue();
 #if defined(USE_MOVIE_SAVER)
        if(pp == NULL) return false;
-       
-       video_size = pp->size();
-       if(video_size <= 0) return false;
-       memcpy(p, pp->data(), video_size);
-       //AGAR_DebugLog(AGAR_LOG_DEBUG, "Movie: Dequeue video data %d bytes", pp->size());
-       //_width = video_width_queue.dequeue();
-       //_height = video_height_queue.dequeue();
+       int y;
+       uint32_t *pq;
+       QImage *pp_i = &(pp->frame_data);
+       left_frames = pp->frames;
+       _width = pp->_width;
+       _height = pp->_height;
+       if((left_frames > 0) && (pp_i != NULL)){ 
+               for(y = 0; y < _height; y++) {
+                       if(y >= pp_i->height()) break;
+                       pq = (uint32_t *)(pp_i->constScanLine(y));
+                       my_memcpy(&(p[_width * y]), pq, (((uint)_width * sizeof(uint32_t)) > pp_i->bytesPerLine()) ? pp_i->bytesPerLine() : _width * sizeof(uint32_t));
+               }
+               video_size = _width * y;
+               //p_logger->debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "Movie: Dequeue video data %d bytes", video_size);
+       }
 #else
        video_size = 0;
 #endif   
        if(pp != NULL) delete pp;
-       
        return true;
 }
 
@@ -150,25 +182,37 @@ void MOVIE_SAVER::enqueue_audio(int16_t *p, int size)
 #if defined(USE_MOVIE_SAVER)
        if(!recording) return;
        if(p == NULL) return;
+       if(req_stop) return;
        QByteArray *pp = new QByteArray((const char *)p, size);
-       //AGAR_DebugLog(AGAR_LOG_DEBUG, "Movie: Enqueue audio data %d bytes", size);
+       //p_logger->debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "Movie: Enqueue audio data %d bytes", size);
        audio_data_queue.enqueue(pp);
+//     audio_enqueue_count++;
 #endif   
 }
 
 bool MOVIE_SAVER::dequeue_audio(int16_t *p)
 {
-       if(!recording) return false;
+       //if(!recording) return false;
        if(audio_data_queue.isEmpty()) return false;
        if(p == NULL) return false;
+       
        QByteArray *pp = audio_data_queue.dequeue();
+       int16_t *q = (int16_t *)pp->constData();
+       int32_t tmpd;
 #if defined(USE_MOVIE_SAVER)
        if(pp == NULL) return false;
-       //AGAR_DebugLog(AGAR_LOG_DEBUG, "Movie: Dequeue audio data %d bytes", pp->size());
-
        audio_size = pp->size();
        if(audio_size <= 0) return false;
-       memcpy(p, pp->constData(), audio_size);
+       // I expect to use SIMD.
+       for(int i = 0; i < (int)(audio_size / sizeof(int16_t)); i++) {
+               tmpd = q[i];
+               tmpd <<= 4;
+               tmpd = tmpd / 3;
+               tmpd >>= 3;
+               p[i] = tmpd;
+       }
+       //memcpy(p, pp->constData(), audio_size);
+       audio_count++;
 #else
        audio_size = 0;
 #endif   
@@ -180,44 +224,53 @@ bool MOVIE_SAVER::dequeue_audio(int16_t *p)
 void MOVIE_SAVER::run()
 {
        bRunThread = true;
-       //AGAR_DebugLog(AGAR_LOG_DEBUG, "MOVIE THREAD: Start");
-    int ret;
-    int got_packet;
-    int dst_nb_samples;
-
-       int fps_wait = (int)((1000.0 / p_osd->vm_frame_rate()) / 8.0);
+       //p_logger->debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "MOVIE THREAD: Start");
+       int ret;
+       int fps_wait = (int)((1000.0 / (double)rec_fps) / 4.0);
        int tmp_wait = fps_wait;
-       int ncount_audio = 0;
-       int ncount_video = 0;
-       bool audio_continue = false;
-       bool video_continue = false;
        bool need_audio_transcode = false;
        bool need_video_transcode = false;
        int i;
-    int64_t total_packets_written = 0;
-       int encode_audio = 0;
-       int encode_video = 0;
-       bool old_recording = false;
+       int64_t total_packets_written = 0;
+       bool a_f, v_f;
+       a_f = v_f = false;
+       volatile bool old_recording = false;
        audio_remain = 0;
        video_remain = 0;
        audio_offset = 0;
        audio_frame_offset = 0;
        video_offset = 0;
-       
+       n_encode_audio = 0;
+       n_encode_video = 0;
+       req_close = false;
+       req_stop = false;
+       left_frames = 0;
        while(bRunThread) {
                if(recording) {
                        if(!bRunThread) break;
-                       if(old_recording != recording) {
-                               AGAR_DebugLog(AGAR_LOG_DEBUG, "MOVIE/Saver: Start to recording.");
-                               encode_video = encode_audio = 1;
+                       if(!old_recording) {
+                               //n_encode_video = n_encode_audio = 1;
                                audio_remain = 0;
                                video_remain = 0;
                                audio_offset = 0;
                                audio_frame_offset = 0;
                                video_offset = 0;
+                               video_count = 0;
+                               audio_count = 0;
+                               req_close = false;
+                               req_stop = false;
+                               left_frames = 0;
+                               if(!do_open_main()) {
+                                       recording = false;
+                                       goto _next_turn;
+                               }
+                               p_logger->debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "MOVIE/Saver: Start to recording.");
+                               old_recording = true;
+                               a_f = v_f = false;
                        }
                        if(audio_remain <= 0) {
-                               if(audio_data_queue.isEmpty()) goto _video;
+                               a_f = audio_data_queue.isEmpty();
+                               if(a_f) goto _video;
                                dequeue_audio(audio_frame_buf);
                                audio_remain = audio_size;
                                audio_offset = 0;
@@ -225,45 +278,48 @@ void MOVIE_SAVER::run()
                        }
                _video:
                        {
-                               //if(video_data_queue.isEmpty() || video_width_queue.isEmpty()
-                               //   || video_height_queue.isEmpty())
-                               if(video_data_queue.isEmpty())
+                               v_f = video_data_queue.isEmpty();
+                               if(v_f)
                                        goto _write_frame;
-                               dequeue_video(video_frame_buf);
+                               if(left_frames <= 0) dequeue_video(video_frame_buf);
+                               left_frames--;
                                video_remain = video_size;
                                video_offset = 0;
                                need_video_transcode = true;
                        }
                _write_frame:
-                       if ((encode_video == 0) &&
-                               ((encode_audio != 0) || av_compare_ts(video_st.next_pts, video_st.st->codec->time_base,
+                       if ((n_encode_video == 0) &&
+                               ((n_encode_audio != 0) || av_compare_ts(video_st.next_pts, video_st.st->codec->time_base,
                                                                                                                audio_st.next_pts, audio_st.st->codec->time_base) <= 0)) {
-                               encode_video = write_video_frame();
-                               if(encode_video < 0) {
-                                       AGAR_DebugLog(AGAR_LOG_DEBUG, "MOVIE/Saver: Something wrong with encoding video.");
+                               n_encode_video = write_video_frame();
+                               if(n_encode_video < 0) {
+                                       p_logger->debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "MOVIE/Saver: Something wrong with encoding video.");
                                        goto _final;
                                }
-                       }  else {
-                               if(encode_audio == 0) {
-                                       encode_audio = write_audio_frame();
-                                       if(encode_audio < 0) {
-                                               AGAR_DebugLog(AGAR_LOG_DEBUG, "MOVIE/Saver: Something wrong with encoding audio.");
-                                               goto _final;
-                                       }
+                       } else {
+                               n_encode_audio = write_audio_frame();
+                               if(n_encode_audio < 0) {
+                                       p_logger->debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "MOVIE/Saver: Something wrong with encoding audio.");
+                                       goto _final;
                                }
                        }
                        if (ret < 0 && ret != AVERROR_EOF) {
                                char errbuf[128];
                                av_strerror(ret, errbuf, sizeof(errbuf));
-                               AGAR_DebugLog(AGAR_LOG_INFO, "Error while filtering: %s\n", (const char *)errbuf);
+                               p_logger->debug_log(CSP_LOG_INFO, CSP_LOG_TYPE_MOVIE_SAVER, "Error while filtering: %s\n", (const char *)errbuf);
                                goto _final;
                        }
                        
                        /* dump report by using the output first video and audio streams */
                        //print_report(0, timer_start, cur_time);
+                       if(req_close) {
+                               do_close_main();
+                               need_video_transcode = need_audio_transcode = false;
+                               old_recording = false;
+                       }
                }
        _next_turn:
-               old_recording = recording;
+               //if(req_stop && a_f && v_f) req_close = true;
                if(!bRunThread) break;
                if(need_video_transcode || need_audio_transcode) {
                        need_video_transcode = need_audio_transcode = false;
@@ -277,18 +333,25 @@ void MOVIE_SAVER::run()
                        tmp_wait -= fps_wait;
                }
                if(tmp_wait <= 0) {
-                       fps_wait = (int)((1000.0 / p_osd->vm_frame_rate()) / 8.0);
+                       fps_wait = (int)((1000.0 / (double)rec_fps) / 4.0);
+                       //fps_wait = 10;
                        tmp_wait = fps_wait;
                }
-               old_recording = recording;
+               if(req_close) {
+                       do_close_main();
+                       old_recording = false;
+               }
                continue;
        _final:
-               old_recording = recording;
-               do_close();
+               req_close = true;
+               do_close_main();
                old_recording = false;
        }
-       AGAR_DebugLog(AGAR_LOG_DEBUG, "MOVIE: Exit thread.");
-       if(recording) do_close();
+       p_logger->debug_log(CSP_LOG_DEBUG, CSP_LOG_TYPE_MOVIE_SAVER, "MOVIE: Exit thread.");
+       if(recording) {
+               req_close = true;
+               do_close_main();
+       }
 }
 
 bool MOVIE_SAVER::is_recording(void)
@@ -300,22 +363,22 @@ bool MOVIE_SAVER::is_recording(void)
 void MOVIE_SAVER::do_exit()
 {
        bRunThread = false;
+       req_close = true;
+       req_stop = true;
 }
 
 void MOVIE_SAVER::do_set_record_fps(int fps)
 {
-       if((fps > 0) && (fps <= 60)) rec_fps = fps;
+       if((fps > 0) && (fps <= 75)) rec_fps = fps;
 }
 
 void MOVIE_SAVER::do_set_width(int width)
 {
-       //printf("width = %d -> %d\n", _width, width);
        _width = width;
 }
 
 void MOVIE_SAVER::do_set_height(int height)
 {
-       //printf("height = %d -> %d\n", _height, height);
        _height = height;
 }
 
@@ -335,3 +398,44 @@ void MOVIE_SAVER::do_add_option(QString key, QString value)
                encode_options.append(value);
        }
 }              
+
+void MOVIE_SAVER::do_set_video_bitrate(int kbps)
+{
+       if(kbps < 10) return;
+       video_bit_rate = kbps * 1000;
+}
+
+void MOVIE_SAVER::do_set_audio_bitrate(int kbps)
+{
+       if(kbps < 8) return;
+       audio_bit_rate = kbps * 1000;
+}
+
+void MOVIE_SAVER::do_set_video_geometry(QSize geometry)
+{
+       if(geometry.width() < 100) return;
+       if(geometry.height() < 80) return;
+       video_geometry = geometry;
+}
+
+void MOVIE_SAVER::do_set_video_threads(int threads)
+{
+       video_encode_threads = threads;
+}
+
+void MOVIE_SAVER::do_reset_encoder_options(void)
+{
+       encode_opt_keys.clear();
+       encode_options.clear();
+
+       do_add_option(QString::fromUtf8("c:v"), QString::fromUtf8("libx264"));
+       do_add_option(QString::fromUtf8("c:a"), QString::fromUtf8("aac"));
+       do_add_option(QString::fromUtf8("preset"), QString::fromUtf8("slow"));
+       do_add_option(QString::fromUtf8("tune"), QString::fromUtf8("grain"));
+       //do_add_option(QString::fromUtf8("crf"), QString::fromUtf8("20"));
+       do_add_option(QString::fromUtf8("qmin"), QString::fromUtf8("16"));
+       do_add_option(QString::fromUtf8("qmax"), QString::fromUtf8("24"));
+       // Dummy
+       do_add_option(QString::fromUtf8("qmax"), QString::fromUtf8("24"));
+}
+