Hello,
Trying to create simple utility that encodes multiple images to x265 annexB
stream.
OS Ubuntu
libav version: 3.4.2
Command line works:
ffmpeg -loglevel 48 -f image2 -s 640x640 -pattern_type sequence
-start_number 180 -i /media/marina/sdxdata/sw/records/pattern/%08d.pgm -c:v
libx265 -preset veryslow -x265-params lossless=1 -vf "format=gray8" -f hevc
out1.bin
Source code - I followed guiding and examples of ffmpeg
avcodec_receive_packet always returns EAGAIN,
When I send null frame, it returns packet - with encoded latest frame.
Attached file is implementation of frame encode and codec init
There is main loop that calls "AppendFrame" multiple times
Thanks,
Marina
//#include <libavutil/common.h>
//#include <libavutil/mem.h>
#include "rec_svc_enc.h"
CRecSvcEnc::CRecSvcEnc(seastar::logger *pLogger, CRecSvcCfg *pCfg, QObject *parent) : QObject(parent)
{
m_Cfg = pCfg;
m_Codec = nullptr;
m_Logger = pLogger;
m_frame = nullptr;
m_FrameDelayed = false;
}
bool CRecSvcEnc::IsValid()
{
if(m_Codec == nullptr) {
return false;
}
return true;
}
void CRecSvcEnc::EncStart(CodecParams params)
{
avcodec_register_all();
av_log_set_level (AV_LOG_TRACE);
m_Codec = avcodec_find_encoder(AV_CODEC_ID_H265);
if(m_Codec == nullptr) {
m_Logger->error("{} codec not found\n", FUNCN);
return;
}
m_CodecCtx= avcodec_alloc_context3(m_Codec);
AVDictionary * av_dict_opts = nullptr;
/// encoding nvenc profiles
//av_dict_set( &av_dict_opts, "loglevel", "48", AV_DICT_MATCH_CASE); /// hq ll lossless losslesshq
av_dict_set( &av_dict_opts, "preset", "veryslow", AV_DICT_MATCH_CASE); /// hq ll lossless losslesshq
av_dict_set( &av_dict_opts, "x265-params", "lossless=1:log-level=4", AV_DICT_MATCH_CASE);
//av_dict_set( &av_dict_opts, "-f", "image2", AV_DICT_MATCH_CASE);
/* put sample parameters */
m_CodecCtx->width = params.width; // /2;
m_CodecCtx->height = params.heiht; // /2;
m_CodecCtx->time_base= {1,30};//m_Cfg->GetFrameRate()};
m_CodecCtx->pix_fmt = AV_PIX_FMT_GRAY8;//params.pixFmt; //AVPixelFormat::AV_PIX_FMT_GRAY8;
m_CodecCtx->debug = 0xffff;// |= FF_DEBUG_BUFFERS | FF_DEBUG_PICT_INFO | FF_DEBUG_BUGS;
m_CodecCtx->gop_size = 10;
//m_CodecCtx->gop_size = 20; /* emit one intra frame every ten frames */
//m_CodecCtx->max_b_frames=1;
//m_CodecCtx->properties = FF_CODEC_PROPERTY_LOSSLESS;
//av_opt_set(m_CodecCtx->priv_data, "preset", "ultrafast", 0);
/* open it */
int ret = avcodec_open2(m_CodecCtx, m_Codec, &av_dict_opts);
if (ret < 0) {
m_Logger->error("{} could not open codec {}\n", FUNCN, ret);
m_Codec = nullptr;
return;
}
m_frame = av_frame_alloc();
if (!m_frame) {
m_Logger->error("{} Could not allocate video frame\n", FUNCN);
return;
}
m_frame->format = m_CodecCtx->pix_fmt;
m_frame->width = m_CodecCtx->width;
m_frame->height = m_CodecCtx->height;
m_frame->pts = 1;
m_frame->display_picture_number = 0;
//m_frame->linesize = m_frame->width[];
/* the image can be allocated by any means and av_image_alloc() is
* just the most convenient way if av_malloc() is to be used */
ret = av_image_alloc(m_frame->data, m_frame->linesize, m_CodecCtx->width, m_CodecCtx->height,
m_CodecCtx->pix_fmt, 32);
//av_frame_get_buffer(m_frame,32);
if (ret < 0) {
m_Logger->error("{} Could not allocate raw picture buffer\n", FUNCN);
av_frame_free(&m_frame);
m_frame = nullptr;
return;
}
}
void CRecSvcEnc::EncStop()
{
if(!IsValid())
return;
avcodec_close(m_CodecCtx);
av_free(m_CodecCtx);
}
void CRecSvcEnc::CopyFrame(QImage im)
{
int Offset = 0;
const uchar* ba = im.constBits();
int CopyLen = m_frame->linesize[0]*im.height();
memcpy(m_frame->data[0], ba,CopyLen);
return;
Offset+=CopyLen;
CopyLen= m_frame->linesize[1]*im.height();
memcpy(m_frame->data[1], ba+Offset, CopyLen);
Offset+=CopyLen;
CopyLen= m_frame->linesize[2]*im.height();
memcpy(m_frame->data[2], ba+Offset, CopyLen);
}
bool CRecSvcEnc::AppendFrame(QImage im)
{
bool bRet = true;
int ret;
if(!IsValid() || m_frame==nullptr)
return false;
// EncPull();
if(m_PendingQ.size() >= MAX_PENDING )
bRet = false;
else
m_PendingQ.append(im);
if(!m_FrameDelayed) {
im = m_PendingQ.first();
const uchar* ba = im.constBits();
//memcpy(m_frame->data[0], ba,m_frame->linesize[0]*m_frame->height);
//memcpy(m_frame->data[0], ba,im.sizeInBytes());
CopyFrame(im);
}
ret = avcodec_send_frame(m_CodecCtx,m_frame);
#if 0
av_init_packet(&m_pkt);
m_pkt.data = nullptr; // packet data will be allocated by the encoder
m_pkt.size = 0;
int gotPkt = 0;
avcodec_encode_video2(m_CodecCtx,&m_pkt,m_frame,&gotPkt );
#endif
if (ret < 0) {
m_Logger->error("{} Error encoding frame {}\n", FUNCN, ret);
m_FrameDelayed = true;
}
else {
m_FrameDelayed = false;
m_PendingQ.takeFirst();
m_frame->pts++;
m_frame->display_picture_number++;
EncPull();
}
return bRet;
#if 0
if (gotOutput) {
m_Logger->trace("{} Write frame (size={})\n", m_pkt.size);
ba.append(m_pkt.data, pkt.size);
av_free_packet(&m_pkt);
}
#endif
}
void CRecSvcEnc::EncPull()
{
av_init_packet(&m_pkt);
m_pkt.data = nullptr; // packet data will be allocated by the encoder
m_pkt.size = 0;
int ret = avcodec_receive_packet(m_CodecCtx, &m_pkt);
if (ret >= 0) {
QByteArray ba = QByteArray(((const char*)m_pkt.data), m_pkt.size);
m_Encoded.append(ba);
av_packet_unref(&m_pkt);
}
else if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) {
;
}
}
void CRecSvcEnc::Save2File()
{
int ret = avcodec_send_frame(m_CodecCtx,nullptr);
EncPull();
QFile ff("/nvxdata/tmp.bin");
ff.open(QIODevice::WriteOnly | QIODevice::Truncate);
ff.write(m_Encoded);
ff.close();
}
#ifndef REC_SVC_ENC_H
#define REC_SVC_ENC_H
#include <QObject>
#include <QImage>
extern "C" {
#include "libavcodec/avcodec.h"
#include <libavutil/opt.h>
#include <libavutil/imgutils.h>
#include <libavutil/log.h>
}
//#include "libavutil/mathematics.h"
#include "log.hh"
#include "svc_record.h"
#include "rec_svc_cfg.h"
using namespace seastar;
#define MAX_PENDING 50
class CRecSvcEnc : public QObject
{
Q_OBJECT
public:
struct CodecParams {
int width;
int heiht;
enum AVPixelFormat pixFmt;
};
explicit CRecSvcEnc(seastar::logger *pLogger,CRecSvcCfg *pCfg, QObject *parent = nullptr);
bool IsValid();
void EncStart(CodecParams params);
void EncStop();
bool AppendFrame(QImage im);
void EncPull();
void Save2File();
signals:
public slots:
private:
AVCodec *m_Codec;
AVCodecContext *m_CodecCtx;
seastar::logger *m_Logger;
QByteArray m_Encoded;
AVFrame *m_frame;
bool m_FrameDelayed;
QList<QImage> m_PendingQ;
AVPacket m_pkt;
CRecSvcCfg *m_Cfg;
void CopyFrame(QImage im);
};
#endif // REC_SVC_ENC_H
_______________________________________________
Libav-user mailing list
[email protected]
http://ffmpeg.org/mailman/listinfo/libav-user