<commit_before>/*
 * Copyright 2006 The Android Open Source Project
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkRect.h"

#include "SkMalloc.h"

void SkIRect::join(int32_t left, int32_t top, int32_t right, int32_t bottom) {
    // do nothing if the params are empty
    if (left >= right || top >= bottom) {
        return;
    }

    // if we are empty, just assign
    if (fLeft >= fRight || fTop >= fBottom) {
        this->set(left, top, right, bottom);
    } else {
        if (left < fLeft) fLeft = left;
        if (top < fTop) fTop = top;
        if (right > fRight) fRight = right;
        if (bottom > fBottom) fBottom = bottom;
    }
}

/////////////////////////////////////////////////////////////////////////////

void SkRect::toQuad(SkPoint quad[4]) const {
    SkASSERT(quad);

    quad[0].set(fLeft, fTop);
    quad[1].set(fRight, fTop);
    quad[2].set(fRight, fBottom);
    quad[3].set(fLeft, fBottom);
}

#include "SkNx.h"

static inline bool is_finite(const Sk4s& value) {
    auto finite = value * Sk4s(0) == Sk4s(0);
    return finite.allTrue();
}

bool SkRect::setBoundsCheck(const SkPoint pts[], int count) {
    SkASSERT((pts && count > 0) || count == 0);

    bool isFinite = true;

    if (count <= 0) {
        sk_bzero(this, sizeof(SkRect));
    } else {
        Sk4s min, max, accum;

        if (count & 1) {
            min = Sk4s(pts[0].fX, pts[0].fY, pts[0].fX, pts[0].fY);
            pts += 1;
            count -= 1;
        } else {
            min = Sk4s::Load(pts);
            pts += 2;
            count -= 2;
        }
        accum = max = min;
        accum = accum * Sk4s(0);

        count >>= 1;
        for (int i = 0; i < count; ++i) {
            Sk4s xy = Sk4s::Load(pts);
            accum = accum * xy;
            min = Sk4s::Min(min, xy);
            max = Sk4s::Max(max, xy);
            pts += 2;
        }

        /**
         *  With some trickery, we may be able to use Min/Max to also propogate non-finites,
         *  in which case we could eliminate accum entirely, and just check min and max for
         *  "is_finite".
         */
        if (is_finite(accum)) {
            float minArray[4], maxArray[4];
            min.store(minArray);
            max.store(maxArray);
            this->set(SkTMin(minArray[0], minArray[2]), SkTMin(minArray[1], minArray[3]),
                      SkTMax(maxArray[0], maxArray[2]), SkTMax(maxArray[1], maxArray[3]));
        } else {
            // we hit a non-finite value, so zero everything and return false
            this->setEmpty();
            isFinite = false;
        }
    }
    return isFinite;
}

void SkRect::setBoundsNoCheck(const SkPoint pts[], int count) {
    SkASSERT((pts && count > 0) || count == 0);

    if (count <= 0) {
        sk_bzero(this, sizeof(SkRect));
    } else {
        Sk4s min, max, accum;

        if (count & 1) {
            min = Sk4s(pts[0].fX, pts[0].fY, pts[0].fX, pts[0].fY);
            pts += 1;
            count -= 1;
        } else {
            min = Sk4s::Load(pts);
            pts += 2;
            count -= 2;
        }
        accum = max = min;
        accum = accum * Sk4s(0);

        count >>= 1;
        for (int i = 0; i < count; ++i) {
            Sk4s xy = Sk4s::Load(pts);
            accum = accum * xy;
            min = Sk4s::Min(min, xy);
            max = Sk4s::Max(max, xy);
            pts += 2;
        }

        float minArray[4], maxArray[4];
        min.store(minArray);
        max.store(maxArray);
        this->set(SkTMin(minArray[0], minArray[2]), SkTMin(minArray[1], minArray[3]),
                  SkTMax(maxArray[0], maxArray[2]), SkTMax(maxArray[1], maxArray[3]));

        if (!is_finite(accum)) {
            this->set(SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN);
        }
    }
}

#define CHECK_INTERSECT(al, at, ar, ab, bl, bt, br, bb) \
    SkScalar L = SkMaxScalar(al, bl);                   \
    SkScalar R = SkMinScalar(ar, br);                   \
    SkScalar T = SkMaxScalar(at, bt);                   \
    SkScalar B = SkMinScalar(ab, bb);                   \
    do { if (!(L < R && T < B)) return false; } while (0)
    // do the !(opposite) check so we return false if either arg is NaN

bool SkRect::intersect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
    CHECK_INTERSECT(left, top, right, bottom, fLeft, fTop, fRight, fBottom);
    this->setLTRB(L, T, R, B);
    return true;
}

bool SkRect::intersect(const SkRect& r) {
    return this->intersect(r.fLeft, r.fTop, r.fRight, r.fBottom);
}

bool SkRect::intersect(const SkRect& a, const SkRect& b) {
    CHECK_INTERSECT(a.fLeft, a.fTop, a.fRight, a.fBottom, b.fLeft, b.fTop, b.fRight, b.fBottom);
    this->setLTRB(L, T, R, B);
    return true;
}

void SkRect::join(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
    // do nothing if the params are empty
    if (left >= right || top >= bottom) {
        return;
    }

    // if we are empty, just assign
    if (fLeft >= fRight || fTop >= fBottom) {
        this->set(left, top, right, bottom);
    } else {
        fLeft   = SkMinScalar(fLeft, left);
        fTop    = SkMinScalar(fTop, top);
        fRight  = SkMaxScalar(fRight, right);
        fBottom = SkMaxScalar(fBottom, bottom);
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////

#include "SkString.h"
#include "SkStringUtils.h"

static const char* set_scalar(SkString* storage, SkScalar value, SkScalarAsStringType asType) {
    storage->reset();
    SkAppendScalar(storage, value, asType);
    return storage->c_str();
}

void SkRect::dump(bool asHex) const {
    SkScalarAsStringType asType = asHex ? kHex_SkScalarAsStringType : kDec_SkScalarAsStringType;

    SkString line;
    if (asHex) {
        SkString tmp;
        line.printf( "SkRect::MakeLTRB(%s, /* %f */\n", set_scalar(&tmp, fLeft, asType), fLeft);
        line.appendf("                 %s, /* %f */\n", set_scalar(&tmp, fTop, asType), fTop);
        line.appendf("                 %s, /* %f */\n", set_scalar(&tmp, fRight, asType), fRight);
        line.appendf("                 %s  /* %f */);", set_scalar(&tmp, fBottom, asType), fBottom);
    } else {
        SkString strL, strT, strR, strB;
        SkAppendScalarDec(&strL, fLeft);
        SkAppendScalarDec(&strT, fTop);
        SkAppendScalarDec(&strR, fRight);
        SkAppendScalarDec(&strB, fBottom);
        line.printf("SkRect::MakeLTRB(%s, %s, %s, %s);",
                    strL.c_str(), strT.c_str(), strR.c_str(), strB.c_str());
    }
    SkDebugf("%s\n", line.c_str());
}
<commit_msg>refactor setBoundsCheck() a bit<commit_after>/*
 * Copyright 2006 The Android Open Source Project
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkRect.h"

#include "SkMalloc.h"

void SkIRect::join(int32_t left, int32_t top, int32_t right, int32_t bottom) {
    // do nothing if the params are empty
    if (left >= right || top >= bottom) {
        return;
    }

    // if we are empty, just assign
    if (fLeft >= fRight || fTop >= fBottom) {
        this->set(left, top, right, bottom);
    } else {
        if (left < fLeft) fLeft = left;
        if (top < fTop) fTop = top;
        if (right > fRight) fRight = right;
        if (bottom > fBottom) fBottom = bottom;
    }
}

/////////////////////////////////////////////////////////////////////////////

void SkRect::toQuad(SkPoint quad[4]) const {
    SkASSERT(quad);

    quad[0].set(fLeft, fTop);
    quad[1].set(fRight, fTop);
    quad[2].set(fRight, fBottom);
    quad[3].set(fLeft, fBottom);
}

#include "SkNx.h"

bool SkRect::setBoundsCheck(const SkPoint pts[], int count) {
    SkASSERT((pts && count > 0) || count == 0);

    if (count <= 0) {
        this->setEmpty();
        return true;
    }

    Sk4s min, max;
    if (count & 1) {
        min = max = Sk4s(pts->fX, pts->fY,
                         pts->fX, pts->fY);
        pts   += 1;
        count -= 1;
    } else {
        min = max = Sk4s::Load(pts);
        pts   += 2;
        count -= 2;
    }

    Sk4s accum = min * 0;
    while (count) {
        Sk4s xy = Sk4s::Load(pts);
        accum = accum * xy;
        min = Sk4s::Min(min, xy);
        max = Sk4s::Max(max, xy);
        pts   += 2;
        count -= 2;
    }

    bool all_finite = (accum * 0 == 0).allTrue();
    if (all_finite) {
        this->set(SkTMin(min[0], min[2]), SkTMin(min[1], min[3]),
                  SkTMax(max[0], max[2]), SkTMax(max[1], max[3]));
    } else {
        this->setEmpty();
    }
    return all_finite;
}

void SkRect::setBoundsNoCheck(const SkPoint pts[], int count) {
    if (!this->setBoundsCheck(pts, count)) {
        this->set(SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN);
    }
}

#define CHECK_INTERSECT(al, at, ar, ab, bl, bt, br, bb) \
    SkScalar L = SkMaxScalar(al, bl);                   \
    SkScalar R = SkMinScalar(ar, br);                   \
    SkScalar T = SkMaxScalar(at, bt);                   \
    SkScalar B = SkMinScalar(ab, bb);                   \
    do { if (!(L < R && T < B)) return false; } while (0)
    // do the !(opposite) check so we return false if either arg is NaN

bool SkRect::intersect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
    CHECK_INTERSECT(left, top, right, bottom, fLeft, fTop, fRight, fBottom);
    this->setLTRB(L, T, R, B);
    return true;
}

bool SkRect::intersect(const SkRect& r) {
    return this->intersect(r.fLeft, r.fTop, r.fRight, r.fBottom);
}

bool SkRect::intersect(const SkRect& a, const SkRect& b) {
    CHECK_INTERSECT(a.fLeft, a.fTop, a.fRight, a.fBottom, b.fLeft, b.fTop, b.fRight, b.fBottom);
    this->setLTRB(L, T, R, B);
    return true;
}

void SkRect::join(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
    // do nothing if the params are empty
    if (left >= right || top >= bottom) {
        return;
    }

    // if we are empty, just assign
    if (fLeft >= fRight || fTop >= fBottom) {
        this->set(left, top, right, bottom);
    } else {
        fLeft   = SkMinScalar(fLeft, left);
        fTop    = SkMinScalar(fTop, top);
        fRight  = SkMaxScalar(fRight, right);
        fBottom = SkMaxScalar(fBottom, bottom);
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////

#include "SkString.h"
#include "SkStringUtils.h"

static const char* set_scalar(SkString* storage, SkScalar value, SkScalarAsStringType asType) {
    storage->reset();
    SkAppendScalar(storage, value, asType);
    return storage->c_str();
}

void SkRect::dump(bool asHex) const {
    SkScalarAsStringType asType = asHex ? kHex_SkScalarAsStringType : kDec_SkScalarAsStringType;

    SkString line;
    if (asHex) {
        SkString tmp;
        line.printf( "SkRect::MakeLTRB(%s, /* %f */\n", set_scalar(&tmp, fLeft, asType), fLeft);
        line.appendf("                 %s, /* %f */\n", set_scalar(&tmp, fTop, asType), fTop);
        line.appendf("                 %s, /* %f */\n", set_scalar(&tmp, fRight, asType), fRight);
        line.appendf("                 %s  /* %f */);", set_scalar(&tmp, fBottom, asType), fBottom);
    } else {
        SkString strL, strT, strR, strB;
        SkAppendScalarDec(&strL, fLeft);
        SkAppendScalarDec(&strT, fTop);
        SkAppendScalarDec(&strR, fRight);
        SkAppendScalarDec(&strB, fBottom);
        line.printf("SkRect::MakeLTRB(%s, %s, %s, %s);",
                    strL.c_str(), strT.c_str(), strR.c_str(), strB.c_str());
    }
    SkDebugf("%s\n", line.c_str());
}
<|endoftext|>


