OSDN Git Service

[Qt][MOVIE] Recorder: Initial. This still not working.
authorK.Ohta <whatisthis.sowhat@gmail.com>
Fri, 27 May 2016 10:09:59 +0000 (19:09 +0900)
committerK.Ohta <whatisthis.sowhat@gmail.com>
Fri, 27 May 2016 10:09:59 +0000 (19:09 +0900)
source/build-cmake/cmake/toolchain_mingw_cross_linux.cmake
source/src/qt/avio/movie_saver.cpp [new file with mode: 0644]
source/src/qt/avio/movie_saver.h [new file with mode: 0644]

index 3ea3837..29bf4a5 100644 (file)
@@ -13,14 +13,14 @@ if(USE_SDL2)
                           /usr/local/i586-mingw-msvc
                           /usr/local/i586-mingw-msvc/SDL2/i686-w64-mingw32
                          /usr/local/i586-mingw-msvc/5.5/mingw492_32
-#                        /usr/local/i586-mingw-msvc/5.5/mingw500_32
+#                        /usr/local/i586-mingw-msvc/5.5/mingw530_32_win32
                          )
 else()
    SET(CMAKE_FIND_ROOT_PATH  /usr/i686-w64-mingw32 
                           /usr/local/i586-mingw-msvc
                           /usr/local/i586-mingw-msvc/SDL1/
                          /usr/local/i586-mingw-msvc/5.5/mingw492_32
-#                        /usr/local/i586-mingw-msvc/5.5/mingw500_32
+#                        /usr/local/i586-mingw-msvc/5.5/mingw530_32_win32
                          )
 endif()
 
@@ -44,6 +44,6 @@ set(SDL_INCLUDE_DIRS /usr/local/i586-mingw-msvc/SDL1/include/SDL)
 set(SDLMAIN_LIBRARY "")
 
 set(ADDITIONAL_LIBRARIES libwinmm.a)
-set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++")
+#set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++")
 #set(CMAKE_EXE_LINKER_FLAGS "-static-libstdc++")
