#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));
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)
#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;
}
#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
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;
}
_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;
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)
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;
}
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"));
+}
+