blob: 0606e5646da18e52c4592937f82f18a9ce8b48ea [file] [log] [blame]
Alexandre Lision8521baa2015-03-13 11:08:00 -04001/*
Alexandre Lision9fe374b2016-01-06 10:17:31 -05002 * Copyright (C) 2015-2016 Savoir-faire Linux Inc.
Alexandre Lision8521baa2015-03-13 11:08:00 -04003 * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Alexandre Lision8521baa2015-03-13 11:08:00 -040018 */
Alexandre Lision4a7b95e2015-02-20 10:06:43 -050019#import "VideoPrefsVC.h"
20
Alexandre Lisionb8a81162015-03-20 18:08:01 -040021#import <QuartzCore/QuartzCore.h>
22
23#import <QItemSelectionModel>
24#import <QAbstractProxyModel>
25
26#import <video/configurationproxy.h>
27#import <video/sourcemodel.h>
28#import <video/previewmanager.h>
29#import <video/renderer.h>
30#import <video/device.h>
31#import <video/devicemodel.h>
32
Alexandre Lision4a7b95e2015-02-20 10:06:43 -050033@interface VideoPrefsVC ()
34
Alexandre Lision861d0102016-02-09 17:19:27 -050035@property (assign) IBOutlet NSView* previewView;
36@property (assign) IBOutlet NSPopUpButton* videoDevicesList;
37@property (assign) IBOutlet NSPopUpButton* sizesList;
38@property (assign) IBOutlet NSPopUpButton* ratesList;
Kateryna Kostiuk0a54fa82017-06-14 15:48:37 -040039@property (assign) IBOutlet NSButton *enableHardwareAccelerationButton;
Alexandre Lisionb8a81162015-03-20 18:08:01 -040040
Alexandre Lisione0c7e332015-07-30 11:11:33 -040041@property BOOL shouldHandlePreview;
42
Alexandre Lision4a7b95e2015-02-20 10:06:43 -050043@end
44
45@implementation VideoPrefsVC
Alexandre Lisionb8a81162015-03-20 18:08:01 -040046@synthesize previewView;
47@synthesize videoDevicesList;
48@synthesize sizesList;
49@synthesize ratesList;
Alexandre Lision4a7b95e2015-02-20 10:06:43 -050050
Alexandre Lisionb8a81162015-03-20 18:08:01 -040051QMetaObject::Connection frameUpdated;
52QMetaObject::Connection previewStarted;
53QMetaObject::Connection previewStopped;
54
55- (void)loadView
56{
57 [super loadView];
58
Alexandre Lision861d0102016-02-09 17:19:27 -050059 // Make sure models are loaded
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -040060 Video::ConfigurationProxy::deviceModel().rowCount();
61 Video::ConfigurationProxy::resolutionModel().rowCount();
62 Video::ConfigurationProxy::rateModel().rowCount();
Alexandre Lisionb8a81162015-03-20 18:08:01 -040063
Alexandre Lision861d0102016-02-09 17:19:27 -050064 // Prepopulate values
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -040065 QModelIndex qDeviceIdx = Video::ConfigurationProxy::deviceSelectionModel().currentIndex();
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -040066 [videoDevicesList addItemWithTitle:Video::ConfigurationProxy::deviceModel().data(qDeviceIdx, Qt::DisplayRole).toString().toNSString()];
Alexandre Lisionb8a81162015-03-20 18:08:01 -040067
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -040068 QModelIndex qSizeIdx = Video::ConfigurationProxy::resolutionSelectionModel().currentIndex();
69 [sizesList addItemWithTitle:Video::ConfigurationProxy::resolutionModel().data(qSizeIdx, Qt::DisplayRole).toString().toNSString()];
Alexandre Lisionb8a81162015-03-20 18:08:01 -040070
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -040071 QModelIndex qRate = Video::ConfigurationProxy::rateSelectionModel().currentIndex();
72 [ratesList addItemWithTitle:Video::ConfigurationProxy::rateModel().data(qDeviceIdx, Qt::DisplayRole).toString().toNSString()];
Alexandre Lisionb8a81162015-03-20 18:08:01 -040073
Alexandre Lision861d0102016-02-09 17:19:27 -050074 // connect to model reset (device may have changed) and selection changed signals
75 QObject::connect(qobject_cast<QAbstractProxyModel*>(&Video::ConfigurationProxy::resolutionModel()),
76 &QAbstractProxyModel::modelReset,
77 [=]() {
78 [sizesList removeAllItems];
79 });
Alexandre Lisionb8a81162015-03-20 18:08:01 -040080
Alexandre Lision861d0102016-02-09 17:19:27 -050081 QObject::connect(&Video::ConfigurationProxy::resolutionSelectionModel(),
82 &QItemSelectionModel::currentChanged,
83 [=](const QModelIndex &current, const QModelIndex &previous) {
84 if (!current.isValid()) {
85 return;
86 }
87 [sizesList removeAllItems];
88 [sizesList addItemWithTitle:Video::ConfigurationProxy::resolutionSelectionModel().currentIndex().data(Qt::DisplayRole).toString().toNSString()];
89
90 });
91
92 QObject::connect(qobject_cast<QAbstractProxyModel*>(&Video::ConfigurationProxy::rateModel()),
93 &QAbstractProxyModel::modelReset,
94 [=]() {
95 [ratesList removeAllItems];
96 });
97
98 QObject::connect(&Video::ConfigurationProxy::rateSelectionModel(),
99 &QItemSelectionModel::currentChanged,
100 [=](const QModelIndex &current, const QModelIndex &previous) {
101 if (!current.isValid()) {
102 return;
103 }
104 [ratesList removeAllItems];
105 [ratesList addItemWithTitle:Video::ConfigurationProxy::rateSelectionModel().currentIndex().data(Qt::DisplayRole).toString().toNSString()];
106
107 });
Alexandre Lisionb8a81162015-03-20 18:08:01 -0400108
Alexandre Lisionb8a81162015-03-20 18:08:01 -0400109 [previewView setWantsLayer:YES];
110 [previewView setLayer:[CALayer layer]];
111 [previewView.layer setBackgroundColor:[NSColor blackColor].CGColor];
112 [previewView.layer setContentsGravity:kCAGravityResizeAspect];
113 [previewView.layer setFrame:previewView.frame];
114 [previewView.layer setBounds:previewView.frame];
Kateryna Kostiuk0a54fa82017-06-14 15:48:37 -0400115
116 [self.enableHardwareAccelerationButton setState:Video::ConfigurationProxy::getDecodingAccelerated()];
117
Alexandre Lisionb8a81162015-03-20 18:08:01 -0400118}
119
120- (IBAction)chooseDevice:(id)sender {
121 int index = [sender indexOfSelectedItem];
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400122 QModelIndex qIdx = Video::ConfigurationProxy::deviceModel().index(index, 0);
123 Video::ConfigurationProxy::deviceSelectionModel().setCurrentIndex(qIdx, QItemSelectionModel::ClearAndSelect);
Alexandre Lisionb8a81162015-03-20 18:08:01 -0400124}
125
126- (IBAction)chooseSize:(id)sender {
127 int index = [sender indexOfSelectedItem];
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400128 QModelIndex qIdx = Video::ConfigurationProxy::resolutionModel().index(index, 0);
129 Video::ConfigurationProxy::resolutionSelectionModel().setCurrentIndex(qIdx, QItemSelectionModel::ClearAndSelect);
Alexandre Lisionb8a81162015-03-20 18:08:01 -0400130}
131
132- (IBAction)chooseRate:(id)sender {
133 int index = [sender indexOfSelectedItem];
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400134 QModelIndex qIdx = Video::ConfigurationProxy::rateModel().index(index, 0);
135 Video::ConfigurationProxy::rateSelectionModel().setCurrentIndex(qIdx, QItemSelectionModel::ClearAndSelect);
Alexandre Lisionb8a81162015-03-20 18:08:01 -0400136}
137
Kateryna Kostiuk0a54fa82017-06-14 15:48:37 -0400138- (IBAction)toggleHardwareAcceleration:(NSButton *)sender {
139 bool enabled = [sender state]==NSOnState;
140 Video::ConfigurationProxy::setDecodingAccelerated(enabled);
141}
142
Alexandre Lisionb8a81162015-03-20 18:08:01 -0400143- (void) connectPreviewSignals
144{
145 QObject::disconnect(frameUpdated);
146 QObject::disconnect(previewStopped);
147 QObject::disconnect(previewStarted);
Alexandre Lisiond7b85462015-11-10 17:14:29 -0500148
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400149 previewStarted = QObject::connect(&Video::PreviewManager::instance(),
Alexandre Lisiond7b85462015-11-10 17:14:29 -0500150 &Video::PreviewManager::previewStarted,
151 [=](Video::Renderer* renderer) {
152 NSLog(@"Preview started");
153 QObject::disconnect(frameUpdated);
154 frameUpdated = QObject::connect(renderer,
155 &Video::Renderer::frameUpdated,
156 [=]() {
157 [self renderer:Video::PreviewManager::instance().previewRenderer() renderFrameForView:previewView];
158 });
159 });
Alexandre Lisionb8a81162015-03-20 18:08:01 -0400160
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400161 previewStopped = QObject::connect(&Video::PreviewManager::instance(),
Alexandre Lisiond7b85462015-11-10 17:14:29 -0500162 &Video::PreviewManager::previewStopped,
163 [=](Video::Renderer* renderer) {
164 NSLog(@"Preview stopped");
165 QObject::disconnect(frameUpdated);
166 [previewView.layer setContents:nil];
167 });
Alexandre Lisionb8a81162015-03-20 18:08:01 -0400168
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400169 frameUpdated = QObject::connect(Video::PreviewManager::instance().previewRenderer(),
Alexandre Lisiond7b85462015-11-10 17:14:29 -0500170 &Video::Renderer::frameUpdated,
171 [=]() {
172 [self renderer:Video::PreviewManager::instance().previewRenderer()
Alexandre Lisionb8a81162015-03-20 18:08:01 -0400173 renderFrameForView:previewView];
Alexandre Lisiond7b85462015-11-10 17:14:29 -0500174 });
Alexandre Lisionb8a81162015-03-20 18:08:01 -0400175}
176
177-(void) renderer: (Video::Renderer*)renderer renderFrameForView:(NSView*) view
178{
Alexandre Lisionb8a81162015-03-20 18:08:01 -0400179 QSize res = renderer->size();
180
Alexandre Lision6731e132015-10-14 14:29:06 -0400181 auto frame_ptr = renderer->currentFrame();
182 auto frame_data = frame_ptr.ptr;
183 if (!frame_data)
184 return;
Alexandre Lisionb8a81162015-03-20 18:08:01 -0400185
186 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
Alexandre Lision6731e132015-10-14 14:29:06 -0400187 CGContextRef newContext = CGBitmapContextCreate(frame_data,
Alexandre Lisionb8a81162015-03-20 18:08:01 -0400188 res.width(),
189 res.height(),
190 8,
191 4*res.width(),
192 colorSpace,
193 kCGImageAlphaPremultipliedLast);
194
195
196 CGImageRef newImage = CGBitmapContextCreateImage(newContext);
197
198 /*We release some components*/
199 CGContextRelease(newContext);
200 CGColorSpaceRelease(colorSpace);
201
202 [CATransaction begin];
203 view.layer.contents = (__bridge id)newImage;
204 [CATransaction commit];
205
206 CFRelease(newImage);
207}
208
Alexandre Lisione78a09b2015-07-03 15:52:23 -0400209- (void) viewWillAppear
210{
Alexandre Lisiond7b85462015-11-10 17:14:29 -0500211 // check if preview has to be started/stopped by this controller
212 self.shouldHandlePreview = !Video::PreviewManager::instance().previewRenderer()->isRendering();
213
214 [self connectPreviewSignals];
Alexandre Lisione0c7e332015-07-30 11:11:33 -0400215 if (self.shouldHandlePreview) {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400216 Video::PreviewManager::instance().startPreview();
Alexandre Lisione0c7e332015-07-30 11:11:33 -0400217 }
Alexandre Lisionb8a81162015-03-20 18:08:01 -0400218}
219
220- (void)viewWillDisappear
221{
Alexandre Lisiond7b85462015-11-10 17:14:29 -0500222 QObject::disconnect(frameUpdated);
223 QObject::disconnect(previewStopped);
224 QObject::disconnect(previewStarted);
Alexandre Lisione0c7e332015-07-30 11:11:33 -0400225 if (self.shouldHandlePreview) {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400226 Video::PreviewManager::instance().stopPreview();
Alexandre Lisione0c7e332015-07-30 11:11:33 -0400227 }
Alexandre Lisionb8a81162015-03-20 18:08:01 -0400228}
229
230#pragma mark - NSMenuDelegate methods
231
232- (BOOL)menu:(NSMenu *)menu updateItem:(NSMenuItem *)item atIndex:(NSInteger)index shouldCancel:(BOOL)shouldCancel
233{
234 QModelIndex qIdx;
Alexandre Lisione1cf4462015-10-08 15:44:38 -0400235 if(self.videoDevicesList.menu == menu) {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400236 qIdx = Video::ConfigurationProxy::deviceModel().index(index, 0);
237 [item setTitle:Video::ConfigurationProxy::deviceModel().data(qIdx, Qt::DisplayRole).toString().toNSString()];
Alexandre Lision861d0102016-02-09 17:19:27 -0500238 if (qIdx == Video::ConfigurationProxy::deviceSelectionModel().currentIndex()) {
239 [videoDevicesList selectItem:item];
240 }
Alexandre Lisione1cf4462015-10-08 15:44:38 -0400241 } else if(self.sizesList.menu == menu) {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400242 qIdx = Video::ConfigurationProxy::resolutionModel().index(index, 0);
243 [item setTitle:Video::ConfigurationProxy::resolutionModel().data(qIdx, Qt::DisplayRole).toString().toNSString()];
Alexandre Lision861d0102016-02-09 17:19:27 -0500244 if (qIdx == Video::ConfigurationProxy::resolutionSelectionModel().currentIndex()) {
245 [sizesList selectItem:item];
246 }
Alexandre Lisionb8a81162015-03-20 18:08:01 -0400247
Alexandre Lisione1cf4462015-10-08 15:44:38 -0400248 } else if(self.ratesList.menu == menu) {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400249 qIdx = Video::ConfigurationProxy::rateModel().index(index, 0);
250 [item setTitle:Video::ConfigurationProxy::rateModel().data(qIdx, Qt::DisplayRole).toString().toNSString()];
Alexandre Lision861d0102016-02-09 17:19:27 -0500251 if (qIdx == Video::ConfigurationProxy::rateSelectionModel().currentIndex()) {
252 [ratesList selectItem:item];
253 }
Alexandre Lisionb8a81162015-03-20 18:08:01 -0400254 }
255 return YES;
256}
257
258- (NSInteger)numberOfItemsInMenu:(NSMenu *)menu
259{
Alexandre Lisione1cf4462015-10-08 15:44:38 -0400260 if(self.videoDevicesList.menu == menu) {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400261 return Video::ConfigurationProxy::deviceModel().rowCount();
Alexandre Lisione1cf4462015-10-08 15:44:38 -0400262 } else if(self.sizesList.menu == menu) {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400263 return Video::ConfigurationProxy::resolutionModel().rowCount();
Alexandre Lisione1cf4462015-10-08 15:44:38 -0400264 } else if(self.ratesList.menu == menu) {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400265 return Video::ConfigurationProxy::rateModel().rowCount();
Alexandre Lisionb8a81162015-03-20 18:08:01 -0400266 }
Alexandre Lisione1cf4462015-10-08 15:44:38 -0400267 return 0;
Alexandre Lision4a7b95e2015-02-20 10:06:43 -0500268}
269
270@end