-#set(CMAKE_EXE_LINKER_FLAGS "")
+set(CMAKE_EXE_LINKER_FLAGS "")
diff --git a/source/src/qt/avio/movie_saver.cpp b/source/src/qt/avio/movie_saver.cpp
new file mode 100644 (file)
index 0000000..7d28915
--- /dev/null
@@ -0,0 +1,362 @@
+/*
+ * Common Source Code Project for Qt : movie saver.
+ * (C) 2016 K.Ohta <whatisthis.sowhat _at_ gmail.com>
+ *  License: GPLv2
+ *  History: May 27, 2016 : Initial. This refer from avidemux 2.5.6 .
+ */
+
+#include <QDateTime>
+#include "movie_saver.h"
+#include "osd.h"
+
+MOVIE_SAVER::MOVIE_SAVER(int width, int height, int fps, OSD *osd) : 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;
+       recording = false;
+
+       audio_data_queue.clear();
+
+       video_data_queue.clear();
+       video_width_queue.clear();
+       video_height_queue.clear();
+       
+       totalSrcFrame = 0;
+       totalDstFrame = 0;
+       totalAudioFrame = 0;
+       bRunThread = false;
+}
+
+MOVIE_SAVER::~MOVIE_SAVER()
+{
+       do_close();
+}
+
+void MOVIE_SAVER::enqueue_video(QByteArray *p, int width, int height)
+{
+       if(!recording) return false;
+       if(p == NULL) return;
+       QByteArray *pp = new QByteArray(p->data(), p->size());
+       
+       video_data_queue.enqueue(pp);
+       video_width_queue.enqueue(width);
+       video_height_queue.enqueue(height);
+}
+
+bool MOVIE_SAVER::dequeue_video(uint32_t *p)
+{
+       if(!recording) return false;
+       if(video_data_queue.isEmpty()) return false;
+       if(p == NULL) return false;
+
+       QByteArray *pp = video_data_queue.dequeue();
+       if((pp == NULL) || (video_size <= 0)) return false;
+       
+       video_size = pp->size();
+       memcpy(p, pp->data(), video_size);
+       delete pp;
+       
+       return true;
+}
+
+void MOVIE_SAVER::enqueue_audio(QByteArray *p)
+{
+       if(!recording) return;
+       if(p == NULL) return;
+       QByteArray *pp = new QByteArray(p->data(), p->size());
+       audio_data_queue.enqueue(pp);
+}
+
+bool MOVIE_SAVER::dequeue_audio(int16_t *p)
+{
+       if(!recording) return false;
+       if(audio_data_queue.isEmpty()) return false;
+       if(p == NULL) return false;
+
+       QByteArray *pp = audio_data_queue.dequeue();
+       if((pp == NULL) || (audio_size <= 0)) return false;
+
+       audio_size = pp->size();
+       memcpy(p, pp->data(), audio_size);
+       delete pp;
+       return true;
+}
+
+void MOVIE_SAVER::run()
+{
+       bRunThread = true;
+       
+       int fps_wait = (int)((1000.0 / this->vm_frame_rate()) / 2.0);
+       int tmp_wait = fps_wait;
+       while(bRunThread) {
+               if(recording) {
+                       if(!bRunThread) break;
+                       if(!recording) break;
+                       if(!audio_data_queue.isEmpty()) {
+                               if(dequeue_audio(audio_frame)) {
+                                       AVPacket pkt;
+                                       uint64_t bytes = audio_size;
+                                       uint64_t us = (uint64_t)floor(((double)bytes * 1000000.0) / (double)audio_codec->sample_rate);
+                                       double samples = ((double)us / 1000000.0) * (double)audio_codec->sample_rate()   
+                                       int ret;
+                                       
+                                       if(bytes == 0) goto _video;
+                                       
+                                       av_init_packet(&pkt);
+                                       pkt.dts = pkt.pts = (int64_t)samples;
+                                       pkt.flags |= AV_PKT_FLAG_KEY;
+                                       pkt.data = (uint8_t *)audio_frame;
+                                       pkt.size = (uint32_t)bytes;
+                                       pkt.stream_index = 1;
+                                       ret = av_write_frame(output_context, &pkt);
+                               
+                                       totalAudioFrame++;
+                               }
+                       }
+               _video:
+#if 0 // ToDo                  
+                       if(!bRunThread) break;
+                       if(!recording) break;
+                       if(!video_data_queue.isEmpty() &&
+                          !video_height_queue.isEmpty() && !video_width_queue.isEmpty()) {
+                               // Scale Video
+                               if(dequeue_video(video_frame)) {
+                                       AVRational fps = {1, 1000000};
+                                       AVPacket pkt;
+                                       uint64_t bytes = video_size;
+                                       int i;
+                                       av_init_packet(&pkt);
+                                       // Call encoder
+                                       
+                                       // Call muxer
+                                       
+                                       totalSrcFrame++;
+                                       totalDstFrame++;
+                               }
+                       }
+#endif                 
+               }
+               if(fps_wait >= tmp_wait) {
+                       this->msleep(tmp_wait);
+                       tmp_wait = 0;
+               } else {
+                       this->msleep(fps_wait);
+                       tmp_wait -= fps_wait;
+               }
+               if(tmp_wait <= 0) {
+                       fps_wait = (int)((1000.0 / this->vm_frame_rate()) / 2.0);
+                       tmp_wait = fps_wait;
+               }
+       }
+}
+
+void MOVIE_SAVER::do_close()
+{
+       if(output_context != NULL) {
+               av_write_trailer(output_context);
+               avio_close(output_context->pb);
+       }
+       if(audio_stream != NULL) {
+               av_free(audio_stream);
+       }
+       if(video_stream != NULL) {
+               av_free(video_stream);
+       }
+       audio_stream = NULL;
+       video_stream = NULL;
+
+       if(output_context != NULL) {
+               av_free(output_context);
+               output_context = NULL;
+       }
+       recording = false;
+
+       audio_data_queue.clear();
+
+       video_data_queue.clear();
+       video_width_queue.clear();
+       video_height_queue.clear();
+
+       // Message
+       AGAR_DebugLog(AGAR_LOG_DEBUG, "MOVIE: Close: Read:   Video %ll frames, Audio %ll frames", totalSrcFrame, totalAudioFrame);
+       AGAR_DebugLog(AGAR_LOG_DEBUG, "MOVIE: Close: Write:  Video %ll frames, Audio %ll frames", totalDstFrame, totalAudioFrame);
+       totalSrcFrame = 0;
+       totalDstFrame = 0;
+       totalAudioFrame = 0;
+}
+
+QString MOVIE_PLAYER::create_date_file_name(void)
+{
+       QDateTime nowTime = QDateTime::currentDateTime();
+       QString tmps = nowTime.toString(QString::fromUtf8("yyyy-MM-dd_hh-mm-ss.zzz."));
+       return tmps;
+}
+
+bool MOVIE_SAVER::is_recording(void)
+{
+       return recording;
+}
+static const AVRational time_base_15 = (AVRational){1001, 14485};
+static const AVRational time_base_24 = (AVRational){1001, 23976};
+static const AVRational time_base_25 = (AVRational){1001, 25025};
+static const AVRational time_base_30 = (AVRational){1001, 29970};
+static const AVRational time_base_60 = (AVRational){1001, 59940};
+
+void MOVIE_SAVER::do_open(QString filename)
+{
+       cur_time_t *t_time;
+       do_close();
+
+       _filename = filename;
+       format = av_guess_format("mp4", NULL, NULL);
+
+       if(format == NULL) {
+               AGAR_DebugLog(AGAR_LOG_DEBUG, "AVC ERROR: Failed to initialize libavf");
+               return;
+       }
+       
+       output_context = avformat_alloc_context();
+       if(output_context == NULL) {
+               AGAR_DebugLog(AGAR_LOG_DEBUG, "AVC ERROR: Failed to get output_context");
+               do_close();
+               return;
+       }
+       
+       output_context->oformat = format;
+       snprintf(output_context->filename, 1000, "file://%s", filename.fromUtf8().constData());
+       AGAR_DebugLog(AGAR_LOG_DEBUG, "Start rec VIDEO: %s", output_context->filename);
+
+#if 0 // Todo  
+       video_stream = av_new_stream(output_context, 0);
+       if(video_stream == NULL) {
+               AGAR_DebugLog(AGAR_LOG_DEBUG, "AVC ERROR: Failed to open video stream");
+               do_close();
+               return;
+       }
+
+       get_host_time(t_time);
+       video_codec = video_stream->codec;
+       video_codec->gop_size = 15;
+       video_codec->max_b_frames = 8;
+       video_codec->has_b_frames = 1;
+       QString author = QString::fromUtf8("emu");
+       author = author + osd->get_vm_config_name();
+       QString date_str = QString::fromUtf8("Record from");
+       date_str = date_str + create_date_file_name();
+       
+       { // Video Start
+               av_dict_set(&output_context->metadata, "title", date_str.fromUtf8().constData(), 0);
+               av_dict_set(&output_context->metadata, "author", author.fromUtf8().constData(), 0);
+               video_codec->has_b_frames=2; // let muxer know we may have bpyramid
+               video_codec->codec_id = CODEC_ID_H264;
+               video_codec->codec = &codec_real;
+               memset(video_codec->codec, 0, sizeof(struct AVCodec));
+               strcpy(video_codec->codec->name, "H264");
+               {
+                       // Set basic rate
+                       video_codec->rc_buffer_size = buffer_size;
+                       video_codec->rc_max_rate = max_rate;
+                       video_codec->rc_min_rate = min_rate;
+                       video_codec->bit_rate = bitrate;
+               }
+               video_codec->codec_type = AVMEDIA_TYPE_VIDEO;
+               video_codec->flags = CODEC_FLAG_QSCALE;
+               video_codec->width = _width;
+               video_codec->height = _height;
+       }
+
+       switch(rec_fps) {
+       case 15:
+               video_codec->time_base = time_base_15;
+               video_codec->bit_rate = bitrate / 2;
+               video_codec->rc_max_rate = max_rate / 2;
+               video_codec->rc_min_rate = min_rate / 2;
+               break;
+       case 24:
+               video_codec->time_base = time_base_24;
+               break;
+       case 25:
+               video_codec->time_base = time_base_25;
+               break;
+       case 30:
+               video_codec->time_base = time_base_30;
+               break;
+       case 60:
+               video_codec->time_base = time_base_60;
+               video_codec->bit_rate = bitrate * 2;
+               video_codec->rc_max_rate = max_rate * 2;
+               video_codec->rc_min_rate = min_rate * 2;
+               break;
+       default:
+               time_base = (AVRational){1001, rec_fps * 1000};
+               video_codec->time_base = time_base;
+               video_codec->bit_rate = (int)((double)bitrate * 30.0 / (double)rec_fps);
+               video_codec->rc_max_rate = (int)((double)max_rate * 30.0 / (double)rec_fps);
+               video_codec->rc_min_rate = (int)((double)min_rate * 30.0 / (double)rec_fps);
+               break;
+       }
+#endif // ToDo
+       
+       //audio_stream = av_new_stream(output_context, 1);
+       audio_stream = av_new_stream(output_context, 0);
+       if(audio_stream == NULL) {
+               AGAR_DebugLog(AGAR_LOG_DEBUG, "AVC ERROR: Failed to open audio stream");
+               do_close();
+               return;
+       }
+
+       // Temporally using libAV's AAC
+       audio_codec = audio_stream->codec;
+       audio_codec->frame_size = 1024; 
+       audio_codec->codec_id = CODEC_ID_AAC;
+       audio_codec->sample_rate = osd->get_sound_rate();
+
+       audio_codec->codec_type = AVMEDIA_TYPE_AUDIO;
+       
+       audio_codec->bit_rate = audio_codec->sample_rate * 8 * 2;
+       audio_codec->rc_buffer_size = audio_codec->sample_rate / 4; // 250 ms worth
+       audio_codec->channels = 2;
+
+       // Context
+       output_context->mux_rate = 100080 * 1000;
+       output_context->pre_load = AV_TIME_BASE / 10;
+       output_context->max_delay = 100 * 1000; // MAX 100ms delay;
+
+       if(avio_open(&(output_context->pb), filename.fromUtf8().constData(), AVIO_FLAG_WRITE) < 0) {
+               AGAR_DebugLog(AGAR_LOG_DEBUG, "AVC ERROR: Failed to open file");
+               do_close();
+               return;
+       }               
+
+       av_dump_format(output_context, 0, filename.fromUtf8().constData(), 1);
+       AGAR_DebugLog(AGAR_LOG_DEBUG, "Successfully opened AVC stream.");
+               
+       recording = true;
+}
+
+void MOVIE_SAVER::do_exit()
+{
+       bRunThread = false;
+}
+
+void MOVIE_SAVER::do_set_record_fps(int fps)
+{
+       if((fps > 0) && (fps <= 60)) rec_fps = fps;
+}
+
+void MOVIE_SAVER::do_set_width(int width)
+{
+       _width = width;
+}
+
+void MOVIE_SAVER::do_set_height(int height)
+{
+       _height = height;
+}
+
diff --git a/source/src/qt/avio/movie_saver.h b/source/src/qt/avio/movie_saver.h
new file mode 100644 (file)
index 0000000..3243232
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Common Source Code Project for Qt : movie saver.
+ * (C) 2016 K.Ohta <whatisthis.sowhat _at_ gmail.com>
+ *  License: GPLv2
+ *  History: May 27, 2016 : Initial. This refer from avidemux 2.5.6 .
+ */
+
+#ifndef _QT_MOVIE_SAVER_H
+#define _QT_MOVIE_SAVER_H
+
+#include <QByteArray>
+#include <QQueue>
+#include <QString>
+#include <QThread>
+
+extern "C" {
+       #include "libavformat/avformat.h"
+}
+
+class OSD;
+
+QT_BEGIN_NAMESPACE
+class MOVIE_SAVER: public QThread
+{
+       Q_OBJECT
+protected:
+       OSD *p_osd;
+       AVOutputFormat *format;
+       AVCodecContext *codec;
+       AVStream *audio_stream;
+       AVStream *video_stream;
+       ADM_MUXER_TYPE muxer_type;
+       
+       AVFormatContext *output_context;
+       struct AVCodec codec_real;
+       QString _filename;
+       bool bRunThread;
+       
+       uint min_rate;
+       uint max_rate;
+       uint buffer_size;
+       uint bitrate;
+       int _width;
+       int _height;
+       bool recording;
+       int rec_fps;
+
+       AVRational time_base;
+       
+       uint64_t audio_size;
+       uint64_t video_size;
+       uint32_t ptsFrame;
+
+       uint64_t totalSrcFrame;
+       uint64_t totalDstFrame;
+       uint64_t totalAudioFrame;
+
+       int16_t audio_frame[2 * 48000 * sizeof(int16_t)]; // 1Sec
+       uint32_t video_frame[1280 * 512 * sizeof(uint32_t)]; // 1 frame : right?
+       uint32_t video_dst[1280 * 1024 * sizeof(uint32_t)]; // 1 frame : right?
+       
+       QQueue<int> video_width_queue;
+       QQueue<int> video_height_queue;
+       QQueue<QByteArray> video_data_queue;
+       
+       QQueue<QByteArray> audio_data_queue;
+       
+       dequeue_audio(int16_t *p);
+       dequeue_video(uint32_t *p);
+       QString create_date_file_name(void);
+       
+public:
+       MOVIE_SAVER(int width, int height, int fps, OSD *osd);
+       ~MOVIE_SAVER();
+       void doWork();
+       bool is_recording(void);
+
+public slots:
+       void enqueue_video(QByteArray *p, int width, int height);
+       void enqueue_audio(QByteArray *p);
+       void do_close();
+       void do_open(QString filename);
+       void do_set_width(int width);
+       void do_set_height(int height);
+       void do_set_record_fps(int fps)
+       void do_exit();
+};
+QT_END_NAMESPACE
+
+#endif