關於Platinum庫的MediaRender具體C++代碼實現探討

接上篇博文 NDK下 將Platinum SDK 編譯成so庫 (android - upnp)

講述瞭如何利用該代碼庫編譯給android程序調用的so庫,其中也提到了,在使用sample-upnp工程來測試生成的so庫是無效的

大家比對一下Platinum開發庫的Platinum\Source\Platform\Android\module\platinum\jni\platinum-jni.cpp

Platinum\Source\Tests\MediaRenderer\MediaRendererTest.cpp


platinum-jni.cpp

#include <assert.h>
#include <jni.h>
#include <string.h>
#include <sys/types.h>

#include "platinum-jni.h"
#include "Platinum.h"

#include <android/log.h>

/*----------------------------------------------------------------------
|   logging
+---------------------------------------------------------------------*/
NPT_SET_LOCAL_LOGGER("platinum.android.jni")

/*----------------------------------------------------------------------
|   functions
+---------------------------------------------------------------------*/
__attribute__((constructor)) static void onDlOpen(void)
{
}

/*----------------------------------------------------------------------
|    JNI_OnLoad
+---------------------------------------------------------------------*/
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
    NPT_LogManager::GetDefault().Configure("plist:.level=FINE;.handlers=ConsoleHandler;.ConsoleHandler.outputs=2;.ConsoleHandler.colors=false;.ConsoleHandler.filter=59");
    return JNI_VERSION_1_4;
}

/*
 * Class:     com_plutinosoft_platinum_UPnP
 * Method:    _init
 * Signature: ()J
 */
JNIEXPORT jlong JNICALL Java_com_plutinosoft_platinum_UPnP__1init(JNIEnv *env, jclass)
{
    NPT_LOG_INFO("init");
    PLT_UPnP* self = new PLT_UPnP();
    return (jlong)self;
}

/*
 * Class:     com_plutinosoft_platinum_UPnP
 * Method:    _start
 * Signature: (J)I
 */
JNIEXPORT jint JNICALL Java_com_plutinosoft_platinum_UPnP__1start(JNIEnv *, jclass, jlong _self)
{
    NPT_LOG_INFO("start");
    PLT_UPnP* self = (PLT_UPnP*)_self;
    
    return self->Start();
}

/*
 * Class:     com_plutinosoft_platinum_UPnP
 * Method:    _stop
 * Signature: (J)I
 */
JNIEXPORT jint JNICALL Java_com_plutinosoft_platinum_UPnP__1stop(JNIEnv *, jclass, jlong _self)
{
    NPT_LOG_INFO("stop");
    PLT_UPnP* self = (PLT_UPnP*)_self;
    
    return self->Stop();
}



MediaRendererTest.cpp

#include "PltUPnP.h"
#include "PltMediaRenderer.h"

#include <stdlib.h>

/*----------------------------------------------------------------------
|   globals
+---------------------------------------------------------------------*/
struct Options {
    const char* friendly_name;
} Options;

/*----------------------------------------------------------------------
|   PrintUsageAndExit
+---------------------------------------------------------------------*/
static void
PrintUsageAndExit(char** args)
{
    fprintf(stderr, "usage: %s [-f <friendly_name>]\n", args[0]);
    fprintf(stderr, "-f : optional upnp server friendly name\n");
    fprintf(stderr, "<path> : local path to serve\n");
    exit(1);
}

/*----------------------------------------------------------------------
|   ParseCommandLine
+---------------------------------------------------------------------*/
static void
ParseCommandLine(char** args)
{
    const char* arg;
    char**      tmp = args+1;

    /* default values */
    Options.friendly_name = NULL;

    while ((arg = *tmp++)) {
        if (!strcmp(arg, "-f")) {
            Options.friendly_name = *tmp++;
        } else {
            fprintf(stderr, "ERROR: too many arguments\n");
            PrintUsageAndExit(args);
        }
    }
}

/*----------------------------------------------------------------------
|   main
+---------------------------------------------------------------------*/
int
main(int /* argc */, char** argv)
{   
    PLT_UPnP upnp;

    /* parse command line */
    ParseCommandLine(argv);

    PLT_DeviceHostReference device(
        new PLT_MediaRenderer(Options.friendly_name?Options.friendly_name:"Platinum Media Renderer",
                              false,
                              "e6572b54-f3c7-2d91-2fb5-b757f2537e21"));
    upnp.AddDevice(device);
    bool added = true;

    upnp.Start();

    char buf[256];
    while (gets(buf)) {
        if (*buf == 'q')
            break;

        if (*buf == 's') {
            if (added) {
                upnp.RemoveDevice(device);
            } else {
                upnp.AddDevice(device);
            }
            added = !added;
        }
    }

    upnp.Stop();
    return 0;
}

