Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

[C++, Linux] Funkcja system(), a przechwytywanie tekstu

Ostatnio zmodyfikowano 2012-04-22 19:33
Autor Wiadomość
Kemsan
Temat założony przez niniejszego użytkownika
[C++, Linux] Funkcja system(), a przechwytywanie tekstu
» 2012-04-21 23:33:44
Witam,

Od pewnego czasu użytkuję serwer RTMP w C++ - CRTMPServer, chwalę sobie ten serwer, jednakże chciałbym go troszkę rozbudować i tu zaczynają się schody.
Jestem webmasterem - php i js nie mają dla mnie tajemnic, liznąłem też delphi, pythona, troszkę ruby on rails i javy, jednakże nijak nigdy nie miałem czasu na języki z rodziny C - C, C++ i w tym leży mój największy problem. Chciałbym przerobić kilka rzeczy w serwerze, wiem jakby to rozwiązać w każdym z języków które znam, tylko nie w C++. Obecnie dorobiłem własny system uwierzytelniania - ponieważ crtmpserver wspiera tylko uwierzytelnianie typu adobe, a program xsplit (w którym streamuje większość osób) nie posiada takiej opcji - musiałem dorobić własną alternatywę. Plan który sobie założyłem - użytkownik jako nazwę streamu wpisuje dwie frazy - nazwę uzytkownika oraz hasło w posaci Nick_Hasło, w kodzie rozwiązałem to tak - przy rejestracji streamu przez rtmp (w kodzie protokołu rtmp, w serwerze crtmp) dodałem linijkę która rozbija podaną nazwę na dwa człony (użyłem wbudowanej funkcji split), wczytuje listę użytkowników, sprawdza czy dany użytkownik jest na liście z danym hasłem. Kod:
C/C++
string streamName = M_INVOKE_PARAM( request, 1 );
string::size_type qpos = streamName.find( '?' );
if( qpos != string::npos )
{
    streamName = streamName.substr( 0, qpos );
}

if( streamName != "" )
{
    vector < string > aStreamUser;
    split( streamName, "_", aStreamUser );
   
    streamName = aStreamUser[ 0 ];
    string streamPassword = aStreamUser[ 1 ];
   
    Variant hUsers;
    if( !ReadLuaFile( "/etc/crtmpserver/main_users.lua", "users", hUsers ) )
    {
        FATAL( "Unable to read users file: /etc/crtmpserver/main_users.lua" );
        return false;
    }
   
    INFO( "Stream name: %s ||| Stream password %s", STR( streamName ), STR( streamPassword ) );
   
    if( hUsers.HasKey( streamName ) )
    {
        if(( VariantType ) hUsers[ streamName ] == V_STRING )
        {
            if( hUsers[ streamName ] != streamPassword )
            {
                FATAL( "User `%s` typed wrong password `%s` != `%s`", STR( streamName ), STR( hUsers[ streamName ] ), STR( streamPassword ) );
                return false;
            }
        }
        else
        {
            FATAL( "Invalid users file: /etc/crtmpserver/main_users.lua" );
            return false;
        }
    }
    else
    {
        FATAL( "User `%s` not present in users file: /etc/crtmpserver/main_users.lua", STR( streamName ) );
        return false;
    }
   
   
    if( aStreamUser.size() == 3 )
    {
        string streamAdd = aStreamUser[ 2 ];
        streamName = format( "%s_%s", STR( streamName ), STR( streamAdd ) );
    }
}
  Tyle mniej więcej słowem wstępu, przejdźmy do meritum - w streamch ważny jest wybór rozdzielczości (pewnie każdy wie o co mi chodzi, mając dajmy na to 1mbs nie oglądniemy wygodnie streamu w 1080p). Plan jest dla mnie prosty - zaraz po rozpoczęciu streamu (po utworzeniu pliku "nagrywanego" [crtmpserver pozwala na bieżąco ściągać stream na dysk]) uruchamiamy polecenie mediainfo sprawdzające wielkość video (ważny element - wysokość, to wg. niej ustalamy "p"). W kodzie php wygląda to tak:

              //Execute stream info
                exec( 'mediainfo --Output=XML media/'.$strStream.'.flv', $aResult );
              
                $aXML = simplexml_load_string( strtr( implode( "\n", $aResult ), array( '& '=>'&amp;' ) ) );
              
                $iWidth = array_shift( explode( ' ', ( string ) $aXML -> File -> track[ 1 ] -> Width ) );
                $iHeight = array_shift( explode( ' ', ( string ) $aXML -> File -> track[ 1 ] -> Height ) );
              
                $aTypes = array( );
              
                if( $iHeight >= 1080 )
                {
                        $aTypes[ '1080p' ] = '1920x1080';
                }
              
                if( $iHeight >= 720 )
                {
                        $aTypes[ '720p' ] = '1280x720';
                }
              
                if( $iHeight > 480 )
                {
                        $aTypes[ '480p' ] = '854x480';
                }     
              
                if( $iHeight > 360 )
                {
                        $aTypes[ '360p' ] = '640x360';
                }     
              
                return $aTypes;
  Następnie muszę "restreamować" całość poprzez ffmpeg (on pozwala na konwertowanie "w locie" video i przesyłanie na serwer) - podgląd na komendę i jej zastosowanie:

    STREAM - nazwa streamu (np Kemsan)
    SIZE - wielkość (np 640x360)
    PASS - hasło / klucz użytkownika
    TYPE_NAME - nazwa jakości (np 360p)
    
    avconv -re -i media/[STREAM].flv -ar 44100 -acodec copy -s [SIZE] -f flv rtmp://188.165.232.15/live/[STREAM]_[PASS]_[TYPE_NAME]
  Ogólnie w kodzie widzę to tak:
W funkcji która rozpoczyna nagrywanie streamu wrzucam funkcję która wywoła mediainfo na pliku STREAM.flv i zwróci dane o wielkości. Dane te posprawdzam tak jak w php - ustalę które jakości mają być aktualnie użyte. Następnie uruchomię ffmpeg z danymi wielkościami X razy (tyle ile jest jakości). Niestety to tylko teoria, o ile jeszcze z autoryzacją sobie poradziłem, to na tym etapie wysiadam, dlatego zwracam się do was o pomoc.

Pozdrawiam Kemsan
P-54978
DejaVu
» 2012-04-22 12:42:43
Opisałeś zadanie, które chcesz wykonać, a nie faktyczny element języka C++, który sprawia Ci problem. Jeżeli umiesz programować to określ czego konkretnie nie wiesz jak zapisać, a np. w PHP nie stanowiło to dla Ciebie problemu. Obecnie nikt nie wie jak Ci pomóc bo nie wiadomo czy czekasz, aż ktoś napisze za Ciebie to, czego potrzebujesz czy też masz inny problem, którego w sumie nie wskazałeś stricte na forum.

PS. My jesteśmy leniwi i nie chodzimy po linkach typu 'pastebin' :) Wklejaj kod w treść postów - szczegóły: » Kurs STC » Kolorowanie składniKolorowanie składni języka C++ lekcja.
P-54994
Kemsan
Temat założony przez niniejszego użytkownika
» 2012-04-22 12:53:51
Ogólnie nie wiem w jaki sposób "złapać" to co zwraca mediainfo - wiem, że do wykonywania cmd w systemie służy funkcja system, szukałem w google informacji o wykonaniu funkcji i "złapaniu" wartości które zwraca - z tego co pamiętam to było wykorzystanie funkcji popen, korzystałem z podpowiedzi na StackOverflow - jednakże próba kompilacji kodu kończyła się fiaskiem - nie zgadzały się typy zmiennych / nie zgadzała się ilość argumentów funkcji. 

Nie potrafię przerobić tego:

exec( 'mediainfo --Output=XML media/'.$strStream.'.flv', $aResult );
             
$aXML = simplexml_load_string( strtr( implode( "\n", $aResult ), array( '& '=>'&amp;' ) ) );
             
$iWidth = array_shift( explode( ' ', ( string ) $aXML -> File -> track[ 1 ] -> Width ) );
$iHeight = array_shift( explode( ' ', ( string ) $aXML -> File -> track[ 1 ] -> Height ) );

na C++

PS. Dodałem kody tak jak prosiłeś :)
PS2: Tu nawet nie chodzi o to, że nie potrafię w C++ pisać, bo podstawy znam (wasz kurs czytałem praktycznie w kilka minut, bo to wszystko ma tak dużo wspólnego z PHP, że łatwe było ogarnięcie całości), lecz wysiadam przy moim problemie.
P-54997
DejaVu
» 2012-04-22 12:57:36
Reasumując:
1. Chcesz wywołać z poziomu C++ aplikację XYZ.
2. Aplikacja XYZ wypisuje coś na ekran (aplikacja nie wymaga interakcji użytkownika).
3. To co wypisuje aplikacja XYZ chcesz móc odczytać z poziomu C++.
O to chodzi?

