public RecordingMetadata ConvertPreviewMedia(ConversionConfiguration config) { // generate output directory if (Directory.Exists(config.OutputDirectory)) { Directory.Delete(config.OutputDirectory, true); } Directory.CreateDirectory(config.OutputDirectory); // convert file ConvertVideoFiles(config, true); // generate recording object var finalRecording = new RecordingMetadata(); finalRecording.FileName = "slides.mp4"; finalRecording.PresenterFileName = "talkinghead.mp4"; finalRecording.StageVideo = "stage.mp4"; finalRecording.Slides = BuildThumbnails(config, "slides.mp4"); finalRecording.Duration = FFmpegHelper.GetMediaLength(config.SlideVideoPath).TotalSeconds; FFmpegHelper.ExportThumbnail(5f, Path.Combine(config.OutputDirectory, "stage.mp4"), config.OutputDirectory, "thumbnail"); return(finalRecording); }
public void FinalizeAnswer(string itemId, string answer) { string recId = Guid.NewGuid().ToString(); // Create a dummy recording so that we can save it to the database Recording rec = new Recording { RecordingId = recId, ItemId = itemId, FileName = $"{recId}-{Constants.MetadataWithoutRecording}", ClientId = Preferences.Get(Constants.ClientIdKey, "unknown"), Timestamp = DateTime.UtcNow, UploadStatus = UploadStatus.Pending }; RecordingMetadata metadata = new RecordingMetadata(rec.RecordingId); metadata.ClientId = rec.ClientId; metadata.ItemId = rec.ItemId; metadata.RecordingTimestamp = rec.Timestamp; // Add user metadata based on the item view model var umd = new UserMetadata(); var userAnswer = new UserAnswer(); userAnswer.ItemId = itemId; userAnswer.Value = answer; umd.AddAnswer(userAnswer); metadata.User = umd; rec.Metadata = metadata.ToJsonString(); App.Database.SaveRecordingAsync(rec); Debug.WriteLine($"Saved answer '{answer}', recording '{rec.RecordingId}', item '{itemId}'"); }
protected override void OnCreate(Bundle bundle) { base.OnCreate(bundle); HideTitleBar(); _fileName = Intent.GetStringExtra("FileNameClicked") ?? RecordingContext.filename; _baseDirectory = ConfigService.BaseDirectory; _metadataService = new MetadataService(); _audioFileService = new AudioFileService(); _audioPlayService = new AudioPlayService(_baseDirectory, _fileName); _metadata = _metadataService.GetRecordingMetadata(_baseDirectory, _fileName); AnalysisContext.UpdateContext(_baseDirectory, _fileName); SetContentView(Resource.Layout.AnalyseActivity); SetupButtons(); if (_metadata.StartedAt != null && _metadata.StartedAt != default(DateTime)) { FindViewById <TextView>(Resource.Id.title).Text = _metadata.StartedAt.ToString("f"); } else { var fullAudioPath = _audioFileService.GetFullPathToRecording(_baseDirectory, _fileName); FindViewById <TextView>(Resource.Id.title).Text = fullAudioPath.Replace(_baseDirectory, ""); } _playing = false; SetPlayButtonIcon(); }
public AudioPlayService(string baseDirectory, string fileName) { _metadataService = new MetadataService(); _audioFileService = new AudioFileService(); _metadata = _metadataService.GetRecordingMetadata(baseDirectory, fileName); _fullPathToAudio = _audioFileService.GetFullPathToRecording(baseDirectory, fileName); }
public void UpdateMetadataFile(RecordingMetadata metadata, string baseDirectory, string fileName) { using (var fs = new FileStream(GetFullPathToMetadata(baseDirectory, fileName), FileMode.Open, FileAccess.Write)) { string dataasstring = JsonConvert.SerializeObject(metadata); byte[] info = new UTF8Encoding(true).GetBytes(dataasstring); fs.Write(info, 0, info.Length); fs.Close(); } }
public async Task <UploadDescription> InitUploadAsync(Recording recording, RecordingMetadata metadata) { var uri = new Uri($"{appConfiguration.RecorderApiUrl}/{InitUploadUrlPath}"); // The recordings are stored in the Documents folder. We only save the filename in the database // (because deleting and reinstalling the app may cause the full path to change), so we need to // construct the full pathname again: //var documentsFolder = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); //string fileName = Path.Combine(documentsFolder, recording.FileName); var fileName = Path.GetFileName(recording.FileName); JObject payload = new JObject( new JProperty("filename", fileName), new JProperty("metadata", metadata.ToJsonObject()) ); try { string payloadString = payload.ToString(); Debug.WriteLine($"JSON payload = '{payloadString}'"); Debug.WriteLine($"JSON payload length = {payloadString.Length} characters"); var content = new StringContent(payloadString, Encoding.UTF8, "application/json"); var request = new HttpRequestMessage() { RequestUri = uri, Method = HttpMethod.Post, Content = content }; request.Headers.Add("x-api-key", appConfiguration.RecorderApiKey); var response = await client.SendAsync(request); if (response.IsSuccessStatusCode) { var responseContent = await response.Content.ReadAsStringAsync(); return(JsonConvert.DeserializeObject <UploadDescription>(responseContent, uploadDescriptionJsonSerializerSettings)); } else { Debug.WriteLine($"Post request to {uri.ToString()} failed, status: {response.StatusCode}, reason: {response.ReasonPhrase}"); throw new Exception("Failed to init upload"); } } catch (Exception ex) { Debug.WriteLine($" ERROR {ex.Message}"); throw new Exception("Failed to init upload", ex); } }
public async Task <RecordingMetadata> CreateAsync(UserRecordingInput data) { var recordingEntry = new RecordingEntry(); var recordingMetadata = new RecordingMetadata(); var id = Guid.NewGuid().ToString(); recordingEntry.Recording = data.Recording; recordingEntry.Id = id; recordingMetadata.Title = data.Title; recordingMetadata.Description = data.Description; recordingMetadata.CreatedBy = User.FindFirst(ClaimTypes.NameIdentifier).Value; recordingMetadata.GivenName = User.FindFirstValue(ClaimTypes.GivenName); recordingMetadata.Surname = User.FindFirstValue(ClaimTypes.Surname); recordingMetadata.Id = id; recordingMetadata.CreatedAt = DateTimeOffset.Now; await _cosmosDbService.AddRecordingAsync(recordingEntry, recordingMetadata); return(recordingMetadata); }
public RecordingMetadata ConvertPreviewZoomMedia(ConversionConfiguration config) { // generate output directory if (Directory.Exists(config.OutputDirectory)) { Directory.Delete(config.OutputDirectory, true); } Directory.CreateDirectory(config.OutputDirectory); // convert file ConvertZoomVideoFile(config, true); var targetVideoPath = Path.Combine(config.OutputDirectory, "slides.mp4"); // generate preview var thumbOutDir = Path.Combine(config.OutputDirectory, "thumbs"); Directory.CreateDirectory(thumbOutDir); string thumbName = FFmpegHelper.ExportThumbnail(5f, targetVideoPath, Path.Combine(config.OutputDirectory, "thumbs"), "0"); // generate recording object var finalRecording = new RecordingMetadata(); finalRecording.FileName = "slides.mp4"; finalRecording.Slides = new Slide[] { new Slide { StartPosition = 0, Thumbnail = "thumbs/" + thumbName, Ocr = "" } }; finalRecording.Duration = FFmpegHelper.GetMediaLength(targetVideoPath).TotalSeconds; FFmpegHelper.ExportThumbnail(5f, Path.Combine(config.OutputDirectory, "slides.mp4"), config.OutputDirectory, "thumbnail"); return(finalRecording); }
public RecordingListTests() { this.data = TestHelper.Get <RecordingMetadata>("recording-search.xml", false); }
public void FinalizeRecording(string answer) { if (!IsRecording) { Debug.WriteLine("Skipping recording finalize since not recording"); return; } Debug.WriteLine($"RecordingManager.FinalizeRecording, item ID = '{recording.ItemId}'"); var file = recorder.Stop(); RecordingMetadata metadata = new RecordingMetadata(recording.RecordingId) { ClientId = recording.ClientId, ItemId = recording.ItemId, RecordingTimestamp = file.CreatedOn, RecordingDuration = file.Duration, RecordingBitDepth = file.BitDepth, RecordingSampleRate = file.SampleRate, RecordingNumberOfChannels = file.NumberOfChannels, ContentType = file.ContentType, }; Debug.WriteLine($"ContentType = '{file.ContentType}'"); // Explicitly set the User object to null for a recording metadata.User = null; string metadataString = metadata.ToJsonString(); Debug.WriteLine($"After finalizing recording, metadata = '{metadataString}'"); recording.FileName = file.FileName; recording.Metadata = metadataString; recording.UploadStatus = UploadStatus.Pending; App.Database.SaveRecordingAsync(recording); Debug.WriteLine($"Saved recording information for '{recording.RecordingId}' in the database"); var dict = new Dictionary <string, string> { //{ AnalyticsParameterNamesConstants.RecordingId, metadata.RecordingId }, { AnalyticsParameterNamesConstants.ClientId, metadata.ClientId }, { AnalyticsParameterNamesConstants.ItemId, metadata.ItemId }, { AnalyticsParameterNamesConstants.RecordingTimestamp, metadata.RecordingTimestamp.ToString("o") }, { AnalyticsParameterNamesConstants.ClientPlatformName, metadata.ClientPlatformName }, { AnalyticsParameterNamesConstants.ClientPlatformVersion, metadata.ClientPlatformVersion }, { AnalyticsParameterNamesConstants.RecordingDuration, metadata.RecordingDuration.ToString() }, { AnalyticsParameterNamesConstants.RecordingSpecification, $"{metadata.RecordingSampleRate.ToString()}/{metadata.RecordingBitDepth.ToString()}/{metadata.RecordingNumberOfChannels.ToString()}" }, { AnalyticsParameterNamesConstants.BuildType, appConfiguration.BuildType }, }; var app = Application.Current as App; app.AnalyticsEventTracker.SendEvent(AnalyticsEventNamesConstants.RecordingCompleted, dict); // Update the total seconds recorded int durationInSeconds = (int)Math.Floor(metadata.RecordingDuration); app.UpdateTotalRecorded(durationInSeconds); }
public async Task SynchronizePublishedRecordings(int lectureId) { var lecture = await _context.Lectures .Include(x => x.Semester) .SingleAsync(x => x.Id == lectureId); if (!lecture.Active) { return; } var recordings = await _context.Recordings .Include(x => x.Chapters) .Include(x => x.Outputs) .Where(x => x.LectureId == lectureId) .Where(x => x.Outputs.Any(x => x.Status == RecordingStatus.PUBLISHED)) .OrderBy(x => x.Sorting).ThenBy(x => x.UploadDate) .ToListAsync(); var lectureMetadata = new RecordingProcessor.Model.Lecture() { Name = lecture.Title, Semester = lecture.Semester.Name, Recordings = new List <RecordingMetadata>() }; foreach (var recording in recordings) { var targetFolderName = "video/" + recording.Id.ToString(); var legacy = false; // generate metadata var metadata = new RecordingMetadata() { Id = recording.Title, Name = recording.Title, Description = recording.Description, Duration = recording.Duration, Date = recording.PublishDate.Value }; // check outputs foreach (var output in recording.Outputs .Where(x => x.Status == RecordingStatus.PUBLISHED) .Where(x => x.JobType == typeof(ProcessRecordingJob).FullName)) { var configuration = JsonConvert.DeserializeObject <ProcessRecordingJobConfiguration>(output.JobConfiguration); // 720p processed? if (configuration.OutputType == ProcessRecordingOutputType.Default || configuration.OutputType == ProcessRecordingOutputType.Video_720p) { metadata.FileName = targetFolderName + "/output_720p/slides.mp4"; metadata.StageVideo = targetFolderName + "/output_720p/stage.mp4"; metadata.PresenterFileName = targetFolderName + "/output_720p/talkinghead.mp4"; } // 1080p processed? if (configuration.OutputType == ProcessRecordingOutputType.Video_1080P) { metadata.FileNameHd = targetFolderName + "/output_1080p/slides.mp4"; metadata.StageVideoHd = targetFolderName + "/output_1080p/stage.mp4"; metadata.PresenterFileNameHd = targetFolderName + "/output_1080p/talkinghead.mp4"; } // legacy? if (configuration.OutputType == ProcessRecordingOutputType.Legacy) { metadata.FileName = "video/" + recording.CustomTargetName + "/slides.mp4"; metadata.StageVideo = "video/" + recording.CustomTargetName + "/stage.mp4"; metadata.PresenterFileName = "video/" + recording.CustomTargetName + "/talkinghead.mp4"; legacy = true; } } metadata.Slides = recording.Chapters.OrderBy(x => x.StartPosition).Select(x => new Slide() { Thumbnail = (legacy ? "video/" + recording.CustomTargetName + "/" : targetFolderName + "/output_720p/") + x.Thumbnail, Ocr = x.Text, StartPosition = (float)x.StartPosition }).ToArray(); lectureMetadata.Recordings.Add(metadata); } // update metadata var courseJsonMetadataFile = Path.Combine(lecture.PublishPath, "assets", "lecture.json"); MetadataService.SaveSettings(lectureMetadata, courseJsonMetadataFile); // update lecture lecture.LastSynchronized = DateTime.Now; await _context.SaveChangesAsync(); await UpdateLectureStatus(); }
public async Task AddRecordingAsync(RecordingEntry recordingEntry, RecordingMetadata recordingMetadata) { await _containers["recordings"].CreateItemAsync(recordingEntry, new PartitionKey(recordingEntry.Id)); await _containers["recording_metadata"].CreateItemAsync(recordingMetadata, new PartitionKey(recordingEntry.Id)); }
public async Task Preview(int recordingId) { // get recording var recording = await _context.Recordings .Include(x => x.Chapters) .Include(x => x.Outputs) .FirstOrDefaultAsync(x => x.Id == recordingId); if (recording == null) { return; } // create new recording output var recordingOutput = new RecordingOutput() { RecordingId = recordingId, DateStarted = DateTime.Now, Status = RecordingStatus.PROCESSING, JobType = typeof(PreviewRecordingJob).FullName, JobConfiguration = null }; _context.RecordingOutputs.Add(recordingOutput); await _context.SaveChangesAsync(); await UpdateLectureRecordingStatus(); // get lecture var lecture = await _context.Lectures .FindAsync(recording.LectureId); // start encoding preview if (recording.Type == RecordingType.GREEN_SCREEN_RECORDING || recording.Type == RecordingType.SIMPLE_RECORDING || recording.Type == RecordingType.ZOOM_RECORDING) { // file path is file? string outputFolder = Path.Combine(lecture.ConvertedPath, recording.Id.ToString(), "preview"); string inputFileName = ""; if (File.Exists(recording.FilePath)) { inputFileName = recording.FilePath; } else if (recording.Type == RecordingType.ZOOM_RECORDING) { inputFileName = Directory.GetFiles(recording.FilePath) .Where(x => x.EndsWith(".mp4")) .SingleOrDefault(); } else { var targetName = recording.CustomTargetName != null ? recording.CustomTargetName : ""; inputFileName = Directory.GetFiles(recording.FilePath) .Where(x => x.EndsWith(targetName + "_meta.json")) .SingleOrDefault(); } if (inputFileName == null) { throw new Exception("Could not find studio meta data."); } // convert files RecordingMetadata metaData = null; if (recording.Type == RecordingType.GREEN_SCREEN_RECORDING) { metaData = ConvertStudioRecording(inputFileName, outputFolder); recording.Duration = metaData.Duration; } else if (recording.Type == RecordingType.SIMPLE_RECORDING) { metaData = ConvertSimpleRecording(inputFileName, outputFolder); recording.Duration = metaData.Duration; } else if (recording.Type == RecordingType.ZOOM_RECORDING) { metaData = ConvertZoomRecording(inputFileName, outputFolder); recording.Duration = metaData.Duration; } if (metaData != null) { _context.RecordingChapters.RemoveRange(recording.Chapters); // add slides foreach (var slide in metaData.Slides) { var chapter = new RecordingChapter() { Recording = recording, StartPosition = slide.StartPosition, Text = slide.Ocr, Thumbnail = slide.Thumbnail }; _context.RecordingChapters.Add(chapter); } } // set status recordingOutput.Status = RecordingStatus.PROCESSED; recordingOutput.JobError = null; recordingOutput.DateFinished = DateTime.Now; await _context.SaveChangesAsync(); await UpdateLectureRecordingStatus(); } }
public static void UpdateSamples(string baseLocation, string fileName) { _metadataService = new MetadataService(); _metadata = _metadataService.GetRecordingMetadata(baseLocation, fileName); UpdateSamples(); }
public async Task Execute(ProcessRecordingJobConfiguration configuration) { // get recording var recording = await _context.Recordings .Include(x => x.Chapters) .Include(x => x.Outputs) .FirstOrDefaultAsync(x => x.Id == configuration.RecordingId); if (recording == null) { return; } // create new recording output var recordingOutput = new RecordingOutput() { RecordingId = configuration.RecordingId, DateStarted = DateTime.Now, Status = RecordingStatus.PROCESSING, JobType = typeof(ProcessRecordingJob).FullName, JobConfiguration = JsonConvert.SerializeObject(configuration) }; _context.RecordingOutputs.Add(recordingOutput); await _context.SaveChangesAsync(); await UpdateLectureRecordingStatus(); // get lecture var lecture = await _context.Lectures .FindAsync(recording.LectureId); // start encoding if (recording.Type == RecordingType.GREEN_SCREEN_RECORDING || recording.Type == RecordingType.SIMPLE_RECORDING || recording.Type == RecordingType.ZOOM_RECORDING) { // file path is file? string outputFolder = Path.Combine(lecture.ConvertedPath, recording.Id.ToString(), "output_" + recordingOutput.Id); string inputFileName = ""; if (File.Exists(recording.FilePath)) { inputFileName = recording.FilePath; } else if (recording.Type == RecordingType.ZOOM_RECORDING) { inputFileName = Directory.GetFiles(recording.FilePath) .Where(x => x.EndsWith(".mp4")) .SingleOrDefault(); } else { string targetName = recording.CustomTargetName != null ? recording.CustomTargetName : ""; inputFileName = Directory.GetFiles(recording.FilePath) .Where(x => x.EndsWith(targetName + "_meta.json")) .SingleOrDefault(); } // create output directory Directory.CreateDirectory(outputFolder); if (inputFileName == null) { recordingOutput.Status = RecordingStatus.ERROR; recordingOutput.JobError = "Could not find studio meta data."; await _context.SaveChangesAsync(); await UpdateLectureRecordingStatus(); throw new Exception("Could not find studio meta data."); } try { // convert files RecordingMetadata metaData = null; if (recording.Type == RecordingType.GREEN_SCREEN_RECORDING) { metaData = ConvertStudioRecording(inputFileName, outputFolder, GetDimension(configuration.OutputType)); } else if (recording.Type == RecordingType.SIMPLE_RECORDING) { metaData = ConvertSimpleRecording(inputFileName, outputFolder, GetDimension(configuration.OutputType)); } else if (recording.Type == RecordingType.ZOOM_RECORDING) { metaData = ConvertZoomRecording(inputFileName, outputFolder); } recordingOutput.Status = RecordingStatus.PROCESSED; recordingOutput.JobError = null; recordingOutput.DateFinished = DateTime.Now; await _context.SaveChangesAsync(); await UpdateLectureRecordingStatus(); } catch (Exception ex) { recordingOutput.Status = RecordingStatus.ERROR; recordingOutput.JobError = ex.Message; recordingOutput.DateFinished = DateTime.Now; await UpdateLectureRecordingStatus(); throw ex; } } }