Commit 9979f93a authored by Tristan Matthews's avatar Tristan Matthews
Browse files

* #12475: video: use new shm backend, video size is still buggy

parent 48f7af81
......@@ -112,16 +112,6 @@
</method>
<method name="startPreview" tp:name-for-bindings="startPreview">
<arg type="i" name="width" direction="out">
</arg>
<arg type="i" name="height" direction="out">
</arg>
<arg type="i" name="shmKey" direction="out">
</arg>
<arg type="i" name="semKey" direction="out">
</arg>
<arg type="i" name="videoBufferSize" direction="out">
</arg>
</method>
<method name="stopPreview" tp:name-for-bindings="stopPreview">
......
......@@ -160,15 +160,10 @@ VideoControls::getSettings() {
return videoPreference_.getSettings();
}
void VideoControls::startPreview(int32_t &width, int32_t &height,
int32_t &shmKey, int32_t &semKey,
int32_t &videoBufferSize)
void VideoControls::startPreview()
{
if (preview_.get()) {
ERROR("Video preview was already started!");
shmKey = -1;
semKey = -1;
videoBufferSize = -1;
return;
}
......@@ -177,10 +172,6 @@ void VideoControls::startPreview(int32_t &width, int32_t &height,
map<string, string> args(videoPreference_.getSettings());
preview_.reset(new sfl_video::VideoPreview(args));
width = atoi(args["width"].c_str());
height = atoi(args["height"].c_str());
preview_->getShmInfo(shmKey, semKey, videoBufferSize);
}
void VideoControls::stopPreview()
......
......@@ -98,8 +98,7 @@ class VideoControls : public org::sflphone::SFLphone::VideoControls_adaptor,
std::string getInputDeviceRate();
std::string getCurrentCodecName(const std::string &callID);
void startPreview(int32_t &width, int32_t &height, int32_t &shmKey,
int32_t &semKey, int32_t &bufferSize);
void startPreview();
void stopPreview();
};
......
......@@ -10,8 +10,7 @@ libvideo_la_SOURCES = video_endpoint.cpp video_endpoint.h libav_utils.cpp \
video_preview.h video_preview.cpp video_v4l2.cpp \
video_v4l2_list.cpp video_v4l2.h video_v4l2_list.h \
video_preferences.h video_preferences.cpp \
packet_handle.h packet_handle.cpp shared_memory.cpp \
shared_memory.h check.h shm_sink.cpp shm_sink.h
packet_handle.h packet_handle.cpp check.h shm_header.h shm_sink.cpp shm_sink.h
libvideo_la_LIBADD = @LIBAVCODEC_LIBS@ @LIBAVFORMAT_LIBS@ @LIBAVDEVICE_LIBS@ @LIBSWSCALE_LIBS@ @LIBAVUTIL_LIBS@ @CCRTP_LIBS@ @UDEV_LIBS@
......
/*
* Copyright (C) 2004-2012 Savoir-Faire Linux Inc.
*
* Author: Tristan Matthews <tristan.matthews@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Additional permission under GNU GPL version 3 section 7:
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#include "shared_memory.h"
// shm includes
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h> /* semaphore functions and structs. */
#include <sys/shm.h>
#include <cstdlib>
#include <stdexcept>
#include "manager.h"
#include "logger.h"
#include "dbus/video_controls.h"
#include "fileutils.h"
namespace sfl_video {
namespace { // anonymous namespace
#if _SEM_SEMUN_UNDEFINED
union semun {
int val; /* value for SETVAL */
struct semid_ds *buf; /* buffer for IPC_STAT & IPC_SET */
unsigned short int *array; /* array for GETALL & SETALL */
struct seminfo *__buf; /* buffer for IPC_INFO */
};
#endif
void cleanupSemaphore(int semaphoreSetID)
{
semctl(semaphoreSetID, 0, IPC_RMID);
}
/*
* function: sem_signal. signals the process that a frame is ready.
* input: semaphore set ID.
* output: none.
*/
void sem_signal(int semaphoreSetID)
{
/* structure for semaphore operations. */
sembuf sem_op;
/* signal the semaphore - increase its value by one. */
sem_op.sem_num = 0;
sem_op.sem_op = 1;
sem_op.sem_flg = 0;
semop(semaphoreSetID, &sem_op, 1);
}
/* join and/or create a shared memory segment */
int createShmKey()
{
/* connect to and possibly create a segment with 644 permissions
(rw-r--r--) */
srand(time(NULL));
int proj_id = rand();
return ftok(fileutils::get_program_dir(), proj_id);
}
int createShmID(int key, int numBytes)
{
int shm_id = shmget(key, numBytes, 0644 | IPC_CREAT);
if (shm_id == -1)
ERROR("shmget:%m");
return shm_id;
}
/* attach a shared memory segment */
uint8_t *attachShm(int shm_id)
{
/* attach to the segment and get a pointer to it */
uint8_t *data = reinterpret_cast<uint8_t*>(shmat(shm_id, (void *) 0, 0));
if (data == reinterpret_cast<uint8_t *>(-1)) {
ERROR("shmat:%m");
data = NULL;
}
return data;
}
void detachShm(uint8_t *data)
{
/* detach from the segment: */
if (data and shmdt(data) == -1)
ERROR("shmdt:%m");
}
void destroyShm(int shm_id)
{
/* destroy it */
shmctl(shm_id, IPC_RMID, NULL);
}
void cleanupShm(int shm_id, uint8_t *data)
{
detachShm(data);
destroyShm(shm_id);
}
int createSemaphoreKey(int shmKey)
{
key_t key;
do
key = ftok(fileutils::get_program_dir(), rand());
while (key == shmKey);
return key;
}
int createSemaphoreSetID(int semaphoreKey)
{
/* first we create a semaphore set with a single semaphore,
whose counter is initialized to '0'. */
int semaphoreSetID = semget(semaphoreKey, 1, 0600 | IPC_CREAT);
if (semaphoreSetID == -1) {
ERROR("semget:%m");
throw std::runtime_error("Could not create semaphore set");
}
/* semaphore value, for semctl(). */
union semun sem_val;
sem_val.val = 0;
semctl(semaphoreSetID, 0, SETVAL, sem_val);
return semaphoreSetID;
}
} // end anonymous namespace
SharedMemory::SharedMemory(VideoControls &controls) :
videoControls_(controls),
shmKey_(0),
shmID_(0),
shmBuffer_(0),
semaphoreSetID_(0),
semaphoreKey_(0),
dstWidth_(0),
dstHeight_(0),
bufferSize_(0),
shmReady_()
{}
void SharedMemory::allocateBuffer(int width, int height, int size)
{
dstWidth_ = width;
dstHeight_ = height;
bufferSize_ = size;
shmKey_ = createShmKey();
shmID_ = createShmID(shmKey_, bufferSize_);
shmBuffer_ = attachShm(shmID_);
semaphoreKey_ = createSemaphoreKey(shmKey_);
semaphoreSetID_ = createSemaphoreSetID(semaphoreKey_);
shmReady_.signal();
}
void SharedMemory::publishShm()
{
DEBUG("Publishing shm: %d sem: %d size: %d", shmKey_, semaphoreKey_,
bufferSize_);
videoControls_.receivingEvent(shmKey_, semaphoreKey_, bufferSize_,
dstWidth_, dstHeight_);
}
void SharedMemory::waitForShm()
{
shmReady_.wait();
}
void SharedMemory::frameUpdatedCallback()
{
// signal the semaphore that a new frame is ready
sem_signal(semaphoreSetID_);
}
SharedMemory::~SharedMemory()
{
// free shared memory resources
videoControls_.stoppedReceivingEvent(shmKey_, semaphoreKey_);
// make sure no one is waiting for the SHM event which will never come if we've error'd out
shmReady_.signal();
cleanupSemaphore(semaphoreSetID_);
cleanupShm(shmID_, shmBuffer_);
}
} // end namespace sfl_video
/*
* Copyright (C) 2011, 2012 Savoir-Faire Linux Inc.
* Author: Tristan Matthews <tristan.matthews@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Additional permission under GNU GPL version 3 section 7:
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#ifndef SHARED_MEMORY_H_
#define SHARED_MEMORY_H_
#include "cc_thread.h"
#include "noncopyable.h"
class VideoControls;
namespace sfl_video {
class SharedMemory {
private:
NON_COPYABLE(SharedMemory);
/*-------------------------------------------------------------*/
/* These variables should be used in thread (i.e. run()) only! */
/*-------------------------------------------------------------*/
VideoControls &videoControls_;
int shmKey_;
int shmID_;
uint8_t *shmBuffer_;
int semaphoreSetID_;
int semaphoreKey_;
int dstWidth_;
int dstHeight_;
int bufferSize_;
ost::Event shmReady_;
public:
SharedMemory(VideoControls &controls);
~SharedMemory();
void frameUpdatedCallback();
void waitForShm();
// Returns a pointer to the memory where frames should be copied
uint8_t *getTargetBuffer() { return shmBuffer_; }
int getShmKey() const { return shmKey_; }
int getSemaphoreKey() const { return semaphoreKey_; }
int getBufferSize() const { return bufferSize_; }
void allocateBuffer(int width, int height, int size);
void publishShm();
};
}
#endif // SHARED_MEMORY_H_
......@@ -46,7 +46,6 @@ struct SHMHeader {
int buffer_size;
char data[0];
SHMHeader() : notification(), mutex(), buffer_gen(0), buffer_size(0) {}
};
#endif
......@@ -148,15 +148,15 @@ SHMSink::resize_area(size_t desired_length)
return true;
}
void SHMSink::render(char *data, size_t len)
void SHMSink::render(const std::vector<unsigned char> &data)
{
shm_lock();
if (!resize_area(sizeof(SHMHeader) + len))
if (!resize_area(sizeof(SHMHeader) + data.size()))
return;
memcpy(shm_area_->data, data, len);
shm_area_->buffer_size = len;
memcpy(shm_area_->data, &(*data.begin()), data.size());
shm_area_->buffer_size = data.size();
shm_area_->buffer_gen++;
sem_post(&shm_area_->notification);
shm_unlock();
......
......@@ -38,6 +38,7 @@
#include <semaphore.h>
#include <string>
#include <vector>
#include "noncopyable.h"
class SHMHeader;
......@@ -52,7 +53,7 @@ class SHMSink {
bool resize_area(size_t desired_length);
void render(char *data, size_t len);
void render(const std::vector<unsigned char> &data);
private:
NON_COPYABLE(SHMSink);
......
c++ shm_src.cpp shmclient.cpp -o shmclient `pkg-config --cflags --libs clutter-1.0` -lrt -pthread -O2
......@@ -34,7 +34,7 @@
*/
#include "shm_src.h"
#include "shm_header.h"
#include "../shm_header.h"
#include <sys/mman.h>
#include <fcntl.h>
#include <cstdio>
......
......@@ -37,7 +37,7 @@
#define SHM_SRC_H_
#include <string>
#include "noncopyable.h"
#include "../../noncopyable.h"
class SHMHeader;
// Example Shared memory source, only useful for testing
......@@ -46,17 +46,16 @@ class SHMHeader;
class SHMSrc {
public:
SHMSrc(const std::string &shm_name);
virtual ~SHMSrc() {};
bool start();
bool stop();
bool resize_area();
void render(char *data, size_t len);
private:
NON_COPYABLE(SHMSrc);
virtual void render(char *data, size_t len);
protected:
void shm_lock();
void shm_unlock();
std::string shm_name_;
......@@ -64,6 +63,9 @@ class SHMSrc {
SHMHeader *shm_area_;
size_t shm_area_len_;
unsigned buffer_gen_;
private:
NON_COPYABLE(SHMSrc);
};
#endif // SHM_SRC_H_
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h> /* semaphore functions and structs. */
#include <sys/shm.h>
#include <clutter/clutter.h>
#define TEMPFILE "/tmp/frame.txt"
#if _SEM_SEMUN_UNDEFINED
union semun
{
int val; /* value for SETVAL */
struct semid_ds *buf; /* buffer for IPC_STAT & IPC_SET */
unsigned short int *array; /* array for GETALL & SETALL */
struct seminfo *__buf; /* buffer for IPC_INFO */
};
#endif
typedef struct {
unsigned size;
unsigned width;
unsigned height;
} FrameInfo;
struct AppData {
unsigned width;
unsigned height;
char *shm_buffer;
int sem_set_id;
ClutterActor *texture;
};
FrameInfo getFrameSize()
{
FrameInfo info;
/* get message out of the file */
FILE *tmp = fopen(TEMPFILE, "r");
fscanf(tmp, "%u\n%u\n%u\n", &info.size, &info.width, &info.height);
printf("Size is %u\n", info.size);
printf("Width is %u\n", info.width);
printf("Height is %u\n", info.height);
fclose(tmp);
return info;
}
int get_sem_set()
{
/* this variable will contain the semaphore set. */
int sem_set_id;
key_t key = ftok("/tmp", 'b');
/* semaphore value, for semctl(). */
union semun sem_val;
/* first we get a semaphore set with a single semaphore, */
/* whose counter is initialized to '0'. */
sem_set_id = semget(key, 1, 0600);
if (sem_set_id == -1) {
perror("semget");
exit(1);
}
sem_val.val = 0;
semctl(sem_set_id, 0, SETVAL, sem_val);
return sem_set_id;
}
/*
* function: sem_wait. wait for frame from other process
* input: semaphore set ID.
* output: none.
*/
void
sem_wait(int sem_set_id)
{
/* structure for semaphore operations. */
struct sembuf sem_op;
/* wait on the semaphore, unless it's value is non-negative. */
sem_op.sem_num = 0;
sem_op.sem_op = -1;
sem_op.sem_flg = 0;
semop(sem_set_id, &sem_op, 1);
}
/* join and/or create a shared memory segment */
int getShm(unsigned numBytes)
{
key_t key;
int shm_id;
/* connect to a segment with 600 permissions
(r--r--r--) */
key = ftok("/tmp", 'c');
shm_id = shmget(key, numBytes, 0644);
return shm_id;
}
/* attach a shared memory segment */
char *attachShm(int shm_id)
{
char *data = NULL;
/* attach to the segment and get a pointer to it */
data = shmat(shm_id, (void *)0, 0);
if (data == (char *)(-1)) {
perror("shmat");
data = NULL;
}
return data;
}
void detachShm(char *data)
{
/* detach from the segment: */
if (shmdt(data) == -1) {
perror("shmdt");
}
}
/* round integer value up to next multiple of 4 */
int round_up_4(int value)
{
return (value + 3) &~ 3;
}
void readFrameFromShm(int width, int height, char *data, int sem_set_id,
ClutterActor *texture)
{
sem_wait(sem_set_id);
clutter_texture_set_from_rgb_data (CLUTTER_TEXTURE(texture),
(void*)data,
FALSE,
width,
height,
round_up_4(3 * width),
3,
0,
NULL);
}
gboolean updateTexture(gpointer data)
{
struct AppData *app = (struct AppData*) data;
readFrameFromShm(app->width, app->height, app->shm_buffer, app->sem_set_id,
app->texture);
return TRUE;
}
int main(int argc, char *argv[])
{
/* Initialize Clutter */
if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS)
return 1;