Open hrnozel opened 2 years ago
HttpResponse response;
response.setContentType("multipart/form-data");
std::string content;
readJpeg(content);
response.setBody(content);
in fact, std::string is binary, so it can represent the unsigned char array;
Thank you for your answer. I will let you know when I try this. After, maybe add an example related with this topic.
I tried something, but it is not working as multipart stream. If I send request for each frame working perfectly, but if I run as below it does not work.
// Callback for http request
auto httpEnterCallback = [&, this](const HTTPParser& httpParser,
const HttpSession::Ptr& session)
{
HttpResponse firstResponse;
firstResponse.setContentType("multipart/x-mixed-replace; boundary=mjpegstream");
firstResponse.addHeadValue("Connection", "close");
firstResponse.addHeadValue("max-ages", "0");
firstResponse.addHeadValue("expires", "0");
firstResponse.addHeadValue("Cache-Control", "no-cache, private");
firstResponse.addHeadValue("Pragma", "no-cache");
session->send(firstResponse.getResult());
while(true)
{
std::this_thread::sleep_for(100ms);
std::lock_guard<std::mutex> lock(m_mutex);
HttpResponse httpResponse;
std::string response{};
// ...
// Prepare frame to client
if (!m_buffer.empty())
{
spdlog::info(m_buffer.size());
httpResponse.setBody(std::move(m_buffer.front()));
m_buffer.pop();
}
// ...
httpResponse.setContentType("image/jpeg");
session->send(httpResponse.getResult("--mjpegstream"));
}
};
Also my mini html snippet:
<html>
<body>
<img src="http://127.0.0.1:8082">
</body>
</html>
BTW I needed to little change on HttpResponse::getResult, because should be added "boundary" attribute to stream part after sending header. Also browser only waits in pending status meanwhile.
Addition, this repo is working mjpegwriter as well.
firstResponse.addHeadValue("Connection", "close");
maby you can't setting close Connection.
BTW , you can use chrome F12 network
for debug HTTP response.
firstResponse.addHeadValue("Connection", "close");
I have removed this header but does not work.
I have debugged mjpegwriter and your library with wireshark. mjpegwriter sends the header;
firstResponse.setContentType("multipart/x-mixed-replace; boundary=mjpegstream"); firstResponse.addHeadValue("Connection", "close"); firstResponse.addHeadValue("max-ages", "0"); firstResponse.addHeadValue("expires", "0"); firstResponse.addHeadValue("Cache-Control", "no-cache, private"); firstResponse.addHeadValue("Pragma", "no-cache");
So the browser passes from pending status to stream. But in your library, If keep to stream in while loop, httpSession did not send either this first header or stream. So if the browser does not get this header firstly which stays in pending status.
If I remove this while stream loop;
`while(true)
{
std::this_thread::sleep_for(100ms);
std::lock_guard
// ...
// Prepare frame to client
if (!m_buffer.empty())
{
spdlog::info(m_buffer.size());
httpResponse.setBody(std::move(m_buffer.front()));
m_buffer.pop();
}
// ...
httpResponse.setContentType("image/jpeg");
session->send(httpResponse.getResult("--mjpegstream"));
}`
Your library sends header to browser and it passes from pending status to stream like mjpegwriter. But I could send stream to browser :/
Is that the question? Only the first response can carry the HTTP header, and the subsequent responses should not carry headers.
subsequent responses already do not have a header they begin with boundary attribute and remained part is payload(jpeg).
The issue is that the first response could not be sent to the browser.
Oh, This httpEnterCallback
is called in network thread, so this callback can't be while loop;
How can I send my stream? Is there a way?
Or can I use the tcp part of your library? Is it's callback call from another thread too?
Send data in another thread;
Of course, we can send data in the callback, but the callback can't do something while loop;
For use another thread, we can start up one main EventLoop;
I have looked your examples but I could not figure out how use mainloop in that my case.
Or, you can start up one std::thread;
I guess you mention this example; https://github.com/IronsDu/brynet/blob/master/examples/BroadCastServer.cpp
If I implement by using this example, I guess I will be rewrapped the http part. So, but I still could not figure out that how to use it with main loop or std::thread. Can you share the code snippet or example?
BTW I am trying to use only http part of your library. I do not want to use other parts as far as possible. Of course, I can use it If you say that should use tcp part, the http part is not able to do this.
Don't need rewrapped the http part; You must send data in another thread, it's only because your logic code is while loop;
What do you mean another thread? How can explain with a code snippet?
auto httpEnterCallback = [&, this](const HTTPParser& httpParser,
const HttpSession::Ptr& session)
{
HttpResponse firstResponse;
firstResponse.setContentType("multipart/x-mixed-replace; boundary=mjpegstream");
firstResponse.addHeadValue("Connection", "close");
firstResponse.addHeadValue("max-ages", "0");
firstResponse.addHeadValue("expires", "0");
firstResponse.addHeadValue("Cache-Control", "no-cache, private");
firstResponse.addHeadValue("Pragma", "no-cache");
session->send(firstResponse.getResult());
std::thread([=]() {
while(true)
{
std::this_thread::sleep_for(100ms);
std::lock_guard<std::mutex> lock(m_mutex);
HttpResponse httpResponse;
std::string response{};
// ...
// Prepare frame to client
if (!m_buffer.empty())
{
spdlog::info(m_buffer.size());
httpResponse.setBody(std::move(m_buffer.front()));
m_buffer.pop();
}
// ...
httpResponse.setContentType("image/jpeg");
session->send(httpResponse.getResult("--mjpegstream"));
}
);
};
That your example code not working, it is crashing when creating the thread in the callback. Do you have an idea about that?
BTW, Library still does not send the first header which is before while loop.
From what I've debugged, it looks like httpsession is trying to destroy the thread created in the callback.
I have found another issue; httpSession cant send sequential responses for a request. I think this is due to running in event loop.
You can give me your project, I have a try.
I viewed your broadcast server example, and I changed my code like this. And now it is working as properly.
void MyClass::addClient(const brynet::net::http::HttpSession::Ptr& client)
{
m_sessions.push_back(client);
}
void MyClass::dispatchFrame()
{
while (true)
{
std::this_thread::sleep_for(10ms);
HttpResponse httpResponse;
// ...
// Prepare frame to client
if (!m_buffer.empty())
{
{
std::lock_guard<std::mutex> lock(m_mutex);
spdlog::info(m_buffer.size());
httpResponse.setBody(std::move(m_buffer.front()));
m_buffer.pop();
}
// ...
httpResponse.setContentType("image/jpeg");
for(auto i : m_sessions)
{
i->send(httpResponse.getResult("--mjpegstream"));
}
}
}
}
void MyClass::listenHTTP()
{
// ...
auto service = TcpService::Create();
service->startWorkerThread(std::thread::hardware_concurrency());
auto mainLoop = std::make_shared<EventLoop>();
// Callback for http request
auto httpEnterCallback = [&, this](const HTTPParser& httpParser,
const HttpSession::Ptr& session)
{
HttpResponse firstResponse;
firstResponse.setContentType("multipart/x-mixed-replace; boundary=mjpegstream");
firstResponse.addHeadValue("Connection", "close");
firstResponse.addHeadValue("max-ages", "0");
firstResponse.addHeadValue("expires", "0");
firstResponse.addHeadValue("Cache-Control", "no-cache, private");
firstResponse.addHeadValue("Pragma", "no-cache");
session->send(firstResponse.getResult());
mainLoop->runAsyncFunctor([&, session]() {
addClient(session);
});
};
wrapper::HttpListenerBuilder httpListenBuilder;
unsigned int httpServerPort{ 8082 };
try
{
httpListenBuilder
.WithService(service)
.AddSocketProcess([](TcpSocket& socket) {
socket.setNodelay();
})
.WithMaxRecvBufferSize(8192)
.WithAddr(false, "127.0.0.1", httpServerPort)
.WithReusePort()
.WithEnterCallback([httpEnterCallback]
(const HttpSession::Ptr& httpSession, HttpSessionHandlers& handlers) {
handlers.setHttpCallback(httpEnterCallback);
})
.asyncRun();
}
catch (const brynet::net::BrynetCommonException& e)
{
// ...
std::string exceptionMessage("HTTP Server Error: could not bind to given port. ");
}
// Infinite loop for connection
while (true)
{
mainLoop->loop(10);
std::this_thread::sleep_for(1s);
if (brynet::base::app_kbhit())
{
break;
}
}
}
BTW I had to make changes on HttpResponse::getResult like below;
std::string getResult(const std::string& firstLine = {}) const
{
std::string ret;
if(firstLine.empty())
{
ret = "HTTP/1.1 ";
ret += std::to_string(static_cast<int>(mStatus));
switch (mStatus)
{
case HTTP_RESPONSE_STATUS::OK:
ret += " OK";
break;
default:
ret += "UNKNOWN";
break;
}
}
else
{
ret = firstLine;
}
ret += "\r\n";
for (auto& v : mHeadField)
{
ret += v.first;
ret += ": ";
ret += v.second;
ret += "\r\n";
}
ret += "\r\n";
if (!mBody.empty())
{
ret += mBody;
}
return ret;
}
I made those changes, you know that I need send boundary attribute as header for sequential responses.
So if it is suitable I can send PR with an example.
I am planning to provide mjpeg stream over HTTP in my project. Briefly, I need to send each frame as jpeg over http but did not see anything about setting body to multipart or there is not a setBody function or its overloads that take byte(unsigned char) as the argument. All of them want std::string as the argument.