1. 程式人生 > >樹莓派sip視訊電話-3:exosip2+硬體h264+g711編碼初步實現

樹莓派sip視訊電話-3:exosip2+硬體h264+g711編碼初步實現

    之前使用python語音實現,但是python下的exosip2庫部分功能不能實現,現改為c語音的exsip方式,初步實現sip視訊電話功能。

    測試環境:樹莓派------------elastix---------------ekiga(pc端)   視訊為h.264 音訊為G711u

    存在問題:1.原始碼中有幾處告警未處理;2.執行緒終止沒有實現,呼叫結束後,視訊執行緒仍在傳送(不能呼入兩次),音訊執行緒因對方埠關閉終止退出;3.接收視音訊還沒有實現,採用外部程式omxplayer 播放sdp檔案方式實現。4.只做了呼入視訊 音訊傳送,撥出未做。(休假快結束了,暫時就到這裡了,有時間再完善!)

1.解除安裝exosip 4.1版本
    sudo apt-get remove libosip2-11 libosip2-dev libexosip2-11    libexosip2-dev
2.編譯安裝3.6版本

    libosip2-3.6.0  ./configure&&make&&sudo make install
    libexosip2-3.6.0  ./configure&&make&&sudo make install

3.安裝編譯視訊編碼動態連結庫

    樹莓派Camkit,原始碼百度查詢,感謝作者!

4.編譯sip原始碼
    (1)sip部分:使用了exsip2實現,開啟了3個執行緒,事件處理,音訊傳送,視訊傳送3個執行緒。

    (2)音訊部分:為g711U編碼,在原始碼中可以改為g711a。

    (3)視訊部分:使用了omx硬體編碼方式,呼叫了牛人的Camkit原始碼,動態連結方式。

5.原始碼:

(1)sip.c

#include <eXosip2/eXosip.h>  
#include <stdio.h>  
#include <stdlib.h>  
#include <netinet/in.h>  
#include <sys/socket.h>  
#include <sys/types.h>  
#include <pthread.h>
#include "audio_rtp.h"
#include "video_rtp.h"

eXosip_event_t *je;  
osip_message_t *reg = NULL;  
osip_message_t *invite = NULL;  
osip_message_t *ack = NULL;  
osip_message_t *info = NULL;  
osip_message_t *message = NULL;  
osip_message_t *answer = NULL;
sdp_message_t *remote_sdp = NULL;
sdp_connection_t * con_req = NULL;
sdp_media_t * md_audio_req = NULL; 
sdp_media_t * md_video_req = NULL; 

int call_id, dialog_id,calling ;  
int i,flag;  
int flag1 = 1;  
int id;  

struct osip_thread *event_thread;
struct osip_thread *audio_thread;
struct osip_thread *video_thread;
struct audio_param_t audioparam;
struct video_param_t videoparam;



