Hi,
I’m trying to encode(h264) a series of .png into a mp4 file. A cv::Mat holds the png data (BGR) and that is converted to YUV420P which is then encoded and written to a .mp4 file. I have added two block statements in the code to store images on the disk (before and after encoding). The first image before it gets encoded is correct but the second one after encoding is not. The avcodec_send_frame returns 0 so up to that point everything works.
Edit : I get an mp4 file of 1 MB but I can’t open it with vlc
ecodec.h
class ECodec
{
public:
MovieCodec();
~MovieCodec();
void MatToFrame( cv::Mat& image );
void encode( AVFrame *frame, AVPacket *pkt );
private:
FILE* m_file;
AVCodec* m_encoder = NULL;
AVCodecContext* m_codecContextOut = NULL;
AVPacket* m_packet = NULL;
};
ecodec.cpp
ECodec::ECodec() :
// m_encoder( avcodec_find_encoder_by_name( videoCodec.c_str()))
m_encoder( avcodec_find_encoder( AV_CODEC_ID_H264 ))
{
m_file = fopen( "c:\\tmp\\outputVideo.mp4", "wb");
}
void ECodec::MatToFrame( cv::Mat& image )
{
int ret( 0 );
int frameRate( 24 );
AVFrame *frame = NULL;
m_encoder( avcodec_find_encoder( AV_CODEC_ID_H264 ))
m_codecContextOut = avcodec_alloc_context3( m_encoder );
m_codecContextOut->width = 800;
m_codecContextOut->height = 640;
m_codecContextOut->bit_rate = 400000;//m_codecContextOut->width * m_codecContextOut->height * 3;
m_codecContextOut->time_base = (AVRational){1, 24};
m_codecContextOut->framerate = (AVRational){24, 1};
m_codecContextOut->codec_tag = AV_CODEC_ID_H264;
m_codecContextOut->pix_fmt = AV_PIX_FMT_YUV420P;
m_codecContextOut->codec_type = AVMEDIA_TYPE_VIDEO;
m_codecContextOut->gop_size = 1;
m_codecContextOut->max_b_frames = 1;
av_log_set_level(AV_LOG_VERBOSE);
ret = av_opt_set(m_codecContextOut->priv_data, "preset", "slow", 0);
ret = avcodec_open2(m_codecContextOut, m_encoder, NULL);
frame = av_frame_alloc();
frame->format = AV_PIX_FMT_YUV420P;
frame->width = image.cols();
frame->height = image.rows();
ret = av_image_alloc(frame->data, frame->linesize, frame->width, frame->height, AV_PIX_FMT_YUV420P, 1);
if (ret < 0)
{
return;
}
struct SwsContext *sws_ctx;
sws_ctx = sws_getContext((int)image.cols(), (int)image.rows(), AV_PIX_FMT_RGB24,
(int)image.cols(), (int)image.rows(), AV_PIX_FMT_YUV420P,
0, NULL, NULL, NULL);
const uint8_t* rgbData[1] = { (uint8_t* )image.getData() };
int rgbLineSize[1] = { 3 * image.cols() };
sws_scale(sws_ctx, rgbData, rgbLineSize, 0, image.rows(), frame->data, frame->linesize);
frame->pict_type = AV_PICTURE_TYPE_I;
cv::Mat yuv420p(frame->height + frame->height/2, frame->width, CV_8UC1,frame->data[0]);
cv::Mat cvmIm;
cv::cvtColor(yuv420p,cvmIm,CV_YUV420p2BGR);
cv::imwrite("c:\\tmp\\rawimage.png", cvmIm);
//OK
m_packet = av_packet_alloc();
ret = av_new_packet( m_packet, m_codecContextOut->width * m_codecContextOut->height * 3 );
/* encode the image */
encode( frame, m_packet );
avcodec_free_context(&m_codecContextOut);
av_frame_free(&frame);
av_packet_free( &m_packet );
}
void ECodec::encode( AVFrame *frame, AVPacket *pkt )
{
int ret;
/* send the frame to the encoder */
ret = avcodec_send_frame( m_codecContextOut, frame);
if (ret < 0)
{
fprintf(stderr, "Error sending a frame for encoding\n");
exit(1);
}
do
{
ret = avcodec_receive_packet(m_codecContextOut, pkt);
if (ret == 0)
{
cv::Mat yuv420p(frame->height + frame->height/2, frame->width, CV_8UC1,pkt->data);
cv::Mat cvmIm;
cv::cvtColor(yuv420p,cvmIm,CV_YUV420p2BGR);
cv::imwrite("c:\\tmp\\rawencodedimage.png", cvmIm);
//NOT OK
fwrite(pkt->data, 1, pkt->size, m_file );
av_packet_unref(pkt);
break;
}
else if ((ret < 0) && (ret != AVERROR(EAGAIN)))
{
return;
}
else if (ret == AVERROR(EAGAIN))
{
ret = avcodec_send_frame(m_codecContextOut, NULL);
if (0 > ret)
{
return;
}
}
} while (ret == 0);
}