webrtc源碼分析之視頻編碼之三

webrtc源碼分析之視頻編碼之一webrtc源碼分析之視頻編碼之二分別分析了視頻編碼模塊的初始化流程和編碼流程曼验,接下來(lái)說(shuō)一下視頻編碼模塊關(guān)鍵的幾個(gè)點(diǎn)讯屈。

硬編碼與軟編碼

視頻編碼有硬編碼和軟編碼,在android平臺(tái)上邓馒,MediaCodec封裝了硬編碼和軟編碼杉辙,對(duì)于軟編碼挑秉,也可以直接使用其他主流開(kāi)源編碼庫(kù),比如H264編碼標(biāo)準(zhǔn)有l(wèi)ibx264勋磕、libopenh264妈候,vp8/vp9編碼標(biāo)準(zhǔn)有l(wèi)ibvpx。

webrtc同時(shí)支持硬編碼和軟編碼挂滓,在android平臺(tái)上苦银,硬編碼使用的是MediaCodec,是在java層封裝調(diào)用的赶站,軟編碼H264使用的是libopenh264幔虏,vp8/vp9使用的是libvpx,都是在native層封裝調(diào)用的贝椿。webrtc定義了如下編碼相關(guān)的主要類:

java層主要類如下所示:


native層主要類如下所示:


在java層和native層都定義了VideoEncoderFactory接口和VideoEncoder和接口想括,VideoEncoderFactory創(chuàng)建VideoEncoder,VideoEncoder實(shí)現(xiàn)視頻編碼功能烙博。從實(shí)現(xiàn)的角度來(lái)說(shuō)瑟蜈,可以分為以下幾類:

  • 基于android系統(tǒng)提供的MediaCodec實(shí)現(xiàn)的HardwareVideoEncoder和MediaCodecVideoEncoder,支持H264习勤、VP8踪栋、VP9編碼。
  • 基于libopenh264實(shí)現(xiàn)的H264Encoder图毕,支持H264編碼夷都。
  • 基于libvpx實(shí)現(xiàn)的VP8Encoder和VP9Encoder,分別支持VP8、VP9編碼囤官。

其他類只是包裝類冬阳,比如native層VideoEncoderWrapper可以用于包裝java層HardwareVideoEncoder,因?yàn)榫幋a操作都是從native層調(diào)用的党饮,借助VideoEncoderWrapper就可以在native層統(tǒng)一接口了肝陪。同理native層的VideoEncoderFactoryWrapper可以用于包裝java層VideoEncoderFactory對(duì)象。

以java層為例刑顺,VideoEncoderFactory定義如下:

/** Factory for creating VideoEncoders. */
public interface VideoEncoderFactory {
  /** Creates an encoder for the given video codec. */
  @CalledByNative VideoEncoder createEncoder(VideoCodecInfo info);

  /**
   * Enumerates the list of supported video codecs. This method will only be called once and the
   * result will be cached.
   */
  @CalledByNative VideoCodecInfo[] getSupportedCodecs();
}

createEncoder根據(jù)VideoCodecInfo創(chuàng)建對(duì)應(yīng)類型的編碼器氯窍,getSupportedCodecs獲取支持的編碼器類型,這里的類型指的是編碼標(biāo)準(zhǔn)蹲堂,比如H264狼讨、VP8、VP9等柒竞。獲取的信息用于生成sdp信息用于協(xié)商會(huì)話使用的編碼器類型政供。實(shí)際定義了HardwareVideoEncoderFactory和SoftwareVideoEncoderFactory兩種,DefaultVideoEncoderFactory只是對(duì)它們的封裝朽基。特定的VideoEncoderFactory創(chuàng)建特定的VideoEncoder布隔,比如HardwareVideoEncoderFactory創(chuàng)建的是HardwareVideoEncoder,SoftwareVideoEncoderFactory創(chuàng)建的是VP8Encoder或者VP9Encoder稼虎,由info參數(shù)決定衅檀。