char *identity = "sip:
[email protected]
"; char *registerer = "sip:192.168.1.200"; char *source_call = "sip:[email protected]"; char *dest_call = "sip:[email protected]"; char command; char tmp[4096]; char localip[128]; void *sipEventThread() { eXosip_event_t *je; for (;;) { je = eXosip_event_wait (0, 100); eXosip_lock(); eXosip_automatic_action (); //401,407錯誤 eXosip_unlock(); if (je == NULL) continue; switch (je->type) { /* REGISTER related events 1-4*/ case EXOSIP_REGISTRATION_NEW: printf("received new registration\n"); break; case EXOSIP_REGISTRATION_SUCCESS: printf( "registrered successfully\n"); break; case EXOSIP_REGISTRATION_FAILURE: printf("EXOSIP_REGISTRATION_FAILURE---401 error!\n"); eXosip_automatic_action(); //傳送認證資訊,完成註冊 break; case EXOSIP_REGISTRATION_REFRESHED: printf("REGISTRATION_REFRESHED\n"); break; case EXOSIP_REGISTRATION_TERMINATED: printf("Registration terminated\n"); break; /* INVITE related events within calls */ case EXOSIP_CALL_INVITE: printf ("Received a INVITE msg from %s:%s, UserName is %s, password is %s\n",je->request->req_uri->host, je->request->req_uri->port, je->request->req_uri->username, je->request->req_uri->password); calling = 1 eXosip_lock (); eXosip_call_send_answer (je->tid, 180, NULL); i = eXosip_call_build_answer (je->tid, 200, &answer); if (i != 0) { printf ("This request msg is invalid!Cann't response!/n"); eXosip_call_send_answer (je->tid, 400, NULL); } else { char localip[128]; eXosip_guess_localip(AF_INET, localip, 128); snprintf (tmp, 4096, "v=0\r\n" "o=- 0 0 IN IP4 %s\r\n" "s=No Name\r\n" "c=IN IP4 %s\r\n" "t=0 0\r\n" "m=audio 54000 RTP/AVP 0\r\n" "a=rtpmap:0 PCMU/8000\r\n" "m=video 4002 RTP/AVP 96\r\n" "a=rtpmap:96 H264/90000\r\n" ,localip, localip ); //設定回覆的SDP訊息體,下一步計劃分析訊息體 //沒有分析訊息體,直接回復原來的訊息,這一塊做的不好。 osip_message_set_body (answer, tmp, strlen(tmp)); osip_message_set_content_type (answer, "application/sdp"); eXosip_call_send_answer (je->tid, 200, answer); printf ("send 200 over!\n"); } eXosip_unlock (); //顯示出在sdp訊息體中的 attribute 的內容,裡面計劃存放我們的資訊 printf ("the INFO is :\n"); //得到訊息體,認為該訊息就是SDP格式. remote_sdp = eXosip_get_remote_sdp (je->did); con_req = eXosip_get_audio_connection(remote_sdp); md_audio_req = eXosip_get_audio_media(remote_sdp); md_video_req = eXosip_get_video_media(remote_sdp); char *remote_sdp_str=NULL; sdp_message_to_str(remote_sdp,&remote_sdp_str); printf("remote_sdp_str=======================\n%s\n",remote_sdp_str); char *payload_str; int pos = 0; printf("audio info:----------------\n"); while (!osip_list_eol ( (const osip_list_t *)&md_audio_req->m_payloads, pos)) { payload_str = (char *)osip_list_get(&md_audio_req->m_payloads, pos);//獲取媒體的pt(0,8) sdp_attribute_t *at; at = (sdp_attribute_t *) osip_list_get ((const osip_list_t *)&md_audio_req->a_attributes, pos); //printf ("%s : %s\n", at->a_att_field, at->a_att_value);//這裡解釋了為什麼在SDP訊息體中屬性a裡面存放必須是兩列 printf("payload_str=%s,m_media=%s\n",payload_str,at->a_att_value); pos++; } printf("video info:----------------\n"); pos = 0; while (!osip_list_eol ( (const osip_list_t *)&md_video_req->m_payloads, pos)) { payload_str = (char *)osip_list_get(&md_video_req->m_payloads, pos);//獲取媒體的pt(0,8) sdp_attribute_t *at; at = (sdp_attribute_t *) osip_list_get ((const osip_list_t *)&md_video_req->a_attributes, pos); //printf ("%s : %s\n", at->a_att_field, at->a_att_value);//這裡解釋了為什麼在SDP訊息體中屬性a裡面存放必須是兩列 printf("payload_str=%s,m_media=%s\n",payload_str,at->a_att_value); pos++; } printf("audio video port info:--------------\n"); printf("conn_add=%s,audio_port=%s,video_port=%s\n",con_req->c_addr,md_audio_req->m_port,md_video_req->m_port); //audioparam.dest_ip = (char *)con_req->c_addr;有誤 audioparam.dest_port =atoi(md_audio_req->m_port); audioparam.dest_ip = "192.168.1.200"; audioparam.audio_hw = "default"; //videoparam.dest_ip = (char *)con_req->c_addr;有誤 videoparam.dest_ip = "192.168.1.200"; videoparam.dest_port =atoi(md_video_req->m_port); videoparam.video_hw = "/dev/video0"; sdp_message_free(remote_sdp); remote_sdp = NULL; break; case EXOSIP_CALL_REINVITE: printf("REINVITE\n"); break; case EXOSIP_CALL_PROCEEDING: printf ("proceeding!\n"); break; case EXOSIP_CALL_RINGING: printf ("ringing!\n"); // call_id = je->cid; // dialog_id = je->did; printf ("call_id is %d, dialog_id is %d \n", je->cid, je->did); break; case EXOSIP_CALL_ANSWERED: printf ("ok! connected!\n"); call_id = je->cid; dialog_id = je->did; printf ("call_id is %d, dialog_id is %d \n", je->cid, je->did); eXosip_call_build_ack (je->did, &ack); eXosip_call_send_ack (je->did, ack); break; case EXOSIP_CALL_CLOSED: printf ("the call sid closed!\n"); //osip_thread_exit(audio_thread);//終止執行緒,未工作 //osip_thread_exit(video_thread); break; case EXOSIP_CALL_ACK: printf ("ACK received!\n"); //獲取到遠端的sdp資訊後,分別建立音訊 視訊2個執行緒 //下面傳遞引數出錯 //printf("conn_add=%s,audio_port=%s,video_port=%s\n",con_req->c_addr,md_audio_req->m_port,md_video_req->m_port); //audio_thread = osip_thread_create (20000, audio_rtp , audido_hw , con_req->c_addr,md_audio_req->m_port); eXosip_lock(); printf("conn_add=%s,audio_port=%d\n",audioparam.dest_ip,audioparam.dest_port); audio_thread = osip_thread_create (20000, &audio_rtp , &audioparam); if (audio_thread==NULL){ fprintf (stderr, "audio_thread_create failed"); exit (1); } else{ fprintf (stderr, "audio_thread created!\n"); } eXosip_unlock(); //視訊執行緒 eXosip_lock(); printf("conn_add=%s,audio_port=%d\n",audioparam.dest_ip,audioparam.dest_port); video_thread = osip_thread_create (20000, &video_rtp,&videoparam); if (video_thread==NULL){ fprintf (stderr, "video_thread_create failed"); exit (1); } else{ fprintf (stderr, "video_thread created!\n"); } eXosip_unlock(); break; case EXOSIP_MESSAGE_NEW: printf("EXOSIP_MESSAGE_NEW!\n"); if (MSG_IS_OPTIONS (je->request)) { printf("options\n");//必須迴應200確認,否則無法callin,返回500錯誤 eXosip_message_build_answer (je->tid, 200,&answer); eXosip_message_send_answer (je->tid, 200,answer); } if (MSG_IS_MESSAGE (je->request))//如果接受到的訊息型別是MESSAGE { osip_body_t *body; osip_message_get_body (je->request, 0, &body); printf ("I get the msg is: %s/n", body->body); //printf ("the cid is %s, did is %s/n", je->did, je->cid); //按照規則,需要回復200 OK資訊 eXosip_message_build_answer (je->tid, 200,&answer); eXosip_message_send_answer (je->tid, 200,answer); } break; default: printf ("other response!\n"); break; } eXosip_event_free(je); } } int main (int argc, char *argv[]) { int regid=0; osip_message_t *reg = NULL; char fromuser[256]; char proxy[256]; sprintf(fromuser,"sip:%[email protected]%s","8006","192.168.1.200"); sprintf(proxy,"sip:%s","192.168.1.200"); printf("r 向伺服器註冊\n"); printf("c 取消註冊\n"); printf("i 發起呼叫請求\n"); printf("h 結束通話\n"); printf("q 退出程式\n"); printf("s 執行方法INFO\n"); printf("m 執行方法MESSAGE\n"); //初始化 if (eXosip_init() != 0) { printf ("Couldn't initialize eXosip!\n"); return -1; } if ( eXosip_listen_addr (IPPROTO_UDP, NULL, 5066, AF_INET, 0) != 0) { eXosip_quit (); fprintf (stderr, "Couldn't initialize transport layer!\n"); return -1; } event_thread = osip_thread_create (20000, sipEventThread); if (event_thread==NULL){ fprintf (stderr, "event_thread_create failed"); exit (1); } else{ fprintf (stderr, "event_thread created!\n"); } flag = 1; while (flag) { printf ("please input the comand:\n"); scanf ("%c", &command); getchar (); switch (command) { //--------------------註冊---------------------------- case 'r': printf ("start register!\n"); eXosip_add_authentication_info ("[email protected]", "8006", "***************", NULL, NULL); regid = eXosip_register_build_initial_register(fromuser, proxy,NULL, 3600, &reg); eXosip_register_send_register(regid, reg); flag1 = 1; break; //--------------------呼叫---------------------------- case 'i':/* INVITE */ i = eXosip_call_build_initial_invite (&invite, dest_call, source_call, NULL, "This si a call for a conversation"); if (i != 0) { printf ("Intial INVITE failed!\n"); break; } snprintf (tmp, 4096, "v=0\r\n" "o=- 0 0 IN IP4 192.168.1.132\r\n" "s=No Name\r\n" "c=IN IP4 192.168.1.132\r\n" "t=0 0\r\n" "m=audio 54000 RTP/AVP 8\r\n" "a=rtpmap:8 PCMA/8000\r\n" "m=video 4002 RTP/AVP 96\r\n" "a=rtpmap:96 H264/90000\r\n" ); osip_message_set_body (invite, tmp, strlen(tmp)); osip_message_set_content_type (invite, "application/sdp"); eXosip_lock (); i = eXosip_call_send_initial_invite (invite); eXosip_unlock (); break; //--------------------結束通話---------------------------- case 'h': printf ("Holded !\n"); eXosip_lock (); eXosip_call_terminate (call_id, dialog_id); eXosip_unlock (); break; //------------------登出------------------------ case 'c': printf ("This modal isn't commpleted!\n"); /* eXosip_lock (); i = eXosip_register_build_register (regid, 0, NULL); if (i < 0) { eXosip_unlock (); break; } eXosip_register_send_register (regid, reg); eXosip_unlock (); */ break; //-------------------訊息--------------------- case 's': //傳輸INFO方法 eXosip_call_build_info (dialog_id, &info); snprintf (tmp , 4096, "hello,aphero"); osip_message_set_body (info, tmp, strlen(tmp)); //格式可以任意設定,text/plain代表文字資訊 osip_message_set_content_type (info, "text/plain"); eXosip_call_send_request (dialog_id, info); break; //-----------------簡訊------------------------- case 'm': //傳輸MESSAGE方法,也就是即時訊息,和INFO方法相比,我認為主要區別,是MESSAGE不用建立連線,直接傳輸資訊,而INFO必須 //在建立INVITE的基礎上傳輸。 printf ("the mothed :MESSAGE\n"); eXosip_message_build_request (&message, "MESSAGE", dest_call, source_call, NULL); snprintf (tmp, 4096, "hellor aphero"); osip_message_set_body (message, tmp, strlen(tmp)); //假設格式是xml osip_message_set_content_type (message, "text/xml"); eXosip_message_send_request (message); break; //--------------------退出--------------------- case 'q': flag1=0; eXosip_quit (); printf ("Exit the setup!\n"); flag = 0; break; } } return (0); }

(2)video_rtp.h 

資料結構為引數傳遞部分,很多設定沒有傳遞,下步再繼續。

typedef struct video_param_t
{
char *video_hw;
char *dest_ip ;
int      dest_port;
} video_param_t;

void video_rtp(struct video_param_t *videoparam) ;

(3)video_rtp.c

原作者的程式,稍作改動。

#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <linux/videodev2.h>
#include <sys/time.h>
#include <pthread.h>
#include <assert.h>
#include <stdint.h>
#include "camkit.h"
#include "video_rtp.h"

#define MAX_RTP_SIZE 1420

int debug = 0;


void video_rtp(struct video_param_t *videoparam)
{
	
	printf("video_rtp:video_hw=%s,dest_ip=%s,dest_port=%d\n",videoparam->video_hw,videoparam->dest_ip,videoparam->dest_port);
	struct cap_handle *caphandle = NULL;
	struct cvt_handle *cvthandle = NULL;
	struct enc_handle *enchandle = NULL;
	struct pac_handle *pachandle = NULL;
	struct net_handle *nethandle = NULL;
	struct cap_param capp;
	struct cvt_param cvtp;
	struct enc_param encp;
	struct pac_param pacp;
	struct net_param netp;

	U32 vfmt = V4L2_PIX_FMT_YUYV;
	U32 ofmt = V4L2_PIX_FMT_YUV420;

	// set default values
	capp.dev_name = "/dev/video0";
	capp.width = 640;
	capp.height = 480;
	capp.pixfmt = vfmt;
	capp.rate = 15;

	cvtp.inwidth = 640;
	cvtp.inheight = 480;
	cvtp.inpixfmt = vfmt;
	cvtp.outwidth = 640;
	cvtp.outheight = 480;
	cvtp.outpixfmt = ofmt;

	encp.src_picwidth = 640;
	encp.src_picheight = 480;
	encp.enc_picwidth = 640;
	encp.enc_picheight = 480;
	encp.chroma_interleave = 0;
	encp.fps = 15;
	encp.gop = 12;
	encp.bitrate = 1000;

	pacp.max_pkt_len = 1400;
	pacp.ssrc = 1234;

	netp.serip = videoparam->dest_ip;//ip地址 
	netp.serport =videoparam->dest_port;//埠 
	netp.type = UDP;


	capp.width = cvtp.inwidth = cvtp.outwidth = encp.src_picwidth =encp.enc_picwidth = 640;
	capp.height = cvtp.inheight = cvtp.outheight =encp.src_picheight = encp.enc_picheight = 480;

	caphandle = capture_open(capp);
	if (!caphandle)
	{
		printf("--- Open capture failed\n");
		exit(1);
	}

		cvthandle = convert_open(cvtp);
		if (!cvthandle)
		{
			printf("--- Open convert failed\n");
			exit(1);
		}

		enchandle = encode_open(encp);
		if (!enchandle)
		{
			printf("--- Open encode failed\n");
			exit(1);
		}

		pachandle = pack_open(pacp);
		if (!pachandle)
		{
			printf("--- Open pack failed\n");
			exit(1);
		}

		if (netp.serip == NULL || netp.serport == -1)
		{
			printf("--- Server ip and port must be specified when using network\n");
			exit(1);
		}

		nethandle = net_open(netp);
		if (!nethandle)
		{
			printf("--- Open network failed\n");
			exit(1);
		}

	// start capture encode loop
	int ret;
	void *cap_buf, *cvt_buf, *hd_buf, *enc_buf;
	char *pac_buf = (char *) malloc(MAX_RTP_SIZE);
	int cap_len, cvt_len, hd_len, enc_len, pac_len;
	enum pic_t ptype;
	struct timeval ctime, ltime;
	unsigned long fps_counter = 0;
	int sec, usec;
	double stat_time = 0;

	capture_start(caphandle);		// !!! need to start capture stream!

	gettimeofday(<ime, NULL);
	while (1)
	{
		if (debug)		// print fps
		{
			gettimeofday(&ctime, NULL);
			sec = ctime.tv_sec - ltime.tv_sec;
			usec = ctime.tv_usec - ltime.tv_usec;
			if (usec < 0)
			{
				sec--;
				usec = usec + 1000000;
			}
			stat_time = (sec * 1000000) + usec;		// diff in microsecond

			if (stat_time >= 1000000)    // >= 1s
			{
				printf("\n*** FPS: %ld\n", fps_counter);

				fps_counter = 0;
				ltime = ctime;
			}
			fps_counter++;
		}

		ret = capture_get_data(caphandle, &cap_buf, &cap_len);
		if (ret != 0)
		{
			if (ret < 0)		// error
			{
				printf("--- capture_get_data failed\n");
				break;
			}
			else	// again
			{
				usleep(10000);
				continue;
			}
		}
		if (cap_len <= 0)
		{
			printf("!!! No capture data\n");
			continue;
		}
		if (debug)
			fputc('.', stdout);



		// convert
		if (capp.pixfmt == V4L2_PIX_FMT_YUV420)    // no need to convert
		{
			cvt_buf = cap_buf;
			cvt_len = cap_len;
		}
		else	// do convert: YUYV => YUV420
		{
			ret = convert_do(cvthandle, cap_buf, cap_len, &cvt_buf, &cvt_len);
			if (ret < 0)
			{
				printf("--- convert_do failed\n");
				break;
			}
			if (cvt_len <= 0)
			{
				printf("!!! No convert data\n");
				continue;
			}
		}
		if (debug)
			fputc('-', stdout);



		// encode
		// fetch h264 headers first!
		while ((ret = encode_get_headers(enchandle, &hd_buf, &hd_len, &ptype))
				!= 0)
		{
			if (debug)
				fputc('S', stdout);

			// pack headers
			pack_put(pachandle, hd_buf, hd_len);
			while (pack_get(pachandle, pac_buf, MAX_RTP_SIZE, &pac_len) == 1)
			{
				if (debug)
					fputc('#', stdout);

				// network
				ret = net_send(nethandle, pac_buf, pac_len);
				if (ret != pac_len)
				{
					printf("send pack failed, size: %d, err: %s\n", pac_len,
							strerror(errno));
				}
				if (debug)
					fputc('>', stdout);
			}
		}

		ret = encode_do(enchandle, cvt_buf, cvt_len, &enc_buf, &enc_len,
				&ptype);
		if (ret < 0)
		{
			printf("--- encode_do failed\n");
			break;
		}
		if (enc_len <= 0)
		{
			printf("!!! No encode data\n");
			continue;
		}

		if (debug)
		{
			char c;
			switch (ptype)
			{
				case PPS:
					c = 'S';
					break;
				case SPS:
					c = 'S';
					break;
				case I:
					c = 'I';
					break;
				case P:
					c = 'P';
					break;
				case B:
					c = 'B';
					break;
				default:
					c = 'N';
					break;
			}

			fputc(c, stdout);
		}

		// pack
		pack_put(pachandle, enc_buf, enc_len);
		while (pack_get(pachandle, pac_buf, MAX_RTP_SIZE, &pac_len) == 1)
		{
			if (debug)
				fputc('#', stdout);

			// network
			ret = net_send(nethandle, pac_buf, pac_len);
			if (ret != pac_len)
			{
				printf("send pack failed, size: %d, err: %s\n", pac_len,
						strerror(errno));
			}
			if (debug)
				fputc('>', stdout);
		}
	}
	capture_stop(caphandle);

	free(pac_buf);
	net_close(nethandle);
	pack_close(pachandle);
	encode_close(enchandle);
	convert_close(cvthandle);
	capture_close(caphandle);

	exit(0);
}

