vlc-添加自定义的demuxer解复用插件----播放h264裸文件

12 篇文章 1 订阅

使用vlc3.0.6  在ubuntu 64bit上编译,vlc使用插件的方式组织对多种视频源的支持,比如 avi、mp4、mkv、等等,这里想添加一个自己的demuxer,从一个h.264文件中读h264数据,播放。(vlc并不支持直接播放h264裸流文件,至少这个v3.0.6版本是没有支持)

一:添加一个demuxer模块:

在moudle/demux 的makefile.am 中添加: 

libwangscr_plugin_la_SOURCES = demux/wang_test/h264src.c 
libwangscr_plugin_la_LIBADD = $(LIBM)
libwangscr_plugin_la_LDFLAGS = $(AM_LDFLAGS)

demux_LTLIBRARIES += libwangscr_plugin.la

然后到源码顶层目录,重新 # ./configure #make

demux/wang_test/h264src.是 自己要添加的插件实现文件

libwangscr 是自己的插件名

最后会得到在 /modules/.libs 目录下得到 libwangscr_plugin.la 

贴上插件代码h264src.c的实现:

            前半部分有从h264文件获取流数据的实现

#define PLAY_FPS 30  //播放的帧率,可自由设置,(不嫌麻烦可以从h264流的sps中解码出来,让视频正常速度播放)

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include<stdio.h>
#include <vlc_demux.h>
#include <vlc_charset.h>                           /* EnsureUTF8 */
#include <vlc_input.h>
#include <vlc_aout.h>
#include <vlc_plugin.h>


#include <sys/time.h>


#define BUFFER_TIME_MS 500 //缓冲500ms的数据
#define PLAY_FPS 30  //播放的帧率,可自由设置,(不嫌麻烦可以从h264流的sps中解码出来,让视频正常速度播放)
/***

***20190828 canok

*** output: complete frames

**/

#include<stdio.h>
#include<stdlib.h>	
#include <unistd.h>
#include <string.h>
#define MIN(a,b) ((a)<(b)?(a):(b))

typedef unsigned char   uint8_t;     //无符号8位数

#define NALU_TYPE_SLICE 1
#define NALU_TYPE_DPA 2
#define NALU_TYPE_DPB 3
#define NALU_TYPE_DPC 4
#define NALU_TYPE_IDR 5
#define NALU_TYPE_SEI 6
#define NALU_TYPE_SPS 7
#define NALU_TYPE_PPS 8
#define NALU_TYPE_AUD 9
#define NALU_TYPE_EOSEQ 10
#define NALU_TYPE_EOSTREAM 11
#define NALU_TYPE_FILL 12