<commit_before>#include "blackmagicsdk_video_source.h"
#include "deck_link_display_mode_detector.h"
#include <chrono>

namespace gg
{

VideoSourceBlackmagicSDK::VideoSourceBlackmagicSDK()
    : IVideoSource()
    , _frame_rate(0.0)
    , _video_buffer(nullptr)
    , _video_buffer_length(0)
    , _cols(0)
    , _rows(0)
    , _buffer_video_frame(VideoFrame(UYVY))
    , _running(false)
{
    // nop
}

VideoSourceBlackmagicSDK::VideoSourceBlackmagicSDK(size_t deck_link_index,
                                                   ColourSpace colour)
    : IVideoSource(colour)
    , _frame_rate(0.0)
    , _video_buffer(nullptr)
    , _video_buffer_length(0)
    , _cols(0)
    , _rows(0)
    , _buffer_video_frame(VideoFrame(colour, false)) // TODO manage data?
    , _deck_link(nullptr)
    , _deck_link_input(nullptr)
    , _video_input_flags(bmdVideoInputFlagDefault | bmdVideoInputDualStream3D)
    , _12_bit_rgb_to_bgra_converter(nullptr)
    , _bgra_frame_buffers({nullptr, nullptr})
    , _running(false)
{
    // Pixel format, i.e. colour space
    BMDPixelFormat pixel_format;
    switch(_colour)
    {
    case UYVY:
        pixel_format = bmdFormat8BitYUV;
        break;
    case BGRA:
        // We currently only support BGRA with DeckLink 4K Extreme 12G,
        // and that card supports only this YUV format:
        pixel_format = bmdFormat10BitYUV;
        break;
    case I420:
    default:
        bail("BlackmagicSDK video source supports only UYVY and BGRA colour spaces");
    }

    // Get an iterator through the available DeckLink ports
    IDeckLinkIterator * deck_link_iterator = CreateDeckLinkIteratorInstance();
    if (deck_link_iterator == nullptr)
        bail("DeckLink drivers do not appear to be installed");

    HRESULT res;

    // Get the desired DeckLink index (port)
    int idx = deck_link_index;
    while ((res = deck_link_iterator->Next(&_deck_link)) == S_OK)
    {
        if (idx == 0)
            break;
        --idx;

        _deck_link->Release();
    }
    if (deck_link_iterator != nullptr)
        deck_link_iterator->Release();
    // No glory: release everything and throw exception
    if (res != S_OK or _deck_link == nullptr)
        bail(
            std::string("Could not get DeckLink device ").append(std::to_string(deck_link_index))
        );

    // Get the input interface of connected DeckLink port
    res = _deck_link->QueryInterface(IID_IDeckLinkInput, reinterpret_cast<void **>(&_deck_link_input));
    // No glory: release everything and throw exception
    if (res != S_OK)
        bail("Could not get the Blackmagic DeckLink input interface");

    // Set the input format (i.e. display mode)
    BMDDisplayMode display_mode;
    BMDFrameFlags frame_flags;
    std::string error_msg = "";
    if (not detect_input_format(pixel_format, _video_input_flags, display_mode,
                                _frame_rate, _cols, _rows, frame_flags, error_msg))
    {
        _video_input_flags ^= bmdVideoInputDualStream3D;
        if (not detect_input_format(pixel_format, _video_input_flags, display_mode,
                                    _frame_rate, _cols, _rows, frame_flags, error_msg))
            bail(error_msg);
    }

    // Set this object (IDeckLinkInputCallback instance) as callback
    res = _deck_link_input->SetCallback(this);
    // No glory: release everything and throw exception
    if (res != S_OK)
        bail("Could not set the callback of Blackmagic DeckLink device");

    // Enable video input
    res = _deck_link_input->EnableVideoInput(display_mode,
                                             pixel_format,
                                             _video_input_flags);
    // No glory
    if (res != S_OK)
        bail("Could not enable video input of Blackmagic DeckLink device");

    // Allocate pixel buffer
    _video_buffer_length = VideoFrame::required_data_length(_colour, _cols, _rows);
    if (is_stereo())
        _video_buffer_length *= 2;
    _video_buffer = reinterpret_cast<uint8_t *>(
        realloc(_video_buffer, _video_buffer_length * sizeof(uint8_t))
    );

    // Colour converter for post-capture colour conversion
    if (_colour == BGRA)
    {
        _12_bit_rgb_to_bgra_converter = CreateVideoConversionInstance();
        if (_12_bit_rgb_to_bgra_converter == nullptr)
            bail("Could not create colour converter for Blackmagic source");
        for (size_t i = 0; i < is_stereo() ? 2 : 1; i++)
            _bgra_frame_buffers[i] = new DeckLinkBGRAVideoFrame(
                _cols, _rows,
                &_video_buffer[i * _video_buffer_length / 2], frame_flags
            );
    }

    // Start streaming
    _running = true;
    res = _deck_link_input->StartStreams();
    // No glory: release everything and throw exception
    if (res != S_OK)
    {
        _running = false;
        bail("Could not start streaming from the Blackmagic DeckLink device");
    }
}

VideoSourceBlackmagicSDK::~VideoSourceBlackmagicSDK()
{
    { // Artificial scope for data lock
        // Make sure streamer thread not trying to access buffer
        std::lock_guard<std::mutex> data_lock_guard(_data_lock);
        _running = false;
    }

    // Stop streaming and disable enabled inputs
    _deck_link_input->SetCallback(nullptr);
    _deck_link_input->StopStreams();
    _deck_link_input->DisableVideoInput();

    // Release DeckLink members
    release_deck_link();
}

bool VideoSourceBlackmagicSDK::get_frame_dimensions(int & width, int & height)
{
    // Make sure only this thread is accessing the cols and rows members now
    std::lock_guard<std::mutex> data_lock_guard(_data_lock);
    if (_cols <= 0 or _rows <= 0)
        return false;

    width = _cols;
    height = _rows;
    return true;
}

bool VideoSourceBlackmagicSDK::get_frame(VideoFrame & frame)
{
    if (frame.colour() != _colour)
        return false;

    // Make sure only this thread is accessing the video frame data now
    std::lock_guard<std::mutex> data_lock_guard(_data_lock);

    if (_video_buffer_length > 0 and _cols > 0 and _rows > 0)
    {
        frame.init_from_specs(_video_buffer, _video_buffer_length, _cols, _rows);
        return true;
    }
    else
        return false;
}

double VideoSourceBlackmagicSDK::get_frame_rate()
{
    return _frame_rate;
}

void VideoSourceBlackmagicSDK::set_sub_frame(int x, int y, int width, int height)
{
    // issue #147
    throw VideoSourceError("Blackmagic does not support cropping yet");
}

void VideoSourceBlackmagicSDK::get_full_frame()
{
    // nop: set_sub_frame currently not implemented
}

HRESULT STDMETHODCALLTYPE VideoSourceBlackmagicSDK::QueryInterface(REFIID iid, LPVOID * ppv)
{
    return E_NOINTERFACE;
}

ULONG STDMETHODCALLTYPE VideoSourceBlackmagicSDK::AddRef(void)
{
    __sync_add_and_fetch(&_n_ref, 1);
    return _n_ref;
}

ULONG STDMETHODCALLTYPE VideoSourceBlackmagicSDK::Release(void)
{
    __sync_sub_and_fetch(&_n_ref, 1);
    return _n_ref;
}

HRESULT STDMETHODCALLTYPE VideoSourceBlackmagicSDK::VideoInputFormatChanged(
    BMDVideoInputFormatChangedEvents events,
    IDeckLinkDisplayMode * display_mode,
    BMDDetectedVideoInputFormatFlags format_flags
)
{
    // not supported yet: see issue #149
    return S_OK;
}

HRESULT STDMETHODCALLTYPE VideoSourceBlackmagicSDK::VideoInputFrameArrived(
    IDeckLinkVideoInputFrame * video_frame,
    IDeckLinkAudioInputPacket * audio_packet
)
{
    if (not _running)
        // nop if not running!
        return S_OK;

    // Not processing the audio packet, but only the video
    // frame for the time being
    if (video_frame == nullptr)
        // nop if no data
        return S_OK;

    // Nr. of bytes of received data
    size_t n_bytes = VideoFrame::required_data_length(
        _colour, video_frame->GetWidth(), video_frame->GetHeight()
    );
    if (is_stereo())
        n_bytes *= 2;

    { // Artificial scope for data lock
        // Make sure only this thread is accessing the buffer now
        std::lock_guard<std::mutex> data_lock_guard(_data_lock);

        // Extend buffer if more memory needed than already allocated
        if (n_bytes > _video_buffer_length or _cols != video_frame->GetWidth()
            or _rows != video_frame->GetHeight())
        {
            _video_buffer = reinterpret_cast<uint8_t *>(
                realloc(_video_buffer, n_bytes * sizeof(uint8_t))
            );

            // Set video frame specs according to new data
            _video_buffer_length = n_bytes;
            _cols = video_frame->GetWidth();
            _rows = video_frame->GetHeight();

            for (size_t i = 0; i < is_stereo() ? 2 : 1; i++)
            {
                if (_bgra_frame_buffers[i] != nullptr)
                {
                    _bgra_frame_buffers[i]->Release();
                    delete _bgra_frame_buffers[i];
                    _bgra_frame_buffers[i] = new DeckLinkBGRAVideoFrame(
                        _cols, _rows,
                        &_video_buffer[i * _video_buffer_length / 2], frame_flags
                    );
                }
            }
        }
        if (_video_buffer == nullptr) // something's terribly wrong!
            // nop if something's terribly wrong!
            return S_OK;

        HRESULT res;

        if (need_conversion())
            // convert to BGRA from capture format
            res = _12_bit_rgb_to_bgra_converter->ConvertFrame(
                video_frame, _bgra_frame_buffers[0]
            );
        else
            // Get the new data into the buffer
            res = video_frame->GetBytes(
                reinterpret_cast<void **>(&_video_buffer)
            );

        // If the last operation failed, return
        if (FAILED(res))
            return res;

        if (is_stereo())
        {
            IDeckLinkVideoFrame *right_eye_frame = nullptr;
            IDeckLinkVideoFrame3DExtensions *three_d_extensions = nullptr;
            if ((video_frame->QueryInterface(
                    IID_IDeckLinkVideoFrame3DExtensions,
                    (void **) &three_d_extensions) != S_OK) ||
                (three_d_extensions->GetFrameForRightEye(
                    &right_eye_frame) != S_OK))
            {
                right_eye_frame = nullptr;
            }

            if (three_d_extensions != nullptr)
                three_d_extensions->Release();

            if (right_eye_frame != nullptr)
            {
                if (need_conversion())
                    // convert to BGRA from capture format
                    res = _12_bit_rgb_to_bgra_converter->ConvertFrame(
                        right_eye_frame, _bgra_frame_buffers[1]
                    );
                else
                    res = right_eye_frame->GetBytes(
                        reinterpret_cast<void **>(&_video_buffer[n_bytes / 2])
                    );
                right_eye_frame->Release();
                // If data could not be read into the buffer, return
                if (FAILED(res))
                    return res;
            }
        }

        // Propagate new video frame to observers
        _buffer_video_frame.init_from_specs(
            _video_buffer, _video_buffer_length, _cols, _rows,
            is_stereo() ? 2 : 1
        );
    }

    this->notify(_buffer_video_frame);

    // Everything went fine, return success
    return S_OK;
}

void VideoSourceBlackmagicSDK::release_deck_link() noexcept
{
    if (_deck_link_input != nullptr)
    {
        _deck_link_input->Release();
        _deck_link_input = nullptr;
    }

    if (_deck_link != nullptr)
        _deck_link->Release();

    if (_12_bit_rgb_to_bgra_converter != nullptr)
    {
        _12_bit_rgb_to_bgra_converter->Release();
        _12_bit_rgb_to_bgra_converter = nullptr;
    }

    for (size_t i = 0; i < 2; i++)
        if (_bgra_frame_buffers[i] != nullptr)
        {
            _bgra_frame_buffers[i]->Release();
            _bgra_frame_buffers[i] = nullptr;
        }
}

bool VideoSourceBlackmagicSDK::detect_input_format(BMDPixelFormat pixel_format,
                                                   BMDVideoInputFlags & video_input_flags,
                                                   BMDDisplayMode & display_mode,
                                                   double & frame_rate,
                                                   size_t & cols, size_t & rows,
                                                   BMDFrameFlags & frame_flags,
                                                   std::string & error_msg) noexcept
{
    std::vector<BMDDisplayMode> display_modes =
    {
        bmdModeHD1080p6000, bmdModeHD1080p5994,
        bmdModeHD1080p50,
        bmdModeHD1080p30, bmdModeHD1080p2997,
        bmdModeHD1080p25, bmdModeHD1080p24, bmdModeHD1080p2398,
        bmdModeHD1080i6000, bmdModeHD1080i5994,
        bmdModeHD1080i50,
        bmdModeHD720p60, bmdModeHD720p5994,
        bmdModeHD720p50,
        bmdMode4K2160p60, bmdMode4K2160p5994,
        bmdMode4K2160p50,
        bmdMode4K2160p30, bmdMode4K2160p2997,
        bmdMode4K2160p25, bmdMode4K2160p24, bmdMode4K2160p2398,
        bmdMode2k25, bmdMode2k24, bmdMode2k2398,
    };

    DeckLinkDisplayModeDetector detector(
                _deck_link_input,
                display_modes, pixel_format, video_input_flags
    );
    BMDDisplayMode display_mode_ = detector.get_display_mode();
    if (display_mode_ != bmdModeUnknown)
    {
        frame_rate = detector.get_frame_rate();
        detector.get_frame_dimensions(cols, rows);
        frame_flags = detector.get_frame_flags();
        display_mode = display_mode_;
        video_input_flags = detector.get_video_input_flags();
        return true;
    }
    else
    {
        error_msg = detector.get_error_msg();
        return false;
    }
}

}
<commit_msg>Issue #30: fixed frame flags for reallocating internal BGRA buffers<commit_after>#include "blackmagicsdk_video_source.h"
#include "deck_link_display_mode_detector.h"
#include <chrono>