(4)audio_rtp.h
typedef struct
{
    /** byte 0 */
    unsigned char csrc_len:4;        /** expect 0 */
    unsigned char extension:1;       /** expect 1, see RTP_OP below */
    unsigned char padding:1;         /** expect 0 */
    unsigned char version:2;         /** expect 2 */
    /** byte 1 */
    unsigned char payload:7;         /** stream type */
    unsigned char marker:1;          /** when send the first framer,set it */
    /** bytes 2, 3 */
    unsigned short seq_no;
    /** bytes 4-7 */
    unsigned  long timestamp;
    /** bytes 8-11 */
    unsigned long ssrc;              /** stream number is used here. */
} RTP_FIXED_HEADER;


typedef struct audio_param_t
{
char *audio_hw;
char *dest_ip ;
int      dest_port;
} audio_param_t;

void audio_rtp(struct audio_param_t *audioparam) ;

(5)audio_rtp.c
/*

This example reads from the default PCM device
and G711 code and rtp send.

*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>

#include <netdb.h>
#include <time.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h> 

#include <alsa/asoundlib.h>
#include <math.h>
#include "g711codec.h"
#include "audio_rtp.h"

#define BUFFERSIZE 4096
#define PERIOD_SIZE 1024
#define PERIODS 2
#define SAMPLE_RATE 8000
#define CHANNELS 1
#define FSIZE 2*CHANNELS

/* Use the newer ALSA API */
#define ALSA_PCM_NEW_HW_PARAMS_API


