extern "C" { #include #include #include #include #include #include } #include #include #include #include #include #include "V210.hpp" #include "FlowDef.hpp" #include "NodeBase.hpp" int main(int argc, char* argv[]) { // Open video file AVFormatContext* formatContext = nullptr; const char* filename = (argc > 1) ? argv[1] : "/home/itten/test-vid/0.ts"; if (avformat_open_input(&formatContext, filename, nullptr, nullptr) != 0) { std::cerr << "Could not open file: " << filename << std::endl; return 1; } // Find stream info if (avformat_find_stream_info(formatContext, nullptr) < 0) { std::cerr << "Could not find stream info" << std::endl; avformat_close_input(&formatContext); return 1; } // Find video stream int videoStreamIndex = -1; for (unsigned int i = 0; i < formatContext->nb_streams; i++) { if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { videoStreamIndex = i; break; } } if (videoStreamIndex == -1) { std::cerr << "No video stream found" << std::endl; avformat_close_input(&formatContext); return 1; } // Get codec parameters AVCodecParameters* codecParams = formatContext->streams[videoStreamIndex]->codecpar; const AVCodec* codec = avcodec_find_decoder(codecParams->codec_id); if (!codec) { std::cerr << "Unsupported codec" << std::endl; avformat_close_input(&formatContext); return 1; } // Open codec AVCodecContext* codecContext = avcodec_alloc_context3(codec); avcodec_parameters_to_context(codecContext, codecParams); if (avcodec_open2(codecContext, codec, nullptr) < 0) { std::cerr << "Could not open codec" << std::endl; avcodec_free_context(&codecContext); avformat_close_input(&formatContext); return 1; } std::cout << "Video info:" << std::endl; std::cout << " Width: " << codecContext->width << std::endl; std::cout << " Height: " << codecContext->height << std::endl; std::cout << " Pixel format: " << av_get_pix_fmt_name(codecContext->pix_fmt) << std::endl; std::cout << " Frame rate: " << codecContext->framerate.num << "/" << codecContext->framerate.den << std::endl; // Create Sws context to convert to UYVY422 struct SwsContext *sws_ctx = sws_getContext( codecContext->width, codecContext->height, codecContext->pix_fmt, codecContext->width, codecContext->height, AV_PIX_FMT_UYVY422, NULL, NULL, NULL, NULL ); if (!sws_ctx) { std::cerr << "Failed to create SwsContext" << std::endl; } // Allocate UYVY buffer int uyvy_buf_size = av_image_get_buffer_size( AV_PIX_FMT_UYVY422, codecContext->width, codecContext->height, 32 ); if (uyvy_buf_size < 0) { std::cerr << "Failed to calculate UYVY buffer size" << std::endl; } uint8_t* uyvy_buffer = (uint8_t*)av_malloc(uyvy_buf_size); if (!uyvy_buffer) { std::cerr << "Failed to allocate UYVY buffer" << std::endl; } int uyvy_line_size = av_image_get_linesize(AV_PIX_FMT_UYVY422, codecContext->width, 0); if (uyvy_line_size < 0) { std::cerr << "Failed to get UYVY line size" << std::endl; } uint8_t* uyvy_data[4] = {uyvy_buffer, nullptr, nullptr, nullptr}; int uyvy_line_sizes[4] = {uyvy_line_size, 0, 0, 0}; // Allocate v210 buffer int blocks_per_row = (codecContext->width + 5) / 6; int v210_bytes_per_row = blocks_per_row * 16; int v210_stride = ((v210_bytes_per_row + 63) / 64) * 64; int v210_buffer_size = v210_stride * codecContext->height; uint8_t* v210_buffer = (uint8_t*)av_malloc(v210_buffer_size); if (!v210_buffer) { std::cerr << "Failed to allocate V210 buffer" << std::endl; } // MXL prep int fps_num = 25, fps_den = 1; mxlInstance mxl_instance = mxlCreateInstance("/tmp/videotest-domain", nullptr); std::string video_flow_def = ""; mxlFlowWriter video_writer = nullptr; mxlFlowConfigInfo video_config = {}; std::string flow_uuid = "5fbec3b1-1b0f-417d-9059-8b94a47197ed"; video_flow_def = dmf::make_video_flow_def( flow_uuid, "libav video flow", codecContext->width, codecContext->height, fps_num, fps_den ); bool is_flow_created = false; mxlStatus vst = mxlCreateFlowWriter(mxl_instance, video_flow_def.c_str(), "", &video_writer, &video_config, &is_flow_created); if (vst != MXL_STATUS_OK) { std::cerr << "MXL flow writer is not created. Reason: " << dmf::mxl_status_str(vst) << std::endl; } mxlRational video_rate = {fps_num, fps_den}; uint64_t video_index = mxlGetCurrentIndex(&video_rate); // Allocate packets and frames AVPacket* packet = av_packet_alloc(); AVFrame* frame = av_frame_alloc(); // Read and decode frames (example - just count them) int frameCount = 0; while (av_read_frame(formatContext, packet) >= 0) { if (packet->stream_index == videoStreamIndex) { if (avcodec_send_packet(codecContext, packet) == 0) { while (avcodec_receive_frame(codecContext, frame) == 0) { frameCount++; // mxl part mxlGrainInfo grain{}; uint8_t* buf = nullptr; vst = mxlFlowWriterOpenGrain(video_writer, video_index, &grain, &buf); if (vst == MXL_STATUS_OK) { // source pix_fmt -> UYVY422 pix_fmt sws_scale(sws_ctx, frame->data, frame->linesize, 0, codecContext->height, uyvy_data, uyvy_line_sizes); // UYVY -> V210 dmf::v210::UYVYtoV210( uyvy_buffer, buf, codecContext->width, codecContext->height, uyvy_line_size, v210_stride ); grain.flags = 0; grain.validSlices = grain.totalSlices; mxlFlowWriterCommitGrain(video_writer, &grain); } const uint64_t ns = mxlGetNsUntilIndex(video_index + 1, &video_rate); if (ns > 0 && ns < 2'000'000'000ULL) mxlSleepForNs(ns); video_index++; av_frame_unref(frame); } } } av_packet_unref(packet); } std::cout << "Total frames: " << frameCount << std::endl; // Cleanup MXL mxlReleaseFlowWriter(mxl_instance, video_writer); // Cleanup context sws_freeContext(sws_ctx); // Cleanup common av_frame_free(&frame); av_packet_free(&packet); avcodec_free_context(&codecContext); avformat_close_input(&formatContext); return 0; }