当前位置:  编程技术>移动开发
本页文章导读:
    ▪Awesomeplayer 里解压跟显示video的有关分析        Awesomeplayer 里解压和显示video的有关分析播放一个视频开始时要调用prepare , 在prepare 里调用 TimedEventQueue::start() ,即mQueue.start() ,会启动一个线程,然后在TimedEventQueue::threadEntry() 会收事件,.........
    ▪ 前不久写的一个makefile,可以配置编成elf,静态库,动态库        最近写的一个makefile,可以配置编成elf,静态库,动态库#environment SHELL=/bin/bash MAKE=make #compiler CC=gcc AR=ar CR=cr #directory MAKE_DIR=$(PWD) SRC_DIR=$(MAKE_DIR)/src/ OBJ_DIR=$(MAKE_DIR)/obj/ LIB_DIR=$(MAKE_DIR)/lib/ INCLUD.........
    ▪ 利用Objective-C运行时hook函数的三种步骤       利用Objective-C运行时hook函数的三种方法方法一,hook已有公开头文件的类: 首先写一个Utility函数: #import <objc/runtime.h> inline void exchangeMethod(Class aClass, SEL oldSEL, SEL newSEL) { Method oldMethod = .........

[1]Awesomeplayer 里解压跟显示video的有关分析
    来源: 互联网  发布时间: 2014-02-18
Awesomeplayer 里解压和显示video的有关分析

播放一个视频开始时要调用prepare , 在prepare 里调用 TimedEventQueue::start() ,即mQueue.start() ,会启动一个线程,然后在TimedEventQueue::threadEntry() 会收事件,处理事件。

TimedEventQueue 是一个按照事件约定时间来执行事件携带动作的类。事件的约定时间存在 QueueItem::realtime_us 里,往TimedEventQueue发事件使用 TimedEventQueue::postTimedEvent ,该方法除this以外的第一个参数是事件,第二个参数是该事件约定的执行时间,它是按顺序往列表里填事件的,请看这段代码:

    TimedEventQueue::event_id TimedEventQueue::postTimedEvent() {
....

    List<QueueItem>::iterator it = mQueue.begin();
    while (it != mQueue.end() && realtime_us >= (*it).realtime_us) {
        ++it;
    }

    QueueItem item;
    item.event = event;
    item.realtime_us = realtime_us;

    if (it == mQueue.begin()) {
        mQueueHeadChangedCondition.signal();
    }

    mQueue.insert(it, item);

....

 }

在TimedEventQueue::threadEntry()按顺序查事件:

   List<QueueItem>::iterator it = mQueue.begin();
                eventID = (*it).event->eventID();

  按该事件约定时间延时后再取事件:event = removeEventFromQueue_l(eventID);

再执行事件携带的动作:event->fire(this, now_us);

 

事件携带的动作(Event::fire) 是在AwesomeEvent类里实现的

struct AwesomeEvent : public TimedEventQueue::Event {
    AwesomeEvent(
            AwesomePlayer *player,
            void (AwesomePlayer::*method)())
        : mPlayer(player),
          mMethod(method) {
    }

protected:
....

    virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) {
        (mPlayer->*mMethod)();
    }

private:
    AwesomePlayer *mPlayer;
    void (AwesomePlayer::*mMethod)();

  ...

};

看下面两行代码:

    virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) {
        (mPlayer->*mMethod)();
    }

mPlayer和mMethod 都是AwesomeEvent 的成员变量,(*mMethod)() 表示调用该指针指向的函数,  mPlayer-> 表示把mPlayer 指针作为(*mMethod)()的第一个参数:this。

其中有关视频解压和显示的事件是 AwesomePlayer::mVideoEvent。

我们看看该事件是如何创建的:

AwesomePlayer::AwesomePlayer(){

.....

   mVideoEvent = new AwesomeEvent(this, &AwesomePlayer::onVideoEvent);

}

可以看出 mVideoEvent 事件创建之后的 mPlayer是AwesomePlayer*, 而动作是AwesomePlayer::onVideoEvent。

我把AwesomePlayer::onVideoEvent()的代码简化和适当注释了一下放在了最后一段。开始通过调用mVideoSource->read(&mVideoBuffer, &options);来解压,  然后mVideoRenderer->render(mVideoBuffer);显示。中间有超时判断。不管哪个分支都有postVideoEvent_l()的调用,也就是说是每10ms就要触发一次解压和显示事件,然后根据视频帧的时戳决定是否延时。

在AwesomePlayer::onVideoEvent 里调用的 postVideoEvent_l(),原型是AwesomePlayer::postVideoEvent_l(int64_t delayUs=-1),声明缺省参数值是-1,所以在AwesomePlayer::onVideoEvent()不带参数调用的话,是以当时后延10ms的时间作为事件约定时间是来调用TimedEventQueue::postTimedEvent方法的。

通过 TimeSource *ts =    ((mFlags & AUDIO_AT_EOS) || !(mFlags & AUDIOPLAYER_STARTED))        ? &mSystemTimeSource : mTimeSource;

这一句可以看出视频是用音频的时间作为同步参考时钟的。

 

void AwesomePlayer::onVideoEvent() {
    Mutex::Autolock autoLock(mLock);
    if (mSeeking != NO_SEEK) {
        if (mVideoBuffer) {
            mVideoBuffer->release();
            mVideoBuffer = NULL;
        }
    }

    for (;;) {

        /// 这里做解压的动作,mVideoSource是一个基于OpenMax的decoder,在里面会通过连接的 DataSource 去读取原始的压缩数据,解压出可以显示的点阵数据。
        status_t err = mVideoSource->read(&mVideoBuffer, &options);  // mVideoSource is  a video decoder  
        options.clearSeekTo();
        break;
    }

    int64_t timeUs;
    CHECK(mVideoBuffer->meta_data()->findInt64(kKeyTime, &timeUs));

    mLastVideoTimeUs = timeUs;

    TimeSource *ts =
        ((mFlags & AUDIO_AT_EOS) || !(mFlags & AUDIOPLAYER_STARTED))
            ? &mSystemTimeSource : mTimeSource; // mTimeSource = mAudioPlayer; so, timeSource is from audio.

    if (mFlags & FIRST_FRAME) {
        modifyFlags(FIRST_FRAME, CLEAR);
        mSinceLastDropped = 0;
        mTimeSourceDeltaUs = ts->getRealTimeUs() - timeUs;
    }

    int64_t realTimeUs, mediaTimeUs;
    if (!(mFlags & AUDIO_AT_EOS) && mAudioPlayer != NULL
        && mAudioPlayer->getMediaTimeMapping(&realTimeUs, &mediaTimeUs)) {
        mTimeSourceDeltaUs = realTimeUs - mediaTimeUs;
    }

    if (wasSeeking == NO_SEEK) { //忽略这一帧的显示
        // Let's display the first frame after seeking right away.
        int64_t nowUs = ts->getRealTimeUs() - mTimeSourceDeltaUs;
        int64_t latenessUs = nowUs - timeUs;

        if (latenessUs > 500000ll ) {
             mVideoBuffer->release(); // delete this mediabuffer
            mVideoBuffer = NULL;

            mSeeking = SEEK_VIDEO_ONLY;
            mSeekTimeUs = mediaTimeUs;

            ///给自己发一个10ms之后执行的事件。
            postVideoEvent_l();
            return;
        }

        if (latenessUs > 40000) { //忽略这一帧的显示
            // We're more than 40ms late.
                 mVideoBuffer->release(); // delete this mediabuffer
                mVideoBuffer = NULL;
                 ++mStats.mNumVideoFramesDropped;          

           ///给自己发一个10ms之后执行的事件。
                 postVideoEvent_l();
                return;         
        }
        if (latenessUs < -10000) {
            // We're more than 10ms early.          

    ///给自己发一个10ms之后执行的事件。
            postVideoEvent_l(10000); // to display after a while
            return;
        }
    }

    if ((mNativeWindow != NULL)
            && (mVideoRendererIsPreview || mVideoRenderer == NULL)) {
        mVideoRendererIsPreview = false;

        initRenderer_l();
    }

    if (mVideoRenderer != NULL) {
        mSinceLastDropped++;

      //// 通过渲染库把点阵数据显示出来
        mVideoRenderer->render(mVideoBuffer); // display it
    }

    mVideoBuffer->release();
    mVideoBuffer = NULL;

    if (wasSeeking != NO_SEEK && (mFlags & SEEK_PREVIEW)) {
        modifyFlags(SEEK_PREVIEW, CLEAR);
        return;
    }

    ///给自己发一个10ms之后执行的事件。

    postVideoEvent_l();
}


    
[2] 前不久写的一个makefile,可以配置编成elf,静态库,动态库
    来源: 互联网  发布时间: 2014-02-18
