How to set up ffmpeg options for HLS? - c++

I have a streaming device that streams mpegts video and audio. I am trying to capture those streams and save them multiple .ts file using HLS of ffmpeg.
So, I have been successful in capturing and saving the streams in a single .ts file. It seems like when I send the output file to be .m3u8, ffmpeg automatically chooses hls demuxer. But, doing so I get a floating point exception.
Here is my simple code...
static int ipcam_streaming_main_configure_input_parameters(const char *p_ifilename, AVFormatContext **ppx_ifmt_ctx)
{
AVStream *px_istream = NULL;
AVCodecContext *px_icodec_ctx = NULL;
int ret = -1;
unsigned int i = 0;
enum AVCodecID input_codec_id = AV_CODEC_ID_NONE;
AVCodec *p_decoder = NULL;
if (avformat_open_input(ppx_ifmt_ctx, p_ifilename, NULL, NULL) < 0)
{
printf("%s(): avformat_open_input() failed\n", __FUNCTION__);
}
else if (avformat_find_stream_info(*ppx_ifmt_ctx, NULL) < 0)
{
printf("%s(): avformat_find_stream_info() failed\n", __FUNCTION__);
}
else
{
/* find the input streams to be remuxed */
for (i = 0; i < (*ppx_ifmt_ctx)->nb_streams; i++)
{
/* get the stream, codec context for the stream */
px_istream = (*ppx_ifmt_ctx)->streams[i];
px_icodec_ctx = px_istream->codec;
if ((AVMEDIA_TYPE_VIDEO == px_icodec_ctx->codec_type)
|| (AVMEDIA_TYPE_AUDIO == px_icodec_ctx->codec_type))
{
/* get the codec_id for the audio/video stream */
input_codec_id = px_icodec_ctx->codec_id;
/* get the decoder for the input codec id */
p_decoder = avcodec_find_decoder(px_icodec_ctx->codec_id);
/* Open decoder for the input codec audio/video */
ret = avcodec_open2(px_icodec_ctx,
p_decoder,
NULL);
if (ret < 0)
{
printf("%s(): avcodec_open2() failed\n", __FUNCTION__);
}
else
{
printf("Input stream type <%d> with codec_id <%d> found and decoder opened\n", px_icodec_ctx->codec_type, input_codec_id);
}
}
}
}
/* dump the data into stdout */
av_dump_format(*ppx_ifmt_ctx, 0, p_ifilename, 0);
return ret;
}
static int ipcam_streaming_main_configure_output_parameters(const char *p_ofilename,
AVFormatContext *px_ifmt_ctx,
AVFormatContext **ppx_ofmt_ctx)
{
AVStream *px_ostream = NULL;
AVStream *px_istream = NULL;
AVCodecContext *px_dec_ctx = NULL;
AVCodecContext *px_enc_ctx = NULL;
int ret = -1;
unsigned int i = 0;
if ((NULL == p_ofilename) || (NULL == px_ifmt_ctx) || (NULL == ppx_ofmt_ctx))
{
printf("%s(): NULL arg(s) <%p, %p, %p>", __FUNCTION__, p_ofilename, px_ifmt_ctx, ppx_ofmt_ctx);
return -1;
}
/* remove the output file if already exists */
remove(p_ofilename);
/* allocate the output format context */
if (avformat_alloc_output_context2(ppx_ofmt_ctx, NULL, NULL, p_ofilename) < 0)
{
printf("%s(): avformat_alloc_output_context2() failed\n", __FUNCTION__);
}
else
{
for (i = 0; i < px_ifmt_ctx->nb_streams; i++)
{
if ((AVMEDIA_TYPE_VIDEO == px_ifmt_ctx->streams[i]->codec->codec_type)
|| (AVMEDIA_TYPE_AUDIO == px_ifmt_ctx->streams[i]->codec->codec_type))
{
printf("Stream <%d> is type <%d>: Adding to output stream\n", i, px_ifmt_ctx->streams[i]->codec->codec_type);
/* create a new output stream */
px_ostream = avformat_new_stream(*ppx_ofmt_ctx, NULL);
if (NULL == px_ostream)
{
printf("%s(): avformat_new_stream() failed\n", __FUNCTION__);
}
else
{
px_istream = px_ifmt_ctx->streams[i];
px_dec_ctx = px_istream->codec;
px_enc_ctx = px_ostream->codec;
/* Since, we do not need to encode the video stream, it is just remuxing
just copying the input codec context to output is sufficient */
ret = avcodec_copy_context((*ppx_ofmt_ctx)->streams[i]->codec,
px_ifmt_ctx->streams[i]->codec);
if ((*ppx_ofmt_ctx)->oformat->flags & AVFMT_GLOBALHEADER)
{
px_enc_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER;
}
}
}
else
{
printf("Stream <%d> is Unknown: Ignore it \n", i);
}
}
/* dump the output media file into stdout */
av_dump_format(*ppx_ofmt_ctx, 0, p_ofilename, 1);
if (0 == ((*ppx_ofmt_ctx)->oformat->flags & AVFMT_NOFILE))
{
/* open the output media file so that we can write the data into it */
ret = avio_open(&(*ppx_ofmt_ctx)->pb, p_ofilename, AVIO_FLAG_WRITE);
if (ret < 0)
{
printf("%s(): avio_open() failed\n", __FUNCTION__);
}
else
{
/* init muxer, write output file header */
ret = avformat_write_header((*ppx_ofmt_ctx), NULL);
if (ret < 0)
{
printf("%s(): avformat_write_header() failed\n", __FUNCTION__);
}
}
}
}
return ret;
}
int main(int argnum, char **argv)
{
AVFormatContext *px_ifmt_ctx = NULL;
AVFormatContext *px_ofmt_ctx = NULL;
AVPacket packet = {0};
enum AVMediaType type = AVMEDIA_TYPE_UNKNOWN;
unsigned int stream_index = -1;
unsigned int i = 0;
int ret = -1;
if (argnum != 3)
{
printf("Please enough number of parameters\n");
return -1;
}
/* register all the services requred */
av_register_all();
avformat_network_init();
if (0 != ipcam_streaming_main_configure_input_parameters(argv[1],
&px_ifmt_ctx))
{
printf("%s(): ipcam_streaming_main_configure_iput_parameters() failed\n", __FUNCTION__);
}
else if (0 != ipcam_streaming_main_configure_output_parameters(argv[2],
px_ifmt_ctx,
&px_ofmt_ctx))
{
printf("%s(): ipcam_streaming_main_configure_output_parameters() failed\n", __FUNCTION__);
}
else
{
printf("Input and output configuration done successfully: Now reading packets\n");
while (true)
{
if ((ret = av_read_frame(px_ifmt_ctx, &packet)) < 0)
{
printf("av_read_frame() failed with error <%d>: Exit\n", ret);
break;
}
/* get the stream index and codec type of the packet read */
stream_index = packet.stream_index;
type = px_ifmt_ctx->streams[stream_index]->codec->codec_type;
/* remux only if the type is video, otherwise ignore it */
if ((AVMEDIA_TYPE_VIDEO == type)
|| (AVMEDIA_TYPE_AUDIO == type))
{
printf("Remuxing the stream type <%d>, frame with stream index <%d>\n", type, stream_index);
/* remux this frame without reencoding */
packet.dts = av_rescale_q_rnd(packet.dts,
px_ifmt_ctx->streams[stream_index]->time_base,
px_ofmt_ctx->streams[stream_index]->time_base,
AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);
packet.pts = av_rescale_q_rnd(packet.pts,
px_ifmt_ctx->streams[stream_index]->time_base,
px_ofmt_ctx->streams[stream_index]->time_base,
AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);
/* write the frame into the output media file */
ret = av_write_frame(px_ofmt_ctx, &packet);
if (ret < 0)
{
printf("Ignoring video packet stream index <%d>\n", packet.stream_index);
}
/* free the packet for next use */
av_free_packet(&packet);
}
else
{
printf("Ignoring stream index <%d>, type <%d>\n", packet.stream_index, type);
}
}
}
/* write the trailer */
av_write_trailer(px_ofmt_ctx);
av_free_packet(&packet);
for (i = 0; i < px_ifmt_ctx->nb_streams; i++)
{
/* close the input codec that has been opened */
avcodec_close(px_ifmt_ctx->streams[i]->codec);
if ((NULL != px_ofmt_ctx) && (px_ofmt_ctx->nb_streams > i) &&
(NULL != px_ofmt_ctx->streams[i]) && ( NULL != px_ofmt_ctx->streams[i]->codec))
{
/* close the output code */
avcodec_close(px_ofmt_ctx->streams[i]->codec);
}
}
/* close the input */
avformat_close_input(&px_ifmt_ctx);
if ((NULL != px_ofmt_ctx) && (0 == (px_ofmt_ctx->oformat->flags & AVFMT_NOFILE)))
{
/* close the output context */
avio_close(px_ofmt_ctx->pb);
}
/* free the output context */
avformat_free_context(px_ofmt_ctx);
return ret;
}
So, If i pass the output filename to be .m3u8 file, it gives a floating point exception.

