I have built a class that reads avi file and displays it.
This is the defination for the class.
typedef struct {
AVFormatContext *fmt_ctx;
int stream_idx;
AVStream *video_stream;
AVCodecContext *codec_ctx;
AVCodec *decoder;
AVPacket *packet;
AVFrame *av_frame;
AVFrame *gl_frame;
struct SwsContext *conv_ctx;
unsigned int frame_tex;
}AppData;
class ClipPlayer{
private:
AppData data;
std::vector< AVFrame* > cache;
public:
ClipPlayer();
ClipPlayer(const ClipPlayer& player);
ClipPlayer& operator=(const ClipPlayer& player);
~ClipPlayer();
void initializeAppData();
void clearAppData();
bool readFrame();
bool initReadFrame();
void playCache();
void init();
void draw();
void reset();
}
In the init function the AVI file is read and the frames are saved in memory.
void init()
{
initializeAppData();
// open video
if (avformat_open_input(&data.fmt_ctx, stdstrPathOfVideo.c_str(), NULL, NULL) < 0) {
clearAppData();
return;
}
// find stream info
if (avformat_find_stream_info(data.fmt_ctx, NULL) < 0) {
clearAppData();
return;
}
// find the video stream
for (unsigned int i = 0; i < data.fmt_ctx->nb_streams; ++i)
{
if (data.fmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
data.stream_idx = i;
break;
}
}
if (data.stream_idx == -1)
{
clearAppData();
return;
}
data.video_stream = data.fmt_ctx->streams[data.stream_idx];
data.codec_ctx = data.video_stream->codec;
// find the decoder
data.decoder = avcodec_find_decoder(data.codec_ctx->codec_id);
if (data.decoder == NULL)
{
clearAppData();
return;
}
// open the decoder
if (avcodec_open2(data.codec_ctx, data.decoder, NULL) < 0)
{
clearAppData();
return;
}
// allocate the video frames
data.av_frame = av_frame_alloc();
data.gl_frame = av_frame_alloc();
int size = avpicture_get_size(AV_PIX_FMT_RGBA, data.codec_ctx->width,
data.codec_ctx->height);
uint8_t *internal_buffer = (uint8_t *)av_malloc(size * sizeof(uint8_t));
avpicture_fill((AVPicture *)data.gl_frame, internal_buffer, AV_PIX_FMT_RGBA,
data.codec_ctx->width, data.codec_ctx->height);
data.packet = (AVPacket *)av_malloc(sizeof(AVPacket));
}
/////////////////////////////////////////////////////////////
bool ClipPlayer::initReadFrame()
{
do {
glBindTexture(GL_TEXTURE_2D, data.frame_tex);
int error = av_read_frame(data.fmt_ctx, data.packet);
if (error)
{
av_free_packet(data.packet);
return false;
}
if (data.packet->stream_index == data.stream_idx)
{
int frame_finished = 0;
if (avcodec_decode_video2(data.codec_ctx, data.av_frame, &frame_finished,
data.packet) < 0) {
av_free_packet(data.packet);
return false;
}
if (frame_finished)
{
if (!data.conv_ctx)
{
data.conv_ctx = sws_getContext(data.codec_ctx->width,
data.codec_ctx->height, data.codec_ctx->pix_fmt,
data.codec_ctx->width, data.codec_ctx->height, AV_PIX_FMT_RGBA,
SWS_BICUBIC, NULL, NULL, NULL);
}
sws_scale(data.conv_ctx, data.av_frame->data, data.av_frame->linesize, 0,
data.codec_ctx->height, data.gl_frame->data, data.gl_frame->linesize);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, data.codec_ctx->width,
data.codec_ctx->height, GL_RGBA, GL_UNSIGNED_BYTE,
data.gl_frame->data[0]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
AVFrame *cachedValue = av_frame_alloc();
cachedValue->format = data.av_frame->format;
cachedValue->width = data.av_frame->width;
cachedValue->height = data.av_frame->height;
cachedValue->channels = data.av_frame->channels;
cachedValue->channel_layout = data.av_frame->channel_layout;
cachedValue->nb_samples = data.av_frame->nb_samples;
av_frame_get_buffer(cachedValue, 32);
av_frame_copy(cachedValue, data.av_frame);
av_frame_copy_props(cachedValue, data.av_frame);
cache.push_back((cachedValue));
}
}
} while (data.packet->stream_index != data.stream_idx);
////////////////////////////////////////////////////////////////////
In the play cache function the frames are displayed
void ClipPlayer::playCache()
{
glActiveTexture(GL_TEXTURE0);
sws_scale(data.conv_ctx, cache[loop]->data, cache[loop]->linesize, 0,
data.codec_ctx->height, data.gl_frame->data, data.gl_frame->linesize);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, data.codec_ctx->width,
data.codec_ctx->height, GL_RGBA, GL_UNSIGNED_BYTE,data.gl_frame->data[0]);
glBindTexture(GL_TEXTURE_2D, data.frame_tex);
}
In the destructor i try to free the memory
~ClipPlayer()
{
for (auto &frame : cache)
{
av_freep(frame);
}
}
I am not very proficient in using FFmpeg , my question is that have i freed the memory properly.
Your code two apparent problem.
You should use av_frame_unref() instead of av_freep() for AVFrames.
avpicture_fill is deprecated long ago. IF your ffmpeg installation is old. Update it. Otherwise use av_image_fill_arrays() instead.
You can refer up to date API documentation here: http://www.ffmpeg.org/doxygen/trunk/index.html
Hope that helps.
Related
Windows GDI ImageCapturing application consumes more GDI objects(Around 320 TaskManager) Single Monitor and Encoding using GDI+ image returns User/ Kernal Marshalling Buffer has been overflowed ​ERROR_MARSHALL_OVERFLOW Marshalling Buffer Error
CImage* getCImageFromBuffer(uint8_t *rgbaBuffer, int imgWidth, int imgHeight, int imgBpp)
{
CImage* image = NULL;
try
{
if (rgbaBuffer != NULL)
{
image = new CImage();
image->Create(imgWidth, imgHeight, imgBpp);
BITMAPINFO bitmapInfo = FormBitmapInfo(imgWidth, imgHeight, imgBpp, true);
HRESULT hErrorCode = SetDIBitsToDevice(image->GetDC(), 0, 0, imgWidth, imgHeight, 0, 0, 0, imgHeight,
rgbaBuffer, &bitmapInfo, DIB_RGB_COLORS);
image->ReleaseDC();
if (FAILED(hErrorCode) && image)
{
cout << "FormCImageFromRGBABuffer: SetDibitsToDevice API failed "<< hErrorCode;
delete image;
image = NULL;
}
else if (FAILED(hErrorCode))
{
image = NULL;
}
}
else
{
cout<< "FormCImageFromRGBABuffer failed, rgbaBuffer is null";
}
}
catch(...)
{
cout<< "Exception in FormCImageFromRGBABuffer";
}
return image;
}
I am trying to encoding incoming raw PCM audio data into an AAC encoded audio file. The following crashes with SIGABRT when it hits the avcodec_encode_audio2 call:
aac_encoding.c
#include <stdio.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswresample/swresample.h>
typedef struct AACEncoder {
AVFormatContext* pFormatCtx;
AVStream* audio_st;
AVCodecContext* pCodecCtx;
AVFrame* pFrame;
AVPacket* pkt;
uint8_t* frame_buf;
} AACEncoder;
AACEncoder *openEncoder(char* out_file) {
AACEncoder* encoder = (AACEncoder*)malloc(sizeof(AACEncoder*));
av_register_all();
AVFormatContext* pFormatCtx = avformat_alloc_context();
encoder->pFormatCtx = pFormatCtx;
AVOutputFormat* outFormat = av_guess_format(NULL, out_file, NULL);
pFormatCtx->oformat = outFormat;
if (avio_open(&pFormatCtx->pb, out_file, AVIO_FLAG_READ_WRITE) < 0) {
printf("Failed to open output file!\n");
return NULL;
}
AVStream* audio_st = avformat_new_stream(pFormatCtx, 0);
if (audio_st==NULL){
return NULL;
}
encoder->audio_st;
AVCodecContext* pCodecCtx = audio_st->codec;
encoder->pCodecCtx = pCodecCtx;
pCodecCtx->codec_id = outFormat->audio_codec;
pCodecCtx->codec_type = AVMEDIA_TYPE_AUDIO;
pCodecCtx->sample_fmt = AV_SAMPLE_FMT_FLTP;
pCodecCtx->sample_rate= 48000;
pCodecCtx->channel_layout = AV_CH_LAYOUT_MONO;
pCodecCtx->channels = av_get_channel_layout_nb_channels(pCodecCtx->channel_layout);
pCodecCtx->bit_rate = 64000;
av_dump_format(pFormatCtx, 0, out_file, 1);
AVCodec* pCodec = avcodec_find_encoder(pCodecCtx->codec_id);
if (!pCodec){
printf("Can not find encoder!\n");
return NULL;
}
if (avcodec_open2(pCodecCtx, pCodec,NULL) < 0){
printf("Failed to open encoder!\n");
return NULL;
}
AVFrame* pFrame = av_frame_alloc();
encoder->pFrame = pFrame;
pFrame->nb_samples= pCodecCtx->frame_size;
pFrame->format= pCodecCtx->sample_fmt;
int size = av_samples_get_buffer_size(NULL, pCodecCtx->channels,pCodecCtx->frame_size,pCodecCtx->sample_fmt, 1);
uint8_t* frame_buf = (uint8_t *)av_malloc(size);
encoder->frame_buf = frame_buf;
avcodec_fill_audio_frame(pFrame, pCodecCtx->channels, pCodecCtx->sample_fmt,(const uint8_t*)frame_buf, size, 1);
//Write Header
avformat_write_header(pFormatCtx,NULL);
AVPacket pkt;
encoder->pkt = &pkt;
av_new_packet(&pkt,size);
return encoder;
}
int writePCM(AACEncoder* encoder, int16_t* pcmData, size_t pcmSize) {
SwrContext* swr = swr_alloc();
av_opt_set_int(swr, "in_channel_layout", encoder->pCodecCtx->channel_layout, 0);
av_opt_set_int(swr, "out_channel_layout", encoder->pCodecCtx->channel_layout, 0);
av_opt_set_int(swr, "in_sample_rate", encoder->pCodecCtx->sample_rate, 0);
av_opt_set_int(swr, "out_sample_rate", encoder->pCodecCtx->sample_rate, 0);
av_opt_set_sample_fmt(swr, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0);
av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_FLT, 0);
swr_init(swr);
printf("Initialized SwrContext\n");
fflush(stdout);
int ret = swr_convert(swr, encoder->pFrame->extended_data, pcmSize, &pcmData, pcmSize);
int got_frame=0;
if(ret < 0){
printf("Failed to resample!\n");
return -1;
}
//Encode
ret = avcodec_encode_audio2(encoder->pCodecCtx, encoder->pkt, encoder->pFrame, &got_frame);
printf("Encoded audio using AAC\n");
fflush(stdout);
swr_free(&swr);
if(ret < 0){
printf("Failed to encode!\n");
return -1;
}
if (got_frame==1){
printf("Succeed to encode 1 frame! \tsize:%5d\n", encoder->pkt->size);
encoder->pkt->stream_index = encoder->audio_st->index;
ret = av_write_frame(encoder->pFormatCtx, encoder->pkt);
av_free_packet(encoder->pkt);
}
}
I am reading a AVI file using ffMpeg.
I want to cache the file into a vector and resuse it later.
This is my code.
typedef struct {
AVFormatContext *fmt_ctx;
int stream_idx;
AVStream *video_stream;
AVCodecContext *codec_ctx;
AVCodec *decoder;
AVPacket *packet;
AVFrame *av_frame;
AVFrame *gl_frame;
struct SwsContext *conv_ctx;
unsigned int frame_tex;
}AppData;
AppData data;
Here i am caching the file to a std::vector
std::vector< AVFrame* > cache;
bool initReadFrame()
{
do {
glBindTexture(GL_TEXTURE_2D, data.frame_tex);
int error = av_read_frame(data.fmt_ctx, data.packet);
if (error)
{
av_free_packet(data.packet);
return false;
}
if (data.packet->stream_index == data.stream_idx)
{
int frame_finished = 0;
if (avcodec_decode_video2(data.codec_ctx, data.av_frame, &frame_finished,
data.packet) < 0) {
av_free_packet(data.packet);
return false;
}
if (frame_finished)
{
if (!data.conv_ctx)
{
data.conv_ctx = sws_getContext(data.codec_ctx->width,
data.codec_ctx->height, data.codec_ctx->pix_fmt,
data.codec_ctx->width, data.codec_ctx->height, AV_PIX_FMT_RGBA,
SWS_BICUBIC, NULL, NULL, NULL);
}
sws_scale(data.conv_ctx, data.av_frame->data, data.av_frame->linesize, 0,
data.codec_ctx->height, data.gl_frame->data, data.gl_frame->linesize);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, data.codec_ctx->width,
data.codec_ctx->height, GL_RGBA, GL_UNSIGNED_BYTE,
data.gl_frame->data[0]);
cache.push_back(av_frame_clone(data.gl_frame)); // Pushing AVFrame* to vector
}
}
av_free_packet(data.packet);
} while (data.packet->stream_index != data.stream_idx);
return true;
}
here i am trying to read the buffer and updating GL_TEXTURE_2D
void playCache()
{
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, data.codec_ctx->width,
data.codec_ctx->height, GL_RGBA, GL_UNSIGNED_BYTE,
cache[temp]->data[0]);
temp++;
}
The issue i am facing is that when i try to read the Cached data the application crashes.
You are storing dead reference in your cache.
cache.push_back(av_frame_clone(data.gl_frame));
the doc says:
av_frame_clone: Create a new frame that references the same data as src.
When you destroy src, you loose its content and you can't access it in your cache.
You can try to move the ref to your new frame, or to copies its value.
Move:
AVFrame* cachedValue;
av_frame_move_ref(cachedValue, data.gl_frame);
cache.push_back(cachedValue);
Copy
AVFrame *cachedValue= av_frame_alloc();
cachedValue->format = data.gl_frame->format;
cachedValue->width = data.gl_frame->width;
cachedValue->height = data.gl_frame->height;
cachedValue->channels = data.gl_frame->channels;
cachedValue->channel_layout = data.gl_frame->channel_layout;
cachedValue->nb_samples = data.gl_frame->nb_samples;
av_frame_get_buffer(cachedValue, 32);
av_frame_copy(cachedValue, data.gl_frame);
av_frame_copy_props(cachedValue, data.gl_frame);
cache.push_back(cachedValue);
/////////////////////////////////////////////////
avformat_network_init();
initializeAppData();
// open video
if (avformat_open_input(&data.fmt_ctx, stdstrPathOfVideo.c_str(), NULL, NULL) < 0) {
clearAppData();
return;
}
// find stream info
if (avformat_find_stream_info(data.fmt_ctx, NULL) < 0) {
clearAppData();
return;
}
// dump debug info
// av_dump_format(data.fmt_ctx, 0, "D:\\E\\Event\\2019\\AVI_Badges\\Generic\\Generic.avi", 0);
// find the video stream
for (unsigned int i = 0; i < data.fmt_ctx->nb_streams; ++i)
{
if (data.fmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
data.stream_idx = i;
break;
}
}
if (data.stream_idx == -1)
{
clearAppData();
return;
}
data.video_stream = data.fmt_ctx->streams[data.stream_idx];
data.codec_ctx = data.video_stream->codec;
// find the decoder
data.decoder = avcodec_find_decoder(data.codec_ctx->codec_id);
if (data.decoder == NULL)
{
clearAppData();
return;
}
// open the decoder
if (avcodec_open2(data.codec_ctx, data.decoder, NULL) < 0)
{
clearAppData();
return;
}
// allocate the video frames
data.av_frame = av_frame_alloc();
data.gl_frame = av_frame_alloc();
int size = avpicture_get_size(AV_PIX_FMT_RGBA, data.codec_ctx->width,
data.codec_ctx->height);
uint8_t *internal_buffer = (uint8_t *)av_malloc(size * sizeof(uint8_t));
avpicture_fill((AVPicture *)data.gl_frame, internal_buffer, AV_PIX_FMT_RGBA,
data.codec_ctx->width, data.codec_ctx->height);
data.packet = (AVPacket *)av_malloc(sizeof(AVPacket));
This question already has answers here:
Pixel format conversion issue [FFMPEG]
(2 answers)
Closed 4 years ago.
I am trying to convert a standard RGB color space to YUV420P. I am struggling to figure out why I keep getting 'Warning: data is not aligned! This can lead to a speedloss' when executing the code. I have looked at a multitude of examples.
int ImageDecoder::rgb2yuv(uint8_t *src,
uint8_t *dest,
uint32_t width,
uint32_t height)
{
struct SwsContext *imgCtx = NULL;
AVFrame *pFrameYUV;
enum AVPixelFormat src_pix_fmt = AV_PIX_FMT_RGB24;
enum AVPixelFormat dst_pix_fmt = AV_PIX_FMT_YUV420P;
int ret;
int size;
const int RGBLinesize[1] = { 3 * (int)width };
pFrameYUV = av_frame_alloc();
pFrameYUV->width = width;
pFrameYUV->height = height;
pFrameYUV->format = dst_pix_fmt;
// Initialize pFrameYUV linesize
ret = av_image_alloc(pFrameYUV->data, pFrameYUV->linesize, pFrameYUV->width, pFrameYUV->height, AV_PIX_FMT_YUV420P, 1);
getLogger()->info("ImageDecoder:{} width={} height={} linesize[0]={} linesize[1]={} linesize[2]={}",
__func__, pFrameYUV->width, pFrameYUV->height, pFrameYUV->linesize[0], pFrameYUV->linesize[1], pFrameYUV->linesize[2]);
size = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pFrameYUV->width, pFrameYUV->height, 1);
imgCtx = sws_getCachedContext(imgCtx,
width,
height,
AV_PIX_FMT_RGB24,
pFrameYUV->width,
pFrameYUV->height,
AV_PIX_FMT_YUV420P,
SWS_BICUBIC, 0, 0, 0);
if( imgCtx == NULL)
{
getLogger()->error("ERROR: ImageDecoder: {} Cannot initialize the conversion context", __func__);
}
sws_scale(imgCtx,
(const uint8_t* const*)&src,
RGBLinesize,
0,
height,
pFrameYUV->data,
pFrameYUV->linesize);
memcpy(dest, &pFrameYUV->data[0], size);
sws_freeContext(imgCtx);
av_free(pFrameYUV);
}
I hope it helps you.
I am converting YUV444 decoded frames to RGBA format.
AVFrame* RGBFrame = av_frame_alloc();
RGBFrame->width = YUV_frame->width; RGBFrame->format = AV_PIX_FMT_RGBA;
RGBFrame->height = YUV_frame->height;
int ret = av_image_alloc(RGBFrame->data, RGBFrame->linesize, RGBFrame->width, RGBFrame->height, AV_PIX_FMT_RGBA, YUV_frame->pict_type);
if (ret < 0)
return false;
SwsContext* sws_Context = NULL;
sws_Context = sws_getCachedContext(sws_Context, YUV_frame->width, YUV_frame->height, pVideoCodecCtx->pix_fmt,
YUV_frame->width, YUV_frame->height, AV_PIX_FMT_RGBA, SWS_BILINEAR, NULL, NULL, NULL);
if (sws_Context == NULL) return false;
int result = sws_scale(sws_Context, YUV_frame->data, YUV_frame->linesize, 0, (int)YUV_frame->height, RGBFrame->data, RGBFrame->linesize);
if (result < 0) return false;
if (RGBFrame == NULL) {
av_frame_unref(RGBFrame);
return false;
}
sws_freeContext(sws_Context);
int ImageDecoder::rgb2yuv(uint8_t *src,
uint8_t *dest,
uint32_t *outBufferSize,
uint32_t width,
uint32_t height)
{
struct SwsContext *imgCtx = NULL;
uint8_t * RGBData[1] = {src};
const int RGBLinesize[1] = {3 * (int) width};
uint8_t * YUVData[] = {dest,
YUVData[0] + ((int) width * (int) height),
YUVData[1] + (((int) width * (int) height) / 4)};
const int YUVLinesize[] = {(int) width, (int) width / 2, (int) width / 2};
int size;
size = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, width, height, 1);
*outBufferSize = size;
imgCtx = sws_getCachedContext(imgCtx,
width,
height,
AV_PIX_FMT_RGB24,
width,
height,
AV_PIX_FMT_YUV420P,
SWS_BICUBIC, 0, 0, 0);
if (imgCtx == NULL)
{
getLogger()->error("ERROR: ImageDecoder: {} Cannot initialize the conversion context", __func__);
return -1;
}
sws_scale(imgCtx,
RGBData,
RGBLinesize,
0,
height,
YUVData,
YUVLinesize);
sws_freeContext(imgCtx);
return 0;
}
I wanna resize a picture by using the ffmpeg's func--->sws_scale().
Is there any one knows how to do it?
Do you have the source code for this function?
First you need to create a SwsContext (you need to do this only once) :
struct SwsContext *resize;
resize = sws_getContext(width1, height1, AV_PIX_FMT_YUV420P, width2, height2, PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL);
You need two frames for conversion, frame1 is the original frame, you need to explicitly allocate frame2 :
AVFrame* frame1 = avcodec_alloc_frame(); // this is your original frame
AVFrame* frame2 = avcodec_alloc_frame();
int num_bytes = avpicture_get_size(AV_PIX_FMT_RGB24, width2, height2);
uint8_t* frame2_buffer = (uint8_t *)av_malloc(num_bytes*sizeof(uint8_t));
avpicture_fill((AVPicture*)frame2, frame2_buffer, AV_PIX_FMT_RGB24, width2, height2);
You may use this part inside a loop if you need to resize each frame you receive :
// frame1 should be filled by now (eg using avcodec_decode_video)
sws_scale(resize, frame1->data, frame1->linesize, 0, height1, frame2->data, frame2->linesize);
Note that I also changed pixel format, but you can use the same pixel format for both frames
Runnable example in FFmpeg 2.8
Basically using arash's method, but runnable so you can try it out.
Generate one short video procedurally, and then convert it to 3 different sizes.
ffmpeg_encoder_init_frame and ffmpeg_encoder_scale are the key methods.
Source:
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#include <libavutil/opt.h>
#include <libswscale/swscale.h>
static AVCodecContext *c = NULL;
static AVFrame *frame;
static AVFrame *frame2;
static AVPacket pkt;
static FILE *file;
static struct SwsContext *sws_context = NULL;
static void ffmpeg_encoder_init_frame(AVFrame **framep, int width, int height) {
int ret;
AVFrame *frame;
frame = av_frame_alloc();
if (!frame) {
fprintf(stderr, "Could not allocate video frame\n");
exit(1);
}
frame->format = c->pix_fmt;
frame->width = width;
frame->height = height;
ret = av_image_alloc(frame->data, frame->linesize, frame->width, frame->height, frame->format, 32);
if (ret < 0) {
fprintf(stderr, "Could not allocate raw picture buffer\n");
exit(1);
}
*framep = frame;
}
static void ffmpeg_encoder_scale(uint8_t *rgb) {
sws_context = sws_getCachedContext(sws_context,
frame->width, frame->height, AV_PIX_FMT_YUV420P,
frame2->width, frame2->height, AV_PIX_FMT_YUV420P,
SWS_BICUBIC, NULL, NULL, NULL);
sws_scale(sws_context, (const uint8_t * const *)frame->data, frame->linesize, 0,
frame->height, frame2->data, frame2->linesize);
}
static void ffmpeg_encoder_set_frame_yuv_from_rgb(uint8_t *rgb) {
const int in_linesize[1] = { 3 * frame->width };
sws_context = sws_getCachedContext(sws_context,
frame->width, frame->height, AV_PIX_FMT_RGB24,
frame->width, frame->height, AV_PIX_FMT_YUV420P,
0, NULL, NULL, NULL);
sws_scale(sws_context, (const uint8_t * const *)&rgb, in_linesize, 0,
frame->height, frame->data, frame->linesize);
}
void generate_rgb(int width, int height, int pts, uint8_t **rgbp) {
int x, y, cur;
uint8_t *rgb = *rgbp;
rgb = realloc(rgb, 3 * sizeof(uint8_t) * height * width);
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
cur = 3 * (y * width + x);
rgb[cur + 0] = 0;
rgb[cur + 1] = 0;
rgb[cur + 2] = 0;
if ((frame->pts / 25) % 2 == 0) {
if (y < height / 2) {
if (x < width / 2) {
/* Black. */
} else {
rgb[cur + 0] = 255;
}
} else {
if (x < width / 2) {
rgb[cur + 1] = 255;
} else {
rgb[cur + 2] = 255;
}
}
} else {
if (y < height / 2) {
rgb[cur + 0] = 255;
if (x < width / 2) {
rgb[cur + 1] = 255;
} else {
rgb[cur + 2] = 255;
}
} else {
if (x < width / 2) {
rgb[cur + 1] = 255;
rgb[cur + 2] = 255;
} else {
rgb[cur + 0] = 255;
rgb[cur + 1] = 255;
rgb[cur + 2] = 255;
}
}
}
}
}
*rgbp = rgb;
}
void ffmpeg_encoder_start(const char *filename, int codec_id, int fps, int width, int height, float factor) {
AVCodec *codec;
int ret;
int width2 = width * factor;
int height2 = height * factor;
avcodec_register_all();
codec = avcodec_find_encoder(codec_id);
if (!codec) {
fprintf(stderr, "Codec not found\n");
exit(1);
}
c = avcodec_alloc_context3(codec);
if (!c) {
fprintf(stderr, "Could not allocate video codec context\n");
exit(1);
}
c->bit_rate = 400000;
c->width = width2;
c->height = height2;
c->time_base.num = 1;
c->time_base.den = fps;
c->gop_size = 10;
c->max_b_frames = 1;
c->pix_fmt = AV_PIX_FMT_YUV420P;
if (codec_id == AV_CODEC_ID_H264)
av_opt_set(c->priv_data, "preset", "slow", 0);
if (avcodec_open2(c, codec, NULL) < 0) {
fprintf(stderr, "Could not open codec\n");
exit(1);
}
file = fopen(filename, "wb");
if (!file) {
fprintf(stderr, "Could not open %s\n", filename);
exit(1);
}
ffmpeg_encoder_init_frame(&frame, width, height);
ffmpeg_encoder_init_frame(&frame2, width2, height2);
}
void ffmpeg_encoder_finish(void) {
uint8_t endcode[] = { 0, 0, 1, 0xb7 };
int got_output, ret;
do {
fflush(stdout);
ret = avcodec_encode_video2(c, &pkt, NULL, &got_output);
if (ret < 0) {
fprintf(stderr, "Error encoding frame\n");
exit(1);
}
if (got_output) {
fwrite(pkt.data, 1, pkt.size, file);
av_packet_unref(&pkt);
}
} while (got_output);
fwrite(endcode, 1, sizeof(endcode), file);
fclose(file);
avcodec_close(c);
av_free(c);
av_freep(&frame->data[0]);
av_frame_free(&frame);
av_freep(&frame2->data[0]);
av_frame_free(&frame2);
}
void ffmpeg_encoder_encode_frame(uint8_t *rgb) {
int ret, got_output;
ffmpeg_encoder_set_frame_yuv_from_rgb(rgb);
ffmpeg_encoder_scale(rgb);
frame2->pts = frame->pts;
av_init_packet(&pkt);
pkt.data = NULL;
pkt.size = 0;
ret = avcodec_encode_video2(c, &pkt, frame2, &got_output);
if (ret < 0) {
fprintf(stderr, "Error encoding frame\n");
exit(1);
}
if (got_output) {
fwrite(pkt.data, 1, pkt.size, file);
av_packet_unref(&pkt);
}
}
static void encode_example(float factor) {
char filename[255];
int pts;
int width = 320;
int height = 240;
uint8_t *rgb = NULL;
snprintf(filename, 255, "tmp." __FILE__ ".%.2f.h264", factor);
ffmpeg_encoder_start(filename, AV_CODEC_ID_H264, 25, width, height, factor);
for (pts = 0; pts < 100; pts++) {
frame->pts = pts;
generate_rgb(width, height, pts, &rgb);
ffmpeg_encoder_encode_frame(rgb);
}
ffmpeg_encoder_finish();
free(rgb);
}
int main(void) {
encode_example(0.5);
encode_example(1.0);
encode_example(2.0);
return EXIT_SUCCESS;
}
Run with:
gcc main.c -lavformat -lavcodec -lswresample -lswscale -lavutil -lx264
./a.out
ffplay tmp.main.c.0.50.h264
ffplay tmp.main.c.1.00.h264
ffplay tmp.main.c.2.00.h264
Tested on ffmpeg 3.4.11, Ubuntu 16.04. Source on GitHub. Adapted from doc/examples/decoding_encoding.c.