How to extract grayscale image from a video with ffmpeg-library? - c

I'v compiled and tested this tutorial from here which works just fine. After I tried to edit the tutorial to read/convert frames into grayscale. I just changed pFrameRGB to pFrameGray, PIX_FMT_RGB24 to PIX_FMT_GRAY16 and to save just the 200th frame. It compiles and run but the image don't show the expected. What's wrong?
The image:
The edited code:
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <stdio.h>
// compatibility with newer API
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,28,1)
#define av_frame_alloc avcodec_alloc_frame
#define av_frame_free avcodec_free_frame
#endif
void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame) {
FILE *pFile;
char szFilename[32];
int y;
// Open file
sprintf(szFilename, "frame%d.ppm", iFrame);
pFile=fopen(szFilename, "wb");
if(pFile==NULL)
return;
// Write header
fprintf(pFile, "P6\n%d %d\n255\n", width, height);
// Write pixel data
for(y=0; y<height; y++)
fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile);
// Close file
fclose(pFile);
}
int main(int argc, char *argv[]) {
// Initalizing these to NULL prevents segfaults!
AVFormatContext *pFormatCtx = NULL;
int i, videoStream;
AVCodecContext *pCodecCtxOrig = NULL;
AVCodecContext *pCodecCtx = NULL;
AVCodec *pCodec = NULL;
AVFrame *pFrame = NULL;
AVFrame *pFrameGRAY = NULL;
AVPacket packet;
int frameFinished;
int numBytes;
uint8_t *buffer = NULL;
struct SwsContext *sws_ctx = NULL;
if(argc < 2) {
printf("Please provide a movie file\n");
return -1;
}
// Register all formats and codecs
av_register_all();
// Open video file
if(avformat_open_input(&pFormatCtx, argv[1], NULL, NULL)!=0)
return -1; // Couldn't open file
// Retrieve stream information
if(avformat_find_stream_info(pFormatCtx, NULL)<0)
return -1; // Couldn't find stream information
// Dump information about file onto standard error
av_dump_format(pFormatCtx, 0, argv[1], 0);
// Find the first video stream
videoStream=-1;
for(i=0; i<pFormatCtx->nb_streams; i++)
if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO) {
videoStream=i;
break;
}
if(videoStream==-1)
return -1; // Didn't find a video stream
// Get a pointer to the codec context for the video stream
pCodecCtxOrig=pFormatCtx->streams[videoStream]->codec;
// Find the decoder for the video stream
pCodec=avcodec_find_decoder(pCodecCtxOrig->codec_id);
if(pCodec==NULL) {
fprintf(stderr, "Unsupported codec!\n");
return -1; // Codec not found
}
// Copy context
pCodecCtx = avcodec_alloc_context3(pCodec);
if(avcodec_copy_context(pCodecCtx, pCodecCtxOrig) != 0) {
fprintf(stderr, "Couldn't copy codec context");
return -1; // Error copying codec context
}
// Open codec
if(avcodec_open2(pCodecCtx, pCodec, NULL)<0)
return -1; // Could not open codec
// Allocate video frame
pFrame=av_frame_alloc();
// Allocate an AVFrame structure
pFrameGRAY=av_frame_alloc();
if(pFrameGRAY==NULL)
return -1;
// Determine required buffer size and allocate buffer
numBytes=avpicture_get_size(PIX_FMT_GRAY16, pCodecCtx->width,
pCodecCtx->height);
buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));
// Assign appropriate parts of buffer to image planes in pFrameGRAY
// Note that pFrameGRAY is an AVFrame, but AVFrame is a superset
// of AVPicture
avpicture_fill((AVPicture *)pFrameGRAY, buffer, PIX_FMT_GRAY16,
pCodecCtx->width, pCodecCtx->height);
// initialize SWS context for software scaling
sws_ctx = sws_getContext(pCodecCtx->width,
pCodecCtx->height,
pCodecCtx->pix_fmt,
pCodecCtx->width,
pCodecCtx->height,
PIX_FMT_GRAY16,
SWS_BILINEAR,
NULL,
NULL,
NULL
);
// Read frames and save first five frames to disk
i=0;
while(av_read_frame(pFormatCtx, &packet)>=0) {
// Is this a packet from the video stream?
if(packet.stream_index==videoStream) {
// Decode video frame
avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
// Did we get a video frame?
if(frameFinished) {
// Convert the image from its native format to GRAY
sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data,
pFrame->linesize, 0, pCodecCtx->height,
pFrameGRAY->data, pFrameGRAY->linesize);
// Save the frame to disk
if(++i==200)
SaveFrame(pFrameGRAY, pCodecCtx->width, pCodecCtx->height,
i);
}
}
// Free the packet that was allocated by av_read_frame
av_free_packet(&packet);
}
// Free the GRAY image
av_free(buffer);
av_frame_free(&pFrameGRAY);
// Free the YUV frame
av_frame_free(&pFrame);
// Close the codecs
avcodec_close(pCodecCtx);
avcodec_close(pCodecCtxOrig);
// Close the video file
avformat_close_input(&pFormatCtx);
return 0;
}

