windows和Linux上利用vlc外掛獲取rtsp視訊流解碼資料

狂人發表於2013-03-31
Linux上實現:
     執行下面程式碼前確保Linux上已經安裝好vlc以及相應外掛,我編譯並安裝了vlc-2.0.1版本,但最終仍然不能開啟rtsp視訊流,不清楚原因,但是本地視訊還是可以獲取到原始視訊流的。
     程式碼裡面用到了SDL來顯示視訊,我下載並編譯安裝了SDL-1.2.15的原始碼。
/* libSDL and libVLC sample code
* Copyright ? 2008 Sam Hocevar <sam@zoy.org>
* license: [http://en.wikipedia.org/wiki/WTFPL WTFPL] */

#include <stdio.h>
#include <stdint.h>
#include <math.h>
#include <stdlib.h>
#include <assert.h>

#include <SDL/SDL.h>
#include <SDL/SDL_mutex.h>

#include <vlc/vlc.h>

#define WIDTH 640
#define HEIGHT 480

#define VIDEOWIDTH 320
#define VIDEOHEIGHT 240

struct ctx
{
    SDL_Surface *surf;
    SDL_mutex *mutex;
};

static void *lock(void *data, void **p_pixels)
{
#if 1
    struct ctx *ctx = data;

    SDL_LockMutex(ctx->mutex);
    SDL_LockSurface(ctx->surf);
    *p_pixels = ctx->surf->pixels;
#endif
    return NULL; /* picture identifier, not needed here */
}

static void unlock(void *data, void *id, void *const *p_pixels)
{
#if 1
    struct ctx *ctx = data;

    /* VLC just rendered the video, but we can also render stuff */
    uint16_t *pixels = *p_pixels;
    int x, y;

     //memset(pixels, 0x00, 320*240);
    for(y = 10; y < 40; y++)
        for(x = 10; x < 40; x++)
            if(x < 13 || y < 13 || x > 36 || y > 36)
                pixels[y * VIDEOWIDTH + x] = 0xffff;
            else
                pixels[y * VIDEOWIDTH + x] = 0x0;

#if 0
     FILE *fp = NULL;
     fp = fopen("data.yuv", "ab+");
     if(fp != NULL)
     {
          fwrite(pixels, 320*240*3, 1, fp);
          fclose(fp);
          printf("write a frame.\r\n");
     }
#endif
    SDL_UnlockSurface(ctx->surf);
    SDL_UnlockMutex(ctx->mutex);

    assert(id == NULL); /* picture identifier, not needed here */
#endif
}

static void display(void *data, void *id)
{
    /* VLC wants to display the video */
    (void) data;
     printf("display a frame.\r\n");
    assert(id == NULL);
}

int main(int argc, char *argv[])
{
    libvlc_instance_t *libvlc;
    libvlc_media_t *m;
    libvlc_media_player_t *mp;
    char const *vlc_argv[] =
    {
        "--no-audio", /* skip any audio track */
        "--no-xlib", /* tell VLC to not use Xlib */
    };
    int vlc_argc = sizeof(vlc_argv) / sizeof(*vlc_argv);

    SDL_Surface *screen, *empty;
    SDL_Event event;
    SDL_Rect rect;
    int done = 0, action = 0, pause = 0, n = 0;

    struct ctx ctx;

    if(argc < 2)
    {
        printf("Usage: %s <filename>\n", argv[0]);
        return EXIT_FAILURE;
    }

    /*
     *  Initialise libSDL
     */
    if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_EVENTTHREAD) == -1)
    {
        printf("cannot initialize SDL\n");
        return EXIT_FAILURE;
    }

     empty = SDL_CreateRGBSurface(SDL_SWSURFACE, VIDEOWIDTH, VIDEOHEIGHT,
                                   16, 0, 0, 0, 0);

#if 1
    ctx.surf = SDL_CreateRGBSurface(SDL_SWSURFACE, VIDEOWIDTH, VIDEOHEIGHT,
                                    16, 0x001f, 0x07e0, 0xf800, 0);
#else
     ctx.surf = SDL_CreateRGBSurface(SDL_SWSURFACE, VIDEOWIDTH, VIDEOHEIGHT,
                                   16, 0xff000000, 0x00ff0000, 0x0000ff00, 0);