void audio_rtp(struct audio_param_t *audioparam) {
	
	//char *audio_hw, char *dest_ip, int dest_port
	
	printf("audio_rtp:audio_hw=%s,dest_ip=%s,dest_port=%d\n",audioparam->audio_hw,audioparam->dest_ip,audioparam->dest_port);

	int rc;	//return code.
	int size;
	snd_pcm_t *handle;
	snd_pcm_hw_params_t *params;
	unsigned int val;
	int dir;
	snd_pcm_uframes_t frames;
	char *buffer;
 	int err;

	/* Open PCM device for recording (capture). */
	err = snd_pcm_open(&handle, audioparam->audio_hw , SND_PCM_STREAM_CAPTURE, 0);
	if (err < 0) {
		fprintf(stderr,"unable to open pcm device: %s\n",snd_strerror(err));
		exit(1);
	}

	/* Allocate a hardware parameters object. */
	snd_pcm_hw_params_alloca(¶ms);

	/* Fill it in with default values. */
	err=snd_pcm_hw_params_any(handle, params);
		if (err < 0) {
		    fprintf(stderr, "Can not configure this PCM device: %s\n",snd_strerror(err));
		    exit(1);
		}
	/* Set the desired hardware parameters. */

	/* Interleaved mode */
	err=snd_pcm_hw_params_set_access(handle, params,SND_PCM_ACCESS_RW_INTERLEAVED);
		if (err < 0) {
		    fprintf(stderr,   "Failed to set PCM device to interleaved: %s\n", snd_strerror(err));
		    exit(1);
		}
	/* Signed 16-bit little-endian format */
	//err=snd_pcm_hw_params_set_format(handle, params,SND_PCM_FORMAT_MU_LAW);
	err=snd_pcm_hw_params_set_format(handle, params,SND_PCM_FORMAT_S16_LE);
		if (err < 0) {
		    fprintf(stderr,"Failed to set PCM device to 16-bit signed PCM: %s\n", snd_strerror(err));
		    exit(1);
		}
	/* One channels (mono) */
	err=snd_pcm_hw_params_set_channels(handle, params, CHANNELS);
		if (err < 0) {
		    fprintf(stderr, "Failed to set PCM device to mono: %s\n",snd_strerror(err));
		    exit(1);
		}
	/* 8000 bits/second sampling rate (CD quality) */
	val = SAMPLE_RATE;//這裡修改為8000
	err=snd_pcm_hw_params_set_rate_near(handle, params,&val, &dir);
		if (err < 0) {
		    fprintf(stderr, "Failed to set PCM device to sample rate =%d: %s\n",val,snd_strerror(err));
		    exit(1);
		}

	/* Set buffer time 500000. */
	unsigned int buffer_time,period_time;
	snd_pcm_hw_params_get_buffer_time_max(params, &buffer_time, 0);
	if ( buffer_time >500000) 		
	buffer_time = 80000;//這裡可修改
	period_time = buffer_time/4;//這裡可修改  size = frames * FSIZE;
	err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, 0);
		if (err < 0) {
		    fprintf(stderr, "Failed to set PCM device to buffer time =%d: %s\n",  buffer_time,snd_strerror(err));
		    exit(1);
		}

	err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, 0);
		if (err < 0) {
		    fprintf(stderr, "Failed to set PCM device to period time =%d: %s\n",period_time,snd_strerror(err));
		    exit(1);
		}

	/* Write the parameters to the driver */
	err = snd_pcm_hw_params(handle, params);
		if (err < 0) {
			fprintf(stderr,"unable to set hw parameters: %s\n",snd_strerror(err));
			exit(1);
		}

	/* Use a buffer large enough to hold one period */
	snd_pcm_hw_params_get_period_size(params,&frames, &dir);
	size = frames * FSIZE; /* 2 bytes/sample, 1 channels *///這裡應該=320  FSIZE=2    frames=160
	buffer = (char *) malloc(size);
	
	printf("read buffer size = %d\n",size);
	printf("period size = %d frames\n", (int)frames);

	/*print alsa config parameter*/
	snd_pcm_hw_params_get_period_time(params,&val, &dir);
	printf("period time is: %d\n",val);
	snd_pcm_hw_params_get_buffer_time(params, &val, &dir);
	printf("buffer time = %d us\n", val);
	snd_pcm_hw_params_get_buffer_size(params, (snd_pcm_uframes_t *) &val);
	printf("buffer size = %d frames\n", val);
	snd_pcm_hw_params_get_periods(params, &val, &dir);
	printf("periods per buffer = %d frames\n", val);
	


    int M_bit=1;

    char sendbuf[1500];
    memset(sendbuf,0,1500);
    unsigned short seq_num = 0;
    RTP_FIXED_HEADER        *rtp_hdr;

    int    rtp_socket;
    struct sockaddr_in server;
    int len = sizeof(server);
    unsigned int timestamp_increse = 0,ts_current = 0;
    timestamp_increse = 160;

    server.sin_family = AF_INET;
    server.sin_port = htons(audioparam->dest_port);
    server.sin_addr.s_addr = inet_addr(audioparam->dest_ip);
    rtp_socket = socket(AF_INET,SOCK_DGRAM,0);
    connect(rtp_socket, (struct sockaddr *)&server, len);

	while (1) {
		//1.採集
		rc = snd_pcm_readi(handle, buffer, frames);//採集音訊資料
		if (rc == -EPIPE) {
			fprintf(stderr, "overrun occurred\n");
			err=snd_pcm_prepare(handle);
			if( err <0){
				fprintf(stderr, "Failed to recover form overrun : %s\n",
				snd_strerror(err));
				exit(1);
			}
		}
		else if (rc < 0) {
			fprintf(stderr,"error from read: %s\n",snd_strerror(rc));
			exit(1);
		} 
		else if (rc != (int)frames) {
			fprintf(stderr, "short read, read %d frames\n", rc);
		}
		
		//2.編碼
		rc = PCM2G711u( (char *)buffer, (char *)&sendbuf[12], size, 0 );//pcm轉g711a
		if(rc<0)  fprintf(stderr,"PCM2G711u error:rc=%d\n",rc);

		//3.打包
		rtp_hdr =(RTP_FIXED_HEADER*)&sendbuf[0];
		rtp_hdr->payload     = 0;  //負載型別號,
		rtp_hdr->version     = 2;  //版本號,此版本固定為2
		if(1 == M_bit)	{
			rtp_hdr->marker    = 1;   //標誌位,由具體協議規定其值。
			M_bit = 0;
		}
		else{
			rtp_hdr->marker    = 0;   //標誌位,由具體協議規定其值。
		}
		rtp_hdr->ssrc        = htonl(10);    //隨機指定為10,並且在本RTP會話中全域性唯一
		rtp_hdr->seq_no = htons(seq_num ++);//rtp包序號
		ts_current = ts_current+timestamp_increse;
		rtp_hdr->timestamp=htonl(ts_current);//rtp傳輸時間戳,增量為timestamp_increse=160
		
		//4.傳送
		rc = send( rtp_socket, sendbuf, rc+12, 0 );//開始傳送rtp包,+12是rtp的包頭+g711荷載
		if(rc<0) {
			fprintf(stderr,"net send error=%d\n",rc);
			break;
		}
        memset(sendbuf,0,1500);//清空sendbuf;此時會將上次的時間戳清空,因此需要ts_current來儲存上次的時間戳值
	}

	snd_pcm_drain(handle);
	snd_pcm_close(handle);
	free(buffer);
}
(6)g711codec.h
/*
 * G711 encode decode HEADER.
 */

#ifndef	__G711CODEC_H__
#define	__G711CODEC_H__

/*
* u-law, A-law and linear PCM conversions.
*/
#define	SIGN_BIT	(0x80)		/* Sign bit for a A-law byte. */
#define	QUANT_MASK	(0xf)		/* Quantization field mask. */
#define	NSEGS		(8)			/* Number of A-law segments. */
#define	SEG_SHIFT	(4)			/* Left shift for segment number. */
#define	SEG_MASK	(0x70)		/* Segment field mask. */
#define	BIAS		(0x84)		/* Bias for linear code. */

int PCM2G711a( char *InAudioData, char *OutAudioData, int DataLen, int reserve );
int PCM2G711u( char *InAudioData, char *OutAudioData, int DataLen, int reserve );

int G711a2PCM( char *InAudioData, char *OutAudioData, int DataLen, int reserve );
int G711u2PCM( char *InAudioData, char *OutAudioData, int DataLen, int reserve );

int g711a_decode(short amp[], const unsigned char g711a_data[], int g711a_bytes);

