您当前的位置:首页 > 生活 > 内容

patsubst(如何用Android NDK编译FFmpeg)

本文目录

  • 如何用Android NDK编译FFmpeg
  • makefile foreach 中能掉用函数么
  • 请问一下这种代码叫什么语言
  • 关于makefile patsubst函数的问题
  • patsubst:模式字符串替换 我想问下什么是模式字符串啊这是一道linux的知识
  • 现已知两个c语言程序hello1.C和hello2.c,请编写一个makefile文件使其生成可执行文
  • makefile当中foreach能不能和patsubst一起使用
  • Android Makefile中是 如何识别 TARGET_PRODUCT 的
  • Linux下 Makefile中的 patsubst 的问题!!!!!!!高手快来

如何用Android NDK编译FFmpeg

  一、安装cygwin、配置ndk和下载ffmpeg源码  这步就不说了,网上很多教程,再次声明本教程只针对ndk R4这个版本。需要说明的是,本人在cygwin安装路径下的.bash_profile文件中指定的NDK路径如下所示。因为本人装了好几个NDK,因此后面的R4只是个标示。  NDK_R4=/cygdrive/d/android-ndk-r4  export NDK_R4  二、编译前准备和编译  1、因为R4这个NDK比较旧,交叉编译的时候需要在一个Android环境中,那简单,创建一个Android空项目,把整个项目拷出来,在项目下建立一个文件夹jni,把ffmpeg0.6.6的源码拷进去。左图,HelloJni就是我新建的一个项目,Android.mk这时候你还没有,先不用管。右图ffmpeg-0.6.6文件夹的内容要跟我一样,直接就是代码。我这里的ffmpeg_cywin这个文件夹是随便建的,放哪里无所谓的。      2、在ffmpeg-0.6.6下建立一个文件config.sh,内容如下所示。需要注意的是,unix下的换行符和windows下是不一样,如果直接拷贝到windows下的记事本,后面执行这个config.sh的时候会出问题,这里我用的是notepad++编辑的,在编辑-》档案格式转换-》转换为UNIX格式。(注意,后面的所有的Android.mk的编辑都有此要求)。  简单说一下这个config.sh,PREBUILT和PLATFORM根据你安装ndk的位置而不同,config.sh其实是一个脚本,执行这个脚本的时候又调用了另外一个脚本configure,configure主要是根据编译选项(下面enable disable那些),生成相应的编译配置,就是说你想要编译ffmpeg什么模块就自己定制编译选项的内容。基本上这个文件只要修改一下PREBUILT和PLATFORM就行,其他都不用改。  #!/bin/bash  export PREBUILT=D://android-ndk-r4/build/prebuilt/windows/arm-eabi-4.4.0  export PLATFORM=D://android-ndk-r4/build/platforms/android-8/arch-arm  ./configure --target-os=linux \  --arch=arm \  --enable-version3 \  --enable-gpl \  --enable-nonfree \  --disable-stripping \  --disable-ffmpeg \  --disable-ffplay \  --disable-ffserver \  --disable-ffprobe \  --disable-encoders \  --disable-muxers \  --disable-devices \  --disable-protocols \  --enable-protocol=file \  --enable-avfilter \  --disable-network \  --disable-mpegaudio-hp \  --disable-avdevice \  --enable-cross-compile \  --cc=$PREBUILT/bin/arm-eabi-gcc \  --cross-prefix=$PREBUILT/bin/arm-eabi- \  --nm=$PREBUILT/bin/arm-eabi-nm \  --extra-cflags=“-fPIC -DANDROID“ \  --disable-asm \  --enable-neon \  --enable-armv5te \  --extra-ldflags=“-Wl,-T,$PREBUILT/arm-eabi/lib/ldscripts/armelf.x -Wl,-rpath-link=$PLATFORM/usr/lib -L$PLATFORM/usr/lib -nostdlib $PREBUILT/lib/gcc/arm-eabi/4.4.0/crtbegin.o $PREBUILT/lib/gcc/arm-eabi/4.4.0/crtend.o -lc -lm -ldl“  3、修改configure文件,找到下图的内容,修改成我这样,这个是用来存放执行脚本过程的临时文件的,我这里用的是D://NDK,你可以设置其他地方,但是要先创建好这个文件夹,放哪里无所谓的。  4、然后在cywin中进入ffmpeg0.6.6文件夹,执行chmod -x config.sh,然后执行./config,此过程需要一定的时间。如果这一步出现问题,很有可能是你config.sh中的PREBUILT和PLATFORM的路径设置不对,或者是你拷贝内容到config.sh的时候没有在UNIX格式下。执行完如下图所示。  5、在ffmpeg-0.6.6下会生成一个config.h文件,编辑它,找到#define restrict restrict这一行,把它改成#define restrict  6、在libavutil/libm.h下,把所有static的方法注释掉或者直接删掉。  7、修改libavcodec,libavfilter,libavformat,libavutil,libpostproc和libswscale目录的MakeFile文件,每个文件中,删除语句  include $( SUBDIR ) ../config.mak 和 include $ (SUBDIR) .. / subdir.mak。  libavcodec下的makefile中搜索inverse.o,把它所在的那一行删掉,要不编译的时候会冲突。  8、在ffmpeg-0.6.6文件夹下,创建av.mk文件(UNIX格式),内容如下:  #LOCAL_PATH is one of libavutil, libavcodec, libavformat, or libswscale  #include $(LOCAL_PATH)/../config-$(TARGET_ARCH).mak  include $(LOCAL_PATH)/../config.mak  OBJS :=  OBJS-yes :=  MMX-OBJS-yes :=  include $(LOCAL_PATH)/Makefile  # collect objects  OBJS-$(HAVE_MMX) += $(MMX-OBJS-yes)  OBJS += $(OBJS-yes)  FFNAME := lib$(NAME)  FFLIBS := $(foreach,NAME,$(FFLIBS),lib$(NAME))  FFCFLAGS = -DHAVE_AV_CONFIG_H -Wno-sign-compare -Wno-switch -Wno-pointer-sign  FFCFLAGS += -DTARGET_CONFIG=\“config-$(TARGET_ARCH).h\“  ALL_S_FILES := $(wildcard $(LOCAL_PATH)/$(TARGET_ARCH)/*.S)  ALL_S_FILES := $(addprefix $(TARGET_ARCH)/, $(notdir $(ALL_S_FILES)))  ifneq ($(ALL_S_FILES),)  ALL_S_OBJS := $(patsubst %.S,%.o,$(ALL_S_FILES))  C_OBJS := $(filter-out $(ALL_S_OBJS),$(OBJS))  S_OBJS := $(filter $(ALL_S_OBJS),$(OBJS))  else  C_OBJS := $(OBJS)  S_OBJS :=  endif  C_FILES := $(patsubst %.o,%.c,$(C_OBJS))  S_FILES := $(patsubst %.o,%.S,$(S_OBJS))  FFFILES := $(sort $(S_FILES)) $(sort $(C_FILES))  9、在jni文件夹下,创建Android.mk(UNIX格式),内容如下:  include $(all-subdir-makefiles)  10、在ffmpeg-0.6.6文件夹下,创建Android.mk,内容如下:  LOCAL_PATH := $(call my-dir)  include $(CLEAR_VARS)  LOCAL_STATIC_LIBRARIES := libavformat libavcodec libavutil libpostproc libswscale  LOCAL_MODULE := ffmpeg  include $(BUILD_SHARED_LIBRARY)  include $(call all-makefiles-under,$(LOCAL_PATH))  11、在ffmpeg-0.6.6\libavformat下,创建Android.mk,内容如下:  LOCAL_PATH := $(call my-dir)  include $(CLEAR_VARS)  include $(LOCAL_PATH)/../av.mk  LOCAL_SRC_FILES := $(FFFILES)  LOCAL_C_INCLUDES := \  $(LOCAL_PATH) \  $(LOCAL_PATH)/..  LOCAL_CFLAGS += $(FFCFLAGS)  LOCAL_CFLAGS += -include “string.h“ -Dipv6mr_interface=ipv6mr_ifindex  LOCAL_LDLIBS := -lz  LOCAL_STATIC_LIBRARIES := $(FFLIBS)  LOCAL_MODULE := $(FFNAME)  include $(BUILD_STATIC_LIBRARY)  12、在ffmpeg-0.6.6\libavcodec下,创建Android.mk,内容如下:  LOCAL_PATH := $(call my-dir)  include $(CLEAR_VARS)  include $(LOCAL_PATH)/../av.mk  LOCAL_SRC_FILES := $(FFFILES)  LOCAL_C_INCLUDES := \  $(LOCAL_PATH) \  $(LOCAL_PATH)/..  LOCAL_CFLAGS += $(FFCFLAGS)  LOCAL_LDLIBS := -lz  LOCAL_STATIC_LIBRARIES := $(FFLIBS)  LOCAL_MODULE := $(FFNAME)  include $(BUILD_STATIC_LIBRARY)  13、在ffmpeg-0.6.6\libavfilter、libavutil、libpostproc和libswscale下,创建Android.mk,内容如下:  LOCAL_PATH := $(call my-dir)  include $(CLEAR_VARS)  include $(LOCAL_PATH)/../av.mk  LOCAL_SRC_FILES := $(FFFILES)  LOCAL_C_INCLUDES := \  $(LOCAL_PATH) \  $(LOCAL_PATH)/..  LOCAL_CFLAGS += $(FFCFLAGS)  LOCAL_STATIC_LIBRARIES := $(FFLIBS)  LOCAL_MODULE := $(FFNAME)  include $(BUILD_STATIC_LIBRARY)  14、然后在jni目录下,运行$NDK_R4/ndk-build -B,这里的命令需要根据你自己的情况修改,然后就开始编译了。过程需要10来分钟,成功之后,会在libs下生产libffmpeg.so。如果编译出来的libffmpeg.so只有1.5k,得如下修改一下NDK,再重新编译。  把下面红色部分加到NDK的build/core/build-binary.mk里:  LOCAL_STATIC_LIBRARIES := $(call strip-lib-prefix,$(LOCAL_STATIC_LIBRARIES))  LOCAL_STATIC_WHOLE_LIBRARIES := $(call strip-lib-prefix,$(LOCAL_STATIC_WHOLE_LIBRARIES))  ...  static_libraries := $(call map,static-library-path,$(LOCAL_STATIC_LIBRARIES))  static_whole_libraries := $(call map,static-library-path,$(LOCAL_STATIC_WHOLE_LIBRARIES))  ...  $(call module-add-static-depends,$(LOCAL_MODULE),$(LOCAL_STATIC_LIBRARIES))  $(call module-add-static-depends,$(LOCAL_MODULE),$(LOCAL_STATIC_WHOLE_LIBRARIES))  ...  $(LOCAL_BUILT_MODULE): $(static_libraries) $(static_whole_libraries) $(shared_libraries)  ...  $(LOCAL_BUILT_MODULE): PRIVATE_STATIC_LIBRARIES := $(static_libraries)  $(LOCAL_BUILT_MODULE): PRIVATE_WHOLE_STATIC_LIBRARIES := $(static_whole_libraries)  接着再将最外层ffmpeg/Android.mk里面的LOCAL_STATIC_LIBRARIES改成LOCAL_STATIC_WHOLE_LIBRARIES

makefile foreach 中能掉用函数么

在Makefile中可以使用函数来处理变量,从而让我们的命令或是规则更为的灵活和具 有智能。make所支持的函数也不算很多,不过已经足够我们的操作了。函数调用后,函 数的返回值可以当做变量来使用。 一、函数的调用语法函数调用,很像变量的使用,也是以“$”来标识的,其语法如下:$(《function》 《arguments》 ) 或是${《function》 《arguments》}这里,《function》就是函数名,make支持的函数不多。《arguments》是函数的参数,参数 间以逗号“,”分隔,而函数名和参数之间以“空格”分隔。函数调用以“$”开头,以圆 括号或花括号把函数名和参数括起。感觉很像一个变量,是不是?函数中的参数可以使 用变量,为了风格的统一,函数和变量的括号最好一样,如使用“$(substa,b,$(x))”这 样的形式,而不是“$(substa,b,${x})”的形式。因为统一会更清楚,也会减少一些不必 要的麻烦。还是来看一个示例:comma:= , empty:= space:= $(empty) $(empty) foo:= a b c bar:= $(subst $(space),$(comma),$(foo))在这个示例中,$(comma)的值是一个逗号。$(space)使用了$(empty)定义了一个空格, $(foo)的值是“a b c”,$(bar)的定义用,调用了函数“subst”,这是一个替换函数,这 个函数有三个参数,第一个参数是被替换字串,第二个参数是替换字串,第三个参数 是替换操作作用的字串。这个函数也就是把$(foo)中的空格替换成逗号,所以$(bar)的值 是“a,b,c”。 二、字符串处理函数$(subst 《from》,《to》,《text》 )名称:字符串替换函数——subst。 功能:把字串《text》中的《from》字符串替换成《to》。 返回:函数返回被替换过后的字符串。示例:$(subst ee,EE,feet on the street),把“feetonthestreet”中的“ee”替换成“EE”,返回结果是“fEEtonthestrEEt”。$(patsubst 《pattern》,《replacement》,《text》 ) 名称:模式字符串替换函数——patsubst。 功能:查找《text》中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否 符合模式《pattern》,如果匹配的话,则以《replacement》替换。这里,《pattern》可以包括 通配符“%”,表示任意长度的字串。如果《replacement》中也包含“%”,那么, 《replacement》中的这个“%”将是《pattern》中的那个“%”所代表的字串。(可以用“\” 来转义,以“\%”来表示真实含义的“%”字符) 返回:函数返回被替换过后的字符串。示例:$(patsubst %.c,%.o,x.c.c bar.c)把字串“x.c.cbar.c”符合模式[%.c]的单词替换成[%.o],返回结果是“x.c.obar.o”备注:这和我们前面“变量章节”说过的相关知识有点相似。如:“$(var:《pattern》=《replacement》 )” 相当于 “$(patsubst 《pattern》,《replacement》,$(var))”,而“$(var:《suffix》=《replacement》)” 则相当于 “$(patsubst %《suffix》,%《replacement》,$(var))”。例如有:objects=foo.obar.obaz.o, 那么,“$(objects:.o=.c)”和“$(patsubst%.o,%.c,$(objects))”是一样的。$(strip 《string》 )名称:去空格函数——strip。 功能:去掉《string》字串中开头和结尾的空字符。 返回:返回被去掉空格的字符串值。 示例:$(strip a b c )把字串“abc”去到开头和结尾的空格,结果是“abc”。$(findstring 《find》,《in》 )名称:查找字符串函数——findstring。 功能:在字串《in》中查找《find》字串。 返回:如果找到,那么返回《find》,否则返回空字符串。 示例:$(findstring a,a b c) $(findstring a,b c)第一个函数返回“a”字符串,第二个返回“”字符串(空字符串)$(filter 《pattern...》,《text》 )名称:过滤函数——filter。 功能:以《pattern》模式过滤《text》字符串中的单词,保留符合模式《pattern》的单词。可 以有多个模式。 返回:返回符合模式《pattern》的字串。 示例:sources := foo.c bar.c baz.s ugh.h foo: $(sources) cc $(filter %.c %.s,$(sources)) -o foo$(filter %.c %.s,$(sources))返回的值是“foo.c bar.c baz.s”。$(filter-out 《pattern...》,《text》 )名称:反过滤函数——filter-out。 功能:以《pattern》模式过滤《text》字符串中的单词,去除符合模式《pattern》的单词。可 以有多个模式。 返回:返回不符合模式《pattern》的字串。 示例:objects=main1.o foo.o main2.o bar.o mains=main1.o main2.o$(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。$(sort 《list》 )名称:排序函数——sort。 功能:给字符串《list》中的单词排序(升序)。 返回:返回排序后的字符串。 示例:$(sortfoobarlose)返回“barfoolose”。 备注:sort函数会去掉《list》中相同的单词。 $(word 《n》,《text》 )名称:取单词函数——word。 功能:取字符串《text》中第《n》个单词。(从一开始) 返回:返回字符串《text》中第《n》个单词。如果《n》比《text》中的单词数要大,那么返回 空字符串。 示例:$(word2,foobarbaz)返回值是“bar”。$(wordlist 《s》,《e》,《text》 )名称:取单词串函数——wordlist。 功能:从字符串《text》中取从《s》开始到《e》的单词串。《s》和《e》是一个数字。 返回:返回字符串《text》中从《s》到《e》的单词字串。如果《s》比《text》中的单词数要大, 那么返回空字符串。如果《e》大于《text》的单词数,那么返回从《s》开始,到《text》结束 的单词串。 示例:$(wordlist2,3,foobarbaz)返回值是“barbaz”。$(words 《text》 )名称:单词个数统计函数——words。 功能:统计《text》中字符串中的单词个数。 返回:返回《text》中的单词数。 示例:$(words,foobarbaz)返回值是“3”。 备注:如果我们要取《text》中最后的一个单词,我们可以这样:$(word $(words 《text》 ),《text》 )。$(firstword 《text》 )名称:首单词函数——firstword。 功能:取字符串《text》中的第一个单词。 返回:返回字符串《text》的第一个单词。 示例:$(firstwordfoobar)返回值是“foo”。 备注:这个函数可以用word函数来实现:$(word1,《text》)。以上,是所有的字符串操作函数,如果搭配混合使用,可以完成比较复杂的功能。这里, 举一个现实中应用的例子。我们知道,make使用“VPATH”变量来指定“依赖文件”的 搜索路径。于是,我们可以利用这个搜索路径来指定编译器对头文件的搜索路径参数 CFLAGS,如:override CFLAGS += $(patsubst %,-I%,$(subst :, ,$(VPATH)))如果我们的“$(VPATH)”值是“src:../headers”,那么“$(patsubst %,-I%,$(subst :, , $(VPATH)))”将返回“-Isrc -I../headers”,这正是 cc 或 gcc 搜索头文件路径的参数。

请问一下这种代码叫什么语言

这个是 makefile 文件,应该不能算作一种语言。一般来讲,这个是在Linux 环境下,用来编译C 语言程序用的。你可以认为是makefile 语言,或者shell 脚本。你这个代码应该是基于某个开发框架写的,比如include ../../$(PLAT).mk,说明编译时包含了开发框架的makefile, SRC 指明源文件的路径,$(CC) 就是你指定的gcc 编译器,总之,有了这个文件,可以在linux 终端中使用make命令直接自动化编译项目想学的话,可以参看 makefile 相关教程,一般Linux C 程序设计的书里面有makefile 的相关介绍

关于makefile patsubst函数的问题

这句话的意思是说,把$lib中的变量,逐个替换为 -Lxxx比如定义LIB = aa bb cc dd ee那么$(patsubst %, -L%, $(LIB))的结果是-Laa -Lbb -Lcc -Ldd -Lee

patsubst:模式字符串替换 我想问下什么是模式字符串啊这是一道linux的知识

patsubst 应该是 makefile 里的一个函数 (如果你问的 patsubst 是其他语言里的,那就别看下面的了)比方说你在 makefile 里定义了一个变量,内容是一堆 .c 文件的的名字,如SRC = aaa.c bbb.c my.c his.c你可以用 patsubst 根据 某种模式,将这些名字改成另外的,比如将这些 .c 文件的名字变成 .s 为后缀的名字:NEW = $(patsubst “%.c“, “%.s“, ${SRC})这样 NEW 这个变量中,就记录了 aaa.s bbb.s my.s his.s 这些后缀名为 .s 的文件。 上面命令中的模式字符串就是 “%.c“ 或 “%.s“, 而其中的 “%“ 就是特殊的匹配字符,对于 aaa.c 这个字符串来说, “%.c“ 中的% 就匹配 aaa。 这个很像我们在 bash 或者 dos 下用 *.c 匹配所有 .c 文件时候的那个 *

现已知两个c语言程序hello1.C和hello2.c,请编写一个makefile文件使其生成可执行文

CC = gcc OBJS = hello1.o hello2.o EXEC = hello $(EXEC): $(OBJS) $(CC) -o $@ $^ hello1.o: hello1.c $(CC) -c $《 hello2.o: hello2.c $(CC) -c $《 clean: rm -rf $(EXEC) *.o

makefile当中foreach能不能和patsubst一起使用

makefile当中foreach能不能和patsubst一起使用不是,这个只是做字符串转换,比方说你在makefile里定义了所有要编译的源文件SRCC = a.c b.c c.c那么你可以用 patsubst 这个函数来将 SRCC 里存放的源文件名字转换成对应的目标文件名,OBJS = $(patsubst %.c, %.o, $(SRCC))此时 OBJS 的内容就是 a.o b.o c.o

Android Makefile中是 如何识别 TARGET_PRODUCT 的

TARGET_PRODUCT 来决定编译定制product.首先, 编译Android 代码 通常情况下使用:# make showcommands这实际上等价于下面的完整命令 (具体参见 build/core/envsetup.mk )# TARGET_ARCH=arm TARGET_PRODUCT=generic TARGET_BUILD_TYPE=release make showcommands可见,默认情况下编译系统认为TARGET_PRODUCT 是generic 的那如何编译特定产品的Android呢?这就需要查看Android Makefile是如何解析环境变量TARGET_PRODUCT的。Android Makefile 的引用关系是这样的Makefile -》 build/core/main.mk -》 build/core/config.mk -》 build/core/envsetup.mk -》 build/core/product_config.mk在build/core/product_config.mk 中编译系统首先调用 build/core/product.mk中定义的函数get-all-product-makefiles ,来遍历整个vendor 的子目录, 找到vendor下所有的 AndroidProducts.mk, 不同子目录下的AndroidProducts.mk 中定义了不同的 PRODUCT_NAME, PRODUCT_DEVICE 等信息,(我们也可以通过 打开build/core/product_config.mk 中的#$(dump-products) 语句使控制台编译的时候输出所有product 的信息) , 接着build/core/product_config.mk 会调用resolve-short-product-name 将TARGET_PRODUCT匹配的AndroidProducts.mk 中定义的 PRODUCT_DEVICE 赋值给TARGET_DEVICE。有了这个TARGET_DEVICE, 再回到 build/core/config.mk,会include $(TARGET_DEVCIE)/BoardConfig.mkboard_config_mk := / $(strip $(wildcard / $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)/BoardConfig.mk / vendor/*/$(TARGET_DEVICE)/BoardConfig.mk / )) include $(board_config_mk)而这个配置文件BoardConfig.mk 决定了目标系统编译属性,比如使用ALSA还是不是 GENERIC_AUDIO 等等另外在这里TARGET_DEVICE 宏也决定了TARGET_DEVICE_DIR, 因为TARGET_DEVICE_DIR 取的是上面提到的BoardConfig.mk 的路径。TARGET_DEVICE_DIR := $(patsubst %/,%,$(dir $(board_config_mk)))当然Android 的Ob目标输出也是由TARGET_DEVICE决定,见build/core/envsetup.mkTARGET_OUT_ROOT_release := $(OUT_DIR)/targetTARGET_OUT_ROOT_debug := $(DEBUG_OUT_DIR)/targetTARGET_OUT_ROOT := $(TARGET_OUT_ROOT_$(TARGET_BUILD_TYPE))TARGET_PRODUCT_OUT_ROOT := $(TARGET_OUT_ROOT)/productPRODUCT_OUT := $(TARGET_PRODUCT_OUT_ROOT)/$(TARGET_DEVICE)再回到 build/core/main.mk, 编译系统接着做的一个件事情是,遍历所有字目录,找到所有Android.mk文件,并将这些Android.mk文件include 进来## Typical build; include any Android.mk files we can find.#subdir_makefiles := / $(shell build/tools/findleaves.py --prune=out --prune=.repo --prune=.git $(subdirs) Android.mk)include $(subdir_makefiles)我们再来看其中的./build/target/board/Android.mk,对了它引用了include $(TARGET_DEVICE_DIR)/AndroidBoard.mk由上面TARGET_DEVICE_DIR的定义,这下又进入了vendor 下TARGET_DEVICE指向的目录了,这个mk文件中定义了特定Product需要编译和安装app 和 script.

Linux下 Makefile中的 patsubst 的问题!!!!!!!高手快来

不是,这个只是做字符串转换,比方说你在makefile里定义了所有要编译的源文件SRCC = a.c b.c c.c那么你可以用 patsubst 这个函数来将 SRCC 里存放的源文件名字转换成对应的目标文件名,OBJS = $(patsubst %.c, %.o, $(SRCC))此时 OBJS 的内容就是 a.o b.o c.o


声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,谢谢。

上一篇: adsl共享(两台电脑,怎样共享ADSL)

下一篇: 领导能力强的表现,领导能力强表现的方面(体现在这五个方面)



猜你感兴趣

推荐阅读

网站内容来自网络,如有侵权请联系我们,立即删除! | 软文发布 | 粤ICP备2021106084号