1587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden/*
2587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden * Copyright 2014 The Android Open Source Project
3587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden *
4587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden * Licensed under the Apache License, Version 2.0 (the "License");
5587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden * you may not use this file except in compliance with the License.
6587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden * You may obtain a copy of the License at
7587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden *
8587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden *      http://www.apache.org/licenses/LICENSE-2.0
9587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden *
10587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden * Unless required by applicable law or agreed to in writing, software
11587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden * distributed under the License is distributed on an "AS IS" BASIS,
12587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden * See the License for the specific language governing permissions and
14587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden * limitations under the License.
15587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden */
16587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden
17587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden#define LOG_TAG "ScreenRecord"
18587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden//#define LOG_NDEBUG 0
19587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden#include <utils/Log.h>
20587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden
21587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden#include <GLES2/gl2.h>
22587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden#include <GLES2/gl2ext.h>
23587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden
24587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden#include "FrameOutput.h"
25587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden
26587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFaddenusing namespace android;
27587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden
28587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFaddenstatic const bool kShowTiming = false;      // set to "true" for debugging
29587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFaddenstatic const int kGlBytesPerPixel = 4;      // GL_RGBA
30587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFaddenstatic const int kOutBytesPerPixel = 3;     // RGB only
31587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden
32587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFaddeninline void FrameOutput::setValueLE(uint8_t* buf, uint32_t value) {
33587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    // Since we're running on an Android device, we're (almost) guaranteed
34587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    // to be little-endian, and (almost) guaranteed that unaligned 32-bit
35587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    // writes will work without any performance penalty... but do it
36587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    // byte-by-byte anyway.
37587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    buf[0] = (uint8_t) value;
38587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    buf[1] = (uint8_t) (value >> 8);
39587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    buf[2] = (uint8_t) (value >> 16);
40587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    buf[3] = (uint8_t) (value >> 24);
41587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden}
42587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden
43587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFaddenstatus_t FrameOutput::createInputSurface(int width, int height,
44587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden        sp<IGraphicBufferProducer>* pBufferProducer) {
45587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    status_t err;
46587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden
47587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    err = mEglWindow.createPbuffer(width, height);
48587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    if (err != NO_ERROR) {
49587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden        return err;
50587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    }
51587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    mEglWindow.makeCurrent();
52587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden
53587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    glViewport(0, 0, width, height);
54587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    glDisable(GL_DEPTH_TEST);
55587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    glDisable(GL_CULL_FACE);
56587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden
57587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    // Shader for rendering the external texture.
58587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    err = mExtTexProgram.setup(Program::PROGRAM_EXTERNAL_TEXTURE);
59587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    if (err != NO_ERROR) {
60587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden        return err;
61587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    }
62587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden
63587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    // Input side (buffers from virtual display).
64587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    glGenTextures(1, &mExtTextureName);
65587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    if (mExtTextureName == 0) {
66587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden        ALOGE("glGenTextures failed: %#x", glGetError());
67587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden        return UNKNOWN_ERROR;
68587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    }
69587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden
70b278f5e70c001391779525fb4d3b024503ba9466Dan Stoza    sp<IGraphicBufferProducer> producer;
71b278f5e70c001391779525fb4d3b024503ba9466Dan Stoza    sp<IGraphicBufferConsumer> consumer;
72b278f5e70c001391779525fb4d3b024503ba9466Dan Stoza    BufferQueue::createBufferQueue(&producer, &consumer);
73b278f5e70c001391779525fb4d3b024503ba9466Dan Stoza    mGlConsumer = new GLConsumer(consumer, mExtTextureName,
74db2722a319d91a869ebc4dd618edbef43fd5b738Dan Stoza                GL_TEXTURE_EXTERNAL_OES, true, false);
75587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    mGlConsumer->setName(String8("virtual display"));
76587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    mGlConsumer->setDefaultBufferSize(width, height);
776e6eaa7ac713ab6606726c3f76a9019ded97f018Pablo Ceballos    producer->setMaxDequeuedBufferCount(4);
78587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    mGlConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_TEXTURE);
79587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden
80587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    mGlConsumer->setFrameAvailableListener(this);
81587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden
82587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    mPixelBuf = new uint8_t[width * height * kGlBytesPerPixel];
83587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden
84b278f5e70c001391779525fb4d3b024503ba9466Dan Stoza    *pBufferProducer = producer;
85587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden
86587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    ALOGD("FrameOutput::createInputSurface OK");
87587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    return NO_ERROR;
88587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden}
89587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden
90e32106fd5175afdf939ae397aece9caf378a4912Benoit Gobystatus_t FrameOutput::copyFrame(FILE* fp, long timeoutUsec, bool rawFrames) {
91587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    Mutex::Autolock _l(mMutex);
92587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    ALOGV("copyFrame %ld\n", timeoutUsec);
93587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden
94587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    if (!mFrameAvailable) {
95587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden        nsecs_t timeoutNsec = (nsecs_t)timeoutUsec * 1000;
96587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden        int cc = mEventCond.waitRelative(mMutex, timeoutNsec);
97587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden        if (cc == -ETIMEDOUT) {
98587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden            ALOGV("cond wait timed out");
99587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden            return ETIMEDOUT;
100587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden        } else if (cc != 0) {
101587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden            ALOGW("cond wait returned error %d", cc);
102587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden            return cc;
103587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden        }
104587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    }
105587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    if (!mFrameAvailable) {
106587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden        // This happens when Ctrl-C is hit.  Apparently POSIX says that the
107587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden        // pthread wait call doesn't return EINTR, treating this instead as
108587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden        // an instance of a "spurious wakeup".  We didn't get a frame, so
109587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden        // we just treat it as a timeout.
110587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden        return ETIMEDOUT;
111587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    }
112587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden
113587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    // A frame is available.  Clear the flag for the next round.
114587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    mFrameAvailable = false;
115587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden
116587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    float texMatrix[16];
117587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    mGlConsumer->updateTexImage();
118587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    mGlConsumer->getTransformMatrix(texMatrix);
119587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden
120587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    // The data is in an external texture, so we need to render it to the
121587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    // pbuffer to get access to RGB pixel data.  We also want to flip it
122587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    // upside-down for easy conversion to a bitmap.
123587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    int width = mEglWindow.getWidth();
124587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    int height = mEglWindow.getHeight();
125587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    status_t err = mExtTexProgram.blit(mExtTextureName, texMatrix, 0, 0,
126587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden            width, height, true);
127587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    if (err != NO_ERROR) {
128587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden        return err;
129587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    }
130587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden
131587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    // GLES only guarantees that glReadPixels() will work with GL_RGBA, so we
132587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    // need to get 4 bytes/pixel and reduce it.  Depending on the size of the
133587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    // screen and the device capabilities, this can take a while.
134587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    int64_t startWhenNsec, pixWhenNsec, endWhenNsec;
135587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    if (kShowTiming) {
136587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden        startWhenNsec = systemTime(CLOCK_MONOTONIC);
137587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    }
138587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    GLenum glErr;
139587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, mPixelBuf);
140587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    if ((glErr = glGetError()) != GL_NO_ERROR) {
141587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden        ALOGE("glReadPixels failed: %#x", glErr);
142587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden        return UNKNOWN_ERROR;
143587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    }
144587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    if (kShowTiming) {
145587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden        pixWhenNsec = systemTime(CLOCK_MONOTONIC);
146587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    }
147587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    reduceRgbaToRgb(mPixelBuf, width * height);
148587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    if (kShowTiming) {
149587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden        endWhenNsec = systemTime(CLOCK_MONOTONIC);
150587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden        ALOGD("got pixels (get=%.3f ms, reduce=%.3fms)",
151587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden                (pixWhenNsec - startWhenNsec) / 1000000.0,
152587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden                (endWhenNsec - pixWhenNsec) / 1000000.0);
153587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    }
154587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden
155587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    size_t rgbDataLen = width * height * kOutBytesPerPixel;
156e32106fd5175afdf939ae397aece9caf378a4912Benoit Goby
157e32106fd5175afdf939ae397aece9caf378a4912Benoit Goby    if (!rawFrames) {
158e32106fd5175afdf939ae397aece9caf378a4912Benoit Goby        // Fill out the header.
159e32106fd5175afdf939ae397aece9caf378a4912Benoit Goby        size_t headerLen = sizeof(uint32_t) * 5;
160e32106fd5175afdf939ae397aece9caf378a4912Benoit Goby        size_t packetLen = headerLen - sizeof(uint32_t) + rgbDataLen;
161e32106fd5175afdf939ae397aece9caf378a4912Benoit Goby        uint8_t header[headerLen];
162e32106fd5175afdf939ae397aece9caf378a4912Benoit Goby        setValueLE(&header[0], packetLen);
163e32106fd5175afdf939ae397aece9caf378a4912Benoit Goby        setValueLE(&header[4], width);
164e32106fd5175afdf939ae397aece9caf378a4912Benoit Goby        setValueLE(&header[8], height);
165e32106fd5175afdf939ae397aece9caf378a4912Benoit Goby        setValueLE(&header[12], width * kOutBytesPerPixel);
166e32106fd5175afdf939ae397aece9caf378a4912Benoit Goby        setValueLE(&header[16], HAL_PIXEL_FORMAT_RGB_888);
167e32106fd5175afdf939ae397aece9caf378a4912Benoit Goby        fwrite(header, 1, headerLen, fp);
168e32106fd5175afdf939ae397aece9caf378a4912Benoit Goby    }
169587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden
170587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    // Currently using buffered I/O rather than writev().  Not expecting it
171587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    // to make much of a difference, but it might be worth a test for larger
172587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    // frame sizes.
173587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    if (kShowTiming) {
174587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden        startWhenNsec = systemTime(CLOCK_MONOTONIC);
175587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    }
176587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    fwrite(mPixelBuf, 1, rgbDataLen, fp);
177587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    fflush(fp);
178587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    if (kShowTiming) {
179587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden        endWhenNsec = systemTime(CLOCK_MONOTONIC);
180587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden        ALOGD("wrote pixels (%.3f ms)",
181587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden                (endWhenNsec - startWhenNsec) / 1000000.0);
182587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    }
183587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden
184587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    if (ferror(fp)) {
185587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden        // errno may not be useful; log it anyway
186587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden        ALOGE("write failed (errno=%d)", errno);
187587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden        return UNKNOWN_ERROR;
188587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    }
189587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden
190587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    return NO_ERROR;
191587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden}
192587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden
193587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFaddenvoid FrameOutput::reduceRgbaToRgb(uint8_t* buf, unsigned int pixelCount) {
194587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    // Convert RGBA to RGB.
195587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    //
196587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    // Unaligned 32-bit accesses are allowed on ARM, so we could do this
197587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    // with 32-bit copies advancing at different rates (taking care at the
198587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    // end to not go one byte over).
199587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    const uint8_t* readPtr = buf;
200587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    for (unsigned int i = 0; i < pixelCount; i++) {
201587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden        *buf++ = *readPtr++;
202587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden        *buf++ = *readPtr++;
203587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden        *buf++ = *readPtr++;
204587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden        readPtr++;
205587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    }
206587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden}
207587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden
208587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden// Callback; executes on arbitrary thread.
20904f101c35eaa90b1f95939afac30674ec1611e6fDan Stozavoid FrameOutput::onFrameAvailable(const BufferItem& /* item */) {
210587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    Mutex::Autolock _l(mMutex);
211587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    mFrameAvailable = true;
212587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden    mEventCond.signal();
213587c6fefcd3c1d05c608ff511cf3534bc765256eAndy McFadden}
214