X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=httpd.cpp;h=7375e8a09db1d597f0a2988975019d709b7276a9;hb=410a5fadcba336c918843cd55e1516cf4fe56abc;hp=3485a83eb564e2a178bc37972b6fe9d8ce38e52a;hpb=b4f16ea9f8969a3ba14be8cd9c88cfe00d19533b;p=nageru diff --git a/httpd.cpp b/httpd.cpp index 3485a83..7375e8a 100644 --- a/httpd.cpp +++ b/httpd.cpp @@ -1,31 +1,81 @@ -#include -#include #include +#include +#include +#include +#include extern "C" { #include +#include +#include +#include +#include +#include +#include } +#include + #include "httpd.h" + +#include "defs.h" +#include "flags.h" #include "timebase.h" +struct MHD_Connection; +struct MHD_Response; + using namespace std; -HTTPD::HTTPD() {} +HTTPD::HTTPD(int width, int height) + : width(width), height(height) +{ +} void HTTPD::start(int port) { MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION | MHD_USE_POLL_INTERNALLY | MHD_USE_DUAL_STACK, port, nullptr, nullptr, - &answer_to_connection_thunk, this, MHD_OPTION_END); + &answer_to_connection_thunk, this, + MHD_OPTION_NOTIFY_COMPLETED, &request_completed_thunk, this, + MHD_OPTION_END); +} + +void HTTPD::add_packet(const AVPacket &pkt, int64_t pts, int64_t dts, PacketDestination destination) +{ + unique_lock lock(streams_mutex); + if (destination != DESTINATION_FILE_ONLY) { + for (Stream *stream : streams) { + stream->add_packet(pkt, pts, dts); + } + } + if (file_mux && destination != DESTINATION_HTTP_ONLY) { + file_mux->add_packet(pkt, pts, dts); + } } -void HTTPD::add_packet(const AVPacket &pkt) +void HTTPD::open_output_file(const string &filename) { - for (Stream *stream : streams) { - stream->add_packet(pkt); + AVFormatContext *avctx = avformat_alloc_context(); + avctx->oformat = av_guess_format(NULL, filename.c_str(), NULL); + assert(filename.size() < sizeof(avctx->filename) - 1); + strcpy(avctx->filename, filename.c_str()); + + string url = "file:" + filename; + int ret = avio_open2(&avctx->pb, url.c_str(), AVIO_FLAG_WRITE, &avctx->interrupt_callback, NULL); + if (ret < 0) { + char tmp[AV_ERROR_MAX_STRING_SIZE]; + fprintf(stderr, "%s: avio_open2() failed: %s\n", filename.c_str(), av_make_error_string(tmp, sizeof(tmp), ret)); + exit(1); } + + file_mux.reset(new Mux(avctx, width, height, Mux::CODEC_H264, TIMEBASE)); +} + +void HTTPD::close_output_file() +{ + file_mux.reset(); } int HTTPD::answer_to_connection_thunk(void *cls, MHD_Connection *connection, @@ -42,13 +92,20 @@ int HTTPD::answer_to_connection(MHD_Connection *connection, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls) { - printf("url %s\n", url); - AVOutputFormat *oformat = av_guess_format("mpegts", nullptr, nullptr); + AVOutputFormat *oformat = av_guess_format(global_flags.stream_mux_name.c_str(), nullptr, nullptr); assert(oformat != nullptr); - HTTPD::Stream *stream = new HTTPD::Stream(oformat); - streams.push_back(stream); + + int time_base = global_flags.stream_coarse_timebase ? COARSE_TIMEBASE : TIMEBASE; + HTTPD::Stream *stream = new HTTPD::Stream(oformat, width, height, time_base); + { + unique_lock lock(streams_mutex); + streams.insert(stream); + } + *con_cls = stream; + + // Does not strictly have to be equal to MUX_BUFFER_SIZE. MHD_Response *response = MHD_create_response_from_callback( - (size_t)-1, 1048576, &HTTPD::Stream::reader_callback_thunk, stream, &HTTPD::free_stream); + (size_t)-1, MUX_BUFFER_SIZE, &HTTPD::Stream::reader_callback_thunk, stream, &HTTPD::free_stream); int ret = MHD_queue_response(connection, MHD_HTTP_OK, response); //MHD_destroy_response(response); @@ -57,54 +114,49 @@ int HTTPD::answer_to_connection(MHD_Connection *connection, void HTTPD::free_stream(void *cls) { + // FIXME: When is this actually called, if ever? + // Also, shouldn't we remove it from streams? HTTPD::Stream *stream = (HTTPD::Stream *)cls; delete stream; } -HTTPD::Stream::Stream(AVOutputFormat *oformat) +void HTTPD::request_completed_thunk(void *cls, struct MHD_Connection *connection, void **con_cls, enum MHD_RequestTerminationCode toe) { - avctx = avformat_alloc_context(); - avctx->oformat = oformat; - uint8_t *buf = (uint8_t *)av_malloc(1048576); - avctx->pb = avio_alloc_context(buf, 1048576, 1, this, nullptr, &HTTPD::Stream::write_packet_thunk, nullptr); - avctx->flags = AVFMT_FLAG_CUSTOM_IO; + HTTPD *httpd = (HTTPD *)cls; + return httpd->request_completed(connection, con_cls, toe); +} - // TODO: Unify with the code in h264encoder.cpp. - AVCodec *codec_video = avcodec_find_encoder(AV_CODEC_ID_H264); - avstream_video = avformat_new_stream(avctx, codec_video); - if (avstream_video == nullptr) { - fprintf(stderr, "avformat_new_stream() failed\n"); - exit(1); +void HTTPD::request_completed(struct MHD_Connection *connection, void **con_cls, enum MHD_RequestTerminationCode toe) +{ + if (con_cls == nullptr) { + // Request was never set up. + return; } - avstream_video->time_base = AVRational{1, TIMEBASE}; - avstream_video->codec->width = 1280; // FIXME - avstream_video->codec->height = 720; // FIXME - avstream_video->codec->time_base = AVRational{1, TIMEBASE}; - avstream_video->codec->ticks_per_frame = 1; // or 2? - - AVCodec *codec_audio = avcodec_find_encoder(AV_CODEC_ID_MP3); - avstream_audio = avformat_new_stream(avctx, codec_audio); - if (avstream_audio == nullptr) { - fprintf(stderr, "avformat_new_stream() failed\n"); - exit(1); - } - avstream_audio->time_base = AVRational{1, TIMEBASE}; - avstream_audio->codec->bit_rate = 256000; - avstream_audio->codec->sample_rate = 48000; - avstream_audio->codec->sample_fmt = AV_SAMPLE_FMT_FLTP; - avstream_audio->codec->channels = 2; - avstream_audio->codec->channel_layout = AV_CH_LAYOUT_STEREO; - avstream_audio->codec->time_base = AVRational{1, TIMEBASE}; - - if (avformat_write_header(avctx, NULL) < 0) { - fprintf(stderr, "avformat_write_header() failed\n"); - exit(1); + HTTPD::Stream *stream = (HTTPD::Stream *)*con_cls; + { + unique_lock lock(streams_mutex); + delete stream; + streams.erase(stream); } } -HTTPD::Stream::~Stream() +HTTPD::Stream::Stream(AVOutputFormat *oformat, int width, int height, int time_base) { - avformat_free_context(avctx); + AVFormatContext *avctx = avformat_alloc_context(); + avctx->oformat = oformat; + uint8_t *buf = (uint8_t *)av_malloc(MUX_BUFFER_SIZE); + avctx->pb = avio_alloc_context(buf, MUX_BUFFER_SIZE, 1, this, nullptr, &HTTPD::Stream::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; + + mux.reset(new Mux(avctx, width, height, video_codec, time_base)); } ssize_t HTTPD::Stream::reader_callback_thunk(void *cls, uint64_t pos, char *buf, size_t max) @@ -126,6 +178,7 @@ ssize_t HTTPD::Stream::reader_callback(uint64_t pos, char *buf, size_t max) if (max >= len) { // Consume the entire (rest of the) string. memcpy(buf, s.data() + used_of_buffered_data, len); + buf += len; ret += len; max -= len; buffered_data.pop_front(); @@ -133,6 +186,7 @@ ssize_t HTTPD::Stream::reader_callback(uint64_t pos, char *buf, size_t max) } else { // We don't need the entire string; just use the first part of it. memcpy(buf, s.data() + used_of_buffered_data, max); + buf += max; used_of_buffered_data += max; ret += max; max = 0; @@ -142,24 +196,9 @@ ssize_t HTTPD::Stream::reader_callback(uint64_t pos, char *buf, size_t max) return ret; } -void HTTPD::Stream::add_packet(const AVPacket &pkt) +void HTTPD::Stream::add_packet(const AVPacket &pkt, int64_t pts, int64_t dts) { - AVPacket pkt_copy; - av_copy_packet(&pkt_copy, &pkt); - if (pkt.stream_index == 0) { - pkt_copy.pts = av_rescale_q(pkt.pts, AVRational{1, TIMEBASE}, avstream_video->time_base); - pkt_copy.dts = av_rescale_q(pkt.dts, AVRational{1, TIMEBASE}, avstream_video->time_base); - } else if (pkt.stream_index == 1) { - pkt_copy.pts = av_rescale_q(pkt.pts, AVRational{1, TIMEBASE}, avstream_audio->time_base); - pkt_copy.dts = av_rescale_q(pkt.dts, AVRational{1, TIMEBASE}, avstream_audio->time_base); - } else { - assert(false); - } - - if (av_interleaved_write_frame(avctx, &pkt_copy) < 0) { - fprintf(stderr, "av_interleaved_write_frame() failed\n"); - exit(1); - } + mux->add_packet(pkt, pts, dts); } int HTTPD::Stream::write_packet_thunk(void *opaque, uint8_t *buf, int buf_size)