int g711u_decode(short amp[], const unsigned char g711u_data[], int g711u_bytes);

int g711a_encode(unsigned char g711_data[], const short amp[], int len);

int g711u_encode(unsigned char g711_data[], const short amp[], int len);

#endif  /* g711codec.h */
(7)g711codec.c
#include "g711codec.h"

static short seg_end[8] = {0xFF, 0x1FF, 0x3FF, 0x7FF,
			    0xFFF, 0x1FFF, 0x3FFF, 0x7FFF};

static int search(int val, short	*table, int	size)
{
	int	i;

	for (i = 0; i < size; i++) {
		if (val <= *table++)
			return (i);
	}
	return (size);
}

/*
* alaw2linear() - Convert an A-law value to 16-bit linear PCM
*
*/
static int alaw2linear( unsigned char a_val )
{
	int	t;
	int	seg;

	a_val ^= 0x55;

	t = (a_val & QUANT_MASK) << 4;
	seg = ( (unsigned)a_val & SEG_MASK ) >> SEG_SHIFT;
	switch (seg) 
	{
		case 0:
			t += 8;
			break;
		case 1:
			t += 0x108;
			break;
		default:
			t += 0x108;
			t <<= seg - 1;
	}
	return ((a_val & SIGN_BIT) ? t : -t);
}


/*
* ulaw2linear() - Convert a u-law value to 16-bit linear PCM
*
* First, a biased linear code is derived from the code word. An unbiased
* output can then be obtained by subtracting 33 from the biased code.
*
* Note that this function expects to be passed the complement of the
* original code word. This is in keeping with ISDN conventions.
*/
static int ulaw2linear(unsigned char u_val)
{
	int	t;

	/* Complement to obtain normal u-law value. */
	u_val = ~u_val;

	/*
	* Extract and bias the quantization bits. Then
	* shift up by the segment number and subtract out the bias.
	*/
	t = ((u_val & QUANT_MASK) << 3) + BIAS;
	t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;

	return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
}


/*
 * linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law
 *
 */
unsigned char linear2alaw(int pcm_val)	/* 2's complement (16-bit range) */
{
	int		mask;
	int		seg;
	unsigned char	aval;

	if (pcm_val >= 0) {
		mask = 0xD5;		/* sign (7th) bit = 1 */
	} else {
		mask = 0x55;		/* sign bit = 0 */
		pcm_val = -pcm_val - 8;
	}

	/* Convert the scaled magnitude to segment number. */
	seg = search(pcm_val, seg_end, 8);

	/* Combine the sign, segment, and quantization bits. */

	if (seg >= 8)		/* out of range, return maximum value. */
		return (0x7F ^ mask);
	else {
		aval = seg << SEG_SHIFT;
		if (seg < 2)
			aval |= (pcm_val >> 4) & QUANT_MASK;
		else
			aval |= (pcm_val >> (seg + 3)) & QUANT_MASK;
		return (aval ^ mask);
	}
}


/*
 * linear2ulaw() - Convert a linear PCM value to u-law
 *
 */
unsigned char linear2ulaw(int pcm_val)	/* 2's complement (16-bit range) */
{
	int		mask;
	int		seg;
	unsigned char	uval;

	/* Get the sign and the magnitude of the value. */
	if (pcm_val < 0) {
		pcm_val = BIAS - pcm_val;
		mask = 0x7F;
	} else {
		pcm_val += BIAS;
		mask = 0xFF;
	}

	/* Convert the scaled magnitude to segment number. */
	seg = search(pcm_val, seg_end, 8);

	/*
	 * Combine the sign, segment, quantization bits;
	 * and complement the code word.
	 */
	if (seg >= 8)		/* out of range, return maximum value. */
		return (0x7F ^ mask);
	else {
		uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF);
		return (uval ^ mask);
	}
}


int g711a_decode( short amp[], const unsigned char g711a_data[], int g711a_bytes )
{
	int i;
	int samples;
	unsigned char code;
	int sl;

	for ( samples = i = 0; ; )
	{
		if (i >= g711a_bytes)
			break;
		code = g711a_data[i++];

		sl = alaw2linear( code );

		amp[samples++] = (short) sl;
	}
	return samples*2;
}

int g711u_decode(short amp[], const unsigned char g711u_data[], int g711u_bytes)
{
	int i;
	int samples;
	unsigned char code;
	int sl;

	for (samples = i = 0;;)
	{
		if (i >= g711u_bytes)
			break;
		code = g711u_data[i++];

		sl = ulaw2linear(code);

		amp[samples++] = (short) sl;
	}
	return samples*2;
}

int g711a_encode(unsigned char g711_data[], const short amp[], int len)
{
    int i;

    for (i = 0;  i < len;  i++)
	{
        g711_data[i] = linear2alaw(amp[i]);
    }

    return len;
}

int g711u_encode(unsigned char g711_data[], const short amp[], int len)
{
    int i;

    for (i = 0;  i < len;  i++)
	{
        g711_data[i] = linear2ulaw(amp[i]);
    }

    return len;
}


(8)g711.c
#include <stdio.h>
#include "g711codec.h"

/*
 * function: convert PCM audio format to g711 alaw/ulaw.(zqj)
 *	 InAudioData:	PCM data prepared for encoding to g711 alaw/ulaw.
 *   OutAudioData:	encoded g711 alaw/ulaw.
 *   DataLen:		PCM data size.
 *   reserve:		reserved param, no use.
 */

/*alaw*/
int PCM2G711a( char *InAudioData, char *OutAudioData, int DataLen, int reserve )
{	
	//check params.
	if( (NULL == InAudioData) && (NULL == OutAudioData) && (0 == DataLen) )
	{
		printf("Error, empty data or transmit failed, exit !\n");	
		return -1;
	}
	//printf("DataLen = %d, %s, %d\n", DataLen, __func__, __LINE__);

	int Retaen = 0; 
	//printf("G711a encode start......\n");
	Retaen = g711a_encode( (unsigned char *)OutAudioData, (short*)InAudioData, DataLen/2 );
	//printf("Retaen = %d, %s, %d\n", Retaen, __func__, __LINE__);

	return Retaen; //index successfully encoded data len.
}

/*ulaw*/
int PCM2G711u( char *InAudioData, char *OutAudioData, int DataLen, int reserve )
{	
	//check params.
	if( (NULL == InAudioData) && (NULL == OutAudioData) && (0 == DataLen) )
	{
		printf("Error, empty data or transmit failed, exit !\n");	
		return -1;
	}
	//printf("DataLen = %d, %s, %d\n", DataLen, __func__, __LINE__);

	int Retuen = 0; 
	//printf("G711u encode start......\n");
	Retuen = g711u_encode( (unsigned char *)OutAudioData, (short*)InAudioData, DataLen/2 );
	//printf("Retuen = %d, %s, %d\n", Retuen, __func__, __LINE__);

	return Retuen; 
}

