zoukankan      html  css  js  c++  java
  • 关于Platinum库的MediaRender具体C++代码实现探讨

    接上篇博文 NDK下 将Platinum SDK 编译成so库 (android - upnp)

    讲述了如何利用该代码库编译给android程序调用的so库,其中也提到了,在使用sample-upnp工程来测试生成的so库是无效的

    大家比对一下Platinum开发库的PlatinumSourcePlatformAndroidmoduleplatinumjniplatinum-jni.cpp

    PlatinumSourceTestsMediaRendererMediaRendererTest.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>]
    ", args[0]);
        fprintf(stderr, "-f : optional upnp server friendly name
    ");
        fprintf(stderr, "<path> : local path to serve
    ");
        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
    ");
                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
    VALUE = %s
    DATA = %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

    重点看standardizeddcpsMediaServer_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 






  • 相关阅读:
    hbase2.x错误记录之 disable表卡住
    hbase2.x 错误记录之 procedure is still running
    yarn timelineserver v2 配置
    Linux 系统出现大量的CLOSE_WAIT
    hbase 2.x 异常记录之 hbase shell不能分配内存
    spark 访问 hive,不能获取到数据信息
    hive 由于distcp导致执行sql慢
    Hbase 安装部署
    MooseFS安装部署
    hbase2.x 单节点启动,master挂掉
  • 原文地址:https://www.cnblogs.com/dyllove98/p/3157236.html
Copyright © 2011-2022 走看看