Related

How to cut videos/audios using FFmpeg C APIs

I'm trying to write a function to cut videos/audios FFmpeg C APIs in C++. I started with the remuxing.c example from FFmpeg GitHub repository, and tried to apply the same changes mentioned in this question, but I'm getting blank screen in the beginning of the output equal to the duration that I want to cut. This is the function I came with (Differences between the function and the remuxing example noted with // <- HERE):
int cut_video(const char *in_filename, const char *out_filename, double from_seconds, double end_seconds) {
const AVOutputFormat *ofmt = NULL;
AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL;
AVPacket *pkt = NULL;
int ret, i;
int stream_index = 0;
int *stream_mapping = NULL;
int stream_mapping_size = 0;
pkt = av_packet_alloc();
if (!pkt) {
fprintf(stderr, "Could not allocate AVPacket\n");
return 1;
}
if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) {
fprintf(stderr, "Could not open input file '%s'", in_filename);
goto end;
}
if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
fprintf(stderr, "Failed to retrieve input stream information");
goto end;
}
av_dump_format(ifmt_ctx, 0, in_filename, 0);
ret = av_seek_frame(ifmt_ctx, -1, from_seconds * AV_TIME_BASE, AVSEEK_FLAG_ANY); // <- HERE
if (ret < 0) { // <- HERE
fprintf(stderr, "Error seek\n"); // <- HERE
goto end; // <- HERE
} // <- HERE
avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename);
if (!ofmt_ctx) {
fprintf(stderr, "Could not create output context\n");
ret = AVERROR_UNKNOWN;
goto end;
}
stream_mapping_size = ifmt_ctx->nb_streams;
stream_mapping = (int *)av_calloc(stream_mapping_size, sizeof(*stream_mapping));
if (!stream_mapping) {
ret = AVERROR(ENOMEM);
goto end;
}
ofmt = ofmt_ctx->oformat;
for (i = 0; i < ifmt_ctx->nb_streams; i++) {
AVStream *out_stream;
AVStream *in_stream = ifmt_ctx->streams[i];
AVCodecParameters *in_codecpar = in_stream->codecpar;
if (in_codecpar->codec_type != AVMEDIA_TYPE_AUDIO && in_codecpar->codec_type != AVMEDIA_TYPE_VIDEO &&
in_codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) {
stream_mapping[i] = -1;
continue;
}
stream_mapping[i] = stream_index++;
out_stream = avformat_new_stream(ofmt_ctx, NULL);
if (!out_stream) {
fprintf(stderr, "Failed allocating output stream\n");
ret = AVERROR_UNKNOWN;
goto end;
}
ret = avcodec_parameters_copy(out_stream->codecpar, in_codecpar);
if (ret < 0) {
fprintf(stderr, "Failed to copy codec parameters\n");
goto end;
}
out_stream->codecpar->codec_tag = 0;
}
av_dump_format(ofmt_ctx, 0, out_filename, 1);
if (!(ofmt->flags & AVFMT_NOFILE)) {
ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);
if (ret < 0) {
fprintf(stderr, "Could not open output file '%s'", out_filename);
goto end;
}
}
ret = avformat_write_header(ofmt_ctx, NULL);
if (ret < 0) {
fprintf(stderr, "Error occurred when opening output file\n");
goto end;
}
while (1) {
AVStream *in_stream, *out_stream;
ret = av_read_frame(ifmt_ctx, pkt);
if (ret < 0) break;
in_stream = ifmt_ctx->streams[pkt->stream_index];
if (pkt->stream_index >= stream_mapping_size || stream_mapping[pkt->stream_index] < 0 ||
av_q2d(in_stream->time_base) * pkt->pts > end_seconds) { // <- HERE
av_packet_unref(pkt);
continue;
}
pkt->stream_index = stream_mapping[pkt->stream_index];
out_stream = ofmt_ctx->streams[pkt->stream_index];
log_packet(ifmt_ctx, pkt, "in");
/* copy packet */
av_packet_rescale_ts(pkt, in_stream->time_base, out_stream->time_base);
pkt->pos = -1;
log_packet(ofmt_ctx, pkt, "out");
ret = av_interleaved_write_frame(ofmt_ctx, pkt);
/* pkt is now blank (av_interleaved_write_frame() takes ownership of
* its contents and resets pkt), so that no unreferencing is necessary.
* This would be different if one used av_write_frame(). */
if (ret < 0) {
fprintf(stderr, "Error muxing packet\n");
break;
}
}
av_write_trailer(ofmt_ctx);
end:
av_packet_free(&pkt);
avformat_close_input(&ifmt_ctx);
/* close output */
if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE)) avio_closep(&ofmt_ctx->pb);
avformat_free_context(ofmt_ctx);
av_freep(&stream_mapping);
if (ret < 0 && ret != AVERROR_EOF) {
fprintf(stderr, "Error occurred: %s\n", av_err2str(ret));
return 1;
}
return 0;
}
And here is how I call it:
cut_video("/Users/aliosm/Desktop/1.mp4", "/Users/aliosm/Desktop/2.mp4", 10, 40);
I searched a lot on Google and I didn't find anything useful related to this specific use-case, do you have any idea?
Finally, I was able to do that by the help from #ffmpeg channel community on Libera.Chat IRC. The final code:
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include <libavutil/timestamp.h>
}
/**
* #brief Print the information of the passed packet.
*
* #fn logPacket
* #param avFormatContext AVFormatContext of the given packet.
* #param avPacket AVPacket to log.
* #param tag String to tag the log output.
*/
void logPacket(const AVFormatContext *avFormatContext, const AVPacket *avPacket, const QString tag) {
AVRational *timeBase = &avFormatContext->streams[avPacket->stream_index]->time_base;
qDebug() << QString("%1: pts:%2 pts_time:%3 dts:%4 dts_time:%5 duration:%6 duration_time:%7 stream_index:%8")
.arg(tag)
.arg(av_ts2str(avPacket->pts))
.arg(av_ts2timestr(avPacket->pts, timeBase))
.arg(av_ts2str(avPacket->dts))
.arg(av_ts2timestr(avPacket->dts, timeBase))
.arg(av_ts2str(avPacket->duration))
.arg(av_ts2timestr(avPacket->duration, timeBase))
.arg(avPacket->stream_index);
}
/**
* #brief Cut a file in the given input file path based on the start and end seconds, and output the cutted file to the
* given output file path.
*
* #fn cutFile
* #param inputFilePath Input file path to be cutted.
* #param startSeconds Cutting start time in seconds.
* #param endSeconds Cutting end time in seconds.
* #param outputFilePath Output file path to write the new cutted file.
*
* #details This function will take an input file path and cut it based on the given start and end seconds. The cutted
* file will then be outputted to the given output file path.
*
* #return True if the cutting operation finished successfully, false otherwise.
*/
bool cutFile(const QString& inputFilePath, const long long& startSeconds, const long long& endSeconds,
const QString& outputFilePath) {
int operationResult;
AVPacket* avPacket = NULL;
AVFormatContext* avInputFormatContext = NULL;
AVFormatContext* avOutputFormatContext = NULL;
avPacket = av_packet_alloc();
if (!avPacket) {
qCritical("Failed to allocate AVPacket.");
return false;
}
try {
operationResult = avformat_open_input(&avInputFormatContext, inputFilePath.toStdString().c_str(), 0, 0);
if (operationResult < 0) {
throw std::runtime_error(QString("Failed to open the input file '%1'.").arg(inputFilePath).toStdString().c_str());
}
operationResult = avformat_find_stream_info(avInputFormatContext, 0);
if (operationResult < 0) {
throw std::runtime_error(QString("Failed to retrieve the input stream information.").toStdString().c_str());
}
avformat_alloc_output_context2(&avOutputFormatContext, NULL, NULL, outputFilePath.toStdString().c_str());
if (!avOutputFormatContext) {
operationResult = AVERROR_UNKNOWN;
throw std::runtime_error(QString("Failed to create the output context.").toStdString().c_str());
}
int streamIndex = 0;
int streamMapping[avInputFormatContext->nb_streams];
int streamRescaledStartSeconds[avInputFormatContext->nb_streams];
int streamRescaledEndSeconds[avInputFormatContext->nb_streams];
// Copy streams from the input file to the output file.
for (int i = 0; i < avInputFormatContext->nb_streams; i++) {
AVStream* outStream;
AVStream* inStream = avInputFormatContext->streams[i];
streamRescaledStartSeconds[i] = av_rescale_q(startSeconds * AV_TIME_BASE, AV_TIME_BASE_Q, inStream->time_base);
streamRescaledEndSeconds[i] = av_rescale_q(endSeconds * AV_TIME_BASE, AV_TIME_BASE_Q, inStream->time_base);
if (inStream->codecpar->codec_type != AVMEDIA_TYPE_AUDIO &&
inStream->codecpar->codec_type != AVMEDIA_TYPE_VIDEO &&
inStream->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) {
streamMapping[i] = -1;
continue;
}
streamMapping[i] = streamIndex++;
outStream = avformat_new_stream(avOutputFormatContext, NULL);
if (!outStream) {
operationResult = AVERROR_UNKNOWN;
throw std::runtime_error(QString("Failed to allocate the output stream.").toStdString().c_str());
}
operationResult = avcodec_parameters_copy(outStream->codecpar, inStream->codecpar);
if (operationResult < 0) {
throw std::runtime_error(
QString("Failed to copy codec parameters from input stream to output stream.").toStdString().c_str());
}
outStream->codecpar->codec_tag = 0;
}
if (!(avOutputFormatContext->oformat->flags & AVFMT_NOFILE)) {
operationResult = avio_open(&avOutputFormatContext->pb, outputFilePath.toStdString().c_str(), AVIO_FLAG_WRITE);
if (operationResult < 0) {
throw std::runtime_error(
QString("Failed to open the output file '%1'.").arg(outputFilePath).toStdString().c_str());
}
}
operationResult = avformat_write_header(avOutputFormatContext, NULL);
if (operationResult < 0) {
throw std::runtime_error(QString("Error occurred when opening output file.").toStdString().c_str());
}
operationResult = avformat_seek_file(avInputFormatContext, -1, INT64_MIN, startSeconds * AV_TIME_BASE,
startSeconds * AV_TIME_BASE, 0);
if (operationResult < 0) {
throw std::runtime_error(
QString("Failed to seek the input file to the targeted start position.").toStdString().c_str());
}
while (true) {
operationResult = av_read_frame(avInputFormatContext, avPacket);
if (operationResult < 0) break;
// Skip packets from unknown streams and packets after the end cut position.
if (avPacket->stream_index >= avInputFormatContext->nb_streams || streamMapping[avPacket->stream_index] < 0 ||
avPacket->pts > streamRescaledEndSeconds[avPacket->stream_index]) {
av_packet_unref(avPacket);
continue;
}
avPacket->stream_index = streamMapping[avPacket->stream_index];
logPacket(avInputFormatContext, avPacket, "in");
// Shift the packet to its new position by subtracting the rescaled start seconds.
avPacket->pts -= streamRescaledStartSeconds[avPacket->stream_index];
avPacket->dts -= streamRescaledStartSeconds[avPacket->stream_index];
av_packet_rescale_ts(avPacket, avInputFormatContext->streams[avPacket->stream_index]->time_base,
avOutputFormatContext->streams[avPacket->stream_index]->time_base);
avPacket->pos = -1;
logPacket(avOutputFormatContext, avPacket, "out");
operationResult = av_interleaved_write_frame(avOutputFormatContext, avPacket);
if (operationResult < 0) {
throw std::runtime_error(QString("Failed to mux the packet.").toStdString().c_str());
}
}
av_write_trailer(avOutputFormatContext);
} catch (std::runtime_error e) {
qCritical("%s", e.what());
}
av_packet_free(&avPacket);
avformat_close_input(&avInputFormatContext);
if (avOutputFormatContext && !(avOutputFormatContext->oformat->flags & AVFMT_NOFILE))
avio_closep(&avOutputFormatContext->pb);
avformat_free_context(avOutputFormatContext);
if (operationResult < 0 && operationResult != AVERROR_EOF) {
qCritical("%s", QString("Error occurred: %1.").arg(av_err2str(operationResult)).toStdString().c_str());
return false;
}
return true;
}
The code is written in C++, and it is using some Qt related classes, you can remove them and use the code on plain C++ projects.
I tried my best to make it readable, I hope it is good and helpful.
Update 1: I updated the code to fix a bug in it.
Update 2: I updated the code to do some refactoring.

