VideoStream.cpp 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  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(::size_t width, ::size_t height, const std::string& filename) :
  10. width{ width }, height{ height }
  11. {
  12. avcodec_register_all();
  13. codec = avcodec_find_encoder(AV_CODEC_ID_MPEG4);
  14. if (!codec) {
  15. fprintf(stderr, "invalid codec\n");
  16. exit(1);
  17. }
  18. codecContext = avcodec_alloc_context3(codec);
  19. picture = av_frame_alloc();
  20. pkt = av_packet_alloc();
  21. if (!pkt)
  22. exit(1);
  23. codecContext->bit_rate = 100 * 1000 * 1000;
  24. codecContext->width = width;
  25. codecContext->height = height;
  26. codecContext->time_base = AVRational{ 1, 60 };
  27. codecContext->framerate = AVRational{ 60, 1 };
  28. codecContext->gop_size = 10; /* emit one intra frame every ten frames */
  29. codecContext->max_b_frames = 1;
  30. codecContext->pix_fmt = AV_PIX_FMT_YUV420P;
  31. if (avcodec_open2(codecContext, codec, nullptr) < 0) {
  32. fprintf(stderr, "could not open codec\n");
  33. exit(1);
  34. }
  35. file = fopen(filename.c_str(), "wb");
  36. if (!file) {
  37. fprintf(stderr, "could not open %s\n", filename.c_str());
  38. exit(1);
  39. }
  40. picture->format = codecContext->pix_fmt;
  41. picture->width = codecContext->width;
  42. picture->height = codecContext->height;
  43. int retval = av_frame_get_buffer(picture, 0);
  44. if (retval < 0) {
  45. fprintf(stderr, "could not alloc the frame data\n");
  46. exit(1);
  47. }
  48. //av_image_alloc(picture->data, picture->linesize, width, height, codecContext->pix_fmt, 32);
  49. swsContext = sws_getContext(width, height,
  50. AV_PIX_FMT_RGB24, width, height,
  51. AV_PIX_FMT_YUV420P, 0, 0, 0, 0);
  52. }
  53. static void encode(AVCodecContext *enc_ctx, AVFrame *frame, AVPacket *pkt,
  54. FILE *outfile)
  55. {
  56. int ret;
  57. /* send the frame to the encoder */
  58. ret = avcodec_send_frame(enc_ctx, frame);
  59. if (ret < 0) {
  60. fprintf(stderr, "error sending a frame for encoding\n");
  61. exit(1);
  62. }
  63. while (ret >= 0) {
  64. ret = avcodec_receive_packet(enc_ctx, pkt);
  65. if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
  66. return;
  67. else if (ret < 0) {
  68. fprintf(stderr, "error during encoding\n");
  69. exit(1);
  70. }
  71. printf("encoded frame %3d\"PRId64\" (size=%5d)\n", pkt->pts, pkt->size);
  72. fwrite(pkt->data, 1, pkt->size, outfile);
  73. av_packet_unref(pkt);
  74. }
  75. }
  76. VideoStream::~VideoStream()
  77. {
  78. /* flush the encoder */
  79. encode(codecContext, NULL, pkt, file);
  80. /* add sequence end code to have a real MPEG file */
  81. fwrite(endcode, 1, sizeof(endcode), file);
  82. fclose(file);
  83. avcodec_free_context(&codecContext);
  84. av_frame_free(&picture);
  85. av_packet_free(&pkt);
  86. }
  87. void VideoStream::addFrame(const Bitmap<RGBColor>& frame)
  88. {
  89. int retval = av_frame_make_writable(picture);
  90. if (retval < 0)
  91. exit(1);
  92. /* prepare a dummy image */
  93. /* Y */
  94. /*for(int y = 0; y < height; y++) {
  95. for(int x = 0; x < width; x++) {
  96. picture->data[0][y * picture->linesize[0] + x] = frame.get(x, y).r / 2;
  97. }
  98. }*/
  99. /* Cb and Cr */
  100. /*for(int y=0;y<height / 2;y++) {
  101. for(int x=0;x<width / 2;x++) {
  102. picture->data[1][y * picture->linesize[1] + x] = frame.get(x * 2, y * 2).g / 2;
  103. picture->data[2][y * picture->linesize[2] + x] = frame.get(x * 2, y * 2).b / 2;
  104. }
  105. }*/
  106. const uint8_t* pixelPointer[] = { reinterpret_cast<const uint8_t*>(frame.pixels.get()), 0 };
  107. const int linesizeIn[] = { int(frame.width * sizeof(RGBColor)) };
  108. sws_scale(swsContext, pixelPointer, linesizeIn, 0,
  109. frame.height, picture->data, picture->linesize);
  110. picture->pts = frameIndex++;
  111. /* encode the image */
  112. encode(codecContext, picture, pkt, file);
  113. }
  114. #endif // FFMPEG_ENABLED