【第一篇章-android平台buffer播放探索】buffer play demo

从google发布的各个android版本源码看,android4.0开始就做了支持buffer播放的接口及demo了,编译完android SDK后在out中会生成一个可执行文件stream,这个就是一个非常好的demo测试程序,支持H264的ts播放,程序源代码位置以android4.3为例说明下,目录:android4.3\frameworks\av\cmds\stagefright,对应文件为stream.cpp,通用播放器用buffer来做突破口就在此哦,从这里开始,感兴趣的可以这么做,先做一个自己的DEMO,先播放本地文件,然后再把流媒体协议加进来集成就OK,此文件代码如下:

/*

* Copyright (C) 2010 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 "stream"

#include "utils/Log.h"

#include <binder/ProcessState.h>

#include <cutils/properties.h> // for property_get

#include <media/IStreamSource.h>

#include <media/mediaplayer.h>

#include <media/stagefright/foundation/ADebug.h>

#include <media/stagefright/foundation/AMessage.h>

#include <media/stagefright/DataSource.h>

#include <media/stagefright/MPEG2TSWriter.h>

#include <media/stagefright/MediaExtractor.h>

#include <media/stagefright/MediaSource.h>

#include <media/stagefright/MetaData.h>

#include <binder/IServiceManager.h>

#include <media/IMediaPlayerService.h>

#include <gui/ISurfaceComposer.h>

#include <gui/SurfaceComposerClient.h>

#include <gui/Surface.h>

#include <fcntl.h>

#include <ui/DisplayInfo.h>

using namespace android;

struct MyStreamSource : public BnStreamSource {

// Object assumes ownership of fd.

MyStreamSource(int fd);

virtual void setListener(const sp<IStreamListener> &listener);

virtual void setBuffers(const Vector<sp<IMemory> > &buffers);

virtual void onBufferAvailable(size_t index);

protected:

virtual ~MyStreamSource();

private:

int mFd;

off64_t mFileSize;

uint64_t mNumPacketsSent;

sp<IStreamListener> mListener;

Vector<sp<IMemory> > mBuffers;

DISALLOW_EVIL_CONSTRUCTORS(MyStreamSource);

};

MyStreamSource::MyStreamSource(int fd)

: mFd(fd),

mFileSize(0),

mNumPacketsSent(0) {

CHECK_GE(fd, 0);

mFileSize = lseek64(fd, 0, SEEK_END);

lseek64(fd, 0, SEEK_SET);

}

MyStreamSource::~MyStreamSource() {

close(mFd);

mFd = -1;

}

void MyStreamSource::setListener(const sp<IStreamListener> &listener) {

mListener = listener;

}

void MyStreamSource::setBuffers(const Vector<sp<IMemory> > &buffers) {

mBuffers = buffers;

}

void MyStreamSource::onBufferAvailable(size_t index) {

CHECK_LT(index, mBuffers.size());

#if 0

if (mNumPacketsSent >= 20000) {

ALOGI("signalling discontinuity now");

off64_t offset = 0;

CHECK((offset % 188) == 0);

lseek(mFd, offset, SEEK_SET);

sp<AMessage> extra = new AMessage;

extra->setInt32(IStreamListener::kKeyFormatChange, 0);

mListener->issueCommand(

IStreamListener::DISCONTINUITY, false /* synchronous */, extra);

mNumPacketsSent = 0;

}

#endif

sp<IMemory> mem = mBuffers.itemAt(index);

ssize_t n = read(mFd, mem->pointer(), mem->size());

if (n <= 0) {

mListener->issueCommand(IStreamListener::EOS, false /* synchronous */);

} else {

mListener->queueBuffer(index, n);

mNumPacketsSent += n / 188;

}

}

////////////////////////////////////////////////////////////////////////////////

struct MyConvertingStreamSource : public BnStreamSource {

MyConvertingStreamSource(const char *filename);

virtual void setListener(const sp<IStreamListener> &listener);

virtual void setBuffers(const Vector<sp<IMemory> > &buffers);

virtual void onBufferAvailable(size_t index);

protected:

virtual ~MyConvertingStreamSource();

private:

Mutex mLock;

Condition mCondition;

sp<IStreamListener> mListener;

Vector<sp<IMemory> > mBuffers;

sp<MPEG2TSWriter> mWriter;

ssize_t mCurrentBufferIndex;

size_t mCurrentBufferOffset;

List<size_t> mBufferQueue;

static ssize_t WriteDataWrapper(void *me, const void *data, size_t size);

ssize_t writeData(const void *data, size_t size);

DISALLOW_EVIL_CONSTRUCTORS(MyConvertingStreamSource);

};

////////////////////////////////////////////////////////////////////////////////

MyConvertingStreamSource::MyConvertingStreamSource(const char *filename)

: mCurrentBufferIndex(-1),

mCurrentBufferOffset(0) {

sp<DataSource> dataSource = DataSource::CreateFromURI(filename);

CHECK(dataSource != NULL);

sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);

CHECK(extractor != NULL);

mWriter = new MPEG2TSWriter(

this, &MyConvertingStreamSource::WriteDataWrapper);

for (size_t i = 0; i < extractor->countTracks(); ++i) {

const sp<MetaData> &meta = extractor->getTrackMetaData(i);

const char *mime;

CHECK(meta->findCString(kKeyMIMEType, &mime));

if (strncasecmp("video/", mime, 6) && strncasecmp("audio/", mime, 6)) {

continue;

}

CHECK_EQ(mWriter->addSource(extractor->getTrack(i)), (status_t)OK);

}

CHECK_EQ(mWriter->start(), (status_t)OK);

}

MyConvertingStreamSource::~MyConvertingStreamSource() {

}

void MyConvertingStreamSource::setListener(

const sp<IStreamListener> &listener) {

mListener = listener;

}

void MyConvertingStreamSource::setBuffers(

const Vector<sp<IMemory> > &buffers) {

mBuffers = buffers;

}

ssize_t MyConvertingStreamSource::WriteDataWrapper(

void *me, const void *data, size_t size) {

return static_cast<MyConvertingStreamSource *>(me)->writeData(data, size);

}

ssize_t MyConvertingStreamSource::writeData(const void *data, size_t size) {

size_t totalWritten = 0;

while (size > 0) {

Mutex::Autolock autoLock(mLock);

if (mCurrentBufferIndex < 0) {

while (mBufferQueue.empty()) {

mCondition.wait(mLock);

}

mCurrentBufferIndex = *mBufferQueue.begin();

mCurrentBufferOffset = 0;

mBufferQueue.erase(mBufferQueue.begin());

}

sp<IMemory> mem = mBuffers.itemAt(mCurrentBufferIndex);

size_t copy = size;

if (copy + mCurrentBufferOffset > mem->size()) {

copy = mem->size() - mCurrentBufferOffset;

}

memcpy((uint8_t *)mem->pointer() + mCurrentBufferOffset, data, copy);

mCurrentBufferOffset += copy;

if (mCurrentBufferOffset == mem->size()) {

mListener->queueBuffer(mCurrentBufferIndex, mCurrentBufferOffset);

mCurrentBufferIndex = -1;

}

data = (const uint8_t *)data + copy;

size -= copy;

totalWritten += copy;

}

return (ssize_t)totalWritten;

}

void MyConvertingStreamSource::onBufferAvailable(size_t index) {

Mutex::Autolock autoLock(mLock);

mBufferQueue.push_back(index);

mCondition.signal();

if (mWriter->reachedEOS()) {

if (mCurrentBufferIndex >= 0) {

mListener->queueBuffer(mCurrentBufferIndex, mCurrentBufferOffset);

mCurrentBufferIndex = -1;

}

mListener->issueCommand(IStreamListener::EOS, false /* synchronous */);

}

}

////////////////////////////////////////////////////////////////////////////////

struct MyClient : public BnMediaPlayerClient {

MyClient()

: mEOS(false) {

}

virtual void notify(int msg, int ext1, int ext2, const Parcel *obj) {

Mutex::Autolock autoLock(mLock);

if (msg == MEDIA_ERROR || msg == MEDIA_PLAYBACK_COMPLETE) {

mEOS = true;

mCondition.signal();

}

}

void waitForEOS() {

Mutex::Autolock autoLock(mLock);

while (!mEOS) {

mCondition.wait(mLock);

}

}

protected:

virtual ~MyClient() {

}

private:

Mutex mLock;

Condition mCondition;

bool mEOS;

DISALLOW_EVIL_CONSTRUCTORS(MyClient);

};

int main(int argc, char **argv) {

android::ProcessState::self()->startThreadPool();

DataSource::RegisterDefaultSniffers();

if (argc != 2) {

fprintf(stderr, "Usage: %s filename\n", argv[0]);

return 1;

}

sp<SurfaceComposerClient> 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;

ALOGV("display is %d x %d\n", displayWidth, displayHeight);

sp<SurfaceControl> 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();

sp<Surface> surface = control->getSurface();

CHECK(surface != NULL);

sp<IServiceManager> sm = defaultServiceManager();

sp<IBinder> binder = sm->getService(String16("media.player"));

sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);

CHECK(service.get() != NULL);

sp<MyClient> client = new MyClient;

sp<IStreamSource> source;

char prop[PROPERTY_VALUE_MAX];

bool usemp4 = property_get("media.stagefright.use-mp4source", prop, NULL) &&

(!strcmp(prop, "1") || !strcasecmp(prop, "true"));

size_t len = strlen(argv[1]);

if ((!usemp4 && len >= 3 && !strcasecmp(".ts", &argv[1][len - 3])) ||

(usemp4 && len >= 4 &&

(!strcasecmp(".mp4", &argv[1][len - 4])

|| !strcasecmp(".3gp", &argv[1][len- 4])

|| !strcasecmp(".3g2", &argv[1][len- 4])))) {

int fd = open(argv[1], O_RDONLY);

if (fd < 0) {

fprintf(stderr, "Failed to open file ‘%s‘.", argv[1]);

return 1;

}

source = new MyStreamSource(fd);

} else {

printf("Converting file to transport stream for streaming...\n");

source = new MyConvertingStreamSource(argv[1]);

}

sp<IMediaPlayer> player =

service->create(client, 0);

if (player != NULL && player->setDataSource(source) == NO_ERROR) {

player->setVideoSurfaceTexture(surface->getIGraphicBufferProducer());

player->start();

client->waitForEOS();

player->stop();

} else {

fprintf(stderr, "failed to instantiate player.\n");

}

composerClient->dispose();

return 0;

}

时间: 2024-10-16 11:20:31

【第一篇章-android平台buffer播放探索】buffer play demo的相关文章

【第一篇章-android平台buffer播放探索】Steaming Source Player

为了在android平台找到buffer播放方式,花了不少功夫,在这个探索过程中,果断否定了native media的方式,在找到Steaming Source Player之前还进行了一次Nuplayer的尝试,我们把android里面的Nuplayer单独独立出来可进行NDK编译用加载库的方式跑起来,在三星以及小米的手机上跑是没有问题的,整体功能还算OK,但是想android平台通吃的话,会遇到很多兼容性问题,因为Nuplayer要对接底层OMX相关的模块,并不是每一家对OMX的封装做的到位

【媒体应用:Android平台Vitamio播放器探索之路系列】之二:Vitamio API:核心类

作者:郭嘉 邮箱:[email protected] 博客:http://blog.csdn.net/allenwells github:https://github.com/AllenWells [媒体应用:Android平台Vitamio播放器探索之路系列]章节索引 [媒体应用:Android平台Vitamio播放器探索之路系列]之二:Vitamio API 注意:为了方便大家对照源码阅览,Vitamio API的介绍按照Vitamio源码程序包名来划分类别.如下图所示: 一 MediaPl

【媒体应用:Android平台Vitamio播放器探索之路系列】之一:Vitamio开篇

作者:郭嘉 邮箱:[email protected] 博客:http://blog.csdn.net/allenwells github:https://github.com/AllenWells [媒体应用:Android平台Vitamio播放器探索之路系列]章节索引 [媒体应用:Android平台Vitamio播放器探索之路系列]之一:Vitamio开篇 一 Vitamio简介 对于媒体播放器的开发而言,Android提供了内置的VideoView组件和MediaPlayer组件,但是功能和

【第二篇章-android平台buffer播放实现】buffer play demo

通过第一篇章的探索,可以明确下在android平台下是可以实现buffer播放的,它支持的android的版本是从4.0开始的,因此,android平台可以实现android4.0以上平台的buffer播放.我已经实现了一个DEMO,支持4.1到4.4,大家可以使用下,需要的人多的话可以分享源码.

【第四篇章-android平台MediaCodec】编解码逻辑认识

假设我们需要做的东西是一个媒体文件合成器,功能是先截取出多段媒体文件中的某些片段,再将这些片段合成为一个新的媒体文件,那么我们可以用mediacodec来帮我们做些事情. MediaCodec进行编解码的大体逻辑是这样的: 主要函数的调用逻辑如下: 从图可知,我们先用解码器将多段outputBuffer数据拿出来,然后再用编码器将它们放到一个文件里去.如果还要加上叠加字幕.特技处理等的效果,那么也是先拿到outputBuffer,然后处理,最后将处理后的数据喂给编码器,生成最终文件.

【第四篇章-android平台MediaCodec】判断是否支持硬解码代码

public boolean isSupportMediaCodecHardDecoder(){ boolean isHardcode = false; //读取系统配置文件/system/etc/media_codecc.xml File file = new File("/system/etc/media_codecs.xml"); InputStream inFile = null; try { in = new FileInputStream(file); } catch (E

【第四篇章-android平台MediaCodec】解决Observer died. Quickly, do something, ... anything...

当出现!!!Observer died. Quickly, do something, ... anything...说明你的程序已经出现严重异常了,那会是什么情况呢?这个问题困扰了我许久,后来原来是使用了mediacodec的非公有API导致的,去掉这个API即OK. 版权声明:本文为博主原创文章,未经博主允许不得转载.

Unity Handheld.PlayFullScreenMovie 视频播放 (android平台 资源存放路径)

对于在Unity中播放视频的用法,这里有篇文章说的很详细了,参考 Unity3D研究院之两种方式播放游戏视频 本文主要针对Android平台调用的视频资源存放位置写个备注. 移动平台视频播放调用的接口如下: public static bool PlayFullScreenMovie(string path, Color bgColor, FullScreenMovieControlMode controlMode, FullScreenMovieScalingMode scalingMode)

【流媒体开发】VLC Media Player - Android 平台源码编译 与 二次开发详解 (提供详细800M下载好的编译源码及eclipse可调试播放器源码下载)

作者 : 韩曙亮  博客地址 : http://blog.csdn.net/shulianghan/article/details/42707293 转载请注明出处 : http://blog.csdn.net/shulianghan VLC 二次开发 视频教程 : http://edu.csdn.net/course/detail/355 博客总结 : -- 本博客目的 : 让 Android 开发者通过看本博客能够掌握独立移植 VLC Media Player 核心框架到自己的 app 中,