基於FFMPEG的Wince版本網絡收音機設計與開發
軟件架構設計
模塊依賴關係圖
FFMPEG的編譯移植
./configure \
--enable-cross-compile \
--enable-memalign-hack \
--target-os=mingw32ce \
--arch=armv4 \
--cross-prefix=arm-wince-mingw32ce- \
--enable-small \
--disable-static \
--enable-shared \
--disable-doc \
--disable-ffplay \
--disable-ffmpeg \
--disable-ffprobe \
--disable-ffserver \
--disable-encoders \
--disable-network \
--disable-muxers \
--disable-protocols \
--enable-protocol=file \
--prefix=armv4 \
--extra-cflags="-march=armv4 -mtune=xscale" \
>armv4.txt
Msdl模塊的移植
Ffmpeg IO模塊的實現,URLProtocol接口的實現
typedef struct _RadioContext{
struct stream_t* stream;
} RadioContext;
static int stream_open(URLContext *h, const char *uri, int flags)
{
RadioContext* rc = h->priv_data;
struct url_t *url=NULL;
struct download_opts_t *dlopts=NULL;
struct stream_t* stream=NULL;
int ret = 0;
char* prawuri = NULL; //原始的uri路徑
char* filepath = NULL;
//printf("%d:%s\n", __LINE__,__FUNCTION__);
netstatus_changed(CONNECTING);
prawuri = strdup(uri+strlen("radio://"));
while (1)
{
ret = prepare_open(prawuri,&url,&dlopts);
if(ret <= 0) {
goto failed;
}
ret = streaming_open(url,dlopts,&stream);
if(ret <= 0) {
goto failed;
}
if(!is_metafile(prawuri))
break;
//下載跳轉文件
{
char* p = getprocessfilepath();
char* buffer = (uint8_t *)xmalloc(BUFSIZE_4_DL);
filepath = malloc(256);
strcpy(filepath,p);
free(p);
p = strrchr(filepath,'\\');
sprintf(p+1,"%s","metafile");
ret = streaming_read(stream,buffer,BUFSIZE_4_DL,filepath);
free(buffer);
if(ret < 0)
goto failed;
}
//分析跳轉文件,解析出url連接
{
struct list_h *p = NULL; /* iterator */
struct list_h *target_str_list = NULL; /* string */
ret = get_url_list_from_file(filepath,&target_str_list);
if(ret < 0) { /*could not open file*/
goto failed;
}
else if(ret == 0) {
display(MSDL_ERR,"input file \"%s\" does not contain any url\n",filepath);
goto failed;
}
p = target_str_list;
if(p && p->next) //不支持多目標播放
{
display(MSDL_ERR,"\n\nDo not support multi-target objects\n\n",filepath);
//輸出所有的連接地址
while(p)
{
display(MSDL_ERR,"radio url:%s\n",(char*)p->p);
p = p->next;
}
//查找第一個mms或rtsp協議的地址,非http
p = target_str_list;
while(p)
{
if(strstr(p->p,"mms://") || strstr(p->p,"rtsp://"))
break;
p = p->next;
}
}
//釋放資源
free_stream(stream);
url = NULL;
dlopts = NULL;
stream = NULL;
if(filepath) free(filepath);
if(prawuri) free(prawuri);
//如果沒有合適的地址,則直接返回
if(!p)
goto failed;
prawuri = strdup(p->p);
}
}
//printf("%d:%s\n", __LINE__,__FUNCTION__);
h->is_streamed = 1;
rc->stream = stream;
if(prawuri) free(prawuri);
return 0;
failed:
if(url) free_url_t(url);
if(dlopts) free_download_opts_t(dlopts);
if(filepath) free(filepath);
if(prawuri) free(prawuri);
if(stream) stream->close(stream);
netstatus_changed(CONNECT_FAILED);
return -1;
}
static int stream_close(URLContext *h)
{
RadioContext* rc = h->priv_data;
struct stream_t *stream = rc->stream;
//增加快速退出代碼行
if(stream)
{
if(stream->url)
free_url_t(stream->url);
if(stream->dlopts)
free_download_opts_t(stream->dlopts);
//必須在上兩句代碼行之後調用,不然會出異常
stream->close(stream);
rc->stream = NULL;
}
return 0;
}
static int radio_open(URLContext *h, const char *uri, int flags)
{
bStopStream = 0;
return stream_open(h,uri,flags);
}
static int radio_close(URLContext *h)
{
stream_close(h);
netstatus_changed(STOPPED);
return 0;
}
static int radio_read(URLContext *h, uint8_t *buf, int size)
{
int ret = 0;
RadioContext* rc = h->priv_data;
struct stream_t *stream = rc->stream;
while(1)
{
if(stream)
ret = streaming_read(stream,buf,size,0);
if(stream && ret > 0)
{
netstatus_changed(DOWNLOADING);
break;
}
else //獲取數據失敗,掉線重連
{
notify_netstatus func = func_cb;
func_cb = NULL; //暫停網絡狀態回調
stream_close(h);
func_cb = func; //開啓網絡狀態回調
if(bStopStream){
netstatus_changed(CONNECT_FAILED);
break;
}
printf("%d:%s retry open server\n",__LINE__,__FUNCTION__);
//重新打開
ret = stream_open(h,h->filename,h->flags);
if(ret < 0)
break;
rc = h->priv_data;
stream = rc->stream;
}
}
return ret;
}
URLProtocol ff_radio_protocol = {
"radio",
radio_open,
radio_read,
0,
0,
radio_close,
/*next*/ NULL,
/*url_read_pause*/ NULL,
/*url_read_seek*/ NULL,
/*url_get_file_handle*/NULL,
/*priv_data_size*/ sizeof(RadioContext),
};
MSDLAPI void StopStreamData()
{
bStopStream = 1;
}
MSDLAPI URLProtocol* RadioProtocol()
{
return &ff_radio_protocol;
}