I am trying to use ffmpeg to decode a video stream and convert to Opencv cv::Mat.
I found this piece of code and made it compiled. Howerver, the decoding process will always fail after several frames. The call to avcodec_send_packet return error code -11.
I do now know what the problem is. I am sure the video stream and my FFmpeg is ok. I can transcode the stream video using ffmpeg command line.
The error always happen on
error = avcodec_send_packet(videoCodecContext, &packet);
#include <iostream>
#include <string>
#include <vector>
extern "C" {
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
#include <libavcodec/avcodec.h>
}
#include <opencv2/opencv.hpp>
// helper function to check for FFmpeg errors
inline void checkError(int error, const std::string& message) {
if (error < 0) {
//std::cerr << message << ": " << av_err2str(error) << std::endl; //error C4576: a parenthesized type followed by an initializer list is a non-standard explicit type conv>
std::cerr << message << ": " << std::to_string(error) << std::endl;
exit(EXIT_FAILURE);
}
}
int main() {
// initialize FFmpeg
av_log_set_level(AV_LOG_ERROR);
avformat_network_init();
// open the input file
std::string fileName = "rtsp://[email protected]:554//Streaming/Channels/101";
AVFormatContext* formatContext = nullptr;
int error = avformat_open_input(&formatContext, fileName.c_str(), nullptr, nullptr);
checkError(error, "Error opening input file");
//Read packets of a media file to get stream information.
////////////////////////////////////////////////////////////////////////////
error = avformat_find_stream_info(formatContext, nullptr);
checkError(error, "Error avformat find stream info");
////////////////////////////////////////////////////////////////////////////
// find the video stream
AVStream* videoStream = nullptr;
for (unsigned int i = 0; i < formatContext->nb_streams; i++) {
if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && !videoStream) {
videoStream = formatContext->streams[i];
}
}
if (!videoStream) {
std::cerr << "Error: input file does not contain a video stream" << std::endl;
exit(EXIT_FAILURE);
}
// create the video codec context
const AVCodec* videoCodec = avcodec_find_decoder(videoStream->codecpar->codec_id);
AVCodecContext* videoCodecContext = avcodec_alloc_context3(videoCodec);
if (!videoCodecContext) {
std::cerr << "Error allocating video codec context" << std::endl;
exit(EXIT_FAILURE);
}
error = avcodec_parameters_to_context(videoCodecContext, videoStream->codecpar);
checkError(error, "Error setting video codec context parameters");
error = avcodec_open2(videoCodecContext, videoCodec, nullptr);
checkError(error, "Error opening video codec");
// create the frame scaler
int width = videoCodecContext->width;
int height = videoCodecContext->height;
std::cout<<"frame width:" << width << std::endl;
std::cout<<"frame height:" << height << std::endl;
std::cout<<"frame pix fmt:" << videoCodecContext->pix_fmt << std::endl;
struct SwsContext* frameScaler = sws_getContext(width, height, videoCodecContext->pix_fmt, width, height, AV_PIX_FMT_BGR24, SWS_BICUBIC, nullptr, nullptr, nullptr);
// read the packets and decode the video frames
std::vector<cv::Mat> videoFrames;
AVPacket packet;
int i=0;
while (av_read_frame(formatContext, &packet) == 0) {
std::cout<<"frame number:"<< i++ <<std::endl;
if (packet.stream_index == videoStream->index) {
// decode the video frame
AVFrame* frame = av_frame_alloc();
int gotFrame = 0;
error = avcodec_send_packet(videoCodecContext, &packet);
checkError(error, "Error sending packet to video codec");
if (error<0) {
continue;
}
error = avcodec_receive_frame(videoCodecContext, frame);
//There is not enough data for decoding the frame, have to free and get more data
////////////////////////////////////////////////////////////////////////////
if (error == AVERROR(EAGAIN))
{
av_frame_unref(frame);
av_freep(frame);
continue;
}
if (error == AVERROR_EOF)
{
std::cerr << "AVERROR_EOF" << std::endl;
break;
}
////////////////////////////////////////////////////////////////////////////
checkError(error, "Error receiving frame from video codec");
if (error == 0) {
gotFrame = 1;
}
if (gotFrame) {
// scale the frame to the desired format
AVFrame* scaledFrame = av_frame_alloc();
av_image_alloc(scaledFrame->data, scaledFrame->linesize, width, height, AV_PIX_FMT_BGR24, 32);
sws_scale(frameScaler, frame->data, frame->linesize, 0, height, scaledFrame->data, scaledFrame->linesize);
// copy the frame data to a cv::Mat object
cv::Mat mat(height, width, CV_8UC3, scaledFrame->data[0], scaledFrame->linesize[0]);
//Show mat image for testing
////////////////////////////////////////////////////////////////////////////
//cv::imshow("mat", mat);
//cv::waitKey(100); //Wait 100msec (relativly long time - for testing).
////////////////////////////////////////////////////////////////////////////
videoFrames.push_back(mat.clone());
// clean up
av_freep(&scaledFrame->data[0]);
av_frame_free(&scaledFrame);
}
av_frame_free(&frame);
}
av_packet_unref(&packet);
}
// clean up
sws_freeContext(frameScaler);
avcodec_free_context(&videoCodecContext);
avformat_close_input(&formatContext);
return 0;
}