recording.cpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. #if DEMOMODE
  2. // this file is horrible, but doesn't need to be anything good as it's only used internally
  3. #include "recording.h"
  4. #include <cstdio>
  5. #include <cstdlib>
  6. #include <cstring>
  7. #include <ctime>
  8. #include <iostream>
  9. #include <thread>
  10. #include <vector>
  11. extern "C" {
  12. #include <libavcodec/avcodec.h>
  13. #include <libavformat/avformat.h>
  14. #include <libswscale/swscale.h>
  15. #include <libavutil/imgutils.h>
  16. }
  17. const int FPS = 30;
  18. const int FRAME_COUNT = FPS * 5;
  19. int WIDTH = 0;
  20. int HEIGHT = 0;
  21. int SOURCE_WIDTH = 0;
  22. int SOURCE_HEIGHT = 0;
  23. int64_t frame_count = 0;
  24. // Global variables to hold encoder context and output stream
  25. const AVCodec *video_codec = nullptr;
  26. AVCodecContext *video_codec_context = nullptr;
  27. AVFormatContext *format_context = nullptr;
  28. AVStream *video_stream = nullptr;
  29. SwsContext *sws_context = nullptr;
  30. AVFrame *video_frame = nullptr;
  31. AVFrame* rgb_frame = nullptr;
  32. int init_encoder(const char *output_file, int sourceWidth, int sourceHeight) {
  33. float factor = 512.0f / (float) sourceWidth;
  34. SOURCE_WIDTH = sourceWidth;
  35. SOURCE_HEIGHT = sourceHeight;
  36. WIDTH = (float) sourceWidth * factor;
  37. HEIGHT = (float) sourceHeight * factor;
  38. avformat_network_init();
  39. // Initialize the output format context
  40. if (avformat_alloc_output_context2(&format_context, nullptr, "webm", output_file) < 0) {
  41. std::cerr << "Error initializing format context" << std::endl;
  42. return -1;
  43. }
  44. // Video codec: VP8
  45. video_codec = avcodec_find_encoder(AV_CODEC_ID_VP9);
  46. if (!video_codec) {
  47. std::cerr << "VP8 codec not found!" << std::endl;
  48. return -1;
  49. }
  50. video_codec_context = avcodec_alloc_context3(video_codec);
  51. if (!video_codec_context) {
  52. std::cerr << "Error allocating video codec context" << std::endl;
  53. return -1;
  54. }
  55. video_codec_context->bit_rate = 4000000;
  56. video_codec_context->width = WIDTH;
  57. video_codec_context->height = HEIGHT;
  58. video_codec_context->pix_fmt = AV_PIX_FMT_YUV420P;
  59. video_codec_context->time_base = (AVRational){1, FPS};
  60. video_codec_context->framerate = (AVRational){FPS, 1};
  61. video_codec_context->gop_size = 12;
  62. video_codec_context->max_b_frames = 1;
  63. video_codec_context->qmin = 10;
  64. video_codec_context->qmax = 40;
  65. if (avcodec_open2(video_codec_context, video_codec, nullptr) < 0) {
  66. std::cerr << "Error opening VP8 codec" << std::endl;
  67. return -1;
  68. }
  69. // Create the video stream in the format context
  70. video_stream = avformat_new_stream(format_context, video_codec);
  71. if (!video_stream) {
  72. std::cerr << "Error creating video stream" << std::endl;
  73. return -1;
  74. }
  75. // Copy codec parameters from the codec context to the stream
  76. if (avcodec_parameters_from_context(video_stream->codecpar, video_codec_context) < 0) {
  77. std::cerr << "Error copying codec parameters to stream" << std::endl;
  78. return -1;
  79. }
  80. video_stream->time_base = video_codec_context->time_base;
  81. // Open output file for writing
  82. if (avio_open(&format_context->pb, output_file, AVIO_FLAG_WRITE) < 0) {
  83. std::cerr << "Error opening output file" << std::endl;
  84. return -1;
  85. }
  86. // Write file header
  87. if (avformat_write_header(format_context, nullptr) < 0) {
  88. std::cerr << "Error writing file header" << std::endl;
  89. return -1;
  90. }
  91. // Allocate video frame
  92. video_frame = av_frame_alloc();
  93. video_frame->format = AV_PIX_FMT_YUV420P;
  94. video_frame->width = WIDTH;
  95. video_frame->height = HEIGHT;
  96. av_frame_get_buffer(video_frame, 0);
  97. rgb_frame = av_frame_alloc();
  98. rgb_frame->format = AV_PIX_FMT_RGB24;
  99. rgb_frame->width = SOURCE_WIDTH;
  100. rgb_frame->height = SOURCE_HEIGHT;
  101. // Set up YUV conversion context (RGB to YUV)
  102. sws_context = sws_getContext(SOURCE_WIDTH, SOURCE_HEIGHT, AV_PIX_FMT_RGB24,
  103. WIDTH, HEIGHT, AV_PIX_FMT_YUV420P,
  104. SWS_BICUBIC, nullptr, nullptr, nullptr);
  105. return 0;
  106. }
  107. int write_video_frame(const uint8_t *rgb_data) {
  108. av_image_fill_arrays(rgb_frame->data, rgb_frame->linesize, rgb_data, AV_PIX_FMT_RGB24, SOURCE_WIDTH, SOURCE_HEIGHT, 1);
  109. sws_scale(sws_context, rgb_frame->data, rgb_frame->linesize, 0, SOURCE_HEIGHT, video_frame->data, video_frame->linesize);
  110. // Send the frame to the encoder
  111. int ret = avcodec_send_frame(video_codec_context, video_frame);
  112. if (ret < 0) {
  113. std::cerr << "Error sending video frame: " << ret << std::endl;
  114. return -1;
  115. }
  116. AVPacket packet;
  117. av_init_packet(&packet);
  118. packet.data = nullptr;
  119. packet.size = 0;
  120. // Receive the encoded packet from the encoder
  121. ret = avcodec_receive_packet(video_codec_context, &packet);
  122. if (ret < 0) {
  123. std::cerr << "Error receiving video packet: " << ret << std::endl;
  124. return -1;
  125. }
  126. packet.stream_index = video_stream->index;
  127. // Set the PTS and DTS values
  128. packet.pts = av_rescale_q(frame_count, video_codec_context->time_base, video_stream->time_base);
  129. packet.dts = packet.pts; // For simplicity, you can set DTS equal to PTS for now
  130. packet.duration = av_rescale_q(1, video_codec_context->time_base, video_stream->time_base);
  131. // Increment frame counter
  132. frame_count++;
  133. // Write the encoded video packet to the file
  134. ret = av_interleaved_write_frame(format_context, &packet);
  135. if (ret < 0) {
  136. std::cerr << "Error writing video packet: " << ret << std::endl;
  137. return -1;
  138. }
  139. // Ensure that the packet is freed
  140. av_packet_unref(&packet);
  141. return 0;
  142. }
  143. int close_encoder() {
  144. // Write any remaining frames (flush encoder)
  145. avcodec_flush_buffers(video_codec_context);
  146. // Write the trailer
  147. av_write_trailer(format_context);
  148. // Clean up
  149. avcodec_free_context(&video_codec_context);
  150. avformat_free_context(format_context);
  151. av_frame_free(&video_frame);
  152. sws_freeContext(sws_context);
  153. return 0;
  154. }
  155. #endif /* DEMOMODE */