前言
使用 Android C++ 编写 MP3 播放程序,使用的是 framework\av\media\libmedia\mediaplayer.cpp 里面的 MediaPlayer 类。实现了本地播放和在线播放两种方式。
代码
其中,如果要播放 video,则需要和屏相关,使用到 surface。如下:
//video : need SurfaceFlinger
if (playback && (useSurface && useVideo)) {
composerClient = new SurfaceComposerClient;
CHECK_EQ(composerClient->initCheck(), (status_t)OK);
sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(
ISurfaceComposer::eDisplayIdMain));
DisplayInfo info;
SurfaceComposerClient::getDisplayInfo(display, &info);
ssize_t displayWidth = info.w;
ssize_t displayHeight = info.h;
ALOGI("display is %ld x %ld\n", displayWidth, displayHeight);
control = composerClient->createSurface(
String8("A Surface"),
displayWidth,
displayHeight,
PIXEL_FORMAT_RGB_565,
0);
CHECK(control != NULL);
CHECK(control->isValid());
SurfaceComposerClient::openGlobalTransaction();
CHECK_EQ(control->setLayer(INT_MAX), (status_t)OK);
CHECK_EQ(control->show(), (status_t)OK);
SurfaceComposerClient::closeGlobalTransaction();
surface = control->getSurface();
CHECK(surface != NULL);
}
要等待一个音源文件播放完毕后,才退出播放程序,则需要使用到 MPlayerListener 进行监听,需要我们注册监听函数:
sp<MPlayerListener> mListener = new MPlayerListener();
player->setListener(mListener);
//....
mSignal.wait(mLock);
//实现监听(注册监听)
class MPlayerListener : public MediaPlayerListener
{
void notify(int msg, int ext1, int ext2, const Parcel * /*obj*/)
{
switch (msg) {
case MEDIA_ERROR:
// Always log errors.
ALOGE("error (%d, %d)", ext1, ext2);
//fall through
case MEDIA_PLAYBACK_COMPLETE:
ALOGD("play media finished, quit");
if (player != NULL) {
player->stop();
player->reset();
player->setListener(0);
player->disconnect();
mPlayState = PLAYER_STATE_STOP;
if (fd != NULL) {
close(fd);
fd = NULL;
}
}
// mSignal.signal();
break;
default:
break;
}
}
};
音源文件有的是放在本地,有的是放置在网络上。MediaPlayer 类已经重载了方法 setDataSource,用于确定是本地播放还是网络在线播放:
//在线播放
player->setDataSource("http://oss.iot.aispeech.com/dcmp/02/BB/CiAB81uI9mOAVKcGAAqZVmc8ics229.mp3", NULL);
//本地播放
int fd = open(argv[0], O_RDONLY);
player->setDataSource(fd, 0, 0x7ffffffffffffffLL);
播放流程一般为:
- prepare
- start
- stop
- reset
完整代码如下:
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//#define LOG_NDEBUG 0
#define LOG_TAG "player"
#include <utils/Log.h>
#include "SimplePlayer.h"
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
#include <media/IMediaPlayerService.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/ALooper.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/AString.h>
#include <media/stagefright/MediaDefs.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/Surface.h>
#include <ui/DisplayInfo.h>
#include <media/mediaplayer.h>
#include <fcntl.h>
#include <media/AudioSystem.h>
sp<MediaPlayer> player = NULL;
player_states mPlayState = PLAYER_STATE_NONE;
int fd = NULL;
//bool isstop = false;
//bool ispause =false;
Mutex mLock;
Condition mSignal;
class MPlayerListener : public MediaPlayerListener
{
void notify(int msg, int ext1, int ext2, const Parcel * /*obj*/)
{
switch (msg) {
case MEDIA_ERROR:
// Always log errors.
ALOGE("error (%d, %d)", ext1, ext2);
//fall through
case MEDIA_PLAYBACK_COMPLETE:
ALOGD("play media finished, quit");
if (player != NULL) {
player->stop();
player->reset();
player->setListener(0);
player->disconnect();
mPlayState = PLAYER_STATE_STOP;
if (fd != NULL) {
close(fd);
fd = NULL;
}
}
// mSignal.signal();
break;
default:
break;
}
}
};
static void usage(const char *me) {
fprintf(stderr, "usage: %s [-a] use audio\n"
"\t\t[-v] use video\n"
"\t\t[-p] playback\n"
"\t\t[-S] allocate buffers from a surface\n",
me);
exit(1);
}
int main(int argc, char **argv) {
using namespace android;
const char *me = argv[0];
bool useAudio = false;
bool useVideo = false;
bool playback = false;
bool useSurface = false;
int res;
while ((res = getopt(argc, argv, "havpSD")) >= 0) {
switch (res) {
case 'a':
{
useAudio = true;
break;
}
case 'v':
{
useVideo = true;
break;
}
case 'p':
{
playback = true;
break;
}
case 'S':
{
useSurface = true;
break;
}
case '?':
case 'h':
default:
{
usage(me);
}
}
}
argc -= optind;
argv += optind;
if (argc != 1) {
usage(me);
}
if (!useAudio && !useVideo) {
useAudio = useVideo = true;
}
ProcessState::self()->startThreadPool();
sp<SurfaceComposerClient> composerClient;
sp<SurfaceControl> control;
sp<Surface> surface;
//video : need SurfaceFlinger
if (playback && (useSurface && useVideo)) {
composerClient = new SurfaceComposerClient;
CHECK_EQ(composerClient->initCheck(), (status_t)OK);
sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(
ISurfaceComposer::eDisplayIdMain));
DisplayInfo info;
SurfaceComposerClient::getDisplayInfo(display, &info);
ssize_t displayWidth = info.w;
ssize_t displayHeight = info.h;
ALOGI("display is %ld x %ld\n", displayWidth, displayHeight);
control = composerClient->createSurface(
String8("A Surface"),
displayWidth,
displayHeight,
PIXEL_FORMAT_RGB_565,
0);
CHECK(control != NULL);
CHECK(control->isValid());
SurfaceComposerClient::openGlobalTransaction();
CHECK_EQ(control->setLayer(INT_MAX), (status_t)OK);
CHECK_EQ(control->show(), (status_t)OK);
SurfaceComposerClient::closeGlobalTransaction();
surface = control->getSurface();
CHECK(surface != NULL);
}
//audio && video
if (playback) {
#ifdef _PLAY_ONLINE_
sp<MediaPlayer> player = new MediaPlayer();
sp<MPlayerListener> mListener = new MPlayerListener();
player->setListener(mListener);
player->setDataSource("http://oss.iot.aispeech.com/dcmp/02/BB/CiAB81uI9mOAVKcGAAqZVmc8ics229.mp3", NULL);
player->setAudioStreamType(AUDIO_STREAM_MUSIC);
ALOGI("player->prepare");
player->prepare();
player->start();
player->setVolume(1.0, 1.0);
ALOGI("player->start()");
//sleep(60);
//player->stop();
//ALOGI("player->stop()");
//player->reset();
//ALOGI("player->reset()");
mSignal.wait(mLock);
#else
int fd = open(argv[0], O_RDONLY);
ALOGI("open path: %s", argv[0]);
if (fd <= 0) {
ALOGE("open file failed, fd: %d, path: %s", fd, argv[0]);
} else {
sp<MediaPlayer> player = new MediaPlayer();
sp<MPlayerListener> mListener = new MPlayerListener();
player->setListener(mListener);
ALOGI("player->setDataSource fd: %d", fd);
player->setDataSource(fd, 0, 0x7ffffffffffffffLL);
player->setAudioStreamType(AUDIO_STREAM_MUSIC);
ALOGI("player->prepare");
player->prepare();
if (useSurface && useVideo) {
player->setVideoSurfaceTexture(surface->getIGraphicBufferProducer());
}
player->start();
player->setVolume(1.0, 1.0);
ALOGI("player->start()");
//sleep(240);
//player->stop();
//ALOGI("player->stop()");
//player->reset();
//ALOGI("player->reset()");
mSignal.wait(mLock);
}
#endif
}
if (playback && (useSurface && useVideo)) {
composerClient->dispose();
}
return 0;
}
该代码可以放置在 framework\av\cmds\stagefright\ 目录下进行编译。
可编写 Android,mk 如下:
################################################################################
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
player.cpp \
LOCAL_SHARED_LIBRARIES := \
libstagefright liblog libutils libbinder libstagefright_foundation \
libmedia libgui libcutils libui
LOCAL_C_INCLUDES:= \
frameworks/av/media/libstagefright \
$(TOP)/frameworks/native/include/media/openmax
LOCAL_CFLAGS += -Wno-multichar
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE:= player
include $(BUILD_EXECUTABLE)
# include $(BUILD_SHARED_LIBRARY)
################################################################################
以上,目前在Android4.4上经过了验证。