#endif


    ctx.mutex = SDL_CreateMutex();

    int options = SDL_ANYFORMAT | SDL_HWSURFACE | SDL_DOUBLEBUF;

    screen = SDL_SetVideoMode(WIDTH, HEIGHT, 0, options);
    if(!screen)
    {
        printf("cannot set video mode\n");
        return EXIT_FAILURE;
    }

    /*
     *  Initialise libVLC
     */
    libvlc = libvlc_new(vlc_argc, vlc_argv);
    m = libvlc_media_new_path(libvlc, argv[1]);
    mp = libvlc_media_player_new_from_media(m);
    libvlc_media_release(m);

    libvlc_video_set_callbacks(mp, lock, unlock, display, &ctx);
    libvlc_video_set_format(mp, "RV16", VIDEOWIDTH, VIDEOHEIGHT, VIDEOWIDTH*2);
    libvlc_media_player_play(mp);

    /*
     *  Main loop
     */
    rect.w = 0;
    rect.h = 0;

    while(!done)
    {
        action = 0;

        /* Keys: enter (fullscreen), space (pause), escape (quit) */
        while( SDL_PollEvent( &event ) )
        {
            switch(event.type)
            {
            case SDL_QUIT:
                done = 1;
                break;
            case SDL_KEYDOWN:
                action = event.key.keysym.sym;
                break;
            }
        }

        switch(action)
        {
        case SDLK_ESCAPE:
            done = 1;
            break;
        case SDLK_RETURN:
            options ^= SDL_FULLSCREEN;
            screen = SDL_SetVideoMode(WIDTH, HEIGHT, 0, options);
            break;
        case ' ':
            pause = !pause;
            break;
        }

        rect.x = (int)((1. + .5 * sin(0.03 * n)) * (WIDTH - VIDEOWIDTH) / 2);
        rect.y = (int)((1. + .5 * cos(0.03 * n)) * (HEIGHT - VIDEOHEIGHT) / 2);

        if(!pause)
            n++;

        /* Blitting the surface does not prevent it from being locked and
         * written to by another thread, so we use this additional mutex. */
        SDL_LockMutex(ctx.mutex);
        SDL_BlitSurface(ctx.surf, NULL, screen, &rect);
        SDL_UnlockMutex(ctx.mutex);

        SDL_Flip(screen);
        SDL_Delay(10);

        SDL_BlitSurface(empty, NULL, screen, &rect);
    }

    /*
     * Stop stream and clean up libVLC
     */
    libvlc_media_player_stop(mp);
    libvlc_media_player_release(mp);
    libvlc_release(libvlc);

    /*
     * Close window and clean up libSDL
     */
    SDL_DestroyMutex(ctx.mutex);
    SDL_FreeSurface(ctx.surf);
    SDL_FreeSurface(empty);

    SDL_Quit();

    return 0;
}

Windows上的實現:
     上面提到的VLC的外掛和SDL,在Windows上同樣需要,我用vlc2.0.1在Linux上編譯了windows版本的vlc,這樣相應的庫和外掛就都有了,SDL可以到官網下載。
     由於vlc的哪個標頭檔案中包含了stdint.h這個標頭檔案,這個標頭檔案說是C99標準標頭檔案,vc裡面沒有,vs2010裡面有,所以用vs2010建立的工程,在工程中新增好vlc和SDL的include、lib。並且將vlc和SDL相應用到的DLL檔案複製到工程的Debug或Release目錄,還要將vlc的plugins目錄複製到Debug或Release目錄,要不然找不到vlc外掛。

#include "stdafx.h"

#include <stdio.h>
#include <tchar.h>
#include <time.h>
#include <windows.h>
#include <stdlib.h>
#include <assert.h>

#include <SDL.h>

#include <vlc/vlc.h>
#pragma comment (lib, "libvlc.lib")
#pragma comment (lib, "libvlccore.lib")
#pragma comment (lib, "SDL.lib")


using namespace std;

#if 0     //這部分程式碼是讀取本地檔案然後顯示,並沒有用到SDL
int _tmain(int argc, _TCHAR* argv[])
{
                libvlc_instance_t *     vlc_ins    = NULL;
                libvlc_media_player_t * vlc_player = NULL;
                libvlc_media_t *        vlc_media  = NULL;

                 const char * vlc_args[] =
                {
                 "-I",
                 "dummy",
                 "--ignore-config",
                 "--extraintf=logger",
                 "--verbose=2",
                }; 

               
                vlc_ins = libvlc_new( sizeof(vlc_args)/sizeof (vlc_args[0]), vlc_args);
                 if(vlc_ins != NULL)
                {
 
                                vlc_player = libvlc_media_player_new(vlc_ins);
                                 if(vlc_player != NULL)
                                {
                                                vlc_media = libvlc_media_new_path(vlc_ins, "rtsp://192.0.0.64/av0_0" );
                                                 if(vlc_media != NULL)
                                                {
                                                               libvlc_media_parse(vlc_media); 
                                                                libvlc_time_t duration = libvlc_media_get_duration(vlc_media);

                                                                libvlc_media_track_info_t *media_tracks = NULL;
                                                                 int trackCount = libvlc_media_get_tracks_info(vlc_media, &media_tracks);
                                                                 // free(media_tracks);  // crash?

                                                                libvlc_media_player_set_media(vlc_player, vlc_media);
                                                                libvlc_media_player_set_hwnd(vlc_player, ::GetDesktopWindow());

                                                                libvlc_media_player_play(vlc_player);

                                                                time_t last_time = time(NULL);
                                                                 while((time(NULL) < (last_time + 20)))
                                                                {
                                                                                Sleep(10);
                                                                                libvlc_time_t play_time = libvlc_media_player_get_time(vlc_player);
                                                                                printf( "playing time : %lld ms\r" , (__int64)(play_time));

                                                                                // libvlc_state_t media_state = libvlc_media_get_state(vlc_media);
                                                                                // printf("\nmedia state : %d\n", (int)(media_state));
                                                                }
                                                                 // 停止1
                                                                libvlc_media_player_stop(vlc_player);
                                                                 // 釋放?
                                                                libvlc_media_release(vlc_media);
                                }
                                 // 釋放?
                                libvlc_media_player_release(vlc_player);
                }
                 // 釋放?
                libvlc_release(vlc_ins);
                }
                 return 0;
}
#endif



/* libSDL and libVLC sample code
 * Copyright ? 2008 Sam Hocevar <sam@zoy.org>
 * license: [http://en.wikipedia.org/wiki/WTFPL WTFPL] */
//下面這部分程式碼獲取rtsp視訊流並用SDL進行顯示
#include <stdio.h>
#include <stdint.h>
#include <math.h>
#include <stdlib.h>
#include <assert.h>

#include <SDL.h>
#include <SDL_mutex.h>

#include <vlc/vlc.h>

#define WIDTH 1280
#define HEIGHT 720

#define VIDEOWIDTH 640                
#define VIDEOHEIGHT 480

struct ctx
{
    SDL_Surface *surf;
    SDL_mutex *mutex;
};

static void *lock(void *data, void **p_pixels)
{
#if 1
    struct ctx *ctx = (struct ctx*)data;

    SDL_LockMutex(ctx->mutex);
    SDL_LockSurface(ctx->surf);
    *p_pixels = ctx->surf->pixels;
#endif
    return NULL; /* picture identifier, not needed here */
}

static void unlock(void *data, void *id, void *const *p_pixels)
{
#if 1
    struct ctx *ctx = (struct ctx*)data;

    /* VLC just rendered the video, but we can also render stuff */
    uint16_t *pixels = (uint16_t*)*p_pixels;
    int x, y;

                 //memset(pixels, 0x00, 640*480);
    for(y = 10; y < 40; y++)
        for(x = 10; x < 40; x++)
            if(x < 13 || y < 13 || x > 36 || y > 36)
                pixels[y * VIDEOWIDTH + x] = 0xffff;
            else
                pixels[y * VIDEOWIDTH + x] = 0x0;

#if 0
                FILE *fp = NULL;
                fp = fopen( "data.rgb", "ab+" );
                 if(fp != NULL)
                {
                                fwrite(pixels, 640*480*2, 1, fp);
                                fclose(fp);
                                printf( "write a frame.\r\n");
                }
#endif
    SDL_UnlockSurface(ctx->surf);
    SDL_UnlockMutex(ctx->mutex);

    assert(id == NULL); /* picture identifier, not needed here */
#endif
}

static void display(void *data, void *id)
{
    /* VLC wants to display the video */
    ( void) data;
                printf( "display a frame.\r\n");
    assert(id == NULL);
}