VideoEncoder主要定義如下:

  /**
   * Initializes the encoding process. Call before any calls to encode.
   */
  @CalledByNative VideoCodecStatus initEncode(Settings settings, Callback encodeCallback);

  /**
   * Releases the encoder. No more calls to encode will be made after this call.
   */
  @CalledByNative VideoCodecStatus release();

  /**
   * Requests the encoder to encode a frame.
   */
  @CalledByNative VideoCodecStatus encode(VideoFrame frame, EncodeInfo info);

  /**
   * Informs the encoder of the packet loss and the round-trip time of the network.
   *
   * @param packetLoss How many packets are lost on average per 255 packets.
   * @param roundTripTimeMs Round-trip time of the network in milliseconds.
   */
  @CalledByNative VideoCodecStatus setChannelParameters(short packetLoss, long roundTripTimeMs);

  /** Sets the bitrate allocation and the target framerate for the encoder. */
  @CalledByNative VideoCodecStatus setRateAllocation(BitrateAllocation allocation, int framerate);

  /** Any encoder that wants to use WebRTC provided quality scaler must implement this method. */
  @CalledByNative ScalingSettings getScalingSettings();

  /**
   * Should return a descriptive name for the implementation. Gets called once and cached. May be
   * called from arbitrary thread.
   */
  @CalledByNative String getImplementationName();

native層的EncoderAdapter的internal_encoder_factory_成員是一個(gè)InternalEncoderFactory對(duì)象,external_encoder_factory_成員是一個(gè)CricketToWebRtcEncoderFactory對(duì)象渡蜻,CricketToWebRtcEncoderFactory的external_encoder_factory_成員是一個(gè)MediaCodecVideoEncoderFactory對(duì)象术吝,VideoEncoderFactoryWrapper的encoder_factory_成員是一個(gè)java層的VideoEncoderFactory對(duì)象,VideoEncoderWrapper的encoder_成員是一個(gè)java層的VideoEncoder對(duì)象茸苇。

上面定義了這么多種VideoEncoderFactory和VideoEncoder排苍,實(shí)際使用的是哪一種呢?實(shí)際使用哪一種跟調(diào)用webrtc api傳遞的參數(shù)学密、硬件平臺(tái)以及android系統(tǒng)版本相關(guān)淘衙。

參數(shù)主要是PeerConnectionFactory相關(guān)的,比如:

  public static void initializeFieldTrials(String fieldTrialsInitString) {
    nativeInitializeFieldTrials(fieldTrialsInitString);
  }

fieldTrialsInitString的值會(huì)影響VideoEncoderSoftwareFallbackWrapper的行為腻暮。
還有就是給PeerConnectionFactory構(gòu)造函數(shù)傳的encoderFactory的值彤守。

相關(guān)的代碼如下所示:

  public PeerConnectionFactory(
      Options options, VideoEncoderFactory encoderFactory, VideoDecoderFactory decoderFactory) {
    checkInitializeHasBeenCalled();
    nativeFactory = nativeCreatePeerConnectionFactory(options, encoderFactory, decoderFactory);
    if (nativeFactory == 0) {
      throw new RuntimeException("Failed to initialize PeerConnectionFactory!");
    }
  }
jlong CreatePeerConnectionFactoryForJava(
    JNIEnv* jni,
    const JavaParamRef<jobject>& joptions,
    const JavaParamRef<jobject>& jencoder_factory,
    const JavaParamRef<jobject>& jdecoder_factory,
    rtc::scoped_refptr<AudioProcessing> audio_processor) {

  cricket::WebRtcVideoEncoderFactory* legacy_video_encoder_factory = nullptr;
  cricket::WebRtcVideoDecoderFactory* legacy_video_decoder_factory = nullptr;
  std::unique_ptr<cricket::MediaEngineInterface> media_engine;
  if (jencoder_factory.is_null() && jdecoder_factory.is_null()) {
    // This uses the legacy API, which automatically uses the internal SW
    // codecs in WebRTC.
    if (video_hw_acceleration_enabled) {
      legacy_video_encoder_factory = CreateLegacyVideoEncoderFactory();
      legacy_video_decoder_factory = CreateLegacyVideoDecoderFactory();
    }
    media_engine.reset(CreateMediaEngine(
        adm, audio_encoder_factory, audio_decoder_factory,
        legacy_video_encoder_factory, legacy_video_decoder_factory, audio_mixer,
        audio_processor));
  } else {
    // This uses the new API, does not automatically include software codecs.
    std::unique_ptr<VideoEncoderFactory> video_encoder_factory = nullptr;
    if (jencoder_factory.is_null()) {
      legacy_video_encoder_factory = CreateLegacyVideoEncoderFactory();
      video_encoder_factory = std::unique_ptr<VideoEncoderFactory>(
          WrapLegacyVideoEncoderFactory(legacy_video_encoder_factory));
    } else {
      video_encoder_factory = std::unique_ptr<VideoEncoderFactory>(
          CreateVideoEncoderFactory(jni, jencoder_factory));
    }

    std::unique_ptr<VideoDecoderFactory> video_decoder_factory = nullptr;
    if (jdecoder_factory.is_null()) {
      legacy_video_decoder_factory = CreateLegacyVideoDecoderFactory();
      video_decoder_factory = std::unique_ptr<VideoDecoderFactory>(
          WrapLegacyVideoDecoderFactory(legacy_video_decoder_factory));
    } else {
      video_decoder_factory = std::unique_ptr<VideoDecoderFactory>(
          CreateVideoDecoderFactory(jni, jdecoder_factory));
    }

    rtc::scoped_refptr<AudioDeviceModule> adm_scoped = nullptr;
    media_engine.reset(CreateMediaEngine(
        adm_scoped, audio_encoder_factory, audio_decoder_factory,
        std::move(video_encoder_factory), std::move(video_decoder_factory),
        audio_mixer, audio_processor));
  }

  rtc::scoped_refptr<PeerConnectionFactoryInterface> factory(
      CreateModularPeerConnectionFactory(
          network_thread.get(), worker_thread.get(), signaling_thread.get(),
          std::move(media_engine), std::move(call_factory),
          std::move(rtc_event_log_factory)));
  RTC_CHECK(factory) << "Failed to create the peer connection factory; "
                     << "WebRTC/libjingle init likely failed on this device";
  // TODO(honghaiz): Maybe put the options as the argument of
  // CreatePeerConnectionFactory.
  if (has_options) {
    factory->SetOptions(options);
  }
  OwnedFactoryAndThreads* owned_factory = new OwnedFactoryAndThreads(
      std::move(network_thread), std::move(worker_thread),
      std::move(signaling_thread), legacy_video_encoder_factory,
      legacy_video_decoder_factory, network_monitor_factory, factory.release());
  owned_factory->InvokeJavaCallbacksOnFactoryThreads();
  return jlongFromPointer(owned_factory);
}

可見(jiàn)傳給PeerConnectionFactory構(gòu)造函數(shù)encoderFactory參數(shù)的值直接影響使用哪一個(gè)VideoEncoderFactory,也就直接影響使用哪一個(gè)VideoEncoder哭靖。demo中調(diào)用如下:

    if (peerConnectionParameters.videoCodecHwAcceleration) {
      encoderFactory = new DefaultVideoEncoderFactory(
          rootEglBase.getEglBaseContext(), true /* enableIntelVp8Encoder */, enableH264HighProfile);
      decoderFactory = new DefaultVideoDecoderFactory(rootEglBase.getEglBaseContext());
    } else {
      encoderFactory = new SoftwareVideoEncoderFactory();
      decoderFactory = new SoftwareVideoDecoderFactory();
    }

    factory = new PeerConnectionFactory(options, encoderFactory, decoderFactory);

可見(jiàn)使能hardware acceleration時(shí)具垫,使用的是DefaultVideoEncoderFactory,而DefaultVideoEncoderFactory createEncoder時(shí)會(huì)先后嘗試HardwareVideoEncoderFactory和SoftwareVideoEncoderFactory兩種试幽,如下所示:

  public VideoEncoder createEncoder(VideoCodecInfo info) {
    final VideoEncoder videoEncoder = hardwareVideoEncoderFactory.createEncoder(info);
    if (videoEncoder != null) {
      return videoEncoder;
    }
    return softwareVideoEncoderFactory.createEncoder(info);
  }

也就是無(wú)法使用硬編碼的情況下使用軟編碼筝蚕,而是否能使用硬編碼取決于硬件平臺(tái)以及android系統(tǒng)版本是否支持對(duì)應(yīng)的編碼標(biāo)準(zhǔn),如下所示:

