2 #include <microhttpd.h>
8 #include <libavcodec/avcodec.h>
9 #include <libavutil/channel_layout.h>
10 #include <libavutil/mathematics.h>
11 #include <libavutil/mem.h>
12 #include <libavutil/pixfmt.h>
13 #include <libavutil/rational.h>
14 #include <libavutil/samplefmt.h>
25 struct MHD_Connection;
30 HTTPD::HTTPD(int width, int height)
31 : width(width), height(height)
35 void HTTPD::start(int port)
37 MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION | MHD_USE_POLL_INTERNALLY | MHD_USE_DUAL_STACK,
40 &answer_to_connection_thunk, this,
41 MHD_OPTION_NOTIFY_COMPLETED, &request_completed_thunk, this,
45 void HTTPD::add_packet(const AVPacket &pkt, int64_t pts, int64_t dts)
47 unique_lock<mutex> lock(streams_mutex);
48 for (Stream *stream : streams) {
49 stream->add_packet(pkt, pts, dts);
53 int HTTPD::answer_to_connection_thunk(void *cls, MHD_Connection *connection,
54 const char *url, const char *method,
55 const char *version, const char *upload_data,
56 size_t *upload_data_size, void **con_cls)
58 HTTPD *httpd = (HTTPD *)cls;
59 return httpd->answer_to_connection(connection, url, method, version, upload_data, upload_data_size, con_cls);
62 int HTTPD::answer_to_connection(MHD_Connection *connection,
63 const char *url, const char *method,
64 const char *version, const char *upload_data,
65 size_t *upload_data_size, void **con_cls)
67 AVOutputFormat *oformat = av_guess_format(global_flags.stream_mux_name.c_str(), nullptr, nullptr);
68 assert(oformat != nullptr);
70 // TODO: This is an ugly place to have this logic.
71 const int bit_rate = global_flags.stream_audio_codec_name.empty() ?
72 DEFAULT_AUDIO_OUTPUT_BIT_RATE :
73 global_flags.stream_audio_codec_bitrate;
75 int time_base = global_flags.stream_coarse_timebase ? COARSE_TIMEBASE : TIMEBASE;
76 HTTPD::Stream *stream = new HTTPD::Stream(oformat, width, height, time_base, bit_rate);
78 unique_lock<mutex> lock(streams_mutex);
79 streams.insert(stream);
83 // Does not strictly have to be equal to MUX_BUFFER_SIZE.
84 MHD_Response *response = MHD_create_response_from_callback(
85 (size_t)-1, MUX_BUFFER_SIZE, &HTTPD::Stream::reader_callback_thunk, stream, &HTTPD::free_stream);
86 int ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
87 //MHD_destroy_response(response);
92 void HTTPD::free_stream(void *cls)
94 // FIXME: When is this actually called, if ever?
95 // Also, shouldn't we remove it from streams?
96 HTTPD::Stream *stream = (HTTPD::Stream *)cls;
100 void HTTPD::request_completed_thunk(void *cls, struct MHD_Connection *connection, void **con_cls, enum MHD_RequestTerminationCode toe)
102 HTTPD *httpd = (HTTPD *)cls;
103 return httpd->request_completed(connection, con_cls, toe);
106 void HTTPD::request_completed(struct MHD_Connection *connection, void **con_cls, enum MHD_RequestTerminationCode toe)
108 if (con_cls == nullptr) {
109 // Request was never set up.
112 HTTPD::Stream *stream = (HTTPD::Stream *)*con_cls;
114 unique_lock<mutex> lock(streams_mutex);
116 streams.erase(stream);
120 HTTPD::Stream::Stream(AVOutputFormat *oformat, int width, int height, int time_base, int bit_rate)
122 AVFormatContext *avctx = avformat_alloc_context();
123 avctx->oformat = oformat;
124 uint8_t *buf = (uint8_t *)av_malloc(MUX_BUFFER_SIZE);
125 avctx->pb = avio_alloc_context(buf, MUX_BUFFER_SIZE, 1, this, nullptr, &HTTPD::Stream::write_packet_thunk, nullptr);
127 Mux::Codec video_codec;
128 if (global_flags.uncompressed_video_to_http) {
129 video_codec = Mux::CODEC_NV12;
131 video_codec = Mux::CODEC_H264;
134 avctx->flags = AVFMT_FLAG_CUSTOM_IO;
136 // TODO: This is an ugly place to have this logic.
137 const string codec_name = global_flags.stream_audio_codec_name.empty() ?
138 AUDIO_OUTPUT_CODEC_NAME :
139 global_flags.stream_audio_codec_name;
141 AVCodec *codec_audio = avcodec_find_encoder_by_name(codec_name.c_str());
142 if (codec_audio == nullptr) {
143 fprintf(stderr, "ERROR: Could not find codec '%s'\n", codec_name.c_str());
147 mux.reset(new Mux(avctx, width, height, video_codec, codec_audio, time_base, bit_rate));
150 ssize_t HTTPD::Stream::reader_callback_thunk(void *cls, uint64_t pos, char *buf, size_t max)
152 HTTPD::Stream *stream = (HTTPD::Stream *)cls;
153 return stream->reader_callback(pos, buf, max);
156 ssize_t HTTPD::Stream::reader_callback(uint64_t pos, char *buf, size_t max)
158 unique_lock<mutex> lock(buffer_mutex);
159 has_buffered_data.wait(lock, [this]{ return !buffered_data.empty(); });
162 while (max > 0 && !buffered_data.empty()) {
163 const string &s = buffered_data.front();
164 assert(s.size() > used_of_buffered_data);
165 size_t len = s.size() - used_of_buffered_data;
167 // Consume the entire (rest of the) string.
168 memcpy(buf, s.data() + used_of_buffered_data, len);
172 buffered_data.pop_front();
173 used_of_buffered_data = 0;
175 // We don't need the entire string; just use the first part of it.
176 memcpy(buf, s.data() + used_of_buffered_data, max);
178 used_of_buffered_data += max;
187 void HTTPD::Stream::add_packet(const AVPacket &pkt, int64_t pts, int64_t dts)
189 mux->add_packet(pkt, pts, dts);
192 int HTTPD::Stream::write_packet_thunk(void *opaque, uint8_t *buf, int buf_size)
194 HTTPD::Stream *stream = (HTTPD::Stream *)opaque;
195 return stream->write_packet(buf, buf_size);
198 int HTTPD::Stream::write_packet(uint8_t *buf, int buf_size)
200 unique_lock<mutex> lock(buffer_mutex);
201 buffered_data.emplace_back((char *)buf, buf_size);
202 has_buffered_data.notify_all();