赞
踩
该系列文章总纲链接:专题分纲目录 Android Framework 音频子系统
本章节主要关注➕ 以上思维导图左上 音量调节 部分 即可。说明了音量的基础知识和AudioFlinger调节音量流程,主要包括:
1 音量基础知识
@1 四大类Volume音量
@2 十种stream
Android系统中有10种stream,在system/core/include/system/audio.h中定义,但把这10种stream分成组,属于同一组的stream具有相同的别名(alias)。在我们设置音量时,一个音量调节滑动条具有一个alias,具有相同alias的stream都会受到这个滑动条的影响。stream与alias的关系(参考)如下所示:
@3 声音播放的两种路径
@4 混音的逻辑
混合在一起: 最终混音 =混音数据1+混音数据2+混音数据3,然后把混合后的数据写给硬件。
@5 音频系统中的一些关键变量说明:
AudioFlinger类中有关成员:
- stream_type_t mStreamTypes[AUDIO_STREAM_CNT];
- float mMasterVolume; //存储master volume
- bool mMasterMute; //存储是否静音
playbackThread类中:
- //为DuplicatingThread的OutputTrack多出一项, DuplicatingThread可以用于在两个声卡上播放出同样的声音
- stream_type_t mStreamTypes[AUDIO_STREAM_CNT + 1];
- bool mMasterMute;
- float mMasterVolume; //来源于AudioFlinger中的同名的变量
AudioTrack类中(App端):
float mVolume[2]; //两项,分别表示App设置的左右声道的音量
说明:stream volume和audioTreack中的volume只是软件上的处理,masterVolue中保存的值若HAL提供了相应的写函数就会写给硬件。
2 AudioFlinger调节音量流程
2.1 AudioFlinger音量设置流程说明
音量设置是通过逻辑运算将音量值存放在变量中,之后再播放中重新进行一轮逻辑运算,最终和声音数据一起写入到声卡中,进而播放出合理的声音。
@1 AudioFlinger对master volume, stream volume的初始化设置流程
最开始MasterVolume,、MasterMute、StreamVolume、StreamMute的初始化是在AudioFlinger对象创建时初始化的,MasterVolume,、MasterMute是在构造器中直接初始化,代码如下:
- AudioFlinger::AudioFlinger()
- : BnAudioFlinger(),
- mPrimaryHardwareDev(NULL),
- mAudioHwDevs(NULL),
- mHardwareStatus(AUDIO_HW_IDLE),
- mMasterVolume(1.0f),//初值1.0f
- mMasterMute(false),//静音初值
- mNextUniqueId(1),
- mMode(AUDIO_MODE_INVALID),
- mBtNrecIsOff(false),
- mIsLowRamDevice(true),
- mIsDeviceTypeKnown(false),
- mGlobalEffectEnableTime(0),
- mPrimaryOutputSampleRate(0)
- {
- //...
- }
而StreamVolume、StreamMute的初始化则是在成员变量结构体中初始化的,代码如下:
- //在创建结构体的时候 直接初始化
- struct stream_type_t {
- stream_type_t()
- : volume(1.0f),
- mute(false)
- {
- }
- float volume;
- bool mute;
- };
这一阶段初始化后,在加载音频库的时候执行loadHwModule,代码如下:
- audio_module_handle_t AudioFlinger::loadHwModule(const char *name)
- {
- if (name == NULL) {
- return 0;
- }
- if (!settingsAllowed()) {
- return 0;
- }
- Mutex::Autolock _l(mLock);
- return loadHwModule_l(name);
- }
继续分析loadHwModule_l,代码实现如下:
- // loadHwModule_l() must be called with AudioFlinger::mLock held
- audio_module_handle_t AudioFlinger::loadHwModule_l(const char *name)
- {
- for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
- if (strncmp(mAudioHwDevs.valueAt(i)->moduleName(), name, strlen(name)) == 0) {
- ALOGW("loadHwModule() module %s already loaded", name);
- return mAudioHwDevs.keyAt(i);
- }
- }
-
- audio_hw_device_t *dev;
- //获取audio_hw_device_t类型设备dev,可以直接操作HAL层
- int rc = load_audio_interface(name, &dev);
- mHardwareStatus = AUDIO_HW_INIT;
- rc = dev->init_check(dev);
- mHardwareStatus = AUDIO_HW_IDLE;
-
- AudioHwDevice::Flags flags = static_cast<AudioHwDevice::Flags>(0);
- { // scope for auto-lock pattern
- AutoMutex lock(mHardwareLock);
-
- if (0 == mAudioHwDevs.size()) {
- mHardwareStatus = AUDIO_HW_GET_MASTER_VOLUME;
- //只要dev中含有get_master_volume,表明可以从库中获取master_volume的初值
- if (NULL != dev->get_master_volume) {
- float mv;
- if (OK == dev->get_master_volume(dev, &mv)) {
- mMasterVolume = mv;
- }
- }
-
- mHardwareStatus = AUDIO_HW_GET_MASTER_MUTE;
- //只要dev中含有get_master_mute,表明可以从库中获取master_mute的初值
- if (NULL != dev->get_master_mute) {
- bool mm;
- if (OK == dev->get_master_mute(dev, &mm)) {
- mMasterMute = mm;
- }
- }
- }
-
- mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME;
- //设置master_volume的初值 到硬件中
- if ((NULL != dev->set_master_volume) &&
- (OK == dev->set_master_volume(dev, mMasterVolume))) {
- flags = static_cast<AudioHwDevice::Flags>(flags |
- AudioHwDevice::AHWD_CAN_SET_MASTER_VOLUME);
- }
- mHardwareStatus = AUDIO_HW_SET_MASTER_MUTE;
- //设置master_mute的初值 到硬件中
- if ((NULL != dev->set_master_mute) &&
- (OK == dev->set_master_mute(dev, mMasterMute))) {
- flags = static_cast<AudioHwDevice::Flags>(flags |
- AudioHwDevice::AHWD_CAN_SET_MASTER_MUTE);
- }
- mHardwareStatus = AUDIO_HW_IDLE;
- }
-
- audio_module_handle_t handle = nextUniqueId();
- mAudioHwDevs.add(handle, new AudioHwDevice(handle, name, dev, flags));
- return handle;
- }
这里对MasterVolume,、MasterMute进行二次初始化,即如果音频库是支持初值设置的,则以音频库中的值为主,否则就是AudioFlinger创建时的初始值。
@2 AudioFlinger::setMasterVolume 主音量设置流程
AudioFlinger::setMasterVolume的代码实现如下:
- status_t AudioFlinger::setMasterVolume(float value)
- {
- status_t ret = initCheck();
- //...
- Mutex::Autolock _l(mLock);
- mMasterVolume = value;
- for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
- AutoMutex lock(mHardwareLock);
- AudioHwDevice *dev = mAudioHwDevs.valueAt(i);
-
- mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME;
- //直接将master_volume的值设置到硬件中
- if (dev->canSetMasterVolume()) {
- dev->hwDevice()->set_master_volume(dev->hwDevice(), value);
- }
- mHardwareStatus = AUDIO_HW_IDLE;
- }
- //将master_volume的值设置到各个播放线程中
- for (size_t i = 0; i < mPlaybackThreads.size(); i++)
- mPlaybackThreads.valueAt(i)->setMasterVolume(value);
-
- return NO_ERROR;
- }
这里PlaybackThread::setMasterVolume的代码实现如下:
- void AudioFlinger::PlaybackThread::setMasterVolume(float value)
- {
- Mutex::Autolock _l(mLock);
- // Don't apply master volume in SW if our HAL can do it for us.
- if (mOutput && mOutput->audioHwDev &&
- mOutput->audioHwDev->canSetMasterVolume()) {
- mMasterVolume = 1.0;
- } else {
- mMasterVolume = value;
- }
- }
可以看到都是直接操作HAL层的接口进行参数设置。
@3 AudioFlinger::setStreamVolume 流音量设置流程
AudioFlinger::setStreamVolume的 代码实现如下:
- status_t AudioFlinger::setStreamVolume(audio_stream_type_t stream, float value,
- audio_io_handle_t output)
- {
- status_t status = checkStreamType(stream);
- AutoMutex lock(mLock);
- PlaybackThread *thread = NULL;
- if (output != AUDIO_IO_HANDLE_NONE) {
- thread = checkPlaybackThread_l(output);
- if (thread == NULL) {
- return BAD_VALUE;
- }
- }
-
- mStreamTypes[stream].volume = value;
- if (thread == NULL) {
- //未指定线程则全部播放线程 均设置
- for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
- mPlaybackThreads.valueAt(i)->setStreamVolume(stream, value);
- }
- } else {
- //指定线程则直接设置
- thread->setStreamVolume(stream, value);
- }
- return NO_ERROR;
- }
继续分析播放线程的setStreamVolume方法,代码实现如下:
- void AudioFlinger::PlaybackThread::setStreamVolume(audio_stream_type_t stream, float value)
- {
- Mutex::Autolock _l(mLock);
- mStreamTypes[stream].volume = value;//赋值
- broadcast_l();
- }
实际上 每个播放线程中都有 mStreamTypes[stream].volume,和 AudioFlinger的mStreamTypes[stream].volume是一致的。
@4 AudioTrack volume的设置
AudioTrack::setVolume的代码实现如下:
- status_t AudioTrack::setVolume(float volume)
- {
- return setVolume(volume, volume);
- }
-
- status_t AudioTrack::setVolume(float left, float right)
- {
- //...
- AutoMutex lock(mLock);
- mVolume[AUDIO_INTERLEAVE_LEFT] = left;
- mVolume[AUDIO_INTERLEAVE_RIGHT] = right;
- //这里会通过ClientProxy将音量参数设置到共享内存中
- //这里的mProxy =
- //new AudioTrackClientProxy(cblk, buffers, frameCount, mFrameSizeAF);
- mProxy->setVolumeLR(gain_minifloat_pack(gain_from_float(left), gain_from_float(right)));
-
- if (isOffloaded_l()) {
- mAudioTrack->signal();
- }
- return NO_ERROR;
- }
这里是把这个数据记录在mVolumeLR域中,创建Proxy时传递的Cblk参数就是共享内存的头部。
2.2 播放线程 加载音量设置 流程
@1 源码流程分析说明
这里分析时主要针对音量部分相关代码进行分析,代码实现如下:
- // prepareTracks_l() must be called with ThreadBase::mLock held
- AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTracks_l(
- Vector< sp<Track> > *tracksToRemove)
- {
- //...
- float masterVolume = mMasterVolume;
- bool masterMute = mMasterMute;
- if (masterMute) {//如果静音条件为真,则设置masterVolume=0
- masterVolume = 0;
- }
- //...
- mMixerBufferValid = false; // mMixerBuffer has no valid data until appropriate tracks found.
- mEffectBufferValid = false; // mEffectBuffer has no valid data until tracks found.
-
- for (size_t i=0 ; i<count ; i++) {
- const sp<Track> t = mActiveTracks[i].promote();
- if (t == 0) {
- continue;
- }
-
- // this const just means the local variable doesn't change
- Track* const track = t.get();
- //...
- { // local variable scope to avoid goto warning
- audio_track_cblk_t* cblk = track->cblk();
- int name = track->name();
- size_t desiredFrames;
- uint32_t sr = track->sampleRate();
- if (sr == mSampleRate) {
- desiredFrames = mNormalFrameCount;
- } else {
- // +1 for rounding and +1 for additional sample needed for interpolation
- desiredFrames = (mNormalFrameCount * sr) / mSampleRate + 1 + 1;
- desiredFrames += mAudioMixer->getUnreleasedFrames(track->name());
- }
- uint32_t minFrames = 1;
- if ((track->sharedBuffer() == 0) && !track->isStopped() && !track->isPausing() &&
- (mMixerStatusIgnoringFastTracks == MIXER_TRACKS_READY)) {
- minFrames = desiredFrames;
- }
-
- size_t framesReady = track->framesReady();
- if ((framesReady >= minFrames) && track->isReady() &&
- !track->isPaused() && !track->isTerminated())
- {
- mixedTracks++;
- //...
- // compute volume for this track
- uint32_t vl, vr; // in U8.24 integer format
- float vlf, vrf, vaf; // in [0.0, 1.0] float format
- if (track->isPausing() || mStreamTypes[track->streamType()].mute) {
- vl = vr = 0;
- vlf = vrf = vaf = 0.;
- if (track->isPausing()) {
- track->setPaused();
- }
- } else {
- // read original volumes with volume control
- //获取 StreamType Volume
- float typeVolume = mStreamTypes[track->streamType()].volume;
- float v = masterVolume * typeVolume;
- //获取共享内存代理
- AudioTrackServerProxy *proxy = track->mAudioTrackServerProxy;
- //从共享内存中获得左右声道
- gain_minifloat_packed_t vlr = proxy->getVolumeLR();
- vlf = float_from_gain(gain_minifloat_unpack_left(vlr));
- vrf = float_from_gain(gain_minifloat_unpack_right(vlr));
- // track volumes come from shared memory, so can't be trusted and must be clamped
- //边界判断
- if (vlf > GAIN_FLOAT_UNITY) {
- ALOGV("Track left volume out of range: %.3g", vlf);
- vlf = GAIN_FLOAT_UNITY;
- }
- if (vrf > GAIN_FLOAT_UNITY) {
- ALOGV("Track right volume out of range: %.3g", vrf);
- vrf = GAIN_FLOAT_UNITY;
- }
- // now apply the master volume and stream type volume
- //放大系数:master_volume * stream_volume * AudioTrack_volume
- vlf *= v;
- vrf *= v;
- // assuming master volume and stream type volume each go up to 1.0,
- // then derive vl and vr as U8.24 versions for the effect chain
- //下面主要是左右声道转换成AUX单声道的一些逻辑运算
- const float scaleto8_24 = MAX_GAIN_INT * MAX_GAIN_INT;
- vl = (uint32_t) (scaleto8_24 * vlf);
- vr = (uint32_t) (scaleto8_24 * vrf);
- // vl and vr are now in U8.24 format
- uint16_t sendLevel = proxy->getSendLevel_U4_12();
- // send level comes from shared memory and so may be corrupt
- if (sendLevel > MAX_GAIN_INT) {
- ALOGV("Track send level out of range: %04X", sendLevel);
- sendLevel = MAX_GAIN_INT;
- }
- // vaf is represented as [0.0, 1.0] float by rescaling sendLevel
- vaf = v * sendLevel * (1. / MAX_GAIN_INT);
- }
-
- // Delegate volume control to effect in track effect chain if needed
- if (chain != 0 && chain->setVolume_l(&vl, &vr)) {
- // Do not ramp volume if volume is controlled by effect
- param = AudioMixer::VOLUME;
- // Update remaining floating point volume levels
- vlf = (float)vl / (1 << 24);
- vrf = (float)vr / (1 << 24);
- track->mHasVolumeController = true;
- } else {
- // force no volume ramp when volume controller was just disabled or removed
- // from effect chain to avoid volume spike
- if (track->mHasVolumeController) {
- param = AudioMixer::VOLUME;
- }
- track->mHasVolumeController = false;
- }
-
- // XXX: these things DON'T need to be done each time
- mAudioMixer->setBufferProvider(name, track);
- mAudioMixer->enable(name);
- //关键点:通过参数设置将音量信息传递出去
- mAudioMixer->setParameter(name, param, AudioMixer::VOLUME0, &vlf);
- mAudioMixer->setParameter(name, param, AudioMixer::VOLUME1, &vrf);
- mAudioMixer->setParameter(name, param, AudioMixer::AUXLEVEL, &vaf);
- //设置其他参数
- //...
- // reset retry count
- track->mRetryCount = kMaxTrackRetries;
- if (mMixerStatusIgnoringFastTracks != MIXER_TRACKS_READY ||
- mixerStatus != MIXER_TRACKS_ENABLED) {
- mixerStatus = MIXER_TRACKS_READY;
- }
- } else {
- //...
- }
-
- } // local variable scope to avoid goto warning
- track_is_ready: ;
- }
- //...
- return mixerStatus;
- }
这里专注分析AudioMixer的 参数设置setParameter方法,代码实现如下:
- void AudioMixer::setParameter(int name, int target, int param, void *value)
- {
- name -= TRACK0;
- track_t& track = mState.tracks[name];
- int valueInt = static_cast<int>(reinterpret_cast<uintptr_t>(value));
- int32_t *valueBuf = reinterpret_cast<int32_t*>(value);
-
- switch (target) {
- //...
- case RAMP_VOLUME:
- case VOLUME:
- switch (param) {
- case AUXLEVEL:
- if (setVolumeRampVariables(*reinterpret_cast<float*>(value),
- target == RAMP_VOLUME ? mState.frameCount : 0,
- &track.auxLevel, &track.prevAuxLevel, &track.auxInc,
- &track.mAuxLevel, &track.mPrevAuxLevel, &track.mAuxInc)) {
- invalidateState(1 << name);
- }
- break;
- default:
- if ((unsigned)param >= VOLUME0 && (unsigned)param < VOLUME0 + MAX_NUM_VOLUMES) {
- //setVolumeRampVariables主要是 float和int类型之间的转换的一些逻辑操作
- if (setVolumeRampVariables(*reinterpret_cast<float*>(value),
- target == RAMP_VOLUME ? mState.frameCount : 0,
- &track.volume[param - VOLUME0], &track.prevVolume[param - VOLUME0],
- &track.volumeInc[param - VOLUME0],
- &track.mVolume[param - VOLUME0], &track.mPrevVolume[param - VOLUME0],
- &track.mVolumeInc[param - VOLUME0])) {
- invalidateState(1 << name);
- }
- } else {
- LOG_ALWAYS_FATAL("setParameter volume: bad param %d", param);
- }
- }
- break;
- default:
- LOG_ALWAYS_FATAL("setParameter: bad target %d", target);
- }
- }
这里专注 invalidateState的实现,代码如下:
- void AudioMixer::invalidateState(uint32_t mask)
- {
- if (mask != 0) {
- mState.needsChanged |= mask;
- mState.hook = process__validate;
- }
- }
process__validate的实现如下:
- void AudioMixer::process__validate(state_t* state, int64_t pts)
- {
- //...
- // compute everything we need...
- while (en) {
- //...
- if (n & NEEDS_MUTE) {
- t.hook = track__nop;
- } else {
- if (n & NEEDS_AUX) {
- all16BitsStereoNoResample = false;
- }
- if (n & NEEDS_RESAMPLE) {
- all16BitsStereoNoResample = false;
- resampling = true;
- t.hook = getTrackHook(TRACKTYPE_RESAMPLE, t.mMixerChannelCount,
- t.mMixerInFormat, t.mMixerFormat);
- } else {
- if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_1){
- t.hook = getTrackHook(
- t.mMixerChannelCount == 2 // TODO: MONO_HACK.
- ? TRACKTYPE_NORESAMPLEMONO : TRACKTYPE_NORESAMPLE,
- t.mMixerChannelCount,
- t.mMixerInFormat, t.mMixerFormat);
- all16BitsStereoNoResample = false;
- }
- if ((n & NEEDS_CHANNEL_COUNT__MASK) >= NEEDS_CHANNEL_2){
- t.hook = getTrackHook(TRACKTYPE_NORESAMPLE, t.mMixerChannelCount,
- t.mMixerInFormat, t.mMixerFormat);
- }
- }
- }
- }
- //...
- }
这里主要关注getTrackHook函数,代码实现如下:
- AudioMixer::hook_t AudioMixer::getTrackHook(int trackType, uint32_t channelCount,
- audio_format_t mixerInFormat, audio_format_t mixerOutFormat __unused)
- {
- if (!kUseNewMixer && channelCount == FCC_2 && mixerInFormat == AUDIO_FORMAT_PCM_16_BIT) {
- switch (trackType) {
- //...
- case TRACKTYPE_NORESAMPLE:
- return track__16BitsStereo;
- default:
- break;
- }
- }
- //...
- return NULL;
- }
这里以关注TRACKTYPE_NORESAMPLE为例,最终会调用到track__16BitsStereo,代码实现如下:
- void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount,
- int32_t* temp __unused, int32_t* aux)
- {
- ALOGVV("track__16BitsStereo\n");
- const int16_t *in = static_cast<const int16_t *>(t->in);
-
- if (CC_UNLIKELY(aux != NULL)) {
- //忽略AUX相关处理
- } else {
- // ramp gain
- if (CC_UNLIKELY(t->volumeInc[0]|t->volumeInc[1])) {
- int32_t vl = t->prevVolume[0];
- int32_t vr = t->prevVolume[1];
- const int32_t vlInc = t->volumeInc[0];左声道音量
- const int32_t vrInc = t->volumeInc[1];右声道音量
-
- do {
- *out++ += (vl >> 16) * (int32_t) *in++;
- *out++ += (vr >> 16) * (int32_t) *in++;
- vl += vlInc;
- vr += vrInc;
- } while (--frameCount);
-
- t->prevVolume[0] = vl;
- t->prevVolume[1] = vr;
- t->adjustVolumeRamp(false);
- }
-
- // constant gain
- else {
- const uint32_t vrl = t->volumeRL;
- do {
- uint32_t rl = *reinterpret_cast<const uint32_t *>(in);
- in += 2;
- out[0] = mulAddRL(1, rl, vrl, out[0]);
- out[1] = mulAddRL(0, rl, vrl, out[1]);
- out += 2;
- } while (--frameCount);
- }
- }
- t->in = in;
- }
最终我们把数据存储到out中,这里的mulAddRL有三个,这里以下面的这个实现为例,代码如下:
- //另外2种实现模式类似
- static inline
- int32_t mulAddRL(int left, uint32_t inRL, int32_t v, int32_t a)
- {
- #if USE_INLINE_ASSEMBLY
- int32_t out;
- if (left) {
- asm( "smlawb %[out], %[v], %[inRL], %[a] \n"
- : [out]"=r"(out)
- : [inRL]"%r"(inRL), [v]"r"(v), [a]"r"(a)
- : );
- } else {
- asm( "smlawt %[out], %[v], %[inRL], %[a] \n"
- : [out]"=r"(out)
- : [inRL]"%r"(inRL), [v]"r"(v), [a]"r"(a)
- : );
- }
- return out;
- #else
- int16_t s = left ? int16_t(inRL) : int16_t(inRL>>16);
- return a + int32_t((int64_t(v) * s) >> 16);
- #endif
- }
虽然有可能会使用汇编语句来优化,但实际上逻辑是一致的(这里用outL表示左声道,outR表示右声道):
最后outL 和 outR 合并成一个值(低16bit是左声道数据,高16bit是右声道数据)并返回。这里实际上是属于播放音频中MixerThread::prepareTracks_l中 tracks[x].hook中的一个操作,通过这操作有了prepareTrack_l设置的参数,在threadLoop_mix中进行混音。最后通过threadLoop_write用于混音后的音频输出,最后将填充好的Buffer写入到硬件中。
@2 关于MixerThread::prepareTracks_l涉及的track_t结构体的说明
代码实现如下:
- struct track_t {
- //...
- // TODO: Eventually remove legacy integer volume settings
- //int类型 普通声音
- union {
- int16_t volume[MAX_NUM_VOLUMES]; // U4.12 fixed point (top bit should be zero)
- int32_t volumeRL;
- };
- int32_t prevVolume[MAX_NUM_VOLUMES];
- int32_t volumeInc[MAX_NUM_VOLUMES];
- //...
- //int类型 aux声音
- int32_t auxInc;
- int32_t prevAuxLevel;
- int16_t auxLevel; // 0 <= auxLevel <= MAX_GAIN_INT, but signed for mul performance
- //...
- //float类型 普通声音
- float mVolume[MAX_NUM_VOLUMES]; // floating point set volume
- float mPrevVolume[MAX_NUM_VOLUMES]; // floating point previous volume
- float mVolumeInc[MAX_NUM_VOLUMES]; // floating point volume increment
- //...
- //float类型 aux声音
- float mAuxLevel; // floating point set aux level
- float mPrevAuxLevel; // floating point prev aux level
- float mAuxInc; // floating point aux increment
- //...
- };
这里aux的数据实际上就是 左右声道叠加在一起,通过特定处理后转换成 特定声道的方式。
这里我们可以发现,track_t结构体中共有4组音量变量。都是PreVolume,VolumeInc,Volume这种模式,这三种变量的意义如下图所示:
解读如下:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。