#define CACH_LEN (1024*200)//缓冲区不能设置太小,如果出现比某一帧比缓冲区大,会被覆盖掉一部分
static uint8_t *g_cach[2] = {NULL,NULL};
static FILE* fp_inH264 = NULL;
static int icach = 0;
static int ioffset = 0;
static int bLoop = 1;
static int init()
{	
	if(g_cach[0] == NULL)
	{
		g_cach[0] = (uint8_t*)malloc(CACH_LEN);
	}
	if(g_cach[1] == NULL)
	{
		g_cach[1] = (uint8_t*)malloc(CACH_LEN);
	}
	
	if(fp_inH264 == NULL)
	{
		fp_inH264 = fopen("./test_move.264","r");
		if(fp_inH264 == NULL)
		{
			printf("fope erro [%d%s]\n",__LINE__,__FUNCTION__);
			return -1;
		}
	}
	
	if(fread(g_cach[icach], 1,CACH_LEN,fp_inH264 )<CACH_LEN)
	{
		printf("intpufile too short [%d%s]\n",__LINE__,__FUNCTION__);
		return -1;
	}
	return 0;
}
static int deinit()
{
	if(g_cach[0])
	{
		free(g_cach[0]);
		g_cach[0] = NULL;
	}
	if(g_cach[1])
	{
		free(g_cach[1]);
		g_cach[1] = NULL;
	}
	
	if(fp_inH264)
	{
		fclose(fp_inH264);
		fp_inH264 = NULL;
	}
	
	
}
static int checkNal(uint8_t nalHeader)
{
	char type = nalHeader & ((1<<5)-1);
	switch(type)
	{
		case NALU_TYPE_SPS:
			printf("sps\n");
			break;
		case NALU_TYPE_PPS:
			printf("pps\n");
			break;
		case NALU_TYPE_IDR:
			printf("I slice !!!!!!!!!!!!!!\n");
			break;
		case NALU_TYPE_SLICE:
			printf("B/P slice\n");
			break;
		case NALU_TYPE_AUD:
			printf("Delimiter==========\n");
			break;
		default:
			printf("type :%d\n",type);
			break;
	}
	return type;
}
static int checkFlag(uint8_t *buffer, int offset) 
{
	static uint8_t mMark[4] = {0x00,0x00,0x00,0x01};
	return !memcmp(buffer+offset,mMark,4);
	//return (!memcmp(buffer+offset,mMark,4) && ((buffer[offset+4]&((1<<5)-1)) == 9) );
}
//获取一个Nal到 buf, bufLen表示缓冲区最大可以容纳的数据
//返回实际的帧数据长度
static int getOneFrame(uint8_t *buf, int bufLen)
{
	int i =0;
	int startpoint = ioffset;
	int endpoint = ioffset;
	for (i = ioffset+4; i <= CACH_LEN - 4; i++) {
		if (checkFlag(g_cach[icach], i)){
			startpoint = ioffset;
			endpoint = i;
			break;
		}
	}
	if(endpoint - startpoint > 0)
	{
		int dataLen = endpoint -startpoint;
		if(bufLen < dataLen)
		{
			printf("recive buffer too short , need %d byte!\n",dataLen);
		}
		memcpy(buf,g_cach[icach]+startpoint, MIN(dataLen,bufLen));
		ioffset = endpoint;
		
		return MIN(dataLen,bufLen);
	}
	else
	{
		int oldLen =CACH_LEN -startpoint;
		memcpy(g_cach[(icach+1)%2],g_cach[icach]+startpoint, oldLen );
		
		int newLen = 0;
		newLen = fread(g_cach[(icach+1)%2]+oldLen, 1,CACH_LEN -(oldLen),fp_inH264);
		if(newLen <CACH_LEN -(oldLen) )
		{
			if(bLoop)
			{
				fseek(fp_inH264,0,SEEK_SET);
				ioffset =0;
				icach =0;
				fread(g_cach[icach], 1,CACH_LEN,fp_inH264 );
				return getOneFrame(buf,bufLen); 
			}
			else
			{
				return -1;
			}
			
		}
		
		ioffset = 0;
		icach = (icach+1)%2;
		
		return getOneFrame(buf,bufLen);
	}
	
}



/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
static int  Open ( vlc_object_t * );
static void Close( vlc_object_t * );


vlc_module_begin ()
    set_category( CAT_INPUT )
    set_subcategory( SUBCAT_INPUT_DEMUX )
    set_description( N_("android src stream demuxer") )
    set_shortname( N_("screen") )
	set_capability( "access_demux", 0 )
    set_callbacks( Open, Close )

vlc_module_end ()

//
static int temp =0;

typedef struct
{
    demux_t         *p_demux;
   
    es_format_t     fmt;
    es_out_id_t     *p_es;
}stream_track_t;

struct demux_sys_t
{
	//记录多路流
	int              i_track;
    stream_track_t     **track;

	int64_t startTimes;
	int count;
	char *buffer;
};
#define DEBUG_INFO 0

