Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
545186a
Abstracted some complexity related to WebcamStream.
slav-at-attachix Feb 6, 2026
3da840e
Minor fixes.
slav-at-attachix Feb 9, 2026
8e62c54
Implemented review recommendations.
slav-at-attachix Feb 9, 2026
8a9e03a
Fix an issue with Firefox not showing the images.
slav-at-attachix Feb 10, 2026
8c67742
Every consumer gets its own camera stream. We still have timer to tak…
slav-at-attachix Feb 11, 2026
cb67514
These changes should make the process less CPU intensive.
slav-at-attachix Feb 11, 2026
dbd74ad
Push Tcp content even when the source is not ready yet.
slav-at-attachix Feb 12, 2026
b323c42
Configure WiFi
mikee47 Feb 12, 2026
5f605d5
Use Periodic timer for frame sync
mikee47 Feb 12, 2026
ec64838
Avoid modifying MultiStream?
mikee47 Feb 12, 2026
8ae7829
Abstracted some complexity related to WebcamStream.
slav-at-attachix Feb 6, 2026
d218844
Minor fixes.
slav-at-attachix Feb 9, 2026
2152dbd
Implemented review recommendations.
slav-at-attachix Feb 9, 2026
5cd49d3
Fix an issue with Firefox not showing the images.
slav-at-attachix Feb 10, 2026
b610560
Every consumer gets its own camera stream. We still have timer to tak…
slav-at-attachix Feb 11, 2026
336036a
These changes should make the process less CPU intensive.
slav-at-attachix Feb 11, 2026
f3b428b
Push Tcp content even when the source is not ready yet.
slav-at-attachix Feb 12, 2026
bd7fc6b
Merge remote-tracking branch 'mikee47/feature/webcam-fps' into featur…
slav-at-attachix Feb 19, 2026
0a1dbd1
Avoid modifying MultiStream?
mikee47 Feb 12, 2026
34dd43c
Enable FPS by default.
slav-at-attachix Feb 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion Sming/Libraries/WebCam/src/Camera/CameraInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class CameraInterface
}

/**
* Prepare for the next picture
* @brief Prepare for the next picture
*/
virtual void next()
{
Expand All @@ -64,6 +64,11 @@ class CameraInterface
*/
virtual size_t getSize() = 0;

/**
* @brief Gets the frames per second the camera can capture
*/
virtual uint8_t getFramesPerSecond() = 0;

/**
* @brief Read picture data from the camera.
* @param buffer the allocated data buffer to store the data
Expand Down
27 changes: 19 additions & 8 deletions Sming/Libraries/WebCam/src/Camera/FakeCamera.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,19 @@
#include "CameraInterface.h"
#include <Data/Stream/FileStream.h>

class FakeCamera: public CameraInterface
class FakeCamera : public CameraInterface
{
public:

/**
* Sets the list of all images that should be used to roll over.
* @brief Sets the list of all images that should be used to roll over.
*/
FakeCamera(const Vector<String>& files)
FakeCamera()
{
this->files = files;
}

void addImage(const String& filename)
{
files.add(filename);
}

const String getMimeType() const override
Expand All @@ -43,8 +46,6 @@ class FakeCamera: public CameraInterface
return true;
}

state = eWCS_INITIALISING;
spiffs_mount(); // Mount file system, in order to work with files
state = eWCS_READY;
index = 0;
return true;
Expand All @@ -59,6 +60,11 @@ class FakeCamera: public CameraInterface
return false;
}

if(files.count() == 0) {
debug_w("No images added to the fake camera");
return false;
}

// go to the next picture.
auto& filename = files[index++];
if(index == files.count()) {
Expand All @@ -73,13 +79,18 @@ class FakeCamera: public CameraInterface
}

/**
* Gets the size of the current picture
* @brief Gets the size of the current picture
*/
size_t getSize() override
{
return file.getSize();
}

uint8_t getFramesPerSecond() override
{
return 10; // fake camera supports 10 fps
}

/**
* @brief Read picture data from the camera.
* @param buffer the allocated data buffer to store the data
Expand Down
67 changes: 67 additions & 0 deletions Sming/Libraries/WebCam/src/WebcamPictureStream.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/****
* Sming Framework Project - Open Source framework for high efficiency native ESP8266 development.
* Created 2015 by Skurydin Alexey
* http://github.com/SmingHub/Sming
* All files of the Sming Core are provided under the LGPL v3 license.
*
* WebcamPictureStream.h
*
* @author: 2019 - Slavey Karadzhov <slav@attachix.com>
*
****/

#pragma once

#include <Data/Stream/DataSourceStream.h>
#include "Camera/CameraInterface.h"

class WebcamPictureStream : public IDataSourceStream
{
public:
/**
* @param camera reference to the camera object.
*/
WebcamPictureStream(CameraInterface& camera, size_t blockSize = 512) : camera(camera), blockSize(blockSize)
{
}

uint16_t readMemoryBlock(char* data, int bufSize)
{
if(camera.getState() == eWCS_HAS_PICTURE) {
if(!size) {
size = camera.getSize();
offset = 0;
}

return camera.read(data, bufSize, offset);
}

return 0;
}

int available()
{
return size;
}

bool seek(int len)
{
offset += len; // TODO: check for invalid offsets
return true;
}

bool isFinished()
{
bool finished = (size && offset >= size);
if(finished) {
camera.next();
}
return finished;
}

private:
CameraInterface& camera;
size_t blockSize;
size_t size = 0;
size_t offset = 0;
};
74 changes: 30 additions & 44 deletions Sming/Libraries/WebCam/src/WebcamStream.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,68 +12,54 @@

#pragma once

#include <Data/Stream/DataSourceStream.h>
#include <Data/Stream/MultipartStream.h>
#include "Camera/CameraInterface.h"
#include "WebcamPictureStream.h"

class WebcamStream: public IDataSourceStream
/**
* @brief Webcam stream producer for multipart streaming
*/
class WebcamStream : public MultipartStream
{
public:
/**
* @param camera pointer to the camera object.
*/
WebcamStream(CameraInterface* camera, size_t blockSize = 512): camera(camera), blockSize(blockSize)
WebcamStream(CameraInterface& camera) : MultipartStream(std::bind(&WebcamStream::produce, this)), camera(camera)
{
assert(camera != nullptr);
}

uint16_t readMemoryBlock(char* data, int bufSize)
uint16_t readMemoryBlock(char* data, int bufSize) override
{
if(camera->getState() == eWCS_HAS_PICTURE) {
if(!size) {
size = camera->getSize();
offset = 0;
if(camera.getState() == eWCS_READY) {
uint8_t fps = camera.getFramesPerSecond();
if(fps > 0 && lastFrameTime) {
uint16_t frameTimeMs = 1000 / fps;
uint32_t now = millis();
if(now < lastFrameTime + frameTimeMs) {
debug_d("Skipping frame to maintain fps");
return 0;
}
}

return camera->read(data, bufSize, offset);
camera.capture();
lastFrameTime = millis();
}

if(camera->getState() != eWCS_WORKING) {
if(camera->capture()) {
size = 0;
offset = 0;
}
}

return 0;
return MultipartStream::readMemoryBlock(data, bufSize);
}

int available()
MultipartStream::BodyPart produce()
{
return size;
}
MultipartStream::BodyPart result;

bool seek(int len)
{
offset += len; // TODO: check for invalid offsets
return true;
}
WebcamPictureStream* webcamStream = new WebcamPictureStream(camera);
result.stream = webcamStream;

bool isFinished()
{
bool finished = (size && offset >= size);
if(finished) {
camera->next();
}
return finished;
}
result.headers = new HttpHeaders();
(*result.headers)[HTTP_HEADER_CONTENT_TYPE] = camera.getMimeType();

~WebcamStream()
{
camera = nullptr;
return result;
}

private:
CameraInterface* camera = nullptr;
size_t blockSize;
size_t size = 0;
size_t offset = 0;
CameraInterface& camera;
unsigned long lastFrameTime = 0;
};
26 changes: 6 additions & 20 deletions samples/WebcamServer/app/application.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#include <SmingCore.h>

#include <Data/Stream/MultipartStream.h>
#include <WebcamStream.h>
#include <Camera/FakeCamera.h>

Expand All @@ -13,7 +12,7 @@
namespace
{
HttpServer server;
FakeCamera* camera;
FakeCamera camera;

/*
* default http handler to check if server is up and running
Expand All @@ -36,24 +35,11 @@ void onFile(HttpRequest& request, HttpResponse& response)
}
}

MultipartStream::BodyPart snapshotProducer()
{
MultipartStream::BodyPart result;

WebcamStream* webcamStream = new WebcamStream(camera);
result.stream = webcamStream;

result.headers = new HttpHeaders();
(*result.headers)[HTTP_HEADER_CONTENT_TYPE] = camera->getMimeType();

return result;
}

void onStream(HttpRequest& request, HttpResponse& response)
{
Serial.println(_F("perform onCapture()"));

MultipartStream* stream = new MultipartStream(snapshotProducer);
WebcamStream* stream = new WebcamStream(camera);
response.sendDataStream(stream, F("multipart/x-mixed-replace; boundary=") + stream->getBoundary());
}

Expand All @@ -66,15 +52,15 @@ void onFavicon(HttpRequest& request, HttpResponse& response)
void startWebServer()
{
// Initialize the camera
Vector<String> images;
for(unsigned int i = 1; i < 6; i++) {
String s = "img";
s.concat(i, DEC, 2);
s += ".jpeg";
images.add(s);
camera.addImage(s);
}
camera = new FakeCamera(images);
camera->init();

spiffs_mount(); // Mount file system, in order to work with files
camera.init();

// .. and run the HTTP server
server.listen(80);
Expand Down
Loading