You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

H264LibavDecoderContext.cxx 6.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. /* Copyright (C) 2021 Vladimir Sukhonosov <xornet@xornet.org>
  2. * Copyright (C) 2021 Martins Mozeiko <martins.mozeiko@gmail.com>
  3. * All Rights Reserved.
  4. *
  5. * This is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This software is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this software; if not, write to the Free Software
  17. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
  18. * USA.
  19. */
  20. extern "C" {
  21. #include <libavutil/imgutils.h>
  22. #include <libavcodec/version.h>
  23. }
  24. #if LIBAVCODEC_VERSION_MAJOR > 57 || LIBAVCODEC_VERSION_MAJOR == 57 && LIBAVCODEC_VERSION_MINOR >= 37
  25. #define FFMPEG_DECODE_VIDEO2_DEPRECATED
  26. #endif
  27. #if LIBAVCODEC_VERSION_MAJOR >= 58
  28. #define FFMPEG_INIT_PACKET_DEPRECATED
  29. #endif
  30. #include <rfb/Exception.h>
  31. #include <rfb/LogWriter.h>
  32. #include <rfb/PixelBuffer.h>
  33. #include <rfb/H264LibavDecoderContext.h>
  34. using namespace rfb;
  35. static LogWriter vlog("H264LibavDecoderContext");
  36. bool H264LibavDecoderContext::initCodec() {
  37. os::AutoMutex lock(&mutex);
  38. sws = NULL;
  39. swsBuffer = NULL;
  40. h264WorkBuffer = NULL;
  41. h264WorkBufferLength = 0;
  42. AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_H264);
  43. if (!codec)
  44. {
  45. vlog.error("Codec not found");
  46. return false;
  47. }
  48. parser = av_parser_init(codec->id);
  49. if (!parser)
  50. {
  51. vlog.error("Could not create H264 parser");
  52. return false;
  53. }
  54. avctx = avcodec_alloc_context3(codec);
  55. if (!avctx)
  56. {
  57. av_parser_close(parser);
  58. vlog.error("Could not allocate video codec context");
  59. return false;
  60. }
  61. frame = av_frame_alloc();
  62. if (!frame)
  63. {
  64. av_parser_close(parser);
  65. avcodec_free_context(&avctx);
  66. vlog.error("Could not allocate video frame");
  67. return false;
  68. }
  69. if (avcodec_open2(avctx, codec, NULL) < 0)
  70. {
  71. av_parser_close(parser);
  72. avcodec_free_context(&avctx);
  73. av_frame_free(&frame);
  74. vlog.error("Could not open codec");
  75. return false;
  76. }
  77. int numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB32, rect.width(), rect.height(), 1);
  78. swsBuffer = new uint8_t[numBytes];
  79. initialized = true;
  80. return true;
  81. }
  82. void H264LibavDecoderContext::freeCodec() {
  83. os::AutoMutex lock(&mutex);
  84. if (!initialized)
  85. return;
  86. av_parser_close(parser);
  87. avcodec_free_context(&avctx);
  88. av_frame_free(&frame);
  89. delete[] swsBuffer;
  90. free(h264WorkBuffer);
  91. initialized = false;
  92. }
  93. // We need to reallocate buffer because AVPacket uses non-const pointer.
  94. // We don't want to const_cast our buffer somewhere. So we would rather to maintain context's own buffer
  95. // Also avcodec requires a right padded buffer
  96. rdr::U8* H264LibavDecoderContext::makeH264WorkBuffer(const rdr::U8* buffer, rdr::U32 len)
  97. {
  98. rdr::U32 reserve_len = len + len % AV_INPUT_BUFFER_PADDING_SIZE;
  99. if (!h264WorkBuffer || reserve_len > h264WorkBufferLength)
  100. {
  101. h264WorkBuffer = (rdr::U8*)realloc(h264WorkBuffer, reserve_len);
  102. if (h264WorkBuffer == NULL) {
  103. throw Exception("H264LibavDecoderContext: Unable to allocate memory");
  104. }
  105. h264WorkBufferLength = reserve_len;
  106. }
  107. memcpy(h264WorkBuffer, buffer, len);
  108. memset(h264WorkBuffer + len, 0, h264WorkBufferLength - len);
  109. return h264WorkBuffer;
  110. }
  111. void H264LibavDecoderContext::decode(const rdr::U8* h264_in_buffer, rdr::U32 len, rdr::U32 flags, ModifiablePixelBuffer* pb) {
  112. os::AutoMutex lock(&mutex);
  113. if (!initialized)
  114. return;
  115. rdr::U8* h264_work_buffer = makeH264WorkBuffer(h264_in_buffer, len);
  116. #ifdef FFMPEG_INIT_PACKET_DEPRECATED
  117. AVPacket *packet = av_packet_alloc();
  118. #else
  119. AVPacket *packet = new AVPacket();
  120. av_init_packet(packet);
  121. #endif
  122. int ret;
  123. int frames_received = 0;
  124. while (len)
  125. {
  126. ret = av_parser_parse2(parser, avctx, &packet->data, &packet->size, h264_work_buffer, len, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
  127. if (ret < 0)
  128. {
  129. vlog.error("Error while parsing");
  130. break;
  131. }
  132. // We need to slap on tv to make it work here (don't ask me why)
  133. if (!packet->size && len == static_cast<rdr::U32>(ret))
  134. ret = av_parser_parse2(parser, avctx, &packet->data, &packet->size, h264_work_buffer, len, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
  135. if (ret < 0)
  136. {
  137. vlog.error("Error while parsing");
  138. break;
  139. }
  140. h264_work_buffer += ret;
  141. len -= ret;
  142. if (!ret)
  143. {
  144. packet->size = len;
  145. packet->data = h264_work_buffer;
  146. len = 0;
  147. }
  148. if (!packet->size)
  149. continue;
  150. #ifndef FFMPEG_DECODE_VIDEO2_DEPRECATED
  151. int got_frame;
  152. ret = avcodec_decode_video2(avctx, frame, &got_frame, packet);
  153. if (ret < 0 || !got_frame)
  154. {
  155. vlog.error("Error during decoding");
  156. break;
  157. }
  158. #else
  159. ret = avcodec_send_packet(avctx, packet);
  160. if (ret < 0)
  161. {
  162. vlog.error("Error sending a packet to decoding");
  163. break;
  164. }
  165. ret = avcodec_receive_frame(avctx, frame);
  166. if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
  167. break;
  168. else if (ret < 0)
  169. {
  170. vlog.error("Error during decoding");
  171. break;
  172. }
  173. #endif
  174. frames_received++;
  175. }
  176. #ifdef FFMPEG_INIT_PACKET_DEPRECATED
  177. packet->size = 0;
  178. packet->data = NULL;
  179. av_packet_free(&packet);
  180. #else
  181. delete packet;
  182. #endif
  183. if (!frames_received)
  184. return;
  185. if (!frame->height)
  186. return;
  187. sws = sws_getCachedContext(sws, frame->width, frame->height, avctx->pix_fmt,
  188. frame->width, frame->height, AV_PIX_FMT_RGB32,
  189. 0, NULL, NULL, NULL);
  190. int stride;
  191. pb->getBuffer(rect, &stride);
  192. int dst_linesize = stride * pb->getPF().bpp/8; // stride is in pixels, linesize is in bytes (stride x4). We need bytes
  193. sws_scale(sws, frame->data, frame->linesize, 0, frame->height, &swsBuffer, &dst_linesize);
  194. pb->imageRect(rect, swsBuffer, stride);
  195. }