/edit:
Napisz jeszcze system operacyjny pod jakim kompilujesz swoją aplikację w C++.
P-54998
Kemsan
Temat założony przez niniejszego użytkownika
» 2012-04-22 13:01:49
Mniej więcej, sam program to serwer rtmp który działa sobie w tle na daemonie, program nie ma nic wypisywać - czyli pomijamy punkt drugi. Chce dane z tego programu wykorzystać tak jak w kodzie php.

//Debian
P-54999
DejaVu
» 2012-04-22 13:06:05
http://cpp0x.pl/forum/temat/?id=6101

/edit:
A do przetwarzania XML polecam libxml++ (konfiguracja może być upierdliwa), bądź TinyXML (prostsze, ale konwertowanie polskich znaków wymaga więcej zachodu).
P-55000
Kemsan
Temat założony przez niniejszego użytkownika
» 2012-04-22 19:28:15
Udało mi się wykodzić takie coś:
C/C++
if( _restreamBase == false )
{
    FILE * pipein_fp;
    char readbuf[ 80 ];
    int iHeight;
   
    vector < string > aStreamUser;
    split( _streamName, "/", aStreamUser );
   
    string strStream = aStreamUser[ aStreamUser.size() - 1 ];
    string::size_type qpos = strStream.find( '.' );
    if( qpos != string::npos )
    {
        strStream = strStream.substr( 0, qpos );
    }
   
    INFO( ">>>>>>>>>>>>>>>>>>>>>>>>>>> Startuje %s", STR( strStream ) );
   
    string strCommand;
    strCommand = format( "cd /media/; mediainfo --Inform=\"Video;%Height%\" %s.flv", STR( strStream ) );
   
    /* Tworzymy jednokierunkowy potok za pomocą popen() */
    pipein_fp = popen( strCommand.c_str(), "r" );
   
    /* Przetwarzanie */
    while( fgets( readbuf, 80, pipein_fp ) )
    {
        iHeight = strtol( readbuf, NULL, 10 );
    }
   
    if( iHeight > 0 )
    {
        Variant hUsers;
        if( !ReadLuaFile( "/etc/crtmpserver/main_users.lua", "users", hUsers ) )
        {
            FATAL( "Unable to read users file: /etc/crtmpserver/main_users.lua" );
            return false;
        }
       
        string strPassword = hUsers[ strStream ];
       
        if( iHeight >= 1080 )
        {
            strCommand = format( "avconv -re -i %s -ar 44100 -acodec copy -s 1920x1080 -f flv rtmp://188.165.232.15/live/%s_%s_1080p", STR( _streamName ), STR( strStream ), STR( strPassword ) );
            system( strCommand.c_str() );
        }
       
        if( iHeight >= 720 )
        {
            strCommand = format( "avconv -re -i %s -ar 44100 -acodec copy -s 1280x720 -f flv rtmp://188.165.232.15/live/%s_%s_720p", STR( _streamName ), STR( strStream ), STR( strPassword ) );
            system( strCommand.c_str() );
        }
       
        if( iHeight > 480 )
        {
            strCommand = format( "avconv -re -i %s -ar 44100 -acodec copy -s 854x480 -f flv rtmp://188.165.232.15/live/%s_%s_480p", STR( _streamName ), STR( strStream ), STR( strPassword ) );
            system( strCommand.c_str() );
        }
       
        if( iHeight > 360 )
        {
            strCommand = format( "avconv -re -i %s -ar 44100 -acodec copy -s 640x360 -f flv rtmp://188.165.232.15/live/%s_%s_360p", STR( _streamName ), STR( strStream ), STR( strPassword ) );
            system( strCommand.c_str() );
        }
       
        _restreamBase = true;
    }
   
    pclose( pipein_fp );
}

Wszystko działało by bez problemu, ale musi być jakieś ale - gdy używam funkcji system, to wszystko co serwer rtmp ma zrobić (czyli wyświetlać dalsze dane itd.) jest zawieszane na rzecz avconv (aka ffmpeg) tj. zwracany jest odpowiedź funkcji na tą komendę, chciałbym uruchomić to zadanie w tle, bez zwracania żadnej wartości (ani do konsoli, ani do zmiennej).
P-55018
DejaVu
» 2012-04-22 19:32:11
No to musisz poczytać o wątkach :)

PS. jeden temat = jeden problem.
P-55020
« 1 » 2
  Strona 1 z 2 Następna strona