Android MediaPlayer 分析- MediaPlayerService.cpp

frameworks/base/libmediaplayerservice/MediaPlayerService.cpp

因为工作的平台是mx51,所以分析的是mx51 10.3的代码,本文主要分析视频播放部分的代码,对于recorder和audio部分忽略掉。

Mediaplayer service是一个系统服务,Android 视频播放,录音录像,元数据获取等客户端应用与Mediaplayer service交互,由MediaPlayer Service实现视频的播放,录像,元数据获取等操作。


 251 MediaPlayerService::MediaPlayerService()
 252 {
 253     LOGV("MediaPlayerService created");
 254     mNextConnId = 1;
 255 }

MediaPlayerService的构造函数

mNextConnId记录着client可用的connect id, 每创建一个client,mNextConnId都递增1,已经用过的connect id不能复用


sp<IMediaMetadataRetriever> MediaPlayerService::createMetadataRetriever(pid_t pid)
{
    sp<MetadataRetrieverClient> retriever = new MetadataRetrieverClient(pid);
    LOGV("Create new media retriever from pid %d", pid);
    return retriever;
}


创建一个Metadata retriever client,从类的名字我们可以猜测出这个函数是获取media文件的metadata。查看MetadataRetrieverClient的定义文件libmediaplayerservice/MetadataRetrieverClient.cpp,可以看到这个类提供了如下几种方法

  • getFrameAtTime 获取video文件参数指定时间的帧内容,可用来获取media文件的thumbnail
  • extractAlbumArt 获取media文件的AlbumArt, AlbumArt是media文件的预览封面,一般来说,如果不能抽取到这个,就用getFrameAtTime获取第一帧来代替
  • extractMetadata:获取media文件的元数据:Album, Artist, Author, Date, Genre, VIDEO_FORMAT, VIDEO_HEIGHT等等
 286 sp<IMediaPlayer> MediaPlayerService::create(
 287         pid_t pid, const sp<IMediaPlayerClient>& client, const char* url,
 288         const KeyedVector<String8, String8> *headers, int audioSessionId)
 289 {
 290     int32_t connId = android_atomic_inc(&mNextConnId);
 291     sp<Client> c = new Client(this, pid, connId, client, audioSessionId);
 292     LOGV("Create new client(%d) from pid %d, url=%s, connId=%d, audioSessionId=%d",
 293             connId, pid, url, connId, audioSessionId);
 294     if (NO_ERROR != c->setDataSource(url, headers))
 295     {
 296         c.clear();
 297         return c;
 298     }
 299     wp<Client> w = c;
 300     Mutex::Autolock lock(mLock);
 301     mClients.add(w);
 302     return c;
 303 }

为MediaPlayer Client创建一个对应的实体

@pid: client所在进程的id

@client: MediaPlayer client

@url: media 文件url

@headers: 未知

@audioSessionId:

301 mClients,是一个client对象数组,每一个新建的client都增加到这里面

 305 sp<IMediaPlayer> MediaPlayerService::create(pid_t pid, const sp<IMediaPlayerClient>& client,
 306         int fd, int64_t offset, int64_t length, int audioSessionId)
 307 {
 308     int32_t connId = android_atomic_inc(&mNextConnId);
 309     sp<Client> c = new Client(this, pid, connId, client, audioSessionId);
 310     LOGV("Create new client(%d) from pid %d, fd=%d, offset=%lld, length=%lld, audioSessionId=%d",
 311             connId, pid, fd, offset, length, audioSessionId);
 312     if (NO_ERROR != c->setDataSource(fd, offset, length)) {
 313         c.clear();
 314     } else {
 315         wp<Client> w = c;
 316         Mutex::Autolock lock(mLock);
 317         mClients.add(w);
 318     }
 319     ::close(fd);
 320     return c;
 321 }

create的另外一个重载形式是

 305 sp<IMediaPlayer> MediaPlayerService::create(pid_t pid, const sp<IMediaPlayerClient>& client,
 306         int fd, int64_t offset, int64_t length, int audioSessionId)
 307 {
 308     int32_t connId = android_atomic_inc(&mNextConnId);
 309     sp<Client> c = new Client(this, pid, connId, client, audioSessionId);
 310     LOGV("Create new client(%d) from pid %d, fd=%d, offset=%lld, length=%lld, audioSessionId=%d",
 311             connId, pid, fd, offset, length, audioSessionId);
 312     if (NO_ERROR != c->setDataSource(fd, offset, length)) {
 313         c.clear();
 314     } else {
 315         wp<Client> w = c;
 316         Mutex::Autolock lock(mLock);
 317         mClients.add(w);
 318     }
 319     ::close(fd);
 320     return c;
 321 }

@fd:  已打开的media文件描述符

@offset: 对android引入offset和length参数很迷惑,难道对media文件的操作不是从fd的起始位置开始吗?猜测可能确实存在这样的情况,media内容在另外一个文件容器中,这样就使用offset和length定义media内容的位置。

@length:


MediaPlayerService::Client::Client(const sp<MediaPlayerService>& service, pid_t pid,
        int32_t connId, const sp<IMediaPlayerClient>& client, int audioSessionId)
{
    LOGV("Client(%d) constructor", connId);
    mPid = pid;
    mConnId = connId;
    mService = service;
    mClient = client;
    mLoop = false;
    mStatus = NO_INIT;
    mAudioSessionId = audioSessionId;

#if CALLBACK_ANTAGONIZER
    LOGD("create Antagonizer");
    mAntagonizer = new Antagonizer(notify, this);
#endif
}

对于一个视频播放,会创建两个client,一个在用户空间MediaPlayer实例(这个client负责在和MediaplayerService通信);另外一个在Mediaplayer Service空间MediaplayerService::Client::Client实例,用于在Mediaplayer Service端维护client的状态。

服务器端client在MediaPlayerService::create中创建,新创建的Client对象加到mClients数组中


player_type getPlayerType(int fd, int64_t offset, int64_t length)
....
....
该函数根据首先读取文件的头部一些字节,然后根据头部标识的文件类型,返回相应的播放器类型

player_type getPlayerType(const char* url)
......
......

根据url来决定player类型

static sp<MediaPlayerBase> createPlayer(player_type playerType, void* cookie,
        notify_callback_f notifyFunc)
{
    sp<MediaPlayerBase> p;
    switch (playerType & 0xff) {
#ifndef NO_OPENCORE
        case PV_PLAYER:
            LOGV(" create PVPlayer");
            p = new PVPlayer();
            break;      
#endif
        case SONIVOX_PLAYER:
            LOGV(" create MidiFile");
            p = new MidiFile();
            break;
        case STAGEFRIGHT_PLAYER:
            LOGV(" create StagefrightPlayer");
            p = new StagefrightPlayer;
            break;
#ifdef PREBUILT_FSL_IMX_OMX
        case OMX_PLAYER:
            LOGV(" Create OMXPlayer.\n");
            p = new OMXPlayer(playerType >> 8);
            break;
#endif
        case TEST_PLAYER:
            LOGV("Create Test Player stub");
            p = new TestPlayerStub();
            break;
    }
    if (p != NULL) {    
        if (p->initCheck() == NO_ERROR) {
            p->setNotifyCallback(cookie, notifyFunc);
        } else {            
            p.clear();
        }
    }
    if (p == NULL) {
        LOGE("Failed to create player object");
    }
    return p;
}

根据指定的不同播放器类型,创建相应的播放器并返回,可以看到,当前支持PVPlayer, MidiFIle, StagefrightPlayer, OMXPlayer

OMXPlayer是freescale专有的播放器

status_t MediaPlayerService::Client::setDataSource(
        const char *url, const KeyedVector<String8, String8> *headers)
{
    LOGV("setDataSource(%s)", url);
    if (url == NULL)
        return UNKNOWN_ERROR;

    if (strncmp(url, "content://", 10) == 0) {
        // get a filedescriptor for the content Uri and
        // pass it to the setDataSource(fd) method

        String16 url16(url);
        int fd = android::openContentProviderFile(url16);
        if (fd < 0)
        {
            LOGE("Couldn't open fd for %s", url);
            return UNKNOWN_ERROR;
        }
        setDataSource(fd, 0, 0x7fffffffffLL); // this sets mStatus
        close(fd);
        return mStatus;
    } else {
        player_type playerType = getPlayerType(url);
        LOGV("player type = %d", playerType);

        // create the right type of player
        sp<MediaPlayerBase> p = createPlayer(playerType);
        if (p == NULL) return NO_INIT;

        if (!p->hardwareOutput()) {
            mAudioOutput = new AudioOutput(mAudioSessionId);
            static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);
        }

        // now set data source
        LOGV(" setDataSource");
        mStatus = p->setDataSource(url, headers);
        if (mStatus == NO_ERROR) {
            mPlayer = p;
        } else {
            LOGE("  error: %d", mStatus);
        }
        return mStatus;
    }
}

setDataSource 顾名思义就是把数据相关的内容设置给播放器,如果使用stagefright player最终会调用Awesome Player的setDataSource成员函数。AwesomePlayer::setDataSource有两个重载函数:一个直接保存@headers,另外一个调用相应的extractor从文件中抽取需要的信息 mflags, mBitrate等等。

status_t MediaPlayerService::Client::setVideoSurface(const sp<ISurface>& surface)
{
    LOGV("[%d] setVideoSurface(%p)", mConnId, surface.get());
    sp<MediaPlayerBase> p = getPlayer();
    if (p == 0) return UNKNOWN_ERROR;
    return p->setVideoSurface(surface);
}   
为播放器设置surface对象

sp<IMemory> MediaPlayerService::Client::captureCurrentFrame()
{
    LOGV("captureCurrentFrame");
    sp<MediaPlayerBase> p = getPlayer();
    if (p == NULL) {
        LOGE("media player is not initialized");
        return NULL;
    }
    Mutex::Autolock lock(mLock);
    mVideoFrame.clear();
    mVideoFrameDealer.clear();

    VideoFrame *frame = NULL;
    p->captureCurrentFrame(&frame);
    if (frame == NULL) {
        LOGE("failed to capture a video frame");
        return NULL;
    }
    size_t size = sizeof(VideoFrame) + frame->mSize;
    mVideoFrameDealer = new MemoryDealer(size);
    if (mVideoFrameDealer == NULL) {
        LOGE("failed to create MemoryDealer");
        return NULL;
    }
    mVideoFrame = mVideoFrameDealer->allocate(size);
    if (mVideoFrame == NULL) {
        LOGE("not enough memory for VideoFrame size=%u", size);
        mVideoFrameDealer.clear();
        return NULL;
    }
    VideoFrame *frameCopy = static_cast<VideoFrame *>(mVideoFrame->pointer());
    frameCopy->mWidth = frame->mWidth;
    frameCopy->mHeight = frame->mHeight;
    frameCopy->mDisplayWidth = frame->mDisplayWidth;
    frameCopy->mDisplayHeight = frame->mDisplayHeight;
    frameCopy->mSize = frame->mSize;
    frameCopy->mData = (uint8_t *)frameCopy + sizeof(VideoFrame);
    memcpy(frameCopy->mData, frame->mData, frame->mSize);
    return mVideoFrame;
}

获取当前帧数据,返回一片内存空间,内存前面部分是帧的一些基本信息: width, height, displayWidth, displayHeight, size,后面是frame 数据。


MediaPlayerService.cpp中和video相关的代码就分析完了。

MediaPlayerService主要的工作就是在客户应用申请播放时,在Service创建对应的Client代理,处理客户端应用发过来的请求,Service会根据打开的文件或者URI类型,选择播放插件。





  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
mediaplayer-viral.zip 是一个文件压缩包,其中包含了一个名为 "mediaplayer-viral" 的媒体播放器应用程序。 这个应用程序可能是一个用于播放音乐和视频的多媒体播放器。通过解压mediaplayer-viral.zip文件,可以将应用程序文件提取出来,并安装到适当的设备上进行使用。 该应用程序可能具有一些特殊的功能,使其能够迅速在社交媒体平台上传播开来。这可能意味着其具有与社交媒体平台的集成,可以方便地分享当前播放的音乐或视频到用户的社交媒体账户上。它可能还具有一些社交化的功能,比如用户之间可以相互关注、分享播放列表等等。这些功能对于即时分享和互动非常有用,可以帮助应用程序在社交媒体上迅速传播。 除了社交媒体功能,mediaplayer-viral.zip 中提供的媒体播放器应用程序可能还具有标准的播放控制功能,比如暂停、播放、切换曲目或视频等。它可能还支持设置播放列表、创建个人收藏夹、调整音量和亮度等常见的媒体播放器功能。此外,还可能具有一些额外的特性,比如可自定义的界面、歌曲识别功能或双重音频输出等等。这些功能将提供更好的用户体验和更多个性化的选项。 总的来说,mediaplayer-viral.zip 是一个包含了一个名为 "mediaplayer-viral" 的媒体播放器应用程序的文件压缩包。这个应用程序具有社交媒体集成和一系列常见的媒体播放器功能,旨在提供良好的用户体验和便捷的分享功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值