float BleX264Encoder::getFrameDuration()
{
    MOption *option = MOption::instance();
    int fps = option->option("fps", "encoder").toInt();

    if (fps > 0) {
        return 1000.0000 / (float)fps;
    }

    // 40 ms : default duration
    return 40.0f;
}
int BleX264Encoder::init()
{
    MOption *option = MOption::instance();

    QString presetName  = option->option("preset", "x264").toString();
    QString tuneName    = option->option("tune", "x264").toString();
    QString profileName = option->option("profile", "x264").toString();
    int fps = option->option("fps", "encoder").toInt();
    int kbps = option->option("bitrate", "encoder").toInt();

    QSize wh = option->option("res", "encoder").toSize();
    int width = wh.width();
    int height = wh.height();

    int maxBitRate = kbps;
    int bufferSize = maxBitRate;
    bool bUseCBR = (option->option("BitrateMode", "x264").toString() == "CBR");
    int quality = option->option("quality", "x264").toInt();
    int KeyFrameInterval = option->option("KeyFrameInterval", "x264").toInt();
    int threadCount = option->option(Key_Thread_Count, Group_X264).toInt();
    bool enableBFrame = option->option(Key_Enable_B_Frame, Group_X264).toString() == "true" ? true : false;
    int B_frame_count = option->option(Key_B_Frame_Count, Group_X264).toInt();

    m_x264Param = new x264_param_t;

    if (tuneName == "Default" || tuneName.isEmpty()) {
        x264_param_default_preset(m_x264Param , presetName.toStdString().c_str(), NULL);
        log_trace("libx264 preset set to %s, tune set to NULL"
                  , presetName.toStdString().c_str());
    } else {
        x264_param_default_preset(m_x264Param , presetName.toStdString().c_str(), tuneName.toStdString().c_str());
        log_trace("libx264 preset set to %s, tune set to %s"\
                  , presetName.toStdString().c_str(), tuneName.toStdString().c_str());
    }

    if (profileName != "Default") {
        x264_param_apply_profile(m_x264Param, profileName.toStdString().c_str());
        log_trace("libx264 profile set to %s", profileName.toStdString().c_str());
    } else {
        log_trace("libx264 profile set to Default");
    }

    if(bUseCBR)
    {
        m_x264Param->rc.i_bitrate          = maxBitRate;
        m_x264Param->rc.i_vbv_max_bitrate  = maxBitRate; // vbv-maxrate
        m_x264Param->rc.i_vbv_buffer_size  = bufferSize; // vbv-bufsize
        m_x264Param->i_nal_hrd             = X264_NAL_HRD_CBR;
        m_x264Param->rc.i_rc_method        = X264_RC_ABR;
        m_x264Param->rc.f_rf_constant      = 0.0f;
    }
    else
    {
        m_x264Param->rc.i_vbv_max_bitrate  = maxBitRate;  // vbv-maxrate
        m_x264Param->rc.i_vbv_buffer_size  = bufferSize;  // vbv-bufsize
        m_x264Param->rc.i_rc_method        = X264_RC_CRF; // X264_RC_CRF;
        m_x264Param->rc.f_rf_constant      = 10.0f + float(20 - quality);

        log_trace("libx264 quality set to %d", quality);
    }

    m_x264Param->b_vfr_input           = 1;
    m_x264Param->i_keyint_max          = fps * KeyFrameInterval;
    m_x264Param->i_width               = width;
    m_x264Param->i_height              = height;
    m_x264Param->vui.b_fullrange       = 0;          //specify full range input levels

    // For some codecs, the time base is closer to the field rate than the frame rate.
    // Most notably, H.264 and MPEG-2 specify time_base as half of frame duration
    // if no telecine is used ...
    // Set to time_base ticks per frame. Default 1, e.g., H.264/MPEG-2 set it to 2.
    // @see ffmpeg: AVodecContex::ticks_per_frame
    // never use timebase = 1000, because vlc will show 1000 fps !!
    int ticks_per_frame = 2;
    m_x264Param->i_timebase_num = 1;
    m_x264Param->i_timebase_den = fps;

    m_x264Param->i_fps_num = m_x264Param->i_timebase_den;
    m_x264Param->i_fps_den = m_x264Param->i_timebase_num * ticks_per_frame;

    // disable start code 00 00 00 01 before NAL
    // instead of nalu size
    m_x264Param->b_repeat_headers = 0;
    m_x264Param->b_annexb = 0;

    m_x264Param->i_frame_reference = 5;
    if (enableBFrame) {
        m_x264Param->i_bframe = B_frame_count;
        m_x264Param->i_bframe_bias = 100;
        m_x264Param->i_bframe_adaptive = 1;
        if (B_frame_count >= 2)
            m_x264Param->i_bframe_pyramid = 1;
    }
    else
        m_x264Param->i_bframe = 0;

    if (threadCount > 0)
        m_x264Param->i_threads = threadCount;

    // @note
    // never use cpu capabilities.
    // let libx264 to choose.
#if 0
    m_x264Param->cpu = 0;
    m_x264Param->cpu |=X264_CPU_MMX;
    m_x264Param->cpu |=X264_CPU_MMXEXT;
    m_x264Param->cpu |=X264_CPU_SSE;
#endif

    m_x264Encoder = x264_encoder_open(m_x264Param);

    // update video sh
    x264_nal_t *nalOut;
    int nalNum;
    x264_encoder_headers(m_x264Encoder, &nalOut, &nalNum);

    for (int i = 0; i < nalNum; ++i) {
        x264_nal_t &nal = nalOut[i];
        if (nal.i_type == NAL_SPS) {
            BleVideoPacket *pkt = new BleVideoPacket(Video_Type_H264);
            pkt->dts = 0;

            MStream &body = pkt->data;

            // SPS Serialize
            body.write1Bytes(0x17);
            body.write1Bytes(0x00);
            body.write3Bytes(0x00);
            body.write1Bytes(0x01);
            body.writeString((char*)nal.p_payload + 5, 3);
            body.write1Bytes(0xff);
            body.write1Bytes(0xe1);
            body.write2Bytes(nal.i_payload - 4);
            body.writeString((char*)nal.p_payload + 4, nal.i_payload - 4);

            //the PPS always comes after the SPS
            x264_nal_t &pps = nalOut[++i];

            // PPS Serialize
            body.write1Bytes(0x01);
            body.write2Bytes(pps.i_payload - 4);
            body.writeString(MString((char*)pps.p_payload + 4, pps.i_payload - 4));

            appCtx->setVideoSh(pkt);
        } else if (nal.i_type == NAL_SEI) {
            BleVideoPacket *seiPkt = new BleVideoPacket(Video_Type_H264);
            seiPkt->dts = 0;
            seiPkt->has_encoded = true;

            MStream &seiBody = seiPkt->data;
            int skipBytes = 4;
            int newPayloadSize = (nal.i_payload - skipBytes);

            unsigned char flshFrameType = 0x17;
            seiBody.write1Bytes(flshFrameType);
            seiBody.write1Bytes(0x01);
            seiBody.write3Bytes(0x00);
            seiBody.write4Bytes(newPayloadSize);
            seiBody.writeString((char*)nal.p_payload + skipBytes, newPayloadSize);

            BleAVQueue::instance()->enqueue(seiPkt);
        }
    }

    m_pictureIn = new x264_picture_t;
    m_pictureIn->i_pts = 0;

    return BLE_SUCESS;
}
void BleSetting::restore()
{
    MOption *option = MOption::instance();

    // audio group
    int audioDevID = option->option("dev_id", "audio").toInt();
    QString audioFormat = option->option("format", "audio").toString();
    QString audioChannels = option->option("channels", "audio").toString();
    QString audioSampleRate = option->option("sample_rate", "audio").toString();
    QString audioBitrate = option->option("sample_rate", "audio").toString();

    // find dev ID
    int audioDevCount = ui->audioDevice->count();
    for (int i = 0; i < audioDevCount; ++i) {
        if (ui->audioDevice->itemData(i).toInt() == audioDevID) {
            ui->audioDevice->setCurrentIndex(i);
            break;
        }
    }

    setIndex(ui->audioFormat, audioFormat);
    setIndex(ui->audioChannels, audioChannels);
    setIndex(ui->audioSampleRate, audioSampleRate);
    setIndex(ui->audioBitrate, audioBitrate);

    // encoder group
    QString format      = option->option("format", "encoder").toString();
    QVariant res        = option->option("res", "encoder");
    QString fps         = option->option("fps", "encoder").toString();
    QString bitrate     = option->option("bitrate", "encoder").toString();

    setIndex(ui->format, format);

    ui->res->setCurrentIndex(ui->res->findData(res));

    setIndex(ui->fps, fps);
    setIndex(ui->bitrate, bitrate);

    // x264 group
    QString x264Preset  = option->option("preset", "x264").toString();
    QString x264Tune    = option->option("tune", "x264").toString();
    QString x264Profile = option->option("profile", "x264").toString();
    QString bitrateMode = option->option("BitrateMode", "x264").toString();
    QString keyFrameInterval = option->option("KeyFrameInterval", "x264").toString();
    QString threadCount = option->option(Key_Thread_Count, Group_X264).toString();
    QString enableBFrame = option->option(Key_Enable_B_Frame, Group_X264).toString();
    QString quality = option->option("quality", "x264").toString();

    setIndex(ui->x264Preset, x264Preset);
    setIndex(ui->x264Tune, x264Tune);
    setIndex(ui->x264Profile, x264Profile);
    setIndex(ui->bitrateMode, bitrateMode);
    setIndex(ui->keyFrameInterval, keyFrameInterval);
    setIndex(ui->threadCount, threadCount);
    ui->enableBFrame->setChecked((enableBFrame == "true") ? true: false);
    ui->qualityBar->setValue(quality.toInt());

    // network group
    QString address = option->option("address", "network").toString();

    ui->address->setText(address);
}
void BleSetting::onApplyClicked()
{
    MOption *option = MOption::instance();

    // audio group
    int audioDevID = ui->audioDevice->itemData(ui->audioDevice->currentIndex()).toInt();
    QString audioFormat = ui->audioFormat->currentText();
    QString audioChannels = ui->audioChannels->currentText();
    QString audioSampleRate = ui->audioSampleRate->currentText();
    QString audioBitrate = ui->audioBitrate->currentText();

    option->setOption(audioDevID, "dev_id", "audio");
    option->setOption(audioFormat, "format", "audio");
    option->setOption(audioChannels, "channels", "audio");
    option->setOption(audioSampleRate, "sample_rate", "audio");
    option->setOption(audioBitrate, "bitrate", "audio");

    QString format      = ui->format->currentText();
    QVariant res        = ui->res->itemData(ui->res->currentIndex());
    QString fps         = ui->fps->currentText();
    QString bitrate     = ui->bitrate->currentText();
    QString x264Preset  = ui->x264Preset->currentText();
    QString x264Tune    = ui->x264Tune->currentText();
    QString x264Profile = ui->x264Profile->currentText();
    QString address     = ui->address->text().trimmed();
    QString bitrateMode = ui->bitrateMode->currentText();
    QString keyFrameInterval = ui->keyFrameInterval->currentText();
    QString threadCount = ui->threadCount->currentText();
    QString enableBFrame = ui->enableBFrame->isChecked() ? "true" : "false";
    QString quality             = QString::number(ui->qualityBar->value());

    // save
    option->setOption(format, "format", "encoder");
    option->setOption(res, "res", "encoder");
    option->setOption(fps, "fps", "encoder");
    option->setOption(bitrate, "bitrate", "encoder");

    option->setOption(x264Preset, "preset", "x264");
    option->setOption(x264Tune, "tune", "x264");
    option->setOption(x264Profile, "profile", "x264");
    option->setOption(bitrateMode, "BitrateMode", "x264");
    option->setOption(keyFrameInterval, "KeyFrameInterval", "x264");
    option->setOption(threadCount, Key_Thread_Count, Group_X264);
    option->setOption(enableBFrame, Key_Enable_B_Frame, Group_X264);
    option->setOption(quality, "quality", "x264");

    option->setOption(address, "address", "network");

    emit settingChanged();
}