Psvbi
From M1Research
psvbi - Pseudo VBI
Contents |
Overview
psvbi software complex was developed for trasmitting control commands in TV video signal. Data block coded into some lines of video signal and could be used as marks for remote stations or analytic system. It much easy to implement and control (and diag) instead of real VBI due to high cost of equipment.
How does it works
Base Theory
Data to transmit coded by 14 bytes block. Each block transmitted in one video frame. Data encoded as luminance part of video signal (most stable to MPEG encoding/decoding) in some video lines (in our case we prefer to use top 2..4 lines - not visible on home equipment, only professional monitors with turned on underscan area could be used to SEE if data real present). During coding data used Hamming Error Correction Code and permutations alorithms to make more stable transmitting data, but some times it was noticed false data detection :-(((, use control sum algorithms in analizator to check if it's your real data packed :-))).
Embedding data
Embedding data performed by VirtualDub software (real greate program). Embedding and encoding data performed by our plug-in.
Make sure you have VirtualDub and copied pseudovbi.vdf to VirtualDub's plugin folder.
Start VirtualDub, open desired video file (in our case we use Matrox Digisuite for capturing and playback). Open Video -> Filters and select pseudovbi plugin. As result you can see plugin dialog window:
Encoding/Embeding parametes:
- Start encoding from - define start frame number of video to insert data;
- Frames to encode - number of frames (duration on frames) of encoded data block;
- Data block - hexadiminal presentation of 14 bytes lenght data block;
- Setup/"Luma" level - defines value is used to encode data (0...255);
- Inserting Position - this blocks define where data will be inserted (position on screen). We prefer use top 2 rows (video lines) to insert;
- BLOCK - enable/disable insert block;
- COLUMN - start position (from left) where data begin to inserts
- ROW - row/line number where data begin to inserts;
Result of plugin: (fragment of VirtualDub program interface)
As result you can see a dot sequence in first 2 lines of frame. Now data block embedded. You can print this video to tape or video server.
Demembedding of data
Dembedding of data performed by another part of software.
This software capture video using V4L2 base interface (we use bttv878 based capture card) perform alization and in a case of detection datablock it call plugins method that perform higher level data decoding (application level).
Application startup parameters:
[root@dev-2 psvbi]# ./psvbid --help
Usage: ./psvbid <args>
args:
--ident=S Identification, tag for displaying (i.e M1int-AUX)
--aux-console=S Use another console for logging (i.e. /dev/tty2)
--white-level=N White level of signal
--pos-x-start=N left 'x' position of search block
--pos-x-stop=N right 'x' position of search block
--pos-y-start=N top 'y' position of search block
--pos-y-stop=N bottom 'y' position of search block
--video-dev=S video device to grab video from
--video-dev=S input number of video device
--load-plugin=S load handler plugin
--info-about-video show supported inputs and formats of video board
--version Program version
--help Show program usage
Real application startup:
[root@dev-2 decoder]# ./psvbid --load-plugin=m1cmds --pos-y-stop=5
API interface for own analizator
Demebedder (psvbid) application perform all operation on "transport" level. To operate on higher level (application) software use plugins set. Each new task required new plugin developing (depends on application level data usage).
Basic plugin structure:
Include required headers:
#include <stdio.h> #include "psvbi.h" #include "psvbid-plugins.h"
Plugins is dynamic loadable library. You can use my_init and my_fini to initialize data structures:
void __attribute__ ((constructor)) my_init(void)
{
printf("debug::my_init\n");
};
void __attribute__ ((destructor)) my_fini(void)
{
printf("debug::my_fini\n");
};
After detecting of code block (or fail to detect) main program calls function detect_ok (or detect_ok, that you can make empty) of all plugins chain loaded:
static int detect_ok(struct psvbi_block* block)
{
// some code to operate with block
// calc checksum, check data is correct
return 0;
};
static int detect_fail(struct psvbi_block* block)
{
return 0;
};
Argument supplied to functions is struct psvbi_block:
struct psvbi_block
{
// in/out datas
unsigned char decoded[PSVBI_DATA_LEN]; // data block to coded/restored
unsigned char encoded[PSVBI_PACKET_LEN_PACKED]; // coded data block
// statistic
unsigned char decode_errors[PSVBI_DATA_LEN]; // errors numbers for bytes
unsigned char decode_corrections[PSVBI_DATA_LEN]; // number of corrected bit
int errors; // total errors count during decoding
int corrections; // total count of corrected bits
// decoding/coding controls
int _h; // horizontal and vertical positions of
int _v; // found block
/*
int state; // state-machine controls
void* data; // input - output data
int offset; // offset from begining
*/
};
Visualization of plugin working/status performed in 2 way:
- 1. Displaying statical values of some variables.
- 2. Displaying messages list.
struct psvbid_plugin has additional fields:
...
struct
{
struct timeval time;
char text[1024];
} log[MAX_LOG_PLUGINS_EVENTS];
pthread_mutex_t log_lock;
char status_header[1024];
char status_data[1024];
psvbid_plugin_status_data_builder status_data_builder;
...
Field status_header could be staticaly initialized and store header of statical descriptive data:
struct psvbid_plugin PLUGIN =
{
....
.status_header = "| D.CALL| D.CORR| S.STATE| S.CALL| ACKS| NACKS| ERRS| TOUTS|THRD.E| THRD.S|",
....
};
Field status_data contains string formated desciptive values. status_data is been rebuilded by calling status_data_builder function outside plugin code. This function perform building formatted string into status_data buffer.
Field log is used for storing events list. To push event into list i use macro. Its build message and scroll messages list:
/* macro sample */
#define LOG_EVENT(X...) \
{ \
int ___k; \
pthread_mutex_lock(&PLUGIN.log_lock); \
for(___k = (MAX_LOG_PLUGINS_EVENTS - 1); ___k > 0 ; ___k--) \
PLUGIN.log[___k] = PLUGIN.log[___k - 1]; \
gettimeofday(&PLUGIN.log[0].time, NULL); \
sprintf(PLUGIN.log[0].text, X); \
pthread_mutex_unlock(&PLUGIN.log_lock); \
};
/* call sample */
// notify ACK
LOG_EVENT("ACK[%d], CMD[%.8lX]", (reply>>8)&0xFF, cmd);
At last you should create a plugin description struct, that define methods and name (classic):
/* ---------------------------------------------------------------------------
Define plugin attributest
----------------------------------------------------------------------------*/
struct psvbid_plugin PLUGIN =
{
.name = "m1cmds",
.ok = detect_ok,
.fail = detect_fail,
.status_data_builder = status,
.status_header = "| D.CALL| D.CORR| S.STATE| S.CALL| ACKS| NACKS| ERRS| TOUTS|THRD.E| THRD.S|",
.log_lock = PTHREAD_MUTEX_INITIALIZER
};
Software
Version 2.0
- psvbi-2.0.bin.tar.bz2 - precompiled (FC3) version of software;
- psvbi-2.0.tar.bz2 - source code;
Version 1.0
- pseudovbi.vdf - VirtualDub plugin, (precompiled win32 application);
- psvbi-1.0.bin.tar.bz2 - precompiled (FC3) version of software;
- psvbi-1.0.tar.bz2 - source code;
Real example
This is most interesting part of whole this story. I try to describe step by step system configuration and creation additional software plugins.
Task
There is playback videoserver that accept command throw RS-232 interface to start/stop playback. There are a lot of video materials that should be replaced by another one that supplied by videoserver.
Videoserver Control
Lets next commands set / config options will be default to control videoserver.
Interface RS-232:
BaudRate = (UINT) CBR_38400
ByteSize = (BYTE) 8
Parity = (BYTE) ODDPARITY
StopBits = (BYTE) ONESTOPBIT
Control commands (fixed length, 4 byte command with STX and ETX)
02 01 01 FF
---- ---- ---- ----
STX CMD1 CMD2 ETX
where:
STX - start head
CMD1,CMD2 - 2 byte command code
ETX - end of command
Reply (sent from videoserver, 2 byte block) :
04 00 - ACK
05 XX - NACK (where XX is error code)
List of available commands:
CMD1 CMD2
---- ----
01 01 - start playback (and control MCS)
02 01 - stop playback (and control MCS)
Command encoding
This is a little agreement about command encoding/embedding into video stream:
# REPLACE
02 01 01 FF 02 01 01 FF 02 01 01 FF 19 78
# RETURN
02 02 01 FF 02 02 01 FF 02 02 01 FF 19 78
# INCORRECT (not tripled data)
01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E
# INCORRECT (incorrect command)
02 02 01 FA 02 02 01 FA 02 02 01 FA 19 78
# INCORRECT (unknown command)
02 03 03 FF 02 03 03 FF 02 03 03 FF 19 78
***NB
command interval is 100 frames
command repeated in 2 frames
luma 128
blocks (24,0),(25,1)
This defines data blocks, that makes videos "marked". (Incorrect commands used with test video to check system for correct application level error detection)
Writing own plugin
Now we are ready to write own plugin for translating commands from videostream into commands set accepted by videoserver:
- 1. Create a skeleton application (using
m1cmds.cplugin); - 2. Add initialization section to setup
ttydevice or other communication device; - 3. Create a block code of command verification.
- 4. Create a block code of status/state logging/displaying.
As result you can see the file: m1cmds.c
Licence
psvbi
(Pseudo-VBI encoding/decoding system)
Copyright (C) 2005 Maksym Veremeyenko.
This file is part of psvbi project (marking video signal and detecting
marks in real-time)
psvbi 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 2 of the License, or
(at your option) any later version.
psvbi 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 psvbi; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA



