added: bitvlc, playes a video using libvlc and sends it to the bitpanel
This commit is contained in:
parent
700ec3abf2
commit
af13595a90
4 changed files with 385 additions and 3 deletions
|
@ -17,9 +17,91 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "bitvlc.h"
|
#include "bitvlc.h"
|
||||||
|
#include "util/log.h"
|
||||||
|
#include "util/misc.h"
|
||||||
|
#include "util/lock.h"
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
CBitVlc::CBitVlc(int argc, char *argv[])
|
CBitVlc::CBitVlc(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
m_debug = false;
|
||||||
|
m_debugscale = 2;
|
||||||
|
m_instance = NULL;
|
||||||
|
m_player = NULL;
|
||||||
|
m_media = NULL;
|
||||||
|
m_port = 1337;
|
||||||
|
m_address = NULL;
|
||||||
|
m_width = 120;
|
||||||
|
m_height = 48;
|
||||||
|
m_plane = NULL;
|
||||||
|
m_volume = 0;
|
||||||
|
m_dither = false;
|
||||||
|
|
||||||
|
const char* flags = "p:a:m:d:v:f";
|
||||||
|
int c;
|
||||||
|
while ((c = getopt(argc, argv, flags)) != -1)
|
||||||
|
{
|
||||||
|
if (c == 'p') //port
|
||||||
|
{
|
||||||
|
int port;
|
||||||
|
if (!StrToInt(string(optarg), port) || port < 0 || port > 65535)
|
||||||
|
{
|
||||||
|
LogError("Wrong argument \"%s\" for port", optarg);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_port = port;
|
||||||
|
}
|
||||||
|
if (c == 'd') //debug
|
||||||
|
{
|
||||||
|
int scale;
|
||||||
|
if (!StrToInt(string(optarg), scale) || scale < 0 || scale > 65535)
|
||||||
|
{
|
||||||
|
LogError("Wrong argument \"%s\" for debug scale", optarg);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_debugscale = scale;
|
||||||
|
m_debug = true;
|
||||||
|
}
|
||||||
|
else if (c == 'a') //address
|
||||||
|
{
|
||||||
|
m_address = optarg;
|
||||||
|
}
|
||||||
|
else if (c == 'm') //media
|
||||||
|
{
|
||||||
|
m_media = optarg;
|
||||||
|
}
|
||||||
|
if (c == 'v') //volume
|
||||||
|
{
|
||||||
|
int volume;
|
||||||
|
if (!StrToInt(string(optarg), volume) || volume < 0 || volume > volume)
|
||||||
|
{
|
||||||
|
LogError("Wrong argument \"%s\" for volume", optarg);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_volume = volume;
|
||||||
|
}
|
||||||
|
else if (c == 'f') //dither
|
||||||
|
{
|
||||||
|
m_dither = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_media == NULL)
|
||||||
|
{
|
||||||
|
LogError("No media given (use -m path)");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_address == NULL)
|
||||||
|
m_debug = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
CBitVlc::~CBitVlc()
|
CBitVlc::~CBitVlc()
|
||||||
|
@ -28,13 +110,269 @@ CBitVlc::~CBitVlc()
|
||||||
|
|
||||||
void CBitVlc::Setup()
|
void CBitVlc::Setup()
|
||||||
{
|
{
|
||||||
}
|
if (m_debug)
|
||||||
|
m_debugwindow.Enable(m_width, m_height, m_debugscale);
|
||||||
|
|
||||||
void CBitVlc::Process()
|
m_instance = libvlc_new(0, NULL);
|
||||||
{
|
if (m_instance == NULL)
|
||||||
|
{
|
||||||
|
LogError("Unable to init VLC");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
libvlc_media_t* media = libvlc_media_new_path(m_instance, m_media);
|
||||||
|
m_player = libvlc_media_player_new_from_media(media);
|
||||||
|
libvlc_media_release(media);
|
||||||
|
|
||||||
|
libvlc_video_set_callbacks(m_player, SVLCLock, SVLCUnlock, SVLCDisplay, this);
|
||||||
|
|
||||||
|
InitPlane(m_width, m_height);
|
||||||
|
|
||||||
|
m_process = false;
|
||||||
|
m_display = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CBitVlc::Cleanup()
|
void CBitVlc::Cleanup()
|
||||||
{
|
{
|
||||||
|
if (m_player)
|
||||||
|
libvlc_media_player_release(m_player);
|
||||||
|
|
||||||
|
if (m_instance);
|
||||||
|
libvlc_release(m_instance);
|
||||||
|
|
||||||
|
delete[] m_plane;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBitVlc::Process()
|
||||||
|
{
|
||||||
|
libvlc_media_player_play(m_player);
|
||||||
|
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
if (!m_socket.IsOpen() && m_address)
|
||||||
|
{
|
||||||
|
if (m_socket.Open(m_address, m_port, 1000000) != SUCCESS)
|
||||||
|
{
|
||||||
|
LogError("Failed to connect: %s", m_socket.GetError().c_str());
|
||||||
|
m_socket.Close();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log("Connected");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CLock lock(m_condition);
|
||||||
|
while (!m_process)
|
||||||
|
m_condition.Wait();
|
||||||
|
m_process = false;
|
||||||
|
|
||||||
|
libvlc_audio_set_volume(m_player, m_volume);
|
||||||
|
|
||||||
|
unsigned int videowidth = 0;
|
||||||
|
unsigned int videoheight = 0;
|
||||||
|
libvlc_video_get_size(m_player, 0, &videowidth, &videoheight);
|
||||||
|
int neededwidth = Round32((float)videowidth * m_height / videoheight);
|
||||||
|
int neededheight = Round32((float)videoheight * m_width / videowidth);
|
||||||
|
if (neededwidth > m_width)
|
||||||
|
InitPlane(neededwidth, m_height);
|
||||||
|
else
|
||||||
|
InitPlane(m_width, neededheight);
|
||||||
|
|
||||||
|
int xplanestart = (m_planewidth - m_width) / 2;
|
||||||
|
int yplanestart = (m_planeheight - m_height) / 2;
|
||||||
|
int xplaneend = xplanestart + m_width;
|
||||||
|
int yplaneend = yplanestart + m_height;
|
||||||
|
|
||||||
|
int avg = 0;
|
||||||
|
for (int y = yplanestart; y < yplaneend; y++)
|
||||||
|
{
|
||||||
|
uint8_t* planeptr = m_plane + y * m_planewidth * 3 + xplanestart * 3;
|
||||||
|
uint8_t* end = planeptr + m_width * 3;
|
||||||
|
while (planeptr != end)
|
||||||
|
{
|
||||||
|
avg += *(planeptr++);
|
||||||
|
avg += *(planeptr++);
|
||||||
|
planeptr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
avg /= m_width * m_height * 2;
|
||||||
|
|
||||||
|
CTcpData data;
|
||||||
|
data.SetData(":00");
|
||||||
|
uint8_t last;
|
||||||
|
|
||||||
|
if (m_dither)
|
||||||
|
{
|
||||||
|
for (int y = yplanestart; y < yplaneend; y++)
|
||||||
|
{
|
||||||
|
uint8_t* planeptr = m_plane + y * m_planewidth * 3 + xplanestart * 3;
|
||||||
|
int value;
|
||||||
|
int quanterror;
|
||||||
|
for (int x = xplanestart; x < xplaneend; x++)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 2; i++)
|
||||||
|
{
|
||||||
|
if (*planeptr > avg)
|
||||||
|
{
|
||||||
|
value = 255;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
quanterror = *planeptr - value;
|
||||||
|
*planeptr = value;
|
||||||
|
|
||||||
|
if (x < xplaneend - 1)
|
||||||
|
{
|
||||||
|
*(planeptr + 3) = Clamp(*(planeptr + 3) + quanterror * 7 / 16, 0, 255);
|
||||||
|
if (y < xplaneend - 1)
|
||||||
|
*(planeptr + 3 + m_planewidth * 3) = Clamp(*(planeptr + 3 + m_planewidth * 3) + quanterror / 16, 0, 255);
|
||||||
|
}
|
||||||
|
if (y < xplaneend - 1)
|
||||||
|
{
|
||||||
|
*(planeptr + m_planewidth * 3) = Clamp(*(planeptr + m_planewidth * 3) + quanterror * 5 / 16, 0, 255);
|
||||||
|
if (x > xplanestart)
|
||||||
|
*(planeptr - 3 + m_planewidth * 3) = Clamp(*(planeptr - 3 + m_planewidth * 3) + quanterror * 3 / 16, 0, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
planeptr++;
|
||||||
|
}
|
||||||
|
planeptr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
avg = 128;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
for (int y = yplanestart; y < yplaneend; y++)
|
||||||
|
{
|
||||||
|
uint8_t* planeptr = m_plane + y * m_planewidth * 3 + xplanestart * 3;
|
||||||
|
uint8_t* end = planeptr + m_width * 3;
|
||||||
|
uint8_t line[m_width / 4];
|
||||||
|
uint8_t* lineptr = line;
|
||||||
|
int pixelcount = 3;
|
||||||
|
memset(line, 0, sizeof(line));
|
||||||
|
|
||||||
|
while (planeptr != end)
|
||||||
|
{
|
||||||
|
planeptr++;
|
||||||
|
if (*(planeptr++) > avg)
|
||||||
|
*lineptr |= 1 << (pixelcount * 2); //green
|
||||||
|
if (*(planeptr++) > avg)
|
||||||
|
*lineptr |= 1 << (pixelcount * 2 + 1); //red
|
||||||
|
pixelcount--;
|
||||||
|
if (pixelcount == -1)
|
||||||
|
{
|
||||||
|
pixelcount = 3;
|
||||||
|
lineptr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y == yplaneend - 1)
|
||||||
|
{
|
||||||
|
//dont add the last byte here, it will be sent later
|
||||||
|
data.SetData(line, sizeof(line) - 1, true);
|
||||||
|
last = line[sizeof(line) - 1];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data.SetData(line, sizeof(line), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//send everything but the last byte, since the bitpanel is double buffered
|
||||||
|
//the timing is improved by sending only the last byte when the frame needs to be displayed
|
||||||
|
lock.Leave();
|
||||||
|
if (m_socket.IsOpen())
|
||||||
|
{
|
||||||
|
if (m_socket.Write(data) != SUCCESS)
|
||||||
|
{
|
||||||
|
LogError("%s", m_socket.GetError().c_str());
|
||||||
|
m_socket.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_debugwindow.DisplayFrame(data);
|
||||||
|
lock.Enter();
|
||||||
|
|
||||||
|
while (!m_display)
|
||||||
|
m_condition.Wait();
|
||||||
|
m_display = false;
|
||||||
|
lock.Leave();
|
||||||
|
|
||||||
|
uint8_t end[10] = {};
|
||||||
|
data.SetData(&last, 1);
|
||||||
|
data.SetData(end, sizeof(end), true);
|
||||||
|
|
||||||
|
if (m_socket.IsOpen())
|
||||||
|
{
|
||||||
|
if (m_socket.Write(data) != SUCCESS)
|
||||||
|
{
|
||||||
|
LogError("%s", m_socket.GetError().c_str());
|
||||||
|
m_socket.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_debugwindow.DisplayFrame(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
libvlc_media_player_stop(m_player);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBitVlc::InitPlane(int width, int height)
|
||||||
|
{
|
||||||
|
if (m_planewidth != width || m_planeheight != height)
|
||||||
|
{
|
||||||
|
Log("Setting plane to %ix%i", width, height);
|
||||||
|
m_planewidth = width;
|
||||||
|
m_planeheight = height;
|
||||||
|
delete[] m_plane;
|
||||||
|
m_plane = new uint8_t[m_planewidth * m_planeheight * 3];
|
||||||
|
memset(m_plane, 0, m_planewidth * m_planeheight * 3);
|
||||||
|
//reset the player to update to the new format
|
||||||
|
libvlc_media_player_stop(m_player);
|
||||||
|
libvlc_video_set_format(m_player, "RV24", m_planewidth, m_planeheight, m_planewidth * 3);
|
||||||
|
libvlc_media_player_play(m_player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void* CBitVlc::SVLCLock (void *opaque, void **planes)
|
||||||
|
{
|
||||||
|
return ((CBitVlc*)opaque)->VLCLock(planes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBitVlc::SVLCUnlock (void *opaque, void *picture, void *const *planes)
|
||||||
|
{
|
||||||
|
((CBitVlc*)opaque)->VLCUnlock(picture, planes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBitVlc::SVLCDisplay(void *opaque, void *picture)
|
||||||
|
{
|
||||||
|
((CBitVlc*)opaque)->VLCDisplay(picture);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* CBitVlc::VLCLock (void **planes)
|
||||||
|
{
|
||||||
|
m_condition.Lock();
|
||||||
|
planes[0] = m_plane;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBitVlc::VLCUnlock (void *picture, void *const *planes)
|
||||||
|
{
|
||||||
|
m_process = true;
|
||||||
|
m_condition.Signal();
|
||||||
|
m_condition.Unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBitVlc::VLCDisplay(void *picture)
|
||||||
|
{
|
||||||
|
m_condition.Lock();
|
||||||
|
m_display = true;
|
||||||
|
m_condition.Signal();
|
||||||
|
m_condition.Unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,11 @@
|
||||||
#ifndef BITVLC_H
|
#ifndef BITVLC_H
|
||||||
#define BITVLC_H
|
#define BITVLC_H
|
||||||
|
|
||||||
|
#include <vlc/vlc.h>
|
||||||
|
#include "util/debugwindow.h"
|
||||||
|
#include "util/condition.h"
|
||||||
|
#include "util/tcpsocket.h"
|
||||||
|
|
||||||
class CBitVlc
|
class CBitVlc
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -28,6 +33,39 @@ class CBitVlc
|
||||||
void Setup();
|
void Setup();
|
||||||
void Process();
|
void Process();
|
||||||
void Cleanup();
|
void Cleanup();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_debug;
|
||||||
|
int m_debugscale;
|
||||||
|
CDebugWindow m_debugwindow;
|
||||||
|
|
||||||
|
int m_port;
|
||||||
|
const char* m_address;
|
||||||
|
CTcpClientSocket m_socket;
|
||||||
|
const char* m_media;
|
||||||
|
libvlc_instance_t* m_instance;
|
||||||
|
libvlc_media_player_t* m_player;
|
||||||
|
int m_volume;
|
||||||
|
int m_width;
|
||||||
|
int m_height;
|
||||||
|
bool m_dither;
|
||||||
|
CCondition m_condition;
|
||||||
|
uint8_t* m_plane;
|
||||||
|
int m_planewidth;
|
||||||
|
int m_planeheight;
|
||||||
|
|
||||||
|
bool m_process;
|
||||||
|
bool m_display;
|
||||||
|
|
||||||
|
void InitPlane(int width, int height);
|
||||||
|
|
||||||
|
static void* SVLCLock (void *opaque, void **planes);
|
||||||
|
static void SVLCUnlock (void *opaque, void *picture, void *const *planes);
|
||||||
|
static void SVLCDisplay(void *opaque, void *picture);
|
||||||
|
|
||||||
|
void* VLCLock (void **planes);
|
||||||
|
void VLCUnlock (void *picture, void *const *planes);
|
||||||
|
void VLCDisplay(void *picture);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //BITVLC_H
|
#endif //BITVLC_H
|
||||||
|
|
|
@ -17,9 +17,12 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "bitvlc.h"
|
#include "bitvlc.h"
|
||||||
|
#include "util/log.h"
|
||||||
|
|
||||||
int main (int argc, char *argv[])
|
int main (int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
SetLogFile(".bitvlc", "bitvlc.log");
|
||||||
|
|
||||||
CBitVlc bitvlc(argc, argv);
|
CBitVlc bitvlc(argc, argv);
|
||||||
|
|
||||||
bitvlc.Setup();
|
bitvlc.Setup();
|
||||||
|
|
3
wscript
3
wscript
|
@ -79,9 +79,12 @@ def build(bld):
|
||||||
|
|
||||||
bld.program(source='src/bitvlc/main.cpp\
|
bld.program(source='src/bitvlc/main.cpp\
|
||||||
src/bitvlc/bitvlc.cpp\
|
src/bitvlc/bitvlc.cpp\
|
||||||
|
src/util/condition.cpp\
|
||||||
|
src/util/debugwindow.cpp\
|
||||||
src/util/log.cpp\
|
src/util/log.cpp\
|
||||||
src/util/misc.cpp\
|
src/util/misc.cpp\
|
||||||
src/util/mutex.cpp\
|
src/util/mutex.cpp\
|
||||||
|
src/util/thread.cpp\
|
||||||
src/util/timeutils.cpp\
|
src/util/timeutils.cpp\
|
||||||
src/util/tcpsocket.cpp',
|
src/util/tcpsocket.cpp',
|
||||||
use=['m', 'rt', 'X11', 'Xrender', 'Xext', 'vlc'],
|
use=['m', 'rt', 'X11', 'Xrender', 'Xext', 'vlc'],
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue