video_encoder.cpp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. #include "video_encoder.h"
  2. extern "C" {
  3. #include <libavutil/opt.h>
  4. }
  5. #include <QDebug>
  6. std::vector<std::string> Encoder<MediaType::VIDEO>::_usableEncoders;
  7. Encoder<MediaType::VIDEO>::Encoder() {}
  8. bool Encoder<MediaType::VIDEO>::Open(const Param& encodeParam, AVFormatContext* fmtCtx)
  9. {
  10. Close();
  11. _isOpen = false;
  12. qDebug() << "VideoEncoder::Open: Initializing video encoder with codec" << QString::fromStdString(encodeParam.name)
  13. << "resolution" << encodeParam.width << "x" << encodeParam.height
  14. << "fps" << encodeParam.fps << "bitrate" << encodeParam.bitRate;
  15. if (!_Init(encodeParam, fmtCtx)) {
  16. qDebug() << "VideoEncoder::Open failed: Encoder initialization failed";
  17. return false;
  18. }
  19. // 打开编码器
  20. int ret = avcodec_open2(_codecCtx, _codec, nullptr);
  21. if (ret < 0) {
  22. char errbuf[AV_ERROR_MAX_STRING_SIZE];
  23. av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);
  24. qDebug() << "VideoEncoder::Open failed: Cannot open codec. Error:" << errbuf << "(" << ret << ")";
  25. qDebug() << "Codec name:" << _codec->name << "ID:" << _codec->id;
  26. return false;
  27. }
  28. _isOpen = true;
  29. qDebug() << "VideoEncoder::Open: Video encoder opened successfully";
  30. return true;
  31. }
  32. bool Encoder<MediaType::VIDEO>::PushFrame(AVFrame* frame, bool isEnd, uint64_t pts)
  33. {
  34. if (!isEnd) {
  35. __CheckBool(_Trans(frame));
  36. frame = _bufferFrame;
  37. __CheckBool(frame);
  38. } else {
  39. frame = nullptr; // 直接刷新编码器缓存
  40. }
  41. if (frame != nullptr) {
  42. frame->pts = pts;
  43. }
  44. __CheckBool(avcodec_send_frame(_codecCtx, frame) >= 0);
  45. return true;
  46. }
  47. void Encoder<MediaType::VIDEO>::AfterEncode()
  48. {
  49. if (_isHardware) {
  50. Free(_hwFrame, [this] { av_frame_free(&_hwFrame); });
  51. }
  52. }
  53. void Encoder<MediaType::VIDEO>::Close()
  54. {
  55. if (_codecCtx != nullptr) {
  56. avcodec_free_context(&_codecCtx);
  57. }
  58. Free(_codecCtx, [this] { avcodec_free_context(&_codecCtx); });
  59. Free(_hwDeviceCtx, [this] { av_buffer_unref(&_hwDeviceCtx); });
  60. _converter = nullptr;
  61. }
  62. const std::vector<std::string>& Encoder<MediaType::VIDEO>::GetUsableEncoders()
  63. {
  64. if (_usableEncoders.empty()) {
  65. _FindUsableEncoders();
  66. // 如果仍然没有找到可用编码器,至少添加libx264作为后备
  67. if (_usableEncoders.empty()) {
  68. qDebug() << "VideoEncoder: No encoders found during testing, adding libx264 as fallback";
  69. _usableEncoders.push_back("libx264");
  70. }
  71. }
  72. return _usableEncoders;
  73. }
  74. void Encoder<MediaType::VIDEO>::_FindUsableEncoders()
  75. {
  76. qDebug() << "VideoEncoder: Finding usable encoders...";
  77. // 尝试打开编码器看看编码器能不能用
  78. Param param;
  79. param.bitRate = 1000;
  80. param.fps = 30;
  81. param.width = 1920;
  82. param.height = 1080;
  83. Encoder encoder;
  84. AVFormatContext* fmtCtx = nullptr;
  85. __CheckNo(avformat_alloc_output_context2(&fmtCtx, nullptr, nullptr, "test.mp4") >= 0);
  86. // 测试所有编码器,不做任何假设
  87. for (const auto& name : _encoderNames) {
  88. param.name = name;
  89. qDebug() << "VideoEncoder: Testing encoder:" << name;
  90. if (encoder.Open(param, fmtCtx)) {
  91. _usableEncoders.push_back(name);
  92. qDebug() << "VideoEncoder: Encoder" << name << "is usable";
  93. } else {
  94. qDebug() << "VideoEncoder: Encoder" << name << "failed to open";
  95. }
  96. encoder.Close();
  97. }
  98. qDebug() << "VideoEncoder: Found" << _usableEncoders.size() << "usable encoders";
  99. Free(fmtCtx, [&fmtCtx] { avformat_free_context(fmtCtx); });
  100. }
  101. bool Encoder<MediaType::VIDEO>::_Init(const Param& encodeParam, AVFormatContext* fmtCtx)
  102. {
  103. _isHardware = (encodeParam.name != "libx264" && encodeParam.name != "libx265" && encodeParam.name != "libaom-av1");
  104. AVHWDeviceType hwType;
  105. if (encodeParam.name == "libx264" || encodeParam.name == "libx265" || encodeParam.name == "libaom-av1") {
  106. _pixFmt = AV_PIX_FMT_YUV420P;
  107. } else if (encodeParam.name == "h264_nvenc") {
  108. _pixFmt = AV_PIX_FMT_CUDA;
  109. hwType = AV_HWDEVICE_TYPE_CUDA;
  110. } else if (encodeParam.name == "h264_qsv") {
  111. _pixFmt = AV_PIX_FMT_QSV;
  112. hwType = AV_HWDEVICE_TYPE_QSV;
  113. } else if (encodeParam.name == "h264_amf") {
  114. _pixFmt = AV_PIX_FMT_D3D11;
  115. hwType = AV_HWDEVICE_TYPE_D3D11VA;
  116. } else if (encodeParam.name == "h264_videotoolbox") {
  117. _pixFmt = AV_PIX_FMT_VIDEOTOOLBOX;
  118. hwType = AV_HWDEVICE_TYPE_VIDEOTOOLBOX;
  119. }
  120. _isHardware = (_pixFmt != AV_PIX_FMT_YUV420P && _pixFmt != AV_PIX_FMT_NV12);
  121. if (_isHardware
  122. && av_hwdevice_ctx_create(&_hwDeviceCtx, hwType, nullptr, nullptr, 0) < 0) { // 硬件解码
  123. __DebugPrint("av_hwdevice_ctx_create failed\n");
  124. return false;
  125. }
  126. __CheckBool(_codec = avcodec_find_encoder_by_name(encodeParam.name.c_str()));
  127. __CheckBool(_codecCtx = avcodec_alloc_context3(_codec));
  128. _codecCtx->bit_rate = encodeParam.bitRate;
  129. _codecCtx->width = encodeParam.width;
  130. _codecCtx->height = encodeParam.height;
  131. _codecCtx->time_base = {1, encodeParam.fps};
  132. _codecCtx->framerate = {encodeParam.fps, 1};
  133. // 影响缓冲区大小
  134. _codecCtx->gop_size = 5;
  135. _codecCtx->max_b_frames = 0;
  136. _codecCtx->pix_fmt = _pixFmt;
  137. /* Some formats want stream headers to be separate. */
  138. if (fmtCtx->oformat->flags & AVFMT_GLOBALHEADER) {
  139. _codecCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
  140. }
  141. if (!_isHardware) { // 软件编码设置为快,避免占用过高的 CPU
  142. if (encodeParam.name == "libx264") {
  143. av_opt_set(_codecCtx->priv_data, "preset", "ultrafast", 0); // 使用最快的预设
  144. av_opt_set(_codecCtx->priv_data, "tune", "zerolatency", 0); // 零延迟调优
  145. } else if (encodeParam.name == "libx265") {
  146. av_opt_set(_codecCtx->priv_data, "preset", "ultrafast", 0);
  147. av_opt_set(_codecCtx->priv_data, "tune", "zerolatency", 0);
  148. } else if (encodeParam.name == "libaom-av1") {
  149. av_opt_set(_codecCtx->priv_data, "cpu-used", "8", 0); // 最快速度
  150. av_opt_set(_codecCtx->priv_data, "rt", "1", 0); // 实时模式
  151. }
  152. } else {
  153. // 为不同硬件编码器设置特定参数
  154. if (encodeParam.name == "h264_nvenc") {
  155. av_opt_set(_codecCtx->priv_data, "preset", "p4", 0); // NVENC预设
  156. av_opt_set(_codecCtx->priv_data, "rc", "vbr", 0);
  157. } else if (encodeParam.name == "h264_qsv") {
  158. av_opt_set(_codecCtx->priv_data, "preset", "medium", 0); // QSV有效预设
  159. av_opt_set(_codecCtx->priv_data, "look_ahead", "0", 0);
  160. } else if (encodeParam.name == "h264_amf") {
  161. av_opt_set(_codecCtx->priv_data, "quality", "speed", 0); // AMF预设
  162. av_opt_set(_codecCtx->priv_data, "rc", "vbr_peak", 0);
  163. }
  164. }
  165. __CheckBool(!_isHardware || _SetHwFrameCtx());
  166. return true;
  167. }
  168. bool Encoder<MediaType::VIDEO>::_SetHwFrameCtx()
  169. {
  170. AVBufferRef* hwFramesRef;
  171. AVHWFramesContext* framesCtx = nullptr;
  172. __CheckBool(hwFramesRef = av_hwframe_ctx_alloc(_hwDeviceCtx));
  173. framesCtx = (AVHWFramesContext*) (hwFramesRef->data);
  174. framesCtx->format = _pixFmt;
  175. framesCtx->sw_format = AV_PIX_FMT_NV12;
  176. framesCtx->width = _codecCtx->width;
  177. framesCtx->height = _codecCtx->height;
  178. framesCtx->initial_pool_size = 20;
  179. if (av_hwframe_ctx_init(hwFramesRef) < 0) {
  180. __DebugPrint("av_hwframe_ctx_init failed\n");
  181. av_buffer_unref(&hwFramesRef);
  182. return false;
  183. }
  184. __CheckBool(_codecCtx->hw_frames_ctx = av_buffer_ref(hwFramesRef));
  185. av_buffer_unref(&hwFramesRef);
  186. return true;
  187. }
  188. bool Encoder<MediaType::VIDEO>::_Trans(AVFrame* frame)
  189. {
  190. std::lock_guard<std::mutex> lk(__mtx);
  191. if (!_isOpen) {
  192. return false;
  193. }
  194. if (frame->format == AV_PIX_FMT_NV12) {
  195. _bufferFrame = frame;
  196. } else {
  197. if (_converter == nullptr) {
  198. _converter = std::make_unique<FfmpegConverter>(AVPixelFormat(frame->format),
  199. AV_PIX_FMT_NV12);
  200. _converter->SetSize(frame->width, frame->height);
  201. }
  202. _bufferFrame = _converter->Trans(frame);
  203. }
  204. if (_isHardware) {
  205. _bufferFrame = _ToHardware();
  206. }
  207. __CheckBool(_bufferFrame);
  208. return true;
  209. }
  210. AVFrame* Encoder<MediaType::VIDEO>::_ToHardware()
  211. {
  212. if (_bufferFrame == nullptr) {
  213. return nullptr;
  214. }
  215. __CheckNullptr(_hwFrame = av_frame_alloc());
  216. __CheckNullptr(av_hwframe_get_buffer(_codecCtx->hw_frames_ctx, _hwFrame, 0) >= 0);
  217. __CheckNullptr(_hwFrame->hw_frames_ctx);
  218. __CheckNullptr(av_hwframe_transfer_data(_hwFrame, _bufferFrame, 0) >= 0);
  219. return _hwFrame;
  220. }