// автостарт записи void autoStartRecord(){ string mediaStreams = xmlGetString(config, "record"); vector<string> arrayMediaStreams = xmlGetArrayTags(mediaStreams, "mediaStream"); static string mediaStreamId; for(vector<string>::const_iterator it = arrayMediaStreams.begin(); it !=arrayMediaStreams.end(); ++it){ if (xmlGetBool(*it, "autoStartRecord")){ mediaStreamId = xmlGetString(*it, "mediaStreamId"); startRecord(mediaStreamId); } } }
// отправка данных для отображения медиапотоков записи void sendMediaStreamsDataToRecord(){ string mediaStreams = xmlGetString(config, "record"); string outDataXML = xmlStringToTag("id", "responseMediastreamsDataToRecord"); outDataXML += xmlGetTags(mediaStreams, "mediaStreamName"); outDataXML += xmlGetTags(mediaStreams, "mediaStreamId"); vector<string> arrayRecordStatus = xmlGetArrayTags(mediaStreams, "mediaStream"); string mediaStreamsRecordStatus; for(vector<string>::const_iterator it = arrayRecordStatus.begin(); it !=arrayRecordStatus.end(); ++it){ string recordStatus = recordStatusMap[xmlGetString(*it, "mediaStreamId")]; if (recordStatus == "") mediaStreamsRecordStatus += xmlStringToTag("mediaStreamRecordStatus", "stop"); else mediaStreamsRecordStatus += xmlStringToTag("mediaStreamRecordStatus", recordStatus); } outDataXML += mediaStreamsRecordStatus; sendClient(outDataXML); // отправляяем клиенту }
////////////////////// // Apply the given tag void CMinimap::ApplyTag(xmlNodePtr node) { CWidget::ApplyTag(node); if (xmlPropExists(node, "map")) cMap->Load(xmlGetString(node, "map")); }
/////////////////// // Apply the given node void CImageButton::ApplyTag(xmlNodePtr node) { CWidget::ApplyTag(node); sPath = xmlGetString(node, "src", sPath); if (sPath.size()) bmpImage = LoadGameImage(sPath, true); }
/////////////////////// // Apply the given tag void CListview::ApplyTag(xmlNodePtr node) { CContainerWidget::ApplyTag(node); // Listview properties if (xmlPropExists(node, "showselect")) bShowSelect.set(xmlGetBool(node, "showselect", bShowSelect), TAG_ATTR_PRIORITY); if (xmlPropExists(node, "scrollalways")) bAlwaysVisibleScrollbar.set(xmlGetBool(node, "scrollalways"), TAG_ATTR_PRIORITY); if (xmlPropExists(node, "showcolumns")) bShowColumnHeaders.set(xmlGetBool(node, "showcolumns"), TAG_ATTR_PRIORITY); // Columns and items xmlNodePtr child = node->children; while (child) { // Column if (!xmlStrcasecmp(child->name, (const xmlChar *)"column")) { std::string text = xmlGetString(child, "caption"); int width = xmlGetInt(child, "width", -1); if (width == -1) AddColumn(text); else AddColumn(text, width); } // Item else if (!xmlStrcasecmp(child->name, (const xmlChar *)"item")) { CListviewItem item(this); item.ApplyTag(child); AddItem(item); } child = child->next; } }
/////////////////// // Apply the given tag void CScrollbar::ApplyTag(xmlNodePtr node) { CWidget::ApplyTag(node); iMin = MAX(0, xmlGetInt(node, "min")); iMax = MAX(iMin + 1, xmlGetInt(node, "max", iMin + 1)); iValue = CLAMP(xmlGetInt(node, "value"), iMin, iMax); iItemsperbox = CLAMP(xmlGetInt(node, "itemsperbox"), 0, iMax); std::string base = xmlGetBaseURL(node); if (xmlPropExists(node, "topbtn")) bmpTop.set(LoadGameImage(JoinPaths(base, xmlGetString(node, "topbtn"))), TAG_ATTR_PRIORITY); if (xmlPropExists(node, "bottombtn")) bmpBottom.set(LoadGameImage(JoinPaths(base, xmlGetString(node, "bottombtn"))), TAG_ATTR_PRIORITY); if (xmlPropExists(node, "slidertop")) bmpSliderTop.set(LoadGameImage(JoinPaths(base, xmlGetString(node, "slidertop"))), TAG_ATTR_PRIORITY); if (xmlPropExists(node, "sliderbottom")) bmpSliderBottom.set(LoadGameImage(JoinPaths(base, xmlGetString(node, "sliderbottom"))), TAG_ATTR_PRIORITY); if (xmlPropExists(node, "slidermain")) bmpSliderMiddle.set(LoadGameImage(JoinPaths(base, xmlGetString(node, "slidermain"))), TAG_ATTR_PRIORITY); if (xmlPropExists(node, "color")) iColor.set(xmlGetColour(node, "color", iColor), TAG_ATTR_PRIORITY); if (xmlPropExists(node, "highcolor")) iHighlightColor.set(xmlGetColour(node, "highcolor", iHighlightColor), TAG_ATTR_PRIORITY); if (xmlPropExists(node, "shadowcolor")) iShadowColor.set(xmlGetColour(node, "shadowcolor", iShadowColor), TAG_ATTR_PRIORITY); if (xmlPropExists(node, "bgcolor")) iBackgroundColor.set(xmlGetColour(node, "bgcolor", iBackgroundColor), TAG_ATTR_PRIORITY); if (xmlPropExists(node, "arrowcolor")) iArrowColor.set(xmlGetColour(node, "arrowcolor", iArrowColor), TAG_ATTR_PRIORITY); if (xmlPropExists(node, "dir")) { std::string dir = xmlGetString(node, "dir", "vertical"); if (stringcaseequal(dir, "vertical")) iDirection.set(scrVertical, TAG_ATTR_PRIORITY); else if (stringcaseequal(dir, "horizontal")) iDirection.set(scrHorizontal, TAG_ATTR_PRIORITY); } }
void *handle_startRecord(void *aMediaStreamId) { cout << "record: Старт потока для записи медиаданных в файл\n"; string mediaStreamId = (const char*)aMediaStreamId; setRecordStatus(mediaStreamId, "started"); // отправляем клиентам статус записи string mountName = xmlGetString(config, "mountName"); string rootRecordDir = xmlGetString(config, "mountDir") + "/" + mountName + "/" + xmlGetString(config, "rootRecordName") + "/"; int numberFilesToDeleted = xmlGetInt(config, "numberFilesToDeleted"); int minFreeSizeFlashKB = xmlGetInt(config, "minFreeSizeFlashMB") * 1024; // лимит минимального свободного места на флешке string mediaStreams = xmlGetString(config, "record"); string mediaStream = xmlGetTagById(mediaStreams, "mediaStream", mediaStreamId); string mediaStreamNameDir = rootRecordDir + xmlGetString(mediaStream, "mediaStreamName") + "/"; time_t startTime, currentTime; string gstLaunchPath = xmlGetString(config, "gstLaunchPath"); string gstLaunchName = xmlGetString(config, "gstLaunchName"); cout << "mediaStream = " << mediaStream << endl; string videoDevice, resolution, resolutionW, resolutionH, fps, codec, bitrate, audioDevice, channel; bool videoEnable = xmlGetBool(mediaStream, "videoEnable"); if (videoEnable){ videoDevice = getVideoDeviceName(xmlGetString(mediaStream, "videoDevice")); resolution = xmlGetString(mediaStream, "resolution"); resolutionW = resolution.substr(0, resolution.find("x")); resolutionH = resolution.substr(resolution.find("x")+1, resolution.length()); fps = xmlGetString(mediaStream, "fps"); codec = xmlGetString(mediaStream, "codec"); bitrate = xmlGetString(mediaStream, "bitrate"); // настройка камеры string systemLine = "sudo v4l2-ctl -d " + videoDevice + " --set-ctrl focus_auto=0"; system(systemLine.c_str()); systemLine = "sudo v4l2-ctl -d " + videoDevice + " --set-ctrl focus_absolute=0"; system(systemLine.c_str()); systemLine = "sudo v4l2-ctl -d " + videoDevice + " --set-ctrl sharpness=255"; system(systemLine.c_str()); systemLine = "sudo v4l2-ctl -d " + videoDevice + " --set-ctrl contrast=170"; system(systemLine.c_str()); } bool audioEnable = xmlGetBool(mediaStream, "audioEnable"); if (audioEnable){ audioDevice = getAudioDeviceName(xmlGetString(mediaStream, "audioDevice")); channel = xmlGetString(mediaStream, "channel"); } int recordingTimeSec = xmlGetInt(mediaStream, "recordingTime") * 60 ; // выполняем запись в цикле while(true){ // проверяем статус записи... if(recordStatusMap[mediaStreamId] == "stop"){ cout << "Процесс записи медиаданных завершен пользователем, выход из потока записи\n"; setRecordStatus(mediaStreamId, "stop"); return 0; } cout << "Создание процесса для записи медиаданных в файл\n"; cout << "mediaStreamId = " << mediaStreamId << endl; cout << "Проверка свободного места на флешке...\n"; if (!checkFreeSize(minFreeSizeFlashKB, mountName, rootRecordDir, numberFilesToDeleted)){ setRecordStatus(mediaStreamId, "starting"); // отправляем клиентам статус записи sleep(10); continue; } cout << "Создание корневого каталога...\n"; if (!createDir(rootRecordDir)){ setRecordStatus(mediaStreamId, "starting"); // отправляем клиентам статус записи sleep(10); continue; } cout << "Создание каталога с именем медиапотока...\n"; if (!createDir(mediaStreamNameDir)){ setRecordStatus(mediaStreamId, "starting"); // отправляем клиентам статус записи sleep(10); continue; } cout << "Создание каталога с текущей датой...\n"; string currentDateDir = mediaStreamNameDir + getDate("%e.%m.%Y") + "/"; if (!createDir(currentDateDir)){ setRecordStatus(mediaStreamId, "starting"); // отправляем клиентам статус записи sleep(10); continue; } time(&startTime); // получаем время старта процесса string recordingFile = currentDateDir + getTime("%H-%M-%S") + ".ts"; string location = "location=" + recordingFile; // получаем имя для файла cout << "Записываемый файл: " << recordingFile << endl; int recordPid = fork(); // создаем дочерний процесс if (recordPid == 0){ // если находимся в дочернем процессе... // только видео if(videoEnable && !audioEnable){ if(codec == "h264"){ cout << "Выбрана запись видео без звука с аппаратным кодером устройства h264\n"; string videoDeviceNameParam = "device=" + videoDevice; string videoFormatParam = "video/x-raw,format=I420,width=" +resolutionW +",height=" +resolutionH +",framerate=" +fps +"/1"; string targetBitrate = "target-bitrate=" +bitrate; execl(gstLaunchPath.c_str(), gstLaunchName.c_str(), "v4l2src", videoDeviceNameParam.c_str(), "!", "videorate", "!", videoFormatParam.c_str(), "!", "omxh264enc", targetBitrate.c_str(), "control-rate=variable", "!", "video/x-h264,profile=high", "!", "queue", "max-size-bytes=10000000", "!", "h264parse", "!", "avimux", "!", "filesink", location.c_str(), (const char*)0); // запускаем код в новом процессе... } // сжатие с помощью аппаратного кодера камеры h264 if(codec == "h264 camera support"){ cout << "Выбрана запись видео без звука с аппаратным кодером камеры h264\n"; string videoDeviceNameParam = "device=" + videoDevice; string videoFormatParam = "video/x-h264,width=" +resolutionW +",height=" +resolutionH +",framerate=" +fps +"/1,profile=constrained-baseline"; string initialBitrate = "initial-bitrate=" + bitrate; execl(gstLaunchPath.c_str(), gstLaunchName.c_str(), "uvch264src", "rate-control=cbr", initialBitrate.c_str(), "iframe-period=2000", videoDeviceNameParam.c_str(), "name=src", "auto-start=true", "src.vfsrc", "!", "queue", "!", "video/x-raw,width=320,height=240,framerate=30/1", "!", "fakesink", "src.vidsrc", "!", "queue", "!", videoFormatParam.c_str(), "!", "queue", "!", "h264parse", "!", "mpegtsmux", "!", "filesink", location.c_str(), (const char*)0); // запускаем код в новом процессе... } // сжатие с помощью аппаратного кодера камеры mjpeg if(codec == "mjpeg camera support"){ // gst-launch-1.0 v4l2src device=/dev/video0 ! videorate ! image/jpeg,framerate=10/1,width=640,height=480 ! queue max-size-bytes=10000000 ! avimux ! filesink location=/mnt/flash/mjpg.ts cout << "Выбрана запись видео без звука с кодером камеры mjpeg\n"; string videoDeviceNameParam = "device=" + videoDevice; string videoFormatParam = "image/jpeg,width=" +resolutionW +",height=" +resolutionH +",framerate=" +fps +"/1"; execl(gstLaunchPath.c_str(), gstLaunchName.c_str(), "v4l2src", videoDeviceNameParam.c_str(), "!", "videorate", "!", videoFormatParam.c_str(), "!", "queue", "max-size-bytes=10000000", "!", "avimux", "!", "filesink", location.c_str(), (const char*)0); // запускаем код в новом процессе... } } // видео и аудио if(videoEnable && audioEnable){ if(codec == "h264"){ cout << "Выбрана запись видео со звуком с аппаратным кодером устройства h264\n"; string videoDeviceNameParam = "device=" + videoDevice; string audioDeviceParam = "device=" + audioDevice; string videoFormatParam = "video/x-raw,format=I420,width=" +resolutionW +",height=" +resolutionH +",framerate=" +fps +"/1"; string targetBitrate = "target-bitrate=" +bitrate; string audioFormatParam = "audio/x-raw,format=S16LE,rate=48000,channels=" + channel; execl(gstLaunchPath.c_str(), gstLaunchName.c_str(), "v4l2src", videoDeviceNameParam.c_str(), "!", "videorate", "!", videoFormatParam.c_str(), "!", "omxh264enc", targetBitrate.c_str(), "control-rate=variable", "!", "video/x-h264,profile=high", "!", "h264parse", "!", "queue", "max-size-bytes=10000000", "!", "avimux", "name=mux", "alsasrc", audioDeviceParam.c_str(), "!", "volume", "volume=10.0", "!", "audioresample", "!", audioFormatParam.c_str(), "!", "queue", "!", "voaacenc", "!", "aacparse", "!", "queue", "!", "mux.", "mux.", "!", "filesink", location.c_str(), (const char*)0); // запускаем код в новом процессе... } // сжатие с помощью аппаратного кодера камеры h264 if(codec == "h264 camera support"){ cout << "Выбрана запись видео со звуком с аппаратным кодером камеры h264\n"; string videoDeviceNameParam = "device=" + videoDevice; string audioDeviceParam = "device=" + audioDevice; string videoFormatParam = "video/x-h264,width=" +resolutionW +",height=" +resolutionH +",framerate=" +fps +"/1,profile=constrained-baseline"; string audioFormatParam = "audio/x-raw,format=S16LE,rate=48000,channels=" + channel; string initialBitrate = "average-bitrate=" + bitrate; execl(gstLaunchPath.c_str(), gstLaunchName.c_str(), "uvch264src", "rate-control=cbr", initialBitrate.c_str(), "iframe-period=2000", videoDeviceNameParam.c_str(), "name=src", "auto-start=true", "src.vidsrc", "!", "queue", "!", videoFormatParam.c_str(), "!", "queue", "!", "h264parse", "!", "avimux", "name=mux", "alsasrc", audioDeviceParam.c_str(), "!", "volume", "volume=10.0", "!", "audioresample", "!", audioFormatParam.c_str(), "!", "queue", "!", "voaacenc", "!", "aacparse", "!", "queue", "!", "mux.", "mux.", "!", "filesink", location.c_str(), (const char*)0); // запускаем код в новом процессе... } // сжатие с помощью аппаратного кодера камеры mjpeg if(codec == "mjpeg camera support"){ // gst-launch-1.0 v4l2src device=/dev/video0 ! videorate ! image/jpeg,framerate=10/1,width=640,height=480 ! // queue max-size-bytes=10000000 ! avimux name=mux alsasrc device=hw:1,0 ! volume volume=10.0 ! audioresample ! // audio/x-raw,rate=48000,channels=2 ! queue ! voaacenc bitrate=16000 ! aacparse ! queue ! mux. mux. ! filesink location=/mnt/flash/mjpg_and_audio.ts cout << "Выбрана запись видео со звуком с кодером камеры mjpeg\n"; string videoDeviceNameParam = "device=" + videoDevice; string videoFormatParam = "image/jpeg,width=" +resolutionW +",height=" +resolutionH +",framerate=" +fps +"/1"; string audioDeviceParam = "device=" + audioDevice; string audioFormatParam = "audio/x-raw,format=S16LE,rate=48000,channels=" + channel; execl(gstLaunchPath.c_str(), gstLaunchName.c_str(), "v4l2src", videoDeviceNameParam.c_str(), "!", "videorate", "!", videoFormatParam.c_str(), "!", "queue", "max-size-bytes=10000000", "!", "avimux", "name=mux", "alsasrc", audioDeviceParam.c_str(), "!", "volume", "volume=10.0", "!", "audioresample", "!", audioFormatParam.c_str(), "!", "queue", "!", "voaacenc", "!", "aacparse", "!", "queue", "!", "mux.", "mux.", "!", "filesink", location.c_str(), (const char*)0); // запускаем код в новом процессе... } } // только аудио if(!videoEnable && audioEnable){ cout << "Выбрана запись звука\n"; string audioDeviceParam = "device=" + audioDevice; string audioFormatParam = "audio/x-raw,format=S16LE,rate=48000,channels=" + channel; execl(gstLaunchPath.c_str(), gstLaunchName.c_str(), "alsasrc", audioDeviceParam.c_str(), "!", "volume", "volume=10.0", "!", "audioresample", "!", audioFormatParam.c_str(), "!", "queue", "!", "voaacenc", "!", "aacparse", "!", "queue", "!", "mpegtsmux", "!", "filesink", location.c_str(), (const char*)0); // запускаем код в новом процессе... } // следующий код сработает если произойдет ошибка при вызове execv cout << "Ошибка запуска записи медиаданных в новом процессе\n"; perror("exec"); exit(1); // завершаем процесс } else{ // если находимся в родительском процессе... recordPidMap[mediaStreamId] = recordPid; // заносим идентификатор дочернего процесса в массив // ожидаем завершение процесса... for (int i=0; i< recordingTimeSec; i++){ if(waitpid(recordPid, NULL, WNOHANG) != 0 ){ // если процесс уже завершился recordPidMap[mediaStreamId] = 0; // зануляем идентификатор процесса записи if(recordStatusMap[mediaStreamId] == "started" || recordStatusMap[mediaStreamId] == "starting"){ cout << "В процессе записи файла произошла ошибка\n"; removeEmptyFile(recordingFile); setRecordStatus(mediaStreamId, "starting"); cout << "Перезапуск процесса записи файла будет произведен через 10 секунд\n"; sleep(10); break; } if(recordStatusMap[mediaStreamId] == "stop"){ cout << "Процесс записи медиаданных завершен пользователем, выход из потока записи\n"; setRecordStatus(mediaStreamId, "stop"); removeEmptyFile(recordingFile); return 0; } } else{ // если процесс еще выполняется... time(¤tTime); // получаем текущее время int workingTime = difftime(currentTime, startTime); // вычисляем время в секундах, которое отработал процесс if(workingTime > 10){ if (recordStatusMap[mediaStreamId] == "starting"){ cout << "Процесс записи файла продолжается более 10 секунд. Смена статуса записи на \"Started\"\n"; setRecordStatus(mediaStreamId, "started"); // отправляем клиентам статус записи } } sleep(1); // ждем одну секунду до следующей проверки на окончание процесса } } if (recordStatusMap[mediaStreamId] == "started"){ cout << "Время записи файла истекло, уничтожаем процесс, и начинаем запись нового файла в новом процессе...\n"; killRecord(mediaStreamId); // убиваем процесс записи медиаданных } } } return 0; }