namespace gg
{

VideoSourceBlackmagicSDK::VideoSourceBlackmagicSDK()
    : IVideoSource()
    , _frame_rate(0.0)
    , _video_buffer(nullptr)
    , _video_buffer_length(0)
    , _cols(0)
    , _rows(0)
    , _buffer_video_frame(VideoFrame(UYVY))
    , _running(false)
{
    // nop
}

VideoSourceBlackmagicSDK::VideoSourceBlackmagicSDK(size_t deck_link_index,
                                                   ColourSpace colour)
    : IVideoSource(colour)
    , _frame_rate(0.0)
    , _video_buffer(nullptr)
    , _video_buffer_length(0)
    , _cols(0)
    , _rows(0)
    , _buffer_video_frame(VideoFrame(colour, false)) // TODO manage data?
    , _deck_link(nullptr)
    , _deck_link_input(nullptr)
    , _video_input_flags(bmdVideoInputFlagDefault | bmdVideoInputDualStream3D)
    , _12_bit_rgb_to_bgra_converter(nullptr)
    , _bgra_frame_buffers({nullptr, nullptr})
    , _running(false)
{
    // Pixel format, i.e. colour space
    BMDPixelFormat pixel_format;
    switch(_colour)
    {
    case UYVY:
        pixel_format = bmdFormat8BitYUV;
        break;
    case BGRA:
        // We currently only support BGRA with DeckLink 4K Extreme 12G,
        // and that card supports only this YUV format:
        pixel_format = bmdFormat10BitYUV;
        break;
    case I420:
    default:
        bail("BlackmagicSDK video source supports only UYVY and BGRA colour spaces");
    }

    // Get an iterator through the available DeckLink ports
    IDeckLinkIterator * deck_link_iterator = CreateDeckLinkIteratorInstance();
    if (deck_link_iterator == nullptr)
        bail("DeckLink drivers do not appear to be installed");

    HRESULT res;

    // Get the desired DeckLink index (port)
    int idx = deck_link_index;
    while ((res = deck_link_iterator->Next(&_deck_link)) == S_OK)
    {
        if (idx == 0)
            break;
        --idx;

        _deck_link->Release();
    }
    if (deck_link_iterator != nullptr)
        deck_link_iterator->Release();
    // No glory: release everything and throw exception
    if (res != S_OK or _deck_link == nullptr)
        bail(
            std::string("Could not get DeckLink device ").append(std::to_string(deck_link_index))
        );

    // Get the input interface of connected DeckLink port
    res = _deck_link->QueryInterface(IID_IDeckLinkInput, reinterpret_cast<void **>(&_deck_link_input));
    // No glory: release everything and throw exception
    if (res != S_OK)
        bail("Could not get the Blackmagic DeckLink input interface");

    // Set the input format (i.e. display mode)
    BMDDisplayMode display_mode;
    BMDFrameFlags frame_flags;
    std::string error_msg = "";
    if (not detect_input_format(pixel_format, _video_input_flags, display_mode,
                                _frame_rate, _cols, _rows, frame_flags, error_msg))
    {
        _video_input_flags ^= bmdVideoInputDualStream3D;
        if (not detect_input_format(pixel_format, _video_input_flags, display_mode,
                                    _frame_rate, _cols, _rows, frame_flags, error_msg))
            bail(error_msg);
    }

    // Set this object (IDeckLinkInputCallback instance) as callback
    res = _deck_link_input->SetCallback(this);
    // No glory: release everything and throw exception
    if (res != S_OK)
        bail("Could not set the callback of Blackmagic DeckLink device");

    // Enable video input
    res = _deck_link_input->EnableVideoInput(display_mode,
                                             pixel_format,
                                             _video_input_flags);
    // No glory
    if (res != S_OK)
        bail("Could not enable video input of Blackmagic DeckLink device");

    // Allocate pixel buffer
    _video_buffer_length = VideoFrame::required_data_length(_colour, _cols, _rows);
    if (is_stereo())
        _video_buffer_length *= 2;
    _video_buffer = reinterpret_cast<uint8_t *>(
        realloc(_video_buffer, _video_buffer_length * sizeof(uint8_t))
    );

    // Colour converter for post-capture colour conversion
    if (_colour == BGRA)
    {
        _12_bit_rgb_to_bgra_converter = CreateVideoConversionInstance();
        if (_12_bit_rgb_to_bgra_converter == nullptr)
            bail("Could not create colour converter for Blackmagic source");
        for (size_t i = 0; i < is_stereo() ? 2 : 1; i++)
            _bgra_frame_buffers[i] = new DeckLinkBGRAVideoFrame(
                _cols, _rows,
                &_video_buffer[i * _video_buffer_length / 2], frame_flags
            );
    }

    // Start streaming
    _running = true;
    res = _deck_link_input->StartStreams();
    // No glory: release everything and throw exception
    if (res != S_OK)
    {
        _running = false;
        bail("Could not start streaming from the Blackmagic DeckLink device");
    }
}

VideoSourceBlackmagicSDK::~VideoSourceBlackmagicSDK()
{
    { // Artificial scope for data lock
        // Make sure streamer thread not trying to access buffer
        std::lock_guard<std::mutex> data_lock_guard(_data_lock);
        _running = false;
    }

    // Stop streaming and disable enabled inputs
    _deck_link_input->SetCallback(nullptr);
    _deck_link_input->StopStreams();
    _deck_link_input->DisableVideoInput();

    // Release DeckLink members
    release_deck_link();
}

bool VideoSourceBlackmagicSDK::get_frame_dimensions(int & width, int & height)
{
    // Make sure only this thread is accessing the cols and rows members now
    std::lock_guard<std::mutex> data_lock_guard(_data_lock);
    if (_cols <= 0 or _rows <= 0)
        return false;

    width = _cols;
    height = _rows;
    return true;
}

bool VideoSourceBlackmagicSDK::get_frame(VideoFrame & frame)
{
    if (frame.colour() != _colour)
        return false;

    // Make sure only this thread is accessing the video frame data now
    std::lock_guard<std::mutex> data_lock_guard(_data_lock);

    if (_video_buffer_length > 0 and _cols > 0 and _rows > 0)
    {
        frame.init_from_specs(_video_buffer, _video_buffer_length, _cols, _rows);
        return true;
    }
    else
        return false;
}

double VideoSourceBlackmagicSDK::get_frame_rate()
{
    return _frame_rate;
}

void VideoSourceBlackmagicSDK::set_sub_frame(int x, int y, int width, int height)
{
    // issue #147
    throw VideoSourceError("Blackmagic does not support cropping yet");
}

void VideoSourceBlackmagicSDK::get_full_frame()
{
    // nop: set_sub_frame currently not implemented
}

HRESULT STDMETHODCALLTYPE VideoSourceBlackmagicSDK::QueryInterface(REFIID iid, LPVOID * ppv)
{
    return E_NOINTERFACE;
}

ULONG STDMETHODCALLTYPE VideoSourceBlackmagicSDK::AddRef(void)
{
    __sync_add_and_fetch(&_n_ref, 1);
    return _n_ref;
}

ULONG STDMETHODCALLTYPE VideoSourceBlackmagicSDK::Release(void)
{
    __sync_sub_and_fetch(&_n_ref, 1);
    return _n_ref;
}

HRESULT STDMETHODCALLTYPE VideoSourceBlackmagicSDK::VideoInputFormatChanged(
    BMDVideoInputFormatChangedEvents events,
    IDeckLinkDisplayMode * display_mode,
    BMDDetectedVideoInputFormatFlags format_flags
)
{
    // not supported yet: see issue #149
    return S_OK;
}

HRESULT STDMETHODCALLTYPE VideoSourceBlackmagicSDK::VideoInputFrameArrived(
    IDeckLinkVideoInputFrame * video_frame,
    IDeckLinkAudioInputPacket * audio_packet
)
{
    if (not _running)
        // nop if not running!
        return S_OK;

    // Not processing the audio packet, but only the video
    // frame for the time being
    if (video_frame == nullptr)
        // nop if no data
        return S_OK;

    // Nr. of bytes of received data
    size_t n_bytes = VideoFrame::required_data_length(
        _colour, video_frame->GetWidth(), video_frame->GetHeight()
    );
    if (is_stereo())
        n_bytes *= 2;

    { // Artificial scope for data lock
        // Make sure only this thread is accessing the buffer now
        std::lock_guard<std::mutex> data_lock_guard(_data_lock);

        // Extend buffer if more memory needed than already allocated
        if (n_bytes > _video_buffer_length or _cols != video_frame->GetWidth()
            or _rows != video_frame->GetHeight())
        {
            _video_buffer = reinterpret_cast<uint8_t *>(
                realloc(_video_buffer, n_bytes * sizeof(uint8_t))
            );

            // Set video frame specs according to new data
            _video_buffer_length = n_bytes;
            _cols = video_frame->GetWidth();
            _rows = video_frame->GetHeight();

            for (size_t i = 0; i < is_stereo() ? 2 : 1; i++)
            {
                if (_bgra_frame_buffers[i] != nullptr)
                {
                    _bgra_frame_buffers[i]->Release();
                    delete _bgra_frame_buffers[i];
                    _bgra_frame_buffers[i] = new DeckLinkBGRAVideoFrame(
                        _cols, _rows,
                        &_video_buffer[i * _video_buffer_length / 2],
                        video_frame->GetFlags()
                    );
                }
            }
        }
        if (_video_buffer == nullptr) // something's terribly wrong!
            // nop if something's terribly wrong!
            return S_OK;

        HRESULT res;

        if (need_conversion())
            // convert to BGRA from capture format
            res = _12_bit_rgb_to_bgra_converter->ConvertFrame(
                video_frame, _bgra_frame_buffers[0]
            );
        else
            // Get the new data into the buffer
            res = video_frame->GetBytes(
                reinterpret_cast<void **>(&_video_buffer)
            );

        // If the last operation failed, return
        if (FAILED(res))
            return res;

        if (is_stereo())
        {
            IDeckLinkVideoFrame *right_eye_frame = nullptr;
            IDeckLinkVideoFrame3DExtensions *three_d_extensions = nullptr;
            if ((video_frame->QueryInterface(
                    IID_IDeckLinkVideoFrame3DExtensions,
                    (void **) &three_d_extensions) != S_OK) ||
                (three_d_extensions->GetFrameForRightEye(
                    &right_eye_frame) != S_OK))
            {
                right_eye_frame = nullptr;
            }

            if (three_d_extensions != nullptr)
                three_d_extensions->Release();

            if (right_eye_frame != nullptr)
            {
                if (need_conversion())
                    // convert to BGRA from capture format
                    res = _12_bit_rgb_to_bgra_converter->ConvertFrame(
                        right_eye_frame, _bgra_frame_buffers[1]
                    );
                else
                    res = right_eye_frame->GetBytes(
                        reinterpret_cast<void **>(&_video_buffer[n_bytes / 2])
                    );
                right_eye_frame->Release();
                // If data could not be read into the buffer, return
                if (FAILED(res))
                    return res;
            }
        }

        // Propagate new video frame to observers
        _buffer_video_frame.init_from_specs(
            _video_buffer, _video_buffer_length, _cols, _rows,
            is_stereo() ? 2 : 1
        );
    }

    this->notify(_buffer_video_frame);

    // Everything went fine, return success
    return S_OK;
}

void VideoSourceBlackmagicSDK::release_deck_link() noexcept
{
    if (_deck_link_input != nullptr)
    {
        _deck_link_input->Release();
        _deck_link_input = nullptr;
    }

    if (_deck_link != nullptr)
        _deck_link->Release();

    if (_12_bit_rgb_to_bgra_converter != nullptr)
    {
        _12_bit_rgb_to_bgra_converter->Release();
        _12_bit_rgb_to_bgra_converter = nullptr;
    }

    for (size_t i = 0; i < 2; i++)
        if (_bgra_frame_buffers[i] != nullptr)
        {
            _bgra_frame_buffers[i]->Release();
            _bgra_frame_buffers[i] = nullptr;
        }
}

bool VideoSourceBlackmagicSDK::detect_input_format(BMDPixelFormat pixel_format,
                                                   BMDVideoInputFlags & video_input_flags,
                                                   BMDDisplayMode & display_mode,
                                                   double & frame_rate,
                                                   size_t & cols, size_t & rows,
                                                   BMDFrameFlags & frame_flags,
                                                   std::string & error_msg) noexcept
{
    std::vector<BMDDisplayMode> display_modes =
    {
        bmdModeHD1080p6000, bmdModeHD1080p5994,
        bmdModeHD1080p50,
        bmdModeHD1080p30, bmdModeHD1080p2997,
        bmdModeHD1080p25, bmdModeHD1080p24, bmdModeHD1080p2398,
        bmdModeHD1080i6000, bmdModeHD1080i5994,
        bmdModeHD1080i50,
        bmdModeHD720p60, bmdModeHD720p5994,
        bmdModeHD720p50,
        bmdMode4K2160p60, bmdMode4K2160p5994,
        bmdMode4K2160p50,
        bmdMode4K2160p30, bmdMode4K2160p2997,
        bmdMode4K2160p25, bmdMode4K2160p24, bmdMode4K2160p2398,
        bmdMode2k25, bmdMode2k24, bmdMode2k2398,
    };

    DeckLinkDisplayModeDetector detector(
                _deck_link_input,
                display_modes, pixel_format, video_input_flags
    );
    BMDDisplayMode display_mode_ = detector.get_display_mode();
    if (display_mode_ != bmdModeUnknown)
    {
        frame_rate = detector.get_frame_rate();
        detector.get_frame_dimensions(cols, rows);
        frame_flags = detector.get_frame_flags();
        display_mode = display_mode_;
        video_input_flags = detector.get_video_input_flags();
        return true;
    }
    else
    {
        error_msg = detector.get_error_msg();
        return false;
    }
}

}
<|endoftext|>