可以看出JNI接口中的PLT_UPnP對象並沒有加入PLT_DeviceHostReference對象,所以無法啓動設備,只要參照MediaRenderTest.cpp的做法我們就可以實現一個dmr設備的開啓與關閉了,童鞋們可以仿照博文 基於Platinum庫的DMR實現(android)當中的jni類來嘗試實現一下這兩個接口

   public static native int startMediaRender(byte[] friendname ,byte[] uuid);

  public static native int stopMediaRender();  

完成這一步,DMR的實現就已經成功一半了


接下來我們要做的兩件事就是

1.將外部的action事件通過反射機制拋給java層處理

2.通過jni將一些事件狀態值更新至所在服務列表

先看第一點,每當有action事件到來時,PLT_MediaRenderer的

virtual NPT_Result OnAction(PLT_ActionReference&action,const PLT_HttpRequestContext& context);方法就會被調用

NPT_Result
PLT_MediaRenderer::OnAction(PLT_ActionReference&          action, 
                            const PLT_HttpRequestContext& context)
{
    NPT_COMPILER_UNUSED(context);

    /* parse the action name */
    NPT_String name = action->GetActionDesc().GetName();

    // since all actions take an instance ID and we only support 1 instance
    // verify that the Instance ID is 0 and return an error here now if not
    NPT_String serviceType = action->GetActionDesc().GetService()->GetServiceType();
    if (serviceType.Compare("urn:schemas-upnp-org:service:AVTransport:1", true) == 0) {
        if (NPT_FAILED(action->VerifyArgumentValue("InstanceID", "0"))) {
            action->SetError(718, "Not valid InstanceID");
            return NPT_FAILURE;
        }
    }
	serviceType = action->GetActionDesc().GetService()->GetServiceType();
	if (serviceType.Compare("urn:schemas-upnp-org:service:RenderingControl:1", true) == 0) {
		if (NPT_FAILED(action->VerifyArgumentValue("InstanceID", "0"))) {
			action->SetError(702, "Not valid InstanceID");
			return NPT_FAILURE;
		}
	}

	/* Is it a ConnectionManager Service Action ? */
	if (name.Compare("GetCurrentConnectionInfo", true) == 0) {
		return OnGetCurrentConnectionInfo(action);
	}  

	/* Is it a AVTransport Service Action ? */
    if (name.Compare("Next", true) == 0) {
        return OnNext(action);
    }
    if (name.Compare("Pause", true) == 0) {
        return OnPause(action);
    }
    if (name.Compare("Play", true) == 0) {
        return OnPlay(action);
    }
    if (name.Compare("Previous", true) == 0) {
        return OnPrevious(action);
    }
    if (name.Compare("Seek", true) == 0) {
        return OnSeek(action);
    }
    if (name.Compare("Stop", true) == 0) {
        return OnStop(action);
    }
    if (name.Compare("SetAVTransportURI", true) == 0) {
        return OnSetAVTransportURI(action);
    }
    if (name.Compare("SetPlayMode", true) == 0) {
        return OnSetPlayMode(action);
    }

    /* Is it a RendererControl Service Action ? */
    if (name.Compare("SetVolume", true) == 0) {
          return OnSetVolume(action);
    }
	if (name.Compare("SetVolumeDB", true) == 0) {
		return OnSetVolumeDB(action);
    }
	if (name.Compare("GetVolumeDBRange", true) == 0) {
		return OnGetVolumeDBRange(action);

	}
    if (name.Compare("SetMute", true) == 0) {
          return OnSetMute(action);
    }

    // other actions rely on state variables
    NPT_CHECK_LABEL_WARNING(action->SetArgumentsOutFromStateVariable(), failure);
    return NPT_SUCCESS;

failure:
    action->SetError(401,"No Such Action.");
    return NPT_FAILURE;
}

部分事件會由m_Delegate成員變量來處理,也就是 virtual void SetDelegate(PLT_MediaRendererDelegate* delegate) { m_Delegate = delegate; }傳進來的

所以我們只需要從PLT_MediaRendererDelegate類派生一個子類,然後將它設進PLT_MediaRenderer並實現相應的action方法並把一些需要的值反射出來即可

class PLT_MediaRendererDelegate
{
public:
    virtual ~PLT_MediaRendererDelegate() {}

    // ConnectionManager
    virtual NPT_Result OnGetCurrentConnectionInfo(PLT_ActionReference& action) = 0;

    // AVTransport
    virtual NPT_Result OnNext(PLT_ActionReference& action) = 0;
    virtual NPT_Result OnPause(PLT_ActionReference& action) = 0;
    virtual NPT_Result OnPlay(PLT_ActionReference& action) = 0;
    virtual NPT_Result OnPrevious(PLT_ActionReference& action) = 0;
    virtual NPT_Result OnSeek(PLT_ActionReference& action) = 0;
    virtual NPT_Result OnStop(PLT_ActionReference& action) = 0;
    virtual NPT_Result OnSetAVTransportURI(PLT_ActionReference& action) = 0;
    virtual NPT_Result OnSetPlayMode(PLT_ActionReference& action) = 0;

    // RenderingControl
    virtual NPT_Result OnSetVolume(PLT_ActionReference& action) = 0;
    virtual NPT_Result OnSetVolumeDB(PLT_ActionReference& action) = 0;
    virtual NPT_Result OnGetVolumeDBRange(PLT_ActionReference& action) = 0;
    virtual NPT_Result OnSetMute(PLT_ActionReference& action) = 0;
};


void ActionInflect(int cmd, const char* value, const char* data)
{

	if (g_vm == NULL)
	{
		UpnpPrintInfo("g_vm = NULL!!!");
		return ;
	}


	int status;
	JNIEnv *env = NULL;
	bool isAttach = false;
	status = g_vm->GetEnv((void **) &env, JNI_VERSION_1_6);
	if(status != JNI_OK) 
	{
		status = g_vm->AttachCurrentThread(&env, NULL);
		if(status < 0) {
			UpnpPrintInfo("callback_handler: failed to attach , current thread, status = %d", status);
			return;
		}
		isAttach = true;
	}

	jstring valueString = NULL;
	jstring dataString = NULL;
	jclass inflectClass = g_inflectClass;
	jmethodID inflectMethod = g_methodID;

	if (inflectClass == NULL || inflectMethod == NULL)
	{
		goto end;
	}


//	UpnpPrintInfo("CMD = %d\nVALUE = %s\nDATA = %s",cmd, value, data);
	
	valueString = env->NewStringUTF(value);
	dataString = env->NewStringUTF(data);

	env->CallStaticVoidMethod(inflectClass, inflectMethod, cmd, valueString, dataString);

	env->DeleteLocalRef(valueString);
	env->DeleteLocalRef(dataString);

end:
	if (env->ExceptionOccurred())
	{
		env->ExceptionDescribe();
		env->ExceptionClear();
	}
	if (isAttach)
	{
		g_vm->DetachCurrentThread();
	}


	

}

再看第二點,如何將事件變量值更新至服務列表

首先通過FindServiceByType方法找到urn:schemas-upnp-org:service:AVTransport:1服務,

然後通過SetStateVariable更新值即可

變量名與值對應關係大家參照dlna文檔開發即可

http://download.csdn.net/detail/geniuseoe2012/4969961

重點看standardizeddcps\MediaServer_4 and  MediaRenderer_3下的UPnP-av-AVTransport-v3-Service-20101231.pdf文檔

代碼實現可以參照PLT_MediaRenderer::SetupServices()方法

NPT_Result
PLT_MediaRenderer::SetupServices()
{
    PLT_Service* service;

    {
        /* AVTransport */
        service = new PLT_Service(
            this,
            "urn:schemas-upnp-org:service:AVTransport:1", 
            "urn:upnp-org:serviceId:AVTransport",
            "AVTransport",
            "urn:schemas-upnp-org:metadata-1-0/AVT/");
        NPT_CHECK_FATAL(service->SetSCPDXML((const char*) RDR_AVTransportSCPD));
        NPT_CHECK_FATAL(AddService(service));

        service->SetStateVariableRate("LastChange", NPT_TimeInterval(0.2f));
        service->SetStateVariable("A_ARG_TYPE_InstanceID", "0"); 

        // GetCurrentTransportActions
        service->SetStateVariable("CurrentTransportActions", "Play,Pause,Stop,Seek,Next,Previous");

        // GetDeviceCapabilities
        service->SetStateVariable("PossiblePlaybackStorageMedia", "NONE,NETWORK,HDD,CD-DA,UNKNOWN");
        service->SetStateVariable("PossibleRecordStorageMedia", "NOT_IMPLEMENTED");
        service->SetStateVariable("PossibleRecordQualityModes", "NOT_IMPLEMENTED");

        // GetMediaInfo
        service->SetStateVariable("NumberOfTracks", "0");
        service->SetStateVariable("CurrentMediaDuration", "00:00:00");
        service->SetStateVariable("AVTransportURI", "");
        service->SetStateVariable("AVTransportURIMetadata", "");;
        service->SetStateVariable("NextAVTransportURI", "NOT_IMPLEMENTED");
        service->SetStateVariable("NextAVTransportURIMetadata", "NOT_IMPLEMENTED");
        service->SetStateVariable("PlaybackStorageMedium", "NONE");
        service->SetStateVariable("RecordStorageMedium", "NOT_IMPLEMENTED");
		service->SetStateVariable("RecordMediumWriteStatus", "NOT_IMPLEMENTED");

        // GetPositionInfo
        service->SetStateVariable("CurrentTrack", "0");
        NPT_Result durResult = service->SetStateVariable("CurrentTrackDuration", "00:00:00");
        service->SetStateVariable("CurrentTrackMetadata", "");
        service->SetStateVariable("CurrentTrackURI", "");
        NPT_Result relTimeResult = service->SetStateVariable("RelativeTimePosition", "00:00:00"); 
        service->SetStateVariable("AbsoluteTimePosition", "00:50:00");
        service->SetStateVariable("RelativeCounterPosition", "2147483647"); // means NOT_IMPLEMENTED
        service->SetStateVariable("AbsoluteCounterPosition", "2147483647"); // means NOT_IMPLEMENTED

        // disable indirect eventing for certain state variables
        PLT_StateVariable* var;
        var = service->FindStateVariable("RelativeTimePosition");
        if (var) var->DisableIndirectEventing();
        var = service->FindStateVariable("AbsoluteTimePosition");
        if (var) var->DisableIndirectEventing();
        var = service->FindStateVariable("RelativeCounterPosition");
        if (var) var->DisableIndirectEventing();
        var = service->FindStateVariable("AbsoluteCounterPosition");
        if (var) var->DisableIndirectEventing();

        // GetTransportInfo
        service->SetStateVariable("TransportState", "NO_MEDIA_PRESENT");
        service->SetStateVariable("TransportStatus", "OK");
        service->SetStateVariable("TransportPlaySpeed", "1");

        // GetTransportSettings
        service->SetStateVariable("CurrentPlayMode", "NORMAL");
        service->SetStateVariable("CurrentRecordQualityMode", "NOT_IMPLEMENTED");
    }

    {
        /* ConnectionManager */
        service = new PLT_Service(
            this,
            "urn:schemas-upnp-org:service:ConnectionManager:1", 
            "urn:upnp-org:serviceId:ConnectionManager",
            "ConnectionManager");
        NPT_CHECK_FATAL(service->SetSCPDXML((const char*) RDR_ConnectionManagerSCPD));
        NPT_CHECK_FATAL(AddService(service));

        service->SetStateVariable("CurrentConnectionIDs", "0");

        // put all supported mime types here instead
        service->SetStateVariable("SinkProtocolInfo", "http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO,http-get:*:video/x-ms-asf:DLNA.ORG_PN=MPEG4_P2_ASF_SP_G726,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMDRM_WMABASE,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMAFULL,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMABASE,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVSPLL_BASE,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC_XAC3,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMDRM_WMVSPLL_BASE,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVSPML_BASE,http-get:*:video/x-ms-asf:DLNA.ORG_PN=MPEG4_P2_ASF_ASP_L5_SO_G726,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL_XAC3,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMAPRO,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN,http-get:*:video/x-ms-asf:DLNA.ORG_PN=MPEG4_P2_ASF_ASP_L4_SO_G726,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3X,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVSPML_MP3,http-get:*:video/x-ms-wmv:*,http-get:*:video/mp4:*");
        service->SetStateVariable("SourceProtocolInfo", "");
    }

    {
        /* RenderingControl */
        service = new PLT_Service(
            this,
            "urn:schemas-upnp-org:service:RenderingControl:1", 
            "urn:upnp-org:serviceId:RenderingControl",
            "RenderingControl",
            "urn:schemas-upnp-org:metadata-1-0/RCS/");
        NPT_CHECK_FATAL(service->SetSCPDXML((const char*) RDR_RenderingControlSCPD));
        NPT_CHECK_FATAL(AddService(service));

        service->SetStateVariableRate("LastChange", NPT_TimeInterval(0.2f));

        service->SetStateVariable("Mute", "0");
        service->SetStateVariableExtraAttribute("Mute", "Channel", "Master");
        service->SetStateVariable("Volume", "100");
        service->SetStateVariableExtraAttribute("Volume", "Channel", "Master");
        service->SetStateVariable("VolumeDB", "0");
        service->SetStateVariableExtraAttribute("VolumeDB", "Channel", "Master");

        service->SetStateVariable("PresetNameList", "FactoryDefaults");
    }

    return NPT_SUCCESS;
}

至此代碼實現步驟已全部講解完畢,當然在具體實現過程中多多少少會遇到些問題

根據log信息多問問谷哥度娘,一般都能找到解決方案的

同時在jni層轉換C++類型與java類型時一定要注意及時釋放資源,不要造成內存泄漏了

另外不要再向樓主索要源碼了,編程最重要的是思維

只有自己動手實踐了,摸索了,思考了,解決了,自身水平纔會提高

否則你永遠都只能是code-farmer而無法成爲code-designer

授之以魚不如授之予漁也是藍老師一貫的教學宗旨,伸手黨請自覺繞道

最後感謝大家的支持,我們下節課再見!


more brilliantPlease pay attention to my CSDN blog -->http://blog.csdn.net/geniuseoe2012 







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章