First commit of brand new rewrite

This commit is contained in:
itsmattkc
2018-05-30 03:24:12 +10:00
commit 244c092591
86 changed files with 8016 additions and 0 deletions
+342
View File
@@ -0,0 +1,342 @@
#include "exportthread.h"
#include "project/sequence.h"
#include "panels/panels.h"
#include "panels/timeline.h"
#include "panels/viewer.h"
#include "ui/viewerwidget.h"
#include "playback/playback.h"
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswresample/swresample.h>
#include <libswscale/swscale.h>
}
#include <QDebug>
#include <QApplication>
#include <QOffscreenSurface>
#include <QOpenGLFramebufferObject>
#include <QOpenGlPaintDevice>
#include <QPainter>
bool encode(AVFormatContext* fmt_ctx, AVCodecContext* codec_ctx, AVFrame* frame, AVPacket* packet, AVStream* stream) {
int ret = avcodec_send_frame(codec_ctx, frame);
if (ret < 0) {
qDebug() << "[ERROR] Failed to send frame to encoder." << ret;
return false;
} else {
while (ret >= 0) {
ret = avcodec_receive_packet(codec_ctx, packet);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
// do nothing, encoder needs more input
} else if (ret < 0) {
qDebug() << "[ERROR] Failed to receive packet from encoder." << ret;
return false;
} else {
packet->stream_index = stream->index;
av_interleaved_write_frame(fmt_ctx, packet);
}
}
}
return true;
}
void ExportThread::run() {
Sequence* sequence = panel_timeline->sequence;
// TODO make customizable
long start = 0;
long end = sequence->getEndFrame();
if (panel_viewer->viewer_widget->context()->makeCurrent(&surface)) {
qDebug() << "make current succeeded";
} else {
qDebug() << "make current failed";
}
panel_viewer->viewer_widget->multithreaded = false;
panel_viewer->viewer_widget->initializeOpenGLFunctions();
AVFormatContext* fmt_ctx = NULL;
QByteArray ba = filename.toLatin1();
char* c_filename = new char[ba.size()+1];
strcpy(c_filename, ba.data());
avformat_alloc_output_context2(&fmt_ctx, NULL, NULL, c_filename);
if (!fmt_ctx) {
qDebug() << "[ERROR] Could not create output context";
} else {
AVStream* video_stream;
AVCodec* vcodec;
AVCodecContext* vcodec_ctx;
AVFrame* video_frame;
SwsContext* sws_ctx = NULL;
// AVStream* audio_stream;
// AVCodec* acodec;
// AVFrame* audio_frame;
// AVCodecContext* acodec_ctx;
AVPacket video_pkt;
// AVPacket audio_pkt;
// SwrContext* swr_ctx = NULL;
int ret;
bool fail = false;
if (video_enabled) {
// initialize array contain opengl data
video_stream = avformat_new_stream(fmt_ctx, NULL);
if (!video_stream) {
qDebug() << "[ERROR] Could not allocate output streams";
fail = true;
} else {
vcodec = avcodec_find_encoder((enum AVCodecID) video_codec);
if (!vcodec) {
qDebug() << "[ERROR] Could not find video encoder";
fail = true;
} else {
vcodec_ctx = avcodec_alloc_context3(vcodec);
if (!vcodec_ctx) {
qDebug() << "[ERROR] Could not find allocate video encoding context";
fail = true;
} else {
vcodec_ctx->width = sequence->width;
vcodec_ctx->height = sequence->height;
vcodec_ctx->sample_aspect_ratio = av_d2q(sequence->width/sequence->height, INT_MAX);
vcodec_ctx->pix_fmt = vcodec->pix_fmts[0]; // maybe be breakable code
vcodec_ctx->framerate = av_d2q(sequence->frame_rate, INT_MAX);
vcodec_ctx->bit_rate = video_bitrate * 1000000;
vcodec_ctx->time_base = av_inv_q(vcodec_ctx->framerate);
ret = avcodec_open2(vcodec_ctx, vcodec, NULL);
if (ret < 0) {
qDebug() << "[ERROR] Could not open output video encoder." << ret;
fail = true;
} else {
ret = avcodec_parameters_from_context(video_stream->codecpar, vcodec_ctx);
if (ret < 0) {
qDebug() << "[ERROR] Could not copy video encoder parameters to output stream." << ret;
fail = true;
} else {
if (fmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) {
vcodec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
video_stream->time_base = vcodec_ctx->time_base;
// initialize raw video frame
video_frame = av_frame_alloc();
av_frame_make_writable(video_frame);
video_frame->format = AV_PIX_FMT_RGBA;
video_frame->width = video_width;
video_frame->height = video_height;
av_frame_get_buffer(video_frame, 0);
av_init_packet(&video_pkt);
sws_ctx = sws_getContext(
video_width,
video_height,
AV_PIX_FMT_RGBA,
video_width,
video_height,
AV_PIX_FMT_YUV420P,
SWS_FAST_BILINEAR,
NULL,
NULL,
NULL
);
}
}
}
}
}
}
// if (audio_enabled && !fail) {
// audio_stream = avformat_new_stream(fmt_ctx, NULL);
// if (!audio_stream) {
// qDebug() << "[ERROR] Could not allocate output streams";
// fail = true;
// } else {
// acodec = avcodec_find_encoder((enum AVCodecID) audio_codec);
// if (!acodec) {
// qDebug() << "[ERROR] Could not find audio encoder";
// fail = true;
// } else {
// acodec_ctx = avcodec_alloc_context3(acodec);
// if (!acodec_ctx) {
// qDebug() << "[ERROR] Could not find allocate audio encoding context";
// fail = true;
// } else {
// acodec_ctx->sample_rate = audio_sampling_rate;
// acodec_ctx->channel_layout = AV_CH_LAYOUT_STEREO;
// acodec_ctx->channels = av_get_channel_layout_nb_channels(acodec_ctx->channel_layout);
// acodec_ctx->sample_fmt = acodec->sample_fmts[0];
// acodec_ctx->time_base = {1, audio_sampling_rate};
// acodec_ctx->bit_rate = audio_bitrate * 1000;
// ret = avcodec_open2(acodec_ctx, acodec, NULL);
// if (ret < 0) {
// qDebug() << "[ERROR] Could not open output audio encoder." << ret;
// fail = true;
// } else {
// ret = avcodec_parameters_from_context(audio_stream->codecpar, acodec_ctx);
// if (ret < 0) {
// qDebug() << "[ERROR] Could not copy audio encoder parameters to output stream." << ret;
// fail = true;
// } else {
// if (fmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) {
// acodec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
// }
// audio_stream->time_base = acodec_ctx->time_base;
// // init audio resampler context
// swr_ctx = swr_alloc_set_opts(
// NULL,
// acodec_ctx->channel_layout,
// acodec_ctx->sample_fmt,
// acodec_ctx->sample_rate,
// sequence->audio_layout,
// AV_SAMPLE_FMT_S16,
// sequence->audio_frequency,
// 0,
// NULL
// );
// swr_init(swr_ctx);
// // initialize raw audio frame
// audio_frame = av_frame_alloc();
// audio_frame->nb_samples = acodec_ctx->frame_size;
// if (audio_frame->nb_samples == 0) audio_frame->nb_samples = 2048; // should probably be way smaller?
// audio_frame->format = acodec_ctx->sample_fmt;
// audio_frame->channels = acodec_ctx->channels;
// audio_frame->channel_layout = acodec_ctx->channel_layout;
// ret = av_frame_get_buffer(audio_frame, 0);
// if (ret < 0) {
// qDebug() << "[ERROR] Could not allocate audio buffer." << ret;
// }
// // av_samples_alloc(&audio_frame->data[0], NULL, av_get_channel_layout_nb_channels(audio_frame->channel_layout), audio_frame->nb_samples, acodec_ctx->sample_fmt, 0);
// av_frame_make_writable(audio_frame);
// av_init_packet(&audio_pkt);
// }
// }
// }
// }
// }
// }
if (!fail) {
av_dump_format(fmt_ctx, 0, c_filename, 1);
ret = avio_open(&fmt_ctx->pb, c_filename, AVIO_FLAG_WRITE);
if (ret < 0) {
qDebug() << "[ERROR] Could not open output file." << ret;
} else {
ret = avformat_write_header(fmt_ctx, NULL);
if (ret < 0) {
qDebug() << "[ERROR] Could not write output file header." << ret;
} else {
panel_timeline->seek(0);
// clean up - close all open clips
for (int i=0;i<sequence->clip_count();i++) {
Clip& c = sequence->get_clip(i);
if (c.open) {
close_clip(&c);
}
}
QOpenGLFramebufferObject fbo(video_width, video_height, QOpenGLFramebufferObject::CombinedDepthStencil, GL_TEXTURE_RECTANGLE);
fbo.bind();
QOpenGLPaintDevice fbo_dev(video_width, video_height);
QPainter painter(&fbo_dev);
painter.beginNativePainting();
AVFrame* sws_frame = av_frame_alloc();
sws_frame->format = AV_PIX_FMT_YUV420P;
sws_frame->width = video_frame->width;
sws_frame->height = video_frame->height;
av_frame_get_buffer(sws_frame, 0);
uint8_t* flip_buffer = new uint8_t[video_frame->width * video_frame->height * 4];
while (panel_timeline->playhead < end && !fail) {
panel_viewer->viewer_widget->paintGL();
double timecode_secs = (double) panel_timeline->playhead / sequence->frame_rate;
if (video_enabled) {
// get image from opengl
glReadPixels(0, 0, video_width, video_height, GL_RGBA, GL_UNSIGNED_BYTE, flip_buffer);
// flip image vertically because opengl sucks ass
int linesize = video_width*4;
for (int i=video_height-1;i>=0;i--) {
int src_index = i*linesize;
int dst_index = (video_height-i)*linesize;
memcpy(video_frame->data[0]+dst_index, flip_buffer+src_index, linesize);
}
// change pixel format
sws_scale(sws_ctx, video_frame->data, video_frame->linesize, 0, video_frame->height, sws_frame->data, sws_frame->linesize);
sws_frame->pts = round(timecode_secs/av_q2d(video_stream->time_base));
// send to encoder
if (!encode(fmt_ctx, vcodec_ctx, sws_frame, &video_pkt, video_stream)) fail = true;
}
emit progress_changed(((float) panel_timeline->playhead / (float) end) * 100);
panel_timeline->playhead++;
}
delete [] flip_buffer;
painter.endNativePainting();
fbo.release();
av_frame_free(&sws_frame);
if (!fail) {
// flush remaining packets
if (video_enabled) {
if (!encode(fmt_ctx, vcodec_ctx, NULL, &video_pkt, video_stream)) fail = true;
}
// if (audio_enabled) {
// if (!encode(fmt_ctx, acodec_ctx, NULL, &audio_pkt, audio_stream)) return false;
// }
}
if (!fail) {
ret = av_write_trailer(fmt_ctx);
if (ret < 0) {
qDebug() << "[ERROR] Could not write output file trailer." << ret;
}
emit progress_changed(100);
}
}
}
av_packet_unref(&video_pkt);
av_frame_free(&video_frame);
avcodec_free_context(&vcodec_ctx);
// avcodec_free_context(&acodec_ctx);
avio_closep(&fmt_ctx->pb);
avformat_free_context(fmt_ctx);
// qDebug() << "Render took: %ih %im %is\n", render_time/3600000, render_time/60000, render_time/1000;
if (sws_ctx != NULL) sws_freeContext(sws_ctx);
// if (swr_ctx != NULL) swr_free(&swr_ctx);
}
}
delete [] c_filename;
panel_viewer->viewer_widget->context()->doneCurrent();
panel_viewer->viewer_widget->context()->moveToThread(qApp->thread());
panel_viewer->viewer_widget->multithreaded = true;
}