<commit_before>#include <ctime>
#include <iostream>
#include <cstdlib>
#include <iterator>

#include <timedb.h>
#include <ctime>
#include <limits.h>
int main(int argc, char *argv[]) {
    auto sz=1024*1024*100;
    uint8_t *buffer=new uint8_t[sz];

    std::fill(buffer,buffer+sz,0);

    timedb::compression::BinaryWriter bw(buffer,buffer+sz);
    timedb::compression::DeltaCompressor dc(bw);

    std::vector<timedb::Time> deltas{50,255,1024,2050};
    timedb::Time t=0;
    auto start=clock();
    for(size_t i=0;i<1000000;i++){
        dc.append(t);
        t+=deltas[i%deltas.size()];
        if (t> _UI32_MAX){// int32
            t=0;
        }
    }
    auto elapsed=((float)clock()-start)/ CLOCKS_PER_SEC;
    std::cout<<"elapsed: "<<elapsed<<std::endl;
    delete[]buffer;
}
<commit_msg>benchmarks.<commit_after>#include <ctime>
#include <iostream>
#include <cstdlib>
#include <iterator>

#include <timedb.h>
#include <ctime>
#include <limits.h>
#include <cmath>
int main(int argc, char *argv[]) {
    auto sz=1024*1024*100;
    uint8_t *buffer=new uint8_t[sz];

    //delta compression
    std::fill(buffer,buffer+sz,0);
    {
        timedb::compression::BinaryWriter bw(buffer,buffer+sz);
        timedb::compression::DeltaCompressor dc(bw);

        std::vector<timedb::Time> deltas{50,255,1024,2050};
        timedb::Time t=0;
        auto start=clock();
        for(size_t i=0;i<1000000;i++){
            dc.append(t);
            t+=deltas[i%deltas.size()];
            if (t> _UI32_MAX){// int32
                t=0;
            }
        }
        auto elapsed=((float)clock()-start)/ CLOCKS_PER_SEC;
        std::cout<<"delta copmressor elapsed: "<<elapsed<<std::endl;
    }
    {
        timedb::compression::BinaryWriter bw(buffer,buffer+sz);
        timedb::compression::DeltaDeCompressor dc(bw,0);

        auto start=clock();
        for(size_t i=1;i<1000000;i++){
            dc.read();
        }
        auto elapsed=((float)clock()-start)/ CLOCKS_PER_SEC;
        std::cout<<"delta decopmressor elapsed: "<<elapsed<<std::endl;
    }
    //xor compression
    std::fill(buffer,buffer+sz,0);
    {
        timedb::compression::BinaryWriter bw(buffer,buffer+sz);
        timedb::compression::XorCompressor dc(bw);

        timedb::Value t=1;
        auto start=clock();
        for(size_t i=0;i<1000000;i++){
            dc.append(t);
            t*=2;
        }
        auto elapsed=((float)clock()-start)/ CLOCKS_PER_SEC;
        std::cout<<"xor copmressor elapsed: "<<elapsed<<std::endl;
    }
    {
        timedb::compression::BinaryWriter bw(buffer,buffer+sz);
        timedb::compression::XorDeCompressor dc(bw,0);

        auto start=clock();
        for(size_t i=1;i<1000000;i++){
            dc.read();
        }
        auto elapsed=((float)clock()-start)/ CLOCKS_PER_SEC;
        std::cout<<"xor decopmressor elapsed: "<<elapsed<<std::endl;
    }
    delete[]buffer;
}
<|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/dom_ui/options/sync_options_handler.h"