Your main problem is here:
fprintf(pFile, "P6\n%d %d\n255\n", width, height);
According to some info on PPM i found: http://paulbourke.net/dataformats/ppm/ you're writing the PPM header with the identifier P6 which means "rgb color". If you're making a greyscale image, you want to use P5 instead.
As well, you're using the pixel format PIX_FMT_GRAY16 from ffmpeg, but you're interpreting it as bytes. you probably meant to use PIX_FMT_GRAY8 instead. If you actually want 16-bit grayscale in your output, you should change the third number in the PPM header from 255 to 65535.

Related

Setting ffmpeg properly in ubuntu 16.04

I am following this website for ffmpeg tutorial: http://dranger.com
I tried to compile the programs after setting up ffmpeg in ubuntu by looking on some online videos but none of them worked. Some times GCC gives me undefined reference error and sometimes header not found error. I looked on some of the answers on SO that said that we need to do some change in the code as the new api is not backwards compatible but still GCC gives me undefined reference error.
Here is the code that I am trying to compile:
// tutorial01.c
// Code based on a tutorial by Martin Bohme (boehme#inb.uni-luebeckREMOVETHIS.de)
// Tested on Gentoo, CVS version 5/01/07 compiled with GCC 4.1.1
// With updates from https://github.com/chelyaev/ffmpeg-tutorial
// Updates tested on:
// LAVC 54.59.100, LAVF 54.29.104, LSWS 2.1.101
// on GCC 4.7.2 in Debian February 2015
// A small sample program that shows how to use libavformat and libavcodec to
// read video from a file.
//
// Use
//
// gcc -o tutorial01 tutorial01.c -lavformat -lavcodec -lswscale -lz
//
// to build (assuming libavformat and libavcodec are correctly installed
// your system).
//
// Run using
//
// tutorial01 myvideofile.mpg
//
// to write the first five frames from "myvideofile.mpg" to disk in PPM
// format.
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <stdio.h>
// compatibility with newer API
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,28,1)
#define av_frame_alloc avcodec_alloc_frame
#define av_frame_free avcodec_free_frame
#endif
void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame) {
FILE *pFile;
char szFilename[32];
int y;
// Open file
sprintf(szFilename, "frame%d.ppm", iFrame);
pFile=fopen(szFilename, "wb");
if(pFile==NULL)
return;
// Write header
fprintf(pFile, "P6\n%d %d\n255\n", width, height);
// Write pixel data
for(y=0; y<height; y++)
fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile);
// Close file
fclose(pFile);
}
int main(int argc, char *argv[]) {
// Initalizing these to NULL prevents segfaults!
AVFormatContext *pFormatCtx = NULL;
int i, videoStream;
AVCodecContext *pCodecCtxOrig = NULL;
AVCodecContext *pCodecCtx = NULL;
AVCodec *pCodec = NULL;
AVFrame *pFrame = NULL;
AVFrame *pFrameRGB = NULL;
AVPacket packet;
int frameFinished;
int numBytes;
uint8_t *buffer = NULL;
struct SwsContext *sws_ctx = NULL;
if(argc < 2) {
printf("Please provide a movie file\n");
return -1;
}
// Register all formats and codecs
av_register_all();
// Open video file
if(avformat_open_input(&pFormatCtx, argv[1], NULL, NULL)!=0)
return -1; // Couldn't open file
// Retrieve stream information
if(avformat_find_stream_info(pFormatCtx, NULL)<0)
return -1; // Couldn't find stream information
// Dump information about file onto standard error
av_dump_format(pFormatCtx, 0, argv[1], 0);
// Find the first video stream
videoStream=-1;
for(i=0; i<pFormatCtx->nb_streams; i++)
if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO) {
videoStream=i;
break;
}
if(videoStream==-1)
return -1; // Didn't find a video stream
// Get a pointer to the codec context for the video stream
pCodecCtxOrig=pFormatCtx->streams[videoStream]->codec;
// Find the decoder for the video stream
pCodec=avcodec_find_decoder(pCodecCtxOrig->codec_id);
if(pCodec==NULL) {
fprintf(stderr, "Unsupported codec!\n");
return -1; // Codec not found
}
// Copy context
pCodecCtx = avcodec_alloc_context3(pCodec);
if(avcodec_copy_context(pCodecCtx, pCodecCtxOrig) != 0) {
fprintf(stderr, "Couldn't copy codec context");
return -1; // Error copying codec context
}
// Open codec
if(avcodec_open2(pCodecCtx, pCodec, NULL)<0)
return -1; // Could not open codec
// Allocate video frame
pFrame=av_frame_alloc();
// Allocate an AVFrame structure
pFrameRGB=av_frame_alloc();
if(pFrameRGB==NULL)
return -1;
// Determine required buffer size and allocate buffer
numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,
pCodecCtx->height);
buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));
// Assign appropriate parts of buffer to image planes in pFrameRGB
// Note that pFrameRGB is an AVFrame, but AVFrame is a superset
// of AVPicture
avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24,
pCodecCtx->width, pCodecCtx->height);
// initialize SWS context for software scaling
sws_ctx = sws_getContext(pCodecCtx->width,
pCodecCtx->height,
pCodecCtx->pix_fmt,
pCodecCtx->width,
pCodecCtx->height,
PIX_FMT_RGB24,
SWS_BILINEAR,
NULL,
NULL,
NULL
);
// Read frames and save first five frames to disk
i=0;
while(av_read_frame(pFormatCtx, &packet)>=0) {
// Is this a packet from the video stream?
if(packet.stream_index==videoStream) {
// Decode video frame
avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
// Did we get a video frame?
if(frameFinished) {
// Convert the image from its native format to RGB
sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data,
pFrame->linesize, 0, pCodecCtx->height,
pFrameRGB->data, pFrameRGB->linesize);
// Save the frame to disk
if(++i<=5)
SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height,
i);
}
}
// Free the packet that was allocated by av_read_frame
av_free_packet(&packet);
}
// Free the RGB image
av_free(buffer);
av_frame_free(&pFrameRGB);
// Free the YUV frame
av_frame_free(&pFrame);
// Close the codecs
avcodec_close(pCodecCtx);
avcodec_close(pCodecCtxOrig);
// Close the video file
avformat_close_input(&pFormatCtx);
return 0;
}
This is the command I use to compile:
gcc -o tutorial01 tutorial01.c -lavformat -lavcodec -lswscale -lz