public VideoEncoder createEncoder(VideoCodecInfo input) {
    VideoCodecType type = VideoCodecType.valueOf(input.name);
    MediaCodecInfo info = findCodecForType(type);

    if (info == null) {
      // No hardware support for this type.
      // TODO(andersc): This is for backwards compatibility. Remove when clients have migrated to
      // new DefaultVideoEncoderFactory.
      if (fallbackToSoftware) {
        SoftwareVideoEncoderFactory softwareVideoEncoderFactory = new SoftwareVideoEncoderFactory();
        return softwareVideoEncoderFactory.createEncoder(input);
      } else {
        return null;
      }
    }

    String codecName = info.getName();
    String mime = type.mimeType();
    Integer surfaceColorFormat = MediaCodecUtils.selectColorFormat(
        MediaCodecUtils.TEXTURE_COLOR_FORMATS, info.getCapabilitiesForType(mime));
    Integer yuvColorFormat = MediaCodecUtils.selectColorFormat(
        MediaCodecUtils.ENCODER_COLOR_FORMATS, info.getCapabilitiesForType(mime));

    if (type == VideoCodecType.H264) {
      boolean isHighProfile = nativeIsSameH264Profile(input.params, getCodecProperties(type, true))
          && isH264HighProfileSupported(info);
      boolean isBaselineProfile =
          nativeIsSameH264Profile(input.params, getCodecProperties(type, false));

      if (!isHighProfile && !isBaselineProfile) {
        return null;
      }
    }

    return new HardwareVideoEncoder(codecName, type, surfaceColorFormat, yuvColorFormat,
        input.params, getKeyFrameIntervalSec(type), getForcedKeyFrameIntervalMs(type, codecName),
        createBitrateAdjuster(type, codecName), sharedContext);
  }

VideoCodecInfo的name包含要使用的編碼類型信息,比如H264起宽、VP8洲胖、VP9,可以對(duì)應(yīng)一個(gè)VideoCodecType坯沪,如下所示:

enum VideoCodecType {
  VP8("video/x-vnd.on2.vp8"),
  VP9("video/x-vnd.on2.vp9"),
  H264("video/avc");

  private final String mimeType;

  private VideoCodecType(String mimeType) {
    this.mimeType = mimeType;
  }

  String mimeType() {
    return mimeType;
  }
}

調(diào)用findCodecForType判斷使用的硬件平臺(tái)以及android系統(tǒng)版本是否支持該編碼類型绿映,支持的話就使用HardwareVideoEncoder,否則就會(huì)調(diào)用SoftwareVideoEncoderFactory創(chuàng)建對(duì)應(yīng)的軟解碼器腐晾,findCodecForType定義如下所示:

  private MediaCodecInfo findCodecForType(VideoCodecType type) {
    for (int i = 0; i < MediaCodecList.getCodecCount(); ++i) {
      MediaCodecInfo info = null;
      try {
        info = MediaCodecList.getCodecInfoAt(i);
      } catch (IllegalArgumentException e) {
        Logging.e(TAG, "Cannot retrieve encoder codec info", e);
      }

      if (info == null || !info.isEncoder()) {
        continue;
      }

      if (isSupportedCodec(info, type)) {
        return info;
      }
    }
    return null; // No support for this type.
  }

isSupportedCodec定義如下所示:

  private boolean isSupportedCodec(MediaCodecInfo info, VideoCodecType type) {
    if (!MediaCodecUtils.codecSupportsType(info, type)) {
      return false;
    }
    // Check for a supported color format.
    if (MediaCodecUtils.selectColorFormat(
            MediaCodecUtils.ENCODER_COLOR_FORMATS, info.getCapabilitiesForType(type.mimeType()))
        == null) {
      return false;
    }
    return isHardwareSupportedInCurrentSdk(info, type);
  }

先調(diào)用codecSupportsType比對(duì)mimeType叉弦,如下所示:

  static boolean codecSupportsType(MediaCodecInfo info, VideoCodecType type) {
    for (String mimeType : info.getSupportedTypes()) {
      if (type.mimeType().equals(mimeType)) {
        return true;
      }
    }
    return false;
  }

接著調(diào)用isHardwareSupportedInCurrentSdk比對(duì)硬件平臺(tái)以及android系統(tǒng)版本,如下所示:

  private boolean isHardwareSupportedInCurrentSdk(MediaCodecInfo info, VideoCodecType type) {
    switch (type) {
      case VP8:
        return isHardwareSupportedInCurrentSdkVp8(info);
      case VP9:
        return isHardwareSupportedInCurrentSdkVp9(info);
      case H264:
        return isHardwareSupportedInCurrentSdkH264(info);
    }
    return false;
  }

如果是H264藻糖,調(diào)用的是isHardwareSupportedInCurrentSdkH264卸奉,如下所示:

  private boolean isHardwareSupportedInCurrentSdkH264(MediaCodecInfo info) {
    // First, H264 hardware might perform poorly on this model.
    if (H264_HW_EXCEPTION_MODELS.contains(Build.MODEL)) {
      return false;
    }
    String name = info.getName();
    // QCOM H264 encoder is supported in KITKAT or later.
    return (name.startsWith(QCOM_PREFIX) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
        // Exynos H264 encoder is supported in LOLLIPOP or later.
        || (name.startsWith(EXYNOS_PREFIX)
               && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP);
  }

可見(jiàn),在這里完成了硬件平臺(tái)以及android系統(tǒng)版本的判斷颖御。

總的來(lái)說(shuō),實(shí)際使用哪一種編碼器跟調(diào)用webrtc api傳遞的參數(shù)凝颇、硬件平臺(tái)以及android系統(tǒng)版本相關(guān)潘拱。

碼率控制

Todo

Todo

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市拧略,隨后出現(xiàn)的幾起案子芦岂,更是在濱河造成了極大的恐慌,老刑警劉巖垫蛆,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件禽最,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡袱饭,警方通過(guò)查閱死者的電腦和手機(jī)川无,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)虑乖,“玉大人懦趋,你說(shuō)我怎么就攤上這事≌钗叮” “怎么了仅叫?”我有些...
    開(kāi)封第一講書人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)糙捺。 經(jīng)常有香客問(wèn)我诫咱,道長(zhǎng),這世上最難降的妖魔是什么洪灯? 我笑而不...
    開(kāi)封第一講書人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任坎缭,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘幻锁。我一直安慰自己凯亮,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布哄尔。 她就那樣靜靜地躺著假消,像睡著了一般。 火紅的嫁衣襯著肌膚如雪岭接。 梳的紋絲不亂的頭發(fā)上富拗,一...
    開(kāi)封第一講書人閱讀 49,730評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音鸣戴,去河邊找鬼啃沪。 笑死,一個(gè)胖子當(dāng)著我的面吹牛窄锅,可吹牛的內(nèi)容都是我干的创千。 我是一名探鬼主播,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼入偷,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼追驴!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起疏之,我...
    開(kāi)封第一講書人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤殿雪,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后锋爪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體丙曙,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年其骄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了亏镰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡年栓,死狀恐怖拆挥,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情某抓,我是刑警寧澤纸兔,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站否副,受9級(jí)特大地震影響汉矿,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜备禀,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一洲拇、第九天 我趴在偏房一處隱蔽的房頂上張望奈揍。 院中可真熱鬧,春花似錦赋续、人聲如沸男翰。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蛾绎。三九已至,卻和暖如春鸦列,著一層夾襖步出監(jiān)牢的瞬間租冠,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工薯嗤, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留顽爹,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓骆姐,卻偏偏與公主長(zhǎng)得像镜粤,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子玻褪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 一繁仁、引言 眾所周知,Chrome/WebRTC中的視頻編解碼器一直使用Google自己開(kāi)發(fā)的VP8/VP9归园,而對(duì)于...
    weizhenwei閱讀 30,046評(píng)論 9 26
  • webrtc視頻處理流水線的第一個(gè)環(huán)節(jié)就是獲取視頻數(shù)據(jù),視頻源可以有多種來(lái)源稚矿,以攝像頭采集為例庸诱,每個(gè)平臺(tái)往往又提供...
    Jimmy2012閱讀 18,102評(píng)論 2 22
  • 朋友圈
    多米諾方塊閱讀 174評(píng)論 0 0
  • 昨天,從南京回來(lái)的華在高中群里說(shuō)晤揣,周日誰(shuí)有空桥爽,一起聚聚。我要體檢昧识,輝要加班钠四,健已經(jīng)有約」蚶悖看樣子缀去,是聚不起來(lái)的節(jié)奏。...
    書蟲小言閱讀 218評(píng)論 0 0
  • 當(dāng)初找家里要了五萬(wàn)塊甸祭,承諾大學(xué)期間不再問(wèn)家里要生活費(fèi)學(xué)費(fèi)缕碎。被現(xiàn)實(shí)狠狠打臉,創(chuàng)業(yè)失敗池户。馬上要放假咏雌,而自己八月中旬以前...
    oO0啦啦閱讀 340評(píng)論 0 0