#include "app/l10n_util.h"
#include "base/basictypes.h"
#include "base/callback.h"
#include "base/stl_util-inl.h"
#include "base/time.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/sync/sync_setup_flow.h"
#include "chrome/common/notification_service.h"
#include "grit/browser_resources.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "grit/locale_settings.h"
#include "grit/theme_resources.h"

SyncOptionsHandler::SyncOptionsHandler() {}

SyncOptionsHandler::~SyncOptionsHandler() {}

void SyncOptionsHandler::GetLocalizedValues(
    DictionaryValue* localized_strings) {
  DCHECK(localized_strings);
  // Sync page - ChromeOS
  localized_strings->SetString("syncPage",
      l10n_util::GetStringUTF16(IDS_SYNC_NTP_SYNC_SECTION_TITLE));
  localized_strings->SetString("sync_title",
      l10n_util::GetStringUTF16(IDS_CUSTOMIZE_SYNC_DESCRIPTION));
  localized_strings->SetString("syncsettings",
      l10n_util::GetStringUTF16(IDS_SYNC_DATATYPE_PREFERENCES));
  localized_strings->SetString("syncbookmarks",
      l10n_util::GetStringUTF16(IDS_SYNC_DATATYPE_BOOKMARKS));
  localized_strings->SetString("synctypedurls",
      l10n_util::GetStringUTF16(IDS_SYNC_DATATYPE_TYPED_URLS));
  localized_strings->SetString("syncpasswords",
      l10n_util::GetStringUTF16(IDS_SYNC_DATATYPE_PASSWORDS));
  localized_strings->SetString("syncextensions",
      l10n_util::GetStringUTF16(IDS_SYNC_DATATYPE_EXTENSIONS));
  localized_strings->SetString("syncautofill",
      l10n_util::GetStringUTF16(IDS_SYNC_DATATYPE_AUTOFILL));
  localized_strings->SetString("syncthemes",
      l10n_util::GetStringUTF16(IDS_SYNC_DATATYPE_THEMES));
  localized_strings->SetString("syncapps",
      l10n_util::GetStringUTF16(IDS_SYNC_DATATYPE_APPS));
}

void SyncOptionsHandler::Initialize() {
  ProfileSyncService* service =
      dom_ui_->GetProfile()->GetOriginalProfile()->GetProfileSyncService();
  DCHECK(service);

  DictionaryValue args;
  SyncSetupFlow::GetArgsForChooseDataTypes(service, &args);

  dom_ui_->CallJavascriptFunction(L"SyncOptions.setRegisteredDataTypes", args);
}

void SyncOptionsHandler::RegisterMessages() {
  dom_ui_->RegisterMessageCallback("updatePreferredDataTypes",
      NewCallback(this, &SyncOptionsHandler::OnPreferredDataTypesUpdated));
}

void SyncOptionsHandler::OnPreferredDataTypesUpdated(const ListValue* args) {
  NotificationService::current()->Notify(
      NotificationType::SYNC_DATA_TYPES_UPDATED,
      Source<Profile>(dom_ui_->GetProfile()),
      NotificationService::NoDetails());
}
<commit_msg>Fix a crash bug that occurs when a user opens chrome:options from guest account on Chrome OS.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/dom_ui/options/sync_options_handler.h"

#include "app/l10n_util.h"
#include "base/basictypes.h"
#include "base/callback.h"
#include "base/stl_util-inl.h"
#include "base/time.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/sync/sync_setup_flow.h"
#include "chrome/common/notification_service.h"
#include "grit/browser_resources.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "grit/locale_settings.h"
#include "grit/theme_resources.h"