libavcodec : how to encode with h264 codec ,with mp4 container using controllable frame rate and bitrate(through c code)

I am trying to record the screen of a pc and encode the recorded frames using h264 encoder
and wrap them into a mp4 container.I want to do this because this super user link https://superuser.com/questions/300897/what-is-a-codec-e-g-divx-and-how-does-it-differ-from-a-file-format-e-g-mp/300997#300997 suggests it allows good trade-off between size and quality of the output file.
The application I am working on should allow users to record a few hours of video and have the minimum output file size with decent quality.
The code I have cooked up so far allows me to record and save .mpg(container) files with the mpeg1video encoder
Running:
ffmpeg -i test.mpg
on the output file gives the following output:
[mpegvideo # 028c7400] Estimating duration from bitrate, this may be inaccurate
Input #0, mpegvideo, from 'test.mpg':
Duration: 00:00:00.29, bitrate: 104857 kb/s
Stream #0:0: Video: mpeg1video, yuv420p(tv), 1366x768 [SAR 1:1 DAR 683:384], 104857 kb/s, 25 fps, 25 tbr, 1200k tbn, 25 tbc
I have these settings for my output:
const char * filename="test.mpg";
int codec_id= AV_CODEC_ID_MPEG1VIDEO;
AVCodec *codec11;
AVCodecContext *outContext= NULL;
int got_output;
FILE *f;
AVPacket pkt;
uint8_t endcode[] = { 0, 0, 1, 0xb7 };
/* put sample parameters */
outContext->bit_rate = 400000;
/* resolution must be a multiple of two */
outContext->width=pCodecCtx->width;
outContext->height=pCodecCtx->height;
/* frames per second */
outContext->time_base.num=1;
outContext->time_base.den=25;
/* emit one intra frame every ten frames
* check frame pict_type before passing frame
* to encoder, if frame->pict_type is AV_PICTURE_TYPE_I
* then gop_size is ignored and the output of encoder
* will always be I frame irrespective to gop_size
*/
outContext->gop_size = 10;
outContext->max_b_frames = 1;
outContext->pix_fmt = AV_PIX_FMT_YUV420P;
When I change int codec_id= AV_CODEC_ID_MPEG1VIDEO to int codec_id= AV_CODEC_ID_H264 i get a file that does not play with vlc.
I have read that writing the
uint8_t endcode[] = { 0, 0, 1, 0xb7 };
array at the end of your file when finished encoding makes your file a legitimate mpeg file.It is written like this:
fwrite(endcode, 1, sizeof(endcode), f);
fclose(f);
in my code. Should I do the same thing when I change my encoder to AV_CODEC_ID_H264?
I am capturing using gdi input like this:
AVDictionary* options = NULL;
//Set some options
//grabbing frame rate
av_dict_set(&options,"framerate","30",0);
AVInputFormat *ifmt=av_find_input_format("gdigrab");
if(avformat_open_input(&pFormatCtx,"desktop",ifmt,&options)!=0){
printf("Couldn't open input stream.\n");
return -1;
}
I want to be able to modify my grabbing rate to optimize for the outptut file size
but When I change it to 20 for example I get a video that plays so fast.How do
I get a video that plays with normal speed with frames captured at 20 fps or any
lower frame rate value?
While recording I get the following output on the standard error output:
[gdigrab # 00cdb8e0] Capturing whole desktop as 1366x768x32 at (0,0)
Input #0, gdigrab, from '(null)':
Duration: N/A, start: 1420718663.655713, bitrate: 1006131 kb/s
Stream #0:0: Video: bmp, bgra, 1366x768, 1006131 kb/s, 29.97 tbr, 1000k tbn, 29.97 tbc
[swscaler # 00d24120] Warning: data is not aligned! This can lead to a speedloss
[mpeg1video # 00cdd160] AVFrame.format is not set
[mpeg1video # 00cdd160] AVFrame.width or height is not set
[mpeg1video # 00cdd160] AVFrame.format is not set
[mpeg1video # 00cdd160] AVFrame.width or height is not set
[mpeg1video # 00cdd160] AVFrame.format is not set
How do I get rid of this error in my code?
In summary:
1) How do I encode h264 video wrapped into mp4 container?
2) How do I capture at lower frame rates and still play
the encoded video at normal speed?
3) How do I set the format(and which format--depends on the codec?)
and width and height info on the frames I write?
The code I am using in its entirety is shown below
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavdevice/avdevice.h"
#include <libavutil/opt.h>
#include <libavutil/channel_layout.h>
#include <libavutil/common.h>
#include <libavutil/imgutils.h>
#include <libavutil/mathematics.h>
#include <libavutil/samplefmt.h>
//SDL
#include "SDL.h"
#include "SDL_thread.h"
}
//Output YUV420P
#define OUTPUT_YUV420P 0
//'1' Use Dshow
//'0' Use GDIgrab
#define USE_DSHOW 0
int main(int argc, char* argv[])
{
//1.WE HAVE THE FORMAT CONTEXT
//THIS IS FROM THE DESKTOP GRAB STREAM.
AVFormatContext *pFormatCtx;
int i, videoindex;
AVCodecContext *pCodecCtx;
AVCodec *pCodec;
av_register_all();
avformat_network_init();
//ASSIGN STH TO THE FORMAT CONTEXT.
pFormatCtx = avformat_alloc_context();
//Register Device
avdevice_register_all();
//Windows
#ifdef _WIN32
#if USE_DSHOW
//Use dshow
//
//Need to Install screen-capture-recorder
//screen-capture-recorder
//Website: http://sourceforge.net/projects/screencapturer/
//
AVInputFormat *ifmt=av_find_input_format("dshow");
//if(avformat_open_input(&pFormatCtx,"video=screen-capture-recorder",ifmt,NULL)!=0){
if(avformat_open_input(&pFormatCtx,"video=UScreenCapture",ifmt,NULL)!=0){
printf("Couldn't open input stream.\n");
return -1;
}
#else
//Use gdigrab
AVDictionary* options = NULL;
//Set some options
//grabbing frame rate
av_dict_set(&options,"framerate","30",0);
//The distance from the left edge of the screen or desktop
//av_dict_set(&options,"offset_x","20",0);
//The distance from the top edge of the screen or desktop
//av_dict_set(&options,"offset_y","40",0);
//Video frame size. The default is to capture the full screen
//av_dict_set(&options,"video_size","640x480",0);
AVInputFormat *ifmt=av_find_input_format("gdigrab");
if(avformat_open_input(&pFormatCtx,"desktop",ifmt,&options)!=0){
printf("Couldn't open input stream.\n");
return -1;
}
#endif
#endif//FOR THE WIN32 THING.
if(avformat_find_stream_info(pFormatCtx,NULL)<0)
{
printf("Couldn't find stream information.\n");
return -1;
}
videoindex=-1;
for(i=0; i<pFormatCtx->nb_streams; i++)
if(pFormatCtx->streams[i]->codec->codec_type
==AVMEDIA_TYPE_VIDEO)
{
videoindex=i;
break;
}
if(videoindex==-1)
{
printf("Didn't find a video stream.\n");
return -1;
}
pCodecCtx=pFormatCtx->streams[videoindex]->codec;
pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
if(pCodec==NULL)
{
printf("Codec not found.\n");
return -1;
}
if(avcodec_open2(pCodecCtx, pCodec,NULL)<0)
{
printf("Could not open codec.\n");
return -1;
}
//THIS IS WHERE YOU CONTROL THE FORMAT(THROUGH FRAMES).
AVFrame *pFrame;
pFrame=av_frame_alloc();
int ret, got_picture;
AVPacket *packet=(AVPacket *)av_malloc(sizeof(AVPacket));
//TRY TO INIT THE PACKET HERE
av_init_packet(packet);
//Output Information-----------------------------
printf("File Information---------------------\n");
av_dump_format(pFormatCtx,0,NULL,0);
printf("-------------------------------------------------\n");
//<<--FOR WRITING MPG FILES
//<<--START:PREPARE TO WRITE YOUR MPG FILE.
const char * filename="test.mpg";
int codec_id= AV_CODEC_ID_MPEG1VIDEO;
AVCodec *codec11;
AVCodecContext *outContext= NULL;
int got_output;
FILE *f;
AVPacket pkt;
uint8_t endcode[] = { 0, 0, 1, 0xb7 };
printf("Encode video file %s\n", filename);
/* find the mpeg1 video encoder */
codec11 = avcodec_find_encoder((AVCodecID)codec_id);
if (!codec11) {
fprintf(stderr, "Codec not found\n");
exit(1);
}
outContext = avcodec_alloc_context3(codec11);
if (!outContext) {
fprintf(stderr, "Could not allocate video codec context\n");
exit(1);
}
/* put sample parameters */
outContext->bit_rate = 400000;
/* resolution must be a multiple of two */
outContext->width=pCodecCtx->width;
outContext->height=pCodecCtx->height;
/* frames per second */
outContext->time_base.num=1;
outContext->time_base.den=25;
/* emit one intra frame every ten frames
* check frame pict_type before passing frame
* to encoder, if frame->pict_type is AV_PICTURE_TYPE_I
* then gop_size is ignored and the output of encoder
* will always be I frame irrespective to gop_size
*/
outContext->gop_size = 10;
outContext->max_b_frames = 1;
outContext->pix_fmt = AV_PIX_FMT_YUV420P;
if (codec_id == AV_CODEC_ID_H264)
av_opt_set(outContext->priv_data, "preset", "slow", 0);
/* open it */
if (avcodec_open2(outContext, codec11, NULL) < 0) {
fprintf(stderr, "Could not open codec\n");
exit(1);
}
f = fopen(filename, "wb");
if (!f) {
fprintf(stderr, "Could not open %s\n", filename);
exit(1);
}
AVFrame *outframe = av_frame_alloc();
int nbytes = avpicture_get_size(outContext->pix_fmt,
outContext->width,
outContext->height);
uint8_t* outbuffer = (uint8_t*)av_malloc(nbytes);
//ASSOCIATE THE FRAME TO THE ALLOCATED BUFFER.
avpicture_fill((AVPicture*)outframe, outbuffer,
AV_PIX_FMT_YUV420P,
outContext->width, outContext->height);
SwsContext* swsCtx_ ;
swsCtx_= sws_getContext(pCodecCtx->width,
pCodecCtx->height,
pCodecCtx->pix_fmt,
outContext->width, outContext->height,
outContext->pix_fmt,
SWS_BICUBIC, NULL, NULL, NULL);
//HERE WE START PULLING PACKETS FROM THE SPECIFIED FORMAT CONTEXT.
while(av_read_frame(pFormatCtx, packet)>=0)
{
if(packet->stream_index==videoindex)
{
ret= avcodec_decode_video2(pCodecCtx,
pFrame,
&got_picture,packet );
if(ret < 0)
{
printf("Decode Error.\n");
return -1;
}
if(got_picture)
{
sws_scale(swsCtx_, pFrame->data, pFrame->linesize,
0, pCodecCtx->height, outframe->data,
outframe->linesize);
av_init_packet(&pkt);
pkt.data = NULL; // packet data will be allocated by the encoder
pkt.size = 0;
ret = avcodec_encode_video2(outContext, &pkt, outframe, &got_output);
if (ret < 0) {
fprintf(stderr, "Error encoding frame\n");
exit(1);
}
if (got_output) {
printf("Write frame %3d (size=%5d)\n", i, pkt.size);
fwrite(pkt.data, 1, pkt.size, f);
av_free_packet(&pkt);
}
}
}
av_free_packet(packet);
}//THE LOOP TO PULL PACKETS FROM THE FORMAT CONTEXT ENDS HERE.
//
/* get the delayed frames */
for (got_output = 1; got_output; i++) {
//fflush(stdout);
ret = avcodec_encode_video2(outContext, &pkt, NULL, &got_output);
if (ret < 0) {
fprintf(stderr, "Error encoding frame\n");
exit(1);
}
if (got_output) {
printf("Write frame %3d (size=%5d)\n", i, pkt.size);
fwrite(pkt.data, 1, pkt.size, f);
av_free_packet(&pkt);
}
}
/* add sequence end code to have a real mpeg file */
fwrite(endcode, 1, sizeof(endcode), f);
fclose(f);
avcodec_close(outContext);
av_free(outContext);
//av_freep(&frame->data[0]);
//av_frame_free(&frame);
//THIS WAS ADDED LATER
av_free(outbuffer);
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
return 0;
}
Thank you for your time.
It is possible to record and play in the normal speed.
AVDictionary* options = NULL;
av_dict_set( &options, "preset", "veryslow", 0 );
following presets are available :
{
"ultrafast",
"superfast",
"veryfast",
"faster",
"fast",
"medium",
"slow",
"slower",
"veryslow",
"placebo", 0
}
set the suitable preset.

Decoding then re-encoding frames in reverse order with ffmpeg

I am pretty new to ffmpeg, and now I want to decode the frames and encode it back in reverse order. I have used this tutorial for decoding the video and this example to encode it back. I can decode the frames properly, but when I re-encode it, I get a dummy image rather than clear video. How can I get the actual reversed video?
static void video_encode_example(const char *filename, int codec_id)
{
AVCodec *codec;
AVCodecContext *c= NULL;
int i, ret, x, y, got_output;
FILE *f;
struct SwsContext *sws_ctx = NULL;
AVFrame *frame;
AVFrame *frameRGB = NULL;
AVPacket pkt;
uint8_t endcode[] = { 0, 0, 1, 0xb7 };
printf("Encode video file %s\n", filename);
/* find the mpeg1 video encoder */
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);
}
/* put sample parameters */
c->bit_rate = 400000;
/* resolution must be a multiple of two */
c->width = 352;
c->height = 288;
/* frames per second */
c->time_base = (AVRational){1,25};
/* emit one intra frame every ten frames
* check frame pict_type before passing frame
* to encoder, if frame->pict_type is AV_PICTURE_TYPE_I
* then gop_size is ignored and the output of encoder
* will always be I frame irrespective to gop_size
*/
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);
/* open it */
if (avcodec_open2(c, codec, NULL) < 0) {
fprintf(stderr, "Could not open codec\n");
exit(1);
}
f = fopen(filename, "wb");
if (!f) {
fprintf(stderr, "Could not open %s\n", filename);
exit(1);
}
frame = av_frame_alloc();
if (!frame) {
fprintf(stderr, "Could not allocate video frame\n");
exit(1);
}
frame->format = c->pix_fmt;
frame->width = c->width;
frame->height = c->height;
/* 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(frame->data, frame->linesize, c->width, c->height,
c->pix_fmt, 32);
if (ret < 0) {
fprintf(stderr, "Could not allocate raw picture buffer\n");
exit(1);
}
/* encode 1 second of video */
for (i = 0; i < 25; i++) {
av_init_packet(&pkt);
pkt.data = NULL; // packet data will be allocated by the encoder
pkt.size = 0;
fflush(stdout);
/* prepare a dummy image */
/* Y */
for (y = 0; y < c->height; y++) {
for (x = 0; x < c->width; x++) {
frame->data[0][y * frame->linesize[0] + x] = x + y + i * 3;
}
}
/* Cb and Cr */
for (y = 0; y < c->height/2; y++) {
for (x = 0; x < c->width/2; x++) {
frame->data[1][y * frame->linesize[1] + x] = 128 + y + i * 2;
frame->data[2][y * frame->linesize[2] + x] = 64 + x + i * 5;
}
}
frame->pts = i;
/* encode the image */
ret = avcodec_encode_video2(c, &pkt, frame, &got_output);
if (ret < 0) {
fprintf(stderr, "Error encoding frame\n");
exit(1);
}
if (got_output) {
// Convert the image from its native format to RGB
sws_scale
(
sws_ctx,
(uint8_t const * const *)frame->data,
frame->linesize,
0,
c->height,
frameRGB->data,
frameRGB->linesize
);
printf("Write frame %3d (size=%5d)\n", i, pkt.size);
fwrite(pkt.data, 1, pkt.size, f);
av_free_packet(&pkt);
}
}
/* get the delayed frames */
for (got_output = 1; got_output; i++) {
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) {
printf("Write frame %3d (size=%5d)\n", i, pkt.size);
fwrite(pkt.data, 1, pkt.size, f);
av_free_packet(&pkt);
}
}
/* add sequence end code to have a real mpeg file */
fwrite(endcode, 1, sizeof(endcode), f);
fclose(f);
avcodec_close(c);
av_free(c);
av_freep(&frame->data[0]);
av_frame_free(&frame);
printf("\n");
}
Assuming that your code sample have nothing to do with a video input nor a reverse encoding, but is only an "encode sample".
A video file is made of severa parts.
First, you have a container format (for example mkv, or avi). It contains various informations on which streams are in this file (subtiles? how many video? multiple audio stream? stuff like that).
Then you have the data streams.
A video stream inside this file is a list of packets, encoded by a specific codec (h264 or mpeg4 for example) from frames.
In your code sample, you have a list of frames, and your are encoding them with a codec, giving you a list of packets (I suppose it works, since it is a tutorial code).
But these packets are dumped into a file, without container. So if a video player wants to read them, I will have a hard time to guess what is the format.
It is stated as the first line in your link
Instead of dumping your packets in a file directly, you want to use FFmpeg to embed your packets in a container, and save your container.
You have an example of all this process here
Last thing: if you want to have your video in reverse order, you must encode frames in the reverse order, not only mux packets in reverse order.

ffmpeg transcode

I want to do a audio trancode using ffmpeg library. Now i have out file but I can listen only noise .
The steps of my program are:
1) Open input file and decode in raw format using avcodec_decode_audio4
2) encode and save the raw format .
I don't Know where I wrong. This is my code.
/*
* File: newmain.c
* Author: antonello
*
* Created on 23 gennaio 2013, 11.24
*/
#include <stdio.h>
#include <stdlib.h>
#include <libavutil/samplefmt.h>
#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>
#include <libavcodec/old_codec_ids.h>
static AVCodecContext *get_encoder(int sampleRate, int channels, int audioBitrate)
{
AVCodecContext *audioCodec;
AVCodec *codec;
//Set up audio encoder
codec = avcodec_find_encoder(CODEC_ID_AAC);
if (codec == NULL)
{
printf("avcodec_find_encoder: ERROR\n");
return NULL;
}
audioCodec = avcodec_alloc_context();
audioCodec->bit_rate = audioBitrate;
audioCodec->sample_fmt = AV_SAMPLE_FMT_S16P;
audioCodec->sample_rate = sampleRate;
audioCodec->channels = channels;
audioCodec->profile = FF_PROFILE_AAC_MAIN;
audioCodec->channel_layout=AV_CH_LAYOUT_MONO;
//audioCodec->time_base = (AVRational){1, sampleRate};
audioCodec->time_base.num = 1;
audioCodec->time_base.den = sampleRate;
audioCodec->codec_type = AVMEDIA_TYPE_AUDIO;
if (avcodec_open(audioCodec, codec) < 0)
{
printf("avcodec_open: ERROR\n");
return NULL;
}
return audioCodec;
}
int main(int argc, char** argv) {
AVFormatContext *aFormatCtx_decoder = NULL;
AVFormatContext *aFormatCtx_encoder = NULL;
int i, audioStream;
AVPacket packet_decoder;
AVPacket packet_encoder;
int got_frame=0;
int complete_decode=0;
int len;
AVFrame *decoded_frame = NULL;
AVCodecContext *aCodec_decoderCtx = NULL;
AVCodec *aCodec_decoder = NULL;
FILE *outfile;
//reding input file
avcodec_register_all();
//register all codecs
av_register_all();
//open file
if(avformat_open_input(&aFormatCtx_decoder, "sample.aac", NULL, NULL)!=0){
fprintf(stderr, "Could not open source file \n");
return -1; // Couldn't open file
}
// Retrieve stream information
if(avformat_find_stream_info(aFormatCtx_decoder, NULL)<0){
fprintf(stderr, "Couldn't find stream information \n");
return -1; // Couldn't find stream information
}
// Dump information about file onto standard error
//av_dump_format(aFormatCtx_decode, 0, argv[1], 0);
// Find the first audio stream
audioStream=-1;
for(i=0; i<aFormatCtx_decoder->nb_streams; i++) {
if(aFormatCtx_decoder->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO &&
audioStream < 0) {
audioStream=i;
}
}
if(audioStream==-1){
fprintf(stderr, "File haven't sudio stream \n");
return -1;
}
//get audio codec contex
aCodec_decoderCtx=aFormatCtx_decoder->streams[audioStream]->codec;
//get audio codec
aCodec_decoder = avcodec_find_decoder(aCodec_decoderCtx->codec_id);
aCodec_decoder->sample_fmts=AV_SAMPLE_FMT_S16P;
if(!aCodec_decoder) {
fprintf(stderr, "Unsupported codec!\n");
return -1;//Unsupported codec!
}
//open codec
// Open codec
if(avcodec_open2(aCodec_decoderCtx, aCodec_decoder, NULL)<0)
return -1; // Could not open codec
// allocate audio frame
decoded_frame = avcodec_alloc_frame();
if (!decoded_frame) {
fprintf(stderr, "Could not allocate audio frame\n");
return -1;//Could not allocate audio frame
}
aCodec_decoderCtx->bit_rate=12000;
aFormatCtx_encoder=get_encoder(8000,1,12000);
av_init_packet(&packet_encoder);
printf("param %d %d %d",aCodec_decoderCtx->sample_fmt,aCodec_decoderCtx->channels,aCodec_decoderCtx->bit_rate);
outfile = fopen("out.aac", "wb");
if (!outfile) {
printf(stderr, "Could not open outfile \n");
return -1;//Could not open outfile
}
while(av_read_frame(aFormatCtx_decoder, &packet_decoder)>=0) {
// decode frame
len = avcodec_decode_audio4(aCodec_decoderCtx, decoded_frame, &got_frame, &packet_decoder);
if (len < 0) {
fprintf(stderr, "Error while decoding\n");
return -1;
}
if (got_frame){
avcodec_encode_audio2(aFormatCtx_encoder,&packet_encoder,decoded_frame,&complete_decode);
if(complete_decode){
// printf("complete decode frame");
fwrite(packet_encoder.data, 1, packet_encoder.size, outfile);
av_free_packet(&packet_encoder);
}
}
}
fclose(outfile);
return (EXIT_SUCCESS);
}
use the following code for sample format conversion.
you can get example in ffmpeg/doc/examples/resampling_audio.c
SwrContext *swr = swr_alloc();
av_opt_set_int(node_handle->swr, "in_channel_layout", decoded_frame->channel_layout, 0);
av_opt_set_int(node_handle->swr, "out_channel_layout", encoder_ctx->channel_layout, 0);
av_opt_set_int(node_handle->swr, "in_sample_rate", decoded_frame->sample_rate, 0);
av_opt_set_int(node_handle->swr, "out_sample_rate", encoder_ctx->sample_rate, 0);
av_opt_set_sample_fmt(swr, "in_sample_fmt", decoded_frame->format, 0);
av_opt_set_sample_fmt(swr, "out_sample_fmt", encoder_ctx->sample_fmt, 0);
swr_init(swr);
uint8_t* swr_out_data;
int linesize;
av_samples_alloc(&swr_out_data,
linesize,
encoder_ctx->nb_channels,
decoded_frame->nb_samples,
encoder_ctx->sample_fmt,
0
);
swr_convert(swr,&swr_out_data, decoded_frame->nb_samples, decoded_frame->data, decoded_frame->nb_samples);
You can not set arbitrary value of of the variable sample_fmts:
aCodec_decoder->sample_fmts=AV_SAMPLE_FMT_S16P; // It's wrong
Decoding will always be performed ​​with the parameters set by the codec.
You have to create SwrContext and perform format conversion to the target (SwrContext converts sample format, sample rate and channels layout)
modify to this, works fine
aCodec_decoder->sample_fmts=audioCodec->sample_fmt;

