Last active
May 3, 2023 01:50
-
-
Save asmwarrior/dc0cbaf37d69eb0561adad1e364a7d5d to your computer and use it in GitHub Desktop.
the source code is mainly from chatGPT to support this question: https://stackoverflow.com/questions/76146859/save-a-serial-of-imagescvmat-to-a-mp4-file-in-variable-frame-rate-mode-by-us
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <vector> | |
#include <cstring> | |
#include <fstream> | |
#include <sstream> | |
#include <stdexcept> | |
#include <opencv2/opencv.hpp> | |
extern "C" { | |
#include <libavutil/imgutils.h> | |
#include <libavcodec/avcodec.h> | |
#include <libavformat/avformat.h> | |
#include <libavutil/opt.h> | |
#include <libswscale/swscale.h> | |
#include <libavutil/frame.h> | |
} | |
// below code from: normalize_ts.cpp:217:49: error: taking address of temporary array Issue #5 | |
// joncampbell123/composite-video-simulator — https://github.com/joncampbell123/composite-video-simulator/issues/5 | |
#ifdef av_err2str | |
#undef av_err2str | |
#include <string> | |
av_always_inline std::string av_err2string(int errnum) { | |
char str[AV_ERROR_MAX_STRING_SIZE]; | |
return av_make_error_string(str, AV_ERROR_MAX_STRING_SIZE, errnum); | |
} | |
#define av_err2str(err) av_err2string(err).c_str() | |
#endif // av_err2str | |
int main(int argc, char* argv[]) | |
{ | |
// Open the input file using FFmpeg | |
const char* filename = "test.mp4"; | |
AVFormatContext* formatContext = nullptr; | |
if (avformat_open_input(&formatContext, filename, nullptr, nullptr) != 0) { | |
std::cerr << "Failed to open file: " << filename << std::endl; | |
return -1; | |
} | |
if (avformat_find_stream_info(formatContext, nullptr) < 0) { | |
std::cerr << "Failed to find stream information: " << filename << std::endl; | |
return -1; | |
} | |
// Find the first video stream and its decoder | |
const AVCodec* codec = nullptr; | |
int videoStreamIndex = av_find_best_stream(formatContext, AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0); | |
if (videoStreamIndex < 0) { | |
std::cerr << "Failed to find video stream: " << filename << std::endl; | |
return -1; | |
} | |
AVCodecContext* codecContext = avcodec_alloc_context3(codec); | |
if (!codecContext) { | |
std::cerr << "Failed to allocate codec context" << std::endl; | |
return -1; | |
} | |
if (avcodec_parameters_to_context(codecContext, formatContext->streams[videoStreamIndex]->codecpar) < 0) { | |
std::cerr << "Failed to copy codec parameters to context" << std::endl; | |
return -1; | |
} | |
if (avcodec_open2(codecContext, codec, nullptr) < 0) { | |
std::cerr << "Failed to open codec" << std::endl; | |
return -1; | |
} | |
// Allocate the frame buffer and the scaler context | |
AVFrame* frame = av_frame_alloc(); | |
AVFrame* frameRGB = av_frame_alloc(); | |
uint8_t* buffer = nullptr; | |
int numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB24, codecContext->width, codecContext->height, 1); | |
buffer = (uint8_t*)av_malloc(numBytes * sizeof(uint8_t)); | |
av_image_fill_arrays(frameRGB->data, frameRGB->linesize, buffer, AV_PIX_FMT_RGB24, codecContext->width, codecContext->height, 1); | |
SwsContext* swsContext = sws_getContext(codecContext->width, codecContext->height, codecContext->pix_fmt, codecContext->width, codecContext->height, AV_PIX_FMT_RGB24, SWS_BILINEAR, nullptr, nullptr, nullptr); | |
// Read frames from the input file and display them using OpenCV | |
AVPacket packet; | |
av_init_packet(&packet); | |
int frameIndex = 0; | |
while (av_read_frame(formatContext, &packet) >= 0) { | |
if (packet.stream_index == videoStreamIndex) { | |
// Decode the frame | |
int response = avcodec_send_packet(codecContext, &packet); | |
if (response < 0) { | |
std::cerr << "Error decoding packet: " << av_err2str(response) << std::endl; | |
break; | |
} | |
while (response >= 0) { | |
response = avcodec_receive_frame(codecContext, frame); | |
if (response == AVERROR(EAGAIN) || response == AVERROR_EOF) { | |
break; | |
} | |
else if (response < 0) { | |
std::cerr << "Error decoding frame: " << av_err2str(response) << std::endl; | |
break; | |
} | |
// Convert the frame to RGB and display it using OpenCV | |
sws_scale(swsContext, frame->data, frame->linesize, 0, codecContext->height, frameRGB->data, frameRGB->linesize); | |
cv::Mat image(codecContext->height, codecContext->width, CV_8UC3, frameRGB->data[0]); | |
cv::imshow("Frame", image); | |
// Get the PTS value of the frame and print it | |
int64_t pts = frame->best_effort_timestamp; //av_frame_get_best_effort_timestamp(frame); | |
std::cout << "Frame " << frameIndex << " PTS: " << pts << std::endl; | |
// Wait for a key press, and exit if the 'q' key is pressed | |
int key = cv::waitKey(1); | |
if (key == 'q') { | |
break; | |
} | |
frameIndex++; | |
} | |
} | |
av_packet_unref(&packet); | |
} | |
// Clean up resources | |
avformat_close_input(&formatContext); | |
avcodec_free_context(&codecContext); | |
av_frame_free(&frame); | |
av_frame_free(&frameRGB); | |
av_free(buffer); | |
sws_freeContext(swsContext); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
When running the code, those messages will be shown on the console window:
I just check the
test.mp4
file with theffprobe
command, such as:It shows the correct content and matched content of the console, here is the frames4.txt content: