linphone-linphone没有视频H264编码分析过程-正确分析正确的方法

本篇是上一篇的彩蛋篇

正确找到H264编码之间,进行的代码追踪工作。其中将linphone启动的来龙去脉走了一遍,给人印象深刻。所以贴到这里,进行一下纪念。

为什么有的有H264编码,有的没有H264编码呢?追踪一下

界面显示SettingsFragment.java

用到的地方–下载篇章,没有什么卵用

LinphoneCoreFactory.java

说明

这里是一个接口, 专门用于创建OpenH264DownloadHelper使用

代码

    /**
     * Must be calling after the LinphoneCore creation
     * @return a new OpenH264DownloadHelper
     */
    abstract public OpenH264DownloadHelper createOpenH264DownloadHelper();

LinphoneCoreFactoryImpl.java

说明

这里主要是创建了一个OpenH264DownloadHelper对象

代码

    @Override
    public OpenH264DownloadHelper createOpenH264DownloadHelper() {
        if (fcontext == null) {
            new LinphoneCoreException("Cannot create OpenH264DownloadHelper");
            return null;//exception
        }
        return new OpenH264DownloadHelper(fcontext);
    }
    private boolean loadingDownloadedOpenH264(Context context) {
        File file = new File(context.getFilesDir() + "/../lib/libmsopenh264.so");

        if (!file.exists()) {
            Log.i("LinphoneCoreFactoryImpl", " libmsopenh264 not found, we disable the download of Openh264");
            return false;
        }

        OpenH264DownloadHelper downloadHelper = new OpenH264DownloadHelper(context);
        if (downloadHelper.isCodecFound()) {
            Log.i("LinphoneCoreFactoryImpl", " Loading OpenH264 downloaded plugin:" + downloadHelper.getFullPathLib());
            System.load(downloadHelper.getFullPathLib());
        } else {
            Log.i("LinphoneCoreFactoryImpl", " Cannot load OpenH264 downloaded plugin");
        }
        return true;
    }

OpenH264DownloadHelper.java

public class OpenH264DownloadHelper {
    private OpenH264DownloadHelperListener openH264DownloadHelperListener;
    private ArrayList<Object> userData;
    private String fileDirection;
    private String nameLib;
    private String urlDownload;
    private String nameFileDownload;
    private String licenseMessage;

    /**
     * Default values
     * nameLib = "libopenh264-1.5.so"
     * urlDownload = "http://ciscobinary.openh264.org/libopenh264-1.5.0-android19.so.bz2"
     * nameFileDownload = "libopenh264-1.5.0-android19.so.bz2"
     */
    public OpenH264DownloadHelper(Context context) {
        userData = new ArrayList<Object>();
        licenseMessage = "OpenH264 Video Codec provided by Cisco Systems, Inc.";
        nameLib = "libopenh264.so";
        urlDownload = "http://ciscobinary.openh264.org/libopenh264-1.5.0-android19.so.bz2";
        nameFileDownload = "libopenh264-1.5.0-android19.so.bz2";
        if(context.getFilesDir() != null) {
            fileDirection = context.getFilesDir().toString();
        }
    }

    /**
     * Set OpenH264DownloadHelperListener
     * @param h264Listener
     */
    public void setOpenH264HelperListener(OpenH264DownloadHelperListener h264Listener) {
        openH264DownloadHelperListener = h264Listener;
    }

    /**
     * @return OpenH264DownloadHelperListener
     */
    public OpenH264DownloadHelperListener getOpenH264DownloadHelperListener() {
        return openH264DownloadHelperListener;
    }

    /**
     * @param index of object in UserData list
     * @constraints (index >= 0 && index < userData.size())
     * @return object if constraints are met
     */
    public Object getUserData(int index) {
        if (index < 0 || index >= userData.size()) return null;
        return userData.get(index);
    }

    /**
     * Adding of object into UserData list
     * @param object
     * @return index of object in UserData list
     */
    public int setUserData(Object object) {
        this.userData.add(object);
        return this.userData.indexOf(object);
    }

    /**
     * @param index
     * @param object
     * @constraints (index >= 0 && index < userData.size())
     */
    public void setUserData(int index, Object object) {
        if (index < 0 || index > userData.size()) return;
        this.userData.add(index,object);
    }

    /**
     * @return size of UserData list
     */
    public int getUserDataSize() {
        return this.userData.size();
    }

    /**
     * @return OpenH264 license message
     */
    public String getLicenseMessage() {
        return licenseMessage;
    }

    /**
     * Set filename to storage for OpenH264 codec
     * @param name
     */
    public void setNameLib(String name) {
        nameLib = name;
    }

    /**
     * @return filename of OpenH264 codec
     */
    public String getNameLib() {
        return nameLib;
    }

    /**
     * @return path of the lib
     */
    public String getFullPathLib() {
        return this.fileDirection + "/" + this.getNameLib();
    }

    /**
     * Set name download file
     * @param name : must be the same name relative to the url
     */
    public void setNameFileDownload(String name) {
        nameFileDownload = name;
    }

    /**
     * Set new url
     * @param url : must be a Cisco Url to OpenH264 and .bzip2 file
     */
    public void setUrlDownload(String url) {
        urlDownload = url;
    }

    /**
     * Indicates whether the lib exists
     * Requirements : fileDirection and nameLib init
     * @return file exists ?
     */
    public boolean isCodecFound() {
        return new File(fileDirection+"/" + nameLib).exists();
    }

    /**
     * Try to download and load codec
     * Requirements :
     *  fileDirection
     *  nameFileDownload
     *  urlDownload
     *  nameLib
     *  codecDownListener
     */
    public void downloadCodec() {
        Thread thread = new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                try {
                    String path = fileDirection+"/" + nameLib;
                    URL url = new URL(urlDownload);
                    HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection();
                    urlConnection.connect();
                    Log.i("OpenH264Downloader"," ");
                    InputStream inputStream = urlConnection.getInputStream();
                    FileOutputStream fileOutputStream = new FileOutputStream(fileDirection+"/"+nameFileDownload);
                    int totalSize = urlConnection.getContentLength();
                    openH264DownloadHelperListener.OnProgress(0,totalSize);

                    Log.i("OpenH264Downloader"," Download file:" + nameFileDownload);

                    byte[] buffer = new byte[4096];
                    int bufferLength;
                    int total = 0;
                    while((bufferLength = inputStream.read(buffer))>0 ){
                        total += bufferLength;
                        fileOutputStream.write(buffer, 0, bufferLength);
                        openH264DownloadHelperListener.OnProgress(total, totalSize);
                    }

                    fileOutputStream.close();
                    inputStream.close();

                    Log.i("OpenH264Downloader"," Uncompress file:" + nameFileDownload);

                    FileInputStream in = new FileInputStream(fileDirection+"/"+nameFileDownload);
                    FileOutputStream out = new FileOutputStream(path);
                    BZip2CompressorInputStream bzIn = new BZip2CompressorInputStream(in);

                    while ((bufferLength = bzIn.read(buffer))>0) {
                        out.write(buffer, 0, bufferLength);
                    }
                    in.close();
                    out.close();
                    bzIn.close();

                    Log.i("OpenH264Downloader"," Remove file:" + nameFileDownload);
                    new File(fileDirection+"/"+nameFileDownload).delete();

                    Log.i("OpenH264Downloader"," Loading plugin:" + path);
                    System.load(path);
                    openH264DownloadHelperListener.OnProgress(2,1);
                } catch (FileNotFoundException e) {
                    openH264DownloadHelperListener.OnError(e.getLocalizedMessage());
                } catch (IOException e) {
                    openH264DownloadHelperListener.OnError(e.getLocalizedMessage());
                }
            }
        });
        thread.start();
    }
}

LinphoneCoreImpl.java

说明

是否允许下载H264

代码

    public void enableDownloadOpenH264(boolean enable) {
        Log.i("[LinphoneCoreImpl] enableDownloadOpenH264  enable = " + enable);
        openh264DownloadEnabled = enable;
    }

    public boolean downloadOpenH264Enabled() {
        Log.i("[LinphoneCoreImpl] downloadOpenH264Enabled return openh264DownloadEnabled = " + openh264DownloadEnabled);
        return openh264DownloadEnabled;
    }

LinphoneManager.java

说明

代码

    public void initOpenH264DownloadHelper() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
            Log.i("Android >= 5.1 we disable the download of OpenH264");
            getLc().enableDownloadOpenH264(false);
            return;
        }

        mCodecDownloader = LinphoneCoreFactory.instance().createOpenH264DownloadHelper();
        mCodecListener = new OpenH264DownloadHelperListener() {
            ProgressDialog progress;
            int ctxt = 0;
            int box = 1;

            @Override
            public void OnProgress(final int current, final int max) {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        OpenH264DownloadHelper ohcodec = LinphoneManager.getInstance().getOpenH264DownloadHelper();
                        if (progress == null) {
                            progress = new ProgressDialog((Context) ohcodec.getUserData(ctxt));
                            progress.setCanceledOnTouchOutside(false);
                            progress.setCancelable(false);
                            progress.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
                        } else if (current <= max) {
                            progress.setMessage(getString(R.string.assistant_openh264_downloading));
                            progress.setMax(max);
                            progress.setProgress(current);
                            progress.show();
                        } else {
                            progress.dismiss();
                            progress = null;
                            LinphoneManager.getLc().reloadMsPlugins(LinphoneManager.this.getContext().getApplicationInfo().nativeLibraryDir);
                            if (ohcodec.getUserDataSize() > box && ohcodec.getUserData(box) != null) {
                                ((CheckBoxPreference) ohcodec.getUserData(box)).setSummary(mCodecDownloader.getLicenseMessage());
                                ((CheckBoxPreference) ohcodec.getUserData(box)).setTitle("OpenH264");
                            }
                        }
                    }
                });
            }

使用iniitOpenH264DownloadHelper()

public synchronized static final LinphoneManager createAndStart(Context c) {
    if (instance != null)
        throw new RuntimeException("Linphone Manager is already initialized");

    instance = new LinphoneManager(c);
    instance.startLibLinphone(c);
    instance.initOpenH264DownloadHelper();

    // H264 codec Management - set to auto mode -> MediaCodec >= android 5.0 >= OpenH264
    //H264Helper.setH264Mode(H264Helper.MODE_AUTO, getLc());
    H264Helper.setH264Mode(H264Helper.MODE_OPENH264, getLc());

    TelephonyManager tm = (TelephonyManager) c.getSystemService(Context.TELEPHONY_SERVICE);
    boolean gsmIdle = tm.getCallState() == TelephonyManager.CALL_STATE_IDLE;
    setGsmIdle(gsmIdle);

    return instance;
}

createAndStart()方法在LinphoneService创建的时候使用。

LinphoneService.java

public void onCreate() {
    ...
     LinphoneManager.createAndStart(LinphoneService.this);
    ...
}

用到的地方-显示篇章-主要的显示逻辑

SettingsFragment.java

说明

主要是显示视频编码的地方。

代码

initVideoSettings()

private void initVideoSettings() {
    initializePreferredVideoSizePreferences((ListPreference) findPreference(getString(R.string.pref_preferred_video_size_key)));
    initializePreferredVideoFpsPreferences((ListPreference) findPreference(getString(R.string.pref_preferred_video_fps_key)));
    EditTextPreference bandwidth = (EditTextPreference) findPreference(getString(R.string.pref_bandwidth_limit_key));
    bandwidth.setText(Integer.toString(mPrefs.getBandwidthLimit()));
    bandwidth.setSummary(bandwidth.getText());
    updateVideoPreferencesAccordingToPreset();

    ListPreference videoPresetPref = (ListPreference) findPreference(getString(R.string.pref_video_preset_key));
    videoPresetPref.setSummary(mPrefs.getVideoPreset());
    videoPresetPref.setValue(mPrefs.getVideoPreset());

    PreferenceCategory codecs = (PreferenceCategory) findPreference(getString(R.string.pref_video_codecs_key));
    codecs.removeAll();

    final LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();

    final OpenH264DownloadHelper mCodecDownloader = LinphoneManager.getInstance().getOpenH264DownloadHelper();

    for (final PayloadType pt : lc.getVideoCodecs()) {
        final CheckBoxPreference codec = new CheckBoxPreference(getActivity());
        codec.setTitle(pt.getMime());

        if (!pt.getMime().equals("VP8")) {
            Log.i("[SettingsFragment] !pt.getMime().equals(\"VP8\"");
            if (getResources().getBoolean(R.bool.disable_all_patented_codecs_for_markets)) {
                continue;
            } else {
                if (!Version.hasFastCpuWithAsmOptim() && pt.getMime().equals("H264"))
                {
                    // Android without neon doesn't support H264
                    Log.w("CPU does not have asm optimisations available, disabling H264");
                    continue;
                }
            }
        }
        if (lc.downloadOpenH264Enabled()) {
            if (pt.getMime().equals("H264") && mCodecDownloader.isCodecFound()) {
                codec.setSummary(mCodecDownloader.getLicenseMessage());
                codec.setTitle("OpenH264");
            }
        }
        codec.setChecked(lc.isPayloadTypeEnabled(pt));
        codec.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
            @Override
            public boolean onPreferenceChange(Preference preference, Object newValue) {
                boolean enable = (Boolean) newValue;
                try {
                    if (lc.downloadOpenH264Enabled()) {
                        if (enable && Version.getCpuAbis().contains("armeabi-v7a") && !Version.getCpuAbis().contains("x86")
                                && pt.getMime().equals("H264") && !mCodecDownloader.isCodecFound()) {
                                mCodecDownloader.setOpenH264HelperListener(LinphoneManager.getInstance().getOpenH264HelperListener());
                            mCodecDownloader.setUserData(0, LinphoneManager.getInstance().getContext());
                            mCodecDownloader.setUserData(1, codec);

                            AlertDialog.Builder builder = new AlertDialog.Builder(LinphoneManager.getInstance().getContext());
                            builder.setCancelable(false);
                            builder.setMessage("Do you agree to download " + mCodecDownloader.getLicenseMessage()).setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    if (which == DialogInterface.BUTTON_POSITIVE)
                                        mCodecDownloader.downloadCodec();
                                }
                            });
                            builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    if (which == DialogInterface.BUTTON_NEGATIVE) {
                                        // Disable H264
                                    }
                                }
                            }).show();
                        }
                    }
                    LinphoneManager.getLcIfManagerNotDestroyedOrNull().enablePayloadType(pt, enable);
                } catch (LinphoneCoreException e) {
                    Log.e(e);
                }
                return true;                }
            });

        codecs.addPreference(codec);
    }
    ((CheckBoxPreference) findPreference(getString(R.string.pref_video_enable_key))).setChecked(mPrefs.isVideoEnabled());
    ((CheckBoxPreference) findPreference(getString(R.string.pref_video_use_front_camera_key))).setChecked(mPrefs.useFrontCam());
    ((CheckBoxPreference) findPreference(getString(R.string.pref_video_initiate_call_with_video_key))).setChecked(mPrefs.shouldInitiateVideoCall());
    ((CheckBoxPreference) findPreference(getString(R.string.pref_video_automatically_accept_video_key))).setChecked(mPrefs.shouldAutomaticallyAcceptVideoRequests());
    ((CheckBoxPreference) findPreference(getString(R.string.pref_overlay_key))).setChecked(mPrefs.isOverlayEnabled());
}

这里用到了lc.getVideoCodecs(), 在Android 5.0以上是有H264编码的, 而且显示在了设置上.

LinphoneCore.java & LinphoneCoreImpl.java

    public synchronized PayloadType[] getVideoCodecs() {
        long[] typesPtr = listVideoPayloadTypes(nativePtr);
        if (typesPtr == null) return null;

        PayloadType[] codecs = new PayloadType[typesPtr.length];

        for (int i = 0; i < codecs.length; i++) {
            codecs[i] = new PayloadTypeImpl(typesPtr[i]);
        }

        return codecs;
    }

这里是获取所有的视频编码。

然后调用层来加载所有的视频编码。

private native long[] listVideoPayloadTypes(long nativePtr);

linphonecore_jni.cc中

extern "C" jlongArray Java_org_linphone_core_LinphoneCoreImpl_listVideoPayloadTypes(JNIEnv*  env
                                                                            ,jobject  thiz
                                                                            ,jlong lc) {
    const bctbx_list_t* codecs = linphone_core_get_video_codecs((LinphoneCore*)lc);
    size_t codecsCount = bctbx_list_size(codecs);
    jlongArray jCodecs = env->NewLongArray(codecsCount);
    jlong *jInternalArray = env->GetLongArrayElements(jCodecs, NULL);

    for (size_t i = 0; i < codecsCount; i++ ) {
        jInternalArray[i] = (unsigned long) (codecs->data);
        codecs = codecs->next;
    }

    env->ReleaseLongArrayElements(jCodecs, jInternalArray, 0);

    return jCodecs;
}

linphonecore.c中

const bctbx_list_t *linphone_core_get_video_codecs(const LinphoneCore *lc) {
    return lc->codecs_conf.video_codecs;
}

LinphoneManager.java中

说明

整个获取VideoCodecs的逻辑是在LinhoneCore中获得的, 所以得明白LinphoneCore的生成, 也就是mLc的生成。

代码

initLiblinphone(LinphoneCore)中

    private synchronized void initLiblinphone(LinphoneCore lc) throws LinphoneCoreException {
        mLc = lc;


        PreferencesMigrator prefMigrator = new PreferencesMigrator(mServiceContext);
        prefMigrator.migrateRemoteProvisioningUriIfNeeded();
        prefMigrator.migrateSharingServerUrlIfNeeded();
        prefMigrator.doPresenceMigrationIfNeeded();

        if (prefMigrator.isMigrationNeeded()) {
            prefMigrator.doMigration();
        }

        mLc.setZrtpSecretsCache(basePath + "/zrtp_secrets");

        try {
            String versionName = mServiceContext.getPackageManager().getPackageInfo(mServiceContext.getPackageName(), 0).versionName;
            if (versionName == null) {
                versionName = String.valueOf(mServiceContext.getPackageManager().getPackageInfo(mServiceContext.getPackageName(), 0).versionCode);
            }
            mLc.setUserAgent("LinphoneAndroid", versionName);
        } catch (NameNotFoundException e) {
            Log.e(e, "cannot get version name");
        }

        mLc.setRingback(mRingbackSoundFile);
        mLc.setRootCA(mLinphoneRootCaFile);
        mLc.setPlayFile(mPauseSoundFile);
        mLc.setChatDatabasePath(mChatDatabaseFile);
        mLc.setCallLogsDatabasePath(mCallLogDatabaseFile);
        mLc.setFriendsDatabasePath(mFriendsDatabaseFile);
        mLc.setUserCertificatesPath(mUserCertificatePath);
        subscribeFriendList(mPrefs.isFriendlistsubscriptionEnabled());
        //mLc.setCallErrorTone(Reason.NotFound, mErrorToneFile);
        enableDeviceRingtone(mPrefs.isDeviceRingtoneEnabled());

        int availableCores = Runtime.getRuntime().availableProcessors();
        Log.w("MediaStreamer : " + availableCores + " cores detected and configured");
        mLc.setCpuCount(availableCores);

        mLc.migrateCallLogs();

        if (mServiceContext.getResources().getBoolean(R.bool.enable_push_id)) {
            initPushNotificationsService();
        }

        /*
         You cannot receive this through components declared in manifests, only
         by explicitly registering for it with Context.registerReceiver(). This is a protected intent that can only
         be sent by the system.
        */
        mKeepAliveIntentFilter = new IntentFilter(Intent.ACTION_SCREEN_ON);
        mKeepAliveIntentFilter.addAction(Intent.ACTION_SCREEN_OFF);

        mKeepAliveReceiver = new KeepAliveReceiver();
        mServiceContext.registerReceiver(mKeepAliveReceiver, mKeepAliveIntentFilter);

        mDozeIntentFilter = new IntentFilter(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
        mDozeIntentFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);

        mDozeReceiver = new DozeReceiver();

        mServiceContext.registerReceiver(mDozeReceiver, mDozeIntentFilter);

        updateNetworkReachability();

        resetCameraFromPreferences();

        accountCreator = LinphoneCoreFactory.instance().createAccountCreator(LinphoneManager.getLc(), LinphonePreferences.instance().getXmlrpcUrl());
        accountCreator.setDomain(getString(R.string.default_domain));
        accountCreator.setListener(this);

startLibLinphone(Context c)中

    private synchronized void startLibLinphone(Context c) {
        try {
            copyAssetsFromPackage();
            //traces alway start with traces enable to not missed first initialization

            mLc = LinphoneCoreFactory.instance().createLinphoneCore(this, mLinphoneConfigFile, mLinphoneFactoryConfigFile, null, c);

            TimerTask lTask = new TimerTask() {
                @Override
                public void run() {
                    UIThreadDispatcher.dispatch(new Runnable() {
                        @Override
                        public void run() {
                            if (mLc != null) {
                                mLc.iterate();
                            }
                        }
                    });
                }
            };
            /*use schedule instead of scheduleAtFixedRate to avoid iterate from being call in burst after cpu wake up*/
            mTimer = new Timer("Linphone scheduler");
            mTimer.schedule(lTask, 0, 20);
        } catch (Exception e) {
            Log.e(e);
            Log.e(e, "Cannot start linphone");
        }
    }

在LinphoneCoreFactoryImpl.java中

这里生成和创建了LinphoneCore

    @Override
    public LinphoneCore createLinphoneCore(LinphoneCoreListener listener, Object context) throws LinphoneCoreException {
        try {
            fcontext = (Context) context;
            boolean openh264DownloadEnabled = false;
            if (context != null) openh264DownloadEnabled = loadingDownloadedOpenH264(fcontext);
            MediastreamerAndroidContext.setContext(context);
            LinphoneCore lc = new LinphoneCoreImpl(listener);
            Log.i("[LinphoneCoreFactoryImpl] createLinphoneCore2 lc.enableDownloadOpenH264(openh264DownloadEnabled) --> " + openh264DownloadEnabled);
            lc.enableDownloadOpenH264(openh264DownloadEnabled);
            if (context != null) lc.setContext(context);
            return lc;
        } catch (IOException e) {
            throw new LinphoneCoreException("Cannot create LinphoneCore", e);
        }
    }

跟H264Helper中的setH264Mode有没有关系,答案是没有太大的关系

/**
 * Define the Codec to use between MediaCodec and OpenH264
 * Possible mode are:
 *      - Auto to let the system choose in function of you OS version,
 *      - OpenH264 to enable OpenH264 Encoder and Decoder,
 *      - Mediacodec to enable Mediacodec only.
 * @param mode String value between Auto, OpenH264 and MediaCodec
 */
public static void setH264Mode(String mode, LinphoneCore linphoneCore){
    if(mode.equals(MODE_OPENH264)){
        Log.i("H264Helper"," setH264Mode  MODE_OPENH264 - Mode = "+mode);
        linphoneCore.getMSFactory().enableFilterFromName(FILTER_NAME_MEDIA_CODEC_DEC , false);
        linphoneCore.getMSFactory().enableFilterFromName(FILTER_NAME_MEDIA_CODEC_ENC , false);
        linphoneCore.getMSFactory().enableFilterFromName(FILTER_NAME_OPENH264_DEC , true);
        linphoneCore.getMSFactory().enableFilterFromName(FILTER_NAME_OPENH264_ENC , true);
    }else if(mode.equals(MODE_MEDIA_CODEC)){
        Log.i("H264Helper"," setH264Mode  MODE_MEDIA_CODEC - Mode = "+mode);
        linphoneCore.getMSFactory().enableFilterFromName(FILTER_NAME_OPENH264_DEC , false);
        linphoneCore.getMSFactory().enableFilterFromName(FILTER_NAME_OPENH264_ENC , false);
        linphoneCore.getMSFactory().enableFilterFromName(FILTER_NAME_MEDIA_CODEC_DEC , true);
        linphoneCore.getMSFactory().enableFilterFromName(FILTER_NAME_MEDIA_CODEC_ENC , true);
    }else if(mode.equals(MODE_AUTO)){
        Log.i("H264Helper"," setH264Mode  MODE_AUTO - Mode = "+mode);
       // if  android >= 5.0 use MediaCodec
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
           Log.i("H264Helper"," setH264Mode  MODE_AUTO 1 - Mode = "+mode);
           Log.i("LinphoneCoreFactoryImpl"," Openh264 disabled on the project, now using MediaCodec");
           linphoneCore.getMSFactory().enableFilterFromName(FILTER_NAME_OPENH264_DEC , false);
           linphoneCore.getMSFactory().enableFilterFromName(FILTER_NAME_OPENH264_ENC , false);
           linphoneCore.getMSFactory().enableFilterFromName(FILTER_NAME_MEDIA_CODEC_DEC , true);
           linphoneCore.getMSFactory().enableFilterFromName(FILTER_NAME_MEDIA_CODEC_ENC , true);
       }
       //otherwise use OpenH264
       else{
           Log.i("H264Helper"," setH264Mode  MODE_AUTO 2 - Mode = "+mode);
           Log.i("LinphoneCoreFactoryImpl"," Openh264 enabled on the project");
           linphoneCore.getMSFactory().enableFilterFromName(FILTER_NAME_MEDIA_CODEC_DEC , false);
           linphoneCore.getMSFactory().enableFilterFromName(FILTER_NAME_MEDIA_CODEC_ENC , false);
           linphoneCore.getMSFactory().enableFilterFromName(FILTER_NAME_OPENH264_DEC , true);
           linphoneCore.getMSFactory().enableFilterFromName(FILTER_NAME_OPENH264_ENC , true);
       }
    }else {
        Log.i("LinphoneCoreFactoryImpl"," Error: Openh264 mode not reconized !");
    }
    Log.i("H264Helper"," setH264Mode - Mode = "+mode);
}

多次修改setH264Mode, 这个是没有效果的。

问题出在了哪里? 先找找设置H264的地方吧,这个实在linphonecore_jni.cc中

extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_enablePayloadType(JNIEnv*  env
                                                                            ,jobject  thiz
                                                                            ,jlong lc
                                                                            ,jlong pt
                                                                            ,jboolean enable) {
    return (jint)linphone_core_enable_payload_type((LinphoneCore*)lc,(PayloadType*)pt,enable);
}

然后就找到了misc.c文件中的一些代码

int linphone_core_enable_payload_type(LinphoneCore *lc, LinphonePayloadType *pt, bool_t enabled){
    if (bctbx_list_find(lc->codecs_conf.audio_codecs,pt) || bctbx_list_find(lc->codecs_conf.video_codecs,pt) || bctbx_list_find(lc->codecs_conf.text_codecs,pt)){
        payload_type_set_enable(pt,enabled);
        _linphone_core_codec_config_write(lc);
        linphone_core_update_allocated_audio_bandwidth(lc);
        return 0;
    }
    ms_error("Enabling codec not in audio or video list of PayloadType !");
    return -1;
}

这里又使用了一些函数

static MS2_INLINE void payload_type_set_enable(PayloadType *pt,int value)
{
    if ((value)!=0) payload_type_set_flag(pt,PAYLOAD_TYPE_ENABLED); \
    else payload_type_unset_flag(pt,PAYLOAD_TYPE_ENABLED);
}

最后找到了payloadtype.h中的代码, 发现没什么用

#define payload_type_set_flag(pt,flag) (pt)->flags|=((int)flag)

从得到编码集找找效果?回过头来继续看LinphoneCore结构体的结果

struct _LinphoneCore 
{
    ...
    codecs_config_t codecs_conf;
    ...
}

找到了codecs_config

typedef struct codecs_config
{
    MSList *audio_codecs;  /* list of audio codecs in order of preference*/
    MSList *video_codecs;
    MSList *text_codecs;
    int dyn_pt;
    int telephone_event_pt;
}codecs_config_t;

突然感觉两处很有用

linphone/coreapi/linphonecore.c:    lc->codecs_conf.audio_codecs=codecs;

linphone/coreapi/linphonecore.c:    lc->codecs_conf.video_codecs=codecs;

这两个都是设置codecs。

第一个设置codecs

# linphonecore.c
int linphone_core_set_video_codecs(LinphoneCore *lc, bctbx_list_t *codecs){
    if (lc->codecs_conf.video_codecs!=NULL) bctbx_list_free(lc->codecs_conf.video_codecs);
    lc->codecs_conf.video_codecs=codecs;
    _linphone_core_codec_config_write(lc);
    return 0;
}

这里面有这样一个函数_linphone_core_codec_config_write

# linphonecore.c
void _linphone_core_codec_config_write(LinphoneCore *lc){
    if (linphone_core_ready(lc)){
        PayloadType *pt;
        codecs_config_t *config=&lc->codecs_conf;
        bctbx_list_t *node;
        char key[50];
        int index;
        index=0;
        for(node=config->audio_codecs;node!=NULL;node=bctbx_list_next(node)){
            pt=(PayloadType*)(node->data);
            sprintf(key,"audio_codec_%i",index);
            lp_config_set_string(lc->config,key,"mime",pt->mime_type);
            lp_config_set_int(lc->config,key,"rate",pt->clock_rate);
            lp_config_set_int(lc->config,key,"channels",pt->channels);
            lp_config_set_int(lc->config,key,"enabled",linphone_core_payload_type_enabled(lc,pt));
            index++;
        }
        sprintf(key,"audio_codec_%i",index);
        lp_config_clean_section (lc->config,key);

        index=0;
        for(node=config->video_codecs;node!=NULL;node=bctbx_list_next(node)){
            pt=(PayloadType*)(node->data);
            sprintf(key,"video_codec_%i",index);
            lp_config_set_string(lc->config,key,"mime",pt->mime_type);
            lp_config_set_int(lc->config,key,"rate",pt->clock_rate);
            lp_config_set_int(lc->config,key,"enabled",linphone_core_payload_type_enabled(lc,pt));
            lp_config_set_string(lc->config,key,"recv_fmtp",pt->recv_fmtp);
            index++;
        }
        sprintf(key,"video_codec_%i",index);
        lp_config_clean_section (lc->config,key);
    }
}

不管怎样, 这个是写入到配置文件的参数。 让我稍微看一看。

该往上找了

第二个设置codecs

跟上面是一个

linphone_core_set_video_codecs被linphonecore_jni.cc用到

codecs_config_config_read是什么

static void codecs_config_read(LinphoneCore *lc){
    int i;
    PayloadType *pt;
    bctbx_list_t *audio_codecs=NULL;
    bctbx_list_t *video_codecs=NULL;
    bctbx_list_t *text_codecs=NULL;

    lc->codecs_conf.dyn_pt=96;
    lc->codecs_conf.telephone_event_pt=lp_config_get_int(lc->config,"misc","telephone_event_pt",101);

    for (i=0;get_codec(lc,SalAudio,i,&pt);i++){
        if (pt){
            audio_codecs=codec_append_if_new(audio_codecs, pt);
        }
    }
    audio_codecs = handle_missing_codecs(lc, lc->default_audio_codecs,audio_codecs, MSAudio);

    for (i=0;get_codec(lc,SalVideo,i,&pt);i++){
        if (pt){
            video_codecs=codec_append_if_new(video_codecs, pt);
        }
    }

    video_codecs = handle_missing_codecs(lc, lc->default_video_codecs, video_codecs, MSVideo);

    for (i=0;get_codec(lc,SalText,i,&pt);i++){
        if (pt){
            text_codecs=codec_append_if_new(text_codecs, pt);
        }
    }
    text_codecs = add_missing_supported_codecs(lc, lc->default_text_codecs, text_codecs);

    linphone_core_set_audio_codecs(lc,audio_codecs);
    linphone_core_set_video_codecs(lc,video_codecs);
    linphone_core_set_text_codecs(lc, text_codecs);
    linphone_core_update_allocated_audio_bandwidth(lc);
}

追踪一下LinphoneCore的实例试试

全在newLinphoneCore()函数中

#linphonecore_jni.cc
extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_newLinphoneCore(JNIEnv*  env
        ,jobject thiz
        ,jobject jlistener
        ,jstring juserConfig
        ,jstring jfactoryConfig
        ,jobject juserdata){

    const char* userConfig = GetStringUTFChars(env, juserConfig);
    const char* factoryConfig = GetStringUTFChars(env, jfactoryConfig);

    LinphoneJavaBindings *ljb = new LinphoneJavaBindings(env);

    LinphoneCoreVTable *vTable = linphone_core_v_table_new();
    LinphoneCoreData* ldata = new LinphoneCoreData(env, thiz, vTable, jlistener, ljb);
    linphone_core_v_table_set_user_data(vTable, ldata);


    jobject core = env->NewGlobalRef(thiz);
    ljb->setCore(core);
    LinphoneCore *lc = linphone_core_new(vTable, userConfig, factoryConfig, ljb);

    jlong nativePtr = (jlong)lc;
    ReleaseStringUTFChars(env, juserConfig, userConfig);
    ReleaseStringUTFChars(env, jfactoryConfig, factoryConfig);
    return nativePtr;
}

这里用到了一下几个函数, 可以依依追踪一下

  • LinphoneCoreVTable *vTable = linphone_core_v_table_new();
  • LinphoneCoreData* ldata = new LinphoneCoreData(env, thiz, vTable, jlistener, ljb);
  • linphone_core_v_table_set_user_data(vTable, ldata);
  • LinphoneCore *lc = linphone_core_new(vTable, userConfig, factoryConfig, ljb);

LinphoneCoreVTable, 回调的一些状态

/**
 * This structure holds all callbacks that the application should implement.
 * None is mandatory.
**/
typedef struct _LinphoneCoreVTable{
    LinphoneCoreGlobalStateChangedCb global_state_changed; /**<Notifies global state changes*/
    LinphoneCoreRegistrationStateChangedCb registration_state_changed;/**<Notifies registration state changes*/
    LinphoneCoreCallStateChangedCb call_state_changed;/**<Notifies call state changes*/
    LinphoneCoreNotifyPresenceReceivedCb notify_presence_received; /**< Notify received presence events*/
    LinphoneCoreNotifyPresenceReceivedForUriOrTelCb notify_presence_received_for_uri_or_tel; /**< Notify received presence events*/
    LinphoneCoreNewSubscriptionRequestedCb new_subscription_requested; /**< Notify about pending presence subscription request */
    LINPHONE_DEPRECATED LinphoneCoreAuthInfoRequestedCb auth_info_requested; /**< @deprecated Use authentication_requested instead. Ask the application some authentication information */
    LinphoneCoreAuthenticationRequestedCb authentication_requested; /**< Ask the application some authentication information */
    LinphoneCoreCallLogUpdatedCb call_log_updated; /**< Notifies that call log list has been updated */
    LinphoneCoreMessageReceivedCb message_received; /**< a message is received, can be text or external body*/
    LinphoneCoreCbsMessageReceivedUnableDecryptCb message_received_unable_decrypt; /**< an encrypted message is received but we can't decrypt it*/
    LinphoneCoreIsComposingReceivedCb is_composing_received; /**< An is-composing notification has been received */
    LinphoneCoreDtmfReceivedCb dtmf_received; /**< A dtmf has been received received */
    LinphoneCoreReferReceivedCb refer_received; /**< An out of call refer was received */
    LinphoneCoreCallEncryptionChangedCb call_encryption_changed; /**<Notifies on change in the encryption of call streams */
    LinphoneCoreTransferStateChangedCb transfer_state_changed; /**<Notifies when a transfer is in progress */
    LinphoneCoreBuddyInfoUpdatedCb buddy_info_updated; /**< a LinphoneFriend's BuddyInfo has changed*/
    LinphoneCoreCallStatsUpdatedCb call_stats_updated; /**<Notifies on refreshing of call's statistics. */
    LinphoneCoreInfoReceivedCb info_received; /**<Notifies an incoming informational message received.*/
    LinphoneCoreSubscriptionStateChangedCb subscription_state_changed; /**<Notifies subscription state change */
    LinphoneCoreNotifyReceivedCb notify_received; /**< Notifies a an event notification, see linphone_core_subscribe() */
    LinphoneCorePublishStateChangedCb publish_state_changed;/**Notifies publish state change (only from #LinphoneEvent api)*/
    LinphoneCoreConfiguringStatusCb configuring_status; /** Notifies configuring status changes */
    LINPHONE_DEPRECATED DisplayStatusCb display_status; /**< @deprecated Callback that notifies various events with human readable text.*/
    LINPHONE_DEPRECATED DisplayMessageCb display_message;/**< @deprecated Callback to display a message to the user */
    LINPHONE_DEPRECATED DisplayMessageCb display_warning;/**< @deprecated Callback to display a warning to the user */
    LINPHONE_DEPRECATED DisplayUrlCb display_url; /**< @deprecated */
    LINPHONE_DEPRECATED ShowInterfaceCb show; /**< vNotifies the application that it should show up*/
    LINPHONE_DEPRECATED LinphoneCoreTextMessageReceivedCb text_received; /**< @deprecated, use #message_received instead <br> A text message has been received */
    LINPHONE_DEPRECATED LinphoneCoreFileTransferRecvCb file_transfer_recv; /**< @deprecated Callback to store file received attached to a #LinphoneChatMessage */
    LINPHONE_DEPRECATED LinphoneCoreFileTransferSendCb file_transfer_send; /**< @deprecated Callback to collect file chunk to be sent for a #LinphoneChatMessage */
    LINPHONE_DEPRECATED LinphoneCoreFileTransferProgressIndicationCb file_transfer_progress_indication; /**< @deprecated Callback to indicate file transfer progress */
    LinphoneCoreNetworkReachableCb network_reachable; /**< Callback to report IP network status (I.E up/down )*/
    LinphoneCoreLogCollectionUploadStateChangedCb log_collection_upload_state_changed; /**< Callback to upload collected logs */
    LinphoneCoreLogCollectionUploadProgressIndicationCb log_collection_upload_progress_indication; /**< Callback to indicate log collection upload progress */
    LinphoneCoreFriendListCreatedCb friend_list_created;
    LinphoneCoreFriendListRemovedCb friend_list_removed;
    void *user_data; /**<User data associated with the above callbacks */
} LinphoneCoreVTable;

linphone_core_v_table_new()就是创建一个内存空间

LinphoneCoreVTable *linphone_core_v_table_new() {
    return ms_new0(LinphoneCoreVTable,1);
}

LinphoneCoreData, 这个代码实在有点长, 但这个才是真正回调的逻辑, 前边LinphoneJavaBinding只是绑定java程序而已。

class LinphoneCoreData {
public:
    LinphoneCoreData(JNIEnv *env, jobject lc, LinphoneCoreVTable *vTable, jobject alistener, LinphoneJavaBindings *ljb) {
        core = env->NewGlobalRef(lc);
        listener = env->NewGlobalRef(alistener);

        memset(vTable, 0, sizeof(LinphoneCoreVTable));

        if (ljb->displayStatusId) {
            vTable->display_status = displayStatusCb;
        }

        if (ljb->globalStateId) {
            vTable->global_state_changed = globalStateChange;
        }

        if (ljb->registrationStateId) {
            vTable->registration_state_changed = registrationStateChange;
        }

        if (ljb->callStateId) {
            vTable->call_state_changed = callStateChange;
        }

        if (ljb->transferStateId) {
            vTable->transfer_state_changed = transferStateChanged;
        }

        if (ljb->callStatsUpdatedId) {
            vTable->call_stats_updated = callStatsUpdated;
        }

        if (ljb->callEncryptionChangedId) {
            vTable->call_encryption_changed = callEncryptionChange;
        }

        if (ljb->newSubscriptionRequestId) {
            vTable->new_subscription_requested = new_subscription_requested;
        }

        if (ljb->authInfoRequestedId) {
            vTable->auth_info_requested = authInfoRequested;
        }

        if (ljb->authenticationRequestedId) {
            vTable->authentication_requested = authenticationRequested;
        }

        if (ljb->notifyPresenceReceivedId) {
            vTable->notify_presence_received = notify_presence_received;
        }

        if (ljb->messageReceivedId) {
            vTable->message_received = message_received;
        }

        if (ljb->messageReceivedUnableToDecryptedId) {
            vTable->message_received_unable_decrypt = message_received_unable_decrypt;
        }

        if (ljb->isComposingReceivedId) {
            vTable->is_composing_received = is_composing_received;
        }

        if (ljb->dtmfReceivedId) {
            vTable->dtmf_received = dtmf_received;
        }

        if (ljb->infoReceivedId) {
            vTable->info_received = infoReceived;
        }

        if (ljb->subscriptionStateId) {
            vTable->subscription_state_changed = subscriptionStateChanged;
        }

        if (ljb->publishStateId) {
            vTable->publish_state_changed = publishStateChanged;
        }

        if (ljb->notifyRecvId) {
            vTable->notify_received = notifyReceived;
        }

        if (ljb->configuringStateId) {
            vTable->configuring_status = configuringStatus;
        }

        if (ljb->fileTransferProgressIndicationId) {
            vTable->file_transfer_progress_indication = fileTransferProgressIndication;
        }

        if (ljb->fileTransferSendId) {
            vTable->file_transfer_send = fileTransferSend;
        }

        if (ljb->fileTransferRecvId) {
            vTable->file_transfer_recv = fileTransferRecv;
        }

        if (ljb->logCollectionUploadProgressId) {
            vTable->log_collection_upload_progress_indication = logCollectionUploadProgressIndication;
        }
        if (ljb->logCollectionUploadStateId) {
            vTable->log_collection_upload_state_changed = logCollectionUploadStateChange;
        }

        if (ljb->friendListCreatedId) {
            vTable->friend_list_created = friendListCreated;
        }
        if (ljb->friendListRemovedId) {
            vTable->friend_list_removed = friendListRemoved;
        }
    }

    ~LinphoneCoreData() {
        JNIEnv *env = 0;
        jvm->AttachCurrentThread(&env,NULL);
        env->DeleteGlobalRef(core);
        env->DeleteGlobalRef(listener);
    }

    jobject core;
    jobject listener;

    LinphoneCoreVTable vTable;

    static void displayStatusCb(LinphoneCore *lc, const char *message) {
        JNIEnv *env = 0;
        jint result = jvm->AttachCurrentThread(&env,NULL);
        if (result != 0) {
            ms_error("cannot attach VM");
            return;
        }

        LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_core_get_user_data(lc);
        LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
        LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
        jstring msg = message ? env->NewStringUTF(message) : NULL;
        env->CallVoidMethod(lcData->listener,ljb->displayStatusId,lcData->core,msg);
        handle_possible_java_exception(env, lcData->listener);
        if (msg) {
            env->DeleteLocalRef(msg);
        }
    }
    static void authInfoRequested(LinphoneCore *lc, const char *realm, const char *username, const char *domain) {
        JNIEnv *env = 0;
        jint result = jvm->AttachCurrentThread(&env,NULL);
        if (result != 0) {
            ms_error("cannot attach VM");
            return;
        }

        LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_core_get_user_data(lc);
        LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
        LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
        jstring r = realm ? env->NewStringUTF(realm) : NULL;
        jstring u = username ? env->NewStringUTF(username) : NULL;
        jstring d = domain ? env->NewStringUTF(domain) : NULL;
        env->CallVoidMethod(lcData->listener,
                            ljb->authInfoRequestedId,
                            lcData->core,
                            r,
                            u,
                            d
                        );
        handle_possible_java_exception(env, lcData->listener);
        if (r) {
            env->DeleteLocalRef(r);
        }
        if (u) {
            env->DeleteLocalRef(u);
        }
        if (d) {
            env->DeleteLocalRef(d);
        }
    }
    static void authenticationRequested(LinphoneCore *lc, LinphoneAuthInfo *auth_info, LinphoneAuthMethod method) {
        JNIEnv *env = 0;
        jint result = jvm->AttachCurrentThread(&env,NULL);
        if (result != 0) {
            ms_error("cannot attach VM");
            return;
        }

        LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_core_get_user_data(lc);
        LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
        LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
        env->CallVoidMethod(lcData->listener,
                            ljb->authenticationRequestedId,
                            lcData->core,
                            getAuthInfo(env, lc, auth_info, lcData->core),
                            env->CallStaticObjectMethod(ljb->authMethodClass,ljb->authMethodFromIntId,(jint)method)
                        );
        handle_possible_java_exception(env, lcData->listener);
    }
    static void setCoreIfNotDone(JNIEnv *env, jobject jcore, LinphoneCore *lc){
        jclass objClass = env->GetObjectClass(jcore);
        jfieldID myFieldID = env->GetFieldID(objClass, "nativePtr", "J");
        jlong fieldVal = env->GetLongField(jcore, myFieldID);
        if (fieldVal == 0){
            env->SetLongField(jcore, myFieldID, (jlong)lc);
        }
    }

    static void globalStateChange(LinphoneCore *lc, LinphoneGlobalState gstate,const char* message) {
        JNIEnv *env = 0;
        jint result = jvm->AttachCurrentThread(&env,NULL);
        if (result != 0) {
            ms_error("cannot attach VM");
            return;
        }

        LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_core_get_user_data(lc);
        LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
        LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);

        jobject jcore = lcData->core;
        /*at this stage, the java object may not be aware of its C object, because linphone_core_new() hasn't returned yet.*/
        setCoreIfNotDone(env,jcore, lc);

        jstring msg = message ? env->NewStringUTF(message) : NULL;
        env->CallVoidMethod(lcData->listener
                            ,ljb->globalStateId
                            ,lcData->core
                            ,env->CallStaticObjectMethod(ljb->globalStateClass,ljb->globalStateFromIntId,(jint)gstate),
                            msg);
        handle_possible_java_exception(env, lcData->listener);
        if (msg) {
            env->DeleteLocalRef(msg);
        }
    }
    static void registrationStateChange(LinphoneCore *lc, LinphoneProxyConfig* proxy,LinphoneRegistrationState state,const char* message) {
        JNIEnv *env = 0;
        jint result = jvm->AttachCurrentThread(&env,NULL);
        jobject jproxy;
        if (result != 0) {
            ms_error("cannot attach VM");
            return;
        }

        LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_core_get_user_data(lc);
        LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
        LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
        jstring msg = message ? env->NewStringUTF(message) : NULL;
        env->CallVoidMethod(lcData->listener
                            ,ljb->registrationStateId
                            ,lcData->core
                            ,(jproxy=getProxy(env,proxy,lcData->core))
                            ,env->CallStaticObjectMethod(ljb->registrationStateClass,ljb->registrationStateFromIntId,(jint)state),
                            msg);
        handle_possible_java_exception(env, lcData->listener);
        if (msg) {
            env->DeleteLocalRef(msg);
        }
        if (jproxy) {
            env->DeleteLocalRef(jproxy);
        }
    }

    static void callStateChange(LinphoneCore *lc, LinphoneCall* call,LinphoneCallState state,const char* message) {
        JNIEnv *env = 0;
        jint result = jvm->AttachCurrentThread(&env,NULL);
        jobject jcall;
        if (result != 0) {
            ms_error("cannot attach VM");
            return;
        }

        LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_core_get_user_data(lc);
        LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
        LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
        jstring msg = message ? env->NewStringUTF(message) : NULL;
        env->CallVoidMethod(lcData->listener
                            ,ljb->callStateId
                            ,lcData->core
                            ,(jcall=getCall(env,call))
                            ,env->CallStaticObjectMethod(ljb->callStateClass,ljb->callStateFromIntId,(jint)state),
                            msg);
        handle_possible_java_exception(env, lcData->listener);
        if (state==LinphoneCallReleased) {
            linphone_call_set_user_pointer(call,NULL);
            env->DeleteGlobalRef(jcall);
        }
        if (msg) {
            env->DeleteLocalRef(msg);
        }
    }
    static void callEncryptionChange(LinphoneCore *lc, LinphoneCall* call, bool_t encrypted,const char* authentication_token) {
        JNIEnv *env = 0;
        jint result = jvm->AttachCurrentThread(&env,NULL);
        if (result != 0) {
            ms_error("cannot attach VM");
            return;
        }

        LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_core_get_user_data(lc);
        LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
        LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
        env->CallVoidMethod(lcData->listener
                            ,ljb->callEncryptionChangedId
                            ,lcData->core
                            ,getCall(env,call)
                            ,encrypted
                            ,authentication_token ? env->NewStringUTF(authentication_token) : NULL);
        handle_possible_java_exception(env, lcData->listener);
    }
    static void notify_presence_received(LinphoneCore *lc,  LinphoneFriend *my_friend) {
        JNIEnv *env = 0;
        jint result = jvm->AttachCurrentThread(&env,NULL);
        jobject jfriend = NULL;

        if (result != 0) {
            ms_error("cannot attach VM");
            return;
        }

        LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_core_get_user_data(lc);
        LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
        LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
        jfriend = getFriend(env, my_friend);
        env->CallVoidMethod(lcData->listener
                            ,ljb->notifyPresenceReceivedId
                            ,lcData->core
                            ,jfriend);
        handle_possible_java_exception(env, lcData->listener);
        env->DeleteLocalRef(jfriend);
    }
    static void new_subscription_requested(LinphoneCore *lc,  LinphoneFriend *my_friend, const char* url) {
        JNIEnv *env = 0;
        jint result = jvm->AttachCurrentThread(&env,NULL);
        if (result != 0) {
            ms_error("cannot attach VM");
            return;
        }

        LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_core_get_user_data(lc);
        LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
        LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
        jobject jfriend =  getFriend(env, my_friend);
        env->CallVoidMethod(lcData->listener
                            ,ljb->newSubscriptionRequestId
                            ,lcData->core
                            ,jfriend
                            ,url ? env->NewStringUTF(url) : NULL);
        env->DeleteLocalRef(jfriend);
        handle_possible_java_exception(env, lcData->listener);
    }
    static void dtmf_received(LinphoneCore *lc, LinphoneCall *call, int dtmf) {
        JNIEnv *env = 0;
        jint result = jvm->AttachCurrentThread(&env,NULL);
        if (result != 0) {
            ms_error("cannot attach VM");
            return;
        }

        LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_core_get_user_data(lc);
        LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
        LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
        env->CallVoidMethod(lcData->listener
                            ,ljb->dtmfReceivedId
                            ,lcData->core
                            ,getCall(env,call)
                            ,dtmf);
        handle_possible_java_exception(env, lcData->listener);
    }
    static void message_received(LinphoneCore *lc, LinphoneChatRoom *room, LinphoneChatMessage *msg) {
        JNIEnv *env = 0;
        jint result = jvm->AttachCurrentThread(&env,NULL);
        if (result != 0) {
            ms_error("cannot attach VM");
            return;
        }

        jobject jmsg;
        jobject jroom;
        LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_core_get_user_data(lc);
        LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
        LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
        /*note: we call linphone_chat_message_ref() because the application does not acquire the object when invoked from a callback*/
        env->CallVoidMethod(lcData->listener
                            ,ljb->messageReceivedId
                            ,lcData->core
                            ,(jroom = getChatRoom(env, room))
                            ,(jmsg = getChatMessage(env, msg)));
        handle_possible_java_exception(env, lcData->listener);

        if (jmsg) {
            env->DeleteLocalRef(jmsg);
        }
        if (jroom) {
            env->DeleteLocalRef(jroom);
        }
    }

    static void message_received_unable_decrypt(LinphoneCore *lc, LinphoneChatRoom *room, LinphoneChatMessage *msg) {
        JNIEnv *env = 0;
        jint result = jvm->AttachCurrentThread(&env,NULL);
        if (result != 0) {
            ms_error("cannot attach VM");
            return;
        }

        jobject jmsg;
        jobject jroom;
        LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_core_get_user_data(lc);
        LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
        LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
        /*note: we call linphone_chat_message_ref() because the application does not acquire the object when invoked from a callback*/
        env->CallVoidMethod(lcData->listener
            ,ljb->messageReceivedUnableToDecryptedId
            ,lcData->core
            ,(jroom = getChatRoom(env, room))
            ,(jmsg = getChatMessage(env, msg)));
            handle_possible_java_exception(env, lcData->listener);

            if (jmsg) {
                env->DeleteLocalRef(jmsg);
            }
            if (jroom) {
                env->DeleteLocalRef(jroom);
            }
        }

    static void is_composing_received(LinphoneCore *lc, LinphoneChatRoom *room) {
        JNIEnv *env = 0;
        jint result = jvm->AttachCurrentThread(&env,NULL);
        if (result != 0) {
            ms_error("cannot attach VM");
            return;
        }

        jobject jroom;
        LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_core_get_user_data(lc);
        LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
        LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
        env->CallVoidMethod(lcData->listener
                            ,ljb->isComposingReceivedId
                            ,lcData->core
                            ,(jroom = getChatRoom(env, room)));
        handle_possible_java_exception(env, lcData->listener);

        if (jroom) {
            env->DeleteLocalRef(jroom);
        }
    }
    static void ecCalibrationStatus(LinphoneCore *lc, LinphoneEcCalibratorStatus status, int delay_ms, void *data) {
        JNIEnv *env = 0;
        jint result = jvm->AttachCurrentThread(&env,NULL);
        if (result != 0) {
            ms_error("cannot attach VM");
            return;
        }

        LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_core_get_user_data(lc);
        LinphoneCoreVTable *table = (LinphoneCoreVTable*) data;
        if (table && ljb) {
            LinphoneCoreData* lcData = (LinphoneCoreData*) linphone_core_v_table_get_user_data(table);
            if (ljb->ecCalibrationStatusId) {
                jobject state = env->CallStaticObjectMethod(ljb->ecCalibratorStatusClass, ljb->ecCalibratorStatusFromIntId, (jint)status);
                env->CallVoidMethod(lcData->listener
                                ,ljb->ecCalibrationStatusId
                                ,lcData->core
                                ,state
                                ,delay_ms
                                ,NULL);
                handle_possible_java_exception(env, lcData->listener);
            }
            if (status != LinphoneEcCalibratorInProgress) {
                linphone_core_v_table_destroy(table);
            }
        }

    }
    static void callStatsUpdated(LinphoneCore *lc, LinphoneCall* call, const LinphoneCallStats *stats) {
        JNIEnv *env = 0;
        jobject statsobj;
        jobject callobj;
        jint result = jvm->AttachCurrentThread(&env,NULL);
        if (result != 0) {
            ms_error("cannot attach VM");
            return;
        }

        LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_core_get_user_data(lc);
        LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
        LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
        statsobj = env->NewObject(ljb->callStatsClass, ljb->callStatsId, (jlong)call, (jlong)stats);
        callobj = getCall(env, call);
        env->CallVoidMethod(lcData->listener, ljb->callStatsUpdatedId, lcData->core, callobj, statsobj);
        handle_possible_java_exception(env, lcData->listener);
        if (statsobj) env->DeleteLocalRef(statsobj);
    }
    static void transferStateChanged(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState remote_call_state){
        JNIEnv *env = 0;
        jint result = jvm->AttachCurrentThread(&env,NULL);
        jobject jcall;
        if (result != 0) {
            ms_error("cannot attach VM");
            return;
        }

        LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_core_get_user_data(lc);
        LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
        LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
        env->CallVoidMethod(lcData->listener
                            ,ljb->transferStateId
                            ,lcData->core
                            ,(jcall=getCall(env,call))
                            ,env->CallStaticObjectMethod(ljb->callStateClass,ljb->callStateFromIntId,(jint)remote_call_state)
                            );
        handle_possible_java_exception(env, lcData->listener);
    }
    static void infoReceived(LinphoneCore *lc, LinphoneCall*call, const LinphoneInfoMessage *info){
        JNIEnv *env = 0;
        jint result = jvm->AttachCurrentThread(&env,NULL);
        if (result != 0) {
            ms_error("cannot attach VM");
            return;
        }

        LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_core_get_user_data(lc);
        LinphoneInfoMessage *copy_info=linphone_info_message_copy(info);
        LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
        LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
        env->CallVoidMethod(lcData->listener
                            ,ljb->infoReceivedId
                            ,lcData->core
                            ,getCall(env,call)
                            ,env->NewObject(ljb->infoMessageClass,ljb->infoMessageCtor,(jlong)copy_info)
                            );
        handle_possible_java_exception(env, lcData->listener);
    }
    static void subscriptionStateChanged(LinphoneCore *lc, LinphoneEvent *ev, LinphoneSubscriptionState state){
        JNIEnv *env = 0;
        jint result = jvm->AttachCurrentThread(&env,NULL);
        jobject jevent;
        jobject jstate;
        if (result != 0) {
            ms_error("cannot attach VM");
            return;
        }

        LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_core_get_user_data(lc);
        LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
        LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
        jevent=getEvent(env,ev);
        jstate=env->CallStaticObjectMethod(ljb->subscriptionStateClass,ljb->subscriptionStateFromIntId,(jint)state);
        env->CallVoidMethod(lcData->listener
                            ,ljb->subscriptionStateId
                            ,lcData->core
                            ,jevent
                            ,jstate
                            );
        handle_possible_java_exception(env, lcData->listener);
        if (state==LinphoneSubscriptionTerminated){
            /*loose the java reference */
            linphone_event_set_user_data(ev,NULL);
            env->DeleteGlobalRef(jevent);
        }
    }
    static void publishStateChanged(LinphoneCore *lc, LinphoneEvent *ev, LinphonePublishState state){
        JNIEnv *env = 0;
        jint result = jvm->AttachCurrentThread(&env,NULL);
        jobject jevent;
        jobject jstate;
        if (result != 0) {
            ms_error("cannot attach VM");
            return;
        }

        LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_core_get_user_data(lc);
        LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
        LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
        jevent=getEvent(env,ev);
        jstate=env->CallStaticObjectMethod(ljb->publishStateClass,ljb->publishStateFromIntId,(jint)state);
        env->CallVoidMethod(lcData->listener
                            ,ljb->publishStateId
                            ,lcData->core
                            ,jevent
                            ,jstate
                            );
        handle_possible_java_exception(env, lcData->listener);
    }
    static void notifyReceived(LinphoneCore *lc, LinphoneEvent *ev, const char *evname, const LinphoneContent *content){
        JNIEnv *env = 0;
        jint result = jvm->AttachCurrentThread(&env,NULL);
        jobject jevent;
        if (result != 0) {
            ms_error("cannot attach VM");
            return;
        }

        LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_core_get_user_data(lc);
        LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
        LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
        jevent=getEvent(env,ev);
        env->CallVoidMethod(lcData->listener
                            ,ljb->notifyRecvId
                            ,lcData->core
                            ,jevent
                            ,env->NewStringUTF(evname)
                            ,content ? create_java_linphone_content(env,content) : NULL
                            );
        handle_possible_java_exception(env, lcData->listener);
    }

    static void configuringStatus(LinphoneCore *lc, LinphoneConfiguringState status, const char *message) {
        JNIEnv *env = 0;
        jint result = jvm->AttachCurrentThread(&env,NULL);
        if (result != 0) {
            ms_error("cannot attach VM");
            return;
        }

        LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_core_get_user_data(lc);
        LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
        LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
        env->CallVoidMethod(lcData->listener, ljb->configuringStateId, lcData->core, env->CallStaticObjectMethod(ljb->configuringStateClass,ljb->configuringStateFromIntId,(jint)status), message ? env->NewStringUTF(message) : NULL);
        handle_possible_java_exception(env, lcData->listener);
    }

    static void fileTransferProgressIndication(LinphoneCore *lc, LinphoneChatMessage *message, const LinphoneContent* content, size_t offset, size_t total) {
        JNIEnv *env = 0;
        jobject jmsg;
        size_t progress = (offset * 100) / total;
        jint result = jvm->AttachCurrentThread(&env,NULL);
        if (result != 0) {
            ms_error("cannot attach VM");
            return;
        }

        LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_core_get_user_data(lc);
        LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
        LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
        jobject jcontent = content ? create_java_linphone_content(env, content) : NULL;
        env->CallVoidMethod(lcData->listener,
                ljb->fileTransferProgressIndicationId,
                lcData->core,
                (jmsg = getChatMessage(env, message)),
                jcontent,
                progress);
        if (jcontent) {
            env->DeleteLocalRef(jcontent);
        }
        if (jmsg) {
            env->DeleteLocalRef(jmsg);
        }
        handle_possible_java_exception(env, lcData->listener);
    }

    static void fileTransferSend(LinphoneCore *lc, LinphoneChatMessage *message, const LinphoneContent* content, char* buff, size_t* size) {
        JNIEnv *env = 0;
        jobject jmsg;
        size_t asking = *size;
        jint result = jvm->AttachCurrentThread(&env,NULL);
        if (result != 0) {
            ms_error("cannot attach VM");
            return;
        }

        LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_core_get_user_data(lc);
        LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
        LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
        jobject jcontent = content ? create_java_linphone_content(env, content) : NULL;
        jobject jbuffer = buff ? env->NewDirectByteBuffer(buff, asking) : NULL;
        *size = env->CallIntMethod(lcData->listener,
                ljb->fileTransferSendId,
                lcData->core,
                (jmsg = getChatMessage(env, message)),
                jcontent,
                jbuffer,
                asking);
        if (jcontent) {
            env->DeleteLocalRef(jcontent);
        }
        if (jbuffer) {
            env->DeleteLocalRef(jbuffer);
        }
        if (jmsg) {
            env->DeleteLocalRef(jmsg);
        }
        handle_possible_java_exception(env, lcData->listener);
    }

    static void fileTransferRecv(LinphoneCore *lc, LinphoneChatMessage *message, const LinphoneContent* content, const char* buff, size_t size) {
        JNIEnv *env = 0;
        jobject jmsg;
        jint result = jvm->AttachCurrentThread(&env,NULL);
        if (result != 0) {
            ms_error("cannot attach VM");
            return;
        }

        LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_core_get_user_data(lc);
        LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
        LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);

        jbyteArray jbytes = env->NewByteArray(size);
        env->SetByteArrayRegion(jbytes, 0, size, (jbyte*)buff);
        jobject jcontent = content ? create_java_linphone_content(env, content) : NULL;

        env->CallVoidMethod(lcData->listener,
                ljb->fileTransferRecvId,
                lcData->core,
                (jmsg = getChatMessage(env, message)),
                jcontent,
                jbytes,
                size);
        if (jcontent) {
            env->DeleteLocalRef(jcontent);
        }
        if (jmsg) {
            env->DeleteLocalRef(jmsg);
        }
        handle_possible_java_exception(env, lcData->listener);
    }
    static void logCollectionUploadProgressIndication(LinphoneCore *lc, size_t offset, size_t total) {
        JNIEnv *env = 0;
        jint result = jvm->AttachCurrentThread(&env,NULL);
        if (result != 0) {
            ms_error("cannot attach VM");
            return;
        }

        LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_core_get_user_data(lc);
        LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
        LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
        env->CallVoidMethod(lcData->listener
                            ,ljb->logCollectionUploadProgressId
                            ,lcData->core
                            ,(jlong)offset
                            ,(jlong)total);
        handle_possible_java_exception(env, lcData->listener);
    }
    static void logCollectionUploadStateChange(LinphoneCore *lc, LinphoneCoreLogCollectionUploadState state, const char *info) {
        JNIEnv *env = 0;
        jint result = jvm->AttachCurrentThread(&env,NULL);
        if (result != 0) {
            ms_error("cannot attach VM");
            return;
        }

        LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_core_get_user_data(lc);
        LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
        LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
        jstring msg = info ? env->NewStringUTF(info) : NULL;
        env->CallVoidMethod(lcData->listener
                            ,ljb->logCollectionUploadStateId
                            ,lcData->core
                            ,env->CallStaticObjectMethod(ljb->logCollectionUploadStateClass,ljb->logCollectionUploadStateFromIntId,(jint)state),
                            msg);
        handle_possible_java_exception(env, lcData->listener);
        if (msg) {
            env->DeleteLocalRef(msg);
        }
    }
    static void friendListCreated(LinphoneCore *lc, LinphoneFriendList *list) {
        JNIEnv *env = 0;
        jint result = jvm->AttachCurrentThread(&env,NULL);
        if (result != 0) {
            ms_error("cannot attach VM");
            return;
        }

        LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_core_get_user_data(lc);
        LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
        LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
        env->CallVoidMethod(lcData->listener
                            ,ljb->friendListCreatedId
                            ,lcData->core
                            ,getFriendList(env, list));
        handle_possible_java_exception(env, lcData->listener);
    }
    static void friendListRemoved(LinphoneCore *lc, LinphoneFriendList *list) {
        JNIEnv *env = 0;
        jint result = jvm->AttachCurrentThread(&env,NULL);
        if (result != 0) {
            ms_error("cannot attach VM");
            return;
        }

        jobject jfriendlist;
        LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_core_get_user_data(lc);
        LinphoneCoreVTable *table = linphone_core_get_current_vtable(lc);
        LinphoneCoreData* lcData = (LinphoneCoreData*)linphone_core_v_table_get_user_data(table);
        env->CallVoidMethod(lcData->listener
                            ,ljb->friendListRemovedId
                            ,lcData->core
                            ,(jfriendlist = getFriendList(env, list)));
        handle_possible_java_exception(env, lcData->listener);

        if (jfriendlist) {
            env->DeleteLocalRef(jfriendlist);
        }
    }

private:
    static inline void handle_possible_java_exception(JNIEnv *env, jobject listener)
    {
        if (env->ExceptionCheck()) {
            env->ExceptionDescribe();
            env->ExceptionClear();
            ms_error("Listener %p raised an exception",listener);
        }
    }
};

原来所有的最原始的回调在这儿呢

# LinphoneManger implements LinphoneCoreListener

private synchronized void startLibLinphone(Context c) {
    try {
        copyAssetsFromPackage();
        //traces alway start with traces enable to not missed first initialization

        mLc = LinphoneCoreFactory.instance().createLinphoneCore(this, mLinphoneConfigFile, mLinphoneFactoryConfigFile, null, c);

        TimerTask lTask = new TimerTask() {
            @Override
            public void run() {
                UIThreadDispatcher.dispatch(new Runnable() {
                    @Override
                    public void run() {
                        if (mLc != null) {
                            mLc.iterate();
                        }
                    }
                });
            }
        };
        /*use schedule instead of scheduleAtFixedRate to avoid iterate from being call in burst after cpu wake up*/
        mTimer = new Timer("Linphone scheduler");
        mTimer.schedule(lTask, 0, 20);
    } catch (Exception e) {
        Log.e(e);
        Log.e(e, "Cannot start linphone");
    }
}

linphone_core_v_table_set_user_data(vTable, ldata);,这么小的函数让我忐忑啊, 可还是设置回调的

void linphone_core_v_table_set_user_data(LinphoneCoreVTable *table, void *data) {
    table->user_data = data;
}

linphone_core_new(vTable, userConfig, factoryConfig, ljb);, 这是这个线索最后的希望了。

static LinphoneCore *_linphone_core_new(const LinphoneCoreVTable *vtable,
                        const char *config_path, const char *factory_config_path, void * userdata) {
    LinphoneCore *lc;
    LpConfig *config = lp_config_new_with_factory(config_path, factory_config_path);
    lc=linphone_core_new_with_config(vtable, config, userdata);
    lp_config_unref(config);
    return lc;
}

LinphoneCore *linphone_core_new(const LinphoneCoreVTable *vtable,
                        const char *config_path, const char *factory_config_path, void * userdata) {
    return _linphone_core_new(vtable, config_path, factory_config_path, userdata);
}

这里又有几个函数

  1. LpConfig *config = lp_config_new_with_factory(config_path, factory_config_path);
  2. lc=linphone_core_new_with_config(vtable, config, userdata);
  3. lp_config_unref(config);

LpConfig

#lpconfig.h
struct _LpConfig{
    belle_sip_object_t base;
    bctbx_vfs_file_t* pFile;
    char *filename;
    char *tmpfilename;
    bctbx_list_t *sections;
    int modified;
    int readonly;
    bctbx_vfs_t* g_bctbx_vfs;
};

liphone_config_new_with_factory

#lpconfig.c
LpConfig *linphone_config_new_with_factory(const char *config_filename, const char *factory_config_filename) {
    LpConfig *lpconfig=belle_sip_object_new(LinphoneConfig);
    if (_linphone_config_init_from_files(lpconfig, config_filename, factory_config_filename) == 0) {
        return lpconfig;
    } else {
        ms_free(lpconfig);
        return NULL;
    }
}

通过这个跟文件打交道

static int _linphone_config_init_from_files(LinphoneConfig *lpconfig, const char *config_filename, const char *factory_config_filename) {
    lpconfig->g_bctbx_vfs = bctbx_vfs_get_default();

    if (config_filename!=NULL){
        if(ortp_file_exist(config_filename) == 0) {
            lpconfig->filename=lp_realpath(config_filename, NULL);
            if(lpconfig->filename == NULL) {
                ms_error("Could not find the real path of %s: %s", config_filename, strerror(errno));
                goto fail;
            }
        } else {
            lpconfig->filename = ms_strdup(config_filename);
        }
        lpconfig->tmpfilename=ortp_strdup_printf("%s.tmp",lpconfig->filename);
        ms_message("Using (r/w) config information from %s", lpconfig->filename);

#if !defined(_WIN32)
        {
            struct stat fileStat;
            if ((stat(lpconfig->filename,&fileStat) == 0) && (S_ISREG(fileStat.st_mode))) {
                /* make existing configuration files non-group/world-accessible */
                if (chmod(lpconfig->filename, S_IRUSR | S_IWUSR) == -1) {
                    ms_warning("unable to correct permissions on "
                        "configuration file: %s", strerror(errno));
                }
            }
        }
#endif /*_WIN32*/

        /*open with r+ to check if we can write on it later*/
        lpconfig->pFile = bctbx_file_open(lpconfig->g_bctbx_vfs,lpconfig->filename, "r+");
#ifdef RENAME_REQUIRES_NONEXISTENT_NEW_PATH
        if (lpconfig->pFile == NULL){
            lpconfig->pFile = bctbx_file_open(lpconfig->g_bctbx_vfs,lpconfig->tmpfilename, "r+");
            if (lpconfig->pFile == NULL){
                ms_warning("Could not open %s but %s works, app may have crashed during last sync.",lpconfig->filename,lpconfig->tmpfilename);
            }
        }
#endif
        if (lpconfig->pFile != NULL){
            linphone_config_parse(lpconfig, lpconfig->pFile);
            bctbx_file_close(lpconfig->pFile);
            lpconfig->pFile = NULL;
            lpconfig->modified=0;
        }
    }
    if (factory_config_filename != NULL) {
        linphone_config_read_file(lpconfig, factory_config_filename);
    }
    return 0;

fail:
    return -1;
}

最后一个函数linphone_core_new_with_config, 比我想象的要简单

#linphone.c
LinphoneCore *linphone_core_new_with_config(const LinphoneCoreVTable *vtable, struct _LpConfig *config, void *userdata) {
    LinphoneCoreCbs *cbs = linphone_factory_create_core_cbs(linphone_factory_get());
    LinphoneCoreVTable *local_vtable = linphone_core_v_table_new();
    LinphoneCore *core = NULL;
    if (vtable != NULL) *local_vtable = *vtable;
    _linphone_core_cbs_set_v_table(cbs, local_vtable, TRUE);
    core = _linphone_core_new_with_config(cbs, config, userdata);
    linphone_core_cbs_unref(cbs);
    return core;
}

又新出来几个函数

  1. LinphoneCoreCbs *cbs = linphone_factory_create_core_cbs(linphone_factory_get())
  2. LinphoneCoreVTable *local_vtable = linphone_core_v_table_new();
  3. _linphone_core_cbs_set_v_table(cbs, local_vtable, TRUE);
  4. core = _linphone_core_new_with_config(cbs, config, userdata);

LinphoneCoreCbs

# private.h
struct _LinphoneCoreCbs {
    belle_sip_object_t base;
    LinphoneCoreVTable *vtable;
    bool_t autorelease;
};

linphone_factory_get(void)

LinphoneFactory *linphone_factory_get(void) {
    if (_factory == NULL) {
        _factory = belle_sip_object_new(LinphoneFactory);
        atexit(_linphone_factory_destroying_cb);
    }
    return _factory;
}

static void _linphone_factory_destroying_cb(void) {
    if (_factory != NULL) {
        belle_sip_object_unref(_factory);
        _factory = NULL;
    }
}

_linphone_core_cbs_new(void)

LinphoneCoreCbs *_linphone_core_cbs_new(void) {
    LinphoneCoreCbs *obj = belle_sip_object_new(LinphoneCoreCbs);
    obj->vtable = ms_new0(LinphoneCoreVTable, 1);
    obj->autorelease = TRUE;
    return obj;
}

LinphoneCoreVTable *linphone_core_v_table_new() {
    return ms_new0(LinphoneCoreVTable,1);
}

_linphone_core_cbs_set_v_table

void _linphone_core_cbs_set_v_table(LinphoneCoreCbs *cbs, LinphoneCoreVTable *vtable, bool_t autorelease) {
    ms_free(cbs->vtable);
    cbs->vtable = vtable;
    cbs->autorelease = autorelease;
}

_linphone_core_new_with_config

LinphoneCore *_linphone_core_new_with_config(LinphoneCoreCbs *cbs, struct _LpConfig *config, void *userdata) {
    LinphoneCore *core = belle_sip_object_new(LinphoneCore);
    linphone_core_init(core, cbs, config, userdata);
    return core;
}

belle_sip_object_unref

#belle_sip_object.c
void belle_sip_object_unref(void *ptr){
    belle_sip_object_t *obj=BELLE_SIP_OBJECT(ptr);
    if (obj->ref <= -1) {
        belle_sip_error("Object [%p] freed twice or corrupted !",obj);
        if (obj->vptr && obj->vptr->type_name) belle_sip_error("Object type might be [%s]",obj->vptr->type_name);
        if (obj->name) belle_sip_error("Object name might be [%s]",obj->name);
        belle_sip_fatal("Fatal object error encountered, aborting.");
        return;
    }
    if (obj->vptr->initially_unowned && obj->ref==0){
        if (obj->pool)
            belle_sip_object_pool_remove(obj->pool,obj);
        obj->ref=-1;
        belle_sip_object_delete(obj);
        return;
    }
    obj->ref--;
    if (obj->ref == 0){
        obj->ref = -1;
        belle_sip_object_delete(obj);
    }
}

linphone_core_init

static void linphone_core_init(LinphoneCore * lc, LinphoneCoreCbs *cbs, LpConfig *config, void * userdata){
    const char *remote_provisioning_uri = NULL;
    LinphoneFactory *lfactory = linphone_factory_get();
    LinphoneCoreCbs *internal_cbs = _linphone_core_cbs_new();
    char *msplugins_dir;
    char *image_resources_dir;

    ms_message("Initializing LinphoneCore %s", linphone_core_get_version());

    lc->config=lp_config_ref(config);
    lc->data=userdata;
    lc->ringstream_autorelease=TRUE;

    linphone_task_list_init(&lc->hooks);

    linphone_core_cbs_set_notify_received(internal_cbs, linphone_core_internal_notify_received);
    linphone_core_cbs_set_subscription_state_changed(internal_cbs, linphone_core_internal_subscription_state_changed);
    _linphone_core_add_callbacks(lc, internal_cbs, TRUE);
    belle_sip_object_unref(internal_cbs);


    if (cbs != NULL) {
        _linphone_core_add_callbacks(lc, cbs, FALSE);
    } else {
        LinphoneCoreCbs *fallback_cbs = linphone_factory_create_core_cbs(linphone_factory_get());
        _linphone_core_add_callbacks(lc, fallback_cbs, FALSE);
        belle_sip_object_unref(fallback_cbs);
    }


    linphone_core_set_state(lc,LinphoneGlobalStartup,"Starting up");
    ortp_init();
    linphone_core_activate_log_serialization_if_needed();

    msplugins_dir = linphone_factory_get_msplugins_dir(lfactory);
    lc->factory = ms_factory_new_with_voip_and_plugins_dir(msplugins_dir);
    if (msplugins_dir) bctbx_free(msplugins_dir);
    image_resources_dir = linphone_factory_get_image_resources_dir(lfactory);
    ms_factory_set_image_resources_dir(lc->factory, image_resources_dir);
    bctbx_free(image_resources_dir);
    linphone_core_register_default_codecs(lc);
    linphone_core_register_offer_answer_providers(lc);
    /* Get the mediastreamer2 event queue */
    /* This allows to run event's callback in linphone_core_iterate() */
    lc->msevq=ms_factory_create_event_queue(lc->factory);

    lc->sal=sal_init(lc->factory);
    sal_set_http_proxy_host(lc->sal, linphone_core_get_http_proxy_host(lc));
    sal_set_http_proxy_port(lc->sal, linphone_core_get_http_proxy_port(lc));

    sal_set_user_pointer(lc->sal,lc);
    sal_set_callbacks(lc->sal,&linphone_sal_callbacks);

#ifdef TUNNEL_ENABLED
    lc->tunnel=linphone_core_tunnel_new(lc);
#endif

    lc->network_last_check = 0;
    lc->network_last_status = FALSE;

    /* Create the http provider in dual stack mode (ipv4 and ipv6.
     * If this creates problem, we may need to implement parallel ipv6/ ipv4 http requests in belle-sip.
     */
    lc->http_provider = belle_sip_stack_create_http_provider(sal_get_stack_impl(lc->sal), "::0");
    lc->http_crypto_config = belle_tls_crypto_config_new();
    belle_http_provider_set_tls_crypto_config(lc->http_provider,lc->http_crypto_config);

    certificates_config_read(lc);

    lc->ringtoneplayer = linphone_ringtoneplayer_new();

#ifdef SQLITE_STORAGE_ENABLED
    sqlite3_bctbx_vfs_register(0);
#endif

    lc->vcard_context = linphone_vcard_context_new();
    linphone_core_initialize_supported_content_types(lc);

    remote_provisioning_uri = linphone_core_get_provisioning_uri(lc);
    if (remote_provisioning_uri == NULL) {
        linphone_configuring_terminated(lc, LinphoneConfiguringSkipped, NULL);
    } // else linphone_core_start will be called after the remote provisioning (see linphone_core_iterate)
    lc->bw_controller = ms_bandwidth_controller_new();
}

linphone_core_register_default_codecs, 这个才是真正用得到的地方


static void linphone_core_register_default_codecs(LinphoneCore *lc){
    const char *aac_fmtp162248, *aac_fmtp3244;
    bool_t opus_enabled=TRUE;
    /*default enabled audio codecs, in order of preference*/
#if defined(__arm__) || defined(_M_ARM)
    /*hack for opus, that needs to be disabed by default on ARM single processor, otherwise there is no cpu left for video processing*/
    //if (ms_get_cpu_count()==1) opus_enabled=FALSE;
    if (ms_factory_get_cpu_count(lc->factory)==1) opus_enabled=FALSE;
#endif
    linphone_core_register_payload_type(lc,&payload_type_opus,"useinbandfec=1",opus_enabled);
    linphone_core_register_payload_type(lc,&payload_type_silk_wb,NULL,TRUE);
    linphone_core_register_payload_type(lc,&payload_type_speex_wb,"vbr=on",TRUE);
    linphone_core_register_payload_type(lc,&payload_type_speex_nb,"vbr=on",TRUE);
    linphone_core_register_payload_type(lc,&payload_type_pcmu8000,NULL,TRUE);
    linphone_core_register_payload_type(lc,&payload_type_pcma8000,NULL,TRUE);

    /* Text codecs in order or preference (RED first (more robust), then T140) */
    linphone_core_register_payload_type(lc, &payload_type_t140_red, NULL, TRUE);
    linphone_core_register_payload_type(lc, &payload_type_t140, NULL, TRUE);

    /*other audio codecs, not enabled by default, in order of preference*/
    linphone_core_register_payload_type(lc,&payload_type_gsm,NULL,FALSE);
    linphone_core_register_payload_type(lc,&payload_type_g722,NULL,FALSE);
    linphone_core_register_payload_type(lc,&payload_type_ilbc,"mode=30",FALSE);
    linphone_core_register_payload_type(lc,&payload_type_amr,"octet-align=1",FALSE);
    linphone_core_register_payload_type(lc,&payload_type_amrwb,"octet-align=1",FALSE);
    linphone_core_register_payload_type(lc,&payload_type_g729,"annexb=yes",TRUE);
    /* For AAC, we use a config value to determine if we ought to support SBR. Since it is not offically supported
     * for the mpeg4-generic mime type, setting this flag to 1 will break compatibility with other clients. */
    if( lp_config_get_int(lc->config, "misc", "aac_use_sbr", FALSE) ) {
        ms_message("Using SBR for AAC");
        aac_fmtp162248 = "config=F8EE2000; constantDuration=512; indexDeltaLength=3; indexLength=3; mode=AAC-hbr; profile-level-id=76; sizeLength=13; streamType=5; SBR-enabled=1";
        aac_fmtp3244   = "config=F8E82000; constantDuration=512; indexDeltaLength=3; indexLength=3; mode=AAC-hbr; profile-level-id=76; sizeLength=13; streamType=5; SBR-enabled=1";
    } else {
        aac_fmtp162248 = "config=F8EE2000; constantDuration=512; indexDeltaLength=3; indexLength=3; mode=AAC-hbr; profile-level-id=76; sizeLength=13; streamType=5";
        aac_fmtp3244   = "config=F8E82000; constantDuration=512; indexDeltaLength=3; indexLength=3; mode=AAC-hbr; profile-level-id=76; sizeLength=13; streamType=5";
    }
    linphone_core_register_payload_type(lc,&payload_type_aaceld_16k,aac_fmtp162248,FALSE);
    linphone_core_register_payload_type(lc,&payload_type_aaceld_22k,aac_fmtp162248,FALSE);
    linphone_core_register_payload_type(lc,&payload_type_aaceld_32k,aac_fmtp3244,FALSE);
    linphone_core_register_payload_type(lc,&payload_type_aaceld_44k,aac_fmtp3244,FALSE);
    linphone_core_register_payload_type(lc,&payload_type_aaceld_48k,aac_fmtp162248,FALSE);
    linphone_core_register_payload_type(lc,&payload_type_isac,NULL,FALSE);
    linphone_core_register_payload_type(lc,&payload_type_speex_uwb,"vbr=on",FALSE);
    linphone_core_register_payload_type(lc,&payload_type_silk_nb,NULL,FALSE);
    linphone_core_register_payload_type(lc,&payload_type_silk_mb,NULL,FALSE);
    linphone_core_register_payload_type(lc,&payload_type_silk_swb,NULL,FALSE);
    linphone_core_register_payload_type(lc,&payload_type_g726_16,NULL,FALSE);
    linphone_core_register_payload_type(lc,&payload_type_g726_24,NULL,FALSE);
    linphone_core_register_payload_type(lc,&payload_type_g726_32,NULL,FALSE);
    linphone_core_register_payload_type(lc,&payload_type_g726_40,NULL,FALSE);
    linphone_core_register_payload_type(lc,&payload_type_aal2_g726_16,NULL,FALSE);
    linphone_core_register_payload_type(lc,&payload_type_aal2_g726_24,NULL,FALSE);
    linphone_core_register_payload_type(lc,&payload_type_aal2_g726_32,NULL,FALSE);
    linphone_core_register_payload_type(lc,&payload_type_aal2_g726_40,NULL,FALSE);
    linphone_core_register_payload_type(lc,&payload_type_codec2,NULL,FALSE);
    linphone_core_register_payload_type(lc,&payload_type_bv16,NULL,FALSE);


#ifdef VIDEO_ENABLED
    /*default enabled video codecs, in order of preference*/
    linphone_core_register_payload_type(lc,&payload_type_vp8,NULL,TRUE);
    linphone_core_register_payload_type(lc,&payload_type_h264,"profile-level-id=42801F",TRUE);
    linphone_core_register_payload_type(lc,&payload_type_mp4v,"profile-level-id=3",TRUE);
    linphone_core_register_payload_type(lc,&payload_type_h263_1998,"CIF=1;QCIF=1",FALSE);
    linphone_core_register_payload_type(lc,&payload_type_h263,NULL,FALSE);
#endif
    /*register all static payload types declared in av_profile of oRTP, if not already declared above*/
    linphone_core_register_static_payloads(lc);
}

在这个函数中找到了一些端倪

static void linphone_core_register_payload_type(LinphoneCore *lc, const PayloadType *const_pt, const char *recv_fmtp, bool_t enabled){
    bctbx_list_t **codec_list = const_pt->type==PAYLOAD_VIDEO ? &lc->default_video_codecs : const_pt->type==PAYLOAD_TEXT ? &lc->default_text_codecs : &lc->default_audio_codecs;
    PayloadType *pt=payload_type_clone(const_pt);
    int number=-1;
    payload_type_set_enable(pt,enabled);
    if (recv_fmtp!=NULL) payload_type_set_recv_fmtp(pt,recv_fmtp);
    /*Set a number to the payload type from the statically defined (RFC3551) profile, if not static, -1 is returned
        and the payload type number will be determined dynamically later, at call time.*/
    payload_type_set_number(pt,
        (number=rtp_profile_find_payload_number(&av_profile, pt->mime_type, pt->clock_rate, pt->channels))
    );
    ms_message("Codec %s/%i fmtp=[%s] number=%i, default enablement: %i) added to the list of possible codecs.", pt->mime_type, pt->clock_rate,
            pt->recv_fmtp ? pt->recv_fmtp : "", number, (int)payload_type_enabled(pt));
    *codec_list=bctbx_list_append(*codec_list,pt);
}

logcat中找到写区别

H264允许
Codec H264/90000 fmtp=[profile-level-id=42801F] number=-1, default enablement: 1) added to the list of possible codecs.
Codec H263-1998/90000 fmtp=[CIF=1;QCIF=1] number=-1, default enablement: 0) added to the list of possible codecs.
Codec H263/90000 fmtp=[] number=34, default enablement: 0) added to the list of possible codecs.
Codec H261/90000 fmtp=[] number=31, default enablement: 0) added to the list of possible codecs.


H264不允许
Codec H264/90000 fmtp=[profile-level-id=42801F] number=-1, default enablement: 1) added to the list of possible codecs.
Codec H263-1998/90000 fmtp=[CIF=1;QCIF=1] number=-1, default enablement: 0) added to the list of possible codecs.
Codec H263/90000 fmtp=[] number=34, default enablement: 0) added to the list of possible codecs.
Codec H261/90000 fmtp=[] number=31, default enablement: 0) added to the list of possible codecs

又一个突破点mLc.iterate(), 调用这个方法, 生成了配置文件

下面就是实实在在的使用的方法

extern "C" void Java_org_linphone_core_LinphoneCoreImpl_iterate(JNIEnv* env
        ,jobject  thiz
        ,jlong lc) {
    linphone_core_iterate((LinphoneCore*)lc);
}

linphone_core_iterate()方法的调用

void linphone_core_iterate(LinphoneCore *lc){
    bctbx_list_t *calls;
    LinphoneCall *call;
    uint64_t curtime_ms = ms_get_cur_time_ms(); /*monotonic time*/
    int elapsed;
    time_t current_real_time = ms_time(NULL);
    int64_t diff_time;
    bool_t one_second_elapsed=FALSE;
    const char *remote_provisioning_uri = NULL;

    if (lc->network_reachable_to_be_notified) {
        lc->network_reachable_to_be_notified=FALSE;
        linphone_core_notify_network_reachable(lc,lc->sip_network_reachable);
    }
    if (linphone_core_get_global_state(lc) == LinphoneGlobalStartup) {
        if (sal_get_root_ca(lc->sal)) {
            belle_tls_crypto_config_set_root_ca(lc->http_crypto_config, sal_get_root_ca(lc->sal));
            belle_http_provider_set_tls_crypto_config(lc->http_provider, lc->http_crypto_config);
        }

        linphone_core_notify_display_status(lc, _("Configuring"));
        linphone_core_set_state(lc, LinphoneGlobalConfiguring, "Configuring");

        remote_provisioning_uri = linphone_core_get_provisioning_uri(lc);
        if (remote_provisioning_uri) {
            int err = linphone_remote_provisioning_download_and_apply(lc, remote_provisioning_uri);
            if (err == -1) {
                linphone_configuring_terminated(lc, LinphoneConfiguringFailed, "Bad URI");
            }
        } // else linphone_configuring_terminated has already been called in linphone_core_init
    }
    if (lc->prevtime_ms == 0){
        lc->prevtime_ms = curtime_ms;
    }
    if ((diff_time=curtime_ms-lc->prevtime_ms) >= 1000){
        one_second_elapsed=TRUE;
        if (diff_time>3000){
            /*since monotonic time doesn't increment while machine is sleeping, we don't want to catchup too much*/
            lc->prevtime_ms = curtime_ms;
        }else{
            lc->prevtime_ms += 1000;

        }
    }

    if (lc->ecc!=NULL){
        LinphoneEcCalibratorStatus ecs=ec_calibrator_get_status(lc->ecc);
        if (ecs!=LinphoneEcCalibratorInProgress){
            if (lc->ecc->cb)
                lc->ecc->cb(lc,ecs,lc->ecc->delay,lc->ecc->cb_data);
            if (ecs==LinphoneEcCalibratorDone){
                int len=lp_config_get_int(lc->config,"sound","ec_tail_len",0);
                int margin=len/2;

                lp_config_set_int(lc->config, "sound", "ec_delay",MAX(lc->ecc->delay-margin,0));
            } else if (ecs == LinphoneEcCalibratorFailed) {
                lp_config_set_int(lc->config, "sound", "ec_delay", -1);/*use default value from soundcard*/
            } else if (ecs == LinphoneEcCalibratorDoneNoEcho) {
                linphone_core_enable_echo_cancellation(lc, FALSE);
            }
            ec_calibrator_destroy(lc->ecc);
            lc->ecc=NULL;
        }
    }

    if (lc->preview_finished){
        lc->preview_finished=0;
        linphone_ringtoneplayer_stop(lc->ringtoneplayer);
        lc_callback_obj_invoke(&lc->preview_finished_cb,lc);
    }

    if (lc->ringstream && lc->ringstream_autorelease && lc->dmfs_playing_start_time!=0
        && (curtime_ms/1000 - lc->dmfs_playing_start_time)>5){
        MSPlayerState state;
        bool_t stop=TRUE;
        if (lc->ringstream->source && ms_filter_call_method(lc->ringstream->source,MS_PLAYER_GET_STATE,&state)==0){
            if (state==MSPlayerPlaying) stop=FALSE;
        }
        if (stop) {
            ms_message("Releasing inactive tone player.");
            linphone_core_stop_dtmf_stream(lc);
        }
    }

    sal_iterate(lc->sal);
    if (lc->msevq) ms_event_queue_pump(lc->msevq);
    if (lc->auto_net_state_mon) monitor_network_state(lc, current_real_time);

    proxy_update(lc);

    //we have to iterate for each call
    calls = lc->calls;
    while(calls!= NULL){
        call = (LinphoneCall *)calls->data;
        elapsed = (int)(current_real_time - call->log->start_date_time);
         /* get immediately a reference to next one in case the one
         we are going to examine is destroy and removed during
         linphone_call_start_invite() */
        calls=calls->next;
        linphone_call_background_tasks(call,one_second_elapsed);
        if (call->state==LinphoneCallOutgoingInit && (elapsed>=lc->sip_conf.delayed_timeout)){
            /*start the call even if the OPTIONS reply did not arrive*/
            if (call->ice_session != NULL) {
                ms_warning("ICE candidates gathering from [%s] has not finished yet, proceed with the call without ICE anyway."
                        ,linphone_core_get_stun_server(lc));
                linphone_call_delete_ice_session(call);
                linphone_call_stop_media_streams_for_ice_gathering(call);
            }
#ifdef BUILD_UPNP
            if (call->upnp_session != NULL) {
                ms_warning("uPnP mapping has not finished yet, proceeded with the call without uPnP anyway.");
                linphone_call_delete_upnp_session(call);
            }
#endif //BUILD_UPNP
            linphone_call_start_invite(call, NULL);
        }
        if (call->state==LinphoneCallIncomingReceived || call->state==LinphoneCallIncomingEarlyMedia){
            if (one_second_elapsed) ms_message("incoming call ringing for %i seconds",elapsed);
            if (elapsed>lc->sip_conf.inc_timeout){
                LinphoneReason decline_reason;
                ms_message("incoming call timeout (%i)",lc->sip_conf.inc_timeout);
                decline_reason = (lc->current_call != call) ? LinphoneReasonBusy : LinphoneReasonDeclined;
                call->log->status=LinphoneCallMissed;
                sal_error_info_set(&call->non_op_error,SalReasonRequestTimeout,408,"Not answered",NULL);
                linphone_core_decline_call(lc,call,decline_reason);
            }
        }
        if ( (lc->sip_conf.in_call_timeout > 0)
             && (call->log->connected_date_time != 0)
             && ((current_real_time - call->log->connected_date_time) > lc->sip_conf.in_call_timeout))
        {
            ms_message("in call timeout (%i)",lc->sip_conf.in_call_timeout);
            linphone_core_terminate_call(lc,call);
        }
    }

    if (linphone_core_video_preview_enabled(lc)){
        if (lc->previewstream==NULL && lc->calls==NULL)
            toggle_video_preview(lc,TRUE);
#ifdef VIDEO_ENABLED
        if (lc->previewstream) video_stream_iterate(lc->previewstream);
#endif
    }else{
        if (lc->previewstream!=NULL)
            toggle_video_preview(lc,FALSE);
    }

    linphone_core_run_hooks(lc);
    linphone_core_do_plugin_tasks(lc);

    if (lc->sip_network_reachable && lc->netup_time!=0 && (current_real_time-lc->netup_time)>=2){
        /*not do that immediately, take your time.*/
        linphone_core_send_initial_subscribes(lc);
    }

    if (one_second_elapsed) {
        bctbx_list_t *elem = NULL;
        if (lp_config_needs_commit(lc->config)) {
            lp_config_sync(lc->config);
        }
        for (elem = lc->friends_lists; elem != NULL; elem = bctbx_list_next(elem)) {
            LinphoneFriendList *list = (LinphoneFriendList *)elem->data;
            if (list->dirty_friends_to_update) {
                linphone_friend_list_update_dirty_friends(list);
            }
        }
    }

    if (liblinphone_serialize_logs == TRUE) {
        ortp_logv_flush();
    }
}

追踪从哪里获得的数据, 从get_codec函数开始

这个函数专门用来判断是否有这个编码的, 这里主要是看视频编码。

# linphonecore.c
static bool_t get_codec(LinphoneCore *lc, SalStreamType type, int index, PayloadType **ret){
    char codeckey[50];
    const char *mime,*fmtp;
    int rate,channels,enabled;
    PayloadType *pt;
    LpConfig *config=lc->config;

    *ret=NULL;
    snprintf(codeckey,50,"%s_codec_%i",type == SalAudio ? "audio" : type == SalVideo ? "video" : "text", index);
    mime=lp_config_get_string(config,codeckey,"mime",NULL);
    if (mime==NULL || strlen(mime)==0 ) return FALSE;

    rate=lp_config_get_int(config,codeckey,"rate",8000);
    fmtp=lp_config_get_string(config,codeckey,"recv_fmtp",NULL);
    channels=lp_config_get_int(config,codeckey,"channels",0);
    enabled=lp_config_get_int(config,codeckey,"enabled",1);
    if (!linphone_core_codec_supported(lc, type, mime)){
        ms_warning("Codec %s/%i read from conf is not supported by mediastreamer2, ignored.",mime,rate);
        return TRUE;
    }
    pt = find_payload(type == SalAudio ? lc->default_audio_codecs : type == SalVideo ? lc->default_video_codecs : lc->default_text_codecs ,mime,rate,channels,fmtp);
    if (!pt){
        bctbx_list_t **default_list = (type==SalAudio) ? &lc->default_audio_codecs : type == SalVideo ? &lc->default_video_codecs : &lc->default_text_codecs;
        if (type == SalAudio)
            ms_warning("Codec %s/%i/%i read from conf is not in the default list.",mime,rate,channels);
        else if (type == SalVideo)
            ms_warning("Codec %s/%i read from conf is not in the default list.",mime,rate);
        else
            ms_warning("Codec %s read from conf is not in the default list.",mime);
        pt=payload_type_new();
        pt->type=(type==SalAudio) ? PAYLOAD_AUDIO_PACKETIZED : type == SalVideo ? PAYLOAD_VIDEO : PAYLOAD_TEXT;
        pt->mime_type=ortp_strdup(mime);
        pt->clock_rate=rate;
        pt->channels=channels;
        payload_type_set_number(pt,-1); /*dynamic assignment*/
        payload_type_set_recv_fmtp(pt,fmtp);
        *default_list=bctbx_list_append(*default_list, pt);
    }
    if (enabled ) pt->flags|=PAYLOAD_TYPE_ENABLED;
    else pt->flags&=~PAYLOAD_TYPE_ENABLED;
    *ret=pt;
    return TRUE;
}

找一下PayloadType结构体, 看看能不能打印出信息。

#payloadtype.h
struct _PayloadType
{
    int type; /**< one of PAYLOAD_* macros*/
    int clock_rate; /**< rtp clock rate*/
    char bits_per_sample;   /* in case of continuous audio data */
    char *zero_pattern;
    int pattern_length;
    /* other useful information for the application*/
    int normal_bitrate; /*in bit/s */
    char *mime_type; /**<actually the submime, ex: pcm, pcma, gsm*/
    int channels; /**< number of channels of audio */
    char *recv_fmtp; /* various format parameters for the incoming stream */
    char *send_fmtp; /* various format parameters for the outgoing stream */
    struct _PayloadTypeAvpfParams avpf; /* AVPF parameters */
    int flags;
    void *user_data;
};

linphone_config_get_string是要获取mime的

# lpconfig.c
const char *linphone_config_get_string(const LpConfig *lpconfig, const char *section, const char *key, const char *default_string){
    LpSection *sec;
    LpItem *item;
    sec=linphone_config_find_section(lpconfig,section);
    if (sec!=NULL){
        item=lp_section_find_item(sec,key);
        if (item!=NULL) return item->value;
    }
    return default_string;
}

LpSection *linphone_config_find_section(const LpConfig *lpconfig, const char *name){
    LpSection *sec;
    bctbx_list_t *elem;
    /*printf("Looking for section %s\n",name);*/
    for (elem=lpconfig->sections;elem!=NULL;elem=bctbx_list_next(elem)){
        sec=(LpSection*)elem->data;
        if (strcmp(sec->name,name)==0){
            /*printf("Section %s found\n",name);*/
            return sec;
        }
    }
    return NULL;
}

Could not find encoder for H264

#msfactory.c
bool_t ms_factory_codec_supported(MSFactory* factory, const char *mime){
    MSFilterDesc *enc = ms_factory_get_encoding_capturer(factory, mime);
    MSFilterDesc *dec = ms_factory_get_decoding_renderer(factory, mime);

    if (enc == NULL) enc = ms_factory_get_encoder(factory, mime);
    if (dec == NULL) dec = ms_factory_get_decoder(factory, mime);

    if(enc!=NULL && dec!=NULL) return TRUE;

    if(enc==NULL) ms_message("[get_codec] Could not find encoder for %s", mime);
    if(dec==NULL) ms_message("[get_codec] Could not find decoder for %s", mime);
    return FALSE;
}
linphone/coreapi/misc.c:    if (ms_factory_codec_supported(lc->factory, pt->mime_type)){
linphone/coreapi/linphonecore.c:    return ms_factory_codec_supported(lc->factory, mime);
linphone/tester/dtmf_tester.c:      if(!ms_factory_codec_supported(marie->lc->factory, "opus") && !ms_factory_codec_supported(pauline->lc->factory, "opus")){
linphone/tester/call_single_tester.c:       if(!ms_factory_codec_supported(marie->lc->factory, "opus") && !ms_factory_codec_supported(pauline->lc->factory, "opus")){
linphone/mediastreamer2/src/base/msfactory.c:bool_t ms_factory_codec_supported(MSFactory* factory, const char *mime){
linphone/mediastreamer2/src/base/msfilter.c:    return ms_factory_codec_supported(ms_factory_get_fallback(),mime);

ms_factory_get_encoder

MSFilterDesc * ms_factory_get_encoder(MSFactory* factory, const char *mime){
    bctbx_list_t *elem;
    for (elem=factory->desc_list;elem!=NULL;elem=bctbx_list_next(elem)){
        MSFilterDesc *desc=(MSFilterDesc*)elem->data;
        if ((desc->flags & MS_FILTER_IS_ENABLED)
            && (desc->category==MS_FILTER_ENCODER || desc->category==MS_FILTER_ENCODING_CAPTURER)
            && strcasecmp(desc->enc_fmt,mime)==0){
            return desc;
        }
    }
    return NULL;
}

_MSFilterDesc

struct _MSFilterDesc{
    MSFilterId id;  /**< the id declared in allfilters.h */
    const char *name; /**< the filter name*/
    const char *text; /**< short text describing the filter's function*/
    MSFilterCategory category; /**< filter's category*/
    const char *enc_fmt; /**< sub-mime of the format, must be set if category is MS_FILTER_ENCODER or MS_FILTER_DECODER */
    int ninputs; /**< number of inputs */
    int noutputs; /**< number of outputs */
    MSFilterFunc init; /**< Filter's init function*/
    MSFilterFunc preprocess; /**< Filter's preprocess function, called one time before starting to process*/
    MSFilterFunc process; /**< Filter's process function, called every tick by the MSTicker to do the filter's job*/
    MSFilterFunc postprocess; /**< Filter's postprocess function, called once after processing (the filter is no longer called in process() after)*/
    MSFilterFunc uninit; /**< Filter's uninit function, used to deallocate internal structures*/
    MSFilterMethod *methods; /**<Filter's method table*/
    unsigned int flags; /**<Filter's special flags, from the MSFilterFlags enum.*/
};

过滤的一些参数

I/Linphone(25635): [ms_factory_get_encoding_capturer] mime = VP8
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSWebRtcIlbcDec , text=WebRtc's iLBC decoder, category=2
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSWebRtcIlbcEnc , text=WebRtc's iLBC encoder, category=1
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSWebRTCAEC , text=Echo canceller using WebRTC library., category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSiSACDec , text=iSAC audio decoder filter., category=2
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSiSACEnc , text=iSAC audio encoder filter., category=1
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSBCG729Dec , text=G729 audio decoder filter, category=2
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSBCG729Enc , text=G729 audio encoder filter, category=1
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSSILKDec , text=Silk decoder filter., category=2
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSSILKEnc , text=SILK audio encoder filter., category=1
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSAndroidDisplay , text=OpenGL-ES2 video display filter for Android., category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSAndroidVideoCapture , text=A filter that captures Android video., category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSMKVPlayer , text=MKV file player, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSMKVRecorder , text=MKV file recorder, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSVp8Dec , text=A VP8 video decoder using libvpx library., category=2
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSVp8Enc , text=A VP8 video encoder using libvpx library., category=1
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSJpegWriter , text=Take a video snapshot as jpg file, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSSizeConv , text=a small video size converter, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSPixConv , text=A pixel format converter, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSStaticImage , text=A filter that outputs a static image., category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSMire , text=A filter that outputs synthetic moving picture, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSExtDisplay , text=A display filter sending the buffers to draw to the upper layer, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSSpeexEC , text=Echo canceller using speex library, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSSpeexEnc , text=The free and wonderful speex codec, category=1
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSSpeexDec , text=The free and wonderful speex codec, category=2
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSResample , text=Audio resampler, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSOpusDec , text=An opus decoder., category=2
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSOpusEnc , text=An opus encoder., category=1
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSGsmEnc , text=The GSM full-rate codec, category=1
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSGsmDec , text=The GSM codec, category=2
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSUdpSend , text=UDP output filter, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSRTT4103Sink , text=A filter to receive real time text, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSRTT4103Source , text=A filter to send real time text, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSRtpRecv , text=RTP input filter, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSRtpSend , text=RTP output filter, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSUlawEnc , text=ITU-G.711 ulaw encoder, category=1
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSUlawDec , text=ITU-G.711 ulaw decoder, category=2
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSToneDetector , text=Custom tone detection filter., category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSVolume , text=A filter that controls and measure sound volume, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSVadDtx , text=A filter detecting silence period and encoding residual noise, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSG722Enc , text=The G.722 wideband codec, category=1
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSG722Dec , text=The G.722 wideband codec, category=2
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSFileRec , text=Wav file recorder, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSFilePlayer , text=Raw files and wav reader, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSL16Enc , text=L16 dummy encoder, category=1
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSL16Dec , text=L16 dummy decoder, category=2
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSGenericPLC , text=Generic PLC., category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSEqualizer , text=Parametric sound equalizer., category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSDtmfGen , text=DTMF generator, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSChannelAdapter , text=A filter that converts from mono to stereo and vice versa., category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSAudioMixer , text=A filter that mixes down 16 bit sample audio streams, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSAlawEnc , text=ITU-G.711 alaw encoder, category=1
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSAlawDec , text=ITU-G.711 alaw decoder, category=2
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSVoidSink , text=A filter that trashes its input (useful for terminating some graphs)., category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSVoidSource , text=A filter that generates silence on its output (useful for beginning some graphs)., category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSTee , text=A filter that reads from input and copy to its multiple outputs., category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSJoin , text=A filter that send several inputs to one output., category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSItcSink , text=Inter ticker communication filter., category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSItcSource , text=Inter ticker communication filter., category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] mime = H264
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSWebRtcIlbcDec , text=WebRtc's iLBC decoder, category=2
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSWebRtcIlbcEnc , text=WebRtc's iLBC encoder, category=1
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSWebRTCAEC , text=Echo canceller using WebRTC library., category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSiSACDec , text=iSAC audio decoder filter., category=2
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSiSACEnc , text=iSAC audio encoder filter., category=1
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSBCG729Dec , text=G729 audio decoder filter, category=2
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSBCG729Enc , text=G729 audio encoder filter, category=1
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSSILKDec , text=Silk decoder filter., category=2
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSSILKEnc , text=SILK audio encoder filter., category=1
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSAndroidDisplay , text=OpenGL-ES2 video display filter for Android., category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSAndroidVideoCapture , text=A filter that captures Android video., category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSMKVPlayer , text=MKV file player, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSMKVRecorder , text=MKV file recorder, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSVp8Dec , text=A VP8 video decoder using libvpx library., category=2
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSVp8Enc , text=A VP8 video encoder using libvpx library., category=1
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSJpegWriter , text=Take a video snapshot as jpg file, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSSizeConv , text=a small video size converter, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSPixConv , text=A pixel format converter, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSStaticImage , text=A filter that outputs a static image., category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSMire , text=A filter that outputs synthetic moving picture, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSExtDisplay , text=A display filter sending the buffers to draw to the upper layer, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSSpeexEC , text=Echo canceller using speex library, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSSpeexEnc , text=The free and wonderful speex codec, category=1
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSSpeexDec , text=The free and wonderful speex codec, category=2
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSResample , text=Audio resampler, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSOpusDec , text=An opus decoder., category=2
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSOpusEnc , text=An opus encoder., category=1
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSGsmEnc , text=The GSM full-rate codec, category=1
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSGsmDec , text=The GSM codec, category=2
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSUdpSend , text=UDP output filter, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSRTT4103Sink , text=A filter to receive real time text, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSRTT4103Source , text=A filter to send real time text, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSRtpRecv , text=RTP input filter, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSRtpSend , text=RTP output filter, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSUlawEnc , text=ITU-G.711 ulaw encoder, category=1
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSUlawDec , text=ITU-G.711 ulaw decoder, category=2
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSToneDetector , text=Custom tone detection filter., category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSVolume , text=A filter that controls and measure sound volume, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSVadDtx , text=A filter detecting silence period and encoding residual noise, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSG722Enc , text=The G.722 wideband codec, category=1
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSG722Dec , text=The G.722 wideband codec, category=2
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSFileRec , text=Wav file recorder, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSFilePlayer , text=Raw files and wav reader, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSL16Enc , text=L16 dummy encoder, category=1
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSL16Dec , text=L16 dummy decoder, category=2
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSGenericPLC , text=Generic PLC., category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSEqualizer , text=Parametric sound equalizer., category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSDtmfGen , text=DTMF generator, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSChannelAdapter , text=A filter that converts from mono to stereo and vice versa., category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSAudioMixer , text=A filter that mixes down 16 bit sample audio streams, category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSAlawEnc , text=ITU-G.711 alaw encoder, category=1
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSAlawDec , text=ITU-G.711 alaw decoder, category=2
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSVoidSink , text=A filter that trashes its input (useful for terminating some graphs)., category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSVoidSource , text=A filter that generates silence on its output (useful for beginning some graphs)., category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSTee , text=A filter that reads from input and copy to its multiple outputs., category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSJoin , text=A filter that send several inputs to one output., category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSItcSink , text=Inter ticker communication filter., category=0
I/Linphone(25635): [ms_factory_get_encoding_capturer] name=MSItcSource , text=Inter ticker communication filter., category=0

linphone_core_codec_supported

#linphonecore.c
static bool_t linphone_core_codec_supported(LinphoneCore *lc, SalStreamType type, const char *mime){
    if (type == SalVideo && lp_config_get_int(lc->config, "video", "rtp_io", FALSE)){
        return TRUE; /*in rtp io mode, we don't transcode video, thus we can support a format for which we have no encoder nor decoder.*/
    } else if (type == SalAudio && lp_config_get_int(lc->config, "sound", "rtp_io", FALSE)){
        return TRUE; /*in rtp io mode, we don't transcode video, thus we can support a format for which we have no encoder nor decoder.*/
    } else if (type == SalText) {
        return TRUE;
    }
    return ms_factory_codec_supported(lc->factory, mime);
}

linphone_core_codec_config_write, 这个就是每次开启linphone程序写入配置文件的地方

void _linphone_core_codec_config_write(LinphoneCore *lc){
    if (linphone_core_ready(lc)){
        PayloadType *pt;
        codecs_config_t *config=&lc->codecs_conf;
        bctbx_list_t *node;
        char key[50];
        int index;
        index=0;
        for(node=config->audio_codecs;node!=NULL;node=bctbx_list_next(node)){
            pt=(PayloadType*)(node->data);
            sprintf(key,"audio_codec_%i",index);
            lp_config_set_string(lc->config,key,"mime",pt->mime_type);
            lp_config_set_int(lc->config,key,"rate",pt->clock_rate);
            lp_config_set_int(lc->config,key,"channels",pt->channels);
            lp_config_set_int(lc->config,key,"enabled",linphone_core_payload_type_enabled(lc,pt));
            index++;
        }
        sprintf(key,"audio_codec_%i",index);
        lp_config_clean_section (lc->config,key);

        index=0;
        for(node=config->video_codecs;node!=NULL;node=bctbx_list_next(node)){
            pt=(PayloadType*)(node->data);
            sprintf(key,"video_codec_%i",index);
            lp_config_set_string(lc->config,key,"mime",pt->mime_type);
            lp_config_set_int(lc->config,key,"rate",pt->clock_rate);
            lp_config_set_int(lc->config,key,"enabled",linphone_core_payload_type_enabled(lc,pt));
            lp_config_set_string(lc->config,key,"recv_fmtp",pt->recv_fmtp);
            index++;
        }
        sprintf(key,"video_codec_%i",index);
        lp_config_clean_section (lc->config,key);
    }
}

PayloadType结构体

struct _PayloadType
{
    int type; /**< one of PAYLOAD_* macros*/
    int clock_rate; /**< rtp clock rate*/
    char bits_per_sample;   /* in case of continuous audio data */
    char *zero_pattern;
    int pattern_length;
    /* other useful information for the application*/
    int normal_bitrate; /*in bit/s */
    char *mime_type; /**<actually the submime, ex: pcm, pcma, gsm*/
    int channels; /**< number of channels of audio */
    char *recv_fmtp; /* various format parameters for the incoming stream */
    char *send_fmtp; /* various format parameters for the outgoing stream */
    struct _PayloadTypeAvpfParams avpf; /* AVPF parameters */
    int flags;
    void *user_data;
};
# private.h
typedef struct codecs_config
{
    MSList *audio_codecs;  /* list of audio codecs in order of preference*/
    MSList *video_codecs;
    MSList *text_codecs;
    int dyn_pt;
    int telephone_event_pt;
}codecs_config_t;

视频编码的问题,搜寻网址:

android linphone 在mediastreamer2新建filter进行h264硬件编码
Linphone探索:7 . 如何进行视频电话

哪里判断和哪里得到的编码信息

# msfactory.h
/*do not use these fields directly*/
struct _MSFactory{
    MSList *desc_list;
    MSList *stats_list;
    MSList *offer_answer_provider_list;
#ifdef _WIN32
    MSList *ms_plugins_loaded_list;
#endif
    MSList *formats;
    MSList *platform_tags;
    char *plugins_dir;
    struct _MSVideoPresetsManager *video_presets_manager;
    int cpu_count;
    struct _MSEventQueue *evq;
    int max_payload_size;
    int mtu;
    struct _MSSndCardManager* sndcardmanager;
    struct _MSWebCamManager* wbcmanager;
    void (*voip_uninit_func)(struct _MSFactory*);
    bool_t statistics_enabled;
    bool_t voip_initd;
    MSDevicesInfo *devices_info;
    char *image_resources_dir;
};
#msfilter.h
struct _MSFilterDesc{
    MSFilterId id;  /**< the id declared in allfilters.h */
    const char *name; /**< the filter name*/
    const char *text; /**< short text describing the filter's function*/
    MSFilterCategory category; /**< filter's category*/
    const char *enc_fmt; /**< sub-mime of the format, must be set if category is MS_FILTER_ENCODER or MS_FILTER_DECODER */
    int ninputs; /**< number of inputs */
    int noutputs; /**< number of outputs */
    MSFilterFunc init; /**< Filter's init function*/
    MSFilterFunc preprocess; /**< Filter's preprocess function, called one time before starting to process*/
    MSFilterFunc process; /**< Filter's process function, called every tick by the MSTicker to do the filter's job*/
    MSFilterFunc postprocess; /**< Filter's postprocess function, called once after processing (the filter is no longer called in process() after)*/
    MSFilterFunc uninit; /**< Filter's uninit function, used to deallocate internal structures*/
    MSFilterMethod *methods; /**<Filter's method table*/
    unsigned int flags; /**<Filter's special flags, from the MSFilterFlags enum.*/
};
/**
 * Filter's category
 *
 */
enum _MSFilterCategory{
    /**others*/
    MS_FILTER_OTHER,
    /**used by encoders*/
    MS_FILTER_ENCODER,
    /**used by decoders*/
    MS_FILTER_DECODER,
    /**used by capture filters that perform encoding*/
    MS_FILTER_ENCODING_CAPTURER,
    /**used by filters that perform decoding and rendering */
    MS_FILTER_DECODER_RENDERER
};

msfilter.c中使用判断编码是否可用

bool_t ms_filter_codec_supported(const char *mime){
    return ms_factory_codec_supported(ms_factory_get_fallback(),mime);
}

应该就是在这里判断是否有这个编码的

bool_t ms_factory_codec_supported(MSFactory* factory, const char *mime){
    ms_message("[get_codec] [msfactory] ms_factory_codec_supported(MSFactory* factory, const char *mime=%s", mime);
    MSFilterDesc *enc = ms_factory_get_encoding_capturer(factory, mime);
    MSFilterDesc *dec = ms_factory_get_decoding_renderer(factory, mime);

    if (enc == NULL) enc = ms_factory_get_encoder(factory, mime);
    if (dec == NULL) dec = ms_factory_get_decoder(factory, mime);

    if(enc!=NULL && dec!=NULL) return TRUE;

    if(enc==NULL) ms_message("Could not find encoder for %s", mime);
    if(dec==NULL) ms_message("Could not find decoder for %s", mime);
    return FALSE;
}

ms_factory_get_encoding_capturer

MSFilterDesc * ms_factory_get_encoding_capturer(MSFactory* factory, const char *mime) {
    bctbx_list_t *elem;

    for (elem = factory->desc_list; elem != NULL; elem = bctbx_list_next(elem)) {
        MSFilterDesc *desc = (MSFilterDesc *)elem->data;
        if (desc->category == MS_FILTER_ENCODING_CAPTURER) {
            char *saveptr=NULL;
            char *enc_fmt = ms_strdup(desc->enc_fmt);
            char *token = strtok_r(enc_fmt, " ", &saveptr);
            while (token != NULL) {
                if (strcasecmp(token, mime) == 0) {
                    break;
                }
                token = strtok_r(NULL, " ", &saveptr);
            }
            ms_free(enc_fmt);
            if (token != NULL) return desc;
        }
    }
    return NULL;
}

ms_factory_get_decoding_renderer

MSFilterDesc * ms_factory_get_decoding_renderer(MSFactory* factory, const char *mime) {
    bctbx_list_t *elem;

    for (elem = factory->desc_list; elem != NULL; elem = bctbx_list_next(elem)) {
        MSFilterDesc *desc = (MSFilterDesc *)elem->data;
        if (desc->category == MS_FILTER_DECODER_RENDERER) {
            char *saveptr=NULL;
            char *enc_fmt = ms_strdup(desc->enc_fmt);
            char *token = strtok_r(enc_fmt, " ", &saveptr);
            while (token != NULL) {
                if (strcasecmp(token, mime) == 0) {
                    break;
                }
                token = strtok_r(NULL, " ", &saveptr);
            }
            ms_free(enc_fmt);
            if (token != NULL) return desc;
        }
    }
    return NULL;
}

ms_factory_get_encoder

MSFilterDesc * ms_factory_get_encoder(MSFactory* factory, const char *mime){
    bctbx_list_t *elem;
    for (elem=factory->desc_list;elem!=NULL;elem=bctbx_list_next(elem)){
        MSFilterDesc *desc=(MSFilterDesc*)elem->data;
        if ((desc->flags & MS_FILTER_IS_ENABLED)
            && (desc->category==MS_FILTER_ENCODER || desc->category==MS_FILTER_ENCODING_CAPTURER)
            && strcasecmp(desc->enc_fmt,mime)==0){
            return desc;
        }
    }
    return NULL;
}

ms_factory_get_decoder

MSFilterDesc * ms_factory_get_decoder(MSFactory* factory, const char *mime){
    bctbx_list_t *elem;
    for (elem=factory->desc_list;elem!=NULL;elem=bctbx_list_next(elem)){
        MSFilterDesc *desc=(MSFilterDesc*)elem->data;
        if ((desc->flags & MS_FILTER_IS_ENABLED)
            && (desc->category==MS_FILTER_DECODER || desc->category==MS_FILTER_DECODER_RENDERER)
            && strcasecmp(desc->enc_fmt,mime)==0){
            return desc;
        }
    }
    return NULL;
}

突然发现所有的编码在MSFilterDesc中

strcasecmp(desc->enc_fmt,mime)==0)

bctbx_list_t *elem;
for (elem=factory->desc_list;elem!=NULL;elem=bctbx_list_next(elem)){
    MSFilterDesc *desc=(MSFilterDesc*)elem->data;
    ms_message("[ms_factory_get_encoder] MSFilterDesc name=%s, text=%s, enc_fmt=%s ",desc->name ,desc->text, desc->enc_fmt);
    if ((desc->flags & MS_FILTER_IS_ENABLED)
        && (desc->category==MS_FILTER_ENCODER || desc->category==MS_FILTER_ENCODING_CAPTURER)
        && strcasecmp(desc->enc_fmt,mime)==0){
        return desc;
    }
}

linphone_core_start 调用的方式

代码调用的过程

要点似乎实在linphone_core_init函数中

# linphonecore.c
static void linphone_core_init(LinphoneCore * lc, LinphoneCoreCbs *cbs, LpConfig *config, void * userdata){
    ms_message("[getLinphoneCoreMSFilterDescDebug] [linphone_core_init]");
    //getLinphoneCoreMSFilterDescDebug(lc);
    const char *remote_provisioning_uri = NULL;
    LinphoneFactory *lfactory = linphone_factory_get();
    LinphoneCoreCbs *internal_cbs = _linphone_core_cbs_new();
    char *msplugins_dir;
    char *image_resources_dir;

    ms_message("Initializing LinphoneCore %s", linphone_core_get_version());

    lc->config=lp_config_ref(config);
    lc->data=userdata;
    lc->ringstream_autorelease=TRUE;

    linphone_task_list_init(&lc->hooks);

    linphone_core_cbs_set_notify_received(internal_cbs, linphone_core_internal_notify_received);
    linphone_core_cbs_set_subscription_state_changed(internal_cbs, linphone_core_internal_subscription_state_changed);
    _linphone_core_add_callbacks(lc, internal_cbs, TRUE);
    belle_sip_object_unref(internal_cbs);


    if (cbs != NULL) {
        _linphone_core_add_callbacks(lc, cbs, FALSE);
    } else {
        LinphoneCoreCbs *fallback_cbs = linphone_factory_create_core_cbs(linphone_factory_get());
        _linphone_core_add_callbacks(lc, fallback_cbs, FALSE);
        belle_sip_object_unref(fallback_cbs);
    }


    linphone_core_set_state(lc,LinphoneGlobalStartup,"Starting up");
    ortp_init();
    linphone_core_activate_log_serialization_if_needed();

    msplugins_dir = linphone_factory_get_msplugins_dir(lfactory);
    lc->factory = ms_factory_new_with_voip_and_plugins_dir(msplugins_dir);
    if (msplugins_dir) bctbx_free(msplugins_dir);
    image_resources_dir = linphone_factory_get_image_resources_dir(lfactory);
    ms_factory_set_image_resources_dir(lc->factory, image_resources_dir);
    bctbx_free(image_resources_dir);
    linphone_core_register_default_codecs(lc);
    linphone_core_register_offer_answer_providers(lc);
    /* Get the mediastreamer2 event queue */
    /* This allows to run event's callback in linphone_core_iterate() */
    lc->msevq=ms_factory_create_event_queue(lc->factory);

    lc->sal=sal_init(lc->factory);
    sal_set_http_proxy_host(lc->sal, linphone_core_get_http_proxy_host(lc));
    sal_set_http_proxy_port(lc->sal, linphone_core_get_http_proxy_port(lc));

    sal_set_user_pointer(lc->sal,lc);
    sal_set_callbacks(lc->sal,&linphone_sal_callbacks);

#ifdef TUNNEL_ENABLED
    lc->tunnel=linphone_core_tunnel_new(lc);
#endif

    lc->network_last_check = 0;
    lc->network_last_status = FALSE;

    /* Create the http provider in dual stack mode (ipv4 and ipv6.
     * If this creates problem, we may need to implement parallel ipv6/ ipv4 http requests in belle-sip.
     */
    lc->http_provider = belle_sip_stack_create_http_provider(sal_get_stack_impl(lc->sal), "::0");
    lc->http_crypto_config = belle_tls_crypto_config_new();
    belle_http_provider_set_tls_crypto_config(lc->http_provider,lc->http_crypto_config);

    certificates_config_read(lc);

    lc->ringtoneplayer = linphone_ringtoneplayer_new();

#ifdef SQLITE_STORAGE_ENABLED
    sqlite3_bctbx_vfs_register(0);
#endif

    lc->vcard_context = linphone_vcard_context_new();
    linphone_core_initialize_supported_content_types(lc);

    remote_provisioning_uri = linphone_core_get_provisioning_uri(lc);
    if (remote_provisioning_uri == NULL) {
        linphone_configuring_terminated(lc, LinphoneConfiguringSkipped, NULL);
    } // else linphone_core_start will be called after the remote provisioning (see linphone_core_iterate)
    lc->bw_controller = ms_bandwidth_controller_new();
}

linphonecore.c 中的 linphone_core_iterate函数, 是不断调用的

没找原因。

android5.0之前后之后代码调用日志情况

android5.0之前

04-10 11:06:54.924 9070-9070/org.linphone I/Linphone: [getLinphoneCoreMSFilterDescDebug] [_linphone_core_new]
04-10 11:06:54.934 9070-9070/org.linphone I/Linphone: [getLinphoneCoreMSFilterDescDebug] [linphone_core_new_with_config]
04-10 11:06:54.934 9070-9070/org.linphone I/Linphone: [getLinphoneCoreMSFilterDescDebug] [_linphone_core_new_with_config]
04-10 11:06:54.934 9070-9070/org.linphone I/Linphone: [getLinphoneCoreMSFilterDescDebug] [linphone_core_init]
04-10 11:06:56.024 9070-9070/org.linphone I/Linphone: [getLinphoneCoreMSFilterDescDebug] [linphone_configuring_terminated] linphone_core_start(lc);
04-10 11:06:56.024 9070-9070/org.linphone I/Linphone: [getLinphoneCoreMSFilterDescDebug] [linphone_core_start]
04-10 11:06:56.024 9070-9070/org.linphone I/Linphone: [getLinphoneCoreMSFilterDescDebug] [codecs_config_read]
04-10 11:06:56.204 9070-9070/org.linphone I/Linphone: [getLinphoneCoreMSFilterDescDebug] [codecs_config_read]

android 5.0之后

[getLinphoneCoreMSFilterDescDebug] [_linphone_core_new]
[getLinphoneCoreMSFilterDescDebug] [linphone_core_new_with_config]
[getLinphoneCoreMSFilterDescDebug] [_linphone_core_new_with_config]
[getLinphoneCoreMSFilterDescDebug] [linphone_core_init]
[getLinphoneCoreMSFilterDescDebug] [linphone_configuring_terminated] linphone_core_start(lc);
[getLinphoneCoreMSFilterDescDebug] [linphone_core_start]
[getLinphoneCoreMSFilterDescDebug] [codecs_config_read]

分析linphone_core_new中的数据Lpconfig

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赵健zj

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值