文章目录

  • 一、整体结构框图
  • 二、AudioTrack的解析
    • 2.1 AudioTrack API 两种数据传输模式
    • 2.2 AudioTrack API 音频流类型
    • 2.3 getMinBufferSize 函数分析
    • 2.4 AudioTrack 对象创建
    • 2.5 AudioTrack 在JNI 中的使用
    • 2.6 AudioTrack 的play和write
    • 2.7 new AudioTrack 和 set 的调用
  • 三、整体总结

最近公司刚好做了一个关于音视频的培训,但是感觉听完云里雾里,所以自己总结一番,做个笔记。
参考文档: https://www.cnblogs.com/innost/archive/2011/01/09/1931457.htmlhttps://blog.csdn.net/zyuanyun/article/details/60890534?spm=1001.2014.3001.5501#t4

我的代码在于rk3288的平台代码

一、整体结构框图

在这里插入图片描述

这个框图算是很详细的解析了在android的里面的audio的一个整体逻辑,从上层到底层。
1) Audio Application Framework:音频应用框架

  1. AudioTrack:负责回放数据的输出,属 Android 应用框架 API 类
  2. AudioRecord:负责录音数据的采集,属 Android 应用框架 API 类
  3. AudioSystem: 负责音频事务的综合管理,属 Android 应用框架 API 类

2)Audio Native Framework:音频本地框架 (cpp的那三个)

  1. AudioTrack:负责回放数据的输出,属 Android 本地框架 API 类
  2. AudioRecord:负责录音数据的采集,属 Android 本地框架 API 类
  3. AudioSystem: 负责音频事务的综合管理,属 Android 本地框架 API 类

3)Audio Services:音频服务

  1. AudioPolicyService:音频策略的制定者,负责音频设备切换的策略抉择、音量调节策略等
  2. AudioFlinger:音频策略的执行者,负责输入输出流设备的管理及音频流数据的处理传输

4)Audio HAL:音频硬件抽象层,负责与音频硬件设备的交互,由 AudioFlinger 直接调用

代码位置

framework\base\media\java\android\media\AudioTrack.java

二、AudioTrack的解析

从图上可以看到播放声音可以使app调用 MediaPlayer 和 AudioTrack这两种接口,两者都提供 Java API 给应用开发者使用。两者的差别在于:MediaPlayer 可以播放多种格式的音源,如 mp3、flac、wma、ogg、wav 等,而 AudioTrack 只能播放解码后的 PCM 数据流。所以 MediaPlayer 的应用场景更广,一般情况下使用它也更方便;只有一些对声音时延要求非常苛刻的应用场景才需要用到AudioTrack。

2.1 AudioTrack API 两种数据传输模式

正常来说,我们会播放一些音乐,打电话,也有系统自带的铃声这种。
对于系统来说,我们需要寻找一个最佳的方式来播放,这样才能追求效率,所以才有了两种模式。
音频不会经过cpu而是通过dma来传输数据,为了达到效率,我们会有一个buffer,也就是缓冲区域,我播放音乐的时候就会把对应的音频数据写入到这个buffer里面,然后我们的AudioTrack就会去读取这个buffer里面的数据,由此来达到播放的效果。

  1. MODE_STATIC 模式:整个音频文件全部数一起写入buffer。适用于铃声等内存占用较小,延时要求较高的声音。
  2. MODE_STREAM模式: 应用程序通过write方式把数据一次一次得写到buffer里面,然后audiotrack去读取buffer的数据,基本适用所有的音频场景,但是有时候会出现卡顿,阻塞的情况。

2.2 AudioTrack API 音频流类型

在对应的源码目录下有这样的注释。

    /*** Returns the volume stream type of this AudioTrack.* Compare the result against {@link AudioManager#STREAM_VOICE_CALL},* {@link AudioManager#STREAM_SYSTEM}, {@link AudioManager#STREAM_RING},* {@link AudioManager#STREAM_MUSIC}, {@link AudioManager#STREAM_ALARM},* {@link AudioManager#STREAM_NOTIFICATION}, {@link AudioManager#STREAM_DTMF} or* {@link AudioManager#STREAM_ACCESSIBILITY}.*/public int getStreamType() {return mStreamType;

这些铃声都已经定义好了,

  1. STREAM_VOICE_CALL 电话语音
  2. STREAM_SYSTEM 系统声音
  3. STREAM_RING 铃声声音,如来电铃声、闹钟铃声等
  4. STREAM_MUSIC 音乐声音
  5. STREAM_ALARM 警告音
  6. STREAM_NOTIFICATION 通知音
  7. STREAM_DTMF DTMF 音(拨号盘按键音)

安卓之所以要定义这么多音频流类型主要是为了方便的音频管理策略。
你关闭媒体音量,不会影响到你的通话音量,代耳机和不带耳机的音量,还有其余的不相关的音量。还有就是比如插着有线耳机期间,音乐声(STREAM_MUSIC)只会输出到有线耳机,而铃声(STREAM_RING)会同时输出到有线耳机和外放。就是方便管理。

2.3 getMinBufferSize 函数分析

这个函数和最小要求的缓冲区大小相关,所以我们先分析这个函数

 /*** Returns the estimated minimum buffer size required for an AudioTrack* object to be created in the {@link #MODE_STREAM} mode.* The size is an estimate because it does not consider either the route or the sink,* since neither is known yet.  Note that this size doesn't* guarantee a smooth playback under load, and higher values should be chosen according to* the expected frequency at which the buffer will be refilled with additional data to play.* For example, if you intend to dynamically set the source sample rate of an AudioTrack* to a higher value than the initial source sample rate, be sure to configure the buffer size* based on the highest planned sample rate.* @param sampleRateInHz the source sample rate expressed in Hz.*   {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED} is not permitted.* @param channelConfig describes the configuration of the audio channels.*   See {@link AudioFormat#CHANNEL_OUT_MONO} and*   {@link AudioFormat#CHANNEL_OUT_STEREO}* @param audioFormat the format in which the audio data is represented.*   See {@link AudioFormat#ENCODING_PCM_16BIT} and*   {@link AudioFormat#ENCODING_PCM_8BIT},*   and {@link AudioFormat#ENCODING_PCM_FLOAT}.* @return {@link #ERROR_BAD_VALUE} if an invalid parameter was passed,*   or {@link #ERROR} if unable to query for output properties,*   or the minimum buffer size expressed in bytes.*//**对应的源码已经有了注释:md,英语真的重要*/static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {int channelCount = 0;/*这里就是声道选择,单声道或则双声道*/switch(channelConfig) {case AudioFormat.CHANNEL_OUT_MONO:case AudioFormat.CHANNEL_CONFIGURATION_MONO:channelCount = 1;break;case AudioFormat.CHANNEL_OUT_STEREO:case AudioFormat.CHANNEL_CONFIGURATION_STEREO:channelCount = 2;break;default:if (!isMultichannelConfigSupported(channelConfig)) {loge("getMinBufferSize(): Invalid channel configuration.");return ERROR_BAD_VALUE;} else {channelCount = AudioFormat.channelCountFromOutChannelMask(channelConfig);}}if (!AudioFormat.isPublicEncoding(audioFormat)) {loge("getMinBufferSize(): Invalid audio format.");return ERROR_BAD_VALUE;}// sample rate, note these values are subject to change// Note: AudioFormat.SAMPLE_RATE_UNSPECIFIED is not allowed/*就是采样率,人耳朵在20hz到20khz之间。*采样率支持:4KHz~192KHz好像可以找到设定的值*/if ( (sampleRateInHz < AudioFormat.SAMPLE_RATE_HZ_MIN) ||(sampleRateInHz > AudioFormat.SAMPLE_RATE_HZ_MAX) ) {loge("getMinBufferSize(): " + sampleRateInHz + " Hz is not a supported sample rate.");return ERROR_BAD_VALUE;}/*frameworks/base/core/jni/android_media_AudioTrack.cpp中 android_media_AudioTrack_get_min_buff_size这个函数来实现的*/int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat);if (size <= 0) {loge("getMinBufferSize(): error querying hardware");return ERROR;}else {return size;}}

好像有的源码这里还有pcm的写法,这里只有注释有说明,目前只有8bit和16bit两种模式支持。
android_media_AudioTrack.cpp对应的代码

static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env,  jobject thiz,jint sampleRateInHertz, jint channelCount, jint audioFormat) {size_t frameCount;/*这个函数用于确定至少设置多少个 frame 才能保证声音正常播放,也就是最低帧数*/const status_t status = AudioTrack::getMinFrameCount(&frameCount, AUDIO_STREAM_DEFAULT,sampleRateInHertz);if (status != NO_ERROR) {ALOGE("AudioTrack::getMinFrameCount() for sample rate %d failed with status %d",sampleRateInHertz, status);return -1;}const audio_format_t format = audioFormatToNative(audioFormat);if (audio_has_proportional_frames(format)) {const size_t bytesPerSample = audio_bytes_per_sample(format);/*PCM 数据最小缓冲区大小*最小缓冲区的大小 = 最低帧数 * 声道数 * 采样深度,(采样深度以字节为单位)*/return frameCount * channelCount * bytesPerSample;} else {return frameCount;}
}

什么是frame:
frame就是帧,也就是1个采样点的字节数*声道。为啥搞个frame出来?因为对于多声道的话,用1个采样点的字节数表示不全,因为播放的时候肯定是多个声道的数据都要播出来。所以为了方便,就说1秒钟有多少个frame,这样就能抛开声道数,把意思表示全了。

2.4 AudioTrack 对象创建

建立对象函数:

public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,int bufferSizeInBytes, int mode)
throws IllegalArgumentException {this(streamType, sampleRateInHz, channelConfig, audioFormat,bufferSizeInBytes, mode, AudioManager.AUDIO_SESSION_ID_GENERATE);
}
.....
audioBuffSizeCheck(bufferSizeInBytes);
/*在这里用到了刚才的BufferSize*/
.....
int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes,sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat,mNativeBufferSizeInBytes, mDataLoadMode, session, 0 /*nativeTrackInJavaObj*/,offload);
.......            

主要由这些函数完成,实在是读不懂这个代码。。
又回到android_media_AudioTrack.cpp这个代码中

static jint
android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, jobject jaa,jintArray jSampleRate, jint channelPositionMask, jint channelIndexMask,jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession,jlong nativeAudioTrack, jboolean offload) {ALOGV("sampleRates=%p, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d,"" nativeAudioTrack=0x%" PRIX64 ", offload=%d",jSampleRate, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes,nativeAudioTrack, offload);sp<AudioTrack> lpTrack = 0;if (jSession == NULL) {ALOGE("Error creating AudioTrack: invalid session ID pointer");return (jint) AUDIO_JAVA_ERROR;}jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);if (nSession == NULL) {ALOGE("Error creating AudioTrack: Error retrieving session id pointer");return (jint) AUDIO_JAVA_ERROR;}audio_session_t sessionId = (audio_session_t) nSession[0];env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);nSession = NULL;AudioTrackJniStorage* lpJniStorage = NULL;jclass clazz = env->GetObjectClass(thiz);if (clazz == NULL) {ALOGE("Can't find %s when setting up callback.", kClassPathName);return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;}// if we pass in an existing *Native* AudioTrack, we don't need to create/initialize one.if (nativeAudioTrack == 0) {if (jaa == 0) {ALOGE("Error creating AudioTrack: invalid audio attributes");return (jint) AUDIO_JAVA_ERROR;}if (jSampleRate == 0) {ALOGE("Error creating AudioTrack: invalid sample rates");return (jint) AUDIO_JAVA_ERROR;}int* sampleRates = env->GetIntArrayElements(jSampleRate, NULL);int sampleRateInHertz = sampleRates[0];env->ReleaseIntArrayElements(jSampleRate, sampleRates, JNI_ABORT);// Invalid channel representations are caught by !audio_is_output_channel() below.audio_channel_mask_t nativeChannelMask = nativeChannelMaskFromJavaChannelMasks(channelPositionMask, channelIndexMask);if (!audio_is_output_channel(nativeChannelMask)) {ALOGE("Error creating AudioTrack: invalid native channel mask %#x.", nativeChannelMask);return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK;}uint32_t channelCount = audio_channel_count_from_out_mask(nativeChannelMask);// check the format.// This function was called from Java, so we compare the format against the Java constantsaudio_format_t format = audioFormatToNative(audioFormat);if (format == AUDIO_FORMAT_INVALID) {ALOGE("Error creating AudioTrack: unsupported audio format %d.", audioFormat);return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT;}// compute the frame countsize_t frameCount;if (audio_has_proportional_frames(format)) {const size_t bytesPerSample = audio_bytes_per_sample(format);frameCount = buffSizeInBytes / (channelCount * bytesPerSample);} else {frameCount = buffSizeInBytes;}// create the native AudioTrack objectlpTrack = new AudioTrack();// read the AudioAttributes valuesauto paa = JNIAudioAttributeHelper::makeUnique();jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get());if (jStatus != (jint)AUDIO_JAVA_SUCCESS) {return jStatus;}ALOGV("AudioTrack_setup for usage=%d content=%d flags=0x%#x tags=%s",paa->usage, paa->content_type, paa->flags, paa->tags);// initialize the callback information:// this data will be passed with every AudioTrack callbacklpJniStorage = new AudioTrackJniStorage();lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);// we use a weak reference so the AudioTrack object can be garbage collected.lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);lpJniStorage->mCallbackData.isOffload = offload;lpJniStorage->mCallbackData.busy = false;audio_offload_info_t offloadInfo;if (offload == JNI_TRUE) {offloadInfo = AUDIO_INFO_INITIALIZER;offloadInfo.format = format;offloadInfo.sample_rate = sampleRateInHertz;offloadInfo.channel_mask = nativeChannelMask;offloadInfo.has_video = false;offloadInfo.stream_type = AUDIO_STREAM_MUSIC; //required for offload}// initialize the native AudioTrack objectstatus_t status = NO_ERROR;switch (memoryMode) {case MODE_STREAM:status = lpTrack->set(AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)sampleRateInHertz,format,// word length, PCMnativeChannelMask,offload ? 0 : frameCount,offload ? AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD : AUDIO_OUTPUT_FLAG_NONE,audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack0,// shared memtrue,// thread can call JavasessionId,// audio session IDoffload ? AudioTrack::TRANSFER_SYNC_NOTIF_CALLBACK : AudioTrack::TRANSFER_SYNC,offload ? &offloadInfo : NULL,-1, -1,                       // default uid, pid valuespaa.get());break;case MODE_STATIC:// AudioTrack is using shared memoryif (!lpJniStorage->allocSharedMem(buffSizeInBytes)) {ALOGE("Error creating AudioTrack in static mode: error creating mem heap base");goto native_init_failure;}status = lpTrack->set(AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)sampleRateInHertz,format,// word length, PCMnativeChannelMask,frameCount,AUDIO_OUTPUT_FLAG_NONE,audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user));0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTracklpJniStorage->mMemBase,// shared memtrue,// thread can call JavasessionId,// audio session IDAudioTrack::TRANSFER_SHARED,NULL,                         // default offloadInfo-1, -1,                       // default uid, pid valuespaa.get());break;default:ALOGE("Unknown mode %d", memoryMode);goto native_init_failure;}if (status != NO_ERROR) {ALOGE("Error %d initializing AudioTrack", status);goto native_init_failure;}} else {  // end if (nativeAudioTrack == 0)lpTrack = (AudioTrack*)nativeAudioTrack;// TODO: We need to find out which members of the Java AudioTrack might// need to be initialized from the Native AudioTrack// these are directly returned from getters://  mSampleRate//  mAudioFormat//  mStreamType//  mChannelConfiguration//  mChannelCount//  mState (?)//  mPlayState (?)// these may be used internally (Java AudioTrack.audioParamCheck()://  mChannelMask//  mChannelIndexMask//  mDataLoadMode// initialize the callback information:// this data will be passed with every AudioTrack callbacklpJniStorage = new AudioTrackJniStorage();lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);// we use a weak reference so the AudioTrack object can be garbage collected.lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);lpJniStorage->mCallbackData.busy = false;}nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);if (nSession == NULL) {ALOGE("Error creating AudioTrack: Error retrieving session id pointer");goto native_init_failure;}// read the audio session ID back from AudioTrack in case we create a new sessionnSession[0] = lpTrack->getSessionId();env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);nSession = NULL;{const jint elements[1] = { (jint) lpTrack->getSampleRate() };env->SetIntArrayRegion(jSampleRate, 0, 1, elements);}{   // scope for the lockMutex::Autolock l(sLock);sAudioTrackCallBackCookies.add(&lpJniStorage->mCallbackData);}// save our newly created C++ AudioTrack in the "nativeTrackInJavaObj" field// of the Java object (in mNativeTrackInJavaObj)setAudioTrack(env, thiz, lpTrack);// save the JNI resources so we can free them later//ALOGV("storing lpJniStorage: %x\n", (long)lpJniStorage);env->SetLongField(thiz, javaAudioTrackFields.jniData, (jlong)lpJniStorage);// since we had audio attributes, the stream type was derived from them during the// creation of the native AudioTrack: push the same value to the Java objectenv->SetIntField(thiz, javaAudioTrackFields.fieldStreamType, (jint) lpTrack->streamType());return (jint) AUDIO_JAVA_SUCCESS;// failures:
native_init_failure:if (nSession != NULL) {env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);}env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_class);env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_ref);delete lpJniStorage;env->SetLongField(thiz, javaAudioTrackFields.jniData, 0);// lpTrack goes out of scope, so reference count drops to zeroreturn (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
}

我已经不想去读源码了,超纲了,以后在复读先记录。有想法的同学可以去看看我贴的参考网址。

2.5 AudioTrack 在JNI 中的使用

在android_media_AudioTrack.cpp代码中
AudioTrack在JNI主要实现的是一个内存共享的机制,对应的源码如下:

class AudioTrackJniStorage {public:sp<MemoryHeapBase>         mMemHeap;sp<MemoryBase>             mMemBase;audiotrack_callback_cookie mCallbackData;sp<JNIDeviceCallback>      mDeviceCallback;AudioTrackJniStorage() {mCallbackData.audioTrack_class = 0;mCallbackData.audioTrack_ref = 0;mCallbackData.isOffload = false;}~AudioTrackJniStorage() {mMemBase.clear();mMemHeap.clear();}
/*这里就是把mMemHeap传到mMemBase中去*/bool allocSharedMem(int sizeInBytes) {mMemHeap = new MemoryHeapBase(sizeInBytes, 0, "AudioTrack Heap Base");if (mMemHeap->getHeapID() < 0) {return false;}mMemBase = new MemoryBase(mMemHeap, 0, sizeInBytes);return true;}
};

2.6 AudioTrack 的play和write

之前的逻辑和共享内存大致都已经说了,最后就是上层调用读写接口去读写内容
首先我们看读的代码:

	static void
android_media_AudioTrack_start(JNIEnv *env, jobject thiz)
{sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);if (lpTrack == NULL) {jniThrowException(env, "java/lang/IllegalStateException","Unable to retrieve AudioTrack pointer for start()");return;}lpTrack->start();
}

再看写的代码:

template <typename T>
static jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const T *data,jint offsetInSamples, jint sizeInSamples, bool blocking) {// give the data to the native AudioTrack object (the data starts at the offset)ssize_t written = 0;// regular write() or copy the data to the AudioTrack's shared memory?size_t sizeInBytes = sizeInSamples * sizeof(T);if (track->sharedBuffer() == 0) {//共享内存written = track->write(data + offsetInSamples, sizeInBytes, blocking);// for compatibility with earlier behavior of write(), return 0 in this caseif (written == (ssize_t) WOULD_BLOCK) {written = 0;}} else {// writing to shared memory, check for capacityif ((size_t)sizeInBytes > track->sharedBuffer()->size()) {sizeInBytes = track->sharedBuffer()->size();}//就直接把数据拷贝到共享内存里,STATIC模式memcpy(track->sharedBuffer()->pointer(), data + offsetInSamples, sizeInBytes);written = sizeInBytes;}if (written >= 0) {return written / sizeof(T);}//返回值return interpretWriteSizeError(written);
}
......
//返回值的函数
static inline
jint interpretWriteSizeError(ssize_t writeSize) {if (writeSize == WOULD_BLOCK) {return (jint)0;} else if (writeSize == NO_INIT) {return AUDIO_JAVA_DEAD_OBJECT;} else {ALOGE("Error %zd during AudioTrack native read", writeSize);return nativeToJavaStatus(writeSize);}
}

2.7 new AudioTrack 和 set 的调用

先看jni层的set函数

  status_t status = NO_ERROR;switch (memoryMode) {case MODE_STREAM:status = lpTrack->set(AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)sampleRateInHertz,format,// word length, PCMnativeChannelMask,offload ? 0 : frameCount,offload ? AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD : AUDIO_OUTPUT_FLAG_NONE,audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack0,// shared memtrue,// thread can call JavasessionId,// audio session IDoffload ? AudioTrack::TRANSFER_SYNC_NOTIF_CALLBACK : AudioTrack::TRANSFER_SYNC,offload ? &offloadInfo : NULL,-1, -1,                       // default uid, pid valuespaa.get());break;

再看audio track中 set函数:这个代码太长了。

status_t AudioTrack::set(audio_stream_type_t streamType,uint32_t sampleRate,audio_format_t format,audio_channel_mask_t channelMask,size_t frameCount,audio_output_flags_t flags,callback_t cbf,void* user,int32_t notificationFrames,const sp<IMemory>& sharedBuffer,bool threadCanCallJava,audio_session_t sessionId,transfer_type transferType,const audio_offload_info_t *offloadInfo,uid_t uid,pid_t pid,const audio_attributes_t* pAttributes,bool doNotReconnect,float maxRequiredSpeed,audio_port_handle_t selectedDeviceId){
......
}

这里就是创造track函数的地方

status_t AudioTrack::createTrack_l()
{status_t status;bool callbackAdded = false;const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();if (audioFlinger == 0) {ALOGE("%s(%d): Could not get audioflinger",__func__, mPortId);status = NO_INIT;goto exit;}{// mFlags (not mOrigFlags) is modified depending on whether fast request is accepted.// After fast request is denied, we will request again if IAudioTrack is re-created.// Client can only express a preference for FAST.  Server will perform additional tests.if (mFlags & AUDIO_OUTPUT_FLAG_FAST) {// either of these use cases:// use case 1: shared bufferbool sharedBuffer = mSharedBuffer != 0;bool transferAllowed =// use case 2: callback transfer mode(mTransfer == TRANSFER_CALLBACK) ||// use case 3: obtain/release mode(mTransfer == TRANSFER_OBTAIN) ||// use case 4: synchronous write((mTransfer == TRANSFER_SYNC || mTransfer == TRANSFER_SYNC_NOTIF_CALLBACK)&& mThreadCanCallJava);bool fastAllowed = sharedBuffer || transferAllowed;if (!fastAllowed) {ALOGW("%s(%d): AUDIO_OUTPUT_FLAG_FAST denied by client,"" not shared buffer and transfer = %s",__func__, mPortId,convertTransferToText(mTransfer));mFlags = (audio_output_flags_t) (mFlags & ~AUDIO_OUTPUT_FLAG_FAST);}}IAudioFlinger::CreateTrackInput input;if (mStreamType != AUDIO_STREAM_DEFAULT) {input.attr = AudioSystem::streamTypeToAttributes(mStreamType);} else {input.attr = mAttributes;}input.config = AUDIO_CONFIG_INITIALIZER;input.config.sample_rate = mSampleRate;input.config.channel_mask = mChannelMask;input.config.format = mFormat;input.config.offload_info = mOffloadInfoCopy;input.clientInfo.clientUid = mClientUid;input.clientInfo.clientPid = mClientPid;input.clientInfo.clientTid = -1;if (mFlags & AUDIO_OUTPUT_FLAG_FAST) {// It is currently meaningless to request SCHED_FIFO for a Java thread.  Even if the// application-level code follows all non-blocking design rules, the language runtime// doesn't also follow those rules, so the thread will not benefit overall.if (mAudioTrackThread != 0 && !mThreadCanCallJava) {input.clientInfo.clientTid = mAudioTrackThread->getTid();}}input.sharedBuffer = mSharedBuffer;input.notificationsPerBuffer = mNotificationsPerBufferReq;input.speed = 1.0;if (audio_has_proportional_frames(mFormat) && mSharedBuffer == 0 &&(mFlags & AUDIO_OUTPUT_FLAG_FAST) == 0) {input.speed  = !isPurePcmData_l() || isOffloadedOrDirect_l() ? 1.0f :max(mMaxRequiredSpeed, mPlaybackRate.mSpeed);}input.flags = mFlags;input.frameCount = mReqFrameCount;input.notificationFrameCount = mNotificationFramesReq;input.selectedDeviceId = mSelectedDeviceId;input.sessionId = mSessionId;IAudioFlinger::CreateTrackOutput output;sp<IAudioTrack> track = audioFlinger->createTrack(input,output,&status);if (status != NO_ERROR || output.outputId == AUDIO_IO_HANDLE_NONE) {ALOGE("%s(%d): AudioFlinger could not create track, status: %d output %d",__func__, mPortId, status, output.outputId);if (status == NO_ERROR) {status = NO_INIT;}goto exit;}ALOG_ASSERT(track != 0);mFrameCount = output.frameCount;mNotificationFramesAct = (uint32_t)output.notificationFrameCount;mRoutedDeviceId = output.selectedDeviceId;mSessionId = output.sessionId;mSampleRate = output.sampleRate;if (mOriginalSampleRate == 0) {mOriginalSampleRate = mSampleRate;}mAfFrameCount = output.afFrameCount;mAfSampleRate = output.afSampleRate;mAfLatency = output.afLatencyMs;mLatency = mAfLatency + (1000LL * mFrameCount) / mSampleRate;// AudioFlinger now owns the reference to the I/O handle,// so we are no longer responsible for releasing it.// FIXME compare to AudioRecord//读写的地方sp<IMemory> iMem = track->getCblk();if (iMem == 0) {ALOGE("%s(%d): Could not get control block", __func__, mPortId);status = NO_INIT;goto exit;}void *iMemPointer = iMem->pointer();if (iMemPointer == NULL) {ALOGE("%s(%d): Could not get control block pointer", __func__, mPortId);status = NO_INIT;goto exit;}// invariant that mAudioTrack != 0 is true only after set() returns successfullyif (mAudioTrack != 0) {IInterface::asBinder(mAudioTrack)->unlinkToDeath(mDeathNotifier, this);mDeathNotifier.clear();}mAudioTrack = track;mCblkMemory = iMem;IPCThreadState::self()->flushCommands();audio_track_cblk_t* cblk = static_cast<audio_track_cblk_t*>(iMemPointer);mCblk = cblk;mAwaitBoost = false;if (mFlags & AUDIO_OUTPUT_FLAG_FAST) {if (output.flags & AUDIO_OUTPUT_FLAG_FAST) {ALOGI("%s(%d): AUDIO_OUTPUT_FLAG_FAST successful; frameCount %zu -> %zu",__func__, mPortId, mReqFrameCount, mFrameCount);if (!mThreadCanCallJava) {mAwaitBoost = true;}} else {ALOGW("%s(%d): AUDIO_OUTPUT_FLAG_FAST denied by server; frameCount %zu -> %zu",__func__, mPortId, mReqFrameCount, mFrameCount);}}mFlags = output.flags;//mOutput != output includes the case where mOutput == AUDIO_IO_HANDLE_NONE for first creationif (mDeviceCallback != 0) {if (mOutput != AUDIO_IO_HANDLE_NONE) {AudioSystem::removeAudioDeviceCallback(this, mOutput, mPortId);}AudioSystem::addAudioDeviceCallback(this, output.outputId, output.portId);callbackAdded = true;}mPortId = output.portId;// We retain a copy of the I/O handle, but don't own the referencemOutput = output.outputId;mRefreshRemaining = true;// Starting address of buffers in shared memory.  If there is a shared buffer, buffers// is the value of pointer() for the shared buffer, otherwise buffers points// immediately after the control block.  This address is for the mapping within client// address space.  AudioFlinger::TrackBase::mBuffer is for the server address space.void* buffers;if (mSharedBuffer == 0) {buffers = cblk + 1;} else {buffers = mSharedBuffer->pointer();if (buffers == NULL) {ALOGE("%s(%d): Could not get buffer pointer", __func__, mPortId);status = NO_INIT;goto exit;}}mAudioTrack->attachAuxEffect(mAuxEffectId);// If IAudioTrack is re-created, don't let the requested frameCount// decrease.  This can confuse clients that cache frameCount().if (mFrameCount > mReqFrameCount) {mReqFrameCount = mFrameCount;}// reset server position to 0 as we have new cblk.mServer = 0;// update proxyif (mSharedBuffer == 0) {mStaticProxy.clear();mProxy = new AudioTrackClientProxy(cblk, buffers, mFrameCount, mFrameSize);} else {mStaticProxy = new StaticAudioTrackClientProxy(cblk, buffers, mFrameCount, mFrameSize);mProxy = mStaticProxy;}mProxy->setVolumeLR(gain_minifloat_pack(gain_from_float(mVolume[AUDIO_INTERLEAVE_LEFT]),gain_from_float(mVolume[AUDIO_INTERLEAVE_RIGHT])));mProxy->setSendLevel(mSendLevel);const uint32_t effectiveSampleRate = adjustSampleRate(mSampleRate, mPlaybackRate.mPitch);const float effectiveSpeed = adjustSpeed(mPlaybackRate.mSpeed, mPlaybackRate.mPitch);const float effectivePitch = adjustPitch(mPlaybackRate.mPitch);mProxy->setSampleRate(effectiveSampleRate);AudioPlaybackRate playbackRateTemp = mPlaybackRate;playbackRateTemp.mSpeed = effectiveSpeed;playbackRateTemp.mPitch = effectivePitch;mProxy->setPlaybackRate(playbackRateTemp);mProxy->setMinimum(mNotificationFramesAct);mDeathNotifier = new DeathNotifier(this);IInterface::asBinder(mAudioTrack)->linkToDeath(mDeathNotifier, this);}exit:if (status != NO_ERROR && callbackAdded) {// note: mOutput is always valid is callbackAdded is trueAudioSystem::removeAudioDeviceCallback(this, mOutput, mPortId);}mStatus = status;// sp<IAudioTrack> track destructor will cause releaseOutput() to be called by AudioFlingerreturn status;
}

三、整体总结

之前没有玩过audio的东西,也没有学过java和c++,现在看这些真的头痛,虽然大致理了下思路,但是还是觉得没有入门,有空还得多看看博客和源码,还需要去找赵对应的api文档,后面继续根新把,上面说的也不一定是对的,毕竟代码总在进步。
整理下之前代码的整体逻辑:

1、首先就是就是计算buffer的大小,和采样率,声道数这些有关。
2、当Buffer计算好了过后,AudioTrack 在JNI就会有一个逻辑去实现共享内存,比如服务端先分配两个BnMemoryHeapBase和BnMemoryBase,然后通过BnMemoryBase把数据传到代理端,通过机制,代理端就可以读取BnMemoryBase的数据。
3、共享内存实现过后,就是上层读写数据实现播放声音这些。

当然这些里面还有一些深奥的东西,比如进程间的同步,共享内存怎么能保证不被破坏,如何实现的,audioTrack和audioFlinger之间的交互,等等,这些都是谜团,还有很多东西要去解析呢。

查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. VTK系列教程二:绘制流程

    前一篇文章我们简单介绍了VTK&#xff0c;从这篇文章开始&#xff0c;我们以一个3D医疗影像后处理的例子来逐步深入理解VTK。 在VTK的渲染流程中&#xff0c;主要涉及到”物体”、“相机”、“过滤器”、“视网膜”、“用户交互”这几个主要角色&#xff0c;下面分别进行分析。…...

    2024/5/6 23:25:42
  2. Cygwin64 Terminal 加入到鼠标右键中

    方法 windows 10 64位管理员方式打开【注册表】regedit 添加注册表项 计算机\HKEY_CLASSES_ROOT\Directory\Background\shell\Cygwin64 Terminal\command 图标可以直接使用程序默认的&#xff0c;icon值为&#xff1a; C:\cygwin64\bin\mintty.exe 执行的程序 C:\cygwin64…...

    2024/4/14 16:55:59
  3. Eclipse 的坑

    1. Eclipse 创建Maven项目 尽量在Eclipse上创建Maven项目前完成如下的环境部署&#xff0c;否则会出现各种奇葩问题&#xff0c;报错举例见下方详细内容。完成配置后&#xff0c;这些问题就可以解决。需要配置的是 Maven环境、Java的 JRE 环境、Web服务器&#xff08;通常是 To…...

    2024/5/6 22:00:38
  4. GitHub Copilot插件 - AI程序员

    Copilot 是一个AI代码块程序员&#xff0c;它会根据你输入的提示&#xff0c;自动给你提供可选的代码块。该插件目前还在内测中&#xff0c;需要申请才能使用。 申请使用Copilot插件的步骤&#xff1a; 第一步&#xff1a;vscode 左下角登录GitHub账号&#xff1a; 第二步&am…...

    2024/4/18 14:28:28
  5. JavaScript的数组 (array / arr)操作

    目录 一、数组的定义 1.1 字面量语法形式 1.2 构造函数定义形式 二、数组的基本操作 2.1 调用 2.1修改 2.3 新增 三、数组的循环遍历 3.1 for...in 3.2 for...of 3.3 forEach 四、数组ES5的函数操作语法 4.1 数组的映射 4.2 数组的过滤 4.3 数组的判断 五、数…...

    2024/5/6 21:44:08
  6. 线程池源码分析_01 FutureTask源码分析

    文章参考&#xff1a;硬核手撕Java线程池FutureTask源码 1、FutureTask简介 Future 是我们在使用 JAVA 异步的时候最常用到的一个类&#xff0c;我们可以向线程池提交一个 Callable 对象&#xff0c;并通过 Future 对象获取执行结果。 FutureTask 的使用场景&#xff1a;Futu…...

    2024/4/13 23:13:42
  7. 中国抗帕金森病药物行业研究及战略研究报告(2022版)

    内容介绍&#xff1a; 在全球500强畅销药物市场中&#xff0c;2017年全球抗帕金森治疗市场为20.56亿美元&#xff0c;同比上一年增长了3.73%&#xff1b;2018年全球抗帕金森治疗市场为21.73亿美元&#xff0c;同比上一年增长了5.71%&#xff1b;2019年全球抗帕金森治疗市场为2…...

    2024/4/7 18:42:25
  8. 计算机系统基础知识

    计算机系统基础基本知识基本介绍数据的存储、表示和操作汇编语言基础符号、链接与ELF基本介绍 计算机系统基础主要是一门针对于计算机底层汇编如何实现的相关知识。想入门计算机类专业的同学可以先从这门课入手&#xff0c;虽然比较难&#xff0c;但他主要针对的是计算机底层&…...

    2024/4/13 23:13:57
  9. 项目如何实现热部署

    文章目录添加依赖IDEA配置添加依赖 在pom.xml中添加&#xff1a; <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId></dependency>IDEA配置 在setting中找到Compiler勾选Build pr…...

    2024/4/13 23:14:02
  10. Visual Studio 2019 使用技巧

    一、下载和安装 链接&#xff1a;https://pan.baidu.com/s/19eZZVf4XRoA9oJ_QCUBBYw 提取码&#xff1a;2lgh 安装根据需要添加所需的资源。 二、使用技巧&#xff08;持续更新&#xff09; 1.快捷键 Ctrl M O: 折叠所有方法 Ctrl M M: 折叠或者展开当前方法 Ctrl…...

    2024/4/13 23:13:52
  11. 算法设计与分析 暴力递归

    暴力递归概述题目一&#xff1a;汉诺塔问题题目二&#xff1a;字符串的全部子序列问题题目三&#xff1a;字符串的全排列问题&#xff08;分支限界&#xff09;题目四&#xff1a;拿纸牌比最大问题题目五&#xff1a;递归逆序栈题目六&#xff1a;数字与字符串的转化问题题目七…...

    2024/4/15 15:22:55
  12. Pytorch、CUDA、cuDNN、英伟达950M

    Pytorch、CUDA、cuDNN、英伟达950M配置CUDA1、查询电脑英伟达版本2、CUDA安装3、测试CUDA是否安装成功cuDNN1、找适应版本的cuDNN2、cuDNN的安装Pytorch1、适配CUDA和cuDNN版本的Pytorch2、安装3、wheel的安装备注对于CUDA的安装可参考下面的链接&#xff1a; 链接1: https://w…...

    2024/4/13 23:13:57
  13. Backpropagation,Tips for Deep Learning小结

    一、chain rule 二、Backpropagation 三、Tips for Deep Learning...

    2024/4/19 23:00:42
  14. Android NDK:在Android Studio下的基本开发步骤和基础知识点总结

    -d 输出目录&#xff0c;jni是gradle默认的路径 -classpath jar的路径&#xff0c;有时碰到的找不到Activity的类的错误可能是由这个引起的 com.zhuanghongji.ndkdemo.JNITest 包名类名 执行上述命令发现在main目录下多了一个jni文件夹&#xff0c;而且里面有生成好的头文件…...

    2024/4/20 0:07:46
  15. python正则表达式——加号

    在正则表达式中&#xff0c;加号表示匹配前面的子表达式一次或多次&#xff0c;不包括0次。 比如选择每行逗号后面的字符串内容&#xff0c;包括逗号本身&#xff0c;如果逗号后面没有内容&#xff0c;则不选择。 content 苹果&#xff0c;是绿色的 橙子&#xff0c;是橙色的…...

    2024/4/19 14:28:01
  16. 算法训练——剑指offer(链表问题)

    摘要 本系列博文将主要是学习和分享的算法基本相关问题和解答思路&#xff0c;帮助小伙伴更好理解相关的算法中有关于链表的内容。 一、链表原理与解题方法 二、链表相关算法练习题目 从尾到头打印链表_牛客题霸_牛客网 解题思路&#xff1a; //一种是将数据放入栈中 &…...

    2024/4/13 23:13:37
  17. 【蓝桥杯】计数法独立按键,拒绝延时

    前言&#xff1a; 延时处理独立按键虽然可以取得我们想要的结果&#xff0c;但是延时10ms对整个代码伤害很大。10ms足以让我们的while多跑好多次了&#xff0c;为了避免你CPU的浪费这里有一种大多数人都在使用的计数法 理解&#xff1a; 我们设置一个静态变量count,每隔10毫秒检…...

    2024/4/19 12:18:14
  18. 【OC/Swift混编】接口中数据类型的建议(三):类型转换底层实现原理

    本文目录一、前言二、OC到Swift类型转换底层实现结论三、Swift到OC类型转换底层实现结论四、工欲善其事&#xff0c;必先利其器五、分析NSDictionary转换为[String:Any]六、分析NSMutableDictionary到Dictionary\一、前言 本文通过反汇编伪代码Swift源码Swift的Foundation源码…...

    2024/4/18 17:26:20
  19. Go语法复习 2

    1.if语句 在go语言中&#xff0c;有一种特殊的if语句用法。 if statement&#xff0c;condition{ } 实例&#xff1a; package mainimport "fmt"func main() {//此处num为局部变量if num : 4; num > 0 {fmt.Printf("number%d\n", num)}//此处为全局变…...

    2024/4/7 18:42:18
  20. 【Warrior刷题笔记】1765.地图中的最高点 【多源广度优先遍历】详细注释简单易懂

    题目 LC1765.地图中的最高点 解题思路 本题可以使用多源广度优先遍历解决。 题目要求得到高度值最大的高度安排方案&#xff0c;限制条件是相邻格子之间高度差最大为1&#xff0c;同时水域的高度限定为了0。因此我们可以从所有水域开始&#xff0c;与水域相邻的位置高度设置…...

    2024/4/20 10:37:14

最新文章

  1. 自动驾驶主流芯片及平台架构(二)特斯拉自动驾驶芯片平台介绍

    早期 对外采购mobileye EyeQ3 芯片摄像头半集成方案&#xff0c;主要是为了满足快速量产需求&#xff0c;且受制于研发资金不足限制&#xff1b; 中期 采用高算力NVIDIA 芯片平台其他摄像头供应商的特斯拉内部集成方案&#xff0c;mobileye开发节奏无法紧跟特斯拉需求&#xff…...

    2024/5/7 4:35:44
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/5/6 9:38:23
  3. SpringMVC初始化工程

    SpringMVC初始化工程 本文采用maven作为构建工具,SpringMVC作为主框架。 创建一个maven的web工程,并配置pom文件<!-- pom.xml --> <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0&qu…...

    2024/5/5 20:56:45
  4. 巨控科技新品发布:全方位升级,引领智能控制新纪元

    标签: #巨控科技 #智能控制 #新品发布 #GRM560 #OPC560 #NET400 在智能控制领域&#xff0c;巨控科技始终以其前沿技术和创新产品引领着市场的潮流。近日&#xff0c;巨控科技再次以其行业领先的研发实力&#xff0c;推出了三大系列的新产品&#xff0c;旨在为各行各业提供更…...

    2024/5/5 9:14:44
  5. AI小程序的创业方向:深度思考与逻辑引领

    随着人工智能技术的快速发展&#xff0c;AI小程序逐渐成为创业的新热点。在这个充满机遇与挑战的时代&#xff0c;我们有必要深入探讨AI小程序的创业方向&#xff0c;以把握未来的发展趋势。 一、目标市场定位 首先&#xff0c;我们要明确目标市场。针对不同的用户需求&#x…...

    2024/5/5 8:51:37
  6. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/5/4 23:54:56
  7. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/5/4 23:54:56
  8. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/5/4 23:54:56
  9. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/5/6 9:21:00
  10. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/5/4 23:54:56
  11. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/5/4 23:55:05
  12. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/5/4 23:54:56
  13. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/5/4 23:55:16
  14. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/5/4 23:54:56
  15. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/5/6 1:40:42
  16. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/5/4 23:54:56
  17. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/5/4 23:55:17
  18. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/5/4 23:55:06
  19. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/5/4 23:54:56
  20. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/5/4 23:55:06
  21. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/5/5 8:13:33
  22. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/5/4 23:55:16
  23. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/5/4 23:54:58
  24. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/5/6 21:42:42
  25. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/5/4 23:54:56
  26. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  27. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  28. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  29. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  30. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  31. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  32. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  33. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  34. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  35. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  36. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  37. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  38. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  39. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  40. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  41. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  42. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  43. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  44. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  45. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57