VideoStream.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. #include "VideoStream.h"
  2. #ifdef FFMPEG_ENABLED
  3. #include <iostream>
  4. #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,28,1)
  5. #define av_frame_alloc avcodec_alloc_frame
  6. #define av_frame_free avcodec_free_frame
  7. #endif
  8. const uint8_t VideoStream::endcode[] = { 0, 0, 1, 0xb7 };
  9. VideoStream::VideoStream(int width, int height, const std::string& filename, int bitrate, const char* preset) :
  10. width{ width & (~1) }, height{ height & (~1) }
  11. {
  12. // only needed with ffmpeg version < 4
  13. //avcodec_register_all();
  14. codec = avcodec_find_encoder(AV_CODEC_ID_H264);
  15. if (!codec) {
  16. fprintf(stderr, "invalid codec\n");
  17. exit(1);
  18. }
  19. AVOutputFormat* oformat = av_guess_format("mp4", NULL, NULL);
  20. if (oformat == nullptr)
  21. throw "invalid format";
  22. codecContext = avcodec_alloc_context3(codec);
  23. pkt = av_packet_alloc();
  24. if (!pkt)
  25. exit(1);
  26. codecContext->bit_rate = bitrate * 1000;
  27. codecContext->width = width;
  28. codecContext->height = height;
  29. codecContext->time_base = AVRational{ 1, 60 };
  30. codecContext->framerate = AVRational{ 60, 1 };
  31. codecContext->gop_size = 5; /* emit one intra frame every five frames */
  32. codecContext->max_b_frames = 1;
  33. codecContext->pix_fmt = AV_PIX_FMT_YUV420P;
  34. formatContext = avformat_alloc_context();
  35. formatContext->oformat = oformat;
  36. formatContext->video_codec_id = oformat->video_codec;
  37. stream = avformat_new_stream(formatContext, codec);
  38. if (!stream)
  39. throw "error";
  40. params = avcodec_parameters_alloc();
  41. avcodec_parameters_from_context(params, codecContext);
  42. stream->codecpar = params;
  43. /*AVCPBProperties *props;
  44. props = (AVCPBProperties*) av_stream_new_side_data(
  45. stream, AV_PKT_DATA_CPB_PROPERTIES, sizeof(*props));
  46. props->buffer_size = 1024 * 1024;
  47. props->max_bitrate = 0;
  48. props->min_bitrate = 0;
  49. props->avg_bitrate = 0;
  50. props->vbv_delay = UINT64_MAX;*/
  51. if (codec->id == AV_CODEC_ID_H264)
  52. av_opt_set(codecContext->priv_data, "preset", preset, 0);
  53. if (avcodec_open2(codecContext, codec, nullptr) < 0) {
  54. fprintf(stderr, "could not open codec\n");
  55. exit(1);
  56. }
  57. avio_open(&formatContext->pb, filename.c_str(), AVIO_FLAG_WRITE);
  58. if (avformat_write_header(formatContext, NULL) < 0) {
  59. throw "error";
  60. }
  61. /*file = fopen(filename.c_str(), "wb");
  62. if (!file) {
  63. fprintf(stderr, "could not open %s\n", filename.c_str());
  64. exit(1);
  65. }*/
  66. picture = av_frame_alloc();
  67. av_frame_make_writable(picture);
  68. picture->format = codecContext->pix_fmt;
  69. picture->width = codecContext->width;
  70. picture->height = codecContext->height;
  71. int retval = av_frame_get_buffer(picture, 0);
  72. if (retval < 0) {
  73. fprintf(stderr, "could not alloc the frame data\n");
  74. exit(1);
  75. }
  76. //av_image_alloc(picture->data, picture->linesize, width, height, codecContext->pix_fmt, 32);
  77. swsContext = sws_getContext(width, height,
  78. AV_PIX_FMT_RGB24, width, height,
  79. AV_PIX_FMT_YUV420P, 0, 0, 0, 0);
  80. }
  81. void VideoStream::encode(AVFrame* frame)
  82. {
  83. int ret;
  84. /* send the frame to the encoder */
  85. ret = avcodec_send_frame(codecContext, frame);
  86. if (ret < 0) {
  87. fprintf(stderr, "error sending a frame for encoding\n");
  88. exit(1);
  89. }
  90. while (ret >= 0) {
  91. ret = avcodec_receive_packet(codecContext, pkt);
  92. //ret = avcodec_encode_video2(codecContext, pkt, picture, &gotPacket);
  93. if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
  94. return;
  95. else if (ret < 0) {
  96. fprintf(stderr, "error during encoding\n");
  97. exit(1);
  98. }
  99. printf("encoded frame %3d\"PRId64\" (size=%5d)\n", pkt->pts, pkt->size);
  100. //fwrite(pkt->data, 1, pkt->size, outfile);
  101. //av_interleaved_write_frame(formatContext, pkt);
  102. av_packet_rescale_ts(pkt, (AVRational){1, 25}, stream->time_base);
  103. pkt->stream_index = stream->index;
  104. av_write_frame(formatContext, pkt);
  105. av_packet_unref(pkt);
  106. }
  107. }
  108. VideoStream::~VideoStream()
  109. {
  110. /* flush the encoder */
  111. encode(nullptr);
  112. av_write_trailer(this->formatContext);
  113. /* add sequence end code to have a real MPEG file */
  114. //fwrite(endcode, 1, sizeof(endcode), file);
  115. //fclose(file);
  116. avcodec_close(codecContext);
  117. avio_close(formatContext->pb);
  118. av_frame_unref(picture);
  119. //av_free(codecContext);
  120. avcodec_parameters_free(&params);
  121. avcodec_free_context(&codecContext);
  122. av_frame_free(&picture);
  123. av_packet_free(&pkt);
  124. /*
  125. AVPacket pkt;
  126. av_init_packet(&pkt);
  127. pkt.data = nullptr;
  128. pkt.size = 0;
  129. for (;;) {
  130. avcodec_send_frame(codecContext, NULL);
  131. if (avcodec_receive_packet(codecContext, &pkt) == 0) {
  132. av_interleaved_write_frame(codecContext, &pkt);
  133. av_packet_unref(&pkt);
  134. }
  135. else {
  136. break;
  137. }
  138. }
  139. av_write_trailer();
  140. if (!(oformat->flags & AVFMT_NOFILE)) {
  141. int err = avio_close(ofctx->pb);
  142. if (err < 0) {
  143. Debug("Failed to close file", err);
  144. }
  145. }*/
  146. }
  147. void VideoStream::addFrame(const Bitmap<RGBColor>& frame)
  148. {
  149. int retval = av_frame_make_writable(picture);
  150. if (retval < 0)
  151. exit(1);
  152. /* prepare a dummy image */
  153. /* Y */
  154. /*for(int y = 0; y < height; y++) {
  155. for(int x = 0; x < width; x++) {
  156. picture->data[0][y * picture->linesize[0] + x] = frame.get(x, y).r / 2;
  157. }
  158. }*/
  159. /* Cb and Cr */
  160. /*for(int y=0;y<height / 2;y++) {
  161. for(int x=0;x<width / 2;x++) {
  162. picture->data[1][y * picture->linesize[1] + x] = frame.get(x * 2, y * 2).g / 2;
  163. picture->data[2][y * picture->linesize[2] + x] = frame.get(x * 2, y * 2).b / 2;
  164. }
  165. }*/
  166. /*auto gammaCorrect = [] (const RGBColor& rgb) {
  167. const float gamma = 2.2f;
  168. return RGBColor {
  169. uint8_t(::powf(rgb.r / 255.0f, 1.0f / gamma) * 255),
  170. uint8_t(::powf(rgb.g / 255.0f, 1.0f / gamma) * 255),
  171. uint8_t(::powf(rgb.b / 255.0f, 1.0f / gamma) * 255),
  172. };
  173. };
  174. Bitmap<RGBColor> gammaCorrected = frame.map<RGBColor>(gammaCorrect);*/
  175. const uint8_t* pixelPointer[] = { reinterpret_cast<const uint8_t*>(frame.pixels.get()), 0 };
  176. const int linesizeIn[] = { int(frame.width * sizeof(RGBColor)) };
  177. sws_scale(swsContext, pixelPointer, linesizeIn, 0,
  178. frame.height, picture->data, picture->linesize);
  179. picture->pts = frameIndex++;
  180. /* encode the image */
  181. encode(picture);
  182. }
  183. #endif // FFMPEG_ENABLED