VideoStream.cpp 3.9 KB

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