static int Demux( demux_t *p_demux )
{
#if 1
	  demux_sys_t    *p_sys = p_demux->p_sys;
	  //在这里获取一帧数据
	  usleep(1000000/PLAY_FPS);
	
	  int len  =0;
	  len  = getOneFrame(p_sys->buffer,CACH_LEN);
	  block_t		 *p_block;
	  
	  if( (p_block = block_Alloc( len )) )
	  {// 填充数据到 block

		struct timeval pts;
		gettimeofday(&pts, NULL);

		int64_t i_pts = (int64_t)pts.tv_sec * INT64_C(1000000) +
			(int64_t)pts.tv_usec;
		
		i_pts &= INT64_C(0x00ffffffffffffff);
		p_block->i_dts =  (i_pts - p_sys->startTimes)+VLC_TS_0;
		p_block->i_pts = p_block->i_dts;

#if DEBUG_INFO
		struct timeval test_time;
		struct tm *st_tm = NULL;
		gettimeofday(&test_time,NULL);
		st_tm = gmtime(&test_time.tv_sec);
		
		printf(" len %08d [%02d:%02d:%02d:%06d] ipts:%ld idts:%ld \n",len,st_tm->tm_hour,st_tm->tm_min,st_tm->tm_sec,test_time.tv_usec,p_block->i_pts,p_block->i_dts);
#endif
		if((p_sys->count++)== (int)(BUFFER_TIME_MS* PLAY_FPS/1000 + 1) )
		{//需要在缓冲区缓冲完后,这里更新一下参考时钟,
		//否则可能出现只解一帧的情况
		//只能手动大致计算下了
			es_out_SetPCR( p_demux->out, p_block->i_dts  );
		}
		memcpy( p_block->p_buffer, p_sys->buffer, len);

	  }
	  //这里只有一个 stream, 发送到track[0] 就行
	  es_out_Send( p_demux->out,p_sys->track[0]->p_es, p_block );
 #endif
	  return VLC_DEMUXER_SUCCESS;

}
static int Control( demux_t *p_demux, int i_query, va_list args )
{
#if DEBUG_INFO
	printf("screen control i_query %#lx [%d%s%s]\n",i_query,__LINE__,__FUNCTION__,__FILE__);
#endif 
	bool *pb;
    int64_t *pi64, i64;
    double  *pf, f;
	
	demux_sys_t *p_sys = p_demux->p_sys;
	switch(i_query)
	{
		case DEMUX_CAN_PAUSE:
        case DEMUX_CAN_SEEK:
			{
				pb = va_arg( args, bool * );
				*pb = false;
				return VLC_SUCCESS;
			}
			break;
		case DEMUX_CAN_CONTROL_PACE:
			{
				pb = va_arg( args, bool * );
				*pb = false;
				 return VLC_SUCCESS;
			}
			break;
		case DEMUX_CAN_CONTROL_RATE:
			{
				pb = va_arg( args, bool * );
				*pb = false  ;
				return VLC_SUCCESS;
			}
			break;
		#if 0
		 case DEMUX_GET_FPS:
            pf = va_arg( args, double * );
            *pf = 30;
            return VLC_SUCCESS;
		#endif
		case DEMUX_GET_PTS_DELAY:
			pi64 = va_arg( args, int64_t * );
			#if 0
			*pi64 = INT64_C(1000)
				  * var_InheritInteger( p_demux, "network-caching" );
			#endif
			*pi64 = INT64_C(1000)*500; 
			return VLC_SUCCESS;
		default:
			return VLC_EGENERIC;
		
	}

}
static int initVideoES(demux_t *p_demux)
{//es_out 添加一个h264 es流
	demux_sys_t *p_sys = p_demux->p_sys;
	
	stream_track_t *tk = (stream_track_t*)malloc( sizeof( stream_track_t ) );
	if(tk ==NULL)
	{
		return -1;
	}
	tk->p_demux     = p_demux;
	es_format_Init( &tk->fmt, VIDEO_ES, VLC_CODEC_H264 );
	 //tk->fmt.video.i_width  = 1920;
     //tk->fmt.video.i_height = 1080;
	tk->p_es = es_out_Add( p_demux->out, &tk->fmt );
	
   if( tk->p_es )
   {
	   TAB_APPEND_CAST( (stream_track_t **), p_sys->i_track, p_sys->track, tk );
   }
   else
   {
	   es_format_Clean( &tk->fmt );
	   free( tk );
	   printf("erro to init VideoEs [%d%s]\n",__LINE__,__FUNCTION__);
	   return -1;
   }

   return init();

}
static int Open( vlc_object_t * p_this )
{
	//printf("wang screen module open ! [%d%s%s]\n",__LINE__,__FUNCTION__,__FILE__);
	demux_t     *p_demux = (demux_t*)p_this;
    demux_sys_t *p_sys = NULL;
	
	p_demux->pf_demux  = Demux;
    p_demux->pf_control= Control;
    p_demux->p_sys     = p_sys = (demux_sys_t*)calloc( 1, sizeof( demux_sys_t ) );
	p_sys->buffer = calloc(1,CACH_LEN);
    if( !p_sys ) return VLC_ENOMEM;
	
	struct timeval pts;
	gettimeofday(&pts, NULL);
	int64_t i_pts = (int64_t)pts.tv_sec * INT64_C(1000000) +
		(int64_t)pts.tv_usec;
	i_pts &= INT64_C(0x00ffffffffffffff);

	p_sys->startTimes = i_pts;
	p_sys->count = 1;
	
	if(initVideoES(p_demux))
	{
		printf("wang screen vedioees init erro![%d%s]\n\n\n",__LINE__,__FUNCTION__);
	}
	//printf("wang screen module open ok! [%d%s%s]\n",__LINE__,__FUNCTION__,__FILE__);


	//important !!!!
	es_out_SetPCR( p_demux->out, VLC_TS_0  );
	return 0;
}

