123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236 |
- #include "VideoStream.h"
- #ifdef FFMPEG_ENABLED
- #include <iostream>
- #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,28,1)
- #define av_frame_alloc avcodec_alloc_frame
- #define av_frame_free avcodec_free_frame
- #endif
- const uint8_t VideoStream::endcode[] = { 0, 0, 1, 0xb7 };
- VideoStream::VideoStream(int width, int height, const std::string& filename, int bitrate, const char* preset) :
- width{ width & (~1) }, height{ height & (~1) }
- {
-
-
- codec = avcodec_find_encoder(AV_CODEC_ID_H264);
- if (!codec) {
- fprintf(stderr, "invalid codec\n");
- exit(1);
- }
- AVOutputFormat* oformat = av_guess_format(nullptr, filename.c_str(), nullptr);
- if (!oformat)
- oformat = av_guess_format("mp4", nullptr, nullptr);
- if (oformat == nullptr)
- throw "invalid format";
- codecContext = avcodec_alloc_context3(codec);
- pkt = av_packet_alloc();
- if (!pkt)
- exit(1);
- codecContext->bit_rate = bitrate * 1000;
- codecContext->width = width;
- codecContext->height = height;
- codecContext->time_base = AVRational{ 1, 60 };
- codecContext->framerate = AVRational{ 60, 1 };
- codecContext->gop_size = 5;
- codecContext->max_b_frames = 1;
- codecContext->pix_fmt = AV_PIX_FMT_YUV420P;
- formatContext = avformat_alloc_context();
- formatContext->oformat = oformat;
- formatContext->video_codec_id = oformat->video_codec;
- stream = avformat_new_stream(formatContext, codec);
- if (!stream)
- throw "error";
- params = avcodec_parameters_alloc();
- avcodec_parameters_from_context(params, codecContext);
- stream->codecpar = params;
-
- if (codec->id == AV_CODEC_ID_H264)
- av_opt_set(codecContext->priv_data, "preset", preset, 0);
- if (avcodec_open2(codecContext, codec, nullptr) < 0) {
- fprintf(stderr, "could not open codec\n");
- exit(1);
- }
- avio_open(&formatContext->pb, filename.c_str(), AVIO_FLAG_WRITE);
- if (avformat_write_header(formatContext, NULL) < 0) {
- throw "error";
- }
-
- picture = av_frame_alloc();
- av_frame_make_writable(picture);
- picture->format = codecContext->pix_fmt;
- picture->width = codecContext->width;
- picture->height = codecContext->height;
- int retval = av_frame_get_buffer(picture, 0);
- if (retval < 0) {
- fprintf(stderr, "could not alloc the frame data\n");
- exit(1);
- }
-
- swsContext = sws_getContext(width, height,
- AV_PIX_FMT_RGB24, width, height,
- AV_PIX_FMT_YUV420P, 0, 0, 0, 0);
- }
- void VideoStream::encode(AVFrame* frame)
- {
- int ret;
-
- ret = avcodec_send_frame(codecContext, frame);
- if (ret < 0) {
- fprintf(stderr, "error sending a frame for encoding\n");
- exit(1);
- }
- while (ret >= 0) {
- ret = avcodec_receive_packet(codecContext, pkt);
-
- if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
- return;
- else if (ret < 0) {
- fprintf(stderr, "error during encoding\n");
- exit(1);
- }
- printf("encoded frame %3d\"PRId64\" (size=%5d)\n", pkt->pts, pkt->size);
-
-
- av_packet_rescale_ts(pkt, AVRational{1, 60}, stream->time_base);
- pkt->stream_index = stream->index;
- av_write_frame(formatContext, pkt);
- av_packet_unref(pkt);
- }
- }
- VideoStream::~VideoStream()
- {
-
- encode(nullptr);
- av_write_trailer(this->formatContext);
-
-
-
- avcodec_close(codecContext);
- avio_close(formatContext->pb);
- av_frame_unref(picture);
-
- avcodec_parameters_free(¶ms);
- avcodec_free_context(&codecContext);
- av_frame_free(&picture);
- av_packet_free(&pkt);
- }
- void VideoStream::addFrame(const Bitmap<RGBColor>& frame)
- {
- int retval = av_frame_make_writable(picture);
- if (retval < 0)
- exit(1);
-
-
-
-
-
-
- const uint8_t* pixelPointer[] = { reinterpret_cast<const uint8_t*>(frame.pixels.get()), 0 };
- const int linesizeIn[] = { int(frame.width * sizeof(RGBColor)) };
- sws_scale(swsContext, pixelPointer, linesizeIn, 0,
- frame.height, picture->data, picture->linesize);
- picture->pts = frameIndex++;
-
- encode(picture);
- }
- #endif
|