After my previous question, I found codes from here to create videos in C++
using avcodec
of ffmpeg
libraries.
I have modified this code to comply with C++
. Everything is fine and it creates mp4
videos. Except for the output video opens with some video managers and does not open with some others.
E.g. I can open it on Linux by totem (the slider does not allow back and forth anyway). But VLC (on the same Linux machine) does not open this file. Probably, there will be similar problem in windows.
Is there any missing process for this code causing this problem?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <iostream>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
#include <libavutil/imgutils.h>
}
static void encode(AVCodecContext *enc_ctx, AVFrame *frame, AVPacket *pkt,
FILE *outfile)
{
int ret;
/* send the frame to the encoder */
if (frame)
std::cout<<"Send frame "<<(frame->pts)<<std::endl;
ret = avcodec_send_frame(enc_ctx, frame);
if (ret < 0) {
fprintf(stderr, "Error sending a frame for encoding\n");
exit(1);
}
while (ret >= 0) {
ret = avcodec_receive_packet(enc_ctx, pkt);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
return;
else if (ret < 0) {
fprintf(stderr, "Error during encoding\n");
exit(1);
}
std::cout<<"Write packet "<<(pkt->pts)<<" (size="<<(pkt->size)<<")"<<std::endl;
// printf("Write packet %3" PRId64" (size=%5d)\n", pkt->pts, pkt->size);
fwrite(pkt->data, 1, pkt->size, outfile);
av_packet_unref(pkt);
}
}
int main(int argc, char **argv)
{
const char *filename;
const AVCodec *codec;
AVCodecContext *c= NULL;
int i, ret, x, y;
FILE *f;
AVFrame *frame;
AVPacket *pkt;
uint8_t endcode[] = { 0, 0, 1, 0xb7 };
if (argc < 2) {
fprintf(stderr, "Usage: %s <output file>\n", argv[0]);
exit(0);
}
filename = argv[1];
std::string codec_name = "mpeg4";
avcodec_register_all();
/* find the mpeg1video encoder */
codec = avcodec_find_encoder_by_name(codec_name.c_str());
if (!codec) {
fprintf(stderr, "Codec '%s' not found\n", codec_name.c_str());
exit(1);
}
c = avcodec_alloc_context3(codec);
if (!c) {
fprintf(stderr, "Could not allocate video codec context\n");
exit(1);
}
pkt = av_packet_alloc();
if (!pkt)
exit(1);
/* put sample parameters */
// c->bit_rate = 400000;
c->bit_rate = 4000000;
/* resolution must be a multiple of two */
// c->width = 352;
// c->height = 288;
c->width = 640;
c->height = 480;
/* frames per second */
c->time_base = (AVRational){1, 25};
c->framerate = (AVRational){25, 1};
/* 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 */
ret = avcodec_open2(c, codec, NULL);
if (ret < 0) {
fprintf(stderr, "Could not open codec\n");
// fprintf(stderr, "Could not open codec: %s\n", av_err2str(ret));
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;
ret = av_frame_get_buffer(frame, 32);
if (ret < 0) {
fprintf(stderr, "Could not allocate the video frame data\n");
exit(1);
}
/* encode 10 second of video */
for (i = 0; i < 250; i++) {
fflush(stdout);
/* make sure the frame data is writable */
ret = av_frame_make_writable(frame);
if (ret < 0)
exit(1);
/* 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] = uint8_t(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] = uint8_t(128 + y + i * 2);
frame->data[2][y * frame->linesize[2] + x] = uint8_t(64 + x + i * 5);
}
}
frame->pts = i;
/* encode the image */
encode(c, frame, pkt, f);
}
/* flush the encoder */
encode(c, NULL, pkt, f);
/* add sequence end code to have a real MPEG file */
fwrite(endcode, 1, sizeof(endcode), f);
fclose(f);
avcodec_free_context(&c);
av_frame_free(&frame);
av_packet_free(&pkt);
return 0;
}
build:
g++ -I ./FFmpeg/ video.cpp -L ./fflibs -lavdevice -lavfilter -lavformat -lavcodec -lrt -ldl -lXfixes -lXext -lX11 -lasound -lSDL -lz -lrt -lswresample -lswscale -lavutil -lm -llzma -lbz2 -lswresample -lpthread
run
./a.out myvideo.mp4
This video will be fine if converted in bash via
ffmpeg -i myvideo.mp4 out1.mp4
But I look for a method to fix it from the code.
Generated video played on totem (Ubuntu):
Video after conversion: