Implement capture
This commit is contained in:
94
api.cpp
94
api.cpp
@@ -3,6 +3,7 @@
|
|||||||
#include <emscripten/val.h>
|
#include <emscripten/val.h>
|
||||||
#include <gphoto2/gphoto2.h>
|
#include <gphoto2/gphoto2.h>
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
using emscripten::val;
|
using emscripten::val;
|
||||||
@@ -55,6 +56,8 @@ auto gpp_rethrow(Func func) {
|
|||||||
|
|
||||||
const thread_local val Uint8Array = val::global("Uint8Array");
|
const thread_local val Uint8Array = val::global("Uint8Array");
|
||||||
const thread_local val Blob = val::global("Blob");
|
const thread_local val Blob = val::global("Blob");
|
||||||
|
const thread_local val File = val::global("File");
|
||||||
|
const thread_local val arrayOf = val::global("Array")["of"];
|
||||||
|
|
||||||
class Context {
|
class Context {
|
||||||
public:
|
public:
|
||||||
@@ -114,61 +117,44 @@ class Context {
|
|||||||
|
|
||||||
val capturePreviewAsBlob() {
|
val capturePreviewAsBlob() {
|
||||||
return gpp_rethrow([=]() {
|
return gpp_rethrow([=]() {
|
||||||
class Output {
|
auto &file = get_file();
|
||||||
public:
|
|
||||||
Output() : totalSize(0), chunks(val::array()) {}
|
|
||||||
|
|
||||||
void push(const uint8_t *data, unsigned long size) {
|
gpp_try(gp_camera_capture_preview(camera.get(), &file, context.get()));
|
||||||
chunks.call<void>(
|
|
||||||
"push",
|
auto params = blob_chunks_and_opts(file);
|
||||||
Uint8Array.new_(emscripten::typed_memory_view(size, data)));
|
return Blob.new_(std::move(params.first), std::move(params.second));
|
||||||
totalSize += size;
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t getTotalSize() { return totalSize; }
|
val captureImageAsFile() {
|
||||||
|
return gpp_rethrow([=]() {
|
||||||
|
CameraFilePath camera_file_path;
|
||||||
|
strcpy(camera_file_path.folder, "/");
|
||||||
|
strcpy(camera_file_path.name, "web-gphoto2");
|
||||||
|
|
||||||
val getChunks() { return chunks; }
|
gpp_try(gp_camera_capture(camera.get(), GP_CAPTURE_IMAGE,
|
||||||
|
&camera_file_path, context.get()));
|
||||||
|
|
||||||
private:
|
auto &file = get_file();
|
||||||
uint64_t totalSize;
|
gpp_try(gp_camera_file_get(camera.get(), camera_file_path.folder,
|
||||||
val chunks;
|
camera_file_path.name, GP_FILE_TYPE_NORMAL,
|
||||||
};
|
&file, context.get()));
|
||||||
|
gpp_try(gp_camera_file_delete(camera.get(), camera_file_path.folder,
|
||||||
|
camera_file_path.name, context.get()));
|
||||||
|
|
||||||
static CameraFileHandler handler = {
|
gpp_try(gp_file_set_name(&file, "image."));
|
||||||
.size =
|
gpp_try(gp_file_adjust_name_for_mime_type(&file));
|
||||||
[](void *priv, uint64_t *size) {
|
auto name = val(GPP_CALL(const char *, gp_file_get_name(&file, _)));
|
||||||
*size = static_cast<Output *>(priv)->getTotalSize();
|
|
||||||
return GP_OK;
|
|
||||||
},
|
|
||||||
.read = [](void *priv, unsigned char *data,
|
|
||||||
uint64_t *len) { return GP_ERROR_NOT_SUPPORTED; },
|
|
||||||
.write =
|
|
||||||
[](void *priv, unsigned char *data, uint64_t *len) {
|
|
||||||
static_cast<Output *>(priv)->push(data, *len);
|
|
||||||
return GP_OK;
|
|
||||||
}};
|
|
||||||
|
|
||||||
Output output;
|
auto params = blob_chunks_and_opts(file);
|
||||||
|
return File.new_(std::move(params.first), std::move(name), std::move(params.second));
|
||||||
gpp_unique_ptr<CameraFile, gp_file_unref> file(GPP_CALL(
|
|
||||||
CameraFile *, gp_file_new_from_handler(_, &handler, &output)));
|
|
||||||
|
|
||||||
gpp_try(
|
|
||||||
gp_camera_capture_preview(camera.get(), file.get(), context.get()));
|
|
||||||
|
|
||||||
auto mime_type =
|
|
||||||
GPP_CALL(const char *, gp_file_get_mime_type(file.get(), _));
|
|
||||||
|
|
||||||
val blob_opts = val::object();
|
|
||||||
blob_opts.set("type", mime_type);
|
|
||||||
|
|
||||||
return Blob.new_(output.getChunks(), std::move(blob_opts));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
gpp_unique_ptr<Camera, gp_camera_unref> camera;
|
gpp_unique_ptr<Camera, gp_camera_unref> camera;
|
||||||
gpp_unique_ptr<GPContext, gp_context_unref> context;
|
gpp_unique_ptr<GPContext, gp_context_unref> context;
|
||||||
|
gpp_unique_ptr<CameraFile, gp_file_unref> file;
|
||||||
|
|
||||||
static std::pair<val, val> walk_config(CameraWidget *widget) {
|
static std::pair<val, val> walk_config(CameraWidget *widget) {
|
||||||
val result = val::object();
|
val result = val::object();
|
||||||
@@ -246,6 +232,27 @@ class Context {
|
|||||||
|
|
||||||
return {name, result};
|
return {name, result};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CameraFile &get_file() {
|
||||||
|
if (file.get() == nullptr) {
|
||||||
|
file.reset(GPP_CALL(CameraFile *, gp_file_new(_)));
|
||||||
|
}
|
||||||
|
return *file;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<val, val> blob_chunks_and_opts(CameraFile &file) {
|
||||||
|
auto mime_type = GPP_CALL(const char *, gp_file_get_mime_type(&file, _));
|
||||||
|
|
||||||
|
const char *data;
|
||||||
|
unsigned long size;
|
||||||
|
gpp_try(gp_file_get_data_and_size(&file, &data, &size));
|
||||||
|
|
||||||
|
val blob_opts = val::object();
|
||||||
|
blob_opts.set("type", mime_type);
|
||||||
|
|
||||||
|
return {arrayOf(Uint8Array.new_(emscripten::typed_memory_view(size, data))),
|
||||||
|
std::move(blob_opts)};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
EMSCRIPTEN_BINDINGS(gphoto2_js_api) {
|
EMSCRIPTEN_BINDINGS(gphoto2_js_api) {
|
||||||
@@ -253,5 +260,6 @@ EMSCRIPTEN_BINDINGS(gphoto2_js_api) {
|
|||||||
.constructor<>()
|
.constructor<>()
|
||||||
.function("configToJS", &Context::configToJS)
|
.function("configToJS", &Context::configToJS)
|
||||||
.function("setConfigValue", &Context::setConfigValue)
|
.function("setConfigValue", &Context::setConfigValue)
|
||||||
.function("capturePreviewAsBlob", &Context::capturePreviewAsBlob);
|
.function("capturePreviewAsBlob", &Context::capturePreviewAsBlob)
|
||||||
|
.function("captureImageAsFile", &Context::captureImageAsFile);
|
||||||
}
|
}
|
||||||
|
|||||||
13
ui.html
13
ui.html
@@ -2,6 +2,7 @@
|
|||||||
<link
|
<link
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
href="https://unpkg.com/purecss@2.0.6/build/pure-min.css"
|
href="https://unpkg.com/purecss@2.0.6/build/pure-min.css"
|
||||||
|
crossorigin="anonymous"
|
||||||
/>
|
/>
|
||||||
<style>
|
<style>
|
||||||
#config label {
|
#config label {
|
||||||
@@ -110,7 +111,7 @@
|
|||||||
choices.map(choice =>
|
choices.map(choice =>
|
||||||
h(
|
h(
|
||||||
'option',
|
'option',
|
||||||
{ value: choice, disabled: attrs.readonly && value !== choice },
|
{ key: choice, value: choice, disabled: attrs.readonly && value !== choice },
|
||||||
choice
|
choice
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -182,6 +183,15 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleCapture = async () => {
|
||||||
|
let file = await scheduleOp(() => context.captureImageAsFile());
|
||||||
|
let a = document.createElement('a');
|
||||||
|
a.download = file.name;
|
||||||
|
a.href = URL.createObjectURL(file);
|
||||||
|
a.click();
|
||||||
|
URL.revokeObjectURL(a.href);
|
||||||
|
}
|
||||||
|
|
||||||
render(props, state) {
|
render(props, state) {
|
||||||
return typeof config === 'string'
|
return typeof config === 'string'
|
||||||
? config
|
? config
|
||||||
@@ -193,6 +203,7 @@
|
|||||||
onfocusout: this.resumeUpdates,
|
onfocusout: this.resumeUpdates,
|
||||||
onchange: this.handleChange
|
onchange: this.handleChange
|
||||||
},
|
},
|
||||||
|
h('input', { type: 'button', value: 'Capture image', onclick: this.handleCapture }),
|
||||||
h(Config, state)
|
h(Config, state)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user