Gstreamer工作原理分析_第1頁
Gstreamer工作原理分析_第2頁
Gstreamer工作原理分析_第3頁
Gstreamer工作原理分析_第4頁
Gstreamer工作原理分析_第5頁
免費預覽已結束,剩余17頁可下載查看

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、Gstreamer 工作原理分析GuideTechHowtoMountMaemoFileSystemGuideIssue:SystemAnalysisandDesignDocumentsIssueDate:RevisionHistoryDateIssueDescriptionAuthorFirstdraftwangfei目錄1.ABSTRACT32.INTRODUCTION33.原理分析 33.1術語介紹 33.1.1元素 33.1.2一些特別的元素。33.2插件的工作原理。53.3GST-LAUNCH的工作邏輯 73.4動態(tài)PIPELINE的創(chuàng)建原理 93.5DECODEBIN工作原理 10

2、3.6TYPEFIND勺實現原理 123.7SETUPELEMENT123.8PLAYBM勺工作原理 143.9數據流動 163.10總結 204.REFERENCE20HowtoMountMaemoFileSystemGuideIssue:SystemAnalysisandDesignDocumentsIssueDate:Abstract主要講的是 gstreamerfl 勺工作原理,包括 gst-launch 的分析和 playbin 的分析,以及數據的流動分析。Introduction先介紹一些術語,然后介紹了插件的工作原理,后面接著介紹了gst-launch,playbin,decod

3、ebin,typefind 數據流動.原理分析術語介紹.漀甀爀椀攀爀一攀眀 元素代碼里面的類型是 GstElement,可以理解為 gstreameifi 面的基類。漀甀爀椀攀爀一攀眀 一些特別的元素。Source:可以理解為源頭,也就是數據流的起始地,就像長江的發(fā)源地是沱沱河一樣。ntsrcSink:就是這個數據流最終要流向的地方,就像長江最終要流向東海一樣。sinlt_elErrient6InkFilter:過濾器, 就像是篩子一樣濾掉我們不感興趣的東西, 流下我們想要的東西, 或者從代碼上來說就是攔截下數據,對這個數據做一定的修改或者其它動作,當然你什么也不做也是可以的,然后再把數據傳出

4、去:flter6InksrcPipeline:典型的 pipeline 是這樣的:HowtoMountMaemoFileSystemGuideIssue:SystemAnalysisandDesignDocumentsIssueDate: Documentidentifierpipeline更復雜一點的:pip5liieaniic/x-raw-lai:audio/x-mw-jnrBin:有點像 pipeline,它們的區(qū)別就是 pipeline 肯定是一個 bin,但 bin 不一定是 pipeline,它就像一個盒子,里面放了什么東西你可以不關心。binGhostpad:文檔上說 ghost

5、pad 就像 linux 里面的 link 文件, 我的理解是在一個盒子上開一個口, 這樣里就可以訪問這個盒子了。decodersinkplay_3udosink.gGOUnsocgdamuxerverbiscfecocbrsinksrc.sinkaudiooonvortersinkscIlaudiooutputsink7即即pli兇兇tknA明明IaucHc/n-Yorbisajio/K-jaw一一fl口出口出ai;di:,比一比一jaw-Ut隆“K7CJtkelEmentlHowtoMountMaemoFileSystemGuideIssue:SystemAnalysisandDesign

6、DocumentsIssueDate:Documentidentifierbin插件的工作原理。如下圖所示,這個所有的基于插件的程序的工作原理類似,本質上都是通過讀取動態(tài)庫實現的,只需要每個動態(tài)庫都實現某一個特定的接口就可以了,比如 XX_init 等,這里就是 plugin_init。里面會有個像注冊表一樣的數據結構會存儲所有的插件的信息。HowtoMountMaemoFileSystemGuideIssue:SystemAnalysisandDesignDocumentsIssueDate:10/29/2007Documentidentifiermain(gst-launch.c)gst_

7、init_get_option_group#defineGST_PLUGIN_DEFINE(major,minor,name,description,init,version,license,package,origi一一n)GST_PLUGIN_EXPORTGstPluginDescgst_plugin_desc=major,minor,name,description,、init,vversion,1license,PACKAGE,package,、origin,GST_PADDING_INIT%GST_PLUGIN_DEFINE(GST_VERSION_MAJOR,GST_VERSION