static void Close ( vlc_object_t * p_this )
{
	demux_t *p_demux = (demux_t*)p_this;
    demux_sys_t *p_sys = p_demux->p_sys;
	

	for( int i = 0; i < p_sys->i_track; i++ )
    {
        stream_track_t *tk = p_sys->track[i];
        es_format_Clean( &tk->fmt );
        free( tk );
    }
    TAB_CLEAN( p_sys->i_track, p_sys->track );
	
	free(p_sys->buffer);
	free( p_sys );

	deinit();
}

二: 使用vlc库播放:

在 bin/vlc.c main 函数,屏蔽掉源代码,用自己的demo:

int main()
{
    /* The so-called POSIX-compliant MacOS X reportedly processes SIGPIPE even
		 * if it is blocked in all thread.
		 * Note: this is NOT an excuse for not protecting against SIGPIPE. If
		 * LibVLC runs outside of VLC, we cannot rely on this code snippet. */
		signal (SIGPIPE, SIG_IGN);
		/* Restore SIGCHLD in case our parent process ignores it. */
		signal (SIGCHLD, SIG_DFL);
	
	printf("wang [%s:%d:%s] \n",__FILE__,__LINE__,__FUNCTION__);
#ifndef NDEBUG
		/* Activate malloc checking routines to detect heap corruptions. */
		setenv ("MALLOC_CHECK_", "2", 1);
	
		/* Disable the ugly Gnome crash dialog so that we properly segfault */
		setenv ("GNOME_DISABLE_CRASH_DIALOG", "1", 1);
#endif
	
#ifdef TOP_BUILDDIR
		setenv ("VLC_PLUGIN_PATH", TOP_BUILDDIR"/modules", 1);
		setenv ("VLC_DATA_PATH", TOP_SRCDIR"/share", 1);
#endif
	
	/* Clear the X.Org startup notification ID. Otherwise the UI might try to
	 * change the environment while the process is multi-threaded. That could
	 * crash. Screw you X.Org. Next time write a thread-safe specification. */
	unsetenv ("DESKTOP_STARTUP_ID");
//=======================================================//

		libvlc_instance_t *vlc; 
		libvlc_media_player_t *media_player_cli;
		libvlc_media_t *media_cli;
	
		vlc =  libvlc_new(0,NULL);
		if(vlc == NULL)
		{
		   printf("wang erro libvlc_new \n");
		   return -1;
		}
	
		media_player_cli = libvlc_media_player_new(vlc);
		media_cli =  libvlc_media_new_location(vlc,"screen://123");
		
		 libvlc_media_player_set_media(media_player_cli,media_cli);
		 libvlc_media_player_play(media_player_cli);

        while(1); //主线程不能退出,不然子线程也挂掉,播放不了了
}

在libvlc_media_new_location(vlc,"screen://123");这一行代码,"screen" 要和  set_shortname( N_("screen") ) 中的描述,至于123这些没什么用,随便加的。 vlc启动的时候会去插件目录里面扫描所有目录,存到一个树中,上述匹配之后vlc创建demuxer的时候会缓存树里面查找对应库,并加载它作为demuxer使用。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值