qt/LedOK/base/ffutil.cpp
2022-10-27 15:07:45 +08:00

274 lines
12 KiB
C++

#include "ffutil.h"
#include <QPainter>
#include <QDebug>
extern "C"{
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
}
QString videoInfo(QByteArray url, QImage &img, int64_t *dur, AVCodecID *codec_id) {
AVFormatContext *fmt_ctx = avformat_alloc_context();
QString err;
{
if(avformat_open_input(&fmt_ctx, url.constData(), nullptr, nullptr) != 0) {
err = "Couldn't open input stream";
goto free;
}
if(avformat_find_stream_info(fmt_ctx, nullptr) < 0) {
err = "Couldn't find stream information";
goto free;
}
if(dur!=nullptr) *dur = fmt_ctx->duration;
int vi_idx = -1;
for(uint ss=0; ss<fmt_ctx->nb_streams; ss++) if(fmt_ctx->streams[ss]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) vi_idx = ss;
if(vi_idx == -1) {
err = "Didn't find a Video Stream";
goto free;
}
auto codecpar = fmt_ctx->streams[vi_idx]->codecpar;
if(codec_id!=nullptr) *codec_id = codecpar->codec_id;
qDebug()<<"codec_id"<<codecpar->codec_id<<avcodec_get_name(codecpar->codec_id);
if(av_seek_frame(fmt_ctx, -1, 1000000, AVSEEK_FLAG_BACKWARD) < 0) {
err = "av_seek_frame fail";
goto free;
}
const AVCodec *decoder = avcodec_find_decoder(codecpar->codec_id);
if(decoder==nullptr) {
err = "Could not found Video Decoder";
goto free;
}
auto vcCtx = avcodec_alloc_context3(decoder);
avcodec_parameters_to_context(vcCtx, codecpar);
if(avcodec_open2(vcCtx, decoder, nullptr) < 0) {
err = "Could not open Video Codec Ctx";
avcodec_free_context(&vcCtx);
goto free;
}
auto sws_ctx = sws_getContext(vcCtx->width, vcCtx->height, vcCtx->pix_fmt, vcCtx->width, vcCtx->height, AV_PIX_FMT_RGB32, SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
auto packet = av_packet_alloc();
auto frm = av_frame_alloc();
while(1) {
if(av_read_frame(fmt_ctx, packet) < 0) break;
if(packet->stream_index != vi_idx) continue;
int res = avcodec_send_packet(vcCtx, packet);
if(res < 0) break;
while((res = avcodec_receive_frame(vcCtx, frm)) != AVERROR(EAGAIN)) {
if(res < 0) goto free2;
img = QImage(vcCtx->width, vcCtx->height, QImage::Format_ARGB32);
uint8_t *dst[4]{img.bits()};
int dstStride[4]{img.bytesPerLine()};
sws_scale(sws_ctx, frm->data, frm->linesize, 0, vcCtx->height, dst, dstStride);
goto free2;
}
}
free2:
av_frame_free(&frm);
av_packet_free(&packet);
avcodec_free_context(&vcCtx);
sws_freeContext(sws_ctx);
}
free:
avformat_close_input(&fmt_ctx);
return err;
}
QString videoTrans(int sw, int sh, int dw, int dh, int cnt, QPointF pos, QByteArray file) {
AVFormatContext *in_fmt = avformat_alloc_context(), *out_fmt = 0;
AVCodecContext *de_ctx = 0, *en_ctx = 0;
QString err;
char buf[AV_ERROR_MAX_STRING_SIZE];
int ret;
{
if((ret = avformat_open_input(&in_fmt, file.constData(), nullptr, nullptr)) < 0) {
err = QString("Couldn't open input stream. ")+av_make_error_string(buf, AV_ERROR_MAX_STRING_SIZE, ret);
goto free;
}
if((ret = avformat_find_stream_info(in_fmt, nullptr)) < 0) {
err = QString("Couldn't find stream information. ")+av_make_error_string(buf, AV_ERROR_MAX_STRING_SIZE, ret);
goto free;
}
auto outfile = file+"-square.mp4";
if((ret = avformat_alloc_output_context2(&out_fmt, 0, "mp4", outfile.constData())) < 0) {
err = QString("avformat_alloc_output_context2 fail. ")+av_make_error_string(buf, AV_ERROR_MAX_STRING_SIZE, ret);
goto free;
}
int vi_idx = -1;
for(uint ss=0; ss<in_fmt->nb_streams; ss++) {
AVStream *stream = in_fmt->streams[ss];
AVStream *out_stream = avformat_new_stream(out_fmt, 0);
if((ret = avcodec_parameters_copy(out_stream->codecpar, stream->codecpar)) < 0) {
err = QString("avcodec_parameters_copy fail. ") + av_make_error_string(buf, AV_ERROR_MAX_STRING_SIZE, ret);
goto free;
}
out_stream->time_base = stream->time_base;
out_stream->start_time = stream->start_time;
out_stream->duration = stream->duration;
out_stream->avg_frame_rate = stream->avg_frame_rate;
out_stream->sample_aspect_ratio = {1,1};
if(vi_idx == -1 && stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) vi_idx = ss;
}
if(vi_idx == -1) {
err = "Didn't find a Video Stream";
goto free;
}
auto codecpar = in_fmt->streams[vi_idx]->codecpar;
qDebug()<<"codec_id"<<codecpar->codec_id<<avcodec_get_name(codecpar->codec_id);
auto decoder = avcodec_find_decoder(codecpar->codec_id);
if(decoder==0) {
err = "Could not found Video Decoder";
goto free;
}
de_ctx = avcodec_alloc_context3(decoder);
avcodec_parameters_to_context(de_ctx, codecpar);
if(avcodec_open2(de_ctx, decoder, 0) < 0) {
err = "Could not open Video decode Ctx";
goto free;
}
auto out_codecpar = out_fmt->streams[vi_idx]->codecpar;
out_codecpar->codec_id = AV_CODEC_ID_H264;
out_codecpar->format = AV_PIX_FMT_YUV420P;
out_codecpar->width = dw;
out_codecpar->height = dh;
out_codecpar->sample_aspect_ratio = {1,1};
out_fmt->streams[vi_idx]->sample_aspect_ratio = {1,1};
auto encoder = avcodec_find_encoder(out_codecpar->codec_id);
if(encoder==0) {
fprintf(stderr, "Codec not found\n");
goto free;
}
en_ctx = avcodec_alloc_context3(encoder);
avcodec_parameters_to_context(en_ctx, out_codecpar);
en_ctx->bit_rate = dw*dh*6;
en_ctx->gop_size = de_ctx->gop_size;
en_ctx->max_b_frames = 3;
en_ctx->framerate = de_ctx->framerate;
en_ctx->time_base = out_fmt->streams[vi_idx]->time_base;
if((ret = avcodec_open2(en_ctx, encoder, 0)) < 0) {
err = QString("Open video encode ctx failed. ") + av_make_error_string(buf, AV_ERROR_MAX_STRING_SIZE, ret);
goto free;
}
if(out_fmt->flags & AVFMT_NOFILE) qDebug()<<"AVFMT_NOFILE";
else if((ret = avio_open(&out_fmt->pb, outfile.constData(), AVIO_FLAG_WRITE)) < 0) {
err = QString("avio_open fail. ")+av_make_error_string(buf, AV_ERROR_MAX_STRING_SIZE, ret);
goto free;
}
if((ret = avformat_write_header(out_fmt, 0)) < 0) {
err = QString("avformat_write_header fail. ")+av_make_error_string(buf, AV_ERROR_MAX_STRING_SIZE, ret);
goto free;
}
auto sws_ctx = sws_getContext(de_ctx->width, de_ctx->height, de_ctx->pix_fmt, sw, sh, AV_PIX_FMT_0RGB, SWS_FAST_BILINEAR, 0, 0, 0);
auto out_sws_ctx = sws_getContext(dw, dh, AV_PIX_FMT_0RGB, dw, dh, AV_PIX_FMT_YUV420P, SWS_FAST_BILINEAR, 0, 0, 0);
auto packet = av_packet_alloc();
auto frm = av_frame_alloc();
auto out_frm = av_frame_alloc();
QImage img(sw, sh, QImage::Format_RGB32);
uint8_t *img_data[4]{img.bits(), 0, 0, 0};
int img_linesize[4]{img.bytesPerLine(), 0, 0, 0};
QImage out_img(dw, dh, QImage::Format_RGB32);
uint8_t *out_img_data[4]{out_img.bits(), 0, 0, 0};
int out_img_linesize[4]{out_img.bytesPerLine(), 0, 0, 0};
QPainter painter(&out_img);
while(1) {
if((ret = av_read_frame(in_fmt, packet)) < 0) {
if(ret!=AVERROR_EOF) {
err = QString("Read packet fail: ")+av_make_error_string(buf, AV_ERROR_MAX_STRING_SIZE, ret);
break;
}
ret = avcodec_send_packet(de_ctx, 0);
} else {
if(packet->stream_index != vi_idx) {
av_interleaved_write_frame(out_fmt, packet);
continue;
}
ret = avcodec_send_packet(de_ctx, packet);
}
if(ret < 0) {
err = QString("avcodec_send_packet fail. ")+av_make_error_string(buf, AV_ERROR_MAX_STRING_SIZE, ret);
break;
}
while((ret = avcodec_receive_frame(de_ctx, frm)) != AVERROR(EAGAIN)) {
if(ret < 0) {
if(ret!=AVERROR_EOF) {
err = QString("Receive frame fail: ")+av_make_error_string(buf, AV_ERROR_MAX_STRING_SIZE, ret);
goto free2;
}
ret = avcodec_send_frame(en_ctx, 0);
} else {
sws_scale(sws_ctx, frm->data, frm->linesize, 0, de_ctx->height, img_data, img_linesize);
painter.drawImage(pos, img);
auto apos = pos;
for(int i=1; i<cnt; i++) {
apos.rx() -= dw;
apos.ry() += dh/cnt;
painter.drawImage(apos, img);
}
av_frame_unref(out_frm);
av_frame_copy_props(out_frm, frm);
out_frm->format = AV_PIX_FMT_YUV420P;
out_frm->width = dw;
out_frm->height = dh;
out_frm->sample_aspect_ratio = {1,1};
if((ret = av_frame_get_buffer(out_frm, 0)) < 0) {
err = QString("av_frame_get_buffer fail. ")+av_make_error_string(buf, AV_ERROR_MAX_STRING_SIZE, ret);
goto free2;
}
sws_scale(out_sws_ctx, out_img_data, out_img_linesize, 0, dh, out_frm->data, out_frm->linesize);
ret = avcodec_send_frame(en_ctx, out_frm);
}
if(ret < 0) {
err = QString("avcodec_send_frame fail. ")+av_make_error_string(buf, AV_ERROR_MAX_STRING_SIZE, ret);
goto free2;
}
while((ret = avcodec_receive_packet(en_ctx, packet)) != AVERROR(EAGAIN)) {
if(ret < 0) {
if(ret!=AVERROR_EOF) err = QString("Receive frame fail: ")+av_make_error_string(buf, AV_ERROR_MAX_STRING_SIZE, ret);
else {
av_interleaved_write_frame(out_fmt, 0);
av_write_trailer(out_fmt);
}
goto free2;
} else {
av_interleaved_write_frame(out_fmt, packet);
}
}
}
}
free2:
av_frame_free(&frm);
av_frame_free(&out_frm);
av_packet_free(&packet);
sws_freeContext(sws_ctx);
sws_freeContext(out_sws_ctx);
}
free:
avcodec_free_context(&de_ctx);
avcodec_free_context(&en_ctx);
avformat_close_input(&in_fmt);
avio_closep(&out_fmt->pb);
if(out_fmt) avformat_free_context(out_fmt);
return err;
}
QString audioInfo(QByteArray url, int64_t *dur) {
AVFormatContext *fmt_ctx = avformat_alloc_context();
QString err;
{
if(avformat_open_input(&fmt_ctx, url.constData(), nullptr, nullptr) != 0) {
err = "Couldn't open input stream";
goto free;
}
if(avformat_find_stream_info(fmt_ctx, nullptr) < 0) {
err = "Couldn't find stream information";
goto free;
}
if(dur!=nullptr) *dur = fmt_ctx->duration;
}
free:
avformat_close_input(&fmt_ctx);
return err;
}