X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=video_encoder.cpp;h=8fd9723239f613345151fb11323371be19497728;hb=90d0745245f83999e88bc50cad57be3e4a1c7439;hp=39bc2641932dce864a6a0c7ba5e18feb80ae677d;hpb=c4c5e2df2b1ef88fa20b71ae981ae6462d84b63c;p=nageru diff --git a/video_encoder.cpp b/video_encoder.cpp index 39bc264..8fd9723 100644 --- a/video_encoder.cpp +++ b/video_encoder.cpp @@ -1,8 +1,13 @@ #include "video_encoder.h" +#include + #include #include "defs.h" +#include "flags.h" +#include "httpd.h" +#include "timebase.h" #include "quicksync_encoder.h" using namespace std; @@ -31,13 +36,16 @@ string generate_local_dump_filename(int frame) VideoEncoder::VideoEncoder(QSurface *surface, const std::string &va_display, int width, int height, HTTPD *httpd) : surface(surface), va_display(va_display), width(width), height(height), httpd(httpd) { - quicksync_encoder.reset(new QuickSyncEncoder(surface, va_display, width, height, httpd)); + open_output_stream(); + + quicksync_encoder.reset(new QuickSyncEncoder(surface, va_display, width, height, stream_mux.get())); quicksync_encoder->open_output_file(generate_local_dump_filename(/*frame=*/0).c_str()); } VideoEncoder::~VideoEncoder() { quicksync_encoder.reset(nullptr); + close_output_stream(); } void VideoEncoder::do_cut(int frame) @@ -46,7 +54,7 @@ void VideoEncoder::do_cut(int frame) printf("Starting new recording: %s\n", filename.c_str()); quicksync_encoder->close_output_file(); quicksync_encoder->shutdown(); - quicksync_encoder.reset(new QuickSyncEncoder(surface, va_display, width, height, httpd)); + quicksync_encoder.reset(new QuickSyncEncoder(surface, va_display, width, height, stream_mux.get())); quicksync_encoder->open_output_file(filename.c_str()); } @@ -64,3 +72,69 @@ RefCountedGLsync VideoEncoder::end_frame(int64_t pts, int64_t duration, const st { return quicksync_encoder->end_frame(pts, duration, input_frames); } + +void VideoEncoder::open_output_stream() +{ + AVFormatContext *avctx = avformat_alloc_context(); + AVOutputFormat *oformat = av_guess_format(global_flags.stream_mux_name.c_str(), nullptr, nullptr); + assert(oformat != nullptr); + avctx->oformat = oformat; + + string codec_name; + int bit_rate; + + if (global_flags.stream_audio_codec_name.empty()) { + codec_name = AUDIO_OUTPUT_CODEC_NAME; + bit_rate = DEFAULT_AUDIO_OUTPUT_BIT_RATE; + } else { + codec_name = global_flags.stream_audio_codec_name; + bit_rate = global_flags.stream_audio_codec_bitrate; + } + + uint8_t *buf = (uint8_t *)av_malloc(MUX_BUFFER_SIZE); + avctx->pb = avio_alloc_context(buf, MUX_BUFFER_SIZE, 1, this, nullptr, &VideoEncoder::write_packet_thunk, nullptr); + + Mux::Codec video_codec; + if (global_flags.uncompressed_video_to_http) { + video_codec = Mux::CODEC_NV12; + } else { + video_codec = Mux::CODEC_H264; + } + + avctx->flags = AVFMT_FLAG_CUSTOM_IO; + AVCodec *codec_audio = avcodec_find_encoder_by_name(codec_name.c_str()); + if (codec_audio == nullptr) { + fprintf(stderr, "ERROR: Could not find codec '%s'\n", codec_name.c_str()); + exit(1); + } + + int time_base = global_flags.stream_coarse_timebase ? COARSE_TIMEBASE : TIMEBASE; + stream_mux_writing_header = true; + stream_mux.reset(new Mux(avctx, width, height, video_codec, codec_audio, time_base, bit_rate, this)); + stream_mux_writing_header = false; + httpd->set_header(stream_mux_header); + stream_mux_header.clear(); +} + +void VideoEncoder::close_output_stream() +{ + stream_mux.reset(); +} + +int VideoEncoder::write_packet_thunk(void *opaque, uint8_t *buf, int buf_size) +{ + VideoEncoder *video_encoder = (VideoEncoder *)opaque; + return video_encoder->write_packet(buf, buf_size); +} + +int VideoEncoder::write_packet(uint8_t *buf, int buf_size) +{ + if (stream_mux_writing_header) { + stream_mux_header.append((char *)buf, buf_size); + } else { + httpd->add_data((char *)buf, buf_size, stream_mux_writing_keyframes); + stream_mux_writing_keyframes = false; + } + return buf_size; +} +