video_encoder.cpp 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  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. }
  67. return _usableEncoders;
  68. }
  69. void Encoder<MediaType::VIDEO>::_FindUsableEncoders()
  70. {
  71. // 尝试打开编码器看看编码器能不能用
  72. Param param;
  73. param.bitRate = 1000;
  74. param.fps = 30;
  75. param.width = 1920;
  76. param.height = 1080;
  77. Encoder encoder;
  78. AVFormatContext* fmtCtx = nullptr;
  79. __CheckNo(avformat_alloc_output_context2(&fmtCtx, nullptr, nullptr, "test.mp4") >= 0);
  80. for (const auto& name : _encoderNames) {
  81. if (strcmp(name, "libx264") == 0) { // 软件编码器必定支持
  82. _usableEncoders.push_back(name);
  83. continue;
  84. }
  85. param.name = name;
  86. if (encoder.Open(param, fmtCtx)) {
  87. _usableEncoders.push_back(name);
  88. }
  89. encoder.Close();
  90. }
  91. Free(fmtCtx, [&fmtCtx] { avformat_free_context(fmtCtx); });
  92. }
  93. bool Encoder<MediaType::VIDEO>::_Init(const Param& encodeParam, AVFormatContext* fmtCtx)
  94. {
  95. _isHardware = encodeParam.name != "libx264";
  96. AVHWDeviceType hwType;
  97. if (encodeParam.name == "libx264") {
  98. _pixFmt = AV_PIX_FMT_NV12;
  99. } else if (encodeParam.name == "h264_nvenc") {
  100. _pixFmt = AV_PIX_FMT_CUDA;
  101. hwType = AV_HWDEVICE_TYPE_CUDA;
  102. } else if (encodeParam.name == "h264_qsv") {
  103. _pixFmt = AV_PIX_FMT_QSV;
  104. hwType = AV_HWDEVICE_TYPE_QSV;
  105. } else if (encodeParam.name == "h264_amf") {
  106. _pixFmt = AV_PIX_FMT_VULKAN;
  107. hwType = AV_HWDEVICE_TYPE_VULKAN;
  108. }
  109. _isHardware = _pixFmt != AV_PIX_FMT_NV12;
  110. if (_isHardware
  111. && av_hwdevice_ctx_create(&_hwDeviceCtx, hwType, nullptr, nullptr, 0) < 0) { // 硬件解码
  112. __DebugPrint("av_hwdevice_ctx_create failed\n");
  113. return false;
  114. }
  115. __CheckBool(_codec = avcodec_find_encoder_by_name(encodeParam.name.c_str()));
  116. __CheckBool(_codecCtx = avcodec_alloc_context3(_codec));
  117. _codecCtx->bit_rate = encodeParam.bitRate;
  118. _codecCtx->width = encodeParam.width;
  119. _codecCtx->height = encodeParam.height;
  120. _codecCtx->time_base = {1, encodeParam.fps};
  121. _codecCtx->framerate = {encodeParam.fps, 1};
  122. // 影响缓冲区大小
  123. _codecCtx->gop_size = 5;
  124. _codecCtx->max_b_frames = 0;
  125. _codecCtx->pix_fmt = _pixFmt;
  126. /* Some formats want stream headers to be separate. */
  127. if (fmtCtx->oformat->flags & AVFMT_GLOBALHEADER) {
  128. _codecCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
  129. }
  130. if (!_isHardware) { // 软件编码设置为快,避免占用过高的 CPU ,反正硬盘不值钱
  131. // av_opt_set(_codecCtx->priv_data, "preset", "veryfast", 0);
  132. av_opt_set(_codecCtx->priv_data, "preset", "ultrafast", 0); // 使用最快的预设
  133. av_opt_set(_codecCtx->priv_data, "tune", "zerolatency", 0); // 零延迟调优
  134. } else {
  135. // 为硬件编码器添加低延迟设置
  136. av_opt_set(_codecCtx->priv_data, "preset", "llhp", 0); // 低延迟高性能
  137. av_opt_set(_codecCtx->priv_data, "zerolatency", "1", 0);
  138. }
  139. __CheckBool(!_isHardware || _SetHwFrameCtx());
  140. return true;
  141. }
  142. bool Encoder<MediaType::VIDEO>::_SetHwFrameCtx()
  143. {
  144. AVBufferRef* hwFramesRef;
  145. AVHWFramesContext* framesCtx = nullptr;
  146. __CheckBool(hwFramesRef = av_hwframe_ctx_alloc(_hwDeviceCtx));
  147. framesCtx = (AVHWFramesContext*) (hwFramesRef->data);
  148. framesCtx->format = _pixFmt;
  149. framesCtx->sw_format = AV_PIX_FMT_NV12;
  150. framesCtx->width = _codecCtx->width;
  151. framesCtx->height = _codecCtx->height;
  152. framesCtx->initial_pool_size = 20;
  153. if (av_hwframe_ctx_init(hwFramesRef) < 0) {
  154. __DebugPrint("av_hwframe_ctx_init failed\n");
  155. av_buffer_unref(&hwFramesRef);
  156. return false;
  157. }
  158. __CheckBool(_codecCtx->hw_frames_ctx = av_buffer_ref(hwFramesRef));
  159. av_buffer_unref(&hwFramesRef);
  160. return true;
  161. }
  162. bool Encoder<MediaType::VIDEO>::_Trans(AVFrame* frame)
  163. {
  164. std::lock_guard<std::mutex> lk(__mtx);
  165. if (!_isOpen) {
  166. return false;
  167. }
  168. if (frame->format == AV_PIX_FMT_NV12) {
  169. _bufferFrame = frame;
  170. } else {
  171. if (_converter == nullptr) {
  172. _converter = std::make_unique<FfmpegConverter>(AVPixelFormat(frame->format),
  173. AV_PIX_FMT_NV12);
  174. _converter->SetSize(frame->width, frame->height);
  175. }
  176. _bufferFrame = _converter->Trans(frame);
  177. }
  178. if (_isHardware) {
  179. _bufferFrame = _ToHardware();
  180. }
  181. __CheckBool(_bufferFrame);
  182. return true;
  183. }
  184. AVFrame* Encoder<MediaType::VIDEO>::_ToHardware()
  185. {
  186. if (_bufferFrame == nullptr) {
  187. return nullptr;
  188. }
  189. __CheckNullptr(_hwFrame = av_frame_alloc());
  190. __CheckNullptr(av_hwframe_get_buffer(_codecCtx->hw_frames_ctx, _hwFrame, 0) >= 0);
  191. __CheckNullptr(_hwFrame->hw_frames_ctx);
  192. __CheckNullptr(av_hwframe_transfer_data(_hwFrame, _bufferFrame, 0) >= 0);
  193. return _hwFrame;
  194. }