SyncOptionsHandler::SyncOptionsHandler() {}

SyncOptionsHandler::~SyncOptionsHandler() {}

void SyncOptionsHandler::GetLocalizedValues(
    DictionaryValue* localized_strings) {
  DCHECK(localized_strings);
  // Sync page - ChromeOS
  localized_strings->SetString("syncPage",
      l10n_util::GetStringUTF16(IDS_SYNC_NTP_SYNC_SECTION_TITLE));
  localized_strings->SetString("sync_title",
      l10n_util::GetStringUTF16(IDS_CUSTOMIZE_SYNC_DESCRIPTION));
  localized_strings->SetString("syncsettings",
      l10n_util::GetStringUTF16(IDS_SYNC_DATATYPE_PREFERENCES));
  localized_strings->SetString("syncbookmarks",
      l10n_util::GetStringUTF16(IDS_SYNC_DATATYPE_BOOKMARKS));
  localized_strings->SetString("synctypedurls",
      l10n_util::GetStringUTF16(IDS_SYNC_DATATYPE_TYPED_URLS));
  localized_strings->SetString("syncpasswords",
      l10n_util::GetStringUTF16(IDS_SYNC_DATATYPE_PASSWORDS));
  localized_strings->SetString("syncextensions",
      l10n_util::GetStringUTF16(IDS_SYNC_DATATYPE_EXTENSIONS));
  localized_strings->SetString("syncautofill",
      l10n_util::GetStringUTF16(IDS_SYNC_DATATYPE_AUTOFILL));
  localized_strings->SetString("syncthemes",
      l10n_util::GetStringUTF16(IDS_SYNC_DATATYPE_THEMES));
  localized_strings->SetString("syncapps",
      l10n_util::GetStringUTF16(IDS_SYNC_DATATYPE_APPS));
}

void SyncOptionsHandler::Initialize() {
  ProfileSyncService* service =
      dom_ui_->GetProfile()->GetOriginalProfile()->GetProfileSyncService();
  if (!service) {
    // This can happen if the user logs in to Chrome OS as guest.
    LOG(ERROR) << "Failed to get ProfileSyncService";
    return;
  }

  DictionaryValue args;
  SyncSetupFlow::GetArgsForChooseDataTypes(service, &args);

  dom_ui_->CallJavascriptFunction(L"SyncOptions.setRegisteredDataTypes", args);
}

void SyncOptionsHandler::RegisterMessages() {
  dom_ui_->RegisterMessageCallback("updatePreferredDataTypes",
      NewCallback(this, &SyncOptionsHandler::OnPreferredDataTypesUpdated));
}

void SyncOptionsHandler::OnPreferredDataTypesUpdated(const ListValue* args) {
  NotificationService::current()->Notify(
      NotificationType::SYNC_DATA_TYPES_UPDATED,
      Source<Profile>(dom_ui_->GetProfile()),
      NotificationService::NoDetails());
}
<|endoftext|>


<commit_before>// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2018 Intel Corporation. All Rights Reserved.

#include "software-device.h"
#include "stream.h"

namespace librealsense
{
    software_device::software_device()
        : device(std::make_shared<context>(backend_type::standard), {})
    {
        register_info(RS2_CAMERA_INFO_NAME, "Software-Device");
    }

    software_sensor& software_device::add_software_sensor(const std::string& name)
    {
        auto sensor = std::make_shared<software_sensor>(name, this);
        add_sensor(sensor);
        _software_sensors.push_back(sensor);

        return *sensor;
    }

    software_sensor& software_device::get_software_sensor(int index)
    {
        if (index >= _software_sensors.size())
        {
            throw rs2::error("Requested index is out of range!");
        }
        return *_software_sensors[index];
    }

    void software_device::set_matcher_type(rs2_matchers matcher)
    {
        _matcher = matcher;
    }

    software_sensor::software_sensor(std::string name, software_device* owner)
        : sensor_base(name, owner)
    {

    }

    std::shared_ptr<matcher> software_device::create_matcher(const frame_holder& frame) const
    {
        std::vector<stream_interface*> profiles;

        for (auto&& s : _software_sensors)
            for (auto&& p : s->get_stream_profiles())
                profiles.push_back(p.get());

        return matcher_factory::create(_matcher, profiles);
    }

    std::shared_ptr<stream_profile_interface> software_sensor::add_video_stream(rs2_video_stream video_stream)
    {
        auto exist = (std::find_if(_profiles.begin(), _profiles.end(), [&](std::shared_ptr<stream_profile_interface> profile)
        {
            if (profile->get_unique_id() == video_stream.uid)
            {
                return true;
            }
            return false;
        } ) != _profiles.end());

        if (exist)
        {
            LOG_WARNING("Stream unique ID already exist!");
            throw rs2::error("Stream unique ID already exist!");
        }

        auto profile = std::make_shared<video_stream_profile>(
            platform::stream_profile{ (uint32_t)video_stream.width, (uint32_t)video_stream.height, (uint32_t)video_stream.fps, 0 });
        profile->set_dims(video_stream.width, video_stream.height);
        profile->set_format(video_stream.fmt);
        profile->set_framerate(video_stream.fps);
        profile->set_stream_index(video_stream.index);
        profile->set_stream_type(video_stream.type);
        profile->set_unique_id(video_stream.uid);
        profile->set_intrinsics([=]() {return video_stream.intrinsics; });
        _profiles.push_back(profile);

        return profile;
    }

    stream_profiles software_sensor::init_stream_profiles()
    {
        return _profiles;
    }

    void software_sensor::open(const stream_profiles& requests)
    {
        set_active_streams(requests);
    }

    void software_sensor::close(){}

    void software_sensor::start(frame_callback_ptr callback)
    {
        _source.init(_metadata_parsers);
        _source.set_sensor(this->shared_from_this());
        _source.set_callback(callback);
        raise_on_before_streaming_changes(true);
    }

    void software_sensor::stop()
    {
        raise_on_before_streaming_changes(false);
        _source.flush();
        _source.reset();
    }

    void software_sensor::on_video_frame(rs2_software_video_frame software_frame)
    {
        frame_additional_data data;
        data.timestamp = software_frame.timestamp;
        data.timestamp_domain = software_frame.domain;
        data.frame_number = software_frame.frame_number;

        rs2_extension extension = software_frame.profile->profile->get_stream_type() == RS2_STREAM_DEPTH ?
            RS2_EXTENSION_DEPTH_FRAME : RS2_EXTENSION_VIDEO_FRAME;

        auto frame = _source.alloc_frame(extension, 0, data, false);
        if (!frame) return;

        auto vid_profile = dynamic_cast<video_stream_profile_interface*>(software_frame.profile->profile);
        auto vid_frame = dynamic_cast<video_frame*>(frame);
        vid_frame->assign(vid_profile->get_width(), vid_profile->get_height(), software_frame.stride, software_frame.bpp);

        frame->set_stream(std::dynamic_pointer_cast<stream_profile_interface>(software_frame.profile->profile->shared_from_this()));
        frame->attach_continuation(frame_continuation{ [=]() {
            software_frame.deleter(software_frame.pixels);
        }, software_frame.pixels });
        _source.invoke_callback(frame);
    }

    void software_sensor::add_read_only_option(rs2_option option, float val)
    {
        register_option(RS2_OPTION_DEPTH_UNITS, std::make_shared<const_value_option>("bypass sensor read only option",
            lazy<float>([=]() { return val; })));
    }

    void software_sensor::update_read_only_option(rs2_option option, float val)
    {
        get_option(option).set(val);
    }
}

<commit_msg>Adding set_active_streams(null)<commit_after>// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2018 Intel Corporation. All Rights Reserved.

#include "software-device.h"
#include "stream.h"

namespace librealsense
{
    software_device::software_device()
        : device(std::make_shared<context>(backend_type::standard), {})
    {
        register_info(RS2_CAMERA_INFO_NAME, "Software-Device");
    }

    software_sensor& software_device::add_software_sensor(const std::string& name)
    {
        auto sensor = std::make_shared<software_sensor>(name, this);
        add_sensor(sensor);
        _software_sensors.push_back(sensor);

        return *sensor;
    }

    software_sensor& software_device::get_software_sensor(int index)
    {
        if (index >= _software_sensors.size())
        {
            throw rs2::error("Requested index is out of range!");
        }
        return *_software_sensors[index];
    }

    void software_device::set_matcher_type(rs2_matchers matcher)
    {
        _matcher = matcher;
    }

    software_sensor::software_sensor(std::string name, software_device* owner)
        : sensor_base(name, owner)
    {

    }

    std::shared_ptr<matcher> software_device::create_matcher(const frame_holder& frame) const
    {
        std::vector<stream_interface*> profiles;

        for (auto&& s : _software_sensors)
            for (auto&& p : s->get_stream_profiles())
                profiles.push_back(p.get());

        return matcher_factory::create(_matcher, profiles);
    }

