关于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 

 






你可能感兴趣的:(media)