X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=video_encoder.cpp;h=0df3b4ffe66c2bf8019be319233faa010c3796f8;hb=1462715cd71d8f61b9e53b31c34d591d150f2df3;hp=39bc2641932dce864a6a0c7ba5e18feb80ae677d;hpb=c4c5e2df2b1ef88fa20b71ae981ae6462d84b63c;p=nageru diff --git a/video_encoder.cpp b/video_encoder.cpp index 39bc264..0df3b4f 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,23 +36,31 @@ 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)); - quicksync_encoder->open_output_file(generate_local_dump_filename(/*frame=*/0).c_str()); + open_output_stream(); + + if (global_flags.stream_audio_codec_name.empty()) { + stream_audio_encoder.reset(new AudioEncoder(AUDIO_OUTPUT_CODEC_NAME, DEFAULT_AUDIO_OUTPUT_BIT_RATE)); + } else { + stream_audio_encoder.reset(new AudioEncoder(global_flags.stream_audio_codec_name, global_flags.stream_audio_codec_bitrate)); + } + stream_audio_encoder->add_mux(stream_mux.get()); + + string filename = generate_local_dump_filename(/*frame=*/0); + quicksync_encoder.reset(new QuickSyncEncoder(filename, surface, va_display, width, height, stream_mux.get(), stream_audio_encoder.get())); } VideoEncoder::~VideoEncoder() { quicksync_encoder.reset(nullptr); + close_output_stream(); } void VideoEncoder::do_cut(int frame) { string filename = generate_local_dump_filename(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->open_output_file(filename.c_str()); + quicksync_encoder.reset(new QuickSyncEncoder(filename, surface, va_display, width, height, stream_mux.get(), stream_audio_encoder.get())); } void VideoEncoder::add_audio(int64_t pts, std::vector audio) @@ -64,3 +77,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; +} +