8、_MINOR,playbin,playerbin,plugin_initVERSION,GST_LICENSE,GST_PACKAGE_NAME,GST_PACKAGE_ORIGIN)gst_plugin_load_file這里進入每一個插件的入口plugin_init;HowtoMountMaemoFileSystemGuideIssue:SystemAnalysisandDesignDocumentsIssueDate:DocumentidentifierGst-launch 的工作邏輯它的工作原理就是根據!號來劃分輸入的字符串,然后為每個元素構建一個 element,最后建立一個 pip

9、eline把這些元素加入,最后通過 setstate 來啟動整個數據循環(huán)。g_main_context_iteration一就M當于進X了g_main_loop_runHowtoMountMaemoFileSystemGuideIssue:HowtoMountMaemoFileSystemGuideIssue:SystemAnalysisandDesignDocumentsIssueDate:DocumentidentifierEvent_loop是消息處理循環(huán), 星而取出能取出的消息并處理,maingst_parse_launchv_gst_parse_escapegst_parse_lau

10、nch把所有的!分割的元素放到一個圖里面去,圖是由一個chain,和一個list組成的,chain存儲的是一條鏈,鏈里面包括元素也包由舌l(xiāng)inks,而list里面存儲的是linksgst_element_link_padsfilteredgst_parse_perform_delayed_link把src的pad和sink的pad連接起來,如果有filter就src-1rDelayedLink*data=g_new(DelayedLink,1);data-src_pad=g_strdup(src_pad);data-sink=sink;data-sink_pad=g_strdup(sink_p

11、ad)data-caps=gst_caps_copy(caps);data-signal_id=g_signal_connect(G_OBJECT(src),pad-added,G_CALLBACK(gst_parse_found_pad),data);gst_parse_found_padgst_element_link_pads_filtered到這里的話表示pipeline都已經建立的差不多了,下面就是接收dbus消息,然后設置相應的狀態(tài)_gst_parse_launchyyparsegst_bin_add(bin,GST_ELEMENT(walk-data);gst_parse_per

12、form_link把這兩個元素連接起來把這些元素加到新建立的pipeline!面去fileter-sink這里根據信號產生的新的pad作新的連接SystemAnalysisandDesignDocumentsIssueDate:動態(tài) pipeline 的創(chuàng)建原理我們經常需要創(chuàng)建動態(tài)的 pipeline,因為我們不知道源頭是什么格式的,這時候需要這么做:比如 demuxer,pipeline=gst_pipeline_new(my_pipeline);/向建 pipelinesource=gst_element_factory_make(filesrc,source);/創(chuàng)建srcg_objec

13、t_set(source,location,argv1,NULL);/國置輸入文件路徑demux=gst_element_factory_make(oggdemux,demuxer);/創(chuàng)建 demuxT6 素gst_bin_add_many(GST_BIN(pipeline),source,demux,NULL);/把這兩個加入到 pipeline 里面gst_element_link_pads(source,src,demux,sink);/把這兩個元素連接起來g_signal_connect(demux,pad-added,G_CALLBACK(cb_new_pad),NULL);/這里

14、是關鍵,demux 會創(chuàng)建出一個 pad,于是發(fā)出信號,等待我們設定的函數cb_new_pad.gst_element_set_state(GST_ELEMENT(pipeline),GST_STATE_PLAYING);/啟動整個數永流循環(huán)loop=g_main_loop_new(NULL,FALSE);g_main_loop_run(loop);/啟動gloop 事件循環(huán) staticvoidcb_new_pad(GstElement*element,GstPad*pad,gpointerdata)gchar*name;name=gst_pad_get_name(pad);g_print(

15、Anewpad%swascreatedn,name);g_free(name);/*here,youwouldsetupanewpadlinkforthenewlycreatedpad*/在這里就可以根據不同 pa 旅連接不同的后面的 element 比如:gst_element_link_pads(pad,src,fakesink,sink);而女而發(fā)現源數天的疝則是這樣的:typefind=gst_element_factory_make(typefind,typefinder);g_signal_connect(typefind,have-type,G_CALLBACK(cb_typef

16、ound),loop);每 Wtypefin 麗件查出類型的時候就會發(fā)出法 b 信號?!癘nceamediatypehasbeendetected,youcanpluganelement(e.g.ademuxerordecoder)totheHowtoMountMaemoFileSystemGuideIssue:SystemAnalysisandDesignDocumentsIssueDate:sourcepadofthetypefindelement,anddecodingofthemediastreamwillstartrightafter.總結:從上面這些例子可以看出,它的工作過程是這樣

17、的:首先連接能夠連接的原素,比如前端 src-decodebi然后后端連接converter-resampler-volume-audiosink。通過 decodebinfi 勺信號,根據源類型創(chuàng)建特定的 srcpad,再把這個 pad接到后端(后端可以創(chuàng)建一個ghostpacOZ 方便使用)這樣一個 pipeline 就建好了Decodebin 的工作原理3.4 節(jié)已經用文字介紹了 decodebin 的工作原理,下面的圖是講它的內部實現的:HowtoMountMaemoFileSystemGuideIssue:SystemAnalysisandDesignDocumentsIssueDa

18、te: gst_decode_bin_class_init:gstelement_klass-change_state=GST_DEBUG_FUNCPTR(gst_decode_bin_change_sta-te);-dynamic_add:dyn-np_sig_id=g_signal_connect(G_OBJECT(element),pad-added,G_CALLBACK(new_pad),dyn);dyn-nmp_sig_id=g_signal_connect(G_OBJECT(element),no-more-pads,G_CALLBACK(no_more_pads),dyn);gs

19、t_decode_bin_init這里基本上是調用的其父親的change_state只有在從ready到pause的時候會加一個add_fakesinkgst_bin_add(GST_BIN(decode_bin),decode_bin-typefind)decode_bin-have_type_id=g_signal_connect(G_OBJECT(decode_bin-typefind),have_type,G_CALLBACK(type_found),decodebin);信號調用parent_class-change_state(element,transition);這個函數的作用

20、是這樣的:首先由typefind發(fā)現源所設定的caps,也就決定了decodebin所提供的sink的類型,這個函數就是用來根據它提供的srccap抹選擇我總結:從上可以看出,decodebin的工作其實是依靠typefind來實現的,每當它識別出類型的時候就發(fā)出信號,由decodebin接收信號并處理,也就是根據它識別出的類型參數,尋找匹配的pad,如果找到了這個元素就把它加到decodebin里面來。這里需要注意的是queue的作用,queued味著是一個thread,他們之間的同步通過一系列的線程同步函數來實現,所以顯得非常的復雜type_foundclose_pad_linkfind_

21、compatibles當沒有pads需要添加的時候執(zhí)行gst_element_no_more_padsg_signal_emit(element,gst_element_signalsNO_MORE_PADS,0);try_to_link_1(decode_bin,element,pad,to_try)如果這個src元素的類型為demux則要先創(chuàng)建個queue;貝U下面的usersrcpad就是queued勺srcpad,到最后的時候會把source的src_消連接至1queue的sink:gst_element_set_state(element,GST_STATE_READY);gst_p

22、ad_link(usedsrcpad,sinkpad)gst_element_set_state(element,GST_STATE_PAUSED);這樣的iS源的srcpad就已經連上了demuxer或者其它元素的sink這里的element可以理解為源,而to_try是一個listg里面放的是可能連接這個sr&勺sink,比如mp3文件為element,那么sink就可能是我們的mp3decsinknew_paddecode_bin-typefind=gst_element_factory_make(typefind,typefind);gst_decode_bin_change_

23、stateForeachelementinbintoinvokegst_bin_class_initgstelement_class-change_state=GST_DEBUG_FUNCPTR(gst_bin_change_state_funcgst_bin_change_statefunc們能提供的iteratoractivatefolgstelementsetstatd_with_resync(oclass-set_state)(element,state);調用各自原素重載后的setstategst_element_set_basetimegst_bin_element_setstat

24、eIst_bin_src_pads_activatesink,比如解碼器Setupelement這里主要講的是如何把這些播放一個HowtoMountMaemoFileSystemGuideIssue:SystemAnalysisandDesignDocumentsIssueDate:3.6Typefind 的實現原理這里是講程序是如何查找出對應文件應該選用什么解碼器之類的,可以簡單理解為source 識另1J。-gst_type_find_element_init:-gst_pad_set_event_function(typefind-sink,GST_DEBUG_FUNCPTR(gst_t

25、ype_find_element_handle_event);TYPE_FIND_REGISTER這里會被會調雨,這是一個對gst_type_find_register 封裝的宏,這星會在二不類似“source 所需要的 element 連接起來的:mpeg4”的字符串設定一個匹配函數,這個函數的作用就是根據一段 buffer 里面的一些字節(jié)的特征來識別它的類型,比如像函數:mpeg4_video_type_find 等video/HowtoMountMaemoFileSystemGuideIssue:SystemAnalysisandDesignDocumentsIssueDate:gst_

26、element_get_request_padgst_element_class_get_pad_template這里返回取得的newpadgst_element_class_get_pad_template_list從這個list里面取出和傳入參數名字相同的templategst_play_bin_class_init:playbasebin_klass-setup_output_pads=setup_sinks;gen_vis_elementelement=gst_bin_new(abin);sink=gst_element_factory_make(alsasink,audiosink)

27、;Audioconvert,Audioresample,Volume,gst_element_link_pads(,)所以最下的結果薪像京這樣的:Audioconvert-audioresample-volume-audiosink最后兩點是:gst_element_add_pad(element,gst_ghost_pad_new一一一(sink,pad);一一一建立一個ghostpad在convert的sinkpad上g_hash_table_insert(play_bin-cache,abin,element);把這整個bin插入至Uplaybin的abincache里面去。gst_te

28、e_request_new_pad上接playbin頁這里就是取得audio類型的srcpad,然后連接到通過gen_audio_elementJi回的bingen_vis_elementorgen_audio_elementgstelement_class-request_new_padGST_DEBUG_FUNCPTR(gst_tee_request_new_pad);*pad=gst_element_get_pad(group-typeGST_STREAM_TYPE_AUDIO-1.preroll,src);res=add_sink(play_bin,sink,pad,NULL);剩下的

29、邏輯就是建立一個bin,bin里面的內容是如右所示:返回的是包含這些元素的bin,并且建立了一個ghostpad,作為整個bin的代表,te&勺意思就是把一個stream分解成兩部分tee在這里設置從這里我們可以推斷出preroll的作用應該是前端的,add_sink的作用應該就是何個src和sink連接起來Aqueue-asink這里連接的是video勺pad,前面一行代碼是說取一個preroll的srcpad,而后面一行里面的sink就是gen_video_elemen版回的那個bin,add_link的作用就是把這srcpad連接到這個sink。pad=gst_element_g

30、et_pad(group-typeGST_STREAM_TYPE_VIDEO-1.preroll,src);res=add_sink(play_bin,sink,pad,textsrcpad);*lement=gst_bin_new(vbin);構謹一不binconv=gst_element_factory_make(ffmpegcolorspace,conv);構建convertersink=gst_element_factory_make(DEFAULT_VIDEOSINK,sink);構建videosinkgst_bin_add(GST_BIN(element),conv);gst_bi

31、n_add(GST_BIN(element),sink);加入到bin里面去gst_element_link_pads(conv,src,sink,sink);連接這些padspad=gst_element_get_pad(conv,sink);gst_element_add_pad(element,gst_ghost_pad_new(sink,pad);創(chuàng)建ghostpadLVqueue-converter-vis-vsinkHowtoMountMaemoFileSystemGuideIssue:SystemAnalysisandDesignDocumentsIssueDate:Docume

32、ntidentifierPlaybin 的工作原理有了上面幾個步驟就可以很容易理解 playbin 的工作原理,不過細節(jié)依然非常的復雜,我只是畫了框架圖:gen_preroll_elementsinkpad=gst_element_get_request_pad(group-typetype-1.selector,sink%d);gst_pad_link(pad,sinkpad);addstreamno_more_pads_fullpreroll=gst_element_factory_make(queue,name);overrun_sig=g_signal_connect(G_OBJECT

33、(preroll),overrun,G_CALLBACK(queue_overrun),play_base_bin);gst_pad_add_event_probeprobe_triggeredqueue_overrun以下的幾個函數在每次新構建組的時候會被執(zhí)行。group_commitsetup_substreamsres=GST_PLAY_BASE_BIN_GET_CLASS(play_base_bin)-setup_output_pads(play_base_bin,group);下面的工作請看setup頁HowtoMountMaemoFileSystemGuideIssue:Syste

34、mAnalysisandDesignDocumentsIssueDate:Documentidentifier這里把sourcedecorder連接起來信號是如何發(fā)出信號是如何發(fā)出的,請看decodebin頁new_decoded_pad_fullgst_play_bin_change_state這里需要特別注意的是,gstreamer里面的狀態(tài)控制是,先從null-ready,然后從ready-pause,,所以setup_source0勺調用時機就是在從ready至1pause的時候gst_play_base_binchange_stateg_signal_connect(decoder,

35、element-added,G_CALLBACK(decodebin_element_added_cb),play_base_bin);g_signal_connect(G_OBJECT(decoder),new-decoded-pad,G_CALLBACK(new_decoded_pad),play_base_bin);g_signal_connect(G_OBJECT(decoder),no-more-pads,G_CALLBACK(no_more_pads),play_base_bin);上面對decoder做了幾個信號連接make_decodergst_element_link信號連接

36、decodebin_elementaddedcbnew_decoded_pad至于程序邏輯如何走到這里請看gst-launch頁這里我分析的是最普通的情形, 也就是需要解碼的, 如果源數據是raw的走別的路線數, 誰調用的待查?但這里只處理eosf言號prepare_output不同的basebin的子類不同的實現HowtoMountMaemoFileSystemGuideIssue:SystemAnalysisandDesignDocumentsIssueDate:Documentidentifier3.9 數據流動下面講的是里面最復雜的數據流動的分析,我主要分析了 src 的流動和 sin

37、k 的流動,因為中間的數據流動太多,比如解碼器,fHter 等等,實在沒空,但是我想有這兩元素的分析,其它的元素就很容易了:HowtoMountMaemoFileSystemGuideIssue:SystemAnalysisandDesignDocumentsIssueDate: klass-set_state=GST_DEBUG_FUNCPTR4-(gst_element_set_state_func);gst_base_src_startgst_element_changestatebclass-start(basesrc);(在init函數里被retval-pool.func=func;

38、gst_element_set_stateCaseGST_STATE_CHANGE_PAUSED_TO_PLAYING:gst_base_src_is_live(basesrc)在這里handlergst_base_src_l00P被賦名了GstTask的funcgst_base_src_loop是在gst_task_create里被我置前,在gst_task_func里通皮調詞的gst_base_src_loop請看數據流動頁2gst_pad_push(pad,一buf);gst_base_src_set_playingbclass-create(src,offset,length,buf)

39、;gst_base_src_do_syncgst_file_src_class_initgst_pad_start_task(basesrc-srcpad,(GstTaskFunction)gstbasesrcloopklass-pool=g_thread_pool_new(GFunc)gst_task_func,klass,-1,FALSE,NULL);gst_task_funcg_thread_pool_newtask-func(task-INdata);g_thread_create(g_thread_pool_thread_proxy,pool,FALSE,&local_err

40、or);設置),當元素為filesrc的時候task-func=func;gst_pad_push_event設置gst_file_src_create設置gst_task_creategst_task_startinit時狀態(tài)為GSTgst_task_class_initK_STOPPED,于是調用它gst_base_src_changestategst_base_src_get_rangegst_file_src_create_mmap或者gst_file_src_create_readgstbasesrc_class-create=GST_DEBUG_FUNCPTR(gst_file_s

41、rc_create);gthreadpoolpushpool-pool.func(task,pool-pool.user_data);調用g_async_queue_popunlockedg_thread_pool_startthreadg_thread_pool_threadproxy設置HowtoMountMaemoFileSystemGuideIssue:SystemAnalysisandDesignDocumentsIssueDate:Documentidentifiergst_pad_emit_havedata_signal(chainfunc二GST_PAD_CHAINFUNC(p

42、ad)ret=chainfunc(pad,buffer);gst_pad_set_chain_function(trans-sinkpad,GST_DEBUG_FUNCPTR(gst_base_transform_cha一in);一gst_base_transform_chain這是volume的處理邏輯gst_base_transform_init從這里整個數據流的循環(huán)就開始了, 從src的pad發(fā)送數據,調用peer的pad的chainfunc函數,這樣一步步的傳下去了HowtoMountMaemoFileSystemGuideIssue:SystemAnalysisandDesignDo

43、cumentsIssueDate:Documentidentifiergst_base_audio_sink_class_init:tbasesink_class-set_caps=GST_DEBUG_FUNCPTR(gst_base_audio_sink_setcaps);res=rclass-acquire(buf,spec);從這個signa廂wait操作就可以看清楚它的脈絡了,當數據流開始的時候,writefunc一直等待在那個環(huán)行緩沖的地方,等待被喚醒,當數據開始的時候由render函數開始一直到gst_ring_buffer_commit_full函數,這里面把數據用memcpy把

44、數據送到環(huán)行數據緩沖區(qū)里面,并且發(fā)出喚醒信號,于是整個生產者消費者循環(huán)開始了,它們之間是通過g_cond_signal來實現同步的GST_AUDIORINGG_BUFFER_SIGNAL(buf);gst_ring_buffer_startVrclass-start(buf);gst_audioringbuffer_class_init:調用調發(fā)出信號gstringbuffer_class-start二GST_DEBUG_FUNCPTR(gst_audioringbuffer_start);HowtoMountMaemoFileSystemGuideIssue:SystemAnalysisan

45、dDesignDocumentsIssueDate:3.10 總結.時間有限,我只能分析這些框架了,估計看懂這幾個圖也不容易,兩個關鍵的函數是 change_statdX 是一個經常被重載的函數,基本上元素的初始化,狀態(tài)的改變都會在這里面會被處理,另外一個非常關鍵的 chaingfunc 這個函數是開啟數據流動的關鍵函數,通常 srdM 用 chainfunc 函數,在這個函數里面會取出 peerpad 的 chainfunc,然后調用 src 對應的 sink 的 chainfunc,另外一個讓我迷惑了很久的是到 sink 的數據是如何流動的,最后分析的結果是它使用了一個環(huán)行緩沖,并且使用了

46、線程同步操作來實現生產者消費者的模式以加快數據的傳遞,所以數據的最終傳遞是通過發(fā)出 p、v 操作來實現的,里面先把數據取出來調用 memcp 冰 copy 到環(huán)行緩沖,然后發(fā)出 v 操作,對應的消費者被喚醒調用 sink 插件的 write 操作把數據寫到設備里面去,這里面還有幾塊我沒有分析的,比如時間戳的處理,各種元素之間的延遲的協(xié)商,以及音頻視頻之間的同步播放等,這里面涉及很多多線程問題,如果要分析估計得花很多的時間。4.reference1/documentation/補充:#playbin 的工作流程1,創(chuàng)建 source。

47、2,創(chuàng)建 decodebin。3,為 decodebin加 typefind。4,在向 bin 添加元素的時候都會發(fā)出 element-added 勺信號。5,向 decodebin力口 fakesink。6,把 decodebinft 口入至1Jplaybin.7,等待 type 巾 nd 元素發(fā)出的 have-type1 號,并調用回調函數,注意這里的信號實際是只包含了 caps息,而后,根據注冊表里面的每個 factory 取出其提供的 capsftype 巾 nd 提供的 cap飄交集,如果有交集則加入到一個 list 表中(find_compatibles)。8,取出 list 里面的元素來連接,如果找到一個就結束,找到后把這個元素與 src 元素連接起來,遞歸的進行這個元素本身的 sinkpad 的連接,直到收到了 no_more_pads 勺消息。9,decodebirn 攵到 no_more_pad肖息后也會發(fā)出同樣的消息,其余處理邏輯請看文檔gstreamerX 作原理.HowtoMountMaemoFileSystemGuideIssue:SystemAnalysisandDe

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論