Hi all,

I am helping with OpenToonz development (
http://opentoonz.github.io/e/index.html), and trying to build in h264, webm
and gif import and export.  OpenToonz handles images in RGBA (RGB32)
format.  I have been successful getting swscale work, but when I try and
save the frames, I think the header and footer get written, but the frames
are empty (I think).  I am new to ffmpeg and would love any help I can get.


The in progress work is at: https://github.com/opentoonz/opentoonz/pull/589
This is a QT Project using c++.

Thanks

Here is the implementation file:


//#include "tmachine.h"
#include "tsystem.h"
#include "texception.h"
#include "tfilepath.h"
#include "tiio_mp4.h"
#include "trasterimage.h"
#include "qmessagebox.h"
//#include "tiio.h"
//#include "../compatibility/tfile_io.h"
//#include "tpixel.h"

namespace {

std::string buildMp4ExceptionString(int rc) {
return "Unable to create mp4.";
}

}


//===========================================================
//
//  TImageWriterMp4
//
//===========================================================

class TImageWriterMp4 : public TImageWriter {
public:
int m_frameIndex;

TImageWriterMp4(const TFilePath &path, int frameIndex, TLevelWriterMp4 *lwg)
: TImageWriter(path), m_frameIndex(frameIndex), m_lwg(lwg) {
m_lwg->addRef();
}
~TImageWriterMp4() { m_lwg->release(); }

bool is64bitOutputSupported() override { return false; }
void save(const TImageP &img) override { m_lwg->save(img, m_frameIndex); }

private:
TLevelWriterMp4 *m_lwg;

};


//===========================================================
//
//  TLevelWriterMp4;
//
//===========================================================

TLevelWriterMp4::TLevelWriterMp4(const TFilePath &path, TPropertyGroup
*winfo)
: TLevelWriter(path, winfo) {
m_frameCount = 0;
av_register_all();
avcodec_register_all();
m_status = "created";
if (TSystem::doesExistFileOrLevel(m_path)) TSystem::deleteFile(m_path);
mp4Init();


}

//-----------------------------------------------------------

TLevelWriterMp4::~TLevelWriterMp4()
{
//std::string status = "";
/* add sequence end code to have a real mpeg file */
//uint8_t endcode[] = { 0, 0, 1, 0xb7 };
//fwrite(endcode, 1, sizeof(endcode), m_file);
//fclose(m_file);

int status = 0;
int numBytes = avpicture_get_size(m_codecContext->pix_fmt,
m_codecContext->width, m_codecContext->height);
int got_packet_ptr = 1;

int ret;

//get the remaining frames
while (got_packet_ptr)
{
uint8_t *outbuf = (uint8_t *)malloc(numBytes);

AVPacket packet;
av_init_packet(&packet);
packet.data = outbuf;
packet.size = numBytes;

ret = avcodec_encode_video2(m_codecContext, &packet, NULL, &got_packet_ptr);
if (got_packet_ptr)
{
av_interleaved_write_frame(m_formatCtx, &packet);
}

av_free_packet(&packet);
free(outbuf);
}

status = av_write_trailer(m_formatCtx);
if (status < 0)
exit(1);

avcodec_close(m_codecContext);
av_free(m_codecContext);
avio_close(m_formatCtx->pb);
}

//-----------------------------------------------------------

TImageWriterP TLevelWriterMp4::getFrameWriter(TFrameId fid) {
//if (IOError != 0)
// throw TImageException(m_path, buildGifExceptionString(IOError));
if (fid.getLetter() != 0) return TImageWriterP(0);
int index = fid.getNumber() - 1;
TImageWriterMp4 *iwg = new TImageWriterMp4(m_path, index, this);
return TImageWriterP(iwg);
}

//-----------------------------------------------------------
void TLevelWriterMp4::setFrameRate(double fps)
{
m_fps = fps;
m_frameRate = fps;
}

void TLevelWriterMp4::saveSoundTrack(TSoundTrack *st)
{
return;
}

void TLevelWriterMp4::mp4Init() {
std::string status = "";
//these need to be replaced with actual values from the camera
int width = 1920;
int height = 1080;

//establish codec context
int codecId = AV_CODEC_ID_H264;
m_codec = avcodec_find_encoder((AVCodecID)codecId);
if (!m_codec) {
status = "Codec not found\n";
}

m_codecContext = avcodec_alloc_context3(m_codec);
if (!m_codecContext) {
status = "Could not allocate video codec context\n";
}

//set codec values
m_codecContext->bit_rate = 400000;
/* resolution must be a multiple of two */
m_codecContext->width = 1920;
m_codecContext->height = 1080;
/* frames per second */
AVRational avr = { 1, m_frameRate };
m_codecContext->time_base = avr;
/* emit one intra frame every ten frames
* check frame pict_type before passing frame
* to encoder, if frame->pict_type is AV_PICTURE_TYPE_I
* then gop_size is ignored and the output of encoder
* will always be I frame irrespective to gop_size
*/
m_codecContext->gop_size = 10;
m_codecContext->max_b_frames = 1;
m_codecContext->pix_fmt = AV_PIX_FMT_YUV420P;

if (codecId == AV_CODEC_ID_H264)
av_opt_set(m_codecContext->priv_data, "preset", "slow", 0);

/* open the codec*/
if (avcodec_open2(m_codecContext, m_codec, NULL) < 0) {
status = "Could not open codec\n";
}

//set up output format
m_outFormat = av_guess_format(NULL, m_path.getQString().toLatin1().data(),
NULL);
if (m_outFormat == NULL)
{
m_outFormat = av_guess_format("mp4", NULL, NULL);
}
if (!m_outFormat) {
status = "Could not find suitable output format\n";
}
//set up format context
m_formatCtx = avformat_alloc_context();
m_formatCtx->oformat = m_outFormat;
m_outFormat->video_codec = (AVCodecID)codecId;
//allocate a video stream
m_videoStream = avformat_new_stream(m_formatCtx, m_codec);
if (m_videoStream)
{
status ="Could not allocate stream\n";
}
m_videoStream->codec = m_codecContext;

if (m_videoStream->codec == NULL) {
status = "AVStream codec is NULL\n";
}

//not sure what this does yet
if (m_formatCtx->oformat->flags & AVFMT_GLOBALHEADER)
{
m_codecContext->flags |= CODEC_FLAG_GLOBAL_HEADER;
}

//open the file
avio_open(&m_formatCtx->pb, m_path.getQString().toLatin1().data(),
AVIO_FLAG_READ_WRITE);
if (m_formatCtx->pb == NULL) {
status = "Could not open for writing";
}

//write the file header
if (avformat_write_header(m_formatCtx, NULL) != 0) {
status = "Could not write header";
}

/*m_file = fopen(m_path.getQString().toLatin1().data(), "wb");
if (!m_file) {
status = "Could not open %s\n", m_path.getQString().toLatin1().data();
}*/

}
//-----------------------------------------------------------

void TLevelWriterMp4::save(const TImageP &img, int frameIndex) {
std::string saveStatus = "";
TRasterImageP image(img);
int lx = image->getRaster()->getLx();
int ly = image->getRaster()->getLy();
m_bpp = image->getRaster()->getPixelSize();
int linesize = image->getRaster()->getRowSize();
int pixelSize = image->getRaster()->getPixelSize();

//lock raster to get data
image->getRaster()->lock();
uint8_t *buffin = image->getRaster()->getRawData();
assert(buffin);

//memcpy(m_buffer, buffin, lx * ly * m_bpp);
image->getRaster()->unlock();

//end get data

//begin convert image to output format
AVFrame *inFrame = av_frame_alloc();

int inSize = lx * ly;
int inNumBytes = pixelSize * lx * ly;
uint8_t *inbuf = (uint8_t *)av_malloc(inNumBytes);
uint8_t *inPicture_buf = (uint8_t *)av_malloc(inNumBytes);
//inFrame->data[0] = inbuf;

avpicture_fill((AVPicture *)inFrame, buffin, AV_PIX_FMT_RGB32,
m_codecContext->width, m_codecContext->height);

int pts = frameIndex;

inFrame->pts = pts;

//set up scaling context
struct SwsContext *sws_ctx = NULL;
int frameFinished;


sws_ctx = sws_getContext(lx,
ly,
AV_PIX_FMT_RGB32,
lx,
ly,
m_codecContext->pix_fmt,
SWS_BILINEAR,
NULL,
NULL,
NULL
);



//begin example
AVFrame* outFrame = av_frame_alloc();
if (!outFrame) {
saveStatus = "Could not allocate video frame\n";
}
outFrame->format = m_codecContext->pix_fmt;
outFrame->width = m_codecContext->width;
outFrame->height = m_codecContext->height;

int ret = av_image_alloc(outFrame->data, outFrame->linesize,
m_codecContext->width, m_codecContext->height,
m_codecContext->pix_fmt, 32);
if (ret < 0) {
saveStatus = "Could not allocate raw picture buffer\n";
}

//convert from RGBA to YUV420P
int swsSuccess = sws_scale(sws_ctx, (uint8_t const * const *)inFrame->data,
inFrame->linesize, 0, ly,
outFrame->data, outFrame->linesize);
AVPacket packet;
av_init_packet(&packet);
packet.data = NULL;    // packet data will be allocated by the encoder
packet.size = 0;

fflush(stdout);

outFrame->pts = pts;
int got_output;
ret = avcodec_encode_video2(m_codecContext, &packet, outFrame, &got_output);
if (ret < 0) {
saveStatus = "Error encoding frame\n";
}

if (got_output) {
saveStatus = "Write frame %3d (size=%5d)\n", frameIndex, packet.size;
av_interleaved_write_frame(m_formatCtx, &packet);
//fwrite(packet.data, 1, packet.size, m_file);
av_free_packet(&packet);
//av_packet_unref(&packet);
}

av_free(inbuf);
av_free(inPicture_buf);
//av_free(outPicture_buf);
//av_free(outbuf);
av_free(inFrame);
av_free(outFrame);

}


Tiio::Mp4WriterProperties::Mp4WriterProperties(){}

Tiio::Reader* Tiio::makeMp4Reader(){ return nullptr; }
Tiio::Writer* Tiio::makeMp4Writer(){ return nullptr; }
_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to