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 a block statement in the code to store
image on the disk (before encoding) and that image is correct so the frame
holds the correct data before it gets passed. The avcodec_send_frame returns 0
so up to that point everything works. But I get an mp4 file of 1 MB and I can't
open it with vlc. Below is a short summary of my code
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)
{
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);
}
_______________________________________________
ffmpeg-user mailing list
[email protected]
https://ffmpeg.org/mailman/listinfo/ffmpeg-user
To unsubscribe, visit link above, or email
[email protected] with subject "unsubscribe".