FFMPEG: Decode and then encode frames to JPEG images

I’ve used FFMPEG library for a while. Actually, the FFMPEG library’s decoding process flow can be described as the following picture. If you want to read the videos and then save to jpeg file, you can take a look on my programming code (tested on FFMPEG ver. 4 library).

// VideoProcessing.cpp 
//
#pragma once
using namespace std;
#include <iostream>
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavdevice/avdevice.h>
#include <libavformat/avformat.h>
#include <libavfilter/avfilter.h>
#include <libavutil/avutil.h>
#include <libswscale/swscale.h>
#pragma comment(lib, "avcodec.lib")
#pragma comment(lib, "avdevice.lib")
#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "avfilter.lib")
#pragma comment(lib, "avutil.lib")
#pragma comment(lib, "swscale.lib")
}

void SaveToJPEG(AVFrame* pFrame, const char * folderName, int index)

{

// Setup Output Path
char outFile[256] = { 0 };
sprintf_s(outFile, sizeof(outFile)/sizeof(outFile[0]), "%s\OutputImages-%d.jpg", folderName, index);


AVFormatContext* pFormatCtx = avformat_alloc_context();

// Setup the output format
pFormatCtx->oformat = av_guess_format("mjpeg", NULL, NULL);

// Initializext
if (avio_open(&pFormatCtx->pb, outFile, AVIO_FLAG_READ_WRITE) < 0) {

printf("Couldn't open output file.");

return;

}

// Get a new Stream from the indicated format context
AVStream* pAVStream = avformat_new_stream(pFormatCtx, 0);

if (pAVStream == NULL) {

return;

}

// Find encoder from the codec identifier.

AVCodec* pCodec = avcodec_find_encoder(pFormatCtx->oformat->video_codec);

if (!pCodec) {
printf("Codec not found.");
return;
}
// Setup the codec context
AVCodecContext* codecCtx = avcodec_alloc_context3(pCodec);
codecCtx->codec_id = pFormatCtx->oformat->video_codec;
codecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
codecCtx->pix_fmt = AV_PIX_FMT_YUVJ420P;
codecCtx->width = pFrame->width;
codecCtx->height = pFrame->height;
codecCtx->time_base = AVRational{ 1,25 };

// Open the codec
if (avcodec_open2(codecCtx, pCodec, NULL) < 0) {

printf("Could not open codec.");

return;

}
// assign the codec context to the stream parameters.
avcodec_parameters_from_context(pAVStream->codecpar, codecCtx);


//Write Header
avformat_write_header(pFormatCtx, NULL);

int y_size = (codecCtx->width) * (codecCtx->height);

// assign large enough space
AVPacket pkt;

av_new_packet(&pkt, y_size);

int got_picture = 0;

// Use avcodec_send_frame()/avcodec_receive_packet() instead
int ret = avcodec_send_frame(codecCtx, pFrame);

if (ret < 0) {

printf("Encode Error.n");

return;

}
else
{
ret = avcodec_receive_packet(codecCtx, &pkt);

ret = av_write_frame(pFormatCtx, &pkt);

}

av_packet_unref(&pkt);

//Write Trailer
av_write_trailer(pFormatCtx);

printf("Encode Successful.n");

avcodec_close(codecCtx);

avio_close(pFormatCtx->pb);

avformat_free_context(pFormatCtx);
avcodec_free_context(&codecCtx);
}

int main(int argc, char * argv[])
{

if (argc < 2) {
cout << "You need to specify a media file." << endl;
cout << "Command line : VideoProcessing.exe [input_video_path] [output_folder]" << endl;
return -1;
}

AVFormatContext* pFormatContext = avformat_alloc_context();
if (!pFormatContext) {
cout << "ERROR could not allocate memory for Format Context" << endl;
return -1;
}

if (avformat_open_input(&pFormatContext, argv[1], NULL, NULL) != 0) {
cout << "ERROR could not open the file" << endl;
return -1;
}

if (avformat_find_stream_info(pFormatContext, NULL) < 0) {
cout << "ERROR could not get the stream info" << endl;
return -1;
}

// Initialize the codec, paramters for subsequent useage.
AVCodec* pCodec = NULL;
AVCodecParameters* pCodecParameters = NULL;
int videoStreamIndex = -1;

for (int i = 0; i < pFormatContext->nb_streams; i++)
{
AVCodecParameters* pLocalCodecParameters = NULL;
// Read the codec parameters corresponding to each stream.
pLocalCodecParameters = pFormatContext->streams[i]->codecpar;

AVCodec* pLocalCodec = NULL;
pLocalCodec = avcodec_find_decoder(pLocalCodecParameters->codec_id);

if (pLocalCodec == NULL)
{
cout << "[ERROR] Cannot find the codec" << endl;
}

if (pLocalCodecParameters->codec_type == AVMEDIA_TYPE_VIDEO)
{
if (videoStreamIndex == -1)
{
videoStreamIndex = i;
pCodec = pLocalCodec;
pCodecParameters = pLocalCodecParameters;
}
}
}

AVCodecContext* pCodecContext = avcodec_alloc_context3(pCodec);
if (pCodecContext == NULL)
{
cout << "Fail to allocate the memoery to the Codec Context." << endl;
return -1;
}

if (avcodec_parameters_to_context(pCodecContext, pCodecParameters) < 0)
{
cout << "failed to copy codec params to codec context" << endl;
return -1;
}

if (avcodec_open2(pCodecContext, pCodec, NULL) < 0)
{
cout << "failed to open codec through avcodec_open2" << endl;
return -1;
}

AVFrame* pFrame = av_frame_alloc();
if (!pFrame)
{
cout << "failed to allocated memory for AVFrame" << endl;
return -1;
}

AVPacket* pPacket = av_packet_alloc();
if (!pPacket)
{
cout << "failed to allocated memory for AVPacket" << endl;
return -1;
}

int indexOfFrame = 0;
while (av_read_frame(pFormatContext, pPacket) >= 0)
{
// if it's the video stream
if (pPacket->stream_index == videoStreamIndex) {
int response = avcodec_send_packet(pCodecContext, pPacket);
if (response < 0)
{
break;
}
else
{
response = avcodec_receive_frame(pCodecContext, pFrame);

if (response >= 0)
{
indexOfFrame++;
SaveToJPEG(pFrame, argv[2], indexOfFrame);
}
}
}
av_packet_unref(pPacket);
// Limit the number of output frame to be 5.
if (indexOfFrame == 5)
{
break;
}

}
// https://ffmpeg.org/doxygen/trunk/group__lavc__packet.html#ga63d5a489b419bd5d45cfd09091cbcbc2
avformat_close_input(&pFormatContext);
av_frame_free(&pFrame);
avcodec_free_context(&pCodecContext);
}

Reference:

  1. FFMPEG libav decode note
  2. FFMPEG libav tutorial
  3. 用AVCodecParameters代替AVCodecContext
  4. 用FFmpeg保存JPEG图片

This entry was posted in C, FFMPEG. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *