Remux RTSP stream into container? (Write read frame without decoding it) - c

I'm trying to use dranger tutorials for writing a RTSP h264 streamed video directly to a file without decoding and encoding it (ffmpeg 3.0/3.1 library). But I'm a bit lost on how do I need to populate the AVFormatContext pointer for the av_write_frame once I get the corresponding AVPacket.
Trying to clarify. What I want to do is this
1. Open webcam stream in h264
2. Read a frame
3. Save it to a file without decoding and encoding it.
EDIT: I've also tried to use the remuxing example in ffmpeg's documentation (doing a network init()) but I'm getting dts and pts sync errors when going from rtsp -> .mp4
Copy pasting the code from the tutorial:
#include <stdio.h>
#include <libavutil/pixfmt.h>
#include <libavcodec/avcodec.h>
#include <libavutil/avconfig.h>
#include <libswscale/swscale.h>
#include <libavformat/avformat.h>
int main(int argc, char *argv[]) {
av_register_all();
avcodec_register_all();
avformat_network_init();
AVFormatContext *pFormatCtx = avformat_alloc_context();
// Camera comes from argv[1]
avformat_open_input(&pFormatCtx, argv[1], NULL, NULL);
avformat_find_stream_info(pFormatCtx, NULL);
av_dump_format(pFormatCtx, 0, argv[1], 0);
int video_stream_idx = -1;
for (int i = 0; i < pFormatCtx->nb_streams; i++) {
if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
video_stream_idx = i;
break;
}
}
AVCodecContext *pCodecContext = NULL;
AVCodecContext *pCodecContextOrig = NULL;
pCodecContextOrig = pFormatCtx->streams[video_stream_idx]->codec;
AVCodec *pCodec;
pCodec = avcodec_find_decoder(pCodecContextOrig->codec_id);
pCodecContext = avcodec_alloc_context3(pCodec);
avcodec_copy_context(pCodecContext, pCodecContextOrig);
avcodec_open2(pCodecContext, pCodec, NULL);
AVFrame *pFrame = av_frame_alloc();
AVFrame *pFrameRGB = av_frame_alloc();
uint8_t *buffer = NULL;
int buffer_size = avpicture_get_size(AV_PIX_FMT_RGB24, pCodecContext->width,
pCodecContext->height);
buffer = (uint8_t *)av_malloc(buffer_size * sizeof(uint8_t));
// fill buffer
avpicture_fill((AVPicture *)pFrameRGB, buffer, AV_PIX_FMT_RGB24,
pCodecContext->width, pCodecContext->height);
struct SwsContext *sws_ctx = NULL;
int frame_finished = 0;
AVPacket packet;
// Size(src), fmt(src), Size(dst), fmt(dst) .. flags
sws_ctx = sws_getContext(pCodecContext->width, pCodecContext->height,
pCodecContext->pix_fmt, pCodecContext->width,
pCodecContext->height, AV_PIX_FMT_RGB24,
SWS_BILINEAR, NULL, NULL, NULL);
AVFormatContext *out_fmt_ctx;
avformat_write_header(out_fmt_ctx, NULL);
int i = 0;
while (i < 100 && (av_read_frame(pFormatCtx, &packet) >= 0)) {
// Want to write these frames to disk
i++;
}
av_write_trailer(out_fmt_ctx);
av_free(buffer);
av_free(pFrameRGB);
av_free(pFrame);
avcodec_close(pCodecContext);
avcodec_close(pCodecContextOrig);
avformat_close_input(&pFormatCtx);
return 0;
}
I think a lot of stuff of this code can be removed. I'm trying to learn :).
Linking against -lavutil -lavformat -lavcodec -lz -lavutil -lm -lswscale

I fixed by using the remuxing.c example from FFmpeg's documentation. The problem was that for some reason (I still dont know why) the first packet dts and pts was higher than the second (and following ones, which increased monotonically). Fixed it by skipping the first packet write :).

Related

FFmpeg: unspecified pixel format when opening video with custom context

I am trying to decode a video with a custom context. The purpose is that I want to decode the video directly from memory. In the following code, I am reading from file in the read function passed to avio_alloc_context - but this is just for testing purposes.
I think I've read any post there is on Stackoverflow or on any other website related to this topic. At least I definitely tried my best to do so. While there is much in common, the details differ: people set different flags, some say av_probe_input_format is required, some say it isn't, etc. And for some reason nothing works for me.
My problem is that the pixel format is unspecified (see output below), which is why I run into problems later when calling sws_getContext. I checked pFormatContext->streams[videoStreamIndex]->codec->pix_fmt, and it is -1.
Please note my comments // things I tried and // seems not to help in the code. I think, the answer might be hidden somehwere there. I tried many combinations of hints that I've read so far, but I am missing a detail I guess.
The problem is not the video file, because when I go the standard way and just call avformat_open_input(&pFormatContext, pFilePath, NULL, NULL) without a custom context, everything runs fine.
The code compiles and runs as is.
#include <libavformat/avformat.h>
#include <string.h>
#include <stdio.h>
FILE *f;
static int read(void *opaque, uint8_t *buf, int buf_size) {
if (feof(f)) return -1;
return fread(buf, 1, buf_size, f);
}
int openVideo(const char *pFilePath) {
const int bufferSize = 32768;
int ret;
av_register_all();
f = fopen(pFilePath, "rb");
uint8_t *pBuffer = (uint8_t *) av_malloc(bufferSize + AVPROBE_PADDING_SIZE);
AVIOContext *pAVIOContext = avio_alloc_context(pBuffer, bufferSize, 0, NULL,
&read, NULL, NULL);
if (!f || !pBuffer || !pAVIOContext) {
printf("error: open / alloc failed\n");
// cleanup...
return 1;
}
AVFormatContext *pFormatContext = avformat_alloc_context();
pFormatContext->pb = pAVIOContext;
const int readBytes = read(NULL, pBuffer, bufferSize);
printf("readBytes = %i\n", readBytes);
if (readBytes <= 0) {
printf("error: read failed\n");
// cleanup...
return 2;
}
if (fseek(f, 0, SEEK_SET) != 0) {
printf("error: fseek failed\n");
// cleanup...
return 3;
}
// required for av_probe_input_format
memset(pBuffer + readBytes, 0, AVPROBE_PADDING_SIZE);
AVProbeData probeData;
probeData.buf = pBuffer;
probeData.buf_size = readBytes;
probeData.filename = "";
probeData.mime_type = NULL;
pFormatContext->iformat = av_probe_input_format(&probeData, 1);
// things I tried:
//pFormatContext->flags = AVFMT_FLAG_CUSTOM_IO;
//pFormatContext->iformat->flags |= AVFMT_NOFILE;
//pFormatContext->iformat->read_header = NULL;
// seems not to help (therefore commented out here):
AVDictionary *pDictionary = NULL;
//av_dict_set(&pDictionary, "analyzeduration", "8000000", 0);
//av_dict_set(&pDictionary, "probesize", "8000000", 0);
if ((ret = avformat_open_input(&pFormatContext, "", NULL, &pDictionary)) < 0) {
char buffer[4096];
av_strerror(ret, buffer, sizeof(buffer));
printf("error: avformat_open_input failed: %s\n", buffer);
// cleanup...
return 4;
}
printf("retrieving stream information...\n");
if ((ret = avformat_find_stream_info(pFormatContext, NULL)) < 0) {
char buffer[4096];
av_strerror(ret, buffer, sizeof(buffer));
printf("error: avformat_find_stream_info failed: %s\n", buffer);
// cleanup...
return 5;
}
printf("nb_streams = %i\n", pFormatContext->nb_streams);
// further code...
// cleanup...
return 0;
}
int main() {
openVideo("video.mp4");
return 0;
}
This is the output that I get:
readBytes = 32768
retrieving stream information...
[mov,mp4,m4a,3gp,3g2,mj2 # 0xdf8d20] stream 0, offset 0x30: partial file
[mov,mp4,m4a,3gp,3g2,mj2 # 0xdf8d20] Could not find codec parameters for stream 0 (Video: h264 (avc1 / 0x31637661), none, 640x360, 351 kb/s): unspecified pixel format
Consider increasing the value for the 'analyzeduration' and 'probesize' options
nb_streams = 2
UPDATE:
Thanks to WLGfx, here is the solution: The only thing that was missing was the seek function. Apparently, implementing it is mandatory for decoding. It is important to return the new offset - and not 0 in case of success (some solutions found in the web just return the return value of fseek, and that is wrong). Here is the minimal solution that made it work:
static int64_t seek(void *opaque, int64_t offset, int whence) {
if (whence == SEEK_SET && fseek(f, offset, SEEK_SET) == 0) {
return offset;
}
// handling AVSEEK_SIZE doesn't seem mandatory
return -1;
}
Of course, the call to avio_alloc_context needs to be adapted accordingly:
AVIOContext *pAVIOContext = avio_alloc_context(pBuffer, bufferSize, 0, NULL,
&read, NULL, &seek);
Seeing as yours is a file based stream then it is seekable so you can provide the AVIO seek when creating the AVIOContext:
avioContext = avio_alloc_context((uint8_t *)avio_buffer, AVIO_QUEUE_SIZE * PKT_SIZE7,
0,
this, // *** This is your data pointer to a class or other data passed to the callbacks
avio_ReadFunc,
NULL,
avio_SeekFunc);
Handle the seeking with this callback: (You can cast ptr to your class or other data structure)
int64_t FFIOBufferManager::avio_SeekFunc(void *ptr, int64_t pos64, int whence) {
// SEEK_SET(0), SEEK_CUR(1), SEEK_END(2), AVSEEK_SIZE
// ptr is cast to your data or class
switch (whence) {
case 0 : // SEEK_SET
... etc
case (AVSEEK_SIZE) : // get size
return -1; // if you're unable to get the size
break;
}
// set new position in the file
return (int64_t)new_pos; // new position
}
You can also define the codec and the probesize when attaching the AVIOContext to the AVFormatContext. This allows ffmpeg to seek in the stream to better determine the format.
context->pb = ffio->avioContext;
context->flags = AVFMT_FLAG_CUSTOM_IO;
context->iformat = av_find_input_format("mpegts"); // not necessary
context->probesize = 1200000;
So far I haven't had the need for av_probe_input_format, but then again my streams are mpegts.
Hope this helps.
EDIT: Added a comment to the avio_alloc_context function to mention how the ptr is used in the callbacks.
Although the seek was the right answer in your situation, the fact is in my case it's not possible because I have to stream the data and in that situation a seek is just not possible.
So I had to look into: why is a seek required?
From what the ffmpeg docs say, they will cache some data so that way they can seek back if required by the current encoder/decoder. But that buffer is relatively small (you probably don't want to cache 100's of Mb of data).
The fact is that MP4 saves some metadata at the end of the file (once it's known). When reading that format, the decoder wants to seek to a position really very far in the file (near the end) and read what is called the moov atom. Without that info, the system doesn't want to decompress your data.
What I had to do to fix this issue is move that moov atom with the following command:
ffmpeg -i far.mp4 -c copy -map 0 -movflags +faststart close.mp4
faststart means you do not have to stream the entire file to start playing (decoding) the file.

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

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

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.

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.

How can i get screen resolution in c (Operating system QNX or Linux)

I am in a GUI development with QNX(screen resolution interdependent design)
How can i get screen resolution in c.
I am using QNX operating system.
Is it possible?
Is any OS function for this solution?
thanks
Assume you are using a device with a framebuffer (and have root access):
(taken from this answer: Paint Pixels to Screen via Linux FrameBuffer)
Also, as mentioned above, what graphics library you are using will make a lot of difference as this code will only tell you what the framebuffer is set to and not what the GUI code is using. So might not be useful at all. If you are not using X or any other graphics library, then you will probably need to be using the framebuffer, and you can see the rest of the answer for how to do that. (I strongly suggest you use DirectFB this will save you implementing a LOT of code).
Also, you could also use the gl drivers that turn up on most devices (inc. embedded ones) so this will also effect how you do what you require.
Are you using a SOC? Does the manufacturer have there own driver layer? That may work completely different and would probably come with it's own API to handle this.
But anyway, I hope this helps.
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
int main()
{
int fbfd = 0;
struct fb_var_screeninfo vinfo;
struct fb_fix_screeninfo finfo;
long int screensize = 0;
char *fbp = 0;
int x = 0, y = 0;
long int location = 0;
// Open the file for reading and writing
fbfd = open("/dev/fb0", O_RDWR);
if (fbfd == -1) {
perror("Error: cannot open framebuffer device");
exit(1);
}
printf("The framebuffer device was opened successfully.\n");
// Get fixed screen information
if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo) == -1) {
perror("Error reading fixed information");
exit(2);
}
// Get variable screen information
if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo) == -1) {
perror("Error reading variable information");
exit(3);
}
printf("%dx%d, %dbpp\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);
// Figure out the size of the screen in bytes
//
close(fbfd);
}
For a unix-like OS, you may use the library X11, but if you need cross-platform solution, try the GTK+.
A full code
// The C standart library
#include <stdlib.h>
// GTK+
#include <gtk/gtk.h>
#include <glib.h>
#include <glib/gprintf.h>
// X11
#include <X11/Xlib.h>
/*
Printing a current screen resoltion by using the GTK+3
https://en.wikipedia.org/wiki/GTK%2B
*/
int
print_screen_resolution_by_GTK(int argc, char *argv[])
{
GdkScreen *screen;
gint width, height;
gtk_init(&argc, &argv);
if ((screen = gdk_screen_get_default()) != NULL) {
width = gdk_screen_get_width(screen);
height = gdk_screen_get_height(screen);
g_printf("Current screen resolution: %dx%d (by used GTK+)\n", width, height);
}
return 0;
}
/*
Printing a current screen resoltion by using the libX11 (worked only for Unix-like OS)
https://en.wikipedia.org/wiki/X_Window_System
Based on:
https://www.x.org/releases/X11R7.6/doc/libX11/specs/libX11/libX11.html
http://surfingtroves.blogspot.com/2011/01/how-to-get-screen-resolution-in-linux-c.html
*/
int
print_display_resolution_by_X11()
{
Display *display;
Window window;
XWindowAttributes xw_attrs;
if ((display = XOpenDisplay(NULL)) == NULL) {
fprintf(stderr, "Failed to open default display\n");
return -1;
}
window = DefaultRootWindow(display);
XGetWindowAttributes(display, window, &xw_attrs);
printf("Current window resolution: %dx%d (by used X11)\n", xw_attrs.width, xw_attrs.height);
XCloseDisplay(display);
return 0;
}
int main(int argc, char *argv[])
{
print_screen_resolution_by_GTK(argc, argv);
print_display_resolution_by_X11();
return EXIT_SUCCESS;
}
A compilation
gcc -o main main.c `pkg-config --libs --cflags gtk+-3.0 x11`
A result (actual for my computer)
Current screen resolution: 1366x768 (by used GTK+)
Current window resolution: 1366x768 (by used X11)
You can simply use this function I created, it get screen size from your configuration files, split it, and then return 2 values (resolution as x and y)
I tried it on Ubuntu 20.04 and it works perfectly !
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
unsigned short *get_screen_size(void)
{
static unsigned short size[2];
char *array[8];
char screen_size[64];
char* token = NULL;
FILE *cmd = popen("xdpyinfo | awk '/dimensions/ {print $2}'", "r");
if (!cmd)
return 0;
while (fgets(screen_size, sizeof(screen_size), cmd) != NULL);
pclose(cmd);
token = strtok(screen_size, "x\n");
if (!token)
return 0;
for (unsigned short i = 0; token != NULL; ++i) {
array[i] = token;
token = strtok(NULL, "x\n");
}
size[0] = atoi(array[0]);
size[1] = atoi(array[1]);
size[2] = -1;
return size;
}
int main(void)
{
unsigned short *size = get_screen_size();
printf("Screen resolution = %dx%d\n", size[0], size[1]);
return 0;
}
If you have any question, do not hesitate ! :)

Resources