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
