2004.08_Katalog filmów_[Programowanie].pdf

(860 KB) Pobierz
439034538 UNPDF
dla programistów
Katalog ilmów
Marek Sawerwain
W czerwcowym numerze
Listing 1. Klasa aplikacji
Linux+ opisałem prostą
aplikację napisaną w ję-
zyku C#. Była ona nie-
skomplikowana, ale jedną z jej zalet była
możliwość bardzo łatwego przenoszenia
kodu pomiędzy Linuksem a Windows.
Jeśli musimy napisać program, który
będzie łatwy do przeniesienia pomię-
dzy różnymi systemami, a C# czy Java z
jakichś powodów nie jest dla nas odpo-
wiednim wyborem, pozostaje nam C/C++
oraz wybór odpowiedniej biblioteki GUI.
Bardzo dobrym narzędziem, sprawdzają-
cym się znakomicie w takich zadaniach,
jest biblioteka o nazwie wxWidgets (kiedyś
ta biblioteka nazywała się wxWindows , ale
pewnej firmie się to nie spodobało i auto-
rzy byli zmuszeni zmienić nazwę).
Nowe rozwiązania najlepiej pozna-
wać na przykładach, więc napiszemy
katalog filmów.
# include "wx/wx.h"
class hw_app : public wxApp {
public :
virtual bool OnInit ();
} ;
DECLARE_APP ( hw_app )
jakiś mechanizm ich rozpoznawania, aby
sprawdzić, czy taki film już mamy. Tutaj
znakomicie sprawdzają się tzw. odciski
(ang. hash ). Istnieje wiele różnych algoryt-
mów, które je generują, a my wybierzemy
system, który jest stosowany w progra-
mach typu eDonkey.
Ostatnia sprawą, którą chciałbym po-
ruszyć, to ogólny schemat naszej aplikacji.
Tutaj, jak zawsze, najlepiej jest przygoto-
wać odpowiedni schemat, np. taki jak na
Rysunku 1. Na schemacie są cztery główne
okna (oznaczone kolorem żółtym). Mamy
okno główne, gdzie można utworzyć bazę
danych bądź przejść do trzech pozosta-
łych okien: okna dodawania nowego
filmu, okna wyszukiwania oraz okna
przeglądania i edycji danych. Kolorem nie-
bieskim zostały oznaczone najważniejsze
operacje na bazie danych. Wszystkie
operacje na danych najlepiej zgroma-
dzić w jednej klasie. Znacząco ułatwi
to napisanie nam programu. Dodatkowo,
do tej klasy dodamy również takie opera-
cje, jak wyznaczenie podpisu oraz wycią-
gnięcie pojedynczej klatki.
CD/DVD
Po uruchomieniu dystrybucji
Linux+ Live CD/DVD można
przetestować działanie
omawianego programu.
Założenia programu
Jak zawsze, zanim rozpocznie się pisanie
programu, warto poświęcić kilka chwil
na zastanowienie się, co dany program
ma robić.
Nasza aplikacja to baza danych.
Musimy wybrać odpowiedni system
baz danych. Podobnie jak przy bazie
teleadresowej z numeru czerwcowego,
będzie to system Sqlite , który przy tego
typu niewielkich bazach danych spraw-
dza się znakomicie. Tym razem będziemy
korzystać bezpośrednio z API Sqlite.
Zastanówmy się, co możemy przecho-
wywać w naszej bazie. Oprócz typowych
danych, takich jak tytuł filmu oraz imię i
nazwisko reżysera, możemy postarać się
o zapisanie w bazie jednej klatki z filmu.
Oczywiście, oznacza to, że nasze filmy
muszą być w postaci plików avi , mpg i
podobnych. Wyciągniecie klatki to dość
trudny problem, więc będziemy korzystać
z oddzielnej biblioteki, a będzie nią Xine .
Skoro w naszej bazie katalogujemy
filmy z plików, to warto byłoby dodać
Na płycie CD/DVD
Na płycie CD/DVD znajdują
się pliki źródłowe potrzebnych
bibliotek, kompletny kod źródłowy
omawianego programu, gotowy
program wykonywalny
oraz listingi z artykułu.
Wyświetlamy pewien
słynny komunikat
Programowanie w wxWidgets jest dość
podobne do innych tego typu rozwią-
zań, takich jak Qt , a nawet GTK+ . Pod
pewnymi względami jest nawet bardziej
wygodne, np. podłączenie funkcji do zda-
rzenia nie wymaga stosowania oddziel-
nego programu, tak jak w przypadku Qt.
Zanim zaczniemy pisać naszą aplikację, na
początek podam niewielki przykład.
Napiszemy nieskomplikowany pro-
gram, który wyświetla komunikat
O autorze
Autor zajmuje się tworzeniem
oprogramowania dla WIN32
i Linuksa. Zainteresowania: teoria
języków programowania oraz
dobra literatura. Kontakt
z autorem: autorzy@linux.com.pl
70 sierpień 2004
439034538.022.png 439034538.023.png 439034538.024.png 439034538.025.png 439034538.001.png 439034538.002.png 439034538.003.png
baza danych ilmów
dla programistów
Instalacja biblioteki
wxWidgets
Biblioteka wxWidgets w przypadku Linuk-
sa może być skompilowana na trzy różne
sposoby (a nawet na pięć, gdyż możemy
wykorzystać nadal stosowaną w środo-
wiskach uniksowych bibliotekę Motif lub
pakiet Wine ).
Pierwszy sposób dotyczy pakietu
wxX11 . Po kompilacji, która jak zwykle
sprowadza się do poleceń:
./configure –prefix=/opt
make
make install
Rysunek 1. Główne zdarzenia, które pojawiają się naszej aplikacji
otrzymamy pakiet, który wymaga tylko
i wyłącznie środowiska X do swojej pracy.
Stanowi to zaletę tej odmiany wxWidgets.
Drugim sposobem jest wykorzysta-
nie biblioteki GTK. Co ważne, należy
mieć zainstalowaną starszą wersję GTK
1.2.x. W naszej ilmowej bazie danych
korzystamy z pakietu wxGTK . Kompilację
przeprowadzamy podobnie jak powyżej,
ale skrypt conigure wywołujemy z nastę-
pującymi opcjami:
./configure --prefix=/opt S
--with-gtk --enable-accel
zawiera tylko jedną metodę: OnInit
– Listing 1 zawiera jej definicję. Podobnie
postąpimy w przypadku bazy danych
z filmami.
Treść Listingu 1 wyróżnia się tylko
jednym dodatkowym makrem: DECLA-
RE_APP . Implementuje ono stałe elementy,
a mianowicie funkcję wxGetApp . Ważniej-
sze czynności wykonujemy w składowej
OnInit , czyli tworzymy obiekt głównego
okna (z przyciskiem oraz etykietą). Pełną
treść implementacji zawiera Listing 2. W
metodzie OnInit funkcją SetTopWindow
powodujemy, że nowo utworzone okno
staję się głównym oknem naszej aplikacji.
Zamknięcie tego okna to zamknięcie całej
aplikacji.
Po ustaleniu głównego okna pozosta-
je nam wyświetlić je na ekranie polece-
niem: win->Show(TRUE); . Gdyby zamiast
wartości TRUE w argumencie Show podać
wartość FALSE , to okno zostałoby ukryte.
Na koniec metody OnInit zwracamy
wartość TRUE, co oznacza, że działanie
aplikacji ma być kontynuowane. Jeśli
podczas wykonywania wstępnych czyn-
ności napotkamy na jakieś problemy, np.
nie uzyskamy połączenia z bazą danych,
to zawsze można zwrócić wartość FALSE.
W takim przypadku działanie naszego
programu zostanie zakończone.
W implementacji zastosowaliśmy
makro IMPLEMENT_APP , w którym dokoń-
czymy tworzenie stałych elementów klasy,
reprezentujących aplikację (zadeklarowa-
ne wcześniej makrem DECLARE_APP ).
RE_CLASS definiujemy stałe elementy
klasy. Pojawia się również drugie makro:
DECLARE_EVENT_TABLE() . Jest ono odpo-
wiedzialne za definicję tabeli zdarzeń,
w której połączymy zdarzenia z metodami
klasy. Nasz pierwszy programik ma tylko
jedno zdarzenie, które jest generowane
w momencie kliknięcia na przycisk.
Z istotnych elementów, o których trzeba
wspomnieć, jest konstruktor hw_mainwin .
Przyjmuje on szereg parametrów, takich
jak wielkość okna i jego styl. Definicje
pominiętych stałych znajdują się w pli-
kach źródłowych (jak zawsze dostępnych
na płycie CD/DVD).
Zajmijmy się budowaniem interfejsu.
Wszystkie czynności z tym związane są
zawarte w konstruktorze hw_mainwin .
Pełną treść kodu konstruktora zawiera
Listing 4.
Zaczynamy od wywołania metody
wxFrame::Create i przekazania wszyst-
kich parametrów konstruktora. Utwo-
rzyliśmy w ten sposób ramkę, bo tak
w nomenklaturze wxWidgets nazywa się
podstawowy typ okna.
Następne trzy linie kodu to utworze-
nie tzw. pudełka. W bibliotece wxWid-
gets , podobnie jak w GTK+/GNOME,
Bardzo istotny dla naszego programu jest
parametr --enable-accel (ta opcja jest
odpowiedzialna za skróty klawiszowe),
gdyż w przeciwnym razie nasz projekt nie
skompiluje się.
Ostatni sposób polega na wykorzy-
staniu nowej wersji GTK 2.2 bądź 2.4.
Polecenie koniguracji wygląda w nastę-
pujący sposób:
./configure --prefix=/opt S
--with-gtk --enable-accel –enable-gtk2
Niestety, w przypadku wxWidgets 2.4.2
nasz program bazy ilmów nie kompiluje
się, gdy chcemy korzystać z tych wersji
GTK. Mogą wystąpić także problemy
z przykładami. Nowa wersja z pewnością
będzie już znacznie bardziej dopracowana.
Listing 2. Implementacja aplikacji
(a jakże by inaczej) Hello World! . Komu-
nikat będzie wyświetlany poprzez
widget wxStaticText – jest to bezpośred-
ni odpowiednik etykiety (ang. label )
z innych bibliotek. Oprócz etykiety,
w naszym pierwszym programiku znaj-
dzie się również przycisk, czyli wxButton .
Pierwszą czynnością, którą należy
wykonać, jest utworzenie klasy reprezen-
tującej całą aplikację. Klasa ta dziedziczy
z klasy wxApp i w naszym przypadku
hw_mainwin * win = new S
hw_mainwin ( NULL );
SetTopWindow ( win );
win -> Show ( TRUE );
return TRUE ;
}
IMPLEMENT_APP ( hw_app )
Etykieta i przycisk
Klasa hw_mainwin jest odpowiedzialna
za nasze główne okno aplikacji. Listing
3 zawiera jej definicję. Podobnie jak
w klasie aplikacji, makrem DECLA-
www.lpmagazine.org
71
# include "wx/wx.h"
# include "hw.h"
# include "hw_mainwin.h"
bool hw_app :: OnInit () {
439034538.004.png 439034538.005.png 439034538.006.png 439034538.007.png 439034538.008.png 439034538.009.png 439034538.010.png 439034538.011.png
 
dla programistów
Listing 3. Klasa reprezentująca okno
Obsługa zdarzenia „click”
W klasie hw_mainwin musimy jeszcze
umieścić jedną specjalną tablicę, gdzie
powiążemy odpowiednie widgety ze
zdarzeniami, które mogą wystąpić w apli-
kacji. Najważniejszym elementem są
identyfikatory kontrolek. W naszym przy-
padku interesujący nas przycisk posiada,
nadany przez nas, identyfikator o nazwie
IB_EndBTN . Połączenie identyfikatora
z odpowiednim zdarzeniem, czyli kliknię-
ciem na przycisk, wymaga zastosowania
makra EVT_BUTTON , natomiast sama tabela
zdarzeń prezentuje się następująco:
class hw_mainwin : public wxFrame {
DECLARE_CLASS ( hw_mainwin )
DECLARE_EVENT_TABLE ()
public :
hw_mainwin ();
hw_mainwin ( wxWindow * parent , wxWindowID id = HW_MAINWIN_IDNAME ,
const wxString & caption = HW_MAINWIN_TITLE ,
const wxPoint & pos = HW_MAINWIN_POSITION ,
const wxSize & size = HW_MAINWIN_SIZE ,
long style = HW_MAINWIN_STYLE );
void OnIbEndbtnClick ( wxCommandEvent & event );
...
} ;
mamy do czynienia ze specjalnymi
kontenerami, w których umieszczamy
widgety. Obiekty tego rodzaju samo-
czynnie dbają o odpowiednie skalowanie
komponentów czy ich wyrównywanie
w poziomie bądź w pionie. Podstawo-
wym komponentem jest pudełko wxBo-
xSizer , które, w zależności od podanego
parametru, może dbać o stosowne roz-
mieszczenie widgetów w pionie bądź
w poziomie.
Przyjrzyjmy się bliżej, w jaki sposób
tworzymy etykietę:
przekazać informacje o sposobie wyrów-
nania, np. jeśli chcemy, aby etykieta była
wyrównana do lewej strony, to podajemy
wartość wxALIGN_LEFT . Zwróćmy w tym
miejscu uwagę na to, że w przypadku
tworzenia przycisku również przyjmuje
on identyczny zestaw parametrów.
Pozostałe czynności dotyczące ety-
kiety są już bardzo proste, gdyż ustalamy
czcionkę:
BEGIN_EVENT_TABLE( hw_mainwin, wxFrame )
EVT_BUTTON( IB_EndBTN, hw_mainwin:: S
OnIbEndbtnClick )
END_EVENT_TABLE()
Pozostało nam określenie postaci samej
metody OnIbEndbtnClick . Zawiera ona
jedną linię kodu, a jest nią wywołanie
metody Close , które spowoduje zamknię-
cie okna i w efekcie całej aplikacji:
label_hw->SetFont(wxFont(16, wxSWISS, S
wxNORMAL, wxBOLD, FALSE));
void hw_mainwin::OnIbEndbtnClick
( wxCommandEvent& event ){ Close(TRUE);
}
wxStaticText* label_hw = new S
wxStaticText( this, ID_LBL, S
_("Hello World!"), wxDefaultPosition, S
wxDefaultSize, 0 );
Ostatnią czynnością jest dodanie etykiety
do pudełka wywołaniem: box_sizer-
>Add(...); . Jak widać, podobnie postępu-
jemy z przyciskiem. Również w naszym
docelowym programie będziemy postę-
pować w bardzo podobny sposób. Z tego
powodu warto przeanalizować ten krótki
program, ponieważ w dalszej części arty-
kułu skupimy się na innych (trudniejszych)
aspektach niż budowanie interfejsu.
Klasa zarządzania danymi
– data_class
Zajmiemy się teraz naszą główną aplika-
cją. Podstawowa klasa to niewątpliwie
data_class . Zawiera ona kilkanaście
metod (krótką charakterystykę kilku
przedstawia Tabela 1). Oprócz dwóch
metod: edonkey_hash oraz make_screen ,
pozostałe składowe tej klasy odno-
Pierwszym argumentem jest odwołanie
się do wskaźnika na okno. Następnie
podajemy identyfikator. W naszym przy-
kładzie przyjął on następującą definicję:
#define ID_LBL 10002
Wartość identyfikatora może być inna,
ale autorzy biblioteki zalecają, aby były
to numery większe od 10000. Wartości
niższe są stosowane przez wewnętrzne
mechanizmy biblioteki. Podobnie okre-
ślamy identyfikator dla przycisku czy
dowolnego innego widgetu. Po okre-
śleniu identyfikatora podajemy treść,
jaka jest wyświetlana przez etykietę.
Koniecznie korzystamy z makra _() , gdyż
w przeciwnym razie mielibyśmy kłopoty
podczas kompilacji kodu. Kolejne para-
metry to pozycja etykiety oraz jej wiel-
kość. Jak widać na Listingu 4, podane
zostały wartości domyślnie. Za stero-
wanie położeniem i wielkością będzie
odpowiedzialne pudełko. Dość istotny
jest ostatni parametr – styl. Można tam
Listing 4. Treść konstruktora hw_mainwin
wxFrame :: Create ( parent , id , caption , pos , size , style );
wxBoxSizer * box_sizer = new wxBoxSizer ( wxVERTICAL );
this -> SetSizer ( box_sizer );
this -> SetAutoLayout ( TRUE );
wxStaticText * label_hw = new wxStaticText ( this , ID_LBL , _ ( "Hello World!" ) , S
wxDefaultPosition , wxDefaultSize , 0 );
label_hw -> SetFont ( wxFont ( 16 , wxSWISS , wxNORMAL , wxBOLD , FALSE ));
box_sizer -> Add ( label_hw , 0 , wxALIGN_CENTER_HORIZONTAL | wxALL | wxADJUST_MINSIZE , 5 );
wxButton * button_ok = new wxButton ( this , IB_EndBTN , _ ( "OK" ) , wxDefaultPosition ,
wxDefaultSize , 0 );
box_sizer -> Add ( button_ok , 0 , wxALIGN_CENTER_HORIZONTAL | wxALL , 5 );
GetSizer ()-> Fit ( this );
GetSizer ()-> SetSizeHints ( this );
Centre ();
72
sierpień 2004
439034538.012.png 439034538.013.png 439034538.014.png 439034538.015.png
 
baza danych ilmów
dla programistów
Projektowanie interfejsu
Biblioteka wxWidgets istnieje od ponad
10 lat. Doczekała się wielu dodatkowych
narzędzi, a jednym z nich jest Dialog-
Blocks . Jest to program do budowania
interfejsu graicznego za pomocą myszki.
Program generuje również całą klasę
oraz samodzielnie zarządza zdarzenia-
mi. Co najważniejsze, nie niszczy kodu
wpisanego przez programistę w innym
edytorze dopóty, dopóki nie usunie się
specjalnie oznaczonych komentarzy. Nie-
stety, program ten ma jedną zasadniczą
wadę – jest narzędziem komercyjnym.
Mimo wszystko zachęcam wszystkich
do jego wypróbowania. Tym bardziej, że
wersja niezarejestrowana pozwala na
edycję naszej bazy danych. W źródłach
znajduje się gotowy projekt do wykorzy-
stania (plik ilm_mainwin.pjd ).
Tym z czytelników, którzy nie akcep-
tują takiego rozwiązania, polecam inny
program, ale już w pełni darmowy i otwar-
ty, o nazwie wxGlade . Nie jest on aż tak
bardzo dopracowany jak DialogBlocks,
ale potrai generować kod nie tylko dla
C++, ale również dla Pythona i Perla
(czego nie potrai DialogBlocks).
funkcję „wytrych” o nazwie: sqlite_exec_
printf . Nie bez powodu w swojej nazwie
zawiera ona nazwę standardowej funkcji
printf , gdyż, podobnie jak oryginal-
na funkcja printf , przyjmuje wzorzec,
w którym stosowane są znaki procentu,
np. %d , oznaczający liczbę całkowitą.
Sposoby użycia sqlite_exec_printf
najlepiej poznać na przykładach. Zobacz-
my, jak wygląda utworzenie tabeli (część
wyrażenia SQL została skrócona):
typu %s , %d czy %Q . W klasie data_class
większość metod wykorzystuje tę cechę.
Najprostszym przykładem jest metoda
delete_data_id , usuwająca rekord
o wskazanym id . Jej kod przedstawia się
następująco:
db_error=sqlite_exec_printf S
(db, "delete from films where id=%d", S
0, 0, &zErrMsg, id);
db_error=sqlite_exec_printf S
(db, "create table films ( S
id integer primary key, S
... S
hash_edonkey char(50));",0,0,&zErrMsg);
W drugim parametrze, w poleceniu SQL,
został umieszczony wzorzec %d (polecenie
przekształcenia dla liczby całkowitej). Po
wszystkich pięciu obowiązkowych parame-
trach, podobnie jak w printf , musimy podać
wartość naszej liczby, czyli zmienną id .
O jednym wzorcu %Q warto wspomnieć
raz jeszcze. Polecenia SQL wymagają, aby
dane tekstowe były objęte apostrofami.
Przekształcenie %Q spełnia ten warunek,
uwalniając nas od żmudnego wstawiania
apostrofów w tekście formatującym.
Ostatnim przykładem, który należy
omówić, jest zastosowanie funkcji call-
back. W naszym programie w taki
sposób będziemy przekazywać dane do
tabel wxGrid . Wzorcowym przykładem
jest metoda search_file_name :
Pierwszy argument sqlite_exec_print to
uchwyt reprezentujący bazę danych. Po
nim podajemy ciąg znaków, czyli pole-
cenie SQL przeznaczone do wykonania.
Jak widać na powyższym przykładzie,
zakończone średnikiem. W trzecim argu-
mencie można podać funkcję callback ,
która będzie wywoływana podczas
odbierania danych. Będziemy ją wykorzy-
stywać w przypadku zapytań typu select ,
gdyż create table nie zwraca żadnych
dodatkowych danych. Następny argument
służy do podania dodatkowego parame-
tru, który zostanie później przekazany
do funkcji zwrotnej. Piątym w kolejności
elementem wywołania jest już znana
zmienna zErrMsg , w której, w przypadku
wystąpienia błędu, zostanie umieszczony
wskaźnik do tekstu komunikatu.
Po tych pięciu obowiązkowych para-
metrach mogą wystąpić następne, jeśli
w poleceniu SQL zostały użyte wzorce
szą się bezpośrednio do systemu baz
danych.
Zanim zaczniemy korzystać z bazy,
należy uzyskać do niej dostęp. Otwarcie
bazy następuje w konstruktorze tej klasy
i przedstawia się następująco:
void data_class::search_file_name(char S
*fn, sqlite_callback fnc, void *d ) {
db_error=sqlite_exec_printf(db, "select S
* from films where file_name S
like '%s%'", S
fnc, d, &zErrMsg, fn); S
}
zErrMsg=0;
db = sqlite_open(fdb_name, 0, &zErrMsg);
if( db == 0 )db_error=-1;
W zmiennej zErrMsg znajdzie się wskaza-
nie na tekst komunikatu o błędzie. Funkcja
sqlite_open samoczynnie założy plik bazy
danych w momencie, gdy nie będzie on
istniał. Warto też pamiętać, że funkcja
sqlite_open (oraz pozostałe z API Sqlite)
dają wartość jeden, gdy nie nastąpiły błędy.
Jak na razie drugi parametr tej funkcji,
gdzie podano zero, nie jest używany przez
API. Być może w przyszłości będzie można
poprzez ten parametr wskazać, że baza ma
być używana w trybie tylko do odczytu.
Zamknięcie bazy danych to jedna
instrukcja: sqlite_close(db); . Ponieważ
nasza klasa używa konstruktora do
otwarcia bazy, to zamknięcie bazy nastę-
puje w destruktorze.
Może wydawać się, że różne typy zapy-
tań mogą wymagać różnego podejścia,
ale autorom Sqlite udało się przygotować
Metoda przyjmuje, podobnie jak dwie
pozostałe: search_title oraz search_
Tabela 1. Najważniejsze metody klasy data_class
Metoda Opis
data_class(char *fdb_name); konstruktor otwiera istniejącą bazę danych
lub zakłada nową
void add_data_full(char *ile_name, ... ); dodaje do bazy danych jeden pełny rekord
razem ze zrzutem ekranowym
void get_data( sqlite_callback, void *d ); pobiera wszystkie dane (metoda przezna-
czona do wypełniania tabel – wxGrid)
void delete_data_id(int id);
usuwa rekord o wskazany ID
void search_title(char *t, sqlite_callback,
void *d );
szuka w bazie ilmu to tytule
void edonkey_hash(char *fname,
char *hash);
wyznacza sumę kontrolną (hash, odcisk) dla
podanego pliku
void make_screen(char *fname_video,
char *pic_ile, char *codec_fourcc);
„wyciąga” klatkę z ilmu i zapisuje ją pod
podaną nazwą jako plik jpeg
void create_table()
utworzenie tabeli
www.lpmagazine.org
73
439034538.016.png 439034538.017.png
 
dla programistów
Listing 5. Zapis danych do pliku jpeg przy użyciu biblioteki DevIL
pimy się tylko na plikach typu mpg , avi
i podobnych.
Zanim wyciągniemy z filmu jakie-
kolwiek dane, tworzymy cztery ważne
zmienne:
void DevILSaveTo ( char * fname , int width , int height , void * ptr ) {
ILuint imageID ;
ilGenImages ( 1 , & imageID );
ilBindImage ( imageID );
ilTexImage ( width , height , 1 , 3 , IL_RGB , IL_UNSIGNED_BYTE , ptr );
iluFlipImage ();
iluScale ( 160 , 100 , 1 );
ilEnable ( IL_FILE_OVERWRITE );
ilSaveImage ( fname );
ilDeleteImages ( 1 , & imageID );
}
xine_t *xine;
xine_stream_t *stream;
xine_vo_driver_t *vo_driver;
xine_ao_driver_t *ao_driver;
Następnie dokonujemy ich inicjalizacji.
W tym miejscu trzeba zwrócić uwagę
na wykorzystanie sterowników audio
i wideo. Dźwięk nie jest nam potrzebny,
dlatego poza pierwszym parametrem,
w którym podajemy uchwyt do biblio-
teki, w dwóch pozostałych podajemy
wartości NULL (wartość ta oznacza rów-
nież, że to Xine samodzielnie znajdzie
odpowiedni sterownik). Nie możemy
tak postąpić w przypadku obrazu.
W drugim parametrze funkcji xine_
open_video_driver podajemy identyfi-
kator sterownika, przy pomocy którego
chcemy wyświetlać obraz. My nie chcemy
oglądać filmu, więc podajemy wartość
none . Podobnie postępujemy w trzecim
parametrze, gdzie dokładniej określamy
rodzaj i typ urządzania, np. dla środowiska
X czy biblioteki GTK. Podaliśmy tu wartość
XINE_VISUAL_TYPE_NONE , co oznacza, że
film będzie dekodowany do wewnętrzne-
go bufora w pamięci RAM. Kod inicjalizacji
jest następujący:
director , trzy parametry. Pierwszy to
nazwa pliku, który chcemy odnaleźć.
Następny to funkcja callback. Będzie ona
wywoływana przy każdym znalezionym
rekordzie. Pozwoli to na przepisanie
rekordów do tabeli. Ostatni parametr
o nazwie d umożliwi przekazanie wska-
zania na obiekt wxGrid . Funkcję fnc
bezpośrednio przekazujemy do sqlite_
exec_printf . W dalszej części omówię
dokładniej postać funkcji zwrotnej na
przykładzie wyszukiwania danych.
w następujący sposób: jeśli odczytamy
wartość 120 , to w tablicy msg umieścimy
tekst w postaci %120 . Wykonuje to nastę-
pującą pętla:
j=0;
for(i=0;i<len;i++) {
memset(value, 0, 16);
sprintf(value, "%%%3d", *(tbl + i));
msg[j++]=value[0]; msg[j++]=value[1];
msg[j++]=value[2]; msg[j++]=value[3];
}
Zapis klatki do bazy
danych
W następnym punkcie pokażę, jak
uzyskać klatkę z pliku wideo. Wcze-
śniej trzeba wspomnieć o sposobie
wprowadzania danych do bazy. Klatka
filmu zostanie zapisana do pliku w for-
macie JPG. Niestety, Sqlite w wersji 2.8
nie obsługuje w wygodny sposób
danych binarnych i wymaga od nas
konwersji do formatu ASCII (3.0 już
radzi sobie z tym typem danych, ale
podczas pracy nad artykułem dostępna
była tylko wersja alpha ). Jest to spo-
wodowane m.in. tym, że do wpisania
nowego rekordu wykorzystamy polece-
nie insert .
Wpisaniem pełnego rekordu wraz
z obrazem zajmuje się metoda add_data_
full .
Na początku otwieramy plik: f=o-
pen(scr_filename, O_RDONLY); . Do
zmiennej len zapisujemy długość pliku.
Możemy w ten sposób przydzielić odpo-
wiednią ilość pamięci:
Zadanie konwersji z wartości binarnej
na tekstową zrzuciliśmy na barki funkcji
sprintf i ciąg przekształcający %%%3d .
W zmiennej value znajdzie się tekst
w dokładnie takiej postaci, jak wcześniej
założyliśmy. Po wykonaniu całej pętli
tablica msg będzie wypełniona wartościa-
mi ASCII, więc bez kłopotów można je
przekazać w wywołaniu funkcji sqlite_
exec_printf .
Łapanie klatki ilmu
Prawdopodobnie najciekawszą funkcją
naszej bazy danych jest przechowywa-
nie jednej klatki z filmu. Zanim dołą-
czymy do naszej bazy plik zawierający
klatkę (w niskiej rozdzielczości, bo tylko
160x100), należy znaleźć w miarę łatwy
sposób na uzyskanie obrazu. Wyko-
rzystamy w tym celu bardzo dobrą
bibliotekę, a mianowicie Xine . Służy
ona do odtwarzania różnego rodzaju
plików audio/wideo, w tym także płyt
DVD czy VCD. My, dla ułatwienia, sku-
xine=xine_new();
xine_init(xine);
ao_driver = xine_open_audio_driver
(xine, NULL, NULL);
vo_driver = xine_open_video_driver
(xine, "none", XINE_VISUAL_TYPE_NONE,
NULL);
stream=xine_stream_new
(xine, ao_driver, vo_driver);
Teraz możemy już otworzyć plik z filmem.
Wystarczy tylko jedna linia kodu:
error=xine_open(stream, fname_video);
Listing 6. Odczytywanie klatki ilmu z bazy danych
tbl=new unsigned char[len];
msg=new char[(len*5)];
memset(msg, 0, (len*5));
s = DataTableGrd -> GetCellValue ( event . GetRow () , 0 );
s . ToLong (& id );
main_db -> get_blob_for_id (( int ) id , "/tmp/pic.jpg" , callback_for_blob );
if ( main_db -> error_level ()== 0 ) {
bmp . LoadFile ( _ ( "/tmp/pic.jpg" ) , wxBITMAP_TYPE_JPEG );
FrameBMP -> SetBitmap ( bmp );
}
Do tablicy tbl wczytujemy całą zawartość
pliku: read(f, (void*)tbl, len); . Pozosta-
je nam teraz dokonać konwersji danych
74
sierpień 2004
439034538.018.png 439034538.019.png 439034538.020.png 439034538.021.png
 
Zgłoś jeśli naruszono regulamin