最近写的一个makefile,可以配置编成elf,静态库,动态库

#environment
SHELL=/bin/bash
MAKE=make

#compiler
CC=gcc
AR=ar
CR=cr


#directory
MAKE_DIR=$(PWD)
SRC_DIR=$(MAKE_DIR)/src/
OBJ_DIR=$(MAKE_DIR)/obj/
LIB_DIR=$(MAKE_DIR)/lib/
INCLUDE_DIR=$(MAKE_DIR)/includes
DEBUG_DIR=$(MAKE_DIR)/debug/
RELEASE_DIR=$(MAKE_DIR)/release/

#vpath
vpath %.cpp $(SRC_DIR)
vpath %.o $(OBJ_DIR)
vpath %.d $(OBJ_DIR)

#VPATH = $(SRC_DIR) $(OBJ_DIR)

#include
INCLUDE=-I$(INCLUDE_DIR)

#src file
SRC_FILES:=$(wildcard $(SRC_DIR)*.cpp)

#obj file
OBJ_FILES:=$(notdir $(patsubst %.cpp, %.o, $(SRC_FILES)))

#depend file, which generated by g++ -MMD xx.cpp $(INCLUDE)
DEPEND_FILES:=$(notdir $(patsubst %.cpp, %.d, $(SRC_FILES)))

#lib, which used for link.
LIBS:=
LIB=-L$(LIB_DIR) $(LIBS)

#LOCAL_MODULE_ELF, which means the final executable file for this module
LOCAL_MODULE_ELF:=calc

LOCAL_MODULE_STATIC_LIB:=libcalc.a

LOCAL_MODULE_DYNAMIC_LIB:=libcalc.so


#flag
FLAG_DEBUG=-g
FLAG_COMPLE=-c
FLAG_LINK=

#debug
DEBUG=1

ifeq ($(DEBUG),1)
OUTPUT_DIR:=$(DEBUG_DIR)
FLAG_COMPLE:=$(FLAG_DEBUG) $(FLAG_COMPLE)
FLAG_LINK:=
else
OUTPUT_DIR:=$(RELEASE_DIR)
FLAG_COMPLE:=$(FLAG_COMPLE)
FLAG_LINK:=
endif

#output
OUTPUT_ELF=$(OUTPUT_DIR)$(LOCAL_MODULE_ELF)
OUTPUT_STATIC_LIB=$(OUTPUT_DIR)$(LOCAL_MODULE_STATIC_LIB)
OUTPUT_DYNAMIC_LIB=$(OUTPUT_DIR)$(LOCAL_MODULE_DYNAMIC_LIB)

.PHONY: all help dir elf staticlib dynamiclib obj move clean cleanall

all:dir obj

#放在任何命令之前
include $(DEPEND_FILES)

help:
 @echo "A simple makefile!"

dir:
 mkdir -p $(OBJ_DIR)
 mkdir -p $(LIB_DIR)
 mkdir -p $(DEBUG_DIR)
 mkdir -p $(RELEASE_DIR)
 
obj:$(OBJ_FILES)

move:
 mv -f *.o $(OBJ_DIR)
 mv -f *.d $(OBJ_DIR)

%.d:$(SRC_DIR)%.cpp
 @echo "~~~~~~~~~~~~~~~~~~"
 $(CC) -MM -MD $^ $(INCLUDE)
 
%.o:
 @echo "------------------"
 $(CC) -fPIC -c $< -o $@ $(INCLUDE)

elf:$(OBJ_FILES)
 @echo "building elf..."
 @echo "OBJ_FILES: $(OBJ_FILES)"
 @echo "LIB: $(LIB)"
 @echo "OUTPUT_ELF: $(OUTPUT_ELF)"
 $(CC) $(FLAG_LINK) -o $(OUTPUT_ELF) $(OBJ_FILES) $(LIB)
 
staticlib:$(OBJ_FILES)
 @echo "building static lib..."
 @echo "OUTPUT_LIB: $(OUTPUT_STATIC_LIB)"
 $(AR) $(CR) $(OUTPUT_STATIC_LIB) $(OBJ_FILES)
 
dynamiclib:$(OBJ_FILES)
 @echo "building dynamic lib..."
 @echo "OUTPUT_LIB: $(OUTPUT_DYNAMIC_LIB)"
 $(CC) -shared -o $(OUTPUT_DYNAMIC_LIB) $(OBJ_FILES)
 
clean:
 @rm -f $(OBJ_DIR)* *.d *.o
 @rm -f $(OUT)
 @clear 

cleanall:
 @rm -f $(OBJ_DIR)*
 @rm -f $(RELEASE_DIR)*
 @rm -f $(DEBUG_DIR)*


    
[3] 利用Objective-C运行时hook函数的三种步骤
    来源: 互联网  发布时间: 2014-02-18
利用Objective-C运行时hook函数的三种方法

方法一,hook已有公开头文件的类:

首先写一个Utility函数:

#import <objc/runtime.h>
inline void exchangeMethod(Class aClass, SEL oldSEL, SEL newSEL)
{
    Method oldMethod = class_getInstanceMethod(aClass, oldSEL);
    assert(oldMethod);
    Method newMethod = class_getInstanceMethod(aClass, newSEL);
    assert(newMethod);
    method_exchangeImplementations(oldMethod, newMethod);
}

现在,目标是hook UIWebView没公开的函数

- (void)webView:(id)arg1 didFinishLoadForFrame:(id)arg2;

因为已知类的声明,所以可以使用category:

@interface UIWebView (Hook)
+ (void)hook;
- (void)hook_webView:(id)arg1 didFinishLoadForFrame:(id)arg2;
@end
@implementation UIWebView (Hook)
+ (void)hook
{
    // hook UIWebView中表示一个HTML的frame加载完毕的函数
    exchangeMethod([UIWebViewclass],
                   @selector(webView:didFinishLoadForFrame:),
                   @selector(hook_webView:didFinishLoadForFrame:));
}
- (void)hook_webView:(id)arg1 didFinishLoadForFrame:(id)arg2
{
    // 因为交换了selector和implementation的映射,原样调一下本函数实际会调用被hook的函数。
    [self hook_webView:arg1 didFinishLoadForFrame:arg2];
    NSLog(@"webView:didFinishLoadForFrame:");
}
在程序启动的时候调用一下 [UIWebView hook] 即可。使用一个UIWebView打开一个网页,即会打印NSLog。


方法二,hook没有公开头文件的类,需要另建一个类作为新函数载体,然后先为被hook的类增加函数,再替换:
UIWebView体系中有一个类叫UIWebBrowserView,它是真正显示网页的UIView,并有部分函数是作为WebCore向外发送回调信息的接收者。
现我们去hook UIWebBrowserView的这个函数:

- (void)webView:(id)arg1 didFinishLoadForFrame:(id)arg2;
嗯,是的,这个函数和UIWebView的一样,实际上就是UIWebBrowserView会再调用通知到UIWebView的同名函数。
创建一个类,不要与被hook的类同名,例如加了个Hook后缀:
@interface UIWebBrowserViewHook : NSObject
+ (void)hook;
- (void)hook_webView:(id)arg1 didFinishLoadForFrame:(id)arg2;
@end

其中以hook_为前缀的新增函数的实现与方法一中相同,差别在类函数中:
@implementation UIWebBrowserViewHook
+ (void)hook
{
    Class aClass = objc_getClass("UIWebBrowserView");
    SEL sel = @selector(hook_webView:didFinishLoadForFrame:);
    // 为UIWebBrowserView增加函数
    class_addMethod(aClass, sel, class_getMethodImplementation([self class], sel), "v@:@@");
    // 交换实现
    exchangeMethod(aClass, @selector(webView:didFinishLoadForFrame:), sel);
}
在程序启动的时候调用一下 [UIWebBrowserViewHook hook] 即可。使用一个UIWebView打开一个网页,即会打印NSLog。


方法三,hook没有公开头文件的类,另建一个类作为新函数载体,用新函数替换旧函数,并把旧函数保存到静态变量里:

继续以UIWebBrowserView为例子。注意新函数可以与被hook的函数同名

@interface UIWebBrowserViewHook : NSObject
+ (void)hook;
- (void)webView:(id)arg1 didFinishLoadForFrame:(id)arg2;
@end
需要用到另一个Utility函数:
inline void replaceImplementation(Class newClass, Class hookedClass, SEL sel, IMP& oldImp)
{
    Method old = class_getInstanceMethod(hookedClass, sel);
    IMP newImp = class_getMethodImplementation(newClass, sel);
    oldImp = method_setImplementation(old, newImp);
}

当两个selector不同名时,以上函数再增加一个参数即可。

下面是实现:

@implementation UIWebBrowserViewHook
static IMP webView_didFinishLoadForFrame = NULL;
+ (void)hook
{
    Class hookedClass = objc_getClass("UIWebBrowserView");
    SEL sel = @selector(webView:didFinishLoadForFrame:);
    replaceImplementation([self class], hookedClass, sel, webView_didFinishLoadForFrame);
}

- (void)webView:(id)arg1 didFinishLoadForFrame:(id)arg2
{
    // 需要这样来调用被替换掉的原实现
    webView_didFinishLoadForFrame(self, @selector(webView:didFinishLoadForFrame:), arg1, arg2);
    NSLog(@"webView:didFinishLoadForFrame:");
}
@end
在程序启动的时候调用一下 [UIWebBrowserViewHook hook] 即可。使用一个UIWebView打开一个网页,即会打印NSLog。


三种方法的比较:

最方便的当然是第一种,但需要是hook有公开头文件的类。

方法二和方法三都新建了一个类,方法二需要描述selector的types,这个比较麻烦,如例子中的"v@:@@"表示返回值为void,第一和第二个参数都是id。方法三不用types,但要增加全局变量。


Objective-C的runtime参考资料:
http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40008048


    
最新技术文章:
▪Android开发之登录验证实例教程
▪Android开发之注册登录方法示例
▪Android获取手机SIM卡运营商信息的方法
▪Android实现将已发送的短信写入短信数据库的...
php iis7站长之家
▪Android根据电话号码获得联系人头像实例代码
▪Android中GPS定位的用法实例
▪Android实现退出时关闭所有Activity的方法
▪Android实现文件的分割和组装
▪Android录音应用实例教程
▪Android双击返回键退出程序的实现方法
▪Android实现侦听电池状态显示、电量及充电动...
▪Android获取当前已连接的wifi信号强度的方法
▪Android实现动态显示或隐藏密码输入框的内容
▪根据USER-AGENT判断手机类型并跳转到相应的app...
▪Android Touch事件分发过程详解
▪Android中实现为TextView添加多个可点击的文本
▪Android程序设计之AIDL实例详解
▪Android显式启动与隐式启动Activity的区别介绍
▪Android按钮单击事件的四种常用写法总结
▪Android消息处理机制Looper和Handler详解
▪Android实现Back功能代码片段总结
▪Android实用的代码片段 常用代码总结
▪Android实现弹出键盘的方法
▪Android中通过view方式获取当前Activity的屏幕截...
▪Android提高之自定义Menu(TabMenu)实现方法
▪Android提高之多方向抽屉实现方法
▪Android提高之MediaPlayer播放网络音频的实现方法...
▪Android提高之MediaPlayer播放网络视频的实现方法...
▪Android提高之手游转电视游戏的模拟操控
 


站内导航:


特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

©2012-2021,,E-mail:www_#163.com(请将#改为@)

浙ICP备11055608号-3