#if DEMOMODE // this file is horrible, but doesn't need to be anything good as it's only used internally #include "recording.h" #include #include #include #include #include #include #include extern "C" { #include #include #include #include } const int FPS = 30; const int FRAME_COUNT = FPS * 5; int WIDTH = 0; int HEIGHT = 0; int SOURCE_WIDTH = 0; int SOURCE_HEIGHT = 0; int64_t frame_count = 0; // Global variables to hold encoder context and output stream const AVCodec *video_codec = nullptr; AVCodecContext *video_codec_context = nullptr; AVFormatContext *format_context = nullptr; AVStream *video_stream = nullptr; SwsContext *sws_context = nullptr; AVFrame *video_frame = nullptr; AVFrame* rgb_frame = nullptr; int init_encoder(const char *output_file, int sourceWidth, int sourceHeight) { float factor = 512.0f / (float) sourceWidth; SOURCE_WIDTH = sourceWidth; SOURCE_HEIGHT = sourceHeight; WIDTH = (float) sourceWidth * factor; HEIGHT = (float) sourceHeight * factor; avformat_network_init(); // Initialize the output format context if (avformat_alloc_output_context2(&format_context, nullptr, "webm", output_file) < 0) { std::cerr << "Error initializing format context" << std::endl; return -1; } // Video codec: VP8 video_codec = avcodec_find_encoder(AV_CODEC_ID_VP9); if (!video_codec) { std::cerr << "VP8 codec not found!" << std::endl; return -1; } video_codec_context = avcodec_alloc_context3(video_codec); if (!video_codec_context) { std::cerr << "Error allocating video codec context" << std::endl; return -1; } video_codec_context->bit_rate = 4000000; video_codec_context->width = WIDTH; video_codec_context->height = HEIGHT; video_codec_context->pix_fmt = AV_PIX_FMT_YUV420P; video_codec_context->time_base = (AVRational){1, FPS}; video_codec_context->framerate = (AVRational){FPS, 1}; video_codec_context->gop_size = 12; video_codec_context->max_b_frames = 1; video_codec_context->qmin = 10; video_codec_context->qmax = 40; if (avcodec_open2(video_codec_context, video_codec, nullptr) < 0) { std::cerr << "Error opening VP8 codec" << std::endl; return -1; } // Create the video stream in the format context video_stream = avformat_new_stream(format_context, video_codec); if (!video_stream) { std::cerr << "Error creating video stream" << std::endl; return -1; } // Copy codec parameters from the codec context to the stream if (avcodec_parameters_from_context(video_stream->codecpar, video_codec_context) < 0) { std::cerr << "Error copying codec parameters to stream" << std::endl; return -1; } video_stream->time_base = video_codec_context->time_base; // Open output file for writing if (avio_open(&format_context->pb, output_file, AVIO_FLAG_WRITE) < 0) { std::cerr << "Error opening output file" << std::endl; return -1; } // Write file header if (avformat_write_header(format_context, nullptr) < 0) { std::cerr << "Error writing file header" << std::endl; return -1; } // Allocate video frame video_frame = av_frame_alloc(); video_frame->format = AV_PIX_FMT_YUV420P; video_frame->width = WIDTH; video_frame->height = HEIGHT; av_frame_get_buffer(video_frame, 0); rgb_frame = av_frame_alloc(); rgb_frame->format = AV_PIX_FMT_RGB24; rgb_frame->width = SOURCE_WIDTH; rgb_frame->height = SOURCE_HEIGHT; // Set up YUV conversion context (RGB to YUV) sws_context = sws_getContext(SOURCE_WIDTH, SOURCE_HEIGHT, AV_PIX_FMT_RGB24, WIDTH, HEIGHT, AV_PIX_FMT_YUV420P, SWS_BICUBIC, nullptr, nullptr, nullptr); return 0; } int write_video_frame(const uint8_t *rgb_data) { av_image_fill_arrays(rgb_frame->data, rgb_frame->linesize, rgb_data, AV_PIX_FMT_RGB24, SOURCE_WIDTH, SOURCE_HEIGHT, 1); sws_scale(sws_context, rgb_frame->data, rgb_frame->linesize, 0, SOURCE_HEIGHT, video_frame->data, video_frame->linesize); // Send the frame to the encoder int ret = avcodec_send_frame(video_codec_context, video_frame); if (ret < 0) { std::cerr << "Error sending video frame: " << ret << std::endl; return -1; } AVPacket packet; av_init_packet(&packet); packet.data = nullptr; packet.size = 0; // Receive the encoded packet from the encoder ret = avcodec_receive_packet(video_codec_context, &packet); if (ret < 0) { std::cerr << "Error receiving video packet: " << ret << std::endl; return -1; } packet.stream_index = video_stream->index; // Set the PTS and DTS values packet.pts = av_rescale_q(frame_count, video_codec_context->time_base, video_stream->time_base); packet.dts = packet.pts; // For simplicity, you can set DTS equal to PTS for now packet.duration = av_rescale_q(1, video_codec_context->time_base, video_stream->time_base); // Increment frame counter frame_count++; // Write the encoded video packet to the file ret = av_interleaved_write_frame(format_context, &packet); if (ret < 0) { std::cerr << "Error writing video packet: " << ret << std::endl; return -1; } // Ensure that the packet is freed av_packet_unref(&packet); return 0; } int close_encoder() { // Write any remaining frames (flush encoder) avcodec_flush_buffers(video_codec_context); // Write the trailer av_write_trailer(format_context); // Clean up avcodec_free_context(&video_codec_context); avformat_free_context(format_context); av_frame_free(&video_frame); sws_freeContext(sws_context); return 0; } #endif /* DEMOMODE */