当前位置: 首页 > news >正文

免费的网站在线客服软件/百度一下网页版浏览器

免费的网站在线客服软件,百度一下网页版浏览器,广西建网站,网页设计师培训费媒体编解码API使用示例 //获取相关格式文件的内容信息,如轨道数量、获取MIME信息、视频的高度与宽度、语言格式、播放总时长等 MediaExtractor mediaExtractor new MediaExtractor(); try {mediaExtractor.setDataSource(path); // 设置数据源 } catch (IOExcept…

媒体编解码API使用示例

//获取相关格式文件的内容信息,如轨道数量、获取MIME信息、视频的高度与宽度、语言格式、播放总时长等
MediaExtractor mediaExtractor = new MediaExtractor(); 
try {mediaExtractor.setDataSource(path); // 设置数据源
} catch (IOException e1) {e1.printStackTrace();
}String mimeType = null; // "video/mp4v-es" - MPEG4 video, "audio/3gpp" - AMR narrowband audio
for (int i = 0; i < mediaExtractor.getTrackCount(); i++) { // 信道总数MediaFormat format = mediaExtractor.getTrackFormat(i); // 音频文件信息mimeType = format.getString(MediaFormat.KEY_MIME);if (mimeType.startsWith("video/")) { // 视频信道mediaExtractor.selectTrack(i); // 切换到视频信道try {mediaCodec = MediaCodec.createDecoderByType(mimeType); // 创建解码器,提供数据输出} catch (IOException e) {e.printStackTrace();}mediaCodec.configure(format, surface, null, 0);break;}
}
mediaCodec.start(); // 启动MediaCodec ,等待传入数据

1.createDecoderByType根据MimeType信息,创建相匹配的解码器。

public static MediaCodec createDecoderByType(@NonNull String type)throws IOException {return new MediaCodec(type, true /* nameIsType */, false /* encoder */);
}private MediaCodec(@NonNull String name, boolean nameIsType, boolean encoder) {Looper looper;if ((looper = Looper.myLooper()) != null) {mEventHandler = new EventHandler(this, looper);} else if ((looper = Looper.getMainLooper()) != null) {mEventHandler = new EventHandler(this, looper);} else {mEventHandler = null;}mCallbackHandler = mEventHandler;mOnFrameRenderedHandler = mEventHandler;mBufferLock = new Object();native_setup(name, nameIsType, encoder);
}

通过JNI调用到MediaCodec.cpp

static void android_media_MediaCodec_native_setup(sp<JMediaCodec> codec = new JMediaCodec(env, thiz, tmp, nameIsType, encoder);
}JMediaCodec::JMediaCodec(JNIEnv *env, jobject thiz,const char *name, bool nameIsType, bool encoder): mClass(NULL),mObject(NULL) {
...if (nameIsType) {mCodec = MediaCodec::CreateByType(mLooper, name, encoder, &mInitStatus);} else {mCodec = MediaCodec::CreateByComponentName(mLooper, name, &mInitStatus);}CHECK((mCodec != NULL) != (mInitStatus != OK));
}

2.CreateByType创建对应的MediaCodec并初始化

// frameworks\av\media\libstagefright\MediaCodec.cpp
sp<MediaCodec> MediaCodec::CreateByType(const sp<ALooper> &looper, const AString &mime, bool encoder, status_t *err, pid_t pid,uid_t uid) {Vector<AString> matchingCodecs;// 1.根据mime获取对应的codecMediaCodecList::findMatchingCodecs(mime.c_str(),encoder,0,&matchingCodecs);if (err != NULL) {*err = NAME_NOT_FOUND;}//2.创建对应的MediaCodec并初始化for (size_t i = 0; i < matchingCodecs.size(); ++i) {sp<MediaCodec> codec = new MediaCodec(looper, pid, uid);AString componentName = matchingCodecs[i];status_t ret = codec->init(componentName);if (err != NULL) {*err = ret;}if (ret == OK) {return codec;}ALOGD("Allocating component '%s' failed (%d), try next one.",componentName.c_str(), ret);}return NULL;
}

3.findMatchingCodecs获取对应的codec

先获取支持的编解码器列表,再匹配最佳的编解码器

//frameworks\av\media\libstagefright\MediaCodecList.cpp
void MediaCodecList::findMatchingCodecs(const char *mime, bool encoder, uint32_t flags,Vector<AString> *matches) {matches->clear();// 获取系统支持的编解码器列表const sp<IMediaCodecList> list = getInstance();if (list == nullptr) {return;}//匹配最佳的编解码器size_t index = 0;for (;;) {ssize_t matchIndex =list->findCodecByType(mime, encoder, index);if (matchIndex < 0) {break;}index = matchIndex + 1;const sp<MediaCodecInfo> info = list->getCodecInfo(matchIndex);CHECK(info != nullptr);AString componentName = info->getCodecName();if ((flags & kHardwareCodecsOnly) && isSoftwareCodec(componentName)) {ALOGV("skipping SW codec '%s'", componentName.c_str());} else {matches->push(componentName);ALOGV("matching '%s'", componentName.c_str());}}if (flags & kPreferSoftwareCodecs ||property_get_bool("debug.stagefright.swcodec", false)) {matches->sort(compareSoftwareCodecsFirst);}
}

4.获取系统支持的Codec列表

先通过binder调到MediaPlayerService的getCodecList函数

//frameworks\av\media\libstagefright\MediaCodecList.cpp
sp<IMediaCodecList> MediaCodecList::getInstance() {Mutex::Autolock _l(sRemoteInitMutex);if (sRemoteList == nullptr) {sp<IBinder> binder =defaultServiceManager()->getService(String16("media.player"));sp<IMediaPlayerService> service =interface_cast<IMediaPlayerService>(binder);if (service.get() != nullptr) {// 获取服务端的MediaCodecListsRemoteList = service->getCodecList();if (sRemoteList != nullptr) {sBinderDeathObserver = new BinderDeathObserver();binder->linkToDeath(sBinderDeathObserver.get());}}if (sRemoteList == nullptr) {// if failed to get remote list, create local listsRemoteList = getLocalInstance();}}return sRemoteList;
}

可以看到又回到了MediaCodecList,没错就是media/stagefright下的MediaCodecList
说明MediaCodecList的创建是在mediaplayerservice进程

//frameworks\av\media\libmediaplayerservice\MediaPlayerService.cpp
sp<IMediaCodecList> MediaPlayerService::getCodecList() const {return MediaCodecList::getLocalInstance();
}


GetBuilders()作为参数创建MediaCodecList单例并返回,关键在于GetBuilders()函数

//frameworks\av\media\libstagefright\MediaCodecList.cpp
sp<IMediaCodecList> MediaCodecList::getLocalInstance() {Mutex::Autolock autoLock(sInitMutex);if (sCodecList == nullptr) {MediaCodecList *codecList = new MediaCodecList(GetBuilders());if (codecList->initCheck() == OK) {sCodecList = codecList;if (isProfilingNeeded()) {ALOGV("Codec profiling needed, will be run in separated thread.");pthread_t profiler;if (pthread_create(&profiler, nullptr, profilerThreadWrapper, nullptr) != 0) {ALOGW("Failed to create thread for codec profiling.");}}} else {// failure to initialize may be temporary. retry on next call.delete codecList;}}return sCodecList;
}GetBuilders
std::vector<MediaCodecListBuilderBase *> GetBuilders() {std::vector<MediaCodecListBuilderBase *> builders;// if plugin provides the input surface, we cannot use OMX video encoders.// In this case, rely on plugin to provide list of OMX codecs that are usable.sp<PersistentSurface> surfaceTest =StagefrightPluginLoader::GetCCodecInstance()->createInputSurface();if (surfaceTest == nullptr) {builders.push_back(&sOmxInfoBuilder);}builders.push_back(GetCodec2InfoBuilder());return builders;
}

5.构造MediaCodecList

frameworks\av\media\libstagefright\MediaCodecList.cpp
MediaCodecList::MediaCodecList(std::vector<MediaCodecListBuilderBase*> builders) {mGlobalSettings = new AMessage();mCodecInfos.clear();MediaCodecListWriter writer;for (MediaCodecListBuilderBase *builder : builders) {if (builder == nullptr) {ALOGD("ignored a null builder");continue;}// 进入build的buildMediaCodecList函数mInitCheck = builder->buildMediaCodecList(&writer);if (mInitCheck != OK) {break;}}writer.writeGlobalSettings(mGlobalSettings);writer.writeCodecInfos(&mCodecInfos);std::stable_sort(mCodecInfos.begin(),mCodecInfos.end(),[](const sp<MediaCodecInfo> &info1, const sp<MediaCodecInfo> &info2) {if (info2 == nullptr) {return false;} else if (info1 == nullptr) {return true;} else {return info1->rank() < info2->rank();}});
}

通过OmxInfoBuilder创建MediaCodec列表(途径一)
1、通过HIDL调用到OmxStore.cpp, 通过OmxStore从xml解析支持的CodecList及Attributes
2、将支持的codec存储进swCodecName2Info, hwCodecName2Info

//frameworks\av\media\libstagefright\OmxInfoBuilder.cpp
status_t OmxInfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) {// Obtain IOmxStoresp<IOmxStore> omxStore = IOmxStore::getService();if (omxStore == nullptr) {ALOGE("Cannot find an IOmxStore service.");return NO_INIT;}// List service attributes (global settings)Status status;hidl_vec<IOmxStore::RoleInfo> roles;// 通过HIDL,获取inRoleListauto transStatus = omxStore->listRoles([&roles] (const hidl_vec<IOmxStore::RoleInfo>& inRoleList) {roles = inRoleList;});if (!transStatus.isOk()) {ALOGE("Fail to obtain codec roles from IOmxStore.");return NO_INIT;}hidl_vec<IOmxStore::ServiceAttribute> serviceAttributes;transStatus = omxStore->listServiceAttributes([&status, &serviceAttributes] (Status inStatus,const hidl_vec<IOmxStore::ServiceAttribute>& inAttributes) {status = inStatus;serviceAttributes = inAttributes;});if (!transStatus.isOk()) {ALOGE("Fail to obtain global settings from IOmxStore.");return NO_INIT;}if (status != Status::OK) {ALOGE("IOmxStore reports parsing error.");return NO_INIT;}for (const auto& p : serviceAttributes) {writer->addGlobalSetting(p.key.c_str(), p.value.c_str());}// Convert roles to lists of codecs// codec name -> index into swCodecs/hwCodecsstd::map<hidl_string, std::unique_ptr<MediaCodecInfoWriter>>swCodecName2Info, hwCodecName2Info;char rank[PROPERTY_VALUE_MAX];uint32_t defaultRank = 0x100;if (property_get("debug.stagefright.omx_default_rank", rank, nullptr)) {defaultRank = std::strtoul(rank, nullptr, 10);}//将支持的codec存储进swCodecName2Info, hwCodecName2Infofor (const IOmxStore::RoleInfo& role : roles) {const hidl_string& typeName = role.type;bool isEncoder = role.isEncoder;bool preferPlatformNodes = role.preferPlatformNodes;// If preferPlatformNodes is true, hardware nodes must be added after// platform (software) nodes. hwCodecs is used to hold hardware nodes// that need to be added after software nodes for the same role.std::vector<const IOmxStore::NodeInfo*> hwCodecs;for (const IOmxStore::NodeInfo& node : role.nodes) {const hidl_string& nodeName = node.name;// OMX.google开头的都是软解码bool isSoftware = hasPrefix(nodeName, "OMX.google");MediaCodecInfoWriter* info;if (isSoftware) {auto c2i = swCodecName2Info.find(nodeName);if (c2i == swCodecName2Info.end()) {// Create a new MediaCodecInfo for a new node.c2i = swCodecName2Info.insert(std::make_pair(nodeName, writer->addMediaCodecInfo())).first;info = c2i->second.get();info->setName(nodeName.c_str());info->setOwner(node.owner.c_str());info->setEncoder(isEncoder);info->setRank(defaultRank);} else {// The node has been seen before. Simply retrieve the// existing MediaCodecInfoWriter.info = c2i->second.get();}} else {auto c2i = hwCodecName2Info.find(nodeName);if (c2i == hwCodecName2Info.end()) {// Create a new MediaCodecInfo for a new node.if (!preferPlatformNodes) {c2i = hwCodecName2Info.insert(std::make_pair(nodeName, writer->addMediaCodecInfo())).first;info = c2i->second.get();info->setName(nodeName.c_str());info->setOwner(node.owner.c_str());info->setEncoder(isEncoder);info->setRank(defaultRank);} else {// If preferPlatformNodes is true, this node must be// added after all software nodes.hwCodecs.push_back(&node);continue;}} else {// The node has been seen before. Simply retrieve the// existing MediaCodecInfoWriter.info = c2i->second.get();}}std::unique_ptr<MediaCodecInfo::CapabilitiesWriter> caps =info->addMime(typeName.c_str());if (queryCapabilities(node, typeName.c_str(), isEncoder, caps.get()) != OK) {ALOGW("Fail to add mime %s to codec %s",typeName.c_str(), nodeName.c_str());info->removeMime(typeName.c_str());}}。。。}return OK;
}

通过OmxStore.cpp解析xml中的编解码器及属性
OmxStore.cpp读取的配置文件有:
实际项目,在 /vendor/etc 中

//frameworks\av\media\libstagefright\xmlparser\include\media\stagefright\xmlparser\MediaCodecsXmlParser.h
static constexpr char const* defaultSearchDirs[] ={"/odm/etc", "/vendor/etc", "/etc", nullptr};
static constexpr char const* defaultMainXmlName ="media_codecs.xml";
static constexpr char const* defaultPerformanceXmlName ="media_codecs_performance.xml";
static constexpr char const* defaultProfilingResultsXmlPath ="/data/misc/media/media_codecs_profiling_results.xml";

获取对应解码器的查询能力,实际为创建对应得解码器

//frameworks\av\media\libstagefright\OmxInfoBuilder.cpp
status_t queryCapabilities(const IOmxStore::NodeInfo& node, const char* mime, bool isEncoder,MediaCodecInfo::CapabilitiesWriter* caps) {sp<ACodec> codec = new ACodec();status_t err = codec->queryCapabilities(node.owner.c_str(), node.name.c_str(), mime, isEncoder, caps);        // ...
}

依靠binder机制调用OMX服务中的allocateNode()

//frameworks\av\media\libstagefright\ACodec.cpp
status_t ACodec::queryCapabilities(const char* owner, const char* name, const char* mime, bool isEncoder,MediaCodecInfo::CapabilitiesWriter* caps) {const char *role = AVUtils::get()->getComponentRole(isEncoder, mime);if (role == NULL) {return BAD_VALUE;}OMXClient client;// 获取名为“” Omx实现status_t err = client.connect(owner);if (err != OK) {return err;}// 获取omx bp代理对象sp<IOMX> omx = client.interface();sp<CodecObserver> observer = new CodecObserver;sp<IOMXNode> omxNode;// hidl远程调用allocateNodeerr = omx->allocateNode(name, observer, &omxNoe);// ...    
}

获取服务名为XXXX的OMX,并以LWOmx封装返回

frameworks\av\media\libstagefright\OMXClient.cpp
status_t OMXClient::connect(const char* name) {using namespace ::android::hardware::media::omx::V1_0;if (name == nullptr) {name = "default";}sp<IOmx> tOmx = IOmx::getService(name);if (tOmx.get() == nullptr) {ALOGE("Cannot obtain IOmx service.");return NO_INIT;}if (!tOmx->isRemote()) {ALOGE("IOmx service running in passthrough mode.");return NO_INIT;}mOMX = new utils::LWOmx(tOmx);ALOGI("IOmx service obtained");return OK;
}

6.allocateNode创建对应的解码器

LWOmx是代理,具体的实现在libstagefright_omx.so

//frameworks\av\media\libmedia\omx\1.0\WOmx.cpp
status_t LWOmx::allocateNode(char const* name,sp<IOMXObserver> const& observer,sp<IOMXNode>* omxNode) {status_t fnStatus;// allocateNodestatus_t transStatus = toStatusT(mBase->allocateNode(name, new TWOmxObserver(observer),[&fnStatus, omxNode](Status status, sp<IOmxNode> const& node) {fnStatus = toStatusT(status);*omxNode = new LWOmxNode(node);}));return transStatus == NO_ERROR ? fnStatus : transStatus;
}

6.1 创建对应的OMXNodeInstance对象

OMXNodeInstance对象持有一些关键信息,比如生成的节点id(mNodeID)、ACodec传递下来的observer、plugin里创建的解码组件handle和构造OMXNodeInstance时传入的omx对象等。还有关键的解码事件返回的kCallbacks。

//frameworks\av\media\libstagefright\omx\1.0\Omx.cpp 
//libstagefright_omx.so
Return<void> Omx::allocateNode(const hidl_string& name,const sp<IOmxObserver>& observer,allocateNode_cb _hidl_cb) {using ::android::IOMXNode;using ::android::IOMXObserver;sp<OMXNodeInstance> instance;{Mutex::Autolock autoLock(mLock);if (mLiveNodes.size() == kMaxNodeInstances) {_hidl_cb(toStatus(NO_MEMORY), nullptr);return Void();}// 1. 实例化OMXNodeInstance对象,存放node id、解码组件handle、ACodec传递下来的observer等instance = new OMXNodeInstance(this, new LWOmxObserver(observer), name.c_str());OMX_COMPONENTTYPE *handle;// 2. master通知plugin创建对应的解码组件、并返回其操作句柄 OMX_ERRORTYPE err = mMaster->makeComponentInstance(name.c_str(), &OMXNodeInstance::kCallbacks,instance.get(), &handle);if (err != OMX_ErrorNone) {LOG(ERROR) << "Failed to allocate omx component ""'" << name.c_str() << "' "" err=" << asString(err) <<"(0x" << std::hex << unsigned(err) << ")";_hidl_cb(toStatus(StatusFromOMXError(err)), nullptr);return Void();}instance->setHandle(handle);// Find quirks from mParserconst auto& codec = mParser.getCodecMap().find(name.c_str());if (codec == mParser.getCodecMap().cend()) {LOG(WARNING) << "Failed to obtain quirks for omx component ""'" << name.c_str() << "' ""from XML files";} else {uint32_t quirks = 0;for (const auto& quirk : codec->second.quirkSet) {if (quirk == "requires-allocate-on-input-ports") {quirks |= OMXNodeInstance::kRequiresAllocateBufferOnInputPorts;}if (quirk == "requires-allocate-on-output-ports") {quirks |= OMXNodeInstance::kRequiresAllocateBufferOnOutputPorts;}}instance->setQuirks(quirks);}mLiveNodes.add(observer.get(), instance);mNode2Observer.add(instance.get(), observer.get());}observer->linkToDeath(this, 0);_hidl_cb(toStatus(OK), new TWOmxNode(instance));return Void();
}// frameworks/av/media/libstagefright/omx/OMXNodeInstance.cpp
// static
OMX_CALLBACKTYPE OMXNodeInstance::kCallbacks = {&OnEvent, &OnEmptyBufferDone, &OnFillBufferDone
};

6.2 makeComponentInstance

OMXMaster是解码库加载的核心
先找到指定的plugin(解码器组件,有厂商定制的及AOSP的)、再通知plugin去创建对应的解码组件。

frameworks\av\media\libstagefright\omx\OMXMaster.cpp
OMX_ERRORTYPE OMXMaster::makeComponentInstance(const char *name,const OMX_CALLBACKTYPE *callbacks,OMX_PTR appData,OMX_COMPONENTTYPE **component) {ALOGI("makeComponentInstance(%s) in %s process", name, mProcessName);Mutex::Autolock autoLock(mLock);*component = NULL;// 找到对应的Pluginssize_t index = mPluginByComponentName.indexOfKey(String8(name));if (index < 0) {return OMX_ErrorInvalidComponentName;}OMXPluginBase *plugin = mPluginByComponentName.valueAt(index);// 创建对应的解码组件OMX_ERRORTYPE err =plugin->makeComponentInstance(name, callbacks, appData, component);if (err != OMX_ErrorNone) {return err;}mPluginByInstance.add(*component, plugin);return err;
}

这些解码器是在哪加载的呢,回到OMXMaster构造函数

加载软硬编解码管理器中的所有解码器,并存在mPluginByComponentName。
1.加载厂商编解码管理器,libstagefrighthw.so
2.加载软编解码管理器,SoftOMXPlugin

OMXMaster::OMXMaster(): mVendorLibHandle(NULL) {pid_t pid = getpid();char filename[20];snprintf(filename, sizeof(filename), "/proc/%d/comm", pid);int fd = open(filename, O_RDONLY);if (fd < 0) {ALOGW("couldn't determine process name");strlcpy(mProcessName, "<unknown>", sizeof(mProcessName));} else {ssize_t len = read(fd, mProcessName, sizeof(mProcessName));if (len < 2) {ALOGW("couldn't determine process name");strlcpy(mProcessName, "<unknown>", sizeof(mProcessName));} else {// the name is newline terminated, so erase the newlinemProcessName[len - 1] = 0;}close(fd);}//加载厂商解码器SOaddVendorPlugin();//加载软解码器SOaddPlugin(new SoftOMXPlugin);
}void OMXMaster::addVendorPlugin() {addPlugin("libstagefrighthw.so");
}

7 实际创建对应解码器makeComponentInstance

如果是软件编解码器,调用SoftOMXPlugin的makeComponentInstance
如果是硬件就调用QComOMXPlugin的makeComponentInstance
Component可以理解为一个解码器实例。

7.1 软解码器

1.根据mime name匹配kComponents数据,如: { "OMX.google.aac.decoder", "aacdec", "audio_decoder.aac" },
2. 拿到的mLibNameSuffix值为aacdec,最后拼接的libName是libstagefright_soft_aacdec.so
3. 从system/lib目录下加载libstagefright_soft_aacdec.so库
4. 调用其对应的createSoftOMXComponent函数创建SoftOMXComponent

// frameworks\av\media\libstagefright\omx\SoftOMXComponent.cpp
OMX_ERRORTYPE SoftOMXPlugin::makeComponentInstance(const char *name,const OMX_CALLBACKTYPE *callbacks,OMX_PTR appData,OMX_COMPONENTTYPE **component) {ALOGV("makeComponentInstance '%s'", name);for (size_t i = 0; i < kNumComponents; ++i) {// 1.根据name匹配kComponents数据,// 如: { "OMX.google.aac.decoder", "aacdec", "audio_decoder.aac" },if (strcmp(name, kComponents[i].mName)) {continue;}// 2. 拿到的mLibNameSuffix值为aacdec,最后拼接的libName是libstagefright_soft_aacdec.soAString libName = "libstagefright_soft_";libName.append(kComponents[i].mLibNameSuffix);libName.append(".so");// RTLD_NODELETE means we keep the shared library around forever.// this eliminates thrashing during sequences like loading soundpools.// It also leaves the rest of the logic around the dlopen()/dlclose()// calls in this file unchanged.//// Implications of the change:// -- the codec process (where this happens) will have a slightly larger//    long-term memory footprint as it accumulates the loaded shared libraries.//    This is expected to be a small amount of memory.// -- plugin codecs can no longer (and never should have) depend on a//    free reset of any static data as the library would have crossed//    a dlclose/dlopen cycle.//// 3. 从system/lib目录下加载libstagefright_soft_aacdec.so库void *libHandle = dlopen(libName.c_str(), RTLD_NOW|RTLD_NODELETE);if (libHandle == NULL) {ALOGE("unable to dlopen %s: %s", libName.c_str(), dlerror());return OMX_ErrorComponentNotFound;}typedef SoftOMXComponent *(*CreateSoftOMXComponentFunc)(const char *, const OMX_CALLBACKTYPE *,OMX_PTR, OMX_COMPONENTTYPE **);CreateSoftOMXComponentFunc createSoftOMXComponent =(CreateSoftOMXComponentFunc)dlsym(libHandle,"_Z22createSoftOMXComponentPKcPK16OMX_CALLBACKTYPE""PvPP17OMX_COMPONENTTYPE");if (createSoftOMXComponent == NULL) {dlclose(libHandle);libHandle = NULL;return OMX_ErrorComponentNotFound;}// 4. 创建对应的SoftOMXComponentsp<SoftOMXComponent> codec =(*createSoftOMXComponent)(name, callbacks, appData, component);if (codec == NULL) {dlclose(libHandle);libHandle = NULL;return OMX_ErrorInsufficientResources;}OMX_ERRORTYPE err = codec->initCheck();if (err != OMX_ErrorNone) {dlclose(libHandle);libHandle = NULL;return err;}codec->incStrong(this);codec->setLibHandle(libHandle);return OMX_ErrorNone;}return OMX_ErrorInvalidComponentName;
}

SoftOmxPlugin是google提供的原生的一套编解码器插件
即通常说的软解硬解中的软解。它支持市面上常用的音视频格式,软解码支持的格式:

// frameworks\av\media\libstagefright\omx\SoftOMXPlugin.cpp
static const struct {const char *mName;const char *mLibNameSuffix;const char *mRole;} kComponents[] = {// two choices for aac decoding.// configurable in media/libstagefright/data/media_codecs_google_audio.xml// default implementation{ "OMX.google.aac.decoder", "aacdec", "audio_decoder.aac" },// alternate implementation{ "OMX.google.xaac.decoder", "xaacdec", "audio_decoder.aac" },{ "OMX.google.aac.encoder", "aacenc", "audio_encoder.aac" },{ "OMX.google.amrnb.decoder", "amrdec", "audio_decoder.amrnb" },{ "OMX.google.amrnb.encoder", "amrnbenc", "audio_encoder.amrnb" },{ "OMX.google.amrwb.decoder", "amrdec", "audio_decoder.amrwb" },{ "OMX.google.amrwb.encoder", "amrwbenc", "audio_encoder.amrwb" },{ "OMX.google.h264.decoder", "avcdec", "video_decoder.avc" },{ "OMX.google.h264.encoder", "avcenc", "video_encoder.avc" },{ "OMX.google.hevc.decoder", "hevcdec", "video_decoder.hevc" },{ "OMX.google.g711.alaw.decoder", "g711dec", "audio_decoder.g711alaw" },{ "OMX.google.g711.mlaw.decoder", "g711dec", "audio_decoder.g711mlaw" },{ "OMX.google.mpeg2.decoder", "mpeg2dec", "video_decoder.mpeg2" },{ "OMX.google.h263.decoder", "mpeg4dec", "video_decoder.h263" },{ "OMX.google.h263.encoder", "mpeg4enc", "video_encoder.h263" },{ "OMX.google.mpeg4.decoder", "mpeg4dec", "video_decoder.mpeg4" },{ "OMX.google.mpeg4.encoder", "mpeg4enc", "video_encoder.mpeg4" },{ "OMX.google.mp3.decoder", "mp3dec", "audio_decoder.mp3" },{ "OMX.google.vorbis.decoder", "vorbisdec", "audio_decoder.vorbis" },{ "OMX.google.opus.decoder", "opusdec", "audio_decoder.opus" },{ "OMX.google.vp8.decoder", "vpxdec", "video_decoder.vp8" },{ "OMX.google.vp9.decoder", "vpxdec", "video_decoder.vp9" },{ "OMX.google.vp8.encoder", "vpxenc", "video_encoder.vp8" },{ "OMX.google.vp9.encoder", "vpxenc", "video_encoder.vp9" },{ "OMX.google.raw.decoder", "rawdec", "audio_decoder.raw" },{ "OMX.google.flac.decoder", "flacdec", "audio_decoder.flac" },{ "OMX.google.flac.encoder", "flacenc", "audio_encoder.flac" },{ "OMX.google.gsm.decoder", "gsmdec", "audio_decoder.gsm" },
#ifdef QTI_FLAC_DECODER{ "OMX.qti.audio.decoder.flac", "qtiflacdec", "audio_decoder.flac" },
#endif
};

软解码器代码目录
frameworks\av\media\libstagefright\codecs,编译会生成对应的so库
车机内软解码so所在的目录

软解码器的实现

是基于“OpenMax IL的标准接口”进行,实际硬解码器也是如此。
以libstagefright_soft_aacdec为例子
libstagefright_soft_aacdec.so主体是SoftAAC2.cpp和SoftAAC2.h,所以dlsym返回的createSoftOMXComponent值如下:

//frameworks\av\media\libstagefright\codecs\aacdec\SoftAAC2.cpp
android::SoftOMXComponent *createSoftOMXComponent(const char *name, const OMX_CALLBACKTYPE *callbacks,OMX_PTR appData, OMX_COMPONENTTYPE **component) {return new android::SoftAAC2(name, callbacks, appData, component);
}

SoftAAC2继承自SimpleSoftOMXComponent

//frameworks\av\media\libstagefright\codecs\aacdec\SoftAAC2.cpp
SoftAAC2::SoftAAC2(const char *name,const OMX_CALLBACKTYPE *callbacks,OMX_PTR appData,OMX_COMPONENTTYPE **component): SimpleSoftOMXComponent(name, callbacks, appData, component),mAACDecoder(NULL),mStreamInfo(NULL),mIsADTS(false),mInputBufferCount(0),mOutputBufferCount(0),mSignalledError(false),mLastInHeader(NULL),mLastHeaderTimeUs(-1),mNextOutBufferTimeUs(0),mOutputPortSettingsChange(NONE) {initPorts();CHECK_EQ(initDecoder(), (status_t)OK);
}

SimpleSoftOMXComponent实现OpenMax IL的标准接口,例如sendCommand、setParameter

//frameworks\av\media\stagefright\omx\SimpleSoftOMXComponent.cpp
SimpleSoftOMXComponent::SimpleSoftOMXComponent(const char *name,const OMX_CALLBACKTYPE *callbacks,OMX_PTR appData,OMX_COMPONENTTYPE **component): SoftOMXComponent(name, callbacks, appData, component),mLooper(new ALooper),mHandler(new AHandlerReflector<SimpleSoftOMXComponent>(this)),mState(OMX_StateLoaded),mTargetState(OMX_StateLoaded) {mLooper->setName(name);mLooper->registerHandler(mHandler);mLooper->start(false, // runOnCallingThreadfalse, // canCallJavaANDROID_PRIORITY_VIDEO);
}

SoftOMXComponent里有对OpenMax接口的

// frameworks\av\media\stagefright\omx\SoftOMXComponent.cpp
// OMX_Component.h为 OpenMax IL version 1.1.2的接口头文件
#include <OMX_Component.h>
SoftOMXComponent::SoftOMXComponent(const char *name,const OMX_CALLBACKTYPE *callbacks,OMX_PTR appData,OMX_COMPONENTTYPE **component): mName(name),mCallbacks(callbacks),mComponent(new OMX_COMPONENTTYPE),mLibHandle(NULL) {mComponent->nSize = sizeof(*mComponent);mComponent->nVersion.s.nVersionMajor = 1;mComponent->nVersion.s.nVersionMinor = 0;mComponent->nVersion.s.nRevision = 0;mComponent->nVersion.s.nStep = 0;mComponent->pComponentPrivate = this;mComponent->pApplicationPrivate = appData;  。。。*component = mComponent;
}

7.2 硬解码器

调用mGetHandle函数指针,而该函数指针是在该类构造时初始化。

//hardware\qcom\media\libstagefrighthw\QComOMXPlugin.cpp
OMX_ERRORTYPE QComOMXPlugin::makeComponentInstance(const char *name,const OMX_CALLBACKTYPE *callbacks,OMX_PTR appData,OMX_COMPONENTTYPE **component) {if (mLibHandle == NULL) {return OMX_ErrorUndefined;}return (*mGetHandle)(reinterpret_cast<OMX_HANDLETYPE *>(component),const_cast<char *>(name),appData, const_cast<OMX_CALLBACKTYPE *>(callbacks));
}

可以看到原来mGetHandle就是libOmxCore.so库中的OMX_GetHandle函数

//hardware\qcom\media\libstagefrighthw\QComOMXPlugin.cpp
QComOMXPlugin::QComOMXPlugin(): mLibHandle(dlopen("libOmxCore.so", RTLD_NOW)),mInit(NULL),mDeinit(NULL),mComponentNameEnum(NULL),mGetHandle(NULL),mFreeHandle(NULL),mGetRolesOfComponentHandle(NULL) {if (mLibHandle != NULL) {mInit = (InitFunc)dlsym(mLibHandle, "OMX_Init");mDeinit = (DeinitFunc)dlsym(mLibHandle, "OMX_Deinit");mComponentNameEnum =(ComponentNameEnumFunc)dlsym(mLibHandle, "OMX_ComponentNameEnum");mGetHandle = (GetHandleFunc)dlsym(mLibHandle, "OMX_GetHandle");mFreeHandle = (FreeHandleFunc)dlsym(mLibHandle, "OMX_FreeHandle");mGetRolesOfComponentHandle =(GetRolesOfComponentFunc)dlsym(mLibHandle, "OMX_GetRolesOfComponent");if (!mInit || !mDeinit || !mComponentNameEnum || !mGetHandle ||!mFreeHandle || !mGetRolesOfComponentHandle) {dlclose(mLibHandle);mLibHandle = NULL;} else(*mInit)();}
}

OMX_GetHandle函数
1、如果是avc解码器,加载libOmxVideoDSMode.so库,判断是否有dsmode
2、视频预处理开启情况需要加载vpp库
3、动态加载对应的编解码so

//hardware\qcom\media\mm-core\src\common\qc_omx_core.c
// libOmxCore.so
OMX_API OMX_ERRORTYPE OMX_APIENTRY
OMX_GetHandle(OMX_OUT OMX_HANDLETYPE*     handle,OMX_IN OMX_STRING    componentName,OMX_IN OMX_PTR             appData,OMX_IN OMX_CALLBACKTYPE* callBacks)
{OMX_ERRORTYPE  eRet = OMX_ErrorNone;int cmp_index = -1;int hnd_index = -1;int vpp_cmp_index = -1;DEBUG_PRINT("OMXCORE API :  GetHandle %p %s %p\n", handle,componentName,appData);pthread_mutex_lock(&lock_core);if(handle){*handle = NULL;char optComponentName[OMX_MAX_STRINGNAME_SIZE];strlcpy(optComponentName, componentName, OMX_MAX_STRINGNAME_SIZE);// 如果是avc解码器,加载libOmxVideoDSMode.so库,判断是否有dsmodeif(strstr(componentName, "avc") && strstr(componentName, "decoder")){void *libhandle = dlopen("libOmxVideoDSMode.so", RTLD_NOW);if(libhandle){int (*fn_ptr)()  = dlsym(libhandle, "isDSModeActive");dlclose(libhandle);}else{DEBUG_PRINT_ERROR("Failed to load dsmode library");}}if(cmp_index < 0){cmp_index = get_cmp_index(componentName);strlcpy(optComponentName, componentName, OMX_MAX_STRINGNAME_SIZE);}if(cmp_index >= 0){char value[PROPERTY_VALUE_MAX];DEBUG_PRINT("getting fn pointer\n");// Load VPP omx component for decoder if vpp// property is enabled// 视频预处理开启情况需要加载vppif ((property_get("vendor.media.vpp.enable", value, NULL))&& (!strcmp("1", value) || !strcmp("true", value))) {DEBUG_PRINT("VPP property is enabled");if (!strcmp(core[cmp_index].so_lib_name, "libOmxVdec.so")|| !strcmp(core[cmp_index].so_lib_name, "libOmxSwVdec.so")) {vpp_cmp_index = get_cmp_index("OMX.qti.vdec.vpp");if (vpp_cmp_index < 0) {DEBUG_PRINT_ERROR("Unable to find VPP OMX lib in registry ");} else {DEBUG_PRINT("Loading vpp for vdec");cmp_index = vpp_cmp_index;}}}// dynamically load the socore[cmp_index].fn_ptr =omx_core_load_cmp_library(core[cmp_index].so_lib_name,&core[cmp_index].so_lib_handle);。。。。
}

加载对应的SO库

// /hardware\qcom\media\mm-core\src\common\qc_omx_core.c
static create_qc_omx_component
omx_core_load_cmp_library(char *libname, void **handle_ptr)
{create_qc_omx_component fn_ptr = NULL;if(handle_ptr){DEBUG_PRINT("Dynamically Loading the library : %s\n",libname);if (!strcmp(libname, "libOmxVpp.so"))*handle_ptr = dlopen(libname, RTLD_NOW|RTLD_GLOBAL);else*handle_ptr = dlopen(libname, RTLD_NOW);if(*handle_ptr){fn_ptr = dlsym(*handle_ptr, "get_omx_component_factory_fn");if(fn_ptr == NULL){DEBUG_PRINT("Error: Library %s incompatible as QCOM OMX component loader - %s\n",libname, dlerror());*handle_ptr = NULL;}}else{DEBUG_PRINT("Error: Couldn't load %s: %s\n",libname,dlerror());}}return fn_ptr;
}

高通平台支持的硬解码器
注意里边有一些是软解码(包含Sw字符的)

omx_core_cb_type core[] =
{//Common entriesOMX_REGISTRY_ENTRY("OMX.qcom.video.decoder.avc", "libOmxVdec.so", "video_decoder.avc"),OMX_REGISTRY_ENTRY("OMX.qcom.video.decoder.avc.secure", "libOmxVdec.so", "video_decoder.avc"),OMX_REGISTRY_ENTRY("OMX.qcom.video.decoder.mpeg2", "libOmxVdec.so", "video_decoder.mpeg2"),OMX_REGISTRY_ENTRY("OMX.qcom.video.decoder.mpeg2.secure", "libOmxVdec.so", "video_decoder.mpeg2"),OMX_REGISTRY_ENTRY("OMX.qcom.video.decoder.hevc", "libOmxVdec.so", "video_decoder.hevc"),OMX_REGISTRY_ENTRY("OMX.qcom.video.decoder.hevc.secure", "libOmxVdec.so", "video_decoder.hevc"),OMX_REGISTRY_ENTRY("OMX.qcom.video.decoder.vp8", "libOmxVdec.so", "video_decoder.vp8"),OMX_REGISTRY_ENTRY("OMX.qcom.video.decoder.vp9", "libOmxVdec.so", "video_decoder.vp9"),OMX_REGISTRY_ENTRY("OMX.qcom.video.decoder.vp9.secure", "libOmxVdec.so", "video_decoder.vp9"),OMX_REGISTRY_ENTRY("OMX.qti.video.decoder.mpeg4sw", "libOmxSwVdec.so", "video_decoder.mpeg4"),OMX_REGISTRY_ENTRY("OMX.qti.video.decoder.divxsw", "libOmxSwVdec.so", "video_decoder.divx"),OMX_REGISTRY_ENTRY("OMX.qti.video.decoder.divx4sw", "libOmxSwVdec.so", DIVX4_MIME),OMX_REGISTRY_ENTRY("OMX.qti.video.decoder.h263sw", "libOmxSwVdec.so", "video_decoder.h263"),OMX_REGISTRY_ENTRY("OMX.qcom.video.encoder.mpeg4sw", "libOmxSwVencMpeg4.so", "video_encoder.mpeg4"),OMX_REGISTRY_ENTRY("OMX.qcom.video.encoder.h263sw", "libOmxSwVencMpeg4.so", "video_encoder.h263"),OMX_REGISTRY_ENTRY("OMX.qcom.video.encoder.avc", "libOmxVenc.so", "video_encoder.avc"),OMX_REGISTRY_ENTRY("OMX.qcom.video.encoder.avc.secure", "libOmxVenc.so", "video_encoder.avc"),OMX_REGISTRY_ENTRY("OMX.qcom.video.encoder.vp8", "libOmxVenc.so", "video_encoder.vp8"),OMX_REGISTRY_ENTRY("OMX.qcom.video.encoder.hevc", "libOmxVenc.so", "video_encoder.hevc"),OMX_REGISTRY_ENTRY("OMX.qcom.video.encoder.hevc.secure", "libOmxVenc.so", "video_encoder.hevc"),OMX_REGISTRY_ENTRY("OMX.qcom.video.encoder.heic", "libOmxVenc.so", "image_encoder.heic"),OMX_REGISTRY_ENTRY("OMX.qcom.audio.decoder.Qcelp13", "libOmxQcelp13Dec.so", "audio_decoder.Qcelp13"),OMX_REGISTRY_ENTRY("OMX.qcom.audio.decoder.evrc", "libOmxEvrcDec.so", "audio_decoder.evrc"),OMX_REGISTRY_ENTRY("OMX.qcom.audio.decoder.wma", "libOmxWmaDec.so", "audio_decoder.wma"),OMX_REGISTRY_ENTRY("OMX.qcom.audio.decoder.wma10Pro", "libOmxWmaDec.so", "audio_decoder.wma"),OMX_REGISTRY_ENTRY("OMX.qcom.audio.decoder.wmaLossLess", "libOmxWmaDec.so", "audio_decoder.wma"),OMX_REGISTRY_ENTRY("OMX.qcom.audio.decoder.amrwbplus", "libOmxAmrwbplusDec.so", "audio_decoder.awbplus"),OMX_REGISTRY_ENTRY("OMX.qcom.audio.decoder.alac", "libOmxAlacDec.so", "audio_decoder.alac"),OMX_REGISTRY_ENTRY("OMX.qti.audio.decoder.alac.sw", "libOmxAlacDecSw.so", "audio_decoder.alac"),。。。。。
};

硬解码器的实现位置
Audio encode:hardware\qcom\audio\mm-audio
Audio decode:\vendor\qcom\proprietary\mm-audio\omx
Video encode/decode: hardware\qcom\media\mm-video-v4l2

对比Audio每个解码器均有一个so,Video采用v4l2框架实现,只有libOmxVenc.so,libOmxSwVencMpeg4
libOmxSwVdec,libOmxVdec.so 这4个库。

部分缩写含义

1.VPP 视频预处理
2.avc 就是H264
3.v4l2 是:video for linux version2,是Linux内核中关于视频设备的内核驱动框架,为上层的访问底层的视频设备提供了统一的接口.凡是内核中的子系统都有抽象底层硬件的差异,为上层提供统一的接口和提取出公共代码避免代码冗余等好处.

相关文章:

MediaCodec创建对应解码器

媒体编解码API使用示例 //获取相关格式文件的内容信息&#xff0c;如轨道数量、获取MIME信息、视频的高度与宽度、语言格式、播放总时长等 MediaExtractor mediaExtractor new MediaExtractor(); try {mediaExtractor.setDataSource(path); // 设置数据源 } catch (IOExcept…...

使用eXosip+ffmpeg、ffplay命令行实现sip客户端

文章目录 前言一、关键实现1、主要流程2、解决端口冲突&#xff08;1&#xff09;、出现原因&#xff08;2&#xff09;、解决方法 3、解析sdp&#xff08;1&#xff09;、定义实体&#xff08;2&#xff09;、解析视频&#xff08;3&#xff09;、解析音频 4、命令行推拉流&am…...

dotNet 之网络TCP

**硬件支持型号 点击 查看 硬件支持 详情** DTU701 产品详情 DTU702 产品详情 DTU801 产品详情 DTU802 产品详情 DTU902 产品详情 G5501 产品详情 ARM dotnet 编程 dotNet使用TCP&#xff0c;可以使用Socket和TcpClient 、TcpListener类 2种&#xff0c;对于高级用户&…...

python基础面试题汇总(持续更新),冲击offer

目录 1.概念理解题python内置数据结构&#xff0c;哪些是不可变的python新式类和经典类的区别is和有什么区别Python中变量查找顺序python函数的参数是值传递还是引用传递python垃圾回收机制什么是闭包什么是装饰器&#xff0c;开发中用到举例如何实现只读属性Python中类方法、实…...

Java课题笔记~ AOP编程术语(掌握)

&#xff08;1&#xff09; 切面&#xff08;Aspect&#xff09; 切面泛指交叉业务逻辑。上例中的事务处理、日志处理就可以理解为切面。常用的切面是通知&#xff08;Advice&#xff09;。实际就是对主业务逻辑的一种增强。 &#xff08;2&#xff09; 连接点&#xff08;Jo…...

暑假刷题第23天--8/6

3748. 递增子串 - AcWing题库 #include<iostream> #include<string> const int N200005; int a[N]; using namespace std; int main(){int t;cin>>t;for(int q1;q<t;q){int n;cin>>n;string s;cin>>s;int cnt1;a[1]1;for(int i2;i<n;i){i…...

ArcGIS API for JavaScript 4.x 教程(一) 显示一张地图

了解如何创建和显示带有基本地图图层的地图。 地图包含地理数据层。地图包含一个基本地图层&#xff0c;以及一个或多个数据层&#xff08;可选&#xff09;。可以使用地图视图显示地图的特定区域&#xff0c;并设置位置和缩放级别。 本教程将向您展示如何使用地形底图层创建和…...

Python-OpenCV中的图像处理

Python-OpenCV中的图像处理 颜色空间转换物体跟踪获取HSV的值几何变换图像缩放图像平移图像旋转仿射变换透视变换 图像阈值单阈值自适应阈值Otsus二值化 颜色空间转换 在 OpenCV 中有超过 150 中进行颜色空间转换的方法。但是你以后就会 发现我们经常用到的也就两种&#xff1…...

分清性能测试,负载测试,压力测试这三个的区别

做测试一年多来&#xff0c;虽然平时的工作都能很好的完成&#xff0c;但最近突然发现自己在关于测试的整体知识体系上面的了解很是欠缺&#xff0c;所以&#xff0c;在工作之余也做了一些测试方面的知识的补充。不足之处&#xff0c;还请大家多多交流&#xff0c;互相学习。 …...

前端架构师岗位的工作职责(合集)

前端架构师岗位的工作职责1 职责&#xff1a; 1.制定前端的标准和规范&#xff0c;并推广和应用&#xff0c;提高团队的开发效率; 2.前端架构的框架或核心模块的设计与实现; 3.在前端架构、设计与开发上对团队进行足够的指导; 4.在日常的系统设计与优化上与服务端团队紧密合…...

使用 Amazon ECS Anywhere 在边缘部署 Amazon IoT Greengrass

1.概述 亚马逊云科技提供了完备的IoT服务能力&#xff0c;涵盖设备服务、连接和控制服务以及云端分析服务&#xff0c;是快速构建安全可靠、可扩展的 IoT 平台的常见选择。Amazon IoT Greengrass 边缘运行时和云服务&#xff0c;可帮助您在设备上构建、部署和管理 IoT 应用。A…...

pytorch Stream 多流处理

CUD Stream https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#c-language-extensions 中指出在kenel的调用函数中最后一个可选参数表示该核函数处在哪个流之中。 - 参数Dg用于定义整个grid的维度和尺寸&#xff0c;即一个grid有多少个block。为dim3类型。…...

微信小程序选项卡切换(滑动切换,点击切换)

效果如下&#xff1a;可点击切换&#xff0c;滑动切换 代码如下 这个可以在项目用 index.wxml <view classtopTabSwiper><view classtab {{currentData 0 ? "tabBorer" : ""}} data-current "0" bindtapcheckCurrent>选项一&…...

安路FPGA的赋值报错——移位处理,加括号

authordaisy.skye的博客_CSDN博客-嵌入式,Qt,Linux领域博主 在使用移位符号用来当作除以号使用时&#xff0c;发现如下问题 其中 cnt_8K 为偶数和奇数时输出的数据不一样 reg [10:0] cnt_8K; reg [10:0] ram1_addra; always(posedge clk_16M) begin if(ram_out_flag )begin if(…...

GO学习之 接口(Interface)

GO系列 1、GO学习之Hello World 2、GO学习之入门语法 3、GO学习之切片操作 4、GO学习之 Map 操作 5、GO学习之 结构体 操作 6、GO学习之 通道(Channel) 7、GO学习之 多线程(goroutine) 8、GO学习之 函数(Function) 9、GO学习之 接口(Interface) 文章目录 GO系列前言一、什么是…...

ansible常见模块的运用

ansible常见模块的运用 一&#xff1a;Ansible简介二&#xff1a;ansible 环境安装部署管理端安装 ansibleansible 目录结构配置主机清单配置密钥对验证 三&#xff1a;ansible 命令行模块1&#xff0e;command 模块在远程主机执行命令&#xff0c;不支持管道&#xff0c;重定向…...

合宙Air724UG LuatOS-Air script lib API--patch

patch Table of Contents patch patch.safeJsonDecode(s) (local函数 无法被外部调用) patch 模块功能&#xff1a;Lua补丁 patch.safeJsonDecode(s) (local函数 无法被外部调用) 封装自定义的json.decode接口 参数 名称 传入值类型 释义 s string json格式的字符串 返回值 t…...

pytorch求导

pytorch求导的初步认识 requires_grad tensor(data, dtypeNone, deviceNone, requires_gradFalse)requires_grad是torch.tensor类的一个属性。如果设置为True&#xff0c;它会告诉PyTorch跟踪对该张量的操作&#xff0c;允许在反向传播期间计算梯度。 x.requires_grad 判…...

Java基础异常详解

Java基础异常详解 文章目录 Java基础异常详解编译时异常&#xff08;Checked Exception&#xff09;&#xff1a;运行时异常&#xff08;Unchecked Exception&#xff09;: Java中的异常是用于处理程序运行时出现的错误或异常情况的一种机制。 异常本身也是一个类。 异常分为…...

vue3+vue-i18n 监听语言的切换

最近在用 vue3 做一个后台管理系统&#xff0c;之前是只考虑中文&#xff0c;现在加了个需求是多语言。 本来也不是太难的需求&#xff0c;但是我用的并不熟悉&#xff0c;并且除了页面展示不同的语言&#xff0c;需求是在切换语言的时候在几个页面中需要做出一些自定义的行为&…...

【考研复习】24王道数据结构课后习题代码|2.3线性表的链式表示

文章目录 总结01 递归删除结点02 删除结点03 反向输出04 删除最小值05 逆置06 链表递增排序07 删除区间值08 找公共结点09 增序输出链表10 拆分链表--尾插11 拆分链表--头插12 删除相同元素13 合并链表14 生成含有公共元素的链表C15 求并集16 判断子序列17 判断循环链表是否对称…...

娇滴滴的一朵花(Python实现)

目录 1 娇滴滴的她 2 Python代码实现 1 娇滴滴的她 娇滴滴。双眉敛破春山色。春山色。 为君含笑,为君愁蹙。多情别後无消息。 此时更有谁知得。谁知得。夜深无寐&#xff0c;度江横笛。 2 Python代码实现 import turtle from turtle import * turtle.title(春天送她一朵小花)…...

Android AccessibilityService研究

AccessibilityService流程分析 AccessibilityService开启方式AccessibilityService 开启原理 AccessibilityService开启方式 . 在Framework里直接添加对应用app 服务component。 loadSetting(stmt, Settings.Secure.ACCESSIBILITY_ENABLED,1); loadSetting(stmt, Settings.Se…...

华为OD机试(含B卷)真题2023 算法分类版,58道20个算法分类,如果距离机考时间不多了,就看这个吧,稳稳的

目录 一、数据结构1、线性表2、优先队列3、滑动窗口4、二叉树5、并查集6、栈 二、算法1、基础算法2、字符串3、图4、动态规划5、数学 三、漫画算法2&#xff1a;小灰的算法进阶参与方式 很多小伙伴问我&#xff0c;华为OD机试算法题太多了&#xff0c;知识点繁杂&#xff0c;如…...

JMeter命令行执行+生成HTML报告

1、为什么用命令行模式 使用GUI方式启动jmeter&#xff0c;运行线程较多的测试时&#xff0c;会造成内存和CPU的大量消耗&#xff0c;导致客户机卡死&#xff1b; 所以一般采用的方式是在GUI模式下调整测试脚本&#xff0c;再用命令行模式执行&#xff1b; 命令行方式支持在…...

学习Boost二:从附录3来看编码习惯

附录C 关键字浅谈 在C11标准中&#xff08;C11.2.12&#xff09;总共定义了73个关键字&#xff08;keyword&#xff09;、2个“准”关键字&#xff08;identifiers with special meaning&#xff09;和11个操作符替代字&#xff08;alternative representation&#xff09;[1]。…...

STM32基础入门学习笔记:核心板 电路原理与驱动编程

文章目录&#xff1a; 一&#xff1a;LED灯操作 1.LED灯的点亮和熄灭 延迟闪烁 main.c led.c led.h BitAction枚举 2.LED呼吸灯&#xff08;灯的强弱交替变化&#xff09; main.c delay.c 3.按键控制LED灯 key.h key.c main.c 二&#xff1a;FLASH读写程序(有…...

最后一次模拟考试题解

哦我想这不用看都知道是为了水任务 T1 黑白染色 其实这题有原 什么手写体 md (指 markdown) 分析 首先这题如果你题目没看错的话 ,会发现其实他是 n m n \times m nm 让你求 n n n \times n nn 的区域内的点&#xff08;不会只有我一个人题目看错了罢 然后我们会发现…...

Mac 创建和删除 Automator 工作流程,设置 Terminal 快捷键

1. 创建 Automator 流程 本文以创建一个快捷键启动 Terminal 的自动操作为示例。 点击打开 自动操作&#xff1b; 点击 新建文稿 点击 快速操作 选择 运行 AppleScript 填入以下内容 保存名为 “Open Terminal” 打开 设置 > 键盘&#xff0c;选择 键盘快捷键 以此选择 服…...

2023华为OD机试真题B卷 Java 实现【最长的元音串】

前言 本题使用Java解答,如果需要Python代码,链接 题目 给定一个只由英文字母(a-z, A-Z)组成的字符串,找出其中最长的只包含元音字母(a, e, i, o, u, A, E, I, O, U)的子串,并返回其长度。如果不存在元音子串,则返回0。 输入: 一个由英文字母组成的字符串,长度大…...