/*
 * function: convert g711 alaw audio format to PCM.(zqj)
 *	 InAudioData:	g711 alaw data prepared for encoding to PCM.
 *   OutAudioData:	encoded PCM audio data.
 *   DataLen:		g711a data size.
 *   reserve:		reserved param, no use.
 */

/*alaw*/
int G711a2PCM( char *InAudioData, char *OutAudioData, int DataLen, int reserve )
{
	//check param.
	if( (NULL == InAudioData) && (NULL == OutAudioData) && (0 == DataLen) )
	{
		printf("Error, empty data or transmit failed, exit !\n");	
		return -1;
	}
	printf("DataLen = %d, %s, %d\n", DataLen, __func__, __LINE__);

	int Retade = 0;
	printf("G711a decode start......\n");
	Retade = g711a_decode( (short*)OutAudioData, (unsigned char *)InAudioData, DataLen );
	printf("Retade = %d, %s, %d\n", Retade, __func__, __LINE__);

	return Retade;	//index successfully decoded data len.
}

/*ulaw*/
int G711u2PCM( char *InAudioData, char *OutAudioData, int DataLen, int reserve )
{
	//check param.
	if( (NULL == InAudioData) && (NULL == OutAudioData) && (0 == DataLen) )
	{
		printf("Error, empty data or transmit failed, exit !\n");	
		return -1;
	}
	printf("DataLen = %d, %s, %d\n", DataLen, __func__, __LINE__);

	int Retude = 0;
	printf("G711u decode start......\n");
	Retude = g711u_decode( (short*)OutAudioData, (unsigned char *)InAudioData, DataLen );
	printf("Retude = %d, %s, %d\n", Retude, __func__, __LINE__);

	return Retude;	
}

(9)makefile

CC=gcc
CCFLAGS=-g -Wall
LDFLAGS=-leXosip2 -losip2 -losipparser2 -lpthread -lasound -lcamkit

all:sip

sip:sip.c
	$(CC) sip.c video_rtp.c audio_rtp.c g711.c g711codec.c  $(CCFLAGS) $(LDFLAGS) -o sip

clean:
	rm sip

6.執行測試

(1)執行後,r 註冊伺服器;(2)ekiga呼入,自動應答建立連結。

[email protected]:~/project$ ./sip 
r     向伺服器註冊
c     取消註冊
i     發起呼叫請求
h     結束通話
q     退出程式
s     執行方法INFO
m     執行方法MESSAGE
event_thread created!
please input the comand:
EXOSIP_MESSAGE_NEW!
EXOSIP_MESSAGE_NEW!
r
start register!
please input the comand:
EXOSIP_REGISTRATION_FAILURE---401 error!
registrered successfully
EXOSIP_MESSAGE_NEW!
options
EXOSIP_MESSAGE_NEW!
EXOSIP_MESSAGE_NEW!
Received a INVITE msg from 192.168.1.101:5066, UserName is 8006, password is (null)
send 200 over!
the INFO is :
remote_sdp_str=======================
v=0
o=root 120365001 120365001 IN IP4 192.168.1.200
s=Asterisk PBX 11.13.0
c=IN IP4 192.168.1.200
b=CT:8096
t=0 0
m=audio 19874 RTP/AVP 0 8 3 9 101
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:3 GSM/8000
a=rtpmap:9 G722/8000
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-16
a=ptime:20
a=sendrecv
m=video 11520 RTP/AVP 99 34
a=rtpmap:99 H264/90000
a=fmtp:99 profile-level-id=42801E;max-mbps=190080;max-fs=6336;packetization-mode=1
a=rtpmap:34 H263/90000
a=fmtp:34 F=0;I=0;J=0;T=0;K=0;N=0;BPP=0;HRD=0
a=sendrecv
a=nortpproxy:yes

audio info:----------------
payload_str=0,m_media=0 PCMU/8000
payload_str=8,m_media=8 PCMA/8000
payload_str=3,m_media=3 GSM/8000
payload_str=9,m_media=9 G722/8000
payload_str=101,m_media=101 telephone-event/8000
video info:----------------
payload_str=99,m_media=99 H264/90000
payload_str=34,m_media=99 profile-level-id=42801E;max-mbps=190080;max-fs=6336;packetization-mode=1
audio video port info:--------------
conn_add=192.168.1.200,audio_port=19874,video_port=11520
ACK received!
conn_add=192.168.1.200,audio_port=19874
audio_thread created!
audio_rtp:audio_hw=hw:1,0,dest_ip=192.168.1.200,dest_port=19874
conn_add=192.168.1.200,audio_port=19874
video_thread created!
video_rtp:video_hw=/dev/video0,dest_ip=192.168.1.200,dest_port=11520
+++ Capture opened
!!! Set crop to (0, 0, 640, 480) failed. Don't panic, not all capture device support crop!
!!! VIDIOC_S_INPUT failed
+++ Device initialized
+++ Capture Opened
+++ Convert Opened
read buffer size = 320
period size = 160 frames
period time is: 20000
buffer time = 80000 us
buffer size = 640 frames
periods per buffer = 4 frames
[libx264 @ 0x73d13830] using cpu capabilities: ARMv6 NEON
[libx264 @ 0x73d13830] profile Main, level 2.2
+++ Encode Opened
+++ Pack Opened
+++ Network Opened
+++ Capture Started
!!! encoded frame delayed!
!!! No encode data
other response!
other response!
other response!
other response!
other response!
other response!
other response!
other response!
other response!
the call sid closed! 
net send error=-1
send pack failed, size: 157, err: Connection refused
send pack failed, size: 1193, err: Connection refused
^C

問題:
the call sid closed! //呼叫結束
net send error=-1  //音訊執行緒出錯退出
send pack failed, size: 157, err: Connection refused//視訊執行緒遠端埠被關閉,連線拒絕。
send pack failed, size: 1193, err: Connection refused




相關推薦

樹莓sip視訊電話-3:exosip2+硬體h264+g711編碼初步實現

    之前使用python語音實現,但是python下的exosip2庫部分功能不能實現,現改為c語音的exsip方式,初步實現sip視訊電話功能。     測試環境:樹莓派------------elastix---------------ekiga(pc端)   視訊

樹莓 MPG視訊硬體解碼破解 Raspberry Pi Patch for MPEG-2, VC-1 license

Enable the Pi's hardware decoding of MPEG-2 and VC-1 MPEG2 patents have expired If you have  start.elf in  /boot  sudo

樹莓】配置Nginx代理實現樹莓遠端視訊監控

背景介紹: 在淘寶上入手一個樹莓派攝像頭,它是通過CSI介面連線到樹莓派板上,之前我使用的是CentOS系統,在網上找了很久,沒有發現能在CentOS系統下的攝像頭驅動(如果各位看客有相關資料或資訊,歡迎聯絡本人),沒辦法重新刷了樹莓派官方Debian系統,它自帶樹莓派攝像頭的管理工具—ra

通過FTP軟體樹莓(Raspberry Pi 3)可以和電腦上傳或者下載檔案

打開了SSH協議之後的樹莓派就形成了一個支援SFTP協議的伺服器,我們可以電腦中使用FTP軟體就可以與樹莓派進行檔案互動。 (注意:至於怎麼開啟SSH,我在《樹莓派(Raspberry Pi)3 Mo

樹莓智慧監控小車(QT+樹莓)------視訊流的獲取及硬體

前兩篇文章講到了總體的思路和客戶端與伺服器的通訊,借下來紀錄一下關於視訊流的獲取,這裡用的是樹莓派官方攝()淘寶版,軟體用的是mjpg-streamer(網上有很多版本,自己搜尋就好,也有教程)。利用這個這個軟體可以直接獲取視訊流在網頁上顯示(360瀏覽器無法顯示,我用的是

樹莓(raspberry pi)學習3: 下載安裝軟體包 (下載慢,修改映象源)

如果沒有更新過樹莓派(raspberry pi)的軟體包,則需要先更新一下,使用以下命令 sudo apt-get update sudo apt-get upgrade 執行過程中,可以按CTRL+C中斷 然後,下載安裝軟體包,命令如下: sudo apt-get

樹莓攝像頭視訊直播技術彙總

使用VLC, 延遲比較厲害,大概延遲2s,好處是支援RTSP協議,移動端方便實現 Here I'm documenting how to stream from the Raspberry Pi Camera board using VLC. Most of this

樹莓搭建視訊監控平臺

上一次用樹莓派搭建了Nexus私服,終於讓樹莓派不再成為吃灰派了,這次用樹莓派搭建視訊監控平臺,並實現視訊畫面推流到流媒體伺服器。 ### 1. 安裝nginx 要實現將視訊畫面推動到媒體伺服器,需要搭建一個流媒體伺服器,這裡選擇nginx + flv module 來搭建,需要用到的原始碼包如下: `

樹莓3B+ 安裝系統;搭建python,opencv環境;實現串列埠通訊

一:安裝系統 二:通過VNC訪問樹莓派(無線和有線) 1.樹莓派設定 安裝vnc sudo apt-get update sudo apt-get install tightvncserver 設定vnc密碼(一定要設定,要不然無法

製作樹莓映象img,並在其他新板上使用,實現批量克隆樹莓

1.      將現有的系統製作成映象img 2.      配置網路 1. 將現有的系統製作成映象img 1.1    先將新買的sd卡用SDFormatter工具格式化,以作備用 1.2    將帶有系統的sd卡用Win32DiskImager.exe工具Read成

樹莓3 Model B實現串列埠撥打電話

首先,由於樹莓派3 Model B的TTL引腳GPIO14、GPIO15預設被分配給了藍芽,所以為了能用這兩個引腳接GSM模組,必須把藍芽禁用掉,然後把GPIO14、GPIO15分配給串列埠。具體實現參考:http://blog.csdn.net/aguangg_6655_

樹莓3下使用QT5配置ffmpeg環境並呼叫硬體編解碼器

最近由於專案需要,使用樹莓派編碼兩路視訊無線傳輸到pc端,遇到種種難題 ,做個簡單筆記.借鑑了不少前人的經驗,給個連結http://www.jianshu.com/p/dec9bf9cffc9 平臺 樹莓派3b,raspbian系統,x264最新版庫,f

樹莓3學習筆記(7):7寸(分辨率800 480)顯示器配置

樹莓派、顯示器配置樹莓派3學習筆記(7):7寸(分辨率800 480)顯示器配置 樹莓派搭載分辨率為800X480的顯示器在顯示的時候可能會遇到無法全屏顯示的問題, 顯示器只有部分能夠顯示,有一部分是黑邊,對於這一種情況,我們只需進入系統的boot目錄,找到config.txt文件,或者直接在命

樹莓3 U盤啟動 配置

目錄 ttr bcd oot 樹莓派 寄存器 ctr print port 樹莓派3添加了一個新特性:允許USB啟動。現在我們既可以從SD卡啟動,也可以從USB啟動。USB設備可以是U盤,帶USB適配器的SSD硬盤,甚至是移動硬盤。 本文介紹怎麽從U盤啟動樹莓派3。 1

樹莓3 學習歷程

neu 修改 命令 默認 用戶 boa linu reboot archive 1 安裝系統 1.1 下載系統 去樹莓派官方下載最新版的系統https://www.raspberrypi.org/downloads/ 1.2 第一種安裝方法 將直接系統燒錄到SD卡,SD卡

從u盤啟動樹莓3

能夠 鏡像 clas font u盤啟動 eboot sd卡 準備 col 本教程將展示如何從移動硬盤,u盤啟動樹莓派3。註意,本項功能目前尚處於試驗性質且並不支持所有的usb存儲設備! program usb boot mode 在樹莓派3從u盤啟動之前,需要從設置了使能

Linux+樹莓3開發總結——樹莓遠程文件共享winows

打開 .net setting inux 技術 怎麽辦 details -s sso http://blog.csdn.net/xqf1528399071/article/details/52192134 ————&mdas

樹莓3 下運行.net core2.0

microsoft connect put net 程序 ros 樹莓派3 sof .cn 折騰兩天,終於在樹莓派上運行 .net 程序。在此記錄一下所踩的坑。先看一下結果: 為了這一行折騰了很久。 第一個坑,樹莓派安裝系統後不能直接ssh。

使用樹莓3獲取CPU溫度

ges 樹莓派 技術分享 ima cpu溫度 alt sys zone https 一、命令:   cat /sys/class/thermal/thermal_zone0/temp 二、上圖:      使用樹莓派3獲取CPU溫度

樹莓3(RaspberryPi 3B)上實現kms自動激活

樹莓派3 RaspberryPi 3B kms 自動激活 前置:1.主機名請從默認的RaspberryPi 改為 test.com sudo nano /etc/hostname 請將文件中的RaspberryPi改為 test.com 2.將樹莓派的ip地址改為靜態,此例中為172.16.0