FFmpeg: avcodec_encode_video() and JPEG images

I'm trying to encode a series of .jpg files into a video using the ffmpeg library, and I can't seem to get the frames to encode. (I have to use the ffmpeg library, and using ffmpeg from a command line is not an option in my case.)
Except for the part where I'm trying to open JPG files as AVFrames, my code is more or less the same thing as found in api-example.c from the ffmpeg library. When I populate the frames as the example does, everything works as expected. In the code below, I fail to encode any frames. Obviously the trouble is related to how I'm opening the JPG files, but I can't figure out what.
I'm opening the image like this:
AVFrame* open_image(const char* imageFileName, int width, int height, long * bufSize)
{
AVFormatContext *pFormatCtx;
if(av_open_input_file(&pFormatCtx, imageFileName, NULL, 0, NULL)!=0)
{
printf("Can't open image file '%s'\n", imageFileName);
return NULL;
}
AVCodecContext *pCodecCtx;
pCodecCtx = pFormatCtx->streams[0]->codec;
pCodecCtx->width = width;
pCodecCtx->height = height;
pCodecCtx->pix_fmt = PIX_FMT_YUV420P;
// Find the decoder for the video stream
AVCodec *pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
if (!pCodec)
{
printf("Codec not found\n");
return NULL;
}
// Open codec
if(avcodec_open(pCodecCtx, pCodec)<0)
{
printf("Could not open codec\n");
return NULL;
}
AVFrame *pFrame = avcodec_alloc_frame();
if (!pFrame)
{
LOGV(TAG, "Can't allocate memory for AVFrame\n");
return NULL;
}
int frameFinished;
int numBytes;
// Determine required buffer size and allocate buffer
numBytes = avpicture_get_size(PIX_FMT_YUVJ420P, pCodecCtx->width, pCodecCtx->height);
// ***
*bufSize = numBytes;
// ***
uint8_t *buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
avpicture_fill((AVPicture *) pFrame, buffer, PIX_FMT_YUVJ420P, pCodecCtx->width, pCodecCtx->height);
// Read frame
AVPacket packet;
int framesNumber = 0;
while (av_read_frame(pFormatCtx, &packet) >= 0)
{
if(packet.stream_index != 0)
continue;
int ret = avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
if (ret > 0)
{
sprintf(buf, "Frame is decoded, size %d", ret);
LOGV(TAG, buf);
pFrame->quality = 4;
return pFrame;
}
else {
// printf("Error [%d] while decoding frame: %s\n", ret, strerror(AVERROR(ret)));
sprintf(buf, "Error %d decoding frame: %s", ret, strerror(AVERROR(ret)));
LOGV(TAG, buf);
}
}
}
...and attempting to encode them like this:
DIR * dir = opendir(path);
int i = 0;
if (dir != NULL) {
for(struct dirent *ent = readdir(dir); ent != NULL; ent = readdir(dir)) {
fflush(stdout);
printf("%s/%s", path, ent->d_name);
LOGV(TAG, filename);
// If not a jpg file, pass it over
const char * ext = strrchr(filename, '.');
if((!ext) || (strcmp(ext, ".jpg"))) {
continue;
}
/*** NOTE: Is this where I've gone wrong? Bufsize is set in open_image based on av_picture_size() */
long bufSize = 0L;
AVFrame * frame = open_image(filename, width, height, &bufSize);
if(frame) {
// This is what it needs to do, and it does not work.
// Causes:
// Wrong format?
// Wrong buffer size?
uint8_t * picBuf = (uint8_t *)malloc(bufSize);
out_size = avcodec_encode_video(c, picBuf, bufSize, frame);
printf("encoding frame %3d (size=%5d)\n", i++, out_size);
/** On the first image, out_size is 0. On the next, it's -1, and fails. */
if(out_size < 0) {
printf("Error encoding frame");
return -6;
}
fwrite(picBuf, 1, bufSize, f);
free(picBuf);
av_free(frame);
}
else {
printf("Couldn't open image");
return -5;
}
}
closedir(dir);
}
else {
printf("Couldn't open directory %s\n", path);
return -4;
}
Could someone point me in the right direction?
What exactly is the error you are getting? Examine the Encoding context after opening it to see it's supported pixel formats, you might have to use sws_scale to convert to a format the encoder supports.

Resources