video_encoder.cpp 9.9 KB

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