    std::shared_ptr<stream_profile_interface> software_sensor::add_video_stream(rs2_video_stream video_stream)
    {
        auto exist = (std::find_if(_profiles.begin(), _profiles.end(), [&](std::shared_ptr<stream_profile_interface> profile)
        {
            if (profile->get_unique_id() == video_stream.uid)
            {
                return true;
            }
            return false;
        } ) != _profiles.end());

        if (exist)
        {
            LOG_WARNING("Stream unique ID already exist!");
            throw rs2::error("Stream unique ID already exist!");
        }

        auto profile = std::make_shared<video_stream_profile>(
            platform::stream_profile{ (uint32_t)video_stream.width, (uint32_t)video_stream.height, (uint32_t)video_stream.fps, 0 });
        profile->set_dims(video_stream.width, video_stream.height);
        profile->set_format(video_stream.fmt);
        profile->set_framerate(video_stream.fps);
        profile->set_stream_index(video_stream.index);
        profile->set_stream_type(video_stream.type);
        profile->set_unique_id(video_stream.uid);
        profile->set_intrinsics([=]() {return video_stream.intrinsics; });
        _profiles.push_back(profile);

        return profile;
    }

    stream_profiles software_sensor::init_stream_profiles()
    {
        return _profiles;
    }

    void software_sensor::open(const stream_profiles& requests)
    {
        set_active_streams(requests);
    }

    void software_sensor::close()
    {
        set_active_streams({});
    }

    void software_sensor::start(frame_callback_ptr callback)
    {
        _source.init(_metadata_parsers);
        _source.set_sensor(this->shared_from_this());
        _source.set_callback(callback);
        raise_on_before_streaming_changes(true);
    }

    void software_sensor::stop()
    {
        raise_on_before_streaming_changes(false);
        _source.flush();
        _source.reset();
    }

    void software_sensor::on_video_frame(rs2_software_video_frame software_frame)
    {
        frame_additional_data data;
        data.timestamp = software_frame.timestamp;
        data.timestamp_domain = software_frame.domain;
        data.frame_number = software_frame.frame_number;

        rs2_extension extension = software_frame.profile->profile->get_stream_type() == RS2_STREAM_DEPTH ?
            RS2_EXTENSION_DEPTH_FRAME : RS2_EXTENSION_VIDEO_FRAME;

        auto frame = _source.alloc_frame(extension, 0, data, false);
        if (!frame) return;

        auto vid_profile = dynamic_cast<video_stream_profile_interface*>(software_frame.profile->profile);
        auto vid_frame = dynamic_cast<video_frame*>(frame);
        vid_frame->assign(vid_profile->get_width(), vid_profile->get_height(), software_frame.stride, software_frame.bpp);

        frame->set_stream(std::dynamic_pointer_cast<stream_profile_interface>(software_frame.profile->profile->shared_from_this()));
        frame->attach_continuation(frame_continuation{ [=]() {
            software_frame.deleter(software_frame.pixels);
        }, software_frame.pixels });
        _source.invoke_callback(frame);
    }

    void software_sensor::add_read_only_option(rs2_option option, float val)
    {
        register_option(RS2_OPTION_DEPTH_UNITS, std::make_shared<const_value_option>("bypass sensor read only option",
            lazy<float>([=]() { return val; })));
    }

    void software_sensor::update_read_only_option(rs2_option option, float val)
    {
        get_option(option).set(val);
    }
}

<|endoftext|>


<commit_before>#include <emscripten.h>
#include <emscripten/bind.h>
#include <emscripten/val.h>
#include <gphoto2/gphoto2.h>

#include <cstring>
#include <iostream>

using emscripten::val;

template <typename T, int (*Deleter)(T *)>
void gpp_deleter(T *ptr) {
  gpp_try(Deleter(ptr));
}

template <typename T, void (*Deleter)(T *)>
void gpp_deleter(T *ptr) {
  Deleter(ptr);
}

template <typename T, auto Deleter>
using gpp_unique_ptr =
    std::unique_ptr<T, std::integral_constant<decltype(Deleter), Deleter>>;

using GPPWidget = gpp_unique_ptr<CameraWidget, gp_widget_unref>;

void gpp_try(int status) {
  if (status != GP_OK) {
    throw std::runtime_error(gp_result_as_string(status));
  }
}

void gpp_error(GPContext *context, const char *text, void *data) {
  throw std::runtime_error(text);
}

#define GPP_CALL(RET, EXPR) \
  ({                        \
    RET OUT;                \
    RET *_ = &OUT;          \
    gpp_try(EXPR);          \
    OUT;                    \
  })

EM_JS([[noreturn]] void, throw_msg, (const char *msg),
      { throw new Error(UTF8ToString(msg)); });

template <typename Func>
auto gpp_rethrow(Func func) {
  try {
    return func();
  } catch (std::exception &e) {
    throw_msg(e.what());
  }
}

const thread_local val Uint8Array = val::global("Uint8Array");
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 {
 public:
  Context() : camera(nullptr), context(nullptr) {
    gpp_rethrow([=]() {
      camera.reset(GPP_CALL(Camera *, gp_camera_new(_)));
      context.reset(gp_context_new());
      gp_context_set_error_func(context.get(), gpp_error, nullptr);
      gpp_try(gp_camera_init(camera.get(), context.get()));
    });
  }

  val configToJS() {
    return gpp_rethrow([=]() {
      GPPWidget config(
          GPP_CALL(CameraWidget *,
                   gp_camera_get_config(camera.get(), _, context.get())));
      return walk_config(config.get()).second;
    });
  }

  void setConfigValue(std::string name, val value) {
    gpp_rethrow([=]() {
      GPPWidget widget(GPP_CALL(
          CameraWidget *, gp_camera_get_single_config(
                              camera.get(), name.c_str(), _, context.get())));
      auto type =
          GPP_CALL(CameraWidgetType, gp_widget_get_type(widget.get(), _));
      switch (type) {
        case GP_WIDGET_RANGE: {
          float number = value.as<float>();
          gpp_try(gp_widget_set_value(widget.get(), &number));
          break;
        }
        case GP_WIDGET_MENU:
        case GP_WIDGET_RADIO:
        case GP_WIDGET_TEXT: {
          auto str = value.as<std::string>();
          gpp_try(gp_widget_set_value(widget.get(), str.c_str()));
          break;
        }
        case GP_WIDGET_TOGGLE: {
          int on = value.as<bool>() ? 1 : 0;
          gpp_try(gp_widget_set_value(widget.get(), &on));
          break;
        }
        case GP_WIDGET_DATE: {
          auto seconds = static_cast<int>(value.as<double>() / 1000);
          gpp_try(gp_widget_set_value(widget.get(), &seconds));
          break;
        }
        default: {
          throw std::logic_error("unimplemented");
        }
      }
      gpp_try(gp_camera_set_single_config(camera.get(), name.c_str(),
                                          widget.get(), context.get()));
    });
  }

  val capturePreviewAsBlob() {
    return gpp_rethrow([=]() {
      auto &file = get_file();

      gpp_try(gp_camera_capture_preview(camera.get(), &file, context.get()));

      auto params = blob_chunks_and_opts(file);
      return Blob.new_(std::move(params.first), std::move(params.second));
    });
  }

  val captureImageAsFile() {
    return gpp_rethrow([=]() {
      struct TempCameraFile {
        Camera &camera;
        GPContext &context;
        CameraFilePath path;

        ~TempCameraFile() {
          gp_camera_file_delete(&camera, path.folder, path.name, &context);
        }
      };

      TempCameraFile camera_file{
          .camera = *camera,
          .context = *context,
      };

      strcpy(camera_file.path.folder, "/");
      strcpy(camera_file.path.name, "web-gphoto2");

      gpp_try(gp_camera_capture(camera.get(), GP_CAPTURE_IMAGE,
                                &camera_file.path, context.get()));

      auto &file = get_file();
      gpp_try(gp_camera_file_get(camera.get(), camera_file.path.folder,
                                 camera_file.path.name, GP_FILE_TYPE_NORMAL,
                                 &file, context.get()));

      gpp_try(gp_file_set_name(&file, "image."));
      gpp_try(gp_file_adjust_name_for_mime_type(&file));
      auto name = val(GPP_CALL(const char *, gp_file_get_name(&file, _)));

      auto params = blob_chunks_and_opts(file);
      return File.new_(std::move(params.first), std::move(name),
                       std::move(params.second));
    });
  }

 private:
  gpp_unique_ptr<Camera, gp_camera_unref> camera;
  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) {
    val result = val::object();

    val name(GPP_CALL(const char *, gp_widget_get_name(widget, _)));
    result.set("name", name);
    result.set("info", GPP_CALL(const char *, gp_widget_get_info(widget, _)));
    result.set("label", GPP_CALL(const char *, gp_widget_get_label(widget, _)));
    result.set("readonly",
               GPP_CALL(int, gp_widget_get_readonly(widget, _)) != 0);

    auto type = GPP_CALL(CameraWidgetType, gp_widget_get_type(widget, _));

    switch (type) {
      case GP_WIDGET_RANGE: {
        result.set("type", "range");
        result.set("value", GPP_CALL(float, gp_widget_get_value(widget, _)));

        float min, max, step;
        gpp_try(gp_widget_get_range(widget, &min, &max, &step));
        result.set("min", min);
        result.set("max", max);
        result.set("step", step);

        break;
      }
      case GP_WIDGET_MENU:
      case GP_WIDGET_RADIO: {
        result.set("type", type == GP_WIDGET_MENU ? "menu" : "radio");
        result.set("value",
                   GPP_CALL(const char *, gp_widget_get_value(widget, _)));

        val choices = val::array();
        for (int i = 0, n = gp_widget_count_choices(widget); i < n; i++) {
          choices.call<void>(
              "push",
              val(GPP_CALL(const char *, gp_widget_get_choice(widget, i, _))));
        }
        result.set("choices", choices);

        break;
      }
      case GP_WIDGET_TOGGLE: {
        result.set("type", "toggle");
        result.set("value", GPP_CALL(int, gp_widget_get_value(widget, _)) != 0);

        break;
      }
      case GP_WIDGET_TEXT: {
        result.set("type", "text");
        result.set("value",
                   GPP_CALL(const char *, gp_widget_get_value(widget, _)));

        break;
      }
      case GP_WIDGET_WINDOW:
      case GP_WIDGET_SECTION: {
        result.set("type", type == GP_WIDGET_WINDOW ? "window" : "section");

        val children = val::object();
        for (int i = 0, n = gp_widget_count_children(widget); i < n; i++) {
          auto child =
              GPP_CALL(CameraWidget *, gp_widget_get_child(widget, i, _));
          auto kv = walk_config(child);
          children.set(kv.first, kv.second);
        }
        result.set("children", children);

        break;
      }
      case GP_WIDGET_DATE: {
        auto seconds = GPP_CALL(int, gp_widget_get_value(widget, _));
        result.set("type", "datetime");
        result.set("value", static_cast<double>(seconds) * 1000);

        break;
      }
      default: {
        throw std::logic_error("unimplemented");
      }
    }

    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::class_<Context>("Context")
      .constructor<>()
      .function("configToJS", &Context::configToJS)
      .function("setConfigValue", &Context::setConfigValue)
      .function("capturePreviewAsBlob", &Context::capturePreviewAsBlob)
      .function("captureImageAsFile", &Context::captureImageAsFile);
}
<commit_msg>Reduce scope of temp camera file<commit_after>#include <emscripten.h>
#include <emscripten/bind.h>
#include <emscripten/val.h>
#include <gphoto2/gphoto2.h>