int _tmain(int argc, _TCHAR* argv[])
{
    libvlc_instance_t *libvlc;
    libvlc_media_t *m;
    libvlc_media_player_t *mp;
    char const *vlc_argv[] =
    {
        "--no-audio", /* skip any audio track */
        "--no-xlib", /* tell VLC to not use Xlib */
    };
    int vlc_argc = sizeof (vlc_argv) / sizeof(*vlc_argv);

    SDL_Surface *screen, *empty;
    SDL_Event event;
    SDL_Rect rect;
    int done = 0, action = 0, pause = 0, n = 0;

    struct ctx ctx;

    //if(argc < 2)
    {
        printf( "Usage: %s <filename>\n" , argv[0]);
       // return EXIT_FAILURE;
    }

    /*
     *  Initialise libSDL
     */
    //if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_EVENTTHREAD) == -1)
                 if(SDL_Init(0) == -1)
    {
        printf( "cannot initialize SDL\n" );
        return EXIT_FAILURE;
    }

                empty = SDL_CreateRGBSurface(SDL_SWSURFACE, VIDEOWIDTH, VIDEOHEIGHT,
                                   16, 0, 0, 0, 0);

#if 1
    ctx.surf = SDL_CreateRGBSurface(SDL_SWSURFACE, VIDEOWIDTH, VIDEOHEIGHT,
                                    16, 0x001f, 0x07e0, 0xf800, 0);
#else
                ctx.surf = SDL_CreateRGBSurface(SDL_SWSURFACE, VIDEOWIDTH, VIDEOHEIGHT,
                                   16, 0xff000000, 0x00ff0000, 0x0000ff00, 0);
#endif


    ctx.mutex = SDL_CreateMutex();

    int options = SDL_ANYFORMAT | SDL_HWSURFACE | SDL_DOUBLEBUF;

    screen = SDL_SetVideoMode(WIDTH, HEIGHT, 0, options);
    if(!screen)
    {
        printf( "cannot set video mode\n" );
        return EXIT_FAILURE;
    }

    /*
     *  Initialise libVLC
     */
    libvlc = libvlc_new(vlc_argc, vlc_argv);
    //m = libvlc_media_new_path(libvlc, (const char*)argv[1]);
                 //m = libvlc_media_new_path(libvlc, "F:\\123.mp4");
                m = libvlc_media_new_path(libvlc, "rtsp://192.0.0.64/av0_0" );
    mp = libvlc_media_player_new_from_media(m);
    libvlc_media_release(m);

    libvlc_video_set_callbacks(mp, lock, unlock, display, &ctx);
    libvlc_video_set_format(mp, "RV16", VIDEOWIDTH, VIDEOHEIGHT, VIDEOWIDTH*2);
    libvlc_media_player_play(mp);

    /*
     *  Main loop
     */
    rect.w = 0;
    rect.h = 0;

    while(!done)
    {
        action = 0;

        /* Keys: enter (fullscreen), space (pause), escape (quit) */
        while( SDL_PollEvent( &event ) )
        {
            switch(event .type)
            {
            case SDL_QUIT:
                done = 1;
                break;
            case SDL_KEYDOWN:
                action = event.key.keysym.sym;
                break;
            }
        }

        switch(action)
        {
        case SDLK_ESCAPE:
            done = 1;
            break;
        case SDLK_RETURN:
            options ^= SDL_FULLSCREEN;
            screen = SDL_SetVideoMode(WIDTH, HEIGHT, 0, options);
            break;
        case ' ' :
            pause = !pause;
            break;
        }

        rect.x = ( int)((1. + .5 * sin(0.03 * n)) * (WIDTH - VIDEOWIDTH) / 2);
        rect.y = ( int)((1. + .5 * cos(0.03 * n)) * (HEIGHT - VIDEOHEIGHT) / 2);

        if(!pause)
            n++;

        /* Blitting the surface does not prevent it from being locked and
         * written to by another thread, so we use this additional mutex. */
        SDL_LockMutex(ctx.mutex);
        SDL_BlitSurface(ctx.surf, NULL, screen, &rect);
        SDL_UnlockMutex(ctx.mutex);

        SDL_Flip(screen);
        SDL_Delay(10);

        SDL_BlitSurface(empty, NULL, screen, &rect);
    }

    /*
     * Stop stream and clean up libVLC
     */
    libvlc_media_player_stop(mp);
    libvlc_media_player_release(mp);
    libvlc_release(libvlc);

    /*
     * Close window and clean up libSDL
     */
    SDL_DestroyMutex(ctx.mutex);
    SDL_FreeSurface(ctx.surf);
    SDL_FreeSurface(empty);

    SDL_Quit();

    return 0;
}




相關文章