av_muxer.cpp 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. #include "av_muxer.h"
  2. #include <QDebug>
  3. bool AvMuxer::Open(std::string_view filePath, std::string_view format)
  4. {
  5. Close();
  6. _isOpenFile = false;
  7. _filePath = filePath;
  8. __CheckBool(avformat_alloc_output_context2(&_fmtCtx, nullptr, format.data(), _filePath.c_str()) >= 0);
  9. __CheckBool(_fmtCtx);
  10. return true;
  11. }
  12. bool AvMuxer::WriteHeader()
  13. {
  14. av_dump_format(_fmtCtx, 0, _filePath.data(), 1);
  15. // 打开输出文件
  16. if (!(_fmtCtx->oformat->flags & AVFMT_NOFILE)) {
  17. __CheckBool(avio_open(&_fmtCtx->pb, _filePath.c_str(), AVIO_FLAG_WRITE) >= 0);
  18. }
  19. // 写入文件头
  20. __CheckBool(avformat_write_header(_fmtCtx, nullptr) >= 0);
  21. // _fmtCtx->flags |= AVFMT_FLAG_NOBUFFER; // 无缓冲
  22. // _fmtCtx->flags |= AVFMT_FLAG_FLUSH_PACKETS; // 立即刷新数据包
  23. _isOpenFile = true;
  24. return true;
  25. }
  26. int AvMuxer::AddVideoStream(const Encoder<MediaType::VIDEO>::Param& param)
  27. {
  28. __Check(-1, _fmtCtx->oformat->video_codec != AV_CODEC_ID_NONE);
  29. Info info;
  30. info.pts = 0;
  31. info.fps = param.fps;
  32. auto encoder = new Encoder<MediaType::VIDEO>;
  33. __Check(-1, encoder->Open(param, _fmtCtx));
  34. info.type = MediaType::VIDEO;
  35. info.encoder = encoder;
  36. __Check(-1, _AddStream(info));
  37. _infos.back().stream->time_base = {1, info.fps};
  38. return info.streamIndex;
  39. }
  40. int AvMuxer::AddAudioStream(const Encoder<MediaType::AUDIO>::Param& param)
  41. {
  42. __Check(-1, _fmtCtx->oformat->audio_codec != AV_CODEC_ID_NONE);
  43. Info info;
  44. info.pts = 0;
  45. info.fps = AUDIO_SAMPLE_RATE;
  46. auto encoder = new Encoder<MediaType::AUDIO>;
  47. info.type = MediaType::AUDIO;
  48. info.encoder = encoder;
  49. __Check(-1, encoder->Open(param, _fmtCtx));
  50. __Check(-1, _AddStream(info));
  51. _infos.back().stream->time_base = {1, AUDIO_SAMPLE_RATE};
  52. return info.streamIndex;
  53. }
  54. bool AvMuxer::Write(AVFrame* frame, int streamIndex, bool isEnd)
  55. {
  56. std::lock_guard<std::mutex> lk(_mtx);
  57. __CheckBool(_infos.size() > streamIndex);
  58. auto&& info = _infos[streamIndex];
  59. if (info.isEnd) {
  60. return true;
  61. }
  62. if (isEnd) {
  63. info.isEnd = isEnd;
  64. frame = nullptr;
  65. }
  66. __CheckBool(info.encoder);
  67. // 只在有多个活跃流且音频流有数据时才做同步检查
  68. int activeStreamCount = 0;
  69. int audioStreamIdx = -1;
  70. for (int i = 0; i < _infos.size(); ++i) {
  71. if (!_infos[i].isEnd && _infos[i].pts > 0) {
  72. ++activeStreamCount;
  73. if (_infos[i].type == MediaType::AUDIO) audioStreamIdx = i;
  74. }
  75. }
  76. // 只在视频流超前音频流时丢帧,音频流超前时不阻塞视频帧写入
  77. if (activeStreamCount > 1 && audioStreamIdx != -1) {
  78. double curTime = double(info.pts) / info.fps;
  79. double audioTime = double(_infos[audioStreamIdx].pts) / _infos[audioStreamIdx].fps;
  80. if (info.type == MediaType::VIDEO && curTime - audioTime > 0.7) {
  81. // 视频流超前音频流太多才丢帧
  82. return false;
  83. }
  84. }
  85. info.isEncodeOverload = false;
  86. if (!info.encoder->PushFrame(frame, isEnd, info.pts)) {
  87. return false;
  88. }
  89. // 调试输出音视频流的pts
  90. // if (info.type == MediaType::AUDIO) {
  91. // qDebug() << "AUDIO PTS:" << info.pts;
  92. // } else if (info.type == MediaType::VIDEO) {
  93. // qDebug() << "VIDEO PTS:" << info.pts;
  94. // }
  95. info.pts += info.type == MediaType::AUDIO ? info.encoder->GetCtx()->frame_size : 1; // 更新 pts
  96. AVPacket* packet = nullptr;
  97. while ((packet = info.encoder->Encode())) {
  98. av_packet_rescale_ts(packet, info.encoder->GetCtx()->time_base, info.stream->time_base);
  99. packet->stream_index = info.stream->index;
  100. if (av_interleaved_write_frame(_fmtCtx, packet) < 0) {
  101. return false;
  102. }
  103. }
  104. info.encoder->AfterEncode();
  105. return true;
  106. }
  107. bool AvMuxer::_CheckTime(double time)
  108. {
  109. auto minTime = double(_infos.front().pts) / _infos.front().fps;
  110. for (int idx = 1; idx < _infos.size(); ++idx) {
  111. minTime = std::min(double(_infos[idx].pts) / _infos[idx].fps, minTime);
  112. }
  113. if (time - minTime > 0.3) { // 放宽到0.3秒
  114. qDebug() << "丢帧: 当前流时间:" << time << "最慢流时间:" << minTime << "差值:" << (time - minTime);
  115. return false;
  116. }
  117. return true;
  118. }
  119. void AvMuxer::Close()
  120. {
  121. if (_fmtCtx == nullptr) {
  122. return;
  123. }
  124. // 清空编码器缓存
  125. for (int index = 0; index < _infos.size(); ++index) {
  126. __DebugPrint("stream: %d, time:%f", index, double(_infos[index].pts) / _infos[index].fps);
  127. }
  128. if (_isOpenFile) {
  129. __CheckNo(av_write_trailer(_fmtCtx) >= 0);
  130. Free(_fmtCtx->pb, [this] { avio_closep(&_fmtCtx->pb); });
  131. }
  132. _isOpenFile = false;
  133. for (auto&& info : _infos) {
  134. info.encoder->Close();
  135. Free(info.encoder, [&info] {info.encoder->Close(); delete info.encoder; });
  136. }
  137. _infos.clear();
  138. Free(_fmtCtx, [this] { avformat_free_context(_fmtCtx); });
  139. }
  140. bool AvMuxer::_AddStream(Info& info)
  141. {
  142. __CheckBool(info.stream = avformat_new_stream(_fmtCtx, nullptr));
  143. info.stream->id = _fmtCtx->nb_streams - 1;
  144. __CheckBool(avcodec_parameters_from_context(info.stream->codecpar, info.encoder->GetCtx()) >= 0);
  145. info.streamIndex = _fmtCtx->nb_streams - 1;
  146. info.pts = 0;
  147. info.isEnd = false;
  148. _infos.push_back(info);
  149. return true;
  150. }
  151. AVCodecContext* AvMuxer::GetCodecCtx(int streamIndex)
  152. {
  153. __CheckNullptr(streamIndex >= 0 && _infos.size() > streamIndex);
  154. return _infos[streamIndex].encoder->GetCtx();
  155. }
  156. bool AvMuxer::IsEncodeOverload() const
  157. {
  158. for (auto&& info : _infos) {
  159. if (info.isEncodeOverload) {
  160. return true;
  161. }
  162. }
  163. return false;
  164. }