#include <cstring>
#include <iostream>

using emscripten::val;

template <typename T, int (*Deleter)(T *)>
void gpp_deleter(T *ptr) {
  gpp_try(Deleter(ptr));
}

template <typename T, void (*Deleter)(T *)>
void gpp_deleter(T *ptr) {
  Deleter(ptr);
}

template <typename T, auto Deleter>
using gpp_unique_ptr =
    std::unique_ptr<T, std::integral_constant<decltype(Deleter), Deleter>>;

using GPPWidget = gpp_unique_ptr<CameraWidget, gp_widget_unref>;

void gpp_try(int status) {
  if (status != GP_OK) {
    throw std::runtime_error(gp_result_as_string(status));
  }
}

void gpp_error(GPContext *context, const char *text, void *data) {
  throw std::runtime_error(text);
}

#define GPP_CALL(RET, EXPR) \
  ({                        \
    RET OUT;                \
    RET *_ = &OUT;          \
    gpp_try(EXPR);          \
    OUT;                    \
  })

EM_JS([[noreturn]] void, throw_msg, (const char *msg),
      { throw new Error(UTF8ToString(msg)); });

template <typename Func>
auto gpp_rethrow(Func func) {
  try {
    return func();
  } catch (std::exception &e) {
    throw_msg(e.what());
  }
}

const thread_local val Uint8Array = val::global("Uint8Array");
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 {
 public:
  Context() : camera(nullptr), context(nullptr) {
    gpp_rethrow([=]() {
      camera.reset(GPP_CALL(Camera *, gp_camera_new(_)));
      context.reset(gp_context_new());
      gp_context_set_error_func(context.get(), gpp_error, nullptr);
      gpp_try(gp_camera_init(camera.get(), context.get()));
    });
  }

  val configToJS() {
    return gpp_rethrow([=]() {
      GPPWidget config(
          GPP_CALL(CameraWidget *,
                   gp_camera_get_config(camera.get(), _, context.get())));
      return walk_config(config.get()).second;
    });
  }

  void setConfigValue(std::string name, val value) {
    gpp_rethrow([=]() {
      GPPWidget widget(GPP_CALL(
          CameraWidget *, gp_camera_get_single_config(
                              camera.get(), name.c_str(), _, context.get())));
      auto type =
          GPP_CALL(CameraWidgetType, gp_widget_get_type(widget.get(), _));
      switch (type) {
        case GP_WIDGET_RANGE: {
          float number = value.as<float>();
          gpp_try(gp_widget_set_value(widget.get(), &number));
          break;
        }
        case GP_WIDGET_MENU:
        case GP_WIDGET_RADIO:
        case GP_WIDGET_TEXT: {
          auto str = value.as<std::string>();
          gpp_try(gp_widget_set_value(widget.get(), str.c_str()));
          break;
        }
        case GP_WIDGET_TOGGLE: {
          int on = value.as<bool>() ? 1 : 0;
          gpp_try(gp_widget_set_value(widget.get(), &on));
          break;
        }
        case GP_WIDGET_DATE: {
          auto seconds = static_cast<int>(value.as<double>() / 1000);
          gpp_try(gp_widget_set_value(widget.get(), &seconds));
          break;
        }
        default: {
          throw std::logic_error("unimplemented");
        }
      }
      gpp_try(gp_camera_set_single_config(camera.get(), name.c_str(),
                                          widget.get(), context.get()));
    });
  }

  val capturePreviewAsBlob() {
    return gpp_rethrow([=]() {
      auto &file = get_file();

      gpp_try(gp_camera_capture_preview(camera.get(), &file, context.get()));

      auto params = blob_chunks_and_opts(file);
      return Blob.new_(std::move(params.first), std::move(params.second));
    });
  }

  val captureImageAsFile() {
    return gpp_rethrow([=]() {
      auto &file = get_file();

      {
        struct TempCameraFile {
          Camera &camera;
          GPContext &context;
          CameraFilePath path;

          ~TempCameraFile() {
            gp_camera_file_delete(&camera, path.folder, path.name, &context);
          }
        };

        TempCameraFile camera_file{
            .camera = *camera,
            .context = *context,
        };

        strcpy(camera_file.path.folder, "/");
        strcpy(camera_file.path.name, "web-gphoto2");

        gpp_try(gp_camera_capture(camera.get(), GP_CAPTURE_IMAGE,
                                  &camera_file.path, context.get()));

        gpp_try(gp_camera_file_get(camera.get(), camera_file.path.folder,
                                   camera_file.path.name, GP_FILE_TYPE_NORMAL,
                                   &file, context.get()));
      }

      gpp_try(gp_file_set_name(&file, "image."));
      gpp_try(gp_file_adjust_name_for_mime_type(&file));
      auto name = val(GPP_CALL(const char *, gp_file_get_name(&file, _)));

      auto params = blob_chunks_and_opts(file);
      return File.new_(std::move(params.first), std::move(name),
                       std::move(params.second));
    });
  }

 private:
  gpp_unique_ptr<Camera, gp_camera_unref> camera;
  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) {
    val result = val::object();

    val name(GPP_CALL(const char *, gp_widget_get_name(widget, _)));
    result.set("name", name);
    result.set("info", GPP_CALL(const char *, gp_widget_get_info(widget, _)));
    result.set("label", GPP_CALL(const char *, gp_widget_get_label(widget, _)));
    result.set("readonly",
               GPP_CALL(int, gp_widget_get_readonly(widget, _)) != 0);

    auto type = GPP_CALL(CameraWidgetType, gp_widget_get_type(widget, _));

    switch (type) {
      case GP_WIDGET_RANGE: {
        result.set("type", "range");
        result.set("value", GPP_CALL(float, gp_widget_get_value(widget, _)));

        float min, max, step;
        gpp_try(gp_widget_get_range(widget, &min, &max, &step));
        result.set("min", min);
        result.set("max", max);
        result.set("step", step);

        break;
      }
      case GP_WIDGET_MENU:
      case GP_WIDGET_RADIO: {
        result.set("type", type == GP_WIDGET_MENU ? "menu" : "radio");
        result.set("value",
                   GPP_CALL(const char *, gp_widget_get_value(widget, _)));

        val choices = val::array();
        for (int i = 0, n = gp_widget_count_choices(widget); i < n; i++) {
          choices.call<void>(
              "push",
              val(GPP_CALL(const char *, gp_widget_get_choice(widget, i, _))));
        }
        result.set("choices", choices);

        break;
      }
      case GP_WIDGET_TOGGLE: {
        result.set("type", "toggle");
        result.set("value", GPP_CALL(int, gp_widget_get_value(widget, _)) != 0);

        break;
      }
      case GP_WIDGET_TEXT: {
        result.set("type", "text");
        result.set("value",
                   GPP_CALL(const char *, gp_widget_get_value(widget, _)));

        break;
      }
      case GP_WIDGET_WINDOW:
      case GP_WIDGET_SECTION: {
        result.set("type", type == GP_WIDGET_WINDOW ? "window" : "section");

        val children = val::object();
        for (int i = 0, n = gp_widget_count_children(widget); i < n; i++) {
          auto child =
              GPP_CALL(CameraWidget *, gp_widget_get_child(widget, i, _));
          auto kv = walk_config(child);
          children.set(kv.first, kv.second);
        }
        result.set("children", children);

        break;
      }
      case GP_WIDGET_DATE: {
        auto seconds = GPP_CALL(int, gp_widget_get_value(widget, _));
        result.set("type", "datetime");
        result.set("value", static_cast<double>(seconds) * 1000);

        break;
      }
      default: {
        throw std::logic_error("unimplemented");
      }
    }

    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::class_<Context>("Context")
      .constructor<>()
      .function("configToJS", &Context::configToJS)
      .function("setConfigValue", &Context::setConfigValue)
      .function("capturePreviewAsBlob", &Context::capturePreviewAsBlob)
      .function("captureImageAsFile", &Context::captureImageAsFile);
}
<|endoftext|>