av_muxer.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. #include "av_muxer.h"
  2. #include <QDebug>
  3. #include <chrono>
  4. #include <errno.h>
  5. bool AvMuxer::Open(std::string_view filePath, std::string_view format)
  6. {
  7. Close();
  8. _isOpenFile = false;
  9. _filePath = filePath;
  10. qDebug() << "AvMuxer::Open: Opening file" << QString::fromStdString(_filePath) << "with format" << QString::fromStdString(std::string(format));
  11. int ret = avformat_alloc_output_context2(&_fmtCtx, nullptr, format.data(), _filePath.c_str());
  12. if (ret < 0) {
  13. char errbuf[AV_ERROR_MAX_STRING_SIZE];
  14. av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);
  15. qDebug() << "AvMuxer::Open failed: Cannot allocate output context. Error:" << errbuf << "(" << ret << ")";
  16. return false;
  17. }
  18. if (!_fmtCtx) {
  19. qDebug() << "AvMuxer::Open failed: Format context is null after allocation";
  20. return false;
  21. }
  22. qDebug() << "AvMuxer::Open: Format context created successfully";
  23. qDebug() << "Output format:" << _fmtCtx->oformat->name << "long name:" << _fmtCtx->oformat->long_name;
  24. qDebug() << "Video codec:" << avcodec_get_name(_fmtCtx->oformat->video_codec);
  25. qDebug() << "Audio codec:" << avcodec_get_name(_fmtCtx->oformat->audio_codec);
  26. return true;
  27. }
  28. bool AvMuxer::WriteHeader()
  29. {
  30. // av_dump_format(_fmtCtx, 0, _filePath.data(), 1);
  31. // 检查格式上下文是否有效
  32. if (!_fmtCtx) {
  33. qDebug() << "WriteHeader failed: Format context is null";
  34. return false;
  35. }
  36. // 检查输出格式是否有效
  37. if (!_fmtCtx->oformat) {
  38. qDebug() << "WriteHeader failed: Output format is null";
  39. return false;
  40. }
  41. qDebug() << "WriteHeader: Opening file" << QString::fromStdString(_filePath) << "with format" << _fmtCtx->oformat->name;
  42. // 检查是否为RTMP流
  43. bool isRtmpStream = _filePath.find("rtmp://") == 0;
  44. if (isRtmpStream) {
  45. qDebug() << "WriteHeader: Detected RTMP stream, setting network options";
  46. // 解析RTMP URL获取服务器信息
  47. std::string rtmpUrl = _filePath;
  48. std::string serverInfo = "Unknown";
  49. size_t protocolEnd = rtmpUrl.find("://");
  50. if (protocolEnd != std::string::npos) {
  51. size_t serverStart = protocolEnd + 3;
  52. size_t serverEnd = rtmpUrl.find("/", serverStart);
  53. if (serverEnd != std::string::npos) {
  54. serverInfo = rtmpUrl.substr(serverStart, serverEnd - serverStart);
  55. }
  56. }
  57. qDebug() << "WriteHeader: RTMP server:" << QString::fromStdString(serverInfo);
  58. // 设置RTMP连接选项
  59. AVDictionary* options = nullptr;
  60. av_dict_set(&options, "rtmp_live", "1", 0); // 设置为直播模式
  61. av_dict_set(&options, "rtmp_buffer", "1000", 0); // 设置缓冲区大小
  62. av_dict_set(&options, "timeout", "3000000", 0); // 设置连接超时(3秒,单位微秒)
  63. av_dict_set(&options, "rw_timeout", "3000000", 0); // 设置读写超时(3秒)
  64. av_dict_set(&options, "stimeout", "3000000", 0); // 设置socket超时(3秒)
  65. av_dict_set(&options, "rtmp_conn", "S:publish", 0); // 设置发布模式
  66. av_dict_set(&options, "rtmp_tcurl", _filePath.c_str(), 0); // 设置RTMP URL
  67. av_dict_set(&options, "rtmp_flashver", "FMLE/3.0 (compatible; FMSc/1.0)", 0); // 设置Flash版本
  68. qDebug() << "WriteHeader: RTMP options set - live mode, 1000ms buffer, 3s timeout";
  69. qDebug() << "WriteHeader: Multiple timeout mechanisms enabled (timeout, rw_timeout, stimeout)";
  70. // 打开RTMP流
  71. if (!(_fmtCtx->oformat->flags & AVFMT_NOFILE)) {
  72. qDebug() << "WriteHeader: Attempting RTMP connection with enhanced timeout protection...";
  73. // 记录开始时间用于额外的超时检查
  74. auto startTime = std::chrono::steady_clock::now();
  75. int ret = avio_open(&_fmtCtx->pb, _filePath.c_str(), AVIO_FLAG_WRITE);
  76. // 检查连接时间
  77. auto endTime = std::chrono::steady_clock::now();
  78. auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime);
  79. qDebug() << "WriteHeader: RTMP connection attempt took" << duration.count() << "ms";
  80. if (ret < 0) {
  81. char errbuf[AV_ERROR_MAX_STRING_SIZE];
  82. av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);
  83. qDebug() << "WriteHeader failed: Cannot connect to RTMP server" << QString::fromStdString(_filePath);
  84. qDebug() << "Error:" << errbuf << "(" << ret << ")";
  85. qDebug() << "RTMP server:" << QString::fromStdString(serverInfo);
  86. qDebug() << "Connection duration:" << duration.count() << "ms";
  87. // 检查是否为超时相关问题
  88. if (duration.count() >= 2900) { // 接近3秒超时
  89. qDebug() << "WARNING: Connection attempt took nearly full timeout period!";
  90. qDebug() << "This suggests network connectivity issues or server overload.";
  91. }
  92. // 针对特定错误码提供详细解释
  93. if (ret == -10049) {
  94. qDebug() << "Error Analysis: WSAEADDRNOTAVAIL - Address not available";
  95. qDebug() << "This error typically means:";
  96. qDebug() << "- The RTMP server is not running or not listening on port 1935";
  97. qDebug() << "- The IP address 192.168.3.76 is not reachable from this machine";
  98. qDebug() << "- Network routing issues or firewall blocking";
  99. } else if (ret == -111) {
  100. qDebug() << "Error Analysis: Connection refused";
  101. qDebug() << "The server actively refused the connection";
  102. } else if (ret == -110 || ret == -ETIMEDOUT) {
  103. qDebug() << "Error Analysis: Connection timeout";
  104. qDebug() << "The server did not respond within the timeout period";
  105. qDebug() << "Consider increasing timeout or checking network latency";
  106. } else if (ret == -ECONNRESET) {
  107. qDebug() << "Error Analysis: Connection reset by peer";
  108. qDebug() << "The server closed the connection unexpectedly";
  109. }
  110. qDebug() << "Enhanced Troubleshooting steps:";
  111. qDebug() << "1. Quick network test: ping -n 1 -w 1000" << QString::fromStdString(serverInfo.substr(0, serverInfo.find(':')));
  112. qDebug() << "2. Port connectivity: telnet" << QString::fromStdString(serverInfo) << "(should connect immediately)";
  113. qDebug() << "3. Verify RTMP server status and logs on" << QString::fromStdString(serverInfo);
  114. qDebug() << "4. Check if server accepts RTMP publish connections";
  115. qDebug() << "5. Test with reduced timeout: try 1-2 second timeout first";
  116. qDebug() << "6. Verify stream key and application name in URL";
  117. qDebug() << "7. Check server-side connection limits and authentication";
  118. qDebug() << "8. Test with FFmpeg: ffmpeg -re -f lavfi -i testsrc=duration=10:size=320x240:rate=30 -c:v libx264 -f flv" << QString::fromStdString(_filePath);
  119. av_dict_free(&options);
  120. return false;
  121. }
  122. av_dict_free(&options);
  123. qDebug() << "WriteHeader: RTMP connection established successfully to" << QString::fromStdString(serverInfo);
  124. }
  125. } else {
  126. // 打开本地文件
  127. if (!(_fmtCtx->oformat->flags & AVFMT_NOFILE)) {
  128. int ret = avio_open(&_fmtCtx->pb, _filePath.c_str(), AVIO_FLAG_WRITE);
  129. if (ret < 0) {
  130. char errbuf[AV_ERROR_MAX_STRING_SIZE];
  131. av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);
  132. qDebug() << "WriteHeader failed: Cannot open output file" << QString::fromStdString(_filePath)
  133. << "Error:" << errbuf << "(" << ret << ")";
  134. return false;
  135. }
  136. qDebug() << "WriteHeader: Local file opened successfully";
  137. }
  138. }
  139. // 写入文件头
  140. int ret = avformat_write_header(_fmtCtx, nullptr);
  141. if (ret < 0) {
  142. char errbuf[AV_ERROR_MAX_STRING_SIZE];
  143. av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);
  144. qDebug() << "WriteHeader failed: Cannot write header. Error:" << errbuf << "(" << ret << ")";
  145. // 输出流信息用于调试
  146. qDebug() << "Format context info:";
  147. qDebug() << " - Number of streams:" << _fmtCtx->nb_streams;
  148. for (unsigned int i = 0; i < _fmtCtx->nb_streams; i++) {
  149. AVStream* stream = _fmtCtx->streams[i];
  150. qDebug() << " - Stream" << i << "codec:" << avcodec_get_name(stream->codecpar->codec_id)
  151. << "type:" << av_get_media_type_string(stream->codecpar->codec_type);
  152. }
  153. return false;
  154. }
  155. qDebug() << "WriteHeader: Header written successfully";
  156. // _fmtCtx->flags |= AVFMT_FLAG_NOBUFFER; // 无缓冲
  157. // _fmtCtx->flags |= AVFMT_FLAG_FLUSH_PACKETS; // 立即刷新数据包
  158. _isOpenFile = true;
  159. return true;
  160. }
  161. int AvMuxer::AddVideoStream(const Encoder<MediaType::VIDEO>::Param& param)
  162. {
  163. if (_fmtCtx->oformat->video_codec == AV_CODEC_ID_NONE) {
  164. qDebug() << "AddVideoStream failed: Output format does not support video";
  165. return -1;
  166. }
  167. qDebug() << "AddVideoStream: Creating video encoder with codec" << avcodec_get_name(_fmtCtx->oformat->video_codec)
  168. << "resolution" << param.width << "x" << param.height << "fps" << param.fps;
  169. Info info;
  170. info.pts = 0;
  171. info.fps = param.fps;
  172. // 获取可用编码器列表
  173. const auto& usableEncoders = Encoder<MediaType::VIDEO>::GetUsableEncoders();
  174. if (usableEncoders.empty()) {
  175. qDebug() << "AddVideoStream failed: No usable video encoders found";
  176. return -1;
  177. }
  178. // 尝试使用指定的编码器,如果失败则自动回退
  179. auto encoder = new Encoder<MediaType::VIDEO>;
  180. auto modifiableParam = param; // 创建可修改的副本
  181. // 首先尝试用户指定的编码器
  182. bool encoderOpened = false;
  183. std::string usedEncoder = modifiableParam.name;
  184. if (!modifiableParam.name.empty()) {
  185. qDebug() << "AddVideoStream: Trying user specified encoder:" << QString::fromStdString(modifiableParam.name);
  186. if (encoder->Open(modifiableParam, _fmtCtx)) {
  187. encoderOpened = true;
  188. qDebug() << "AddVideoStream: Successfully opened user specified encoder:" << QString::fromStdString(modifiableParam.name);
  189. } else {
  190. qDebug() << "AddVideoStream: User specified encoder failed:" << QString::fromStdString(modifiableParam.name);
  191. }
  192. }
  193. // 如果指定编码器失败,尝试可用编码器列表
  194. if (!encoderOpened) {
  195. qDebug() << "AddVideoStream: Trying fallback encoders...";
  196. for (const auto& encoderName : usableEncoders) {
  197. if (encoderName == modifiableParam.name) {
  198. continue; // 跳过已经尝试过的编码器
  199. }
  200. modifiableParam.name = encoderName;
  201. qDebug() << "AddVideoStream: Trying fallback encoder:" << QString::fromStdString(encoderName);
  202. if (encoder->Open(modifiableParam, _fmtCtx)) {
  203. encoderOpened = true;
  204. usedEncoder = encoderName;
  205. qDebug() << "AddVideoStream: Successfully opened fallback encoder:" << QString::fromStdString(encoderName);
  206. break;
  207. } else {
  208. qDebug() << "AddVideoStream: Fallback encoder failed:" << QString::fromStdString(encoderName);
  209. }
  210. }
  211. }
  212. if (!encoderOpened) {
  213. qDebug() << "AddVideoStream failed: All video encoders failed to open";
  214. delete encoder;
  215. return -1;
  216. }
  217. info.type = MediaType::VIDEO;
  218. info.encoder = encoder;
  219. if (!_AddStream(info)) {
  220. qDebug() << "AddVideoStream failed: Cannot add video stream to format context";
  221. delete encoder;
  222. return -1;
  223. }
  224. _infos.back().stream->time_base = {1, info.fps};
  225. qDebug() << "AddVideoStream: Video stream added successfully with encoder:" << QString::fromStdString(usedEncoder) << "index:" << info.streamIndex;
  226. return info.streamIndex;
  227. }
  228. int AvMuxer::AddAudioStream(const Encoder<MediaType::AUDIO>::Param& param)
  229. {
  230. if (_fmtCtx->oformat->audio_codec == AV_CODEC_ID_NONE) {
  231. qDebug() << "AddAudioStream failed: Output format does not support audio";
  232. return -1;
  233. }
  234. qDebug() << "AddAudioStream: Creating audio encoder with codec"
  235. << avcodec_get_name(_fmtCtx->oformat->audio_codec);
  236. // << "sample rate" << param.sampleRate << "channels" << param.channels;
  237. Info info;
  238. info.pts = 0;
  239. info.fps = AUDIO_SAMPLE_RATE;
  240. auto encoder = new Encoder<MediaType::AUDIO>;
  241. info.type = MediaType::AUDIO;
  242. info.encoder = encoder;
  243. if (!encoder->Open(param, _fmtCtx)) {
  244. qDebug() << "AddAudioStream failed: Cannot open audio encoder";
  245. delete encoder;
  246. return -1;
  247. }
  248. if (!_AddStream(info)) {
  249. qDebug() << "AddAudioStream failed: Cannot add audio stream to format context";
  250. delete encoder;
  251. return -1;
  252. }
  253. _infos.back().stream->time_base = {1, AUDIO_SAMPLE_RATE};
  254. qDebug() << "AddAudioStream: Audio stream added successfully, index:" << info.streamIndex;
  255. return info.streamIndex;
  256. }
  257. bool AvMuxer::Write(AVFrame* frame, int streamIndex, bool isEnd)
  258. {
  259. std::lock_guard<std::mutex> lk(_mtx);
  260. __CheckBool(_infos.size() > streamIndex);
  261. auto&& info = _infos[streamIndex];
  262. if (info.isEnd) {
  263. return true;
  264. }
  265. if (isEnd) {
  266. info.isEnd = isEnd;
  267. frame = nullptr;
  268. }
  269. __CheckBool(info.encoder);
  270. // 只在有多个活跃流且音频流有数据时才做同步检查
  271. int activeStreamCount = 0;
  272. int audioStreamIdx = -1;
  273. for (int i = 0; i < _infos.size(); ++i) {
  274. if (!_infos[i].isEnd && _infos[i].pts > 0) {
  275. ++activeStreamCount;
  276. if (_infos[i].type == MediaType::AUDIO) audioStreamIdx = i;
  277. }
  278. }
  279. // 只在视频流超前音频流时丢帧,音频流超前时不阻塞视频帧写入
  280. if (activeStreamCount > 1 && audioStreamIdx != -1) {
  281. double curTime = double(info.pts) / info.fps;
  282. double audioTime = double(_infos[audioStreamIdx].pts) / _infos[audioStreamIdx].fps;
  283. if (info.type == MediaType::VIDEO && curTime - audioTime > 0.7) {
  284. // 视频流超前音频流太多才丢帧
  285. return false;
  286. }
  287. }
  288. info.isEncodeOverload = false;
  289. if (!info.encoder->PushFrame(frame, isEnd, info.pts)) {
  290. return false;
  291. }
  292. // 调试输出音视频流的pts
  293. // if (info.type == MediaType::AUDIO) {
  294. // qDebug() << "AUDIO PTS:" << info.pts;
  295. // } else if (info.type == MediaType::VIDEO) {
  296. // qDebug() << "VIDEO PTS:" << info.pts;
  297. // }
  298. info.pts += info.type == MediaType::AUDIO ? info.encoder->GetCtx()->frame_size : 1; // 更新 pts
  299. AVPacket* packet = nullptr;
  300. while ((packet = info.encoder->Encode())) {
  301. av_packet_rescale_ts(packet, info.encoder->GetCtx()->time_base, info.stream->time_base);
  302. packet->stream_index = info.stream->index;
  303. if (av_interleaved_write_frame(_fmtCtx, packet) < 0) {
  304. return false;
  305. }
  306. }
  307. info.encoder->AfterEncode();
  308. return true;
  309. }
  310. bool AvMuxer::_CheckTime(double time)
  311. {
  312. auto minTime = double(_infos.front().pts) / _infos.front().fps;
  313. for (int idx = 1; idx < _infos.size(); ++idx) {
  314. minTime = std::min(double(_infos[idx].pts) / _infos[idx].fps, minTime);
  315. }
  316. if (time - minTime > 0.3) { // 放宽到0.3秒
  317. qDebug() << "丢帧: 当前流时间:" << time << "最慢流时间:" << minTime << "差值:" << (time - minTime);
  318. return false;
  319. }
  320. return true;
  321. }
  322. void AvMuxer::Close()
  323. {
  324. if (_fmtCtx == nullptr) {
  325. return;
  326. }
  327. // 清空编码器缓存
  328. for (int index = 0; index < _infos.size(); ++index) {
  329. __DebugPrint("stream: %d, time:%f", index, double(_infos[index].pts) / _infos[index].fps);
  330. }
  331. if (_isOpenFile) {
  332. __CheckNo(av_write_trailer(_fmtCtx) >= 0);
  333. Free(_fmtCtx->pb, [this] { avio_closep(&_fmtCtx->pb); });
  334. }
  335. _isOpenFile = false;
  336. for (auto&& info : _infos) {
  337. info.encoder->Close();
  338. Free(info.encoder, [&info] {info.encoder->Close(); delete info.encoder; });
  339. }
  340. _infos.clear();
  341. Free(_fmtCtx, [this] { avformat_free_context(_fmtCtx); });
  342. }
  343. bool AvMuxer::_AddStream(Info& info)
  344. {
  345. info.stream = avformat_new_stream(_fmtCtx, nullptr);
  346. if (!info.stream) {
  347. qDebug() << "_AddStream failed: Cannot create new stream";
  348. return false;
  349. }
  350. info.stream->id = _fmtCtx->nb_streams - 1;
  351. int ret = avcodec_parameters_from_context(info.stream->codecpar, info.encoder->GetCtx());
  352. if (ret < 0) {
  353. char errbuf[AV_ERROR_MAX_STRING_SIZE];
  354. av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);
  355. qDebug() << "_AddStream failed: Cannot copy codec parameters. Error:" << errbuf << "(" << ret << ")";
  356. return false;
  357. }
  358. info.streamIndex = _fmtCtx->nb_streams - 1;
  359. info.pts = 0;
  360. info.isEnd = false;
  361. _infos.push_back(info);
  362. qDebug() << "_AddStream: Stream added successfully, index:" << info.streamIndex;
  363. return true;
  364. }
  365. AVCodecContext* AvMuxer::GetCodecCtx(int streamIndex)
  366. {
  367. __CheckNullptr(streamIndex >= 0 && _infos.size() > streamIndex);
  368. return _infos[streamIndex].encoder->GetCtx();
  369. }
  370. bool AvMuxer::IsEncodeOverload() const
  371. {
  372. for (auto&& info : _infos) {
  373. if (info.isEncodeOverload) {
  374. return true;
  375. }
  376. }
  377. return false;
  378. }