diff --git a/src/bitvlc/bitvlc.cpp b/src/bitvlc/bitvlc.cpp index 8d80da5..6038f22 100644 --- a/src/bitvlc/bitvlc.cpp +++ b/src/bitvlc/bitvlc.cpp @@ -17,9 +17,91 @@ */ #include "bitvlc.h" +#include "util/log.h" +#include "util/misc.h" +#include "util/lock.h" +#include +#include +#include +#include + +using namespace std; 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() @@ -28,13 +110,269 @@ CBitVlc::~CBitVlc() 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() { + 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(); } diff --git a/src/bitvlc/bitvlc.h b/src/bitvlc/bitvlc.h index 5c4b091..a2a5d41 100644 --- a/src/bitvlc/bitvlc.h +++ b/src/bitvlc/bitvlc.h @@ -19,6 +19,11 @@ #ifndef BITVLC_H #define BITVLC_H +#include +#include "util/debugwindow.h" +#include "util/condition.h" +#include "util/tcpsocket.h" + class CBitVlc { public: @@ -28,6 +33,39 @@ class CBitVlc void Setup(); void Process(); 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 diff --git a/src/bitvlc/main.cpp b/src/bitvlc/main.cpp index 8d0265b..fc374b2 100644 --- a/src/bitvlc/main.cpp +++ b/src/bitvlc/main.cpp @@ -17,9 +17,12 @@ */ #include "bitvlc.h" +#include "util/log.h" int main (int argc, char *argv[]) { + SetLogFile(".bitvlc", "bitvlc.log"); + CBitVlc bitvlc(argc, argv); bitvlc.Setup(); diff --git a/wscript b/wscript index 296faa1..dae8ab6 100644 --- a/wscript +++ b/wscript @@ -79,9 +79,12 @@ def build(bld): bld.program(source='src/bitvlc/main.cpp\ src/bitvlc/bitvlc.cpp\ + src/util/condition.cpp\ + src/util/debugwindow.cpp\ src/util/log.cpp\ src/util/misc.cpp\ src/util/mutex.cpp\ + src/util/thread.cpp\ src/util/timeutils.cpp\ src/util/tcpsocket.cpp', use=['m', 'rt', 'X11', 'Xrender', 'Xext', 'vlc'],