Screen Recording with FFmpeg-Lib with c++

I'm trying to record the whole desktop stream with FFmpeg on Windows.
I found a working example here. The Problem is that some og the functions depricated. So I tried to replace them with the updated ones.
But there are some slight problems. The error "has triggered a breakpoint." occurse and also "not able to read the location."
The bigger problem is that I don't know if this is the right way to do this..
My code looks like this:
using namespace std;
/* initialize the resources*/
Recorder::Recorder()
{
av_register_all();
avcodec_register_all();
avdevice_register_all();
cout<<"\nall required functions are registered successfully";
}
/* uninitialize the resources */
Recorder::~Recorder()
{
avformat_close_input(&pAVFormatContext);
if( !pAVFormatContext )
{
cout<<"\nfile closed sucessfully";
}
else
{
cout<<"\nunable to close the file";
exit(1);
}
avformat_free_context(pAVFormatContext);
if( !pAVFormatContext )
{
cout<<"\navformat free successfully";
}
else
{
cout<<"\nunable to free avformat context";
exit(1);
}
}
/* establishing the connection between camera or screen through its respective folder */
int Recorder::openCamera()
{
value = 0;
options = NULL;
pAVFormatContext = NULL;
pAVFormatContext = avformat_alloc_context();//Allocate an AVFormatContext.
openScreen(pAVFormatContext);
/* set frame per second */
value = av_dict_set( &options,"framerate","30",0 );
if(value < 0)
{
cout<<"\nerror in setting dictionary value";
exit(1);
}
value = av_dict_set( &options, "preset", "medium", 0 );
if(value < 0)
{
cout<<"\nerror in setting preset values";
exit(1);
}
// value = avformat_find_stream_info(pAVFormatContext,NULL);
if(value < 0)
{
cout<<"\nunable to find the stream information";
exit(1);
}
VideoStreamIndx = -1;
/* find the first video stream index . Also there is an API available to do the below operations */
for(int i = 0; i < pAVFormatContext->nb_streams; i++ ) // find video stream posistion/index.
{
if( pAVFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO )
{
VideoStreamIndx = i;
break;
}
}
if( VideoStreamIndx == -1)
{
cout<<"\nunable to find the video stream index. (-1)";
exit(1);
}
// assign pAVFormatContext to VideoStreamIndx
pAVCodecContext = pAVFormatContext->streams[VideoStreamIndx]->codec;
pAVCodec = avcodec_find_decoder(pAVCodecContext->codec_id);
if( pAVCodec == NULL )
{
cout<<"\nunable to find the decoder";
exit(1);
}
value = avcodec_open2(pAVCodecContext , pAVCodec , NULL);//Initialize the AVCodecContext to use the given AVCodec.
if( value < 0 )
{
cout<<"\nunable to open the av codec";
exit(1);
}
}
/* initialize the video output file and its properties */
int Recorder::init_outputfile()
{
outAVFormatContext = NULL;
value = 0;
output_file = "output.mp4";
avformat_alloc_output_context2(&outAVFormatContext, NULL, NULL, output_file);
if (!outAVFormatContext)
{
cout<<"\nerror in allocating av format output context";
exit(1);
}
/* Returns the output format in the list of registered output formats which best matches the provided parameters, or returns NULL if there is no match. */
output_format = av_guess_format(NULL, output_file ,NULL);
if( !output_format )
{
cout<<"\nerror in guessing the video format. try with correct format";
exit(1);
}
video_st = avformat_new_stream(outAVFormatContext ,NULL);
if( !video_st )
{
cout<<"\nerror in creating a av format new stream";
exit(1);
}
if (codec_id == AV_CODEC_ID_H264)
{
av_opt_set(outAVCodecContext->priv_data, "preset", "slow", 0);
}
outAVCodec = avcodec_find_encoder(AV_CODEC_ID_MPEG4);
if (!outAVCodec)
{
cout << "\nerror in finding the av codecs. try again with correct codec";
exit(1);
}
outAVCodecContext = avcodec_alloc_context3(outAVCodec);
if( !outAVCodecContext )
{
cout<<"\nerror in allocating the codec contexts";
exit(1);
}
/* set property of the video file */
outAVCodecContext = video_st->codec;
outAVCodecContext->codec_id = AV_CODEC_ID_MPEG4;// AV_CODEC_ID_MPEG4; // AV_CODEC_ID_H264 // AV_CODEC_ID_MPEG1VIDEO
outAVCodecContext->codec_type = AVMEDIA_TYPE_VIDEO;
outAVCodecContext->pix_fmt = AV_PIX_FMT_YUV420P;
outAVCodecContext->bit_rate = 400000; // 2500000
outAVCodecContext->width = 1920;
outAVCodecContext->height = 1080;
outAVCodecContext->gop_size = 3;
outAVCodecContext->max_b_frames = 2;
outAVCodecContext->time_base.num = 1;
outAVCodecContext->time_base.den = 30; //15fps
/* Some container formats (like MP4) require global headers to be present
Mark the encoder so that it behaves accordingly. */
if ( outAVFormatContext->oformat->flags & AVFMT_GLOBALHEADER)
{
outAVCodecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
value = avcodec_open2(outAVCodecContext, outAVCodec, NULL);
if( value < 0)
{
cout<<"\nerror in opening the avcodec";
exit(1);
}
/* create empty video file */
if ( !(outAVFormatContext->flags & AVFMT_NOFILE) )
{
if( avio_open2(&outAVFormatContext->pb , output_file , AVIO_FLAG_WRITE ,NULL, NULL) < 0 )
{
cout<<"\nerror in creating the video file";
exit(1);
}
}
if(!outAVFormatContext->nb_streams)
{
cout<<"\noutput file dose not contain any stream";
exit(1);
}
/* imp: mp4 container or some advanced container file required header information*/
value = avformat_write_header(outAVFormatContext , &options);
if(value < 0)
{
cout<<"\nerror in writing the header context";
exit(1);
}
/*
// uncomment here to view the complete video file informations
cout<<"\n\nOutput file information :\n\n";
av_dump_format(outAVFormatContext , 0 ,output_file ,1);
*/
}
int Recorder::stop() {
threading = false;
demux->join();
rescale->join();
mux->join();
return 0;
}
int Recorder::start() {
initVideoThreads();
return 0;
}
int Recorder::initVideoThreads() {
demux = new thread(&Recorder::demuxVideoStream, this, pAVCodecContext, pAVFormatContext, VideoStreamIndx);
rescale = new thread(&Recorder::rescaleVideoStream, this, pAVCodecContext, outAVCodecContext);
demux = new thread(&Recorder::encodeVideoStream, this, outAVCodecContext);
return 0;
}
void Recorder::demuxVideoStream(AVCodecContext* codecContext, AVFormatContext* formatContext, int streamIndex)
{
// init packet
AVPacket* packet = (AVPacket*)av_malloc(sizeof(AVPacket));
av_init_packet(packet);
int ctr = 0;
while (threading)
{
if (av_read_frame(formatContext, packet) < 0) {
exit(1);
}
if (packet->stream_index == streamIndex)
{
int return_value; // = 0;
ctr++;
do
{
return_value = avcodec_send_packet(codecContext, packet);
} while (return_value == AVERROR(EAGAIN) && threading);
//int i = avcodec_send_packet(codecContext, packet);
if (return_value < 0 && threading) { // call Decoder
cout << "unable to decode video";
exit(1);
}
}
}
avcodec_send_packet(codecContext, NULL); // flush decoder
// return 0;
}
void Recorder::rescaleVideoStream(AVCodecContext* inCodecContext, AVCodecContext* outCodecContext)
{
bool closing = false;
AVFrame* inFrame = av_frame_alloc();
if (!inFrame)
{
cout << "\nunable to release the avframe resources";
exit(1);
}
int nbytes = av_image_get_buffer_size(outAVCodecContext->pix_fmt, outAVCodecContext->width, outAVCodecContext->height, 32);
uint8_t* video_outbuf = (uint8_t*)av_malloc(nbytes);
if (video_outbuf == NULL)
{
cout << "\nunable to allocate memory";
exit(1);
}
AVFrame* outFrame = av_frame_alloc();//Allocate an AVFrame and set its fields to default values.
if (!outFrame)
{
cout << "\nunable to release the avframe resources for outframe";
exit(1);
}
// Setup the data pointers and linesizes based on the specified image parameters and the provided array.
int value = av_image_fill_arrays(outFrame->data, outFrame->linesize, video_outbuf, AV_PIX_FMT_YUV420P, outAVCodecContext->width, outAVCodecContext->height, 1); // returns : the size in bytes required for src
if (value < 0)
{
cout << "\nerror in filling image array";
}
int ctr = 0;
while (threading || !closing) {
int value = avcodec_receive_frame(inCodecContext, inFrame);
if (value == 0) {
ctr++;
SwsContext* swsCtx_ = sws_getContext(inCodecContext->width,
inCodecContext->height,
inCodecContext->pix_fmt,
outAVCodecContext->width,
outAVCodecContext->height,
outAVCodecContext->pix_fmt,
SWS_BICUBIC, NULL, NULL, NULL);
sws_scale(swsCtx_, inFrame->data, inFrame->linesize, 0, inCodecContext->height, outFrame->data, outFrame->linesize);
int return_value;
do
{
return_value = avcodec_send_frame(outCodecContext, outFrame);
} while (return_value == AVERROR(EAGAIN) && threading);
}
closing = (value == AVERROR_EOF);
}
avcodec_send_frame(outCodecContext, NULL);
// av_free(video_outbuf);
// return 0;
}
void Recorder::encodeVideoStream(AVCodecContext* codecContext)
{
bool closing = true;
AVPacket* packet = (AVPacket*)av_malloc(sizeof(AVPacket));
av_init_packet(packet);
int ctr = 0;
while (threading || !closing) {
packet->data = NULL; // packet data will be allocated by the encoder
packet->size = 0;
ctr++;
int value = avcodec_receive_packet(codecContext, packet);
if (value == 0) {
if (packet->pts != AV_NOPTS_VALUE)
packet->pts = av_rescale_q(packet->pts, video_st->codec->time_base, video_st->time_base);
if (packet->dts != AV_NOPTS_VALUE)
packet->dts = av_rescale_q(packet->dts, video_st->codec->time_base, video_st->time_base);
//printf("Write frame %3d (size= %2d)\n", j++, packet->size / 1000);
if (av_write_frame(outAVFormatContext, packet) != 0)
{
cout << "\nerror in writing video frame";
}
}
closing = (value == AVERROR_EOF);
}
value = av_write_trailer(outAVFormatContext);
if (value < 0)
{
cout << "\nerror in writing av trailer";
exit(1);
}
// av_free(packet);
// return 0;
}
int Recorder::openScreen(AVFormatContext* pFormatCtx) {
/*
X11 video input device.
To enable this input device during configuration you need libxcb installed on your system. It will be automatically detected during configuration.
This device allows one to capture a region of an X11 display.
refer : https://www.ffmpeg.org/ffmpeg-devices.html#x11grab
*/
/* current below is for screen recording. to connect with camera use v4l2 as a input parameter for av_find_input_format */
pAVInputFormat = av_find_input_format("gdigrab");
//value = avformat_open_input(&pAVFormatContext, ":0.0+10,250", pAVInputFormat, NULL);
value = avformat_open_input(&pAVFormatContext, "desktop", pAVInputFormat, NULL);
if (value != 0)
{
cout << "\nerror in opening input device";
exit(1);
}
return 0;
}

How to use FFmpeg API overlay filter in C / C++

I have a C++ project which creates 7/24 WebTV like RTMP stream and allows operations like changing current content on runtime, seeking content, looping through a playlist which is constructed by a json array, also supports changing whole playlist on runtime.
Currently i am reading H264 and AAC encoded packets from mp4 files then sending them to destination RTMP server after adjusting their PTS & DTS values without any encoding or decoding.
But i want to apply overlay images to raw frames using FFmpeg "overlay" filter after decoding H264 packets. I looked at sample which came with FFmpeg examples ;
#define _XOPEN_SOURCE 600 /* for usleep */
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavutil/opt.h>
const char *filter_descr = "scale=78:24,transpose=cclock";
/* other way:
scale=78:24 [scl]; [scl] transpose=cclock // assumes "[in]" and "[out]" to be input output pads respectively
*/
static AVFormatContext *fmt_ctx;
static AVCodecContext *dec_ctx;
AVFilterContext *buffersink_ctx;
AVFilterContext *buffersrc_ctx;
AVFilterGraph *filter_graph;
static int video_stream_index = -1;
static int64_t last_pts = AV_NOPTS_VALUE;
static int open_input_file(const char *filename)
{
int ret;
AVCodec *dec;
if ((ret = avformat_open_input(&fmt_ctx, filename, NULL, NULL)) < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot open input file\n");
return ret;
}
if ((ret = avformat_find_stream_info(fmt_ctx, NULL)) < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot find stream information\n");
return ret;
}
/* select the video stream */
ret = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &dec, 0);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot find a video stream in the input file\n");
return ret;
}
video_stream_index = ret;
/* create decoding context */
dec_ctx = avcodec_alloc_context3(dec);
if (!dec_ctx)
return AVERROR(ENOMEM);
avcodec_parameters_to_context(dec_ctx, fmt_ctx->streams[video_stream_index]->codecpar);
/* init the video decoder */
if ((ret = avcodec_open2(dec_ctx, dec, NULL)) < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot open video decoder\n");
return ret;
}
return 0;
}
static int init_filters(const char *filters_descr)
{
char args[512];
int ret = 0;
const AVFilter *buffersrc = avfilter_get_by_name("buffer");
const AVFilter *buffersink = avfilter_get_by_name("buffersink");
AVFilterInOut *outputs = avfilter_inout_alloc();
AVFilterInOut *inputs = avfilter_inout_alloc();
AVRational time_base = fmt_ctx->streams[video_stream_index]->time_base;
enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE };
filter_graph = avfilter_graph_alloc();
if (!outputs || !inputs || !filter_graph) {
ret = AVERROR(ENOMEM);
goto end;
}
/* buffer video source: the decoded frames from the decoder will be inserted here. */
snprintf(args, sizeof(args),
"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
dec_ctx->width, dec_ctx->height, dec_ctx->pix_fmt,
time_base.num, time_base.den,
dec_ctx->sample_aspect_ratio.num, dec_ctx->sample_aspect_ratio.den);
ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in",
args, NULL, filter_graph);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot create buffer source\n");
goto end;
}
/* buffer video sink: to terminate the filter chain. */
ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out",
NULL, NULL, filter_graph);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot create buffer sink\n");
goto end;
}
ret = av_opt_set_int_list(buffersink_ctx, "pix_fmts", pix_fmts,
AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot set output pixel format\n");
goto end;
}
/*
* Set the endpoints for the filter graph. The filter_graph will
* be linked to the graph described by filters_descr.
*/
/*
* The buffer source output must be connected to the input pad of
* the first filter described by filters_descr; since the first
* filter input label is not specified, it is set to "in" by
* default.
*/
outputs->name = av_strdup("in");
outputs->filter_ctx = buffersrc_ctx;
outputs->pad_idx = 0;
outputs->next = NULL;
/*
* The buffer sink input must be connected to the output pad of
* the last filter described by filters_descr; since the last
* filter output label is not specified, it is set to "out" by
* default.
*/
inputs->name = av_strdup("out");
inputs->filter_ctx = buffersink_ctx;
inputs->pad_idx = 0;
inputs->next = NULL;
if ((ret = avfilter_graph_parse_ptr(filter_graph, filters_descr,
&inputs, &outputs, NULL)) < 0)
goto end;
if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0)
goto end;
end:
avfilter_inout_free(&inputs);
avfilter_inout_free(&outputs);
return ret;
}
static void display_frame(const AVFrame *frame, AVRational time_base)
{
int x, y;
uint8_t *p0, *p;
int64_t delay;
if (frame->pts != AV_NOPTS_VALUE) {
if (last_pts != AV_NOPTS_VALUE) {
/* sleep roughly the right amount of time;
* usleep is in microseconds, just like AV_TIME_BASE. */
delay = av_rescale_q(frame->pts - last_pts,
time_base, AV_TIME_BASE_Q);
if (delay > 0 && delay < 1000000)
usleep(delay);
}
last_pts = frame->pts;
}
/* Trivial ASCII grayscale display. */
p0 = frame->data[0];
puts("\033c");
for (y = 0; y < frame->height; y++) {
p = p0;
for (x = 0; x < frame->width; x++)
putchar(" .-+#"[*(p++) / 52]);
putchar('\n');
p0 += frame->linesize[0];
}
fflush(stdout);
}
int main(int argc, char **argv)
{
int ret;
AVPacket packet;
AVFrame *frame;
AVFrame *filt_frame;
if (argc != 2) {
fprintf(stderr, "Usage: %s file\n", argv[0]);
exit(1);
}
frame = av_frame_alloc();
filt_frame = av_frame_alloc();
if (!frame || !filt_frame) {
perror("Could not allocate frame");
exit(1);
}
if ((ret = open_input_file(argv[1])) < 0)
goto end;
if ((ret = init_filters(filter_descr)) < 0)
goto end;
/* read all packets */
while (1) {
if ((ret = av_read_frame(fmt_ctx, &packet)) < 0)
break;
if (packet.stream_index == video_stream_index) {
ret = avcodec_send_packet(dec_ctx, &packet);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Error while sending a packet to the decoder\n");
break;
}
while (ret >= 0) {
ret = avcodec_receive_frame(dec_ctx, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
} else if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Error while receiving a frame from the decoder\n");
goto end;
}
frame->pts = frame->best_effort_timestamp;
/* push the decoded frame into the filtergraph */
if (av_buffersrc_add_frame_flags(buffersrc_ctx, frame, AV_BUFFERSRC_FLAG_KEEP_REF) < 0) {
av_log(NULL, AV_LOG_ERROR, "Error while feeding the filtergraph\n");
break;
}
/* pull filtered frames from the filtergraph */
while (1) {
ret = av_buffersink_get_frame(buffersink_ctx, filt_frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
break;
if (ret < 0)
goto end;
display_frame(filt_frame, buffersink_ctx->inputs[0]->time_base);
av_frame_unref(filt_frame);
}
av_frame_unref(frame);
}
}
av_packet_unref(&packet);
}
end:
avfilter_graph_free(&filter_graph);
avcodec_free_context(&dec_ctx);
avformat_close_input(&fmt_ctx);
av_frame_free(&frame);
av_frame_free(&filt_frame);
if (ret < 0 && ret != AVERROR_EOF) {
fprintf(stderr, "Error occurred: %s\n", av_err2str(ret));
exit(1);
}
exit(0);
}
That sample uses these filters ;
"scale=78:24,transpose=cclock"
I compiled and run it with a sample video file but it just outputs fancy characters to console, the code block given below is responsible for this ;
/* Trivial ASCII grayscale display. */
p0 = frame->data[0];
puts("\033c");
for (y = 0; y < frame->height; y++) {
p = p0;
for (x = 0; x < frame->width; x++)
putchar(" .-+#"[*(p++) / 52]);
putchar('\n');
p0 += frame->linesize[0];
}
fflush(stdout);
I have no issues with Encoding & Decoding, i just don't know how to apply "overlay" filter. Are there any tutorials out there demonstrate how to use "overlay" filter?
Just like in the example, except you use "overlay".
snprintf(args, sizeof(args), args here...);
avfilter_graph_create_filter(sink, avfilter_get_by_name("overlay"), nullptr, nullptr, arg, graph);
Then you need TWO create two source pads. i.e.
avfilter_graph_create_filter(sourceX, avfilter_get_by_name("buffer"), nullptr, args, nullptr, m_graph);
and one sink pad. Then feed one source with the video frame and other second with the image to overlay
Following code fragments will be helpful..
char args[512];
int ret = 0;
const AVFilter *bufferSrc = avfilter_get_by_name("buffer");
const AVFilter *bufferOvr = avfilter_get_by_name("buffer");
const AVFilter *bufferSink = avfilter_get_by_name("buffersink");
const AVFilter *ovrFilter = avfilter_get_by_name("overlay");
const AVFilter *colorFilter = avfilter_get_by_name("colorchannelmixer");
enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE };
fFilterGraph = avfilter_graph_alloc();
if (!fFilterGraph) {
ret = AVERROR(ENOMEM);
goto end;
}
/* buffer video source: the decoded frames from the decoder will be inserted here. */
snprintf(args, sizeof(args),
"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
decCtx->width, decCtx->height, decCtx->pix_fmt,
fTimeBase.num, fTimeBase.den,
decCtx->sample_aspect_ratio.num, decCtx->sample_aspect_ratio.den);
ret = avfilter_graph_create_filter(&fBufSrc0Ctx, bufferSrc, "in0",
args, NULL, fFilterGraph);
if (ret < 0)
goto end;
/* buffer video overlay source: the overlayed frame from the file will be inserted here. */
snprintf(args, sizeof(args),
"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
ovrCtx->width, ovrCtx->height, ovrCtx->pix_fmt,
fTimeBase.num, fTimeBase.den,
ovrCtx->sample_aspect_ratio.num, ovrCtx->sample_aspect_ratio.den);
ret = avfilter_graph_create_filter(&fBufSrc1Ctx, bufferOvr, "in1",
args, NULL, fFilterGraph);
if (ret < 0)
goto end;
/* color filter */
snprintf(args, sizeof(args), "aa=%f", (float)fWatermarkOpacity / 10.0);
ret = avfilter_graph_create_filter(&fColorFilterCtx, colorFilter, "colorFilter",
args, NULL, fFilterGraph);
if (ret < 0)
goto end;
/* overlay filter */
switch (fWatermarkPos) {
case 0:
/* Top left */
snprintf(args, sizeof(args), "x=%d:y=%d:repeatlast=1",
fWatermarkOffset, fWatermarkOffset);
break;
case 1:
/* Top right */
snprintf(args, sizeof(args), "x=W-w-%d:y=%d:repeatlast=1",
fWatermarkOffset, fWatermarkOffset);
break;
case 3:
/* Bottom left */
snprintf(args, sizeof(args), "x=%d:y=H-h-%d:repeatlast=1",
fWatermarkOffset, fWatermarkOffset);
break;
case 4:
/* Bottom right */
snprintf(args, sizeof(args), "x=W-w-%d:y=H-h-%d:repeatlast=1",
fWatermarkOffset, fWatermarkOffset);
break;
case 2:
default:
/* Center */
snprintf(args, sizeof(args), "x=(W-w)/2:y=(H-h)/2:repeatlast=1");
break;
}
ret = avfilter_graph_create_filter(&fOvrFilterCtx, ovrFilter, "overlay",
args, NULL, fFilterGraph);
if (ret < 0)
goto end;
/* buffer sink - destination of the final video */
ret = avfilter_graph_create_filter(&fBufSinkCtx, bufferSink, "out",
NULL, NULL, fFilterGraph);
if (ret < 0)
goto end;
ret = av_opt_set_int_list(fBufSinkCtx, "pix_fmts", pix_fmts,
AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN);
if (ret < 0)
goto end;
/*
* Link all filters..
*/
avfilter_link(fBufSrc0Ctx, 0, fOvrFilterCtx, 0);
avfilter_link(fBufSrc1Ctx, 0, fColorFilterCtx, 0);
avfilter_link(fColorFilterCtx, 0, fOvrFilterCtx, 1);
avfilter_link(fOvrFilterCtx, 0, fBufSinkCtx, 0);
if ((ret = avfilter_graph_config(fFilterGraph, NULL)) < 0)
goto end;
end:

How to set start time of video saved from RTSP stream with FFMPEG

I use FFMPEG to record video from a RTSP stream. What my code does is get current day time, create a folder with this format year/month/day/hour/minute and save the video to that folder.
When a new minute arrive, I create the new folder base on the new minute and run the record again to the new folder.
Basically It works, but the next video start time is continue the end of previous video. For example:
video1: 00:00 -> 00:55
video2: 00:56 -> ...
I hope I can set for all videos start from 00:00. Can I do that?
Here my code
ffmpeg.h
class CtFfmpeg {
public:
CtFfmpeg();
~CtFfmpeg();
void init();
int getInput();
int getOutputName(const char *filename);
int release();
int ret;
AVFormatContext *ifmt_ctx, *ofmt_ctx;
AVStream *in_stream, *out_stream;
AVPacket pkt;
const char *in_filename;
char *out_filename;
private:
int setOutput(const char *outfilename);
AVOutputFormat *ofmt;
};
ffmpeg.cpp
#include "ctffmpeg.h"
CtFfmpeg::CtFfmpeg() {
in_filename = new char [1024];
out_filename = new char [1024];
}
CtFfmpeg::~CtFfmpeg() {
delete [] in_filename;
delete [] out_filename;
}
void CtFfmpeg::init() {
avcodec_register_all();
av_register_all();
avformat_network_init();
pkt = { 0 };
av_init_packet(&pkt);
ofmt = NULL;
ifmt_ctx = NULL;
ofmt_ctx = NULL;
return;
}
int CtFfmpeg::release() {
av_write_trailer(ofmt_ctx);
avcodec_close(out_stream->codec);
// avcodec_close(in_stream->codec);
// avformat_close_input(&ifmt_ctx);
/* close output */
if (!(ofmt->flags & AVFMT_NOFILE))
avio_close(ofmt_ctx->pb);
avformat_free_context(ofmt_ctx);
av_free_packet(&pkt);
if (ret < 0 && ret != AVERROR_EOF) {
fprintf(stderr, "Error occurred\n");
return 1;
}
}
int CtFfmpeg::getInput() {
if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) {
fprintf(stderr, "Could not open input file '%s'", in_filename);
release();
}
if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
fprintf(stderr, "Failed to retrieve input stream information");
release();
}
av_dump_format(ifmt_ctx, 0, in_filename, 0);
}
int CtFfmpeg::setOutput(const char *outfilename) {
avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, outfilename);
if (!ofmt_ctx) {
fprintf(stderr, "Could not create output context\n");
ret = AVERROR_UNKNOWN;
release();
}
ofmt = ofmt_ctx->oformat;
for (int i = 0; i < ifmt_ctx->nb_streams; i++) {
in_stream = ifmt_ctx->streams[i];
out_stream = avformat_new_stream(ofmt_ctx, in_stream->codec->codec);
if (!out_stream) {
fprintf(stderr, "Failed allocating output stream\n");
ret = AVERROR_UNKNOWN;
release();
}
ret = avcodec_copy_context(out_stream->codec, in_stream->codec);
if (ret < 0) {
fprintf(stderr, "Failed to copy context from input to output stream codec context\n");
release();
}
out_stream->codec->codec_tag = 0;
if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
} // for
av_dump_format(ofmt_ctx, 0, outfilename, 1);
if (!(ofmt->flags & AVFMT_NOFILE)) {
ret = avio_open(&ofmt_ctx->pb, outfilename, AVIO_FLAG_WRITE);
if (ret < 0) {
fprintf(stderr, "Could not open output file '%s'", outfilename);
release();
}
}
ret = avformat_write_header(ofmt_ctx, NULL);
if (ret < 0) {
fprintf(stderr, "Error occurred when opening output file\n");
release();
}
}
int CtFfmpeg::getOutputName(const char *filename){
sprintf(out_filename,filename);
setOutput(out_filename);
}
main.cpp
#include "ctfolder.h"
#include "ctffmpeg.h"
CtFfmpeg * ff;
int main(int argc, char** argv) {
if (argc < 2) {
printf("usage: %s <RTSP link> \n", argv[0]);
return 1;
}
ff = new CtFfmpeg();
ff->in_filename = argv[1]; //RTSP input link
ff->init();
ff->getInput();
string filename;
videoRecorder obj;
int start, now;
start = obj.get_current_min();
if(obj.create_folder(0755))
cout << "Cannot create folder, maybe it already exists" << endl;
else
cout << "Create folder succesfully" << endl;
int skip = 0;
while(1){
filename = obj.update_filename();
ff->getOutputName(filename.c_str());
while((now = obj.get_current_min()) == start) {
ff->ret = av_read_frame(ff->ifmt_ctx, &(ff->pkt));
skip++;
if(skip==1)
continue;
if(skip>2)
skip=2;
if (ff->ret < 0)
continue;
ff->pkt.pts = av_rescale_q_rnd(ff->pkt.pts, ff->in_stream->time_base, ff->out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
ff->pkt.dts = av_rescale_q_rnd(ff->pkt.dts, ff->in_stream->time_base, ff->out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
ff->pkt.duration = av_rescale_q(ff->pkt.duration, ff->in_stream->time_base, ff->out_stream->time_base);
ff->pkt.pos = -1;
ff->ret = av_interleaved_write_frame(ff->ofmt_ctx, &(ff->pkt));
if (ff->ret < 0) {
fprintf(stderr, "Error muxing packet\n");
continue;
}
av_free_packet(&(ff->pkt));
}
ff->release();
cout << "New minute!" << endl;
if(obj.create_folder(0755))
cout << "Cannot create folder, something's wrong" << endl;
else
cout << "Create folder succesfully" << endl;
start = now;
}
return 0;
}
You need to shift your recording packet's pts to 0.
while(<some condition>)
{
//...
int64_t pts_offset = AV_NOPTS_VALUE ;
while((now = obj.get_current_min()) == start)
{
//...
ff.pkt.pts = ...
//...
if( pts_offset == AV_NOPTS_VALUE )
{
pts_offset = ff.pkt.pts ;
}
ff.pkt.pts -= pts_offset ;
// ...
}
}
I tried to build your code and add Alexander Chernin suggestion to it but I face to muxer error!
When you decrease recording packet's pts, it's value go lower than recording packet's dts. In avcodec.h, above declaration of pts I found this comment:
pts MUST be larger or equal to dts as presentation cannot happen before decompression.
I solved this error by decreasing recording packet's dts.
ff->pkt.pts = av_rescale_q_rnd(ff->pkt.pts, ff->in_stream->ff->out_stream->(AVRounding) (AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
if (pts_offset == AV_NOPTS_VALUE) {
pts_offset = ff->pkt.pts;
}
ff->pkt.pts -= pts_offset;
ff->pkt.dts -= pts_offset;
ff->pkt.dts = av_rescale_q_rnd(ff->pkt.dts, ff->in_stream->time_base,ff->out_stream->time_base,(AVRounding) (AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
ff->pkt.duration = av_rescale_q(ff->pkt.duration,ff->in_stream->time_base,ff->out_stream->time_base);
ff->pkt.pos = -1;

FFMPEG. Read frame, process it, put it to output video. Copy sound stream unchanged

I want to apply processing to a video clip with sound track, extract and process frame by frame and write result to output file. Number of frames, size of frame and speed remains unchanged in output clip. Also I want to keep the same audio track as I have in source.
I can read clip, decode frames and process then using opencv. Audio packets are also writes fine. I'm stuck on forming output video stream.
The minimal runnable code I have for now (sorry it not so short, but cant do it shorter):
extern "C" {
#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>
#include "libavcodec/avcodec.h"
#include <libavutil/opt.h>
#include <libavdevice/avdevice.h>
#include <libswscale/swscale.h>
}
#include "opencv2/opencv.hpp"
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,28,1)
#define av_frame_alloc avcodec_alloc_frame
#endif
using namespace std;
using namespace cv;
static void log_packet(const AVFormatContext *fmt_ctx, const AVPacket *pkt, const char *tag)
{
AVRational *time_base = &fmt_ctx->streams[pkt->stream_index]->time_base;
char buf1[AV_TS_MAX_STRING_SIZE] = { 0 };
av_ts_make_string(buf1, pkt->pts);
char buf2[AV_TS_MAX_STRING_SIZE] = { 0 };
av_ts_make_string(buf1, pkt->dts);
char buf3[AV_TS_MAX_STRING_SIZE] = { 0 };
av_ts_make_string(buf1, pkt->duration);
char buf4[AV_TS_MAX_STRING_SIZE] = { 0 };
av_ts_make_time_string(buf1, pkt->pts, time_base);
char buf5[AV_TS_MAX_STRING_SIZE] = { 0 };
av_ts_make_time_string(buf1, pkt->dts, time_base);
char buf6[AV_TS_MAX_STRING_SIZE] = { 0 };
av_ts_make_time_string(buf1, pkt->duration, time_base);
printf("pts:%s pts_time:%s dts:%s dts_time:%s duration:%s duration_time:%s stream_index:%d\n",
buf1, buf4,
buf2, buf5,
buf3, buf6,
pkt->stream_index);
}
int main(int argc, char **argv)
{
AVOutputFormat *ofmt = NULL;
AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL;
AVPacket pkt;
AVFrame *pFrame = NULL;
AVFrame *pFrameRGB = NULL;
int frameFinished = 0;
pFrame = av_frame_alloc();
pFrameRGB = av_frame_alloc();
const char *in_filename, *out_filename;
int ret, i;
in_filename = "../../TestClips/Audio Video Sync Test.mp4";
out_filename = "out.mp4";
// Initialize FFMPEG
av_register_all();
// Get input file format context
if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0)
{
fprintf(stderr, "Could not open input file '%s'", in_filename);
goto end;
}
// Extract streams description
if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0)
{
fprintf(stderr, "Failed to retrieve input stream information");
goto end;
}
// Print detailed information about the input or output format,
// such as duration, bitrate, streams, container, programs, metadata, side data, codec and time base.
av_dump_format(ifmt_ctx, 0, in_filename, 0);
// Allocate an AVFormatContext for an output format.
avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename);
if (!ofmt_ctx)
{
fprintf(stderr, "Could not create output context\n");
ret = AVERROR_UNKNOWN;
goto end;
}
// The output container format.
ofmt = ofmt_ctx->oformat;
// Allocating output streams
for (i = 0; i < ifmt_ctx->nb_streams; i++)
{
AVStream *in_stream = ifmt_ctx->streams[i];
AVStream *out_stream = avformat_new_stream(ofmt_ctx, in_stream->codec->codec);
if (!out_stream)
{
fprintf(stderr, "Failed allocating output stream\n");
ret = AVERROR_UNKNOWN;
goto end;
}
ret = avcodec_copy_context(out_stream->codec, in_stream->codec);
if (ret < 0)
{
fprintf(stderr, "Failed to copy context from input to output stream codec context\n");
goto end;
}
out_stream->codec->codec_tag = 0;
if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
{
out_stream->codec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
}
// Show output format info
av_dump_format(ofmt_ctx, 0, out_filename, 1);
// Open output file
if (!(ofmt->flags & AVFMT_NOFILE))
{
ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);
if (ret < 0)
{
fprintf(stderr, "Could not open output file '%s'", out_filename);
goto end;
}
}
// Write output file header
ret = avformat_write_header(ofmt_ctx, NULL);
if (ret < 0)
{
fprintf(stderr, "Error occurred when opening output file\n");
goto end;
}
// Search for input video codec info
AVCodec *in_codec = nullptr;
AVCodecContext* avctx = nullptr;
int video_stream_index = -1;
for (int i = 0; i < ifmt_ctx->nb_streams; i++)
{
if (ifmt_ctx->streams[i]->codec->coder_type == AVMEDIA_TYPE_VIDEO)
{
video_stream_index = i;
avctx = ifmt_ctx->streams[i]->codec;
in_codec = avcodec_find_decoder(avctx->codec_id);
if (!in_codec)
{
fprintf(stderr, "in codec not found\n");
exit(1);
}
break;
}
}
// Search for output video codec info
AVCodec *out_codec = nullptr;
AVCodecContext* o_avctx = nullptr;
int o_video_stream_index = -1;
for (int i = 0; i < ofmt_ctx->nb_streams; i++)
{
if (ofmt_ctx->streams[i]->codec->coder_type == AVMEDIA_TYPE_VIDEO)
{
o_video_stream_index = i;
o_avctx = ofmt_ctx->streams[i]->codec;
out_codec = avcodec_find_encoder(o_avctx->codec_id);
if (!out_codec)
{
fprintf(stderr, "out codec not found\n");
exit(1);
}
break;
}
}
// openCV pixel format
AVPixelFormat pFormat = AV_PIX_FMT_RGB24;
// Data size
int numBytes = avpicture_get_size(pFormat, avctx->width, avctx->height);
// allocate buffer
uint8_t *buffer = (uint8_t *)av_malloc(numBytes * sizeof(uint8_t));
// fill frame structure
avpicture_fill((AVPicture *)pFrameRGB, buffer, pFormat, avctx->width, avctx->height);
// frame area
int y_size = avctx->width * avctx->height;
// Open input codec
avcodec_open2(avctx, in_codec, NULL);
// Main loop
while (1)
{
AVStream *in_stream, *out_stream;
ret = av_read_frame(ifmt_ctx, &pkt);
if (ret < 0)
{
break;
}
in_stream = ifmt_ctx->streams[pkt.stream_index];
out_stream = ofmt_ctx->streams[pkt.stream_index];
log_packet(ifmt_ctx, &pkt, "in");
// copy packet
pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, AVRounding(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, AVRounding(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
pkt.pos = -1;
log_packet(ofmt_ctx, &pkt, "out");
if (pkt.stream_index == video_stream_index)
{
avcodec_decode_video2(avctx, pFrame, &frameFinished, &pkt);
if (frameFinished)
{
struct SwsContext *img_convert_ctx;
img_convert_ctx = sws_getCachedContext(NULL,
avctx->width,
avctx->height,
avctx->pix_fmt,
avctx->width,
avctx->height,
AV_PIX_FMT_BGR24,
SWS_BICUBIC,
NULL,
NULL,
NULL);
sws_scale(img_convert_ctx,
((AVPicture*)pFrame)->data,
((AVPicture*)pFrame)->linesize,
0,
avctx->height,
((AVPicture *)pFrameRGB)->data,
((AVPicture *)pFrameRGB)->linesize);
sws_freeContext(img_convert_ctx);
// Do some image processing
cv::Mat img(pFrame->height, pFrame->width, CV_8UC3, pFrameRGB->data[0],false);
cv::GaussianBlur(img,img,Size(5,5),3);
cv::imshow("Display", img);
cv::waitKey(5);
// --------------------------------
// Transform back to initial format
// --------------------------------
img_convert_ctx = sws_getCachedContext(NULL,
avctx->width,
avctx->height,
AV_PIX_FMT_BGR24,
avctx->width,
avctx->height,
avctx->pix_fmt,
SWS_BICUBIC,
NULL,
NULL,
NULL);
sws_scale(img_convert_ctx,
((AVPicture*)pFrameRGB)->data,
((AVPicture*)pFrameRGB)->linesize,
0,
avctx->height,
((AVPicture *)pFrame)->data,
((AVPicture *)pFrame)->linesize);
// --------------------------------------------
// Something must be here
// --------------------------------------------
//
// Write fideo frame (How to write frame to output stream ?)
//
// --------------------------------------------
sws_freeContext(img_convert_ctx);
}
}
else // write sound frame
{
ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
}
if (ret < 0)
{
fprintf(stderr, "Error muxing packet\n");
break;
}
// Decrease packet ref counter
av_packet_unref(&pkt);
}
av_write_trailer(ofmt_ctx);
end:
avformat_close_input(&ifmt_ctx);
// close output
if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
{
avio_closep(&ofmt_ctx->pb);
}
avformat_free_context(ofmt_ctx);
if (ret < 0 && ret != AVERROR_EOF)
{
char buf_err[AV_ERROR_MAX_STRING_SIZE] = { 0 };
av_make_error_string(buf_err, AV_ERROR_MAX_STRING_SIZE, ret);
fprintf(stderr, "Error occurred: %s\n", buf_err);
return 1;
}
avcodec_close(avctx);
av_free(pFrame);
av_free(pFrameRGB);
return 0;
}
Your original code segfaults in my case. Initializing the output codec context seems to fix it. The code below works for me but I didn't test the OpenCV stuff as I don't have the lib installed.
Get the codec context:
// Search for output video codec info
AVCodec *out_codec = NULL;
AVCodecContext* o_avctx = NULL;
int o_video_stream_index = -1;
for (int i = 0; i < ofmt_ctx->nb_streams; i++)
{
if (ofmt_ctx->streams[i]->codec->coder_type == AVMEDIA_TYPE_VIDEO)
{
o_video_stream_index = i;
out_codec = avcodec_find_encoder(ofmt_ctx->streams[i]->codec->codec_id);
o_avctx = avcodec_alloc_context3(out_codec);
o_avctx->height = avctx->height;
o_avctx->width = avctx->width;
o_avctx->sample_aspect_ratio = avctx->sample_aspect_ratio;
if (out_codec->pix_fmts)
o_avctx->pix_fmt = out_codec->pix_fmts[0];
else
o_avctx->pix_fmt = avctx->pix_fmt;
o_avctx->time_base = avctx->time_base;
avcodec_open2(o_avctx, out_codec, NULL);
}
}
Encode and write:
// Main loop
while (1)
{
...
if (pkt.stream_index == video_stream_index)
{
avcodec_decode_video2(avctx, pFrame, &frameFinished, &pkt);
if (frameFinished)
{
...
// --------------------------------------------
// Something must be here
// --------------------------------------------
int got_packet = 0;
AVPacket enc_pkt = { 0 };
av_init_packet(&enc_pkt);
avcodec_encode_video2(o_avctx, &enc_pkt, pFrame, &got_packet);
av_interleaved_write_frame(ofmt_ctx, &enc_pkt);
....
}
}
you should assign processed frame's packets information to your Original packets then pass it to av_interleaved_write_frame