MyWork

이지영 2009. 11. 23. 14:17

어느날 예전 소스랑 비교하다 보니 최근에 받은 소스는 Donut으로 패치되었다는 걸 알았다.

repo init -u git://android.git.kernel.org/platform/manifest.git

 

이전 Cupcake 소스 받으려면 다음과 같이 해야 한다.

repo init -u git://android.git.kernel.org/platform/manifest.git -b cupcake

출처 : 일상 속의 행복
글쓴이 : 바람진 원글보기
메모 :

 
 
 

MyWork

이지영 2009. 11. 23. 10:27

OpenCore를 정리하다가 잠깐 쉬는 개념으로 비디오 플레이어 응용 프로그램의 파일 구조를 정리해 본다.

 

라이브러리로 묶여 있는 부분, JNI 파트, JAVA 클래스 등 여기 저기 소스가 나뉘어져 있어서 JAVA Application에서 아래쪽으로 어떻게 연결되는지 먼저 정리한다. 순서대로 정리하면 다음과 같다.

 

1. packages/apps/Camera/src/com/android/camera/MovieView.java

2. frameworks/base/core/java/android/widget/VideoView.java

3. frameworks/base/media/java/android/media/MediaPlayer.java

4. frameworks/base/media/jni/android_media_MediaPlayer.cpp

5. frameworks/base/media/libmedia/mediaplayer.cpp

6. frameworks/base/media/libmedia/IMediaPlayer.cpp

 

* 헤더 파일은 framework/base/include/media/ 를 참조한다.

 

MovieView.java는 비디오 플레이 소스이다. 다음과 같이 라이브러리를 import! 한다.

 

import! android.widget.VideoView;

 

public class MovieView extends Activity implements MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener
{
    private static final String TAG = "MovieView";
    // Copied from MediaPlaybackService in the Music Player app. Should be public, but isn't.
    private static final String SERVICECMD = "com.android.music.musicservicecommand";
    private static final String CMDNAME = "command";
    private static final String CMDPAUSE = "pause";

    private VideoView   mVideoView;

 

    ...... 

 

VideoView 클래스는 다시 다음 라이브러리를 import! 한다. 우리는 위에 정리한 파일 관계만 쫓아갈 것이다.

 

import! android.media.MediaPlayer;
import! android.media.MediaPlayer.OnCompletionListener;
import! android.media.MediaPlayer.OnErrorListener;

 

 

public class VideoView extends SurfaceView implements MediaPlayerControl {
    private String TAG = "VideoView";
    // settable by the client
    private Uri         mUri;
    private int         mDuration;

    // All the stuff we need for playing and showing a video
    private SurfaceHolder mSurfaceHolder = null;
    private MediaPlayer mMediaPlayer = null;
    private boolean     mIsPrepared;

 

    ...... 

 

 

MediaPlayer 클래스는 JNI를 이용하는 클래스이다. android_media_MediaPlayer.cpp는 JNI 관련 파일이고 이를 JAVA에서 사용하기 쉽도록 구현한 클래스가 MediaPlayer이다.

 

public class MediaPlayer
{   
    static {
        System.loadLibrary("media_jni");
    }


    ......   

 

 

android_media_MediaPlayer.cpp는 JNI 파일이므로 보통 가음과 같은 형태로 API 들이 만들어져 있다.

 

static void
android_media_MediaPlayer_start(JNIEnv *env, jobject thiz)
{
    LOGV("start");
    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
    if (mp == NULL ) {
        jniThrowException(env, "java/lang/IllegalStateException", NULL);
        return;
    }
    process_media_player_call( env, thiz, mp->start(), NULL, NULL );
}

 

static void
android_media_MediaPlayer_stop(JNIEnv *env, jobject thiz)
{
    LOGV("stop");
    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
    if (mp == NULL ) {
        jniThrowException(env, "java/lang/IllegalStateException", NULL);
        return;
    }
    process_media_player_call( env, thiz, mp->stop(), NULL, NULL );   
}

 

이러한 함수들은 Table로 다시 관리된다.

 

static JNINativeMethod gMethods[] = {
    {"setDataSource",       "(Ljava/lang/String;)V",            (void *)android_media_MediaPlayer_setDataSource},
    {"setDataSource",       "(Ljava/io/FileDescriptor;JJ)V",    (void *)android_media_MediaPlayer_setDataSourceFD},
    {"prepare",             "()V",                              (void *)android_media_MediaPlayer_prepare},
    {"prepareAsync",        "()V",                              (void *)android_media_MediaPlayer_prepareAsync},
    {"_start",              "()V",                              (void *)android_media_MediaPlayer_start},
    {"_stop",               "()V",                              (void *)android_media_MediaPlayer_stop},
    {"getVideoWidth",       "()I",                              (void *)android_media_MediaPlayer_getVideoWidth},
    {"getVideoHeight",      "()I",                              (void *)android_media_MediaPlayer_getVideoHeight},
    {"seekTo",              "(I)V",                             (void *)android_media_MediaPlayer_seekTo},
    {"_pause",              "()V",                              (void *)android_media_MediaPlayer_pause},
    {"isPlaying",           "()Z",                              (void *)android_media_MediaPlayer_isPlaying},
    {"getCurrentPosition",  "()I",                              (void *)android_media_MediaPlayer_getCurrentPosition},
    {"getDuration",         "()I",                              (void *)android_media_MediaPlayer_getDuration},
    {"_release",            "()V",                              (void *)android_media_MediaPlayer_release},
    {"_reset",              "()V",                              (void *)android_media_MediaPlayer_reset},
    {"setAudioStreamType",  "(I)V",                             (void *)android_media_MediaPlayer_setAudioStreamType},
    {"setLooping",          "(Z)V",                             (void *)android_media_MediaPlayer_setLooping},
    {"isLooping",           "()Z",                              (void *)android_media_MediaPlayer_isLooping},
    {"setVolume",           "(FF)V",                            (void *)android_media_MediaPlayer_setVolume},
    {"getFrameAt",          "(I)Landroid/graphics/Bitmap;",     (void *)android_media_MediaPlayer_getFrameAt},
    {"native_setup",        "(Ljava/lang/Object;)V",            (void *)android_media_MediaPlayer_native_setup},
    {"native_finalize",     "()V",                              (void *)android_media_MediaPlayer_native_finalize},
};

 

android_media_MediaPlayer_start()를 보면 getMediaPlayer()로부터 포인터를 얻어서 함수를 실행하는 구조인데

이와 관련된 내용은 mediaplayer.cpp에 구현되어 있으며 여기에서 메시지를 전달하는 내용이 IMediaPlayer.cpp에 기술 되어 있다.

 

사실 PV Player의 PlayerDriver와 관련된 부분도 더 정리하고 싶었는데 이 부분은 아직 찾지 못했다.

 

출처 : 일상 속의 행복
글쓴이 : 바람진 원글보기
메모 :

 
 
 

MyWork

이지영 2009. 11. 23. 10:25

PVPlayerEngine이 실행되면 하나의 Thread가 동작하면서 메시지를 기다리고 메시지가 큐에 쌓이면 이를 처리하도록 되어 있다.

 

PVPlayerEngine은 이를 위해서 iPendingCmds 큐를 가지고 있다.

 

AddDataSource(), Init() 등과 같은 함수들이 Command를 메시지 큐에 넣도록 AddCommandToQueue() 함수를 제공한다.

 

PVCommandId PVPlayerEngine::AddCommandToQueue(int32 aCmdType, OsclAny* aContextData,
        Oscl_Vector<PVPlayerEngineCommandParamUnion, OsclMemAllocator>* aParamVector,
        const PVUuid* aUuid, bool aAPICommand, PVCommandId* aId)

{

    ......

 

    PVPlayerEngineCommand cmd(aCmdType, commandId, aContextData, aParamVector, aAPICommand);
    if (aUuid)
    {
        cmd.SetUuid(*aUuid);
    }

    int32 leavecode = 0;
    OSCL_TRY(leavecode, iPendingCmds.push(cmd));

 

}

 

GetSDKInfo()라는 함수를 통해서 처리 과정을 보면 다음과 같다.

PVCommandId PVPlayerEngine::GetSDKInfo(PVSDKInfo &aSDKInfo, const OsclAny* aContextData)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVPlayerEngine::GetSDKInfo()"));
    Oscl_Vector<PVPlayerEngineCommandParamUnion, OsclMemAllocator> paramvec;
    paramvec.reserve(1);
    paramvec.clear();
    PVPlayerEngineCommandParamUnion param;
    param.pOsclAny_value = (OsclAny*) & aSDKInfo;
    paramvec.push_back(param);
    return AddCommandToQueue(PVP_ENGINE_COMMAND_GET_SDK_INFO, (OsclAny*)aContextData, ¶mvec);
}

 

GetSDKInfo()를 호출하면 PVP_ENGINE_COMMAND_GET_SDK_INFO Command를 메시지 큐에 넣는다 이 때 결과값을 돌려받기 위하여  aParamVector에 반활될 위치(aSDKInfo)를 pOsclAny_value 형태로 전달한다.

 

이렇게 메시지 큐로 전달된 Commnad는 Run() 함수에서 처리된다.

void PVPlayerEngine::Run()
{

    ......

 

    // Handle other requests normally
    if (!iPendingCmds.empty() && iCurrentCmd.empty())
    {
        // Retrieve the first pending command from queue
        PVPlayerEngineCommand cmd(iPendingCmds.top());
        iPendingCmds.pop();

 

        // Put in on the current command queue
        leavecode = 0;
        OSCL_TRY(leavecode, iCurrentCmd.push_front(cmd));
        OSCL_FIRST_CATCH_ANY(leavecode,
                             PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVPlayerEngine::Run() Command could not be pushed onto iCurrentCmd vector"));
                             EngineCommandCompleted(cmd.GetCmdId(), cmd.GetContext(), PVMFErrNoMemory);
                             return;);

 

        // Process the command according to the cmd type
        PVMFStatus cmdstatus = PVMFSuccess;
        bool ootsync = false;
        PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVPlayerEngine::Run() Processing command with type=%d", cmd.GetCmdType()));
        switch (cmd.GetCmdType())
        {
            case PVP_ENGINE_COMMAND_GET_SDK_INFO:
                cmdstatus = DoGetSDKInfo(cmd);
                break;

 

             ...... 

       

}

 

즉 CmdType에 따라서 DoXXX() 함수를 호출하게 된다.

PVMFStatus PVPlayerEngine::DoGetSDKInfo(PVPlayerEngineCommand& aCmd)
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVPlayerEngine::DoGetSDKInfo() In"));

 

    PVSDKInfo* sdkinfo = (PVSDKInfo*)(aCmd.GetParam(0).pOsclAny_value);
    if (sdkinfo == NULL)
    {
        PVLOGGER_LOGMSG(PVLOGMSG_INST_HLDBG, iLogger, PVLOGMSG_ERR, (0, "PVPlayerEngine::DoGetSDKInfo() Passed in parameter invalid."));
        return PVMFErrArgument;
    }

 

    // Set the SDK info to the ones defined in the header file pv_player_sdkinfo.h generated at build time
    sdkinfo->iLabel = PVPLAYER_ENGINE_SDKINFO_LABEL;
    sdkinfo->iDate = PVPLAYER_ENGINE_SDKINFO_DATE;

 

    EngineCommandCompleted(iCurrentCmd[0].GetCmdId(), iCurrentCmd[0].GetContext(), PVMFSuccess);

    PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE, (0, "PVPlayerEngine::DoGetSDKInfo() Out"));
    return PVMFSuccess;
}

 

이러한 함수들은 aCmd를 통해서 Command 및 Parameter 들을 전달받는다. 처리된 결과(이 경우는 SDKINFO)는 전달받은 pOsclAny_value 값을 다시 casting 하여 저장한다.

DoGetSDKInfo() 함수를 보면 하나의 Command가 완료되기 위해서는 EngineCommandCompleted()를 호출함을 알 수 있다. EngineCommandCompleted()는 iCurrentCmd에서 Command를 삭제하고 Command Type에 따라서 필요한 Event를 날리거나 Observer가 있다면 CommandComplete를 호출하도록 되어 있다.

void PVPlayerEngine::EngineCommandCompleted(PVCommandId aId, OsclAny* aContext, PVMFStatus aStatus, PVInterface* aExtInterface, OsclAny* aEventData, int32 aEventDataSize)

{

    ......

 

    PVPlayerEngineCommand completedcmd(iCurrentCmd[0]);
    iCurrentCmd.erase(iCurrentCmd.begin());
    if (!iPendingCmds.empty())
    {
        RunIfNotReady();
    }

 

    ......

 

    // Send informational event or send other callback if needed
    switch (completedcmd.GetCmdType())
    {
        case PVP_ENGINE_COMMAND_PAUSE_DUE_TO_ENDOFCLIP:
            SendEndOfClipInfoEvent(aStatus, aExtInterface);
            break;

        case PVP_ENGINE_COMMAND_PAUSE_DUE_TO_ENDTIME_REACHED:
            SendEndTimeReachedInfoEvent(aStatus, aExtInterface);
            break;

        case PVP_ENGINE_COMMAND_PAUSE_DUE_TO_BUFFER_UNDERFLOW:
            SendSourceUnderflowInfoEvent(aStatus, aExtInterface);
            break;

        case PVP_ENGINE_COMMAND_RESUME_DUE_TO_BUFFER_DATAREADY:
            SendSourceDataReadyInfoEvent(aStatus, aExtInterface);
            break;

        case PVP_ENGINE_COMMAND_CAPCONFIG_SET_PARAMETERS:
            // Send callback to the specified observer
            if (iCfgCapCmdObserver)
            {
                iCfgCapCmdObserver->SignalEvent(aId);
            }
            break;

        default:
            // None to be sent
            break;
    }

     // Send the command completed event
    if (iCmdStatusObserver)
    {
        if (aId != -1 && completedcmd.IsAPICommand())
        {
            PVLOGGER_LOGMSG(PVLOGMSG_INST_LLDBG, iLogger, PVLOGMSG_STACK_TRACE,
                            (0, "PVPlayerEngine::EngineCommandCompleted() Notifying engine command as completed. CmdId %d Status %d", aId, aStatus));
            PVCmdResponse cmdcompleted(aId, aContext, aStatus, aExtInterface, aEventData, aEventDataSize);
            iCmdStatusObserver->CommandCompleted(cmdcompleted);

        }

 

    ......

}

 

위의 예제의 GetSDKInfo()처럼 PVPlayerEngine에서 바로 처리할 수 있는 경우 말고, 만약 Node 들에 다시 명령을 주어서 그 결과에 따라 Command가 종료되어야 하는 경우는 어떻게 처리될까?

 

이를 위하여 PVPlayerEngine은 PVMFNodeInterface 클래스를 이용한다.

PVPlayerEngine에서 HandleXXX() 함수들이 대부분 이와 관련되어 있다.

 

특히 HandleXXX() 함수는 4개 그룹으로 구분할 수 있다. 1개는 Datapath와 관련되어 있고 나머지 3개가 NodeInterface와 관련되어 있다.

HandlePlayerDatapathEvent()

 

HandleNodeInformationEvent()

HandleNodeErrorEvent()

NodeCommandComplete()

 

이 4개의 함수는 조건에 따라 각각의 HandleXXX() 함수를 호출하게 된다. 이를 정리하면 다음과 같다.

 

HandlePlayerDatapathEvent()

- HandleDatapathPause()

- HandleDatapathPrepare()

- HandleDatapathResume()

- HandleDatapathReset()

- HandleDatapathStart()

- HandleDatapathStop()

- HandleDatapathTeardown()

 

HandleNodeInformationEvent()

- HandleSourceNodeInfoEvent()

- HandleDecNodeInfoEvent()

- HandleSinkNodeEvent()

 

HandleNodeErrorEvent()

- HandleSourceNodeErrorEvent()

- HandleDecNodeErrorEvent()

- HandleSinkNodeErrorEvent()

 

NodeCommandComplete() : 그 외 모든 HandelXXX()

- HandleSinkNodeXXX()

- HandleSourceNodeXXX()

- HandleDecNodeXXX()

 

HandleNodeInformationEvent(), HandleNodeErrorEvent(), NodeCommandComplete()는 Pvmf_node_interface.h에서 정의한 PVMFNodeInfoEventObserver, PVMFNodeErrorEventObserver, PVMFNodeCmdStatusObserver클래스에서 정의한 순수 가상 함수이다. 

 

class PVMFNodeErrorEventObserver
{
    public:
        /**
         * Handle an error event that has been generated.
         *
         * @param "aEvent" "The event to be handled."
         */
        virtual void HandleNodeErrorEvent(const PVMFAsyncEvent& aEvent) = 0;

        virtual ~PVMFNodeErrorEventObserver() {}
};

 

즉 Node Interface를 위한 이들 클래스는 함수 원형만 선언해 두고 실제 구현은 세개의 Node Interface 클래스로부터 파생된 PVPlayerEngine 클래스에서 가상 함수를 구체화 한 것이다. (음 이 부분은 C++을 잘 모르면 자꾸 헷갈린다.)

 

이렇게 정의된 HandleNodeInformationEvent(), HandleNodeErrorEvent(), NodeCommandComplete()는 pvmf_node_interface.cpp에서 정의된 PVMFNodeInterface 클래스의 멤버 함수에서 호출된다. (사실 더 분석하려면 이부분에서 Session에 대해서도 좀 더 봐야 할 것 같은데 이 부분은 나중에 보자)

OSCL_EXPORT_REF void PVMFNodeInterface::ReportInfoEvent(PVMFAsyncEvent &aEvent)
{
    for (uint32 i = 0; i < iSessions.size(); i++)
    {
        PVMFAsyncEvent resp(PVMFInfoEvent
                            , aEvent.GetEventType()
                            , iSessions[i].iInfo.iInfoContext
                            , aEvent.GetEventExtensionInterface()
                            , aEvent.GetEventData()
                            , aEvent.GetLocalBuffer()
                            , aEvent.GetLocalBufferSize());
        if (iSessions[i].iInfo.iInfoObserver)
            iSessions[i].iInfo.iInfoObserver->HandleNodeInformationalEvent(resp);
    }
}

 

OSCL_EXPORT_REF void PVMFNodeInterface::ReportErrorEvent(PVMFAsyncEvent &aEvent)
{
    for (uint32 i = 0; i < iSessions.size(); i++)
    {
        PVMFAsyncEvent resp(PVMFErrorEvent
                            , aEvent.GetEventType()
                            , iSessions[i].iInfo.iErrorContext
                            , aEvent.GetEventExtensionInterface()
                            , aEvent.GetEventData()
                            , aEvent.GetLocalBuffer()
                            , aEvent.GetLocalBufferSize());
        if (iSessions[i].iInfo.iErrorObserver)
            iSessions[i].iInfo.iErrorObserver->HandleNodeErrorEvent(resp);
    }
}


OSCL_EXPORT_REF void PVMFNodeInterface::ReportCmdCompleteEvent(PVMFSessionId s, PVMFCmdResp &resp)
{
    for (uint32 i = 0; i < iSessions.size(); i++)
    {
        if (iSessions[i].iId == s)
        {
            if (iSessions[i].iInfo.iCmdStatusObserver)
                iSessions[i].iInfo.iCmdStatusObserver->NodeCommandCompleted(resp);
            break;
        }
    }
}

 

 ReportInfoEvent(), ReportErrorEvent(), ReportCmdCompleteEvent()는 PVMFNodeInterface를 상속받은 (파생된) Node 관련 클래스에서 대부분 재정의해서 사용하거나 PVPlayerEngine에서 정의된 것으로 사용한다.

 

HandlePlayerDatapathEvent()도 유사하다. Pv_player_datapath.h에 정의된 PVPlayerDatapathObserver 클래스를 쫗아가면 될 것 같다.

 

출처 : 일상 속의 행복
글쓴이 : 바람진 원글보기
메모 :
좋은 정보 감사합니다..
퍼갈께요^^;