diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..735528e --- /dev/null +++ b/.DS_Store Binary files differ diff --git a/Classes/ChatController.h b/Classes/ChatController.h new file mode 100644 index 0000000..79e0fba --- /dev/null +++ b/Classes/ChatController.h @@ -0,0 +1,18 @@ +// +// ChatController.h +// Tuve +// +// Created by Philippe Hausler on 9/8/08. +// Copyright 2008 Philippe Hausler. All rights reserved. +// + +#import + + +@interface ChatController : UIViewController { + NSMutableArray *messages; + IBOutlet UITableView *tableView; +} +@property (nonatomic,assign) UITableView *tableView; +- (void)addMessage:(NSString *)message fromUser:(NSString *)user; +@end diff --git a/Classes/ChatController.m b/Classes/ChatController.m new file mode 100644 index 0000000..5bbed39 --- /dev/null +++ b/Classes/ChatController.m @@ -0,0 +1,118 @@ +// +// ChatController.m +// Tuve +// +// Created by Philippe Hausler on 9/8/08. +// Copyright 2008 Philippe Hausler. All rights reserved. +// + +#import "ChatController.h" + + +@implementation ChatController +@synthesize tableView; + +- (id)initWithStyle:(UITableViewStyle)style { + if (self = [super initWithStyle:style]) { + } + return self; +} + + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { + return 1; +} + + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + return [messages count]; +} + + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { + NSInteger index = [indexPath indexAtPosition:1]; + static NSString *cellTypeId = @"chatCell"; + + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellTypeId]; + if (cell == nil) { + cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:cellTypeId] autorelease]; + cell.font = [UIFont systemFontOfSize:10]; + cell.lineBreakMode = UILineBreakModeWordWrap; + } + // Configure the cell + cell.text = [messages objectAtIndex:index]; + return cell; +} + +/* +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { +} +*/ +/* +- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { + + if (editingStyle == UITableViewCellEditingStyleDelete) { + } + if (editingStyle == UITableViewCellEditingStyleInsert) { + } +} +*/ +/* +- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { + return YES; +} +*/ +/* +- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath { +} +*/ +/* +- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath { + return YES; +} +*/ +- (void)msgRecieved:(NSNotification *)aNotification +{ + NSDictionary *userInfo = [aNotification userInfo]; + [self addMessage:[userInfo objectForKey:@"message"] fromUser:[userInfo objectForKey:@"sender"]]; +} +- (void)addMessage:(NSString *)message fromUser:(NSString *)user +{ + [messages addObject:[NSString stringWithFormat:@"%@: %@", user,message]]; + [self.tableView reloadData]; + [self.tableView scrollToRowAtIndexPath:[[NSIndexPath indexPathWithIndex:0] indexPathByAddingIndex:[messages count]-1] atScrollPosition:UITableViewScrollPositionBottom animated:YES]; +} + +- (void)dealloc { + [super dealloc]; +} + + +- (void)viewDidLoad { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(msgRecieved:) name:@"TUMSG" object:NULL]; + messages = [[NSMutableArray alloc] init]; + [super viewDidLoad]; +} + + +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; +} + +- (void)viewDidAppear:(BOOL)animated { + [super viewDidAppear:animated]; +} + +- (void)viewWillDisappear:(BOOL)animated { +} + +- (void)viewDidDisappear:(BOOL)animated { +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; +} + + +@end + diff --git a/Classes/TCPSocket.h b/Classes/TCPSocket.h new file mode 100644 index 0000000..2502795 --- /dev/null +++ b/Classes/TCPSocket.h @@ -0,0 +1,26 @@ +// +// ALSocketPort.h +// Aluminum Framework +// +// Created by Philippe Hausler on 8/8/08. +// Copyright 2008. All rights reserved. +// + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +@interface TCPSocket : NSObject { + NSSocketNativeHandle fd; + struct sockaddr_in serverAddress; +} +- (id)initWithTCPPort:(unsigned short)port; +- (id)initWithConnectionTo:(NSString *)host onTCPPort:(unsigned short)port; +- (NSSocketNativeHandle)socket; +@end diff --git a/Classes/TCPSocket.m b/Classes/TCPSocket.m new file mode 100644 index 0000000..a3c5ead --- /dev/null +++ b/Classes/TCPSocket.m @@ -0,0 +1,74 @@ +// +// ALSocketPort.m +// Aluminum Framework +// +// Created by Philippe Hausler on 8/8/08. +// Copyright 2008. All rights reserved. +// + + +#import "TCPSocket.h" +@implementation TCPSocket +- (id)initWithTCPPort:(unsigned short)port +{ + self = [super init]; + if ( (fd = socket( AF_INET, SOCK_STREAM, 0 )) < 0 ) { + return NULL; + } + int on = 1; + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + bzero( &serverAddress, sizeof(serverAddress) ); + serverAddress.sin_family = AF_INET; + serverAddress.sin_port = htons(port); + serverAddress.sin_addr.s_addr = htonl( INADDR_ANY ); + + if ( bind( fd, (struct sockaddr *)&serverAddress, sizeof(serverAddress) ) < 0 ) { + return NULL; + } + + if ( listen( fd, 5 ) < 0 ) { + return NULL; + } + return self; +} +- (id)initWithConnectionTo:(NSString *)host onTCPPort:(unsigned short)port +{ + self = [super init]; + fd = socket(PF_INET, SOCK_STREAM, 0); + + if(fd == -1) + { + return NULL; + } + + struct sockaddr_in stSockAddr; + bzero(&stSockAddr, sizeof(stSockAddr)); + + stSockAddr.sin_family = AF_INET; + stSockAddr.sin_port = htons(port); + + struct hostent *host_entry = gethostbyname([host UTF8String]); + char * ip = inet_ntoa (*(struct in_addr *)*host_entry->h_addr_list); + + int err = inet_pton(AF_INET, ip, (void *) &stSockAddr.sin_addr); + + if(err <= 0) + { + return NULL; + } + + if(connect(fd,(struct sockaddr*) &stSockAddr, sizeof(stSockAddr)) == -1) + { + return NULL; + } + return self; +} +- (NSData *)address +{ + return [NSData dataWithBytes:(void *)&serverAddress length:sizeof(struct sockaddr_in)]; +} +- (NSSocketNativeHandle)socket +{ + return fd; +} +@end diff --git a/Classes/TuveAppDelegate.h b/Classes/TuveAppDelegate.h new file mode 100644 index 0000000..2fab28c --- /dev/null +++ b/Classes/TuveAppDelegate.h @@ -0,0 +1,26 @@ +// +// TuveAppDelegate.h +// Tuve +// +// Created by Philippe Hausler on 9/8/08. +// Copyright Philippe Hausler 2008. All rights reserved. +// + +#import + +@class TuveViewController; +@class TuveConnection; +@interface TuveAppDelegate : NSObject { + IBOutlet UIWindow *window; + IBOutlet UINavigationController *nav; + IBOutlet UIViewController *flipside; + IBOutlet UITextField *chatBox; + TuveConnection *connection; + BOOL flipped; +} + +@property (nonatomic, retain) UIWindow *window; +- (IBAction)toggleView; +- (IBAction)sendChat; +@end + diff --git a/Classes/TuveAppDelegate.m b/Classes/TuveAppDelegate.m new file mode 100644 index 0000000..67169ea --- /dev/null +++ b/Classes/TuveAppDelegate.m @@ -0,0 +1,86 @@ +// +// TuveAppDelegate.m +// Tuve +// +// Created by Philippe Hausler on 9/8/08. +// Copyright Philippe Hausler 2008. All rights reserved. +// + +#import "TuveAppDelegate.h" +#import "TuveConnection.h" +@implementation TuveAppDelegate + +@synthesize window; + +- (void)applicationDidFinishLaunching:(UIApplication *)application { + connection = [[TuveConnection alloc] init]; + //NSURL *test = [NSURL URLWithString:@"rtmp://rtmp.ubixonline.com/Titiyo%20-%20Come%20Along.flv"]; + //NSLog(@"%@", test); + flipped = NO; + [window makeKeyAndVisible]; + [window addSubview:nav.view]; + +} + +- (IBAction)toggleView { + [UIView beginAnimations:nil context:NULL]; + [UIView setAnimationDuration:1]; + [UIView setAnimationTransition:(flipped ? UIViewAnimationTransitionFlipFromRight : UIViewAnimationTransitionFlipFromLeft) forView:window cache:YES]; + if(!flipped) + { + [flipside viewWillAppear:YES]; + [nav viewWillDisappear:YES]; + [nav.view removeFromSuperview]; + [window addSubview:flipside.view]; + [nav viewDidDisappear:YES]; + [flipside viewDidAppear:YES]; + flipped = YES; + } + else + { + [nav viewWillAppear:YES]; + [flipside viewWillDisappear:YES]; + [flipside.view removeFromSuperview]; + [window addSubview:nav.view]; + [flipside viewDidDisappear:YES]; + [nav viewDidAppear:YES]; + flipped = NO; + } + [UIView commitAnimations]; +} +- (IBAction)sendChat +{ + [connection sendChat:chatBox.text toChannel:@"#TUvé"]; + chatBox.text = @""; +} +- (void)dealloc { + [window release]; + [super dealloc]; +} + +- (void)textFieldDidBeginEditing:(UITextField *)textField +{ + CGRect rect = window.frame; + [UIView beginAnimations:nil context:NULL]; + [UIView setAnimationDuration:0.3]; + rect.origin.y = 35-window.frame.size.height+textField.superview.frame.origin.y-textField.superview.frame.size.height; + window.frame = rect; + [UIView commitAnimations]; +} +- (BOOL)textFieldShouldReturn:(UITextField *)textField +{ + [textField endEditing:YES]; + return YES; +} +- (BOOL)textFieldShouldEndEditing:(UITextField *)textField +{ + + CGRect rect = window.frame; + [UIView beginAnimations:nil context:NULL]; + [UIView setAnimationDuration:0.3]; + rect.origin.y = 0; + window.frame = rect; + [UIView commitAnimations]; + return YES; +} +@end diff --git a/Classes/TuveConnection.h b/Classes/TuveConnection.h new file mode 100644 index 0000000..09fd3ec --- /dev/null +++ b/Classes/TuveConnection.h @@ -0,0 +1,36 @@ +// +// TuveConnection.h +// Tuve +// +// Created by Philippe Hausler on 9/8/08. +// Copyright 2008 Philippe Hausler. All rights reserved. +// + +#import +enum { + TUWaitingForCrossDomainPolicy = 1, + TUWaitingForVersionHandshake = 2, + TUWaitingForIdentResponse = 3, + TUWaitingForChannelJoinResponse = 4, + TUWaitingForLoginResponse = 5, + TUIde = 999 +}; +@class TCPSocket; +@interface TuveConnection : NSObject { + TCPSocket *channelSocket; + NSFileHandle *channelConnection; + NSUInteger protocolStage; + NSString *buffer; + NSString *tChannel; + NSString *login; + NSString *password; +} +@property (nonatomic, copy) NSString *tChannel; +- (void)processString:(NSString *)packetStr; +- (void)sendVersion; +- (void)sendIdent; +- (void)joinChannel:(NSString *)channel; +- (void)login; +- (void)pong; +- (void)sendChat:(NSString *)chat toChannel:(NSString *)channel; +@end diff --git a/Classes/TuveConnection.m b/Classes/TuveConnection.m new file mode 100644 index 0000000..afc4a06 --- /dev/null +++ b/Classes/TuveConnection.m @@ -0,0 +1,161 @@ +// +// TuveConnection.m +// Tuve +// +// Created by Philippe Hausler on 9/8/08. +// Copyright 2008 Philippe Hausler. All rights reserved. +// + +#import "TuveConnection.h" +#import "TCPSocket.h" +#import "MediaPlayer/MediaPlayer.h" +@implementation TuveConnection +@synthesize tChannel; +- (id)init +{ + self = [super init]; + channelSocket = [[TCPSocket alloc] initWithConnectionTo:@"Ivorytower.UbixOnline.com" onTCPPort:9999]; + channelConnection = [[NSFileHandle alloc] initWithFileDescriptor:[channelSocket socket]]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dataAvailable:) name:NSFileHandleDataAvailableNotification object:NULL]; + [channelConnection writeData:[@"\0" dataUsingEncoding:NSUTF8StringEncoding]]; + tChannel = @"#TUvé"; + login = @"Lenari"; + password = @"cynthiana12"; + protocolStage = TUWaitingForCrossDomainPolicy; + [channelConnection waitForDataInBackgroundAndNotify]; + return self; +} +- (void)dataAvailable:(NSNotification *)aNotification +{ + NSData *packet = [channelConnection availableData]; + NSString *packetStr = [[[NSString alloc] initWithBytes:[packet bytes] length:[packet length] encoding:NSUTF8StringEncoding] autorelease]; + [self processString:packetStr]; + [[aNotification object] waitForDataInBackgroundAndNotify]; +} +- (void)processString:(NSString *)packetStr +{ + NSArray *packets = [packetStr componentsSeparatedByString:@"\n"]; + if(buffer == NULL) + { + [buffer release]; + } + if(![packetStr hasSuffix:@"\n"]) + { + buffer = [[NSString stringWithString:[packets objectAtIndex:([packets count]-1)]] retain]; + } + if(protocolStage == TUWaitingForCrossDomainPolicy) + { + [self sendVersion]; + } + else + { + for(NSString *packet in packets) + { + NSLog(packet); + NSArray *packetParts = [packet componentsSeparatedByString:@":"]; + if([packet hasPrefix:@"CLIENT"]) + { + if([[packetParts objectAtIndex:1] isEqualToString:@"1"]) + { + NSLog(@"Negotiation Error: %@", [packetParts objectAtIndex:2]); + } + else if([[packetParts objectAtIndex:1] isEqualToString:@"0"]) + { + [self sendIdent]; + } + } + else if([packet hasPrefix:@"IDENT"]) + { + if([[packetParts objectAtIndex:1] isEqualToString:@"1"]) + { + NSLog(@"Identification Error: %@", [packetParts objectAtIndex:2]); + } + else if([[packetParts objectAtIndex:1] isEqualToString:@"0"]) + { + [self joinChannel:tChannel]; + } + } + else if([packet hasPrefix:@"JOIN"]) + { + if([[packetParts objectAtIndex:1] isEqualToString:@"ERROR"]) + { + NSLog(@"Channel Error: %@", [packetParts objectAtIndex:2]); + } + else + { + [self login]; + } + } + else if([packet hasPrefix:@"MODE"]) + { + } + else if([packet hasPrefix:@"PING"]) + { + [self pong]; + } + else if([packet hasPrefix:@"CURPOS"]) + { + NSString *movieURL = [NSString stringWithFormat:@"rtmp://rtmp.ubixonline.com/%@", [packetParts objectAtIndex:3]]; + + MPMoviePlayerController *player = [[MPMoviePlayerController alloc] initWithContentURL:[NSURL URLWithString:[movieURL stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]]; + [player play]; + } + else if([packet hasPrefix:@"MSG"]) + { + if([[packetParts objectAtIndex:2] isEqualToString:tChannel]) + { + + [[NSNotificationCenter defaultCenter] postNotificationName:@"TUMSG" + object:self + userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[packetParts objectAtIndex:1],@"sender", [packetParts objectAtIndex:2], @"channel", [packetParts objectAtIndex:3], @"message", NULL]]; + } + } + } + } +} +- (void)sendVersion +{ + NSLog(@"Sending version"); + protocolStage = TUWaitingForVersionHandshake; + [channelConnection writeData:[@"CLIENT 3.0\n" dataUsingEncoding:NSISOLatin1StringEncoding]]; + +} +- (void)sendIdent +{ + NSLog(@"Sending ident"); + NSString *ident = @"2151367332198"; + protocolStage = TUWaitingForIdentResponse; + [channelConnection writeData:[[NSString stringWithFormat:@"IDENT %@:%@\n", login, ident] dataUsingEncoding:NSUTF8StringEncoding]]; + +} +- (void)joinChannel:(NSString *)channel +{ + NSLog(@"joining channel %@", channel); + protocolStage = TUWaitingForChannelJoinResponse; + [channelConnection writeData:[[NSString stringWithFormat:@"JOIN %@\n", channel] dataUsingEncoding:NSUTF8StringEncoding]]; +} +- (void)login +{ + NSLog(@"logging in"); + + protocolStage = TUIde; + [channelConnection writeData:[[NSString stringWithFormat:@"MODE tuvebot:LOGIN %@ %@\n", login, password] dataUsingEncoding:NSUTF8StringEncoding]]; +} +- (void)pong +{ + [channelConnection writeData:[[NSString stringWithFormat:@"PONG!\n"] dataUsingEncoding:NSUTF8StringEncoding]]; +} +- (void)sendChat:(NSString *)chat toChannel:(NSString *)channel +{ + [channelConnection writeData:[[NSString stringWithFormat:@"MSG %@:%@\n", channel, chat] dataUsingEncoding:NSUTF8StringEncoding]]; + [[NSNotificationCenter defaultCenter] postNotificationName:@"TUMSG" + object:self + userInfo:[NSDictionary dictionaryWithObjectsAndKeys:login,@"sender", channel, @"channel", chat, @"message", NULL]]; +} +/* + + + + + */ +@end diff --git a/Images/Default.png b/Images/Default.png new file mode 100644 index 0000000..8afa877 --- /dev/null +++ b/Images/Default.png Binary files differ diff --git a/Images/icon.png b/Images/icon.png new file mode 100644 index 0000000..c4374ea --- /dev/null +++ b/Images/icon.png Binary files differ diff --git a/Images/tuve512.png b/Images/tuve512.png new file mode 100644 index 0000000..cc7dda4 --- /dev/null +++ b/Images/tuve512.png Binary files differ diff --git a/Images/tuvelogo.xcf b/Images/tuvelogo.xcf new file mode 100644 index 0000000..e765ed9 --- /dev/null +++ b/Images/tuvelogo.xcf Binary files differ diff --git a/Info.plist b/Info.plist new file mode 100644 index 0000000..ecc5139 --- /dev/null +++ b/Info.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + icon + CFBundleIdentifier + com.keelscreek.${PRODUCT_NAME:identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + LSRequiresIPhoneOS + + NSMainNibFile + MainWindow + + diff --git a/MainWindow.xib b/MainWindow.xib new file mode 100644 index 0000000..9d146e0 --- /dev/null +++ b/MainWindow.xib @@ -0,0 +1,905 @@ + + + + 512 + 9E17 + 670 + 949.33 + 352.00 + + YES + + + + + + YES + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + YES + + IBFilesOwner + + + IBFirstResponder + + + + + 1316 + + {320, 480} + + + 1 + MSAxIDEgMAA + + NO + NO + + + + + + + 256 + {0, 0} + NO + YES + YES + 1 + + + YES + + + + VHV2w6k + + + + 292 + {{149, 341}, {18, 19}} + NO + NO + 0 + 0 + + Helvetica-Bold + 1.500000e+01 + 16 + + 3 + YES + + 1 + MSAxIDEAA + + + 1 + MC4xOTYwNzg0MyAwLjMwOTgwMzkzIDAuNTIxNTY4NjYAA + + + + + + + + + + + 274 + + YES + + + 274 + {320, 323} + + + NO + YES + NO + 1 + 0 + YES + 4.400000e+01 + 2.700000e+01 + 2.700000e+01 + + + + 266 + + YES + + + 292 + {{12, 7}, {243, 31}} + + NO + NO + 0 + + 3 + + 3 + MAA + + 2 + + + YES + YES + 1.700000e+01 + + + + {{0, 323}, {320, 44}} + + NO + NO + 1 + + YES + + + + + + Send + 2 + + + + + + {320, 367} + + + 3 + MQA + + + NO + + + Chat + + + + + + YES + + + + 274 + + YES + + + -2147483356 + {{20, 338}, {280, 9}} + + NO + YES + YES + 5.000000e-01 + + + {320, 367} + + 3 + MAA + + + NO + + + Now Playing + + + + + + + + + 274 + {320, 367} + + NO + YES + NO + 1 + 0 + YES + 4.400000e+01 + 2.700000e+01 + 2.700000e+01 + + + Playlist + + + + + + + + + 274 + {320, 367} + + 3 + MQA + + + NO + + + Channel + + + + + + + + + 266 + {{129, 330}, {163, 49}} + + 3 + MCAwAA + + NO + + + + + + + + 274 + + YES + + + 290 + {320, 44} + + NO + NO + 1 + + YES + + + Settings + + 2 + 3 + + + + + + + 292 + {{20, 59}, {280, 31}} + + NO + NO + 0 + + 3 + + 3 + MAA + + + YES + YES + 1.700000e+01 + + + + + 292 + {{20, 98}, {280, 31}} + + NO + NO + 0 + + 3 + + 3 + MAA + + + YES + YES + 1.700000e+01 + + + + {320, 460} + + + 3 + MQA + + + NO + + + + + + + YES + + + delegate + + + + 4 + + + + window + + + + 5 + + + + nav + + + + 45 + + + + flipside + + + + 54 + + + + toggleView + + + 7 + + 55 + + + + toggleView + + + + 59 + + + + sendChat + + + + 63 + + + + chatBox + + + + 64 + + + + delegate + + + + 65 + + + + tableView + + + + 66 + + + + dataSource + + + + 68 + + + + delegate + + + + 69 + + + + + YES + + 0 + + YES + + + + + + 2 + + + YES + + + + + -1 + + + RmlsZSdzIE93bmVyA + + + 3 + + + + + -2 + + + + + 10 + + + YES + + + + + + + 12 + + + + + 14 + + + YES + + + + + + + + + + + 15 + + + + + 16 + + + YES + + + + + + + 17 + + + YES + + + + + + + 18 + + + + + 19 + + + + + 21 + + + YES + + + + + + + 22 + + + + + 23 + + + YES + + + + + + + 24 + + + + + 27 + + + YES + + + + + + + 28 + + + + + 29 + + + YES + + + + + + + 30 + + + + + 32 + + + YES + + + + + + 31 + + + + + 33 + + + + + 36 + + + YES + + + + + 25 + + + YES + + + + + + 26 + + + + + 20 + + + YES + + + + + + 51 + + + YES + + + + + + 50 + + + + + 52 + + + YES + + + + + + 53 + + + YES + + + + + + + + 56 + + + YES + + + + + + 57 + + + YES + + + + + + 58 + + + + + 60 + + + + + 61 + + + + + + + YES + + YES + -1.CustomClassName + -2.CustomClassName + 10.IBEditorWindowLastContentRect + 10.IBPluginDependency + 12.IBPluginDependency + 14.IBPluginDependency + 15.IBPluginDependency + 16.IBPluginDependency + 17.CustomClassName + 17.IBPluginDependency + 18.IBPluginDependency + 19.IBPluginDependency + 2.IBAttributePlaceholdersKey + 2.IBEditorWindowLastContentRect + 2.IBPluginDependency + 21.IBPluginDependency + 23.IBPluginDependency + 25.IBPluginDependency + 26.IBPluginDependency + 27.IBPluginDependency + 28.IBPluginDependency + 29.IBPluginDependency + 3.CustomClassName + 3.IBPluginDependency + 30.IBPluginDependency + 31.IBPluginDependency + 33.IBPluginDependency + 36.IBPluginDependency + 50.IBPluginDependency + 52.IBEditorWindowLastContentRect + 52.IBPluginDependency + 53.IBPluginDependency + 56.IBPluginDependency + 57.IBPluginDependency + 58.IBPluginDependency + 60.IBPluginDependency + 61.IBPluginDependency + + + YES + UIApplication + UIResponder + {{0, 665}, {320, 480}} + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + ChatController + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + YES + + YES + + + YES + + + {{440, 320}, {320, 480}} + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + TuveAppDelegate + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + {{353, 581}, {320, 480}} + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + YES + + YES + + + YES + + + + + YES + + YES + + + YES + + + + 69 + + + + YES + + ChatController + UIViewController + + tableView + UITableView + + + IBProjectSource + Classes/ChatController.h + + + + TuveAppDelegate + NSObject + + YES + + YES + sendChat + toggleView + + + YES + id + id + + + + YES + + YES + chatBox + flipside + nav + window + + + YES + UITextField + UIViewController + UINavigationController + UIWindow + + + + IBProjectSource + Classes/TuveAppDelegate.h + + + + TuveAppDelegate + NSObject + + viewController + id + + + IBUserSource + + + + + + 0 + Tuve.xcodeproj + 3 + + diff --git a/Tuve.xcodeproj/phausler.mode1v3 b/Tuve.xcodeproj/phausler.mode1v3 new file mode 100644 index 0000000..506275b --- /dev/null +++ b/Tuve.xcodeproj/phausler.mode1v3 @@ -0,0 +1,1457 @@ + + + + + ActivePerspectiveName + Project + AllowedModules + + + BundleLoadPath + + MaxInstances + n + Module + PBXSmartGroupTreeModule + Name + Groups and Files Outline View + + + BundleLoadPath + + MaxInstances + n + Module + PBXNavigatorGroup + Name + Editor + + + BundleLoadPath + + MaxInstances + n + Module + XCTaskListModule + Name + Task List + + + BundleLoadPath + + MaxInstances + n + Module + XCDetailModule + Name + File and Smart Group Detail Viewer + + + BundleLoadPath + + MaxInstances + 1 + Module + PBXBuildResultsModule + Name + Detailed Build Results Viewer + + + BundleLoadPath + + MaxInstances + 1 + Module + PBXProjectFindModule + Name + Project Batch Find Tool + + + BundleLoadPath + + MaxInstances + n + Module + XCProjectFormatConflictsModule + Name + Project Format Conflicts List + + + BundleLoadPath + + MaxInstances + n + Module + PBXBookmarksModule + Name + Bookmarks Tool + + + BundleLoadPath + + MaxInstances + n + Module + PBXClassBrowserModule + Name + Class Browser + + + BundleLoadPath + + MaxInstances + n + Module + PBXCVSModule + Name + Source Code Control Tool + + + BundleLoadPath + + MaxInstances + n + Module + PBXDebugBreakpointsModule + Name + Debug Breakpoints Tool + + + BundleLoadPath + + MaxInstances + n + Module + XCDockableInspector + Name + Inspector + + + BundleLoadPath + + MaxInstances + n + Module + PBXOpenQuicklyModule + Name + Open Quickly Tool + + + BundleLoadPath + + MaxInstances + 1 + Module + PBXDebugSessionModule + Name + Debugger + + + BundleLoadPath + + MaxInstances + 1 + Module + PBXDebugCLIModule + Name + Debug Console + + + BundleLoadPath + + MaxInstances + n + Module + XCSnapshotModule + Name + Snapshots Tool + + + BundlePath + /Developer/Library/PrivateFrameworks/DevToolsInterface.framework/Resources + Description + DefaultDescriptionKey + DockingSystemVisible + + Extension + mode1v3 + FavBarConfig + + PBXProjectModuleGUID + 5B6BDD070E74EC480008C579 + XCBarModuleItemNames + + XCBarModuleItems + + + FirstTimeWindowDisplayed + + Identifier + com.apple.perspectives.project.mode1v3 + MajorVersion + 33 + MinorVersion + 0 + Name + Default + Notifications + + OpenEditors + + PerspectiveWidths + + -1 + -1 + + Perspectives + + + ChosenToolbarItems + + active-platform-popup + active-buildstyle-popup + active-target-popup + action + clean-target + buildOrClean + go + build-and-goOrGo + com.apple.ide.PBXToolbarStopButton + get-info + show-inspector + toggle-editor + servicesModuledebug + servicesModuleclasses + Quick Model + servicesModulebreakpoints + servicesModuleCVS + NSToolbarFlexibleSpaceItem + com.apple.pbx.toolbar.searchfield + + ControllerClassBaseName + + IconName + WindowOfProjectWithEditor + Identifier + perspective.project + IsVertical + + Layout + + + BecomeActive + + ContentConfiguration + + PBXBottomSmartGroupGIDs + + 1C37FBAC04509CD000000102 + 1C37FAAC04509CD000000102 + 1C08E77C0454961000C914BD + 1C37FABC05509CD000000102 + 1C37FABC05539CD112110102 + E2644B35053B69B200211256 + 1C37FABC04509CD000100104 + 1CC0EA4004350EF90044410B + 1CC0EA4004350EF90041110B + + PBXProjectModuleGUID + 1CE0B1FE06471DED0097A5F4 + PBXProjectModuleLabel + Files + PBXProjectStructureProvided + yes + PBXSmartGroupTreeModuleColumnData + + PBXSmartGroupTreeModuleColumnWidthsKey + + 319 + + PBXSmartGroupTreeModuleColumnsKey_v4 + + MainColumn + + + PBXSmartGroupTreeModuleOutlineStateKey_v7 + + PBXSmartGroupTreeModuleOutlineStateExpansionKey + + 29B97314FDCFA39411CA2CEA + 5B891F220E75951600933BE0 + 29B97315FDCFA39411CA2CEA + 29B97317FDCFA39411CA2CEA + 19C28FACFE9D520D11CA2CBB + 1C37FABC05509CD000000102 + 1CC0EA4004350EF90041110B + + PBXSmartGroupTreeModuleOutlineStateSelectionKey + + + 29 + 25 + 0 + + + PBXSmartGroupTreeModuleOutlineStateVisibleRectKey + {{0, 0}, {319, 808}} + + PBXTopSmartGroupGIDs + + XCIncludePerspectivesSwitch + + XCSharingToken + com.apple.Xcode.GFSharingToken + + GeometryConfiguration + + Frame + {{0, 0}, {336, 826}} + GroupTreeTableConfiguration + + MainColumn + 319 + + RubberWindowFrame + 70 311 1450 867 0 0 1920 1178 + + Module + PBXSmartGroupTreeModule + Proportion + 336pt + + + Dock + + + ContentConfiguration + + PBXProjectModuleGUID + 1CE0B20306471E060097A5F4 + PBXProjectModuleLabel + vlm.c + PBXSplitModuleInNavigatorKey + + Split0 + + PBXProjectModuleGUID + 1CE0B20406471E060097A5F4 + PBXProjectModuleLabel + vlm.c + history + + 5B6BDD3A0E74F3A70008C579 + 5B6BDD4E0E74F3D30008C579 + 5B6BDDD10E74FC420008C579 + 5B6BDED70E754FFC0008C579 + 5B6BDED90E754FFC0008C579 + 5B6BDEF20E7551420008C579 + 5B6BDEFE0E7551CE0008C579 + 5B6BDF140E7556910008C579 + 5B6BDF470E756A950008C579 + 5B6BDFAB0E756D860008C579 + 5B891DF30E75706300933BE0 + 5B891DF50E75706300933BE0 + 5B891E060E75737500933BE0 + 5B891E210E757D0900933BE0 + 5B891EA90E758A9800933BE0 + 5B891EAB0E758A9800933BE0 + 5B891EAD0E758A9800933BE0 + 5B891EE00E758BAD00933BE0 + 5B891EE10E758BAD00933BE0 + 5B891EE30E758BAD00933BE0 + 5B891EE40E758BAD00933BE0 + 5B891EE60E758BAD00933BE0 + 5B891F0B0E758C7600933BE0 + 5B891F240E75952600933BE0 + 5B891F250E75952600933BE0 + 5B891FB50E75981200933BE0 + 5B891FD70E759A8200933BE0 + 5B8920190E763B5A00933BE0 + 5B8921C10E76A6D000933BE0 + 5B8923BA0E76C1F400933BE0 + 5B8924070E76C71100933BE0 + 5B8924080E76C71100933BE0 + + prevStack + + 5B6BDD150E74EFA40008C579 + 5B6BDD160E74EFA40008C579 + 5B6BDD320E74F2270008C579 + 5B6BDD330E74F2270008C579 + 5B6BDD470E74F3A70008C579 + 5B6BDD690E74F8D90008C579 + 5B6BDD6E0E74F8D90008C579 + 5B6BDD8B0E74FA070008C579 + 5B6BDEB60E754E7C0008C579 + 5B6BDEDF0E754FFC0008C579 + 5B6BDEE10E754FFC0008C579 + 5B6BDF1A0E7556910008C579 + 5B6BDF4F0E756A950008C579 + 5B6BDF510E756A950008C579 + 5B6BDF530E756A950008C579 + 5B6BDF550E756A950008C579 + 5B891E5F0E757EA300933BE0 + 5B891EB40E758A9800933BE0 + 5B891EB50E758A9800933BE0 + 5B891EC30E758A9800933BE0 + 5B891EEE0E758BAD00933BE0 + 5B891EF20E758BAD00933BE0 + 5B891F100E758C7600933BE0 + 5B891FD90E759A8200933BE0 + 5B8920210E763B5A00933BE0 + 5B8921C60E76A6D000933BE0 + 5B8923C30E76C1F400933BE0 + 5B89240F0E76C71100933BE0 + + + SplitCount + 1 + + StatusBarVisibility + + + GeometryConfiguration + + Frame + {{0, 0}, {1109, 648}} + RubberWindowFrame + 70 311 1450 867 0 0 1920 1178 + + Module + PBXNavigatorGroup + Proportion + 648pt + + + ContentConfiguration + + PBXProjectModuleGUID + 1CE0B20506471E060097A5F4 + PBXProjectModuleLabel + Detail + + GeometryConfiguration + + Frame + {{0, 653}, {1109, 173}} + RubberWindowFrame + 70 311 1450 867 0 0 1920 1178 + + Module + XCDetailModule + Proportion + 173pt + + + Proportion + 1109pt + + + Name + Project + ServiceClasses + + XCModuleDock + PBXSmartGroupTreeModule + XCModuleDock + PBXNavigatorGroup + XCDetailModule + + TableOfContents + + 5B1A3F4C0E76DEC90034C8F3 + 1CE0B1FE06471DED0097A5F4 + 5B1A3F4D0E76DEC90034C8F3 + 1CE0B20306471E060097A5F4 + 1CE0B20506471E060097A5F4 + + ToolbarConfiguration + xcode.toolbar.config.defaultV3 + + + ControllerClassBaseName + + IconName + WindowOfProject + Identifier + perspective.morph + IsVertical + 0 + Layout + + + BecomeActive + 1 + ContentConfiguration + + PBXBottomSmartGroupGIDs + + 1C37FBAC04509CD000000102 + 1C37FAAC04509CD000000102 + 1C08E77C0454961000C914BD + 1C37FABC05509CD000000102 + 1C37FABC05539CD112110102 + E2644B35053B69B200211256 + 1C37FABC04509CD000100104 + 1CC0EA4004350EF90044410B + 1CC0EA4004350EF90041110B + + PBXProjectModuleGUID + 11E0B1FE06471DED0097A5F4 + PBXProjectModuleLabel + Files + PBXProjectStructureProvided + yes + PBXSmartGroupTreeModuleColumnData + + PBXSmartGroupTreeModuleColumnWidthsKey + + 186 + + PBXSmartGroupTreeModuleColumnsKey_v4 + + MainColumn + + + PBXSmartGroupTreeModuleOutlineStateKey_v7 + + PBXSmartGroupTreeModuleOutlineStateExpansionKey + + 29B97314FDCFA39411CA2CEA + 1C37FABC05509CD000000102 + + PBXSmartGroupTreeModuleOutlineStateSelectionKey + + + 0 + + + PBXSmartGroupTreeModuleOutlineStateVisibleRectKey + {{0, 0}, {186, 337}} + + PBXTopSmartGroupGIDs + + XCIncludePerspectivesSwitch + 1 + XCSharingToken + com.apple.Xcode.GFSharingToken + + GeometryConfiguration + + Frame + {{0, 0}, {203, 355}} + GroupTreeTableConfiguration + + MainColumn + 186 + + RubberWindowFrame + 373 269 690 397 0 0 1440 878 + + Module + PBXSmartGroupTreeModule + Proportion + 100% + + + Name + Morph + PreferredWidth + 300 + ServiceClasses + + XCModuleDock + PBXSmartGroupTreeModule + + TableOfContents + + 11E0B1FE06471DED0097A5F4 + + ToolbarConfiguration + xcode.toolbar.config.default.shortV3 + + + PerspectivesBarVisible + + ShelfIsVisible + + SourceDescription + file at '/Developer/Library/PrivateFrameworks/DevToolsInterface.framework/Resources/XCPerspectivesSpecificationMode1.xcperspec' + StatusbarIsVisible + + TimeStamp + 0.0 + ToolbarDisplayMode + 1 + ToolbarIsVisible + + ToolbarSizeMode + 1 + Type + Perspectives + UpdateMessage + The Default Workspace in this version of Xcode now includes support to hide and show the detail view (what has been referred to as the "Metro-Morph" feature). You must discard your current Default Workspace settings and update to the latest Default Workspace in order to gain this feature. Do you wish to update to the latest Workspace defaults for project '%@'? + WindowJustification + 5 + WindowOrderList + + 5B6BDD180E74EFA40008C579 + /Users/phausler/Documents/Projects/Tuve-iPhone/Tuve.xcodeproj + + WindowString + 70 311 1450 867 0 0 1920 1178 + WindowToolsV3 + + + FirstTimeWindowDisplayed + + Identifier + windowTool.build + IsVertical + + Layout + + + Dock + + + ContentConfiguration + + PBXProjectModuleGUID + 1CD0528F0623707200166675 + PBXProjectModuleLabel + + StatusBarVisibility + + + GeometryConfiguration + + Frame + {{0, 0}, {1602, 210}} + RubberWindowFrame + 362 311 1602 867 0 0 1920 1178 + + Module + PBXNavigatorGroup + Proportion + 210pt + + + BecomeActive + + ContentConfiguration + + PBXBuildLogShowsTranscriptDefaultKey + {{0, 476}, {1602, 135}} + PBXProjectModuleGUID + XCMainBuildResultsModuleGUID + PBXProjectModuleLabel + Build + XCBuildResultsTrigger_Collapse + 1021 + XCBuildResultsTrigger_Open + 1011 + + GeometryConfiguration + + Frame + {{0, 215}, {1602, 611}} + RubberWindowFrame + 362 311 1602 867 0 0 1920 1178 + + Module + PBXBuildResultsModule + Proportion + 611pt + + + Proportion + 826pt + + + Name + Build Results + ServiceClasses + + PBXBuildResultsModule + + StatusbarIsVisible + + TableOfContents + + 5B6BDD180E74EFA40008C579 + 5B1A3F510E76DEFD0034C8F3 + 1CD0528F0623707200166675 + XCMainBuildResultsModuleGUID + + ToolbarConfiguration + xcode.toolbar.config.buildV3 + WindowString + 362 311 1602 867 0 0 1920 1178 + WindowToolGUID + 5B6BDD180E74EFA40008C579 + WindowToolIsVisible + + + + FirstTimeWindowDisplayed + + Identifier + windowTool.debugger + IsVertical + + Layout + + + Dock + + + ContentConfiguration + + Debugger + + HorizontalSplitView + + _collapsingFrameDimension + 0.0 + _indexOfCollapsedView + 0 + _percentageOfCollapsedView + 0.0 + isCollapsed + yes + sizes + + {{0, 0}, {239, 236}} + {{239, 0}, {668, 236}} + + + VerticalSplitView + + _collapsingFrameDimension + 0.0 + _indexOfCollapsedView + 0 + _percentageOfCollapsedView + 0.0 + isCollapsed + yes + sizes + + {{0, 0}, {907, 236}} + {{0, 236}, {907, 209}} + + + + LauncherConfigVersion + 8 + PBXProjectModuleGUID + 1C162984064C10D400B95A72 + PBXProjectModuleLabel + Debug - GLUTExamples (Underwater) + + GeometryConfiguration + + DebugConsoleVisible + None + DebugConsoleWindowFrame + {{200, 200}, {500, 300}} + DebugSTDIOWindowFrame + {{200, 200}, {500, 300}} + Frame + {{0, 0}, {907, 445}} + PBXDebugSessionStackFrameViewKey + + DebugVariablesTableConfiguration + + Name + 120 + Value + 85 + Summary + 438 + + Frame + {{239, 0}, {668, 236}} + RubberWindowFrame + 935 432 907 486 0 0 1920 1178 + + RubberWindowFrame + 935 432 907 486 0 0 1920 1178 + + Module + PBXDebugSessionModule + Proportion + 445pt + + + Proportion + 445pt + + + Name + Debugger + ServiceClasses + + PBXDebugSessionModule + + StatusbarIsVisible + + TableOfContents + + 1CD10A99069EF8BA00B06720 + 5B891DDB0E756E5200933BE0 + 1C162984064C10D400B95A72 + 5B891DDC0E756E5200933BE0 + 5B891DDD0E756E5200933BE0 + 5B891DDE0E756E5200933BE0 + 5B891DDF0E756E5200933BE0 + 5B891DE00E756E5200933BE0 + + ToolbarConfiguration + xcode.toolbar.config.debugV3 + WindowString + 935 432 907 486 0 0 1920 1178 + WindowToolGUID + 1CD10A99069EF8BA00B06720 + WindowToolIsVisible + + + + FirstTimeWindowDisplayed + + Identifier + windowTool.find + IsVertical + + Layout + + + Dock + + + Dock + + + BecomeActive + + ContentConfiguration + + PBXProjectModuleGUID + 1CDD528C0622207200134675 + PBXProjectModuleLabel + libvlc-module.c + StatusBarVisibility + + + GeometryConfiguration + + Frame + {{0, 0}, {838, 762}} + RubberWindowFrame + 708 77 838 1020 0 0 1920 1178 + + Module + PBXNavigatorGroup + Proportion + 838pt + + + Proportion + 762pt + + + ContentConfiguration + + PBXProjectModuleGUID + 1CD0528E0623707200166675 + PBXProjectModuleLabel + Project Find + + GeometryConfiguration + + Frame + {{0, 767}, {838, 212}} + RubberWindowFrame + 708 77 838 1020 0 0 1920 1178 + + Module + PBXProjectFindModule + Proportion + 212pt + + + Proportion + 979pt + + + Name + Project Find + ServiceClasses + + PBXProjectFindModule + + StatusbarIsVisible + + TableOfContents + + 1C530D57069F1CE1000CFCEE + 5B891FAB0E7597E300933BE0 + 5B891FAC0E7597E300933BE0 + 1CDD528C0622207200134675 + 1CD0528E0623707200166675 + + WindowString + 708 77 838 1020 0 0 1920 1178 + WindowToolGUID + 1C530D57069F1CE1000CFCEE + WindowToolIsVisible + + + + Identifier + MENUSEPARATOR + + + FirstTimeWindowDisplayed + + Identifier + windowTool.debuggerConsole + IsVertical + + Layout + + + Dock + + + BecomeActive + + ContentConfiguration + + PBXProjectModuleGUID + 1C78EAAC065D492600B07095 + PBXProjectModuleLabel + Debugger Console + + GeometryConfiguration + + Frame + {{0, 0}, {1008, 528}} + RubberWindowFrame + 61 559 1008 569 0 0 1920 1178 + + Module + PBXDebugCLIModule + Proportion + 528pt + + + Proportion + 528pt + + + Name + Debugger Console + ServiceClasses + + PBXDebugCLIModule + + StatusbarIsVisible + + TableOfContents + + 1C78EAAD065D492600B07095 + 5B891DE10E756E5200933BE0 + 1C78EAAC065D492600B07095 + + ToolbarConfiguration + xcode.toolbar.config.consoleV3 + WindowString + 61 559 1008 569 0 0 1920 1178 + WindowToolGUID + 1C78EAAD065D492600B07095 + WindowToolIsVisible + + + + Identifier + windowTool.snapshots + Layout + + + Dock + + + Module + XCSnapshotModule + Proportion + 100% + + + Proportion + 100% + + + Name + Snapshots + ServiceClasses + + XCSnapshotModule + + StatusbarIsVisible + Yes + ToolbarConfiguration + xcode.toolbar.config.snapshots + WindowString + 315 824 300 550 0 0 1440 878 + WindowToolIsVisible + Yes + + + Identifier + windowTool.scm + Layout + + + Dock + + + ContentConfiguration + + PBXProjectModuleGUID + 1C78EAB2065D492600B07095 + PBXProjectModuleLabel + <No Editor> + PBXSplitModuleInNavigatorKey + + Split0 + + PBXProjectModuleGUID + 1C78EAB3065D492600B07095 + + SplitCount + 1 + + StatusBarVisibility + 1 + + GeometryConfiguration + + Frame + {{0, 0}, {452, 0}} + RubberWindowFrame + 743 379 452 308 0 0 1280 1002 + + Module + PBXNavigatorGroup + Proportion + 0pt + + + BecomeActive + 1 + ContentConfiguration + + PBXProjectModuleGUID + 1CD052920623707200166675 + PBXProjectModuleLabel + SCM + + GeometryConfiguration + + ConsoleFrame + {{0, 259}, {452, 0}} + Frame + {{0, 7}, {452, 259}} + RubberWindowFrame + 743 379 452 308 0 0 1280 1002 + TableConfiguration + + Status + 30 + FileName + 199 + Path + 197.0950012207031 + + TableFrame + {{0, 0}, {452, 250}} + + Module + PBXCVSModule + Proportion + 262pt + + + Proportion + 266pt + + + Name + SCM + ServiceClasses + + PBXCVSModule + + StatusbarIsVisible + 1 + TableOfContents + + 1C78EAB4065D492600B07095 + 1C78EAB5065D492600B07095 + 1C78EAB2065D492600B07095 + 1CD052920623707200166675 + + ToolbarConfiguration + xcode.toolbar.config.scm + WindowString + 743 379 452 308 0 0 1280 1002 + + + Identifier + windowTool.breakpoints + IsVertical + 0 + Layout + + + Dock + + + BecomeActive + 1 + ContentConfiguration + + PBXBottomSmartGroupGIDs + + 1C77FABC04509CD000000102 + + PBXProjectModuleGUID + 1CE0B1FE06471DED0097A5F4 + PBXProjectModuleLabel + Files + PBXProjectStructureProvided + no + PBXSmartGroupTreeModuleColumnData + + PBXSmartGroupTreeModuleColumnWidthsKey + + 168 + + PBXSmartGroupTreeModuleColumnsKey_v4 + + MainColumn + + + PBXSmartGroupTreeModuleOutlineStateKey_v7 + + PBXSmartGroupTreeModuleOutlineStateExpansionKey + + 1C77FABC04509CD000000102 + + PBXSmartGroupTreeModuleOutlineStateSelectionKey + + + 0 + + + PBXSmartGroupTreeModuleOutlineStateVisibleRectKey + {{0, 0}, {168, 350}} + + PBXTopSmartGroupGIDs + + XCIncludePerspectivesSwitch + 0 + + GeometryConfiguration + + Frame + {{0, 0}, {185, 368}} + GroupTreeTableConfiguration + + MainColumn + 168 + + RubberWindowFrame + 315 424 744 409 0 0 1440 878 + + Module + PBXSmartGroupTreeModule + Proportion + 185pt + + + ContentConfiguration + + PBXProjectModuleGUID + 1CA1AED706398EBD00589147 + PBXProjectModuleLabel + Detail + + GeometryConfiguration + + Frame + {{190, 0}, {554, 368}} + RubberWindowFrame + 315 424 744 409 0 0 1440 878 + + Module + XCDetailModule + Proportion + 554pt + + + Proportion + 368pt + + + MajorVersion + 3 + MinorVersion + 0 + Name + Breakpoints + ServiceClasses + + PBXSmartGroupTreeModule + XCDetailModule + + StatusbarIsVisible + 1 + TableOfContents + + 1CDDB66807F98D9800BB5817 + 1CDDB66907F98D9800BB5817 + 1CE0B1FE06471DED0097A5F4 + 1CA1AED706398EBD00589147 + + ToolbarConfiguration + xcode.toolbar.config.breakpointsV3 + WindowString + 315 424 744 409 0 0 1440 878 + WindowToolGUID + 1CDDB66807F98D9800BB5817 + WindowToolIsVisible + 1 + + + Identifier + windowTool.debugAnimator + Layout + + + Dock + + + Module + PBXNavigatorGroup + Proportion + 100% + + + Proportion + 100% + + + Name + Debug Visualizer + ServiceClasses + + PBXNavigatorGroup + + StatusbarIsVisible + 1 + ToolbarConfiguration + xcode.toolbar.config.debugAnimatorV3 + WindowString + 100 100 700 500 0 0 1280 1002 + + + Identifier + windowTool.bookmarks + Layout + + + Dock + + + Module + PBXBookmarksModule + Proportion + 100% + + + Proportion + 100% + + + Name + Bookmarks + ServiceClasses + + PBXBookmarksModule + + StatusbarIsVisible + 0 + WindowString + 538 42 401 187 0 0 1280 1002 + + + Identifier + windowTool.projectFormatConflicts + Layout + + + Dock + + + Module + XCProjectFormatConflictsModule + Proportion + 100% + + + Proportion + 100% + + + Name + Project Format Conflicts + ServiceClasses + + XCProjectFormatConflictsModule + + StatusbarIsVisible + 0 + WindowContentMinSize + 450 300 + WindowString + 50 850 472 307 0 0 1440 877 + + + Identifier + windowTool.classBrowser + Layout + + + Dock + + + BecomeActive + 1 + ContentConfiguration + + OptionsSetName + Hierarchy, all classes + PBXProjectModuleGUID + 1CA6456E063B45B4001379D8 + PBXProjectModuleLabel + Class Browser - NSObject + + GeometryConfiguration + + ClassesFrame + {{0, 0}, {374, 96}} + ClassesTreeTableConfiguration + + PBXClassNameColumnIdentifier + 208 + PBXClassBookColumnIdentifier + 22 + + Frame + {{0, 0}, {630, 331}} + MembersFrame + {{0, 105}, {374, 395}} + MembersTreeTableConfiguration + + PBXMemberTypeIconColumnIdentifier + 22 + PBXMemberNameColumnIdentifier + 216 + PBXMemberTypeColumnIdentifier + 97 + PBXMemberBookColumnIdentifier + 22 + + PBXModuleWindowStatusBarHidden2 + 1 + RubberWindowFrame + 385 179 630 352 0 0 1440 878 + + Module + PBXClassBrowserModule + Proportion + 332pt + + + Proportion + 332pt + + + Name + Class Browser + ServiceClasses + + PBXClassBrowserModule + + StatusbarIsVisible + 0 + TableOfContents + + 1C0AD2AF069F1E9B00FABCE6 + 1C0AD2B0069F1E9B00FABCE6 + 1CA6456E063B45B4001379D8 + + ToolbarConfiguration + xcode.toolbar.config.classbrowser + WindowString + 385 179 630 352 0 0 1440 878 + WindowToolGUID + 1C0AD2AF069F1E9B00FABCE6 + WindowToolIsVisible + 0 + + + Identifier + windowTool.refactoring + IncludeInToolsMenu + 0 + Layout + + + Dock + + + BecomeActive + 1 + GeometryConfiguration + + Frame + {0, 0}, {500, 335} + RubberWindowFrame + {0, 0}, {500, 335} + + Module + XCRefactoringModule + Proportion + 100% + + + Proportion + 100% + + + Name + Refactoring + ServiceClasses + + XCRefactoringModule + + WindowString + 200 200 500 356 0 0 1920 1200 + + + + diff --git a/Tuve.xcodeproj/phausler.pbxuser b/Tuve.xcodeproj/phausler.pbxuser new file mode 100644 index 0000000..75a6446 --- /dev/null +++ b/Tuve.xcodeproj/phausler.pbxuser @@ -0,0 +1,1951 @@ +// !$*UTF8*$! +{ + 1D3623240D0F684500981E51 /* TuveAppDelegate.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {936, 553}}"; + sepNavSelRange = "{409, 7}"; + sepNavVisRange = "{0, 565}"; + }; + }; + 1D3623250D0F684500981E51 /* TuveAppDelegate.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1181, 1218}}"; + sepNavSelRange = "{321, 0}"; + sepNavVisRange = "{25, 1255}"; + }; + }; + 1D6058900D05DD3D006BFB54 /* Tuvé */ = { + activeExec = 0; + executables = ( + 5B6BDCFE0E74EC450008C579 /* Tuvé */, + ); + }; + 29B97313FDCFA39411CA2CEA /* Project object */ = { + activeBuildConfigurationName = Debug; + activeExecutable = 5B6BDCFE0E74EC450008C579 /* Tuvé */; + activeSDKPreference = iphonesimulator2.0; + activeTarget = 1D6058900D05DD3D006BFB54 /* Tuvé */; + addToTargets = ( + 1D6058900D05DD3D006BFB54 /* Tuvé */, + ); + breakpoints = ( + 5B891EDD0E758B8800933BE0 /* TuveAppDelegate.m:17 */, + ); + codeSenseManager = 5B6BDD090E74EC480008C579 /* Code sense */; + executables = ( + 5B6BDCFE0E74EC450008C579 /* Tuvé */, + ); + perUserDictionary = { + PBXConfiguration.PBXFileTableDataSource3.PBXFileTableDataSource = { + PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; + PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID; + PBXFileTableDataSourceColumnWidthsKey = ( + 20, + 870, + 20, + 48, + 43, + 43, + 20, + ); + PBXFileTableDataSourceColumnsKey = ( + PBXFileDataSource_FiletypeID, + PBXFileDataSource_Filename_ColumnID, + PBXFileDataSource_Built_ColumnID, + PBXFileDataSource_ObjectSize_ColumnID, + PBXFileDataSource_Errors_ColumnID, + PBXFileDataSource_Warnings_ColumnID, + PBXFileDataSource_Target_ColumnID, + ); + }; + PBXConfiguration.PBXTargetDataSource.PBXTargetDataSource = { + PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; + PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID; + PBXFileTableDataSourceColumnWidthsKey = ( + 20, + 830, + 60, + 20, + 48, + 43, + 43, + ); + PBXFileTableDataSourceColumnsKey = ( + PBXFileDataSource_FiletypeID, + PBXFileDataSource_Filename_ColumnID, + PBXTargetDataSource_PrimaryAttribute, + PBXFileDataSource_Built_ColumnID, + PBXFileDataSource_ObjectSize_ColumnID, + PBXFileDataSource_Errors_ColumnID, + PBXFileDataSource_Warnings_ColumnID, + ); + }; + PBXPerProjectTemplateStateSaveDate = 242671301; + PBXWorkspaceStateSaveDate = 242671301; + }; + perUserProjectItems = { + 5B6BDD150E74EFA40008C579 /* PBXTextBookmark */ = 5B6BDD150E74EFA40008C579 /* PBXTextBookmark */; + 5B6BDD160E74EFA40008C579 /* PBXTextBookmark */ = 5B6BDD160E74EFA40008C579 /* PBXTextBookmark */; + 5B6BDD320E74F2270008C579 /* PBXTextBookmark */ = 5B6BDD320E74F2270008C579 /* PBXTextBookmark */; + 5B6BDD330E74F2270008C579 /* PBXTextBookmark */ = 5B6BDD330E74F2270008C579 /* PBXTextBookmark */; + 5B6BDD3A0E74F3A70008C579 /* PBXTextBookmark */ = 5B6BDD3A0E74F3A70008C579 /* PBXTextBookmark */; + 5B6BDD470E74F3A70008C579 /* PBXTextBookmark */ = 5B6BDD470E74F3A70008C579 /* PBXTextBookmark */; + 5B6BDD4E0E74F3D30008C579 /* PBXTextBookmark */ = 5B6BDD4E0E74F3D30008C579 /* PBXTextBookmark */; + 5B6BDD690E74F8D90008C579 /* PBXTextBookmark */ = 5B6BDD690E74F8D90008C579 /* PBXTextBookmark */; + 5B6BDD6E0E74F8D90008C579 /* PBXTextBookmark */ = 5B6BDD6E0E74F8D90008C579 /* PBXTextBookmark */; + 5B6BDD8B0E74FA070008C579 /* PBXTextBookmark */ = 5B6BDD8B0E74FA070008C579 /* PBXTextBookmark */; + 5B6BDDD10E74FC420008C579 /* PBXTextBookmark */ = 5B6BDDD10E74FC420008C579 /* PBXTextBookmark */; + 5B6BDEB60E754E7C0008C579 /* PBXTextBookmark */ = 5B6BDEB60E754E7C0008C579 /* PBXTextBookmark */; + 5B6BDED70E754FFC0008C579 /* PBXTextBookmark */ = 5B6BDED70E754FFC0008C579 /* PBXTextBookmark */; + 5B6BDED90E754FFC0008C579 /* PBXTextBookmark */ = 5B6BDED90E754FFC0008C579 /* PBXTextBookmark */; + 5B6BDEDF0E754FFC0008C579 /* PBXTextBookmark */ = 5B6BDEDF0E754FFC0008C579 /* PBXTextBookmark */; + 5B6BDEE10E754FFC0008C579 /* PBXTextBookmark */ = 5B6BDEE10E754FFC0008C579 /* PBXTextBookmark */; + 5B6BDEF20E7551420008C579 /* PBXTextBookmark */ = 5B6BDEF20E7551420008C579 /* PBXTextBookmark */; + 5B6BDEFE0E7551CE0008C579 /* PBXTextBookmark */ = 5B6BDEFE0E7551CE0008C579 /* PBXTextBookmark */; + 5B6BDF140E7556910008C579 /* PlistBookmark */ = 5B6BDF140E7556910008C579 /* PlistBookmark */; + 5B6BDF1A0E7556910008C579 /* PlistBookmark */ = 5B6BDF1A0E7556910008C579 /* PlistBookmark */; + 5B6BDF470E756A950008C579 /* PBXTextBookmark */ = 5B6BDF470E756A950008C579 /* PBXTextBookmark */; + 5B6BDF4F0E756A950008C579 /* PBXTextBookmark */ = 5B6BDF4F0E756A950008C579 /* PBXTextBookmark */; + 5B6BDF510E756A950008C579 /* PBXTextBookmark */ = 5B6BDF510E756A950008C579 /* PBXTextBookmark */; + 5B6BDF530E756A950008C579 /* PBXTextBookmark */ = 5B6BDF530E756A950008C579 /* PBXTextBookmark */; + 5B6BDF550E756A950008C579 /* PBXTextBookmark */ = 5B6BDF550E756A950008C579 /* PBXTextBookmark */; + 5B6BDFAB0E756D860008C579 /* PBXTextBookmark */ = 5B6BDFAB0E756D860008C579 /* PBXTextBookmark */; + 5B891DF30E75706300933BE0 /* PBXTextBookmark */ = 5B891DF30E75706300933BE0 /* PBXTextBookmark */; + 5B891DF50E75706300933BE0 /* PBXTextBookmark */ = 5B891DF50E75706300933BE0 /* PBXTextBookmark */; + 5B891E060E75737500933BE0 /* PBXTextBookmark */ = 5B891E060E75737500933BE0 /* PBXTextBookmark */; + 5B891E210E757D0900933BE0 /* PBXTextBookmark */ = 5B891E210E757D0900933BE0 /* PBXTextBookmark */; + 5B891E5F0E757EA300933BE0 /* PBXTextBookmark */ = 5B891E5F0E757EA300933BE0 /* PBXTextBookmark */; + 5B891EA90E758A9800933BE0 /* PBXTextBookmark */ = 5B891EA90E758A9800933BE0 /* PBXTextBookmark */; + 5B891EAB0E758A9800933BE0 /* PBXTextBookmark */ = 5B891EAB0E758A9800933BE0 /* PBXTextBookmark */; + 5B891EAD0E758A9800933BE0 /* PBXTextBookmark */ = 5B891EAD0E758A9800933BE0 /* PBXTextBookmark */; + 5B891EB40E758A9800933BE0 /* PBXTextBookmark */ = 5B891EB40E758A9800933BE0 /* PBXTextBookmark */; + 5B891EB50E758A9800933BE0 /* PBXTextBookmark */ = 5B891EB50E758A9800933BE0 /* PBXTextBookmark */; + 5B891EC30E758A9800933BE0 /* PBXTextBookmark */ = 5B891EC30E758A9800933BE0 /* PBXTextBookmark */; + 5B891EE00E758BAD00933BE0 /* PBXTextBookmark */ = 5B891EE00E758BAD00933BE0 /* PBXTextBookmark */; + 5B891EE10E758BAD00933BE0 /* PBXTextBookmark */ = 5B891EE10E758BAD00933BE0 /* PBXTextBookmark */; + 5B891EE30E758BAD00933BE0 /* PBXTextBookmark */ = 5B891EE30E758BAD00933BE0 /* PBXTextBookmark */; + 5B891EE40E758BAD00933BE0 /* PBXTextBookmark */ = 5B891EE40E758BAD00933BE0 /* PBXTextBookmark */; + 5B891EE60E758BAD00933BE0 /* PBXTextBookmark */ = 5B891EE60E758BAD00933BE0 /* PBXTextBookmark */; + 5B891EEE0E758BAD00933BE0 /* PBXTextBookmark */ = 5B891EEE0E758BAD00933BE0 /* PBXTextBookmark */; + 5B891EF20E758BAD00933BE0 /* PBXTextBookmark */ = 5B891EF20E758BAD00933BE0 /* PBXTextBookmark */; + 5B891F0B0E758C7600933BE0 /* PBXTextBookmark */ = 5B891F0B0E758C7600933BE0 /* PBXTextBookmark */; + 5B891F100E758C7600933BE0 /* PBXTextBookmark */ = 5B891F100E758C7600933BE0 /* PBXTextBookmark */; + 5B891F240E75952600933BE0 /* PBXTextBookmark */ = 5B891F240E75952600933BE0 /* PBXTextBookmark */; + 5B891F250E75952600933BE0 /* PBXTextBookmark */ = 5B891F250E75952600933BE0 /* PBXTextBookmark */; + 5B891FB50E75981200933BE0 /* PBXTextBookmark */ = 5B891FB50E75981200933BE0 /* PBXTextBookmark */; + 5B891FD70E759A8200933BE0 /* PBXTextBookmark */ = 5B891FD70E759A8200933BE0 /* PBXTextBookmark */; + 5B891FD90E759A8200933BE0 /* PBXTextBookmark */ = 5B891FD90E759A8200933BE0 /* PBXTextBookmark */; + 5B8920190E763B5A00933BE0 /* PBXTextBookmark */ = 5B8920190E763B5A00933BE0 /* PBXTextBookmark */; + 5B8920210E763B5A00933BE0 /* PBXTextBookmark */ = 5B8920210E763B5A00933BE0 /* PBXTextBookmark */; + 5B8921C10E76A6D000933BE0 /* PBXTextBookmark */ = 5B8921C10E76A6D000933BE0 /* PBXTextBookmark */; + 5B8921C60E76A6D000933BE0 /* PBXTextBookmark */ = 5B8921C60E76A6D000933BE0 /* PBXTextBookmark */; + 5B8923BA0E76C1F400933BE0 /* PBXTextBookmark */ = 5B8923BA0E76C1F400933BE0 /* PBXTextBookmark */; + 5B8923C30E76C1F400933BE0 /* PBXTextBookmark */ = 5B8923C30E76C1F400933BE0 /* PBXTextBookmark */; + 5B8924070E76C71100933BE0 /* PBXTextBookmark */ = 5B8924070E76C71100933BE0 /* PBXTextBookmark */; + 5B8924080E76C71100933BE0 /* PBXTextBookmark */ = 5B8924080E76C71100933BE0 /* PBXTextBookmark */; + 5B89240F0E76C71100933BE0 /* PBXTextBookmark */ = 5B89240F0E76C71100933BE0 /* PBXTextBookmark */; + }; + sourceControlManager = 5B6BDD080E74EC480008C579 /* Source Control */; + userBuildSettings = { + }; + }; + 5B6BDCFE0E74EC450008C579 /* Tuvé */ = { + isa = PBXExecutable; + activeArgIndices = ( + ); + argumentStrings = ( + ); + autoAttachOnCrash = 1; + breakpointsEnabled = 1; + configStateDict = { + }; + customDataFormattersEnabled = 1; + debuggerPlugin = GDBDebugging; + disassemblyDisplayState = 0; + dylibVariantSuffix = ""; + enableDebugStr = 1; + environmentEntries = ( + ); + executableSystemSymbolLevel = 0; + executableUserSymbolLevel = 0; + libgmallocEnabled = 0; + name = "Tuvé"; + savedGlobals = { + }; + sourceDirectories = ( + ); + variableFormatDictionary = { + }; + }; + 5B6BDD080E74EC480008C579 /* Source Control */ = { + isa = PBXSourceControlManager; + fallbackIsa = XCSourceControlManager; + isSCMEnabled = 0; + scmConfiguration = { + }; + }; + 5B6BDD090E74EC480008C579 /* Code sense */ = { + isa = PBXCodeSenseManager; + indexTemplatePath = ""; + }; + 5B6BDD150E74EFA40008C579 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 1D3623250D0F684500981E51 /* TuveAppDelegate.m */; + name = "TuveAppDelegate.m: 19"; + rLen = 0; + rLoc = 567; + rType = 0; + vrLen = 335; + vrLoc = 138; + }; + 5B6BDD160E74EFA40008C579 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 1D3623240D0F684500981E51 /* TuveAppDelegate.h */; + name = "TuveAppDelegate.h: 15"; + rLen = 0; + rLoc = 461; + rType = 0; + vrLen = 378; + vrLoc = 0; + }; + 5B6BDD2A0E74F12D0008C579 /* TCPSocket.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {936, 553}}"; + sepNavSelRange = "{353, 0}"; + sepNavVisRange = "{0, 608}"; + }; + }; + 5B6BDD2B0E74F12D0008C579 /* TCPSocket.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {936, 1036}}"; + sepNavSelRange = "{136, 0}"; + sepNavVisRange = "{0, 886}"; + }; + }; + 5B6BDD320E74F2270008C579 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B6BDD2B0E74F12D0008C579 /* TCPSocket.m */; + name = "TCPSocket.m: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 877; + vrLoc = 0; + }; + 5B6BDD330E74F2270008C579 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B6BDD2A0E74F12D0008C579 /* TCPSocket.h */; + name = "TCPSocket.h: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 425; + vrLoc = 0; + }; + 5B6BDD3A0E74F3A70008C579 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B6BDD3B0E74F3A70008C579 /* UIWindow.h */; + name = "UIWindow.h: 18"; + rLen = 50; + rLoc = 301; + rType = 0; + vrLen = 1370; + vrLoc = 188; + }; + 5B6BDD3B0E74F3A70008C579 /* UIWindow.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = UIWindow.h; + path = /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator2.0.sdk/System/Library/Frameworks/UIKit.framework/Headers/UIWindow.h; + sourceTree = ""; + }; + 5B6BDD470E74F3A70008C579 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B6BDD480E74F3A70008C579 /* UIWindow.h */; + name = "UIWindow.h: 18"; + rLen = 50; + rLoc = 301; + rType = 0; + vrLen = 1370; + vrLoc = 188; + }; + 5B6BDD480E74F3A70008C579 /* UIWindow.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = UIWindow.h; + path = /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator2.0.sdk/System/Library/Frameworks/UIKit.framework/Headers/UIWindow.h; + sourceTree = ""; + }; + 5B6BDD4E0E74F3D30008C579 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B6BDD2A0E74F12D0008C579 /* TCPSocket.h */; + name = "TCPSocket.h: 18"; + rLen = 0; + rLoc = 353; + rType = 0; + vrLen = 608; + vrLoc = 0; + }; + 5B6BDD5D0E74F64D0008C579 /* TuveConnection.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1181, 616}}"; + sepNavSelRange = "{584, 0}"; + sepNavVisRange = "{0, 860}"; + }; + }; + 5B6BDD5E0E74F64D0008C579 /* TuveConnection.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {846, 2562}}"; + sepNavSelRange = "{2784, 0}"; + sepNavVisRange = "{2130, 377}"; + }; + }; + 5B6BDD690E74F8D90008C579 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B6BDD5D0E74F64D0008C579 /* TuveConnection.h */; + name = "TuveConnection.h: 11"; + rLen = 0; + rLoc = 378; + rType = 0; + vrLen = 251; + vrLoc = 0; + }; + 5B6BDD6E0E74F8D90008C579 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B6BDD6F0E74F8D90008C579 /* NSFileHandle.h */; + name = "NSFileHandle.h: 57"; + rLen = 25; + rLoc = 1695; + rType = 0; + vrLen = 1536; + vrLoc = 703; + }; + 5B6BDD6F0E74F8D90008C579 /* NSFileHandle.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = NSFileHandle.h; + path = /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator2.0.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSFileHandle.h; + sourceTree = ""; + }; + 5B6BDD8B0E74FA070008C579 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B6BDD8C0E74FA070008C579 /* NSString.h */; + name = "NSString.h: 183"; + rLen = 17; + rLoc = 9041; + rType = 0; + vrLen = 3374; + vrLoc = 8627; + }; + 5B6BDD8C0E74FA070008C579 /* NSString.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = NSString.h; + path = /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator2.0.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSString.h; + sourceTree = ""; + }; + 5B6BDDD10E74FC420008C579 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B6BDD6F0E74F8D90008C579 /* NSFileHandle.h */; + name = "NSFileHandle.h: 66"; + rLen = 32; + rLoc = 2036; + rType = 0; + vrLen = 1299; + vrLoc = 1111; + }; + 5B6BDEB60E754E7C0008C579 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B6BDEB70E754E7C0008C579 /* UITextField.h */; + name = "UITextField.h: 163"; + rLen = 60; + rLoc = 6892; + rType = 0; + vrLen = 1911; + vrLoc = 6011; + }; + 5B6BDEB70E754E7C0008C579 /* UITextField.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = UITextField.h; + path = /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator2.0.sdk/System/Library/Frameworks/UIKit.framework/Headers/UITextField.h; + sourceTree = ""; + }; + 5B6BDED70E754FFC0008C579 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B6BDED80E754FFC0008C579 /* UIControl.h */; + name = "UIControl.h: 67"; + rLen = 6; + rLoc = 2650; + rType = 0; + vrLen = 1565; + vrLoc = 1619; + }; + 5B6BDED80E754FFC0008C579 /* UIControl.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = UIControl.h; + path = /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator2.0.sdk/System/Library/Frameworks/UIKit.framework/Headers/UIControl.h; + sourceTree = ""; + }; + 5B6BDED90E754FFC0008C579 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B6BDEDA0E754FFC0008C579 /* UIView.h */; + name = "UIView.h: 136"; + rLen = 9; + rLoc = 5966; + rType = 0; + vrLen = 1389; + vrLoc = 5885; + }; + 5B6BDEDA0E754FFC0008C579 /* UIView.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = UIView.h; + path = /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator2.0.sdk/System/Library/Frameworks/UIKit.framework/Headers/UIView.h; + sourceTree = ""; + }; + 5B6BDEDF0E754FFC0008C579 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B6BDEE00E754FFC0008C579 /* UIControl.h */; + name = "UIControl.h: 67"; + rLen = 6; + rLoc = 2650; + rType = 0; + vrLen = 1565; + vrLoc = 1619; + }; + 5B6BDEE00E754FFC0008C579 /* UIControl.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = UIControl.h; + path = /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator2.0.sdk/System/Library/Frameworks/UIKit.framework/Headers/UIControl.h; + sourceTree = ""; + }; + 5B6BDEE10E754FFC0008C579 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B6BDEE20E754FFC0008C579 /* UIView.h */; + name = "UIView.h: 136"; + rLen = 9; + rLoc = 5966; + rType = 0; + vrLen = 1389; + vrLoc = 5885; + }; + 5B6BDEE20E754FFC0008C579 /* UIView.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = UIView.h; + path = /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator2.0.sdk/System/Library/Frameworks/UIKit.framework/Headers/UIView.h; + sourceTree = ""; + }; + 5B6BDEF20E7551420008C579 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B6BDEB70E754E7C0008C579 /* UITextField.h */; + name = "UITextField.h: 169"; + rLen = 21; + rLoc = 7584; + rType = 0; + vrLen = 2047; + vrLoc = 5875; + }; + 5B6BDEFE0E7551CE0008C579 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 1D3623240D0F684500981E51 /* TuveAppDelegate.h */; + name = "TuveAppDelegate.h: 17"; + rLen = 7; + rLoc = 409; + rType = 0; + vrLen = 565; + vrLoc = 0; + }; + 5B6BDF140E7556910008C579 /* PlistBookmark */ = { + isa = PlistBookmark; + fRef = 8D1107310486CEB800E47090 /* Info.plist */; + fallbackIsa = PBXBookmark; + isK = 0; + kPath = ( + ); + name = /Users/phausler/Documents/Projects/Tuve/Info.plist; + rLen = 0; + rLoc = 2147483647; + }; + 5B6BDF1A0E7556910008C579 /* PlistBookmark */ = { + isa = PlistBookmark; + fRef = 8D1107310486CEB800E47090 /* Info.plist */; + fallbackIsa = PBXBookmark; + isK = 0; + kPath = ( + ); + name = /Users/phausler/Documents/Projects/Tuve/Info.plist; + rLen = 0; + rLoc = 2147483647; + }; + 5B6BDF470E756A950008C579 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B6BDF480E756A950008C579 /* MediaPlayerDefines.h */; + name = "MediaPlayerDefines.h: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 342; + vrLoc = 0; + }; + 5B6BDF480E756A950008C579 /* MediaPlayerDefines.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = MediaPlayerDefines.h; + path = /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS2.0.sdk/System/Library/Frameworks/MediaPlayer.framework/Headers/MediaPlayerDefines.h; + sourceTree = ""; + }; + 5B6BDF4F0E756A950008C579 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B6BDF500E756A950008C579 /* MPMoviePlayerController.h */; + name = "MPMoviePlayerController.h: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 1874; + vrLoc = 107; + }; + 5B6BDF500E756A950008C579 /* MPMoviePlayerController.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = MPMoviePlayerController.h; + path = /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS2.0.sdk/System/Library/Frameworks/MediaPlayer.framework/Headers/MPMoviePlayerController.h; + sourceTree = ""; + }; + 5B6BDF510E756A950008C579 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B6BDF520E756A950008C579 /* MediaPlayer.h */; + name = "MediaPlayer.h: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 265; + vrLoc = 0; + }; + 5B6BDF520E756A950008C579 /* MediaPlayer.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = MediaPlayer.h; + path = /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS2.0.sdk/System/Library/Frameworks/MediaPlayer.framework/Headers/MediaPlayer.h; + sourceTree = ""; + }; + 5B6BDF530E756A950008C579 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B6BDF540E756A950008C579 /* MediaPlayerDefines.h */; + name = "MediaPlayerDefines.h: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 342; + vrLoc = 0; + }; + 5B6BDF540E756A950008C579 /* MediaPlayerDefines.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = MediaPlayerDefines.h; + path = /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS2.0.sdk/System/Library/Frameworks/MediaPlayer.framework/Headers/MediaPlayerDefines.h; + sourceTree = ""; + }; + 5B6BDF550E756A950008C579 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B6BDF560E756A950008C579 /* MPVolumeSettings.h */; + name = "MPVolumeSettings.h: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 340; + vrLoc = 0; + }; + 5B6BDF560E756A950008C579 /* MPVolumeSettings.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = MPVolumeSettings.h; + path = /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS2.0.sdk/System/Library/Frameworks/MediaPlayer.framework/Headers/MPVolumeSettings.h; + sourceTree = ""; + }; + 5B6BDFAB0E756D860008C579 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B6BDD2B0E74F12D0008C579 /* TCPSocket.m */; + name = "TCPSocket.m: 9"; + rLen = 0; + rLoc = 136; + rType = 0; + vrLen = 886; + vrLoc = 0; + }; + 5B891DF30E75706300933BE0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B6BDF560E756A950008C579 /* MPVolumeSettings.h */; + name = "MPVolumeSettings.h: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 340; + vrLoc = 0; + }; + 5B891DF50E75706300933BE0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B6BDF520E756A950008C579 /* MediaPlayer.h */; + name = "MediaPlayer.h: 2"; + rLen = 11; + rLoc = 7; + rType = 0; + vrLen = 265; + vrLoc = 0; + }; + 5B891E060E75737500933BE0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B6BDF500E756A950008C579 /* MPMoviePlayerController.h */; + name = "MPMoviePlayerController.h: 30"; + rLen = 64; + rLoc = 1300; + rType = 0; + vrLen = 1630; + vrLoc = 1434; + }; + 5B891E210E757D0900933BE0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B6BDD8C0E74FA070008C579 /* NSString.h */; + name = "NSString.h: 149"; + rLen = 27; + rLoc = 7537; + rType = 0; + vrLen = 2183; + vrLoc = 6394; + }; + 5B891E5F0E757EA300933BE0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B891E600E757EA300933BE0 /* MPMoviePlayerController.h */; + name = "MPMoviePlayerController.h: 55"; + rLen = 5; + rLoc = 2222; + rType = 0; + vrLen = 1667; + vrLoc = 1397; + }; + 5B891E600E757EA300933BE0 /* MPMoviePlayerController.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = MPMoviePlayerController.h; + path = /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator2.0.sdk/System/Library/Frameworks/MediaPlayer.framework/Headers/MPMoviePlayerController.h; + sourceTree = ""; + }; + 5B891E630E757EA300933BE0 /* NSURL.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = NSURL.h; + path = /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator2.0.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSURL.h; + sourceTree = ""; + }; + 5B891E9A0E75829900933BE0 /* ChatController.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1181, 616}}"; + sepNavSelRange = "{329, 0}"; + sepNavVisRange = "{0, 401}"; + }; + }; + 5B891E9B0E75829900933BE0 /* ChatController.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 1734}}"; + sepNavSelRange = "{271, 43}"; + sepNavVisRange = "{168, 167}"; + }; + }; + 5B891EA90E758A9800933BE0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B891EAA0E758A9800933BE0 /* UITableView.h */; + name = "UITableView.h: 23"; + rLen = 31; + rLoc = 573; + rType = 0; + vrLen = 1349; + vrLoc = 0; + }; + 5B891EAA0E758A9800933BE0 /* UITableView.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = UITableView.h; + path = /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS2.0.sdk/System/Library/Frameworks/UIKit.framework/Headers/UITableView.h; + sourceTree = ""; + }; + 5B891EAB0E758A9800933BE0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B891EAC0E758A9800933BE0 /* NSIndexPath.h */; + name = "NSIndexPath.h: 22"; + rLen = 22; + rLoc = 605; + rType = 0; + vrLen = 1035; + vrLoc = 0; + }; + 5B891EAC0E758A9800933BE0 /* NSIndexPath.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = NSIndexPath.h; + path = /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS2.0.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSIndexPath.h; + sourceTree = ""; + }; + 5B891EAD0E758A9800933BE0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B891EAE0E758A9800933BE0 /* NSNotification.h */; + name = "NSNotification.h: 15"; + rLen = 8; + rLoc = 310; + rType = 0; + vrLen = 1148; + vrLoc = 82; + }; + 5B891EAE0E758A9800933BE0 /* NSNotification.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = NSNotification.h; + path = /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS2.0.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSNotification.h; + sourceTree = ""; + }; + 5B891EB40E758A9800933BE0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B6BDD5E0E74F64D0008C579 /* TuveConnection.m */; + name = "TuveConnection.m: 94"; + rLen = 0; + rLoc = 2900; + rType = 0; + vrLen = 1173; + vrLoc = 1993; + }; + 5B891EB50E758A9800933BE0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B891E9A0E75829900933BE0 /* ChatController.h */; + name = "ChatController.h: 13"; + rLen = 0; + rLoc = 274; + rType = 0; + vrLen = 319; + vrLoc = 0; + }; + 5B891EC30E758A9800933BE0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B891EC40E758A9800933BE0 /* NSNotification.h */; + name = "NSNotification.h: 41"; + rLen = 94; + rLoc = 1010; + rType = 0; + vrLen = 1148; + vrLoc = 82; + }; + 5B891EC40E758A9800933BE0 /* NSNotification.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = NSNotification.h; + path = /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS2.0.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSNotification.h; + sourceTree = ""; + }; + 5B891EDD0E758B8800933BE0 /* TuveAppDelegate.m:17 */ = { + isa = PBXFileBreakpoint; + actions = ( + ); + breakpointStyle = 0; + continueAfterActions = 0; + countType = 0; + delayBeforeContinue = 0; + fileReference = 1D3623250D0F684500981E51 /* TuveAppDelegate.m */; + functionName = "-applicationDidFinishLaunching:"; + hitCount = 0; + ignoreCount = 0; + lineNumber = 17; + location = "Tuvé"; + modificationTime = 242586042.3965951; + state = 1; + }; + 5B891EE00E758BAD00933BE0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B6BDD5D0E74F64D0008C579 /* TuveConnection.h */; + name = "TuveConnection.h: 26"; + rLen = 0; + rLoc = 584; + rType = 0; + vrLen = 860; + vrLoc = 0; + }; + 5B891EE10E758BAD00933BE0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B891EE20E758BAD00933BE0 /* UIStringDrawing.h */; + name = "UIStringDrawing.h: 12"; + rLen = 23; + rLoc = 192; + rType = 0; + vrLen = 1964; + vrLoc = 0; + }; + 5B891EE20E758BAD00933BE0 /* UIStringDrawing.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = UIStringDrawing.h; + path = /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS2.0.sdk/System/Library/Frameworks/UIKit.framework/Headers/UIStringDrawing.h; + sourceTree = ""; + }; + 5B891EE30E758BAD00933BE0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B891E9A0E75829900933BE0 /* ChatController.h */; + name = "ChatController.h: 16"; + rLen = 0; + rLoc = 329; + rType = 0; + vrLen = 401; + vrLoc = 0; + }; + 5B891EE40E758BAD00933BE0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B891EE50E758BAD00933BE0 /* UITableViewCell.h */; + name = "UITableViewCell.h: 40"; + rLen = 68; + rLoc = 1152; + rType = 0; + vrLen = 1322; + vrLoc = 0; + }; + 5B891EE50E758BAD00933BE0 /* UITableViewCell.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = UITableViewCell.h; + path = /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS2.0.sdk/System/Library/Frameworks/UIKit.framework/Headers/UITableViewCell.h; + sourceTree = ""; + }; + 5B891EE60E758BAD00933BE0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B891EE70E758BAD00933BE0 /* UIFont.h */; + name = "UIFont.h: 12"; + rLen = 50; + rLoc = 193; + rType = 0; + vrLen = 1353; + vrLoc = 0; + }; + 5B891EE70E758BAD00933BE0 /* UIFont.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = UIFont.h; + path = /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS2.0.sdk/System/Library/Frameworks/UIKit.framework/Headers/UIFont.h; + sourceTree = ""; + }; + 5B891EEE0E758BAD00933BE0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B891EEF0E758BAD00933BE0 /* UITableViewCell.h */; + name = "UITableViewCell.h: 93"; + rLen = 15; + rLoc = 3352; + rType = 0; + vrLen = 4080; + vrLoc = 2701; + }; + 5B891EEF0E758BAD00933BE0 /* UITableViewCell.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = UITableViewCell.h; + path = /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS2.0.sdk/System/Library/Frameworks/UIKit.framework/Headers/UITableViewCell.h; + sourceTree = ""; + }; + 5B891EF20E758BAD00933BE0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B891E9B0E75829900933BE0 /* ChatController.m */; + name = "ChatController.m: 39"; + rLen = 0; + rLoc = 988; + rType = 0; + vrLen = 1180; + vrLoc = 331; + }; + 5B891F0B0E758C7600933BE0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B891E630E757EA300933BE0 /* NSURL.h */; + name = "NSURL.h: 65"; + rLen = 41; + rLoc = 3301; + rType = 0; + vrLen = 3238; + vrLoc = 1846; + }; + 5B891F100E758C7600933BE0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B891E630E757EA300933BE0 /* NSURL.h */; + name = "NSURL.h: 65"; + rLen = 41; + rLoc = 3301; + rType = 0; + vrLen = 3238; + vrLoc = 1846; + }; + 5B891F240E75952600933BE0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 1D3623250D0F684500981E51 /* TuveAppDelegate.m */; + name = "TuveAppDelegate.m: 16"; + rLen = 0; + rLoc = 321; + rType = 0; + vrLen = 1255; + vrLoc = 25; + }; + 5B891F250E75952600933BE0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B6BDD5E0E74F64D0008C579 /* TuveConnection.m */; + name = "TuveConnection.m: 100"; + rLen = 23; + rLoc = 2947; + rType = 0; + vrLen = 1371; + vrLoc = 2197; + }; + 5B891F2C0E75952B00933BE0 /* access.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1271, 7840}}"; + sepNavSelRange = "{17557, 8}"; + sepNavVisRange = "{17081, 1170}"; + }; + }; + 5B891F2D0E75952B00933BE0 /* rtmp_amf_flv.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1271, 35966}}"; + sepNavSelRange = "{28014, 7}"; + sepNavVisRange = "{27488, 853}"; + }; + }; + 5B891F2E0E75952B00933BE0 /* rtmp_amf_flv.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1271, 2198}}"; + sepNavSelRange = "{2078, 32}"; + sepNavVisRange = "{1583, 631}"; + }; + }; + 5B891F360E75956C00933BE0 /* vlc_messages.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1271, 4270}}"; + sepNavSelRange = "{3347, 0}"; + sepNavVisRange = "{2439, 1758}"; + }; + }; + 5B891F370E75956C00933BE0 /* vlc_input.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1271, 7966}}"; + sepNavSelRange = "{1364, 0}"; + sepNavVisRange = "{1253, 1054}"; + }; + }; + 5B891F3B0E75956C00933BE0 /* vlc_plugin.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1181, 6622}}"; + sepNavSelRange = "{2763, 0}"; + sepNavVisRange = "{2382, 2223}"; + }; + }; + 5B891F3E0E75956C00933BE0 /* vlc_playlist.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 6566}}"; + sepNavSelRange = "{1268, 0}"; + sepNavVisRange = "{0, 1379}"; + }; + }; + 5B891F420E75956C00933BE0 /* vlc_vout.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1271, 10430}}"; + sepNavSelRange = "{1383, 0}"; + sepNavVisRange = "{912, 1142}"; + }; + }; + 5B891F450E75956C00933BE0 /* vlc_tls.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1271, 1162}}"; + sepNavSelRange = "{1251, 0}"; + sepNavVisRange = "{0, 1304}"; + }; + }; + 5B891F470E75956C00933BE0 /* vlc_common.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1048, 12782}}"; + sepNavSelRange = "{4105, 0}"; + sepNavVisRange = "{3489, 1248}"; + sepNavWindowFrame = "{{15, 273}, {1647, 900}}"; + }; + }; + 5B891F480E75956C00933BE0 /* vlc_network.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1271, 5446}}"; + sepNavSelRange = "{7484, 0}"; + sepNavVisRange = "{7439, 709}"; + }; + }; + 5B891F4A0E75956C00933BE0 /* vlc_modules.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {777, 1022}}"; + sepNavSelRange = "{3108, 4}"; + sepNavVisRange = "{941, 2312}"; + }; + }; + 5B891F4B0E75956C00933BE0 /* vlc_meta.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1271, 2856}}"; + sepNavSelRange = "{1247, 0}"; + sepNavVisRange = "{0, 1278}"; + }; + }; + 5B891F4C0E75956C00933BE0 /* vlc_sout.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1271, 3612}}"; + sepNavSelRange = "{1439, 0}"; + sepNavVisRange = "{1170, 734}"; + }; + }; + 5B891F4D0E75956C00933BE0 /* vlc_services_discovery.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 1442}}"; + sepNavSelRange = "{1365, 0}"; + sepNavVisRange = "{908, 1061}"; + }; + }; + 5B891F560E75956C00933BE0 /* vlc_aout.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 5796}}"; + sepNavSelRange = "{11611, 23}"; + sepNavVisRange = "{12326, 431}"; + }; + }; + 5B891F650E75956C00933BE0 /* vlc_stream.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1271, 2072}}"; + sepNavSelRange = "{1202, 0}"; + sepNavVisRange = "{0, 1357}"; + }; + }; + 5B891F690E75956C00933BE0 /* vlc_events.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1271, 3584}}"; + sepNavSelRange = "{1196, 0}"; + sepNavVisRange = "{0, 1308}"; + }; + }; + 5B891F6D0E75956C00933BE0 /* vlc_image.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 1162}}"; + sepNavSelRange = "{1276, 0}"; + sepNavVisRange = "{1038, 260}"; + }; + }; + 5B891F6F0E75956C00933BE0 /* vlc_filter.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1271, 4480}}"; + sepNavSelRange = "{1244, 0}"; + sepNavVisRange = "{0, 1394}"; + }; + }; + 5B891F710E75956C00933BE0 /* vlc_osd.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 9604}}"; + sepNavSelRange = "{1990, 0}"; + sepNavVisRange = "{1458, 1487}"; + }; + }; + 5B891F720E75956C00933BE0 /* vlc_objects.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {777, 2674}}"; + sepNavSelRange = "{2474, 4}"; + sepNavVisRange = "{1973, 2005}"; + }; + }; + 5B891F730E75956C00933BE0 /* vlc_vlm.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 3962}}"; + sepNavSelRange = "{1285, 0}"; + sepNavVisRange = "{0, 1548}"; + }; + }; + 5B891F750E75956C00933BE0 /* vlc_demux.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1271, 3710}}"; + sepNavSelRange = "{1274, 24}"; + sepNavVisRange = "{707, 900}"; + }; + }; + 5B891F790E75956C00933BE0 /* vlc_access.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1271, 2492}}"; + sepNavSelRange = "{1267, 0}"; + sepNavVisRange = "{0, 1300}"; + }; + }; + 5B891F7A0E75956C00933BE0 /* vlc_codec.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1271, 2156}}"; + sepNavSelRange = "{1215, 0}"; + sepNavVisRange = "{223, 1180}"; + }; + }; + 5B891FB00E7597F600933BE0 /* threads.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1181, 10542}}"; + sepNavSelRange = "{1361, 0}"; + sepNavVisRange = "{0, 1622}"; + }; + }; + 5B891FB50E75981200933BE0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B891E600E757EA300933BE0 /* MPMoviePlayerController.h */; + name = "MPMoviePlayerController.h: 30"; + rLen = 23; + rLoc = 1327; + rType = 0; + vrLen = 1798; + vrLoc = 0; + }; + 5B891FBB0E75982300933BE0 /* libvlc.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1181, 4298}}"; + sepNavSelRange = "{5692, 78}"; + sepNavVisRange = "{5084, 1204}"; + }; + }; + 5B891FBD0E75985900933BE0 /* messages.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1181, 9352}}"; + sepNavSelRange = "{1541, 0}"; + sepNavVisRange = "{0, 1813}"; + }; + }; + 5B891FCB0E759A3600933BE0 /* entry.c */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.c; + name = entry.c; + path = "/Users/phausler/Downloads/vlc-0.9.1/src/modules/entry.c"; + sourceTree = ""; + }; + 5B891FCE0E759A4C00933BE0 /* entry.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1181, 7266}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{0, 1614}"; + }; + }; + 5B891FD70E759A8200933BE0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B891FCB0E759A3600933BE0 /* entry.c */; + name = "entry.c: 236"; + rLen = 60; + rLoc = 7103; + rType = 0; + vrLen = 1065; + vrLoc = 6404; + }; + 5B891FD90E759A8200933BE0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B891FCB0E759A3600933BE0 /* entry.c */; + name = "entry.c: 236"; + rLen = 60; + rLoc = 7103; + rType = 0; + vrLen = 1065; + vrLoc = 6404; + }; + 5B891FDD0E759A8600933BE0 /* modules.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1271, 2352}}"; + sepNavSelRange = "{2951, 0}"; + sepNavVisRange = "{2486, 873}"; + }; + }; + 5B891FE70E759AE600933BE0 /* configuration.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1181, 1036}}"; + sepNavSelRange = "{2580, 0}"; + sepNavVisRange = "{1225, 1386}"; + }; + }; + 5B891FEA0E7639E500933BE0 /* modules.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1048, 19740}}"; + sepNavSelRange = "{7722, 0}"; + sepNavVisRange = "{6805, 1214}"; + }; + }; + 5B891FEF0E763A4B00933BE0 /* builtin.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {777, 730}}"; + sepNavSelRange = "{1263, 15}"; + sepNavVisRange = "{0, 1315}"; + }; + }; + 5B891FF20E763A9900933BE0 /* os.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1181, 5250}}"; + sepNavSelRange = "{1330, 0}"; + sepNavVisRange = "{901, 1342}"; + }; + }; + 5B8920190E763B5A00933BE0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B89201A0E763B5A00933BE0 /* modules.h */; + name = "modules.h: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 1788; + vrLoc = 0; + }; + 5B89201A0E763B5A00933BE0 /* modules.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = modules.h; + path = "/Users/phausler/Downloads/vlc-0.9.1/src/modules/modules.h"; + sourceTree = ""; + }; + 5B8920210E763B5A00933BE0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B8920220E763B5A00933BE0 /* modules.h */; + name = "modules.h: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 1788; + vrLoc = 0; + }; + 5B8920220E763B5A00933BE0 /* modules.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = modules.h; + path = "/Users/phausler/Downloads/vlc-0.9.1/src/modules/modules.h"; + sourceTree = ""; + }; + 5B8920270E763B7A00933BE0 /* cache.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1181, 10388}}"; + sepNavSelRange = "{1585, 0}"; + sepNavVisRange = "{642, 1405}"; + }; + }; + 5B8920380E763CF500933BE0 /* tcp.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1181, 7392}}"; + sepNavSelRange = "{1683, 0}"; + sepNavVisRange = "{1344, 1263}"; + }; + }; + 5B8920390E763CF500933BE0 /* poll.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1271, 1624}}"; + sepNavSelRange = "{1417, 0}"; + sepNavVisRange = "{1074, 679}"; + }; + }; + 5B89203B0E763CF500933BE0 /* httpd.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1271, 37548}}"; + sepNavSelRange = "{1254, 0}"; + sepNavVisRange = "{0, 1309}"; + }; + }; + 5B89203C0E763CF500933BE0 /* tls.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1271, 3022}}"; + sepNavSelRange = "{1257, 21}"; + sepNavVisRange = "{783, 1039}"; + }; + }; + 5B89203D0E763CF500933BE0 /* udp.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1271, 12124}}"; + sepNavSelRange = "{1521, 0}"; + sepNavVisRange = "{448, 1210}"; + }; + }; + 5B89203E0E763CF500933BE0 /* acl.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1271, 5334}}"; + sepNavSelRange = "{1353, 0}"; + sepNavVisRange = "{602, 1069}"; + }; + }; + 5B89203F0E763CF500933BE0 /* getaddrinfo.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1181, 11116}}"; + sepNavSelRange = "{7479, 0}"; + sepNavVisRange = "{7112, 1226}"; + }; + }; + 5B8920410E763CF500933BE0 /* io.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1271, 7854}}"; + sepNavSelRange = "{1628, 0}"; + sepNavVisRange = "{1177, 684}"; + }; + }; + 5B89207C0E763EA300933BE0 /* objects.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1271, 22078}}"; + sepNavSelRange = "{1733, 23}"; + sepNavVisRange = "{1262, 812}"; + }; + }; + 5B89209A0E763FA100933BE0 /* variables.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1271, 1078}}"; + sepNavSelRange = "{1170, 0}"; + sepNavVisRange = "{0, 1397}"; + }; + }; + 5B89209D0E763FEE00933BE0 /* block.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1181, 6776}}"; + sepNavSelRange = "{1352, 0}"; + sepNavVisRange = "{1321, 957}"; + }; + }; + 5B8920A10E76404300933BE0 /* core.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1181, 8008}}"; + sepNavSelRange = "{1343, 0}"; + sepNavVisRange = "{887, 1247}"; + }; + }; + 5B8920A40E76407800933BE0 /* variables.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1181, 22036}}"; + sepNavSelRange = "{1355, 0}"; + sepNavVisRange = "{814, 1668}"; + }; + }; + 5B8920A70E76409700933BE0 /* error.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1181, 924}}"; + sepNavSelRange = "{1335, 0}"; + sepNavVisRange = "{193, 1752}"; + }; + }; + 5B8920B60E76416000933BE0 /* cpu.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 5138}}"; + sepNavSelRange = "{6895, 0}"; + sepNavVisRange = "{6657, 1026}"; + }; + }; + 5B8920CD0E76A4A700933BE0 /* stats.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1181, 8456}}"; + sepNavSelRange = "{1337, 0}"; + sepNavVisRange = "{546, 1927}"; + }; + }; + 5B8921B70E76A6A800933BE0 /* mtime.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1048, 7700}}"; + sepNavSelRange = "{2033, 0}"; + sepNavVisRange = "{1806, 1146}"; + }; + }; + 5B8921C10E76A6D000933BE0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B8921C20E76A6D000933BE0 /* interface.c */; + name = "interface.c: 140"; + rLen = 39; + rLoc = 4752; + rType = 0; + vrLen = 1545; + vrLoc = 1580; + }; + 5B8921C20E76A6D000933BE0 /* interface.c */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.c; + name = interface.c; + path = "/Users/phausler/Downloads/vlc-0.9.1/src/interface/interface.c"; + sourceTree = ""; + }; + 5B8921C60E76A6D000933BE0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B8921C70E76A6D000933BE0 /* interface.c */; + name = "interface.c: 140"; + rLen = 39; + rLoc = 4752; + rType = 0; + vrLen = 1545; + vrLoc = 1580; + }; + 5B8921C70E76A6D000933BE0 /* interface.c */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.c; + name = interface.c; + path = "/Users/phausler/Downloads/vlc-0.9.1/src/interface/interface.c"; + sourceTree = ""; + }; + 5B8921CA0E76A70F00933BE0 /* file.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 10976}}"; + sepNavSelRange = "{16689, 17}"; + sepNavVisRange = "{16256, 1151}"; + }; + }; + 5B8921DB0E76A80800933BE0 /* dirs.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1181, 3220}}"; + sepNavSelRange = "{1473, 0}"; + sepNavVisRange = "{1210, 963}"; + }; + }; + 5B8922160E76A94700933BE0 /* iso_lang.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1048, 1118}}"; + sepNavSelRange = "{1666, 25}"; + sepNavVisRange = "{852, 1309}"; + }; + }; + 5B8922170E76A94700933BE0 /* strings.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1048, 15736}}"; + sepNavSelRange = "{33184, 0}"; + sepNavVisRange = "{32668, 915}"; + }; + }; + 5B8922180E76A94700933BE0 /* unicode.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 10948}}"; + sepNavSelRange = "{1746, 0}"; + sepNavVisRange = "{1148, 742}"; + }; + }; + 5B8922200E76A95700933BE0 /* iso-639_def.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 2898}}"; + sepNavSelRange = "{1699, 69}"; + sepNavVisRange = "{10678, 2525}"; + }; + }; + 5B89223D0E76AA7100933BE0 /* clock.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1048, 3388}}"; + sepNavSelRange = "{293, 0}"; + sepNavVisRange = "{0, 1918}"; + }; + }; + 5B89223F0E76AA7100933BE0 /* decoder.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 20258}}"; + sepNavSelRange = "{1679, 0}"; + sepNavVisRange = "{414, 1434}"; + }; + }; + 5B8922420E76AA7100933BE0 /* es_out.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 29428}}"; + sepNavSelRange = "{1640, 0}"; + sepNavVisRange = "{1075, 969}"; + }; + }; + 5B8922430E76AA7100933BE0 /* input.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 39872}}"; + sepNavSelRange = "{1655, 0}"; + sepNavVisRange = "{1100, 1221}"; + }; + }; + 5B8922450E76AA7100933BE0 /* item.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 8484}}"; + sepNavSelRange = "{1191, 0}"; + sepNavVisRange = "{0, 1518}"; + }; + }; + 5B8922460E76AA7100933BE0 /* mem_stream.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 2156}}"; + sepNavSelRange = "{1201, 0}"; + sepNavVisRange = "{0, 1395}"; + }; + }; + 5B8922470E76AA7100933BE0 /* meta.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 7504}}"; + sepNavSelRange = "{1554, 0}"; + sepNavVisRange = "{1341, 298}"; + }; + }; + 5B8922480E76AA7100933BE0 /* stream.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 29904}}"; + sepNavSelRange = "{5069, 0}"; + sepNavVisRange = "{5216, 423}"; + }; + }; + 5B8922490E76AA7100933BE0 /* subtitles.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 6676}}"; + sepNavSelRange = "{6802, 48}"; + sepNavVisRange = "{6581, 359}"; + }; + }; + 5B89224A0E76AA7100933BE0 /* var.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1048, 10724}}"; + sepNavSelRange = "{1349, 0}"; + sepNavVisRange = "{0, 1965}"; + }; + }; + 5B89224B0E76AA7100933BE0 /* vlm.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 40208}}"; + sepNavSelRange = "{67141, 78}"; + sepNavVisRange = "{66824, 381}"; + }; + }; + 5B89224C0E76AA7100933BE0 /* vlm_internal.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1048, 1596}}"; + sepNavSelRange = "{1299, 0}"; + sepNavVisRange = "{1911, 951}"; + }; + }; + 5B89225D0E76AA7800933BE0 /* access.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1048, 1820}}"; + sepNavSelRange = "{238, 0}"; + sepNavVisRange = "{0, 1825}"; + }; + }; + 5B8922620E76AAE100933BE0 /* aout_internal.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 3276}}"; + sepNavSelRange = "{4718, 15}"; + sepNavVisRange = "{4506, 946}"; + }; + }; + 5B8922630E76AAE100933BE0 /* common.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 8904}}"; + sepNavSelRange = "{1397, 22}"; + sepNavVisRange = "{0, 1503}"; + }; + }; + 5B8922640E76AAE100933BE0 /* dec.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 5642}}"; + sepNavSelRange = "{1356, 0}"; + sepNavVisRange = "{1031, 378}"; + }; + }; + 5B8922650E76AAE100933BE0 /* filters.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 5332}}"; + sepNavSelRange = "{1358, 0}"; + sepNavVisRange = "{1327, 365}"; + }; + }; + 5B8922660E76AAE100933BE0 /* input.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 13006}}"; + sepNavSelRange = "{1752, 0}"; + sepNavVisRange = "{1770, 746}"; + }; + }; + 5B8922670E76AAE100933BE0 /* intf.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 7376}}"; + sepNavSelRange = "{1371, 0}"; + sepNavVisRange = "{1340, 338}"; + }; + }; + 5B8922680E76AAE100933BE0 /* mixer.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 5598}}"; + sepNavSelRange = "{1375, 0}"; + sepNavVisRange = "{1202, 349}"; + }; + }; + 5B8922690E76AAE100933BE0 /* output.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 5138}}"; + sepNavSelRange = "{1384, 0}"; + sepNavVisRange = "{0, 597}"; + }; + }; + 5B8922860E76AB0200933BE0 /* announce.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1048, 3178}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{0, 1657}"; + }; + }; + 5B8922870E76AB0200933BE0 /* sap.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 8904}}"; + sepNavSelRange = "{1749, 0}"; + sepNavVisRange = "{1069, 1060}"; + }; + }; + 5B8922880E76AB0200933BE0 /* sdp.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 4200}}"; + sepNavSelRange = "{4440, 14}"; + sepNavVisRange = "{1097, 210}"; + }; + }; + 5B8922890E76AB0200933BE0 /* stream_output.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 12110}}"; + sepNavSelRange = "{1444, 0}"; + sepNavVisRange = "{1198, 1008}"; + }; + }; + 5B8922B40E76ABF500933BE0 /* loadsave.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 3036}}"; + sepNavSelRange = "{4868, 54}"; + sepNavVisRange = "{4653, 389}"; + }; + }; + 5B8922B50E76ABF500933BE0 /* item.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 13452}}"; + sepNavSelRange = "{1244, 0}"; + sepNavVisRange = "{0, 1819}"; + }; + }; + 5B8922B60E76ABF500933BE0 /* engine.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 11044}}"; + sepNavSelRange = "{1222, 24}"; + sepNavVisRange = "{990, 1284}"; + }; + }; + 5B8922B70E76ABF500933BE0 /* control.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 7812}}"; + sepNavSelRange = "{1245, 0}"; + sepNavVisRange = "{0, 1739}"; + }; + }; + 5B8922B80E76ABF500933BE0 /* sort.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 3346}}"; + sepNavSelRange = "{1223, 0}"; + sepNavVisRange = "{0, 1644}"; + }; + }; + 5B8922B90E76ABF500933BE0 /* services_discovery.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 6412}}"; + sepNavSelRange = "{1226, 0}"; + sepNavVisRange = "{0, 1558}"; + }; + }; + 5B8922BA0E76ABF500933BE0 /* tree.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 9128}}"; + sepNavSelRange = "{1181, 0}"; + sepNavVisRange = "{0, 1791}"; + }; + }; + 5B8922BB0E76ABF500933BE0 /* thread.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 2758}}"; + sepNavSelRange = "{1220, 0}"; + sepNavVisRange = "{854, 1473}"; + }; + }; + 5B8922BC0E76ABF500933BE0 /* search.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 2478}}"; + sepNavSelRange = "{1188, 0}"; + sepNavVisRange = "{0, 1583}"; + }; + }; + 5B8923610E76B0FA00933BE0 /* libc.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1048, 11046}}"; + sepNavSelRange = "{1440, 0}"; + sepNavVisRange = "{1287, 777}"; + }; + }; + 5B89236E0E76B14F00933BE0 /* video_text.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1048, 2114}}"; + sepNavSelRange = "{1281, 0}"; + sepNavVisRange = "{642, 1737}"; + }; + }; + 5B89236F0E76B14F00933BE0 /* vout_subpictures.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1048, 21000}}"; + sepNavSelRange = "{1495, 0}"; + sepNavVisRange = "{0, 1762}"; + }; + }; + 5B8923700E76B14F00933BE0 /* vout_intf.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1048, 17206}}"; + sepNavSelRange = "{1647, 0}"; + sepNavVisRange = "{296, 1589}"; + }; + }; + 5B8923710E76B14F00933BE0 /* vout_pictures.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1048, 1988}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{3725, 1276}"; + }; + }; + 5B8923720E76B14F00933BE0 /* video_widgets.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1048, 1106}}"; + sepNavSelRange = "{1350, 0}"; + sepNavVisRange = "{822, 1872}"; + }; + }; + 5B8923730E76B14F00933BE0 /* vout_pictures.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1048, 15316}}"; + sepNavSelRange = "{1398, 0}"; + sepNavVisRange = "{1232, 1046}"; + }; + }; + 5B8923740E76B14F00933BE0 /* video_output.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1048, 22022}}"; + sepNavSelRange = "{2017, 0}"; + sepNavVisRange = "{1924, 1782}"; + }; + }; + 5B89237E0E76B25C00933BE0 /* es_format.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1048, 3080}}"; + sepNavSelRange = "{1340, 0}"; + sepNavVisRange = "{117, 1715}"; + }; + }; + 5B8923810E76B29C00933BE0 /* image.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1048, 10444}}"; + sepNavSelRange = "{1492, 0}"; + sepNavVisRange = "{573, 1604}"; + }; + }; + 5B8923840E76B2D400933BE0 /* events.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1048, 5278}}"; + sepNavSelRange = "{1477, 0}"; + sepNavVisRange = "{370, 1654}"; + }; + }; + 5B8923A70E76B39700933BE0 /* osd_text.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1048, 1904}}"; + sepNavSelRange = "{1188, 0}"; + sepNavVisRange = "{0, 1808}"; + }; + }; + 5B8923A80E76B39700933BE0 /* osd.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1048, 11620}}"; + sepNavSelRange = "{1337, 0}"; + sepNavVisRange = "{25968, 1432}"; + }; + }; + 5B8923A90E76B39700933BE0 /* osd_widgets.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1048, 4788}}"; + sepNavSelRange = "{7007, 47}"; + sepNavVisRange = "{6214, 1684}"; + }; + }; + 5B8923AE0E76B54000933BE0 /* libvlc.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 28574}}"; + sepNavSelRange = "{27284, 22}"; + sepNavVisRange = "{27040, 386}"; + }; + }; + 5B8923BA0E76C1F400933BE0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B8923BB0E76C1F400933BE0 /* vlmshell.c */; + name = "vlmshell.c: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 1688; + vrLoc = 0; + }; + 5B8923BB0E76C1F400933BE0 /* vlmshell.c */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.c; + name = vlmshell.c; + path = "/Users/phausler/Downloads/vlc-0.9.1/src/input/vlmshell.c"; + sourceTree = ""; + }; + 5B8923C30E76C1F400933BE0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B8923C40E76C1F400933BE0 /* vlmshell.c */; + name = "vlmshell.c: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 1688; + vrLoc = 0; + }; + 5B8923C40E76C1F400933BE0 /* vlmshell.c */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.c; + name = vlmshell.c; + path = "/Users/phausler/Downloads/vlc-0.9.1/src/input/vlmshell.c"; + sourceTree = ""; + }; + 5B8923D60E76C1FB00933BE0 /* libvlc_internal.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 4774}}"; + sepNavSelRange = "{1295, 17}"; + sepNavVisRange = "{1266, 352}"; + }; + }; + 5B8923E40E76C25B00933BE0 /* deprecated.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1048, 3430}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRange = "{0, 1613}"; + }; + }; + 5B8923EC0E76C25B00933BE0 /* vlc.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 728}}"; + sepNavSelRange = "{1581, 0}"; + sepNavVisRange = "{1407, 222}"; + }; + }; + 5B8923EF0E76C2B600933BE0 /* libvlc-module.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {777, 37702}}"; + sepNavSelRange = "{1388, 0}"; + sepNavVisRange = "{0, 1811}"; + }; + }; + 5B8924010E76C6FA00933BE0 /* version.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1048, 882}}"; + sepNavSelRange = "{2537, 0}"; + sepNavVisRange = "{946, 1683}"; + }; + }; + 5B8924070E76C71100933BE0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B891E9B0E75829900933BE0 /* ChatController.m */; + name = "ChatController.m: 16"; + rLen = 43; + rLoc = 271; + rType = 0; + vrLen = 1037; + vrLoc = 0; + }; + 5B8924080E76C71100933BE0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B8924090E76C71100933BE0 /* version.c */; + name = "version.c: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 1704; + vrLoc = 946; + }; + 5B8924090E76C71100933BE0 /* version.c */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.c; + name = version.c; + path = "/Users/phausler/Downloads/vlc-0.9.1/src/version.c"; + sourceTree = ""; + }; + 5B89240F0E76C71100933BE0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = 5B8924100E76C71100933BE0 /* version.c */; + name = "version.c: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 1704; + vrLoc = 946; + }; + 5B8924100E76C71100933BE0 /* version.c */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.c; + name = version.c; + path = "/Users/phausler/Downloads/vlc-0.9.1/src/version.c"; + sourceTree = ""; + }; + 5B8924270E76C91200933BE0 /* darwin_specific.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1048, 2660}}"; + sepNavSelRange = "{1278, 0}"; + sepNavVisRange = "{250, 1507}"; + }; + }; + 5B89244C0E76C9BF00933BE0 /* intf_eject.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1048, 4144}}"; + sepNavSelRange = "{1555, 0}"; + sepNavVisRange = "{850, 1142}"; + }; + }; + 5B89244D0E76C9BF00933BE0 /* interaction.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1048, 9016}}"; + sepNavSelRange = "{1493, 0}"; + sepNavVisRange = "{1469, 1738}"; + }; + }; + 5B89244E0E76C9BF00933BE0 /* interface.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1048, 4536}}"; + sepNavSelRange = "{1640, 0}"; + sepNavVisRange = "{814, 1410}"; + }; + }; + 5B89244F0E76C9BF00933BE0 /* interface.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 560}}"; + sepNavSelRange = "{1065, 0}"; + sepNavVisRange = "{995, 455}"; + }; + }; + 5B89245A0E76CAAB00933BE0 /* filter_chain.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1541, 7028}}"; + sepNavSelRange = "{13809, 0}"; + sepNavVisRange = "{13603, 316}"; + }; + }; + 5B89247A0E76CB3500933BE0 /* chain.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1048, 5320}}"; + sepNavSelRange = "{1456, 0}"; + sepNavVisRange = "{735, 1422}"; + }; + }; + 5B8924810E76CB6000933BE0 /* action.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1048, 658}}"; + sepNavSelRange = "{1107, 0}"; + sepNavVisRange = "{0, 1588}"; + }; + }; + 5B8924840E76CB9C00933BE0 /* vlmshell.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1588, 25326}}"; + sepNavSelRange = "{39130, 3488}"; + sepNavVisRange = "{41251, 1993}"; + sepNavWindowFrame = "{{222, 84}, {1647, 900}}"; + }; + }; + 5B8924890E76D61F00933BE0 /* cmdline.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1048, 5530}}"; + sepNavSelRange = "{1295, 0}"; + sepNavVisRange = "{872, 1555}"; + }; + }; + 8D1107310486CEB800E47090 /* Info.plist */ = { + uiCtxt = { + sepNavWindowFrame = "{{130, 168}, {1647, 900}}"; + }; + }; +} diff --git a/Tuve.xcodeproj/project.pbxproj b/Tuve.xcodeproj/project.pbxproj new file mode 100755 index 0000000..3a137c2 --- /dev/null +++ b/Tuve.xcodeproj/project.pbxproj @@ -0,0 +1,978 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 45; + objects = { + +/* Begin PBXBuildFile section */ + 1D3623260D0F684500981E51 /* TuveAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D3623250D0F684500981E51 /* TuveAppDelegate.m */; }; + 1D60589B0D05DD56006BFB54 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; }; + 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; }; + 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; }; + 28AD733F0D9D9553002E5188 /* MainWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28AD733E0D9D9553002E5188 /* MainWindow.xib */; }; + 5B6BDD0B0E74EC510008C579 /* icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 5B6BDD0A0E74EC510008C579 /* icon.png */; }; + 5B6BDD2C0E74F12D0008C579 /* TCPSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B6BDD2B0E74F12D0008C579 /* TCPSocket.m */; }; + 5B6BDD5F0E74F64D0008C579 /* TuveConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B6BDD5E0E74F64D0008C579 /* TuveConnection.m */; }; + 5B6BDF380E7569D90008C579 /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B6BDF370E7569D90008C579 /* MediaPlayer.framework */; }; + 5B891DEA0E756EE900933BE0 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 5B891DE90E756EE900933BE0 /* Default.png */; }; + 5B891E9C0E75829900933BE0 /* ChatController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B891E9B0E75829900933BE0 /* ChatController.m */; }; + 5B891EA40E75875D00933BE0 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B891EA30E75875D00933BE0 /* CoreGraphics.framework */; }; + 5B891F2F0E75952B00933BE0 /* access.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B891F2C0E75952B00933BE0 /* access.c */; }; + 5B891F300E75952B00933BE0 /* rtmp_amf_flv.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B891F2D0E75952B00933BE0 /* rtmp_amf_flv.c */; }; + 5B891FB10E7597F600933BE0 /* threads.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B891FB00E7597F600933BE0 /* threads.c */; }; + 5B891FBE0E75985900933BE0 /* messages.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B891FBD0E75985900933BE0 /* messages.c */; }; + 5B891FCF0E759A4C00933BE0 /* entry.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B891FCE0E759A4C00933BE0 /* entry.c */; }; + 5B891FEB0E7639E500933BE0 /* modules.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B891FEA0E7639E500933BE0 /* modules.c */; }; + 5B891FF00E763A4B00933BE0 /* builtin.h in Resources */ = {isa = PBXBuildFile; fileRef = 5B891FEF0E763A4B00933BE0 /* builtin.h */; }; + 5B891FF30E763A9900933BE0 /* os.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B891FF20E763A9900933BE0 /* os.c */; }; + 5B8920280E763B7A00933BE0 /* cache.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8920270E763B7A00933BE0 /* cache.c */; }; + 5B8920420E763CF500933BE0 /* tcp.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8920380E763CF500933BE0 /* tcp.c */; }; + 5B8920430E763CF500933BE0 /* poll.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8920390E763CF500933BE0 /* poll.c */; }; + 5B8920440E763CF500933BE0 /* rootbind.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B89203A0E763CF500933BE0 /* rootbind.c */; }; + 5B8920450E763CF500933BE0 /* httpd.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B89203B0E763CF500933BE0 /* httpd.c */; }; + 5B8920460E763CF500933BE0 /* tls.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B89203C0E763CF500933BE0 /* tls.c */; }; + 5B8920470E763CF500933BE0 /* udp.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B89203D0E763CF500933BE0 /* udp.c */; }; + 5B8920480E763CF500933BE0 /* acl.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B89203E0E763CF500933BE0 /* acl.c */; }; + 5B8920490E763CF500933BE0 /* getaddrinfo.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B89203F0E763CF500933BE0 /* getaddrinfo.c */; }; + 5B89204B0E763CF500933BE0 /* io.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8920410E763CF500933BE0 /* io.c */; }; + 5B89207D0E763EA300933BE0 /* objects.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B89207C0E763EA300933BE0 /* objects.c */; }; + 5B89209E0E763FEE00933BE0 /* block.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B89209D0E763FEE00933BE0 /* block.c */; }; + 5B8920A20E76404300933BE0 /* core.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8920A10E76404300933BE0 /* core.c */; }; + 5B8920A50E76407800933BE0 /* variables.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8920A40E76407800933BE0 /* variables.c */; }; + 5B8920A80E76409700933BE0 /* error.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8920A70E76409700933BE0 /* error.c */; }; + 5B8920B70E76416000933BE0 /* cpu.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8920B60E76416000933BE0 /* cpu.c */; }; + 5B8920CE0E76A4A700933BE0 /* stats.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8920CD0E76A4A700933BE0 /* stats.c */; }; + 5B8921B80E76A6A800933BE0 /* mtime.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8921B70E76A6A800933BE0 /* mtime.c */; }; + 5B8921CB0E76A70F00933BE0 /* file.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8921CA0E76A70F00933BE0 /* file.c */; }; + 5B8921DC0E76A80800933BE0 /* dirs.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8921DB0E76A80800933BE0 /* dirs.c */; }; + 5B89221A0E76A94700933BE0 /* charset.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8922150E76A94700933BE0 /* charset.c */; }; + 5B89221B0E76A94700933BE0 /* iso_lang.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8922160E76A94700933BE0 /* iso_lang.c */; }; + 5B89221C0E76A94700933BE0 /* strings.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8922170E76A94700933BE0 /* strings.c */; }; + 5B89221D0E76A94700933BE0 /* unicode.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8922180E76A94700933BE0 /* unicode.c */; }; + 5B89221E0E76A94700933BE0 /* wincp.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8922190E76A94700933BE0 /* wincp.c */; }; + 5B89224E0E76AA7100933BE0 /* clock.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B89223D0E76AA7100933BE0 /* clock.c */; }; + 5B89224F0E76AA7100933BE0 /* control.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B89223E0E76AA7100933BE0 /* control.c */; }; + 5B8922500E76AA7100933BE0 /* decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B89223F0E76AA7100933BE0 /* decoder.c */; }; + 5B8922510E76AA7100933BE0 /* decoder_synchro.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8922400E76AA7100933BE0 /* decoder_synchro.c */; }; + 5B8922520E76AA7100933BE0 /* demux.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8922410E76AA7100933BE0 /* demux.c */; }; + 5B8922530E76AA7100933BE0 /* es_out.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8922420E76AA7100933BE0 /* es_out.c */; }; + 5B8922540E76AA7100933BE0 /* input.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8922430E76AA7100933BE0 /* input.c */; }; + 5B8922550E76AA7100933BE0 /* item.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8922450E76AA7100933BE0 /* item.c */; }; + 5B8922560E76AA7100933BE0 /* mem_stream.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8922460E76AA7100933BE0 /* mem_stream.c */; }; + 5B8922570E76AA7100933BE0 /* meta.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8922470E76AA7100933BE0 /* meta.c */; }; + 5B8922580E76AA7100933BE0 /* stream.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8922480E76AA7100933BE0 /* stream.c */; }; + 5B8922590E76AA7100933BE0 /* subtitles.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8922490E76AA7100933BE0 /* subtitles.c */; }; + 5B89225A0E76AA7100933BE0 /* var.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B89224A0E76AA7100933BE0 /* var.c */; }; + 5B89225B0E76AA7100933BE0 /* vlm.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B89224B0E76AA7100933BE0 /* vlm.c */; }; + 5B89225E0E76AA7800933BE0 /* access.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B89225D0E76AA7800933BE0 /* access.c */; }; + 5B89226A0E76AAE100933BE0 /* common.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8922630E76AAE100933BE0 /* common.c */; }; + 5B89226B0E76AAE100933BE0 /* dec.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8922640E76AAE100933BE0 /* dec.c */; }; + 5B89226C0E76AAE100933BE0 /* filters.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8922650E76AAE100933BE0 /* filters.c */; }; + 5B89226D0E76AAE100933BE0 /* input.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8922660E76AAE100933BE0 /* input.c */; }; + 5B89226E0E76AAE100933BE0 /* intf.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8922670E76AAE100933BE0 /* intf.c */; }; + 5B89226F0E76AAE100933BE0 /* mixer.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8922680E76AAE100933BE0 /* mixer.c */; }; + 5B8922700E76AAE100933BE0 /* output.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8922690E76AAE100933BE0 /* output.c */; }; + 5B89228B0E76AB0200933BE0 /* announce.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8922860E76AB0200933BE0 /* announce.c */; }; + 5B89228C0E76AB0200933BE0 /* sap.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8922870E76AB0200933BE0 /* sap.c */; }; + 5B89228D0E76AB0200933BE0 /* sdp.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8922880E76AB0200933BE0 /* sdp.c */; }; + 5B89228E0E76AB0200933BE0 /* stream_output.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8922890E76AB0200933BE0 /* stream_output.c */; }; + 5B8922BD0E76ABF500933BE0 /* loadsave.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8922B40E76ABF500933BE0 /* loadsave.c */; }; + 5B8922BE0E76ABF500933BE0 /* item.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8922B50E76ABF500933BE0 /* item.c */; }; + 5B8922BF0E76ABF500933BE0 /* engine.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8922B60E76ABF500933BE0 /* engine.c */; }; + 5B8922C00E76ABF500933BE0 /* control.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8922B70E76ABF500933BE0 /* control.c */; }; + 5B8922C10E76ABF500933BE0 /* sort.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8922B80E76ABF500933BE0 /* sort.c */; }; + 5B8922C20E76ABF500933BE0 /* services_discovery.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8922B90E76ABF500933BE0 /* services_discovery.c */; }; + 5B8922C30E76ABF500933BE0 /* tree.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8922BA0E76ABF500933BE0 /* tree.c */; }; + 5B8922C40E76ABF500933BE0 /* thread.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8922BB0E76ABF500933BE0 /* thread.c */; }; + 5B8922C50E76ABF500933BE0 /* search.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8922BC0E76ABF500933BE0 /* search.c */; }; + 5B8923620E76B0FA00933BE0 /* libc.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8923610E76B0FA00933BE0 /* libc.c */; }; + 5B8923750E76B14F00933BE0 /* video_text.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B89236E0E76B14F00933BE0 /* video_text.c */; }; + 5B8923760E76B14F00933BE0 /* vout_subpictures.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B89236F0E76B14F00933BE0 /* vout_subpictures.c */; }; + 5B8923770E76B14F00933BE0 /* vout_intf.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8923700E76B14F00933BE0 /* vout_intf.c */; }; + 5B8923780E76B14F00933BE0 /* video_widgets.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8923720E76B14F00933BE0 /* video_widgets.c */; }; + 5B8923790E76B14F00933BE0 /* vout_pictures.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8923730E76B14F00933BE0 /* vout_pictures.c */; }; + 5B89237A0E76B14F00933BE0 /* video_output.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8923740E76B14F00933BE0 /* video_output.c */; }; + 5B89237F0E76B25C00933BE0 /* es_format.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B89237E0E76B25C00933BE0 /* es_format.c */; }; + 5B8923820E76B29C00933BE0 /* image.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8923810E76B29C00933BE0 /* image.c */; }; + 5B8923850E76B2D400933BE0 /* events.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8923840E76B2D400933BE0 /* events.c */; }; + 5B8923AA0E76B39700933BE0 /* osd_text.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8923A70E76B39700933BE0 /* osd_text.c */; }; + 5B8923AB0E76B39700933BE0 /* osd.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8923A80E76B39700933BE0 /* osd.c */; }; + 5B8923AC0E76B39700933BE0 /* osd_widgets.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8923A90E76B39700933BE0 /* osd_widgets.c */; }; + 5B8923AF0E76B54000933BE0 /* libvlc.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8923AE0E76B54000933BE0 /* libvlc.c */; }; + 5B8923F00E76C2B600933BE0 /* libvlc-module.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8923EF0E76C2B600933BE0 /* libvlc-module.c */; }; + 5B8924020E76C6FA00933BE0 /* version.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8924010E76C6FA00933BE0 /* version.c */; }; + 5B8924280E76C91200933BE0 /* darwin_specific.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8924270E76C91200933BE0 /* darwin_specific.c */; }; + 5B8924500E76C9BF00933BE0 /* intf_eject.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B89244C0E76C9BF00933BE0 /* intf_eject.c */; }; + 5B8924510E76C9BF00933BE0 /* interaction.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B89244D0E76C9BF00933BE0 /* interaction.c */; }; + 5B8924520E76C9BF00933BE0 /* interface.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B89244E0E76C9BF00933BE0 /* interface.c */; }; + 5B89245B0E76CAAB00933BE0 /* filter_chain.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B89245A0E76CAAB00933BE0 /* filter_chain.c */; }; + 5B89247B0E76CB3500933BE0 /* chain.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B89247A0E76CB3500933BE0 /* chain.c */; }; + 5B8924820E76CB6000933BE0 /* action.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8924810E76CB6000933BE0 /* action.c */; }; + 5B8924850E76CB9C00933BE0 /* vlmshell.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8924840E76CB9C00933BE0 /* vlmshell.c */; }; + 5B89248A0E76D61F00933BE0 /* cmdline.c in Sources */ = {isa = PBXBuildFile; fileRef = 5B8924890E76D61F00933BE0 /* cmdline.c */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 1D30AB110D05D00D00671497 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 1D3623240D0F684500981E51 /* TuveAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TuveAppDelegate.h; sourceTree = ""; }; + 1D3623250D0F684500981E51 /* TuveAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TuveAppDelegate.m; sourceTree = ""; }; + 1D6058910D05DD3D006BFB54 /* Tuvé.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Tuvé.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 28AD733E0D9D9553002E5188 /* MainWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainWindow.xib; sourceTree = ""; }; + 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 32CA4F630368D1EE00C91783 /* Tuve_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Tuve_Prefix.pch; sourceTree = ""; }; + 5B6BDD0A0E74EC510008C579 /* icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = icon.png; path = Images/icon.png; sourceTree = ""; }; + 5B6BDD2A0E74F12D0008C579 /* TCPSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TCPSocket.h; sourceTree = ""; }; + 5B6BDD2B0E74F12D0008C579 /* TCPSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TCPSocket.m; sourceTree = ""; }; + 5B6BDD5D0E74F64D0008C579 /* TuveConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TuveConnection.h; sourceTree = ""; }; + 5B6BDD5E0E74F64D0008C579 /* TuveConnection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TuveConnection.m; sourceTree = ""; }; + 5B6BDF370E7569D90008C579 /* MediaPlayer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaPlayer.framework; path = System/Library/Frameworks/MediaPlayer.framework; sourceTree = SDKROOT; }; + 5B891DE90E756EE900933BE0 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Default.png; path = Images/Default.png; sourceTree = ""; }; + 5B891E9A0E75829900933BE0 /* ChatController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ChatController.h; sourceTree = ""; }; + 5B891E9B0E75829900933BE0 /* ChatController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ChatController.m; sourceTree = ""; }; + 5B891EA30E75875D00933BE0 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 5B891F2C0E75952B00933BE0 /* access.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = access.c; sourceTree = ""; }; + 5B891F2D0E75952B00933BE0 /* rtmp_amf_flv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rtmp_amf_flv.c; sourceTree = ""; }; + 5B891F2E0E75952B00933BE0 /* rtmp_amf_flv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rtmp_amf_flv.h; sourceTree = ""; }; + 5B891F360E75956C00933BE0 /* vlc_messages.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_messages.h; sourceTree = ""; }; + 5B891F370E75956C00933BE0 /* vlc_input.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_input.h; sourceTree = ""; }; + 5B891F380E75956C00933BE0 /* vlc_md5.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_md5.h; sourceTree = ""; }; + 5B891F390E75956C00933BE0 /* vlc_intf_strings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_intf_strings.h; sourceTree = ""; }; + 5B891F3A0E75956C00933BE0 /* vlc_rand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_rand.h; sourceTree = ""; }; + 5B891F3B0E75956C00933BE0 /* vlc_plugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_plugin.h; sourceTree = ""; }; + 5B891F3C0E75956C00933BE0 /* vlc_block.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_block.h; sourceTree = ""; }; + 5B891F3D0E75956C00933BE0 /* vlc_interface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_interface.h; sourceTree = ""; }; + 5B891F3E0E75956C00933BE0 /* vlc_playlist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_playlist.h; sourceTree = ""; }; + 5B891F3F0E75956C00933BE0 /* vlc_pgpkey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_pgpkey.h; sourceTree = ""; }; + 5B891F400E75956C00933BE0 /* vlc_xml.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_xml.h; sourceTree = ""; }; + 5B891F410E75956C00933BE0 /* vlc_window.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_window.h; sourceTree = ""; }; + 5B891F420E75956C00933BE0 /* vlc_vout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_vout.h; sourceTree = ""; }; + 5B891F430E75956C00933BE0 /* vlc_vod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_vod.h; sourceTree = ""; }; + 5B891F440E75956C00933BE0 /* vlc_configuration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_configuration.h; sourceTree = ""; }; + 5B891F450E75956C00933BE0 /* vlc_tls.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_tls.h; sourceTree = ""; }; + 5B891F460E75956C00933BE0 /* vlc_threads.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_threads.h; sourceTree = ""; }; + 5B891F470E75956C00933BE0 /* vlc_common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_common.h; sourceTree = ""; }; + 5B891F480E75956C00933BE0 /* vlc_network.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_network.h; sourceTree = ""; }; + 5B891F490E75956C00933BE0 /* vlc_mtime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_mtime.h; sourceTree = ""; }; + 5B891F4A0E75956C00933BE0 /* vlc_modules.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_modules.h; sourceTree = ""; }; + 5B891F4B0E75956C00933BE0 /* vlc_meta.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_meta.h; sourceTree = ""; }; + 5B891F4C0E75956C00933BE0 /* vlc_sout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_sout.h; sourceTree = ""; }; + 5B891F4D0E75956C00933BE0 /* vlc_services_discovery.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_services_discovery.h; sourceTree = ""; }; + 5B891F4E0E75956C00933BE0 /* vlc_arrays.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_arrays.h; sourceTree = ""; }; + 5B891F4F0E75956C00933BE0 /* vlc_epg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_epg.h; sourceTree = ""; }; + 5B891F500E75956C00933BE0 /* vlc_fixups.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_fixups.h; sourceTree = ""; }; + 5B891F510E75956C00933BE0 /* vlc_config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_config.h; sourceTree = ""; }; + 5B891F520E75956C00933BE0 /* vlc_url.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_url.h; sourceTree = ""; }; + 5B891F530E75956C00933BE0 /* vlc_update.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_update.h; sourceTree = ""; }; + 5B891F540E75956C00933BE0 /* vlc_main.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_main.h; sourceTree = ""; }; + 5B891F550E75956C00933BE0 /* vlc_keys.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_keys.h; sourceTree = ""; }; + 5B891F560E75956C00933BE0 /* vlc_aout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_aout.h; sourceTree = ""; }; + 5B891F620E75956C00933BE0 /* vlc_es.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_es.h; sourceTree = ""; }; + 5B891F630E75956C00933BE0 /* vlc_block_helper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_block_helper.h; sourceTree = ""; }; + 5B891F640E75956C00933BE0 /* vlc_strings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_strings.h; sourceTree = ""; }; + 5B891F650E75956C00933BE0 /* vlc_stream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_stream.h; sourceTree = ""; }; + 5B891F660E75956C00933BE0 /* vlc_codec_synchro.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_codec_synchro.h; sourceTree = ""; }; + 5B891F670E75956C00933BE0 /* vlc_bits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_bits.h; sourceTree = ""; }; + 5B891F680E75956C00933BE0 /* vlc_charset.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_charset.h; sourceTree = ""; }; + 5B891F690E75956C00933BE0 /* vlc_events.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_events.h; sourceTree = ""; }; + 5B891F6A0E75956C00933BE0 /* vlc_gcrypt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_gcrypt.h; sourceTree = ""; }; + 5B891F6B0E75956C00933BE0 /* vlc_devices.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_devices.h; sourceTree = ""; }; + 5B891F6C0E75956C00933BE0 /* vlc_iso_lang.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_iso_lang.h; sourceTree = ""; }; + 5B891F6D0E75956C00933BE0 /* vlc_image.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_image.h; sourceTree = ""; }; + 5B891F6E0E75956C00933BE0 /* vlc_config_cat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_config_cat.h; sourceTree = ""; }; + 5B891F6F0E75956C00933BE0 /* vlc_filter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_filter.h; sourceTree = ""; }; + 5B891F700E75956C00933BE0 /* vlc_httpd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_httpd.h; sourceTree = ""; }; + 5B891F710E75956C00933BE0 /* vlc_osd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_osd.h; sourceTree = ""; }; + 5B891F720E75956C00933BE0 /* vlc_objects.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_objects.h; sourceTree = ""; }; + 5B891F730E75956C00933BE0 /* vlc_vlm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_vlm.h; sourceTree = ""; }; + 5B891F740E75956C00933BE0 /* vlc_variables.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_variables.h; sourceTree = ""; }; + 5B891F750E75956C00933BE0 /* vlc_demux.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_demux.h; sourceTree = ""; }; + 5B891F760E75956C00933BE0 /* mmx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mmx.h; sourceTree = ""; }; + 5B891F770E75956C00933BE0 /* vlc_acl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_acl.h; sourceTree = ""; }; + 5B891F780E75956C00933BE0 /* vlc_codecs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_codecs.h; sourceTree = ""; }; + 5B891F790E75956C00933BE0 /* vlc_access.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_access.h; sourceTree = ""; }; + 5B891F7A0E75956C00933BE0 /* vlc_codec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_codec.h; sourceTree = ""; }; + 5B891F7B0E75956C00933BE0 /* vlc_es_out.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vlc_es_out.h; sourceTree = ""; }; + 5B891FB00E7597F600933BE0 /* threads.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = threads.c; sourceTree = ""; }; + 5B891FBB0E75982300933BE0 /* libvlc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libvlc.h; sourceTree = ""; }; + 5B891FBD0E75985900933BE0 /* messages.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = messages.c; sourceTree = ""; }; + 5B891FCE0E759A4C00933BE0 /* entry.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = entry.c; sourceTree = ""; }; + 5B891FDD0E759A8600933BE0 /* modules.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = modules.h; sourceTree = ""; }; + 5B891FE70E759AE600933BE0 /* configuration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = configuration.h; sourceTree = ""; }; + 5B891FEA0E7639E500933BE0 /* modules.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = modules.c; sourceTree = ""; }; + 5B891FEF0E763A4B00933BE0 /* builtin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin.h; sourceTree = ""; }; + 5B891FF20E763A9900933BE0 /* os.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = os.c; sourceTree = ""; }; + 5B8920270E763B7A00933BE0 /* cache.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cache.c; sourceTree = ""; }; + 5B8920380E763CF500933BE0 /* tcp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tcp.c; sourceTree = ""; }; + 5B8920390E763CF500933BE0 /* poll.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = poll.c; sourceTree = ""; }; + 5B89203A0E763CF500933BE0 /* rootbind.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rootbind.c; sourceTree = ""; }; + 5B89203B0E763CF500933BE0 /* httpd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = httpd.c; sourceTree = ""; }; + 5B89203C0E763CF500933BE0 /* tls.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tls.c; sourceTree = ""; }; + 5B89203D0E763CF500933BE0 /* udp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = udp.c; sourceTree = ""; }; + 5B89203E0E763CF500933BE0 /* acl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = acl.c; sourceTree = ""; }; + 5B89203F0E763CF500933BE0 /* getaddrinfo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = getaddrinfo.c; sourceTree = ""; }; + 5B8920410E763CF500933BE0 /* io.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = io.c; sourceTree = ""; }; + 5B89207C0E763EA300933BE0 /* objects.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = objects.c; sourceTree = ""; }; + 5B89209A0E763FA100933BE0 /* variables.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = variables.h; sourceTree = ""; }; + 5B89209D0E763FEE00933BE0 /* block.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = block.c; sourceTree = ""; }; + 5B8920A10E76404300933BE0 /* core.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = core.c; sourceTree = ""; }; + 5B8920A40E76407800933BE0 /* variables.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = variables.c; sourceTree = ""; }; + 5B8920A70E76409700933BE0 /* error.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = error.c; sourceTree = ""; }; + 5B8920B60E76416000933BE0 /* cpu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cpu.c; sourceTree = ""; }; + 5B8920CD0E76A4A700933BE0 /* stats.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = stats.c; sourceTree = ""; }; + 5B8921B70E76A6A800933BE0 /* mtime.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mtime.c; sourceTree = ""; }; + 5B8921CA0E76A70F00933BE0 /* file.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = file.c; sourceTree = ""; }; + 5B8921DB0E76A80800933BE0 /* dirs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dirs.c; sourceTree = ""; }; + 5B8922150E76A94700933BE0 /* charset.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = charset.c; sourceTree = ""; }; + 5B8922160E76A94700933BE0 /* iso_lang.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = iso_lang.c; sourceTree = ""; }; + 5B8922170E76A94700933BE0 /* strings.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = strings.c; sourceTree = ""; }; + 5B8922180E76A94700933BE0 /* unicode.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = unicode.c; sourceTree = ""; }; + 5B8922190E76A94700933BE0 /* wincp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wincp.c; sourceTree = ""; }; + 5B8922200E76A95700933BE0 /* iso-639_def.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "iso-639_def.h"; path = "text/iso-639_def.h"; sourceTree = ""; }; + 5B89223D0E76AA7100933BE0 /* clock.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = clock.c; path = input/clock.c; sourceTree = ""; }; + 5B89223E0E76AA7100933BE0 /* control.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = control.c; path = input/control.c; sourceTree = ""; }; + 5B89223F0E76AA7100933BE0 /* decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = decoder.c; path = input/decoder.c; sourceTree = ""; }; + 5B8922400E76AA7100933BE0 /* decoder_synchro.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = decoder_synchro.c; path = input/decoder_synchro.c; sourceTree = ""; }; + 5B8922410E76AA7100933BE0 /* demux.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = demux.c; path = input/demux.c; sourceTree = ""; }; + 5B8922420E76AA7100933BE0 /* es_out.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = es_out.c; path = input/es_out.c; sourceTree = ""; }; + 5B8922430E76AA7100933BE0 /* input.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = input.c; path = input/input.c; sourceTree = ""; }; + 5B8922440E76AA7100933BE0 /* input_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = input_internal.h; path = input/input_internal.h; sourceTree = ""; }; + 5B8922450E76AA7100933BE0 /* item.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = item.c; path = input/item.c; sourceTree = ""; }; + 5B8922460E76AA7100933BE0 /* mem_stream.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = mem_stream.c; path = input/mem_stream.c; sourceTree = ""; }; + 5B8922470E76AA7100933BE0 /* meta.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = meta.c; path = input/meta.c; sourceTree = ""; }; + 5B8922480E76AA7100933BE0 /* stream.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = stream.c; path = input/stream.c; sourceTree = ""; }; + 5B8922490E76AA7100933BE0 /* subtitles.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = subtitles.c; path = input/subtitles.c; sourceTree = ""; }; + 5B89224A0E76AA7100933BE0 /* var.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = var.c; path = input/var.c; sourceTree = ""; }; + 5B89224B0E76AA7100933BE0 /* vlm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = vlm.c; path = input/vlm.c; sourceTree = ""; }; + 5B89224C0E76AA7100933BE0 /* vlm_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = vlm_internal.h; path = input/vlm_internal.h; sourceTree = ""; }; + 5B89225D0E76AA7800933BE0 /* access.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = access.c; path = input/access.c; sourceTree = ""; }; + 5B8922620E76AAE100933BE0 /* aout_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = aout_internal.h; path = audio_output/aout_internal.h; sourceTree = ""; }; + 5B8922630E76AAE100933BE0 /* common.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = common.c; path = audio_output/common.c; sourceTree = ""; }; + 5B8922640E76AAE100933BE0 /* dec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dec.c; path = audio_output/dec.c; sourceTree = ""; }; + 5B8922650E76AAE100933BE0 /* filters.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = filters.c; path = audio_output/filters.c; sourceTree = ""; }; + 5B8922660E76AAE100933BE0 /* input.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = input.c; path = audio_output/input.c; sourceTree = ""; }; + 5B8922670E76AAE100933BE0 /* intf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = intf.c; path = audio_output/intf.c; sourceTree = ""; }; + 5B8922680E76AAE100933BE0 /* mixer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = mixer.c; path = audio_output/mixer.c; sourceTree = ""; }; + 5B8922690E76AAE100933BE0 /* output.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = output.c; path = audio_output/output.c; sourceTree = ""; }; + 5B8922860E76AB0200933BE0 /* announce.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = announce.c; path = stream_output/announce.c; sourceTree = ""; }; + 5B8922870E76AB0200933BE0 /* sap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = sap.c; path = stream_output/sap.c; sourceTree = ""; }; + 5B8922880E76AB0200933BE0 /* sdp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = sdp.c; path = stream_output/sdp.c; sourceTree = ""; }; + 5B8922890E76AB0200933BE0 /* stream_output.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = stream_output.c; path = stream_output/stream_output.c; sourceTree = ""; }; + 5B89228A0E76AB0200933BE0 /* stream_output.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = stream_output.h; path = stream_output/stream_output.h; sourceTree = ""; }; + 5B8922B30E76ABF500933BE0 /* playlist_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = playlist_internal.h; sourceTree = ""; }; + 5B8922B40E76ABF500933BE0 /* loadsave.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadsave.c; sourceTree = ""; }; + 5B8922B50E76ABF500933BE0 /* item.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = item.c; sourceTree = ""; }; + 5B8922B60E76ABF500933BE0 /* engine.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = engine.c; sourceTree = ""; }; + 5B8922B70E76ABF500933BE0 /* control.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = control.c; sourceTree = ""; }; + 5B8922B80E76ABF500933BE0 /* sort.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sort.c; sourceTree = ""; }; + 5B8922B90E76ABF500933BE0 /* services_discovery.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = services_discovery.c; sourceTree = ""; }; + 5B8922BA0E76ABF500933BE0 /* tree.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tree.c; sourceTree = ""; }; + 5B8922BB0E76ABF500933BE0 /* thread.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = thread.c; sourceTree = ""; }; + 5B8922BC0E76ABF500933BE0 /* search.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = search.c; sourceTree = ""; }; + 5B8923610E76B0FA00933BE0 /* libc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = libc.c; sourceTree = ""; }; + 5B89236E0E76B14F00933BE0 /* video_text.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = video_text.c; sourceTree = ""; }; + 5B89236F0E76B14F00933BE0 /* vout_subpictures.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vout_subpictures.c; sourceTree = ""; }; + 5B8923700E76B14F00933BE0 /* vout_intf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vout_intf.c; sourceTree = ""; }; + 5B8923710E76B14F00933BE0 /* vout_pictures.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vout_pictures.h; sourceTree = ""; }; + 5B8923720E76B14F00933BE0 /* video_widgets.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = video_widgets.c; sourceTree = ""; }; + 5B8923730E76B14F00933BE0 /* vout_pictures.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vout_pictures.c; sourceTree = ""; }; + 5B8923740E76B14F00933BE0 /* video_output.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = video_output.c; sourceTree = ""; }; + 5B89237E0E76B25C00933BE0 /* es_format.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = es_format.c; sourceTree = ""; }; + 5B8923810E76B29C00933BE0 /* image.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = image.c; sourceTree = ""; }; + 5B8923840E76B2D400933BE0 /* events.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = events.c; sourceTree = ""; }; + 5B8923A70E76B39700933BE0 /* osd_text.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = osd_text.c; sourceTree = ""; }; + 5B8923A80E76B39700933BE0 /* osd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = osd.c; sourceTree = ""; }; + 5B8923A90E76B39700933BE0 /* osd_widgets.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = osd_widgets.c; sourceTree = ""; }; + 5B8923AE0E76B54000933BE0 /* libvlc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = libvlc.c; sourceTree = ""; }; + 5B8923CD0E76C1FB00933BE0 /* video.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = video.c; sourceTree = ""; }; + 5B8923CE0E76C1FB00933BE0 /* playlist.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = playlist.c; sourceTree = ""; }; + 5B8923CF0E76C1FB00933BE0 /* mediacontrol_util.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = mediacontrol_util.c; sourceTree = ""; }; + 5B8923D00E76C1FB00933BE0 /* mediacontrol_internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = mediacontrol_internal.h; sourceTree = ""; }; + 5B8923D10E76C1FB00933BE0 /* mediacontrol_core.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = mediacontrol_core.c; sourceTree = ""; }; + 5B8923D20E76C1FB00933BE0 /* mediacontrol_audio_video.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = mediacontrol_audio_video.c; sourceTree = ""; }; + 5B8923D30E76C1FB00933BE0 /* media_library.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = media_library.c; sourceTree = ""; }; + 5B8923D40E76C1FB00933BE0 /* vlm.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = vlm.c; sourceTree = ""; }; + 5B8923D50E76C1FB00933BE0 /* media_list.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = media_list.c; sourceTree = ""; }; + 5B8923D60E76C1FB00933BE0 /* libvlc_internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = libvlc_internal.h; sourceTree = ""; }; + 5B8923D70E76C1FB00933BE0 /* hierarchical_node_media_list_view.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = hierarchical_node_media_list_view.c; sourceTree = ""; }; + 5B8923D80E76C1FB00933BE0 /* flat_media_list_view.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = flat_media_list_view.c; sourceTree = ""; }; + 5B8923D90E76C1FB00933BE0 /* audio.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = audio.c; sourceTree = ""; }; + 5B8923DA0E76C1FB00933BE0 /* hierarchical_media_list_view.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = hierarchical_media_list_view.c; sourceTree = ""; }; + 5B8923DB0E76C1FB00933BE0 /* media_discoverer.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = media_discoverer.c; sourceTree = ""; }; + 5B8923DC0E76C1FB00933BE0 /* media.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = media.c; sourceTree = ""; }; + 5B8923DD0E76C1FB00933BE0 /* media_list_player.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = media_list_player.c; sourceTree = ""; }; + 5B8923DE0E76C1FB00933BE0 /* media_list_path.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = media_list_path.h; sourceTree = ""; }; + 5B8923DF0E76C1FC00933BE0 /* log.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = log.c; sourceTree = ""; }; + 5B8923E00E76C1FC00933BE0 /* media_player.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = media_player.c; sourceTree = ""; }; + 5B8923E10E76C1FC00933BE0 /* media_list_view.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = media_list_view.c; sourceTree = ""; }; + 5B8923E40E76C25B00933BE0 /* deprecated.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = deprecated.h; path = vlc/deprecated.h; sourceTree = ""; }; + 5B8923E50E76C25B00933BE0 /* libvlc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = libvlc.h; path = vlc/libvlc.h; sourceTree = ""; }; + 5B8923E60E76C25B00933BE0 /* libvlc_events.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = libvlc_events.h; path = vlc/libvlc_events.h; sourceTree = ""; }; + 5B8923E70E76C25B00933BE0 /* libvlc_media_list.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = libvlc_media_list.h; path = vlc/libvlc_media_list.h; sourceTree = ""; }; + 5B8923E80E76C25B00933BE0 /* libvlc_structures.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = libvlc_structures.h; path = vlc/libvlc_structures.h; sourceTree = ""; }; + 5B8923E90E76C25B00933BE0 /* libvlc_vlm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = libvlc_vlm.h; path = vlc/libvlc_vlm.h; sourceTree = ""; }; + 5B8923EA0E76C25B00933BE0 /* mediacontrol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mediacontrol.h; path = vlc/mediacontrol.h; sourceTree = ""; }; + 5B8923EB0E76C25B00933BE0 /* mediacontrol_structures.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mediacontrol_structures.h; path = vlc/mediacontrol_structures.h; sourceTree = ""; }; + 5B8923EC0E76C25B00933BE0 /* vlc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = vlc.h; path = vlc/vlc.h; sourceTree = ""; }; + 5B8923EF0E76C2B600933BE0 /* libvlc-module.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "libvlc-module.c"; sourceTree = ""; }; + 5B8924010E76C6FA00933BE0 /* version.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = version.c; sourceTree = ""; }; + 5B8924270E76C91200933BE0 /* darwin_specific.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = darwin_specific.c; sourceTree = ""; }; + 5B89244C0E76C9BF00933BE0 /* intf_eject.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = intf_eject.c; sourceTree = ""; }; + 5B89244D0E76C9BF00933BE0 /* interaction.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = interaction.c; sourceTree = ""; }; + 5B89244E0E76C9BF00933BE0 /* interface.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = interface.c; sourceTree = ""; }; + 5B89244F0E76C9BF00933BE0 /* interface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = interface.h; sourceTree = ""; }; + 5B89245A0E76CAAB00933BE0 /* filter_chain.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = filter_chain.c; sourceTree = ""; }; + 5B89247A0E76CB3500933BE0 /* chain.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = chain.c; sourceTree = ""; }; + 5B8924810E76CB6000933BE0 /* action.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = action.c; sourceTree = ""; }; + 5B8924840E76CB9C00933BE0 /* vlmshell.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vlmshell.c; sourceTree = ""; }; + 5B8924890E76D61F00933BE0 /* cmdline.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmdline.c; sourceTree = ""; }; + 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 1D60588F0D05DD3D006BFB54 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */, + 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */, + 5B6BDF380E7569D90008C579 /* MediaPlayer.framework in Frameworks */, + 5B891EA40E75875D00933BE0 /* CoreGraphics.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 080E96DDFE201D6D7F000001 /* Classes */ = { + isa = PBXGroup; + children = ( + 5B6BDD2A0E74F12D0008C579 /* TCPSocket.h */, + 5B6BDD2B0E74F12D0008C579 /* TCPSocket.m */, + 1D3623240D0F684500981E51 /* TuveAppDelegate.h */, + 1D3623250D0F684500981E51 /* TuveAppDelegate.m */, + 5B6BDD5D0E74F64D0008C579 /* TuveConnection.h */, + 5B6BDD5E0E74F64D0008C579 /* TuveConnection.m */, + 5B891E9A0E75829900933BE0 /* ChatController.h */, + 5B891E9B0E75829900933BE0 /* ChatController.m */, + ); + path = Classes; + sourceTree = ""; + }; + 19C28FACFE9D520D11CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 1D6058910D05DD3D006BFB54 /* Tuvé.app */, + ); + name = Products; + sourceTree = ""; + }; + 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = { + isa = PBXGroup; + children = ( + 5B891F220E75951600933BE0 /* VLC */, + 080E96DDFE201D6D7F000001 /* Classes */, + 29B97315FDCFA39411CA2CEA /* Other Sources */, + 29B97317FDCFA39411CA2CEA /* Resources */, + 29B97323FDCFA39411CA2CEA /* Frameworks */, + 19C28FACFE9D520D11CA2CBB /* Products */, + ); + name = CustomTemplate; + sourceTree = ""; + }; + 29B97315FDCFA39411CA2CEA /* Other Sources */ = { + isa = PBXGroup; + children = ( + 32CA4F630368D1EE00C91783 /* Tuve_Prefix.pch */, + 29B97316FDCFA39411CA2CEA /* main.m */, + ); + name = "Other Sources"; + sourceTree = ""; + }; + 29B97317FDCFA39411CA2CEA /* Resources */ = { + isa = PBXGroup; + children = ( + 5B6BDD0A0E74EC510008C579 /* icon.png */, + 28AD733E0D9D9553002E5188 /* MainWindow.xib */, + 8D1107310486CEB800E47090 /* Info.plist */, + 5B891DE90E756EE900933BE0 /* Default.png */, + ); + name = Resources; + sourceTree = ""; + }; + 29B97323FDCFA39411CA2CEA /* Frameworks */ = { + isa = PBXGroup; + children = ( + 5B891EA30E75875D00933BE0 /* CoreGraphics.framework */, + 5B6BDF370E7569D90008C579 /* MediaPlayer.framework */, + 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */, + 1D30AB110D05D00D00671497 /* Foundation.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 5B891F220E75951600933BE0 /* VLC */ = { + isa = PBXGroup; + children = ( + 5B8924490E76C9A600933BE0 /* interface */, + 5B8924010E76C6FA00933BE0 /* version.c */, + 5B8923EF0E76C2B600933BE0 /* libvlc-module.c */, + 5B8923B40E76C1EF00933BE0 /* control */, + 5B8923AE0E76B54000933BE0 /* libvlc.c */, + 5B8923A40E76B38600933BE0 /* osd */, + 5B8923660E76B14000933BE0 /* video output */, + 5B8922A70E76ABE700933BE0 /* playlist */, + 5B8922850E76AAF700933BE0 /* stream_output */, + 5B8922610E76AACB00933BE0 /* output */, + 5B89223C0E76AA5700933BE0 /* input */, + 5B8922140E76A93800933BE0 /* text */, + 5B8920360E763CE200933BE0 /* network */, + 5B891FE00E759ADA00933BE0 /* config */, + 5B891FD60E759A7B00933BE0 /* Modules */, + 5B891FBB0E75982300933BE0 /* libvlc.h */, + 5B891FB20E7597F900933BE0 /* Misc */, + 5B891F320E75955400933BE0 /* include */, + 5B891F230E75952100933BE0 /* rtmp */, + ); + path = VLC; + sourceTree = ""; + }; + 5B891F230E75952100933BE0 /* rtmp */ = { + isa = PBXGroup; + children = ( + 5B891F2C0E75952B00933BE0 /* access.c */, + 5B891F2D0E75952B00933BE0 /* rtmp_amf_flv.c */, + 5B891F2E0E75952B00933BE0 /* rtmp_amf_flv.h */, + ); + name = rtmp; + sourceTree = ""; + }; + 5B891F320E75955400933BE0 /* include */ = { + isa = PBXGroup; + children = ( + 5B8923E40E76C25B00933BE0 /* deprecated.h */, + 5B8923E50E76C25B00933BE0 /* libvlc.h */, + 5B8923E60E76C25B00933BE0 /* libvlc_events.h */, + 5B8923E70E76C25B00933BE0 /* libvlc_media_list.h */, + 5B8923E80E76C25B00933BE0 /* libvlc_structures.h */, + 5B8923E90E76C25B00933BE0 /* libvlc_vlm.h */, + 5B8923EA0E76C25B00933BE0 /* mediacontrol.h */, + 5B8923EB0E76C25B00933BE0 /* mediacontrol_structures.h */, + 5B8923EC0E76C25B00933BE0 /* vlc.h */, + 5B891F360E75956C00933BE0 /* vlc_messages.h */, + 5B891F370E75956C00933BE0 /* vlc_input.h */, + 5B891F380E75956C00933BE0 /* vlc_md5.h */, + 5B891F390E75956C00933BE0 /* vlc_intf_strings.h */, + 5B891F3A0E75956C00933BE0 /* vlc_rand.h */, + 5B891F3B0E75956C00933BE0 /* vlc_plugin.h */, + 5B891F3C0E75956C00933BE0 /* vlc_block.h */, + 5B891F3D0E75956C00933BE0 /* vlc_interface.h */, + 5B891F3E0E75956C00933BE0 /* vlc_playlist.h */, + 5B891F3F0E75956C00933BE0 /* vlc_pgpkey.h */, + 5B891F400E75956C00933BE0 /* vlc_xml.h */, + 5B891F410E75956C00933BE0 /* vlc_window.h */, + 5B891F420E75956C00933BE0 /* vlc_vout.h */, + 5B891F430E75956C00933BE0 /* vlc_vod.h */, + 5B891F440E75956C00933BE0 /* vlc_configuration.h */, + 5B891F450E75956C00933BE0 /* vlc_tls.h */, + 5B891F460E75956C00933BE0 /* vlc_threads.h */, + 5B891F470E75956C00933BE0 /* vlc_common.h */, + 5B891F480E75956C00933BE0 /* vlc_network.h */, + 5B891F490E75956C00933BE0 /* vlc_mtime.h */, + 5B891F4A0E75956C00933BE0 /* vlc_modules.h */, + 5B891F4B0E75956C00933BE0 /* vlc_meta.h */, + 5B891F4C0E75956C00933BE0 /* vlc_sout.h */, + 5B891F4D0E75956C00933BE0 /* vlc_services_discovery.h */, + 5B891F4E0E75956C00933BE0 /* vlc_arrays.h */, + 5B891F4F0E75956C00933BE0 /* vlc_epg.h */, + 5B891F500E75956C00933BE0 /* vlc_fixups.h */, + 5B891F510E75956C00933BE0 /* vlc_config.h */, + 5B891F520E75956C00933BE0 /* vlc_url.h */, + 5B891F530E75956C00933BE0 /* vlc_update.h */, + 5B891F540E75956C00933BE0 /* vlc_main.h */, + 5B891F550E75956C00933BE0 /* vlc_keys.h */, + 5B891F560E75956C00933BE0 /* vlc_aout.h */, + 5B891F620E75956C00933BE0 /* vlc_es.h */, + 5B891F630E75956C00933BE0 /* vlc_block_helper.h */, + 5B891F640E75956C00933BE0 /* vlc_strings.h */, + 5B891F650E75956C00933BE0 /* vlc_stream.h */, + 5B891F660E75956C00933BE0 /* vlc_codec_synchro.h */, + 5B891F670E75956C00933BE0 /* vlc_bits.h */, + 5B891F680E75956C00933BE0 /* vlc_charset.h */, + 5B891F690E75956C00933BE0 /* vlc_events.h */, + 5B891F6A0E75956C00933BE0 /* vlc_gcrypt.h */, + 5B891F6B0E75956C00933BE0 /* vlc_devices.h */, + 5B891F6C0E75956C00933BE0 /* vlc_iso_lang.h */, + 5B891F6D0E75956C00933BE0 /* vlc_image.h */, + 5B891F6E0E75956C00933BE0 /* vlc_config_cat.h */, + 5B891F6F0E75956C00933BE0 /* vlc_filter.h */, + 5B891F700E75956C00933BE0 /* vlc_httpd.h */, + 5B891F710E75956C00933BE0 /* vlc_osd.h */, + 5B891F720E75956C00933BE0 /* vlc_objects.h */, + 5B891F730E75956C00933BE0 /* vlc_vlm.h */, + 5B891F740E75956C00933BE0 /* vlc_variables.h */, + 5B891F750E75956C00933BE0 /* vlc_demux.h */, + 5B891F760E75956C00933BE0 /* mmx.h */, + 5B891F770E75956C00933BE0 /* vlc_acl.h */, + 5B891F780E75956C00933BE0 /* vlc_codecs.h */, + 5B891F790E75956C00933BE0 /* vlc_access.h */, + 5B891F7A0E75956C00933BE0 /* vlc_codec.h */, + 5B891F7B0E75956C00933BE0 /* vlc_es_out.h */, + ); + name = include; + sourceTree = ""; + }; + 5B891FB20E7597F900933BE0 /* Misc */ = { + isa = PBXGroup; + children = ( + 5B8924810E76CB6000933BE0 /* action.c */, + 5B89245A0E76CAAB00933BE0 /* filter_chain.c */, + 5B8924270E76C91200933BE0 /* darwin_specific.c */, + 5B8923840E76B2D400933BE0 /* events.c */, + 5B8923810E76B29C00933BE0 /* image.c */, + 5B89237E0E76B25C00933BE0 /* es_format.c */, + 5B8923610E76B0FA00933BE0 /* libc.c */, + 5B8921B70E76A6A800933BE0 /* mtime.c */, + 5B8920B60E76416000933BE0 /* cpu.c */, + 5B8920A70E76409700933BE0 /* error.c */, + 5B8920A40E76407800933BE0 /* variables.c */, + 5B89209D0E763FEE00933BE0 /* block.c */, + 5B89209A0E763FA100933BE0 /* variables.h */, + 5B89207C0E763EA300933BE0 /* objects.c */, + 5B891FBD0E75985900933BE0 /* messages.c */, + 5B891FB00E7597F600933BE0 /* threads.c */, + ); + name = Misc; + sourceTree = ""; + }; + 5B891FD60E759A7B00933BE0 /* Modules */ = { + isa = PBXGroup; + children = ( + 5B8920CD0E76A4A700933BE0 /* stats.c */, + 5B8920270E763B7A00933BE0 /* cache.c */, + 5B891FF20E763A9900933BE0 /* os.c */, + 5B891FEF0E763A4B00933BE0 /* builtin.h */, + 5B891FEA0E7639E500933BE0 /* modules.c */, + 5B891FDD0E759A8600933BE0 /* modules.h */, + 5B891FCE0E759A4C00933BE0 /* entry.c */, + ); + name = Modules; + sourceTree = ""; + }; + 5B891FE00E759ADA00933BE0 /* config */ = { + isa = PBXGroup; + children = ( + 5B8924890E76D61F00933BE0 /* cmdline.c */, + 5B89247A0E76CB3500933BE0 /* chain.c */, + 5B8921DB0E76A80800933BE0 /* dirs.c */, + 5B8921CA0E76A70F00933BE0 /* file.c */, + 5B8920A10E76404300933BE0 /* core.c */, + 5B891FE70E759AE600933BE0 /* configuration.h */, + ); + name = config; + sourceTree = ""; + }; + 5B8920360E763CE200933BE0 /* network */ = { + isa = PBXGroup; + children = ( + 5B8920380E763CF500933BE0 /* tcp.c */, + 5B8920390E763CF500933BE0 /* poll.c */, + 5B89203A0E763CF500933BE0 /* rootbind.c */, + 5B89203B0E763CF500933BE0 /* httpd.c */, + 5B89203C0E763CF500933BE0 /* tls.c */, + 5B89203D0E763CF500933BE0 /* udp.c */, + 5B89203E0E763CF500933BE0 /* acl.c */, + 5B89203F0E763CF500933BE0 /* getaddrinfo.c */, + 5B8920410E763CF500933BE0 /* io.c */, + ); + name = network; + sourceTree = ""; + }; + 5B8922140E76A93800933BE0 /* text */ = { + isa = PBXGroup; + children = ( + 5B8922200E76A95700933BE0 /* iso-639_def.h */, + 5B8922150E76A94700933BE0 /* charset.c */, + 5B8922160E76A94700933BE0 /* iso_lang.c */, + 5B8922170E76A94700933BE0 /* strings.c */, + 5B8922180E76A94700933BE0 /* unicode.c */, + 5B8922190E76A94700933BE0 /* wincp.c */, + ); + name = text; + sourceTree = ""; + }; + 5B89223C0E76AA5700933BE0 /* input */ = { + isa = PBXGroup; + children = ( + 5B89225D0E76AA7800933BE0 /* access.c */, + 5B89223D0E76AA7100933BE0 /* clock.c */, + 5B89223E0E76AA7100933BE0 /* control.c */, + 5B89223F0E76AA7100933BE0 /* decoder.c */, + 5B8922400E76AA7100933BE0 /* decoder_synchro.c */, + 5B8922410E76AA7100933BE0 /* demux.c */, + 5B8922420E76AA7100933BE0 /* es_out.c */, + 5B8922430E76AA7100933BE0 /* input.c */, + 5B8922440E76AA7100933BE0 /* input_internal.h */, + 5B8922450E76AA7100933BE0 /* item.c */, + 5B8922460E76AA7100933BE0 /* mem_stream.c */, + 5B8922470E76AA7100933BE0 /* meta.c */, + 5B8922480E76AA7100933BE0 /* stream.c */, + 5B8922490E76AA7100933BE0 /* subtitles.c */, + 5B89224A0E76AA7100933BE0 /* var.c */, + 5B89224B0E76AA7100933BE0 /* vlm.c */, + 5B89224C0E76AA7100933BE0 /* vlm_internal.h */, + 5B8924840E76CB9C00933BE0 /* vlmshell.c */, + ); + name = input; + sourceTree = ""; + }; + 5B8922610E76AACB00933BE0 /* output */ = { + isa = PBXGroup; + children = ( + 5B8922620E76AAE100933BE0 /* aout_internal.h */, + 5B8922630E76AAE100933BE0 /* common.c */, + 5B8922640E76AAE100933BE0 /* dec.c */, + 5B8922650E76AAE100933BE0 /* filters.c */, + 5B8922660E76AAE100933BE0 /* input.c */, + 5B8922670E76AAE100933BE0 /* intf.c */, + 5B8922680E76AAE100933BE0 /* mixer.c */, + 5B8922690E76AAE100933BE0 /* output.c */, + ); + name = output; + sourceTree = ""; + }; + 5B8922850E76AAF700933BE0 /* stream_output */ = { + isa = PBXGroup; + children = ( + 5B8922860E76AB0200933BE0 /* announce.c */, + 5B8922870E76AB0200933BE0 /* sap.c */, + 5B8922880E76AB0200933BE0 /* sdp.c */, + 5B8922890E76AB0200933BE0 /* stream_output.c */, + 5B89228A0E76AB0200933BE0 /* stream_output.h */, + ); + name = stream_output; + sourceTree = ""; + }; + 5B8922A70E76ABE700933BE0 /* playlist */ = { + isa = PBXGroup; + children = ( + 5B8922B30E76ABF500933BE0 /* playlist_internal.h */, + 5B8922B40E76ABF500933BE0 /* loadsave.c */, + 5B8922B50E76ABF500933BE0 /* item.c */, + 5B8922B60E76ABF500933BE0 /* engine.c */, + 5B8922B70E76ABF500933BE0 /* control.c */, + 5B8922B80E76ABF500933BE0 /* sort.c */, + 5B8922B90E76ABF500933BE0 /* services_discovery.c */, + 5B8922BA0E76ABF500933BE0 /* tree.c */, + 5B8922BB0E76ABF500933BE0 /* thread.c */, + 5B8922BC0E76ABF500933BE0 /* search.c */, + ); + name = playlist; + sourceTree = ""; + }; + 5B8923660E76B14000933BE0 /* video output */ = { + isa = PBXGroup; + children = ( + 5B89236E0E76B14F00933BE0 /* video_text.c */, + 5B89236F0E76B14F00933BE0 /* vout_subpictures.c */, + 5B8923700E76B14F00933BE0 /* vout_intf.c */, + 5B8923710E76B14F00933BE0 /* vout_pictures.h */, + 5B8923720E76B14F00933BE0 /* video_widgets.c */, + 5B8923730E76B14F00933BE0 /* vout_pictures.c */, + 5B8923740E76B14F00933BE0 /* video_output.c */, + ); + name = "video output"; + sourceTree = ""; + }; + 5B8923A40E76B38600933BE0 /* osd */ = { + isa = PBXGroup; + children = ( + 5B8923A70E76B39700933BE0 /* osd_text.c */, + 5B8923A80E76B39700933BE0 /* osd.c */, + 5B8923A90E76B39700933BE0 /* osd_widgets.c */, + ); + name = osd; + sourceTree = ""; + }; + 5B8923B40E76C1EF00933BE0 /* control */ = { + isa = PBXGroup; + children = ( + 5B8923CD0E76C1FB00933BE0 /* video.c */, + 5B8923CE0E76C1FB00933BE0 /* playlist.c */, + 5B8923CF0E76C1FB00933BE0 /* mediacontrol_util.c */, + 5B8923D00E76C1FB00933BE0 /* mediacontrol_internal.h */, + 5B8923D10E76C1FB00933BE0 /* mediacontrol_core.c */, + 5B8923D20E76C1FB00933BE0 /* mediacontrol_audio_video.c */, + 5B8923D30E76C1FB00933BE0 /* media_library.c */, + 5B8923D40E76C1FB00933BE0 /* vlm.c */, + 5B8923D50E76C1FB00933BE0 /* media_list.c */, + 5B8923D60E76C1FB00933BE0 /* libvlc_internal.h */, + 5B8923D70E76C1FB00933BE0 /* hierarchical_node_media_list_view.c */, + 5B8923D80E76C1FB00933BE0 /* flat_media_list_view.c */, + 5B8923D90E76C1FB00933BE0 /* audio.c */, + 5B8923DA0E76C1FB00933BE0 /* hierarchical_media_list_view.c */, + 5B8923DB0E76C1FB00933BE0 /* media_discoverer.c */, + 5B8923DC0E76C1FB00933BE0 /* media.c */, + 5B8923DD0E76C1FB00933BE0 /* media_list_player.c */, + 5B8923DE0E76C1FB00933BE0 /* media_list_path.h */, + 5B8923DF0E76C1FC00933BE0 /* log.c */, + 5B8923E00E76C1FC00933BE0 /* media_player.c */, + 5B8923E10E76C1FC00933BE0 /* media_list_view.c */, + ); + name = control; + sourceTree = ""; + }; + 5B8924490E76C9A600933BE0 /* interface */ = { + isa = PBXGroup; + children = ( + 5B89244C0E76C9BF00933BE0 /* intf_eject.c */, + 5B89244D0E76C9BF00933BE0 /* interaction.c */, + 5B89244E0E76C9BF00933BE0 /* interface.c */, + 5B89244F0E76C9BF00933BE0 /* interface.h */, + ); + name = interface; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 1D6058900D05DD3D006BFB54 /* Tuvé */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "Tuvé" */; + buildPhases = ( + 1D60588D0D05DD3D006BFB54 /* Resources */, + 1D60588E0D05DD3D006BFB54 /* Sources */, + 1D60588F0D05DD3D006BFB54 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Tuvé"; + productName = Tuve; + productReference = 1D6058910D05DD3D006BFB54 /* Tuvé.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 29B97313FDCFA39411CA2CEA /* Project object */ = { + isa = PBXProject; + buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Tuve" */; + compatibilityVersion = "Xcode 3.1"; + hasScannedForEncodings = 1; + mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 1D6058900D05DD3D006BFB54 /* Tuvé */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 1D60588D0D05DD3D006BFB54 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 28AD733F0D9D9553002E5188 /* MainWindow.xib in Resources */, + 5B6BDD0B0E74EC510008C579 /* icon.png in Resources */, + 5B891DEA0E756EE900933BE0 /* Default.png in Resources */, + 5B891FF00E763A4B00933BE0 /* builtin.h in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 1D60588E0D05DD3D006BFB54 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1D60589B0D05DD56006BFB54 /* main.m in Sources */, + 1D3623260D0F684500981E51 /* TuveAppDelegate.m in Sources */, + 5B6BDD2C0E74F12D0008C579 /* TCPSocket.m in Sources */, + 5B6BDD5F0E74F64D0008C579 /* TuveConnection.m in Sources */, + 5B891E9C0E75829900933BE0 /* ChatController.m in Sources */, + 5B891F2F0E75952B00933BE0 /* access.c in Sources */, + 5B891F300E75952B00933BE0 /* rtmp_amf_flv.c in Sources */, + 5B891FB10E7597F600933BE0 /* threads.c in Sources */, + 5B891FBE0E75985900933BE0 /* messages.c in Sources */, + 5B891FCF0E759A4C00933BE0 /* entry.c in Sources */, + 5B891FEB0E7639E500933BE0 /* modules.c in Sources */, + 5B891FF30E763A9900933BE0 /* os.c in Sources */, + 5B8920280E763B7A00933BE0 /* cache.c in Sources */, + 5B8920420E763CF500933BE0 /* tcp.c in Sources */, + 5B8920430E763CF500933BE0 /* poll.c in Sources */, + 5B8920440E763CF500933BE0 /* rootbind.c in Sources */, + 5B8920450E763CF500933BE0 /* httpd.c in Sources */, + 5B8920460E763CF500933BE0 /* tls.c in Sources */, + 5B8920470E763CF500933BE0 /* udp.c in Sources */, + 5B8920480E763CF500933BE0 /* acl.c in Sources */, + 5B8920490E763CF500933BE0 /* getaddrinfo.c in Sources */, + 5B89204B0E763CF500933BE0 /* io.c in Sources */, + 5B89207D0E763EA300933BE0 /* objects.c in Sources */, + 5B89209E0E763FEE00933BE0 /* block.c in Sources */, + 5B8920A20E76404300933BE0 /* core.c in Sources */, + 5B8920A50E76407800933BE0 /* variables.c in Sources */, + 5B8920A80E76409700933BE0 /* error.c in Sources */, + 5B8920B70E76416000933BE0 /* cpu.c in Sources */, + 5B8920CE0E76A4A700933BE0 /* stats.c in Sources */, + 5B8921B80E76A6A800933BE0 /* mtime.c in Sources */, + 5B8921CB0E76A70F00933BE0 /* file.c in Sources */, + 5B8921DC0E76A80800933BE0 /* dirs.c in Sources */, + 5B89221A0E76A94700933BE0 /* charset.c in Sources */, + 5B89221B0E76A94700933BE0 /* iso_lang.c in Sources */, + 5B89221C0E76A94700933BE0 /* strings.c in Sources */, + 5B89221D0E76A94700933BE0 /* unicode.c in Sources */, + 5B89221E0E76A94700933BE0 /* wincp.c in Sources */, + 5B89224E0E76AA7100933BE0 /* clock.c in Sources */, + 5B89224F0E76AA7100933BE0 /* control.c in Sources */, + 5B8922500E76AA7100933BE0 /* decoder.c in Sources */, + 5B8922510E76AA7100933BE0 /* decoder_synchro.c in Sources */, + 5B8922520E76AA7100933BE0 /* demux.c in Sources */, + 5B8922530E76AA7100933BE0 /* es_out.c in Sources */, + 5B8922540E76AA7100933BE0 /* input.c in Sources */, + 5B8922550E76AA7100933BE0 /* item.c in Sources */, + 5B8922560E76AA7100933BE0 /* mem_stream.c in Sources */, + 5B8922570E76AA7100933BE0 /* meta.c in Sources */, + 5B8922580E76AA7100933BE0 /* stream.c in Sources */, + 5B8922590E76AA7100933BE0 /* subtitles.c in Sources */, + 5B89225A0E76AA7100933BE0 /* var.c in Sources */, + 5B89225B0E76AA7100933BE0 /* vlm.c in Sources */, + 5B89225E0E76AA7800933BE0 /* access.c in Sources */, + 5B89226A0E76AAE100933BE0 /* common.c in Sources */, + 5B89226B0E76AAE100933BE0 /* dec.c in Sources */, + 5B89226C0E76AAE100933BE0 /* filters.c in Sources */, + 5B89226D0E76AAE100933BE0 /* input.c in Sources */, + 5B89226E0E76AAE100933BE0 /* intf.c in Sources */, + 5B89226F0E76AAE100933BE0 /* mixer.c in Sources */, + 5B8922700E76AAE100933BE0 /* output.c in Sources */, + 5B89228B0E76AB0200933BE0 /* announce.c in Sources */, + 5B89228C0E76AB0200933BE0 /* sap.c in Sources */, + 5B89228D0E76AB0200933BE0 /* sdp.c in Sources */, + 5B89228E0E76AB0200933BE0 /* stream_output.c in Sources */, + 5B8922BD0E76ABF500933BE0 /* loadsave.c in Sources */, + 5B8922BE0E76ABF500933BE0 /* item.c in Sources */, + 5B8922BF0E76ABF500933BE0 /* engine.c in Sources */, + 5B8922C00E76ABF500933BE0 /* control.c in Sources */, + 5B8922C10E76ABF500933BE0 /* sort.c in Sources */, + 5B8922C20E76ABF500933BE0 /* services_discovery.c in Sources */, + 5B8922C30E76ABF500933BE0 /* tree.c in Sources */, + 5B8922C40E76ABF500933BE0 /* thread.c in Sources */, + 5B8922C50E76ABF500933BE0 /* search.c in Sources */, + 5B8923620E76B0FA00933BE0 /* libc.c in Sources */, + 5B8923750E76B14F00933BE0 /* video_text.c in Sources */, + 5B8923760E76B14F00933BE0 /* vout_subpictures.c in Sources */, + 5B8923770E76B14F00933BE0 /* vout_intf.c in Sources */, + 5B8923780E76B14F00933BE0 /* video_widgets.c in Sources */, + 5B8923790E76B14F00933BE0 /* vout_pictures.c in Sources */, + 5B89237A0E76B14F00933BE0 /* video_output.c in Sources */, + 5B89237F0E76B25C00933BE0 /* es_format.c in Sources */, + 5B8923820E76B29C00933BE0 /* image.c in Sources */, + 5B8923850E76B2D400933BE0 /* events.c in Sources */, + 5B8923AA0E76B39700933BE0 /* osd_text.c in Sources */, + 5B8923AB0E76B39700933BE0 /* osd.c in Sources */, + 5B8923AC0E76B39700933BE0 /* osd_widgets.c in Sources */, + 5B8923AF0E76B54000933BE0 /* libvlc.c in Sources */, + 5B8923F00E76C2B600933BE0 /* libvlc-module.c in Sources */, + 5B8924020E76C6FA00933BE0 /* version.c in Sources */, + 5B8924280E76C91200933BE0 /* darwin_specific.c in Sources */, + 5B8924500E76C9BF00933BE0 /* intf_eject.c in Sources */, + 5B8924510E76C9BF00933BE0 /* interaction.c in Sources */, + 5B8924520E76C9BF00933BE0 /* interface.c in Sources */, + 5B89245B0E76CAAB00933BE0 /* filter_chain.c in Sources */, + 5B89247B0E76CB3500933BE0 /* chain.c in Sources */, + 5B8924820E76CB6000933BE0 /* action.c in Sources */, + 5B8924850E76CB9C00933BE0 /* vlmshell.c in Sources */, + 5B89248A0E76D61F00933BE0 /* cmdline.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1D6058940D05DD3E006BFB54 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: Philippe Hausler"; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = Tuve_Prefix.pch; + INFOPLIST_FILE = Info.plist; + PRODUCT_NAME = "Tuvé"; + PROVISIONING_PROFILE = "0B176FC1-E6D9-41D1-91CA-8EA1BFE13C7A"; + }; + name = Debug; + }; + 1D6058950D05DD3E006BFB54 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: Philippe Hausler"; + COPY_PHASE_STRIP = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = Tuve_Prefix.pch; + INFOPLIST_FILE = Info.plist; + PRODUCT_NAME = "Tuvé"; + PROVISIONING_PROFILE = "0B176FC1-E6D9-41D1-91CA-8EA1BFE13C7A"; + }; + name = Release; + }; + C01FCF4F08A954540054247B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + ONLY_ACTIVE_ARCH = YES; + PREBINDING = NO; + SDKROOT = iphoneos2.0; + }; + name = Debug; + }; + C01FCF5008A954540054247B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + PREBINDING = NO; + SDKROOT = iphoneos2.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "Tuvé" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1D6058940D05DD3E006BFB54 /* Debug */, + 1D6058950D05DD3E006BFB54 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Tuve" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C01FCF4F08A954540054247B /* Debug */, + C01FCF5008A954540054247B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; +} diff --git a/Tuve_Prefix.pch b/Tuve_Prefix.pch new file mode 100644 index 0000000..0d6e3af --- /dev/null +++ b/Tuve_Prefix.pch @@ -0,0 +1,8 @@ +// +// Prefix header for all source files of the 'Tuve' target in the 'Tuve' project +// + +#ifdef __OBJC__ +#import +#import +#endif diff --git a/VLC/.DS_Store b/VLC/.DS_Store new file mode 100644 index 0000000..0ccd6a7 --- /dev/null +++ b/VLC/.DS_Store Binary files differ diff --git a/VLC/access.c b/VLC/access.c new file mode 100644 index 0000000..2969129 --- /dev/null +++ b/VLC/access.c @@ -0,0 +1,547 @@ +/***************************************************************************** + * access.c: RTMP input. + ***************************************************************************** + * Copyright (C) URJC - LADyR - Luis Lopez Fernandez + * + * Author: Miguel Angel Cabrera Moya + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "vlc_plugin.h" +#include "vlc_access.h" + +#include "vlc_network.h" /* DOWN: #include */ +#include "vlc_url.h" +#include "vlc_block.h" + +#include "rtmp_amf_flv.h" + +/***************************************************************************** + * Module descriptor + *****************************************************************************/ +#define CACHING_TEXT N_("Caching value in ms") +#define CACHING_LONGTEXT N_( \ + "Caching value for RTMP streams. This " \ + "value should be set in milliseconds." ) + +static int Open ( vlc_object_t * ); +static void Close( vlc_object_t * ); + +vlc_module_begin(); + set_description( N_("RTMP input") ); + set_shortname( N_("RTMP") ); + set_category( CAT_INPUT ); + set_subcategory( SUBCAT_INPUT_ACCESS ); + + add_integer( "rtmp-caching", DEFAULT_PTS_DELAY / 1000, NULL, CACHING_TEXT, + CACHING_LONGTEXT, true ); + + set_capability( "access", 0 ); + set_callbacks( Open, Close ); + add_shortcut( "rtmp" ); +vlc_module_end(); + + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +static ssize_t Read( access_t *, uint8_t *, size_t ); +static int Seek( access_t *, int64_t ); +static int Control( access_t *, int, va_list ); + +static void* ThreadControl( vlc_object_t * ); + +/***************************************************************************** + * Open: open the rtmp connection + *****************************************************************************/ +static int Open( vlc_object_t *p_this ) +{ + access_t *p_access = (access_t *) p_this; + access_sys_t *p_sys; + char *psz, *p; + int length_path, length_media_name; + int i; + + STANDARD_READ_ACCESS_INIT + + p_sys->p_thread = + vlc_object_create( p_access, sizeof( rtmp_control_thread_t ) ); + if( !p_sys->p_thread ) + return VLC_ENOMEM; + vlc_object_attach( p_sys->p_thread, p_access ); + + /* Parse URI - remove spaces */ + p = psz = strdup( p_access->psz_path ); + while( (p = strchr( p, ' ' )) != NULL ) + *p = '+'; + vlc_UrlParse( &p_sys->p_thread->url, psz, 0 ); + free( psz ); + + if( p_sys->p_thread->url.psz_host == NULL + || *p_sys->p_thread->url.psz_host == '\0' ) + { + msg_Warn( p_access, "invalid host" ); + goto error; + } + + if( p_sys->p_thread->url.i_port <= 0 ) + p_sys->p_thread->url.i_port = 1935; + + if( p_sys->p_thread->url.psz_path == NULL ) + { + msg_Warn( p_access, "invalid path" ); + goto error; + } + + length_path = strlen( p_sys->p_thread->url.psz_path ); + length_media_name = strlen( strrchr( p_sys->p_thread->url.psz_path, '/' ) ) - 1; + + p_sys->p_thread->psz_application = strndup( p_sys->p_thread->url.psz_path + 1, length_path - length_media_name - 2 ); + p_sys->p_thread->psz_media = strdup( p_sys->p_thread->url.psz_path + ( length_path - length_media_name ) ); + + msg_Dbg( p_access, "rtmp: host='%s' port=%d path='%s'", + p_sys->p_thread->url.psz_host, p_sys->p_thread->url.i_port, p_sys->p_thread->url.psz_path ); + + if( p_sys->p_thread->url.psz_username && *p_sys->p_thread->url.psz_username ) + { + msg_Dbg( p_access, " user='%s', pwd='%s'", + p_sys->p_thread->url.psz_username, p_sys->p_thread->url.psz_password ); + } + + /* Initialize thread variables */ + p_sys->p_thread->b_die = 0; + p_sys->p_thread->b_error= 0; + p_sys->p_thread->p_fifo_input = block_FifoNew(); + p_sys->p_thread->p_empty_blocks = block_FifoNew(); + p_sys->p_thread->has_audio = 0; + p_sys->p_thread->has_video = 0; + p_sys->p_thread->metadata_received = 0; + p_sys->p_thread->first_media_packet = 1; + p_sys->p_thread->flv_tag_previous_tag_size = 0x00000000; /* FLV_TAG_FIRST_PREVIOUS_TAG_SIZE */ + p_sys->p_thread->chunk_size_recv = 128; /* RTMP_DEFAULT_CHUNK_SIZE */ + p_sys->p_thread->chunk_size_send = 128; /* RTMP_DEFAULT_CHUNK_SIZE */ + for(i = 0; i < 64; i++) + { + memset( &p_sys->p_thread->rtmp_headers_recv[i], 0, sizeof( rtmp_packet_t ) ); + p_sys->p_thread->rtmp_headers_send[i].length_header = -1; + p_sys->p_thread->rtmp_headers_send[i].stream_index = -1; + p_sys->p_thread->rtmp_headers_send[i].timestamp = -1; + p_sys->p_thread->rtmp_headers_send[i].timestamp_relative = -1; + p_sys->p_thread->rtmp_headers_send[i].length_encoded = -1; + p_sys->p_thread->rtmp_headers_send[i].length_body = -1; + p_sys->p_thread->rtmp_headers_send[i].content_type = -1; + p_sys->p_thread->rtmp_headers_send[i].src_dst = -1; + p_sys->p_thread->rtmp_headers_send[i].body = NULL; + } + + p_sys->p_thread->p_base_object = p_this; + + vlc_cond_init( p_sys->p_thread, &p_sys->p_thread->wait ); + + vlc_mutex_init( &p_sys->p_thread->lock ); + + p_sys->p_thread->result_connect = 1; + p_sys->p_thread->result_play = 1; + p_sys->p_thread->result_stop = 0; + + /* Open connection */ + p_sys->p_thread->fd = net_ConnectTCP( p_access, p_sys->p_thread->url.psz_host, p_sys->p_thread->url.i_port ); + if( p_sys->p_thread->fd == -1 ) + { + int *p_fd_listen; + + msg_Warn( p_access, "cannot connect to %s:%d", p_sys->p_thread->url.psz_host, p_sys->p_thread->url.i_port ); + msg_Dbg( p_access, "switching to passive mode" ); + + p_sys->active = 0; + + p_fd_listen = net_ListenTCP( p_access, p_sys->p_thread->url.psz_host, p_sys->p_thread->url.i_port ); + if( p_fd_listen == NULL ) + { + msg_Err( p_access, "cannot listen to %s port %i", p_sys->p_thread->url.psz_host, p_sys->p_thread->url.i_port ); + goto error2; + } + + p_sys->p_thread->fd = net_Accept( p_access, p_fd_listen, -1 ); + + net_ListenClose( p_fd_listen ); + + if( rtmp_handshake_passive( p_this, p_sys->p_thread->fd ) < 0 ) + { + msg_Err( p_access, "handshake passive failed"); + goto error2; + } + + p_sys->p_thread->result_publish = 1; + } + else + { + p_sys->active = 1; + + if( rtmp_handshake_active( p_this, p_sys->p_thread->fd ) < 0 ) + { + msg_Err( p_access, "handshake active failed"); + goto error2; + } + + p_sys->p_thread->result_publish = 0; + } + + if( vlc_thread_create( p_sys->p_thread, "rtmp control thread", ThreadControl, + VLC_THREAD_PRIORITY_INPUT, false ) ) + { + msg_Err( p_access, "cannot spawn rtmp control thread" ); + goto error2; + } + + if( p_sys->active ) + { + if( rtmp_connect_active( p_sys->p_thread ) < 0 ) + { + msg_Err( p_access, "connect active failed"); + goto error2; + } + } + + /* Set vars for reading from fifo */ + p_access->p_sys->flv_packet = NULL; + p_access->p_sys->read_packet = 1; + + /* Update default_pts to a suitable value for rtmp access */ + var_Create( p_access, "rtmp-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); + + return VLC_SUCCESS; + +error2: + vlc_cond_destroy( &p_sys->p_thread->wait ); + vlc_mutex_destroy( &p_sys->p_thread->lock ); + + free( p_sys->p_thread->psz_application ); + free( p_sys->p_thread->psz_media ); + + net_Close( p_sys->p_thread->fd ); +error: + vlc_UrlClean( &p_sys->p_thread->url ); + + vlc_object_detach( p_sys->p_thread ); + vlc_object_release( p_sys->p_thread ); + + free( p_sys ); + + return VLC_EGENERIC; +} + +/***************************************************************************** + * Close: close the rtmp connection + *****************************************************************************/ +static void Close( vlc_object_t * p_this ) +{ + access_t *p_access = (access_t *) p_this; + access_sys_t *p_sys = p_access->p_sys; + int i; + +/* p_sys->p_thread->b_die = true;*/ + vlc_object_kill( p_sys->p_thread ); + block_FifoWake( p_sys->p_thread->p_fifo_input ); + block_FifoWake( p_sys->p_thread->p_empty_blocks ); + + vlc_thread_join( p_sys->p_thread ); + + vlc_cond_destroy( &p_sys->p_thread->wait ); + vlc_mutex_destroy( &p_sys->p_thread->lock ); + + block_FifoRelease( p_sys->p_thread->p_fifo_input ); + block_FifoRelease( p_sys->p_thread->p_empty_blocks ); + + for( i = 0; i < 64; i++ ) /* RTMP_HEADER_STREAM_INDEX_MASK */ + { + if( p_sys->p_thread->rtmp_headers_recv[i].body != NULL ) + { + free( p_sys->p_thread->rtmp_headers_recv[i].body->body ); + free( p_sys->p_thread->rtmp_headers_recv[i].body ); + } + } + + net_Close( p_sys->p_thread->fd ); + + var_Destroy( p_access, "rtmp-caching" ); + + vlc_UrlClean( &p_sys->p_thread->url ); + free( p_sys->p_thread->psz_application ); + free( p_sys->p_thread->psz_media ); + + vlc_object_detach( p_sys->p_thread ); + vlc_object_release( p_sys->p_thread ); + free( p_sys ); +} + +/***************************************************************************** + * Read: standard read on a file descriptor. + *****************************************************************************/ +static ssize_t Read( access_t *p_access, uint8_t *p_buffer, size_t i_len ) +{ + access_sys_t *p_sys = p_access->p_sys; + rtmp_packet_t *rtmp_packet; + uint8_t *tmp_buffer; + ssize_t i_ret; + size_t i_len_tmp; + + i_len_tmp = 0; + + while( i_len_tmp < i_len ) + { + if( p_sys->p_thread->result_stop || p_access->info.b_eof || !vlc_object_alive (p_access) ) + { + p_access->info.b_eof = true; + return 0; + } + + if( p_sys->read_packet ) + { + if( !p_sys->p_thread->metadata_received ) + { + /* Wait until enough data is received for extracting metadata */ + if( block_FifoCount( p_sys->p_thread->p_fifo_input ) < 10 ) + { + msleep(100000); + continue; + } + + p_sys->flv_packet = flv_get_metadata( p_access ); + + p_sys->p_thread->metadata_received = 1; + } + else + { + p_sys->flv_packet = block_FifoGet( p_sys->p_thread->p_fifo_input ); + if( p_sys->flv_packet == NULL ) + continue; /* Forced wake-up */ + } + + if( p_sys->p_thread->first_media_packet ) + { + p_sys->flv_packet = flv_insert_header( p_access, p_sys->flv_packet ); + + p_sys->p_thread->first_media_packet = 0; + } + } + if( i_len - i_len_tmp >= p_sys->flv_packet->i_buffer ) + { + p_sys->read_packet = 1; + + memcpy( p_buffer + i_len_tmp, p_sys->flv_packet->p_buffer, p_sys->flv_packet->i_buffer ); + block_FifoPut( p_sys->p_thread->p_empty_blocks, p_sys->flv_packet ); + + i_len_tmp += p_sys->flv_packet->i_buffer; + } + else + { + p_sys->read_packet = 0; + + memcpy( p_buffer + i_len_tmp, p_sys->flv_packet->p_buffer, i_len - i_len_tmp); + p_sys->flv_packet->i_buffer -= i_len - i_len_tmp; + memmove( p_sys->flv_packet->p_buffer, p_sys->flv_packet->p_buffer + i_len - i_len_tmp, p_sys->flv_packet->i_buffer ); + + i_len_tmp += i_len - i_len_tmp; + } + } + if( i_len_tmp > 0 ) + { + if( p_sys->p_thread->result_publish ) + { + /* Send publish onStatus event only once */ + p_sys->p_thread->result_publish = 0; + + rtmp_packet = rtmp_build_publish_start( p_sys->p_thread ); + + tmp_buffer = rtmp_encode_packet( p_sys->p_thread, rtmp_packet ); + + i_ret = net_Write( p_sys->p_thread, p_sys->p_thread->fd, NULL, tmp_buffer, rtmp_packet->length_encoded ); + if( i_ret != rtmp_packet->length_encoded ) + { + free( rtmp_packet->body->body ); + free( rtmp_packet->body ); + free( rtmp_packet ); + free( tmp_buffer ); + msg_Err( p_access, "failed send publish start" ); + return -1; + } + free( rtmp_packet->body->body ); + free( rtmp_packet->body ); + free( rtmp_packet ); + free( tmp_buffer ); + } + + p_access->info.i_pos += i_len_tmp; + + rtmp_packet = rtmp_build_bytes_read( p_sys->p_thread, p_access->info.i_pos ); + + tmp_buffer = rtmp_encode_packet( p_sys->p_thread, rtmp_packet ); + + i_ret = net_Write( p_sys->p_thread, p_sys->p_thread->fd, NULL, tmp_buffer, rtmp_packet->length_encoded ); + if( i_ret != rtmp_packet->length_encoded ) + { + free( rtmp_packet->body->body ); + free( rtmp_packet->body ); + free( rtmp_packet ); + free( tmp_buffer ); + msg_Err( p_access, "failed send bytes read" ); + return -1; + } + free( rtmp_packet->body->body ); + free( rtmp_packet->body ); + free( rtmp_packet ); + free( tmp_buffer ); + } + + return i_len_tmp; +} + +/***************************************************************************** + * Seek: seek to a specific location in a file + *****************************************************************************/ +static int Seek( access_t *p_access, int64_t i_pos ) +{ + VLC_UNUSED( p_access ); + VLC_UNUSED( i_pos ); +/*msg_Warn ( p_access, "Seek to %lld", i_pos); + switch( rtmp_seek( p_access, i_pos ) ) + { + case -1: + return VLC_EGENERIC; + case 0: + break; + default: + msg_Err( p_access, "You should not be here" ); + abort(); + } +*/ + return VLC_EGENERIC; +} + +/***************************************************************************** + * Control: + *****************************************************************************/ +static int Control( access_t *p_access, int i_query, va_list args ) +{ + bool *pb_bool; + int *pi_int; + int64_t *pi_64; + + switch( i_query ) + { + /* */ + case ACCESS_CAN_SEEK: + case ACCESS_CAN_FASTSEEK: + pb_bool = (bool*) va_arg( args, bool* ); + *pb_bool = false; /* TODO */ + break; + + case ACCESS_CAN_PAUSE: + pb_bool = (bool*) va_arg( args, bool* ); + *pb_bool = false; /* TODO */ + break; + + case ACCESS_CAN_CONTROL_PACE: + pb_bool = (bool*) va_arg( args, bool* ); + *pb_bool = true; + break; + + /* */ + case ACCESS_GET_MTU: + pi_int = (int*) va_arg( args, int * ); + *pi_int = 0; + break; + + case ACCESS_GET_PTS_DELAY: + pi_64 = (int64_t*) va_arg( args, int64_t * ); + *pi_64 = var_GetInteger( p_access, "rtmp-caching" ) * INT64_C(1000); + break; + + /* */ + case ACCESS_SET_PAUSE_STATE: + /* Nothing to do */ + break; + + case ACCESS_GET_TITLE_INFO: + case ACCESS_SET_TITLE: + case ACCESS_SET_SEEKPOINT: + case ACCESS_SET_PRIVATE_ID_STATE: + case ACCESS_GET_META: + case ACCESS_GET_CONTENT_TYPE: /* DOWN: comment this line */ + return VLC_EGENERIC; + + default: + msg_Warn( p_access, "unimplemented query in control" ); + return VLC_EGENERIC; + } + + return VLC_SUCCESS; +} + +/***************************************************************************** + * ThreadControl: manage control messages and pipe media to Read + *****************************************************************************/ +static void* ThreadControl( vlc_object_t *p_this ) +{ + rtmp_control_thread_t *p_thread = (rtmp_control_thread_t *) p_this; + rtmp_packet_t *rtmp_packet; + + rtmp_init_handler( p_thread->rtmp_handler ); + + while( vlc_object_alive (p_thread) ) + { + rtmp_packet = rtmp_read_net_packet( p_thread ); + if( rtmp_packet != NULL ) + { + if( rtmp_packet->content_type < 0x01 /* RTMP_CONTENT_TYPE_CHUNK_SIZE */ + || rtmp_packet->content_type > 0x14 ) /* RTMP_CONTENT_TYPE_INVOKE */ + { + free( rtmp_packet->body->body ); + free( rtmp_packet->body ); + free( rtmp_packet ); + + msg_Warn( p_thread, "unknown content type received" ); + } + else + p_thread->rtmp_handler[rtmp_packet->content_type]( p_thread, rtmp_packet ); + } + else + { + /* Sometimes server close connection too soon */ + if( p_thread->result_connect ) + { + vlc_mutex_lock( &p_thread->lock ); + vlc_cond_signal( &p_thread->wait ); + vlc_mutex_unlock( &p_thread->lock ); + } + + p_thread->b_die = 1; + ((access_t *) p_thread->p_base_object)->info.b_eof = true; + + block_FifoWake( p_thread->p_fifo_input ); + } + } + return NULL; +} diff --git a/VLC/acl.c b/VLC/acl.c new file mode 100644 index 0000000..85d4ba0 --- /dev/null +++ b/VLC/acl.c @@ -0,0 +1,391 @@ +/***************************************************************************** + * acl.c: + ***************************************************************************** + * Copyright © 2005-2007 Rémi Denis-Courmont + * $Id$ + * + * Authors: Rémi Denis-Courmont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#include +#include "vlc_acl.h" + +#include + +#include "vlc_network.h" +#include "vlc_charset.h" + +/* FIXME: rwlock on acl, but libvlc doesn't implement rwlock */ +typedef struct vlc_acl_entry_t +{ + uint8_t host[17]; + uint8_t i_bytes_match; + uint8_t i_bits_mask; + bool b_allow; +} vlc_acl_entry_t; + +struct vlc_acl_t +{ + vlc_object_t *p_owner; + unsigned i_size; + vlc_acl_entry_t *p_entries; + bool b_allow_default; +}; + +static int ACL_Resolve( vlc_object_t *p_this, uint8_t *p_bytes, + const char *psz_ip ) +{ + struct addrinfo hints, *res; + int i_family; + + memset (&hints, 0, sizeof (hints)); + hints.ai_socktype = SOCK_STREAM; /* doesn't matter */ + hints.ai_flags = AI_NUMERICHOST; + + if( vlc_getaddrinfo( p_this, psz_ip, 0, &hints, &res ) ) + { + msg_Err( p_this, "invalid IP address %s", psz_ip ); + return -1; + } + + p_bytes[16] = 0; /* avoids overflowing when i_bytes_match = 16 */ + + i_family = res->ai_addr->sa_family; + switch( i_family ) + { + case AF_INET: + { + struct sockaddr_in *addr; + + addr = (struct sockaddr_in *)res->ai_addr; + memset( p_bytes, 0, 12 ); + memcpy( p_bytes + 12, &addr->sin_addr, 4 ); + break; + } + +#if defined (HAVE_GETADDRINFO) || defined (WIN32) + /* unfortunately many people define AF_INET6 + though they don't have struct sockaddr_in6 */ + case AF_INET6: + { + struct sockaddr_in6 *addr; + + addr = (struct sockaddr_in6 *)res->ai_addr; + memcpy( p_bytes, &addr->sin6_addr, 16 ); + break; + } +#endif + + default: + msg_Err( p_this, "unknown address family" ); + vlc_freeaddrinfo( res ); + return -1; + } + + vlc_freeaddrinfo( res ); + return i_family; +} + + +/** + * Check if a given address passes an access control list. + * + * @param p_acl pre-existing ACL to match the address against + * @param psz_ip numeric IPv4/IPv6 address + * + * @return 0 if the first matching ACL entry is an access grant, + * 1 if the first matching ACL entry is a denial of access, + * -1 on error. + */ +int ACL_Check( vlc_acl_t *p_acl, const char *psz_ip ) +{ + const vlc_acl_entry_t *p_cur, *p_end; + uint8_t host[17]; + + if( p_acl == NULL ) + return -1; + + p_cur = p_acl->p_entries; + p_end = p_cur + p_acl->i_size; + + if( ACL_Resolve( p_acl->p_owner, host, psz_ip ) < 0 ) + return -1; + + while (p_cur < p_end) + { + unsigned i; + + i = p_cur->i_bytes_match; + if( (memcmp( p_cur->host, host, i ) == 0) + && (((p_cur->host[i] ^ host[i]) & p_cur->i_bits_mask) == 0) ) + return !p_cur->b_allow; + + p_cur++; + } + + return !p_acl->b_allow_default; +} + +/** + * Adds an item to an ACL. + * Items are always matched in the same order as they are added. + */ +int ACL_AddNet( vlc_acl_t *p_acl, const char *psz_ip, int i_len, + bool b_allow ) +{ + vlc_acl_entry_t *p_ent; + unsigned i_size; + div_t d; + int i_family; + + i_size = p_acl->i_size; + p_ent = (vlc_acl_entry_t *)realloc( p_acl->p_entries, + ++p_acl->i_size * sizeof( *p_ent ) ); + + if( p_ent == NULL ) + return -1; + + p_acl->p_entries = p_ent; + p_ent += i_size; + + i_family = ACL_Resolve( p_acl->p_owner, p_ent->host, psz_ip ); + if( i_family < 0 ) + { + /* + * I'm lazy : memory space will be re-used in the next ACL_Add call... + * or not. + */ + p_acl->i_size--; + return -1; + } + + if( i_len >= 0 ) + { + if( i_family == AF_INET ) + i_len += 96; + + if( i_len > 128 ) + i_len = 128; + } + else + i_len = 128; /* ACL_AddHost */ + + d = div( i_len, 8 ); + p_ent->i_bytes_match = d.quot; + p_ent->i_bits_mask = 0xff << (8 - d.rem); + + p_ent->b_allow = b_allow; + return 0; +} + + +/** + * Creates an empty ACL. + * + * @param b_allow whether to grant (true) or deny (false) access + * by default (ie if none of the ACL entries matched). + * + * @return an ACL object. NULL in case of error. + */ +vlc_acl_t *__ACL_Create( vlc_object_t *p_this, bool b_allow ) +{ + vlc_acl_t *p_acl; + + p_acl = (vlc_acl_t *)malloc( sizeof( *p_acl ) ); + if( p_acl == NULL ) + return NULL; + + vlc_object_yield( p_this ); + p_acl->p_owner = p_this; + p_acl->i_size = 0; + p_acl->p_entries = NULL; + p_acl->b_allow_default = b_allow; + + return p_acl; +} + + +/** + * Perform a deep copy of an existing ACL. + * + * @param p_this object to attach the copy to. + * @param p_acl ACL object to be copied. + * + * @return a new ACL object, or NULL on error. + */ +vlc_acl_t *__ACL_Duplicate( vlc_object_t *p_this, const vlc_acl_t *p_acl ) +{ + vlc_acl_t *p_dupacl; + + if( p_acl == NULL ) + return NULL; + + p_dupacl = (vlc_acl_t *)malloc( sizeof( *p_dupacl ) ); + if( p_dupacl == NULL ) + return NULL; + + if( p_acl->i_size ) + { + p_dupacl->p_entries = (vlc_acl_entry_t *) + malloc( p_acl->i_size * sizeof( vlc_acl_entry_t ) ); + + if( p_dupacl->p_entries == NULL ) + { + free( p_dupacl ); + return NULL; + } + + memcpy( p_dupacl->p_entries, p_acl->p_entries, + p_acl->i_size * sizeof( vlc_acl_entry_t ) ); + } + else + p_dupacl->p_entries = NULL; + + vlc_object_yield( p_this ); + p_dupacl->p_owner = p_this; + p_dupacl->i_size = p_acl->i_size; + p_dupacl->b_allow_default = p_acl->b_allow_default; + + return p_dupacl; +} + + +/** + * Releases all resources associated with an ACL object. + */ +void ACL_Destroy( vlc_acl_t *p_acl ) +{ + if( p_acl != NULL ) + { + if( p_acl->p_entries != NULL ) + free( p_acl->p_entries ); + + vlc_object_release( p_acl->p_owner ); + free( p_acl ); + } +} + + +/** + * Reads ACL entries from a file. + * + * @param p_acl ACL object in which to insert parsed entries. + * @param psz_patch filename from which to parse entries. + * + * @return 0 on success, -1 on error. + */ +int ACL_LoadFile( vlc_acl_t *p_acl, const char *psz_path ) +{ + FILE *file; + + if( p_acl == NULL ) + return -1; + + file = utf8_fopen( psz_path, "r" ); + if( file == NULL ) + return -1; + + msg_Dbg( p_acl->p_owner, "find .hosts in dir=%s", psz_path ); + + while( !feof( file ) ) + { + char line[1024], *psz_ip, *ptr; + + if( fgets( line, sizeof( line ), file ) == NULL ) + { + if( ferror( file ) ) + { + msg_Err( p_acl->p_owner, "error reading %s : %m", psz_path ); + goto error; + } + continue; + } + + /* fgets() is cool : never overflow, always nul-terminate */ + psz_ip = line; + + /* skips blanks - cannot overflow given '\0' is not space */ + while( isspace( *psz_ip ) ) + psz_ip++; + + if( *psz_ip == '\0' ) /* empty/blank line */ + continue; + + ptr = strchr( psz_ip, '\n' ); + if( ptr == NULL ) + { + msg_Warn( p_acl->p_owner, "skipping overly long line in %s", + psz_path); + do + { + if( fgets( line, sizeof( line ), file ) == NULL ) + { + if( ferror( file ) ) + { + msg_Err( p_acl->p_owner, "error reading %s : %m", + psz_path ); + } + goto error; + } + } + while( strchr( line, '\n' ) == NULL); + + continue; /* skip unusable line */ + } + + /* skips comment-only line */ + if( *psz_ip == '#' ) + continue; + + /* looks for first space, CR, LF, etc. or end-of-line comment */ + /* (there is at least a linefeed) */ + for( ptr = psz_ip; ( *ptr != '#' ) && !isspace( *ptr ); ptr++ ); + + *ptr = '\0'; + + msg_Dbg( p_acl->p_owner, "restricted to %s", psz_ip ); + + ptr = strchr( psz_ip, '/' ); + if( ptr != NULL ) + *ptr++ = '\0'; /* separate address from mask length */ + + if( (ptr != NULL) + ? ACL_AddNet( p_acl, psz_ip, atoi( ptr ), true ) + : ACL_AddHost( p_acl, psz_ip, true ) ) + { + msg_Err( p_acl->p_owner, "cannot add ACL from %s", psz_path ); + continue; + } + } + + fclose( file ); + return 0; + +error: + fclose( file ); + return -1; +} + diff --git a/VLC/action.c b/VLC/action.c new file mode 100644 index 0000000..0438885 --- /dev/null +++ b/VLC/action.c @@ -0,0 +1,46 @@ +/***************************************************************************** + * action.c: key to action mapping + ***************************************************************************** + * Copyright © 2008 Rémi Denis-Courmont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "libvlc.h" + +int vlc_key_to_action (vlc_object_t *libvlc, const char *varname, + vlc_value_t prevkey, vlc_value_t curkey, void *priv) +{ + const struct hotkey *key = priv; + + (void)varname; + (void)prevkey; + + while (key->i_key != curkey.i_int) + { + if (key->psz_action == NULL) + return VLC_SUCCESS; /* key is not mapped to anything */ + + key++; + } + + return var_SetInteger (libvlc, "key-action", key->i_action); +} + diff --git a/VLC/audio.c b/VLC/audio.c new file mode 100644 index 0000000..22a886e --- /dev/null +++ b/VLC/audio.c @@ -0,0 +1,262 @@ +/***************************************************************************** + * libvlc_audio.c: New libvlc audio control API + ***************************************************************************** + * Copyright (C) 2006 the VideoLAN team + * $Id$ + * + * Authors: Filippo Carone + * Jean-Paul Saman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#include "libvlc_internal.h" +#include + +#include +#include + + +/* + * Remember to release the returned aout_instance_t since it is locked at + * the end of this function. + */ +static aout_instance_t *GetAOut( libvlc_instance_t *p_instance, + libvlc_exception_t *p_exception ) +{ + aout_instance_t * p_aout = NULL; + + p_aout = vlc_object_find( p_instance->p_libvlc_int, VLC_OBJECT_AOUT, FIND_CHILD ); + if( !p_aout ) + { + libvlc_exception_raise( p_exception, "No active audio output" ); + return NULL; + } + + return p_aout; +} + + +/***************************************************************************** + * libvlc_audio_get_mute : Get the volume state, true if muted + *****************************************************************************/ +void libvlc_audio_toggle_mute( libvlc_instance_t *p_instance, + libvlc_exception_t *p_e ) +{ + VLC_UNUSED(p_e); + + aout_VolumeMute( p_instance->p_libvlc_int, NULL ); +} + +int libvlc_audio_get_mute( libvlc_instance_t *p_instance, + libvlc_exception_t *p_e ) +{ + /* + * If the volume level is 0, then the channel is muted + */ + audio_volume_t i_volume; + + i_volume = libvlc_audio_get_volume(p_instance, p_e); + if ( i_volume == 0 ) + return true; + return false; +} + +void libvlc_audio_set_mute( libvlc_instance_t *p_instance, int mute, + libvlc_exception_t *p_e ) +{ + if ( mute ^ libvlc_audio_get_mute( p_instance, p_e ) ) + { + aout_VolumeMute( p_instance->p_libvlc_int, NULL ); + } +} + +/***************************************************************************** + * libvlc_audio_get_volume : Get the current volume (range 0-200 %) + *****************************************************************************/ +int libvlc_audio_get_volume( libvlc_instance_t *p_instance, + libvlc_exception_t *p_e ) +{ + VLC_UNUSED(p_e); + + audio_volume_t i_volume; + + aout_VolumeGet( p_instance->p_libvlc_int, &i_volume ); + + return (i_volume*200+AOUT_VOLUME_MAX/2)/AOUT_VOLUME_MAX; +} + + +/***************************************************************************** + * libvlc_audio_set_volume : Set the current volume + *****************************************************************************/ +void libvlc_audio_set_volume( libvlc_instance_t *p_instance, int i_volume, + libvlc_exception_t *p_e ) +{ + if( i_volume >= 0 && i_volume <= 200 ) + { + i_volume = (i_volume * AOUT_VOLUME_MAX + 100) / 200; + + aout_VolumeSet( p_instance->p_libvlc_int, i_volume ); + } + else + { + libvlc_exception_raise( p_e, "Volume out of range" ); + } +} + +/***************************************************************************** + * libvlc_audio_get_track_count : Get the number of available audio tracks + *****************************************************************************/ +int libvlc_audio_get_track_count( libvlc_media_player_t *p_mi, + libvlc_exception_t *p_e ) +{ + input_thread_t *p_input_thread = libvlc_get_input_thread( p_mi, p_e ); + vlc_value_t val_list; + + if( !p_input_thread ) + return -1; + + var_Change( p_input_thread, "audio-es", VLC_VAR_GETCHOICES, &val_list, NULL ); + vlc_object_release( p_input_thread ); + return val_list.p_list->i_count; +} + +/***************************************************************************** + * libvlc_audio_get_track : Get the current audio track + *****************************************************************************/ +int libvlc_audio_get_track( libvlc_media_player_t *p_mi, + libvlc_exception_t *p_e ) +{ + input_thread_t *p_input_thread = libvlc_get_input_thread( p_mi, p_e ); + vlc_value_t val_list; + vlc_value_t val; + int i_track = -1; + int i_ret = -1; + int i; + + if( !p_input_thread ) + return -1; + + i_ret = var_Get( p_input_thread, "audio-es", &val ); + if( i_ret < 0 ) + { + libvlc_exception_raise( p_e, "Getting Audio track information failed" ); + vlc_object_release( p_input_thread ); + return i_ret; + } + + var_Change( p_input_thread, "audio-es", VLC_VAR_GETCHOICES, &val_list, NULL ); + for( i = 0; i < val_list.p_list->i_count; i++ ) + { + vlc_value_t track_val = val_list.p_list->p_values[i]; + if( track_val.i_int == val.i_int ) + { + i_track = i; + break; + } + } + vlc_object_release( p_input_thread ); + return i_track; +} + + +/***************************************************************************** + * libvlc_audio_set_track : Set the current audio track + *****************************************************************************/ +void libvlc_audio_set_track( libvlc_media_player_t *p_mi, int i_track, + libvlc_exception_t *p_e ) +{ + input_thread_t *p_input_thread = libvlc_get_input_thread( p_mi, p_e ); + vlc_value_t val_list; + int i_ret = -1; + int i; + + if( !p_input_thread ) + return; + + var_Change( p_input_thread, "audio-es", VLC_VAR_GETCHOICES, &val_list, NULL ); + for( i = 0; i < val_list.p_list->i_count; i++ ) + { + vlc_value_t val = val_list.p_list->p_values[i]; + if( i_track == val.i_int ) + { + i_ret = var_Set( p_input_thread, "audio-es", val ); + if( i_ret < 0 ) + { + libvlc_exception_raise( p_e, "Setting audio track failed" ); + } + vlc_object_release( p_input_thread ); + return; + } + } + libvlc_exception_raise( p_e, "Audio track out of range" ); + vlc_object_release( p_input_thread ); +} + +/***************************************************************************** + * libvlc_audio_get_channel : Get the current audio channel + *****************************************************************************/ +int libvlc_audio_get_channel( libvlc_instance_t *p_instance, + libvlc_exception_t *p_e ) +{ + aout_instance_t *p_aout = GetAOut( p_instance, p_e ); + if( p_aout ) + { + vlc_value_t val; + + var_Get( p_aout, "audio-channels", &val ); + vlc_object_release( p_aout ); + return val.i_int; + } + return -1; +} + +/***************************************************************************** + * libvlc_audio_set_channel : Set the current audio channel + *****************************************************************************/ +void libvlc_audio_set_channel( libvlc_instance_t *p_instance, int i_channel, + libvlc_exception_t *p_e ) +{ + aout_instance_t *p_aout = GetAOut( p_instance, p_e ); + if( p_aout ) + { + vlc_value_t val; + int i_ret = -1; + + val.i_int = i_channel; + switch( i_channel ) + { + case AOUT_VAR_CHAN_RSTEREO: + case AOUT_VAR_CHAN_STEREO: + case AOUT_VAR_CHAN_LEFT: + case AOUT_VAR_CHAN_RIGHT: + case AOUT_VAR_CHAN_DOLBYS: + i_ret = var_Set( p_aout, "audio-channels", val ); + if( i_ret < 0 ) + { + libvlc_exception_raise( p_e, "Failed setting audio channel" ); + vlc_object_release( p_aout ); + return; + } + vlc_object_release( p_aout ); + return; /* Found */ + default: + libvlc_exception_raise( p_e, "Audio channel out of range" ); + break; + } + vlc_object_release( p_aout ); + } +} diff --git a/VLC/audio_output/aout_internal.h b/VLC/audio_output/aout_internal.h new file mode 100644 index 0000000..979224e --- /dev/null +++ b/VLC/audio_output/aout_internal.h @@ -0,0 +1,262 @@ +/***************************************************************************** + * aout_internal.h : internal defines for audio output + ***************************************************************************** + * Copyright (C) 2002 the VideoLAN team + * $Id$ + * + * Authors: Christophe Massiot + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#if defined(__PLUGIN__) || defined(__BUILTIN__) || !defined(__LIBVLC__) + +#endif + +#ifndef __LIBVLC_AOUT_INTERNAL_H +# define __LIBVLC_AOUT_INTERNAL_H 1 + +#include + +#if defined( __APPLE__ ) || defined( SYS_BSD ) +#undef HAVE_ALLOCA +#endif + +#ifdef HAVE_ALLOCA +# define ALLOCA_TEST( p_alloc, p_new_buffer ) \ + if ( (p_alloc)->i_alloc_type == AOUT_ALLOC_STACK ) \ + { \ + (p_new_buffer) = alloca( i_alloc_size + sizeof(aout_buffer_t) );\ + i_alloc_type = AOUT_ALLOC_STACK; \ + } \ + else +#else +# define ALLOCA_TEST( p_alloc, p_new_buffer ) +#endif + +#define aout_BufferAlloc( p_alloc, i_nb_usec, p_previous_buffer, \ + p_new_buffer ) \ + if ( (p_alloc)->i_alloc_type == AOUT_ALLOC_NONE ) \ + { \ + (p_new_buffer) = p_previous_buffer; \ + } \ + else \ + { \ + int i_alloc_size, i_alloc_type; \ + i_alloc_size = (int)( (uint64_t)(p_alloc)->i_bytes_per_sec \ + * (i_nb_usec) / 1000000 + 1 ); \ + ALLOCA_TEST( p_alloc, p_new_buffer ) \ + { \ + (p_new_buffer) = malloc( i_alloc_size + sizeof(aout_buffer_t) );\ + i_alloc_type = AOUT_ALLOC_HEAP; \ + } \ + if ( p_new_buffer != NULL ) \ + { \ + (p_new_buffer)->i_alloc_type = i_alloc_type; \ + (p_new_buffer)->i_size = i_alloc_size; \ + (p_new_buffer)->p_buffer = (uint8_t *)(p_new_buffer) \ + + sizeof(aout_buffer_t); \ + (p_new_buffer)->b_discontinuity = false; \ + if ( (p_previous_buffer) != NULL ) \ + { \ + (p_new_buffer)->start_date = \ + ((aout_buffer_t *)p_previous_buffer)->start_date;\ + (p_new_buffer)->end_date = \ + ((aout_buffer_t *)p_previous_buffer)->end_date; \ + } \ + } \ + /* we'll keep that for a while --Meuuh */ \ + /* else printf("%s:%d\n", __FILE__, __LINE__); */ \ + } + +/**************************************************************************** + * Prototypes + *****************************************************************************/ +/* From input.c : */ +int aout_InputNew( aout_instance_t * p_aout, aout_input_t * p_input ); +int aout_InputDelete( aout_instance_t * p_aout, aout_input_t * p_input ); +int aout_InputPlay( aout_instance_t * p_aout, aout_input_t * p_input, + aout_buffer_t * p_buffer, int i_input_rate ); + +/* From filters.c : */ +int aout_FiltersCreatePipeline ( aout_instance_t * p_aout, aout_filter_t ** pp_filters, int * pi_nb_filters, const audio_sample_format_t * p_input_format, const audio_sample_format_t * p_output_format ); +void aout_FiltersDestroyPipeline ( aout_instance_t * p_aout, aout_filter_t ** pp_filters, int i_nb_filters ); +void aout_FiltersPlay ( aout_instance_t * p_aout, aout_filter_t ** pp_filters, int i_nb_filters, aout_buffer_t ** pp_input_buffer ); +void aout_FiltersHintBuffers( aout_instance_t * p_aout, aout_filter_t ** pp_filters, int i_nb_filters, aout_alloc_t * p_first_alloc ); + +/* From mixer.c : */ +int aout_MixerNew( aout_instance_t * p_aout ); +void aout_MixerDelete( aout_instance_t * p_aout ); +void aout_MixerRun( aout_instance_t * p_aout ); +int aout_MixerMultiplierSet( aout_instance_t * p_aout, float f_multiplier ); +int aout_MixerMultiplierGet( aout_instance_t * p_aout, float * pf_multiplier ); + +/* From output.c : */ +int aout_OutputNew( aout_instance_t * p_aout, + audio_sample_format_t * p_format ); +void aout_OutputPlay( aout_instance_t * p_aout, aout_buffer_t * p_buffer ); +void aout_OutputDelete( aout_instance_t * p_aout ); + + +/* From common.c : */ +#define aout_New(a) __aout_New(VLC_OBJECT(a)) +/* Release with vlc_object_release() */ +aout_instance_t * __aout_New ( vlc_object_t * ); + +void aout_FifoInit( aout_instance_t *, aout_fifo_t *, uint32_t ); +mtime_t aout_FifoNextStart( aout_instance_t *, aout_fifo_t * ); +void aout_FifoPush( aout_instance_t *, aout_fifo_t *, aout_buffer_t * ); +void aout_FifoSet( aout_instance_t *, aout_fifo_t *, mtime_t ); +void aout_FifoMoveDates( aout_instance_t *, aout_fifo_t *, mtime_t ); +void aout_FifoDestroy( aout_instance_t * p_aout, aout_fifo_t * p_fifo ); +void aout_FormatsPrint( aout_instance_t * p_aout, const char * psz_text, const audio_sample_format_t * p_format1, const audio_sample_format_t * p_format2 ); + + +/* From intf.c :*/ +int aout_VolumeSoftGet( aout_instance_t *, audio_volume_t * ); +int aout_VolumeSoftSet( aout_instance_t *, audio_volume_t ); +int aout_VolumeSoftInfos( aout_instance_t *, audio_volume_t * ); +int aout_VolumeNoneGet( aout_instance_t *, audio_volume_t * ); +int aout_VolumeNoneSet( aout_instance_t *, audio_volume_t ); +int aout_VolumeNoneInfos( aout_instance_t *, audio_volume_t * ); + +/* From dec.c */ +#define aout_DecNew(a, b, c, d) __aout_DecNew(VLC_OBJECT(a), b, c, d) +aout_input_t * __aout_DecNew( vlc_object_t *, aout_instance_t **, audio_sample_format_t *, audio_replay_gain_t * ); +int aout_DecDelete ( aout_instance_t *, aout_input_t * ); +aout_buffer_t * aout_DecNewBuffer( aout_input_t *, size_t ); +void aout_DecDeleteBuffer( aout_instance_t *, aout_input_t *, aout_buffer_t * ); +int aout_DecPlay( aout_instance_t *, aout_input_t *, aout_buffer_t *, int i_input_rate ); + +/* Helpers */ + +static inline void aout_lock_mixer( aout_instance_t *p_aout ) +{ + vlc_mutex_lock( &p_aout->mixer_lock ); +} + +static inline void aout_unlock_mixer( aout_instance_t *p_aout ) +{ + vlc_mutex_unlock( &p_aout->mixer_lock ); +} + +static inline void aout_lock_input_fifos( aout_instance_t *p_aout ) +{ + vlc_mutex_lock( &p_aout->input_fifos_lock ); +} + +static inline void aout_unlock_input_fifos( aout_instance_t *p_aout ) +{ + vlc_mutex_unlock( &p_aout->input_fifos_lock ); +} + +static inline void aout_lock_output_fifo( aout_instance_t *p_aout ) +{ + vlc_mutex_lock( &p_aout->output_fifo_lock ); +} + +static inline void aout_unlock_output_fifo( aout_instance_t *p_aout ) +{ + vlc_mutex_unlock( &p_aout->output_fifo_lock ); +} + +static inline void aout_lock_input( aout_instance_t *p_aout, aout_input_t * p_input ) +{ + (void)p_aout; + vlc_mutex_lock( &p_input->lock ); +} + +static inline void aout_unlock_input( aout_instance_t *p_aout, aout_input_t * p_input ) +{ + (void)p_aout; + vlc_mutex_unlock( &p_input->lock ); +} + + +/** + * This function will safely mark aout input to be restarted as soon as + * possible to take configuration changes into account */ +static inline void AoutInputsMarkToRestart( aout_instance_t *p_aout ) +{ + int i; + aout_lock_mixer( p_aout ); + for( i = 0; i < p_aout->i_nb_inputs; i++ ) + p_aout->pp_inputs[i]->b_restart = true; + aout_unlock_mixer( p_aout ); +} + +/* This function will add or remove a a module from a string list (comma + * separated). It will return true if there is a modification + * In case p_aout is NULL, we will use configuration instead of variable */ +static inline bool AoutChangeFilterString( vlc_object_t *p_obj, aout_instance_t * p_aout, + const char* psz_variable, + const char *psz_name, bool b_add ) +{ + vlc_value_t val; + char *psz_parser; + + if( *psz_name == '\0' ) + return false; + + if( p_aout ) + var_Get( p_aout, psz_variable, &val ); + else + val.psz_string = config_GetPsz( p_obj, "audio-filter" ); + + if( !val.psz_string ) + val.psz_string = strdup(""); + + psz_parser = strstr( val.psz_string, psz_name ); + + if( ( b_add && psz_parser ) || ( !b_add && !psz_parser ) ) + { + /* Nothing to do */ + free( val.psz_string ); + return false; + } + + if( b_add ) + { + char *psz_old = val.psz_string; + if( *psz_old ) + { + if( asprintf( &val.psz_string, "%s:%s", psz_old, psz_name ) == -1 ) + val.psz_string = NULL; + } + else + val.psz_string = strdup( psz_name ); + free( psz_old ); + } + else + { + const int i_name = strlen( psz_name ); + const char *psz_next; + + psz_next = &psz_parser[i_name]; + if( *psz_next == ':' ) + psz_next++; + + memmove( psz_parser, psz_next, strlen(psz_next)+1 ); + } + + if( p_aout ) + var_Set( p_aout, psz_variable, val ); + else + config_PutPsz( p_obj, psz_variable, val.psz_string ); + free( val.psz_string ); + return true; +} + +#endif /* !__LIBVLC_AOUT_INTERNAL_H */ diff --git a/VLC/audio_output/common.c b/VLC/audio_output/common.c new file mode 100644 index 0000000..560e7f0 --- /dev/null +++ b/VLC/audio_output/common.c @@ -0,0 +1,630 @@ +/***************************************************************************** + * common.c : audio output management of common data structures + ***************************************************************************** + * Copyright (C) 2002-2007 the VideoLAN team + * $Id$ + * + * Authors: Christophe Massiot + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "vlc_common.h" +#include "vlc_aout.h" +#include "aout_internal.h" + +/* + * Instances management (internal and external) + */ + +#define AOUT_ASSERT_FIFO_LOCKED aout_assert_fifo_locked(p_aout, p_fifo) +static inline void aout_assert_fifo_locked( aout_instance_t * p_aout, aout_fifo_t * p_fifo ) +{ +#ifndef NDEBUG + if( p_fifo == &p_aout->output.fifo ) + vlc_assert_locked( &p_aout->output_fifo_lock ); + else + { + int i; + for( i = 0; i < p_aout->i_nb_inputs; i++ ) + { + if( p_fifo == &p_aout->pp_inputs[i]->fifo) + { + vlc_assert_locked( &p_aout->input_fifos_lock ); + break; + } + } + if( i == p_aout->i_nb_inputs ) + vlc_assert_locked( &p_aout->mixer_lock ); + } +#else + (void)p_aout; + (void)p_fifo; +#endif +} + +/* Local functions */ +static void aout_Destructor( vlc_object_t * p_this ); + +/***************************************************************************** + * aout_New: initialize aout structure + *****************************************************************************/ +aout_instance_t * __aout_New( vlc_object_t * p_parent ) +{ + aout_instance_t * p_aout; + vlc_value_t val; + + /* Allocate descriptor. */ + p_aout = vlc_object_create( p_parent, VLC_OBJECT_AOUT ); + if( p_aout == NULL ) + { + return NULL; + } + + /* Initialize members. */ + vlc_mutex_init( &p_aout->input_fifos_lock ); + vlc_mutex_init( &p_aout->mixer_lock ); + vlc_mutex_init( &p_aout->output_fifo_lock ); + p_aout->i_nb_inputs = 0; + p_aout->mixer.f_multiplier = 1.0; + p_aout->mixer.b_error = 1; + p_aout->output.b_error = 1; + p_aout->output.b_starving = 1; + + var_Create( p_aout, "intf-change", VLC_VAR_BOOL ); + val.b_bool = true; + var_Set( p_aout, "intf-change", val ); + + vlc_object_set_destructor( p_aout, aout_Destructor ); + + return p_aout; +} + +/***************************************************************************** + * aout_Destructor: destroy aout structure + *****************************************************************************/ +static void aout_Destructor( vlc_object_t * p_this ) +{ + aout_instance_t * p_aout = (aout_instance_t *)p_this; + vlc_mutex_destroy( &p_aout->input_fifos_lock ); + vlc_mutex_destroy( &p_aout->mixer_lock ); + vlc_mutex_destroy( &p_aout->output_fifo_lock ); +} + + +/* + * Formats management (internal and external) + */ + +/***************************************************************************** + * aout_FormatNbChannels : return the number of channels + *****************************************************************************/ +unsigned int aout_FormatNbChannels( const audio_sample_format_t * p_format ) +{ + static const uint32_t pi_channels[] = + { AOUT_CHAN_CENTER, AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, + AOUT_CHAN_REARCENTER, AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, + AOUT_CHAN_MIDDLELEFT, AOUT_CHAN_MIDDLERIGHT, AOUT_CHAN_LFE }; + unsigned int i_nb = 0, i; + + for ( i = 0; i < sizeof(pi_channels)/sizeof(uint32_t); i++ ) + { + if ( p_format->i_physical_channels & pi_channels[i] ) i_nb++; + } + + return i_nb; +} + +/***************************************************************************** + * aout_BitsPerSample : get the number of bits per sample + *****************************************************************************/ +unsigned int aout_BitsPerSample( vlc_fourcc_t i_format ) +{ + switch( i_format ) + { + case VLC_FOURCC('u','8',' ',' '): + case VLC_FOURCC('s','8',' ',' '): + return 8; + + case VLC_FOURCC('u','1','6','l'): + case VLC_FOURCC('s','1','6','l'): + case VLC_FOURCC('u','1','6','b'): + case VLC_FOURCC('s','1','6','b'): + return 16; + + case VLC_FOURCC('u','2','4','l'): + case VLC_FOURCC('s','2','4','l'): + case VLC_FOURCC('u','2','4','b'): + case VLC_FOURCC('s','2','4','b'): + return 24; + + case VLC_FOURCC('s','3','2','l'): + case VLC_FOURCC('s','3','2','b'): + case VLC_FOURCC('f','l','3','2'): + case VLC_FOURCC('f','i','3','2'): + return 32; + + case VLC_FOURCC('f','l','6','4'): + return 64; + + default: + /* For these formats the caller has to indicate the parameters + * by hand. */ + return 0; + } +} + +/***************************************************************************** + * aout_FormatPrepare : compute the number of bytes per frame & frame length + *****************************************************************************/ +void aout_FormatPrepare( audio_sample_format_t * p_format ) +{ + p_format->i_bitspersample = aout_BitsPerSample( p_format->i_format ); + if( p_format->i_bitspersample > 0 ) + { + p_format->i_bytes_per_frame = ( p_format->i_bitspersample / 8 ) + * aout_FormatNbChannels( p_format ); + p_format->i_frame_length = 1; + } +} + +/***************************************************************************** + * aout_FormatPrintChannels : print a channel in a human-readable form + *****************************************************************************/ +const char * aout_FormatPrintChannels( const audio_sample_format_t * p_format ) +{ + switch ( p_format->i_physical_channels & AOUT_CHAN_PHYSMASK ) + { + case AOUT_CHAN_CENTER: + if ( (p_format->i_original_channels & AOUT_CHAN_CENTER) + || (p_format->i_original_channels + & (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT)) ) + return "Mono"; + else if ( p_format->i_original_channels & AOUT_CHAN_LEFT ) + return "Left"; + return "Right"; + case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT: + if ( p_format->i_original_channels & AOUT_CHAN_REVERSESTEREO ) + { + if ( p_format->i_original_channels & AOUT_CHAN_DOLBYSTEREO ) + return "Dolby/Reverse"; + return "Stereo/Reverse"; + } + else + { + if ( p_format->i_original_channels & AOUT_CHAN_DOLBYSTEREO ) + return "Dolby"; + else if ( p_format->i_original_channels & AOUT_CHAN_DUALMONO ) + return "Dual-mono"; + else if ( p_format->i_original_channels == AOUT_CHAN_CENTER ) + return "Stereo/Mono"; + else if ( !(p_format->i_original_channels & AOUT_CHAN_RIGHT) ) + return "Stereo/Left"; + else if ( !(p_format->i_original_channels & AOUT_CHAN_LEFT) ) + return "Stereo/Right"; + return "Stereo"; + } + case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER: + return "3F"; + case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARCENTER: + return "2F1R"; + case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER + | AOUT_CHAN_REARCENTER: + return "3F1R"; + case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT + | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT: + return "2F2R"; + case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER + | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT: + return "3F2R"; + + case AOUT_CHAN_CENTER | AOUT_CHAN_LFE: + if ( (p_format->i_original_channels & AOUT_CHAN_CENTER) + || (p_format->i_original_channels + & (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT)) ) + return "Mono/LFE"; + else if ( p_format->i_original_channels & AOUT_CHAN_LEFT ) + return "Left/LFE"; + return "Right/LFE"; + case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_LFE: + if ( p_format->i_original_channels & AOUT_CHAN_DOLBYSTEREO ) + return "Dolby/LFE"; + else if ( p_format->i_original_channels & AOUT_CHAN_DUALMONO ) + return "Dual-mono/LFE"; + else if ( p_format->i_original_channels == AOUT_CHAN_CENTER ) + return "Mono/LFE"; + else if ( !(p_format->i_original_channels & AOUT_CHAN_RIGHT) ) + return "Stereo/Left/LFE"; + else if ( !(p_format->i_original_channels & AOUT_CHAN_LEFT) ) + return "Stereo/Right/LFE"; + return "Stereo/LFE"; + case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER | AOUT_CHAN_LFE: + return "3F/LFE"; + case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARCENTER + | AOUT_CHAN_LFE: + return "2F1R/LFE"; + case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER + | AOUT_CHAN_REARCENTER | AOUT_CHAN_LFE: + return "3F1R/LFE"; + case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT + | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE: + return "2F2R/LFE"; + case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER + | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE: + return "3F2R/LFE"; + case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER + | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_MIDDLELEFT + | AOUT_CHAN_MIDDLERIGHT: + return "3F2M2R"; + case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER + | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_MIDDLELEFT + | AOUT_CHAN_MIDDLERIGHT | AOUT_CHAN_LFE: + return "3F2M2R/LFE"; + } + + return "ERROR"; +} + +/***************************************************************************** + * aout_FormatPrint : print a format in a human-readable form + *****************************************************************************/ +void aout_FormatPrint( aout_instance_t * p_aout, const char * psz_text, + const audio_sample_format_t * p_format ) +{ + msg_Dbg( p_aout, "%s '%4.4s' %d Hz %s frame=%d samples/%d bytes", psz_text, + (char *)&p_format->i_format, p_format->i_rate, + aout_FormatPrintChannels( p_format ), + p_format->i_frame_length, p_format->i_bytes_per_frame ); +} + +/***************************************************************************** + * aout_FormatsPrint : print two formats in a human-readable form + *****************************************************************************/ +void aout_FormatsPrint( aout_instance_t * p_aout, const char * psz_text, + const audio_sample_format_t * p_format1, + const audio_sample_format_t * p_format2 ) +{ + msg_Dbg( p_aout, "%s '%4.4s'->'%4.4s' %d Hz->%d Hz %s->%s", + psz_text, + (char *)&p_format1->i_format, (char *)&p_format2->i_format, + p_format1->i_rate, p_format2->i_rate, + aout_FormatPrintChannels( p_format1 ), + aout_FormatPrintChannels( p_format2 ) ); +} + + +/* + * FIFO management (internal) - please understand that solving race conditions + * is _your_ job, ie. in the audio output you should own the mixer lock + * before calling any of these functions. + */ + +/***************************************************************************** + * aout_FifoInit : initialize the members of a FIFO + *****************************************************************************/ +void aout_FifoInit( aout_instance_t * p_aout, aout_fifo_t * p_fifo, + uint32_t i_rate ) +{ + AOUT_ASSERT_FIFO_LOCKED; + + if( i_rate == 0 ) + { + msg_Err( p_aout, "initialising fifo with zero divider" ); + } + + p_fifo->p_first = NULL; + p_fifo->pp_last = &p_fifo->p_first; + aout_DateInit( &p_fifo->end_date, i_rate ); +} + +/***************************************************************************** + * aout_FifoPush : push a packet into the FIFO + *****************************************************************************/ +void aout_FifoPush( aout_instance_t * p_aout, aout_fifo_t * p_fifo, + aout_buffer_t * p_buffer ) +{ + (void)p_aout; + AOUT_ASSERT_FIFO_LOCKED; + + *p_fifo->pp_last = p_buffer; + p_fifo->pp_last = &p_buffer->p_next; + *p_fifo->pp_last = NULL; + /* Enforce the continuity of the stream. */ + if ( aout_DateGet( &p_fifo->end_date ) ) + { + p_buffer->start_date = aout_DateGet( &p_fifo->end_date ); + p_buffer->end_date = aout_DateIncrement( &p_fifo->end_date, + p_buffer->i_nb_samples ); + } + else + { + aout_DateSet( &p_fifo->end_date, p_buffer->end_date ); + } +} + +/***************************************************************************** + * aout_FifoSet : set end_date and trash all buffers (because they aren't + * properly dated) + *****************************************************************************/ +void aout_FifoSet( aout_instance_t * p_aout, aout_fifo_t * p_fifo, + mtime_t date ) +{ + aout_buffer_t * p_buffer; + (void)p_aout; + AOUT_ASSERT_FIFO_LOCKED; + + aout_DateSet( &p_fifo->end_date, date ); + p_buffer = p_fifo->p_first; + while ( p_buffer != NULL ) + { + aout_buffer_t * p_next = p_buffer->p_next; + aout_BufferFree( p_buffer ); + p_buffer = p_next; + } + p_fifo->p_first = NULL; + p_fifo->pp_last = &p_fifo->p_first; +} + +/***************************************************************************** + * aout_FifoMoveDates : Move forwards or backwards all dates in the FIFO + *****************************************************************************/ +void aout_FifoMoveDates( aout_instance_t * p_aout, aout_fifo_t * p_fifo, + mtime_t difference ) +{ + aout_buffer_t * p_buffer; + (void)p_aout; + AOUT_ASSERT_FIFO_LOCKED; + + aout_DateMove( &p_fifo->end_date, difference ); + p_buffer = p_fifo->p_first; + while ( p_buffer != NULL ) + { + p_buffer->start_date += difference; + p_buffer->end_date += difference; + p_buffer = p_buffer->p_next; + } +} + +/***************************************************************************** + * aout_FifoNextStart : return the current end_date + *****************************************************************************/ +mtime_t aout_FifoNextStart( aout_instance_t * p_aout, aout_fifo_t * p_fifo ) +{ + (void)p_aout; + AOUT_ASSERT_FIFO_LOCKED; + return aout_DateGet( &p_fifo->end_date ); +} + +/***************************************************************************** + * aout_FifoFirstDate : return the playing date of the first buffer in the + * FIFO + *****************************************************************************/ +mtime_t aout_FifoFirstDate( aout_instance_t * p_aout, aout_fifo_t * p_fifo ) +{ + (void)p_aout; + AOUT_ASSERT_FIFO_LOCKED; + return p_fifo->p_first ? p_fifo->p_first->start_date : 0; +} + +/***************************************************************************** + * aout_FifoPop : get the next buffer out of the FIFO + *****************************************************************************/ +aout_buffer_t * aout_FifoPop( aout_instance_t * p_aout, aout_fifo_t * p_fifo ) +{ + aout_buffer_t * p_buffer; + (void)p_aout; + AOUT_ASSERT_FIFO_LOCKED; + + p_buffer = p_fifo->p_first; + if ( p_buffer == NULL ) return NULL; + p_fifo->p_first = p_buffer->p_next; + if ( p_fifo->p_first == NULL ) + { + p_fifo->pp_last = &p_fifo->p_first; + } + + return p_buffer; +} + +/***************************************************************************** + * aout_FifoDestroy : destroy a FIFO and its buffers + *****************************************************************************/ +void aout_FifoDestroy( aout_instance_t * p_aout, aout_fifo_t * p_fifo ) +{ + aout_buffer_t * p_buffer; + (void)p_aout; + AOUT_ASSERT_FIFO_LOCKED; + + p_buffer = p_fifo->p_first; + while ( p_buffer != NULL ) + { + aout_buffer_t * p_next = p_buffer->p_next; + aout_BufferFree( p_buffer ); + p_buffer = p_next; + } + + p_fifo->p_first = NULL; + p_fifo->pp_last = &p_fifo->p_first; +} + + +/* + * Date management (internal and external) + */ + +/***************************************************************************** + * aout_DateInit : set the divider of an audio_date_t + *****************************************************************************/ +void aout_DateInit( audio_date_t * p_date, uint32_t i_divider ) +{ + p_date->date = 0; + p_date->i_divider = i_divider; + p_date->i_remainder = 0; +} + +/***************************************************************************** + * aout_DateSet : set the date of an audio_date_t + *****************************************************************************/ +void aout_DateSet( audio_date_t * p_date, mtime_t new_date ) +{ + p_date->date = new_date; + p_date->i_remainder = 0; +} + +/***************************************************************************** + * aout_DateMove : move forwards or backwards the date of an audio_date_t + *****************************************************************************/ +void aout_DateMove( audio_date_t * p_date, mtime_t difference ) +{ + p_date->date += difference; +} + +/***************************************************************************** + * aout_DateGet : get the date of an audio_date_t + *****************************************************************************/ +mtime_t aout_DateGet( const audio_date_t * p_date ) +{ + return p_date->date; +} + +/***************************************************************************** + * aout_DateIncrement : increment the date and return the result, taking + * into account rounding errors + *****************************************************************************/ +mtime_t aout_DateIncrement( audio_date_t * p_date, uint32_t i_nb_samples ) +{ + mtime_t i_dividend = (mtime_t)i_nb_samples * 1000000; + p_date->date += i_dividend / p_date->i_divider; + p_date->i_remainder += (int)(i_dividend % p_date->i_divider); + if ( p_date->i_remainder >= p_date->i_divider ) + { + /* This is Bresenham algorithm. */ + p_date->date++; + p_date->i_remainder -= p_date->i_divider; + } + return p_date->date; +} + +/***************************************************************************** + * aout_CheckChannelReorder : Check if we need to do some channel re-ordering + *****************************************************************************/ +int aout_CheckChannelReorder( const uint32_t *pi_chan_order_in, + const uint32_t *pi_chan_order_out, + uint32_t i_channel_mask, + int i_channels, int *pi_chan_table ) +{ + bool b_chan_reorder = false; + int i, j, k, l; + + if( i_channels > AOUT_CHAN_MAX ) return false; + + for( i = 0, j = 0; pi_chan_order_in[i]; i++ ) + { + if( !(i_channel_mask & pi_chan_order_in[i]) ) continue; + + for( k = 0, l = 0; pi_chan_order_in[i] != pi_chan_order_out[k]; k++ ) + { + if( i_channel_mask & pi_chan_order_out[k] ) l++; + } + + pi_chan_table[j++] = l; + } + + for( i = 0; i < i_channels; i++ ) + { + if( pi_chan_table[i] != i ) b_chan_reorder = true; + } + + return b_chan_reorder; +} + +/***************************************************************************** + * aout_ChannelReorder : + *****************************************************************************/ +void aout_ChannelReorder( uint8_t *p_buf, int i_buffer, + int i_channels, const int *pi_chan_table, + int i_bits_per_sample ) +{ + uint8_t p_tmp[AOUT_CHAN_MAX * 4]; + int i, j; + + if( i_bits_per_sample == 8 ) + { + for( i = 0; i < i_buffer / i_channels; i++ ) + { + for( j = 0; j < i_channels; j++ ) + { + p_tmp[pi_chan_table[j]] = p_buf[j]; + } + + memcpy( p_buf, p_tmp, i_channels ); + p_buf += i_channels; + } + } + else if( i_bits_per_sample == 16 ) + { + for( i = 0; i < i_buffer / i_channels / 2; i++ ) + { + for( j = 0; j < i_channels; j++ ) + { + p_tmp[2 * pi_chan_table[j]] = p_buf[2 * j]; + p_tmp[2 * pi_chan_table[j] + 1] = p_buf[2 * j + 1]; + } + + memcpy( p_buf, p_tmp, 2 * i_channels ); + p_buf += 2 * i_channels; + } + } + else if( i_bits_per_sample == 24 ) + { + for( i = 0; i < i_buffer / i_channels / 3; i++ ) + { + for( j = 0; j < i_channels; j++ ) + { + p_tmp[3 * pi_chan_table[j]] = p_buf[3 * j]; + p_tmp[3 * pi_chan_table[j] + 1] = p_buf[3 * j + 1]; + p_tmp[3 * pi_chan_table[j] + 2] = p_buf[3 * j + 2]; + } + + memcpy( p_buf, p_tmp, 3 * i_channels ); + p_buf += 3 * i_channels; + } + } + else if( i_bits_per_sample == 32 ) + { + for( i = 0; i < i_buffer / i_channels / 4; i++ ) + { + for( j = 0; j < i_channels; j++ ) + { + p_tmp[4 * pi_chan_table[j]] = p_buf[4 * j]; + p_tmp[4 * pi_chan_table[j] + 1] = p_buf[4 * j + 1]; + p_tmp[4 * pi_chan_table[j] + 2] = p_buf[4 * j + 2]; + p_tmp[4 * pi_chan_table[j] + 3] = p_buf[4 * j + 3]; + } + + memcpy( p_buf, p_tmp, 4 * i_channels ); + p_buf += 4 * i_channels; + } + } +} diff --git a/VLC/audio_output/dec.c b/VLC/audio_output/dec.c new file mode 100644 index 0000000..da15ab4 --- /dev/null +++ b/VLC/audio_output/dec.c @@ -0,0 +1,414 @@ +/***************************************************************************** + * dec.c : audio output API towards decoders + ***************************************************************************** + * Copyright (C) 2002-2007 the VideoLAN team + * $Id$ + * + * Authors: Christophe Massiot + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#ifdef HAVE_ALLOCA_H +# include +#endif + +#include "vlc_aout.h" +#include "vlc_input.h" + +#include "aout_internal.h" + +/** FIXME: Ugly but needed to access the counters */ +#include "input_internal.h" + +/***************************************************************************** + * aout_DecNew : create a decoder + *****************************************************************************/ +static aout_input_t * DecNew( vlc_object_t * p_this, aout_instance_t * p_aout, + audio_sample_format_t *p_format, + audio_replay_gain_t *p_replay_gain ) +{ + aout_input_t * p_input; + input_thread_t * p_input_thread; + + /* Sanitize audio format */ + if( p_format->i_channels > 32 ) + { + msg_Err( p_aout, "too many audio channels (%u)", + p_format->i_channels ); + return NULL; + } + if( p_format->i_channels <= 0 ) + { + msg_Err( p_aout, "no audio channels" ); + return NULL; + } + + if( p_format->i_rate > 192000 ) + { + msg_Err( p_aout, "excessive audio sample frequency (%u)", + p_format->i_rate ); + return NULL; + } + if( p_format->i_rate < 4000 ) + { + msg_Err( p_aout, "too low audio sample frequency (%u)", + p_format->i_rate ); + return NULL; + } + + /* We can only be called by the decoder, so no need to lock + * p_input->lock. */ + aout_lock_mixer( p_aout ); + + if ( p_aout->i_nb_inputs >= AOUT_MAX_INPUTS ) + { + msg_Err( p_aout, "too many inputs already (%d)", p_aout->i_nb_inputs ); + goto error; + } + + p_input = malloc(sizeof(aout_input_t)); + if ( p_input == NULL ) + goto error; + memset( p_input, 0, sizeof(aout_input_t) ); + + vlc_mutex_init( &p_input->lock ); + + p_input->b_changed = 0; + p_input->b_error = 1; + + aout_FormatPrepare( p_format ); + + memcpy( &p_input->input, p_format, + sizeof(audio_sample_format_t) ); + if( p_replay_gain ) + p_input->replay_gain = *p_replay_gain; + + aout_lock_input_fifos( p_aout ); + p_aout->pp_inputs[p_aout->i_nb_inputs] = p_input; + p_aout->i_nb_inputs++; + + if ( p_aout->mixer.b_error ) + { + int i; + + var_Destroy( p_aout, "audio-device" ); + var_Destroy( p_aout, "audio-channels" ); + + /* Recreate the output using the new format. */ + if ( aout_OutputNew( p_aout, p_format ) < 0 ) + { + for ( i = 0; i < p_aout->i_nb_inputs - 1; i++ ) + { + aout_lock_input( p_aout, p_aout->pp_inputs[i] ); + aout_InputDelete( p_aout, p_aout->pp_inputs[i] ); + aout_unlock_input( p_aout, p_aout->pp_inputs[i] ); + } + aout_unlock_input_fifos( p_aout ); + aout_unlock_mixer( p_aout ); + return p_input; + } + + /* Create other input streams. */ + for ( i = 0; i < p_aout->i_nb_inputs - 1; i++ ) + { + aout_lock_input( p_aout, p_aout->pp_inputs[i] ); + aout_InputDelete( p_aout, p_aout->pp_inputs[i] ); + aout_InputNew( p_aout, p_aout->pp_inputs[i] ); + aout_unlock_input( p_aout, p_aout->pp_inputs[i] ); + } + } + else + { + aout_MixerDelete( p_aout ); + } + + if ( aout_MixerNew( p_aout ) == -1 ) + { + aout_OutputDelete( p_aout ); + aout_unlock_input_fifos( p_aout ); + goto error; + } + + aout_InputNew( p_aout, p_input ); + aout_unlock_input_fifos( p_aout ); + + aout_unlock_mixer( p_aout ); + p_input->i_desync = var_CreateGetInteger( p_this, "audio-desync" ) * 1000; + + p_input_thread = (input_thread_t *)vlc_object_find( p_this, + VLC_OBJECT_INPUT, FIND_PARENT ); + if( p_input_thread ) + { + p_input->i_pts_delay = p_input_thread->i_pts_delay; + p_input->i_pts_delay += p_input->i_desync; + p_input->p_input_thread = p_input_thread; + vlc_object_release( p_input_thread ); + } + else + { + p_input->i_pts_delay = DEFAULT_PTS_DELAY; + p_input->i_pts_delay += p_input->i_desync; + p_input->p_input_thread = NULL; + } + + return p_input; + +error: + aout_unlock_mixer( p_aout ); + return NULL; +} + +aout_input_t * __aout_DecNew( vlc_object_t * p_this, + aout_instance_t ** pp_aout, + audio_sample_format_t * p_format, + audio_replay_gain_t *p_replay_gain ) +{ + aout_instance_t *p_aout = *pp_aout; + if ( p_aout == NULL ) + { + msg_Dbg( p_this, "no aout present, spawning one" ); + p_aout = aout_New( p_this ); + + /* Everything failed, I'm a loser, I just wanna die */ + if( p_aout == NULL ) + return NULL; + + vlc_object_attach( p_aout, p_this ); + *pp_aout = p_aout; + } + + return DecNew( p_this, p_aout, p_format, p_replay_gain ); +} + +/***************************************************************************** + * aout_DecDelete : delete a decoder + *****************************************************************************/ +int aout_DecDelete( aout_instance_t * p_aout, aout_input_t * p_input ) +{ + int i_input; + + /* This function can only be called by the decoder itself, so no need + * to lock p_input->lock. */ + aout_lock_mixer( p_aout ); + + for ( i_input = 0; i_input < p_aout->i_nb_inputs; i_input++ ) + { + if ( p_aout->pp_inputs[i_input] == p_input ) + { + break; + } + } + + if ( i_input == p_aout->i_nb_inputs ) + { + msg_Err( p_aout, "cannot find an input to delete" ); + aout_unlock_mixer( p_aout ); + return -1; + } + + /* Remove the input from the list. */ + memmove( &p_aout->pp_inputs[i_input], &p_aout->pp_inputs[i_input + 1], + (AOUT_MAX_INPUTS - i_input - 1) * sizeof(aout_input_t *) ); + p_aout->i_nb_inputs--; + + aout_InputDelete( p_aout, p_input ); + + vlc_mutex_destroy( &p_input->lock ); + free( p_input ); + + if ( !p_aout->i_nb_inputs ) + { + aout_OutputDelete( p_aout ); + aout_MixerDelete( p_aout ); + if ( var_Type( p_aout, "audio-device" ) != 0 ) + { + var_Destroy( p_aout, "audio-device" ); + } + if ( var_Type( p_aout, "audio-channels" ) != 0 ) + { + var_Destroy( p_aout, "audio-channels" ); + } + } + + aout_unlock_mixer( p_aout ); + + return 0; +} + + +/* + * Buffer management + */ + +/***************************************************************************** + * aout_DecNewBuffer : ask for a new empty buffer + *****************************************************************************/ +aout_buffer_t * aout_DecNewBuffer( aout_input_t * p_input, + size_t i_nb_samples ) +{ + aout_buffer_t * p_buffer; + mtime_t duration; + + aout_lock_input( NULL, p_input ); + + if ( p_input->b_error ) + { + aout_unlock_input( NULL, p_input ); + return NULL; + } + + duration = (1000000 * (mtime_t)i_nb_samples) / p_input->input.i_rate; + + /* This necessarily allocates in the heap. */ + aout_BufferAlloc( &p_input->input_alloc, duration, NULL, p_buffer ); + if( p_buffer != NULL ) + p_buffer->i_nb_bytes = i_nb_samples * p_input->input.i_bytes_per_frame + / p_input->input.i_frame_length; + + /* Suppose the decoder doesn't have more than one buffered buffer */ + p_input->b_changed = 0; + + aout_unlock_input( NULL, p_input ); + + if( p_buffer == NULL ) + return NULL; + + p_buffer->i_nb_samples = i_nb_samples; + p_buffer->start_date = p_buffer->end_date = 0; + return p_buffer; +} + +/***************************************************************************** + * aout_DecDeleteBuffer : destroy an undecoded buffer + *****************************************************************************/ +void aout_DecDeleteBuffer( aout_instance_t * p_aout, aout_input_t * p_input, + aout_buffer_t * p_buffer ) +{ + (void)p_aout; (void)p_input; + aout_BufferFree( p_buffer ); +} + +/***************************************************************************** + * aout_DecPlay : filter & mix the decoded buffer + *****************************************************************************/ +int aout_DecPlay( aout_instance_t * p_aout, aout_input_t * p_input, + aout_buffer_t * p_buffer, int i_input_rate ) +{ + if ( p_buffer->start_date == 0 ) + { + msg_Warn( p_aout, "non-dated buffer received" ); + aout_BufferFree( p_buffer ); + return -1; + } + +#ifndef FIXME + /* This hack for #transcode{acodec=...}:display to work -- Courmisch */ + if( i_input_rate == 0 ) + i_input_rate = INPUT_RATE_DEFAULT; +#endif + if( i_input_rate > INPUT_RATE_DEFAULT * AOUT_MAX_INPUT_RATE || + i_input_rate < INPUT_RATE_DEFAULT / AOUT_MAX_INPUT_RATE ) + { + aout_BufferFree( p_buffer ); + return 0; + } + + /* Apply the desynchronisation requested by the user */ + p_buffer->start_date += p_input->i_desync; + p_buffer->end_date += p_input->i_desync; + + if ( p_buffer->start_date > mdate() + p_input->i_pts_delay + + AOUT_MAX_ADVANCE_TIME ) + { + msg_Warn( p_aout, "received buffer in the future (%"PRId64")", + p_buffer->start_date - mdate()); + if( p_input->p_input_thread ) + { + vlc_mutex_lock( &p_input->p_input_thread->p->counters.counters_lock); + stats_UpdateInteger( p_aout, + p_input->p_input_thread->p->counters.p_lost_abuffers, + 1, NULL ); + vlc_mutex_unlock( &p_input->p_input_thread->p->counters.counters_lock); + } + aout_BufferFree( p_buffer ); + return -1; + } + + p_buffer->end_date = p_buffer->start_date + + (mtime_t)p_buffer->i_nb_samples * 1000000 + / p_input->input.i_rate; + + aout_lock_input( p_aout, p_input ); + + if ( p_input->b_error ) + { + aout_unlock_input( p_aout, p_input ); + aout_BufferFree( p_buffer ); + return -1; + } + + if ( p_input->b_changed ) + { + /* Maybe the allocation size has changed. Re-allocate a buffer. */ + aout_buffer_t * p_new_buffer; + mtime_t duration = (1000000 * (mtime_t)p_buffer->i_nb_samples) + / p_input->input.i_rate; + + aout_BufferAlloc( &p_input->input_alloc, duration, NULL, p_new_buffer ); + vlc_memcpy( p_new_buffer->p_buffer, p_buffer->p_buffer, + p_buffer->i_nb_bytes ); + p_new_buffer->i_nb_samples = p_buffer->i_nb_samples; + p_new_buffer->i_nb_bytes = p_buffer->i_nb_bytes; + p_new_buffer->start_date = p_buffer->start_date; + p_new_buffer->end_date = p_buffer->end_date; + aout_BufferFree( p_buffer ); + p_buffer = p_new_buffer; + p_input->b_changed = 0; + } + + /* If the buffer is too early, wait a while. */ + mwait( p_buffer->start_date - AOUT_MAX_PREPARE_TIME ); + + int ret = aout_InputPlay( p_aout, p_input, p_buffer, i_input_rate ); + + aout_unlock_input( p_aout, p_input ); + + if ( ret == -1 ) return -1; + + /* Run the mixer if it is able to run. */ + aout_lock_mixer( p_aout ); + aout_MixerRun( p_aout ); + if( p_input->p_input_thread ) + { + vlc_mutex_lock( &p_input->p_input_thread->p->counters.counters_lock); + stats_UpdateInteger( p_aout, + p_input->p_input_thread->p->counters.p_played_abuffers, + 1, NULL ); + vlc_mutex_unlock( &p_input->p_input_thread->p->counters.counters_lock); + } + aout_unlock_mixer( p_aout ); + + return 0; +} diff --git a/VLC/audio_output/filters.c b/VLC/audio_output/filters.c new file mode 100644 index 0000000..2159553 --- /dev/null +++ b/VLC/audio_output/filters.c @@ -0,0 +1,367 @@ +/***************************************************************************** + * filters.c : audio output filters management + ***************************************************************************** + * Copyright (C) 2002-2007 the VideoLAN team + * $Id$ + * + * Authors: Christophe Massiot + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "vlc_interface.h" + +#ifdef HAVE_ALLOCA_H +# include +#endif + +#include "vlc_aout.h" +#include "aout_internal.h" +#include "libvlc.h" + +/***************************************************************************** + * FindFilter: find an audio filter for a specific transformation + *****************************************************************************/ +static aout_filter_t * FindFilter( aout_instance_t * p_aout, + const audio_sample_format_t * p_input_format, + const audio_sample_format_t * p_output_format ) +{ + static const char typename[] = "audio output"; + aout_filter_t * p_filter; + + p_filter = vlc_custom_create( p_aout, sizeof(*p_filter), + VLC_OBJECT_GENERIC, typename ); + + if ( p_filter == NULL ) return NULL; + vlc_object_attach( p_filter, p_aout ); + + memcpy( &p_filter->input, p_input_format, sizeof(audio_sample_format_t) ); + memcpy( &p_filter->output, p_output_format, + sizeof(audio_sample_format_t) ); + p_filter->p_module = module_Need( p_filter, "audio filter", NULL, 0 ); + if ( p_filter->p_module == NULL ) + { + vlc_object_detach( p_filter ); + vlc_object_release( p_filter ); + return NULL; + } + + p_filter->b_continuity = false; + + return p_filter; +} + +/***************************************************************************** + * SplitConversion: split a conversion in two parts + ***************************************************************************** + * Returns the number of conversions required by the first part - 0 if only + * one conversion was asked. + * Beware : p_output_format can be modified during this function if the + * developer passed SplitConversion( toto, titi, titi, ... ). That is legal. + * SplitConversion( toto, titi, toto, ... ) isn't. + *****************************************************************************/ +static int SplitConversion( const audio_sample_format_t * p_input_format, + const audio_sample_format_t * p_output_format, + audio_sample_format_t * p_middle_format ) +{ + bool b_format = + (p_input_format->i_format != p_output_format->i_format); + bool b_rate = (p_input_format->i_rate != p_output_format->i_rate); + bool b_channels = + (p_input_format->i_physical_channels + != p_output_format->i_physical_channels) + || (p_input_format->i_original_channels + != p_output_format->i_original_channels); + int i_nb_conversions = b_format + b_rate + b_channels; + + if ( i_nb_conversions <= 1 ) return 0; + + memcpy( p_middle_format, p_output_format, sizeof(audio_sample_format_t) ); + + if ( i_nb_conversions == 2 ) + { + if ( !b_format || !b_channels ) + { + p_middle_format->i_rate = p_input_format->i_rate; + aout_FormatPrepare( p_middle_format ); + return 1; + } + + /* !b_rate */ + p_middle_format->i_physical_channels + = p_input_format->i_physical_channels; + p_middle_format->i_original_channels + = p_input_format->i_original_channels; + aout_FormatPrepare( p_middle_format ); + return 1; + } + + /* i_nb_conversion == 3 */ + p_middle_format->i_rate = p_input_format->i_rate; + aout_FormatPrepare( p_middle_format ); + return 2; +} + +static void ReleaseFilter( aout_filter_t * p_filter ) +{ + module_Unneed( p_filter, p_filter->p_module ); + vlc_object_detach( p_filter ); + vlc_object_release( p_filter ); +} + +/***************************************************************************** + * aout_FiltersCreatePipeline: create a filters pipeline to transform a sample + * format to another + ***************************************************************************** + * pi_nb_filters must be initialized before calling this function + *****************************************************************************/ +int aout_FiltersCreatePipeline( aout_instance_t * p_aout, + aout_filter_t ** pp_filters_start, + int * pi_nb_filters, + const audio_sample_format_t * p_input_format, + const audio_sample_format_t * p_output_format ) +{ + aout_filter_t** pp_filters = pp_filters_start + *pi_nb_filters; + audio_sample_format_t temp_format; + int i_nb_conversions; + + if ( AOUT_FMTS_IDENTICAL( p_input_format, p_output_format ) ) + { + msg_Dbg( p_aout, "no need for any filter" ); + return 0; + } + + aout_FormatsPrint( p_aout, "filter(s)", p_input_format, p_output_format ); + + if( *pi_nb_filters + 1 > AOUT_MAX_FILTERS ) + { + msg_Err( p_aout, "max filter reached (%d)", AOUT_MAX_FILTERS ); + intf_UserFatal( p_aout, false, _("Audio filtering failed"), + _("The maximum number of filters (%d) was reached."), + AOUT_MAX_FILTERS ); + return -1; + } + + /* Try to find a filter to do the whole conversion. */ + pp_filters[0] = FindFilter( p_aout, p_input_format, p_output_format ); + if ( pp_filters[0] != NULL ) + { + msg_Dbg( p_aout, "found a filter for the whole conversion" ); + ++*pi_nb_filters; + return 0; + } + + /* We'll have to split the conversion. We always do the downmixing + * before the resampling, because the audio decoder can probably do it + * for us. */ + i_nb_conversions = SplitConversion( p_input_format, + p_output_format, &temp_format ); + if ( !i_nb_conversions ) + { + /* There was only one conversion to do, and we already failed. */ + msg_Err( p_aout, "couldn't find a filter for the conversion" ); + return -1; + } + + pp_filters[0] = FindFilter( p_aout, p_input_format, &temp_format ); + if ( pp_filters[0] == NULL && i_nb_conversions == 2 ) + { + /* Try with only one conversion. */ + SplitConversion( p_input_format, &temp_format, &temp_format ); + pp_filters[0] = FindFilter( p_aout, p_input_format, &temp_format ); + } + if ( pp_filters[0] == NULL ) + { + msg_Err( p_aout, + "couldn't find a filter for the first part of the conversion" ); + return -1; + } + + /* We have the first stage of the conversion. Find a filter for + * the rest. */ + if( *pi_nb_filters + 2 > AOUT_MAX_FILTERS ) + { + ReleaseFilter( pp_filters[0] ); + msg_Err( p_aout, "max filter reached (%d)", AOUT_MAX_FILTERS ); + intf_UserFatal( p_aout, false, _("Audio filtering failed"), + _("The maximum number of filters (%d) was reached."), + AOUT_MAX_FILTERS ); + return -1; + } + pp_filters[1] = FindFilter( p_aout, &pp_filters[0]->output, + p_output_format ); + if ( pp_filters[1] == NULL ) + { + /* Try to split the conversion. */ + i_nb_conversions = SplitConversion( &pp_filters[0]->output, + p_output_format, &temp_format ); + if ( !i_nb_conversions ) + { + ReleaseFilter( pp_filters[0] ); + msg_Err( p_aout, + "couldn't find a filter for the second part of the conversion" ); + return -1; + } + if( *pi_nb_filters + 3 > AOUT_MAX_FILTERS ) + { + ReleaseFilter( pp_filters[0] ); + msg_Err( p_aout, "max filter reached (%d)", AOUT_MAX_FILTERS ); + intf_UserFatal( p_aout, false, _("Audio filtering failed"), + _("The maximum number of filters (%d) was reached."), + AOUT_MAX_FILTERS ); + return -1; + } + pp_filters[1] = FindFilter( p_aout, &pp_filters[0]->output, + &temp_format ); + pp_filters[2] = FindFilter( p_aout, &temp_format, + p_output_format ); + + if ( pp_filters[1] == NULL || pp_filters[2] == NULL ) + { + ReleaseFilter( pp_filters[0] ); + if ( pp_filters[1] != NULL ) + { + ReleaseFilter( pp_filters[1] ); + } + if ( pp_filters[2] != NULL ) + { + ReleaseFilter( pp_filters[2] ); + } + msg_Err( p_aout, + "couldn't find filters for the second part of the conversion" ); + return -1; + } + *pi_nb_filters += 3; + msg_Dbg( p_aout, "found 3 filters for the whole conversion" ); + } + else + { + *pi_nb_filters += 2; + msg_Dbg( p_aout, "found 2 filters for the whole conversion" ); + } + + return 0; +} + +/***************************************************************************** + * aout_FiltersDestroyPipeline: deallocate a filters pipeline + *****************************************************************************/ +void aout_FiltersDestroyPipeline( aout_instance_t * p_aout, + aout_filter_t ** pp_filters, + int i_nb_filters ) +{ + int i; + (void)p_aout; + + for ( i = 0; i < i_nb_filters; i++ ) + { + module_Unneed( pp_filters[i], pp_filters[i]->p_module ); + vlc_object_detach( pp_filters[i] ); + vlc_object_release( pp_filters[i] ); + } +} + +/***************************************************************************** + * aout_FiltersHintBuffers: fill in aout_alloc_t structures to optimize + * buffer allocations + *****************************************************************************/ +void aout_FiltersHintBuffers( aout_instance_t * p_aout, + aout_filter_t ** pp_filters, + int i_nb_filters, aout_alloc_t * p_first_alloc ) +{ + int i; + + (void)p_aout; /* unused */ + + for ( i = i_nb_filters - 1; i >= 0; i-- ) + { + aout_filter_t * p_filter = pp_filters[i]; + + int i_output_size = p_filter->output.i_bytes_per_frame + * p_filter->output.i_rate * AOUT_MAX_INPUT_RATE + / p_filter->output.i_frame_length; + int i_input_size = p_filter->input.i_bytes_per_frame + * p_filter->input.i_rate * AOUT_MAX_INPUT_RATE + / p_filter->input.i_frame_length; + + p_first_alloc->i_bytes_per_sec = __MAX( p_first_alloc->i_bytes_per_sec, + i_output_size ); + + if ( p_filter->b_in_place ) + { + p_first_alloc->i_bytes_per_sec = __MAX( + p_first_alloc->i_bytes_per_sec, + i_input_size ); + p_filter->output_alloc.i_alloc_type = AOUT_ALLOC_NONE; + } + else + { + /* We're gonna need a buffer allocation. */ + memcpy( &p_filter->output_alloc, p_first_alloc, + sizeof(aout_alloc_t) ); + p_first_alloc->i_alloc_type = AOUT_ALLOC_STACK; + p_first_alloc->i_bytes_per_sec = i_input_size; + } + } +} + +/***************************************************************************** + * aout_FiltersPlay: play a buffer + *****************************************************************************/ +void aout_FiltersPlay( aout_instance_t * p_aout, + aout_filter_t ** pp_filters, + int i_nb_filters, aout_buffer_t ** pp_input_buffer ) +{ + int i; + + for ( i = 0; i < i_nb_filters; i++ ) + { + aout_filter_t * p_filter = pp_filters[i]; + aout_buffer_t * p_output_buffer; + + /* Resamplers can produce slightly more samples than (i_in_nb * + * p_filter->output.i_rate / p_filter->input.i_rate) so we need + * slightly bigger buffers. */ + aout_BufferAlloc( &p_filter->output_alloc, + ((mtime_t)(*pp_input_buffer)->i_nb_samples + 2) + * 1000000 / p_filter->input.i_rate, + *pp_input_buffer, p_output_buffer ); + if ( p_output_buffer == NULL ) + return; + /* Please note that p_output_buffer->i_nb_samples & i_nb_bytes + * shall be set by the filter plug-in. */ + + p_filter->pf_do_work( p_aout, p_filter, *pp_input_buffer, + p_output_buffer ); + + if ( !p_filter->b_in_place ) + { + aout_BufferFree( *pp_input_buffer ); + *pp_input_buffer = p_output_buffer; + } + + if( p_output_buffer->i_nb_samples <= 0 ) + break; + } +} + diff --git a/VLC/audio_output/input.c b/VLC/audio_output/input.c new file mode 100644 index 0000000..bdc0837 --- /dev/null +++ b/VLC/audio_output/input.c @@ -0,0 +1,927 @@ +/***************************************************************************** + * input.c : internal management of input streams for the audio output + ***************************************************************************** + * Copyright (C) 2002-2007 the VideoLAN team + * $Id$ + * + * Authors: Christophe Massiot + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#include +#include +#include +#include + +#include "vlc_input.h" /* for input_thread_t and i_pts_delay */ + +#ifdef HAVE_ALLOCA_H +# include +#endif +#include "vlc_aout.h" + +#include "aout_internal.h" + +/** FIXME: Ugly but needed to access the counters */ +#include "input_internal.h" + +#define AOUT_ASSERT_MIXER_LOCKED vlc_assert_locked( &p_aout->mixer_lock ) +#define AOUT_ASSERT_INPUT_LOCKED vlc_assert_locked( &p_input->lock ) + +static void inputFailure( aout_instance_t *, aout_input_t *, const char * ); +static void inputDrop( aout_instance_t *, aout_input_t *, aout_buffer_t * ); +static void inputResamplingStop( aout_input_t *p_input ); + +static int VisualizationCallback( vlc_object_t *, char const *, + vlc_value_t, vlc_value_t, void * ); +static int EqualizerCallback( vlc_object_t *, char const *, + vlc_value_t, vlc_value_t, void * ); +static int ReplayGainCallback( vlc_object_t *, char const *, + vlc_value_t, vlc_value_t, void * ); +static void ReplayGainSelect( aout_instance_t *, aout_input_t * ); +/***************************************************************************** + * aout_InputNew : allocate a new input and rework the filter pipeline + *****************************************************************************/ +int aout_InputNew( aout_instance_t * p_aout, aout_input_t * p_input ) +{ + audio_sample_format_t chain_input_format; + audio_sample_format_t chain_output_format; + vlc_value_t val, text; + char * psz_filters, *psz_visual; + int i_visual; + + aout_FormatPrint( p_aout, "input", &p_input->input ); + + p_input->i_nb_resamplers = p_input->i_nb_filters = 0; + + /* Prepare FIFO. */ + aout_FifoInit( p_aout, &p_input->fifo, p_aout->mixer.mixer.i_rate ); + p_input->p_first_byte_to_mix = NULL; + + /* Prepare format structure */ + memcpy( &chain_input_format, &p_input->input, + sizeof(audio_sample_format_t) ); + memcpy( &chain_output_format, &p_aout->mixer.mixer, + sizeof(audio_sample_format_t) ); + chain_output_format.i_rate = p_input->input.i_rate; + aout_FormatPrepare( &chain_output_format ); + + /* Now add user filters */ + if( var_Type( p_aout, "visual" ) == 0 ) + { + var_Create( p_aout, "visual", VLC_VAR_STRING | VLC_VAR_HASCHOICE ); + text.psz_string = _("Visualizations"); + var_Change( p_aout, "visual", VLC_VAR_SETTEXT, &text, NULL ); + val.psz_string = (char*)""; text.psz_string = _("Disable"); + var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text ); + val.psz_string = (char*)"spectrometer"; text.psz_string = _("Spectrometer"); + var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text ); + val.psz_string = (char*)"scope"; text.psz_string = _("Scope"); + var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text ); + val.psz_string = (char*)"spectrum"; text.psz_string = _("Spectrum"); + var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text ); + val.psz_string = (char*)"vuMeter"; text.psz_string = _("Vu meter"); + var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text ); + + /* Look for goom plugin */ + if( module_Exists( VLC_OBJECT(p_aout), "goom" ) ) + { + val.psz_string = (char*)"goom"; text.psz_string = (char*)"Goom"; + var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text ); + } + + /* Look for galaktos plugin */ + if( module_Exists( VLC_OBJECT(p_aout), "galaktos" ) ) + { + val.psz_string = (char*)"galaktos"; text.psz_string = (char*)"GaLaktos"; + var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text ); + } + + if( var_Get( p_aout, "effect-list", &val ) == VLC_SUCCESS ) + { + var_Set( p_aout, "visual", val ); + free( val.psz_string ); + } + var_AddCallback( p_aout, "visual", VisualizationCallback, NULL ); + } + + if( var_Type( p_aout, "equalizer" ) == 0 ) + { + module_config_t *p_config; + int i; + + p_config = config_FindConfig( VLC_OBJECT(p_aout), "equalizer-preset" ); + if( p_config && p_config->i_list ) + { + var_Create( p_aout, "equalizer", + VLC_VAR_STRING | VLC_VAR_HASCHOICE ); + text.psz_string = _("Equalizer"); + var_Change( p_aout, "equalizer", VLC_VAR_SETTEXT, &text, NULL ); + + val.psz_string = (char*)""; text.psz_string = _("Disable"); + var_Change( p_aout, "equalizer", VLC_VAR_ADDCHOICE, &val, &text ); + + for( i = 0; i < p_config->i_list; i++ ) + { + val.psz_string = (char *)p_config->ppsz_list[i]; + text.psz_string = (char *)p_config->ppsz_list_text[i]; + var_Change( p_aout, "equalizer", VLC_VAR_ADDCHOICE, + &val, &text ); + } + + var_AddCallback( p_aout, "equalizer", EqualizerCallback, NULL ); + } + } + + if( var_Type( p_aout, "audio-filter" ) == 0 ) + { + var_Create( p_aout, "audio-filter", + VLC_VAR_STRING | VLC_VAR_DOINHERIT ); + text.psz_string = _("Audio filters"); + var_Change( p_aout, "audio-filter", VLC_VAR_SETTEXT, &text, NULL ); + } + if( var_Type( p_aout, "audio-visual" ) == 0 ) + { + var_Create( p_aout, "audio-visual", + VLC_VAR_STRING | VLC_VAR_DOINHERIT ); + text.psz_string = _("Audio visualizations"); + var_Change( p_aout, "audio-visual", VLC_VAR_SETTEXT, &text, NULL ); + } + + if( var_Type( p_aout, "audio-replay-gain-mode" ) == 0 ) + { + module_config_t *p_config; + int i; + + p_config = config_FindConfig( VLC_OBJECT(p_aout), "audio-replay-gain-mode" ); + if( p_config && p_config->i_list ) + { + var_Create( p_aout, "audio-replay-gain-mode", + VLC_VAR_STRING | VLC_VAR_DOINHERIT ); + + text.psz_string = _("Replay gain"); + var_Change( p_aout, "audio-replay-gain-mode", VLC_VAR_SETTEXT, &text, NULL ); + + for( i = 0; i < p_config->i_list; i++ ) + { + val.psz_string = (char *)p_config->ppsz_list[i]; + text.psz_string = (char *)p_config->ppsz_list_text[i]; + var_Change( p_aout, "audio-replay-gain-mode", VLC_VAR_ADDCHOICE, + &val, &text ); + } + + var_AddCallback( p_aout, "audio-replay-gain-mode", ReplayGainCallback, NULL ); + } + } + if( var_Type( p_aout, "audio-replay-gain-preamp" ) == 0 ) + { + var_Create( p_aout, "audio-replay-gain-preamp", + VLC_VAR_FLOAT | VLC_VAR_DOINHERIT ); + } + if( var_Type( p_aout, "audio-replay-gain-default" ) == 0 ) + { + var_Create( p_aout, "audio-replay-gain-default", + VLC_VAR_FLOAT | VLC_VAR_DOINHERIT ); + } + if( var_Type( p_aout, "audio-replay-gain-peak-protection" ) == 0 ) + { + var_Create( p_aout, "audio-replay-gain-peak-protection", + VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); + } + + var_Get( p_aout, "audio-filter", &val ); + psz_filters = val.psz_string; + var_Get( p_aout, "audio-visual", &val ); + psz_visual = val.psz_string; + + /* parse user filter lists */ + for( i_visual = 0; i_visual < 2; i_visual++ ) + { + char *psz_next = NULL; + char *psz_parser = i_visual ? psz_visual : psz_filters; + + if( psz_parser == NULL || !*psz_parser ) + continue; + + while( psz_parser && *psz_parser ) + { + aout_filter_t * p_filter = NULL; + + if( p_input->i_nb_filters >= AOUT_MAX_FILTERS ) + { + msg_Dbg( p_aout, "max filters reached (%d)", AOUT_MAX_FILTERS ); + break; + } + + while( *psz_parser == ' ' && *psz_parser == ':' ) + { + psz_parser++; + } + if( ( psz_next = strchr( psz_parser , ':' ) ) ) + { + *psz_next++ = '\0'; + } + if( *psz_parser =='\0' ) + { + break; + } + + /* Create a VLC object */ + static const char typename[] = "audio filter"; + p_filter = vlc_custom_create( p_aout, sizeof(*p_filter), + VLC_OBJECT_GENERIC, typename ); + if( p_filter == NULL ) + { + msg_Err( p_aout, "cannot add user filter %s (skipped)", + psz_parser ); + psz_parser = psz_next; + continue; + } + + vlc_object_attach( p_filter , p_aout ); + + /* try to find the requested filter */ + if( i_visual == 1 ) /* this can only be a visualization module */ + { + /* request format */ + memcpy( &p_filter->input, &chain_output_format, + sizeof(audio_sample_format_t) ); + memcpy( &p_filter->output, &chain_output_format, + sizeof(audio_sample_format_t) ); + + p_filter->p_module = module_Need( p_filter, "visualization", + psz_parser, true ); + } + else /* this can be a audio filter module as well as a visualization module */ + { + /* request format */ + memcpy( &p_filter->input, &chain_input_format, + sizeof(audio_sample_format_t) ); + memcpy( &p_filter->output, &chain_output_format, + sizeof(audio_sample_format_t) ); + + p_filter->p_module = module_Need( p_filter, "audio filter", + psz_parser, true ); + + if ( p_filter->p_module == NULL ) + { + /* if the filter requested a special format, retry */ + if ( !( AOUT_FMTS_IDENTICAL( &p_filter->input, + &chain_input_format ) + && AOUT_FMTS_IDENTICAL( &p_filter->output, + &chain_output_format ) ) ) + { + aout_FormatPrepare( &p_filter->input ); + aout_FormatPrepare( &p_filter->output ); + p_filter->p_module = module_Need( p_filter, + "audio filter", + psz_parser, true ); + } + /* try visual filters */ + else + { + memcpy( &p_filter->input, &chain_output_format, + sizeof(audio_sample_format_t) ); + memcpy( &p_filter->output, &chain_output_format, + sizeof(audio_sample_format_t) ); + p_filter->p_module = module_Need( p_filter, + "visualization", + psz_parser, true ); + } + } + } + + /* failure */ + if ( p_filter->p_module == NULL ) + { + msg_Err( p_aout, "cannot add user filter %s (skipped)", + psz_parser ); + + vlc_object_detach( p_filter ); + vlc_object_release( p_filter ); + + psz_parser = psz_next; + continue; + } + + /* complete the filter chain if necessary */ + if ( !AOUT_FMTS_IDENTICAL( &chain_input_format, &p_filter->input ) ) + { + if ( aout_FiltersCreatePipeline( p_aout, p_input->pp_filters, + &p_input->i_nb_filters, + &chain_input_format, + &p_filter->input ) < 0 ) + { + msg_Err( p_aout, "cannot add user filter %s (skipped)", + psz_parser ); + + module_Unneed( p_filter, p_filter->p_module ); + vlc_object_detach( p_filter ); + vlc_object_release( p_filter ); + + psz_parser = psz_next; + continue; + } + } + + /* success */ + p_filter->b_continuity = false; + p_input->pp_filters[p_input->i_nb_filters++] = p_filter; + memcpy( &chain_input_format, &p_filter->output, + sizeof( audio_sample_format_t ) ); + + /* next filter if any */ + psz_parser = psz_next; + } + } + free( psz_filters ); + free( psz_visual ); + + /* complete the filter chain if necessary */ + if ( !AOUT_FMTS_IDENTICAL( &chain_input_format, &chain_output_format ) ) + { + if ( aout_FiltersCreatePipeline( p_aout, p_input->pp_filters, + &p_input->i_nb_filters, + &chain_input_format, + &chain_output_format ) < 0 ) + { + inputFailure( p_aout, p_input, "couldn't set an input pipeline" ); + return -1; + } + } + + /* Prepare hints for the buffer allocator. */ + p_input->input_alloc.i_alloc_type = AOUT_ALLOC_HEAP; + p_input->input_alloc.i_bytes_per_sec = -1; + + /* Create resamplers. */ + if ( !AOUT_FMT_NON_LINEAR( &p_aout->mixer.mixer ) ) + { + chain_output_format.i_rate = (__MAX(p_input->input.i_rate, + p_aout->mixer.mixer.i_rate) + * (100 + AOUT_MAX_RESAMPLING)) / 100; + if ( chain_output_format.i_rate == p_aout->mixer.mixer.i_rate ) + { + /* Just in case... */ + chain_output_format.i_rate++; + } + if ( aout_FiltersCreatePipeline( p_aout, p_input->pp_resamplers, + &p_input->i_nb_resamplers, + &chain_output_format, + &p_aout->mixer.mixer ) < 0 ) + { + inputFailure( p_aout, p_input, "couldn't set a resampler pipeline"); + return -1; + } + + aout_FiltersHintBuffers( p_aout, p_input->pp_resamplers, + p_input->i_nb_resamplers, + &p_input->input_alloc ); + p_input->input_alloc.i_alloc_type = AOUT_ALLOC_HEAP; + + /* Setup the initial rate of the resampler */ + p_input->pp_resamplers[0]->input.i_rate = p_input->input.i_rate; + } + p_input->i_resampling_type = AOUT_RESAMPLING_NONE; + + p_input->p_playback_rate_filter = NULL; + for( int i = 0; i < p_input->i_nb_filters; i++ ) + { + aout_filter_t *p_filter = p_input->pp_filters[i]; + if( strcmp( "scaletempo", p_filter->psz_object_name ) == 0 ) + { + p_input->p_playback_rate_filter = p_filter; + break; + } + } + if( ! p_input->p_playback_rate_filter && p_input->i_nb_resamplers > 0 ) + { + p_input->p_playback_rate_filter = p_input->pp_resamplers[0]; + } + + aout_FiltersHintBuffers( p_aout, p_input->pp_filters, + p_input->i_nb_filters, + &p_input->input_alloc ); + p_input->input_alloc.i_alloc_type = AOUT_ALLOC_HEAP; + + /* i_bytes_per_sec is still == -1 if no filters */ + p_input->input_alloc.i_bytes_per_sec = __MAX( + p_input->input_alloc.i_bytes_per_sec, + (int)(p_input->input.i_bytes_per_frame + * p_input->input.i_rate + / p_input->input.i_frame_length) ); + + ReplayGainSelect( p_aout, p_input ); + + /* Success */ + p_input->b_error = false; + p_input->b_restart = false; + p_input->i_last_input_rate = INPUT_RATE_DEFAULT; + + return 0; +} + +/***************************************************************************** + * aout_InputDelete : delete an input + ***************************************************************************** + * This function must be entered with the mixer lock. + *****************************************************************************/ +int aout_InputDelete( aout_instance_t * p_aout, aout_input_t * p_input ) +{ + AOUT_ASSERT_MIXER_LOCKED; + if ( p_input->b_error ) return 0; + + aout_FiltersDestroyPipeline( p_aout, p_input->pp_filters, + p_input->i_nb_filters ); + p_input->i_nb_filters = 0; + aout_FiltersDestroyPipeline( p_aout, p_input->pp_resamplers, + p_input->i_nb_resamplers ); + p_input->i_nb_resamplers = 0; + aout_FifoDestroy( p_aout, &p_input->fifo ); + + return 0; +} + +/***************************************************************************** + * aout_InputPlay : play a buffer + ***************************************************************************** + * This function must be entered with the input lock. + *****************************************************************************/ +/* XXX Do not activate it !! */ +//#define AOUT_PROCESS_BEFORE_CHEKS +int aout_InputPlay( aout_instance_t * p_aout, aout_input_t * p_input, + aout_buffer_t * p_buffer, int i_input_rate ) +{ + mtime_t start_date; + AOUT_ASSERT_INPUT_LOCKED; + + if( p_input->b_restart ) + { + aout_fifo_t fifo, dummy_fifo; + uint8_t *p_first_byte_to_mix; + + aout_lock_mixer( p_aout ); + aout_lock_input_fifos( p_aout ); + + /* A little trick to avoid loosing our input fifo */ + aout_FifoInit( p_aout, &dummy_fifo, p_aout->mixer.mixer.i_rate ); + p_first_byte_to_mix = p_input->p_first_byte_to_mix; + fifo = p_input->fifo; + p_input->fifo = dummy_fifo; + aout_InputDelete( p_aout, p_input ); + aout_InputNew( p_aout, p_input ); + p_input->p_first_byte_to_mix = p_first_byte_to_mix; + p_input->fifo = fifo; + + aout_unlock_input_fifos( p_aout ); + aout_unlock_mixer( p_aout ); + } + + if( i_input_rate != INPUT_RATE_DEFAULT && p_input->p_playback_rate_filter == NULL ) + { + inputDrop( p_aout, p_input, p_buffer ); + return 0; + } + +#ifdef AOUT_PROCESS_BEFORE_CHEKS + /* Run pre-filters. */ + aout_FiltersPlay( p_aout, p_input->pp_filters, p_input->i_nb_filters, + &p_buffer ); + + /* Actually run the resampler now. */ + if ( p_input->i_nb_resamplers > 0 ) + { + const mtime_t i_date = p_buffer->start_date; + aout_FiltersPlay( p_aout, p_input->pp_resamplers, + p_input->i_nb_resamplers, + &p_buffer ); + } + + if( p_buffer->i_nb_samples <= 0 ) + { + aout_BufferFree( p_buffer ); + return 0; + } +#endif + + /* Handle input rate change, but keep drift correction */ + if( i_input_rate != p_input->i_last_input_rate ) + { + unsigned int * const pi_rate = &p_input->p_playback_rate_filter->input.i_rate; +#define F(r,ir) ( INPUT_RATE_DEFAULT * (r) / (ir) ) + const int i_delta = *pi_rate - F(p_input->input.i_rate,p_input->i_last_input_rate); + *pi_rate = F(p_input->input.i_rate + i_delta, i_input_rate); +#undef F + p_input->i_last_input_rate = i_input_rate; + } + + /* We don't care if someone changes the start date behind our back after + * this. We'll deal with that when pushing the buffer, and compensate + * with the next incoming buffer. */ + aout_lock_input_fifos( p_aout ); + start_date = aout_FifoNextStart( p_aout, &p_input->fifo ); + aout_unlock_input_fifos( p_aout ); + + if ( start_date != 0 && start_date < mdate() ) + { + /* The decoder is _very_ late. This can only happen if the user + * pauses the stream (or if the decoder is buggy, which cannot + * happen :). */ + msg_Warn( p_aout, "computed PTS is out of range (%"PRId64"), " + "clearing out", mdate() - start_date ); + aout_lock_input_fifos( p_aout ); + aout_FifoSet( p_aout, &p_input->fifo, 0 ); + p_input->p_first_byte_to_mix = NULL; + aout_unlock_input_fifos( p_aout ); + if ( p_input->i_resampling_type != AOUT_RESAMPLING_NONE ) + msg_Warn( p_aout, "timing screwed, stopping resampling" ); + inputResamplingStop( p_input ); + start_date = 0; + } + + if ( p_buffer->start_date < mdate() + AOUT_MIN_PREPARE_TIME ) + { + /* The decoder gives us f*cked up PTS. It's its business, but we + * can't present it anyway, so drop the buffer. */ + msg_Warn( p_aout, "PTS is out of range (%"PRId64"), dropping buffer", + mdate() - p_buffer->start_date ); + + inputDrop( p_aout, p_input, p_buffer ); + inputResamplingStop( p_input ); + return 0; + } + + /* If the audio drift is too big then it's not worth trying to resample + * the audio. */ + mtime_t i_pts_tolerance = 3 * AOUT_PTS_TOLERANCE * i_input_rate / INPUT_RATE_DEFAULT; + if ( start_date != 0 && + ( start_date < p_buffer->start_date - i_pts_tolerance ) ) + { + msg_Warn( p_aout, "audio drift is too big (%"PRId64"), clearing out", + start_date - p_buffer->start_date ); + aout_lock_input_fifos( p_aout ); + aout_FifoSet( p_aout, &p_input->fifo, 0 ); + p_input->p_first_byte_to_mix = NULL; + aout_unlock_input_fifos( p_aout ); + if ( p_input->i_resampling_type != AOUT_RESAMPLING_NONE ) + msg_Warn( p_aout, "timing screwed, stopping resampling" ); + inputResamplingStop( p_input ); + start_date = 0; + } + else if ( start_date != 0 && + ( start_date > p_buffer->start_date + i_pts_tolerance) ) + { + msg_Warn( p_aout, "audio drift is too big (%"PRId64"), dropping buffer", + start_date - p_buffer->start_date ); + inputDrop( p_aout, p_input, p_buffer ); + return 0; + } + + if ( start_date == 0 ) start_date = p_buffer->start_date; + +#ifndef AOUT_PROCESS_BEFORE_CHEKS + /* Run pre-filters. */ + aout_FiltersPlay( p_aout, p_input->pp_filters, p_input->i_nb_filters, + &p_buffer ); +#endif + + /* Run the resampler if needed. + * We first need to calculate the output rate of this resampler. */ + if ( ( p_input->i_resampling_type == AOUT_RESAMPLING_NONE ) && + ( start_date < p_buffer->start_date - AOUT_PTS_TOLERANCE + || start_date > p_buffer->start_date + AOUT_PTS_TOLERANCE ) && + p_input->i_nb_resamplers > 0 ) + { + /* Can happen in several circumstances : + * 1. A problem at the input (clock drift) + * 2. A small pause triggered by the user + * 3. Some delay in the output stage, causing a loss of lip + * synchronization + * Solution : resample the buffer to avoid a scratch. + */ + mtime_t drift = p_buffer->start_date - start_date; + + p_input->i_resamp_start_date = mdate(); + p_input->i_resamp_start_drift = (int)drift; + + if ( drift > 0 ) + p_input->i_resampling_type = AOUT_RESAMPLING_DOWN; + else + p_input->i_resampling_type = AOUT_RESAMPLING_UP; + + msg_Warn( p_aout, "buffer is %"PRId64" %s, triggering %ssampling", + drift > 0 ? drift : -drift, + drift > 0 ? "in advance" : "late", + drift > 0 ? "down" : "up"); + } + + if ( p_input->i_resampling_type != AOUT_RESAMPLING_NONE ) + { + /* Resampling has been triggered previously (because of dates + * mismatch). We want the resampling to happen progressively so + * it isn't too audible to the listener. */ + + if( p_input->i_resampling_type == AOUT_RESAMPLING_UP ) + { + p_input->pp_resamplers[0]->input.i_rate += 2; /* Hz */ + } + else + { + p_input->pp_resamplers[0]->input.i_rate -= 2; /* Hz */ + } + + /* Check if everything is back to normal, in which case we can stop the + * resampling */ + unsigned int i_nominal_rate = + (p_input->pp_resamplers[0] == p_input->p_playback_rate_filter) + ? INPUT_RATE_DEFAULT * p_input->input.i_rate / i_input_rate + : p_input->input.i_rate; + if( p_input->pp_resamplers[0]->input.i_rate == i_nominal_rate ) + { + p_input->i_resampling_type = AOUT_RESAMPLING_NONE; + msg_Warn( p_aout, "resampling stopped after %"PRIi64" usec " + "(drift: %"PRIi64")", + mdate() - p_input->i_resamp_start_date, + p_buffer->start_date - start_date); + } + else if( abs( (int)(p_buffer->start_date - start_date) ) < + abs( p_input->i_resamp_start_drift ) / 2 ) + { + /* if we reduced the drift from half, then it is time to switch + * back the resampling direction. */ + if( p_input->i_resampling_type == AOUT_RESAMPLING_UP ) + p_input->i_resampling_type = AOUT_RESAMPLING_DOWN; + else + p_input->i_resampling_type = AOUT_RESAMPLING_UP; + p_input->i_resamp_start_drift = 0; + } + else if( p_input->i_resamp_start_drift && + ( abs( (int)(p_buffer->start_date - start_date) ) > + abs( p_input->i_resamp_start_drift ) * 3 / 2 ) ) + { + /* If the drift is increasing and not decreasing, than something + * is bad. We'd better stop the resampling right now. */ + msg_Warn( p_aout, "timing screwed, stopping resampling" ); + inputResamplingStop( p_input ); + } + } + +#ifndef AOUT_PROCESS_BEFORE_CHEKS + /* Actually run the resampler now. */ + if ( p_input->i_nb_resamplers > 0 ) + { + aout_FiltersPlay( p_aout, p_input->pp_resamplers, + p_input->i_nb_resamplers, + &p_buffer ); + } + + if( p_buffer->i_nb_samples <= 0 ) + { + aout_BufferFree( p_buffer ); + return 0; + } +#endif + + /* Adding the start date will be managed by aout_FifoPush(). */ + p_buffer->end_date = start_date + + (p_buffer->end_date - p_buffer->start_date); + p_buffer->start_date = start_date; + + aout_lock_input_fifos( p_aout ); + aout_FifoPush( p_aout, &p_input->fifo, p_buffer ); + aout_unlock_input_fifos( p_aout ); + return 0; +} + +/***************************************************************************** + * static functions + *****************************************************************************/ + +static void inputFailure( aout_instance_t * p_aout, aout_input_t * p_input, + const char * psz_error_message ) +{ + /* error message */ + msg_Err( p_aout, "%s", psz_error_message ); + + /* clean up */ + aout_FiltersDestroyPipeline( p_aout, p_input->pp_filters, + p_input->i_nb_filters ); + aout_FiltersDestroyPipeline( p_aout, p_input->pp_resamplers, + p_input->i_nb_resamplers ); + aout_FifoDestroy( p_aout, &p_input->fifo ); + var_Destroy( p_aout, "visual" ); + var_Destroy( p_aout, "equalizer" ); + var_Destroy( p_aout, "audio-filter" ); + var_Destroy( p_aout, "audio-visual" ); + + var_Destroy( p_aout, "audio-replay-gain-mode" ); + var_Destroy( p_aout, "audio-replay-gain-default" ); + var_Destroy( p_aout, "audio-replay-gain-preamp" ); + var_Destroy( p_aout, "audio-replay-gain-peak-protection" ); + + /* error flag */ + p_input->b_error = 1; +} + +static void inputDrop( aout_instance_t *p_aout, aout_input_t *p_input, aout_buffer_t *p_buffer ) +{ + aout_BufferFree( p_buffer ); + + if( !p_input->p_input_thread ) + return; + + vlc_mutex_lock( &p_input->p_input_thread->p->counters.counters_lock); + stats_UpdateInteger( p_aout, p_input->p_input_thread->p->counters.p_lost_abuffers, 1, NULL ); + vlc_mutex_unlock( &p_input->p_input_thread->p->counters.counters_lock); +} + +static void inputResamplingStop( aout_input_t *p_input ) +{ + p_input->i_resampling_type = AOUT_RESAMPLING_NONE; + if( p_input->i_nb_resamplers != 0 ) + { + p_input->pp_resamplers[0]->input.i_rate = + ( p_input->pp_resamplers[0] == p_input->p_playback_rate_filter ) + ? INPUT_RATE_DEFAULT * p_input->input.i_rate / p_input->i_last_input_rate + : p_input->input.i_rate; + p_input->pp_resamplers[0]->b_continuity = false; + } +} + +static int ChangeFiltersString( aout_instance_t * p_aout, const char* psz_variable, + const char *psz_name, bool b_add ) +{ + return AoutChangeFilterString( VLC_OBJECT(p_aout), p_aout, + psz_variable, psz_name, b_add ) ? 1 : 0; +} + +static int VisualizationCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) +{ + aout_instance_t *p_aout = (aout_instance_t *)p_this; + char *psz_mode = newval.psz_string; + vlc_value_t val; + (void)psz_cmd; (void)oldval; (void)p_data; + + if( !psz_mode || !*psz_mode ) + { + ChangeFiltersString( p_aout, "audio-visual", "goom", false ); + ChangeFiltersString( p_aout, "audio-visual", "visual", false ); + ChangeFiltersString( p_aout, "audio-visual", "galaktos", false ); + } + else + { + if( !strcmp( "goom", psz_mode ) ) + { + ChangeFiltersString( p_aout, "audio-visual", "visual", false ); + ChangeFiltersString( p_aout, "audio-visual", "goom", true ); + ChangeFiltersString( p_aout, "audio-visual", "galaktos", false); + } + else if( !strcmp( "galaktos", psz_mode ) ) + { + ChangeFiltersString( p_aout, "audio-visual", "visual", false ); + ChangeFiltersString( p_aout, "audio-visual", "goom", false ); + ChangeFiltersString( p_aout, "audio-visual", "galaktos", true ); + } + else + { + val.psz_string = psz_mode; + var_Create( p_aout, "effect-list", VLC_VAR_STRING ); + var_Set( p_aout, "effect-list", val ); + + ChangeFiltersString( p_aout, "audio-visual", "goom", false ); + ChangeFiltersString( p_aout, "audio-visual", "visual", true ); + ChangeFiltersString( p_aout, "audio-visual", "galaktos", false); + } + } + + /* That sucks */ + AoutInputsMarkToRestart( p_aout ); + + return VLC_SUCCESS; +} + +static int EqualizerCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) +{ + aout_instance_t *p_aout = (aout_instance_t *)p_this; + char *psz_mode = newval.psz_string; + vlc_value_t val; + int i_ret; + (void)psz_cmd; (void)oldval; (void)p_data; + + if( !psz_mode || !*psz_mode ) + { + i_ret = ChangeFiltersString( p_aout, "audio-filter", "equalizer", + false ); + } + else + { + val.psz_string = psz_mode; + var_Create( p_aout, "equalizer-preset", VLC_VAR_STRING ); + var_Set( p_aout, "equalizer-preset", val ); + i_ret = ChangeFiltersString( p_aout, "audio-filter", "equalizer", + true ); + + } + + /* That sucks */ + if( i_ret == 1 ) + AoutInputsMarkToRestart( p_aout ); + return VLC_SUCCESS; +} + +static int ReplayGainCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) +{ + VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); + VLC_UNUSED(newval); VLC_UNUSED(p_data); + aout_instance_t *p_aout = (aout_instance_t *)p_this; + int i; + + aout_lock_mixer( p_aout ); + for( i = 0; i < p_aout->i_nb_inputs; i++ ) + ReplayGainSelect( p_aout, p_aout->pp_inputs[i] ); + + /* Restart the mixer (a trivial mixer may be in use) */ + aout_MixerMultiplierSet( p_aout, p_aout->mixer.f_multiplier ); + aout_unlock_mixer( p_aout ); + + return VLC_SUCCESS; +} + +static void ReplayGainSelect( aout_instance_t *p_aout, aout_input_t *p_input ) +{ + char *psz_replay_gain = var_GetNonEmptyString( p_aout, + "audio-replay-gain-mode" ); + int i_mode; + int i_use; + float f_gain; + + p_input->f_multiplier = 1.0; + + if( !psz_replay_gain ) + return; + + /* Find select mode */ + if( !strcmp( psz_replay_gain, "track" ) ) + i_mode = AUDIO_REPLAY_GAIN_TRACK; + else if( !strcmp( psz_replay_gain, "album" ) ) + i_mode = AUDIO_REPLAY_GAIN_ALBUM; + else + i_mode = AUDIO_REPLAY_GAIN_MAX; + + /* If the select mode is not available, prefer the other one */ + i_use = i_mode; + if( i_use != AUDIO_REPLAY_GAIN_MAX && !p_input->replay_gain.pb_gain[i_use] ) + { + for( i_use = 0; i_use < AUDIO_REPLAY_GAIN_MAX; i_use++ ) + { + if( p_input->replay_gain.pb_gain[i_use] ) + break; + } + } + + /* */ + if( i_use != AUDIO_REPLAY_GAIN_MAX ) + f_gain = p_input->replay_gain.pf_gain[i_use] + var_GetFloat( p_aout, "audio-replay-gain-preamp" ); + else if( i_mode != AUDIO_REPLAY_GAIN_MAX ) + f_gain = var_GetFloat( p_aout, "audio-replay-gain-default" ); + else + f_gain = 0.0; + p_input->f_multiplier = pow( 10.0, f_gain / 20.0 ); + + /* */ + if( p_input->replay_gain.pb_peak[i_use] && + var_GetBool( p_aout, "audio-replay-gain-peak-protection" ) && + p_input->replay_gain.pf_peak[i_use] * p_input->f_multiplier > 1.0 ) + { + p_input->f_multiplier = 1.0f / p_input->replay_gain.pf_peak[i_use]; + } + + free( psz_replay_gain ); +} + diff --git a/VLC/audio_output/intf.c b/VLC/audio_output/intf.c new file mode 100644 index 0000000..9c96bea --- /dev/null +++ b/VLC/audio_output/intf.c @@ -0,0 +1,519 @@ +/***************************************************************************** + * intf.c : audio output API towards the interface modules + ***************************************************************************** + * Copyright (C) 2002-2007 the VideoLAN team + * $Id$ + * + * Authors: Christophe Massiot + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#include +#include /* calloc(), malloc(), free() */ +#include + +#include "vlc_aout.h" +#include "aout_internal.h" + +/* + * Volume management + * + * The hardware volume cannot be set if the output module gets deleted, so + * we must take the mixer lock. The software volume cannot be set while the + * mixer is running, so we need the mixer lock (too). + * + * Here is a schematic of the i_volume range : + * + * |------------------------------+---------------------------------------| + * 0 pi_soft 1024 + * + * Between 0 and pi_soft, the volume is done in hardware by the output + * module. Above, the output module will change p_aout->mixer.i_multiplier + * (done in software). This scaling may result * in cropping errors and + * should be avoided as much as possible. + * + * It is legal to have *pi_soft == 0, and do everything in software. + * It is also legal to have *pi_soft == 1024, and completely avoid + * software scaling. However, some streams (esp. A/52) are encoded with + * a very low volume and users may complain. + */ + +/***************************************************************************** + * aout_VolumeGet : get the volume of the output device + *****************************************************************************/ +int __aout_VolumeGet( vlc_object_t * p_object, audio_volume_t * pi_volume ) +{ + int i_result = 0; + aout_instance_t * p_aout = vlc_object_find( p_object, VLC_OBJECT_AOUT, + FIND_ANYWHERE ); + + if ( pi_volume == NULL ) return -1; + + if ( p_aout == NULL ) + { + *pi_volume = (audio_volume_t)config_GetInt( p_object, "volume" ); + return 0; + } + + aout_lock_mixer( p_aout ); + if ( !p_aout->mixer.b_error ) + { + i_result = p_aout->output.pf_volume_get( p_aout, pi_volume ); + } + else + { + *pi_volume = (audio_volume_t)config_GetInt( p_object, "volume" ); + } + aout_unlock_mixer( p_aout ); + + vlc_object_release( p_aout ); + return i_result; +} + +/***************************************************************************** + * aout_VolumeSet : set the volume of the output device + *****************************************************************************/ +int __aout_VolumeSet( vlc_object_t * p_object, audio_volume_t i_volume ) +{ + vlc_value_t val; + aout_instance_t *p_aout = vlc_object_find( p_object, VLC_OBJECT_AOUT, FIND_ANYWHERE ); + int i_result = 0; + + config_PutInt( p_object, "volume", i_volume ); + + val.b_bool = true; + var_Set( p_object->p_libvlc, "volume-change", val ); + + if ( p_aout == NULL ) return 0; + + aout_lock_mixer( p_aout ); + if ( !p_aout->mixer.b_error ) + { + i_result = p_aout->output.pf_volume_set( p_aout, i_volume ); + } + aout_unlock_mixer( p_aout ); + + var_Set( p_aout, "intf-change", val ); + vlc_object_release( p_aout ); + return i_result; +} + +/***************************************************************************** + * aout_VolumeInfos : get the boundary pi_soft + *****************************************************************************/ +int __aout_VolumeInfos( vlc_object_t * p_object, audio_volume_t * pi_soft ) +{ + aout_instance_t * p_aout = vlc_object_find( p_object, VLC_OBJECT_AOUT, + FIND_ANYWHERE ); + int i_result; + + if ( p_aout == NULL ) return 0; + + aout_lock_mixer( p_aout ); + if ( p_aout->mixer.b_error ) + { + /* The output module is destroyed. */ + i_result = -1; + } + else + { + i_result = p_aout->output.pf_volume_infos( p_aout, pi_soft ); + } + aout_unlock_mixer( p_aout ); + + vlc_object_release( p_aout ); + return i_result; +} + +/***************************************************************************** + * aout_VolumeUp : raise the output volume + ***************************************************************************** + * If pi_volume != NULL, *pi_volume will contain the volume at the end of the + * function. + *****************************************************************************/ +int __aout_VolumeUp( vlc_object_t * p_object, int i_nb_steps, + audio_volume_t * pi_volume ) +{ + vlc_value_t val; + aout_instance_t * p_aout = vlc_object_find( p_object, VLC_OBJECT_AOUT, + FIND_ANYWHERE ); + int i_result = 0, i_volume = 0, i_volume_step = 0; + + i_volume_step = config_GetInt( p_object->p_libvlc, "volume-step" ); + i_volume = config_GetInt( p_object, "volume" ); + i_volume += i_volume_step * i_nb_steps; + if ( i_volume > AOUT_VOLUME_MAX ) + { + i_volume = AOUT_VOLUME_MAX; + } + config_PutInt( p_object, "volume", i_volume ); + var_Create( p_object->p_libvlc, "saved-volume", VLC_VAR_INTEGER ); + var_SetInteger( p_object->p_libvlc, "saved-volume" , + (audio_volume_t) i_volume ); + if ( pi_volume != NULL ) *pi_volume = (audio_volume_t) i_volume; + + val.b_bool = true; + var_Set( p_object->p_libvlc, "volume-change", val ); + + if ( p_aout == NULL ) return 0; + + aout_lock_mixer( p_aout ); + if ( !p_aout->mixer.b_error ) + { + i_result = p_aout->output.pf_volume_set( p_aout, + (audio_volume_t) i_volume ); + } + aout_unlock_mixer( p_aout ); + + vlc_object_release( p_aout ); + return i_result; +} + +/***************************************************************************** + * aout_VolumeDown : lower the output volume + ***************************************************************************** + * If pi_volume != NULL, *pi_volume will contain the volume at the end of the + * function. + *****************************************************************************/ +int __aout_VolumeDown( vlc_object_t * p_object, int i_nb_steps, + audio_volume_t * pi_volume ) +{ + vlc_value_t val; + aout_instance_t * p_aout = vlc_object_find( p_object, VLC_OBJECT_AOUT, + FIND_ANYWHERE ); + int i_result = 0, i_volume = 0, i_volume_step = 0; + + i_volume_step = config_GetInt( p_object->p_libvlc, "volume-step" ); + i_volume = config_GetInt( p_object, "volume" ); + i_volume -= i_volume_step * i_nb_steps; + if ( i_volume < AOUT_VOLUME_MIN ) + { + i_volume = AOUT_VOLUME_MIN; + } + config_PutInt( p_object, "volume", i_volume ); + var_Create( p_object->p_libvlc, "saved-volume", VLC_VAR_INTEGER ); + var_SetInteger( p_object->p_libvlc, "saved-volume", (audio_volume_t) i_volume ); + if ( pi_volume != NULL ) *pi_volume = (audio_volume_t) i_volume; + + val.b_bool = true; + var_Set( p_object->p_libvlc, "volume-change", val ); + + if ( p_aout == NULL ) return 0; + + aout_lock_mixer( p_aout ); + if ( !p_aout->mixer.b_error ) + { + i_result = p_aout->output.pf_volume_set( p_aout, (audio_volume_t) i_volume ); + } + aout_unlock_mixer( p_aout ); + + vlc_object_release( p_aout ); + return i_result; +} + +/***************************************************************************** + * aout_VolumeMute : Mute/un-mute the output volume + ***************************************************************************** + * If pi_volume != NULL, *pi_volume will contain the volume at the end of the + * function (muted => 0). + *****************************************************************************/ +int __aout_VolumeMute( vlc_object_t * p_object, audio_volume_t * pi_volume ) +{ + int i_result; + audio_volume_t i_volume; + + i_volume = (audio_volume_t)config_GetInt( p_object, "volume" ); + if ( i_volume != 0 ) + { + /* Mute */ + i_result = aout_VolumeSet( p_object, AOUT_VOLUME_MIN ); + var_Create( p_object->p_libvlc, "saved-volume", VLC_VAR_INTEGER ); + var_SetInteger( p_object->p_libvlc, "saved-volume", (int)i_volume ); + if ( pi_volume != NULL ) *pi_volume = AOUT_VOLUME_MIN; + } + else + { + /* Un-mute */ + var_Create( p_object->p_libvlc, "saved-volume", VLC_VAR_INTEGER ); + i_volume = (audio_volume_t)var_GetInteger( p_object->p_libvlc, + "saved-volume" ); + i_result = aout_VolumeSet( p_object, i_volume ); + if ( pi_volume != NULL ) *pi_volume = i_volume; + } + + return i_result; +} + +/* + * The next functions are not supposed to be called by the interface, but + * are placeholders for software-only scaling. + */ + +/* Meant to be called by the output plug-in's Open(). */ +void aout_VolumeSoftInit( aout_instance_t * p_aout ) +{ + int i_volume; + + p_aout->output.pf_volume_infos = aout_VolumeSoftInfos; + p_aout->output.pf_volume_get = aout_VolumeSoftGet; + p_aout->output.pf_volume_set = aout_VolumeSoftSet; + + i_volume = config_GetInt( p_aout, "volume" ); + if ( i_volume < AOUT_VOLUME_MIN ) + { + i_volume = AOUT_VOLUME_DEFAULT; + } + else if ( i_volume > AOUT_VOLUME_MAX ) + { + i_volume = AOUT_VOLUME_MAX; + } + + aout_VolumeSoftSet( p_aout, (audio_volume_t)i_volume ); +} + +/* Placeholder for pf_volume_infos(). */ +int aout_VolumeSoftInfos( aout_instance_t * p_aout, audio_volume_t * pi_soft ) +{ + (void)p_aout; + *pi_soft = 0; + return 0; +} + +/* Placeholder for pf_volume_get(). */ +int aout_VolumeSoftGet( aout_instance_t * p_aout, audio_volume_t * pi_volume ) +{ + *pi_volume = p_aout->output.i_volume; + return 0; +} + + +/* Placeholder for pf_volume_set(). */ +int aout_VolumeSoftSet( aout_instance_t * p_aout, audio_volume_t i_volume ) +{ + aout_MixerMultiplierSet( p_aout, (float)i_volume / AOUT_VOLUME_DEFAULT ); + p_aout->output.i_volume = i_volume; + return 0; +} + +/* + * The next functions are not supposed to be called by the interface, but + * are placeholders for unsupported scaling. + */ + +/* Meant to be called by the output plug-in's Open(). */ +void aout_VolumeNoneInit( aout_instance_t * p_aout ) +{ + p_aout->output.pf_volume_infos = aout_VolumeNoneInfos; + p_aout->output.pf_volume_get = aout_VolumeNoneGet; + p_aout->output.pf_volume_set = aout_VolumeNoneSet; +} + +/* Placeholder for pf_volume_infos(). */ +int aout_VolumeNoneInfos( aout_instance_t * p_aout, audio_volume_t * pi_soft ) +{ + (void)p_aout; (void)pi_soft; + return -1; +} + +/* Placeholder for pf_volume_get(). */ +int aout_VolumeNoneGet( aout_instance_t * p_aout, audio_volume_t * pi_volume ) +{ + (void)p_aout; (void)pi_volume; + return -1; +} + +/* Placeholder for pf_volume_set(). */ +int aout_VolumeNoneSet( aout_instance_t * p_aout, audio_volume_t i_volume ) +{ + (void)p_aout; (void)i_volume; + return -1; +} + + +/* + * Pipelines management + */ + +/***************************************************************************** + * aout_Restart : re-open the output device and rebuild the input and output + * pipelines + ***************************************************************************** + * This function is used whenever the parameters of the output plug-in are + * changed (eg. selecting S/PDIF or PCM). + *****************************************************************************/ +static int aout_Restart( aout_instance_t * p_aout ) +{ + int i; + bool b_error = 0; + + aout_lock_mixer( p_aout ); + + if ( p_aout->i_nb_inputs == 0 ) + { + aout_unlock_mixer( p_aout ); + msg_Err( p_aout, "no decoder thread" ); + return -1; + } + + /* Lock all inputs. */ + aout_lock_input_fifos( p_aout ); + + for ( i = 0; i < p_aout->i_nb_inputs; i++ ) + { + aout_lock_input( p_aout, p_aout->pp_inputs[i] ); + aout_InputDelete( p_aout, p_aout->pp_inputs[i] ); + } + + aout_MixerDelete( p_aout ); + + /* Re-open the output plug-in. */ + aout_OutputDelete( p_aout ); + + if ( aout_OutputNew( p_aout, &p_aout->pp_inputs[0]->input ) == -1 ) + { + /* Release all locks and report the error. */ + for ( i = 0; i < p_aout->i_nb_inputs; i++ ) + { + vlc_mutex_unlock( &p_aout->pp_inputs[i]->lock ); + } + aout_unlock_input_fifos( p_aout ); + aout_unlock_mixer( p_aout ); + return -1; + } + + if ( aout_MixerNew( p_aout ) == -1 ) + { + aout_OutputDelete( p_aout ); + for ( i = 0; i < p_aout->i_nb_inputs; i++ ) + { + vlc_mutex_unlock( &p_aout->pp_inputs[i]->lock ); + } + aout_unlock_input_fifos( p_aout ); + aout_unlock_mixer( p_aout ); + return -1; + } + + /* Re-open all inputs. */ + for ( i = 0; i < p_aout->i_nb_inputs; i++ ) + { + aout_input_t * p_input = p_aout->pp_inputs[i]; + b_error |= aout_InputNew( p_aout, p_input ); + p_input->b_changed = 1; + aout_unlock_input( p_aout, p_input ); + } + + aout_unlock_input_fifos( p_aout ); + aout_unlock_mixer( p_aout ); + + return b_error; +} + +/***************************************************************************** + * aout_FindAndRestart : find the audio output instance and restart + ***************************************************************************** + * This is used for callbacks of the configuration variables, and we believe + * that when those are changed, it is a significant change which implies + * rebuilding the audio-device and audio-channels variables. + *****************************************************************************/ +int aout_FindAndRestart( vlc_object_t * p_this, const char *psz_name, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) +{ + aout_instance_t * p_aout = vlc_object_find( p_this, VLC_OBJECT_AOUT, + FIND_ANYWHERE ); + + (void)psz_name; (void)oldval; (void)newval; (void)p_data; + if ( p_aout == NULL ) return VLC_SUCCESS; + + if ( var_Type( p_aout, "audio-device" ) != 0 ) + { + var_Destroy( p_aout, "audio-device" ); + } + if ( var_Type( p_aout, "audio-channels" ) != 0 ) + { + var_Destroy( p_aout, "audio-channels" ); + } + + aout_Restart( p_aout ); + vlc_object_release( p_aout ); + + return VLC_SUCCESS; +} + +/***************************************************************************** + * aout_ChannelsRestart : change the audio device or channels and restart + *****************************************************************************/ +int aout_ChannelsRestart( vlc_object_t * p_this, const char * psz_variable, + vlc_value_t oldval, vlc_value_t newval, + void *p_data ) +{ + aout_instance_t * p_aout = (aout_instance_t *)p_this; + (void)oldval; (void)newval; (void)p_data; + + if ( !strcmp( psz_variable, "audio-device" ) ) + { + /* This is supposed to be a significant change and supposes + * rebuilding the channel choices. */ + if ( var_Type( p_aout, "audio-channels" ) >= 0 ) + { + var_Destroy( p_aout, "audio-channels" ); + } + } + aout_Restart( p_aout ); + return 0; +} + +/** Enable or disable an audio filter + * \param p_this a vlc object + * \param psz_name name of the filter + * \param b_add are we adding or removing the filter ? + */ +void aout_EnableFilter( vlc_object_t *p_this, const char *psz_name, + bool b_add ) +{ + aout_instance_t *p_aout = vlc_object_find( p_this, VLC_OBJECT_AOUT, + FIND_ANYWHERE ); + + if( AoutChangeFilterString( p_this, p_aout, "audio-filter", psz_name, b_add ) ) + { + if( p_aout ) + AoutInputsMarkToRestart( p_aout ); + } + + if( p_aout ) + vlc_object_release( p_aout ); +} + +/** + * Change audio visualization + * -1 goes backwards, +1 goes forward + */ +char *aout_VisualChange( vlc_object_t *p_this, int i_skip ) +{ + (void)p_this; (void)i_skip; + msg_Err( p_this, "FIXME: %s (%s %d) isn't implemented.", __func__, + __FILE__, __LINE__ ); + return strdup("foobar"); +} diff --git a/VLC/audio_output/mixer.c b/VLC/audio_output/mixer.c new file mode 100644 index 0000000..ab57555 --- /dev/null +++ b/VLC/audio_output/mixer.c @@ -0,0 +1,392 @@ +/***************************************************************************** + * mixer.c : audio output mixing operations + ***************************************************************************** + * Copyright (C) 2002-2004 the VideoLAN team + * $Id$ + * + * Authors: Christophe Massiot + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include "vlc_common.h" + +#ifdef HAVE_ALLOCA_H +# include +#endif +#include "vlc_aout.h" +#include "aout_internal.h" +/***************************************************************************** + * aout_MixerNew: prepare a mixer plug-in + ***************************************************************************** + * Please note that you must hold the mixer lock. + *****************************************************************************/ +int aout_MixerNew( aout_instance_t * p_aout ) +{ + p_aout->mixer.p_module = module_Need( p_aout, "audio mixer", NULL, 0 ); + if ( p_aout->mixer.p_module == NULL ) + { + msg_Err( p_aout, "no suitable audio mixer" ); + return -1; + } + p_aout->mixer.b_error = 0; + return 0; +} + +/***************************************************************************** + * aout_MixerDelete: delete the mixer + ***************************************************************************** + * Please note that you must hold the mixer lock. + *****************************************************************************/ +void aout_MixerDelete( aout_instance_t * p_aout ) +{ + if ( p_aout->mixer.b_error ) return; + module_Unneed( p_aout, p_aout->mixer.p_module ); + p_aout->mixer.b_error = 1; +} + +/***************************************************************************** + * MixBuffer: try to prepare one output buffer + ***************************************************************************** + * Please note that you must hold the mixer lock. + *****************************************************************************/ +static int MixBuffer( aout_instance_t * p_aout ) +{ + int i, i_first_input = 0; + aout_buffer_t * p_output_buffer; + mtime_t start_date, end_date; + audio_date_t exact_start_date; + + if ( p_aout->mixer.b_error ) + { + /* Free all incoming buffers. */ + aout_lock_input_fifos( p_aout ); + for ( i = 0; i < p_aout->i_nb_inputs; i++ ) + { + aout_input_t * p_input = p_aout->pp_inputs[i]; + aout_buffer_t * p_buffer = p_input->fifo.p_first; + if ( p_input->b_error ) continue; + while ( p_buffer != NULL ) + { + aout_buffer_t * p_next = p_buffer->p_next; + aout_BufferFree( p_buffer ); + p_buffer = p_next; + } + } + aout_unlock_input_fifos( p_aout ); + return -1; + } + + + aout_lock_output_fifo( p_aout ); + aout_lock_input_fifos( p_aout ); + + /* Retrieve the date of the next buffer. */ + memcpy( &exact_start_date, &p_aout->output.fifo.end_date, + sizeof(audio_date_t) ); + start_date = aout_DateGet( &exact_start_date ); + + if ( start_date != 0 && start_date < mdate() ) + { + /* The output is _very_ late. This can only happen if the user + * pauses the stream (or if the decoder is buggy, which cannot + * happen :). */ + msg_Warn( p_aout, "output PTS is out of range (%"PRId64"), clearing out", + mdate() - start_date ); + aout_FifoSet( p_aout, &p_aout->output.fifo, 0 ); + aout_DateSet( &exact_start_date, 0 ); + start_date = 0; + } + + aout_unlock_output_fifo( p_aout ); + + /* See if we have enough data to prepare a new buffer for the audio + * output. First : start date. */ + if ( !start_date ) + { + /* Find the latest start date available. */ + for ( i = 0; i < p_aout->i_nb_inputs; i++ ) + { + aout_input_t * p_input = p_aout->pp_inputs[i]; + aout_fifo_t * p_fifo = &p_input->fifo; + aout_buffer_t * p_buffer; + + if ( p_input->b_error ) continue; + + p_buffer = p_fifo->p_first; + while ( p_buffer != NULL && p_buffer->start_date < mdate() ) + { + msg_Warn( p_aout, "input PTS is out of range (%"PRId64"), " + "trashing", mdate() - p_buffer->start_date ); + p_buffer = aout_FifoPop( p_aout, p_fifo ); + aout_BufferFree( p_buffer ); + if( p_input->p_input_thread ) + { +// stats_UpdateInteger( p_input->p_input_thread, +// "lost_abuffers", 1 ); + } + p_buffer = p_fifo->p_first; + p_input->p_first_byte_to_mix = NULL; + } + + if ( p_buffer == NULL ) + { + break; + } + + if ( !start_date || start_date < p_buffer->start_date ) + { + aout_DateSet( &exact_start_date, p_buffer->start_date ); + start_date = p_buffer->start_date; + } + } + + if ( i < p_aout->i_nb_inputs ) + { + /* Interrupted before the end... We can't run. */ + aout_unlock_input_fifos( p_aout ); + return -1; + } + } + aout_DateIncrement( &exact_start_date, p_aout->output.i_nb_samples ); + end_date = aout_DateGet( &exact_start_date ); + + /* Check that start_date and end_date are available for all input + * streams. */ + for ( i = 0; i < p_aout->i_nb_inputs; i++ ) + { + aout_input_t * p_input = p_aout->pp_inputs[i]; + aout_fifo_t * p_fifo = &p_input->fifo; + aout_buffer_t * p_buffer; + mtime_t prev_date; + bool b_drop_buffers; + + if ( p_input->b_error ) + { + if ( i_first_input == i ) i_first_input++; + continue; + } + + p_buffer = p_fifo->p_first; + if ( p_buffer == NULL ) + { + break; + } + + /* Check for the continuity of start_date */ + while ( p_buffer != NULL && p_buffer->end_date < start_date - 1 ) + { + /* We authorize a +-1 because rounding errors get compensated + * regularly. */ + aout_buffer_t * p_next = p_buffer->p_next; + msg_Warn( p_aout, "the mixer got a packet in the past (%"PRId64")", + start_date - p_buffer->end_date ); + aout_BufferFree( p_buffer ); + if( p_input->p_input_thread ) + { +// stats_UpdateInteger( p_input->p_input_thread, +// "lost_abuffers", 1 ); + } + p_fifo->p_first = p_buffer = p_next; + p_input->p_first_byte_to_mix = NULL; + } + if ( p_buffer == NULL ) + { + p_fifo->pp_last = &p_fifo->p_first; + break; + } + + /* Check that we have enough samples. */ + for ( ; ; ) + { + p_buffer = p_fifo->p_first; + if ( p_buffer == NULL ) break; + if ( p_buffer->end_date >= end_date ) break; + + /* Check that all buffers are contiguous. */ + prev_date = p_fifo->p_first->end_date; + p_buffer = p_buffer->p_next; + b_drop_buffers = 0; + for ( ; p_buffer != NULL; p_buffer = p_buffer->p_next ) + { + if ( prev_date != p_buffer->start_date ) + { + msg_Warn( p_aout, + "buffer hole, dropping packets (%"PRId64")", + p_buffer->start_date - prev_date ); + b_drop_buffers = 1; + break; + } + if ( p_buffer->end_date >= end_date ) break; + prev_date = p_buffer->end_date; + } + if ( b_drop_buffers ) + { + aout_buffer_t * p_deleted = p_fifo->p_first; + while ( p_deleted != NULL && p_deleted != p_buffer ) + { + aout_buffer_t * p_next = p_deleted->p_next; + aout_BufferFree( p_deleted ); + p_deleted = p_next; + } + p_fifo->p_first = p_deleted; /* == p_buffer */ + } + else break; + } + if ( p_buffer == NULL ) break; + + p_buffer = p_fifo->p_first; + if ( !AOUT_FMT_NON_LINEAR( &p_aout->mixer.mixer ) ) + { + /* Additionally check that p_first_byte_to_mix is well + * located. */ + mtime_t i_nb_bytes = (start_date - p_buffer->start_date) + * p_aout->mixer.mixer.i_bytes_per_frame + * p_aout->mixer.mixer.i_rate + / p_aout->mixer.mixer.i_frame_length + / 1000000; + ptrdiff_t mixer_nb_bytes; + + if ( p_input->p_first_byte_to_mix == NULL ) + { + p_input->p_first_byte_to_mix = p_buffer->p_buffer; + } + mixer_nb_bytes = p_input->p_first_byte_to_mix - p_buffer->p_buffer; + + if ( !((i_nb_bytes + p_aout->mixer.mixer.i_bytes_per_frame + > mixer_nb_bytes) && + (i_nb_bytes < p_aout->mixer.mixer.i_bytes_per_frame + + mixer_nb_bytes)) ) + { + msg_Warn( p_aout, "mixer start isn't output start (%"PRId64")", + i_nb_bytes - mixer_nb_bytes ); + + /* Round to the nearest multiple */ + i_nb_bytes /= p_aout->mixer.mixer.i_bytes_per_frame; + i_nb_bytes *= p_aout->mixer.mixer.i_bytes_per_frame; + if( i_nb_bytes < 0 ) + { + /* Is it really the best way to do it ? */ + aout_lock_output_fifo( p_aout ); + aout_FifoSet( p_aout, &p_aout->output.fifo, 0 ); + aout_DateSet( &exact_start_date, 0 ); + aout_unlock_output_fifo( p_aout ); + break; + } + + p_input->p_first_byte_to_mix = p_buffer->p_buffer + i_nb_bytes; + } + } + } + + if ( i < p_aout->i_nb_inputs || i_first_input == p_aout->i_nb_inputs ) + { + /* Interrupted before the end... We can't run. */ + aout_unlock_input_fifos( p_aout ); + return -1; + } + + /* Run the mixer. */ + aout_BufferAlloc( &p_aout->mixer.output_alloc, + ((uint64_t)p_aout->output.i_nb_samples * 1000000) + / p_aout->output.output.i_rate, + /* This is a bit kludgy, but is actually only used + * for the S/PDIF dummy mixer : */ + p_aout->pp_inputs[i_first_input]->fifo.p_first, + p_output_buffer ); + if ( p_output_buffer == NULL ) + { + aout_unlock_input_fifos( p_aout ); + return -1; + } + /* This is again a bit kludgy - for the S/PDIF mixer. */ + if ( p_aout->mixer.output_alloc.i_alloc_type != AOUT_ALLOC_NONE ) + { + p_output_buffer->i_nb_samples = p_aout->output.i_nb_samples; + p_output_buffer->i_nb_bytes = p_aout->output.i_nb_samples + * p_aout->mixer.mixer.i_bytes_per_frame + / p_aout->mixer.mixer.i_frame_length; + } + p_output_buffer->start_date = start_date; + p_output_buffer->end_date = end_date; + + p_aout->mixer.pf_do_work( p_aout, p_output_buffer ); + + aout_unlock_input_fifos( p_aout ); + + aout_OutputPlay( p_aout, p_output_buffer ); + + return 0; +} + +/***************************************************************************** + * aout_MixerRun: entry point for the mixer & post-filters processing + ***************************************************************************** + * Please note that you must hold the mixer lock. + *****************************************************************************/ +void aout_MixerRun( aout_instance_t * p_aout ) +{ + while( MixBuffer( p_aout ) != -1 ); +} + +/***************************************************************************** + * aout_MixerMultiplierSet: set p_aout->mixer.f_multiplier + ***************************************************************************** + * Please note that we assume that you own the mixer lock when entering this + * function. This function returns -1 on error. + *****************************************************************************/ +int aout_MixerMultiplierSet( aout_instance_t * p_aout, float f_multiplier ) +{ + float f_old = p_aout->mixer.f_multiplier; + bool b_new_mixer = 0; + + if ( !p_aout->mixer.b_error ) + { + aout_MixerDelete( p_aout ); + b_new_mixer = 1; + } + + p_aout->mixer.f_multiplier = f_multiplier; + + if ( b_new_mixer && aout_MixerNew( p_aout ) ) + { + p_aout->mixer.f_multiplier = f_old; + aout_MixerNew( p_aout ); + return -1; + } + + return 0; +} + +/***************************************************************************** + * aout_MixerMultiplierGet: get p_aout->mixer.f_multiplier + ***************************************************************************** + * Please note that we assume that you own the mixer lock when entering this + * function. This function returns -1 on error. + *****************************************************************************/ +int aout_MixerMultiplierGet( aout_instance_t * p_aout, float * pf_multiplier ) +{ + *pf_multiplier = p_aout->mixer.f_multiplier; + return 0; +} + diff --git a/VLC/audio_output/output.c b/VLC/audio_output/output.c new file mode 100644 index 0000000..24ef41e --- /dev/null +++ b/VLC/audio_output/output.c @@ -0,0 +1,371 @@ +/***************************************************************************** + * output.c : internal management of output streams for the audio output + ***************************************************************************** + * Copyright (C) 2002-2004 the VideoLAN team + * $Id$ + * + * Authors: Christophe Massiot + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "vlc_aout.h" +#include "aout_internal.h" + +/***************************************************************************** + * aout_OutputNew : allocate a new output and rework the filter pipeline + ***************************************************************************** + * This function is entered with the mixer lock. + *****************************************************************************/ +int aout_OutputNew( aout_instance_t * p_aout, + audio_sample_format_t * p_format ) +{ + /* Retrieve user defaults. */ + int i_rate = config_GetInt( p_aout, "aout-rate" ); + vlc_value_t val, text; + /* kludge to avoid a fpu error when rate is 0... */ + if( i_rate == 0 ) i_rate = -1; + + memcpy( &p_aout->output.output, p_format, sizeof(audio_sample_format_t) ); + if ( i_rate != -1 ) + p_aout->output.output.i_rate = i_rate; + aout_FormatPrepare( &p_aout->output.output ); + + aout_lock_output_fifo( p_aout ); + + /* Find the best output plug-in. */ + p_aout->output.p_module = module_Need( p_aout, "audio output", "$aout", 0); + if ( p_aout->output.p_module == NULL ) + { + msg_Err( p_aout, "no suitable audio output module" ); + aout_unlock_output_fifo( p_aout ); + return -1; + } + + if ( var_Type( p_aout, "audio-channels" ) == + (VLC_VAR_INTEGER | VLC_VAR_HASCHOICE) ) + { + /* The user may have selected a different channels configuration. */ + var_Get( p_aout, "audio-channels", &val ); + + if ( val.i_int == AOUT_VAR_CHAN_RSTEREO ) + { + p_aout->output.output.i_original_channels |= + AOUT_CHAN_REVERSESTEREO; + } + else if ( val.i_int == AOUT_VAR_CHAN_STEREO ) + { + p_aout->output.output.i_original_channels = + AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT; + } + else if ( val.i_int == AOUT_VAR_CHAN_LEFT ) + { + p_aout->output.output.i_original_channels = AOUT_CHAN_LEFT; + } + else if ( val.i_int == AOUT_VAR_CHAN_RIGHT ) + { + p_aout->output.output.i_original_channels = AOUT_CHAN_RIGHT; + } + else if ( val.i_int == AOUT_VAR_CHAN_DOLBYS ) + { + p_aout->output.output.i_original_channels + = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_DOLBYSTEREO; + } + } + else if ( p_aout->output.output.i_physical_channels == AOUT_CHAN_CENTER + && (p_aout->output.output.i_original_channels + & AOUT_CHAN_PHYSMASK) == (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT) ) + { + /* Mono - create the audio-channels variable. */ + var_Create( p_aout, "audio-channels", + VLC_VAR_INTEGER | VLC_VAR_HASCHOICE ); + text.psz_string = _("Audio Channels"); + var_Change( p_aout, "audio-channels", VLC_VAR_SETTEXT, &text, NULL ); + + val.i_int = AOUT_VAR_CHAN_STEREO; text.psz_string = _("Stereo"); + var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text ); + val.i_int = AOUT_VAR_CHAN_LEFT; text.psz_string = _("Left"); + var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text ); + val.i_int = AOUT_VAR_CHAN_RIGHT; text.psz_string = _("Right"); + var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text ); + if ( p_aout->output.output.i_original_channels & AOUT_CHAN_DUALMONO ) + { + /* Go directly to the left channel. */ + p_aout->output.output.i_original_channels = AOUT_CHAN_LEFT; + val.i_int = AOUT_VAR_CHAN_LEFT; + var_Set( p_aout, "audio-channels", val ); + } + var_AddCallback( p_aout, "audio-channels", aout_ChannelsRestart, + NULL ); + } + else if ( p_aout->output.output.i_physical_channels == + (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT) + && (p_aout->output.output.i_original_channels & + (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT)) ) + { + /* Stereo - create the audio-channels variable. */ + var_Create( p_aout, "audio-channels", + VLC_VAR_INTEGER | VLC_VAR_HASCHOICE ); + text.psz_string = _("Audio Channels"); + var_Change( p_aout, "audio-channels", VLC_VAR_SETTEXT, &text, NULL ); + + if ( p_aout->output.output.i_original_channels & AOUT_CHAN_DOLBYSTEREO ) + { + val.i_int = AOUT_VAR_CHAN_DOLBYS; + text.psz_string = _("Dolby Surround"); + } + else + { + val.i_int = AOUT_VAR_CHAN_STEREO; + text.psz_string = _("Stereo"); + } + var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text ); + val.i_int = AOUT_VAR_CHAN_LEFT; text.psz_string = _("Left"); + var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text ); + val.i_int = AOUT_VAR_CHAN_RIGHT; text.psz_string = _("Right"); + var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text ); + val.i_int = AOUT_VAR_CHAN_RSTEREO; text.psz_string=_("Reverse stereo"); + var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text ); + if ( p_aout->output.output.i_original_channels & AOUT_CHAN_DUALMONO ) + { + /* Go directly to the left channel. */ + p_aout->output.output.i_original_channels = AOUT_CHAN_LEFT; + val.i_int = AOUT_VAR_CHAN_LEFT; + var_Set( p_aout, "audio-channels", val ); + } + var_AddCallback( p_aout, "audio-channels", aout_ChannelsRestart, + NULL ); + } + val.b_bool = true; + var_Set( p_aout, "intf-change", val ); + + aout_FormatPrepare( &p_aout->output.output ); + + /* Prepare FIFO. */ + aout_FifoInit( p_aout, &p_aout->output.fifo, + p_aout->output.output.i_rate ); + + aout_unlock_output_fifo( p_aout ); + + aout_FormatPrint( p_aout, "output", &p_aout->output.output ); + + /* Calculate the resulting mixer output format. */ + memcpy( &p_aout->mixer.mixer, &p_aout->output.output, + sizeof(audio_sample_format_t) ); + if ( !AOUT_FMT_NON_LINEAR(&p_aout->output.output) ) + { + /* Non-S/PDIF mixer only deals with float32 or fixed32. */ + p_aout->mixer.mixer.i_format + = (vlc_CPU() & CPU_CAPABILITY_FPU) ? + VLC_FOURCC('f','l','3','2') : + VLC_FOURCC('f','i','3','2'); + aout_FormatPrepare( &p_aout->mixer.mixer ); + } + else + { + p_aout->mixer.mixer.i_format = p_format->i_format; + } + + aout_FormatPrint( p_aout, "mixer", &p_aout->mixer.mixer ); + + /* Create filters. */ + p_aout->output.i_nb_filters = 0; + if ( aout_FiltersCreatePipeline( p_aout, p_aout->output.pp_filters, + &p_aout->output.i_nb_filters, + &p_aout->mixer.mixer, + &p_aout->output.output ) < 0 ) + { + msg_Err( p_aout, "couldn't create audio output pipeline" ); + module_Unneed( p_aout, p_aout->output.p_module ); + return -1; + } + + /* Prepare hints for the buffer allocator. */ + p_aout->mixer.output_alloc.i_alloc_type = AOUT_ALLOC_HEAP; + p_aout->mixer.output_alloc.i_bytes_per_sec + = p_aout->mixer.mixer.i_bytes_per_frame + * p_aout->mixer.mixer.i_rate + / p_aout->mixer.mixer.i_frame_length; + + aout_FiltersHintBuffers( p_aout, p_aout->output.pp_filters, + p_aout->output.i_nb_filters, + &p_aout->mixer.output_alloc ); + + p_aout->output.b_error = 0; + return 0; +} + +/***************************************************************************** + * aout_OutputDelete : delete the output + ***************************************************************************** + * This function is entered with the mixer lock. + *****************************************************************************/ +void aout_OutputDelete( aout_instance_t * p_aout ) +{ + if ( p_aout->output.b_error ) + { + return; + } + + module_Unneed( p_aout, p_aout->output.p_module ); + + aout_FiltersDestroyPipeline( p_aout, p_aout->output.pp_filters, + p_aout->output.i_nb_filters ); + + aout_lock_output_fifo( p_aout ); + aout_FifoDestroy( p_aout, &p_aout->output.fifo ); + aout_unlock_output_fifo( p_aout ); + + p_aout->output.b_error = true; +} + +/***************************************************************************** + * aout_OutputPlay : play a buffer + ***************************************************************************** + * This function is entered with the mixer lock. + *****************************************************************************/ +void aout_OutputPlay( aout_instance_t * p_aout, aout_buffer_t * p_buffer ) +{ + aout_FiltersPlay( p_aout, p_aout->output.pp_filters, + p_aout->output.i_nb_filters, + &p_buffer ); + + if( p_buffer->i_nb_bytes == 0 ) + { + aout_BufferFree( p_buffer ); + return; + } + + aout_lock_output_fifo( p_aout ); + aout_FifoPush( p_aout, &p_aout->output.fifo, p_buffer ); + p_aout->output.pf_play( p_aout ); + aout_unlock_output_fifo( p_aout ); +} + +/***************************************************************************** + * aout_OutputNextBuffer : give the audio output plug-in the right buffer + ***************************************************************************** + * If b_can_sleek is 1, the aout core functions won't try to resample + * new buffers to catch up - that is we suppose that the output plug-in can + * compensate it by itself. S/PDIF outputs should always set b_can_sleek = 1. + * This function is entered with no lock at all :-). + *****************************************************************************/ +aout_buffer_t * aout_OutputNextBuffer( aout_instance_t * p_aout, + mtime_t start_date, + bool b_can_sleek ) +{ + aout_buffer_t * p_buffer; + + aout_lock_output_fifo( p_aout ); + + p_buffer = p_aout->output.fifo.p_first; + + /* Drop the audio sample if the audio output is really late. + * In the case of b_can_sleek, we don't use a resampler so we need to be + * a lot more severe. */ + while ( p_buffer && p_buffer->start_date < + (b_can_sleek ? start_date : mdate()) - AOUT_PTS_TOLERANCE ) + { + msg_Dbg( p_aout, "audio output is too slow (%"PRId64"), " + "trashing %"PRId64"us", mdate() - p_buffer->start_date, + p_buffer->end_date - p_buffer->start_date ); + p_buffer = p_buffer->p_next; + aout_BufferFree( p_aout->output.fifo.p_first ); + p_aout->output.fifo.p_first = p_buffer; + } + + if ( p_buffer == NULL ) + { + p_aout->output.fifo.pp_last = &p_aout->output.fifo.p_first; + +#if 0 /* This is bad because the audio output might just be trying to fill + * in its internal buffers. And anyway, it's up to the audio output + * to deal with this kind of starvation. */ + + /* Set date to 0, to allow the mixer to send a new buffer ASAP */ + aout_FifoSet( p_aout, &p_aout->output.fifo, 0 ); + if ( !p_aout->output.b_starving ) + msg_Dbg( p_aout, + "audio output is starving (no input), playing silence" ); + p_aout->output.b_starving = 1; +#endif + + aout_unlock_output_fifo( p_aout ); + return NULL; + } + + /* Here we suppose that all buffers have the same duration - this is + * generally true, and anyway if it's wrong it won't be a disaster. + */ + if ( p_buffer->start_date > start_date + + (p_buffer->end_date - p_buffer->start_date) ) + /* + * + AOUT_PTS_TOLERANCE ) + * There is no reason to want that, it just worsen the scheduling of + * an audio sample after an output starvation (ie. on start or on resume) + * --Gibalou + */ + { + const mtime_t i_delta = p_buffer->start_date - start_date; + aout_unlock_output_fifo( p_aout ); + + if ( !p_aout->output.b_starving ) + msg_Dbg( p_aout, "audio output is starving (%"PRId64"), " + "playing silence", i_delta ); + p_aout->output.b_starving = 1; + return NULL; + } + + p_aout->output.b_starving = 0; + + if ( !b_can_sleek && + ( (p_buffer->start_date - start_date > AOUT_PTS_TOLERANCE) + || (start_date - p_buffer->start_date > AOUT_PTS_TOLERANCE) ) ) + { + /* Try to compensate the drift by doing some resampling. */ + int i; + mtime_t difference = start_date - p_buffer->start_date; + msg_Warn( p_aout, "output date isn't PTS date, requesting " + "resampling (%"PRId64")", difference ); + + aout_lock_input_fifos( p_aout ); + for ( i = 0; i < p_aout->i_nb_inputs; i++ ) + { + aout_fifo_t * p_fifo = &p_aout->pp_inputs[i]->fifo; + + aout_FifoMoveDates( p_aout, p_fifo, difference ); + } + + aout_FifoMoveDates( p_aout, &p_aout->output.fifo, difference ); + aout_unlock_input_fifos( p_aout ); + } + + p_aout->output.fifo.p_first = p_buffer->p_next; + if ( p_buffer->p_next == NULL ) + { + p_aout->output.fifo.pp_last = &p_aout->output.fifo.p_first; + } + + aout_unlock_output_fifo( p_aout ); + return p_buffer; +} diff --git a/VLC/block.c b/VLC/block.c new file mode 100644 index 0000000..902f365 --- /dev/null +++ b/VLC/block.c @@ -0,0 +1,495 @@ +/***************************************************************************** + * block.c: Data blocks management functions + ***************************************************************************** + * Copyright (C) 2003-2004 the VideoLAN team + * $Id$ + * + * Authors: Laurent Aimar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include +#include "vlc_block.h" + +/***************************************************************************** + * Block functions. + *****************************************************************************/ +/* private */ +struct block_sys_t +{ + block_t self; + size_t i_allocated_buffer; + uint8_t p_allocated_buffer[0]; +}; + +#ifndef NDEBUG +static void BlockNoRelease( block_t *b ) +{ + fprintf( stderr, "block %p has no release callback! This is a bug!\n", b ); + abort(); +} +#endif + +void block_Init( block_t *restrict b, void *buf, size_t size ) +{ + /* Fill all fields to their default */ + b->p_next = b->p_prev = NULL; + b->i_flags = 0; + b->i_pts = b->i_dts = b->i_length = 0; + b->i_rate = 0; + b->p_buffer = buf; + b->i_buffer = size; +#ifndef NDEBUG + b->pf_release = BlockNoRelease; +#endif +} + +static void BlockRelease( block_t *p_block ) +{ + free( p_block ); +} + +/* Memory alignment */ +#define BLOCK_ALIGN 16 +/* Initial size of reserved header and footer */ +#define BLOCK_PADDING_SIZE 32 +/* Maximum size of reserved footer before we release with realloc() */ +#define BLOCK_WASTE_SIZE 2048 + +block_t *block_Alloc( size_t i_size ) +{ + /* We do only one malloc + * TODO: bench if doing 2 malloc but keeping a pool of buffer is better + * TODO: use memalign + * 16 -> align on 16 + * 2 * BLOCK_PADDING_SIZE -> pre + post padding + */ + const size_t i_alloc = i_size + 2 * BLOCK_PADDING_SIZE + BLOCK_ALIGN; + block_sys_t *p_sys = malloc( sizeof( *p_sys ) + i_alloc ); + + if( p_sys == NULL ) + return NULL; + + /* Fill opaque data */ + p_sys->i_allocated_buffer = i_alloc; + + block_Init( &p_sys->self, p_sys->p_allocated_buffer + BLOCK_PADDING_SIZE + + BLOCK_ALIGN + - ((uintptr_t)p_sys->p_allocated_buffer % BLOCK_ALIGN), + i_size ); + p_sys->self.pf_release = BlockRelease; + + return &p_sys->self; +} + +block_t *block_Realloc( block_t *p_block, ssize_t i_prebody, size_t i_body ) +{ + block_sys_t *p_sys = (block_sys_t *)p_block; + ssize_t i_buffer_size = i_prebody + i_body; + + if( i_buffer_size <= 0 ) + { + block_Release( p_block ); + return NULL; + } + + if( p_block->pf_release != BlockRelease ) + { + /* Special case when pf_release if overloaded + * TODO if used one day, then implement it in a smarter way */ + block_t *p_dup = block_Duplicate( p_block ); + block_Release( p_block ); + if( !p_dup ) + return NULL; + + p_block = p_dup; + } + + /* Adjust reserved header if there is enough room */ + if( p_block->p_buffer - i_prebody > p_sys->p_allocated_buffer && + p_block->p_buffer - i_prebody < p_sys->p_allocated_buffer + + p_sys->i_allocated_buffer ) + { + p_block->p_buffer -= i_prebody; + p_block->i_buffer += i_prebody; + i_prebody = 0; + } + + /* Adjust payload size if there is enough room */ + if( p_block->p_buffer + i_body < p_sys->p_allocated_buffer + + p_sys->i_allocated_buffer ) + { + p_block->i_buffer = i_buffer_size; + i_body = 0; + } + + /* Not enough room, reallocate the buffer */ + if( i_body > 0 || i_prebody > 0 ) + { + /* FIXME: this is really dumb, we should use realloc() */ + block_t *p_rea = block_New( NULL, i_buffer_size ); + + if( p_rea ) + { + p_rea->i_dts = p_block->i_dts; + p_rea->i_pts = p_block->i_pts; + p_rea->i_flags = p_block->i_flags; + p_rea->i_length = p_block->i_length; + p_rea->i_rate = p_block->i_rate; + p_rea->i_samples = p_block->i_samples; + + memcpy( p_rea->p_buffer + i_prebody, p_block->p_buffer, + __MIN( p_block->i_buffer, p_rea->i_buffer - i_prebody ) ); + } + + block_Release( p_block ); + + return p_rea; + } + + /* We have a very large reserved footer now? Release some of it. */ + if ((p_sys->p_allocated_buffer + p_sys->i_allocated_buffer) - + (p_block->p_buffer + p_block->i_buffer) > BLOCK_WASTE_SIZE) + { + const size_t news = p_block->i_buffer + 2 * BLOCK_PADDING_SIZE + 16; + block_sys_t *newb = realloc (p_sys, sizeof (*p_sys) + news); + + if (newb != NULL) + { + p_sys = newb; + p_sys->i_allocated_buffer = news; + p_block = &p_sys->self; + p_block->p_buffer = p_sys->p_allocated_buffer + BLOCK_PADDING_SIZE + + BLOCK_ALIGN + - ((uintptr_t)p_sys->p_allocated_buffer % BLOCK_ALIGN); + } + } + + return p_block; +} + +#ifdef HAVE_MMAP +# include + +typedef struct block_mmap_t +{ + block_t self; + void *base_addr; + size_t length; +} block_mmap_t; + +static void block_mmap_Release (block_t *block) +{ + block_mmap_t *p_sys = (block_mmap_t *)block; + + munmap (p_sys->base_addr, p_sys->length); + free (p_sys); +} + +/** + * Creates a block from a virtual address memory mapping (mmap). + * This is provided by LibVLC so that mmap blocks can safely be deallocated + * even after the allocating plugin has been unloaded from memory. + * + * @param addr base address of the mapping (as returned by mmap) + * @param length length (bytes) of the mapping (as passed to mmap) + * @return NULL if addr is MAP_FAILED, or an error occurred (in the later + * case, munmap(addr, length) is invoked before returning). + */ +block_t *block_mmap_Alloc (void *addr, size_t length) +{ + if (addr == MAP_FAILED) + return NULL; + + block_mmap_t *block = malloc (sizeof (*block)); + if (block == NULL) + { + munmap (addr, length); + return NULL; + } + + block_Init (&block->self, (uint8_t *)addr, length); + block->self.pf_release = block_mmap_Release; + block->base_addr = addr; + block->length = length; + return &block->self; +} +#else +block_t *block_mmap_Alloc (void *addr, size_t length) +{ + (void)addr; (void)length; return NULL; +} +#endif + + +#ifdef WIN32 +static +ssize_t pread (int fd, void *buf, size_t count, off_t offset) +{ + HANDLE handle = (HANDLE)(intptr_t)_get_osfhandle (fd); + if (handle == INVALID_HANDLE_VALUE) + return -1; + + OVERLAPPED olap = { .Offset = offset, .OffsetHigh = (offset >> 32), }; + DWORD written; + /* This braindead API will override the file pointer even if we specify + * an explicit read offset... So do not expect this to mix well with + * regular read() calls. */ + if (ReadFile (handle, buf, count, &written, &olap)) + return written; + return -1; +} +#endif + +/** + * Loads a file into a block of memory. If possible a private file mapping is + * created. Otherwise, the file is read normally. On 32-bits platforms, this + * function will not work for very large files, due to memory space + * constraints. + * + * @param fd file descriptor to load from + * @return a new block with the file content at p_buffer, and file length at + * i_buffer (release it with block_Release()), or NULL upon error (see errno). + */ +block_t *block_File (int fd) +{ + size_t length; + struct stat st; + + /* First, get the file size */ + if (fstat (fd, &st)) + return NULL; + + /* st_size is meaningful for regular files, shared memory and typed memory. + * It's also meaning for symlinks, but that's not possible with fstat(). + * In other cases, it's undefined, and we should really not go further. */ +#ifndef S_TYPEISSHM +# define S_TYPEISSHM( buf ) (0) +#endif + if (S_ISDIR (st.st_mode)) + { + errno = EISDIR; + return NULL; + } + if (!S_ISREG (st.st_mode) && !S_TYPEISSHM (&st)) + { + errno = ESPIPE; + return NULL; + } + + /* Prevent an integer overflow in mmap() and malloc() */ + if (st.st_size >= SIZE_MAX) + { + errno = ENOMEM; + return NULL; + } + length = (size_t)st.st_size; + +#ifdef HAVE_MMAP + if (length > 0) + { + void *addr; + + addr = mmap (NULL, length, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); + if (addr != MAP_FAILED) + return block_mmap_Alloc (addr, length); + } +#endif + + /* If mmap() is not implemented by the OS _or_ the filesystem... */ + block_t *block = block_Alloc (length); + if (block == NULL) + return NULL; + + for (size_t i = 0; i < length;) + { + ssize_t len = pread (fd, block->p_buffer + i, length - i, i); + if (len == -1) + { + block_Release (block); + return NULL; + } + i += len; + } + return block; +} + +/***************************************************************************** + * block_fifo_t management + *****************************************************************************/ +struct block_fifo_t +{ + vlc_mutex_t lock; /* fifo data lock */ + vlc_cond_t wait; /* fifo data conditional variable */ + + block_t *p_first; + block_t **pp_last; + size_t i_depth; + size_t i_size; + bool b_force_wake; +}; + +block_fifo_t *block_FifoNew( void ) +{ + block_fifo_t *p_fifo = malloc( sizeof( block_fifo_t ) ); + if( !p_fifo ) + return NULL; + + vlc_mutex_init( &p_fifo->lock ); + vlc_cond_init( NULL, &p_fifo->wait ); + p_fifo->p_first = NULL; + p_fifo->pp_last = &p_fifo->p_first; + p_fifo->i_depth = p_fifo->i_size = 0; + p_fifo->b_force_wake = false; + + return p_fifo; +} + +void block_FifoRelease( block_fifo_t *p_fifo ) +{ + block_FifoEmpty( p_fifo ); + vlc_cond_destroy( &p_fifo->wait ); + vlc_mutex_destroy( &p_fifo->lock ); + free( p_fifo ); +} + +void block_FifoEmpty( block_fifo_t *p_fifo ) +{ + block_t *b; + + vlc_mutex_lock( &p_fifo->lock ); + for( b = p_fifo->p_first; b != NULL; ) + { + block_t *p_next; + + p_next = b->p_next; + block_Release( b ); + b = p_next; + } + + p_fifo->i_depth = p_fifo->i_size = 0; + p_fifo->p_first = NULL; + p_fifo->pp_last = &p_fifo->p_first; + vlc_mutex_unlock( &p_fifo->lock ); +} + +size_t block_FifoPut( block_fifo_t *p_fifo, block_t *p_block ) +{ + size_t i_size = 0; + vlc_mutex_lock( &p_fifo->lock ); + + do + { + i_size += p_block->i_buffer; + + *p_fifo->pp_last = p_block; + p_fifo->pp_last = &p_block->p_next; + p_fifo->i_depth++; + p_fifo->i_size += p_block->i_buffer; + + p_block = p_block->p_next; + + } while( p_block ); + + /* warn there is data in this fifo */ + vlc_cond_signal( &p_fifo->wait ); + vlc_mutex_unlock( &p_fifo->lock ); + + return i_size; +} + +void block_FifoWake( block_fifo_t *p_fifo ) +{ + vlc_mutex_lock( &p_fifo->lock ); + if( p_fifo->p_first == NULL ) + p_fifo->b_force_wake = true; + vlc_cond_signal( &p_fifo->wait ); + vlc_mutex_unlock( &p_fifo->lock ); +} + +block_t *block_FifoGet( block_fifo_t *p_fifo ) +{ + block_t *b; + + vlc_mutex_lock( &p_fifo->lock ); + + /* Remember vlc_cond_wait() may cause spurious wakeups + * (on both Win32 and POSIX) */ + while( ( p_fifo->p_first == NULL ) && !p_fifo->b_force_wake ) + { + vlc_cond_wait( &p_fifo->wait, &p_fifo->lock ); + } + + b = p_fifo->p_first; + + p_fifo->b_force_wake = false; + if( b == NULL ) + { + /* Forced wakeup */ + vlc_mutex_unlock( &p_fifo->lock ); + return NULL; + } + + p_fifo->p_first = b->p_next; + p_fifo->i_depth--; + p_fifo->i_size -= b->i_buffer; + + if( p_fifo->p_first == NULL ) + { + p_fifo->pp_last = &p_fifo->p_first; + } + + vlc_mutex_unlock( &p_fifo->lock ); + + b->p_next = NULL; + return b; +} + +block_t *block_FifoShow( block_fifo_t *p_fifo ) +{ + block_t *b; + + vlc_mutex_lock( &p_fifo->lock ); + + if( p_fifo->p_first == NULL ) + { + vlc_cond_wait( &p_fifo->wait, &p_fifo->lock ); + } + + b = p_fifo->p_first; + + vlc_mutex_unlock( &p_fifo->lock ); + + return( b ); +} + +size_t block_FifoSize( const block_fifo_t *p_fifo ) +{ + return p_fifo->i_size; +} + +size_t block_FifoCount( const block_fifo_t *p_fifo ) +{ + return p_fifo->i_depth; +} diff --git a/VLC/builtin.h b/VLC/builtin.h new file mode 100644 index 0000000..333837c --- /dev/null +++ b/VLC/builtin.h @@ -0,0 +1,29 @@ +/***************************************************************************** + * modules_builtin.h: built-in modules list + ***************************************************************************** + * Copyright (C) 2001 the VideoLAN team + * + * Authors: Samuel Hocevar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#define ALLOCATE_BUILTIN( NAME ) \ + AllocateBuiltinModule( p_this, vlc_entry__ ## NAME ); + +/* We also consider the main program as a module (useful for config stuff) */ +int vlc_entry__main( module_t* ); + +/* Add stuff here */ diff --git a/VLC/cache.c b/VLC/cache.c new file mode 100644 index 0000000..73486e6 --- /dev/null +++ b/VLC/cache.c @@ -0,0 +1,743 @@ +/***************************************************************************** + * cache.c: Plugins cache + ***************************************************************************** + * Copyright (C) 2001-2007 the VideoLAN team + * $Id$ + * + * Authors: Sam Hocevar + * Ethan C. Baldridge + * Hans-Peter Jansen + * Gildas Bazin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "libvlc.h" + +#include /* free(), strtol() */ +#include /* sprintf() */ +#include /* strdup() */ +#include "vlc_plugin.h" + +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif + +#if !defined(HAVE_DYNAMIC_PLUGINS) + /* no support for plugins */ +#elif defined(HAVE_DL_DYLD) +# if defined(HAVE_MACH_O_DYLD_H) +# include +# endif +#elif defined(HAVE_DL_BEOS) +# if defined(HAVE_IMAGE_H) +# include +# endif +#elif defined(HAVE_DL_WINDOWS) +# include +#elif defined(HAVE_DL_DLOPEN) +# if defined(HAVE_DLFCN_H) /* Linux, BSD, Hurd */ +# include +# endif +# if defined(HAVE_SYS_DL_H) +# include +# endif +#elif defined(HAVE_DL_SHL_LOAD) +# if defined(HAVE_DL_H) +# include +# endif +#endif + +#include "configuration.h" + +#include "vlc_charset.h" + +#include "modules.h" + + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +#ifdef HAVE_DYNAMIC_PLUGINS +static int CacheLoadConfig ( module_t *, FILE * ); +static int CacheSaveConfig ( module_t *, FILE * ); +static char * CacheName ( void ); + +/* Sub-version number + * (only used to avoid breakage in dev version when cache structure changes) */ +#define CACHE_SUBVERSION_NUM 3 + +/***************************************************************************** + * LoadPluginsCache: loads the plugins cache file + ***************************************************************************** + * This function will load the plugin cache if present and valid. This cache + * will in turn be queried by AllocateAllPlugins() to see if it needs to + * actually load the dynamically loadable module. + * This allows us to only fully load plugins when they are actually used. + *****************************************************************************/ +void CacheLoad( vlc_object_t *p_this ) +{ + char *psz_filename, *psz_cachedir = config_GetCacheDir(); + FILE *file; + int i, j, i_size, i_read; + char p_cachestring[sizeof("cache " COPYRIGHT_MESSAGE)]; + char p_cachelang[6], p_lang[6]; + int i_cache; + module_cache_t **pp_cache = 0; + int32_t i_file_size, i_marker; + libvlc_global_data_t *p_libvlc_global = vlc_global(); + + if( !psz_cachedir ) /* XXX: this should never happen */ + { + msg_Err( p_this, "Unable to get cache directory" ); + return; + } + + if( asprintf( &psz_filename, "%s"DIR_SEP"%s", + psz_cachedir, CacheName() ) == -1 ) + { + free( psz_cachedir ); + return; + } + free( psz_cachedir ); + + if( p_libvlc_global->p_module_bank->b_cache_delete ) + { +#if !defined( UNDER_CE ) + unlink( psz_filename ); +#else + wchar_t psz_wf[MAX_PATH]; + MultiByteToWideChar( CP_ACP, 0, psz_filename, -1, psz_wf, MAX_PATH ); + DeleteFile( psz_wf ); +#endif + msg_Dbg( p_this, "removing plugins cache file %s", psz_filename ); + free( psz_filename ); + return; + } + + msg_Dbg( p_this, "loading plugins cache file %s", psz_filename ); + + file = utf8_fopen( psz_filename, "rb" ); + if( !file ) + { + msg_Warn( p_this, "could not open plugins cache file %s for reading", + psz_filename ); + free( psz_filename ); + return; + } + free( psz_filename ); + + /* Check the file size */ + i_read = fread( &i_file_size, 1, sizeof(i_file_size), file ); + if( i_read != sizeof(i_file_size) ) + { + msg_Warn( p_this, "This doesn't look like a valid plugins cache " + "(too short)" ); + fclose( file ); + return; + } + + fseek( file, 0, SEEK_END ); + if( ftell( file ) != i_file_size ) + { + msg_Warn( p_this, "This doesn't look like a valid plugins cache " + "(corrupted size)" ); + fclose( file ); + return; + } + fseek( file, sizeof(i_file_size), SEEK_SET ); + + /* Check the file is a plugins cache */ + i_size = sizeof("cache " COPYRIGHT_MESSAGE) - 1; + i_read = fread( p_cachestring, 1, i_size, file ); + if( i_read != i_size || + memcmp( p_cachestring, "cache " COPYRIGHT_MESSAGE, i_size ) ) + { + msg_Warn( p_this, "This doesn't look like a valid plugins cache" ); + fclose( file ); + return; + } + +#ifdef DISTRO_VERSION + /* Check for distribution specific version */ + char p_distrostring[sizeof( DISTRO_VERSION )]; + i_size = sizeof( DISTRO_VERSION ) - 1; + i_read = fread( p_distrostring, 1, i_size, file ); + if( i_read != i_size || + memcmp( p_distrostring, DISTRO_VERSION, i_size ) ) + { + msg_Warn( p_this, "This doesn't look like a valid plugins cache" ); + fclose( file ); + return; + } +#endif + + /* Check Sub-version number */ + i_read = fread( &i_marker, 1, sizeof(i_marker), file ); + if( i_read != sizeof(i_marker) || i_marker != CACHE_SUBVERSION_NUM ) + { + msg_Warn( p_this, "This doesn't look like a valid plugins cache " + "(corrupted header)" ); + fclose( file ); + return; + } + + /* Check the language hasn't changed */ + sprintf( p_lang, "%5.5s", _("C") ); i_size = 5; + i_read = fread( p_cachelang, 1, i_size, file ); + if( i_read != i_size || memcmp( p_cachelang, p_lang, i_size ) ) + { + msg_Warn( p_this, "This doesn't look like a valid plugins cache " + "(language changed)" ); + fclose( file ); + return; + } + + /* Check header marker */ + i_read = fread( &i_marker, 1, sizeof(i_marker), file ); + if( i_read != sizeof(i_marker) || + i_marker != ftell( file ) - (int)sizeof(i_marker) ) + { + msg_Warn( p_this, "This doesn't look like a valid plugins cache " + "(corrupted header)" ); + fclose( file ); + return; + } + + p_libvlc_global->p_module_bank->i_loaded_cache = 0; + if (fread( &i_cache, 1, sizeof(i_cache), file ) != sizeof(i_cache) ) + { + msg_Warn( p_this, "This doesn't look like a valid plugins cache " + "(file too short)" ); + fclose( file ); + return; + } + + if( i_cache ) + pp_cache = p_libvlc_global->p_module_bank->pp_loaded_cache = + malloc( i_cache * sizeof(void *) ); + +#define LOAD_IMMEDIATE(a) \ + if( fread( (void *)&a, sizeof(char), sizeof(a), file ) != sizeof(a) ) goto error +#define LOAD_STRING(a) \ +{ \ + a = NULL; \ + if( ( fread( &i_size, sizeof(i_size), 1, file ) != 1 ) \ + || ( i_size > 16384 ) ) \ + goto error; \ + if( i_size ) { \ + char *psz = malloc( i_size ); \ + if( fread( psz, i_size, 1, file ) != 1 ) { \ + free( psz ); \ + goto error; \ + } \ + if( psz[i_size-1] ) { \ + free( psz ); \ + goto error; \ + } \ + a = psz; \ + } \ +} + + for( i = 0; i < i_cache; i++ ) + { + uint16_t i_size; + int i_submodules; + + pp_cache[i] = malloc( sizeof(module_cache_t) ); + p_libvlc_global->p_module_bank->i_loaded_cache++; + + /* Load common info */ + LOAD_STRING( pp_cache[i]->psz_file ); + LOAD_IMMEDIATE( pp_cache[i]->i_time ); + LOAD_IMMEDIATE( pp_cache[i]->i_size ); + LOAD_IMMEDIATE( pp_cache[i]->b_junk ); + pp_cache[i]->b_used = false; + + if( pp_cache[i]->b_junk ) continue; + + pp_cache[i]->p_module = vlc_module_create( p_this ); + + /* Load additional infos */ + free( pp_cache[i]->p_module->psz_object_name ); + LOAD_STRING( pp_cache[i]->p_module->psz_object_name ); + LOAD_STRING( pp_cache[i]->p_module->psz_shortname ); + LOAD_STRING( pp_cache[i]->p_module->psz_longname ); + LOAD_STRING( pp_cache[i]->p_module->psz_help ); + for( j = 0; j < MODULE_SHORTCUT_MAX; j++ ) + { + LOAD_STRING( pp_cache[i]->p_module->pp_shortcuts[j] ); // FIX + } + LOAD_STRING( pp_cache[i]->p_module->psz_capability ); + LOAD_IMMEDIATE( pp_cache[i]->p_module->i_score ); + LOAD_IMMEDIATE( pp_cache[i]->p_module->i_cpu ); + LOAD_IMMEDIATE( pp_cache[i]->p_module->b_unloadable ); + LOAD_IMMEDIATE( pp_cache[i]->p_module->b_reentrant ); + LOAD_IMMEDIATE( pp_cache[i]->p_module->b_submodule ); + + /* Config stuff */ + if( CacheLoadConfig( pp_cache[i]->p_module, file ) != VLC_SUCCESS ) + goto error; + + LOAD_STRING( pp_cache[i]->p_module->psz_filename ); + + LOAD_IMMEDIATE( i_submodules ); + + while( i_submodules-- ) + { + module_t *p_module = vlc_submodule_create( pp_cache[i]->p_module ); + free( p_module->psz_object_name ); + LOAD_STRING( p_module->psz_object_name ); + LOAD_STRING( p_module->psz_shortname ); + LOAD_STRING( p_module->psz_longname ); + LOAD_STRING( p_module->psz_help ); + for( j = 0; j < MODULE_SHORTCUT_MAX; j++ ) + { + LOAD_STRING( p_module->pp_shortcuts[j] ); // FIX + } + LOAD_STRING( p_module->psz_capability ); + LOAD_IMMEDIATE( p_module->i_score ); + LOAD_IMMEDIATE( p_module->i_cpu ); + LOAD_IMMEDIATE( p_module->b_unloadable ); + LOAD_IMMEDIATE( p_module->b_reentrant ); + } + } + + fclose( file ); + return; + + error: + + msg_Warn( p_this, "plugins cache not loaded (corrupted)" ); + + /* TODO: cleanup */ + p_libvlc_global->p_module_bank->i_loaded_cache = 0; + + fclose( file ); + return; +} + + +static int CacheLoadConfig( module_t *p_module, FILE *file ) +{ + uint32_t i_lines; + uint16_t i_size; + + /* Calculate the structure length */ + LOAD_IMMEDIATE( p_module->i_config_items ); + LOAD_IMMEDIATE( p_module->i_bool_items ); + + LOAD_IMMEDIATE( i_lines ); + + /* Allocate memory */ + if (i_lines) + { + p_module->p_config = + (module_config_t *)calloc( i_lines, sizeof(module_config_t) ); + if( p_module->p_config == NULL ) + { + p_module->confsize = 0; + msg_Err( p_module, "config error: can't duplicate p_config" ); + return VLC_ENOMEM; + } + } + p_module->confsize = i_lines; + + /* Do the duplication job */ + for (size_t i = 0; i < i_lines; i++ ) + { + LOAD_IMMEDIATE( p_module->p_config[i] ); + + LOAD_STRING( p_module->p_config[i].psz_type ); + LOAD_STRING( p_module->p_config[i].psz_name ); + LOAD_STRING( p_module->p_config[i].psz_text ); + LOAD_STRING( p_module->p_config[i].psz_longtext ); + LOAD_STRING( p_module->p_config[i].psz_oldname ); + LOAD_IMMEDIATE( p_module->p_config[i].b_removed ); + + if (IsConfigStringType (p_module->p_config[i].i_type)) + { + LOAD_STRING (p_module->p_config[i].orig.psz); + p_module->p_config[i].value.psz = + (p_module->p_config[i].orig.psz != NULL) + ? strdup (p_module->p_config[i].orig.psz) : NULL; + p_module->p_config[i].saved.psz = NULL; + } + else + { + memcpy (&p_module->p_config[i].value, &p_module->p_config[i].orig, + sizeof (p_module->p_config[i].value)); + memcpy (&p_module->p_config[i].saved, &p_module->p_config[i].orig, + sizeof (p_module->p_config[i].saved)); + } + + p_module->p_config[i].b_dirty = false; + + p_module->p_config[i].p_lock = &(vlc_internals(p_module)->lock); + + if( p_module->p_config[i].i_list ) + { + if( p_module->p_config[i].ppsz_list ) + { + int j; + p_module->p_config[i].ppsz_list = + malloc( (p_module->p_config[i].i_list+1) * sizeof(char *)); + if( p_module->p_config[i].ppsz_list ) + { + for( j = 0; j < p_module->p_config[i].i_list; j++ ) + LOAD_STRING( p_module->p_config[i].ppsz_list[j] ); + p_module->p_config[i].ppsz_list[j] = NULL; + } + } + if( p_module->p_config[i].ppsz_list_text ) + { + int j; + p_module->p_config[i].ppsz_list_text = + malloc( (p_module->p_config[i].i_list+1) * sizeof(char *)); + if( p_module->p_config[i].ppsz_list_text ) + { + for( j = 0; j < p_module->p_config[i].i_list; j++ ) + LOAD_STRING( p_module->p_config[i].ppsz_list_text[j] ); + p_module->p_config[i].ppsz_list_text[j] = NULL; + } + } + if( p_module->p_config[i].pi_list ) + { + p_module->p_config[i].pi_list = + malloc( (p_module->p_config[i].i_list + 1) * sizeof(int) ); + if( p_module->p_config[i].pi_list ) + { + for (int j = 0; j < p_module->p_config[i].i_list; j++) + LOAD_IMMEDIATE( p_module->p_config[i].pi_list[j] ); + } + } + } + + if( p_module->p_config[i].i_action ) + { + p_module->p_config[i].ppf_action = + malloc( p_module->p_config[i].i_action * sizeof(void *) ); + p_module->p_config[i].ppsz_action_text = + malloc( p_module->p_config[i].i_action * sizeof(char *) ); + + for (int j = 0; j < p_module->p_config[i].i_action; j++) + { + p_module->p_config[i].ppf_action[j] = 0; + LOAD_STRING( p_module->p_config[i].ppsz_action_text[j] ); + } + } + + LOAD_IMMEDIATE( p_module->p_config[i].pf_callback ); + } + + return VLC_SUCCESS; + + error: + + return VLC_EGENERIC; +} + +/***************************************************************************** + * SavePluginsCache: saves the plugins cache to a file + *****************************************************************************/ +void CacheSave( vlc_object_t *p_this ) +{ + static char const psz_tag[] = + "Signature: 8a477f597d28d172789f06886806bc55\r\n" + "# This file is a cache directory tag created by VLC.\r\n" + "# For information about cache directory tags, see:\r\n" + "# http://www.brynosaurus.com/cachedir/\r\n"; + + char *psz_cachedir = config_GetCacheDir(); + FILE *file; + int i, j, i_cache; + module_cache_t **pp_cache; + uint32_t i_file_size = 0; + libvlc_global_data_t *p_libvlc_global = vlc_global(); + + if( !psz_cachedir ) /* XXX: this should never happen */ + { + msg_Err( p_this, "unable to get cache directory" ); + return; + } + + char psz_filename[sizeof(DIR_SEP) + 32 + strlen(psz_cachedir)]; + config_CreateDir( p_this, psz_cachedir ); + + snprintf( psz_filename, sizeof( psz_filename ), + "%s"DIR_SEP"CACHEDIR.TAG", psz_cachedir ); + file = utf8_fopen( psz_filename, "wb" ); + if (file != NULL) + { + if (fwrite (psz_tag, 1, sizeof (psz_tag) - 1, file) != 1) + clearerr (file); /* what else can we do? */ + fclose( file ); + } + + snprintf( psz_filename, sizeof( psz_filename ), + "%s"DIR_SEP"%s", psz_cachedir, CacheName() ); + free( psz_cachedir ); + msg_Dbg( p_this, "writing plugins cache %s", psz_filename ); + + file = utf8_fopen( psz_filename, "wb" ); + if (file == NULL) + goto error; + + /* Empty space for file size */ + if (fwrite (&i_file_size, sizeof (i_file_size), 1, file) != 1) + goto error; + + /* Contains version number */ + if (fputs ("cache "COPYRIGHT_MESSAGE, file) == EOF) + goto error; +#ifdef DISTRO_VERSION + /* Allow binary maintaner to pass a string to detect new binary version*/ + if (fputs( DISTRO_VERSION, file ) == EOF) + goto error; +#endif + /* Sub-version number (to avoid breakage in the dev version when cache + * structure changes) */ + i_file_size = CACHE_SUBVERSION_NUM; + if (fwrite (&i_file_size, sizeof (i_file_size), 1, file) != 1 ) + goto error; + + /* Language */ + if (fprintf (file, "%5.5s", _("C")) == EOF) + goto error; + + /* Header marker */ + i_file_size = ftell( file ); + if (fwrite (&i_file_size, sizeof (i_file_size), 1, file) != 1) + goto error; + + i_cache = p_libvlc_global->p_module_bank->i_cache; + pp_cache = p_libvlc_global->p_module_bank->pp_cache; + + if (fwrite( &i_cache, sizeof (i_cache), 1, file) != 1) + goto error; + +#define SAVE_IMMEDIATE( a ) \ + if (fwrite (&a, sizeof(a), 1, file) != 1) \ + goto error +#define SAVE_STRING( a ) \ + { \ + uint16_t i_size = (a != NULL) ? (strlen (a) + 1) : 0; \ + if ((fwrite (&i_size, sizeof (i_size), 1, file) != 1) \ + || (a && (fwrite (a, 1, i_size, file) != i_size))) \ + goto error; \ + } while(0) + + for( i = 0; i < i_cache; i++ ) + { + uint32_t i_submodule; + + /* Save common info */ + SAVE_STRING( pp_cache[i]->psz_file ); + SAVE_IMMEDIATE( pp_cache[i]->i_time ); + SAVE_IMMEDIATE( pp_cache[i]->i_size ); + SAVE_IMMEDIATE( pp_cache[i]->b_junk ); + + if( pp_cache[i]->b_junk ) continue; + + /* Save additional infos */ + SAVE_STRING( pp_cache[i]->p_module->psz_object_name ); + SAVE_STRING( pp_cache[i]->p_module->psz_shortname ); + SAVE_STRING( pp_cache[i]->p_module->psz_longname ); + SAVE_STRING( pp_cache[i]->p_module->psz_help ); + for( j = 0; j < MODULE_SHORTCUT_MAX; j++ ) + { + SAVE_STRING( pp_cache[i]->p_module->pp_shortcuts[j] ); // FIX + } + SAVE_STRING( pp_cache[i]->p_module->psz_capability ); + SAVE_IMMEDIATE( pp_cache[i]->p_module->i_score ); + SAVE_IMMEDIATE( pp_cache[i]->p_module->i_cpu ); + SAVE_IMMEDIATE( pp_cache[i]->p_module->b_unloadable ); + SAVE_IMMEDIATE( pp_cache[i]->p_module->b_reentrant ); + SAVE_IMMEDIATE( pp_cache[i]->p_module->b_submodule ); + + /* Config stuff */ + if (CacheSaveConfig (pp_cache[i]->p_module, file)) + goto error; + + SAVE_STRING( pp_cache[i]->p_module->psz_filename ); + + i_submodule = vlc_internals( pp_cache[i]->p_module )->i_children; + SAVE_IMMEDIATE( i_submodule ); + for( i_submodule = 0; + i_submodule < (unsigned)vlc_internals( pp_cache[i]->p_module)->i_children; + i_submodule++ ) + { + module_t *p_module = + (module_t *)vlc_internals( pp_cache[i]->p_module )->pp_children[i_submodule]; + + SAVE_STRING( p_module->psz_object_name ); + SAVE_STRING( p_module->psz_shortname ); + SAVE_STRING( p_module->psz_longname ); + SAVE_STRING( p_module->psz_help ); + for( j = 0; j < MODULE_SHORTCUT_MAX; j++ ) + { + SAVE_STRING( p_module->pp_shortcuts[j] ); // FIX + } + SAVE_STRING( p_module->psz_capability ); + SAVE_IMMEDIATE( p_module->i_score ); + SAVE_IMMEDIATE( p_module->i_cpu ); + SAVE_IMMEDIATE( p_module->b_unloadable ); + SAVE_IMMEDIATE( p_module->b_reentrant ); + } + } + + /* Fill-up file size */ + i_file_size = ftell( file ); + fseek( file, 0, SEEK_SET ); + if (fwrite (&i_file_size, sizeof (i_file_size), 1, file) != 1) + goto error; + + if (fclose (file) == 0) + return; /* success! */ + + file = NULL; +error: + msg_Warn (p_this, "could not write plugins cache %s (%m)", + psz_filename); + if (file != NULL) + { + clearerr (file); + fclose (file); + } +} + +static int CacheSaveConfig( module_t *p_module, FILE *file ) +{ + uint32_t i_lines = p_module->confsize; + + SAVE_IMMEDIATE( p_module->i_config_items ); + SAVE_IMMEDIATE( p_module->i_bool_items ); + SAVE_IMMEDIATE( i_lines ); + + for (size_t i = 0; i < i_lines ; i++) + { + SAVE_IMMEDIATE( p_module->p_config[i] ); + + SAVE_STRING( p_module->p_config[i].psz_type ); + SAVE_STRING( p_module->p_config[i].psz_name ); + SAVE_STRING( p_module->p_config[i].psz_text ); + SAVE_STRING( p_module->p_config[i].psz_longtext ); + SAVE_STRING( p_module->p_config[i].psz_oldname ); + SAVE_IMMEDIATE( p_module->p_config[i].b_removed ); + + if (IsConfigStringType (p_module->p_config[i].i_type)) + SAVE_STRING( p_module->p_config[i].orig.psz ); + + if( p_module->p_config[i].i_list ) + { + if( p_module->p_config[i].ppsz_list ) + { + for (int j = 0; j < p_module->p_config[i].i_list; j++) + SAVE_STRING( p_module->p_config[i].ppsz_list[j] ); + } + + if( p_module->p_config[i].ppsz_list_text ) + { + for (int j = 0; j < p_module->p_config[i].i_list; j++) + SAVE_STRING( p_module->p_config[i].ppsz_list_text[j] ); + } + if( p_module->p_config[i].pi_list ) + { + for (int j = 0; j < p_module->p_config[i].i_list; j++) + SAVE_IMMEDIATE( p_module->p_config[i].pi_list[j] ); + } + } + + for (int j = 0; j < p_module->p_config[i].i_action; j++) + SAVE_STRING( p_module->p_config[i].ppsz_action_text[j] ); + + SAVE_IMMEDIATE( p_module->p_config[i].pf_callback ); + } + return 0; + +error: + return -1; +} + +/***************************************************************************** + * CacheName: Return the cache file name for this platform. + *****************************************************************************/ +static char *CacheName( void ) +{ + static char psz_cachename[32]; + + /* Code int size, pointer size and endianness in the filename */ + int32_t x = 0xbe00001e; + sprintf( psz_cachename, "plugins-%.2x%.2x%.2x.dat", (int)sizeof(int), + (int)sizeof(void *), (unsigned int)((unsigned char *)&x)[0] ); + return psz_cachename; +} + +/***************************************************************************** + * CacheMerge: Merge a cache module descriptor with a full module descriptor. + *****************************************************************************/ +void CacheMerge( vlc_object_t *p_this, module_t *p_cache, module_t *p_module ) +{ + int i_submodule; + (void)p_this; + + p_cache->pf_activate = p_module->pf_activate; + p_cache->pf_deactivate = p_module->pf_deactivate; + p_cache->handle = p_module->handle; + + for( i_submodule = 0; i_submodule < vlc_internals( p_module )->i_children; i_submodule++ ) + { + module_t *p_child = (module_t*)vlc_internals( p_module )->pp_children[i_submodule]; + module_t *p_cchild = (module_t*)vlc_internals( p_cache )->pp_children[i_submodule]; + p_cchild->pf_activate = p_child->pf_activate; + p_cchild->pf_deactivate = p_child->pf_deactivate; + } + + p_cache->b_loaded = true; + p_module->b_loaded = false; +} + +/***************************************************************************** + * CacheFind: finds the cache entry corresponding to a file + *****************************************************************************/ +module_cache_t *CacheFind( const char *psz_file, + int64_t i_time, int64_t i_size ) +{ + module_cache_t **pp_cache; + int i_cache, i; + libvlc_global_data_t *p_libvlc_global = vlc_global(); + + pp_cache = p_libvlc_global->p_module_bank->pp_loaded_cache; + i_cache = p_libvlc_global->p_module_bank->i_loaded_cache; + + for( i = 0; i < i_cache; i++ ) + { + if( !strcmp( pp_cache[i]->psz_file, psz_file ) && + pp_cache[i]->i_time == i_time && + pp_cache[i]->i_size == i_size ) return pp_cache[i]; + } + + return NULL; +} + +#endif /* HAVE_DYNAMIC_PLUGINS */ diff --git a/VLC/chain.c b/VLC/chain.c new file mode 100644 index 0000000..5f075ec --- /dev/null +++ b/VLC/chain.c @@ -0,0 +1,386 @@ +/***************************************************************************** + * chain.c : configuration module chain parsing stuff + ***************************************************************************** + * Copyright (C) 2002-2007 the VideoLAN team + * $Id$ + * + * Authors: Christophe Massiot + * Laurent Aimar + * Eric Petit + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "libvlc.h" + +#include "vlc_interface.h" + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ + +/* chain format: + module{option=*:option=*}[:module{option=*:...}] + */ +#define SKIPSPACE( p ) { while( *p && ( *p == ' ' || *p == '\t' ) ) p++; } +#define SKIPTRAILINGSPACE( p, e ) \ + { while( e > p && ( *(e-1) == ' ' || *(e-1) == '\t' ) ) e--; } + +/* go accross " " and { } */ +static const char *_get_chain_end( const char *str ) +{ + char c; + const char *p = str; + + SKIPSPACE( p ); + + for( ;; ) + { + if( !*p || *p == ',' || *p == '}' ) return p; + + if( *p != '{' && *p != '"' && *p != '\'' ) + { + p++; + continue; + } + + if( *p == '{' ) c = '}'; + else c = *p; + p++; + + for( ;; ) + { + if( !*p ) return p; + + if( *p == c ) return ++p; + else if( *p == '{' && c == '}' ) p = _get_chain_end( p ); + else p++; + } + } +} + +char *config_ChainCreate( char **ppsz_name, config_chain_t **pp_cfg, const char *psz_chain ) +{ + config_chain_t *p_cfg = NULL; + const char *p = psz_chain; + + *ppsz_name = NULL; + *pp_cfg = NULL; + + if( !p ) return NULL; + + SKIPSPACE( p ); + + while( *p && *p != '{' && *p != ':' && *p != ' ' && *p != '\t' ) p++; + + if( p == psz_chain ) return NULL; + + *ppsz_name = strndup( psz_chain, p - psz_chain ); + + SKIPSPACE( p ); + + if( *p == '{' ) + { + const char *psz_name; + + p++; + + for( ;; ) + { + config_chain_t cfg; + + SKIPSPACE( p ); + + psz_name = p; + + while( *p && *p != '=' && *p != ',' && *p != '{' && *p != '}' && + *p != ' ' && *p != '\t' ) p++; + + /* fprintf( stderr, "name=%s - rest=%s\n", psz_name, p ); */ + if( p == psz_name ) + { + fprintf( stderr, "config_ChainCreate: invalid options (empty) \n" ); + break; + } + + cfg.psz_name = strndup( psz_name, p - psz_name ); + + SKIPSPACE( p ); + + if( *p == '=' || *p == '{' ) + { + const char *end; + bool b_keep_brackets = (*p == '{'); + + if( *p == '=' ) p++; + + end = _get_chain_end( p ); + if( end <= p ) + { + cfg.psz_value = NULL; + } + else + { + /* Skip heading and trailing spaces. + * This ain't necessary but will avoid simple + * user mistakes. */ + SKIPSPACE( p ); + } + + if( end <= p ) + { + cfg.psz_value = NULL; + } + else + { + if( *p == '\'' || *p == '"' || + ( !b_keep_brackets && *p == '{' ) ) + { + p++; + + if( *(end-1) != '\'' && *(end-1) == '"' ) + SKIPTRAILINGSPACE( p, end ); + + if( end - 1 <= p ) cfg.psz_value = NULL; + else cfg.psz_value = strndup( p, end -1 - p ); + } + else + { + SKIPTRAILINGSPACE( p, end ); + if( end <= p ) cfg.psz_value = NULL; + else cfg.psz_value = strndup( p, end - p ); + } + } + + p = end; + SKIPSPACE( p ); + } + else + { + cfg.psz_value = NULL; + } + + cfg.p_next = NULL; + if( p_cfg ) + { + p_cfg->p_next = malloc( sizeof( config_chain_t ) ); + memcpy( p_cfg->p_next, &cfg, sizeof( config_chain_t ) ); + + p_cfg = p_cfg->p_next; + } + else + { + p_cfg = malloc( sizeof( config_chain_t ) ); + memcpy( p_cfg, &cfg, sizeof( config_chain_t ) ); + + *pp_cfg = p_cfg; + } + + if( *p == ',' ) p++; + + if( *p == '}' ) + { + p++; + break; + } + } + } + + if( *p == ':' ) return( strdup( p + 1 ) ); + + return NULL; +} + +void config_ChainDestroy( config_chain_t *p_cfg ) +{ + while( p_cfg != NULL ) + { + config_chain_t *p_next; + + p_next = p_cfg->p_next; + + FREENULL( p_cfg->psz_name ); + FREENULL( p_cfg->psz_value ); + free( p_cfg ); + + p_cfg = p_next; + } +} + +void __config_ChainParse( vlc_object_t *p_this, const char *psz_prefix, + const char *const *ppsz_options, config_chain_t *cfg ) +{ + if( psz_prefix == NULL ) psz_prefix = ""; + size_t plen = 1 + strlen( psz_prefix ); + + /* First, var_Create all variables */ + for( size_t i = 0; ppsz_options[i] != NULL; i++ ) + { + const char *optname = ppsz_options[i]; + if (optname[0] == '*') + optname++; + + char name[plen + strlen( optname )]; + snprintf( name, sizeof (name), "%s%s", psz_prefix, optname ); + if( var_Create( p_this, name, + config_GetType( p_this, name ) | VLC_VAR_DOINHERIT ) ) + return /* VLC_xxx */; + } + + /* Now parse options and set value */ + for(; cfg; cfg = cfg->p_next ) + { + vlc_value_t val; + bool b_yes = true; + bool b_once = false; + module_config_t *p_conf; + int i_type; + size_t i; + + if( cfg->psz_name == NULL || *cfg->psz_name == '\0' ) + continue; + + for( i = 0; ppsz_options[i] != NULL; i++ ) + { + if( !strcmp( ppsz_options[i], cfg->psz_name ) ) + { + break; + } + if( ( !strncmp( cfg->psz_name, "no-", 3 ) && + !strcmp( ppsz_options[i], cfg->psz_name + 3 ) ) || + ( !strncmp( cfg->psz_name, "no", 2 ) && + !strcmp( ppsz_options[i], cfg->psz_name + 2 ) ) ) + { + b_yes = false; + break; + } + + if( *ppsz_options[i] == '*' && + !strcmp( &ppsz_options[i][1], cfg->psz_name ) ) + { + b_once = true; + break; + } + + } + + if( ppsz_options[i] == NULL ) + { + msg_Warn( p_this, "option %s is unknown", cfg->psz_name ); + continue; + } + + /* create name */ + char name[plen + strlen( ppsz_options[i] )]; + const char *psz_name = name; + snprintf( name, sizeof (name), "%s%s", psz_prefix, + b_once ? (ppsz_options[i] + 1) : ppsz_options[i] ); + + /* Check if the option is deprecated */ + p_conf = config_FindConfig( p_this, name ); + + /* This is basically cut and paste from src/misc/configuration.c + * with slight changes */ + if( p_conf ) + { + if( p_conf->b_removed ) + { + msg_Err( p_this, "Option %s is not supported anymore.", + name ); + /* TODO: this should return an error and end option parsing + * ... but doing this would change the VLC API and all the + * modules so i'll do it later */ + continue; + } + if( p_conf->psz_oldname + && !strcmp( p_conf->psz_oldname, name ) ) + { + psz_name = p_conf->psz_name; + msg_Warn( p_this, "Option %s is obsolete. Use %s instead.", + name, psz_name ); + } + } + /* */ + + /* get the type of the variable */ + i_type = config_GetType( p_this, psz_name ); + if( !i_type ) + { + msg_Warn( p_this, "unknown option %s (value=%s)", + cfg->psz_name, cfg->psz_value ); + continue; + } + + i_type &= CONFIG_ITEM; + + if( i_type != VLC_VAR_BOOL && cfg->psz_value == NULL ) + { + msg_Warn( p_this, "missing value for option %s", cfg->psz_name ); + continue; + } + if( i_type != VLC_VAR_STRING && b_once ) + { + msg_Warn( p_this, "*option_name need to be a string option" ); + continue; + } + + switch( i_type ) + { + case VLC_VAR_BOOL: + val.b_bool = b_yes; + break; + case VLC_VAR_INTEGER: + val.i_int = strtol( cfg->psz_value ? cfg->psz_value : "0", + NULL, 0 ); + break; + case VLC_VAR_FLOAT: + val.f_float = atof( cfg->psz_value ? cfg->psz_value : "0" ); + break; + case VLC_VAR_STRING: + case VLC_VAR_MODULE: + val.psz_string = cfg->psz_value; + break; + default: + msg_Warn( p_this, "unhandled config var type (%d)", i_type ); + memset( &val, 0, sizeof( vlc_value_t ) ); + break; + } + if( b_once ) + { + vlc_value_t val2; + + var_Get( p_this, psz_name, &val2 ); + if( *val2.psz_string ) + { + free( val2.psz_string ); + msg_Dbg( p_this, "ignoring option %s (not first occurrence)", psz_name ); + continue; + } + free( val2.psz_string ); + } + var_Set( p_this, psz_name, val ); + msg_Dbg( p_this, "set config option: %s to %s", psz_name, + cfg->psz_value ? cfg->psz_value : "(null)" ); + } +} diff --git a/VLC/charset.c b/VLC/charset.c new file mode 100644 index 0000000..7087c48 --- /dev/null +++ b/VLC/charset.c @@ -0,0 +1,102 @@ +/***************************************************************************** + * charset.c: Locale's character encoding stuff. + ***************************************************************************** + * See also unicode.c for Unicode to locale conversion helpers. + * + * Copyright (C) 2003-2008 the VideoLAN team + * + * Authors: Christophe Massiot + * Rémi Denis-Courmont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#if !defined WIN32 +# include +#else +# include +#endif + +#ifdef __APPLE__ +# include +# include +# include +#endif + +#include "libvlc.h" +#include "vlc_charset.h" + +char *vlc_fix_readdir( const char *psz_string ) +{ +#ifdef __APPLE__ + vlc_iconv_t hd = vlc_iconv_open( "UTF-8", "UTF-8-MAC" ); + + if (hd != (vlc_iconv_t)(-1)) + { + const char *psz_in = psz_string; + size_t i_in = strlen(psz_in); + size_t i_out = i_in * 2; + char *psz_utf8 = malloc(i_out + 1); + char *psz_out = psz_utf8; + + size_t i_ret = vlc_iconv (hd, &psz_in, &i_in, &psz_out, &i_out); + vlc_iconv_close (hd); + if( i_ret == (size_t)(-1) || i_in ) + { + free( psz_utf8 ); + return strdup( psz_string ); + } + + *psz_out = '\0'; + return psz_utf8; + } +#endif + return strdup( psz_string ); +} + + +/** + * us_strtod() has the same prototype as ANSI C strtod() but it uses the + * POSIX/C decimal format, regardless of the current numeric locale. + */ +double us_strtod( const char *str, char **end ) +{ + locale_t loc = newlocale (LC_NUMERIC_MASK, "C", NULL); + locale_t oldloc = uselocale (loc); + double res = strtod (str, end); + + if (loc != (locale_t)0) + { + uselocale (oldloc); + freelocale (loc); + } + return res; +} + +/** + * us_atof() has the same prototype as ANSI C atof() but it expects a dot + * as decimal separator, regardless of the system locale. + */ +double us_atof( const char *str ) +{ + return us_strtod( str, NULL ); +} + diff --git a/VLC/cmdline.c b/VLC/cmdline.c new file mode 100644 index 0000000..5d1ef74 --- /dev/null +++ b/VLC/cmdline.c @@ -0,0 +1,400 @@ +/***************************************************************************** + * cmdline.c: command line parsing + ***************************************************************************** + * Copyright (C) 2001-2007 the VideoLAN team + * $Id$ + * + * Authors: Gildas Bazin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "libvlc.h" +#include "vlc_keys.h" + + +# include /* getopt() */ + + +#include "configuration.h" +#include "modules.h" + +#include + +/***************************************************************************** + * config_LoadCmdLine: parse command line + ***************************************************************************** + * Parse command line for configuration options. + * Now that the module_bank has been initialized, we can dynamically + * generate the longopts structure used by getops. We have to do it this way + * because we don't know (and don't want to know) in advance the configuration + * options used (ie. exported) by each module. + *****************************************************************************/ +int __config_LoadCmdLine( vlc_object_t *p_this, int *pi_argc, + const char *ppsz_argv[], + bool b_ignore_errors ) +{ + int i_cmd, i_index, i_opts, i_shortopts, flag, i_verbose = 0; + module_t *p_parser; + vlc_list_t *p_list; + struct option *p_longopts; + int i_modules_index; + const char **argv_copy = NULL; + + /* Short options */ + module_config_t *pp_shortopts[256]; + char *psz_shortopts; + +#ifdef __APPLE__ + /* When VLC.app is run by double clicking in Mac OS X, the 2nd arg + * is the PSN - process serial number (a unique PID-ish thingie) + * still ok for real Darwin & when run from command line */ + if ( (*pi_argc > 1) && (strncmp( ppsz_argv[ 1 ] , "-psn" , 4 ) == 0) ) + /* for example -psn_0_9306113 */ + { + /* GDMF!... I can't do this or else the MacOSX window server will + * not pick up the PSN and not register the app and we crash... + * hence the following kludge otherwise we'll get confused w/ argv[1] + * being an input file name. + * As there won't be any more args to parse, just exit. */ + assert( *pi_argc == 2 ); + *pi_argc = 1; + return 0; + } +#endif + + /* List all modules */ + p_list = vlc_list_find( p_this, VLC_OBJECT_MODULE, FIND_ANYWHERE ); + + /* + * Generate the longopts and shortopts structures used by getopt_long + */ + + i_opts = 0; + for( i_modules_index = 0; i_modules_index < p_list->i_count; + i_modules_index++ ) + { + p_parser = (module_t *)p_list->p_values[i_modules_index].p_object ; + + /* count the number of exported configuration options (to allocate + * longopts). We also need to allocate space for two options when + * dealing with boolean to allow for --foo and --no-foo */ + i_opts += p_parser->i_config_items + + 2 * p_parser->i_bool_items; + } + + p_longopts = malloc( sizeof(struct option) * (i_opts + 1) ); + if( p_longopts == NULL ) + { + vlc_list_release( p_list ); + return -1; + } + + psz_shortopts = malloc( sizeof( char ) * (2 * i_opts + 1) ); + if( psz_shortopts == NULL ) + { + free( p_longopts ); + vlc_list_release( p_list ); + return -1; + } + + /* If we are requested to ignore errors, then we must work on a copy + * of the ppsz_argv array, otherwise getopt_long will reorder it for + * us, ignoring the arity of the options */ + if( b_ignore_errors ) + { + argv_copy = (const char**)malloc( *pi_argc * sizeof(char *) ); + if( argv_copy == NULL ) + { + free( psz_shortopts ); + free( p_longopts ); + vlc_list_release( p_list ); + return -1; + } + memcpy( argv_copy, ppsz_argv, *pi_argc * sizeof(char *) ); + ppsz_argv = argv_copy; + } + + i_shortopts = 0; + for( i_index = 0; i_index < 256; i_index++ ) + { + pp_shortopts[i_index] = NULL; + } + + /* Fill the p_longopts and psz_shortopts structures */ + i_index = 0; + for( i_modules_index = 0; i_modules_index < p_list->i_count; + i_modules_index++ ) + { + module_config_t *p_item, *p_end; + p_parser = (module_t *)p_list->p_values[i_modules_index].p_object ; + + if( !p_parser->i_config_items ) + continue; + + for( p_item = p_parser->p_config, p_end = p_item + p_parser->confsize; + p_item < p_end; + p_item++ ) + { + /* Ignore hints */ + if( p_item->i_type & CONFIG_HINT ) + continue; + + /* Add item to long options */ + p_longopts[i_index].name = strdup( p_item->psz_name ); + if( p_longopts[i_index].name == NULL ) continue; + p_longopts[i_index].has_arg = + (p_item->i_type == CONFIG_ITEM_BOOL) ? no_argument : +#ifndef __APPLE__ + required_argument; +#else +/* It seems that required_argument is broken on Darwin. + * Radar ticket #6113829 */ + optional_argument; +#endif + p_longopts[i_index].flag = &flag; + p_longopts[i_index].val = 0; + i_index++; + + /* When dealing with bools we also need to add the --no-foo + * option */ + if( p_item->i_type == CONFIG_ITEM_BOOL ) + { + char *psz_name = malloc( strlen(p_item->psz_name) + 3 ); + if( psz_name == NULL ) continue; + strcpy( psz_name, "no" ); + strcat( psz_name, p_item->psz_name ); + + p_longopts[i_index].name = psz_name; + p_longopts[i_index].has_arg = no_argument; + p_longopts[i_index].flag = &flag; + p_longopts[i_index].val = 1; + i_index++; + + psz_name = malloc( strlen(p_item->psz_name) + 4 ); + if( psz_name == NULL ) continue; + strcpy( psz_name, "no-" ); + strcat( psz_name, p_item->psz_name ); + + p_longopts[i_index].name = psz_name; + p_longopts[i_index].has_arg = no_argument; + p_longopts[i_index].flag = &flag; + p_longopts[i_index].val = 1; + i_index++; + } + + /* If item also has a short option, add it */ + if( p_item->i_short ) + { + pp_shortopts[(int)p_item->i_short] = p_item; + psz_shortopts[i_shortopts] = p_item->i_short; + i_shortopts++; + if( p_item->i_type != CONFIG_ITEM_BOOL ) + { + psz_shortopts[i_shortopts] = ':'; + i_shortopts++; + + if( p_item->i_short == 'v' ) + { + psz_shortopts[i_shortopts] = ':'; + i_shortopts++; + } + } + } + } + } + + /* We don't need the module list anymore */ + vlc_list_release( p_list ); + + /* Close the longopts and shortopts structures */ + memset( &p_longopts[i_index], 0, sizeof(struct option) ); + psz_shortopts[i_shortopts] = '\0'; + + /* + * Parse the command line options + */ + opterr = 0; + optind = 0; /* set to 0 to tell GNU getopt to reinitialize */ + while( ( i_cmd = getopt_long( *pi_argc, (char **)ppsz_argv, psz_shortopts, + p_longopts, &i_index ) ) != -1 ) + { + /* A long option has been recognized */ + if( i_cmd == 0 ) + { + module_config_t *p_conf; + char *psz_name = (char *)p_longopts[i_index].name; + + /* Check if we deal with a --nofoo or --no-foo long option */ + if( flag ) psz_name += psz_name[2] == '-' ? 3 : 2; + + /* Store the configuration option */ + p_conf = config_FindConfig( p_this, psz_name ); + if( p_conf ) + { + /* Check if the option is deprecated */ + if( p_conf->b_removed ) + { + fprintf(stderr, + "Warning: option --%s no longer exists.\n", + psz_name); + continue; + } + + if( p_conf->psz_oldname + && !strcmp( p_conf->psz_oldname, psz_name) ) + { + fprintf( stderr, + "%s: option --%s is deprecated. Use --%s instead.\n", + b_ignore_errors ? "Warning" : "Error", + psz_name, p_conf->psz_name ); + if( !b_ignore_errors ) + { + /*free */ + for( i_index = 0; p_longopts[i_index].name; i_index++ ) + free( (char *)p_longopts[i_index].name ); + + free( p_longopts ); + free( psz_shortopts ); + return -1; + } + + psz_name = p_conf->psz_name; + } + + switch( p_conf->i_type ) + { + case CONFIG_ITEM_STRING: + case CONFIG_ITEM_PASSWORD: + case CONFIG_ITEM_FILE: + case CONFIG_ITEM_DIRECTORY: + case CONFIG_ITEM_MODULE: + case CONFIG_ITEM_MODULE_LIST: + case CONFIG_ITEM_MODULE_LIST_CAT: + case CONFIG_ITEM_MODULE_CAT: + config_PutPsz( p_this, psz_name, optarg ); + break; + case CONFIG_ITEM_INTEGER: + config_PutInt( p_this, psz_name, strtol(optarg, 0, 0)); + break; + case CONFIG_ITEM_FLOAT: + config_PutFloat( p_this, psz_name, (float)atof(optarg) ); + break; + case CONFIG_ITEM_KEY: + config_PutInt( p_this, psz_name, ConfigStringToKey( optarg ) ); + break; + case CONFIG_ITEM_BOOL: + config_PutInt( p_this, psz_name, !flag ); + break; + } + continue; + } + } + + /* A short option has been recognized */ + if( pp_shortopts[i_cmd] != NULL ) + { + switch( pp_shortopts[i_cmd]->i_type ) + { + case CONFIG_ITEM_STRING: + case CONFIG_ITEM_PASSWORD: + case CONFIG_ITEM_FILE: + case CONFIG_ITEM_DIRECTORY: + case CONFIG_ITEM_MODULE: + case CONFIG_ITEM_MODULE_CAT: + case CONFIG_ITEM_MODULE_LIST: + case CONFIG_ITEM_MODULE_LIST_CAT: + config_PutPsz( p_this, pp_shortopts[i_cmd]->psz_name, optarg ); + break; + case CONFIG_ITEM_INTEGER: + if( i_cmd == 'v' ) + { + if( optarg ) + { + if( *optarg == 'v' ) /* eg. -vvv */ + { + i_verbose++; + while( *optarg == 'v' ) + { + i_verbose++; + optarg++; + } + } + else + { + i_verbose += atoi( optarg ); /* eg. -v2 */ + } + } + else + { + i_verbose++; /* -v */ + } + config_PutInt( p_this, pp_shortopts[i_cmd]->psz_name, + i_verbose ); + } + else + { + config_PutInt( p_this, pp_shortopts[i_cmd]->psz_name, + strtol(optarg, 0, 0) ); + } + break; + case CONFIG_ITEM_BOOL: + config_PutInt( p_this, pp_shortopts[i_cmd]->psz_name, 1 ); + break; + } + + continue; + } + + /* Internal error: unknown option */ + if( !b_ignore_errors ) + { + fprintf( stderr, "%s: unknown option" + " or missing mandatory argument ", + p_this->p_libvlc->psz_object_name ); + if( optopt ) + { + fprintf( stderr, "`-%c'\n", optopt ); + } + else + { + fprintf( stderr, "`%s'\n", ppsz_argv[optind-1] ); + } + fprintf( stderr, "Try `%s --help' for more information.\n", + p_this->p_libvlc->psz_object_name ); + + for( i_index = 0; p_longopts[i_index].name; i_index++ ) + free( (char *)p_longopts[i_index].name ); + free( p_longopts ); + free( psz_shortopts ); + return -1; + } + } + + /* Free allocated resources */ + for( i_index = 0; p_longopts[i_index].name; i_index++ ) + free( (char *)p_longopts[i_index].name ); + free( p_longopts ); + free( psz_shortopts ); + free( argv_copy ); + + return 0; +} + diff --git a/VLC/configuration.h b/VLC/configuration.h new file mode 100644 index 0000000..f4df308 --- /dev/null +++ b/VLC/configuration.h @@ -0,0 +1,73 @@ +/***************************************************************************** + * configuration.h management of the modules configuration + ***************************************************************************** + * Copyright (C) 2007 the VideoLAN team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#if defined(__PLUGIN__) || defined(__BUILTIN__) || !defined(__LIBVLC__) + +#endif + +#ifndef LIBVLC_CONFIGURATION_H +# define LIBVLC_CONFIGURATION_H 1 + +# ifdef __cplusplus +extern "C" { +# endif + +/* Internal configuration prototypes and structures */ + +int config_CreateDir( vlc_object_t *, const char * ); +int config_AutoSaveConfigFile( vlc_object_t * ); + +void config_Free( module_t * ); + +void config_SetCallbacks( module_config_t *, module_config_t *, size_t ); +void config_UnsetCallbacks( module_config_t *, size_t ); + +#define config_LoadCmdLine(a,b,c,d) __config_LoadCmdLine(VLC_OBJECT(a),b,c,d) +#define config_LoadConfigFile(a,b) __config_LoadConfigFile(VLC_OBJECT(a),b) + +int __config_LoadCmdLine ( vlc_object_t *, int *, const char *[], bool ); +char *config_GetCustomConfigFile( libvlc_int_t * ); +int __config_LoadConfigFile( vlc_object_t *, const char * ); + +int IsConfigStringType( int type ); +int IsConfigIntegerType (int type); +static inline int IsConfigFloatType (int type) +{ + return type == CONFIG_ITEM_FLOAT; +} + +int ConfigStringToKey( const char * ); + +/* The configuration file and directory */ +#if defined (SYS_BEOS) +# define CONFIG_DIR "config/settings/VideoLAN Client" +#elif defined (__APPLE__) +# define CONFIG_DIR "Library/Preferences/VLC" +#elif defined( WIN32 ) || defined( UNDER_CE ) +# define CONFIG_DIR "vlc" +#else +# define CONFIG_DIR ".vlc" +#endif +#define CONFIG_FILE "vlcrc" +#define PACKAGE "vlc" +# ifdef __cplusplus +} +# endif +#endif diff --git a/VLC/control.c b/VLC/control.c new file mode 100644 index 0000000..e591daa --- /dev/null +++ b/VLC/control.c @@ -0,0 +1,569 @@ +/***************************************************************************** + * control.c : Handle control of the playlist & running through it + ***************************************************************************** + * Copyright (C) 1999-2004 the VideoLAN team + * $Id$ + * + * Authors: Samuel Hocevar + * Clément Stenac + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "vlc_playlist.h" +#include "playlist_internal.h" +#include + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +static int PlaylistVAControl( playlist_t * p_playlist, int i_query, va_list args ); + +static void PreparseEnqueueItemSub( playlist_t *, playlist_item_t * ); + +/***************************************************************************** + * Playlist control + *****************************************************************************/ + +playlist_t *__pl_Yield( vlc_object_t *p_this ) +{ + playlist_t *pl; + + barrier (); + pl = libvlc_priv (p_this->p_libvlc)->p_playlist; + + assert( VLC_OBJECT(pl) != p_this /* This does not make sense to yield the playlist + using pl_Yield. use vlc_object_yield in this case */ ); + + if (pl) + vlc_object_yield (pl); + return pl; +} + +void __pl_Release( vlc_object_t *p_this ) +{ + playlist_t *pl = libvlc_priv (p_this->p_libvlc)->p_playlist; + assert( pl != NULL ); + + assert( VLC_OBJECT(pl) != p_this /* The rule is that pl_Release() should act on + the same object than pl_Yield() */ ); + + vlc_object_release( pl ); +} + +int playlist_Control( playlist_t * p_playlist, int i_query, + bool b_locked, ... ) +{ + va_list args; + int i_result; + va_start( args, b_locked ); + PL_LOCK_IF( !b_locked ); + i_result = PlaylistVAControl( p_playlist, i_query, args ); + va_end( args ); + PL_UNLOCK_IF( !b_locked ); + + return i_result; +} + +static int PlaylistVAControl( playlist_t * p_playlist, int i_query, va_list args ) +{ + playlist_item_t *p_item, *p_node; + vlc_value_t val; + + PL_ASSERT_LOCKED; + + if( !vlc_object_alive( p_playlist ) ) + return VLC_EGENERIC; + + if( playlist_IsEmpty( p_playlist ) ) + return VLC_EGENERIC; + + switch( i_query ) + { + case PLAYLIST_STOP: + p_playlist->request.i_status = PLAYLIST_STOPPED; + p_playlist->request.b_request = true; + p_playlist->request.p_item = NULL; + break; + + // Node can be null, it will keep the same. Use with care ... + // Item null = take the first child of node + case PLAYLIST_VIEWPLAY: + p_node = (playlist_item_t *)va_arg( args, playlist_item_t * ); + p_item = (playlist_item_t *)va_arg( args, playlist_item_t * ); + if ( p_node == NULL ) + { + p_node = get_current_status_node( p_playlist ); + assert( p_node ); + } + p_playlist->request.i_status = PLAYLIST_RUNNING; + p_playlist->request.i_skip = 0; + p_playlist->request.b_request = true; + p_playlist->request.p_node = p_node; + p_playlist->request.p_item = p_item; + if( p_item && var_GetBool( p_playlist, "random" ) ) + p_playlist->b_reset_currently_playing = true; + break; + + case PLAYLIST_PLAY: + if( p_playlist->p_input ) + { + val.i_int = PLAYING_S; + var_Set( p_playlist->p_input, "state", val ); + break; + } + else + { + p_playlist->request.i_status = PLAYLIST_RUNNING; + p_playlist->request.b_request = true; + p_playlist->request.p_node = get_current_status_node( p_playlist ); + p_playlist->request.p_item = get_current_status_item( p_playlist ); + p_playlist->request.i_skip = 0; + } + break; + + case PLAYLIST_PAUSE: + val.i_int = 0; + if( p_playlist->p_input ) + var_Get( p_playlist->p_input, "state", &val ); + + if( val.i_int == PAUSE_S ) + { + p_playlist->status.i_status = PLAYLIST_RUNNING; + if( p_playlist->p_input ) + { + val.i_int = PLAYING_S; + var_Set( p_playlist->p_input, "state", val ); + } + } + else + { + p_playlist->status.i_status = PLAYLIST_PAUSED; + if( p_playlist->p_input ) + { + val.i_int = PAUSE_S; + var_Set( p_playlist->p_input, "state", val ); + } + } + break; + + case PLAYLIST_SKIP: + p_playlist->request.p_node = get_current_status_node( p_playlist ); + p_playlist->request.p_item = get_current_status_item( p_playlist ); + p_playlist->request.i_skip = (int) va_arg( args, int ); + /* if already running, keep running */ + if( p_playlist->status.i_status != PLAYLIST_STOPPED ) + p_playlist->request.i_status = p_playlist->status.i_status; + p_playlist->request.b_request = true; + break; + + default: + msg_Err( p_playlist, "unknown playlist query" ); + return VLC_EBADVAR; + break; + } + vlc_object_signal_unlocked( p_playlist ); + + return VLC_SUCCESS; +} + +/***************************************************************************** + * Preparse control + *****************************************************************************/ +/** Enqueue an item for preparsing */ +int playlist_PreparseEnqueue( playlist_t *p_playlist, + input_item_t *p_item ) +{ + vlc_object_lock( p_playlist->p_preparse ); + if( !vlc_object_alive( p_playlist->p_preparse ) ) + { + vlc_object_unlock( p_playlist->p_preparse ); + return VLC_EGENERIC; + } + vlc_gc_incref( p_item ); + INSERT_ELEM( p_playlist->p_preparse->pp_waiting, + p_playlist->p_preparse->i_waiting, + p_playlist->p_preparse->i_waiting, + p_item ); + vlc_object_signal_unlocked( p_playlist->p_preparse ); + vlc_object_unlock( p_playlist->p_preparse ); + return VLC_SUCCESS; +} + +/** Enqueue a playlist item or a node for peparsing. + * This function should be entered without playlist and preparser locks */ +int playlist_PreparseEnqueueItem( playlist_t *p_playlist, + playlist_item_t *p_item ) +{ + vlc_object_lock( p_playlist ); + vlc_object_lock( p_playlist->p_preparse ); + if( !vlc_object_alive( p_playlist->p_preparse ) ) + { + vlc_object_unlock( p_playlist->p_preparse ); + vlc_object_unlock( p_playlist ); + return VLC_EGENERIC; + } + PreparseEnqueueItemSub( p_playlist, p_item ); + vlc_object_unlock( p_playlist->p_preparse ); + vlc_object_unlock( p_playlist ); + return VLC_SUCCESS; +} + +int playlist_AskForArtEnqueue( playlist_t *p_playlist, + input_item_t *p_item ) +{ + vlc_object_lock( p_playlist->p_fetcher ); + if( !vlc_object_alive( p_playlist->p_fetcher ) ) + { + vlc_object_unlock( p_playlist->p_fetcher ); + return VLC_EGENERIC; + } + + vlc_gc_incref( p_item ); + INSERT_ELEM( p_playlist->p_fetcher->pp_waiting, + p_playlist->p_fetcher->i_waiting, + p_playlist->p_fetcher->i_waiting, p_item ); + vlc_object_signal_unlocked( p_playlist->p_fetcher ); + vlc_object_unlock( p_playlist->p_fetcher ); + return VLC_SUCCESS; +} + +static void PreparseEnqueueItemSub( playlist_t *p_playlist, + playlist_item_t *p_item ) +{ + int i; + if( p_item->i_children == -1 ) + { + vlc_gc_incref( p_item->p_input ); + INSERT_ELEM( p_playlist->p_preparse->pp_waiting, + p_playlist->p_preparse->i_waiting, + p_playlist->p_preparse->i_waiting, + p_item->p_input ); + } + else + { + for( i = 0; i < p_item->i_children; i++) + { + PreparseEnqueueItemSub( p_playlist, p_item->pp_children[i] ); + } + } +} + +/***************************************************************************** + * Playback logic + *****************************************************************************/ + +/** + * Synchronise the current index of the playlist + * to match the index of the current item. + * + * \param p_playlist the playlist structure + * \param p_cur the current playlist item + * \return nothing + */ +static void ResyncCurrentIndex( playlist_t *p_playlist, playlist_item_t *p_cur ) +{ + PL_DEBUG( "resyncing on %s", PLI_NAME( p_cur ) ); + /* Simply resync index */ + int i; + p_playlist->i_current_index = -1; + for( i = 0 ; i< p_playlist->current.i_size; i++ ) + { + if( ARRAY_VAL( p_playlist->current, i ) == p_cur ) + { + p_playlist->i_current_index = i; + break; + } + } + PL_DEBUG( "%s is at %i", PLI_NAME( p_cur ), p_playlist->i_current_index ); +} + +void ResetCurrentlyPlaying( playlist_t *p_playlist, bool b_random, + playlist_item_t *p_cur ) +{ + playlist_item_t *p_next = NULL; + stats_TimerStart( p_playlist, "Items array build", + STATS_TIMER_PLAYLIST_BUILD ); + PL_DEBUG( "rebuilding array of current - root %s", + PLI_NAME( p_playlist->status.p_node ) ); + ARRAY_RESET( p_playlist->current ); + p_playlist->i_current_index = -1; + while( 1 ) + { + /** FIXME: this is *slow* */ + p_next = playlist_GetNextLeaf( p_playlist, + p_playlist->status.p_node, + p_next, true, false ); + if( p_next ) + { + if( p_next == p_cur ) + p_playlist->i_current_index = p_playlist->current.i_size; + ARRAY_APPEND( p_playlist->current, p_next); + } + else break; + } + PL_DEBUG("rebuild done - %i items, index %i", p_playlist->current.i_size, + p_playlist->i_current_index); + if( b_random ) + { + /* Shuffle the array */ + srand( (unsigned int)mdate() ); + int j; + for( j = p_playlist->current.i_size - 1; j > 0; j-- ) + { + int i = rand() % (j+1); /* between 0 and j */ + playlist_item_t *p_tmp; + /* swap the two items */ + p_tmp = ARRAY_VAL(p_playlist->current, i); + ARRAY_VAL(p_playlist->current,i) = ARRAY_VAL(p_playlist->current,j); + ARRAY_VAL(p_playlist->current,j) = p_tmp; + } + } + p_playlist->b_reset_currently_playing = false; + stats_TimerStop( p_playlist, STATS_TIMER_PLAYLIST_BUILD ); +} + +/** + * Compute the next playlist item depending on + * the playlist course mode (forward, backward, random, view,...). + * + * \param p_playlist the playlist object + * \return nothing + */ +playlist_item_t * playlist_NextItem( playlist_t *p_playlist ) +{ + playlist_item_t *p_new = NULL; + int i_skip = 0, i; + + bool b_loop = var_GetBool( p_playlist, "loop" ); + bool b_random = var_GetBool( p_playlist, "random" ); + bool b_repeat = var_GetBool( p_playlist, "repeat" ); + bool b_playstop = var_GetBool( p_playlist, "play-and-stop" ); + + /* Handle quickly a few special cases */ + /* No items to play */ + if( p_playlist->items.i_size == 0 ) + { + msg_Info( p_playlist, "playlist is empty" ); + return NULL; + } + + /* Repeat and play/stop */ + if( !p_playlist->request.b_request && b_repeat == true && + get_current_status_item( p_playlist ) ) + { + msg_Dbg( p_playlist,"repeating item" ); + return get_current_status_item( p_playlist ); + } + if( !p_playlist->request.b_request && b_playstop == true ) + { + msg_Dbg( p_playlist,"stopping (play and stop)" ); + return NULL; + } + + if( !p_playlist->request.b_request && + get_current_status_item( p_playlist ) ) + { + playlist_item_t *p_parent = get_current_status_item( p_playlist ); + while( p_parent ) + { + if( p_parent->i_flags & PLAYLIST_SKIP_FLAG ) + { + msg_Dbg( p_playlist, "blocking item, stopping") ; + return NULL; + } + p_parent = p_parent->p_parent; + } + } + + /* Start the real work */ + if( p_playlist->request.b_request ) + { + p_new = p_playlist->request.p_item; + i_skip = p_playlist->request.i_skip; + PL_DEBUG( "processing request item %s node %s skip %i", + PLI_NAME( p_playlist->request.p_item ), + PLI_NAME( p_playlist->request.p_node ), i_skip ); + + if( p_playlist->request.p_node && + p_playlist->request.p_node != get_current_status_node( p_playlist ) ) + { + + set_current_status_node( p_playlist, p_playlist->request.p_node ); + p_playlist->request.p_node = NULL; + p_playlist->b_reset_currently_playing = true; + } + + /* If we are asked for a node, go to it's first child */ + if( i_skip == 0 && ( p_new == NULL || p_new->i_children != -1 ) ) + { + i_skip++; + if( p_new != NULL ) + { + p_new = playlist_GetNextLeaf( p_playlist, p_new, NULL, true, false ); + for( i = 0; i < p_playlist->current.i_size; i++ ) + { + if( p_new == ARRAY_VAL( p_playlist->current, i ) ) + { + p_playlist->i_current_index = i; + i_skip = 0; + } + } + } + } + + if( p_playlist->b_reset_currently_playing ) + /* A bit too bad to reset twice ... */ + ResetCurrentlyPlaying( p_playlist, b_random, p_new ); + else if( p_new ) + ResyncCurrentIndex( p_playlist, p_new ); + else + p_playlist->i_current_index = -1; + + if( p_playlist->current.i_size && (i_skip > 0) ) + { + if( p_playlist->i_current_index < -1 ) + p_playlist->i_current_index = -1; + for( i = i_skip; i > 0 ; i-- ) + { + p_playlist->i_current_index++; + if( p_playlist->i_current_index >= p_playlist->current.i_size ) + { + PL_DEBUG( "looping - restarting at beginning of node" ); + p_playlist->i_current_index = 0; + } + } + p_new = ARRAY_VAL( p_playlist->current, + p_playlist->i_current_index ); + } + else if( p_playlist->current.i_size && (i_skip < 0) ) + { + for( i = i_skip; i < 0 ; i++ ) + { + p_playlist->i_current_index--; + if( p_playlist->i_current_index <= -1 ) + { + PL_DEBUG( "looping - restarting at end of node" ); + p_playlist->i_current_index = p_playlist->current.i_size-1; + } + } + p_new = ARRAY_VAL( p_playlist->current, + p_playlist->i_current_index ); + } + /* Clear the request */ + p_playlist->request.b_request = false; + } + /* "Automatic" item change ( next ) */ + else + { + PL_DEBUG( "changing item without a request (current %i/%i)", + p_playlist->i_current_index, p_playlist->current.i_size ); + /* Cant go to next from current item */ + if( get_current_status_item( p_playlist ) && + get_current_status_item( p_playlist )->i_flags & PLAYLIST_SKIP_FLAG ) + return NULL; + + if( p_playlist->b_reset_currently_playing ) + ResetCurrentlyPlaying( p_playlist, b_random, + get_current_status_item( p_playlist ) ); + + p_playlist->i_current_index++; + assert( p_playlist->i_current_index <= p_playlist->current.i_size ); + if( p_playlist->i_current_index == p_playlist->current.i_size ) + { + if( !b_loop || p_playlist->current.i_size == 0 ) return NULL; + p_playlist->i_current_index = 0; + } + PL_DEBUG( "using item %i", p_playlist->i_current_index ); + if ( p_playlist->current.i_size == 0 ) return NULL; + + p_new = ARRAY_VAL( p_playlist->current, p_playlist->i_current_index ); + /* The new item can't be autoselected */ + if( p_new != NULL && p_new->i_flags & PLAYLIST_SKIP_FLAG ) + return NULL; + } + return p_new; +} + +/** + * Start the input for an item + * + * \param p_playlist the playlist objetc + * \param p_item the item to play + * \return nothing + */ +int playlist_PlayItem( playlist_t *p_playlist, playlist_item_t *p_item ) +{ + input_item_t *p_input = p_item->p_input; + sout_instance_t **pp_sout = &libvlc_priv(p_playlist->p_libvlc)->p_sout; + int i_activity = var_GetInteger( p_playlist, "activity" ) ; + + msg_Dbg( p_playlist, "creating new input thread" ); + + p_input->i_nb_played++; + set_current_status_item( p_playlist, p_item ); + + p_playlist->status.i_status = PLAYLIST_RUNNING; + + var_SetInteger( p_playlist, "activity", i_activity + + DEFAULT_INPUT_ACTIVITY ); + + input_thread_t * p_input_thread = + input_CreateThreadExtended( p_playlist, p_input, NULL, *pp_sout ); + playlist_set_current_input( p_playlist, p_input_thread ); + vlc_object_release( p_input_thread ); + + *pp_sout = NULL; + + char *psz_uri = input_item_GetURI( p_item->p_input ); + if( psz_uri && ( !strncmp( psz_uri, "directory:", 10 ) || + !strncmp( psz_uri, "vlc:", 4 ) ) ) + { + free( psz_uri ); + return VLC_SUCCESS; + } + free( psz_uri ); + + if( p_playlist->p_fetcher && + p_playlist->p_fetcher->i_art_policy == ALBUM_ART_WHEN_PLAYED ) + { + bool b_has_art; + + char *psz_arturl, *psz_name; + psz_arturl = input_item_GetArtURL( p_input ); + psz_name = input_item_GetName( p_input ); + + /* p_input->p_meta should not be null after a successfull CreateThread */ + b_has_art = !EMPTY_STR( psz_arturl ); + + if( !b_has_art || strncmp( psz_arturl, "attachment://", 13 ) ) + { + PL_DEBUG( "requesting art for %s", psz_name ); + playlist_AskForArtEnqueue( p_playlist, p_input ); + } + free( psz_arturl ); + free( psz_name ); + } + + PL_UNLOCK; + var_SetInteger( p_playlist, "playlist-current", p_input->i_id ); + PL_LOCK; + + return VLC_SUCCESS; +} diff --git a/VLC/core.c b/VLC/core.c new file mode 100644 index 0000000..68bc879 --- /dev/null +++ b/VLC/core.c @@ -0,0 +1,571 @@ +/***************************************************************************** + * core.c management of the modules configuration + ***************************************************************************** + * Copyright (C) 2001-2007 the VideoLAN team + * $Id$ + * + * Authors: Gildas Bazin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "libvlc.h" +#include "vlc_keys.h" +#include "vlc_charset.h" +#include "vlc_configuration.h" + +#include + +#include "configuration.h" +#include "modules.h" + +static inline char *strdupnull (const char *src) +{ + return src ? strdup (src) : NULL; +} + +/* Item types that use a string value (i.e. serialized in the module cache) */ +int IsConfigStringType (int type) +{ + static const unsigned char config_types[] = + { + CONFIG_ITEM_STRING, CONFIG_ITEM_FILE, CONFIG_ITEM_MODULE, + CONFIG_ITEM_DIRECTORY, CONFIG_ITEM_MODULE_CAT, CONFIG_ITEM_PASSWORD, + CONFIG_ITEM_MODULE_LIST, CONFIG_ITEM_MODULE_LIST_CAT + }; + + /* NOTE: this needs to be changed if we ever get more than 255 types */ + return memchr (config_types, type, sizeof (config_types)) != NULL; +} + + +int IsConfigIntegerType (int type) +{ + static const unsigned char config_types[] = + { + CONFIG_ITEM_INTEGER, CONFIG_ITEM_KEY, CONFIG_ITEM_BOOL, + CONFIG_CATEGORY, CONFIG_SUBCATEGORY + }; + + return memchr (config_types, type, sizeof (config_types)) != NULL; +} + + +/***************************************************************************** + * config_GetType: get the type of a variable (bool, int, float, string) + ***************************************************************************** + * This function is used to get the type of a variable from its name. + * Beware, this is quite slow. + *****************************************************************************/ +int __config_GetType( vlc_object_t *p_this, const char *psz_name ) +{ + module_config_t *p_config; + int i_type; + + p_config = config_FindConfig( p_this, psz_name ); + + /* sanity checks */ + if( !p_config ) + { + return 0; + } + + switch( p_config->i_type ) + { + case CONFIG_ITEM_BOOL: + i_type = VLC_VAR_BOOL; + break; + + case CONFIG_ITEM_INTEGER: + case CONFIG_ITEM_KEY: + i_type = VLC_VAR_INTEGER; + break; + + case CONFIG_ITEM_FLOAT: + i_type = VLC_VAR_FLOAT; + break; + + case CONFIG_ITEM_MODULE: + case CONFIG_ITEM_MODULE_CAT: + case CONFIG_ITEM_MODULE_LIST: + case CONFIG_ITEM_MODULE_LIST_CAT: + i_type = VLC_VAR_MODULE; + break; + + case CONFIG_ITEM_STRING: + i_type = VLC_VAR_STRING; + break; + + case CONFIG_ITEM_PASSWORD: + i_type = VLC_VAR_STRING; + break; + + case CONFIG_ITEM_FILE: + i_type = VLC_VAR_FILE; + break; + + case CONFIG_ITEM_DIRECTORY: + i_type = VLC_VAR_DIRECTORY; + break; + + default: + i_type = 0; + break; + } + + return i_type; +} + +/***************************************************************************** + * config_GetInt: get the value of an int variable + ***************************************************************************** + * This function is used to get the value of variables which are internally + * represented by an integer (CONFIG_ITEM_INTEGER and + * CONFIG_ITEM_BOOL). + *****************************************************************************/ +int __config_GetInt( vlc_object_t *p_this, const char *psz_name ) +{ + module_config_t *p_config; + + p_config = config_FindConfig( p_this, psz_name ); + + /* sanity checks */ + if( !p_config ) + { + msg_Err( p_this, "option %s does not exist", psz_name ); + return -1; + } + + if (!IsConfigIntegerType (p_config->i_type)) + { + msg_Err( p_this, "option %s does not refer to an int", psz_name ); + return -1; + } + + return p_config->value.i; +} + +/***************************************************************************** + * config_GetFloat: get the value of a float variable + ***************************************************************************** + * This function is used to get the value of variables which are internally + * represented by a float (CONFIG_ITEM_FLOAT). + *****************************************************************************/ +float __config_GetFloat( vlc_object_t *p_this, const char *psz_name ) +{ + module_config_t *p_config; + + p_config = config_FindConfig( p_this, psz_name ); + + /* sanity checks */ + if( !p_config ) + { + msg_Err( p_this, "option %s does not exist", psz_name ); + return -1; + } + + if (!IsConfigFloatType (p_config->i_type)) + { + msg_Err( p_this, "option %s does not refer to a float", psz_name ); + return -1; + } + + return p_config->value.f; +} + +/***************************************************************************** + * config_GetPsz: get the string value of a string variable + ***************************************************************************** + * This function is used to get the value of variables which are internally + * represented by a string (CONFIG_ITEM_STRING, CONFIG_ITEM_FILE, + * CONFIG_ITEM_DIRECTORY, CONFIG_ITEM_PASSWORD, and CONFIG_ITEM_MODULE). + * + * Important note: remember to free() the returned char* because it's a + * duplicate of the actual value. It isn't safe to return a pointer to the + * actual value as it can be modified at any time. + *****************************************************************************/ +char * __config_GetPsz( vlc_object_t *p_this, const char *psz_name ) +{ + module_config_t *p_config; + + p_config = config_FindConfig( p_this, psz_name ); + + /* sanity checks */ + if( !p_config ) + { + msg_Err( p_this, "option %s does not exist", psz_name ); + return NULL; + } + + if (!IsConfigStringType (p_config->i_type)) + { + msg_Err( p_this, "option %s does not refer to a string", psz_name ); + return NULL; + } + + /* return a copy of the string */ + vlc_mutex_lock( p_config->p_lock ); + char *psz_value = strdupnull (p_config->value.psz); + vlc_mutex_unlock( p_config->p_lock ); + + return psz_value; +} + +/***************************************************************************** + * config_PutPsz: set the string value of a string variable + ***************************************************************************** + * This function is used to set the value of variables which are internally + * represented by a string (CONFIG_ITEM_STRING, CONFIG_ITEM_FILE, + * CONFIG_ITEM_DIRECTORY, CONFIG_ITEM_PASSWORD, and CONFIG_ITEM_MODULE). + *****************************************************************************/ +void __config_PutPsz( vlc_object_t *p_this, + const char *psz_name, const char *psz_value ) +{ + module_config_t *p_config; + vlc_value_t oldval, val; + + p_config = config_FindConfig( p_this, psz_name ); + + + /* sanity checks */ + if( !p_config ) + { + msg_Warn( p_this, "option %s does not exist", psz_name ); + return; + } + + if (!IsConfigStringType (p_config->i_type)) + { + msg_Err( p_this, "option %s does not refer to a string", psz_name ); + return; + } + + vlc_mutex_lock( p_config->p_lock ); + + /* backup old value */ + oldval.psz_string = (char *)p_config->value.psz; + + if ((psz_value != NULL) && *psz_value) + p_config->value.psz = strdup (psz_value); + else + p_config->value.psz = NULL; + + p_config->b_dirty = true; + + val.psz_string = (char *)p_config->value.psz; + + vlc_mutex_unlock( p_config->p_lock ); + + if( p_config->pf_callback ) + { + p_config->pf_callback( p_this, psz_name, oldval, val, + p_config->p_callback_data ); + } + + /* free old string */ + free( oldval.psz_string ); +} + +/***************************************************************************** + * config_PutInt: set the integer value of an int variable + ***************************************************************************** + * This function is used to set the value of variables which are internally + * represented by an integer (CONFIG_ITEM_INTEGER and + * CONFIG_ITEM_BOOL). + *****************************************************************************/ +void __config_PutInt( vlc_object_t *p_this, const char *psz_name, int i_value ) +{ + module_config_t *p_config; + vlc_value_t oldval, val; + + p_config = config_FindConfig( p_this, psz_name ); + + /* sanity checks */ + if( !p_config ) + { + msg_Warn( p_this, "option %s does not exist", psz_name ); + return; + } + + if (!IsConfigIntegerType (p_config->i_type)) + { + msg_Err( p_this, "option %s does not refer to an int", psz_name ); + return; + } + + /* backup old value */ + oldval.i_int = p_config->value.i; + + /* if i_min == i_max == 0, then do not use them */ + if ((p_config->min.i == 0) && (p_config->max.i == 0)) + { + p_config->value.i = i_value; + } + else if (i_value < p_config->min.i) + { + p_config->value.i = p_config->min.i; + } + else if (i_value > p_config->max.i) + { + p_config->value.i = p_config->max.i; + } + else + { + p_config->value.i = i_value; + } + + p_config->b_dirty = true; + + val.i_int = p_config->value.i; + + if( p_config->pf_callback ) + { + p_config->pf_callback( p_this, psz_name, oldval, val, + p_config->p_callback_data ); + } +} + +/***************************************************************************** + * config_PutFloat: set the value of a float variable + ***************************************************************************** + * This function is used to set the value of variables which are internally + * represented by a float (CONFIG_ITEM_FLOAT). + *****************************************************************************/ +void __config_PutFloat( vlc_object_t *p_this, + const char *psz_name, float f_value ) +{ + module_config_t *p_config; + vlc_value_t oldval, val; + + p_config = config_FindConfig( p_this, psz_name ); + + /* sanity checks */ + if( !p_config ) + { + msg_Warn( p_this, "option %s does not exist", psz_name ); + return; + } + + if (!IsConfigFloatType (p_config->i_type)) + { + msg_Err( p_this, "option %s does not refer to a float", psz_name ); + return; + } + + /* backup old value */ + oldval.f_float = p_config->value.f; + + /* if f_min == f_max == 0, then do not use them */ + if ((p_config->min.f == 0) && (p_config->max.f == 0)) + { + p_config->value.f = f_value; + } + else if (f_value < p_config->min.f) + { + p_config->value.f = p_config->min.f; + } + else if (f_value > p_config->max.f) + { + p_config->value.f = p_config->max.f; + } + else + { + p_config->value.f = f_value; + } + + p_config->b_dirty = true; + + val.f_float = p_config->value.f; + + if( p_config->pf_callback ) + { + p_config->pf_callback( p_this, psz_name, oldval, val, + p_config->p_callback_data ); + } +} + +/***************************************************************************** + * config_FindConfig: find the config structure associated with an option. + ***************************************************************************** + * FIXME: This function really needs to be optimized. + * FIXME: And now even more. + *****************************************************************************/ +module_config_t *config_FindConfig( vlc_object_t *p_this, const char *psz_name ) +{ + vlc_list_t *p_list; + int i_index; + + if( !psz_name ) return NULL; + + p_list = vlc_list_find( p_this, VLC_OBJECT_MODULE, FIND_ANYWHERE ); + + for( i_index = 0; i_index < p_list->i_count; i_index++ ) + { + module_config_t *p_item, *p_end; + module_t *p_parser = (module_t *)p_list->p_values[i_index].p_object; + + if( !p_parser->i_config_items ) + continue; + + for( p_item = p_parser->p_config, p_end = p_item + p_parser->confsize; + p_item < p_end; + p_item++ ) + { + if( p_item->i_type & CONFIG_HINT ) + /* ignore hints */ + continue; + if( !strcmp( psz_name, p_item->psz_name ) + || ( p_item->psz_oldname + && !strcmp( psz_name, p_item->psz_oldname ) ) ) + { + vlc_list_release( p_list ); + return p_item; + } + } + } + + vlc_list_release( p_list ); + + return NULL; +} + +/***************************************************************************** + * config_Free: frees a duplicated module's configuration data. + ***************************************************************************** + * This function frees all the data duplicated by config_Duplicate. + *****************************************************************************/ +void config_Free( module_t *p_module ) +{ + int i; + + for (size_t j = 0; j < p_module->confsize; j++) + { + module_config_t *p_item = p_module->p_config + j; + + free( p_item->psz_type ); + free( p_item->psz_name ); + free( p_item->psz_text ); + free( p_item->psz_longtext ); + free( p_item->psz_oldname ); + + if (IsConfigStringType (p_item->i_type)) + { + free (p_item->value.psz); + free (p_item->orig.psz); + free (p_item->saved.psz); + } + + if( p_item->ppsz_list ) + for( i = 0; i < p_item->i_list; i++ ) + free( p_item->ppsz_list[i] ); + if( p_item->ppsz_list_text ) + for( i = 0; i < p_item->i_list; i++ ) + free( p_item->ppsz_list_text[i] ); + free( p_item->ppsz_list ); + free( p_item->ppsz_list_text ); + free( p_item->pi_list ); + + if( p_item->i_action ) + { + for( i = 0; i < p_item->i_action; i++ ) + { + free( p_item->ppsz_action_text[i] ); + } + free( p_item->ppf_action ); + free( p_item->ppsz_action_text ); + } + } + + free (p_module->p_config); + p_module->p_config = NULL; +} + +/***************************************************************************** + * config_SetCallbacks: sets callback functions in the duplicate p_config. + ***************************************************************************** + * Unfortunatly we cannot work directly with the module's config data as + * this module might be unloaded from memory at any time (remember HideModule). + * This is why we need to duplicate callbacks each time we reload the module. + *****************************************************************************/ +void config_SetCallbacks( module_config_t *p_new, module_config_t *p_orig, + size_t n ) +{ + for (size_t i = 0; i < n; i++) + { + p_new->pf_callback = p_orig->pf_callback; + p_new++; + p_orig++; + } +} + +/***************************************************************************** + * config_UnsetCallbacks: unsets callback functions in the duplicate p_config. + ***************************************************************************** + * We simply undo what we did in config_SetCallbacks. + *****************************************************************************/ +void config_UnsetCallbacks( module_config_t *p_new, size_t n ) +{ + for (size_t i = 0; i < n; i++) + { + p_new->pf_callback = NULL; + p_new++; + } +} + +/***************************************************************************** + * config_ResetAll: reset the configuration data for all the modules. + *****************************************************************************/ +void __config_ResetAll( vlc_object_t *p_this ) +{ + libvlc_priv_t *priv = libvlc_priv (p_this->p_libvlc); + int i_index; + vlc_list_t *p_list; + module_t *p_module; + + /* Acquire config file lock */ + vlc_mutex_lock( &priv->config_lock ); + + p_list = vlc_list_find( p_this, VLC_OBJECT_MODULE, FIND_ANYWHERE ); + + for( i_index = 0; i_index < p_list->i_count; i_index++ ) + { + p_module = (module_t *)p_list->p_values[i_index].p_object ; + if( p_module->b_submodule ) continue; + + for (size_t i = 0; i < p_module->confsize; i++ ) + { + if (IsConfigIntegerType (p_module->p_config[i].i_type)) + p_module->p_config[i].value.i = p_module->p_config[i].orig.i; + else + if (IsConfigFloatType (p_module->p_config[i].i_type)) + p_module->p_config[i].value.f = p_module->p_config[i].orig.f; + else + if (IsConfigStringType (p_module->p_config[i].i_type)) + { + free ((char *)p_module->p_config[i].value.psz); + p_module->p_config[i].value.psz = + strdupnull (p_module->p_config[i].orig.psz); + } + } + } + + vlc_list_release( p_list ); + vlc_mutex_unlock( &priv->config_lock ); +} diff --git a/VLC/cpu.c b/VLC/cpu.c new file mode 100644 index 0000000..38c92d8 --- /dev/null +++ b/VLC/cpu.c @@ -0,0 +1,360 @@ +/***************************************************************************** + * cpu.c: CPU detection code + ***************************************************************************** + * Copyright (C) 1998-2004 the VideoLAN team + * $Id$ + * + * Authors: Samuel Hocevar + * Christophe Massiot + * Eugenio Jarosiewicz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#ifdef HAVE_SIGNAL_H +# include /* SIGHUP, SIGINT, SIGKILL */ +# include /* longjmp, setjmp */ +#endif + +#include "libvlc.h" + +#if defined(__APPLE__) && (defined(__ppc__) || defined(__ppc64__)) +#include +#endif + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +#ifdef HAVE_SIGNAL_H +static void SigHandler ( int ); +#endif + +/***************************************************************************** + * Global variables - they're needed for signal handling + *****************************************************************************/ +#ifdef HAVE_SIGNAL_H +static jmp_buf env; +static int i_illegal; +#if defined( __i386__ ) || defined( __x86_64__ ) +static const char *psz_capability; +#endif +#endif + +/***************************************************************************** + * CPUCapabilities: get the CPU capabilities + ***************************************************************************** + * This function is called to list extensions the CPU may have. + *****************************************************************************/ +uint32_t CPUCapabilities( void ) +{ + volatile uint32_t i_capabilities = CPU_CAPABILITY_NONE; + +#if defined(__APPLE__) && (defined(__ppc__) || defined(__ppc64__)) + int selectors[2] = { CTL_HW, HW_VECTORUNIT }; + int i_has_altivec = 0; + size_t i_length = sizeof( i_has_altivec ); + int i_error = sysctl( selectors, 2, &i_has_altivec, &i_length, NULL, 0); + + i_capabilities |= CPU_CAPABILITY_FPU; + + if( i_error == 0 && i_has_altivec != 0 ) + i_capabilities |= CPU_CAPABILITY_ALTIVEC; + + return i_capabilities; + +#elif defined( __i386__ ) || defined( __x86_64__ ) + volatile unsigned int i_eax, i_ebx, i_ecx, i_edx; + volatile bool b_amd; + + /* Needed for x86 CPU capabilities detection */ +# if defined( __x86_64__ ) +# define cpuid( reg ) \ + asm volatile ( "cpuid\n\t" \ + "movl %%ebx,%1\n\t" \ + : "=a" ( i_eax ), \ + "=b" ( i_ebx ), \ + "=c" ( i_ecx ), \ + "=d" ( i_edx ) \ + : "a" ( reg ) \ + : "cc" ); +# else +# define cpuid( reg ) \ + asm volatile ( "push %%ebx\n\t" \ + "cpuid\n\t" \ + "movl %%ebx,%1\n\t" \ + "pop %%ebx\n\t" \ + : "=a" ( i_eax ), \ + "=r" ( i_ebx ), \ + "=c" ( i_ecx ), \ + "=d" ( i_edx ) \ + : "a" ( reg ) \ + : "cc" ); +# endif + +# if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW ) \ + && defined( HAVE_SIGNAL_H ) + void (*pf_sigill) (int) = signal( SIGILL, SigHandler ); +# endif + + i_capabilities |= CPU_CAPABILITY_FPU; + + + + i_capabilities |= CPU_CAPABILITY_486; + + /* the CPU supports the CPUID instruction - get its level */ + //cpuid( 0x00000000 ); + + if( !i_eax ) + { +# if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW ) \ + && defined( HAVE_SIGNAL_H ) + signal( SIGILL, pf_sigill ); +# endif + return i_capabilities; + } + + /* FIXME: this isn't correct, since some 486s have cpuid */ + i_capabilities |= CPU_CAPABILITY_586; + + /* borrowed from mpeg2dec */ + b_amd = ( i_ebx == 0x68747541 ) && ( i_ecx == 0x444d4163 ) + && ( i_edx == 0x69746e65 ); + + /* test for the MMX flag */ + //cpuid( 0x00000001 ); + + if( ! (i_edx & 0x00800000) ) + { +# if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW ) \ + && defined( HAVE_SIGNAL_H ) + signal( SIGILL, pf_sigill ); +# endif + return i_capabilities; + } + + i_capabilities |= CPU_CAPABILITY_MMX; + + if( i_edx & 0x02000000 ) + { + i_capabilities |= CPU_CAPABILITY_MMXEXT; + +# ifdef CAN_COMPILE_SSE + /* We test if OS supports the SSE instructions */ + psz_capability = "SSE"; + i_illegal = 0; + + if( setjmp( env ) == 0 ) + { + /* Test a SSE instruction */ + __asm__ __volatile__ ( "xorps %%xmm0,%%xmm0\n" : : ); + } + + if( i_illegal == 0 ) + { + i_capabilities |= CPU_CAPABILITY_SSE; + } +# endif + } + + if( i_edx & 0x04000000 ) + { +# if defined(CAN_COMPILE_SSE) + /* We test if OS supports the SSE instructions */ + psz_capability = "SSE2"; + i_illegal = 0; + + if( setjmp( env ) == 0 ) + { + /* Test a SSE2 instruction */ + __asm__ __volatile__ ( "movupd %%xmm0, %%xmm0\n" : : ); + } + + if( i_illegal == 0 ) + { + i_capabilities |= CPU_CAPABILITY_SSE2; + } +# endif + } + + /* test for additional capabilities */ + //cpuid( 0x80000000 ); + + if( i_eax < 0x80000001 ) + { +# if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW ) \ + && defined( HAVE_SIGNAL_H ) + signal( SIGILL, pf_sigill ); +# endif + return i_capabilities; + } + + /* list these additional capabilities */ + //cpuid( 0x80000001 ); + +# ifdef CAN_COMPILE_3DNOW + if( i_edx & 0x80000000 ) + { + psz_capability = "3D Now!"; + i_illegal = 0; + + if( setjmp( env ) == 0 ) + { + /* Test a 3D Now! instruction */ + __asm__ __volatile__ ( "pfadd %%mm0,%%mm0\n" "femms\n" : : ); + } + + if( i_illegal == 0 ) + { + i_capabilities |= CPU_CAPABILITY_3DNOW; + } + } +# endif + + if( b_amd && ( i_edx & 0x00400000 ) ) + { + i_capabilities |= CPU_CAPABILITY_MMXEXT; + } + +# if defined( CAN_COMPILE_SSE ) || defined ( CAN_COMPILE_3DNOW ) \ + && defined( HAVE_SIGNAL_H ) + signal( SIGILL, pf_sigill ); +# endif + return i_capabilities; + +#elif defined( __powerpc__ ) || defined( __ppc__ ) || defined( __ppc64__ ) + +# ifdef CAN_COMPILE_ALTIVEC && defined( HAVE_SIGNAL_H ) + void (*pf_sigill) (int) = signal( SIGILL, SigHandler ); + + i_capabilities |= CPU_CAPABILITY_FPU; + + i_illegal = 0; + + if( setjmp( env ) == 0 ) + { + asm volatile ("mtspr 256, %0\n\t" + "vand %%v0, %%v0, %%v0" + : + : "r" (-1)); + } + + if( i_illegal == 0 ) + { + i_capabilities |= CPU_CAPABILITY_ALTIVEC; + } + + signal( SIGILL, pf_sigill ); +# else + (void)SigHandler; /* Don't complain about dead code here */ +# endif + + return i_capabilities; + +#elif defined( __sparc__ ) + + i_capabilities |= CPU_CAPABILITY_FPU; + return i_capabilities; + +#elif defined( _MSC_VER ) && !defined( UNDER_CE ) + i_capabilities |= CPU_CAPABILITY_FPU; + return i_capabilities; + +#else + /* default behaviour */ + return i_capabilities; + +#endif +} + +/***************************************************************************** + * SigHandler: system signal handler + ***************************************************************************** + * This function is called when an illegal instruction signal is received by + * the program. We use this function to test OS and CPU capabilities + *****************************************************************************/ +#if defined( HAVE_SIGNAL_H ) +static void SigHandler( int i_signal ) +{ + /* Acknowledge the signal received */ + i_illegal = 1; + +#ifdef HAVE_SIGRELSE + sigrelse( i_signal ); +#else + VLC_UNUSED( i_signal ); +#endif + +#if defined( __i386__ ) + fprintf( stderr, "warning: your CPU has %s instructions, but not your " + "operating system.\n", psz_capability ); + fprintf( stderr, " some optimizations will be disabled unless " + "you upgrade your OS\n" ); +# if defined( __linux__ ) + fprintf( stderr, " (for instance Linux kernel 2.4.x or later)\n" ); +# endif +#endif + + longjmp( env, 1 ); +} +#endif + + +uint32_t cpu_flags = 0; + + +/***************************************************************************** + * vlc_CPU: get pre-computed CPU capability flags + ****************************************************************************/ +unsigned vlc_CPU (void) +{ + return cpu_flags; +} + +static vlc_memcpy_t pf_vlc_memcpy = memcpy; +static vlc_memset_t pf_vlc_memset = memset; + +void vlc_fastmem_register (vlc_memcpy_t cpy, vlc_memset_t set) +{ + if (cpy) + pf_vlc_memcpy = cpy; + if (set) + pf_vlc_memset = set; +} + +/** + * vlc_memcpy: fast CPU-dependent memcpy + */ +void *vlc_memcpy (void *tgt, const void *src, size_t n) +{ + return pf_vlc_memcpy (tgt, src, n); +} + +/** + * vlc_memset: fast CPU-dependent memset + */ +void *vlc_memset (void *tgt, int c, size_t n) +{ + return pf_vlc_memset (tgt, c, n); +} diff --git a/VLC/darwin_specific.c b/VLC/darwin_specific.c new file mode 100644 index 0000000..f6793c6 --- /dev/null +++ b/VLC/darwin_specific.c @@ -0,0 +1,191 @@ +/***************************************************************************** + * darwin_specific.m: Darwin specific features + ***************************************************************************** + * Copyright (C) 2001-2007 the VideoLAN team + * $Id$ + * + * Authors: Sam Hocevar + * Christophe Massiot + * Pierre d'Herbemont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "libvlc.h" +#include /* *dir() */ + +#include + +#ifdef HAVE_LOCALE_H +# include +#endif +#ifdef HAVE_MACH_O_DYLD_H +# include +#endif + +#ifndef MAXPATHLEN +# define MAXPATHLEN 1024 +#endif + +/* CFLocaleCopyAvailableLocaleIdentifiers is present only on post-10.4 */ +extern CFArrayRef CFLocaleCopyAvailableLocaleIdentifiers(void) __attribute__((weak_import)); + +/* emulate CFLocaleCopyAvailableLocaleIdentifiers on pre-10.4 */ +static CFArrayRef copy_all_locale_indentifiers(void) +{ + CFMutableArrayRef available_locales; + DIR * dir; + struct dirent *file; + + dir = opendir( "/usr/share/locale" ); + available_locales = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks ); + + while ( (file = readdir(dir)) ) + { + /* we should probably filter out garbage */ + /* we can't use CFStringCreateWithFileSystemRepresentation as it is + * supported only on post-10.4 (and this function is only for pre-10.4) */ + CFStringRef locale = CFStringCreateWithCString( kCFAllocatorDefault, file->d_name, kCFStringEncodingUTF8 ); + CFArrayAppendValue( available_locales, (void*)locale ); + CFRelease( locale ); + } + + closedir( dir ); + return available_locales; +} + +/***************************************************************************** + * system_Init: fill in program path & retrieve language + *****************************************************************************/ +void system_Init( libvlc_int_t *p_this, int *pi_argc, const char *ppsz_argv[] ) +{ + VLC_UNUSED(p_this); + char i_dummy; + char *p_char = NULL; + char *p_oldchar = &i_dummy; + unsigned int i; + (void)pi_argc; + + /* Get the full program path and name */ + + /* First try to see if we are linked to the framework */ + for (i = 0; i < _dyld_image_count(); i++) + { + const char * psz_img_name = _dyld_get_image_name(i); + /* Check for "VLCKit.framework/Versions/Current/VLCKit", + * as well as "VLCKit.framework/Versions/A/VLCKit" and + * "VLC.framework/Versions/B/VLCKit" */ + if( (p_char = strstr( psz_img_name, "VLCKit.framework/Versions/" )) ) + { + /* Look for the next forward slash */ + p_char += 26; /* p_char += strlen(" VLCKit.framework/Versions/" ) */ + while( *p_char != '\0' && *p_char != '/') + p_char++; + + /* If the string ends with VLC then we've found a winner */ + if ( !strcmp( p_char, "/VLCKit" ) ) + { + p_char = strdup( psz_img_name ); + break; + } + else + p_char = NULL; + } + } + + if( !p_char ) + { + char path[MAXPATHLEN+1]; + uint32_t path_len = MAXPATHLEN; + if ( !_NSGetExecutablePath(path, &path_len) ) + p_char = strdup(path); + } + if( !p_char ) + { + /* We are not linked to the VLC.framework, return the executable path */ + p_char = strdup( ppsz_argv[ 0 ] ); + } + + vlc_global()->psz_vlcpath = p_char; + + /* Remove trailing program name */ + for( ; *p_char ; ) + { + if( *p_char == '/' ) + { + *p_oldchar = '/'; + *p_char = '\0'; + p_oldchar = p_char; + } + + p_char++; + } + + /* Check if $LANG is set. */ + if ( (p_char = getenv("LANG")) == NULL ) + { + /* + Retrieve the preferred language as chosen in System Preferences.app + (note that CFLocaleCopyCurrent() is not used because it returns the + preferred locale not language) + */ + CFArrayRef all_locales, preferred_locales; + char psz_locale[50]; + + if( CFLocaleCopyAvailableLocaleIdentifiers ) + all_locales = CFLocaleCopyAvailableLocaleIdentifiers(); + else + all_locales = copy_all_locale_indentifiers(); + + preferred_locales = CFBundleCopyLocalizationsForPreferences( all_locales, NULL ); + + if ( preferred_locales ) + { + if ( CFArrayGetCount( preferred_locales ) ) + { + CFStringRef user_language_string_ref = CFArrayGetValueAtIndex( preferred_locales, 0 ); + CFStringGetCString( user_language_string_ref, psz_locale, sizeof(psz_locale), kCFStringEncodingUTF8 ); + setenv( "LANG", psz_locale, 1 ); + } + CFRelease( preferred_locales ); + } + CFRelease( all_locales ); + } +} + +/***************************************************************************** + * system_Configure: check for system specific configuration options. + *****************************************************************************/ +void system_Configure( libvlc_int_t *p_this, int *pi_argc, const char *ppsz_argv[] ) +{ + (void)p_this; + (void)pi_argc; + (void)ppsz_argv; +} + +/***************************************************************************** + * system_End: free the program path. + *****************************************************************************/ +void system_End( libvlc_int_t *p_this ) +{ + (void)p_this; + free( vlc_global()->psz_vlcpath ); +} + diff --git a/VLC/dirs.c b/VLC/dirs.c new file mode 100644 index 0000000..ee2101e --- /dev/null +++ b/VLC/dirs.c @@ -0,0 +1,232 @@ +/***************************************************************************** + * dirs.c: directories configuration + ***************************************************************************** + * Copyright (C) 2001-2007 the VideoLAN team + * Copyright © 2007-2008 Rémi Denis-Courmont + * + * Authors: Gildas Bazin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#if defined( WIN32 ) +# define _WIN32_IE IE5 +# include +# include +# include +#else +# include +# include +#endif + +#include "libvlc.h" +#include "configuration.h" +#include "vlc_charset.h" +#include "vlc_configuration.h" + +#include /* errno */ +#include +#include + +#if defined( WIN32 ) +# define DIR_SHARE "" +#else +# define DIR_SHARE "share" +#endif + +/** + * config_GetDataDir: find directory where shared data is installed + * + * @return a string (always succeeds). + */ +const char *config_GetDataDir( void ) +{ +#if defined (WIN32) || defined(__APPLE__) || defined (SYS_BEOS) + static char path[PATH_MAX] = ""; + + if( *path == '\0' ) + { + snprintf( path, sizeof( path ), "%s" DIR_SEP DIR_SHARE, + vlc_global()->psz_vlcpath ); + path[sizeof( path ) - 1] = '\0'; + } + return path; +#else + return DATA_PATH; +#endif +} + +static const char *GetDir( bool b_appdata, bool b_common_appdata ) +{ + /* FIXME: a full memory page here - quite a waste... */ + static char homedir[PATH_MAX] = ""; + +#if defined (WIN32) + wchar_t wdir[MAX_PATH]; + +# if defined (UNDER_CE) + if( SHGetSpecialFolderPath( NULL, wdir, CSIDL_APPDATA, 1 ) ) +# else + /* Get the "Application Data" folder for the current user */ + if( S_OK == SHGetFolderPathW( NULL, + (b_appdata ? CSIDL_APPDATA : + (b_common_appdata ? CSIDL_PERSONAL : CSIDL_COMMON_APPDATA)) + | CSIDL_FLAG_CREATE, + NULL, SHGFP_TYPE_CURRENT, wdir ) ) +# endif + { + static char appdir[PATH_MAX] = ""; + static char comappdir[PATH_MAX] = ""; + WideCharToMultiByte (CP_UTF8, 0, wdir, -1, + b_appdata ? appdir : + (b_common_appdata ? comappdir :homedir), + PATH_MAX, NULL, NULL); + return b_appdata ? appdir : (b_common_appdata ? comappdir :homedir); + } +#else + (void)b_appdata; + (void)b_common_appdata; +#endif + +#ifdef LIBVLC_USE_PTHREAD + static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + pthread_mutex_lock (&lock); +#endif + + if (!*homedir) + { + const char *psz_localhome = getenv( "HOME" ); +#if defined(HAVE_GETPWUID_R) + char buf[sysconf (_SC_GETPW_R_SIZE_MAX)]; + if (psz_localhome == NULL) + { + struct passwd pw, *res; + + if (!getpwuid_r (getuid (), &pw, buf, sizeof (buf), &res) && res) + psz_localhome = pw.pw_dir; + } +#endif + if (psz_localhome == NULL) + psz_localhome = getenv( "TMP" ); + if (psz_localhome == NULL) + psz_localhome = "/tmp"; + + const char *uhomedir = FromLocale (psz_localhome); + strncpy (homedir, uhomedir, sizeof (homedir) - 1); + homedir[sizeof (homedir) - 1] = '\0'; + LocaleFree (uhomedir); + } +#ifdef LIBVLC_USE_PTHREAD + pthread_mutex_unlock (&lock); +#endif + return homedir; +} + +/** + * Determines the system configuration directory. + * + * @return a string (always succeeds). + */ +const char *config_GetConfDir( void ) +{ +#if defined (WIN32) + return GetDir( false, true ); +#elif defined(__APPLE__) || defined (SYS_BEOS) + static char path[PATH_MAX] = ""; + + if( *path == '\0' ) + { + snprintf( path, sizeof( path ), "%s"DIR_SEP DIR_SHARE, /* FIXME: Duh? */ + vlc_global()->psz_vlcpath ); + path[sizeof( path ) - 1] = '\0'; + } + return path; +#else + return SYSCONFDIR; +#endif +} + +/** + * Get the user's home directory + */ +const char *config_GetHomeDir( void ) +{ + return GetDir (false, false); +} + +static char *config_GetFooDir (const char *xdg_name, const char *xdg_default) +{ + char *psz_dir; +#if defined(WIN32) || defined(__APPLE__) || defined(SYS_BEOS) + const char *psz_parent = GetDir (true, false); + + if( asprintf( &psz_dir, "%s" DIR_SEP CONFIG_DIR, psz_parent ) == -1 ) + psz_dir = NULL; + + (void)xdg_name; (void)xdg_default; +#else + char var[sizeof ("XDG__HOME") + strlen (xdg_name)]; + /* XDG Base Directory Specification - Version 0.6 */ + snprintf (var, sizeof (var), "XDG_%s_HOME", xdg_name); + + const char *psz_home = getenv (var); + psz_home = psz_home ? FromLocale (psz_home) : NULL; + if( psz_home ) + { + if( asprintf( &psz_dir, "%s/vlc", psz_home ) == -1 ) + psz_dir = NULL; + LocaleFree (psz_home); + return psz_dir; + } + + /* Try HOME, then fallback to non-XDG dirs */ + psz_home = config_GetHomeDir(); + if( asprintf( &psz_dir, "%s/%s/vlc", psz_home, xdg_default ) == -1 ) + psz_dir = NULL; +#endif + return psz_dir; +} + +/** + * Get the user's VLC configuration directory + */ +char *config_GetUserConfDir( void ) +{ + return config_GetFooDir ("CONFIG", ".config"); +} + +/** + * Get the user's VLC data directory + * (used for stuff like the skins, custom lua modules, ...) + */ +char *config_GetUserDataDir( void ) +{ + return config_GetFooDir ("DATA", ".local/share"); +} + +/** + * Get the user's VLC cache directory + * (used for stuff like the modules cache, the album art cache, ...) + */ +char *config_GetCacheDir( void ) +{ + return config_GetFooDir ("CACHE", ".cache"); +} diff --git a/VLC/engine.c b/VLC/engine.c new file mode 100644 index 0000000..166d5b3 --- /dev/null +++ b/VLC/engine.c @@ -0,0 +1,774 @@ +/***************************************************************************** + * engine.c : Run the playlist and handle its control + ***************************************************************************** + * Copyright (C) 1999-2008 the VideoLAN team + * + * Authors: Samuel Hocevar + * Clément Stenac + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include "vlc_common.h" +#include "vlc_sout.h" +#include "vlc_playlist.h" +#include "vlc_interface.h" +#include "playlist_internal.h" +#include "stream_output/stream_output.h" /* sout_DeleteInstance */ + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +static void VariablesInit( playlist_t *p_playlist ); +static void playlist_Destructor( vlc_object_t * p_this ); +static void playlist_Destructor( vlc_object_t * p_this ); + +static int RandomCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void *a ) +{ + (void)psz_cmd; (void)oldval; (void)newval; (void)a; + + ((playlist_t*)p_this)->b_reset_currently_playing = true; + playlist_Signal( ((playlist_t*)p_this) ); + return VLC_SUCCESS; +} + +/** + * Create playlist + * + * Create a playlist structure. + * \param p_parent the vlc object that is to be the parent of this playlist + * \return a pointer to the created playlist, or NULL on error + */ +playlist_t * playlist_Create( vlc_object_t *p_parent ) +{ + static const char playlist_name[] = "playlist"; + playlist_t *p_playlist; + bool b_save; + + /* Allocate structure */ + p_playlist = vlc_custom_create( p_parent, sizeof( *p_playlist ), + VLC_OBJECT_GENERIC, playlist_name ); + if( !p_playlist ) + return NULL; + + TAB_INIT( p_playlist->i_sds, p_playlist->pp_sds ); + + libvlc_priv(p_parent->p_libvlc)->p_playlist = p_playlist; + + VariablesInit( p_playlist ); + + /* Initialise data structures */ + p_playlist->i_last_playlist_id = 0; + p_playlist->p_input = NULL; + + p_playlist->gc_date = 0; + p_playlist->b_cant_sleep = false; + + ARRAY_INIT( p_playlist->items ); + ARRAY_INIT( p_playlist->all_items ); + ARRAY_INIT( p_playlist->items_to_delete ); + ARRAY_INIT( p_playlist->current ); + + p_playlist->i_current_index = 0; + p_playlist->b_reset_currently_playing = true; + p_playlist->last_rebuild_date = 0; + + p_playlist->b_tree = var_CreateGetBool( p_playlist, "playlist-tree" ); + + p_playlist->b_doing_ml = false; + + p_playlist->b_auto_preparse = + var_CreateGetBool( p_playlist, "auto-preparse" ) ; + + PL_LOCK; /* playlist_NodeCreate will check for it */ + p_playlist->p_root_category = playlist_NodeCreate( p_playlist, NULL, NULL, + 0, NULL ); + p_playlist->p_root_onelevel = playlist_NodeCreate( p_playlist, NULL, NULL, + 0, p_playlist->p_root_category->p_input ); + PL_UNLOCK; + + if( !p_playlist->p_root_category || !p_playlist->p_root_onelevel ) + return NULL; + + /* Create playlist and media library */ + PL_LOCK; /* playlist_NodesPairCreate will check for it */ + playlist_NodesPairCreate( p_playlist, _( "Playlist" ), + &p_playlist->p_local_category, + &p_playlist->p_local_onelevel, false ); + PL_UNLOCK; + + p_playlist->p_local_category->i_flags |= PLAYLIST_RO_FLAG; + p_playlist->p_local_onelevel->i_flags |= PLAYLIST_RO_FLAG; + + if( !p_playlist->p_local_category || !p_playlist->p_local_onelevel || + !p_playlist->p_local_category->p_input || + !p_playlist->p_local_onelevel->p_input ) + return NULL; + + if( config_GetInt( p_playlist, "media-library") ) + { + PL_LOCK; /* playlist_NodesPairCreate will check for it */ + playlist_NodesPairCreate( p_playlist, _( "Media Library" ), + &p_playlist->p_ml_category, + &p_playlist->p_ml_onelevel, false ); + PL_UNLOCK; + + if(!p_playlist->p_ml_category || !p_playlist->p_ml_onelevel) + return NULL; + + p_playlist->p_ml_category->i_flags |= PLAYLIST_RO_FLAG; + p_playlist->p_ml_onelevel->i_flags |= PLAYLIST_RO_FLAG; + } + else + { + p_playlist->p_ml_category = p_playlist->p_ml_onelevel = NULL; + } + + /* Initial status */ + p_playlist->status.p_item = NULL; + p_playlist->status.p_node = p_playlist->p_local_onelevel; + p_playlist->request.b_request = false; + p_playlist->status.i_status = PLAYLIST_STOPPED; + + p_playlist->i_sort = SORT_ID; + p_playlist->i_order = ORDER_NORMAL; + + + b_save = p_playlist->b_auto_preparse; + p_playlist->b_auto_preparse = false; + playlist_MLLoad( p_playlist ); + p_playlist->b_auto_preparse = true; + + vlc_object_set_destructor( p_playlist, playlist_Destructor ); + + return p_playlist; +} + +/** + * Destroy playlist + * + * Destroy a playlist structure. + * \param p_playlist the playlist object + * \return nothing + */ + +static void playlist_Destructor( vlc_object_t * p_this ) +{ + playlist_t * p_playlist = (playlist_t *)p_this; + + if( p_playlist->p_preparse ) + { + vlc_object_release( p_playlist->p_preparse ); + } + + if( p_playlist->p_fetcher ) + { + vlc_object_release( p_playlist->p_fetcher ); + } + msg_Dbg( p_this, "Destroyed" ); +} + +/* Destroy remaining objects */ +static void ObjectGarbageCollector( playlist_t *p_playlist, bool b_force ) +{ + if( !b_force ) + { + if( mdate() - p_playlist->gc_date < 1000000 ) + { + p_playlist->b_cant_sleep = true; + return; + } + else if( p_playlist->gc_date == 0 ) + return; + } + + p_playlist->b_cant_sleep = false; +} + +/* Input Callback */ +static void input_state_changed( const vlc_event_t * event, void * data ) +{ + (void)event; + playlist_t * p_playlist = data; + playlist_Signal( p_playlist ); +} + +/* Input Callback */ +static void input_selected_stream_changed( const vlc_event_t * event, void * data ) +{ + (void)event; + playlist_t * p_playlist = data; + PL_LOCK; + p_playlist->gc_date = mdate(); + vlc_object_signal_unlocked( p_playlist ); + PL_UNLOCK; +} + +/* Internals */ +void playlist_release_current_input( playlist_t * p_playlist ) +{ + PL_ASSERT_LOCKED; + + if( !p_playlist->p_input ) return; + + input_thread_t * p_input = p_playlist->p_input; + vlc_event_manager_t * p_em = input_get_event_manager( p_input ); + + vlc_event_detach( p_em, vlc_InputStateChanged, + input_state_changed, p_playlist ); + vlc_event_detach( p_em, vlc_InputSelectedStreamChanged, + input_selected_stream_changed, p_playlist ); + p_playlist->p_input = NULL; + + /* Release the playlist lock, because we may get stuck + * in vlc_object_release() for some time. */ + PL_UNLOCK; + vlc_thread_join( p_input ); + vlc_object_release( p_input ); + PL_LOCK; +} + +void playlist_set_current_input( + playlist_t * p_playlist, input_thread_t * p_input ) +{ + PL_ASSERT_LOCKED; + + playlist_release_current_input( p_playlist ); + + if( p_input ) + { + vlc_object_yield( p_input ); + p_playlist->p_input = p_input; + vlc_event_manager_t * p_em = input_get_event_manager( p_input ); + vlc_event_attach( p_em, vlc_InputStateChanged, + input_state_changed, p_playlist ); + vlc_event_attach( p_em, vlc_InputSelectedStreamChanged, + input_selected_stream_changed, p_playlist ); + } +} + +/** Get current playing input. + */ +input_thread_t * playlist_CurrentInput( playlist_t * p_playlist ) +{ + input_thread_t * p_input; + PL_LOCK; + p_input = p_playlist->p_input; + if( p_input ) vlc_object_yield( p_input ); + PL_UNLOCK; + return p_input; +} + +/** + * @} + */ + +/** Accessor for status item and status nodes. + */ +playlist_item_t * get_current_status_item( playlist_t * p_playlist ) +{ + PL_ASSERT_LOCKED; + + return p_playlist->status.p_item; +} + +playlist_item_t * get_current_status_node( playlist_t * p_playlist ) +{ + PL_ASSERT_LOCKED; + + return p_playlist->status.p_node; +} + +void set_current_status_item( playlist_t * p_playlist, + playlist_item_t * p_item ) +{ + PL_ASSERT_LOCKED; + + if( p_playlist->status.p_item && + p_playlist->status.p_item->i_flags & PLAYLIST_REMOVE_FLAG && + p_playlist->status.p_item != p_item ) + { + /* It's unsafe given current design to delete a playlist item :( + playlist_ItemDelete( p_playlist->status.p_item ); */ + } + p_playlist->status.p_item = p_item; +} + +void set_current_status_node( playlist_t * p_playlist, + playlist_item_t * p_node ) +{ + PL_ASSERT_LOCKED; + + if( p_playlist->status.p_node && + p_playlist->status.p_node->i_flags & PLAYLIST_REMOVE_FLAG && + p_playlist->status.p_node != p_node ) + { + /* It's unsafe given current design to delete a playlist item :( + playlist_ItemDelete( p_playlist->status.p_node ); */ + } + p_playlist->status.p_node = p_node; +} + +/** + * Main loop + * + * Main loop for the playlist. It should be entered with the + * playlist lock (otherwise input event may be lost) + * \param p_playlist the playlist object + * \return nothing + */ +void playlist_MainLoop( playlist_t *p_playlist ) +{ + playlist_item_t *p_item = NULL; + bool b_playexit = var_GetBool( p_playlist, "play-and-exit" ); + + PL_ASSERT_LOCKED; + + if( p_playlist->b_reset_currently_playing && + mdate() - p_playlist->last_rebuild_date > 30000 ) // 30 ms + { + ResetCurrentlyPlaying( p_playlist, var_GetBool( p_playlist, "random" ), + get_current_status_item( p_playlist ) ); + p_playlist->last_rebuild_date = mdate(); + } + +check_input: + /* If there is an input, check that it doesn't need to die. */ + if( p_playlist->p_input ) + { + if( p_playlist->request.b_request && !p_playlist->p_input->b_die ) + { + PL_DEBUG( "incoming request - stopping current input" ); + input_StopThread( p_playlist->p_input ); + } + + /* This input is dead. Remove it ! */ + if( p_playlist->p_input->b_dead ) + { + int i_activity; + input_thread_t *p_input; + sout_instance_t **pp_sout = + &libvlc_priv(p_playlist->p_libvlc)->p_sout; + + PL_DEBUG( "dead input" ); + + p_input = p_playlist->p_input; + + assert( *pp_sout == NULL ); + if( var_CreateGetBool( p_input, "sout-keep" ) ) + *pp_sout = input_DetachSout( p_input ); + + /* Destroy input */ + playlist_release_current_input( p_playlist ); + + p_playlist->gc_date = mdate(); + p_playlist->b_cant_sleep = true; + + i_activity= var_GetInteger( p_playlist, "activity" ); + var_SetInteger( p_playlist, "activity", i_activity - + DEFAULT_INPUT_ACTIVITY ); + + goto check_input; + } + /* This input is dying, let it do */ + else if( p_playlist->p_input->b_die ) + { + PL_DEBUG( "dying input" ); + PL_UNLOCK; + msleep( INTF_IDLE_SLEEP ); + PL_LOCK; + goto check_input; + } + /* This input has finished, ask it to die ! */ + else if( p_playlist->p_input->b_error + || p_playlist->p_input->b_eof ) + { + PL_DEBUG( "finished input" ); + input_StopThread( p_playlist->p_input ); + /* No need to wait here, we'll wait in the p_input->b_die case */ + goto check_input; + } + else if( p_playlist->p_input->i_state != INIT_S ) + { + ObjectGarbageCollector( p_playlist, false ); + } + } + else + { + /* No input. Several cases + * - No request, running status -> start new item + * - No request, stopped status -> collect garbage + * - Request, running requested -> start new item + * - Request, stopped requested -> collect garbage + */ + int i_status = p_playlist->request.b_request ? + p_playlist->request.i_status : p_playlist->status.i_status; + if( i_status != PLAYLIST_STOPPED ) + { + msg_Dbg( p_playlist, "starting new item" ); + p_item = playlist_NextItem( p_playlist ); + + if( p_item == NULL ) + { + msg_Dbg( p_playlist, "nothing to play" ); + p_playlist->status.i_status = PLAYLIST_STOPPED; + + if( b_playexit == true ) + { + msg_Info( p_playlist, "end of playlist, exiting" ); + vlc_object_kill( p_playlist->p_libvlc ); + } + ObjectGarbageCollector( p_playlist, true ); + return; + } + playlist_PlayItem( p_playlist, p_item ); + /* playlist_PlayItem loose input event, we need to recheck */ + goto check_input; + } + else + { + const bool b_gc_forced = p_playlist->status.i_status != PLAYLIST_STOPPED; + + p_playlist->status.i_status = PLAYLIST_STOPPED; + + /* Collect garbage */ + ObjectGarbageCollector( p_playlist, b_gc_forced ); + } + } +} + +/** + * Last loop + * + * The playlist is dying so do the last loop + * \param p_playlist the playlist object + * \return nothing +*/ +void playlist_LastLoop( playlist_t *p_playlist ) +{ + /* If there is an input, kill it */ + while( 1 ) + { + PL_LOCK; + if( p_playlist->p_input == NULL ) + { + PL_UNLOCK; + break; + } + + if( p_playlist->p_input->b_dead ) + { + /* remove input */ + playlist_release_current_input( p_playlist ); + + /* sout-keep: no need to anything here. + * The last input will destroy its sout, if any, by itself */ + + PL_UNLOCK; + continue; + } + else if( p_playlist->p_input->b_die ) + { + /* This input is dying, leave it alone */ + ; + } + else if( p_playlist->p_input->b_error || p_playlist->p_input->b_eof ) + { + input_StopThread( p_playlist->p_input ); + PL_UNLOCK; + continue; + } + else + { + p_playlist->p_input->b_eof = 1; + } + PL_UNLOCK; + + msleep( INTF_IDLE_SLEEP ); + } + +#ifdef ENABLE_SOUT + /* close the remaining sout-keep (if there was no input atm) */ + sout_instance_t *p_sout = libvlc_priv (p_playlist->p_libvlc)->p_sout; + if (p_sout) + sout_DeleteInstance( p_sout ); +#endif + + /* Core should have terminated all SDs before the playlist */ + /* TODO: It fails to do so when not playing anything -- Courmisch */ + playlist_ServicesDiscoveryKillAll( p_playlist ); + playlist_MLDump( p_playlist ); + + vlc_object_kill( p_playlist->p_preparse ); + vlc_thread_join( p_playlist->p_preparse ); + vlc_object_kill( p_playlist->p_fetcher ); + vlc_thread_join( p_playlist->p_fetcher ); + + PL_LOCK; + + /* Release the current node */ + set_current_status_node( p_playlist, NULL ); + + /* Release the current item */ + set_current_status_item( p_playlist, NULL ); + + FOREACH_ARRAY( playlist_item_t *p_del, p_playlist->all_items ) + free( p_del->pp_children ); + vlc_gc_decref( p_del->p_input ); + free( p_del ); + FOREACH_END(); + ARRAY_RESET( p_playlist->all_items ); + FOREACH_ARRAY( playlist_item_t *p_del, p_playlist->items_to_delete ) + free( p_del->pp_children ); + vlc_gc_decref( p_del->p_input ); + free( p_del ); + FOREACH_END(); + ARRAY_RESET( p_playlist->items_to_delete ); + + ARRAY_RESET( p_playlist->items ); + ARRAY_RESET( p_playlist->current ); + + PL_UNLOCK; +} + +/** + * Preparse loop + * + * Main loop for preparser queue + * \param p_obj items to preparse + * \return nothing + */ +void playlist_PreparseLoop( playlist_preparse_t *p_obj ) +{ + playlist_t *p_playlist = (playlist_t *)p_obj->p_parent; + input_item_t *p_current; + int i_activity; + + vlc_object_lock( p_obj ); + + while( vlc_object_alive( p_obj ) ) + { + if( p_obj->i_waiting == 0 ) + { + vlc_object_wait( p_obj ); + continue; + } + + p_current = p_obj->pp_waiting[0]; + REMOVE_ELEM( p_obj->pp_waiting, p_obj->i_waiting, 0 ); + vlc_object_unlock( p_obj ); + + PL_LOCK; + if( p_current ) + { + if( p_current->i_type == ITEM_TYPE_FILE ) + { + stats_TimerStart( p_playlist, "Preparse run", + STATS_TIMER_PREPARSE ); + /* Do not preparse if it is already done (like by playing it) */ + if( !input_item_IsPreparsed( p_current ) ) + { + PL_UNLOCK; + input_Preparse( p_playlist, p_current ); + PL_LOCK; + } + stats_TimerStop( p_playlist, STATS_TIMER_PREPARSE ); + PL_UNLOCK; + input_item_SetPreparsed( p_current, true ); + var_SetInteger( p_playlist, "item-change", p_current->i_id ); + PL_LOCK; + } + /* If we haven't retrieved enough meta, add to secondary queue + * which will run the "meta fetchers". + * This only checks for meta, not for art + * \todo don't do this for things we won't get meta for, like vids + */ + char *psz_arturl = input_item_GetArtURL( p_current ); + char *psz_name = input_item_GetName( p_current ); + if( p_playlist->p_fetcher->i_art_policy == ALBUM_ART_ALL && + ( !psz_arturl || strncmp( psz_arturl, "file://", 7 ) ) ) + { + PL_DEBUG("meta ok for %s, need to fetch art", psz_name ); + vlc_object_lock( p_playlist->p_fetcher ); + if( vlc_object_alive( p_playlist->p_fetcher ) ) + { + INSERT_ELEM( p_playlist->p_fetcher->pp_waiting, + p_playlist->p_fetcher->i_waiting, + p_playlist->p_fetcher->i_waiting, p_current); + vlc_object_signal_unlocked( p_playlist->p_fetcher ); + } + else + vlc_gc_decref( p_current ); + vlc_object_unlock( p_playlist->p_fetcher ); + } + else + { + PL_DEBUG( "no fetch required for %s (art currently %s)", + psz_name, psz_arturl ); + vlc_gc_decref( p_current ); + } + free( psz_name ); + free( psz_arturl ); + PL_UNLOCK; + } + else + PL_UNLOCK; + + vlc_object_lock( p_obj ); + i_activity = var_GetInteger( p_playlist, "activity" ); + if( i_activity < 0 ) i_activity = 0; + vlc_object_unlock( p_obj ); + /* Sleep at least 1ms */ + msleep( (i_activity+1) * 1000 ); + vlc_object_lock( p_obj ); + } + + while( p_obj->i_waiting > 0 ) + { + vlc_gc_decref( p_obj->pp_waiting[0] ); + REMOVE_ELEM( p_obj->pp_waiting, p_obj->i_waiting, 0 ); + } + + vlc_object_unlock( p_obj ); +} + +/** + * Fetcher loop + * + * Main loop for secondary preparser queue + * \param p_obj items to preparse + * \return nothing + */ +void playlist_FetcherLoop( playlist_fetcher_t *p_obj ) +{ + playlist_t *p_playlist = (playlist_t *)p_obj->p_parent; + input_item_t *p_item; + int i_activity; + + vlc_object_lock( p_obj ); + + while( vlc_object_alive( p_obj ) ) + { + if( p_obj->i_waiting == 0 ) + { + vlc_object_wait( p_obj ); + continue; + } + + p_item = p_obj->pp_waiting[0]; + REMOVE_ELEM( p_obj->pp_waiting, p_obj->i_waiting, 0 ); + vlc_object_unlock( p_obj ); + if( p_item ) + { + int i_ret; + + /* Check if it is not yet preparsed and if so wait for it (at most 0.5s) + * (This can happen if we fetch art on play) + * FIXME this doesn't work if we need to fetch meta before art ... */ + for( i_ret = 0; i_ret < 10 && !input_item_IsPreparsed( p_item ); i_ret++ ) + { + bool b_break; + PL_LOCK; + b_break = ( !p_playlist->p_input || input_GetItem(p_playlist->p_input) != p_item || + p_playlist->p_input->b_die || p_playlist->p_input->b_eof || p_playlist->p_input->b_error ); + PL_UNLOCK; + if( b_break ) + break; + msleep( 50000 ); + } + + i_ret = input_ArtFind( p_playlist, p_item ); + if( i_ret == 1 ) + { + PL_DEBUG( "downloading art for %s", p_item->psz_name ); + if( input_DownloadAndCacheArt( p_playlist, p_item ) ) + input_item_SetArtNotFound( p_item, true ); + else { + input_item_SetArtFetched( p_item, true ); + var_SetInteger( p_playlist, "item-change", + p_item->i_id ); + } + } + else if( i_ret == 0 ) /* Was in cache */ + { + PL_DEBUG( "found art for %s in cache", p_item->psz_name ); + input_item_SetArtFetched( p_item, true ); + var_SetInteger( p_playlist, "item-change", p_item->i_id ); + } + else + { + PL_DEBUG( "art not found for %s", p_item->psz_name ); + input_item_SetArtNotFound( p_item, true ); + } + vlc_gc_decref( p_item ); + } + vlc_object_lock( p_obj ); + i_activity = var_GetInteger( p_playlist, "activity" ); + if( i_activity < 0 ) i_activity = 0; + vlc_object_unlock( p_obj ); + /* Sleep at least 1ms */ + msleep( (i_activity+1) * 1000 ); + vlc_object_lock( p_obj ); + } + + while( p_obj->i_waiting > 0 ) + { + vlc_gc_decref( p_obj->pp_waiting[0] ); + REMOVE_ELEM( p_obj->pp_waiting, p_obj->i_waiting, 0 ); + } + + vlc_object_unlock( p_obj ); +} + +static void VariablesInit( playlist_t *p_playlist ) +{ + vlc_value_t val; + /* These variables control updates */ + var_Create( p_playlist, "intf-change", VLC_VAR_BOOL ); + val.b_bool = true; + var_Set( p_playlist, "intf-change", val ); + + var_Create( p_playlist, "item-change", VLC_VAR_INTEGER ); + val.i_int = -1; + var_Set( p_playlist, "item-change", val ); + + var_Create( p_playlist, "item-deleted", VLC_VAR_INTEGER ); + val.i_int = -1; + var_Set( p_playlist, "item-deleted", val ); + + var_Create( p_playlist, "item-append", VLC_VAR_ADDRESS ); + + var_Create( p_playlist, "playlist-current", VLC_VAR_INTEGER ); + val.i_int = -1; + var_Set( p_playlist, "playlist-current", val ); + + var_Create( p_playlist, "activity", VLC_VAR_INTEGER ); + var_SetInteger( p_playlist, "activity", 0 ); + + /* Variables to control playback */ + var_CreateGetBool( p_playlist, "play-and-stop" ); + var_CreateGetBool( p_playlist, "play-and-exit" ); + var_CreateGetBool( p_playlist, "random" ); + var_CreateGetBool( p_playlist, "repeat" ); + var_CreateGetBool( p_playlist, "loop" ); + + var_AddCallback( p_playlist, "random", RandomCallback, NULL ); +} diff --git a/VLC/entry.c b/VLC/entry.c new file mode 100644 index 0000000..8fada6f --- /dev/null +++ b/VLC/entry.c @@ -0,0 +1,528 @@ +/***************************************************************************** + * entry.c : Callbacks for module entry point + ***************************************************************************** + * Copyright (C) 2007 the VideoLAN team + * Copyright © 2007-2008 Rémi Denis-Courmont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "vlc_plugin.h" +#include +#include + +#include "modules.h" +#include "configuration.h" +#include "libvlc.h" +#ifndef ENABLE_NLS +# define dgettext(d, m) ((char *)(m)) +#endif + +static const char default_name[] = "unnamed"; + +module_t *vlc_module_create (vlc_object_t *obj) +{ + module_t *module = + (module_t *)vlc_custom_create (obj, sizeof (module_t), + VLC_OBJECT_MODULE, "module"); + if (module == NULL) + return NULL; + + module->b_reentrant = module->b_unloadable = true; + module->psz_object_name = strdup( default_name ); + module->psz_longname = (char*)default_name; + module->psz_capability = (char*)""; + module->i_score = 1; + module->i_config_items = module->i_bool_items = 0; + + return module; +} + + +module_t *vlc_submodule_create (module_t *module) +{ + assert (module != NULL); + assert (!module->b_submodule); // subsubmodules are not supported + + module_t *submodule = + (module_t *)vlc_custom_create (VLC_OBJECT (module), sizeof (module_t), + VLC_OBJECT_MODULE, "submodule"); + if (submodule == NULL) + return NULL; + + vlc_object_attach (submodule, module); + submodule->b_submodule = true; + + /* Muahahaha! Heritage! Polymorphism! Ugliness!! */ + memcpy (submodule->pp_shortcuts, module->pp_shortcuts, + sizeof (submodule->pp_shortcuts)); + + submodule->psz_object_name = strdup( module->psz_object_name ); + submodule->psz_shortname = module->psz_shortname; + submodule->psz_longname = module->psz_longname; + submodule->psz_capability = module->psz_capability; + submodule->i_score = module->i_score; + submodule->i_cpu = module->i_cpu; + return submodule; +} + + +int vlc_module_set (module_t *module, int propid, ...) +{ + va_list ap; + int ret = VLC_SUCCESS; + + va_start (ap, propid); + switch (propid) + { + case VLC_MODULE_CPU_REQUIREMENT: + assert (!module->b_submodule); + module->i_cpu |= va_arg (ap, int); + break; + + case VLC_MODULE_SHORTCUT: + { + unsigned i; + for (i = 0; module->pp_shortcuts[i] != NULL; i++); + if (i >= (MODULE_SHORTCUT_MAX - 1)) + { + ret = VLC_ENOMEM; + break; + } + + module->pp_shortcuts[i] = va_arg (ap, char *); + break; + } + + case VLC_MODULE_SHORTNAME_NODOMAIN: + { + const char *name = va_arg (ap, char *); + ret = vlc_module_set (module, VLC_MODULE_SHORTNAME, NULL, name); + break; + } + + case VLC_MODULE_DESCRIPTION_NODOMAIN: + { + const char *desc = va_arg (ap, char *); + ret = vlc_module_set (module, VLC_MODULE_DESCRIPTION, NULL, desc); + break; + } + + case VLC_MODULE_HELP_NODOMAIN: + { + const char *help = va_arg (ap, char *); + ret = vlc_module_set (module, VLC_MODULE_HELP, NULL, help); + break; + } + + case VLC_MODULE_CAPABILITY: + module->psz_capability = va_arg (ap, char *); + break; + + case VLC_MODULE_SCORE: + module->i_score = va_arg (ap, int); + break; + + case VLC_MODULE_PROGRAM: + msg_Warn (module, "deprecated module property %d", propid); + break; + + case VLC_MODULE_CB_OPEN: + module->pf_activate = va_arg (ap, int (*) (vlc_object_t *)); + break; + + case VLC_MODULE_CB_CLOSE: + module->pf_deactivate = va_arg (ap, void (*) (vlc_object_t *)); + break; + + case VLC_MODULE_NO_UNLOAD: + module->b_unloadable = false; + break; + + case VLC_MODULE_NAME: + { + const char *value = va_arg (ap, const char *); + free( module->psz_object_name ); + module->psz_object_name = strdup( value ); + module->pp_shortcuts[0] = (char*)value; /* dooh! */ + if (module->psz_longname == default_name) + module->psz_longname = (char*)value; /* dooh! */ + break; + } + + case VLC_MODULE_SHORTNAME: + { + const char *domain = va_arg (ap, const char *); + if (domain == NULL) + domain = PACKAGE; + module->psz_shortname = dgettext (domain, va_arg (ap, char *)); + break; + } + + case VLC_MODULE_DESCRIPTION: + { + const char *domain = va_arg (ap, const char *); + if (domain == NULL) + domain = PACKAGE; + module->psz_longname = dgettext (domain, va_arg (ap, char *)); + break; + } + + case VLC_MODULE_HELP: + { + const char *domain = va_arg (ap, const char *); + if (domain == NULL) + domain = PACKAGE; + module->psz_help = dgettext (domain, va_arg (ap, char *)); + break; + } + + default: + msg_Err (module, "unknown module property %d", propid); + msg_Err (module, "LibVLC might be too old to use this module."); + ret = VLC_EGENERIC; + break; + } + va_end (ap); + return ret; +} + +module_config_t *vlc_config_create (module_t *module, int type) +{ + unsigned confsize = module->confsize; + module_config_t *tab = module->p_config; + + if ((confsize & 0xf) == 0) + { + tab = realloc (tab, (confsize + 17) * sizeof (*tab)); + if (tab == NULL) + return NULL; + + module->p_config = tab; + } + + memset (tab + confsize, 0, sizeof (tab[confsize])); + tab[confsize].i_type = type; + tab[confsize].p_lock = &(vlc_internals(module)->lock); + + if (type & CONFIG_ITEM) + { + module->i_config_items++; + if (type == CONFIG_ITEM_BOOL) + module->i_bool_items++; + } + + module->confsize++; + return tab + confsize; +} + +int vlc_config_set (module_config_t *restrict item, int id, ...) +{ + int ret = -1; + va_list ap; + + assert (item != NULL); + va_start (ap, id); + + switch (id) + { + case VLC_CONFIG_NAME: + { + const char *name = va_arg (ap, const char *); + vlc_callback_t cb = va_arg (ap, vlc_callback_t); + + assert (name != NULL); + item->psz_name = strdup (name); + item->pf_callback = cb; + ret = 0; + break; + } + + case VLC_CONFIG_DESC_NODOMAIN: + { + const char *text = va_arg (ap, const char *); + const char *longtext = va_arg (ap, const char *); + ret = vlc_config_set (item, VLC_CONFIG_DESC, NULL, text, longtext); + break; + } + + case VLC_CONFIG_VALUE: + { + if (IsConfigIntegerType (item->i_type)) + { + item->orig.i = item->saved.i = + item->value.i = va_arg (ap, int); + ret = 0; + } + else + if (IsConfigFloatType (item->i_type)) + { + item->orig.f = item->saved.f = + item->value.f = va_arg (ap, double); + ret = 0; + } + else + if (IsConfigStringType (item->i_type)) + { + const char *value = va_arg (ap, const char *); + item->value.psz = value ? strdup (value) : NULL; + item->orig.psz = value ? strdup (value) : NULL; + item->saved.psz = value ? strdup (value) : NULL; + ret = 0; + } + break; + } + + case VLC_CONFIG_RANGE: + { + if (IsConfigIntegerType (item->i_type)) + { + item->min.i = va_arg (ap, int); + item->max.i = va_arg (ap, int); + ret = 0; + } + else + if (IsConfigFloatType (item->i_type)) + { + item->min.f = va_arg (ap, double); + item->max.f = va_arg (ap, double); + ret = 0; + } + break; + } + + case VLC_CONFIG_ADVANCED: + item->b_advanced = true; + ret = 0; + break; + + case VLC_CONFIG_VOLATILE: + item->b_unsaveable = true; + ret = 0; + break; + + case VLC_CONFIG_PERSISTENT: + item->b_autosave = true; + ret = 0; + break; + + case VLC_CONFIG_RESTART: + item->b_restart = true; + ret = 0; + break; + + case VLC_CONFIG_PRIVATE: + item->b_internal = true; + ret = 0; + break; + + case VLC_CONFIG_REMOVED: + item->b_removed = true; + ret = 0; + break; + + case VLC_CONFIG_CAPABILITY: + { + const char *cap = va_arg (ap, const char *); + item->psz_type = cap ? strdup (cap) : NULL; + ret = 0; + break; + } + + case VLC_CONFIG_SHORTCUT: + item->i_short = va_arg (ap, int); + ret = 0; + break; + + case VLC_CONFIG_LIST_NODOMAIN: + { + size_t len = va_arg (ap, size_t); + if (IsConfigIntegerType (item->i_type)) + { + const int *src = va_arg (ap, const int *); + const char *const *text = va_arg (ap, const char *const *); + ret = vlc_config_set (item, VLC_CONFIG_LIST, NULL, len, src, + text); + } + else + if (IsConfigStringType (item->i_type)) + { + const char *const *src = va_arg (ap, const char *const *); + const char *const *text = va_arg (ap, const char *const *); + ret = vlc_config_set (item, VLC_CONFIG_LIST, NULL, len, src, + text); + } + break; + } + + case VLC_CONFIG_ADD_ACTION_NODOMAIN: + { + vlc_callback_t cb = va_arg (ap, vlc_callback_t); + const char *name = va_arg (ap, const char *); + ret = vlc_config_set (item, VLC_CONFIG_ADD_ACTION, NULL, cb, name); + break; + } + + case VLC_CONFIG_OLDNAME: + { + const char *oldname = va_arg (ap, const char *); + item->psz_oldname = oldname ? strdup (oldname) : NULL; + ret = 0; + break; + } + + case VLC_CONFIG_SAFE: + item->b_safe = true; + ret = 0; + break; + + case VLC_CONFIG_DESC: + { + const char *domain = va_arg (ap, const char *); + const char *text = va_arg (ap, const char *); + const char *longtext = va_arg (ap, const char *); + + if (domain == NULL) + domain = PACKAGE; + item->psz_text = text ? strdup (dgettext (domain, text)) : NULL; + item->psz_longtext = + longtext ? strdup (dgettext (domain, longtext)) : NULL; + ret = 0; + break; + } + + case VLC_CONFIG_LIST: + { + const char *domain = va_arg (ap, const char *); + size_t len = va_arg (ap, size_t); + char **dtext = malloc (sizeof (char *) * (len + 1)); + + if (dtext == NULL) + break; + + /* Copy values */ + if (IsConfigIntegerType (item->i_type)) + { + const int *src = va_arg (ap, const int *); + int *dst = malloc (sizeof (int) * (len + 1)); + + if (dst != NULL) + { + memcpy (dst, src, sizeof (int) * len); + dst[len] = 0; + } + item->pi_list = dst; + } + else +#if 0 + if (IsConfigFloatType (item->i_type)) + { + const float *src = va_arg (ap, const float *); + float *dst = malloc (sizeof (float) * (len + 1)); + + if (dst != NULL) + { + memcpy (dst, src, sizeof (float) * len); + dst[len] = 0.; + } + item->pf_list = dst; + } + else +#endif + if (IsConfigStringType (item->i_type)) + { + const char *const *src = va_arg (ap, const char *const *); + char **dst = malloc (sizeof (char *) * (len + 1)); + + if (dst != NULL) + { + for (size_t i = 0; i < len; i++) + dst[i] = src[i] ? strdup (src[i]) : NULL; + dst[len] = NULL; + } + item->ppsz_list = dst; + } + else + break; + + /* Copy textual descriptions */ + if (domain == NULL) + domain = PACKAGE; + + const char *const *text = va_arg (ap, const char *const *); + if (text != NULL) + { + for (size_t i = 0; i < len; i++) + dtext[i] = + text[i] ? strdup (dgettext (domain, text[i])) : NULL; + + dtext[len] = NULL; + item->ppsz_list_text = dtext; + } + else + { + free (dtext); + item->ppsz_list_text = NULL; + } + + item->i_list = len; + item->pf_update_list = va_arg (ap, vlc_callback_t); + ret = 0; + break; + } + + case VLC_CONFIG_ADD_ACTION: + { + const char *domain = va_arg (ap, const char *); + vlc_callback_t cb = va_arg (ap, vlc_callback_t), *tabcb; + const char *name = va_arg (ap, const char *); + char **tabtext; + + tabcb = realloc (item->ppf_action, + (item->i_action + 2) * sizeof (cb)); + if (tabcb == NULL) + break; + item->ppf_action = tabcb; + tabcb[item->i_action] = cb; + tabcb[item->i_action + 1] = NULL; + + tabtext = realloc (item->ppsz_action_text, + (item->i_action + 2) * sizeof (name)); + if (tabtext == NULL) + break; + item->ppsz_action_text = tabtext; + + if (domain == NULL) + domain = PACKAGE; + if (name) + tabtext[item->i_action] = strdup (dgettext (domain, name)); + else + tabtext[item->i_action] = NULL; + tabtext[item->i_action + 1] = NULL; + + item->i_action++; + ret = 0; + break; + } + } + + va_end (ap); + return ret; +} diff --git a/VLC/error.c b/VLC/error.c new file mode 100644 index 0000000..2796796 --- /dev/null +++ b/VLC/error.c @@ -0,0 +1,72 @@ +/***************************************************************************** + * error.c: error handling routine + ***************************************************************************** + * Copyright (C) 2002-2004 the VideoLAN team + * $Id$ + * + * Authors: Samuel Hocevar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +/***************************************************************************** + * vlc_error: strerror() equivalent + ***************************************************************************** + * This function returns a string describing the error code passed in the + * argument. A list of all errors can be found in include/vlc_common.h. + *****************************************************************************/ +char const * vlc_error ( int i_err ) +{ + switch( i_err ) + { + case VLC_SUCCESS: + return "no error"; + + case VLC_ENOMEM: + return "not enough memory"; + case VLC_ETHREAD: + return "thread error"; + case VLC_ETIMEOUT: + return "timeout"; + + case VLC_ENOMOD: + return "module not found"; + + case VLC_ENOOBJ: + return "object not found"; + + case VLC_ENOVAR: + return "variable not found"; + case VLC_EBADVAR: + return "bad variable value"; + + case VLC_EEXIT: + return "program exited"; + case VLC_EGENERIC: + return "generic error"; + default: + return "unknown error"; + } +} + diff --git a/VLC/es_format.c b/VLC/es_format.c new file mode 100644 index 0000000..d53eddb --- /dev/null +++ b/VLC/es_format.c @@ -0,0 +1,228 @@ +/***************************************************************************** + * es_format.c : es_format_t helpers. + ***************************************************************************** + * Copyright (C) 2008 the VideoLAN team + * $Id$ + * + * Author: Laurent Aimar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "vlc_es.h" + + +/***************************************************************************** + * BinaryLog: computes the base 2 log of a binary value + ***************************************************************************** + * This functions is used by MaskToShift, to get a bit index from a binary + * value. + *****************************************************************************/ +static int BinaryLog( uint32_t i ) +{ + int i_log = 0; + + if( i == 0 ) return -31337; + + if( i & 0xffff0000 ) i_log += 16; + if( i & 0xff00ff00 ) i_log += 8; + if( i & 0xf0f0f0f0 ) i_log += 4; + if( i & 0xcccccccc ) i_log += 2; + if( i & 0xaaaaaaaa ) i_log += 1; + + return i_log; +} + +/** + * It transforms a color mask into right and left shifts + * FIXME copied from video_output.c + */ +static void MaskToShift( int *pi_left, int *pi_right, uint32_t i_mask ) +{ + uint32_t i_low, i_high; /* lower hand higher bits of the mask */ + + if( !i_mask ) + { + *pi_left = *pi_right = 0; + return; + } + + /* Get bits */ + i_low = i_high = i_mask; + + i_low &= - (int32_t)i_low; /* lower bit of the mask */ + i_high += i_low; /* higher bit of the mask */ + + /* Transform bits into an index. Also deal with i_high overflow, which + * is faster than changing the BinaryLog code to handle 64 bit integers. */ + i_low = BinaryLog (i_low); + i_high = i_high ? BinaryLog (i_high) : 32; + + /* Update pointers and return */ + *pi_left = i_low; + *pi_right = (8 - i_high + i_low); +} + +/* */ +void video_format_FixRgb( video_format_t *p_fmt ) +{ + /* FIXME find right default mask */ + if( !p_fmt->i_rmask || !p_fmt->i_gmask || !p_fmt->i_bmask ) + { + switch( p_fmt->i_chroma ) + { + case VLC_FOURCC('R','V','1','5'): + p_fmt->i_rmask = 0x7c00; + p_fmt->i_gmask = 0x03e0; + p_fmt->i_bmask = 0x001f; + break; + + case VLC_FOURCC('R','V','1','6'): + p_fmt->i_rmask = 0xf800; + p_fmt->i_gmask = 0x07e0; + p_fmt->i_bmask = 0x001f; + break; + + case VLC_FOURCC('R','V','2','4'): + p_fmt->i_rmask = 0xff0000; + p_fmt->i_gmask = 0x00ff00; + p_fmt->i_bmask = 0x0000ff; + break; + case VLC_FOURCC('R','V','3','2'): + p_fmt->i_rmask = 0x00ff0000; + p_fmt->i_gmask = 0x0000ff00; + p_fmt->i_bmask = 0x000000ff; + break; + + default: + return; + } + } + + MaskToShift( &p_fmt->i_lrshift, &p_fmt->i_rrshift, + p_fmt->i_rmask ); + MaskToShift( &p_fmt->i_lgshift, &p_fmt->i_rgshift, + p_fmt->i_gmask ); + MaskToShift( &p_fmt->i_lbshift, &p_fmt->i_rbshift, + p_fmt->i_bmask ); +} + +void es_format_Init( es_format_t *fmt, + int i_cat, vlc_fourcc_t i_codec ) +{ + fmt->i_cat = i_cat; + fmt->i_codec = i_codec; + fmt->i_id = -1; + fmt->i_group = 0; + fmt->i_priority = 0; + fmt->psz_language = NULL; + fmt->psz_description = NULL; + + fmt->i_extra_languages = 0; + fmt->p_extra_languages = NULL; + + memset( &fmt->audio, 0, sizeof(audio_format_t) ); + memset( &fmt->audio_replay_gain, 0, sizeof(audio_replay_gain_t) ); + memset( &fmt->video, 0, sizeof(video_format_t) ); + memset( &fmt->subs, 0, sizeof(subs_format_t) ); + + fmt->b_packetized = true; + fmt->i_bitrate = 0; + fmt->i_extra = 0; + fmt->p_extra = NULL; +} + +int es_format_Copy( es_format_t *dst, const es_format_t *src ) +{ + int i; + memcpy( dst, src, sizeof( es_format_t ) ); + if( src->psz_language ) + dst->psz_language = strdup( src->psz_language ); + if( src->psz_description ) + dst->psz_description = strdup( src->psz_description ); + if( src->i_extra > 0 ) + { + dst->i_extra = src->i_extra; + dst->p_extra = malloc( src->i_extra ); + memcpy( dst->p_extra, src->p_extra, src->i_extra ); + } + else + { + dst->i_extra = 0; + dst->p_extra = NULL; + } + + if( src->subs.psz_encoding ) + dst->subs.psz_encoding = strdup( src->subs.psz_encoding ); + + if( src->video.p_palette ) + { + dst->video.p_palette = + (video_palette_t*)malloc( sizeof( video_palette_t ) ); + memcpy( dst->video.p_palette, src->video.p_palette, + sizeof( video_palette_t ) ); + } + + dst->i_extra_languages = src->i_extra_languages; + if( dst->i_extra_languages ) + dst->p_extra_languages = (extra_languages_t*) + malloc(dst->i_extra_languages * sizeof(*dst->p_extra_languages )); + for( i = 0; i < dst->i_extra_languages; i++ ) { + if( src->p_extra_languages[i].psz_language ) + dst->p_extra_languages[i].psz_language = strdup( src->p_extra_languages[i].psz_language ); + else + dst->p_extra_languages[i].psz_language = NULL; + if( src->p_extra_languages[i].psz_description ) + dst->p_extra_languages[i].psz_description = strdup( src->p_extra_languages[i].psz_description ); + else + dst->p_extra_languages[i].psz_description = NULL; + } + return VLC_SUCCESS; +} + +void es_format_Clean( es_format_t *fmt ) +{ + free( fmt->psz_language ); + free( fmt->psz_description ); + + if( fmt->i_extra > 0 ) free( fmt->p_extra ); + + free( fmt->video.p_palette ); + free( fmt->subs.psz_encoding ); + + if( fmt->i_extra_languages > 0 && fmt->p_extra_languages ) + { + int i; + for( i = 0; i < fmt->i_extra_languages; i++ ) + { + free( fmt->p_extra_languages[i].psz_language ); + free( fmt->p_extra_languages[i].psz_description ); + } + free( fmt->p_extra_languages ); + } + + /* es_format_Clean can be called multiple times */ + memset( fmt, 0, sizeof(*fmt) ); +} + diff --git a/VLC/events.c b/VLC/events.c new file mode 100644 index 0000000..f9ef9bc --- /dev/null +++ b/VLC/events.c @@ -0,0 +1,380 @@ +/***************************************************************************** + * events.c: events interface + * This library provides an interface to the send and receive events. + * It is more lightweight than variable based callback. + ***************************************************************************** + * Copyright (C) 1998-2005 the VideoLAN team + * $Id$ + * + * Authors: Pierre d'Herbemont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#include + +#include "vlc_events.h" +#include "vlc_arrays.h" + +/***************************************************************************** + * Documentation : Read vlc_events.h + *****************************************************************************/ + +//#define DEBUG_EVENT + +/***************************************************************************** + * Private types. + *****************************************************************************/ + +typedef struct vlc_event_listener_t +{ + void * p_user_data; + vlc_event_callback_t pf_callback; +#ifdef DEBUG_EVENT + char * psz_debug_name; +#endif +} vlc_event_listener_t; + +typedef struct vlc_event_listeners_group_t +{ + vlc_event_type_t event_type; + DECL_ARRAY(struct vlc_event_listener_t *) listeners; + + /* Used in vlc_event_send() to make sure to behave + Correctly when vlc_event_detach was called during + a callback */ + bool b_sublistener_removed; + +} vlc_event_listeners_group_t; + +#ifdef DEBUG_EVENT +static const char ppsz_event_type_to_name[][33] = +{ + [vlc_InputStateChanged] = "vlc_InputStateChanged", + [vlc_InputSelectedStreamChanged] = "vlc_InputSelectedStreamChanged", + + [vlc_InputItemMetaChanged] = "vlc_InputItemMetaChanged", + [vlc_InputItemSubItemAdded] = "vlc_InputItemSubItemAdded", + [vlc_InputItemDurationChanged] = "vlc_InputItemDurationChanged", + [vlc_InputItemPreparsedChanged] = "vlc_InputItemPreparsedChanged", + [vlc_InputItemNameChanged] = "vlc_InputItemNameChanged", + [vlc_InputItemInfoChanged] = "vlc_InputItemInfoChanged", + [vlc_InputItemErrorWhenReadingChanged] = "vlc_InputItemErrorWhenReadingChanged", + + [vlc_ServicesDiscoveryItemAdded] = "vlc_ServicesDiscoveryItemAdded", + [vlc_ServicesDiscoveryItemRemoved] = "vlc_ServicesDiscoveryItemRemoved" + [vlc_ServicesDiscoveryStarted] = "vlc_ServicesDiscoveryStarted" + [vlc_ServicesDiscoveryEnded] = "vlc_ServicesDiscoveryEnded" +}; +#endif + +static bool +listeners_are_equal( vlc_event_listener_t * listener1, + vlc_event_listener_t * listener2 ) +{ + return listener1->pf_callback == listener2->pf_callback && + listener1->p_user_data == listener2->p_user_data; +} + +static bool +group_contains_listener( vlc_event_listeners_group_t * group, + vlc_event_listener_t * searched_listener ) +{ + vlc_event_listener_t * listener; + FOREACH_ARRAY( listener, group->listeners ) + if( listeners_are_equal(searched_listener, listener) ) + return true; + FOREACH_END() + return false; +} + +/***************************************************************************** + * + *****************************************************************************/ + +/** + * Initialize event manager object + * p_obj is the object that contains the event manager. But not + * necessarily a vlc_object_t (an input_item_t is not a vlc_object_t + * for instance). + * p_parent_obj gives a libvlc instance + */ +int __vlc_event_manager_init( vlc_event_manager_t * p_em, void * p_obj, + vlc_object_t * p_parent_obj ) +{ + p_em->p_obj = p_obj; + p_em->p_parent_object = p_parent_obj; + vlc_mutex_init( &p_em->object_lock ); + + /* We need a recursive lock here, because we need to be able + * to call libvlc_event_detach even if vlc_event_send is in + * the call frame. + * This ensures that after libvlc_event_detach, the callback + * will never gets triggered. + * */ + vlc_mutex_init_recursive( &p_em->event_sending_lock ); + ARRAY_INIT( p_em->listeners_groups ); + return VLC_SUCCESS; +} + +/** + * Destroy the event manager + */ +void vlc_event_manager_fini( vlc_event_manager_t * p_em ) +{ + struct vlc_event_listeners_group_t * listeners_group; + struct vlc_event_listener_t * listener; + + vlc_mutex_destroy( &p_em->object_lock ); + vlc_mutex_destroy( &p_em->event_sending_lock ); + + FOREACH_ARRAY( listeners_group, p_em->listeners_groups ) + FOREACH_ARRAY( listener, listeners_group->listeners ) + free( listener ); + FOREACH_END() + ARRAY_RESET( listeners_group->listeners ); + free( listeners_group ); + FOREACH_END() + ARRAY_RESET( p_em->listeners_groups ); +} + +/** + * Destroy the event manager + */ +int vlc_event_manager_register_event_type( + vlc_event_manager_t * p_em, + vlc_event_type_t event_type ) +{ + vlc_event_listeners_group_t * listeners_group; + listeners_group = malloc(sizeof(vlc_event_listeners_group_t)); + + if( !listeners_group ) + return VLC_ENOMEM; + + listeners_group->event_type = event_type; + ARRAY_INIT( listeners_group->listeners ); + + vlc_mutex_lock( &p_em->object_lock ); + ARRAY_APPEND( p_em->listeners_groups, listeners_group ); + vlc_mutex_unlock( &p_em->object_lock ); + + return VLC_SUCCESS; +} + +/** + * Send an event to the listener attached to this p_em. + */ +void vlc_event_send( vlc_event_manager_t * p_em, + vlc_event_t * p_event ) +{ + vlc_event_listeners_group_t * listeners_group = NULL; + vlc_event_listener_t * listener; + vlc_event_listener_t * array_of_cached_listeners = NULL; + vlc_event_listener_t * cached_listener; + int i, i_cached_listeners = 0; + + /* Fill event with the sending object now */ + p_event->p_obj = p_em->p_obj; + + vlc_mutex_lock( &p_em->object_lock ); + FOREACH_ARRAY( listeners_group, p_em->listeners_groups ) + if( listeners_group->event_type == p_event->type ) + { + if( listeners_group->listeners.i_size <= 0 ) + break; + + /* Save the function to call */ + i_cached_listeners = listeners_group->listeners.i_size; + array_of_cached_listeners = malloc( + sizeof(vlc_event_listener_t)*i_cached_listeners ); + if( !array_of_cached_listeners ) + { + vlc_mutex_unlock( &p_em->object_lock ); + return; + } + + cached_listener = array_of_cached_listeners; + FOREACH_ARRAY( listener, listeners_group->listeners ) + memcpy( cached_listener, listener, sizeof(vlc_event_listener_t)); +#ifdef DEBUG_EVENT + cached_listener->psz_debug_name = strdup(cached_listener->psz_debug_name); +#endif + cached_listener++; + FOREACH_END() + + break; + } + FOREACH_END() + vlc_mutex_unlock( &p_em->object_lock ); + + /* Call the function attached */ + cached_listener = array_of_cached_listeners; + + if( !listeners_group || !array_of_cached_listeners ) + { + free( array_of_cached_listeners ); + return; + } + + vlc_mutex_lock( &p_em->event_sending_lock ) ; + + /* Track item removed from *this* thread, with a simple flag */ + listeners_group->b_sublistener_removed = false; + + for( i = 0; i < i_cached_listeners; i++ ) + { +#ifdef DEBUG_EVENT + msg_Dbg( p_em->p_parent_object, + "Calling '%s' with a '%s' event (data %p)", + cached_listener->psz_debug_name, + ppsz_event_type_to_name[p_event->type], + cached_listener->p_user_data ); + free(cached_listener->psz_debug_name); +#endif + /* No need to lock on listeners_group, a listener group can't be removed */ + if( listeners_group->b_sublistener_removed ) + { + /* If a callback was removed, this gets called */ + bool valid_listener; + vlc_mutex_lock( &p_em->object_lock ); + valid_listener = group_contains_listener( listeners_group, cached_listener ); + vlc_mutex_unlock( &p_em->object_lock ); + if( !valid_listener ) + { +#ifdef DEBUG_EVENT + msg_Dbg( p_em->p_parent_object, "Callback was removed during execution" ); +#endif + cached_listener++; + continue; + } + } + cached_listener->pf_callback( p_event, cached_listener->p_user_data ); + cached_listener++; + } + vlc_mutex_unlock( &p_em->event_sending_lock ); + + free( array_of_cached_listeners ); +} + +/** + * Add a callback for an event. + */ +int __vlc_event_attach( vlc_event_manager_t * p_em, + vlc_event_type_t event_type, + vlc_event_callback_t pf_callback, + void *p_user_data, + const char * psz_debug_name ) +{ + vlc_event_listeners_group_t * listeners_group; + vlc_event_listener_t * listener; + listener = malloc(sizeof(vlc_event_listener_t)); + if( !listener ) + return VLC_ENOMEM; + + listener->p_user_data = p_user_data; + listener->pf_callback = pf_callback; +#ifdef DEBUG_EVENT + listener->psz_debug_name = strdup( psz_debug_name ); +#else + (void)psz_debug_name; +#endif + + vlc_mutex_lock( &p_em->object_lock ); + FOREACH_ARRAY( listeners_group, p_em->listeners_groups ) + if( listeners_group->event_type == event_type ) + { + ARRAY_APPEND( listeners_group->listeners, listener ); +#ifdef DEBUG_EVENT + msg_Dbg( p_em->p_parent_object, + "Listening to '%s' event with '%s' (data %p)", + ppsz_event_type_to_name[event_type], + listener->psz_debug_name, + listener->p_user_data ); +#endif + vlc_mutex_unlock( &p_em->object_lock ); + return VLC_SUCCESS; + } + FOREACH_END() + vlc_mutex_unlock( &p_em->object_lock ); + + msg_Err( p_em->p_parent_object, "Can't attach to an object event manager event" ); + free(listener); + return VLC_EGENERIC; +} + +/** + * Remove a callback for an event. + */ + +int vlc_event_detach( vlc_event_manager_t *p_em, + vlc_event_type_t event_type, + vlc_event_callback_t pf_callback, + void *p_user_data ) +{ + vlc_event_listeners_group_t * listeners_group; + struct vlc_event_listener_t * listener; + + vlc_mutex_lock( &p_em->object_lock ); + vlc_mutex_lock( &p_em->event_sending_lock ); + FOREACH_ARRAY( listeners_group, p_em->listeners_groups ) + if( listeners_group->event_type == event_type ) + { + FOREACH_ARRAY( listener, listeners_group->listeners ) + if( listener->pf_callback == pf_callback && + listener->p_user_data == p_user_data ) + { + /* Tell vlc_event_send, we did remove an item from that group, + in case vlc_event_send is in our caller stack */ + listeners_group->b_sublistener_removed = true; + + /* that's our listener */ + ARRAY_REMOVE( listeners_group->listeners, + fe_idx /* This comes from the macro (and that's why + I hate macro) */ ); +#ifdef DEBUG_EVENT + msg_Dbg( p_em->p_parent_object, + "Detaching '%s' from '%s' event (data %p)", + listener->psz_debug_name, + ppsz_event_type_to_name[event_type], + listener->p_user_data ); + + free( listener->psz_debug_name ); +#endif + free( listener ); + vlc_mutex_unlock( &p_em->event_sending_lock ); + vlc_mutex_unlock( &p_em->object_lock ); + return VLC_SUCCESS; + } + FOREACH_END() + } + FOREACH_END() + vlc_mutex_unlock( &p_em->event_sending_lock ); + vlc_mutex_unlock( &p_em->object_lock ); + + msg_Warn( p_em->p_parent_object, "Can't detach to an object event manager event" ); + + return VLC_EGENERIC; +} + diff --git a/VLC/file.c b/VLC/file.c new file mode 100644 index 0000000..439e6d4 --- /dev/null +++ b/VLC/file.c @@ -0,0 +1,791 @@ +/***************************************************************************** + * file.c: configuration file handling + ***************************************************************************** + * Copyright (C) 2001-2007 the VideoLAN team + * $Id$ + * + * Authors: Gildas Bazin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "libvlc.h" +#include "vlc_charset.h" +#include "vlc_keys.h" + +#include /* errno */ +#include +#include +#ifdef __APPLE__ +# include +#else +#include +#endif + +#include "configuration.h" +#include "modules.h" + +static char *ConfigKeyToString( int ); + +static inline char *strdupnull (const char *src) +{ + return src ? strdup (src) : NULL; +} + +/** + * Get the user's configuration file + */ +static char *config_GetConfigFile( void ) +{ + char *psz_dir = config_GetUserConfDir(); + char *psz_configfile; + + if( asprintf( &psz_configfile, "%s" DIR_SEP CONFIG_FILE, psz_dir ) == -1 ) + psz_configfile = NULL; + free( psz_dir ); + return psz_configfile; +} + +static FILE *config_OpenConfigFile( vlc_object_t *p_obj, const char *mode ) +{ + char *psz_filename = libvlc_priv (p_obj->p_libvlc)->psz_configfile; + FILE *p_stream; + + if( !psz_filename ) + { + psz_filename = config_GetConfigFile(); + } + + msg_Dbg( p_obj, "opening config file (%s)", psz_filename ); + + p_stream = utf8_fopen( psz_filename, mode ); + if( p_stream == NULL && errno != ENOENT ) + { + msg_Err( p_obj, "cannot open config file (%s): %m", + psz_filename ); + + } +#if !( defined(WIN32) || defined(__APPLE__) || defined(SYS_BEOS) ) + else if( p_stream == NULL && errno == ENOENT && mode[0] == 'r' ) + { + /* This is the fallback for pre XDG Base Directory + * Specification configs */ + char *psz_old; + if( asprintf( &psz_old, "%s" DIR_SEP CONFIG_DIR DIR_SEP CONFIG_FILE, + config_GetHomeDir() ) != -1 ) + { + p_stream = utf8_fopen( psz_old, mode ); + if( p_stream ) + { + /* Old config file found. We want to write it at the + * new location now. */ + msg_Info( p_obj->p_libvlc, "Found old config file at %s. " + "VLC will now use %s.", psz_old, psz_filename ); + char *psz_readme; + if( asprintf(&psz_readme,"%s"DIR_SEP CONFIG_DIR DIR_SEP"README", + config_GetHomeDir() ) != -1 ) + { + FILE *p_readme = utf8_fopen( psz_readme, "wt" ); + if( p_readme ) + { + fprintf( p_readme, "The VLC media player " + "configuration folder has moved to comply\n" + "with the XDG Base Directory Specification " + "version 0.6. Your\nconfiguration has been " + "copied to the new location:\n%s\nYou can " + "delete this directory and all its contents.", + psz_filename); + fclose( p_readme ); + } + free( psz_readme ); + } + } + free( psz_old ); + } + } +#endif + else if( p_stream != NULL ) + { + libvlc_priv (p_obj->p_libvlc)->psz_configfile = psz_filename; + } + + return p_stream; +} + + +static int strtoi (const char *str) +{ + char *end; + long l; + + errno = 0; + l = strtol (str, &end, 0); + + if (!errno) + { + if ((l > INT_MAX) || (l < INT_MIN)) + errno = ERANGE; + if (*end) + errno = EINVAL; + } + return (int)l; +} + + +/***************************************************************************** + * config_LoadConfigFile: loads the configuration file. + ***************************************************************************** + * This function is called to load the config options stored in the config + * file. + *****************************************************************************/ +int __config_LoadConfigFile( vlc_object_t *p_this, const char *psz_module_name ) +{ + libvlc_priv_t *priv = libvlc_priv (p_this->p_libvlc); + vlc_list_t *p_list; + FILE *file; + + file = config_OpenConfigFile (p_this, "rt"); + if (file == NULL) + return VLC_EGENERIC; + + /* Acquire config file lock */ + vlc_mutex_lock( &priv->config_lock ); + + /* Look for the selected module, if NULL then save everything */ + p_list = vlc_list_find( p_this, VLC_OBJECT_MODULE, FIND_ANYWHERE ); + + /* Look for UTF-8 Byte Order Mark */ + char * (*convert) (const char *) = strdupnull; + char bom[3]; + + if ((fread (bom, 1, 3, file) != 3) + || memcmp (bom, "\xEF\xBB\xBF", 3)) + { + convert = FromLocaleDup; + rewind (file); /* no BOM, rewind */ + } + + module_t *module = NULL; + char line[1024], section[1022]; + section[0] = '\0'; + + /* Ensure consistent number formatting... */ + locale_t loc = newlocale (LC_NUMERIC_MASK, "C", NULL); + locale_t baseloc = uselocale (loc); + + while (fgets (line, 1024, file) != NULL) + { + /* Ignore comments and empty lines */ + switch (line[0]) + { + case '#': + case '\n': + case '\0': + continue; + } + + if (line[0] == '[') + { + char *ptr = strchr (line, ']'); + if (ptr == NULL) + continue; /* syntax error; */ + *ptr = '\0'; + + /* New section ( = a given module) */ + strcpy (section, line + 1); + module = NULL; + + if ((psz_module_name == NULL) + || (strcmp (psz_module_name, section) == 0)) + { + for (int i = 0; i < p_list->i_count; i++) + { + module_t *m = (module_t *)p_list->p_values[i].p_object; + + if ((strcmp (section, m->psz_object_name) == 0) + && (m->i_config_items > 0)) /* ignore config-less modules */ + { + module = m; + if (psz_module_name != NULL) + msg_Dbg (p_this, + "loading config for module \"%s\"", + section); + break; + } + } + } + + continue; + } + + if (module == NULL) + continue; /* no need to parse if there is no matching module */ + + char *ptr = strchr (line, '\n'); + if (ptr != NULL) + *ptr = '\0'; + + /* look for option name */ + const char *psz_option_name = line; + + ptr = strchr (line, '='); + if (ptr == NULL) + continue; /* syntax error */ + + *ptr = '\0'; + const char *psz_option_value = ptr + 1; + + /* try to match this option with one of the module's options */ + for (size_t i = 0; i < module->confsize; i++) + { + module_config_t *p_item = module->p_config + i; + + if ((p_item->i_type & CONFIG_HINT) + || strcmp (p_item->psz_name, psz_option_name)) + continue; + + /* We found it */ + errno = 0; + + switch( p_item->i_type ) + { + case CONFIG_ITEM_BOOL: + case CONFIG_ITEM_INTEGER: + { + long l = strtoi (psz_option_value); + if (errno) + msg_Warn (p_this, "Integer value (%s) for %s: %m", + psz_option_value, psz_option_name); + else + p_item->saved.i = p_item->value.i = (int)l; + break; + } + + case CONFIG_ITEM_FLOAT: + if( !*psz_option_value ) + break; /* ignore empty option */ + p_item->value.f = (float)atof (psz_option_value); + p_item->saved.f = p_item->value.f; + break; + + case CONFIG_ITEM_KEY: + if( !*psz_option_value ) + break; /* ignore empty option */ + p_item->value.i = ConfigStringToKey(psz_option_value); + p_item->saved.i = p_item->value.i; + break; + + default: + vlc_mutex_lock( p_item->p_lock ); + + /* free old string */ + free( (char*) p_item->value.psz ); + free( (char*) p_item->saved.psz ); + + p_item->value.psz = convert (psz_option_value); + p_item->saved.psz = strdupnull (p_item->value.psz); + + vlc_mutex_unlock( p_item->p_lock ); + break; + } + + break; + } + } + + if (ferror (file)) + { + msg_Err (p_this, "error reading configuration: %m"); + clearerr (file); + } + fclose (file); + + vlc_list_release( p_list ); + if (loc != (locale_t)0) + { + uselocale (baseloc); + freelocale (loc); + } + + vlc_mutex_unlock( &priv->config_lock ); + return 0; +} + +/***************************************************************************** + * config_CreateDir: Create configuration directory if it doesn't exist. + *****************************************************************************/ +int config_CreateDir( vlc_object_t *p_this, const char *psz_dirname ) +{ + if( !psz_dirname || !*psz_dirname ) return -1; + + if( utf8_mkdir( psz_dirname, 0700 ) == 0 ) + return 0; + + switch( errno ) + { + case EEXIST: + return 0; + + case ENOENT: + { + /* Let's try to create the parent directory */ + char psz_parent[strlen( psz_dirname ) + 1], *psz_end; + strcpy( psz_parent, psz_dirname ); + + psz_end = strrchr( psz_parent, DIR_SEP_CHAR ); + if( psz_end && psz_end != psz_parent ) + { + *psz_end = '\0'; + if( config_CreateDir( p_this, psz_parent ) == 0 ) + { + if( !utf8_mkdir( psz_dirname, 0700 ) ) + return 0; + } + } + } + } + + msg_Err( p_this, "could not create %s: %m", psz_dirname ); + return -1; +} + +static int +config_Write (FILE *file, const char *type, const char *desc, + bool comment, const char *name, const char *fmt, ...) +{ + va_list ap; + int ret; + + if (desc == NULL) + desc = "?"; + + if (fprintf (file, "# %s (%s)\n%s%s=", desc, _(type), + comment ? "#" : "", name) < 0) + return -1; + + va_start (ap, fmt); + ret = vfprintf (file, fmt, ap); + va_end (ap); + if (ret < 0) + return -1; + + if (fputs ("\n\n", file) == EOF) + return -1; + return 0; +} + + +/***************************************************************************** + * config_SaveConfigFile: Save a module's config options. + ***************************************************************************** + * This will save the specified module's config options to the config file. + * If psz_module_name is NULL then we save all the modules config options. + * It's no use to save the config options that kept their default values, so + * we'll try to be a bit clever here. + * + * When we save we mustn't delete the config options of the modules that + * haven't been loaded. So we cannot just create a new config file with the + * config structures we've got in memory. + * I don't really know how to deal with this nicely, so I will use a completly + * dumb method ;-) + * I will load the config file in memory, but skipping all the sections of the + * modules we want to save. Then I will create a brand new file, dump the file + * loaded in memory and then append the sections of the modules we want to + * save. + * Really stupid no ? + *****************************************************************************/ +static int SaveConfigFile( vlc_object_t *p_this, const char *psz_module_name, + bool b_autosave ) +{ + libvlc_priv_t *priv = libvlc_priv (p_this->p_libvlc); + module_t *p_parser; + vlc_list_t *p_list; + FILE *file; + char p_line[1024], *p_index2; + int i_sizebuf = 0; + char *p_bigbuffer, *p_index; + bool b_backup; + int i_index; + + /* Acquire config file lock */ + vlc_mutex_lock( &priv->config_lock ); + + if( libvlc_priv (p_this->p_libvlc)->psz_configfile == NULL ) + { + char *psz_configdir = config_GetUserConfDir(); + if( !psz_configdir ) /* XXX: This should never happen */ + { + msg_Err( p_this, "no configuration directory defined" ); + vlc_mutex_unlock( &priv->config_lock ); + return -1; + } + + config_CreateDir( p_this, psz_configdir ); + free( psz_configdir ); + } + + file = config_OpenConfigFile( p_this, "rt" ); + if( file != NULL ) + { + /* look for file size */ + fseek( file, 0L, SEEK_END ); + i_sizebuf = ftell( file ); + fseek( file, 0L, SEEK_SET ); + } + + p_bigbuffer = p_index = malloc( i_sizebuf+1 ); + if( !p_bigbuffer ) + { + if( file ) fclose( file ); + vlc_mutex_unlock( &priv->config_lock ); + return -1; + } + p_bigbuffer[0] = 0; + + /* List all available modules */ + p_list = vlc_list_find( p_this, VLC_OBJECT_MODULE, FIND_ANYWHERE ); + + /* backup file into memory, we only need to backup the sections we won't + * save later on */ + b_backup = 0; + while( file && fgets( p_line, 1024, file ) ) + { + if( (p_line[0] == '[') && (p_index2 = strchr(p_line,']'))) + { + + /* we found a section, check if we need to do a backup */ + for( i_index = 0; i_index < p_list->i_count; i_index++ ) + { + p_parser = (module_t *)p_list->p_values[i_index].p_object ; + + if( ((p_index2 - &p_line[1]) + == (int)strlen(p_parser->psz_object_name) ) + && !memcmp( &p_line[1], p_parser->psz_object_name, + strlen(p_parser->psz_object_name) ) ) + { + if( !psz_module_name ) + break; + else if( !strcmp( psz_module_name, + p_parser->psz_object_name ) ) + break; + } + } + + if( i_index == p_list->i_count ) + { + /* we don't have this section in our list so we need to back + * it up */ + *p_index2 = 0; +#if 0 + msg_Dbg( p_this, "backing up config for unknown module \"%s\"", + &p_line[1] ); +#endif + *p_index2 = ']'; + + b_backup = 1; + } + else + { + b_backup = 0; + } + } + + /* save line if requested and line is valid (doesn't begin with a + * space, tab, or eol) */ + if( b_backup && (p_line[0] != '\n') && (p_line[0] != ' ') + && (p_line[0] != '\t') ) + { + strcpy( p_index, p_line ); + p_index += strlen( p_line ); + } + } + if( file ) fclose( file ); + + + /* + * Save module config in file + */ + + file = config_OpenConfigFile (p_this, "wt"); + if( !file ) + { + vlc_list_release( p_list ); + free( p_bigbuffer ); + vlc_mutex_unlock( &priv->config_lock ); + return -1; + } + + fprintf( file, "\xEF\xBB\xBF###\n### ",COPYRIGHT_MESSAGE, "\n###\n\n" + "###\n### lines beginning with a '#' character are comments\n###\n\n" ); + + /* Ensure consistent number formatting... */ + locale_t loc = newlocale (LC_NUMERIC_MASK, "C", NULL); + locale_t baseloc = uselocale (loc); + + /* Look for the selected module, if NULL then save everything */ + for( i_index = 0; i_index < p_list->i_count; i_index++ ) + { + module_config_t *p_item, *p_end; + p_parser = (module_t *)p_list->p_values[i_index].p_object ; + + if( psz_module_name && strcmp( psz_module_name, + p_parser->psz_object_name ) ) + continue; + + if( !p_parser->i_config_items ) + continue; + + if( psz_module_name ) + msg_Dbg( p_this, "saving config for module \"%s\"", + p_parser->psz_object_name ); + + fprintf( file, "[%s]", p_parser->psz_object_name ); + if( p_parser->psz_longname ) + fprintf( file, " # %s\n\n", p_parser->psz_longname ); + else + fprintf( file, "\n\n" ); + + for( p_item = p_parser->p_config, p_end = p_item + p_parser->confsize; + p_item < p_end; + p_item++ ) + { + /* Do not save the new value in the configuration file + * if doing an autosave, and the item is not an "autosaved" one. */ + bool b_retain = b_autosave && !p_item->b_autosave; + + if ((p_item->i_type & CONFIG_HINT) /* ignore hint */ + || p_item->b_removed /* ignore deprecated option */ + || p_item->b_unsaveable) /* ignore volatile option */ + continue; + + if (IsConfigIntegerType (p_item->i_type)) + { + int val = b_retain ? p_item->saved.i : p_item->value.i; + if (p_item->i_type == CONFIG_ITEM_KEY) + { + char *psz_key = ConfigKeyToString (val); + config_Write (file, p_item->psz_text, N_("key"), + val == p_item->orig.i, + p_item->psz_name, "%s", + psz_key ? psz_key : ""); + free (psz_key); + } + else + config_Write (file, p_item->psz_text, + (p_item->i_type == CONFIG_ITEM_BOOL) + ? N_("boolean") : N_("integer"), + val == p_item->orig.i, + p_item->psz_name, "%d", val); + p_item->saved.i = val; + } + else + if (IsConfigFloatType (p_item->i_type)) + { + float val = b_retain ? p_item->saved.f : p_item->value.f; + config_Write (file, p_item->psz_text, N_("float"), + val == p_item->orig.f, + p_item->psz_name, "%f", val); + p_item->saved.f = val; + } + else + { + const char *psz_value = b_retain ? p_item->saved.psz + : p_item->value.psz; + bool modified; + + assert (IsConfigStringType (p_item->i_type)); + + if (b_retain && (psz_value == NULL)) /* FIXME: hack */ + psz_value = p_item->orig.psz; + + modified = + (psz_value != NULL) + ? ((p_item->orig.psz != NULL) + ? (strcmp (psz_value, p_item->orig.psz) != 0) + : true) + : (p_item->orig.psz != NULL); + + config_Write (file, p_item->psz_text, N_("string"), + !modified, p_item->psz_name, "%s", + psz_value ? psz_value : ""); + + if ( !b_retain ) + { + + free ((char *)p_item->saved.psz); + if( (psz_value && p_item->orig.psz && + strcmp( psz_value, p_item->orig.psz )) || + !psz_value || !p_item->orig.psz) + p_item->saved.psz = strdupnull (psz_value); + else + p_item->saved.psz = NULL; + } + } + + if (!b_retain) + p_item->b_dirty = false; + } + } + + vlc_list_release( p_list ); + if (loc != (locale_t)0) + { + uselocale (baseloc); + freelocale (loc); + } + + /* + * Restore old settings from the config in file + */ + fputs( p_bigbuffer, file ); + free( p_bigbuffer ); + + fclose( file ); + vlc_mutex_unlock( &priv->config_lock ); + + return 0; +} + +int config_AutoSaveConfigFile( vlc_object_t *p_this ) +{ + libvlc_priv_t *priv = libvlc_priv (p_this->p_libvlc); + vlc_list_t *p_list; + int i_index, i_count; + + assert( p_this ); + + /* Check if there's anything to save */ + vlc_mutex_lock( &priv->config_lock ); + p_list = vlc_list_find( p_this, VLC_OBJECT_MODULE, FIND_ANYWHERE ); + i_count = p_list->i_count; + for( i_index = 0; i_index < i_count; i_index++ ) + { + module_t *p_parser = (module_t *)p_list->p_values[i_index].p_object ; + module_config_t *p_item, *p_end; + + if( !p_parser->i_config_items ) continue; + + for( p_item = p_parser->p_config, p_end = p_item + p_parser->confsize; + p_item < p_end; + p_item++ ) + { + if( p_item->b_autosave && p_item->b_dirty ) break; + } + if( p_item < p_end ) break; + } + vlc_list_release( p_list ); + vlc_mutex_unlock( &priv->config_lock ); + + if( i_index == i_count ) return VLC_SUCCESS; + return SaveConfigFile( p_this, 0, true ); +} + +int __config_SaveConfigFile( vlc_object_t *p_this, const char *psz_module_name ) +{ + return SaveConfigFile( p_this, psz_module_name, false ); +} + +/** + * Get the user's configuration file when given with the --config option + */ +char *config_GetCustomConfigFile( libvlc_int_t *p_libvlc ) +{ + char *psz_configfile = config_GetPsz( p_libvlc, "config" ); + if( psz_configfile != NULL ) + { + if( psz_configfile[0] == '~' && psz_configfile[1] == '/' ) + { + /* This is incomplete: we should also support the ~cmassiot/ syntax */ + char *psz_buf; + if( asprintf( &psz_buf, "%s/%s", config_GetHomeDir(), + psz_configfile + 2 ) == -1 ) + { + free( psz_configfile ); + return NULL; + } + free( psz_configfile ); + psz_configfile = psz_buf; + } + } + return psz_configfile; +} + +int ConfigStringToKey( const char *psz_key ) +{ + int i_key = 0; + unsigned int i; + const char *psz_parser = strchr( psz_key, '-' ); + while( psz_parser && psz_parser != psz_key ) + { + for( i = 0; i < sizeof(vlc_modifiers) / sizeof(key_descriptor_t); i++ ) + { + if( !strncasecmp( vlc_modifiers[i].psz_key_string, psz_key, + strlen( vlc_modifiers[i].psz_key_string ) ) ) + { + i_key |= vlc_modifiers[i].i_key_code; + } + } + psz_key = psz_parser + 1; + psz_parser = strchr( psz_key, '-' ); + } + for( i = 0; i < sizeof(vlc_keys) / sizeof( key_descriptor_t ); i++ ) + { + if( !strcasecmp( vlc_keys[i].psz_key_string, psz_key ) ) + { + i_key |= vlc_keys[i].i_key_code; + break; + } + } + return i_key; +} + +char *ConfigKeyToString( int i_key ) +{ + char *psz_key = malloc( 100 ); + char *p; + size_t index; + + if ( !psz_key ) + { + return NULL; + } + *psz_key = '\0'; + p = psz_key; + + for( index = 0; index < (sizeof(vlc_modifiers) / sizeof(key_descriptor_t)); + index++ ) + { + if( i_key & vlc_modifiers[index].i_key_code ) + { + p += sprintf( p, "%s-", vlc_modifiers[index].psz_key_string ); + } + } + for( index = 0; index < (sizeof(vlc_keys) / sizeof( key_descriptor_t)); + index++) + { + if( (int)( i_key & ~KEY_MODIFIER ) == vlc_keys[index].i_key_code ) + { + p += sprintf( p, "%s", vlc_keys[index].psz_key_string ); + break; + } + } + return psz_key; +} + diff --git a/VLC/filter_chain.c b/VLC/filter_chain.c new file mode 100644 index 0000000..10b26d4 --- /dev/null +++ b/VLC/filter_chain.c @@ -0,0 +1,483 @@ +/***************************************************************************** + * filter_chain.c : Handle chains of filter_t objects. + ***************************************************************************** + * Copyright (C) 2008 the VideoLAN team + * $Id$ + * + * Author: Antoine Cellerier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_filter.h" +#include "vlc_arrays.h" +#include "libvlc.h" + +struct filter_chain_t +{ + vlc_object_t *p_this; /* Parent object */ + + vlc_array_t filters; /* List of filters */ + + char *psz_capability; /* Capability of all the filters in the chain */ + es_format_t fmt_in; /* Input format (read only) */ + es_format_t fmt_out; /* Output format (writable depending on ... */ + bool b_allow_fmt_out_change; /* allow changing fmt_out if true */ + + int (* pf_buffer_allocation_init)( filter_t *, void *p_data ); /* Callback called once filter allocation has succeeded to initialize the filter's buffer allocation callbacks. This function is responsible for setting p_owner if needed. */ + void (* pf_buffer_allocation_clear)( filter_t * ); /* Callback called on filter removal from chain to clean up buffer allocation callbacks data (ie p_owner) */ + void *p_buffer_allocation_data; /* Data for pf_buffer_allocation_init */ +}; + +/** + * Local prototypes + */ +static filter_t *filter_chain_AppendFilterInternal( filter_chain_t *, const char *, config_chain_t *, const es_format_t *, const es_format_t * ); +static int filter_chain_AppendFromStringInternal( filter_chain_t *, const char * ); +static int filter_chain_DeleteFilterInternal( filter_chain_t *, filter_t * ); + +static int UpdateBufferFunctions( filter_chain_t * ); +static picture_t *VideoBufferNew( filter_t * ); + +/** + * Filter chain initialisation + */ +filter_chain_t *__filter_chain_New( vlc_object_t *p_this, + const char *psz_capability, + bool b_allow_fmt_out_change, + int (*pf_buffer_allocation_init)( filter_t *, void * ), + void (*pf_buffer_allocation_clear)( filter_t * ), + void *p_buffer_allocation_data ) +{ + filter_chain_t *p_chain = (filter_chain_t *) + malloc( sizeof( filter_chain_t ) ); + if( !p_chain ) return NULL; + p_chain->p_this = p_this; + vlc_array_init( &p_chain->filters ); + p_chain->psz_capability = strdup( psz_capability ); + if( !p_chain->psz_capability ) + { + free( p_chain ); + return NULL; + } + es_format_Init( &p_chain->fmt_in, UNKNOWN_ES, 0 ); + es_format_Init( &p_chain->fmt_out, UNKNOWN_ES, 0 ); + p_chain->b_allow_fmt_out_change = b_allow_fmt_out_change; + + p_chain->pf_buffer_allocation_init = pf_buffer_allocation_init; + p_chain->pf_buffer_allocation_clear = pf_buffer_allocation_clear; + p_chain->p_buffer_allocation_data = p_buffer_allocation_data; + + return p_chain; +} + +/** + * Filter chain destruction + */ +void filter_chain_Delete( filter_chain_t *p_chain ) +{ + while( p_chain->filters.i_count ) + filter_chain_DeleteFilterInternal( p_chain, + (filter_t*)p_chain->filters.pp_elems[0] ); + vlc_array_clear( &p_chain->filters ); + free( p_chain->psz_capability ); + es_format_Clean( &p_chain->fmt_in ); + es_format_Clean( &p_chain->fmt_out ); + free( p_chain ); +} +/** + * Filter chain reinitialisation + */ +void filter_chain_Reset( filter_chain_t *p_chain, const es_format_t *p_fmt_in, + const es_format_t *p_fmt_out ) +{ + while( p_chain->filters.i_count ) + filter_chain_DeleteFilterInternal( p_chain, + (filter_t*)p_chain->filters.pp_elems[0] ); + if( p_fmt_in ) + { + es_format_Clean( &p_chain->fmt_in ); + es_format_Copy( &p_chain->fmt_in, p_fmt_in ); + } + if( p_fmt_out ) + { + es_format_Clean( &p_chain->fmt_out ); + es_format_Copy( &p_chain->fmt_out, p_fmt_out ); + } +} + + +/** + * Modifying the filter chain + */ +static filter_t *filter_chain_AppendFilterInternal( filter_chain_t *p_chain, + const char *psz_name, + config_chain_t *p_cfg, + const es_format_t *p_fmt_in, + const es_format_t *p_fmt_out ) +{ + static const char typename[] = "filter"; + filter_t *p_filter = + vlc_custom_create( p_chain->p_this, sizeof(filter_t), + VLC_OBJECT_GENERIC, typename ); + if( !p_filter ) return NULL; + vlc_object_attach( p_filter, p_chain->p_this ); + + if( !p_fmt_in ) + { + if( p_chain->filters.i_count ) + p_fmt_in = &((filter_t*)p_chain->filters.pp_elems[p_chain->filters.i_count-1])->fmt_out; + else + p_fmt_in = &p_chain->fmt_in; + } + + if( !p_fmt_out ) + { + p_fmt_out = &p_chain->fmt_out; + } + + es_format_Copy( &p_filter->fmt_in, p_fmt_in ); + es_format_Copy( &p_filter->fmt_out, p_fmt_out ); + p_filter->p_cfg = p_cfg; + p_filter->b_allow_fmt_out_change = p_chain->b_allow_fmt_out_change; + + p_filter->p_module = module_Need( p_filter, p_chain->psz_capability, + psz_name, psz_name ? true : false ); + + if( !p_filter->p_module ) + goto error; + + if( p_filter->b_allow_fmt_out_change ) + { + es_format_Clean( &p_chain->fmt_out ); + es_format_Copy( &p_chain->fmt_out, &p_filter->fmt_out ); + } + + if( p_chain->pf_buffer_allocation_init( p_filter, + p_chain->p_buffer_allocation_data ) != VLC_SUCCESS ) + goto error; + + vlc_array_append( &p_chain->filters, p_filter ); + + msg_Dbg( p_chain->p_this, "Filter '%s' (%p) appended to chain", + psz_name?:p_filter->psz_object_name, p_filter ); + + return p_filter; + + error: + if( psz_name ) + msg_Err( p_chain->p_this, "Failed to create %s '%s'", + p_chain->psz_capability, psz_name ); + else + msg_Err( p_chain->p_this, "Failed to create %s", + p_chain->psz_capability ); + if( p_filter->p_module ) module_Unneed( p_filter, + p_filter->p_module ); + es_format_Clean( &p_filter->fmt_in ); + es_format_Clean( &p_filter->fmt_out ); + vlc_object_detach( p_filter ); + vlc_object_release( p_filter ); + return NULL; +} + +filter_t *filter_chain_AppendFilter( filter_chain_t *p_chain, + const char *psz_name, + config_chain_t *p_cfg, + const es_format_t *p_fmt_in, + const es_format_t *p_fmt_out ) +{ + filter_t *p_filter = filter_chain_AppendFilterInternal( p_chain, psz_name, + p_cfg, p_fmt_in, + p_fmt_out ); + if( UpdateBufferFunctions( p_chain ) < 0 ) + msg_Err( p_filter, "Woah! This doesn't look good." ); + return p_filter; +} + +static int filter_chain_AppendFromStringInternal( filter_chain_t *p_chain, + const char *psz_string ) +{ + config_chain_t *p_cfg = NULL; + char *psz_name = NULL; + char* psz_new_string; + + if( !psz_string || !*psz_string ) return 0; + + psz_new_string = config_ChainCreate( &psz_name, &p_cfg, psz_string ); + + filter_t *p_filter = filter_chain_AppendFilterInternal( p_chain, psz_name, + p_cfg, NULL, NULL ); + if( !p_filter ) + { + msg_Err( p_chain->p_this, "Failed while trying to append '%s' " + "to filter chain", psz_name ); + free( psz_name ); + free( p_cfg ); + free( psz_new_string ); + return -1; + } + free( psz_name ); + + int ret = filter_chain_AppendFromStringInternal( p_chain, psz_new_string ); + free( psz_new_string ); + if( ret < 0 ) + { + filter_chain_DeleteFilterInternal( p_chain, p_filter ); + return ret; + } + return 1 + ret; +} + +int filter_chain_AppendFromString( filter_chain_t *p_chain, + const char *psz_string ) +{ + int i_ret = filter_chain_AppendFromStringInternal( p_chain, psz_string ); + if( i_ret < 0 ) return i_ret; + int i_ret2 = UpdateBufferFunctions( p_chain ); + if( i_ret2 < 0 ) return i_ret2; + return i_ret; +} + +static int filter_chain_DeleteFilterInternal( filter_chain_t *p_chain, + filter_t *p_filter ) +{ + int i; + /* Find the filter in the chain */ + for( i = 0; i < p_chain->filters.i_count; i++ ) + if( (filter_t*)p_chain->filters.pp_elems[i] == p_filter ) + break; + + /* Oops, filter wasn't found */ + if( i == p_chain->filters.i_count ) + { + msg_Err( p_chain->p_this, "Couldn't find filter '%s' (%p) when trying " + "to remove it from chain", p_filter->psz_object_name, + p_filter ); + return VLC_EGENERIC; + } + + /* Remove it from the chain */ + vlc_array_remove( &p_chain->filters, i ); + msg_Dbg( p_chain->p_this, "Filter '%s' (%p) removed from chain", + p_filter->psz_object_name, p_filter ); + + /* Destroy the filter object */ + if( p_chain->pf_buffer_allocation_clear ) + p_chain->pf_buffer_allocation_clear( p_filter ); + vlc_object_detach( p_filter ); + if( p_filter->p_module ) + module_Unneed( p_filter, p_filter->p_module ); + vlc_object_release( p_filter ); + + /* FIXME: check fmt_in/fmt_out consitency */ + return VLC_SUCCESS; +} + +int filter_chain_DeleteFilter( filter_chain_t *p_chain, filter_t *p_filter ) +{ + int i_ret = filter_chain_DeleteFilterInternal( p_chain, p_filter ); + if( i_ret < 0 ) return i_ret; + return UpdateBufferFunctions( p_chain ); +} + +/** + * Reading from the filter chain + */ +filter_t *filter_chain_GetFilter( filter_chain_t *p_chain, int i_position, + const char *psz_name ) +{ + int i; + filter_t **pp_filters = (filter_t **)p_chain->filters.pp_elems; + + if( i_position < 0 ) + return NULL; + + if( !psz_name ) + { + if( i_position >= p_chain->filters.i_count ) + return NULL; + return pp_filters[i_position]; + } + + for( i = 0; i < p_chain->filters.i_count; i++ ) + { + if( !strcmp( psz_name, pp_filters[i]->psz_object_name ) ) + i_position--; + if( i_position < 0 ) + return pp_filters[i]; + } + return NULL; +} + +int filter_chain_GetLength( filter_chain_t *p_chain ) +{ + return p_chain->filters.i_count; +} + +const es_format_t *filter_chain_GetFmtOut( filter_chain_t *p_chain ) +{ + + if( p_chain->b_allow_fmt_out_change ) + return &p_chain->fmt_out; + + /* Unless filter_chain_Reset has been called we are doomed */ + if( p_chain->filters.i_count <= 0 ) + return &p_chain->fmt_out; + + /* */ + filter_t *p_last = (filter_t*)p_chain->filters.pp_elems[p_chain->filters.i_count-1]; + + return &p_last->fmt_out; +} + +/** + * Apply the filter chain + */ + +/* FIXME This include is needed by the Ugly hack */ +#include "vlc_vout.h" + +picture_t *filter_chain_VideoFilter( filter_chain_t *p_chain, picture_t *p_pic ) +{ + int i; + filter_t **pp_filter = (filter_t **)p_chain->filters.pp_elems; + for( i = 0; i < p_chain->filters.i_count; i++ ) + { + filter_t *p_filter = pp_filter[i]; + picture_t *p_newpic = p_filter->pf_video_filter( p_filter, p_pic ); + + /* FIXME Ugly hack to make it work in picture core. + * FIXME Remove this code when the picture release API has been + * FIXME cleaned up (a git revert of the commit should work) */ + if( p_chain->p_this->i_object_type == VLC_OBJECT_VOUT ) + { + vout_thread_t *p_vout = (vout_thread_t*)p_chain->p_this; + vlc_mutex_lock( &p_vout->picture_lock ); + if( p_pic->i_refcount ) + { + p_pic->i_status = DISPLAYED_PICTURE; + } + else + { + p_pic->i_status = DESTROYED_PICTURE; + p_vout->i_heap_size--; + } + vlc_mutex_unlock( &p_vout->picture_lock ); + + if( p_newpic ) + p_newpic->i_status = READY_PICTURE; + } + if( !p_newpic ) + return NULL; + + p_pic = p_newpic; + } + return p_pic; +} + +block_t *filter_chain_AudioFilter( filter_chain_t *p_chain, block_t *p_block ) +{ + int i; + filter_t **pp_filter = (filter_t **)p_chain->filters.pp_elems; + for( i = 0; i < p_chain->filters.i_count; i++ ) + { + filter_t *p_filter = pp_filter[i]; + p_block = p_filter->pf_audio_filter( p_filter, p_block ); + if( !p_block ) + return NULL; + } + return p_block; +} + +#include "vlc_osd.h" + +void filter_chain_SubFilter( filter_chain_t *p_chain, + mtime_t display_date ) +{ + int i; + filter_t **pp_filter = (filter_t **)p_chain->filters.pp_elems; + for( i = 0; i < p_chain->filters.i_count; i++ ) + { + filter_t *p_filter = pp_filter[i]; + subpicture_t *p_subpic = p_filter->pf_sub_filter( p_filter, + display_date ); + if( p_subpic ) + spu_DisplaySubpicture( (spu_t*)p_chain->p_this, p_subpic ); + } +} + +/** + * Internal chain buffer handling + */ + +/** + * This function should be called after every filter chain change + */ +static int UpdateBufferFunctions( filter_chain_t *p_chain ) +{ + if( !strcmp( p_chain->psz_capability, "video filter2" ) ) + { + /** + * Last filter uses the filter chain's parent buffer allocation + * functions. All the other filters use internal functions. + * This makes it possible to have format changes between each + * filter without having to worry about the parent's picture + * heap format. + */ + int i; + filter_t **pp_filter = (filter_t **)p_chain->filters.pp_elems; + filter_t *p_filter; + for( i = 0; i < p_chain->filters.i_count - 1; i++ ) + { + p_filter = pp_filter[i]; + if( p_filter->pf_vout_buffer_new != VideoBufferNew ) + { + if( p_chain->pf_buffer_allocation_clear ) + p_chain->pf_buffer_allocation_clear( p_filter ); + p_filter->pf_vout_buffer_new = VideoBufferNew; + p_filter->pf_vout_buffer_del = NULL; + } + } + if( p_chain->filters.i_count >= 1 ) + { + p_filter = pp_filter[i]; + if( p_filter->pf_vout_buffer_new == VideoBufferNew ) + { + p_filter->pf_vout_buffer_new = NULL; + if( p_chain->pf_buffer_allocation_init( p_filter, + p_chain->p_buffer_allocation_data ) != VLC_SUCCESS ) + return VLC_EGENERIC; + } + } + } + return VLC_SUCCESS; +} + +static picture_t *VideoBufferNew( filter_t *p_filter ) +{ + const video_format_t *p_fmt = &p_filter->fmt_out.video; + + picture_t *p_picture = picture_New( p_fmt->i_chroma, + p_fmt->i_width, p_fmt->i_height, + p_fmt->i_aspect ); + if( !p_picture ) + msg_Err( p_filter, "Failed to allocate picture\n" ); + return p_picture; +} + diff --git a/VLC/flat_media_list_view.c b/VLC/flat_media_list_view.c new file mode 100644 index 0000000..f367e7b --- /dev/null +++ b/VLC/flat_media_list_view.c @@ -0,0 +1,190 @@ +/***************************************************************************** + * flat_media_list_view.c: libvlc flat media list view functions. + ***************************************************************************** + * Copyright (C) 2007 the VideoLAN team + * $Id$ + * + * Authors: Pierre d'Herbemont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#include "libvlc_internal.h" +#include +#include +#include "vlc_arrays.h" + +//#define DEBUG_FLAT_VIEW + +#ifdef DEBUG_FLAT_VIEW +# define trace( fmt, ... ) printf( "%s(): " fmt, __FUNCTION__, ##__VA_ARGS__ ) +#else +# define trace( ... ) +#endif + +struct libvlc_media_list_view_private_t +{ + vlc_array_t array; +}; + +/* + * Private functions + */ + +/************************************************************************** + * ml_item_added (private) (Callback from media_list_view item_added) + **************************************************************************/ +static void +ml_item_added( const libvlc_event_t * p_event, libvlc_media_list_view_t * p_mlv ) +{ + int index = vlc_array_count( &p_mlv->p_this_view_data->array ); + libvlc_media_t * p_md = p_event->u.media_list_item_added.item; + libvlc_media_retain( p_md ); + trace("appending item at index %d\n", index); + + libvlc_media_list_view_will_add_item( p_mlv, p_md, index ); + vlc_array_append( &p_mlv->p_this_view_data->array, p_md ); + libvlc_media_list_view_item_added( p_mlv, p_md, index ); +} + +/************************************************************************** + * ml_item_removed (private) (Callback from media_list_view) + **************************************************************************/ +static void +ml_item_removed( const libvlc_event_t * p_event, libvlc_media_list_view_t * p_mlv ) +{ + libvlc_media_t * p_md = p_event->u.media_list_item_deleted.item; + int i = vlc_array_index_of_item( &p_mlv->p_this_view_data->array, p_md ); + if( i >= 0 ) + { + libvlc_media_list_view_will_delete_item( p_mlv, p_md, i ); + vlc_array_remove( &p_mlv->p_this_view_data->array, i ); + libvlc_media_list_view_item_deleted( p_mlv, p_md, i ); + libvlc_media_release( p_md ); + } +} + +/************************************************************************** + * flat_media_list_view_count (private) + * (called by media_list_view_count) + **************************************************************************/ +static int +flat_media_list_view_count( libvlc_media_list_view_t * p_mlv, + libvlc_exception_t * p_e ) +{ + (void)p_e; + return vlc_array_count( &p_mlv->p_this_view_data->array ); +} + +/************************************************************************** + * flat_media_list_view_item_at_index (private) + * (called by flat_media_list_view_item_at_index) + **************************************************************************/ +static libvlc_media_t * +flat_media_list_view_item_at_index( libvlc_media_list_view_t * p_mlv, + int index, + libvlc_exception_t * p_e ) +{ + libvlc_media_t * p_md; + (void)p_e; + p_md = vlc_array_item_at_index( &p_mlv->p_this_view_data->array, index ); + libvlc_media_retain( p_md ); + return p_md; +} + +/************************************************************************** + * flat_media_list_view_item_at_index (private) + * (called by flat_media_list_view_item_at_index) + **************************************************************************/ +static libvlc_media_list_view_t * +flat_media_list_view_children_at_index( libvlc_media_list_view_t * p_mlv, + int index, + libvlc_exception_t * p_e ) +{ + (void)p_mlv; (void)index; (void)p_e; + return NULL; +} + +/************************************************************************** + * flat_media_list_view_release (private) + * (called by media_list_view_release) + **************************************************************************/ +static void +flat_media_list_view_release( libvlc_media_list_view_t * p_mlv ) +{ + vlc_array_clear( &p_mlv->p_this_view_data->array ); + free( p_mlv->p_this_view_data ); +} + +/* + * Public libvlc functions + */ + +/* Little helper */ +static void +import_mlist_rec( libvlc_media_list_view_t * p_mlv, + libvlc_media_list_t * p_mlist, + libvlc_exception_t * p_e ) +{ + int i, count; + count = libvlc_media_list_count( p_mlist, p_e ); + for( i = 0; i < count; i++ ) + { + libvlc_media_t * p_md; + libvlc_media_list_t * p_submlist; + p_md = libvlc_media_list_item_at_index( p_mlist, i, p_e ); + vlc_array_append( &p_mlv->p_this_view_data->array, p_md ); + p_submlist = libvlc_media_subitems( p_md, p_e ); + if( p_submlist ) + { + libvlc_media_list_lock( p_submlist ); + import_mlist_rec( p_mlv, p_submlist, p_e ); + libvlc_media_list_unlock( p_submlist ); + libvlc_media_list_release( p_submlist ); + } + /* No need to release the md, as we want to retain it, as it is + * stored in our array */ + } +} + +/************************************************************************** + * libvlc_media_list_flat_view (Public) + **************************************************************************/ +libvlc_media_list_view_t * +libvlc_media_list_flat_view( libvlc_media_list_t * p_mlist, + libvlc_exception_t * p_e ) +{ + trace("\n"); + libvlc_media_list_view_t * p_mlv; + struct libvlc_media_list_view_private_t * p_this_view_data; + p_this_view_data = malloc(sizeof(struct libvlc_media_list_view_private_t)); + vlc_array_init( &p_this_view_data->array ); + p_mlv = libvlc_media_list_view_new( p_mlist, + flat_media_list_view_count, + flat_media_list_view_item_at_index, + flat_media_list_view_children_at_index, + libvlc_media_list_flat_view, + flat_media_list_view_release, + p_this_view_data, + p_e ); + libvlc_media_list_lock( p_mlist ); + import_mlist_rec( p_mlv, p_mlist, p_e ); + libvlc_media_list_view_set_ml_notification_callback( p_mlv, + ml_item_added, + ml_item_removed ); + libvlc_media_list_unlock( p_mlist ); + + return p_mlv; +} diff --git a/VLC/getaddrinfo.c b/VLC/getaddrinfo.c new file mode 100644 index 0000000..2527646 --- /dev/null +++ b/VLC/getaddrinfo.c @@ -0,0 +1,773 @@ +/***************************************************************************** + * getaddrinfo.c: getaddrinfo/getnameinfo replacement functions + ***************************************************************************** + * Copyright (C) 2005 the VideoLAN team + * Copyright (C) 2002-2007 Rémi Denis-Courmont + * $Id$ + * + * Author: Rémi Denis-Courmont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#include /* size_t */ +#include /* strlen(), memcpy(), memset(), strchr() */ +#include /* malloc(), free(), strtoul() */ +#include + +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_ARPA_INET_H +# include +#endif +#ifdef HAVE_NETINET_IN_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif + +#include "vlc_network.h" + +#ifndef NO_ADDRESS +# define NO_ADDRESS NO_DATA +#endif +#ifndef INADDR_NONE +# define INADDR_NONE 0xFFFFFFFF +#endif +#ifndef AF_UNSPEC +# define AF_UNSPEC 0 +#endif + + +#ifndef HAVE_GAI_STRERROR +static const struct +{ + int code; + const char msg[41]; +} gai_errlist[] = +{ + { 0, "Error 0" }, + { EAI_BADFLAGS, "Invalid flag used" }, + { EAI_NONAME, "Host or service not found" }, + { EAI_AGAIN, "Temporary name service failure" }, + { EAI_FAIL, "Non-recoverable name service failure" }, + { EAI_NODATA, "No data for host name" }, + { EAI_FAMILY, "Unsupported address family" }, + { EAI_SOCKTYPE, "Unsupported socket type" }, + { EAI_SERVICE, "Incompatible service for socket type" }, + { EAI_ADDRFAMILY, "Unavailable address family for host name" }, + { EAI_MEMORY, "Memory allocation failure" }, + { EAI_OVERFLOW, "Buffer overflow" }, + { EAI_SYSTEM, "System error" }, + { 0, "" }, +}; + +static const char gai_unknownerr[] = "Unrecognized error number"; + +/**************************************************************************** + * Converts an EAI_* error code into human readable english text. + ****************************************************************************/ +const char *vlc_gai_strerror (int errnum) +{ + for (unsigned i = 0; *gai_errlist[i].msg; i++) + if (errnum == gai_errlist[i].code) + return gai_errlist[i].msg; + + return gai_unknownerr; +} +#else /* ifndef HAVE_GAI_STRERROR */ +const char *vlc_gai_strerror (int errnum) +{ + return gai_strerror (errnum); +} +#endif + +#ifndef HAVE_GETNAMEINFO +#define _NI_MASK (NI_NUMERICHOST|NI_NUMERICSERV|NI_NOFQDN|NI_NAMEREQD|\ + NI_DGRAM) +/* + * getnameinfo() non-thread-safe IPv4-only implementation, + * Address-family-independent address to hostname translation + * (reverse DNS lookup in case of IPv4). + * + * This is meant for use on old IP-enabled systems that are not IPv6-aware, + * and probably do not have getnameinfo(), but have the old gethostbyaddr() + * function. + * + * GNU C library 2.0.x is known to lack this function, even though it defines + * getaddrinfo(). + */ +#ifdef WIN32 +static int WSAAPI +stub_getnameinfo (const struct sockaddr *sa, socklen_t salen, + char *host, DWORD hostlen, char *serv, DWORD servlen, int flags) +#else +static int +stub_getnameinfo (const struct sockaddr *sa, socklen_t salen, + char *host, int hostlen, char *serv, int servlen, int flags) +#endif +{ + if (((size_t)salen < sizeof (struct sockaddr_in)) + || (sa->sa_family != AF_INET)) + return EAI_FAMILY; + else if (flags & (~_NI_MASK)) + return EAI_BADFLAGS; + else + { + const struct sockaddr_in *addr; + + addr = (const struct sockaddr_in *)sa; + + if (host != NULL) + { + /* host name resolution */ + if (!(flags & NI_NUMERICHOST)) + { + if (flags & NI_NAMEREQD) + return EAI_NONAME; + } + + /* inet_ntoa() is not thread-safe, do not use it */ + uint32_t ipv4 = ntohl (addr->sin_addr.s_addr); + + if (snprintf (host, hostlen, "%u.%u.%u.%u", ipv4 >> 24, + (ipv4 >> 16) & 0xff, (ipv4 >> 8) & 0xff, + ipv4 & 0xff) >= (int)hostlen) + return EAI_OVERFLOW; + } + + if (serv != NULL) + { + if (snprintf (serv, servlen, "%u", + (unsigned int)ntohs (addr->sin_port)) >= (int)servlen) + return EAI_OVERFLOW; + } + } + return 0; +} +#undef getnameinfo +#define getnameifo stub_getnameinfo +#endif /* if !HAVE_GETNAMEINFO */ + +#ifndef HAVE_GETADDRINFO +#define _AI_MASK (AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST) +/* + * Converts the current herrno error value into an EAI_* error code. + * That error code is normally returned by getnameinfo() or getaddrinfo(). + */ +static int +gai_error_from_herrno (void) +{ + switch (h_errno) + { + case HOST_NOT_FOUND: + return EAI_NONAME; + + case NO_ADDRESS: +# if (NO_ADDRESS != NO_DATA) + case NO_DATA: +# endif + return EAI_NODATA; + + case NO_RECOVERY: + return EAI_FAIL; + + case TRY_AGAIN: + return EAI_AGAIN; + } + return EAI_SYSTEM; +} + +/* + * This functions must be used to free the memory allocated by getaddrinfo(). + */ +#ifdef WIN32 +static void WSAAPI stub_freeaddrinfo (struct addrinfo *res) +#else +static void stub_freeaddrinfo (struct addrinfo *res) +#endif +{ + if (res == NULL) + return; + free (res->ai_canonname); + free (res->ai_addr); + free (res->ai_next); + free (res); +} + + +/* + * Internal function that builds an addrinfo struct. + */ +static struct addrinfo * +makeaddrinfo (int af, int type, int proto, + const struct sockaddr *addr, size_t addrlen, + const char *canonname) +{ + struct addrinfo *res; + + res = (struct addrinfo *)malloc (sizeof (struct addrinfo)); + if (res != NULL) + { + res->ai_flags = 0; + res->ai_family = af; + res->ai_socktype = type; + res->ai_protocol = proto; + res->ai_addrlen = addrlen; + res->ai_addr = malloc (addrlen); + res->ai_canonname = NULL; + res->ai_next = NULL; + + if (res->ai_addr != NULL) + { + memcpy (res->ai_addr, addr, addrlen); + + if (canonname != NULL) + { + res->ai_canonname = strdup (canonname); + if (res->ai_canonname != NULL) + return res; /* success ! */ + } + else + return res; + } + } + /* failsafe */ + vlc_freeaddrinfo (res); + return NULL; +} + + +static struct addrinfo * +makeipv4info (int type, int proto, unsigned long ip, unsigned short port, const char *name) +{ + struct sockaddr_in addr; + + memset (&addr, 0, sizeof (addr)); + addr.sin_family = AF_INET; +# ifdef HAVE_SA_LEN + addr.sin_len = sizeof (addr); +# endif + addr.sin_port = port; + addr.sin_addr.s_addr = ip; + + return makeaddrinfo (AF_INET, type, proto, + (struct sockaddr*)&addr, sizeof (addr), name); +} + + +/* + * getaddrinfo() non-thread-safe IPv4-only implementation + * Address-family-independent hostname to address resolution. + * + * This is meant for IPv6-unaware systems that do probably not provide + * getaddrinfo(), but still have old function gethostbyname(). + * + * Only UDP and TCP over IPv4 are supported here. + */ +#ifdef WIN32 +static int WSAAPI +stub_getaddrinfo (const char *node, const char *service, + const struct addrinfo *hints, struct addrinfo **res) +#else +static int +stub_getaddrinfo (const char *node, const char *service, + const struct addrinfo *hints, struct addrinfo **res) +#endif +{ + struct addrinfo *info; + unsigned long ip; + unsigned short port; + int protocol = 0, flags = 0; + const char *name = NULL; + +#ifdef WIN32 + /* + * Maybe you knew already that Winsock does not handle TCP/RST packets + * properly, so that when a TCP connection fails, it will wait until it + * times out even if the remote host did return a TCP/RST. However, it + * still sees the TCP/RST as the error code is 10061 instead of 10060. + * Basically, we have the stupid brainfucked behavior with DNS queries... + * When the recursive DNS server returns an error, Winsock waits about + * 2 seconds before it returns to the callers, even though it should know + * that is pointless. I'd like to know how come this hasn't been fixed + * for the past decade, or maybe not. + * + * Anyway, this is causing a severe delay when the SAP listener tries + * to resolve more than ten IPv6 numeric addresses. Modern systems will + * eventually realize that it is an IPv6 address, and won't try to resolve + * it as a IPv4 address via the Domain Name Service. Old systems + * (including Windows XP without the IPv6 stack) will not. It is normally + * not an issue as the DNS server usually returns an error very quickly. + * But it IS a severe issue on Windows, given the bug explained above. + * So here comes one more bug-to-bug Windows compatibility fix. + */ + if ((node != NULL) && (strchr (node, ':') != NULL)) + return EAI_NONAME; +#endif + + if (hints != NULL) + { + flags = hints->ai_flags; + + if (flags & ~_AI_MASK) + return EAI_BADFLAGS; + /* only accept AF_INET and AF_UNSPEC */ + if (hints->ai_family && (hints->ai_family != AF_INET)) + return EAI_FAMILY; + + /* protocol sanity check */ + switch (hints->ai_socktype) + { + case SOCK_STREAM: + protocol = IPPROTO_TCP; + break; + + case SOCK_DGRAM: + protocol = IPPROTO_UDP; + break; + +#ifndef SOCK_RAW + case SOCK_RAW: +#endif + case 0: + break; + + default: + return EAI_SOCKTYPE; + } + if (hints->ai_protocol && protocol + && (protocol != hints->ai_protocol)) + return EAI_SERVICE; + } + + *res = NULL; + + /* default values */ + if (node == NULL) + { + if (flags & AI_PASSIVE) + ip = htonl (INADDR_ANY); + else + ip = htonl (INADDR_LOOPBACK); + } + else + if ((ip = inet_addr (node)) == INADDR_NONE) + { + struct hostent *entry = NULL; + + /* hostname resolution */ + if (!(flags & AI_NUMERICHOST)) + entry = gethostbyname (node); + + if (entry == NULL) + return gai_error_from_herrno (); + + if ((entry->h_length != 4) || (entry->h_addrtype != AF_INET)) + return EAI_FAMILY; + + ip = *((unsigned long *) entry->h_addr); + if (flags & AI_CANONNAME) + name = entry->h_name; + } + + if ((flags & AI_CANONNAME) && (name == NULL)) + name = node; + + /* service resolution */ + if (service == NULL) + port = 0; + else + { + unsigned long d; + char *end; + + d = strtoul (service, &end, 0); + if (end[0] || (d > 65535u)) + return EAI_SERVICE; + + port = htons ((unsigned short)d); + } + + /* building results... */ + if ((!protocol) || (protocol == IPPROTO_UDP)) + { + info = makeipv4info (SOCK_DGRAM, IPPROTO_UDP, ip, port, name); + if (info == NULL) + { + errno = ENOMEM; + return EAI_SYSTEM; + } + if (flags & AI_PASSIVE) + info->ai_flags |= AI_PASSIVE; + *res = info; + } + if ((!protocol) || (protocol == IPPROTO_TCP)) + { + info = makeipv4info (SOCK_STREAM, IPPROTO_TCP, ip, port, name); + if (info == NULL) + { + errno = ENOMEM; + return EAI_SYSTEM; + } + info->ai_next = *res; + if (flags & AI_PASSIVE) + info->ai_flags |= AI_PASSIVE; + *res = info; + } + + return 0; +} +#undef getaddrinfo +#define getaddrifo stub_getaddrinfo +#undef freeaddrinfo +#define freeaddrifo stub_freeaddrinfo +#endif /* if !HAVE_GETADDRINFO */ + +#if defined( WIN32 ) && !defined( UNDER_CE ) + /* + * Here is the kind of kludge you need to keep binary compatibility among + * varying OS versions... + */ +typedef int (WSAAPI * GETNAMEINFO) ( const struct sockaddr FAR *, socklen_t, + char FAR *, DWORD, char FAR *, DWORD, int ); +typedef int (WSAAPI * GETADDRINFO) (const char FAR *, const char FAR *, + const struct addrinfo FAR *, + struct addrinfo FAR * FAR *); + +typedef void (WSAAPI * FREEADDRINFO) ( struct addrinfo FAR * ); + +static int WSAAPI _ws2_getnameinfo_bind ( const struct sockaddr FAR *, socklen_t, + char FAR *, DWORD, char FAR *, DWORD, int ); +static int WSAAPI _ws2_getaddrinfo_bind (const char FAR *, const char FAR *, + const struct addrinfo FAR *, + struct addrinfo FAR * FAR *); + +static GETNAMEINFO ws2_getnameinfo = _ws2_getnameinfo_bind; +static GETADDRINFO ws2_getaddrinfo = _ws2_getaddrinfo_bind; +static FREEADDRINFO ws2_freeaddrinfo; + +static FARPROC ws2_find_api (LPCTSTR name) +{ + FARPROC f = NULL; + + HMODULE m = GetModuleHandle (TEXT("WS2_32")); + if (m != NULL) + f = GetProcAddress (m, name); + + if (f == NULL) + { + /* Windows 2K IPv6 preview */ + m = LoadLibrary (TEXT("WSHIP6")); + if (m != NULL) + f = GetProcAddress (m, name); + } + + return f; +} + +static WSAAPI int _ws2_getnameinfo_bind( const struct sockaddr FAR * sa, socklen_t salen, + char FAR *host, DWORD hostlen, char FAR *serv, DWORD servlen, int flags ) +{ + GETNAMEINFO entry = (GETNAMEINFO)ws2_find_api (TEXT("getnameinfo")); + int result; + + if (entry == NULL) + { + /* not found, use replacement API instead */ + entry = stub_getnameinfo; + } + /* call API before replacing function pointer to avoid crash */ + result = entry (sa, salen, host, hostlen, serv, servlen, flags); + ws2_getnameinfo = entry; + return result; +} +#undef getnameinfo +#define getnameinfo ws2_getnameinfo + +static WSAAPI int _ws2_getaddrinfo_bind(const char FAR *node, const char FAR *service, + const struct addrinfo FAR *hints, struct addrinfo FAR * FAR *res) +{ + GETADDRINFO entry; + FREEADDRINFO freentry; + int result; + + entry = (GETADDRINFO)ws2_find_api (TEXT("getaddrinfo")); + freentry = (FREEADDRINFO)ws2_find_api (TEXT("freeaddrinfo")); + + if ((entry == NULL) || (freentry == NULL)) + { + /* not found, use replacement API instead */ + entry = stub_getaddrinfo; + freentry = stub_freeaddrinfo; + } + /* call API before replacing function pointer to avoid crash */ + result = entry (node, service, hints, res); + ws2_freeaddrinfo = freentry; + ws2_getaddrinfo = entry; + return result; +} +#undef getaddrinfo +#undef freeaddrinfo +#define getaddrinfo ws2_getaddrinfo +#define freeaddrinfo ws2_freeaddrinfo +#define HAVE_GETADDRINFO +#endif + + +int vlc_getnameinfo( const struct sockaddr *sa, int salen, + char *host, int hostlen, int *portnum, int flags ) +{ + char psz_servbuf[6], *psz_serv; + int i_servlen, i_val; + + flags |= NI_NUMERICSERV; + if( portnum != NULL ) + { + psz_serv = psz_servbuf; + i_servlen = sizeof( psz_servbuf ); + } + else + { + psz_serv = NULL; + i_servlen = 0; + } + + i_val = getnameinfo(sa, salen, host, hostlen, psz_serv, i_servlen, flags); + + if( portnum != NULL ) + *portnum = atoi( psz_serv ); + + return i_val; +} + + +int vlc_getaddrinfo( vlc_object_t *p_this, const char *node, + int i_port, const struct addrinfo *p_hints, + struct addrinfo **res ) +{ + struct addrinfo hints; + char psz_buf[NI_MAXHOST], *psz_node, psz_service[6]; + + /* + * In VLC, we always use port number as integer rather than strings + * for historical reasons (and portability). + */ + if( ( i_port > 65535 ) || ( i_port < 0 ) ) + { + msg_Err( p_this, "invalid port number %d specified", i_port ); + return EAI_SERVICE; + } + + /* cannot overflow */ + snprintf( psz_service, 6, "%d", i_port ); + + /* Check if we have to force ipv4 or ipv6 */ + memset (&hints, 0, sizeof (hints)); + if (p_hints != NULL) + { + const int safe_flags = + AI_PASSIVE | + AI_CANONNAME | + AI_NUMERICHOST | + AI_NUMERICSERV | +#ifdef AI_ALL + AI_ALL | +#endif +#ifdef AI_ADDRCONFIG + AI_ADDRCONFIG | +#endif +#ifdef AI_V4MAPPED + AI_V4MAPPED | +#endif + 0; + + hints.ai_family = p_hints->ai_family; + hints.ai_socktype = p_hints->ai_socktype; + hints.ai_protocol = p_hints->ai_protocol; + /* Unfortunately, some flags chang the layout of struct addrinfo, so + * they cannot be copied blindly from p_hints to &hints. Therefore, we + * only copy flags that we know for sure are "safe". + */ + hints.ai_flags = p_hints->ai_flags & safe_flags; + } + + /* We only ever use port *numbers* */ + hints.ai_flags |= AI_NUMERICSERV; + + if( hints.ai_family == AF_UNSPEC ) + { +#ifdef AF_INET6 + if (var_CreateGetBool (p_this, "ipv6")) + hints.ai_family = AF_INET6; + else +#endif + if (var_CreateGetBool (p_this, "ipv4")) + hints.ai_family = AF_INET; + } + + /* + * VLC extensions : + * - accept "" as NULL + * - ignore square brackets + */ + if( ( node == NULL ) || (node[0] == '\0' ) ) + { + psz_node = NULL; + } + else + { + strlcpy( psz_buf, node, NI_MAXHOST ); + + psz_node = psz_buf; + + if( psz_buf[0] == '[' ) + { + char *ptr; + + ptr = strrchr( psz_buf, ']' ); + if( ( ptr != NULL ) && (ptr[1] == '\0' ) ) + { + *ptr = '\0'; + psz_node++; + } + } + } + +#ifdef WIN32 + /* + * Winsock tries to resolve numerical IPv4 addresses as AAAA + * and IPv6 addresses as A... There comes the bug-to-bug fix. + */ + if ((hints.ai_flags & AI_NUMERICHOST) == 0) + { + hints.ai_flags |= AI_NUMERICHOST; + + if (getaddrinfo (psz_node, psz_service, &hints, res) == 0) + return 0; + + hints.ai_flags &= ~AI_NUMERICHOST; + } +#endif +#ifdef AI_IDN + /* Run-time I18n Domain Names support */ + hints.ai_flags |= AI_IDN; + int ret = getaddrinfo (psz_node, psz_service, &hints, res); + if (ret != EAI_BADFLAGS) + return ret; + + /* IDN not available: disable and retry without it */ + hints.ai_flags &= ~AI_IDN; +#endif + return getaddrinfo (psz_node, psz_service, &hints, res); +} + + +void vlc_freeaddrinfo( struct addrinfo *infos ) +{ + freeaddrinfo (infos); +} + +/** + * inet_pton() replacement + */ +int vlc_inet_pton (int af, const char *src, void *dst) +{ +#ifndef HAVE_INET_PTON + /* Windows Vista has inet_pton(), but not XP. */ + /* We have a pretty good example of abstraction inversion here... */ + struct addrinfo hints = { + .ai_family = af, + .ai_socktype = SOCK_DGRAM, /* make sure we have... */ + .ai_protocol = IPPROTO_UDP, /* ...only one response */ + .ai_flags = AI_NUMERICHOST, + }, *res; + + if (getaddrinfo (src, NULL, &hints, &res)) + return 0; + + const void *data; + size_t len; + + switch (af) + { + case AF_INET: + data = &((const struct sockaddr_in *)res->ai_addr)->sin_addr; + len = sizeof (struct in_addr); + break; +#ifdef AF_INET6 + case AF_INET6: + data = &((const struct sockaddr_in6 *)res->ai_addr)->sin6_addr; + len = sizeof (struct in6_addr); + break; +#endif + default: + freeaddrinfo (res); + return -1; + } + memcpy (dst, data, len); + freeaddrinfo (res); + return 1; +#else /* HAVE_INET_PTON */ + return inet_pton( af, src, dst ); +#endif /* HAVE_INET_PTON */ +} + +/** + * inet_ntop() replacement + */ +const char *vlc_inet_ntop (int af, const void *src, char *dst, socklen_t cnt) +{ +#ifndef HAVE_INET_NTOP + int ret = EAI_FAMILY; + + switch (af) + { +#ifdef AF_INET6 + case AF_INET6: + { + struct sockaddr_in6 addr; + memset (&addr, 0, sizeof(addr)); + addr.sin6_family = AF_INET6; + addr.sin6_addr = *(struct in6_addr *)src; + ret = getnameinfo ((struct sockaddr *)&addr, sizeof (addr), + dst, cnt, NULL, 0, NI_NUMERICHOST); + } + +#endif + case AF_INET: + { + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr = *(struct in_addr *)src; + ret = getnameinfo ((struct sockaddr *)&addr, sizeof (addr), + dst, cnt, NULL, 0, NI_NUMERICHOST); + } + } + return (ret == 0) ? dst : NULL; +#else /* HAVE_INET_NTOP */ + return inet_ntop( af, src, dst, cnt ); +#endif /* HAVE_INET_NTOP */ +} + diff --git a/VLC/hierarchical_media_list_view.c b/VLC/hierarchical_media_list_view.c new file mode 100644 index 0000000..d3a4eda --- /dev/null +++ b/VLC/hierarchical_media_list_view.c @@ -0,0 +1,190 @@ +/***************************************************************************** + * hierarchical_media_list_view.c: libvlc hierarchical media list view functs. + ***************************************************************************** + * Copyright (C) 2007 the VideoLAN team + * $Id$ + * + * Authors: Pierre d'Herbemont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#include "libvlc_internal.h" +#include +#include +#include "vlc_arrays.h" + +//#define DEBUG_HIERARCHICAL_VIEW + +#ifdef DEBUG_HIERARCHICAL_VIEW +# define trace( fmt, ... ) printf( "%s(): " fmt, __FUNCTION__, ##__VA_ARGS__ ) +#else +# define trace( ... ) +#endif + +struct libvlc_media_list_view_private_t +{ + vlc_array_t array; +}; + +/* + * Private functions + */ + +/************************************************************************** + * flat_media_list_view_count (private) + * (called by media_list_view_count) + **************************************************************************/ +static int +hierarch_media_list_view_count( libvlc_media_list_view_t * p_mlv, + libvlc_exception_t * p_e ) +{ + return libvlc_media_list_count( p_mlv->p_mlist, p_e ); +} + +/************************************************************************** + * flat_media_list_view_item_at_index (private) + * (called by flat_media_list_view_item_at_index) + **************************************************************************/ +static libvlc_media_t * +hierarch_media_list_view_item_at_index( libvlc_media_list_view_t * p_mlv, + int index, + libvlc_exception_t * p_e ) +{ + return libvlc_media_list_item_at_index( p_mlv->p_mlist, index, p_e ); +} + +/************************************************************************** + * flat_media_list_view_item_at_index (private) + * (called by flat_media_list_view_item_at_index) + **************************************************************************/ +static libvlc_media_list_view_t * +hierarch_media_list_view_children_at_index( libvlc_media_list_view_t * p_mlv, + int index, + libvlc_exception_t * p_e ) +{ + libvlc_media_t * p_md; + libvlc_media_list_t * p_submlist; + libvlc_media_list_view_t * p_ret; + p_md = libvlc_media_list_item_at_index( p_mlv->p_mlist, index, p_e ); + if( !p_md ) return NULL; + p_submlist = libvlc_media_subitems( p_md, p_e ); + libvlc_media_release( p_md ); + if( !p_submlist ) return NULL; + p_ret = libvlc_media_list_hierarchical_view( p_submlist, p_e ); + libvlc_media_list_release( p_submlist ); + + return p_ret; +} + +/************************************************************************** + * media_list_(item|will)_* (private) (Event callback) + **************************************************************************/ +static void +media_list_item_added( const libvlc_event_t * p_event, void * user_data ) +{ + libvlc_media_t * p_md; + libvlc_media_list_view_t * p_mlv = user_data; + int index = p_event->u.media_list_item_added.index; + p_md = p_event->u.media_list_item_added.item; + libvlc_media_list_view_item_added( p_mlv, p_md, index ); +} +static void +media_list_will_add_item( const libvlc_event_t * p_event, void * user_data ) +{ + libvlc_media_t * p_md; + libvlc_media_list_view_t * p_mlv = user_data; + int index = p_event->u.media_list_will_add_item.index; + p_md = p_event->u.media_list_will_add_item.item; + libvlc_media_list_view_will_add_item( p_mlv, p_md, index ); +} +static void +media_list_item_deleted( const libvlc_event_t * p_event, void * user_data ) +{ + libvlc_media_t * p_md; + libvlc_media_list_view_t * p_mlv = user_data; + int index = p_event->u.media_list_item_deleted.index; + p_md = p_event->u.media_list_item_deleted.item; + libvlc_media_list_view_item_deleted( p_mlv, p_md, index ); +} +static void +media_list_will_delete_item( const libvlc_event_t * p_event, void * user_data ) +{ + libvlc_media_t * p_md; + libvlc_media_list_view_t * p_mlv = user_data; + int index = p_event->u.media_list_will_delete_item.index; + p_md = p_event->u.media_list_will_delete_item.item; + libvlc_media_list_view_will_delete_item( p_mlv, p_md, index ); +} + +/* + * Public libvlc functions + */ + + +/************************************************************************** + * flat_media_list_view_release (private) + * (called by media_list_view_release) + **************************************************************************/ +static void +hierarch_media_list_view_release( libvlc_media_list_view_t * p_mlv ) +{ + libvlc_event_detach( p_mlv->p_mlist->p_event_manager, + libvlc_MediaListItemAdded, + media_list_item_added, p_mlv, NULL ); + libvlc_event_detach( p_mlv->p_mlist->p_event_manager, + libvlc_MediaListWillAddItem, + media_list_will_add_item, p_mlv, NULL ); + libvlc_event_detach( p_mlv->p_mlist->p_event_manager, + libvlc_MediaListItemDeleted, + media_list_item_deleted, p_mlv, NULL ); + libvlc_event_detach( p_mlv->p_mlist->p_event_manager, + libvlc_MediaListWillDeleteItem, + media_list_will_delete_item, p_mlv, NULL ); +} + +/************************************************************************** + * libvlc_media_list_flat_view (Public) + **************************************************************************/ +libvlc_media_list_view_t * +libvlc_media_list_hierarchical_view( libvlc_media_list_t * p_mlist, + libvlc_exception_t * p_e ) +{ + trace("\n"); + libvlc_media_list_view_t * p_mlv; + p_mlv = libvlc_media_list_view_new( p_mlist, + hierarch_media_list_view_count, + hierarch_media_list_view_item_at_index, + hierarch_media_list_view_children_at_index, + libvlc_media_list_hierarchical_view, + hierarch_media_list_view_release, + NULL, + p_e ); + libvlc_media_list_lock( p_mlist ); + libvlc_event_attach( p_mlv->p_mlist->p_event_manager, + libvlc_MediaListItemAdded, + media_list_item_added, p_mlv, NULL ); + libvlc_event_attach( p_mlv->p_mlist->p_event_manager, + libvlc_MediaListWillAddItem, + media_list_will_add_item, p_mlv, NULL ); + libvlc_event_attach( p_mlv->p_mlist->p_event_manager, + libvlc_MediaListItemDeleted, + media_list_item_deleted, p_mlv, NULL ); + libvlc_event_attach( p_mlv->p_mlist->p_event_manager, + libvlc_MediaListWillDeleteItem, + media_list_will_delete_item, p_mlv, NULL ); + libvlc_media_list_unlock( p_mlist ); + return p_mlv; +} diff --git a/VLC/hierarchical_node_media_list_view.c b/VLC/hierarchical_node_media_list_view.c new file mode 100644 index 0000000..505ce8f --- /dev/null +++ b/VLC/hierarchical_node_media_list_view.c @@ -0,0 +1,309 @@ +/***************************************************************************** + * hierarchical_node_media_list_view.c: libvlc hierarchical nodes media list + * view functs. + ***************************************************************************** + * Copyright (C) 2007 the VideoLAN team + * $Id$ + * + * Authors: Pierre d'Herbemont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#include "libvlc_internal.h" +#include +#include +#include "vlc_arrays.h" + +/* FIXME: This version is probably a bit overheaded, and we may want to store + * the items in a vlc_array_t to speed everything up */ + +//#define DEBUG_HIERARCHICAL_VIEW + +#ifdef DEBUG_HIERARCHICAL_VIEW +# define trace( fmt, ... ) printf( "[HIERARCHICAL_NODE] %s(): " fmt, __FUNCTION__, ##__VA_ARGS__ ) +#else +# define trace( ... ) {} +#endif + +struct libvlc_media_list_view_private_t +{ + vlc_array_t array; +}; + +/* + * Private functions + */ + +/************************************************************************** + * flat_media_list_view_count (private) + * (called by media_list_view_count) + **************************************************************************/ +static int +hierarch_node_media_list_view_count( libvlc_media_list_view_t * p_mlv, + libvlc_exception_t * p_e ) +{ + /* FIXME: we may want to cache that */ + int i, ret, count = libvlc_media_list_count( p_mlv->p_mlist, p_e ); + libvlc_media_t * p_md; + libvlc_media_list_t * p_submlist; + ret = 0; + trace("\n"); + for( i = 0; i < count; i++ ) + { + p_md = libvlc_media_list_item_at_index( p_mlv->p_mlist, i, p_e ); + if( !p_md ) continue; + p_submlist = libvlc_media_subitems( p_md, p_e ); + if( !p_submlist ) continue; + libvlc_media_release( p_md ); + libvlc_media_list_release( p_submlist ); + ret++; + } + return ret; +} + +/************************************************************************** + * flat_media_list_view_item_at_index (private) + * (called by flat_media_list_view_item_at_index) + **************************************************************************/ +static libvlc_media_t * +hierarch_node_media_list_view_item_at_index( libvlc_media_list_view_t * p_mlv, + int index, + libvlc_exception_t * p_e ) +{ + /* FIXME: we may want to cache that */ + libvlc_media_t * p_md; + libvlc_media_list_t * p_submlist; + trace("%d\n", index); + int i, current_index, count = libvlc_media_list_count( p_mlv->p_mlist, p_e ); + current_index = -1; + for( i = 0; i < count; i++ ) + { + p_md = libvlc_media_list_item_at_index( p_mlv->p_mlist, i, p_e ); + if( !p_md ) continue; + p_submlist = libvlc_media_subitems( p_md, p_e ); + if( !p_submlist ) continue; + libvlc_media_list_release( p_submlist ); + current_index++; + if( current_index == index ) + return p_md; + libvlc_media_release( p_md ); + } + + libvlc_exception_raise( p_e, "Index out of bound in Media List View" ); + return NULL; +} + +/************************************************************************** + * flat_media_list_view_item_at_index (private) + * (called by flat_media_list_view_item_at_index) + **************************************************************************/ +static libvlc_media_list_view_t * +hierarch_node_media_list_view_children_at_index( libvlc_media_list_view_t * p_mlv, + int index, + libvlc_exception_t * p_e ) +{ + libvlc_media_t * p_md; + libvlc_media_list_t * p_submlist; + libvlc_media_list_view_t * p_ret; + p_md = hierarch_node_media_list_view_item_at_index( p_mlv, index, p_e ); + if( !p_md ) return NULL; + p_submlist = libvlc_media_subitems( p_md, p_e ); + libvlc_media_release( p_md ); + if( !p_submlist ) return NULL; + p_ret = libvlc_media_list_hierarchical_node_view( p_submlist, p_e ); + libvlc_media_list_release( p_submlist ); + + return p_ret; +} + +/* Helper */ +static int +index_of_item( libvlc_media_list_view_t * p_mlv, libvlc_media_t * p_md ) +{ + libvlc_media_t * p_iter_md; + libvlc_media_list_t * p_submlist; + + int i, current_index, count = libvlc_media_list_count( p_mlv->p_mlist, NULL ); + current_index = -1; + for( i = 0; i < count; i++ ) + { + p_iter_md = libvlc_media_list_item_at_index( p_mlv->p_mlist, i, NULL ); + if( !p_iter_md ) continue; + p_submlist = libvlc_media_subitems( p_iter_md, NULL ); + if( !p_submlist ) continue; + libvlc_media_list_release( p_submlist ); + libvlc_media_release( p_iter_md ); + current_index++; + if( p_md == p_iter_md ) + return current_index; + } + return -1; +} + +static bool +item_is_already_added( libvlc_media_t * p_md ) +{ + libvlc_media_list_t * p_submlist; + + p_submlist = libvlc_media_subitems( p_md, NULL ); + if( !p_submlist ) return false; + int count = libvlc_media_list_count( p_submlist, NULL ); + libvlc_media_list_release( p_submlist ); + return count > 1; +} + + +/************************************************************************** + * media_list_(item|will)_* (private) (Event callback) + **************************************************************************/ +static void +items_subitems_added( const libvlc_event_t * p_event, void * user_data ) +{ + libvlc_media_t * p_md; + libvlc_media_list_view_t * p_mlv = user_data; + int index; + p_md = p_event->p_obj; + if( !item_is_already_added( p_md ) ) + { + index = index_of_item( p_mlv, p_md ); + trace("%d\n", index); + if( index >= 0 ) + { + libvlc_media_list_view_will_add_item( p_mlv, p_md, index ); + libvlc_media_list_view_item_added( p_mlv, p_md, index ); + } + } + else + { + trace("item already added\n"); + } +} + +static void +media_list_item_added( const libvlc_event_t * p_event, void * user_data ) +{ + libvlc_media_t * p_md; + libvlc_media_list_view_t * p_mlv = user_data; + int index; + p_md = p_event->u.media_list_item_added.item; + index = index_of_item( p_mlv, p_md ); + trace("%d\n", index); + if( index >= 0) + libvlc_media_list_view_item_added( p_mlv, p_md, index ); + libvlc_event_attach( p_md->p_event_manager, libvlc_MediaSubItemAdded, + items_subitems_added, p_mlv, NULL ); + +} +static void +media_list_will_add_item( const libvlc_event_t * p_event, void * user_data ) +{ + libvlc_media_t * p_md; + libvlc_media_list_view_t * p_mlv = user_data; + int index; + p_md = p_event->u.media_list_will_add_item.item; + index = index_of_item( p_mlv, p_md ); + trace("%d\n", index); + if( index >= 0) + libvlc_media_list_view_will_add_item( p_mlv, p_md, index ); +} +static void +media_list_item_deleted( const libvlc_event_t * p_event, void * user_data ) +{ + libvlc_media_t * p_md; + libvlc_media_list_view_t * p_mlv = user_data; + int index; + p_md = p_event->u.media_list_item_deleted.item; + index = index_of_item( p_mlv, p_md ); + trace("%d\n", index); + if( index >= 0) + libvlc_media_list_view_item_deleted( p_mlv, p_md, index ); + libvlc_event_detach( p_md->p_event_manager, libvlc_MediaSubItemAdded, + items_subitems_added, p_mlv, NULL ); +} +static void +media_list_will_delete_item( const libvlc_event_t * p_event, void * user_data ) +{ + libvlc_media_t * p_md; + libvlc_media_list_view_t * p_mlv = user_data; + int index; + p_md = p_event->u.media_list_will_delete_item.item; + index = index_of_item( p_mlv, p_md ); + trace("%d\n", index); + if( index >= 0) + libvlc_media_list_view_will_delete_item( p_mlv, p_md, index ); +} + + +/* + * Public libvlc functions + */ + + +/************************************************************************** + * flat_media_list_view_release (private) + * (called by media_list_view_release) + **************************************************************************/ +static void +hierarch_node_media_list_view_release( libvlc_media_list_view_t * p_mlv ) +{ + trace("\n"); + libvlc_event_detach( p_mlv->p_mlist->p_event_manager, + libvlc_MediaListItemAdded, + media_list_item_added, p_mlv, NULL ); + libvlc_event_detach( p_mlv->p_mlist->p_event_manager, + libvlc_MediaListWillAddItem, + media_list_will_add_item, p_mlv, NULL ); + libvlc_event_detach( p_mlv->p_mlist->p_event_manager, + libvlc_MediaListItemDeleted, + media_list_item_deleted, p_mlv, NULL ); + libvlc_event_detach( p_mlv->p_mlist->p_event_manager, + libvlc_MediaListWillDeleteItem, + media_list_will_delete_item, p_mlv, NULL ); +} + +/************************************************************************** + * libvlc_media_list_flat_view (Public) + **************************************************************************/ +libvlc_media_list_view_t * +libvlc_media_list_hierarchical_node_view( libvlc_media_list_t * p_mlist, + libvlc_exception_t * p_e ) +{ + trace("\n"); + libvlc_media_list_view_t * p_mlv; + p_mlv = libvlc_media_list_view_new( p_mlist, + hierarch_node_media_list_view_count, + hierarch_node_media_list_view_item_at_index, + hierarch_node_media_list_view_children_at_index, + libvlc_media_list_hierarchical_node_view, + hierarch_node_media_list_view_release, + NULL, + p_e ); + libvlc_media_list_lock( p_mlist ); + libvlc_event_attach( p_mlv->p_mlist->p_event_manager, + libvlc_MediaListItemAdded, + media_list_item_added, p_mlv, NULL ); + libvlc_event_attach( p_mlv->p_mlist->p_event_manager, + libvlc_MediaListWillAddItem, + media_list_will_add_item, p_mlv, NULL ); + libvlc_event_attach( p_mlv->p_mlist->p_event_manager, + libvlc_MediaListItemDeleted, + media_list_item_deleted, p_mlv, NULL ); + libvlc_event_attach( p_mlv->p_mlist->p_event_manager, + libvlc_MediaListWillDeleteItem, + media_list_will_delete_item, p_mlv, NULL ); + libvlc_media_list_unlock( p_mlist ); + return p_mlv; +} diff --git a/VLC/httpd.c b/VLC/httpd.c new file mode 100644 index 0000000..9569801 --- /dev/null +++ b/VLC/httpd.c @@ -0,0 +1,2717 @@ +/***************************************************************************** + * httpd.c + ***************************************************************************** + * Copyright (C) 2004-2006 the VideoLAN team + * Copyright © 2004-2007 Rémi Denis-Courmont + * $Id$ + * + * Authors: Laurent Aimar + * Rémi Denis-Courmont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "vlc_httpd.h" + +#ifdef ENABLE_HTTPD + +#include + +#include +#include +#include +#include +#include "../libvlc.h" + +#include +#include + +#ifdef HAVE_UNISTD_H +# include +#endif + +#ifdef HAVE_FCNTL_H +# include +#endif + +#ifdef HAVE_POLL +# include +#endif + +#if defined( UNDER_CE ) +# include +#elif defined( WIN32 ) +# include +#else +# include +#endif + +#if defined( WIN32 ) +/* We need HUGE buffer otherwise TCP throughput is very limited */ +#define HTTPD_CL_BUFSIZE 1000000 +#else +#define HTTPD_CL_BUFSIZE 10000 +#endif + +static void httpd_ClientClean( httpd_client_t *cl ); + +struct httpd_t +{ + VLC_COMMON_MEMBERS + + int i_host; + httpd_host_t **host; +}; + + +/* each host run in his own thread */ +struct httpd_host_t +{ + VLC_COMMON_MEMBERS + + httpd_t *httpd; + + /* ref count */ + int i_ref; + + /* address/port and socket for listening at connections */ + char *psz_hostname; + int i_port; + int *fds; + unsigned nfd; + + vlc_mutex_t lock; + + /* all registered url (becarefull that 2 httpd_url_t could point at the same url) + * This will slow down the url research but make my live easier + * All url will have their cb trigger, but only the first one can answer + * */ + int i_url; + httpd_url_t **url; + + int i_client; + httpd_client_t **client; + + /* TLS data */ + tls_server_t *p_tls; +}; + + +struct httpd_url_t +{ + httpd_host_t *host; + + vlc_mutex_t lock; + + char *psz_url; + char *psz_user; + char *psz_password; + vlc_acl_t *p_acl; + + struct + { + httpd_callback_t cb; + httpd_callback_sys_t *p_sys; + } catch[HTTPD_MSG_MAX]; +}; + +/* status */ +enum +{ + HTTPD_CLIENT_RECEIVING, + HTTPD_CLIENT_RECEIVE_DONE, + + HTTPD_CLIENT_SENDING, + HTTPD_CLIENT_SEND_DONE, + + HTTPD_CLIENT_WAITING, + + HTTPD_CLIENT_DEAD, + + HTTPD_CLIENT_TLS_HS_IN, + HTTPD_CLIENT_TLS_HS_OUT +}; + +/* mode */ +enum +{ + HTTPD_CLIENT_FILE, /* default */ + HTTPD_CLIENT_STREAM, /* regulary get data from cb */ + HTTPD_CLIENT_BIDIR, /* check for reading and get data from cb */ +}; + +struct httpd_client_t +{ + httpd_url_t *url; + + int i_ref; + + int fd; + + int i_mode; + int i_state; + int b_read_waiting; /* stop as soon as possible sending */ + + mtime_t i_activity_date; + mtime_t i_activity_timeout; + + /* buffer for reading header */ + int i_buffer_size; + int i_buffer; + uint8_t *p_buffer; + + /* */ + httpd_message_t query; /* client -> httpd */ + httpd_message_t answer; /* httpd -> client */ + + /* TLS data */ + tls_session_t *p_tls; +}; + + +/***************************************************************************** + * Various functions + *****************************************************************************/ +static const struct +{ + const char psz_ext[8]; + const char *psz_mime; +} http_mime[] = +{ + { ".htm", "text/html" }, + { ".html", "text/html" }, + { ".txt", "text/plain" }, + { ".xml", "text/xml" }, + { ".dtd", "text/dtd" }, + + { ".css", "text/css" }, + + /* image mime */ + { ".gif", "image/gif" }, + { ".jpe", "image/jpeg" }, + { ".jpg", "image/jpeg" }, + { ".jpeg", "image/jpeg" }, + { ".png", "image/png" }, + /* same as modules/mux/mpjpeg.c here: */ + { ".mpjpeg","multipart/x-mixed-replace; boundary=7b3cc56e5f51db803f790dad720ed50a" }, + + /* media mime */ + { ".avi", "video/avi" }, + { ".asf", "video/x-ms-asf" }, + { ".m1a", "audio/mpeg" }, + { ".m2a", "audio/mpeg" }, + { ".m1v", "video/mpeg" }, + { ".m2v", "video/mpeg" }, + { ".mp2", "audio/mpeg" }, + { ".mp3", "audio/mpeg" }, + { ".mpa", "audio/mpeg" }, + { ".mpg", "video/mpeg" }, + { ".mpeg", "video/mpeg" }, + { ".mpe", "video/mpeg" }, + { ".mov", "video/quicktime" }, + { ".moov", "video/quicktime" }, + { ".ogg", "application/ogg" }, + { ".ogm", "application/ogg" }, + { ".wav", "audio/wav" }, + { ".wma", "audio/x-ms-wma" }, + { ".wmv", "video/x-ms-wmv" }, + + + /* end */ + { "", "" } +}; + +static const char *httpd_MimeFromUrl( const char *psz_url ) +{ + + char *psz_ext; + + psz_ext = strrchr( psz_url, '.' ); + if( psz_ext ) + { + int i; + + for( i = 0; http_mime[i].psz_ext[0] ; i++ ) + { + if( !strcasecmp( http_mime[i].psz_ext, psz_ext ) ) + { + return http_mime[i].psz_mime; + } + } + } + return "application/octet-stream"; +} + + +typedef struct +{ + unsigned i_code; + const char psz_reason[36]; +} http_status_info; + +static const http_status_info http_reason[] = +{ + /*{ 100, "Continue" }, + { 101, "Switching Protocols" },*/ + { 200, "OK" }, + /*{ 201, "Created" }, + { 202, "Accepted" }, + { 203, "Non-authoritative information" }, + { 204, "No content" }, + { 205, "Reset content" }, + { 206, "Partial content" }, + { 250, "Low on storage space" }, + { 300, "Multiple choices" },*/ + { 301, "Moved permanently" }, + /*{ 302, "Moved temporarily" }, + { 303, "See other" }, + { 304, "Not modified" }, + { 305, "Use proxy" }, + { 307, "Temporary redirect" }, + { 400, "Bad request" },*/ + { 401, "Unauthorized" }, + /*{ 402, "Payment Required" },*/ + { 403, "Forbidden" }, + { 404, "Not found" }, + { 405, "Method not allowed" }, + /*{ 406, "Not acceptable" }, + { 407, "Proxy authentication required" }, + { 408, "Request time-out" }, + { 409, "Conflict" }, + { 410, "Gone" }, + { 411, "Length required" }, + { 412, "Precondition failed" }, + { 413, "Request entity too large" }, + { 414, "Request-URI too large" }, + { 415, "Unsupported media Type" }, + { 416, "Requested range not satisfiable" }, + { 417, "Expectation failed" }, + { 451, "Parameter not understood" }, + { 452, "Conference not found" }, + { 453, "Not enough bandwidth" },*/ + { 454, "Session not found" }, + /*{ 455, "Method not valid in this State" },*/ + { 456, "Header field not valid for resource" }, + /*{ 457, "Invalid range" }, + { 458, "Read-only parameter" },*/ + { 459, "Aggregate operation not allowed" }, + { 460, "Non-aggregate operation not allowed" }, + { 461, "Unsupported transport" }, + /*{ 462, "Destination unreachable" },*/ + { 500, "Internal server error" }, + { 501, "Not implemented" }, + /*{ 502, "Bad gateway" },*/ + { 503, "Service unavailable" }, + /*{ 504, "Gateway time-out" },*/ + { 505, "Protocol version not supported" }, + { 551, "Option not supported" }, + { 999, "" } +}; + +static const char psz_fallback_reason[5][16] = +{ "Continue", "OK", "Found", "Client error", "Server error" }; + +static const char *httpd_ReasonFromCode( unsigned i_code ) +{ + const http_status_info *p; + + assert( ( i_code >= 100 ) && ( i_code <= 599 ) ); + + for (p = http_reason; i_code > p->i_code; p++); + + if( p->i_code == i_code ) + return p->psz_reason; + + return psz_fallback_reason[(i_code / 100) - 1]; +} + + +static size_t httpd_HtmlError (char **body, int code, const char *url) +{ + const char *errname = httpd_ReasonFromCode (code); + assert (errname != NULL); + + int res = asprintf (body, + "\n" + "\n" + "\n" + "\n" + "%s\n" + "\n" + "\n" + "

%d %s%s%s%s

\n" + "
\n" + "VideoLAN\n" + "\n" + "\n", errname, code, errname, + (url ? " (" : ""), (url ?: ""), (url ? ")" : "")); + + if (res == -1) + { + *body = NULL; + return 0; + } + + return (size_t)res; +} + + +/***************************************************************************** + * High Level Functions: httpd_file_t + *****************************************************************************/ +struct httpd_file_t +{ + httpd_url_t *url; + + char *psz_url; + char *psz_mime; + + httpd_file_callback_t pf_fill; + httpd_file_sys_t *p_sys; + +}; + +static int +httpd_FileCallBack( httpd_callback_sys_t *p_sys, httpd_client_t *cl, + httpd_message_t *answer, const httpd_message_t *query ) +{ + httpd_file_t *file = (httpd_file_t*)p_sys; + uint8_t **pp_body, *p_body; + const char *psz_connection; + int *pi_body, i_body; + + if( answer == NULL || query == NULL ) + { + return VLC_SUCCESS; + } + answer->i_proto = HTTPD_PROTO_HTTP; + answer->i_version= 1; + answer->i_type = HTTPD_MSG_ANSWER; + + answer->i_status = 200; + + httpd_MsgAdd( answer, "Content-type", "%s", file->psz_mime ); + httpd_MsgAdd( answer, "Cache-Control", "%s", "no-cache" ); + + if( query->i_type != HTTPD_MSG_HEAD ) + { + pp_body = &answer->p_body; + pi_body = &answer->i_body; + } + else + { + /* The file still needs to be executed. */ + p_body = NULL; + i_body = 0; + pp_body = &p_body; + pi_body = &i_body; + } + + if( query->i_type == HTTPD_MSG_POST ) + { + /* msg_Warn not supported */ + } + + uint8_t *psz_args = query->psz_args; + file->pf_fill( file->p_sys, file, psz_args, pp_body, pi_body ); + + if( query->i_type == HTTPD_MSG_HEAD && p_body != NULL ) + { + free( p_body ); + } + + /* We respect client request */ + psz_connection = httpd_MsgGet( &cl->query, "Connection" ); + if( psz_connection != NULL ) + { + httpd_MsgAdd( answer, "Connection", "%s", psz_connection ); + } + + httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body ); + + return VLC_SUCCESS; +} + +httpd_file_t *httpd_FileNew( httpd_host_t *host, + const char *psz_url, const char *psz_mime, + const char *psz_user, const char *psz_password, + const vlc_acl_t *p_acl, httpd_file_callback_t pf_fill, + httpd_file_sys_t *p_sys ) +{ + httpd_file_t *file = malloc( sizeof( httpd_file_t ) ); + + if( ( file->url = httpd_UrlNewUnique( host, psz_url, psz_user, + psz_password, p_acl ) + ) == NULL ) + { + free( file ); + return NULL; + } + + file->psz_url = strdup( psz_url ); + if( psz_mime && *psz_mime ) + { + file->psz_mime = strdup( psz_mime ); + } + else + { + file->psz_mime = strdup( httpd_MimeFromUrl( psz_url ) ); + } + + file->pf_fill = pf_fill; + file->p_sys = p_sys; + + httpd_UrlCatch( file->url, HTTPD_MSG_HEAD, httpd_FileCallBack, + (httpd_callback_sys_t*)file ); + httpd_UrlCatch( file->url, HTTPD_MSG_GET, httpd_FileCallBack, + (httpd_callback_sys_t*)file ); + httpd_UrlCatch( file->url, HTTPD_MSG_POST, httpd_FileCallBack, + (httpd_callback_sys_t*)file ); + + return file; +} + +httpd_file_sys_t *httpd_FileDelete( httpd_file_t *file ) +{ + httpd_file_sys_t *p_sys = file->p_sys; + + httpd_UrlDelete( file->url ); + + free( file->psz_url ); + free( file->psz_mime ); + + free( file ); + + return p_sys; +} + +/***************************************************************************** + * High Level Functions: httpd_handler_t (for CGIs) + *****************************************************************************/ +struct httpd_handler_t +{ + httpd_url_t *url; + + httpd_handler_callback_t pf_fill; + httpd_handler_sys_t *p_sys; + +}; + +static int +httpd_HandlerCallBack( httpd_callback_sys_t *p_sys, httpd_client_t *cl, + httpd_message_t *answer, const httpd_message_t *query ) +{ + httpd_handler_t *handler = (httpd_handler_t*)p_sys; + char psz_remote_addr[NI_MAXNUMERICHOST]; + + if( answer == NULL || query == NULL ) + { + return VLC_SUCCESS; + } + answer->i_proto = HTTPD_PROTO_NONE; + answer->i_type = HTTPD_MSG_ANSWER; + + /* We do it ourselves, thanks */ + answer->i_status = 0; + + if( httpd_ClientIP( cl, psz_remote_addr ) == NULL ) + *psz_remote_addr = '\0'; + + uint8_t *psz_args = query->psz_args; + handler->pf_fill( handler->p_sys, handler, query->psz_url, psz_args, + query->i_type, query->p_body, query->i_body, + psz_remote_addr, NULL, + &answer->p_body, &answer->i_body ); + + if( query->i_type == HTTPD_MSG_HEAD ) + { + char *p = (char *)answer->p_body; + + /* Looks for end of header (i.e. one empty line) */ + while ( (p = strchr( p, '\r' )) != NULL ) + { + if( p[1] && p[1] == '\n' && p[2] && p[2] == '\r' + && p[3] && p[3] == '\n' ) + { + break; + } + } + + if( p != NULL ) + { + p[4] = '\0'; + answer->i_body = strlen((char*)answer->p_body) + 1; + answer->p_body = realloc( answer->p_body, answer->i_body ); + } + } + + if( strncmp( (char *)answer->p_body, "HTTP/1.", 7 ) ) + { + int i_status, i_headers; + char *psz_headers, *psz_new; + const char *psz_status; + + if( !strncmp( (char *)answer->p_body, "Status: ", 8 ) ) + { + /* Apache-style */ + i_status = strtol( (char *)&answer->p_body[8], &psz_headers, 0 ); + if( *psz_headers ) psz_headers++; + if( *psz_headers ) psz_headers++; + i_headers = answer->i_body - (psz_headers - (char *)answer->p_body); + } + else + { + i_status = 200; + psz_headers = (char *)answer->p_body; + i_headers = answer->i_body; + } + + psz_status = httpd_ReasonFromCode( i_status ); + answer->i_body = sizeof("HTTP/1.0 xxx \r\n") + + strlen(psz_status) + i_headers - 1; + psz_new = (char *)malloc( answer->i_body + 1); + sprintf( psz_new, "HTTP/1.0 %03d %s\r\n", i_status, psz_status ); + memcpy( &psz_new[strlen(psz_new)], psz_headers, i_headers ); + free( answer->p_body ); + answer->p_body = (uint8_t *)psz_new; + } + + return VLC_SUCCESS; +} + +httpd_handler_t *httpd_HandlerNew( httpd_host_t *host, const char *psz_url, + const char *psz_user, + const char *psz_password, + const vlc_acl_t *p_acl, + httpd_handler_callback_t pf_fill, + httpd_handler_sys_t *p_sys ) +{ + httpd_handler_t *handler = malloc( sizeof( httpd_handler_t ) ); + + if( ( handler->url = httpd_UrlNewUnique( host, psz_url, psz_user, + psz_password, p_acl ) + ) == NULL ) + { + free( handler ); + return NULL; + } + + handler->pf_fill = pf_fill; + handler->p_sys = p_sys; + + httpd_UrlCatch( handler->url, HTTPD_MSG_HEAD, httpd_HandlerCallBack, + (httpd_callback_sys_t*)handler ); + httpd_UrlCatch( handler->url, HTTPD_MSG_GET, httpd_HandlerCallBack, + (httpd_callback_sys_t*)handler ); + httpd_UrlCatch( handler->url, HTTPD_MSG_POST, httpd_HandlerCallBack, + (httpd_callback_sys_t*)handler ); + + return handler; +} + +httpd_handler_sys_t *httpd_HandlerDelete( httpd_handler_t *handler ) +{ + httpd_handler_sys_t *p_sys = handler->p_sys; + httpd_UrlDelete( handler->url ); + free( handler ); + return p_sys; +} + +/***************************************************************************** + * High Level Functions: httpd_redirect_t + *****************************************************************************/ +struct httpd_redirect_t +{ + httpd_url_t *url; + char *psz_dst; +}; + +static int httpd_RedirectCallBack( httpd_callback_sys_t *p_sys, + httpd_client_t *cl, httpd_message_t *answer, + const httpd_message_t *query ) +{ + httpd_redirect_t *rdir = (httpd_redirect_t*)p_sys; + char *p_body; + (void)cl; + + if( answer == NULL || query == NULL ) + { + return VLC_SUCCESS; + } + answer->i_proto = HTTPD_PROTO_HTTP; + answer->i_version= 1; + answer->i_type = HTTPD_MSG_ANSWER; + answer->i_status = 301; + + answer->i_body = httpd_HtmlError (&p_body, 301, rdir->psz_dst); + answer->p_body = (unsigned char *)p_body; + + /* XXX check if it's ok or we need to set an absolute url */ + httpd_MsgAdd( answer, "Location", "%s", rdir->psz_dst ); + + httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body ); + + return VLC_SUCCESS; +} + +httpd_redirect_t *httpd_RedirectNew( httpd_host_t *host, const char *psz_url_dst, + const char *psz_url_src ) +{ + httpd_redirect_t *rdir = malloc( sizeof( httpd_redirect_t ) ); + + if( !( rdir->url = httpd_UrlNewUnique( host, psz_url_src, NULL, NULL, NULL ) ) ) + { + free( rdir ); + return NULL; + } + rdir->psz_dst = strdup( psz_url_dst ); + + /* Redirect apply for all HTTP request and RTSP DESCRIBE resquest */ + httpd_UrlCatch( rdir->url, HTTPD_MSG_HEAD, httpd_RedirectCallBack, + (httpd_callback_sys_t*)rdir ); + httpd_UrlCatch( rdir->url, HTTPD_MSG_GET, httpd_RedirectCallBack, + (httpd_callback_sys_t*)rdir ); + httpd_UrlCatch( rdir->url, HTTPD_MSG_POST, httpd_RedirectCallBack, + (httpd_callback_sys_t*)rdir ); + httpd_UrlCatch( rdir->url, HTTPD_MSG_DESCRIBE, httpd_RedirectCallBack, + (httpd_callback_sys_t*)rdir ); + + return rdir; +} +void httpd_RedirectDelete( httpd_redirect_t *rdir ) +{ + httpd_UrlDelete( rdir->url ); + free( rdir->psz_dst ); + free( rdir ); +} + +/***************************************************************************** + * High Level Funtions: httpd_stream_t + *****************************************************************************/ +struct httpd_stream_t +{ + vlc_mutex_t lock; + httpd_url_t *url; + + char *psz_mime; + + /* Header to send as first packet */ + uint8_t *p_header; + int i_header; + + /* circular buffer */ + int i_buffer_size; /* buffer size, can't be reallocated smaller */ + uint8_t *p_buffer; /* buffer */ + int64_t i_buffer_pos; /* absolute position from begining */ + int64_t i_buffer_last_pos; /* a new connection will start with that */ +}; + +static int httpd_StreamCallBack( httpd_callback_sys_t *p_sys, + httpd_client_t *cl, httpd_message_t *answer, + const httpd_message_t *query ) +{ + httpd_stream_t *stream = (httpd_stream_t*)p_sys; + + if( answer == NULL || query == NULL || cl == NULL ) + { + return VLC_SUCCESS; + } + + if( answer->i_body_offset > 0 ) + { + int64_t i_write; + int i_pos; + +#if 0 + fprintf( stderr, "httpd_StreamCallBack i_body_offset=%lld\n", + answer->i_body_offset ); +#endif + + if( answer->i_body_offset >= stream->i_buffer_pos ) + { + /* fprintf( stderr, "httpd_StreamCallBack: no data\n" ); */ + return VLC_EGENERIC; /* wait, no data available */ + } + if( answer->i_body_offset + stream->i_buffer_size < + stream->i_buffer_pos ) + { + /* this client isn't fast enough */ +#if 0 + fprintf( stderr, "fixing i_body_offset (old=%lld new=%lld)\n", + answer->i_body_offset, stream->i_buffer_last_pos ); +#endif + answer->i_body_offset = stream->i_buffer_last_pos; + } + + i_pos = answer->i_body_offset % stream->i_buffer_size; + i_write = stream->i_buffer_pos - answer->i_body_offset; + if( i_write > HTTPD_CL_BUFSIZE ) + { + i_write = HTTPD_CL_BUFSIZE; + } + else if( i_write <= 0 ) + { + return VLC_EGENERIC; /* wait, no data available */ + } + + /* Don't go past the end of the circular buffer */ + i_write = __MIN( i_write, stream->i_buffer_size - i_pos ); + + /* using HTTPD_MSG_ANSWER -> data available */ + answer->i_proto = HTTPD_PROTO_HTTP; + answer->i_version= 0; + answer->i_type = HTTPD_MSG_ANSWER; + + answer->i_body = i_write; + answer->p_body = malloc( i_write ); + memcpy( answer->p_body, &stream->p_buffer[i_pos], i_write ); + + answer->i_body_offset += i_write; + + return VLC_SUCCESS; + } + else + { + answer->i_proto = HTTPD_PROTO_HTTP; + answer->i_version= 0; + answer->i_type = HTTPD_MSG_ANSWER; + + answer->i_status = 200; + + if( query->i_type != HTTPD_MSG_HEAD ) + { + httpd_ClientModeStream( cl ); + vlc_mutex_lock( &stream->lock ); + /* Send the header */ + if( stream->i_header > 0 ) + { + answer->i_body = stream->i_header; + answer->p_body = malloc( stream->i_header ); + memcpy( answer->p_body, stream->p_header, stream->i_header ); + } + answer->i_body_offset = stream->i_buffer_last_pos; + vlc_mutex_unlock( &stream->lock ); + } + else + { + httpd_MsgAdd( answer, "Content-Length", "%d", 0 ); + answer->i_body_offset = 0; + } + + if( !strcmp( stream->psz_mime, "video/x-ms-asf-stream" ) ) + { + bool b_xplaystream = false; + int i; + + httpd_MsgAdd( answer, "Content-type", "%s", + "application/octet-stream" ); + httpd_MsgAdd( answer, "Server", "Cougar 4.1.0.3921" ); + httpd_MsgAdd( answer, "Pragma", "no-cache" ); + httpd_MsgAdd( answer, "Pragma", "client-id=%d", rand()&0x7fff ); + httpd_MsgAdd( answer, "Pragma", "features=\"broadcast\"" ); + + /* Check if there is a xPlayStrm=1 */ + for( i = 0; i < query->i_name; i++ ) + { + if( !strcasecmp( query->name[i], "Pragma" ) && + strstr( query->value[i], "xPlayStrm=1" ) ) + { + b_xplaystream = true; + } + } + + if( !b_xplaystream ) + { + answer->i_body_offset = 0; + } + } + else + { + httpd_MsgAdd( answer, "Content-type", "%s", stream->psz_mime ); + } + httpd_MsgAdd( answer, "Cache-Control", "%s", "no-cache" ); + return VLC_SUCCESS; + } +} + +httpd_stream_t *httpd_StreamNew( httpd_host_t *host, + const char *psz_url, const char *psz_mime, + const char *psz_user, const char *psz_password, + const vlc_acl_t *p_acl ) +{ + httpd_stream_t *stream = malloc( sizeof( httpd_stream_t ) ); + + if( ( stream->url = httpd_UrlNewUnique( host, psz_url, psz_user, + psz_password, p_acl ) + ) == NULL ) + { + free( stream ); + return NULL; + } + vlc_mutex_init( &stream->lock ); + if( psz_mime && *psz_mime ) + { + stream->psz_mime = strdup( psz_mime ); + } + else + { + stream->psz_mime = strdup( httpd_MimeFromUrl( psz_url ) ); + } + stream->i_header = 0; + stream->p_header = NULL; + stream->i_buffer_size = 5000000; /* 5 Mo per stream */ + stream->p_buffer = malloc( stream->i_buffer_size ); + /* We set to 1 to make life simpler + * (this way i_body_offset can never be 0) */ + stream->i_buffer_pos = 1; + stream->i_buffer_last_pos = 1; + + httpd_UrlCatch( stream->url, HTTPD_MSG_HEAD, httpd_StreamCallBack, + (httpd_callback_sys_t*)stream ); + httpd_UrlCatch( stream->url, HTTPD_MSG_GET, httpd_StreamCallBack, + (httpd_callback_sys_t*)stream ); + httpd_UrlCatch( stream->url, HTTPD_MSG_POST, httpd_StreamCallBack, + (httpd_callback_sys_t*)stream ); + + return stream; +} + +int httpd_StreamHeader( httpd_stream_t *stream, uint8_t *p_data, int i_data ) +{ + vlc_mutex_lock( &stream->lock ); + free( stream->p_header ); + stream->p_header = NULL; + + stream->i_header = i_data; + if( i_data > 0 ) + { + stream->p_header = malloc( i_data ); + memcpy( stream->p_header, p_data, i_data ); + } + vlc_mutex_unlock( &stream->lock ); + + return VLC_SUCCESS; +} + +int httpd_StreamSend( httpd_stream_t *stream, uint8_t *p_data, int i_data ) +{ + int i_count; + int i_pos; + + if( i_data < 0 || p_data == NULL ) + { + return VLC_SUCCESS; + } + vlc_mutex_lock( &stream->lock ); + + /* save this pointer (to be used by new connection) */ + stream->i_buffer_last_pos = stream->i_buffer_pos; + + i_pos = stream->i_buffer_pos % stream->i_buffer_size; + i_count = i_data; + while( i_count > 0) + { + int i_copy; + + i_copy = __MIN( i_count, stream->i_buffer_size - i_pos ); + + /* Ok, we can't go past the end of our buffer */ + memcpy( &stream->p_buffer[i_pos], p_data, i_copy ); + + i_pos = ( i_pos + i_copy ) % stream->i_buffer_size; + i_count -= i_copy; + p_data += i_copy; + } + + stream->i_buffer_pos += i_data; + + vlc_mutex_unlock( &stream->lock ); + return VLC_SUCCESS; +} + +void httpd_StreamDelete( httpd_stream_t *stream ) +{ + httpd_UrlDelete( stream->url ); + vlc_mutex_destroy( &stream->lock ); + free( stream->psz_mime ); + free( stream->p_header ); + free( stream->p_buffer ); + free( stream ); +} + +/***************************************************************************** + * Low level + *****************************************************************************/ +static void* httpd_HostThread( vlc_object_t * ); + +/* create a new host */ +httpd_host_t *httpd_HostNew( vlc_object_t *p_this, const char *psz_host, + int i_port ) +{ + return httpd_TLSHostNew( p_this, psz_host, i_port, NULL, NULL, NULL, NULL + ); +} + +static const char psz_object_type[] = "http server"; + +httpd_host_t *httpd_TLSHostNew( vlc_object_t *p_this, const char *psz_hostname, + int i_port, + const char *psz_cert, const char *psz_key, + const char *psz_ca, const char *psz_crl ) +{ + httpd_t *httpd; + httpd_host_t *host; + tls_server_t *p_tls; + char *psz_host; + vlc_value_t lockval, ptrval; + int i; + + if( psz_hostname == NULL ) + psz_hostname = ""; + + psz_host = strdup( psz_hostname ); + if( psz_host == NULL ) + return NULL; + + /* to be sure to avoid multiple creation */ + var_Create( p_this->p_libvlc, "httpd_mutex", VLC_VAR_MUTEX ); + var_Get( p_this->p_libvlc, "httpd_mutex", &lockval ); + vlc_mutex_lock( lockval.p_address ); + httpd = libvlc_priv (p_this->p_libvlc)->p_httpd; + + if( httpd == NULL ) + { + msg_Info( p_this, "creating httpd" ); + httpd = (httpd_t *)vlc_custom_create( p_this, sizeof (*httpd), + VLC_OBJECT_GENERIC, + psz_object_type ); + if( httpd == NULL ) + { + vlc_mutex_unlock( lockval.p_address ); + free( psz_host ); + return NULL; + } + + httpd->i_host = 0; + httpd->host = NULL; + + ptrval.p_address = httpd; + libvlc_priv (p_this->p_libvlc)->p_httpd = httpd; + vlc_object_yield( httpd ); + vlc_object_attach( httpd, p_this->p_libvlc ); + } + + /* verify if it already exist */ + for( i = httpd->i_host - 1; i >= 0; i-- ) + { + host = httpd->host[i]; + + /* cannot mix TLS and non-TLS hosts */ + if( ( ( httpd->host[i]->p_tls != NULL ) != ( psz_cert != NULL ) ) + || ( host->i_port != i_port ) + || strcmp( host->psz_hostname, psz_hostname ) ) + continue; + + /* yep found */ + host->i_ref++; + + vlc_mutex_unlock( lockval.p_address ); + return host; + } + + host = NULL; + + /* determine TLS configuration */ + if ( psz_cert != NULL ) + { + p_tls = tls_ServerCreate( p_this, psz_cert, psz_key ); + if ( p_tls == NULL ) + { + msg_Err( p_this, "TLS initialization error" ); + goto error; + } + + if ( ( psz_ca != NULL) && tls_ServerAddCA( p_tls, psz_ca ) ) + { + msg_Err( p_this, "TLS CA error" ); + goto error; + } + + if ( ( psz_crl != NULL) && tls_ServerAddCRL( p_tls, psz_crl ) ) + { + msg_Err( p_this, "TLS CRL error" ); + goto error; + } + } + else + p_tls = NULL; + + /* create the new host */ + host = (httpd_host_t *)vlc_custom_create( p_this, sizeof (*host), + VLC_OBJECT_GENERIC, + psz_object_type ); + if (host == NULL) + goto error; + + vlc_object_lock( host ); + if( vlc_object_waitpipe( VLC_OBJECT( host ) ) == -1 ) + { + msg_Err( host, "signaling pipe error: %m" ); + vlc_object_unlock( host ); + goto error; + } + vlc_object_unlock( host ); + + host->httpd = httpd; + vlc_mutex_init( &host->lock ); + host->i_ref = 1; + + host->fds = net_ListenTCP( p_this, psz_host, i_port ); + if( host->fds == NULL ) + { + msg_Err( p_this, "cannot create socket(s) for HTTP host" ); + goto error; + } + for (host->nfd = 0; host->fds[host->nfd] != -1; host->nfd++); + + host->i_port = i_port; + host->psz_hostname = psz_host; + + host->i_url = 0; + host->url = NULL; + host->i_client = 0; + host->client = NULL; + + host->p_tls = p_tls; + + /* create the thread */ + if( vlc_thread_create( host, "httpd host thread", httpd_HostThread, + VLC_THREAD_PRIORITY_LOW, false ) ) + { + msg_Err( p_this, "cannot spawn http host thread" ); + goto error; + } + + /* now add it to httpd */ + TAB_APPEND( httpd->i_host, httpd->host, host ); + vlc_mutex_unlock( lockval.p_address ); + + return host; + +error: + free( psz_host ); + if( httpd->i_host <= 0 ) + { + vlc_object_release( httpd ); + vlc_object_detach( httpd ); + vlc_object_release( httpd ); + } + vlc_mutex_unlock( lockval.p_address ); + + if( host != NULL ) + { + net_ListenClose( host->fds ); + vlc_mutex_destroy( &host->lock ); + vlc_object_release( host ); + } + + if( p_tls != NULL ) + tls_ServerDelete( p_tls ); + + return NULL; +} + +/* delete a host */ +void httpd_HostDelete( httpd_host_t *host ) +{ + httpd_t *httpd = host->httpd; + vlc_value_t lockval; + int i; + + var_Get( httpd->p_libvlc, "httpd_mutex", &lockval ); + vlc_mutex_lock( lockval.p_address ); + + host->i_ref--; + if( host->i_ref > 0 ) + { + /* still used */ + vlc_mutex_unlock( lockval.p_address ); + msg_Dbg( host, "httpd_HostDelete: host still used" ); + return; + } + TAB_REMOVE( httpd->i_host, httpd->host, host ); + + vlc_object_kill( host ); + vlc_thread_join( host ); + + msg_Dbg( host, "HTTP host removed" ); + + for( i = 0; i < host->i_url; i++ ) + { + msg_Err( host, "url still registered: %s", host->url[i]->psz_url ); + } + for( i = 0; i < host->i_client; i++ ) + { + httpd_client_t *cl = host->client[i]; + msg_Warn( host, "client still connected" ); + httpd_ClientClean( cl ); + TAB_REMOVE( host->i_client, host->client, cl ); + free( cl ); + i--; + /* TODO */ + } + + if( host->p_tls != NULL) + tls_ServerDelete( host->p_tls ); + + net_ListenClose( host->fds ); + free( host->psz_hostname ); + + vlc_mutex_destroy( &host->lock ); + vlc_object_release( host ); + + vlc_object_release( httpd ); + if( httpd->i_host <= 0 ) + { + msg_Dbg( httpd, "no host left, stopping httpd" ); + + libvlc_priv (httpd->p_libvlc)->p_httpd = NULL; + vlc_object_detach( httpd ); + vlc_object_release( httpd ); + + } + vlc_mutex_unlock( lockval.p_address ); +} + +/* register a new url */ +static httpd_url_t *httpd_UrlNewPrivate( httpd_host_t *host, const char *psz_url, + const char *psz_user, const char *psz_password, + const vlc_acl_t *p_acl, bool b_check ) +{ + httpd_url_t *url; + int i; + + assert( psz_url != NULL ); + + vlc_mutex_lock( &host->lock ); + if( b_check ) + { + for( i = 0; i < host->i_url; i++ ) + { + if( !strcmp( psz_url, host->url[i]->psz_url ) ) + { + msg_Warn( host->httpd, + "cannot add '%s' (url already defined)", psz_url ); + vlc_mutex_unlock( &host->lock ); + return NULL; + } + } + } + + url = malloc( sizeof( httpd_url_t ) ); + url->host = host; + + vlc_mutex_init( &url->lock ); + url->psz_url = strdup( psz_url ); + url->psz_user = strdup( psz_user ? psz_user : "" ); + url->psz_password = strdup( psz_password ? psz_password : "" ); + url->p_acl = ACL_Duplicate( host, p_acl ); + for( i = 0; i < HTTPD_MSG_MAX; i++ ) + { + url->catch[i].cb = NULL; + url->catch[i].p_sys = NULL; + } + + TAB_APPEND( host->i_url, host->url, url ); + vlc_mutex_unlock( &host->lock ); + + return url; +} + +httpd_url_t *httpd_UrlNew( httpd_host_t *host, const char *psz_url, + const char *psz_user, const char *psz_password, + const vlc_acl_t *p_acl ) +{ + return httpd_UrlNewPrivate( host, psz_url, psz_user, + psz_password, p_acl, false ); +} + +httpd_url_t *httpd_UrlNewUnique( httpd_host_t *host, const char *psz_url, + const char *psz_user, const char *psz_password, + const vlc_acl_t *p_acl ) +{ + return httpd_UrlNewPrivate( host, psz_url, psz_user, + psz_password, p_acl, true ); +} + +/* register callback on a url */ +int httpd_UrlCatch( httpd_url_t *url, int i_msg, httpd_callback_t cb, + httpd_callback_sys_t *p_sys ) +{ + vlc_mutex_lock( &url->lock ); + url->catch[i_msg].cb = cb; + url->catch[i_msg].p_sys= p_sys; + vlc_mutex_unlock( &url->lock ); + + return VLC_SUCCESS; +} + +/* delete an url */ +void httpd_UrlDelete( httpd_url_t *url ) +{ + httpd_host_t *host = url->host; + int i; + + vlc_mutex_lock( &host->lock ); + TAB_REMOVE( host->i_url, host->url, url ); + + vlc_mutex_destroy( &url->lock ); + free( url->psz_url ); + free( url->psz_user ); + free( url->psz_password ); + ACL_Destroy( url->p_acl ); + + for( i = 0; i < host->i_client; i++ ) + { + httpd_client_t *client = host->client[i]; + + if( client->url == url ) + { + /* TODO complete it */ + msg_Warn( host, "force closing connections" ); + httpd_ClientClean( client ); + TAB_REMOVE( host->i_client, host->client, client ); + free( client ); + i--; + } + } + free( url ); + vlc_mutex_unlock( &host->lock ); +} + +void httpd_MsgInit( httpd_message_t *msg ) +{ + msg->cl = NULL; + msg->i_type = HTTPD_MSG_NONE; + msg->i_proto = HTTPD_PROTO_NONE; + msg->i_version = -1; /* FIXME */ + + msg->i_status = 0; + + msg->psz_url = NULL; + msg->psz_args = NULL; + + msg->i_channel = -1; + + msg->i_name = 0; + msg->name = NULL; + msg->i_value = 0; + msg->value = NULL; + + msg->i_body_offset = 0; + msg->i_body = 0; + msg->p_body = NULL; +} + +void httpd_MsgClean( httpd_message_t *msg ) +{ + int i; + + free( msg->psz_url ); + free( msg->psz_args ); + for( i = 0; i < msg->i_name; i++ ) + { + free( msg->name[i] ); + free( msg->value[i] ); + } + free( msg->name ); + free( msg->value ); + free( msg->p_body ); + httpd_MsgInit( msg ); +} + +const char *httpd_MsgGet( const httpd_message_t *msg, const char *name ) +{ + int i; + + for( i = 0; i < msg->i_name; i++ ) + { + if( !strcasecmp( msg->name[i], name )) + { + return msg->value[i]; + } + } + return NULL; +} + +void httpd_MsgAdd( httpd_message_t *msg, const char *name, const char *psz_value, ... ) +{ + va_list args; + char *value = NULL; + + va_start( args, psz_value ); + if( vasprintf( &value, psz_value, args ) == -1 ) + value = NULL; + va_end( args ); + + if( value == NULL ) + return; + + name = strdup( name ); + if( name == NULL ) + { + free( value ); + return; + } + + TAB_APPEND( msg->i_name, msg->name, (char*)name ); + TAB_APPEND( msg->i_value, msg->value, value ); +} + +static void httpd_ClientInit( httpd_client_t *cl, mtime_t now ) +{ + cl->i_state = HTTPD_CLIENT_RECEIVING; + cl->i_activity_date = now; + cl->i_activity_timeout = INT64_C(10000000); + cl->i_buffer_size = HTTPD_CL_BUFSIZE; + cl->i_buffer = 0; + cl->p_buffer = malloc( cl->i_buffer_size ); + cl->i_mode = HTTPD_CLIENT_FILE; + cl->b_read_waiting = false; + + httpd_MsgInit( &cl->query ); + httpd_MsgInit( &cl->answer ); +} + +void httpd_ClientModeStream( httpd_client_t *cl ) +{ + cl->i_mode = HTTPD_CLIENT_STREAM; +} + +void httpd_ClientModeBidir( httpd_client_t *cl ) +{ + cl->i_mode = HTTPD_CLIENT_BIDIR; +} + +char* httpd_ClientIP( const httpd_client_t *cl, char *psz_ip ) +{ + return net_GetPeerAddress( cl->fd, psz_ip, NULL ) ? NULL : psz_ip; +} + +char* httpd_ServerIP( const httpd_client_t *cl, char *psz_ip ) +{ + return net_GetSockAddress( cl->fd, psz_ip, NULL ) ? NULL : psz_ip; +} + +static void httpd_ClientClean( httpd_client_t *cl ) +{ + if( cl->fd >= 0 ) + { + if( cl->p_tls != NULL ) + tls_ServerSessionClose( cl->p_tls ); + net_Close( cl->fd ); + cl->fd = -1; + } + + httpd_MsgClean( &cl->answer ); + httpd_MsgClean( &cl->query ); + + free( cl->p_buffer ); + cl->p_buffer = NULL; +} + +static httpd_client_t *httpd_ClientNew( int fd, tls_session_t *p_tls, mtime_t now ) +{ + httpd_client_t *cl = malloc( sizeof( httpd_client_t ) ); + + if( !cl ) return NULL; + + cl->i_ref = 0; + cl->fd = fd; + cl->url = NULL; + cl->p_tls = p_tls; + + httpd_ClientInit( cl, now ); + + return cl; +} + +static +ssize_t httpd_NetRecv (httpd_client_t *cl, uint8_t *p, size_t i_len) +{ + tls_session_t *p_tls; + ssize_t val; + + p_tls = cl->p_tls; + do + val = p_tls ? tls_Recv (p_tls, p, i_len) + : recv (cl->fd, p, i_len, 0); + while (val == -1 && errno == EINTR); + return val; +} + +static +ssize_t httpd_NetSend (httpd_client_t *cl, const uint8_t *p, size_t i_len) +{ + tls_session_t *p_tls; + ssize_t val; + + p_tls = cl->p_tls; + do + val = p_tls ? tls_Send( p_tls, p, i_len ) + : send (cl->fd, p, i_len, 0); + while (val == -1 && errno == EINTR); + return val; +} + + +static const struct +{ + const char name[16]; + int i_type; + int i_proto; +} +msg_type[] = +{ + { "OPTIONS", HTTPD_MSG_OPTIONS, HTTPD_PROTO_RTSP }, + { "DESCRIBE", HTTPD_MSG_DESCRIBE, HTTPD_PROTO_RTSP }, + { "SETUP", HTTPD_MSG_SETUP, HTTPD_PROTO_RTSP }, + { "PLAY", HTTPD_MSG_PLAY, HTTPD_PROTO_RTSP }, + { "PAUSE", HTTPD_MSG_PAUSE, HTTPD_PROTO_RTSP }, + { "GET_PARAMETER", HTTPD_MSG_GETPARAMETER, HTTPD_PROTO_RTSP }, + { "TEARDOWN", HTTPD_MSG_TEARDOWN, HTTPD_PROTO_RTSP }, + { "GET", HTTPD_MSG_GET, HTTPD_PROTO_HTTP }, + { "HEAD", HTTPD_MSG_HEAD, HTTPD_PROTO_HTTP }, + { "POST", HTTPD_MSG_POST, HTTPD_PROTO_HTTP }, + { "", HTTPD_MSG_NONE, HTTPD_PROTO_NONE } +}; + + +static void httpd_ClientRecv( httpd_client_t *cl ) +{ + int i_len; + + /* ignore leading whites */ + if( ( cl->query.i_proto == HTTPD_PROTO_NONE ) && + ( cl->i_buffer == 0 ) ) + { + unsigned char c; + + i_len = httpd_NetRecv( cl, &c, 1 ); + + if( ( i_len > 0 ) && ( strchr( "\r\n\t ", c ) == NULL ) ) + { + cl->p_buffer[0] = c; + cl->i_buffer++; + } + } + else + if( cl->query.i_proto == HTTPD_PROTO_NONE ) + { + /* enough to see if it's Interleaved RTP over RTSP or RTSP/HTTP */ + i_len = httpd_NetRecv( cl, &cl->p_buffer[cl->i_buffer], + 7 - cl->i_buffer ); + if( i_len > 0 ) + { + cl->i_buffer += i_len; + } + + if( ( cl->i_buffer >= 4 ) && ( cl->p_buffer[0] == '$' ) ) + { + /* Interleaved RTP over RTSP */ + cl->query.i_proto = HTTPD_PROTO_RTSP; + cl->query.i_type = HTTPD_MSG_CHANNEL; + cl->query.i_channel = cl->p_buffer[1]; + cl->query.i_body = (cl->p_buffer[2] << 8)|cl->p_buffer[3]; + cl->query.p_body = malloc( cl->query.i_body ); + cl->i_buffer -= 4; + memcpy( cl->query.p_body, cl->p_buffer + 4, cl->i_buffer ); + } + else + /* The smallest legal request is 7 bytes ("GET /\r\n"), + * this is the maximum we can ask at this point. */ + if( cl->i_buffer >= 7 ) + { + if( !memcmp( cl->p_buffer, "HTTP/1.", 7 ) ) + { + cl->query.i_proto = HTTPD_PROTO_HTTP; + cl->query.i_type = HTTPD_MSG_ANSWER; + } + else if( !memcmp( cl->p_buffer, "RTSP/1.", 7 ) ) + { + cl->query.i_proto = HTTPD_PROTO_RTSP; + cl->query.i_type = HTTPD_MSG_ANSWER; + } + else + { + /* We need the full request line to determine the protocol. */ + cl->query.i_proto = HTTPD_PROTO_HTTP0; + cl->query.i_type = HTTPD_MSG_NONE; + } + } + } + else if( cl->query.i_body > 0 ) + { + /* we are reading the body of a request or a channel */ + i_len = httpd_NetRecv( cl, &cl->query.p_body[cl->i_buffer], + cl->query.i_body - cl->i_buffer ); + if( i_len > 0 ) + { + cl->i_buffer += i_len; + } + if( cl->i_buffer >= cl->query.i_body ) + { + cl->i_state = HTTPD_CLIENT_RECEIVE_DONE; + } + } + else + { + /* we are reading a header -> char by char */ + for( ;; ) + { + if( cl->i_buffer == cl->i_buffer_size ) + { + uint8_t *newbuf = realloc( cl->p_buffer, cl->i_buffer_size + 1024 ); + if( newbuf == NULL ) + { + i_len = 0; + break; + } + + cl->p_buffer = newbuf; + cl->i_buffer_size += 1024; + } + + i_len = httpd_NetRecv (cl, &cl->p_buffer[cl->i_buffer], 1 ); + if( i_len <= 0 ) + { + break; + } + cl->i_buffer++; + + if( ( cl->query.i_proto == HTTPD_PROTO_HTTP0 ) + && ( cl->p_buffer[cl->i_buffer - 1] == '\n' ) ) + { + /* Request line is now complete */ + const char *p = memchr( cl->p_buffer, ' ', cl->i_buffer ); + size_t len; + + assert( cl->query.i_type == HTTPD_MSG_NONE ); + + if( p == NULL ) /* no URI: evil guy */ + { + i_len = 0; /* drop connection */ + break; + } + + do + p++; /* skips extra spaces */ + while( *p == ' ' ); + + p = memchr( p, ' ', ((char *)cl->p_buffer) + cl->i_buffer - p ); + if( p == NULL ) /* no explicit protocol: HTTP/0.9 */ + { + i_len = 0; /* not supported currently -> drop */ + break; + } + + do + p++; /* skips extra spaces ever again */ + while( *p == ' ' ); + + len = ((char *)cl->p_buffer) + cl->i_buffer - p; + if( len < 7 ) /* foreign protocol */ + i_len = 0; /* I don't understand -> drop */ + else + if( memcmp( p, "HTTP/1.", 7 ) == 0 ) + { + cl->query.i_proto = HTTPD_PROTO_HTTP; + cl->query.i_version = atoi( p + 7 ); + } + else + if( memcmp( p, "RTSP/1.", 7 ) == 0 ) + { + cl->query.i_proto = HTTPD_PROTO_RTSP; + cl->query.i_version = atoi( p + 7 ); + } + else + if( memcmp( p, "HTTP/", 5 ) == 0 ) + { + const uint8_t sorry[] = + "HTTP/1.1 505 Unknown HTTP version\r\n\r\n"; + httpd_NetSend( cl, sorry, sizeof( sorry ) - 1 ); + i_len = 0; /* drop */ + } + else + if( memcmp( p, "RTSP/", 5 ) == 0 ) + { + const uint8_t sorry[] = + "RTSP/1.0 505 Unknown RTSP version\r\n\r\n"; + httpd_NetSend( cl, sorry, sizeof( sorry ) - 1 ); + i_len = 0; /* drop */ + } + else /* yet another foreign protocol */ + i_len = 0; + + if( i_len == 0 ) + break; + } + + if( ( cl->i_buffer >= 2 && !memcmp( &cl->p_buffer[cl->i_buffer-2], "\n\n", 2 ) )|| + ( cl->i_buffer >= 4 && !memcmp( &cl->p_buffer[cl->i_buffer-4], "\r\n\r\n", 4 ) ) ) + { + char *p; + + /* we have finished the header so parse it and set i_body */ + cl->p_buffer[cl->i_buffer] = '\0'; + + if( cl->query.i_type == HTTPD_MSG_ANSWER ) + { + /* FIXME: + * assume strlen( "HTTP/1.x" ) = 8 + */ + cl->query.i_status = + strtol( (char *)&cl->p_buffer[8], + &p, 0 ); + while( *p == ' ' ) + p++; + } + else + { + unsigned i; + + p = NULL; + cl->query.i_type = HTTPD_MSG_NONE; + + /*fprintf( stderr, "received new request=%s\n", cl->p_buffer);*/ + + for( i = 0; msg_type[i].name[0]; i++ ) + { + if( !strncmp( (char *)cl->p_buffer, msg_type[i].name, + strlen( msg_type[i].name ) ) ) + { + p = (char *)&cl->p_buffer[strlen(msg_type[i].name) + 1 ]; + cl->query.i_type = msg_type[i].i_type; + if( cl->query.i_proto != msg_type[i].i_proto ) + { + p = NULL; + cl->query.i_proto = HTTPD_PROTO_NONE; + cl->query.i_type = HTTPD_MSG_NONE; + } + break; + } + } + if( p == NULL ) + { + if( strstr( (char *)cl->p_buffer, "HTTP/1." ) ) + { + cl->query.i_proto = HTTPD_PROTO_HTTP; + } + else if( strstr( (char *)cl->p_buffer, "RTSP/1." ) ) + { + cl->query.i_proto = HTTPD_PROTO_RTSP; + } + } + else + { + char *p2; + char *p3; + + while( *p == ' ' ) + { + p++; + } + p2 = strchr( p, ' ' ); + if( p2 ) + { + *p2++ = '\0'; + } + if( !strncasecmp( p, "rtsp:", 5 ) ) + { + /* for rtsp url, you have rtsp://localhost:port/path */ + p += 5; + while( *p == '/' ) p++; + while( *p && *p != '/' ) p++; + } + cl->query.psz_url = strdup( p ); + if( ( p3 = strchr( cl->query.psz_url, '?' ) ) ) + { + *p3++ = '\0'; + cl->query.psz_args = (uint8_t *)strdup( p3 ); + } + p = p2; + } + } + if( p ) + { + p = strchr( p, '\n' ); + } + if( p ) + { + while( *p == '\n' || *p == '\r' ) + { + p++; + } + while( p && *p != '\0' ) + { + char *line = p; + char *eol = p = strchr( p, '\n' ); + char *colon; + + while( eol && eol >= line && ( *eol == '\n' || *eol == '\r' ) ) + { + *eol-- = '\0'; + } + + if( ( colon = strchr( line, ':' ) ) ) + { + char *name; + char *value; + + *colon++ = '\0'; + while( *colon == ' ' ) + { + colon++; + } + name = strdup( line ); + value = strdup( colon ); + + TAB_APPEND( cl->query.i_name, cl->query.name, name ); + TAB_APPEND( cl->query.i_value,cl->query.value,value); + + if( !strcasecmp( name, "Content-Length" ) ) + { + cl->query.i_body = atol( value ); + } + } + + if( p ) + { + p++; + while( *p == '\n' || *p == '\r' ) + { + p++; + } + } + } + } + if( cl->query.i_body > 0 ) + { + /* TODO Mhh, handle the case client will only send a + * request and close the connection + * to mark and of body (probably only RTSP) */ + cl->query.p_body = malloc( cl->query.i_body ); + cl->i_buffer = 0; + } + else + { + cl->i_state = HTTPD_CLIENT_RECEIVE_DONE; + } + } + } + } + + /* check if the client is to be set to dead */ +#if defined( WIN32 ) || defined( UNDER_CE ) + if( ( i_len < 0 && WSAGetLastError() != WSAEWOULDBLOCK ) || ( i_len == 0 ) ) +#else + if( ( i_len < 0 && errno != EAGAIN ) || ( i_len == 0 ) ) +#endif + { + if( cl->query.i_proto != HTTPD_PROTO_NONE && cl->query.i_type != HTTPD_MSG_NONE ) + { + /* connection closed -> end of data */ + if( cl->query.i_body > 0 ) + { + cl->query.i_body = cl->i_buffer; + } + cl->i_state = HTTPD_CLIENT_RECEIVE_DONE; + } + else + { + cl->i_state = HTTPD_CLIENT_DEAD; + } + } + + /* XXX: for QT I have to disable timeout. Try to find why */ + if( cl->query.i_proto == HTTPD_PROTO_RTSP ) + cl->i_activity_timeout = 0; + +#if 0 /* Debugging only */ + if( cl->i_state == HTTPD_CLIENT_RECEIVE_DONE ) + { + int i; + + fprintf( stderr, "received new request\n" ); + fprintf( stderr, " - proto=%s\n", + cl->query.i_proto == HTTPD_PROTO_HTTP ? "HTTP" : "RTSP" ); + fprintf( stderr, " - version=%d\n", cl->query.i_version ); + fprintf( stderr, " - msg=%d\n", cl->query.i_type ); + if( cl->query.i_type == HTTPD_MSG_ANSWER ) + { + fprintf( stderr, " - answer=%d '%s'\n", cl->query.i_status, + cl->query.psz_status ); + } + else if( cl->query.i_type != HTTPD_MSG_NONE ) + { + fprintf( stderr, " - url=%s\n", cl->query.psz_url ); + } + for( i = 0; i < cl->query.i_name; i++ ) + { + fprintf( stderr, " - option name='%s' value='%s'\n", + cl->query.name[i], cl->query.value[i] ); + } + } +#endif +} + +static void httpd_ClientSend( httpd_client_t *cl ) +{ + int i; + int i_len; + + if( cl->i_buffer < 0 ) + { + /* We need to create the header */ + int i_size = 0; + char *p; + const char *psz_status = httpd_ReasonFromCode( cl->answer.i_status ); + + i_size = strlen( "HTTP/1.") + 10 + 10 + strlen( psz_status ) + 5; + for( i = 0; i < cl->answer.i_name; i++ ) + { + i_size += strlen( cl->answer.name[i] ) + 2 + + strlen( cl->answer.value[i] ) + 2; + } + + if( cl->i_buffer_size < i_size ) + { + cl->i_buffer_size = i_size; + free( cl->p_buffer ); + cl->p_buffer = malloc( i_size ); + } + p = (char *)cl->p_buffer; + + p += sprintf( p, "%s.%u %d %s\r\n", + cl->answer.i_proto == HTTPD_PROTO_HTTP ? "HTTP/1" : "RTSP/1", + cl->answer.i_version, + cl->answer.i_status, psz_status ); + for( i = 0; i < cl->answer.i_name; i++ ) + { + p += sprintf( p, "%s: %s\r\n", cl->answer.name[i], + cl->answer.value[i] ); + } + p += sprintf( p, "\r\n" ); + + cl->i_buffer = 0; + cl->i_buffer_size = (uint8_t*)p - cl->p_buffer; + + /*fprintf( stderr, "sending answer\n" ); + fprintf( stderr, "%s", cl->p_buffer );*/ + } + + i_len = httpd_NetSend( cl, &cl->p_buffer[cl->i_buffer], + cl->i_buffer_size - cl->i_buffer ); + if( i_len >= 0 ) + { + cl->i_buffer += i_len; + + if( cl->i_buffer >= cl->i_buffer_size ) + { + if( cl->answer.i_body == 0 && cl->answer.i_body_offset > 0 && + !cl->b_read_waiting ) + { + /* catch more body data */ + int i_msg = cl->query.i_type; + int64_t i_offset = cl->answer.i_body_offset; + + httpd_MsgClean( &cl->answer ); + cl->answer.i_body_offset = i_offset; + + cl->url->catch[i_msg].cb( cl->url->catch[i_msg].p_sys, cl, + &cl->answer, &cl->query ); + } + + if( cl->answer.i_body > 0 ) + { + /* send the body data */ + free( cl->p_buffer ); + cl->p_buffer = cl->answer.p_body; + cl->i_buffer_size = cl->answer.i_body; + cl->i_buffer = 0; + + cl->answer.i_body = 0; + cl->answer.p_body = NULL; + } + else + { + /* send finished */ + cl->i_state = HTTPD_CLIENT_SEND_DONE; + } + } + } + else + { +#if defined( WIN32 ) || defined( UNDER_CE ) + if( ( i_len < 0 && WSAGetLastError() != WSAEWOULDBLOCK ) || ( i_len == 0 ) ) +#else + if( ( i_len < 0 && errno != EAGAIN ) || ( i_len == 0 ) ) +#endif + { + /* error */ + cl->i_state = HTTPD_CLIENT_DEAD; + } + } +} + +static void httpd_ClientTlsHsIn( httpd_client_t *cl ) +{ + switch( tls_SessionContinueHandshake( cl->p_tls ) ) + { + case 0: + cl->i_state = HTTPD_CLIENT_RECEIVING; + break; + + case -1: + cl->i_state = HTTPD_CLIENT_DEAD; + cl->p_tls = NULL; + break; + + case 2: + cl->i_state = HTTPD_CLIENT_TLS_HS_OUT; + } +} + +static void httpd_ClientTlsHsOut( httpd_client_t *cl ) +{ + switch( tls_SessionContinueHandshake( cl->p_tls ) ) + { + case 0: + cl->i_state = HTTPD_CLIENT_RECEIVING; + break; + + case -1: + cl->i_state = HTTPD_CLIENT_DEAD; + cl->p_tls = NULL; + break; + + case 1: + cl->i_state = HTTPD_CLIENT_TLS_HS_IN; + break; + } +} + +static void* httpd_HostThread( vlc_object_t *p_this ) +{ + httpd_host_t *host = (httpd_host_t *)p_this; + tls_session_t *p_tls = NULL; + counter_t *p_total_counter = stats_CounterCreate( host, VLC_VAR_INTEGER, STATS_COUNTER ); + counter_t *p_active_counter = stats_CounterCreate( host, VLC_VAR_INTEGER, STATS_COUNTER ); + int evfd; + bool b_die; + +retry: + vlc_object_lock( host ); + evfd = vlc_object_waitpipe( VLC_OBJECT( host ) ); + b_die = !vlc_object_alive( host ); + vlc_object_unlock( host ); + + while( !b_die ) + { + if( host->i_url <= 0 ) + { + /* 0.2s (FIXME: use a condition variable) */ + msleep( 200000 ); + goto retry; + } + + /* prepare a new TLS session */ + if( ( p_tls == NULL ) && ( host->p_tls != NULL ) ) + p_tls = tls_ServerSessionPrepare( host->p_tls ); + + struct pollfd ufd[host->nfd + host->i_client + 1]; + unsigned nfd; + for( nfd = 0; nfd < host->nfd; nfd++ ) + { + ufd[nfd].fd = host->fds[nfd]; + ufd[nfd].events = POLLIN; + ufd[nfd].revents = 0; + } + + /* add all socket that should be read/write and close dead connection */ + vlc_mutex_lock( &host->lock ); + mtime_t now = mdate(); + bool b_low_delay = false; + + for(int i_client = 0; i_client < host->i_client; i_client++ ) + { + httpd_client_t *cl = host->client[i_client]; + if( cl->i_ref < 0 || ( cl->i_ref == 0 && + ( cl->i_state == HTTPD_CLIENT_DEAD || + ( cl->i_activity_timeout > 0 && + cl->i_activity_date+cl->i_activity_timeout < now) ) ) ) + { + httpd_ClientClean( cl ); + stats_UpdateInteger( host, p_active_counter, -1, NULL ); + TAB_REMOVE( host->i_client, host->client, cl ); + free( cl ); + i_client--; + continue; + } + + struct pollfd *pufd = ufd + nfd; + assert (pufd < ufd + (sizeof (ufd) / sizeof (ufd[0]))); + + pufd->fd = cl->fd; + pufd->events = pufd->revents = 0; + + if( ( cl->i_state == HTTPD_CLIENT_RECEIVING ) + || ( cl->i_state == HTTPD_CLIENT_TLS_HS_IN ) ) + { + pufd->events = POLLIN; + } + else if( ( cl->i_state == HTTPD_CLIENT_SENDING ) + || ( cl->i_state == HTTPD_CLIENT_TLS_HS_OUT ) ) + { + pufd->events = POLLOUT; + } + else if( cl->i_state == HTTPD_CLIENT_RECEIVE_DONE ) + { + httpd_message_t *answer = &cl->answer; + httpd_message_t *query = &cl->query; + int i_msg = query->i_type; + + httpd_MsgInit( answer ); + + /* Handle what we received */ + if( (cl->i_mode != HTTPD_CLIENT_BIDIR) && + (i_msg == HTTPD_MSG_ANSWER || i_msg == HTTPD_MSG_CHANNEL) ) + { + /* we can only receive request from client when not + * in BIDIR mode */ + cl->url = NULL; + cl->i_state = HTTPD_CLIENT_DEAD; + } + else if( i_msg == HTTPD_MSG_ANSWER ) + { + /* We are in BIDIR mode, trigger the callback and then + * check for new data */ + if( cl->url && cl->url->catch[i_msg].cb ) + { + cl->url->catch[i_msg].cb( cl->url->catch[i_msg].p_sys, + cl, NULL, query ); + } + cl->i_state = HTTPD_CLIENT_WAITING; + } + else if( i_msg == HTTPD_MSG_CHANNEL ) + { + /* We are in BIDIR mode, trigger the callback and then + * check for new data */ + if( cl->url && cl->url->catch[i_msg].cb ) + { + cl->url->catch[i_msg].cb( cl->url->catch[i_msg].p_sys, + cl, NULL, query ); + } + cl->i_state = HTTPD_CLIENT_WAITING; + } + else if( i_msg == HTTPD_MSG_OPTIONS ) + { + + answer->i_type = HTTPD_MSG_ANSWER; + answer->i_proto = query->i_proto; + answer->i_status = 200; + answer->i_body = 0; + answer->p_body = NULL; + + httpd_MsgAdd( answer, "Server", "%s", PACKAGE_STRING ); + httpd_MsgAdd( answer, "Content-Length", "0" ); + + switch( query->i_proto ) + { + case HTTPD_PROTO_HTTP: + answer->i_version = 1; + httpd_MsgAdd( answer, "Allow", + "GET,HEAD,POST,OPTIONS" ); + break; + + case HTTPD_PROTO_RTSP: + { + const char *p; + answer->i_version = 0; + + p = httpd_MsgGet( query, "Cseq" ); + if( p != NULL ) + httpd_MsgAdd( answer, "Cseq", "%s", p ); + p = httpd_MsgGet( query, "Timestamp" ); + if( p != NULL ) + httpd_MsgAdd( answer, "Timestamp", "%s", p ); + + p = httpd_MsgGet( query, "Require" ); + if( p != NULL ) + { + answer->i_status = 551; + httpd_MsgAdd( query, "Unsupported", "%s", p ); + } + + httpd_MsgAdd( answer, "Public", "DESCRIBE,SETUP," + "TEARDOWN,PLAY,PAUSE,GET_PARAMETER" ); + break; + } + } + + cl->i_buffer = -1; /* Force the creation of the answer in + * httpd_ClientSend */ + cl->i_state = HTTPD_CLIENT_SENDING; + } + else if( i_msg == HTTPD_MSG_NONE ) + { + if( query->i_proto == HTTPD_PROTO_NONE ) + { + cl->url = NULL; + cl->i_state = HTTPD_CLIENT_DEAD; + } + else + { + char *p; + + /* unimplemented */ + answer->i_proto = query->i_proto ; + answer->i_type = HTTPD_MSG_ANSWER; + answer->i_version= 0; + answer->i_status = 501; + + answer->i_body = httpd_HtmlError (&p, 501, NULL); + answer->p_body = (uint8_t *)p; + httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body ); + + cl->i_buffer = -1; /* Force the creation of the answer in httpd_ClientSend */ + cl->i_state = HTTPD_CLIENT_SENDING; + } + } + else + { + bool b_auth_failed = false; + bool b_hosts_failed = false; + + /* Search the url and trigger callbacks */ + for(int i = 0; i < host->i_url; i++ ) + { + httpd_url_t *url = host->url[i]; + + if( !strcmp( url->psz_url, query->psz_url ) ) + { + if( url->catch[i_msg].cb ) + { + if( answer && ( url->p_acl != NULL ) ) + { + char ip[NI_MAXNUMERICHOST]; + + if( ( httpd_ClientIP( cl, ip ) == NULL ) + || ACL_Check( url->p_acl, ip ) ) + { + b_hosts_failed = true; + break; + } + } + + if( answer && ( *url->psz_user || *url->psz_password ) ) + { + /* create the headers */ + const char *b64 = httpd_MsgGet( query, "Authorization" ); /* BASIC id */ + char *user = NULL, *pass = NULL; + + if( b64 != NULL + && !strncasecmp( b64, "BASIC", 5 ) ) + { + b64 += 5; + while( *b64 == ' ' ) + b64++; + + user = vlc_b64_decode( b64 ); + if (user != NULL) + { + pass = strchr (user, ':'); + if (pass != NULL) + *pass++ = '\0'; + } + } + + if ((user == NULL) || (pass == NULL) + || strcmp (user, url->psz_user) + || strcmp (pass, url->psz_password)) + { + httpd_MsgAdd( answer, + "WWW-Authenticate", + "Basic realm=\"%s\"", + url->psz_user ); + /* We fail for all url */ + b_auth_failed = true; + free( user ); + break; + } + + free( user ); + } + + if( !url->catch[i_msg].cb( url->catch[i_msg].p_sys, cl, answer, query ) ) + { + if( answer->i_proto == HTTPD_PROTO_NONE ) + { + /* Raw answer from a CGI */ + cl->i_buffer = cl->i_buffer_size; + } + else + cl->i_buffer = -1; + + /* only one url can answer */ + answer = NULL; + if( cl->url == NULL ) + { + cl->url = url; + } + } + } + } + } + + if( answer ) + { + char *p; + + answer->i_proto = query->i_proto; + answer->i_type = HTTPD_MSG_ANSWER; + answer->i_version= 0; + + if( b_hosts_failed ) + { + answer->i_status = 403; + } + else if( b_auth_failed ) + { + answer->i_status = 401; + } + else + { + /* no url registered */ + answer->i_status = 404; + } + + answer->i_body = httpd_HtmlError (&p, + answer->i_status, + query->psz_url); + answer->p_body = (uint8_t *)p; + + cl->i_buffer = -1; /* Force the creation of the answer in httpd_ClientSend */ + httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body ); + httpd_MsgAdd( answer, "Content-Type", "%s", "text/html" ); + } + + cl->i_state = HTTPD_CLIENT_SENDING; + } + } + else if( cl->i_state == HTTPD_CLIENT_SEND_DONE ) + { + if( cl->i_mode == HTTPD_CLIENT_FILE || cl->answer.i_body_offset == 0 ) + { + const char *psz_connection = httpd_MsgGet( &cl->answer, "Connection" ); + const char *psz_query = httpd_MsgGet( &cl->query, "Connection" ); + bool b_connection = false; + bool b_keepalive = false; + bool b_query = false; + + cl->url = NULL; + if( psz_connection ) + { + b_connection = ( strcasecmp( psz_connection, "Close" ) == 0 ); + b_keepalive = ( strcasecmp( psz_connection, "Keep-Alive" ) == 0 ); + } + + if( psz_query ) + { + b_query = ( strcasecmp( psz_query, "Close" ) == 0 ); + } + + if( ( ( cl->query.i_proto == HTTPD_PROTO_HTTP ) && + ( ( cl->answer.i_version == 0 && b_keepalive ) || + ( cl->answer.i_version == 1 && !b_connection ) ) ) || + ( ( cl->query.i_proto == HTTPD_PROTO_RTSP ) && + !b_query && !b_connection ) ) + { + httpd_MsgClean( &cl->query ); + httpd_MsgInit( &cl->query ); + + cl->i_buffer = 0; + cl->i_buffer_size = 1000; + free( cl->p_buffer ); + cl->p_buffer = malloc( cl->i_buffer_size ); + cl->i_state = HTTPD_CLIENT_RECEIVING; + } + else + { + cl->i_state = HTTPD_CLIENT_DEAD; + } + httpd_MsgClean( &cl->answer ); + } + else if( cl->b_read_waiting ) + { + /* we have a message waiting for us to read it */ + httpd_MsgClean( &cl->answer ); + httpd_MsgClean( &cl->query ); + + cl->i_buffer = 0; + cl->i_buffer_size = 1000; + free( cl->p_buffer ); + cl->p_buffer = malloc( cl->i_buffer_size ); + cl->i_state = HTTPD_CLIENT_RECEIVING; + cl->b_read_waiting = false; + } + else + { + int64_t i_offset = cl->answer.i_body_offset; + httpd_MsgClean( &cl->answer ); + + cl->answer.i_body_offset = i_offset; + free( cl->p_buffer ); + cl->p_buffer = NULL; + cl->i_buffer = 0; + cl->i_buffer_size = 0; + + cl->i_state = HTTPD_CLIENT_WAITING; + } + } + else if( cl->i_state == HTTPD_CLIENT_WAITING ) + { + int64_t i_offset = cl->answer.i_body_offset; + int i_msg = cl->query.i_type; + + httpd_MsgInit( &cl->answer ); + cl->answer.i_body_offset = i_offset; + + cl->url->catch[i_msg].cb( cl->url->catch[i_msg].p_sys, cl, + &cl->answer, &cl->query ); + if( cl->answer.i_type != HTTPD_MSG_NONE ) + { + /* we have new data, so re-enter send mode */ + cl->i_buffer = 0; + cl->p_buffer = cl->answer.p_body; + cl->i_buffer_size = cl->answer.i_body; + cl->answer.p_body = NULL; + cl->answer.i_body = 0; + cl->i_state = HTTPD_CLIENT_SENDING; + } + } + + /* Special for BIDIR mode we also check reading */ + if( cl->i_mode == HTTPD_CLIENT_BIDIR && + cl->i_state == HTTPD_CLIENT_SENDING ) + { + pufd->events |= POLLIN; + } + + if (pufd->events != 0) + nfd++; + else + b_low_delay = true; + } + vlc_mutex_unlock( &host->lock ); + + ufd[nfd].fd = evfd; + ufd[nfd].events = POLLIN; + ufd[nfd].revents = 0; + nfd++; + + /* we will wait 20ms (not too big) if HTTPD_CLIENT_WAITING */ + switch( poll( ufd, nfd, b_low_delay ? 20 : -1) ) + { + case -1: + if (errno != EINTR) + { + /* Kernel on low memory or a bug: pace */ + msg_Err( host, "polling error: %m" ); + msleep( 100000 ); + } + case 0: + continue; + } + + vlc_object_lock( host ); + if( ufd[nfd - 1].revents ) + { + b_die = !vlc_object_alive( host ); + if( !b_die ) + vlc_object_wait( host ); + } + vlc_object_unlock( host ); + + /* Handle client sockets */ + vlc_mutex_lock( &host->lock ); + now = mdate(); + for( int i_client = 0; i_client < host->i_client; i_client++ ) + { + httpd_client_t *cl = host->client[i_client]; + const struct pollfd *pufd = &ufd[host->nfd + i_client]; + + assert( pufd < &ufd[sizeof(ufd) / sizeof(ufd[0])] ); + + if( cl->fd != pufd->fd ) + continue; // we were not waiting for this client + if( pufd->revents == 0 ) + continue; // no event received + + cl->i_activity_date = now; + + if( cl->i_state == HTTPD_CLIENT_RECEIVING ) + { + httpd_ClientRecv( cl ); + } + else if( cl->i_state == HTTPD_CLIENT_SENDING ) + { + httpd_ClientSend( cl ); + } + else if( cl->i_state == HTTPD_CLIENT_TLS_HS_IN ) + { + httpd_ClientTlsHsIn( cl ); + } + else if( cl->i_state == HTTPD_CLIENT_TLS_HS_OUT ) + { + httpd_ClientTlsHsOut( cl ); + } + + if( cl->i_mode == HTTPD_CLIENT_BIDIR && + cl->i_state == HTTPD_CLIENT_SENDING && + (pufd->revents & POLLIN) ) + { + cl->b_read_waiting = true; + } + } + vlc_mutex_unlock( &host->lock ); + + /* Handle server sockets (accept new connections) */ + for( nfd = 0; nfd < host->nfd; nfd++ ) + { + httpd_client_t *cl; + int i_state = -1; + int fd = ufd[nfd].fd; + + assert (fd == host->fds[nfd]); + + if( ufd[nfd].revents == 0 ) + continue; + + /* */ + fd = accept (fd, NULL, NULL); + if (fd == -1) + continue; + + net_SetupSocket (fd); + if( p_tls != NULL ) + { + switch( tls_ServerSessionHandshake( p_tls, fd ) ) + { + case -1: + msg_Err( host, "Rejecting TLS connection" ); + net_Close( fd ); + fd = -1; + p_tls = NULL; + break; + + case 1: /* missing input - most likely */ + i_state = HTTPD_CLIENT_TLS_HS_IN; + break; + + case 2: /* missing output */ + i_state = HTTPD_CLIENT_TLS_HS_OUT; + break; + } + + if( (p_tls == NULL) != (host->p_tls == NULL) ) + break; // wasted TLS session, cannot accept() anymore + } + + stats_UpdateInteger( host, p_total_counter, 1, NULL ); + stats_UpdateInteger( host, p_active_counter, 1, NULL ); + cl = httpd_ClientNew( fd, p_tls, now ); + p_tls = NULL; + vlc_mutex_lock( &host->lock ); + TAB_APPEND( host->i_client, host->client, cl ); + vlc_mutex_unlock( &host->lock ); + if( i_state != -1 ) + cl->i_state = i_state; // override state for TLS + + if (host->p_tls != NULL) + break; // cannot accept further without new TLS session + } + + } + + if( p_tls != NULL ) + tls_ServerSessionClose( p_tls ); + if( p_total_counter ) + stats_CounterClean( p_total_counter ); + if( p_active_counter ) + stats_CounterClean( p_active_counter ); + return NULL; +} + +#else /* ENABLE_HTTPD */ + +/* We just define an empty wrapper */ +httpd_host_t *httpd_TLSHostNew( vlc_object_t *a, const char *b, + int c, + const char *e, const char *f, + const char *g, const char* h) +{ + msg_Err( a, "HTTP daemon support is disabled" ); + return NULL; +} + +httpd_host_t *httpd_HostNew( vlc_object_t *a, const char *b, + int c ) +{ + msg_Err( a, "HTTP daemon support is disabled" ); + return NULL; +} + +void httpd_HostDelete( httpd_host_t *a ) +{ +} + +httpd_url_t *httpd_UrlNew( httpd_host_t *host, const char *psz_url, + const char *psz_user, const char *psz_password, + const vlc_acl_t *p_acl ) +{ + return NULL; +} + +httpd_url_t *httpd_UrlNewUnique( httpd_host_t *host, const char *psz_url, + const char *psz_user, const char *psz_password, + const vlc_acl_t *p_acl ) +{ + return NULL; +} + +int httpd_UrlCatch( httpd_url_t *a, int b, httpd_callback_t c, + httpd_callback_sys_t *d ) +{ + return 0; +} + +void httpd_UrlDelete( httpd_url_t *a ) +{ +} + +char* httpd_ClientIP( const httpd_client_t *cl, char *psz_ip ) +{ + return NULL; +} + +char* httpd_ServerIP( const httpd_client_t *cl, char *psz_ip ) +{ + return NULL; +} + +void httpd_ClientModeStream( httpd_client_t *a ) +{ +} + +void httpd_ClientModeBidir( httpd_client_t *a ) +{ +} + +httpd_file_sys_t *httpd_FileDelete( httpd_file_t *file ) +{ + return NULL; +} + +httpd_file_t *httpd_FileNew( httpd_host_t *host, + const char *psz_url, const char *psz_mime, + const char *psz_user, const char *psz_password, + const vlc_acl_t *p_acl, httpd_file_callback_t pf_fill, + httpd_file_sys_t *p_sys ) +{ + return NULL; +} + +httpd_handler_t *httpd_HandlerNew( httpd_host_t *host, const char *psz_url, + const char *psz_user, + const char *psz_password, + const vlc_acl_t *p_acl, + httpd_handler_callback_t pf_fill, + httpd_handler_sys_t *p_sys ) +{ + return NULL; +} + +httpd_handler_sys_t *httpd_HandlerDelete( httpd_handler_t *handler ) +{ + return NULL; +} + +void httpd_RedirectDelete( httpd_redirect_t *a ) +{ +} + +httpd_redirect_t *httpd_RedirectNew( httpd_host_t *host, const char *psz_url_dst, + const char *psz_url_src ) +{ + return NULL; +} + +void httpd_StreamDelete( httpd_stream_t *a ) +{ +} + +int httpd_StreamHeader( httpd_stream_t *a, uint8_t *b, int c ) +{ + return 0; +} + +int httpd_StreamSend ( httpd_stream_t *a, uint8_t *b, int c ) +{ + return 0; +} + +httpd_stream_t *httpd_StreamNew( httpd_host_t *host, + const char *psz_url, const char *psz_mime, + const char *psz_user, const char *psz_password, + const vlc_acl_t *p_acl ) +{ + return NULL; +} + +void httpd_MsgInit ( httpd_message_t *a ) +{ +} + +void httpd_MsgAdd ( httpd_message_t *a, const char *b, const char *c, ... ) +{ +} + +const char *httpd_MsgGet( const httpd_message_t *msg, const char *name ) +{ + return ""; +} + +void httpd_MsgClean( httpd_message_t *a ) +{ +} + +#endif /* ENABLE_HTTPD */ diff --git a/VLC/image.c b/VLC/image.c new file mode 100644 index 0000000..6e55d91 --- /dev/null +++ b/VLC/image.c @@ -0,0 +1,771 @@ +/***************************************************************************** + * image.c : wrapper for image reading/writing facilities + ***************************************************************************** + * Copyright (C) 2004-2007 the VideoLAN team + * $Id$ + * + * Author: Gildas Bazin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/** + * \file + * This file contains the functions to handle the image_handler_t type + */ + +/***************************************************************************** + * Preamble + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "vlc_common.h" +#include "vlc_codec.h" +#include "vlc_filter.h" +#include "vlc_es.h" +#include "vlc_image.h" +#include "vlc_stream.h" +#include "vlc_charset.h" +#include "libvlc.h" + +static picture_t *ImageRead( image_handler_t *, block_t *, + video_format_t *, video_format_t * ); +static picture_t *ImageReadUrl( image_handler_t *, const char *, + video_format_t *, video_format_t * ); +static block_t *ImageWrite( image_handler_t *, picture_t *, + video_format_t *, video_format_t * ); +static int ImageWriteUrl( image_handler_t *, picture_t *, + video_format_t *, video_format_t *, const char * ); + +static picture_t *ImageConvert( image_handler_t *, picture_t *, + video_format_t *, video_format_t * ); +static picture_t *ImageFilter( image_handler_t *, picture_t *, + video_format_t *, const char *psz_module ); + +static decoder_t *CreateDecoder( vlc_object_t *, video_format_t * ); +static void DeleteDecoder( decoder_t * ); +static encoder_t *CreateEncoder( vlc_object_t *, video_format_t *, + video_format_t * ); +static void DeleteEncoder( encoder_t * ); +static filter_t *CreateFilter( vlc_object_t *, es_format_t *, + video_format_t *, const char * ); +static void DeleteFilter( filter_t * ); + +static vlc_fourcc_t Ext2Fourcc( const char * ); +/*static const char *Fourcc2Ext( vlc_fourcc_t );*/ + +/** + * Create an image_handler_t instance + * + */ +image_handler_t *__image_HandlerCreate( vlc_object_t *p_this ) +{ + image_handler_t *p_image = malloc( sizeof(image_handler_t) ); + + memset( p_image, 0, sizeof(image_handler_t) ); + p_image->p_parent = p_this; + + p_image->pf_read = ImageRead; + p_image->pf_read_url = ImageReadUrl; + p_image->pf_write = ImageWrite; + p_image->pf_write_url = ImageWriteUrl; + p_image->pf_convert = ImageConvert; + p_image->pf_filter = ImageFilter; + + return p_image; +} + +/** + * Delete the image_handler_t instance + * + */ +void image_HandlerDelete( image_handler_t *p_image ) +{ + if( !p_image ) return; + + if( p_image->p_dec ) DeleteDecoder( p_image->p_dec ); + if( p_image->p_enc ) DeleteEncoder( p_image->p_enc ); + if( p_image->p_filter ) DeleteFilter( p_image->p_filter ); + + free( p_image ); + p_image = NULL; +} + +/** + * Read an image + * + */ + +static picture_t *ImageRead( image_handler_t *p_image, block_t *p_block, + video_format_t *p_fmt_in, + video_format_t *p_fmt_out ) +{ + picture_t *p_pic = NULL, *p_tmp; + + /* Check if we can reuse the current decoder */ + if( p_image->p_dec && + p_image->p_dec->fmt_in.i_codec != p_fmt_in->i_chroma ) + { + DeleteDecoder( p_image->p_dec ); + p_image->p_dec = 0; + } + + /* Start a decoder */ + if( !p_image->p_dec ) + { + p_image->p_dec = CreateDecoder( p_image->p_parent, p_fmt_in ); + if( !p_image->p_dec ) return NULL; + } + + p_block->i_pts = p_block->i_dts = mdate(); + while( (p_tmp = p_image->p_dec->pf_decode_video( p_image->p_dec, &p_block )) + != NULL ) + { + if( p_pic != NULL ) + picture_Release( p_pic ); + p_pic = p_tmp; + } + + if( p_pic == NULL ) + { + msg_Warn( p_image->p_parent, "no image decoded" ); + return 0; + } + + if( !p_fmt_out->i_chroma ) + p_fmt_out->i_chroma = p_image->p_dec->fmt_out.video.i_chroma; + if( !p_fmt_out->i_width && p_fmt_out->i_height ) + p_fmt_out->i_width = p_fmt_out->i_height + * p_image->p_dec->fmt_out.video.i_aspect + / VOUT_ASPECT_FACTOR; + if( !p_fmt_out->i_height && p_fmt_out->i_width ) + p_fmt_out->i_height = p_fmt_out->i_width * VOUT_ASPECT_FACTOR + / p_image->p_dec->fmt_out.video.i_aspect; + if( !p_fmt_out->i_width ) + p_fmt_out->i_width = p_image->p_dec->fmt_out.video.i_width; + if( !p_fmt_out->i_height ) + p_fmt_out->i_height = p_image->p_dec->fmt_out.video.i_height; + + /* Check if we need chroma conversion or resizing */ + if( p_image->p_dec->fmt_out.video.i_chroma != p_fmt_out->i_chroma || + p_image->p_dec->fmt_out.video.i_width != p_fmt_out->i_width || + p_image->p_dec->fmt_out.video.i_height != p_fmt_out->i_height ) + { + if( p_image->p_filter ) + if( p_image->p_filter->fmt_in.video.i_chroma != + p_image->p_dec->fmt_out.video.i_chroma || + p_image->p_filter->fmt_out.video.i_chroma != p_fmt_out->i_chroma ) + { + /* We need to restart a new filter */ + DeleteFilter( p_image->p_filter ); + p_image->p_filter = 0; + } + + /* Start a filter */ + if( !p_image->p_filter ) + { + p_image->p_filter = + CreateFilter( p_image->p_parent, &p_image->p_dec->fmt_out, + p_fmt_out, NULL ); + + if( !p_image->p_filter ) + { + picture_Release( p_pic ); + return NULL; + } + } + else + { + /* Filters should handle on-the-fly size changes */ + p_image->p_filter->fmt_in = p_image->p_dec->fmt_out; + p_image->p_filter->fmt_out = p_image->p_dec->fmt_out; + p_image->p_filter->fmt_out.i_codec = p_fmt_out->i_chroma; + p_image->p_filter->fmt_out.video = *p_fmt_out; + } + + p_pic = p_image->p_filter->pf_video_filter( p_image->p_filter, p_pic ); + *p_fmt_out = p_image->p_filter->fmt_out.video; + } + else *p_fmt_out = p_image->p_dec->fmt_out.video; + + return p_pic; +} + +static picture_t *ImageReadUrl( image_handler_t *p_image, const char *psz_url, + video_format_t *p_fmt_in, + video_format_t *p_fmt_out ) +{ + block_t *p_block; + picture_t *p_pic; + stream_t *p_stream = NULL; + int i_size; + + p_stream = stream_UrlNew( p_image->p_parent, psz_url ); + + if( !p_stream ) + { + msg_Dbg( p_image->p_parent, "could not open %s for reading", + psz_url ); + return NULL; + } + + i_size = stream_Size( p_stream ); + + p_block = block_New( p_image->p_parent, i_size ); + + stream_Read( p_stream, p_block->p_buffer, i_size ); + stream_Delete( p_stream ); + + if( !p_fmt_in->i_chroma ) + { + /* Try to guess format from file name */ + p_fmt_in->i_chroma = Ext2Fourcc( psz_url ); + } + + p_pic = ImageRead( p_image, p_block, p_fmt_in, p_fmt_out ); + + return p_pic; +} + +/** + * Write an image + * + */ + +static block_t *ImageWrite( image_handler_t *p_image, picture_t *p_pic, + video_format_t *p_fmt_in, + video_format_t *p_fmt_out ) +{ + block_t *p_block; + + /* Check if we can reuse the current encoder */ + if( p_image->p_enc && + ( p_image->p_enc->fmt_out.i_codec != p_fmt_out->i_chroma || + p_image->p_enc->fmt_out.video.i_width != p_fmt_out->i_width || + p_image->p_enc->fmt_out.video.i_height != p_fmt_out->i_height ) ) + { + DeleteEncoder( p_image->p_enc ); + p_image->p_enc = 0; + } + + /* Start an encoder */ + if( !p_image->p_enc ) + { + p_image->p_enc = CreateEncoder( p_image->p_parent, + p_fmt_in, p_fmt_out ); + if( !p_image->p_enc ) return NULL; + } + + /* Check if we need chroma conversion or resizing */ + if( p_image->p_enc->fmt_in.video.i_chroma != p_fmt_in->i_chroma || + p_image->p_enc->fmt_in.video.i_width != p_fmt_in->i_width || + p_image->p_enc->fmt_in.video.i_height != p_fmt_in->i_height ) + { + picture_t *p_tmp_pic; + + if( p_image->p_filter ) + if( p_image->p_filter->fmt_in.video.i_chroma != p_fmt_in->i_chroma || + p_image->p_filter->fmt_out.video.i_chroma != + p_image->p_enc->fmt_in.video.i_chroma ) + { + /* We need to restart a new filter */ + DeleteFilter( p_image->p_filter ); + p_image->p_filter = 0; + } + + /* Start a filter */ + if( !p_image->p_filter ) + { + es_format_t fmt_in; + es_format_Init( &fmt_in, VIDEO_ES, p_fmt_in->i_chroma ); + fmt_in.video = *p_fmt_in; + + p_image->p_filter = + CreateFilter( p_image->p_parent, &fmt_in, + &p_image->p_enc->fmt_in.video, NULL ); + + if( !p_image->p_filter ) + { + return NULL; + } + } + else + { + /* Filters should handle on-the-fly size changes */ + p_image->p_filter->fmt_in.i_codec = p_fmt_in->i_chroma; + p_image->p_filter->fmt_out.video = *p_fmt_in; + p_image->p_filter->fmt_out.i_codec =p_image->p_enc->fmt_in.i_codec; + p_image->p_filter->fmt_out.video = p_image->p_enc->fmt_in.video; + } + + picture_Yield( p_pic ); + + p_tmp_pic = + p_image->p_filter->pf_video_filter( p_image->p_filter, p_pic ); + + p_block = p_image->p_enc->pf_encode_video( p_image->p_enc, p_tmp_pic ); + + p_image->p_filter->pf_vout_buffer_del( p_image->p_filter, p_tmp_pic ); + } + else + { + p_block = p_image->p_enc->pf_encode_video( p_image->p_enc, p_pic ); + } + + if( !p_block ) + { + msg_Dbg( p_image->p_parent, "no image encoded" ); + return 0; + } + + return p_block; +} + +static int ImageWriteUrl( image_handler_t *p_image, picture_t *p_pic, + video_format_t *p_fmt_in, video_format_t *p_fmt_out, + const char *psz_url ) +{ + block_t *p_block; + FILE *file; + + if( !p_fmt_out->i_chroma ) + { + /* Try to guess format from file name */ + p_fmt_out->i_chroma = Ext2Fourcc( psz_url ); + } + + file = utf8_fopen( psz_url, "wb" ); + if( !file ) + { + msg_Err( p_image->p_parent, "%s: %m", psz_url ); + return VLC_EGENERIC; + } + + p_block = ImageWrite( p_image, p_pic, p_fmt_in, p_fmt_out ); + + int err = 0; + if( p_block ) + { + if( fwrite( p_block->p_buffer, p_block->i_buffer, 1, file ) != 1 ) + err = errno; + block_Release( p_block ); + } + + if( fclose( file ) && !err ) + err = errno; + + if( err ) + { + errno = err; + msg_Err( p_image->p_parent, "%s: %m", psz_url ); + } + + return err ? VLC_EGENERIC : VLC_SUCCESS; +} + +/** + * Convert an image to a different format + * + */ + +static picture_t *ImageConvert( image_handler_t *p_image, picture_t *p_pic, + video_format_t *p_fmt_in, + video_format_t *p_fmt_out ) +{ + picture_t *p_pif; + + if( !p_fmt_out->i_width && !p_fmt_out->i_height && + p_fmt_out->i_sar_num && p_fmt_out->i_sar_den && + p_fmt_out->i_sar_num * p_fmt_in->i_sar_den != + p_fmt_out->i_sar_den * p_fmt_in->i_sar_num ) + { + p_fmt_out->i_width = + p_fmt_in->i_sar_num * (int64_t)p_fmt_out->i_sar_den * + p_fmt_in->i_width / p_fmt_in->i_sar_den / p_fmt_out->i_sar_num; + p_fmt_out->i_visible_width = + p_fmt_in->i_sar_num * (int64_t)p_fmt_out->i_sar_den * + p_fmt_in->i_visible_width / p_fmt_in->i_sar_den / + p_fmt_out->i_sar_num; + } + + if( !p_fmt_out->i_chroma ) p_fmt_out->i_chroma = p_fmt_in->i_chroma; + if( !p_fmt_out->i_width ) + p_fmt_out->i_width = p_fmt_out->i_visible_width = p_fmt_in->i_width; + if( !p_fmt_out->i_height ) + p_fmt_out->i_height = p_fmt_out->i_visible_height = p_fmt_in->i_height; + if( !p_fmt_out->i_sar_num ) p_fmt_out->i_sar_num = p_fmt_in->i_sar_num; + if( !p_fmt_out->i_sar_den ) p_fmt_out->i_sar_den = p_fmt_in->i_sar_den; + if( !p_fmt_out->i_aspect ) p_fmt_out->i_aspect = p_fmt_in->i_aspect; + + if( p_image->p_filter ) + if( p_image->p_filter->fmt_in.video.i_chroma != p_fmt_in->i_chroma || + p_image->p_filter->fmt_out.video.i_chroma != p_fmt_out->i_chroma ) + { + /* We need to restart a new filter */ + DeleteFilter( p_image->p_filter ); + p_image->p_filter = NULL; + } + + /* Start a filter */ + if( !p_image->p_filter ) + { + es_format_t fmt_in; + es_format_Init( &fmt_in, VIDEO_ES, p_fmt_in->i_chroma ); + fmt_in.video = *p_fmt_in; + + p_image->p_filter = + CreateFilter( p_image->p_parent, &fmt_in, p_fmt_out, NULL ); + + if( !p_image->p_filter ) + { + return NULL; + } + } + else + { + /* Filters should handle on-the-fly size changes */ + p_image->p_filter->fmt_in.video = *p_fmt_in; + p_image->p_filter->fmt_out.video = *p_fmt_out; + } + + picture_Yield( p_pic ); + + p_pif = p_image->p_filter->pf_video_filter( p_image->p_filter, p_pic ); + + if( p_fmt_in->i_chroma == p_fmt_out->i_chroma && + p_fmt_in->i_width == p_fmt_out->i_width && + p_fmt_in->i_height == p_fmt_out->i_height ) + { + /* Duplicate image */ + picture_Release( p_pif ); /* XXX: Better fix must be possible */ + p_pif = p_image->p_filter->pf_vout_buffer_new( p_image->p_filter ); + if( p_pif ) vout_CopyPicture( p_image->p_parent, p_pif, p_pic ); + } + + return p_pif; +} + +/** + * Filter an image with a psz_module filter + * + */ + +static picture_t *ImageFilter( image_handler_t *p_image, picture_t *p_pic, + video_format_t *p_fmt, const char *psz_module ) +{ + /* Start a filter */ + if( !p_image->p_filter ) + { + es_format_t fmt; + es_format_Init( &fmt, VIDEO_ES, p_fmt->i_chroma ); + fmt.video = *p_fmt; + + p_image->p_filter = + CreateFilter( p_image->p_parent, &fmt, &fmt.video, psz_module ); + + if( !p_image->p_filter ) + { + return NULL; + } + } + else + { + /* Filters should handle on-the-fly size changes */ + p_image->p_filter->fmt_in.video = *p_fmt; + p_image->p_filter->fmt_out.video = *p_fmt; + } + + picture_Yield( p_pic ); + + return p_image->p_filter->pf_video_filter( p_image->p_filter, p_pic ); +} + +/** + * Misc functions + * + */ +static const struct +{ + vlc_fourcc_t i_codec; + const char *psz_ext; + +} ext_table[] = +{ + { VLC_FOURCC('j','p','e','g'), "jpeg" }, + { VLC_FOURCC('j','p','e','g'), "jpg" }, + { VLC_FOURCC('l','j','p','g'), "ljpg" }, + { VLC_FOURCC('p','n','g',' '), "png" }, + { VLC_FOURCC('p','g','m',' '), "pgm" }, + { VLC_FOURCC('p','g','m','y'), "pgmyuv" }, + { VLC_FOURCC('p','b','m',' '), "pbm" }, + { VLC_FOURCC('p','a','m',' '), "pam" }, + { VLC_FOURCC('t','g','a',' '), "tga" }, + { VLC_FOURCC('b','m','p',' '), "bmp" }, + { VLC_FOURCC('p','n','m',' '), "pnm" }, + { VLC_FOURCC('x','p','m',' '), "xpm" }, + { VLC_FOURCC('x','c','f',' '), "xcf" }, + { VLC_FOURCC('p','c','x',' '), "pcx" }, + { VLC_FOURCC('g','i','f',' '), "gif" }, + { VLC_FOURCC('t','i','f','f'), "tif" }, + { VLC_FOURCC('t','i','f','f'), "tiff" }, + { VLC_FOURCC('l','b','m',' '), "lbm" }, + { 0, NULL } +}; + +static vlc_fourcc_t Ext2Fourcc( const char *psz_name ) +{ + int i; + + psz_name = strrchr( psz_name, '.' ); + if( !psz_name ) return 0; + psz_name++; + + for( i = 0; ext_table[i].i_codec; i++ ) + { + int j; + for( j = 0; toupper(ext_table[i].psz_ext[j]) == toupper(psz_name[j]); + j++ ) + { + if( !ext_table[i].psz_ext[j] && !psz_name[j] ) + return ext_table[i].i_codec; + } + } + + return 0; +} + +/* +static const char *Fourcc2Ext( vlc_fourcc_t i_codec ) +{ + int i; + + for( i = 0; ext_table[i].i_codec != 0; i++ ) + { + if( ext_table[i].i_codec == i_codec ) return ext_table[i].psz_ext; + } + + return NULL; +} +*/ + +static picture_t *video_new_buffer( decoder_t *p_dec ) +{ + p_dec->fmt_out.video.i_chroma = p_dec->fmt_out.i_codec; + return picture_New( p_dec->fmt_out.video.i_chroma, + p_dec->fmt_out.video.i_width, + p_dec->fmt_out.video.i_height, + p_dec->fmt_out.video.i_aspect ); +} + +static void video_del_buffer( decoder_t *p_dec, picture_t *p_pic ) +{ + if( p_pic->i_refcount != 1 ) + msg_Err( p_dec, "invalid picture reference count" ); + + p_pic->i_refcount = 0; + picture_Delete( p_pic ); +} + +static void video_link_picture( decoder_t *p_dec, picture_t *p_pic ) +{ + (void)p_dec; + picture_Yield( p_pic ); +} + +static void video_unlink_picture( decoder_t *p_dec, picture_t *p_pic ) +{ + (void)p_dec; + picture_Release( p_pic ); +} + +static decoder_t *CreateDecoder( vlc_object_t *p_this, video_format_t *fmt ) +{ + decoder_t *p_dec; + + p_dec = vlc_object_create( p_this, VLC_OBJECT_DECODER ); + if( p_dec == NULL ) + return NULL; + + p_dec->p_module = NULL; + es_format_Init( &p_dec->fmt_in, VIDEO_ES, fmt->i_chroma ); + es_format_Init( &p_dec->fmt_out, VIDEO_ES, 0 ); + p_dec->fmt_in.video = *fmt; + p_dec->b_pace_control = true; + + p_dec->pf_vout_buffer_new = video_new_buffer; + p_dec->pf_vout_buffer_del = video_del_buffer; + p_dec->pf_picture_link = video_link_picture; + p_dec->pf_picture_unlink = video_unlink_picture; + + vlc_object_attach( p_dec, p_this ); + + /* Find a suitable decoder module */ + p_dec->p_module = module_Need( p_dec, "decoder", "$codec", 0 ); + if( !p_dec->p_module ) + { + msg_Err( p_dec, "no suitable decoder module for fourcc `%4.4s'.\n" + "VLC probably does not support this image format.", + (char*)&p_dec->fmt_in.i_codec ); + + DeleteDecoder( p_dec ); + return NULL; + } + + return p_dec; +} + +static void DeleteDecoder( decoder_t * p_dec ) +{ + vlc_object_detach( p_dec ); + + if( p_dec->p_module ) module_Unneed( p_dec, p_dec->p_module ); + + es_format_Clean( &p_dec->fmt_in ); + es_format_Clean( &p_dec->fmt_out ); + + vlc_object_release( p_dec ); + p_dec = NULL; +} + +static encoder_t *CreateEncoder( vlc_object_t *p_this, video_format_t *fmt_in, + video_format_t *fmt_out ) +{ + encoder_t *p_enc; + + p_enc = vlc_object_create( p_this, VLC_OBJECT_ENCODER ); + if( p_enc == NULL ) + return NULL; + + p_enc->p_module = NULL; + es_format_Init( &p_enc->fmt_in, VIDEO_ES, fmt_in->i_chroma ); + p_enc->fmt_in.video = *fmt_in; + if( fmt_out->i_width > 0 && fmt_out->i_height > 0 ) + { + p_enc->fmt_in.video.i_width = fmt_out->i_width; + p_enc->fmt_in.video.i_height = fmt_out->i_height; + + if( fmt_out->i_visible_width > 0 && + fmt_out->i_visible_height > 0 ) + { + p_enc->fmt_in.video.i_visible_width = fmt_out->i_visible_width; + p_enc->fmt_in.video.i_visible_height = fmt_out->i_visible_height; + } + else + { + p_enc->fmt_in.video.i_visible_width = fmt_out->i_width; + p_enc->fmt_in.video.i_visible_height = fmt_out->i_height; + } + } + else if( fmt_out->i_sar_num && fmt_out->i_sar_den && + fmt_out->i_sar_num * fmt_in->i_sar_den != + fmt_out->i_sar_den * fmt_in->i_sar_num ) + { + p_enc->fmt_in.video.i_width = + fmt_in->i_sar_num * (int64_t)fmt_out->i_sar_den * fmt_in->i_width / + fmt_in->i_sar_den / fmt_out->i_sar_num; + p_enc->fmt_in.video.i_visible_width = + fmt_in->i_sar_num * (int64_t)fmt_out->i_sar_den * + fmt_in->i_visible_width / fmt_in->i_sar_den / fmt_out->i_sar_num; + } + + p_enc->fmt_in.video.i_frame_rate = 25; + p_enc->fmt_in.video.i_frame_rate_base = 1; + + es_format_Init( &p_enc->fmt_out, VIDEO_ES, fmt_out->i_chroma ); + p_enc->fmt_out.video = *fmt_out; + p_enc->fmt_out.video.i_width = p_enc->fmt_in.video.i_width; + p_enc->fmt_out.video.i_height = p_enc->fmt_in.video.i_height; + + vlc_object_attach( p_enc, p_this ); + + /* Find a suitable decoder module */ + p_enc->p_module = module_Need( p_enc, "encoder", 0, 0 ); + if( !p_enc->p_module ) + { + msg_Err( p_enc, "no suitable encoder module for fourcc `%4.4s'.\n" + "VLC probably does not support this image format.", + (char*)&p_enc->fmt_out.i_codec ); + + DeleteEncoder( p_enc ); + return NULL; + } + p_enc->fmt_in.video.i_chroma = p_enc->fmt_in.i_codec; + + return p_enc; +} + +static void DeleteEncoder( encoder_t * p_enc ) +{ + vlc_object_detach( p_enc ); + + if( p_enc->p_module ) module_Unneed( p_enc, p_enc->p_module ); + + es_format_Clean( &p_enc->fmt_in ); + es_format_Clean( &p_enc->fmt_out ); + + vlc_object_release( p_enc ); + p_enc = NULL; +} + +static filter_t *CreateFilter( vlc_object_t *p_this, es_format_t *p_fmt_in, + video_format_t *p_fmt_out, + const char *psz_module ) +{ + static const char typename[] = "filter"; + filter_t *p_filter; + + p_filter = vlc_custom_create( p_this, sizeof(filter_t), + VLC_OBJECT_GENERIC, typename ); + vlc_object_attach( p_filter, p_this ); + + p_filter->pf_vout_buffer_new = + (picture_t *(*)(filter_t *))video_new_buffer; + p_filter->pf_vout_buffer_del = + (void (*)(filter_t *, picture_t *))video_del_buffer; + + p_filter->fmt_in = *p_fmt_in; + p_filter->fmt_out = *p_fmt_in; + p_filter->fmt_out.i_codec = p_fmt_out->i_chroma; + p_filter->fmt_out.video = *p_fmt_out; + p_filter->p_module = module_Need( p_filter, "video filter2", + psz_module, 0 ); + + if( !p_filter->p_module ) + { + msg_Dbg( p_filter, "no video filter found" ); + DeleteFilter( p_filter ); + return NULL; + } + + return p_filter; +} + +static void DeleteFilter( filter_t * p_filter ) +{ + vlc_object_detach( p_filter ); + + if( p_filter->p_module ) module_Unneed( p_filter, p_filter->p_module ); + + es_format_Clean( &p_filter->fmt_in ); + es_format_Clean( &p_filter->fmt_out ); + + vlc_object_release( p_filter ); +} diff --git a/VLC/input/access.c b/VLC/input/access.c new file mode 100644 index 0000000..fba6967 --- /dev/null +++ b/VLC/input/access.c @@ -0,0 +1,135 @@ +/***************************************************************************** + * access.c + ***************************************************************************** + * Copyright (C) 1999-2004 the VideoLAN team + * $Id$ + * + * Author: Laurent Aimar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#include "input_internal.h" + +/***************************************************************************** + * access_InternalNew: + *****************************************************************************/ +static access_t *access_InternalNew( vlc_object_t *p_obj, const char *psz_access, + const char *psz_demux, const char *psz_path, + access_t *p_source ) +{ + static const char typename[] = "access"; + access_t *p_access = vlc_custom_create( p_obj, sizeof (*p_access), + VLC_OBJECT_GENERIC, typename ); + + if( p_access == NULL ) + return NULL; + + /* Parse URL */ + p_access->p_source = p_source; + if( p_source ) + { + msg_Dbg( p_obj, "creating access filter '%s'", psz_access ); + } + else + { + msg_Dbg( p_obj, "creating access '%s' path='%s'", + psz_access, psz_path ); + p_access->psz_path = strdup( psz_path ); + } + p_access->psz_access = strdup( psz_access ); + p_access->psz_demux = strdup( psz_demux ); + + p_access->pf_read = NULL; + p_access->pf_block = NULL; + p_access->pf_seek = NULL; + p_access->pf_control = NULL; + p_access->p_sys = NULL; + p_access->info.i_update = 0; + p_access->info.i_size = 0; + p_access->info.i_pos = 0; + p_access->info.b_eof = false; + p_access->info.b_prebuffered = false; + p_access->info.i_title = 0; + p_access->info.i_seekpoint = 0; + + + /* Before module_Need (for var_Create...) */ + vlc_object_attach( p_access, p_obj ); + + p_access->p_module = + module_Need( p_access, p_source ? "access_filter" : "access", + psz_access, true ); + + if( p_access->p_module == NULL ) + { + msg_StackAdd( "could not create access" ); + vlc_object_detach( p_access ); + free( p_access->psz_access ); + free( p_access->psz_path ); + free( p_access->psz_demux ); + vlc_object_release( p_access ); + return NULL; + } + + return p_access; +} + +/***************************************************************************** + * access_New: + *****************************************************************************/ +access_t *__access_New( vlc_object_t *p_obj, const char *psz_access, + const char *psz_demux, const char *psz_path ) +{ + return access_InternalNew( p_obj, psz_access, psz_demux, + psz_path, NULL ); +} + +/***************************************************************************** + * access_FilterNew: + *****************************************************************************/ +access_t *access_FilterNew( access_t *p_source, const char *psz_access_filter ) +{ + return access_InternalNew( VLC_OBJECT(p_source), psz_access_filter, + p_source->psz_demux, p_source->psz_path, + p_source ); +} + +/***************************************************************************** + * access_Delete: + *****************************************************************************/ +void access_Delete( access_t *p_access ) +{ + module_Unneed( p_access, p_access->p_module ); + vlc_object_detach( p_access ); + + free( p_access->psz_access ); + free( p_access->psz_path ); + free( p_access->psz_demux ); + + if( p_access->p_source ) + { + access_Delete( p_access->p_source ); + } + + vlc_object_release( p_access ); +} + diff --git a/VLC/input/clock.c b/VLC/input/clock.c new file mode 100644 index 0000000..f9f5d1b --- /dev/null +++ b/VLC/input/clock.c @@ -0,0 +1,247 @@ +/***************************************************************************** + * input_clock.c: Clock/System date convertions, stream management + ***************************************************************************** + * Copyright (C) 1999-2004 the VideoLAN team + * $Id$ + * + * Authors: Christophe Massiot + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#include "input_internal.h" + +/* + * DISCUSSION : SYNCHRONIZATION METHOD + * + * In some cases we can impose the pace of reading (when reading from a + * file or a pipe), and for the synchronization we simply sleep() until + * it is time to deliver the packet to the decoders. When reading from + * the network, we must be read at the same pace as the server writes, + * otherwise the kernel's buffer will trash packets. The risk is now to + * overflow the input buffers in case the server goes too fast, that is + * why we do these calculations : + * + * We compute a mean for the pcr because we want to eliminate the + * network jitter and keep the low frequency variations. The mean is + * in fact a low pass filter and the jitter is a high frequency signal + * that is why it is eliminated by the filter/average. + * + * The low frequency variations enable us to synchronize the client clock + * with the server clock because they represent the time variation between + * the 2 clocks. Those variations (ie the filtered pcr) are used to compute + * the presentation dates for the audio and video frames. With those dates + * we can decode (or trash) the MPEG2 stream at "exactly" the same rate + * as it is sent by the server and so we keep the synchronization between + * the server and the client. + * + * It is a very important matter if you want to avoid underflow or overflow + * in all the FIFOs, but it may be not enough. + */ + +/* p_input->p->i_cr_average : Maximum number of samples used to compute the + * dynamic average value. + * We use the following formula : + * new_average = (old_average * c_average + new_sample_value) / (c_average +1) + */ + + +static void ClockNewRef( input_clock_t * p_pgrm, + mtime_t i_clock, mtime_t i_sysdate ); + +/***************************************************************************** + * Constants + *****************************************************************************/ + +/* Maximum gap allowed between two CRs. */ +#define CR_MAX_GAP (INT64_C(2000000)*100/9) + +/* Latency introduced on DVDs with CR == 0 on chapter change - this is from + * my dice --Meuuh */ +#define CR_MEAN_PTS_GAP 300000 + +/***************************************************************************** + * ClockToSysdate: converts a movie clock to system date + *****************************************************************************/ +static mtime_t ClockToSysdate( input_clock_t *cl, mtime_t i_clock ) +{ + if( cl->i_synchro_state != SYNCHRO_OK ) + return 0; + + return (i_clock - cl->cr_ref) * cl->i_rate / INPUT_RATE_DEFAULT + + cl->sysdate_ref; +} + +/***************************************************************************** + * ClockCurrent: converts current system date to clock units + ***************************************************************************** + * Caution : the synchro state must be SYNCHRO_OK for this to operate. + *****************************************************************************/ +static mtime_t ClockCurrent( input_clock_t *cl ) +{ + return (mdate() - cl->sysdate_ref) * INPUT_RATE_DEFAULT / cl->i_rate + + cl->cr_ref; +} + +/***************************************************************************** + * ClockNewRef: writes a new clock reference + *****************************************************************************/ +static void ClockNewRef( input_clock_t *cl, + mtime_t i_clock, mtime_t i_sysdate ) +{ + cl->cr_ref = i_clock; + cl->sysdate_ref = i_sysdate ; +} + +/***************************************************************************** + * input_ClockInit: reinitializes the clock reference after a stream + * discontinuity + *****************************************************************************/ +void input_ClockInit( input_clock_t *cl, bool b_master, int i_cr_average, int i_rate ) +{ + cl->i_synchro_state = SYNCHRO_START; + + cl->last_cr = 0; + cl->last_pts = 0; + cl->last_sysdate = 0; + cl->cr_ref = 0; + cl->sysdate_ref = 0; + cl->delta_cr = 0; + cl->i_delta_cr_residue = 0; + cl->i_rate = i_rate; + + cl->i_cr_average = i_cr_average; + + cl->b_master = b_master; +} + +/***************************************************************************** + * input_ClockSetPCR: manages a clock reference + *****************************************************************************/ +void input_ClockSetPCR( input_thread_t *p_input, + input_clock_t *cl, mtime_t i_clock ) +{ + const bool b_synchronize = p_input->b_can_pace_control && cl->b_master; + const mtime_t i_mdate = mdate(); + + if( ( cl->i_synchro_state != SYNCHRO_OK ) || + ( i_clock == 0 && cl->last_cr != 0 ) ) + { + /* Feed synchro with a new reference point. */ + ClockNewRef( cl, i_clock, + __MAX( cl->last_pts + CR_MEAN_PTS_GAP, i_mdate ) ); + cl->i_synchro_state = SYNCHRO_OK; + + if( !b_synchronize ) + { + cl->delta_cr = 0; + cl->i_delta_cr_residue = 0; + cl->last_update = 0; + } + } + else if ( cl->last_cr != 0 && + ( (cl->last_cr - i_clock) > CR_MAX_GAP || + (cl->last_cr - i_clock) < - CR_MAX_GAP ) ) + { + /* Stream discontinuity, for which we haven't received a + * warning from the stream control facilities (dd-edited + * stream ?). */ + msg_Warn( p_input, "clock gap, unexpected stream discontinuity" ); + input_ClockInit( cl, cl->b_master, cl->i_cr_average, cl->i_rate ); + /* Feed synchro with a new reference point. */ + msg_Warn( p_input, "feeding synchro with a new reference point trying to recover from clock gap" ); + ClockNewRef( cl, i_clock, + __MAX( cl->last_pts + CR_MEAN_PTS_GAP, i_mdate ) ); + cl->i_synchro_state = SYNCHRO_OK; + } + + cl->last_cr = i_clock; + cl->last_sysdate = i_mdate; + + if( b_synchronize ) + { + /* Wait a while before delivering the packets to the decoder. + * In case of multiple programs, we arbitrarily follow the + * clock of the selected program. */ + if( !p_input->p->b_out_pace_control ) + { + mtime_t i_wakeup = ClockToSysdate( cl, i_clock ); + while( (i_wakeup - mdate()) / CLOCK_FREQ > 1 ) + { + msleep( CLOCK_FREQ ); + if( p_input->b_die ) i_wakeup = mdate(); + } + mwait( i_wakeup ); + } + } + else if ( i_mdate - cl->last_update > 200000 ) + { + /* Smooth clock reference variations. */ + const mtime_t i_extrapoled_clock = ClockCurrent( cl ); + /* Bresenham algorithm to smooth variations. */ + const mtime_t i_tmp = cl->delta_cr * (cl->i_cr_average - 1) + + ( i_extrapoled_clock - i_clock ) * 1 + + cl->i_delta_cr_residue; + + cl->i_delta_cr_residue = i_tmp % cl->i_cr_average; + cl->delta_cr = i_tmp / cl->i_cr_average; + + cl->last_update = i_mdate; + } +} + +/***************************************************************************** + * input_ClockResetPCR: + *****************************************************************************/ +void input_ClockResetPCR( input_clock_t *cl ) +{ + cl->i_synchro_state = SYNCHRO_REINIT; + cl->last_pts = 0; +} + +/***************************************************************************** + * input_ClockGetTS: manages a PTS or DTS + *****************************************************************************/ +mtime_t input_ClockGetTS( input_thread_t * p_input, + input_clock_t *cl, mtime_t i_ts ) +{ + if( cl->i_synchro_state != SYNCHRO_OK ) + return 0; + + cl->last_pts = ClockToSysdate( cl, i_ts + cl->delta_cr ); + return cl->last_pts + p_input->i_pts_delay; +} + +/***************************************************************************** + * input_ClockSetRate: + *****************************************************************************/ +void input_ClockSetRate( input_clock_t *cl, int i_rate ) +{ + /* Move the reference point */ + if( cl->i_synchro_state == SYNCHRO_OK ) + ClockNewRef( cl, cl->last_cr, cl->last_sysdate ); + + cl->i_rate = i_rate; +} + diff --git a/VLC/input/control.c b/VLC/input/control.c new file mode 100644 index 0000000..67c6b75 --- /dev/null +++ b/VLC/input/control.c @@ -0,0 +1,643 @@ +/***************************************************************************** + * control.c + ***************************************************************************** + * Copyright (C) 1999-2004 the VideoLAN team + * $Id$ + * + * Authors: Gildas Bazin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#include +#include + +#include "input_internal.h" + + +static void UpdateBookmarksOption( input_thread_t * ); + +/**************************************************************************** + * input_Control + ****************************************************************************/ +/** + * Control function for inputs. + * \param p_input input handle + * \param i_query query type + * \return VLC_SUCCESS if ok + */ +int input_Control( input_thread_t *p_input, int i_query, ... ) +{ + va_list args; + int i_result; + + va_start( args, i_query ); + i_result = input_vaControl( p_input, i_query, args ); + va_end( args ); + + return i_result; +} + +int input_vaControl( input_thread_t *p_input, int i_query, va_list args ) +{ + seekpoint_t *p_bkmk, ***ppp_bkmk; + int i_bkmk = 0; + int *pi_bkmk; + + int i_int, *pi_int; + double f, *pf; + int64_t i_64, *pi_64; + + char *psz; + vlc_value_t val; + + switch( i_query ) + { + case INPUT_GET_POSITION: + pf = (double*)va_arg( args, double * ); + *pf = var_GetFloat( p_input, "position" ); + return VLC_SUCCESS; + + case INPUT_SET_POSITION: + f = (double)va_arg( args, double ); + return var_SetFloat( p_input, "position", f ); + + case INPUT_GET_LENGTH: + pi_64 = (int64_t*)va_arg( args, int64_t * ); + *pi_64 = var_GetTime( p_input, "length" ); + return VLC_SUCCESS; + + case INPUT_GET_TIME: + pi_64 = (int64_t*)va_arg( args, int64_t * ); + *pi_64 = var_GetTime( p_input, "time" ); + return VLC_SUCCESS; + + case INPUT_SET_TIME: + i_64 = (int64_t)va_arg( args, int64_t ); + return var_SetTime( p_input, "time", i_64 ); + + case INPUT_GET_RATE: + pi_int = (int*)va_arg( args, int * ); + *pi_int = var_GetInteger( p_input, "rate" ); + return VLC_SUCCESS; + + case INPUT_SET_RATE: + i_int = (int)va_arg( args, int ); + return var_SetInteger( p_input, "rate", i_int ); + + case INPUT_GET_STATE: + pi_int = (int*)va_arg( args, int * ); + *pi_int = var_GetInteger( p_input, "state" ); + return VLC_SUCCESS; + + case INPUT_SET_STATE: + i_int = (int)va_arg( args, int ); + return var_SetInteger( p_input, "state", i_int ); + + case INPUT_GET_AUDIO_DELAY: + pi_64 = (int64_t*)va_arg( args, int64_t * ); + *pi_64 = var_GetTime( p_input, "audio-delay" ); + return VLC_SUCCESS; + + case INPUT_GET_SPU_DELAY: + pi_64 = (int64_t*)va_arg( args, int64_t * ); + *pi_64 = var_GetTime( p_input, "spu-delay" ); + return VLC_SUCCESS; + + case INPUT_SET_AUDIO_DELAY: + i_64 = (int64_t)va_arg( args, int64_t ); + return var_SetTime( p_input, "audio-delay", i_64 ); + + case INPUT_SET_SPU_DELAY: + i_64 = (int64_t)va_arg( args, int64_t ); + return var_SetTime( p_input, "spu-delay", i_64 ); + + case INPUT_ADD_INFO: + { + /* FIXME : Impossible to use input_item_AddInfo because of + * the ... problem ? */ + char *psz_cat = (char *)va_arg( args, char * ); + char *psz_name = (char *)va_arg( args, char * ); + char *psz_format = (char *)va_arg( args, char * ); + + info_category_t *p_cat; + info_t *p_info; + int i; + + vlc_mutex_lock( &p_input->p->input.p_item->lock ); + for( i = 0; i < p_input->p->input.p_item->i_categories; i++ ) + { + if( !strcmp( p_input->p->input.p_item->pp_categories[i]->psz_name, + psz_cat ) ) break; + } + + if( i == p_input->p->input.p_item->i_categories ) + { + p_cat = malloc( sizeof( info_category_t ) ); + if( !p_cat ) + { + vlc_mutex_unlock( &p_input->p->input.p_item->lock ); + return VLC_EGENERIC; + } + p_cat->psz_name = strdup( psz_cat ); + p_cat->i_infos = 0; + p_cat->pp_infos = NULL; + INSERT_ELEM( p_input->p->input.p_item->pp_categories, + p_input->p->input.p_item->i_categories, + p_input->p->input.p_item->i_categories, p_cat ); + } + + p_cat = p_input->p->input.p_item->pp_categories[i]; + + for( i = 0; i < p_cat->i_infos; i++ ) + { + if( !strcmp( p_cat->pp_infos[i]->psz_name, psz_name ) ) + { + if( p_cat->pp_infos[i]->psz_value ) + free( p_cat->pp_infos[i]->psz_value ); + break; + } + } + + if( i == p_cat->i_infos ) + { + p_info = malloc( sizeof( info_t ) ); + if( !p_info ) + { + vlc_mutex_unlock( &p_input->p->input.p_item->lock ); + return VLC_EGENERIC; + } + + INSERT_ELEM( p_cat->pp_infos, p_cat->i_infos, + p_cat->i_infos, p_info ); + p_info->psz_name = strdup( psz_name ); + } + + p_info = p_cat->pp_infos[i]; + if( vasprintf( &p_info->psz_value, psz_format, args ) == -1 ) + p_info->psz_value = NULL; + + vlc_mutex_unlock( &p_input->p->input.p_item->lock ); + + if( !p_input->b_preparsing ) + { + vlc_event_t event; + event.type = vlc_InputItemInfoChanged; + vlc_event_send( &p_input->p->input.p_item->event_manager, &event ); + } + } + return VLC_SUCCESS; + + case INPUT_DEL_INFO: + { + char *psz_cat = (char *)va_arg( args, char * ); + char *psz_name = (char *)va_arg( args, char * ); + + info_category_t *p_cat = NULL; + int i_cat; + int i; + + vlc_mutex_lock( &p_input->p->input.p_item->lock ); + for( i_cat = 0; i_cat < p_input->p->input.p_item->i_categories; i_cat++ ) + { + if( !strcmp( p_input->p->input.p_item->pp_categories[i_cat]->psz_name, + psz_cat ) ) + { + p_cat = p_input->p->input.p_item->pp_categories[i_cat]; + break; + } + } + if( p_cat == NULL ) + { + vlc_mutex_unlock( &p_input->p->input.p_item->lock ); + return VLC_EGENERIC; + } + + if( psz_name ) + { + /* Remove a specific info */ + for( i = 0; i < p_cat->i_infos; i++ ) + { + if( !strcmp( p_cat->pp_infos[i]->psz_name, psz_name ) ) + { + free( p_cat->pp_infos[i]->psz_name ); + if( p_cat->pp_infos[i]->psz_value ) + free( p_cat->pp_infos[i]->psz_value ); + free( p_cat->pp_infos[i] ); + REMOVE_ELEM( p_cat->pp_infos, p_cat->i_infos, i ); + break; + } + } + if( i >= p_cat->i_infos ) + { + vlc_mutex_unlock( &p_input->p->input.p_item->lock ); + return VLC_EGENERIC; + } + } + else + { + /* Remove the complete categorie */ + for( i = 0; i < p_cat->i_infos; i++ ) + { + free( p_cat->pp_infos[i]->psz_name ); + if( p_cat->pp_infos[i]->psz_value ) + free( p_cat->pp_infos[i]->psz_value ); + free( p_cat->pp_infos[i] ); + } + if( p_cat->pp_infos ) + free( p_cat->pp_infos ); + REMOVE_ELEM( p_input->p->input.p_item->pp_categories, p_input->p->input.p_item->i_categories, i_cat ); + } + vlc_mutex_unlock( &p_input->p->input.p_item->lock ); + + if( !p_input->b_preparsing ) + { + vlc_event_t event; + event.type = vlc_InputItemInfoChanged; + vlc_event_send( &p_input->p->input.p_item->event_manager, &event ); + } + return VLC_SUCCESS; + } + + + case INPUT_GET_INFO: + { + char *psz_cat = (char *)va_arg( args, char * ); + char *psz_name = (char *)va_arg( args, char * ); + char **ppsz_value = (char **)va_arg( args, char ** ); + int i_ret = VLC_EGENERIC; + *ppsz_value = NULL; + + *ppsz_value = input_item_GetInfo( p_input->p->input.p_item, + psz_cat, psz_name ); + return i_ret; + } + + case INPUT_SET_NAME: + { + char *psz_name = (char *)va_arg( args, char * ); + + if( !psz_name ) return VLC_EGENERIC; + + vlc_mutex_lock( &p_input->p->input.p_item->lock ); + if( p_input->p->input.p_item->psz_name ) + free( p_input->p->input.p_item->psz_name ); + p_input->p->input.p_item->psz_name = strdup( psz_name ); + vlc_mutex_unlock( &p_input->p->input.p_item->lock ); + + if( !p_input->b_preparsing ) + { + vlc_event_t event; + event.type = vlc_InputItemNameChanged; + event.u.input_item_name_changed.new_name = psz_name; + vlc_event_send( &p_input->p->input.p_item->event_manager, &event ); + } + return VLC_SUCCESS; + } + + case INPUT_ADD_BOOKMARK: + p_bkmk = (seekpoint_t *)va_arg( args, seekpoint_t * ); + p_bkmk = vlc_seekpoint_Duplicate( p_bkmk ); + + vlc_mutex_lock( &p_input->p->input.p_item->lock ); + if( !p_bkmk->psz_name ) + { + if( asprintf( &p_bkmk->psz_name, _("Bookmark %i"), + p_input->p->i_bookmark ) == -1 ) + p_bkmk->psz_name = NULL; + } + + TAB_APPEND( p_input->p->i_bookmark, p_input->p->bookmark, p_bkmk ); + + /* Reflect the changes on the object var */ + var_Change( p_input, "bookmark", VLC_VAR_CLEARCHOICES, 0, 0 ); + { + vlc_value_t val, text; + int i; + + for( i = 0; i < p_input->p->i_bookmark; i++ ) + { + val.i_int = i; + text.psz_string = p_input->p->bookmark[i]->psz_name; + var_Change( p_input, "bookmark", VLC_VAR_ADDCHOICE, + &val, &text ); + } + } + vlc_mutex_unlock( &p_input->p->input.p_item->lock ); + + UpdateBookmarksOption( p_input ); + + return VLC_SUCCESS; + + case INPUT_CHANGE_BOOKMARK: + p_bkmk = (seekpoint_t *)va_arg( args, seekpoint_t * ); + i_bkmk = (int)va_arg( args, int ); + + vlc_mutex_lock( &p_input->p->input.p_item->lock ); + if( i_bkmk < p_input->p->i_bookmark ) + { + vlc_value_t val, text; + int i; + + p_input->p->bookmark[i_bkmk] = p_bkmk; + + /* Reflect the changes on the object var */ + var_Change( p_input, "bookmark", VLC_VAR_CLEARCHOICES, 0, 0 ); + for( i = 0; i < p_input->p->i_bookmark; i++ ) + { + val.i_int = i; + text.psz_string = p_input->p->bookmark[i]->psz_name; + var_Change( p_input, "bookmark", VLC_VAR_ADDCHOICE, + &val, &text ); + } + } + vlc_mutex_unlock( &p_input->p->input.p_item->lock ); + + UpdateBookmarksOption( p_input ); + + return VLC_SUCCESS; + + case INPUT_DEL_BOOKMARK: + i_bkmk = (int)va_arg( args, int ); + + vlc_mutex_lock( &p_input->p->input.p_item->lock ); + if( i_bkmk < p_input->p->i_bookmark ) + { + vlc_value_t val, text; + int i; + + p_bkmk = p_input->p->bookmark[i_bkmk]; + TAB_REMOVE( p_input->p->i_bookmark, p_input->p->bookmark, + p_bkmk ); + vlc_seekpoint_Delete( p_bkmk ); + + /* Reflect the changes on the object var */ + var_Change( p_input, "bookmark", VLC_VAR_CLEARCHOICES, 0, 0 ); + for( i = 0; i < p_input->p->i_bookmark; i++ ) + { + val.i_int = i; + text.psz_string = p_input->p->bookmark[i]->psz_name; + var_Change( p_input, "bookmark", VLC_VAR_ADDCHOICE, + &val, &text ); + } + vlc_mutex_unlock( &p_input->p->input.p_item->lock ); + + UpdateBookmarksOption( p_input ); + + return VLC_SUCCESS; + } + vlc_mutex_unlock( &p_input->p->input.p_item->lock ); + + return VLC_EGENERIC; + + case INPUT_GET_BOOKMARKS: + ppp_bkmk = (seekpoint_t ***)va_arg( args, seekpoint_t *** ); + pi_bkmk = (int *)va_arg( args, int * ); + + vlc_mutex_lock( &p_input->p->input.p_item->lock ); + if( p_input->p->i_bookmark ) + { + int i; + + *pi_bkmk = p_input->p->i_bookmark; + *ppp_bkmk = malloc( sizeof(seekpoint_t *) * + p_input->p->i_bookmark ); + for( i = 0; i < p_input->p->i_bookmark; i++ ) + { + (*ppp_bkmk)[i] = + vlc_seekpoint_Duplicate(p_input->p->bookmark[i]); + } + + vlc_mutex_unlock( &p_input->p->input.p_item->lock ); + return VLC_SUCCESS; + } + else + { + *ppp_bkmk = NULL; + *pi_bkmk = 0; + + vlc_mutex_unlock( &p_input->p->input.p_item->lock ); + return VLC_EGENERIC; + } + break; + + case INPUT_CLEAR_BOOKMARKS: + + vlc_mutex_lock( &p_input->p->input.p_item->lock ); + if( p_input->p->i_bookmark ) + { + int i; + + for( i = p_input->p->i_bookmark - 1; i >= 0; i-- ) + { + p_bkmk = p_input->p->bookmark[i]; + TAB_REMOVE( p_input->p->i_bookmark, p_input->p->bookmark, + p_bkmk ); + vlc_seekpoint_Delete( p_bkmk ); + } + var_Change( p_input, "bookmark", VLC_VAR_CLEARCHOICES, 0, 0 ); + } + vlc_mutex_unlock( &p_input->p->input.p_item->lock ); + + UpdateBookmarksOption( p_input ); + + return VLC_SUCCESS; + + case INPUT_SET_BOOKMARK: + i_bkmk = (int)va_arg( args, int ); + + vlc_mutex_lock( &p_input->p->input.p_item->lock ); + if( i_bkmk >= 0 && i_bkmk < p_input->p->i_bookmark ) + { + vlc_value_t pos; + int i_ret; + + if( p_input->p->bookmark[i_bkmk]->i_time_offset != -1 ) + { + pos.i_time = p_input->p->bookmark[i_bkmk]->i_time_offset; + i_ret = var_Set( p_input, "time", pos ); + } + else if( p_input->p->bookmark[i_bkmk]->i_byte_offset != -1 ) + { + // don't crash on bookmarks in live streams + if( stream_Size( p_input->p->input.p_stream ) == 0 ) + { + vlc_mutex_unlock( &p_input->p->input.p_item->lock ); + return VLC_EGENERIC; + } + pos.f_float = !p_input->p->input.p_stream ? 0 : + p_input->p->bookmark[i_bkmk]->i_byte_offset / + stream_Size( p_input->p->input.p_stream ); + i_ret = var_Set( p_input, "position", pos ); + } + else + { + pos.f_float = 0; + i_ret = var_Set( p_input, "position", pos ); + } + + vlc_mutex_unlock( &p_input->p->input.p_item->lock ); + return i_ret; + } + else + { + vlc_mutex_unlock( &p_input->p->input.p_item->lock ); + return VLC_EGENERIC; + } + + break; + + case INPUT_ADD_OPTION: + { + const char *psz_option = va_arg( args, const char * ); + const char *psz_value = va_arg( args, const char * ); + char *str; + int i; + + if( asprintf( &str, "%s=%s", psz_option, psz_value ) == -1 ) + return VLC_ENOMEM; + + i = input_item_AddOpt( p_input->p->input.p_item, str, + VLC_INPUT_OPTION_UNIQUE ); + free( str ); + return i; + } + + case INPUT_GET_BYTE_POSITION: + pi_64 = (int64_t*)va_arg( args, int64_t * ); + *pi_64 = !p_input->p->input.p_stream ? 0 : + stream_Tell( p_input->p->input.p_stream ); + return VLC_SUCCESS; + + case INPUT_SET_BYTE_SIZE: + pi_64 = (int64_t*)va_arg( args, int64_t * ); + *pi_64 = !p_input->p->input.p_stream ? 0 : + stream_Size( p_input->p->input.p_stream ); + return VLC_SUCCESS; + + case INPUT_GET_VIDEO_FPS: + { + int i; + pf = (double*)va_arg( args, double * ); + vlc_mutex_lock( &p_input->p->input.p_item->lock ); + *pf = p_input->p->input.f_fps; + for( i = 0; i < p_input->p->i_slave && *pf <= 0.001; i++ ) + *pf = p_input->p->slave[i]->f_fps; + vlc_mutex_unlock( &p_input->p->input.p_item->lock ); + return VLC_SUCCESS; + } + + case INPUT_ADD_SLAVE: + psz = (char*)va_arg( args, char * ); + if( psz && *psz ) + { + val.psz_string = strdup( psz ); + input_ControlPush( p_input, INPUT_CONTROL_ADD_SLAVE, &val ); + } + return VLC_SUCCESS; + + case INPUT_GET_ATTACHMENTS: /* arg1=input_attachment_t***, arg2=int* res=can fail */ + { + input_attachment_t ***ppp_attachment = (input_attachment_t***)va_arg( args, input_attachment_t *** ); + int *pi_attachment = (int*)va_arg( args, int * ); + int i; + + vlc_mutex_lock( &p_input->p->input.p_item->lock ); + if( p_input->p->i_attachment <= 0 ) + { + vlc_mutex_unlock( &p_input->p->input.p_item->lock ); + *ppp_attachment = NULL; + *pi_attachment = 0; + return VLC_EGENERIC; + } + *pi_attachment = p_input->p->i_attachment; + *ppp_attachment = malloc( sizeof(input_attachment_t**) * p_input->p->i_attachment ); + for( i = 0; i < p_input->p->i_attachment; i++ ) + (*ppp_attachment)[i] = vlc_input_attachment_Duplicate( p_input->p->attachment[i] ); + + vlc_mutex_unlock( &p_input->p->input.p_item->lock ); + return VLC_SUCCESS; + } + + case INPUT_GET_ATTACHMENT: /* arg1=input_attachment_t**, arg2=char* res=can fail */ + { + input_attachment_t **pp_attachment = (input_attachment_t**)va_arg( args, input_attachment_t ** ); + const char *psz_name = (const char*)va_arg( args, const char * ); + int i; + + vlc_mutex_lock( &p_input->p->input.p_item->lock ); + for( i = 0; i < p_input->p->i_attachment; i++ ) + { + if( !strcmp( p_input->p->attachment[i]->psz_name, psz_name ) ) + { + *pp_attachment = vlc_input_attachment_Duplicate( p_input->p->attachment[i] ); + vlc_mutex_unlock( &p_input->p->input.p_item->lock ); + return VLC_SUCCESS; + } + } + *pp_attachment = NULL; + vlc_mutex_unlock( &p_input->p->input.p_item->lock ); + return VLC_EGENERIC; + } + + + default: + msg_Err( p_input, "unknown query in input_vaControl" ); + return VLC_EGENERIC; + } +} + +static void UpdateBookmarksOption( input_thread_t *p_input ) +{ + int i, i_len = 0; + char *psz_value = NULL, *psz_next = NULL; + + vlc_mutex_lock( &p_input->p->input.p_item->lock ); + if( p_input->p->i_bookmark > 0 ) + { + for( i = 0; i < p_input->p->i_bookmark; i++ ) + { + i_len += snprintf( NULL, 0, "{name=%s,bytes=%"PRId64",time=%"PRId64"}", + p_input->p->bookmark[i]->psz_name, + p_input->p->bookmark[i]->i_byte_offset, + p_input->p->bookmark[i]->i_time_offset/1000000 ); + } + psz_value = psz_next = malloc( i_len + p_input->p->i_bookmark ); + + for( i = 0; i < p_input->p->i_bookmark; i++ ) + { + sprintf( psz_next, "{name=%s,bytes=%"PRId64",time=%"PRId64"}", + p_input->p->bookmark[i]->psz_name, + p_input->p->bookmark[i]->i_byte_offset, + p_input->p->bookmark[i]->i_time_offset/1000000 ); + + psz_next += strlen( psz_next ); + if( i < p_input->p->i_bookmark - 1) + *psz_next = ','; psz_next++; + } + } + vlc_mutex_unlock( &p_input->p->input.p_item->lock ); + + input_Control( p_input, INPUT_ADD_OPTION, "bookmarks", + psz_value ? psz_value : "" ); + free( psz_value ); +} + diff --git a/VLC/input/decoder.c b/VLC/input/decoder.c new file mode 100644 index 0000000..4217608 --- /dev/null +++ b/VLC/input/decoder.c @@ -0,0 +1,1459 @@ +/***************************************************************************** + * decoder.c: Functions for the management of decoders + ***************************************************************************** + * Copyright (C) 1999-2004 the VideoLAN team + * $Id$ + * + * Authors: Christophe Massiot + * Gildas Bazin + * Laurent Aimar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include + +#include "vlc_common.h" + +#include "vlc_block.h" +#include "vlc_vout.h" +#include "vlc_aout.h" +#include "vlc_sout.h" +#include "vlc_codec.h" +#include "vlc_osd.h" + +#include "vlc_interface.h" +#include "aout_internal.h" +#include "stream_output.h" +#include "input_internal.h" + +static decoder_t * CreateDecoder( input_thread_t *, es_format_t *, int ); +static void DeleteDecoder( decoder_t * ); + +static void* DecoderThread( vlc_object_t * ); +static int DecoderDecode( decoder_t * p_dec, block_t *p_block ); + +/* Buffers allocation callbacks for the decoders */ +static aout_buffer_t *aout_new_buffer( decoder_t *, int ); +static void aout_del_buffer( decoder_t *, aout_buffer_t * ); + +static picture_t *vout_new_buffer( decoder_t * ); +static void vout_del_buffer( decoder_t *, picture_t * ); +static void vout_link_picture( decoder_t *, picture_t * ); +static void vout_unlink_picture( decoder_t *, picture_t * ); + +static subpicture_t *spu_new_buffer( decoder_t * ); +static void spu_del_buffer( decoder_t *, subpicture_t * ); + +static es_format_t null_es_format; + +struct decoder_owner_sys_t +{ + bool b_own_thread; + + int64_t i_preroll_end; + + input_thread_t *p_input; + + aout_instance_t *p_aout; + aout_input_t *p_aout_input; + + vout_thread_t *p_vout; + + vout_thread_t *p_spu_vout; + int i_spu_channel; + + sout_instance_t *p_sout; + sout_packetizer_input_t *p_sout_input; + + /* Some decoders require already packetized data (ie. not truncated) */ + decoder_t *p_packetizer; + + /* Current format in use by the output */ + video_format_t video; + audio_format_t audio; + es_format_t sout; + + /* fifo */ + block_fifo_t *p_fifo; + + /* CC */ + bool b_cc_supported; + vlc_mutex_t lock_cc; + bool pb_cc_present[4]; + decoder_t *pp_cc[4]; +}; + +/* */ +static void DecoderUnsupportedCodec( decoder_t *p_dec, vlc_fourcc_t codec ) +{ + msg_Err( p_dec, "no suitable decoder module for fourcc `%4.4s'.\n" + "VLC probably does not support this sound or video format.", + (char*)&codec ); + intf_UserFatal( p_dec, false, _("No suitable decoder module"), + _("VLC does not support the audio or video format \"%4.4s\". " + "Unfortunately there is no way for you to fix this."), (char*)&codec ); +} + +/* decoder_GetInputAttachment: + */ +input_attachment_t *decoder_GetInputAttachment( decoder_t *p_dec, + const char *psz_name ) +{ + input_attachment_t *p_attachment; + if( input_Control( p_dec->p_owner->p_input, INPUT_GET_ATTACHMENT, &p_attachment, psz_name ) ) + return NULL; + return p_attachment; +} +/* decoder_GetInputAttachments: + */ +int decoder_GetInputAttachments( decoder_t *p_dec, + input_attachment_t ***ppp_attachment, + int *pi_attachment ) +{ + return input_Control( p_dec->p_owner->p_input, INPUT_GET_ATTACHMENTS, + ppp_attachment, pi_attachment ); +} +/* decoder_GetDisplayDate: + */ +mtime_t decoder_GetDisplayDate( decoder_t *p_dec, mtime_t i_ts ) +{ + VLC_UNUSED(p_dec); + return i_ts; +} + +/** + * Spawns a new decoder thread + * + * \param p_input the input thread + * \param p_es the es descriptor + * \return the spawned decoder object + */ +decoder_t *input_DecoderNew( input_thread_t *p_input, + es_format_t *fmt, bool b_force_decoder ) +{ + decoder_t *p_dec = NULL; + vlc_value_t val; + +#ifndef ENABLE_SOUT + (void)b_force_decoder; +#else + /* If we are in sout mode, search for packetizer module */ + if( p_input->p->p_sout && !b_force_decoder ) + { + /* Create the decoder configuration structure */ + p_dec = CreateDecoder( p_input, fmt, VLC_OBJECT_PACKETIZER ); + if( p_dec == NULL ) + { + msg_Err( p_input, "could not create packetizer" ); + intf_UserFatal( p_input, false, _("Streaming / Transcoding failed"), + _("VLC could not open the packetizer module.") ); + return NULL; + } + } + else +#endif + { + /* Create the decoder configuration structure */ + p_dec = CreateDecoder( p_input, fmt, VLC_OBJECT_DECODER ); + if( p_dec == NULL ) + { + msg_Err( p_input, "could not create decoder" ); + intf_UserFatal( p_input, false, _("Streaming / Transcoding failed"), + _("VLC could not open the decoder module.") ); + return NULL; + } + } + + if( !p_dec->p_module ) + { + DecoderUnsupportedCodec( p_dec, fmt->i_codec ); + + DeleteDecoder( p_dec ); + vlc_object_release( p_dec ); + return NULL; + } + + if( p_input->p->p_sout && p_input->p->input.b_can_pace_control && + !b_force_decoder ) + { + msg_Dbg( p_input, "stream out mode -> no decoder thread" ); + p_dec->p_owner->b_own_thread = false; + } + else + { + var_Get( p_input, "minimize-threads", &val ); + p_dec->p_owner->b_own_thread = !val.b_bool; + } + + if( p_dec->p_owner->b_own_thread ) + { + int i_priority; + if( fmt->i_cat == AUDIO_ES ) + i_priority = VLC_THREAD_PRIORITY_AUDIO; + else + i_priority = VLC_THREAD_PRIORITY_VIDEO; + + /* Spawn the decoder thread */ + if( vlc_thread_create( p_dec, "decoder", DecoderThread, + i_priority, false ) ) + { + msg_Err( p_dec, "cannot spawn decoder thread" ); + module_Unneed( p_dec, p_dec->p_module ); + DeleteDecoder( p_dec ); + vlc_object_release( p_dec ); + return NULL; + } + } + + return p_dec; +} + +/** + * Kills a decoder thread and waits until it's finished + * + * \param p_input the input thread + * \param p_es the es descriptor + * \return nothing + */ +void input_DecoderDelete( decoder_t *p_dec ) +{ + vlc_object_kill( p_dec ); + + if( p_dec->p_owner->b_own_thread ) + { + /* Make sure the thread leaves the function by + * sending it an empty block. */ + block_t *p_block = block_New( p_dec, 0 ); + input_DecoderDecode( p_dec, p_block ); + + vlc_thread_join( p_dec ); + + /* Don't module_Unneed() here because of the dll loader that wants + * close() in the same thread than open()/decode() */ + } + else + { + /* Flush */ + input_DecoderDecode( p_dec, NULL ); + + module_Unneed( p_dec, p_dec->p_module ); + } + + /* */ + if( p_dec->p_owner->b_cc_supported ) + { + int i; + for( i = 0; i < 4; i++ ) + input_DecoderSetCcState( p_dec, false, i ); + } + + /* Delete decoder configuration */ + DeleteDecoder( p_dec ); + + /* Delete the decoder */ + vlc_object_release( p_dec ); +} + +/** + * Put a block_t in the decoder's fifo. + * + * \param p_dec the decoder object + * \param p_block the data block + */ +void input_DecoderDecode( decoder_t * p_dec, block_t *p_block ) +{ + if( p_dec->p_owner->b_own_thread ) + { + if( p_dec->p_owner->p_input->p->b_out_pace_control ) + { + /* FIXME !!!!! */ + while( !p_dec->b_die && !p_dec->b_error && + block_FifoCount( p_dec->p_owner->p_fifo ) > 10 ) + { + msleep( 1000 ); + } + } + else if( block_FifoSize( p_dec->p_owner->p_fifo ) > 50000000 /* 50 MB */ ) + { + /* FIXME: ideally we would check the time amount of data + * in the fifo instead of its size. */ + msg_Warn( p_dec, "decoder/packetizer fifo full (data not " + "consumed quickly enough), resetting fifo!" ); + block_FifoEmpty( p_dec->p_owner->p_fifo ); + } + + block_FifoPut( p_dec->p_owner->p_fifo, p_block ); + } + else + { + if( p_dec->b_error || (p_block && p_block->i_buffer <= 0) ) + { + if( p_block ) block_Release( p_block ); + } + else + { + DecoderDecode( p_dec, p_block ); + } + } +} + +void input_DecoderDiscontinuity( decoder_t * p_dec, bool b_flush ) +{ + block_t *p_null; + + /* Empty the fifo */ + if( p_dec->p_owner->b_own_thread && b_flush ) + block_FifoEmpty( p_dec->p_owner->p_fifo ); + + /* Send a special block */ + p_null = block_New( p_dec, 128 ); + p_null->i_flags |= BLOCK_FLAG_DISCONTINUITY; + /* FIXME check for p_packetizer or b_packitized from es_format_t of input ? */ + if( p_dec->p_owner->p_packetizer && b_flush ) + p_null->i_flags |= BLOCK_FLAG_CORRUPTED; + memset( p_null->p_buffer, 0, p_null->i_buffer ); + + input_DecoderDecode( p_dec, p_null ); +} + +bool input_DecoderEmpty( decoder_t * p_dec ) +{ + if( p_dec->p_owner->b_own_thread + && block_FifoCount( p_dec->p_owner->p_fifo ) > 0 ) + { + return false; + } + return true; +} + +void input_DecoderIsCcPresent( decoder_t *p_dec, bool pb_present[4] ) +{ + int i; + + vlc_mutex_lock( &p_dec->p_owner->lock_cc ); + for( i = 0; i < 4; i++ ) + pb_present[i] = p_dec->p_owner->pb_cc_present[i]; + vlc_mutex_unlock( &p_dec->p_owner->lock_cc ); +} +int input_DecoderSetCcState( decoder_t *p_dec, bool b_decode, int i_channel ) +{ + decoder_owner_sys_t *p_owner = p_dec->p_owner; + + //msg_Warn( p_dec, "input_DecoderSetCcState: %d @%d", b_decode, i_channel ); + + if( i_channel < 0 || i_channel >= 4 || !p_owner->pb_cc_present[i_channel] ) + return VLC_EGENERIC; + + if( b_decode ) + { + static const vlc_fourcc_t fcc[4] = { + VLC_FOURCC('c', 'c', '1', ' '), + VLC_FOURCC('c', 'c', '2', ' '), + VLC_FOURCC('c', 'c', '3', ' '), + VLC_FOURCC('c', 'c', '4', ' '), + }; + decoder_t *p_cc; + es_format_t fmt; + + es_format_Init( &fmt, SPU_ES, fcc[i_channel] ); + p_cc = CreateDecoder( p_owner->p_input, &fmt, VLC_OBJECT_DECODER ); + if( !p_cc ) + { + msg_Err( p_dec, "could not create decoder" ); + intf_UserFatal( p_dec, false, _("Streaming / Transcoding failed"), + _("VLC could not open the decoder module.") ); + return VLC_EGENERIC; + } + else if( !p_cc->p_module ) + { + DecoderUnsupportedCodec( p_dec, fcc[i_channel] ); + DeleteDecoder( p_cc ); + vlc_object_release( p_cc ); + return VLC_EGENERIC; + } + + vlc_mutex_lock( &p_owner->lock_cc ); + p_dec->p_owner->pp_cc[i_channel] = p_cc; + vlc_mutex_unlock( &p_owner->lock_cc ); + } + else + { + decoder_t *p_cc; + + vlc_mutex_lock( &p_owner->lock_cc ); + p_cc = p_dec->p_owner->pp_cc[i_channel]; + p_dec->p_owner->pp_cc[i_channel] = NULL; + vlc_mutex_unlock( &p_dec->p_owner->lock_cc ); + + if( p_cc ) + { + vlc_object_kill( p_cc ); + module_Unneed( p_cc, p_cc->p_module ); + DeleteDecoder( p_cc ); + vlc_object_release( p_cc ); + } + } + return VLC_SUCCESS; +} +int input_DecoderGetCcState( decoder_t *p_dec, bool *pb_decode, int i_channel ) +{ + decoder_owner_sys_t *p_owner = p_dec->p_owner; + + *pb_decode = false; + if( i_channel < 0 || i_channel >= 4 || !p_owner->pb_cc_present[i_channel] ) + return VLC_EGENERIC; + + vlc_mutex_lock( &p_owner->lock_cc ); + *pb_decode = p_dec->p_owner->pp_cc[i_channel] != NULL; + vlc_mutex_unlock( &p_dec->p_owner->lock_cc ); + return VLC_EGENERIC; +} + +/** + * Create a decoder object + * + * \param p_input the input thread + * \param p_es the es descriptor + * \param i_object_type Object type as define in include/vlc_objects.h + * \return the decoder object + */ +static decoder_t * CreateDecoder( input_thread_t *p_input, + es_format_t *fmt, int i_object_type ) +{ + decoder_t *p_dec; + decoder_owner_sys_t *p_owner; + int i; + + p_dec = vlc_object_create( p_input, i_object_type ); + if( p_dec == NULL ) + return NULL; + + p_dec->pf_decode_audio = 0; + p_dec->pf_decode_video = 0; + p_dec->pf_decode_sub = 0; + p_dec->pf_get_cc = 0; + p_dec->pf_packetize = 0; + + /* Initialize the decoder fifo */ + p_dec->p_module = NULL; + + memset( &null_es_format, 0, sizeof(es_format_t) ); + es_format_Copy( &p_dec->fmt_in, fmt ); + es_format_Copy( &p_dec->fmt_out, &null_es_format ); + + /* Allocate our private structure for the decoder */ + p_dec->p_owner = p_owner = malloc( sizeof( decoder_owner_sys_t ) ); + if( p_dec->p_owner == NULL ) + { + vlc_object_release( p_dec ); + return NULL; + } + p_dec->p_owner->b_own_thread = true; + p_dec->p_owner->i_preroll_end = -1; + p_dec->p_owner->p_input = p_input; + p_dec->p_owner->p_aout = NULL; + p_dec->p_owner->p_aout_input = NULL; + p_dec->p_owner->p_vout = NULL; + p_dec->p_owner->p_spu_vout = NULL; + p_dec->p_owner->i_spu_channel = 0; + p_dec->p_owner->p_sout = p_input->p->p_sout; + p_dec->p_owner->p_sout_input = NULL; + p_dec->p_owner->p_packetizer = NULL; + + /* decoder fifo */ + if( ( p_dec->p_owner->p_fifo = block_FifoNew() ) == NULL ) + { + free( p_dec->p_owner ); + vlc_object_release( p_dec ); + return NULL; + } + + /* Set buffers allocation callbacks for the decoders */ + p_dec->pf_aout_buffer_new = aout_new_buffer; + p_dec->pf_aout_buffer_del = aout_del_buffer; + p_dec->pf_vout_buffer_new = vout_new_buffer; + p_dec->pf_vout_buffer_del = vout_del_buffer; + p_dec->pf_picture_link = vout_link_picture; + p_dec->pf_picture_unlink = vout_unlink_picture; + p_dec->pf_spu_buffer_new = spu_new_buffer; + p_dec->pf_spu_buffer_del = spu_del_buffer; + + vlc_object_attach( p_dec, p_input ); + + /* Find a suitable decoder/packetizer module */ + if( i_object_type == VLC_OBJECT_DECODER ) + p_dec->p_module = module_Need( p_dec, "decoder", "$codec", 0 ); + else + p_dec->p_module = module_Need( p_dec, "packetizer", "$packetizer", 0 ); + + /* Check if decoder requires already packetized data */ + if( i_object_type == VLC_OBJECT_DECODER && + p_dec->b_need_packetized && !p_dec->fmt_in.b_packetized ) + { + p_dec->p_owner->p_packetizer = + vlc_object_create( p_input, VLC_OBJECT_PACKETIZER ); + if( p_dec->p_owner->p_packetizer ) + { + es_format_Copy( &p_dec->p_owner->p_packetizer->fmt_in, + &p_dec->fmt_in ); + + es_format_Copy( &p_dec->p_owner->p_packetizer->fmt_out, + &null_es_format ); + + vlc_object_attach( p_dec->p_owner->p_packetizer, p_input ); + + p_dec->p_owner->p_packetizer->p_module = + module_Need( p_dec->p_owner->p_packetizer, + "packetizer", "$packetizer", 0 ); + + if( !p_dec->p_owner->p_packetizer->p_module ) + { + es_format_Clean( &p_dec->p_owner->p_packetizer->fmt_in ); + vlc_object_detach( p_dec->p_owner->p_packetizer ); + vlc_object_release( p_dec->p_owner->p_packetizer ); + } + } + } + + /* Copy ourself the input replay gain */ + if( fmt->i_cat == AUDIO_ES ) + { + for( i = 0; i < AUDIO_REPLAY_GAIN_MAX; i++ ) + { + if( !p_dec->fmt_out.audio_replay_gain.pb_peak[i] ) + { + p_dec->fmt_out.audio_replay_gain.pb_peak[i] = fmt->audio_replay_gain.pb_peak[i]; + p_dec->fmt_out.audio_replay_gain.pf_peak[i] = fmt->audio_replay_gain.pf_peak[i]; + } + if( !p_dec->fmt_out.audio_replay_gain.pb_gain[i] ) + { + p_dec->fmt_out.audio_replay_gain.pb_gain[i] = fmt->audio_replay_gain.pb_gain[i]; + p_dec->fmt_out.audio_replay_gain.pf_gain[i] = fmt->audio_replay_gain.pf_gain[i]; + } + } + } + /* */ + p_owner->b_cc_supported = false; + if( i_object_type == VLC_OBJECT_DECODER ) + { + if( p_owner->p_packetizer && p_owner->p_packetizer->pf_get_cc ) + p_owner->b_cc_supported = true; + if( p_dec->pf_get_cc ) + p_owner->b_cc_supported = true; + } + + vlc_mutex_init( &p_owner->lock_cc ); + for( i = 0; i < 4; i++ ) + { + p_owner->pb_cc_present[i] = false; + p_owner->pp_cc[i] = NULL; + } + return p_dec; +} + +/** + * The decoding main loop + * + * \param p_dec the decoder + * \return 0 + */ +static void* DecoderThread( vlc_object_t *p_this ) +{ + decoder_t * p_dec = (decoder_t *)p_this; + block_t *p_block; + + /* The decoder's main loop */ + while( !p_dec->b_die && !p_dec->b_error ) + { + if( ( p_block = block_FifoGet( p_dec->p_owner->p_fifo ) ) == NULL ) + { + p_dec->b_error = 1; + break; + } + if( DecoderDecode( p_dec, p_block ) != VLC_SUCCESS ) + { + break; + } + } + + while( !p_dec->b_die ) + { + /* Trash all received PES packets */ + p_block = block_FifoGet( p_dec->p_owner->p_fifo ); + if( p_block ) block_Release( p_block ); + } + + /* We do it here because of the dll loader that wants close() in the + * same thread than open()/decode() */ + module_Unneed( p_dec, p_dec->p_module ); + + return 0; +} + +static inline void DecoderUpdatePreroll( int64_t *pi_preroll, const block_t *p ) +{ + if( p->i_flags & (BLOCK_FLAG_PREROLL|BLOCK_FLAG_DISCONTINUITY) ) + *pi_preroll = INT64_MAX; + else if( p->i_pts > 0 ) + *pi_preroll = __MIN( *pi_preroll, p->i_pts ); + else if( p->i_dts > 0 ) + *pi_preroll = __MIN( *pi_preroll, p->i_dts ); +} +static void DecoderDecodeAudio( decoder_t *p_dec, block_t *p_block ) +{ + input_thread_t *p_input = p_dec->p_owner->p_input; + const int i_rate = p_block->i_rate; + aout_buffer_t *p_aout_buf; + + while( (p_aout_buf = p_dec->pf_decode_audio( p_dec, &p_block )) ) + { + aout_instance_t *p_aout = p_dec->p_owner->p_aout; + aout_input_t *p_aout_input = p_dec->p_owner->p_aout_input; + + if( p_dec->b_die ) + { + /* It prevent freezing VLC in case of broken decoder */ + aout_DecDeleteBuffer( p_aout, p_aout_input, p_aout_buf ); + if( p_block ) + block_Release( p_block ); + break; + } + vlc_mutex_lock( &p_input->p->counters.counters_lock ); + stats_UpdateInteger( p_dec, p_input->p->counters.p_decoded_audio, 1, NULL ); + vlc_mutex_unlock( &p_input->p->counters.counters_lock ); + + if( p_aout_buf->start_date < p_dec->p_owner->i_preroll_end ) + { + aout_DecDeleteBuffer( p_aout, p_aout_input, p_aout_buf ); + continue; + } + + if( p_dec->p_owner->i_preroll_end > 0 ) + { + /* FIXME TODO flush audio output (don't know how to do that) */ + msg_Dbg( p_dec, "End of audio preroll" ); + p_dec->p_owner->i_preroll_end = -1; + } + aout_DecPlay( p_aout, p_aout_input, p_aout_buf, i_rate ); + } +} +static void DecoderGetCc( decoder_t *p_dec, decoder_t *p_dec_cc ) +{ + block_t *p_cc; + bool pb_present[4]; + int i; + int i_cc_decoder; + + assert( p_dec_cc->pf_get_cc != NULL ); + + /* Do not try retreiving CC if not wanted (sout) or cannot be retreived */ + if( !p_dec->p_owner->b_cc_supported ) + return; + + p_cc = p_dec_cc->pf_get_cc( p_dec_cc, pb_present ); + if( !p_cc ) + return; + + vlc_mutex_lock( &p_dec->p_owner->lock_cc ); + for( i = 0, i_cc_decoder = 0; i < 4; i++ ) + { + p_dec->p_owner->pb_cc_present[i] |= pb_present[i]; + if( p_dec->p_owner->pp_cc[i] ) + i_cc_decoder++; + } + + for( i = 0; i < 4; i++ ) + { + if( !p_dec->p_owner->pp_cc[i] ) + continue; + + if( i_cc_decoder > 1 ) + DecoderDecode( p_dec->p_owner->pp_cc[i], block_Duplicate( p_cc ) ); + else + DecoderDecode( p_dec->p_owner->pp_cc[i], p_cc ); + i_cc_decoder--; + } + vlc_mutex_unlock( &p_dec->p_owner->lock_cc ); +} +static void VoutDisplayedPicture( vout_thread_t *p_vout, picture_t *p_pic ) +{ + vlc_mutex_lock( &p_vout->picture_lock ); + + if( p_pic->i_status == READY_PICTURE ) + { + /* Grr cannot destroy ready picture by myself so be sure vout won't like it */ + p_pic->date = 1; + } + else if( p_pic->i_refcount > 0 ) + { + p_pic->i_status = DISPLAYED_PICTURE; + } + else + { + p_pic->i_status = DESTROYED_PICTURE; + p_vout->i_heap_size--; + } + + vlc_mutex_unlock( &p_vout->picture_lock ); +} +static void VoutFlushPicture( vout_thread_t *p_vout ) +{ + int i; + vlc_mutex_lock( &p_vout->picture_lock ); + for( i = 0; i < p_vout->render.i_pictures; i++ ) + { + picture_t *p_pic = p_vout->render.pp_picture[i]; + + if( p_pic->i_status == READY_PICTURE || + p_pic->i_status == DISPLAYED_PICTURE ) + { + /* We cannot change picture status if it is in READY_PICTURE state, + * Just make sure they won't be displayed */ + p_pic->date = 1; + } + } + vlc_mutex_unlock( &p_vout->picture_lock ); +} + + +static void optimize_video_pts( decoder_t *p_dec ) +{ + picture_t * oldest_pict = NULL; + picture_t * youngest_pict = NULL; + int i; + + input_thread_t * p_input = p_dec->p_owner->p_input; + vout_thread_t * p_vout = p_dec->p_owner->p_vout; + input_thread_private_t * p_priv = p_input->p; + + /* Enable with --auto-adjust-pts-delay */ + if( !p_priv->pts_adjust.auto_adjust ) return; + + for( i = 0; i < I_RENDERPICTURES; i++ ) + { + picture_t * pic = PP_RENDERPICTURE[i]; + if( pic->i_status != READY_PICTURE ) + continue; + + if( !oldest_pict || pic->date < oldest_pict->date ) + oldest_pict = pic; + if( !youngest_pict || pic->date > youngest_pict->date ) + youngest_pict = pic; + } + + if( !youngest_pict || !oldest_pict ) + return; + + /* Try to find if we can reduce the pts + * This first draft is way to simple, and we can't say if the + * algo will converge. It's also full of constants. + * But this simple algo allows to reduce the latency + * to the minimum. + * The whole point of this, is to bypass the pts_delay set + * by the access but also the delay arbitraly set by + * the remote server. + * Actually the remote server's muxer may set up a + * pts<->dts delay in the muxed stream. That is + * why we may end up in having a negative pts_delay, + * to compensate that artificial delay. */ + mtime_t buffer_size = youngest_pict->date - oldest_pict->date; + int64_t pts_slide = 0; + if( buffer_size < 10000 ) + { + if( p_priv->pts_adjust.i_num_faulty > 10 ) + { + pts_slide = __MAX(p_input->i_pts_delay *3 / 2, 10000); + p_priv->pts_adjust.i_num_faulty = 0; + } + if( p_priv->pts_adjust.to_high ) + { + p_priv->pts_adjust.to_high = !p_priv->pts_adjust.to_high; + p_priv->pts_adjust.i_num_faulty = 0; + } + p_priv->pts_adjust.i_num_faulty++; + } + else if( buffer_size > 100000 ) + { + if( p_priv->pts_adjust.i_num_faulty > 25 ) + { + pts_slide = -buffer_size/2; + p_priv->pts_adjust.i_num_faulty = 0; + } + if( p_priv->pts_adjust.to_high ) + { + p_priv->pts_adjust.to_high = !p_priv->pts_adjust.to_high; + p_priv->pts_adjust.i_num_faulty = 0; + } + p_priv->pts_adjust.i_num_faulty++; + } + if( pts_slide ) + { + mtime_t origi_delay = p_input->i_pts_delay; + + p_input->i_pts_delay += pts_slide; + + /* Don't play with the pts delay for more than -2<->3sec */ + if( p_input->i_pts_delay < -2000000 ) + p_input->i_pts_delay = -2000000; + else if( p_input->i_pts_delay > 3000000 ) + p_input->i_pts_delay = 3000000; + pts_slide = p_input->i_pts_delay - origi_delay; + + msg_Dbg( p_input, "Sliding the pts by %dms pts delay at %dms picture buffer was %dms", + (int)pts_slide/1000, (int)p_input->i_pts_delay/1000, (int)buffer_size/1000); + + vlc_mutex_lock( &p_vout->picture_lock ); + /* Slide all the picture */ + for( i = 0; i < I_RENDERPICTURES; i++ ) + PP_RENDERPICTURE[i]->date += pts_slide; + /* FIXME: slide aout/spu */ + vlc_mutex_unlock( &p_vout->picture_lock ); + + } +} + +static void DecoderDecodeVideo( decoder_t *p_dec, block_t *p_block ) +{ + input_thread_t *p_input = p_dec->p_owner->p_input; + picture_t *p_pic; + + while( (p_pic = p_dec->pf_decode_video( p_dec, &p_block )) ) + { + vout_thread_t *p_vout = p_dec->p_owner->p_vout; + if( p_dec->b_die ) + { + /* It prevent freezing VLC in case of broken decoder */ + VoutDisplayedPicture( p_vout, p_pic ); + if( p_block ) + block_Release( p_block ); + break; + } + + vlc_mutex_lock( &p_input->p->counters.counters_lock ); + stats_UpdateInteger( p_dec, p_input->p->counters.p_decoded_video, 1, NULL ); + vlc_mutex_unlock( &p_input->p->counters.counters_lock ); + + if( p_pic->date < p_dec->p_owner->i_preroll_end ) + { + VoutDisplayedPicture( p_vout, p_pic ); + continue; + } + + if( p_dec->p_owner->i_preroll_end > 0 ) + { + msg_Dbg( p_dec, "End of video preroll" ); + if( p_vout ) + VoutFlushPicture( p_vout ); + /* */ + p_dec->p_owner->i_preroll_end = -1; + } + + if( ( !p_dec->p_owner->p_packetizer || !p_dec->p_owner->p_packetizer->pf_get_cc ) && p_dec->pf_get_cc ) + DecoderGetCc( p_dec, p_dec ); + + vout_DatePicture( p_vout, p_pic, p_pic->date ); + + optimize_video_pts( p_dec ); + + vout_DisplayPicture( p_vout, p_pic ); + } +} + +/** + * Decode a block + * + * \param p_dec the decoder object + * \param p_block the block to decode + * \return VLC_SUCCESS or an error code + */ +static int DecoderDecode( decoder_t *p_dec, block_t *p_block ) +{ + decoder_owner_sys_t *p_sys = (decoder_owner_sys_t *)p_dec->p_owner; + const int i_rate = p_block ? p_block->i_rate : INPUT_RATE_DEFAULT; + + if( p_block && p_block->i_buffer <= 0 ) + { + block_Release( p_block ); + return VLC_SUCCESS; + } + +#ifdef ENABLE_SOUT + if( p_dec->i_object_type == VLC_OBJECT_PACKETIZER ) + { + block_t *p_sout_block; + + while( ( p_sout_block = + p_dec->pf_packetize( p_dec, p_block ? &p_block : 0 ) ) ) + { + if( !p_dec->p_owner->p_sout_input ) + { + es_format_Copy( &p_dec->p_owner->sout, &p_dec->fmt_out ); + + p_dec->p_owner->sout.i_group = p_dec->fmt_in.i_group; + p_dec->p_owner->sout.i_id = p_dec->fmt_in.i_id; + if( p_dec->fmt_in.psz_language ) + { + if( p_dec->p_owner->sout.psz_language ) + free( p_dec->p_owner->sout.psz_language ); + p_dec->p_owner->sout.psz_language = + strdup( p_dec->fmt_in.psz_language ); + } + + p_dec->p_owner->p_sout_input = + sout_InputNew( p_dec->p_owner->p_sout, + &p_dec->p_owner->sout ); + + if( p_dec->p_owner->p_sout_input == NULL ) + { + msg_Err( p_dec, "cannot create packetizer output (%4.4s)", + (char *)&p_dec->p_owner->sout.i_codec ); + p_dec->b_error = true; + + while( p_sout_block ) + { + block_t *p_next = p_sout_block->p_next; + block_Release( p_sout_block ); + p_sout_block = p_next; + } + break; + } + } + + while( p_sout_block ) + { + block_t *p_next = p_sout_block->p_next; + + p_sout_block->p_next = NULL; + p_sout_block->i_rate = i_rate; + + sout_InputSendBuffer( p_dec->p_owner->p_sout_input, + p_sout_block ); + + p_sout_block = p_next; + } + + /* For now it's enough, as only sout impact on this flag */ + if( p_dec->p_owner->p_sout->i_out_pace_nocontrol > 0 && + p_dec->p_owner->p_input->p->b_out_pace_control ) + { + msg_Dbg( p_dec, "switching to sync mode" ); + p_dec->p_owner->p_input->p->b_out_pace_control = false; + } + else if( p_dec->p_owner->p_sout->i_out_pace_nocontrol <= 0 && + !p_dec->p_owner->p_input->p->b_out_pace_control ) + { + msg_Dbg( p_dec, "switching to async mode" ); + p_dec->p_owner->p_input->p->b_out_pace_control = true; + } + } + } + else +#endif + if( p_dec->fmt_in.i_cat == AUDIO_ES ) + { + if( p_block ) + DecoderUpdatePreroll( &p_dec->p_owner->i_preroll_end, p_block ); + + if( p_dec->p_owner->p_packetizer ) + { + block_t *p_packetized_block; + decoder_t *p_packetizer = p_dec->p_owner->p_packetizer; + + while( (p_packetized_block = + p_packetizer->pf_packetize( p_packetizer, p_block ? &p_block : NULL )) ) + { + if( p_packetizer->fmt_out.i_extra && !p_dec->fmt_in.i_extra ) + { + es_format_Clean( &p_dec->fmt_in ); + es_format_Copy( &p_dec->fmt_in, &p_packetizer->fmt_out ); + } + + while( p_packetized_block ) + { + block_t *p_next = p_packetized_block->p_next; + p_packetized_block->p_next = NULL; + p_packetized_block->i_rate = i_rate; + + DecoderDecodeAudio( p_dec, p_packetized_block ); + + p_packetized_block = p_next; + } + } + } + else if( p_block ) + { + DecoderDecodeAudio( p_dec, p_block ); + } + } + else if( p_dec->fmt_in.i_cat == VIDEO_ES ) + { + if( p_block ) + DecoderUpdatePreroll( &p_dec->p_owner->i_preroll_end, p_block ); + + if( p_dec->p_owner->p_packetizer ) + { + block_t *p_packetized_block; + decoder_t *p_packetizer = p_dec->p_owner->p_packetizer; + + while( (p_packetized_block = + p_packetizer->pf_packetize( p_packetizer, p_block ? &p_block : NULL )) ) + { + if( p_packetizer->fmt_out.i_extra && !p_dec->fmt_in.i_extra ) + { + es_format_Clean( &p_dec->fmt_in ); + es_format_Copy( &p_dec->fmt_in, &p_packetizer->fmt_out ); + } + if( p_packetizer->pf_get_cc ) + DecoderGetCc( p_dec, p_packetizer ); + + while( p_packetized_block ) + { + block_t *p_next = p_packetized_block->p_next; + p_packetized_block->p_next = NULL; + p_packetized_block->i_rate = i_rate; + + DecoderDecodeVideo( p_dec, p_packetized_block ); + + p_packetized_block = p_next; + } + } + } + else if( p_block ) + { + DecoderDecodeVideo( p_dec, p_block ); + } + } + else if( p_dec->fmt_in.i_cat == SPU_ES ) + { + input_thread_t *p_input = p_dec->p_owner->p_input; + vout_thread_t *p_vout; + subpicture_t *p_spu; + + if( p_block ) + DecoderUpdatePreroll( &p_dec->p_owner->i_preroll_end, p_block ); + + while( (p_spu = p_dec->pf_decode_sub( p_dec, p_block ? &p_block : NULL ) ) ) + { + vlc_mutex_lock( &p_input->p->counters.counters_lock ); + stats_UpdateInteger( p_dec, p_input->p->counters.p_decoded_sub, 1, NULL ); + vlc_mutex_unlock( &p_input->p->counters.counters_lock ); + + p_vout = vlc_object_find( p_dec, VLC_OBJECT_VOUT, FIND_ANYWHERE ); + if( p_vout && p_sys->p_spu_vout == p_vout ) + { + /* Prerool does not work very well with subtitle */ + if( p_spu->i_start < p_dec->p_owner->i_preroll_end && + ( p_spu->i_stop <= 0 || p_spu->i_stop < p_dec->p_owner->i_preroll_end ) ) + spu_DestroySubpicture( p_vout->p_spu, p_spu ); + else + spu_DisplaySubpicture( p_vout->p_spu, p_spu ); + } + else + { + msg_Warn( p_dec, "no vout found, leaking subpicture" ); + } + if( p_vout ) + vlc_object_release( p_vout ); + } + } + else + { + msg_Err( p_dec, "unknown ES format" ); + p_dec->b_error = 1; + } + + return p_dec->b_error ? VLC_EGENERIC : VLC_SUCCESS; +} + +/** + * Destroys a decoder object + * + * \param p_dec the decoder object + * \return nothing + */ +static void DeleteDecoder( decoder_t * p_dec ) +{ + msg_Dbg( p_dec, "killing decoder fourcc `%4.4s', %u PES in FIFO", + (char*)&p_dec->fmt_in.i_codec, + (unsigned)block_FifoCount( p_dec->p_owner->p_fifo ) ); + + /* Free all packets still in the decoder fifo. */ + block_FifoEmpty( p_dec->p_owner->p_fifo ); + block_FifoRelease( p_dec->p_owner->p_fifo ); + + /* Cleanup */ + if( p_dec->p_owner->p_aout_input ) + aout_DecDelete( p_dec->p_owner->p_aout, p_dec->p_owner->p_aout_input ); + if( p_dec->p_owner->p_aout ) + { + vlc_object_release( p_dec->p_owner->p_aout ); + p_dec->p_owner->p_aout = NULL; + } + if( p_dec->p_owner->p_vout ) + { + int i_pic; + +#define p_pic p_dec->p_owner->p_vout->render.pp_picture[i_pic] + /* Hack to make sure all the the pictures are freed by the decoder */ + for( i_pic = 0; i_pic < p_dec->p_owner->p_vout->render.i_pictures; + i_pic++ ) + { + if( p_pic->i_status == RESERVED_PICTURE ) + vout_DestroyPicture( p_dec->p_owner->p_vout, p_pic ); + if( p_pic->i_refcount > 0 ) + vout_UnlinkPicture( p_dec->p_owner->p_vout, p_pic ); + } +#undef p_pic + + /* We are about to die. Reattach video output to p_vlc. */ + vout_Request( p_dec, p_dec->p_owner->p_vout, 0 ); + } + +#ifdef ENABLE_SOUT + if( p_dec->p_owner->p_sout_input ) + { + sout_InputDelete( p_dec->p_owner->p_sout_input ); + es_format_Clean( &p_dec->p_owner->sout ); + } +#endif + + if( p_dec->fmt_in.i_cat == SPU_ES ) + { + vout_thread_t *p_vout; + + p_vout = vlc_object_find( p_dec, VLC_OBJECT_VOUT, FIND_ANYWHERE ); + if( p_vout ) + { + spu_Control( p_vout->p_spu, SPU_CHANNEL_CLEAR, + p_dec->p_owner->i_spu_channel ); + vlc_object_release( p_vout ); + } + } + + es_format_Clean( &p_dec->fmt_in ); + es_format_Clean( &p_dec->fmt_out ); + + if( p_dec->p_owner->p_packetizer ) + { + module_Unneed( p_dec->p_owner->p_packetizer, + p_dec->p_owner->p_packetizer->p_module ); + es_format_Clean( &p_dec->p_owner->p_packetizer->fmt_in ); + es_format_Clean( &p_dec->p_owner->p_packetizer->fmt_out ); + vlc_object_detach( p_dec->p_owner->p_packetizer ); + vlc_object_release( p_dec->p_owner->p_packetizer ); + } + + vlc_mutex_destroy( &p_dec->p_owner->lock_cc ); + + vlc_object_detach( p_dec ); + + free( p_dec->p_owner ); +} + +/***************************************************************************** + * Buffers allocation callbacks for the decoders + *****************************************************************************/ +static aout_buffer_t *aout_new_buffer( decoder_t *p_dec, int i_samples ) +{ + decoder_owner_sys_t *p_sys = (decoder_owner_sys_t *)p_dec->p_owner; + aout_buffer_t *p_buffer; + + if( p_sys->p_aout_input != NULL && + ( p_dec->fmt_out.audio.i_rate != p_sys->audio.i_rate || + p_dec->fmt_out.audio.i_original_channels != + p_sys->audio.i_original_channels || + p_dec->fmt_out.audio.i_bytes_per_frame != + p_sys->audio.i_bytes_per_frame ) ) + { + /* Parameters changed, restart the aout */ + aout_DecDelete( p_sys->p_aout, p_sys->p_aout_input ); + p_sys->p_aout_input = NULL; + } + + if( p_sys->p_aout_input == NULL ) + { + audio_sample_format_t format; + int i_force_dolby = config_GetInt( p_dec, "force-dolby-surround" ); + + p_dec->fmt_out.audio.i_format = p_dec->fmt_out.i_codec; + p_sys->audio = p_dec->fmt_out.audio; + + memcpy( &format, &p_sys->audio, sizeof( audio_sample_format_t ) ); + if ( i_force_dolby && (format.i_original_channels&AOUT_CHAN_PHYSMASK) + == (AOUT_CHAN_LEFT|AOUT_CHAN_RIGHT) ) + { + if ( i_force_dolby == 1 ) + { + format.i_original_channels = format.i_original_channels | + AOUT_CHAN_DOLBYSTEREO; + } + else /* i_force_dolby == 2 */ + { + format.i_original_channels = format.i_original_channels & + ~AOUT_CHAN_DOLBYSTEREO; + } + } + + p_sys->p_aout_input = + aout_DecNew( p_dec, &p_sys->p_aout, &format, &p_dec->fmt_out.audio_replay_gain ); + if( p_sys->p_aout_input == NULL ) + { + msg_Err( p_dec, "failed to create audio output" ); + p_dec->b_error = true; + return NULL; + } + p_dec->fmt_out.audio.i_bytes_per_frame = + p_sys->audio.i_bytes_per_frame; + } + + p_buffer = aout_DecNewBuffer( p_sys->p_aout_input, i_samples ); + + return p_buffer; +} + +static void aout_del_buffer( decoder_t *p_dec, aout_buffer_t *p_buffer ) +{ + aout_DecDeleteBuffer( p_dec->p_owner->p_aout, + p_dec->p_owner->p_aout_input, p_buffer ); +} + + +int vout_CountPictureAvailable( vout_thread_t *p_vout ); + +static picture_t *vout_new_buffer( decoder_t *p_dec ) +{ + decoder_owner_sys_t *p_sys = (decoder_owner_sys_t *)p_dec->p_owner; + picture_t *p_pic; + + if( p_sys->p_vout == NULL || + p_dec->fmt_out.video.i_width != p_sys->video.i_width || + p_dec->fmt_out.video.i_height != p_sys->video.i_height || + p_dec->fmt_out.video.i_chroma != p_sys->video.i_chroma || + p_dec->fmt_out.video.i_aspect != p_sys->video.i_aspect ) + { + if( !p_dec->fmt_out.video.i_width || + !p_dec->fmt_out.video.i_height ) + { + /* Can't create a new vout without display size */ + return NULL; + } + + if( !p_dec->fmt_out.video.i_visible_width || + !p_dec->fmt_out.video.i_visible_height ) + { + if( p_dec->fmt_in.video.i_visible_width && + p_dec->fmt_in.video.i_visible_height ) + { + p_dec->fmt_out.video.i_visible_width = + p_dec->fmt_in.video.i_visible_width; + p_dec->fmt_out.video.i_visible_height = + p_dec->fmt_in.video.i_visible_height; + } + else + { + p_dec->fmt_out.video.i_visible_width = + p_dec->fmt_out.video.i_width; + p_dec->fmt_out.video.i_visible_height = + p_dec->fmt_out.video.i_height; + } + } + + if( p_dec->fmt_out.video.i_visible_height == 1088 && + var_CreateGetBool( p_dec, "hdtv-fix" ) ) + { + p_dec->fmt_out.video.i_visible_height = 1080; + p_dec->fmt_out.video.i_sar_num *= 135; + p_dec->fmt_out.video.i_sar_den *= 136; + msg_Warn( p_dec, "Fixing broken HDTV stream (display_height=1088)"); + } + + if( !p_dec->fmt_out.video.i_sar_num || + !p_dec->fmt_out.video.i_sar_den ) + { + p_dec->fmt_out.video.i_sar_num = p_dec->fmt_out.video.i_aspect * + p_dec->fmt_out.video.i_visible_height; + + p_dec->fmt_out.video.i_sar_den = VOUT_ASPECT_FACTOR * + p_dec->fmt_out.video.i_visible_width; + } + + vlc_ureduce( &p_dec->fmt_out.video.i_sar_num, + &p_dec->fmt_out.video.i_sar_den, + p_dec->fmt_out.video.i_sar_num, + p_dec->fmt_out.video.i_sar_den, 50000 ); + + p_dec->fmt_out.video.i_chroma = p_dec->fmt_out.i_codec; + p_sys->video = p_dec->fmt_out.video; + + p_sys->p_vout = vout_Request( p_dec, p_sys->p_vout, + &p_dec->fmt_out.video ); + if( p_sys->p_vout == NULL ) + { + msg_Err( p_dec, "failed to create video output" ); + p_dec->b_error = true; + return NULL; + } + + if( p_sys->video.i_rmask ) + p_sys->p_vout->render.i_rmask = p_sys->video.i_rmask; + if( p_sys->video.i_gmask ) + p_sys->p_vout->render.i_gmask = p_sys->video.i_gmask; + if( p_sys->video.i_bmask ) + p_sys->p_vout->render.i_bmask = p_sys->video.i_bmask; + } + + /* Get a new picture + */ + for( p_pic = NULL; ; ) + { + int i_pic, i_ready_pic = 0; + + if( p_dec->b_die || p_dec->b_error ) + return NULL; + + /* The video filter chain required that there is always 1 free buffer + * that it will use as temporary one. It will release the temporary + * buffer once its work is done, so this check is safe even if we don't + * lock around both count() and create(). + */ + if( vout_CountPictureAvailable( p_sys->p_vout ) >= 2 ) + { + p_pic = vout_CreatePicture( p_sys->p_vout, 0, 0, 0 ); + if( p_pic ) + break; + } + +#define p_pic p_dec->p_owner->p_vout->render.pp_picture[i_pic] + /* Check the decoder doesn't leak pictures */ + for( i_pic = 0; i_pic < p_dec->p_owner->p_vout->render.i_pictures; + i_pic++ ) + { + if( p_pic->i_status == READY_PICTURE ) + { + if( i_ready_pic++ > 0 ) break; + else continue; + } + + if( p_pic->i_status != DISPLAYED_PICTURE && + p_pic->i_status != RESERVED_PICTURE && + p_pic->i_status != READY_PICTURE ) break; + + if( !p_pic->i_refcount && p_pic->i_status != RESERVED_PICTURE ) + break; + } + if( i_pic == p_dec->p_owner->p_vout->render.i_pictures ) + { + msg_Err( p_dec, "decoder is leaking pictures, resetting the heap" ); + + /* Just free all the pictures */ + for( i_pic = 0; i_pic < p_dec->p_owner->p_vout->render.i_pictures; + i_pic++ ) + { + if( p_pic->i_status == RESERVED_PICTURE ) + vout_DestroyPicture( p_dec->p_owner->p_vout, p_pic ); + if( p_pic->i_refcount > 0 ) + vout_UnlinkPicture( p_dec->p_owner->p_vout, p_pic ); + } + } +#undef p_pic + + msleep( VOUT_OUTMEM_SLEEP ); + } + + return p_pic; +} + +static void vout_del_buffer( decoder_t *p_dec, picture_t *p_pic ) +{ + VoutDisplayedPicture( p_dec->p_owner->p_vout, p_pic ); +} + +static void vout_link_picture( decoder_t *p_dec, picture_t *p_pic ) +{ + vout_LinkPicture( p_dec->p_owner->p_vout, p_pic ); +} + +static void vout_unlink_picture( decoder_t *p_dec, picture_t *p_pic ) +{ + vout_UnlinkPicture( p_dec->p_owner->p_vout, p_pic ); +} + +static subpicture_t *spu_new_buffer( decoder_t *p_dec ) +{ + decoder_owner_sys_t *p_sys = (decoder_owner_sys_t *)p_dec->p_owner; + vout_thread_t *p_vout = NULL; + subpicture_t *p_subpic; + int i_attempts = 30; + + while( i_attempts-- ) + { + if( p_dec->b_die || p_dec->b_error ) break; + + p_vout = vlc_object_find( p_dec, VLC_OBJECT_VOUT, FIND_ANYWHERE ); + if( p_vout ) break; + + msleep( VOUT_DISPLAY_DELAY ); + } + + if( !p_vout ) + { + msg_Warn( p_dec, "no vout found, dropping subpicture" ); + return NULL; + } + + if( p_sys->p_spu_vout != p_vout ) + { + spu_Control( p_vout->p_spu, SPU_CHANNEL_REGISTER, + &p_sys->i_spu_channel ); + p_sys->p_spu_vout = p_vout; + } + + p_subpic = spu_CreateSubpicture( p_vout->p_spu ); + if( p_subpic ) + { + p_subpic->i_channel = p_sys->i_spu_channel; + } + + vlc_object_release( p_vout ); + + return p_subpic; +} + +static void spu_del_buffer( decoder_t *p_dec, subpicture_t *p_subpic ) +{ + decoder_owner_sys_t *p_sys = (decoder_owner_sys_t *)p_dec->p_owner; + vout_thread_t *p_vout = NULL; + + p_vout = vlc_object_find( p_dec, VLC_OBJECT_VOUT, FIND_ANYWHERE ); + if( !p_vout || p_sys->p_spu_vout != p_vout ) + { + if( p_vout ) + vlc_object_release( p_vout ); + msg_Warn( p_dec, "no vout found, leaking subpicture" ); + return; + } + + spu_DestroySubpicture( p_vout->p_spu, p_subpic ); + + vlc_object_release( p_vout ); +} + diff --git a/VLC/input/decoder_synchro.c b/VLC/input/decoder_synchro.c new file mode 100644 index 0000000..15db7bf --- /dev/null +++ b/VLC/input/decoder_synchro.c @@ -0,0 +1,588 @@ +/***************************************************************************** + * decoder_synchro.c : frame dropping routines + ***************************************************************************** + * Copyright (C) 1999-2005 the VideoLAN team + * $Id$ + * + * Authors: Christophe Massiot + * Samuel Hocevar + * Jean-Marc Dressler + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/* + * DISCUSSION : How to Write an efficient Frame-Dropping Algorithm + * ========== + * + * This implementation is based on mathematical and statistical + * developments. Older implementations used an enslavement, considering + * that if we're late when reading an I picture, we will decode one frame + * less. It had a tendancy to derive, and wasn't responsive enough, which + * would have caused trouble with the stream control stuff. + * + * 1. Structure of a picture stream + * ============================= + * Between 2 I's, we have for instance : + * I B P B P B P B P B P B I + * t0 t1 t2 t3 t4 t5 t6 t7 t8 t9 t10 t11 t12 + * Please bear in mind that B's and IP's will be inverted when displaying + * (decoding order != presentation order). Thus, t1 < t0. + * + * 2. Definitions + * =========== + * t[0..12] : Presentation timestamps of pictures 0..12. + * t : Current timestamp, at the moment of the decoding. + * T : Picture period, T = 1/frame_rate. + * tau[I,P,B] : Mean time to decode an [I,P,B] picture. + * tauYUV : Mean time to render a picture (given by the video_output). + * tau´[I,P,B] = 2 * tau[I,P,B] + tauYUV + * : Mean time + typical difference (estimated to tau/2, that + * needs to be confirmed) + render time. + * DELTA : A given error margin. + * + * 3. General considerations + * ====================== + * We define three types of machines : + * 14T > tauI : machines capable of decoding all I pictures + * 2T > tauP : machines capable of decoding all P pictures + * T > tauB : machines capable of decoding all B pictures + * + * 4. Decoding of an I picture + * ======================== + * On fast machines, we decode all I's. + * Otherwise : + * We can decode an I picture if we simply have enough time to decode it + * before displaying : + * t0 - t > tau´I + DELTA + * + * 5. Decoding of a P picture + * ======================= + * On fast machines, we decode all P's. + * Otherwise : + * First criterion : have time to decode it. + * t2 - t > tau´P + DELTA + * + * Second criterion : it shouldn't prevent us from displaying the forthcoming + * I picture, which is more important. + * t12 - t > tau´P + tau´I + DELTA + * + * 6. Decoding of a B picture + * ======================= + * On fast machines, we decode all B's. Otherwise : + * t1 - t > tau´B + DELTA + * Since the next displayed I or P is already decoded, we don't have to + * worry about it. + * + * I hope you will have a pleasant flight and do not forget your life + * jacket. + * --Meuuh (2000-12-29) + */ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "vlc_input.h" +#include "vlc_codec.h" +#include "vlc_codec_synchro.h" + +/* + * Local prototypes + */ + +#define MAX_PIC_AVERAGE 8 + +struct decoder_synchro_t +{ + /* */ + decoder_t *p_dec; + + /* */ + int i_frame_rate; + int i_current_rate; + bool b_no_skip; + bool b_quiet; + + /* date of the beginning of the decoding of the current picture */ + mtime_t decoding_start; + + /* stream properties */ + unsigned int i_n_p, i_n_b; + + /* decoding values */ + mtime_t p_tau[4]; /* average decoding durations */ + unsigned int pi_meaningful[4]; /* number of durations read */ + + /* render_time filled by SynchroChoose() */ + int i_render_time; + + /* stream context */ + int i_nb_ref; /* Number of reference pictures */ + int i_dec_nb_ref; /* Number of reference pictures we'll * + * have if we decode the current pic */ + int i_trash_nb_ref; /* Number of reference pictures we'll * + * have if we trash the current pic */ + unsigned int i_eta_p, i_eta_b; + mtime_t backward_pts, current_pts; + int i_current_period; /* period to add to the next picture */ + int i_backward_period; /* period to add after the next + * reference picture + * (backward_period * period / 2) */ + + /* statistics */ + unsigned int i_trashed_pic, i_not_chosen_pic, i_pic; +}; + +/* Error margins */ +#define DELTA (int)(0.075*CLOCK_FREQ) +#define MAX_VALID_TAU (int)(0.3*CLOCK_FREQ) + +#define DEFAULT_NB_P 5 +#define DEFAULT_NB_B 1 + +/***************************************************************************** + * decoder_SynchroInit : You know what ? + *****************************************************************************/ +decoder_synchro_t * decoder_SynchroInit( decoder_t *p_dec, int i_frame_rate ) +{ + decoder_synchro_t * p_synchro = malloc( sizeof(*p_synchro) ); + if ( p_synchro == NULL ) + return NULL; + memset( p_synchro, 0, sizeof(*p_synchro) ); + + p_synchro->p_dec = p_dec; + p_synchro->b_no_skip = !config_GetInt( p_dec, "skip-frames" ); + p_synchro->b_quiet = config_GetInt( p_dec, "quiet-synchro" ); + + /* We use a fake stream pattern, which is often right. */ + p_synchro->i_n_p = p_synchro->i_eta_p = DEFAULT_NB_P; + p_synchro->i_n_b = p_synchro->i_eta_b = DEFAULT_NB_B; + memset( p_synchro->p_tau, 0, 4 * sizeof(mtime_t) ); + memset( p_synchro->pi_meaningful, 0, 4 * sizeof(unsigned int) ); + p_synchro->i_nb_ref = 0; + p_synchro->i_trash_nb_ref = p_synchro->i_dec_nb_ref = 0; + p_synchro->current_pts = mdate() + DEFAULT_PTS_DELAY; + p_synchro->backward_pts = 0; + p_synchro->i_current_period = p_synchro->i_backward_period = 0; + p_synchro->i_trashed_pic = p_synchro->i_not_chosen_pic = + p_synchro->i_pic = 0; + + p_synchro->i_frame_rate = i_frame_rate; + + return p_synchro; +} + +/***************************************************************************** + * decoder_SynchroRelease : You know what ? + *****************************************************************************/ +void decoder_SynchroRelease( decoder_synchro_t * p_synchro ) +{ + free( p_synchro ); +} + +/***************************************************************************** + * decoder_SynchroReset : Reset the reference picture counter + *****************************************************************************/ +void decoder_SynchroReset( decoder_synchro_t * p_synchro ) +{ + p_synchro->i_nb_ref = 0; + p_synchro->i_trash_nb_ref = p_synchro->i_dec_nb_ref = 0; +} + +/***************************************************************************** + * decoder_SynchroChoose : Decide whether we will decode a picture or not + *****************************************************************************/ +bool decoder_SynchroChoose( decoder_synchro_t * p_synchro, int i_coding_type, + int i_render_time, bool b_low_delay ) +{ +#define TAU_PRIME( coding_type ) (p_synchro->p_tau[(coding_type)] \ + + (p_synchro->p_tau[(coding_type)] >> 1) \ + + p_synchro->i_render_time) +#define S (*p_synchro) + mtime_t now, period; + mtime_t pts = 0; + bool b_decode = 0; + + if ( p_synchro->b_no_skip ) + return 1; + + now = mdate(); + period = 1000000 * 1001 / p_synchro->i_frame_rate + * p_synchro->i_current_rate / INPUT_RATE_DEFAULT; + + p_synchro->i_render_time = i_render_time; + + switch( i_coding_type ) + { + case I_CODING_TYPE: + if( b_low_delay ) + { + pts = S.current_pts; + } + else if( S.backward_pts ) + { + pts = S.backward_pts; + } + else + { + /* displaying order : B B P B B I + * ^ ^ + * | +- current picture + * +- current PTS + */ + pts = S.current_pts + period * (S.i_n_b + 2); + } + + if( (1 + S.i_n_p * (S.i_n_b + 1)) * period > + S.p_tau[I_CODING_TYPE] ) + { + b_decode = 1; + } + else + { + b_decode = (pts - now) > (TAU_PRIME(I_CODING_TYPE) + DELTA); + } + if( !b_decode && !p_synchro->b_quiet ) + { + msg_Warn( p_synchro->p_dec, + "synchro trashing I (%"PRId64")", pts - now ); + } + break; + + case P_CODING_TYPE: + if( b_low_delay ) + { + pts = S.current_pts; + } + else if( S.backward_pts ) + { + pts = S.backward_pts; + } + else + { + pts = S.current_pts + period * (S.i_n_b + 1); + } + + if( p_synchro->i_nb_ref < 1 ) + { + b_decode = 0; + } + else if( (1 + S.i_n_p * (S.i_n_b + 1)) * period > + S.p_tau[I_CODING_TYPE] ) + { + if( (S.i_n_b + 1) * period > S.p_tau[P_CODING_TYPE] ) + { + /* Security in case we're _really_ late */ + b_decode = (pts - now > 0); + } + else + { + b_decode = (pts - now) > (TAU_PRIME(P_CODING_TYPE) + DELTA); + /* next I */ + b_decode &= (pts - now + + period + * ( (S.i_n_p - S.i_eta_p) * (1 + S.i_n_b) - 1 )) + > (TAU_PRIME(P_CODING_TYPE) + + TAU_PRIME(I_CODING_TYPE) + DELTA); + } + } + else + { + b_decode = 0; + } + break; + + case B_CODING_TYPE: + pts = S.current_pts; + + if( p_synchro->i_nb_ref < 2 ) + { + b_decode = 0; + } + else if( (S.i_n_b + 1) * period > S.p_tau[P_CODING_TYPE] ) + { + b_decode = (pts - now) > (TAU_PRIME(B_CODING_TYPE) + DELTA); + } + else + { + b_decode = 0; + } + } + + if( !b_decode ) + { + S.i_not_chosen_pic++; + } + return( b_decode ); +#undef S +#undef TAU_PRIME +} + +/***************************************************************************** + * decoder_SynchroTrash : Update counters when we trash a picture + *****************************************************************************/ +void decoder_SynchroTrash( decoder_synchro_t * p_synchro ) +{ + p_synchro->i_trashed_pic++; + p_synchro->i_nb_ref = p_synchro->i_trash_nb_ref; +} + +/***************************************************************************** + * decoder_SynchroDecode : Update timers when we decide to decode a picture + *****************************************************************************/ +void decoder_SynchroDecode( decoder_synchro_t * p_synchro ) +{ + p_synchro->decoding_start = mdate(); + p_synchro->i_nb_ref = p_synchro->i_dec_nb_ref; +} + +/***************************************************************************** + * decoder_SynchroEnd : Called when the image is totally decoded + *****************************************************************************/ +void decoder_SynchroEnd( decoder_synchro_t * p_synchro, int i_coding_type, + bool b_garbage ) +{ + mtime_t tau; + + if( !b_garbage ) + { + tau = mdate() - p_synchro->decoding_start; + + /* If duration too high, something happened (pause ?), so don't + * take it into account. */ + if( tau < 3 * p_synchro->p_tau[i_coding_type] + || ( !p_synchro->pi_meaningful[i_coding_type] + && tau < MAX_VALID_TAU ) ) + { + /* Mean with average tau, to ensure stability. */ + p_synchro->p_tau[i_coding_type] = + (p_synchro->pi_meaningful[i_coding_type] + * p_synchro->p_tau[i_coding_type] + tau) + / (p_synchro->pi_meaningful[i_coding_type] + 1); + if( p_synchro->pi_meaningful[i_coding_type] < MAX_PIC_AVERAGE ) + { + p_synchro->pi_meaningful[i_coding_type]++; + } + } + } +} + +/***************************************************************************** + * decoder_SynchroDate : When an image has been decoded, ask for its date + *****************************************************************************/ +mtime_t decoder_SynchroDate( decoder_synchro_t * p_synchro ) +{ + /* No need to lock, since PTS are only used by the video parser. */ + return p_synchro->current_pts; +} + +/***************************************************************************** + * decoder_SynchroNewPicture: Update stream structure and PTS + *****************************************************************************/ +void decoder_SynchroNewPicture( decoder_synchro_t * p_synchro, int i_coding_type, + int i_repeat_field, mtime_t next_pts, + mtime_t next_dts, int i_current_rate, + bool b_low_delay ) +{ + mtime_t period = 1000000 * 1001 / p_synchro->i_frame_rate + * i_current_rate / INPUT_RATE_DEFAULT; +#if 0 + mtime_t now = mdate(); +#endif + p_synchro->i_current_rate = i_current_rate; + + switch( i_coding_type ) + { + case I_CODING_TYPE: + if( p_synchro->i_eta_p + && p_synchro->i_eta_p != p_synchro->i_n_p ) + { +#if 0 + if( !p_synchro->b_quiet ) + msg_Dbg( p_synchro->p_dec, + "stream periodicity changed from P[%d] to P[%d]", + p_synchro->i_n_p, p_synchro->i_eta_p ); +#endif + p_synchro->i_n_p = p_synchro->i_eta_p; + } + p_synchro->i_eta_p = p_synchro->i_eta_b = 0; + p_synchro->i_trash_nb_ref = 0; + if( p_synchro->i_nb_ref < 2 ) + p_synchro->i_dec_nb_ref = p_synchro->i_nb_ref + 1; + else + p_synchro->i_dec_nb_ref = p_synchro->i_nb_ref; + +#if 0 + if( !p_synchro->b_quiet ) + msg_Dbg( p_synchro->p_dec, "I(%"PRId64") P(%"PRId64")[%d] B(%"PRId64")" + "[%d] YUV(%"PRId64") : trashed %d:%d/%d", + p_synchro->p_tau[I_CODING_TYPE], + p_synchro->p_tau[P_CODING_TYPE], + p_synchro->i_n_p, + p_synchro->p_tau[B_CODING_TYPE], + p_synchro->i_n_b, + p_synchro->i_render_time, + p_synchro->i_not_chosen_pic, + p_synchro->i_trashed_pic - + p_synchro->i_not_chosen_pic, + p_synchro->i_pic ); + p_synchro->i_trashed_pic = p_synchro->i_not_chosen_pic + = p_synchro->i_pic = 0; +#else + if( p_synchro->i_pic >= 100 ) + { + if( !p_synchro->b_quiet && p_synchro->i_trashed_pic != 0 ) + msg_Dbg( p_synchro->p_dec, "decoded %d/%d pictures", + p_synchro->i_pic + - p_synchro->i_trashed_pic, + p_synchro->i_pic ); + p_synchro->i_trashed_pic = p_synchro->i_not_chosen_pic + = p_synchro->i_pic = 0; + } +#endif + break; + + case P_CODING_TYPE: + p_synchro->i_eta_p++; + if( p_synchro->i_eta_b + && p_synchro->i_eta_b != p_synchro->i_n_b ) + { +#if 0 + if( !p_synchro->b_quiet ) + msg_Dbg( p_synchro->p_dec, + "stream periodicity changed from B[%d] to B[%d]", + p_synchro->i_n_b, p_synchro->i_eta_b ); +#endif + p_synchro->i_n_b = p_synchro->i_eta_b; + } + p_synchro->i_eta_b = 0; + p_synchro->i_dec_nb_ref = 2; + p_synchro->i_trash_nb_ref = 0; + break; + + case B_CODING_TYPE: + p_synchro->i_eta_b++; + p_synchro->i_dec_nb_ref = p_synchro->i_trash_nb_ref + = p_synchro->i_nb_ref; + break; + } + + p_synchro->current_pts += p_synchro->i_current_period + * (period >> 1); + +#define PTS_THRESHOLD (period >> 2) + if( i_coding_type == B_CODING_TYPE || b_low_delay ) + { + /* A video frame can be displayed 1, 2 or 3 times, according to + * repeat_first_field, top_field_first, progressive_sequence and + * progressive_frame. */ + p_synchro->i_current_period = i_repeat_field; + + if( next_pts ) + { + if( (next_pts - p_synchro->current_pts + > PTS_THRESHOLD + || p_synchro->current_pts - next_pts + > PTS_THRESHOLD) && !p_synchro->b_quiet ) + { + msg_Warn( p_synchro->p_dec, "decoder synchro warning: pts != " + "current_date (%"PRId64")", + p_synchro->current_pts + - next_pts ); + } + p_synchro->current_pts = next_pts; + } + } + else + { + p_synchro->i_current_period = p_synchro->i_backward_period; + p_synchro->i_backward_period = i_repeat_field; + + if( p_synchro->backward_pts ) + { + if( next_dts && + (next_dts - p_synchro->backward_pts + > PTS_THRESHOLD + || p_synchro->backward_pts - next_dts + > PTS_THRESHOLD) && !p_synchro->b_quiet ) + { + msg_Warn( p_synchro->p_dec, "backward_pts != dts (%"PRId64")", + next_dts + - p_synchro->backward_pts ); + } + if( (p_synchro->backward_pts - p_synchro->current_pts + > PTS_THRESHOLD + || p_synchro->current_pts - p_synchro->backward_pts + > PTS_THRESHOLD) && !p_synchro->b_quiet ) + { + msg_Warn( p_synchro->p_dec, + "backward_pts != current_pts (%"PRId64")", + p_synchro->current_pts + - p_synchro->backward_pts ); + } + p_synchro->current_pts = p_synchro->backward_pts; + p_synchro->backward_pts = 0; + } + else if( next_dts ) + { + if( (next_dts - p_synchro->current_pts + > PTS_THRESHOLD + || p_synchro->current_pts - next_dts + > PTS_THRESHOLD) && !p_synchro->b_quiet ) + { + msg_Warn( p_synchro->p_dec, "dts != current_pts (%"PRId64")", + p_synchro->current_pts + - next_dts ); + } + /* By definition of a DTS. */ + p_synchro->current_pts = next_dts; + next_dts = 0; + } + + if( next_pts ) + { + /* Store the PTS for the next time we have to date an I picture. */ + p_synchro->backward_pts = next_pts; + next_pts = 0; + } + } +#undef PTS_THRESHOLD + +#if 0 + /* Removed for incompatibility with slow motion */ + if( p_synchro->current_pts + DEFAULT_PTS_DELAY < now ) + { + /* We cannot be _that_ late, something must have happened, reinit + * the dates. */ + if( !p_synchro->b_quiet ) + msg_Warn( p_synchro->p_dec, "PTS << now (%"PRId64"), resetting", + now - p_synchro->current_pts - DEFAULT_PTS_DELAY ); + p_synchro->current_pts = now + DEFAULT_PTS_DELAY; + } + if( p_synchro->backward_pts + && p_synchro->backward_pts + DEFAULT_PTS_DELAY < now ) + { + /* The same. */ + p_synchro->backward_pts = 0; + } +#endif + + p_synchro->i_pic++; +} diff --git a/VLC/input/demux.c b/VLC/input/demux.c new file mode 100644 index 0000000..557e880 --- /dev/null +++ b/VLC/input/demux.c @@ -0,0 +1,628 @@ +/***************************************************************************** + * demux.c + ***************************************************************************** + * Copyright (C) 1999-2004 the VideoLAN team + * $Id$ + * + * Author: Laurent Aimar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#include "input_internal.h" + +static bool SkipID3Tag( demux_t * ); +static bool SkipAPETag( demux_t *p_demux ); + +/***************************************************************************** + * demux_New: + * if s is NULL then load a access_demux + *****************************************************************************/ +demux_t *__demux_New( vlc_object_t *p_obj, + const char *psz_access, const char *psz_demux, + const char *psz_path, + stream_t *s, es_out_t *out, bool b_quick ) +{ + static const char typename[] = "demux"; + demux_t *p_demux = vlc_custom_create( p_obj, sizeof( *p_demux ), + VLC_OBJECT_GENERIC, typename ); + const char *psz_module; + + if( p_demux == NULL ) return NULL; + + /* Parse URL */ + p_demux->psz_access = strdup( psz_access ); + p_demux->psz_demux = strdup( psz_demux ); + p_demux->psz_path = strdup( psz_path ); + + /* Take into account "demux" to be able to do :demux=dump */ + if( p_demux->psz_demux && *p_demux->psz_demux == '\0' ) + { + free( p_demux->psz_demux ); + p_demux->psz_demux = var_GetNonEmptyString( p_obj, "demux" ); + if( p_demux->psz_demux == NULL ) + p_demux->psz_demux = strdup( "" ); + } + + if( !b_quick ) + { + msg_Dbg( p_obj, "creating demux: access='%s' demux='%s' path='%s'", + p_demux->psz_access, p_demux->psz_demux, p_demux->psz_path ); + } + + p_demux->s = s; + p_demux->out = out; + + p_demux->pf_demux = NULL; + p_demux->pf_control = NULL; + p_demux->p_sys = NULL; + p_demux->info.i_update = 0; + p_demux->info.i_title = 0; + p_demux->info.i_seekpoint = 0; + + if( s ) psz_module = p_demux->psz_demux; + else psz_module = p_demux->psz_access; + + if( s && *psz_module == '\0' && strrchr( p_demux->psz_path, '.' ) ) + { + /* XXX: add only file without any problem here and with strong detection. + * - no .mp3, .a52, ... (aac is added as it works only by file ext + * anyway + * - wav can't be added 'cause of a52 and dts in them as raw audio + */ + static const struct { char ext[5]; char demux[9]; } exttodemux[] = + { + { "aac", "aac" }, + { "aiff", "aiff" }, + { "asf", "asf" }, { "wmv", "asf" }, { "wma", "asf" }, + { "avi", "avi" }, + { "au", "au" }, + { "flac", "flac" }, + { "dv", "dv" }, + { "m3u", "playlist" }, + { "mkv", "mkv" }, { "mka", "mkv" }, { "mks", "mkv" }, + { "mp4", "mp4" }, { "m4a", "mp4" }, { "mov", "mp4" }, { "moov", "mp4" }, + { "mod", "mod" }, { "xm", "mod" }, + { "nsv", "nsv" }, + { "ogg", "ogg" }, { "ogm", "ogg" }, + { "pva", "pva" }, + { "rm", "rm" }, + { "m4v", "m4v" }, + { "h264", "h264" }, + { "", "" }, + }; + /* Here, we don't mind if it does not work, it must be quick */ + static const struct { char ext[4]; char demux[5]; } exttodemux_quick[] = + { + { "mp3", "mpga" }, + { "ogg", "ogg" }, + { "wma", "asf" }, + { "", "" } + }; + + const char *psz_ext = strrchr( p_demux->psz_path, '.' ) + 1; + int i; + + if( !b_quick ) + { + for( i = 0; exttodemux[i].ext[0]; i++ ) + { + if( !strcasecmp( psz_ext, exttodemux[i].ext ) ) + { + psz_module = exttodemux[i].demux; + break; + } + } + } + else + { + for( i = 0; exttodemux_quick[i].ext[0]; i++ ) + { + if( !strcasecmp( psz_ext, exttodemux_quick[i].ext ) ) + { + psz_module = exttodemux_quick[i].demux; + break; + } + } + + } + } + + /* Before module_Need (for var_Create...) */ + vlc_object_attach( p_demux, p_obj ); + + if( s ) + { + /* ID3/APE tags will mess-up demuxer probing so we skip it here. + * ID3/APE parsers will called later on in the demuxer to access the + * skipped info. */ + if( !SkipID3Tag( p_demux ) ) + SkipAPETag( p_demux ); + + p_demux->p_module = + module_Need( p_demux, "demux", psz_module, + !strcmp( psz_module, p_demux->psz_demux ) ? + true : false ); + } + else + { + p_demux->p_module = + module_Need( p_demux, "access_demux", psz_module, + !strcmp( psz_module, p_demux->psz_access ) ? + true : false ); + } + + if( p_demux->p_module == NULL ) + { + vlc_object_detach( p_demux ); + free( p_demux->psz_path ); + free( p_demux->psz_demux ); + free( p_demux->psz_access ); + vlc_object_release( p_demux ); + return NULL; + } + + return p_demux; +} + +/***************************************************************************** + * demux_Delete: + *****************************************************************************/ +void demux_Delete( demux_t *p_demux ) +{ + module_Unneed( p_demux, p_demux->p_module ); + vlc_object_detach( p_demux ); + + free( p_demux->psz_path ); + free( p_demux->psz_demux ); + free( p_demux->psz_access ); + + vlc_object_release( p_demux ); +} + +/***************************************************************************** + * demux_vaControlHelper: + *****************************************************************************/ +int demux_vaControlHelper( stream_t *s, + int64_t i_start, int64_t i_end, + int i_bitrate, int i_align, + int i_query, va_list args ) +{ + int64_t i_tell; + double f, *pf; + int64_t i64, *pi64; + + if( i_end < 0 ) i_end = stream_Size( s ); + if( i_start < 0 ) i_start = 0; + if( i_align <= 0 ) i_align = 1; + i_tell = stream_Tell( s ); + + switch( i_query ) + { + case DEMUX_GET_LENGTH: + pi64 = (int64_t*)va_arg( args, int64_t * ); + if( i_bitrate > 0 && i_end > i_start ) + { + *pi64 = INT64_C(8000000) * (i_end - i_start) / i_bitrate; + return VLC_SUCCESS; + } + return VLC_EGENERIC; + + case DEMUX_GET_TIME: + pi64 = (int64_t*)va_arg( args, int64_t * ); + if( i_bitrate > 0 && i_end > i_start ) + { + *pi64 = INT64_C(8000000) * (i_tell - i_start) / i_bitrate; + return VLC_SUCCESS; + } + return VLC_EGENERIC; + + case DEMUX_GET_POSITION: + pf = (double*)va_arg( args, double * ); + if( i_start < i_end ) + { + *pf = (double)( i_tell - i_start ) / + (double)( i_end - i_start ); + return VLC_SUCCESS; + } + return VLC_EGENERIC; + + + case DEMUX_SET_POSITION: + f = (double)va_arg( args, double ); + if( i_start < i_end && f >= 0.0 && f <= 1.0 ) + { + int64_t i_block = (f * ( i_end - i_start )) / i_align; + + if( stream_Seek( s, i_start + i_block * i_align ) ) + { + return VLC_EGENERIC; + } + return VLC_SUCCESS; + } + return VLC_EGENERIC; + + case DEMUX_SET_TIME: + i64 = (int64_t)va_arg( args, int64_t ); + if( i_bitrate > 0 && i64 >= 0 ) + { + int64_t i_block = i64 * i_bitrate / INT64_C(8000000) / i_align; + if( stream_Seek( s, i_start + i_block * i_align ) ) + { + return VLC_EGENERIC; + } + return VLC_SUCCESS; + } + return VLC_EGENERIC; + + case DEMUX_GET_FPS: + case DEMUX_GET_META: + case DEMUX_HAS_UNSUPPORTED_META: + case DEMUX_SET_NEXT_DEMUX_TIME: + case DEMUX_GET_TITLE_INFO: + case DEMUX_SET_GROUP: + case DEMUX_GET_ATTACHMENTS: + return VLC_EGENERIC; + + default: + msg_Err( s, "unknown query in demux_vaControlDefault" ); + return VLC_EGENERIC; + } +} + +/**************************************************************************** + * stream_Demux*: create a demuxer for an outpout stream (allow demuxer chain) + ****************************************************************************/ +typedef struct +{ + /* Data buffer */ + block_fifo_t *p_fifo; + block_t *p_block; + + int64_t i_pos; + + /* Demuxer */ + char *psz_name; + es_out_t *out; + demux_t *p_demux; + +} d_stream_sys_t; + +static int DStreamRead ( stream_t *, void *p_read, unsigned int i_read ); +static int DStreamPeek ( stream_t *, const uint8_t **pp_peek, unsigned int i_peek ); +static int DStreamControl( stream_t *, int i_query, va_list ); +static void* DStreamThread ( vlc_object_t * ); + + +stream_t *__stream_DemuxNew( vlc_object_t *p_obj, const char *psz_demux, + es_out_t *out ) +{ + /* We create a stream reader, and launch a thread */ + stream_t *s; + d_stream_sys_t *p_sys; + + if( psz_demux == NULL || *psz_demux == '\0' ) return NULL; + + s = vlc_stream_create( p_obj ); + if( s == NULL ) + return NULL; + s->pf_read = DStreamRead; + s->pf_peek = DStreamPeek; + s->pf_control= DStreamControl; + + s->i_char_width = 1; + s->b_little_endian = false; + + s->p_sys = malloc( sizeof( d_stream_sys_t) ); + if( s->p_sys == NULL ) + { + vlc_object_release( s ); + return NULL; + } + p_sys = (d_stream_sys_t*)s->p_sys; + + p_sys->i_pos = 0; + p_sys->out = out; + p_sys->p_demux = NULL; + p_sys->p_block = NULL; + p_sys->psz_name = strdup( psz_demux ); + + /* decoder fifo */ + if( ( p_sys->p_fifo = block_FifoNew() ) == NULL ) + { + vlc_object_release( s ); + free( p_sys->psz_name ); + free( p_sys ); + return NULL; + } + + if( vlc_thread_create( s, "stream out", DStreamThread, + VLC_THREAD_PRIORITY_INPUT, false ) ) + { + vlc_object_release( s ); + free( p_sys->psz_name ); + free( p_sys ); + return NULL; + } + + return s; +} + +void stream_DemuxSend( stream_t *s, block_t *p_block ) +{ + d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys; + if( p_block ) block_FifoPut( p_sys->p_fifo, p_block ); +} + +void stream_DemuxDelete( stream_t *s ) +{ + d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys; + block_t *p_empty; + + vlc_object_kill( s ); + if( p_sys->p_demux ) + vlc_object_kill( p_sys->p_demux ); + p_empty = block_New( s, 1 ); p_empty->i_buffer = 0; + block_FifoPut( p_sys->p_fifo, p_empty ); + vlc_thread_join( s ); + + if( p_sys->p_demux ) demux_Delete( p_sys->p_demux ); + if( p_sys->p_block ) block_Release( p_sys->p_block ); + + block_FifoRelease( p_sys->p_fifo ); + free( p_sys->psz_name ); + free( p_sys ); + + vlc_object_release( s ); +} + + +static int DStreamRead( stream_t *s, void *p_read, unsigned int i_read ) +{ + d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys; + uint8_t *p_out = p_read; + int i_out = 0; + + //msg_Dbg( s, "DStreamRead: wanted %d bytes", i_read ); + + while( !s->b_die && !s->b_error && i_read ) + { + block_t *p_block = p_sys->p_block; + int i_copy; + + if( !p_block ) + { + p_block = block_FifoGet( p_sys->p_fifo ); + if( !p_block ) s->b_error = 1; + p_sys->p_block = p_block; + } + + if( p_block && i_read ) + { + i_copy = __MIN( i_read, p_block->i_buffer ); + if( p_out && i_copy ) memcpy( p_out, p_block->p_buffer, i_copy ); + i_read -= i_copy; + i_out += i_copy; + p_block->i_buffer -= i_copy; + p_block->p_buffer += i_copy; + + if( !p_block->i_buffer ) + { + block_Release( p_block ); + p_sys->p_block = NULL; + } + } + } + + p_sys->i_pos += i_out; + return i_out; +} + +static int DStreamPeek( stream_t *s, const uint8_t **pp_peek, unsigned int i_peek ) +{ + d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys; + block_t **pp_block = &p_sys->p_block; + int i_out = 0; + *pp_peek = 0; + + //msg_Dbg( s, "DStreamPeek: wanted %d bytes", i_peek ); + + while( !s->b_die && !s->b_error && i_peek ) + { + int i_copy; + + if( !*pp_block ) + { + *pp_block = block_FifoGet( p_sys->p_fifo ); + if( !*pp_block ) s->b_error = 1; + } + + if( *pp_block && i_peek ) + { + i_copy = __MIN( i_peek, (*pp_block)->i_buffer ); + i_peek -= i_copy; + i_out += i_copy; + + if( i_peek ) pp_block = &(*pp_block)->p_next; + } + } + + if( p_sys->p_block ) + { + p_sys->p_block = block_ChainGather( p_sys->p_block ); + *pp_peek = p_sys->p_block->p_buffer; + } + + return i_out; +} + +static int DStreamControl( stream_t *s, int i_query, va_list args ) +{ + d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys; + int64_t *p_i64; + bool *p_b; + int *p_int; + + switch( i_query ) + { + case STREAM_GET_SIZE: + p_i64 = (int64_t*) va_arg( args, int64_t * ); + *p_i64 = 0; + return VLC_SUCCESS; + + case STREAM_CAN_SEEK: + p_b = (bool*) va_arg( args, bool * ); + *p_b = false; + return VLC_SUCCESS; + + case STREAM_CAN_FASTSEEK: + p_b = (bool*) va_arg( args, bool * ); + *p_b = false; + return VLC_SUCCESS; + + case STREAM_GET_POSITION: + p_i64 = (int64_t*) va_arg( args, int64_t * ); + *p_i64 = p_sys->i_pos; + return VLC_SUCCESS; + + case STREAM_SET_POSITION: + { + int64_t i64 = (int64_t)va_arg( args, int64_t ); + int i_skip; + if( i64 < p_sys->i_pos ) return VLC_EGENERIC; + i_skip = i64 - p_sys->i_pos; + + while( i_skip > 0 ) + { + int i_read = DStreamRead( s, NULL, (long)i_skip ); + if( i_read <= 0 ) return VLC_EGENERIC; + i_skip -= i_read; + } + return VLC_SUCCESS; + } + + case STREAM_GET_MTU: + p_int = (int*) va_arg( args, int * ); + *p_int = 0; + return VLC_SUCCESS; + + case STREAM_CONTROL_ACCESS: + case STREAM_GET_CONTENT_TYPE: + return VLC_EGENERIC; + + default: + msg_Err( s, "invalid DStreamControl query=0x%x", i_query ); + return VLC_EGENERIC; + } +} + +static void* DStreamThread( vlc_object_t* p_this ) +{ + stream_t *s = (stream_t *)p_this; + d_stream_sys_t *p_sys = (d_stream_sys_t*)s->p_sys; + demux_t *p_demux; + + /* Create the demuxer */ + if( !(p_demux = demux_New( s, "", p_sys->psz_name, "", s, p_sys->out, + false )) ) + { + return NULL; + } + + p_sys->p_demux = p_demux; + + /* Main loop */ + while( !s->b_die && !p_demux->b_die ) + { + if( p_demux->pf_demux( p_demux ) <= 0 ) break; + } + + vlc_object_kill( p_demux ); + return NULL; +} + +/**************************************************************************** + * Utility functions + ****************************************************************************/ +static bool SkipID3Tag( demux_t *p_demux ) +{ + const uint8_t *p_peek; + uint8_t version, revision; + int i_size; + int b_footer; + + if( !p_demux->s ) + return false; + + /* Get 10 byte id3 header */ + if( stream_Peek( p_demux->s, &p_peek, 10 ) < 10 ) + return false; + + if( memcmp( p_peek, "ID3", 3 ) ) + return false; + + version = p_peek[3]; + revision = p_peek[4]; + b_footer = p_peek[5] & 0x10; + i_size = (p_peek[6]<<21) + (p_peek[7]<<14) + (p_peek[8]<<7) + p_peek[9]; + + if( b_footer ) i_size += 10; + i_size += 10; + + /* Skip the entire tag */ + stream_Read( p_demux->s, NULL, i_size ); + + msg_Dbg( p_demux, "ID3v2.%d revision %d tag found, skipping %d bytes", + version, revision, i_size ); + return true; +} +static bool SkipAPETag( demux_t *p_demux ) +{ + const uint8_t *p_peek; + int i_version; + int i_size; + uint32_t flags; + + if( !p_demux->s ) + return false; + + /* Get 32 byte ape header */ + if( stream_Peek( p_demux->s, &p_peek, 32 ) < 32 ) + return false; + + if( memcmp( p_peek, "APETAGEX", 8 ) ) + return false; + + i_version = GetDWLE( &p_peek[8] ); + flags = GetDWLE( &p_peek[8+4+4] ); + if( ( i_version != 1000 && i_version != 2000 ) || !( flags & (1<<29) ) ) + return false; + + i_size = GetDWLE( &p_peek[8+4] ) + ( (flags&(1<<30)) ? 32 : 0 ); + + /* Skip the entire tag */ + stream_Read( p_demux->s, NULL, i_size ); + + msg_Dbg( p_demux, "AP2 v%d tag found, skipping %d bytes", + i_version/1000, i_size ); + return true; +} + diff --git a/VLC/input/es_out.c b/VLC/input/es_out.c new file mode 100644 index 0000000..4638a65 --- /dev/null +++ b/VLC/input/es_out.c @@ -0,0 +1,2123 @@ +/***************************************************************************** + * es_out.c: Es Out handler for input. + ***************************************************************************** + * Copyright (C) 2003-2004 the VideoLAN team + * $Id$ + * + * Authors: Laurent Aimar + * Jean-Paul Saman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#include + +#include "vlc_input.h" +#include "vlc_es_out.h" +#include "vlc_block.h" +#include "vlc_aout.h" + +#include "input_internal.h" + +#include "vlc_iso_lang.h" +/* FIXME we should find a better way than including that */ +#include "iso-639_def.h" + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +typedef struct +{ + /* Program ID */ + int i_id; + + /* Number of es for this pgrm */ + int i_es; + + bool b_selected; + + /* Clock for this program */ + input_clock_t clock; + + char *psz_name; + char *psz_now_playing; + char *psz_publisher; + + vlc_epg_t *p_epg; +} es_out_pgrm_t; + +struct es_out_id_t +{ + /* ES ID */ + int i_id; + es_out_pgrm_t *p_pgrm; + + /* Misc. */ + int64_t i_preroll_end; + + /* Channel in the track type */ + int i_channel; + es_format_t fmt; + char *psz_language; + char *psz_language_code; + + decoder_t *p_dec; + + /* Fields for Video with CC */ + bool pb_cc_present[4]; + es_out_id_t *pp_cc_es[4]; + + /* Field for CC track from a master video */ + es_out_id_t *p_master; +}; + +struct es_out_sys_t +{ + input_thread_t *p_input; + + /* all programs */ + int i_pgrm; + es_out_pgrm_t **pgrm; + es_out_pgrm_t **pp_selected_pgrm; /* --programs */ + es_out_pgrm_t *p_pgrm; /* Master program */ + + /* all es */ + int i_id; + int i_es; + es_out_id_t **es; + + /* mode gestion */ + bool b_active; + int i_mode; + + /* es count */ + int i_audio; + int i_video; + int i_sub; + + /* es to select */ + int i_audio_last, i_audio_id; + int i_sub_last, i_sub_id; + int i_default_sub_id; /* As specified in container; if applicable */ + char **ppsz_audio_language; + char **ppsz_sub_language; + + /* current main es */ + es_out_id_t *p_es_audio; + es_out_id_t *p_es_video; + es_out_id_t *p_es_sub; + + /* delay */ + int64_t i_audio_delay; + int64_t i_spu_delay; + + /* Rate used to rescale ES ts */ + int i_rate; +}; + +static es_out_id_t *EsOutAdd ( es_out_t *, es_format_t * ); +static int EsOutSend ( es_out_t *, es_out_id_t *, block_t * ); +static void EsOutDel ( es_out_t *, es_out_id_t * ); +static void EsOutSelect( es_out_t *out, es_out_id_t *es, bool b_force ); +static int EsOutControl( es_out_t *, int i_query, va_list ); + +static void EsOutAddInfo( es_out_t *, es_out_id_t *es ); + +static bool EsIsSelected( es_out_id_t *es ); +static void EsSelect( es_out_t *out, es_out_id_t *es ); +static void EsUnselect( es_out_t *out, es_out_id_t *es, bool b_update ); +static char *LanguageGetName( const char *psz_code ); +static char *LanguageGetCode( const char *psz_lang ); +static char **LanguageSplit( const char *psz_langs ); +static int LanguageArrayIndex( char **ppsz_langs, char *psz_lang ); + +static char *EsOutProgramGetMetaName( es_out_pgrm_t *p_pgrm ); + +static const vlc_fourcc_t EsOutFourccClosedCaptions[4] = { + VLC_FOURCC('c', 'c', '1', ' '), + VLC_FOURCC('c', 'c', '2', ' '), + VLC_FOURCC('c', 'c', '3', ' '), + VLC_FOURCC('c', 'c', '4', ' '), +}; +static inline int EsOutGetClosedCaptionsChannel( vlc_fourcc_t fcc ) +{ + int i; + for( i = 0; i < 4; i++ ) + { + if( fcc == EsOutFourccClosedCaptions[i] ) + return i; + } + return -1; +} + + +/***************************************************************************** + * input_EsOutNew: + *****************************************************************************/ +es_out_t *input_EsOutNew( input_thread_t *p_input, int i_rate ) +{ + vlc_value_t val; + int i; + + es_out_t *out = malloc( sizeof( es_out_t ) ); + if( !out ) return NULL; + + es_out_sys_t *p_sys = malloc( sizeof( es_out_sys_t ) ); + if( !p_sys ) + { + free( out ); + return NULL; + } + + out->pf_add = EsOutAdd; + out->pf_send = EsOutSend; + out->pf_del = EsOutDel; + out->pf_control = EsOutControl; + out->p_sys = p_sys; + out->b_sout = p_input->p->p_sout != NULL; + + p_sys->p_input = p_input; + + p_sys->b_active = false; + p_sys->i_mode = ES_OUT_MODE_AUTO; + + + TAB_INIT( p_sys->i_pgrm, p_sys->pgrm ); + p_sys->p_pgrm = NULL; + + p_sys->i_id = 0; + + TAB_INIT( p_sys->i_es, p_sys->es ); + + p_sys->i_audio = 0; + p_sys->i_video = 0; + p_sys->i_sub = 0; + + /* */ + var_Get( p_input, "audio-track", &val ); + p_sys->i_audio_last = val.i_int; + + var_Get( p_input, "sub-track", &val ); + p_sys->i_sub_last = val.i_int; + + p_sys->i_default_sub_id = -1; + + if( !p_input->b_preparsing ) + { + var_Get( p_input, "audio-language", &val ); + p_sys->ppsz_audio_language = LanguageSplit(val.psz_string); + if( p_sys->ppsz_audio_language ) + { + for( i = 0; p_sys->ppsz_audio_language[i]; i++ ) + msg_Dbg( p_input, "selected audio language[%d] %s", + i, p_sys->ppsz_audio_language[i] ); + } + free( val.psz_string ); + + var_Get( p_input, "sub-language", &val ); + p_sys->ppsz_sub_language = LanguageSplit(val.psz_string); + if( p_sys->ppsz_sub_language ) + { + for( i = 0; p_sys->ppsz_sub_language[i]; i++ ) + msg_Dbg( p_input, "selected subtitle language[%d] %s", + i, p_sys->ppsz_sub_language[i] ); + } + free( val.psz_string ); + } + else + { + p_sys->ppsz_sub_language = NULL; + p_sys->ppsz_audio_language = NULL; + } + + var_Get( p_input, "audio-track-id", &val ); + p_sys->i_audio_id = val.i_int; + + var_Get( p_input, "sub-track-id", &val ); + p_sys->i_sub_id = val.i_int; + + p_sys->p_es_audio = NULL; + p_sys->p_es_video = NULL; + p_sys->p_es_sub = NULL; + + p_sys->i_audio_delay= 0; + p_sys->i_spu_delay = 0; + + p_sys->i_rate = i_rate; + + return out; +} + +/***************************************************************************** + * input_EsOutDelete: + *****************************************************************************/ +void input_EsOutDelete( es_out_t *out ) +{ + es_out_sys_t *p_sys = out->p_sys; + int i; + + for( i = 0; i < p_sys->i_es; i++ ) + { + if( p_sys->es[i]->p_dec ) + { + input_DecoderDelete( p_sys->es[i]->p_dec ); + } + free( p_sys->es[i]->psz_language ); + free( p_sys->es[i]->psz_language_code ); + es_format_Clean( &p_sys->es[i]->fmt ); + + free( p_sys->es[i] ); + } + if( p_sys->ppsz_audio_language ) + { + for( i = 0; p_sys->ppsz_audio_language[i]; i++ ) + free( p_sys->ppsz_audio_language[i] ); + free( p_sys->ppsz_audio_language ); + } + if( p_sys->ppsz_sub_language ) + { + for( i = 0; p_sys->ppsz_sub_language[i]; i++ ) + free( p_sys->ppsz_sub_language[i] ); + free( p_sys->ppsz_sub_language ); + } + + free( p_sys->es ); + + /* FIXME duplicate work EsOutProgramDel (but we cannot use it) add a EsOutProgramClean ? */ + for( i = 0; i < p_sys->i_pgrm; i++ ) + { + es_out_pgrm_t *p_pgrm = p_sys->pgrm[i]; + free( p_pgrm->psz_now_playing ); + free( p_pgrm->psz_publisher ); + free( p_pgrm->psz_name ); + if( p_pgrm->p_epg ) + vlc_epg_Delete( p_pgrm->p_epg ); + + free( p_pgrm ); + } + TAB_CLEAN( p_sys->i_pgrm, p_sys->pgrm ); + + free( p_sys ); + free( out ); +} + +es_out_id_t *input_EsOutGetFromID( es_out_t *out, int i_id ) +{ + int i; + if( i_id < 0 ) + { + /* Special HACK, -i_id is tha cat of the stream */ + return (es_out_id_t*)((uint8_t*)NULL-i_id); + } + + for( i = 0; i < out->p_sys->i_es; i++ ) + { + if( out->p_sys->es[i]->i_id == i_id ) + return out->p_sys->es[i]; + } + return NULL; +} + +static void EsOutDiscontinuity( es_out_t *out, bool b_flush, bool b_audio ) +{ + es_out_sys_t *p_sys = out->p_sys; + int i; + + for( i = 0; i < p_sys->i_es; i++ ) + { + es_out_id_t *es = p_sys->es[i]; + + /* Send a dummy block to let decoder know that + * there is a discontinuity */ + if( es->p_dec && ( !b_audio || es->fmt.i_cat == AUDIO_ES ) ) + input_DecoderDiscontinuity( es->p_dec, b_flush ); + } +} +void input_EsOutChangeRate( es_out_t *out, int i_rate ) +{ + es_out_sys_t *p_sys = out->p_sys; + int i; + + p_sys->i_rate = i_rate; + EsOutDiscontinuity( out, false, false ); + + for( i = 0; i < p_sys->i_pgrm; i++ ) + input_ClockSetRate( &p_sys->pgrm[i]->clock, i_rate ); +} + +void input_EsOutSetDelay( es_out_t *out, int i_cat, int64_t i_delay ) +{ + es_out_sys_t *p_sys = out->p_sys; + + if( i_cat == AUDIO_ES ) + p_sys->i_audio_delay = i_delay; + else if( i_cat == SPU_ES ) + p_sys->i_spu_delay = i_delay; +} +void input_EsOutChangeState( es_out_t *out ) +{ + es_out_sys_t *p_sys = out->p_sys; + input_thread_t *p_input = p_sys->p_input; + + if( p_input->i_state == PAUSE_S ) + { + /* Send discontinuity to decoders (it will allow them to flush + * * if implemented */ + EsOutDiscontinuity( out, false, false ); + } + else + { + /* Out of pause, reset pcr */ + es_out_Control( out, ES_OUT_RESET_PCR ); + } +} +void input_EsOutChangePosition( es_out_t *out ) +{ + //es_out_sys_t *p_sys = out->p_sys; + + es_out_Control( out, ES_OUT_RESET_PCR ); + EsOutDiscontinuity( out, true, false ); +} + +bool input_EsOutDecodersEmpty( es_out_t *out ) +{ + es_out_sys_t *p_sys = out->p_sys; + int i; + + for( i = 0; i < p_sys->i_es; i++ ) + { + es_out_id_t *es = p_sys->es[i]; + + if( es->p_dec && !input_DecoderEmpty( es->p_dec ) ) + return false; + } + return true; +} + +/***************************************************************************** + * + *****************************************************************************/ +static void EsOutESVarUpdateGeneric( es_out_t *out, int i_id, es_format_t *fmt, const char *psz_language, + bool b_delete ) +{ + es_out_sys_t *p_sys = out->p_sys; + input_thread_t *p_input = p_sys->p_input; + const bool b_teletext = fmt->i_cat == SPU_ES && fmt->i_codec == VLC_FOURCC( 't', 'e', 'l', 'x' ); + vlc_value_t val, text; + + const char *psz_var; + + if( fmt->i_cat == AUDIO_ES ) + psz_var = "audio-es"; + else if( fmt->i_cat == VIDEO_ES ) + psz_var = "video-es"; + else if( fmt->i_cat == SPU_ES ) + psz_var = "spu-es"; + else + return; + + if( b_delete ) + { + if( b_teletext ) + var_SetInteger( p_sys->p_input, "teletext-es", -1 ); + + val.i_int = i_id; + var_Change( p_input, psz_var, VLC_VAR_DELCHOICE, &val, NULL ); + + var_SetBool( p_sys->p_input, "intf-change", true ); + return; + } + + /* Get the number of ES already added */ + var_Change( p_input, psz_var, VLC_VAR_CHOICESCOUNT, &val, NULL ); + if( val.i_int == 0 ) + { + vlc_value_t val2; + + /* First one, we need to add the "Disable" choice */ + val2.i_int = -1; text.psz_string = _("Disable"); + var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val2, &text ); + val.i_int++; + } + + /* Take care of the ES description */ + if( fmt->psz_description && *fmt->psz_description ) + { + if( psz_language && *psz_language ) + { + text.psz_string = malloc( strlen( fmt->psz_description) + + strlen( psz_language ) + 10 ); + sprintf( text.psz_string, "%s - [%s]", fmt->psz_description, + psz_language ); + } + else text.psz_string = strdup( fmt->psz_description ); + } + else + { + if( psz_language && *psz_language ) + { + if( asprintf( &text.psz_string, "%s %i - [%s]", _( "Track" ), val.i_int, psz_language ) == -1 ) + text.psz_string = NULL; + } + else + { + if( asprintf( &text.psz_string, "%s %i", _( "Track" ), val.i_int ) == -1 ) + text.psz_string = NULL; + } + } + + val.i_int = i_id; + var_Change( p_input, psz_var, VLC_VAR_ADDCHOICE, &val, &text ); + + free( text.psz_string ); + + if( b_teletext ) + var_SetInteger( p_sys->p_input, "teletext-es", i_id ); + + var_SetBool( p_sys->p_input, "intf-change", true ); +} + +static void EsOutESVarUpdate( es_out_t *out, es_out_id_t *es, + bool b_delete ) +{ + EsOutESVarUpdateGeneric( out, es->i_id, &es->fmt, es->psz_language, b_delete ); +} + +/* EsOutProgramSelect: + * Select a program and update the object variable + */ +static void EsOutProgramSelect( es_out_t *out, es_out_pgrm_t *p_pgrm ) +{ + es_out_sys_t *p_sys = out->p_sys; + input_thread_t *p_input = p_sys->p_input; + vlc_value_t val; + int i; + + if( p_sys->p_pgrm == p_pgrm ) + return; /* Nothing to do */ + + if( p_sys->p_pgrm ) + { + es_out_pgrm_t *old = p_sys->p_pgrm; + msg_Dbg( p_input, "unselecting program id=%d", old->i_id ); + + for( i = 0; i < p_sys->i_es; i++ ) + { + if( p_sys->es[i]->p_pgrm == old && EsIsSelected( p_sys->es[i] ) && + p_sys->i_mode != ES_OUT_MODE_ALL ) + EsUnselect( out, p_sys->es[i], true ); + } + + p_sys->p_es_audio = NULL; + p_sys->p_es_sub = NULL; + p_sys->p_es_video = NULL; + } + + msg_Dbg( p_input, "selecting program id=%d", p_pgrm->i_id ); + + /* Mark it selected */ + p_pgrm->b_selected = true; + + /* Switch master stream */ + if( p_sys->p_pgrm && p_sys->p_pgrm->clock.b_master ) + { + p_sys->p_pgrm->clock.b_master = false; + } + p_pgrm->clock.b_master = true; + p_sys->p_pgrm = p_pgrm; + + /* Update "program" */ + val.i_int = p_pgrm->i_id; + var_Change( p_input, "program", VLC_VAR_SETVALUE, &val, NULL ); + + /* Update "es-*" */ + var_Change( p_input, "audio-es", VLC_VAR_CLEARCHOICES, NULL, NULL ); + var_Change( p_input, "video-es", VLC_VAR_CLEARCHOICES, NULL, NULL ); + var_Change( p_input, "spu-es", VLC_VAR_CLEARCHOICES, NULL, NULL ); + var_SetInteger( p_input, "teletext-es", -1 ); + for( i = 0; i < p_sys->i_es; i++ ) + { + if( p_sys->es[i]->p_pgrm == p_sys->p_pgrm ) + EsOutESVarUpdate( out, p_sys->es[i], false ); + EsOutSelect( out, p_sys->es[i], false ); + } + + /* Update now playing */ + input_item_SetNowPlaying( p_input->p->input.p_item, + p_pgrm->psz_now_playing ); + input_item_SetPublisher( p_input->p->input.p_item, + p_pgrm->psz_publisher ); + + var_SetBool( p_sys->p_input, "intf-change", true ); +} + +/* EsOutAddProgram: + * Add a program + */ +static es_out_pgrm_t *EsOutProgramAdd( es_out_t *out, int i_group ) +{ + es_out_sys_t *p_sys = out->p_sys; + input_thread_t *p_input = p_sys->p_input; + vlc_value_t val; + + es_out_pgrm_t *p_pgrm = malloc( sizeof( es_out_pgrm_t ) ); + if( !p_pgrm ) return NULL; + + /* Init */ + p_pgrm->i_id = i_group; + p_pgrm->i_es = 0; + p_pgrm->b_selected = false; + p_pgrm->psz_name = NULL; + p_pgrm->psz_now_playing = NULL; + p_pgrm->psz_publisher = NULL; + p_pgrm->p_epg = NULL; + input_ClockInit( &p_pgrm->clock, false, p_input->p->input.i_cr_average, p_sys->i_rate ); + + /* Append it */ + TAB_APPEND( p_sys->i_pgrm, p_sys->pgrm, p_pgrm ); + + /* Update "program" variable */ + val.i_int = i_group; + var_Change( p_input, "program", VLC_VAR_ADDCHOICE, &val, NULL ); + + if( i_group == var_GetInteger( p_input, "program" ) ) + { + EsOutProgramSelect( out, p_pgrm ); + } + else + { + var_SetBool( p_sys->p_input, "intf-change", true ); + } + return p_pgrm; +} + +/* EsOutDelProgram: + * Delete a program + */ +static int EsOutProgramDel( es_out_t *out, int i_group ) +{ + es_out_sys_t *p_sys = out->p_sys; + input_thread_t *p_input = p_sys->p_input; + es_out_pgrm_t *p_pgrm = NULL; + vlc_value_t val; + int i; + + for( i = 0; i < p_sys->i_pgrm; i++ ) + { + if( p_sys->pgrm[i]->i_id == i_group ) + { + p_pgrm = p_sys->pgrm[i]; + break; + } + } + + if( p_pgrm == NULL ) + return VLC_EGENERIC; + + if( p_pgrm->i_es ) + { + msg_Dbg( p_input, "can't delete program %d which still has %i ES", + i_group, p_pgrm->i_es ); + return VLC_EGENERIC; + } + + TAB_REMOVE( p_sys->i_pgrm, p_sys->pgrm, p_pgrm ); + + /* If program is selected we need to unselect it */ + if( p_sys->p_pgrm == p_pgrm ) p_sys->p_pgrm = NULL; + + free( p_pgrm->psz_name ); + free( p_pgrm->psz_now_playing ); + free( p_pgrm->psz_publisher ); + if( p_pgrm->p_epg ) + vlc_epg_Delete( p_pgrm->p_epg ); + free( p_pgrm ); + + /* Update "program" variable */ + val.i_int = i_group; + var_Change( p_input, "program", VLC_VAR_DELCHOICE, &val, NULL ); + + var_SetBool( p_sys->p_input, "intf-change", true ); + + return VLC_SUCCESS; +} + +/* EsOutProgramMeta: + */ +static char *EsOutProgramGetMetaName( es_out_pgrm_t *p_pgrm ) +{ + char *psz = NULL; + if( p_pgrm->psz_name ) + { + if( asprintf( &psz, _("%s [%s %d]"), p_pgrm->psz_name, _("Program"), p_pgrm->i_id ) == -1 ) + return NULL; + } + else + { + if( asprintf( &psz, "%s %d", _("Program"), p_pgrm->i_id ) == -1 ) + return NULL; + } + return psz; +} + +static void EsOutProgramMeta( es_out_t *out, int i_group, vlc_meta_t *p_meta ) +{ + es_out_sys_t *p_sys = out->p_sys; + es_out_pgrm_t *p_pgrm = NULL; + input_thread_t *p_input = p_sys->p_input; + char *psz_cat; + const char *psz_title = NULL; + const char *psz_provider = NULL; + int i; + + msg_Dbg( p_input, "EsOutProgramMeta: number=%d", i_group ); + + /* Check against empty meta data (empty for what we handle) */ + if( !vlc_meta_Get( p_meta, vlc_meta_Title) && + !vlc_meta_Get( p_meta, vlc_meta_NowPlaying) && + !vlc_meta_Get( p_meta, vlc_meta_Publisher) && + vlc_dictionary_keys_count( &p_meta->extra_tags ) <= 0 ) + { + return; + } + /* Find program */ + for( i = 0; i < p_sys->i_pgrm; i++ ) + { + if( p_sys->pgrm[i]->i_id == i_group ) + { + p_pgrm = p_sys->pgrm[i]; + break; + } + } + if( p_pgrm == NULL ) + p_pgrm = EsOutProgramAdd( out, i_group ); /* Create it */ + + /* */ + psz_title = vlc_meta_Get( p_meta, vlc_meta_Title); + psz_provider = vlc_meta_Get( p_meta, vlc_meta_Publisher); + + /* Update the description text of the program */ + if( psz_title && *psz_title ) + { + vlc_value_t val; + vlc_value_t text; + + if( !p_pgrm->psz_name || strcmp( p_pgrm->psz_name, psz_title ) ) + { + char *psz_cat = EsOutProgramGetMetaName( p_pgrm ); + + /* Remove old entries */ + input_Control( p_input, INPUT_DEL_INFO, psz_cat, NULL ); + /* TODO update epg name */ + free( psz_cat ); + } + free( p_pgrm->psz_name ); + p_pgrm->psz_name = strdup( psz_title ); + + /* ugly but it works */ + val.i_int = i_group; + var_Change( p_input, "program", VLC_VAR_DELCHOICE, &val, NULL ); + + if( psz_provider && *psz_provider ) + { + if( asprintf( &text.psz_string, "%s [%s]", psz_title, psz_provider ) != -1 ) + { + var_Change( p_input, "program", VLC_VAR_ADDCHOICE, &val, &text ); + free( text.psz_string ); + } + } + else + { + text.psz_string = (char *)psz_title; + var_Change( p_input, "program", VLC_VAR_ADDCHOICE, &val, &text ); + } + } + + psz_cat = EsOutProgramGetMetaName( p_pgrm ); + if( psz_provider ) + { + if( p_sys->p_pgrm == p_pgrm ) + input_item_SetPublisher( p_input->p->input.p_item, psz_provider ); + input_Control( p_input, INPUT_ADD_INFO, psz_cat, input_MetaTypeToLocalizedString(vlc_meta_Publisher), psz_provider ); + } + char ** ppsz_all_keys = vlc_dictionary_all_keys( &p_meta->extra_tags ); + for( i = 0; ppsz_all_keys[i]; i++ ) + { + input_Control( p_input, INPUT_ADD_INFO, psz_cat, _(ppsz_all_keys[i]), + vlc_dictionary_value_for_key( &p_meta->extra_tags, ppsz_all_keys[i] ) ); + free( ppsz_all_keys[i] ); + } + free( ppsz_all_keys ); + + free( psz_cat ); +} + +static void vlc_epg_Merge( vlc_epg_t *p_dst, const vlc_epg_t *p_src ) +{ + int i; + + /* Add new event */ + for( i = 0; i < p_src->i_event; i++ ) + { + vlc_epg_event_t *p_evt = p_src->pp_event[i]; + bool b_add = true; + int j; + + for( j = 0; j < p_dst->i_event; j++ ) + { + if( p_dst->pp_event[j]->i_start == p_evt->i_start && p_dst->pp_event[j]->i_duration == p_evt->i_duration ) + { + b_add = false; + break; + } + if( p_dst->pp_event[j]->i_start > p_evt->i_start ) + break; + } + if( b_add ) + { + vlc_epg_event_t *p_copy = malloc( sizeof(vlc_epg_event_t) ); + if( !p_copy ) + break; + memset( p_copy, 0, sizeof(vlc_epg_event_t) ); + p_copy->i_start = p_evt->i_start; + p_copy->i_duration = p_evt->i_duration; + p_copy->psz_name = p_evt->psz_name ? strdup( p_evt->psz_name ) : NULL; + p_copy->psz_short_description = p_evt->psz_short_description ? strdup( p_evt->psz_short_description ) : NULL; + p_copy->psz_description = p_evt->psz_description ? strdup( p_evt->psz_description ) : NULL; + TAB_INSERT( p_dst->i_event, p_dst->pp_event, p_copy, j ); + } + } + /* Update current */ + vlc_epg_SetCurrent( p_dst, p_src->p_current ? p_src->p_current->i_start : -1 ); + + /* Keep only 1 old event */ + if( p_dst->p_current ) + { + while( p_dst->i_event > 1 && p_dst->pp_event[0] != p_dst->p_current && p_dst->pp_event[1] != p_dst->p_current ) + TAB_REMOVE( p_dst->i_event, p_dst->pp_event, p_dst->pp_event[0] ); + } +} + +static void EsOutProgramEpg( es_out_t *out, int i_group, vlc_epg_t *p_epg ) +{ + es_out_sys_t *p_sys = out->p_sys; + input_thread_t *p_input = p_sys->p_input; + es_out_pgrm_t *p_pgrm = NULL; + char *psz_cat; + int i; + + /* Find program */ + for( i = 0; i < p_sys->i_pgrm; i++ ) + { + if( p_sys->pgrm[i]->i_id == i_group ) + { + p_pgrm = p_sys->pgrm[i]; + break; + } + } + if( p_pgrm == NULL ) + p_pgrm = EsOutProgramAdd( out, i_group ); /* Create it */ + + /* Merge EPG */ + if( !p_pgrm->p_epg ) + p_pgrm->p_epg = vlc_epg_New( p_pgrm->psz_name ); + vlc_epg_Merge( p_pgrm->p_epg, p_epg ); + + /* Update info */ + psz_cat = EsOutProgramGetMetaName( p_pgrm ); +#ifdef HAVE_LOCALTIME_R + char *psz_epg; + if( asprintf( &psz_epg, "EPG %s", psz_cat ) == -1 ) + psz_epg = NULL; + input_Control( p_input, INPUT_DEL_INFO, psz_epg, NULL ); + msg_Dbg( p_input, "EsOutProgramEpg: number=%d name=%s", i_group, p_pgrm->p_epg->psz_name ); + for( i = 0; i < p_pgrm->p_epg->i_event; i++ ) + { + const vlc_epg_event_t *p_evt = p_pgrm->p_epg->pp_event[i]; + time_t t_start = (time_t)p_evt->i_start; + struct tm tm_start; + char psz_start[128]; + + localtime_r( &t_start, &tm_start ); + + snprintf( psz_start, sizeof(psz_start), "%2.2d:%2.2d:%2.2d", tm_start.tm_hour, tm_start.tm_min, tm_start.tm_sec ); + if( p_evt->psz_short_description || p_evt->psz_description ) + input_Control( p_input, INPUT_ADD_INFO, psz_epg, psz_start, "%s (%2.2d:%2.2d) - %s", + p_evt->psz_name, + p_evt->i_duration/60/60, (p_evt->i_duration/60)%60, + p_evt->psz_short_description ? p_evt->psz_short_description : p_evt->psz_description ); + else + input_Control( p_input, INPUT_ADD_INFO, psz_epg, psz_start, "%s (%2.2d:%2.2d)", + p_evt->psz_name, + p_evt->i_duration/60/60, (p_evt->i_duration/60)%60 ); + } + free( psz_epg ); +#endif + /* Update now playing */ + free( p_pgrm->psz_now_playing ); + p_pgrm->psz_now_playing = NULL; + if( p_epg->p_current && p_epg->p_current->psz_name && *p_epg->p_current->psz_name ) + p_pgrm->psz_now_playing = strdup( p_epg->p_current->psz_name ); + + if( p_pgrm == p_sys->p_pgrm ) + input_item_SetNowPlaying( p_input->p->input.p_item, p_pgrm->psz_now_playing ); + + if( p_pgrm->psz_now_playing ) + { + input_Control( p_input, INPUT_ADD_INFO, psz_cat, + input_MetaTypeToLocalizedString(vlc_meta_NowPlaying), + p_pgrm->psz_now_playing ); + } + else + { + input_Control( p_input, INPUT_DEL_INFO, psz_cat, + input_MetaTypeToLocalizedString(vlc_meta_NowPlaying) ); + } + + free( psz_cat ); +} + +/* EsOutAdd: + * Add an es_out + */ +static es_out_id_t *EsOutAdd( es_out_t *out, es_format_t *fmt ) +{ + es_out_sys_t *p_sys = out->p_sys; + input_thread_t *p_input = p_sys->p_input; + + es_out_id_t *es = malloc( sizeof( es_out_id_t ) ); + es_out_pgrm_t *p_pgrm = NULL; + int i; + + if( !es ) return NULL; + + if( fmt->i_group < 0 ) + { + msg_Err( p_input, "invalid group number" ); + free( es ); + return NULL; + } + + /* Search the program */ + for( i = 0; i < p_sys->i_pgrm; i++ ) + { + if( fmt->i_group == p_sys->pgrm[i]->i_id ) + { + p_pgrm = p_sys->pgrm[i]; + break; + } + } + if( p_pgrm == NULL ) + { + /* Create a new one */ + p_pgrm = EsOutProgramAdd( out, fmt->i_group ); + } + + /* Increase ref count for program */ + p_pgrm->i_es++; + + /* Set up ES */ + if( fmt->i_id < 0 ) + fmt->i_id = out->p_sys->i_id; + es->i_id = fmt->i_id; + es->p_pgrm = p_pgrm; + es_format_Copy( &es->fmt, fmt ); + es->i_preroll_end = -1; + + switch( fmt->i_cat ) + { + case AUDIO_ES: + { + audio_replay_gain_t rg; + + es->i_channel = p_sys->i_audio; + + vlc_mutex_lock( &p_input->p->input.p_item->lock ); + memset( &rg, 0, sizeof(rg) ); + vlc_audio_replay_gain_MergeFromMeta( &rg, p_input->p->input.p_item->p_meta ); + vlc_mutex_unlock( &p_input->p->input.p_item->lock ); + + for( i = 0; i < AUDIO_REPLAY_GAIN_MAX; i++ ) + { + if( !es->fmt.audio_replay_gain.pb_peak[i] ) + { + es->fmt.audio_replay_gain.pb_peak[i] = rg.pb_peak[i]; + es->fmt.audio_replay_gain.pf_peak[i] = rg.pf_peak[i]; + } + if( !es->fmt.audio_replay_gain.pb_gain[i] ) + { + es->fmt.audio_replay_gain.pb_gain[i] = rg.pb_gain[i]; + es->fmt.audio_replay_gain.pf_gain[i] = rg.pf_gain[i]; + } + } + break; + } + + case VIDEO_ES: + es->i_channel = p_sys->i_video; + if( fmt->video.i_frame_rate && fmt->video.i_frame_rate_base ) + vlc_ureduce( &es->fmt.video.i_frame_rate, + &es->fmt.video.i_frame_rate_base, + fmt->video.i_frame_rate, + fmt->video.i_frame_rate_base, 0 ); + break; + + case SPU_ES: + es->i_channel = p_sys->i_sub; + break; + + default: + es->i_channel = 0; + break; + } + es->psz_language = LanguageGetName( fmt->psz_language ); /* remember so we only need to do it once */ + es->psz_language_code = LanguageGetCode( fmt->psz_language ); + es->p_dec = NULL; + for( i = 0; i < 4; i++ ) + es->pb_cc_present[i] = false; + es->p_master = false; + + if( es->p_pgrm == p_sys->p_pgrm ) + EsOutESVarUpdate( out, es, false ); + + /* Select it if needed */ + EsOutSelect( out, es, false ); + + + TAB_APPEND( out->p_sys->i_es, out->p_sys->es, es ); + p_sys->i_id++; /* always incremented */ + switch( fmt->i_cat ) + { + case AUDIO_ES: + p_sys->i_audio++; + break; + case SPU_ES: + p_sys->i_sub++; + break; + case VIDEO_ES: + p_sys->i_video++; + break; + } + + EsOutAddInfo( out, es ); + + return es; +} + +static bool EsIsSelected( es_out_id_t *es ) +{ + if( es->p_master ) + { + bool b_decode = false; + if( es->p_master->p_dec ) + { + int i_channel = EsOutGetClosedCaptionsChannel( es->fmt.i_codec ); + if( i_channel != -1 ) + input_DecoderGetCcState( es->p_master->p_dec, &b_decode, i_channel ); + } + return b_decode; + } + else + { + return es->p_dec != NULL; + } +} +static void EsSelect( es_out_t *out, es_out_id_t *es ) +{ + es_out_sys_t *p_sys = out->p_sys; + input_thread_t *p_input = p_sys->p_input; + vlc_value_t val; + const char *psz_var; + + if( EsIsSelected( es ) ) + { + msg_Warn( p_input, "ES 0x%x is already selected", es->i_id ); + return; + } + + if( es->p_master ) + { + int i_channel; + if( !es->p_master->p_dec ) + return; + + i_channel = EsOutGetClosedCaptionsChannel( es->fmt.i_codec ); + if( i_channel == -1 || input_DecoderSetCcState( es->p_master->p_dec, true, i_channel ) ) + return; + } + else + { + if( es->fmt.i_cat == VIDEO_ES || es->fmt.i_cat == SPU_ES ) + { + if( !var_GetBool( p_input, out->b_sout ? "sout-video" : "video" ) ) + { + msg_Dbg( p_input, "video is disabled, not selecting ES 0x%x", + es->i_id ); + return; + } + } + else if( es->fmt.i_cat == AUDIO_ES ) + { + var_Get( p_input, "audio", &val ); + if( !var_GetBool( p_input, out->b_sout ? "sout-audio" : "audio" ) ) + { + msg_Dbg( p_input, "audio is disabled, not selecting ES 0x%x", + es->i_id ); + return; + } + } + if( es->fmt.i_cat == SPU_ES ) + { + var_Get( p_input, "spu", &val ); + if( !var_GetBool( p_input, out->b_sout ? "sout-spu" : "spu" ) ) + { + msg_Dbg( p_input, "spu is disabled, not selecting ES 0x%x", + es->i_id ); + return; + } + } + + es->i_preroll_end = -1; + es->p_dec = input_DecoderNew( p_input, &es->fmt, false ); + if( es->p_dec == NULL || es->p_pgrm != p_sys->p_pgrm ) + return; + } + + if( es->fmt.i_cat == VIDEO_ES ) + psz_var = "video-es"; + else if( es->fmt.i_cat == AUDIO_ES ) + psz_var = "audio-es"; + else if( es->fmt.i_cat == SPU_ES ) + psz_var = "spu-es"; + else + return; + + /* Mark it as selected */ + val.i_int = es->i_id; + var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL ); + + var_SetBool( p_sys->p_input, "intf-change", true ); +} + +static void EsUnselect( es_out_t *out, es_out_id_t *es, bool b_update ) +{ + es_out_sys_t *p_sys = out->p_sys; + input_thread_t *p_input = p_sys->p_input; + vlc_value_t val; + const char *psz_var; + + if( !EsIsSelected( es ) ) + { + msg_Warn( p_input, "ES 0x%x is already unselected", es->i_id ); + return; + } + + if( es->p_master ) + { + if( es->p_master->p_dec ) + { + int i_channel = EsOutGetClosedCaptionsChannel( es->fmt.i_codec ); + if( i_channel != -1 ) + input_DecoderSetCcState( es->p_master->p_dec, false, i_channel ); + } + } + else + { + const int i_spu_id = var_GetInteger( p_input, "spu-es"); + int i; + for( i = 0; i < 4; i++ ) + { + if( !es->pb_cc_present[i] || !es->pp_cc_es[i] ) + continue; + + if( i_spu_id == es->pp_cc_es[i]->i_id ) + { + /* Force unselection of the CC */ + val.i_int = -1; + var_Change( p_input, "spu-es", VLC_VAR_SETVALUE, &val, NULL ); + if( !b_update ) + var_SetBool( p_sys->p_input, "intf-change", true ); + } + EsOutDel( out, es->pp_cc_es[i] ); + + es->pb_cc_present[i] = false; + } + input_DecoderDelete( es->p_dec ); + es->p_dec = NULL; + } + + if( !b_update ) + return; + + /* Update var */ + if( es->p_dec == NULL ) + return; + if( es->fmt.i_cat == VIDEO_ES ) + psz_var = "video-es"; + else if( es->fmt.i_cat == AUDIO_ES ) + psz_var = "audio-es"; + else if( es->fmt.i_cat == SPU_ES ) + psz_var = "spu-es"; + else + return; + + /* Mark it as unselected */ + val.i_int = -1; + var_Change( p_input, psz_var, VLC_VAR_SETVALUE, &val, NULL ); + + var_SetBool( p_sys->p_input, "intf-change", true ); +} + +/** + * Select an ES given the current mode + * XXX: you need to take a the lock before (stream.stream_lock) + * + * \param out The es_out structure + * \param es es_out_id structure + * \param b_force ... + * \return nothing + */ +static void EsOutSelect( es_out_t *out, es_out_id_t *es, bool b_force ) +{ + es_out_sys_t *p_sys = out->p_sys; + + int i_cat = es->fmt.i_cat; + + if( !p_sys->b_active || + ( !b_force && es->fmt.i_priority < 0 ) ) + { + return; + } + + if( p_sys->i_mode == ES_OUT_MODE_ALL || b_force ) + { + if( !EsIsSelected( es ) ) + EsSelect( out, es ); + } + else if( p_sys->i_mode == ES_OUT_MODE_PARTIAL ) + { + vlc_value_t val; + int i; + var_Get( p_sys->p_input, "programs", &val ); + for ( i = 0; i < val.p_list->i_count; i++ ) + { + if ( val.p_list->p_values[i].i_int == es->p_pgrm->i_id || b_force ) + { + if( !EsIsSelected( es ) ) + EsSelect( out, es ); + break; + } + } + var_Change( p_sys->p_input, "programs", VLC_VAR_FREELIST, &val, NULL ); + } + else if( p_sys->i_mode == ES_OUT_MODE_AUTO ) + { + int i_wanted = -1; + + if( es->p_pgrm != p_sys->p_pgrm ) + return; + + if( i_cat == AUDIO_ES ) + { + int idx1 = LanguageArrayIndex( p_sys->ppsz_audio_language, + es->psz_language_code ); + + if( p_sys->p_es_audio && + p_sys->p_es_audio->fmt.i_priority >= es->fmt.i_priority ) + { + int idx2 = LanguageArrayIndex( p_sys->ppsz_audio_language, + p_sys->p_es_audio->psz_language_code ); + + if( idx1 < 0 || ( idx2 >= 0 && idx2 <= idx1 ) ) + return; + i_wanted = es->i_channel; + } + else + { + /* Select audio if (no audio selected yet) + * - no audio-language + * - no audio code for the ES + * - audio code in the requested list */ + if( idx1 >= 0 || + !strcmp( es->psz_language_code, "??" ) || + !p_sys->ppsz_audio_language ) + i_wanted = es->i_channel; + } + + if( p_sys->i_audio_last >= 0 ) + i_wanted = p_sys->i_audio_last; + + if( p_sys->i_audio_id >= 0 ) + { + if( es->i_id == p_sys->i_audio_id ) + i_wanted = es->i_channel; + else + return; + } + } + else if( i_cat == SPU_ES ) + { + int idx1 = LanguageArrayIndex( p_sys->ppsz_sub_language, + es->psz_language_code ); + + if( p_sys->p_es_sub && + p_sys->p_es_sub->fmt.i_priority >= es->fmt.i_priority ) + { + int idx2 = LanguageArrayIndex( p_sys->ppsz_sub_language, + p_sys->p_es_sub->psz_language_code ); + + msg_Dbg( p_sys->p_input, "idx1=%d(%s) idx2=%d(%s)", + idx1, es->psz_language_code, idx2, + p_sys->p_es_sub->psz_language_code ); + + if( idx1 < 0 || ( idx2 >= 0 && idx2 <= idx1 ) ) + return; + /* We found a SPU that matches our language request */ + i_wanted = es->i_channel; + } + else if( idx1 >= 0 ) + { + msg_Dbg( p_sys->p_input, "idx1=%d(%s)", + idx1, es->psz_language_code ); + + i_wanted = es->i_channel; + } + else if( p_sys->i_default_sub_id >= 0 ) + { + if( es->i_id == p_sys->i_default_sub_id ) + i_wanted = es->i_channel; + } + + if( p_sys->i_sub_last >= 0 ) + i_wanted = p_sys->i_sub_last; + + if( p_sys->i_sub_id >= 0 ) + { + if( es->i_id == p_sys->i_sub_id ) + i_wanted = es->i_channel; + else + return; + } + } + else if( i_cat == VIDEO_ES ) + { + i_wanted = es->i_channel; + } + + if( i_wanted == es->i_channel && !EsIsSelected( es ) ) + EsSelect( out, es ); + } + + /* FIXME TODO handle priority here */ + if( EsIsSelected( es ) ) + { + if( i_cat == AUDIO_ES ) + { + if( p_sys->i_mode == ES_OUT_MODE_AUTO && + p_sys->p_es_audio && + p_sys->p_es_audio != es && + EsIsSelected( p_sys->p_es_audio ) ) + { + EsUnselect( out, p_sys->p_es_audio, false ); + } + p_sys->p_es_audio = es; + } + else if( i_cat == SPU_ES ) + { + if( p_sys->i_mode == ES_OUT_MODE_AUTO && + p_sys->p_es_sub && + p_sys->p_es_sub != es && + EsIsSelected( p_sys->p_es_sub ) ) + { + EsUnselect( out, p_sys->p_es_sub, false ); + } + p_sys->p_es_sub = es; + } + else if( i_cat == VIDEO_ES ) + { + p_sys->p_es_video = es; + } + } +} + +/** + * Send a block for the given es_out + * + * \param out the es_out to send from + * \param es the es_out_id + * \param p_block the data block to send + */ +static int EsOutSend( es_out_t *out, es_out_id_t *es, block_t *p_block ) +{ + es_out_sys_t *p_sys = out->p_sys; + input_thread_t *p_input = p_sys->p_input; + es_out_pgrm_t *p_pgrm = es->p_pgrm; + int64_t i_delay; + int i_total=0; + + if( es->fmt.i_cat == AUDIO_ES ) + i_delay = p_sys->i_audio_delay; + else if( es->fmt.i_cat == SPU_ES ) + i_delay = p_sys->i_spu_delay; + else + i_delay = 0; + + if( libvlc_stats (p_input) ) + { + vlc_mutex_lock( &p_input->p->counters.counters_lock ); + stats_UpdateInteger( p_input, p_input->p->counters.p_demux_read, + p_block->i_buffer, &i_total ); + stats_UpdateFloat( p_input , p_input->p->counters.p_demux_bitrate, + (float)i_total, NULL ); + vlc_mutex_unlock( &p_input->p->counters.counters_lock ); + } + + /* Mark preroll blocks */ + if( es->i_preroll_end >= 0 ) + { + int64_t i_date = p_block->i_pts; + if( i_date <= 0 ) + i_date = p_block->i_dts; + + if( i_date < es->i_preroll_end ) + p_block->i_flags |= BLOCK_FLAG_PREROLL; + else + es->i_preroll_end = -1; + } + + if( p_block->i_dts > 0 && (p_block->i_flags&BLOCK_FLAG_PREROLL) ) + { + p_block->i_dts += i_delay; + } + else if( p_block->i_dts > 0 ) + { + p_block->i_dts = + input_ClockGetTS( p_input, &p_pgrm->clock, p_block->i_dts ) + i_delay; + } + if( p_block->i_pts > 0 && (p_block->i_flags&BLOCK_FLAG_PREROLL) ) + { + p_block->i_pts += i_delay; + } + else if( p_block->i_pts > 0 ) + { + p_block->i_pts = + input_ClockGetTS( p_input, &p_pgrm->clock, p_block->i_pts ) + i_delay; + } + if ( p_block->i_rate == INPUT_RATE_DEFAULT && + es->fmt.i_codec == VLC_FOURCC( 't', 'e', 'l', 'x' ) ) + { + mtime_t current_date = mdate(); + if( !p_block->i_pts + || p_block->i_pts > current_date + 10000000 + || current_date > p_block->i_pts ) + { + /* ETSI EN 300 472 Annex A : do not take into account the PTS + * for teletext streams. */ + p_block->i_pts = current_date + 400000 + + p_input->i_pts_delay + i_delay; + } + } + + p_block->i_rate = p_sys->i_rate; + + /* TODO handle mute */ + if( es->p_dec && + ( es->fmt.i_cat != AUDIO_ES || + ( p_sys->i_rate >= INPUT_RATE_DEFAULT/AOUT_MAX_INPUT_RATE && + p_sys->i_rate <= INPUT_RATE_DEFAULT*AOUT_MAX_INPUT_RATE ) ) ) + { + bool pb_cc[4]; + bool b_cc_new = false; + int i; + input_DecoderDecode( es->p_dec, p_block ); + + /* Check CC status */ + input_DecoderIsCcPresent( es->p_dec, pb_cc ); + for( i = 0; i < 4; i++ ) + { + static const vlc_fourcc_t fcc[4] = { + VLC_FOURCC('c', 'c', '1', ' '), + VLC_FOURCC('c', 'c', '2', ' '), + VLC_FOURCC('c', 'c', '3', ' '), + VLC_FOURCC('c', 'c', '4', ' '), + }; + static const char ppsz_description[4][18] = { + N_("Closed captions 1"), + N_("Closed captions 2"), + N_("Closed captions 3"), + N_("Closed captions 4"), + }; + es_format_t fmt; + + if( es->pb_cc_present[i] || !pb_cc[i] ) + continue; + msg_Dbg( p_input, "Adding CC track %d for es[%d]", 1+i, es->i_id ); + + es_format_Init( &fmt, SPU_ES, fcc[i] ); + fmt.i_group = es->fmt.i_group; + fmt.psz_description = strdup( _(ppsz_description[i] ) ); + es->pp_cc_es[i] = EsOutAdd( out, &fmt ); + es->pp_cc_es[i]->p_master = es; + es_format_Clean( &fmt ); + + /* */ + es->pb_cc_present[i] = true; + b_cc_new = true; + } + if( b_cc_new ) + var_SetBool( p_sys->p_input, "intf-change", true ); + } + else + { + block_Release( p_block ); + } + + return VLC_SUCCESS; +} + +/***************************************************************************** + * EsOutDel: + *****************************************************************************/ +static void EsOutDel( es_out_t *out, es_out_id_t *es ) +{ + es_out_sys_t *p_sys = out->p_sys; + bool b_reselect = false; + int i; + + /* We don't try to reselect */ + if( es->p_dec ) + { + while( !out->p_sys->p_input->b_die && es->p_dec ) + { + if( input_DecoderEmpty( es->p_dec ) ) + break; + msleep( 20*1000 ); + } + EsUnselect( out, es, es->p_pgrm == p_sys->p_pgrm ); + } + + if( es->p_pgrm == p_sys->p_pgrm ) + EsOutESVarUpdate( out, es, true ); + + TAB_REMOVE( p_sys->i_es, p_sys->es, es ); + + es->p_pgrm->i_es--; + if( es->p_pgrm->i_es == 0 ) + { + msg_Dbg( p_sys->p_input, "Program doesn't contain anymore ES" ); + } + + if( p_sys->p_es_audio == es || p_sys->p_es_video == es || + p_sys->p_es_sub == es ) b_reselect = true; + + if( p_sys->p_es_audio == es ) p_sys->p_es_audio = NULL; + if( p_sys->p_es_video == es ) p_sys->p_es_video = NULL; + if( p_sys->p_es_sub == es ) p_sys->p_es_sub = NULL; + + switch( es->fmt.i_cat ) + { + case AUDIO_ES: + p_sys->i_audio--; + break; + case SPU_ES: + p_sys->i_sub--; + break; + case VIDEO_ES: + p_sys->i_video--; + break; + } + + /* Re-select another track when needed */ + if( b_reselect ) + for( i = 0; i < p_sys->i_es; i++ ) + { + if( es->fmt.i_cat == p_sys->es[i]->fmt.i_cat ) + EsOutSelect( out, p_sys->es[i], false ); + } + + free( es->psz_language ); + free( es->psz_language_code ); + + es_format_Clean( &es->fmt ); + + free( es ); +} + +/** + * Control query handler + * + * \param out the es_out to control + * \param i_query A es_out query as defined in include/ninput.h + * \param args a variable list of arguments for the query + * \return VLC_SUCCESS or an error code + */ +static int EsOutControl( es_out_t *out, int i_query, va_list args ) +{ + es_out_sys_t *p_sys = out->p_sys; + bool b, *pb; + int i, *pi; + + es_out_id_t *es; + + switch( i_query ) + { + case ES_OUT_SET_ES_STATE: + es = (es_out_id_t*) va_arg( args, es_out_id_t * ); + b = (bool) va_arg( args, int ); + if( b && !EsIsSelected( es ) ) + { + EsSelect( out, es ); + return EsIsSelected( es ) ? VLC_SUCCESS : VLC_EGENERIC; + } + else if( !b && EsIsSelected( es ) ) + { + EsUnselect( out, es, es->p_pgrm == p_sys->p_pgrm ); + return VLC_SUCCESS; + } + return VLC_SUCCESS; + + case ES_OUT_GET_ES_STATE: + es = (es_out_id_t*) va_arg( args, es_out_id_t * ); + pb = (bool*) va_arg( args, bool * ); + + *pb = EsIsSelected( es ); + return VLC_SUCCESS; + + case ES_OUT_SET_ACTIVE: + { + b = (bool) va_arg( args, int ); + p_sys->b_active = b; + /* Needed ? */ + if( b ) + var_SetBool( p_sys->p_input, "intf-change", true ); + return VLC_SUCCESS; + } + + case ES_OUT_GET_ACTIVE: + pb = (bool*) va_arg( args, bool * ); + *pb = p_sys->b_active; + return VLC_SUCCESS; + + case ES_OUT_SET_MODE: + i = (int) va_arg( args, int ); + if( i == ES_OUT_MODE_NONE || i == ES_OUT_MODE_ALL || + i == ES_OUT_MODE_AUTO || i == ES_OUT_MODE_PARTIAL ) + { + p_sys->i_mode = i; + + /* Reapply policy mode */ + for( i = 0; i < p_sys->i_es; i++ ) + { + if( EsIsSelected( p_sys->es[i] ) ) + { + EsUnselect( out, p_sys->es[i], + p_sys->es[i]->p_pgrm == p_sys->p_pgrm ); + } + } + for( i = 0; i < p_sys->i_es; i++ ) + { + EsOutSelect( out, p_sys->es[i], false ); + } + return VLC_SUCCESS; + } + return VLC_EGENERIC; + + case ES_OUT_GET_MODE: + pi = (int*) va_arg( args, int* ); + *pi = p_sys->i_mode; + return VLC_SUCCESS; + + case ES_OUT_SET_ES: + es = (es_out_id_t*) va_arg( args, es_out_id_t * ); + /* Special case NULL, NULL+i_cat */ + if( es == NULL ) + { + for( i = 0; i < p_sys->i_es; i++ ) + { + if( EsIsSelected( p_sys->es[i] ) ) + EsUnselect( out, p_sys->es[i], + p_sys->es[i]->p_pgrm == p_sys->p_pgrm ); + } + } + else if( es == (es_out_id_t*)((uint8_t*)NULL+AUDIO_ES) ) + { + for( i = 0; i < p_sys->i_es; i++ ) + { + if( p_sys->es[i]->fmt.i_cat == AUDIO_ES && + EsIsSelected( p_sys->es[i] ) ) + EsUnselect( out, p_sys->es[i], + p_sys->es[i]->p_pgrm == p_sys->p_pgrm ); + } + } + else if( es == (es_out_id_t*)((uint8_t*)NULL+VIDEO_ES) ) + { + for( i = 0; i < p_sys->i_es; i++ ) + { + if( p_sys->es[i]->fmt.i_cat == VIDEO_ES && + EsIsSelected( p_sys->es[i] ) ) + EsUnselect( out, p_sys->es[i], + p_sys->es[i]->p_pgrm == p_sys->p_pgrm ); + } + } + else if( es == (es_out_id_t*)((uint8_t*)NULL+SPU_ES) ) + { + for( i = 0; i < p_sys->i_es; i++ ) + { + if( p_sys->es[i]->fmt.i_cat == SPU_ES && + EsIsSelected( p_sys->es[i] ) ) + EsUnselect( out, p_sys->es[i], + p_sys->es[i]->p_pgrm == p_sys->p_pgrm ); + } + } + else + { + for( i = 0; i < p_sys->i_es; i++ ) + { + if( es == p_sys->es[i] ) + { + EsOutSelect( out, es, true ); + break; + } + } + } + { + vlc_event_t event; + event.type = vlc_InputSelectedStreamChanged; + vlc_event_send( &p_sys->p_input->p->event_manager, &event ); + } + return VLC_SUCCESS; + + case ES_OUT_SET_DEFAULT: + { + es = (es_out_id_t*) va_arg( args, es_out_id_t * ); + + if( es == NULL ) + { + /*p_sys->i_default_video_id = -1;*/ + /*p_sys->i_default_audio_id = -1;*/ + p_sys->i_default_sub_id = -1; + } + else if( es == (es_out_id_t*)((uint8_t*)NULL+AUDIO_ES) ) + { + /*p_sys->i_default_video_id = -1;*/ + } + else if( es == (es_out_id_t*)((uint8_t*)NULL+VIDEO_ES) ) + { + /*p_sys->i_default_audio_id = -1;*/ + } + else if( es == (es_out_id_t*)((uint8_t*)NULL+SPU_ES) ) + { + p_sys->i_default_sub_id = -1; + } + else + { + /*if( es->fmt.i_cat == VIDEO_ES ) + p_sys->i_default_video_id = es->i_id; + else + if( es->fmt.i_cat == AUDIO_ES ) + p_sys->i_default_audio_id = es->i_id; + else*/ + if( es->fmt.i_cat == SPU_ES ) + p_sys->i_default_sub_id = es->i_id; + } + return VLC_SUCCESS; + } + + case ES_OUT_SET_PCR: + case ES_OUT_SET_GROUP_PCR: + { + es_out_pgrm_t *p_pgrm = NULL; + int i_group = 0; + int64_t i_pcr; + + if( i_query == ES_OUT_SET_PCR ) + { + p_pgrm = p_sys->p_pgrm; + } + else + { + int i; + i_group = (int)va_arg( args, int ); + for( i = 0; i < p_sys->i_pgrm; i++ ) + { + if( p_sys->pgrm[i]->i_id == i_group ) + { + p_pgrm = p_sys->pgrm[i]; + break; + } + } + } + if( p_pgrm == NULL ) + p_pgrm = EsOutProgramAdd( out, i_group ); /* Create it */ + + i_pcr = (int64_t)va_arg( args, int64_t ); + /* search program */ + input_ClockSetPCR( p_sys->p_input, &p_pgrm->clock, i_pcr ); + return VLC_SUCCESS; + } + + case ES_OUT_RESET_PCR: + for( i = 0; i < p_sys->i_pgrm; i++ ) + input_ClockResetPCR( &p_sys->pgrm[i]->clock ); + return VLC_SUCCESS; + + case ES_OUT_GET_TS: + if( p_sys->p_pgrm ) + { + int64_t i_ts = (int64_t)va_arg( args, int64_t ); + int64_t *pi_ts = (int64_t *)va_arg( args, int64_t * ); + *pi_ts = input_ClockGetTS( p_sys->p_input, + &p_sys->p_pgrm->clock, i_ts ); + return VLC_SUCCESS; + } + return VLC_EGENERIC; + + case ES_OUT_GET_GROUP: + pi = (int*) va_arg( args, int* ); + if( p_sys->p_pgrm ) + *pi = p_sys->p_pgrm->i_id; + else + *pi = -1; /* FIXME */ + return VLC_SUCCESS; + + case ES_OUT_SET_GROUP: + { + int j; + i = (int) va_arg( args, int ); + for( j = 0; j < p_sys->i_pgrm; j++ ) + { + es_out_pgrm_t *p_pgrm = p_sys->pgrm[j]; + if( p_pgrm->i_id == i ) + { + EsOutProgramSelect( out, p_pgrm ); + return VLC_SUCCESS; + } + } + return VLC_EGENERIC; + } + + case ES_OUT_SET_FMT: + { + /* This ain't pretty but is need by some demuxers (eg. Ogg ) + * to update the p_extra data */ + es_format_t *p_fmt; + es = (es_out_id_t*) va_arg( args, es_out_id_t * ); + p_fmt = (es_format_t*) va_arg( args, es_format_t * ); + if( es == NULL ) return VLC_EGENERIC; + + if( p_fmt->i_extra ) + { + es->fmt.i_extra = p_fmt->i_extra; + es->fmt.p_extra = realloc( es->fmt.p_extra, p_fmt->i_extra ); + memcpy( es->fmt.p_extra, p_fmt->p_extra, p_fmt->i_extra ); + + if( !es->p_dec ) return VLC_SUCCESS; + +#if 1 + input_DecoderDelete( es->p_dec ); + es->p_dec = input_DecoderNew( p_sys->p_input, + &es->fmt, false ); + +#else + es->p_dec->fmt_in.i_extra = p_fmt->i_extra; + es->p_dec->fmt_in.p_extra = + realloc( es->p_dec->fmt_in.p_extra, p_fmt->i_extra ); + memcpy( es->p_dec->fmt_in.p_extra, + p_fmt->p_extra, p_fmt->i_extra ); +#endif + } + + return VLC_SUCCESS; + } + + case ES_OUT_SET_NEXT_DISPLAY_TIME: + { + int64_t i_date; + + es = (es_out_id_t*) va_arg( args, es_out_id_t * ); + i_date = (int64_t)va_arg( args, int64_t ); + + if( !es || !es->p_dec ) + return VLC_EGENERIC; + + /* XXX We should call input_ClockGetTS but PCR has been reseted + * and it will return 0, so we won't call input_ClockGetTS on all preroll samples + * but that's ugly(more time discontinuity), it need to be improved -- fenrir */ + es->i_preroll_end = i_date; + + return VLC_SUCCESS; + } + case ES_OUT_SET_GROUP_META: + { + int i_group = (int)va_arg( args, int ); + vlc_meta_t *p_meta = (vlc_meta_t*)va_arg( args, vlc_meta_t * ); + + EsOutProgramMeta( out, i_group, p_meta ); + return VLC_SUCCESS; + } + case ES_OUT_SET_GROUP_EPG: + { + int i_group = (int)va_arg( args, int ); + vlc_epg_t *p_epg = (vlc_epg_t*)va_arg( args, vlc_epg_t * ); + + EsOutProgramEpg( out, i_group, p_epg ); + return VLC_SUCCESS; + } + case ES_OUT_DEL_GROUP: + { + int i_group = (int)va_arg( args, int ); + + return EsOutProgramDel( out, i_group ); + } + + default: + msg_Err( p_sys->p_input, "unknown query in es_out_Control" ); + return VLC_EGENERIC; + } +} + +/**************************************************************************** + * LanguageGetName: try to expend iso639 into plain name + ****************************************************************************/ +static char *LanguageGetName( const char *psz_code ) +{ + const iso639_lang_t *pl; + + if( psz_code == NULL ) + { + return strdup( "" ); + } + + if( strlen( psz_code ) == 2 ) + { + pl = GetLang_1( psz_code ); + } + else if( strlen( psz_code ) == 3 ) + { + pl = GetLang_2B( psz_code ); + if( !strcmp( pl->psz_iso639_1, "??" ) ) + { + pl = GetLang_2T( psz_code ); + } + } + else + { + return strdup( psz_code ); + } + + if( !strcmp( pl->psz_iso639_1, "??" ) ) + { + return strdup( psz_code ); + } + else + { + if( *pl->psz_native_name ) + { + return strdup( pl->psz_native_name ); + } + return strdup( pl->psz_eng_name ); + } +} + +/* Get a 2 char code */ +static char *LanguageGetCode( const char *psz_lang ) +{ + const iso639_lang_t *pl; + + if( psz_lang == NULL || *psz_lang == '\0' ) + return strdup("??"); + + for( pl = p_languages; pl->psz_iso639_1 != NULL; pl++ ) + { + if( !strcasecmp( pl->psz_eng_name, psz_lang ) || + !strcasecmp( pl->psz_native_name, psz_lang ) || + !strcasecmp( pl->psz_iso639_1, psz_lang ) || + !strcasecmp( pl->psz_iso639_2T, psz_lang ) || + !strcasecmp( pl->psz_iso639_2B, psz_lang ) ) + break; + } + + if( pl->psz_iso639_1 != NULL ) + return strdup( pl->psz_iso639_1 ); + + return strdup("??"); +} + +static char **LanguageSplit( const char *psz_langs ) +{ + char *psz_dup; + char *psz_parser; + char **ppsz = NULL; + int i_psz = 0; + + if( psz_langs == NULL ) return NULL; + + psz_parser = psz_dup = strdup(psz_langs); + + while( psz_parser && *psz_parser ) + { + char *psz; + char *psz_code; + + psz = strchr(psz_parser, ',' ); + if( psz ) *psz++ = '\0'; + + if( !strcmp( psz_parser, "any" ) ) + { + TAB_APPEND( i_psz, ppsz, strdup("any") ); + } + else + { + psz_code = LanguageGetCode( psz_parser ); + if( strcmp( psz_code, "??" ) ) + { + TAB_APPEND( i_psz, ppsz, psz_code ); + } + else + { + free( psz_code ); + } + } + + psz_parser = psz; + } + + if( i_psz ) + { + TAB_APPEND( i_psz, ppsz, NULL ); + } + + free( psz_dup ); + return ppsz; +} + +static int LanguageArrayIndex( char **ppsz_langs, char *psz_lang ) +{ + int i; + + if( !ppsz_langs || !psz_lang ) return -1; + + for( i = 0; ppsz_langs[i]; i++ ) + { + if( !strcasecmp( ppsz_langs[i], psz_lang ) || + !strcasecmp( ppsz_langs[i], "any" ) ) + { + return i; + } + } + + return -1; +} + +/**************************************************************************** + * EsOutAddInfo: + * - add meta info to the playlist item + ****************************************************************************/ +static void EsOutAddInfo( es_out_t *out, es_out_id_t *es ) +{ + es_out_sys_t *p_sys = out->p_sys; + input_thread_t *p_input = p_sys->p_input; + es_format_t *fmt = &es->fmt; + char *psz_cat; + lldiv_t div; + + /* Add stream info */ + if( asprintf( &psz_cat, _("Stream %d"), out->p_sys->i_id - 1 ) == -1 ) + return; + + input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Codec"), + "%.4s", (char*)&fmt->i_codec ); + + input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Language"), + "%s", es->psz_language ); + + /* Add information */ + switch( fmt->i_cat ) + { + case AUDIO_ES: + input_Control( p_input, INPUT_ADD_INFO, psz_cat, + _("Type"), _("Audio") ); + + if( fmt->audio.i_channels > 0 ) + input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Channels"), + "%u", fmt->audio.i_channels ); + + if( fmt->audio.i_rate > 0 ) + { + input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Sample rate"), + _("%u Hz"), fmt->audio.i_rate ); + var_SetInteger( p_input, "sample-rate", fmt->audio.i_rate ); + } + + if( fmt->audio.i_bitspersample > 0 ) + input_Control( p_input, INPUT_ADD_INFO, psz_cat, + _("Bits per sample"), "%u", + fmt->audio.i_bitspersample ); + + if( fmt->i_bitrate > 0 ) + { + input_Control( p_input, INPUT_ADD_INFO, psz_cat, _("Bitrate"), + _("%u kb/s"), fmt->i_bitrate / 1000 ); + var_SetInteger( p_input, "bit-rate", fmt->i_bitrate ); + } + break; + + case VIDEO_ES: + input_Control( p_input, INPUT_ADD_INFO, psz_cat, + _("Type"), _("Video") ); + + if( fmt->video.i_width > 0 && fmt->video.i_height > 0 ) + input_Control( p_input, INPUT_ADD_INFO, psz_cat, + _("Resolution"), "%ux%u", + fmt->video.i_width, fmt->video.i_height ); + + if( fmt->video.i_visible_width > 0 && + fmt->video.i_visible_height > 0 ) + input_Control( p_input, INPUT_ADD_INFO, psz_cat, + _("Display resolution"), "%ux%u", + fmt->video.i_visible_width, + fmt->video.i_visible_height); + if( fmt->video.i_frame_rate > 0 && + fmt->video.i_frame_rate_base > 0 ) + { + div = lldiv( (float)fmt->video.i_frame_rate / + fmt->video.i_frame_rate_base * 1000000, + 1000000 ); + input_Control( p_input, INPUT_ADD_INFO, psz_cat, + _("Frame rate"), "%"PRId64".%06u", + div.quot, (unsigned int )div.rem ); + } + break; + + case SPU_ES: + input_Control( p_input, INPUT_ADD_INFO, psz_cat, + _("Type"), _("Subtitle") ); + break; + + default: + break; + } + + free( psz_cat ); +} diff --git a/VLC/input/input.c b/VLC/input/input.c new file mode 100644 index 0000000..f7b7e72 --- /dev/null +++ b/VLC/input/input.c @@ -0,0 +1,2882 @@ +/***************************************************************************** + * input.c: input thread + ***************************************************************************** + * Copyright (C) 1998-2007 the VideoLAN team + * $Id$ + * + * Authors: Christophe Massiot + * Laurent Aimar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#include +#include +#include + +#include "input_internal.h" + +#include "vlc_sout.h" +#include "../stream_output/stream_output.h" + +#include "vlc_interface.h" +#include "vlc_url.h" +#include "vlc_charset.h" + +#ifdef HAVE_SYS_STAT_H +# include +#endif + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +static void Destructor( input_thread_t * p_input ); + +static void* Run ( vlc_object_t *p_this ); +static void* RunAndDestroy ( vlc_object_t *p_this ); + +static input_thread_t * Create ( vlc_object_t *, input_item_t *, + const char *, bool, sout_instance_t * ); +static int Init ( input_thread_t *p_input ); +static void WaitDie ( input_thread_t *p_input ); +static void End ( input_thread_t *p_input ); +static void MainLoop( input_thread_t *p_input ); + +static inline int ControlPopNoLock( input_thread_t *, int *, vlc_value_t * ); +static void ControlReduce( input_thread_t * ); +static bool Control( input_thread_t *, int, vlc_value_t ); + +static int UpdateFromAccess( input_thread_t * ); +static int UpdateFromDemux( input_thread_t * ); + +static void UpdateItemLength( input_thread_t *, int64_t i_length ); + +static void MRLSections( input_thread_t *, char *, int *, int *, int *, int *); + +static input_source_t *InputSourceNew( input_thread_t *); +static int InputSourceInit( input_thread_t *, input_source_t *, + const char *, const char *psz_forced_demux ); +static void InputSourceClean( input_source_t * ); +/* TODO */ +//static void InputGetAttachments( input_thread_t *, input_source_t * ); +static void SlaveDemux( input_thread_t *p_input ); +static void SlaveSeek( input_thread_t *p_input ); + +static void InputMetaUser( input_thread_t *p_input, vlc_meta_t *p_meta ); +static void InputUpdateMeta( input_thread_t *p_input, vlc_meta_t *p_meta ); + +static void DemuxMeta( input_thread_t *p_input, vlc_meta_t *p_meta, demux_t *p_demux ); +static void AccessMeta( input_thread_t * p_input, vlc_meta_t *p_meta ); +static void AppendAttachment( int *pi_attachment, input_attachment_t ***ppp_attachment, + int i_new, input_attachment_t **pp_new ); + +/***************************************************************************** + * This function creates a new input, and returns a pointer + * to its description. On error, it returns NULL. + * + * Variables for _public_ use: + * * Get and Set: + * - state + * - rate,rate-slower, rate-faster + * - position, position-offset + * - time, time-offset + * - title,title-next,title-prev + * - chapter,chapter-next, chapter-prev + * - program, audio-es, video-es, spu-es + * - audio-delay, spu-delay + * - bookmark + * * Get only: + * - length + * - bookmarks + * - seekable (if you can seek, it doesn't say if 'bar display' has be shown + * or not, for that check position != 0.0) + * - can-pause + * - teletext-es to get the index of spu track that is teletext --1 if no teletext) + * * For intf callback upon changes + * - intf-change + * - rate-change for when playback rate changes + * TODO explain when Callback is called + * TODO complete this list (?) + *****************************************************************************/ +static input_thread_t *Create( vlc_object_t *p_parent, input_item_t *p_item, + const char *psz_header, bool b_quick, + sout_instance_t *p_sout ) +{ + static const char input_name[] = "input"; + input_thread_t *p_input = NULL; /* thread descriptor */ + vlc_value_t val; + int i; + + /* Allocate descriptor */ + p_input = vlc_custom_create( p_parent, sizeof( *p_input ), + VLC_OBJECT_INPUT, input_name ); + if( p_input == NULL ) + return NULL; + + /* Construct a nice name for the input timer */ + char psz_timer_name[255]; + char * psz_name = input_item_GetName( p_item ); + snprintf( psz_timer_name, sizeof(psz_timer_name), + "input launching for '%s'", psz_name ); + + msg_Dbg( p_input, "Creating an input for '%s'", psz_name); + + free( psz_name ); + + /* Start a timer to mesure how long it takes + * to launch an input */ + stats_TimerStart( p_input, psz_timer_name, + STATS_TIMER_INPUT_LAUNCHING ); + + MALLOC_NULL( p_input->p, input_thread_private_t ); + memset( p_input->p, 0, sizeof( input_thread_private_t ) ); + + /* One "randomly" selected input thread is responsible for computing + * the global stats. Check if there is already someone doing this */ + if( p_input->p_libvlc->p_stats && !b_quick ) + { + libvlc_priv_t *priv = libvlc_priv (p_input->p_libvlc); + vlc_mutex_lock( &p_input->p_libvlc->p_stats->lock ); + if( priv->p_stats_computer == NULL ) + priv->p_stats_computer = p_input; + vlc_mutex_unlock( &p_input->p_libvlc->p_stats->lock ); + } + + p_input->b_preparsing = b_quick; + p_input->psz_header = psz_header ? strdup( psz_header ) : NULL; + + /* Init events */ + vlc_event_manager_t * p_em = &p_input->p->event_manager; + vlc_event_manager_init_with_vlc_object( p_em, p_input ); + vlc_event_manager_register_event_type( p_em, vlc_InputStateChanged ); + vlc_event_manager_register_event_type( p_em, vlc_InputSelectedStreamChanged ); + + /* Init Common fields */ + p_input->b_eof = false; + p_input->b_can_pace_control = true; + p_input->p->i_start = 0; + p_input->i_time = 0; + p_input->p->i_stop = 0; + p_input->p->i_run = 0; + p_input->p->i_title = 0; + p_input->p->title = NULL; + p_input->p->i_title_offset = p_input->p->i_seekpoint_offset = 0; + p_input->i_state = INIT_S; + p_input->p->i_rate = INPUT_RATE_DEFAULT; + TAB_INIT( p_input->p->i_bookmark, p_input->p->bookmark ); + TAB_INIT( p_input->p->i_attachment, p_input->p->attachment ); + p_input->p->p_es_out = NULL; + p_input->p->p_sout = NULL; + p_input->p->b_out_pace_control = false; + p_input->i_pts_delay = 0; + + /* Init Input fields */ + vlc_gc_incref( p_item ); /* Released in Destructor() */ + p_input->p->input.p_item = p_item; + p_input->p->input.p_access = NULL; + p_input->p->input.p_stream = NULL; + p_input->p->input.p_demux = NULL; + p_input->p->input.b_title_demux = false; + p_input->p->input.i_title = 0; + p_input->p->input.title = NULL; + p_input->p->input.i_title_offset = p_input->p->input.i_seekpoint_offset = 0; + p_input->p->input.b_can_pace_control = true; + p_input->p->input.b_can_rate_control = true; + p_input->p->input.b_rescale_ts = true; + p_input->p->input.b_eof = false; + p_input->p->input.i_cr_average = 0; + + vlc_mutex_lock( &p_item->lock ); + + if( !p_item->p_stats ) + p_item->p_stats = stats_NewInputStats( p_input ); + vlc_mutex_unlock( &p_item->lock ); + + /* No slave */ + p_input->p->i_slave = 0; + p_input->p->slave = NULL; + + /* Init control buffer */ + vlc_mutex_init( &p_input->p->lock_control ); + p_input->p->i_control = 0; + + /* Parse input options */ + vlc_mutex_lock( &p_item->lock ); + assert( (int)p_item->optflagc == p_item->i_options ); + for( i = 0; i < p_item->i_options; i++ ) + var_OptionParse( VLC_OBJECT(p_input), p_item->ppsz_options[i], + !!(p_item->optflagv[i] & VLC_INPUT_OPTION_TRUSTED) ); + vlc_mutex_unlock( &p_item->lock ); + + /* Create Object Variables for private use only */ + input_ConfigVarInit( p_input ); + + /* Create Objects variables for public Get and Set */ + input_ControlVarInit( p_input ); + + p_input->p->input.i_cr_average = var_GetInteger( p_input, "cr-average" ); + + if( !p_input->b_preparsing ) + { + var_Get( p_input, "bookmarks", &val ); + if( val.psz_string ) + { + /* FIXME: have a common cfg parsing routine used by sout and others */ + char *psz_parser, *psz_start, *psz_end; + psz_parser = val.psz_string; + while( (psz_start = strchr( psz_parser, '{' ) ) ) + { + seekpoint_t *p_seekpoint = vlc_seekpoint_New(); + char backup; + psz_start++; + psz_end = strchr( psz_start, '}' ); + if( !psz_end ) break; + psz_parser = psz_end + 1; + backup = *psz_parser; + *psz_parser = 0; + *psz_end = ','; + while( (psz_end = strchr( psz_start, ',' ) ) ) + { + *psz_end = 0; + if( !strncmp( psz_start, "name=", 5 ) ) + { + p_seekpoint->psz_name = strdup(psz_start + 5); + } + else if( !strncmp( psz_start, "bytes=", 6 ) ) + { + p_seekpoint->i_byte_offset = atoll(psz_start + 6); + } + else if( !strncmp( psz_start, "time=", 5 ) ) + { + p_seekpoint->i_time_offset = atoll(psz_start + 5) * + 1000000; + } + psz_start = psz_end + 1; + } + msg_Dbg( p_input, "adding bookmark: %s, bytes=%"PRId64", time=%"PRId64, + p_seekpoint->psz_name, p_seekpoint->i_byte_offset, + p_seekpoint->i_time_offset ); + input_Control( p_input, INPUT_ADD_BOOKMARK, p_seekpoint ); + vlc_seekpoint_Delete( p_seekpoint ); + *psz_parser = backup; + } + free( val.psz_string ); + } + } + + /* Remove 'Now playing' info as it is probably outdated */ + input_item_SetNowPlaying( p_item, NULL ); + + /* */ + if( p_input->b_preparsing ) + p_input->i_flags |= OBJECT_FLAGS_QUIET | OBJECT_FLAGS_NOINTERACT; + + /* */ + if( p_sout ) + p_input->p->p_sout = p_sout; + + memset( &p_input->p->counters, 0, sizeof( p_input->p->counters ) ); + vlc_mutex_init( &p_input->p->counters.counters_lock ); + + /* Set the destructor when we are sure we are initialized */ + vlc_object_set_destructor( p_input, (vlc_destructor_t)Destructor ); + + /* Attach only once we are ready */ + vlc_object_attach( p_input, p_parent ); + + return p_input; +} + +/** + * Input destructor (called when the object's refcount reaches 0). + */ +static void Destructor( input_thread_t * p_input ) +{ + input_thread_private_t *priv = p_input->p; + +#ifndef NDEBUG + char * psz_name = input_item_GetName( p_input->p->input.p_item ); + msg_Dbg( p_input, "Destroying the input for '%s'", psz_name); + free( psz_name ); +#endif + + vlc_event_manager_fini( &p_input->p->event_manager ); + + stats_TimerDump( p_input, STATS_TIMER_INPUT_LAUNCHING ); + stats_TimerClean( p_input, STATS_TIMER_INPUT_LAUNCHING ); +#ifdef ENABLE_SOUT + if( priv->p_sout ) + sout_DeleteInstance( priv->p_sout ); +#endif + vlc_gc_decref( p_input->p->input.p_item ); + + vlc_mutex_destroy( &p_input->p->counters.counters_lock ); + + vlc_mutex_destroy( &priv->lock_control ); + free( priv ); +} + +/** + * Initialize an input thread and run it. You will need to monitor the + * thread to clean up after it is done + * + * \param p_parent a vlc_object + * \param p_item an input item + * \return a pointer to the spawned input thread + */ +input_thread_t *__input_CreateThread( vlc_object_t *p_parent, + input_item_t *p_item ) +{ + return __input_CreateThreadExtended( p_parent, p_item, NULL, NULL ); +} + +/* */ +input_thread_t *__input_CreateThreadExtended( vlc_object_t *p_parent, + input_item_t *p_item, + const char *psz_log, sout_instance_t *p_sout ) +{ + input_thread_t *p_input; + + p_input = Create( p_parent, p_item, psz_log, false, p_sout ); + if( !p_input ) + return NULL; + + /* Create thread and wait for its readiness. */ + if( vlc_thread_create( p_input, "input", Run, + VLC_THREAD_PRIORITY_INPUT, true ) ) + { + input_ChangeState( p_input, ERROR_S ); + msg_Err( p_input, "cannot create input thread" ); + vlc_object_detach( p_input ); + vlc_object_release( p_input ); + return NULL; + } + + return p_input; +} + +/** + * Initialize an input thread and run it. This thread will clean after itself, + * you can forget about it. It can work either in blocking or non-blocking mode + * + * \param p_parent a vlc_object + * \param p_item an input item + * \param b_block should we block until read is finished ? + * \return the input object id if non blocking, an error code else + */ +int __input_Read( vlc_object_t *p_parent, input_item_t *p_item, + bool b_block ) +{ + input_thread_t *p_input; + + p_input = Create( p_parent, p_item, NULL, false, NULL ); + if( !p_input ) + return VLC_EGENERIC; + + if( b_block ) + { + RunAndDestroy( VLC_OBJECT(p_input) ); + return VLC_SUCCESS; + } + else + { + if( vlc_thread_create( p_input, "input", RunAndDestroy, + VLC_THREAD_PRIORITY_INPUT, true ) ) + { + input_ChangeState( p_input, ERROR_S ); + msg_Err( p_input, "cannot create input thread" ); + vlc_object_release( p_input ); + return VLC_EGENERIC; + } + } + return p_input->i_object_id; +} + +/** + * Initialize an input and initialize it to preparse the item + * This function is blocking. It will only accept to parse files + * + * \param p_parent a vlc_object_t + * \param p_item an input item + * \return VLC_SUCCESS or an error + */ +int __input_Preparse( vlc_object_t *p_parent, input_item_t *p_item ) +{ + input_thread_t *p_input; + + /* Allocate descriptor */ + p_input = Create( p_parent, p_item, NULL, true, NULL ); + if( !p_input ) + return VLC_EGENERIC; + + if( !Init( p_input ) ) + End( p_input ); + + vlc_object_detach( p_input ); + vlc_object_release( p_input ); + + return VLC_SUCCESS; +} + +/** + * Request a running input thread to stop and die + * + * \param the input thread to stop + */ +static void ObjectKillChildrens( input_thread_t *p_input, vlc_object_t *p_obj ) +{ + vlc_list_t *p_list; + int i; + + if( p_obj->i_object_type == VLC_OBJECT_VOUT || + p_obj->i_object_type == VLC_OBJECT_AOUT || + p_obj == VLC_OBJECT(p_input->p->p_sout) ) + return; + + vlc_object_kill( p_obj ); + + p_list = vlc_list_children( p_obj ); + for( i = 0; i < p_list->i_count; i++ ) + ObjectKillChildrens( p_input, p_list->p_values[i].p_object ); + vlc_list_release( p_list ); +} +void input_StopThread( input_thread_t *p_input ) +{ + /* Set die for input and ALL of this childrens (even (grand-)grand-childrens) + * It is needed here even if it is done in INPUT_CONTROL_SET_DIE handler to + * unlock the control loop */ + ObjectKillChildrens( p_input, VLC_OBJECT(p_input) ); + + input_ControlPush( p_input, INPUT_CONTROL_SET_DIE, NULL ); +} + +sout_instance_t * input_DetachSout( input_thread_t *p_input ) +{ + sout_instance_t *p_sout = p_input->p->p_sout; + vlc_object_detach( p_sout ); + p_input->p->p_sout = NULL; + return p_sout; +} + +/***************************************************************************** + * Run: main thread loop + * This is the "normal" thread that spawns the input processing chain, + * reads the stream, cleans up and waits + *****************************************************************************/ +static void* Run( vlc_object_t *p_this ) +{ + input_thread_t *p_input = (input_thread_t *)p_this; + /* Signal that the thread is launched */ + vlc_thread_ready( p_input ); + + if( Init( p_input ) ) + { + /* If we failed, wait before we are killed, and exit */ + p_input->b_error = true; + + WaitDie( p_input ); + + /* Tell we're dead */ + p_input->b_dead = true; + + return NULL; + } + + MainLoop( p_input ); + + if( !p_input->b_eof && !p_input->b_error && p_input->p->input.b_eof ) + { + /* We have finish to demux data but not to play them */ + while( !p_input->b_die ) + { + if( input_EsOutDecodersEmpty( p_input->p->p_es_out ) ) + break; + + msg_Dbg( p_input, "waiting decoder fifos to empty" ); + + msleep( INPUT_IDLE_SLEEP ); + } + + /* We have finished */ + p_input->b_eof = true; + input_ChangeState( p_input, END_S ); + } + + /* Wait until we are asked to die */ + if( !p_input->b_die ) + { + WaitDie( p_input ); + } + + /* Clean up */ + End( p_input ); + + return NULL; +} + +/***************************************************************************** + * RunAndDestroy: main thread loop + * This is the "just forget me" thread that spawns the input processing chain, + * reads the stream, cleans up and releases memory + *****************************************************************************/ +static void* RunAndDestroy( vlc_object_t *p_this ) +{ + input_thread_t *p_input = (input_thread_t *)p_this; + /* Signal that the thread is launched */ + vlc_thread_ready( p_input ); + + if( Init( p_input ) ) + goto exit; + + MainLoop( p_input ); + + if( !p_input->b_eof && !p_input->b_error && p_input->p->input.b_eof ) + { + /* We have finished demuxing data but not playing it */ + while( !p_input->b_die ) + { + if( input_EsOutDecodersEmpty( p_input->p->p_es_out ) ) + break; + + msg_Dbg( p_input, "waiting decoder fifos to empty" ); + + msleep( INPUT_IDLE_SLEEP ); + } + + /* We have finished */ + p_input->b_eof = true; + } + + /* Clean up */ + End( p_input ); + +exit: + /* Release memory */ + vlc_object_release( p_input ); + return 0; +} + +/***************************************************************************** + * Main loop: Fill buffers from access, and demux + *****************************************************************************/ +static void MainLoop( input_thread_t *p_input ) +{ + int64_t i_start_mdate = mdate(); + int64_t i_intf_update = 0; + int i_updates = 0; + + /* Stop the timer */ + stats_TimerStop( p_input, STATS_TIMER_INPUT_LAUNCHING ); + + while( !p_input->b_die && !p_input->b_error && !p_input->p->input.b_eof ) + { + bool b_force_update = false; + int i_ret; + int i_type; + vlc_value_t val; + + /* Do the read */ + if( p_input->i_state != PAUSE_S ) + { + if( ( p_input->p->i_stop > 0 && p_input->i_time >= p_input->p->i_stop ) || + ( p_input->p->i_run > 0 && i_start_mdate+p_input->p->i_run < mdate() ) ) + i_ret = 0; /* EOF */ + else + i_ret = p_input->p->input.p_demux->pf_demux(p_input->p->input.p_demux); + + if( i_ret > 0 ) + { + /* TODO */ + if( p_input->p->input.b_title_demux && + p_input->p->input.p_demux->info.i_update ) + { + i_ret = UpdateFromDemux( p_input ); + b_force_update = true; + } + else if( !p_input->p->input.b_title_demux && + p_input->p->input.p_access && + p_input->p->input.p_access->info.i_update ) + { + i_ret = UpdateFromAccess( p_input ); + b_force_update = true; + } + } + + if( i_ret == 0 ) /* EOF */ + { + vlc_value_t repeat; + + var_Get( p_input, "input-repeat", &repeat ); + if( repeat.i_int == 0 ) + { + /* End of file - we do not set b_die because only the + * playlist is allowed to do so. */ + msg_Dbg( p_input, "EOF reached" ); + p_input->p->input.b_eof = true; + } + else + { + msg_Dbg( p_input, "repeating the same input (%d)", + repeat.i_int ); + if( repeat.i_int > 0 ) + { + repeat.i_int--; + var_Set( p_input, "input-repeat", repeat ); + } + + /* Seek to start title/seekpoint */ + val.i_int = p_input->p->input.i_title_start - + p_input->p->input.i_title_offset; + if( val.i_int < 0 || val.i_int >= p_input->p->input.i_title ) + val.i_int = 0; + input_ControlPush( p_input, + INPUT_CONTROL_SET_TITLE, &val ); + + val.i_int = p_input->p->input.i_seekpoint_start - + p_input->p->input.i_seekpoint_offset; + if( val.i_int > 0 /* TODO: check upper boundary */ ) + input_ControlPush( p_input, + INPUT_CONTROL_SET_SEEKPOINT, &val ); + + /* Seek to start position */ + if( p_input->p->i_start > 0 ) + { + val.i_time = p_input->p->i_start; + input_ControlPush( p_input, INPUT_CONTROL_SET_TIME, + &val ); + } + else + { + val.f_float = 0.0; + input_ControlPush( p_input, INPUT_CONTROL_SET_POSITION, + &val ); + } + + /* */ + i_start_mdate = mdate(); + } + } + else if( i_ret < 0 ) + { + p_input->b_error = true; + } + + if( i_ret > 0 && p_input->p->i_slave > 0 ) + { + SlaveDemux( p_input ); + } + } + else + { + /* Small wait */ + msleep( 10*1000 ); + } + + /* Handle control */ + vlc_mutex_lock( &p_input->p->lock_control ); + ControlReduce( p_input ); + while( !ControlPopNoLock( p_input, &i_type, &val ) ) + { + msg_Dbg( p_input, "control type=%d", i_type ); + if( Control( p_input, i_type, val ) ) + b_force_update = true; + } + vlc_mutex_unlock( &p_input->p->lock_control ); + + if( b_force_update || i_intf_update < mdate() ) + { + vlc_value_t val; + double f_pos; + int64_t i_time, i_length; + /* update input status variables */ + if( !demux_Control( p_input->p->input.p_demux, + DEMUX_GET_POSITION, &f_pos ) ) + { + val.f_float = (float)f_pos; + var_Change( p_input, "position", VLC_VAR_SETVALUE, &val, NULL ); + } + if( !demux_Control( p_input->p->input.p_demux, + DEMUX_GET_TIME, &i_time ) ) + { + p_input->i_time = i_time; + val.i_time = i_time; + var_Change( p_input, "time", VLC_VAR_SETVALUE, &val, NULL ); + } + if( !demux_Control( p_input->p->input.p_demux, + DEMUX_GET_LENGTH, &i_length ) ) + { + vlc_value_t old_val; + var_Get( p_input, "length", &old_val ); + val.i_time = i_length; + var_Change( p_input, "length", VLC_VAR_SETVALUE, &val, NULL ); + + if( old_val.i_time != val.i_time ) + { + UpdateItemLength( p_input, i_length ); + } + } + + var_SetBool( p_input, "intf-change", true ); + i_intf_update = mdate() + INT64_C(150000); + } + /* 150ms * 8 = ~ 1 second */ + if( ++i_updates % 8 == 0 ) + { + stats_ComputeInputStats( p_input, p_input->p->input.p_item->p_stats ); + /* Are we the thread responsible for computing global stats ? */ + if( libvlc_priv (p_input->p_libvlc)->p_stats_computer == p_input ) + { + stats_ComputeGlobalStats( p_input->p_libvlc, + p_input->p_libvlc->p_stats ); + } + } + } +} + +static void InitStatistics( input_thread_t * p_input ) +{ + if( p_input->b_preparsing ) return; + + /* Prepare statistics */ +#define INIT_COUNTER( c, type, compute ) p_input->p->counters.p_##c = \ + stats_CounterCreate( p_input, VLC_VAR_##type, STATS_##compute); + if( libvlc_stats (p_input) ) + { + INIT_COUNTER( read_bytes, INTEGER, COUNTER ); + INIT_COUNTER( read_packets, INTEGER, COUNTER ); + INIT_COUNTER( demux_read, INTEGER, COUNTER ); + INIT_COUNTER( input_bitrate, FLOAT, DERIVATIVE ); + INIT_COUNTER( demux_bitrate, FLOAT, DERIVATIVE ); + INIT_COUNTER( played_abuffers, INTEGER, COUNTER ); + INIT_COUNTER( lost_abuffers, INTEGER, COUNTER ); + INIT_COUNTER( displayed_pictures, INTEGER, COUNTER ); + INIT_COUNTER( lost_pictures, INTEGER, COUNTER ); + INIT_COUNTER( decoded_audio, INTEGER, COUNTER ); + INIT_COUNTER( decoded_video, INTEGER, COUNTER ); + INIT_COUNTER( decoded_sub, INTEGER, COUNTER ); + p_input->p->counters.p_sout_send_bitrate = NULL; + p_input->p->counters.p_sout_sent_packets = NULL; + p_input->p->counters.p_sout_sent_bytes = NULL; + if( p_input->p->counters.p_demux_bitrate ) + p_input->p->counters.p_demux_bitrate->update_interval = 1000000; + if( p_input->p->counters.p_input_bitrate ) + p_input->p->counters.p_input_bitrate->update_interval = 1000000; + } +} + +#ifdef ENABLE_SOUT +static int InitSout( input_thread_t * p_input ) +{ + char *psz; + + if( p_input->b_preparsing ) return VLC_SUCCESS; + + /* Find a usable sout and attach it to p_input */ + psz = var_GetNonEmptyString( p_input, "sout" ); + if( psz && strncasecmp( p_input->p->input.p_item->psz_uri, "vlc:", 4 ) ) + { + /* Check the validity of the provided sout */ + if( p_input->p->p_sout ) + { + if( strcmp( p_input->p->p_sout->psz_sout, psz ) ) + { + msg_Dbg( p_input, "destroying unusable sout" ); + + sout_DeleteInstance( p_input->p->p_sout ); + p_input->p->p_sout = NULL; + } + } + + if( p_input->p->p_sout ) + { + /* Reuse it */ + msg_Dbg( p_input, "sout keep: reusing sout" ); + msg_Dbg( p_input, "sout keep: you probably want to use " + "gather stream_out" ); + vlc_object_attach( p_input->p->p_sout, p_input ); + } + else + { + /* Create a new one */ + p_input->p->p_sout = sout_NewInstance( p_input, psz ); + if( !p_input->p->p_sout ) + { + input_ChangeState( p_input, ERROR_S ); + msg_Err( p_input, "cannot start stream output instance, " \ + "aborting" ); + free( psz ); + return VLC_EGENERIC; + } + } + if( libvlc_stats (p_input) ) + { + INIT_COUNTER( sout_sent_packets, INTEGER, COUNTER ); + INIT_COUNTER (sout_sent_bytes, INTEGER, COUNTER ); + INIT_COUNTER( sout_send_bitrate, FLOAT, DERIVATIVE ); + if( p_input->p->counters.p_sout_send_bitrate ) + p_input->p->counters.p_sout_send_bitrate->update_interval = + 1000000; + } + } + else if( p_input->p->p_sout ) + { + msg_Dbg( p_input, "destroying useless sout" ); + + sout_DeleteInstance( p_input->p->p_sout ); + p_input->p->p_sout = NULL; + } + free( psz ); + + return VLC_SUCCESS; +} +#endif + +static void InitTitle( input_thread_t * p_input ) +{ + vlc_value_t val; + + if( p_input->b_preparsing ) return; + + /* Create global title (from master) */ + p_input->p->i_title = p_input->p->input.i_title; + p_input->p->title = p_input->p->input.title; + p_input->p->i_title_offset = p_input->p->input.i_title_offset; + p_input->p->i_seekpoint_offset = p_input->p->input.i_seekpoint_offset; + if( p_input->p->i_title > 0 ) + { + /* Setup variables */ + input_ControlVarNavigation( p_input ); + input_ControlVarTitle( p_input, 0 ); + } + + /* Global flag */ + p_input->b_can_pace_control = p_input->p->input.b_can_pace_control; + p_input->p->b_can_pause = p_input->p->input.b_can_pause; + p_input->p->b_can_rate_control = p_input->p->input.b_can_rate_control; + + /* Fix pts delay */ + if( p_input->i_pts_delay < 0 ) + p_input->i_pts_delay = 0; + + /* If the desynchronisation requested by the user is < 0, we need to + * cache more data. */ + var_Get( p_input, "audio-desync", &val ); + if( val.i_int < 0 ) p_input->i_pts_delay -= (val.i_int * 1000); + + /* Update cr_average depending on the caching */ + p_input->p->input.i_cr_average *= (10 * p_input->i_pts_delay / 200000); + p_input->p->input.i_cr_average /= 10; + if( p_input->p->input.i_cr_average < 10 ) p_input->p->input.i_cr_average = 10; +} + +static void StartTitle( input_thread_t * p_input ) +{ + double f_fps; + vlc_value_t val; + int i, i_delay; + char *psz; + char *psz_subtitle; + int64_t i_length; + + /* Start title/chapter */ + + if( p_input->b_preparsing ) + { + p_input->p->i_start = 0; + return; + } + + val.i_int = p_input->p->input.i_title_start - + p_input->p->input.i_title_offset; + if( val.i_int > 0 && val.i_int < p_input->p->input.i_title ) + input_ControlPush( p_input, INPUT_CONTROL_SET_TITLE, &val ); + val.i_int = p_input->p->input.i_seekpoint_start - + p_input->p->input.i_seekpoint_offset; + if( val.i_int > 0 /* TODO: check upper boundary */ ) + input_ControlPush( p_input, INPUT_CONTROL_SET_SEEKPOINT, &val ); + + /* Start time*/ + /* Set start time */ + p_input->p->i_start = INT64_C(1000000) * var_GetInteger( p_input, "start-time" ); + p_input->p->i_stop = INT64_C(1000000) * var_GetInteger( p_input, "stop-time" ); + p_input->p->i_run = INT64_C(1000000) * var_GetInteger( p_input, "run-time" ); + i_length = var_GetTime( p_input, "length" ); + if( p_input->p->i_run < 0 ) + { + msg_Warn( p_input, "invalid run-time ignored" ); + p_input->p->i_run = 0; + } + + if( p_input->p->i_start > 0 ) + { + if( p_input->p->i_start >= i_length ) + { + msg_Warn( p_input, "invalid start-time ignored" ); + } + else + { + vlc_value_t s; + + msg_Dbg( p_input, "starting at time: %ds", + (int)( p_input->p->i_start / INT64_C(1000000) ) ); + + s.i_time = p_input->p->i_start; + input_ControlPush( p_input, INPUT_CONTROL_SET_TIME, &s ); + } + } + if( p_input->p->i_stop > 0 && p_input->p->i_stop <= p_input->p->i_start ) + { + msg_Warn( p_input, "invalid stop-time ignored" ); + p_input->p->i_stop = 0; + } + + /* Load subtitles */ + /* Get fps and set it if not already set */ + if( !demux_Control( p_input->p->input.p_demux, DEMUX_GET_FPS, &f_fps ) && + f_fps > 1.0 ) + { + float f_requested_fps; + + var_Create( p_input, "sub-original-fps", VLC_VAR_FLOAT ); + var_SetFloat( p_input, "sub-original-fps", f_fps ); + + f_requested_fps = var_CreateGetFloat( p_input, "sub-fps" ); + if( f_requested_fps != f_fps ) + { + var_Create( p_input, "sub-fps", VLC_VAR_FLOAT| + VLC_VAR_DOINHERIT ); + var_SetFloat( p_input, "sub-fps", f_requested_fps ); + } + } + + i_delay = var_CreateGetInteger( p_input, "sub-delay" ); + if( i_delay != 0 ) + { + var_SetTime( p_input, "spu-delay", (mtime_t)i_delay * 100000 ); + } + + /* Look for and add subtitle files */ + psz_subtitle = var_GetNonEmptyString( p_input, "sub-file" ); + if( psz_subtitle != NULL ) + { + msg_Dbg( p_input, "forced subtitle: %s", psz_subtitle ); + input_AddSubtitles( p_input, psz_subtitle, false ); + } + + var_Get( p_input, "sub-autodetect-file", &val ); + if( val.b_bool ) + { + char *psz_autopath = var_GetNonEmptyString( p_input, "sub-autodetect-path" ); + char **subs = subtitles_Detect( p_input, psz_autopath, + p_input->p->input.p_item->psz_uri ); + input_source_t *sub; + i = 0; + if( psz_autopath == NULL ) + psz_autopath = strdup(""); + + /* Try to autoselect the first autodetected subtitles file + * if no subtitles file was specified */ + if( ( psz_subtitle == NULL ) && subs && subs[0] ) + { + input_AddSubtitles( p_input, subs[0], false ); + free( subs[0] ); + i = 1; + } + + /* Then, just add the following subtitles files */ + for( ; subs && subs[i]; i++ ) + { + if( !psz_subtitle || strcmp( psz_subtitle, subs[i] ) ) + { + sub = InputSourceNew( p_input ); + if( !InputSourceInit( p_input, sub, subs[i], "subtitle" ) ) + { + TAB_APPEND( p_input->p->i_slave, p_input->p->slave, sub ); + } + else free( sub ); + } + free( subs[i] ); + } + free( subs ); + free( psz_autopath ); + } + free( psz_subtitle ); + + /* Look for slave */ + psz = var_GetNonEmptyString( p_input, "input-slave" ); + if( psz != NULL ) + { + char *psz_delim; + input_source_t *slave; + while( psz && *psz ) + { + while( *psz == ' ' || *psz == '#' ) + { + psz++; + } + if( ( psz_delim = strchr( psz, '#' ) ) ) + { + *psz_delim++ = '\0'; + } + if( *psz == 0 ) + { + break; + } + + msg_Dbg( p_input, "adding slave input '%s'", psz ); + slave = InputSourceNew( p_input ); + if( !InputSourceInit( p_input, slave, psz, NULL ) ) + { + TAB_APPEND( p_input->p->i_slave, p_input->p->slave, slave ); + } + else free( slave ); + psz = psz_delim; + } + free( psz ); + } +} + +static void InitPrograms( input_thread_t * p_input ) +{ + int i_es_out_mode; + vlc_value_t val; + + if( p_input->b_preparsing ) return; + + /* Set up es_out */ + es_out_Control( p_input->p->p_es_out, ES_OUT_SET_ACTIVE, true ); + i_es_out_mode = ES_OUT_MODE_AUTO; + val.p_list = NULL; + if( p_input->p->p_sout ) + { + var_Get( p_input, "sout-all", &val ); + if ( val.b_bool ) + { + i_es_out_mode = ES_OUT_MODE_ALL; + val.p_list = NULL; + } + else + { + var_Get( p_input, "programs", &val ); + if ( val.p_list && val.p_list->i_count ) + { + i_es_out_mode = ES_OUT_MODE_PARTIAL; + /* Note : we should remove the "program" callback. */ + } + else + var_Change( p_input, "programs", VLC_VAR_FREELIST, &val, + NULL ); + } + } + es_out_Control( p_input->p->p_es_out, ES_OUT_SET_MODE, i_es_out_mode ); + + /* Inform the demuxer about waited group (needed only for DVB) */ + if( i_es_out_mode == ES_OUT_MODE_ALL ) + { + demux_Control( p_input->p->input.p_demux, DEMUX_SET_GROUP, -1, NULL ); + } + else if( i_es_out_mode == ES_OUT_MODE_PARTIAL ) + { + demux_Control( p_input->p->input.p_demux, DEMUX_SET_GROUP, -1, + val.p_list ); + } + else + { + demux_Control( p_input->p->input.p_demux, DEMUX_SET_GROUP, + (int) var_GetInteger( p_input, "program" ), NULL ); + } +} + +static int Init( input_thread_t * p_input ) +{ + vlc_meta_t *p_meta; + vlc_value_t val; + int i, ret; + + for( i = 0; i < p_input->p->input.p_item->i_options; i++ ) + { + if( !strncmp( p_input->p->input.p_item->ppsz_options[i], "meta-file", 9 ) ) + { + msg_Dbg( p_input, "Input is a meta file: disabling unneeded options" ); + var_SetString( p_input, "sout", "" ); + var_SetBool( p_input, "sout-all", false ); + var_SetString( p_input, "input-slave", "" ); + var_SetInteger( p_input, "input-repeat", 0 ); + var_SetString( p_input, "sub-file", "" ); + var_SetBool( p_input, "sub-autodetect-file", false ); + } + } + + InitStatistics( p_input ); +#ifdef ENABLE_SOUT + ret = InitSout( p_input ); + if( ret != VLC_SUCCESS ) + return ret; /* FIXME: goto error; should be better here */ +#endif + + /* Create es out */ + p_input->p->p_es_out = input_EsOutNew( p_input, p_input->p->i_rate ); + es_out_Control( p_input->p->p_es_out, ES_OUT_SET_ACTIVE, false ); + es_out_Control( p_input->p->p_es_out, ES_OUT_SET_MODE, ES_OUT_MODE_NONE ); + + var_Create( p_input, "bit-rate", VLC_VAR_INTEGER ); + var_Create( p_input, "sample-rate", VLC_VAR_INTEGER ); + + if( InputSourceInit( p_input, &p_input->p->input, + p_input->p->input.p_item->psz_uri, NULL ) ) + { + goto error; + } + + InitTitle( p_input ); + + /* Load master infos */ + /* Init length */ + if( !demux_Control( p_input->p->input.p_demux, DEMUX_GET_LENGTH, + &val.i_time ) && val.i_time > 0 ) + { + var_Change( p_input, "length", VLC_VAR_SETVALUE, &val, NULL ); + UpdateItemLength( p_input, val.i_time ); + } + else + { + val.i_time = input_item_GetDuration( p_input->p->input.p_item ); + if( val.i_time > 0 ) + { /* fallback: gets length from metadata */ + var_Change( p_input, "length", VLC_VAR_SETVALUE, &val, NULL ); + UpdateItemLength( p_input, val.i_time ); + } + } + + StartTitle( p_input ); + + InitPrograms( p_input ); + + if( !p_input->b_preparsing && p_input->p->p_sout ) + { + p_input->p->b_out_pace_control = (p_input->p->p_sout->i_out_pace_nocontrol > 0); + + if( p_input->b_can_pace_control && p_input->p->b_out_pace_control ) + { + /* We don't want a high input priority here or we'll + * end-up sucking up all the CPU time */ + vlc_thread_set_priority( p_input, VLC_THREAD_PRIORITY_LOW ); + } + + msg_Dbg( p_input, "starting in %s mode", + p_input->p->b_out_pace_control ? "async" : "sync" ); + } + + p_meta = vlc_meta_New(); + + /* Get meta data from users */ + InputMetaUser( p_input, p_meta ); + + /* Get meta data from master input */ + DemuxMeta( p_input, p_meta, p_input->p->input.p_demux ); + + /* Access_file does not give any meta, and there are no slave */ + AccessMeta( p_input, p_meta ); + + InputUpdateMeta( p_input, p_meta ); + + if( !p_input->b_preparsing ) + { + msg_Dbg( p_input, "`%s' successfully opened", + p_input->p->input.p_item->psz_uri ); + + } + + /* initialization is complete */ + input_ChangeState( p_input, PLAYING_S ); + + return VLC_SUCCESS; + +error: + input_ChangeState( p_input, ERROR_S ); + + if( p_input->p->p_es_out ) + input_EsOutDelete( p_input->p->p_es_out ); +#ifdef ENABLE_SOUT + if( p_input->p->p_sout ) + { + vlc_object_detach( p_input->p->p_sout ); + sout_DeleteInstance( p_input->p->p_sout ); + } +#endif + + if( !p_input->b_preparsing && libvlc_stats (p_input) ) + { +#define EXIT_COUNTER( c ) do { if( p_input->p->counters.p_##c ) \ + stats_CounterClean( p_input->p->counters.p_##c );\ + p_input->p->counters.p_##c = NULL; } while(0) + EXIT_COUNTER( read_bytes ); + EXIT_COUNTER( read_packets ); + EXIT_COUNTER( demux_read ); + EXIT_COUNTER( input_bitrate ); + EXIT_COUNTER( demux_bitrate ); + EXIT_COUNTER( played_abuffers ); + EXIT_COUNTER( lost_abuffers ); + EXIT_COUNTER( displayed_pictures ); + EXIT_COUNTER( lost_pictures ); + EXIT_COUNTER( decoded_audio ); + EXIT_COUNTER( decoded_video ); + EXIT_COUNTER( decoded_sub ); + + if( p_input->p->p_sout ) + { + EXIT_COUNTER( sout_sent_packets ); + EXIT_COUNTER (sout_sent_bytes ); + EXIT_COUNTER( sout_send_bitrate ); + } +#undef EXIT_COUNTER + } + + /* Mark them deleted */ + p_input->p->input.p_demux = NULL; + p_input->p->input.p_stream = NULL; + p_input->p->input.p_access = NULL; + p_input->p->p_es_out = NULL; + p_input->p->p_sout = NULL; + + return VLC_EGENERIC; +} + +/***************************************************************************** + * WaitDie: Wait until we are asked to die. + ***************************************************************************** + * This function is called when an error occurred during thread main's loop. + *****************************************************************************/ +static void WaitDie( input_thread_t *p_input ) +{ + input_ChangeState( p_input, p_input->b_error ? ERROR_S : END_S ); + while( !p_input->b_die ) + { + /* Sleep a while */ + msleep( INPUT_IDLE_SLEEP ); + } +} + +/***************************************************************************** + * End: end the input thread + *****************************************************************************/ +static void End( input_thread_t * p_input ) +{ + int i; + + /* We are at the end */ + input_ChangeState( p_input, END_S ); + + /* Clean control variables */ + input_ControlVarStop( p_input ); + + /* Clean up master */ + InputSourceClean( &p_input->p->input ); + + /* Delete slave */ + for( i = 0; i < p_input->p->i_slave; i++ ) + { + InputSourceClean( p_input->p->slave[i] ); + free( p_input->p->slave[i] ); + } + free( p_input->p->slave ); + + /* Unload all modules */ + if( p_input->p->p_es_out ) + input_EsOutDelete( p_input->p->p_es_out ); + + if( !p_input->b_preparsing ) + { +#define CL_CO( c ) stats_CounterClean( p_input->p->counters.p_##c ); p_input->p->counters.p_##c = NULL; + if( libvlc_stats (p_input) ) + { + libvlc_priv_t *priv = libvlc_priv (p_input->p_libvlc); + + /* make sure we are up to date */ + stats_ComputeInputStats( p_input, p_input->p->input.p_item->p_stats ); + if( priv->p_stats_computer == p_input ) + { + stats_ComputeGlobalStats( p_input->p_libvlc, + p_input->p_libvlc->p_stats ); + priv->p_stats_computer = NULL; + } + CL_CO( read_bytes ); + CL_CO( read_packets ); + CL_CO( demux_read ); + CL_CO( input_bitrate ); + CL_CO( demux_bitrate ); + CL_CO( played_abuffers ); + CL_CO( lost_abuffers ); + CL_CO( displayed_pictures ); + CL_CO( lost_pictures ); + CL_CO( decoded_audio) ; + CL_CO( decoded_video ); + CL_CO( decoded_sub) ; + } + + /* Close optional stream output instance */ + if( p_input->p->p_sout ) + { + CL_CO( sout_sent_packets ); + CL_CO( sout_sent_bytes ); + CL_CO( sout_send_bitrate ); + + vlc_object_detach( p_input->p->p_sout ); + } +#undef CL_CO + } + + if( p_input->p->i_attachment > 0 ) + { + for( i = 0; i < p_input->p->i_attachment; i++ ) + vlc_input_attachment_Delete( p_input->p->attachment[i] ); + TAB_CLEAN( p_input->p->i_attachment, p_input->p->attachment ); + } + + /* Tell we're dead */ + p_input->b_dead = true; +} + +/***************************************************************************** + * Control + *****************************************************************************/ +static inline int ControlPopNoLock( input_thread_t *p_input, + int *pi_type, vlc_value_t *p_val ) +{ + if( p_input->p->i_control <= 0 ) + { + return VLC_EGENERIC; + } + + *pi_type = p_input->p->control[0].i_type; + *p_val = p_input->p->control[0].val; + + p_input->p->i_control--; + if( p_input->p->i_control > 0 ) + { + int i; + + for( i = 0; i < p_input->p->i_control; i++ ) + { + p_input->p->control[i].i_type = p_input->p->control[i+1].i_type; + p_input->p->control[i].val = p_input->p->control[i+1].val; + } + } + + return VLC_SUCCESS; +} + +static void ControlReduce( input_thread_t *p_input ) +{ + int i; + + if( !p_input ) + return; + + for( i = 1; i < p_input->p->i_control; i++ ) + { + const int i_lt = p_input->p->control[i-1].i_type; + const int i_ct = p_input->p->control[i].i_type; + + /* XXX We can't merge INPUT_CONTROL_SET_ES */ +/* msg_Dbg( p_input, "[%d/%d] l=%d c=%d", i, p_input->p->i_control, + i_lt, i_ct ); +*/ + if( i_lt == i_ct && + ( i_ct == INPUT_CONTROL_SET_STATE || + i_ct == INPUT_CONTROL_SET_RATE || + i_ct == INPUT_CONTROL_SET_POSITION || + i_ct == INPUT_CONTROL_SET_TIME || + i_ct == INPUT_CONTROL_SET_PROGRAM || + i_ct == INPUT_CONTROL_SET_TITLE || + i_ct == INPUT_CONTROL_SET_SEEKPOINT || + i_ct == INPUT_CONTROL_SET_BOOKMARK ) ) + { + int j; +// msg_Dbg( p_input, "merged at %d", i ); + /* Remove the i-1 */ + for( j = i; j < p_input->p->i_control; j++ ) + p_input->p->control[j-1] = p_input->p->control[j]; + p_input->p->i_control--; + } + else + { + /* TODO but that's not that important + - merge SET_X with SET_X_CMD + - remove SET_SEEKPOINT/SET_POSITION/SET_TIME before a SET_TITLE + - remove SET_SEEKPOINT/SET_POSITION/SET_TIME before another among them + - ? + */ + } + } +} + +static bool Control( input_thread_t *p_input, int i_type, + vlc_value_t val ) +{ + bool b_force_update = false; + + if( !p_input ) return b_force_update; + + switch( i_type ) + { + case INPUT_CONTROL_SET_DIE: + msg_Dbg( p_input, "control: stopping input" ); + + /* Mark all submodules to die */ + ObjectKillChildrens( p_input, VLC_OBJECT(p_input) ); + break; + + case INPUT_CONTROL_SET_POSITION: + case INPUT_CONTROL_SET_POSITION_OFFSET: + { + double f_pos; + if( i_type == INPUT_CONTROL_SET_POSITION ) + { + f_pos = val.f_float; + } + else + { + /* Should not fail */ + demux_Control( p_input->p->input.p_demux, + DEMUX_GET_POSITION, &f_pos ); + f_pos += val.f_float; + } + if( f_pos < 0.0 ) f_pos = 0.0; + if( f_pos > 1.0 ) f_pos = 1.0; + /* Reset the decoders states and clock sync (before calling the demuxer */ + input_EsOutChangePosition( p_input->p->p_es_out ); + if( demux_Control( p_input->p->input.p_demux, DEMUX_SET_POSITION, + f_pos ) ) + { + msg_Err( p_input, "INPUT_CONTROL_SET_POSITION(_OFFSET) " + "%2.1f%% failed", f_pos * 100 ); + } + else + { + if( p_input->p->i_slave > 0 ) + SlaveSeek( p_input ); + + b_force_update = true; + } + break; + } + + case INPUT_CONTROL_SET_TIME: + case INPUT_CONTROL_SET_TIME_OFFSET: + { + int64_t i_time; + int i_ret; + + if( i_type == INPUT_CONTROL_SET_TIME ) + { + i_time = val.i_time; + } + else + { + /* Should not fail */ + demux_Control( p_input->p->input.p_demux, + DEMUX_GET_TIME, &i_time ); + i_time += val.i_time; + } + if( i_time < 0 ) i_time = 0; + + /* Reset the decoders states and clock sync (before calling the demuxer */ + input_EsOutChangePosition( p_input->p->p_es_out ); + + i_ret = demux_Control( p_input->p->input.p_demux, + DEMUX_SET_TIME, i_time ); + if( i_ret ) + { + int64_t i_length; + + /* Emulate it with a SET_POS */ + demux_Control( p_input->p->input.p_demux, + DEMUX_GET_LENGTH, &i_length ); + if( i_length > 0 ) + { + double f_pos = (double)i_time / (double)i_length; + i_ret = demux_Control( p_input->p->input.p_demux, + DEMUX_SET_POSITION, f_pos ); + } + } + if( i_ret ) + { + msg_Warn( p_input, "INPUT_CONTROL_SET_TIME(_OFFSET) %"PRId64 + " failed or not possible", i_time ); + } + else + { + if( p_input->p->i_slave > 0 ) + SlaveSeek( p_input ); + + b_force_update = true; + } + break; + } + + case INPUT_CONTROL_SET_STATE: + if( ( val.i_int == PLAYING_S && p_input->i_state == PAUSE_S ) || + ( val.i_int == PAUSE_S && p_input->i_state == PAUSE_S ) ) + { + int i_ret; + if( p_input->p->input.p_access ) + i_ret = access_Control( p_input->p->input.p_access, + ACCESS_SET_PAUSE_STATE, false ); + else + i_ret = demux_Control( p_input->p->input.p_demux, + DEMUX_SET_PAUSE_STATE, false ); + + if( i_ret ) + { + /* FIXME What to do ? */ + msg_Warn( p_input, "cannot unset pause -> EOF" ); + vlc_mutex_unlock( &p_input->p->lock_control ); + input_ControlPush( p_input, INPUT_CONTROL_SET_DIE, NULL ); + vlc_mutex_lock( &p_input->p->lock_control ); + } + + b_force_update = true; + + /* Switch to play */ + input_ChangeStateWithVarCallback( p_input, PLAYING_S, false ); + + /* */ + if( !i_ret ) + input_EsOutChangeState( p_input->p->p_es_out ); + } + else if( val.i_int == PAUSE_S && p_input->i_state == PLAYING_S && + p_input->p->b_can_pause ) + { + int i_ret, state; + if( p_input->p->input.p_access ) + i_ret = access_Control( p_input->p->input.p_access, + ACCESS_SET_PAUSE_STATE, true ); + else + i_ret = demux_Control( p_input->p->input.p_demux, + DEMUX_SET_PAUSE_STATE, true ); + + b_force_update = true; + + if( i_ret ) + { + msg_Warn( p_input, "cannot set pause state" ); + state = p_input->i_state; + } + else + { + state = PAUSE_S; + } + + /* Switch to new state */ + input_ChangeStateWithVarCallback( p_input, state, false ); + + /* */ + if( !i_ret ) + input_EsOutChangeState( p_input->p->p_es_out ); + } + else if( val.i_int == PAUSE_S && !p_input->p->b_can_pause ) + { + b_force_update = true; + + /* Correct "state" value */ + input_ChangeStateWithVarCallback( p_input, p_input->i_state, false ); + } + else if( val.i_int != PLAYING_S && val.i_int != PAUSE_S ) + { + msg_Err( p_input, "invalid state in INPUT_CONTROL_SET_STATE" ); + } + break; + + case INPUT_CONTROL_SET_RATE: + case INPUT_CONTROL_SET_RATE_SLOWER: + case INPUT_CONTROL_SET_RATE_FASTER: + { + int i_rate; + + if( i_type == INPUT_CONTROL_SET_RATE ) + { + i_rate = val.i_int; + } + else + { + static const int ppi_factor[][2] = { + {1,64}, {1,32}, {1,16}, {1,8}, {1,4}, {1,3}, {1,2}, {2,3}, + {1,1}, + {3,2}, {2,1}, {3,1}, {4,1}, {8,1}, {16,1}, {32,1}, {64,1}, + {0,0} + }; + int i_error; + int i_idx; + int i; + + i_error = INT_MAX; + i_idx = -1; + for( i = 0; ppi_factor[i][0] != 0; i++ ) + { + const int i_test_r = INPUT_RATE_DEFAULT * ppi_factor[i][0] / ppi_factor[i][1]; + const int i_test_e = abs(p_input->p->i_rate - i_test_r); + if( i_test_e < i_error ) + { + i_idx = i; + i_error = i_test_e; + } + } + assert( i_idx >= 0 && ppi_factor[i_idx][0] != 0 ); + + if( i_type == INPUT_CONTROL_SET_RATE_SLOWER ) + { + if( ppi_factor[i_idx+1][0] > 0 ) + i_rate = INPUT_RATE_DEFAULT * ppi_factor[i_idx+1][0] / ppi_factor[i_idx+1][1]; + else + i_rate = INPUT_RATE_MAX+1; + } + else + { + assert( i_type == INPUT_CONTROL_SET_RATE_FASTER ); + if( i_idx > 0 ) + i_rate = INPUT_RATE_DEFAULT * ppi_factor[i_idx-1][0] / ppi_factor[i_idx-1][1]; + else + i_rate = INPUT_RATE_MIN-1; + } + } + + if( i_rate < INPUT_RATE_MIN ) + { + msg_Dbg( p_input, "cannot set rate faster" ); + i_rate = INPUT_RATE_MIN; + } + else if( i_rate > INPUT_RATE_MAX ) + { + msg_Dbg( p_input, "cannot set rate slower" ); + i_rate = INPUT_RATE_MAX; + } + if( i_rate != INPUT_RATE_DEFAULT && + ( ( !p_input->b_can_pace_control && !p_input->p->b_can_rate_control ) || + ( p_input->p->p_sout && !p_input->p->b_out_pace_control ) ) ) + { + msg_Dbg( p_input, "cannot change rate" ); + i_rate = INPUT_RATE_DEFAULT; + } + if( i_rate != p_input->p->i_rate && + !p_input->b_can_pace_control && p_input->p->b_can_rate_control ) + { + int i_ret; + if( p_input->p->input.p_access ) + i_ret = VLC_EGENERIC; + else + i_ret = demux_Control( p_input->p->input.p_demux, + DEMUX_SET_RATE, &i_rate ); + if( i_ret ) + { + msg_Warn( p_input, "ACCESS/DEMUX_SET_RATE failed" ); + i_rate = p_input->p->i_rate; + } + } + + /* */ + if( i_rate != p_input->p->i_rate ) + { + val.i_int = i_rate; + var_Change( p_input, "rate", VLC_VAR_SETVALUE, &val, NULL ); + var_SetBool( p_input, "rate-change", true ); + + p_input->p->i_rate = i_rate; + + /* FIXME do we need a RESET_PCR when !p_input->p->input.b_rescale_ts ? */ + if( p_input->p->input.b_rescale_ts ) + input_EsOutChangeRate( p_input->p->p_es_out, i_rate ); + + b_force_update = true; + } + break; + } + + case INPUT_CONTROL_SET_PROGRAM: + /* No need to force update, es_out does it if needed */ + es_out_Control( p_input->p->p_es_out, + ES_OUT_SET_GROUP, val.i_int ); + + demux_Control( p_input->p->input.p_demux, DEMUX_SET_GROUP, val.i_int, + NULL ); + break; + + case INPUT_CONTROL_SET_ES: + /* No need to force update, es_out does it if needed */ + es_out_Control( p_input->p->p_es_out, ES_OUT_SET_ES, + input_EsOutGetFromID( p_input->p->p_es_out, + val.i_int ) ); + break; + + case INPUT_CONTROL_SET_AUDIO_DELAY: + input_EsOutSetDelay( p_input->p->p_es_out, + AUDIO_ES, val.i_time ); + var_Change( p_input, "audio-delay", VLC_VAR_SETVALUE, &val, NULL ); + break; + + case INPUT_CONTROL_SET_SPU_DELAY: + input_EsOutSetDelay( p_input->p->p_es_out, + SPU_ES, val.i_time ); + var_Change( p_input, "spu-delay", VLC_VAR_SETVALUE, &val, NULL ); + break; + + case INPUT_CONTROL_SET_TITLE: + case INPUT_CONTROL_SET_TITLE_NEXT: + case INPUT_CONTROL_SET_TITLE_PREV: + if( p_input->p->input.b_title_demux && + p_input->p->input.i_title > 0 ) + { + /* TODO */ + /* FIXME handle demux title */ + demux_t *p_demux = p_input->p->input.p_demux; + int i_title; + + if( i_type == INPUT_CONTROL_SET_TITLE_PREV ) + i_title = p_demux->info.i_title - 1; + else if( i_type == INPUT_CONTROL_SET_TITLE_NEXT ) + i_title = p_demux->info.i_title + 1; + else + i_title = val.i_int; + + if( i_title >= 0 && i_title < p_input->p->input.i_title ) + { + input_EsOutChangePosition( p_input->p->p_es_out ); + + demux_Control( p_demux, DEMUX_SET_TITLE, i_title ); + input_ControlVarTitle( p_input, i_title ); + } + } + else if( p_input->p->input.i_title > 0 ) + { + access_t *p_access = p_input->p->input.p_access; + int i_title; + + if( i_type == INPUT_CONTROL_SET_TITLE_PREV ) + i_title = p_access->info.i_title - 1; + else if( i_type == INPUT_CONTROL_SET_TITLE_NEXT ) + i_title = p_access->info.i_title + 1; + else + i_title = val.i_int; + + if( i_title >= 0 && i_title < p_input->p->input.i_title ) + { + input_EsOutChangePosition( p_input->p->p_es_out ); + + access_Control( p_access, ACCESS_SET_TITLE, i_title ); + stream_AccessReset( p_input->p->input.p_stream ); + } + } + break; + case INPUT_CONTROL_SET_SEEKPOINT: + case INPUT_CONTROL_SET_SEEKPOINT_NEXT: + case INPUT_CONTROL_SET_SEEKPOINT_PREV: + if( p_input->p->input.b_title_demux && + p_input->p->input.i_title > 0 ) + { + demux_t *p_demux = p_input->p->input.p_demux; + int i_seekpoint; + int64_t i_input_time; + int64_t i_seekpoint_time; + + if( i_type == INPUT_CONTROL_SET_SEEKPOINT_PREV ) + { + i_seekpoint = p_demux->info.i_seekpoint; + i_seekpoint_time = p_input->p->input.title[p_demux->info.i_title]->seekpoint[i_seekpoint]->i_time_offset; + if( i_seekpoint_time >= 0 && + !demux_Control( p_demux, + DEMUX_GET_TIME, &i_input_time ) ) + { + if ( i_input_time < i_seekpoint_time + 3000000 ) + i_seekpoint--; + } + else + i_seekpoint--; + } + else if( i_type == INPUT_CONTROL_SET_SEEKPOINT_NEXT ) + i_seekpoint = p_demux->info.i_seekpoint + 1; + else + i_seekpoint = val.i_int; + + if( i_seekpoint >= 0 && i_seekpoint < + p_input->p->input.title[p_demux->info.i_title]->i_seekpoint ) + { + + input_EsOutChangePosition( p_input->p->p_es_out ); + + demux_Control( p_demux, DEMUX_SET_SEEKPOINT, i_seekpoint ); + } + } + else if( p_input->p->input.i_title > 0 ) + { + demux_t *p_demux = p_input->p->input.p_demux; + access_t *p_access = p_input->p->input.p_access; + int i_seekpoint; + int64_t i_input_time; + int64_t i_seekpoint_time; + + if( i_type == INPUT_CONTROL_SET_SEEKPOINT_PREV ) + { + i_seekpoint = p_access->info.i_seekpoint; + i_seekpoint_time = p_input->p->input.title[p_access->info.i_title]->seekpoint[i_seekpoint]->i_time_offset; + if( i_seekpoint_time >= 0 && + demux_Control( p_demux, + DEMUX_GET_TIME, &i_input_time ) ) + { + if ( i_input_time < i_seekpoint_time + 3000000 ) + i_seekpoint--; + } + else + i_seekpoint--; + } + else if( i_type == INPUT_CONTROL_SET_SEEKPOINT_NEXT ) + i_seekpoint = p_access->info.i_seekpoint + 1; + else + i_seekpoint = val.i_int; + + if( i_seekpoint >= 0 && i_seekpoint < + p_input->p->input.title[p_access->info.i_title]->i_seekpoint ) + { + input_EsOutChangePosition( p_input->p->p_es_out ); + + access_Control( p_access, ACCESS_SET_SEEKPOINT, + i_seekpoint ); + stream_AccessReset( p_input->p->input.p_stream ); + } + } + break; + + case INPUT_CONTROL_ADD_SLAVE: + if( val.psz_string ) + { + input_source_t *slave = InputSourceNew( p_input ); + + if( !InputSourceInit( p_input, slave, val.psz_string, NULL ) ) + { + vlc_meta_t *p_meta; + int64_t i_time; + + /* Add the slave */ + msg_Dbg( p_input, "adding %s as slave on the fly", + val.psz_string ); + + /* Set position */ + if( demux_Control( p_input->p->input.p_demux, + DEMUX_GET_TIME, &i_time ) ) + { + msg_Err( p_input, "demux doesn't like DEMUX_GET_TIME" ); + InputSourceClean( slave ); + free( slave ); + break; + } + if( demux_Control( slave->p_demux, + DEMUX_SET_TIME, i_time ) ) + { + msg_Err( p_input, "seek failed for new slave" ); + InputSourceClean( slave ); + free( slave ); + break; + } + + /* Get meta (access and demux) */ + p_meta = vlc_meta_New(); + access_Control( slave->p_access, ACCESS_GET_META, + p_meta ); + demux_Control( slave->p_demux, DEMUX_GET_META, p_meta ); + InputUpdateMeta( p_input, p_meta ); + + TAB_APPEND( p_input->p->i_slave, p_input->p->slave, slave ); + } + else + { + free( slave ); + msg_Warn( p_input, "failed to add %s as slave", + val.psz_string ); + } + + free( val.psz_string ); + } + break; + + case INPUT_CONTROL_SET_BOOKMARK: + default: + msg_Err( p_input, "not yet implemented" ); + break; + } + + return b_force_update; +} + +/***************************************************************************** + * UpdateFromDemux: + *****************************************************************************/ +static int UpdateFromDemux( input_thread_t *p_input ) +{ + demux_t *p_demux = p_input->p->input.p_demux; + vlc_value_t v; + + if( p_demux->info.i_update & INPUT_UPDATE_TITLE ) + { + v.i_int = p_demux->info.i_title; + var_Change( p_input, "title", VLC_VAR_SETVALUE, &v, NULL ); + + input_ControlVarTitle( p_input, p_demux->info.i_title ); + + p_demux->info.i_update &= ~INPUT_UPDATE_TITLE; + } + if( p_demux->info.i_update & INPUT_UPDATE_SEEKPOINT ) + { + v.i_int = p_demux->info.i_seekpoint; + var_Change( p_input, "chapter", VLC_VAR_SETVALUE, &v, NULL); + + p_demux->info.i_update &= ~INPUT_UPDATE_SEEKPOINT; + } + p_demux->info.i_update &= ~INPUT_UPDATE_SIZE; + + /* Hmmm only works with master input */ + if( p_input->p->input.p_demux == p_demux ) + { + int i_title_end = p_input->p->input.i_title_end - + p_input->p->input.i_title_offset; + int i_seekpoint_end = p_input->p->input.i_seekpoint_end - + p_input->p->input.i_seekpoint_offset; + + if( i_title_end >= 0 && i_seekpoint_end >= 0 ) + { + if( p_demux->info.i_title > i_title_end || + ( p_demux->info.i_title == i_title_end && + p_demux->info.i_seekpoint > i_seekpoint_end ) ) return 0; + } + else if( i_seekpoint_end >=0 ) + { + if( p_demux->info.i_seekpoint > i_seekpoint_end ) return 0; + } + else if( i_title_end >= 0 ) + { + if( p_demux->info.i_title > i_title_end ) return 0; + } + } + + return 1; +} + +/***************************************************************************** + * UpdateFromAccess: + *****************************************************************************/ +static int UpdateFromAccess( input_thread_t *p_input ) +{ + access_t *p_access = p_input->p->input.p_access; + vlc_value_t v; + + if( p_access->info.i_update & INPUT_UPDATE_TITLE ) + { + v.i_int = p_access->info.i_title; + var_Change( p_input, "title", VLC_VAR_SETVALUE, &v, NULL ); + + input_ControlVarTitle( p_input, p_access->info.i_title ); + + stream_AccessUpdate( p_input->p->input.p_stream ); + + p_access->info.i_update &= ~INPUT_UPDATE_TITLE; + } + if( p_access->info.i_update & INPUT_UPDATE_SEEKPOINT ) + { + v.i_int = p_access->info.i_seekpoint; + var_Change( p_input, "chapter", VLC_VAR_SETVALUE, &v, NULL); + p_access->info.i_update &= ~INPUT_UPDATE_SEEKPOINT; + } + if( p_access->info.i_update & INPUT_UPDATE_META ) + { + /* TODO maybe multi - access ? */ + vlc_meta_t *p_meta = vlc_meta_New(); + access_Control( p_input->p->input.p_access,ACCESS_GET_META, p_meta ); + InputUpdateMeta( p_input, p_meta ); + p_access->info.i_update &= ~INPUT_UPDATE_META; + } + + p_access->info.i_update &= ~INPUT_UPDATE_SIZE; + + /* Hmmm only works with master input */ + if( p_input->p->input.p_access == p_access ) + { + int i_title_end = p_input->p->input.i_title_end - + p_input->p->input.i_title_offset; + int i_seekpoint_end = p_input->p->input.i_seekpoint_end - + p_input->p->input.i_seekpoint_offset; + + if( i_title_end >= 0 && i_seekpoint_end >=0 ) + { + if( p_access->info.i_title > i_title_end || + ( p_access->info.i_title == i_title_end && + p_access->info.i_seekpoint > i_seekpoint_end ) ) return 0; + } + else if( i_seekpoint_end >=0 ) + { + if( p_access->info.i_seekpoint > i_seekpoint_end ) return 0; + } + else if( i_title_end >= 0 ) + { + if( p_access->info.i_title > i_title_end ) return 0; + } + } + + return 1; +} + +/***************************************************************************** + * UpdateItemLength: + *****************************************************************************/ +static void UpdateItemLength( input_thread_t *p_input, int64_t i_length ) +{ + input_item_SetDuration( p_input->p->input.p_item, (mtime_t) i_length ); +} + +/***************************************************************************** + * InputSourceNew: + *****************************************************************************/ +static input_source_t *InputSourceNew( input_thread_t *p_input ) +{ + (void)p_input; + input_source_t *in = (input_source_t*) malloc( sizeof( input_source_t ) ); + if( in ) + memset( in, 0, sizeof( input_source_t ) ); + return in; +} + +/***************************************************************************** + * InputSourceInit: + *****************************************************************************/ +static int InputSourceInit( input_thread_t *p_input, + input_source_t *in, const char *psz_mrl, + const char *psz_forced_demux ) +{ + const bool b_master = in == &p_input->p->input; + + char psz_dup[strlen (psz_mrl) + 1]; + const char *psz_access; + const char *psz_demux; + char *psz_path; + char *psz_tmp; + char *psz; + vlc_value_t val; + double f_fps; + + strcpy( psz_dup, psz_mrl ); + + if( !in ) return VLC_EGENERIC; + if( !p_input ) return VLC_EGENERIC; + + /* Split uri */ + input_SplitMRL( &psz_access, &psz_demux, &psz_path, psz_dup ); + + msg_Dbg( p_input, "`%s' gives access `%s' demux `%s' path `%s'", + psz_mrl, psz_access, psz_demux, psz_path ); + if( !p_input->b_preparsing ) + { + /* Hack to allow udp://@:port syntax */ + if( !psz_access || + (strncmp( psz_access, "udp", 3 ) && + strncmp( psz_access, "rtp", 3 )) ) + { + /* Find optional titles and seekpoints */ + MRLSections( p_input, psz_path, &in->i_title_start, &in->i_title_end, + &in->i_seekpoint_start, &in->i_seekpoint_end ); + } + + if( psz_forced_demux && *psz_forced_demux ) + { + psz_demux = psz_forced_demux; + } + else if( *psz_demux == '\0' ) + { + /* special hack for forcing a demuxer with --demux=module + * (and do nothing with a list) */ + char *psz_var_demux = var_GetNonEmptyString( p_input, "demux" ); + + if( psz_var_demux != NULL && + !strchr(psz_var_demux, ',' ) && + !strchr(psz_var_demux, ':' ) ) + { + psz_demux = psz_var_demux; + + msg_Dbg( p_input, "enforced demux ` %s'", psz_demux ); + } + } + + /* Try access_demux if no demux given */ + if( *psz_demux == '\0' ) + { + in->p_demux = demux_New( p_input, psz_access, psz_demux, psz_path, + NULL, p_input->p->p_es_out, false ); + } + } + else + { + /* Preparsing is only for file:// */ + if( *psz_demux ) + goto error; + if( !*psz_access ) /* path without scheme:// */ + psz_access = "file"; + if( strcmp( psz_access, "file" ) ) + goto error; + msg_Dbg( p_input, "trying to pre-parse %s", psz_path ); + } + + if( in->p_demux ) + { + int64_t i_pts_delay; + + /* Get infos from access_demux */ + demux_Control( in->p_demux, + DEMUX_GET_PTS_DELAY, &i_pts_delay ); + p_input->i_pts_delay = __MAX( p_input->i_pts_delay, i_pts_delay ); + + in->b_title_demux = true; + if( demux_Control( in->p_demux, DEMUX_GET_TITLE_INFO, + &in->title, &in->i_title, + &in->i_title_offset, &in->i_seekpoint_offset ) ) + { + in->i_title = 0; + in->title = NULL; + } + if( demux_Control( in->p_demux, DEMUX_CAN_CONTROL_PACE, + &in->b_can_pace_control ) ) + in->b_can_pace_control = false; + + if( !in->b_can_pace_control ) + { + if( demux_Control( in->p_demux, DEMUX_CAN_CONTROL_RATE, + &in->b_can_rate_control, &in->b_rescale_ts ) ) + { + in->b_can_rate_control = false; + in->b_rescale_ts = true; /* not used */ + } + } + else + { + in->b_can_rate_control = true; + in->b_rescale_ts = true; + } + if( demux_Control( in->p_demux, DEMUX_CAN_PAUSE, + &in->b_can_pause ) ) + in->b_can_pause = false; + var_SetBool( p_input, "can-pause", in->b_can_pause ); + + int ret = demux_Control( in->p_demux, DEMUX_CAN_SEEK, + &val.b_bool ); + if( ret != VLC_SUCCESS ) + val.b_bool = false; + var_Set( p_input, "seekable", val ); + } + else + { + int64_t i_pts_delay; + + if( b_master ) + input_ChangeState( p_input, OPENING_S ); + + /* Now try a real access */ + in->p_access = access_New( p_input, psz_access, psz_demux, psz_path ); + + /* Access failed, URL encoded ? */ + if( in->p_access == NULL && strchr( psz_path, '%' ) ) + { + decode_URI( psz_path ); + + msg_Dbg( p_input, "retrying with access `%s' demux `%s' path `%s'", + psz_access, psz_demux, psz_path ); + + in->p_access = access_New( p_input, + psz_access, psz_demux, psz_path ); + } + if( in->p_access == NULL ) + { + msg_Err( p_input, "open of `%s' failed: %s", psz_mrl, + msg_StackMsg() ); + intf_UserFatal( VLC_OBJECT( p_input), false, + _("Your input can't be opened"), + _("VLC is unable to open the MRL '%s'." + " Check the log for details."), psz_mrl ); + goto error; + } + + /* */ + psz_tmp = psz = var_GetNonEmptyString( p_input, "access-filter" ); + while( psz && *psz ) + { + access_t *p_access = in->p_access; + char *end = strchr( psz, ':' ); + + if( end ) + *end++ = '\0'; + + in->p_access = access_FilterNew( in->p_access, psz ); + if( in->p_access == NULL ) + { + in->p_access = p_access; + msg_Warn( p_input, "failed to insert access filter %s", + psz ); + } + + psz = end; + } + free( psz_tmp ); + + /* Get infos from access */ + if( !p_input->b_preparsing ) + { + access_Control( in->p_access, + ACCESS_GET_PTS_DELAY, &i_pts_delay ); + p_input->i_pts_delay = __MAX( p_input->i_pts_delay, i_pts_delay ); + + in->b_title_demux = false; + if( access_Control( in->p_access, ACCESS_GET_TITLE_INFO, + &in->title, &in->i_title, + &in->i_title_offset, &in->i_seekpoint_offset ) ) + + { + in->i_title = 0; + in->title = NULL; + } + access_Control( in->p_access, ACCESS_CAN_CONTROL_PACE, + &in->b_can_pace_control ); + in->b_can_rate_control = in->b_can_pace_control; + in->b_rescale_ts = true; + + access_Control( in->p_access, ACCESS_CAN_PAUSE, + &in->b_can_pause ); + var_SetBool( p_input, "can-pause", in->b_can_pause ); + access_Control( in->p_access, ACCESS_CAN_SEEK, + &val.b_bool ); + var_Set( p_input, "seekable", val ); + } + + if( b_master ) + input_ChangeState( p_input, BUFFERING_S ); + + /* Create the stream_t */ + in->p_stream = stream_AccessNew( in->p_access, p_input->b_preparsing ); + if( in->p_stream == NULL ) + { + msg_Warn( p_input, "cannot create a stream_t from access" ); + goto error; + } + + /* Open a demuxer */ + if( *psz_demux == '\0' && *in->p_access->psz_demux ) + { + psz_demux = in->p_access->psz_demux; + } + + { + /* Take access redirections into account */ + char *psz_real_path; + char *psz_buf = NULL; + if( in->p_access->psz_path ) + { + const char *psz_a, *psz_d; + psz_buf = strdup( in->p_access->psz_path ); + input_SplitMRL( &psz_a, &psz_d, &psz_real_path, psz_buf ); + } + else + { + psz_real_path = psz_path; + } + in->p_demux = demux_New( p_input, psz_access, psz_demux, + psz_real_path, + in->p_stream, p_input->p->p_es_out, + p_input->b_preparsing ); + free( psz_buf ); + } + + if( in->p_demux == NULL ) + { + msg_Err( p_input, "no suitable demux module for `%s/%s://%s'", + psz_access, psz_demux, psz_path ); + intf_UserFatal( VLC_OBJECT( p_input ), false, + _("VLC can't recognize the input's format"), + _("The format of '%s' cannot be detected. " + "Have a look the log for details."), psz_mrl ); + goto error; + } + + /* Get title from demux */ + if( !p_input->b_preparsing && in->i_title <= 0 ) + { + if( demux_Control( in->p_demux, DEMUX_GET_TITLE_INFO, + &in->title, &in->i_title, + &in->i_title_offset, &in->i_seekpoint_offset )) + { + TAB_INIT( in->i_title, in->title ); + } + else + { + in->b_title_demux = true; + } + } + } + + /* get attachment + * FIXME improve for b_preparsing: move it after GET_META and check psz_arturl */ + if( 1 || !p_input->b_preparsing ) + { + int i_attachment; + input_attachment_t **attachment; + if( !demux_Control( in->p_demux, DEMUX_GET_ATTACHMENTS, + &attachment, &i_attachment ) ) + { + vlc_mutex_lock( &p_input->p->input.p_item->lock ); + AppendAttachment( &p_input->p->i_attachment, &p_input->p->attachment, + i_attachment, attachment ); + vlc_mutex_unlock( &p_input->p->input.p_item->lock ); + } + } + if( !demux_Control( in->p_demux, DEMUX_GET_FPS, &f_fps ) ) + { + vlc_mutex_lock( &p_input->p->input.p_item->lock ); + in->f_fps = f_fps; + vlc_mutex_unlock( &p_input->p->input.p_item->lock ); + } + + if( var_GetInteger( p_input, "clock-synchro" ) != -1 ) + in->b_can_pace_control = !var_GetInteger( p_input, "clock-synchro" ); + + return VLC_SUCCESS; + +error: + if( b_master ) + input_ChangeState( p_input, ERROR_S ); + + if( in->p_demux ) + demux_Delete( in->p_demux ); + + if( in->p_stream ) + stream_Delete( in->p_stream ); + + if( in->p_access ) + access_Delete( in->p_access ); + + return VLC_EGENERIC; +} + +/***************************************************************************** + * InputSourceClean: + *****************************************************************************/ +static void InputSourceClean( input_source_t *in ) +{ + int i; + + if( in->p_demux ) + demux_Delete( in->p_demux ); + + if( in->p_stream ) + stream_Delete( in->p_stream ); + + if( in->p_access ) + access_Delete( in->p_access ); + + if( in->i_title > 0 ) + { + for( i = 0; i < in->i_title; i++ ) + vlc_input_title_Delete( in->title[i] ); + TAB_CLEAN( in->i_title, in->title ); + } +} + +static void SlaveDemux( input_thread_t *p_input ) +{ + int64_t i_time; + int i; + + if( demux_Control( p_input->p->input.p_demux, DEMUX_GET_TIME, &i_time ) ) + { + msg_Err( p_input, "demux doesn't like DEMUX_GET_TIME" ); + return; + } + + for( i = 0; i < p_input->p->i_slave; i++ ) + { + input_source_t *in = p_input->p->slave[i]; + int i_ret = 1; + + if( in->b_eof ) + continue; + + if( demux_Control( in->p_demux, DEMUX_SET_NEXT_DEMUX_TIME, i_time ) ) + { + for( ;; ) + { + int64_t i_stime; + if( demux_Control( in->p_demux, DEMUX_GET_TIME, &i_stime ) ) + { + msg_Err( p_input, "slave[%d] doesn't like " + "DEMUX_GET_TIME -> EOF", i ); + i_ret = 0; + break; + } + + if( i_stime >= i_time ) + break; + + if( ( i_ret = in->p_demux->pf_demux( in->p_demux ) ) <= 0 ) + break; + } + } + else + { + i_ret = in->p_demux->pf_demux( in->p_demux ); + } + + if( i_ret <= 0 ) + { + msg_Dbg( p_input, "slave %d EOF", i ); + in->b_eof = true; + } + } +} + +static void SlaveSeek( input_thread_t *p_input ) +{ + int64_t i_time; + int i; + + if( !p_input ) return; + + if( demux_Control( p_input->p->input.p_demux, DEMUX_GET_TIME, &i_time ) ) + { + msg_Err( p_input, "demux doesn't like DEMUX_GET_TIME" ); + return; + } + + for( i = 0; i < p_input->p->i_slave; i++ ) + { + input_source_t *in = p_input->p->slave[i]; + + if( demux_Control( in->p_demux, DEMUX_SET_TIME, i_time ) ) + { + msg_Err( p_input, "seek failed for slave %d -> EOF", i ); + in->b_eof = true; + } + } +} + +/***************************************************************************** + * InputMetaUser: + *****************************************************************************/ +static void InputMetaUser( input_thread_t *p_input, vlc_meta_t *p_meta ) +{ + vlc_value_t val; + + if( !p_meta ) return; + + /* Get meta information from user */ +#define GET_META( field, s ) \ + var_Get( p_input, (s), &val ); \ + if( *val.psz_string ) \ + vlc_meta_Set( p_meta, vlc_meta_ ## field, val.psz_string ); \ + free( val.psz_string ) + + GET_META( Title, "meta-title" ); + GET_META( Artist, "meta-artist" ); + GET_META( Genre, "meta-genre" ); + GET_META( Copyright, "meta-copyright" ); + GET_META( Description, "meta-description" ); + GET_META( Date, "meta-date" ); + GET_META( URL, "meta-url" ); +#undef GET_META +} + +/***************************************************************************** + * InputUpdateMeta: merge p_item meta data with p_meta taking care of + * arturl and locking issue. + *****************************************************************************/ +static void InputUpdateMeta( input_thread_t *p_input, vlc_meta_t *p_meta ) +{ + input_item_t *p_item = p_input->p->input.p_item; + char * psz_arturl = NULL; + char *psz_title = NULL; + int i_arturl_event = false; + + if( !p_meta ) + return; + + psz_arturl = input_item_GetArtURL( p_item ); + + vlc_mutex_lock( &p_item->lock ); + if( vlc_meta_Get( p_meta, vlc_meta_Title ) && !p_item->b_fixed_name ) + psz_title = strdup( vlc_meta_Get( p_meta, vlc_meta_Title ) ); + + vlc_meta_Merge( p_item->p_meta, p_meta ); + + if( psz_arturl && *psz_arturl ) + { + vlc_meta_Set( p_item->p_meta, vlc_meta_ArtworkURL, psz_arturl ); + i_arturl_event = true; + } + + vlc_meta_Delete( p_meta ); + + if( psz_arturl && !strncmp( psz_arturl, "attachment://", strlen("attachment") ) ) + { + /* Don't look for art cover if sout + * XXX It can change when sout has meta data support */ + if( p_input->p->p_sout && !p_input->b_preparsing ) + { + vlc_meta_Set( p_item->p_meta, vlc_meta_ArtworkURL, "" ); + i_arturl_event = true; + + } + else + input_ExtractAttachmentAndCacheArt( p_input ); + } + free( psz_arturl ); + + /* A bit ugly */ + p_meta = NULL; + if( vlc_dictionary_keys_count( &p_item->p_meta->extra_tags ) > 0 ) + { + p_meta = vlc_meta_New(); + vlc_meta_Merge( p_meta, input_item_GetMetaObject( p_item ) ); + } + vlc_mutex_unlock( &p_item->lock ); + + input_item_SetPreparsed( p_item, true ); + + if( i_arturl_event == true ) + { + vlc_event_t event; + + /* Notify interested third parties */ + event.type = vlc_InputItemMetaChanged; + event.u.input_item_meta_changed.meta_type = vlc_meta_ArtworkURL; + vlc_event_send( &p_item->event_manager, &event ); + } + + if( psz_title ) + { + input_Control( p_input, INPUT_SET_NAME, psz_title ); + free( psz_title ); + } + + /** \todo handle sout meta */ +} + + +static void AppendAttachment( int *pi_attachment, input_attachment_t ***ppp_attachment, + int i_new, input_attachment_t **pp_new ) +{ + int i_attachment = *pi_attachment; + input_attachment_t **attachment = *ppp_attachment; + int i; + + attachment = realloc( attachment, + sizeof(input_attachment_t**) * ( i_attachment + i_new ) ); + for( i = 0; i < i_new; i++ ) + attachment[i_attachment++] = pp_new[i]; + free( pp_new ); + + /* */ + *pi_attachment = i_attachment; + *ppp_attachment = attachment; +} + +static void AccessMeta( input_thread_t * p_input, vlc_meta_t *p_meta ) +{ + int i; + + if( p_input->b_preparsing ) + return; + + if( p_input->p->input.p_access ) + access_Control( p_input->p->input.p_access, ACCESS_GET_META, + p_meta ); + + /* Get meta data from slave input */ + for( i = 0; i < p_input->p->i_slave; i++ ) + { + DemuxMeta( p_input, p_meta, p_input->p->slave[i]->p_demux ); + if( p_input->p->slave[i]->p_access ) + { + access_Control( p_input->p->slave[i]->p_access, + ACCESS_GET_META, p_meta ); + } + } +} + +static void DemuxMeta( input_thread_t *p_input, vlc_meta_t *p_meta, demux_t *p_demux ) +{ + bool b_bool; + module_t *p_id3; + + +#if 0 + /* XXX I am not sure it is a great idea, besides, there is more than that + * if we want to do it right */ + vlc_mutex_lock( &p_item->lock ); + if( p_item->p_meta && (p_item->p_meta->i_status & ITEM_PREPARSED ) ) + { + vlc_mutex_unlock( &p_item->lock ); + return; + } + vlc_mutex_unlock( &p_item->lock ); +#endif + + demux_Control( p_demux, DEMUX_GET_META, p_meta ); + if( demux_Control( p_demux, DEMUX_HAS_UNSUPPORTED_META, &b_bool ) ) + return; + if( !b_bool ) + return; + + p_demux->p_private = malloc( sizeof( demux_meta_t ) ); + if(! p_demux->p_private ) + return; + + p_id3 = module_Need( p_demux, "meta reader", NULL, 0 ); + if( p_id3 ) + { + demux_meta_t *p_demux_meta = (demux_meta_t *)p_demux->p_private; + + if( p_demux_meta->p_meta ) + { + vlc_meta_Merge( p_meta, p_demux_meta->p_meta ); + vlc_meta_Delete( p_demux_meta->p_meta ); + } + + if( p_demux_meta->i_attachments > 0 ) + { + vlc_mutex_lock( &p_input->p->input.p_item->lock ); + AppendAttachment( &p_input->p->i_attachment, &p_input->p->attachment, + p_demux_meta->i_attachments, p_demux_meta->attachments ); + vlc_mutex_unlock( &p_input->p->input.p_item->lock ); + } + module_Unneed( p_demux, p_id3 ); + } + free( p_demux->p_private ); +} + + +/***************************************************************************** + * MRLSplit: parse the access, demux and url part of the + * Media Resource Locator. + *****************************************************************************/ +void input_SplitMRL( const char **ppsz_access, const char **ppsz_demux, char **ppsz_path, + char *psz_dup ) +{ + char *psz_access = NULL; + char *psz_demux = NULL; + char *psz_path; + + /* Either there is an access/demux specification before :// + * or we have a plain local file path. */ + psz_path = strstr( psz_dup, "://" ); + if( psz_path != NULL ) + { + *psz_path = '\0'; + psz_path += 3; /* skips "://" */ + + /* Separate access from demux (/://) */ + psz_access = psz_dup; + psz_demux = strchr( psz_access, '/' ); + if( psz_demux ) + *psz_demux++ = '\0'; + + /* We really don't want module name substitution here! */ + if( psz_access[0] == '$' ) + psz_access++; + if( psz_demux && psz_demux[0] == '$' ) + psz_demux++; + } + else + { + psz_path = psz_dup; + } + *ppsz_access = psz_access ? psz_access : (char*)""; + *ppsz_demux = psz_demux ? psz_demux : (char*)""; + *ppsz_path = psz_path; +} + +static inline bool next(char ** src) +{ + char *end; + errno = 0; + long result = strtol( *src, &end, 0 ); + if( errno != 0 || result >= LONG_MAX || result <= LONG_MIN || + end == *src ) + { + return false; + } + *src = end; + return true; +} + +/***************************************************************************** + * MRLSections: parse title and seekpoint info from the Media Resource Locator. + * + * Syntax: + * [url][@[title-start][:chapter-start][-[title-end][:chapter-end]]] + *****************************************************************************/ +static void MRLSections( input_thread_t *p_input, char *psz_source, + int *pi_title_start, int *pi_title_end, + int *pi_chapter_start, int *pi_chapter_end ) +{ + char *psz, *psz_end, *psz_next, *psz_check; + + *pi_title_start = *pi_title_end = -1; + *pi_chapter_start = *pi_chapter_end = -1; + + /* Start by parsing titles and chapters */ + if( !psz_source || !( psz = strrchr( psz_source, '@' ) ) ) return; + + + /* Check we are really dealing with a title/chapter section */ + psz_check = psz + 1; + if( !*psz_check ) return; + if( isdigit(*psz_check) ) + if(!next(&psz_check)) return; + if( *psz_check != ':' && *psz_check != '-' && *psz_check ) return; + if( *psz_check == ':' && ++psz_check ) + { + if( isdigit(*psz_check) ) + if(!next(&psz_check)) return; + } + if( *psz_check != '-' && *psz_check ) return; + if( *psz_check == '-' && ++psz_check ) + { + if( isdigit(*psz_check) ) + if(!next(&psz_check)) return; + } + if( *psz_check != ':' && *psz_check ) return; + if( *psz_check == ':' && ++psz_check ) + { + if( isdigit(*psz_check) ) + if(!next(&psz_check)) return; + } + if( *psz_check ) return; + + /* Separate start and end */ + *psz++ = 0; + if( ( psz_end = strchr( psz, '-' ) ) ) *psz_end++ = 0; + + /* Look for the start title */ + *pi_title_start = strtol( psz, &psz_next, 0 ); + if( !*pi_title_start && psz == psz_next ) *pi_title_start = -1; + *pi_title_end = *pi_title_start; + psz = psz_next; + + /* Look for the start chapter */ + if( *psz ) psz++; + *pi_chapter_start = strtol( psz, &psz_next, 0 ); + if( !*pi_chapter_start && psz == psz_next ) *pi_chapter_start = -1; + *pi_chapter_end = *pi_chapter_start; + + if( psz_end ) + { + /* Look for the end title */ + *pi_title_end = strtol( psz_end, &psz_next, 0 ); + if( !*pi_title_end && psz_end == psz_next ) *pi_title_end = -1; + psz_end = psz_next; + + /* Look for the end chapter */ + if( *psz_end ) psz_end++; + *pi_chapter_end = strtol( psz_end, &psz_next, 0 ); + if( !*pi_chapter_end && psz_end == psz_next ) *pi_chapter_end = -1; + } + + msg_Dbg( p_input, "source=`%s' title=%d/%d seekpoint=%d/%d", + psz_source, *pi_title_start, *pi_chapter_start, + *pi_title_end, *pi_chapter_end ); +} + +/***************************************************************************** + * input_AddSubtitles: add a subtitles file and enable it + *****************************************************************************/ +bool input_AddSubtitles( input_thread_t *p_input, char *psz_subtitle, + bool b_check_extension ) +{ + input_source_t *sub; + vlc_value_t count; + vlc_value_t list; + char *psz_path, *psz_extension; + + if( b_check_extension && !subtitles_Filter( psz_subtitle ) ) + { + return false; + } + + /* if we are provided a subtitle.sub file, + * see if we don't have a subtitle.idx and use it instead */ + psz_path = strdup( psz_subtitle ); + if( psz_path ) + { + psz_extension = strrchr( psz_path, '.'); + if( psz_extension && strcmp( psz_extension, ".sub" ) == 0 ) + { + FILE *f; + + strcpy( psz_extension, ".idx" ); + /* FIXME: a portable wrapper for stat() or access() would be more suited */ + if( ( f = utf8_fopen( psz_path, "rt" ) ) ) + { + fclose( f ); + msg_Dbg( p_input, "using %s subtitles file instead of %s", + psz_path, psz_subtitle ); + strcpy( psz_subtitle, psz_path ); + } + } + free( psz_path ); + } + + var_Change( p_input, "spu-es", VLC_VAR_CHOICESCOUNT, &count, NULL ); + + sub = InputSourceNew( p_input ); + if( !InputSourceInit( p_input, sub, psz_subtitle, "subtitle" ) ) + { + TAB_APPEND( p_input->p->i_slave, p_input->p->slave, sub ); + + /* Select the ES */ + if( !var_Change( p_input, "spu-es", VLC_VAR_GETLIST, &list, NULL ) ) + { + if( count.i_int == 0 ) + count.i_int++; + /* if it was first one, there is disable too */ + + if( count.i_int < list.p_list->i_count ) + { + input_ControlPush( p_input, INPUT_CONTROL_SET_ES, + &list.p_list->p_values[count.i_int] ); + } + var_Change( p_input, "spu-es", VLC_VAR_FREELIST, &list, NULL ); + } + } + else free( sub ); + + return true; +} + +/***************************************************************************** + * input_get_event_manager + *****************************************************************************/ +vlc_event_manager_t *input_get_event_manager( input_thread_t *p_input ) +{ + return &p_input->p->event_manager; +} diff --git a/VLC/input/input_internal.h b/VLC/input/input_internal.h new file mode 100644 index 0000000..a6edd31 --- /dev/null +++ b/VLC/input/input_internal.h @@ -0,0 +1,485 @@ +/***************************************************************************** + * input_internal.h: Internal input structures + ***************************************************************************** + * Copyright (C) 1998-2006 the VideoLAN team + * $Id$ + * + * Authors: Laurent Aimar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#if defined(__PLUGIN__) || defined(__BUILTIN__) || !defined(__LIBVLC__) + +#endif + +#ifndef _INPUT_INTERNAL_H +#define _INPUT_INTERNAL_H 1 + +#include "vlc_access.h" +#include "vlc_demux.h" +#include "vlc_input.h" + +/***************************************************************************** + * Private input fields + *****************************************************************************/ +/* input_source_t: gathers all information per input source */ +typedef struct +{ + /* Input item description */ + input_item_t *p_item; + + /* Access/Stream/Demux plugins */ + access_t *p_access; + stream_t *p_stream; + demux_t *p_demux; + + /* Title infos for that input */ + bool b_title_demux; /* Titles/Seekpoints provided by demux */ + int i_title; + input_title_t **title; + + int i_title_offset; + int i_seekpoint_offset; + + int i_title_start; + int i_title_end; + int i_seekpoint_start; + int i_seekpoint_end; + + /* Properties */ + bool b_can_pause; + bool b_can_pace_control; + bool b_can_rate_control; + bool b_rescale_ts; + + bool b_eof; /* eof of demuxer */ + double f_fps; + + /* Clock average variation */ + int i_cr_average; + +} input_source_t; + +/** Private input fields */ +struct input_thread_private_t +{ + /* Object's event manager */ + vlc_event_manager_t event_manager; + + /* Global properties */ + bool b_can_pause; + bool b_can_rate_control; + + int i_rate; + /* */ + int64_t i_start; /* :start-time,0 by default */ + int64_t i_stop; /* :stop-time, 0 if none */ + int64_t i_run; /* :run-time, 0 if none */ + + /* Title infos FIXME multi-input (not easy) ? */ + int i_title; + input_title_t **title; + + int i_title_offset; + int i_seekpoint_offset; + + /* User bookmarks FIXME won't be easy with multiples input */ + int i_bookmark; + seekpoint_t **bookmark; + + /* Input attachment */ + int i_attachment; + input_attachment_t **attachment; + + /* Output */ + es_out_t *p_es_out; + sout_instance_t *p_sout; /* XXX Move it to es_out ? */ + bool b_out_pace_control; /* idem ? */ + + /* Main input properties */ + input_source_t input; + /* Slave demuxers (subs, and others) */ + int i_slave; + input_source_t **slave; + + /* pts delay fixup */ + struct { + int i_num_faulty; + bool to_high; + bool auto_adjust; + } pts_adjust; + + /* Stats counters */ + struct { + counter_t *p_read_packets; + counter_t *p_read_bytes; + counter_t *p_input_bitrate; + counter_t *p_demux_read; + counter_t *p_demux_bitrate; + counter_t *p_decoded_audio; + counter_t *p_decoded_video; + counter_t *p_decoded_sub; + counter_t *p_sout_sent_packets; + counter_t *p_sout_sent_bytes; + counter_t *p_sout_send_bitrate; + counter_t *p_played_abuffers; + counter_t *p_lost_abuffers; + counter_t *p_displayed_pictures; + counter_t *p_lost_pictures; + vlc_mutex_t counters_lock; + } counters; + + /* Buffer of pending actions */ + vlc_mutex_t lock_control; + int i_control; + struct + { + /* XXX for string value you have to allocate it before calling + * input_ControlPush */ + int i_type; + vlc_value_t val; + } control[INPUT_CONTROL_FIFO_SIZE]; +}; + +/*************************************************************************** + * Internal control helpers + ***************************************************************************/ +enum input_control_e +{ + INPUT_CONTROL_SET_DIE, + + INPUT_CONTROL_SET_STATE, + + INPUT_CONTROL_SET_RATE, + INPUT_CONTROL_SET_RATE_SLOWER, + INPUT_CONTROL_SET_RATE_FASTER, + + INPUT_CONTROL_SET_POSITION, + INPUT_CONTROL_SET_POSITION_OFFSET, + + INPUT_CONTROL_SET_TIME, + INPUT_CONTROL_SET_TIME_OFFSET, + + INPUT_CONTROL_SET_PROGRAM, + + INPUT_CONTROL_SET_TITLE, + INPUT_CONTROL_SET_TITLE_NEXT, + INPUT_CONTROL_SET_TITLE_PREV, + + INPUT_CONTROL_SET_SEEKPOINT, + INPUT_CONTROL_SET_SEEKPOINT_NEXT, + INPUT_CONTROL_SET_SEEKPOINT_PREV, + + INPUT_CONTROL_SET_BOOKMARK, + + INPUT_CONTROL_SET_ES, + + INPUT_CONTROL_SET_AUDIO_DELAY, + INPUT_CONTROL_SET_SPU_DELAY, + + INPUT_CONTROL_ADD_SLAVE, +}; + +/* Internal helpers */ +static inline void input_ControlPush( input_thread_t *p_input, + int i_type, vlc_value_t *p_val ) +{ + vlc_mutex_lock( &p_input->p->lock_control ); + if( i_type == INPUT_CONTROL_SET_DIE ) + { + /* Special case, empty the control */ + p_input->p->i_control = 1; + p_input->p->control[0].i_type = i_type; + memset( &p_input->p->control[0].val, 0, sizeof( vlc_value_t ) ); + } + else + if( p_input->p->i_control >= INPUT_CONTROL_FIFO_SIZE ) + { + msg_Err( p_input, "input control fifo overflow, trashing type=%d", + i_type ); + } + else + { + p_input->p->control[p_input->p->i_control].i_type = i_type; + if( p_val ) + p_input->p->control[p_input->p->i_control].val = *p_val; + else + memset( &p_input->p->control[p_input->p->i_control].val, 0, + sizeof( vlc_value_t ) ); + + p_input->p->i_control++; + } + vlc_mutex_unlock( &p_input->p->lock_control ); +} + +/** Stuff moved out of vlc_input.h -- FIXME: should probably not be inline + * anyway. */ + +static inline void input_item_SetPreparsed( input_item_t *p_i, bool preparsed ) +{ + bool send_event = false; + + if( !p_i->p_meta ) + p_i->p_meta = vlc_meta_New(); + + vlc_mutex_lock( &p_i->lock ); + int new_status; + if( preparsed ) + new_status = p_i->p_meta->i_status | ITEM_PREPARSED; + else + new_status = p_i->p_meta->i_status & ~ITEM_PREPARSED; + if( p_i->p_meta->i_status != new_status ) + { + p_i->p_meta->i_status = new_status; + send_event = true; + } + + vlc_mutex_unlock( &p_i->lock ); + + if( send_event ) + { + vlc_event_t event; + event.type = vlc_InputItemPreparsedChanged; + event.u.input_item_preparsed_changed.new_status = new_status; + vlc_event_send( &p_i->event_manager, &event ); + } +} + +static inline void input_item_SetArtNotFound( input_item_t *p_i, bool notfound ) +{ + if( !p_i->p_meta ) + p_i->p_meta = vlc_meta_New(); + + if( notfound ) + p_i->p_meta->i_status |= ITEM_ART_NOTFOUND; + else + p_i->p_meta->i_status &= ~ITEM_ART_NOTFOUND; +} + +static inline void input_item_SetArtFetched( input_item_t *p_i, bool artfetched ) +{ + if( !p_i->p_meta ) + p_i->p_meta = vlc_meta_New(); + + if( artfetched ) + p_i->p_meta->i_status |= ITEM_ART_FETCHED; + else + p_i->p_meta->i_status &= ~ITEM_ART_FETCHED; +} + +void input_item_SetHasErrorWhenReading( input_item_t *p_i, bool error ); + +/********************************************************************** + * Item metadata + **********************************************************************/ +typedef struct playlist_album_t +{ + char *psz_artist; + char *psz_album; + char *psz_arturl; + bool b_found; +} playlist_album_t; + +int input_ArtFind ( playlist_t *, input_item_t * ); +int input_DownloadAndCacheArt ( playlist_t *, input_item_t * ); + +/* Becarefull; p_item lock HAS to be taken */ +void input_ExtractAttachmentAndCacheArt( input_thread_t *p_input ); + +/*************************************************************************** + * Internal prototypes + ***************************************************************************/ + +/* misc/stats.c */ +input_stats_t *stats_NewInputStats( input_thread_t *p_input ); + +/* input.c */ +#define input_CreateThreadExtended(a,b,c,d) __input_CreateThreadExtended(VLC_OBJECT(a),b,c,d) +input_thread_t *__input_CreateThreadExtended ( vlc_object_t *, input_item_t *, const char *, sout_instance_t * ); + +sout_instance_t * input_DetachSout( input_thread_t *p_input ); + +/* var.c */ +void input_ControlVarInit ( input_thread_t * ); +void input_ControlVarStop( input_thread_t * ); +void input_ControlVarNavigation( input_thread_t * ); +void input_ControlVarTitle( input_thread_t *, int i_title ); + +void input_ConfigVarInit ( input_thread_t * ); + +/* stream.c */ +stream_t *stream_AccessNew( access_t *p_access, bool ); +void stream_AccessDelete( stream_t *s ); +void stream_AccessReset( stream_t *s ); +void stream_AccessUpdate( stream_t *s ); + +/* decoder.c */ +void input_DecoderDiscontinuity( decoder_t * p_dec, bool b_flush ); +bool input_DecoderEmpty( decoder_t * p_dec ); +int input_DecoderSetCcState( decoder_t *, bool b_decode, int i_channel ); +int input_DecoderGetCcState( decoder_t *, bool *pb_decode, int i_channel ); +void input_DecoderIsCcPresent( decoder_t *, bool pb_present[4] ); + +/* es_out.c */ +es_out_t *input_EsOutNew( input_thread_t *, int i_rate ); +void input_EsOutDelete( es_out_t * ); +es_out_id_t *input_EsOutGetFromID( es_out_t *, int i_id ); +void input_EsOutSetDelay( es_out_t *, int i_cat, int64_t ); +void input_EsOutChangeRate( es_out_t *, int ); +void input_EsOutChangeState( es_out_t * ); +void input_EsOutChangePosition( es_out_t * ); +bool input_EsOutDecodersEmpty( es_out_t * ); + +/* clock.c */ +enum /* Synchro states */ +{ + SYNCHRO_OK = 0, + SYNCHRO_START = 1, + SYNCHRO_REINIT = 2, +}; + +typedef struct +{ + /* Synchronization information */ + mtime_t delta_cr; + mtime_t cr_ref, sysdate_ref; + mtime_t last_sysdate; + mtime_t last_cr; /* reference to detect unexpected stream + * discontinuities */ + mtime_t last_pts; + mtime_t last_update; + int i_synchro_state; + + bool b_master; + + int i_rate; + + /* Config */ + int i_cr_average; + int i_delta_cr_residue; +} input_clock_t; + +void input_ClockInit( input_clock_t *, bool b_master, int i_cr_average, int i_rate ); +void input_ClockSetPCR( input_thread_t *, input_clock_t *, mtime_t ); +void input_ClockResetPCR( input_clock_t * ); +mtime_t input_ClockGetTS( input_thread_t *, input_clock_t *, mtime_t ); +void input_ClockSetRate( input_clock_t *cl, int i_rate ); + +/* Subtitles */ +char **subtitles_Detect( input_thread_t *, char* path, const char *fname ); +int subtitles_Filter( const char *); + +static inline void input_ChangeStateWithVarCallback( input_thread_t *p_input, int state, bool callback ) +{ + const bool changed = p_input->i_state != state; + + p_input->i_state = state; + + input_item_SetHasErrorWhenReading( p_input->p->input.p_item, (state == ERROR_S) ); + + if( callback ) + { + var_SetInteger( p_input, "state", state ); + } + else + { + vlc_value_t val; + val.i_int = state; + var_Change( p_input, "state", VLC_VAR_SETVALUE, &val, NULL ); + } + if( changed ) + { + vlc_event_t event; + event.type = vlc_InputStateChanged; + event.u.input_state_changed.new_state = state; + vlc_event_send( &p_input->p->event_manager, &event ); + } +} + +static inline void input_ChangeState( input_thread_t *p_input, int state ) +{ + input_ChangeStateWithVarCallback( p_input, state, true ); +} + + +/* Access */ + +#define access_New( a, b, c, d ) __access_New(VLC_OBJECT(a), b, c, d ) +access_t * __access_New( vlc_object_t *p_obj, const char *psz_access, + const char *psz_demux, const char *psz_path ); +access_t * access_FilterNew( access_t *p_source, + const char *psz_access_filter ); +void access_Delete( access_t * ); + +/* Demuxer */ +#include "vlc_demux.h" + +/* stream_t *s could be null and then it mean a access+demux in one */ +#define demux_New( a, b, c, d, e, f,g ) __demux_New(VLC_OBJECT(a),b,c,d,e,f,g) +demux_t *__demux_New(vlc_object_t *p_obj, const char *psz_access, const char *psz_demux, const char *psz_path, stream_t *s, es_out_t *out, bool ); + +void demux_Delete(demux_t *); + +static inline int demux_Demux( demux_t *p_demux ) +{ + return p_demux->pf_demux( p_demux ); +} +static inline int demux_vaControl( demux_t *p_demux, int i_query, va_list args ) +{ + return p_demux->pf_control( p_demux, i_query, args ); +} +static inline int demux_Control( demux_t *p_demux, int i_query, ... ) +{ + va_list args; + int i_result; + + va_start( args, i_query ); + i_result = demux_vaControl( p_demux, i_query, args ); + va_end( args ); + return i_result; +} + +/* Stream */ +/** + * stream_t definition + */ +struct stream_t +{ + VLC_COMMON_MEMBERS + + /*block_t *(*pf_block) ( stream_t *, int i_size );*/ + int (*pf_read) ( stream_t *, void *p_read, unsigned int i_read ); + int (*pf_peek) ( stream_t *, const uint8_t **pp_peek, unsigned int i_peek ); + int (*pf_control)( stream_t *, int i_query, va_list ); + void (*pf_destroy)( stream_t *); + + stream_sys_t *p_sys; + + /* UTF-16 and UTF-32 file reading */ + vlc_iconv_t conv; + int i_char_width; + bool b_little_endian; +}; + +#include "libvlc.h" + +static inline stream_t *vlc_stream_create( vlc_object_t *obj ) +{ + return (stream_t *)vlc_custom_create( obj, sizeof(stream_t), + VLC_OBJECT_GENERIC, "stream" ); +} + +#endif diff --git a/VLC/input/item.c b/VLC/input/item.c new file mode 100644 index 0000000..04deed4 --- /dev/null +++ b/VLC/input/item.c @@ -0,0 +1,610 @@ +/***************************************************************************** + * item.c: input_item management + ***************************************************************************** + * Copyright (C) 1998-2004 the VideoLAN team + * $Id$ + * + * Authors: Clément Stenac + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include + +#include "vlc_common.h" +#include "vlc_playlist.h" +#include "vlc_interface.h" + +#include "input_internal.h" + +static void GuessType( input_item_t *p_item ); + +/** Stuff moved out of vlc_input.h -- FIXME: should probably not be inline + * anyway. */ +static inline void input_item_Init( vlc_object_t *p_o, input_item_t *p_i ) +{ + memset( p_i, 0, sizeof(input_item_t) ); + p_i->psz_name = NULL; + p_i->psz_uri = NULL; + TAB_INIT( p_i->i_es, p_i->es ); + TAB_INIT( p_i->i_options, p_i->ppsz_options ); + p_i->optflagv = NULL, p_i->optflagc = 0; + TAB_INIT( p_i->i_categories, p_i->pp_categories ); + + p_i->i_type = ITEM_TYPE_UNKNOWN; + p_i->b_fixed_name = true; + + p_i->p_stats = NULL; + p_i->p_meta = NULL; + + vlc_mutex_init( &p_i->lock ); + vlc_event_manager_t * p_em = &p_i->event_manager; + vlc_event_manager_init( p_em, p_i, p_o ); + vlc_event_manager_register_event_type( p_em, vlc_InputItemMetaChanged ); + vlc_event_manager_register_event_type( p_em, vlc_InputItemSubItemAdded ); + vlc_event_manager_register_event_type( p_em, vlc_InputItemDurationChanged ); + vlc_event_manager_register_event_type( p_em, vlc_InputItemPreparsedChanged ); + vlc_event_manager_register_event_type( p_em, vlc_InputItemNameChanged ); + vlc_event_manager_register_event_type( p_em, vlc_InputItemInfoChanged ); + vlc_event_manager_register_event_type( p_em, vlc_InputItemErrorWhenReadingChanged ); +} + +static inline void input_item_Clean( input_item_t *p_i ) +{ + int i; + + vlc_event_manager_fini( &p_i->event_manager ); + + free( p_i->psz_name ); + free( p_i->psz_uri ); + if( p_i->p_stats ) + { + vlc_mutex_destroy( &p_i->p_stats->lock ); + free( p_i->p_stats ); + } + + if( p_i->p_meta ) + vlc_meta_Delete( p_i->p_meta ); + + for( i = 0; i < p_i->i_options; i++ ) + free( p_i->ppsz_options[i] ); + TAB_CLEAN( p_i->i_options, p_i->ppsz_options ); + free( p_i->optflagv); + + for( i = 0; i < p_i->i_es; i++ ) + { + es_format_Clean( p_i->es[i] ); + free( p_i->es[i] ); + } + TAB_CLEAN( p_i->i_es, p_i->es ); + + for( i = 0; i < p_i->i_categories; i++ ) + { + info_category_t *p_category = p_i->pp_categories[i]; + int j; + + for( j = 0; j < p_category->i_infos; j++ ) + { + struct info_t *p_info = p_category->pp_infos[j]; + + free( p_info->psz_name); + free( p_info->psz_value ); + free( p_info ); + } + TAB_CLEAN( p_category->i_infos, p_category->pp_infos ); + + free( p_category->psz_name ); + free( p_category ); + } + TAB_CLEAN( p_i->i_categories, p_i->pp_categories ); + + vlc_mutex_destroy( &p_i->lock ); +} + +void input_item_SetHasErrorWhenReading( input_item_t *p_i, bool error ) +{ + vlc_event_t event; + + if( p_i->b_error_when_reading == error ) + return; + + p_i->b_error_when_reading = error; + + /* Notify interested third parties */ + event.type = vlc_InputItemErrorWhenReadingChanged; + event.u.input_item_error_when_reading_changed.new_value = error; + vlc_event_send( &p_i->event_manager, &event ); +} + +void input_item_SetMeta( input_item_t *p_i, vlc_meta_type_t meta_type, const char *psz_val ) +{ + vlc_event_t event; + + vlc_mutex_lock( &p_i->lock ); + if( !p_i->p_meta ) + p_i->p_meta = vlc_meta_New(); + vlc_meta_Set( p_i->p_meta, meta_type, psz_val ); + vlc_mutex_unlock( &p_i->lock ); + + /* Notify interested third parties */ + event.type = vlc_InputItemMetaChanged; + event.u.input_item_meta_changed.meta_type = meta_type; + vlc_event_send( &p_i->event_manager, &event ); +} + +/** + * Get the item from an input thread + */ +input_item_t *input_GetItem( input_thread_t *p_input ) +{ + assert( p_input && p_input->p ); + return p_input->p->input.p_item; +} + +void input_item_CopyOptions( input_item_t *p_parent, + input_item_t *p_child ) +{ + int i; + for( i = 0 ; i< p_parent->i_options; i++ ) + { + char *psz_option= strdup( p_parent->ppsz_options[i] ); + if( !strcmp( psz_option, "meta-file" ) ) + { + free( psz_option ); + continue; + } + p_child->i_options++; + p_child->ppsz_options = (char **)realloc( p_child->ppsz_options, + p_child->i_options * + sizeof( char * ) ); + p_child->ppsz_options[p_child->i_options-1] = psz_option; + p_child->optflagc++; + p_child->optflagv = (uint8_t *)realloc( p_child->optflagv, + p_child->optflagc ); + p_child->optflagv[p_child->optflagc - 1] = p_parent->optflagv[i]; + } +} + +void input_item_SetName( input_item_t *p_item, const char *psz_name ) +{ + free( p_item->psz_name ); + p_item->psz_name = strdup( psz_name ); +} + +/* This won't hold the item, but can tell to interested third parties + * Like the playlist, that there is a new sub item. With this design + * It is not the input item's responsability to keep all the ref of + * the input item children. */ +void input_item_AddSubItem( input_item_t *p_parent, + input_item_t *p_child ) +{ + vlc_event_t event; + + p_parent->i_type = ITEM_TYPE_PLAYLIST; + + /* Notify interested third parties */ + event.type = vlc_InputItemSubItemAdded; + event.u.input_item_subitem_added.p_new_child = p_child; + vlc_event_send( &p_parent->event_manager, &event ); +} + +int input_item_AddOption (input_item_t *item, const char *str) +{ + return input_item_AddOpt (item, str, VLC_INPUT_OPTION_TRUSTED); +} + +bool input_item_HasErrorWhenReading (input_item_t *item) +{ + return item->b_error_when_reading; +} + +bool input_item_MetaMatch( input_item_t *p_i, vlc_meta_type_t meta_type, const char *psz ) +{ + vlc_mutex_lock( &p_i->lock ); + if( !p_i->p_meta ) + { + vlc_mutex_unlock( &p_i->lock ); + return false; + } + const char * meta = vlc_meta_Get( p_i->p_meta, meta_type ); + bool ret = meta && strcasestr( meta, psz ); + vlc_mutex_unlock( &p_i->lock ); + + return ret; +} + +char * input_item_GetMeta( input_item_t *p_i, vlc_meta_type_t meta_type ) +{ + char * psz = NULL; + vlc_mutex_lock( &p_i->lock ); + + if( !p_i->p_meta ) + { + vlc_mutex_unlock( &p_i->lock ); + return NULL; + } + + if( vlc_meta_Get( p_i->p_meta, meta_type ) ) + psz = strdup( vlc_meta_Get( p_i->p_meta, meta_type ) ); + + vlc_mutex_unlock( &p_i->lock ); + return psz; +} + +char * input_item_GetName( input_item_t * p_i ) +{ + vlc_mutex_lock( &p_i->lock ); + char *psz_s = p_i->psz_name ? strdup( p_i->psz_name ) : NULL; + vlc_mutex_unlock( &p_i->lock ); + return psz_s; +} + +char * input_item_GetURI( input_item_t * p_i ) +{ + vlc_mutex_lock( &p_i->lock ); + char *psz_s = p_i->psz_uri ? strdup( p_i->psz_uri ) : NULL; + vlc_mutex_unlock( &p_i->lock ); + return psz_s; +} + +void input_item_SetURI( input_item_t * p_i, char * psz_uri ) +{ + vlc_mutex_lock( &p_i->lock ); + free( p_i->psz_uri ); + p_i->psz_uri = strdup( psz_uri ); + vlc_mutex_unlock( &p_i->lock ); +} + +mtime_t input_item_GetDuration( input_item_t * p_i ) +{ + vlc_mutex_lock( &p_i->lock ); + mtime_t i_duration = p_i->i_duration; + vlc_mutex_unlock( &p_i->lock ); + return i_duration; +} + +void input_item_SetDuration( input_item_t * p_i, mtime_t i_duration ) +{ + bool send_event = false; + + vlc_mutex_lock( &p_i->lock ); + if( p_i->i_duration != i_duration ) + { + p_i->i_duration = i_duration; + send_event = true; + } + vlc_mutex_unlock( &p_i->lock ); + + if ( send_event == true ) + { + vlc_event_t event; + event.type = vlc_InputItemDurationChanged; + event.u.input_item_duration_changed.new_duration = i_duration; + vlc_event_send( &p_i->event_manager, &event ); + } + + return; +} + + +bool input_item_IsPreparsed( input_item_t *p_i ) +{ + return p_i->p_meta ? p_i->p_meta->i_status & ITEM_PREPARSED : false ; +} + +bool input_item_IsArtFetched( input_item_t *p_i ) +{ + return p_i->p_meta ? p_i->p_meta->i_status & ITEM_ART_FETCHED : false ; +} + +const vlc_meta_t * input_item_GetMetaObject( input_item_t *p_i ) +{ + if( !p_i->p_meta ) + p_i->p_meta = vlc_meta_New(); + + return p_i->p_meta; +} + +void input_item_MetaMerge( input_item_t *p_i, const vlc_meta_t * p_new_meta ) +{ + if( !p_i->p_meta ) + p_i->p_meta = vlc_meta_New(); + + vlc_meta_Merge( p_i->p_meta, p_new_meta ); +} + +/** + * Get a info item from a given category in a given input item. + * + * \param p_i The input item to get info from + * \param psz_cat String representing the category for the info + * \param psz_name String representing the name of the desired info + * \return A pointer to the string with the given info if found, or an + * empty string otherwise. The caller should free the returned + * pointer. + */ +char *input_item_GetInfo( input_item_t *p_i, + const char *psz_cat, + const char *psz_name ) +{ + int i,j; + + vlc_mutex_lock( &p_i->lock ); + + for( i = 0 ; i< p_i->i_categories ; i++ ) + { + info_category_t *p_cat = p_i->pp_categories[i]; + + if( !psz_cat || strcmp( p_cat->psz_name, psz_cat ) ) + continue; + + for( j = 0; j < p_cat->i_infos ; j++ ) + { + if( !strcmp( p_cat->pp_infos[j]->psz_name, psz_name ) ) + { + char *psz_ret = strdup( p_cat->pp_infos[j]->psz_value ); + vlc_mutex_unlock( &p_i->lock ); + return psz_ret; + } + } + } + vlc_mutex_unlock( &p_i->lock ); + return strdup( "" ); +} + +static void input_item_Destroy ( gc_object_t *p_this ) +{ + vlc_object_t *p_obj = (vlc_object_t *)p_this->p_destructor_arg; + libvlc_priv_t *priv = libvlc_priv (p_obj->p_libvlc); + input_item_t *p_input = (input_item_t *) p_this; + int i; + + input_item_Clean( p_input ); + + vlc_object_lock( p_obj->p_libvlc ); + + ARRAY_BSEARCH( priv->input_items,->i_id, int, p_input->i_id, i); + if( i != -1 ) + ARRAY_REMOVE( priv->input_items, i); + + vlc_object_unlock( p_obj->p_libvlc ); + + free( p_input ); +} + +int input_item_AddOpt( input_item_t *p_input, const char *psz_option, + unsigned flags ) +{ + int err = VLC_SUCCESS; + + if( psz_option == NULL ) + return VLC_EGENERIC; + + vlc_mutex_lock( &p_input->lock ); + if (flags & VLC_INPUT_OPTION_UNIQUE) + { + for (int i = 0 ; i < p_input->i_options; i++) + if( !strcmp( p_input->ppsz_options[i], psz_option ) ) + goto out; + } + + uint8_t *flagv = realloc (p_input->optflagv, p_input->optflagc + 1); + if (flagv == NULL) + { + err = VLC_ENOMEM; + goto out; + } + p_input->optflagv = flagv; + flagv[p_input->optflagc++] = flags; + + INSERT_ELEM( p_input->ppsz_options, p_input->i_options, + p_input->i_options, strdup( psz_option ) ); +out: + vlc_mutex_unlock( &p_input->lock ); + return err; +} + +int input_item_AddInfo( input_item_t *p_i, + const char *psz_cat, + const char *psz_name, + const char *psz_format, ... ) +{ + va_list args; + int i; + info_t *p_info = NULL; + info_category_t *p_cat = NULL ; + + vlc_mutex_lock( &p_i->lock ); + + for( i = 0 ; i < p_i->i_categories ; i ++ ) + { + if( !strcmp( p_i->pp_categories[i]->psz_name, psz_cat ) ) + { + p_cat = p_i->pp_categories[i]; + break; + } + } + if( !p_cat ) + { + if( !(p_cat = (info_category_t *)malloc( sizeof(info_category_t) )) ) + { + vlc_mutex_unlock( &p_i->lock ); + return VLC_ENOMEM; + } + p_cat->psz_name = strdup( psz_cat ); + p_cat->i_infos = 0; + p_cat->pp_infos = 0; + INSERT_ELEM( p_i->pp_categories, p_i->i_categories, p_i->i_categories, + p_cat ); + } + + for( i = 0; i< p_cat->i_infos; i++ ) + { + if( !strcmp( p_cat->pp_infos[i]->psz_name, psz_name ) ) + { + p_info = p_cat->pp_infos[i]; + break; + } + } + + if( !p_info ) + { + if( ( p_info = (info_t *)malloc( sizeof( info_t ) ) ) == NULL ) + { + vlc_mutex_unlock( &p_i->lock ); + return VLC_ENOMEM; + } + INSERT_ELEM( p_cat->pp_infos, p_cat->i_infos, p_cat->i_infos, p_info ); + p_info->psz_name = strdup( psz_name ); + } + else + { + free( p_info->psz_value ); + } + + va_start( args, psz_format ); + if( vasprintf( &p_info->psz_value, psz_format, args) == -1 ) + p_info->psz_value = NULL; + va_end( args ); + + vlc_mutex_unlock( &p_i->lock ); + + return p_info->psz_value ? VLC_SUCCESS : VLC_ENOMEM; +} + +input_item_t *__input_item_GetById( vlc_object_t *p_obj, int i_id ) +{ + libvlc_priv_t *priv = libvlc_priv (p_obj->p_libvlc); + input_item_t * p_ret = NULL; + int i; + + vlc_object_lock( p_obj->p_libvlc ); + + ARRAY_BSEARCH( priv->input_items, ->i_id, int, i_id, i); + if( i != -1 ) + p_ret = ARRAY_VAL( priv->input_items, i); + + vlc_object_unlock( p_obj->p_libvlc ); + + return p_ret; +} + +input_item_t *__input_item_NewExt( vlc_object_t *p_obj, const char *psz_uri, + const char *psz_name, + int i_options, + const char *const *ppsz_options, + mtime_t i_duration ) +{ + return input_item_NewWithType( p_obj, psz_uri, psz_name, + i_options, ppsz_options, + i_duration, ITEM_TYPE_UNKNOWN ); +} + + +input_item_t *input_item_NewWithType( vlc_object_t *p_obj, const char *psz_uri, + const char *psz_name, + int i_options, + const char *const *ppsz_options, + mtime_t i_duration, + int i_type ) +{ + libvlc_priv_t *priv = libvlc_priv (p_obj->p_libvlc); + + DECMALLOC_NULL( p_input, input_item_t ); + + input_item_Init( p_obj, p_input ); + vlc_gc_init( p_input, input_item_Destroy, (void *)p_obj->p_libvlc ); + + vlc_object_lock( p_obj->p_libvlc ); + p_input->i_id = ++priv->i_last_input_id; + ARRAY_APPEND( priv->input_items, p_input ); + vlc_object_unlock( p_obj->p_libvlc ); + + p_input->b_fixed_name = false; + + if( psz_uri ) + p_input->psz_uri = strdup( psz_uri ); + else + p_input->psz_uri = NULL; + + p_input->i_type = i_type; + p_input->b_prefers_tree = false; + + if( p_input->i_type == ITEM_TYPE_UNKNOWN ) + GuessType( p_input ); + + if( psz_name != NULL ) + p_input->psz_name = strdup( psz_name ); + else if( p_input->i_type == ITEM_TYPE_FILE && p_input->psz_uri ) + { + const char *psz_filename = strrchr( p_input->psz_uri, DIR_SEP_CHAR ); + if( psz_filename && *psz_filename == DIR_SEP_CHAR ) + psz_filename++; + p_input->psz_name = strdup( psz_filename && *psz_filename + ? psz_filename : p_input->psz_uri ); + } + else + p_input->psz_name = p_input->psz_uri ? strdup( p_input->psz_uri ) : NULL; + + p_input->i_duration = i_duration; + + for( int i = 0; i < i_options; i++ ) + input_item_AddOption( p_input, ppsz_options[i] ); + return p_input; +} + +/* Guess the type of the item using the beginning of the mrl */ +static void GuessType( input_item_t *p_item) +{ + int i; + static struct { const char *psz_search; int i_type; } types_array[] = + { + { "http", ITEM_TYPE_NET }, + { "dvd", ITEM_TYPE_DISC }, + { "cdda", ITEM_TYPE_CDDA }, + { "mms", ITEM_TYPE_NET }, + { "rtsp", ITEM_TYPE_NET }, + { "udp", ITEM_TYPE_NET }, + { "rtp", ITEM_TYPE_NET }, + { "vcd", ITEM_TYPE_DISC }, + { "v4l", ITEM_TYPE_CARD }, + { "dshow", ITEM_TYPE_CARD }, + { "pvr", ITEM_TYPE_CARD }, + { "dvb", ITEM_TYPE_CARD }, + { "qpsk", ITEM_TYPE_CARD }, + { "sdp", ITEM_TYPE_NET }, + { NULL, 0 } + }; + + if( !p_item->psz_uri ) + { + p_item->i_type = ITEM_TYPE_FILE; + return; + } + + for( i = 0; types_array[i].psz_search != NULL; i++ ) + { + if( !strncmp( p_item->psz_uri, types_array[i].psz_search, + strlen( types_array[i].psz_search ) ) ) + { + p_item->i_type = types_array[i].i_type; + return; + } + } + p_item->i_type = ITEM_TYPE_FILE; +} diff --git a/VLC/input/mem_stream.c b/VLC/input/mem_stream.c new file mode 100644 index 0000000..3d6b429 --- /dev/null +++ b/VLC/input/mem_stream.c @@ -0,0 +1,161 @@ +/***************************************************************************** + * mem_stream.c: stream_t wrapper around memory buffer + ***************************************************************************** + * Copyright (C) 1999-2004 the VideoLAN team + * $Id$ + * + * Authors: Sigmund Augdal Helberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#include "input_internal.h" + +struct stream_sys_t +{ + bool i_preserve_memory; + int64_t i_pos; /* Current reading offset */ + int64_t i_size; + uint8_t *p_buffer; + +}; + +static int Read ( stream_t *, void *p_read, unsigned int i_read ); +static int Peek ( stream_t *, const uint8_t **pp_peek, unsigned int i_read ); +static int Control( stream_t *, int i_query, va_list ); +static void Delete ( stream_t * ); + +/** + * Create a stream from a memory buffer + * + * \param p_this the calling vlc_object + * \param p_buffer the memory buffer for the stream + * \param i_buffer the size of the buffer + * \param i_preserve_memory if this is set to false the memory buffer + * pointed to by p_buffer is freed on stream_Destroy + */ +stream_t *__stream_MemoryNew( vlc_object_t *p_this, uint8_t *p_buffer, + int64_t i_size, bool i_preserve_memory ) +{ + stream_t *s = vlc_stream_create( p_this ); + stream_sys_t *p_sys; + + if( !s ) return NULL; + + s->p_sys = p_sys = malloc( sizeof( stream_sys_t ) ); + p_sys->i_pos = 0; + p_sys->i_size = i_size; + p_sys->p_buffer = p_buffer; + p_sys->i_preserve_memory = i_preserve_memory; + + s->pf_read = Read; + s->pf_peek = Peek; + s->pf_control = Control; + s->pf_destroy = Delete; + + s->i_char_width = 1; + s->b_little_endian = false; + vlc_object_attach( s, p_this ); + + return s; +} + +static void Delete( stream_t *s ) +{ + if( !s->p_sys->i_preserve_memory ) free( s->p_sys->p_buffer ); + free( s->p_sys ); + vlc_object_detach( s ); + vlc_object_release( s ); +} + +/**************************************************************************** + * AStreamControl: + ****************************************************************************/ +static int Control( stream_t *s, int i_query, va_list args ) +{ + stream_sys_t *p_sys = s->p_sys; + + bool *p_bool; + int64_t *pi_64, i_64; + int i_int; + + switch( i_query ) + { + case STREAM_GET_SIZE: + pi_64 = (int64_t*)va_arg( args, int64_t * ); + *pi_64 = p_sys->i_size; + break; + + case STREAM_CAN_SEEK: + p_bool = (bool*)va_arg( args, bool * ); + *p_bool = true; + break; + + case STREAM_CAN_FASTSEEK: + p_bool = (bool*)va_arg( args, bool * ); + *p_bool = true; + break; + + case STREAM_GET_POSITION: + pi_64 = (int64_t*)va_arg( args, int64_t * ); + *pi_64 = p_sys->i_pos; + break; + + case STREAM_SET_POSITION: + i_64 = (int64_t)va_arg( args, int64_t ); + i_64 = __MAX( i_64, 0 ); + i_64 = __MIN( i_64, s->p_sys->i_size ); + p_sys->i_pos = i_64; + break; + + case STREAM_GET_MTU: + case STREAM_GET_CONTENT_TYPE: + return VLC_EGENERIC; + + case STREAM_CONTROL_ACCESS: + i_int = (int) va_arg( args, int ); + msg_Err( s, "Hey, what are you thinking ?" + "DON'T USE STREAM_CONTROL_ACCESS !!!" ); + return VLC_EGENERIC; + + default: + msg_Err( s, "invalid stream_vaControl query=0x%x", i_query ); + return VLC_EGENERIC; + } + return VLC_SUCCESS; +} + +static int Read( stream_t *s, void *p_read, unsigned int i_read ) +{ + stream_sys_t *p_sys = s->p_sys; + int i_res = __MIN( i_read, p_sys->i_size - p_sys->i_pos ); + memcpy( p_read, p_sys->p_buffer + p_sys->i_pos, i_res ); + p_sys->i_pos += i_res; + return i_res; +} + +static int Peek( stream_t *s, const uint8_t **pp_peek, unsigned int i_read ) +{ + stream_sys_t *p_sys = s->p_sys; + int i_res = __MIN( i_read, p_sys->i_size - p_sys->i_pos ); + *pp_peek = p_sys->p_buffer + p_sys->i_pos; + return i_res; +} diff --git a/VLC/input/meta.c b/VLC/input/meta.c new file mode 100644 index 0000000..51c0cce --- /dev/null +++ b/VLC/input/meta.c @@ -0,0 +1,535 @@ +/***************************************************************************** + * meta.c : Metadata handling + ***************************************************************************** + * Copyright (C) 1998-2004 the VideoLAN team + * $Id$ + * + * Authors: Antoine Cellerier + * Clément Stenac +#include /* PATH_MAX */ +#include + + +# include + + +#include "libvlc.h" + +const char * +input_MetaTypeToLocalizedString( vlc_meta_type_t meta_type ) +{ + switch( meta_type ) + { + case vlc_meta_Title: return _("Title"); + case vlc_meta_Artist: return _("Artist"); + case vlc_meta_Genre: return _("Genre"); + case vlc_meta_Copyright: return _("Copyright"); + case vlc_meta_Album: return _("Album"); + case vlc_meta_TrackNumber: return _("Track number"); + case vlc_meta_Description: return _("Description"); + case vlc_meta_Rating: return _("Rating"); + case vlc_meta_Date: return _("Date"); + case vlc_meta_Setting: return _("Setting"); + case vlc_meta_URL: return _("URL"); + case vlc_meta_Language: return _("Language"); + case vlc_meta_NowPlaying: return _("Now Playing"); + case vlc_meta_Publisher: return _("Publisher"); + case vlc_meta_EncodedBy: return _("Encoded by"); + case vlc_meta_ArtworkURL: return _("Artwork URL"); + case vlc_meta_TrackID: return _("Track ID"); + + default: abort(); + } +}; + +#define input_FindArtInCache(a,b) __input_FindArtInCache(VLC_OBJECT(a),b) +static int __input_FindArtInCache( vlc_object_t *, input_item_t *p_item ); + +/* Return codes: + * 0 : Art is in cache or is a local file + * 1 : Art found, need to download + * -X : Error/not found + */ +int input_ArtFind( playlist_t *p_playlist, input_item_t *p_item ) +{ + int i_ret = VLC_EGENERIC; + module_t *p_module; + char *psz_title, *psz_artist, *psz_album; + + psz_artist = input_item_GetArtist( p_item ); + psz_album = input_item_GetAlbum( p_item ); + psz_title = input_item_GetTitle( p_item ); + if(!psz_title) + psz_title = input_item_GetName( p_item ); + + if( !psz_title && !psz_artist && !psz_album ) + return VLC_EGENERIC; + + free( psz_title ); + + /* If we already checked this album in this session, skip */ + if( psz_artist && psz_album ) + { + FOREACH_ARRAY( playlist_album_t album, p_playlist->p_fetcher->albums ) + if( !strcmp( album.psz_artist, psz_artist ) && + !strcmp( album.psz_album, psz_album ) ) + { + msg_Dbg( p_playlist, " %s - %s has already been searched", + psz_artist, psz_album ); + /* TODO-fenrir if we cache art filename too, we can go faster */ + free( psz_artist ); + free( psz_album ); + if( album.b_found ) + { + if( !strncmp( album.psz_arturl, "file://", 7 ) ) + input_item_SetArtURL( p_item, album.psz_arturl ); + else /* Actually get URL from cache */ + input_FindArtInCache( p_playlist, p_item ); + return 0; + } + else + { + return VLC_EGENERIC; + } + } + FOREACH_END(); + } + free( psz_artist ); + free( psz_album ); + + input_FindArtInCache( p_playlist, p_item ); + + char *psz_arturl = input_item_GetArtURL( p_item ); + if( psz_arturl ) + { + /* We already have an URL */ + if( !strncmp( psz_arturl, "file://", strlen( "file://" ) ) ) + { + free( psz_arturl ); + return 0; /* Art is in cache, no need to go further */ + } + + free( psz_arturl ); + + /* Art need to be put in cache */ + return 1; + } + + PL_LOCK; + p_playlist->p_private = p_item; + psz_album = input_item_GetAlbum( p_item ); + psz_artist = input_item_GetArtist( p_item ); + psz_title = input_item_GetTitle( p_item ); + if( !psz_title ) + psz_title = input_item_GetName( p_item ); + + if( psz_album && psz_artist ) + { + msg_Dbg( p_playlist, "searching art for %s - %s", + psz_artist, psz_album ); + } + else + { + msg_Dbg( p_playlist, "searching art for %s", + psz_title ); + } + free( psz_title ); + + p_module = module_Need( p_playlist, "art finder", 0, false ); + + if( p_module ) + i_ret = 1; + else + msg_Dbg( p_playlist, "unable to find art" ); + + /* Record this album */ + if( psz_artist && psz_album ) + { + playlist_album_t a; + a.psz_artist = psz_artist; + a.psz_album = psz_album; + a.psz_arturl = input_item_GetArtURL( p_item ); + a.b_found = (i_ret == VLC_EGENERIC ? false : true ); + ARRAY_APPEND( p_playlist->p_fetcher->albums, a ); + } + else + { + free( psz_artist ); + free( psz_album ); + } + + if( p_module ) + module_Unneed( p_playlist, p_module ); + p_playlist->p_private = NULL; + PL_UNLOCK; + + return i_ret; +} + +static void ArtCacheCreateDir( const char *psz_dir ) +{ + char newdir[strlen( psz_dir ) + 1]; + strcpy( newdir, psz_dir ); + char * psz_newdir = newdir; + char * psz = psz_newdir; + + while( *psz ) + { + while( *psz && *psz != DIR_SEP_CHAR) psz++; + if( !*psz ) break; + *psz = 0; + if( !EMPTY_STR( psz_newdir ) ) + utf8_mkdir( psz_newdir, 0700 ); + *psz = DIR_SEP_CHAR; + psz++; + } + utf8_mkdir( psz_dir, 0700 ); +} + +static char * ArtCacheGetSanitizedFileName( const char *psz ) +{ + char *dup = strdup(psz); + int i; + + filename_sanitize( dup ); + + /* Doesn't create a filename with invalid characters + * TODO: several filesystems forbid several characters: list them all + */ + for( i = 0; dup[i] != '\0'; i++ ) + { + if( dup[i] == DIR_SEP_CHAR ) + dup[i] = ' '; + } + return dup; +} + +#define ArtCacheGetDirPath(a,b,c,d,e) __ArtCacheGetDirPath(VLC_OBJECT(a),b,c,d,e) +static void __ArtCacheGetDirPath( vlc_object_t *p_obj, + char *psz_dir, + const char *psz_title, + const char *psz_artist, const char *psz_album ) +{ + (void)p_obj; + char *psz_cachedir = config_GetCacheDir(); + + if( !EMPTY_STR(psz_artist) && !EMPTY_STR(psz_album) ) + { + char * psz_album_sanitized = ArtCacheGetSanitizedFileName( psz_album ); + char * psz_artist_sanitized = ArtCacheGetSanitizedFileName( psz_artist ); + snprintf( psz_dir, PATH_MAX, "%s" DIR_SEP + "art" DIR_SEP "artistalbum" DIR_SEP "%s" DIR_SEP "%s", + psz_cachedir, psz_artist_sanitized, psz_album_sanitized ); + free( psz_album_sanitized ); + free( psz_artist_sanitized ); + } + else + { + char * psz_title_sanitized = ArtCacheGetSanitizedFileName( psz_title ); + snprintf( psz_dir, PATH_MAX, "%s" DIR_SEP + "art" DIR_SEP "title" DIR_SEP "%s", + psz_cachedir, psz_title_sanitized ); + free( psz_title_sanitized ); + } + free( psz_cachedir ); +} + + + +#define ArtCacheGetFilePath(a,b,c,d,e,f) __ArtCacheGetFilePath(VLC_OBJECT(a),b,c,d,e,f) +static void __ArtCacheGetFilePath( vlc_object_t *p_obj, + char * psz_filename, + const char *psz_title, + const char *psz_artist, const char *psz_album, + const char *psz_extension ) +{ + char psz_dir[PATH_MAX+1]; + char * psz_ext; + ArtCacheGetDirPath( p_obj, psz_dir, psz_title, psz_artist, psz_album ); + + if( psz_extension ) + { + psz_ext = strndup( psz_extension, 6 ); + filename_sanitize( psz_ext ); + } + else psz_ext = strdup( "" ); + + snprintf( psz_filename, PATH_MAX, "file://%s" DIR_SEP "art%s", + psz_dir, psz_ext ); + + free( psz_ext ); +} + +static int __input_FindArtInCache( vlc_object_t *p_obj, input_item_t *p_item ) +{ + char *psz_artist; + char *psz_album; + char *psz_title; + char psz_dirpath[PATH_MAX+1]; + char psz_filepath[PATH_MAX+1]; + char * psz_filename; + DIR * p_dir; + + psz_artist = input_item_GetArtist( p_item ); + psz_album = input_item_GetAlbum( p_item ); + psz_title = input_item_GetTitle( p_item ); + if( !psz_title ) psz_title = input_item_GetName( p_item ); + + if( !psz_title && ( !psz_album || !psz_artist ) ) + { + free( psz_artist ); + free( psz_album ); + free( psz_title ); + return VLC_EGENERIC; + } + + ArtCacheGetDirPath( p_obj, psz_dirpath, psz_title, + psz_artist, psz_album ); + + free( psz_artist ); + free( psz_album ); + free( psz_title ); + + /* Check if file exists */ + p_dir = utf8_opendir( psz_dirpath ); + if( !p_dir ) + return VLC_EGENERIC; + + while( (psz_filename = utf8_readdir( p_dir )) ) + { + if( !strncmp( psz_filename, "art", 3 ) ) + { + snprintf( psz_filepath, PATH_MAX, "file://%s" DIR_SEP "%s", + psz_dirpath, psz_filename ); + input_item_SetArtURL( p_item, psz_filepath ); + free( psz_filename ); + closedir( p_dir ); + return VLC_SUCCESS; + } + free( psz_filename ); + } + + /* Not found */ + closedir( p_dir ); + return VLC_EGENERIC; +} + +/** + * Download the art using the URL or an art downloaded + * This function should be called only if data is not already in cache + */ +int input_DownloadAndCacheArt( playlist_t *p_playlist, input_item_t *p_item ) +{ + int i_status = VLC_EGENERIC; + stream_t *p_stream; + char psz_filename[PATH_MAX+1]; + char *psz_artist = NULL; + char *psz_album = NULL; + char *psz_title = NULL; + char *psz_arturl; + char *psz_type; + + psz_artist = input_item_GetArtist( p_item ); + psz_album = input_item_GetAlbum( p_item ); + psz_title = input_item_GetTitle( p_item ); + if( !psz_title ) + psz_title = input_item_GetName( p_item ); + + if( !psz_title && (!psz_artist || !psz_album) ) + { + free( psz_title ); + free( psz_album ); + free( psz_artist ); + return VLC_EGENERIC; + } + + psz_arturl = input_item_GetArtURL( p_item ); + assert( !EMPTY_STR( psz_arturl ) ); + + if( !strncmp( psz_arturl , "file://", 7 ) ) + { + msg_Dbg( p_playlist, "Album art is local file, no need to cache" ); + free( psz_arturl ); + return VLC_SUCCESS; + } + else if( !strncmp( psz_arturl , "APIC", 4 ) ) + { + msg_Warn( p_playlist, "APIC fetch not supported yet" ); + free( psz_arturl ); + return VLC_EGENERIC; + } + + psz_type = strrchr( psz_arturl, '.' ); + if( psz_type && strlen( psz_type ) > 5 ) + psz_type = NULL; /* remove extension if it's > to 4 characters */ + + /* Warning: psz_title, psz_artist, psz_album may change in ArtCache*() */ + + ArtCacheGetDirPath( p_playlist, psz_filename, psz_title, psz_artist, + psz_album ); + ArtCacheCreateDir( psz_filename ); + ArtCacheGetFilePath( p_playlist, psz_filename, psz_title, psz_artist, + psz_album, psz_type ); + + free( psz_artist ); + free( psz_album ); + free( psz_title ); + + p_stream = stream_UrlNew( p_playlist, psz_arturl ); + if( p_stream ) + { + uint8_t p_buffer[65536]; + long int l_read; + FILE *p_file = utf8_fopen( psz_filename+7, "w" ); + if( p_file == NULL ) { + msg_Err( p_playlist, "Unable write album art in %s", + psz_filename + 7 ); + free( psz_arturl ); + return VLC_EGENERIC; + } + int err = 0; + while( ( l_read = stream_Read( p_stream, p_buffer, sizeof (p_buffer) ) ) ) + { + if( fwrite( p_buffer, l_read, 1, p_file ) != 1 ) + { + err = errno; + break; + } + } + if( fclose( p_file ) && !err ) + err = errno; + stream_Delete( p_stream ); + + if( err ) + { + errno = err; + msg_Err( p_playlist, "%s: %m", psz_filename ); + } + else + msg_Dbg( p_playlist, "album art saved to %s\n", psz_filename ); + + input_item_SetArtURL( p_item, psz_filename ); + i_status = VLC_SUCCESS; + } + free( psz_arturl ); + return i_status; +} + +void input_ExtractAttachmentAndCacheArt( input_thread_t *p_input ) +{ + input_item_t *p_item = p_input->p->input.p_item; + const char *psz_arturl; + const char *psz_artist = NULL; + const char *psz_album = NULL; + const char *psz_title = NULL; + char *psz_type = NULL; + char psz_filename[PATH_MAX+1]; + FILE *f; + input_attachment_t *p_attachment; + struct stat s; + int i_idx; + + /* TODO-fenrir merge input_ArtFind with download and make it set the flags FETCH + * and then set it here to to be faster */ + + psz_arturl = vlc_meta_Get( p_item->p_meta, vlc_meta_ArtworkURL ); + + if( !psz_arturl || strncmp( psz_arturl, "attachment://", strlen("attachment://") ) ) + { + msg_Err( p_input, "internal input error with input_ExtractAttachmentAndCacheArt" ); + return; + } + + if( input_item_IsArtFetched( p_item ) ) + { + /* XXX Weird, we should not have end up with attachment:// art url unless there is a race + * condition */ + msg_Warn( p_input, "internal input error with input_ExtractAttachmentAndCacheArt" ); + input_FindArtInCache( p_input, p_item ); + return; + } + + /* */ + for( i_idx = 0, p_attachment = NULL; i_idx < p_input->p->i_attachment; i_idx++ ) + { + if( !strcmp( p_input->p->attachment[i_idx]->psz_name, + &psz_arturl[strlen("attachment://")] ) ) + { + p_attachment = p_input->p->attachment[i_idx]; + break; + } + } + if( !p_attachment || p_attachment->i_data <= 0 ) + { + msg_Warn( p_input, "internal input error with input_ExtractAttachmentAndCacheArt" ); + return; + } + + psz_artist = vlc_meta_Get( p_item->p_meta, vlc_meta_Artist ); + psz_album = vlc_meta_Get( p_item->p_meta, vlc_meta_Album ); + psz_title = vlc_meta_Get( p_item->p_meta, vlc_meta_Title ); + if( !strcmp( p_attachment->psz_mime, "image/jpeg" ) ) + psz_type = strdup( ".jpg" ); + else if( !strcmp( p_attachment->psz_mime, "image/png" ) ) + psz_type = strdup( ".png" ); + + if( !psz_title ) + psz_title = p_item->psz_name; + + if( (!psz_artist || !psz_album ) && !psz_title ) + return; + + ArtCacheGetDirPath( p_input, psz_filename, psz_title, psz_artist, psz_album ); + ArtCacheCreateDir( psz_filename ); + ArtCacheGetFilePath( p_input, psz_filename, psz_title, psz_artist, psz_album, psz_type ); + free( psz_type ); + + /* Check if we already dumped it */ + if( !utf8_stat( psz_filename+7, &s ) ) + { + vlc_meta_Set( p_item->p_meta, vlc_meta_ArtworkURL, psz_filename ); + return; + } + + f = utf8_fopen( psz_filename+7, "w" ); + if( f ) + { + if( fwrite( p_attachment->p_data, p_attachment->i_data, 1, f ) != 1 ) + msg_Err( p_input, "%s: %m", psz_filename ); + else + { + msg_Dbg( p_input, "album art saved to %s\n", psz_filename ); + vlc_meta_Set( p_item->p_meta, vlc_meta_ArtworkURL, psz_filename ); + } + fclose( f ); + } +} diff --git a/VLC/input/stream.c b/VLC/input/stream.c new file mode 100644 index 0000000..e05a990 --- /dev/null +++ b/VLC/input/stream.c @@ -0,0 +1,2137 @@ +/***************************************************************************** + * stream.c + ***************************************************************************** + * Copyright (C) 1999-2004 the VideoLAN team + * $Id$ + * + * Authors: Laurent Aimar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#include + +#include "input_internal.h" + +#undef STREAM_DEBUG + +/* TODO: + * - tune the 2 methods (block/stream) + * - compute cost for seek + * - improve stream mode seeking with closest segments + * - ... + * - Maybe remove (block/stream) in favour of immediate + */ + +/* Two methods: + * - using pf_block + * One linked list of data read + * - using pf_read + * More complex scheme using mutliple track to avoid seeking + * - using directly the access (only indirection for peeking). + * This method is known to introduce much less latency. + * It should probably defaulted (instead of the stream method (2)). + */ + +/* How many tracks we have, currently only used for stream mode */ +#ifdef OPTIMIZE_MEMORY +# define STREAM_CACHE_TRACK 1 + /* Max size of our cache 128Ko per track */ +# define STREAM_CACHE_SIZE (STREAM_CACHE_TRACK*1024*128) +#else +# define STREAM_CACHE_TRACK 3 + /* Max size of our cache 4Mo per track */ +# define STREAM_CACHE_SIZE (4*STREAM_CACHE_TRACK*1024*1024) +#endif + +/* How many data we try to prebuffer */ +#define STREAM_CACHE_PREBUFFER_SIZE (32767) +/* Maximum time we take to pre-buffer */ +#define STREAM_CACHE_PREBUFFER_LENGTH (100*1000) + +/* Method1: Simple, for pf_block. + * We get blocks and put them in the linked list. + * We release blocks once the total size is bigger than CACHE_BLOCK_SIZE + */ +#define STREAM_DATA_WAIT 40000 /* Time between before a pf_block retry */ + +/* Method2: A bit more complex, for pf_read + * - We use ring buffers, only one if unseekable, all if seekable + * - Upon seek date current ring, then search if one ring match the pos, + * yes: switch to it, seek the access to match the end of the ring + * no: search the ring with i_end the closer to i_pos, + * if close enough, read data and use this ring + * else use the oldest ring, seek and use it. + * + * TODO: - with access non seekable: use all space available for only one ring, but + * we have to support seekable/non-seekable switch on the fly. + * - compute a good value for i_read_size + * - ? + */ +#define STREAM_READ_ATONCE 32767 +#define STREAM_CACHE_TRACK_SIZE (STREAM_CACHE_SIZE/STREAM_CACHE_TRACK) + +typedef struct +{ + int64_t i_date; + + int64_t i_start; + int64_t i_end; + + uint8_t *p_buffer; + +} stream_track_t; + +typedef struct +{ + char *psz_path; + int64_t i_size; + +} access_entry_t; + +typedef enum stream_read_method_t +{ + Immediate, + Block, + Stream +} stream_read_method_t; + +struct stream_sys_t +{ + access_t *p_access; + + stream_read_method_t method; /* method to use */ + + int64_t i_pos; /* Current reading offset */ + + /* Method 1: pf_block */ + struct + { + int64_t i_start; /* Offset of block for p_first */ + int64_t i_offset; /* Offset for data in p_current */ + block_t *p_current; /* Current block */ + + int i_size; /* Total amount of data in the list */ + block_t *p_first; + block_t **pp_last; + + } block; + + /* Method 2: for pf_read */ + struct + { + int i_offset; /* Buffer offset in the current track */ + int i_tk; /* Current track */ + stream_track_t tk[STREAM_CACHE_TRACK]; + + /* Global buffer */ + uint8_t *p_buffer; + + /* */ + int i_used; /* Used since last read */ + int i_read_size; + + } stream; + + /* Method 3: for pf_read */ + struct + { + int64_t i_end; + uint8_t *p_buffer; + } immediate; + + /* Peek temporary buffer */ + unsigned int i_peek; + uint8_t *p_peek; + + /* Stat for both method */ + struct + { + bool b_fastseek; /* From access */ + + /* Stat about reading data */ + int64_t i_read_count; + int64_t i_bytes; + int64_t i_read_time; + + /* Stat about seek */ + int i_seek_count; + int64_t i_seek_time; + + } stat; + + /* Streams list */ + int i_list; + access_entry_t **list; + int i_list_index; + access_t *p_list_access; + + /* Preparse mode ? */ + bool b_quick; +}; + +/* Method 1: */ +static int AStreamReadBlock( stream_t *s, void *p_read, unsigned int i_read ); +static int AStreamPeekBlock( stream_t *s, const uint8_t **p_peek, unsigned int i_read ); +static int AStreamSeekBlock( stream_t *s, int64_t i_pos ); +static void AStreamPrebufferBlock( stream_t *s ); +static block_t *AReadBlock( stream_t *s, bool *pb_eof ); + +/* Method 2 */ +static int AStreamReadStream( stream_t *s, void *p_read, unsigned int i_read ); +static int AStreamPeekStream( stream_t *s, const uint8_t **pp_peek, unsigned int i_read ); +static int AStreamSeekStream( stream_t *s, int64_t i_pos ); +static void AStreamPrebufferStream( stream_t *s ); +static int AReadStream( stream_t *s, void *p_read, unsigned int i_read ); + +/* Method 3 */ +static int AStreamReadImmediate( stream_t *s, void *p_read, unsigned int i_read ); +static int AStreamPeekImmediate( stream_t *s, const uint8_t **pp_peek, unsigned int i_read ); +static int AStreamSeekImmediate( stream_t *s, int64_t i_pos ); + +/* Common */ +static int AStreamControl( stream_t *s, int i_query, va_list ); +static void AStreamDestroy( stream_t *s ); +static void UStreamDestroy( stream_t *s ); +static int ASeek( stream_t *s, int64_t i_pos ); + +/**************************************************************************** + * Method 3 helpers: + ****************************************************************************/ + +static inline int64_t stream_buffered_size( stream_t *s ) +{ + return s->p_sys->immediate.i_end; +} + +static inline void stream_buffer_empty( stream_t *s, int length ) +{ + length = __MAX( stream_buffered_size( s ), length ); + if( length ) + { + memmove( s->p_sys->immediate.p_buffer, + s->p_sys->immediate.p_buffer + length, + stream_buffered_size( s ) - length ); + } + s->p_sys->immediate.i_end -= length; +} + +static inline void stream_buffer_fill( stream_t *s, int length ) +{ + s->p_sys->immediate.i_end += length; +} + +static inline uint8_t * stream_buffer( stream_t *s ) +{ + return s->p_sys->immediate.p_buffer; +} + +/**************************************************************************** + * stream_UrlNew: create a stream from a access + ****************************************************************************/ +stream_t *__stream_UrlNew( vlc_object_t *p_parent, const char *psz_url ) +{ + const char *psz_access, *psz_demux; + char *psz_path; + access_t *p_access; + stream_t *p_res; + + if( !psz_url ) + return NULL; + + char psz_dup[strlen( psz_url ) + 1]; + strcpy( psz_dup, psz_url ); + input_SplitMRL( &psz_access, &psz_demux, &psz_path, psz_dup ); + + /* Now try a real access */ + p_access = access_New( p_parent, psz_access, psz_demux, psz_path ); + + if( p_access == NULL ) + { + msg_Err( p_parent, "no suitable access module for `%s'", psz_url ); + return NULL; + } + + if( !( p_res = stream_AccessNew( p_access, true ) ) ) + { + access_Delete( p_access ); + return NULL; + } + + p_res->pf_destroy = UStreamDestroy; + return p_res; +} + +stream_t *stream_AccessNew( access_t *p_access, bool b_quick ) +{ + stream_t *s = vlc_stream_create( VLC_OBJECT(p_access) ); + stream_sys_t *p_sys; + char *psz_list = NULL; + + if( !s ) return NULL; + + /* Attach it now, needed for b_die */ + vlc_object_attach( s, p_access ); + + s->pf_read = NULL; /* Set up later */ + s->pf_peek = NULL; + s->pf_control = AStreamControl; + s->pf_destroy = AStreamDestroy; + + s->p_sys = p_sys = malloc( sizeof( stream_sys_t ) ); + if( p_sys == NULL ) + goto error; + + /* UTF16 and UTF32 text file conversion */ + s->i_char_width = 1; + s->b_little_endian = false; + s->conv = (vlc_iconv_t)(-1); + + /* Common field */ + p_sys->p_access = p_access; + if( p_access->pf_block ) + p_sys->method = Block; + else if (var_CreateGetBool( s, "use-stream-immediate")) + p_sys->method = Immediate; + else + p_sys->method = Stream; + + p_sys->i_pos = p_access->info.i_pos; + + /* Stats */ + access_Control( p_access, ACCESS_CAN_FASTSEEK, &p_sys->stat.b_fastseek ); + p_sys->stat.i_bytes = 0; + p_sys->stat.i_read_time = 0; + p_sys->stat.i_read_count = 0; + p_sys->stat.i_seek_count = 0; + p_sys->stat.i_seek_time = 0; + + p_sys->i_list = 0; + p_sys->list = 0; + p_sys->i_list_index = 0; + p_sys->p_list_access = 0; + + p_sys->b_quick = b_quick; + + /* Get the additional list of inputs if any (for concatenation) */ + if( (psz_list = var_CreateGetString( s, "input-list" )) && *psz_list ) + { + access_entry_t *p_entry = malloc( sizeof(access_entry_t) ); + if( p_entry == NULL ) + goto error; + char *psz_name, *psz_parser = psz_name = psz_list; + + p_sys->p_list_access = p_access; + p_entry->i_size = p_access->info.i_size; + p_entry->psz_path = strdup( p_access->psz_path ); + if( p_entry->psz_path == NULL ) + { + free( p_entry ); + goto error; + } + TAB_APPEND( p_sys->i_list, p_sys->list, p_entry ); + msg_Dbg( p_access, "adding file `%s', (%"PRId64" bytes)", + p_entry->psz_path, p_access->info.i_size ); + + while( psz_name && *psz_name ) + { + psz_parser = strchr( psz_name, ',' ); + if( psz_parser ) *psz_parser = 0; + + psz_name = strdup( psz_name ); + if( psz_name ) + { + access_t *p_tmp = access_New( p_access, p_access->psz_access, + "", psz_name ); + + if( !p_tmp ) + { + psz_name = psz_parser; + if( psz_name ) psz_name++; + continue; + } + + msg_Dbg( p_access, "adding file `%s', (%"PRId64" bytes)", + psz_name, p_tmp->info.i_size ); + + p_entry = malloc( sizeof(access_entry_t) ); + if( p_entry == NULL ) + goto error; + p_entry->i_size = p_tmp->info.i_size; + p_entry->psz_path = psz_name; + TAB_APPEND( p_sys->i_list, p_sys->list, p_entry ); + + access_Delete( p_tmp ); + } + + psz_name = psz_parser; + if( psz_name ) psz_name++; + } + } + FREENULL( psz_list ); + + /* Peek */ + p_sys->i_peek = 0; + p_sys->p_peek = NULL; + + if( p_sys->method == Block ) + { + msg_Dbg( s, "Using AStream*Block" ); + s->pf_read = AStreamReadBlock; + s->pf_peek = AStreamPeekBlock; + + /* Init all fields of p_sys->block */ + p_sys->block.i_start = p_sys->i_pos; + p_sys->block.i_offset = 0; + p_sys->block.p_current = NULL; + p_sys->block.i_size = 0; + p_sys->block.p_first = NULL; + p_sys->block.pp_last = &p_sys->block.p_first; + + /* Do the prebuffering */ + AStreamPrebufferBlock( s ); + + if( p_sys->block.i_size <= 0 ) + { + msg_Err( s, "cannot pre fill buffer" ); + goto error; + } + } + else if (p_sys->method == Immediate) + { + msg_Dbg( s, "Using AStream*Immediate" ); + + s->pf_read = AStreamReadImmediate; + s->pf_peek = AStreamPeekImmediate; + + /* Allocate/Setup our tracks (useful to peek)*/ + p_sys->immediate.i_end = 0; + p_sys->immediate.p_buffer = malloc( STREAM_CACHE_SIZE ); + + msg_Dbg( s, "p_buffer %p-%p", p_sys->immediate.p_buffer, + p_sys->immediate.p_buffer + STREAM_CACHE_SIZE ); + + if( p_sys->immediate.p_buffer == NULL ) + { + msg_Err( s, "Out of memory when allocating stream cache (%d bytes)", + STREAM_CACHE_SIZE ); + goto error; + } + } + else /* ( p_sys->method == Stream ) */ + { + int i; + + msg_Dbg( s, "Using AStream*Stream" ); + + s->pf_read = AStreamReadStream; + s->pf_peek = AStreamPeekStream; + + /* Allocate/Setup our tracks */ + p_sys->stream.i_offset = 0; + p_sys->stream.i_tk = 0; + p_sys->stream.p_buffer = malloc( STREAM_CACHE_SIZE ); + if( p_sys->stream.p_buffer == NULL ) + { + msg_Err( s, "Out of memory when allocating stream cache (%d bytes)", + STREAM_CACHE_SIZE ); + goto error; + } + p_sys->stream.i_used = 0; + access_Control( p_access, ACCESS_GET_MTU, + &p_sys->stream.i_read_size ); + if( p_sys->stream.i_read_size <= 0 ) + p_sys->stream.i_read_size = STREAM_READ_ATONCE; + else if( p_sys->stream.i_read_size <= 256 ) + p_sys->stream.i_read_size = 256; + + for( i = 0; i < STREAM_CACHE_TRACK; i++ ) + { + p_sys->stream.tk[i].i_date = 0; + p_sys->stream.tk[i].i_start = p_sys->i_pos; + p_sys->stream.tk[i].i_end = p_sys->i_pos; + p_sys->stream.tk[i].p_buffer= + &p_sys->stream.p_buffer[i * STREAM_CACHE_TRACK_SIZE]; + } + + /* Do the prebuffering */ + AStreamPrebufferStream( s ); + + if( p_sys->stream.tk[p_sys->stream.i_tk].i_end <= 0 ) + { + msg_Err( s, "cannot pre fill buffer" ); + goto error; + } + } + + return s; + +error: + if( p_sys->method == Block ) + { + /* Nothing yet */ + } + else + { + free( p_sys->stream.p_buffer ); + } + while( p_sys->i_list > 0 ) + free( p_sys->list[--(p_sys->i_list)] ); + free( p_sys->list ); + free( psz_list ); + free( s->p_sys ); + vlc_object_detach( s ); + vlc_object_release( s ); + return NULL; +} + +/**************************************************************************** + * AStreamDestroy: + ****************************************************************************/ +static void AStreamDestroy( stream_t *s ) +{ + stream_sys_t *p_sys = s->p_sys; + + vlc_object_detach( s ); + + if( p_sys->method == Block ) block_ChainRelease( p_sys->block.p_first ); + else if ( p_sys->method == Immediate ) free( p_sys->immediate.p_buffer ); + else free( p_sys->stream.p_buffer ); + + free( p_sys->p_peek ); + + if( p_sys->p_list_access && p_sys->p_list_access != p_sys->p_access ) + access_Delete( p_sys->p_list_access ); + + while( p_sys->i_list-- ) + { + free( p_sys->list[p_sys->i_list]->psz_path ); + free( p_sys->list[p_sys->i_list] ); + } + + free( p_sys->list ); + free( p_sys ); + + vlc_object_release( s ); +} + +static void UStreamDestroy( stream_t *s ) +{ + access_t *p_access = (access_t *)s->p_parent; + AStreamDestroy( s ); + access_Delete( p_access ); +} + +/**************************************************************************** + * stream_AccessReset: + ****************************************************************************/ +void stream_AccessReset( stream_t *s ) +{ + stream_sys_t *p_sys = s->p_sys; + + p_sys->i_pos = p_sys->p_access->info.i_pos; + + if( p_sys->method == Block ) + { + block_ChainRelease( p_sys->block.p_first ); + + /* Init all fields of p_sys->block */ + p_sys->block.i_start = p_sys->i_pos; + p_sys->block.i_offset = 0; + p_sys->block.p_current = NULL; + p_sys->block.i_size = 0; + p_sys->block.p_first = NULL; + p_sys->block.pp_last = &p_sys->block.p_first; + + /* Do the prebuffering */ + AStreamPrebufferBlock( s ); + } + else if( p_sys->method == Immediate ) + { + stream_buffer_empty( s, stream_buffered_size( s ) ); + } + else /* ( p_sys->method == Stream ) */ + { + int i; + + /* Setup our tracks */ + p_sys->stream.i_offset = 0; + p_sys->stream.i_tk = 0; + p_sys->stream.i_used = 0; + + for( i = 0; i < STREAM_CACHE_TRACK; i++ ) + { + p_sys->stream.tk[i].i_date = 0; + p_sys->stream.tk[i].i_start = p_sys->i_pos; + p_sys->stream.tk[i].i_end = p_sys->i_pos; + } + + /* Do the prebuffering */ + AStreamPrebufferStream( s ); + } +} + +/**************************************************************************** + * stream_AccessUpdate: + ****************************************************************************/ +void stream_AccessUpdate( stream_t *s ) +{ + stream_sys_t *p_sys = s->p_sys; + + p_sys->i_pos = p_sys->p_access->info.i_pos; + + if( p_sys->i_list ) + { + int i; + for( i = 0; i < p_sys->i_list_index; i++ ) + { + p_sys->i_pos += p_sys->list[i]->i_size; + } + } +} + +/**************************************************************************** + * AStreamControl: + ****************************************************************************/ +static int AStreamControl( stream_t *s, int i_query, va_list args ) +{ + stream_sys_t *p_sys = s->p_sys; + access_t *p_access = p_sys->p_access; + + bool *p_bool; + int64_t *pi_64, i_64; + int i_int; + + switch( i_query ) + { + case STREAM_GET_SIZE: + pi_64 = (int64_t*)va_arg( args, int64_t * ); + if( s->p_sys->i_list ) + { + int i; + *pi_64 = 0; + for( i = 0; i < s->p_sys->i_list; i++ ) + *pi_64 += s->p_sys->list[i]->i_size; + break; + } + *pi_64 = p_access->info.i_size; + break; + + case STREAM_CAN_SEEK: + p_bool = (bool*)va_arg( args, bool * ); + access_Control( p_access, ACCESS_CAN_SEEK, p_bool ); + break; + + case STREAM_CAN_FASTSEEK: + p_bool = (bool*)va_arg( args, bool * ); + access_Control( p_access, ACCESS_CAN_FASTSEEK, p_bool ); + break; + + case STREAM_GET_POSITION: + pi_64 = (int64_t*)va_arg( args, int64_t * ); + *pi_64 = p_sys->i_pos; + break; + + case STREAM_SET_POSITION: + i_64 = (int64_t)va_arg( args, int64_t ); + if( p_sys->method == Block ) + return AStreamSeekBlock( s, i_64 ); + else if( p_sys->method == Immediate ) + return AStreamSeekImmediate( s, i_64 ); + else /* ( p_sys->method == Stream ) */ + return AStreamSeekStream( s, i_64 ); + + case STREAM_GET_MTU: + return VLC_EGENERIC; + + case STREAM_CONTROL_ACCESS: + i_int = (int) va_arg( args, int ); + if( i_int != ACCESS_SET_PRIVATE_ID_STATE && + i_int != ACCESS_SET_PRIVATE_ID_CA && + i_int != ACCESS_GET_PRIVATE_ID_STATE ) + { + msg_Err( s, "Hey, what are you thinking ?" + "DON'T USE STREAM_CONTROL_ACCESS !!!" ); + return VLC_EGENERIC; + } + return access_vaControl( p_access, i_int, args ); + + case STREAM_GET_CONTENT_TYPE: + return access_Control( p_access, ACCESS_GET_CONTENT_TYPE, + va_arg( args, char ** ) ); + + default: + msg_Err( s, "invalid stream_vaControl query=0x%x", i_query ); + return VLC_EGENERIC; + } + return VLC_SUCCESS; +} + + + +/**************************************************************************** + * Method 1: + ****************************************************************************/ +static void AStreamPrebufferBlock( stream_t *s ) +{ + stream_sys_t *p_sys = s->p_sys; + access_t *p_access = p_sys->p_access; + + int64_t i_first = 0; + int64_t i_start; + + msg_Dbg( s, "pre buffering" ); + i_start = mdate(); + for( ;; ) + { + int64_t i_date = mdate(); + bool b_eof; + block_t *b; + + if( s->b_die || p_sys->block.i_size > STREAM_CACHE_PREBUFFER_SIZE || + ( i_first > 0 && i_first + STREAM_CACHE_PREBUFFER_LENGTH < i_date ) ) + { + int64_t i_byterate; + + /* Update stat */ + p_sys->stat.i_bytes = p_sys->block.i_size; + p_sys->stat.i_read_time = i_date - i_start; + i_byterate = ( INT64_C(1000000) * p_sys->stat.i_bytes ) / + (p_sys->stat.i_read_time + 1); + + msg_Dbg( s, "prebuffering done %"PRId64" bytes in %"PRId64"s - " + "%"PRId64" kbytes/s", + p_sys->stat.i_bytes, + p_sys->stat.i_read_time / INT64_C(1000000), + i_byterate / 1024 ); + break; + } + + /* Fetch a block */ + if( ( b = AReadBlock( s, &b_eof ) ) == NULL ) + { + if( b_eof ) break; + + msleep( STREAM_DATA_WAIT ); + continue; + } + + while( b ) + { + /* Append the block */ + p_sys->block.i_size += b->i_buffer; + *p_sys->block.pp_last = b; + p_sys->block.pp_last = &b->p_next; + + p_sys->stat.i_read_count++; + b = b->p_next; + } + + if( p_access->info.b_prebuffered ) + { + /* Access has already prebufferred - update stats and exit */ + p_sys->stat.i_bytes = p_sys->block.i_size; + p_sys->stat.i_read_time = mdate() - i_start; + break; + } + + if( i_first == 0 ) + { + i_first = mdate(); + msg_Dbg( s, "received first data for our buffer"); + } + + } + + p_sys->block.p_current = p_sys->block.p_first; +} + +static int AStreamRefillBlock( stream_t *s ); + +static int AStreamReadBlock( stream_t *s, void *p_read, unsigned int i_read ) +{ + stream_sys_t *p_sys = s->p_sys; + + uint8_t *p_data= (uint8_t*)p_read; + unsigned int i_data = 0; + + /* It means EOF */ + if( p_sys->block.p_current == NULL ) + return 0; + + if( p_read == NULL ) + { + /* seek within this stream if possible, else use plain old read and discard */ + stream_sys_t *p_sys = s->p_sys; + access_t *p_access = p_sys->p_access; + bool b_aseek; + access_Control( p_access, ACCESS_CAN_SEEK, &b_aseek ); + if( b_aseek ) + return AStreamSeekBlock( s, p_sys->i_pos + i_read ) ? 0 : i_read; + } + + while( i_data < i_read ) + { + int i_current = + p_sys->block.p_current->i_buffer - p_sys->block.i_offset; + unsigned int i_copy = __MIN( (unsigned int)__MAX(i_current,0), i_read - i_data); + + /* Copy data */ + if( p_data ) + { + memcpy( p_data, + &p_sys->block.p_current->p_buffer[p_sys->block.i_offset], + i_copy ); + p_data += i_copy; + } + i_data += i_copy; + + p_sys->block.i_offset += i_copy; + if( p_sys->block.i_offset >= p_sys->block.p_current->i_buffer ) + { + /* Current block is now empty, switch to next */ + if( p_sys->block.p_current ) + { + p_sys->block.i_offset = 0; + p_sys->block.p_current = p_sys->block.p_current->p_next; + } + /*Get a new block if needed */ + if( !p_sys->block.p_current && AStreamRefillBlock( s ) ) + { + break; + } + } + } + + p_sys->i_pos += i_data; + return i_data; +} + +static int AStreamPeekBlock( stream_t *s, const uint8_t **pp_peek, unsigned int i_read ) +{ + stream_sys_t *p_sys = s->p_sys; + uint8_t *p_data; + unsigned int i_data = 0; + block_t *b; + unsigned int i_offset; + + if( p_sys->block.p_current == NULL ) return 0; /* EOF */ + + /* We can directly give a pointer over our buffer */ + if( i_read <= p_sys->block.p_current->i_buffer - p_sys->block.i_offset ) + { + *pp_peek = &p_sys->block.p_current->p_buffer[p_sys->block.i_offset]; + return i_read; + } + + /* We need to create a local copy */ + if( p_sys->i_peek < i_read ) + { + p_sys->p_peek = realloc( p_sys->p_peek, i_read ); + if( !p_sys->p_peek ) + { + p_sys->i_peek = 0; + return 0; + } + p_sys->i_peek = i_read; + } + + /* Fill enough data */ + while( p_sys->block.i_size - (p_sys->i_pos - p_sys->block.i_start) + < i_read ) + { + block_t **pp_last = p_sys->block.pp_last; + + if( AStreamRefillBlock( s ) ) break; + + /* Our buffer are probably filled enough, don't try anymore */ + if( pp_last == p_sys->block.pp_last ) break; + } + + /* Copy what we have */ + b = p_sys->block.p_current; + i_offset = p_sys->block.i_offset; + p_data = p_sys->p_peek; + + while( b && i_data < i_read ) + { + unsigned int i_current = __MAX(b->i_buffer - i_offset,0); + int i_copy = __MIN( i_current, i_read - i_data ); + + memcpy( p_data, &b->p_buffer[i_offset], i_copy ); + i_data += i_copy; + p_data += i_copy; + i_offset += i_copy; + + if( i_offset >= b->i_buffer ) + { + i_offset = 0; + b = b->p_next; + } + } + + *pp_peek = p_sys->p_peek; + return i_data; +} + +static int AStreamSeekBlock( stream_t *s, int64_t i_pos ) +{ + stream_sys_t *p_sys = s->p_sys; + access_t *p_access = p_sys->p_access; + int64_t i_offset = i_pos - p_sys->block.i_start; + bool b_seek; + + /* We already have thoses data, just update p_current/i_offset */ + if( i_offset >= 0 && i_offset < p_sys->block.i_size ) + { + block_t *b = p_sys->block.p_first; + int i_current = 0; + + while( i_current + b->i_buffer < i_offset ) + { + i_current += b->i_buffer; + b = b->p_next; + } + + p_sys->block.p_current = b; + p_sys->block.i_offset = i_offset - i_current; + + p_sys->i_pos = i_pos; + + return VLC_SUCCESS; + } + + /* We may need to seek or to read data */ + if( i_offset < 0 ) + { + bool b_aseek; + access_Control( p_access, ACCESS_CAN_SEEK, &b_aseek ); + + if( !b_aseek ) + { + msg_Err( s, "backward seeking impossible (access not seekable)" ); + return VLC_EGENERIC; + } + + b_seek = true; + } + else + { + bool b_aseek, b_aseekfast; + + access_Control( p_access, ACCESS_CAN_SEEK, &b_aseek ); + access_Control( p_access, ACCESS_CAN_FASTSEEK, &b_aseekfast ); + + if( !b_aseek ) + { + b_seek = false; + msg_Warn( s, "%"PRId64" bytes need to be skipped " + "(access non seekable)", + i_offset - p_sys->block.i_size ); + } + else + { + int64_t i_skip = i_offset - p_sys->block.i_size; + + /* Avg bytes per packets */ + int i_avg = p_sys->stat.i_bytes / p_sys->stat.i_read_count; + /* TODO compute a seek cost instead of fixed threshold */ + int i_th = b_aseekfast ? 1 : 5; + + if( i_skip <= i_th * i_avg && + i_skip < STREAM_CACHE_SIZE ) + b_seek = false; + else + b_seek = true; + + msg_Dbg( s, "b_seek=%d th*avg=%d skip=%"PRId64, + b_seek, i_th*i_avg, i_skip ); + } + } + + if( b_seek ) + { + int64_t i_start, i_end; + /* Do the access seek */ + i_start = mdate(); + if( ASeek( s, i_pos ) ) return VLC_EGENERIC; + i_end = mdate(); + + /* Release data */ + block_ChainRelease( p_sys->block.p_first ); + + /* Reinit */ + p_sys->block.i_start = p_sys->i_pos = i_pos; + p_sys->block.i_offset = 0; + p_sys->block.p_current = NULL; + p_sys->block.i_size = 0; + p_sys->block.p_first = NULL; + p_sys->block.pp_last = &p_sys->block.p_first; + + /* Refill a block */ + if( AStreamRefillBlock( s ) ) + return VLC_EGENERIC; + + /* Update stat */ + p_sys->stat.i_seek_time += i_end - i_start; + p_sys->stat.i_seek_count++; + return VLC_SUCCESS; + } + else + { + do + { + /* Read and skip enough data */ + if( AStreamRefillBlock( s ) ) + return VLC_EGENERIC; + + while( p_sys->block.p_current && + p_sys->i_pos + p_sys->block.p_current->i_buffer - p_sys->block.i_offset < i_pos ) + { + p_sys->i_pos += p_sys->block.p_current->i_buffer - p_sys->block.i_offset; + p_sys->block.p_current = p_sys->block.p_current->p_next; + p_sys->block.i_offset = 0; + } + } + while( p_sys->block.i_start + p_sys->block.i_size < i_pos ); + + p_sys->block.i_offset = i_pos - p_sys->i_pos; + p_sys->i_pos = i_pos; + + return VLC_SUCCESS; + } + + return VLC_EGENERIC; +} + +static int AStreamRefillBlock( stream_t *s ) +{ + stream_sys_t *p_sys = s->p_sys; + int64_t i_start, i_stop; + block_t *b; + + /* Release data */ + while( p_sys->block.i_size >= STREAM_CACHE_SIZE && + p_sys->block.p_first != p_sys->block.p_current ) + { + block_t *b = p_sys->block.p_first; + + p_sys->block.i_start += b->i_buffer; + p_sys->block.i_size -= b->i_buffer; + p_sys->block.p_first = b->p_next; + + block_Release( b ); + } + if( p_sys->block.i_size >= STREAM_CACHE_SIZE && + p_sys->block.p_current == p_sys->block.p_first && + p_sys->block.p_current->p_next ) /* At least 2 packets */ + { + /* Enough data, don't read more */ + return VLC_SUCCESS; + } + + /* Now read a new block */ + i_start = mdate(); + for( ;; ) + { + bool b_eof; + + if( s->b_die ) return VLC_EGENERIC; + + + /* Fetch a block */ + if( ( b = AReadBlock( s, &b_eof ) ) ) break; + + if( b_eof ) return VLC_EGENERIC; + + msleep( STREAM_DATA_WAIT ); + } + + while( b ) + { + i_stop = mdate(); + + /* Append the block */ + p_sys->block.i_size += b->i_buffer; + *p_sys->block.pp_last = b; + p_sys->block.pp_last = &b->p_next; + + /* Fix p_current */ + if( p_sys->block.p_current == NULL ) + p_sys->block.p_current = b; + + /* Update stat */ + p_sys->stat.i_bytes += b->i_buffer; + p_sys->stat.i_read_time += i_stop - i_start; + p_sys->stat.i_read_count++; + + b = b->p_next; + i_start = mdate(); + } + return VLC_SUCCESS; +} + + +/**************************************************************************** + * Method 2: + ****************************************************************************/ +static int AStreamRefillStream( stream_t *s ); + +static int AStreamReadStream( stream_t *s, void *p_read, unsigned int i_read ) +{ + stream_sys_t *p_sys = s->p_sys; + stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk]; + + uint8_t *p_data = (uint8_t *)p_read; + unsigned int i_data = 0; + + if( tk->i_start >= tk->i_end ) return 0; /* EOF */ + + if( p_read == NULL ) + { + /* seek within this stream if possible, else use plain old read and discard */ + stream_sys_t *p_sys = s->p_sys; + access_t *p_access = p_sys->p_access; + + /* seeking after EOF is not what we want */ + if( !( p_access->info.b_eof ) ) + { + bool b_aseek; + access_Control( p_access, ACCESS_CAN_SEEK, &b_aseek ); + if( b_aseek ) + return AStreamSeekStream( s, p_sys->i_pos + i_read ) ? 0 : i_read; + } + } + +#ifdef STREAM_DEBUG + msg_Dbg( s, "AStreamReadStream: %d pos=%"PRId64" tk=%d start=%"PRId64 + " offset=%d end=%"PRId64, + i_read, p_sys->i_pos, p_sys->stream.i_tk, + tk->i_start, p_sys->stream.i_offset, tk->i_end ); +#endif + + while( i_data < i_read ) + { + int i_off = (tk->i_start + p_sys->stream.i_offset) % + STREAM_CACHE_TRACK_SIZE; + unsigned int i_current = + __MAX(0,__MIN( tk->i_end - tk->i_start - p_sys->stream.i_offset, + STREAM_CACHE_TRACK_SIZE - i_off )); + int i_copy = __MIN( i_current, i_read - i_data ); + + if( i_copy <= 0 ) break; /* EOF */ + + /* Copy data */ + /* msg_Dbg( s, "AStreamReadStream: copy %d", i_copy ); */ + if( p_data ) + { + memcpy( p_data, &tk->p_buffer[i_off], i_copy ); + p_data += i_copy; + } + i_data += i_copy; + p_sys->stream.i_offset += i_copy; + + /* Update pos now */ + p_sys->i_pos += i_copy; + + /* */ + p_sys->stream.i_used += i_copy; + if( tk->i_start + p_sys->stream.i_offset >= tk->i_end || + p_sys->stream.i_used >= p_sys->stream.i_read_size ) + { + if( AStreamRefillStream( s ) ) + { + /* EOF */ + if( tk->i_start >= tk->i_end ) break; + } + } + } + + return i_data; +} + +static int AStreamPeekStream( stream_t *s, const uint8_t **pp_peek, unsigned int i_read ) +{ + stream_sys_t *p_sys = s->p_sys; + stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk]; + int64_t i_off; + + if( tk->i_start >= tk->i_end ) return 0; /* EOF */ + +#ifdef STREAM_DEBUG + msg_Dbg( s, "AStreamPeekStream: %d pos=%"PRId64" tk=%d " + "start=%"PRId64" offset=%d end=%"PRId64, + i_read, p_sys->i_pos, p_sys->stream.i_tk, + tk->i_start, p_sys->stream.i_offset, tk->i_end ); +#endif + + /* Avoid problem, but that should *never* happen */ + if( i_read > STREAM_CACHE_TRACK_SIZE / 2 ) + i_read = STREAM_CACHE_TRACK_SIZE / 2; + + while( tk->i_end - tk->i_start - p_sys->stream.i_offset < i_read ) + { + if( p_sys->stream.i_used <= 1 ) + { + /* Be sure we will read something */ + p_sys->stream.i_used += i_read - + (tk->i_end - tk->i_start - p_sys->stream.i_offset); + } + if( AStreamRefillStream( s ) ) break; + } + + if( tk->i_end - tk->i_start - p_sys->stream.i_offset < i_read ) + i_read = tk->i_end - tk->i_start - p_sys->stream.i_offset; + + /* Now, direct pointer or a copy ? */ + i_off = (tk->i_start + p_sys->stream.i_offset) % STREAM_CACHE_TRACK_SIZE; + if( i_off + i_read <= STREAM_CACHE_TRACK_SIZE ) + { + *pp_peek = &tk->p_buffer[i_off]; + return i_read; + } + + if( p_sys->i_peek < i_read ) + { + p_sys->p_peek = realloc( p_sys->p_peek, i_read ); + if( !p_sys->p_peek ) + { + p_sys->i_peek = 0; + return 0; + } + p_sys->i_peek = i_read; + } + + memcpy( p_sys->p_peek, &tk->p_buffer[i_off], + STREAM_CACHE_TRACK_SIZE - i_off ); + memcpy( &p_sys->p_peek[STREAM_CACHE_TRACK_SIZE - i_off], + &tk->p_buffer[0], i_read - (STREAM_CACHE_TRACK_SIZE - i_off) ); + + *pp_peek = p_sys->p_peek; + return i_read; +} + +static int AStreamSeekStream( stream_t *s, int64_t i_pos ) +{ + stream_sys_t *p_sys = s->p_sys; + access_t *p_access = p_sys->p_access; + bool b_aseek; + bool b_afastseek; + int i_maxth; + int i_new; + int i; + +#ifdef STREAM_DEBUG + msg_Dbg( s, "AStreamSeekStream: to %"PRId64" pos=%"PRId64 + " tk=%d start=%"PRId64" offset=%d end=%"PRId64, + i_pos, p_sys->i_pos, p_sys->stream.i_tk, + p_sys->stream.tk[p_sys->stream.i_tk].i_start, + p_sys->stream.i_offset, + p_sys->stream.tk[p_sys->stream.i_tk].i_end ); +#endif + + + /* Seek in our current track ? */ + if( i_pos >= p_sys->stream.tk[p_sys->stream.i_tk].i_start && + i_pos < p_sys->stream.tk[p_sys->stream.i_tk].i_end ) + { + stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk]; +#ifdef STREAM_DEBUG + msg_Dbg( s, "AStreamSeekStream: current track" ); +#endif + p_sys->i_pos = i_pos; + p_sys->stream.i_offset = i_pos - tk->i_start; + + /* If there is not enough data left in the track, refill */ + /* \todo How to get a correct value for + * - refilling threshold + * - how much to refill + */ + if( (tk->i_end - tk->i_start ) - p_sys->stream.i_offset < + p_sys->stream.i_read_size ) + { + if( p_sys->stream.i_used < STREAM_READ_ATONCE / 2 ) + { + p_sys->stream.i_used = STREAM_READ_ATONCE / 2 ; + AStreamRefillStream( s ); + } + } + return VLC_SUCCESS; + } + + access_Control( p_access, ACCESS_CAN_SEEK, &b_aseek ); + if( !b_aseek ) + { + /* We can't do nothing */ + msg_Dbg( s, "AStreamSeekStream: can't seek" ); + return VLC_EGENERIC; + } + + /* Date the current track */ + p_sys->stream.tk[p_sys->stream.i_tk].i_date = mdate(); + + /* Try to reuse already read data */ + for( i = 0; i < STREAM_CACHE_TRACK; i++ ) + { + stream_track_t *tk = &p_sys->stream.tk[i]; + + if( i_pos >= tk->i_start && i_pos <= tk->i_end ) + { +#ifdef STREAM_DEBUG + msg_Dbg( s, "AStreamSeekStream: reusing %d start=%"PRId64 + " end=%"PRId64, i, tk->i_start, tk->i_end ); +#endif + + /* Seek at the end of the buffer */ + if( ASeek( s, tk->i_end ) ) return VLC_EGENERIC; + + /* That's it */ + p_sys->i_pos = i_pos; + p_sys->stream.i_tk = i; + p_sys->stream.i_offset = i_pos - tk->i_start; + + if( p_sys->stream.i_used < 1024 ) + p_sys->stream.i_used = 1024; + + if( AStreamRefillStream( s ) && i_pos == tk->i_end ) + return VLC_EGENERIC; + + return VLC_SUCCESS; + } + } + + access_Control( p_access, ACCESS_CAN_SEEK, &b_afastseek ); + /* FIXME compute seek cost (instead of static 'stupid' value) */ + i_maxth = __MIN( p_sys->stream.i_read_size, STREAM_READ_ATONCE / 2 ); + if( !b_afastseek ) + i_maxth *= 3; + + /* FIXME TODO */ +#if 0 + /* Search closest segment TODO */ + for( i = 0; i < STREAM_CACHE_TRACK; i++ ) + { + stream_track_t *tk = &p_sys->stream.tk[i]; + + if( i_pos + i_maxth >= tk->i_start ) + { + msg_Dbg( s, "good segment before current pos, TODO" ); + } + if( i_pos - i_maxth <= tk->i_end ) + { + msg_Dbg( s, "good segment after current pos, TODO" ); + } + } +#endif + + /* Nothing good, seek and choose oldest segment */ + if( ASeek( s, i_pos ) ) return VLC_EGENERIC; + p_sys->i_pos = i_pos; + + i_new = 0; + for( i = 1; i < STREAM_CACHE_TRACK; i++ ) + { + if( p_sys->stream.tk[i].i_date < p_sys->stream.tk[i_new].i_date ) + i_new = i; + } + + /* Reset the segment */ + p_sys->stream.i_tk = i_new; + p_sys->stream.i_offset = 0; + p_sys->stream.tk[i_new].i_start = i_pos; + p_sys->stream.tk[i_new].i_end = i_pos; + + /* Read data */ + if( p_sys->stream.i_used < STREAM_READ_ATONCE / 2 ) + p_sys->stream.i_used = STREAM_READ_ATONCE / 2; + + if( AStreamRefillStream( s ) ) + return VLC_EGENERIC; + + return VLC_SUCCESS; +} + +static int AStreamRefillStream( stream_t *s ) +{ + stream_sys_t *p_sys = s->p_sys; + stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk]; + + /* We read but won't increase i_start after initial start + offset */ + int i_toread = + __MIN( p_sys->stream.i_used, STREAM_CACHE_TRACK_SIZE - + (tk->i_end - tk->i_start - p_sys->stream.i_offset) ); + bool b_read = false; + int64_t i_start, i_stop; + + if( i_toread <= 0 ) return VLC_EGENERIC; /* EOF */ + +#ifdef STREAM_DEBUG + msg_Dbg( s, "AStreamRefillStream: used=%d toread=%d", + p_sys->stream.i_used, i_toread ); +#endif + + i_start = mdate(); + while( i_toread > 0 ) + { + int i_off = tk->i_end % STREAM_CACHE_TRACK_SIZE; + int i_read; + + if( s->b_die ) + return VLC_EGENERIC; + + i_read = __MIN( i_toread, STREAM_CACHE_TRACK_SIZE - i_off ); + i_read = AReadStream( s, &tk->p_buffer[i_off], i_read ); + + /* msg_Dbg( s, "AStreamRefillStream: read=%d", i_read ); */ + if( i_read < 0 ) + { + msleep( STREAM_DATA_WAIT ); + continue; + } + else if( i_read == 0 ) + { + if( !b_read ) return VLC_EGENERIC; + return VLC_SUCCESS; + } + b_read = true; + + /* Update end */ + tk->i_end += i_read; + + /* Windows of STREAM_CACHE_TRACK_SIZE */ + if( tk->i_end - tk->i_start > STREAM_CACHE_TRACK_SIZE ) + { + int i_invalid = tk->i_end - tk->i_start - STREAM_CACHE_TRACK_SIZE; + + tk->i_start += i_invalid; + p_sys->stream.i_offset -= i_invalid; + } + + i_toread -= i_read; + p_sys->stream.i_used -= i_read; + + p_sys->stat.i_bytes += i_read; + p_sys->stat.i_read_count++; + } + i_stop = mdate(); + + p_sys->stat.i_read_time += i_stop - i_start; + + return VLC_SUCCESS; +} + +static void AStreamPrebufferStream( stream_t *s ) +{ + stream_sys_t *p_sys = s->p_sys; + access_t *p_access = p_sys->p_access; + + int64_t i_first = 0; + int64_t i_start; + int64_t i_prebuffer = p_sys->b_quick ? STREAM_CACHE_TRACK_SIZE /100 : + ( (p_access->info.i_title > 1 || p_access->info.i_seekpoint > 1) ? + STREAM_CACHE_PREBUFFER_SIZE : STREAM_CACHE_TRACK_SIZE / 3 ); + + msg_Dbg( s, "pre-buffering..." ); + i_start = mdate(); + for( ;; ) + { + stream_track_t *tk = &p_sys->stream.tk[p_sys->stream.i_tk]; + + int64_t i_date = mdate(); + int i_read; + + if( s->b_die || tk->i_end >= i_prebuffer || + (i_first > 0 && i_first + STREAM_CACHE_PREBUFFER_LENGTH < i_date) ) + { + int64_t i_byterate; + + /* Update stat */ + p_sys->stat.i_bytes = tk->i_end - tk->i_start; + p_sys->stat.i_read_time = i_date - i_start; + i_byterate = ( INT64_C(1000000) * p_sys->stat.i_bytes ) / + (p_sys->stat.i_read_time+1); + + msg_Dbg( s, "pre-buffering done %"PRId64" bytes in %"PRId64"s - " + "%"PRId64" kbytes/s", + p_sys->stat.i_bytes, + p_sys->stat.i_read_time / INT64_C(1000000), + i_byterate / 1024 ); + break; + } + + /* */ + i_read = STREAM_CACHE_TRACK_SIZE - tk->i_end; + i_read = __MIN( p_sys->stream.i_read_size, i_read ); + i_read = AReadStream( s, &tk->p_buffer[tk->i_end], i_read ); + if( i_read < 0 ) + { + msleep( STREAM_DATA_WAIT ); + continue; + } + else if( i_read == 0 ) + { + /* EOF */ + break; + } + + if( i_first == 0 ) + { + i_first = mdate(); + msg_Dbg( s, "received first data for our buffer"); + } + + tk->i_end += i_read; + + p_sys->stat.i_read_count++; + } +} + +/**************************************************************************** + * Method 3: + ****************************************************************************/ + +static int AStreamReadImmediate( stream_t *s, void *p_read, unsigned int i_read ) +{ + stream_sys_t *p_sys = s->p_sys; + +#ifdef STREAM_DEBUG + msg_Dbg( s, "AStreamReadImmediate p_read=%p i_read=%d", + p_read, i_read ); +#endif + + /* First, check if we already have some data in the buffer, + * that we could copy directly */ + int i_copy = __MIN( stream_buffered_size( s ), i_read ); + if( i_copy ) + { +#ifdef STREAM_DEBUG + msg_Dbg( s, "AStreamReadImmediate: copy %d from %p", i_copy, stream_buffer( s ) ); +#endif + + assert( i_copy <= STREAM_CACHE_SIZE ); + + if( p_read ) + { + memcpy( p_read, stream_buffer( s ), i_copy ); + p_read = (uint8_t *)p_read + i_copy; + } + } + + /* Now that we've read our buffer we don't need its i_copy bytes */ + stream_buffer_empty( s, i_copy ); + + /* Now check if we have still to really read some data */ + int i_to_read = i_read - i_copy; + if( i_to_read ) + { + if( p_read ) + i_to_read = AReadStream( s, p_read, i_to_read ); + else + { + void * dummy = malloc(i_to_read); + i_to_read = AReadStream( s, dummy, i_to_read ); + free(dummy); + } + } + + p_sys->i_pos += i_to_read; + + return i_to_read + i_copy; +} + +static int AStreamPeekImmediate( stream_t *s, const uint8_t **pp_peek, unsigned int i_read ) +{ +#ifdef STREAM_DEBUG + msg_Dbg( s, "AStreamPeekImmediate: %d size=%"PRId64, + i_read, size_buffered_size( s ) ); +#endif + + /* Avoid problem, but that shouldn't happen */ + if( i_read > STREAM_CACHE_SIZE / 2 ) + i_read = STREAM_CACHE_SIZE / 2; + + int i_to_read = i_read - stream_buffered_size( s ); + if( i_to_read > 0 ) + { +#ifdef STREAM_DEBUG + msg_Dbg( s, "AStreamPeekImmediate: Reading %d", + i_to_read ); +#endif + i_to_read = AReadStream( s, stream_buffer( s ) + stream_buffered_size( s ), + i_to_read ); + + if( i_to_read > 0 ) + stream_buffer_fill( s, i_to_read ); + } + + *pp_peek = stream_buffer( s ); + + return __MIN(stream_buffered_size( s ), i_read); +} + +static int AStreamSeekImmediate( stream_t *s, int64_t i_pos ) +{ + stream_sys_t *p_sys = s->p_sys; + access_t *p_access = p_sys->p_access; + bool b_aseek; + +#ifdef STREAM_DEBUG + msg_Dbg( s, "AStreamSeekImmediate to %"PRId64" pos=%"PRId64 + i_pos, p_sys->i_pos ); +#endif + + access_Control( p_access, ACCESS_CAN_SEEK, &b_aseek ); + if( !b_aseek ) + { + /* We can't do nothing */ + msg_Dbg( s, "AStreamSeekImmediate: can't seek" ); + return VLC_EGENERIC; + } + + /* Just reset our buffer */ + stream_buffer_empty( s, stream_buffered_size( s ) ); + + if( ASeek( s, i_pos ) ) return VLC_EGENERIC; + + return VLC_SUCCESS; +} + +/**************************************************************************** + * stream_ReadLine: + ****************************************************************************/ +/** + * Read from the stream untill first newline. + * \param s Stream handle to read from + * \return A pointer to the allocated output string. You need to free this when you are done. + */ +#define STREAM_PROBE_LINE 2048 +#define STREAM_LINE_MAX (2048*100) +char * stream_ReadLine( stream_t *s ) +{ + char *p_line = NULL; + int i_line = 0, i_read = 0; + + while( i_read < STREAM_LINE_MAX ) + { + char *psz_eol; + const uint8_t *p_data; + int i_data; + int64_t i_pos; + + /* Probe new data */ + i_data = stream_Peek( s, &p_data, STREAM_PROBE_LINE ); + if( i_data <= 0 ) break; /* No more data */ + + /* BOM detection */ + i_pos = stream_Tell( s ); + if( i_pos == 0 && i_data > 4 ) + { + int i_bom_size = 0; + char *psz_encoding = NULL; + + if( p_data[0] == 0xEF && p_data[1] == 0xBB && p_data[2] == 0xBF ) + { + psz_encoding = strdup( "UTF-8" ); + i_bom_size = 3; + } + else if( p_data[0] == 0x00 && p_data[1] == 0x00 ) + { + if( p_data[2] == 0xFE && p_data[3] == 0xFF ) + { + psz_encoding = strdup( "UTF-32BE" ); + s->i_char_width = 4; + i_bom_size = 4; + } + } + else if( p_data[0] == 0xFF && p_data[1] == 0xFE ) + { + if( p_data[2] == 0x00 && p_data[3] == 0x00 ) + { + psz_encoding = strdup( "UTF-32LE" ); + s->i_char_width = 4; + s->b_little_endian = true; + i_bom_size = 4; + } + else + { + psz_encoding = strdup( "UTF-16LE" ); + s->b_little_endian = true; + s->i_char_width = 2; + i_bom_size = 2; + } + } + else if( p_data[0] == 0xFE && p_data[1] == 0xFF ) + { + psz_encoding = strdup( "UTF-16BE" ); + s->i_char_width = 2; + i_bom_size = 2; + } + + /* Seek past the BOM */ + if( i_bom_size ) + { + stream_Seek( s, i_bom_size ); + p_data += i_bom_size; + i_data -= i_bom_size; + } + + /* Open the converter if we need it */ + if( psz_encoding != NULL ) + { + input_thread_t *p_input; + msg_Dbg( s, "%s BOM detected", psz_encoding ); + p_input = (input_thread_t *)vlc_object_find( s, VLC_OBJECT_INPUT, FIND_PARENT ); + if( s->i_char_width > 1 ) + { + s->conv = vlc_iconv_open( "UTF-8", psz_encoding ); + if( s->conv == (vlc_iconv_t)-1 ) + { + msg_Err( s, "iconv_open failed" ); + } + } + if( p_input != NULL) + { + var_Create( p_input, "subsdec-encoding", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); + var_SetString( p_input, "subsdec-encoding", "UTF-8" ); + vlc_object_release( p_input ); + } + free( psz_encoding ); + } + } + + if( i_data % s->i_char_width ) + { + /* keep i_char_width boundary */ + i_data = i_data - ( i_data % s->i_char_width ); + msg_Warn( s, "the read is not i_char_width compatible"); + } + + if( i_data == 0 ) + break; + + /* Check if there is an EOL */ + if( s->i_char_width == 1 ) + { + /* UTF-8: 0A */ + psz_eol = memchr( p_data, '\n', i_data ); + } + else + { + const uint8_t *p = p_data; + const uint8_t *p_last = p + i_data - s->i_char_width; + + if( s->i_char_width == 2 ) + { + if( s->b_little_endian == true) + { + /* UTF-16LE: 0A 00 */ + while( p <= p_last && ( p[0] != 0x0A || p[1] != 0x00 ) ) + p += 2; + } + else + { + /* UTF-16BE: 00 0A */ + while( p <= p_last && ( p[1] != 0x0A || p[0] != 0x00 ) ) + p += 2; + } + } + else if( s->i_char_width == 4 ) + { + if( s->b_little_endian == true) + { + /* UTF-32LE: 0A 00 00 00 */ + while( p <= p_last && ( p[0] != 0x0A || p[1] != 0x00 || + p[2] != 0x00 || p[3] != 0x00 ) ) + p += 4; + } + else + { + /* UTF-32BE: 00 00 00 0A */ + while( p <= p_last && ( p[3] != 0x0A || p[2] != 0x00 || + p[1] != 0x00 || p[0] != 0x00 ) ) + p += 4; + } + } + + if( p > p_last ) + { + psz_eol = NULL; + } + else + { + psz_eol = (char *)p + ( s->i_char_width - 1 ); + } + } + + if(psz_eol) + { + i_data = (psz_eol - (char *)p_data) + 1; + p_line = realloc( p_line, i_line + i_data + s->i_char_width ); /* add \0 */ + if( !p_line ) + goto error; + i_data = stream_Read( s, &p_line[i_line], i_data ); + if( i_data <= 0 ) break; /* Hmmm */ + i_line += i_data - s->i_char_width; /* skip \n */; + i_read += i_data; + + /* We have our line */ + break; + } + + /* Read data (+1 for easy \0 append) */ + p_line = realloc( p_line, i_line + STREAM_PROBE_LINE + s->i_char_width ); + if( !p_line ) + goto error; + i_data = stream_Read( s, &p_line[i_line], STREAM_PROBE_LINE ); + if( i_data <= 0 ) break; /* Hmmm */ + i_line += i_data; + i_read += i_data; + } + + if( i_read > 0 ) + { + int j; + for( j = 0; j < s->i_char_width; j++ ) + { + p_line[i_line + j] = '\0'; + } + i_line += s->i_char_width; /* the added \0 */ + if( s->i_char_width > 1 ) + { + size_t i_in = 0, i_out = 0; + const char * p_in = NULL; + char * p_out = NULL; + char * psz_new_line = NULL; + + /* iconv */ + psz_new_line = malloc( i_line ); + if( psz_new_line == NULL ) + goto error; + i_in = i_out = (size_t)i_line; + p_in = p_line; + p_out = psz_new_line; + + if( vlc_iconv( s->conv, &p_in, &i_in, &p_out, &i_out ) == (size_t)-1 ) + { + msg_Err( s, "iconv failed" ); + msg_Dbg( s, "original: %d, in %d, out %d", i_line, (int)i_in, (int)i_out ); + } + free( p_line ); + p_line = psz_new_line; + i_line = (size_t)i_line - i_out; /* does not include \0 */ + } + + /* Remove trailing LF/CR */ + while( i_line >= 2 && ( p_line[i_line-2] == '\r' || + p_line[i_line-2] == '\n') ) i_line--; + + /* Make sure the \0 is there */ + p_line[i_line-1] = '\0'; + + return p_line; + } + +error: + + /* We failed to read any data, probably EOF */ + free( p_line ); + if( s->conv != (vlc_iconv_t)(-1) ) vlc_iconv_close( s->conv ); + return NULL; +} + +/**************************************************************************** + * Access reading/seeking wrappers to handle concatenated streams. + ****************************************************************************/ +static int AReadStream( stream_t *s, void *p_read, unsigned int i_read ) +{ + stream_sys_t *p_sys = s->p_sys; + access_t *p_access = p_sys->p_access; + input_thread_t *p_input = NULL; + int i_read_orig = i_read; + int i_total = 0; + + if( s->p_parent && s->p_parent->p_parent && + s->p_parent->p_parent->i_object_type == VLC_OBJECT_INPUT ) + p_input = (input_thread_t *)s->p_parent->p_parent; + + if( !p_sys->i_list ) + { + i_read = p_access->pf_read( p_access, p_read, i_read ); + if( p_access->b_die ) + vlc_object_kill( s ); + if( p_input ) + { + vlc_mutex_lock( &p_input->p->counters.counters_lock ); + stats_UpdateInteger( s, p_input->p->counters.p_read_bytes, i_read, + &i_total ); + stats_UpdateFloat( s, p_input->p->counters.p_input_bitrate, + (float)i_total, NULL ); + stats_UpdateInteger( s, p_input->p->counters.p_read_packets, 1, NULL ); + vlc_mutex_unlock( &p_input->p->counters.counters_lock ); + } + return i_read; + } + + i_read = p_sys->p_list_access->pf_read( p_sys->p_list_access, p_read, + i_read ); + if( p_access->b_die ) + vlc_object_kill( s ); + + /* If we reached an EOF then switch to the next stream in the list */ + if( i_read == 0 && p_sys->i_list_index + 1 < p_sys->i_list ) + { + char *psz_name = p_sys->list[++p_sys->i_list_index]->psz_path; + access_t *p_list_access; + + msg_Dbg( s, "opening input `%s'", psz_name ); + + p_list_access = access_New( s, p_access->psz_access, "", psz_name ); + + if( !p_list_access ) return 0; + + if( p_sys->p_list_access != p_access ) + access_Delete( p_sys->p_list_access ); + + p_sys->p_list_access = p_list_access; + + /* We have to read some data */ + return AReadStream( s, p_read, i_read_orig ); + } + + /* Update read bytes in input */ + if( p_input ) + { + vlc_mutex_lock( &p_input->p->counters.counters_lock ); + stats_UpdateInteger( s, p_input->p->counters.p_read_bytes, i_read, &i_total ); + stats_UpdateFloat( s, p_input->p->counters.p_input_bitrate, + (float)i_total, NULL ); + stats_UpdateInteger( s, p_input->p->counters.p_read_packets, 1, NULL ); + vlc_mutex_unlock( &p_input->p->counters.counters_lock ); + } + return i_read; +} + +static block_t *AReadBlock( stream_t *s, bool *pb_eof ) +{ + stream_sys_t *p_sys = s->p_sys; + access_t *p_access = p_sys->p_access; + input_thread_t *p_input = NULL; + block_t *p_block; + bool b_eof; + int i_total = 0; + + if( s->p_parent && s->p_parent->p_parent && + s->p_parent->p_parent->i_object_type == VLC_OBJECT_INPUT ) + p_input = (input_thread_t *)s->p_parent->p_parent; + + if( !p_sys->i_list ) + { + p_block = p_access->pf_block( p_access ); + if( p_access->b_die ) + vlc_object_kill( s ); + if( pb_eof ) *pb_eof = p_access->info.b_eof; + if( p_input && p_block && libvlc_stats (p_access) ) + { + vlc_mutex_lock( &p_input->p->counters.counters_lock ); + stats_UpdateInteger( s, p_input->p->counters.p_read_bytes, + p_block->i_buffer, &i_total ); + stats_UpdateFloat( s, p_input->p->counters.p_input_bitrate, + (float)i_total, NULL ); + stats_UpdateInteger( s, p_input->p->counters.p_read_packets, 1, NULL ); + vlc_mutex_unlock( &p_input->p->counters.counters_lock ); + } + return p_block; + } + + p_block = p_sys->p_list_access->pf_block( p_access ); + if( p_access->b_die ) + vlc_object_kill( s ); + b_eof = p_sys->p_list_access->info.b_eof; + if( pb_eof ) *pb_eof = b_eof; + + /* If we reached an EOF then switch to the next stream in the list */ + if( !p_block && b_eof && p_sys->i_list_index + 1 < p_sys->i_list ) + { + char *psz_name = p_sys->list[++p_sys->i_list_index]->psz_path; + access_t *p_list_access; + + msg_Dbg( s, "opening input `%s'", psz_name ); + + p_list_access = access_New( s, p_access->psz_access, "", psz_name ); + + if( !p_list_access ) return 0; + + if( p_sys->p_list_access != p_access ) + access_Delete( p_sys->p_list_access ); + + p_sys->p_list_access = p_list_access; + + /* We have to read some data */ + return AReadBlock( s, pb_eof ); + } + if( p_block ) + { + if( p_input ) + { + vlc_mutex_lock( &p_input->p->counters.counters_lock ); + stats_UpdateInteger( s, p_input->p->counters.p_read_bytes, + p_block->i_buffer, &i_total ); + stats_UpdateFloat( s, p_input->p->counters.p_input_bitrate, + (float)i_total, NULL ); + stats_UpdateInteger( s, p_input->p->counters.p_read_packets, + 1 , NULL); + vlc_mutex_unlock( &p_input->p->counters.counters_lock ); + } + } + return p_block; +} + +static int ASeek( stream_t *s, int64_t i_pos ) +{ + stream_sys_t *p_sys = s->p_sys; + access_t *p_access = p_sys->p_access; + + /* Check which stream we need to access */ + if( p_sys->i_list ) + { + int i; + char *psz_name; + int64_t i_size = 0; + access_t *p_list_access = 0; + + for( i = 0; i < p_sys->i_list - 1; i++ ) + { + if( i_pos < p_sys->list[i]->i_size + i_size ) break; + i_size += p_sys->list[i]->i_size; + } + psz_name = p_sys->list[i]->psz_path; + + if( i != p_sys->i_list_index ) + msg_Dbg( s, "opening input `%s'", psz_name ); + + if( i != p_sys->i_list_index && i != 0 ) + { + p_list_access = + access_New( s, p_access->psz_access, "", psz_name ); + } + else if( i != p_sys->i_list_index ) + { + p_list_access = p_access; + } + + if( p_list_access ) + { + if( p_sys->p_list_access != p_access ) + access_Delete( p_sys->p_list_access ); + + p_sys->p_list_access = p_list_access; + } + + p_sys->i_list_index = i; + return p_sys->p_list_access->pf_seek( p_sys->p_list_access, + i_pos - i_size ); + } + + return p_access->pf_seek( p_access, i_pos ); +} + + +/** + * Try to read "i_read" bytes into a buffer pointed by "p_read". If + * "p_read" is NULL then data are skipped instead of read. The return + * value is the real numbers of bytes read/skip. If this value is less + * than i_read that means that it's the end of the stream. + */ +int stream_Read( stream_t *s, void *p_read, int i_read ) +{ + return s->pf_read( s, p_read, i_read ); +} + +/** + * Store in pp_peek a pointer to the next "i_peek" bytes in the stream + * \return The real numbers of valid bytes, if it's less + * or equal to 0, *pp_peek is invalid. + * \note pp_peek is a pointer to internal buffer and it will be invalid as + * soons as other stream_* functions are called. + * \note Due to input limitation, it could be less than i_peek without meaning + * the end of the stream (but only when you have i_peek >= + * p_input->i_bufsize) + */ +int stream_Peek( stream_t *s, const uint8_t **pp_peek, int i_peek ) +{ + return s->pf_peek( s, pp_peek, i_peek ); +} + +/** + * Use to control the "stream_t *". Look at #stream_query_e for + * possible "i_query" value and format arguments. Return VLC_SUCCESS + * if ... succeed ;) and VLC_EGENERIC if failed or unimplemented + */ +int stream_vaControl( stream_t *s, int i_query, va_list args ) +{ + return s->pf_control( s, i_query, args ); +} + +/** + * Destroy a stream + */ +void stream_Delete( stream_t *s ) +{ + s->pf_destroy( s ); +} + +int stream_Control( stream_t *s, int i_query, ... ) +{ + va_list args; + int i_result; + + if ( s == NULL ) + return VLC_EGENERIC; + + va_start( args, i_query ); + i_result = s->pf_control( s, i_query, args ); + va_end( args ); + return i_result; +} + +/** + * Read "i_size" bytes and store them in a block_t. + * It always read i_size bytes unless you are at the end of the stream + * where it return what is available. + */ +block_t *stream_Block( stream_t *s, int i_size ) +{ + if( i_size <= 0 ) return NULL; + + /* emulate block read */ + block_t *p_bk = block_New( s, i_size ); + if( p_bk ) + { + int i_read = stream_Read( s, p_bk->p_buffer, i_size ); + if( i_read > 0 ) + { + p_bk->i_buffer = i_read; + return p_bk; + } + block_Release( p_bk ); + } + return NULL; +} diff --git a/VLC/input/subtitles.c b/VLC/input/subtitles.c new file mode 100644 index 0000000..64de229 --- /dev/null +++ b/VLC/input/subtitles.c @@ -0,0 +1,479 @@ +/***************************************************************************** + * subtitles.c + ***************************************************************************** + * Copyright (C) 2003-2006 the VideoLAN team + * $Id$ + * + * Authors: Derk-Jan Hartman + * This is adapted code from the GPL'ed MPlayer (http://mplayerhq.hu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/** + * \file + * This file contains functions to dectect subtitle files. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "vlc_input.h" +#include "vlc_charset.h" + +#ifdef HAVE_DIRENT_H +# include +#endif + +#include + +#ifdef HAVE_UNISTD_H +# include +#endif +#include + +#include +#include "input_internal.h" + +/** + * We are not going to autodetect more subtitle files than this. + */ +#define MAX_SUBTITLE_FILES 128 + + +/** + * The possible extensions for subtitle files we support + */ +static const char const sub_exts[][6] = { + "utf", "utf8", "utf-8", + "sub", "srt", "smi", + "txt", "ssa", "idx", + + "cdg", + + "" +}; + +/* extensions from unsupported types */ +/* rt, aqt, jss, js, ass */ + +static void strcpy_trim( char *d, const char *s ) +{ + /* skip leading whitespace */ + while( *s && !isalnum(*s) ) + { + s++; + } + for(;;) + { + /* copy word */ + while( *s && isalnum(*s) ) + { + *d = tolower(*s); + s++; d++; + } + if( *s == 0 ) break; + /* trim excess whitespace */ + while( *s && !isalnum(*s) ) + { + s++; + } + if( *s == 0 ) break; + *d++ = ' '; + } + *d = 0; +} + +static void strcpy_strip_ext( char *d, const char *s ) +{ + const char *tmp = strrchr(s, '.'); + if( !tmp ) + { + strcpy(d, s); + return; + } + else + strlcpy(d, s, tmp - s + 1 ); + while( *d ) + { + *d = tolower(*d); + d++; + } +} + +static void strcpy_get_ext( char *d, const char *s ) +{ + const char *tmp = strrchr(s, '.'); + if( !tmp ) + strcpy(d, ""); + else + strcpy( d, tmp + 1 ); +} + +static int whiteonly( const char *s ) +{ + while( *s ) + { + if( isalnum( *s ) ) + return 0; + s++; + } + return 1; +} + +enum +{ + SUB_PRIORITY_NONE = 0, + SUB_PRIORITY_MATCH_NONE = 1, + SUB_PRIORITY_MATCH_RIGHT = 2, + SUB_PRIORITY_MATCH_LEFT = 3, + SUB_PRIORITY_MATCH_ALL = 4, +}; +typedef struct +{ + int priority; + char *psz_fname; + char *psz_ext; +} vlc_subfn_t; + +static int compare_sub_priority( const void *a, const void *b ) +{ + const vlc_subfn_t *p0 = a; + const vlc_subfn_t *p1 = b; + + if( p0->priority > p1->priority ) + return -1; + + if( p0->priority < p1->priority ) + return 1; + +#ifndef UNDER_CE + return strcoll( p0->psz_fname, p1->psz_fname); +#else + return strcmp( p0->psz_fname, p1->psz_fname); +#endif +} + +/* + * Check if a file ends with a subtitle extension + */ +int subtitles_Filter( const char *psz_dir_content ) +{ + const char *tmp = strrchr( psz_dir_content, '.'); + int i; + + if( !tmp ) + return 0; + tmp++; + + for( i = 0; sub_exts[i][0]; i++ ) + if( strcasecmp( sub_exts[i], tmp ) == 0 ) + return 1; + return 0; +} + + +/** + * Convert a list of paths separated by ',' to a char** + */ +static char **paths_to_list( const char *psz_dir, char *psz_path ) +{ + unsigned int i, k, i_nb_subdirs; + char **subdirs; /* list of subdirectories to look in */ + char *psz_parser = psz_path; + + if( !psz_dir || !psz_path ) + return NULL; + + for( k = 0, i_nb_subdirs = 1; psz_path[k] != '\0'; k++ ) + { + if( psz_path[k] == ',' ) + i_nb_subdirs++; + } + + subdirs = calloc( i_nb_subdirs + 1, sizeof(char*) ); + if( !subdirs ) + return NULL; + + for( i = 0; psz_parser && *psz_parser != '\0' ; ) + { + char *psz_subdir = psz_parser; + psz_parser = strchr( psz_subdir, ',' ); + if( psz_parser ) + { + *psz_parser++ = '\0'; + while( *psz_parser == ' ' ) + psz_parser++; + } + if( *psz_subdir == '\0' ) + continue; + + if( asprintf( &subdirs[i++], "%s%s%c", + psz_subdir[0] == '.' ? psz_dir : "", + psz_subdir, + psz_subdir[strlen(psz_subdir) - 1] == DIR_SEP_CHAR ? + '\0' : DIR_SEP_CHAR ) == -1 ) + break; + } + subdirs[i] = NULL; + + return subdirs; +} + + +/** + * Detect subtitle files. + * + * When called this function will split up the psz_name string into a + * directory, filename and extension. It then opens the directory + * in which the file resides and tries to find possible matches of + * subtitles files. + * + * \ingroup Demux + * \param p_this the calling \ref input_thread_t + * \param psz_path a list of subdirectories (separated by a ',') to look in. + * \param psz_name the complete filename to base the search on. + * \return a NULL terminated array of filenames with detected possible subtitles. + * The array contains max MAX_SUBTITLE_FILES items and you need to free it after use. + */ +char **subtitles_Detect( input_thread_t *p_this, char *psz_path, + const char *psz_name_org ) +{ + vlc_value_t fuzzy; + int j, i_result2, i_sub_count, i_fname_len; + char *f_dir = NULL, *f_fname = NULL, *f_fname_noext = NULL, *f_fname_trim = NULL; + char *tmp = NULL; + + char **subdirs; /* list of subdirectories to look in */ + + vlc_subfn_t *result = NULL; /* unsorted results */ + char **result2; /* sorted results */ + const char *psz_fname = psz_name_org; + + if( !psz_fname ) + return NULL; + + if( !strncmp( psz_fname, "file://", 7 ) ) + psz_fname += 7; + + /* extract filename & dirname from psz_fname */ + tmp = strrchr( psz_fname, DIR_SEP_CHAR ); + if( tmp ) + { + const int i_dirlen = strlen(psz_fname)-strlen(tmp)+1; /* include the separator */ + f_fname = strdup( &tmp[1] ); /* skip the separator */ + f_dir = strndup( psz_fname, i_dirlen ); + } + else + { +#ifdef HAVE_UNISTD_H + /* Get the current working directory */ + char *psz_cwd = getcwd( NULL, 0 ); +#else + char *psz_cwd = NULL; +#endif + if( !psz_cwd ) + return NULL; + + f_fname = strdup( psz_fname ); + if( asprintf( &f_dir, "%s%c", psz_cwd, DIR_SEP_CHAR ) == -1 ) + f_dir = NULL; /* Assure that function will return in next test */ + free( psz_cwd ); + } + if( !f_fname || !f_dir ) + { + free( f_fname ); + free( f_dir ); + return NULL; + } + + i_fname_len = strlen( f_fname ); + + f_fname_noext = malloc(i_fname_len + 1); + f_fname_trim = malloc(i_fname_len + 1 ); + if( !f_fname_noext || !f_fname_trim ) + { + free( f_fname ); + free( f_dir ); + free( f_fname_noext ); + free( f_fname_trim ); + return NULL; + } + + strcpy_strip_ext( f_fname_noext, f_fname ); + strcpy_trim( f_fname_trim, f_fname_noext ); + + var_Get( p_this, "sub-autodetect-fuzzy", &fuzzy ); + + result = calloc( MAX_SUBTITLE_FILES+1, sizeof(vlc_subfn_t) ); /* We check it later (simplify code) */ + subdirs = paths_to_list( f_dir, psz_path ); + for( j = -1, i_sub_count = 0; (j == -1) || ( j >= 0 && subdirs != NULL && subdirs[j] != NULL ); j++ ) + { + const char *psz_dir = j < 0 ? f_dir : subdirs[j]; + char **ppsz_dir_content; + int i_dir_content; + int a; + + if( psz_dir == NULL || ( j >= 0 && !strcmp( psz_dir, f_dir ) ) ) + continue; + + /* parse psz_src dir */ + i_dir_content = utf8_scandir( psz_dir, &ppsz_dir_content, + subtitles_Filter, NULL ); + if( i_dir_content < 0 ) + continue; + + msg_Dbg( p_this, "looking for a subtitle file in %s", psz_dir ); + for( a = 0; a < i_dir_content && i_sub_count < MAX_SUBTITLE_FILES ; a++ ) + { + char *psz_name = ppsz_dir_content[a]; + char tmp_fname_noext[strlen( psz_name ) + 1]; + char tmp_fname_trim[strlen( psz_name ) + 1]; + char tmp_fname_ext[strlen( psz_name ) + 1]; + + int i_prio; + + if( psz_name == NULL ) + continue; + + /* retrieve various parts of the filename */ + strcpy_strip_ext( tmp_fname_noext, psz_name ); + strcpy_get_ext( tmp_fname_ext, psz_name ); + strcpy_trim( tmp_fname_trim, tmp_fname_noext ); + + i_prio = SUB_PRIORITY_NONE; + if( i_prio == SUB_PRIORITY_NONE && !strcmp( tmp_fname_trim, f_fname_trim ) ) + { + /* matches the movie name exactly */ + i_prio = SUB_PRIORITY_MATCH_ALL; + } + if( i_prio == SUB_PRIORITY_NONE && + ( tmp = strstr( tmp_fname_trim, f_fname_trim ) ) ) + { + /* contains the movie name */ + tmp += strlen( f_fname_trim ); + if( whiteonly( tmp ) ) + { + /* chars in front of the movie name */ + i_prio = SUB_PRIORITY_MATCH_RIGHT; + } + else + { + /* chars after (and possibly in front of) + * the movie name */ + i_prio = SUB_PRIORITY_MATCH_LEFT; + } + } + if( i_prio == SUB_PRIORITY_NONE && + j == 0 ) + { + /* doesn't contain the movie name, prefer files in f_dir over subdirs */ + i_prio = SUB_PRIORITY_MATCH_NONE; + } + if( i_prio >= fuzzy.i_int ) + { + char psz_path[strlen( psz_dir ) + strlen( psz_name ) + 1]; + struct stat st; + + sprintf( psz_path, "%s%s", psz_dir, psz_name ); + if( !strcmp( psz_path, psz_fname ) ) + continue; + + if( !utf8_stat( psz_path, &st ) && S_ISREG( st.st_mode ) && result ) + { + msg_Dbg( p_this, + "autodetected subtitle: %s with priority %d", + psz_path, i_prio ); + result[i_sub_count].priority = i_prio; + result[i_sub_count].psz_fname = strdup( psz_path ); + result[i_sub_count].psz_ext = strdup(tmp_fname_ext); + i_sub_count++; + } + else + { + msg_Dbg( p_this, "stat failed (autodetecting subtitle: %s with priority %d)", + psz_path, i_prio ); + } + } + } + if( ppsz_dir_content ) + { + for( a = 0; a < i_dir_content; a++ ) + free( ppsz_dir_content[a] ); + free( ppsz_dir_content ); + } + } + if( subdirs ) + { + for( j = 0; subdirs[j]; j++ ) + free( subdirs[j] ); + free( subdirs ); + } + free( f_fname ); + free( f_dir ); + free( f_fname_trim ); + free( f_fname_noext ); + + if( !result ) + return NULL; + + qsort( result, i_sub_count, sizeof(vlc_subfn_t), compare_sub_priority ); + + result2 = calloc( i_sub_count + 1, sizeof(char*) ); + + for( j = 0, i_result2 = 0; j < i_sub_count && result2 != NULL; j++ ) + { + bool b_reject = false; + + if( !result[j].psz_fname || !result[j].psz_ext ) /* memory out */ + break; + + if( !strcasecmp( result[j].psz_ext, "sub" ) ) + { + int i; + for( i = 0; i < i_sub_count; i++ ) + { + if( result[i].psz_fname && result[i].psz_ext && + !strncasecmp( result[j].psz_fname, result[i].psz_fname, + strlen( result[j].psz_fname) - 3 ) && + !strcasecmp( result[i].psz_ext, "idx" ) ) + break; + } + if( i < i_sub_count ) + b_reject = true; + } + else if( !strcasecmp( result[j].psz_ext, "cdg" ) ) + { + if( result[j].priority < SUB_PRIORITY_MATCH_ALL ) + b_reject = true; + } + + /* */ + if( !b_reject ) + result2[i_result2++] = strdup( result[j].psz_fname ); + } + + for( j = 0; j < i_sub_count; j++ ) + { + free( result[j].psz_fname ); + free( result[j].psz_ext ); + } + free( result ); + + return result2; +} + diff --git a/VLC/input/var.c b/VLC/input/var.c new file mode 100644 index 0000000..43d032b --- /dev/null +++ b/VLC/input/var.c @@ -0,0 +1,778 @@ +/***************************************************************************** + * var.c: object variables for input thread + ***************************************************************************** + * Copyright (C) 2004-2007 the VideoLAN team + * $Id$ + * + * Authors: Laurent Aimar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include +#include + +#include "input_internal.h" + +/***************************************************************************** + * Callbacks + *****************************************************************************/ +static int StateCallback ( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void * ); +static int RateCallback ( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void * ); +static int PositionCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void * ); +static int TimeCallback ( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void * ); +static int ProgramCallback ( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void * ); +static int TitleCallback ( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void * ); +static int SeekpointCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void * ); +static int NavigationCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void * ); +static int ESCallback ( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void * ); +static int EsDelayCallback ( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void * ); + +static int BookmarkCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void * ); + +typedef struct +{ + const char *psz_name; + vlc_callback_t callback; +} vlc_input_callback_t; +static void InputAddCallbacks( input_thread_t *, const vlc_input_callback_t * ); +static void InputDelCallbacks( input_thread_t *, const vlc_input_callback_t * ); + +#ifdef CALLBACK /* For windows */ +# undef CALLBACK /* We don't care of this one here */ +#endif +/* List all callbacks added by input */ +#define CALLBACK(name,cb) { name, cb } +static const vlc_input_callback_t p_input_callbacks[] = +{ + CALLBACK( "state", StateCallback ), + CALLBACK( "rate", RateCallback ), + CALLBACK( "rate-slower", RateCallback ), + CALLBACK( "rate-faster", RateCallback ), + CALLBACK( "position", PositionCallback ), + CALLBACK( "position-offset", PositionCallback ), + CALLBACK( "time", TimeCallback ), + CALLBACK( "time-offset", TimeCallback ), + CALLBACK( "bookmark", BookmarkCallback ), + CALLBACK( "program", ProgramCallback ), + CALLBACK( "title", TitleCallback ), + CALLBACK( "chapter", SeekpointCallback ), + CALLBACK( "audio-delay", EsDelayCallback ), + CALLBACK( "spu-delay", EsDelayCallback ), + CALLBACK( "video-es", ESCallback ), + CALLBACK( "audio-es", ESCallback ), + CALLBACK( "spu-es", ESCallback ), + + CALLBACK( NULL, NULL ) +}; +static const vlc_input_callback_t p_input_navigation_callbacks[] = +{ + CALLBACK( "next-title", TitleCallback ), + CALLBACK( "prev-title", TitleCallback ), + + CALLBACK( NULL, NULL ) +}; +static const vlc_input_callback_t p_input_title_callbacks[] = +{ + CALLBACK( "next-chapter", SeekpointCallback ), + CALLBACK( "prev-chapter", SeekpointCallback ), + + CALLBACK( NULL, NULL ) +}; +#undef CALLBACK + +/***************************************************************************** + * input_ControlVarInit: + * Create all control object variables with their callbacks + *****************************************************************************/ +void input_ControlVarInit ( input_thread_t *p_input ) +{ + vlc_value_t val, text; + + /* State */ + var_Create( p_input, "state", VLC_VAR_INTEGER ); + val.i_int = p_input->i_state; + var_Change( p_input, "state", VLC_VAR_SETVALUE, &val, NULL ); + + /* Rate */ + var_Create( p_input, "rate", VLC_VAR_INTEGER ); + val.i_int = p_input->p->i_rate; + var_Change( p_input, "rate", VLC_VAR_SETVALUE, &val, NULL ); + + var_Create( p_input, "rate-slower", VLC_VAR_VOID ); + + var_Create( p_input, "rate-faster", VLC_VAR_VOID ); + + /* Position */ + var_Create( p_input, "position", VLC_VAR_FLOAT ); + var_Create( p_input, "position-offset", VLC_VAR_FLOAT ); + val.f_float = 0.0; + var_Change( p_input, "position", VLC_VAR_SETVALUE, &val, NULL ); + + /* Time */ + var_Create( p_input, "time", VLC_VAR_TIME ); + var_Create( p_input, "time-offset", VLC_VAR_TIME ); /* relative */ + val.i_time = 0; + var_Change( p_input, "time", VLC_VAR_SETVALUE, &val, NULL ); + + /* Bookmark */ + var_Create( p_input, "bookmark", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE | + VLC_VAR_ISCOMMAND ); + val.psz_string = _("Bookmark"); + var_Change( p_input, "bookmark", VLC_VAR_SETTEXT, &val, NULL ); + + /* Program */ + var_Create( p_input, "program", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE | + VLC_VAR_DOINHERIT ); + var_Get( p_input, "program", &val ); + if( val.i_int <= 0 ) + var_Change( p_input, "program", VLC_VAR_DELCHOICE, &val, NULL ); + text.psz_string = _("Program"); + var_Change( p_input, "program", VLC_VAR_SETTEXT, &text, NULL ); + + /* Programs */ + var_Create( p_input, "programs", VLC_VAR_LIST | VLC_VAR_DOINHERIT ); + text.psz_string = _("Programs"); + var_Change( p_input, "programs", VLC_VAR_SETTEXT, &text, NULL ); + + /* Title */ + var_Create( p_input, "title", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE ); + text.psz_string = _("Title"); + var_Change( p_input, "title", VLC_VAR_SETTEXT, &text, NULL ); + + /* Chapter */ + var_Create( p_input, "chapter", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE ); + text.psz_string = _("Chapter"); + var_Change( p_input, "chapter", VLC_VAR_SETTEXT, &text, NULL ); + + /* Navigation The callback is added after */ + var_Create( p_input, "navigation", VLC_VAR_VARIABLE | VLC_VAR_HASCHOICE ); + text.psz_string = _("Navigation"); + var_Change( p_input, "navigation", VLC_VAR_SETTEXT, &text, NULL ); + + /* Delay */ + var_Create( p_input, "audio-delay", VLC_VAR_TIME ); + val.i_time = 0; + var_Change( p_input, "audio-delay", VLC_VAR_SETVALUE, &val, NULL ); + var_Create( p_input, "spu-delay", VLC_VAR_TIME ); + val.i_time = 0; + var_Change( p_input, "spu-delay", VLC_VAR_SETVALUE, &val, NULL ); + + p_input->p->pts_adjust.auto_adjust = var_CreateGetBool( + p_input, "auto-adjust-pts-delay" ); + + /* Video ES */ + var_Create( p_input, "video-es", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE ); + text.psz_string = _("Video Track"); + var_Change( p_input, "video-es", VLC_VAR_SETTEXT, &text, NULL ); + + /* Audio ES */ + var_Create( p_input, "audio-es", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE ); + text.psz_string = _("Audio Track"); + var_Change( p_input, "audio-es", VLC_VAR_SETTEXT, &text, NULL ); + + /* Spu ES */ + var_Create( p_input, "spu-es", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE ); + text.psz_string = _("Subtitles Track"); + var_Change( p_input, "spu-es", VLC_VAR_SETTEXT, &text, NULL ); + + /* Special read only objects variables for intf */ + var_Create( p_input, "bookmarks", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); + + var_Create( p_input, "length", VLC_VAR_TIME ); + val.i_time = 0; + var_Change( p_input, "length", VLC_VAR_SETVALUE, &val, NULL ); + + if( !p_input->b_preparsing ) + { + /* Special "intf-change" variable, it allows intf to set up a callback + * to be notified of some changes. + * + * Add rate-change to inform about rate changin + * + * TODO list all changes warn by this callbacks */ + var_Create( p_input, "intf-change", VLC_VAR_BOOL ); + var_SetBool( p_input, "intf-change", true ); + var_Create( p_input, "rate-change", VLC_VAR_BOOL ); + var_SetBool( p_input, "rate-change", true ); + } + + /* Add all callbacks + * XXX we put callback only in non preparsing mode. We need to create the variable + * unless someone want to check all var_Get/var_Change return value ... */ + if( !p_input->b_preparsing ) + InputAddCallbacks( p_input, p_input_callbacks ); +} + +/***************************************************************************** + * input_ControlVarStop: + *****************************************************************************/ +void input_ControlVarStop( input_thread_t *p_input ) +{ + InputDelCallbacks( p_input, p_input_callbacks ); + + if( p_input->p->i_title > 0 ) + { + char name[sizeof("title ") + 5 ]; + int i; + + InputDelCallbacks( p_input, p_input_navigation_callbacks ); + InputDelCallbacks( p_input, p_input_title_callbacks ); + + for( i = 0; i < p_input->p->i_title; i++ ) + { + snprintf( name, sizeof(name), "title %2i", i ); + var_DelCallback( p_input, name, NavigationCallback, (void *)(intptr_t)i ); + } + } +} + +/***************************************************************************** + * input_ControlVarNavigation: + * Create all remaining control object variables + *****************************************************************************/ +void input_ControlVarNavigation( input_thread_t *p_input ) +{ + vlc_value_t val, text; + int i; + + /* Create more command variables */ + if( p_input->p->i_title > 1 ) + { + var_Create( p_input, "next-title", VLC_VAR_VOID ); + text.psz_string = _("Next title"); + var_Change( p_input, "next-title", VLC_VAR_SETTEXT, &text, NULL ); + var_AddCallback( p_input, "next-title", TitleCallback, NULL ); + + var_Create( p_input, "prev-title", VLC_VAR_VOID ); + text.psz_string = _("Previous title"); + var_Change( p_input, "prev-title", VLC_VAR_SETTEXT, &text, NULL ); + var_AddCallback( p_input, "prev-title", TitleCallback, NULL ); + } + + /* Create title and navigation */ + val.psz_string = malloc( sizeof("title ") + 5 ); + if( !val.psz_string ) + return; + + for( i = 0; i < p_input->p->i_title; i++ ) + { + vlc_value_t val2, text2; + int j; + + /* Add Navigation entries */ + sprintf( val.psz_string, "title %2i", i ); + var_Destroy( p_input, val.psz_string ); + var_Create( p_input, val.psz_string, + VLC_VAR_INTEGER|VLC_VAR_HASCHOICE|VLC_VAR_ISCOMMAND ); + var_AddCallback( p_input, val.psz_string, + NavigationCallback, (void *)(intptr_t)i ); + + if( p_input->p->title[i]->psz_name == NULL || + *p_input->p->title[i]->psz_name == '\0' ) + { + if( asprintf( &text.psz_string, _("Title %i"), + i + p_input->p->i_title_offset ) == -1 ) + continue; + } + else + { + text.psz_string = strdup( p_input->p->title[i]->psz_name ); + } + var_Change( p_input, "navigation", VLC_VAR_ADDCHOICE, &val, &text ); + + /* Add title choice */ + val2.i_int = i; + var_Change( p_input, "title", VLC_VAR_ADDCHOICE, &val2, &text ); + + free( text.psz_string ); + + for( j = 0; j < p_input->p->title[i]->i_seekpoint; j++ ) + { + val2.i_int = j; + + if( p_input->p->title[i]->seekpoint[j]->psz_name == NULL || + *p_input->p->title[i]->seekpoint[j]->psz_name == '\0' ) + { + /* Default value */ + if( asprintf( &text2.psz_string, _("Chapter %i"), + j + p_input->p->i_seekpoint_offset ) == -1 ) + continue; + } + else + { + text2.psz_string = + strdup( p_input->p->title[i]->seekpoint[j]->psz_name ); + } + + var_Change( p_input, val.psz_string, VLC_VAR_ADDCHOICE, + &val2, &text2 ); + free( text2.psz_string ); + } + + } + free( val.psz_string ); +} + +/***************************************************************************** + * input_ControlVarTitle: + * Create all variables for a title + *****************************************************************************/ +void input_ControlVarTitle( input_thread_t *p_input, int i_title ) +{ + input_title_t *t = p_input->p->title[i_title]; + vlc_value_t val, text; + int i; + + /* Create/Destroy command variables */ + if( t->i_seekpoint <= 1 ) + { + var_Destroy( p_input, "next-chapter" ); + var_Destroy( p_input, "prev-chapter" ); + } + else if( var_Get( p_input, "next-chapter", &val ) != VLC_SUCCESS ) + { + var_Create( p_input, "next-chapter", VLC_VAR_VOID ); + text.psz_string = _("Next chapter"); + var_Change( p_input, "next-chapter", VLC_VAR_SETTEXT, &text, NULL ); + var_AddCallback( p_input, "next-chapter", SeekpointCallback, NULL ); + + var_Create( p_input, "prev-chapter", VLC_VAR_VOID ); + text.psz_string = _("Previous chapter"); + var_Change( p_input, "prev-chapter", VLC_VAR_SETTEXT, &text, NULL ); + var_AddCallback( p_input, "prev-chapter", SeekpointCallback, NULL ); + } + + /* Build chapter list */ + var_Change( p_input, "chapter", VLC_VAR_CLEARCHOICES, NULL, NULL ); + for( i = 0; i < t->i_seekpoint; i++ ) + { + val.i_int = i; + + if( t->seekpoint[i]->psz_name == NULL || + *t->seekpoint[i]->psz_name == '\0' ) + { + /* Default value */ + if( asprintf( &text.psz_string, _("Chapter %i"), + i + p_input->p->i_seekpoint_offset ) == -1 ) + continue; + } + else + { + text.psz_string = strdup( t->seekpoint[i]->psz_name ); + } + + var_Change( p_input, "chapter", VLC_VAR_ADDCHOICE, &val, &text ); + free( text.psz_string ); + } +} + +/***************************************************************************** + * input_ConfigVarInit: + * Create all config object variables + *****************************************************************************/ +void input_ConfigVarInit ( input_thread_t *p_input ) +{ + vlc_value_t val; + + /* Create Object Variables for private use only */ + + if( !p_input->b_preparsing ) + { + var_Create( p_input, "video", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); + var_Create( p_input, "audio", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); + var_Create( p_input, "spu", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); + + var_Create( p_input, "audio-track", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT ); + var_Create( p_input, "sub-track", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT ); + + var_Create( p_input, "audio-language", + VLC_VAR_STRING|VLC_VAR_DOINHERIT ); + var_Create( p_input, "sub-language", + VLC_VAR_STRING|VLC_VAR_DOINHERIT ); + + var_Create( p_input, "audio-track-id", + VLC_VAR_INTEGER|VLC_VAR_DOINHERIT ); + var_Create( p_input, "sub-track-id", + VLC_VAR_INTEGER|VLC_VAR_DOINHERIT ); + + var_Create( p_input, "sub-file", VLC_VAR_FILE | VLC_VAR_DOINHERIT ); + var_Create( p_input, "sub-autodetect-file", VLC_VAR_BOOL | + VLC_VAR_DOINHERIT ); + var_Create( p_input, "sub-autodetect-path", VLC_VAR_STRING | + VLC_VAR_DOINHERIT ); + var_Create( p_input, "sub-autodetect-fuzzy", VLC_VAR_INTEGER | + VLC_VAR_DOINHERIT ); + + var_Create( p_input, "sout", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); + var_Create( p_input, "sout-all", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); + var_Create( p_input, "sout-audio", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); + var_Create( p_input, "sout-video", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); + var_Create( p_input, "sout-spu", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); + var_Create( p_input, "sout-keep", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); + + var_Create( p_input, "input-repeat", + VLC_VAR_INTEGER|VLC_VAR_DOINHERIT ); + var_Create( p_input, "start-time", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT ); + var_Create( p_input, "stop-time", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT ); + var_Create( p_input, "run-time", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT ); + + var_Create( p_input, "input-slave", + VLC_VAR_STRING | VLC_VAR_DOINHERIT ); + + var_Create( p_input, "minimize-threads", + VLC_VAR_BOOL|VLC_VAR_DOINHERIT ); + + var_Create( p_input, "audio-desync", + VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); + var_Create( p_input, "cr-average", + VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); + var_Create( p_input, "clock-synchro", + VLC_VAR_INTEGER | VLC_VAR_DOINHERIT); + } + + var_Create( p_input, "seekable", VLC_VAR_BOOL ); + val.b_bool = true; /* Fixed later*/ + var_Change( p_input, "seekable", VLC_VAR_SETVALUE, &val, NULL ); + var_Create( p_input, "can-pause", VLC_VAR_BOOL ); + val.b_bool = true; /* Fixed later*/ + var_Change( p_input, "can-pause", VLC_VAR_SETVALUE, &val, NULL ); + var_Create( p_input, "teletext-es", VLC_VAR_INTEGER ); + var_SetInteger( p_input, "teletext-es", -1 ); + + /* */ + var_Create( p_input, "access-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); + var_Create( p_input, "access", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); + var_Create( p_input, "demux", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); + + /* Meta */ + var_Create( p_input, "meta-title", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); + var_Create( p_input, "meta-author", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); + var_Create( p_input, "meta-artist", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); + var_Create( p_input, "meta-genre", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); + var_Create( p_input, "meta-copyright", VLC_VAR_STRING | VLC_VAR_DOINHERIT); + var_Create( p_input, "meta-description", VLC_VAR_STRING|VLC_VAR_DOINHERIT); + var_Create( p_input, "meta-date", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); + var_Create( p_input, "meta-url", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); +} + +/***************************************************************************** + * Callbacks managements: + *****************************************************************************/ +static void InputAddCallbacks( input_thread_t *p_input, + const vlc_input_callback_t *p_callbacks ) +{ + int i; + for( i = 0; p_callbacks[i].psz_name != NULL; i++ ) + var_AddCallback( p_input, + p_callbacks[i].psz_name, + p_callbacks[i].callback, NULL ); +} +static void InputDelCallbacks( input_thread_t *p_input, + const vlc_input_callback_t *p_callbacks ) +{ + int i; + for( i = 0; p_callbacks[i].psz_name != NULL; i++ ) + var_DelCallback( p_input, + p_callbacks[i].psz_name, + p_callbacks[i].callback, NULL ); +} +/***************************************************************************** + * All Callbacks: + *****************************************************************************/ +static int StateCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, + void *p_data ) +{ + input_thread_t *p_input = (input_thread_t*)p_this; + VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data); + + if( newval.i_int == PLAYING_S || newval.i_int == PAUSE_S ) + { + input_ControlPush( p_input, INPUT_CONTROL_SET_STATE, &newval ); + return VLC_SUCCESS; + } + + return VLC_EGENERIC; +} + +static int RateCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) +{ + input_thread_t *p_input = (input_thread_t*)p_this; + VLC_UNUSED(oldval); VLC_UNUSED(p_data); + + /* Problem with this way: the "rate" variable is update after the input thread do the change */ + if( !strcmp( psz_cmd, "rate-slower" ) ) + { + input_ControlPush( p_input, INPUT_CONTROL_SET_RATE_SLOWER, NULL ); + } + else if( !strcmp( psz_cmd, "rate-faster" ) ) + { + input_ControlPush( p_input, INPUT_CONTROL_SET_RATE_FASTER, NULL ); + } + else + { + input_ControlPush( p_input, INPUT_CONTROL_SET_RATE, &newval ); + } + + return VLC_SUCCESS; +} + +static int PositionCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, + void *p_data ) +{ + input_thread_t *p_input = (input_thread_t*)p_this; + vlc_value_t val, length; + VLC_UNUSED(oldval); VLC_UNUSED(p_data); + + if( !strcmp( psz_cmd, "position-offset" ) ) + { + input_ControlPush( p_input, INPUT_CONTROL_SET_POSITION_OFFSET, &newval ); + + val.f_float = var_GetFloat( p_input, "position" ) + newval.f_float; + if( val.f_float < 0.0 ) val.f_float = 0.0; + if( val.f_float > 1.0 ) val.f_float = 1.0; + var_Change( p_input, "position", VLC_VAR_SETVALUE, &val, NULL ); + } + else + { + val.f_float = newval.f_float; + input_ControlPush( p_input, INPUT_CONTROL_SET_POSITION, &newval ); + } + + /* Update "position" for better intf behavour */ + var_Get( p_input, "length", &length ); + if( length.i_time > 0 && val.f_float >= 0.0 && val.f_float <= 1.0 ) + { + val.i_time = length.i_time * val.f_float; + var_Change( p_input, "time", VLC_VAR_SETVALUE, &val, NULL ); + } + + return VLC_SUCCESS; +} + +static int TimeCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) +{ + input_thread_t *p_input = (input_thread_t*)p_this; + vlc_value_t val, length; + VLC_UNUSED(oldval); VLC_UNUSED(p_data); + + if( !strcmp( psz_cmd, "time-offset" ) ) + { + input_ControlPush( p_input, INPUT_CONTROL_SET_TIME_OFFSET, &newval ); + val.i_time = var_GetTime( p_input, "time" ) + newval.i_time; + if( val.i_time < 0 ) val.i_time = 0; + /* TODO maybe test against i_length ? */ + var_Change( p_input, "time", VLC_VAR_SETVALUE, &val, NULL ); + } + else + { + val.i_time = newval.i_time; + input_ControlPush( p_input, INPUT_CONTROL_SET_TIME, &newval ); + } + + /* Update "position" for better intf behavour */ + var_Get( p_input, "length", &length ); + if( length.i_time > 0 && val.i_time >= 0 && val.i_time <= length.i_time ) + { + val.f_float = (double)val.i_time/(double)length.i_time; + var_Change( p_input, "position", VLC_VAR_SETVALUE, &val, NULL ); + } + + return VLC_SUCCESS; +} + +static int ProgramCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, + void *p_data ) +{ + input_thread_t *p_input = (input_thread_t*)p_this; + VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data); + + input_ControlPush( p_input, INPUT_CONTROL_SET_PROGRAM, &newval ); + + return VLC_SUCCESS; +} + +static int TitleCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, + void *p_data ) +{ + input_thread_t *p_input = (input_thread_t*)p_this; + vlc_value_t val, count; + VLC_UNUSED(oldval); VLC_UNUSED(p_data); + + if( !strcmp( psz_cmd, "next-title" ) ) + { + input_ControlPush( p_input, INPUT_CONTROL_SET_TITLE_NEXT, NULL ); + + val.i_int = var_GetInteger( p_input, "title" ) + 1; + var_Change( p_input, "title", VLC_VAR_CHOICESCOUNT, &count, NULL ); + if( val.i_int < count.i_int ) + var_Change( p_input, "title", VLC_VAR_SETVALUE, &val, NULL ); + } + else if( !strcmp( psz_cmd, "prev-title" ) ) + { + input_ControlPush( p_input, INPUT_CONTROL_SET_TITLE_PREV, NULL ); + + val.i_int = var_GetInteger( p_input, "title" ) - 1; + if( val.i_int >= 0 ) + var_Change( p_input, "title", VLC_VAR_SETVALUE, &val, NULL ); + } + else + { + input_ControlPush( p_input, INPUT_CONTROL_SET_TITLE, &newval ); + } + + return VLC_SUCCESS; +} + +static int SeekpointCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, + void *p_data ) +{ + input_thread_t *p_input = (input_thread_t*)p_this; + vlc_value_t val, count; + VLC_UNUSED(oldval); VLC_UNUSED(p_data); + + if( !strcmp( psz_cmd, "next-chapter" ) ) + { + input_ControlPush( p_input, INPUT_CONTROL_SET_SEEKPOINT_NEXT, NULL ); + + val.i_int = var_GetInteger( p_input, "chapter" ) + 1; + var_Change( p_input, "chapter", VLC_VAR_CHOICESCOUNT, &count, NULL ); + if( val.i_int < count.i_int ) + var_Change( p_input, "chapter", VLC_VAR_SETVALUE, &val, NULL ); + } + else if( !strcmp( psz_cmd, "prev-chapter" ) ) + { + input_ControlPush( p_input, INPUT_CONTROL_SET_SEEKPOINT_PREV, NULL ); + + val.i_int = var_GetInteger( p_input, "chapter" ) - 1; + if( val.i_int >= 0 ) + var_Change( p_input, "chapter", VLC_VAR_SETVALUE, &val, NULL ); + } + else + { + input_ControlPush( p_input, INPUT_CONTROL_SET_SEEKPOINT, &newval ); + } + + return VLC_SUCCESS; +} + +static int NavigationCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, + void *p_data ) +{ + input_thread_t *p_input = (input_thread_t*)p_this; + vlc_value_t val; + VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); + + /* Issue a title change */ + val.i_int = (intptr_t)p_data; + input_ControlPush( p_input, INPUT_CONTROL_SET_TITLE, &val ); + + var_Change( p_input, "title", VLC_VAR_SETVALUE, &val, NULL ); + + /* And a chapter change */ + input_ControlPush( p_input, INPUT_CONTROL_SET_SEEKPOINT, &newval ); + + var_Change( p_input, "chapter", VLC_VAR_SETVALUE, &newval, NULL ); + + return VLC_SUCCESS; +} + +static int ESCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) +{ + input_thread_t *p_input = (input_thread_t*)p_this; + VLC_UNUSED(oldval); VLC_UNUSED(p_data); + + if( newval.i_int < 0 ) + { + vlc_value_t v; + /* Hack */ + if( !strcmp( psz_cmd, "audio-es" ) ) + v.i_int = -AUDIO_ES; + else if( !strcmp( psz_cmd, "video-es" ) ) + v.i_int = -VIDEO_ES; + else if( !strcmp( psz_cmd, "spu-es" ) ) + v.i_int = -SPU_ES; + else + v.i_int = 0; + if( v.i_int != 0 ) + input_ControlPush( p_input, INPUT_CONTROL_SET_ES, &v ); + } + else + { + input_ControlPush( p_input, INPUT_CONTROL_SET_ES, &newval ); + } + + return VLC_SUCCESS; +} + +static int EsDelayCallback ( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) +{ + input_thread_t *p_input = (input_thread_t*)p_this; + VLC_UNUSED(oldval); VLC_UNUSED(p_data); + + if( !strcmp( psz_cmd, "audio-delay" ) ) + { + /*Change i_pts_delay to make sure es are decoded in time*/ + if (newval.i_int < 0 || oldval.i_int < 0 ) + { + p_input->i_pts_delay -= newval.i_int - oldval.i_int; + } + input_ControlPush( p_input, INPUT_CONTROL_SET_AUDIO_DELAY, &newval ); + } + else if( !strcmp( psz_cmd, "spu-delay" ) ) + input_ControlPush( p_input, INPUT_CONTROL_SET_SPU_DELAY, &newval ); + return VLC_SUCCESS; +} + +static int BookmarkCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, + void *p_data ) +{ + input_thread_t *p_input = (input_thread_t*)p_this; + VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data); + + input_ControlPush( p_input, INPUT_CONTROL_SET_BOOKMARK, &newval ); + + return VLC_SUCCESS; +} diff --git a/VLC/input/vlm.c b/VLC/input/vlm.c new file mode 100644 index 0000000..460ec84 --- /dev/null +++ b/VLC/input/vlm.c @@ -0,0 +1,2874 @@ +/***************************************************************************** + * vlm.c: VLM interface plugin + ***************************************************************************** + * Copyright (C) 2000-2005 the VideoLAN team + * $Id$ + * + * Authors: Simon Latapie + * Laurent Aimar + * Gildas Bazin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#include +#include /* tolower() */ +#include + +#include "vlc_vlm.h" + +#ifndef WIN32 +# include /* gettimeofday() */ +#endif + +#ifdef HAVE_TIME_H +# include /* ctime() */ +# include /* ftime() */ +#endif + +#include "vlc_input.h" +#include "input_internal.h" +#include "vlc_stream.h" +#include "vlm_internal.h" +#include "vlc_vod.h" +#include "vlc_charset.h" +#include "vlc_sout.h" +#include "stream_output.h" +#include "libvlc.h" +static const char *vlm_NULL = NULL; +static const char quotes[] = "\"'"; + +/***************************************************************************** + * Local prototypes. + *****************************************************************************/ + +static void vlm_Destructor( vlm_t *p_vlm ); +static void* Manage( vlc_object_t * ); +static int vlm_MediaVodControl( void *, vod_media_t *, const char *, int, va_list ); +static vlm_media_sys_t *vlm_MediaSearch( vlm_t *, const char *); +static vlm_schedule_sys_t *vlm_ScheduleSearch( vlm_t *, const char * ); +static const char *FindCommandEnd( const char *psz_sent ); +static int vlm_ScheduleSetup( vlm_schedule_sys_t *schedule, const char *psz_cmd, + const char *psz_value ); +static vlm_message_t *vlm_Show( vlm_t *vlm, vlm_media_sys_t *media, + vlm_schedule_sys_t *schedule, + const char *psz_filter ); +static int Load( vlm_t *vlm, char *file ); +static char *Save( vlm_t *vlm ); +static vlm_schedule_sys_t *vlm_ScheduleNew( vlm_t *vlm, const char *psz_name ); +/***************************************************************************** + * vlm_New: + *****************************************************************************/ +vlm_t *__vlm_New ( vlc_object_t *p_this ) +{ + vlc_value_t lockval; + vlm_t *p_vlm = NULL, **pp_vlm = &(libvlc_priv (p_this->p_libvlc)->p_vlm); + char *psz_vlmconf; + static const char vlm_object_name[] = "vlm daemon"; + + /* Avoid multiple creation */ + if( var_Create( p_this->p_libvlc, "vlm_mutex", VLC_VAR_MUTEX ) || + var_Get( p_this->p_libvlc, "vlm_mutex", &lockval ) ) + return NULL; + + vlc_mutex_lock( lockval.p_address ); + + p_vlm = *pp_vlm; + if( p_vlm ) + { /* VLM already exists */ + vlc_object_yield( p_vlm ); + vlc_mutex_unlock( lockval.p_address ); + return p_vlm; + } + + msg_Dbg( p_this, "creating VLM" ); + + p_vlm = vlc_custom_create( p_this, sizeof( *p_vlm ), VLC_OBJECT_GENERIC, + vlm_object_name ); + if( !p_vlm ) + { + vlc_mutex_unlock( lockval.p_address ); + return NULL; + } + + vlc_mutex_init( &p_vlm->lock ); + p_vlm->i_id = 1; + TAB_INIT( p_vlm->i_media, p_vlm->media ); + TAB_INIT( p_vlm->i_schedule, p_vlm->schedule ); + p_vlm->i_vod = 0; + p_vlm->p_vod = NULL; + vlc_object_attach( p_vlm, p_this->p_libvlc ); + + if( vlc_thread_create( p_vlm, "vlm thread", + Manage, VLC_THREAD_PRIORITY_LOW, false ) ) + { + vlc_mutex_destroy( &p_vlm->lock ); + vlc_object_release( p_vlm ); + return NULL; + } + + /* Load our configuration file */ + psz_vlmconf = var_CreateGetString( p_vlm, "vlm-conf" ); + if( psz_vlmconf && *psz_vlmconf ) + { + vlm_message_t *p_message = NULL; + char *psz_buffer = NULL; + + msg_Dbg( p_this, "loading VLM configuration" ); + if( asprintf(&psz_buffer, "load %s", psz_vlmconf ) != -1 ) + { + msg_Dbg( p_this, "%s", psz_buffer ); + if( vlm_ExecuteCommand( p_vlm, psz_buffer, &p_message ) ) + msg_Warn( p_this, "error while loading the configuration file" ); + + vlm_MessageDelete( p_message ); + free( psz_buffer ); + } + } + free( psz_vlmconf ); + + vlc_object_set_destructor( p_vlm, (vlc_destructor_t)vlm_Destructor ); + *pp_vlm = p_vlm; /* for future reference */ + vlc_mutex_unlock( lockval.p_address ); + + return p_vlm; +} + +/***************************************************************************** + * vlm_Delete: + *****************************************************************************/ +void vlm_Delete( vlm_t *p_vlm ) +{ + vlc_value_t lockval; + + /* vlm_Delete() is serialized against itself, and against vlm_New(). + * This way, vlm_Destructor () (called from vlc_objet_release() above) + * is serialized against setting libvlc_priv->p_vlm from vlm_New(). */ + var_Get( p_vlm->p_libvlc, "vlm_mutex", &lockval ); + vlc_mutex_lock( lockval.p_address ); + /* Parental advisory: terrific humongous horrible follows... + * This is so that vlc_object_destroy() (from vlc_object_release()) will + * NOT join our thread. See also vlm_Destructor(). + * -- Courmisch, 24/08/2008 */ + vlc_internals( p_vlm )->b_thread = false; + vlc_object_release( p_vlm ); + vlc_mutex_unlock( lockval.p_address ); +} + +/***************************************************************************** + * vlm_Destructor: + *****************************************************************************/ +static void vlm_Destructor( vlm_t *p_vlm ) +{ + libvlc_priv (p_vlm->p_libvlc)->p_vlm = NULL; + + vlm_ControlInternal( p_vlm, VLM_CLEAR_MEDIAS ); + TAB_CLEAN( p_vlm->i_media, p_vlm->media ); + + vlm_ControlInternal( p_vlm, VLM_CLEAR_SCHEDULES ); + TAB_CLEAN( p_vlm->schedule, p_vlm->schedule ); + + vlc_object_kill( p_vlm ); + /* Continuation of the vlm_Delete() hack. -- Courmisch */ + vlc_internals( p_vlm )->b_thread = true; + vlc_thread_join( p_vlm ); + vlc_mutex_destroy( &p_vlm->lock ); +} + +/***************************************************************************** + * vlm_ExecuteCommand: + *****************************************************************************/ +int vlm_ExecuteCommand( vlm_t *p_vlm, const char *psz_command, + vlm_message_t **pp_message) +{ + int i_result; + + vlc_mutex_lock( &p_vlm->lock ); + i_result = ExecuteCommand( p_vlm, psz_command, pp_message ); + vlc_mutex_unlock( &p_vlm->lock ); + + return i_result; +} + + +int64_t vlm_Date(void) +{ +#ifdef WIN32 + struct timeb tm; + ftime( &tm ); + return ((int64_t)tm.time) * 1000000 + ((int64_t)tm.millitm) * 1000; +#else + struct timeval tv_date; + + /* gettimeofday() cannot fail given &tv_date is a valid address */ + (void)gettimeofday( &tv_date, NULL ); + return (mtime_t) tv_date.tv_sec * 1000000 + (mtime_t) tv_date.tv_usec; +#endif +} + + +/***************************************************************************** + * + *****************************************************************************/ +static int vlm_MediaVodControl( void *p_private, vod_media_t *p_vod_media, + const char *psz_id, int i_query, va_list args ) +{ + vlm_t *vlm = (vlm_t *)p_private; + int i, i_ret; + const char *psz; + int64_t id; + + if( !p_private || !p_vod_media ) + return VLC_EGENERIC; + + vlc_mutex_lock( &vlm->lock ); + + /* Find media id */ + for( i = 0, id = -1; i < vlm->i_media; i++ ) + { + if( p_vod_media == vlm->media[i]->vod.p_media ) + { + id = vlm->media[i]->cfg.id; + break; + } + } + if( id == -1 ) + { + vlc_mutex_unlock( &vlm->lock ); + return VLC_EGENERIC; + } + + switch( i_query ) + { + case VOD_MEDIA_PLAY: + psz = (const char *)va_arg( args, const char * ); + i_ret = vlm_ControlInternal( vlm, VLM_START_MEDIA_VOD_INSTANCE, id, psz_id, 0, psz ); + break; + + case VOD_MEDIA_PAUSE: + i_ret = vlm_ControlInternal( vlm, VLM_PAUSE_MEDIA_INSTANCE, id, psz_id ); + break; + + case VOD_MEDIA_STOP: + i_ret = vlm_ControlInternal( vlm, VLM_STOP_MEDIA_INSTANCE, id, psz_id ); + break; + + case VOD_MEDIA_SEEK: + { + double d_position = (double)va_arg( args, double ); + i_ret = vlm_ControlInternal( vlm, VLM_SET_MEDIA_INSTANCE_POSITION, id, psz_id, d_position/100.0 ); + break; + } + + case VOD_MEDIA_REWIND: + { + double d_scale = (double)va_arg( args, double ); + double d_position; + + vlm_ControlInternal( vlm, VLM_GET_MEDIA_INSTANCE_POSITION, id, psz_id, &d_position ); + d_position -= (d_scale / 1000.0); + if( d_position < 0.0 ) + d_position = 0.0; + i_ret = vlm_ControlInternal( vlm, VLM_SET_MEDIA_INSTANCE_POSITION, id, psz_id, d_position ); + break; + } + + case VOD_MEDIA_FORWARD: + { + double d_scale = (double)va_arg( args, double ); + double d_position; + + vlm_ControlInternal( vlm, VLM_GET_MEDIA_INSTANCE_POSITION, id, psz_id, &d_position ); + d_position += (d_scale / 1000.0); + if( d_position > 1.0 ) + d_position = 1.0; + i_ret = vlm_ControlInternal( vlm, VLM_SET_MEDIA_INSTANCE_POSITION, id, psz_id, d_position ); + break; + } + + default: + i_ret = VLC_EGENERIC; + break; + } + + vlc_mutex_unlock( &vlm->lock ); + + return i_ret; +} + + +/***************************************************************************** + * Manage: + *****************************************************************************/ +static void* Manage( vlc_object_t* p_object ) +{ + vlm_t *vlm = (vlm_t*)p_object; + int i, j; + mtime_t i_lastcheck; + mtime_t i_time; + + i_lastcheck = vlm_Date(); + + while( !vlm->b_die ) + { + char **ppsz_scheduled_commands = NULL; + int i_scheduled_commands = 0; + + vlc_mutex_lock( &vlm->lock ); + + /* destroy the inputs that wants to die, and launch the next input */ + for( i = 0; i < vlm->i_media; i++ ) + { + vlm_media_sys_t *p_media = vlm->media[i]; + + for( j = 0; j < p_media->i_instance; ) + { + vlm_media_instance_sys_t *p_instance = p_media->instance[j]; + + if( p_instance->p_input && ( p_instance->p_input->b_eof || p_instance->p_input->b_error ) ) + { + int i_new_input_index; + + /* */ + i_new_input_index = p_instance->i_index + 1; + if( !p_media->cfg.b_vod && p_media->cfg.broadcast.b_loop && i_new_input_index >= p_media->cfg.i_input ) + i_new_input_index = 0; + + /* FIXME implement multiple input with VOD */ + if( p_media->cfg.b_vod || i_new_input_index >= p_media->cfg.i_input ) + vlm_ControlInternal( vlm, VLM_STOP_MEDIA_INSTANCE, p_media->cfg.id, p_instance->psz_name ); + else + vlm_ControlInternal( vlm, VLM_START_MEDIA_BROADCAST_INSTANCE, p_media->cfg.id, p_instance->psz_name, i_new_input_index ); + + j = 0; + } + else + { + j++; + } + } + } + + /* scheduling */ + i_time = vlm_Date(); + + for( i = 0; i < vlm->i_schedule; i++ ) + { + mtime_t i_real_date = vlm->schedule[i]->i_date; + + if( vlm->schedule[i]->b_enabled == true ) + { + if( vlm->schedule[i]->i_date == 0 ) // now ! + { + vlm->schedule[i]->i_date = (i_time / 1000000) * 1000000 ; + i_real_date = i_time; + } + else if( vlm->schedule[i]->i_period != 0 ) + { + int j = 0; + while( vlm->schedule[i]->i_date + j * + vlm->schedule[i]->i_period <= i_lastcheck && + ( vlm->schedule[i]->i_repeat > j || + vlm->schedule[i]->i_repeat == -1 ) ) + { + j++; + } + + i_real_date = vlm->schedule[i]->i_date + j * + vlm->schedule[i]->i_period; + } + + if( i_real_date <= i_time && i_real_date > i_lastcheck ) + { + for( j = 0; j < vlm->schedule[i]->i_command; j++ ) + { + TAB_APPEND( i_scheduled_commands, + ppsz_scheduled_commands, + strdup(vlm->schedule[i]->command[j] ) ); + } + } + } + } + while( i_scheduled_commands ) + { + vlm_message_t *message = NULL; + char *psz_command = ppsz_scheduled_commands[0]; + ExecuteCommand( vlm, psz_command,&message ); + + /* for now, drop the message */ + vlm_MessageDelete( message ); + TAB_REMOVE( i_scheduled_commands, + ppsz_scheduled_commands, + psz_command ); + free( psz_command ); + } + + i_lastcheck = i_time; + + vlc_mutex_unlock( &vlm->lock ); + + msleep( 100000 ); + } + + return NULL; +} + +/* New API + */ +/* +typedef struct +{ + struct + { + int i_connection_count; + int i_connection_active; + } vod; + struct + { + int i_count; + bool b_playing; + int i_playing_index; + } broadcast; + +} vlm_media_status_t; +*/ + +/* */ +static vlm_media_sys_t *vlm_ControlMediaGetById( vlm_t *p_vlm, int64_t id ) +{ + int i; + + for( i = 0; i < p_vlm->i_media; i++ ) + { + if( p_vlm->media[i]->cfg.id == id ) + return p_vlm->media[i]; + } + return NULL; +} +static vlm_media_sys_t *vlm_ControlMediaGetByName( vlm_t *p_vlm, const char *psz_name ) +{ + int i; + + for( i = 0; i < p_vlm->i_media; i++ ) + { + if( !strcmp( p_vlm->media[i]->cfg.psz_name, psz_name ) ) + return p_vlm->media[i]; + } + return NULL; +} +static int vlm_MediaDescriptionCheck( vlm_t *p_vlm, vlm_media_t *p_cfg ) +{ + int i; + + if( !p_cfg || !p_cfg->psz_name || + !strcmp( p_cfg->psz_name, "all" ) || !strcmp( p_cfg->psz_name, "media" ) || !strcmp( p_cfg->psz_name, "schedule" ) ) + return VLC_EGENERIC; + + for( i = 0; i < p_vlm->i_media; i++ ) + { + if( p_vlm->media[i]->cfg.id == p_cfg->id ) + continue; + if( !strcmp( p_vlm->media[i]->cfg.psz_name, p_cfg->psz_name ) ) + return VLC_EGENERIC; + } + return VLC_SUCCESS; +} + + +/* Called after a media description is changed/added */ +static int vlm_OnMediaUpdate( vlm_t *p_vlm, vlm_media_sys_t *p_media ) +{ + vlm_media_t *p_cfg = &p_media->cfg; + /* Check if we need to create/delete a vod media */ + if( p_cfg->b_vod ) + { + if( !p_cfg->b_enabled && p_media->vod.p_media ) + { + p_vlm->p_vod->pf_media_del( p_vlm->p_vod, p_media->vod.p_media ); + p_media->vod.p_media = NULL; + } + else if( p_cfg->b_enabled && !p_media->vod.p_media && p_cfg->i_input ) + { + /* Pre-parse the input */ + input_thread_t *p_input; + char *psz_output; + char *psz_header; + char *psz_dup; + int i; + + vlc_gc_decref( p_media->vod.p_item ); + p_media->vod.p_item = input_item_New( p_vlm, p_cfg->ppsz_input[0], + p_cfg->psz_name ); + + if( p_cfg->psz_output ) + { + if( asprintf( &psz_output, "%s:description", p_cfg->psz_output ) == -1 ) + psz_output = NULL; + } + else + psz_output = strdup( "#description" ); + + if( psz_output && asprintf( &psz_dup, "sout=%s", psz_output ) != -1 ) + { + input_item_AddOption( p_media->vod.p_item, psz_dup ); + free( psz_dup ); + } + free( psz_output ); + + for( i = 0; i < p_cfg->i_option; i++ ) + input_item_AddOption( p_media->vod.p_item, + p_cfg->ppsz_option[i] ); + + if( asprintf( &psz_header, _("Media: %s"), p_cfg->psz_name ) == -1 ) + psz_header = NULL; + + if( (p_input = input_CreateThreadExtended( p_vlm, p_media->vod.p_item, psz_header, NULL ) ) ) + { + while( !p_input->b_eof && !p_input->b_error ) + msleep( 100000 ); + + input_StopThread( p_input ); + vlc_object_release( p_input ); + } + free( psz_header ); + + if( p_cfg->vod.psz_mux ) + { + input_item_t item; + es_format_t es, *p_es = &es; + char fourcc[5]; + + sprintf( fourcc, "%4.4s", p_cfg->vod.psz_mux ); + fourcc[0] = tolower(fourcc[0]); fourcc[1] = tolower(fourcc[1]); + fourcc[2] = tolower(fourcc[2]); fourcc[3] = tolower(fourcc[3]); + + /* XXX: Don't do it that way, but properly use a new input item ref. */ + item = *p_media->vod.p_item; + item.i_es = 1; + item.es = &p_es; + es_format_Init( &es, VIDEO_ES, *((int *)fourcc) ); + + p_media->vod.p_media = + p_vlm->p_vod->pf_media_new( p_vlm->p_vod, p_cfg->psz_name, &item ); + } + else + { + p_media->vod.p_media = + p_vlm->p_vod->pf_media_new( p_vlm->p_vod, p_cfg->psz_name, p_media->vod.p_item ); + } + } + } + else + { + /* TODO start media if needed */ + } + + /* TODO add support of var vlm_media_broadcast/vlm_media_vod */ + + return VLC_SUCCESS; +} +static int vlm_ControlMediaChange( vlm_t *p_vlm, vlm_media_t *p_cfg ) +{ + vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, p_cfg->id ); + + /* */ + if( !p_media || vlm_MediaDescriptionCheck( p_vlm, p_cfg ) ) + return VLC_EGENERIC; + if( ( p_media->cfg.b_vod && !p_cfg->b_vod ) || ( !p_media->cfg.b_vod && p_cfg->b_vod ) ) + return VLC_EGENERIC; + + if( 0 ) + { + /* TODO check what are the changes being done (stop instance if needed) */ + } + + vlm_media_Clean( &p_media->cfg ); + vlm_media_Copy( &p_media->cfg, p_cfg ); + + return vlm_OnMediaUpdate( p_vlm, p_media ); +} + +static int vlm_ControlMediaAdd( vlm_t *p_vlm, vlm_media_t *p_cfg, int64_t *p_id ) +{ + vlm_media_sys_t *p_media; + + if( vlm_MediaDescriptionCheck( p_vlm, p_cfg ) || vlm_ControlMediaGetByName( p_vlm, p_cfg->psz_name ) ) + { + msg_Err( p_vlm, "invalid media description" ); + return VLC_EGENERIC; + } + /* Check if we need to load the VOD server */ + if( p_cfg->b_vod && !p_vlm->i_vod ) + { + p_vlm->p_vod = vlc_custom_create( VLC_OBJECT(p_vlm), sizeof( vod_t ), + VLC_OBJECT_GENERIC, "vod server" ); + vlc_object_attach( p_vlm->p_vod, p_vlm ); + p_vlm->p_vod->p_module = module_Need( p_vlm->p_vod, "vod server", 0, 0 ); + if( !p_vlm->p_vod->p_module ) + { + msg_Err( p_vlm, "cannot find vod server" ); + vlc_object_detach( p_vlm->p_vod ); + vlc_object_release( p_vlm->p_vod ); + p_vlm->p_vod = 0; + return VLC_EGENERIC; + } + + p_vlm->p_vod->p_data = p_vlm; + p_vlm->p_vod->pf_media_control = vlm_MediaVodControl; + } + + p_media = malloc( sizeof( vlm_media_sys_t ) ); + if( !p_media ) + return VLC_ENOMEM; + memset( p_media, 0, sizeof(vlm_media_sys_t) ); + + if( p_cfg->b_vod ) + p_vlm->i_vod++; + + vlm_media_Copy( &p_media->cfg, p_cfg ); + p_media->cfg.id = p_vlm->i_id++; + /* FIXME do we do something here if enabled is true ? */ + + p_media->vod.p_item = input_item_New( p_vlm, NULL, NULL ); + + p_media->vod.p_media = NULL; + TAB_INIT( p_media->i_instance, p_media->instance ); + + /* */ + TAB_APPEND( p_vlm->i_media, p_vlm->media, p_media ); + + if( p_id ) + *p_id = p_media->cfg.id; + + return vlm_OnMediaUpdate( p_vlm, p_media ); +} + +static int vlm_ControlMediaDel( vlm_t *p_vlm, int64_t id ) +{ + vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id ); + + if( !p_media ) + return VLC_EGENERIC; + + while( p_media->i_instance > 0 ) + vlm_ControlInternal( p_vlm, VLM_STOP_MEDIA_INSTANCE, id, p_media->instance[0]->psz_name ); + + if( p_media->cfg.b_vod ) + { + p_media->cfg.b_enabled = false; + vlm_OnMediaUpdate( p_vlm, p_media ); + p_vlm->i_vod--; + } + + vlm_media_Clean( &p_media->cfg ); + + vlc_gc_decref( p_media->vod.p_item ); + + TAB_REMOVE( p_vlm->i_media, p_vlm->media, p_media ); + + free( p_media ); + + /* Check if we need to unload the VOD server */ + if( p_vlm->p_vod && p_vlm->i_vod <= 0 ) + { + module_Unneed( p_vlm->p_vod, p_vlm->p_vod->p_module ); + vlc_object_detach( p_vlm->p_vod ); + vlc_object_release( p_vlm->p_vod ); + p_vlm->p_vod = NULL; + } + return VLC_SUCCESS; +} + +static int vlm_ControlMediaGets( vlm_t *p_vlm, vlm_media_t ***ppp_dsc, int *pi_dsc ) +{ + vlm_media_t **pp_dsc; + int i_dsc; + int i; + + TAB_INIT( i_dsc, pp_dsc ); + for( i = 0; i < p_vlm->i_media; i++ ) + { + vlm_media_t *p_dsc = vlm_media_Duplicate( &p_vlm->media[i]->cfg ); + TAB_APPEND( i_dsc, pp_dsc, p_dsc ); + } + + *ppp_dsc = pp_dsc; + *pi_dsc = i_dsc; + + return VLC_SUCCESS; +} +static int vlm_ControlMediaClear( vlm_t *p_vlm ) +{ + while( p_vlm->i_media > 0 ) + vlm_ControlMediaDel( p_vlm, p_vlm->media[0]->cfg.id ); + + return VLC_SUCCESS; +} +static int vlm_ControlMediaGet( vlm_t *p_vlm, int64_t id, vlm_media_t **pp_dsc ) +{ + vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id ); + if( !p_media ) + return VLC_EGENERIC; + + *pp_dsc = vlm_media_Duplicate( &p_media->cfg ); + return VLC_SUCCESS; +} +static int vlm_ControlMediaGetId( vlm_t *p_vlm, const char *psz_name, int64_t *p_id ) +{ + vlm_media_sys_t *p_media = vlm_ControlMediaGetByName( p_vlm, psz_name ); + if( !p_media ) + return VLC_EGENERIC; + + *p_id = p_media->cfg.id; + return VLC_SUCCESS; +} + +static vlm_media_instance_sys_t *vlm_ControlMediaInstanceGetByName( vlm_media_sys_t *p_media, const char *psz_id ) +{ + int i; + + for( i = 0; i < p_media->i_instance; i++ ) + { + const char *psz = p_media->instance[i]->psz_name; + if( ( psz == NULL && psz_id == NULL ) || + ( psz && psz_id && !strcmp( psz, psz_id ) ) ) + return p_media->instance[i]; + } + return NULL; +} +static vlm_media_instance_sys_t *vlm_MediaInstanceNew( vlm_t *p_vlm, const char *psz_name ) +{ + vlm_media_instance_sys_t *p_instance = malloc( sizeof(vlm_media_instance_sys_t) ); + if( !p_instance ) + return NULL; + + memset( p_instance, 0, sizeof(vlm_media_instance_sys_t) ); + + p_instance->psz_name = NULL; + if( psz_name ) + p_instance->psz_name = strdup( psz_name ); + + p_instance->p_item = input_item_New( p_vlm, NULL, NULL ); + + p_instance->i_index = 0; + p_instance->b_sout_keep = false; + p_instance->p_input = NULL; + p_instance->p_sout = NULL; + + return p_instance; +} +static void vlm_MediaInstanceDelete( vlm_media_instance_sys_t *p_instance ) +{ + input_thread_t *p_input = p_instance->p_input; + if( p_input ) + { + input_StopThread( p_input ); + p_instance->p_sout = input_DetachSout( p_input ); + vlc_thread_join( p_input ); + vlc_object_release( p_input ); + } + if( p_instance->p_sout ) + sout_DeleteInstance( p_instance->p_sout ); + + vlc_gc_decref( p_instance->p_item ); + free( p_instance->psz_name ); + free( p_instance ); +} + + +static int vlm_ControlMediaInstanceStart( vlm_t *p_vlm, int64_t id, const char *psz_id, int i_input_index, const char *psz_vod_output ) +{ + vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id ); + vlm_media_instance_sys_t *p_instance; + char *psz_log; + + if( !p_media || !p_media->cfg.b_enabled || p_media->cfg.i_input <= 0 ) + return VLC_EGENERIC; + + /* TODO support multiple input for VOD with sout-keep ? */ + + if( ( p_media->cfg.b_vod && !psz_vod_output ) || ( !p_media->cfg.b_vod && psz_vod_output ) ) + return VLC_EGENERIC; + + if( i_input_index < 0 || i_input_index >= p_media->cfg.i_input ) + return VLC_EGENERIC; + + p_instance = vlm_ControlMediaInstanceGetByName( p_media, psz_id ); + if( !p_instance ) + { + vlm_media_t *p_cfg = &p_media->cfg; + int i; + + p_instance = vlm_MediaInstanceNew( p_vlm, psz_id ); + if( !p_instance ) + return VLC_ENOMEM; + + if( p_cfg->psz_output != NULL || psz_vod_output != NULL ) + { + char *psz_buffer; + if( asprintf( &psz_buffer, "sout=%s%s%s", + p_cfg->psz_output ? p_cfg->psz_output : "", + (p_cfg->psz_output && psz_vod_output) ? ":" : psz_vod_output ? "#" : "", + psz_vod_output ? psz_vod_output : "" ) != -1 ) + { + input_item_AddOption( p_instance->p_item, psz_buffer ); + free( psz_buffer ); + } + } + + for( i = 0; i < p_cfg->i_option; i++ ) + { + if( !strcmp( p_cfg->ppsz_option[i], "sout-keep" ) ) + p_instance->b_sout_keep = true; + else if( !strcmp( p_cfg->ppsz_option[i], "nosout-keep" ) || !strcmp( p_cfg->ppsz_option[i], "no-sout-keep" ) ) + p_instance->b_sout_keep = false; + else + input_item_AddOption( p_instance->p_item, p_cfg->ppsz_option[i] ); + } + TAB_APPEND( p_media->i_instance, p_media->instance, p_instance ); + } + + /* Stop old instance */ + input_thread_t *p_input = p_instance->p_input; + if( p_input ) + { + if( p_instance->i_index == i_input_index && + !p_input->b_eof && !p_input->b_error ) + { + if( var_GetInteger( p_input, "state" ) == PAUSE_S ) + var_SetInteger( p_input, "state", PLAYING_S ); + return VLC_SUCCESS; + } + + input_StopThread( p_input ); + p_instance->p_sout = input_DetachSout( p_input ); + vlc_thread_join( p_input ); + vlc_object_release( p_input ); + if( !p_instance->b_sout_keep && p_instance->p_sout ) + { + sout_DeleteInstance( p_instance->p_sout ); + p_instance->p_sout = NULL; + } + } + + /* Start new one */ + p_instance->i_index = i_input_index; + input_item_SetURI( p_instance->p_item, p_media->cfg.ppsz_input[p_instance->i_index] ) ; + + if( asprintf( &psz_log, _("Media: %s"), p_media->cfg.psz_name ) != -1 ) + { + p_instance->p_input = input_CreateThreadExtended( p_vlm, p_instance->p_item, psz_log, p_instance->p_sout ); + if( !p_instance->p_input ) + { + TAB_REMOVE( p_media->i_instance, p_media->instance, p_instance ); + vlm_MediaInstanceDelete( p_instance ); + } + free( psz_log ); + } + + return VLC_SUCCESS; +} + +static int vlm_ControlMediaInstanceStop( vlm_t *p_vlm, int64_t id, const char *psz_id ) +{ + vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id ); + vlm_media_instance_sys_t *p_instance; + + if( !p_media ) + return VLC_EGENERIC; + + p_instance = vlm_ControlMediaInstanceGetByName( p_media, psz_id ); + if( !p_instance ) + return VLC_EGENERIC; + + TAB_REMOVE( p_media->i_instance, p_media->instance, p_instance ); + + vlm_MediaInstanceDelete( p_instance ); + + return VLC_SUCCESS; +} +static int vlm_ControlMediaInstancePause( vlm_t *p_vlm, int64_t id, const char *psz_id ) +{ + vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id ); + vlm_media_instance_sys_t *p_instance; + int i_state; + + if( !p_media ) + return VLC_EGENERIC; + + p_instance = vlm_ControlMediaInstanceGetByName( p_media, psz_id ); + if( !p_instance || !p_instance->p_input ) + return VLC_EGENERIC; + + /* Toggle pause state */ + i_state = var_GetInteger( p_instance->p_input, "state" ); + if( i_state == PAUSE_S ) + var_SetInteger( p_instance->p_input, "state", PLAYING_S ); + else if( i_state == PLAYING_S ) + var_SetInteger( p_instance->p_input, "state", PAUSE_S ); + return VLC_SUCCESS; +} +static int vlm_ControlMediaInstanceGetTimePosition( vlm_t *p_vlm, int64_t id, const char *psz_id, int64_t *pi_time, double *pd_position ) +{ + vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id ); + vlm_media_instance_sys_t *p_instance; + + if( !p_media ) + return VLC_EGENERIC; + + p_instance = vlm_ControlMediaInstanceGetByName( p_media, psz_id ); + if( !p_instance || !p_instance->p_input ) + return VLC_EGENERIC; + + if( pi_time ) + *pi_time = var_GetTime( p_instance->p_input, "time" ); + if( pd_position ) + *pd_position = var_GetFloat( p_instance->p_input, "position" ); + return VLC_SUCCESS; +} +static int vlm_ControlMediaInstanceSetTimePosition( vlm_t *p_vlm, int64_t id, const char *psz_id, int64_t i_time, double d_position ) +{ + vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id ); + vlm_media_instance_sys_t *p_instance; + + if( !p_media ) + return VLC_EGENERIC; + + p_instance = vlm_ControlMediaInstanceGetByName( p_media, psz_id ); + if( !p_instance || !p_instance->p_input ) + return VLC_EGENERIC; + + if( i_time >= 0 ) + return var_SetTime( p_instance->p_input, "time", i_time ); + else if( d_position >= 0 && d_position <= 100 ) + return var_SetFloat( p_instance->p_input, "position", d_position ); + return VLC_EGENERIC; +} + +static int vlm_ControlMediaInstanceGets( vlm_t *p_vlm, int64_t id, vlm_media_instance_t ***ppp_idsc, int *pi_instance ) +{ + vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id ); + vlm_media_instance_t **pp_idsc; + int i_idsc; + int i; + + if( !p_media ) + return VLC_EGENERIC; + + TAB_INIT( i_idsc, pp_idsc ); + for( i = 0; i < p_media->i_instance; i++ ) + { + vlm_media_instance_sys_t *p_instance = p_media->instance[i]; + vlm_media_instance_t *p_idsc = vlm_media_instance_New(); + + if( p_instance->psz_name ) + p_idsc->psz_name = strdup( p_instance->psz_name ); + if( p_instance->p_input ) + { + p_idsc->i_time = var_GetTime( p_instance->p_input, "time" ); + p_idsc->i_length = var_GetTime( p_instance->p_input, "length" ); + p_idsc->d_position = var_GetFloat( p_instance->p_input, "position" ); + if( var_GetInteger( p_instance->p_input, "state" ) == PAUSE_S ) + p_idsc->b_paused = true; + p_idsc->i_rate = var_GetInteger( p_instance->p_input, "rate" ); + } + + TAB_APPEND( i_idsc, pp_idsc, p_idsc ); + } + *ppp_idsc = pp_idsc; + *pi_instance = i_idsc; + return VLC_SUCCESS; +} + +static int vlm_ControlMediaInstanceClear( vlm_t *p_vlm, int64_t id ) +{ + vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id ); + + if( !p_media ) + return VLC_EGENERIC; + + while( p_media->i_instance > 0 ) + vlm_ControlMediaInstanceStop( p_vlm, id, p_media->instance[0]->psz_name ); + + return VLC_SUCCESS; +} + +static int vlm_ControlScheduleClear( vlm_t *p_vlm ) +{ + while( p_vlm->i_schedule > 0 ) + vlm_ScheduleDelete( p_vlm, p_vlm->schedule[0] ); + + return VLC_SUCCESS; +} + +static int vlm_vaControlInternal( vlm_t *p_vlm, int i_query, va_list args ) +{ + vlm_media_t *p_dsc; + vlm_media_t **pp_dsc; + vlm_media_t ***ppp_dsc; + vlm_media_instance_t ***ppp_idsc; + const char *psz_id; + const char *psz_vod; + int64_t *p_id; + int64_t id; + int i_int; + int *pi_int; + + int64_t *pi_i64; + int64_t i_i64; + double *pd_double; + double d_double; + + switch( i_query ) + { + /* Media control */ + case VLM_GET_MEDIAS: + ppp_dsc = (vlm_media_t ***)va_arg( args, vlm_media_t *** ); + pi_int = (int *)va_arg( args, int * ); + return vlm_ControlMediaGets( p_vlm, ppp_dsc, pi_int ); + + case VLM_CLEAR_MEDIAS: + return vlm_ControlMediaClear( p_vlm ); + + case VLM_CHANGE_MEDIA: + p_dsc = (vlm_media_t*)va_arg( args, vlm_media_t * ); + return vlm_ControlMediaChange( p_vlm, p_dsc ); + + case VLM_ADD_MEDIA: + p_dsc = (vlm_media_t*)va_arg( args, vlm_media_t * ); + p_id = (int64_t*)va_arg( args, int64_t * ); + return vlm_ControlMediaAdd( p_vlm, p_dsc, p_id ); + + case VLM_DEL_MEDIA: + id = (int64_t)va_arg( args, int64_t ); + return vlm_ControlMediaDel( p_vlm, id ); + + case VLM_GET_MEDIA: + id = (int64_t)va_arg( args, int64_t ); + pp_dsc = (vlm_media_t **)va_arg( args, vlm_media_t ** ); + return vlm_ControlMediaGet( p_vlm, id, pp_dsc ); + + case VLM_GET_MEDIA_ID: + psz_id = (const char*)va_arg( args, const char * ); + p_id = (int64_t*)va_arg( args, int64_t * ); + return vlm_ControlMediaGetId( p_vlm, psz_id, p_id ); + + + /* Media instance control */ + case VLM_GET_MEDIA_INSTANCES: + id = (int64_t)va_arg( args, int64_t ); + ppp_idsc = (vlm_media_instance_t ***)va_arg( args, vlm_media_instance_t *** ); + pi_int = (int *)va_arg( args, int *); + return vlm_ControlMediaInstanceGets( p_vlm, id, ppp_idsc, pi_int ); + + case VLM_CLEAR_MEDIA_INSTANCES: + id = (int64_t)va_arg( args, int64_t ); + return vlm_ControlMediaInstanceClear( p_vlm, id ); + + + case VLM_START_MEDIA_BROADCAST_INSTANCE: + id = (int64_t)va_arg( args, int64_t ); + psz_id = (const char*)va_arg( args, const char* ); + i_int = (int)va_arg( args, int ); + return vlm_ControlMediaInstanceStart( p_vlm, id, psz_id, i_int, NULL ); + + case VLM_START_MEDIA_VOD_INSTANCE: + id = (int64_t)va_arg( args, int64_t ); + psz_id = (const char*)va_arg( args, const char* ); + i_int = (int)va_arg( args, int ); + psz_vod = (const char*)va_arg( args, const char* ); + if( !psz_vod ) + return VLC_EGENERIC; + return vlm_ControlMediaInstanceStart( p_vlm, id, psz_id, i_int, psz_vod ); + + case VLM_STOP_MEDIA_INSTANCE: + id = (int64_t)va_arg( args, int64_t ); + psz_id = (const char*)va_arg( args, const char* ); + return vlm_ControlMediaInstanceStop( p_vlm, id, psz_id ); + + case VLM_PAUSE_MEDIA_INSTANCE: + id = (int64_t)va_arg( args, int64_t ); + psz_id = (const char*)va_arg( args, const char* ); + return vlm_ControlMediaInstancePause( p_vlm, id, psz_id ); + + case VLM_GET_MEDIA_INSTANCE_TIME: + id = (int64_t)va_arg( args, int64_t ); + psz_id = (const char*)va_arg( args, const char* ); + pi_i64 = (int64_t*)va_arg( args, int64_t * ); + return vlm_ControlMediaInstanceGetTimePosition( p_vlm, id, psz_id, pi_i64, NULL ); + case VLM_GET_MEDIA_INSTANCE_POSITION: + id = (int64_t)va_arg( args, int64_t ); + psz_id = (const char*)va_arg( args, const char* ); + pd_double = (double*)va_arg( args, double* ); + return vlm_ControlMediaInstanceGetTimePosition( p_vlm, id, psz_id, NULL, pd_double ); + + case VLM_SET_MEDIA_INSTANCE_TIME: + id = (int64_t)va_arg( args, int64_t ); + psz_id = (const char*)va_arg( args, const char* ); + i_i64 = (int64_t)va_arg( args, int64_t); + return vlm_ControlMediaInstanceSetTimePosition( p_vlm, id, psz_id, i_i64, -1 ); + case VLM_SET_MEDIA_INSTANCE_POSITION: + id = (int64_t)va_arg( args, int64_t ); + psz_id = (const char*)va_arg( args, const char* ); + d_double = (double)va_arg( args, double ); + return vlm_ControlMediaInstanceSetTimePosition( p_vlm, id, psz_id, -1, d_double ); + + case VLM_CLEAR_SCHEDULES: + return vlm_ControlScheduleClear( p_vlm ); + + default: + msg_Err( p_vlm, "unknown VLM query" ); + return VLC_EGENERIC; + } +} + +int vlm_ControlInternal( vlm_t *p_vlm, int i_query, ... ) +{ + va_list args; + int i_result; + + va_start( args, i_query ); + i_result = vlm_vaControlInternal( p_vlm, i_query, args ); + va_end( args ); + + return i_result; +} + +int vlm_Control( vlm_t *p_vlm, int i_query, ... ) +{ + va_list args; + int i_result; + + va_start( args, i_query ); + + vlc_mutex_lock( &p_vlm->lock ); + i_result = vlm_vaControlInternal( p_vlm, i_query, args ); + vlc_mutex_unlock( &p_vlm->lock ); + + va_end( args ); + + return i_result; +} + +void vlm_MessageDelete( vlm_message_t *p_message ) +{ + free( p_message->psz_name ); + free( p_message->psz_value ); + while( p_message->i_child-- ) + vlm_MessageDelete( p_message->child[p_message->i_child] ); + free( p_message->child ); + free( p_message ); +} + +void vlm_ScheduleDelete( vlm_t *vlm, vlm_schedule_sys_t *sched ) +{ + if( sched == NULL ) return; + + TAB_REMOVE( vlm->i_schedule, vlm->schedule, sched ); + + if( vlm->i_schedule == 0 ) free( vlm->schedule ); + free( sched->psz_name ); + while( sched->i_command ) + { + char *psz_cmd = sched->command[0]; + TAB_REMOVE( sched->i_command, sched->command, psz_cmd ); + free( psz_cmd ); + } + free( sched ); +} + +int ExecuteCommand( vlm_t *p_vlm, const char *psz_command, + vlm_message_t **pp_message ) +{ + size_t i_command = 0; + char buf[strlen (psz_command) + 1], *psz_buf = buf; + char *ppsz_command[3+sizeof (buf) / 2]; + vlm_message_t *p_message = NULL; + + /* First, parse the line and cut it */ + while( *psz_command != '\0' ) + { + const char *psz_temp; + + if(isspace (*psz_command)) + { + psz_command++; + continue; + } + + /* support for comments */ + if( i_command == 0 && *psz_command == '#') + { + p_message = vlm_MessageNew( "", vlm_NULL ); + goto success; + } + + psz_temp = FindCommandEnd( psz_command ); + + if( psz_temp == NULL ) + { + p_message = vlm_MessageNew( "Incomplete command", psz_command ); + goto error; + } + + assert (i_command < (sizeof (ppsz_command) / sizeof (ppsz_command[0]))); + + ppsz_command[i_command] = psz_buf; + memcpy (psz_buf, psz_command, psz_temp - psz_command); + psz_buf[psz_temp - psz_command] = '\0'; + + Unescape (psz_buf, psz_buf); + + i_command++; + psz_buf += psz_temp - psz_command + 1; + psz_command = psz_temp; + + assert (buf + sizeof (buf) >= psz_buf); + } + + /* + * And then Interpret it + */ + +#define IF_EXECUTE( name, check, cmd ) if( !strcmp(ppsz_command[0], name ) ) { if( (check) ) goto syntax_error; if( (cmd) ) goto error; goto success; } + if( i_command == 0 ) + { + p_message = vlm_MessageNew( "", vlm_NULL ); + goto success; + } + else IF_EXECUTE( "del", (i_command != 2), ExecuteDel(p_vlm, ppsz_command[1], &p_message) ) + else IF_EXECUTE( "show", (i_command > 2), ExecuteShow(p_vlm, i_command > 1 ? ppsz_command[1] : NULL, &p_message) ) + else IF_EXECUTE( "help", (i_command != 1), ExecuteHelp( &p_message ) ) + else IF_EXECUTE( "control", (i_command < 3), ExecuteControl(p_vlm, ppsz_command[1], i_command - 2, &ppsz_command[2], &p_message) ) + else IF_EXECUTE( "save", (i_command != 2), ExecuteSave(p_vlm, ppsz_command[1], &p_message) ) + else IF_EXECUTE( "export", (i_command != 1), ExecuteExport(p_vlm, &p_message) ) + else IF_EXECUTE( "load", (i_command != 2), ExecuteLoad(p_vlm, ppsz_command[1], &p_message) ) + else IF_EXECUTE( "new", (i_command < 3), ExecuteNew(p_vlm, ppsz_command[1], ppsz_command[2], i_command-3, &ppsz_command[3], &p_message) ) + else IF_EXECUTE( "setup", (i_command < 2), ExecuteSetup(p_vlm, ppsz_command[1], i_command-2, &ppsz_command[2], &p_message) ) + else + { + p_message = vlm_MessageNew( ppsz_command[0], "Unknown command" ); + goto error; + } +#undef IF_EXECUTE + +success: + *pp_message = p_message; + return VLC_SUCCESS; + +syntax_error: + return ExecuteSyntaxError( ppsz_command[0], pp_message ); + +error: + *pp_message = p_message; + return VLC_EGENERIC; +} + +/***************************************************************************** + * ExecuteCommand: The main state machine + ***************************************************************************** + * Execute a command which ends with '\0' (string) + *****************************************************************************/ +int ExecuteSyntaxError( const char *psz_cmd, vlm_message_t **pp_status ) +{ + *pp_status = vlm_MessageNew( psz_cmd, "Wrong command syntax" ); + return VLC_EGENERIC; +} + +static bool ExecuteIsMedia( vlm_t *p_vlm, const char *psz_name ) +{ + int64_t id; + + if( !psz_name || vlm_ControlInternal( p_vlm, VLM_GET_MEDIA_ID, psz_name, &id ) ) + return false; + return true; +} +static bool ExecuteIsSchedule( vlm_t *p_vlm, const char *psz_name ) +{ + if( !psz_name || !vlm_ScheduleSearch( p_vlm, psz_name ) ) + return false; + return true; +} + +int ExecuteDel( vlm_t *p_vlm, const char *psz_name, vlm_message_t **pp_status ) +{ + vlm_media_sys_t *p_media; + vlm_schedule_sys_t *p_schedule; + + p_media = vlm_MediaSearch( p_vlm, psz_name ); + p_schedule = vlm_ScheduleSearch( p_vlm, psz_name ); + + if( p_schedule != NULL ) + { + vlm_ScheduleDelete( p_vlm, p_schedule ); + } + else if( p_media != NULL ) + { + vlm_ControlInternal( p_vlm, VLM_DEL_MEDIA, p_media->cfg.id ); + } + else if( !strcmp(psz_name, "media") ) + { + vlm_ControlInternal( p_vlm, VLM_CLEAR_MEDIAS ); + } + else if( !strcmp(psz_name, "schedule") ) + { + vlm_ControlInternal( p_vlm, VLM_CLEAR_SCHEDULES ); + } + else if( !strcmp(psz_name, "all") ) + { + vlm_ControlInternal( p_vlm, VLM_CLEAR_MEDIAS ); + vlm_ControlInternal( p_vlm, VLM_CLEAR_SCHEDULES ); + } + else + { + *pp_status = vlm_MessageNew( "del", "%s: media unknown", psz_name ); + return VLC_EGENERIC; + } + + *pp_status = vlm_MessageNew( "del", vlm_NULL ); + return VLC_SUCCESS; +} + +int ExecuteShow( vlm_t *p_vlm, const char *psz_name, vlm_message_t **pp_status ) +{ + vlm_media_sys_t *p_media; + vlm_schedule_sys_t *p_schedule; + + if( !psz_name ) + { + *pp_status = vlm_Show( p_vlm, NULL, NULL, NULL ); + return VLC_SUCCESS; + } + + p_media = vlm_MediaSearch( p_vlm, psz_name ); + p_schedule = vlm_ScheduleSearch( p_vlm, psz_name ); + + if( p_schedule != NULL ) + *pp_status = vlm_Show( p_vlm, NULL, p_schedule, NULL ); + else if( p_media != NULL ) + *pp_status = vlm_Show( p_vlm, p_media, NULL, NULL ); + else + *pp_status = vlm_Show( p_vlm, NULL, NULL, psz_name ); + + return VLC_SUCCESS; +} + +int ExecuteHelp( vlm_message_t **pp_status ) +{ + vlm_message_t *message_child; + +#define MessageAdd( a ) \ +vlm_MessageAdd( *pp_status, vlm_MessageNew( a, vlm_NULL ) ); +#define MessageAddChild( a ) \ +vlm_MessageAdd( message_child, vlm_MessageNew( a, vlm_NULL ) ); + + *pp_status = vlm_MessageNew( "help", vlm_NULL ); + + message_child = MessageAdd( "Commands Syntax:" ); + MessageAddChild( "new (name) vod|broadcast|schedule [properties]" ); + MessageAddChild( "setup (name) (properties)" ); + MessageAddChild( "show [(name)|media|schedule]" ); + MessageAddChild( "del (name)|all|media|schedule" ); + MessageAddChild( "control (name) [instance_name] (command)" ); + MessageAddChild( "save (config_file)" ); + MessageAddChild( "export" ); + MessageAddChild( "load (config_file)" ); + + message_child = MessageAdd( "Media Proprieties Syntax:" ); + MessageAddChild( "input (input_name)" ); + MessageAddChild( "inputdel (input_name)|all" ); + MessageAddChild( "inputdeln input_number" ); + MessageAddChild( "output (output_name)" ); + MessageAddChild( "option (option_name)[=value]" ); + MessageAddChild( "enabled|disabled" ); + MessageAddChild( "loop|unloop (broadcast only)" ); + MessageAddChild( "mux (mux_name)" ); + + message_child = MessageAdd( "Schedule Proprieties Syntax:" ); + MessageAddChild( "enabled|disabled" ); + MessageAddChild( "append (command_until_rest_of_the_line)" ); + MessageAddChild( "date (year)/(month)/(day)-(hour):(minutes):" + "(seconds)|now" ); + MessageAddChild( "period (years_aka_12_months)/(months_aka_30_days)/" + "(days)-(hours):(minutes):(seconds)" ); + MessageAddChild( "repeat (number_of_repetitions)" ); + + message_child = MessageAdd( "Control Commands Syntax:" ); + MessageAddChild( "play [input_number]" ); + MessageAddChild( "pause" ); + MessageAddChild( "stop" ); + MessageAddChild( "seek [+-](percentage) | [+-](seconds)s | [+-](miliseconds)ms" ); + + return VLC_SUCCESS; +} + +int ExecuteControl( vlm_t *p_vlm, const char *psz_name, const int i_arg, char ** ppsz_arg, vlm_message_t **pp_status ) +{ + vlm_media_sys_t *p_media; + const char *psz_control = NULL; + const char *psz_instance = NULL; + const char *psz_argument = NULL; + int i_index; + int i_result; + + if( !ExecuteIsMedia( p_vlm, psz_name ) ) + { + *pp_status = vlm_MessageNew( "control", "%s: media unknown", psz_name ); + return VLC_EGENERIC; + } + + assert( i_arg > 0 ); + +#define IS(txt) ( !strcmp( ppsz_arg[i_index], (txt) ) ) + i_index = 0; + if( !IS("play") && !IS("stop") && !IS("pause") && !IS("seek") ) + { + i_index = 1; + psz_instance = ppsz_arg[0]; + + if( i_index >= i_arg || ( !IS("play") && !IS("stop") && !IS("pause") && !IS("seek") ) ) + return ExecuteSyntaxError( "control", pp_status ); + } +#undef IS + psz_control = ppsz_arg[i_index]; + + if( i_index+1 < i_arg ) + psz_argument = ppsz_arg[i_index+1]; + + p_media = vlm_MediaSearch( p_vlm, psz_name ); + assert( p_media ); + + if( !strcmp( psz_control, "play" ) ) + { + int i_input_index = 0; + int i; + + if( ( psz_argument && sscanf(psz_argument, "%d", &i) == 1 ) && i > 0 && i-1 < p_media->cfg.i_input ) + { + i_input_index = i-1; + } + else if( psz_argument ) + { + int j; + vlm_media_t *p_cfg = &p_media->cfg; + for ( j=0; j < p_cfg->i_input; j++) + { + if( !strcmp( p_cfg->ppsz_input[j], psz_argument ) ) + { + i_input_index = j; + break; + } + } + } + + if( p_media->cfg.b_vod ) + i_result = vlm_ControlInternal( p_vlm, VLM_START_MEDIA_VOD_INSTANCE, p_media->cfg.id, psz_instance, i_input_index, NULL ); // we should get here now + else + i_result = vlm_ControlInternal( p_vlm, VLM_START_MEDIA_BROADCAST_INSTANCE, p_media->cfg.id, psz_instance, i_input_index ); + } + else if( !strcmp( psz_control, "seek" ) ) + { + if( psz_argument ) + { + bool b_relative; + if( psz_argument[0] == '+' || psz_argument[0] == '-' ) + b_relative = true; + else + b_relative = false; + + if( strstr( psz_argument, "ms" ) || strstr( psz_argument, "s" ) ) + { + /* Time (ms or s) */ + int64_t i_new_time; + + if( strstr( psz_argument, "ms" ) ) + i_new_time = 1000 * (int64_t)atoi( psz_argument ); + else + i_new_time = 1000000 * (int64_t)atoi( psz_argument ); + + if( b_relative ) + { + int64_t i_time = 0; + vlm_ControlInternal( p_vlm, VLM_GET_MEDIA_INSTANCE_TIME, p_media->cfg.id, psz_instance, &i_time ); + i_new_time += i_time; + } + if( i_new_time < 0 ) + i_new_time = 0; + i_result = vlm_ControlInternal( p_vlm, VLM_SET_MEDIA_INSTANCE_TIME, p_media->cfg.id, psz_instance, i_new_time ); + } + else + { + /* Percent */ + double d_new_position = us_atof( psz_argument ) / 100.0; + + if( b_relative ) + { + double d_position = 0.0; + + vlm_ControlInternal( p_vlm, VLM_GET_MEDIA_INSTANCE_POSITION, p_media->cfg.id, psz_instance, &d_position ); + d_new_position += d_position; + } + if( d_new_position < 0.0 ) + d_new_position = 0.0; + else if( d_new_position > 1.0 ) + d_new_position = 1.0; + i_result = vlm_ControlInternal( p_vlm, VLM_SET_MEDIA_INSTANCE_POSITION, p_media->cfg.id, psz_instance, d_new_position ); + } + } + else + { + i_result = VLC_EGENERIC; + } + } + else if( !strcmp( psz_control, "rewind" ) ) + { + if( psz_argument ) + { + const double d_scale = us_atof( psz_argument ); + double d_position; + + vlm_ControlInternal( p_vlm, VLM_GET_MEDIA_INSTANCE_POSITION, p_media->cfg.id, psz_instance, &d_position ); + d_position -= (d_scale / 1000.0); + if( d_position < 0.0 ) + d_position = 0.0; + i_result = vlm_ControlInternal( p_vlm, VLM_SET_MEDIA_INSTANCE_POSITION, p_media->cfg.id, psz_instance, d_position ); + } + else + { + i_result = VLC_EGENERIC; + } + } + else if( !strcmp( psz_control, "forward" ) ) + { + if( psz_argument ) + { + const double d_scale = us_atof( psz_argument ); + double d_position; + + vlm_ControlInternal( p_vlm, VLM_GET_MEDIA_INSTANCE_POSITION, p_media->cfg.id, psz_instance, &d_position ); + d_position += (d_scale / 1000.0); + if( d_position > 1.0 ) + d_position = 1.0; + i_result = vlm_ControlInternal( p_vlm, VLM_SET_MEDIA_INSTANCE_POSITION, p_media->cfg.id, psz_instance, d_position ); + + } + else + { + i_result = VLC_EGENERIC; + } + } + else if( !strcmp( psz_control, "stop" ) ) + { + i_result = vlm_ControlInternal( p_vlm, VLM_STOP_MEDIA_INSTANCE, p_media->cfg.id, psz_instance ); + } + else if( !strcmp( psz_control, "pause" ) ) + { + i_result = vlm_ControlInternal( p_vlm, VLM_PAUSE_MEDIA_INSTANCE, p_media->cfg.id, psz_instance ); + } + else + { + i_result = VLC_EGENERIC; + } + + if( i_result ) + { + *pp_status = vlm_MessageNew( "control", "unknown error" ); + return VLC_SUCCESS; + } + *pp_status = vlm_MessageNew( "control", vlm_NULL ); + return VLC_SUCCESS; +} + +int ExecuteExport( vlm_t *p_vlm, vlm_message_t **pp_status ) +{ + char *psz_export = Save( p_vlm ); + + *pp_status = vlm_MessageNew( "export", psz_export ); + free( psz_export ); + return VLC_SUCCESS; +} + +int ExecuteSave( vlm_t *p_vlm, const char *psz_file, vlm_message_t **pp_status ) +{ + FILE *f = utf8_fopen( psz_file, "wt" ); + char *psz_save = NULL; + + if( !f ) + goto error; + + psz_save = Save( p_vlm ); + if( psz_save == NULL ) + goto error; + if( fputs( psz_save, f ) == EOF ) + goto error;; + if( fclose( f ) ) + { + f = NULL; + goto error; + } + + free( psz_save ); + + *pp_status = vlm_MessageNew( "save", vlm_NULL ); + return VLC_SUCCESS; + +error: + free( psz_save ); + if( f ) + fclose( f ); + *pp_status = vlm_MessageNew( "save", "Unable to save to file"); + return VLC_EGENERIC; +} + +int ExecuteLoad( vlm_t *p_vlm, const char *psz_url, vlm_message_t **pp_status ) +{ + stream_t *p_stream = stream_UrlNew( p_vlm, psz_url ); + int64_t i_size; + char *psz_buffer; + + if( !p_stream ) + { + *pp_status = vlm_MessageNew( "load", "Unable to load from file" ); + return VLC_EGENERIC; + } + + /* FIXME needed ? */ + if( stream_Seek( p_stream, 0 ) != 0 ) + { + stream_Delete( p_stream ); + + *pp_status = vlm_MessageNew( "load", "Read file error" ); + return VLC_EGENERIC; + } + + i_size = stream_Size( p_stream ); + + psz_buffer = malloc( i_size + 1 ); + if( !psz_buffer ) + { + stream_Delete( p_stream ); + + *pp_status = vlm_MessageNew( "load", "Read file error" ); + return VLC_EGENERIC; + } + + stream_Read( p_stream, psz_buffer, i_size ); + psz_buffer[i_size] = '\0'; + + stream_Delete( p_stream ); + + if( Load( p_vlm, psz_buffer ) ) + { + free( psz_buffer ); + + *pp_status = vlm_MessageNew( "load", "Error while loading file" ); + return VLC_EGENERIC; + } + + free( psz_buffer ); + + *pp_status = vlm_MessageNew( "load", vlm_NULL ); + return VLC_SUCCESS; +} + +static int ExecuteScheduleProperty( vlm_t *p_vlm, vlm_schedule_sys_t *p_schedule, bool b_new, + const int i_property, char *ppsz_property[], vlm_message_t **pp_status ) +{ + const char *psz_cmd = b_new ? "new" : "setup"; + int i; + + for( i = 0; i < i_property; i++ ) + { + if( !strcmp( ppsz_property[i], "enabled" ) || + !strcmp( ppsz_property[i], "disabled" ) ) + { + if ( vlm_ScheduleSetup( p_schedule, ppsz_property[i], NULL ) ) + goto error; + } + else if( !strcmp( ppsz_property[i], "append" ) ) + { + char *psz_line; + int j; + /* Beware: everything behind append is considered as + * command line */ + + if( ++i >= i_property ) + break; + + psz_line = strdup( ppsz_property[i] ); + for( j = i+1; j < i_property; j++ ) + { + psz_line = realloc( psz_line, strlen(psz_line) + strlen(ppsz_property[j]) + 1 + 1 ); + strcat( psz_line, " " ); + strcat( psz_line, ppsz_property[j] ); + } + + if( vlm_ScheduleSetup( p_schedule, "append", psz_line ) ) + goto error; + break; + } + else + { + if( i + 1 >= i_property ) + { + if( b_new ) + vlm_ScheduleDelete( p_vlm, p_schedule ); + return ExecuteSyntaxError( psz_cmd, pp_status ); + } + + if( vlm_ScheduleSetup( p_schedule, ppsz_property[i], ppsz_property[i+1] ) ) + goto error; + i++; + } + } + *pp_status = vlm_MessageNew( psz_cmd, vlm_NULL ); + return VLC_SUCCESS; + +error: + *pp_status = vlm_MessageNew( psz_cmd, "Error while setting the property '%s' to the schedule", + ppsz_property[i] ); + return VLC_EGENERIC; +} + +static int ExecuteMediaProperty( vlm_t *p_vlm, int64_t id, bool b_new, + const int i_property, char *ppsz_property[], vlm_message_t **pp_status ) +{ + const char *psz_cmd = b_new ? "new" : "setup"; + vlm_media_t *p_cfg = NULL; + int i_result; + int i; + +#undef ERROR +#undef MISSING +#define ERROR( txt ) do { *pp_status = vlm_MessageNew( psz_cmd, txt); goto error; } while(0) + if( vlm_ControlInternal( p_vlm, VLM_GET_MEDIA, id, &p_cfg ) ) + ERROR( "unknown media" ); + +#define MISSING(cmd) do { if( !psz_value ) ERROR( "missing argument for " cmd ); } while(0) + for( i = 0; i < i_property; i++ ) + { + const char *psz_option = ppsz_property[i]; + const char *psz_value = i+1 < i_property ? ppsz_property[i+1] : NULL; + + if( !strcmp( psz_option, "enabled" ) ) + { + p_cfg->b_enabled = true; + } + else if( !strcmp( psz_option, "disabled" ) ) + { + p_cfg->b_enabled = false; + } + else if( !strcmp( psz_option, "input" ) ) + { + MISSING( "input" ); + TAB_APPEND( p_cfg->i_input, p_cfg->ppsz_input, strdup(psz_value) ); + i++; + } + else if( !strcmp( psz_option, "inputdel" ) && psz_value && !strcmp( psz_value, "all" ) ) + { + while( p_cfg->i_input > 0 ) + TAB_REMOVE( p_cfg->i_input, p_cfg->ppsz_input, p_cfg->ppsz_input[0] ); + i++; + } + else if( !strcmp( psz_option, "inputdel" ) ) + { + int j; + + MISSING( "inputdel" ); + + for( j = 0; j < p_cfg->i_input; j++ ) + { + if( !strcmp( p_cfg->ppsz_input[j], psz_value ) ) + { + TAB_REMOVE( p_cfg->i_input, p_cfg->ppsz_input, p_cfg->ppsz_input[j] ); + break; + } + } + i++; + } + else if( !strcmp( psz_option, "inputdeln" ) ) + { + int i_index; + + MISSING( "inputdeln" ); + + i_index = atoi( psz_value ); + if( i_index > 0 && i_index <= p_cfg->i_input ) + TAB_REMOVE( p_cfg->i_input, p_cfg->ppsz_input, p_cfg->ppsz_input[i_index-1] ); + i++; + } + else if( !strcmp( psz_option, "output" ) ) + { + MISSING( "output" ); + + free( p_cfg->psz_output ); + p_cfg->psz_output = *psz_value ? strdup( psz_value ) : NULL; + i++; + } + else if( !strcmp( psz_option, "option" ) ) + { + MISSING( "option" ); + + TAB_APPEND( p_cfg->i_option, p_cfg->ppsz_option, strdup( psz_value ) ); + i++; + } + else if( !strcmp( psz_option, "loop" ) ) + { + if( p_cfg->b_vod ) + ERROR( "invalid loop option for vod" ); + p_cfg->broadcast.b_loop = true; + } + else if( !strcmp( psz_option, "unloop" ) ) + { + if( p_cfg->b_vod ) + ERROR( "invalid unloop option for vod" ); + p_cfg->broadcast.b_loop = false; + } + else if( !strcmp( psz_option, "mux" ) ) + { + MISSING( "mux" ); + if( !p_cfg->b_vod ) + ERROR( "invalid mux option for broadcast" ); + + free( p_cfg->vod.psz_mux ); + p_cfg->vod.psz_mux = *psz_value ? strdup( psz_value ) : NULL; + i++; + } + else + { + fprintf( stderr, "PROP: name=%s unknown\n", psz_option ); + ERROR( "Wrong command syntax" ); + } + } +#undef MISSING +#undef ERROR + + /* */ + i_result = vlm_ControlInternal( p_vlm, VLM_CHANGE_MEDIA, p_cfg ); + vlm_media_Delete( p_cfg ); + + *pp_status = vlm_MessageNew( psz_cmd, vlm_NULL ); + return i_result; + +error: + if( p_cfg ) + { + if( b_new ) + vlm_ControlInternal( p_vlm, VLM_DEL_MEDIA, p_cfg->id ); + vlm_media_Delete( p_cfg ); + } + return VLC_EGENERIC; +} + +int ExecuteNew( vlm_t *p_vlm, const char *psz_name, const char *psz_type, const int i_property, char *ppsz_property[], vlm_message_t **pp_status ) +{ + /* Check name */ + if( !strcmp( psz_name, "all" ) || !strcmp( psz_name, "media" ) || !strcmp( psz_name, "schedule" ) ) + { + *pp_status = vlm_MessageNew( "new", "\"all\", \"media\" and \"schedule\" are reserved names" ); + return VLC_EGENERIC; + } + if( ExecuteIsMedia( p_vlm, psz_name ) || ExecuteIsSchedule( p_vlm, psz_name ) ) + { + *pp_status = vlm_MessageNew( "new", "%s: Name already in use", psz_name ); + return VLC_EGENERIC; + } + /* */ + if( !strcmp( psz_type, "schedule" ) ) + { + vlm_schedule_sys_t *p_schedule = vlm_ScheduleNew( p_vlm, psz_name ); + if( !p_schedule ) + { + *pp_status = vlm_MessageNew( "new", "could not create schedule" ); + return VLC_EGENERIC; + } + return ExecuteScheduleProperty( p_vlm, p_schedule, true, i_property, ppsz_property, pp_status ); + } + else if( !strcmp( psz_type, "vod" ) || !strcmp( psz_type, "broadcast" ) ) + { + vlm_media_t cfg; + int64_t id; + + vlm_media_Init( &cfg ); + cfg.psz_name = strdup( psz_name ); + cfg.b_vod = !strcmp( psz_type, "vod" ); + + if( vlm_ControlInternal( p_vlm, VLM_ADD_MEDIA, &cfg, &id ) ) + { + vlm_media_Clean( &cfg ); + *pp_status = vlm_MessageNew( "new", "could not create media" ); + return VLC_EGENERIC; + } + vlm_media_Clean( &cfg ); + return ExecuteMediaProperty( p_vlm, id, true, i_property, ppsz_property, pp_status ); + } + else + { + *pp_status = vlm_MessageNew( "new", "%s: Choose between vod, broadcast or schedule", psz_type ); + return VLC_EGENERIC; + } +} + +int ExecuteSetup( vlm_t *p_vlm, const char *psz_name, const int i_property, char *ppsz_property[], vlm_message_t **pp_status ) +{ + if( ExecuteIsSchedule( p_vlm, psz_name ) ) + { + vlm_schedule_sys_t *p_schedule = vlm_ScheduleSearch( p_vlm, psz_name ); + return ExecuteScheduleProperty( p_vlm, p_schedule, false, i_property, ppsz_property, pp_status ); + } + else if( ExecuteIsMedia( p_vlm, psz_name ) ) + { + int64_t id; + if( vlm_ControlInternal( p_vlm, VLM_GET_MEDIA_ID, psz_name, &id ) ) + goto error; + return ExecuteMediaProperty( p_vlm, id, false, i_property, ppsz_property, pp_status ); + } + +error: + *pp_status = vlm_MessageNew( "setup", "%s unknown", psz_name ); + return VLC_EGENERIC; +} + +int Unescape( char *out, const char *in ) +{ + char c, quote = 0; + + while( (c = *in++) != '\0' ) + { + if( !quote ) + { + if (strchr(quotes,c)) // opening quote + { + quote = c; + continue; + } + else if( c == '\\' ) + { + switch (c = *in++) + { + case '"': + case '\'': + case '\\': + *out++ = c; + continue; + + case '\0': + *out = '\0'; + return 0; + } + if( isspace(c) ) + { + *out++ = c; + continue; + } + /* None of the special cases - copy the backslash */ + *out++ = '\\'; + } + } + else + { + if( c == quote ) // non-escaped matching quote + { + quote = 0; + continue; + } + if( (quote == '"') && (c == '\\') ) + { + switch( c = *in++ ) + { + case '"': + case '\\': + *out++ = c; + continue; + + case '\0': // should never happen + *out = '\0'; + return -1; + } + /* None of the special cases - copy the backslash */ + *out++ = '\\'; + } + } + *out++ = c; + } + + *out = '\0'; + return 0; +} + +/***************************************************************************** + * Media handling + *****************************************************************************/ +vlm_media_sys_t *vlm_MediaSearch( vlm_t *vlm, const char *psz_name ) +{ + int i; + + for( i = 0; i < vlm->i_media; i++ ) + { + if( strcmp( psz_name, vlm->media[i]->cfg.psz_name ) == 0 ) + return vlm->media[i]; + } + + return NULL; +} +/***************************************************************************** + * Message handling functions + *****************************************************************************/ +vlm_message_t *vlm_MessageNew( const char *psz_name, + const char *psz_format, ... ) +{ + vlm_message_t *p_message; + va_list args; + + if( !psz_name ) return NULL; + + p_message = malloc( sizeof(vlm_message_t) ); + if( !p_message) + { + return NULL; + } + + p_message->psz_value = 0; + + if( psz_format ) + { + va_start( args, psz_format ); + if( vasprintf( &p_message->psz_value, psz_format, args ) == -1 ) + { + va_end( args ); + free( p_message ); + return NULL; + } + va_end( args ); + } + + p_message->psz_name = strdup( psz_name ); + p_message->i_child = 0; + p_message->child = NULL; + + return p_message; +} + +/* Add a child */ +vlm_message_t *vlm_MessageAdd( vlm_message_t *p_message, + vlm_message_t *p_child ) +{ + if( p_message == NULL ) return NULL; + + if( p_child ) + { + TAB_APPEND( p_message->i_child, p_message->child, p_child ); + } + + return p_child; +} + +static vlm_schedule_sys_t *vlm_ScheduleSearch( vlm_t *vlm, const char *psz_name ) +{ + int i; + + for( i = 0; i < vlm->i_schedule; i++ ) + { + if( strcmp( psz_name, vlm->schedule[i]->psz_name ) == 0 ) + { + return vlm->schedule[i]; + } + } + + return NULL; +} + +static const char *FindCommandEnd( const char *psz_sent ) +{ + char c, quote = 0; + + while( (c = *psz_sent) != '\0' ) + { + if( !quote ) + { + if( strchr(quotes,c) ) // opening quote + quote = c; + else if( isspace(c) ) // non-escaped space + return psz_sent; + else if( c == '\\' ) + { + psz_sent++; // skip escaped character + if( *psz_sent == '\0' ) + return psz_sent; + } + } + else + { + if( c == quote ) // non-escaped matching quote + quote = 0; + else if( (quote == '"') && (c == '\\') ) + { + psz_sent++; // skip escaped character + if (*psz_sent == '\0') + return NULL; // error, closing quote missing + } + } + psz_sent++; + } + + // error (NULL) if we could not find a matching quote + return quote ? NULL : psz_sent; +} +static vlm_schedule_sys_t *vlm_ScheduleNew( vlm_t *vlm, const char *psz_name ) +{ + if( !psz_name ) + return NULL; + + vlm_schedule_sys_t *p_sched = malloc( sizeof( vlm_schedule_sys_t ) ); + if( !p_sched ) + return NULL; + + p_sched->psz_name = strdup( psz_name ); + p_sched->b_enabled = false; + p_sched->i_command = 0; + p_sched->command = NULL; + p_sched->i_date = 0; + p_sched->i_period = 0; + p_sched->i_repeat = -1; + + TAB_APPEND( vlm->i_schedule, vlm->schedule, p_sched ); + + return p_sched; +} + +/* Ok, setup schedule command will be able to support only one (argument value) at a time */ +static int vlm_ScheduleSetup( vlm_schedule_sys_t *schedule, const char *psz_cmd, + const char *psz_value ) +{ + if( !strcmp( psz_cmd, "enabled" ) ) + { + schedule->b_enabled = true; + } + else if( !strcmp( psz_cmd, "disabled" ) ) + { + schedule->b_enabled = false; + } + else if( !strcmp( psz_cmd, "date" ) ) + { + struct tm time; + const char *p; + time_t date; + + time.tm_sec = 0; /* seconds */ + time.tm_min = 0; /* minutes */ + time.tm_hour = 0; /* hours */ + time.tm_mday = 0; /* day of the month */ + time.tm_mon = 0; /* month */ + time.tm_year = 0; /* year */ + time.tm_wday = 0; /* day of the week */ + time.tm_yday = 0; /* day in the year */ + time.tm_isdst = -1; /* daylight saving time */ + + /* date should be year/month/day-hour:minutes:seconds */ + p = strchr( psz_value, '-' ); + + if( !strcmp( psz_value, "now" ) ) + { + schedule->i_date = 0; + } + else if(p == NULL) + { + return 1; + } + else + { + unsigned i,j,k; + + switch( sscanf( p + 1, "%u:%u:%u", &i, &j, &k ) ) + { + case 1: + time.tm_sec = i; + break; + case 2: + time.tm_min = i; + time.tm_sec = j; + break; + case 3: + time.tm_hour = i; + time.tm_min = j; + time.tm_sec = k; + break; + default: + return 1; + } + + switch( sscanf( psz_value, "%d/%d/%d", &i, &j, &k ) ) + { + case 1: + time.tm_mday = i; + break; + case 2: + time.tm_mon = i - 1; + time.tm_mday = j; + break; + case 3: + time.tm_year = i - 1900; + time.tm_mon = j - 1; + time.tm_mday = k; + break; + default: + return 1; + } + + date = mktime( &time ); + schedule->i_date = ((mtime_t) date) * 1000000; + } + } + else if( !strcmp( psz_cmd, "period" ) ) + { + struct tm time; + const char *p; + const char *psz_time = NULL, *psz_date = NULL; + time_t date; + unsigned i,j,k; + + /* First, if date or period are modified, repeat should be equal to -1 */ + schedule->i_repeat = -1; + + time.tm_sec = 0; /* seconds */ + time.tm_min = 0; /* minutes */ + time.tm_hour = 0; /* hours */ + time.tm_mday = 0; /* day of the month */ + time.tm_mon = 0; /* month */ + time.tm_year = 0; /* year */ + time.tm_wday = 0; /* day of the week */ + time.tm_yday = 0; /* day in the year */ + time.tm_isdst = -1; /* daylight saving time */ + + /* date should be year/month/day-hour:minutes:seconds */ + p = strchr( psz_value, '-' ); + if( p ) + { + psz_date = psz_value; + psz_time = p + 1; + } + else + { + psz_time = psz_value; + } + + switch( sscanf( psz_time, "%u:%u:%u", &i, &j, &k ) ) + { + case 1: + time.tm_sec = i; + break; + case 2: + time.tm_min = i; + time.tm_sec = j; + break; + case 3: + time.tm_hour = i; + time.tm_min = j; + time.tm_sec = k; + break; + default: + return 1; + } + if( psz_date ) + { + switch( sscanf( psz_date, "%u/%u/%u", &i, &j, &k ) ) + { + case 1: + time.tm_mday = i; + break; + case 2: + time.tm_mon = i; + time.tm_mday = j; + break; + case 3: + time.tm_year = i; + time.tm_mon = j; + time.tm_mday = k; + break; + default: + return 1; + } + } + + /* ok, that's stupid... who is going to schedule streams every 42 years ? */ + date = (((( time.tm_year * 12 + time.tm_mon ) * 30 + time.tm_mday ) * 24 + time.tm_hour ) * 60 + time.tm_min ) * 60 + time.tm_sec ; + schedule->i_period = ((mtime_t) date) * 1000000; + } + else if( !strcmp( psz_cmd, "repeat" ) ) + { + int i; + + if( sscanf( psz_value, "%d", &i ) == 1 ) + { + schedule->i_repeat = i; + } + else + { + return 1; + } + } + else if( !strcmp( psz_cmd, "append" ) ) + { + char *command = strdup( psz_value ); + + TAB_APPEND( schedule->i_command, schedule->command, command ); + } + else + { + return 1; + } + return 0; +} +static vlm_message_t *vlm_ShowMedia( vlm_media_sys_t *p_media ) +{ + vlm_media_t *p_cfg = &p_media->cfg; + vlm_message_t *p_msg; + vlm_message_t *p_msg_sub; + int i; + + p_msg = vlm_MessageNew( p_cfg->psz_name, vlm_NULL ); + vlm_MessageAdd( p_msg, + vlm_MessageNew( "type", p_cfg->b_vod ? "vod" : "broadcast" ) ); + vlm_MessageAdd( p_msg, + vlm_MessageNew( "enabled", p_cfg->b_enabled ? "yes" : "no" ) ); + + if( p_cfg->b_vod ) + vlm_MessageAdd( p_msg, + vlm_MessageNew( "mux", p_cfg->vod.psz_mux ) ); + else + vlm_MessageAdd( p_msg, + vlm_MessageNew( "loop", p_cfg->broadcast.b_loop ? "yes" : "no" ) ); + + p_msg_sub = vlm_MessageAdd( p_msg, vlm_MessageNew( "inputs", vlm_NULL ) ); + for( i = 0; i < p_cfg->i_input; i++ ) + { + char *psz_tmp; + if( asprintf( &psz_tmp, "%d", i+1 ) != -1 ) + { + vlm_MessageAdd( p_msg_sub, + vlm_MessageNew( psz_tmp, p_cfg->ppsz_input[i] ) ); + free( psz_tmp ); + } + } + + vlm_MessageAdd( p_msg, + vlm_MessageNew( "output", p_cfg->psz_output ? p_cfg->psz_output : "" ) ); + + p_msg_sub = vlm_MessageAdd( p_msg, vlm_MessageNew( "options", vlm_NULL ) ); + for( i = 0; i < p_cfg->i_option; i++ ) + vlm_MessageAdd( p_msg_sub, vlm_MessageNew( p_cfg->ppsz_option[i], vlm_NULL ) ); + + p_msg_sub = vlm_MessageAdd( p_msg, vlm_MessageNew( "instances", vlm_NULL ) ); + for( i = 0; i < p_media->i_instance; i++ ) + { + vlm_media_instance_sys_t *p_instance = p_media->instance[i]; + vlc_value_t val; + vlm_message_t *p_msg_instance; + char *psz_tmp; + + val.i_int = END_S; + if( p_instance->p_input ) + var_Get( p_instance->p_input, "state", &val ); + + p_msg_instance = vlm_MessageAdd( p_msg_sub, vlm_MessageNew( "instance" , vlm_NULL ) ); + + vlm_MessageAdd( p_msg_instance, + vlm_MessageNew( "name" , p_instance->psz_name ? p_instance->psz_name : "default" ) ); + vlm_MessageAdd( p_msg_instance, + vlm_MessageNew( "state", + val.i_int == PLAYING_S ? "playing" : + val.i_int == PAUSE_S ? "paused" : + "stopped" ) ); + + /* FIXME should not do that this way */ + if( p_instance->p_input ) + { +#define APPEND_INPUT_INFO( a, format, type ) \ +if( asprintf( &psz_tmp, format, \ +var_Get ## type( p_instance->p_input, a ) ) != -1 ) \ +{ \ +vlm_MessageAdd( p_msg_instance, vlm_MessageNew( a, \ +psz_tmp ) ); \ +free( psz_tmp ); \ +} + APPEND_INPUT_INFO( "position", "%f", Float ); + APPEND_INPUT_INFO( "time", "%"PRIi64, Time ); + APPEND_INPUT_INFO( "length", "%"PRIi64, Time ); + APPEND_INPUT_INFO( "rate", "%d", Integer ); + APPEND_INPUT_INFO( "title", "%d", Integer ); + APPEND_INPUT_INFO( "chapter", "%d", Integer ); + APPEND_INPUT_INFO( "seekable", "%d", Bool ); + } +#undef APPEND_INPUT_INFO + if( asprintf( &psz_tmp, "%d", p_instance->i_index + 1 ) != -1 ) + { + vlm_MessageAdd( p_msg_instance, vlm_MessageNew( "playlistindex", + psz_tmp ) ); + free( psz_tmp ); + } + } + return p_msg; +} + +static vlm_message_t *vlm_Show( vlm_t *vlm, vlm_media_sys_t *media, + vlm_schedule_sys_t *schedule, + const char *psz_filter ) +{ + if( media != NULL ) + { + vlm_message_t *p_msg = vlm_MessageNew( "show", vlm_NULL ); + if( p_msg ) + vlm_MessageAdd( p_msg, vlm_ShowMedia( media ) ); + return p_msg; + } + + else if( schedule != NULL ) + { + int i; + vlm_message_t *msg; + vlm_message_t *msg_schedule; + vlm_message_t *msg_child; + char buffer[100]; + + msg = vlm_MessageNew( "show", vlm_NULL ); + msg_schedule = + vlm_MessageAdd( msg, vlm_MessageNew( schedule->psz_name, vlm_NULL ) ); + + vlm_MessageAdd( msg_schedule, vlm_MessageNew("type", "schedule") ); + + vlm_MessageAdd( msg_schedule, + vlm_MessageNew( "enabled", schedule->b_enabled ? + "yes" : "no" ) ); + + if( schedule->i_date != 0 ) + { + struct tm date; + time_t i_time = (time_t)( schedule->i_date / 1000000 ); + char *psz_date; + + localtime_r( &i_time, &date); + if( asprintf( &psz_date, "%d/%d/%d-%d:%d:%d", + date.tm_year + 1900, date.tm_mon + 1, date.tm_mday, + date.tm_hour, date.tm_min, date.tm_sec ) != -1 ) + { + vlm_MessageAdd( msg_schedule, + vlm_MessageNew( "date", psz_date ) ); + free( psz_date ); + } + } + else + vlm_MessageAdd( msg_schedule, vlm_MessageNew("date", "now") ); + + if( schedule->i_period != 0 ) + { + time_t i_time = (time_t) ( schedule->i_period / 1000000 ); + struct tm date; + + date.tm_sec = (int)( i_time % 60 ); + i_time = i_time / 60; + date.tm_min = (int)( i_time % 60 ); + i_time = i_time / 60; + date.tm_hour = (int)( i_time % 24 ); + i_time = i_time / 24; + date.tm_mday = (int)( i_time % 30 ); + i_time = i_time / 30; + /* okay, okay, months are not always 30 days long */ + date.tm_mon = (int)( i_time % 12 ); + i_time = i_time / 12; + date.tm_year = (int)i_time; + + sprintf( buffer, "%d/%d/%d-%d:%d:%d", date.tm_year, date.tm_mon, + date.tm_mday, date.tm_hour, date.tm_min, date.tm_sec); + + vlm_MessageAdd( msg_schedule, vlm_MessageNew("period", buffer) ); + } + else + vlm_MessageAdd( msg_schedule, vlm_MessageNew("period", "0") ); + + sprintf( buffer, "%d", schedule->i_repeat ); + vlm_MessageAdd( msg_schedule, vlm_MessageNew( "repeat", buffer ) ); + + msg_child = + vlm_MessageAdd( msg_schedule, vlm_MessageNew("commands", vlm_NULL ) ); + + for( i = 0; i < schedule->i_command; i++ ) + { + vlm_MessageAdd( msg_child, + vlm_MessageNew( schedule->command[i], vlm_NULL ) ); + } + + return msg; + + } + + else if( psz_filter && !strcmp( psz_filter, "media" ) ) + { + vlm_message_t *p_msg; + vlm_message_t *p_msg_child; + int i_vod = 0, i_broadcast = 0; + int i; + char *psz_count; + + for( i = 0; i < vlm->i_media; i++ ) + { + if( vlm->media[i]->cfg.b_vod ) + i_vod++; + else + i_broadcast++; + } + + if( asprintf( &psz_count, "( %d broadcast - %d vod )", i_broadcast, + i_vod) == -1 ) + return NULL; + p_msg = vlm_MessageNew( "show", vlm_NULL ); + p_msg_child = vlm_MessageAdd( p_msg, vlm_MessageNew( "media", psz_count ) ); + free( psz_count ); + + for( i = 0; i < vlm->i_media; i++ ) + vlm_MessageAdd( p_msg_child, vlm_ShowMedia( vlm->media[i] ) ); + + return p_msg; + } + + else if( psz_filter && !strcmp( psz_filter, "schedule" ) ) + { + int i; + vlm_message_t *msg; + vlm_message_t *msg_child; + + msg = vlm_MessageNew( "show", vlm_NULL ); + msg_child = vlm_MessageAdd( msg, vlm_MessageNew( "schedule", vlm_NULL ) ); + + for( i = 0; i < vlm->i_schedule; i++ ) + { + vlm_schedule_sys_t *s = vlm->schedule[i]; + vlm_message_t *msg_schedule; + mtime_t i_time, i_next_date; + + msg_schedule = vlm_MessageAdd( msg_child, + vlm_MessageNew( s->psz_name, vlm_NULL ) ); + vlm_MessageAdd( msg_schedule, + vlm_MessageNew( "enabled", s->b_enabled ? + "yes" : "no" ) ); + + /* calculate next date */ + i_time = vlm_Date(); + i_next_date = s->i_date; + + if( s->i_period != 0 ) + { + int j = 0; + while( s->i_date + j * s->i_period <= i_time && + s->i_repeat > j ) + { + j++; + } + + i_next_date = s->i_date + j * s->i_period; + } + + if( i_next_date > i_time ) + { + time_t i_date = (time_t) (i_next_date / 1000000) ; + +#if !defined( UNDER_CE ) +#ifdef HAVE_CTIME_R + char psz_date[500]; + ctime_r( &i_date, psz_date ); +#else + char *psz_date = ctime( &i_date ); +#endif + + vlm_MessageAdd( msg_schedule, + vlm_MessageNew( "next launch", psz_date ) ); +#endif + } + } + + return msg; + } + + else if( ( psz_filter == NULL ) && ( media == NULL ) && ( schedule == NULL ) ) + { + vlm_message_t *show1 = vlm_Show( vlm, NULL, NULL, "media" ); + vlm_message_t *show2 = vlm_Show( vlm, NULL, NULL, "schedule" ); + + vlm_MessageAdd( show1, show2->child[0] ); + + /* We must destroy the parent node "show" of show2 + * and not the children */ + free( show2->psz_name ); + free( show2 ); + + return show1; + } + + else + { + return vlm_MessageNew( "show", vlm_NULL ); + } +} + +/***************************************************************************** + * Config handling functions + *****************************************************************************/ +static int Load( vlm_t *vlm, char *file ) +{ + char *pf = file; + int i_line = 1; + + while( *pf != '\0' ) + { + vlm_message_t *message = NULL; + int i_end = 0; + + while( pf[i_end] != '\n' && pf[i_end] != '\0' && pf[i_end] != '\r' ) + { + i_end++; + } + + if( pf[i_end] == '\r' || pf[i_end] == '\n' ) + { + pf[i_end] = '\0'; + i_end++; + if( pf[i_end] == '\n' ) i_end++; + } + + if( *pf && ExecuteCommand( vlm, pf, &message ) ) + { + if( message ) + { + if( message->psz_value ) + msg_Err( vlm, "Load error on line %d: %s: %s", + i_line, message->psz_name, message->psz_value ); + vlm_MessageDelete( message ); + } + return 1; + } + if( message ) vlm_MessageDelete( message ); + + pf += i_end; + i_line++; + } + + return 0; +} + +static char *Save( vlm_t *vlm ) +{ + char *save = NULL; + char psz_header[] = "\n" + "# VLC media player VLM command batch\n" + "# http://www.videolan.org/vlc/\n\n" ; + char *p; + int i,j; + int i_length = strlen( psz_header ); + + for( i = 0; i < vlm->i_media; i++ ) + { + vlm_media_sys_t *media = vlm->media[i]; + vlm_media_t *p_cfg = &media->cfg; + + if( p_cfg->b_vod ) + i_length += strlen( "new * vod " ) + strlen(p_cfg->psz_name); + else + i_length += strlen( "new * broadcast " ) + strlen(p_cfg->psz_name); + + if( p_cfg->b_enabled == true ) + i_length += strlen( "enabled" ); + else + i_length += strlen( "disabled" ); + + if( !p_cfg->b_vod && p_cfg->broadcast.b_loop == true ) + i_length += strlen( " loop\n" ); + else + i_length += strlen( "\n" ); + + for( j = 0; j < p_cfg->i_input; j++ ) + i_length += strlen( "setup * input \"\"\n" ) + strlen( p_cfg->psz_name ) + strlen( p_cfg->ppsz_input[j] ); + + if( p_cfg->psz_output != NULL ) + i_length += strlen( "setup * output \n" ) + strlen(p_cfg->psz_name) + strlen(p_cfg->psz_output); + + for( j = 0; j < p_cfg->i_option; j++ ) + i_length += strlen("setup * option \n") + strlen(p_cfg->psz_name) + strlen(p_cfg->ppsz_option[j]); + + if( p_cfg->b_vod && p_cfg->vod.psz_mux ) + i_length += strlen("setup * mux \n") + strlen(p_cfg->psz_name) + strlen(p_cfg->vod.psz_mux); + } + + for( i = 0; i < vlm->i_schedule; i++ ) + { + vlm_schedule_sys_t *schedule = vlm->schedule[i]; + + i_length += strlen( "new schedule " ) + strlen( schedule->psz_name ); + + if( schedule->b_enabled == true ) + { + i_length += strlen( "date //-:: enabled\n" ) + 14; + } + else + { + i_length += strlen( "date //-:: disabled\n" ) + 14; + } + + + if( schedule->i_period != 0 ) + { + i_length += strlen( "setup " ) + strlen( schedule->psz_name ) + + strlen( "period //-::\n" ) + 14; + } + + if( schedule->i_repeat >= 0 ) + { + char buffer[12]; + + sprintf( buffer, "%d", schedule->i_repeat ); + i_length += strlen( "setup repeat \n" ) + + strlen( schedule->psz_name ) + strlen( buffer ); + } + else + { + i_length++; + } + + for( j = 0; j < schedule->i_command; j++ ) + { + i_length += strlen( "setup append \n" ) + + strlen( schedule->psz_name ) + strlen( schedule->command[j] ); + } + + } + + /* Don't forget the '\0' */ + i_length++; + /* now we have the length of save */ + + p = save = malloc( i_length ); + if( !save ) return NULL; + *save = '\0'; + + p += sprintf( p, "%s", psz_header ); + + /* finally we can write in it */ + for( i = 0; i < vlm->i_media; i++ ) + { + vlm_media_sys_t *media = vlm->media[i]; + vlm_media_t *p_cfg = &media->cfg; + + if( p_cfg->b_vod ) + p += sprintf( p, "new %s vod ", p_cfg->psz_name ); + else + p += sprintf( p, "new %s broadcast ", p_cfg->psz_name ); + + if( p_cfg->b_enabled ) + p += sprintf( p, "enabled" ); + else + p += sprintf( p, "disabled" ); + + if( !p_cfg->b_vod && p_cfg->broadcast.b_loop ) + p += sprintf( p, " loop\n" ); + else + p += sprintf( p, "\n" ); + + for( j = 0; j < p_cfg->i_input; j++ ) + p += sprintf( p, "setup %s input \"%s\"\n", p_cfg->psz_name, p_cfg->ppsz_input[j] ); + + if( p_cfg->psz_output ) + p += sprintf( p, "setup %s output %s\n", p_cfg->psz_name, p_cfg->psz_output ); + + for( j = 0; j < p_cfg->i_option; j++ ) + p += sprintf( p, "setup %s option %s\n", p_cfg->psz_name, p_cfg->ppsz_option[j] ); + + if( p_cfg->b_vod && p_cfg->vod.psz_mux ) + p += sprintf( p, "setup %s mux %s\n", p_cfg->psz_name, p_cfg->vod.psz_mux ); + } + + /* and now, the schedule scripts */ + for( i = 0; i < vlm->i_schedule; i++ ) + { + vlm_schedule_sys_t *schedule = vlm->schedule[i]; + struct tm date; + time_t i_time = (time_t) ( schedule->i_date / 1000000 ); + + localtime_r( &i_time, &date); + p += sprintf( p, "new %s schedule ", schedule->psz_name); + + if( schedule->b_enabled == true ) + { + p += sprintf( p, "date %d/%d/%d-%d:%d:%d enabled\n", + date.tm_year + 1900, date.tm_mon + 1, date.tm_mday, + date.tm_hour, date.tm_min, date.tm_sec ); + } + else + { + p += sprintf( p, "date %d/%d/%d-%d:%d:%d disabled\n", + date.tm_year + 1900, date.tm_mon + 1, date.tm_mday, + date.tm_hour, date.tm_min, date.tm_sec); + } + + if( schedule->i_period != 0 ) + { + p += sprintf( p, "setup %s ", schedule->psz_name ); + + i_time = (time_t) ( schedule->i_period / 1000000 ); + + date.tm_sec = (int)( i_time % 60 ); + i_time = i_time / 60; + date.tm_min = (int)( i_time % 60 ); + i_time = i_time / 60; + date.tm_hour = (int)( i_time % 24 ); + i_time = i_time / 24; + date.tm_mday = (int)( i_time % 30 ); + i_time = i_time / 30; + /* okay, okay, months are not always 30 days long */ + date.tm_mon = (int)( i_time % 12 ); + i_time = i_time / 12; + date.tm_year = (int)i_time; + + p += sprintf( p, "period %d/%d/%d-%d:%d:%d\n", + date.tm_year, date.tm_mon, date.tm_mday, + date.tm_hour, date.tm_min, date.tm_sec); + } + + if( schedule->i_repeat >= 0 ) + { + p += sprintf( p, "setup %s repeat %d\n", + schedule->psz_name, schedule->i_repeat ); + } + else + { + p += sprintf( p, "\n" ); + } + + for( j = 0; j < schedule->i_command; j++ ) + { + p += sprintf( p, "setup %s append %s\n", + schedule->psz_name, schedule->command[j] ); + } + + } + + return save; +} diff --git a/VLC/input/vlm_internal.h b/VLC/input/vlm_internal.h new file mode 100644 index 0000000..fa42344 --- /dev/null +++ b/VLC/input/vlm_internal.h @@ -0,0 +1,113 @@ +/***************************************************************************** + * vlm_internal.h: Internal vlm structures + ***************************************************************************** + * Copyright (C) 1998-2006 the VideoLAN team + * $Id$ + * + * Authors: Laurent Aimar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#if defined(__PLUGIN__) || defined(__BUILTIN__) || !defined(__LIBVLC__) + +#endif + +#ifndef _VLM_INTERNAL_H +#define _VLM_INTERNAL_H 1 + +#include "vlc_vlm.h" + +/* Private */ +typedef struct +{ + /* instance name */ + char *psz_name; + + /* "playlist" index */ + int i_index; + + bool b_sout_keep; + + input_item_t *p_item; + input_thread_t *p_input; + sout_instance_t *p_sout; + +} vlm_media_instance_sys_t; + + +typedef struct +{ + vlm_media_t cfg; + + struct + { + input_item_t *p_item; + vod_media_t *p_media; + } vod; + + /* actual input instances */ + int i_instance; + vlm_media_instance_sys_t **instance; +} vlm_media_sys_t; + +typedef struct +{ + /* names "schedule" is reserved */ + char *psz_name; + bool b_enabled; + /* list of commands to execute on date */ + int i_command; + char **command; + + /* the date of 1st execution */ + mtime_t i_date; + + /* if != 0 repeat schedule every (period) */ + mtime_t i_period; + /* number of times you have to repeat + i_repeat < 0 : endless repeat */ + int i_repeat; +} vlm_schedule_sys_t; + + +struct vlm_t +{ + VLC_COMMON_MEMBERS + + vlc_mutex_t lock; + + /* */ + int64_t i_id; + + /* Vod server (used by media) */ + int i_vod; + vod_t *p_vod; + + /* Media list */ + int i_media; + vlm_media_sys_t **media; + + /* Schedule list */ + int i_schedule; + vlm_schedule_sys_t **schedule; +}; + +int64_t vlm_Date(void); +int vlm_ControlInternal( vlm_t *p_vlm, int i_query, ... ); +int ExecuteCommand( vlm_t *, const char *, vlm_message_t ** ); +void vlm_ScheduleDelete( vlm_t *vlm, vlm_schedule_sys_t *sched ); + +#endif diff --git a/VLC/interaction.c b/VLC/interaction.c new file mode 100644 index 0000000..b73bb20 --- /dev/null +++ b/VLC/interaction.c @@ -0,0 +1,664 @@ +/***************************************************************************** + * interaction.c: User interaction functions + ***************************************************************************** + * Copyright © 2005-2008 the VideoLAN team + * $Id$ + * + * Authors: Clément Stenac + * Felix Kühne + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/** + * \file + * This file contains functions related to user interaction management + */ + +/***************************************************************************** + * Preamble + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#include "vlc_interface.h" +#include "interface.h" +#include "libvlc.h" + +#include + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +static interaction_t * InteractionGet( vlc_object_t * ); +static void InteractionSearchInterface( interaction_t * ); +static void* InteractionLoop( vlc_object_t * ); +static void InteractionManage( interaction_t * ); + +static interaction_dialog_t *DialogGetById( interaction_t* , int ); +static void DialogDestroy( interaction_dialog_t * ); +static int DialogSend( vlc_object_t *, interaction_dialog_t * ); + +#define DIALOG_INIT( type ) \ + DECMALLOC_ERR( p_new, interaction_dialog_t ); \ + memset( p_new, 0, sizeof( interaction_dialog_t ) ); \ + p_new->b_cancelled = false; \ + p_new->i_status = NEW_DIALOG; \ + p_new->i_flags = 0; \ + p_new->i_type = INTERACT_DIALOG_##type; \ + p_new->psz_returned[0] = NULL; \ + p_new->psz_returned[1] = NULL + +#define FORMAT_DESC \ + va_start( args, psz_format ); \ + if( vasprintf( &p_new->psz_description, psz_format, args ) == -1 ) \ + return VLC_EGENERIC; \ + va_end( args ) + +/** + * Send an error message, both in a blocking and non-blocking way + * + * \param p_this Parent vlc_object + * \param b_blocking Is this dialog blocking or not? + * \param psz_title Title for the dialog + * \param psz_format The message to display + * \return VLC_SUCCESS or VLC_EGENERIC + */ +int __intf_UserFatal( vlc_object_t *p_this, bool b_blocking, + const char *psz_title, + const char *psz_format, ... ) +{ + va_list args; + DIALOG_INIT( ONEWAY ); + + p_new->psz_title = strdup( psz_title ); + FORMAT_DESC; + + if( b_blocking ) + p_new->i_flags = DIALOG_BLOCKING_ERROR; + else + p_new->i_flags = DIALOG_NONBLOCKING_ERROR; + + return DialogSend( p_this, p_new ); +} + +/** + * Helper function to send a warning, which is always shown non-blocking + * + * \param p_this Parent vlc_object + * \param psz_title Title for the dialog + * \param psz_format The message to display + * \return VLC_SUCCESS or VLC_EGENERIC + */ +int __intf_UserWarn( vlc_object_t *p_this, + const char *psz_title, + const char *psz_format, ... ) +{ + va_list args; + DIALOG_INIT( ONEWAY ); + + p_new->psz_title = strdup( psz_title ); + FORMAT_DESC; + + p_new->i_flags = DIALOG_WARNING; + + return DialogSend( p_this, p_new ); +} + +/** + * Helper function to ask a yes-no-cancel question + * + * \param p_this Parent vlc_object + * \param psz_title Title for the dialog + * \param psz_description A description + * \param psz_default caption for the default button + * \param psz_alternate caption for the alternate button + * \param psz_other caption for the optional 3rd button (== cancel) + * \return Clicked button code + */ +int __intf_UserYesNo( vlc_object_t *p_this, + const char *psz_title, + const char *psz_description, + const char *psz_default, + const char *psz_alternate, + const char *psz_other ) +{ + DIALOG_INIT( TWOWAY ); + + p_new->psz_title = strdup( psz_title ); + p_new->psz_description = strdup( psz_description ); + p_new->i_flags = DIALOG_YES_NO_CANCEL; + p_new->psz_default_button = strdup( psz_default ); + p_new->psz_alternate_button = strdup( psz_alternate ); + if( psz_other ) + p_new->psz_other_button = strdup( psz_other ); + + return DialogSend( p_this, p_new ); +} + +/** + * Helper function to create a dialogue showing a progress-bar with some info + * + * \param p_this Parent vlc_object + * \param psz_title Title for the dialog (NULL implies main intf ) + * \param psz_status Current status + * \param f_position Current position (0.0->100.0) + * \param i_timeToGo Time (in sec) to go until process is finished + * \return Dialog id, to give to UserProgressUpdate + */ +int __intf_Progress( vlc_object_t *p_this, const char *psz_title, + const char *psz_status, float f_pos, int i_time ) +{ + DIALOG_INIT( ONEWAY ); + p_new->psz_description = strdup( psz_status ); + p_new->val.f_float = f_pos; + p_new->i_timeToGo = i_time; + p_new->psz_alternate_button = strdup( _( "Cancel" ) ); + + if( psz_title ) + { + p_new->psz_title = strdup( psz_title ); + p_new->i_flags = DIALOG_USER_PROGRESS; + } + else + p_new->i_flags = DIALOG_INTF_PROGRESS; + + DialogSend( p_this, p_new ); + return p_new->i_id; +} + +/** + * Update a progress bar in a dialogue + * + * \param p_this Parent vlc_object + * \param i_id Identifier of the dialog + * \param psz_status New status + * \param f_position New position (0.0->100.0) + * \param i_timeToGo Time (in sec) to go until process is finished + * \return nothing + */ +void __intf_ProgressUpdate( vlc_object_t *p_this, int i_id, + const char *psz_status, float f_pos, int i_time ) +{ + interaction_t *p_interaction = InteractionGet( p_this ); + interaction_dialog_t *p_dialog; + + if( !p_interaction ) return; + + vlc_object_lock( p_interaction ); + p_dialog = DialogGetById( p_interaction, i_id ); + + if( !p_dialog ) + { + vlc_object_unlock( p_interaction ); + vlc_object_release( p_interaction ); + return; + } + + free( p_dialog->psz_description ); + p_dialog->psz_description = strdup( psz_status ); + + p_dialog->val.f_float = f_pos; + p_dialog->i_timeToGo = i_time; + + p_dialog->i_status = UPDATED_DIALOG; + + vlc_object_signal_unlocked( p_interaction ); + vlc_object_unlock( p_interaction ); + vlc_object_release( p_interaction ); +} + +/** + * Helper function to communicate dialogue cancellations between the + * interface module and the caller + * + * \param p_this Parent vlc_object + * \param i_id Identifier of the dialogue + * \return Either true or false + */ +bool __intf_UserProgressIsCancelled( vlc_object_t *p_this, int i_id ) +{ + interaction_t *p_interaction = InteractionGet( p_this ); + interaction_dialog_t *p_dialog; + bool b_cancel; + + if( !p_interaction ) return true; + + vlc_object_lock( p_interaction ); + p_dialog = DialogGetById( p_interaction, i_id ); + if( !p_dialog ) + { + vlc_object_unlock( p_interaction ) ; + vlc_object_release( p_interaction ); + return true; + } + + b_cancel = p_dialog->b_cancelled; + vlc_object_unlock( p_interaction ); + vlc_object_release( p_interaction ); + return b_cancel; +} + +/** + * Helper function to make a login/password dialogue + * + * \param p_this Parent vlc_object + * \param psz_title Title for the dialog + * \param psz_description A description + * \param ppsz_login Returned login + * \param ppsz_password Returned password + * \return Clicked button code + */ +int __intf_UserLoginPassword( vlc_object_t *p_this, + const char *psz_title, + const char *psz_description, + char **ppsz_login, + char **ppsz_password ) +{ + int i_ret; + DIALOG_INIT( TWOWAY ); + p_new->i_type = INTERACT_DIALOG_TWOWAY; + p_new->psz_title = strdup( psz_title ); + p_new->psz_description = strdup( psz_description ); + p_new->psz_default_button = strdup( _("OK" ) ); + p_new->psz_alternate_button = strdup( _("Cancel" ) ); + + p_new->i_flags = DIALOG_LOGIN_PW_OK_CANCEL; + + i_ret = DialogSend( p_this, p_new ); + + if( i_ret != DIALOG_CANCELLED && i_ret != VLC_EGENERIC ) + { + *ppsz_login = p_new->psz_returned[0]? + strdup( p_new->psz_returned[0] ) : NULL; + *ppsz_password = p_new->psz_returned[1]? + strdup( p_new->psz_returned[1] ) : NULL; + } + return i_ret; +} + +/** + * Helper function to make a dialogue asking the user for !password string + * + * \param p_this Parent vlc_object + * \param psz_title Title for the dialog + * \param psz_description A description + * \param ppsz_usersString Returned login + * \return Clicked button code + */ +int __intf_UserStringInput( vlc_object_t *p_this, + const char *psz_title, + const char *psz_description, + char **ppsz_usersString ) +{ + int i_ret; + DIALOG_INIT( TWOWAY ); + p_new->i_type = INTERACT_DIALOG_TWOWAY; + p_new->psz_title = strdup( psz_title ); + p_new->psz_description = strdup( psz_description ); + + p_new->i_flags = DIALOG_PSZ_INPUT_OK_CANCEL; + + i_ret = DialogSend( p_this, p_new ); + + if( i_ret != DIALOG_CANCELLED ) + { + *ppsz_usersString = p_new->psz_returned[0]? + strdup( p_new->psz_returned[0] ) : NULL; + } + return i_ret; +} + +/** + * Hide an interaction dialog + * + * \param p_this the parent vlc object + * \param i_id the id of the item to hide + * \return nothing + */ +void __intf_UserHide( vlc_object_t *p_this, int i_id ) +{ + interaction_t *p_interaction = InteractionGet( p_this ); + interaction_dialog_t *p_dialog; + + if( !p_interaction ) return; + + vlc_object_lock( p_interaction ); + p_dialog = DialogGetById( p_interaction, i_id ); + + if( p_dialog ) + { + p_dialog->i_status = ANSWERED_DIALOG; + vlc_object_signal_unlocked( p_interaction ); + } + + vlc_object_unlock( p_interaction ); + vlc_object_release( p_interaction ); +} + +/** + * Create the initial interaction object + * (should only be used in libvlc_InternalInit, LibVLC private) + * + * \return a vlc_object_t that should be freed when done. + */ +interaction_t * interaction_Init( libvlc_int_t *p_libvlc ) +{ + interaction_t *p_interaction; + + /* Make sure we haven't yet created an interaction object */ + assert( libvlc_priv(p_libvlc)->p_interaction == NULL ); + + p_interaction = vlc_custom_create( VLC_OBJECT(p_libvlc), + sizeof( *p_interaction ), + VLC_OBJECT_GENERIC, "interaction" ); + if( !p_interaction ) + return NULL; + + vlc_object_attach( p_interaction, p_libvlc ); + p_interaction->i_dialogs = 0; + p_interaction->pp_dialogs = NULL; + p_interaction->p_intf = NULL; + p_interaction->i_last_id = 0; + + if( vlc_thread_create( p_interaction, "Interaction control", + InteractionLoop, VLC_THREAD_PRIORITY_LOW, + false ) ) + { + msg_Err( p_interaction, "Interaction control thread creation failed, " + "interaction will not be displayed" ); + vlc_object_detach( p_interaction ); + vlc_object_release( p_interaction ); + return NULL; + } + + return p_interaction; +} + +void interaction_Destroy( interaction_t *p_interaction ) +{ + if( !p_interaction ) + return; + + vlc_object_kill( p_interaction ); + vlc_thread_join( p_interaction ); + vlc_object_release( p_interaction ); +} + +/********************************************************************** + * The following functions are local + **********************************************************************/ + +/* Get the interaction object. Create it if needed */ +static interaction_t * InteractionGet( vlc_object_t *p_this ) +{ + interaction_t *obj = libvlc_priv(p_this->p_libvlc)->p_interaction; + if( obj ) + vlc_object_yield( obj ); + return obj; +} + + +/* Look for an interface suitable for interaction */ +static void InteractionSearchInterface( interaction_t *p_interaction ) +{ + vlc_list_t *p_list; + int i_index; + + p_interaction->p_intf = NULL; + + p_list = vlc_list_find( p_interaction, VLC_OBJECT_INTF, FIND_ANYWHERE ); + if( !p_list ) + { + msg_Err( p_interaction, "unable to create module list" ); + return; + } + + for( i_index = 0; i_index < p_list->i_count; i_index ++ ) + { + intf_thread_t *p_intf = (intf_thread_t *) + p_list->p_values[i_index].p_object; + if( p_intf->b_interaction ) + { + p_interaction->p_intf = p_intf; + break; + } + } + vlc_list_release ( p_list ); +} + +/* Find an interaction dialog by its id */ +static interaction_dialog_t *DialogGetById( interaction_t *p_interaction, + int i_id ) +{ + int i; + for( i = 0 ; i< p_interaction->i_dialogs; i++ ) + { + if( p_interaction->pp_dialogs[i]->i_id == i_id ) + return p_interaction->pp_dialogs[i]; + } + return NULL; +} + +/* Destroy a dialog */ +static void DialogDestroy( interaction_dialog_t *p_dialog ) +{ + free( p_dialog->psz_title ); + free( p_dialog->psz_description ); + free( p_dialog->psz_default_button ); + free( p_dialog->psz_alternate_button ); + free( p_dialog->psz_other_button ); + free( p_dialog ); +} + +/* Ask for the dialog to be sent to the user. Wait for answer + * if required */ +static int DialogSend( vlc_object_t *p_this, interaction_dialog_t *p_dialog ) +{ + interaction_t *p_interaction = InteractionGet( p_this ); + + if( !p_interaction ) + return VLC_EGENERIC; + + /* Get an id, if we don't already have one */ + vlc_object_lock( p_interaction ); + if( p_dialog->i_id == 0 ) + p_dialog->i_id = ++p_interaction->i_last_id; + vlc_object_unlock( p_interaction ); + + if( p_this->i_flags & OBJECT_FLAGS_NOINTERACT ) + { + vlc_object_release( p_interaction ); + return VLC_EGENERIC; + } + + if( config_GetInt( p_this, "interact" ) || + p_dialog->i_flags & DIALOG_BLOCKING_ERROR || + p_dialog->i_flags & DIALOG_NONBLOCKING_ERROR ) + { + bool b_found = false; + int i; + p_dialog->p_interaction = p_interaction; + p_dialog->p_parent = p_this; + + /* Check if we have already added this dialog */ + vlc_object_lock( p_interaction ); + for( i = 0 ; i< p_interaction->i_dialogs; i++ ) + { + if( p_interaction->pp_dialogs[i]->i_id == p_dialog->i_id ) + b_found = true; + } + /* Add it to the queue, the main loop will send the orders to the + * interface */ + if( ! b_found ) + { + INSERT_ELEM( p_interaction->pp_dialogs, + p_interaction->i_dialogs, + p_interaction->i_dialogs, + p_dialog ); + } + else + p_dialog->i_status = UPDATED_DIALOG; + + if( p_dialog->i_type == INTERACT_DIALOG_TWOWAY ) /* Wait for answer */ + { + vlc_object_signal_unlocked( p_interaction ); + while( p_dialog->i_status != ANSWERED_DIALOG && + p_dialog->i_status != HIDING_DIALOG && + p_dialog->i_status != HIDDEN_DIALOG && + !p_dialog->p_parent->b_die ) + { + vlc_object_unlock( p_interaction ); + msleep( 100000 ); + vlc_object_lock( p_interaction ); + } + if( p_dialog->p_parent->b_die ) + { + p_dialog->i_return = DIALOG_CANCELLED; + p_dialog->i_status = ANSWERED_DIALOG; + } + p_dialog->i_flags |= DIALOG_GOT_ANSWER; + vlc_object_signal_unlocked( p_interaction ); + vlc_object_unlock( p_interaction ); + vlc_object_release( p_interaction ); + return p_dialog->i_return; + } + else + { + /* Pretend we already retrieved the "answer" */ + p_dialog->i_flags |= DIALOG_GOT_ANSWER; + vlc_object_signal_unlocked( p_interaction ); + vlc_object_unlock( p_interaction ); + vlc_object_release( p_interaction ); + return VLC_SUCCESS; + } + } + else + { + vlc_object_release( p_interaction ); + return VLC_EGENERIC; + } +} + +static void* InteractionLoop( vlc_object_t *p_this ) +{ + int i; + interaction_t *p_interaction = (interaction_t*) p_this; + + vlc_object_lock( p_this ); + while( vlc_object_alive( p_this ) ) + { + InteractionManage( p_interaction ); + vlc_object_wait( p_this ); + } + vlc_object_unlock( p_this ); + + /* Remove all dialogs - Interfaces must be able to clean up their data */ + for( i = p_interaction->i_dialogs -1 ; i >= 0; i-- ) + { + interaction_dialog_t * p_dialog = p_interaction->pp_dialogs[i]; + DialogDestroy( p_dialog ); + REMOVE_ELEM( p_interaction->pp_dialogs, p_interaction->i_dialogs, i ); + } + return NULL; +} + +/** + * The main interaction processing loop + * + * \param p_interaction the interaction object + * \return nothing + */ + +static void InteractionManage( interaction_t *p_interaction ) +{ + vlc_value_t val; + int i_index; + + /* Nothing to do */ + if( p_interaction->i_dialogs == 0 ) return; + + InteractionSearchInterface( p_interaction ); + if( !p_interaction->p_intf ) + { + /* We mark all dialogs as answered with their "default" answer */ + for( i_index = 0 ; i_index < p_interaction->i_dialogs; i_index ++ ) + { + interaction_dialog_t *p_dialog = p_interaction->pp_dialogs[i_index]; + p_dialog->i_return = DIALOG_DEFAULT; /* Give default answer */ + + /* Pretend we have hidden and destroyed it */ + if( p_dialog->i_status == HIDDEN_DIALOG ) + p_dialog->i_status = DESTROYED_DIALOG; + else + p_dialog->i_status = HIDING_DIALOG; + } + } + else + vlc_object_yield( p_interaction->p_intf ); + + for( i_index = 0 ; i_index < p_interaction->i_dialogs; i_index ++ ) + { + interaction_dialog_t *p_dialog = p_interaction->pp_dialogs[i_index]; + switch( p_dialog->i_status ) + { + case ANSWERED_DIALOG: + /* Ask interface to hide it */ + p_dialog->i_action = INTERACT_HIDE; + val.p_address = p_dialog; + if( p_interaction->p_intf ) + var_Set( p_interaction->p_intf, "interaction", val ); + p_dialog->i_status = HIDING_DIALOG; + break; + case UPDATED_DIALOG: + p_dialog->i_action = INTERACT_UPDATE; + val.p_address = p_dialog; + if( p_interaction->p_intf ) + var_Set( p_interaction->p_intf, "interaction", val ); + p_dialog->i_status = SENT_DIALOG; + break; + case HIDDEN_DIALOG: + if( !(p_dialog->i_flags & DIALOG_GOT_ANSWER) ) break; + p_dialog->i_action = INTERACT_DESTROY; + val.p_address = p_dialog; + if( p_interaction->p_intf ) + var_Set( p_interaction->p_intf, "interaction", val ); + break; + case DESTROYED_DIALOG: + /* Interface has now destroyed it, remove it */ + REMOVE_ELEM( p_interaction->pp_dialogs, p_interaction->i_dialogs, + i_index); + i_index--; + DialogDestroy( p_dialog ); + break; + case NEW_DIALOG: + /* This is truly a new dialog, send it. */ + + p_dialog->i_action = INTERACT_NEW; + val.p_address = p_dialog; + if( p_interaction->p_intf ) + var_Set( p_interaction->p_intf, "interaction", val ); + p_dialog->i_status = SENT_DIALOG; + break; + } + } + + if( p_interaction->p_intf ) + vlc_object_release( p_interaction->p_intf ); +} diff --git a/VLC/interface.c b/VLC/interface.c new file mode 100644 index 0000000..fb3dac6 --- /dev/null +++ b/VLC/interface.c @@ -0,0 +1,328 @@ +/***************************************************************************** + * interface.c: interface access for other threads + * This library provides basic functions for threads to interact with user + * interface, such as command line. + ***************************************************************************** + * Copyright (C) 1998-2007 the VideoLAN team + * $Id$ + * + * Authors: Vincent Seguin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/** + * \file + * This file contains functions related to interface management + */ + + +/***************************************************************************** + * Preamble + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#include "vlc_aout.h" +#include "vlc_vout.h" + +#include "vlc_interface.h" +#include "modules.h" // Gruik! +#include "libvlc.h" + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +static void* RunInterface( vlc_object_t *p_this ); +#ifdef __APPLE__ +static void * MonitorLibVLCDeath( vlc_object_t *p_this ); +#endif +static int AddIntfCallback( vlc_object_t *, char const *, + vlc_value_t , vlc_value_t , void * ); + +/** + * \brief Destroy the interface after the main loop endeed. + * + * \param p_intf the interface thread + * \return nothing + */ +static void intf_Destroy( vlc_object_t *obj ) +{ + intf_thread_t *p_intf = (intf_thread_t *)obj; + + /* Unlock module if present (a switch may have failed) */ + if( p_intf->p_module ) + module_Unneed( p_intf, p_intf->p_module ); + + free( p_intf->psz_intf ); + vlc_mutex_destroy( &p_intf->change_lock ); +} + +/***************************************************************************** + * intf_Create: prepare interface before main loop + ***************************************************************************** + * This function opens output devices and creates specific interfaces. It sends + * its own error messages. + *****************************************************************************/ +/** + * Create the interface, and prepare it for main loop. + * + * \param p_this the calling vlc_object_t + * \param psz_module a preferred interface module + * \return a pointer to the created interface thread, NULL on error + */ +intf_thread_t* __intf_Create( vlc_object_t *p_this, const char *psz_module ) +{ + intf_thread_t * p_intf; + + /* Allocate structure */ + p_intf = vlc_object_create( p_this, VLC_OBJECT_INTF ); + if( !p_intf ) + return NULL; + p_intf->b_interaction = false; +#ifdef __APPLE__ + p_intf->b_should_run_on_first_thread = false; +#endif + + /* Choose the best module */ + p_intf->psz_intf = strdup( psz_module ); + p_intf->p_module = module_Need( p_intf, "interface", psz_module, false ); + + if( p_intf->p_module == NULL ) + { + msg_Err( p_intf, "no suitable interface module" ); + free( p_intf->psz_intf ); + vlc_object_release( p_intf ); + return NULL; + } + + /* Initialize structure */ + p_intf->b_menu = false; + p_intf->b_menu_change = false; + + /* Initialize mutexes */ + vlc_mutex_init( &p_intf->change_lock ); + + /* Attach interface to its parent object */ + vlc_object_attach( p_intf, p_this ); + vlc_object_set_destructor( p_intf, intf_Destroy ); + + return p_intf; +} + +/***************************************************************************** + * intf_RunThread: launch the interface thread + ***************************************************************************** + * This function either creates a new thread and runs the interface in it. + *****************************************************************************/ +/** + * Starts and runs the interface thread. + * + * \param p_intf the interface thread + * \return VLC_SUCCESS on success, an error number else + */ +int intf_RunThread( intf_thread_t *p_intf ) +{ +#ifdef __APPLE__ + /* Hack to get Mac OS X Cocoa runtime running + * (it needs access to the main thread) */ + if( p_intf->b_should_run_on_first_thread ) + { + if( vlc_thread_create( p_intf, "interface", MonitorLibVLCDeath, + VLC_THREAD_PRIORITY_LOW, false ) ) + { + msg_Err( p_intf, "cannot spawn libvlc death monitoring thread" ); + return VLC_EGENERIC; + } + RunInterface( VLC_OBJECT(p_intf) ); + + /* Make sure our MonitorLibVLCDeath thread exit */ + vlc_object_kill( p_intf ); + /* It is monitoring libvlc, not the p_intf */ + vlc_object_signal( p_intf->p_libvlc ); + vlc_thread_join( p_intf ); + + vlc_object_detach( p_intf ); + vlc_object_release( p_intf ); + return VLC_SUCCESS; + } +#endif + /* Run the interface in a separate thread */ + if( vlc_thread_create( p_intf, "interface", RunInterface, + VLC_THREAD_PRIORITY_LOW, false ) ) + { + msg_Err( p_intf, "cannot spawn interface thread" ); + return VLC_EGENERIC; + } + + return VLC_SUCCESS; +} + +/** + * Stops the interface thread + * + * This function asks the interface thread to stop + * \param p_intf the interface thread + * \return nothing + */ +void intf_StopThread( intf_thread_t *p_intf ) +{ + /* Tell the interface to die */ + vlc_object_kill( p_intf ); + vlc_thread_join( p_intf ); +} + +/* Following functions are local */ + +/***************************************************************************** + * RunInterface: setups necessary data and give control to the interface + *****************************************************************************/ +static void* RunInterface( vlc_object_t *p_this ) +{ + intf_thread_t *p_intf = (intf_thread_t *)p_this; + vlc_value_t val, text; + char *psz_intf; + + /* Variable used for interface spawning */ + var_Create( p_intf, "intf-add", VLC_VAR_STRING | + VLC_VAR_HASCHOICE | VLC_VAR_ISCOMMAND ); + text.psz_string = _("Add Interface"); + var_Change( p_intf, "intf-add", VLC_VAR_SETTEXT, &text, NULL ); + + val.psz_string = (char *)"rc"; + text.psz_string = (char *)_("Console"); + var_Change( p_intf, "intf-add", VLC_VAR_ADDCHOICE, &val, &text ); + val.psz_string = (char *)"telnet"; + text.psz_string = (char *)_("Telnet Interface"); + var_Change( p_intf, "intf-add", VLC_VAR_ADDCHOICE, &val, &text ); + val.psz_string = (char *)"http"; + text.psz_string = (char *)_("Web Interface"); + var_Change( p_intf, "intf-add", VLC_VAR_ADDCHOICE, &val, &text ); + val.psz_string = (char *)"logger"; + text.psz_string = (char *)_("Debug logging"); + var_Change( p_intf, "intf-add", VLC_VAR_ADDCHOICE, &val, &text ); + val.psz_string = (char *)"gestures"; + text.psz_string = (char *)_("Mouse Gestures"); + var_Change( p_intf, "intf-add", VLC_VAR_ADDCHOICE, &val, &text ); + + var_AddCallback( p_intf, "intf-add", AddIntfCallback, NULL ); + + do + { + /* Give control to the interface */ + if( p_intf->pf_run ) + p_intf->pf_run( p_intf ); + else + { + vlc_object_lock( p_intf ); + while( vlc_object_alive( p_intf ) ) + vlc_object_wait( p_intf ); + vlc_object_unlock( p_intf ); + } + + if( !p_intf->psz_switch_intf ) + { + break; + } + + /* Make sure the old interface is completely uninitialized */ + module_Unneed( p_intf, p_intf->p_module ); + + /* Provide ability to switch the main interface on the fly */ + psz_intf = p_intf->psz_switch_intf; + p_intf->psz_switch_intf = NULL; + + vlc_object_lock( p_intf ); + p_intf->b_die = false; /* FIXME */ + p_intf->b_dead = false; + + vlc_object_unlock( p_intf ); + + p_intf->psz_intf = psz_intf; + p_intf->p_module = module_Need( p_intf, "interface", psz_intf, 0 ); + } + while( p_intf->p_module ); + return NULL; +} + +#ifdef __APPLE__ +/***************************************************************************** + * MonitorLibVLCDeath: Used when b_should_run_on_first_thread is set. + *****************************************************************************/ +static void * MonitorLibVLCDeath( vlc_object_t * p_this ) +{ + intf_thread_t *p_intf = (intf_thread_t *)p_this; + libvlc_int_t * p_libvlc = p_intf->p_libvlc; + vlc_object_lock( p_libvlc ); + while(vlc_object_alive( p_libvlc ) ) + { + if(p_intf->b_die) + { + vlc_object_unlock( p_libvlc ); + return NULL; + } + vlc_object_wait( p_libvlc ); + } + vlc_object_unlock( p_libvlc ); + + /* Someone killed libvlc */ + + /* Make sure we kill all interface objects, especially + * those that are blocking libvlc (running on main thread) */ + vlc_list_t * p_list = vlc_list_find( p_libvlc, VLC_OBJECT_INTF, FIND_CHILD ); + for( int i = 0; i < p_list->i_count; i++ ) + { + vlc_object_t * p_intf = p_list->p_values[i].p_object; + vlc_object_kill( p_intf ); + } + vlc_list_release( p_list ); + return NULL; +} +#endif + +static int AddIntfCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) +{ + intf_thread_t *p_intf; + char *psz_intf = malloc( strlen(newval.psz_string) + sizeof(",none") ); + + (void)psz_cmd; (void)oldval; (void)p_data; + + /* Try to create the interface */ + sprintf( psz_intf, "%s,none", newval.psz_string ); + p_intf = intf_Create( p_this->p_libvlc, psz_intf ); + free( psz_intf ); + if( p_intf == NULL ) + { + msg_Err( p_this, "interface \"%s\" initialization failed", + newval.psz_string ); + return VLC_EGENERIC; + } + + /* Try to run the interface */ + if( intf_RunThread( p_intf ) != VLC_SUCCESS ) + { + vlc_object_detach( p_intf ); + vlc_object_release( p_intf ); + return VLC_EGENERIC; + } + + return VLC_SUCCESS; +} + diff --git a/VLC/interface.h b/VLC/interface.h new file mode 100644 index 0000000..79ffc1f --- /dev/null +++ b/VLC/interface.h @@ -0,0 +1,40 @@ +/***************************************************************************** + * interface.h: Internal interface prototypes and structures + ***************************************************************************** + * Copyright (C) 1998-2006 the VideoLAN team + * $Id$ + * + * Authors: Vincent Seguin + * Clément Stenac + * Felix Kühne + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#if defined(__PLUGIN__) || defined(__BUILTIN__) || !defined(__LIBVLC__) + +#endif + +#ifndef __LIBVLC_INTERFACE_H +# define __LIBVLC_INTERFACE_H 1 + +/********************************************************************** + * Interaction + **********************************************************************/ + +interaction_t * interaction_Init( libvlc_int_t *p_libvlc ); +void interaction_Destroy( interaction_t * ); + +#endif diff --git a/VLC/intf_eject.c b/VLC/intf_eject.c new file mode 100644 index 0000000..35d2b9f --- /dev/null +++ b/VLC/intf_eject.c @@ -0,0 +1,295 @@ +/***************************************************************************** + * intf_eject.c: CD/DVD-ROM ejection handling functions + ***************************************************************************** + * Copyright (C) 2001-2004 the VideoLAN team + * $Id$ + * + * Authors: Julien Blache for the Linux part + * with code taken from the Linux "eject" command + * Jon Lech Johansen for Darwin + * Gildas Bazin for Win32 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/** + * \file + * This file contain functions to eject CD and DVD drives + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#ifdef HAVE_UNISTD_H +# include +#endif + + +# include + + +#ifdef HAVE_DVD_H +# include +#endif + +#if defined(__linux__) && defined(HAVE_LINUX_VERSION_H) +# include + /* handy macro found in 2.1 kernels, but not in older ones */ +# ifndef KERNEL_VERSION +# define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) +# endif + +# include +# include +# include + +# include +# include + +# include +# if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) +# include +# endif + +# include +# include +# include +#endif + +#if defined( WIN32 ) && !defined( UNDER_CE ) +# include +#endif + +#include "vlc_interface.h" + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +#if defined(__linux__) && defined(HAVE_LINUX_VERSION_H) +static int EjectSCSI ( int i_fd ); +#endif + +/***************************************************************************** + * intf_Eject: eject the CDRom + ***************************************************************************** + * returns 0 on success + * returns 1 on failure + * returns -1 if not implemented + *****************************************************************************/ +/** + * \brief Ejects the CD /DVD + * \ingroup vlc_interface + * \param p_this the calling vlc_object_t + * \param psz_device the CD/DVD to eject + * \return 0 on success, 1 on failure, -1 if not implemented + */ +int __intf_Eject( vlc_object_t *p_this, const char *psz_device ) +{ + VLC_UNUSED(p_this); + int i_ret = VLC_SUCCESS; + +#ifdef __APPLE__ + FILE *p_eject; + char *psz_disk; + char sz_cmd[32]; + + /* + * The only way to cleanly unmount the disc under MacOS X + * is to use the 'disktool' command line utility. It uses + * the non-public Disk Arbitration API, which can not be + * used by Cocoa or Carbon applications. + */ + + if( ( psz_disk = (char *)strstr( psz_device, "disk" ) ) != NULL && + strlen( psz_disk ) > 4 ) + { +#define EJECT_CMD "/usr/sbin/disktool -e %s 0" + snprintf( sz_cmd, sizeof(sz_cmd), EJECT_CMD, psz_disk ); +#undef EJECT_CMD + + if( ( p_eject = popen( sz_cmd, "r" ) ) != NULL ) + { + char psz_result[0x200]; + i_ret = fread( psz_result, 1, sizeof(psz_result) - 1, p_eject ); + + if( i_ret == 0 && ferror( p_eject ) != 0 ) + { + pclose( p_eject ); + return VLC_EGENERIC; + } + + pclose( p_eject ); + + psz_result[ i_ret ] = 0; + + if( strstr( psz_result, "Disk Ejected" ) != NULL ) + { + return VLC_SUCCESS; + } + } + } + + return VLC_EGENERIC; + +#elif defined(UNDER_CE) + msg_Warn( p_this, "CD-Rom ejection unsupported on this platform" ); + return i_ret; + +#elif defined(WIN32) + MCI_OPEN_PARMS op; + MCI_STATUS_PARMS st; + DWORD i_flags; + char psz_drive[4]; + + memset( &op, 0, sizeof(MCI_OPEN_PARMS) ); + op.lpstrDeviceType = (LPCSTR)MCI_DEVTYPE_CD_AUDIO; + + strcpy( psz_drive, "X:" ); + psz_drive[0] = psz_device[0]; + op.lpstrElementName = psz_drive; + + /* Set the flags for the device type */ + i_flags = MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID | + MCI_OPEN_ELEMENT | MCI_OPEN_SHAREABLE; + + if( !mciSendCommand( 0, MCI_OPEN, i_flags, (unsigned long)&op ) ) + { + st.dwItem = MCI_STATUS_READY; + /* Eject disc */ + i_ret = mciSendCommand( op.wDeviceID, MCI_SET, MCI_SET_DOOR_OPEN, 0 ); + /* Release access to the device */ + mciSendCommand( op.wDeviceID, MCI_CLOSE, MCI_WAIT, 0 ); + } + else i_ret = VLC_EGENERIC; + + return i_ret; +#else /* WIN32 */ + + int i_fd; + + /* This code could be extended to support CD/DVD-ROM chargers */ + + i_fd = open( psz_device, O_RDONLY | O_NONBLOCK ); + + if( i_fd == -1 ) + { + msg_Err( p_this, "could not open device %s", psz_device ); + return VLC_EGENERIC; + } + +#if defined(__linux__) && defined(HAVE_LINUX_VERSION_H) + /* Try a simple ATAPI eject */ + i_ret = ioctl( i_fd, CDROMEJECT, 0 ); + + if( i_ret != 0 ) + { + i_ret = EjectSCSI( i_fd ); + } + + if( i_ret != 0 ) + { + msg_Err( p_this, "could not eject %s", psz_device ); + } + +#elif defined (HAVE_DVD_H) + i_ret = ioctl( i_fd, CDROMEJECT, 0 ); + +#else + msg_Warn( p_this, "CD-ROM ejection unsupported on this platform" ); + i_ret = -1; + +#endif + close( i_fd ); + + return i_ret; +#endif +} + +/* The following functions are local */ + +#if defined(__linux__) && defined(HAVE_LINUX_VERSION_H) +/***************************************************************************** + * Eject using SCSI commands. Return 0 if successful + *****************************************************************************/ +/** + * \brief Ejects the CD /DVD using SCSI commands + * \ingroup vlc_interface + * This function is local + * \param i_fd a device nummber + * \return 0 on success, VLC_EGENERIC on failure + */ +static int EjectSCSI( int i_fd ) +{ + int i_status; + + struct sdata + { + int inlen; + int outlen; + char cmd[256]; + } scsi_cmd; + + scsi_cmd.inlen = 0; + scsi_cmd.outlen = 0; + scsi_cmd.cmd[0] = ALLOW_MEDIUM_REMOVAL; + scsi_cmd.cmd[1] = 0; + scsi_cmd.cmd[2] = 0; + scsi_cmd.cmd[3] = 0; + scsi_cmd.cmd[4] = 0; + scsi_cmd.cmd[5] = 0; + i_status = ioctl( i_fd, SCSI_IOCTL_SEND_COMMAND, (void *)&scsi_cmd ); + if( i_status != 0 ) + { + return VLC_EGENERIC; + } + + scsi_cmd.inlen = 0; + scsi_cmd.outlen = 0; + scsi_cmd.cmd[0] = START_STOP; + scsi_cmd.cmd[1] = 0; + scsi_cmd.cmd[2] = 0; + scsi_cmd.cmd[3] = 0; + scsi_cmd.cmd[4] = 1; + scsi_cmd.cmd[5] = 0; + i_status = ioctl( i_fd, SCSI_IOCTL_SEND_COMMAND, (void *)&scsi_cmd ); + if( i_status != 0 ) + { + return VLC_EGENERIC; + } + + scsi_cmd.inlen = 0; + scsi_cmd.outlen = 0; + scsi_cmd.cmd[0] = START_STOP; + scsi_cmd.cmd[1] = 0; + scsi_cmd.cmd[2] = 0; + scsi_cmd.cmd[3] = 0; + scsi_cmd.cmd[4] = 2; + scsi_cmd.cmd[5] = 0; + i_status = ioctl( i_fd, SCSI_IOCTL_SEND_COMMAND, (void *)&scsi_cmd ); + if( i_status != 0 ) + { + return VLC_EGENERIC; + } + + /* Force kernel to reread partition table when new disc inserted */ + i_status = ioctl( i_fd, BLKRRPART ); + + return i_status; +} +#endif + diff --git a/VLC/io.c b/VLC/io.c new file mode 100644 index 0000000..25c5376 --- /dev/null +++ b/VLC/io.c @@ -0,0 +1,567 @@ +/***************************************************************************** + * io.c: network I/O functions + ***************************************************************************** + * Copyright (C) 2004-2005, 2007 the VideoLAN team + * Copyright © 2005-2006 Rémi Denis-Courmont + * $Id$ + * + * Authors: Laurent Aimar + * Rémi Denis-Courmont + * Christophe Mutricy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#include +#include +#include + +#include +#include + + +# include + +#ifdef HAVE_SYS_TIME_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif +#ifdef HAVE_POLL +# include +#endif + +#include "vlc_network.h" + +#ifndef INADDR_ANY +# define INADDR_ANY 0x00000000 +#endif +#ifndef INADDR_NONE +# define INADDR_NONE 0xFFFFFFFF +#endif + +#if defined(WIN32) || defined(UNDER_CE) +# undef EAFNOSUPPORT +# define EAFNOSUPPORT WSAEAFNOSUPPORT +#endif + +#ifdef HAVE_LINUX_DCCP_H +/* TODO: use glibc instead of linux-kernel headers */ +# include +# define SOL_DCCP 269 +#endif + +extern int rootwrap_bind (int family, int socktype, int protocol, + const struct sockaddr *addr, size_t alen); + +int net_SetupSocket (int fd) +{ +#if defined (WIN32) || defined (UNDER_CE) + ioctlsocket (fd, FIONBIO, &(unsigned long){ 1 }); +#else + fcntl (fd, F_SETFD, FD_CLOEXEC); + fcntl (fd, F_SETFL, fcntl (fd, F_GETFL, 0) | O_NONBLOCK); +#endif + + setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof (int)); + return 0; +} + + +int net_Socket (vlc_object_t *p_this, int family, int socktype, + int protocol) +{ + int fd = socket (family, socktype, protocol); + if (fd == -1) + { + if (net_errno != EAFNOSUPPORT) + msg_Err (p_this, "cannot create socket: %m"); + return -1; + } + + net_SetupSocket (fd); + +#ifdef IPV6_V6ONLY + /* + * Accepts only IPv6 connections on IPv6 sockets. + * If possible, we should open two sockets, but it is not always possible. + */ + if (family == AF_INET6) + setsockopt (fd, IPPROTO_IPV6, IPV6_V6ONLY, &(int){ 1 }, sizeof (int)); +#endif + +#if defined (WIN32) || defined (UNDER_CE) +# ifndef IPV6_PROTECTION_LEVEL +# warning Please update your C library headers. +# define IPV6_PROTECTION_LEVEL 23 +# define PROTECTION_LEVEL_UNRESTRICTED 10 +# endif + if (family == AF_INET6) + setsockopt (fd, IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, + &(int){ PROTECTION_LEVEL_UNRESTRICTED }, sizeof (int)); +#endif + +#ifdef DCCP_SOCKOPT_SERVICE + if (socktype == SOL_DCCP) + { + char *dccps = var_CreateGetNonEmptyString (p_this, "dccp-service"); + if (dccps != NULL) + { + setsockopt (fd, SOL_DCCP, DCCP_SOCKOPT_SERVICE, dccps, + (strlen (dccps) + 3) & ~3); + free (dccps); + } + } +#endif + + return fd; +} + + +int *net_Listen (vlc_object_t *p_this, const char *psz_host, + int i_port, int protocol) +{ + struct addrinfo hints, *res; + int socktype = SOCK_DGRAM; + + switch( protocol ) + { + case IPPROTO_TCP: + socktype = SOCK_STREAM; + break; + case 33: /* DCCP */ +#ifdef __linux__ +# ifndef SOCK_DCCP +# define SOCK_DCCP 6 +# endif + socktype = SOCK_DCCP; +#endif + break; + } + + memset (&hints, 0, sizeof( hints )); + /* Since we use port numbers rather than service names, the socket type + * does not really matter. */ + hints.ai_socktype = SOCK_DGRAM; + hints.ai_flags = AI_PASSIVE; + + msg_Dbg (p_this, "net: listening to %s port %d", psz_host, i_port); + + int i_val = vlc_getaddrinfo (p_this, psz_host, i_port, &hints, &res); + if (i_val) + { + msg_Err (p_this, "Cannot resolve %s port %d : %s", psz_host, i_port, + vlc_gai_strerror (i_val)); + return NULL; + } + + int *sockv = NULL; + unsigned sockc = 0; + + for (struct addrinfo *ptr = res; ptr != NULL; ptr = ptr->ai_next) + { + int fd = net_Socket (p_this, ptr->ai_family, socktype, protocol); + if (fd == -1) + { + msg_Dbg (p_this, "socket error: %m"); + continue; + } + + /* Bind the socket */ +#if defined (WIN32) || defined (UNDER_CE) + /* + * Under Win32 and for multicasting, we bind to INADDR_ANY. + * This is of course a severe bug, since the socket would logically + * receive unicast traffic, and multicast traffic of groups subscribed + * to via other sockets. + */ + if (net_SockAddrIsMulticast (ptr->ai_addr, ptr->ai_addrlen) + && (sizeof (struct sockaddr_storage) >= ptr->ai_addrlen)) + { + // This works for IPv4 too - don't worry! + struct sockaddr_in6 dumb = + { + .sin6_family = ptr->ai_addr->sa_family, + .sin6_port = ((struct sockaddr_in *)(ptr->ai_addr))->sin_port + }; + + bind (fd, (struct sockaddr *)&dumb, ptr->ai_addrlen); + } + else +#endif + if (bind (fd, ptr->ai_addr, ptr->ai_addrlen)) + { + net_Close (fd); +#if !defined(WIN32) && !defined(UNDER_CE) + fd = rootwrap_bind (ptr->ai_family, socktype, + protocol ?: ptr->ai_protocol, ptr->ai_addr, + ptr->ai_addrlen); + if (fd != -1) + { + msg_Dbg (p_this, "got socket %d from rootwrap", fd); + } + else +#endif + { + msg_Err (p_this, "socket bind error (%m)"); + continue; + } + } + + if (net_SockAddrIsMulticast (ptr->ai_addr, ptr->ai_addrlen)) + { + if (net_Subscribe (p_this, fd, ptr->ai_addr, ptr->ai_addrlen)) + { + net_Close (fd); + continue; + } + } + + /* Listen */ + switch (socktype) + { + case SOCK_STREAM: + case SOCK_RDM: + case SOCK_SEQPACKET: +#ifdef SOCK_DCCP + case SOCK_DCCP: +#endif + if (listen (fd, INT_MAX)) + { + msg_Err (p_this, "socket listen error (%m)"); + net_Close (fd); + continue; + } + } + + int *nsockv = (int *)realloc (sockv, (sockc + 2) * sizeof (int)); + if (nsockv != NULL) + { + nsockv[sockc++] = fd; + sockv = nsockv; + } + else + net_Close (fd); + } + + vlc_freeaddrinfo (res); + + if (sockv != NULL) + sockv[sockc] = -1; + + return sockv; +} + + +/***************************************************************************** + * __net_Read: + ***************************************************************************** + * Reads from a network socket. + * If waitall is true, then we repeat until we have read the right amount of + * data; in that case, a short count means EOF has been reached or the VLC + * object has been signaled. + *****************************************************************************/ +ssize_t +__net_Read (vlc_object_t *restrict p_this, int fd, const v_socket_t *vs, + uint8_t *restrict p_buf, size_t i_buflen, bool waitall) +{ + size_t i_total = 0; + struct pollfd ufd[2] = { + { .fd = fd, .events = POLLIN }, + { .fd = vlc_object_waitpipe (p_this), .events = POLLIN }, + }; + + if (ufd[1].fd == -1) + return -1; /* vlc_object_waitpipe() sets errno */ + + while (i_buflen > 0) + { + ufd[0].revents = ufd[1].revents = 0; + + if (poll (ufd, sizeof (ufd) / sizeof (ufd[0]), -1) < 0) + { + if (errno != EINTR) + goto error; + continue; + } + +#ifndef POLLRDHUP /* This is nice but non-portable */ +# define POLLRDHUP 0 +#endif + if (i_total > 0) + { + /* Errors (-1) and EOF (0) will be returned on next call, + * otherwise we'd "hide" the error from the caller, which is a + * bad idea™. */ + if (ufd[0].revents & (POLLERR|POLLNVAL|POLLRDHUP)) + break; + if (ufd[1].revents) + break; + } + else + { + if (ufd[1].revents) + { + assert (p_this->b_die); + msg_Dbg (p_this, "socket %d polling interrupted", fd); +#if defined(WIN32) || defined(UNDER_CE) + WSASetLastError (WSAEINTR); +#else + errno = EINTR; +#endif + goto silent; + } + } + + assert (ufd[0].revents); + + ssize_t n; + if (vs != NULL) + { + n = vs->pf_recv (vs->p_sys, p_buf, i_buflen); + } + else + { +#ifdef WIN32 + n = recv (fd, p_buf, i_buflen, 0); +#else + n = read (fd, p_buf, i_buflen); +#endif + } + + if (n == -1) + { +#if defined(WIN32) || defined(UNDER_CE) + switch (WSAGetLastError ()) + { + case WSAEWOULDBLOCK: + /* only happens with vs != NULL (TLS) - not really an error */ + continue; + + case WSAEMSGSIZE: + /* For UDP only */ + /* On Win32, recv() fails if the datagram doesn't fit inside + * the passed buffer, even though the buffer will be filled + * with the first part of the datagram. */ + msg_Err (p_this, "Receive error: " + "Increase the mtu size (--mtu option)"); + n = i_buflen; + break; + } +#else + switch (errno) + { + case EAGAIN: /* spurious wakeup or no TLS data */ + case EINTR: /* asynchronous signal */ + continue; + } +#endif + goto error; + } + + if (n == 0) + /* For streams, this means end of file, and there will not be any + * further data ever on the stream. For datagram sockets, this + * means empty datagram, and there could be more data coming. + * However, it makes no sense to set with datagrams in the + * first place. + */ + break; // EOF + + i_total += n; + p_buf += n; + i_buflen -= n; + + if (!waitall) + break; + } + + return i_total; + +error: + msg_Err (p_this, "Read error: %m"); +silent: + return -1; +} + + +/* Write exact amount requested */ +ssize_t __net_Write( vlc_object_t *p_this, int fd, const v_socket_t *p_vs, + const uint8_t *p_data, size_t i_data ) +{ + size_t i_total = 0; + struct pollfd ufd[2] = { + { .fd = fd, .events = POLLOUT }, + { .fd = vlc_object_waitpipe (p_this), .events = POLLIN }, + }; + + if (ufd[1].fd == -1) + return -1; + + while( i_data > 0 ) + { + ssize_t val; + + ufd[0].revents = ufd[1].revents = 0; + + if (poll (ufd, 1, -1) == -1) + { + if (errno == EINTR) + continue; + msg_Err (p_this, "Polling error: %m"); + return -1; + } + + if (i_total > 0) + { /* If POLLHUP resp. POLLERR|POLLNVAL occurs while we have already + * read some data, it is important that we first return the number + * of bytes read, and then return 0 resp. -1 on the NEXT call. */ + if (ufd[0].revents & (POLLHUP|POLLERR|POLLNVAL)) + break; + if (ufd[1].revents) /* VLC object signaled */ + break; + } + else + { + if (ufd[1].revents) + { + assert (p_this->b_die); + errno = EINTR; + goto error; + } + } + + if (p_vs != NULL) + val = p_vs->pf_send (p_vs->p_sys, p_data, i_data); + else +#ifdef WIN32 + val = send (fd, p_data, i_data, 0); +#else + val = write (fd, p_data, i_data); +#endif + + if (val == -1) + { + if (errno == EINTR) + continue; + msg_Err (p_this, "Write error: %m"); + break; + } + + p_data += val; + i_data -= val; + i_total += val; + } + + if ((i_total > 0) || (i_data == 0)) + return i_total; + +error: + return -1; +} + +char *__net_Gets( vlc_object_t *p_this, int fd, const v_socket_t *p_vs ) +{ + char *psz_line = NULL, *ptr = NULL; + size_t i_line = 0, i_max = 0; + + + for( ;; ) + { + if( i_line == i_max ) + { + i_max += 1024; + psz_line = realloc( psz_line, i_max ); + ptr = psz_line + i_line; + } + + if( net_Read( p_this, fd, p_vs, (uint8_t *)ptr, 1, true ) != 1 ) + { + if( i_line == 0 ) + { + free( psz_line ); + return NULL; + } + break; + } + + if ( *ptr == '\n' ) + break; + + i_line++; + ptr++; + } + + *ptr-- = '\0'; + + if( ( ptr >= psz_line ) && ( *ptr == '\r' ) ) + *ptr = '\0'; + + return psz_line; +} + +ssize_t net_Printf( vlc_object_t *p_this, int fd, const v_socket_t *p_vs, + const char *psz_fmt, ... ) +{ + int i_ret; + va_list args; + va_start( args, psz_fmt ); + i_ret = net_vaPrintf( p_this, fd, p_vs, psz_fmt, args ); + va_end( args ); + + return i_ret; +} + +ssize_t __net_vaPrintf( vlc_object_t *p_this, int fd, const v_socket_t *p_vs, + const char *psz_fmt, va_list args ) +{ + char *psz; + int i_ret; + + int i_size = vasprintf( &psz, psz_fmt, args ); + if( i_size == -1 ) + return -1; + i_ret = __net_Write( p_this, fd, p_vs, (uint8_t *)psz, i_size ) < i_size + ? -1 : i_size; + free( psz ); + + return i_ret; +} + +#ifdef WIN32 + /* vlc_sendmsg, vlc_recvmsg Defined in winsock.c */ +#else /* !WIN32 */ +ssize_t vlc_sendmsg (int s, struct msghdr *hdr, int flags) +{ + return sendmsg (s, hdr, flags); +} + +ssize_t vlc_recvmsg (int s, struct msghdr *hdr, int flags) +{ + return recvmsg (s, hdr, flags); +} +#endif /* WIN32 */ + diff --git a/VLC/iso_lang.c b/VLC/iso_lang.c new file mode 100644 index 0000000..f2c66b6 --- /dev/null +++ b/VLC/iso_lang.c @@ -0,0 +1,79 @@ +/***************************************************************************** + * iso_lang.c: function to decode language code (in dvd or a52 for instance). + ***************************************************************************** + * Copyright (C) 1998-2004 the VideoLAN team + * $Id$ + * + * Author: Stéphane Borel + * Arnaud de Bossoreille de Ribou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "vlc_common.h" + +#include "vlc_iso_lang.h" + +/***************************************************************************** + * Local tables + *****************************************************************************/ +#include "iso-639_def.h" + +static const iso639_lang_t unknown_language = + { "Unknown", "Unknown", "??", "???", "???" }; + +const iso639_lang_t * GetLang_1( const char * psz_code ) +{ + const iso639_lang_t *p_lang; + + for( p_lang = p_languages; p_lang->psz_eng_name; p_lang++ ) + if( !strncmp( p_lang->psz_iso639_1, psz_code, 2 ) ) + return p_lang; + + return &unknown_language; +} + +const iso639_lang_t * GetLang_2T( const char * psz_code ) +{ + const iso639_lang_t *p_lang; + + for( p_lang = p_languages; p_lang->psz_eng_name; p_lang++ ) + if( !strncmp( p_lang->psz_iso639_2T, psz_code, 3 ) ) + return p_lang; + + return &unknown_language; +} + +const iso639_lang_t * GetLang_2B( const char * psz_code ) +{ + const iso639_lang_t *p_lang; + + for( p_lang = p_languages; p_lang->psz_eng_name; p_lang++ ) + if( !strncmp( p_lang->psz_iso639_2B, psz_code, 3 ) ) + return p_lang; + + return &unknown_language; +} + diff --git a/VLC/item.c b/VLC/item.c new file mode 100644 index 0000000..9d769c1 --- /dev/null +++ b/VLC/item.c @@ -0,0 +1,956 @@ +/***************************************************************************** + * item.c : Playlist item creation/deletion/add/removal functions + ***************************************************************************** + * Copyright (C) 1999-2007 the VideoLAN team + * $Id$ + * + * Authors: Samuel Hocevar + * Clément Stenac + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include +#include "vlc_playlist.h" +#include "playlist_internal.h" + +static void AddItem( playlist_t *p_playlist, playlist_item_t *p_item, + playlist_item_t *p_node, int i_mode, int i_pos ); +static void GoAndPreparse( playlist_t *p_playlist, int i_mode, + playlist_item_t *, playlist_item_t * ); +static void ChangeToNode( playlist_t *p_playlist, playlist_item_t *p_item ); +static int DeleteInner( playlist_t * p_playlist, playlist_item_t *p_item, + bool b_stop ); + +/***************************************************************************** + * An input item has gained a subitem (Event Callback) + *****************************************************************************/ +static void input_item_subitem_added( const vlc_event_t * p_event, + void * user_data ) +{ + playlist_item_t *p_parent_playlist_item = user_data; + playlist_t * p_playlist = p_parent_playlist_item->p_playlist; + input_item_t * p_parent, * p_child; + playlist_item_t * p_child_in_category; + playlist_item_t * p_item_in_category; + bool b_play; + + p_parent = p_event->p_obj; + p_child = p_event->u.input_item_subitem_added.p_new_child; + + PL_LOCK; + b_play = var_CreateGetBool( p_playlist, "playlist-autostart" ); + + /* This part is really hakish, but this playlist system isn't simple */ + /* First check if we haven't already added the item as we are + * listening using the onelevel and the category representent + * (Because of the playlist design) */ + p_child_in_category = playlist_ItemFindFromInputAndRoot( + p_playlist, p_child->i_id, + p_playlist->p_root_category, + false /* Only non-node */ ); + + if( !p_child_in_category ) + { + /* Then, transform to a node if needed */ + p_item_in_category = playlist_ItemFindFromInputAndRoot( + p_playlist, p_parent->i_id, + p_playlist->p_root_category, + false /* Only non-node */ ); + if( !p_item_in_category ) + { + /* Item may have been removed */ + PL_UNLOCK; + return; + } + + b_play = b_play && + p_item_in_category == get_current_status_item( p_playlist ); + + /* If this item is already a node don't transform it */ + if( p_item_in_category->i_children == -1 ) + { + p_item_in_category = playlist_ItemToNode( p_playlist, + p_item_in_category, pl_Locked ); + p_item_in_category->p_input->i_type = ITEM_TYPE_PLAYLIST; + } + + int i_ret = playlist_BothAddInput( p_playlist, p_child, + p_item_in_category, + PLAYLIST_APPEND | PLAYLIST_SPREPARSE , PLAYLIST_END, + NULL, NULL, pl_Locked ); + + if( i_ret == VLC_SUCCESS && b_play ) + { + playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, + pl_Locked, p_item_in_category, NULL ); + } + } + + PL_UNLOCK; + +} + +/***************************************************************************** + * An input item's meta or duration has changed (Event Callback) + *****************************************************************************/ +static void input_item_changed( const vlc_event_t * p_event, + void * user_data ) +{ + (void)p_event; + playlist_item_t * p_item = user_data; + var_SetInteger( p_item->p_playlist, "item-change", p_item->i_id ); +} + +/***************************************************************************** + * Listen to vlc_InputItemAddSubItem event + *****************************************************************************/ +static void install_input_item_observer( playlist_item_t * p_item ) +{ + vlc_event_manager_t * p_em = &p_item->p_input->event_manager; + vlc_event_attach( p_em, vlc_InputItemSubItemAdded, + input_item_subitem_added, p_item ); + vlc_event_attach( p_em, vlc_InputItemDurationChanged, + input_item_changed, p_item ); + vlc_event_attach( p_em, vlc_InputItemMetaChanged, + input_item_changed, p_item ); + vlc_event_attach( p_em, vlc_InputItemNameChanged, + input_item_changed, p_item ); + vlc_event_attach( p_em, vlc_InputItemInfoChanged, + input_item_changed, p_item ); + vlc_event_attach( p_em, vlc_InputItemErrorWhenReadingChanged, + input_item_changed, p_item ); +} + +static void uninstall_input_item_observer( playlist_item_t * p_item ) +{ + vlc_event_manager_t * p_em = &p_item->p_input->event_manager; + vlc_event_detach( p_em, vlc_InputItemSubItemAdded, + input_item_subitem_added, p_item ); + vlc_event_detach( p_em, vlc_InputItemMetaChanged, + input_item_changed, p_item ); + vlc_event_detach( p_em, vlc_InputItemDurationChanged, + input_item_changed, p_item ); + vlc_event_detach( p_em, vlc_InputItemNameChanged, + input_item_changed, p_item ); + vlc_event_detach( p_em, vlc_InputItemInfoChanged, + input_item_changed, p_item ); + vlc_event_detach( p_em, vlc_InputItemErrorWhenReadingChanged, + input_item_changed, p_item ); +} + +/***************************************************************************** + * Playlist item creation + *****************************************************************************/ +playlist_item_t *playlist_ItemNewFromInput( playlist_t *p_playlist, + input_item_t *p_input ) +{ + DECMALLOC_NULL( p_item, playlist_item_t ); + + assert( p_input ); + + p_item->p_input = p_input; + vlc_gc_incref( p_item->p_input ); + + p_item->i_id = ++p_playlist->i_last_playlist_id; + + p_item->p_parent = NULL; + p_item->i_children = -1; + p_item->pp_children = NULL; + p_item->i_flags = 0; + p_item->p_playlist = p_playlist; + + install_input_item_observer( p_item ); + + return p_item; +} + +playlist_item_t * playlist_ItemNewWithType( playlist_t *p_playlist, + const char *psz_uri, + const char *psz_name, + int i_options, + const char *const *ppsz_options, + int i_duration, int i_type ) +{ + input_item_t *p_input; + if( psz_uri == NULL ) return NULL; + p_input = input_item_NewWithType( VLC_OBJECT(p_playlist), psz_uri, + psz_name, i_options, ppsz_options, + i_duration, i_type ); + return playlist_ItemNewFromInput( p_playlist, p_input ); +} + +/*************************************************************************** + * Playlist item destruction + ***************************************************************************/ + +/** + * Release an item + * + * \param p_item item to delete + * \return VLC_SUCCESS +*/ +int playlist_ItemRelease( playlist_item_t *p_item ) +{ + /* For the assert */ + playlist_t *p_playlist = p_item->p_playlist; + PL_ASSERT_LOCKED; + + /* Surprise, we can't actually do more because we + * don't do refcounting, or eauivalent. + * Because item are not only accessed by their id + * using playlist_item outside the PL_LOCK isn't safe. + * Most of the modules does that. + * + * Who wants to add proper memory management? */ + uninstall_input_item_observer( p_item ); + ARRAY_APPEND( p_item->p_playlist->items_to_delete, p_item); + return VLC_SUCCESS; +} + +/** + * Delete input item + * + * Remove an input item when it appears from a root playlist item + * \param p_playlist playlist object + * \param i_input_id id of the input to delete + * \param p_root root playlist item + * \param b_do_stop must stop or not the playlist + * \return VLC_SUCCESS or VLC_EGENERIC +*/ +static int DeleteFromInput( playlist_t *p_playlist, int i_input_id, + playlist_item_t *p_root, bool b_do_stop ) +{ + int i; + PL_ASSERT_LOCKED; + for( i = 0 ; i< p_root->i_children ; i++ ) + { + if( p_root->pp_children[i]->i_children == -1 && + p_root->pp_children[i]->p_input->i_id == i_input_id ) + { + DeleteInner( p_playlist, p_root->pp_children[i], b_do_stop ); + return VLC_SUCCESS; + } + else if( p_root->pp_children[i]->i_children >= 0 ) + { + int i_ret = DeleteFromInput( p_playlist, i_input_id, + p_root->pp_children[i], b_do_stop ); + if( i_ret == VLC_SUCCESS ) return VLC_SUCCESS; + } + } + return VLC_EGENERIC; +} + +/** + * Delete input item + * + * Remove an input item when it appears from a root playlist item + * \param p_playlist playlist object + * \param i_input_id id of the input to delete + * \param p_root root playlist item + * \param b_locked TRUE if the playlist is locked + * \return VLC_SUCCESS or VLC_EGENERIC + */ +int playlist_DeleteFromInputInParent( playlist_t *p_playlist, int i_input_id, + playlist_item_t *p_root, bool b_locked ) +{ + int i_ret; + PL_LOCK_IF( !b_locked ); + i_ret = DeleteFromInput( p_playlist, i_input_id, + p_root, true ); + PL_UNLOCK_IF( !b_locked ); + return i_ret; +} + +/** + * Delete from input + * + * Remove an input item from ONELEVEL and CATEGORY + * \param p_playlist playlist object + * \param i_input_id id of the input to delete + * \param b_locked TRUE if the playlist is locked + * \return VLC_SUCCESS or VLC_ENOITEM + */ +int playlist_DeleteFromInput( playlist_t *p_playlist, int i_input_id, + bool b_locked ) +{ + int i_ret1, i_ret2; + PL_LOCK_IF( !b_locked ); + i_ret1 = DeleteFromInput( p_playlist, i_input_id, + p_playlist->p_root_category, true ); + i_ret2 = DeleteFromInput( p_playlist, i_input_id, + p_playlist->p_root_onelevel, true ); + PL_UNLOCK_IF( !b_locked ); + return ( i_ret1 == VLC_SUCCESS || i_ret2 == VLC_SUCCESS ) ? + VLC_SUCCESS : VLC_ENOITEM; +} + +/** + * Clear the playlist + * + * \param p_playlist playlist object + * \param b_locked TRUE if the playlist is locked + * \return nothing + */ +void playlist_Clear( playlist_t * p_playlist, bool b_locked ) +{ + PL_LOCK_IF( !b_locked ); + playlist_NodeEmpty( p_playlist, p_playlist->p_local_category, true ); + playlist_NodeEmpty( p_playlist, p_playlist->p_local_onelevel, true ); + PL_UNLOCK_IF( !b_locked ); +} + +/** + * Delete playlist item + * + * Remove a playlist item from the playlist, given its id + * This function is to be used only by the playlist + * \param p_playlist playlist object + * \param i_id id of the item do delete + * \return VLC_SUCCESS or an error + */ +int playlist_DeleteFromItemId( playlist_t *p_playlist, int i_id ) +{ + PL_ASSERT_LOCKED; + playlist_item_t *p_item = playlist_ItemGetById( p_playlist, i_id, + pl_Locked ); + if( !p_item ) return VLC_EGENERIC; + return DeleteInner( p_playlist, p_item, true ); +} + +/*************************************************************************** + * Playlist item addition + ***************************************************************************/ +/** + * Playlist add + * + * Add an item to the playlist or the media library + * \param p_playlist the playlist to add into + * \param psz_uri the mrl to add to the playlist + * \param psz_name a text giving a name or description of this item + * \param i_mode the mode used when adding + * \param i_pos the position in the playlist where to add. If this is + * PLAYLIST_END the item will be added at the end of the playlist + * regardless of its size + * \param b_playlist TRUE for playlist, FALSE for media library + * \param b_locked TRUE if the playlist is locked + * \return The id of the playlist item + */ +int playlist_Add( playlist_t *p_playlist, const char *psz_uri, + const char *psz_name, int i_mode, int i_pos, + bool b_playlist, bool b_locked ) +{ + return playlist_AddExt( p_playlist, psz_uri, psz_name, + i_mode, i_pos, -1, NULL, 0, b_playlist, b_locked ); +} + +/** + * Add a MRL into the playlist or the media library, duration and options given + * + * \param p_playlist the playlist to add into + * \param psz_uri the mrl to add to the playlist + * \param psz_name a text giving a name or description of this item + * \param i_mode the mode used when adding + * \param i_pos the position in the playlist where to add. If this is + * PLAYLIST_END the item will be added at the end of the playlist + * regardless of its size + * \param i_duration length of the item in milliseconds. + * \param ppsz_options an array of options + * \param i_options the number of options + * \param b_playlist TRUE for playlist, FALSE for media library + * \param b_locked TRUE if the playlist is locked + * \return The id of the playlist item +*/ +int playlist_AddExt( playlist_t *p_playlist, const char * psz_uri, + const char *psz_name, int i_mode, int i_pos, + mtime_t i_duration, const char *const *ppsz_options, + int i_options, bool b_playlist, bool b_locked ) +{ + int i_ret; + input_item_t *p_input = input_item_NewExt( p_playlist, psz_uri, psz_name, + i_options, ppsz_options, + i_duration ); + + i_ret = playlist_AddInput( p_playlist, p_input, i_mode, i_pos, b_playlist, + b_locked ); + int i_id = (i_ret == VLC_SUCCESS) ? p_input->i_id : -1; + + vlc_gc_decref( p_input ); + + return i_id; +} + +/** + * Add an input item to the playlist node + * + * \param p_playlist the playlist to add into + * \param p_input the input item to add + * \param i_mode the mode used when adding + * \param i_pos the position in the playlist where to add. If this is + * PLAYLIST_END the item will be added at the end of the playlist + * regardless of its size + * \param b_playlist TRUE for playlist, FALSE for media library + * \param b_locked TRUE if the playlist is locked + * \return VLC_SUCCESS or VLC_ENOMEM or VLC_EGENERIC +*/ +int playlist_AddInput( playlist_t* p_playlist, input_item_t *p_input, + int i_mode, int i_pos, bool b_playlist, + bool b_locked ) +{ + playlist_item_t *p_item_cat, *p_item_one; + if( p_playlist->b_die ) return VLC_EGENERIC; + if( !p_playlist->b_doing_ml ) + PL_DEBUG( "adding item `%s' ( %s )", p_input->psz_name, + p_input->psz_uri ); + + PL_LOCK_IF( !b_locked ); + + /* Add to ONELEVEL */ + p_item_one = playlist_ItemNewFromInput( p_playlist, p_input ); + if( p_item_one == NULL ) return VLC_ENOMEM; + AddItem( p_playlist, p_item_one, + b_playlist ? p_playlist->p_local_onelevel : + p_playlist->p_ml_onelevel , i_mode, i_pos ); + + /* Add to CATEGORY */ + p_item_cat = playlist_ItemNewFromInput( p_playlist, p_input ); + if( p_item_cat == NULL ) return VLC_ENOMEM; + AddItem( p_playlist, p_item_cat, + b_playlist ? p_playlist->p_local_category : + p_playlist->p_ml_category , i_mode, i_pos ); + + GoAndPreparse( p_playlist, i_mode, p_item_cat, p_item_one ); + + PL_UNLOCK_IF( !b_locked ); + return VLC_SUCCESS; +} + +/** + * Add input + * + * Add an input item to p_direct_parent in the category tree, and to the + * matching top category in onelevel + * \param p_playlist the playlist to add into + * \param p_input the input item to add + * \param p_direct_parent the parent item to add into + * \param i_mode the mode used when adding + * \param i_pos the position in the playlist where to add. If this is + * PLAYLIST_END the item will be added at the end of the playlist + * regardless of its size + * \param i_cat id of the items category + * \param i_one id of the item onelevel category + * \param b_locked TRUE if the playlist is locked + * \return VLC_SUCCESS if success, VLC_EGENERIC if fail, VLC_ENOMEM if OOM + */ +int playlist_BothAddInput( playlist_t *p_playlist, + input_item_t *p_input, + playlist_item_t *p_direct_parent, + int i_mode, int i_pos, + int *i_cat, int *i_one, bool b_locked ) +{ + playlist_item_t *p_item_cat, *p_item_one, *p_up; + int i_top; + assert( p_input ); + + if( !vlc_object_alive( p_playlist ) ) + return VLC_EGENERIC; + + PL_LOCK_IF( !b_locked ); + + /* Add to category */ + p_item_cat = playlist_ItemNewFromInput( p_playlist, p_input ); + if( p_item_cat == NULL ) return VLC_ENOMEM; + AddItem( p_playlist, p_item_cat, p_direct_parent, i_mode, i_pos ); + + /* Add to onelevel */ + /** \todo make a faster case for ml import */ + p_item_one = playlist_ItemNewFromInput( p_playlist, p_input ); + if( p_item_one == NULL ) return VLC_ENOMEM; + + p_up = p_direct_parent; + while( p_up->p_parent != p_playlist->p_root_category ) + { + p_up = p_up->p_parent; + } + for( i_top = 0 ; i_top < p_playlist->p_root_onelevel->i_children; i_top++ ) + { + if( p_playlist->p_root_onelevel->pp_children[i_top]->p_input->i_id == + p_up->p_input->i_id ) + { + AddItem( p_playlist, p_item_one, + p_playlist->p_root_onelevel->pp_children[i_top], + i_mode, i_pos ); + break; + } + } + GoAndPreparse( p_playlist, i_mode, p_item_cat, p_item_one ); + + if( i_cat ) *i_cat = p_item_cat->i_id; + if( i_one ) *i_one = p_item_one->i_id; + + PL_UNLOCK_IF( !b_locked ); + return VLC_SUCCESS; +} + +/** + * Add an input item to a given node + * + * \param p_playlist the playlist to add into + * \param p_input the input item to add + * \param p_parent the parent item to add into + * \param i_mode the mode used when addin + * \param i_pos the position in the playlist where to add. If this is + * PLAYLIST_END the item will be added at the end of the playlist + * regardless of its size + * \param b_locked TRUE if the playlist is locked + * \return the new playlist item + */ +playlist_item_t * playlist_NodeAddInput( playlist_t *p_playlist, + input_item_t *p_input, + playlist_item_t *p_parent, + int i_mode, int i_pos, + bool b_locked ) +{ + playlist_item_t *p_item; + assert( p_input ); + assert( p_parent && p_parent->i_children != -1 ); + + if( p_playlist->b_die ) + return NULL; + PL_LOCK_IF( !b_locked ); + + p_item = playlist_ItemNewFromInput( p_playlist, p_input ); + if( p_item == NULL ) return NULL; + AddItem( p_playlist, p_item, p_parent, i_mode, i_pos ); + + PL_UNLOCK_IF( !b_locked ); + + return p_item; +} + +/***************************************************************************** + * Playlist item misc operations + *****************************************************************************/ + +/** + * Item to node + * + * Transform an item to a node. Return the node in the category tree, or NULL + * if not found there + * This function must be entered without the playlist lock + * \param p_playlist the playlist object + * \param p_item the item to transform + * \param b_locked TRUE if the playlist is locked + * \return the item transform in a node + */ +playlist_item_t *playlist_ItemToNode( playlist_t *p_playlist, + playlist_item_t *p_item, + bool b_locked ) +{ + + playlist_item_t *p_item_in_category; + /* What we do + * Find the input in CATEGORY. + * - If we find it + * - change it to node + * - we'll return it at the end + * - If we are a direct child of onelevel root, change to node, else + * delete the input from ONELEVEL + * - If we don't find it, just change to node (we are probably in VLM) + * and return NULL + * + * If we were in ONELEVEL, we thus retrieve the node in CATEGORY (will be + * useful for later BothAddInput ) + */ + + PL_LOCK_IF( !b_locked ); + + /* Fast track the media library, no time to loose */ + if( p_item == p_playlist->p_ml_category ) { + PL_UNLOCK_IF( !b_locked ); + return p_item; + } + + /** \todo First look if we don't already have it */ + p_item_in_category = playlist_ItemFindFromInputAndRoot( + p_playlist, p_item->p_input->i_id, + p_playlist->p_root_category, + true ); + + if( p_item_in_category ) + { + playlist_item_t *p_item_in_one = playlist_ItemFindFromInputAndRoot( + p_playlist, p_item->p_input->i_id, + p_playlist->p_root_onelevel, + true ); + assert( p_item_in_one ); + + /* We already have it, and there is nothing more to do */ + ChangeToNode( p_playlist, p_item_in_category ); + + /* Item in one is a root, change it to node */ + if( p_item_in_one->p_parent == p_playlist->p_root_onelevel ) + ChangeToNode( p_playlist, p_item_in_one ); + else + { + playlist_item_t *p_status_item = get_current_status_item( p_playlist ); + playlist_item_t *p_status_node = get_current_status_node( p_playlist ); + if( p_item_in_one == p_status_item ) + { + /* We're deleting the current playlist item. Update + * the playlist object to point at the previous item + * so the playlist won't be restarted */ + playlist_item_t *p_prev_status_item = NULL; + int i = 0; + while( i < p_status_node->i_children && + p_status_node->pp_children[i] != p_status_item ) + { + p_prev_status_item = p_status_node->pp_children[i]; + i++; + } + if( i == p_status_node->i_children ) + p_prev_status_item = NULL; + if( p_prev_status_item ) + set_current_status_item( p_playlist, p_prev_status_item ); + } + DeleteFromInput( p_playlist, p_item_in_one->p_input->i_id, + p_playlist->p_root_onelevel, false ); + } + p_playlist->b_reset_currently_playing = true; + vlc_object_signal_unlocked( p_playlist ); + var_SetInteger( p_playlist, "item-change", p_item_in_category-> + p_input->i_id ); + PL_UNLOCK_IF( !b_locked ); + return p_item_in_category; + } + else + { + ChangeToNode( p_playlist, p_item ); + PL_UNLOCK_IF( !b_locked ); + return p_item; + } +} + +/** + * Find an item within a root, given its input id. + * + * \param p_playlist the playlist object + * \param i_input_id id of the input + * \param p_root root playlist item + * \param b_items_only TRUE if we want the item himself + * \return the first found item, or NULL if not found + */ +playlist_item_t *playlist_ItemFindFromInputAndRoot( playlist_t *p_playlist, + int i_input_id, + playlist_item_t *p_root, + bool b_items_only ) +{ + int i; + for( i = 0 ; i< p_root->i_children ; i++ ) + { + if( ( b_items_only ? p_root->pp_children[i]->i_children == -1 : 1 ) && + p_root->pp_children[i]->p_input->i_id == i_input_id ) + { + return p_root->pp_children[i]; + } + else if( p_root->pp_children[i]->i_children >= 0 ) + { + playlist_item_t *p_search = + playlist_ItemFindFromInputAndRoot( p_playlist, i_input_id, + p_root->pp_children[i], + b_items_only ); + if( p_search ) return p_search; + } + } + return NULL; +} + + +static int TreeMove( playlist_t *p_playlist, playlist_item_t *p_item, + playlist_item_t *p_node, int i_newpos ) +{ + int j; + playlist_item_t *p_detach = p_item->p_parent; + (void)p_playlist; + + if( p_node->i_children == -1 ) return VLC_EGENERIC; + + for( j = 0; j < p_detach->i_children; j++ ) + { + if( p_detach->pp_children[j] == p_item ) break; + } + REMOVE_ELEM( p_detach->pp_children, p_detach->i_children, j ); + + /* If j < i_newpos, we are moving the element from the top to the + * down of the playlist. So when removing the element we change have + * to change the position as we loose one element + */ + if( j < i_newpos ) + i_newpos--; + + /* Attach to new parent */ + INSERT_ELEM( p_node->pp_children, p_node->i_children, i_newpos, p_item ); + p_item->p_parent = p_node; + + return VLC_SUCCESS; +} + +/** + * Moves an item + * + * This function must be entered with the playlist lock + * + * \param p_playlist the playlist + * \param p_item the item to move + * \param p_node the new parent of the item + * \param i_newpos the new position under this new parent + * \return VLC_SUCCESS or an error + */ +int playlist_TreeMove( playlist_t * p_playlist, playlist_item_t *p_item, + playlist_item_t *p_node, int i_newpos ) +{ + int i_ret; + PL_ASSERT_LOCKED; + + /* Drop on a top level node. Move in the two trees */ + if( p_node->p_parent == p_playlist->p_root_category || + p_node->p_parent == p_playlist->p_root_onelevel ) + { + /* Fixme: avoid useless lookups but we need some clean helpers */ + { + /* Fixme: if we try to move a node on a top-level node, it will + * fail because the node doesn't exist in onelevel and we will + * do some shit in onelevel. We should recursively move all items + * within the node */ + playlist_item_t *p_node_onelevel; + playlist_item_t *p_item_onelevel; + p_node_onelevel = playlist_ItemFindFromInputAndRoot( p_playlist, + p_node->p_input->i_id, + p_playlist->p_root_onelevel, + false ); + p_item_onelevel = playlist_ItemFindFromInputAndRoot( p_playlist, + p_item->p_input->i_id, + p_playlist->p_root_onelevel, + false ); + if( p_node_onelevel && p_item_onelevel ) + TreeMove( p_playlist, p_item_onelevel, p_node_onelevel, i_newpos ); + } + { + playlist_item_t *p_node_category; + playlist_item_t *p_item_category; + p_node_category = playlist_ItemFindFromInputAndRoot( p_playlist, + p_node->p_input->i_id, + p_playlist->p_root_category, + false ); + p_item_category = playlist_ItemFindFromInputAndRoot( p_playlist, + p_item->p_input->i_id, + p_playlist->p_root_category, + false ); + if( p_node_category && p_item_category ) + TreeMove( p_playlist, p_item_category, p_node_category, 0 ); + } + i_ret = VLC_SUCCESS; + } + else + i_ret = TreeMove( p_playlist, p_item, p_node, i_newpos ); + p_playlist->b_reset_currently_playing = true; + vlc_object_signal_unlocked( p_playlist ); + return i_ret; +} + +/** + * Send a notification that an item has been added to a node + * + * \param p_playlist the playlist object + * \param i_item_id id of the item added + * \param i_node_id id of the node in wich the item was added + * \param b_signal TRUE if the function must send a signal + * \return nothing + */ +void playlist_SendAddNotify( playlist_t *p_playlist, int i_item_id, + int i_node_id, bool b_signal ) +{ + vlc_value_t val; + PL_ASSERT_LOCKED; + + playlist_add_t *p_add = (playlist_add_t *)malloc( sizeof( playlist_add_t) ); + if( !p_add ) + return; + + p_add->i_item = i_item_id; + p_add->i_node = i_node_id; + val.p_address = p_add; + p_playlist->b_reset_currently_playing = true; + if( b_signal ) + vlc_object_signal_unlocked( p_playlist ); + + var_Set( p_playlist, "item-append", val ); + free( p_add ); +} + +/***************************************************************************** + * Playlist item accessors + *****************************************************************************/ + +/** + * Set the name of a playlist item + * + * \param p_item the item + * \param psz_name the name + * \return VLC_SUCCESS or VLC_EGENERIC + */ +int playlist_ItemSetName( playlist_item_t *p_item, const char *psz_name ) +{ + if( psz_name && p_item ) + { + input_item_SetName( p_item->p_input, psz_name ); + return VLC_SUCCESS; + } + return VLC_EGENERIC; +} + +/*************************************************************************** + * The following functions are local + ***************************************************************************/ + +/* Enqueue an item for preparsing, and play it, if needed */ +static void GoAndPreparse( playlist_t *p_playlist, int i_mode, + playlist_item_t *p_item_cat, + playlist_item_t *p_item_one ) +{ + PL_ASSERT_LOCKED; + if( (i_mode & PLAYLIST_GO ) ) + { + playlist_item_t *p_parent = p_item_one; + playlist_item_t *p_toplay = NULL; + while( p_parent ) + { + if( p_parent == p_playlist->p_root_category ) + { + p_toplay = p_item_cat; break; + } + else if( p_parent == p_playlist->p_root_onelevel ) + { + p_toplay = p_item_one; break; + } + p_parent = p_parent->p_parent; + } + assert( p_toplay ); + p_playlist->request.b_request = true; + p_playlist->request.i_skip = 0; + p_playlist->request.p_item = p_toplay; + if( p_playlist->p_input ) + input_StopThread( p_playlist->p_input ); + p_playlist->request.i_status = PLAYLIST_RUNNING; + vlc_object_signal_unlocked( p_playlist ); + } + /* Preparse if PREPARSE or SPREPARSE & not enough meta */ + char *psz_artist = input_item_GetArtist( p_item_cat->p_input ); + char *psz_album = input_item_GetAlbum( p_item_cat->p_input ); + if( p_playlist->b_auto_preparse && + (i_mode & PLAYLIST_PREPARSE || + ( i_mode & PLAYLIST_SPREPARSE && + ( EMPTY_STR( psz_artist ) || ( EMPTY_STR( psz_album ) ) ) + ) ) ) + playlist_PreparseEnqueue( p_playlist, p_item_cat->p_input ); + /* If we already have it, signal it */ + else if( !EMPTY_STR( psz_artist ) && !EMPTY_STR( psz_album ) ) + input_item_SetPreparsed( p_item_cat->p_input, true ); + free( psz_artist ); + free( psz_album ); +} + +/* Add the playlist item to the requested node and fire a notification */ +static void AddItem( playlist_t *p_playlist, playlist_item_t *p_item, + playlist_item_t *p_node, int i_mode, int i_pos ) +{ + PL_ASSERT_LOCKED; + ARRAY_APPEND(p_playlist->items, p_item); + ARRAY_APPEND(p_playlist->all_items, p_item); + + if( i_pos == PLAYLIST_END ) + playlist_NodeAppend( p_playlist, p_item, p_node ); + else + playlist_NodeInsert( p_playlist, p_item, p_node, i_pos ); + + if( !p_playlist->b_doing_ml ) + playlist_SendAddNotify( p_playlist, p_item->i_id, p_node->i_id, + !( i_mode & PLAYLIST_NO_REBUILD ) ); +} + +/* Actually convert an item to a node */ +static void ChangeToNode( playlist_t *p_playlist, playlist_item_t *p_item ) +{ + int i; + if( p_item->i_children == -1 ) + p_item->i_children = 0; + + /* Remove it from the array of available items */ + ARRAY_BSEARCH( p_playlist->items,->i_id, int, p_item->i_id, i ); + if( i != -1 ) + ARRAY_REMOVE( p_playlist->items, i ); +} + +/* Do the actual removal */ +static int DeleteInner( playlist_t * p_playlist, playlist_item_t *p_item, + bool b_stop ) +{ + int i; + int i_id = p_item->i_id; + PL_ASSERT_LOCKED; + + if( p_item->i_children > -1 ) + { + return playlist_NodeDelete( p_playlist, p_item, true, false ); + } + p_playlist->b_reset_currently_playing = true; + var_SetInteger( p_playlist, "item-deleted", i_id ); + + /* Remove the item from the bank */ + ARRAY_BSEARCH( p_playlist->all_items,->i_id, int, i_id, i ); + if( i != -1 ) + ARRAY_REMOVE( p_playlist->all_items, i ); + + ARRAY_BSEARCH( p_playlist->items,->i_id, int, i_id, i ); + if( i != -1 ) + ARRAY_REMOVE( p_playlist->items, i ); + + /* Check if it is the current item */ + if( get_current_status_item( p_playlist ) == p_item ) + { + /* Hack we don't call playlist_Control for lock reasons */ + if( b_stop ) + { + p_playlist->request.i_status = PLAYLIST_STOPPED; + p_playlist->request.b_request = true; + p_playlist->request.p_item = NULL; + msg_Info( p_playlist, "stopping playback" ); + vlc_object_signal_unlocked( VLC_OBJECT(p_playlist) ); + } + } + + PL_DEBUG( "deleting item `%s'", p_item->p_input->psz_name ); + + /* Remove the item from its parent */ + playlist_NodeRemoveItem( p_playlist, p_item, p_item->p_parent ); + + playlist_ItemRelease( p_item ); + + return VLC_SUCCESS; +} diff --git a/VLC/libc.c b/VLC/libc.c new file mode 100644 index 0000000..526eb9b --- /dev/null +++ b/VLC/libc.c @@ -0,0 +1,803 @@ +/***************************************************************************** + * libc.c: Extra libc function for some systems. + ***************************************************************************** + * Copyright (C) 2002-2006 the VideoLAN team + * $Id$ + * + * Authors: Jon Lech Johansen + * Samuel Hocevar + * Gildas Bazin + * Derk-Jan Hartman + * Christophe Massiot + * Rémi Denis-Courmont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#include + + +#undef iconv_t +#undef iconv_open +#undef iconv +#undef iconv_close + +#if defined(HAVE_ICONV) +# include +#endif + +#ifdef HAVE_DIRENT_H +# include +#endif + +#ifdef HAVE_SIGNAL_H +# include +#endif + +#ifdef HAVE_FORK +# include +# include +# include +# include +# include +# include +# include +#endif + +#if defined(WIN32) || defined(UNDER_CE) +# undef _wopendir +# undef _wreaddir +# undef _wclosedir +# undef rewinddir +# define WIN32_LEAN_AND_MEAN +# include +#endif + +/****************************************************************************** + * strcasestr: find a substring (little) in another substring (big) + * Case sensitive. Return NULL if not found, return big if little == null + *****************************************************************************/ +char * vlc_strcasestr( const char *psz_big, const char *psz_little ) +{ +#if defined (HAVE_STRCASESTR) || defined (HAVE_STRISTR) + return strcasestr (psz_big, psz_little); +#else + char *p_pos = (char *)psz_big; + + if( !psz_big || !psz_little || !*psz_little ) return p_pos; + + while( *p_pos ) + { + if( toupper( *p_pos ) == toupper( *psz_little ) ) + { + char * psz_cur1 = p_pos + 1; + char * psz_cur2 = (char *)psz_little + 1; + while( *psz_cur1 && *psz_cur2 && + toupper( *psz_cur1 ) == toupper( *psz_cur2 ) ) + { + psz_cur1++; + psz_cur2++; + } + if( !*psz_cur2 ) return p_pos; + } + p_pos++; + } + return NULL; +#endif +} + +/***************************************************************************** + * strtoll: convert a string to a 64 bits int. + *****************************************************************************/ +long long vlc_strtoll( const char *nptr, char **endptr, int base ) +{ +#if defined( HAVE_STRTOLL ) + return strtoll( nptr, endptr, base ); +#else + long long i_value = 0; + int sign = 1, newbase = base ? base : 10; + + while( isspace(*nptr) ) nptr++; + + if( *nptr == '-' ) + { + sign = -1; + nptr++; + } + + /* Try to detect base */ + if( *nptr == '0' ) + { + newbase = 8; + nptr++; + + if( *nptr == 'x' ) + { + newbase = 16; + nptr++; + } + } + + if( base && newbase != base ) + { + if( endptr ) *endptr = (char *)nptr; + return i_value; + } + + switch( newbase ) + { + case 10: + while( *nptr >= '0' && *nptr <= '9' ) + { + i_value *= 10; + i_value += ( *nptr++ - '0' ); + } + if( endptr ) *endptr = (char *)nptr; + break; + + case 16: + while( (*nptr >= '0' && *nptr <= '9') || + (*nptr >= 'a' && *nptr <= 'f') || + (*nptr >= 'A' && *nptr <= 'F') ) + { + int i_valc = 0; + if(*nptr >= '0' && *nptr <= '9') i_valc = *nptr - '0'; + else if(*nptr >= 'a' && *nptr <= 'f') i_valc = *nptr - 'a' +10; + else if(*nptr >= 'A' && *nptr <= 'F') i_valc = *nptr - 'A' +10; + i_value *= 16; + i_value += i_valc; + nptr++; + } + if( endptr ) *endptr = (char *)nptr; + break; + + default: + i_value = strtol( nptr, endptr, newbase ); + break; + } + + return i_value * sign; +#endif +} + +/** + * Copy a string to a sized buffer. The result is always nul-terminated + * (contrary to strncpy()). + * + * @param dest destination buffer + * @param src string to be copied + * @param len maximum number of characters to be copied plus one for the + * terminating nul. + * + * @return strlen(src) + */ +extern size_t vlc_strlcpy (char *tgt, const char *src, size_t bufsize) +{ +#ifdef HAVE_STRLCPY + return strlcpy (tgt, src, bufsize); +#else + size_t length; + + for (length = 1; (length < bufsize) && *src; length++) + *tgt++ = *src++; + + if (bufsize) + *tgt = '\0'; + + while (*src++) + length++; + + return length - 1; +#endif +} + +/***************************************************************************** + * vlc_*dir_wrapper: wrapper under Windows to return the list of drive letters + * when called with an empty argument or just '\' + *****************************************************************************/ +#if defined(WIN32) && !defined(UNDER_CE) +# include + +typedef struct vlc_DIR +{ + _WDIR *p_real_dir; + int i_drives; + struct _wdirent dd_dir; + bool b_insert_back; +} vlc_DIR; + +void *vlc_wopendir( const wchar_t *wpath ) +{ + vlc_DIR *p_dir = NULL; + _WDIR *p_real_dir = NULL; + + if ( wpath == NULL || wpath[0] == '\0' + || (wcscmp (wpath, L"\\") == 0) ) + { + /* Special mode to list drive letters */ + p_dir = malloc( sizeof(vlc_DIR) ); + if( !p_dir ) + return NULL; + p_dir->p_real_dir = NULL; + p_dir->i_drives = GetLogicalDrives(); + return (void *)p_dir; + } + + p_real_dir = _wopendir( wpath ); + if ( p_real_dir == NULL ) + return NULL; + + p_dir = malloc( sizeof(vlc_DIR) ); + if( !p_dir ) + { + _wclosedir( p_real_dir ); + return NULL; + } + p_dir->p_real_dir = p_real_dir; + + assert (wpath[0]); // wpath[1] is defined + p_dir->b_insert_back = !wcscmp (wpath + 1, L":\\"); + return (void *)p_dir; +} + +struct _wdirent *vlc_wreaddir( void *_p_dir ) +{ + vlc_DIR *p_dir = (vlc_DIR *)_p_dir; + unsigned int i; + DWORD i_drives; + + if ( p_dir->p_real_dir != NULL ) + { + if ( p_dir->b_insert_back ) + { + /* Adds "..", gruik! */ + p_dir->dd_dir.d_ino = 0; + p_dir->dd_dir.d_reclen = 0; + p_dir->dd_dir.d_namlen = 2; + wcscpy( p_dir->dd_dir.d_name, L".." ); + p_dir->b_insert_back = false; + return &p_dir->dd_dir; + } + + return _wreaddir( p_dir->p_real_dir ); + } + + /* Drive letters mode */ + i_drives = p_dir->i_drives; + if ( !i_drives ) + return NULL; /* end */ + + for ( i = 0; i < sizeof(DWORD)*8; i++, i_drives >>= 1 ) + if ( i_drives & 1 ) break; + + if ( i >= 26 ) + return NULL; /* this should not happen */ + + swprintf( p_dir->dd_dir.d_name, L"%c:\\", 'A' + i ); + p_dir->dd_dir.d_namlen = wcslen(p_dir->dd_dir.d_name); + p_dir->i_drives &= ~(1UL << i); + return &p_dir->dd_dir; +} + +void vlc_rewinddir( void *_p_dir ) +{ + vlc_DIR *p_dir = (vlc_DIR *)_p_dir; + + if ( p_dir->p_real_dir != NULL ) + _wrewinddir( p_dir->p_real_dir ); +} +#endif + +/* This one is in the libvlccore exported symbol list */ +int vlc_wclosedir( void *_p_dir ) +{ +#if defined(WIN32) && !defined(UNDER_CE) + vlc_DIR *p_dir = (vlc_DIR *)_p_dir; + int i_ret = 0; + + if ( p_dir->p_real_dir != NULL ) + i_ret = _wclosedir( p_dir->p_real_dir ); + + free( p_dir ); + return i_ret; +#else + return closedir( _p_dir ); +#endif +} + +/** + * In-tree plugins share their gettext domain with LibVLC. + */ +char *vlc_gettext( const char *msgid ) +{ +#ifdef ENABLE_NLS + return dgettext( PACKAGE_NAME, msgid ); +#else + return (char *)msgid; +#endif +} + +/***************************************************************************** + * count_utf8_string: returns the number of characters in the string. + *****************************************************************************/ +static int count_utf8_string( const char *psz_string ) +{ + int i = 0, i_count = 0; + while( psz_string[ i ] != 0 ) + { + if( ((unsigned char *)psz_string)[ i ] < 0x80UL ) i_count++; + i++; + } + return i_count; +} + +/***************************************************************************** + * wraptext: inserts \n at convenient places to wrap the text. + * Returns the modified string in a new buffer. + *****************************************************************************/ +char *vlc_wraptext( const char *psz_text, int i_line ) +{ + int i_len; + char *psz_line, *psz_new_text; + + psz_line = psz_new_text = strdup( psz_text ); + + i_len = count_utf8_string( psz_text ); + + while( i_len > i_line ) + { + /* Look if there is a newline somewhere. */ + char *psz_parser = psz_line; + int i_count = 0; + while( i_count <= i_line && *psz_parser != '\n' ) + { + while( *((unsigned char *)psz_parser) >= 0x80UL ) psz_parser++; + psz_parser++; + i_count++; + } + if( *psz_parser == '\n' ) + { + i_len -= (i_count + 1); + psz_line = psz_parser + 1; + continue; + } + + /* Find the furthest space. */ + while( psz_parser > psz_line && *psz_parser != ' ' ) + { + while( *((unsigned char *)psz_parser) >= 0x80UL ) psz_parser--; + psz_parser--; + i_count--; + } + if( *psz_parser == ' ' ) + { + *psz_parser = '\n'; + i_len -= (i_count + 1); + psz_line = psz_parser + 1; + continue; + } + + /* Wrapping has failed. Find the first space or newline */ + while( i_count < i_len && *psz_parser != ' ' && *psz_parser != '\n' ) + { + while( *((unsigned char *)psz_parser) >= 0x80UL ) psz_parser++; + psz_parser++; + i_count++; + } + if( i_count < i_len ) *psz_parser = '\n'; + i_len -= (i_count + 1); + psz_line = psz_parser + 1; + } + + return psz_new_text; +} + +/***************************************************************************** + * iconv wrapper + *****************************************************************************/ +vlc_iconv_t vlc_iconv_open( const char *tocode, const char *fromcode ) +{ +#if defined(HAVE_ICONV) + return iconv_open( tocode, fromcode ); +#else + return (vlc_iconv_t)(-1); +#endif +} + +size_t vlc_iconv( vlc_iconv_t cd, const char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft ) +{ +#if defined(HAVE_ICONV) + return iconv( cd, (ICONV_CONST char **)inbuf, inbytesleft, + outbuf, outbytesleft ); +#else + abort (); +#endif +} + +int vlc_iconv_close( vlc_iconv_t cd ) +{ +#if defined(HAVE_ICONV) + return iconv_close( cd ); +#else + abort (); +#endif +} + +/***************************************************************************** + * reduce a fraction + * (adapted from libavcodec, author Michael Niedermayer ) + *****************************************************************************/ +bool vlc_ureduce( unsigned *pi_dst_nom, unsigned *pi_dst_den, + uint64_t i_nom, uint64_t i_den, uint64_t i_max ) +{ + bool b_exact = 1; + uint64_t i_gcd; + + if( i_den == 0 ) + { + *pi_dst_nom = 0; + *pi_dst_den = 1; + return 1; + } + + i_gcd = GCD( i_nom, i_den ); + i_nom /= i_gcd; + i_den /= i_gcd; + + if( i_max == 0 ) i_max = INT64_C(0xFFFFFFFF); + + if( i_nom > i_max || i_den > i_max ) + { + uint64_t i_a0_num = 0, i_a0_den = 1, i_a1_num = 1, i_a1_den = 0; + b_exact = 0; + + for( ; ; ) + { + uint64_t i_x = i_nom / i_den; + uint64_t i_a2n = i_x * i_a1_num + i_a0_num; + uint64_t i_a2d = i_x * i_a1_den + i_a0_den; + + if( i_a2n > i_max || i_a2d > i_max ) break; + + i_nom %= i_den; + + i_a0_num = i_a1_num; i_a0_den = i_a1_den; + i_a1_num = i_a2n; i_a1_den = i_a2d; + if( i_nom == 0 ) break; + i_x = i_nom; i_nom = i_den; i_den = i_x; + } + i_nom = i_a1_num; + i_den = i_a1_den; + } + + *pi_dst_nom = i_nom; + *pi_dst_den = i_den; + + return b_exact; +} + +/************************************************************************* + * vlc_execve: Execute an external program with a given environment, + * wait until it finishes and return its standard output + *************************************************************************/ +int __vlc_execve( vlc_object_t *p_object, int i_argc, char *const *ppsz_argv, + char *const *ppsz_env, const char *psz_cwd, + const char *p_in, size_t i_in, + char **pp_data, size_t *pi_data ) +{ + (void)i_argc; // <-- hmph +#ifdef HAVE_FORK +# define BUFSIZE 1024 + int fds[2], i_status; + + if (socketpair (AF_LOCAL, SOCK_STREAM, 0, fds)) + return -1; + + pid_t pid = -1; + if ((fds[0] > 2) && (fds[1] > 2)) + pid = fork (); + + switch (pid) + { + case -1: + msg_Err (p_object, "unable to fork (%m)"); + close (fds[0]); + close (fds[1]); + return -1; + + case 0: + { + sigset_t set; + sigemptyset (&set); + pthread_sigmask (SIG_SETMASK, &set, NULL); + + /* NOTE: + * Like it or not, close can fail (and not only with EBADF) + */ + if ((close (0) == 0) && (close (1) == 0) && (close (2) == 0) + && (dup (fds[1]) == 0) && (dup (fds[1]) == 1) + && (open ("/dev/null", O_RDONLY) == 2) + && ((psz_cwd == NULL) || (chdir (psz_cwd) == 0))) + execve (ppsz_argv[0], ppsz_argv, ppsz_env); + + exit (EXIT_FAILURE); + } + } + + close (fds[1]); + + *pi_data = 0; + if (*pp_data) + free (*pp_data); + *pp_data = NULL; + + if (i_in == 0) + shutdown (fds[0], SHUT_WR); + + while (!p_object->b_die) + { + struct pollfd ufd[1]; + memset (ufd, 0, sizeof (ufd)); + ufd[0].fd = fds[0]; + ufd[0].events = POLLIN; + + if (i_in > 0) + ufd[0].events |= POLLOUT; + + if (poll (ufd, 1, 10) <= 0) + continue; + + if (ufd[0].revents & ~POLLOUT) + { + char *ptr = realloc (*pp_data, *pi_data + BUFSIZE + 1); + if (ptr == NULL) + break; /* safely abort */ + + *pp_data = ptr; + + ssize_t val = read (fds[0], ptr + *pi_data, BUFSIZE); + switch (val) + { + case -1: + case 0: + shutdown (fds[0], SHUT_RD); + break; + + default: + *pi_data += val; + } + } + + if (ufd[0].revents & POLLOUT) + { + ssize_t val = write (fds[0], p_in, i_in); + switch (val) + { + case -1: + case 0: + i_in = 0; + shutdown (fds[0], SHUT_WR); + break; + + default: + i_in -= val; + p_in += val; + } + } + } + + close (fds[0]); + + while (waitpid (pid, &i_status, 0) == -1); + + if (WIFEXITED (i_status)) + { + i_status = WEXITSTATUS (i_status); + if (i_status) + msg_Warn (p_object, "child %s (PID %d) exited with error code %d", + ppsz_argv[0], (int)pid, i_status); + } + else + if (WIFSIGNALED (i_status)) // <-- this should be redumdant a check + { + i_status = WTERMSIG (i_status); + msg_Warn (p_object, "child %s (PID %d) exited on signal %d (%s)", + ppsz_argv[0], (int)pid, i_status, strsignal (i_status)); + } + +#elif defined( WIN32 ) && !defined( UNDER_CE ) + SECURITY_ATTRIBUTES saAttr; + PROCESS_INFORMATION piProcInfo; + STARTUPINFO siStartInfo; + BOOL bFuncRetn = FALSE; + HANDLE hChildStdinRd, hChildStdinWr, hChildStdoutRd, hChildStdoutWr; + DWORD i_status; + char *psz_cmd = NULL, *p_env = NULL, *p = NULL; + char **ppsz_parser; + int i_size; + + /* Set the bInheritHandle flag so pipe handles are inherited. */ + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; + saAttr.lpSecurityDescriptor = NULL; + + /* Create a pipe for the child process's STDOUT. */ + if ( !CreatePipe( &hChildStdoutRd, &hChildStdoutWr, &saAttr, 0 ) ) + { + msg_Err( p_object, "stdout pipe creation failed" ); + return -1; + } + + /* Ensure the read handle to the pipe for STDOUT is not inherited. */ + SetHandleInformation( hChildStdoutRd, HANDLE_FLAG_INHERIT, 0 ); + + /* Create a pipe for the child process's STDIN. */ + if ( !CreatePipe( &hChildStdinRd, &hChildStdinWr, &saAttr, 0 ) ) + { + msg_Err( p_object, "stdin pipe creation failed" ); + return -1; + } + + /* Ensure the write handle to the pipe for STDIN is not inherited. */ + SetHandleInformation( hChildStdinWr, HANDLE_FLAG_INHERIT, 0 ); + + /* Set up members of the PROCESS_INFORMATION structure. */ + ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) ); + + /* Set up members of the STARTUPINFO structure. */ + ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) ); + siStartInfo.cb = sizeof(STARTUPINFO); + siStartInfo.hStdError = hChildStdoutWr; + siStartInfo.hStdOutput = hChildStdoutWr; + siStartInfo.hStdInput = hChildStdinRd; + siStartInfo.wShowWindow = SW_HIDE; + siStartInfo.dwFlags |= STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + + /* Set up the command line. */ + psz_cmd = malloc(32768); + if( !psz_cmd ) + return -1; + psz_cmd[0] = '\0'; + i_size = 32768; + ppsz_parser = &ppsz_argv[0]; + while ( ppsz_parser[0] != NULL && i_size > 0 ) + { + /* Protect the last argument with quotes ; the other arguments + * are supposed to be already protected because they have been + * passed as a command-line option. */ + if ( ppsz_parser[1] == NULL ) + { + strncat( psz_cmd, "\"", i_size ); + i_size--; + } + strncat( psz_cmd, *ppsz_parser, i_size ); + i_size -= strlen( *ppsz_parser ); + if ( ppsz_parser[1] == NULL ) + { + strncat( psz_cmd, "\"", i_size ); + i_size--; + } + strncat( psz_cmd, " ", i_size ); + i_size--; + ppsz_parser++; + } + + /* Set up the environment. */ + p = p_env = malloc(32768); + if( !p ) + { + free( psz_cmd ); + return -1; + } + + i_size = 32768; + ppsz_parser = &ppsz_env[0]; + while ( *ppsz_parser != NULL && i_size > 0 ) + { + memcpy( p, *ppsz_parser, + __MIN((int)(strlen(*ppsz_parser) + 1), i_size) ); + p += strlen(*ppsz_parser) + 1; + i_size -= strlen(*ppsz_parser) + 1; + ppsz_parser++; + } + *p = '\0'; + + /* Create the child process. */ + bFuncRetn = CreateProcess( NULL, + psz_cmd, // command line + NULL, // process security attributes + NULL, // primary thread security attributes + TRUE, // handles are inherited + 0, // creation flags + p_env, + psz_cwd, + &siStartInfo, // STARTUPINFO pointer + &piProcInfo ); // receives PROCESS_INFORMATION + + free( psz_cmd ); + free( p_env ); + + if ( bFuncRetn == 0 ) + { + msg_Err( p_object, "child creation failed" ); + return -1; + } + + /* Read from a file and write its contents to a pipe. */ + while ( i_in > 0 && !p_object->b_die ) + { + DWORD i_written; + if ( !WriteFile( hChildStdinWr, p_in, i_in, &i_written, NULL ) ) + break; + i_in -= i_written; + p_in += i_written; + } + + /* Close the pipe handle so the child process stops reading. */ + CloseHandle(hChildStdinWr); + + /* Close the write end of the pipe before reading from the + * read end of the pipe. */ + CloseHandle(hChildStdoutWr); + + /* Read output from the child process. */ + *pi_data = 0; + if( *pp_data ) + free( pp_data ); + *pp_data = NULL; + *pp_data = malloc( 1025 ); /* +1 for \0 */ + + while ( !p_object->b_die ) + { + DWORD i_read; + if ( !ReadFile( hChildStdoutRd, &(*pp_data)[*pi_data], 1024, &i_read, + NULL ) + || i_read == 0 ) + break; + *pi_data += i_read; + *pp_data = realloc( *pp_data, *pi_data + 1025 ); + } + + while ( !p_object->b_die + && !GetExitCodeProcess( piProcInfo.hProcess, &i_status ) + && i_status != STILL_ACTIVE ) + msleep( 10000 ); + + CloseHandle(piProcInfo.hProcess); + CloseHandle(piProcInfo.hThread); + + if ( i_status ) + msg_Warn( p_object, + "child %s returned with error code %ld", + ppsz_argv[0], i_status ); + +#else + msg_Err( p_object, "vlc_execve called but no implementation is available" ); + return -1; + +#endif + + if (*pp_data == NULL) + return -1; + + (*pp_data)[*pi_data] = '\0'; + return 0; +} diff --git a/VLC/libvlc-module.c b/VLC/libvlc-module.c new file mode 100644 index 0000000..c39e2f6 --- /dev/null +++ b/VLC/libvlc-module.c @@ -0,0 +1,2667 @@ +/***************************************************************************** + * libvlc.h: Options for the main (libvlc itself) module + ***************************************************************************** + * Copyright (C) 1998-2006 the VideoLAN team + * $Id$ + * + * Authors: Vincent Seguin + * Samuel Hocevar + * Gildas Bazin + * Jean-Paul Saman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +// Pretend we are a builtin module +#define MODULE_NAME vlcmain +#define MODULE_PATH vlcmain +#define __BUILTIN__ + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "vlc_plugin.h" +#include "libvlc.h" + +//#define Nothing here, this is just to prevent update-po from being stupid +#include "vlc_keys.h" +#include "vlc_meta.h" + +#if defined (WIN32) || defined (__APPLE__) +static const char *const ppsz_language[] = +{ + "auto", + "en", + "ar", + "pt_BR", + "en_GB", + "bg", + "ca", + "zh_TW", + "cs", + "da", + "nl", + "fi", + "fr", + "gl", + "ka", + "de", + "he", + "hu", + "it", + "ja", + "ko", + "ms", + "oc", + "fa", + "pl", + "pt_PT", + "pa", + "ro", + "ru", + "zh_CN", + "sr", + "sk", + "sl", + "es", + "sv", + "tr" +}; + +static const char *const ppsz_language_text[] = +{ + N_("Auto"), + N_("American English"), + N_("Arabic"), + N_("Brazilian Portuguese"), + N_("British English"), + N_("Bulgarian"), + N_("Catalan"), + N_("Chinese Traditional"), + N_("Czech"), + N_("Danish"), + N_("Dutch"), + N_("Finnish"), + N_("French"), + N_("Galician"), + N_("Georgian"), + N_("German"), + N_("Hebrew"), + N_("Hungarian"), + N_("Italian"), + N_("Japanese"), + N_("Korean"), + N_("Malay"), + N_("Occitan"), + N_("Persian"), + N_("Polish"), + N_("Portuguese"), + N_("Punjabi"), + N_("Romanian"), + N_("Russian"), + N_("Simplified Chinese"), + N_("Serbian"), + N_("Slovak"), + N_("Slovenian"), + N_("Spanish"), + N_("Swedish"), + N_("Turkish") +}; +#endif + +static const char *const ppsz_snap_formats[] = +{ "png", "jpg" }; + +/***************************************************************************** + * Configuration options for the main program. Each module will also separatly + * define its own configuration options. + * Look into configuration.h if you need to know more about the following + * macros. + *****************************************************************************/ + +/***************************************************************************** + * Intf + ****************************************************************************/ + +// DEPRECATED +#define INTF_CAT_LONGTEXT N_( \ + "These options allow you to configure the interfaces used by VLC. " \ + "You can select the main interface, additional " \ + "interface modules, and define various related options." ) + +#define INTF_TEXT N_("Interface module") +#define INTF_LONGTEXT N_( \ + "This is the main interface used by VLC. " \ + "The default behavior is to automatically select the best module " \ + "available.") + +#define EXTRAINTF_TEXT N_("Extra interface modules") +#define EXTRAINTF_LONGTEXT N_( \ + "You can select \"additional interfaces\" for VLC. " \ + "They will be launched in the background in addition to the default " \ + "interface. Use a comma separated list of interface modules. (common " \ + "values are \"rc\" (remote control), \"http\", \"gestures\" ...)") + +#define CONTROL_TEXT N_("Control interfaces") +#define CONTROL_LONGTEXT N_( \ + "You can select control interfaces for VLC.") + +#define VERBOSE_TEXT N_("Verbosity (0,1,2)") +#define VERBOSE_LONGTEXT N_( \ + "This is the verbosity level (0=only errors and " \ + "standard messages, 1=warnings, 2=debug).") + +#define QUIET_TEXT N_("Be quiet") +#define QUIET_LONGTEXT N_( \ + "Turn off all warning and information messages.") + +#define OPEN_TEXT N_("Default stream") +#define OPEN_LONGTEXT N_( \ + "This stream will always be opened at VLC startup." ) + +#define LANGUAGE_TEXT N_("Language") +#define LANGUAGE_LONGTEXT N_( "You can manually select a language for the " \ + "interface. The system language is auto-detected if \"auto\" is " \ + "specified here." ) + +#define COLOR_TEXT N_("Color messages") +#define COLOR_LONGTEXT N_( \ + "This enables colorization of the messages sent to the console " \ + "Your terminal needs Linux color support for this to work.") + +#define ADVANCED_TEXT N_("Show advanced options") +#define ADVANCED_LONGTEXT N_( \ + "When this is enabled, the preferences and/or interfaces will " \ + "show all available options, including those that most users should " \ + "never touch.") + +#define SHOWINTF_TEXT N_("Show interface with mouse") +#define SHOWINTF_LONGTEXT N_( \ + "When this is enabled, the interface is shown when you move the mouse to "\ + "the edge of the screen in fullscreen mode." ) + +#define INTERACTION_TEXT N_("Interface interaction") +#define INTERACTION_LONGTEXT N_( \ + "When this is enabled, the interface will show a dialog box each time " \ + "some user input is required." ) + + +/***************************************************************************** + * Audio + ****************************************************************************/ + +// DEPRECATED +#define AOUT_CAT_LONGTEXT N_( \ + "These options allow you to modify the behavior of the audio " \ + "subsystem, and to add audio filters which can be used for " \ + "post processing or visual effects (spectrum analyzer, etc.). " \ + "Enable these filters here, and configure them in the \"audio filters\" " \ + "modules section.") + +#define AOUT_TEXT N_("Audio output module") +#define AOUT_LONGTEXT N_( \ + "This is the audio output method used by VLC. " \ + "The default behavior is to automatically select the best method " \ + "available.") + +#define AUDIO_TEXT N_("Enable audio") +#define AUDIO_LONGTEXT N_( \ + "You can completely disable the audio output. The audio " \ + "decoding stage will not take place, thus saving some processing power.") + +#if 0 +#define MONO_TEXT N_("Force mono audio") +#define MONO_LONGTEXT N_("This will force a mono audio output.") +#endif + +#define VOLUME_TEXT N_("Default audio volume") +#define VOLUME_LONGTEXT N_( \ + "You can set the default audio output volume here, in a range from 0 to " \ + "1024.") + +#define VOLUME_SAVE_TEXT N_("Audio output saved volume") +#define VOLUME_SAVE_LONGTEXT N_( \ + "This saves the audio output volume when you use the mute function. " \ + "You should not change this option manually.") + +#define VOLUME_STEP_TEXT N_("Audio output volume step") +#define VOLUME_STEP_LONGTEXT N_( \ + "The step size of the volume is adjustable using this option, " \ + "in a range from 0 to 1024." ) + +#define AOUT_RATE_TEXT N_("Audio output frequency (Hz)") +#define AOUT_RATE_LONGTEXT N_( \ + "You can force the audio output frequency here. Common values are " \ + "-1 (default), 48000, 44100, 32000, 22050, 16000, 11025, 8000.") + +#if !defined( __APPLE__ ) +#define AOUT_RESAMP_TEXT N_("High quality audio resampling") +#define AOUT_RESAMP_LONGTEXT N_( \ + "This uses a high quality audio resampling algorithm. High quality "\ + "audio resampling can be processor intensive so you can " \ + "disable it and a cheaper resampling algorithm will be used instead.") +#endif + +#define DESYNC_TEXT N_("Audio desynchronization compensation") +#define DESYNC_LONGTEXT N_( \ + "This delays the audio output. The delay must be given in milliseconds." \ + "This can be handy if you notice a lag between the video and the audio.") + +#define MULTICHA_TEXT N_("Audio output channels mode") +#define MULTICHA_LONGTEXT N_( \ + "This sets the audio output channels mode that will " \ + "be used by default when possible (ie. if your hardware supports it as " \ + "well as the audio stream being played).") + +#define SPDIF_TEXT N_("Use S/PDIF when available") +#define SPDIF_LONGTEXT N_( \ + "S/PDIF can be used by default when " \ + "your hardware supports it as well as the audio stream being played.") + +#define FORCE_DOLBY_TEXT N_("Force detection of Dolby Surround") +#define FORCE_DOLBY_LONGTEXT N_( \ + "Use this when you know your stream is (or is not) encoded with Dolby "\ + "Surround but fails to be detected as such. Even if the stream is "\ + "not actually encoded with Dolby Surround, turning on this option might "\ + "enhance your experience, especially when combined with the Headphone "\ + "Channel Mixer." ) +static const int pi_force_dolby_values[] = { 0, 1, 2 }; +static const char *const ppsz_force_dolby_descriptions[] = { + N_("Auto"), N_("On"), N_("Off") }; + + +#define AUDIO_FILTER_TEXT N_("Audio filters") +#define AUDIO_FILTER_LONGTEXT N_( \ + "This adds audio post processing filters, to modify " \ + "the sound rendering." ) + +#define AUDIO_VISUAL_TEXT N_("Audio visualizations ") +#define AUDIO_VISUAL_LONGTEXT N_( \ + "This adds visualization modules (spectrum analyzer, etc.).") + + +#define AUDIO_REPLAY_GAIN_MODE_TEXT N_( \ + "Replay gain mode" ) +#define AUDIO_REPLAY_GAIN_MODE_LONGTEXT N_( \ + "Select the replay gain mode" ) +#define AUDIO_REPLAY_GAIN_PREAMP_TEXT N_( \ + "Replay preamp" ) +#define AUDIO_REPLAY_GAIN_PREAMP_LONGTEXT N_( \ + "This allows you to change the default target level (89 dB) " \ + "for stream with replay gain information" ) +#define AUDIO_REPLAY_GAIN_DEFAULT_TEXT N_( \ + "Default replay gain" ) +#define AUDIO_REPLAY_GAIN_DEFAULT_LONGTEXT N_( \ + "This is the gain used for stream without replay gain information" ) +#define AUDIO_REPLAY_GAIN_PEAK_PROTECTION_TEXT N_( \ + "Peak protection" ) +#define AUDIO_REPLAY_GAIN_PEAK_PROTECTION_LONGTEXT N_( \ + "Protect against sound clipping" ) + +static const char *const ppsz_replay_gain_mode[] = { + "none", "track", "album" }; +static const char *const ppsz_replay_gain_mode_text[] = { + N_("None"), N_("Track"), N_("Album") }; + +/***************************************************************************** + * Video + ****************************************************************************/ + +// DEPRECATED +#define VOUT_CAT_LONGTEXT N_( \ + "These options allow you to modify the behavior of the video output " \ + "subsystem. You can for example enable video filters (deinterlacing, " \ + "image adjusting, etc.). Enable these filters here and configure " \ + "them in the \"video filters\" modules section. You can also set many " \ + "miscellaneous video options." ) + +#define VOUT_TEXT N_("Video output module") +#define VOUT_LONGTEXT N_( \ + "This is the the video output method used by VLC. " \ + "The default behavior is to automatically select the best method available.") + +#define VIDEO_TEXT N_("Enable video") +#define VIDEO_LONGTEXT N_( \ + "You can completely disable the video output. The video " \ + "decoding stage will not take place, thus saving some processing power.") + +#define WIDTH_TEXT N_("Video width") +#define WIDTH_LONGTEXT N_( \ + "You can enforce the video width. By default (-1) VLC will " \ + "adapt to the video characteristics.") + +#define HEIGHT_TEXT N_("Video height") +#define HEIGHT_LONGTEXT N_( \ + "You can enforce the video height. By default (-1) VLC will " \ + "adapt to the video characteristics.") + +#define VIDEOX_TEXT N_("Video X coordinate") +#define VIDEOX_LONGTEXT N_( \ + "You can enforce the position of the top left corner of the video window "\ + "(X coordinate).") + +#define VIDEOY_TEXT N_("Video Y coordinate") +#define VIDEOY_LONGTEXT N_( \ + "You can enforce the position of the top left corner of the video window "\ + "(Y coordinate).") + +#define VIDEO_TITLE_TEXT N_("Video title") +#define VIDEO_TITLE_LONGTEXT N_( \ + "Custom title for the video window (in case the video is not embedded in "\ + "the interface).") + +#define ALIGN_TEXT N_("Video alignment") +#define ALIGN_LONGTEXT N_( \ + "Enforce the alignment of the video in its window. By default (0) it " \ + "will be centered (0=center, 1=left, 2=right, 4=top, 8=bottom, you can " \ + "also use combinations of these values, like 6=4+2 meaning top-right).") +static const int pi_align_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 }; +static const char *const ppsz_align_descriptions[] = +{ N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"), + N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") }; + +#define ZOOM_TEXT N_("Zoom video") +#define ZOOM_LONGTEXT N_( \ + "You can zoom the video by the specified factor.") + +#define GRAYSCALE_TEXT N_("Grayscale video output") +#define GRAYSCALE_LONGTEXT N_( \ + "Output video in grayscale. As the color information aren't decoded, " \ + "this can save some processing power." ) + +#define EMBEDDED_TEXT N_("Embedded video") +#define EMBEDDED_LONGTEXT N_( \ + "Embed the video output in the main interface." ) + +#define FULLSCREEN_TEXT N_("Fullscreen video output") +#define FULLSCREEN_LONGTEXT N_( \ + "Start video in fullscreen mode" ) + +#define OVERLAY_TEXT N_("Overlay video output") +#define OVERLAY_LONGTEXT N_( \ + "Overlay is the hardware acceleration capability of your video card " \ + "(ability to render video directly). VLC will try to use it by default." ) + +#define VIDEO_ON_TOP_TEXT N_("Always on top") +#define VIDEO_ON_TOP_LONGTEXT N_( \ + "Always place the video window on top of other windows." ) + +#define VIDEO_TITLE_SHOW_TEXT N_("Show media title on video") +#define VIDEO_TITLE_SHOW_LONGTEXT N_( \ + "Display the title of the video on top of the movie.") + +#define VIDEO_TITLE_TIMEOUT_TEXT N_("Show video title for x miliseconds") +#define VIDEO_TITLE_TIMEOUT_LONGTEXT N_( \ + "Show the video title for n miliseconds, default is 5000 ms (5 sec.)") + +#define VIDEO_TITLE_POSITION_TEXT N_("Position of video title") +#define VIDEO_TITLE_POSITION_LONGTEXT N_( \ + "Place on video where to display the title (default bottom center).") + +#define MOUSE_HIDE_TIMEOUT_TEXT N_("Hide cursor and fullscreen " \ + "controller after x miliseconds") +#define MOUSE_HIDE_TIMEOUT_LONGTEXT N_( \ + "Hide mouse cursor and fullscreen controller after " \ + "n miliseconds, default is 3000 ms (3 sec.)") + +static const int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 }; +static const char *const ppsz_pos_descriptions[] = +{ N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"), + N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") }; + +#define SS_TEXT N_("Disable screensaver") +#define SS_LONGTEXT N_("Disable the screensaver during video playback." ) + +#define INHIBIT_TEXT N_("Inhibit the power management daemon during playback") +#define INHIBIT_LONGTEXT N_("Inhibits the power management daemon during any " \ + "playback, to avoid the computer being suspended because of inactivity.") + +#define VIDEO_DECO_TEXT N_("Window decorations") +#define VIDEO_DECO_LONGTEXT N_( \ + "VLC can avoid creating window caption, frames, etc... around the video" \ + ", giving a \"minimal\" window.") + +#define VOUT_FILTER_TEXT N_("Video output filter module") +#define VOUT_FILTER_LONGTEXT N_( \ + "This adds video output filters like clone or wall" ) + +#define VIDEO_FILTER_TEXT N_("Video filter module") +#define VIDEO_FILTER_LONGTEXT N_( \ + "This adds post-processing filters to enhance the " \ + "picture quality, for instance deinterlacing, or distort" \ + "the video.") + +#define SNAP_PATH_TEXT N_("Video snapshot directory (or filename)") +#define SNAP_PATH_LONGTEXT N_( \ + "Directory where the video snapshots will be stored.") + +#define SNAP_PREFIX_TEXT N_("Video snapshot file prefix") +#define SNAP_PREFIX_LONGTEXT N_( \ + "Video snapshot file prefix" ) + +#define SNAP_FORMAT_TEXT N_("Video snapshot format") +#define SNAP_FORMAT_LONGTEXT N_( \ + "Image format which will be used to store the video snapshots" ) + +#define SNAP_PREVIEW_TEXT N_("Display video snapshot preview") +#define SNAP_PREVIEW_LONGTEXT N_( \ + "Display the snapshot preview in the screen's top-left corner.") + +#define SNAP_SEQUENTIAL_TEXT N_("Use sequential numbers instead of timestamps") +#define SNAP_SEQUENTIAL_LONGTEXT N_( \ + "Use sequential numbers instead of timestamps for snapshot numbering") + +#define SNAP_WIDTH_TEXT N_("Video snapshot width") +#define SNAP_WIDTH_LONGTEXT N_( \ + "You can enforce the width of the video snapshot. By default " \ + "it will keep the original width (-1). Using 0 will scale the width " \ + "to keep the aspect ratio." ) + +#define SNAP_HEIGHT_TEXT N_("Video snapshot height") +#define SNAP_HEIGHT_LONGTEXT N_( \ + "You can enforce the height of the video snapshot. By default " \ + "it will keep the original height (-1). Using 0 will scale the height " \ + "to keep the aspect ratio." ) + +#define CROP_TEXT N_("Video cropping") +#define CROP_LONGTEXT N_( \ + "This forces the cropping of the source video. " \ + "Accepted formats are x:y (4:3, 16:9, etc.) expressing the global image " \ + "aspect.") + +#define ASPECT_RATIO_TEXT N_("Source aspect ratio") +#define ASPECT_RATIO_LONGTEXT N_( \ + "This forces the source aspect ratio. For instance, some DVDs claim " \ + "to be 16:9 while they are actually 4:3. This can also be used as a " \ + "hint for VLC when a movie does not have aspect ratio information. " \ + "Accepted formats are x:y (4:3, 16:9, etc.) expressing the global image " \ + "aspect, or a float value (1.25, 1.3333, etc.) expressing pixel " \ + "squareness.") + +#define CUSTOM_CROP_RATIOS_TEXT N_("Custom crop ratios list") +#define CUSTOM_CROP_RATIOS_LONGTEXT N_( \ + "Comma seperated list of crop ratios which will be added in the " \ + "interface's crop ratios list.") + +#define CUSTOM_ASPECT_RATIOS_TEXT N_("Custom aspect ratios list") +#define CUSTOM_ASPECT_RATIOS_LONGTEXT N_( \ + "Comma seperated list of aspect ratios which will be added in the " \ + "interface's aspect ratio list.") + +#define HDTV_FIX_TEXT N_("Fix HDTV height") +#define HDTV_FIX_LONGTEXT N_( \ + "This allows proper handling of HDTV-1080 video format " \ + "even if broken encoder incorrectly sets height to 1088 lines. " \ + "You should only disable this option if your video has a " \ + "non-standard format requiring all 1088 lines.") + +#define MASPECT_RATIO_TEXT N_("Monitor pixel aspect ratio") +#define MASPECT_RATIO_LONGTEXT N_( \ + "This forces the monitor aspect ratio. Most monitors have square " \ + "pixels (1:1). If you have a 16:9 screen, you might need to change this " \ + "to 4:3 in order to keep proportions.") + +#define SKIP_FRAMES_TEXT N_("Skip frames") +#define SKIP_FRAMES_LONGTEXT N_( \ + "Enables framedropping on MPEG2 stream. Framedropping " \ + "occurs when your computer is not powerful enough" ) + +#define DROP_LATE_FRAMES_TEXT N_("Drop late frames") +#define DROP_LATE_FRAMES_LONGTEXT N_( \ + "This drops frames that are late (arrive to the video output after " \ + "their intended display date)." ) + +#define QUIET_SYNCHRO_TEXT N_("Quiet synchro") +#define QUIET_SYNCHRO_LONGTEXT N_( \ + "This avoids flooding the message log with debug output from the " \ + "video output synchronization mechanism.") + +/***************************************************************************** + * Input + ****************************************************************************/ + +// Deprecated +#define INPUT_CAT_LONGTEXT N_( \ + "These options allow you to modify the behavior of the input " \ + "subsystem, such as the DVD or VCD device, the network interface " \ + "settings or the subtitle channel.") + +#define CR_AVERAGE_TEXT N_("Clock reference average counter") +#define CR_AVERAGE_LONGTEXT N_( \ + "When using the PVR input (or a very irregular source), you should " \ + "set this to 10000.") + +#define CLOCK_SYNCHRO_TEXT N_("Clock synchronisation") +#define CLOCK_SYNCHRO_LONGTEXT N_( \ + "It is possible to disable the input clock synchronisation for " \ + "real-time sources. Use this if you experience jerky playback of " \ + "network streams.") + +#define NETSYNC_TEXT N_("Network synchronisation" ) +#define NETSYNC_LONGTEXT N_( "This allows you to remotely " \ + "synchronise clocks for server and client. The detailed settings " \ + "are available in Advanced / Network Sync." ) + +static const int pi_clock_values[] = { -1, 0, 1 }; +static const char *const ppsz_clock_descriptions[] = +{ N_("Default"), N_("Disable"), N_("Enable") }; + +#define SERVER_PORT_TEXT N_("UDP port") +#define SERVER_PORT_LONGTEXT N_( \ + "This is the default port used for UDP streams. Default is 1234." ) + +#define MTU_TEXT N_("MTU of the network interface") +#define MTU_LONGTEXT N_( \ + "This is the maximum application-layer packet size that can be " \ + "transmitted over the network (in bytes).") +/* Should be less than 1500 - 8[ppp] - 40[ip6] - 8[udp] in any case. */ +#define MTU_DEFAULT 1400 + +#define TTL_TEXT N_("Hop limit (TTL)") +#define TTL_LONGTEXT N_( \ + "This is the hop limit (also known as \"Time-To-Live\" or TTL) of " \ + "the multicast packets sent by the stream output (-1 = use operating " \ + "system built-in default).") + +#define MIFACE_TEXT N_("Multicast output interface") +#define MIFACE_LONGTEXT N_( \ + "Default multicast interface. This overrides the routing table.") + +#define MIFACE_ADDR_TEXT N_("IPv4 multicast output interface address") +#define MIFACE_ADDR_LONGTEXT N_( \ + "IPv4 adress for the default multicast interface. This overrides " \ + "the routing table.") + +#define DSCP_TEXT N_("DiffServ Code Point") +#define DSCP_LONGTEXT N_("Differentiated Services Code Point " \ + "for outgoing UDP streams (or IPv4 Type Of Service, " \ + "or IPv6 Traffic Class). This is used for network Quality of Service.") + +#define INPUT_PROGRAM_TEXT N_("Program") +#define INPUT_PROGRAM_LONGTEXT N_( \ + "Choose the program to select by giving its Service ID. " \ + "Only use this option if you want to read a multi-program stream " \ + "(like DVB streams for example)." ) + +#define INPUT_PROGRAMS_TEXT N_("Programs") +#define INPUT_PROGRAMS_LONGTEXT N_( \ + "Choose the programs to select by giving a comma-separated list of " \ + "Service IDs (SIDs). " \ + "Only use this option if you want to read a multi-program stream " \ + "(like DVB streams for example)." ) + +/// \todo Document how to find it +#define INPUT_AUDIOTRACK_TEXT N_("Audio track") +#define INPUT_AUDIOTRACK_LONGTEXT N_( \ + "Stream number of the audio track to use " \ + "(from 0 to n).") + +#define INPUT_SUBTRACK_TEXT N_("Subtitles track") +#define INPUT_SUBTRACK_LONGTEXT N_( \ + "Stream number of the subtitle track to use " \ + "(from 0 to n).") + +#define INPUT_AUDIOTRACK_LANG_TEXT N_("Audio language") +#define INPUT_AUDIOTRACK_LANG_LONGTEXT N_( \ + "Language of the audio track you want to use " \ + "(comma separated, two or three letter country code).") + +#define INPUT_SUBTRACK_LANG_TEXT N_("Subtitle language") +#define INPUT_SUBTRACK_LANG_LONGTEXT N_( \ + "Language of the subtitle track you want to use " \ + "(comma separated, two or three letters country code).") + +/// \todo Document how to find it +#define INPUT_AUDIOTRACK_ID_TEXT N_("Audio track ID") +#define INPUT_AUDIOTRACK_ID_LONGTEXT N_( \ + "Stream ID of the audio track to use.") + +#define INPUT_SUBTRACK_ID_TEXT N_("Subtitles track ID") +#define INPUT_SUBTRACK_ID_LONGTEXT N_( \ + "Stream ID of the subtitle track to use.") + +#define INPUT_REPEAT_TEXT N_("Input repetitions") +#define INPUT_REPEAT_LONGTEXT N_( \ + "Number of time the same input will be repeated") + +#define START_TIME_TEXT N_("Start time") +#define START_TIME_LONGTEXT N_( \ + "The stream will start at this position (in seconds)." ) + +#define STOP_TIME_TEXT N_("Stop time") +#define STOP_TIME_LONGTEXT N_( \ + "The stream will stop at this position (in seconds)." ) + +#define RUN_TIME_TEXT N_("Run time") +#define RUN_TIME_LONGTEXT N_( \ + "The stream will run this duration (in seconds)." ) + +#define INPUT_LIST_TEXT N_("Input list") +#define INPUT_LIST_LONGTEXT N_( \ + "You can give a comma-separated list " \ + "of inputs that will be concatenated together after the normal one.") + +#define INPUT_SLAVE_TEXT N_("Input slave (experimental)") +#define INPUT_SLAVE_LONGTEXT N_( \ + "This allows you to play from several inputs at " \ + "the same time. This feature is experimental, not all formats " \ + "are supported. Use a '#' separated list of inputs.") + +#define BOOKMARKS_TEXT N_("Bookmarks list for a stream") +#define BOOKMARKS_LONGTEXT N_( \ + "You can manually give a list of bookmarks for a stream in " \ + "the form \"{name=bookmark-name,time=optional-time-offset," \ + "bytes=optional-byte-offset},{...}\"") + +// DEPRECATED +#define SUB_CAT_LONGTEXT N_( \ + "These options allow you to modify the behavior of the subpictures " \ + "subsystem. You can for example enable subpictures filters (logo, etc.). " \ + "Enable these filters here and configure them in the " \ + "\"subpictures filters\" modules section. You can also set many " \ + "miscellaneous subpictures options." ) + +#define SUB_MARGIN_TEXT N_("Force subtitle position") +#define SUB_MARGIN_LONGTEXT N_( \ + "You can use this option to place the subtitles under the movie, " \ + "instead of over the movie. Try several positions.") + +#define SPU_TEXT N_("Enable sub-pictures") +#define SPU_LONGTEXT N_( \ + "You can completely disable the sub-picture processing.") + +#define OSD_TEXT N_("On Screen Display") +#define OSD_LONGTEXT N_( \ + "VLC can display messages on the video. This is called OSD (On Screen " \ + "Display).") + +#define TEXTRENDERER_TEXT N_("Text rendering module") +#define TEXTRENDERER_LONGTEXT N_( \ + "VLC normally uses Freetype for rendering, but this allows you to use svg for instance.") + +#define SUB_FILTER_TEXT N_("Subpictures filter module") +#define SUB_FILTER_LONGTEXT N_( \ + "This adds so-called \"subpicture filters\". These filters overlay " \ + "some images or text over the video (like a logo, arbitrary text...)." ) + +#define SUB_AUTO_TEXT N_("Autodetect subtitle files") +#define SUB_AUTO_LONGTEXT N_( \ + "Automatically detect a subtitle file, if no subtitle filename is " \ + "specified (based on the filename of the movie).") + +#define SUB_FUZZY_TEXT N_("Subtitle autodetection fuzziness") +#define SUB_FUZZY_LONGTEXT N_( \ + "This determines how fuzzy subtitle and movie filename matching " \ + "will be. Options are:\n" \ + "0 = no subtitles autodetected\n" \ + "1 = any subtitle file\n" \ + "2 = any subtitle file containing the movie name\n" \ + "3 = subtitle file matching the movie name with additional chars\n" \ + "4 = subtitle file matching the movie name exactly") + +#define SUB_PATH_TEXT N_("Subtitle autodetection paths") +#define SUB_PATH_LONGTEXT N_( \ + "Look for a subtitle file in those paths too, if your subtitle " \ + "file was not found in the current directory.") + +#define SUB_FILE_TEXT N_("Use subtitle file") +#define SUB_FILE_LONGTEXT N_( \ + "Load this subtitle file. To be used when autodetect cannot detect " \ + "your subtitle file.") + +#define DVD_DEV_TEXT N_("DVD device") +#ifdef WIN32 +#define DVD_DEV_LONGTEXT N_( \ + "This is the default DVD drive (or file) to use. Don't forget the colon " \ + "after the drive letter (eg. D:)") +#else +#define DVD_DEV_LONGTEXT N_( \ + "This is the default DVD device to use.") +#endif + +#define VCD_DEV_TEXT N_("VCD device") +#ifdef HAVE_VCDX +#define VCD_DEV_LONGTEXT N_( \ + "This is the default VCD device to use. " \ + "If you don't specify anything, we'll scan for a suitable CD-ROM device." ) +#else +#define VCD_DEV_LONGTEXT N_( \ + "This is the default VCD device to use." ) +#endif + +#define CDAUDIO_DEV_TEXT N_("Audio CD device") +#ifdef HAVE_CDDAX +#define CDAUDIO_DEV_LONGTEXT N_( \ + "This is the default Audio CD device to use. " \ + "If you don't specify anything, we'll scan for a suitable CD-ROM device." ) +#else +#define CDAUDIO_DEV_LONGTEXT N_( \ + "This is the default Audio CD device to use." ) +#endif + +#define IPV6_TEXT N_("Force IPv6") +#define IPV6_LONGTEXT N_( \ + "IPv6 will be used by default for all connections.") + +#define IPV4_TEXT N_("Force IPv4") +#define IPV4_LONGTEXT N_( \ + "IPv4 will be used by default for all connections.") + +#define TIMEOUT_TEXT N_("TCP connection timeout") +#define TIMEOUT_LONGTEXT N_( \ + "Default TCP connection timeout (in milliseconds). " ) + +#define SOCKS_SERVER_TEXT N_("SOCKS server") +#define SOCKS_SERVER_LONGTEXT N_( \ + "SOCKS proxy server to use. This must be of the form " \ + "address:port. It will be used for all TCP connections" ) + +#define SOCKS_USER_TEXT N_("SOCKS user name") +#define SOCKS_USER_LONGTEXT N_( \ + "User name to be used for connection to the SOCKS proxy." ) + +#define SOCKS_PASS_TEXT N_("SOCKS password") +#define SOCKS_PASS_LONGTEXT N_( \ + "Password to be used for connection to the SOCKS proxy." ) + +#define META_TITLE_TEXT N_("Title metadata") +#define META_TITLE_LONGTEXT N_( \ + "Allows you to specify a \"title\" metadata for an input.") + +#define META_AUTHOR_TEXT N_("Author metadata") +#define META_AUTHOR_LONGTEXT N_( \ + "Allows you to specify an \"author\" metadata for an input.") + +#define META_ARTIST_TEXT N_("Artist metadata") +#define META_ARTIST_LONGTEXT N_( \ + "Allows you to specify an \"artist\" metadata for an input.") + +#define META_GENRE_TEXT N_("Genre metadata") +#define META_GENRE_LONGTEXT N_( \ + "Allows you to specify a \"genre\" metadata for an input.") + +#define META_CPYR_TEXT N_("Copyright metadata") +#define META_CPYR_LONGTEXT N_( \ + "Allows you to specify a \"copyright\" metadata for an input.") + +#define META_DESCR_TEXT N_("Description metadata") +#define META_DESCR_LONGTEXT N_( \ + "Allows you to specify a \"description\" metadata for an input.") + +#define META_DATE_TEXT N_("Date metadata") +#define META_DATE_LONGTEXT N_( \ + "Allows you to specify a \"date\" metadata for an input.") + +#define META_URL_TEXT N_("URL metadata") +#define META_URL_LONGTEXT N_( \ + "Allows you to specify a \"url\" metadata for an input.") + +// DEPRECATED +#define CODEC_CAT_LONGTEXT N_( \ + "This option can be used to alter the way VLC selects " \ + "its codecs (decompression methods). Only advanced users should " \ + "alter this option as it can break playback of all your streams." ) + +#define CODEC_TEXT N_("Preferred decoders list") +#define CODEC_LONGTEXT N_( \ + "List of codecs that VLC will use in " \ + "priority. For instance, 'dummy,a52' will try the dummy and a52 codecs " \ + "before trying the other ones. Only advanced users should " \ + "alter this option as it can break playback of all your streams." ) + +#define ENCODER_TEXT N_("Preferred encoders list") +#define ENCODER_LONGTEXT N_( \ + "This allows you to select a list of encoders that VLC will use in " \ + "priority.") + +#define SYSTEM_CODEC_TEXT N_("Prefer system plugins over VLC") +#define SYSTEM_CODEC_LONGTEXT N_( \ + "Indicates whether VLC will prefer native plugins installed " \ + "on system over VLC owns plugins whenever a choice is available." ) + +/***************************************************************************** + * Sout + ****************************************************************************/ + +// DEPRECATED +#define SOUT_CAT_LONGTEXT N_( \ + "These options allow you to set default global options for the " \ + "stream output subsystem." ) + +#define SOUT_TEXT N_("Default stream output chain") +#define SOUT_LONGTEXT N_( \ + "You can enter here a default stream output chain. Refer to "\ + "the documentation to learn how to build such chains." \ + "Warning: this chain will be enabled for all streams." ) + +#define SOUT_ALL_TEXT N_("Enable streaming of all ES") +#define SOUT_ALL_LONGTEXT N_( \ + "Stream all elementary streams (video, audio and subtitles)") + +#define SOUT_DISPLAY_TEXT N_("Display while streaming") +#define SOUT_DISPLAY_LONGTEXT N_( \ + "Play locally the stream while streaming it.") + +#define SOUT_VIDEO_TEXT N_("Enable video stream output") +#define SOUT_VIDEO_LONGTEXT N_( \ + "Choose whether the video stream should be redirected to " \ + "the stream output facility when this last one is enabled.") + +#define SOUT_AUDIO_TEXT N_("Enable audio stream output") +#define SOUT_AUDIO_LONGTEXT N_( \ + "Choose whether the audio stream should be redirected to " \ + "the stream output facility when this last one is enabled.") + +#define SOUT_SPU_TEXT N_("Enable SPU stream output") +#define SOUT_SPU_LONGTEXT N_( \ + "Choose whether the SPU streams should be redirected to " \ + "the stream output facility when this last one is enabled.") + +#define SOUT_KEEP_TEXT N_("Keep stream output open" ) +#define SOUT_KEEP_LONGTEXT N_( \ + "This allows you to keep an unique stream output instance across " \ + "multiple playlist item (automatically insert the gather stream output " \ + "if not specified)" ) + +#define SOUT_MUX_CACHING_TEXT N_("Stream output muxer caching (ms)") +#define SOUT_MUX_CACHING_LONGTEXT N_( \ + "This allow you to configure the initial caching amount for stream output " \ + " muxer. This value should be set in milliseconds." ) + +#define PACKETIZER_TEXT N_("Preferred packetizer list") +#define PACKETIZER_LONGTEXT N_( \ + "This allows you to select the order in which VLC will choose its " \ + "packetizers." ) + +#define MUX_TEXT N_("Mux module") +#define MUX_LONGTEXT N_( \ + "This is a legacy entry to let you configure mux modules") + +#define ACCESS_OUTPUT_TEXT N_("Access output module") +#define ACCESS_OUTPUT_LONGTEXT N_( \ + "This is a legacy entry to let you configure access output modules") + +#define ANN_SAPCTRL_TEXT N_("Control SAP flow") +#define ANN_SAPCTRL_LONGTEXT N_( \ + "If this option is enabled, the flow on " \ + "the SAP multicast address will be controlled. This is needed if you " \ + "want to make announcements on the MBone." ) + +#define ANN_SAPINTV_TEXT N_("SAP announcement interval") +#define ANN_SAPINTV_LONGTEXT N_( \ + "When the SAP flow control is disabled, " \ + "this lets you set the fixed interval between SAP announcements." ) + +/***************************************************************************** + * Advanced + ****************************************************************************/ + +// DEPRECATED +#define CPU_CAT_LONGTEXT N_( \ + "These options allow you to enable special CPU optimizations. " \ + "You should always leave all these enabled." ) + +#define FPU_TEXT N_("Enable FPU support") +#define FPU_LONGTEXT N_( \ + "If your processor has a floating point calculation unit, VLC can take " \ + "advantage of it.") + +#define MMX_TEXT N_("Enable CPU MMX support") +#define MMX_LONGTEXT N_( \ + "If your processor supports the MMX instructions set, VLC can take " \ + "advantage of them.") + +#define THREE_DN_TEXT N_("Enable CPU 3D Now! support") +#define THREE_DN_LONGTEXT N_( \ + "If your processor supports the 3D Now! instructions set, VLC can take " \ + "advantage of them.") + +#define MMXEXT_TEXT N_("Enable CPU MMX EXT support") +#define MMXEXT_LONGTEXT N_( \ + "If your processor supports the MMX EXT instructions set, VLC can take " \ + "advantage of them.") + +#define SSE_TEXT N_("Enable CPU SSE support") +#define SSE_LONGTEXT N_( \ + "If your processor supports the SSE instructions set, VLC can take " \ + "advantage of them.") + +#define SSE2_TEXT N_("Enable CPU SSE2 support") +#define SSE2_LONGTEXT N_( \ + "If your processor supports the SSE2 instructions set, VLC can take " \ + "advantage of them.") + +#define ALTIVEC_TEXT N_("Enable CPU AltiVec support") +#define ALTIVEC_LONGTEXT N_( \ + "If your processor supports the AltiVec instructions set, VLC can take " \ + "advantage of them.") + +// DEPRECATED +#define MISC_CAT_LONGTEXT N_( \ + "These options allow you to select default modules. Leave these " \ + "alone unless you really know what you are doing." ) + +#define MEMCPY_TEXT N_("Memory copy module") +#define MEMCPY_LONGTEXT N_( \ + "You can select which memory copy module you want to use. By default " \ + "VLC will select the fastest one supported by your hardware.") + +#define ACCESS_TEXT N_("Access module") +#define ACCESS_LONGTEXT N_( \ + "This allows you to force an access module. You can use it if " \ + "the correct access is not automatically detected. You should not "\ + "set this as a global option unless you really know what you are doing." ) + +#define ACCESS_FILTER_TEXT N_("Access filter module") +#define ACCESS_FILTER_LONGTEXT N_( \ + "Access filters are used to modify the stream that is being read. " \ + "This is used for instance for timeshifting.") + +#define DEMUX_TEXT N_("Demux module") +#define DEMUX_LONGTEXT N_( \ + "Demultiplexers are used to separate the \"elementary\" streams " \ + "(like audio and video streams). You can use it if " \ + "the correct demuxer is not automatically detected. You should not "\ + "set this as a global option unless you really know what you are doing." ) + +#define RT_PRIORITY_TEXT N_("Allow real-time priority") +#define RT_PRIORITY_LONGTEXT N_( \ + "Running VLC in real-time priority will allow for much more precise " \ + "scheduling and yield better, especially when streaming content. " \ + "It can however lock up your whole machine, or make it very very " \ + "slow. You should only activate this if you know what you're " \ + "doing.") + +#define RT_OFFSET_TEXT N_("Adjust VLC priority") +#define RT_OFFSET_LONGTEXT N_( \ + "This option adds an offset (positive or negative) to VLC default " \ + "priorities. You can use it to tune VLC priority against other " \ + "programs, or against other VLC instances.") + +#define MINIMIZE_THREADS_TEXT N_("Minimize number of threads") +#define MINIMIZE_THREADS_LONGTEXT N_( \ + "This option minimizes the number of threads needed to run VLC.") + +#define USE_STREAM_IMMEDIATE N_("(Experimental) Don't do caching at the access level.") +#define USE_STREAM_IMMEDIATE_LONGTEXT N_( \ + "This option is useful if you want to lower the latency when " \ + "reading a stream") + +#define AUTO_ADJUST_PTS_DELAY N_("(Experimental) Minimize latency when" \ + "reading live stream.") +#define AUTO_ADJUST_PTS_DELAY_LONGTEXT N_( \ + "This option is useful if you want to lower the latency when " \ + "reading a stream") + +#define PLUGIN_PATH_TEXT N_("Modules search path") +#define PLUGIN_PATH_LONGTEXT N_( \ + "Additional path for VLC to look for its modules. You can add " \ + "several paths by concatenating them using \" PATH_SEP \" as separator") + +#define VLM_CONF_TEXT N_("VLM configuration file") +#define VLM_CONF_LONGTEXT N_( \ + "Read a VLM configuration file as soon as VLM is started." ) + +#define PLUGINS_CACHE_TEXT N_("Use a plugins cache") +#define PLUGINS_CACHE_LONGTEXT N_( \ + "Use a plugins cache which will greatly improve the startup time of VLC.") + +#define STATS_TEXT N_("Collect statistics") +#define STATS_LONGTEXT N_( \ + "Collect miscellaneous statistics.") + +#define DAEMON_TEXT N_("Run as daemon process") +#define DAEMON_LONGTEXT N_( \ + "Runs VLC as a background daemon process.") + +#define PIDFILE_TEXT N_("Write process id to file") +#define PIDFILE_LONGTEXT N_( \ + "Writes process id into specified file.") + +#define FILE_LOG_TEXT N_( "Log to file" ) +#define FILE_LOG_LONGTEXT N_( \ + "Log all VLC messages to a text file." ) + +#define SYSLOG_TEXT N_( "Log to syslog" ) +#define SYSLOG_LONGTEXT N_( \ + "Log all VLC messages to syslog (UNIX systems)." ) + +#define ONEINSTANCE_WIN_TEXT N_("Allow only one running instance") +#define ONEINSTANCE_WIN_LONGTEXT N_( \ + "Allowing only one running instance of VLC can sometimes be useful, " \ + "for example if you associated VLC with some media types and you " \ + "don't want a new instance of VLC to be opened each time you " \ + "double-click on a file in the explorer. This option will allow you " \ + "to play the file with the already running instance or enqueue it.") + +#define ONEINSTANCE_DBUS_TEXT ONEINSTANCE_WIN_TEXT +#define ONEINSTANCE_DBUS_LONGTEXT N_( \ + "Allowing only one running instance of VLC can sometimes be useful, " \ + "for example if you associated VLC with some media types and you " \ + "don't want a new instance of VLC to be opened each time you " \ + "open a file in your file manager. This option will allow you " \ + "to play the file with the already running instance or enqueue it. " \ + "This option require the D-Bus session daemon to be active " \ + "and the running instance of VLC to use D-Bus control interface.") + +#define STARTEDFROMFILE_TEXT N_("VLC is started from file association") +#define STARTEDFROMFILE_LONGTEXT N_( \ + "Tell VLC that it is being launched due to a file association in the OS" ) + +#define ONEINSTANCEWHENSTARTEDFROMFILE_TEXT N_( \ + "One instance when started from file") +#define ONEINSTANCEWHENSTARTEDFROMFILE_LONGTEXT N_( \ + "Allow only one running instance when started from file.") + +#define HPRIORITY_TEXT N_("Increase the priority of the process") +#define HPRIORITY_LONGTEXT N_( \ + "Increasing the priority of the process will very likely improve your " \ + "playing experience as it allows VLC not to be disturbed by other " \ + "applications that could otherwise take too much processor time. " \ + "However be advised that in certain circumstances (bugs) VLC could take " \ + "all the processor time and render the whole system unresponsive which " \ + "might require a reboot of your machine.") + +#define PLAYLISTENQUEUE_TEXT N_( \ + "Enqueue items to playlist when in one instance mode") +#define PLAYLISTENQUEUE_LONGTEXT N_( \ + "When using the one instance only option, enqueue items to playlist " \ + "and keep playing current item.") + +/***************************************************************************** + * Playlist + ****************************************************************************/ + +// DEPRECATED +#define PLAYLIST_CAT_LONGTEXT N_( \ + "These options define the behavior of the playlist. Some " \ + "of them can be overridden in the playlist dialog box." ) + +#define PREPARSE_TEXT N_( "Automatically preparse files") +#define PREPARSE_LONGTEXT N_( \ + "Automatically preparse files added to the playlist " \ + "(to retrieve some metadata)." ) + +#define ALBUM_ART_TEXT N_( "Album art policy" ) +#define ALBUM_ART_LONGTEXT N_( \ + "Choose how album art will be downloaded." ) + +static const int pi_albumart_values[] = { ALBUM_ART_WHEN_ASKED, + ALBUM_ART_WHEN_PLAYED, + ALBUM_ART_ALL }; +static const char *const ppsz_albumart_descriptions[] = + { N_("Manual download only"), + N_("When track starts playing"), + N_("As soon as track is added") }; + +#define SD_TEXT N_( "Services discovery modules") +#define SD_LONGTEXT N_( \ + "Specifies the services discovery modules to load, separated by " \ + "semi-colons. Typical values are sap, hal, ..." ) + +#define RANDOM_TEXT N_("Play files randomly forever") +#define RANDOM_LONGTEXT N_( \ + "VLC will randomly play files in the playlist until interrupted.") + +#define LOOP_TEXT N_("Repeat all") +#define LOOP_LONGTEXT N_( \ + "VLC will keep playing the playlist indefinitely." ) + +#define REPEAT_TEXT N_("Repeat current item") +#define REPEAT_LONGTEXT N_( \ + "VLC will keep playing the current playlist item." ) + +#define PAS_TEXT N_("Play and stop") +#define PAS_LONGTEXT N_( \ + "Stop the playlist after each played playlist item." ) + +#define PAE_TEXT N_("Play and exit") +#define PAE_LONGTEXT N_( \ + "Exit if there are no more items in the playlist." ) + +#define ML_TEXT N_("Use media library") +#define ML_LONGTEXT N_( \ + "The media library is automatically saved and reloaded each time you " \ + "start VLC." ) + +#define PLTREE_TEXT N_("Display playlist tree") +#define PLTREE_LONGTEXT N_( \ + "The playlist can use a tree to categorize some items, like the " \ + "contents of a directory." ) + + +/***************************************************************************** + * Hotkeys + ****************************************************************************/ + +// DEPRECATED +#define HOTKEY_CAT_LONGTEXT N_( "These settings are the global VLC key " \ + "bindings, known as \"hotkeys\"." ) + +#define TOGGLE_FULLSCREEN_KEY_TEXT N_("Fullscreen") +#define TOGGLE_FULLSCREEN_KEY_LONGTEXT N_("Select the hotkey to use to swap fullscreen state.") +#define LEAVE_FULLSCREEN_KEY_TEXT N_("Leave fullscreen") +#define LEAVE_FULLSCREEN_KEY_LONGTEXT N_("Select the hotkey to use to leave fullscreen state.") +#define PLAY_PAUSE_KEY_TEXT N_("Play/Pause") +#define PLAY_PAUSE_KEY_LONGTEXT N_("Select the hotkey to use to swap paused state.") +#define PAUSE_KEY_TEXT N_("Pause only") +#define PAUSE_KEY_LONGTEXT N_("Select the hotkey to use to pause.") +#define PLAY_KEY_TEXT N_("Play only") +#define PLAY_KEY_LONGTEXT N_("Select the hotkey to use to play.") +#define FASTER_KEY_TEXT N_("Faster") +#define FASTER_KEY_LONGTEXT N_("Select the hotkey to use for fast forward playback.") +#define SLOWER_KEY_TEXT N_("Slower") +#define SLOWER_KEY_LONGTEXT N_("Select the hotkey to use for slow motion playback.") +#define NEXT_KEY_TEXT N_("Next") +#define NEXT_KEY_LONGTEXT N_("Select the hotkey to use to skip to the next item in the playlist.") +#define PREV_KEY_TEXT N_("Previous") +#define PREV_KEY_LONGTEXT N_("Select the hotkey to use to skip to the previous item in the playlist.") +#define STOP_KEY_TEXT N_("Stop") +#define STOP_KEY_LONGTEXT N_("Select the hotkey to stop playback.") +#define POSITION_KEY_TEXT N_("Position") +#define POSITION_KEY_LONGTEXT N_("Select the hotkey to display the position.") + +#define JBEXTRASHORT_KEY_TEXT N_("Very short backwards jump") +#define JBEXTRASHORT_KEY_LONGTEXT \ + N_("Select the hotkey to make a very short backwards jump.") +#define JBSHORT_KEY_TEXT N_("Short backwards jump") +#define JBSHORT_KEY_LONGTEXT \ + N_("Select the hotkey to make a short backwards jump.") +#define JBMEDIUM_KEY_TEXT N_("Medium backwards jump") +#define JBMEDIUM_KEY_LONGTEXT \ + N_("Select the hotkey to make a medium backwards jump.") +#define JBLONG_KEY_TEXT N_("Long backwards jump") +#define JBLONG_KEY_LONGTEXT \ + N_("Select the hotkey to make a long backwards jump.") + +#define JFEXTRASHORT_KEY_TEXT N_("Very short forward jump") +#define JFEXTRASHORT_KEY_LONGTEXT \ + N_("Select the hotkey to make a very short forward jump.") +#define JFSHORT_KEY_TEXT N_("Short forward jump") +#define JFSHORT_KEY_LONGTEXT \ + N_("Select the hotkey to make a short forward jump.") +#define JFMEDIUM_KEY_TEXT N_("Medium forward jump") +#define JFMEDIUM_KEY_LONGTEXT \ + N_("Select the hotkey to make a medium forward jump.") +#define JFLONG_KEY_TEXT N_("Long forward jump") +#define JFLONG_KEY_LONGTEXT \ + N_("Select the hotkey to make a long forward jump.") + +#define JIEXTRASHORT_TEXT N_("Very short jump length") +#define JIEXTRASHORT_LONGTEXT N_("Very short jump length, in seconds.") +#define JISHORT_TEXT N_("Short jump length") +#define JISHORT_LONGTEXT N_("Short jump length, in seconds.") +#define JIMEDIUM_TEXT N_("Medium jump length") +#define JIMEDIUM_LONGTEXT N_("Medium jump length, in seconds.") +#define JILONG_TEXT N_("Long jump length") +#define JILONG_LONGTEXT N_("Long jump length, in seconds.") + +#define QUIT_KEY_TEXT N_("Quit") +#define QUIT_KEY_LONGTEXT N_("Select the hotkey to quit the application.") +#define NAV_UP_KEY_TEXT N_("Navigate up") +#define NAV_UP_KEY_LONGTEXT N_("Select the key to move the selector up in DVD menus.") +#define NAV_DOWN_KEY_TEXT N_("Navigate down") +#define NAV_DOWN_KEY_LONGTEXT N_("Select the key to move the selector down in DVD menus.") +#define NAV_LEFT_KEY_TEXT N_("Navigate left") +#define NAV_LEFT_KEY_LONGTEXT N_("Select the key to move the selector left in DVD menus.") +#define NAV_RIGHT_KEY_TEXT N_("Navigate right") +#define NAV_RIGHT_KEY_LONGTEXT N_("Select the key to move the selector right in DVD menus.") +#define NAV_ACTIVATE_KEY_TEXT N_("Activate") +#define NAV_ACTIVATE_KEY_LONGTEXT N_("Select the key to activate selected item in DVD menus.") +#define DISC_MENU_TEXT N_("Go to the DVD menu") +#define DISC_MENU_LONGTEXT N_("Select the key to take you to the DVD menu") +#define TITLE_PREV_TEXT N_("Select previous DVD title") +#define TITLE_PREV_LONGTEXT N_("Select the key to choose the previous title from the DVD") +#define TITLE_NEXT_TEXT N_("Select next DVD title") +#define TITLE_NEXT_LONGTEXT N_("Select the key to choose the next title from the DVD") +#define CHAPTER_PREV_TEXT N_("Select prev DVD chapter") +#define CHAPTER_PREV_LONGTEXT N_("Select the key to choose the previous chapter from the DVD") +#define CHAPTER_NEXT_TEXT N_("Select next DVD chapter") +#define CHAPTER_NEXT_LONGTEXT N_("Select the key to choose the next chapter from the DVD") +#define VOL_UP_KEY_TEXT N_("Volume up") +#define VOL_UP_KEY_LONGTEXT N_("Select the key to increase audio volume.") +#define VOL_DOWN_KEY_TEXT N_("Volume down") +#define VOL_DOWN_KEY_LONGTEXT N_("Select the key to decrease audio volume.") +#define VOL_MUTE_KEY_TEXT N_("Mute") +#define VOL_MUTE_KEY_LONGTEXT N_("Select the key to mute audio.") +#define SUBDELAY_UP_KEY_TEXT N_("Subtitle delay up") +#define SUBDELAY_UP_KEY_LONGTEXT N_("Select the key to increase the subtitle delay.") +#define SUBDELAY_DOWN_KEY_TEXT N_("Subtitle delay down") +#define SUBDELAY_DOWN_KEY_LONGTEXT N_("Select the key to decrease the subtitle delay.") +#define AUDIODELAY_UP_KEY_TEXT N_("Audio delay up") +#define AUDIODELAY_UP_KEY_LONGTEXT N_("Select the key to increase the audio delay.") +#define AUDIODELAY_DOWN_KEY_TEXT N_("Audio delay down") +#define AUDIODELAY_DOWN_KEY_LONGTEXT N_("Select the key to decrease the audio delay.") + +#define ZOOM_QUARTER_KEY_TEXT N_("1:4 Quarter") +#define ZOOM_HALF_KEY_TEXT N_("1:2 Half") +#define ZOOM_ORIGINAL_KEY_TEXT N_("1:1 Original") +#define ZOOM_DOUBLE_KEY_TEXT N_("2:1 Double") + +#define PLAY_BOOKMARK1_KEY_TEXT N_("Play playlist bookmark 1") +#define PLAY_BOOKMARK2_KEY_TEXT N_("Play playlist bookmark 2") +#define PLAY_BOOKMARK3_KEY_TEXT N_("Play playlist bookmark 3") +#define PLAY_BOOKMARK4_KEY_TEXT N_("Play playlist bookmark 4") +#define PLAY_BOOKMARK5_KEY_TEXT N_("Play playlist bookmark 5") +#define PLAY_BOOKMARK6_KEY_TEXT N_("Play playlist bookmark 6") +#define PLAY_BOOKMARK7_KEY_TEXT N_("Play playlist bookmark 7") +#define PLAY_BOOKMARK8_KEY_TEXT N_("Play playlist bookmark 8") +#define PLAY_BOOKMARK9_KEY_TEXT N_("Play playlist bookmark 9") +#define PLAY_BOOKMARK10_KEY_TEXT N_("Play playlist bookmark 10") +#define PLAY_BOOKMARK_KEY_LONGTEXT N_("Select the key to play this bookmark.") +#define SET_BOOKMARK1_KEY_TEXT N_("Set playlist bookmark 1") +#define SET_BOOKMARK2_KEY_TEXT N_("Set playlist bookmark 2") +#define SET_BOOKMARK3_KEY_TEXT N_("Set playlist bookmark 3") +#define SET_BOOKMARK4_KEY_TEXT N_("Set playlist bookmark 4") +#define SET_BOOKMARK5_KEY_TEXT N_("Set playlist bookmark 5") +#define SET_BOOKMARK6_KEY_TEXT N_("Set playlist bookmark 6") +#define SET_BOOKMARK7_KEY_TEXT N_("Set playlist bookmark 7") +#define SET_BOOKMARK8_KEY_TEXT N_("Set playlist bookmark 8") +#define SET_BOOKMARK9_KEY_TEXT N_("Set playlist bookmark 9") +#define SET_BOOKMARK10_KEY_TEXT N_("Set playlist bookmark 10") +#define SET_BOOKMARK_KEY_LONGTEXT N_("Select the key to set this playlist bookmark.") + +#define BOOKMARK1_TEXT N_("Playlist bookmark 1") +#define BOOKMARK2_TEXT N_("Playlist bookmark 2") +#define BOOKMARK3_TEXT N_("Playlist bookmark 3") +#define BOOKMARK4_TEXT N_("Playlist bookmark 4") +#define BOOKMARK5_TEXT N_("Playlist bookmark 5") +#define BOOKMARK6_TEXT N_("Playlist bookmark 6") +#define BOOKMARK7_TEXT N_("Playlist bookmark 7") +#define BOOKMARK8_TEXT N_("Playlist bookmark 8") +#define BOOKMARK9_TEXT N_("Playlist bookmark 9") +#define BOOKMARK10_TEXT N_("Playlist bookmark 10") +#define BOOKMARK_LONGTEXT N_( \ + "This allows you to define playlist bookmarks.") + +#define HISTORY_BACK_TEXT N_("Go back in browsing history") +#define HISTORY_BACK_LONGTEXT N_("Select the key to go back (to the previous media item) in the browsing history.") +#define HISTORY_FORWARD_TEXT N_("Go forward in browsing history") +#define HISTORY_FORWARD_LONGTEXT N_("Select the key to go forward (to the next media item) in the browsing history.") + +#define AUDIO_TRACK_KEY_TEXT N_("Cycle audio track") +#define AUDIO_TRACK_KEY_LONGTEXT N_("Cycle through the available audio tracks(languages).") +#define SUBTITLE_TRACK_KEY_TEXT N_("Cycle subtitle track") +#define SUBTITLE_TRACK_KEY_LONGTEXT N_("Cycle through the available subtitle tracks.") +#define ASPECT_RATIO_KEY_TEXT N_("Cycle source aspect ratio") +#define ASPECT_RATIO_KEY_LONGTEXT N_("Cycle through a predefined list of source aspect ratios.") +#define CROP_KEY_TEXT N_("Cycle video crop") +#define CROP_KEY_LONGTEXT N_("Cycle through a predefined list of crop formats.") +#define DEINTERLACE_KEY_TEXT N_("Cycle deinterlace modes") +#define DEINTERLACE_KEY_LONGTEXT N_("Cycle through deinterlace modes.") +#define INTF_SHOW_KEY_TEXT N_("Show interface") +#define INTF_SHOW_KEY_LONGTEXT N_("Raise the interface above all other windows.") +#define INTF_HIDE_KEY_TEXT N_("Hide interface") +#define INTF_HIDE_KEY_LONGTEXT N_("Lower the interface below all other windows.") +#define SNAP_KEY_TEXT N_("Take video snapshot") +#define SNAP_KEY_LONGTEXT N_("Takes a video snapshot and writes it to disk.") + +#define RECORD_KEY_TEXT N_("Record") +#define RECORD_KEY_LONGTEXT N_("Record access filter start/stop.") +#define DUMP_KEY_TEXT N_("Dump") +#define DUMP_KEY_LONGTEXT N_("Media dump access filter trigger.") + +#define LOOP_KEY_TEXT N_("Normal/Repeat/Loop") +#define LOOP_KEY_LONGTEXT N_("Toggle Normal/Repeat/Loop playlist modes") + +#define RANDOM_KEY_TEXT N_("Random") +#define RANDOM_KEY_LONGTEXT N_("Toggle random playlist playback") + +#define ZOOM_KEY_TEXT N_("Zoom") +#define ZOOM_KEY_LONGTEXT N_("Zoom") + +#define UNZOOM_KEY_TEXT N_("Un-Zoom") +#define UNZOOM_KEY_LONGTEXT N_("Un-Zoom") + +#define CROP_TOP_KEY_TEXT N_("Crop one pixel from the top of the video") +#define CROP_TOP_KEY_LONGTEXT N_("Crop one pixel from the top of the video") +#define UNCROP_TOP_KEY_TEXT N_("Uncrop one pixel from the top of the video") +#define UNCROP_TOP_KEY_LONGTEXT N_("Uncrop one pixel from the top of the video") + +#define CROP_LEFT_KEY_TEXT N_("Crop one pixel from the left of the video") +#define CROP_LEFT_KEY_LONGTEXT N_("Crop one pixel from the left of the video") +#define UNCROP_LEFT_KEY_TEXT N_("Uncrop one pixel from the left of the video") +#define UNCROP_LEFT_KEY_LONGTEXT N_("Uncrop one pixel from the left of the video") + +#define CROP_BOTTOM_KEY_TEXT N_("Crop one pixel from the bottom of the video") +#define CROP_BOTTOM_KEY_LONGTEXT N_("Crop one pixel from the bottom of the video") +#define UNCROP_BOTTOM_KEY_TEXT N_("Uncrop one pixel from the bottom of the video") +#define UNCROP_BOTTOM_KEY_LONGTEXT N_("Uncrop one pixel from the bottom of the video") + +#define CROP_RIGHT_KEY_TEXT N_("Crop one pixel from the right of the video") +#define CROP_RIGHT_KEY_LONGTEXT N_("Crop one pixel from the right of the video") +#define UNCROP_RIGHT_KEY_TEXT N_("Uncrop one pixel from the right of the video") +#define UNCROP_RIGHT_KEY_LONGTEXT N_("Uncrop one pixel from the right of the video") + +#define WALLPAPER_KEY_TEXT N_("Toggle wallpaper mode in video output") +#define WALLPAPER_KEY_LONGTEXT N_( \ + "Toggle wallpaper mode in video output. Only works with the directx " \ + "video output for the time being." ) + +#define MENU_ON_KEY_TEXT N_("Display OSD menu on top of video output") +#define MENU_ON_KEY_LONGTEXT N_("Display OSD menu on top of video output") +#define MENU_OFF_KEY_TEXT N_("Do not display OSD menu on video output") +#define MENU_OFF_KEY_LONGTEXT N_("Do not display OSD menu on top of video output") +#define MENU_RIGHT_KEY_TEXT N_("Highlight widget on the right") +#define MENU_RIGHT_KEY_LONGTEXT N_( \ + "Move OSD menu highlight to the widget on the right") +#define MENU_LEFT_KEY_TEXT N_("Highlight widget on the left") +#define MENU_LEFT_KEY_LONGTEXT N_( \ + "Move OSD menu highlight to the widget on the left") +#define MENU_UP_KEY_TEXT N_("Highlight widget on top") +#define MENU_UP_KEY_LONGTEXT N_( \ + "Move OSD menu highlight to the widget on top") +#define MENU_DOWN_KEY_TEXT N_("Highlight widget below") +#define MENU_DOWN_KEY_LONGTEXT N_( \ + "Move OSD menu highlight to the widget below") +#define MENU_SELECT_KEY_TEXT N_("Select current widget") +#define MENU_SELECT_KEY_LONGTEXT N_( \ + "Selecting current widget performs the associated action.") + +#define AUDI_DEVICE_CYCLE_KEY_TEXT N_("Cycle through audio devices") +#define AUDI_DEVICE_CYCLE_KEY_LONGTEXT N_("Cycle through available audio devices") +const char vlc_usage[] = N_( + "Usage: %s [options] [stream] ..." + "\nYou can specify multiple streams on the commandline. They will be enqueued in the playlist." + "\nThe first item specified will be played first." + "\n" + "\nOptions-styles:" + "\n --option A global option that is set for the duration of the program." + "\n -option A single letter version of a global --option." + "\n :option An option that only applies to the stream directly before it" + "\n and that overrides previous settings." + "\n" + "\nStream MRL syntax:" + "\n [[access][/demux]://]URL[@[title][:chapter][-[title][:chapter]]] [:option=value ...]" + "\n" + "\n Many of the global --options can also be used as MRL specific :options." + "\n Multiple :option=value pairs can be specified." + "\n" + "\nURL syntax:" + "\n [file://]filename Plain media file" + "\n http://ip:port/file HTTP URL" + "\n ftp://ip:port/file FTP URL" + "\n mms://ip:port/file MMS URL" + "\n screen:// Screen capture" + "\n [dvd://][device][@raw_device] DVD device" + "\n [vcd://][device] VCD device" + "\n [cdda://][device] Audio CD device" + "\n udp://[[]@[][:]]" + "\n UDP stream sent by a streaming server" + "\n vlc://pause: Special item to pause the playlist for a certain time" + "\n vlc://quit Special item to quit VLC" + "\n"); + +/* + * Quick usage guide for the configuration options: + * + * add_category_hint( N_(text), N_(longtext), b_advanced_option ); + * add_subcategory_hint( N_(text), N_(longtext), b_advanced_option ); + * add_usage_hint( N_(text), b_advanced_option ); + * add_string( option_name, value, p_callback, N_(text), N_(longtext), + b_advanced_option ); + * add_file( option_name, psz_value, p_callback, N_(text), N_(longtext) ); + * add_module( option_name, psz_value, i_capability, p_callback, + * N_(text), N_(longtext) ); + * add_integer( option_name, i_value, p_callback, N_(text), N_(longtext), + b_advanced_option ); + * add_bool( option_name, b_value, p_callback, N_(text), N_(longtext), + b_advanced_option ); + */ + +vlc_module_begin(); +/* Audio options */ + set_category( CAT_AUDIO ); + set_subcategory( SUBCAT_AUDIO_GENERAL ); + add_category_hint( N_("Audio"), AOUT_CAT_LONGTEXT , false ); + + add_bool( "audio", 1, NULL, AUDIO_TEXT, AUDIO_LONGTEXT, false ); + change_safe(); + add_integer_with_range( "volume", AOUT_VOLUME_DEFAULT, AOUT_VOLUME_MIN, + AOUT_VOLUME_MAX, NULL, VOLUME_TEXT, + VOLUME_LONGTEXT, false ); + add_integer_with_range( "volume-step", AOUT_VOLUME_STEP, AOUT_VOLUME_MIN, + AOUT_VOLUME_MAX, NULL, VOLUME_STEP_TEXT, + VOLUME_STEP_LONGTEXT, true ); + add_integer( "aout-rate", -1, NULL, AOUT_RATE_TEXT, + AOUT_RATE_LONGTEXT, true ); +#if !defined( __APPLE__ ) + add_bool( "hq-resampling", 1, NULL, AOUT_RESAMP_TEXT, + AOUT_RESAMP_LONGTEXT, true ); +#endif + add_bool( "spdif", 0, NULL, SPDIF_TEXT, SPDIF_LONGTEXT, false ); + add_integer( "force-dolby-surround", 0, NULL, FORCE_DOLBY_TEXT, + FORCE_DOLBY_LONGTEXT, false ); + change_integer_list( pi_force_dolby_values, ppsz_force_dolby_descriptions, NULL ); + add_integer( "audio-desync", 0, NULL, DESYNC_TEXT, + DESYNC_LONGTEXT, true ); + change_safe(); + + /* FIXME TODO create a subcat replay gain ? */ + add_string( "audio-replay-gain-mode", ppsz_replay_gain_mode[0], NULL, AUDIO_REPLAY_GAIN_MODE_TEXT, + AUDIO_REPLAY_GAIN_MODE_LONGTEXT, false ); + change_string_list( ppsz_replay_gain_mode, ppsz_replay_gain_mode_text, 0 ); + add_float( "audio-replay-gain-preamp", 0.0, NULL, + AUDIO_REPLAY_GAIN_PREAMP_TEXT, AUDIO_REPLAY_GAIN_PREAMP_LONGTEXT, false ); + add_float( "audio-replay-gain-default", -7.0, NULL, + AUDIO_REPLAY_GAIN_DEFAULT_TEXT, AUDIO_REPLAY_GAIN_DEFAULT_LONGTEXT, false ); + add_bool( "audio-replay-gain-peak-protection", true, NULL, + AUDIO_REPLAY_GAIN_PEAK_PROTECTION_TEXT, AUDIO_REPLAY_GAIN_PEAK_PROTECTION_LONGTEXT, true ); + + set_subcategory( SUBCAT_AUDIO_AOUT ); + add_module( "aout", "audio output", NULL, NULL, AOUT_TEXT, AOUT_LONGTEXT, + true ); + change_short('A'); + set_subcategory( SUBCAT_AUDIO_AFILTER ); + add_module_list_cat( "audio-filter", SUBCAT_AUDIO_AFILTER, 0, + NULL, AUDIO_FILTER_TEXT, + AUDIO_FILTER_LONGTEXT, false ); + set_subcategory( SUBCAT_AUDIO_VISUAL ); + add_module( "audio-visual", "visualization",NULL, NULL,AUDIO_VISUAL_TEXT, + AUDIO_VISUAL_LONGTEXT, false ); + +/* Video options */ + set_category( CAT_VIDEO ); + set_subcategory( SUBCAT_VIDEO_GENERAL ); + add_category_hint( N_("Video"), VOUT_CAT_LONGTEXT , false ); + + add_bool( "video", 1, NULL, VIDEO_TEXT, VIDEO_LONGTEXT, true ); + change_safe(); + add_bool( "grayscale", 0, NULL, GRAYSCALE_TEXT, + GRAYSCALE_LONGTEXT, true ); + add_bool( "fullscreen", 0, NULL, FULLSCREEN_TEXT, + FULLSCREEN_LONGTEXT, false ); + change_short('f'); + change_safe(); + add_bool( "embedded-video", 1, NULL, EMBEDDED_TEXT, EMBEDDED_LONGTEXT, + true ); +#ifdef __APPLE__ + add_deprecated_alias( "macosx-embedded" ); /*deprecated since 0.9.0 */ +#endif + add_bool( "drop-late-frames", 1, NULL, DROP_LATE_FRAMES_TEXT, + DROP_LATE_FRAMES_LONGTEXT, true ); + /* Used in vout_synchro */ + add_bool( "skip-frames", 1, NULL, SKIP_FRAMES_TEXT, + SKIP_FRAMES_LONGTEXT, true ); + add_bool( "quiet-synchro", 0, NULL, QUIET_SYNCHRO_TEXT, + QUIET_SYNCHRO_LONGTEXT, true ); +#ifndef __APPLE__ + add_bool( "overlay", 1, NULL, OVERLAY_TEXT, OVERLAY_LONGTEXT, false ); +#endif + add_bool( "video-on-top", 0, NULL, VIDEO_ON_TOP_TEXT, + VIDEO_ON_TOP_LONGTEXT, false ); + add_bool( "disable-screensaver", true, NULL, SS_TEXT, SS_LONGTEXT, + true ); + + add_bool( "video-title-show", 1, NULL, VIDEO_TITLE_SHOW_TEXT, + VIDEO_TITLE_SHOW_LONGTEXT, false ); + add_integer( "video-title-timeout", 5000, NULL, VIDEO_TITLE_TIMEOUT_TEXT, + VIDEO_TITLE_TIMEOUT_LONGTEXT, false ); + add_integer( "video-title-position", 8, NULL, VIDEO_TITLE_POSITION_TEXT, + VIDEO_TITLE_POSITION_LONGTEXT, false ); + change_integer_list( pi_pos_values, ppsz_pos_descriptions, NULL ); + // autohide after 1.5s + add_integer( "mouse-hide-timeout", 1500, NULL, MOUSE_HIDE_TIMEOUT_TEXT, + MOUSE_HIDE_TIMEOUT_LONGTEXT, false ); + set_section( N_("Snapshot") , NULL ); + add_directory( "snapshot-path", NULL, NULL, SNAP_PATH_TEXT, + SNAP_PATH_LONGTEXT, false ); + change_unsafe(); + add_string( "snapshot-prefix", "vlcsnap-", NULL, SNAP_PREFIX_TEXT, + SNAP_PREFIX_LONGTEXT, false ); + add_string( "snapshot-format", "png", NULL, SNAP_FORMAT_TEXT, + SNAP_FORMAT_LONGTEXT, false ); + change_string_list( ppsz_snap_formats, NULL, 0 ); + add_bool( "snapshot-preview", true, NULL, SNAP_PREVIEW_TEXT, + SNAP_PREVIEW_LONGTEXT, false ); + add_bool( "snapshot-sequential", false, NULL, SNAP_SEQUENTIAL_TEXT, + SNAP_SEQUENTIAL_LONGTEXT, false ); + add_integer( "snapshot-width", -1, NULL, SNAP_WIDTH_TEXT, + SNAP_WIDTH_LONGTEXT, true ); + add_integer( "snapshot-height", -1, NULL, SNAP_HEIGHT_TEXT, + SNAP_HEIGHT_LONGTEXT, true ); + + set_section( N_("Window properties" ), NULL ); + add_integer( "width", -1, NULL, WIDTH_TEXT, WIDTH_LONGTEXT, true ); + change_safe(); + add_integer( "height", -1, NULL, HEIGHT_TEXT, HEIGHT_LONGTEXT, true ); + change_safe(); + add_integer( "video-x", -1, NULL, VIDEOX_TEXT, VIDEOX_LONGTEXT, true ); + change_safe(); + add_integer( "video-y", -1, NULL, VIDEOY_TEXT, VIDEOY_LONGTEXT, true ); + change_safe(); + add_string( "crop", NULL, NULL, CROP_TEXT, CROP_LONGTEXT, false ); + change_safe(); + add_string( "custom-crop-ratios", NULL, NULL, CUSTOM_CROP_RATIOS_TEXT, + CUSTOM_CROP_RATIOS_LONGTEXT, false ); + add_string( "aspect-ratio", NULL, NULL, + ASPECT_RATIO_TEXT, ASPECT_RATIO_LONGTEXT, false ); + change_safe(); + add_string( "monitor-par", NULL, NULL, + MASPECT_RATIO_TEXT, MASPECT_RATIO_LONGTEXT, true ); + add_string( "custom-aspect-ratios", NULL, NULL, CUSTOM_ASPECT_RATIOS_TEXT, + CUSTOM_ASPECT_RATIOS_LONGTEXT, false ); + add_bool( "hdtv-fix", 1, NULL, HDTV_FIX_TEXT, HDTV_FIX_LONGTEXT, true ); + add_bool( "video-deco", 1, NULL, VIDEO_DECO_TEXT, + VIDEO_DECO_LONGTEXT, true ); + add_string( "video-title", NULL, NULL, VIDEO_TITLE_TEXT, + VIDEO_TITLE_LONGTEXT, true ); + add_integer( "align", 0, NULL, ALIGN_TEXT, ALIGN_LONGTEXT, true ); + change_integer_list( pi_align_values, ppsz_align_descriptions, NULL ); + add_float( "zoom", 1, NULL, ZOOM_TEXT, ZOOM_LONGTEXT, true ); + + + set_subcategory( SUBCAT_VIDEO_VOUT ); + add_module( "vout", "video output", NULL, NULL, VOUT_TEXT, VOUT_LONGTEXT, + true ); + change_short('V'); + + set_subcategory( SUBCAT_VIDEO_VFILTER ); + add_module_list_cat( "video-filter", SUBCAT_VIDEO_VFILTER, NULL, NULL, + VIDEO_FILTER_TEXT, VIDEO_FILTER_LONGTEXT, false ); + add_deprecated_alias( "filter" ); /*deprecated since 0.8.2 */ + add_module_list_cat( "vout-filter", SUBCAT_VIDEO_VFILTER, NULL, NULL, + VOUT_FILTER_TEXT, VOUT_FILTER_LONGTEXT, false ); +#if 0 + add_string( "pixel-ratio", "1", NULL, PIXEL_RATIO_TEXT, PIXEL_RATIO_TEXT ); +#endif + +/* Subpictures options */ + set_subcategory( SUBCAT_VIDEO_SUBPIC ); + set_section( N_("On Screen Display") , NULL ); + add_category_hint( N_("Subpictures"), SUB_CAT_LONGTEXT , false ); + + add_bool( "spu", 1, NULL, SPU_TEXT, SPU_LONGTEXT, true ); + change_safe(); + add_bool( "osd", 1, NULL, OSD_TEXT, OSD_LONGTEXT, false ); + add_module( "text-renderer", "text renderer", NULL, NULL, TEXTRENDERER_TEXT, + TEXTRENDERER_LONGTEXT, true ); + + set_section( N_("Subtitles") , NULL ); + add_file( "sub-file", NULL, NULL, SUB_FILE_TEXT, + SUB_FILE_LONGTEXT, false ); + add_bool( "sub-autodetect-file", true, NULL, + SUB_AUTO_TEXT, SUB_AUTO_LONGTEXT, false ); + add_integer( "sub-autodetect-fuzzy", 3, NULL, + SUB_FUZZY_TEXT, SUB_FUZZY_LONGTEXT, true ); +#ifdef WIN32 +# define SUB_PATH ".\\subtitles" +#else +# define SUB_PATH "./Subtitles, ./subtitles" +#endif + add_string( "sub-autodetect-path", SUB_PATH, NULL, + SUB_PATH_TEXT, SUB_PATH_LONGTEXT, true ); + add_integer( "sub-margin", 0, NULL, SUB_MARGIN_TEXT, + SUB_MARGIN_LONGTEXT, true ); + add_deprecated_alias( "spu-margin" ); /*Deprecated since 0.8.2 */ + set_section( N_( "Overlays" ) , NULL ); + add_module_list_cat( "sub-filter", SUBCAT_VIDEO_SUBPIC, NULL, NULL, + SUB_FILTER_TEXT, SUB_FILTER_LONGTEXT, false ); + +/* Input options */ + set_category( CAT_INPUT ); + set_subcategory( SUBCAT_INPUT_GENERAL ); + + set_section( N_( "Track settings" ), NULL ); + add_integer( "program", 0, NULL, + INPUT_PROGRAM_TEXT, INPUT_PROGRAM_LONGTEXT, true ); + change_safe(); + add_string( "programs", "", NULL, + INPUT_PROGRAMS_TEXT, INPUT_PROGRAMS_LONGTEXT, true ); + change_safe(); + add_integer( "audio-track", -1, NULL, + INPUT_AUDIOTRACK_TEXT, INPUT_AUDIOTRACK_LONGTEXT, true ); + change_safe(); + add_deprecated_alias( "audio-channel" ); /*deprecated since 0.8.2 */ + add_integer( "sub-track", -1, NULL, + INPUT_SUBTRACK_TEXT, INPUT_SUBTRACK_LONGTEXT, true ); + change_safe(); + add_deprecated_alias("spu-channel" ); /*deprecated since 0.8.2*/ + add_string( "audio-language", "", NULL, + INPUT_AUDIOTRACK_LANG_TEXT, INPUT_AUDIOTRACK_LANG_LONGTEXT, + false ); + change_safe(); + add_string( "sub-language", "", NULL, + INPUT_SUBTRACK_LANG_TEXT, INPUT_SUBTRACK_LANG_LONGTEXT, + false ); + change_safe(); + add_integer( "audio-track-id", -1, NULL, INPUT_AUDIOTRACK_ID_TEXT, + INPUT_AUDIOTRACK_ID_LONGTEXT, true ); + change_safe(); + add_integer( "sub-track-id", -1, NULL, + INPUT_SUBTRACK_ID_TEXT, INPUT_SUBTRACK_ID_LONGTEXT, true ); + change_safe(); + + set_section( N_( "Playback control" ) , NULL); + add_integer( "input-repeat", 0, NULL, + INPUT_REPEAT_TEXT, INPUT_REPEAT_LONGTEXT, false ); + change_safe(); + add_integer( "start-time", 0, NULL, + START_TIME_TEXT, START_TIME_LONGTEXT, true ); + change_safe(); + add_integer( "stop-time", 0, NULL, + STOP_TIME_TEXT, STOP_TIME_LONGTEXT, true ); + change_safe(); + add_integer( "run-time", 0, NULL, + RUN_TIME_TEXT, RUN_TIME_LONGTEXT, true ); + change_safe(); + add_string( "input-list", NULL, NULL, + INPUT_LIST_TEXT, INPUT_LIST_LONGTEXT, true ); + add_string( "input-slave", NULL, NULL, + INPUT_SLAVE_TEXT, INPUT_SLAVE_LONGTEXT, true ); + + add_string( "bookmarks", NULL, NULL, + BOOKMARKS_TEXT, BOOKMARKS_LONGTEXT, true ); + + set_section( N_( "Default devices") , NULL ); + + add_file( "dvd", DVD_DEVICE, NULL, DVD_DEV_TEXT, DVD_DEV_LONGTEXT, + false ); + add_file( "vcd", VCD_DEVICE, NULL, VCD_DEV_TEXT, VCD_DEV_LONGTEXT, + false ); + add_file( "cd-audio", CDAUDIO_DEVICE, NULL, CDAUDIO_DEV_TEXT, + CDAUDIO_DEV_LONGTEXT, false ); + + set_section( N_( "Network settings" ), NULL ); + + add_integer( "server-port", 1234, NULL, + SERVER_PORT_TEXT, SERVER_PORT_LONGTEXT, false ); + add_integer( "mtu", MTU_DEFAULT, NULL, MTU_TEXT, MTU_LONGTEXT, true ); + add_bool( "ipv6", 0, NULL, IPV6_TEXT, IPV6_LONGTEXT, false ); + change_short('6'); + add_bool( "ipv4", 0, NULL, IPV4_TEXT, IPV4_LONGTEXT, false ); + change_short('4'); + add_integer( "ipv4-timeout", 5 * 1000, NULL, TIMEOUT_TEXT, + TIMEOUT_LONGTEXT, true ); + + set_section( N_( "Socks proxy") , NULL ); + add_string( "socks", NULL, NULL, + SOCKS_SERVER_TEXT, SOCKS_SERVER_LONGTEXT, true ); + add_string( "socks-user", NULL, NULL, + SOCKS_USER_TEXT, SOCKS_USER_LONGTEXT, true ); + add_string( "socks-pwd", NULL, NULL, + SOCKS_PASS_TEXT, SOCKS_PASS_LONGTEXT, true ); + + + set_section( N_("Metadata" ) , NULL ); + add_string( "meta-title", NULL, NULL, META_TITLE_TEXT, + META_TITLE_LONGTEXT, true ); + add_string( "meta-author", NULL, NULL, META_AUTHOR_TEXT, + META_AUTHOR_LONGTEXT, true ); + add_string( "meta-artist", NULL, NULL, META_ARTIST_TEXT, + META_ARTIST_LONGTEXT, true ); + add_string( "meta-genre", NULL, NULL, META_GENRE_TEXT, + META_GENRE_LONGTEXT, true ); + add_string( "meta-copyright", NULL, NULL, META_CPYR_TEXT, + META_CPYR_LONGTEXT, true ); + add_string( "meta-description", NULL, NULL, META_DESCR_TEXT, + META_DESCR_LONGTEXT, true ); + add_string( "meta-date", NULL, NULL, META_DATE_TEXT, + META_DATE_LONGTEXT, true ); + add_string( "meta-url", NULL, NULL, META_URL_TEXT, + META_URL_LONGTEXT, true ); + + set_section( N_( "Advanced" ), NULL ); + + add_integer( "cr-average", 40, NULL, CR_AVERAGE_TEXT, + CR_AVERAGE_LONGTEXT, true ); + add_integer( "clock-synchro", -1, NULL, CLOCK_SYNCHRO_TEXT, + CLOCK_SYNCHRO_LONGTEXT, true ); + change_integer_list( pi_clock_values, ppsz_clock_descriptions, NULL ); + + add_bool( "network-synchronisation", false, NULL, NETSYNC_TEXT, + NETSYNC_LONGTEXT, true ); + +/* Decoder options */ + add_category_hint( N_("Decoders"), CODEC_CAT_LONGTEXT , true ); + add_string( "codec", NULL, NULL, CODEC_TEXT, + CODEC_LONGTEXT, true ); + add_string( "encoder", NULL, NULL, ENCODER_TEXT, + ENCODER_LONGTEXT, true ); + + set_subcategory( SUBCAT_INPUT_ACCESS ); + add_category_hint( N_("Input"), INPUT_CAT_LONGTEXT , false ); + add_module( "access", "access", NULL, NULL, ACCESS_TEXT, + ACCESS_LONGTEXT, true ); + + set_subcategory( SUBCAT_INPUT_ACCESS_FILTER ); + add_module_list_cat( "access-filter", SUBCAT_INPUT_ACCESS_FILTER, NULL, NULL, + ACCESS_FILTER_TEXT, ACCESS_FILTER_LONGTEXT, false ); + + + set_subcategory( SUBCAT_INPUT_DEMUX ); + add_module( "demux", "demux", NULL, NULL, DEMUX_TEXT, + DEMUX_LONGTEXT, true ); + set_subcategory( SUBCAT_INPUT_VCODEC ); + set_subcategory( SUBCAT_INPUT_ACODEC ); + set_subcategory( SUBCAT_INPUT_SCODEC ); + add_bool( "prefer-system-codecs", false, NULL, SYSTEM_CODEC_TEXT, + SYSTEM_CODEC_LONGTEXT, false ); + + +/* Stream output options */ + set_category( CAT_SOUT ); + set_subcategory( SUBCAT_SOUT_GENERAL ); + add_category_hint( N_("Stream output"), SOUT_CAT_LONGTEXT , true ); + + add_string( "sout", NULL, NULL, SOUT_TEXT, SOUT_LONGTEXT, true ); + add_bool( "sout-display", false, NULL, SOUT_DISPLAY_TEXT, + SOUT_DISPLAY_LONGTEXT, true ); + add_bool( "sout-keep", true, NULL, SOUT_KEEP_TEXT, + SOUT_KEEP_LONGTEXT, true ); + add_bool( "sout-all", 0, NULL, SOUT_ALL_TEXT, + SOUT_ALL_LONGTEXT, true ); + add_bool( "sout-audio", 1, NULL, SOUT_AUDIO_TEXT, + SOUT_AUDIO_LONGTEXT, true ); + add_bool( "sout-video", 1, NULL, SOUT_VIDEO_TEXT, + SOUT_VIDEO_LONGTEXT, true ); + add_bool( "sout-spu", 1, NULL, SOUT_SPU_TEXT, + SOUT_SPU_LONGTEXT, true ); + add_integer( "sout-mux-caching", 1500, NULL, SOUT_MUX_CACHING_TEXT, + SOUT_MUX_CACHING_LONGTEXT, true ); + + set_section( N_("VLM"), NULL ); + add_string( "vlm-conf", NULL, NULL, VLM_CONF_TEXT, + VLM_CONF_LONGTEXT, true ); + + + + set_subcategory( SUBCAT_SOUT_STREAM ); + set_subcategory( SUBCAT_SOUT_MUX ); + add_module( "mux", "sout mux", NULL, NULL, MUX_TEXT, + MUX_LONGTEXT, true ); + set_subcategory( SUBCAT_SOUT_ACO ); + add_module( "access_output", "sout access", NULL, NULL, + ACCESS_OUTPUT_TEXT, ACCESS_OUTPUT_LONGTEXT, true ); + add_integer( "ttl", -1, NULL, TTL_TEXT, TTL_LONGTEXT, true ); + add_string( "miface", NULL, NULL, MIFACE_TEXT, MIFACE_LONGTEXT, true ); + add_string( "miface-addr", NULL, NULL, MIFACE_ADDR_TEXT, MIFACE_ADDR_LONGTEXT, true ); + add_integer( "dscp", 0, NULL, DSCP_TEXT, DSCP_LONGTEXT, true ); + + set_subcategory( SUBCAT_SOUT_PACKETIZER ); + add_module( "packetizer","packetizer", NULL, NULL, + PACKETIZER_TEXT, PACKETIZER_LONGTEXT, true ); + + set_subcategory( SUBCAT_SOUT_SAP ); + add_bool( "sap-flow-control", false, NULL, ANN_SAPCTRL_TEXT, + ANN_SAPCTRL_LONGTEXT, true ); + add_integer( "sap-interval", 5, NULL, ANN_SAPINTV_TEXT, + ANN_SAPINTV_LONGTEXT, true ); + + set_subcategory( SUBCAT_SOUT_VOD ); + +/* CPU options */ + set_category( CAT_ADVANCED ); + set_subcategory( SUBCAT_ADVANCED_CPU ); + add_category_hint( N_("CPU"), CPU_CAT_LONGTEXT, true ); + add_bool( "fpu", 1, NULL, FPU_TEXT, FPU_LONGTEXT, true ); + change_need_restart(); +#if defined( __i386__ ) || defined( __x86_64__ ) + add_bool( "mmx", 1, NULL, MMX_TEXT, MMX_LONGTEXT, true ); + change_need_restart(); + add_bool( "3dn", 1, NULL, THREE_DN_TEXT, THREE_DN_LONGTEXT, true ); + change_need_restart(); + add_bool( "mmxext", 1, NULL, MMXEXT_TEXT, MMXEXT_LONGTEXT, true ); + change_need_restart(); + add_bool( "sse", 1, NULL, SSE_TEXT, SSE_LONGTEXT, true ); + change_need_restart(); + add_bool( "sse2", 1, NULL, SSE2_TEXT, SSE2_LONGTEXT, true ); + change_need_restart(); +#endif +#if defined( __powerpc__ ) || defined( __ppc__ ) || defined( __ppc64__ ) + add_bool( "altivec", 1, NULL, ALTIVEC_TEXT, ALTIVEC_LONGTEXT, true ); + change_need_restart(); +#endif + +/* Misc options */ + set_subcategory( SUBCAT_ADVANCED_MISC ); + set_section( N_("Special modules"), NULL ); + add_category_hint( N_("Miscellaneous"), MISC_CAT_LONGTEXT, true ); + add_module( "memcpy", "memcpy", NULL, NULL, MEMCPY_TEXT, + MEMCPY_LONGTEXT, true ); + change_need_restart(); + + set_section( N_("Plugins" ), NULL ); + add_bool( "plugins-cache", true, NULL, PLUGINS_CACHE_TEXT, + PLUGINS_CACHE_LONGTEXT, true ); + change_need_restart(); + add_directory( "plugin-path", NULL, NULL, PLUGIN_PATH_TEXT, + PLUGIN_PATH_LONGTEXT, true ); + change_need_restart(); + change_unsafe(); + + set_section( N_("Performance options"), NULL ); + add_bool( "minimize-threads", 0, NULL, MINIMIZE_THREADS_TEXT, + MINIMIZE_THREADS_LONGTEXT, true ); + change_need_restart(); + + add_bool( "use-stream-immediate", false, NULL, + USE_STREAM_IMMEDIATE, USE_STREAM_IMMEDIATE_LONGTEXT, true ); + + add_bool( "auto-adjust-pts-delay", false, NULL, + AUTO_ADJUST_PTS_DELAY, AUTO_ADJUST_PTS_DELAY_LONGTEXT, true ); + +#if !defined(__APPLE__) && !defined(SYS_BEOS) && defined(LIBVLC_USE_PTHREAD) + add_bool( "rt-priority", false, NULL, RT_PRIORITY_TEXT, + RT_PRIORITY_LONGTEXT, true ); + change_need_restart(); +#endif + +#if !defined(SYS_BEOS) && defined(LIBVLC_USE_PTHREAD) + add_integer( "rt-offset", 0, NULL, RT_OFFSET_TEXT, + RT_OFFSET_LONGTEXT, true ); + change_need_restart(); +#endif + +#if defined(HAVE_DBUS) + add_bool( "one-instance", 0, NULL, ONEINSTANCE_DBUS_TEXT, + ONEINSTANCE_DBUS_LONGTEXT, true ); + add_bool( "playlist-enqueue", 0, NULL, PLAYLISTENQUEUE_TEXT, + PLAYLISTENQUEUE_LONGTEXT, true ); + + add_bool( "inhibit", 1, NULL, INHIBIT_TEXT, + INHIBIT_LONGTEXT, true ); +#endif + +#if defined(WIN32) + add_bool( "one-instance", 0, NULL, ONEINSTANCE_WIN_TEXT, + ONEINSTANCE_WIN_LONGTEXT, true ); + add_bool( "started-from-file", 0, NULL, STARTEDFROMFILE_TEXT, + STARTEDFROMFILE_LONGTEXT, true ); + change_internal(); + change_unsaveable(); + add_bool( "one-instance-when-started-from-file", 1, NULL, + ONEINSTANCEWHENSTARTEDFROMFILE_TEXT, + ONEINSTANCEWHENSTARTEDFROMFILE_LONGTEXT, true ); + add_bool( "playlist-enqueue", 0, NULL, PLAYLISTENQUEUE_TEXT, + PLAYLISTENQUEUE_LONGTEXT, true ); + change_unsaveable(); + add_bool( "high-priority", 0, NULL, HPRIORITY_TEXT, + HPRIORITY_LONGTEXT, false ); + change_need_restart(); +#endif + +/* Playlist options */ + set_category( CAT_PLAYLIST ); + set_subcategory( SUBCAT_PLAYLIST_GENERAL ); + add_category_hint( N_("Playlist"), PLAYLIST_CAT_LONGTEXT , false ); + add_bool( "random", 0, NULL, RANDOM_TEXT, RANDOM_LONGTEXT, false ); + change_short('Z'); + add_bool( "loop", 0, NULL, LOOP_TEXT, LOOP_LONGTEXT, false ); + change_short('L'); + add_bool( "repeat", 0, NULL, REPEAT_TEXT, REPEAT_LONGTEXT, false ); + change_short('R'); + add_bool( "play-and-exit", 0, NULL, PAE_TEXT, PAE_LONGTEXT, false ); + add_bool( "play-and-stop", 0, NULL, PAS_TEXT, PAS_LONGTEXT, false ); + add_bool( "media-library", 1, NULL, ML_TEXT, ML_LONGTEXT, false ); + add_bool( "playlist-tree", 0, NULL, PLTREE_TEXT, PLTREE_LONGTEXT, false ); + + add_string( "open", "", NULL, OPEN_TEXT, OPEN_LONGTEXT, false ); + change_need_restart(); + + add_bool( "auto-preparse", true, NULL, PREPARSE_TEXT, + PREPARSE_LONGTEXT, false ); + + add_integer( "album-art", ALBUM_ART_WHEN_ASKED, NULL, ALBUM_ART_TEXT, + ALBUM_ART_LONGTEXT, false ); + change_integer_list( pi_albumart_values, + ppsz_albumart_descriptions, 0 ); + + set_subcategory( SUBCAT_PLAYLIST_SD ); + add_module_list_cat( "services-discovery", SUBCAT_PLAYLIST_SD, NULL, + NULL, SD_TEXT, SD_LONGTEXT, false ); + change_short('S'); + change_need_restart(); + +/* Interface options */ + set_category( CAT_INTERFACE ); + set_subcategory( SUBCAT_INTERFACE_GENERAL ); + add_integer( "verbose", 0, NULL, VERBOSE_TEXT, VERBOSE_LONGTEXT, + false ); + change_short('v'); + add_bool( "quiet", 0, NULL, QUIET_TEXT, QUIET_LONGTEXT, true ); + change_short('q'); + +#if !defined(WIN32) + add_bool( "daemon", 0, NULL, DAEMON_TEXT, DAEMON_LONGTEXT, true ); + change_short('d'); + change_need_restart(); + + add_string( "pidfile", NULL, NULL, PIDFILE_TEXT, PIDFILE_LONGTEXT, + false ); + change_need_restart(); +#endif + + add_bool( "file-logging", false, NULL, FILE_LOG_TEXT, FILE_LOG_LONGTEXT, + true ); + change_need_restart(); +#ifdef HAVE_SYSLOG_H + add_bool ( "syslog", false, NULL, SYSLOG_TEXT, SYSLOG_LONGTEXT, + true ); + change_need_restart(); +#endif + +#if defined (WIN32) || defined (__APPLE__) + add_string( "language", "auto", NULL, LANGUAGE_TEXT, LANGUAGE_LONGTEXT, + false ); + change_string_list( ppsz_language, ppsz_language_text, 0 ); + change_need_restart(); +#endif + + add_bool( "color", true, NULL, COLOR_TEXT, COLOR_LONGTEXT, true ); + add_bool( "advanced", false, NULL, ADVANCED_TEXT, ADVANCED_LONGTEXT, + false ); + change_need_restart(); + add_bool( "interact", true, NULL, INTERACTION_TEXT, + INTERACTION_LONGTEXT, false ); + + add_bool( "show-intf", false, NULL, SHOWINTF_TEXT, SHOWINTF_LONGTEXT, + false ); + change_need_restart(); + + add_bool ( "stats", true, NULL, STATS_TEXT, STATS_LONGTEXT, true ); + change_need_restart(); + + set_subcategory( SUBCAT_INTERFACE_MAIN ); + add_module_cat( "intf", SUBCAT_INTERFACE_MAIN, NULL, NULL, INTF_TEXT, + INTF_LONGTEXT, false ); + change_short('I'); + change_need_restart(); + add_module_list_cat( "extraintf", SUBCAT_INTERFACE_MAIN, + NULL, NULL, EXTRAINTF_TEXT, + EXTRAINTF_LONGTEXT, false ); + change_need_restart(); + + + set_subcategory( SUBCAT_INTERFACE_CONTROL ); + add_module_list_cat( "control", SUBCAT_INTERFACE_CONTROL, NULL, NULL, + CONTROL_TEXT, CONTROL_LONGTEXT, false ); + change_need_restart(); + +/* Hotkey options*/ + set_subcategory( SUBCAT_INTERFACE_HOTKEYS ); + add_category_hint( N_("Hot keys"), HOTKEY_CAT_LONGTEXT , false ); + +#if defined(__APPLE__) +/* Don't use the following combo's */ + +/* copy KEY_MODIFIER_COMMAND|'c' + * cut KEY_MODIFIER_COMMAND|'x' + * paste KEY_MODIFIER_COMMAND|'v' + * select all KEY_MODIFIER_COMMAND|'a' + * preferences KEY_MODIFIER_COMMAND|',' + * hide vlc KEY_MODIFIER_COMMAND|'h' + * hide other KEY_MODIFIER_COMMAND|KEY_MODIFIER_ALT|'h' + * open file KEY_MODIFIER_COMMAND|KEY_MODIFIER_SHIFT|'o' + * open KEY_MODIFIER_COMMAND|'o' + * open disk KEY_MODIFIER_COMMAND|'d' + * open network KEY_MODIFIER_COMMAND|'n' + * open capture KEY_MODIFIER_COMMAND|'r' + * save playlist KEY_MODIFIER_COMMAND|'s' + * playlist random KEY_MODIFIER_COMMAND|'z' + * playlist repeat all KEY_MODIFIER_COMMAND|'l' + * playlist repeat KEY_MODIFIER_COMMAND|'r' + * video half size KEY_MODIFIER_COMMAND|'0' + * video normal size KEY_MODIFIER_COMMAND|'1' + * video double size KEY_MODIFIER_COMMAND|'2' + * video fit to screen KEY_MODIFIER_COMMAND|'3' + * minimize window KEY_MODIFIER_COMMAND|'m' + * quit application KEY_MODIFIER_COMMAND|'q' + * close window KEY_MODIFIER_COMMAND|'w' + * streaming wizard KEY_MODIFIER_COMMAND|KEY_MODIFIER_SHIFT|'w' + * show controller KEY_MODIFIER_COMMAND|KEY_MODIFIER_SHIFT|'c' + * show playlist KEY_MODIFIER_COMMAND|KEY_MODIFIER_SHIFT|'p' + * show info KEY_MODIFIER_COMMAND|'i' + * show extended controls KEY_MODIFIER_COMMAND|'e' + * show equaliser KEY_MODIFIER_COMMAND|KEY_MODIFIER_SHIFT|'e' + * show bookmarks KEY_MODIFIER_COMMAND|'b' + * show messages KEY_MODIFIER_COMMAND|KEY_MODIFIER_SHIFT|'m' + * show errors and warnings KEY_MODIFIER_COMMAND|KEY_MODIFIER_CTRL|'m' + * help KEY_MODIFIER_COMMAND|'?' + * readme / FAQ KEY_MODIFIER_COMMAND|KEY_MODIFIER_ALT|'?' + */ +# define KEY_TOGGLE_FULLSCREEN KEY_MODIFIER_COMMAND|'f' +# define KEY_LEAVE_FULLSCREEN KEY_ESC +# define KEY_PLAY_PAUSE KEY_MODIFIER_COMMAND|'p' +# define KEY_PAUSE KEY_UNSET +# define KEY_PLAY KEY_UNSET +# define KEY_FASTER KEY_MODIFIER_COMMAND|'=' +# define KEY_SLOWER KEY_MODIFIER_COMMAND|'-' +# define KEY_NEXT KEY_MODIFIER_COMMAND|KEY_RIGHT +# define KEY_PREV KEY_MODIFIER_COMMAND|KEY_LEFT +# define KEY_STOP KEY_MODIFIER_COMMAND|'.' +# define KEY_POSITION 't' +# define KEY_JUMP_MEXTRASHORT KEY_MODIFIER_COMMAND|KEY_MODIFIER_CTRL|KEY_LEFT +# define KEY_JUMP_PEXTRASHORT KEY_MODIFIER_COMMAND|KEY_MODIFIER_CTRL|KEY_RIGHT +# define KEY_JUMP_MSHORT KEY_MODIFIER_COMMAND|KEY_MODIFIER_ALT|KEY_LEFT +# define KEY_JUMP_PSHORT KEY_MODIFIER_COMMAND|KEY_MODIFIER_ALT|KEY_RIGHT +# define KEY_JUMP_MMEDIUM KEY_MODIFIER_COMMAND|KEY_MODIFIER_SHIFT|KEY_LEFT +# define KEY_JUMP_PMEDIUM KEY_MODIFIER_COMMAND|KEY_MODIFIER_SHIFT|KEY_RIGHT +# define KEY_JUMP_MLONG KEY_MODIFIER_COMMAND|KEY_MODIFIER_SHIFT|KEY_MODIFIER_ALT|KEY_LEFT +# define KEY_JUMP_PLONG KEY_MODIFIER_COMMAND|KEY_MODIFIER_SHIFT|KEY_MODIFIER_ALT|KEY_RIGHT +# define KEY_NAV_ACTIVATE KEY_ENTER +# define KEY_NAV_UP KEY_UP +# define KEY_NAV_DOWN KEY_DOWN +# define KEY_NAV_LEFT KEY_LEFT +# define KEY_NAV_RIGHT KEY_RIGHT +# define KEY_QUIT KEY_MODIFIER_COMMAND|'q' +# define KEY_VOL_UP KEY_MODIFIER_COMMAND|KEY_UP +# define KEY_VOL_DOWN KEY_MODIFIER_COMMAND|KEY_DOWN +# define KEY_VOL_MUTE KEY_MODIFIER_COMMAND|KEY_MODIFIER_ALT|KEY_DOWN +# define KEY_SUBDELAY_UP 'j' +# define KEY_SUBDELAY_DOWN 'h' +# define KEY_AUDIODELAY_UP 'g' +# define KEY_AUDIODELAY_DOWN 'f' +# define KEY_AUDIO_TRACK 'l' +# define KEY_SUBTITLE_TRACK 's' +# define KEY_ASPECT_RATIO 'a' +# define KEY_CROP 'c' +# define KEY_DEINTERLACE 'd' +# define KEY_INTF_SHOW 'i' +# define KEY_INTF_HIDE KEY_MODIFIER_SHIFT|'i' +# define KEY_DISC_MENU KEY_MODIFIER_CTRL|'m' +# define KEY_TITLE_PREV KEY_MODIFIER_CTRL|'p' +# define KEY_TITLE_NEXT KEY_MODIFIER_CTRL|'n' +# define KEY_CHAPTER_PREV KEY_MODIFIER_CTRL|'u' +# define KEY_CHAPTER_NEXT KEY_MODIFIER_CTRL|'d' +# define KEY_SNAPSHOT KEY_MODIFIER_COMMAND|KEY_MODIFIER_ALT|'s' +# define KEY_ZOOM 'z' +# define KEY_UNZOOM KEY_MODIFIER_SHIFT|'z' +# define KEY_RANDOM 'r' +# define KEY_LOOP KEY_MODIFIER_SHIFT|'l' + +# define KEY_CROP_TOP KEY_MODIFIER_ALT|'i' +# define KEY_UNCROP_TOP KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT|'i' +# define KEY_CROP_LEFT KEY_MODIFIER_ALT|'j' +# define KEY_UNCROP_LEFT KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT|'j' +# define KEY_CROP_BOTTOM KEY_MODIFIER_ALT|'k' +# define KEY_UNCROP_BOTTOM KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT|'k' +# define KEY_CROP_RIGHT KEY_MODIFIER_ALT|'l' +# define KEY_UNCROP_RIGHT KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT|'l' + +/* the macosx-interface already has bindings */ +# define KEY_ZOOM_QUARTER KEY_UNSET +# define KEY_ZOOM_HALF KEY_UNSET +# define KEY_ZOOM_ORIGINAL KEY_UNSET +# define KEY_ZOOM_DOUBLE KEY_UNSET + +# define KEY_SET_BOOKMARK1 KEY_MODIFIER_COMMAND|KEY_F1 +# define KEY_SET_BOOKMARK2 KEY_MODIFIER_COMMAND|KEY_F2 +# define KEY_SET_BOOKMARK3 KEY_MODIFIER_COMMAND|KEY_F3 +# define KEY_SET_BOOKMARK4 KEY_MODIFIER_COMMAND|KEY_F4 +# define KEY_SET_BOOKMARK5 KEY_MODIFIER_COMMAND|KEY_F5 +# define KEY_SET_BOOKMARK6 KEY_MODIFIER_COMMAND|KEY_F6 +# define KEY_SET_BOOKMARK7 KEY_MODIFIER_COMMAND|KEY_F7 +# define KEY_SET_BOOKMARK8 KEY_MODIFIER_COMMAND|KEY_F8 +# define KEY_SET_BOOKMARK9 KEY_UNSET +# define KEY_SET_BOOKMARK10 KEY_UNSET +# define KEY_PLAY_BOOKMARK1 KEY_F1 +# define KEY_PLAY_BOOKMARK2 KEY_F2 +# define KEY_PLAY_BOOKMARK3 KEY_F3 +# define KEY_PLAY_BOOKMARK4 KEY_F4 +# define KEY_PLAY_BOOKMARK5 KEY_F5 +# define KEY_PLAY_BOOKMARK6 KEY_F6 +# define KEY_PLAY_BOOKMARK7 KEY_F7 +# define KEY_PLAY_BOOKMARK8 KEY_F8 +# define KEY_PLAY_BOOKMARK9 KEY_UNSET +# define KEY_PLAY_BOOKMARK10 KEY_UNSET +# define KEY_HISTORY_BACK KEY_MODIFIER_COMMAND|'[' +# define KEY_HISTORY_FORWARD KEY_MODIFIER_COMMAND|']' +# define KEY_RECORD KEY_MODIFIER_COMMAND|KEY_MODIFIER_SHIFT|'r' +# define KEY_DUMP KEY_MODIFIER_COMMAND|KEY_MODIFIER_SHIFT|'d' +# define KEY_WALLPAPER KEY_MODIFIER_COMMAND|'w' + +# define KEY_MENU_ON KEY_MODIFIER_ALT|'m' +# define KEY_MENU_OFF KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT|'m' +# define KEY_MENU_RIGHT KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT|KEY_RIGHT +# define KEY_MENU_LEFT KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT|KEY_LEFT +# define KEY_MENU_UP KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT|KEY_UP +# define KEY_MENU_DOWN KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT|KEY_DOWN +# define KEY_MENU_SELECT KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT|KEY_ENTER + +#else +# define KEY_TOGGLE_FULLSCREEN 'f' +# define KEY_LEAVE_FULLSCREEN KEY_ESC +# define KEY_PLAY_PAUSE KEY_SPACE +# define KEY_PAUSE KEY_UNSET +# define KEY_PLAY KEY_UNSET +# define KEY_FASTER '+' +# define KEY_SLOWER '-' +# define KEY_NEXT 'n' +# define KEY_PREV 'p' +# define KEY_STOP 's' +# define KEY_POSITION 't' +# define KEY_JUMP_MEXTRASHORT KEY_MODIFIER_SHIFT|KEY_LEFT +# define KEY_JUMP_PEXTRASHORT KEY_MODIFIER_SHIFT|KEY_RIGHT +# define KEY_JUMP_MSHORT KEY_MODIFIER_ALT|KEY_LEFT +# define KEY_JUMP_PSHORT KEY_MODIFIER_ALT|KEY_RIGHT +# define KEY_JUMP_MMEDIUM KEY_MODIFIER_CTRL|KEY_LEFT +# define KEY_JUMP_PMEDIUM KEY_MODIFIER_CTRL|KEY_RIGHT +# define KEY_JUMP_MLONG KEY_MODIFIER_CTRL|KEY_MODIFIER_ALT|KEY_LEFT +# define KEY_JUMP_PLONG KEY_MODIFIER_CTRL|KEY_MODIFIER_ALT|KEY_RIGHT +# define KEY_NAV_ACTIVATE KEY_ENTER +# define KEY_NAV_UP KEY_UP +# define KEY_NAV_DOWN KEY_DOWN +# define KEY_NAV_LEFT KEY_LEFT +# define KEY_NAV_RIGHT KEY_RIGHT +# define KEY_QUIT KEY_MODIFIER_CTRL|'q' +# define KEY_VOL_UP KEY_MODIFIER_CTRL|KEY_UP +# define KEY_VOL_DOWN KEY_MODIFIER_CTRL|KEY_DOWN +# define KEY_VOL_MUTE 'm' +# define KEY_SUBDELAY_UP 'h' +# define KEY_SUBDELAY_DOWN 'g' +# define KEY_AUDIODELAY_UP 'k' +# define KEY_AUDIODELAY_DOWN 'j' +# define KEY_RANDOM 'r' +# define KEY_LOOP 'l' + +# define KEY_AUDIO_TRACK 'b' +# define KEY_SUBTITLE_TRACK 'v' +# define KEY_ASPECT_RATIO 'a' +# define KEY_CROP 'c' +# define KEY_DEINTERLACE 'd' +# define KEY_INTF_SHOW 'i' +# define KEY_INTF_HIDE KEY_MODIFIER_SHIFT|'i' +# define KEY_DISC_MENU KEY_MODIFIER_ALT|'r' +# define KEY_TITLE_PREV KEY_MODIFIER_ALT|'o' +# define KEY_TITLE_NEXT KEY_MODIFIER_ALT|'b' +# define KEY_CHAPTER_PREV KEY_MODIFIER_ALT|'p' +# define KEY_CHAPTER_NEXT KEY_MODIFIER_ALT|'n' +# define KEY_SNAPSHOT KEY_MODIFIER_CTRL|KEY_MODIFIER_ALT|'s' + +# define KEY_ZOOM 'z' +# define KEY_UNZOOM KEY_MODIFIER_SHIFT|'z' + +# define KEY_CROP_TOP KEY_MODIFIER_ALT|'i' +# define KEY_UNCROP_TOP KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT|'i' +# define KEY_CROP_LEFT KEY_MODIFIER_ALT|'j' +# define KEY_UNCROP_LEFT KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT|'j' +# define KEY_CROP_BOTTOM KEY_MODIFIER_ALT|'k' +# define KEY_UNCROP_BOTTOM KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT|'k' +# define KEY_CROP_RIGHT KEY_MODIFIER_ALT|'l' +# define KEY_UNCROP_RIGHT KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT|'l' + +# define KEY_ZOOM_QUARTER KEY_MODIFIER_CTRL|'1' +# define KEY_ZOOM_HALF KEY_MODIFIER_CTRL|'2' +# define KEY_ZOOM_ORIGINAL KEY_MODIFIER_CTRL|'3' +# define KEY_ZOOM_DOUBLE KEY_MODIFIER_CTRL|'4' + +# define KEY_SET_BOOKMARK1 KEY_MODIFIER_CTRL|KEY_F1 +# define KEY_SET_BOOKMARK2 KEY_MODIFIER_CTRL|KEY_F2 +# define KEY_SET_BOOKMARK3 KEY_MODIFIER_CTRL|KEY_F3 +# define KEY_SET_BOOKMARK4 KEY_MODIFIER_CTRL|KEY_F4 +# define KEY_SET_BOOKMARK5 KEY_MODIFIER_CTRL|KEY_F5 +# define KEY_SET_BOOKMARK6 KEY_MODIFIER_CTRL|KEY_F6 +# define KEY_SET_BOOKMARK7 KEY_MODIFIER_CTRL|KEY_F7 +# define KEY_SET_BOOKMARK8 KEY_MODIFIER_CTRL|KEY_F8 +# define KEY_SET_BOOKMARK9 KEY_MODIFIER_CTRL|KEY_F9 +# define KEY_SET_BOOKMARK10 KEY_MODIFIER_CTRL|KEY_F10 +# define KEY_PLAY_BOOKMARK1 KEY_F1 +# define KEY_PLAY_BOOKMARK2 KEY_F2 +# define KEY_PLAY_BOOKMARK3 KEY_F3 +# define KEY_PLAY_BOOKMARK4 KEY_F4 +# define KEY_PLAY_BOOKMARK5 KEY_F5 +# define KEY_PLAY_BOOKMARK6 KEY_F6 +# define KEY_PLAY_BOOKMARK7 KEY_F7 +# define KEY_PLAY_BOOKMARK8 KEY_F8 +# define KEY_PLAY_BOOKMARK9 KEY_F9 +# define KEY_PLAY_BOOKMARK10 KEY_F10 +# define KEY_HISTORY_BACK KEY_MODIFIER_ALT|'g' +# define KEY_HISTORY_FORWARD KEY_MODIFIER_ALT|'h' +# define KEY_RECORD KEY_MODIFIER_CTRL|KEY_MODIFIER_SHIFT|'r' +# define KEY_DUMP KEY_MODIFIER_CTRL|KEY_MODIFIER_SHIFT|'d' +# define KEY_WALLPAPER 'w' + +# define KEY_MENU_ON KEY_MODIFIER_ALT|'m' +# define KEY_MENU_OFF KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT|'m' +# define KEY_MENU_RIGHT KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT|KEY_RIGHT +# define KEY_MENU_LEFT KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT|KEY_LEFT +# define KEY_MENU_UP KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT|KEY_UP +# define KEY_MENU_DOWN KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT|KEY_DOWN +# define KEY_MENU_SELECT KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT|KEY_ENTER +# define KEY_AUDIODEVICE_CYCLE KEY_MODIFIER_ALT|'a' +#endif + + add_key( "key-toggle-fullscreen", KEY_TOGGLE_FULLSCREEN, NULL, TOGGLE_FULLSCREEN_KEY_TEXT, + TOGGLE_FULLSCREEN_KEY_LONGTEXT, false ); + add_deprecated_alias( "key-fullscreen" ); /*deprecated since 0.9.0 */ + add_key( "key-leave-fullscreen", KEY_LEAVE_FULLSCREEN, NULL, LEAVE_FULLSCREEN_KEY_TEXT, + LEAVE_FULLSCREEN_KEY_LONGTEXT, false ); + add_key( "key-play-pause", KEY_PLAY_PAUSE, NULL, PLAY_PAUSE_KEY_TEXT, + PLAY_PAUSE_KEY_LONGTEXT, false ); + add_key( "key-pause", KEY_PAUSE, NULL, PAUSE_KEY_TEXT, + PAUSE_KEY_LONGTEXT, true ); + add_key( "key-play", KEY_PLAY, NULL, PLAY_KEY_TEXT, + PLAY_KEY_LONGTEXT, true ); + add_key( "key-faster", KEY_FASTER, NULL, FASTER_KEY_TEXT, + FASTER_KEY_LONGTEXT, false ); + add_key( "key-slower", KEY_SLOWER, NULL, SLOWER_KEY_TEXT, + SLOWER_KEY_LONGTEXT, false ); + add_key( "key-next", KEY_NEXT, NULL, NEXT_KEY_TEXT, + NEXT_KEY_LONGTEXT, false ); + add_key( "key-prev", KEY_PREV, NULL, PREV_KEY_TEXT, + PREV_KEY_LONGTEXT, false ); + add_key( "key-stop", KEY_STOP, NULL, STOP_KEY_TEXT, + STOP_KEY_LONGTEXT, false ); + add_key( "key-position", KEY_POSITION, NULL, POSITION_KEY_TEXT, + POSITION_KEY_LONGTEXT, true ); + add_key( "key-jump-extrashort", KEY_JUMP_MEXTRASHORT, NULL, + JBEXTRASHORT_KEY_TEXT, JBEXTRASHORT_KEY_LONGTEXT, false ); + add_key( "key-jump+extrashort", KEY_JUMP_PEXTRASHORT, NULL, + JFEXTRASHORT_KEY_TEXT, JFEXTRASHORT_KEY_LONGTEXT, false ); + add_key( "key-jump-short", KEY_JUMP_MSHORT, NULL, JBSHORT_KEY_TEXT, + JBSHORT_KEY_LONGTEXT, false ); + add_key( "key-jump+short", KEY_JUMP_PSHORT, NULL, JFSHORT_KEY_TEXT, + JFSHORT_KEY_LONGTEXT, false ); + add_key( "key-jump-medium", KEY_JUMP_MMEDIUM, NULL, JBMEDIUM_KEY_TEXT, + JBMEDIUM_KEY_LONGTEXT, false ); + add_key( "key-jump+medium", KEY_JUMP_PMEDIUM, NULL, JFMEDIUM_KEY_TEXT, + JFMEDIUM_KEY_LONGTEXT, false ); + add_key( "key-jump-long", KEY_JUMP_MLONG, NULL, JBLONG_KEY_TEXT, + JBLONG_KEY_LONGTEXT, false ); + add_key( "key-jump+long", KEY_JUMP_PLONG, NULL, JFLONG_KEY_TEXT, + JFLONG_KEY_LONGTEXT, false ); + add_key( "key-nav-activate", KEY_NAV_ACTIVATE, NULL, NAV_ACTIVATE_KEY_TEXT, + NAV_ACTIVATE_KEY_LONGTEXT, true ); + add_key( "key-nav-up", KEY_NAV_UP, NULL, NAV_UP_KEY_TEXT, + NAV_UP_KEY_LONGTEXT, true ); + add_key( "key-nav-down", KEY_NAV_DOWN, NULL, NAV_DOWN_KEY_TEXT, + NAV_DOWN_KEY_LONGTEXT, true ); + add_key( "key-nav-left", KEY_NAV_LEFT, NULL, NAV_LEFT_KEY_TEXT, + NAV_LEFT_KEY_LONGTEXT, true ); + add_key( "key-nav-right", KEY_NAV_RIGHT, NULL, NAV_RIGHT_KEY_TEXT, + NAV_RIGHT_KEY_LONGTEXT, true ); + + add_key( "key-disc-menu", KEY_DISC_MENU, NULL, DISC_MENU_TEXT, + DISC_MENU_LONGTEXT, true ); + add_key( "key-title-prev", KEY_TITLE_PREV, NULL, TITLE_PREV_TEXT, + TITLE_PREV_LONGTEXT, true ); + add_key( "key-title-next", KEY_TITLE_NEXT, NULL, TITLE_NEXT_TEXT, + TITLE_NEXT_LONGTEXT, true ); + add_key( "key-chapter-prev", KEY_CHAPTER_PREV, NULL, CHAPTER_PREV_TEXT, + CHAPTER_PREV_LONGTEXT, true ); + add_key( "key-chapter-next", KEY_CHAPTER_NEXT, NULL, CHAPTER_NEXT_TEXT, + CHAPTER_NEXT_LONGTEXT, true ); + add_key( "key-quit", KEY_QUIT, NULL, QUIT_KEY_TEXT, + QUIT_KEY_LONGTEXT, false ); + add_key( "key-vol-up", KEY_VOL_UP, NULL, VOL_UP_KEY_TEXT, + VOL_UP_KEY_LONGTEXT, false ); + add_key( "key-vol-down", KEY_VOL_DOWN, NULL, VOL_DOWN_KEY_TEXT, + VOL_DOWN_KEY_LONGTEXT, false ); + add_key( "key-vol-mute", KEY_VOL_MUTE, NULL, VOL_MUTE_KEY_TEXT, + VOL_MUTE_KEY_LONGTEXT, false ); + add_key( "key-subdelay-up", KEY_SUBDELAY_UP, NULL, + SUBDELAY_UP_KEY_TEXT, SUBDELAY_UP_KEY_LONGTEXT, true ); + add_key( "key-subdelay-down", KEY_SUBDELAY_DOWN, NULL, + SUBDELAY_DOWN_KEY_TEXT, SUBDELAY_DOWN_KEY_LONGTEXT, true ); + add_key( "key-audiodelay-up", KEY_AUDIODELAY_UP, NULL, + AUDIODELAY_UP_KEY_TEXT, AUDIODELAY_UP_KEY_LONGTEXT, true ); + add_key( "key-audiodelay-down", KEY_AUDIODELAY_DOWN, NULL, + AUDIODELAY_DOWN_KEY_TEXT, AUDIODELAY_DOWN_KEY_LONGTEXT, true ); + add_key( "key-audio-track", KEY_AUDIO_TRACK, NULL, AUDIO_TRACK_KEY_TEXT, + AUDIO_TRACK_KEY_LONGTEXT, false ); + add_key( "key-audiodevice-cycle", KEY_STOP, NULL, AUDI_DEVICE_CYCLE_KEY_TEXT, + AUDI_DEVICE_CYCLE_KEY_LONGTEXT, false ); + add_key( "key-subtitle-track", KEY_SUBTITLE_TRACK, NULL, + SUBTITLE_TRACK_KEY_TEXT, SUBTITLE_TRACK_KEY_LONGTEXT, false ); + add_key( "key-aspect-ratio", KEY_ASPECT_RATIO, NULL, + ASPECT_RATIO_KEY_TEXT, ASPECT_RATIO_KEY_LONGTEXT, false ); + add_key( "key-crop", KEY_CROP, NULL, + CROP_KEY_TEXT, CROP_KEY_LONGTEXT, false ); + add_key( "key-deinterlace", KEY_DEINTERLACE, NULL, + DEINTERLACE_KEY_TEXT, DEINTERLACE_KEY_LONGTEXT, false ); + add_key( "key-intf-show", KEY_INTF_SHOW, NULL, + INTF_SHOW_KEY_TEXT, INTF_SHOW_KEY_LONGTEXT, true ); + add_key( "key-intf-hide", KEY_INTF_HIDE, NULL, + INTF_HIDE_KEY_TEXT, INTF_HIDE_KEY_LONGTEXT, true ); + add_key( "key-snapshot", KEY_SNAPSHOT, NULL, + SNAP_KEY_TEXT, SNAP_KEY_LONGTEXT, true ); + add_key( "key-history-back", KEY_HISTORY_BACK, NULL, HISTORY_BACK_TEXT, + HISTORY_BACK_LONGTEXT, true ); + add_key( "key-history-forward", KEY_HISTORY_FORWARD, NULL, + HISTORY_FORWARD_TEXT, HISTORY_FORWARD_LONGTEXT, true ); + add_key( "key-record", KEY_RECORD, NULL, + RECORD_KEY_TEXT, RECORD_KEY_LONGTEXT, true ); + add_key( "key-dump", KEY_DUMP, NULL, + DUMP_KEY_TEXT, DUMP_KEY_LONGTEXT, true ); + add_key( "key-zoom", KEY_ZOOM, NULL, + ZOOM_KEY_TEXT, ZOOM_KEY_LONGTEXT, true ); + add_key( "key-unzoom", KEY_UNZOOM, NULL, + UNZOOM_KEY_TEXT, UNZOOM_KEY_LONGTEXT, true ); + add_key( "key-wallpaper", KEY_WALLPAPER, NULL, WALLPAPER_KEY_TEXT, + WALLPAPER_KEY_LONGTEXT, false ); + + add_key( "key-menu-on", KEY_MENU_ON, NULL, + MENU_ON_KEY_TEXT, MENU_ON_KEY_LONGTEXT, true ); + add_key( "key-menu-off", KEY_MENU_OFF, NULL, + MENU_OFF_KEY_TEXT, MENU_OFF_KEY_LONGTEXT, true ); + add_key( "key-menu-right", KEY_MENU_RIGHT, NULL, + MENU_RIGHT_KEY_TEXT, MENU_RIGHT_KEY_LONGTEXT, true ); + add_key( "key-menu-left", KEY_MENU_LEFT, NULL, + MENU_LEFT_KEY_TEXT, MENU_LEFT_KEY_LONGTEXT, true ); + add_key( "key-menu-up", KEY_MENU_UP, NULL, + MENU_UP_KEY_TEXT, MENU_UP_KEY_LONGTEXT, true ); + add_key( "key-menu-down", KEY_MENU_DOWN, NULL, + MENU_DOWN_KEY_TEXT, MENU_DOWN_KEY_LONGTEXT, true ); + add_key( "key-menu-select", KEY_MENU_SELECT, NULL, + MENU_SELECT_KEY_TEXT, MENU_SELECT_KEY_LONGTEXT, true ); + + add_key( "key-crop-top", KEY_CROP_TOP, NULL, + CROP_TOP_KEY_TEXT, CROP_TOP_KEY_LONGTEXT, true ); + add_key( "key-uncrop-top", KEY_UNCROP_TOP, NULL, + UNCROP_TOP_KEY_TEXT, UNCROP_TOP_KEY_LONGTEXT, true ); + add_key( "key-crop-left", KEY_CROP_LEFT, NULL, + CROP_LEFT_KEY_TEXT, CROP_LEFT_KEY_LONGTEXT, true ); + add_key( "key-uncrop-left", KEY_UNCROP_LEFT, NULL, + UNCROP_LEFT_KEY_TEXT, UNCROP_LEFT_KEY_LONGTEXT, true ); + add_key( "key-crop-bottom", KEY_CROP_BOTTOM, NULL, + CROP_BOTTOM_KEY_TEXT, CROP_BOTTOM_KEY_LONGTEXT, true ); + add_key( "key-uncrop-bottom", KEY_UNCROP_BOTTOM, NULL, + UNCROP_BOTTOM_KEY_TEXT, UNCROP_BOTTOM_KEY_LONGTEXT, true ); + add_key( "key-crop-right", KEY_CROP_RIGHT, NULL, + CROP_RIGHT_KEY_TEXT, CROP_RIGHT_KEY_LONGTEXT, true ); + add_key( "key-uncrop-right", KEY_UNCROP_RIGHT, NULL, + UNCROP_RIGHT_KEY_TEXT, UNCROP_RIGHT_KEY_LONGTEXT, true ); + add_key( "key-random", KEY_RANDOM, NULL, + RANDOM_KEY_TEXT, RANDOM_KEY_LONGTEXT, false ); + add_key( "key-loop", KEY_LOOP, NULL, + LOOP_KEY_TEXT, LOOP_KEY_LONGTEXT, false ); + + set_section ( N_("Zoom" ), NULL ); + add_key( "key-zoom-quarter", KEY_ZOOM_QUARTER, NULL, + ZOOM_QUARTER_KEY_TEXT, NULL, false ); + add_key( "key-zoom-half", KEY_ZOOM_HALF, NULL, + ZOOM_HALF_KEY_TEXT, NULL, false ); + add_key( "key-zoom-original", KEY_ZOOM_ORIGINAL, NULL, + ZOOM_ORIGINAL_KEY_TEXT, NULL, false ); + add_key( "key-zoom-double", KEY_ZOOM_DOUBLE, NULL, + ZOOM_DOUBLE_KEY_TEXT, NULL, false ); + + set_section ( N_("Jump sizes" ), NULL ); + add_integer( "extrashort-jump-size", 3, NULL, JIEXTRASHORT_TEXT, + JIEXTRASHORT_LONGTEXT, false ); + add_integer( "short-jump-size", 10, NULL, JISHORT_TEXT, + JISHORT_LONGTEXT, false ); + add_integer( "medium-jump-size", 60, NULL, JIMEDIUM_TEXT, + JIMEDIUM_LONGTEXT, false ); + add_integer( "long-jump-size", 300, NULL, JILONG_TEXT, + JILONG_LONGTEXT, false ); + + /* HACK so these don't get displayed */ + set_category( -1 ); + set_subcategory( -1 ); + add_key( "key-set-bookmark1", KEY_SET_BOOKMARK1, NULL, + SET_BOOKMARK1_KEY_TEXT, SET_BOOKMARK_KEY_LONGTEXT, true ); + add_key( "key-set-bookmark2", KEY_SET_BOOKMARK2, NULL, + SET_BOOKMARK2_KEY_TEXT, SET_BOOKMARK_KEY_LONGTEXT, true ); + add_key( "key-set-bookmark3", KEY_SET_BOOKMARK3, NULL, + SET_BOOKMARK3_KEY_TEXT, SET_BOOKMARK_KEY_LONGTEXT, true ); + add_key( "key-set-bookmark4", KEY_SET_BOOKMARK4, NULL, + SET_BOOKMARK4_KEY_TEXT, SET_BOOKMARK_KEY_LONGTEXT, true ); + add_key( "key-set-bookmark5", KEY_SET_BOOKMARK5, NULL, + SET_BOOKMARK5_KEY_TEXT, SET_BOOKMARK_KEY_LONGTEXT, true ); + add_key( "key-set-bookmark6", KEY_SET_BOOKMARK6, NULL, + SET_BOOKMARK6_KEY_TEXT, SET_BOOKMARK_KEY_LONGTEXT, true ); + add_key( "key-set-bookmark7", KEY_SET_BOOKMARK7, NULL, + SET_BOOKMARK7_KEY_TEXT, SET_BOOKMARK_KEY_LONGTEXT, true ); + add_key( "key-set-bookmark8", KEY_SET_BOOKMARK8, NULL, + SET_BOOKMARK8_KEY_TEXT, SET_BOOKMARK_KEY_LONGTEXT, true ); + add_key( "key-set-bookmark9", KEY_SET_BOOKMARK9, NULL, + SET_BOOKMARK9_KEY_TEXT, SET_BOOKMARK_KEY_LONGTEXT, true ); + add_key( "key-set-bookmark10", KEY_SET_BOOKMARK10, NULL, + SET_BOOKMARK10_KEY_TEXT, SET_BOOKMARK_KEY_LONGTEXT, true ); + add_key( "key-play-bookmark1", KEY_PLAY_BOOKMARK1, NULL, + PLAY_BOOKMARK1_KEY_TEXT, PLAY_BOOKMARK_KEY_LONGTEXT, true ); + add_key( "key-play-bookmark2", KEY_PLAY_BOOKMARK2, NULL, + PLAY_BOOKMARK2_KEY_TEXT, PLAY_BOOKMARK_KEY_LONGTEXT, true ); + add_key( "key-play-bookmark3", KEY_PLAY_BOOKMARK3, NULL, + PLAY_BOOKMARK3_KEY_TEXT, PLAY_BOOKMARK_KEY_LONGTEXT, true ); + add_key( "key-play-bookmark4", KEY_PLAY_BOOKMARK4, NULL, + PLAY_BOOKMARK4_KEY_TEXT, PLAY_BOOKMARK_KEY_LONGTEXT, true ); + add_key( "key-play-bookmark5", KEY_PLAY_BOOKMARK5, NULL, + PLAY_BOOKMARK5_KEY_TEXT, PLAY_BOOKMARK_KEY_LONGTEXT, true ); + add_key( "key-play-bookmark6", KEY_PLAY_BOOKMARK6, NULL, + PLAY_BOOKMARK6_KEY_TEXT, PLAY_BOOKMARK_KEY_LONGTEXT, true ); + add_key( "key-play-bookmark7", KEY_PLAY_BOOKMARK7, NULL, + PLAY_BOOKMARK7_KEY_TEXT, PLAY_BOOKMARK_KEY_LONGTEXT, true ); + add_key( "key-play-bookmark8", KEY_PLAY_BOOKMARK8, NULL, + PLAY_BOOKMARK8_KEY_TEXT, PLAY_BOOKMARK_KEY_LONGTEXT, true ); + add_key( "key-play-bookmark9", KEY_PLAY_BOOKMARK9, NULL, + PLAY_BOOKMARK9_KEY_TEXT, PLAY_BOOKMARK_KEY_LONGTEXT, true ); + add_key( "key-play-bookmark10", KEY_PLAY_BOOKMARK10, NULL, + PLAY_BOOKMARK10_KEY_TEXT, PLAY_BOOKMARK_KEY_LONGTEXT, true ); + + + add_string( "bookmark1", NULL, NULL, + BOOKMARK1_TEXT, BOOKMARK_LONGTEXT, false ); + add_string( "bookmark2", NULL, NULL, + BOOKMARK2_TEXT, BOOKMARK_LONGTEXT, false ); + add_string( "bookmark3", NULL, NULL, + BOOKMARK3_TEXT, BOOKMARK_LONGTEXT, false ); + add_string( "bookmark4", NULL, NULL, + BOOKMARK4_TEXT, BOOKMARK_LONGTEXT, false ); + add_string( "bookmark5", NULL, NULL, + BOOKMARK5_TEXT, BOOKMARK_LONGTEXT, false ); + add_string( "bookmark6", NULL, NULL, + BOOKMARK6_TEXT, BOOKMARK_LONGTEXT, false ); + add_string( "bookmark7", NULL, NULL, + BOOKMARK7_TEXT, BOOKMARK_LONGTEXT, false ); + add_string( "bookmark8", NULL, NULL, + BOOKMARK8_TEXT, BOOKMARK_LONGTEXT, false ); + add_string( "bookmark9", NULL, NULL, + BOOKMARK9_TEXT, BOOKMARK_LONGTEXT, false ); + add_string( "bookmark10", NULL, NULL, + BOOKMARK10_TEXT, BOOKMARK_LONGTEXT, false ); + +#define HELP_TEXT \ + N_("print help for VLC (can be combined with --advanced and " \ + "--help-verbose)") +#define FULL_HELP_TEXT \ + N_("Exhaustive help for VLC and its modules") +#define LONGHELP_TEXT \ + N_("print help for VLC and all its modules (can be combined with " \ + "--advanced and --help-verbose)") +#define HELP_VERBOSE_TEXT \ + N_("ask for extra verbosity when displaying help") +#define LIST_TEXT \ + N_("print a list of available modules") +#define LIST_VERBOSE_TEXT \ + N_("print a list of available modules with extra detail") +#define MODULE_TEXT \ + N_("print help on a specific module (can be combined with --advanced " \ + "and --help-verbose)") +#define IGNORE_CONFIG_TEXT \ + N_("no configuration option will be loaded nor saved to config file") +#define SAVE_CONFIG_TEXT \ + N_("save the current command line options in the config") +#define RESET_CONFIG_TEXT \ + N_("reset the current config to the default values") +#define CONFIG_TEXT \ + N_("use alternate config file") +#define RESET_PLUGINS_CACHE_TEXT \ + N_("resets the current plugins cache") +#define VERSION_TEXT \ + N_("print version information") + + add_bool( "help", false, NULL, HELP_TEXT, "", false ); + change_short( 'h' ); + change_internal(); + change_unsaveable(); + add_bool( "full-help", false, NULL, FULL_HELP_TEXT, "", false ); + change_short( 'H' ); + change_internal(); + change_unsaveable(); + add_bool( "longhelp", false, NULL, LONGHELP_TEXT, "", false ); + change_internal(); + change_unsaveable(); + add_bool( "help-verbose", false, NULL, HELP_VERBOSE_TEXT, "", + false ); + change_internal(); + change_unsaveable(); + add_bool( "list", false, NULL, LIST_TEXT, "", false ); + change_short( 'l' ); + change_internal(); + change_unsaveable(); + add_bool( "list-verbose", false, NULL, LIST_VERBOSE_TEXT, "", + false ); + change_short( 'l' ); + change_internal(); + change_unsaveable(); + add_string( "module", NULL, NULL, MODULE_TEXT, "", false ); + change_short( 'p' ); + change_internal(); + change_unsaveable(); + add_bool( "ignore-config", false, NULL, IGNORE_CONFIG_TEXT, "", false ); + change_internal(); + change_unsaveable(); + add_bool( "save-config", false, NULL, SAVE_CONFIG_TEXT, "", + false ); + change_internal(); + change_unsaveable(); + add_bool( "reset-config", false, NULL, RESET_CONFIG_TEXT, "", false ); + change_internal(); + change_unsaveable(); + add_bool( "reset-plugins-cache", false, NULL, + RESET_PLUGINS_CACHE_TEXT, "", false ); + change_internal(); + change_unsaveable(); + add_bool( "version", false, NULL, VERSION_TEXT, "", false ); + change_internal(); + change_unsaveable(); + add_string( "config", NULL, NULL, CONFIG_TEXT, "", false ); + change_internal(); + change_unsaveable(); + add_bool( "version", false, NULL, VERSION_TEXT, "", false ); + change_internal(); + change_unsaveable(); + + /* Usage (mainly useful for cmd line stuff) */ + /* add_usage_hint( PLAYLIST_USAGE ); */ + + set_description( N_("main program") ); + set_capability( "main", 100 ); +vlc_module_end(); + +/***************************************************************************** + * End configuration. + *****************************************************************************/ + +/***************************************************************************** + * Initializer for the libvlc instance structure + * storing the action / key associations + *****************************************************************************/ +const struct hotkey libvlc_hotkeys[] = +{ + { "key-quit", ACTIONID_QUIT, 0, }, + { "key-play-pause", ACTIONID_PLAY_PAUSE, 0, }, + { "key-play", ACTIONID_PLAY, 0, }, + { "key-pause", ACTIONID_PAUSE, 0, }, + { "key-stop", ACTIONID_STOP, 0, }, + { "key-position", ACTIONID_POSITION, 0, }, + { "key-jump-extrashort", ACTIONID_JUMP_BACKWARD_EXTRASHORT, 0, }, + { "key-jump+extrashort", ACTIONID_JUMP_FORWARD_EXTRASHORT, 0, }, + { "key-jump-short", ACTIONID_JUMP_BACKWARD_SHORT, 0, }, + { "key-jump+short", ACTIONID_JUMP_FORWARD_SHORT, 0, }, + { "key-jump-medium", ACTIONID_JUMP_BACKWARD_MEDIUM, 0, }, + { "key-jump+medium", ACTIONID_JUMP_FORWARD_MEDIUM, 0, }, + { "key-jump-long", ACTIONID_JUMP_BACKWARD_LONG, 0, }, + { "key-jump+long", ACTIONID_JUMP_FORWARD_LONG, 0, }, + { "key-prev", ACTIONID_PREV, 0, }, + { "key-next", ACTIONID_NEXT, 0, }, + { "key-faster", ACTIONID_FASTER, 0, }, + { "key-slower", ACTIONID_SLOWER, 0, }, + { "key-toggle-fullscreen", ACTIONID_TOGGLE_FULLSCREEN, 0, }, + { "key-leave-fullscreen", ACTIONID_LEAVE_FULLSCREEN, 0, }, + { "key-vol-up", ACTIONID_VOL_UP, 0, }, + { "key-vol-down", ACTIONID_VOL_DOWN, 0, }, + { "key-vol-mute", ACTIONID_VOL_MUTE, 0, }, + { "key-subdelay-down", ACTIONID_SUBDELAY_DOWN, 0, }, + { "key-subdelay-up", ACTIONID_SUBDELAY_UP, 0, }, + { "key-audiodelay-down", ACTIONID_AUDIODELAY_DOWN, 0, }, + { "key-audiodelay-up", ACTIONID_AUDIODELAY_UP, 0, }, + { "key-audio-track", ACTIONID_AUDIO_TRACK, 0, }, + { "key-subtitle-track", ACTIONID_SUBTITLE_TRACK, 0, }, + { "key-aspect-ratio", ACTIONID_ASPECT_RATIO, 0, }, + { "key-crop", ACTIONID_CROP, 0, }, + { "key-deinterlace", ACTIONID_DEINTERLACE, 0, }, + { "key-intf-show", ACTIONID_INTF_SHOW, 0, }, + { "key-intf-hide", ACTIONID_INTF_HIDE, 0, }, + { "key-snapshot", ACTIONID_SNAPSHOT, 0, }, + { "key-zoom", ACTIONID_ZOOM, 0, }, + { "key-unzoom", ACTIONID_UNZOOM, 0, }, + { "key-crop-top", ACTIONID_CROP_TOP, 0, }, + { "key-uncrop-top", ACTIONID_UNCROP_TOP, 0, }, + { "key-crop-left", ACTIONID_CROP_LEFT, 0, }, + { "key-uncrop-left", ACTIONID_UNCROP_LEFT, 0, }, + { "key-crop-bottom", ACTIONID_CROP_BOTTOM, 0, }, + { "key-uncrop-bottom", ACTIONID_UNCROP_BOTTOM, 0, }, + { "key-crop-right", ACTIONID_CROP_RIGHT, 0, }, + { "key-uncrop-right", ACTIONID_UNCROP_RIGHT, 0, }, + { "key-nav-activate", ACTIONID_NAV_ACTIVATE, 0, }, + { "key-nav-up", ACTIONID_NAV_UP, 0, }, + { "key-nav-down", ACTIONID_NAV_DOWN, 0, }, + { "key-nav-left", ACTIONID_NAV_LEFT, 0, }, + { "key-nav-right", ACTIONID_NAV_RIGHT, 0, }, + { "key-disc-menu", ACTIONID_DISC_MENU, 0, }, + { "key-title-prev", ACTIONID_TITLE_PREV, 0, }, + { "key-title-next", ACTIONID_TITLE_NEXT, 0, }, + { "key-chapter-prev", ACTIONID_CHAPTER_PREV, 0, }, + { "key-chapter-next", ACTIONID_CHAPTER_NEXT, 0, }, + { "key-zoom-quarter", ACTIONID_ZOOM_QUARTER, 0, }, + { "key-zoom-half", ACTIONID_ZOOM_HALF, 0, }, + { "key-zoom-original", ACTIONID_ZOOM_ORIGINAL, 0, }, + { "key-zoom-double", ACTIONID_ZOOM_DOUBLE, 0, }, + { "key-set-bookmark1", ACTIONID_SET_BOOKMARK1, 0, }, + { "key-set-bookmark2", ACTIONID_SET_BOOKMARK2, 0, }, + { "key-set-bookmark3", ACTIONID_SET_BOOKMARK3, 0, }, + { "key-set-bookmark4", ACTIONID_SET_BOOKMARK4, 0, }, + { "key-set-bookmark5", ACTIONID_SET_BOOKMARK5, 0, }, + { "key-set-bookmark6", ACTIONID_SET_BOOKMARK6, 0, }, + { "key-set-bookmark7", ACTIONID_SET_BOOKMARK7, 0, }, + { "key-set-bookmark8", ACTIONID_SET_BOOKMARK8, 0, }, + { "key-set-bookmark9", ACTIONID_SET_BOOKMARK9, 0, }, + { "key-set-bookmark10", ACTIONID_SET_BOOKMARK10, 0, }, + { "key-play-bookmark1", ACTIONID_PLAY_BOOKMARK1, 0, }, + { "key-play-bookmark2", ACTIONID_PLAY_BOOKMARK2, 0, }, + { "key-play-bookmark3", ACTIONID_PLAY_BOOKMARK3, 0, }, + { "key-play-bookmark4", ACTIONID_PLAY_BOOKMARK4, 0, }, + { "key-play-bookmark5", ACTIONID_PLAY_BOOKMARK5, 0, }, + { "key-play-bookmark6", ACTIONID_PLAY_BOOKMARK6, 0, }, + { "key-play-bookmark7", ACTIONID_PLAY_BOOKMARK7, 0, }, + { "key-play-bookmark8", ACTIONID_PLAY_BOOKMARK8, 0, }, + { "key-play-bookmark9", ACTIONID_PLAY_BOOKMARK9, 0, }, + { "key-play-bookmark10", ACTIONID_PLAY_BOOKMARK10, 0, }, + { "key-history-back", ACTIONID_HISTORY_BACK, 0, }, + { "key-history-forward", ACTIONID_HISTORY_FORWARD, 0, }, + { "key-record", ACTIONID_RECORD, 0, }, + { "key-dump", ACTIONID_DUMP, 0, }, + { "key-random", ACTIONID_RANDOM, 0, }, + { "key-loop", ACTIONID_LOOP, 0, }, + { "key-wallpaper", ACTIONID_WALLPAPER, 0, }, + { "key-menu-on", ACTIONID_MENU_ON, 0, }, + { "key-menu-off", ACTIONID_MENU_OFF, 0, }, + { "key-menu-right", ACTIONID_MENU_RIGHT, 0, }, + { "key-menu-left", ACTIONID_MENU_LEFT, 0, }, + { "key-menu-up", ACTIONID_MENU_UP, 0, }, + { "key-menu-down", ACTIONID_MENU_DOWN, 0, }, + { "key-menu-select", ACTIONID_MENU_SELECT, 0, }, + { "key-audiodevice-cycle", ACTIONID_AUDIODEVICE_CYCLE, 0, }, + { NULL, 0, 0, } +}; + +const size_t libvlc_hotkeys_size = sizeof (libvlc_hotkeys); diff --git a/VLC/libvlc.c b/VLC/libvlc.c new file mode 100644 index 0000000..624743d --- /dev/null +++ b/VLC/libvlc.c @@ -0,0 +1,2067 @@ +/***************************************************************************** + * libvlc.c: libvlc instances creation and deletion, interfaces handling + ***************************************************************************** + * Copyright (C) 1998-2008 the VideoLAN team + * $Id$ + * + * Authors: Vincent Seguin + * Samuel Hocevar + * Gildas Bazin + * Derk-Jan Hartman + * Rémi Denis-Courmont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/** \file + * This file contains functions to create and destroy libvlc instances + */ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "libvlc_internal.h" +#include "vlc_input.h" + +#include "modules.h" +#include "configuration.h" +//#include "interface/interface.h" + +#include /* ENOMEM */ +#include /* sprintf() */ +#include +#include /* free() */ + +#ifndef WIN32 +# include /* BSD: struct in_addr */ +#endif + +#ifdef HAVE_UNISTD_H +# include +#elif defined( WIN32 ) && !defined( UNDER_CE ) +# include +#endif + +#ifdef WIN32 /* optind, getopt(), included in unistd.h */ +# include "extras/getopt.h" +#endif + +#ifdef HAVE_LOCALE_H +# include +#endif + +#ifdef HAVE_DBUS +/* used for one-instance mode */ +# include +#endif + +#ifdef HAVE_HAL +# include +#endif + +#include "vlc_playlist.h" +#include "vlc_interface.h" + +#include "vlc_aout.h" +#include "aout_internal.h" + +#include "vlc_vout.h" + +#include "vlc_sout.h" +#include "stream_output/stream_output.h" + +#include "vlc_charset.h" + +#include "libvlc.h" + +#include "playlist_internal.h" + +#include "vlc_vlm.h" + +#include + +/***************************************************************************** + * The evil global variables. We handle them with care, don't worry. + *****************************************************************************/ +static libvlc_int_t * p_static_vlc = NULL; +static unsigned i_instances = 0; + +#ifndef WIN32 +static bool b_daemon = false; +#endif + +/***************************************************************************** + * vlc_gc_*. + *****************************************************************************/ +void __vlc_gc_incref( gc_object_t * p_gc ) +{ + assert( p_gc->i_gc_refcount > 0 ); + + /* FIXME: atomic version needed! */ + p_gc->i_gc_refcount ++; +} + +void __vlc_gc_decref( gc_object_t *p_gc ) +{ + assert( p_gc ); + assert( p_gc->i_gc_refcount > 0 ); + + /* FIXME: atomic version needed! */ + p_gc->i_gc_refcount -- ; + + if( p_gc->i_gc_refcount == 0 ) + { + p_gc->pf_destructor( p_gc ); + /* Do not use the p_gc pointer from now on ! */ + } +} + +void +__vlc_gc_init( gc_object_t * p_gc, void (*pf_destructor)( gc_object_t * ), + void * arg) +{ + p_gc->i_gc_refcount = 1; + p_gc->pf_destructor = pf_destructor; + p_gc->p_destructor_arg = arg; +} + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +#if defined( ENABLE_NLS ) && (defined (__APPLE__) || defined (WIN32)) && \ + ( defined( HAVE_GETTEXT ) || defined( HAVE_INCLUDED_GETTEXT ) ) +static void SetLanguage ( char const * ); +#endif +static inline int LoadMessages (void); +static int GetFilenames ( libvlc_int_t *, int, const char *[] ); +static void Help ( libvlc_int_t *, char const *psz_help_name ); +static void Usage ( libvlc_int_t *, char const *psz_module_name ); +static void ListModules ( libvlc_int_t *, bool ); +static void Version ( void ); + +#ifdef WIN32 +static void ShowConsole ( bool ); +static void PauseConsole ( void ); +#endif +static int ConsoleWidth ( void ); + +static int VerboseCallback( vlc_object_t *, char const *, + vlc_value_t, vlc_value_t, void * ); + +static void InitDeviceValues( libvlc_int_t * ); + +/** + * Allocate a libvlc instance, initialize global data if needed + * It also initializes the threading system + */ +libvlc_int_t * libvlc_InternalCreate( void ) +{ + libvlc_int_t *p_libvlc; + libvlc_priv_t *priv; + char *psz_env = NULL; + + /* vlc_threads_init *must* be the first internal call! No other call is + * allowed before the thread system has been initialized. */ + if (vlc_threads_init ()) + return NULL; + + libvlc_global_data_t *p_libvlc_global = vlc_global(); + /* Now that the thread system is initialized, we don't have much, but + * at least we have variables */ + vlc_mutex_t *lock = var_AcquireMutex( "libvlc" ); + if( i_instances == 0 ) + { + /* Guess what CPU we have */ + cpu_flags = CPUCapabilities(); + /* The module bank will be initialized later */ + p_libvlc_global->p_module_bank = NULL; + } + + /* Allocate a libvlc instance object */ + p_libvlc = vlc_custom_create( VLC_OBJECT(p_libvlc_global), sizeof (*priv), + VLC_OBJECT_LIBVLC, "libvlc" ); + if( p_libvlc != NULL ) + i_instances++; + vlc_mutex_unlock( lock ); + + if( p_libvlc == NULL ) + return NULL; + + priv = libvlc_priv (p_libvlc); + priv->p_playlist = NULL; + priv->p_interaction = NULL; + priv->p_vlm = NULL; + p_libvlc->psz_object_name = strdup( "libvlc" ); + + /* Initialize message queue */ + msg_Create( p_libvlc ); + + /* Find verbosity from VLC_VERBOSE environment variable */ + psz_env = getenv( "VLC_VERBOSE" ); + if( psz_env != NULL ) + priv->i_verbose = atoi( psz_env ); + else + priv->i_verbose = 3; +#if defined( HAVE_ISATTY ) && !defined( WIN32 ) + priv->b_color = isatty( 2 ); /* 2 is for stderr */ +#else + priv->b_color = false; +#endif + + /* Announce who we are - Do it only for first instance ? */ + msg_Dbg( p_libvlc, "%s", COPYRIGHT_MESSAGE ); + //msg_Dbg( p_libvlc, "libvlc was configured with %s", CONFIGURE_LINE ); + + /* Initialize mutexes */ + vlc_mutex_init( &priv->timer_lock ); + vlc_mutex_init( &priv->config_lock ); + + /* Store data for the non-reentrant API */ + p_static_vlc = p_libvlc; + + return p_libvlc; +} + +/** + * Initialize a libvlc instance + * This function initializes a previously allocated libvlc instance: + * - CPU detection + * - gettext initialization + * - message queue, module bank and playlist initialization + * - configuration and commandline parsing + */ +int libvlc_InternalInit( libvlc_int_t *p_libvlc, int i_argc, + const char *ppsz_argv[] ) +{ + libvlc_global_data_t *p_libvlc_global = vlc_global(); + libvlc_priv_t *priv = libvlc_priv (p_libvlc); + char p_capabilities[200]; + char * p_tmp = NULL; + char * psz_modules = NULL; + char * psz_parser = NULL; + char * psz_control = NULL; + bool b_exit = false; + int i_ret = VLC_EEXIT; + playlist_t *p_playlist = NULL; + vlc_value_t val; +#if defined( ENABLE_NLS ) \ + && ( defined( HAVE_GETTEXT ) || defined( HAVE_INCLUDED_GETTEXT ) ) +# if defined (WIN32) || defined (__APPLE__) + char * psz_language; +#endif +#endif + + /* System specific initialization code */ + system_Init( p_libvlc, &i_argc, ppsz_argv ); + + /* Get the executable name (similar to the basename command) */ + if( i_argc > 0 && ppsz_argv[0][0] ) + { + free( p_libvlc->psz_object_name ); + + const char *psz_exe = strrchr( ppsz_argv[0], '/' ); + if( psz_exe && *(psz_exe + 1) ) + p_libvlc->psz_object_name = strdup( psz_exe + 1 ); + else + p_libvlc->psz_object_name = strdup( ppsz_argv[0] ); + } + + /* + * Support for gettext + */ + LoadMessages (); + + /* Translate "C" to the language code: "fr", "en_GB", "nl", "ru"... */ + msg_Dbg( p_libvlc, "translation test: code is \"%s\"", _("C") ); + + /* Initialize the module bank and load the configuration of the + * main module. We need to do this at this stage to be able to display + * a short help if required by the user. (short help == main module + * options) */ + module_InitBank( p_libvlc ); + + if( config_LoadCmdLine( p_libvlc, &i_argc, ppsz_argv, true ) ) + { + module_EndBank( p_libvlc ); + return VLC_EGENERIC; + } + + /* Check for short help option */ + if( config_GetInt( p_libvlc, "help" ) > 0 ) + { + Help( p_libvlc, "help" ); + b_exit = true; + i_ret = VLC_EEXITSUCCESS; + } + /* Check for version option */ + else if( config_GetInt( p_libvlc, "version" ) > 0 ) + { + Version(); + b_exit = true; + i_ret = VLC_EEXITSUCCESS; + } + + /* Set the config file stuff */ + priv->psz_configfile = config_GetCustomConfigFile( p_libvlc ); + + /* Check for plugins cache options */ + if( config_GetInt( p_libvlc, "reset-plugins-cache" ) > 0 ) + { + p_libvlc_global->p_module_bank->b_cache_delete = true; + } + + /* Will be re-done properly later on */ + priv->i_verbose = config_GetInt( p_libvlc, "verbose" ); + + /* Check for daemon mode */ +#ifndef WIN32 + if( config_GetInt( p_libvlc, "daemon" ) > 0 ) + { +#ifdef HAVE_DAEMON + char *psz_pidfile = NULL; + + if( daemon( 1, 0) != 0 ) + { + msg_Err( p_libvlc, "Unable to fork vlc to daemon mode" ); + b_exit = true; + } + b_daemon = true; + + /* lets check if we need to write the pidfile */ + psz_pidfile = config_GetPsz( p_libvlc, "pidfile" ); + if( psz_pidfile != NULL ) + { + FILE *pidfile; + pid_t i_pid = getpid (); + msg_Dbg( p_libvlc, "PID is %d, writing it to %s", + i_pid, psz_pidfile ); + pidfile = utf8_fopen( psz_pidfile,"w" ); + if( pidfile != NULL ) + { + utf8_fprintf( pidfile, "%d", (int)i_pid ); + fclose( pidfile ); + } + else + { + msg_Err( p_libvlc, "cannot open pid file for writing: %s (%m)", + psz_pidfile ); + } + } + free( psz_pidfile ); + +#else + pid_t i_pid; + + if( ( i_pid = fork() ) < 0 ) + { + msg_Err( p_libvlc, "unable to fork vlc to daemon mode" ); + b_exit = true; + } + else if( i_pid ) + { + /* This is the parent, exit right now */ + msg_Dbg( p_libvlc, "closing parent process" ); + b_exit = true; + i_ret = VLC_EEXITSUCCESS; + } + else + { + /* We are the child */ + msg_Dbg( p_libvlc, "daemon spawned" ); + close( STDIN_FILENO ); + close( STDOUT_FILENO ); + close( STDERR_FILENO ); + + b_daemon = true; + } +#endif + } +#endif + + if( b_exit ) + { + module_EndBank( p_libvlc ); + return i_ret; + } + + /* Check for translation config option */ +#if defined( ENABLE_NLS ) \ + && ( defined( HAVE_GETTEXT ) || defined( HAVE_INCLUDED_GETTEXT ) ) +# if defined (WIN32) || defined (__APPLE__) + /* This ain't really nice to have to reload the config here but it seems + * the only way to do it. */ + + if( !config_GetInt( p_libvlc, "ignore-config" ) ) + config_LoadConfigFile( p_libvlc, "main" ); + config_LoadCmdLine( p_libvlc, &i_argc, ppsz_argv, true ); + + /* Check if the user specified a custom language */ + psz_language = config_GetPsz( p_libvlc, "language" ); + if( psz_language && *psz_language && strcmp( psz_language, "auto" ) ) + { + bool b_cache_delete = p_libvlc_global->p_module_bank->b_cache_delete; + + /* Reset the default domain */ + SetLanguage( psz_language ); + + /* Translate "C" to the language code: "fr", "en_GB", "nl", "ru"... */ + msg_Dbg( p_libvlc, "translation test: code is \"%s\"", _("C") ); + + module_EndBank( p_libvlc ); + module_InitBank( p_libvlc ); + if( !config_GetInt( p_libvlc, "ignore-config" ) ) + config_LoadConfigFile( p_libvlc, "main" ); + config_LoadCmdLine( p_libvlc, &i_argc, ppsz_argv, true ); + p_libvlc_global->p_module_bank->b_cache_delete = b_cache_delete; + } + free( psz_language ); +# endif +#endif + + /* + * Load the builtins and plugins into the module_bank. + * We have to do it before config_Load*() because this also gets the + * list of configuration options exported by each module and loads their + * default values. + */ + module_LoadBuiltins( p_libvlc ); + module_LoadPlugins( p_libvlc ); + if( p_libvlc->b_die ) + { + b_exit = true; + } + + msg_Dbg( p_libvlc, "module bank initialized, found %i modules", + vlc_internals( p_libvlc_global->p_module_bank )->i_children ); + + /* Check for help on modules */ + if( (p_tmp = config_GetPsz( p_libvlc, "module" )) ) + { + Help( p_libvlc, p_tmp ); + free( p_tmp ); + b_exit = true; + i_ret = VLC_EEXITSUCCESS; + } + /* Check for full help option */ + else if( config_GetInt( p_libvlc, "full-help" ) > 0 ) + { + config_PutInt( p_libvlc, "advanced", 1); + config_PutInt( p_libvlc, "help-verbose", 1); + Help( p_libvlc, "full-help" ); + b_exit = true; + i_ret = VLC_EEXITSUCCESS; + } + /* Check for long help option */ + else if( config_GetInt( p_libvlc, "longhelp" ) > 0 ) + { + Help( p_libvlc, "longhelp" ); + b_exit = true; + i_ret = VLC_EEXITSUCCESS; + } + /* Check for module list option */ + else if( config_GetInt( p_libvlc, "list" ) > 0 ) + { + ListModules( p_libvlc, false ); + b_exit = true; + i_ret = VLC_EEXITSUCCESS; + } + else if( config_GetInt( p_libvlc, "list-verbose" ) > 0 ) + { + ListModules( p_libvlc, true ); + b_exit = true; + i_ret = VLC_EEXITSUCCESS; + } + + /* Check for config file options */ + if( !config_GetInt( p_libvlc, "ignore-config" ) ) + { + if( config_GetInt( p_libvlc, "reset-config" ) > 0 ) + { + config_ResetAll( p_libvlc ); + config_LoadCmdLine( p_libvlc, &i_argc, ppsz_argv, true ); + config_SaveConfigFile( p_libvlc, NULL ); + } + if( config_GetInt( p_libvlc, "save-config" ) > 0 ) + { + config_LoadConfigFile( p_libvlc, NULL ); + config_LoadCmdLine( p_libvlc, &i_argc, ppsz_argv, true ); + config_SaveConfigFile( p_libvlc, NULL ); + } + } + + if( b_exit ) + { + module_EndBank( p_libvlc ); + return i_ret; + } + + /* + * Init device values + */ + InitDeviceValues( p_libvlc ); + + /* + * Override default configuration with config file settings + */ + if( !config_GetInt( p_libvlc, "ignore-config" ) ) + config_LoadConfigFile( p_libvlc, NULL ); + + /* + * Override configuration with command line settings + */ + if( config_LoadCmdLine( p_libvlc, &i_argc, ppsz_argv, false ) ) + { +#ifdef WIN32 + ShowConsole( false ); + /* Pause the console because it's destroyed when we exit */ + fprintf( stderr, "The command line options couldn't be loaded, check " + "that they are valid.\n" ); + PauseConsole(); +#endif + module_EndBank( p_libvlc ); + return VLC_EGENERIC; + } + + /* + * System specific configuration + */ + system_Configure( p_libvlc, &i_argc, ppsz_argv ); + +/* FIXME: could be replaced by using Unix sockets */ +#ifdef HAVE_DBUS + dbus_threads_init_default(); + + if( config_GetInt( p_libvlc, "one-instance" ) > 0 ) + { + /* Initialise D-Bus interface, check for other instances */ + DBusConnection *p_conn = NULL; + DBusError dbus_error; + + dbus_error_init( &dbus_error ); + + /* connect to the session bus */ + p_conn = dbus_bus_get( DBUS_BUS_SESSION, &dbus_error ); + if( !p_conn ) + { + msg_Err( p_libvlc, "Failed to connect to D-Bus session daemon: %s", + dbus_error.message ); + dbus_error_free( &dbus_error ); + } + else + { + /* check if VLC is available on the bus + * if not: D-Bus control is not enabled on the other + * instance and we can't pass MRLs to it */ + DBusMessage *p_test_msg = NULL; + DBusMessage *p_test_reply = NULL; + p_test_msg = dbus_message_new_method_call( + "org.mpris.vlc", "/", + "org.freedesktop.MediaPlayer", "Identity" ); + /* block until a reply arrives */ + p_test_reply = dbus_connection_send_with_reply_and_block( + p_conn, p_test_msg, -1, &dbus_error ); + dbus_message_unref( p_test_msg ); + if( p_test_reply == NULL ) + { + dbus_error_free( &dbus_error ); + msg_Dbg( p_libvlc, "No Media Player is running. " + "Continuing normally." ); + } + else + { + int i_input; + DBusMessage* p_dbus_msg = NULL; + DBusMessageIter dbus_args; + DBusPendingCall* p_dbus_pending = NULL; + dbus_bool_t b_play; + + dbus_message_unref( p_test_reply ); + msg_Warn( p_libvlc, "Another Media Player is running. Exiting"); + + for( i_input = optind;i_input < i_argc;i_input++ ) + { + msg_Dbg( p_libvlc, "Adds %s to the running Media Player", + ppsz_argv[i_input] ); + + p_dbus_msg = dbus_message_new_method_call( + "org.mpris.vlc", "/TrackList", + "org.freedesktop.MediaPlayer", "AddTrack" ); + + if ( NULL == p_dbus_msg ) + { + msg_Err( p_libvlc, "D-Bus problem" ); + system_End( p_libvlc ); + exit( VLC_ETIMEOUT ); + } + + /* append MRLs */ + dbus_message_iter_init_append( p_dbus_msg, &dbus_args ); + if ( !dbus_message_iter_append_basic( &dbus_args, + DBUS_TYPE_STRING, &ppsz_argv[i_input] ) ) + { + dbus_message_unref( p_dbus_msg ); + system_End( p_libvlc ); + exit( VLC_ENOMEM ); + } + b_play = TRUE; + if( config_GetInt( p_libvlc, "playlist-enqueue" ) > 0 ) + b_play = FALSE; + if ( !dbus_message_iter_append_basic( &dbus_args, + DBUS_TYPE_BOOLEAN, &b_play ) ) + { + dbus_message_unref( p_dbus_msg ); + system_End( p_libvlc ); + exit( VLC_ENOMEM ); + } + + /* send message and get a handle for a reply */ + if ( !dbus_connection_send_with_reply ( p_conn, + p_dbus_msg, &p_dbus_pending, -1 ) ) + { + msg_Err( p_libvlc, "D-Bus problem" ); + dbus_message_unref( p_dbus_msg ); + system_End( p_libvlc ); + exit( VLC_ETIMEOUT ); + } + + if ( NULL == p_dbus_pending ) + { + msg_Err( p_libvlc, "D-Bus problem" ); + dbus_message_unref( p_dbus_msg ); + system_End( p_libvlc ); + exit( VLC_ETIMEOUT ); + } + dbus_connection_flush( p_conn ); + dbus_message_unref( p_dbus_msg ); + /* block until we receive a reply */ + dbus_pending_call_block( p_dbus_pending ); + dbus_pending_call_unref( p_dbus_pending ); + } /* processes all command line MRLs */ + + /* bye bye */ + system_End( p_libvlc ); + exit( VLC_SUCCESS ); + } + } + /* we unreference the connection when we've finished with it */ + if( p_conn ) dbus_connection_unref( p_conn ); + } +#endif + + /* + * Message queue options + */ + + var_Create( p_libvlc, "verbose", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); + if( config_GetInt( p_libvlc, "quiet" ) > 0 ) + { + val.i_int = -1; + var_Set( p_libvlc, "verbose", val ); + } + var_AddCallback( p_libvlc, "verbose", VerboseCallback, NULL ); + var_Change( p_libvlc, "verbose", VLC_VAR_TRIGGER_CALLBACKS, NULL, NULL ); + + if( priv->b_color ) + priv->b_color = config_GetInt( p_libvlc, "color" ) > 0; + + /* + * Output messages that may still be in the queue + */ + msg_Flush( p_libvlc ); + + if( !config_GetInt( p_libvlc, "fpu" ) ) + cpu_flags &= ~CPU_CAPABILITY_FPU; + +#if defined( __i386__ ) || defined( __x86_64__ ) + if( !config_GetInt( p_libvlc, "mmx" ) ) + cpu_flags &= ~CPU_CAPABILITY_MMX; + if( !config_GetInt( p_libvlc, "3dn" ) ) + cpu_flags &= ~CPU_CAPABILITY_3DNOW; + if( !config_GetInt( p_libvlc, "mmxext" ) ) + cpu_flags &= ~CPU_CAPABILITY_MMXEXT; + if( !config_GetInt( p_libvlc, "sse" ) ) + cpu_flags &= ~CPU_CAPABILITY_SSE; + if( !config_GetInt( p_libvlc, "sse2" ) ) + cpu_flags &= ~CPU_CAPABILITY_SSE2; +#endif +#if defined( __powerpc__ ) || defined( __ppc__ ) || defined( __ppc64__ ) + if( !config_GetInt( p_libvlc, "altivec" ) ) + cpu_flags &= ~CPU_CAPABILITY_ALTIVEC; +#endif + +#define PRINT_CAPABILITY( capability, string ) \ + if( vlc_CPU() & capability ) \ + { \ + strncat( p_capabilities, string " ", \ + sizeof(p_capabilities) - strlen(p_capabilities) ); \ + p_capabilities[sizeof(p_capabilities) - 1] = '\0'; \ + } + + p_capabilities[0] = '\0'; + PRINT_CAPABILITY( CPU_CAPABILITY_486, "486" ); + PRINT_CAPABILITY( CPU_CAPABILITY_586, "586" ); + PRINT_CAPABILITY( CPU_CAPABILITY_PPRO, "Pentium Pro" ); + PRINT_CAPABILITY( CPU_CAPABILITY_MMX, "MMX" ); + PRINT_CAPABILITY( CPU_CAPABILITY_3DNOW, "3DNow!" ); + PRINT_CAPABILITY( CPU_CAPABILITY_MMXEXT, "MMXEXT" ); + PRINT_CAPABILITY( CPU_CAPABILITY_SSE, "SSE" ); + PRINT_CAPABILITY( CPU_CAPABILITY_SSE2, "SSE2" ); + PRINT_CAPABILITY( CPU_CAPABILITY_ALTIVEC, "AltiVec" ); + PRINT_CAPABILITY( CPU_CAPABILITY_FPU, "FPU" ); + msg_Dbg( p_libvlc, "CPU has capabilities %s", p_capabilities ); + + /* + * Choose the best memcpy module + */ + priv->p_memcpy_module = module_Need( p_libvlc, "memcpy", "$memcpy", 0 ); + + priv->b_stats = config_GetInt( p_libvlc, "stats" ) > 0; + priv->i_timers = 0; + priv->pp_timers = NULL; + + /* Init stats */ + p_libvlc->p_stats = (global_stats_t *)malloc( sizeof( global_stats_t ) ); + if( !p_libvlc->p_stats ) + { + vlc_object_release( p_libvlc ); + return VLC_ENOMEM; + } + vlc_mutex_init( &p_libvlc->p_stats->lock ); + priv->p_stats_computer = NULL; + + /* Init the array that holds every input item */ + ARRAY_INIT( priv->input_items ); + priv->i_last_input_id = 0; + + /* + * Initialize hotkey handling + */ + var_Create( p_libvlc, "key-pressed", VLC_VAR_INTEGER ); + var_Create( p_libvlc, "key-action", VLC_VAR_INTEGER ); + p_libvlc->p_hotkeys = malloc( libvlc_hotkeys_size ); + /* Do a copy (we don't need to modify the strings) */ + memcpy( p_libvlc->p_hotkeys, libvlc_hotkeys, libvlc_hotkeys_size ); + var_AddCallback( p_libvlc, "key-pressed", vlc_key_to_action, + p_libvlc->p_hotkeys ); + + /* Initialize interaction */ + priv->p_interaction = interaction_Init( p_libvlc ); + + /* Initialize playlist and get commandline files */ + playlist_ThreadCreate( p_libvlc ); + if( !priv->p_playlist ) + { + msg_Err( p_libvlc, "playlist initialization failed" ); + if( priv->p_memcpy_module != NULL ) + { + module_Unneed( p_libvlc, priv->p_memcpy_module ); + } + module_EndBank( p_libvlc ); + return VLC_EGENERIC; + } + p_playlist = priv->p_playlist; + + psz_modules = config_GetPsz( p_playlist, "services-discovery" ); + if( psz_modules && *psz_modules ) + { + /* Add service discovery modules */ + playlist_ServicesDiscoveryAdd( p_playlist, psz_modules ); + } + free( psz_modules ); + +#ifdef ENABLE_VLM + /* Initialize VLM if vlm-conf is specified */ + psz_parser = config_GetPsz( p_libvlc, "vlm-conf" ); + if( psz_parser && *psz_parser ) + { + priv->p_vlm = vlm_New( p_libvlc ); + if( !priv->p_vlm ) + msg_Err( p_libvlc, "VLM initialization failed" ); + } + free( psz_parser ); +#endif + + /* + * Load background interfaces + */ + psz_modules = config_GetPsz( p_libvlc, "extraintf" ); + psz_control = config_GetPsz( p_libvlc, "control" ); + + if( psz_modules && *psz_modules && psz_control && *psz_control ) + { + psz_modules = (char *)realloc( psz_modules, strlen( psz_modules ) + + strlen( psz_control ) + 1 ); + sprintf( psz_modules, "%s:%s", psz_modules, psz_control ); + } + else if( psz_control && *psz_control ) + { + free( psz_modules ); + psz_modules = strdup( psz_control ); + } + + psz_parser = psz_modules; + while ( psz_parser && *psz_parser ) + { + char *psz_module, *psz_temp; + psz_module = psz_parser; + psz_parser = strchr( psz_module, ':' ); + if ( psz_parser ) + { + *psz_parser = '\0'; + psz_parser++; + } + psz_temp = (char *)malloc( strlen(psz_module) + sizeof(",none") ); + if( psz_temp ) + { + sprintf( psz_temp, "%s,none", psz_module ); + libvlc_InternalAddIntf( p_libvlc, psz_temp ); + free( psz_temp ); + } + } + free( psz_modules ); + free( psz_control ); + + /* + * Always load the hotkeys interface if it exists + */ + libvlc_InternalAddIntf( p_libvlc, "hotkeys,none" ); + +#ifdef HAVE_DBUS + /* loads dbus control interface if in one-instance mode + * we do it only when playlist exists, because dbus module needs it */ + if( config_GetInt( p_libvlc, "one-instance" ) > 0 ) + libvlc_InternalAddIntf( p_libvlc, "dbus,none" ); + + /* Prevents the power management daemon from suspending the system + * when VLC is active */ + if( config_GetInt( p_libvlc, "inhibit" ) > 0 ) + libvlc_InternalAddIntf( p_libvlc, "inhibit,none" ); +#endif + + /* + * If needed, load the Xscreensaver interface + * Currently, only for X + */ +#ifdef HAVE_X11_XLIB_H + if( config_GetInt( p_libvlc, "disable-screensaver" ) ) + { + libvlc_InternalAddIntf( p_libvlc, "screensaver,none" ); + } +#endif + + if( config_GetInt( p_libvlc, "file-logging" ) > 0 ) + { + libvlc_InternalAddIntf( p_libvlc, "logger,none" ); + } +#ifdef HAVE_SYSLOG_H + if( config_GetInt( p_libvlc, "syslog" ) > 0 ) + { + char *logmode = var_CreateGetString( p_libvlc, "logmode" ); + var_SetString( p_libvlc, "logmode", "syslog" ); + libvlc_InternalAddIntf( p_libvlc, "logger,none" ); + + if( logmode ) + { + var_SetString( p_libvlc, "logmode", logmode ); + free( logmode ); + } + else + var_Destroy( p_libvlc, "logmode" ); + } +#endif + + if( config_GetInt( p_libvlc, "show-intf" ) > 0 ) + { + libvlc_InternalAddIntf( p_libvlc, "showintf,none" ); + } + + if( config_GetInt( p_libvlc, "network-synchronisation") > 0 ) + { + libvlc_InternalAddIntf( p_libvlc, "netsync,none" ); + } + +#ifdef WIN32 + if( config_GetInt( p_libvlc, "prefer-system-codecs") > 0 ) + { + char *psz_codecs = config_GetPsz( p_playlist, "codec" ); + if( psz_codecs ) + { + char *psz_morecodecs; + if( asprintf(&psz_morecodecs, "%s,dmo,quicktime", psz_codecs) != -1 ) + { + config_PutPsz( p_libvlc, "codec", psz_morecodecs); + free( psz_morecodecs ); + } + } + else + config_PutPsz( p_libvlc, "codec", "dmo,quicktime"); + free( psz_codecs ); + } +#endif + + /* + * FIXME: kludge to use a p_libvlc-local variable for the Mozilla plugin + */ + var_Create( p_libvlc, "drawable", VLC_VAR_INTEGER ); + var_Create( p_libvlc, "drawable-view-top", VLC_VAR_INTEGER ); + var_Create( p_libvlc, "drawable-view-left", VLC_VAR_INTEGER ); + var_Create( p_libvlc, "drawable-view-bottom", VLC_VAR_INTEGER ); + var_Create( p_libvlc, "drawable-view-right", VLC_VAR_INTEGER ); + var_Create( p_libvlc, "drawable-clip-top", VLC_VAR_INTEGER ); + var_Create( p_libvlc, "drawable-clip-left", VLC_VAR_INTEGER ); + var_Create( p_libvlc, "drawable-clip-bottom", VLC_VAR_INTEGER ); + var_Create( p_libvlc, "drawable-clip-right", VLC_VAR_INTEGER ); + + /* Create volume callback system. */ + var_Create( p_libvlc, "volume-change", VLC_VAR_BOOL ); + + /* Create a variable for showing the interface (moved from playlist). */ + var_Create( p_libvlc, "intf-show", VLC_VAR_BOOL ); + var_SetBool( p_libvlc, "intf-show", true ); + + var_Create( p_libvlc, "intf-popupmenu", VLC_VAR_BOOL ); + + /* + * Get input filenames given as commandline arguments + */ + GetFilenames( p_libvlc, i_argc, ppsz_argv ); + + /* + * Get --open argument + */ + var_Create( p_libvlc, "open", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); + var_Get( p_libvlc, "open", &val ); + if ( val.psz_string != NULL && *val.psz_string ) + { + playlist_t *p_playlist = pl_Yield( p_libvlc ); + playlist_AddExt( p_playlist, val.psz_string, NULL, PLAYLIST_INSERT, 0, + -1, NULL, 0, true, pl_Unlocked ); + pl_Release( p_libvlc ); + } + free( val.psz_string ); + + return VLC_SUCCESS; +} + +/** + * Cleanup a libvlc instance. The instance is not completely deallocated + * \param p_libvlc the instance to clean + */ +int libvlc_InternalCleanup( libvlc_int_t *p_libvlc ) +{ + intf_thread_t * p_intf = NULL; + libvlc_priv_t *priv = libvlc_priv (p_libvlc); + + /* Ask the interfaces to stop and destroy them */ + msg_Dbg( p_libvlc, "removing all interfaces" ); + while( (p_intf = vlc_object_find( p_libvlc, VLC_OBJECT_INTF, FIND_CHILD )) ) + { + intf_StopThread( p_intf ); + vlc_object_detach( p_intf ); + vlc_object_release( p_intf ); /* for intf_Create() */ + vlc_object_release( p_intf ); /* for vlc_object_find() */ + } + +#ifdef ENABLE_VLM + /* Destroy VLM if created in libvlc_InternalInit */ + if( priv->p_vlm ) + { + vlm_Delete( priv->p_vlm ); + } +#endif + + playlist_t *p_playlist = priv->p_playlist; + /* Remove all services discovery */ + msg_Dbg( p_libvlc, "removing all services discovery tasks" ); + playlist_ServicesDiscoveryKillAll( p_playlist ); + + /* Free playlist */ + /* Any thread still running must not assume pl_Yield() succeeds. */ + msg_Dbg( p_libvlc, "removing playlist" ); + priv->p_playlist = NULL; + vlc_object_kill( p_playlist ); /* <-- memory barrier for pl_Yield() */ + vlc_thread_join( p_playlist ); + vlc_object_release( p_playlist ); + + /* Free interaction */ + msg_Dbg( p_libvlc, "removing interaction" ); + interaction_Destroy( priv->p_interaction ); + + /* Free video outputs */ + msg_Dbg( p_libvlc, "removing all video outputs" ); + vlc_list_t *list = vlc_list_find (p_libvlc, VLC_OBJECT_VOUT, FIND_CHILD); + for (int i = 0; i < list->i_count; i++) + vlc_object_release (list->p_values[i].p_object); + vlc_list_release (list); + + stats_TimersDumpAll( p_libvlc ); + stats_TimersCleanAll( p_libvlc ); + +#ifdef ENABLE_SOUT + announce_handler_t * p_announce; + + /* Free announce handler(s?) */ + while( (p_announce = vlc_object_find( p_libvlc, VLC_OBJECT_ANNOUNCE, + FIND_CHILD ) ) ) + { + msg_Dbg( p_libvlc, "removing announce handler" ); + vlc_object_detach( p_announce ); + vlc_object_release( p_announce ); + announce_HandlerDestroy( p_announce ); + } +#endif + + bool b_clean = true; + FOREACH_ARRAY( input_item_t *p_del, priv->input_items ) + msg_Err( p_libvlc, "input item %p has not been deleted properly: refcount %d, name %s", + p_del, p_del->i_gc_refcount, p_del->psz_name ? p_del->psz_name : "(null)" ); + b_clean = false; + FOREACH_END(); + assert( b_clean ); + ARRAY_RESET( priv->input_items ); + + msg_Dbg( p_libvlc, "removing stats" ); + vlc_mutex_destroy( &p_libvlc->p_stats->lock ); + FREENULL( p_libvlc->p_stats ); + + return VLC_SUCCESS; +} + +/** + * Destroy everything. + * This function requests the running threads to finish, waits for their + * termination, and destroys their structure. + * It stops the thread systems: no instance can run after this has run + * \param p_libvlc the instance to destroy + */ +int libvlc_InternalDestroy( libvlc_int_t *p_libvlc ) +{ + if( !p_libvlc ) + return VLC_EGENERIC; + + libvlc_priv_t *priv = libvlc_priv (p_libvlc); + +#ifndef WIN32 + char* psz_pidfile = NULL; + + if( b_daemon ) + { + psz_pidfile = config_GetPsz( p_libvlc, "pidfile" ); + if( psz_pidfile != NULL ) + { + msg_Dbg( p_libvlc, "removing pid file %s", psz_pidfile ); + if( unlink( psz_pidfile ) == -1 ) + { + msg_Dbg( p_libvlc, "removing pid file %s: %m", + psz_pidfile ); + } + } + free( psz_pidfile ); + } +#endif + + if( priv->p_memcpy_module ) + { + module_Unneed( p_libvlc, priv->p_memcpy_module ); + priv->p_memcpy_module = NULL; + } + + /* Free module bank. It is refcounted, so we call this each time */ + module_EndBank( p_libvlc ); + + FREENULL( priv->psz_configfile ); + var_DelCallback( p_libvlc, "key-pressed", vlc_key_to_action, + p_libvlc->p_hotkeys ); + FREENULL( p_libvlc->p_hotkeys ); + + vlc_mutex_t *lock = var_AcquireMutex( "libvlc" ); + i_instances--; + + if( i_instances == 0 ) + { + /* System specific cleaning code */ + system_End( p_libvlc ); + } + vlc_mutex_unlock( lock ); + + msg_Flush( p_libvlc ); + msg_Destroy( p_libvlc ); + + /* Destroy mutexes */ + vlc_mutex_destroy( &priv->config_lock ); + vlc_mutex_destroy( &priv->timer_lock ); + + vlc_object_release( p_libvlc ); + p_libvlc = NULL; + + /* Stop thread system: last one out please shut the door! + * The number of initializations of the thread system is counted, we + * can call this each time */ + vlc_threads_end (); + + return VLC_SUCCESS; +} + +/** + * Add an interface plugin and run it + */ +int libvlc_InternalAddIntf( libvlc_int_t *p_libvlc, char const *psz_module ) +{ + int i_err; + intf_thread_t *p_intf = NULL; + + if( !p_libvlc ) + return VLC_EGENERIC; + + if( !psz_module ) /* requesting the default interface */ + { + char *psz_interface = config_GetPsz( p_libvlc, "intf" ); + if( !psz_interface || !*psz_interface ) /* "intf" has not been set */ + { +#ifndef WIN32 + if( b_daemon ) + /* Daemon mode hack. + * We prefer the dummy interface if none is specified. */ + psz_module = "dummy"; + else +#endif + msg_Info( p_libvlc, _("Running vlc with the default interface. Use 'cvlc' to use vlc without interface.") ); + } + free( psz_interface ); + } + + /* Try to create the interface */ + p_intf = intf_Create( p_libvlc, psz_module ? psz_module : "$intf" ); + if( p_intf == NULL ) + { + msg_Err( p_libvlc, "interface \"%s\" initialization failed", + psz_module ); + return VLC_EGENERIC; + } + + /* Try to run the interface */ + i_err = intf_RunThread( p_intf ); + if( i_err ) + { + vlc_object_detach( p_intf ); + vlc_object_release( p_intf ); + return i_err; + } + + return VLC_SUCCESS; +}; + +#if defined( ENABLE_NLS ) && (defined (__APPLE__) || defined (WIN32)) && \ + ( defined( HAVE_GETTEXT ) || defined( HAVE_INCLUDED_GETTEXT ) ) +/***************************************************************************** + * SetLanguage: set the interface language. + ***************************************************************************** + * We set the LC_MESSAGES locale category for interface messages and buttons, + * as well as the LC_CTYPE category for string sorting and possible wide + * character support. + *****************************************************************************/ +static void SetLanguage ( const char *psz_lang ) +{ +#ifdef __APPLE__ + /* I need that under Darwin, please check it doesn't disturb + * other platforms. --Meuuh */ + setenv( "LANG", psz_lang, 1 ); + +#else + /* We set LC_ALL manually because it is the only way to set + * the language at runtime under eg. Windows. Beware that this + * makes the environment unconsistent when libvlc is unloaded and + * should probably be moved to a safer place like vlc.c. */ + static char psz_lcall[20]; + snprintf( psz_lcall, 19, "LC_ALL=%s", psz_lang ); + psz_lcall[19] = '\0'; + putenv( psz_lcall ); +#endif + + setlocale( LC_ALL, psz_lang ); +} +#endif + + +static inline int LoadMessages (void) +{ +#if defined( ENABLE_NLS ) \ + && ( defined( HAVE_GETTEXT ) || defined( HAVE_INCLUDED_GETTEXT ) ) + /* Specify where to find the locales for current domain */ +#if !defined( __APPLE__ ) && !defined( WIN32 ) && !defined( SYS_BEOS ) + static const char psz_path[] = LOCALEDIR; +#else + char psz_path[1024]; + if (snprintf (psz_path, sizeof (psz_path), "%s" DIR_SEP "%s", + config_GetDataDir(), "locale") + >= (int)sizeof (psz_path)) + return -1; + +#endif + if (bindtextdomain (PACKAGE_NAME, psz_path) == NULL) + { + fprintf (stderr, "Warning: cannot bind text domain "PACKAGE_NAME + " to directory %s\n", psz_path); + return -1; + } + + /* LibVLC wants all messages in UTF-8. + * Unfortunately, we cannot ask UTF-8 for strerror_r(), strsignal_r() + * and other functions that are not part of our text domain. + */ + if (bind_textdomain_codeset (PACKAGE_NAME, "UTF-8") == NULL) + { + fprintf (stderr, "Error: cannot set Unicode encoding for text domain " + PACKAGE_NAME"\n"); + // Unbinds the text domain to avoid broken encoding + bindtextdomain (PACKAGE_NAME, "DOES_NOT_EXIST"); + return -1; + } + + /* LibVLC does NOT set the default textdomain, since it is a library. + * This could otherwise break programs using LibVLC (other than VLC). + * textdomain (PACKAGE_NAME); + */ +#endif + return 0; +} + +/***************************************************************************** + * GetFilenames: parse command line options which are not flags + ***************************************************************************** + * Parse command line for input files as well as their associated options. + * An option always follows its associated input and begins with a ":". + *****************************************************************************/ +static int GetFilenames( libvlc_int_t *p_vlc, int i_argc, const char *ppsz_argv[] ) +{ + int i_opt, i_options; + + /* We assume that the remaining parameters are filenames + * and their input options */ + for( i_opt = i_argc - 1; i_opt >= optind; i_opt-- ) + { + i_options = 0; + + /* Count the input options */ + while( *ppsz_argv[ i_opt ] == ':' && i_opt > optind ) + { + i_options++; + i_opt--; + } + + /* TODO: write an internal function of this one, to avoid + * unnecessary lookups. */ + + playlist_t *p_playlist = pl_Yield( p_vlc ); + playlist_AddExt( p_playlist, ppsz_argv[i_opt], NULL, PLAYLIST_INSERT, + 0, -1, ( i_options ? &ppsz_argv[i_opt + 1] : NULL ), + i_options, true, pl_Unlocked ); + pl_Release( p_vlc ); + } + + return VLC_SUCCESS; +} + +/***************************************************************************** + * Help: print program help + ***************************************************************************** + * Print a short inline help. Message interface is initialized at this stage. + *****************************************************************************/ +static inline void print_help_on_full_help( void ) +{ + utf8_fprintf( stdout, "\n" ); + utf8_fprintf( stdout, "%s\n", _("To get exhaustive help, use '-H'.") ); +} + +static void Help( libvlc_int_t *p_this, char const *psz_help_name ) +{ +#ifdef WIN32 + ShowConsole( true ); +#endif + + if( psz_help_name && !strcmp( psz_help_name, "help" ) ) + { + utf8_fprintf( stdout, vlc_usage, p_this->psz_object_name ); + Usage( p_this, "help" ); + Usage( p_this, "main" ); + print_help_on_full_help(); + } + else if( psz_help_name && !strcmp( psz_help_name, "longhelp" ) ) + { + utf8_fprintf( stdout, vlc_usage, p_this->psz_object_name ); + Usage( p_this, NULL ); + print_help_on_full_help(); + } + else if( psz_help_name && !strcmp( psz_help_name, "full-help" ) ) + { + utf8_fprintf( stdout, vlc_usage, p_this->psz_object_name ); + Usage( p_this, NULL ); + } + else if( psz_help_name ) + { + Usage( p_this, psz_help_name ); + } + +#ifdef WIN32 /* Pause the console because it's destroyed when we exit */ + PauseConsole(); +#endif +} + +/***************************************************************************** + * Usage: print module usage + ***************************************************************************** + * Print a short inline help. Message interface is initialized at this stage. + *****************************************************************************/ +# define COL(x) "\033[" #x ";1m" +# define RED COL(31) +# define GREEN COL(32) +# define YELLOW COL(33) +# define BLUE COL(34) +# define MAGENTA COL(35) +# define CYAN COL(36) +# define WHITE COL(0) +# define GRAY "\033[0m" +static void print_help_section( module_config_t *p_item, bool b_color, bool b_description ) +{ + if( !p_item ) return; + if( b_color ) + { + utf8_fprintf( stdout, RED" %s:\n"GRAY, + p_item->psz_text ); + if( b_description && p_item->psz_longtext ) + utf8_fprintf( stdout, MAGENTA" %s\n"GRAY, + p_item->psz_longtext ); + } + else + { + utf8_fprintf( stdout, " %s:\n", p_item->psz_text ); + if( b_description && p_item->psz_longtext ) + utf8_fprintf( stdout, " %s\n", p_item->psz_longtext ); + } +} + +static void Usage( libvlc_int_t *p_this, char const *psz_module_name ) +{ +#define FORMAT_STRING " %s --%s%s%s%s%s%s%s " + /* short option ------' | | | | | | | + * option name ------------' | | | | | | + * -------------------------' | | | + * padding spaces -----------------' | | + * comment --------------------------' | + * comment suffix ---------------------' + * + * The purpose of having bra and ket is that we might i18n them as well. + */ + +#define COLOR_FORMAT_STRING (WHITE" %s --%s"YELLOW"%s%s%s%s%s%s "GRAY) +#define COLOR_FORMAT_STRING_BOOL (WHITE" %s --%s%s%s%s%s%s%s "GRAY) + +#define LINE_START 8 +#define PADDING_SPACES 25 +#ifdef WIN32 +# define OPTION_VALUE_SEP "=" +#else +# define OPTION_VALUE_SEP " " +#endif + vlc_list_t *p_list = NULL; + char psz_spaces_text[PADDING_SPACES+LINE_START+1]; + char psz_spaces_longtext[LINE_START+3]; + char psz_format[sizeof(COLOR_FORMAT_STRING)]; + char psz_format_bool[sizeof(COLOR_FORMAT_STRING_BOOL)]; + char psz_buffer[10000]; + char psz_short[4]; + int i_index; + int i_width = ConsoleWidth() - (PADDING_SPACES+LINE_START+1); + int i_width_description = i_width + PADDING_SPACES - 1; + bool b_advanced = config_GetInt( p_this, "advanced" ) > 0; + bool b_description = config_GetInt( p_this, "help-verbose" ) > 0; + bool b_description_hack; + bool b_color = config_GetInt( p_this, "color" ) > 0; + bool b_has_advanced = false; + + memset( psz_spaces_text, ' ', PADDING_SPACES+LINE_START ); + psz_spaces_text[PADDING_SPACES+LINE_START] = '\0'; + memset( psz_spaces_longtext, ' ', LINE_START+2 ); + psz_spaces_longtext[LINE_START+2] = '\0'; +#ifndef WIN32 + if( !isatty( 1 ) ) +#endif + b_color = false; // don't put color control codes in a .txt file + + if( b_color ) + { + strcpy( psz_format, COLOR_FORMAT_STRING ); + strcpy( psz_format_bool, COLOR_FORMAT_STRING_BOOL ); + } + else + { + strcpy( psz_format, FORMAT_STRING ); + strcpy( psz_format_bool, FORMAT_STRING ); + } + + /* List all modules */ + p_list = vlc_list_find( p_this, VLC_OBJECT_MODULE, FIND_ANYWHERE ); + + /* Ugly hack to make sure that the help options always come first + * (part 1) */ + if( !psz_module_name ) + Usage( p_this, "help" ); + + /* Enumerate the config for each module */ + for( i_index = 0; i_index < p_list->i_count; i_index++ ) + { + bool b_help_module; + module_t *p_parser = (module_t *)p_list->p_values[i_index].p_object; + module_config_t *p_item = NULL; + module_config_t *p_section = NULL; + module_config_t *p_end = p_parser->p_config + p_parser->confsize; + + if( psz_module_name && strcmp( psz_module_name, + p_parser->psz_object_name ) ) + { + char *const *pp_shortcut = p_parser->pp_shortcuts; + while( *pp_shortcut ) + { + if( !strcmp( psz_module_name, *pp_shortcut ) ) + break; + pp_shortcut ++; + } + if( !*pp_shortcut ) + continue; + } + + /* Ignore modules without config options */ + if( !p_parser->i_config_items ) + { + continue; + } + + b_help_module = !strcmp( "help", p_parser->psz_object_name ); + /* Ugly hack to make sure that the help options always come first + * (part 2) */ + if( !psz_module_name && b_help_module ) + continue; + + /* Ignore modules with only advanced config options if requested */ + if( !b_advanced ) + { + for( p_item = p_parser->p_config; + p_item < p_end; + p_item++ ) + { + if( (p_item->i_type & CONFIG_ITEM) && + !p_item->b_advanced ) break; + } + } + + /* Print name of module */ + if( strcmp( "main", p_parser->psz_object_name ) ) + { + if( b_color ) + utf8_fprintf( stdout, "\n " GREEN "%s" GRAY "\n", + p_parser->psz_longname ); + else + utf8_fprintf( stdout, "\n %s\n", p_parser->psz_longname ); + } + if( p_parser->psz_help ) + { + if( b_color ) + utf8_fprintf( stdout, CYAN" %s\n"GRAY, p_parser->psz_help ); + else + utf8_fprintf( stdout, " %s\n", p_parser->psz_help ); + } + + /* Print module options */ + for( p_item = p_parser->p_config; + p_item < p_end; + p_item++ ) + { + char *psz_text, *psz_spaces = psz_spaces_text; + const char *psz_bra = NULL, *psz_type = NULL, *psz_ket = NULL; + const char *psz_suf = "", *psz_prefix = NULL; + signed int i; + size_t i_cur_width; + + /* Skip removed options */ + if( p_item->b_removed ) + { + continue; + } + /* Skip advanced options if requested */ + if( p_item->b_advanced && !b_advanced ) + { + b_has_advanced = true; + continue; + } + + switch( p_item->i_type ) + { + case CONFIG_HINT_CATEGORY: + case CONFIG_HINT_USAGE: + if( !strcmp( "main", p_parser->psz_object_name ) ) + { + if( b_color ) + utf8_fprintf( stdout, GREEN "\n %s\n" GRAY, + p_item->psz_text ); + else + utf8_fprintf( stdout, "\n %s\n", p_item->psz_text ); + } + if( b_description && p_item->psz_longtext ) + { + if( b_color ) + utf8_fprintf( stdout, CYAN " %s\n" GRAY, + p_item->psz_longtext ); + else + utf8_fprintf( stdout, " %s\n", p_item->psz_longtext ); + } + break; + + case CONFIG_HINT_SUBCATEGORY: + if( strcmp( "main", p_parser->psz_object_name ) ) + break; + case CONFIG_SECTION: + p_section = p_item; + break; + + case CONFIG_ITEM_STRING: + case CONFIG_ITEM_FILE: + case CONFIG_ITEM_DIRECTORY: + case CONFIG_ITEM_MODULE: /* We could also have "=<" here */ + case CONFIG_ITEM_MODULE_CAT: + case CONFIG_ITEM_MODULE_LIST: + case CONFIG_ITEM_MODULE_LIST_CAT: + case CONFIG_ITEM_FONT: + case CONFIG_ITEM_PASSWORD: + print_help_section( p_section, b_color, b_description ); + p_section = NULL; + psz_bra = OPTION_VALUE_SEP "<"; + psz_type = _("string"); + psz_ket = ">"; + + if( p_item->ppsz_list ) + { + psz_bra = OPTION_VALUE_SEP "{"; + psz_type = psz_buffer; + psz_buffer[0] = '\0'; + for( i = 0; p_item->ppsz_list[i]; i++ ) + { + if( i ) strcat( psz_buffer, "," ); + strcat( psz_buffer, p_item->ppsz_list[i] ); + } + psz_ket = "}"; + } + break; + case CONFIG_ITEM_INTEGER: + case CONFIG_ITEM_KEY: /* FIXME: do something a bit more clever */ + print_help_section( p_section, b_color, b_description ); + p_section = NULL; + psz_bra = OPTION_VALUE_SEP "<"; + psz_type = _("integer"); + psz_ket = ">"; + + if( p_item->min.i || p_item->max.i ) + { + sprintf( psz_buffer, "%s [%i .. %i]", psz_type, + p_item->min.i, p_item->max.i ); + psz_type = psz_buffer; + } + + if( p_item->i_list ) + { + psz_bra = OPTION_VALUE_SEP "{"; + psz_type = psz_buffer; + psz_buffer[0] = '\0'; + for( i = 0; p_item->ppsz_list_text[i]; i++ ) + { + if( i ) strcat( psz_buffer, ", " ); + sprintf( psz_buffer + strlen(psz_buffer), "%i (%s)", + p_item->pi_list[i], + p_item->ppsz_list_text[i] ); + } + psz_ket = "}"; + } + break; + case CONFIG_ITEM_FLOAT: + print_help_section( p_section, b_color, b_description ); + p_section = NULL; + psz_bra = OPTION_VALUE_SEP "<"; + psz_type = _("float"); + psz_ket = ">"; + if( p_item->min.f || p_item->max.f ) + { + sprintf( psz_buffer, "%s [%f .. %f]", psz_type, + p_item->min.f, p_item->max.f ); + psz_type = psz_buffer; + } + break; + case CONFIG_ITEM_BOOL: + print_help_section( p_section, b_color, b_description ); + p_section = NULL; + psz_bra = ""; psz_type = ""; psz_ket = ""; + if( !b_help_module ) + { + psz_suf = p_item->value.i ? _(" (default enabled)") : + _(" (default disabled)"); + } + break; + } + + if( !psz_type ) + { + continue; + } + + /* Add short option if any */ + if( p_item->i_short ) + { + sprintf( psz_short, "-%c,", p_item->i_short ); + } + else + { + strcpy( psz_short, " " ); + } + + i = PADDING_SPACES - strlen( p_item->psz_name ) + - strlen( psz_bra ) - strlen( psz_type ) + - strlen( psz_ket ) - 1; + + if( p_item->i_type == CONFIG_ITEM_BOOL && !b_help_module ) + { + psz_prefix = ", --no-"; + i -= strlen( p_item->psz_name ) + strlen( psz_prefix ); + } + + if( i < 0 ) + { + psz_spaces[0] = '\n'; + i = 0; + } + else + { + psz_spaces[i] = '\0'; + } + + if( p_item->i_type == CONFIG_ITEM_BOOL && !b_help_module ) + { + utf8_fprintf( stdout, psz_format_bool, psz_short, + p_item->psz_name, psz_prefix, p_item->psz_name, + psz_bra, psz_type, psz_ket, psz_spaces ); + } + else + { + utf8_fprintf( stdout, psz_format, psz_short, p_item->psz_name, + "", "", psz_bra, psz_type, psz_ket, psz_spaces ); + } + + psz_spaces[i] = ' '; + + /* We wrap the rest of the output */ + sprintf( psz_buffer, "%s%s", p_item->psz_text, psz_suf ); + b_description_hack = b_description; + + description: + psz_text = psz_buffer; + i_cur_width = b_description && !b_description_hack + ? i_width_description + : i_width; + while( *psz_text ) + { + char *psz_parser, *psz_word; + size_t i_end = strlen( psz_text ); + + /* If the remaining text fits in a line, print it. */ + if( i_end <= i_cur_width ) + { + if( b_color ) + { + if( !b_description || b_description_hack ) + utf8_fprintf( stdout, BLUE"%s\n"GRAY, psz_text ); + else + utf8_fprintf( stdout, "%s\n", psz_text ); + } + else + { + utf8_fprintf( stdout, "%s\n", psz_text ); + } + break; + } + + /* Otherwise, eat as many words as possible */ + psz_parser = psz_text; + do + { + psz_word = psz_parser; + psz_parser = strchr( psz_word, ' ' ); + /* If no space was found, we reached the end of the text + * block; otherwise, we skip the space we just found. */ + psz_parser = psz_parser ? psz_parser + 1 + : psz_text + i_end; + + } while( (size_t)(psz_parser - psz_text) <= i_cur_width ); + + /* We cut a word in one of these cases: + * - it's the only word in the line and it's too long. + * - we used less than 80% of the width and the word we are + * going to wrap is longer than 40% of the width, and even + * if the word would have fit in the next line. */ + if( psz_word == psz_text + || ( (size_t)(psz_word - psz_text) < 80 * i_cur_width / 100 + && (size_t)(psz_parser - psz_word) > 40 * i_cur_width / 100 ) ) + { + char c = psz_text[i_cur_width]; + psz_text[i_cur_width] = '\0'; + if( b_color ) + { + if( !b_description || b_description_hack ) + utf8_fprintf( stdout, BLUE"%s\n%s"GRAY, + psz_text, psz_spaces ); + else + utf8_fprintf( stdout, "%s\n%s", + psz_text, psz_spaces ); + } + else + { + utf8_fprintf( stdout, "%s\n%s", psz_text, psz_spaces ); + } + psz_text += i_cur_width; + psz_text[0] = c; + } + else + { + psz_word[-1] = '\0'; + if( b_color ) + { + if( !b_description || b_description_hack ) + utf8_fprintf( stdout, BLUE"%s\n%s"GRAY, + psz_text, psz_spaces ); + else + utf8_fprintf( stdout, "%s\n%s", + psz_text, psz_spaces ); + } + else + { + utf8_fprintf( stdout, "%s\n%s", psz_text, psz_spaces ); + } + psz_text = psz_word; + } + } + + if( b_description_hack && p_item->psz_longtext ) + { + sprintf( psz_buffer, "%s%s", p_item->psz_longtext, psz_suf ); + b_description_hack = false; + psz_spaces = psz_spaces_longtext; + utf8_fprintf( stdout, "%s", psz_spaces ); + goto description; + } + } + } + + if( b_has_advanced ) + { + if( b_color ) + utf8_fprintf( stdout, "\n" WHITE "%s" GRAY " %s\n", _( "Note:" ), + _( "add --advanced to your command line to see advanced options.")); + else + utf8_fprintf( stdout, "\n %s %s\n", _( "Note:" ), + _( "add --advanced to your command line to see advanced options.")); + } + + /* Release the module list */ + vlc_list_release( p_list ); +} + +/***************************************************************************** + * ListModules: list the available modules with their description + ***************************************************************************** + * Print a list of all available modules (builtins and plugins) and a short + * description for each one. + *****************************************************************************/ +static void ListModules( libvlc_int_t *p_this, bool b_verbose ) +{ + vlc_list_t *p_list = NULL; + module_t *p_parser = NULL; + char psz_spaces[22]; + int i_index; + + bool b_color = config_GetInt( p_this, "color" ) > 0; + + memset( psz_spaces, ' ', 22 ); + +#ifdef WIN32 + ShowConsole( true ); +#endif + + /* List all modules */ + p_list = vlc_list_find( p_this, VLC_OBJECT_MODULE, FIND_ANYWHERE ); + + /* Enumerate each module */ + for( i_index = 0; i_index < p_list->i_count; i_index++ ) + { + int i; + + p_parser = (module_t *)p_list->p_values[i_index].p_object ; + + /* Nasty hack, but right now I'm too tired to think about a nice + * solution */ + i = 22 - strlen( p_parser->psz_object_name ) - 1; + if( i < 0 ) i = 0; + psz_spaces[i] = 0; + + if( b_color ) + utf8_fprintf( stdout, GREEN" %s%s "WHITE"%s\n"GRAY, + p_parser->psz_object_name, + psz_spaces, + p_parser->psz_longname ); + else + utf8_fprintf( stdout, " %s%s %s\n", + p_parser->psz_object_name, + psz_spaces, p_parser->psz_longname ); + + if( b_verbose ) + { + char *const *pp_shortcut = p_parser->pp_shortcuts; + while( *pp_shortcut ) + { + if( strcmp( *pp_shortcut, p_parser->psz_object_name ) ) + { + if( b_color ) + utf8_fprintf( stdout, CYAN" s %s\n"GRAY, + *pp_shortcut ); + else + utf8_fprintf( stdout, " s %s\n", + *pp_shortcut ); + } + pp_shortcut++; + } + if( p_parser->psz_capability ) + { + if( b_color ) + utf8_fprintf( stdout, MAGENTA" c %s (%d)\n"GRAY, + p_parser->psz_capability, + p_parser->i_score ); + else + utf8_fprintf( stdout, " c %s (%d)\n", + p_parser->psz_capability, + p_parser->i_score ); + } + } + + psz_spaces[i] = ' '; + } + + vlc_list_release( p_list ); + +#ifdef WIN32 /* Pause the console because it's destroyed when we exit */ + PauseConsole(); +#endif +} + +/***************************************************************************** + * Version: print complete program version + ***************************************************************************** + * Print complete program version and build number. + *****************************************************************************/ +static void Version( void ) +{ +#ifdef WIN32 + ShowConsole( true ); +#endif + + utf8_fprintf( stdout, _("VLC version %s\n"), VLC_Version() ); + utf8_fprintf( stdout, _("Compiled by %s@%s.%s\n"), + VLC_CompileBy(), VLC_CompileHost(), VLC_CompileDomain() ); + utf8_fprintf( stdout, _("Compiler: %s\n"), VLC_Compiler() ); + if( strcmp( VLC_Changeset(), "exported" ) ) + utf8_fprintf( stdout, _("Based upon Git commit [%s]\n"), + VLC_Changeset() ); + utf8_fprintf( stdout, LICENSE_MSG ); + +#ifdef WIN32 /* Pause the console because it's destroyed when we exit */ + PauseConsole(); +#endif +} + +/***************************************************************************** + * ShowConsole: On Win32, create an output console for debug messages + ***************************************************************************** + * This function is useful only on Win32. + *****************************************************************************/ +#ifdef WIN32 /* */ +static void ShowConsole( bool b_dofile ) +{ +# ifndef UNDER_CE + FILE *f_help = NULL; + + if( getenv( "PWD" ) && getenv( "PS1" ) ) return; /* cygwin shell */ + + AllocConsole(); + /* Use the ANSI code page (e.g. Windows-1252) as expected by the LibVLC + * Unicode/locale subsystem. By default, we have the obsolecent OEM code + * page (e.g. CP437 or CP850). */ + SetConsoleOutputCP (GetACP ()); + SetConsoleTitle ("VLC media player version "PACKAGE_VERSION); + + freopen( "CONOUT$", "w", stderr ); + freopen( "CONIN$", "r", stdin ); + + if( b_dofile && (f_help = fopen( "vlc-help.txt", "wt" )) ) + { + fclose( f_help ); + freopen( "vlc-help.txt", "wt", stdout ); + utf8_fprintf( stderr, _("\nDumped content to vlc-help.txt file.\n") ); + } + else freopen( "CONOUT$", "w", stdout ); + +# endif +} +#endif + +/***************************************************************************** + * PauseConsole: On Win32, wait for a key press before closing the console + ***************************************************************************** + * This function is useful only on Win32. + *****************************************************************************/ +#ifdef WIN32 /* */ +static void PauseConsole( void ) +{ +# ifndef UNDER_CE + + if( getenv( "PWD" ) && getenv( "PS1" ) ) return; /* cygwin shell */ + + utf8_fprintf( stderr, _("\nPress the RETURN key to continue...\n") ); + getchar(); + fclose( stdout ); + +# endif +} +#endif + +/***************************************************************************** + * ConsoleWidth: Return the console width in characters + ***************************************************************************** + * We use the stty shell command to get the console width; if this fails or + * if the width is less than 80, we default to 80. + *****************************************************************************/ +static int ConsoleWidth( void ) +{ + unsigned i_width = 80; + +#ifndef WIN32 + FILE *file = popen( "stty size 2>/dev/null", "r" ); + if (file != NULL) + { + if (fscanf (file, "%*u %u", &i_width) <= 0) + i_width = 80; + pclose( file ); + } +#else + CONSOLE_SCREEN_BUFFER_INFO buf; + + if (GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &buf)) + i_width = buf.dwSize.X; +#endif + + return i_width; +} + +static int VerboseCallback( vlc_object_t *p_this, const char *psz_variable, + vlc_value_t old_val, vlc_value_t new_val, void *param) +{ + libvlc_int_t *p_libvlc = (libvlc_int_t *)p_this; + (void)psz_variable; + (void)old_val; + (void)param; + + if( new_val.i_int >= -1 ) + { + libvlc_priv (p_libvlc)->i_verbose = __MIN( new_val.i_int, 2 ); + } + return VLC_SUCCESS; +} + +/***************************************************************************** + * InitDeviceValues: initialize device values + ***************************************************************************** + * This function inits the dvd, vcd and cd-audio values + *****************************************************************************/ +static void InitDeviceValues( libvlc_int_t *p_vlc ) +{ +#ifdef HAVE_HAL + LibHalContext * ctx = NULL; + int i, i_devices; + char **devices = NULL; + char *block_dev = NULL; + dbus_bool_t b_dvd; + + DBusConnection *p_connection = NULL; + DBusError error; + + ctx = libhal_ctx_new(); + if( !ctx ) return; + dbus_error_init( &error ); + p_connection = dbus_bus_get ( DBUS_BUS_SYSTEM, &error ); + if( dbus_error_is_set( &error ) || !p_connection ) + { + libhal_ctx_free( ctx ); + dbus_error_free( &error ); + return; + } + libhal_ctx_set_dbus_connection( ctx, p_connection ); + if( libhal_ctx_init( ctx, &error ) ) + { + if( ( devices = libhal_get_all_devices( ctx, &i_devices, NULL ) ) ) + { + for( i = 0; i < i_devices; i++ ) + { + if( !libhal_device_property_exists( ctx, devices[i], + "storage.cdrom.dvd", NULL ) ) + { + continue; + } + b_dvd = libhal_device_get_property_bool( ctx, devices[ i ], + "storage.cdrom.dvd", NULL ); + block_dev = libhal_device_get_property_string( ctx, + devices[ i ], "block.device" , NULL ); + if( b_dvd ) + { + config_PutPsz( p_vlc, "dvd", block_dev ); + } + + config_PutPsz( p_vlc, "vcd", block_dev ); + config_PutPsz( p_vlc, "cd-audio", block_dev ); + libhal_free_string( block_dev ); + } + libhal_free_string_array( devices ); + } + libhal_ctx_shutdown( ctx, NULL ); + dbus_connection_unref( p_connection ); + libhal_ctx_free( ctx ); + } + else + { + msg_Warn( p_vlc, "Unable to get HAL device properties" ); + } +#else + (void)p_vlc; +#endif /* HAVE_HAL */ +} diff --git a/VLC/libvlc.h b/VLC/libvlc.h new file mode 100644 index 0000000..10c0d87 --- /dev/null +++ b/VLC/libvlc.h @@ -0,0 +1,316 @@ +/***************************************************************************** + * libvlc.h: Internal libvlc generic/misc declaration + ***************************************************************************** + * Copyright (C) 1999, 2000, 2001, 2002 the VideoLAN team + * Copyright © 2006-2007 Rémi Denis-Courmont + * $Id$ + * + * Authors: Vincent Seguin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef LIBVLC_LIBVLC_H +# define LIBVLC_LIBVLC_H 1 + +extern const char vlc_usage[]; + +/* Hotkey stuff */ +extern const struct hotkey libvlc_hotkeys[]; +extern const size_t libvlc_hotkeys_size; +extern int vlc_key_to_action (vlc_object_t *, const char *, + vlc_value_t, vlc_value_t, void *); + +/* + * OS-specific initialization + */ +void system_Init ( libvlc_int_t *, int *, const char *[] ); +void system_Configure ( libvlc_int_t *, int *, const char *[] ); +void system_End ( libvlc_int_t * ); + +/* + * Threads subsystem + */ +int vlc_threads_init( void ); +void vlc_threads_end( void ); +vlc_object_t *vlc_threadobj (void); +#ifdef LIBVLC_REFCHECK +void vlc_refcheck (vlc_object_t *obj); +#else +# define vlc_refcheck( obj ) (void)0 +#endif + +/* + * CPU capabilities + */ +extern uint32_t cpu_flags; +uint32_t CPUCapabilities( void ); + +/* + * Message/logging stuff + */ + +typedef struct msg_queue_t +{ + /** Message queue lock */ + vlc_mutex_t lock; + bool b_overflow; + + /* Message queue */ + msg_item_t msg[VLC_MSG_QSIZE]; /**< message queue */ + int i_start; + int i_stop; + + /* Subscribers */ + int i_sub; + msg_subscription_t **pp_sub; + + /* Logfile for WinCE */ +#ifdef UNDER_CE + FILE *logfile; +#endif +} msg_queue_t; + +/** + * Store all data required by messages interfaces. + */ +typedef struct msg_bank_t +{ + vlc_mutex_t lock; + msg_queue_t queue; +} msg_bank_t; + +void msg_Create (libvlc_int_t *); +void msg_Flush (libvlc_int_t *); +void msg_Destroy (libvlc_int_t *); + +/** Internal message stack context */ +typedef struct +{ + int i_code; + char * psz_message; +} msg_context_t; + +void msg_StackSet ( int, const char*, ... ); +void msg_StackAdd ( const char*, ... ); +const char* msg_StackMsg ( void ); +/** The global thread var for msg stack context + * We store this as a static global variable so we don't need a vlc_object_t + * everywhere. + * This key is created in vlc_threads_init and is therefore ready to use at + * the very beginning of the universe */ +extern vlc_threadvar_t msg_context_global_key; +void msg_StackDestroy (void *); + +/* + * Unicode stuff + */ +char *vlc_fix_readdir (const char *); + +/* + * LibVLC objects stuff + */ + +/** + * Creates a VLC object. + * + * Note that because the object name pointer must remain valid, potentially + * even after the destruction of the object (through the message queues), this + * function CANNOT be exported to plugins as is. In this case, the old + * vlc_object_create() must be used instead. + * + * @param p_this an existing VLC object + * @param i_size byte size of the object structure + * @param i_type object type, usually VLC_OBJECT_CUSTOM + * @param psz_type object type name + * @return the created object, or NULL. + */ +extern void * +__vlc_custom_create (vlc_object_t *p_this, size_t i_size, int i_type, + const char *psz_type); +#define vlc_custom_create(o, s, t, n) \ + __vlc_custom_create(VLC_OBJECT(o), s, t, n) + +/** + * libvlc_global_data_t (global variable) + * + * This structure has an unique instance, statically allocated in libvlc and + * never accessed from the outside. It stores process-wide VLC variables, + * mostly process-wide locks, and (currently) the module bank and objects tree. + */ +typedef struct libvlc_global_data_t +{ + VLC_COMMON_MEMBERS + + module_bank_t * p_module_bank; ///< The module bank + + char * psz_vlcpath; +} libvlc_global_data_t; + + +libvlc_global_data_t *vlc_global (void); + +/** + * Private LibVLC data for each object. + */ +struct vlc_object_internals_t +{ + /* Object variables */ + variable_t * p_vars; + vlc_mutex_t var_lock; + int i_vars; + + /* Thread properties, if any */ + vlc_thread_t thread_id; + bool b_thread; + + /* Objects thread synchronization */ + vlc_mutex_t lock; + vlc_cond_t wait; + int pipes[2]; + vlc_spinlock_t spin; + + /* Objects management */ + vlc_spinlock_t ref_spin; + unsigned i_refcount; + vlc_destructor_t pf_destructor; +#ifndef LIBVLC_REFCHECK + vlc_thread_t creator_id; +#endif + + /* Objects tree structure */ + vlc_object_t *prev, *next; + vlc_object_t **pp_children; + int i_children; +}; + +#define ZOOM_SECTION N_("Zoom") +#define ZOOM_QUARTER_KEY_TEXT N_("1:4 Quarter") +#define ZOOM_HALF_KEY_TEXT N_("1:2 Half") +#define ZOOM_ORIGINAL_KEY_TEXT N_("1:1 Original") +#define ZOOM_DOUBLE_KEY_TEXT N_("2:1 Double") + +#define vlc_internals( obj ) (((vlc_object_internals_t*)(VLC_OBJECT(obj)))-1) + +/* Signal an object without checking for locking consistency. This is wrong. */ +#ifdef __GNUC__ +__attribute__((deprecated)) +#endif +static inline void +vlc_object_signal_maybe (vlc_object_t *p_this) +{ + vlc_cond_signal (&(vlc_internals(p_this)->wait)); +} + +/** + * Private LibVLC instance data. + */ +typedef struct libvlc_priv_t +{ + libvlc_int_t public_data; + + /* Configuration */ + vlc_mutex_t config_lock; ///< config file lock + char * psz_configfile; ///< location of config file + + /* There is no real reason to keep a list of items, but not to break + * everything, let's keep it */ + input_item_array_t input_items; ///< Array of all created input items + int i_last_input_id ; ///< Last id of input item + + /* Messages */ + msg_bank_t msg_bank; ///< The message bank + int i_verbose; ///< info messages + bool b_color; ///< color messages? + + /* Timer stats */ + vlc_mutex_t timer_lock; ///< Lock to protect timers + counter_t **pp_timers; ///< Array of all timers + int i_timers; ///< Number of timers + bool b_stats; ///< Whether to collect stats + + void *p_stats_computer; ///< Input thread computing stats + /// (needs cleanup) + + /* Singleton objects */ + module_t *p_memcpy_module; ///< Fast memcpy plugin used + playlist_t *p_playlist; //< the playlist singleton + vlm_t *p_vlm; ///< the VLM singleton (or NULL) + interaction_t *p_interaction; ///< interface interaction object + httpd_t *p_httpd; ///< HTTP daemon (src/network/httpd.c) + + /* Private playlist data (FIXME - playlist_t is too public...) */ + sout_instance_t *p_sout; ///< kept sout instance (for playlist) +} libvlc_priv_t; + +static inline libvlc_priv_t *libvlc_priv (libvlc_int_t *libvlc) +{ + return (libvlc_priv_t *)libvlc; +} + +void playlist_ServicesDiscoveryKillAll( playlist_t *p_playlist ); + +#define libvlc_stats( o ) (libvlc_priv((VLC_OBJECT(o))->p_libvlc)->b_stats) + +/** + * LibVLC "main module" configuration settings array. + */ +extern module_config_t libvlc_config[]; +extern const size_t libvlc_config_count; + +/* + * Variables stuff + */ +void var_OptionParse (vlc_object_t *, const char *, bool trusted); + +/* + * Replacement functions + */ +/* +# ifndef HAVE_DIRENT_H +typedef void DIR; +# ifndef FILENAME_MAX +# define FILENAME_MAX (260) +# endif +struct dirent +{ + long d_ino; //Always zero. + unsigned short d_reclen; //Always zero. + unsigned short d_namlen; //Length of name in d_name. + char d_name[FILENAME_MAX]; // File name. +}; +# define opendir vlc_opendir +# define readdir vlc_readdir +# define closedir vlc_closedir +# define rewinddir vlc_rewindir +void *vlc_opendir (const char *); +void *vlc_readdir (void *); +int vlc_closedir(void *); +void vlc_rewinddir(void *); +# endif +*/ +#if defined (WIN32) +# include +void *vlc_wopendir (const wchar_t *); +/* void *vlc_wclosedir (void *); in vlc's exported symbols */ +struct _wdirent *vlc_wreaddir (void *); +void vlc_rewinddir (void *); +# define _wopendir vlc_wopendir +# define _wreaddir vlc_wreaddir +# define _wclosedir vlc_wclosedir +# define rewinddir vlc_rewinddir +#endif + +#endif diff --git a/VLC/libvlc_internal.h b/VLC/libvlc_internal.h new file mode 100644 index 0000000..abf2470 --- /dev/null +++ b/VLC/libvlc_internal.h @@ -0,0 +1,352 @@ +/***************************************************************************** + * libvlc_internal.h : Definition of opaque structures for libvlc exported API + * Also contains some internal utility functions + ***************************************************************************** + * Copyright (C) 2005 the VideoLAN team + * $Id$ + * + * Authors: Clément Stenac + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef _LIBVLC_INTERNAL_H +#define _LIBVLC_INTERNAL_H 1 + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc.h" +#include "libvlc_structures.h" + +#include "vlc_common.h" +#include "vlc_arrays.h" +#include "vlc_input.h" + +/*************************************************************************** + * Internal creation and destruction functions + ***************************************************************************/ +VLC_EXPORT (libvlc_int_t *, libvlc_InternalCreate, ( void ) ); +VLC_EXPORT (int, libvlc_InternalInit, ( libvlc_int_t *, int, const char *ppsz_argv[] ) ); +VLC_EXPORT (int, libvlc_InternalCleanup, ( libvlc_int_t * ) ); +VLC_EXPORT (int, libvlc_InternalDestroy, ( libvlc_int_t * ) ); + +VLC_EXPORT (int, libvlc_InternalAddIntf, ( libvlc_int_t *, const char * ) ); + +/*************************************************************************** + * Opaque structures for libvlc API + ***************************************************************************/ + +typedef int * libvlc_media_list_path_t; /* (Media List Player Internal) */ + +typedef enum libvlc_lock_state_t +{ + libvlc_Locked, + libvlc_UnLocked +} libvlc_lock_state_t; + +struct libvlc_instance_t +{ + libvlc_int_t *p_libvlc_int; + vlm_t *p_vlm; + int b_playlist_locked; + unsigned ref_count; + vlc_mutex_t instance_lock; + vlc_mutex_t event_callback_lock; + struct libvlc_callback_entry_list_t *p_callback_list; +}; + +struct libvlc_media_t +{ + libvlc_event_manager_t * p_event_manager; + int b_preparsed; + input_item_t *p_input_item; + int i_refcount; + libvlc_instance_t *p_libvlc_instance; + libvlc_state_t state; + struct libvlc_media_list_t *p_subitems; /* A media descriptor can have + * Sub item */ + void *p_user_data; /* Allows for VLC.framework to hook into media descriptor without creating a new VLCMedia object. */ +}; + +struct libvlc_media_list_t +{ + libvlc_event_manager_t * p_event_manager; + libvlc_instance_t * p_libvlc_instance; + int i_refcount; + vlc_mutex_t object_lock; + libvlc_media_t * p_md; /* The media from which the + * mlist comes, if any. */ + vlc_array_t items; + + /* Other way to see that media list */ + /* Used in flat_media_list.c */ + libvlc_media_list_t * p_flat_mlist; + + /* This indicates if this media list is read-only + * from a user point of view */ + bool b_read_only; +}; + +typedef libvlc_media_list_view_t * (*libvlc_media_list_view_constructor_func_t)( libvlc_media_list_t * p_mlist, libvlc_exception_t * p_e ) ; +typedef void (*libvlc_media_list_view_release_func_t)( libvlc_media_list_view_t * p_mlv ) ; + +typedef int (*libvlc_media_list_view_count_func_t)( libvlc_media_list_view_t * p_mlv, + libvlc_exception_t * ) ; + +typedef libvlc_media_t * + (*libvlc_media_list_view_item_at_index_func_t)( + libvlc_media_list_view_t * p_mlv, + int index, + libvlc_exception_t * ) ; + +typedef libvlc_media_list_view_t * + (*libvlc_media_list_view_children_at_index_func_t)( + libvlc_media_list_view_t * p_mlv, + int index, + libvlc_exception_t * ) ; + +/* A way to see a media list */ +struct libvlc_media_list_view_t +{ + libvlc_event_manager_t * p_event_manager; + libvlc_instance_t * p_libvlc_instance; + int i_refcount; + vlc_mutex_t object_lock; + + libvlc_media_list_t * p_mlist; + + struct libvlc_media_list_view_private_t * p_this_view_data; + + /* Accessors */ + libvlc_media_list_view_count_func_t pf_count; + libvlc_media_list_view_item_at_index_func_t pf_item_at_index; + libvlc_media_list_view_children_at_index_func_t pf_children_at_index; + + libvlc_media_list_view_constructor_func_t pf_constructor; + libvlc_media_list_view_release_func_t pf_release; + + /* Notification callback */ + void (*pf_ml_item_added)(const libvlc_event_t *, libvlc_media_list_view_t *); + void (*pf_ml_item_removed)(const libvlc_event_t *, libvlc_media_list_view_t *); +}; + +struct libvlc_media_player_t +{ + int i_refcount; + vlc_mutex_t object_lock; + input_thread_t * p_input_thread; + struct libvlc_instance_t * p_libvlc_instance; /* Parent instance */ + libvlc_media_t * p_md; /* current media descriptor */ + libvlc_event_manager_t * p_event_manager; + libvlc_drawable_t drawable; + + bool b_own_its_input_thread; +}; + +struct libvlc_media_list_player_t +{ + libvlc_event_manager_t * p_event_manager; + libvlc_instance_t * p_libvlc_instance; + int i_refcount; + vlc_mutex_t object_lock; + libvlc_media_list_path_t current_playing_item_path; + libvlc_media_t * p_current_playing_item; + libvlc_media_list_t * p_mlist; + libvlc_media_player_t * p_mi; +}; + +struct libvlc_media_library_t +{ + libvlc_event_manager_t * p_event_manager; + libvlc_instance_t * p_libvlc_instance; + int i_refcount; + libvlc_media_list_t * p_mlist; +}; + +struct libvlc_media_discoverer_t +{ + libvlc_event_manager_t * p_event_manager; + libvlc_instance_t * p_libvlc_instance; + services_discovery_t * p_sd; + libvlc_media_list_t * p_mlist; + bool running; + vlc_dictionary_t catname_to_submedialist; +}; + +/* + * Event Handling + */ +/* Example usage + * + * struct libvlc_cool_object_t + * { + * ... + * libvlc_event_manager_t * p_event_manager; + * ... + * } + * + * libvlc_my_cool_object_new() + * { + * ... + * p_self->p_event_manager = libvlc_event_manager_init( p_self, + * p_self->p_libvlc_instance, p_e); + * libvlc_event_manager_register_event_type(p_self->p_event_manager, + * libvlc_MyCoolObjectDidSomething, p_e) + * ... + * } + * + * libvlc_my_cool_object_release() + * { + * ... + * libvlc_event_manager_release( p_self->p_event_manager ); + * ... + * } + * + * libvlc_my_cool_object_do_something() + * { + * ... + * libvlc_event_t event; + * event.type = libvlc_MyCoolObjectDidSomething; + * event.u.my_cool_object_did_something.what_it_did = kSomething; + * libvlc_event_send( p_self->p_event_manager, &event ); + * } + * */ + +typedef struct libvlc_event_listener_t +{ + libvlc_event_type_t event_type; + void * p_user_data; + libvlc_callback_t pf_callback; +} libvlc_event_listener_t; + +typedef struct libvlc_event_listeners_group_t +{ + libvlc_event_type_t event_type; + vlc_array_t listeners; + bool b_sublistener_removed; +} libvlc_event_listeners_group_t; + +typedef struct libvlc_event_manager_t +{ + void * p_obj; + struct libvlc_instance_t * p_libvlc_instance; + vlc_array_t listeners_groups; + vlc_mutex_t object_lock; + vlc_mutex_t event_sending_lock; +} libvlc_event_sender_t; + + +/*************************************************************************** + * Other internal functions + ***************************************************************************/ +input_thread_t *libvlc_get_input_thread( + libvlc_media_player_t *, + libvlc_exception_t * ); + +/* Media instance */ +libvlc_media_player_t * +libvlc_media_player_new_from_input_thread( libvlc_instance_t *, + input_thread_t *, + libvlc_exception_t * ); + +void libvlc_media_player_destroy( + libvlc_media_player_t * ); + +/* Media Descriptor */ +libvlc_media_t * libvlc_media_new_from_input_item( + libvlc_instance_t *, input_item_t *, + libvlc_exception_t * ); + +void libvlc_media_set_state( + libvlc_media_t *, libvlc_state_t, + libvlc_exception_t * ); + +/* Media List */ +void _libvlc_media_list_add_media( + libvlc_media_list_t * p_mlist, + libvlc_media_t * p_md, + libvlc_exception_t * p_e ); + +void _libvlc_media_list_insert_media( + libvlc_media_list_t * p_mlist, + libvlc_media_t * p_md, int index, + libvlc_exception_t * p_e ); + +void _libvlc_media_list_remove_index( + libvlc_media_list_t * p_mlist, int index, + libvlc_exception_t * p_e ); + +/* Media List View */ +libvlc_media_list_view_t * libvlc_media_list_view_new( + libvlc_media_list_t * p_mlist, + libvlc_media_list_view_count_func_t pf_count, + libvlc_media_list_view_item_at_index_func_t pf_item_at_index, + libvlc_media_list_view_children_at_index_func_t pf_children_at_index, + libvlc_media_list_view_constructor_func_t pf_constructor, + libvlc_media_list_view_release_func_t pf_release, + void * this_view_data, + libvlc_exception_t * p_e ); + +void libvlc_media_list_view_set_ml_notification_callback( + libvlc_media_list_view_t * p_mlv, + void (*item_added)(const libvlc_event_t *, libvlc_media_list_view_t *), + void (*item_removed)(const libvlc_event_t *, libvlc_media_list_view_t *) ); + +void libvlc_media_list_view_will_delete_item( + libvlc_media_list_view_t * p_mlv, + libvlc_media_t * p_item, int index ); + +void libvlc_media_list_view_item_deleted( + libvlc_media_list_view_t * p_mlv, + libvlc_media_t * p_item, int index ); + +void libvlc_media_list_view_will_add_item ( + libvlc_media_list_view_t * p_mlv, + libvlc_media_t * p_item, int index ); + +void libvlc_media_list_view_item_added( + libvlc_media_list_view_t * p_mlv, + libvlc_media_t * p_item, int index ); + +/* Events */ +libvlc_event_manager_t * libvlc_event_manager_new( + void * p_obj, libvlc_instance_t * p_libvlc_inst, + libvlc_exception_t *p_e ); + +void libvlc_event_manager_release( + libvlc_event_manager_t * p_em ); + +void libvlc_event_manager_register_event_type( + libvlc_event_manager_t * p_em, + libvlc_event_type_t event_type, + libvlc_exception_t * p_e ); + +void libvlc_event_send( + libvlc_event_manager_t * p_em, + libvlc_event_t * p_event ); + + +/* Exception shorcuts */ + +#define RAISENULL( psz,a... ) { libvlc_exception_raise( p_e, psz,##a ); \ + return NULL; } +#define RAISEVOID( psz,a... ) { libvlc_exception_raise( p_e, psz,##a ); \ + return; } +#define RAISEZERO( psz,a... ) { libvlc_exception_raise( p_e, psz,##a ); \ + return 0; } + +#endif diff --git a/VLC/loadsave.c b/VLC/loadsave.c new file mode 100644 index 0000000..16098ca --- /dev/null +++ b/VLC/loadsave.c @@ -0,0 +1,213 @@ +/***************************************************************************** + * loadsave.c : Playlist loading / saving functions + ***************************************************************************** + * Copyright (C) 1999-2004 the VideoLAN team + * $Id$ + * + * Authors: Samuel Hocevar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "vlc_playlist.h" +#include "vlc_events.h" +#include "playlist_internal.h" +#include "configuration.h" +#include "vlc_charset.h" + +#include +#include +#include +#include + +int playlist_Export( playlist_t * p_playlist, const char *psz_filename , + playlist_item_t *p_export_root,const char *psz_type ) +{ + module_t *p_module; + playlist_export_t *p_export; + + if( p_export_root == NULL ) return VLC_EGENERIC; + + msg_Dbg( p_playlist, "saving %s to file %s", + p_export_root->p_input->psz_name, psz_filename ); + + /* Prepare the playlist_export_t structure */ + p_export = (playlist_export_t *)malloc( sizeof(playlist_export_t) ); + if( !p_export) + return VLC_ENOMEM; + p_export->psz_filename = NULL; + if ( psz_filename ) + p_export->psz_filename = strdup( psz_filename ); + p_export->p_file = utf8_fopen( psz_filename, "wt" ); + if( !p_export->p_file ) + { + msg_Err( p_playlist , "could not create playlist file %s (%m)", + psz_filename ); + return VLC_EGENERIC; + } + + p_export->p_root = p_export_root; + + /* Lock the playlist */ + vlc_object_lock( p_playlist ); + p_playlist->p_private = (void *)p_export; + + /* And call the module ! All work is done now */ + p_module = module_Need( p_playlist, "playlist export", psz_type, true); + if( !p_module ) + { + msg_Warn( p_playlist, "exporting playlist failed" ); + vlc_object_unlock( p_playlist ); + return VLC_ENOOBJ; + } + module_Unneed( p_playlist , p_module ); + + /* Clean up */ + fclose( p_export->p_file ); + free( p_export->psz_filename ); + free ( p_export ); + p_playlist->p_private = NULL; + vlc_object_unlock( p_playlist ); + + return VLC_SUCCESS; +} + +/***************************************************************************** + * A subitem has been added to the Media Library (Event Callback) + *****************************************************************************/ +static void input_item_subitem_added( const vlc_event_t * p_event, + void * user_data ) +{ + playlist_t *p_playlist = user_data; + input_item_t *p_item = p_event->u.input_item_subitem_added.p_new_child; + + /* playlist_AddInput() can fail, but we have no way to report that .. + * Any way when it has failed, either the playlist is dying, either OOM */ + playlist_AddInput( p_playlist, p_item, PLAYLIST_APPEND, PLAYLIST_END, + false, pl_Unlocked ); +} + +int playlist_MLLoad( playlist_t *p_playlist ) +{ + char *psz_datadir = config_GetUserDataDir(); + char *psz_uri = NULL; + input_item_t *p_input; + + if( !config_GetInt( p_playlist, "media-library") ) return VLC_SUCCESS; + if( !psz_datadir ) /* XXX: This should never happen */ + { + msg_Err( p_playlist, "no data directory, cannot load media library") ; + return VLC_EGENERIC; + } + + if( asprintf( &psz_uri, "%s" DIR_SEP "ml.xspf", psz_datadir ) == -1 ) + { + psz_uri = NULL; + goto error; + } + struct stat p_stat; + /* checks if media library file is present */ + if( utf8_stat( psz_uri , &p_stat ) ) + goto error; + free( psz_uri ); + + /* FIXME: WTF? stat() should never be used right before open()! */ + if( asprintf( &psz_uri, "file/xspf-open://%s" DIR_SEP "ml.xspf", + psz_datadir ) == -1 ) + { + psz_uri = NULL; + goto error; + } + free( psz_datadir ); + psz_datadir = NULL; + + const char *const psz_option = "meta-file"; + /* that option has to be cleaned in input_item_subitem_added() */ + /* vlc_gc_decref() in the same function */ + p_input = input_item_NewExt( p_playlist, psz_uri, + _("Media Library"), 1, &psz_option, -1 ); + if( p_input == NULL ) + goto error; + + PL_LOCK; + if( p_playlist->p_ml_onelevel->p_input ) + vlc_gc_decref( p_playlist->p_ml_onelevel->p_input ); + if( p_playlist->p_ml_category->p_input ) + vlc_gc_decref( p_playlist->p_ml_category->p_input ); + + p_playlist->p_ml_onelevel->p_input = + p_playlist->p_ml_category->p_input = p_input; + /* We save the input at two different place, incref */ + vlc_gc_incref( p_input ); + vlc_gc_incref( p_input ); + + vlc_event_attach( &p_input->event_manager, vlc_InputItemSubItemAdded, + input_item_subitem_added, p_playlist ); + + p_playlist->b_doing_ml = true; + PL_UNLOCK; + + stats_TimerStart( p_playlist, "ML Load", STATS_TIMER_ML_LOAD ); + input_Read( p_playlist, p_input, true ); + stats_TimerStop( p_playlist,STATS_TIMER_ML_LOAD ); + + PL_LOCK; + p_playlist->b_doing_ml = false; + PL_UNLOCK; + + vlc_event_detach( &p_input->event_manager, vlc_InputItemSubItemAdded, + input_item_subitem_added, p_playlist ); + + vlc_gc_decref( p_input ); + free( psz_uri ); + return VLC_SUCCESS; + +error: + free( psz_uri ); + free( psz_datadir ); + return VLC_ENOMEM; +} + +int playlist_MLDump( playlist_t *p_playlist ) +{ + char *psz_datadir = config_GetUserDataDir(); + if( !config_GetInt( p_playlist, "media-library") ) return VLC_SUCCESS; + if( !psz_datadir ) /* XXX: This should never happen */ + { + msg_Err( p_playlist, "no data directory, cannot save media library") ; + return VLC_EGENERIC; + } + + char psz_dirname[ strlen( psz_datadir ) + sizeof( DIR_SEP "ml.xspf")]; + strcpy( psz_dirname, psz_datadir ); + free( psz_datadir ); + if( config_CreateDir( (vlc_object_t *)p_playlist, psz_dirname ) ) + { + return VLC_EGENERIC; + } + + strcat( psz_dirname, DIR_SEP "ml.xspf" ); + + stats_TimerStart( p_playlist, "ML Dump", STATS_TIMER_ML_DUMP ); + playlist_Export( p_playlist, psz_dirname, p_playlist->p_ml_category, + "export-xspf" ); + stats_TimerStop( p_playlist, STATS_TIMER_ML_DUMP ); + + return VLC_SUCCESS; +} diff --git a/VLC/log.c b/VLC/log.c new file mode 100644 index 0000000..b8fb181 --- /dev/null +++ b/VLC/log.c @@ -0,0 +1,192 @@ +/***************************************************************************** + * log.c: libvlc new API log functions + ***************************************************************************** + * Copyright (C) 2005 the VideoLAN team + * + * $Id$ + * + * Authors: Damien Fouilleul + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#include "libvlc_internal.h" +#include "../libvlc.h" +#include + +struct libvlc_log_t +{ + libvlc_instance_t *p_instance; + msg_subscription_t *p_messages; +}; + +struct libvlc_log_iterator_t +{ + msg_subscription_t *p_messages; + int i_start; + int i_pos; + int i_end; +}; + +unsigned libvlc_get_log_verbosity( const libvlc_instance_t *p_instance, libvlc_exception_t *p_e ) +{ + if( p_instance ) + { + libvlc_priv_t *p_priv = libvlc_priv( p_instance->p_libvlc_int ); + return p_priv->i_verbose; + } + RAISEZERO("Invalid VLC instance!"); +} + +void libvlc_set_log_verbosity( libvlc_instance_t *p_instance, unsigned level, libvlc_exception_t *p_e ) +{ + if( p_instance ) + { + libvlc_priv_t *p_priv = libvlc_priv( p_instance->p_libvlc_int ); + p_priv->i_verbose = level; + } + else + RAISEVOID("Invalid VLC instance!"); +} + +libvlc_log_t *libvlc_log_open( libvlc_instance_t *p_instance, libvlc_exception_t *p_e ) +{ + struct libvlc_log_t *p_log = + (struct libvlc_log_t *)malloc(sizeof(struct libvlc_log_t)); + + if( !p_log ) RAISENULL( "Out of memory" ); + + p_log->p_instance = p_instance; + p_log->p_messages = msg_Subscribe(p_instance->p_libvlc_int); + + if( !p_log->p_messages ) + { + free( p_log ); + RAISENULL( "Out of memory" ); + } + + libvlc_retain( p_instance ); + return p_log; +} + +void libvlc_log_close( libvlc_log_t *p_log, libvlc_exception_t *p_e ) +{ + if( p_log && p_log->p_messages ) + { + msg_Unsubscribe(p_log->p_instance->p_libvlc_int, p_log->p_messages); + libvlc_release( p_log->p_instance ); + free(p_log); + } + else + RAISEVOID("Invalid log object!"); +} + +unsigned libvlc_log_count( const libvlc_log_t *p_log, libvlc_exception_t *p_e ) +{ + if( p_log && p_log->p_messages ) + { + int i_start = p_log->p_messages->i_start; + int i_stop = *(p_log->p_messages->pi_stop); + + if( i_stop >= i_start ) + return i_stop-i_start; + else + return VLC_MSG_QSIZE-(i_start-i_stop); + } + RAISEZERO("Invalid log object!"); +} + +void libvlc_log_clear( libvlc_log_t *p_log, libvlc_exception_t *p_e ) +{ + if( p_log && p_log->p_messages ) + { + vlc_mutex_lock(p_log->p_messages->p_lock); + p_log->p_messages->i_start = *(p_log->p_messages->pi_stop); + vlc_mutex_unlock(p_log->p_messages->p_lock); + } + else + RAISEVOID("Invalid log object!"); +} + +libvlc_log_iterator_t *libvlc_log_get_iterator( const libvlc_log_t *p_log, libvlc_exception_t *p_e ) +{ + if( p_log && p_log->p_messages ) + { + struct libvlc_log_iterator_t *p_iter = + (struct libvlc_log_iterator_t *)malloc(sizeof(struct libvlc_log_iterator_t)); + + if( !p_iter ) RAISENULL( "Out of memory" ); + + vlc_mutex_lock(p_log->p_messages->p_lock); + p_iter->p_messages = p_log->p_messages; + p_iter->i_start = p_log->p_messages->i_start; + p_iter->i_pos = p_log->p_messages->i_start; + p_iter->i_end = *(p_log->p_messages->pi_stop); + vlc_mutex_unlock(p_log->p_messages->p_lock); + + return p_iter; + } + RAISENULL("Invalid log object!"); +} + +void libvlc_log_iterator_free( libvlc_log_iterator_t *p_iter, libvlc_exception_t *p_e ) +{ + if( p_iter ) + { + free(p_iter); + } + else + RAISEVOID("Invalid log iterator!"); +} + +int libvlc_log_iterator_has_next( const libvlc_log_iterator_t *p_iter, libvlc_exception_t *p_e ) +{ + if( p_iter ) + { + return p_iter->i_pos != p_iter->i_end; + } + RAISEZERO("Invalid log iterator!"); +} + +libvlc_log_message_t *libvlc_log_iterator_next( libvlc_log_iterator_t *p_iter, + libvlc_log_message_t *buffer, + libvlc_exception_t *p_e ) +{ + int i_pos; + + if( !p_iter ) + RAISENULL("Invalid log iterator!"); + if( !buffer ) + RAISENULL("Invalid message buffer!"); + + i_pos = p_iter->i_pos; + if( i_pos != p_iter->i_end ) + { + msg_item_t *msg; + vlc_mutex_lock(p_iter->p_messages->p_lock); + msg = p_iter->p_messages->p_msg+i_pos; + buffer->i_severity = msg->i_type; + buffer->psz_type = msg->psz_object_type; + buffer->psz_name = msg->psz_module; + buffer->psz_header = msg->psz_header; + buffer->psz_message = msg->psz_msg; + p_iter->i_pos = ++i_pos % VLC_MSG_QSIZE; + vlc_mutex_unlock(p_iter->p_messages->p_lock); + + return buffer; + } + RAISENULL("No more messages"); +} + diff --git a/VLC/media.c b/VLC/media.c new file mode 100644 index 0000000..0192227 --- /dev/null +++ b/VLC/media.c @@ -0,0 +1,586 @@ +/***************************************************************************** + * media.c: Libvlc API media descripor management + ***************************************************************************** + * Copyright (C) 2007 the VideoLAN team + * $Id$ + * + * Authors: Pierre d'Herbemont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#include "libvlc_internal.h" +#include "libvlc.h" +#include +#include +#include + +/* For the preparser */ +#include + +static const vlc_meta_type_t libvlc_to_vlc_meta[] = +{ + [libvlc_meta_Title] = vlc_meta_Title, + [libvlc_meta_Artist] = vlc_meta_Artist, + [libvlc_meta_Genre] = vlc_meta_Genre, + [libvlc_meta_Copyright] = vlc_meta_Copyright, + [libvlc_meta_Album] = vlc_meta_Album, + [libvlc_meta_TrackNumber] = vlc_meta_TrackNumber, + [libvlc_meta_Description] = vlc_meta_Description, + [libvlc_meta_Rating] = vlc_meta_Rating, + [libvlc_meta_Date] = vlc_meta_Date, + [libvlc_meta_Setting] = vlc_meta_Setting, + [libvlc_meta_URL] = vlc_meta_URL, + [libvlc_meta_Language] = vlc_meta_Language, + [libvlc_meta_NowPlaying] = vlc_meta_NowPlaying, + [libvlc_meta_Publisher] = vlc_meta_Publisher, + [libvlc_meta_EncodedBy] = vlc_meta_EncodedBy, + [libvlc_meta_ArtworkURL] = vlc_meta_ArtworkURL, + [libvlc_meta_TrackID] = vlc_meta_TrackID +}; + +static const libvlc_meta_t vlc_to_libvlc_meta[] = +{ + [vlc_meta_Title] = libvlc_meta_Title, + [vlc_meta_Artist] = libvlc_meta_Artist, + [vlc_meta_Genre] = libvlc_meta_Genre, + [vlc_meta_Copyright] = libvlc_meta_Copyright, + [vlc_meta_Album] = libvlc_meta_Album, + [vlc_meta_TrackNumber] = libvlc_meta_TrackNumber, + [vlc_meta_Description] = libvlc_meta_Description, + [vlc_meta_Rating] = libvlc_meta_Rating, + [vlc_meta_Date] = libvlc_meta_Date, + [vlc_meta_Setting] = libvlc_meta_Setting, + [vlc_meta_URL] = libvlc_meta_URL, + [vlc_meta_Language] = libvlc_meta_Language, + [vlc_meta_NowPlaying] = libvlc_meta_NowPlaying, + [vlc_meta_Publisher] = libvlc_meta_Publisher, + [vlc_meta_EncodedBy] = libvlc_meta_EncodedBy, + [vlc_meta_ArtworkURL] = libvlc_meta_ArtworkURL, + [vlc_meta_TrackID] = libvlc_meta_TrackID +}; + +/************************************************************************** + * input_item_subitem_added (Private) (vlc event Callback) + **************************************************************************/ +static void input_item_subitem_added( const vlc_event_t *p_event, + void * user_data ) +{ + libvlc_media_t * p_md = user_data; + libvlc_media_t * p_md_child; + libvlc_event_t event; + + p_md_child = libvlc_media_new_from_input_item( + p_md->p_libvlc_instance, + p_event->u.input_item_subitem_added.p_new_child, NULL ); + + /* Add this to our media list */ + if( !p_md->p_subitems ) + { + p_md->p_subitems = libvlc_media_list_new( p_md->p_libvlc_instance, NULL ); + libvlc_media_list_set_media( p_md->p_subitems, p_md, NULL ); + } + if( p_md->p_subitems ) + { + libvlc_media_list_add_media( p_md->p_subitems, p_md_child, NULL ); + } + + /* Construct the event */ + event.type = libvlc_MediaSubItemAdded; + event.u.media_subitem_added.new_child = p_md_child; + + /* Send the event */ + libvlc_event_send( p_md->p_event_manager, &event ); + libvlc_media_release( p_md_child ); +} + +/************************************************************************** + * input_item_meta_changed (Private) (vlc event Callback) + **************************************************************************/ +static void input_item_meta_changed( const vlc_event_t *p_event, + void * user_data ) +{ + libvlc_media_t * p_md = user_data; + libvlc_event_t event; + + /* Construct the event */ + event.type = libvlc_MediaMetaChanged; + event.u.media_meta_changed.meta_type = + vlc_to_libvlc_meta[p_event->u.input_item_meta_changed.meta_type]; + + /* Send the event */ + libvlc_event_send( p_md->p_event_manager, &event ); +} + +/************************************************************************** + * input_item_duration_changed (Private) (vlc event Callback) + **************************************************************************/ +static void input_item_duration_changed( const vlc_event_t *p_event, + void * user_data ) +{ + libvlc_media_t * p_md = user_data; + libvlc_event_t event; + + /* Construct the event */ + event.type = libvlc_MediaDurationChanged; + event.u.media_duration_changed.new_duration = + p_event->u.input_item_duration_changed.new_duration; + + /* Send the event */ + libvlc_event_send( p_md->p_event_manager, &event ); +} + +/************************************************************************** + * input_item_preparsed_changed (Private) (vlc event Callback) + **************************************************************************/ +static void input_item_preparsed_changed( const vlc_event_t *p_event, + void * user_data ) +{ + libvlc_media_t * p_md = user_data; + libvlc_event_t event; + + /* Construct the event */ + event.type = libvlc_MediaPreparsedChanged; + event.u.media_preparsed_changed.new_status = + p_event->u.input_item_preparsed_changed.new_status; + + /* Send the event */ + libvlc_event_send( p_md->p_event_manager, &event ); +} + +/************************************************************************** + * Install event handler (Private) + **************************************************************************/ +static void install_input_item_observer( libvlc_media_t *p_md ) +{ + vlc_event_attach( &p_md->p_input_item->event_manager, + vlc_InputItemSubItemAdded, + input_item_subitem_added, + p_md ); + vlc_event_attach( &p_md->p_input_item->event_manager, + vlc_InputItemMetaChanged, + input_item_meta_changed, + p_md ); + vlc_event_attach( &p_md->p_input_item->event_manager, + vlc_InputItemDurationChanged, + input_item_duration_changed, + p_md ); + vlc_event_attach( &p_md->p_input_item->event_manager, + vlc_InputItemPreparsedChanged, + input_item_preparsed_changed, + p_md ); +} + +/************************************************************************** + * Uninstall event handler (Private) + **************************************************************************/ +static void uninstall_input_item_observer( libvlc_media_t *p_md ) +{ + vlc_event_detach( &p_md->p_input_item->event_manager, + vlc_InputItemSubItemAdded, + input_item_subitem_added, + p_md ); + vlc_event_detach( &p_md->p_input_item->event_manager, + vlc_InputItemMetaChanged, + input_item_meta_changed, + p_md ); + vlc_event_detach( &p_md->p_input_item->event_manager, + vlc_InputItemDurationChanged, + input_item_duration_changed, + p_md ); + vlc_event_detach( &p_md->p_input_item->event_manager, + vlc_InputItemPreparsedChanged, + input_item_preparsed_changed, + p_md ); +} + +/************************************************************************** + * Preparse if not already done (Private) + **************************************************************************/ +static void preparse_if_needed( libvlc_media_t *p_md ) +{ + /* XXX: need some locking here */ + if (!p_md->b_preparsed) + { + playlist_PreparseEnqueue( + libvlc_priv (p_md->p_libvlc_instance->p_libvlc_int)->p_playlist, + p_md->p_input_item ); + p_md->b_preparsed = true; + } +} + +/************************************************************************** + * Create a new media descriptor object from an input_item + * (libvlc internal) + * That's the generic constructor + **************************************************************************/ +libvlc_media_t * libvlc_media_new_from_input_item( + libvlc_instance_t *p_instance, + input_item_t *p_input_item, + libvlc_exception_t *p_e ) +{ + libvlc_media_t * p_md; + + if (!p_input_item) + { + libvlc_exception_raise( p_e, "No input item given" ); + return NULL; + } + + p_md = malloc( sizeof(libvlc_media_t) ); + if( !p_md ) + { + libvlc_exception_raise( p_e, "Not enough memory" ); + return NULL; + } + + p_md->p_libvlc_instance = p_instance; + p_md->p_input_item = p_input_item; + p_md->b_preparsed = false; + p_md->i_refcount = 1; + p_md->p_user_data = NULL; + + p_md->state = libvlc_NothingSpecial; + + /* A media descriptor can be a playlist. When you open a playlist + * It can give a bunch of item to read. */ + p_md->p_subitems = NULL; + + p_md->p_event_manager = libvlc_event_manager_new( p_md, p_instance, p_e ); + libvlc_event_manager_register_event_type( p_md->p_event_manager, + libvlc_MediaMetaChanged, p_e ); + libvlc_event_manager_register_event_type( p_md->p_event_manager, + libvlc_MediaSubItemAdded, p_e ); + libvlc_event_manager_register_event_type( p_md->p_event_manager, + libvlc_MediaFreed, p_e ); + libvlc_event_manager_register_event_type( p_md->p_event_manager, + libvlc_MediaDurationChanged, p_e ); + libvlc_event_manager_register_event_type( p_md->p_event_manager, + libvlc_MediaStateChanged, p_e ); + + vlc_gc_incref( p_md->p_input_item ); + + install_input_item_observer( p_md ); + + return p_md; +} + +/************************************************************************** + * Create a new media descriptor object + **************************************************************************/ +libvlc_media_t * libvlc_media_new( + libvlc_instance_t *p_instance, + const char * psz_mrl, + libvlc_exception_t *p_e ) +{ + input_item_t * p_input_item; + libvlc_media_t * p_md; + + p_input_item = input_item_New( p_instance->p_libvlc_int, psz_mrl, NULL ); + + if (!p_input_item) + { + libvlc_exception_raise( p_e, "Can't create md's input_item" ); + return NULL; + } + + p_md = libvlc_media_new_from_input_item( p_instance, + p_input_item, p_e ); + + /* The p_input_item is retained in libvlc_media_new_from_input_item */ + vlc_gc_decref( p_input_item ); + + return p_md; +} + +/************************************************************************** + * Create a new media descriptor object + **************************************************************************/ +libvlc_media_t * libvlc_media_new_as_node( + libvlc_instance_t *p_instance, + const char * psz_name, + libvlc_exception_t *p_e ) +{ + input_item_t * p_input_item; + libvlc_media_t * p_md; + + p_input_item = input_item_New( p_instance->p_libvlc_int, "vlc://nop", psz_name ); + + if (!p_input_item) + { + libvlc_exception_raise( p_e, "Can't create md's input_item" ); + return NULL; + } + + p_md = libvlc_media_new_from_input_item( p_instance, + p_input_item, p_e ); + + p_md->p_subitems = libvlc_media_list_new( p_md->p_libvlc_instance, NULL ); + + return p_md; +} + +/************************************************************************** + * Add an option to the media descriptor, + * that will be used to determine how the media_player will read the + * media. This allow to use VLC advanced reading/streaming + * options in a per-media basis + * + * The options are detailled in vlc --long-help, for instance "--sout-all" + **************************************************************************/ +void libvlc_media_add_option( + libvlc_media_t * p_md, + const char * ppsz_option, + libvlc_exception_t *p_e ) +{ + VLC_UNUSED(p_e); + input_item_AddOpt( p_md->p_input_item, ppsz_option, + VLC_INPUT_OPTION_UNIQUE|VLC_INPUT_OPTION_TRUSTED ); +} + +/************************************************************************** + * Delete a media descriptor object + **************************************************************************/ +void libvlc_media_release( libvlc_media_t *p_md ) +{ + if (!p_md) + return; + + p_md->i_refcount--; + + if( p_md->i_refcount > 0 ) + return; + + if( p_md->p_subitems ) + libvlc_media_list_release( p_md->p_subitems ); + + uninstall_input_item_observer( p_md ); + vlc_gc_decref( p_md->p_input_item ); + + /* Construct the event */ + libvlc_event_t event; + event.type = libvlc_MediaFreed; + event.u.media_freed.md = p_md; + + /* Send the event */ + libvlc_event_send( p_md->p_event_manager, &event ); + + libvlc_event_manager_release( p_md->p_event_manager ); + + free( p_md ); +} + +/************************************************************************** + * Retain a media descriptor object + **************************************************************************/ +void libvlc_media_retain( libvlc_media_t *p_md ) +{ + if (!p_md) + return; + + p_md->i_refcount++; +} + +/************************************************************************** + * Duplicate a media descriptor object + **************************************************************************/ +libvlc_media_t * +libvlc_media_duplicate( libvlc_media_t *p_md_orig ) +{ + return libvlc_media_new_from_input_item( + p_md_orig->p_libvlc_instance, p_md_orig->p_input_item, NULL ); +} + +/************************************************************************** + * Get mrl from a media descriptor object + **************************************************************************/ +char * +libvlc_media_get_mrl( libvlc_media_t * p_md, + libvlc_exception_t * p_e ) +{ + VLC_UNUSED(p_e); + return input_item_GetURI( p_md->p_input_item ); +} + +/************************************************************************** + * Getter for meta information + **************************************************************************/ + +char * libvlc_media_get_meta( libvlc_media_t *p_md, + libvlc_meta_t e_meta, + libvlc_exception_t *p_e ) +{ + char * psz_meta; + VLC_UNUSED(p_e); + + /* XXX: locking */ + + preparse_if_needed( p_md ); + + psz_meta = input_item_GetMeta( p_md->p_input_item, + libvlc_to_vlc_meta[e_meta] ); + + if( e_meta == libvlc_meta_ArtworkURL && !psz_meta ) + { + playlist_AskForArtEnqueue( + libvlc_priv(p_md->p_libvlc_instance->p_libvlc_int)->p_playlist, + p_md->p_input_item ); + } + + /* Should be integrated in core */ + if( !psz_meta && e_meta == libvlc_meta_Title && p_md->p_input_item->psz_name ) + { + free( psz_meta ); + return strdup( p_md->p_input_item->psz_name ); + } + + return psz_meta; +} + +/************************************************************************** + * Getter for state information + * Can be error, playing, buffering, NothingSpecial. + **************************************************************************/ + +libvlc_state_t +libvlc_media_get_state( libvlc_media_t *p_md, + libvlc_exception_t *p_e ) +{ + VLC_UNUSED(p_e); + return p_md->state; +} + +/************************************************************************** + * Setter for state information (LibVLC Internal) + **************************************************************************/ + +void +libvlc_media_set_state( libvlc_media_t *p_md, + libvlc_state_t state, + libvlc_exception_t *p_e ) +{ + libvlc_event_t event; + VLC_UNUSED(p_e); + + p_md->state = state; + + /* Construct the event */ + event.type = libvlc_MediaStateChanged; + event.u.media_state_changed.new_state = state; + + /* Send the event */ + libvlc_event_send( p_md->p_event_manager, &event ); +} + +/************************************************************************** + * subitems + **************************************************************************/ +libvlc_media_list_t * +libvlc_media_subitems( libvlc_media_t * p_md, + libvlc_exception_t * p_e ) +{ + VLC_UNUSED(p_e); + + if( p_md->p_subitems ) + libvlc_media_list_retain( p_md->p_subitems ); + return p_md->p_subitems; +} + +/************************************************************************** + * event_manager + **************************************************************************/ +libvlc_event_manager_t * +libvlc_media_event_manager( libvlc_media_t * p_md, + libvlc_exception_t * p_e ) +{ + VLC_UNUSED(p_e); + + return p_md->p_event_manager; +} + +/************************************************************************** + * Get duration of media object. + **************************************************************************/ +int64_t +libvlc_media_get_duration( libvlc_media_t * p_md, + libvlc_exception_t * p_e ) +{ + VLC_UNUSED(p_e); + + if( !p_md || !p_md->p_input_item) + { + libvlc_exception_raise( p_e, "No input item" ); + return -1; + } + + return input_item_GetDuration( p_md->p_input_item ); +} + +/************************************************************************** + * Get preparsed status for media object. + **************************************************************************/ +int +libvlc_media_is_preparsed( libvlc_media_t * p_md, + libvlc_exception_t * p_e ) +{ + VLC_UNUSED(p_e); + + if( !p_md || !p_md->p_input_item) + { + libvlc_exception_raise( p_e, "No input item" ); + return false; + } + + return input_item_IsPreparsed( p_md->p_input_item ); +} + +/************************************************************************** + * Sets media descriptor's user_data. user_data is specialized data + * accessed by the host application, VLC.framework uses it as a pointer to + * an native object that references a libvlc_media_t pointer + **************************************************************************/ +void +libvlc_media_set_user_data( libvlc_media_t * p_md, + void * p_new_user_data, + libvlc_exception_t * p_e ) +{ + VLC_UNUSED(p_e); + + if( p_md ) + { + p_md->p_user_data = p_new_user_data; + } +} + +/************************************************************************** + * Get media descriptor's user_data. user_data is specialized data + * accessed by the host application, VLC.framework uses it as a pointer to + * an native object that references a libvlc_media_t pointer + **************************************************************************/ +void * +libvlc_media_get_user_data( libvlc_media_t * p_md, + libvlc_exception_t * p_e ) +{ + VLC_UNUSED(p_e); + + if( p_md ) + { + return p_md->p_user_data; + } + else + { + return NULL; + } +} diff --git a/VLC/media_discoverer.c b/VLC/media_discoverer.c new file mode 100644 index 0000000..5f33b14 --- /dev/null +++ b/VLC/media_discoverer.c @@ -0,0 +1,272 @@ +/***************************************************************************** + * media_discoverer.c: libvlc new API media discoverer functions + ***************************************************************************** + * Copyright (C) 2007 the VideoLAN team + * $Id$ + * + * Authors: Pierre d'Herbemont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#include "libvlc_internal.h" +#include +#include +#include "vlc_services_discovery.h" + +/* + * Private functions + */ + +/************************************************************************** + * services_discovery_item_added (Private) (VLC event callback) + **************************************************************************/ + +static void services_discovery_item_added( const vlc_event_t * p_event, + void * user_data ) +{ + input_item_t * p_item = p_event->u.services_discovery_item_added.p_new_item; + const char * psz_cat = p_event->u.services_discovery_item_added.psz_category; + libvlc_media_t * p_md; + libvlc_media_discoverer_t * p_mdis = user_data; + libvlc_media_list_t * p_mlist = p_mdis->p_mlist; + + p_md = libvlc_media_new_from_input_item( + p_mdis->p_libvlc_instance, + p_item, NULL ); + + /* If we have a category, that mean we have to group the items having + * that category in a media_list. */ + if( psz_cat ) + { + p_mlist = vlc_dictionary_value_for_key( &p_mdis->catname_to_submedialist, psz_cat ); + + if( p_mlist == kVLCDictionaryNotFound ) + { + libvlc_media_t * p_catmd; + p_catmd = libvlc_media_new_as_node( p_mdis->p_libvlc_instance, psz_cat, NULL ); + p_mlist = libvlc_media_subitems( p_catmd, NULL ); + p_mlist->b_read_only = true; + + /* Insert the newly created mlist in our dictionary */ + vlc_dictionary_insert( &p_mdis->catname_to_submedialist, psz_cat, p_mlist ); + + /* Insert the md into the root list */ + libvlc_media_list_lock( p_mdis->p_mlist ); + _libvlc_media_list_add_media( p_mdis->p_mlist, p_catmd, NULL ); + libvlc_media_list_unlock( p_mdis->p_mlist ); + + /* We don't release the mlist cause the dictionary + * doesn't retain the object. But we release the md. */ + libvlc_media_release( p_catmd ); + } + } + else + { + libvlc_media_list_lock( p_mlist ); + _libvlc_media_list_add_media( p_mlist, p_md, NULL ); + libvlc_media_list_unlock( p_mlist ); + } +} + +/************************************************************************** + * services_discovery_item_removed (Private) (VLC event callback) + **************************************************************************/ + +static void services_discovery_item_removed( const vlc_event_t * p_event, + void * user_data ) +{ + input_item_t * p_item = p_event->u.services_discovery_item_added.p_new_item; + libvlc_media_t * p_md; + libvlc_media_discoverer_t * p_mdis = user_data; + + int i, count = libvlc_media_list_count( p_mdis->p_mlist, NULL ); + libvlc_media_list_lock( p_mdis->p_mlist ); + for( i = 0; i < count; i++ ) + { + p_md = libvlc_media_list_item_at_index( p_mdis->p_mlist, i, NULL ); + if( p_md->p_input_item == p_item ) + { + _libvlc_media_list_remove_index( p_mdis->p_mlist, i, NULL ); + break; + } + } + libvlc_media_list_unlock( p_mdis->p_mlist ); +} + +/************************************************************************** + * services_discovery_started (Private) (VLC event callback) + **************************************************************************/ + +static void services_discovery_started( const vlc_event_t * p_event, + void * user_data ) +{ + VLC_UNUSED(p_event); + libvlc_media_discoverer_t * p_mdis = user_data; + libvlc_event_t event; + p_mdis->running = true; + event.type = libvlc_MediaDiscovererStarted; + libvlc_event_send( p_mdis->p_event_manager, &event ); +} + +/************************************************************************** + * services_discovery_ended (Private) (VLC event callback) + **************************************************************************/ + +static void services_discovery_ended( const vlc_event_t * p_event, + void * user_data ) +{ + VLC_UNUSED(p_event); + libvlc_media_discoverer_t * p_mdis = user_data; + libvlc_event_t event; + p_mdis->running = false; + event.type = libvlc_MediaDiscovererEnded; + libvlc_event_send( p_mdis->p_event_manager, &event ); +} + +/* + * Public libvlc functions + */ + +/************************************************************************** + * new (Public) + * + * Init an object. + **************************************************************************/ +libvlc_media_discoverer_t * +libvlc_media_discoverer_new_from_name( libvlc_instance_t * p_inst, + const char * psz_name, + libvlc_exception_t * p_e ) +{ + libvlc_media_discoverer_t * p_mdis; + + p_mdis = malloc(sizeof(libvlc_media_discoverer_t)); + if( !p_mdis ) + { + libvlc_exception_raise( p_e, "Not enough memory" ); + return NULL; + } + + p_mdis->p_libvlc_instance = p_inst; + p_mdis->p_mlist = libvlc_media_list_new( p_inst, NULL ); + p_mdis->p_mlist->b_read_only = true; + p_mdis->running = false; + + vlc_dictionary_init( &p_mdis->catname_to_submedialist, 0 ); + + p_mdis->p_event_manager = libvlc_event_manager_new( p_mdis, + p_inst, NULL ); + + libvlc_event_manager_register_event_type( p_mdis->p_event_manager, + libvlc_MediaDiscovererStarted, NULL ); + libvlc_event_manager_register_event_type( p_mdis->p_event_manager, + libvlc_MediaDiscovererEnded, NULL ); + + p_mdis->p_sd = services_discovery_Create( (vlc_object_t*)p_inst->p_libvlc_int, psz_name ); + + if( !p_mdis->p_sd ) + { + free( p_mdis ); + libvlc_exception_raise( p_e, "Can't find the services_discovery module named '%s'", psz_name ); + return NULL; + } + + vlc_event_attach( services_discovery_EventManager( p_mdis->p_sd ), + vlc_ServicesDiscoveryItemAdded, + services_discovery_item_added, + p_mdis ); + vlc_event_attach( services_discovery_EventManager( p_mdis->p_sd ), + vlc_ServicesDiscoveryItemRemoved, + services_discovery_item_removed, + p_mdis ); + vlc_event_attach( services_discovery_EventManager( p_mdis->p_sd ), + vlc_ServicesDiscoveryStarted, + services_discovery_started, + p_mdis ); + vlc_event_attach( services_discovery_EventManager( p_mdis->p_sd ), + vlc_ServicesDiscoveryEnded, + services_discovery_ended, + p_mdis ); + + services_discovery_Start( p_mdis->p_sd ); + + /* Here we go */ + + return p_mdis; +} + +/************************************************************************** + * release (Public) + **************************************************************************/ +void +libvlc_media_discoverer_release( libvlc_media_discoverer_t * p_mdis ) +{ + int i; + + libvlc_media_list_release( p_mdis->p_mlist ); + services_discovery_Destroy( p_mdis->p_sd ); + + /* Free catname_to_submedialist and all the mlist */ + char ** all_keys = vlc_dictionary_all_keys( &p_mdis->catname_to_submedialist ); + for( i = 0; all_keys[i]; i++ ) + { + libvlc_media_list_t * p_catmlist = vlc_dictionary_value_for_key( &p_mdis->catname_to_submedialist, all_keys[i] ); + libvlc_media_list_release( p_catmlist ); + free( all_keys[i] ); + } + free( all_keys ); + + vlc_dictionary_clear( &p_mdis->catname_to_submedialist ); + + free( p_mdis ); +} + +/************************************************************************** + * localized_name (Public) + **************************************************************************/ +char * +libvlc_media_discoverer_localized_name( libvlc_media_discoverer_t * p_mdis ) +{ + return services_discovery_GetLocalizedName( p_mdis->p_sd ); +} + +/************************************************************************** + * media_list (Public) + **************************************************************************/ +libvlc_media_list_t * +libvlc_media_discoverer_media_list( libvlc_media_discoverer_t * p_mdis ) +{ + libvlc_media_list_retain( p_mdis->p_mlist ); + return p_mdis->p_mlist; +} + +/************************************************************************** + * event_manager (Public) + **************************************************************************/ +libvlc_event_manager_t * +libvlc_media_discoverer_event_manager( libvlc_media_discoverer_t * p_mdis ) +{ + return p_mdis->p_event_manager; +} + + +/************************************************************************** + * running (Public) + **************************************************************************/ +int +libvlc_media_discoverer_is_running( libvlc_media_discoverer_t * p_mdis ) +{ + return p_mdis->running; +} diff --git a/VLC/media_library.c b/VLC/media_library.c new file mode 100644 index 0000000..a85c1b9 --- /dev/null +++ b/VLC/media_library.c @@ -0,0 +1,143 @@ +/***************************************************************************** + * tree.c: libvlc tags tree functions + * Create a tree of the 'tags' of a media_list's medias. + ***************************************************************************** + * Copyright (C) 2007 the VideoLAN team + * $Id$ + * + * Authors: Pierre d'Herbemont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ +#include "libvlc_internal.h" +#include +#include "libvlc.h" +#include "vlc_arrays.h" + +/* + * Private functions + */ + +/* + * Public libvlc functions + */ + +/************************************************************************** + * new (Public) + **************************************************************************/ +libvlc_media_library_t * +libvlc_media_library_new( libvlc_instance_t * p_inst, + libvlc_exception_t * p_e ) +{ + (void)p_e; + libvlc_media_library_t * p_mlib; + + p_mlib = malloc(sizeof(libvlc_media_library_t)); + + if( !p_mlib ) + return NULL; + + p_mlib->p_libvlc_instance = p_inst; + p_mlib->i_refcount = 1; + p_mlib->p_mlist = NULL; + + p_mlib->p_event_manager = libvlc_event_manager_new( p_mlib, p_inst, p_e ); + + return p_mlib; +} + +/************************************************************************** + * release (Public) + **************************************************************************/ +void libvlc_media_library_release( libvlc_media_library_t * p_mlib ) +{ + p_mlib->i_refcount--; + + if( p_mlib->i_refcount > 0 ) + return; + + libvlc_event_manager_release( p_mlib->p_event_manager ); + free( p_mlib ); +} + +/************************************************************************** + * retain (Public) + **************************************************************************/ +void libvlc_media_library_retain( libvlc_media_library_t * p_mlib ) +{ + p_mlib->i_refcount++; +} + +/************************************************************************** + * load (Public) + * + * It doesn't yet load the playlists + **************************************************************************/ +void +libvlc_media_library_load( libvlc_media_library_t * p_mlib, + libvlc_exception_t * p_e ) +{ + char *psz_datadir = config_GetUserDataDir(); + char * psz_uri; + + if( !psz_datadir ) /* XXX: i doubt that this can ever happen */ + { + libvlc_exception_raise( p_e, "Can't get data directory" ); + return; + } + + if( asprintf( &psz_uri, "file/xspf-open://%s" DIR_SEP "ml.xsp", + psz_datadir ) == -1 ) + { + free( psz_datadir ); + libvlc_exception_raise( p_e, "Can't get create the path" ); + return; + } + free( psz_datadir ); + if( p_mlib->p_mlist ) + libvlc_media_list_release( p_mlib->p_mlist ); + + p_mlib->p_mlist = libvlc_media_list_new( + p_mlib->p_libvlc_instance, + p_e ); + + libvlc_media_list_add_file_content( p_mlib->p_mlist, psz_uri, p_e ); + free( psz_uri ); + return; +} + +/************************************************************************** + * save (Public) + **************************************************************************/ +void +libvlc_media_library_save( libvlc_media_library_t * p_mlib, + libvlc_exception_t * p_e ) +{ + (void)p_mlib; + libvlc_exception_raise( p_e, "Not supported" ); +} + +/************************************************************************** + * media_list (Public) + **************************************************************************/ +libvlc_media_list_t * +libvlc_media_library_media_list( libvlc_media_library_t * p_mlib, + libvlc_exception_t * p_e ) +{ + (void)p_e; + if( p_mlib->p_mlist ) + libvlc_media_list_retain( p_mlib->p_mlist ); + return p_mlib->p_mlist; +} diff --git a/VLC/media_list.c b/VLC/media_list.c new file mode 100644 index 0000000..2eae497 --- /dev/null +++ b/VLC/media_list.c @@ -0,0 +1,508 @@ +/***************************************************************************** + * media_list.c: libvlc new API media list functions + ***************************************************************************** + * Copyright (C) 2007 the VideoLAN team + * $Id$ + * + * Authors: Pierre d'Herbemont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#include "libvlc_internal.h" +#include +#include +#include "vlc_arrays.h" + +typedef enum EventPlaceInTime { + EventWillHappen, + EventDidHappen +} EventPlaceInTime; + +//#define DEBUG_MEDIA_LIST + +#ifdef DEBUG_MEDIA_LIST +# define trace( fmt, ... ) printf( "%s(): " fmt, __FUNCTION__, ##__VA_ARGS__ ) +#else +# define trace( ... ) +#endif + +/* + * Private functions + */ + + + +/************************************************************************** + * notify_item_addition (private) + * + * Do the appropriate action when an item is deleted. + **************************************************************************/ +static void +notify_item_addition( libvlc_media_list_t * p_mlist, + libvlc_media_t * p_md, + int index, + EventPlaceInTime event_status ) +{ + libvlc_event_t event; + + /* Construct the event */ + if( event_status == EventDidHappen ) + { + trace("item was added at index %d\n", index); + event.type = libvlc_MediaListItemAdded; + event.u.media_list_item_added.item = p_md; + event.u.media_list_item_added.index = index; + } + else /* if( event_status == EventWillHappen ) */ + { + event.type = libvlc_MediaListWillAddItem; + event.u.media_list_will_add_item.item = p_md; + event.u.media_list_will_add_item.index = index; + } + + /* Send the event */ + libvlc_event_send( p_mlist->p_event_manager, &event ); +} + +/************************************************************************** + * notify_item_deletion (private) + * + * Do the appropriate action when an item is added. + **************************************************************************/ +static void +notify_item_deletion( libvlc_media_list_t * p_mlist, + libvlc_media_t * p_md, + int index, + EventPlaceInTime event_status ) +{ + libvlc_event_t event; + + /* Construct the event */ + if( event_status == EventDidHappen ) + { + trace("item at index %d was deleted\n", index); + event.type = libvlc_MediaListItemDeleted; + event.u.media_list_item_deleted.item = p_md; + event.u.media_list_item_deleted.index = index; + } + else /* if( event_status == EventWillHappen ) */ + { + event.type = libvlc_MediaListWillDeleteItem; + event.u.media_list_will_delete_item.item = p_md; + event.u.media_list_will_delete_item.index = index; + } + + /* Send the event */ + libvlc_event_send( p_mlist->p_event_manager, &event ); +} + +/* + * Public libvlc functions + */ + +/************************************************************************** + * libvlc_media_list_new (Public) + * + * Init an object. + **************************************************************************/ +libvlc_media_list_t * +libvlc_media_list_new( libvlc_instance_t * p_inst, + libvlc_exception_t * p_e ) +{ + libvlc_media_list_t * p_mlist; + + p_mlist = malloc(sizeof(libvlc_media_list_t)); + if( !p_mlist ) + return NULL; + + p_mlist->p_libvlc_instance = p_inst; + p_mlist->p_event_manager = libvlc_event_manager_new( p_mlist, p_inst, p_e ); + + /* Code for that one should be handled in flat_media_list.c */ + p_mlist->p_flat_mlist = NULL; + p_mlist->b_read_only = false; + + libvlc_event_manager_register_event_type( p_mlist->p_event_manager, + libvlc_MediaListItemAdded, p_e ); + libvlc_event_manager_register_event_type( p_mlist->p_event_manager, + libvlc_MediaListWillAddItem, p_e ); + libvlc_event_manager_register_event_type( p_mlist->p_event_manager, + libvlc_MediaListItemDeleted, p_e ); + libvlc_event_manager_register_event_type( p_mlist->p_event_manager, + libvlc_MediaListWillDeleteItem, p_e ); + + if( libvlc_exception_raised( p_e ) ) + { + libvlc_event_manager_release( p_mlist->p_event_manager ); + free( p_mlist ); + return NULL; + } + + vlc_mutex_init( &p_mlist->object_lock ); + + vlc_array_init( &p_mlist->items ); + p_mlist->i_refcount = 1; + p_mlist->p_md = NULL; + + return p_mlist; +} + +/************************************************************************** + * libvlc_media_list_release (Public) + * + * Release an object. + **************************************************************************/ +void libvlc_media_list_release( libvlc_media_list_t * p_mlist ) +{ + libvlc_media_t * p_md; + int i; + + vlc_mutex_lock( &p_mlist->object_lock ); + p_mlist->i_refcount--; + if( p_mlist->i_refcount > 0 ) + { + vlc_mutex_unlock( &p_mlist->object_lock ); + return; + } + vlc_mutex_unlock( &p_mlist->object_lock ); + + /* Refcount null, time to free */ + + libvlc_event_manager_release( p_mlist->p_event_manager ); + + if( p_mlist->p_md ) + libvlc_media_release( p_mlist->p_md ); + + for ( i = 0; i < vlc_array_count( &p_mlist->items ); i++ ) + { + p_md = vlc_array_item_at_index( &p_mlist->items, i ); + libvlc_media_release( p_md ); + } + + vlc_mutex_destroy( &p_mlist->object_lock ); + vlc_array_clear( &p_mlist->items ); + + free( p_mlist ); +} + +/************************************************************************** + * libvlc_media_list_retain (Public) + * + * Increase an object refcount. + **************************************************************************/ +void libvlc_media_list_retain( libvlc_media_list_t * p_mlist ) +{ + vlc_mutex_lock( &p_mlist->object_lock ); + p_mlist->i_refcount++; + vlc_mutex_unlock( &p_mlist->object_lock ); +} + + +/************************************************************************** + * add_file_content (Public) + **************************************************************************/ +void +libvlc_media_list_add_file_content( libvlc_media_list_t * p_mlist, + const char * psz_uri, + libvlc_exception_t * p_e ) +{ + input_item_t * p_input_item; + libvlc_media_t * p_md; + + p_input_item = input_item_NewExt( p_mlist->p_libvlc_instance->p_libvlc_int, psz_uri, + _("Media Library"), 0, NULL, -1 ); + + if( !p_input_item ) + { + libvlc_exception_raise( p_e, "Can't create an input item" ); + return; + } + + p_md = libvlc_media_new_from_input_item( + p_mlist->p_libvlc_instance, + p_input_item, p_e ); + + if( !p_md ) + { + vlc_gc_decref( p_input_item ); + return; + } + + libvlc_media_list_add_media( p_mlist, p_md, p_e ); + if( libvlc_exception_raised( p_e ) ) + return; + + input_Read( p_mlist->p_libvlc_instance->p_libvlc_int, p_input_item, true ); + + return; +} + +/************************************************************************** + * set_media (Public) + **************************************************************************/ +void libvlc_media_list_set_media( libvlc_media_list_t * p_mlist, + libvlc_media_t * p_md, + libvlc_exception_t * p_e) + +{ + (void)p_e; + vlc_mutex_lock( &p_mlist->object_lock ); + if( p_mlist->p_md ) + libvlc_media_release( p_mlist->p_md ); + libvlc_media_retain( p_md ); + p_mlist->p_md = p_md; + vlc_mutex_unlock( &p_mlist->object_lock ); +} + +/************************************************************************** + * media (Public) + * + * If this media_list comes is a media's subitems, + * This holds the corresponding media. + * This md is also seen as the information holder for the media_list. + * Indeed a media_list can have meta information through this + * media. + **************************************************************************/ +libvlc_media_t * +libvlc_media_list_media( libvlc_media_list_t * p_mlist, + libvlc_exception_t * p_e) +{ + libvlc_media_t *p_md; + (void)p_e; + + vlc_mutex_lock( &p_mlist->object_lock ); + p_md = p_mlist->p_md; + if( p_md ) + libvlc_media_retain( p_md ); + vlc_mutex_unlock( &p_mlist->object_lock ); + + return p_md; +} + +/************************************************************************** + * libvlc_media_list_count (Public) + * + * Lock should be hold when entering. + **************************************************************************/ +int libvlc_media_list_count( libvlc_media_list_t * p_mlist, + libvlc_exception_t * p_e ) +{ + (void)p_e; + return vlc_array_count( &p_mlist->items ); +} + +/************************************************************************** + * libvlc_media_list_add_media (Public) + * + * Lock should be hold when entering. + **************************************************************************/ +void libvlc_media_list_add_media( + libvlc_media_list_t * p_mlist, + libvlc_media_t * p_md, + libvlc_exception_t * p_e ) +{ + if( p_mlist->b_read_only ) + { + /* We are read only from user side */ + libvlc_exception_raise( p_e, "Trying to write into a read-only media list." ); + return; + } + + _libvlc_media_list_add_media( p_mlist, p_md, p_e ); +} + +/* LibVLC internal version */ +void _libvlc_media_list_add_media( + libvlc_media_list_t * p_mlist, + libvlc_media_t * p_md, + libvlc_exception_t * p_e ) +{ + (void)p_e; + libvlc_media_retain( p_md ); + + notify_item_addition( p_mlist, p_md, vlc_array_count( &p_mlist->items ), EventWillHappen ); + vlc_array_append( &p_mlist->items, p_md ); + notify_item_addition( p_mlist, p_md, vlc_array_count( &p_mlist->items )-1, EventDidHappen ); +} + +/************************************************************************** + * libvlc_media_list_insert_media (Public) + * + * Lock should be hold when entering. + **************************************************************************/ +void libvlc_media_list_insert_media( + libvlc_media_list_t * p_mlist, + libvlc_media_t * p_md, + int index, + libvlc_exception_t * p_e ) +{ + if( p_mlist->b_read_only ) + { + /* We are read only from user side */ + libvlc_exception_raise( p_e, "Trying to write into a read-only media list." ); + return; + } + _libvlc_media_list_insert_media( p_mlist, p_md, index, p_e ); +} + +/* LibVLC internal version */ +void _libvlc_media_list_insert_media( + libvlc_media_list_t * p_mlist, + libvlc_media_t * p_md, + int index, + libvlc_exception_t * p_e ) +{ + (void)p_e; + libvlc_media_retain( p_md ); + + notify_item_addition( p_mlist, p_md, index, EventWillHappen ); + vlc_array_insert( &p_mlist->items, p_md, index ); + notify_item_addition( p_mlist, p_md, index, EventDidHappen ); +} + +/************************************************************************** + * libvlc_media_list_remove_index (Public) + * + * Lock should be hold when entering. + **************************************************************************/ +void libvlc_media_list_remove_index( libvlc_media_list_t * p_mlist, + int index, + libvlc_exception_t * p_e ) +{ + if( p_mlist->b_read_only ) + { + /* We are read only from user side */ + libvlc_exception_raise( p_e, "Trying to write into a read-only media list." ); + return; + } + _libvlc_media_list_remove_index( p_mlist, index, p_e ); +} + +/* LibVLC internal version */ +void _libvlc_media_list_remove_index( libvlc_media_list_t * p_mlist, + int index, + libvlc_exception_t * p_e ) +{ + + libvlc_media_t * p_md; + + if( index < 0 || index >= vlc_array_count( &p_mlist->items )) + { + libvlc_exception_raise( p_e, "Index out of bounds exception"); + return; + } + + p_md = vlc_array_item_at_index( &p_mlist->items, index ); + + notify_item_deletion( p_mlist, p_md, index, EventWillHappen ); + vlc_array_remove( &p_mlist->items, index ); + notify_item_deletion( p_mlist, p_md, index, EventDidHappen ); + + libvlc_media_release( p_md ); +} + +/************************************************************************** + * libvlc_media_list_item_at_index (Public) + * + * Lock should be hold when entering. + **************************************************************************/ +libvlc_media_t * +libvlc_media_list_item_at_index( libvlc_media_list_t * p_mlist, + int index, + libvlc_exception_t * p_e ) +{ + VLC_UNUSED(p_e); + + if( index < 0 || index >= vlc_array_count( &p_mlist->items )) + { + libvlc_exception_raise( p_e, "Index out of bounds exception"); + return NULL; + } + + libvlc_media_t * p_md; + p_md = vlc_array_item_at_index( &p_mlist->items, index ); + libvlc_media_retain( p_md ); + return p_md; +} + +/************************************************************************** + * libvlc_media_list_index_of_item (Public) + * + * Lock should be hold when entering. + * Warning: this function would return the first matching item + **************************************************************************/ +int libvlc_media_list_index_of_item( libvlc_media_list_t * p_mlist, + libvlc_media_t * p_searched_md, + libvlc_exception_t * p_e ) +{ + VLC_UNUSED(p_e); + + libvlc_media_t * p_md; + int i; + for ( i = 0; i < vlc_array_count( &p_mlist->items ); i++ ) + { + p_md = vlc_array_item_at_index( &p_mlist->items, i ); + if( p_searched_md == p_md ) + return i; + } + return -1; +} + +/************************************************************************** + * libvlc_media_list_is_readonly (Public) + * + * This indicates if this media list is read-only from a user point of view + **************************************************************************/ +int libvlc_media_list_is_readonly( libvlc_media_list_t * p_mlist ) +{ + return p_mlist->b_read_only; +} + +/************************************************************************** + * libvlc_media_list_lock (Public) + * + * The lock must be held in access operations. It is never used in the + * Public method. + **************************************************************************/ +void libvlc_media_list_lock( libvlc_media_list_t * p_mlist ) +{ + vlc_mutex_lock( &p_mlist->object_lock ); +} + + +/************************************************************************** + * libvlc_media_list_unlock (Public) + * + * The lock must be held in access operations + **************************************************************************/ +void libvlc_media_list_unlock( libvlc_media_list_t * p_mlist ) +{ + vlc_mutex_unlock( &p_mlist->object_lock ); +} + + +/************************************************************************** + * libvlc_media_list_p_event_manager (Public) + * + * The p_event_manager is immutable, so you don't have to hold the lock + **************************************************************************/ +libvlc_event_manager_t * +libvlc_media_list_event_manager( libvlc_media_list_t * p_mlist, + libvlc_exception_t * p_e ) +{ + (void)p_e; + return p_mlist->p_event_manager; +} diff --git a/VLC/media_list_path.h b/VLC/media_list_path.h new file mode 100644 index 0000000..c3e698d --- /dev/null +++ b/VLC/media_list_path.h @@ -0,0 +1,223 @@ +/***************************************************************************** + * media_list_path.h : Some inlined function that allows media_list_path + * manipulation. This is internal and used only by media_list_player. + ***************************************************************************** + * Copyright (C) 2005 the VideoLAN team + * $Id $ + * + * Authors: Pierre d'Herbemont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef _LIBVLC_MEDIA_LIST_PATH_H +#define _LIBVLC_MEDIA_LIST_PATH_H 1 + +/************************************************************************** + * path_empty (Media List Player Internal) + **************************************************************************/ +static inline libvlc_media_list_path_t libvlc_media_list_path_empty( void ) +{ + libvlc_media_list_path_t ret = malloc(sizeof(int)); + ret[0] = -1; + return ret; +} + +/************************************************************************** + * path_with_root_index (Media List Player Internal) + **************************************************************************/ +static inline libvlc_media_list_path_t libvlc_media_list_path_with_root_index( int index ) +{ + libvlc_media_list_path_t ret = malloc(sizeof(int)*2); + ret[0] = index; + ret[1] = -1; + return ret; +} + +/************************************************************************** + * path_deepness (Media List Player Internal) + **************************************************************************/ +static inline int libvlc_media_list_path_deepness( libvlc_media_list_path_t path ) +{ + int i; + for( i = 0; path[i] != -1; i++ ); + return i; +} + +/************************************************************************** + * path_append (Media List Player Internal) + **************************************************************************/ +static inline void libvlc_media_list_path_append( libvlc_media_list_path_t * p_path, int index ) +{ + int old_deepness = libvlc_media_list_path_deepness( *p_path ); + *p_path = realloc( *p_path, sizeof(int)*(old_deepness+2)); + *p_path[old_deepness] = index; + *p_path[old_deepness+1] = -1; +} + +/************************************************************************** + * path_copy_by_appending (Media List Player Internal) + **************************************************************************/ +static inline libvlc_media_list_path_t libvlc_media_list_path_copy_by_appending( libvlc_media_list_path_t path, int index ) +{ + libvlc_media_list_path_t ret; + int old_deepness = libvlc_media_list_path_deepness( path ); + ret = malloc( sizeof(int)*(old_deepness+2) ); + memcpy( ret, path, sizeof(int)*(old_deepness+2) ); + ret[old_deepness] = index; + ret[old_deepness+1] = -1; + return ret; +} + +/************************************************************************** + * path_copy (Media List Player Internal) + **************************************************************************/ +static inline libvlc_media_list_path_t libvlc_media_list_path_copy( libvlc_media_list_path_t path ) +{ + libvlc_media_list_path_t ret; + int deepness = libvlc_media_list_path_deepness( path ); + ret = malloc( sizeof(int)*(deepness+1) ); + memcpy( ret, path, sizeof(int)*(deepness+1) ); + return ret; +} + +/************************************************************************** + * get_path_rec (Media List Player Internal) + **************************************************************************/ +static libvlc_media_list_path_t +get_path_rec( libvlc_media_list_path_t path, libvlc_media_list_t * p_current_mlist, libvlc_media_t * p_searched_md ) +{ + int i, count; + count = libvlc_media_list_count( p_current_mlist, NULL ); + for( i = 0; i < count; i++ ) + { + libvlc_media_t * p_md = libvlc_media_list_item_at_index( p_current_mlist, i, NULL ); + + if( p_md == p_searched_md ) + return libvlc_media_list_path_copy_by_appending( path, i ); /* Found! */ + + libvlc_media_list_t * p_subitems = libvlc_media_subitems( p_md, NULL ); + libvlc_media_release( p_md ); + if( p_subitems ) + { + libvlc_media_list_path_t new_path = libvlc_media_list_path_copy_by_appending( path, i ); + libvlc_media_list_lock( p_subitems ); + libvlc_media_list_path_t ret = get_path_rec( new_path, p_subitems, p_searched_md ); + libvlc_media_list_unlock( p_subitems ); + free( new_path ); + libvlc_media_list_release( p_subitems ); + if( ret ) + return ret; /* Found in sublist! */ + } + } + return NULL; +} + +/************************************************************************** + * path_of_item (Media List Player Internal) + **************************************************************************/ +static inline libvlc_media_list_path_t libvlc_media_list_path_of_item( libvlc_media_list_t * p_mlist, libvlc_media_t * p_md ) +{ + libvlc_media_list_path_t path = libvlc_media_list_path_empty(); + libvlc_media_list_path_t ret; + ret = get_path_rec( path, p_mlist, p_md ); + free( path ); + return ret; +} + +/************************************************************************** + * item_at_path (Media List Player Internal) + **************************************************************************/ +static libvlc_media_t * +libvlc_media_list_item_at_path( libvlc_media_list_t * p_mlist, libvlc_media_list_path_t path ) +{ + libvlc_media_list_t * p_current_mlist = p_mlist; + libvlc_media_t * p_md = NULL; + int i; + for( i = 0; path[i] != -1; i++ ) + { + p_md = libvlc_media_list_item_at_index( p_current_mlist, path[i], NULL ); + + if( p_current_mlist != p_mlist ) + libvlc_media_list_release( p_current_mlist ); + + if( path[i+1] == -1 ) + return p_md; + + p_current_mlist = libvlc_media_subitems( p_md, NULL ); + libvlc_media_release( p_md ); + + if( !p_current_mlist ) + return NULL; + + /* Fetch next one */ + } + /* Not found, shouldn't happen if the p_path is not empty */ + if( p_current_mlist != p_mlist ) + libvlc_media_list_release( p_current_mlist ); + return NULL; +} + +/************************************************************************** + * parentlist_at_path (Media List Player Internal) + **************************************************************************/ +static libvlc_media_list_t * +libvlc_media_list_parentlist_at_path( libvlc_media_list_t * p_mlist, libvlc_media_list_path_t path ) +{ + libvlc_media_list_t * p_current_mlist = p_mlist; + libvlc_media_t * p_md = NULL; + int i; + for( i = 0; path[i] != -1; i++ ) + { + if( p_current_mlist != p_mlist ) + libvlc_media_list_release( p_current_mlist ); + + if( path[i+1] == -1 ) + return p_current_mlist; + + p_md = libvlc_media_list_item_at_index( p_current_mlist, path[i], NULL ); + + p_current_mlist = libvlc_media_subitems( p_md, NULL ); + libvlc_media_release( p_md ); + + if( !p_current_mlist ) + return NULL; + + /* Fetch next one */ + } + /* Not found, shouldn't happen if the p_path is not empty */ + if( p_current_mlist != p_mlist ) + libvlc_media_list_release( p_current_mlist ); + return NULL; +} + +/************************************************************************** + * sublist_at_path (Media List Player Internal) + **************************************************************************/ +static libvlc_media_list_t * +libvlc_media_list_sublist_at_path( libvlc_media_list_t * p_mlist, libvlc_media_list_path_t path ) +{ + libvlc_media_list_t * ret; + libvlc_media_t * p_md = libvlc_media_list_item_at_path( p_mlist, path ); + if( !p_md ) + return NULL; + + ret = libvlc_media_subitems( p_md, NULL ); + libvlc_media_release( p_md ); + + return ret; +} + +#endif diff --git a/VLC/media_list_player.c b/VLC/media_list_player.c new file mode 100644 index 0000000..c5f0451 --- /dev/null +++ b/VLC/media_list_player.c @@ -0,0 +1,494 @@ +/***************************************************************************** + * media_list_player.c: libvlc new API media_list player functions + ***************************************************************************** + * Copyright (C) 2007 the VideoLAN team + * $Id$ + * + * Authors: Pierre d'Herbemont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ +#include "libvlc_internal.h" +#include +#include "media_list_path.h" + +/* + * Private functions + */ + +/************************************************************************** + * get_next_index (private) + * + * Simple next item fetcher. + **************************************************************************/ +static libvlc_media_list_path_t +get_next_path( libvlc_media_list_player_t * p_mlp ) +{ + /* We are entered with libvlc_media_list_lock( p_mlp->p_list ) */ + libvlc_media_list_path_t ret; + libvlc_media_list_t * p_parent_of_playing_item; + libvlc_media_list_t * p_sublist_of_playing_item; + + if ( !p_mlp->current_playing_item_path ) + { + if( !libvlc_media_list_count( p_mlp->p_mlist, NULL ) ) + return NULL; + return libvlc_media_list_path_with_root_index(0); + } + + p_sublist_of_playing_item = libvlc_media_list_sublist_at_path( + p_mlp->p_mlist, + p_mlp->current_playing_item_path ); + + /* If item just gained a sublist just play it */ + if( p_sublist_of_playing_item ) + { + libvlc_media_list_release( p_sublist_of_playing_item ); + return libvlc_media_list_path_copy_by_appending( p_mlp->current_playing_item_path, 0 ); + } + + /* Try to catch next element */ + p_parent_of_playing_item = libvlc_media_list_parentlist_at_path( + p_mlp->p_mlist, + p_mlp->current_playing_item_path ); + + int deepness = libvlc_media_list_path_deepness( p_mlp->current_playing_item_path ); + if( deepness < 1 || !p_parent_of_playing_item ) + return NULL; + + ret = libvlc_media_list_path_copy( p_mlp->current_playing_item_path ); + + while( ret[deepness-1] >= libvlc_media_list_count( p_parent_of_playing_item, NULL ) ) + { + deepness--; + if( deepness <= 0 ) + { + free( ret ); + libvlc_media_list_release( p_parent_of_playing_item ); + return NULL; + } + ret[deepness] = -1; + ret[deepness-1]++; + p_parent_of_playing_item = libvlc_media_list_parentlist_at_path( + p_mlp->p_mlist, + ret ); + } + libvlc_media_list_release( p_parent_of_playing_item ); + return ret; +} + +/************************************************************************** + * media_player_reached_end (private) (Event Callback) + **************************************************************************/ +static void +media_player_reached_end( const libvlc_event_t * p_event, + void * p_user_data ) +{ + libvlc_media_list_player_t * p_mlp = p_user_data; + libvlc_media_player_t * p_mi = p_event->p_obj; + libvlc_media_t *p_md, * p_current_md; + + p_md = libvlc_media_player_get_media( p_mi, NULL ); + /* XXX: need if p_mlp->p_current_playing_index is beyond */ + p_current_md = libvlc_media_list_item_at_path( + p_mlp->p_mlist, + p_mlp->current_playing_item_path ); + if( p_md != p_current_md ) + { + msg_Warn( p_mlp->p_libvlc_instance->p_libvlc_int, + "We are not sync-ed with the media instance" ); + libvlc_media_release( p_md ); + libvlc_media_release( p_current_md ); + return; + } + libvlc_media_release( p_md ); + libvlc_media_release( p_current_md ); + libvlc_media_list_player_next( p_mlp, NULL ); +} + +/************************************************************************** + * playlist_item_deleted (private) (Event Callback) + **************************************************************************/ +static void +mlist_item_deleted( const libvlc_event_t * p_event, void * p_user_data ) +{ + libvlc_media_t * p_current_md; + libvlc_media_list_player_t * p_mlp = p_user_data; + libvlc_media_list_t * p_emitting_mlist = p_event->p_obj; + /* XXX: need if p_mlp->p_current_playing_index is beyond */ + p_current_md = libvlc_media_list_item_at_path( + p_mlp->p_mlist, + p_mlp->current_playing_item_path ); + + if( p_event->u.media_list_item_deleted.item == p_current_md && + p_emitting_mlist == p_mlp->p_mlist ) + { + /* We are playing this item, we choose to stop */ + libvlc_media_list_player_stop( p_mlp, NULL ); + } +} + +/************************************************************************** + * install_playlist_observer (private) + **************************************************************************/ +static void +install_playlist_observer( libvlc_media_list_player_t * p_mlp ) +{ + libvlc_event_attach( libvlc_media_list_event_manager( p_mlp->p_mlist, NULL ), + libvlc_MediaListItemDeleted, mlist_item_deleted, p_mlp, NULL ); +} + +/************************************************************************** + * uninstall_playlist_observer (private) + **************************************************************************/ +static void +uninstall_playlist_observer( libvlc_media_list_player_t * p_mlp ) +{ + if ( !p_mlp->p_mlist ) + { + return; + } + + libvlc_event_detach( libvlc_media_list_event_manager( p_mlp->p_mlist, NULL ), + libvlc_MediaListItemDeleted, mlist_item_deleted, p_mlp, NULL ); +} + +/************************************************************************** + * install_media_player_observer (private) + **************************************************************************/ +static void +install_media_player_observer( libvlc_media_list_player_t * p_mlp ) +{ + libvlc_event_attach( libvlc_media_player_event_manager( p_mlp->p_mi, NULL ), + libvlc_MediaPlayerEndReached, + media_player_reached_end, p_mlp, NULL ); +} + + +/************************************************************************** + * uninstall_media_player_observer (private) + **************************************************************************/ +static void +uninstall_media_player_observer( libvlc_media_list_player_t * p_mlp ) +{ + if ( !p_mlp->p_mi ) + { + return; + } + + libvlc_event_detach( libvlc_media_player_event_manager( p_mlp->p_mi, NULL ), + libvlc_MediaPlayerEndReached, + media_player_reached_end, p_mlp, NULL ); +} + +/************************************************************************** + * set_current_playing_item (private) + * + * Playlist lock should be held + **************************************************************************/ +static void +set_current_playing_item( libvlc_media_list_player_t * p_mlp, + libvlc_media_list_path_t path, + libvlc_exception_t * p_e ) +{ + VLC_UNUSED(p_e); + + libvlc_media_t * p_md; + + p_md = libvlc_media_list_item_at_path( p_mlp->p_mlist, path ); + vlc_mutex_lock( &p_mlp->object_lock ); + + if( p_mlp->current_playing_item_path != path ) + { + free( p_mlp->current_playing_item_path ); + p_mlp->current_playing_item_path = path; + } + + if( !p_md ) + { + vlc_mutex_unlock( &p_mlp->object_lock ); + return; + } + + /* We are not interested in getting media stop event now */ + uninstall_media_player_observer( p_mlp ); + + if ( !p_mlp->p_mi ) + { + p_mlp->p_mi = libvlc_media_player_new_from_media(p_md, p_e); + } + + if( p_md->p_subitems && libvlc_media_list_count( p_md->p_subitems, NULL ) > 0 ) + { + libvlc_media_t * p_submd; + p_submd = libvlc_media_list_item_at_index( p_md->p_subitems, 0, NULL ); + libvlc_media_player_set_media( p_mlp->p_mi, p_submd, NULL ); + libvlc_media_release( p_submd ); + } + else + libvlc_media_player_set_media( p_mlp->p_mi, p_md, NULL ); +// wait_playing_state(); /* If we want to be synchronous */ + install_media_player_observer( p_mlp ); + + vlc_mutex_unlock( &p_mlp->object_lock ); + + libvlc_media_release( p_md ); /* for libvlc_media_list_item_at_index */ +} + +/* + * Public libvlc functions + */ + +/************************************************************************** + * new (Public) + **************************************************************************/ +libvlc_media_list_player_t * +libvlc_media_list_player_new( libvlc_instance_t * p_instance, + libvlc_exception_t * p_e ) +{ + (void)p_e; + libvlc_media_list_player_t * p_mlp; + p_mlp = malloc(sizeof(libvlc_media_list_player_t)); + if( !p_mlp ) + return NULL; + + p_mlp->current_playing_item_path = NULL; + p_mlp->p_mi = NULL; + p_mlp->p_mlist = NULL; + vlc_mutex_init( &p_mlp->object_lock ); + p_mlp->p_event_manager = libvlc_event_manager_new( p_mlp, + p_instance, + p_e ); + libvlc_event_manager_register_event_type( p_mlp->p_event_manager, + libvlc_MediaListPlayerNextItemSet, p_e ); + + return p_mlp; +} + +/************************************************************************** + * release (Public) + **************************************************************************/ +void libvlc_media_list_player_release( libvlc_media_list_player_t * p_mlp ) +{ + free(p_mlp); +} + +/************************************************************************** + * set_media_player (Public) + **************************************************************************/ +void libvlc_media_list_player_set_media_player( + libvlc_media_list_player_t * p_mlp, + libvlc_media_player_t * p_mi, + libvlc_exception_t * p_e ) +{ + VLC_UNUSED(p_e); + + vlc_mutex_lock( &p_mlp->object_lock ); + + if( p_mlp->p_mi ) + { + uninstall_media_player_observer( p_mlp ); + libvlc_media_player_release( p_mlp->p_mi ); + } + libvlc_media_player_retain( p_mi ); + p_mlp->p_mi = p_mi; + + install_media_player_observer( p_mlp ); + + vlc_mutex_unlock( &p_mlp->object_lock ); +} + +/************************************************************************** + * set_media_list (Public) + **************************************************************************/ +void libvlc_media_list_player_set_media_list( + libvlc_media_list_player_t * p_mlp, + libvlc_media_list_t * p_mlist, + libvlc_exception_t * p_e ) +{ + vlc_mutex_lock( &p_mlp->object_lock ); + + if( libvlc_media_list_player_is_playing( p_mlp, p_e ) ) + { + libvlc_media_player_stop( p_mlp->p_mi, p_e ); + /* Don't bother if there was an error. */ + libvlc_exception_clear( p_e ); + } + + if( p_mlp->p_mlist ) + { + uninstall_playlist_observer( p_mlp ); + libvlc_media_list_release( p_mlp->p_mlist ); + } + libvlc_media_list_retain( p_mlist ); + p_mlp->p_mlist = p_mlist; + + install_playlist_observer( p_mlp ); + + vlc_mutex_unlock( &p_mlp->object_lock ); +} + +/************************************************************************** + * Play (Public) + **************************************************************************/ +void libvlc_media_list_player_play( libvlc_media_list_player_t * p_mlp, + libvlc_exception_t * p_e ) +{ + if( !p_mlp->current_playing_item_path ) + { + libvlc_media_list_player_next( p_mlp, p_e ); + return; /* Will set to play */ + } + + libvlc_media_player_play( p_mlp->p_mi, p_e ); +} + + +/************************************************************************** + * Pause (Public) + **************************************************************************/ +void libvlc_media_list_player_pause( libvlc_media_list_player_t * p_mlp, + libvlc_exception_t * p_e ) +{ + if( !p_mlp->p_mi ) + return; + libvlc_media_player_pause( p_mlp->p_mi, p_e ); +} + +/************************************************************************** + * is_playing (Public) + **************************************************************************/ +int +libvlc_media_list_player_is_playing( libvlc_media_list_player_t * p_mlp, + libvlc_exception_t * p_e ) +{ + libvlc_state_t state = libvlc_media_player_get_state( p_mlp->p_mi, p_e ); + return (state == libvlc_Opening) || (state == libvlc_Buffering) || + (state == libvlc_Forward) || (state == libvlc_Backward) || + (state == libvlc_Playing); +} + +/************************************************************************** + * State (Public) + **************************************************************************/ +libvlc_state_t +libvlc_media_list_player_get_state( libvlc_media_list_player_t * p_mlp, + libvlc_exception_t * p_e ) +{ + if( !p_mlp->p_mi ) + return libvlc_Ended; + return libvlc_media_player_get_state( p_mlp->p_mi, p_e ); +} + +/************************************************************************** + * Play item at index (Public) + **************************************************************************/ +void libvlc_media_list_player_play_item_at_index( + libvlc_media_list_player_t * p_mlp, + int i_index, + libvlc_exception_t * p_e ) +{ + set_current_playing_item( p_mlp, libvlc_media_list_path_with_root_index(i_index), p_e ); + + if( libvlc_exception_raised( p_e ) ) + return; + + /* Send the next item event */ + libvlc_event_t event; + event.type = libvlc_MediaListPlayerNextItemSet; + libvlc_event_send( p_mlp->p_event_manager, &event ); + + libvlc_media_player_play( p_mlp->p_mi, p_e ); +} + +/************************************************************************** + * Play item (Public) + **************************************************************************/ +void libvlc_media_list_player_play_item( + libvlc_media_list_player_t * p_mlp, + libvlc_media_t * p_md, + libvlc_exception_t * p_e ) +{ + libvlc_media_list_path_t path = libvlc_media_list_path_of_item( p_mlp->p_mlist, p_md ); + if( !path ) + { + libvlc_exception_raise( p_e, "No such item in media list" ); + return; + } + set_current_playing_item( p_mlp, path, p_e ); + + if( libvlc_exception_raised( p_e ) ) + return; + + libvlc_media_player_play( p_mlp->p_mi, p_e ); +} + +/************************************************************************** + * Stop (Public) + **************************************************************************/ +void libvlc_media_list_player_stop( libvlc_media_list_player_t * p_mlp, + libvlc_exception_t * p_e ) +{ + if ( p_mlp->p_mi ) + { + /* We are not interested in getting media stop event now */ + uninstall_media_player_observer( p_mlp ); + libvlc_media_player_stop( p_mlp->p_mi, p_e ); + install_media_player_observer( p_mlp ); + } + + vlc_mutex_lock( &p_mlp->object_lock ); + free( p_mlp->current_playing_item_path ); + p_mlp->current_playing_item_path = NULL; + vlc_mutex_unlock( &p_mlp->object_lock ); +} + +/************************************************************************** + * Next (Public) + **************************************************************************/ +void libvlc_media_list_player_next( libvlc_media_list_player_t * p_mlp, + libvlc_exception_t * p_e ) +{ + libvlc_media_list_path_t path; + + if (! p_mlp->p_mlist ) + { + libvlc_exception_raise( p_e, "No more element to play" ); + return; + } + + libvlc_media_list_lock( p_mlp->p_mlist ); + + path = get_next_path( p_mlp ); + + if( !path ) + { + libvlc_media_list_unlock( p_mlp->p_mlist ); + libvlc_exception_raise( p_e, "No more element to play" ); + libvlc_media_list_player_stop( p_mlp, p_e ); + return; + } + + set_current_playing_item( p_mlp, path, p_e ); + + libvlc_media_player_play( p_mlp->p_mi, p_e ); + + libvlc_media_list_unlock( p_mlp->p_mlist ); + + /* Send the next item event */ + libvlc_event_t event; + event.type = libvlc_MediaListPlayerNextItemSet; + libvlc_event_send( p_mlp->p_event_manager, &event); +} diff --git a/VLC/media_list_view.c b/VLC/media_list_view.c new file mode 100644 index 0000000..5ed71d0 --- /dev/null +++ b/VLC/media_list_view.c @@ -0,0 +1,489 @@ +/***************************************************************************** + * flat_media_list.c: libvlc flat media list functions. (extension to + * media_list.c). + ***************************************************************************** + * Copyright (C) 2007 the VideoLAN team + * $Id$ + * + * Authors: Pierre d'Herbemont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#include "libvlc_internal.h" +#include +#include +#include "vlc_arrays.h" + +//#define DEBUG_FLAT_LIST + +#ifdef DEBUG_FLAT_LIST +# define trace( fmt, ... ) printf( "%s(): " fmt, __FUNCTION__, ##__VA_ARGS__ ) +#else +# define trace( ... ) +#endif + +/* + * Private functions + */ +static void +media_list_item_added( const libvlc_event_t * p_event, void * p_user_data ); +static void +media_list_item_removed( const libvlc_event_t * p_event, void * p_user_data ); +static void +media_list_subitem_added( const libvlc_event_t * p_event, void * p_user_data ); + +static void +install_md_listener( libvlc_media_list_view_t * p_mlv, + libvlc_media_t * p_md) +{ + libvlc_media_list_t * p_mlist; + if((p_mlist = libvlc_media_subitems( p_md, NULL ))) + { + libvlc_media_list_lock( p_mlist ); + int i, count = libvlc_media_list_count( p_mlist, NULL ); + for( i = 0; i < count; i++) + { + libvlc_event_t added_event; + libvlc_media_t * p_submd; + p_submd = libvlc_media_list_item_at_index( p_mlist, i, NULL ); + + /* Install our listeners */ + install_md_listener( p_mlv, p_submd ); + + /* For each item, send a notification to the mlv subclasses */ + added_event.u.media_list_item_added.item = p_submd; + added_event.u.media_list_item_added.index = 0; + if( p_mlv->pf_ml_item_added ) p_mlv->pf_ml_item_added( &added_event, p_mlv ); + libvlc_media_release( p_submd ); + } + libvlc_event_attach( p_mlist->p_event_manager, + libvlc_MediaListItemAdded, + media_list_item_added, p_mlv, NULL ); + libvlc_event_attach( p_mlist->p_event_manager, + libvlc_MediaListItemDeleted, + media_list_item_removed, p_mlv, NULL ); + libvlc_media_list_unlock( p_mlist ); + libvlc_media_list_release( p_mlist ); + } + else + { + /* No mlist, wait for a subitem added event */ + libvlc_event_attach( p_md->p_event_manager, + libvlc_MediaSubItemAdded, + media_list_subitem_added, p_mlv, NULL ); + } +} + +static void +uninstall_md_listener( libvlc_media_list_view_t * p_mlv, + libvlc_media_t * p_md) +{ + libvlc_media_list_t * p_mlist; + libvlc_exception_t ignored_exception; + libvlc_exception_init( &ignored_exception ); + libvlc_event_detach( p_md->p_event_manager, + libvlc_MediaSubItemAdded, + media_list_subitem_added, p_mlv, &ignored_exception ); + if( libvlc_exception_raised( &ignored_exception ) ) + libvlc_exception_clear( &ignored_exception ); /* We don't care if we encounter an exception */ + if((p_mlist = libvlc_media_subitems( p_md, NULL ))) + { + libvlc_media_list_lock( p_mlist ); + libvlc_event_detach( p_mlist->p_event_manager, + libvlc_MediaListItemAdded, + media_list_item_added, p_mlv, NULL ); + libvlc_event_detach( p_mlist->p_event_manager, + libvlc_MediaListItemDeleted, + media_list_item_removed, p_mlv, NULL ); + + int i, count = libvlc_media_list_count( p_mlist, NULL ); + for( i = 0; i < count; i++) + { + libvlc_media_t * p_submd; + p_submd = libvlc_media_list_item_at_index( p_mlist,i, NULL ); + uninstall_md_listener( p_mlv, p_submd ); + libvlc_media_release( p_submd ); + } + libvlc_media_list_unlock( p_mlist ); + libvlc_media_list_release( p_mlist ); + } +} + +static void +media_list_item_added( const libvlc_event_t * p_event, void * p_user_data ) +{ + libvlc_media_list_view_t * p_mlv = p_user_data; + libvlc_media_t * p_md = p_event->u.media_list_item_added.item; + install_md_listener( p_mlv, p_md ); + if( p_mlv->pf_ml_item_added ) p_mlv->pf_ml_item_added( p_event, p_mlv ); +} + +static void +media_list_item_removed( const libvlc_event_t * p_event, void * p_user_data ) +{ + libvlc_media_list_view_t * p_mlv = p_user_data; + libvlc_media_t * p_md = p_event->u.media_list_item_added.item; + uninstall_md_listener( p_mlv, p_md ); + if( p_mlv->pf_ml_item_removed ) p_mlv->pf_ml_item_removed( p_event, p_mlv ); +} + +static void +media_list_subitem_added( const libvlc_event_t * p_event, void * p_user_data ) +{ + libvlc_media_list_t * p_mlist; + libvlc_event_t added_event; + libvlc_media_list_view_t * p_mlv = p_user_data; + libvlc_media_t * p_submd = p_event->u.media_subitem_added.new_child; + libvlc_media_t * p_md = p_event->p_obj; + + if((p_mlist = libvlc_media_subitems( p_md, NULL ))) + { + /* We have a mlist to which we're going to listen to events + * thus, no need to wait for SubItemAdded events */ + libvlc_event_detach( p_md->p_event_manager, + libvlc_MediaSubItemAdded, + media_list_subitem_added, p_mlv, NULL ); + libvlc_media_list_lock( p_mlist ); + + libvlc_event_attach( p_mlist->p_event_manager, + libvlc_MediaListItemAdded, + media_list_item_added, p_mlv, NULL ); + libvlc_event_attach( p_mlist->p_event_manager, + libvlc_MediaListItemDeleted, + media_list_item_removed, p_mlv, NULL ); + libvlc_media_list_unlock( p_mlist ); + libvlc_media_list_release( p_mlist ); + } + + install_md_listener( p_mlv, p_submd ); + + added_event.u.media_list_item_added.item = p_submd; + added_event.u.media_list_item_added.index = 0; + if( p_mlv->pf_ml_item_added ) p_mlv->pf_ml_item_added( &added_event, p_mlv ); +} + +/* + * LibVLC Internal functions + */ +/************************************************************************** + * libvlc_media_list_view_set_ml_notification_callback (Internal) + * The mlist lock should be held when entered + **************************************************************************/ +void +libvlc_media_list_view_set_ml_notification_callback( + libvlc_media_list_view_t * p_mlv, + void (*item_added)(const libvlc_event_t *, libvlc_media_list_view_t *), + void (*item_removed)(const libvlc_event_t *, libvlc_media_list_view_t *) ) +{ + p_mlv->pf_ml_item_added = item_added; + p_mlv->pf_ml_item_removed = item_removed; + libvlc_event_attach( p_mlv->p_mlist->p_event_manager, + libvlc_MediaListItemAdded, + media_list_item_added, p_mlv, NULL ); + libvlc_event_attach( p_mlv->p_mlist->p_event_manager, + libvlc_MediaListItemDeleted, + media_list_item_removed, p_mlv, NULL ); + int i, count = libvlc_media_list_count( p_mlv->p_mlist, NULL ); + for( i = 0; i < count; i++) + { + libvlc_media_t * p_md; + p_md = libvlc_media_list_item_at_index( p_mlv->p_mlist, i, NULL ); + install_md_listener( p_mlv, p_md ); + libvlc_media_release( p_md ); + } +} + +/************************************************************************** + * libvlc_media_list_view_notify_deletion (Internal) + **************************************************************************/ +void +libvlc_media_list_view_will_delete_item( + libvlc_media_list_view_t * p_mlv, + libvlc_media_t * p_item, + int index ) +{ + libvlc_event_t event; + + /* Construct the event */ + event.type = libvlc_MediaListViewWillDeleteItem; + event.u.media_list_view_will_delete_item.item = p_item; + event.u.media_list_view_will_delete_item.index = index; + + /* Send the event */ + libvlc_event_send( p_mlv->p_event_manager, &event ); +} + +/************************************************************************** + * libvlc_media_list_view_item_deleted (Internal) + **************************************************************************/ +void +libvlc_media_list_view_item_deleted( + libvlc_media_list_view_t * p_mlv, + libvlc_media_t * p_item, + int index ) +{ + libvlc_event_t event; + + /* Construct the event */ + event.type = libvlc_MediaListViewItemDeleted; + event.u.media_list_view_item_deleted.item = p_item; + event.u.media_list_view_item_deleted.index = index; + + /* Send the event */ + libvlc_event_send( p_mlv->p_event_manager, &event ); +} + +/************************************************************************** + * libvlc_media_list_view_will_add_item (Internal) + **************************************************************************/ +void +libvlc_media_list_view_will_add_item( + libvlc_media_list_view_t * p_mlv, + libvlc_media_t * p_item, + int index ) +{ + libvlc_event_t event; + + /* Construct the event */ + event.type = libvlc_MediaListViewWillAddItem; + event.u.media_list_view_will_add_item.item = p_item; + event.u.media_list_view_will_add_item.index = index; + + /* Send the event */ + libvlc_event_send( p_mlv->p_event_manager, &event ); +} + +/************************************************************************** + * libvlc_media_list_view_item_added (Internal) + **************************************************************************/ +void +libvlc_media_list_view_item_added( + libvlc_media_list_view_t * p_mlv, + libvlc_media_t * p_item, + int index ) +{ + libvlc_event_t event; + + /* Construct the event */ + event.type = libvlc_MediaListViewItemAdded; + event.u.media_list_view_item_added.item = p_item; + event.u.media_list_view_item_added.index = index; + + /* Send the event */ + libvlc_event_send( p_mlv->p_event_manager, &event ); +} + +/************************************************************************** + * libvlc_media_list_view_new (Internal) + **************************************************************************/ +libvlc_media_list_view_t * +libvlc_media_list_view_new( libvlc_media_list_t * p_mlist, + libvlc_media_list_view_count_func_t pf_count, + libvlc_media_list_view_item_at_index_func_t pf_item_at_index, + libvlc_media_list_view_children_at_index_func_t pf_children_at_index, + libvlc_media_list_view_constructor_func_t pf_constructor, + libvlc_media_list_view_release_func_t pf_release, + void * this_view_data, + libvlc_exception_t * p_e ) +{ + libvlc_media_list_view_t * p_mlv; + p_mlv = calloc( 1, sizeof(libvlc_media_list_view_t) ); + if( !p_mlv ) + return NULL; + + p_mlv->p_libvlc_instance = p_mlist->p_libvlc_instance; + p_mlv->p_event_manager = libvlc_event_manager_new( p_mlist, + p_mlv->p_libvlc_instance, p_e ); + + libvlc_event_manager_register_event_type( p_mlv->p_event_manager, + libvlc_MediaListViewItemAdded, p_e ); + libvlc_event_manager_register_event_type( p_mlv->p_event_manager, + libvlc_MediaListViewWillAddItem, p_e ); + libvlc_event_manager_register_event_type( p_mlv->p_event_manager, + libvlc_MediaListViewItemDeleted, p_e ); + libvlc_event_manager_register_event_type( p_mlv->p_event_manager, + libvlc_MediaListViewWillDeleteItem, p_e ); + + libvlc_media_list_retain( p_mlist ); + p_mlv->p_mlist = p_mlist; + + p_mlv->pf_count = pf_count; + p_mlv->pf_item_at_index = pf_item_at_index; + p_mlv->pf_children_at_index = pf_children_at_index; + p_mlv->pf_constructor = pf_constructor; + p_mlv->pf_release = pf_release; + + p_mlv->p_this_view_data = this_view_data; + + vlc_mutex_init( &p_mlv->object_lock ); + p_mlv->i_refcount = 1; + + return p_mlv; +} + + +/* + * Public libvlc functions + */ + +/************************************************************************** + * libvlc_media_list_view_retain (Public) + **************************************************************************/ +void +libvlc_media_list_view_retain( libvlc_media_list_view_t * p_mlv ) +{ + vlc_mutex_lock( &p_mlv->object_lock ); + p_mlv->i_refcount++; + vlc_mutex_unlock( &p_mlv->object_lock ); +} + +/************************************************************************** + * libvlc_media_list_view_release (Public) + **************************************************************************/ +void +libvlc_media_list_view_release( libvlc_media_list_view_t * p_mlv ) +{ + vlc_mutex_lock( &p_mlv->object_lock ); + p_mlv->i_refcount--; + if( p_mlv->i_refcount > 0 ) + { + vlc_mutex_unlock( &p_mlv->object_lock ); + return; + } + vlc_mutex_unlock( &p_mlv->object_lock ); + + /* Refcount null, time to free */ + libvlc_media_list_lock( p_mlv->p_mlist ); + + if( p_mlv->pf_ml_item_added ) + { + libvlc_event_detach( p_mlv->p_mlist->p_event_manager, + libvlc_MediaListItemAdded, + media_list_item_added, p_mlv, NULL ); + } + if( p_mlv->pf_ml_item_removed ) + { + libvlc_event_detach( p_mlv->p_mlist->p_event_manager, + libvlc_MediaListItemDeleted, + media_list_item_removed, p_mlv, NULL ); + } + int i, count = libvlc_media_list_count( p_mlv->p_mlist, NULL ); + for( i = 0; i < count; i++) + { + libvlc_media_t * p_md; + p_md = libvlc_media_list_item_at_index( p_mlv->p_mlist, i, NULL ); + uninstall_md_listener( p_mlv, p_md ); + libvlc_media_release( p_md ); + } + libvlc_media_list_unlock( p_mlv->p_mlist ); + + libvlc_event_manager_release( p_mlv->p_event_manager ); + + if( p_mlv->pf_release ) p_mlv->pf_release( p_mlv ); + libvlc_media_list_release( p_mlv->p_mlist ); + vlc_mutex_destroy( &p_mlv->object_lock ); +} + +/************************************************************************** + * libvlc_media_list_view_event_manager (Public) + **************************************************************************/ +libvlc_event_manager_t * +libvlc_media_list_view_event_manager( libvlc_media_list_view_t * p_mlv ) +{ + libvlc_event_manager_t * p_em; + vlc_mutex_lock( &p_mlv->object_lock ); + p_em = p_mlv->p_event_manager; + vlc_mutex_unlock( &p_mlv->object_lock ); + return p_em; +} + +/************************************************************************** + * libvlc_media_list_view_parent_media_list (Public) + **************************************************************************/ +libvlc_media_list_t * +libvlc_media_list_view_parent_media_list( libvlc_media_list_view_t * p_mlv, + libvlc_exception_t * p_e) +{ + (void)p_e; + libvlc_media_list_t * p_mlist; + vlc_mutex_lock( &p_mlv->object_lock ); + p_mlist = p_mlv->p_mlist; + libvlc_media_list_retain( p_mlv->p_mlist ); + vlc_mutex_unlock( &p_mlv->object_lock ); + return p_mlist; +} + +/************************************************************************** + * libvlc_media_list_view_children_for_item (Public) + **************************************************************************/ +libvlc_media_list_view_t * +libvlc_media_list_view_children_for_item( libvlc_media_list_view_t * p_mlv, + libvlc_media_t * p_md, + libvlc_exception_t * p_e) +{ + (void)p_e; + libvlc_media_list_t * p_mlist; + libvlc_media_list_view_t * ret; + + p_mlist = libvlc_media_subitems(p_md, p_e); + if(!p_mlist) return NULL; + + ret = p_mlv->pf_constructor( p_mlist, p_e ); + libvlc_media_list_release( p_mlist ); + + return ret; +} + +/* Limited to four args, because it should be enough */ + +#define AN_SELECT( collapser, dec1, dec2, dec3, dec4, p, ...) p +#define ARGS(...) AN_SELECT( collapser, ##__VA_ARGS__, \ + (p_mlv, arg1, arg2, arg3, arg4, p_e), \ + (p_mlv, arg1, arg2, arg3, p_e), \ + (p_mlv, arg1, arg2, p_e), \ + (p_mlv, arg1, p_e), (p_mlv, p_e) ) + +#define MEDIA_LIST_VIEW_FUNCTION( name, ret_type, default_ret_value, /* Params */ ... ) \ + ret_type \ + libvlc_media_list_view_##name( libvlc_media_list_view_t * p_mlv, \ + ##__VA_ARGS__, \ + libvlc_exception_t * p_e ) \ + { \ + if( p_mlv->pf_##name ) \ + return p_mlv->pf_##name ARGS(__VA_ARGS__) ; \ + libvlc_exception_raise( p_e, "No '" #name "' method in this media_list_view" ); \ + return default_ret_value;\ + } + +#define MEDIA_LIST_VIEW_FUNCTION_VOID_RET( name, /* Params */ ... ) \ + void \ + libvlc_media_list_view_##name( libvlc_media_list_view_t * p_mlv, \ + ##__VA_ARGS__, \ + libvlc_exception_t * p_e ) \ + { \ + if( p_mlv->pf_##name ) \ + { \ + p_mlv->pf_##name ARGS(__VA_ARGS__) ; \ + return; \ + } \ + libvlc_exception_raise( p_e, "No '" #name "' method in this media_list_view" ); \ + } + + +MEDIA_LIST_VIEW_FUNCTION( count, int, 0 ) +MEDIA_LIST_VIEW_FUNCTION( item_at_index, libvlc_media_t *, NULL, int arg1 ) +MEDIA_LIST_VIEW_FUNCTION( children_at_index, libvlc_media_list_view_t *, NULL, int arg1 ) + diff --git a/VLC/media_player.c b/VLC/media_player.c new file mode 100644 index 0000000..a90d177 --- /dev/null +++ b/VLC/media_player.c @@ -0,0 +1,1015 @@ +/***************************************************************************** + * media_player.c: Libvlc API Media Instance management functions + ***************************************************************************** + * Copyright (C) 2005 the VideoLAN team + * $Id$ + * + * Authors: Clément Stenac + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#include "libvlc_internal.h" + +#include +#include +#include +#include +#include "libvlc.h" + +static void +input_state_changed( const vlc_event_t * event, void * p_userdata ); + +static int +input_seekable_changed( vlc_object_t * p_this, char const * psz_cmd, + vlc_value_t oldval, vlc_value_t newval, + void * p_userdata ); +static int +input_pausable_changed( vlc_object_t * p_this, char const * psz_cmd, + vlc_value_t oldval, vlc_value_t newval, + void * p_userdata ); +static int +input_position_changed( vlc_object_t * p_this, char const * psz_cmd, + vlc_value_t oldval, vlc_value_t newval, + void * p_userdata ); +static int +input_time_changed( vlc_object_t * p_this, char const * psz_cmd, + vlc_value_t oldval, vlc_value_t newval, + void * p_userdata ); + +static const libvlc_state_t vlc_to_libvlc_state_array[] = +{ + [INIT_S] = libvlc_NothingSpecial, + [OPENING_S] = libvlc_Opening, + [BUFFERING_S] = libvlc_Buffering, + [PLAYING_S] = libvlc_Playing, + [PAUSE_S] = libvlc_Paused, + [STOP_S] = libvlc_Stopped, + [FORWARD_S] = libvlc_Forward, + [BACKWARD_S] = libvlc_Backward, + [END_S] = libvlc_Ended, + [ERROR_S] = libvlc_Error, +}; + +static inline libvlc_state_t vlc_to_libvlc_state( int vlc_state ) +{ + if( vlc_state < 0 || vlc_state > 6 ) + return libvlc_Ended; + + return vlc_to_libvlc_state_array[vlc_state]; +} + +/* + * Release the associated input thread + * + * Object lock is NOT held. + */ +static void release_input_thread( libvlc_media_player_t *p_mi ) +{ + input_thread_t * p_input_thread; + + if( !p_mi || !p_mi->p_input_thread ) + return; + + p_input_thread = p_mi->p_input_thread; + + /* No one is tracking this input_thread appart us. Destroy it */ + if( p_mi->b_own_its_input_thread ) + { + vlc_event_manager_t * p_em = input_get_event_manager( p_input_thread ); + vlc_event_detach( p_em, vlc_InputStateChanged, input_state_changed, p_mi ); + var_DelCallback( p_input_thread, "seekable", input_seekable_changed, p_mi ); + var_DelCallback( p_input_thread, "pausable", input_pausable_changed, p_mi ); + var_DelCallback( p_input_thread, "intf-change", input_position_changed, p_mi ); + var_DelCallback( p_input_thread, "intf-change", input_time_changed, p_mi ); + + /* We owned this one */ + input_StopThread( p_input_thread ); + vlc_thread_join( p_input_thread ); + + var_Destroy( p_input_thread, "drawable" ); + } + + vlc_object_release( p_input_thread ); + + p_mi->p_input_thread = NULL; +} + +/* + * Retrieve the input thread. Be sure to release the object + * once you are done with it. (libvlc Internal) + * + * Object lock is held. + */ +input_thread_t *libvlc_get_input_thread( libvlc_media_player_t *p_mi, + libvlc_exception_t *p_e ) +{ + input_thread_t *p_input_thread; + + if( !p_mi ) RAISENULL( "Media Instance is NULL" ); + + vlc_mutex_lock( &p_mi->object_lock ); + + if( !p_mi->p_input_thread ) + { + vlc_mutex_unlock( &p_mi->object_lock ); + RAISENULL( "Input is NULL" ); + } + + p_input_thread = p_mi->p_input_thread; + vlc_object_yield( p_input_thread ); + + vlc_mutex_unlock( &p_mi->object_lock ); + + return p_input_thread; +} + +/* + * input_state_changed (Private) (vlc_InputStateChanged callback) + */ +static void +input_state_changed( const vlc_event_t * event, void * p_userdata ) +{ + libvlc_media_player_t * p_mi = p_userdata; + libvlc_event_t forwarded_event; + libvlc_event_type_t type = event->u.input_state_changed.new_state; + + switch ( type ) + { + case INIT_S: + libvlc_media_set_state( p_mi->p_md, libvlc_NothingSpecial, NULL); + forwarded_event.type = libvlc_MediaPlayerNothingSpecial; + break; + case OPENING_S: + libvlc_media_set_state( p_mi->p_md, libvlc_Opening, NULL); + forwarded_event.type = libvlc_MediaPlayerOpening; + break; + case BUFFERING_S: + libvlc_media_set_state( p_mi->p_md, libvlc_Buffering, NULL); + forwarded_event.type = libvlc_MediaPlayerBuffering; + break; + case PLAYING_S: + libvlc_media_set_state( p_mi->p_md, libvlc_Playing, NULL); + forwarded_event.type = libvlc_MediaPlayerPlaying; + break; + case PAUSE_S: + libvlc_media_set_state( p_mi->p_md, libvlc_Paused, NULL); + forwarded_event.type = libvlc_MediaPlayerPaused; + break; + case STOP_S: + libvlc_media_set_state( p_mi->p_md, libvlc_Stopped, NULL); + forwarded_event.type = libvlc_MediaPlayerStopped; + break; + case FORWARD_S: + libvlc_media_set_state( p_mi->p_md, libvlc_Forward, NULL); + forwarded_event.type = libvlc_MediaPlayerForward; + break; + case BACKWARD_S: + libvlc_media_set_state( p_mi->p_md, libvlc_Backward, NULL); + forwarded_event.type = libvlc_MediaPlayerBackward; + break; + case END_S: + libvlc_media_set_state( p_mi->p_md, libvlc_Ended, NULL); + forwarded_event.type = libvlc_MediaPlayerEndReached; + case ERROR_S: + libvlc_media_set_state( p_mi->p_md, libvlc_Error, NULL); + forwarded_event.type = libvlc_MediaPlayerEncounteredError; + break; + + default: + return; + } + + libvlc_event_send( p_mi->p_event_manager, &forwarded_event ); + return; +} + +static int +input_seekable_changed( vlc_object_t * p_this, char const * psz_cmd, + vlc_value_t oldval, vlc_value_t newval, + void * p_userdata ) +{ + VLC_UNUSED(oldval); + VLC_UNUSED(p_this); + VLC_UNUSED(psz_cmd); + libvlc_media_player_t * p_mi = p_userdata; + libvlc_event_t event; + + libvlc_media_set_state( p_mi->p_md, libvlc_NothingSpecial, NULL); + event.type = libvlc_MediaPlayerSeekableChanged; + event.u.media_player_seekable_changed.new_seekable = newval.b_bool; + + libvlc_event_send( p_mi->p_event_manager, &event ); + return VLC_SUCCESS; +} + +static int +input_pausable_changed( vlc_object_t * p_this, char const * psz_cmd, + vlc_value_t oldval, vlc_value_t newval, + void * p_userdata ) +{ + VLC_UNUSED(oldval); + VLC_UNUSED(p_this); + VLC_UNUSED(psz_cmd); + libvlc_media_player_t * p_mi = p_userdata; + libvlc_event_t event; + + libvlc_media_set_state( p_mi->p_md, libvlc_NothingSpecial, NULL); + event.type = libvlc_MediaPlayerPausableChanged; + event.u.media_player_pausable_changed.new_pausable = newval.b_bool; + + libvlc_event_send( p_mi->p_event_manager, &event ); + return VLC_SUCCESS; +} + +/* + * input_position_changed (Private) (input var "intf-change" Callback) + */ +static int +input_position_changed( vlc_object_t * p_this, char const * psz_cmd, + vlc_value_t oldval, vlc_value_t newval, + void * p_userdata ) +{ + VLC_UNUSED(oldval); + libvlc_media_player_t * p_mi = p_userdata; + vlc_value_t val; + + if (!strncmp(psz_cmd, "intf", 4 /* "-change" no need to go further */)) + { + input_thread_t * p_input = (input_thread_t *)p_this; + + var_Get( p_input, "state", &val ); + if( val.i_int != PLAYING_S ) + return VLC_SUCCESS; /* Don't send the position while stopped */ + + var_Get( p_input, "position", &val ); + } + else + val.i_time = newval.i_time; + + libvlc_event_t event; + event.type = libvlc_MediaPlayerPositionChanged; + event.u.media_player_position_changed.new_position = val.f_float; + + libvlc_event_send( p_mi->p_event_manager, &event ); + return VLC_SUCCESS; +} + +/* + * input_time_changed (Private) (input var "intf-change" Callback) + */ +static int +input_time_changed( vlc_object_t * p_this, char const * psz_cmd, + vlc_value_t oldval, vlc_value_t newval, + void * p_userdata ) +{ + VLC_UNUSED(oldval); + libvlc_media_player_t * p_mi = p_userdata; + vlc_value_t val; + + if (!strncmp(psz_cmd, "intf", 4 /* "-change" no need to go further */)) + { + input_thread_t * p_input = (input_thread_t *)p_this; + + var_Get( p_input, "state", &val ); + if( val.i_int != PLAYING_S ) + return VLC_SUCCESS; /* Don't send the position while stopped */ + + var_Get( p_input, "time", &val ); + } + else + val.i_time = newval.i_time; + + libvlc_event_t event; + event.type = libvlc_MediaPlayerTimeChanged; + event.u.media_player_time_changed.new_time = val.i_time; + libvlc_event_send( p_mi->p_event_manager, &event ); + return VLC_SUCCESS; +} + +/************************************************************************** + * Create a Media Instance object + **************************************************************************/ +libvlc_media_player_t * +libvlc_media_player_new( libvlc_instance_t * p_libvlc_instance, + libvlc_exception_t * p_e ) +{ + libvlc_media_player_t * p_mi; + + if( !p_libvlc_instance ) + { + libvlc_exception_raise( p_e, "invalid libvlc instance" ); + return NULL; + } + + p_mi = malloc( sizeof(libvlc_media_player_t) ); + if( !p_mi ) + { + libvlc_exception_raise( p_e, "Not enough memory" ); + return NULL; + } + p_mi->p_md = NULL; + p_mi->drawable = 0; + p_mi->p_libvlc_instance = p_libvlc_instance; + p_mi->p_input_thread = NULL; + /* refcount strategy: + * - All items created by _new start with a refcount set to 1 + * - Accessor _release decrease the refcount by 1, if after that + * operation the refcount is 0, the object is destroyed. + * - Accessor _retain increase the refcount by 1 (XXX: to implement) */ + p_mi->i_refcount = 1; + p_mi->b_own_its_input_thread = true; + /* object_lock strategy: + * - No lock held in constructor + * - Lock when accessing all variable this lock is held + * - Lock when attempting to destroy the object the lock is also held */ + vlc_mutex_init( &p_mi->object_lock ); + p_mi->p_event_manager = libvlc_event_manager_new( p_mi, + p_libvlc_instance, p_e ); + if( libvlc_exception_raised( p_e ) ) + { + free( p_mi ); + return NULL; + } + + libvlc_event_manager_register_event_type( p_mi->p_event_manager, + libvlc_MediaPlayerNothingSpecial, p_e ); + libvlc_event_manager_register_event_type( p_mi->p_event_manager, + libvlc_MediaPlayerOpening, p_e ); + libvlc_event_manager_register_event_type( p_mi->p_event_manager, + libvlc_MediaPlayerBuffering, p_e ); + libvlc_event_manager_register_event_type( p_mi->p_event_manager, + libvlc_MediaPlayerPlaying, p_e ); + libvlc_event_manager_register_event_type( p_mi->p_event_manager, + libvlc_MediaPlayerPaused, p_e ); + libvlc_event_manager_register_event_type( p_mi->p_event_manager, + libvlc_MediaPlayerStopped, p_e ); + libvlc_event_manager_register_event_type( p_mi->p_event_manager, + libvlc_MediaPlayerForward, p_e ); + libvlc_event_manager_register_event_type( p_mi->p_event_manager, + libvlc_MediaPlayerBackward, p_e ); + libvlc_event_manager_register_event_type( p_mi->p_event_manager, + libvlc_MediaPlayerEndReached, p_e ); + libvlc_event_manager_register_event_type( p_mi->p_event_manager, + libvlc_MediaPlayerEncounteredError, p_e ); + + libvlc_event_manager_register_event_type( p_mi->p_event_manager, + libvlc_MediaPlayerPositionChanged, p_e ); + libvlc_event_manager_register_event_type( p_mi->p_event_manager, + libvlc_MediaPlayerTimeChanged, p_e ); + libvlc_event_manager_register_event_type( p_mi->p_event_manager, + libvlc_MediaPlayerSeekableChanged, p_e ); + libvlc_event_manager_register_event_type( p_mi->p_event_manager, + libvlc_MediaPlayerPausableChanged, p_e ); + + return p_mi; +} + +/************************************************************************** + * Create a Media Instance object with a media descriptor + **************************************************************************/ +libvlc_media_player_t * +libvlc_media_player_new_from_media( + libvlc_media_t * p_md, + libvlc_exception_t *p_e ) +{ + libvlc_media_player_t * p_mi; + p_mi = libvlc_media_player_new( p_md->p_libvlc_instance, p_e ); + + if( !p_mi ) + return NULL; + + libvlc_media_retain( p_md ); + p_mi->p_md = p_md; + + return p_mi; +} + +/************************************************************************** + * Create a new media instance object from an input_thread (Libvlc Internal) + **************************************************************************/ +libvlc_media_player_t * libvlc_media_player_new_from_input_thread( + struct libvlc_instance_t *p_libvlc_instance, + input_thread_t *p_input, + libvlc_exception_t *p_e ) +{ + libvlc_media_player_t * p_mi; + + if( !p_input ) + { + libvlc_exception_raise( p_e, "invalid input thread" ); + return NULL; + } + + p_mi = libvlc_media_player_new( p_libvlc_instance, p_e ); + + if( !p_mi ) + return NULL; + + p_mi->p_md = libvlc_media_new_from_input_item( + p_libvlc_instance, + input_GetItem( p_input ), p_e ); + + if( !p_mi->p_md ) + { + libvlc_media_player_destroy( p_mi ); + return NULL; + } + + /* will be released in media_player_release() */ + vlc_object_yield( p_input ); + + p_mi->p_input_thread = p_input; + p_mi->b_own_its_input_thread = false; + + return p_mi; +} + +/************************************************************************** + * Destroy a Media Instance object (libvlc internal) + * + * Warning: No lock held here, but hey, this is internal. + **************************************************************************/ +void libvlc_media_player_destroy( libvlc_media_player_t *p_mi ) +{ + input_thread_t *p_input_thread; + libvlc_exception_t p_e; + + libvlc_exception_init( &p_e ); + + if( !p_mi ) + return; + + p_input_thread = libvlc_get_input_thread( p_mi, &p_e ); + + if( libvlc_exception_raised( &p_e ) ) + { + libvlc_event_manager_release( p_mi->p_event_manager ); + libvlc_exception_clear( &p_e ); + free( p_mi ); + return; /* no need to worry about no input thread */ + } + vlc_mutex_destroy( &p_mi->object_lock ); + + vlc_object_release( p_input_thread ); + + libvlc_media_release( p_mi->p_md ); + + free( p_mi ); +} + +/************************************************************************** + * Release a Media Instance object + **************************************************************************/ +void libvlc_media_player_release( libvlc_media_player_t *p_mi ) +{ + if( !p_mi ) + return; + + vlc_mutex_lock( &p_mi->object_lock ); + + p_mi->i_refcount--; + + if( p_mi->i_refcount > 0 ) + { + vlc_mutex_unlock( &p_mi->object_lock ); + return; + } + vlc_mutex_unlock( &p_mi->object_lock ); + vlc_mutex_destroy( &p_mi->object_lock ); + + release_input_thread( p_mi ); + + libvlc_event_manager_release( p_mi->p_event_manager ); + + libvlc_media_release( p_mi->p_md ); + + free( p_mi ); +} + +/************************************************************************** + * Retain a Media Instance object + **************************************************************************/ +void libvlc_media_player_retain( libvlc_media_player_t *p_mi ) +{ + if( !p_mi ) + return; + + p_mi->i_refcount++; +} + +/************************************************************************** + * Set the Media descriptor associated with the instance + **************************************************************************/ +void libvlc_media_player_set_media( + libvlc_media_player_t *p_mi, + libvlc_media_t *p_md, + libvlc_exception_t *p_e ) +{ + VLC_UNUSED(p_e); + + if( !p_mi ) + return; + + vlc_mutex_lock( &p_mi->object_lock ); + + release_input_thread( p_mi ); + + if( p_mi->p_md ) + libvlc_media_set_state( p_mi->p_md, libvlc_NothingSpecial, p_e ); + + libvlc_media_release( p_mi->p_md ); + + if( !p_md ) + { + p_mi->p_md = NULL; + vlc_mutex_unlock( &p_mi->object_lock ); + return; /* It is ok to pass a NULL md */ + } + + libvlc_media_retain( p_md ); + p_mi->p_md = p_md; + + /* The policy here is to ignore that we were created using a different + * libvlc_instance, because we don't really care */ + p_mi->p_libvlc_instance = p_md->p_libvlc_instance; + + vlc_mutex_unlock( &p_mi->object_lock ); +} + +/************************************************************************** + * Get the Media descriptor associated with the instance + **************************************************************************/ +libvlc_media_t * +libvlc_media_player_get_media( + libvlc_media_player_t *p_mi, + libvlc_exception_t *p_e ) +{ + VLC_UNUSED(p_e); + + if( !p_mi->p_md ) + return NULL; + + libvlc_media_retain( p_mi->p_md ); + return p_mi->p_md; +} + +/************************************************************************** + * Get the event Manager + **************************************************************************/ +libvlc_event_manager_t * +libvlc_media_player_event_manager( + libvlc_media_player_t *p_mi, + libvlc_exception_t *p_e ) +{ + VLC_UNUSED(p_e); + + return p_mi->p_event_manager; +} + +/************************************************************************** + * Play + **************************************************************************/ +void libvlc_media_player_play( libvlc_media_player_t *p_mi, + libvlc_exception_t *p_e ) +{ + input_thread_t * p_input_thread; + + if( (p_input_thread = libvlc_get_input_thread( p_mi, p_e )) ) + { + /* A thread already exists, send it a play message */ + input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S ); + vlc_object_release( p_input_thread ); + return; + } + + /* Ignore previous exception */ + libvlc_exception_clear( p_e ); + + vlc_mutex_lock( &p_mi->object_lock ); + + if( !p_mi->p_md ) + { + libvlc_exception_raise( p_e, "no associated media descriptor" ); + vlc_mutex_unlock( &p_mi->object_lock ); + return; + } + + p_mi->p_input_thread = input_CreateThread( p_mi->p_libvlc_instance->p_libvlc_int, + p_mi->p_md->p_input_item ); + + + if( !p_mi->p_input_thread ) + { + vlc_mutex_unlock( &p_mi->object_lock ); + return; + } + + p_input_thread = p_mi->p_input_thread; + + if( p_mi->drawable ) + { + vlc_value_t val; + val.i_int = p_mi->drawable; + var_Create( p_input_thread, "drawable", VLC_VAR_DOINHERIT ); + var_Set( p_input_thread, "drawable", val ); + } + + vlc_event_manager_t * p_em = input_get_event_manager( p_input_thread ); + vlc_event_attach( p_em, vlc_InputStateChanged, input_state_changed, p_mi ); + + var_AddCallback( p_input_thread, "seekable", input_seekable_changed, p_mi ); + var_AddCallback( p_input_thread, "pausable", input_pausable_changed, p_mi ); + var_AddCallback( p_input_thread, "intf-change", input_position_changed, p_mi ); + var_AddCallback( p_input_thread, "intf-change", input_time_changed, p_mi ); + + vlc_mutex_unlock( &p_mi->object_lock ); +} + +/************************************************************************** + * Pause + **************************************************************************/ +void libvlc_media_player_pause( libvlc_media_player_t *p_mi, + libvlc_exception_t *p_e ) +{ + input_thread_t * p_input_thread = libvlc_get_input_thread( p_mi, p_e ); + + if( !p_input_thread ) + return; + + libvlc_state_t state = libvlc_media_player_get_state( p_mi, p_e ); + + if( state == libvlc_Playing ) + { + if( libvlc_media_player_can_pause( p_mi, p_e ) ) + input_Control( p_input_thread, INPUT_SET_STATE, PAUSE_S ); + else + libvlc_media_player_stop( p_mi, p_e ); + } + else + input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S ); + + vlc_object_release( p_input_thread ); +} + +/************************************************************************** + * Stop + **************************************************************************/ +void libvlc_media_player_stop( libvlc_media_player_t *p_mi, + libvlc_exception_t *p_e ) +{ + libvlc_state_t state = libvlc_media_player_get_state( p_mi, p_e ); + + if( state == libvlc_Playing || state == libvlc_Paused ) + { + /* Send a stop notification event only of we are in playing or paused states */ + libvlc_media_set_state( p_mi->p_md, libvlc_Ended, p_e ); + + /* Construct and send the event */ + libvlc_event_t event; + event.type = libvlc_MediaPlayerEndReached; + libvlc_event_send( p_mi->p_event_manager, &event ); + } + + if( p_mi->b_own_its_input_thread ) + { + vlc_mutex_lock( &p_mi->object_lock ); + release_input_thread( p_mi ); /* This will stop the input thread */ + vlc_mutex_unlock( &p_mi->object_lock ); + } + else + { + input_thread_t * p_input_thread = libvlc_get_input_thread( p_mi, p_e ); + + if( !p_input_thread ) + return; + + input_StopThread( p_input_thread ); + vlc_object_release( p_input_thread ); + } +} + +/************************************************************************** + * Set Drawable + **************************************************************************/ +void libvlc_media_player_set_drawable( libvlc_media_player_t *p_mi, + libvlc_drawable_t drawable, + libvlc_exception_t *p_e ) +{ + input_thread_t *p_input_thread; + vout_thread_t *p_vout = NULL; + + p_mi->drawable = drawable; + + /* Allow on the fly drawable changing. This is tricky has this may + * not be supported by every vout. We though can't disable it + * because of some creepy drawable type that are not flexible enough + * (Win32 HWND for instance) */ + p_input_thread = libvlc_get_input_thread( p_mi, p_e ); + if( !p_input_thread ) { + /* No input, nothing more to do, we are fine */ + libvlc_exception_clear( p_e ); + return; + } + + p_vout = vlc_object_find( p_input_thread, VLC_OBJECT_VOUT, FIND_CHILD ); + if( p_vout ) + { + vout_Control( p_vout , VOUT_REPARENT, drawable); + vlc_object_release( p_vout ); + } + vlc_object_release( p_input_thread ); +} + +/************************************************************************** + * Get Drawable + **************************************************************************/ +libvlc_drawable_t +libvlc_media_player_get_drawable ( libvlc_media_player_t *p_mi, libvlc_exception_t *p_e ) +{ + VLC_UNUSED(p_e); + return p_mi->drawable; +} + +/************************************************************************** + * Getters for stream information + **************************************************************************/ +libvlc_time_t libvlc_media_player_get_length( + libvlc_media_player_t *p_mi, + libvlc_exception_t *p_e ) +{ + input_thread_t *p_input_thread; + vlc_value_t val; + + p_input_thread = libvlc_get_input_thread ( p_mi, p_e); + if( !p_input_thread ) + return -1; + + var_Get( p_input_thread, "length", &val ); + vlc_object_release( p_input_thread ); + + return (val.i_time+500LL)/1000LL; +} + +libvlc_time_t libvlc_media_player_get_time( + libvlc_media_player_t *p_mi, + libvlc_exception_t *p_e ) +{ + input_thread_t *p_input_thread; + vlc_value_t val; + + p_input_thread = libvlc_get_input_thread ( p_mi, p_e ); + if( !p_input_thread ) + return -1; + + var_Get( p_input_thread , "time", &val ); + vlc_object_release( p_input_thread ); + return (val.i_time+500LL)/1000LL; +} + +void libvlc_media_player_set_time( + libvlc_media_player_t *p_mi, + libvlc_time_t time, + libvlc_exception_t *p_e ) +{ + input_thread_t *p_input_thread; + vlc_value_t value; + + p_input_thread = libvlc_get_input_thread ( p_mi, p_e ); + if( !p_input_thread ) + return; + + value.i_time = time*1000LL; + var_Set( p_input_thread, "time", value ); + vlc_object_release( p_input_thread ); +} + +void libvlc_media_player_set_position( + libvlc_media_player_t *p_mi, + float position, + libvlc_exception_t *p_e ) +{ + input_thread_t *p_input_thread; + vlc_value_t val; + val.f_float = position; + + p_input_thread = libvlc_get_input_thread ( p_mi, p_e); + if( !p_input_thread ) + return; + + var_Set( p_input_thread, "position", val ); + vlc_object_release( p_input_thread ); +} + +float libvlc_media_player_get_position( + libvlc_media_player_t *p_mi, + libvlc_exception_t *p_e ) +{ + input_thread_t *p_input_thread; + vlc_value_t val; + + p_input_thread = libvlc_get_input_thread ( p_mi, p_e ); + if( !p_input_thread ) + return -1.0; + + var_Get( p_input_thread, "position", &val ); + vlc_object_release( p_input_thread ); + + return val.f_float; +} + +void libvlc_media_player_set_chapter( + libvlc_media_player_t *p_mi, + int chapter, + libvlc_exception_t *p_e ) +{ + input_thread_t *p_input_thread; + vlc_value_t val; + val.i_int = chapter; + + p_input_thread = libvlc_get_input_thread ( p_mi, p_e); + if( !p_input_thread ) + return; + + var_Set( p_input_thread, "chapter", val ); + vlc_object_release( p_input_thread ); +} + +int libvlc_media_player_get_chapter( + libvlc_media_player_t *p_mi, + libvlc_exception_t *p_e ) +{ + input_thread_t *p_input_thread; + vlc_value_t val; + + p_input_thread = libvlc_get_input_thread ( p_mi, p_e ); + if( !p_input_thread ) + return -1.0; + + var_Get( p_input_thread, "chapter", &val ); + vlc_object_release( p_input_thread ); + + return val.i_int; +} + +int libvlc_media_player_get_chapter_count( + libvlc_media_player_t *p_mi, + libvlc_exception_t *p_e ) +{ + input_thread_t *p_input_thread; + vlc_value_t val; + + p_input_thread = libvlc_get_input_thread ( p_mi, p_e ); + if( !p_input_thread ) + return -1.0; + + var_Change( p_input_thread, "chapter", VLC_VAR_CHOICESCOUNT, &val, NULL ); + vlc_object_release( p_input_thread ); + + return val.i_int; +} + +float libvlc_media_player_get_fps( + libvlc_media_player_t *p_mi, + libvlc_exception_t *p_e) +{ + input_thread_t *p_input_thread = libvlc_get_input_thread ( p_mi, p_e ); + double f_fps = 0.0; + + if( p_input_thread ) + { + if( input_Control( p_input_thread, INPUT_GET_VIDEO_FPS, &f_fps ) ) + f_fps = 0.0; + vlc_object_release( p_input_thread ); + } + return f_fps; +} + +int libvlc_media_player_will_play( libvlc_media_player_t *p_mi, + libvlc_exception_t *p_e) +{ + input_thread_t *p_input_thread = + libvlc_get_input_thread ( p_mi, p_e); + if ( !p_input_thread ) + return false; + + if ( !p_input_thread->b_die && !p_input_thread->b_dead ) + { + vlc_object_release( p_input_thread ); + return true; + } + vlc_object_release( p_input_thread ); + return false; +} + +void libvlc_media_player_set_rate( + libvlc_media_player_t *p_mi, + float rate, + libvlc_exception_t *p_e ) +{ + input_thread_t *p_input_thread; + vlc_value_t val; + + if( rate <= 0 ) + RAISEVOID( "Rate value is invalid" ); + + val.i_int = 1000.0f/rate; + + p_input_thread = libvlc_get_input_thread ( p_mi, p_e); + if ( !p_input_thread ) + return; + + var_Set( p_input_thread, "rate", val ); + vlc_object_release( p_input_thread ); +} + +float libvlc_media_player_get_rate( + libvlc_media_player_t *p_mi, + libvlc_exception_t *p_e ) +{ + input_thread_t *p_input_thread; + vlc_value_t val; + + p_input_thread = libvlc_get_input_thread ( p_mi, p_e); + if ( !p_input_thread ) + return -1.0; + + var_Get( p_input_thread, "rate", &val ); + vlc_object_release( p_input_thread ); + + return (float)1000.0f/val.i_int; +} + +libvlc_state_t libvlc_media_player_get_state( + libvlc_media_player_t *p_mi, + libvlc_exception_t *p_e ) +{ + input_thread_t *p_input_thread; + vlc_value_t val; + + p_input_thread = libvlc_get_input_thread ( p_mi, p_e ); + if ( !p_input_thread ) + { + /* We do return the right value, no need to throw an exception */ + if( libvlc_exception_raised( p_e ) ) + libvlc_exception_clear( p_e ); + return libvlc_Ended; + } + + var_Get( p_input_thread, "state", &val ); + vlc_object_release( p_input_thread ); + + return vlc_to_libvlc_state(val.i_int); +} + +int libvlc_media_player_is_seekable( libvlc_media_player_t *p_mi, + libvlc_exception_t *p_e ) +{ + input_thread_t *p_input_thread; + vlc_value_t val; + + p_input_thread = libvlc_get_input_thread ( p_mi, p_e ); + if ( !p_input_thread ) + { + /* We do return the right value, no need to throw an exception */ + if( libvlc_exception_raised( p_e ) ) + libvlc_exception_clear( p_e ); + return false; + } + var_Get( p_input_thread, "seekable", &val ); + vlc_object_release( p_input_thread ); + + return val.b_bool; +} + +int libvlc_media_player_can_pause( libvlc_media_player_t *p_mi, + libvlc_exception_t *p_e ) +{ + input_thread_t *p_input_thread; + vlc_value_t val; + + p_input_thread = libvlc_get_input_thread ( p_mi, p_e ); + if ( !p_input_thread ) + { + /* We do return the right value, no need to throw an exception */ + if( libvlc_exception_raised( p_e ) ) + libvlc_exception_clear( p_e ); + return false; + } + var_Get( p_input_thread, "can-pause", &val ); + vlc_object_release( p_input_thread ); + + return val.b_bool; +} diff --git a/VLC/mediacontrol_audio_video.c b/VLC/mediacontrol_audio_video.c new file mode 100644 index 0000000..e139553 --- /dev/null +++ b/VLC/mediacontrol_audio_video.c @@ -0,0 +1,346 @@ +/***************************************************************************** + * audio_video.c: Audio/Video management : volume, snapshot, OSD + ***************************************************************************** + * Copyright (C) 2005 the VideoLAN team + * $Id$ + * + * Authors: Olivier Aubert + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include "mediacontrol_internal.h" +#include "libvlc_internal.h" + +#include +#include + +#include +#include + +#include /* malloc(), free() */ +#include + +#include /* ENOMEM */ +#include +#include + +#ifdef HAVE_UNISTD_H +# include +#endif +#ifdef HAVE_SYS_TIME_H +# include +#endif +#ifdef HAVE_SYS_TYPES_H +# include +#endif + +mediacontrol_RGBPicture * +mediacontrol_snapshot( mediacontrol_Instance *self, + const mediacontrol_Position * a_position, + mediacontrol_Exception *exception ) +{ + (void)a_position; + vlc_object_t* p_cache; + vout_thread_t* p_vout; + input_thread_t *p_input; + mediacontrol_RGBPicture *p_pic = NULL; + char path[256]; + snapshot_t *p_snapshot; + libvlc_exception_t ex; + + libvlc_exception_init( &ex ); + mediacontrol_exception_init( exception ); + + p_input = libvlc_get_input_thread( self->p_media_player, &ex ); + if( ! p_input ) + { + RAISE_NULL( mediacontrol_InternalException, "No input" ); + } + p_vout = vlc_object_find( p_input, VLC_OBJECT_VOUT, FIND_CHILD ); + if( ! p_vout ) + { + RAISE_NULL( mediacontrol_InternalException, "No video output" ); + } + p_cache = vlc_object_create( p_input, sizeof( vlc_object_t ) ); + if( p_cache == NULL ) + { + vlc_object_release( p_vout ); + vlc_object_release( p_input ); + RAISE_NULL( mediacontrol_InternalException, "Out of memory" ); + } + snprintf( path, 255, "object:%d", p_cache->i_object_id ); + var_SetString( p_vout, "snapshot-path", path ); + var_SetString( p_vout, "snapshot-format", "png" ); + + vlc_object_lock( p_cache ); + vout_Control( p_vout, VOUT_SNAPSHOT ); + vlc_object_wait( p_cache ); + vlc_object_release( p_vout ); + + p_snapshot = ( snapshot_t* ) p_cache->p_private; + vlc_object_unlock( p_cache ); + vlc_object_release( p_cache ); + vlc_object_release( p_input ); + + if( p_snapshot ) + { + p_pic = private_mediacontrol_createRGBPicture( p_snapshot->i_width, + p_snapshot->i_height, + VLC_FOURCC( 'p','n','g',' ' ), + p_snapshot->date, + p_snapshot->p_data, + p_snapshot->i_datasize ); + if( !p_pic ) + { + free( p_snapshot->p_data ); + free( p_snapshot ); + RAISE_NULL( mediacontrol_InternalException, "Out of memory" ); + } + } + else + { + RAISE_NULL( mediacontrol_InternalException, "Snapshot exception" ); + } + return p_pic; +} + +static +int mediacontrol_showtext( vout_thread_t *p_vout, int i_channel, + char *psz_string, text_style_t *p_style, + int i_flags, int i_hmargin, int i_vmargin, + mtime_t i_start, mtime_t i_stop ) +{ + subpicture_t *p_spu; + video_format_t fmt; + + if( !psz_string ) return VLC_EGENERIC; + + p_spu = spu_CreateSubpicture( p_vout->p_spu ); + if( !p_spu ) return VLC_EGENERIC; + + /* Create a new subpicture region */ + memset( &fmt, 0, sizeof(video_format_t) ); + fmt.i_chroma = VLC_FOURCC('T','E','X','T'); + fmt.i_aspect = 0; + fmt.i_width = fmt.i_height = 0; + fmt.i_x_offset = fmt.i_y_offset = 0; + p_spu->p_region = p_spu->pf_create_region( VLC_OBJECT(p_vout), &fmt ); + if( !p_spu->p_region ) + { + msg_Err( p_vout, "cannot allocate SPU region" ); + spu_DestroySubpicture( p_vout->p_spu, p_spu ); + return VLC_EGENERIC; + } + + p_spu->p_region->psz_text = strdup( psz_string ); + p_spu->p_region->i_align = i_flags & SUBPICTURE_ALIGN_MASK; + p_spu->p_region->p_style = p_style; + p_spu->i_start = i_start; + p_spu->i_stop = i_stop; + p_spu->b_ephemer = false; + p_spu->b_absolute = false; + + p_spu->i_x = i_hmargin; + p_spu->i_y = i_vmargin; + p_spu->i_flags = i_flags & ~SUBPICTURE_ALIGN_MASK; + p_spu->i_channel = i_channel; + + spu_DisplaySubpicture( p_vout->p_spu, p_spu ); + + return VLC_SUCCESS; +} + + +void +mediacontrol_display_text( mediacontrol_Instance *self, + const char * message, + const mediacontrol_Position * begin, + const mediacontrol_Position * end, + mediacontrol_Exception *exception ) +{ + vout_thread_t *p_vout = NULL; + char* psz_message; + input_thread_t *p_input; + libvlc_exception_t ex; + + libvlc_exception_init( &ex ); + mediacontrol_exception_init( exception ); + + p_input = libvlc_get_input_thread( self->p_media_player, &ex ); + if( ! p_input ) + { + RAISE_VOID( mediacontrol_InternalException, "No input" ); + } + p_vout = vlc_object_find( p_input, VLC_OBJECT_VOUT, FIND_CHILD ); + if( ! p_vout ) + { + RAISE_VOID( mediacontrol_InternalException, "No video output" ); + } + + psz_message = strdup( message ); + if( !psz_message ) + { + RAISE_VOID( mediacontrol_InternalException, "no more memory" ); + } + + if( begin->origin == mediacontrol_RelativePosition && + begin->value == 0 && + end->origin == mediacontrol_RelativePosition ) + { + mtime_t i_duration = 0; + mtime_t i_now = mdate(); + + i_duration = 1000 * private_mediacontrol_unit_convert( + self->p_media_player, + end->key, + mediacontrol_MediaTime, + end->value ); + + mediacontrol_showtext( p_vout, DEFAULT_CHAN, psz_message, NULL, + OSD_ALIGN_BOTTOM | OSD_ALIGN_LEFT, 0, 0, + i_now, i_now + i_duration ); + } + else + { + mtime_t i_debut, i_fin, i_now; + + /* FIXME */ + /* i_now = input_ClockGetTS( p_input, NULL, 0 ); */ + i_now = mdate(); + + i_debut = private_mediacontrol_position2microsecond( self->p_media_player, + ( mediacontrol_Position* ) begin ); + i_debut += i_now; + + i_fin = private_mediacontrol_position2microsecond( self->p_media_player, + ( mediacontrol_Position * ) end ); + i_fin += i_now; + + vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN, psz_message, NULL, + OSD_ALIGN_BOTTOM | OSD_ALIGN_LEFT, 0, 0, + i_debut, i_fin ); + } + + vlc_object_release( p_vout ); +} + +unsigned short +mediacontrol_sound_get_volume( mediacontrol_Instance *self, + mediacontrol_Exception *exception ) +{ + libvlc_exception_t ex; + int i_ret = 0; + + mediacontrol_exception_init( exception ); + libvlc_exception_init( &ex ); + + i_ret = libvlc_audio_get_volume( self->p_instance, &ex ); + HANDLE_LIBVLC_EXCEPTION_ZERO( &ex ); + /* FIXME: Normalize in [0..100] */ + return (unsigned short)i_ret; +} + +void +mediacontrol_sound_set_volume( mediacontrol_Instance *self, + const unsigned short volume, + mediacontrol_Exception *exception ) +{ + /* FIXME: Normalize in [0..100] */ + libvlc_exception_t ex; + + mediacontrol_exception_init( exception ); + libvlc_exception_init( &ex ); + + libvlc_audio_set_volume( self->p_instance, volume, &ex ); + HANDLE_LIBVLC_EXCEPTION_VOID( &ex ); +} + +int mediacontrol_set_visual( mediacontrol_Instance *self, + WINDOWHANDLE visual_id, + mediacontrol_Exception *exception ) +{ + libvlc_exception_t ex; + + mediacontrol_exception_init( exception ); + libvlc_exception_init( &ex ); + + libvlc_media_player_set_drawable( self->p_media_player, (libvlc_drawable_t)visual_id, &ex ); + HANDLE_LIBVLC_EXCEPTION_ZERO( &ex ); + return true; +} + +int +mediacontrol_get_rate( mediacontrol_Instance *self, + mediacontrol_Exception *exception ) +{ + libvlc_exception_t ex; + int i_ret; + + mediacontrol_exception_init( exception ); + libvlc_exception_init( &ex ); + + i_ret = libvlc_media_player_get_rate( self->p_media_player, &ex ); + HANDLE_LIBVLC_EXCEPTION_ZERO( &ex ); + + return i_ret / 10; +} + +void +mediacontrol_set_rate( mediacontrol_Instance *self, + const int rate, + mediacontrol_Exception *exception ) +{ + libvlc_exception_t ex; + + mediacontrol_exception_init( exception ); + libvlc_exception_init( &ex ); + + libvlc_media_player_set_rate( self->p_media_player, rate * 10, &ex ); + HANDLE_LIBVLC_EXCEPTION_VOID( &ex ); +} + +int +mediacontrol_get_fullscreen( mediacontrol_Instance *self, + mediacontrol_Exception *exception ) +{ + libvlc_exception_t ex; + int i_ret; + + mediacontrol_exception_init( exception ); + libvlc_exception_init( &ex ); + + i_ret = libvlc_get_fullscreen( self->p_media_player, &ex ); + HANDLE_LIBVLC_EXCEPTION_ZERO( &ex ); + + return i_ret; +} + +void +mediacontrol_set_fullscreen( mediacontrol_Instance *self, + const int b_fullscreen, + mediacontrol_Exception *exception ) +{ + libvlc_exception_t ex; + + mediacontrol_exception_init( exception ); + libvlc_exception_init( &ex ); + + libvlc_set_fullscreen( self->p_media_player, b_fullscreen, &ex ); + HANDLE_LIBVLC_EXCEPTION_VOID( &ex ); +} diff --git a/VLC/mediacontrol_core.c b/VLC/mediacontrol_core.c new file mode 100644 index 0000000..9c13dbf --- /dev/null +++ b/VLC/mediacontrol_core.c @@ -0,0 +1,418 @@ +/***************************************************************************** + * core.c: Core functions : init, playlist, stream management + ***************************************************************************** + * Copyright (C) 2005 the VideoLAN team + * $Id$ + * + * Authors: Olivier Aubert + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "mediacontrol_internal.h" +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include /* malloc(), free() */ +#include + +#include /* ENOMEM */ +#include +#include + +#ifdef HAVE_UNISTD_H +# include +#endif +#ifdef HAVE_SYS_TIME_H +# include +#endif +#ifdef HAVE_SYS_TYPES_H +# include +#endif + +mediacontrol_Instance* mediacontrol_new( int argc, char** argv, mediacontrol_Exception *exception ) +{ + mediacontrol_Instance* retval; + libvlc_exception_t ex; + + libvlc_exception_init( &ex ); + mediacontrol_exception_init( exception ); + + retval = ( mediacontrol_Instance* )malloc( sizeof( mediacontrol_Instance ) ); + if( !retval ) + RAISE_NULL( mediacontrol_InternalException, "Out of memory" ); + + retval->p_instance = libvlc_new( argc, (const char**)argv, &ex ); + HANDLE_LIBVLC_EXCEPTION_NULL( &ex ); + retval->p_media_player = libvlc_media_player_new( retval->p_instance, &ex ); + HANDLE_LIBVLC_EXCEPTION_NULL( &ex ); + return retval; +} + +void +mediacontrol_exit( mediacontrol_Instance *self ) +{ + libvlc_release( self->p_instance ); +} + +libvlc_instance_t* +mediacontrol_get_libvlc_instance( mediacontrol_Instance *self ) +{ + return self->p_instance; +} + +libvlc_media_player_t* +mediacontrol_get_media_player( mediacontrol_Instance *self ) +{ + return self->p_media_player; +} + +mediacontrol_Instance * +mediacontrol_new_from_instance( libvlc_instance_t* p_instance, + mediacontrol_Exception *exception ) +{ + mediacontrol_Instance* retval; + libvlc_exception_t ex; + + libvlc_exception_init( &ex ); + + retval = ( mediacontrol_Instance* )malloc( sizeof( mediacontrol_Instance ) ); + if( ! retval ) + { + RAISE_NULL( mediacontrol_InternalException, "Out of memory" ); + } + retval->p_instance = p_instance; + retval->p_media_player = libvlc_media_player_new( retval->p_instance, &ex ); + HANDLE_LIBVLC_EXCEPTION_NULL( &ex ); + return retval; +} + +/************************************************************************** + * Playback management + **************************************************************************/ +mediacontrol_Position* +mediacontrol_get_media_position( mediacontrol_Instance *self, + const mediacontrol_PositionOrigin an_origin, + const mediacontrol_PositionKey a_key, + mediacontrol_Exception *exception ) +{ + mediacontrol_Position* retval = NULL; + libvlc_exception_t ex; + int64_t pos; + + mediacontrol_exception_init( exception ); + libvlc_exception_init( &ex ); + + retval = ( mediacontrol_Position* )malloc( sizeof( mediacontrol_Position ) ); + retval->origin = an_origin; + retval->key = a_key; + + if( an_origin != mediacontrol_AbsolutePosition ) + { + free( retval ); + /* Relative or ModuloPosition make no sense */ + RAISE_NULL( mediacontrol_PositionOriginNotSupported, + "Only absolute position is valid." ); + } + + /* We are asked for an AbsolutePosition. */ + pos = libvlc_media_player_get_time( self->p_media_player, &ex ); + + if( a_key == mediacontrol_MediaTime ) + { + retval->value = pos; + } + else + { + retval->value = private_mediacontrol_unit_convert( self->p_media_player, + mediacontrol_MediaTime, + a_key, + pos ); + } + return retval; +} + +/* Sets the media position */ +void +mediacontrol_set_media_position( mediacontrol_Instance *self, + const mediacontrol_Position * a_position, + mediacontrol_Exception *exception ) +{ + libvlc_exception_t ex; + int64_t i_pos; + + libvlc_exception_init( &ex ); + mediacontrol_exception_init( exception ); + + i_pos = private_mediacontrol_position2microsecond( self->p_media_player, a_position ); + libvlc_media_player_set_time( self->p_media_player, i_pos / 1000, &ex ); + HANDLE_LIBVLC_EXCEPTION_VOID( &ex ); +} + +/* Starts playing a stream */ +/* + * Known issues: since moving in the playlist using playlist_Next + * or playlist_Prev implies starting to play items, the a_position + * argument will be only honored for the 1st item in the list. + * + * XXX:FIXME split moving in the playlist and playing items two + * different actions or make playlist_ accept a time + * value to start to play from. + */ +void +mediacontrol_start( mediacontrol_Instance *self, + const mediacontrol_Position * a_position, + mediacontrol_Exception *exception ) +{ + libvlc_media_t * p_media; + char * psz_name; + libvlc_exception_t ex; + + mediacontrol_exception_init( exception ); + libvlc_exception_init( &ex ); + + p_media = libvlc_media_player_get_media( self->p_media_player, &ex ); + HANDLE_LIBVLC_EXCEPTION_VOID( &ex ); + + if ( ! p_media ) + { + /* No media was defined. */ + RAISE( mediacontrol_PlaylistException, "No defined media." ); + } + else + { + /* A media was defined. Get its mrl to reuse it, but reset the options + (because start-time may have been set on the previous invocation */ + psz_name = libvlc_media_get_mrl( p_media, &ex ); + HANDLE_LIBVLC_EXCEPTION_VOID( &ex ); + + /* Create a new media */ + p_media = libvlc_media_new( self->p_instance, psz_name, &ex ); + HANDLE_LIBVLC_EXCEPTION_VOID( &ex ); + + if( a_position->value ) + { + char * psz_from; + libvlc_time_t i_from; + + /* A start position was specified. Add it to media options */ + psz_from = ( char * )malloc( 20 * sizeof( char ) ); + i_from = private_mediacontrol_position2microsecond( self->p_media_player, a_position ) / 1000000; + snprintf( psz_from, 20, "start-time=%"PRId64, i_from ); + libvlc_media_add_option( p_media, psz_from, &ex ); + HANDLE_LIBVLC_EXCEPTION_VOID( &ex ); + } + + libvlc_media_player_set_media( self->p_media_player, p_media, &ex ); + HANDLE_LIBVLC_EXCEPTION_VOID( &ex ); + + libvlc_media_player_play( self->p_media_player, &ex ); + HANDLE_LIBVLC_EXCEPTION_VOID( &ex ); + } +} + +void +mediacontrol_pause( mediacontrol_Instance *self, + mediacontrol_Exception *exception ) +{ + libvlc_exception_t ex; + + mediacontrol_exception_init( exception ); + libvlc_exception_init( &ex ); + libvlc_media_player_pause( self->p_media_player, &ex ); + HANDLE_LIBVLC_EXCEPTION_VOID( &ex ); +} + +void +mediacontrol_resume( mediacontrol_Instance *self, + mediacontrol_Exception *exception ) +{ + libvlc_exception_t ex; + + mediacontrol_exception_init( exception ); + libvlc_exception_init( &ex ); + libvlc_media_player_pause( self->p_media_player, &ex ); + HANDLE_LIBVLC_EXCEPTION_VOID( &ex ); +} + +void +mediacontrol_stop( mediacontrol_Instance *self, + mediacontrol_Exception *exception ) +{ + libvlc_exception_t ex; + + mediacontrol_exception_init( exception ); + libvlc_exception_init( &ex ); + libvlc_media_player_stop( self->p_media_player, &ex ); + HANDLE_LIBVLC_EXCEPTION_VOID( &ex ); +} + +/************************************************************************** + * File management + **************************************************************************/ + +void +mediacontrol_set_mrl( mediacontrol_Instance *self, + const char * psz_file, + mediacontrol_Exception *exception ) +{ + libvlc_media_t * p_media; + libvlc_exception_t ex; + + mediacontrol_exception_init( exception ); + libvlc_exception_init( &ex ); + + p_media = libvlc_media_new( self->p_instance, psz_file, &ex ); + HANDLE_LIBVLC_EXCEPTION_VOID( &ex ); + + libvlc_media_player_set_media( self->p_media_player, p_media, &ex ); + HANDLE_LIBVLC_EXCEPTION_VOID( &ex ); +} + +char * +mediacontrol_get_mrl( mediacontrol_Instance *self, + mediacontrol_Exception *exception ) +{ + libvlc_media_t * p_media; + libvlc_exception_t ex; + + mediacontrol_exception_init( exception ); + libvlc_exception_init( &ex ); + + p_media = libvlc_media_player_get_media( self->p_media_player, &ex ); + HANDLE_LIBVLC_EXCEPTION_NULL( &ex ); + + if ( ! p_media ) + { + return strdup( "" ); + } + else + { + char * psz_mrl; + + psz_mrl = libvlc_media_get_mrl( p_media, &ex ); + HANDLE_LIBVLC_EXCEPTION_NULL( &ex ); + return psz_mrl; + } +} + +/*************************************************************************** + * Status feedback + ***************************************************************************/ + +mediacontrol_StreamInformation * +mediacontrol_get_stream_information( mediacontrol_Instance *self, + mediacontrol_PositionKey a_key, + mediacontrol_Exception *exception ) +{ + (void)a_key; + mediacontrol_StreamInformation *retval = NULL; + libvlc_media_t * p_media; + libvlc_exception_t ex; + + libvlc_exception_init( &ex ); + + retval = ( mediacontrol_StreamInformation* ) + malloc( sizeof( mediacontrol_StreamInformation ) ); + if( ! retval ) + { + RAISE( mediacontrol_InternalException, "Out of memory" ); + return NULL; + } + + p_media = libvlc_media_player_get_media( self->p_media_player, &ex ); + HANDLE_LIBVLC_EXCEPTION_NULL( &ex ); + if( ! p_media ) + { + /* No p_media defined */ + retval->streamstatus = mediacontrol_UndefinedStatus; + retval->url = strdup( "" ); + retval->position = 0; + retval->length = 0; + } + else + { + libvlc_state_t state; + state = libvlc_media_player_get_state( self->p_media_player, &ex ); + HANDLE_LIBVLC_EXCEPTION_NULL( &ex ); + switch( state ) + { + case libvlc_NothingSpecial: + retval->streamstatus = mediacontrol_UndefinedStatus; + break; + case libvlc_Opening : + retval->streamstatus = mediacontrol_InitStatus; + break; + case libvlc_Buffering: + retval->streamstatus = mediacontrol_BufferingStatus; + break; + case libvlc_Playing: + retval->streamstatus = mediacontrol_PlayingStatus; + break; + case libvlc_Paused: + retval->streamstatus = mediacontrol_PauseStatus; + break; + case libvlc_Stopped: + retval->streamstatus = mediacontrol_StopStatus; + break; + case libvlc_Forward: + retval->streamstatus = mediacontrol_ForwardStatus; + break; + case libvlc_Backward: + retval->streamstatus = mediacontrol_BackwardStatus; + break; + case libvlc_Ended: + retval->streamstatus = mediacontrol_EndStatus; + break; + case libvlc_Error: + retval->streamstatus = mediacontrol_ErrorStatus; + break; + default : + retval->streamstatus = mediacontrol_UndefinedStatus; + break; + } + + retval->url = libvlc_media_get_mrl( p_media, &ex ); + + retval->position = libvlc_media_player_get_time( self->p_media_player, &ex ); + if( libvlc_exception_raised( &ex ) ) + { + libvlc_exception_clear( &ex ); + retval->position = 0; + } + + retval->length = libvlc_media_player_get_length( self->p_media_player, &ex ); + if( libvlc_exception_raised( &ex ) ) + { + libvlc_exception_clear( &ex ); + retval->length = 0; + } + + } + return retval; +} diff --git a/VLC/mediacontrol_internal.h b/VLC/mediacontrol_internal.h new file mode 100644 index 0000000..6ca9a43 --- /dev/null +++ b/VLC/mediacontrol_internal.h @@ -0,0 +1,83 @@ +/***************************************************************************** + * control.h: private header for mediacontrol + ***************************************************************************** + * Copyright (C) 2005 the VideoLAN team + * $Id$ + * + * Authors: Olivier Aubert + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef _VLC_MEDIACONTROL_INTERNAL_H +#define _VLC_MEDIACONTROL_INTERNAL_H 1 + +# ifdef __cplusplus +extern "C" { +# endif + +#include +#include +#include +#include + +struct mediacontrol_Instance { + libvlc_instance_t * p_instance; + libvlc_media_player_t * p_media_player; +}; + +libvlc_time_t private_mediacontrol_unit_convert( libvlc_media_player_t *p_media_player, + mediacontrol_PositionKey from, + mediacontrol_PositionKey to, + int64_t value ); +libvlc_time_t private_mediacontrol_position2microsecond( libvlc_media_player_t *p_media_player, + const mediacontrol_Position *pos ); + +/** + * Allocate a RGBPicture structure. + * \param datasize: the size of the data + */ +mediacontrol_RGBPicture *private_mediacontrol_RGBPicture__alloc( int datasize ); + +mediacontrol_RGBPicture *private_mediacontrol_createRGBPicture( int, int, long, int64_t l_date, char *, int); + + +#define RAISE( c, m ) if( exception ) { exception->code = c; \ + exception->message = strdup(m); } + +#define RAISE_NULL( c, m ) { RAISE( c, m ); return NULL; } +#define RAISE_VOID( c, m ) { RAISE( c, m ); return; } + +#define HANDLE_LIBVLC_EXCEPTION_VOID( e ) if( libvlc_exception_raised( e ) ) { \ + RAISE( mediacontrol_InternalException, libvlc_exception_get_message( e )); \ + libvlc_exception_clear( e ); \ + return; } + +#define HANDLE_LIBVLC_EXCEPTION_NULL( e ) if( libvlc_exception_raised( e ) ) { \ + RAISE( mediacontrol_InternalException, libvlc_exception_get_message( e )); \ + libvlc_exception_clear( e ); \ + return NULL; } + +#define HANDLE_LIBVLC_EXCEPTION_ZERO( e ) if( libvlc_exception_raised( e ) ) { \ + RAISE( mediacontrol_InternalException, libvlc_exception_get_message( e )); \ + libvlc_exception_clear( e ); \ + return 0; } + + +# ifdef __cplusplus +} +# endif + +#endif diff --git a/VLC/mediacontrol_util.c b/VLC/mediacontrol_util.c new file mode 100644 index 0000000..e86646f --- /dev/null +++ b/VLC/mediacontrol_util.c @@ -0,0 +1,265 @@ +/***************************************************************************** + * util.c: Utility functions and exceptions management + ***************************************************************************** + * Copyright (C) 2005 the VideoLAN team + * $Id$ + * + * Authors: Olivier Aubert + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "mediacontrol_internal.h" +#include + +#include +#include +#include + +#include /* malloc(), free() */ +#include + +#include /* ENOMEM */ +#include +#include + +#ifdef HAVE_UNISTD_H +# include +#endif +#ifdef HAVE_SYS_TIME_H +# include +#endif +#ifdef HAVE_SYS_TYPES_H +# include +#endif + +libvlc_time_t private_mediacontrol_unit_convert( libvlc_media_player_t *p_media_player, + mediacontrol_PositionKey from, + mediacontrol_PositionKey to, + int64_t value ) +{ + if( to == from ) + return value; + + if( !p_media_player ) + return 0; + + switch( from ) + { + case mediacontrol_MediaTime: + if( to == mediacontrol_ByteCount ) + { + /* FIXME Unsupported */ + /* vlc < 0.8 API */ + /* return value * 50 * p_input->stream.i_mux_rate / 1000; */ + return 0; + } + if( to == mediacontrol_SampleCount ) + { + double f_fps; + libvlc_exception_t ex; + libvlc_exception_init( &ex ); + + f_fps = libvlc_media_player_get_rate( p_media_player, &ex ); + if( f_fps < 0 ) + return 0; + else + return( value * f_fps / 1000.0 ); + } + /* Cannot happen */ + /* See http://catb.org/~esr/jargon/html/entry/can-t-happen.html */ + break; + + case mediacontrol_SampleCount: + { + double f_fps; + libvlc_exception_t ex; + libvlc_exception_init( &ex ); + + f_fps = libvlc_media_player_get_rate( p_media_player, &ex ); + if( f_fps < 0 ) + return 0; + + if( to == mediacontrol_ByteCount ) + { + /* FIXME */ + /* vlc < 0.8 API */ +/* return ( int64_t )( value * 50 * p_input->stream.i_mux_rate / f_fps ); */ + return 0; + } + + if( to == mediacontrol_MediaTime ) + return( int64_t )( value * 1000.0 / ( double )f_fps ); + + /* Cannot happen */ + break; + } + case mediacontrol_ByteCount: + /* FIXME */ + return 0; + } + /* Cannot happen */ + return 0; +} + +/* Converts a mediacontrol_Position into a time in microseconds in + movie clock time */ +libvlc_time_t +private_mediacontrol_position2microsecond( libvlc_media_player_t * p_media_player, + const mediacontrol_Position * pos ) +{ + switch( pos->origin ) + { + case mediacontrol_AbsolutePosition: + return ( 1000 * private_mediacontrol_unit_convert( p_media_player, + pos->key, /* from */ + mediacontrol_MediaTime, /* to */ + pos->value ) ); + break; + case mediacontrol_RelativePosition: + { + libvlc_time_t l_time = 0; + libvlc_time_t l_pos = 0; + libvlc_exception_t ex; + libvlc_exception_init( &ex ); + + l_time = libvlc_media_player_get_time( p_media_player, &ex ); + /* Ignore exception, we will assume a 0 time value */ + + l_pos = private_mediacontrol_unit_convert( p_media_player, + pos->key, + mediacontrol_MediaTime, + pos->value ); + return 1000 * ( l_time + l_pos ); + break; + } + case mediacontrol_ModuloPosition: + { + libvlc_time_t l_time = 0; + libvlc_time_t l_length = 0; + libvlc_time_t l_pos = 0; + libvlc_exception_t ex; + libvlc_exception_init( &ex ); + + l_length = libvlc_media_player_get_length( p_media_player, &ex ); + if( l_length <= 0 ) + return 0; + + l_time = libvlc_media_player_get_time( p_media_player, &ex ); + /* Ignore exception, we will assume a 0 time value */ + + l_pos = private_mediacontrol_unit_convert( p_media_player, + pos->key, + mediacontrol_MediaTime, + pos->value ); + + return 1000 * ( ( l_time + l_pos ) % l_length ); + break; + } + } + return 0; +} + +mediacontrol_RGBPicture* +private_mediacontrol_RGBPicture__alloc( int datasize ) +{ + mediacontrol_RGBPicture* pic; + + pic = ( mediacontrol_RGBPicture * )malloc( sizeof( mediacontrol_RGBPicture ) ); + if( ! pic ) + return NULL; + + pic->size = datasize; + pic->data = ( char* )malloc( datasize * sizeof( char ) ); + return pic; +} + +void +mediacontrol_RGBPicture__free( mediacontrol_RGBPicture* pic ) +{ + if( pic ) + { + free( pic->data ); + free( pic ); + } +} + +void +mediacontrol_StreamInformation__free( mediacontrol_StreamInformation* p_si ) +{ + if( p_si ) + { + free( p_si->url ); + free( p_si ); + } +} + + +mediacontrol_Exception* +mediacontrol_exception_create( void ) +{ + mediacontrol_Exception* exception; + + exception = ( mediacontrol_Exception* )malloc( sizeof( mediacontrol_Exception ) ); + mediacontrol_exception_init( exception ); + return exception; +} + +void +mediacontrol_exception_init( mediacontrol_Exception *exception ) +{ + if( exception ) + { + exception->code = 0; + exception->message = NULL; + } +} + +void +mediacontrol_exception_cleanup( mediacontrol_Exception *exception ) +{ + if( exception ) + free( exception->message ); +} + +void +mediacontrol_exception_free( mediacontrol_Exception *exception ) +{ + mediacontrol_exception_cleanup( exception ); + free( exception ); +} + +mediacontrol_RGBPicture* +private_mediacontrol_createRGBPicture( int i_width, int i_height, long i_chroma, int64_t l_date, + char* p_data, int i_datasize ) +{ + mediacontrol_RGBPicture *retval; + + retval = private_mediacontrol_RGBPicture__alloc( i_datasize ); + if( retval ) + { + retval->width = i_width; + retval->height = i_height; + retval->type = i_chroma; + retval->date = l_date; + retval->size = i_datasize; + memcpy( retval->data, p_data, i_datasize ); + } + return retval; +} diff --git a/VLC/messages.c b/VLC/messages.c new file mode 100644 index 0000000..09f1504 --- /dev/null +++ b/VLC/messages.c @@ -0,0 +1,675 @@ +/***************************************************************************** + * messages.c: messages interface + * This library provides an interface to the message queue to be used by other + * modules, especially intf modules. See vlc_config.h for output configuration. + ***************************************************************************** + * Copyright (C) 1998-2005 the VideoLAN team + * $Id$ + * + * Authors: Vincent Seguin + * Samuel Hocevar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#include /* va_list for BSD */ + +#ifdef HAVE_FCNTL_H +# include /* O_CREAT, O_TRUNC, O_WRONLY, O_SYNC */ +#endif + +#include /* errno */ + +#ifdef WIN32 +# include /* 'net_strerror' and 'WSAGetLastError' */ +#endif + +#ifdef HAVE_UNISTD_H +# include /* close(), write() */ +#endif + +#include + +#include "vlc_charset.h" +#include "libvlc.h" + +/***************************************************************************** + * Local macros + *****************************************************************************/ +#if defined(HAVE_VA_COPY) +# define vlc_va_copy(dest,src) va_copy(dest,src) +#elif defined(HAVE___VA_COPY) +# define vlc_va_copy(dest,src) __va_copy(dest,src) +#else +# define vlc_va_copy(dest,src) (dest)=(src) +#endif + +#define QUEUE priv->msg_bank.queue +#define LOCK_BANK vlc_mutex_lock( &priv->msg_bank.lock ); +#define UNLOCK_BANK vlc_mutex_unlock( &priv->msg_bank.lock ); + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +static void QueueMsg ( vlc_object_t *, int, const char *, + const char *, va_list ); +static void FlushMsg ( msg_queue_t * ); +static void PrintMsg ( vlc_object_t *, msg_item_t * ); + +/** + * Initialize messages queues + * This function initializes all message queues + */ +void msg_Create (libvlc_int_t *p_libvlc) +{ + libvlc_priv_t *priv = libvlc_priv (p_libvlc); + vlc_mutex_init( &priv->msg_bank.lock ); + vlc_mutex_init( &QUEUE.lock ); + QUEUE.b_overflow = false; + QUEUE.i_start = 0; + QUEUE.i_stop = 0; + QUEUE.i_sub = 0; + QUEUE.pp_sub = 0; + +#ifdef UNDER_CE + QUEUE.logfile = + CreateFile( L"vlc-log.txt", GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, + CREATE_ALWAYS, 0, NULL ); + SetFilePointer( QUEUE.logfile, 0, NULL, FILE_END ); +#endif +} + +/** + * Flush all message queues + */ +void msg_Flush (libvlc_int_t *p_libvlc) +{ + libvlc_priv_t *priv = libvlc_priv (p_libvlc); + vlc_mutex_lock( &QUEUE.lock ); + FlushMsg( &QUEUE ); + vlc_mutex_unlock( &QUEUE.lock ); +} + +/** + * Destroy the message queues + * + * This functions prints all messages remaining in the queues, + * then frees all the allocated ressources + * No other messages interface functions should be called after this one. + */ +void msg_Destroy (libvlc_int_t *p_libvlc) +{ + libvlc_priv_t *priv = libvlc_priv (p_libvlc); + + if( QUEUE.i_sub ) + msg_Err( p_libvlc, "stale interface subscribers" ); + + FlushMsg( &QUEUE ); + +#ifdef UNDER_CE + CloseHandle( QUEUE.logfile ); +#endif + /* Destroy lock */ + vlc_mutex_destroy( &QUEUE.lock ); + vlc_mutex_destroy( &priv->msg_bank.lock); +} + +/** + * Subscribe to a message queue. + */ +msg_subscription_t *__msg_Subscribe( vlc_object_t *p_this ) +{ + libvlc_priv_t *priv = libvlc_priv (p_this->p_libvlc); + msg_subscription_t *p_sub = malloc( sizeof( msg_subscription_t ) ); + + if (p_sub == NULL) + return NULL; + + LOCK_BANK; + vlc_mutex_lock( &QUEUE.lock ); + + TAB_APPEND( QUEUE.i_sub, QUEUE.pp_sub, p_sub ); + + p_sub->i_start = QUEUE.i_start; + p_sub->pi_stop = &QUEUE.i_stop; + p_sub->p_msg = QUEUE.msg; + p_sub->p_lock = &QUEUE.lock; + + vlc_mutex_unlock( &QUEUE.lock ); + UNLOCK_BANK; + + return p_sub; +} + +/** + * Unsubscribe from a message queue. + */ +void __msg_Unsubscribe( vlc_object_t *p_this, msg_subscription_t *p_sub ) +{ + libvlc_priv_t *priv = libvlc_priv (p_this->p_libvlc); + + LOCK_BANK; + vlc_mutex_lock( &QUEUE.lock ); + for( int j = 0 ; j< QUEUE.i_sub ; j++ ) + { + if( QUEUE.pp_sub[j] == p_sub ) + { + REMOVE_ELEM( QUEUE.pp_sub, QUEUE.i_sub, j ); + free( p_sub ); + } + } + vlc_mutex_unlock( &QUEUE.lock ); + UNLOCK_BANK; +} + +/***************************************************************************** + * __msg_*: print a message + ***************************************************************************** + * These functions queue a message for later printing. + *****************************************************************************/ +void __msg_Generic( vlc_object_t *p_this, int i_type, const char *psz_module, + const char *psz_format, ... ) +{ + va_list args; + + va_start( args, psz_format ); + QueueMsg( p_this, i_type, psz_module, psz_format, args ); + va_end( args ); +} + +void __msg_GenericVa( vlc_object_t *p_this, int i_type, const char *psz_module, + const char *psz_format, va_list args ) +{ + QueueMsg( p_this, i_type, psz_module, psz_format, args ); +} + +/* Generic functions used when variadic macros are not available. */ +#define DECLARE_MSG_FN( FN_NAME, FN_TYPE ) \ + void FN_NAME( vlc_object_t *p_this, const char *psz_format, ... ) \ + { \ + va_list args; \ + va_start( args, psz_format ); \ + QueueMsg( p_this, FN_TYPE, "unknown", psz_format, args ); \ + va_end( args ); \ + } \ + struct _ +/** + * Output an informational message. + * \note Do not use this for debug messages + * \see input_AddInfo + */ +DECLARE_MSG_FN( __msg_Info, VLC_MSG_INFO ); +/** + * Output an error message. + */ +DECLARE_MSG_FN( __msg_Err, VLC_MSG_ERR ); +/** + * Output a waring message + */ +DECLARE_MSG_FN( __msg_Warn, VLC_MSG_WARN ); +/** + * Output a debug message + */ +DECLARE_MSG_FN( __msg_Dbg, VLC_MSG_DBG ); + +/** + * Add a message to a queue + * + * This function provides basic functionnalities to other msg_* functions. + * It adds a message to a queue (after having printed all stored messages if it + * is full). If the message can't be converted to string in memory, it issues + * a warning. + */ +static void QueueMsg( vlc_object_t *p_this, int i_type, const char *psz_module, + const char *psz_format, va_list _args ) +{ + assert (p_this); + libvlc_priv_t *priv = libvlc_priv (p_this->p_libvlc); + int i_header_size; /* Size of the additionnal header */ + vlc_object_t *p_obj; + char * psz_str = NULL; /* formatted message string */ + char * psz_header = NULL; + va_list args; + msg_item_t * p_item = NULL; /* pointer to message */ + msg_item_t item; /* message in case of a full queue */ + msg_queue_t *p_queue; + +#if !defined(HAVE_VASPRINTF) || defined(__APPLE__) || defined(SYS_BEOS) + int i_size = strlen(psz_format) + INTF_MAX_MSG_SIZE; +#endif + + if( p_this->i_flags & OBJECT_FLAGS_QUIET || + (p_this->i_flags & OBJECT_FLAGS_NODBG && i_type == VLC_MSG_DBG) ) + return; + +#ifndef __GLIBC__ + /* Expand %m to strerror(errno) - only once */ + char buf[strlen( psz_format ) + 2001], *ptr; + strcpy( buf, psz_format ); + ptr = (char*)buf; + psz_format = (const char*) buf; + + for( ;; ) + { + ptr = strchr( ptr, '%' ); + if( ptr == NULL ) + break; + + if( ptr[1] == 'm' ) + { + char errbuf[2001]; + size_t errlen; + +#ifndef WIN32 + strerror_r( errno, errbuf, 1001 ); +#else + int sockerr = WSAGetLastError( ); + if( sockerr ) + { + strncpy( errbuf, net_strerror( sockerr ), 1001 ); + WSASetLastError( sockerr ); + } + if ((sockerr == 0) + || (strcmp ("Unknown network stack error", errbuf) == 0)) + strncpy( errbuf, strerror( errno ), 1001 ); +#endif + errbuf[1000] = 0; + + /* Escape '%' from the error string */ + for( char *percent = strchr( errbuf, '%' ); + percent != NULL; + percent = strchr( percent + 2, '%' ) ) + { + memmove( percent + 1, percent, strlen( percent ) + 1 ); + } + + errlen = strlen( errbuf ); + memmove( ptr + errlen, ptr + 2, strlen( ptr + 2 ) + 1 ); + memcpy( ptr, errbuf, errlen ); + break; /* Only once, so we don't overflow */ + } + + /* Looks for conversion specifier... */ + do + ptr++; + while( *ptr && ( strchr( "diouxXeEfFgGaAcspn%", *ptr ) == NULL ) ); + if( *ptr ) + ptr++; /* ...and skip it */ + } +#endif + + /* Convert message to string */ +#if defined(HAVE_VASPRINTF) && !defined(__APPLE__) && !defined( SYS_BEOS ) + vlc_va_copy( args, _args ); + if( vasprintf( &psz_str, psz_format, args ) == -1 ) + psz_str = NULL; + va_end( args ); +#else + psz_str = (char*) malloc( i_size ); +#endif + + if( psz_str == NULL ) + { +#ifdef __GLIBC__ + fprintf( stderr, "main warning: can't store message (%m): " ); +#else + char psz_err[1001]; +#ifndef WIN32 + /* we're not using GLIBC, so we are sure that the error description + * will be stored in the buffer we provide to strerror_r() */ + strerror_r( errno, psz_err, 1001 ); +#else + strncpy( psz_err, strerror( errno ), 1001 ); +#endif + psz_err[1000] = '\0'; + fprintf( stderr, "main warning: can't store message (%s): ", psz_err ); +#endif + vlc_va_copy( args, _args ); + /* We should use utf8_vfprintf - but it calls malloc()... */ + vfprintf( stderr, psz_format, args ); + va_end( args ); + fputs( "\n", stderr ); + return; + } + + i_header_size = 0; + p_obj = p_this; + while( p_obj != NULL ) + { + char *psz_old = NULL; + if( p_obj->psz_header ) + { + i_header_size += strlen( p_obj->psz_header ) + 4; + if( psz_header ) + { + psz_old = strdup( psz_header ); + psz_header = (char*)realloc( psz_header, i_header_size ); + snprintf( psz_header, i_header_size , "[%s] %s", + p_obj->psz_header, psz_old ); + } + else + { + psz_header = (char *)malloc( i_header_size ); + snprintf( psz_header, i_header_size, "[%s]", + p_obj->psz_header ); + } + } + free( psz_old ); + p_obj = p_obj->p_parent; + } + +#if !defined(HAVE_VASPRINTF) || defined(__APPLE__) || defined(SYS_BEOS) + vlc_va_copy( args, _args ); + vsnprintf( psz_str, i_size, psz_format, args ); + va_end( args ); + psz_str[ i_size - 1 ] = 0; /* Just in case */ +#endif + + LOCK_BANK; + p_queue = &QUEUE; + vlc_mutex_lock( &p_queue->lock ); + + /* Check there is room in the queue for our message */ + if( p_queue->b_overflow ) + { + FlushMsg( p_queue ); + + if( ((p_queue->i_stop - p_queue->i_start + 1) % VLC_MSG_QSIZE) == 0 ) + { + /* Still in overflow mode, print from a dummy item */ + p_item = &item; + } + else + { + /* Pheeew, at last, there is room in the queue! */ + p_queue->b_overflow = false; + } + } + else if( ((p_queue->i_stop - p_queue->i_start + 2) % VLC_MSG_QSIZE) == 0 ) + { + FlushMsg( p_queue ); + + if( ((p_queue->i_stop - p_queue->i_start + 2) % VLC_MSG_QSIZE) == 0 ) + { + p_queue->b_overflow = true; + + /* Put the overflow message in the queue */ + p_item = p_queue->msg + p_queue->i_stop; + p_queue->i_stop = (p_queue->i_stop + 1) % VLC_MSG_QSIZE; + + p_item->i_type = VLC_MSG_WARN; + p_item->i_object_id = p_this->i_object_id; + p_item->psz_object_type = p_this->psz_object_type; + p_item->psz_module = strdup( "message" ); + p_item->psz_msg = strdup( "message queue overflowed" ); + p_item->psz_header = NULL; + + PrintMsg( p_this, p_item ); + /* We print from a dummy item */ + p_item = &item; + } + } + + if( !p_queue->b_overflow ) + { + /* Put the message in the queue */ + p_item = p_queue->msg + p_queue->i_stop; + p_queue->i_stop = (p_queue->i_stop + 1) % VLC_MSG_QSIZE; + } + + /* Fill message information fields */ + p_item->i_type = i_type; + p_item->i_object_id = p_this->i_object_id; + p_item->psz_object_type = p_this->psz_object_type; + p_item->psz_module = strdup( psz_module ); + p_item->psz_msg = psz_str; + p_item->psz_header = psz_header; + + PrintMsg( p_this, p_item ); + + if( p_queue->b_overflow ) + { + free( p_item->psz_module ); + free( p_item->psz_msg ); + free( p_item->psz_header ); + } + + vlc_mutex_unlock ( &p_queue->lock ); + UNLOCK_BANK; +} + +/* following functions are local */ + +/***************************************************************************** + * FlushMsg + ***************************************************************************** + * Print all messages remaining in queue. MESSAGE QUEUE MUST BE LOCKED, since + * this function does not check the lock. + *****************************************************************************/ +static void FlushMsg ( msg_queue_t *p_queue ) +{ + int i_index, i_start, i_stop; + + /* Get the maximum message index that can be freed */ + i_stop = p_queue->i_stop; + + /* Check until which value we can free messages */ + for( i_index = 0; i_index < p_queue->i_sub; i_index++ ) + { + i_start = p_queue->pp_sub[ i_index ]->i_start; + + /* If this subscriber is late, we don't free messages before + * his i_start value, otherwise he'll miss messages */ + if( ( i_start < i_stop + && (p_queue->i_stop <= i_start || i_stop <= p_queue->i_stop) ) + || ( i_stop < i_start + && (i_stop <= p_queue->i_stop && p_queue->i_stop <= i_start) ) ) + { + i_stop = i_start; + } + } + + /* Free message data */ + for( i_index = p_queue->i_start; + i_index != i_stop; + i_index = (i_index+1) % VLC_MSG_QSIZE ) + { + free( p_queue->msg[i_index].psz_msg ); + free( p_queue->msg[i_index].psz_module ); + free( p_queue->msg[i_index].psz_header ); + } + + /* Update the new start value */ + p_queue->i_start = i_index; +} + +/***************************************************************************** + * PrintMsg: output a standard message item to stderr + ***************************************************************************** + * Print a message to stderr, with colour formatting if needed. + *****************************************************************************/ +static void PrintMsg ( vlc_object_t * p_this, msg_item_t * p_item ) +{ +# define COL(x) "\033[" #x ";1m" +# define RED COL(31) +# define GREEN COL(32) +# define YELLOW COL(33) +# define WHITE COL(0) +# define GRAY "\033[0m" + +#ifdef UNDER_CE + int i_dummy; +#endif + static const char ppsz_type[4][9] = { "", " error", " warning", " debug" }; + static const char ppsz_color[4][8] = { WHITE, RED, YELLOW, GRAY }; + const char *psz_object; + libvlc_priv_t *priv = libvlc_priv (p_this->p_libvlc); + int i_type = p_item->i_type; + + switch( i_type ) + { + case VLC_MSG_ERR: + if( priv->i_verbose < 0 ) return; + break; + case VLC_MSG_INFO: + if( priv->i_verbose < 0 ) return; + break; + case VLC_MSG_WARN: + if( priv->i_verbose < 1 ) return; + break; + case VLC_MSG_DBG: + if( priv->i_verbose < 2 ) return; + break; + } + + psz_object = p_item->psz_object_type; + +#ifdef UNDER_CE +# define CE_WRITE(str) WriteFile( QUEUE.logfile, \ + str, strlen(str), &i_dummy, NULL ); + CE_WRITE( p_item->psz_module ); + CE_WRITE( " " ); + CE_WRITE( psz_object ); + CE_WRITE( ppsz_type[i_type] ); + CE_WRITE( ": " ); + CE_WRITE( p_item->psz_msg ); + CE_WRITE( "\r\n" ); + FlushFileBuffers( QUEUE.logfile ); + +#else + /* Send the message to stderr */ + if( priv->b_color ) + { + if( p_item->psz_header ) + { + utf8_fprintf( stderr, "[" GREEN "%.8i" GRAY "] %s %s %s%s: %s%s" GRAY + "\n", + p_item->i_object_id, p_item->psz_header, + p_item->psz_module, psz_object, + ppsz_type[i_type], ppsz_color[i_type], + p_item->psz_msg ); + } + else + { + utf8_fprintf( stderr, "[" GREEN "%.8i" GRAY "] %s %s%s: %s%s" GRAY "\n", + p_item->i_object_id, p_item->psz_module, psz_object, + ppsz_type[i_type], ppsz_color[i_type], + p_item->psz_msg ); + } + } + else + { + if( p_item->psz_header ) + { + utf8_fprintf( stderr, "[%.8i] %s %s %s%s: %s\n", p_item->i_object_id, + p_item->psz_header, p_item->psz_module, + psz_object, ppsz_type[i_type], p_item->psz_msg ); + } + else + { + utf8_fprintf( stderr, "[%.8i] %s %s%s: %s\n", p_item->i_object_id, + p_item->psz_module, psz_object, ppsz_type[i_type], + p_item->psz_msg ); + } + } + +# if defined(WIN32) + fflush( stderr ); +# endif +#endif +} + +static msg_context_t* GetContext(void) +{ + msg_context_t *p_ctx = vlc_threadvar_get( &msg_context_global_key ); + if( p_ctx == NULL ) + { + MALLOC_NULL( p_ctx, msg_context_t ); + p_ctx->psz_message = NULL; + vlc_threadvar_set( &msg_context_global_key, p_ctx ); + } + return p_ctx; +} + +void msg_StackDestroy (void *data) +{ + msg_context_t *p_ctx = data; + + free (p_ctx->psz_message); + free (p_ctx); +} + +void msg_StackSet( int i_code, const char *psz_message, ... ) +{ + va_list ap; + msg_context_t *p_ctx = GetContext(); + + if( p_ctx == NULL ) + return; + free( p_ctx->psz_message ); + + va_start( ap, psz_message ); + if( vasprintf( &p_ctx->psz_message, psz_message, ap ) == -1 ) + p_ctx->psz_message = NULL; + va_end( ap ); + + p_ctx->i_code = i_code; +} + +void msg_StackAdd( const char *psz_message, ... ) +{ + char *psz_tmp; + va_list ap; + msg_context_t *p_ctx = GetContext(); + + if( p_ctx == NULL ) + return; + + va_start( ap, psz_message ); + if( vasprintf( &psz_tmp, psz_message, ap ) == -1 ) + psz_tmp = NULL; + va_end( ap ); + + if( !p_ctx->psz_message ) + p_ctx->psz_message = psz_tmp; + else + { + char *psz_new; + if( asprintf( &psz_new, "%s: %s", psz_tmp, p_ctx->psz_message ) == -1 ) + psz_new = NULL; + + free( p_ctx->psz_message ); + p_ctx->psz_message = psz_new; + free( psz_tmp ); + } +} + +const char* msg_StackMsg( void ) +{ + msg_context_t *p_ctx = GetContext(); + assert( p_ctx ); + return p_ctx->psz_message; +} diff --git a/VLC/mmx.h b/VLC/mmx.h new file mode 100644 index 0000000..e302f87 --- /dev/null +++ b/VLC/mmx.h @@ -0,0 +1,255 @@ +/* + * mmx.h + * Copyright (C) 1997-1999 H. Dietz and R. Fisher + * + * This file is part of mpeg2dec, a free MPEG-2 video stream decoder. + * + * mpeg2dec is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mpeg2dec is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * The type of an value that fits in an MMX register (note that long + * long constant values MUST be suffixed by LL and unsigned long long + * values by ULL, lest they be truncated by the compiler) + */ + +typedef union { + int64_t q; /* Quadword (64-bit) value */ + uint64_t uq; /* Unsigned Quadword */ + int32_t d[2]; /* 2 Doubleword (32-bit) values */ + uint32_t ud[2]; /* 2 Unsigned Doubleword */ + int16_t w[4]; /* 4 Word (16-bit) values */ + uint16_t uw[4]; /* 4 Unsigned Word */ + int8_t b[8]; /* 8 Byte (8-bit) values */ + uint8_t ub[8]; /* 8 Unsigned Byte */ + float s[2]; /* Single-precision (32-bit) value */ +} ATTR_ALIGN(8) mmx_t; /* On an 8-byte (64-bit) boundary */ + + +#define mmx_i2r(op,imm,reg) \ + __asm__ __volatile__ (#op " %0, %%" #reg \ + : /* nothing */ \ + : "i" (imm) ) + +#define mmx_m2r(op,mem,reg) \ + __asm__ __volatile__ (#op " %0, %%" #reg \ + : /* nothing */ \ + : "m" (mem)) + +#define mmx_r2m(op,reg,mem) \ + __asm__ __volatile__ (#op " %%" #reg ", %0" \ + : "=m" (mem) \ + : /* nothing */ ) + +#define mmx_r2r(op,regs,regd) \ + __asm__ __volatile__ (#op " %" #regs ", %" #regd) + + +#define emms() __asm__ __volatile__ ("emms") + +#define movd_m2r(var,reg) mmx_m2r (movd, var, reg) +#define movd_r2m(reg,var) mmx_r2m (movd, reg, var) +#define movd_r2r(regs,regd) mmx_r2r (movd, regs, regd) + +#define movq_m2r(var,reg) mmx_m2r (movq, var, reg) +#define movq_r2m(reg,var) mmx_r2m (movq, reg, var) +#define movq_r2r(regs,regd) mmx_r2r (movq, regs, regd) + +#define packssdw_m2r(var,reg) mmx_m2r (packssdw, var, reg) +#define packssdw_r2r(regs,regd) mmx_r2r (packssdw, regs, regd) +#define packsswb_m2r(var,reg) mmx_m2r (packsswb, var, reg) +#define packsswb_r2r(regs,regd) mmx_r2r (packsswb, regs, regd) + +#define packuswb_m2r(var,reg) mmx_m2r (packuswb, var, reg) +#define packuswb_r2r(regs,regd) mmx_r2r (packuswb, regs, regd) + +#define paddb_m2r(var,reg) mmx_m2r (paddb, var, reg) +#define paddb_r2r(regs,regd) mmx_r2r (paddb, regs, regd) +#define paddd_m2r(var,reg) mmx_m2r (paddd, var, reg) +#define paddd_r2r(regs,regd) mmx_r2r (paddd, regs, regd) +#define paddw_m2r(var,reg) mmx_m2r (paddw, var, reg) +#define paddw_r2r(regs,regd) mmx_r2r (paddw, regs, regd) + +#define paddsb_m2r(var,reg) mmx_m2r (paddsb, var, reg) +#define paddsb_r2r(regs,regd) mmx_r2r (paddsb, regs, regd) +#define paddsw_m2r(var,reg) mmx_m2r (paddsw, var, reg) +#define paddsw_r2r(regs,regd) mmx_r2r (paddsw, regs, regd) + +#define paddusb_m2r(var,reg) mmx_m2r (paddusb, var, reg) +#define paddusb_r2r(regs,regd) mmx_r2r (paddusb, regs, regd) +#define paddusw_m2r(var,reg) mmx_m2r (paddusw, var, reg) +#define paddusw_r2r(regs,regd) mmx_r2r (paddusw, regs, regd) + +#define pand_m2r(var,reg) mmx_m2r (pand, var, reg) +#define pand_r2r(regs,regd) mmx_r2r (pand, regs, regd) + +#define pandn_m2r(var,reg) mmx_m2r (pandn, var, reg) +#define pandn_r2r(regs,regd) mmx_r2r (pandn, regs, regd) + +#define pcmpeqb_m2r(var,reg) mmx_m2r (pcmpeqb, var, reg) +#define pcmpeqb_r2r(regs,regd) mmx_r2r (pcmpeqb, regs, regd) +#define pcmpeqd_m2r(var,reg) mmx_m2r (pcmpeqd, var, reg) +#define pcmpeqd_r2r(regs,regd) mmx_r2r (pcmpeqd, regs, regd) +#define pcmpeqw_m2r(var,reg) mmx_m2r (pcmpeqw, var, reg) +#define pcmpeqw_r2r(regs,regd) mmx_r2r (pcmpeqw, regs, regd) + +#define pcmpgtb_m2r(var,reg) mmx_m2r (pcmpgtb, var, reg) +#define pcmpgtb_r2r(regs,regd) mmx_r2r (pcmpgtb, regs, regd) +#define pcmpgtd_m2r(var,reg) mmx_m2r (pcmpgtd, var, reg) +#define pcmpgtd_r2r(regs,regd) mmx_r2r (pcmpgtd, regs, regd) +#define pcmpgtw_m2r(var,reg) mmx_m2r (pcmpgtw, var, reg) +#define pcmpgtw_r2r(regs,regd) mmx_r2r (pcmpgtw, regs, regd) + +#define pmaddwd_m2r(var,reg) mmx_m2r (pmaddwd, var, reg) +#define pmaddwd_r2r(regs,regd) mmx_r2r (pmaddwd, regs, regd) + +#define pmulhw_m2r(var,reg) mmx_m2r (pmulhw, var, reg) +#define pmulhw_r2r(regs,regd) mmx_r2r (pmulhw, regs, regd) + +#define pmullw_m2r(var,reg) mmx_m2r (pmullw, var, reg) +#define pmullw_r2r(regs,regd) mmx_r2r (pmullw, regs, regd) + +#define por_m2r(var,reg) mmx_m2r (por, var, reg) +#define por_r2r(regs,regd) mmx_r2r (por, regs, regd) + +#define pslld_i2r(imm,reg) mmx_i2r (pslld, imm, reg) +#define pslld_m2r(var,reg) mmx_m2r (pslld, var, reg) +#define pslld_r2r(regs,regd) mmx_r2r (pslld, regs, regd) +#define psllq_i2r(imm,reg) mmx_i2r (psllq, imm, reg) +#define psllq_m2r(var,reg) mmx_m2r (psllq, var, reg) +#define psllq_r2r(regs,regd) mmx_r2r (psllq, regs, regd) +#define psllw_i2r(imm,reg) mmx_i2r (psllw, imm, reg) +#define psllw_m2r(var,reg) mmx_m2r (psllw, var, reg) +#define psllw_r2r(regs,regd) mmx_r2r (psllw, regs, regd) + +#define psrad_i2r(imm,reg) mmx_i2r (psrad, imm, reg) +#define psrad_m2r(var,reg) mmx_m2r (psrad, var, reg) +#define psrad_r2r(regs,regd) mmx_r2r (psrad, regs, regd) +#define psraw_i2r(imm,reg) mmx_i2r (psraw, imm, reg) +#define psraw_m2r(var,reg) mmx_m2r (psraw, var, reg) +#define psraw_r2r(regs,regd) mmx_r2r (psraw, regs, regd) + +#define psrld_i2r(imm,reg) mmx_i2r (psrld, imm, reg) +#define psrld_m2r(var,reg) mmx_m2r (psrld, var, reg) +#define psrld_r2r(regs,regd) mmx_r2r (psrld, regs, regd) +#define psrlq_i2r(imm,reg) mmx_i2r (psrlq, imm, reg) +#define psrlq_m2r(var,reg) mmx_m2r (psrlq, var, reg) +#define psrlq_r2r(regs,regd) mmx_r2r (psrlq, regs, regd) +#define psrlw_i2r(imm,reg) mmx_i2r (psrlw, imm, reg) +#define psrlw_m2r(var,reg) mmx_m2r (psrlw, var, reg) +#define psrlw_r2r(regs,regd) mmx_r2r (psrlw, regs, regd) + +#define psubb_m2r(var,reg) mmx_m2r (psubb, var, reg) +#define psubb_r2r(regs,regd) mmx_r2r (psubb, regs, regd) +#define psubd_m2r(var,reg) mmx_m2r (psubd, var, reg) +#define psubd_r2r(regs,regd) mmx_r2r (psubd, regs, regd) +#define psubw_m2r(var,reg) mmx_m2r (psubw, var, reg) +#define psubw_r2r(regs,regd) mmx_r2r (psubw, regs, regd) + +#define psubsb_m2r(var,reg) mmx_m2r (psubsb, var, reg) +#define psubsb_r2r(regs,regd) mmx_r2r (psubsb, regs, regd) +#define psubsw_m2r(var,reg) mmx_m2r (psubsw, var, reg) +#define psubsw_r2r(regs,regd) mmx_r2r (psubsw, regs, regd) + +#define psubusb_m2r(var,reg) mmx_m2r (psubusb, var, reg) +#define psubusb_r2r(regs,regd) mmx_r2r (psubusb, regs, regd) +#define psubusw_m2r(var,reg) mmx_m2r (psubusw, var, reg) +#define psubusw_r2r(regs,regd) mmx_r2r (psubusw, regs, regd) + +#define punpckhbw_m2r(var,reg) mmx_m2r (punpckhbw, var, reg) +#define punpckhbw_r2r(regs,regd) mmx_r2r (punpckhbw, regs, regd) +#define punpckhdq_m2r(var,reg) mmx_m2r (punpckhdq, var, reg) +#define punpckhdq_r2r(regs,regd) mmx_r2r (punpckhdq, regs, regd) +#define punpckhwd_m2r(var,reg) mmx_m2r (punpckhwd, var, reg) +#define punpckhwd_r2r(regs,regd) mmx_r2r (punpckhwd, regs, regd) + +#define punpcklbw_m2r(var,reg) mmx_m2r (punpcklbw, var, reg) +#define punpcklbw_r2r(regs,regd) mmx_r2r (punpcklbw, regs, regd) +#define punpckldq_m2r(var,reg) mmx_m2r (punpckldq, var, reg) +#define punpckldq_r2r(regs,regd) mmx_r2r (punpckldq, regs, regd) +#define punpcklwd_m2r(var,reg) mmx_m2r (punpcklwd, var, reg) +#define punpcklwd_r2r(regs,regd) mmx_r2r (punpcklwd, regs, regd) + +#define pxor_m2r(var,reg) mmx_m2r (pxor, var, reg) +#define pxor_r2r(regs,regd) mmx_r2r (pxor, regs, regd) + + +/* 3DNOW extensions */ + +#define pavgusb_m2r(var,reg) mmx_m2r (pavgusb, var, reg) +#define pavgusb_r2r(regs,regd) mmx_r2r (pavgusb, regs, regd) + + +/* AMD MMX extensions - also available in intel SSE */ + + +#define mmx_m2ri(op,mem,reg,imm) \ + __asm__ __volatile__ (#op " %1, %0, %%" #reg \ + : /* nothing */ \ + : "X" (mem), "X" (imm)) +#define mmx_r2ri(op,regs,regd,imm) \ + __asm__ __volatile__ (#op " %0, %%" #regs ", %%" #regd \ + : /* nothing */ \ + : "X" (imm) ) + +#define mmx_fetch(mem,hint) \ + __asm__ __volatile__ ("prefetch" #hint " %0" \ + : /* nothing */ \ + : "X" (mem)) + + +#define maskmovq(regs,maskreg) mmx_r2ri (maskmovq, regs, maskreg) + +#define movntq_r2m(mmreg,var) mmx_r2m (movntq, mmreg, var) + +#define pavgb_m2r(var,reg) mmx_m2r (pavgb, var, reg) +#define pavgb_r2r(regs,regd) mmx_r2r (pavgb, regs, regd) +#define pavgw_m2r(var,reg) mmx_m2r (pavgw, var, reg) +#define pavgw_r2r(regs,regd) mmx_r2r (pavgw, regs, regd) + +#define pextrw_r2r(mmreg,reg,imm) mmx_r2ri (pextrw, mmreg, reg, imm) + +#define pinsrw_r2r(reg,mmreg,imm) mmx_r2ri (pinsrw, reg, mmreg, imm) + +#define pmaxsw_m2r(var,reg) mmx_m2r (pmaxsw, var, reg) +#define pmaxsw_r2r(regs,regd) mmx_r2r (pmaxsw, regs, regd) + +#define pmaxub_m2r(var,reg) mmx_m2r (pmaxub, var, reg) +#define pmaxub_r2r(regs,regd) mmx_r2r (pmaxub, regs, regd) + +#define pminsw_m2r(var,reg) mmx_m2r (pminsw, var, reg) +#define pminsw_r2r(regs,regd) mmx_r2r (pminsw, regs, regd) + +#define pminub_m2r(var,reg) mmx_m2r (pminub, var, reg) +#define pminub_r2r(regs,regd) mmx_r2r (pminub, regs, regd) + +#define pmovmskb(mmreg,reg) \ + __asm__ __volatile__ ("movmskps %" #mmreg ", %" #reg) + +#define pmulhuw_m2r(var,reg) mmx_m2r (pmulhuw, var, reg) +#define pmulhuw_r2r(regs,regd) mmx_r2r (pmulhuw, regs, regd) + +#define prefetcht0(mem) mmx_fetch (mem, t0) +#define prefetcht1(mem) mmx_fetch (mem, t1) +#define prefetcht2(mem) mmx_fetch (mem, t2) +#define prefetchnta(mem) mmx_fetch (mem, nta) + +#define psadbw_m2r(var,reg) mmx_m2r (psadbw, var, reg) +#define psadbw_r2r(regs,regd) mmx_r2r (psadbw, regs, regd) + +#define pshufw_m2r(var,reg,imm) mmx_m2ri(pshufw, var, reg, imm) +#define pshufw_r2r(regs,regd,imm) mmx_r2ri(pshufw, regs, regd, imm) + +#define sfence() __asm__ __volatile__ ("sfence\n\t") diff --git a/VLC/modules.c b/VLC/modules.c new file mode 100644 index 0000000..df67398 --- /dev/null +++ b/VLC/modules.c @@ -0,0 +1,1406 @@ +/***************************************************************************** + * modules.c : Builtin and plugin modules management functions + ***************************************************************************** + * Copyright (C) 2001-2007 the VideoLAN team + * $Id$ + * + * Authors: Sam Hocevar + * Ethan C. Baldridge + * Hans-Peter Jansen + * Gildas Bazin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "vlc_plugin.h" +#include "libvlc.h" + +/* Some faulty libcs have a broken struct dirent when _FILE_OFFSET_BITS + * is set to 64. Don't try to be cleverer. */ +#ifdef _FILE_OFFSET_BITS +#undef _FILE_OFFSET_BITS +#endif + +#include /* free(), strtol() */ +#include /* sprintf() */ +#include /* strdup() */ +#include + +#ifdef HAVE_DIRENT_H +# include +#endif + +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif + +#if !defined(HAVE_DYNAMIC_PLUGINS) + /* no support for plugins */ +#elif defined(HAVE_DL_DYLD) +# if defined(HAVE_MACH_O_DYLD_H) +# include +# endif +#elif defined(HAVE_DL_BEOS) +# if defined(HAVE_IMAGE_H) +# include +# endif +#elif defined(HAVE_DL_WINDOWS) +# include +#elif defined(HAVE_DL_DLOPEN) +# if defined(HAVE_DLFCN_H) /* Linux, BSD, Hurd */ +# include +# endif +# if defined(HAVE_SYS_DL_H) +# include +# endif +#elif defined(HAVE_DL_SHL_LOAD) +# if defined(HAVE_DL_H) +# include +# endif +#endif + +#include "configuration.h" + +#include "vlc_charset.h" +#include "vlc_arrays.h" + +#include "modules.h" +#include "builtin.h" + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +#ifdef HAVE_DYNAMIC_PLUGINS +static void AllocateAllPlugins ( vlc_object_t * ); +static void AllocatePluginDir ( vlc_object_t *, const char *, int ); +static int AllocatePluginFile ( vlc_object_t *, char *, int64_t, int64_t ); +static module_t * AllocatePlugin( vlc_object_t *, char * ); +#endif +static int AllocateBuiltinModule( vlc_object_t *, int ( * ) ( module_t * ) ); +static void DeleteModule ( module_t *, bool ); +#ifdef HAVE_DYNAMIC_PLUGINS +static void DupModule ( module_t * ); +static void UndupModule ( module_t * ); +#endif + +/** + * Init bank + * + * Creates a module bank structure which will be filled later + * on with all the modules found. + * \param p_this vlc object structure + * \return nothing + */ +void __module_InitBank( vlc_object_t *p_this ) +{ + module_bank_t *p_bank = NULL; + libvlc_global_data_t *p_libvlc_global = vlc_global(); + + vlc_mutex_t *lock = var_AcquireMutex( "libvlc" ); + + if( p_libvlc_global->p_module_bank == NULL ) + { + p_bank = vlc_custom_create( p_this, sizeof(module_bank_t), + VLC_OBJECT_GENERIC, "module bank"); + p_bank->i_usage = 1; + p_bank->i_cache = p_bank->i_loaded_cache = 0; + p_bank->pp_cache = p_bank->pp_loaded_cache = NULL; + p_bank->b_cache = p_bank->b_cache_dirty = + p_bank->b_cache_delete = false; + + /* Everything worked, attach the object */ + p_libvlc_global->p_module_bank = p_bank; + vlc_object_attach( p_bank, p_libvlc_global ); + + /* Fills the module bank structure with the main module infos. + * This is very useful as it will allow us to consider the main + * library just as another module, and for instance the configuration + * options of main will be available in the module bank structure just + * as for every other module. */ + AllocateBuiltinModule( p_this, vlc_entry__main ); + } + else + p_libvlc_global->p_module_bank->i_usage++; + + vlc_mutex_unlock( lock ); +} + + +/** + * End bank + * + * Unloads all unused plugin modules and empties the module + * bank in case of success. + * \param p_this vlc object structure + * \return nothing + */ +void __module_EndBank( vlc_object_t *p_this ) +{ + module_t * p_next = NULL; + libvlc_global_data_t *p_libvlc_global = vlc_global(); + + vlc_mutex_t *lock = var_AcquireMutex( "libvlc" ); + if( !p_libvlc_global->p_module_bank ) + { + vlc_mutex_unlock( lock ); + return; + } + if( --p_libvlc_global->p_module_bank->i_usage ) + { + vlc_mutex_unlock( lock ); + return; + } + vlc_mutex_unlock( lock ); + + /* Save the configuration */ + config_AutoSaveConfigFile( p_this ); + +#ifdef HAVE_DYNAMIC_PLUGINS +# define p_bank p_libvlc_global->p_module_bank + if( p_bank->b_cache ) CacheSave( p_this ); + while( p_bank->i_loaded_cache-- ) + { + if( p_bank->pp_loaded_cache[p_bank->i_loaded_cache] ) + { + DeleteModule( + p_bank->pp_loaded_cache[p_bank->i_loaded_cache]->p_module, + p_bank->pp_loaded_cache[p_bank->i_loaded_cache]->b_used ); + free( p_bank->pp_loaded_cache[p_bank->i_loaded_cache]->psz_file ); + free( p_bank->pp_loaded_cache[p_bank->i_loaded_cache] ); + p_bank->pp_loaded_cache[p_bank->i_loaded_cache] = NULL; + } + } + if( p_bank->pp_loaded_cache ) + { + free( p_bank->pp_loaded_cache ); + p_bank->pp_loaded_cache = NULL; + } + while( p_bank->i_cache-- ) + { + free( p_bank->pp_cache[p_bank->i_cache]->psz_file ); + free( p_bank->pp_cache[p_bank->i_cache] ); + p_bank->pp_cache[p_bank->i_cache] = NULL; + } + if( p_bank->pp_cache ) + { + free( p_bank->pp_cache ); + p_bank->pp_cache = NULL; + } +# undef p_bank +#endif + + vlc_object_detach( p_libvlc_global->p_module_bank ); + + while( vlc_internals( p_libvlc_global->p_module_bank )->i_children ) + { + p_next = (module_t *)vlc_internals( p_libvlc_global->p_module_bank )->pp_children[0]; + DeleteModule( p_next, true ); + } + + vlc_object_release( p_libvlc_global->p_module_bank ); + p_libvlc_global->p_module_bank = NULL; +} + +/** + * Load all modules which we built with. + * + * Fills the module bank structure with the builtin modules. + * \param p_this vlc object structure + * \return nothing + */ +void __module_LoadBuiltins( vlc_object_t * p_this ) +{ + libvlc_global_data_t *p_libvlc_global = vlc_global(); + + vlc_mutex_t *lock = var_AcquireMutex( "libvlc" ); + if( p_libvlc_global->p_module_bank->b_builtins ) + { + vlc_mutex_unlock( lock ); + return; + } + p_libvlc_global->p_module_bank->b_builtins = true; + vlc_mutex_unlock( lock ); + + msg_Dbg( p_this, "checking builtin modules" ); + //ALLOCATE_ALL_BUILTINS(); +} + +/** + * Load all plugins + * + * Load all plugin modules we can find. + * Fills the module bank structure with the plugin modules. + * \param p_this vlc object structure + * \return nothing + */ +void __module_LoadPlugins( vlc_object_t * p_this ) +{ +#ifdef HAVE_DYNAMIC_PLUGINS + libvlc_global_data_t *p_libvlc_global = vlc_global(); + + vlc_mutex_t *lock = var_AcquireMutex( "libvlc" ); + if( p_libvlc_global->p_module_bank->b_plugins ) + { + vlc_mutex_unlock( lock ); + return; + } + p_libvlc_global->p_module_bank->b_plugins = true; + vlc_mutex_unlock( lock ); + + msg_Dbg( p_this, "checking plugin modules" ); + + if( config_GetInt( p_this, "plugins-cache" ) ) + p_libvlc_global->p_module_bank->b_cache = true; + + if( p_libvlc_global->p_module_bank->b_cache || + p_libvlc_global->p_module_bank->b_cache_delete ) CacheLoad( p_this ); + + AllocateAllPlugins( p_this ); +#endif +} + +/** + * Checks whether a module implements a capability. + * + * \param m the module + * \param cap the capability to check + * \return TRUE if the module have the capability + */ +bool module_IsCapable( const module_t *m, const char *cap ) +{ + return !strcmp( m->psz_capability, cap ); +} + +/** + * Get the internal name of a module + * + * \param m the module + * \return the module name + */ +const char *module_GetObjName( const module_t *m ) +{ + return m->psz_object_name; +} + +/** + * Get the human-friendly name of a module. + * + * \param m the module + * \param long_name TRUE to have the long name of the module + * \return the short or long name of the module + */ +const char *module_GetName( const module_t *m, bool long_name ) +{ + if( long_name && ( m->psz_longname != NULL) ) + return m->psz_longname; + + return m->psz_shortname ?: m->psz_object_name; +} + +/** + * Get the help for a module + * + * \param m the module + * \return the help + */ +const char *module_GetHelp( const module_t *m ) +{ + return m->psz_help; +} + +/** + * module Need + * + * Return the best module function, given a capability list. + * \param p_this the vlc object + * \param psz_capability list of capabilities needed + * \param psz_name name of the module asked + * \param b_strict TRUE yto use the strict mode + * \return the module or NULL in case of a failure + */ +module_t * __module_Need( vlc_object_t *p_this, const char *psz_capability, + const char *psz_name, bool b_strict ) +{ + typedef struct module_list_t module_list_t; + + stats_TimerStart( p_this, "module_Need()", STATS_TIMER_MODULE_NEED ); + + struct module_list_t + { + module_t *p_module; + int i_score; + bool b_force; + module_list_t *p_next; + }; + + module_list_t *p_list, *p_first, *p_tmp; + vlc_list_t *p_all; + + int i_which_module, i_index = 0; + + module_t *p_module; + + int i_shortcuts = 0; + char *psz_shortcuts = NULL, *psz_var = NULL, *psz_alias = NULL; + bool b_force_backup = p_this->b_force; + + + /* Deal with variables */ + if( psz_name && psz_name[0] == '$' ) + { + psz_name = psz_var = var_CreateGetString( p_this, psz_name + 1 ); + } + + /* Count how many different shortcuts were asked for */ + if( psz_name && *psz_name ) + { + char *psz_parser, *psz_last_shortcut; + + /* If the user wants none, give him none. */ + if( !strcmp( psz_name, "none" ) ) + { + free( psz_var ); + stats_TimerStop( p_this, STATS_TIMER_MODULE_NEED ); + stats_TimerDump( p_this, STATS_TIMER_MODULE_NEED ); + stats_TimerClean( p_this, STATS_TIMER_MODULE_NEED ); + return NULL; + } + + i_shortcuts++; + psz_shortcuts = psz_last_shortcut = strdup( psz_name ); + + for( psz_parser = psz_shortcuts; *psz_parser; psz_parser++ ) + { + if( *psz_parser == ',' ) + { + *psz_parser = '\0'; + i_shortcuts++; + psz_last_shortcut = psz_parser + 1; + } + } + + /* Check if the user wants to override the "strict" mode */ + if( psz_last_shortcut ) + { + if( !strcmp(psz_last_shortcut, "none") ) + { + b_strict = true; + i_shortcuts--; + } + else if( !strcmp(psz_last_shortcut, "any") ) + { + b_strict = false; + i_shortcuts--; + } + } + } + + /* Sort the modules and test them */ + p_all = vlc_list_find( p_this, VLC_OBJECT_MODULE, FIND_ANYWHERE ); + p_list = malloc( p_all->i_count * sizeof( module_list_t ) ); + p_first = NULL; + unsigned i_cpu = vlc_CPU(); + + /* Parse the module list for capabilities and probe each of them */ + for( i_which_module = 0; i_which_module < p_all->i_count; i_which_module++ ) + { + int i_shortcut_bonus = 0; + + p_module = (module_t *)p_all->p_values[i_which_module].p_object; + + /* Test that this module can do what we need */ + if( !module_IsCapable( p_module, psz_capability ) ) + { + /* Don't recurse through the sub-modules because vlc_list_find() + * will list them anyway. */ + continue; + } + + /* Test if we have the required CPU */ + if( (p_module->i_cpu & i_cpu) != p_module->i_cpu ) + { + continue; + } + + /* If we required a shortcut, check this plugin provides it. */ + if( i_shortcuts > 0 ) + { + bool b_trash; + const char *psz_name = psz_shortcuts; + + /* Let's drop modules with a <= 0 score (unless they are + * explicitly requested) */ + b_trash = p_module->i_score <= 0; + + for( unsigned i_short = i_shortcuts; i_short > 0; i_short-- ) + { + for( unsigned i = 0; p_module->pp_shortcuts[i]; i++ ) + { + char *c; + if( ( c = strchr( psz_name, '@' ) ) + ? !strncasecmp( psz_name, p_module->pp_shortcuts[i], + c-psz_name ) + : !strcasecmp( psz_name, p_module->pp_shortcuts[i] ) ) + { + /* Found it */ + if( c && c[1] ) + psz_alias = c+1; + i_shortcut_bonus = i_short * 10000; + goto found_shortcut; + } + } + + /* Go to the next shortcut... This is so lame! */ + psz_name += strlen( psz_name ) + 1; + } + + /* If we are in "strict" mode and we couldn't + * find the module in the list of provided shortcuts, + * then kick the bastard out of here!!! */ + if( b_strict ) + continue; + } + /* If we didn't require a shortcut, trash <= 0 scored plugins */ + else if( p_module->i_score <= 0 ) + { + continue; + } + +found_shortcut: + + /* Store this new module */ + p_list[ i_index ].p_module = p_module; + p_list[ i_index ].i_score = p_module->i_score + i_shortcut_bonus; + p_list[ i_index ].b_force = i_shortcut_bonus && b_strict; + + /* Add it to the modules-to-probe list */ + if( i_index == 0 ) + { + p_list[ 0 ].p_next = NULL; + p_first = p_list; + } + else + { + /* Ok, so at school you learned that quicksort is quick, and + * bubble sort sucks raw eggs. But that's when dealing with + * thousands of items. Here we have barely 50. */ + module_list_t *p_newlist = p_first; + + if( p_first->i_score < p_list[ i_index ].i_score ) + { + p_list[ i_index ].p_next = p_first; + p_first = &p_list[ i_index ]; + } + else + { + while( p_newlist->p_next != NULL && + p_newlist->p_next->i_score >= p_list[ i_index ].i_score ) + { + p_newlist = p_newlist->p_next; + } + + p_list[ i_index ].p_next = p_newlist->p_next; + p_newlist->p_next = &p_list[ i_index ]; + } + } + + i_index++; + } + + msg_Dbg( p_this, "looking for %s module: %i candidate%s", psz_capability, + i_index, i_index == 1 ? "" : "s" ); + + /* Lock all candidate modules */ + p_tmp = p_first; + while( p_tmp != NULL ) + { + vlc_object_yield( p_tmp->p_module ); + p_tmp = p_tmp->p_next; + } + + /* We can release the list, interesting modules were yielded */ + vlc_list_release( p_all ); + + /* Parse the linked list and use the first successful module */ + p_tmp = p_first; + while( p_tmp != NULL ) + { +#ifdef HAVE_DYNAMIC_PLUGINS + /* Make sure the module is loaded in mem */ + module_t *p_module = p_tmp->p_module; + if( p_module->b_submodule ) + p_module = (module_t *)p_module->p_parent; + + if( !p_module->b_builtin && !p_module->b_loaded ) + { + module_t *p_new_module = + AllocatePlugin( p_this, p_module->psz_filename ); + if( p_new_module ) + { + CacheMerge( p_this, p_module, p_new_module ); + vlc_object_attach( p_new_module, p_module ); + DeleteModule( p_new_module, true ); + } + } +#endif + + p_this->b_force = p_tmp->b_force; + if( p_tmp->p_module->pf_activate + && p_tmp->p_module->pf_activate( p_this ) == VLC_SUCCESS ) + { + break; + } + + vlc_object_release( p_tmp->p_module ); + p_tmp = p_tmp->p_next; + } + + /* Store the locked module value */ + if( p_tmp != NULL ) + { + p_module = p_tmp->p_module; + p_tmp = p_tmp->p_next; + } + else + { + p_module = NULL; + } + + /* Unlock the remaining modules */ + while( p_tmp != NULL ) + { + vlc_object_release( p_tmp->p_module ); + p_tmp = p_tmp->p_next; + } + + free( p_list ); + p_this->b_force = b_force_backup; + + if( p_module != NULL ) + { + msg_Dbg( p_this, "using %s module \"%s\"", + psz_capability, p_module->psz_object_name ); + } + else if( p_first == NULL ) + { + if( !strcmp( psz_capability, "access_demux" ) ) + { + msg_Warn( p_this, "no %s module matched \"%s\"", + psz_capability, (psz_name && *psz_name) ? psz_name : "any" ); + } + else + { + msg_Err( p_this, "no %s module matched \"%s\"", + psz_capability, (psz_name && *psz_name) ? psz_name : "any" ); + + msg_StackSet( VLC_EGENERIC, "no %s module matched \"%s\"", + psz_capability, (psz_name && *psz_name) ? psz_name : "any" ); + } + } + else if( psz_name != NULL && *psz_name ) + { + msg_Warn( p_this, "no %s module matching \"%s\" could be loaded", + psz_capability, (psz_name && *psz_name) ? psz_name : "any" ); + } + else + msg_StackSet( VLC_EGENERIC, "no suitable %s module", psz_capability ); + + if( p_module && !p_this->psz_object_name ) + { + /* This assumes that p_this is the object which will be using the + * module. That's not always the case ... but it is in most cases. + */ + if( psz_alias ) + p_this->psz_object_name = strdup( psz_alias ); + else + p_this->psz_object_name = strdup( p_module->psz_object_name ); + } + + free( psz_shortcuts ); + free( psz_var ); + + stats_TimerStop( p_this, STATS_TIMER_MODULE_NEED ); + stats_TimerDump( p_this, STATS_TIMER_MODULE_NEED ); + stats_TimerClean( p_this, STATS_TIMER_MODULE_NEED ); + + /* Don't forget that the module is still locked */ + return p_module; +} + +/** + * Module unneed + * + * This function must be called by the thread that called module_Need, to + * decrease the reference count and allow for hiding of modules. + * \param p_this vlc object structure + * \param p_module the module structure + * \return nothing + */ +void __module_Unneed( vlc_object_t * p_this, module_t * p_module ) +{ + /* Use the close method */ + if( p_module->pf_deactivate ) + { + p_module->pf_deactivate( p_this ); + } + + msg_Dbg( p_this, "removing module \"%s\"", p_module->psz_object_name ); + + vlc_object_release( p_module ); +} + +/** + * Get a pointer to a module_t given it's name. + * + * \param p_this vlc object structure + * \param psz_name the name of the module + * \return a pointer to the module or NULL in case of a failure + */ +module_t *__module_Find( vlc_object_t *p_this, const char * psz_name ) +{ + vlc_list_t *p_list; + int i; + p_list = vlc_list_find( p_this, VLC_OBJECT_MODULE, FIND_ANYWHERE ); + for( i = 0 ; i < p_list->i_count; i++) + { + module_t *p_module = ((module_t *) p_list->p_values[i].p_object); + const char *psz_module_name = p_module->psz_object_name; + if( psz_module_name && !strcmp( psz_module_name, psz_name ) ) + { + /* We can release the list, and return yes */ + vlc_object_yield( p_module ); + vlc_list_release( p_list ); + return p_module; + } + } + vlc_list_release( p_list ); + return NULL; +} + + +/** + * Release a module_t pointer from module_Find(). + * + * \param module the module to release + * \return nothing + */ +void module_Put( module_t *module ) +{ + vlc_object_release( module ); +} + + +/** + * Tell if a module exists and release it in thic case + * + * \param p_this vlc object structure + * \param psz_name th name of the module + * \return TRUE if the module exists + */ +bool __module_Exists( vlc_object_t *p_this, const char * psz_name ) +{ + module_t *p_module = __module_Find( p_this, psz_name ); + if( p_module ) + { + module_Put( p_module ); + return true; + } + else + { + return false; + } +} + +/** + * GetModuleNamesForCapability + * + * Return a NULL terminated array with the names of the modules + * that have a certain capability. + * Free after uses both the string and the table. + * \param p_this vlc object structure + * \param psz_capability the capability asked + * \param pppsz_longname an pointer to an array of string to contain + the long names of the modules. If set to NULL the function don't use it. + * \return the NULL terminated array + */ +char ** __module_GetModulesNamesForCapability( vlc_object_t *p_this, + const char *psz_capability, + char ***pppsz_longname ) +{ + vlc_list_t *p_list; + int i, j, count = 0; + char **psz_ret; + + /* Do it in two passes : count the number of modules before */ + p_list = vlc_list_find( p_this, VLC_OBJECT_MODULE, FIND_ANYWHERE ); + for( i = 0 ; i < p_list->i_count; i++) + { + module_t *p_module = ((module_t *) p_list->p_values[i].p_object); + const char *psz_module_capability = p_module->psz_capability; + if( psz_module_capability && !strcmp( psz_module_capability, psz_capability ) ) + count++; + } + + psz_ret = malloc( sizeof(char*) * (count+1) ); + if( pppsz_longname ) + *pppsz_longname = malloc( sizeof(char*) * (count+1) ); + if( !psz_ret || ( pppsz_longname && *pppsz_longname == NULL ) ) + { + free( psz_ret ); + free( *pppsz_longname ); + *pppsz_longname = NULL; + vlc_list_release( p_list ); + return NULL; + } + + j = 0; + for( i = 0 ; i < p_list->i_count; i++) + { + module_t *p_module = ((module_t *) p_list->p_values[i].p_object); + const char *psz_module_capability = p_module->psz_capability; + if( psz_module_capability && !strcmp( psz_module_capability, psz_capability ) ) + { + int k = -1; /* hack to handle submodules properly */ + if( p_module->b_submodule ) + { + while( p_module->pp_shortcuts[++k] != NULL ); + k--; + } + psz_ret[j] = strdup( k>=0?p_module->pp_shortcuts[k] + :p_module->psz_object_name ); + if( pppsz_longname ) + (*pppsz_longname)[j] = strdup( module_GetName( p_module, true ) ); + j++; + } + } + psz_ret[count] = NULL; + + vlc_list_release( p_list ); + + return psz_ret; +} + +/** + * Get the configuration of a module + * + * \param module the module + * \param psize the size of the configuration returned + * \return the configuration as an array + */ +module_config_t *module_GetConfig( const module_t *module, unsigned *restrict psize ) +{ + unsigned i,j; + unsigned size = module->confsize; + module_config_t *config = malloc( size * sizeof( *config ) ); + + assert( psize != NULL ); + *psize = 0; + + if( !config ) + return NULL; + + for( i = 0, j = 0; i < size; i++ ) + { + const module_config_t *item = module->p_config + i; + if( item->b_internal /* internal option */ + || item->b_unsaveable /* non-modifiable option */ + || item->b_removed /* removed option */ ) + continue; + + memcpy( config + j, item, sizeof( *config ) ); + j++; + } + *psize = j; + + return config; +} + +/** + * Release the configuration + * + * \param the configuration + * \return nothing + */ +void module_PutConfig( module_config_t *config ) +{ + free( config ); +} + +/***************************************************************************** + * Following functions are local. + *****************************************************************************/ + + /***************************************************************************** + * copy_next_paths_token: from a PATH_SEP_CHAR (a ':' or a ';') separated paths + * return first path. + *****************************************************************************/ +static char * copy_next_paths_token( char * paths, char ** remaining_paths ) +{ + char * path; + int i, done; + bool escaped = false; + + assert( paths ); + + /* Alloc a buffer to store the path */ + path = malloc( strlen( paths ) + 1 ); + if( !path ) return NULL; + + /* Look for PATH_SEP_CHAR (a ':' or a ';') */ + for( i = 0, done = 0 ; paths[i]; i++ ) + { + /* Take care of \\ and \: or \; escapement */ + if( escaped ) + { + escaped = false; + path[done++] = paths[i]; + } +#ifdef WIN32 + else if( paths[i] == '/' ) + escaped = true; +#else + else if( paths[i] == '\\' ) + escaped = true; +#endif + else if( paths[i] == PATH_SEP_CHAR ) + break; + else + path[done++] = paths[i]; + } + path[done++] = 0; + + /* Return the remaining paths */ + if( remaining_paths ) { + *remaining_paths = paths[i] ? &paths[i]+1 : NULL; + } + + return path; +} + +/***************************************************************************** + * AllocateAllPlugins: load all plugin modules we can find. + *****************************************************************************/ +#ifdef HAVE_DYNAMIC_PLUGINS +static void AllocateAllPlugins( vlc_object_t *p_this ) +{ + const char *vlcpath = vlc_global()->psz_vlcpath; + int count,i; + char * path; + vlc_array_t *arraypaths = vlc_array_new(); + + /* Contruct the special search path for system that have a relocatable + * executable. Set it to /modules and /plugins. */ + + if( vlcpath && asprintf( &path, "%s" DIR_SEP "modules", vlcpath ) != -1 ) + vlc_array_append( arraypaths, path ); + if( vlcpath && asprintf( &path, "%s" DIR_SEP "plugins", vlcpath ) != -1 ) + vlc_array_append( arraypaths, path ); +#ifndef WIN32 + vlc_array_append( arraypaths, strdup( PLUGIN_PATH ) ); +#endif + + /* If the user provided a plugin path, we add it to the list */ + char *userpaths = config_GetPsz( p_this, "plugin-path" ); + char *paths_iter; + + for( paths_iter = userpaths; paths_iter; ) + { + path = copy_next_paths_token( paths_iter, &paths_iter ); + if( path ) + vlc_array_append( arraypaths, path ); + } + + count = vlc_array_count( arraypaths ); + for( i = 0 ; i < count ; i++ ) + { + path = vlc_array_item_at_index( arraypaths, i ); + if( !path ) + continue; + + msg_Dbg( p_this, "recursively browsing `%s'", path ); + + /* Don't go deeper than 5 subdirectories */ + AllocatePluginDir( p_this, path, 5 ); + + free( path ); + } + + vlc_array_destroy( arraypaths ); + free( userpaths ); +} + +/***************************************************************************** + * AllocatePluginDir: recursively parse a directory to look for plugins + *****************************************************************************/ +static void AllocatePluginDir( vlc_object_t *p_this, const char *psz_dir, + int i_maxdepth ) +{ +/* FIXME: Needs to be ported to wide char on ALL Windows builds */ +#ifdef WIN32 +# undef opendir +# undef closedir +# undef readdir +#endif +#if defined( UNDER_CE ) || defined( _MSC_VER ) +#ifdef UNDER_CE + wchar_t psz_wpath[MAX_PATH + 256]; + wchar_t psz_wdir[MAX_PATH]; +#endif + char psz_path[MAX_PATH + 256]; + WIN32_FIND_DATA finddata; + HANDLE handle; + int rc; +#else + int i_dirlen; + DIR * dir; + struct dirent * file; +#endif + char * psz_file; + + if( p_this->p_libvlc->b_die || i_maxdepth < 0 ) + { + return; + } + +#if defined( UNDER_CE ) || defined( _MSC_VER ) +#ifdef UNDER_CE + MultiByteToWideChar( CP_ACP, 0, psz_dir, -1, psz_wdir, MAX_PATH ); + + rc = GetFileAttributes( psz_wdir ); + if( rc<0 || !(rc&FILE_ATTRIBUTE_DIRECTORY) ) return; /* Not a directory */ + + /* Parse all files in the directory */ + swprintf( psz_wpath, L"%ls\\*", psz_wdir ); +#else + rc = GetFileAttributes( psz_dir ); + if( rc<0 || !(rc&FILE_ATTRIBUTE_DIRECTORY) ) return; /* Not a directory */ +#endif + + /* Parse all files in the directory */ + sprintf( psz_path, "%s\\*", psz_dir ); + +#ifdef UNDER_CE + handle = FindFirstFile( psz_wpath, &finddata ); +#else + handle = FindFirstFile( psz_path, &finddata ); +#endif + if( handle == INVALID_HANDLE_VALUE ) + { + /* Empty directory */ + return; + } + + /* Parse the directory and try to load all files it contains. */ + do + { +#ifdef UNDER_CE + unsigned int i_len = wcslen( finddata.cFileName ); + swprintf( psz_wpath, L"%ls\\%ls", psz_wdir, finddata.cFileName ); + sprintf( psz_path, "%s\\%ls", psz_dir, finddata.cFileName ); +#else + unsigned int i_len = strlen( finddata.cFileName ); + sprintf( psz_path, "%s\\%s", psz_dir, finddata.cFileName ); +#endif + + /* Skip ".", ".." */ + if( !*finddata.cFileName || !strcmp( finddata.cFileName, "." ) + || !strcmp( finddata.cFileName, ".." ) ) + { + if( !FindNextFile( handle, &finddata ) ) break; + continue; + } + +#ifdef UNDER_CE + if( GetFileAttributes( psz_wpath ) & FILE_ATTRIBUTE_DIRECTORY ) +#else + if( GetFileAttributes( psz_path ) & FILE_ATTRIBUTE_DIRECTORY ) +#endif + { + AllocatePluginDir( p_this, psz_path, i_maxdepth - 1 ); + } + else if( i_len > strlen( LIBEXT ) + /* We only load files ending with LIBEXT */ + && !strncasecmp( psz_path + strlen( psz_path) + - strlen( LIBEXT ), + LIBEXT, strlen( LIBEXT ) ) ) + { + WIN32_FILE_ATTRIBUTE_DATA attrbuf; + int64_t i_time = 0, i_size = 0; + +#ifdef UNDER_CE + if( GetFileAttributesEx( psz_wpath, GetFileExInfoStandard, + &attrbuf ) ) +#else + if( GetFileAttributesEx( psz_path, GetFileExInfoStandard, + &attrbuf ) ) +#endif + { + i_time = attrbuf.ftLastWriteTime.dwHighDateTime; + i_time <<= 32; + i_time |= attrbuf.ftLastWriteTime.dwLowDateTime; + i_size = attrbuf.nFileSizeHigh; + i_size <<= 32; + i_size |= attrbuf.nFileSizeLow; + } + psz_file = psz_path; + + AllocatePluginFile( p_this, psz_file, i_time, i_size ); + } + } + while( !p_this->p_libvlc->b_die && FindNextFile( handle, &finddata ) ); + + /* Close the directory */ + FindClose( handle ); + +#else + dir = opendir( psz_dir ); + if( !dir ) + { + return; + } + + i_dirlen = strlen( psz_dir ); + + /* Parse the directory and try to load all files it contains. */ + while( !p_this->p_libvlc->b_die && ( file = readdir( dir ) ) ) + { + struct stat statbuf; + unsigned int i_len; + int i_stat; + + /* Skip ".", ".." */ + if( !*file->d_name || !strcmp( file->d_name, "." ) + || !strcmp( file->d_name, ".." ) ) + { + continue; + } + + i_len = strlen( file->d_name ); + psz_file = malloc( i_dirlen + 1 + i_len + 1 ); + sprintf( psz_file, "%s"DIR_SEP"%s", psz_dir, file->d_name ); + + i_stat = stat( psz_file, &statbuf ); + if( !i_stat && statbuf.st_mode & S_IFDIR ) + { + AllocatePluginDir( p_this, psz_file, i_maxdepth - 1 ); + } + else if( i_len > strlen( LIBEXT ) + /* We only load files ending with LIBEXT */ + && !strncasecmp( file->d_name + i_len - strlen( LIBEXT ), + LIBEXT, strlen( LIBEXT ) ) ) + { + int64_t i_time = 0, i_size = 0; + + if( !i_stat ) + { + i_time = statbuf.st_mtime; + i_size = statbuf.st_size; + } + + AllocatePluginFile( p_this, psz_file, i_time, i_size ); + } + + free( psz_file ); + } + + /* Close the directory */ + closedir( dir ); + +#endif +} + +/***************************************************************************** + * AllocatePluginFile: load a module into memory and initialize it. + ***************************************************************************** + * This function loads a dynamically loadable module and allocates a structure + * for its information data. The module can then be handled by module_Need + * and module_Unneed. It can be removed by DeleteModule. + *****************************************************************************/ +static int AllocatePluginFile( vlc_object_t * p_this, char * psz_file, + int64_t i_file_time, int64_t i_file_size ) +{ + module_t * p_module = NULL; + module_cache_t *p_cache_entry = NULL; + + /* + * Check our plugins cache first then load plugin if needed + */ + p_cache_entry = + CacheFind( psz_file, i_file_time, i_file_size ); + + if( !p_cache_entry ) + { + p_module = AllocatePlugin( p_this, psz_file ); + } + else + { + /* If junk dll, don't try to load it */ + if( p_cache_entry->b_junk ) + { + p_module = NULL; + } + else + { + module_config_t *p_item = NULL, *p_end = NULL; + + p_module = p_cache_entry->p_module; + p_module->b_loaded = false; + + /* For now we force loading if the module's config contains + * callbacks or actions. + * Could be optimized by adding an API call.*/ + for( p_item = p_module->p_config, p_end = p_item + p_module->confsize; + p_item < p_end; p_item++ ) + { + if( p_item->pf_callback || p_item->i_action ) + { + p_module = AllocatePlugin( p_this, psz_file ); + break; + } + } + if( p_module == p_cache_entry->p_module ) + p_cache_entry->b_used = true; + } + } + + if( p_module ) + { + libvlc_global_data_t *p_libvlc_global = vlc_global(); + + /* Everything worked fine ! + * The module is ready to be added to the list. */ + p_module->b_builtin = false; + + /* msg_Dbg( p_this, "plugin \"%s\", %s", + p_module->psz_object_name, p_module->psz_longname ); */ + + vlc_object_attach( p_module, p_libvlc_global->p_module_bank ); + + if( !p_libvlc_global->p_module_bank->b_cache ) + return 0; + +#define p_bank p_libvlc_global->p_module_bank + /* Add entry to cache */ + p_bank->pp_cache = + realloc( p_bank->pp_cache, (p_bank->i_cache + 1) * sizeof(void *) ); + p_bank->pp_cache[p_bank->i_cache] = malloc( sizeof(module_cache_t) ); + if( !p_bank->pp_cache[p_bank->i_cache] ) + return -1; + p_bank->pp_cache[p_bank->i_cache]->psz_file = strdup( psz_file ); + p_bank->pp_cache[p_bank->i_cache]->i_time = i_file_time; + p_bank->pp_cache[p_bank->i_cache]->i_size = i_file_size; + p_bank->pp_cache[p_bank->i_cache]->b_junk = p_module ? 0 : 1; + p_bank->pp_cache[p_bank->i_cache]->b_used = true; + p_bank->pp_cache[p_bank->i_cache]->p_module = p_module; + p_bank->i_cache++; +#undef p_bank + } + + return p_module ? 0 : -1; +} + +/***************************************************************************** + * AllocatePlugin: load a module into memory and initialize it. + ***************************************************************************** + * This function loads a dynamically loadable module and allocates a structure + * for its information data. The module can then be handled by module_Need + * and module_Unneed. It can be removed by DeleteModule. + *****************************************************************************/ +static module_t * AllocatePlugin( vlc_object_t * p_this, char * psz_file ) +{ + module_t * p_module = NULL; + module_handle_t handle; + + if( module_Load( p_this, psz_file, &handle ) ) + return NULL; + + /* Now that we have successfully loaded the module, we can + * allocate a structure for it */ + p_module = vlc_module_create( p_this ); + if( p_module == NULL ) + { + module_Unload( handle ); + return NULL; + } + + /* We need to fill these since they may be needed by module_Call() */ + p_module->psz_filename = psz_file; + p_module->handle = handle; + p_module->b_loaded = true; + + /* Initialize the module: fill p_module, default config */ + if( module_Call( p_module ) != 0 ) + { + /* We couldn't call module_init() */ + vlc_object_release( p_module ); + module_Unload( handle ); + return NULL; + } + + DupModule( p_module ); + p_module->psz_filename = strdup( p_module->psz_filename ); + + /* Everything worked fine ! The module is ready to be added to the list. */ + p_module->b_builtin = false; + + return p_module; +} + +/***************************************************************************** + * DupModule: make a plugin module standalone. + ***************************************************************************** + * This function duplicates all strings in the module, so that the dynamic + * object can be unloaded. It acts recursively on submodules. + *****************************************************************************/ +static void DupModule( module_t *p_module ) +{ + char **pp_shortcut; + int i_submodule; + + for( pp_shortcut = p_module->pp_shortcuts ; *pp_shortcut ; pp_shortcut++ ) + { + *pp_shortcut = strdup( *pp_shortcut ); + } + + /* We strdup() these entries so that they are still valid when the + * module is unloaded. */ + p_module->psz_capability = strdup( p_module->psz_capability ); + p_module->psz_shortname = p_module->psz_shortname ? + strdup( p_module->psz_shortname ) : NULL; + p_module->psz_longname = strdup( p_module->psz_longname ); + p_module->psz_help = p_module->psz_help ? strdup( p_module->psz_help ) + : NULL; + + for( i_submodule = 0; i_submodule < vlc_internals( p_module )->i_children; i_submodule++ ) + { + DupModule( (module_t*)vlc_internals( p_module )->pp_children[ i_submodule ] ); + } +} + +/***************************************************************************** + * UndupModule: free a duplicated module. + ***************************************************************************** + * This function frees the allocations done in DupModule(). + *****************************************************************************/ +static void UndupModule( module_t *p_module ) +{ + char **pp_shortcut; + int i_submodule; + + for( i_submodule = 0; i_submodule < vlc_internals( p_module )->i_children; i_submodule++ ) + { + UndupModule( (module_t*)vlc_internals( p_module )->pp_children[ i_submodule ] ); + } + + for( pp_shortcut = p_module->pp_shortcuts ; *pp_shortcut ; pp_shortcut++ ) + { + free( *pp_shortcut ); + } + + FREENULL( p_module->psz_object_name ); + free( p_module->psz_capability ); + free( p_module->psz_shortname ); + free( p_module->psz_longname ); + free( p_module->psz_help ); +} + +#endif /* HAVE_DYNAMIC_PLUGINS */ + +/***************************************************************************** + * AllocateBuiltinModule: initialize a builtin module. + ***************************************************************************** + * This function registers a builtin module and allocates a structure + * for its information data. The module can then be handled by module_Need + * and module_Unneed. It can be removed by DeleteModule. + *****************************************************************************/ +static int AllocateBuiltinModule( vlc_object_t * p_this, + int ( *pf_entry ) ( module_t * ) ) +{ + module_t * p_module; + + /* Now that we have successfully loaded the module, we can + * allocate a structure for it */ + p_module = vlc_module_create( p_this ); + if( p_module == NULL ) + return -1; + + /* Initialize the module : fill p_module->psz_object_name, etc. */ + if( pf_entry( p_module ) != 0 ) + { + /* With a well-written module we shouldn't have to print an + * additional error message here, but just make sure. */ + msg_Err( p_this, "failed calling entry point in builtin module" ); + vlc_object_release( p_module ); + return -1; + } + + /* Everything worked fine ! The module is ready to be added to the list. */ + p_module->b_builtin = true; + + /* msg_Dbg( p_this, "builtin \"%s\", %s", + p_module->psz_object_name, p_module->psz_longname ); */ + + vlc_object_attach( p_module, vlc_global()->p_module_bank ); + + return 0; +} + +/***************************************************************************** + * DeleteModule: delete a module and its structure. + ***************************************************************************** + * This function can only be called if the module isn't being used. + *****************************************************************************/ +static void DeleteModule( module_t * p_module, bool b_detach ) +{ + assert( p_module ); + + if( b_detach ) + vlc_object_detach( p_module ); + + /* We free the structures that we strdup()ed in Allocate*Module(). */ +#ifdef HAVE_DYNAMIC_PLUGINS + if( !p_module->b_builtin ) + { + if( p_module->b_loaded && p_module->b_unloadable ) + { + module_Unload( p_module->handle ); + } + UndupModule( p_module ); + free( p_module->psz_filename ); + } +#endif + + /* Free and detach the object's children */ + while( vlc_internals( p_module )->i_children ) + { + vlc_object_t *p_this = vlc_internals( p_module )->pp_children[0]; + vlc_object_detach( p_this ); + vlc_object_release( p_this ); + } + + config_Free( p_module ); + vlc_object_release( p_module ); +} diff --git a/VLC/modules.h b/VLC/modules.h new file mode 100644 index 0000000..50cf16a --- /dev/null +++ b/VLC/modules.h @@ -0,0 +1,167 @@ +/***************************************************************************** + * modules.h : Module management functions. + ***************************************************************************** + * Copyright (C) 2001 the VideoLAN team + * $Id$ + * + * Authors: Samuel Hocevar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#if defined(__PLUGIN__) || defined(__BUILTIN__) || !defined(__LIBVLC__) + +#endif + +#ifndef __LIBVLC_MODULES_H +# define __LIBVLC_MODULES_H 1 + + +/* Number of tries before we unload an unused module */ +#define MODULE_HIDE_DELAY 50 + +/***************************************************************************** + * module_bank_t: the module bank + ***************************************************************************** + * This variable is accessed by any function using modules. + *****************************************************************************/ +struct module_bank_t +{ + VLC_COMMON_MEMBERS + + int i_usage; + + bool b_builtins; + bool b_plugins; + + /* Plugins cache */ + bool b_cache; + bool b_cache_dirty; + bool b_cache_delete; + + int i_cache; + module_cache_t **pp_cache; + + int i_loaded_cache; + module_cache_t **pp_loaded_cache; +}; + +/***************************************************************************** + * Module cache description structure + *****************************************************************************/ +struct module_cache_t +{ + /* Mandatory cache entry header */ + char *psz_file; + int64_t i_time; + int64_t i_size; + bool b_junk; + + /* Optional extra data */ + module_t *p_module; + bool b_used; +}; + + +#define MODULE_SHORTCUT_MAX 50 + +/* The module handle type. */ +#if defined(HAVE_DL_DYLD) +# if defined (HAVE_MACH_O_DYLD_H) +# include +# endif +typedef NSModule module_handle_t; +#elif defined(HAVE_IMAGE_H) +typedef int module_handle_t; +#elif defined(WIN32) || defined(UNDER_CE) +typedef void * module_handle_t; +#elif defined(HAVE_DL_DLOPEN) + +#elif defined(HAVE_DL_SHL_LOAD) +typedef shl_t module_handle_t; +#endif +typedef void * module_handle_t; +/** + * Internal module descriptor + */ +struct module_t +{ + VLC_COMMON_MEMBERS + + /* + * Variables set by the module to identify itself + */ + char *psz_shortname; /**< Module name */ + char *psz_longname; /**< Module descriptive name */ + char *psz_help; /**< Long help string for "special" modules */ + + /** Shortcuts to the module */ + char *pp_shortcuts[ MODULE_SHORTCUT_MAX ]; + + char *psz_capability; /**< Capability */ + int i_score; /**< Score for the capability */ + uint32_t i_cpu; /**< Required CPU capabilities */ + + bool b_unloadable; /**< Can we be dlclosed? */ + bool b_reentrant; /**< Are we reentrant? */ + bool b_submodule; /**< Is this a submodule? */ + + /* Callbacks */ + int ( * pf_activate ) ( vlc_object_t * ); + void ( * pf_deactivate ) ( vlc_object_t * ); + + /* + * Variables set by the module to store its config options + */ + module_config_t *p_config; /* Module configuration structure */ + size_t confsize; /* Number of module_config_t items */ + unsigned int i_config_items; /* number of configuration items */ + unsigned int i_bool_items; /* number of bool config items */ + + /* + * Variables used internally by the module manager + */ + /* Plugin-specific stuff */ + module_handle_t handle; /* Unique handle */ + char * psz_filename; /* Module filename */ + + bool b_builtin; /* Set to true if the module is built in */ + bool b_loaded; /* Set to true if the dll is loaded */ +}; + + +#define module_InitBank(a) __module_InitBank(VLC_OBJECT(a)) +void __module_InitBank ( vlc_object_t * ); +#define module_LoadBuiltins(a) __module_LoadBuiltins(VLC_OBJECT(a)) +void __module_LoadBuiltins ( vlc_object_t * ); +#define module_LoadPlugins(a) __module_LoadPlugins(VLC_OBJECT(a)) +void __module_LoadPlugins ( vlc_object_t * ); +#define module_EndBank(a) __module_EndBank(VLC_OBJECT(a)) +void __module_EndBank ( vlc_object_t * ); +#define module_ResetBank(a) __module_ResetBank(VLC_OBJECT(a)) +void __module_ResetBank ( vlc_object_t * ); + +/* Low-level OS-dependent handler */ +int module_Call (module_t *); +int module_Load (vlc_object_t *, const char *, module_handle_t *); +void module_Unload (module_handle_t); + +/* Plugins cache */ +void CacheMerge (vlc_object_t *, module_t *, module_t *); +void CacheLoad (vlc_object_t * ); +void CacheSave (vlc_object_t * ); +module_cache_t * CacheFind (const char *, int64_t, int64_t); + +#endif /* !__LIBVLC_MODULES_H */ diff --git a/VLC/mtime.c b/VLC/mtime.c new file mode 100644 index 0000000..8936edf --- /dev/null +++ b/VLC/mtime.c @@ -0,0 +1,563 @@ +/***************************************************************************** + * mtime.c: high resolution time management functions + * Functions are prototyped in vlc_mtime.h. + ***************************************************************************** + * Copyright (C) 1998-2007 the VideoLAN team + * Copyright © 2006-2007 Rémi Denis-Courmont + * $Id$ + * + * Authors: Vincent Seguin + * Rémi Denis-Courmont + * Gisle Vanem + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#include /* clock_gettime(), clock_nanosleep() */ +#include +#include + +#ifdef HAVE_UNISTD_H +# include /* select() */ +#endif + +#ifdef HAVE_KERNEL_OS_H +# include +#endif + +#if defined( WIN32 ) || defined( UNDER_CE ) +# include +# include +#endif + +#if defined( UNDER_CE ) +# include +#endif + +#if defined(HAVE_SYS_TIME_H) +# include +#endif + + + +#if defined(HAVE_NANOSLEEP) && !defined(HAVE_DECL_NANOSLEEP) +int nanosleep(struct timespec *, struct timespec *); +#endif + +#if !defined (_POSIX_CLOCK_SELECTION) +# define _POSIX_CLOCK_SELECTION (-1) +#endif + +# if (_POSIX_CLOCK_SELECTION < 0) +/* + * We cannot use the monotonic clock is clock selection is not available, + * as it would screw vlc_cond_timedwait() completely. Instead, we have to + * stick to the realtime clock. Nevermind it screws everything when ntpdate + * warps the wall clock. + */ +# undef CLOCK_MONOTONIC +# define CLOCK_MONOTONIC CLOCK_REALTIME +#elif !defined (HAVE_CLOCK_NANOSLEEP) +/* Clock selection without clock in the first place, I don't think so. */ +# error We have quite a situation here! Fix me if it ever happens. +#endif + +/** + * Return a date in a readable format + * + * This function converts a mtime date into a string. + * psz_buffer should be a buffer long enough to store the formatted + * date. + * \param date to be converted + * \param psz_buffer should be a buffer at least MSTRTIME_MAX_SIZE characters + * \return psz_buffer is returned so this can be used as printf parameter. + */ +char *mstrtime( char *psz_buffer, mtime_t date ) +{ + static const mtime_t ll1000 = 1000, ll60 = 60, ll24 = 24; + + snprintf( psz_buffer, MSTRTIME_MAX_SIZE, "%02d:%02d:%02d-%03d.%03d", + (int) (date / (ll1000 * ll1000 * ll60 * ll60) % ll24), + (int) (date / (ll1000 * ll1000 * ll60) % ll60), + (int) (date / (ll1000 * ll1000) % ll60), + (int) (date / ll1000 % ll1000), + (int) (date % ll1000) ); + return( psz_buffer ); +} + +/** + * Convert seconds to a time in the format h:mm:ss. + * + * This function is provided for any interface function which need to print a + * time string in the format h:mm:ss + * date. + * \param secs the date to be converted + * \param psz_buffer should be a buffer at least MSTRTIME_MAX_SIZE characters + * \return psz_buffer is returned so this can be used as printf parameter. + */ +char *secstotimestr( char *psz_buffer, int i_seconds ) +{ + int i_hours, i_mins; + i_mins = i_seconds / 60; + i_hours = i_mins / 60 ; + if( i_hours ) + { + snprintf( psz_buffer, MSTRTIME_MAX_SIZE, "%d:%2.2d:%2.2d", + (int) i_hours, + (int) (i_mins % 60), + (int) (i_seconds % 60) ); + } + else + { + snprintf( psz_buffer, MSTRTIME_MAX_SIZE, "%2.2d:%2.2d", + (int) i_mins , + (int) (i_seconds % 60) ); + } + return( psz_buffer ); +} + +#if defined (HAVE_CLOCK_NANOSLEEP) +static unsigned prec = 0; + +static void mprec_once( void ) +{ + struct timespec ts; + if( clock_getres( CLOCK_MONOTONIC, &ts )) + clock_getres( CLOCK_REALTIME, &ts ); + + prec = ts.tv_nsec / 1000; +} +#endif + +/** + * Return a value that is no bigger than the clock precision + * (possibly zero). + */ +static inline unsigned mprec( void ) +{ +#if defined (HAVE_CLOCK_NANOSLEEP) + static pthread_once_t once = PTHREAD_ONCE_INIT; + pthread_once( &once, mprec_once ); + return prec; +#else + return 0; +#endif +} + +/** + * Return high precision date + * + * Use a 1 MHz clock when possible, or 1 kHz + * + * Beware ! It doesn't reflect the actual date (since epoch), but can be the machine's uptime or anything (when monotonic clock is used) + */ +mtime_t mdate( void ) +{ + mtime_t res; + +#if defined (HAVE_CLOCK_NANOSLEEP) + struct timespec ts; + + /* Try to use POSIX monotonic clock if available */ + if( clock_gettime( CLOCK_MONOTONIC, &ts ) == EINVAL ) + /* Run-time fallback to real-time clock (always available) */ + (void)clock_gettime( CLOCK_REALTIME, &ts ); + + res = ((mtime_t)ts.tv_sec * (mtime_t)1000000) + + (mtime_t)(ts.tv_nsec / 1000); + +#elif defined( HAVE_KERNEL_OS_H ) + res = real_time_clock_usecs(); + +#elif defined( WIN32 ) || defined( UNDER_CE ) + /* We don't need the real date, just the value of a high precision timer */ + static mtime_t freq = INT64_C(-1); + + if( freq == INT64_C(-1) ) + { + /* Extract from the Tcl source code: + * (http://www.cs.man.ac.uk/fellowsd-bin/TIP/7.html) + * + * Some hardware abstraction layers use the CPU clock + * in place of the real-time clock as a performance counter + * reference. This results in: + * - inconsistent results among the processors on + * multi-processor systems. + * - unpredictable changes in performance counter frequency + * on "gearshift" processors such as Transmeta and + * SpeedStep. + * There seems to be no way to test whether the performance + * counter is reliable, but a useful heuristic is that + * if its frequency is 1.193182 MHz or 3.579545 MHz, it's + * derived from a colorburst crystal and is therefore + * the RTC rather than the TSC. If it's anything else, we + * presume that the performance counter is unreliable. + */ + LARGE_INTEGER buf; + + freq = ( QueryPerformanceFrequency( &buf ) && + (buf.QuadPart == INT64_C(1193182) || buf.QuadPart == INT64_C(3579545) ) ) + ? buf.QuadPart : 0; + +#if defined( WIN32 ) + /* on windows 2000, XP and Vista detect if there are two + cores there - that makes QueryPerformanceFrequency in + any case not trustable? + (may also be true, for single cores with adaptive + CPU frequency and active power management?) + */ + HINSTANCE h_Kernel32 = LoadLibrary(_T("kernel32.dll")); + if(h_Kernel32) + { + void WINAPI (*pf_GetSystemInfo)(LPSYSTEM_INFO); + pf_GetSystemInfo = (void WINAPI (*)(LPSYSTEM_INFO)) + GetProcAddress(h_Kernel32, _T("GetSystemInfo")); + if(pf_GetSystemInfo) + { + SYSTEM_INFO system_info; + pf_GetSystemInfo(&system_info); + if(system_info.dwNumberOfProcessors > 1) + freq = 0; + } + FreeLibrary(h_Kernel32); + } +#endif + } + + if( freq != 0 ) + { + LARGE_INTEGER counter; + QueryPerformanceCounter (&counter); + + /* Convert to from (1/freq) to microsecond resolution */ + /* We need to split the division to avoid 63-bits overflow */ + lldiv_t d = lldiv (counter.QuadPart, freq); + + res = (d.quot * 1000000) + ((d.rem * 1000000) / freq); + } + else + { + /* Fallback on timeGetTime() which has a milisecond resolution + * (actually, best case is about 5 ms resolution) + * timeGetTime() only returns a DWORD thus will wrap after + * about 49.7 days so we try to detect the wrapping. */ + + static CRITICAL_SECTION date_lock; + static mtime_t i_previous_time = INT64_C(-1); + static int i_wrap_counts = -1; + + if( i_wrap_counts == -1 ) + { + /* Initialization */ +#if defined( WIN32 ) + i_previous_time = INT64_C(1000) * timeGetTime(); +#else + i_previous_time = INT64_C(1000) * GetTickCount(); +#endif + InitializeCriticalSection( &date_lock ); + i_wrap_counts = 0; + } + + EnterCriticalSection( &date_lock ); +#if defined( WIN32 ) + res = INT64_C(1000) * + (i_wrap_counts * INT64_C(0x100000000) + timeGetTime()); +#else + res = INT64_C(1000) * + (i_wrap_counts * INT64_C(0x100000000) + GetTickCount()); +#endif + if( i_previous_time > res ) + { + /* Counter wrapped */ + i_wrap_counts++; + res += INT64_C(0x100000000) * 1000; + } + i_previous_time = res; + LeaveCriticalSection( &date_lock ); + } +#else + struct timeval tv_date; + + /* gettimeofday() cannot fail given &tv_date is a valid address */ + (void)gettimeofday( &tv_date, NULL ); + res = (mtime_t) tv_date.tv_sec * 1000000 + (mtime_t) tv_date.tv_usec; +#endif + + return res; +} + +/** + * Wait for a date + * + * This function uses select() and an system date function to wake up at a + * precise date. It should be used for process synchronization. If current date + * is posterior to wished date, the function returns immediately. + * \param date The date to wake up at + */ +void mwait( mtime_t date ) +{ + /* If the deadline is already elapsed, or within the clock precision, + * do not even bother the system timer. */ + date -= mprec(); + +#if defined (HAVE_CLOCK_NANOSLEEP) + lldiv_t d = lldiv( date, 1000000 ); + struct timespec ts = { d.quot, d.rem * 1000 }; + + int val; + while( ( val = clock_nanosleep( CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, + NULL ) ) == EINTR ); + if( val == EINVAL ) + { + ts.tv_sec = d.quot; ts.tv_nsec = d.rem * 1000; + while( clock_nanosleep( CLOCK_REALTIME, 0, &ts, NULL ) == EINTR ); + } +#else + + mtime_t delay = date - mdate(); + if( delay > 0 ) + msleep( delay ); + +#endif +} + +/** + * More precise sleep() + * + * Portable usleep() function. + * \param delay the amount of time to sleep + */ +void msleep( mtime_t delay ) +{ +#if defined( HAVE_CLOCK_NANOSLEEP ) + lldiv_t d = lldiv( delay, 1000000 ); + struct timespec ts = { d.quot, d.rem * 1000 }; + + int val; + while( ( val = clock_nanosleep( CLOCK_MONOTONIC, 0, &ts, &ts ) ) == EINTR ); + if( val == EINVAL ) + { + ts.tv_sec = d.quot; ts.tv_nsec = d.rem * 1000; + while( clock_nanosleep( CLOCK_REALTIME, 0, &ts, &ts ) == EINTR ); + } + +#elif defined( HAVE_KERNEL_OS_H ) + snooze( delay ); + +#elif defined( WIN32 ) || defined( UNDER_CE ) + for (delay /= 1000; delay > 0x7fffffff; delay -= 0x7fffffff) + Sleep (0x7fffffff); + Sleep (delay); + +#elif defined( HAVE_NANOSLEEP ) + struct timespec ts_delay; + + ts_delay.tv_sec = delay / 1000000; + ts_delay.tv_nsec = (delay % 1000000) * 1000; + + while( nanosleep( &ts_delay, &ts_delay ) && ( errno == EINTR ) ); + +#else + struct timeval tv_delay; + + tv_delay.tv_sec = delay / 1000000; + tv_delay.tv_usec = delay % 1000000; + + /* If a signal is caught, you are screwed. Update your OS to nanosleep() + * or clock_nanosleep() if this is an issue. */ + select( 0, NULL, NULL, NULL, &tv_delay ); +#endif +} + +/* + * Date management (internal and external) + */ + +/** + * Initialize a date_t. + * + * \param date to initialize + * \param divider (sample rate) numerator + * \param divider (sample rate) denominator + */ + +void date_Init( date_t *p_date, uint32_t i_divider_n, uint32_t i_divider_d ) +{ + p_date->date = 0; + p_date->i_divider_num = i_divider_n; + p_date->i_divider_den = i_divider_d; + p_date->i_remainder = 0; +} + +/** + * Change a date_t. + * + * \param date to change + * \param divider (sample rate) numerator + * \param divider (sample rate) denominator + */ + +void date_Change( date_t *p_date, uint32_t i_divider_n, uint32_t i_divider_d ) +{ + /* change time scale of remainder */ + p_date->i_remainder = p_date->i_remainder * i_divider_n / p_date->i_divider_num; + p_date->i_divider_num = i_divider_n; + p_date->i_divider_den = i_divider_d; +} + +/** + * Set the date value of a date_t. + * + * \param date to set + * \param date value + */ +void date_Set( date_t *p_date, mtime_t i_new_date ) +{ + p_date->date = i_new_date; + p_date->i_remainder = 0; +} + +/** + * Get the date of a date_t + * + * \param date to get + * \return date value + */ +mtime_t date_Get( const date_t *p_date ) +{ + return p_date->date; +} + +/** + * Move forwards or backwards the date of a date_t. + * + * \param date to move + * \param difference value + */ +void date_Move( date_t *p_date, mtime_t i_difference ) +{ + p_date->date += i_difference; +} + +/** + * Increment the date and return the result, taking into account + * rounding errors. + * + * \param date to increment + * \param incrementation in number of samples + * \return date value + */ +mtime_t date_Increment( date_t *p_date, uint32_t i_nb_samples ) +{ + mtime_t i_dividend = (mtime_t)i_nb_samples * 1000000 * p_date->i_divider_den; + p_date->date += i_dividend / p_date->i_divider_num; + p_date->i_remainder += (int)(i_dividend % p_date->i_divider_num); + + if( p_date->i_remainder >= p_date->i_divider_num ) + { + /* This is Bresenham algorithm. */ + assert( p_date->i_remainder < 2*p_date->i_divider_num); + p_date->date += 1; + p_date->i_remainder -= p_date->i_divider_num; + } + + return p_date->date; +} + +#ifndef HAVE_GETTIMEOFDAY + +#ifdef WIN32 + +/* + * Number of micro-seconds between the beginning of the Windows epoch + * (Jan. 1, 1601) and the Unix epoch (Jan. 1, 1970). + * + * This assumes all Win32 compilers have 64-bit support. + */ +#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS) || defined(__WATCOMC__) +# define DELTA_EPOCH_IN_USEC 11644473600000000Ui64 +#else +# define DELTA_EPOCH_IN_USEC 11644473600000000ULL +#endif + +static uint64_t filetime_to_unix_epoch (const FILETIME *ft) +{ + uint64_t res = (uint64_t) ft->dwHighDateTime << 32; + + res |= ft->dwLowDateTime; + res /= 10; /* from 100 nano-sec periods to usec */ + res -= DELTA_EPOCH_IN_USEC; /* from Win epoch to Unix epoch */ + return (res); +} + +static int gettimeofday (struct timeval *tv, void *tz ) +{ + FILETIME ft; + uint64_t tim; + + if (!tv) { + return VLC_EGENERIC; + } + GetSystemTimeAsFileTime (&ft); + tim = filetime_to_unix_epoch (&ft); + tv->tv_sec = (long) (tim / 1000000L); + tv->tv_usec = (long) (tim % 1000000L); + return (0); +} + +#endif + +#endif + +/** + * @return NTP 64-bits timestamp in host byte order. + */ +uint64_t NTPtime64 (void) +{ + struct timespec ts; +#if defined (CLOCK_REALTIME) + clock_gettime (CLOCK_REALTIME, &ts); +#else + { + struct timeval tv; + gettimeofday (&tv, NULL); + ts.tv_sec = tv.tv_sec; + ts.tv_nsec = tv.tv_usec * 1000; + } +#endif + + /* Convert nanoseconds to 32-bits fraction (232 picosecond units) */ + uint64_t t = (uint64_t)(ts.tv_nsec) << 32; + t /= 1000000000; + + + /* There is 70 years (incl. 17 leap ones) offset to the Unix Epoch. + * No leap seconds during that period since they were not invented yet. + */ + assert (t < 0x100000000); + t |= ((70LL * 365 + 17) * 24 * 60 * 60 + ts.tv_sec) << 32; + return t; +} + diff --git a/VLC/objects.c b/VLC/objects.c new file mode 100644 index 0000000..db4e555 --- /dev/null +++ b/VLC/objects.c @@ -0,0 +1,1557 @@ +/***************************************************************************** + * objects.c: vlc_object_t handling + ***************************************************************************** + * Copyright (C) 2004-2008 the VideoLAN team + * + * Authors: Samuel Hocevar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/** + * \file + * This file contains the functions to handle the vlc_object_t type + */ + + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#include "libvlc.h" +#include "vlc_vout.h" +#include "vlc_aout.h" +#include "audio_output/aout_internal.h" + +#include "vlc_access.h" +#include "vlc_demux.h" +#include "vlc_stream.h" + +#include "vlc_sout.h" +#include "stream_output/stream_output.h" + +#include "vlc_interface.h" +#include "vlc_codec.h" +#include "vlc_filter.h" + +#include "variables.h" +#ifndef WIN32 +# include +#else +# include +# include +# include /* ENOSYS */ +#endif +#include + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +static int DumpCommand( vlc_object_t *, char const *, + vlc_value_t, vlc_value_t, void * ); + +static vlc_object_t * FindObject ( vlc_object_t *, int, int ); +static vlc_object_t * FindObjectName( vlc_object_t *, const char *, int ); +static void PrintObject ( vlc_object_t *, const char * ); +static void DumpStructure ( vlc_object_t *, int, char * ); + +static vlc_list_t * NewList ( int ); +static void ListReplace ( vlc_list_t *, vlc_object_t *, int ); +/*static void ListAppend ( vlc_list_t *, vlc_object_t * );*/ +static int CountChildren ( vlc_object_t *, int ); +static void ListChildren ( vlc_list_t *, vlc_object_t *, int ); + +static void vlc_object_destroy( vlc_object_t *p_this ); +static void vlc_object_detach_unlocked (vlc_object_t *p_this); + +#ifdef LIBVLC_REFCHECK +static vlc_threadvar_t held_objects; +typedef struct held_list_t +{ + struct held_list_t *next; + vlc_object_t *obj; +} held_list_t; +static void held_objects_destroy (void *); +#endif + +/***************************************************************************** + * Local structure lock + *****************************************************************************/ +static vlc_mutex_t structure_lock; +static unsigned object_counter = 0; + +void *__vlc_custom_create( vlc_object_t *p_this, size_t i_size, + int i_type, const char *psz_type ) +{ + vlc_object_t *p_new; + vlc_object_internals_t *p_priv; + + /* NOTE: + * VLC objects are laid out as follow: + * - first the LibVLC-private per-object data, + * - then VLC_COMMON members from vlc_object_t, + * - finally, the type-specific data (if any). + * + * This function initializes the LibVLC and common data, + * and zeroes the rest. + */ + p_priv = calloc( 1, sizeof( *p_priv ) + i_size ); + if( p_priv == NULL ) + return NULL; + + assert (i_size >= sizeof (vlc_object_t)); + p_new = (vlc_object_t *)(p_priv + 1); + + p_new->i_object_type = i_type; + p_new->psz_object_type = psz_type; + p_new->psz_object_name = NULL; + + p_new->b_die = false; + p_new->b_error = false; + p_new->b_dead = false; + p_new->b_force = false; + + p_new->psz_header = NULL; + + if (p_this) + p_new->i_flags = p_this->i_flags + & (OBJECT_FLAGS_NODBG|OBJECT_FLAGS_QUIET|OBJECT_FLAGS_NOINTERACT); + + p_priv->p_vars = calloc( sizeof( variable_t ), 16 ); + + if( !p_priv->p_vars ) + { + free( p_priv ); + return NULL; + } + + libvlc_global_data_t *p_libvlc_global; + if( p_this == NULL ) + { + /* Only the global root object is created out of the blue */ + p_libvlc_global = (libvlc_global_data_t *)p_new; + p_new->p_libvlc = NULL; + + object_counter = 0; /* reset */ + p_priv->next = p_priv->prev = p_new; + vlc_mutex_init( &structure_lock ); +#ifdef LIBVLC_REFCHECK + /* TODO: use the destruction callback to track ref leaks */ + vlc_threadvar_create( &held_objects, held_objects_destroy ); +#endif + } + else + { + p_libvlc_global = vlc_global(); + if( i_type == VLC_OBJECT_LIBVLC ) + p_new->p_libvlc = (libvlc_int_t*)p_new; + else + p_new->p_libvlc = p_this->p_libvlc; + } + + vlc_spin_init( &p_priv->ref_spin ); + p_priv->i_refcount = 1; + p_priv->pf_destructor = NULL; + p_priv->b_thread = false; + p_new->p_parent = NULL; + p_priv->pp_children = NULL; + p_priv->i_children = 0; + + p_new->p_private = NULL; + + /* Initialize mutexes and condvars */ + vlc_mutex_init( &p_priv->lock ); + vlc_cond_init( p_new, &p_priv->wait ); + vlc_mutex_init( &p_priv->var_lock ); + vlc_spin_init( &p_priv->spin ); + p_priv->pipes[0] = p_priv->pipes[1] = -1; + + p_priv->next = VLC_OBJECT (p_libvlc_global); +#if !defined (LIBVLC_REFCHECK) + /* ... */ +#elif defined (LIBVLC_USE_PTHREAD) + p_priv->creator_id = pthread_self (); +#elif defined (WIN32) + p_priv->creator_id = GetCurrentThreadId (); +#endif + vlc_mutex_lock( &structure_lock ); + p_priv->prev = vlc_internals (p_libvlc_global)->prev; + vlc_internals (p_libvlc_global)->prev = p_new; + vlc_internals (p_priv->prev)->next = p_new; + p_new->i_object_id = object_counter++; /* fetch THEN increment */ + vlc_mutex_unlock( &structure_lock ); + + if( i_type == VLC_OBJECT_LIBVLC ) + { + var_Create( p_new, "list", VLC_VAR_STRING | VLC_VAR_ISCOMMAND ); + var_AddCallback( p_new, "list", DumpCommand, NULL ); + var_Create( p_new, "tree", VLC_VAR_STRING | VLC_VAR_ISCOMMAND ); + var_AddCallback( p_new, "tree", DumpCommand, NULL ); + var_Create( p_new, "vars", VLC_VAR_STRING | VLC_VAR_ISCOMMAND ); + var_AddCallback( p_new, "vars", DumpCommand, NULL ); + } + + return p_new; +} + + +/** + * Allocates and initializes a vlc object. + * + * @param i_type known object type (all of them are negative integer values), + * or object byte size (always positive). + * + * @return the new object, or NULL on error. + */ +void * __vlc_object_create( vlc_object_t *p_this, int i_type ) +{ + const char * psz_type; + size_t i_size; + + switch( i_type ) + { + case VLC_OBJECT_INTF: + i_size = sizeof(intf_thread_t); + psz_type = "interface"; + break; + case VLC_OBJECT_DECODER: + i_size = sizeof(decoder_t); + psz_type = "decoder"; + break; + case VLC_OBJECT_PACKETIZER: + i_size = sizeof(decoder_t); + psz_type = "packetizer"; + break; + case VLC_OBJECT_ENCODER: + i_size = sizeof(encoder_t); + psz_type = "encoder"; + break; + case VLC_OBJECT_AOUT: + i_size = sizeof(aout_instance_t); + psz_type = "audio output"; + break; + case VLC_OBJECT_OPENGL: + i_size = sizeof( vout_thread_t ); + psz_type = "opengl"; + break; + case VLC_OBJECT_ANNOUNCE: + i_size = sizeof( announce_handler_t ); + psz_type = "announce"; + break; + default: + assert( i_type > 0 ); /* unknown type?! */ + i_size = i_type; + i_type = VLC_OBJECT_GENERIC; + psz_type = "generic"; + break; + } + + return vlc_custom_create( p_this, i_size, i_type, psz_type ); +} + + +/** + **************************************************************************** + * Set the destructor of a vlc object + * + * This function sets the destructor of the vlc object. It will be called + * when the object is destroyed when the its refcount reaches 0. + * (It is called by the internal function vlc_object_destroy()) + *****************************************************************************/ +void __vlc_object_set_destructor( vlc_object_t *p_this, + vlc_destructor_t pf_destructor ) +{ + vlc_object_internals_t *p_priv = vlc_internals(p_this ); + p_priv->pf_destructor = pf_destructor; +} + +/** + **************************************************************************** + * Destroy a vlc object (Internal) + * + * This function destroys an object that has been previously allocated with + * vlc_object_create. The object's refcount must be zero and it must not be + * attached to other objects in any way. + *****************************************************************************/ +static void vlc_object_destroy( vlc_object_t *p_this ) +{ + vlc_object_internals_t *p_priv = vlc_internals( p_this ); + + /* Objects are always detached beforehand */ + assert( !p_this->p_parent ); + + /* Send a kill to the object's thread if applicable */ + vlc_object_kill( p_this ); + + /* If we are running on a thread, wait until it ends */ + if( p_priv->b_thread ) + { + msg_Warn (p_this->p_libvlc, /* do NOT use a dead object for logging! */ + "%s %d destroyed while thread alive (VLC might crash)", + p_this->psz_object_type, p_this->i_object_id); + vlc_thread_join( p_this ); + } + + /* Call the custom "subclass" destructor */ + if( p_priv->pf_destructor ) + p_priv->pf_destructor( p_this ); + + /* Destroy the associated variables, starting from the end so that + * no memmove calls have to be done. */ + while( p_priv->i_vars ) + { + var_Destroy( p_this, p_priv->p_vars[p_priv->i_vars - 1].psz_name ); + } + + free( p_priv->p_vars ); + vlc_mutex_destroy( &p_priv->var_lock ); + + free( p_this->psz_header ); + + if( p_this->p_libvlc == NULL ) + { +#ifndef NDEBUG + libvlc_global_data_t *p_global = (libvlc_global_data_t *)p_this; + + assert( p_global == vlc_global() ); + /* Test for leaks */ + if (p_priv->next != p_this) + { + vlc_object_t *leaked = p_priv->next, *first = leaked; + do + { + /* We are leaking this object */ + fprintf( stderr, + "ERROR: leaking object (id:%i, type:%s, name:%s)\n", + leaked->i_object_id, leaked->psz_object_type, + leaked->psz_object_name ); + /* Dump libvlc object to ease debugging */ + vlc_object_dump( leaked ); + fflush(stderr); + leaked = vlc_internals (leaked)->next; + } + while (leaked != first); + + /* Dump global object to ease debugging */ + vlc_object_dump( p_this ); + /* Strongly abort, cause we want these to be fixed */ + abort(); + } +#endif + + /* We are the global object ... no need to lock. */ + vlc_mutex_destroy( &structure_lock ); +#ifdef LIBVLC_REFCHECK + held_objects_destroy( vlc_threadvar_get( &held_objects ) ); + vlc_threadvar_delete( &held_objects ); +#endif + } + + FREENULL( p_this->psz_object_name ); + +#if defined(WIN32) || defined(UNDER_CE) + /* if object has an associated thread, close it now */ + if( p_priv->thread_id ) + CloseHandle(p_priv->thread_id); +#endif + + vlc_spin_destroy( &p_priv->ref_spin ); + vlc_mutex_destroy( &p_priv->lock ); + vlc_cond_destroy( &p_priv->wait ); + vlc_spin_destroy( &p_priv->spin ); + if( p_priv->pipes[1] != -1 ) + close( p_priv->pipes[1] ); + if( p_priv->pipes[0] != -1 ) + close( p_priv->pipes[0] ); + + free( p_priv ); +} + + +/** Inter-object signaling */ + +void __vlc_object_lock( vlc_object_t *obj ) +{ + vlc_mutex_lock( &(vlc_internals(obj)->lock) ); +} + +void __vlc_object_unlock( vlc_object_t *obj ) +{ + vlc_assert_locked( &(vlc_internals(obj)->lock) ); + vlc_mutex_unlock( &(vlc_internals(obj)->lock) ); +} + +#ifdef WIN32 +# include +# include + +/** + * select()-able pipes emulated using Winsock + */ +static int pipe (int fd[2]) +{ + SOCKADDR_IN addr; + int addrlen = sizeof (addr); + + SOCKET l = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP), a, + c = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); + if ((l == INVALID_SOCKET) || (c == INVALID_SOCKET)) + goto error; + + memset (&addr, 0, sizeof (addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + if (bind (l, (PSOCKADDR)&addr, sizeof (addr)) + || getsockname (l, (PSOCKADDR)&addr, &addrlen) + || listen (l, 1) + || connect (c, (PSOCKADDR)&addr, addrlen)) + goto error; + + a = accept (l, NULL, NULL); + if (a == INVALID_SOCKET) + goto error; + + closesocket (l); + //shutdown (a, 0); + //shutdown (c, 1); + fd[0] = c; + fd[1] = a; + return 0; + +error: + if (l != INVALID_SOCKET) + closesocket (l); + if (c != INVALID_SOCKET) + closesocket (c); + return -1; +} + +#undef read +#define read( a, b, c ) recv (a, b, c, 0) +#undef write +#define write( a, b, c ) send (a, b, c, 0) +#undef close +#define close( a ) closesocket (a) +#endif /* WIN32 */ + +/** + * Returns the readable end of a pipe that becomes readable once termination + * of the object is requested (vlc_object_kill()). + * This can be used to wake-up out of a select() or poll() event loop, such + * typically when doing network I/O. + * + * Note that the pipe will remain the same for the lifetime of the object. + * DO NOT read the pipe nor close it yourself. Ever. + * + * @param obj object that would be "killed" + * @return a readable pipe descriptor, or -1 on error. + */ +int __vlc_object_waitpipe( vlc_object_t *obj ) +{ + int pfd[2] = { -1, -1 }; + vlc_object_internals_t *internals = vlc_internals( obj ); + bool killed = false; + + vlc_spin_lock (&internals->spin); + if (internals->pipes[0] == -1) + { + /* This can only ever happen if someone killed us without locking: */ + assert (internals->pipes[1] == -1); + vlc_spin_unlock (&internals->spin); + + if (pipe (pfd)) + return -1; + + vlc_spin_lock (&internals->spin); + if (internals->pipes[0] == -1) + { + internals->pipes[0] = pfd[0]; + internals->pipes[1] = pfd[1]; + pfd[0] = pfd[1] = -1; + } + killed = obj->b_die; + } + vlc_spin_unlock (&internals->spin); + + if (killed) + { + /* Race condition: vlc_object_kill() already invoked! */ + int fd; + + vlc_spin_lock (&internals->spin); + fd = internals->pipes[1]; + internals->pipes[1] = -1; + vlc_spin_unlock (&internals->spin); + + msg_Dbg (obj, "waitpipe: object already dying"); + if (fd != -1) + close (fd); + } + + /* Race condition: two threads call pipe() - unlikely */ + if (pfd[0] != -1) + close (pfd[0]); + if (pfd[1] != -1) + close (pfd[1]); + + return internals->pipes[0]; +} + + +/** + * Waits for the object to be signaled (using vlc_object_signal()). + * It is assumed that the caller has locked the object. This function will + * unlock the object, and lock it again before returning. + * If the object was signaled before the caller locked the object, it is + * undefined whether the signal will be lost or will wake the process. + * + * @return true if the object is dying and should terminate. + */ +void __vlc_object_wait( vlc_object_t *obj ) +{ + vlc_object_internals_t *priv = vlc_internals( obj ); + vlc_assert_locked( &priv->lock); + vlc_cond_wait( &priv->wait, &priv->lock ); +} + + +/** + * Waits for the object to be signaled (using vlc_object_signal()), or for + * a timer to expire. It is asserted that the caller holds the object lock. + * + * @return 0 if the object was signaled before the timer expiration, or + * ETIMEDOUT if the timer expired without any signal. + */ +int __vlc_object_timedwait( vlc_object_t *obj, mtime_t deadline ) +{ + vlc_object_internals_t *priv = vlc_internals( obj ); + vlc_assert_locked( &priv->lock); + return vlc_cond_timedwait( &priv->wait, &priv->lock, deadline ); +} + + +/** + * Signals an object for which the lock is held. + * At least one thread currently sleeping in vlc_object_wait() or + * vlc_object_timedwait() will wake up, assuming that there is at least one + * such thread in the first place. Otherwise, it is undefined whether the + * signal will be lost or will wake up one or more thread later. + */ +void __vlc_object_signal_unlocked( vlc_object_t *obj ) +{ + vlc_assert_locked (&(vlc_internals(obj)->lock)); + vlc_cond_signal( &(vlc_internals(obj)->wait) ); +} + + +/** + * Requests termination of an object. + * If the object is LibVLC, also request to terminate all its children. + */ +void __vlc_object_kill( vlc_object_t *p_this ) +{ + vlc_object_internals_t *priv = vlc_internals( p_this ); + int fd; + + vlc_object_lock( p_this ); + p_this->b_die = true; + + vlc_spin_lock (&priv->spin); + fd = priv->pipes[1]; + priv->pipes[1] = -1; + vlc_spin_unlock (&priv->spin); + + if( fd != -1 ) + { + msg_Dbg (p_this, "waitpipe: object killed"); + close (fd); + } + + vlc_object_signal_unlocked( p_this ); + /* This also serves as a memory barrier toward vlc_object_alive(): */ + vlc_object_unlock( p_this ); +} + + +/** + * Find an object given its ID. + * + * This function looks for the object whose i_object_id field is i_id. + * This function is slow, and often used to hide bugs. Do not use it. + * If you need to retain reference to an object, yield the object pointer with + * vlc_object_yield(), use the pointer as your reference, and call + * vlc_object_release() when you're done. + */ +void * vlc_object_get( int i_id ) +{ + libvlc_global_data_t *p_libvlc_global = vlc_global(); + vlc_object_t *obj = NULL; +#ifndef NDEBUG + vlc_object_t *caller = vlc_threadobj (); + + if (caller) + msg_Dbg (caller, "uses deprecated vlc_object_get(%d)", i_id); + else + fprintf (stderr, "main thread uses deprecated vlc_object_get(%d)\n", + i_id); +#endif + vlc_mutex_lock( &structure_lock ); + + for( obj = vlc_internals (p_libvlc_global)->next; + obj != VLC_OBJECT (p_libvlc_global); + obj = vlc_internals (obj)->next ) + { + if( obj->i_object_id == i_id ) + { + vlc_object_yield( obj ); + goto out; + } + } + obj = NULL; +#ifndef NDEBUG + if (caller) + msg_Warn (caller, "wants non-existing object %d", i_id); + else + fprintf (stderr, "main thread wants non-existing object %d\n", i_id); +#endif +out: + vlc_mutex_unlock( &structure_lock ); + return obj; +} + +/** + **************************************************************************** + * find a typed object and increment its refcount + ***************************************************************************** + * This function recursively looks for a given object type. i_mode can be one + * of FIND_PARENT, FIND_CHILD or FIND_ANYWHERE. + *****************************************************************************/ +void * __vlc_object_find( vlc_object_t *p_this, int i_type, int i_mode ) +{ + vlc_object_t *p_found; + + /* If we are of the requested type ourselves, don't look further */ + if( !(i_mode & FIND_STRICT) && p_this->i_object_type == i_type ) + { + vlc_object_yield( p_this ); + return p_this; + } + + /* Otherwise, recursively look for the object */ + if ((i_mode & 0x000f) == FIND_ANYWHERE) + { +#ifndef NDEBUG + if (i_type == VLC_OBJECT_PLAYLIST) + msg_Err (p_this, "using vlc_object_find(VLC_OBJECT_PLAYLIST) " + "instead of pl_Yield()"); +#endif + return vlc_object_find (p_this->p_libvlc, i_type, + (i_mode & ~0x000f)|FIND_CHILD); + } + + vlc_mutex_lock( &structure_lock ); + p_found = FindObject( p_this, i_type, i_mode ); + vlc_mutex_unlock( &structure_lock ); + return p_found; +} + +/** + **************************************************************************** + * find a named object and increment its refcount + ***************************************************************************** + * This function recursively looks for a given object name. i_mode can be one + * of FIND_PARENT, FIND_CHILD or FIND_ANYWHERE. + *****************************************************************************/ +void * __vlc_object_find_name( vlc_object_t *p_this, const char *psz_name, + int i_mode ) +{ + vlc_object_t *p_found; + + /* If have the requested name ourselves, don't look further */ + if( !(i_mode & FIND_STRICT) + && p_this->psz_object_name + && !strcmp( p_this->psz_object_name, psz_name ) ) + { + vlc_object_yield( p_this ); + return p_this; + } + + vlc_mutex_lock( &structure_lock ); + + /* Otherwise, recursively look for the object */ + if( (i_mode & 0x000f) == FIND_ANYWHERE ) + { + vlc_object_t *p_root = p_this; + + /* Find the root */ + while( p_root->p_parent != NULL && + p_root != VLC_OBJECT( p_this->p_libvlc ) ) + { + p_root = p_root->p_parent; + } + + p_found = FindObjectName( p_root, psz_name, + (i_mode & ~0x000f)|FIND_CHILD ); + if( p_found == NULL && p_root != VLC_OBJECT( p_this->p_libvlc ) ) + { + p_found = FindObjectName( VLC_OBJECT( p_this->p_libvlc ), + psz_name, (i_mode & ~0x000f)|FIND_CHILD ); + } + } + else + { + p_found = FindObjectName( p_this, psz_name, i_mode ); + } + + vlc_mutex_unlock( &structure_lock ); + + return p_found; +} + +/** + * Increment an object reference counter. + */ +void __vlc_object_yield( vlc_object_t *p_this ) +{ + vlc_object_internals_t *internals = vlc_internals( p_this ); + + vlc_spin_lock( &internals->ref_spin ); + /* Avoid obvious freed object uses */ + assert( internals->i_refcount > 0 ); + /* Increment the counter */ + internals->i_refcount++; + vlc_spin_unlock( &internals->ref_spin ); +#ifdef LIBVLC_REFCHECK + /* Update the list of referenced objects */ + /* Using TLS, so no need to lock */ + /* The following line may leak memory if a thread leaks objects. */ + held_list_t *newhead = malloc (sizeof (*newhead)); + held_list_t *oldhead = vlc_threadvar_get (&held_objects); + newhead->next = oldhead; + newhead->obj = p_this; + vlc_threadvar_set (&held_objects, newhead); +#endif +} + +/***************************************************************************** + * decrement an object refcount + * And destroy the object if its refcount reach zero. + *****************************************************************************/ +void __vlc_object_release( vlc_object_t *p_this ) +{ + vlc_object_internals_t *internals = vlc_internals( p_this ); + bool b_should_destroy; + +#ifdef LIBVLC_REFCHECK + /* Update the list of referenced objects */ + /* Using TLS, so no need to lock */ + for (held_list_t *hlcur = vlc_threadvar_get (&held_objects), + *hlprev = NULL; + hlcur != NULL; + hlprev = hlcur, hlcur = hlcur->next) + { + if (hlcur->obj == p_this) + { + if (hlprev == NULL) + vlc_threadvar_set (&held_objects, hlcur->next); + else + hlprev->next = hlcur->next; + free (hlcur); + break; + } + } + /* TODO: what if releasing without references? */ +#endif + + vlc_spin_lock( &internals->ref_spin ); + assert( internals->i_refcount > 0 ); + + if( internals->i_refcount > 1 ) + { + /* Fast path */ + /* There are still other references to the object */ + internals->i_refcount--; + vlc_spin_unlock( &internals->ref_spin ); + return; + } + vlc_spin_unlock( &internals->ref_spin ); + + /* Slow path */ + /* Remember that we cannot hold the spin while waiting on the mutex */ + vlc_mutex_lock( &structure_lock ); + /* Take the spin again. Note that another thread may have yielded the + * object in the (very short) mean time. */ + vlc_spin_lock( &internals->ref_spin ); + b_should_destroy = --internals->i_refcount == 0; + vlc_spin_unlock( &internals->ref_spin ); + + if( b_should_destroy ) + { + /* Remove the object from object list + * so that it cannot be encountered by vlc_object_get() */ + vlc_internals (internals->next)->prev = internals->prev; + vlc_internals (internals->prev)->next = internals->next; + + /* Detach from parent to protect against FIND_CHILDREN */ + vlc_object_detach_unlocked (p_this); + /* Detach from children to protect against FIND_PARENT */ + for (int i = 0; i < internals->i_children; i++) + internals->pp_children[i]->p_parent = NULL; + } + + vlc_mutex_unlock( &structure_lock ); + + if( b_should_destroy ) + { + free( internals->pp_children ); + internals->pp_children = NULL; + internals->i_children = 0; + vlc_object_destroy( p_this ); + } +} + +/** + **************************************************************************** + * attach object to a parent object + ***************************************************************************** + * This function sets p_this as a child of p_parent, and p_parent as a parent + * of p_this. This link can be undone using vlc_object_detach. + *****************************************************************************/ +void __vlc_object_attach( vlc_object_t *p_this, vlc_object_t *p_parent ) +{ + if( !p_this ) return; + + vlc_mutex_lock( &structure_lock ); + + /* Attach the parent to its child */ + assert (!p_this->p_parent); + p_this->p_parent = p_parent; + + /* Attach the child to its parent */ + vlc_object_internals_t *priv = vlc_internals( p_parent ); + INSERT_ELEM( priv->pp_children, priv->i_children, priv->i_children, + p_this ); + + vlc_mutex_unlock( &structure_lock ); +} + + +static void vlc_object_detach_unlocked (vlc_object_t *p_this) +{ + vlc_assert_locked (&structure_lock); + + if (p_this->p_parent == NULL) + return; + + vlc_object_internals_t *priv = vlc_internals( p_this->p_parent ); + + int i_index, i; + + /* Remove p_this's parent */ + p_this->p_parent = NULL; + + /* Remove all of p_parent's children which are p_this */ + for( i_index = priv->i_children ; i_index-- ; ) + { + if( priv->pp_children[i_index] == p_this ) + { + priv->i_children--; + for( i = i_index ; i < priv->i_children ; i++ ) + priv->pp_children[i] = priv->pp_children[i+1]; + } + } + + if( priv->i_children ) + { + priv->pp_children = (vlc_object_t **)realloc( priv->pp_children, + priv->i_children * sizeof(vlc_object_t *) ); + } + else + { + /* Special case - don't realloc() to zero to avoid leaking */ + free( priv->pp_children ); + priv->pp_children = NULL; + } +} + + +/** + **************************************************************************** + * detach object from its parent + ***************************************************************************** + * This function removes all links between an object and its parent. + *****************************************************************************/ +void __vlc_object_detach( vlc_object_t *p_this ) +{ + if( !p_this ) return; + + vlc_mutex_lock( &structure_lock ); + if( !p_this->p_parent ) + msg_Err( p_this, "object is not attached" ); + else + vlc_object_detach_unlocked( p_this ); + vlc_mutex_unlock( &structure_lock ); +} + + +/** + **************************************************************************** + * find a list typed objects and increment their refcount + ***************************************************************************** + * This function recursively looks for a given object type. i_mode can be one + * of FIND_PARENT, FIND_CHILD or FIND_ANYWHERE. + *****************************************************************************/ +vlc_list_t * __vlc_list_find( vlc_object_t *p_this, int i_type, int i_mode ) +{ + vlc_list_t *p_list; + int i_count = 0; + + /* Look for the objects */ + switch( i_mode & 0x000f ) + { + case FIND_ANYWHERE: + /* Modules should probably not be object, and the module should perhaps + * not be shared across LibVLC instances. In the mean time, this ugly + * hack is brought to you by Courmisch. */ + if (i_type == VLC_OBJECT_MODULE) + return vlc_list_find ((vlc_object_t *)vlc_global ()->p_module_bank, + i_type, FIND_CHILD); + return vlc_list_find (p_this->p_libvlc, i_type, FIND_CHILD); + + case FIND_CHILD: + vlc_mutex_lock( &structure_lock ); + i_count = CountChildren( p_this, i_type ); + p_list = NewList( i_count ); + + /* Check allocation was successful */ + if( p_list->i_count != i_count ) + { + vlc_mutex_unlock( &structure_lock ); + msg_Err( p_this, "list allocation failed!" ); + p_list->i_count = 0; + break; + } + + p_list->i_count = 0; + ListChildren( p_list, p_this, i_type ); + vlc_mutex_unlock( &structure_lock ); + break; + + default: + msg_Err( p_this, "unimplemented!" ); + p_list = NewList( 0 ); + break; + } + + return p_list; +} + +/** + * Gets the list of children of an objects, and increment their reference + * count. + * @return a list (possibly empty) or NULL in case of error. + */ +vlc_list_t *__vlc_list_children( vlc_object_t *obj ) +{ + vlc_list_t *l; + vlc_object_internals_t *priv = vlc_internals( obj ); + + vlc_mutex_lock( &structure_lock ); + l = NewList( priv->i_children ); + for (int i = 0; i < l->i_count; i++) + { + vlc_object_yield( priv->pp_children[i] ); + l->p_values[i].p_object = priv->pp_children[i]; + } + vlc_mutex_unlock( &structure_lock ); + return l; +} + +/***************************************************************************** + * DumpCommand: print the current vlc structure + ***************************************************************************** + * This function prints either an ASCII tree showing the connections between + * vlc objects, and additional information such as their refcount, thread ID, + * etc. (command "tree"), or the same data as a simple list (command "list"). + *****************************************************************************/ +static int DumpCommand( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) +{ + (void)oldval; (void)p_data; + if( *psz_cmd == 'l' ) + { + vlc_object_t *root = VLC_OBJECT (vlc_global ()), *cur = root; + + vlc_mutex_lock( &structure_lock ); + do + { + PrintObject (cur, ""); + cur = vlc_internals (cur)->next; + } + while (cur != root); + vlc_mutex_unlock( &structure_lock ); + } + else + { + vlc_object_t *p_object = NULL; + + if( *newval.psz_string ) + { + char *end; + int i_id = strtol( newval.psz_string, &end, 0 ); + if( end != newval.psz_string ) + p_object = vlc_object_get( i_id ); + else + /* try using the object's name to find it */ + p_object = vlc_object_find_name( p_this, newval.psz_string, + FIND_ANYWHERE ); + + if( !p_object ) + { + return VLC_ENOOBJ; + } + } + + vlc_mutex_lock( &structure_lock ); + + if( *psz_cmd == 't' ) + { + char psz_foo[2 * MAX_DUMPSTRUCTURE_DEPTH + 1]; + + if( !p_object ) + p_object = p_this->p_libvlc ? VLC_OBJECT(p_this->p_libvlc) : p_this; + + psz_foo[0] = '|'; + DumpStructure( p_object, 0, psz_foo ); + } + else if( *psz_cmd == 'v' ) + { + int i; + + if( !p_object ) + p_object = p_this->p_libvlc ? VLC_OBJECT(p_this->p_libvlc) : p_this; + + PrintObject( p_object, "" ); + + if( !vlc_internals( p_object )->i_vars ) + printf( " `-o No variables\n" ); + for( i = 0; i < vlc_internals( p_object )->i_vars; i++ ) + { + variable_t *p_var = vlc_internals( p_object )->p_vars + i; + + const char *psz_type = "unknown"; + switch( p_var->i_type & VLC_VAR_TYPE ) + { +#define MYCASE( type, nice ) \ + case VLC_VAR_ ## type: \ + psz_type = nice; \ + break; + MYCASE( VOID, "void" ); + MYCASE( BOOL, "bool" ); + MYCASE( INTEGER, "integer" ); + MYCASE( HOTKEY, "hotkey" ); + MYCASE( STRING, "string" ); + MYCASE( MODULE, "module" ); + MYCASE( FILE, "file" ); + MYCASE( DIRECTORY, "directory" ); + MYCASE( VARIABLE, "variable" ); + MYCASE( FLOAT, "float" ); + MYCASE( TIME, "time" ); + MYCASE( ADDRESS, "address" ); + MYCASE( MUTEX, "mutex" ); + MYCASE( LIST, "list" ); +#undef MYCASE + } + printf( " %c-o \"%s\" (%s", + i + 1 == vlc_internals( p_object )->i_vars ? '`' : '|', + p_var->psz_name, psz_type ); + if( p_var->psz_text ) + printf( ", %s", p_var->psz_text ); + printf( ")" ); + if( p_var->i_type & VLC_VAR_ISCOMMAND ) + printf( ", command" ); + if( p_var->i_entries ) + printf( ", %d callbacks", p_var->i_entries ); + switch( p_var->i_type & 0x00f0 ) + { + case VLC_VAR_VOID: + case VLC_VAR_MUTEX: + break; + case VLC_VAR_BOOL: + printf( ": %s", p_var->val.b_bool ? "true" : "false" ); + break; + case VLC_VAR_INTEGER: + printf( ": %d", p_var->val.i_int ); + break; + case VLC_VAR_STRING: + printf( ": \"%s\"", p_var->val.psz_string ); + break; + case VLC_VAR_FLOAT: + printf( ": %f", p_var->val.f_float ); + break; + case VLC_VAR_TIME: + printf( ": %"PRIi64, (int64_t)p_var->val.i_time ); + break; + case VLC_VAR_ADDRESS: + printf( ": %p", p_var->val.p_address ); + break; + case VLC_VAR_LIST: + printf( ": TODO" ); + break; + } + printf( "\n" ); + } + } + + vlc_mutex_unlock( &structure_lock ); + + if( *newval.psz_string ) + { + vlc_object_release( p_object ); + } + } + + return VLC_SUCCESS; +} + +/***************************************************************************** + * vlc_list_release: free a list previously allocated by vlc_list_find + ***************************************************************************** + * This function decreases the refcount of all objects in the list and + * frees the list. + *****************************************************************************/ +void vlc_list_release( vlc_list_t *p_list ) +{ + int i_index; + + for( i_index = 0; i_index < p_list->i_count; i_index++ ) + { + vlc_object_release( p_list->p_values[i_index].p_object ); + } + + free( p_list->p_values ); + free( p_list ); +} + +/***************************************************************************** + * dump an object. (Debug function) + *****************************************************************************/ +void __vlc_object_dump( vlc_object_t *p_this ) +{ + vlc_mutex_lock( &structure_lock ); + char psz_foo[2 * MAX_DUMPSTRUCTURE_DEPTH + 1]; + psz_foo[0] = '|'; + DumpStructure( p_this, 0, psz_foo ); + vlc_mutex_unlock( &structure_lock ); +} + +/* Following functions are local */ + +static vlc_object_t * FindObject( vlc_object_t *p_this, int i_type, int i_mode ) +{ + int i; + vlc_object_t *p_tmp; + + switch( i_mode & 0x000f ) + { + case FIND_PARENT: + p_tmp = p_this->p_parent; + if( p_tmp ) + { + if( p_tmp->i_object_type == i_type ) + { + vlc_object_yield( p_tmp ); + return p_tmp; + } + else + { + return FindObject( p_tmp, i_type, i_mode ); + } + } + break; + + case FIND_CHILD: + for( i = vlc_internals( p_this )->i_children; i--; ) + { + p_tmp = vlc_internals( p_this )->pp_children[i]; + if( p_tmp->i_object_type == i_type ) + { + vlc_object_yield( p_tmp ); + return p_tmp; + } + else if( vlc_internals( p_tmp )->i_children ) + { + p_tmp = FindObject( p_tmp, i_type, i_mode ); + if( p_tmp ) + { + return p_tmp; + } + } + } + break; + + case FIND_ANYWHERE: + /* Handled in vlc_object_find */ + break; + } + + return NULL; +} + +static vlc_object_t * FindObjectName( vlc_object_t *p_this, + const char *psz_name, + int i_mode ) +{ + int i; + vlc_object_t *p_tmp; + + switch( i_mode & 0x000f ) + { + case FIND_PARENT: + p_tmp = p_this->p_parent; + if( p_tmp ) + { + if( p_tmp->psz_object_name + && !strcmp( p_tmp->psz_object_name, psz_name ) ) + { + vlc_object_yield( p_tmp ); + return p_tmp; + } + else + { + return FindObjectName( p_tmp, psz_name, i_mode ); + } + } + break; + + case FIND_CHILD: + for( i = vlc_internals( p_this )->i_children; i--; ) + { + p_tmp = vlc_internals( p_this )->pp_children[i]; + if( p_tmp->psz_object_name + && !strcmp( p_tmp->psz_object_name, psz_name ) ) + { + vlc_object_yield( p_tmp ); + return p_tmp; + } + else if( vlc_internals( p_tmp )->i_children ) + { + p_tmp = FindObjectName( p_tmp, psz_name, i_mode ); + if( p_tmp ) + { + return p_tmp; + } + } + } + break; + + case FIND_ANYWHERE: + /* Handled in vlc_object_find */ + break; + } + + return NULL; +} + + +static void PrintObject( vlc_object_t *p_this, const char *psz_prefix ) +{ + char psz_children[20], psz_refcount[20], psz_thread[30], psz_name[50], + psz_parent[20]; + + memset( &psz_name, 0, sizeof(psz_name) ); + if( p_this->psz_object_name ) + { + snprintf( psz_name, 49, " \"%s\"", p_this->psz_object_name ); + if( psz_name[48] ) + psz_name[48] = '\"'; + } + + psz_children[0] = '\0'; + switch( vlc_internals( p_this )->i_children ) + { + case 0: + break; + case 1: + strcpy( psz_children, ", 1 child" ); + break; + default: + snprintf( psz_children, 19, ", %i children", + vlc_internals( p_this )->i_children ); + break; + } + + psz_refcount[0] = '\0'; + if( vlc_internals( p_this )->i_refcount > 0 ) + snprintf( psz_refcount, 19, ", refcount %u", + vlc_internals( p_this )->i_refcount ); + + psz_thread[0] = '\0'; + if( vlc_internals( p_this )->b_thread ) + snprintf( psz_thread, 29, " (thread %lu)", + (unsigned long)vlc_internals( p_this )->thread_id ); + + psz_parent[0] = '\0'; + if( p_this->p_parent ) + snprintf( psz_parent, 19, ", parent %i", p_this->p_parent->i_object_id ); + + printf( " %so %.8i %s%s%s%s%s%s\n", psz_prefix, + p_this->i_object_id, p_this->psz_object_type, + psz_name, psz_thread, psz_refcount, psz_children, + psz_parent ); +} + +static void DumpStructure( vlc_object_t *p_this, int i_level, char *psz_foo ) +{ + int i; + char i_back = psz_foo[i_level]; + psz_foo[i_level] = '\0'; + + PrintObject( p_this, psz_foo ); + + psz_foo[i_level] = i_back; + + if( i_level / 2 >= MAX_DUMPSTRUCTURE_DEPTH ) + { + msg_Warn( p_this, "structure tree is too deep" ); + return; + } + + for( i = 0 ; i < vlc_internals( p_this )->i_children ; i++ ) + { + if( i_level ) + { + psz_foo[i_level-1] = ' '; + + if( psz_foo[i_level-2] == '`' ) + { + psz_foo[i_level-2] = ' '; + } + } + + if( i == vlc_internals( p_this )->i_children - 1 ) + { + psz_foo[i_level] = '`'; + } + else + { + psz_foo[i_level] = '|'; + } + + psz_foo[i_level+1] = '-'; + psz_foo[i_level+2] = '\0'; + + DumpStructure( vlc_internals( p_this )->pp_children[i], i_level + 2, + psz_foo ); + } +} + +static vlc_list_t * NewList( int i_count ) +{ + vlc_list_t * p_list = (vlc_list_t *)malloc( sizeof( vlc_list_t ) ); + if( p_list == NULL ) + { + return NULL; + } + + p_list->i_count = i_count; + + if( i_count == 0 ) + { + p_list->p_values = NULL; + return p_list; + } + + p_list->p_values = malloc( i_count * sizeof( vlc_value_t ) ); + if( p_list->p_values == NULL ) + { + p_list->i_count = 0; + return p_list; + } + + return p_list; +} + +static void ListReplace( vlc_list_t *p_list, vlc_object_t *p_object, + int i_index ) +{ + if( p_list == NULL || i_index >= p_list->i_count ) + { + return; + } + + vlc_object_yield( p_object ); + + p_list->p_values[i_index].p_object = p_object; + + return; +} + +/*static void ListAppend( vlc_list_t *p_list, vlc_object_t *p_object ) +{ + if( p_list == NULL ) + { + return; + } + + p_list->p_values = realloc( p_list->p_values, (p_list->i_count + 1) + * sizeof( vlc_value_t ) ); + if( p_list->p_values == NULL ) + { + p_list->i_count = 0; + return; + } + + vlc_object_yield( p_object ); + + p_list->p_values[p_list->i_count].p_object = p_object; + p_list->i_count++; + + return; +}*/ + +static int CountChildren( vlc_object_t *p_this, int i_type ) +{ + vlc_object_t *p_tmp; + int i, i_count = 0; + + for( i = 0; i < vlc_internals( p_this )->i_children; i++ ) + { + p_tmp = vlc_internals( p_this )->pp_children[i]; + + if( p_tmp->i_object_type == i_type ) + { + i_count++; + } + i_count += CountChildren( p_tmp, i_type ); + } + + return i_count; +} + +static void ListChildren( vlc_list_t *p_list, vlc_object_t *p_this, int i_type ) +{ + vlc_object_t *p_tmp; + int i; + + for( i = 0; i < vlc_internals( p_this )->i_children; i++ ) + { + p_tmp = vlc_internals( p_this )->pp_children[i]; + + if( p_tmp->i_object_type == i_type ) + ListReplace( p_list, p_tmp, p_list->i_count++ ); + + ListChildren( p_list, p_tmp, i_type ); + } +} + +#ifdef LIBVLC_REFCHECK +# if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) +# include +# endif + +void vlc_refcheck (vlc_object_t *obj) +{ + static unsigned errors = 0; + if (errors > 100) + return; + + /* Anyone can use the root object (though it should not exist) */ + if (obj == VLC_OBJECT (vlc_global ())) + return; + + /* Anyone can use its libvlc instance object */ + if (obj == VLC_OBJECT (obj->p_libvlc)) + return; + + /* The thread that created the object holds the initial reference */ + vlc_object_internals_t *priv = vlc_internals (obj); +#if defined (LIBVLC_USE_PTHREAD) + if (pthread_equal (priv->creator_id, pthread_self ())) +#elif defined WIN32 + if (priv->creator_id == GetCurrentThreadId ()) +#else + if (0) +#endif + return; + + /* A thread can use its own object without references! */ + vlc_object_t *caller = vlc_threadobj (); + if (caller == obj) + return; +#if 0 + /* The calling thread is younger than the object. + * Access could be valid through cross-thread synchronization; + * we would need better accounting. */ + if (caller && (caller->i_object_id > obj->i_object_id)) + return; +#endif + int refs; + vlc_spin_lock (&priv->ref_spin); + refs = priv->i_refcount; + vlc_spin_unlock (&priv->ref_spin); + + for (held_list_t *hlcur = vlc_threadvar_get (&held_objects); + hlcur != NULL; hlcur = hlcur->next) + if (hlcur->obj == obj) + return; + + fprintf (stderr, "The %s %s thread object is accessing...\n" + "the %s %s object without references.\n", + caller && caller->psz_object_name + ? caller->psz_object_name : "unnamed", + caller ? caller->psz_object_type : "main", + obj->psz_object_name ? obj->psz_object_name : "unnamed", + obj->psz_object_type); + fflush (stderr); + +#ifdef HAVE_BACKTRACE + void *stack[20]; + int stackdepth = backtrace (stack, sizeof (stack) / sizeof (stack[0])); + backtrace_symbols_fd (stack, stackdepth, 2); +#endif + + if (++errors == 100) + fprintf (stderr, "Too many reference errors!\n"); +} + +static void held_objects_destroy (void *data) +{ + VLC_UNUSED( data ); + held_list_t *hl = vlc_threadvar_get (&held_objects); + vlc_object_t *caller = vlc_threadobj (); + + while (hl != NULL) + { + held_list_t *buf = hl->next; + vlc_object_t *obj = hl->obj; + + fprintf (stderr, "The %s %s thread object leaked a reference to...\n" + "the %s %s object.\n", + caller && caller->psz_object_name + ? caller->psz_object_name : "unnamed", + caller ? caller->psz_object_type : "main", + obj->psz_object_name ? obj->psz_object_name : "unnamed", + obj->psz_object_type); + free (hl); + hl = buf; + } +} +#endif diff --git a/VLC/os.c b/VLC/os.c new file mode 100644 index 0000000..e000d41 --- /dev/null +++ b/VLC/os.c @@ -0,0 +1,384 @@ +/***************************************************************************** + * os.c : Low-level dynamic library handling + ***************************************************************************** + * Copyright (C) 2001-2007 the VideoLAN team + * $Id$ + * + * Authors: Sam Hocevar + * Ethan C. Baldridge + * Hans-Peter Jansen + * Gildas Bazin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "vlc_plugin.h" /* MODULE_SUFFIX */ +#include "libvlc.h" +#include "modules.h" + +#include /* free(), strtol() */ +#include /* sprintf() */ +#include /* strdup() */ + +#ifdef HAVE_SYS_TYPES_H +# include +#endif + +#if !defined(HAVE_DYNAMIC_PLUGINS) + /* no support for plugins */ +#elif defined(HAVE_DL_DYLD) +# if defined(HAVE_MACH_O_DYLD_H) +# include +# endif +#elif defined(HAVE_DL_BEOS) +# if defined(HAVE_IMAGE_H) +# include +# endif +#elif defined(HAVE_DL_WINDOWS) +# include +#elif defined(HAVE_DL_DLOPEN) +# if defined(HAVE_DLFCN_H) /* Linux, BSD, Hurd */ +# include +# endif +# if defined(HAVE_SYS_DL_H) +# include +# endif +#elif defined(HAVE_DL_SHL_LOAD) +# if defined(HAVE_DL_H) +# include +# endif +#endif + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +#ifdef HAVE_DYNAMIC_PLUGINS +static void * GetSymbol ( module_handle_t, const char * ); + +#if defined(HAVE_DL_WINDOWS) +static char * GetWindowsError ( void ); +#endif + +/** + * module Call + * + * Call a symbol given its name and a module structure. The symbol MUST + * refer to a function returning int and taking a module_t* as an argument. + * \param p_module the modules + * \return 0 if it pass and -1 in case of a failure + */ +int module_Call( module_t *p_module ) +{ + static const char psz_name[] = "vlc_entry" MODULE_SUFFIX; + int (* pf_symbol) ( module_t * p_module ); + + /* Try to resolve the symbol */ + pf_symbol = (int (*)(module_t *)) GetSymbol( p_module->handle, psz_name ); + + if( pf_symbol == NULL ) + { +#if defined(HAVE_DL_DYLD) || defined(HAVE_DL_BEOS) + msg_Warn( p_module, "cannot find symbol \"%s\" in file `%s'", + psz_name, p_module->psz_filename ); +#elif defined(HAVE_DL_WINDOWS) + char *psz_error = GetWindowsError(); + msg_Warn( p_module, "cannot find symbol \"%s\" in file `%s' (%s)", + psz_name, p_module->psz_filename, psz_error ); + free( psz_error ); +#elif defined(HAVE_DL_DLOPEN) + msg_Warn( p_module, "cannot find symbol \"%s\" in file `%s' (%s)", + psz_name, p_module->psz_filename, dlerror() ); +#elif defined(HAVE_DL_SHL_LOAD) + msg_Warn( p_module, "cannot find symbol \"%s\" in file `%s' (%m)", + psz_name, p_module->psz_filename ); +#else +# error "Something is wrong in modules.c" +#endif + return -1; + } + + /* We can now try to call the symbol */ + if( pf_symbol( p_module ) != 0 ) + { + /* With a well-written module we shouldn't have to print an + * additional error message here, but just make sure. */ + msg_Err( p_module, "Failed to call symbol \"%s\" in file `%s'", + psz_name, p_module->psz_filename ); + return -1; + } + + /* Everything worked fine, we can return */ + return 0; +} + +/** + * Load a dynamically linked library using a system dependent method. + * + * \param p_this vlc object + * \param psz_file library file + * \param p_handle the module handle returned + * \return 0 on success as well as the module handle. + */ +int module_Load( vlc_object_t *p_this, const char *psz_file, + module_handle_t *p_handle ) +{ + module_handle_t handle; + +#if defined(HAVE_DL_DYLD) + NSObjectFileImage image; + NSObjectFileImageReturnCode ret; + + ret = NSCreateObjectFileImageFromFile( psz_file, &image ); + + if( ret != NSObjectFileImageSuccess ) + { + msg_Warn( p_this, "cannot create image from `%s'", psz_file ); + return -1; + } + + /* Open the dynamic module */ + handle = NSLinkModule( image, psz_file, + NSLINKMODULE_OPTION_RETURN_ON_ERROR ); + + if( !handle ) + { + NSLinkEditErrors errors; + const char *psz_file, *psz_err; + int i_errnum; + NSLinkEditError( &errors, &i_errnum, &psz_file, &psz_err ); + msg_Warn( p_this, "cannot link module `%s' (%s)", psz_file, psz_err ); + NSDestroyObjectFileImage( image ); + return -1; + } + + /* Destroy our image, we won't need it */ + NSDestroyObjectFileImage( image ); + +#elif defined(HAVE_DL_BEOS) + handle = load_add_on( psz_file ); + if( handle < 0 ) + { + msg_Warn( p_this, "cannot load module `%s'", psz_file ); + return -1; + } + +#elif defined(HAVE_DL_WINDOWS) +#ifdef UNDER_CE + { + wchar_t psz_wfile[MAX_PATH]; + MultiByteToWideChar( CP_ACP, 0, psz_file, -1, psz_wfile, MAX_PATH ); + handle = LoadLibrary( psz_wfile ); + } +#else + handle = LoadLibrary( psz_file ); +#endif + if( handle == NULL ) + { + char *psz_err = GetWindowsError(); + msg_Warn( p_this, "cannot load module `%s' (%s)", psz_file, psz_err ); + free( psz_err ); + return -1; + } + +#elif defined(HAVE_DL_DLOPEN) && defined(RTLD_NOW) + /* static is OK, we are called atomically */ + handle = dlopen( psz_file, RTLD_NOW ); + if( handle == NULL ) + { + msg_Warn( p_this, "cannot load module `%s' (%s)", + psz_file, dlerror() ); + return -1; + } + +#elif defined(HAVE_DL_DLOPEN) +# if defined(DL_LAZY) + handle = dlopen( psz_file, DL_LAZY ); +# else + handle = dlopen( psz_file, 0 ); +# endif + if( handle == NULL ) + { + msg_Warn( p_this, "cannot load module `%s' (%s)", + psz_file, dlerror() ); + return -1; + } + +#elif defined(HAVE_DL_SHL_LOAD) + handle = shl_load( psz_file, BIND_IMMEDIATE | BIND_NONFATAL, NULL ); + if( handle == NULL ) + { + msg_Warn( p_this, "cannot load module `%s' (%m)", psz_file ); + return -1; + } + +#else +# error "Something is wrong in modules.c" + +#endif + + *p_handle = handle; + return 0; +} + +/** + * CloseModule: unload a dynamic library + * + * This function unloads a previously opened dynamically linked library + * using a system dependent method. No return value is taken in consideration, + * since some libraries sometimes refuse to close properly. + * \param handle handle of the library + * \return nothing + */ +void module_Unload( module_handle_t handle ) +{ +#if defined(HAVE_DL_DYLD) + NSUnLinkModule( handle, FALSE ); + +#elif defined(HAVE_DL_BEOS) + unload_add_on( handle ); + +#elif defined(HAVE_DL_WINDOWS) + FreeLibrary( handle ); + +#elif defined(HAVE_DL_DLOPEN) +# ifdef NDEBUG + dlclose( handle ); +# else + (void)handle; +# endif + +#elif defined(HAVE_DL_SHL_LOAD) + shl_unload( handle ); + +#endif + return; +} + +/** + * GetSymbol: get a symbol from a dynamic library + * + * This function queries a loaded library for a symbol specified in a + * string, and returns a pointer to it. We don't check for dlerror() or + * similar functions, since we want a non-NULL symbol anyway. + * \param handle handle to the module + * \param psz_function function name + * \return nothing + */ +static void * _module_getsymbol( module_handle_t, const char * ); + +static void * GetSymbol( module_handle_t handle, const char * psz_function ) +{ + void * p_symbol = _module_getsymbol( handle, psz_function ); + + /* MacOS X dl library expects symbols to begin with "_". So do + * some other operating systems. That's really lame, but hey, what + * can we do ? */ + if( p_symbol == NULL ) + { + char psz_call[strlen( psz_function ) + 2]; + + psz_call[ 0 ] = '_'; + memcpy( psz_call + 1, psz_function, sizeof (psz_call) - 1 ); + p_symbol = _module_getsymbol( handle, psz_call ); + } + + return p_symbol; +} + +static void * _module_getsymbol( module_handle_t handle, + const char * psz_function ) +{ +#if defined(HAVE_DL_DYLD) + NSSymbol sym = NSLookupSymbolInModule( handle, psz_function ); + return NSAddressOfSymbol( sym ); + +#elif defined(HAVE_DL_BEOS) + void * p_symbol; + if( B_OK == get_image_symbol( handle, psz_function, + B_SYMBOL_TYPE_TEXT, &p_symbol ) ) + { + return p_symbol; + } + else + { + return NULL; + } + +#elif defined(HAVE_DL_WINDOWS) && defined(UNDER_CE) + wchar_t psz_real[256]; + MultiByteToWideChar( CP_ACP, 0, psz_function, -1, psz_real, 256 ); + + return (void *)GetProcAddress( handle, psz_real ); + +#elif defined(HAVE_DL_WINDOWS) && defined(WIN32) + return (void *)GetProcAddress( handle, (char *)psz_function ); + +#elif defined(HAVE_DL_DLOPEN) + return dlsym( handle, psz_function ); + +#elif defined(HAVE_DL_SHL_LOAD) + void *p_sym; + shl_findsym( &handle, psz_function, TYPE_UNDEFINED, &p_sym ); + return p_sym; + +#endif +} + +#if defined(HAVE_DL_WINDOWS) +static char * GetWindowsError( void ) +{ +#if defined(UNDER_CE) + wchar_t psz_tmp[MAX_PATH]; + char * psz_buffer = malloc( MAX_PATH ); +#else + char * psz_tmp = malloc( MAX_PATH ); +#endif + int i = 0, i_error = GetLastError(); + + FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, i_error, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)psz_tmp, MAX_PATH, NULL ); + + /* Go to the end of the string */ + while( psz_tmp[i] && psz_tmp[i] != _T('\r') && psz_tmp[i] != _T('\n') ) + { + i++; + } + + if( psz_tmp[i] ) + { +#if defined(UNDER_CE) + swprintf( psz_tmp + i, L" (error %i)", i_error ); + psz_tmp[ 255 ] = L'\0'; +#else + snprintf( psz_tmp + i, 256 - i, " (error %i)", i_error ); + psz_tmp[ 255 ] = '\0'; +#endif + } + +#if defined(UNDER_CE) + wcstombs( psz_buffer, psz_tmp, MAX_PATH ); + return psz_buffer; +#else + return psz_tmp; +#endif +} +#endif /* HAVE_DL_WINDOWS */ +#endif /* HAVE_DYNAMIC_PLUGINS */ diff --git a/VLC/osd.c b/VLC/osd.c new file mode 100644 index 0000000..d68737d --- /dev/null +++ b/VLC/osd.c @@ -0,0 +1,829 @@ +/***************************************************************************** + * osd.c - The OSD Menu core code. + ***************************************************************************** + * Copyright (C) 2005-2008 M2X + * $Id$ + * + * Authors: Jean-Paul Saman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "vlc_keys.h" +#include "vlc_osd.h" +#include "vlc_image.h" + +#include "libvlc.h" + +#undef OSD_MENU_DEBUG + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ + +static void osd_UpdateState( osd_menu_state_t *, int, int, int, int, picture_t * ); +static inline osd_state_t *osd_VolumeStateChange( osd_state_t *, int ); +static int osd_VolumeStep( vlc_object_t *, int, int ); +static bool osd_isVisible( osd_menu_t *p_osd ); +static bool osd_ParserLoad( osd_menu_t *, const char * ); +static void osd_ParserUnload( osd_menu_t * ); + +static bool osd_isVisible( osd_menu_t *p_osd ) +{ + vlc_value_t val; + + var_Get( p_osd, "osd-menu-visible", &val ); + return val.b_bool; +} + +/***************************************************************************** + * Wrappers for loading and unloading osd parser modules. + *****************************************************************************/ +static bool osd_ParserLoad( osd_menu_t *p_menu, const char *psz_file ) +{ + /* Stuff needed for Parser */ + p_menu->psz_file = strdup( psz_file ); + p_menu->p_image = image_HandlerCreate( p_menu ); + if( !p_menu->p_image || !p_menu->psz_file ) + { + msg_Err( p_menu, "unable to load images, aborting .." ); + return true; + } + else + { + char *psz_type; + char *psz_ext = strrchr( p_menu->psz_file, '.' ); + + if( psz_ext && !strcmp( psz_ext, ".cfg") ) + psz_type = (char*)"import-osd"; + else + psz_type = (char*)"import-osd-xml"; + + p_menu->p_parser = module_Need( p_menu, "osd parser", + psz_type, true ); + if( !p_menu->p_parser ) + { + return false; + } + } + return true; +} + +static void osd_ParserUnload( osd_menu_t *p_menu ) +{ + if( p_menu->p_image ) + image_HandlerDelete( p_menu->p_image ); + + if( p_menu->p_parser ) + module_Unneed( p_menu, p_menu->p_parser ); + + free( p_menu->psz_file ); +} + +/** + * Change state on an osd_button_t. + * + * This function selects the specified state and returns a pointer vlc_custom_createto it. The + * following states are currently supported: + * \see OSD_BUTTON_UNSELECT + * \see OSD_BUTTON_SELECT + * \see OSD_BUTTON_PRESSED + */ +static osd_state_t *osd_StateChange( osd_button_t *p_button, const int i_state ) +{ + osd_state_t *p_current = p_button->p_states; + osd_state_t *p_temp = NULL; + int i = 0; + + for( i=0; p_current != NULL; i++ ) + { + if( p_current->i_state == i_state ) + { + p_button->i_x = p_current->i_x; + p_button->i_y = p_current->i_y; + p_button->i_width = p_current->i_width; + p_button->i_height = p_current->i_height; + return p_current; + } + p_temp = p_current->p_next; + p_current = p_temp; + } + return p_button->p_states; +} + +/***************************************************************************** + * OSD menu Funtions + *****************************************************************************/ +osd_menu_t *__osd_MenuCreate( vlc_object_t *p_this, const char *psz_file ) +{ + osd_menu_t *p_osd = NULL; + vlc_value_t lockval; + int i_volume = 0; + int i_steps = 0; + + /* to be sure to avoid multiple creation */ + var_Create( p_this->p_libvlc, "osd_mutex", VLC_VAR_MUTEX ); + var_Get( p_this->p_libvlc, "osd_mutex", &lockval ); + vlc_mutex_lock( lockval.p_address ); + + p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE ); + if( p_osd == NULL ) + { + static const char osdmenu_name[] = "osd menu"; + vlc_value_t val; + + p_osd = vlc_custom_create( p_this, sizeof( *p_osd ), VLC_OBJECT_OSDMENU, + osdmenu_name ); + if( !p_osd ) + return NULL; + + p_osd->p_parser = NULL; + vlc_object_attach( p_osd, p_this->p_libvlc ); + + /* Parse configuration file */ + if ( !osd_ParserLoad( p_osd, psz_file ) ) + goto error; + if( !p_osd->p_state ) + goto error; + + /* Setup default button (first button) */ + p_osd->p_state->p_visible = p_osd->p_button; + p_osd->p_state->p_visible->p_current_state = + osd_StateChange( p_osd->p_state->p_visible, OSD_BUTTON_SELECT ); + p_osd->i_width = p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_pitch; + p_osd->i_height = p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_lines; + + if( p_osd->p_state->p_volume ) + { + /* Update the volume state images to match the current volume */ + i_volume = config_GetInt( p_this, "volume" ); + i_steps = osd_VolumeStep( p_this, i_volume, p_osd->p_state->p_volume->i_ranges ); + p_osd->p_state->p_volume->p_current_state = osd_VolumeStateChange( + p_osd->p_state->p_volume->p_states, i_steps ); + } + /* Initialize OSD state */ + osd_UpdateState( p_osd->p_state, p_osd->i_x, p_osd->i_y, + p_osd->i_width, p_osd->i_height, NULL ); + + /* Signal when an update of OSD menu is needed */ + var_Create( p_osd, "osd-menu-update", VLC_VAR_BOOL ); + var_Create( p_osd, "osd-menu-visible", VLC_VAR_BOOL ); + + val.b_bool = false; + var_Set( p_osd, "osd-menu-update", val ); + var_Set( p_osd, "osd-menu-visible", val ); + } + vlc_mutex_unlock( lockval.p_address ); + return p_osd; + +error: + vlc_mutex_unlock( lockval.p_address ); + __osd_MenuDelete( p_this, p_osd ); + return NULL; +} + +void __osd_MenuDelete( vlc_object_t *p_this, osd_menu_t *p_osd ) +{ + vlc_value_t lockval; + + if( !p_osd || !p_this ) return; + + var_Get( p_this->p_libvlc, "osd_mutex", &lockval ); + vlc_mutex_lock( lockval.p_address ); + + if( vlc_internals( VLC_OBJECT(p_osd) )->i_refcount == 1 ) + { + var_Destroy( p_osd, "osd-menu-visible" ); + var_Destroy( p_osd, "osd-menu-update" ); + osd_ParserUnload( p_osd ); + } + + vlc_object_release( p_osd ); + if( vlc_internals( VLC_OBJECT(p_osd) )->i_refcount > 0 ) + { + vlc_mutex_unlock( lockval.p_address ); + return; + } + p_osd = NULL; + vlc_mutex_unlock( lockval.p_address ); +} + +/* The volume can be modified in another interface while the OSD Menu + * has not been instantiated yet. This routines updates the "volume OSD menu item" + * to reflect the current state of the GUI. + */ +static inline osd_state_t *osd_VolumeStateChange( osd_state_t *p_current, int i_steps ) +{ + osd_state_t *p_temp = NULL; + int i; + + if( i_steps < 0 ) i_steps = 0; + + for( i=0; (i < i_steps) && (p_current != NULL); i++ ) + { + p_temp = p_current->p_next; + if( !p_temp ) return p_current; + p_current = p_temp; + } + return (!p_temp) ? p_current : p_temp; +} + +/* Update the state of the OSD Menu */ +static void osd_UpdateState( osd_menu_state_t *p_state, int i_x, int i_y, + int i_width, int i_height, picture_t *p_pic ) +{ + p_state->i_x = i_x; + p_state->i_y = i_y; + p_state->i_width = i_width; + p_state->i_height = i_height; + p_state->p_pic = p_pic; +} + +void __osd_MenuShow( vlc_object_t *p_this ) +{ + osd_menu_t *p_osd = NULL; + osd_button_t *p_button = NULL; + vlc_value_t lockval; + + p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE ); + if( p_osd == NULL ) + { + msg_Err( p_this, "osd_MenuNext failed" ); + return; + } + + var_Get( p_this->p_libvlc, "osd_mutex", &lockval ); + vlc_mutex_lock( lockval.p_address ); + +#if defined(OSD_MENU_DEBUG) + msg_Dbg( p_osd, "menu on" ); +#endif + p_button = p_osd->p_state->p_visible; + if( p_button ) + { + if( !p_button->b_range ) + p_button->p_current_state = osd_StateChange( p_button, OSD_BUTTON_UNSELECT ); + p_osd->p_state->p_visible = p_osd->p_button; + + if( !p_osd->p_state->p_visible->b_range ) + p_osd->p_state->p_visible->p_current_state = + osd_StateChange( p_osd->p_state->p_visible, OSD_BUTTON_SELECT ); + + osd_UpdateState( p_osd->p_state, + p_osd->p_state->p_visible->i_x, p_osd->p_state->p_visible->i_y, + p_osd->p_state->p_visible->p_current_state->i_width, + p_osd->p_state->p_visible->p_current_state->i_height, + p_osd->p_state->p_visible->p_current_state->p_pic ); + osd_SetMenuUpdate( p_osd, true ); + } + osd_SetMenuVisible( p_osd, true ); + + vlc_object_release( (vlc_object_t*) p_osd ); + vlc_mutex_unlock( lockval.p_address ); +} + +void __osd_MenuHide( vlc_object_t *p_this ) +{ + osd_menu_t *p_osd = NULL; + vlc_value_t lockval; + + p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE ); + if( p_osd == NULL ) + { + msg_Err( p_this, "osd_MenuNext failed" ); + return; + } + + var_Get( p_this->p_libvlc, "osd_mutex", &lockval ); + vlc_mutex_lock( lockval.p_address ); + +#if defined(OSD_MENU_DEBUG) + msg_Dbg( p_osd, "menu off" ); +#endif + osd_UpdateState( p_osd->p_state, + p_osd->p_state->i_x, p_osd->p_state->i_y, + 0, 0, NULL ); + osd_SetMenuUpdate( p_osd, true ); + + vlc_object_release( (vlc_object_t*) p_osd ); + vlc_mutex_unlock( lockval.p_address ); +} + +void __osd_MenuActivate( vlc_object_t *p_this ) +{ + osd_menu_t *p_osd = NULL; + osd_button_t *p_button = NULL; + vlc_value_t lockval; + + p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE ); + if( p_osd == NULL ) + { + msg_Err( p_this, "osd_MenuNext failed" ); + return; + } + + if( osd_isVisible( p_osd ) == false ) + { + vlc_object_release( (vlc_object_t*) p_osd ); + return; + } + + var_Get( p_this->p_libvlc, "osd_mutex", &lockval ); + vlc_mutex_lock( lockval.p_address ); + +#if defined(OSD_MENU_DEBUG) + msg_Dbg( p_osd, "select" ); +#endif + p_button = p_osd->p_state->p_visible; + /* + * Is there a menu item above or below? If so, then select it. + */ + if( p_button && p_button->p_up ) + { + vlc_object_release( (vlc_object_t*) p_osd ); + vlc_mutex_unlock( lockval.p_address ); + __osd_MenuUp( p_this ); /* "menu select" means go to menu item above. */ + return; + } + if( p_button && p_button->p_down ) + { + vlc_object_release( (vlc_object_t*) p_osd ); + vlc_mutex_unlock( lockval.p_address ); + __osd_MenuDown( p_this ); /* "menu select" means go to menu item below. */ + return; + } + + if( p_button && !p_button->b_range ) + { + p_button->p_current_state = osd_StateChange( p_button, OSD_BUTTON_PRESSED ); + osd_UpdateState( p_osd->p_state, + p_button->i_x, p_button->i_y, + p_osd->p_state->p_visible->p_current_state->i_width, + p_osd->p_state->p_visible->p_current_state->i_height, + p_button->p_current_state->p_pic ); + osd_SetMenuUpdate( p_osd, true ); + osd_SetMenuVisible( p_osd, true ); + osd_SetKeyPressed( VLC_OBJECT(p_osd->p_libvlc), config_GetInt( p_osd, p_button->psz_action ) ); +#if defined(OSD_MENU_DEBUG) + msg_Dbg( p_osd, "select (%d, %s)", config_GetInt( p_osd, p_button->psz_action ), p_button->psz_action ); +#endif + } + vlc_object_release( (vlc_object_t*) p_osd ); + vlc_mutex_unlock( lockval.p_address ); +} + +void __osd_MenuNext( vlc_object_t *p_this ) +{ + osd_menu_t *p_osd = NULL; + osd_button_t *p_button = NULL; + vlc_value_t lockval; + + p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE ); + if( p_osd == NULL ) + { + msg_Err( p_this, "osd_MenuNext failed" ); + return; + } + + if( osd_isVisible( p_osd ) == false ) + { + vlc_object_release( (vlc_object_t*) p_osd ); + return; + } + + var_Get( p_this->p_libvlc, "osd_mutex", &lockval ); + vlc_mutex_lock( lockval.p_address ); + + p_button = p_osd->p_state->p_visible; + if( p_button ) + { + if( !p_button->b_range ) + p_button->p_current_state = osd_StateChange( p_button, OSD_BUTTON_UNSELECT ); + if( p_button->p_next ) + p_osd->p_state->p_visible = p_button->p_next; + else + p_osd->p_state->p_visible = p_osd->p_button; + + if( !p_osd->p_state->p_visible->b_range ) + p_osd->p_state->p_visible->p_current_state = + osd_StateChange( p_osd->p_state->p_visible, OSD_BUTTON_SELECT ); + + osd_UpdateState( p_osd->p_state, + p_osd->p_state->p_visible->i_x, p_osd->p_state->p_visible->i_y, + p_osd->p_state->p_visible->p_current_state->i_width, + p_osd->p_state->p_visible->p_current_state->i_height, + p_osd->p_state->p_visible->p_current_state->p_pic ); + osd_SetMenuUpdate( p_osd, true ); + } +#if defined(OSD_MENU_DEBUG) + msg_Dbg( p_osd, "direction right [button %s]", p_osd->p_state->p_visible->psz_action ); +#endif + + vlc_object_release( (vlc_object_t*) p_osd ); + vlc_mutex_unlock( lockval.p_address ); +} + +void __osd_MenuPrev( vlc_object_t *p_this ) +{ + osd_menu_t *p_osd = NULL; + osd_button_t *p_button = NULL; + vlc_value_t lockval; + + p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE ); + if( p_osd == NULL ) + { + msg_Err( p_this, "osd_MenuPrev failed" ); + return; + } + + if( osd_isVisible( p_osd ) == false ) + { + vlc_object_release( (vlc_object_t*) p_osd ); + return; + } + + var_Get( p_this->p_libvlc, "osd_mutex", &lockval ); + vlc_mutex_lock( lockval.p_address ); + + p_button = p_osd->p_state->p_visible; + if( p_button ) + { + if( !p_button->b_range ) + p_button->p_current_state = osd_StateChange( p_button, OSD_BUTTON_UNSELECT ); + if( p_button->p_prev ) + p_osd->p_state->p_visible = p_button->p_prev; + else + p_osd->p_state->p_visible = p_osd->p_last_button; + + if( !p_osd->p_state->p_visible->b_range ) + p_osd->p_state->p_visible->p_current_state = + osd_StateChange( p_osd->p_state->p_visible, OSD_BUTTON_SELECT ); + + osd_UpdateState( p_osd->p_state, + p_osd->p_state->p_visible->i_x, p_osd->p_state->p_visible->i_y, + p_osd->p_state->p_visible->p_current_state->i_width, + p_osd->p_state->p_visible->p_current_state->i_height, + p_osd->p_state->p_visible->p_current_state->p_pic ); + osd_SetMenuUpdate( p_osd, true ); + } +#if defined(OSD_MENU_DEBUG) + msg_Dbg( p_osd, "direction left [button %s]", p_osd->p_state->p_visible->psz_action ); +#endif + + vlc_object_release( (vlc_object_t*) p_osd ); + vlc_mutex_unlock( lockval.p_address ); +} + +void __osd_MenuUp( vlc_object_t *p_this ) +{ + osd_menu_t *p_osd = NULL; + osd_button_t *p_button = NULL; + vlc_value_t lockval; +#if defined(OSD_MENU_DEBUG) + vlc_value_t val; +#endif + p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE ); + if( p_osd == NULL ) + { + msg_Err( p_this, "osd_MenuDown failed" ); + return; + } + + if( osd_isVisible( p_osd ) == false ) + { + vlc_object_release( (vlc_object_t*) p_osd ); + return; + } + + var_Get( p_this->p_libvlc, "osd_mutex", &lockval ); + vlc_mutex_lock( lockval.p_address ); + + p_button = p_osd->p_state->p_visible; + if( p_button ) + { + if( !p_button->b_range ) + { + p_button->p_current_state = osd_StateChange( p_button, OSD_BUTTON_SELECT ); + if( p_button->p_up ) + p_osd->p_state->p_visible = p_button->p_up; + } + + if( p_button->b_range && p_osd->p_state->p_visible->b_range ) + { + osd_state_t *p_temp = p_osd->p_state->p_visible->p_current_state; + if( p_temp && p_temp->p_next ) + p_osd->p_state->p_visible->p_current_state = p_temp->p_next; + } + else if( !p_osd->p_state->p_visible->b_range ) + { + p_osd->p_state->p_visible->p_current_state = + osd_StateChange( p_osd->p_state->p_visible, OSD_BUTTON_SELECT ); + } + + osd_UpdateState( p_osd->p_state, + p_osd->p_state->p_visible->i_x, p_osd->p_state->p_visible->i_y, + p_osd->p_state->p_visible->p_current_state->i_width, + p_osd->p_state->p_visible->p_current_state->i_height, + p_osd->p_state->p_visible->p_current_state->p_pic ); + osd_SetMenuUpdate( p_osd, true ); + /* If this is a range style action with associated images of only one state, + * then perform "menu select" on every menu navigation + */ + if( p_button->b_range ) + { + osd_SetKeyPressed( VLC_OBJECT(p_osd->p_libvlc), config_GetInt(p_osd, p_button->psz_action) ); +#if defined(OSD_MENU_DEBUG) + msg_Dbg( p_osd, "select (%d, %s)", val.i_int, p_button->psz_action ); +#endif + } + } +#if defined(OSD_MENU_DEBUG) + msg_Dbg( p_osd, "direction up [button %s]", p_osd->p_state->p_visible->psz_action ); +#endif + + vlc_object_release( (vlc_object_t*) p_osd ); + vlc_mutex_unlock( lockval.p_address ); +} + +void __osd_MenuDown( vlc_object_t *p_this ) +{ + osd_menu_t *p_osd = NULL; + osd_button_t *p_button = NULL; + vlc_value_t lockval; +#if defined(OSD_MENU_DEBUG) + vlc_value_t val; +#endif + + p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE ); + if( p_osd == NULL ) + { + msg_Err( p_this, "osd_MenuDown failed" ); + return; + } + + if( osd_isVisible( p_osd ) == false ) + { + vlc_object_release( (vlc_object_t*) p_osd ); + return; + } + + var_Get( p_this->p_libvlc, "osd_mutex", &lockval ); + vlc_mutex_lock( lockval.p_address ); + + p_button = p_osd->p_state->p_visible; + if( p_button ) + { + if( !p_button->b_range ) + { + p_button->p_current_state = osd_StateChange( p_button, OSD_BUTTON_SELECT ); + if( p_button->p_down ) + p_osd->p_state->p_visible = p_button->p_down; + } + + if( p_button->b_range && p_osd->p_state->p_visible->b_range ) + { + osd_state_t *p_temp = p_osd->p_state->p_visible->p_current_state; + if( p_temp && p_temp->p_prev ) + p_osd->p_state->p_visible->p_current_state = p_temp->p_prev; + } + else if( !p_osd->p_state->p_visible->b_range ) + { + p_osd->p_state->p_visible->p_current_state = + osd_StateChange( p_osd->p_state->p_visible, OSD_BUTTON_SELECT ); + } + + osd_UpdateState( p_osd->p_state, + p_osd->p_state->p_visible->i_x, p_osd->p_state->p_visible->i_y, + p_osd->p_state->p_visible->p_current_state->i_width, + p_osd->p_state->p_visible->p_current_state->i_height, + p_osd->p_state->p_visible->p_current_state->p_pic ); + osd_SetMenuUpdate( p_osd, true ); + /* If this is a range style action with associated images of only one state, + * then perform "menu select" on every menu navigation + */ + if( p_button->b_range ) + { + osd_SetKeyPressed( VLC_OBJECT(p_osd->p_libvlc), config_GetInt(p_osd, p_button->psz_action_down) ); +#if defined(OSD_MENU_DEBUG) + msg_Dbg( p_osd, "select (%d, %s)", val.i_int, p_button->psz_action_down ); +#endif + } + } +#if defined(OSD_MENU_DEBUG) + msg_Dbg( p_osd, "direction down [button %s]", p_osd->p_state->p_visible->psz_action ); +#endif + + vlc_object_release( (vlc_object_t*) p_osd ); + vlc_mutex_unlock( lockval.p_address ); +} + +static int osd_VolumeStep( vlc_object_t *p_this, int i_volume, int i_steps ) +{ + int i_volume_step = 0; + (void)i_steps; + + i_volume_step = config_GetInt( p_this->p_libvlc, "volume-step" ); + return (i_volume/i_volume_step); +} + +/** + * Display current audio volume bitmap + * + * The OSD Menu audio volume bar is updated to reflect the new audio volume. Call this function + * when the audio volume is updated outside the OSD menu command "menu up", "menu down" or "menu select". + */ +void __osd_Volume( vlc_object_t *p_this ) +{ + osd_menu_t *p_osd = NULL; + osd_button_t *p_button = NULL; + vlc_value_t lockval; + int i_volume = 0; + int i_steps = 0; + + p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE ); + if( p_osd == NULL ) + { + msg_Err( p_this, "OSD menu volume update failed" ); + return; + } + + if( p_osd->p_state && p_osd->p_state->p_volume ) + { + var_Get( p_this->p_libvlc, "osd_mutex", &lockval ); + vlc_mutex_lock( lockval.p_address ); + + p_button = p_osd->p_state->p_volume; + if( p_osd->p_state->p_volume ) + p_osd->p_state->p_visible = p_osd->p_state->p_volume; + if( p_button && p_button->b_range ) + { + /* Update the volume state images to match the current volume */ + i_volume = config_GetInt( p_this, "volume" ); + i_steps = osd_VolumeStep( p_this, i_volume, p_button->i_ranges ); + p_button->p_current_state = osd_VolumeStateChange( p_button->p_states, i_steps ); + + osd_UpdateState( p_osd->p_state, + p_button->i_x, p_button->i_y, + p_button->p_current_state->i_width, + p_button->p_current_state->i_height, + p_button->p_current_state->p_pic ); + osd_SetMenuUpdate( p_osd, true ); + osd_SetMenuVisible( p_osd, true ); + } + vlc_mutex_unlock( lockval.p_address ); + } + vlc_object_release( p_osd ); +} + +osd_button_t *__osd_ButtonFind( vlc_object_t *p_this, int i_x, int i_y, + int i_window_height, int i_window_width, + int i_scale_width, int i_scale_height ) +{ + osd_menu_t *p_osd; + osd_button_t *p_button; + vlc_value_t lockval; + + p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE ); + if( p_osd == NULL ) + { + msg_Err( p_this, "OSD menu button find failed" ); + return NULL; + } + + if( osd_isVisible( p_osd ) == false ) + { + vlc_object_release( p_osd ); + return NULL; + } + + var_Get( p_this->p_libvlc, "osd_mutex", &lockval ); + vlc_mutex_lock( lockval.p_address ); + + p_button = p_osd->p_button; + for( ; p_button != NULL; p_button = p_button->p_next ) + { + int i_source_video_width = ( i_window_width * 1000 ) / i_scale_width; + int i_source_video_height = ( i_window_height * 1000 ) / i_scale_height; + int i_y_offset = p_button->i_y; + int i_x_offset = p_button->i_x; + int i_width = p_button->i_width; + int i_height = p_button->i_height; + + if( p_osd->i_position > 0 ) + { + int i_inv_scale_y = i_source_video_height; + int i_inv_scale_x = i_source_video_width; + int pi_x = 0; + + if( p_osd->i_position & SUBPICTURE_ALIGN_BOTTOM ) + { + i_y_offset = i_window_height - p_button->i_height - + (p_osd->i_y + p_button->i_y) * i_inv_scale_y / 1000; + } + else if ( !(p_osd->i_position & SUBPICTURE_ALIGN_TOP) ) + { + i_y_offset = i_window_height / 2 - p_button->i_height / 2; + } + + if( p_osd->i_position & SUBPICTURE_ALIGN_RIGHT ) + { + i_x_offset = i_window_width - p_button->i_width - + (pi_x + p_button->i_x) + * i_inv_scale_x / 1000; + } + else if ( !(p_osd->i_position & SUBPICTURE_ALIGN_LEFT) ) + { + i_x_offset = i_window_width / 2 - p_button->i_width / 2; + } + + i_width = i_window_width - p_button->i_width - i_inv_scale_x / 1000; + i_height = i_window_height - p_button->i_height - i_inv_scale_y / 1000; + } + + // TODO: write for Up / Down case too. + // TODO: handle absolute positioning case + if( ( i_x >= i_x_offset ) && ( i_x <= i_x_offset + i_width ) && + ( i_y >= i_y_offset ) && ( i_y <= i_y_offset + i_height ) ) + { + vlc_object_release( p_osd ); + vlc_mutex_unlock( lockval.p_address ); + return p_button; + } + } + + vlc_object_release( p_osd ); + vlc_mutex_unlock( lockval.p_address ); + return NULL; +} + +/** + * Select the button provided as the new active button + */ +void __osd_ButtonSelect( vlc_object_t *p_this, osd_button_t *p_button ) +{ + osd_menu_t *p_osd; + osd_button_t *p_old; + vlc_value_t lockval; + + p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE ); + if( p_osd == NULL ) + { + msg_Err( p_this, "OSD menu button select failed" ); + return; + } + + if( osd_isVisible( p_osd ) == false ) + { + vlc_object_release( (vlc_object_t*) p_osd ); + return; + } + + var_Get( p_this->p_libvlc, "osd_mutex", &lockval ); + vlc_mutex_lock( lockval.p_address ); + + p_old = p_osd->p_state->p_visible; + if( p_old ) + { + if( !p_old->b_range ) + p_old->p_current_state = osd_StateChange( p_old, OSD_BUTTON_UNSELECT ); + p_osd->p_state->p_visible = p_button; + + if( !p_osd->p_state->p_visible->b_range ) + p_osd->p_state->p_visible->p_current_state = + osd_StateChange( p_osd->p_state->p_visible, OSD_BUTTON_SELECT ); + + osd_UpdateState( p_osd->p_state, + p_osd->p_state->p_visible->i_x, p_osd->p_state->p_visible->i_y, + p_osd->p_state->p_visible->p_current_state->i_width, + p_osd->p_state->p_visible->p_current_state->i_height, + p_osd->p_state->p_visible->p_current_state->p_pic ); + osd_SetMenuUpdate( p_osd, true ); + } +#if defined(OSD_MENU_DEBUG) + msg_Dbg( p_osd, "button selected is [button %s]", p_osd->p_state->p_visible->psz_action ); +#endif + + vlc_object_release( p_osd ); + vlc_mutex_unlock( lockval.p_address ); +} diff --git a/VLC/osd_text.c b/VLC/osd_text.c new file mode 100644 index 0000000..d00fbf9 --- /dev/null +++ b/VLC/osd_text.c @@ -0,0 +1,141 @@ +/***************************************************************************** + * osd_text.c : text manipulation functions + ***************************************************************************** + * Copyright (C) 1999-2007 the VideoLAN team + * $Id$ + * + * Author: Sigmund Augdal Helberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "vlc_vout.h" +#include "vlc_block.h" +#include "vlc_filter.h" +#include "vlc_osd.h" + +/** + * \brief Show text on the video for some time + * \param p_spu pointer to the subpicture queue the text is to be showed on + * \param i_channel Subpicture channel + * \param psz_string The text to be shown + * \param p_style Pointer to a struct with text style info + * \param i_flags flags for alignment and such + * \param i_hmargin horizontal margin in pixels + * \param i_vmargin vertical margin in pixels + * \param i_duration Amount of time the text is to be shown. + */ +int osd_ShowTextRelative( spu_t *p_spu, int i_channel, + char *psz_string, text_style_t *p_style, + int i_flags, int i_hmargin, int i_vmargin, + mtime_t i_duration ) +{ + mtime_t i_now = mdate(); + + return osd_ShowTextAbsolute( p_spu, i_channel, psz_string, + p_style, i_flags, i_hmargin, i_vmargin, + i_now, i_now + i_duration ); +} + +/** + * \brief Show text on the video from a given start date to a given end date + * \param p_spu pointer to the subpicture queue the text is to be showed on + * \param i_channel Subpicture channel + * \param psz_string The text to be shown + * \param p_style Pointer to a struct with text style info + * \param i_flags flags for alignment and such + * \param i_hmargin horizontal margin in pixels + * \param i_vmargin vertical margin in pixels + * \param i_start the time when this string is to appear on the video + * \param i_stop the time when this string should stop to be displayed + * if this is 0 the string will be shown untill the next string + * is about to be shown + */ +int osd_ShowTextAbsolute( spu_t *p_spu_channel, int i_channel, + char *psz_string, text_style_t *p_style, + int i_flags, int i_hmargin, int i_vmargin, + mtime_t i_start, mtime_t i_stop ) +{ + subpicture_t *p_spu; + video_format_t fmt; + (void)p_style; + + if( !psz_string ) return VLC_EGENERIC; + + p_spu = spu_CreateSubpicture( p_spu_channel ); + if( !p_spu ) return VLC_EGENERIC; + + /* Create a new subpicture region */ + memset( &fmt, 0, sizeof(video_format_t) ); + fmt.i_chroma = VLC_FOURCC('T','E','X','T'); + fmt.i_aspect = 0; + fmt.i_width = fmt.i_height = 0; + fmt.i_x_offset = fmt.i_y_offset = 0; + p_spu->p_region = p_spu->pf_create_region( VLC_OBJECT(p_spu_channel), &fmt ); + if( !p_spu->p_region ) + { + msg_Err( p_spu_channel, "cannot allocate SPU region" ); + spu_DestroySubpicture( p_spu_channel, p_spu ); + return VLC_EGENERIC; + } + + p_spu->p_region->psz_text = strdup( psz_string ); + p_spu->p_region->i_align = i_flags & SUBPICTURE_ALIGN_MASK; + p_spu->i_start = i_start; + p_spu->i_stop = i_stop; + p_spu->b_ephemer = true; + p_spu->b_absolute = false; + + p_spu->i_x = i_hmargin; + p_spu->i_y = i_vmargin; + p_spu->i_flags = i_flags & ~SUBPICTURE_ALIGN_MASK; + p_spu->i_channel = i_channel; + + spu_DisplaySubpicture( p_spu_channel, p_spu ); + + return VLC_SUCCESS; +} + + +/** + * \brief Write an informative message at the default location, + * for the default duration and only if the OSD option is enabled. + * \param p_caller The object that called the function. + * \param i_channel Subpicture channel + * \param psz_format printf style formatting + **/ +void osd_Message( spu_t *p_spu, int i_channel, + char *psz_format, ... ) +{ + va_list args; + + if( p_spu ) + { + char *psz_string; + va_start( args, psz_format ); + if( vasprintf( &psz_string, psz_format, args ) != -1 ) + { + osd_ShowTextRelative( p_spu, i_channel, psz_string, NULL, + OSD_ALIGN_TOP|OSD_ALIGN_RIGHT, 30,20,1000000 ); + + free( psz_string ); + } + va_end( args ); + } +} diff --git a/VLC/osd_widgets.c b/VLC/osd_widgets.c new file mode 100644 index 0000000..b39d7e3 --- /dev/null +++ b/VLC/osd_widgets.c @@ -0,0 +1,345 @@ +/***************************************************************************** + * osd_widgets.c : OSD widgets manipulation functions + ***************************************************************************** + * Copyright (C) 2004-2007 the VideoLAN team + * $Id$ + * + * Author: Yoann Peronneau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "vlc_osd.h" +#include "vlc_vout.h" +#include "vlc_filter.h" + +#define STYLE_EMPTY 0 +#define STYLE_FILLED 1 + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +static void DrawRect( subpicture_t *, int, int, int, int, short ); +static void DrawTriangle( subpicture_t *, int, int, int, int, short ); +static int CreatePicture( spu_t *, subpicture_t *, int, int, int, int ); +static subpicture_t *osd_CreateWidget( spu_t *, int ); + +/***************************************************************************** + * Draws a rectangle at the given position in the subpic. + * It may be filled (fill == STYLE_FILLED) or empty (fill == STYLE_EMPTY). + *****************************************************************************/ +static void DrawRect( subpicture_t *p_subpic, int i_x1, int i_y1, + int i_x2, int i_y2, short fill ) +{ + int x, y; + uint8_t *p_a = p_subpic->p_region->picture.A_PIXELS; + int i_pitch = p_subpic->p_region->picture.Y_PITCH; + + if( fill == STYLE_FILLED ) + { + for( y = i_y1; y <= i_y2; y++ ) + { + for( x = i_x1; x <= i_x2; x++ ) + { + p_a[ x + i_pitch * y ] = 0xff; + } + } + } + else + { + for( y = i_y1; y <= i_y2; y++ ) + { + p_a[ i_x1 + i_pitch * y ] = 0xff; + p_a[ i_x2 + i_pitch * y ] = 0xff; + } + for( x = i_x1; x <= i_x2; x++ ) + { + p_a[ x + i_pitch * i_y1 ] = 0xff; + p_a[ x + i_pitch * i_y2 ] = 0xff; + } + } +} + +/***************************************************************************** + * Draws a triangle at the given position in the subpic. + * It may be filled (fill == STYLE_FILLED) or empty (fill == STYLE_EMPTY). + *****************************************************************************/ +static void DrawTriangle( subpicture_t *p_subpic, int i_x1, int i_y1, + int i_x2, int i_y2, short fill ) +{ + int x, y, i_mid, h; + uint8_t *p_a = p_subpic->p_region->picture.A_PIXELS; + int i_pitch = p_subpic->p_region->picture.Y_PITCH; + + i_mid = i_y1 + ( ( i_y2 - i_y1 ) >> 1 ); + + if( i_x2 >= i_x1 ) + { + if( fill == STYLE_FILLED ) + { + for( y = i_y1; y <= i_mid; y++ ) + { + h = y - i_y1; + for( x = i_x1; x <= i_x1 + h && x <= i_x2; x++ ) + { + p_a[ x + i_pitch * y ] = 0xff; + p_a[ x + i_pitch * ( i_y2 - h ) ] = 0xff; + } + } + } + else + { + for( y = i_y1; y <= i_mid; y++ ) + { + h = y - i_y1; + p_a[ i_x1 + i_pitch * y ] = 0xff; + p_a[ i_x1 + h + i_pitch * y ] = 0xff; + p_a[ i_x1 + i_pitch * ( i_y2 - h ) ] = 0xff; + p_a[ i_x1 + h + i_pitch * ( i_y2 - h ) ] = 0xff; + } + } + } + else + { + if( fill == STYLE_FILLED ) + { + for( y = i_y1; y <= i_mid; y++ ) + { + h = y - i_y1; + for( x = i_x1; x >= i_x1 - h && x >= i_x2; x-- ) + { + p_a[ x + i_pitch * y ] = 0xff; + p_a[ x + i_pitch * ( i_y2 - h ) ] = 0xff; + } + } + } + else + { + for( y = i_y1; y <= i_mid; y++ ) + { + h = y - i_y1; + p_a[ i_x1 + i_pitch * y ] = 0xff; + p_a[ i_x1 - h + i_pitch * y ] = 0xff; + p_a[ i_x1 + i_pitch * ( i_y2 - h ) ] = 0xff; + p_a[ i_x1 - h + i_pitch * ( i_y2 - h ) ] = 0xff; + } + } + } +} + +/***************************************************************************** + * Create Picture: creates subpicture region and picture + *****************************************************************************/ +static int CreatePicture( spu_t *p_spu, subpicture_t *p_subpic, + int i_x, int i_y, int i_width, int i_height ) +{ + uint8_t *p_y, *p_u, *p_v, *p_a; + video_format_t fmt; + int i_pitch; + + /* Create a new subpicture region */ + memset( &fmt, 0, sizeof(video_format_t) ); + fmt.i_chroma = VLC_FOURCC('Y','U','V','A'); + fmt.i_aspect = 0; + fmt.i_width = fmt.i_visible_width = i_width; + fmt.i_height = fmt.i_visible_height = i_height; + fmt.i_x_offset = fmt.i_y_offset = 0; + p_subpic->p_region = p_subpic->pf_create_region( VLC_OBJECT(p_spu), &fmt ); + if( !p_subpic->p_region ) + { + msg_Err( p_spu, "cannot allocate SPU region" ); + return VLC_EGENERIC; + } + + p_subpic->p_region->i_x = i_x; + p_subpic->p_region->i_y = i_y; + p_y = p_subpic->p_region->picture.Y_PIXELS; + p_u = p_subpic->p_region->picture.U_PIXELS; + p_v = p_subpic->p_region->picture.V_PIXELS; + p_a = p_subpic->p_region->picture.A_PIXELS; + i_pitch = p_subpic->p_region->picture.Y_PITCH; + + /* Initialize the region pixels (only the alpha will be changed later) */ + memset( p_y, 0xff, i_pitch * p_subpic->p_region->fmt.i_height ); + memset( p_u, 0x80, i_pitch * p_subpic->p_region->fmt.i_height ); + memset( p_v, 0x80, i_pitch * p_subpic->p_region->fmt.i_height ); + memset( p_a, 0x00, i_pitch * p_subpic->p_region->fmt.i_height ); + + return VLC_SUCCESS; +} + +/***************************************************************************** + * Creates and initializes an OSD widget. + *****************************************************************************/ +subpicture_t *osd_CreateWidget( spu_t *p_spu, int i_channel ) +{ + subpicture_t *p_subpic; + mtime_t i_now = mdate(); + + /* Create and initialize a subpicture */ + p_subpic = spu_CreateSubpicture( p_spu ); + if( p_subpic == NULL ) return NULL; + + p_subpic->i_channel = i_channel; + p_subpic->i_start = i_now; + p_subpic->i_stop = i_now + 1200000; + p_subpic->b_ephemer = true; + p_subpic->b_fade = true; + + return p_subpic; +} + +/***************************************************************************** + * Displays an OSD slider. + * Types are: OSD_HOR_SLIDER and OSD_VERT_SLIDER. + *****************************************************************************/ +int osd_Slider( vlc_object_t *p_this, spu_t *p_spu, + int i_render_width, int i_render_height, + int i_margin_left, int i_margin_bottom, + int i_channel, int i_position, short i_type ) +{ + subpicture_t *p_subpic; + int i_x_margin, i_y_margin, i_x, i_y, i_width, i_height; + (void)p_this; + + p_subpic = osd_CreateWidget( p_spu, i_channel ); + if( p_subpic == NULL ) + { + return VLC_EGENERIC; + } + + i_y_margin = i_render_height / 10; + i_x_margin = i_y_margin + i_margin_left; + i_y_margin += i_margin_bottom; + + if( i_type == OSD_HOR_SLIDER ) + { + i_width = i_render_width - 2 * i_x_margin; + i_height = i_render_height / 20; + i_x = i_x_margin; + i_y = i_render_height - i_y_margin - i_height; + } + else + { + i_width = i_render_width / 40; + i_height = i_render_height - 2 * i_y_margin; + i_x = i_render_width - i_x_margin - i_width; + i_y = i_y_margin; + } + + /* Create subpicture region and picture */ + CreatePicture( p_spu, p_subpic, i_x, i_y, i_width, i_height ); + + if( i_type == OSD_HOR_SLIDER ) + { + int i_x_pos = ( i_width - 2 ) * i_position / 100; + DrawRect( p_subpic, i_x_pos - 1, 2, i_x_pos + 1, + i_height - 3, STYLE_FILLED ); + DrawRect( p_subpic, 0, 0, i_width - 1, i_height - 1, STYLE_EMPTY ); + } + else if( i_type == OSD_VERT_SLIDER ) + { + int i_y_pos = i_height / 2; + DrawRect( p_subpic, 2, i_height - ( i_height - 2 ) * i_position / 100, + i_width - 3, i_height - 3, STYLE_FILLED ); + DrawRect( p_subpic, 1, i_y_pos, 1, i_y_pos, STYLE_FILLED ); + DrawRect( p_subpic, i_width - 2, i_y_pos, + i_width - 2, i_y_pos, STYLE_FILLED ); + DrawRect( p_subpic, 0, 0, i_width - 1, i_height - 1, STYLE_EMPTY ); + } + + spu_DisplaySubpicture( p_spu, p_subpic ); + + return VLC_SUCCESS; +} + +/***************************************************************************** + * Displays an OSD icon. + * Types are: OSD_PLAY_ICON, OSD_PAUSE_ICON, OSD_SPEAKER_ICON, OSD_MUTE_ICON + *****************************************************************************/ +int osd_Icon( vlc_object_t *p_this, spu_t *p_spu, + int i_render_width, int i_render_height, int i_margin_right, + int i_margin_top, int i_channel, short i_type ) +{ + subpicture_t *p_subpic; + int i_x_margin, i_y_margin, i_x, i_y, i_width, i_height; + (void)p_this; + + p_subpic = osd_CreateWidget( p_spu, i_channel ); + if( p_subpic == NULL ) + { + return VLC_EGENERIC; + } + + i_y_margin = i_render_height / 15; + i_x_margin = i_y_margin + i_margin_right; + i_y_margin += i_margin_top; + i_width = i_render_width / 20; + i_height = i_width; + i_x = i_render_width - i_x_margin - i_width; + i_y = i_y_margin; + + /* Create subpicture region and picture */ + CreatePicture( p_spu, p_subpic, i_x, i_y, i_width, i_height ); + + if( i_type == OSD_PAUSE_ICON ) + { + int i_bar_width = i_width / 3; + DrawRect( p_subpic, 0, 0, i_bar_width - 1, i_height -1, STYLE_FILLED ); + DrawRect( p_subpic, i_width - i_bar_width, 0, + i_width - 1, i_height - 1, STYLE_FILLED ); + } + else if( i_type == OSD_PLAY_ICON ) + { + int i_mid = i_height >> 1; + int i_delta = ( i_width - i_mid ) >> 1; + int i_y2 = ( ( i_height - 1 ) >> 1 ) * 2; + DrawTriangle( p_subpic, i_delta, 0, i_width - i_delta, i_y2, + STYLE_FILLED ); + } + else if( i_type == OSD_SPEAKER_ICON || i_type == OSD_MUTE_ICON ) + { + int i_mid = i_height >> 1; + int i_delta = ( i_width - i_mid ) >> 1; + int i_y2 = ( ( i_height - 1 ) >> 1 ) * 2; + DrawRect( p_subpic, i_delta, i_mid / 2, i_width - i_delta, + i_height - 1 - i_mid / 2, STYLE_FILLED ); + DrawTriangle( p_subpic, i_width - i_delta, 0, i_delta, i_y2, + STYLE_FILLED ); + if( i_type == OSD_MUTE_ICON ) + { + uint8_t *p_a = p_subpic->p_region->picture.A_PIXELS; + int i_pitch = p_subpic->p_region->picture.Y_PITCH; + int i; + for( i = 1; i < i_pitch; i++ ) + { + int k = i + ( i_height - i - 1 ) * i_pitch; + p_a[ k ] = 0xff - p_a[ k ]; + } + } + } + + spu_DisplaySubpicture( p_spu, p_subpic ); + + return VLC_SUCCESS; +} diff --git a/VLC/playlist.c b/VLC/playlist.c new file mode 100644 index 0000000..4cd0ecc --- /dev/null +++ b/VLC/playlist.c @@ -0,0 +1,259 @@ +/***************************************************************************** + * playlist.c: libvlc new API playlist handling functions + ***************************************************************************** + * Copyright (C) 2005 the VideoLAN team + * $Id$ + * + * Authors: Clément Stenac + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#include "libvlc_internal.h" + +#include +#include + +#include + +#include "../playlist/playlist_internal.h" + +#define PL (libvlc_priv (p_instance->p_libvlc_int)->p_playlist) + +static inline int playlist_was_locked( libvlc_instance_t *p_instance ) +{ + int was_locked; + vlc_mutex_lock( &p_instance->instance_lock ); + was_locked = p_instance->b_playlist_locked; + vlc_mutex_unlock( &p_instance->instance_lock ); + return was_locked; +} + +static inline void playlist_mark_locked( libvlc_instance_t *p_instance, + int locked ) +{ + vlc_mutex_lock( &p_instance->instance_lock ); + p_instance->b_playlist_locked = locked; + vlc_mutex_unlock( &p_instance->instance_lock ); +} + +void libvlc_playlist_loop( libvlc_instance_t *p_instance, int loop, + libvlc_exception_t *p_e) +{ + VLC_UNUSED(p_e); + + assert( PL ); + var_SetBool( PL, "loop", loop ); +} + +void libvlc_playlist_play( libvlc_instance_t *p_instance, int i_id, + int i_options, char **ppsz_options, + libvlc_exception_t *p_e ) +{ + VLC_UNUSED(p_e); VLC_UNUSED(i_options); VLC_UNUSED(ppsz_options); + + int did_lock = 0; + assert( PL ); + ///\todo Handle additionnal options + + if( PL->items.i_size == 0 ) RAISEVOID( "Empty playlist" ); + if( i_id > 0 ) + { + playlist_item_t *p_item; + if (! playlist_was_locked( p_instance ) ) + { + playlist_mark_locked( p_instance, 1 ); + vlc_object_lock( PL ); + did_lock = 1; + } + + p_item = playlist_ItemGetByInputId( PL, i_id, + PL->status.p_node ); + if( !p_item ) + { + if( did_lock == 1 ) + { + vlc_object_unlock( PL ); + playlist_mark_locked( p_instance, 0 ); + } + RAISEVOID( "Unable to find item" ); + } + + playlist_Control( PL, PLAYLIST_VIEWPLAY, pl_Locked, + PL->status.p_node, p_item ); + if( did_lock == 1 ) + { + vlc_object_unlock( PL ); + playlist_mark_locked( p_instance, 0 ); + } + } + else + { + playlist_Control( PL, PLAYLIST_PLAY, + playlist_was_locked( p_instance ) ); + } +} + +void libvlc_playlist_pause( libvlc_instance_t *p_instance, + libvlc_exception_t *p_e ) +{ + assert( PL ); + if( playlist_Control( PL, PLAYLIST_PAUSE, + playlist_was_locked( p_instance ) ) != VLC_SUCCESS ) + RAISEVOID( "Empty playlist" ); +} + + +void libvlc_playlist_stop( libvlc_instance_t *p_instance, + libvlc_exception_t *p_e ) +{ + assert( PL ); + if( playlist_Control( PL, PLAYLIST_STOP, + playlist_was_locked( p_instance ) ) != VLC_SUCCESS ) + RAISEVOID( "Empty playlist" ); +} + +void libvlc_playlist_clear( libvlc_instance_t *p_instance, + libvlc_exception_t *p_e ) +{ + VLC_UNUSED(p_e); + + assert( PL ); + playlist_Clear( PL, playlist_was_locked( p_instance ) ); +} + +void libvlc_playlist_next( libvlc_instance_t *p_instance, + libvlc_exception_t *p_e ) +{ + assert( PL ); + if( playlist_Control( PL, PLAYLIST_SKIP, playlist_was_locked( p_instance ), + 1 ) != VLC_SUCCESS ) + RAISEVOID( "Empty playlist" ); +} + +void libvlc_playlist_prev( libvlc_instance_t *p_instance, + libvlc_exception_t *p_e ) +{ + if( playlist_Control( PL, PLAYLIST_SKIP, playlist_was_locked( p_instance ), + -1 ) != VLC_SUCCESS ) + RAISEVOID( "Empty playlist" ); +} + +int libvlc_playlist_add( libvlc_instance_t *p_instance, const char *psz_uri, + const char *psz_name, libvlc_exception_t *p_e ) +{ + return libvlc_playlist_add_extended( p_instance, psz_uri, psz_name, + 0, NULL, p_e ); +} + +int libvlc_playlist_add_extended( libvlc_instance_t *p_instance, + const char *psz_uri, const char *psz_name, + int i_options, const char **ppsz_options, + libvlc_exception_t *p_e ) +{ + assert( PL ); + if( playlist_was_locked( p_instance ) ) + { + libvlc_exception_raise( p_e, "You must unlock playlist before " + "calling libvlc_playlist_add" ); + return VLC_EGENERIC; + } + return playlist_AddExt( PL, psz_uri, psz_name, + PLAYLIST_INSERT, PLAYLIST_END, -1, ppsz_options, + i_options, 1, pl_Unlocked ); +} + + +int libvlc_playlist_delete_item( libvlc_instance_t *p_instance, int i_id, + libvlc_exception_t *p_e ) +{ + assert( PL ); + + if( playlist_DeleteFromInput( PL, i_id, + playlist_was_locked( p_instance ) ) ) + { + libvlc_exception_raise( p_e, "deletion failed" ); + return VLC_ENOITEM; + } + return VLC_SUCCESS; +} + +int libvlc_playlist_isplaying( libvlc_instance_t *p_instance, + libvlc_exception_t *p_e ) +{ + VLC_UNUSED(p_e); + + assert( PL ); + return playlist_IsPlaying( PL ); +} + +int libvlc_playlist_items_count( libvlc_instance_t *p_instance, + libvlc_exception_t *p_e ) +{ + VLC_UNUSED(p_e); + + assert( PL ); + return playlist_CurrentSize( PL ); +} + +int libvlc_playlist_get_current_index ( libvlc_instance_t *p_instance, + libvlc_exception_t *p_e ) +{ + VLC_UNUSED(p_e); + + assert( PL ); + if( !PL->status.p_item ) + return -1; + return playlist_CurrentId( PL ); +} + +void libvlc_playlist_lock( libvlc_instance_t *p_instance ) +{ + assert( PL ); + vlc_object_lock( PL ); + p_instance->b_playlist_locked = 1; +} + +void libvlc_playlist_unlock( libvlc_instance_t *p_instance ) +{ + assert( PL ); + p_instance->b_playlist_locked = 0; + vlc_object_unlock( PL ); +} + +libvlc_media_player_t * libvlc_playlist_get_media_player( + libvlc_instance_t *p_instance, + libvlc_exception_t *p_e ) +{ + libvlc_media_player_t *p_mi; + assert( PL ); + + vlc_object_lock( PL ); + if( PL->p_input ) + { + p_mi = libvlc_media_player_new_from_input_thread( + p_instance, PL->p_input, p_e ); + } + else + { + /* no active input */ + p_mi = NULL; + libvlc_exception_raise( p_e, "No active input" ); + } + vlc_object_unlock( PL ); + + return p_mi; +} + diff --git a/VLC/playlist_internal.h b/VLC/playlist_internal.h new file mode 100644 index 0000000..6ea98a2 --- /dev/null +++ b/VLC/playlist_internal.h @@ -0,0 +1,152 @@ +/***************************************************************************** + * playlist_internal.h : Functions for use by the playlist + ***************************************************************************** + * Copyright (C) 1999-2004 the VideoLAN team + * $Id$ + * + * Authors: Samuel Hocevar + * Clément Stenac + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef __LIBVLC_PLAYLIST_INTERNAL_H +# define __LIBVLC_PLAYLIST_INTERNAL_H 1 + +/** + * \file + * This file contain internal structures and function prototypes related + * to the playlist in vlc + * + * \defgroup vlc_playlist Playlist + * @{ + */ + +#include "input/input_internal.h" +#include + +struct playlist_preparse_t +{ + VLC_COMMON_MEMBERS + vlc_mutex_t lock; + int i_waiting; + input_item_t **pp_waiting; +}; + +struct playlist_fetcher_t +{ + VLC_COMMON_MEMBERS + vlc_mutex_t lock; + int i_art_policy; + int i_waiting; + input_item_t **pp_waiting; + + DECL_ARRAY(playlist_album_t) albums; +}; + +/***************************************************************************** + * Prototypes + *****************************************************************************/ + +/* Global thread */ +#define playlist_ThreadCreate(a) __playlist_ThreadCreate(VLC_OBJECT(a)) +void __playlist_ThreadCreate ( vlc_object_t * ); + +playlist_item_t *playlist_ItemNewFromInput( playlist_t *p_playlist, + input_item_t *p_input ); + +/* Creation/Deletion */ +playlist_t *playlist_Create ( vlc_object_t * ); + +/* Engine */ +void playlist_MainLoop( playlist_t * ); +void playlist_LastLoop( playlist_t * ); +void playlist_PreparseLoop( playlist_preparse_t * ); +void playlist_FetcherLoop( playlist_fetcher_t * ); + +void ResetCurrentlyPlaying( playlist_t *, bool, playlist_item_t * ); + +playlist_item_t * get_current_status_item( playlist_t * p_playlist); +playlist_item_t * get_current_status_node( playlist_t * p_playlist ); +void set_current_status_item( playlist_t *, playlist_item_t * ); +void set_current_status_node( playlist_t *, playlist_item_t * ); + +/* Control */ +playlist_item_t * playlist_NextItem ( playlist_t * ); +int playlist_PlayItem ( playlist_t *, playlist_item_t * ); + +/* Load/Save */ +int playlist_MLLoad( playlist_t *p_playlist ); +int playlist_MLDump( playlist_t *p_playlist ); + +/********************************************************************** + * Item management + **********************************************************************/ + +void playlist_SendAddNotify( playlist_t *p_playlist, int i_item_id, + int i_node_id, bool b_signal ); + +playlist_item_t * playlist_NodeAddInput( playlist_t *, input_item_t *, + playlist_item_t *,int , int, bool ); + +/* Tree walking */ +playlist_item_t *playlist_ItemFindFromInputAndRoot( playlist_t *p_playlist, + int i_input_id, playlist_item_t *p_root, + bool ); + +int playlist_DeleteFromInputInParent( playlist_t *, int, playlist_item_t *, bool ); +int playlist_DeleteFromItemId( playlist_t*, int ); +int playlist_ItemRelease( playlist_item_t * ); + +void playlist_release_current_input( playlist_t * p_playlist ); +void playlist_set_current_input( + playlist_t * p_playlist, input_thread_t * p_input ); + +/** + * @} + */ + +#define PLAYLIST_DEBUG 1 +//#undef PLAYLIST_DEBUG2 + +#ifdef PLAYLIST_DEBUG + #define PL_DEBUG( msg, args... ) msg_Dbg( p_playlist, msg, ## args ) + #ifdef PLAYLIST_DEBUG2 + #define PL_DEBUG2( msg, args... ) msg_Dbg( p_playlist, msg, ## args ) + #else + #define PL_DEBUG2( msg, args... ) {} + #endif +#else + #define PL_DEBUG( msg, args ... ) {} + #define PL_DEBUG2( msg, args... ) {} +#endif + +#define PLI_NAME( p ) p && p->p_input ? p->p_input->psz_name : "null" + +#define PL_ASSERT_LOCKED vlc_assert_locked( &(vlc_internals(p_playlist)->lock) ) + +#define PL_LOCK_IF( cond ) pl_lock_if( p_playlist, cond ) +static inline void pl_lock_if( playlist_t * p_playlist, bool cond ) +{ + if( cond ) PL_LOCK; else PL_ASSERT_LOCKED; +} + +#define PL_UNLOCK_IF( cond ) pl_unlock_if( p_playlist, cond ) +static inline void pl_unlock_if( playlist_t * p_playlist, bool cond ) +{ + if( cond ) PL_UNLOCK; +} + +#endif /* !__LIBVLC_PLAYLIST_INTERNAL_H */ diff --git a/VLC/poll.c b/VLC/poll.c new file mode 100644 index 0000000..3b4b625 --- /dev/null +++ b/VLC/poll.c @@ -0,0 +1,108 @@ +/***************************************************************************** + * poll.c: I/O event multiplexing + ***************************************************************************** + * Copyright © 2007 Rémi Denis-Courmont + * $Id$ + * + * Author: Rémi Denis-Courmont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "vlc_network.h" + +#ifdef HAVE_POLL +struct pollfd; + +int vlc_poll (struct pollfd *fds, unsigned nfds, int timeout) +{ + (void)fds; (void)nfds; (void)timeout; + abort (); +} +#else /* !HAVE_POLL */ +#include +#include +#include "vlc_network.h" + +int vlc_poll (struct pollfd *fds, unsigned nfds, int timeout) +{ + fd_set rdset, wrset, exset; + struct timeval tv = { 0, 0 }; + int val = -1; + + FD_ZERO (&rdset); + FD_ZERO (&wrset); + FD_ZERO (&exset); + for (unsigned i = 0; i < nfds; i++) + { + int fd = fds[i].fd; + if (val < fd) + val = fd; + + /* With POSIX, FD_SET & FD_ISSET are not defined if fd is negative or + * bigger or equal than FD_SETSIZE. That is one of the reasons why VLC + * uses poll() rather than select(). Most POSIX systems implement + * fd_set has a bit field with no sanity checks. This is especially bad + * on systems (such as BSD) that have no process open files limit by + * default, such that it is quite feasible to get fd >= FD_SETSIZE. + * The next instructions will result in a buffer overflow if run on + * a POSIX system, and the later FD_ISSET will do undefined memory + * access. + * + * With Winsock, fd_set is a table of integers. This is awfully slow. + * However, FD_SET and FD_ISSET silently and safely discard + * overflows. If it happens we will loose socket events. Note that + * most (if not all) Winsock SOCKET handles are actually bigger than + * FD_SETSIZE in terms of absolute value - they are not POSIX file + * descriptors. From Vista, there is a much nicer WSAPoll(), but Mingw + * is yet to support it. + * + * With BeOS, the situation is unknown (FIXME: document). + */ + if (fds[i].events & POLLIN) + FD_SET (fd, &rdset); + if (fds[i].events & POLLOUT) + FD_SET (fd, &wrset); + if (fds[i].events & POLLPRI) + FD_SET (fd, &exset); + } + + if (timeout >= 0) + { + div_t d = div (timeout, 1000); + tv.tv_sec = d.quot; + tv.tv_usec = d.rem * 1000; + } + + val = select (val + 1, &rdset, &wrset, &exset, + (timeout >= 0) ? &tv : NULL); + if (val == -1) + return -1; + + for (unsigned i = 0; i < nfds; i++) + { + int fd = fds[i].fd; + fds[i].revents = (FD_ISSET (fd, &rdset) ? POLLIN : 0) + | (FD_ISSET (fd, &wrset) ? POLLOUT : 0) + | (FD_ISSET (fd, &exset) ? POLLPRI : 0); + } + return val; +} +#endif /* !HAVE_POLL */ diff --git a/VLC/rootbind.c b/VLC/rootbind.c new file mode 100644 index 0000000..dfdf624 --- /dev/null +++ b/VLC/rootbind.c @@ -0,0 +1,172 @@ +/***************************************************************************** + * rootbind.c: bind to reserved ports through the root wrapper + ***************************************************************************** + * Copyright © 2005-2008 Rémi Denis-Courmont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#if HAVE_CONFIG_H +# include +#endif + +#if !defined (WIN32) && !defined (SYS_BEOS) +# define ENABLE_ROOTWRAP 1 +#endif + +#include +struct sockaddr; +int rootwrap_bind (int, int, int, const struct sockaddr *, size_t); + +#include + +#ifdef ENABLE_ROOTWRAP + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/** + * Receive a file descriptor from another process + */ +static int recv_fd (int p) +{ + struct msghdr hdr; + struct iovec iov; + struct cmsghdr *cmsg; + int val, fd; + char buf[CMSG_SPACE (sizeof (fd))]; + + hdr.msg_name = NULL; + hdr.msg_namelen = 0; + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; + hdr.msg_control = buf; + hdr.msg_controllen = sizeof (buf); + + iov.iov_base = &val; + iov.iov_len = sizeof (val); + + if (recvmsg (p, &hdr, 0) != sizeof (val)) + return -1; + + for (cmsg = CMSG_FIRSTHDR (&hdr); cmsg != NULL; + cmsg = CMSG_NXTHDR (&hdr, cmsg)) + { + if ((cmsg->cmsg_level == SOL_SOCKET) + && (cmsg->cmsg_type = SCM_RIGHTS) + && (cmsg->cmsg_len >= CMSG_LEN (sizeof (fd)))) + { + memcpy (&fd, CMSG_DATA (cmsg), sizeof (fd)); + return fd; + } + } + + errno = val; + return -1; +} + +/** + * Tries to obtain a bound TCP socket from the root process + */ +int rootwrap_bind (int family, int socktype, int protocol, + const struct sockaddr *addr, size_t alen) +{ + /* can't use libvlc */ + static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + struct sockaddr_storage ss; + int fd, sock = -1; + + const char *sockenv = getenv ("VLC_ROOTWRAP_SOCK"); + if (sockenv != NULL) + sock = atoi (sockenv); + if (sock == -1) + { + errno = EACCES; + return -1; + } + + switch (family) + { + case AF_INET: + if (alen < sizeof (struct sockaddr_in)) + { + errno = EINVAL; + return -1; + } + break; + +#ifdef AF_INET6 + case AF_INET6: + if (alen < sizeof (struct sockaddr_in6)) + { + errno = EINVAL; + return -1; + } + break; +#endif + + default: + errno = EAFNOSUPPORT; + return -1; + } + + if (family != addr->sa_family) + { + errno = EAFNOSUPPORT; + return -1; + } + + /* Only TCP is implemented at the moment */ + if ((socktype != SOCK_STREAM) + || (protocol && (protocol != IPPROTO_TCP))) + { + errno = EACCES; + return -1; + } + + memset (&ss, 0, sizeof (ss)); + memcpy (&ss, addr, (alen > sizeof (ss)) ? sizeof (ss) : alen); + + pthread_mutex_lock (&mutex); + if (send (sock, &ss, sizeof (ss), 0) != sizeof (ss)) + return -1; + + fd = recv_fd (sock); + pthread_mutex_unlock (&mutex); + return fd; +} + +#else +int rootwrap_bind (int family, int socktype, int protocol, + const struct sockaddr *addr, size_t alen) +{ + (void)family; + (void)socktype; + (void)protocol; + (void)addr; + (void)alen; + errno = EACCES; + return -1; +} + +#endif /* ENABLE_ROOTWRAP */ diff --git a/VLC/rtmp_amf_flv.c b/VLC/rtmp_amf_flv.c new file mode 100644 index 0000000..7526f00 --- /dev/null +++ b/VLC/rtmp_amf_flv.c @@ -0,0 +1,2564 @@ +/***************************************************************************** + * rtmp_amf_flv.c: RTMP, AMF and FLV over RTMP implementation. + ***************************************************************************** + * Copyright (C) URJC - LADyR - Luis Lopez Fernandez + * + * Author: Miguel Angel Cabrera Moya + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * RTMP header: + ******************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "vlc_access.h" + +#include "vlc_network.h" /* DOWN: #include */ +#include "vlc_url.h" +#include "vlc_block.h" + +#include +#include +#include + +#include "rtmp_amf_flv.h" + +/* header length (including itself) */ +const uint8_t RTMP_HEADER_SIZE_MASK = 0xC0; +const uint8_t RTMP_HEADER_SIZE_12 = 0x00; +const uint8_t RTMP_HEADER_SIZE_8 = 0x40; +const uint8_t RTMP_HEADER_SIZE_4 = 0x80; +const uint8_t RTMP_HEADER_SIZE_1 = 0xC0; + +/* streams */ +const uint8_t RTMP_HEADER_STREAM_MAX = 64; +const uint8_t RTMP_HEADER_STREAM_INDEX_MASK = 0x3F; + +/* handshake */ +const uint8_t RTMP_HANDSHAKE = 0x03; +const uint16_t RTMP_HANDSHAKE_BODY_SIZE = 1536; + +/* content types */ +const uint8_t RTMP_CONTENT_TYPE_CHUNK_SIZE = 0x01; +const uint8_t RTMP_CONTENT_TYPE_UNKNOWN_02 = 0x02; +const uint8_t RTMP_CONTENT_TYPE_BYTES_READ = 0x03; +const uint8_t RTMP_CONTENT_TYPE_PING = 0x04; +const uint8_t RTMP_CONTENT_TYPE_SERVER_BW = 0x05; +const uint8_t RTMP_CONTENT_TYPE_CLIENT_BW = 0x06; +const uint8_t RTMP_CONTENT_TYPE_UNKNOWN_07 = 0x07; +const uint8_t RTMP_CONTENT_TYPE_AUDIO_DATA = 0x08; +const uint8_t RTMP_CONTENT_TYPE_VIDEO_DATA = 0x09; +const uint8_t RTMP_CONTENT_TYPE_UNKNOWN_0A_0E = 0x0A; +const uint8_t RTMP_CONTENT_TYPE_FLEX_STREAM = 0x0F; +const uint8_t RTMP_CONTENT_TYPE_FLEX_SHARED_OBJECT = 0x10; +const uint8_t RTMP_CONTENT_TYPE_MESSAGE = 0x11; +const uint8_t RTMP_CONTENT_TYPE_NOTIFY = 0x12; +const uint8_t RTMP_CONTENT_TYPE_SHARED_OBJECT = 0x13; +const uint8_t RTMP_CONTENT_TYPE_INVOKE = 0x14; + +/* shared object datatypes */ +const uint8_t RTMP_SHARED_OBJECT_DATATYPE_CONNECT = 0x01; +const uint8_t RTMP_SHARED_OBJECT_DATATYPE_DISCONNECT = 0x02; +const uint8_t RTMP_SHARED_OBJECT_DATATYPE_SET_ATTRIBUTE = 0x03; +const uint8_t RTMP_SHARED_OBJECT_DATATYPE_UPDATE_DATA = 0x04; +const uint8_t RTMP_SHARED_OBJECT_DATATYPE_UPDATE_ATTRIBUTE = 0x05; +const uint8_t RTMP_SHARED_OBJECT_DATATYPE_SEND_MESSAGE = 0x06; +const uint8_t RTMP_SHARED_OBJECT_DATATYPE_STATUS = 0x07; +const uint8_t RTMP_SHARED_OBJECT_DATATYPE_CLEAR_DATA = 0x08; +const uint8_t RTMP_SHARED_OBJECT_DATATYPE_DELETE_DATA = 0x09; +const uint8_t RTMP_SHARED_OBJECT_DATATYPE_DELETE_ATTRIBUTE = 0x0A; +const uint8_t RTMP_SHARED_OBJECT_DATATYPE_INITIAL_DATA = 0x0B; + +/* pings */ +const uint16_t RTMP_PING_CLEAR_STREAM = 0x0000; +const uint16_t RTMP_PING_CLEAR_PLAYING_BUFFER = 0x0001; +const uint16_t RTMP_PING_BUFFER_TIME_CLIENT = 0x0003; +const uint16_t RTMP_PING_RESET_STREAM = 0x0004; +const uint16_t RTMP_PING_CLIENT_FROM_SERVER = 0x0006; +const uint16_t RTMP_PING_PONG_FROM_CLIENT = 0x0007; + +/* pings sizes */ +const uint8_t RTMP_PING_SIZE_CLEAR_STREAM = 6; +const uint8_t RTMP_PING_SIZE_CLEAR_PLAYING_BUFFER = 6; +const uint8_t RTMP_PING_SIZE_BUFFER_TIME_CLIENT = 10; +const uint8_t RTMP_PING_SIZE_RESET_STREAM = 6; +/*const uint8_t RTMP_PING_SIZE_CLIENT_FROM_SERVER = 0x0006; TODO +const uint8_t RTMP_PING_SIZE_PONG_FROM_CLIENT = 0x0007; +*/ + +/* default values */ +const uint8_t RTMP_DEFAULT_STREAM_INDEX_CONTROL = 0x02; +const uint8_t RTMP_DEFAULT_STREAM_INDEX_INVOKE = 0x03; +const uint8_t RTMP_DEFAULT_STREAM_INDEX_NOTIFY = 0x04; +const uint8_t RTMP_DEFAULT_STREAM_INDEX_VIDEO_DATA = 0x05; +const uint8_t RTMP_DEFAULT_STREAM_INDEX_AUDIO_DATA = 0x06; +const uint32_t RTMP_DEFAULT_CHUNK_SIZE = 128; +const double RTMP_DEFAULT_STREAM_CLIENT_ID = 1.0; +const double RTMP_DEFAULT_STREAM_SERVER_ID = 1.0; + +/* misc */ +const uint16_t MAX_EMPTY_BLOCKS = 200; /* empty blocks in fifo for media*/ +const uint16_t RTMP_BODY_SIZE_ALLOC = 1024; +const uint32_t RTMP_TIME_CLIENT_BUFFER = 2000; /* miliseconds */ +const uint32_t RTMP_SERVER_BW = 0x00000200; +const uint32_t RTMP_SRC_DST_CONNECT_OBJECT = 0x00000000; +const uint32_t RTMP_SRC_DST_CONNECT_OBJECT2 = 0x00000001; +const uint32_t RTMP_SRC_DST_DEFAULT = 0x01000000; +const uint64_t RTMP_AUDIOCODECS = 0x4083380000000000; +const uint64_t RTMP_VIDEOCODECS = 0x405f000000000000; +const uint64_t RTMP_VIDEOFUNCTION = 0x3ff0000000000000; +/***************************************************************************** + * AMF header: + ******************************************************************************/ + +/* boolean constants */ +const uint8_t AMF_BOOLEAN_FALSE = 0x00; +const uint8_t AMF_BOOLEAN_TRUE = 0x01; + +/* datatypes */ +const uint8_t AMF_DATATYPE_NUMBER = 0x00; +const uint8_t AMF_DATATYPE_BOOLEAN = 0x01; +const uint8_t AMF_DATATYPE_STRING = 0x02; +const uint8_t AMF_DATATYPE_OBJECT = 0x03; +const uint8_t AMF_DATATYPE_MOVIE_CLIP = 0x04; +const uint8_t AMF_DATATYPE_NULL = 0x05; +const uint8_t AMF_DATATYPE_UNDEFINED = 0x06; +const uint8_t AMF_DATATYPE_REFERENCE = 0x07; +const uint8_t AMF_DATATYPE_MIXED_ARRAY = 0x08; +const uint8_t AMF_DATATYPE_END_OF_OBJECT = 0x09; +const uint8_t AMF_DATATYPE_ARRAY = 0x0A; +const uint8_t AMF_DATATYPE_DATE = 0x0B; +const uint8_t AMF_DATATYPE_LONG_STRING = 0x0C; +const uint8_t AMF_DATATYPE_UNSUPPORTED = 0x0D; +const uint8_t AMF_DATATYPE_RECORDSET = 0x0E; +const uint8_t AMF_DATATYPE_XML = 0x0F; +const uint8_t AMF_DATATYPE_TYPED_OBJECT = 0x10; +const uint8_t AMF_DATATYPE_AMF3_DATA = 0x11; + +/* datatypes sizes */ +const uint8_t AMF_DATATYPE_SIZE_NUMBER = 9; +const uint8_t AMF_DATATYPE_SIZE_BOOLEAN = 2; +const uint8_t AMF_DATATYPE_SIZE_STRING = 3; +const uint8_t AMF_DATATYPE_SIZE_OBJECT = 1; +const uint8_t AMF_DATATYPE_SIZE_NULL = 1; +const uint8_t AMF_DATATYPE_SIZE_OBJECT_VARIABLE = 2; +const uint8_t AMF_DATATYPE_SIZE_MIXED_ARRAY = 5; +const uint8_t AMF_DATATYPE_SIZE_END_OF_OBJECT = 3; + +/* amf remote calls */ +const uint64_t AMF_CALL_NETCONNECTION_CONNECT = 0x3FF0000000000000; +const uint64_t AMF_CALL_NETCONNECTION_CONNECT_AUDIOCODECS = 0x4083380000000000; +const uint64_t AMF_CALL_NETCONNECTION_CONNECT_VIDEOCODECS = 0x405F000000000000; +const uint64_t AMF_CALL_NETCONNECTION_CONNECT_VIDEOFUNCTION = 0x3FF0000000000000; +const uint64_t AMF_CALL_NETCONNECTION_CONNECT_OBJECTENCODING = 0x0; +const double AMF_CALL_STREAM_CLIENT_NUMBER = 3.0; +const double AMF_CALL_ONBWDONE = 2.0; +const uint64_t AMF_CALL_NETSTREAM_PLAY = 0x0; + +/***************************************************************************** + * FLV header: + ******************************************************************************/ +const uint8_t FLV_HEADER_SIGNATURE[3] = { 0x46, 0x4C, 0x56 }; /* always "FLV" */ +const uint8_t FLV_HEADER_VERSION = 0x01; +const uint8_t FLV_HEADER_AUDIO = 0x04; +const uint8_t FLV_HEADER_VIDEO = 0x01; +const uint32_t FLV_HEADER_SIZE = 0x00000009; /* always 9 for known FLV files */ + +const uint32_t FLV_TAG_FIRST_PREVIOUS_TAG_SIZE = 0x00000000; +const uint8_t FLV_TAG_PREVIOUS_TAG_SIZE = 4; +const uint8_t FLV_TAG_SIZE = 11; + +/* audio stereo types */ +const uint8_t FLV_AUDIO_STEREO_MASK = 0x01; +const uint8_t FLV_AUDIO_STEREO_MONO = 0x00; +const uint8_t FLV_AUDIO_STEREO_STEREO = 0x01; + +/* audio size */ +const uint8_t FLV_AUDIO_SIZE_MASK = 0x02; +const uint8_t FLV_AUDIO_SIZE_8_BIT = 0x00; +const uint8_t FLV_AUDIO_SIZE_16_BIT = 0x02; + +/* audio rate */ +const uint8_t FLV_AUDIO_RATE_MASK = 0x0C; +const uint8_t FLV_AUDIO_RATE_5_5_KHZ = 0x00; +const uint8_t FLV_AUDIO_RATE_11_KHZ = 0x04; +const uint8_t FLV_AUDIO_RATE_22_KHZ = 0x08; +const uint8_t FLV_AUDIO_RATE_44_KHZ = 0x0C; + +/* audio codec types */ +const uint8_t FLV_AUDIO_CODEC_ID_MASK = 0xF0; +const uint8_t FLV_AUDIO_CODEC_ID_UNCOMPRESSED = 0x00; +const uint8_t FLV_AUDIO_CODEC_ID_ADPCM = 0x10; +const uint8_t FLV_AUDIO_CODEC_ID_MP3 = 0x20; +const uint8_t FLV_AUDIO_CODEC_ID_NELLYMOSER_8KHZ_MONO = 0x50; +const uint8_t FLV_AUDIO_CODEC_ID_NELLYMOSER = 0x60; + +/* video codec types */ +const uint8_t FLV_VIDEO_CODEC_ID_MASK = 0x0F; +const uint8_t FLV_VIDEO_CODEC_ID_SORENSEN_H263 = 0x02; +const uint8_t FLV_VIDEO_CODEC_ID_SCREEN_VIDEO = 0x03; +const uint8_t FLV_VIDEO_CODEC_ID_ON2_VP6 = 0x04; +const uint8_t FLV_VIDEO_CODEC_ID_ON2_VP6_ALPHA = 0x05; +const uint8_t FLV_VIDEO_CODEC_ID_SCREEN_VIDEO_2 = 0x06; + +/* video frame types */ +const uint8_t FLV_VIDEO_FRAME_TYPE_MASK = 0xF0; +const uint8_t FLV_VIDEO_FRAME_TYPE_KEYFRAME = 0x10; +const uint8_t FLV_VIDEO_FRAME_TYPE_INTER_FRAME = 0x20; +const uint8_t FLV_VIDEO_FRAME_TYPE_DISPOSABLE_INTER_FRAME = 0x30; + +/***************************************************************************** + * static RTMP functions: + ******************************************************************************/ +static void rtmp_handler_null ( rtmp_control_thread_t *p_thread, rtmp_packet_t *rtmp_packet ); +static void rtmp_handler_chunk_size ( rtmp_control_thread_t *p_thread, rtmp_packet_t *rtmp_packet ); +static void rtmp_handler_invoke ( rtmp_control_thread_t *p_thread, rtmp_packet_t *rtmp_packet ); +static void rtmp_handler_audio_data ( rtmp_control_thread_t *p_thread, rtmp_packet_t *rtmp_packet ); +static void rtmp_handler_video_data ( rtmp_control_thread_t *p_thread, rtmp_packet_t *rtmp_packet ); +static void rtmp_handler_notify ( rtmp_control_thread_t *p_thread, rtmp_packet_t *rtmp_packet ); + +static rtmp_packet_t *rtmp_new_packet( rtmp_control_thread_t *p_thread, uint8_t stream_index, uint32_t timestamp, uint8_t content_type, uint32_t src_dst, rtmp_body_t *body ); +static block_t *rtmp_new_block( rtmp_control_thread_t *p_thread, uint8_t *buffer, int32_t length_buffer ); + +static rtmp_packet_t *rtmp_encode_onBWDone( rtmp_control_thread_t *p_thread, double number ); +static rtmp_packet_t *rtmp_encode_server_bw( rtmp_control_thread_t *p_thread, uint32_t number ); +static rtmp_packet_t *rtmp_encode_NetConnection_connect_result( rtmp_control_thread_t *p_thread, double number ); +static rtmp_packet_t *rtmp_encode_createStream_result( rtmp_control_thread_t *p_thread, double stream_client, double stream_server ); +static rtmp_packet_t *rtmp_encode_ping_reset_stream( rtmp_control_thread_t *p_thread ); +static rtmp_packet_t *rtmp_encode_ping_clear_stream( rtmp_control_thread_t *p_thread, uint32_t src_dst ); +static rtmp_packet_t *rtmp_encode_NetStream_play_reset_onStatus( rtmp_control_thread_t *p_thread, char *psz_media ); +static rtmp_packet_t *rtmp_encode_NetStream_play_start_onStatus( rtmp_control_thread_t *p_thread, char *psz_media ); +static uint8_t rtmp_encode_header_size( vlc_object_t *p_this, uint8_t header_size ); +static uint8_t rtmp_decode_header_size( vlc_object_t *p_this, uint8_t header_size ); +static uint8_t rtmp_get_stream_index( uint8_t content_type ); + +static void rtmp_body_append( rtmp_body_t *rtmp_body, uint8_t *buffer, uint32_t length ); + +static uint8_t *rtmp_encode_ping( uint16_t type, uint32_t src_dst, uint32_t third_arg, uint32_t fourth_arg ); + +/***************************************************************************** + * static AMF functions: + ******************************************************************************/ +static uint8_t *amf_encode_element( uint8_t element, const void *value ); +static uint8_t *amf_encode_object_variable( const char *key, uint8_t element, const void *value ); +static double amf_decode_number( uint8_t **buffer ); +static int amf_decode_boolean( uint8_t **buffer ); +static char *amf_decode_string( uint8_t **buffer ); +static char *amf_decode_object( uint8_t **buffer ); + +/***************************************************************************** + * static FLV functions: + ******************************************************************************/ +static void flv_rebuild( rtmp_control_thread_t *p_thread, rtmp_packet_t *rtmp_packet ); +static void flv_get_metadata_audio( rtmp_control_thread_t *p_thread, rtmp_packet_t *packet_audio, uint8_t *stereo, uint8_t *audiosamplesize, uint32_t *audiosamplerate, uint8_t *audiocodecid ); +static void flv_get_metadata_video( rtmp_control_thread_t *p_thread, rtmp_packet_t *packet_video, uint8_t *videocodecid, uint8_t *frametype ); +static rtmp_packet_t *flv_build_onMetaData( access_t *p_access, uint64_t duration, uint8_t stereo, uint8_t audiosamplesize, uint32_t audiosamplerate, uint8_t audiocodecid, uint8_t videocodecid ); + +/***************************************************************************** + * RTMP implementation: + ******************************************************************************/ +int +rtmp_handshake_passive( vlc_object_t *p_this, int fd ) +{ + uint8_t p_read[RTMP_HANDSHAKE_BODY_SIZE + 1]; + uint8_t p_write[RTMP_HANDSHAKE_BODY_SIZE * 2 + 1]; + ssize_t i_ret; + int i; + + /* Receive handshake */ + i_ret = net_Read( p_this, fd, NULL, p_read, RTMP_HANDSHAKE_BODY_SIZE + 1, true ); + if( i_ret != RTMP_HANDSHAKE_BODY_SIZE + 1 ) + { + msg_Err( p_this, "failed to receive handshake" ); + return -1; + } + + /* Check handshake */ + if ( p_read[0] != RTMP_HANDSHAKE ) + { + msg_Err( p_this, "first byte in handshake corrupt" ); + return -1; + } + + /* Answer handshake */ + p_write[0] = RTMP_HANDSHAKE; + memset( p_write + 1, 0, RTMP_HANDSHAKE_BODY_SIZE ); + memcpy( p_write + 1 + RTMP_HANDSHAKE_BODY_SIZE, p_read + 1, RTMP_HANDSHAKE_BODY_SIZE ); + + /* Send handshake*/ + i_ret = net_Write( p_this, fd, NULL, p_write, RTMP_HANDSHAKE_BODY_SIZE * 2 + 1 ); + if( i_ret != RTMP_HANDSHAKE_BODY_SIZE * 2 + 1 ) + { + msg_Err( p_this, "failed to send handshake" ); + return -1; + } + + /* Receive acknowledge */ + i_ret = net_Read( p_this, fd, NULL, p_read, RTMP_HANDSHAKE_BODY_SIZE, true ); + if( i_ret != RTMP_HANDSHAKE_BODY_SIZE ) + { + msg_Err( p_this, "failed to receive acknowledge" ); + return -1; + } + + /* Check acknowledge */ + for(i = 8; i < RTMP_HANDSHAKE_BODY_SIZE; i++ ) + if( p_write[i + 1] != p_read[i] ) + { + msg_Err( p_this, "body acknowledge received corrupt" ); + return -1; + } + + return 0; +} + +int +rtmp_handshake_active( vlc_object_t *p_this, int fd ) +{ + uint8_t p_read[RTMP_HANDSHAKE_BODY_SIZE * 2 + 1]; + uint8_t p_write[RTMP_HANDSHAKE_BODY_SIZE + 1]; + ssize_t i_ret; + int i; + + p_write[0] = RTMP_HANDSHAKE; + for( i = 0; i < RTMP_HANDSHAKE_BODY_SIZE; i++ ) + p_write[i + 1] = i & 0xFF; + + /* Send handshake*/ + i_ret = net_Write( p_this, fd, NULL, p_write, RTMP_HANDSHAKE_BODY_SIZE + 1 ); + if( i_ret != RTMP_HANDSHAKE_BODY_SIZE + 1 ) + { + msg_Err( p_this, "failed to send handshake" ); + return -1; + } + + /* Receive handshake */ + i_ret = net_Read( p_this, fd, NULL, p_read, RTMP_HANDSHAKE_BODY_SIZE * 2 + 1, true ); + if( i_ret != RTMP_HANDSHAKE_BODY_SIZE * 2 + 1 ) + { + msg_Err( p_this, "failed to receive handshake" ); + return -1; + } + + /* Check handshake */ + if( p_read[0] != RTMP_HANDSHAKE ) + { + msg_Err( p_this, "first byte in handshake received corrupt" ); + return -1; + } + + for(i = 8; i < RTMP_HANDSHAKE_BODY_SIZE; i++ ) + if( p_write[i + 1] != p_read[i + 1 + RTMP_HANDSHAKE_BODY_SIZE] ) + { + msg_Err( p_this, "body handshake received corrupt" ); + return -1; + } + + /* Acknowledge handshake */ + i_ret = net_Write( p_this, fd, NULL, p_read + 1, RTMP_HANDSHAKE_BODY_SIZE ); + if( i_ret != RTMP_HANDSHAKE_BODY_SIZE ) + { + msg_Err( p_this, "failed to acknowledge handshake" ); + return -1; + } + + return 0; +} + +int +rtmp_connect_active( rtmp_control_thread_t *p_thread ) +{ + rtmp_packet_t *rtmp_packet; + rtmp_body_t *rtmp_body; + uint8_t *tmp_buffer; + char *tmp_url; + ssize_t i_ret; + + /* Build NetConnection.connect call */ + rtmp_body = rtmp_body_new( -1 ); + + tmp_buffer = amf_encode_element( AMF_DATATYPE_STRING, "connect" ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_STRING + strlen( "connect" ) ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_element( AMF_DATATYPE_NUMBER, + &AMF_CALL_NETCONNECTION_CONNECT ); + rtmp_body_append( rtmp_body, tmp_buffer, AMF_DATATYPE_SIZE_NUMBER ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_element( AMF_DATATYPE_OBJECT, NULL ); + rtmp_body_append( rtmp_body, tmp_buffer, AMF_DATATYPE_SIZE_OBJECT ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_object_variable( "app", + AMF_DATATYPE_STRING, p_thread->psz_application ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_OBJECT_VARIABLE + strlen( "app" ) + + AMF_DATATYPE_SIZE_STRING + strlen( p_thread->psz_application ) ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_object_variable( "flashVer", + AMF_DATATYPE_STRING, "LNX 9,0,48,0" ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_OBJECT_VARIABLE + strlen( "flashVer" ) + + AMF_DATATYPE_SIZE_STRING + strlen( "LNX 9,0,48,0" ) ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_object_variable( "swfUrl", + AMF_DATATYPE_STRING, "file:///mac.flv" ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_OBJECT_VARIABLE + strlen( "swfUrl" ) + + AMF_DATATYPE_SIZE_STRING + strlen( "file:///mac.flv" ) ); + free( tmp_buffer ); + + tmp_url = (char *) malloc( strlen( "rtmp://") + strlen( p_thread->url.psz_buffer ) + 1 ); + if( !tmp_url ) + { + free( rtmp_body->body ); + free( rtmp_body ); + return -1; + } + sprintf( tmp_url, "rtmp://%s", p_thread->url.psz_buffer ); + tmp_buffer = amf_encode_object_variable( "tcUrl", + AMF_DATATYPE_STRING, tmp_url ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_OBJECT_VARIABLE + strlen( "tcUrl" ) + + AMF_DATATYPE_SIZE_STRING + strlen( tmp_url ) ); + free( tmp_url ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_object_variable( "fpad", + AMF_DATATYPE_BOOLEAN, &AMF_BOOLEAN_FALSE ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_OBJECT_VARIABLE + strlen( "fpad" ) + + AMF_DATATYPE_SIZE_BOOLEAN ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_object_variable( "audioCodecs", + AMF_DATATYPE_NUMBER, &AMF_CALL_NETCONNECTION_CONNECT_AUDIOCODECS ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_OBJECT_VARIABLE + strlen( "audioCodecs" ) + + AMF_DATATYPE_SIZE_NUMBER ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_object_variable( "videoCodecs", + AMF_DATATYPE_NUMBER, &AMF_CALL_NETCONNECTION_CONNECT_VIDEOCODECS ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_OBJECT_VARIABLE + strlen( "videoCodecs" ) + + AMF_DATATYPE_SIZE_NUMBER ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_object_variable( "videoFunction", + AMF_DATATYPE_NUMBER, &AMF_CALL_NETCONNECTION_CONNECT_VIDEOFUNCTION ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_OBJECT_VARIABLE + strlen( "videoFunction" ) + + AMF_DATATYPE_SIZE_NUMBER ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_object_variable( "pageUrl", + AMF_DATATYPE_STRING, "file:///mac.html" ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_OBJECT_VARIABLE + strlen( "pageUrl" ) + + AMF_DATATYPE_SIZE_STRING + strlen( "file:///mac.html" ) ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_object_variable( "objectEncoding", + AMF_DATATYPE_NUMBER, &AMF_CALL_NETCONNECTION_CONNECT_OBJECTENCODING ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_OBJECT_VARIABLE + strlen( "objectEncoding" ) + + AMF_DATATYPE_SIZE_NUMBER ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_element ( AMF_DATATYPE_END_OF_OBJECT, NULL ); + rtmp_body_append( rtmp_body, tmp_buffer, AMF_DATATYPE_SIZE_END_OF_OBJECT ); + free( tmp_buffer ); + + rtmp_packet = rtmp_new_packet( p_thread, RTMP_DEFAULT_STREAM_INDEX_INVOKE, + 0, RTMP_CONTENT_TYPE_INVOKE, 0, rtmp_body ); + free( rtmp_body->body ); + free( rtmp_body ); + + tmp_buffer = rtmp_encode_packet( p_thread, rtmp_packet ); + + /* Call NetConnection.connect */ + i_ret = net_Write( p_thread, p_thread->fd, NULL, tmp_buffer, rtmp_packet->length_encoded ); + if( i_ret != rtmp_packet->length_encoded ) + { + free( rtmp_packet->body->body ); + free( rtmp_packet->body ); + free( rtmp_packet ); + free( tmp_buffer ); + msg_Err( p_thread, "failed send call NetConnection.connect" ); + return -1; + } + free( rtmp_packet->body->body ); + free( rtmp_packet->body ); + free( rtmp_packet ); + free( tmp_buffer ); + + /* Wait for NetConnection.connect result */ + vlc_mutex_lock( &p_thread->lock ); + vlc_cond_wait( &p_thread->wait, &p_thread->lock ); + vlc_mutex_unlock( &p_thread->lock ); + + if( p_thread->result_connect ) + { + msg_Err( p_thread, "failed call NetConnection.connect" ); + return -1; + } + + /* Force control thread to stop if receive NetStream.play call and wait is not ready */ + vlc_mutex_lock( &p_thread->lock ); + + /* Build NetStream.createStream call */ + rtmp_body = rtmp_body_new( -1 ); + + tmp_buffer = amf_encode_element( AMF_DATATYPE_STRING, "createStream" ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_STRING + strlen( "createStream" ) ); + free( tmp_buffer ); + + p_thread->stream_client_id = RTMP_DEFAULT_STREAM_CLIENT_ID; + + tmp_buffer = amf_encode_element( AMF_DATATYPE_NUMBER, + &AMF_CALL_STREAM_CLIENT_NUMBER ); + rtmp_body_append( rtmp_body, tmp_buffer, AMF_DATATYPE_SIZE_NUMBER ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_element( AMF_DATATYPE_NULL, NULL ); + rtmp_body_append( rtmp_body, tmp_buffer, AMF_DATATYPE_SIZE_NULL ); + free( tmp_buffer ); + + rtmp_packet = rtmp_new_packet( p_thread, RTMP_DEFAULT_STREAM_INDEX_INVOKE, + 0, RTMP_CONTENT_TYPE_INVOKE, 0, rtmp_body ); + free( rtmp_body->body ); + free( rtmp_body ); + + tmp_buffer = rtmp_encode_packet( p_thread, rtmp_packet ); + + /* Call NetStream.createStream */ + i_ret = net_Write( p_thread, p_thread->fd, NULL, tmp_buffer, rtmp_packet->length_encoded ); + if( i_ret != rtmp_packet->length_encoded ) + { + free( rtmp_packet->body->body ); + free( rtmp_packet->body ); + free( rtmp_packet ); + free( tmp_buffer ); + msg_Err( p_thread, "failed send call NetStream.createStream" ); + return -1; + } + free( rtmp_packet->body->body ); + free( rtmp_packet->body ); + free( rtmp_packet ); + free( tmp_buffer ); +/*TODO: read server stream number*/ + /* Build ping packet */ + rtmp_body = rtmp_body_new( -1 ); + + tmp_buffer = rtmp_encode_ping( RTMP_PING_BUFFER_TIME_CLIENT, RTMP_SRC_DST_CONNECT_OBJECT, RTMP_TIME_CLIENT_BUFFER, 0 ); + rtmp_body_append( rtmp_body, tmp_buffer, RTMP_PING_SIZE_BUFFER_TIME_CLIENT ); + free( tmp_buffer ); + + rtmp_packet = rtmp_new_packet( p_thread, RTMP_DEFAULT_STREAM_INDEX_CONTROL, + 0, RTMP_CONTENT_TYPE_PING, 0, rtmp_body ); + free( rtmp_body->body ); + free( rtmp_body ); + + tmp_buffer = rtmp_encode_packet( p_thread, rtmp_packet ); + + /* Send ping packet */ + i_ret = net_Write( p_thread, p_thread->fd, NULL, tmp_buffer, rtmp_packet->length_encoded ); + if( i_ret != rtmp_packet->length_encoded ) + { + free( rtmp_packet->body->body ); + free( rtmp_packet->body ); + free( rtmp_packet ); + free( tmp_buffer ); + msg_Err( p_thread, "failed send ping BUFFER_TIME_CLIENT" ); + return -1; + } + free( rtmp_packet->body->body ); + free( rtmp_packet->body ); + free( rtmp_packet ); + free( tmp_buffer ); + + /* Build NetStream.play call */ + rtmp_body = rtmp_body_new( -1 ); + + tmp_buffer = amf_encode_element( AMF_DATATYPE_STRING, "play" ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_STRING + strlen( "play" ) ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_element( AMF_DATATYPE_NUMBER, + &AMF_CALL_NETSTREAM_PLAY ); + rtmp_body_append( rtmp_body, tmp_buffer, AMF_DATATYPE_SIZE_NUMBER ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_element( AMF_DATATYPE_NULL, NULL ); + rtmp_body_append( rtmp_body, tmp_buffer, AMF_DATATYPE_SIZE_NULL ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_element( AMF_DATATYPE_STRING, p_thread->psz_media ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_STRING + strlen( p_thread->psz_media ) ); + free( tmp_buffer ); + + rtmp_packet = rtmp_new_packet( p_thread, RTMP_DEFAULT_STREAM_INDEX_INVOKE, + 0, RTMP_CONTENT_TYPE_INVOKE, RTMP_SRC_DST_DEFAULT, rtmp_body ); + free( rtmp_body->body ); + free( rtmp_body ); + + tmp_buffer = rtmp_encode_packet( p_thread, rtmp_packet ); + + /* Call NetStream.play */ + i_ret = net_Write( p_thread, p_thread->fd, NULL, tmp_buffer, rtmp_packet->length_encoded ); + if( i_ret != rtmp_packet->length_encoded ) + { + free( rtmp_packet->body->body ); + free( rtmp_packet->body ); + free( rtmp_packet ); + free( tmp_buffer ); + msg_Err( p_thread, "failed send call NetStream.play" ); + return -1; + } + free( rtmp_packet->body->body ); + free( rtmp_packet->body ); + free( rtmp_packet ); + free( tmp_buffer ); + + /* Build ping packet */ + rtmp_body = rtmp_body_new( -1 ); + + tmp_buffer = rtmp_encode_ping( RTMP_PING_BUFFER_TIME_CLIENT, RTMP_SRC_DST_CONNECT_OBJECT2, RTMP_TIME_CLIENT_BUFFER, 0 ); + rtmp_body_append( rtmp_body, tmp_buffer, RTMP_PING_SIZE_BUFFER_TIME_CLIENT ); + free( tmp_buffer ); + + rtmp_packet = rtmp_new_packet( p_thread, RTMP_DEFAULT_STREAM_INDEX_CONTROL, + 0, RTMP_CONTENT_TYPE_PING, 0, rtmp_body ); + free( rtmp_body->body ); + free( rtmp_body ); + + tmp_buffer = rtmp_encode_packet( p_thread, rtmp_packet ); + + /* Send ping packet */ + i_ret = net_Write( p_thread, p_thread->fd, NULL, tmp_buffer, rtmp_packet->length_encoded ); + if( i_ret != rtmp_packet->length_encoded ) + { + free( rtmp_packet->body->body ); + free( rtmp_packet->body ); + free( rtmp_packet ); + free( tmp_buffer ); + msg_Err( p_thread, "failed send ping BUFFER_TIME_CLIENT" ); + return -1; + } + free( rtmp_packet->body->body ); + free( rtmp_packet->body ); + free( rtmp_packet ); + free( tmp_buffer ); + + /* Wait for NetStream.play.start result */ + vlc_cond_wait( &p_thread->wait, &p_thread->lock ); + vlc_mutex_unlock( &p_thread->lock ); + + if( p_thread->result_play ) + { + msg_Err( p_thread, "failed call NetStream.play" ); + return -1; + } + + /* Next packet is the beginning of flv stream */ + msg_Dbg( p_thread, "next packet is the beginning of flv stream" ); + + return 0; +} + +int +rtmp_connect_passive( rtmp_control_thread_t *p_thread ) +{ + /* Force control thread to stop if receive NetStream.play call and wait is not ready */ + vlc_mutex_lock( &p_thread->lock ); + + /* Wait for NetStream.play.start result */ + vlc_cond_wait( &p_thread->wait, &p_thread->lock ); + vlc_mutex_unlock( &p_thread->lock ); + + if( p_thread->result_play ) + { + msg_Err( p_thread, "failed call NetStream.play" ); + return -1; + } + + return 0; +} + +/* TODO +int +rtmp_seek( access_t *p_access, int64_t i_pos ) +{ + access_sys_t *p_sys = p_access->p_sys; + rtmp_packet_t *rtmp_packet; + rtmp_body_t *rtmp_body; + uint8_t *tmp_buffer; + uint64_t tmp_number; + ssize_t i_ret; +msg_Warn(p_access, "i_pos %lld", i_pos); + // Build NetStream.seek call // + rtmp_body = rtmp_body_new(); + + tmp_buffer = amf_encode_element( AMF_DATATYPE_STRING, "seek" ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_STRING + strlen( "seek" ) ); + free( tmp_buffer ); + + tmp_number = 0; + tmp_buffer = amf_encode_element( AMF_DATATYPE_NUMBER, + &tmp_number ); + rtmp_body_append( rtmp_body, tmp_buffer, AMF_DATATYPE_SIZE_NUMBER ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_element( AMF_DATATYPE_NULL, NULL ); + rtmp_body_append( rtmp_body, tmp_buffer, AMF_DATATYPE_SIZE_NULL ); + free( tmp_buffer ); +//TODO: convert i_pos to double and see if they are milliseconds + tmp_buffer = amf_encode_element( AMF_DATATYPE_NUMBER, + &i_pos ); + rtmp_body_append( rtmp_body, tmp_buffer, AMF_DATATYPE_SIZE_NUMBER ); + free( tmp_buffer ); + + rtmp_packet = rtmp_new_packet( p_sys->p_thread, RTMP_DEFAULT_STREAM_INDEX_INVOKE, + 0, RTMP_DATATYPE_INVOKE, RTMP_SRC_DST_DEFAULT, rtmp_body ); + free( rtmp_body->body ); + free( rtmp_body ); + + tmp_buffer = rtmp_encode_packet( p_access, rtmp_packet ); + + // Call NetStream.seek // + i_ret = net_Write( p_access, p_sys->fd, NULL, tmp_buffer, rtmp_packet->length_encoded ); + if( i_ret < rtmp_packet->length_encoded ) + { + free( rtmp_packet->body->body ); + free( rtmp_packet->body ); + free( rtmp_packet ); + free( tmp_buffer ); + msg_Err( p_access, "failed call NetStream.seek" ); + return -1; + } + free( rtmp_packet->body->body ); + free( rtmp_packet->body ); + free( rtmp_packet ); + free( tmp_buffer ); + + // Receive TODO: see what // + rtmp_packet = rtmp_read_net_packet( p_access, p_sys->fd ); + free( rtmp_packet->body->body ); + free( rtmp_packet->body ); + free( rtmp_packet ); + + return 0; +} +*/ + +rtmp_packet_t * +rtmp_build_bytes_read( rtmp_control_thread_t *p_thread, uint32_t reply ) +{ + rtmp_packet_t *rtmp_packet; + rtmp_body_t *rtmp_body; + uint8_t *tmp_buffer; + + /* Build bytes read packet */ + rtmp_body = rtmp_body_new( -1 ); + + tmp_buffer = (uint8_t *) malloc( sizeof( uint32_t ) * sizeof( uint8_t ) ); + if( !tmp_buffer ) return NULL; + + reply = hton32( reply ); + memcpy( tmp_buffer, &reply, sizeof( uint32_t ) ); + + rtmp_body_append( rtmp_body, tmp_buffer, sizeof( uint32_t ) ); + free( tmp_buffer ); + + rtmp_packet = rtmp_new_packet( p_thread, RTMP_DEFAULT_STREAM_INDEX_CONTROL, + 0, RTMP_CONTENT_TYPE_BYTES_READ, 0, rtmp_body ); + free( rtmp_body->body ); + free( rtmp_body ); + + return rtmp_packet; +} + +rtmp_packet_t * +rtmp_build_publish_start( rtmp_control_thread_t *p_thread ) +{ + rtmp_packet_t *rtmp_packet; + rtmp_body_t *rtmp_body; + uint8_t *tmp_buffer; + + /* Build publish start event */ + rtmp_body = rtmp_body_new( -1 ); + + tmp_buffer = amf_encode_element( AMF_DATATYPE_STRING, "onStatus" ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_STRING + strlen( "onStatus" ) ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_element( AMF_DATATYPE_NUMBER, + &p_thread->stream_server_id ); + rtmp_body_append( rtmp_body, tmp_buffer, AMF_DATATYPE_SIZE_NUMBER ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_element( AMF_DATATYPE_NULL, NULL ); + rtmp_body_append( rtmp_body, tmp_buffer, AMF_DATATYPE_SIZE_NULL ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_element( AMF_DATATYPE_OBJECT, NULL ); + rtmp_body_append( rtmp_body, tmp_buffer, AMF_DATATYPE_SIZE_OBJECT ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_object_variable( "level", + AMF_DATATYPE_STRING, "status" ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_OBJECT_VARIABLE + strlen( "level" ) + + AMF_DATATYPE_SIZE_STRING + strlen( "status" ) ); + free ( tmp_buffer ); + + tmp_buffer = amf_encode_object_variable( "code", + AMF_DATATYPE_STRING, "NetStream.Publish.Start" ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_OBJECT_VARIABLE + strlen( "code" ) + + AMF_DATATYPE_SIZE_STRING + strlen( "NetStream.Publish.Start" ) ); + free ( tmp_buffer ); + + tmp_buffer = amf_encode_object_variable( "description", + AMF_DATATYPE_STRING, "" ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_OBJECT_VARIABLE + strlen( "description" ) + + AMF_DATATYPE_SIZE_STRING + strlen( "" ) ); + free ( tmp_buffer ); + + tmp_buffer = amf_encode_object_variable( "details", + AMF_DATATYPE_STRING, p_thread->psz_publish ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_OBJECT_VARIABLE + strlen( "details" ) + + AMF_DATATYPE_SIZE_STRING + strlen( p_thread->psz_publish ) ); + free ( tmp_buffer ); + + tmp_buffer = amf_encode_object_variable( "clientid", + AMF_DATATYPE_NUMBER, &p_thread->stream_client_id ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_OBJECT_VARIABLE + strlen( "clientid" ) + + AMF_DATATYPE_SIZE_NUMBER ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_element ( AMF_DATATYPE_END_OF_OBJECT, NULL ); + rtmp_body_append( rtmp_body, tmp_buffer, AMF_DATATYPE_SIZE_END_OF_OBJECT ); + free( tmp_buffer ); + + rtmp_packet = rtmp_new_packet( p_thread, RTMP_DEFAULT_STREAM_INDEX_INVOKE, + 0, RTMP_CONTENT_TYPE_INVOKE, 0, rtmp_body ); + free( rtmp_body->body ); + free( rtmp_body ); + + return rtmp_packet; +} + +rtmp_packet_t * +rtmp_build_flv_over_rtmp( rtmp_control_thread_t *p_thread, block_t *p_buffer ) +{ + rtmp_packet_t *rtmp_packet; + + if( p_thread->flv_length_body > 0 ) + { + p_thread->flv_length_body -= p_buffer->i_buffer; + rtmp_body_append( p_thread->flv_body, p_buffer->p_buffer, p_buffer->i_buffer ); + + if( p_thread->flv_length_body > 0 ) + return NULL; + + } + else + { + p_thread->flv_content_type = *p_buffer->p_buffer; + + p_buffer->p_buffer[0] = 0; + p_thread->flv_length_body = ntoh32( *(uint32_t *) (p_buffer->p_buffer) ); + + p_buffer->p_buffer[3] = 0; + p_thread->flv_timestamp = ntoh32( *(uint32_t *) (p_buffer->p_buffer + 3) ); + } + + if( p_thread->flv_length_body > p_buffer->i_buffer - FLV_TAG_SIZE - FLV_TAG_PREVIOUS_TAG_SIZE ) + { + p_thread->flv_length_body -= p_buffer->i_buffer - FLV_TAG_SIZE - FLV_TAG_PREVIOUS_TAG_SIZE; + rtmp_body_append( p_thread->flv_body, p_buffer->p_buffer + FLV_TAG_SIZE, p_buffer->i_buffer - FLV_TAG_SIZE ); + + return NULL; + } + else + { + rtmp_body_append( p_thread->flv_body, p_buffer->p_buffer + FLV_TAG_SIZE, p_thread->flv_length_body ); + } + + rtmp_packet = rtmp_new_packet( p_thread, rtmp_get_stream_index( p_thread->flv_content_type ), + p_thread->flv_timestamp, p_thread->flv_content_type, RTMP_SRC_DST_DEFAULT, p_thread->flv_body ); + p_thread->flv_length_body = 0; + rtmp_body_reset( p_thread->flv_body ); + + return rtmp_packet; +} + +rtmp_packet_t * +rtmp_read_net_packet( rtmp_control_thread_t *p_thread ) +{ + int length_header; + int stream_index; + int bytes_left; + uint8_t p_read[12]; + rtmp_packet_t *rtmp_packet; + ssize_t i_ret; + + + for(;;) + { + i_ret = net_Read( p_thread, p_thread->fd, NULL, p_read, 1, true ); + if( i_ret != 1 ) + goto error; + + length_header = rtmp_decode_header_size( (vlc_object_t *) p_thread, p_read[0] & RTMP_HEADER_SIZE_MASK ); + stream_index = p_read[0] & RTMP_HEADER_STREAM_INDEX_MASK; + + i_ret = net_Read( p_thread, p_thread->fd, NULL, p_read + 1, length_header - 1, true ); + if( i_ret != length_header - 1 ) + goto error; + + /* Update timestamp if not is an interchunk packet */ + if( length_header == 1 && p_thread->rtmp_headers_recv[stream_index].body == NULL ) + { + p_thread->rtmp_headers_recv[stream_index].timestamp += + p_thread->rtmp_headers_recv[stream_index].timestamp_relative; + } + + /* Length 4 and 8 headers have relative timestamp */ + if( length_header == 4 || length_header == 8 ) + { + p_read[0] = 0; + + p_thread->rtmp_headers_recv[stream_index].timestamp_relative = ntoh32( *(uint32_t *) p_read ); + p_thread->rtmp_headers_recv[stream_index].timestamp += + p_thread->rtmp_headers_recv[stream_index].timestamp_relative; + } + + if( length_header >= 8 ) + { + p_read[3] = 0; + + p_thread->rtmp_headers_recv[stream_index].length_body = ntoh32( *(uint32_t *) (p_read + 3) ); + p_thread->rtmp_headers_recv[stream_index].content_type = p_read[7]; + } + + /* Length 12 headers have absolute timestamp */ + if( length_header >= 12 ) + { + p_read[0] = 0; + + p_thread->rtmp_headers_recv[stream_index].timestamp = ntoh32( *(uint32_t *) p_read ); + p_thread->rtmp_headers_recv[stream_index].src_dst = ntoh32( *(uint32_t *) (p_read + 8) ); + } + + if( p_thread->rtmp_headers_recv[stream_index].body == NULL ) + { + p_thread->rtmp_headers_recv[stream_index].body = + rtmp_body_new( p_thread->rtmp_headers_recv[stream_index].length_body ); + } + + bytes_left = p_thread->rtmp_headers_recv[stream_index].body->length_buffer - + p_thread->rtmp_headers_recv[stream_index].body->length_body; + + if( bytes_left > p_thread->chunk_size_recv ) + bytes_left = p_thread->chunk_size_recv; + + i_ret = net_Read( p_thread, p_thread->fd, NULL, + p_thread->rtmp_headers_recv[stream_index].body->body + + p_thread->rtmp_headers_recv[stream_index].body->length_body, + bytes_left, true ); + if( i_ret != bytes_left ) + goto error; + + p_thread->rtmp_headers_recv[stream_index].body->length_body += bytes_left; + + if( p_thread->rtmp_headers_recv[stream_index].length_body == p_thread->rtmp_headers_recv[stream_index].body->length_body ) + { + rtmp_packet = (rtmp_packet_t *) malloc( sizeof( rtmp_packet_t ) ); + if( !rtmp_packet ) goto error; + + rtmp_packet->stream_index = stream_index; + rtmp_packet->timestamp = p_thread->rtmp_headers_recv[stream_index].timestamp; + rtmp_packet->timestamp_relative = p_thread->rtmp_headers_recv[stream_index].timestamp_relative; + rtmp_packet->content_type = p_thread->rtmp_headers_recv[stream_index].content_type; + rtmp_packet->src_dst = p_thread->rtmp_headers_recv[stream_index].src_dst; + rtmp_packet->length_body = p_thread->rtmp_headers_recv[stream_index].length_body; + rtmp_packet->body = p_thread->rtmp_headers_recv[stream_index].body; + + p_thread->rtmp_headers_recv[stream_index].body = NULL; + + return rtmp_packet; + } + } + +error: + msg_Err( p_thread, "rtmp_read_net_packet: net_Read error"); + return NULL; +} + +void +rtmp_init_handler( rtmp_handler_t *rtmp_handler ) +{ + rtmp_handler[RTMP_CONTENT_TYPE_CHUNK_SIZE] = rtmp_handler_chunk_size; + rtmp_handler[RTMP_CONTENT_TYPE_UNKNOWN_02] = rtmp_handler_null; + rtmp_handler[RTMP_CONTENT_TYPE_BYTES_READ] = rtmp_handler_null; + rtmp_handler[RTMP_CONTENT_TYPE_PING] = rtmp_handler_null; + rtmp_handler[RTMP_CONTENT_TYPE_SERVER_BW] = rtmp_handler_null; + rtmp_handler[RTMP_CONTENT_TYPE_CLIENT_BW] = rtmp_handler_null; + rtmp_handler[RTMP_CONTENT_TYPE_UNKNOWN_07] = rtmp_handler_null; + rtmp_handler[RTMP_CONTENT_TYPE_AUDIO_DATA] = rtmp_handler_audio_data; + rtmp_handler[RTMP_CONTENT_TYPE_VIDEO_DATA] = rtmp_handler_video_data; + rtmp_handler[RTMP_CONTENT_TYPE_UNKNOWN_0A_0E] = rtmp_handler_null; + rtmp_handler[RTMP_CONTENT_TYPE_FLEX_STREAM] = rtmp_handler_null; + rtmp_handler[RTMP_CONTENT_TYPE_FLEX_SHARED_OBJECT] = rtmp_handler_null; + rtmp_handler[RTMP_CONTENT_TYPE_MESSAGE] = rtmp_handler_null; + rtmp_handler[RTMP_CONTENT_TYPE_NOTIFY] = rtmp_handler_notify; + rtmp_handler[RTMP_CONTENT_TYPE_SHARED_OBJECT] = rtmp_handler_null; + rtmp_handler[RTMP_CONTENT_TYPE_INVOKE] = rtmp_handler_invoke; +} + +static void +rtmp_handler_null( rtmp_control_thread_t *p_thread, rtmp_packet_t *rtmp_packet ) +{ + VLC_UNUSED(p_thread); + free( rtmp_packet->body->body ); + free( rtmp_packet->body ); + free( rtmp_packet ); +} + +static void +rtmp_handler_chunk_size( rtmp_control_thread_t *p_thread, rtmp_packet_t *rtmp_packet ) +{ + p_thread->chunk_size_recv = ntoh32( *(uint32_t *) (rtmp_packet->body->body) ); + + free( rtmp_packet->body->body ); + free( rtmp_packet->body ); + free( rtmp_packet ); +} + +static void +rtmp_handler_audio_data( rtmp_control_thread_t *p_thread, rtmp_packet_t *rtmp_packet ) +{ + block_t *p_buffer; + + if( !p_thread->has_audio ) + { + p_thread->has_audio = 1; + + flv_get_metadata_audio( p_thread, rtmp_packet, + &p_thread->metadata_stereo, &p_thread->metadata_samplesize, + &p_thread->metadata_samplerate, &p_thread->metadata_audiocodecid ); + } + + flv_rebuild( p_thread, rtmp_packet ); + p_buffer = rtmp_new_block( p_thread, rtmp_packet->body->body, rtmp_packet->body->length_body ); + block_FifoPut( p_thread->p_fifo_input, p_buffer ); + + free( rtmp_packet->body->body ); + free( rtmp_packet->body ); + free( rtmp_packet ); +} + +static void +rtmp_handler_video_data( rtmp_control_thread_t *p_thread, rtmp_packet_t *rtmp_packet ) +{ + block_t *p_buffer; + + if( !p_thread->has_video ) + { + p_thread->has_video = 1; + + flv_get_metadata_video( p_thread, rtmp_packet, + &p_thread->metadata_videocodecid, &p_thread->metadata_frametype ); + } + + flv_rebuild( p_thread, rtmp_packet ); + p_buffer = rtmp_new_block( p_thread, rtmp_packet->body->body, rtmp_packet->body->length_body ); + block_FifoPut( p_thread->p_fifo_input, p_buffer ); + + free( rtmp_packet->body->body ); + free( rtmp_packet->body ); + free( rtmp_packet ); +} + +static void +rtmp_handler_notify( rtmp_control_thread_t *p_thread, rtmp_packet_t *rtmp_packet ) +{ + block_t *p_buffer; + + p_thread->metadata_received = 1; + + flv_rebuild( p_thread, rtmp_packet ); + p_buffer = rtmp_new_block( p_thread, rtmp_packet->body->body, rtmp_packet->body->length_body ); + block_FifoPut( p_thread->p_fifo_input, p_buffer ); + + free( rtmp_packet->body->body ); + free( rtmp_packet->body ); + free( rtmp_packet ); +} + +static void +rtmp_handler_invoke( rtmp_control_thread_t *p_thread, rtmp_packet_t *rtmp_packet ) +{ + rtmp_packet_t *tmp_rtmp_packet; + uint8_t *i, *end, *tmp_buffer; + double number; + char *string, *string2; + ssize_t i_ret; + + i = rtmp_packet->body->body; + end = rtmp_packet->body->body + rtmp_packet->body->length_body; + + i++; /* Pass over AMF_DATATYPE_STRING */ + string = amf_decode_string( &i ); + + i++; /* Pass over AMF_DATATYPE_NUMBER */ + number = amf_decode_number( &i ); + + msg_Dbg( p_thread, "%s %.1f", string, number ); + + if( strcmp( "connect", string ) == 0 ) + { + /* Connection bandwith */ + tmp_rtmp_packet = rtmp_encode_onBWDone( p_thread, AMF_CALL_ONBWDONE ); + + tmp_buffer = rtmp_encode_packet( p_thread, tmp_rtmp_packet ); + + i_ret = net_Write( p_thread, p_thread->fd, NULL, tmp_buffer, tmp_rtmp_packet->length_encoded ); + if( i_ret != tmp_rtmp_packet->length_encoded ) + { + msg_Err( p_thread, "failed send connection bandwith" ); + goto error; + } + free( tmp_rtmp_packet->body->body ); + free( tmp_rtmp_packet->body ); + free( tmp_rtmp_packet ); + free( tmp_buffer ); + + /* Server bandwith */ + tmp_rtmp_packet = rtmp_encode_server_bw( p_thread, RTMP_SERVER_BW ); + + tmp_buffer = rtmp_encode_packet( p_thread, tmp_rtmp_packet ); + + i_ret = net_Write( p_thread, p_thread->fd, NULL, tmp_buffer, tmp_rtmp_packet->length_encoded ); + if( i_ret != tmp_rtmp_packet->length_encoded ) + { + msg_Err( p_thread, "failed send server bandwith" ); + goto error; + } + free( tmp_rtmp_packet->body->body ); + free( tmp_rtmp_packet->body ); + free( tmp_rtmp_packet ); + free( tmp_buffer ); + + /* Clear stream */ + tmp_rtmp_packet = rtmp_encode_ping_clear_stream( p_thread, RTMP_SRC_DST_CONNECT_OBJECT ); + + tmp_buffer = rtmp_encode_packet( p_thread, tmp_rtmp_packet ); + + i_ret = net_Write( p_thread, p_thread->fd, NULL, tmp_buffer, tmp_rtmp_packet->length_encoded ); + if( i_ret != tmp_rtmp_packet->length_encoded ) + { + msg_Err( p_thread, "failed send clear stream" ); + goto error; + } + free( tmp_rtmp_packet->body->body ); + free( tmp_rtmp_packet->body ); + free( tmp_rtmp_packet ); + free( tmp_buffer ); + + /* Reply NetConnection.connect */ + tmp_rtmp_packet = rtmp_encode_NetConnection_connect_result( p_thread, number ); + + tmp_buffer = rtmp_encode_packet( p_thread, tmp_rtmp_packet ); + + i_ret = net_Write( p_thread, p_thread->fd, NULL, tmp_buffer, tmp_rtmp_packet->length_encoded ); + if( i_ret != tmp_rtmp_packet->length_encoded ) + { + msg_Err( p_thread, "failed send reply NetConnection.connect" ); + goto error; + } + free( tmp_rtmp_packet->body->body ); + free( tmp_rtmp_packet->body ); + free( tmp_rtmp_packet ); + free( tmp_buffer ); + } + else if( strcmp( "createStream", string ) == 0 ) + { + p_thread->stream_client_id = number; + p_thread->stream_server_id = RTMP_DEFAULT_STREAM_SERVER_ID; + + /* Reply createStream */ + tmp_rtmp_packet = rtmp_encode_createStream_result( p_thread, p_thread->stream_client_id, p_thread->stream_server_id ); + + tmp_buffer = rtmp_encode_packet( p_thread, tmp_rtmp_packet ); + + i_ret = net_Write( p_thread, p_thread->fd, NULL, tmp_buffer, tmp_rtmp_packet->length_encoded ); + if( i_ret != tmp_rtmp_packet->length_encoded ) + { + msg_Err( p_thread, "failed send reply createStream" ); + goto error; + } + free( tmp_rtmp_packet->body->body ); + free( tmp_rtmp_packet->body ); + free( tmp_rtmp_packet ); + free( tmp_buffer ); + + /* Reset stream */ + tmp_rtmp_packet = rtmp_encode_ping_reset_stream( p_thread ); + + tmp_buffer = rtmp_encode_packet( p_thread, tmp_rtmp_packet ); + + i_ret = net_Write( p_thread, p_thread->fd, NULL, tmp_buffer, tmp_rtmp_packet->length_encoded ); + if( i_ret != tmp_rtmp_packet->length_encoded ) + { + msg_Err( p_thread, "failed send reset stream" ); + goto error; + } + free( tmp_rtmp_packet->body->body ); + free( tmp_rtmp_packet->body ); + free( tmp_rtmp_packet ); + free( tmp_buffer ); + + /* Clear stream */ + tmp_rtmp_packet = rtmp_encode_ping_clear_stream( p_thread, RTMP_SRC_DST_CONNECT_OBJECT2 ); + + tmp_buffer = rtmp_encode_packet( p_thread, tmp_rtmp_packet ); + + i_ret = net_Write( p_thread, p_thread->fd, NULL, tmp_buffer, tmp_rtmp_packet->length_encoded ); + if( i_ret != tmp_rtmp_packet->length_encoded ) + { + msg_Err( p_thread, "failed send clear stream" ); + goto error; + } + free( tmp_rtmp_packet->body->body ); + free( tmp_rtmp_packet->body ); + free( tmp_rtmp_packet ); + free( tmp_buffer ); + } + else if( strcmp( "publish", string ) == 0 ) + { + i++; + msg_Dbg( p_thread, "null" ); + + i++; + string2 = amf_decode_string( &i ); + msg_Dbg( p_thread, "string: %s", string2 ); + + p_thread->psz_publish = strdup( string2 ); + + free( string2 ); + } + else if( strcmp( "play", string ) == 0 ) + { + i++; + msg_Dbg( p_thread, "null" ); + + i++; + string2 = amf_decode_string( &i ); + msg_Dbg( p_thread, "string: %s", string2 ); + + /* Reply NetStream.play.reset */ + tmp_rtmp_packet = rtmp_encode_NetStream_play_reset_onStatus( p_thread, string2 ); + + tmp_buffer = rtmp_encode_packet( p_thread, tmp_rtmp_packet ); + + i_ret = net_Write( p_thread, p_thread->fd, NULL, tmp_buffer, tmp_rtmp_packet->length_encoded ); + if( i_ret != tmp_rtmp_packet->length_encoded ) + { + msg_Err( p_thread, "failed send reply NetStream.play.reset" ); + goto error; + } + free( tmp_rtmp_packet->body->body ); + free( tmp_rtmp_packet->body ); + free( tmp_rtmp_packet ); + free( tmp_buffer ); + + /* Reply NetStream.play.start */ + tmp_rtmp_packet = rtmp_encode_NetStream_play_start_onStatus( p_thread, string2 ); + + tmp_buffer = rtmp_encode_packet( p_thread, tmp_rtmp_packet ); + + i_ret = net_Write( p_thread, p_thread->fd, NULL, tmp_buffer, tmp_rtmp_packet->length_encoded ); + if( i_ret != tmp_rtmp_packet->length_encoded ) + { + msg_Err( p_thread, "failed send reply NetStream.play.start" ); + goto error; + } + free( tmp_rtmp_packet->body->body ); + free( tmp_rtmp_packet->body ); + free( tmp_rtmp_packet ); + free( tmp_buffer ); + + free( string2 ); + + p_thread->result_play = 0; + + vlc_mutex_lock( &p_thread->lock ); + vlc_cond_signal( &p_thread->wait ); + vlc_mutex_unlock( &p_thread->lock ); + } + + free( string ); + + while( i < end ) + { + if( *i == AMF_DATATYPE_NUMBER ) + { + i++; + msg_Dbg( p_thread, "number: %le", amf_decode_number( &i ) ); + } + else if( *i == AMF_DATATYPE_BOOLEAN ) + { + i++; + msg_Dbg( p_thread, "boolean: %s", amf_decode_boolean( &i ) ? "true" : "false" ); + } + else if( *i == AMF_DATATYPE_STRING ) + { + i++; + string = amf_decode_string( &i ); + msg_Dbg( p_thread, "string: %s", string ); + free( string ); + } + else if( *i == AMF_DATATYPE_OBJECT ) + { + i++; + msg_Dbg( p_thread, "object" ); + while( ( string = amf_decode_object( &i ) ) != NULL ) + { + if( *i == AMF_DATATYPE_NUMBER ) + { + i++; + msg_Dbg( p_thread, "key: %s value: %le", string, amf_decode_number( &i ) ); + } + else if( *i == AMF_DATATYPE_BOOLEAN ) + { + i++; + msg_Dbg( p_thread, "key: %s value: %s", string, amf_decode_boolean( &i ) ? "true" : "false" ); + } + else if( *i == AMF_DATATYPE_STRING ) + { + i++; + string2 = amf_decode_string( &i ); + msg_Dbg( p_thread, "key: %s value: %s", string, string2 ); + if( strcmp( "code", string ) == 0 ) + { + if( strcmp( "NetConnection.Connect.Success", string2 ) == 0 ) + { + p_thread->result_connect = 0; + + vlc_mutex_lock( &p_thread->lock ); + vlc_cond_signal( &p_thread->wait ); + vlc_mutex_unlock( &p_thread->lock ); + } + else if( strcmp( "NetConnection.Connect.InvalidApp", string2 ) == 0 ) + { + p_thread->b_die = 1; + + vlc_mutex_lock( &p_thread->lock ); + vlc_cond_signal( &p_thread->wait ); + vlc_mutex_unlock( &p_thread->lock ); + } + else if( strcmp( "NetStream.Play.Start", string2 ) == 0 ) + { + p_thread->result_play = 0; + + vlc_mutex_lock( &p_thread->lock ); + vlc_cond_signal( &p_thread->wait ); + vlc_mutex_unlock( &p_thread->lock ); + } + else if( strcmp( "NetStream.Play.Stop", string2 ) == 0 ) + { + p_thread->result_stop = 1; + + block_FifoWake( p_thread->p_fifo_input ); + } + } + free( string2 ); + } + else if( *i == AMF_DATATYPE_NULL ) + { + i++; + msg_Dbg( p_thread, "key: %s value: Null", string ); + } + else if( *i == AMF_DATATYPE_UNDEFINED ) + { + i++; + msg_Dbg( p_thread, "key: %s value: undefined (Null)", string ); + } + else + { + i++; + msg_Warn( p_thread, "key: %s value: undefined AMF type", string ); + } + free( string ); + } + msg_Dbg( p_thread, "end of object" ); + } + else if( *i == AMF_DATATYPE_NULL) + { + i++; + msg_Dbg( p_thread, "null" ); + } + else if( *i == AMF_DATATYPE_UNDEFINED ) + { + i++; + msg_Dbg( p_thread, "undefined (null)" ); + } + else + { + i++; + msg_Warn( p_thread, "undefined AMF type" ); + } + } + + free( rtmp_packet->body->body ); + free( rtmp_packet->body ); + free( rtmp_packet ); + + return; + +error: + free( tmp_rtmp_packet->body->body ); + free( tmp_rtmp_packet->body ); + free( tmp_rtmp_packet ); + free( tmp_buffer ); +} + +/* length header calculated automatically based on last packet in the same channel */ +/* timestamps passed are always absolute */ +static rtmp_packet_t * +rtmp_new_packet( rtmp_control_thread_t *p_thread, uint8_t stream_index, uint32_t timestamp, uint8_t content_type, uint32_t src_dst, rtmp_body_t *body ) +{ + int interchunk_headers; + rtmp_packet_t *rtmp_packet; + + rtmp_packet = (rtmp_packet_t *) malloc( sizeof( rtmp_packet_t ) ); + if( !rtmp_packet ) return NULL; + + interchunk_headers = body->length_body / p_thread->chunk_size_send; + if( body->length_body % p_thread->chunk_size_send == 0 ) + interchunk_headers--; + + if( src_dst != p_thread->rtmp_headers_send[stream_index].src_dst ) + { + p_thread->rtmp_headers_send[stream_index].timestamp = timestamp; + p_thread->rtmp_headers_send[stream_index].length_body = body->length_body; + p_thread->rtmp_headers_send[stream_index].content_type = content_type; + p_thread->rtmp_headers_send[stream_index].src_dst = src_dst; + + rtmp_packet->length_header = 12; + } + else if( content_type != p_thread->rtmp_headers_send[stream_index].content_type + || body->length_body != p_thread->rtmp_headers_send[stream_index].length_body ) + { + p_thread->rtmp_headers_send[stream_index].timestamp_relative = + timestamp - p_thread->rtmp_headers_send[stream_index].timestamp; + p_thread->rtmp_headers_send[stream_index].timestamp = timestamp; + p_thread->rtmp_headers_send[stream_index].length_body = body->length_body; + p_thread->rtmp_headers_send[stream_index].content_type = content_type; + + rtmp_packet->length_header = 8; + } + else if( timestamp != p_thread->rtmp_headers_send[stream_index].timestamp ) + { + p_thread->rtmp_headers_send[stream_index].timestamp_relative = + timestamp - p_thread->rtmp_headers_send[stream_index].timestamp; + p_thread->rtmp_headers_send[stream_index].timestamp = timestamp; + + rtmp_packet->length_header = 4; + } + else + { + rtmp_packet->length_header = 1; + } +/*TODO: puede que no haga falta guardar el timestamp relative */ + rtmp_packet->stream_index = stream_index; + if( rtmp_packet->length_header == 12 ) + { + rtmp_packet->timestamp = timestamp; + rtmp_packet->timestamp_relative = 0; + } + else + { + rtmp_packet->timestamp = timestamp; + rtmp_packet->timestamp_relative = p_thread->rtmp_headers_send[stream_index].timestamp_relative; + } + rtmp_packet->length_encoded = rtmp_packet->length_header + body->length_body + interchunk_headers; + rtmp_packet->length_body = body->length_body; + rtmp_packet->content_type = content_type; + rtmp_packet->src_dst = src_dst; + + rtmp_packet->body = (rtmp_body_t *) malloc( sizeof( rtmp_body_t ) ); + if( !rtmp_packet->body ) + { + free( rtmp_packet ); + return NULL; + } + + rtmp_packet->body->length_body = body->length_body; + rtmp_packet->body->length_buffer = body->length_body; + rtmp_packet->body->body = (uint8_t *) malloc( rtmp_packet->body->length_buffer * sizeof( uint8_t ) ); + if( !rtmp_packet->body->body ) + { + free( rtmp_packet->body ); + free( rtmp_packet ); + return NULL; + } + memcpy( rtmp_packet->body->body, body->body, rtmp_packet->body->length_body ); + + return rtmp_packet; +} + +static block_t * +rtmp_new_block( rtmp_control_thread_t *p_thread, uint8_t *buffer, int32_t length_buffer ) +{ + block_t *p_buffer; + /* DOWN: p_thread->p_empty_blocks->i_depth */ + while ( block_FifoCount( p_thread->p_empty_blocks ) > MAX_EMPTY_BLOCKS ) + { + p_buffer = block_FifoGet( p_thread->p_empty_blocks ); + block_Release( p_buffer ); + } + /* DOWN: p_thread->p_empty_blocks->i_depth */ + if( block_FifoCount( p_thread->p_empty_blocks ) == 0 ) + { + p_buffer = block_New( p_thread, length_buffer ); + } + else + { + p_buffer = block_FifoGet( p_thread->p_empty_blocks ); + p_buffer = block_Realloc( p_buffer, 0, length_buffer ); + } + + p_buffer->i_buffer = length_buffer; + + memcpy( p_buffer->p_buffer, buffer, p_buffer->i_buffer ); + + return p_buffer; +} + +/* call sequence for each packet rtmp_new_packet -> rtmp_encode_packet -> send */ +/* no parallelism allowed because of optimization in header length */ +uint8_t * +rtmp_encode_packet( rtmp_control_thread_t *p_thread, rtmp_packet_t *rtmp_packet ) +{ + uint8_t *out; + int interchunk_headers; + uint32_t timestamp, length_body, src_dst; + int i, j; + + out = (uint8_t *) malloc( rtmp_packet->length_encoded * sizeof( uint8_t ) ); + if( !out ) return NULL; + + interchunk_headers = rtmp_packet->body->length_body / p_thread->chunk_size_send; + if( rtmp_packet->body->length_body % p_thread->chunk_size_send == 0 ) + interchunk_headers--; + + if( rtmp_packet->length_header == 12 ) + { + /* Timestamp absolute */ + timestamp = hton32( rtmp_packet->timestamp ); + memcpy( out, ×tamp, sizeof( uint32_t ) ); + + src_dst = hton32( rtmp_packet->src_dst ); + memcpy( out + 8, &src_dst, sizeof( uint32_t ) ); + } + + if( rtmp_packet->length_header >= 8 ) + { + /* Length without inter chunk headers */ + length_body = hton32( rtmp_packet->body->length_body ); + memcpy( out + 3, &length_body, sizeof( uint32_t ) ); + + out[7] = rtmp_packet->content_type; + } + if( rtmp_packet->length_header >= 4 && rtmp_packet->length_header != 12 ) + { + /* Timestamp relative */ + timestamp = hton32( rtmp_packet->timestamp_relative ); + memcpy( out, ×tamp, sizeof( uint32_t ) ); + } + + out[0] = rtmp_encode_header_size( (vlc_object_t *) p_thread, rtmp_packet->length_header ) + rtmp_packet->stream_index; + + /* Insert inter chunk headers */ + for(i = 0, j = 0; i < rtmp_packet->body->length_body + interchunk_headers; i++, j++) + { + if( j % p_thread->chunk_size_send == 0 && j != 0 ) + out[rtmp_packet->length_header + i++] = RTMP_HEADER_SIZE_1 + rtmp_packet->stream_index; + out[rtmp_packet->length_header + i] = rtmp_packet->body->body[j]; + } + + return out; +} + +static rtmp_packet_t * +rtmp_encode_onBWDone( rtmp_control_thread_t *p_thread, double number ) +{ + rtmp_packet_t *rtmp_packet; + rtmp_body_t *rtmp_body; + uint8_t *tmp_buffer; + + /* Build onBWDone */ + rtmp_body = rtmp_body_new( -1 ); + + tmp_buffer = amf_encode_element( AMF_DATATYPE_STRING, "onBWDone" ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_STRING + strlen( "onBWDone" ) ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_element( AMF_DATATYPE_NUMBER, &number ); + rtmp_body_append( rtmp_body, tmp_buffer, AMF_DATATYPE_SIZE_NUMBER ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_element( AMF_DATATYPE_NULL, NULL ); + rtmp_body_append( rtmp_body, tmp_buffer, AMF_DATATYPE_SIZE_NULL ); + free( tmp_buffer ); + + rtmp_packet = rtmp_new_packet( p_thread, RTMP_DEFAULT_STREAM_INDEX_INVOKE, + 0, RTMP_CONTENT_TYPE_INVOKE, RTMP_SRC_DST_CONNECT_OBJECT, rtmp_body ); + free( rtmp_body->body ); + free( rtmp_body ); + + return rtmp_packet; +} + +static rtmp_packet_t * +rtmp_encode_server_bw( rtmp_control_thread_t *p_thread, uint32_t number ) +{ + rtmp_packet_t *rtmp_packet; + rtmp_body_t *rtmp_body; + + /* Build server bw */ + rtmp_body = rtmp_body_new( -1 ); + + rtmp_body_append( rtmp_body, (uint8_t *) &number, sizeof( uint32_t ) ); + + rtmp_packet = rtmp_new_packet( p_thread, RTMP_DEFAULT_STREAM_INDEX_CONTROL, + 0, RTMP_CONTENT_TYPE_SERVER_BW, RTMP_SRC_DST_CONNECT_OBJECT, rtmp_body ); + free( rtmp_body->body ); + free( rtmp_body ); + + return rtmp_packet; +} + +static rtmp_packet_t * +rtmp_encode_NetConnection_connect_result( rtmp_control_thread_t *p_thread, double number ) +{ + rtmp_packet_t *rtmp_packet; + rtmp_body_t *rtmp_body; + uint8_t *tmp_buffer; + + /* Build NetConnection.connect result */ + rtmp_body = rtmp_body_new( -1 ); + + tmp_buffer = amf_encode_element( AMF_DATATYPE_STRING, "_result" ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_STRING + strlen( "_result" ) ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_element( AMF_DATATYPE_NUMBER, &number ); + rtmp_body_append( rtmp_body, tmp_buffer, AMF_DATATYPE_SIZE_NUMBER ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_element( AMF_DATATYPE_NULL, NULL ); + rtmp_body_append( rtmp_body, tmp_buffer, AMF_DATATYPE_SIZE_NULL ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_element( AMF_DATATYPE_OBJECT, NULL ); + rtmp_body_append( rtmp_body, tmp_buffer, AMF_DATATYPE_SIZE_OBJECT ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_object_variable( "level", + AMF_DATATYPE_STRING, "status" ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_OBJECT_VARIABLE + strlen( "level" ) + + AMF_DATATYPE_SIZE_STRING + strlen( "status" ) ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_object_variable( "code", + AMF_DATATYPE_STRING, "NetConnection.Connect.Success" ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_OBJECT_VARIABLE + strlen( "code" ) + + AMF_DATATYPE_SIZE_STRING + strlen( "NetConnection.Connect.Success" ) ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_object_variable( "description", + AMF_DATATYPE_STRING, "Connection succeeded." ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_OBJECT_VARIABLE + strlen( "description" ) + + AMF_DATATYPE_SIZE_STRING + strlen( "Connection succeeded." ) ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_element ( AMF_DATATYPE_END_OF_OBJECT, NULL ); + rtmp_body_append( rtmp_body, tmp_buffer, AMF_DATATYPE_SIZE_END_OF_OBJECT ); + free( tmp_buffer ); + + rtmp_packet = rtmp_new_packet( p_thread, RTMP_DEFAULT_STREAM_INDEX_INVOKE, + 0, RTMP_CONTENT_TYPE_INVOKE, 0, rtmp_body ); + free( rtmp_body->body ); + free( rtmp_body ); + + return rtmp_packet; +} + +static rtmp_packet_t * +rtmp_encode_createStream_result( rtmp_control_thread_t *p_thread, double stream_client_id, double stream_server_id ) +{ + rtmp_packet_t *rtmp_packet; + rtmp_body_t *rtmp_body; + uint8_t *tmp_buffer; + + /* Build createStream result */ + rtmp_body = rtmp_body_new( -1 ); + + tmp_buffer = amf_encode_element( AMF_DATATYPE_STRING, "_result" ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_STRING + strlen( "_result" ) ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_element( AMF_DATATYPE_NUMBER, &stream_client_id ); + rtmp_body_append( rtmp_body, tmp_buffer, AMF_DATATYPE_SIZE_NUMBER ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_element( AMF_DATATYPE_NULL, NULL ); + rtmp_body_append( rtmp_body, tmp_buffer, AMF_DATATYPE_SIZE_NULL ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_element( AMF_DATATYPE_NUMBER, &stream_server_id ); + rtmp_body_append( rtmp_body, tmp_buffer, AMF_DATATYPE_SIZE_NUMBER ); + free( tmp_buffer ); + + rtmp_packet = rtmp_new_packet( p_thread, RTMP_DEFAULT_STREAM_INDEX_INVOKE, + 0, RTMP_CONTENT_TYPE_INVOKE, 0, rtmp_body ); + free( rtmp_body->body ); + free( rtmp_body ); + + return rtmp_packet; +} + +static rtmp_packet_t * +rtmp_encode_ping_reset_stream( rtmp_control_thread_t *p_thread ) +{ + rtmp_packet_t *rtmp_packet; + rtmp_body_t *rtmp_body; + uint8_t *tmp_buffer; + + /* Build ping reset stream */ + rtmp_body = rtmp_body_new( -1 ); + + tmp_buffer = rtmp_encode_ping( RTMP_PING_RESET_STREAM, RTMP_SRC_DST_CONNECT_OBJECT2, 0, 0 ); + rtmp_body_append( rtmp_body, tmp_buffer, RTMP_PING_SIZE_RESET_STREAM ); + free( tmp_buffer ); + + rtmp_packet = rtmp_new_packet( p_thread, RTMP_DEFAULT_STREAM_INDEX_CONTROL, + 0, RTMP_CONTENT_TYPE_PING, 0, rtmp_body ); + free( rtmp_body->body ); + free( rtmp_body ); + + return rtmp_packet; +} + +static rtmp_packet_t * +rtmp_encode_ping_clear_stream( rtmp_control_thread_t *p_thread, uint32_t src_dst ) +{ + rtmp_packet_t *rtmp_packet; + rtmp_body_t *rtmp_body; + uint8_t *tmp_buffer; + + /* Build ping clear stream */ + rtmp_body = rtmp_body_new( -1 ); + + tmp_buffer = rtmp_encode_ping( RTMP_PING_CLEAR_STREAM, src_dst, 0, 0 ); + rtmp_body_append( rtmp_body, tmp_buffer, RTMP_PING_SIZE_CLEAR_STREAM ); + free( tmp_buffer ); + + rtmp_packet = rtmp_new_packet( p_thread, RTMP_DEFAULT_STREAM_INDEX_CONTROL, + 0, RTMP_CONTENT_TYPE_PING, 0, rtmp_body ); + free( rtmp_body->body ); + free( rtmp_body ); + + return rtmp_packet; +} + +static rtmp_packet_t * +rtmp_encode_NetStream_play_reset_onStatus( rtmp_control_thread_t *p_thread, char *psz_media ) +{ + rtmp_packet_t *rtmp_packet; + rtmp_body_t *rtmp_body; + uint8_t *tmp_buffer; + double number; + char *description; + + /* Build NetStream.play.reset onStatus */ + rtmp_body = rtmp_body_new( -1 ); + + tmp_buffer = amf_encode_element( AMF_DATATYPE_STRING, "onStatus" ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_STRING + strlen( "onStatus" ) ); + free( tmp_buffer ); + + number = 1; /* TODO: review this number*/ + tmp_buffer = amf_encode_element( AMF_DATATYPE_NUMBER, &number ); + rtmp_body_append( rtmp_body, tmp_buffer, AMF_DATATYPE_SIZE_NUMBER ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_element( AMF_DATATYPE_NULL, NULL ); + rtmp_body_append( rtmp_body, tmp_buffer, AMF_DATATYPE_SIZE_NULL ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_element( AMF_DATATYPE_OBJECT, NULL ); + rtmp_body_append( rtmp_body, tmp_buffer, AMF_DATATYPE_SIZE_OBJECT ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_object_variable( "level", + AMF_DATATYPE_STRING, "status" ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_OBJECT_VARIABLE + strlen( "level" ) + + AMF_DATATYPE_SIZE_STRING + strlen( "status" ) ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_object_variable( "code", + AMF_DATATYPE_STRING, "NetStream.Play.Reset" ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_OBJECT_VARIABLE + strlen( "code" ) + + AMF_DATATYPE_SIZE_STRING + strlen( "NetStream.Play.Reset" ) ); + free( tmp_buffer ); + + description = (char *) malloc( strlen( "Playing and resetting ") + strlen( psz_media ) + strlen( "." ) + 1 ); + if( !description ) + { + free( rtmp_body->body ); + free( rtmp_body ); + return NULL; + } + sprintf( description, "Playing and resetting %s.", psz_media ); + tmp_buffer = amf_encode_object_variable( "description", + AMF_DATATYPE_STRING, description ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_OBJECT_VARIABLE + strlen( "description" ) + + AMF_DATATYPE_SIZE_STRING + strlen( description ) ); + free( tmp_buffer ); + free( description ); + + tmp_buffer = amf_encode_object_variable( "details", + AMF_DATATYPE_STRING, psz_media ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_OBJECT_VARIABLE + strlen( "details" ) + + AMF_DATATYPE_SIZE_STRING + strlen( psz_media ) ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_object_variable( "clientid", + AMF_DATATYPE_NUMBER, &p_thread->stream_client_id ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_OBJECT_VARIABLE + strlen( "clientid" ) + + AMF_DATATYPE_SIZE_NUMBER ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_element ( AMF_DATATYPE_END_OF_OBJECT, NULL ); + rtmp_body_append( rtmp_body, tmp_buffer, AMF_DATATYPE_SIZE_END_OF_OBJECT ); + free( tmp_buffer ); + + rtmp_packet = rtmp_new_packet( p_thread, RTMP_DEFAULT_STREAM_INDEX_NOTIFY, + 0, RTMP_CONTENT_TYPE_INVOKE, RTMP_SRC_DST_DEFAULT, rtmp_body ); + free( rtmp_body->body ); + free( rtmp_body ); + + return rtmp_packet; +} + +static rtmp_packet_t * +rtmp_encode_NetStream_play_start_onStatus( rtmp_control_thread_t *p_thread, char *psz_media ) +{ + rtmp_packet_t *rtmp_packet; + rtmp_body_t *rtmp_body; + uint8_t *tmp_buffer; + double number; + char *description; + + /* Build NetStream.play.start onStatus */ + rtmp_body = rtmp_body_new( -1 ); + + tmp_buffer = amf_encode_element( AMF_DATATYPE_STRING, "onStatus" ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_STRING + strlen( "onStatus" ) ); + free( tmp_buffer ); + + number = 1; /* TODO: review this number*/ + tmp_buffer = amf_encode_element( AMF_DATATYPE_NUMBER, &number ); + rtmp_body_append( rtmp_body, tmp_buffer, AMF_DATATYPE_SIZE_NUMBER ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_element( AMF_DATATYPE_NULL, NULL ); + rtmp_body_append( rtmp_body, tmp_buffer, AMF_DATATYPE_SIZE_NULL ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_element( AMF_DATATYPE_OBJECT, NULL ); + rtmp_body_append( rtmp_body, tmp_buffer, AMF_DATATYPE_SIZE_OBJECT ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_object_variable( "level", + AMF_DATATYPE_STRING, "status" ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_OBJECT_VARIABLE + strlen( "level" ) + + AMF_DATATYPE_SIZE_STRING + strlen( "status" ) ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_object_variable( "code", + AMF_DATATYPE_STRING, "NetStream.Play.Start" ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_OBJECT_VARIABLE + strlen( "code" ) + + AMF_DATATYPE_SIZE_STRING + strlen( "NetStream.Play.Start" ) ); + free( tmp_buffer ); + + description = (char *) malloc( strlen( "Started playing ") + strlen( psz_media ) + strlen( "." ) + 1 ); + if( !description ) + { + free( rtmp_body->body ); + free( rtmp_body ); + return NULL; + } + + sprintf( description, "Started playing %s.", psz_media ); + tmp_buffer = amf_encode_object_variable( "description", + AMF_DATATYPE_STRING, description ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_OBJECT_VARIABLE + strlen( "description" ) + + AMF_DATATYPE_SIZE_STRING + strlen( description ) ); + free( tmp_buffer ); + free( description ); + + tmp_buffer = amf_encode_object_variable( "details", + AMF_DATATYPE_STRING, psz_media ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_OBJECT_VARIABLE + strlen( "details" ) + + AMF_DATATYPE_SIZE_STRING + strlen( psz_media ) ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_object_variable( "clientid", + AMF_DATATYPE_NUMBER, &p_thread->stream_client_id ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_OBJECT_VARIABLE + strlen( "clientid" ) + + AMF_DATATYPE_SIZE_NUMBER ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_element ( AMF_DATATYPE_END_OF_OBJECT, NULL ); + rtmp_body_append( rtmp_body, tmp_buffer, AMF_DATATYPE_SIZE_END_OF_OBJECT ); + free( tmp_buffer ); + + rtmp_packet = rtmp_new_packet( p_thread, RTMP_DEFAULT_STREAM_INDEX_NOTIFY, + 0, RTMP_CONTENT_TYPE_INVOKE, RTMP_SRC_DST_DEFAULT, rtmp_body ); + free( rtmp_body->body ); + free( rtmp_body ); + + return rtmp_packet; +} + +static uint8_t +rtmp_encode_header_size( vlc_object_t *p_this, uint8_t header_size ) +{ + if( header_size == 1 ) + return RTMP_HEADER_SIZE_1; + else if( header_size == 4 ) + return RTMP_HEADER_SIZE_4; + else if( header_size == 8 ) + return RTMP_HEADER_SIZE_8; + else if( header_size == 12 ) + return RTMP_HEADER_SIZE_12; + else + { + msg_Err( p_this, "invalid header size for encoding" ); + return 0; + } +} + +static uint8_t +rtmp_decode_header_size( vlc_object_t *p_this, uint8_t header_size ) +{ + if( header_size == RTMP_HEADER_SIZE_1 ) + return 1; + else if( header_size == RTMP_HEADER_SIZE_4 ) + return 4; + else if( header_size == RTMP_HEADER_SIZE_8 ) + return 8; + else if( header_size == RTMP_HEADER_SIZE_12 ) + return 12; + else + { + msg_Err( p_this, "invalid RTMP_HEADER_SIZE_XX " ); + return 0; + } +} + +static uint8_t +rtmp_get_stream_index( uint8_t content_type ) +{ + if( content_type == RTMP_CONTENT_TYPE_AUDIO_DATA ) + return RTMP_DEFAULT_STREAM_INDEX_AUDIO_DATA; + else if( content_type == RTMP_CONTENT_TYPE_VIDEO_DATA ) + return RTMP_DEFAULT_STREAM_INDEX_VIDEO_DATA; + else if( content_type == RTMP_CONTENT_TYPE_NOTIFY ) + return RTMP_DEFAULT_STREAM_INDEX_NOTIFY; + else + return -1; +} + +/***************************************************************************** + * Body handling implementation: + ******************************************************************************/ +rtmp_body_t * +rtmp_body_new( int length_buffer ) +{ + rtmp_body_t *rtmp_body; + + rtmp_body = (rtmp_body_t *) malloc( sizeof( rtmp_body_t ) ); + if( !rtmp_body ) return NULL; + + rtmp_body->length_body = 0; + if( length_buffer < 0 ) + rtmp_body->length_buffer = RTMP_BODY_SIZE_ALLOC; + else + rtmp_body->length_buffer = length_buffer; + rtmp_body->body = (uint8_t *) malloc( rtmp_body->length_buffer * sizeof( uint8_t ) ); + if( !rtmp_body->body ) + { + free( rtmp_body ); + return NULL; + } + return rtmp_body; +} + +void +rtmp_body_reset( rtmp_body_t *rtmp_body ) +{ + rtmp_body->length_body = 0; +} + +static void +rtmp_body_append( rtmp_body_t *rtmp_body, uint8_t *buffer, uint32_t length ) +{ + if( rtmp_body->length_body + length > rtmp_body->length_buffer ) + { + uint8_t *tmp; + rtmp_body->length_buffer = rtmp_body->length_body + length; + tmp = realloc( rtmp_body->body, + rtmp_body->length_buffer * sizeof( uint8_t ) ); + if( !tmp ) return; + rtmp_body->body = tmp; + } + + memcpy( rtmp_body->body + rtmp_body->length_body, buffer, length ); + rtmp_body->length_body += length; +} + +/***************************************************************************** + * RTMP ping implementation: + ******************************************************************************/ +static uint8_t * +rtmp_encode_ping( uint16_t type, uint32_t src_dst, uint32_t third_arg, uint32_t fourth_arg ) +{ + uint8_t *out = NULL; + VLC_UNUSED(fourth_arg); + + if( type == RTMP_PING_CLEAR_STREAM ) + out = (uint8_t *) malloc( RTMP_PING_SIZE_CLEAR_STREAM * sizeof( uint8_t ) ); + else if( type == RTMP_PING_CLEAR_PLAYING_BUFFER ) + out = (uint8_t *) malloc( RTMP_PING_SIZE_CLEAR_PLAYING_BUFFER * sizeof( uint8_t ) ); + else if( type == RTMP_PING_BUFFER_TIME_CLIENT ) + { + out = (uint8_t *) malloc( RTMP_PING_SIZE_BUFFER_TIME_CLIENT * sizeof( uint8_t ) ); + if( !out ) goto error; + third_arg = hton32( third_arg ); + memcpy( out + 6, &third_arg, sizeof( uint32_t ) ); + } + else if( type == RTMP_PING_RESET_STREAM ) + { + out = (uint8_t *) malloc( RTMP_PING_SIZE_RESET_STREAM * sizeof( uint8_t ) ); + } +/* else if( type == RTMP_PING_CLIENT_FROM_SERVER ) TODO: research this + { + } + else if( type == RTMP_PING_PONG_FROM_CLIENT ) + { + } +*/ else + { + out = (uint8_t *) malloc( RTMP_PING_SIZE_BUFFER_TIME_CLIENT * sizeof( uint8_t ) ); + if( !out ) goto error; + out[6] = 0x0D; out[7] = 0x0E; out[8] = 0x0A; out[9] = 0x0D; + } + + if( !out ) goto error; + + type = hton16( type ); + memcpy( out, &type, sizeof( uint16_t ) ); + + src_dst = hton32( src_dst ); + memcpy( out + 2, &src_dst, sizeof( uint32_t ) ); + + return out; + +error: + return NULL; +} + +/***************************************************************************** + * AMF implementation: + ******************************************************************************/ +static uint8_t * +amf_encode_element( uint8_t element, const void *value ) +{ + uint8_t *out; + + if ( element == AMF_DATATYPE_NUMBER ) + { + uint64_t number = *(uint64_t *) value; + + out = (uint8_t *) malloc( AMF_DATATYPE_SIZE_NUMBER * sizeof( uint8_t ) ); + if( !out ) return NULL; + + number = hton64( number ); + out[0] = AMF_DATATYPE_NUMBER; + memcpy( out + 1, &number, sizeof( uint64_t ) ); + } else if ( element == AMF_DATATYPE_BOOLEAN ) + { + out = (uint8_t *) malloc( AMF_DATATYPE_SIZE_BOOLEAN * sizeof( uint8_t ) ); + if( !out ) return NULL; + + out[0] = AMF_DATATYPE_BOOLEAN; + out[1] = *(uint8_t *) value; + } else if ( element == AMF_DATATYPE_STRING ) + { + uint16_t length_psz, length_psz_cpy; + + length_psz = length_psz_cpy = strlen( (char *) value ); + out = (uint8_t *) malloc( ( AMF_DATATYPE_SIZE_STRING + length_psz ) * sizeof( uint8_t ) ); + if( !out ) return NULL; + + out[0] = AMF_DATATYPE_STRING; + length_psz = hton16( length_psz ); + memcpy( out + 1, &length_psz, sizeof( uint16_t ) ); + memcpy( out + 3, value, length_psz_cpy ); + } else if ( element == AMF_DATATYPE_OBJECT ) + { + out = (uint8_t *) malloc( AMF_DATATYPE_SIZE_OBJECT * sizeof( uint8_t ) ); + if( !out ) return NULL; + + out[0] = AMF_DATATYPE_OBJECT; + } else if ( element == AMF_DATATYPE_NULL ) + { + out = (uint8_t *) malloc( AMF_DATATYPE_SIZE_NULL * sizeof( uint8_t ) ); + if( !out ) return NULL; + + out[0] = AMF_DATATYPE_NULL; + } else if ( element == AMF_DATATYPE_MIXED_ARRAY ) + { + uint32_t highest_index = *(uint32_t *) value; + + out = (uint8_t *) malloc( AMF_DATATYPE_SIZE_MIXED_ARRAY * sizeof( uint8_t ) ); + if( !out ) return NULL; + + highest_index = hton32( highest_index ); + out[0] = AMF_DATATYPE_MIXED_ARRAY; + memcpy( out + 1, &highest_index, sizeof( uint32_t ) ); + } else if ( element == AMF_DATATYPE_END_OF_OBJECT ) + { + out = (uint8_t *) calloc( AMF_DATATYPE_SIZE_END_OF_OBJECT, sizeof( uint8_t ) ); + + out[AMF_DATATYPE_SIZE_END_OF_OBJECT - 1] = AMF_DATATYPE_END_OF_OBJECT; + } else + { + out = (uint8_t *) malloc( AMF_DATATYPE_SIZE_NUMBER * sizeof( uint8_t ) ); + if( !out ) return NULL; + + out[0] = AMF_DATATYPE_NUMBER; + out[1] = 0x0D; out[2] = 0x0E; out[3] = 0x0A; out[4] = 0x0D; + out[5] = 0x0B; out[6] = 0x0E; out[7] = 0x0E; out[8] = 0x0F; + } + + return out; +} + +static uint8_t * +amf_encode_object_variable( const char *key, uint8_t element, const void *value ) +{ + uint8_t *out, *out_value; + int length_value; + uint16_t length_psz, length_psz_cpy; + + length_psz = length_psz_cpy = strlen( key ); + + if( element == AMF_DATATYPE_NUMBER ) + length_value = AMF_DATATYPE_SIZE_NUMBER; + else if( element == AMF_DATATYPE_BOOLEAN ) + length_value = AMF_DATATYPE_SIZE_BOOLEAN; + else if( element == AMF_DATATYPE_STRING ) + length_value = AMF_DATATYPE_SIZE_STRING + strlen( (char *) value ); + else if( element == AMF_DATATYPE_NULL ) + length_value = AMF_DATATYPE_SIZE_NULL; + else + { + out = (uint8_t *) malloc( AMF_DATATYPE_SIZE_NUMBER * sizeof( uint8_t ) ); + if( !out ) return NULL; + + out[0] = AMF_DATATYPE_NUMBER; + out[1] = 0xD; out[2] = 0xE; out[3] = 0xA; out[4] = 0xD; + out[5] = 0xB; out[6] = 0xE; out[7] = 0xE; out[8] = 0xF; + + return out; + } + + out = (uint8_t *) malloc( ( AMF_DATATYPE_SIZE_OBJECT_VARIABLE + length_psz + length_value ) * sizeof( uint8_t ) ); + if( !out ) return NULL; + + length_psz = hton16( length_psz ); + memcpy( out, &length_psz, sizeof( uint16_t ) ); + memcpy( out + 2, key, length_psz_cpy ); + + out_value = amf_encode_element( element, value ); + memcpy( out + 2 + length_psz_cpy, out_value, length_value ); + free( out_value ); + + return out; +} + +static double +amf_decode_number( uint8_t **buffer ) +{ + uint64_t number; + double out; + + number = ntoh64( *(uint64_t *) *buffer ); + memcpy(&out, &number, sizeof( uint64_t ) ); + *buffer += sizeof( uint64_t ); + + return out; +} + +static int +amf_decode_boolean( uint8_t **buffer ) +{ + int out; + + out = **buffer; + *buffer += 1; + + return out; +} + +/* return value allocated dinamically */ +static char * +amf_decode_string( uint8_t **buffer ) +{ + char *out; + int length; + int i; + + length = ntoh16( *(uint16_t *) *buffer ); + *buffer += sizeof( uint16_t ); + + out = (char *) malloc( length + 1 ); /* '\0' terminated */ + if( !out ) return NULL; + + for(i = 0; i < length; i++) + out[i] = (*buffer)[i]; + + *buffer += length; + + out[i] = '\0'; + + return out; +} + +/* returns in each call next key, at end of object returns NULL */ +/* need to decode value of key after call */ +static char * +amf_decode_object( uint8_t **buffer ) +{ + if( **buffer == 0x0 && *(*buffer + 1) == 0x00 && *(*buffer + 2) == 0x09) + { + *buffer += 3; + + return NULL; + } + else + return amf_decode_string( buffer ); +} + +/***************************************************************************** + * FLV rebuilding implementation: + ******************************************************************************/ +static void +flv_rebuild( rtmp_control_thread_t *p_thread, rtmp_packet_t *rtmp_packet ) +{ + uint32_t length_tag, timestamp; + uint8_t *tmp; + + tmp = (uint8_t *) realloc( rtmp_packet->body->body, + rtmp_packet->body->length_body + + FLV_TAG_PREVIOUS_TAG_SIZE + FLV_TAG_SIZE ); + if( !tmp ) return; + rtmp_packet->body->body = tmp; + memmove( rtmp_packet->body->body + FLV_TAG_PREVIOUS_TAG_SIZE + FLV_TAG_SIZE, + rtmp_packet->body->body, rtmp_packet->body->length_body ); + + /* Insert tag */ + p_thread->flv_tag_previous_tag_size = hton32( p_thread->flv_tag_previous_tag_size ); + memcpy( rtmp_packet->body->body, &p_thread->flv_tag_previous_tag_size, sizeof( uint32_t ) ); + + /* Fill backwards because of overlapping*/ + rtmp_packet->body->body[11] = 0x00; + + timestamp = hton32( rtmp_packet->timestamp ); + memcpy( rtmp_packet->body->body + 7, ×tamp, sizeof( uint32_t ) ); + + length_tag = hton32( rtmp_packet->body->length_body ); + memcpy( rtmp_packet->body->body + 4, &length_tag, sizeof( uint32_t ) ); + + rtmp_packet->body->body[4] = rtmp_packet->content_type; + + rtmp_packet->body->body[12] = 0x00; + rtmp_packet->body->body[13] = 0x00; + rtmp_packet->body->body[14] = 0x00; + + p_thread->flv_tag_previous_tag_size = rtmp_packet->body->length_body + FLV_TAG_SIZE; + + /* Update size */ + rtmp_packet->body->length_body += FLV_TAG_PREVIOUS_TAG_SIZE + FLV_TAG_SIZE; + rtmp_packet->body->length_buffer = rtmp_packet->body->length_body; +} + +static void +flv_get_metadata_audio( rtmp_control_thread_t *p_thread, rtmp_packet_t *packet_audio, uint8_t *stereo, uint8_t *audiosamplesize, uint32_t *audiosamplerate, uint8_t *audiocodecid ) +{ + uint8_t data_audio; + + data_audio = *packet_audio->body->body; + + if( ( data_audio & FLV_AUDIO_STEREO_MASK ) == FLV_AUDIO_STEREO_MONO ) + *stereo = FLV_AUDIO_STEREO_MONO; + else if( ( data_audio & FLV_AUDIO_STEREO_MASK ) == FLV_AUDIO_STEREO_STEREO ) + *stereo = FLV_AUDIO_STEREO_STEREO; + else + msg_Warn( p_thread, "unknown metadata audio stereo" ); + + if( ( data_audio & FLV_AUDIO_SIZE_MASK ) == FLV_AUDIO_SIZE_8_BIT ) + *audiosamplesize = FLV_AUDIO_SIZE_8_BIT >> 1; + else if( ( data_audio & FLV_AUDIO_SIZE_MASK ) == FLV_AUDIO_SIZE_16_BIT ) + *audiosamplesize = FLV_AUDIO_SIZE_16_BIT >> 1; + else + msg_Warn( p_thread, "unknown metadata audio sample size" ); + + if( ( data_audio & FLV_AUDIO_RATE_MASK ) == FLV_AUDIO_RATE_5_5_KHZ ) + *audiosamplerate = 5512; + else if( ( data_audio & FLV_AUDIO_RATE_MASK ) == FLV_AUDIO_RATE_11_KHZ ) + *audiosamplerate = 11025; + else if( ( data_audio & FLV_AUDIO_RATE_MASK ) == FLV_AUDIO_RATE_22_KHZ ) + *audiosamplerate = 22050; + else if( ( data_audio & FLV_AUDIO_RATE_MASK ) == FLV_AUDIO_RATE_44_KHZ ) + *audiosamplerate = 44100; + else + msg_Warn( p_thread, "unknown metadata audio sample rate" ); + + if( ( data_audio & FLV_AUDIO_CODEC_ID_MASK ) == FLV_AUDIO_CODEC_ID_UNCOMPRESSED ) + *audiocodecid = FLV_AUDIO_CODEC_ID_UNCOMPRESSED >> 4; + else if( ( data_audio & FLV_AUDIO_CODEC_ID_MASK ) == FLV_AUDIO_CODEC_ID_ADPCM ) + *audiocodecid = FLV_AUDIO_CODEC_ID_ADPCM >> 4; + else if( ( data_audio & FLV_AUDIO_CODEC_ID_MASK ) == FLV_AUDIO_CODEC_ID_MP3 ) + *audiocodecid = FLV_AUDIO_CODEC_ID_MP3 >> 4; + else if( ( data_audio & FLV_AUDIO_CODEC_ID_MASK ) == FLV_AUDIO_CODEC_ID_NELLYMOSER_8KHZ_MONO ) + *audiocodecid = FLV_AUDIO_CODEC_ID_NELLYMOSER_8KHZ_MONO >> 4; + else if( ( data_audio & FLV_AUDIO_CODEC_ID_MASK ) == FLV_AUDIO_CODEC_ID_NELLYMOSER ) + *audiocodecid = FLV_AUDIO_CODEC_ID_NELLYMOSER >> 4; + else + msg_Warn( p_thread, "unknown metadata audio codec id" ); +} + +static void +flv_get_metadata_video( rtmp_control_thread_t *p_thread, rtmp_packet_t *packet_video, uint8_t *videocodecid, uint8_t *frametype ) +{ + uint8_t data_video; + + data_video = *packet_video->body->body; + + if( ( data_video & FLV_VIDEO_CODEC_ID_MASK ) == FLV_VIDEO_CODEC_ID_SORENSEN_H263 ) + *videocodecid = FLV_VIDEO_CODEC_ID_SORENSEN_H263; + else if( ( data_video & FLV_VIDEO_CODEC_ID_MASK ) == FLV_VIDEO_CODEC_ID_SCREEN_VIDEO ) + *videocodecid = FLV_VIDEO_CODEC_ID_SCREEN_VIDEO; + else if( ( data_video & FLV_VIDEO_CODEC_ID_MASK ) == FLV_VIDEO_CODEC_ID_ON2_VP6 ) + *videocodecid = FLV_VIDEO_CODEC_ID_ON2_VP6; + else if( ( data_video & FLV_VIDEO_CODEC_ID_MASK ) == FLV_VIDEO_CODEC_ID_ON2_VP6_ALPHA ) + *videocodecid = FLV_VIDEO_CODEC_ID_ON2_VP6_ALPHA; + else if( ( data_video & FLV_VIDEO_CODEC_ID_MASK ) == FLV_VIDEO_CODEC_ID_SCREEN_VIDEO_2 ) + *videocodecid = FLV_VIDEO_CODEC_ID_SCREEN_VIDEO_2; + else + msg_Warn( p_thread, "unknown metadata video codec id" ); + + if( ( data_video & FLV_VIDEO_FRAME_TYPE_MASK ) == FLV_VIDEO_FRAME_TYPE_KEYFRAME ) + *frametype = FLV_VIDEO_FRAME_TYPE_KEYFRAME >> 4; + else if( ( data_video & FLV_VIDEO_FRAME_TYPE_MASK ) == FLV_VIDEO_FRAME_TYPE_INTER_FRAME ) + *frametype = FLV_VIDEO_FRAME_TYPE_INTER_FRAME >> 4; + else if( ( data_video & FLV_VIDEO_FRAME_TYPE_MASK ) == FLV_VIDEO_FRAME_TYPE_DISPOSABLE_INTER_FRAME ) + *frametype = FLV_VIDEO_FRAME_TYPE_DISPOSABLE_INTER_FRAME >> 4; + else + msg_Warn( p_thread, "unknown metadata video frame type" ); +} + +static rtmp_packet_t * +flv_build_onMetaData( access_t *p_access, uint64_t duration, uint8_t stereo, uint8_t audiosamplesize, uint32_t audiosamplerate, uint8_t audiocodecid, uint8_t videocodecid ) +{ + rtmp_packet_t *rtmp_packet; + rtmp_body_t *rtmp_body; + uint8_t *tmp_buffer; + double number; + + rtmp_body = rtmp_body_new( -1 ); + + tmp_buffer = amf_encode_element( AMF_DATATYPE_STRING, "onMetaData" ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_STRING + strlen( "onMetaData" ) ); + free( tmp_buffer ); + + number = 0; + tmp_buffer = amf_encode_element( AMF_DATATYPE_MIXED_ARRAY, &number ); + rtmp_body_append( rtmp_body, tmp_buffer, AMF_DATATYPE_SIZE_MIXED_ARRAY ); + free( tmp_buffer ); + + number = duration; + tmp_buffer = amf_encode_object_variable( "duration", + AMF_DATATYPE_NUMBER, &number ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_OBJECT_VARIABLE + strlen( "duration" ) + + AMF_DATATYPE_SIZE_NUMBER ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_object_variable( "stereo", + AMF_DATATYPE_BOOLEAN, &stereo ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_OBJECT_VARIABLE + strlen( "stereo" ) + + AMF_DATATYPE_SIZE_BOOLEAN ); + free( tmp_buffer ); + + number = audiosamplesize; + tmp_buffer = amf_encode_object_variable( "audiosamplesize", + AMF_DATATYPE_NUMBER, &number ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_OBJECT_VARIABLE + strlen( "audiosamplesize" ) + + AMF_DATATYPE_SIZE_NUMBER ); + free( tmp_buffer ); + + number = audiosamplerate; + tmp_buffer = amf_encode_object_variable( "audiosamplerate", + AMF_DATATYPE_NUMBER, &number ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_OBJECT_VARIABLE + strlen( "audiosamplerate" ) + + AMF_DATATYPE_SIZE_NUMBER ); + free( tmp_buffer ); + + number = audiocodecid; + tmp_buffer = amf_encode_object_variable( "audiocodecid", + AMF_DATATYPE_NUMBER, &number ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_OBJECT_VARIABLE + strlen( "audiocodecid" ) + + AMF_DATATYPE_SIZE_NUMBER ); + free( tmp_buffer ); + + number = videocodecid; + tmp_buffer = amf_encode_object_variable( "videocodecid", + AMF_DATATYPE_NUMBER, &number ); + rtmp_body_append( rtmp_body, tmp_buffer, + AMF_DATATYPE_SIZE_OBJECT_VARIABLE + strlen( "videocodecid" ) + + AMF_DATATYPE_SIZE_NUMBER ); + free( tmp_buffer ); + + tmp_buffer = amf_encode_element( AMF_DATATYPE_END_OF_OBJECT, NULL ); + rtmp_body_append( rtmp_body, tmp_buffer, AMF_DATATYPE_SIZE_END_OF_OBJECT ); + free( tmp_buffer ); + + rtmp_packet = rtmp_new_packet( p_access->p_sys->p_thread, RTMP_DEFAULT_STREAM_INDEX_INVOKE, + 0, RTMP_CONTENT_TYPE_NOTIFY, 0, rtmp_body ); + free( rtmp_body->body ); + free( rtmp_body ); + + return rtmp_packet; +} + +block_t * +flv_get_metadata( access_t *p_access ) +{ + access_sys_t *p_sys = p_access->p_sys; + rtmp_packet_t *flv_metadata_packet; + block_t *p_buffer; + + flv_metadata_packet = flv_build_onMetaData( p_access, 0, p_sys->p_thread->metadata_stereo, + p_sys->p_thread->metadata_samplesize, p_sys->p_thread->metadata_samplerate, + p_sys->p_thread->metadata_audiocodecid, p_sys->p_thread->metadata_videocodecid ); + flv_rebuild( p_sys->p_thread, flv_metadata_packet ); + p_buffer = rtmp_new_block( p_sys->p_thread, flv_metadata_packet->body->body, flv_metadata_packet->body->length_buffer ); + + free( flv_metadata_packet->body->body ); + free( flv_metadata_packet->body ); + free( flv_metadata_packet ); + + return p_buffer; +} + +block_t * +flv_insert_header( access_t *p_access, block_t *first_packet ) +{ + access_sys_t *p_sys = p_access->p_sys; + int old_buffer_size; + uint32_t tmp_number; + + old_buffer_size = first_packet->i_buffer; + + first_packet = block_Realloc( first_packet, 0, first_packet->i_buffer + FLV_HEADER_SIZE ); + + memmove( first_packet->p_buffer + FLV_HEADER_SIZE, + first_packet->p_buffer, old_buffer_size ); + + memcpy( first_packet->p_buffer, FLV_HEADER_SIGNATURE, sizeof( FLV_HEADER_SIGNATURE ) ); + first_packet->p_buffer[3] = FLV_HEADER_VERSION; + if( p_sys->p_thread->has_audio && p_sys->p_thread->has_video ) + first_packet->p_buffer[4] = FLV_HEADER_AUDIO | FLV_HEADER_VIDEO; + else if( p_sys->p_thread->has_audio ) + first_packet->p_buffer[4] = FLV_HEADER_AUDIO; + else + first_packet->p_buffer[4] = FLV_HEADER_VIDEO; + tmp_number = hton32( FLV_HEADER_SIZE ); + memcpy( first_packet->p_buffer + 5, &tmp_number, sizeof( uint32_t ) ); + + return first_packet; +} diff --git a/VLC/rtmp_amf_flv.h b/VLC/rtmp_amf_flv.h new file mode 100644 index 0000000..27b6dba --- /dev/null +++ b/VLC/rtmp_amf_flv.h @@ -0,0 +1,146 @@ +/***************************************************************************** + * rtmp_amf_flv.h: RTMP, AMF and FLV over RTMP implementation. + ***************************************************************************** + * Copyright (C) URJC - LADyR - Luis Lopez Fernandez + * + * Author: Miguel Angel Cabrera Moya + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Local prototypes (continued from access.c) + *****************************************************************************/ +typedef struct rtmp_packet_t rtmp_packet_t; +typedef struct rtmp_body_t rtmp_body_t; +typedef struct rtmp_control_thread_t rtmp_control_thread_t; +typedef void (*rtmp_handler_t)( rtmp_control_thread_t *p_thread, rtmp_packet_t *rtmp_packet ); + +struct rtmp_packet_t +{ + int length_header; + int stream_index; + uint32_t timestamp; + uint32_t timestamp_relative; + int32_t length_encoded; + int32_t length_body; + uint8_t content_type; + uint32_t src_dst; + rtmp_body_t *body; +}; + +struct rtmp_body_t +{ + int32_t length_body; /* without interchunk headers */ + int32_t length_buffer; + uint8_t *body; +}; + +struct rtmp_control_thread_t +{ + VLC_COMMON_MEMBERS + + int fd; + + vlc_url_t url; + char *psz_application; + char *psz_media; + + block_fifo_t *p_fifo_input; + block_fifo_t *p_empty_blocks; + + vlc_mutex_t lock; + vlc_cond_t wait; + + int result_connect; + int result_publish; + int result_play; + int result_stop; + + double stream_client_id; + double stream_server_id; + + char *psz_publish; + + /* Rebuild FLV variables (access) */ + int has_audio; + int has_video; + int metadata_received; + uint8_t metadata_stereo; + uint8_t metadata_samplesize; + uint32_t metadata_samplerate; + uint8_t metadata_audiocodecid; + uint8_t metadata_videocodecid; + uint8_t metadata_frametype; + int first_media_packet; + uint32_t flv_tag_previous_tag_size; + + /* Vars for rebuilding FLV (access_output) */ + rtmp_body_t *flv_body; + uint8_t flv_content_type; + uint32_t flv_length_body; + uint32_t flv_timestamp; + + /* vars for channel state */ + uint32_t chunk_size_recv; + uint32_t chunk_size_send; + rtmp_packet_t rtmp_headers_recv[64]; /* RTMP_HEADER_STREAM_MAX */ + rtmp_packet_t rtmp_headers_send[64]; + + rtmp_handler_t rtmp_handler[21]; /* index by RTMP_CONTENT_TYPE */ + + /* Pointer to base module object (later needs to casted) */ + void *p_base_object; +}; + +struct access_sys_t +{ + int active; + + /* vars for reading from fifo */ + block_t *flv_packet; + int read_packet; + + /* thread for filtering and handling control messages */ + rtmp_control_thread_t *p_thread; +}; + +/***************************************************************************** + * RTMP header: + ******************************************************************************/ +int rtmp_handshake_active( vlc_object_t *p_this, int fd ); +int rtmp_handshake_passive( vlc_object_t *p_this, int fd ); +int rtmp_connect_active( rtmp_control_thread_t *p_thread ); +int rtmp_connect_passive( rtmp_control_thread_t *p_thread ); +//int rtmp_seek( access_t *p_access, int64_t i_pos ); TODO +// +rtmp_packet_t *rtmp_build_bytes_read( rtmp_control_thread_t *p_thread, uint32_t reply ); +rtmp_packet_t *rtmp_build_publish_start( rtmp_control_thread_t *p_thread ); +rtmp_packet_t *rtmp_build_flv_over_rtmp( rtmp_control_thread_t *p_thread, block_t *p_buffer ); + +rtmp_packet_t *rtmp_read_net_packet( rtmp_control_thread_t *p_thread ); +uint8_t *rtmp_encode_packet( rtmp_control_thread_t *p_thread, rtmp_packet_t *rtmp_packet ); +void rtmp_init_handler( rtmp_handler_t *rtmp_handler ); +/***************************************************************************** + * FLV header: + ******************************************************************************/ +block_t *flv_get_metadata( access_t *p_access ); +block_t *flv_insert_header( access_t *p_access, block_t *first_packet ); + +/***************************************************************************** + * RTMP body header: + ******************************************************************************/ +rtmp_body_t *rtmp_body_new( int length_buffer ); +void rtmp_body_reset( rtmp_body_t * ); diff --git a/VLC/search.c b/VLC/search.c new file mode 100644 index 0000000..eccbf1e --- /dev/null +++ b/VLC/search.c @@ -0,0 +1,177 @@ +/***************************************************************************** + * search.c : Search functions + ***************************************************************************** + * Copyright (C) 1999-2004 the VideoLAN team + * $Id$ + * + * Authors: Clément Stenac + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include + +#include "vlc_common.h" +#include "vlc_playlist.h" +#include "playlist_internal.h" + +/*************************************************************************** + * Item search functions + ***************************************************************************/ + +/** + * Search a playlist item by its playlist_item id + * + * \param p_playlist the playlist + * \param i_id the id to find + * \return the item or NULL on failure + */ +playlist_item_t * playlist_ItemGetById( playlist_t * p_playlist , int i_id, + bool b_locked ) +{ + int i; + PL_LOCK_IF( !b_locked ); + ARRAY_BSEARCH( p_playlist->all_items,->i_id, int, i_id, i ); + if( i != -1 ) + { + PL_UNLOCK_IF( !b_locked ); + return ARRAY_VAL( p_playlist->all_items, i ); + } + PL_UNLOCK_IF( !b_locked ); + return NULL; +} + +/** + * Search an item by its input_item_t + * + * \param p_playlist the playlist + * \param p_item the input_item_t to find + * \return the item, or NULL on failure + */ +playlist_item_t * playlist_ItemGetByInput( playlist_t * p_playlist , + input_item_t *p_item, + bool b_locked ) +{ + int i; + PL_LOCK_IF( !b_locked ); + if( get_current_status_item( p_playlist ) && + get_current_status_item( p_playlist )->p_input == p_item ) + { + /* FIXME: this is potentially dangerous, we could destroy + * p_ret any time soon */ + playlist_item_t *p_ret = get_current_status_item( p_playlist ); + PL_UNLOCK_IF( !b_locked ); + return p_ret; + } + /** \todo Check if this is always incremental and whether we can bsearch */ + for( i = 0 ; i < p_playlist->all_items.i_size; i++ ) + { + if( ARRAY_VAL(p_playlist->all_items, i)->p_input->i_id == p_item->i_id ) + { + PL_UNLOCK_IF( !b_locked ); + return ARRAY_VAL(p_playlist->all_items, i); + } + } + PL_UNLOCK_IF( !b_locked ); + return NULL; +} + +/** + * Get input by item id + * + * Find the playlist item matching the input id under the given node + * \param p_playlist the playlist + * \param i_input_id the id of the input to find + * \param p_root the root node of the search + * \return the playlist item or NULL on failure + */ +playlist_item_t * playlist_ItemGetByInputId( playlist_t *p_playlist, + int i_input_id, + playlist_item_t *p_root ) +{ + int i; + PL_ASSERT_LOCKED; + assert( p_root != NULL ); + for( i = 0 ; i< p_root->i_children ; i++ ) + { + if( p_root->pp_children[i]->p_input && + p_root->pp_children[i]->p_input->i_id == i_input_id ) + { + return p_root->pp_children[i]; + } + else if( p_root->pp_children[i]->i_children >= 0 ) + { + return playlist_ItemGetByInputId( p_playlist, i_input_id, + p_root->pp_children[i] ); + } + } + return NULL; +} + +/*************************************************************************** + * Live search handling + ***************************************************************************/ + +static bool playlist_LiveSearchUpdateInternal( playlist_t *p_playlist, + playlist_item_t *p_root, + const char *psz_string ) +{ + int i; + bool b_match = false; + for( i = 0 ; i < p_root->i_children ; i ++ ) + { + playlist_item_t *p_item = p_root->pp_children[i]; + if( p_item->i_children > -1 ) + { + if( playlist_LiveSearchUpdateInternal( p_playlist, p_item, psz_string ) || + strcasestr( p_item->p_input->psz_name, psz_string ) ) + { + p_item->i_flags &= ~PLAYLIST_DBL_FLAG; + b_match = true; + } + else + { + p_item->i_flags |= PLAYLIST_DBL_FLAG; + } + } + else + { + if( strcasestr( p_item->p_input->psz_name, psz_string ) || /* Soon to be replaced by vlc_meta_Title */ + input_item_MetaMatch( p_item->p_input, vlc_meta_Album, psz_string ) || + input_item_MetaMatch( p_item->p_input, vlc_meta_Artist, psz_string ) ) + { + p_item->i_flags &= ~PLAYLIST_DBL_FLAG; + b_match = true; + } + else + { + p_item->i_flags |= PLAYLIST_DBL_FLAG; + } + } + } + return b_match; +} + +int playlist_LiveSearchUpdate( playlist_t *p_playlist, playlist_item_t *p_root, + const char *psz_string ) +{ + PL_ASSERT_LOCKED; + p_playlist->b_reset_currently_playing = true; + playlist_LiveSearchUpdateInternal( p_playlist, p_root, psz_string ); + vlc_object_signal_unlocked( p_playlist ); + return VLC_SUCCESS; +} diff --git a/VLC/services_discovery.c b/VLC/services_discovery.c new file mode 100644 index 0000000..64a0fe6 --- /dev/null +++ b/VLC/services_discovery.c @@ -0,0 +1,459 @@ +/***************************************************************************** + * services_discovery.c : Manage playlist services_discovery modules + ***************************************************************************** + * Copyright (C) 1999-2004 the VideoLAN team + * $Id$ + * + * Authors: Clément Stenac + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include + +#include "vlc_common.h" +#include "vlc_playlist.h" +#include "vlc_events.h" +#include "playlist_internal.h" +#include "libvlc.h" + +static void* RunSD( vlc_object_t *p_this ); + +/* + * Services discovery + * Basically you just listen to Service discovery event through the + * sd's event manager. + * That's how the playlist get's Service Discovery information + */ + +/*********************************************************************** + * GetServicesNames + ***********************************************************************/ +char ** __services_discovery_GetServicesNames( vlc_object_t * p_super, + char ***pppsz_longnames ) +{ + return module_GetModulesNamesForCapability( p_super, "services_discovery", + pppsz_longnames ); +} + +/*********************************************************************** + * Create + ***********************************************************************/ +services_discovery_t * +services_discovery_Create ( vlc_object_t * p_super, const char * psz_module_name ) +{ + services_discovery_t *p_sd; + p_sd = vlc_custom_create( p_super, sizeof( *p_sd ), VLC_OBJECT_GENERIC, + "services discovery" ); + if( !p_sd ) + return NULL; + + p_sd->pf_run = NULL; + p_sd->psz_localized_name = NULL; + + vlc_event_manager_init( &p_sd->event_manager, p_sd, (vlc_object_t *)p_sd ); + vlc_event_manager_register_event_type( &p_sd->event_manager, + vlc_ServicesDiscoveryItemAdded ); + vlc_event_manager_register_event_type( &p_sd->event_manager, + vlc_ServicesDiscoveryItemRemoved ); + vlc_event_manager_register_event_type( &p_sd->event_manager, + vlc_ServicesDiscoveryStarted ); + vlc_event_manager_register_event_type( &p_sd->event_manager, + vlc_ServicesDiscoveryEnded ); + + p_sd->p_module = module_Need( p_sd, "services_discovery", psz_module_name, true ); + + if( p_sd->p_module == NULL ) + { + msg_Err( p_super, "no suitable services discovery module" ); + vlc_object_release( p_sd ); + return NULL; + } + p_sd->psz_module = strdup( psz_module_name ); + p_sd->b_die = false; /* FIXME */ + + vlc_object_attach( p_sd, p_super ); + return p_sd; +} + +/*********************************************************************** + * Destroy + ***********************************************************************/ +void services_discovery_Destroy ( services_discovery_t * p_sd ) +{ + vlc_event_manager_fini( &p_sd->event_manager ); + + free( p_sd->psz_module ); + free( p_sd->psz_localized_name ); + + vlc_object_detach( p_sd ); + vlc_object_release( p_sd ); +} + +/*********************************************************************** + * Start + ***********************************************************************/ +int services_discovery_Start ( services_discovery_t * p_sd ) +{ + if ((p_sd->pf_run != NULL) + && vlc_thread_create( p_sd, "services_discovery", RunSD, + VLC_THREAD_PRIORITY_LOW, false)) + { + msg_Err( p_sd, "cannot create services discovery thread" ); + vlc_object_release( p_sd ); + return VLC_EGENERIC; + } + return VLC_SUCCESS; +} + +/*********************************************************************** + * Stop + ***********************************************************************/ +static void ObjectKillChildrens( vlc_object_t *p_obj ) +{ + vlc_list_t *p_list; + int i; + vlc_object_kill( p_obj ); + + p_list = vlc_list_children( p_obj ); + for( i = 0; i < p_list->i_count; i++ ) + ObjectKillChildrens( p_list->p_values[i].p_object ); + vlc_list_release( p_list ); +} + +void services_discovery_Stop ( services_discovery_t * p_sd ) +{ + ObjectKillChildrens( VLC_OBJECT(p_sd) ); + if( p_sd->pf_run ) vlc_thread_join( p_sd ); + + module_Unneed( p_sd, p_sd->p_module ); +} + +/*********************************************************************** + * GetLocalizedName + ***********************************************************************/ +char * +services_discovery_GetLocalizedName ( services_discovery_t * p_sd ) +{ + return p_sd->psz_localized_name ? strdup( p_sd->psz_localized_name ) : NULL; +} + +/*********************************************************************** + * SetLocalizedName + ***********************************************************************/ +void +services_discovery_SetLocalizedName ( services_discovery_t * p_sd, const char *psz ) +{ + free( p_sd->psz_localized_name ); + p_sd->psz_localized_name = strdup(psz); +} + +/*********************************************************************** + * EventManager + ***********************************************************************/ +vlc_event_manager_t * +services_discovery_EventManager ( services_discovery_t * p_sd ) +{ + return &p_sd->event_manager; +} + +/*********************************************************************** + * AddItem + ***********************************************************************/ +void +services_discovery_AddItem ( services_discovery_t * p_sd, input_item_t * p_item, + const char * psz_category ) +{ + vlc_event_t event; + event.type = vlc_ServicesDiscoveryItemAdded; + event.u.services_discovery_item_added.p_new_item = p_item; + event.u.services_discovery_item_added.psz_category = psz_category; + + vlc_event_send( &p_sd->event_manager, &event ); +} + +/*********************************************************************** + * RemoveItem + ***********************************************************************/ +void +services_discovery_RemoveItem ( services_discovery_t * p_sd, input_item_t * p_item ) +{ + vlc_event_t event; + event.type = vlc_ServicesDiscoveryItemRemoved; + event.u.services_discovery_item_removed.p_item = p_item; + + vlc_event_send( &p_sd->event_manager, &event ); +} + +/*********************************************************************** + * RunSD (Private) + ***********************************************************************/ +static void* RunSD( vlc_object_t *p_this ) +{ + services_discovery_t *p_sd = (services_discovery_t *)p_this; + vlc_event_t event; + + event.type = vlc_ServicesDiscoveryStarted; + vlc_event_send( &p_sd->event_manager, &event ); + + p_sd->pf_run( p_sd ); + + event.type = vlc_ServicesDiscoveryEnded; + vlc_event_send( &p_sd->event_manager, &event ); + return NULL; +} + +/* + * Playlist - Services discovery bridge + */ + + /* A new item has been added to a certain sd */ +static void playlist_sd_item_added( const vlc_event_t * p_event, void * user_data ) +{ + input_item_t * p_input = p_event->u.services_discovery_item_added.p_new_item; + const char * psz_cat = p_event->u.services_discovery_item_added.psz_category; + playlist_item_t *p_new_item, * p_parent = user_data; + playlist_t * p_playlist = p_parent->p_playlist; + + msg_Dbg( p_playlist, "Adding %s in %s", + p_input->psz_name ? p_input->psz_name : "(null)", + psz_cat ? psz_cat : "(null)" ); + + PL_LOCK; + /* If p_parent is in root category (this is clearly a hack) and we have a cat */ + if( !EMPTY_STR(psz_cat) && + p_parent->p_parent == p_playlist->p_root_category ) + { + /* */ + playlist_item_t * p_cat; + p_cat = playlist_ChildSearchName( p_parent, psz_cat ); + if( !p_cat ) + { + p_cat = playlist_NodeCreate( p_playlist, psz_cat, + p_parent, 0, NULL ); + p_cat->i_flags &= ~PLAYLIST_SKIP_FLAG; + } + p_parent = p_cat; + } + + p_new_item = playlist_NodeAddInput( p_playlist, p_input, p_parent, + PLAYLIST_APPEND, PLAYLIST_END, pl_Locked ); + if( p_new_item ) + { + p_new_item->i_flags &= ~PLAYLIST_SKIP_FLAG; + p_new_item->i_flags &= ~PLAYLIST_SAVE_FLAG; + } + PL_UNLOCK; +} + + /* A new item has been removed from a certain sd */ +static void playlist_sd_item_removed( const vlc_event_t * p_event, void * user_data ) +{ + input_item_t * p_input = p_event->u.services_discovery_item_removed.p_item; + playlist_item_t * p_parent = user_data; + playlist_item_t * p_pl_item; + + /* First make sure that if item is a node it will be deleted. + * XXX: Why don't we have a function to ensure that in the playlist code ? */ + vlc_object_lock( p_parent->p_playlist ); + p_pl_item = playlist_ItemFindFromInputAndRoot( p_parent->p_playlist, + p_input->i_id, p_parent, false ); + + if( p_pl_item && p_pl_item->i_children > -1 ) + { + playlist_NodeDelete( p_parent->p_playlist, p_pl_item, true, false ); + vlc_object_unlock( p_parent->p_playlist ); + return; + } + + /* Delete the non-node item normally */ + playlist_DeleteFromInputInParent( p_parent->p_playlist, p_input->i_id, + p_parent, pl_Locked ); + + vlc_object_unlock( p_parent->p_playlist ); +} + +int playlist_ServicesDiscoveryAdd( playlist_t *p_playlist, const char *psz_modules ) +{ + const char *psz_parser = psz_modules ?: ""; + int retval = VLC_SUCCESS; + + for (;;) + { + struct playlist_services_discovery_support_t * p_sds; + playlist_item_t * p_cat; + playlist_item_t * p_one; + + while( *psz_parser == ' ' || *psz_parser == ':' || *psz_parser == ',' ) + psz_parser++; + + if( *psz_parser == '\0' ) + break; + + const char *psz_next = strchr( psz_parser, ':' ); + if( psz_next == NULL ) + psz_next = psz_parser + strlen( psz_parser ); + + char psz_plugin[psz_next - psz_parser + 1]; + memcpy (psz_plugin, psz_parser, sizeof (psz_plugin) - 1); + psz_plugin[sizeof (psz_plugin) - 1] = '\0'; + psz_parser = psz_next; + + /* Perform the addition */ + msg_Dbg( p_playlist, "Add services_discovery %s", psz_plugin ); + services_discovery_t *p_sd; + + p_sd = services_discovery_Create( (vlc_object_t*)p_playlist, psz_plugin ); + if( !p_sd ) + continue; + + char * psz = services_discovery_GetLocalizedName( p_sd ); + assert( psz ); + PL_LOCK; + playlist_NodesPairCreate( p_playlist, psz, + &p_cat, &p_one, false ); + PL_UNLOCK; + free( psz ); + + vlc_event_attach( services_discovery_EventManager( p_sd ), + vlc_ServicesDiscoveryItemAdded, + playlist_sd_item_added, + p_one ); + + vlc_event_attach( services_discovery_EventManager( p_sd ), + vlc_ServicesDiscoveryItemAdded, + playlist_sd_item_added, + p_cat ); + + vlc_event_attach( services_discovery_EventManager( p_sd ), + vlc_ServicesDiscoveryItemRemoved, + playlist_sd_item_removed, + p_one ); + + vlc_event_attach( services_discovery_EventManager( p_sd ), + vlc_ServicesDiscoveryItemRemoved, + playlist_sd_item_removed, + p_cat ); + + services_discovery_Start( p_sd ); + + /* Free in playlist_ServicesDiscoveryRemove */ + p_sds = malloc( sizeof(struct playlist_services_discovery_support_t) ); + if( !p_sds ) + return VLC_ENOMEM; + + /* We want tree-view for service directory */ + p_one->p_input->b_prefers_tree = true; + p_sds->p_sd = p_sd; + p_sds->p_one = p_one; + p_sds->p_cat = p_cat; + + PL_LOCK; + TAB_APPEND( p_playlist->i_sds, p_playlist->pp_sds, p_sds ); + PL_UNLOCK; + } + + return retval; +} + +int playlist_ServicesDiscoveryRemove( playlist_t * p_playlist, + const char *psz_module ) +{ + struct playlist_services_discovery_support_t * p_sds = NULL; + int i; + + PL_LOCK; + for( i = 0 ; i< p_playlist->i_sds ; i ++ ) + { + if( !strcmp( psz_module, p_playlist->pp_sds[i]->p_sd->psz_module ) ) + { + p_sds = p_playlist->pp_sds[i]; + REMOVE_ELEM( p_playlist->pp_sds, p_playlist->i_sds, i ); + break; + } + } + PL_UNLOCK; + + if( !p_sds || !p_sds->p_sd ) + { + msg_Warn( p_playlist, "module %s is not loaded", psz_module ); + return VLC_EGENERIC; + } + + services_discovery_Stop( p_sds->p_sd ); + + vlc_event_detach( services_discovery_EventManager( p_sds->p_sd ), + vlc_ServicesDiscoveryItemAdded, + playlist_sd_item_added, + p_sds->p_one ); + + vlc_event_detach( services_discovery_EventManager( p_sds->p_sd ), + vlc_ServicesDiscoveryItemAdded, + playlist_sd_item_added, + p_sds->p_cat ); + + vlc_event_detach( services_discovery_EventManager( p_sds->p_sd ), + vlc_ServicesDiscoveryItemRemoved, + playlist_sd_item_removed, + p_sds->p_one ); + + vlc_event_detach( services_discovery_EventManager( p_sds->p_sd ), + vlc_ServicesDiscoveryItemRemoved, + playlist_sd_item_removed, + p_sds->p_cat ); + + /* Remove the sd playlist node if it exists */ + PL_LOCK; + if( p_sds->p_cat != p_playlist->p_root_category && + p_sds->p_one != p_playlist->p_root_onelevel ) + { + playlist_NodeDelete( p_playlist, p_sds->p_cat, true, false ); + playlist_NodeDelete( p_playlist, p_sds->p_one, true, false ); + } + PL_UNLOCK; + + services_discovery_Destroy( p_sds->p_sd ); + free( p_sds ); + + return VLC_SUCCESS; +} + +bool playlist_IsServicesDiscoveryLoaded( playlist_t * p_playlist, + const char *psz_module ) +{ + int i; + PL_LOCK; + + for( i = 0 ; i< p_playlist->i_sds ; i ++ ) + { + if( !strcmp( psz_module, p_playlist->pp_sds[i]->p_sd->psz_module ) ) + { + PL_UNLOCK; + return true; + } + } + PL_UNLOCK; + return false; +} + +void playlist_ServicesDiscoveryKillAll( playlist_t *p_playlist ) +{ + while( p_playlist->i_sds > 0 ) + playlist_ServicesDiscoveryRemove( p_playlist, + p_playlist->pp_sds[0]->p_sd->psz_module ); +} + diff --git a/VLC/sort.c b/VLC/sort.c new file mode 100644 index 0000000..5624bba --- /dev/null +++ b/VLC/sort.c @@ -0,0 +1,246 @@ +/***************************************************************************** + * sort.c : Playlist sorting functions + ***************************************************************************** + * Copyright (C) 1999-2007 the VideoLAN team + * $Id$ + * + * Authors: Clément Stenac + * Ilkka Ollakka + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "vlc_playlist.h" +#include "playlist_internal.h" + + +static int playlist_ItemArraySort( playlist_t *p_playlist, int i_items, + playlist_item_t **pp_items, int i_mode, + int i_type ); +static int playlist_cmp(const void *, const void *); + +/** + * Sort a node. + * This function must be entered with the playlist lock ! + * + * \param p_playlist the playlist + * \param p_node the node to sort + * \param i_mode: SORT_ID, SORT_TITLE, SORT_ARTIST, SORT_ALBUM, SORT_RANDOM + * \param i_type: ORDER_NORMAL or ORDER_REVERSE (reversed order) + * \return VLC_SUCCESS on success + */ +static int playlist_NodeSort( playlist_t * p_playlist , playlist_item_t *p_node, + int i_mode, int i_type ) +{ + playlist_ItemArraySort( p_playlist,p_node->i_children, + p_node->pp_children, i_mode, i_type ); + return VLC_SUCCESS; +} + +/** + * Sort a node recursively. + * + * This function must be entered with the playlist lock ! + * + * \param p_playlist the playlist + * \param p_node the node to sort + * \param i_mode: SORT_ID, SORT_TITLE, SORT_ARTIST, SORT_ALBUM, SORT_RANDOM + * \param i_type: ORDER_NORMAL or ORDER_REVERSE (reversed order) + * \return VLC_SUCCESS on success + */ +int playlist_RecursiveNodeSort( playlist_t *p_playlist, playlist_item_t *p_node, + int i_mode, int i_type ) +{ + int i; + playlist_NodeSort( p_playlist, p_node, i_mode, i_type ); + for( i = 0 ; i< p_node->i_children; i++ ) + { + if( p_node->pp_children[i]->i_children != -1 ) + { + playlist_RecursiveNodeSort( p_playlist, p_node->pp_children[i], + i_mode,i_type ); + } + } + return VLC_SUCCESS; +} + +static int sort_mode = 0; +static int sort_type = 0; + +static int playlist_ItemArraySort( playlist_t *p_playlist, int i_items, + playlist_item_t **pp_items, int i_mode, + int i_type ) +{ + int i_position; + playlist_item_t *p_temp; + vlc_value_t val; + val.b_bool = true; + sort_mode = i_mode; + sort_type = i_type; + + (void)p_playlist; // a bit surprising we don't need p_playlist! + + if( i_mode == SORT_RANDOM ) + { + for( i_position = 0; i_position < i_items ; i_position ++ ) + { + int i_new; + + if( i_items > 1 ) + i_new = rand() % (i_items - 1); + else + i_new = 0; + p_temp = pp_items[i_position]; + pp_items[i_position] = pp_items[i_new]; + pp_items[i_new] = p_temp; + } + + return VLC_SUCCESS; + } + qsort(pp_items,i_items,sizeof(pp_items[0]),playlist_cmp); + return VLC_SUCCESS; +} + +static int playlist_cmp(const void *first, const void *second) +{ + +#define META_STRCASECMP_NAME( ) { \ + char *psz_i = input_item_GetName( (*(playlist_item_t **)first)->p_input ); \ + char *psz_ismall = input_item_GetName( (*(playlist_item_t **)second)->p_input ); \ + i_test = strcasecmp( psz_i, psz_ismall ); \ + free( psz_i ); \ + free( psz_ismall ); \ +} + + +#define DO_META_SORT_ADV( node, integer ) { \ + char *psz_a = input_item_GetMeta( (*(playlist_item_t **)first)->p_input, vlc_meta_##node ); \ + char *psz_b = input_item_GetMeta( (*(playlist_item_t **)second)->p_input, vlc_meta_##node ); \ + /* Nodes go first */ \ + if( (*(playlist_item_t **)first)->i_children == -1 && (*(playlist_item_t **)second)->i_children >= 0 ) \ + i_test = 1;\ + else if( (*(playlist_item_t **)first)->i_children >= 0 &&\ + (*(playlist_item_t **)second)->i_children == -1 ) \ + i_test = -1; \ + /* Both are nodes, sort by name */ \ + else if( (*(playlist_item_t **)first)->i_children >= 0 && \ + (*(playlist_item_t **)second)->i_children >= 0 ) \ + { \ + META_STRCASECMP_NAME( ) \ + } \ + /* Both are items */ \ + else if( psz_a == NULL && psz_b != NULL ) \ + i_test = 1; \ + else if( psz_a != NULL && psz_b == NULL ) \ + i_test = -1;\ + /* No meta, sort by name */ \ + else if( psz_a == NULL && psz_b == NULL ) \ + { \ + META_STRCASECMP_NAME( ); \ + } \ + else \ + { \ + if( !integer ) i_test = strcmp( psz_a, psz_b ); \ + else i_test = atoi( psz_a ) - atoi( psz_b ); \ + } \ + free( psz_a ); \ + free( psz_b ); \ +} +#define DO_META_SORT( node ) DO_META_SORT_ADV( node, false ) + + int i_test = 0; + + if( sort_mode == SORT_TITLE ) + { + META_STRCASECMP_NAME( ); + } + else if( sort_mode == SORT_TITLE_NUMERIC ) + { + char *psz_i = input_item_GetName( (*(playlist_item_t **)first)->p_input ); + char *psz_ismall = + input_item_GetName( (*(playlist_item_t **)second)->p_input ); + i_test = atoi( psz_i ) - atoi( psz_ismall ); + free( psz_i ); + free( psz_ismall ); + } + else if( sort_mode == SORT_DURATION ) + { + i_test = input_item_GetDuration( (*(playlist_item_t **)first)->p_input ) - + input_item_GetDuration( (*(playlist_item_t **)second)->p_input ); + } + else if( sort_mode == SORT_ARTIST ) + { + DO_META_SORT( Artist ); + /* sort by artist, album, tracknumber */ + if( i_test == 0 ) + DO_META_SORT( Album ); + if( i_test == 0 ) + DO_META_SORT_ADV( TrackNumber, true ); + } + else if( sort_mode == SORT_GENRE ) + { + DO_META_SORT( Genre ); + } + else if( sort_mode == SORT_ALBUM ) + { + DO_META_SORT( Album ); + /* Sort by tracknumber if albums are the same */ + if( i_test == 0 ) + DO_META_SORT_ADV( TrackNumber, true ); + } + else if( sort_mode == SORT_TRACK_NUMBER ) + { + DO_META_SORT_ADV( TrackNumber, true ); + } + else if( sort_mode == SORT_DESCRIPTION ) + { + DO_META_SORT( Description ); + } + else if( sort_mode == SORT_ID ) + { + i_test = (*(playlist_item_t **)first)->i_id - (*(playlist_item_t **)second)->i_id; + } + else if( sort_mode == SORT_TITLE_NODES_FIRST ) + { + /* Alphabetic sort, all nodes first */ + + if( (*(playlist_item_t **)first)->i_children == -1 && + (*(playlist_item_t **)second)->i_children >= 0 ) + { + i_test = 1; + } + else if( (*(playlist_item_t **)first)->i_children >= 0 && + (*(playlist_item_t **)second)->i_children == -1 ) + { + i_test = -1; + } + else + { + i_test = strcasecmp( (*(playlist_item_t **)first)->p_input->psz_name, + (*(playlist_item_t **)second)->p_input->psz_name ); + } + } + + if ( sort_type == ORDER_REVERSE ) + i_test = i_test * -1; +#undef DO_META_SORT +#undef DO_META_SORT_ADV + + return i_test; +} diff --git a/VLC/stats.c b/VLC/stats.c new file mode 100644 index 0000000..8d0b6bd --- /dev/null +++ b/VLC/stats.c @@ -0,0 +1,621 @@ +/***************************************************************************** + * stats.c: Statistics handling + ***************************************************************************** + * Copyright (C) 2006 the VideoLAN team + * $Id$ + * + * Authors: Clément Stenac + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include /* required */ + +#include "input/input_internal.h" + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +static int CounterUpdate( vlc_object_t *p_this, + counter_t *p_counter, + vlc_value_t val, vlc_value_t * ); +static void TimerDump( vlc_object_t *p_this, counter_t *p_counter, bool); + +/***************************************************************************** + * Exported functions + *****************************************************************************/ + +/** + * Create a statistics counter + * \param p_this a VLC object + * \param i_type the type of stored data. One of VLC_VAR_STRING, + * VLC_VAR_INTEGER, VLC_VAR_FLOAT + * \param i_compute_type the aggregation type. One of STATS_LAST (always + * keep the last value), STATS_COUNTER (increment by the passed value), + * STATS_MAX (keep the maximum passed value), STATS_MIN, or STATS_DERIVATIVE + * (keep a time derivative of the value) + */ +counter_t * __stats_CounterCreate( vlc_object_t *p_this, + int i_type, int i_compute_type ) +{ + counter_t *p_counter = (counter_t*) malloc( sizeof( counter_t ) ) ; + (void)p_this; + + if( !p_counter ) return NULL; + p_counter->i_compute_type = i_compute_type; + p_counter->i_type = i_type; + p_counter->i_samples = 0; + p_counter->pp_samples = NULL; + p_counter->psz_name = NULL; + + p_counter->update_interval = 0; + p_counter->last_update = 0; + + return p_counter; +} + +/** Update a counter element with new values + * \param p_this a VLC object + * \param p_counter the counter to update + * \param val the vlc_value union containing the new value to aggregate. For + * more information on how data is aggregated, \see __stats_Create + * \param val_new a pointer that will be filled with new data + */ +int __stats_Update( vlc_object_t *p_this, counter_t *p_counter, + vlc_value_t val, vlc_value_t *val_new ) +{ + if( !libvlc_stats (p_this) || !p_counter ) return VLC_EGENERIC; + return CounterUpdate( p_this, p_counter, val, val_new ); +} + +/** Get the aggregated value for a counter + * \param p_this an object + * \param p_counter the counter + * \param val a pointer to an initialized vlc_value union. It will contain the + * retrieved value + * \return an error code + */ +int __stats_Get( vlc_object_t *p_this, counter_t *p_counter, vlc_value_t *val ) +{ + if( !libvlc_stats (p_this) || !p_counter || p_counter->i_samples == 0 ) + { + val->i_int = val->f_float = 0.0; + return VLC_EGENERIC; + } + + switch( p_counter->i_compute_type ) + { + case STATS_LAST: + case STATS_MIN: + case STATS_MAX: + case STATS_COUNTER: + *val = p_counter->pp_samples[0]->value; + break; + case STATS_DERIVATIVE: + /* Not ready yet */ + if( p_counter->i_samples < 2 ) + { + val->i_int = 0; val->f_float = 0.0; + return VLC_EGENERIC; + } + if( p_counter->i_type == VLC_VAR_INTEGER ) + { + float f = ( p_counter->pp_samples[0]->value.i_int - + p_counter->pp_samples[1]->value.i_int ) / + (float)( p_counter->pp_samples[0]->date - + p_counter->pp_samples[1]->date ); + val->i_int = (int)f; + } + else + { + float f = (float)( p_counter->pp_samples[0]->value.f_float - + p_counter->pp_samples[1]->value.f_float ) / + (float)( p_counter->pp_samples[0]->date - + p_counter->pp_samples[1]->date ); + val->f_float = f; + } + break; + } + return VLC_SUCCESS;; +} + +input_stats_t *stats_NewInputStats( input_thread_t *p_input ) +{ + (void)p_input; + input_stats_t *p_stats = malloc( sizeof(input_stats_t) ); + + if( !p_stats ) + return NULL; + + memset( p_stats, 0, sizeof(*p_stats) ); + vlc_mutex_init( &p_stats->lock ); + stats_ReinitInputStats( p_stats ); + + return p_stats; +} + +void stats_ComputeInputStats( input_thread_t *p_input, input_stats_t *p_stats ) +{ + if( !libvlc_stats (p_input) ) return; + + vlc_mutex_lock( &p_input->p->counters.counters_lock ); + vlc_mutex_lock( &p_stats->lock ); + + /* Input */ + stats_GetInteger( p_input, p_input->p->counters.p_read_packets, + &p_stats->i_read_packets ); + stats_GetInteger( p_input, p_input->p->counters.p_read_bytes, + &p_stats->i_read_bytes ); + stats_GetFloat( p_input, p_input->p->counters.p_input_bitrate, + &p_stats->f_input_bitrate ); + stats_GetInteger( p_input, p_input->p->counters.p_demux_read, + &p_stats->i_demux_read_bytes ); + stats_GetFloat( p_input, p_input->p->counters.p_demux_bitrate, + &p_stats->f_demux_bitrate ); + + /* Decoders */ + stats_GetInteger( p_input, p_input->p->counters.p_decoded_video, + &p_stats->i_decoded_video ); + stats_GetInteger( p_input, p_input->p->counters.p_decoded_audio, + &p_stats->i_decoded_audio ); + + /* Sout */ + if( p_input->p->counters.p_sout_send_bitrate ) + { + stats_GetInteger( p_input, p_input->p->counters.p_sout_sent_packets, + &p_stats->i_sent_packets ); + stats_GetInteger( p_input, p_input->p->counters.p_sout_sent_bytes, + &p_stats->i_sent_bytes ); + stats_GetFloat ( p_input, p_input->p->counters.p_sout_send_bitrate, + &p_stats->f_send_bitrate ); + } + + /* Aout */ + stats_GetInteger( p_input, p_input->p->counters.p_played_abuffers, + &p_stats->i_played_abuffers ); + stats_GetInteger( p_input, p_input->p->counters.p_lost_abuffers, + &p_stats->i_lost_abuffers ); + + /* Vouts */ + stats_GetInteger( p_input, p_input->p->counters.p_displayed_pictures, + &p_stats->i_displayed_pictures ); + stats_GetInteger( p_input, p_input->p->counters.p_lost_pictures, + &p_stats->i_lost_pictures ); + + vlc_mutex_unlock( &p_stats->lock ); + vlc_mutex_unlock( &p_input->p->counters.counters_lock ); +} + +void stats_ReinitInputStats( input_stats_t *p_stats ) +{ + vlc_mutex_lock( &p_stats->lock ); + p_stats->i_read_packets = p_stats->i_read_bytes = + p_stats->f_input_bitrate = p_stats->f_average_input_bitrate = + p_stats->i_demux_read_packets = p_stats->i_demux_read_bytes = + p_stats->f_demux_bitrate = p_stats->f_average_demux_bitrate = + p_stats->i_displayed_pictures = p_stats->i_lost_pictures = + p_stats->i_played_abuffers = p_stats->i_lost_abuffers = + p_stats->i_decoded_video = p_stats->i_decoded_audio = + p_stats->i_sent_bytes = p_stats->i_sent_packets = p_stats->f_send_bitrate + = 0; + vlc_mutex_unlock( &p_stats->lock ); +} + +void stats_DumpInputStats( input_stats_t *p_stats ) +{ + vlc_mutex_lock( &p_stats->lock ); + /* f_bitrate is in bytes / microsecond + * *1000 => bytes / millisecond => kbytes / seconds */ + fprintf( stderr, "Input : %i (%i bytes) - %f kB/s - " + "Demux : %i (%i bytes) - %f kB/s\n" + " - Vout : %i/%i - Aout : %i/%i - Sout : %f\n", + p_stats->i_read_packets, p_stats->i_read_bytes, + p_stats->f_input_bitrate * 1000, + p_stats->i_demux_read_packets, p_stats->i_demux_read_bytes, + p_stats->f_demux_bitrate * 1000, + p_stats->i_displayed_pictures, p_stats->i_lost_pictures, + p_stats->i_played_abuffers, p_stats->i_lost_abuffers, + p_stats->f_send_bitrate ); + vlc_mutex_unlock( &p_stats->lock ); +} + +void __stats_ComputeGlobalStats( vlc_object_t *p_obj, global_stats_t *p_stats ) +{ + vlc_list_t *p_list; + int i_index; + + if( !libvlc_stats (p_obj) ) return; + + vlc_mutex_lock( &p_stats->lock ); + + p_list = vlc_list_find( p_obj, VLC_OBJECT_INPUT, FIND_ANYWHERE ); + if( p_list ) + { + float f_total_in = 0, f_total_out = 0,f_total_demux = 0; + for( i_index = 0; i_index < p_list->i_count ; i_index ++ ) + { + float f_in = 0, f_out = 0, f_demux = 0; + input_thread_t *p_input = (input_thread_t *) + p_list->p_values[i_index].p_object; + vlc_mutex_lock( &p_input->p->counters.counters_lock ); + stats_GetFloat( p_obj, p_input->p->counters.p_input_bitrate, &f_in ); + if( p_input->p->counters.p_sout_send_bitrate ) + stats_GetFloat( p_obj, p_input->p->counters.p_sout_send_bitrate, + &f_out ); + stats_GetFloat( p_obj, p_input->p->counters.p_demux_bitrate, + &f_demux ); + vlc_mutex_unlock( &p_input->p->counters.counters_lock ); + f_total_in += f_in; f_total_out += f_out;f_total_demux += f_demux; + } + p_stats->f_input_bitrate = f_total_in; + p_stats->f_output_bitrate = f_total_out; + p_stats->f_demux_bitrate = f_total_demux; + vlc_list_release( p_list ); + } + + vlc_mutex_unlock( &p_stats->lock ); +} + +void __stats_TimerStart( vlc_object_t *p_obj, const char *psz_name, + unsigned int i_id ) +{ + libvlc_priv_t *priv = libvlc_priv (p_obj->p_libvlc); + counter_t *p_counter = NULL; + + if( !priv->b_stats ) return; + + vlc_mutex_lock( &priv->timer_lock ); + + for( int i = 0 ; i < priv->i_timers; i++ ) + { + if( priv->pp_timers[i]->i_id == i_id + && priv->pp_timers[i]->p_obj == p_obj ) + { + p_counter = priv->pp_timers[i]; + break; + } + } + if( !p_counter ) + { + counter_sample_t *p_sample; + p_counter = stats_CounterCreate( p_obj->p_libvlc, VLC_VAR_TIME, + STATS_TIMER ); + if( !p_counter ) + goto out; + p_counter->psz_name = strdup( psz_name ); + p_counter->i_id = i_id; + p_counter->p_obj = p_obj; + INSERT_ELEM( priv->pp_timers, priv->i_timers, + priv->i_timers, p_counter ); + + /* 1st sample : if started: start_date, else last_time, b_started */ + p_sample = (counter_sample_t *)malloc( sizeof( counter_sample_t ) ); + INSERT_ELEM( p_counter->pp_samples, p_counter->i_samples, + p_counter->i_samples, p_sample ); + p_sample->date = 0; p_sample->value.b_bool = 0; + /* 2nd sample : global_time, i_samples */ + p_sample = (counter_sample_t *)malloc( sizeof( counter_sample_t ) ); + INSERT_ELEM( p_counter->pp_samples, p_counter->i_samples, + p_counter->i_samples, p_sample ); + p_sample->date = 0; p_sample->value.i_int = 0; + } + if( p_counter->pp_samples[0]->value.b_bool == true ) + { + msg_Warn( p_obj, "timer '%s' was already started !", psz_name ); + goto out; + } + p_counter->pp_samples[0]->value.b_bool = true; + p_counter->pp_samples[0]->date = mdate(); +out: + vlc_mutex_unlock( &priv->timer_lock ); +} + +void __stats_TimerStop( vlc_object_t *p_obj, unsigned int i_id ) +{ + counter_t *p_counter = NULL; + libvlc_priv_t *priv = libvlc_priv (p_obj->p_libvlc); + + if( !priv->b_stats ) return; + vlc_mutex_lock( &priv->timer_lock ); + for( int i = 0 ; i < priv->i_timers; i++ ) + { + if( priv->pp_timers[i]->i_id == i_id + && priv->pp_timers[i]->p_obj == p_obj ) + { + p_counter = priv->pp_timers[i]; + break; + } + } + if( !p_counter || p_counter->i_samples != 2 ) + { + msg_Err( p_obj, "timer does not exist" ); + goto out; + } + p_counter->pp_samples[0]->value.b_bool = false; + p_counter->pp_samples[1]->value.i_int += 1; + p_counter->pp_samples[0]->date = mdate() - p_counter->pp_samples[0]->date; + p_counter->pp_samples[1]->date += p_counter->pp_samples[0]->date; +out: + vlc_mutex_unlock( &priv->timer_lock ); +} + +void __stats_TimerDump( vlc_object_t *p_obj, unsigned int i_id ) +{ + counter_t *p_counter = NULL; + libvlc_priv_t *priv = libvlc_priv (p_obj->p_libvlc); + + if( !priv->b_stats ) return; + vlc_mutex_lock( &priv->timer_lock ); + for( int i = 0 ; i < priv->i_timers; i++ ) + { + if( priv->pp_timers[i]->i_id == i_id + && priv->pp_timers[i]->p_obj == p_obj ) + { + p_counter = priv->pp_timers[i]; + break; + } + } + TimerDump( p_obj, p_counter, true ); + vlc_mutex_unlock( &priv->timer_lock ); +} + +void __stats_TimersDumpAll( vlc_object_t *p_obj ) +{ + libvlc_priv_t *priv = libvlc_priv (p_obj->p_libvlc); + + if( !priv->b_stats ) return; + vlc_mutex_lock( &priv->timer_lock ); + for ( int i = 0 ; i < priv->i_timers ; i++ ) + TimerDump( p_obj, priv->pp_timers[i], false ); + vlc_mutex_unlock( &priv->timer_lock ); +} + +void __stats_TimerClean( vlc_object_t *p_obj, unsigned int i_id ) +{ + libvlc_priv_t *priv = libvlc_priv (p_obj->p_libvlc); + + vlc_mutex_lock( &priv->timer_lock ); + for ( int i = priv->i_timers -1 ; i >= 0; i-- ) + { + counter_t *p_counter = priv->pp_timers[i]; + if( p_counter->i_id == i_id && p_counter->p_obj == p_obj ) + { + REMOVE_ELEM( priv->pp_timers, priv->i_timers, i ); + stats_CounterClean( p_counter ); + } + } + vlc_mutex_unlock( &priv->timer_lock ); +} + +void __stats_TimersCleanAll( vlc_object_t *p_obj ) +{ + libvlc_priv_t *priv = libvlc_priv (p_obj->p_libvlc); + + vlc_mutex_lock( &priv->timer_lock ); + for ( int i = priv->i_timers -1 ; i >= 0; i-- ) + { + counter_t *p_counter = priv->pp_timers[i]; + REMOVE_ELEM( priv->pp_timers, priv->i_timers, i ); + stats_CounterClean( p_counter ); + } + vlc_mutex_unlock( &priv->timer_lock ); +} + +void stats_CounterClean( counter_t *p_c ) +{ + if( p_c ) + { + int i = p_c->i_samples - 1 ; + while( i >= 0 ) + { + counter_sample_t *p_s = p_c->pp_samples[i]; + REMOVE_ELEM( p_c->pp_samples, p_c->i_samples, i ); + free( p_s ); + i--; + } + free( p_c->psz_name ); + free( p_c ); + } +} + + +/******************************************************************** + * Following functions are local + ********************************************************************/ + +/** + * Update a statistics counter, according to its type + * If needed, perform a bit of computation (derivative, mostly) + * This function must be entered with stats handler lock + * \param p_counter the counter to update + * \param val the "new" value + * \return an error code + */ +static int CounterUpdate( vlc_object_t *p_handler, + counter_t *p_counter, + vlc_value_t val, vlc_value_t *new_val ) +{ + switch( p_counter->i_compute_type ) + { + case STATS_LAST: + case STATS_MIN: + case STATS_MAX: + if( p_counter->i_samples > 1) + { + msg_Err( p_handler, "LAST counter has several samples !" ); + return VLC_EGENERIC; + } + if( p_counter->i_type != VLC_VAR_FLOAT && + p_counter->i_type != VLC_VAR_INTEGER && + p_counter->i_compute_type != STATS_LAST ) + { + msg_Err( p_handler, "unable to compute MIN or MAX for this type"); + return VLC_EGENERIC; + } + + if( p_counter->i_samples == 0 ) + { + counter_sample_t *p_new = (counter_sample_t*)malloc( + sizeof( counter_sample_t ) ); + p_new->value.psz_string = NULL; + + INSERT_ELEM( p_counter->pp_samples, p_counter->i_samples, + p_counter->i_samples, p_new ); + } + if( p_counter->i_samples == 1 ) + { + /* Update if : LAST or (MAX and bigger) or (MIN and bigger) */ + if( p_counter->i_compute_type == STATS_LAST || + ( p_counter->i_compute_type == STATS_MAX && + ( ( p_counter->i_type == VLC_VAR_INTEGER && + p_counter->pp_samples[0]->value.i_int > val.i_int ) || + ( p_counter->i_type == VLC_VAR_FLOAT && + p_counter->pp_samples[0]->value.f_float > val.f_float ) + ) ) || + ( p_counter->i_compute_type == STATS_MIN && + ( ( p_counter->i_type == VLC_VAR_INTEGER && + p_counter->pp_samples[0]->value.i_int < val.i_int ) || + ( p_counter->i_type == VLC_VAR_FLOAT && + p_counter->pp_samples[0]->value.f_float < val.f_float ) + ) ) ) + { + if( p_counter->i_type == VLC_VAR_STRING && + p_counter->pp_samples[0]->value.psz_string ) + { + free( p_counter->pp_samples[0]->value.psz_string ); + } + p_counter->pp_samples[0]->value = val; + *new_val = p_counter->pp_samples[0]->value; + } + } + break; + case STATS_DERIVATIVE: + { + counter_sample_t *p_new, *p_old; + mtime_t now = mdate(); + if( now - p_counter->last_update < p_counter->update_interval ) + { + return VLC_EGENERIC; + } + p_counter->last_update = now; + if( p_counter->i_type != VLC_VAR_FLOAT && + p_counter->i_type != VLC_VAR_INTEGER ) + { + msg_Err( p_handler, "Unable to compute DERIVATIVE for this type"); + return VLC_EGENERIC; + } + /* Insert the new one at the beginning */ + p_new = (counter_sample_t*)malloc( sizeof( counter_sample_t ) ); + p_new->value = val; + p_new->date = p_counter->last_update; + INSERT_ELEM( p_counter->pp_samples, p_counter->i_samples, + 0, p_new ); + + if( p_counter->i_samples == 3 ) + { + p_old = p_counter->pp_samples[2]; + REMOVE_ELEM( p_counter->pp_samples, p_counter->i_samples, 2 ); + free( p_old ); + } + break; + } + case STATS_COUNTER: + if( p_counter->i_samples > 1) + { + msg_Err( p_handler, "LAST counter has several samples !" ); + return VLC_EGENERIC; + } + if( p_counter->i_samples == 0 ) + { + counter_sample_t *p_new = (counter_sample_t*)malloc( + sizeof( counter_sample_t ) ); + p_new->value.psz_string = NULL; + + INSERT_ELEM( p_counter->pp_samples, p_counter->i_samples, + p_counter->i_samples, p_new ); + } + if( p_counter->i_samples == 1 ) + { + switch( p_counter->i_type ) + { + case VLC_VAR_INTEGER: + p_counter->pp_samples[0]->value.i_int += val.i_int; + if( new_val ) + new_val->i_int = p_counter->pp_samples[0]->value.i_int; + break; + case VLC_VAR_FLOAT: + p_counter->pp_samples[0]->value.f_float += val.f_float; + if( new_val ) + new_val->f_float = p_counter->pp_samples[0]->value.f_float; + default: + msg_Err( p_handler, "Trying to increment invalid variable %s", + p_counter->psz_name ); + return VLC_EGENERIC; + } + } + break; + } + return VLC_SUCCESS; +} + +static void TimerDump( vlc_object_t *p_obj, counter_t *p_counter, + bool b_total ) +{ + if( !p_counter ) + return; + + mtime_t last, total; + int i_total; + if( p_counter->i_samples != 2 ) + { + msg_Err( p_obj, "timer %s does not exist", p_counter->psz_name ); + return; + } + i_total = p_counter->pp_samples[1]->value.i_int; + total = p_counter->pp_samples[1]->date; + if( p_counter->pp_samples[0]->value.b_bool == true ) + { + last = mdate() - p_counter->pp_samples[0]->date; + i_total += 1; + total += last; + } + else + { + last = p_counter->pp_samples[0]->date; + } + if( b_total ) + { + msg_Dbg( p_obj, + "TIMER %s : %.3f ms - Total %.3f ms / %i intvls (Avg %.3f ms)", + p_counter->psz_name, (float)last/1000, (float)total/1000, i_total, + (float)(total)/(1000*(float)i_total ) ); + } + else + { + msg_Dbg( p_obj, + "TIMER %s : Total %.3f ms / %i intvls (Avg %.3f ms)", + p_counter->psz_name, (float)total/1000, i_total, + (float)(total)/(1000*(float)i_total ) ); + } +} diff --git a/VLC/stream_output/announce.c b/VLC/stream_output/announce.c new file mode 100644 index 0000000..36e8586 --- /dev/null +++ b/VLC/stream_output/announce.c @@ -0,0 +1,234 @@ +/***************************************************************************** + * announce.c : announce handler + ***************************************************************************** + * Copyright (C) 2002-2007 the VideoLAN team + * $Id$ + * + * Authors: Clément Stenac + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "vlc_sout.h" +#include "stream_output.h" + +#include + +/* Private functions for the announce handler */ +static announce_handler_t* announce_HandlerCreate( vlc_object_t *); +static int announce_Register( announce_handler_t *p_announce, + session_descriptor_t *p_session, + announce_method_t *p_method ); +static int announce_UnRegister( announce_handler_t *p_announce, + session_descriptor_t *p_session ); + +struct announce_method_t +{ +} sap_method; + +/**************************************************************************** + * Sout-side functions + ****************************************************************************/ + +/** + * Register a new session with the announce handler, using a pregenerated SDP + * + * \param p_sout a sout instance structure + * \param psz_sdp the SDP to register + * \param psz_dst session address (needed for SAP address auto detection) + * \param p_method an announce method descriptor + * \return the new session descriptor structure + */ +session_descriptor_t * +sout_AnnounceRegisterSDP( sout_instance_t *p_sout, const char *psz_sdp, + const char *psz_dst, announce_method_t *p_method ) +{ + session_descriptor_t *p_session; + announce_handler_t *p_announce = (announce_handler_t*) + vlc_object_find( p_sout, + VLC_OBJECT_ANNOUNCE, + FIND_ANYWHERE ); + if( !p_announce ) + { + msg_Dbg( p_sout, "no announce handler found, creating one" ); + p_announce = announce_HandlerCreate( VLC_OBJECT( p_sout ) ); + if( !p_announce ) + { + msg_Err( p_sout, "Creation failed" ); + return NULL; + } + vlc_object_yield( p_announce ); + } + + p_session = malloc( sizeof( *p_session ) ); + memset( p_session, 0, sizeof( *p_session ) ); + p_session->psz_sdp = strdup( psz_sdp ); + + /* GRUIK. We should not convert back-and-forth from string to numbers */ + struct addrinfo *res; + if (vlc_getaddrinfo (VLC_OBJECT (p_sout), psz_dst, 0, NULL, &res) == 0) + { + if (res->ai_addrlen <= sizeof (p_session->addr)) + memcpy (&p_session->addr, res->ai_addr, + p_session->addrlen = res->ai_addrlen); + vlc_freeaddrinfo (res); + } + + announce_Register( p_announce, p_session, p_method ); + + vlc_object_release( p_announce ); + return p_session; +} + +/** + * UnRegister an existing session + * + * \param p_sout a sout instance structure + * \param p_session the session descriptor + * \return VLC_SUCCESS or an error + */ +int sout_AnnounceUnRegister( sout_instance_t *p_sout, + session_descriptor_t *p_session ) +{ + int i_ret; + announce_handler_t *p_announce = (announce_handler_t*) + vlc_object_find( p_sout, + VLC_OBJECT_ANNOUNCE, + FIND_ANYWHERE ); + if( !p_announce ) + { + msg_Dbg( p_sout, "unable to remove announce: no announce handler" ); + return VLC_ENOOBJ; + } + i_ret = announce_UnRegister( p_announce, p_session ); + if( i_ret == 0 ) + free( p_session ); + + vlc_object_release( p_announce ); + + return i_ret; +} + +/** + * \return the SAP announce method + */ +announce_method_t * sout_SAPMethod (void) +{ + return &sap_method; +} + +void sout_MethodRelease (announce_method_t *m) +{ + assert (m == &sap_method); +} + +/************************************************************************ + * Announce handler functions (private) + ************************************************************************/ + +/** + * Create the announce handler object + * + * \param p_this a vlc_object structure + * \return the new announce handler or NULL on error + */ +static announce_handler_t *announce_HandlerCreate( vlc_object_t *p_this ) +{ + announce_handler_t *p_announce; + + p_announce = vlc_object_create( p_this, VLC_OBJECT_ANNOUNCE ); + + if( !p_announce ) + return NULL; + + p_announce->p_sap = NULL; + vlc_object_attach( p_announce, p_this->p_libvlc); + + return p_announce; +} + +/** + * Destroy a announce handler object + * + * \param p_announce the announce handler to destroy + * \return VLC_SUCCESS or an error + */ +int announce_HandlerDestroy( announce_handler_t *p_announce ) +{ + if( p_announce->p_sap ) + { + /* Exit the SAP */ + vlc_object_release( p_announce->p_sap ); + } + + /* Free the structure */ + vlc_object_release( p_announce ); + + return VLC_SUCCESS; +} + +/* Register an announce */ +static int announce_Register( announce_handler_t *p_announce, + session_descriptor_t *p_session, + announce_method_t *p_method ) +{ + if (p_method == NULL) + return VLC_EGENERIC; + + msg_Dbg( p_announce, "registering announce"); + if( p_method == &sap_method ) + { + /* Do we already have a SAP announce handler ? */ + if( !p_announce->p_sap ) + { + sap_handler_t *p_sap = announce_SAPHandlerCreate( p_announce ); + msg_Dbg( p_announce, "creating SAP announce handler"); + if( !p_sap ) + { + msg_Err( p_announce, "SAP handler creation failed" ); + return VLC_ENOOBJ; + } + p_announce->p_sap = p_sap; + } + /* this will set p_session->p_sap for later deletion */ + msg_Dbg( p_announce, "adding SAP session"); + p_announce->p_sap->pf_add( p_announce->p_sap, p_session ); + } + else + { + msg_Err( p_announce, "announce type unsupported" ); + return VLC_EGENERIC; + } + return VLC_SUCCESS; +} + + +/* Unregister an announce */ +static int announce_UnRegister( announce_handler_t *p_announce, + session_descriptor_t *p_session ) +{ + msg_Dbg( p_announce, "unregistering announce" ); + if( p_announce->p_sap ) + p_announce->p_sap->pf_del( p_announce->p_sap, p_session ); + return VLC_SUCCESS; +} diff --git a/VLC/stream_output/sap.c b/VLC/stream_output/sap.c new file mode 100644 index 0000000..1ae8642 --- /dev/null +++ b/VLC/stream_output/sap.c @@ -0,0 +1,633 @@ +/***************************************************************************** + * sap.c : SAP announce handler + ***************************************************************************** + * Copyright (C) 2002-2007 the VideoLAN team + * $Id$ + * + * Authors: Clément Stenac + * Rémi Denis-Courmont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#include /* free() */ +#include /* sprintf() */ +#include +#include /* tolower(), isxdigit() */ +#include + +#include "vlc_sout.h" +#include "vlc_network.h" +#include "vlc_charset.h" + +#include "stream_output.h" +#include "libvlc.h" + +/* SAP is always on that port */ +#define SAP_PORT 9875 + +#define DEFAULT_PORT "1234" + +#undef EXTRA_DEBUG + +/* SAP Specific structures */ + +/* 100ms */ +#define SAP_IDLE ((mtime_t)(0.100*CLOCK_FREQ)) +#define SAP_MAX_BUFFER 65534 +#define MIN_INTERVAL 2 +#define MAX_INTERVAL 300 + +/* A SAP announce address. For each of these, we run the + * control flow algorithm */ +struct sap_address_t +{ + char *psz_address; + struct sockaddr_storage orig; + socklen_t origlen; + int i_rfd; /* Read socket */ + int i_wfd; /* Write socket */ + + /* Used for flow control */ + mtime_t t1; + bool b_enabled; + bool b_ready; + int i_interval; + int i_buff; + int i_limit; +}; + +/* A SAP session descriptor, enqueued in the SAP handler queue */ +struct sap_session_t { + uint8_t *psz_data; + unsigned i_length; + sap_address_t *p_address; + session_descriptor_t *p_sd; + + /* Last and next send */ + mtime_t i_last; + mtime_t i_next; +}; + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +static void * RunThread( vlc_object_t *p_this); +static int ComputeRate( sap_address_t *p_address ); + +static int announce_SendSAPAnnounce( sap_handler_t *p_sap, + sap_session_t *p_session ); + + +static int announce_SAPAnnounceAdd( sap_handler_t *p_sap, + session_descriptor_t *p_session ); + +static int announce_SAPAnnounceDel( sap_handler_t *p_sap, + session_descriptor_t *p_session ); + +static void announce_SAPHandlerDestructor( vlc_object_t *p_this ); + + +/** + * Create the SAP handler + * + * \param p_announce the parent announce_handler + * \return the newly created SAP handler or NULL on error + */ +sap_handler_t *announce_SAPHandlerCreate( announce_handler_t *p_announce ) +{ + sap_handler_t *p_sap; + + p_sap = vlc_custom_create( VLC_OBJECT(p_announce), sizeof( sap_handler_t ), + VLC_OBJECT_ANNOUNCE, "announce" ); + if( !p_sap ) + return NULL; + + p_sap->psz_object_name = strdup( "sap announcer" ); + + p_sap->pf_add = announce_SAPAnnounceAdd; + p_sap->pf_del = announce_SAPAnnounceDel; + + p_sap->i_sessions = 0; + p_sap->i_addresses = 0; + p_sap->i_current_session = 0; + + p_sap->b_control = config_GetInt( p_sap, "sap-flow-control"); + + if( vlc_thread_create( p_sap, "sap handler", RunThread, + VLC_THREAD_PRIORITY_LOW, false ) ) + { + msg_Dbg( p_announce, "unable to spawn SAP handler thread"); + vlc_object_release( p_sap ); + return NULL; + } + + vlc_object_set_destructor( p_sap, announce_SAPHandlerDestructor ); + + msg_Dbg( p_announce, "thread created, %i sessions", p_sap->i_sessions); + + return p_sap; +} + +static void announce_SAPHandlerDestructor( vlc_object_t * p_this ) +{ + sap_handler_t *p_sap = (sap_handler_t *)p_this; + int i; + + /* Free the remaining sessions */ + for( i = 0 ; i< p_sap->i_sessions ; i++) + { + sap_session_t *p_session = p_sap->pp_sessions[i]; + FREENULL( p_session->psz_data ); + REMOVE_ELEM( p_sap->pp_sessions, p_sap->i_sessions , i ); + FREENULL( p_session ); + } + + /* Free the remaining addresses */ + for( i = 0 ; i< p_sap->i_addresses ; i++) + { + sap_address_t *p_address = p_sap->pp_addresses[i]; + FREENULL( p_address->psz_address ); + if( p_address->i_rfd > -1 ) + { + net_Close( p_address->i_rfd ); + } + if( p_address->i_wfd > -1 && p_sap->b_control ) + { + net_Close( p_address->i_wfd ); + } + REMOVE_ELEM( p_sap->pp_addresses, p_sap->i_addresses, i ); + FREENULL( p_address ); + } +} + +/** + * main SAP handler thread + * \param p_this the SAP Handler object + * \return nothing + */ +static void * RunThread( vlc_object_t *p_this) +{ + sap_handler_t *p_sap = (sap_handler_t*)p_this; + sap_session_t *p_session; + + while( !p_sap->b_die ) + { + int i; + + msleep( SAP_IDLE ); + + /* If needed, get the rate info */ + if( p_sap->b_control == true ) + { + for( i = 0 ; i< p_sap->i_addresses ; i++) + { + if( p_sap->pp_addresses[i]->b_enabled == true ) + { + ComputeRate( p_sap->pp_addresses[i] ); + } + } + } + + /* Find the session to announce */ + vlc_object_lock( p_sap ); + if( p_sap->i_sessions > p_sap->i_current_session + 1) + { + p_sap->i_current_session++; + } + else if( p_sap->i_sessions > 0) + { + p_sap->i_current_session = 0; + } + else + { + vlc_object_unlock( p_sap ); + continue; + } + p_session = p_sap->pp_sessions[p_sap->i_current_session]; + + /* And announce it */ + if( p_session->p_address->b_enabled == true && + p_session->p_address->b_ready == true ) + { + announce_SendSAPAnnounce( p_sap, p_session ); + } + vlc_object_unlock( p_sap ); + } + return NULL; +} + +/* Add a SAP announce */ +static int announce_SAPAnnounceAdd( sap_handler_t *p_sap, + session_descriptor_t *p_session ) +{ + int i; + char psz_addr[NI_MAXNUMERICHOST]; + bool b_ipv6 = false, b_ssm = false; + sap_session_t *p_sap_session; + mtime_t i_hash; + struct sockaddr_storage addr; + socklen_t addrlen; + + vlc_object_lock( p_sap ); + addrlen = p_session->addrlen; + if ((addrlen == 0) || (addrlen > sizeof (addr))) + { + vlc_object_unlock( p_sap ); + msg_Err( p_sap, "No/invalid address specified for SAP announce" ); + return VLC_EGENERIC; + } + + /* Determine SAP multicast address automatically */ + memcpy (&addr, &p_session->addr, addrlen); + + switch( p_session->addr.ss_family ) + { +#if defined (HAVE_INET_PTON) || defined (WIN32) + case AF_INET6: + { + /* See RFC3513 for list of valid IPv6 scopes */ + struct in6_addr *a6 = &((struct sockaddr_in6 *)&addr)->sin6_addr; + + memcpy( a6->s6_addr + 2, "\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x02\x7f\xfe", 14 ); + if( IN6_IS_ADDR_MULTICAST( a6 ) ) + { + /* SSM <=> ff3x::/32 */ + b_ssm = (U32_AT (a6->s6_addr) & 0xfff0ffff) == 0xff300000; + + /* force flags to zero, preserve scope */ + a6->s6_addr[1] &= 0xf; + } + else + /* Unicast IPv6 - assume global scope */ + memcpy( a6->s6_addr, "\xff\x0e", 2 ); + + b_ipv6 = true; + break; + } +#endif + + case AF_INET: + { + /* See RFC2365 for IPv4 scopes */ + uint32_t ipv4; + + ipv4 = ntohl( ((struct sockaddr_in *)&addr)->sin_addr.s_addr ); + /* 224.0.0.0/24 => 224.0.0.255 */ + if ((ipv4 & 0xffffff00) == 0xe0000000) + ipv4 = 0xe00000ff; + else + /* 239.255.0.0/16 => 239.255.255.255 */ + if ((ipv4 & 0xffff0000) == 0xefff0000) + ipv4 = 0xefffffff; + else + /* 239.192.0.0/14 => 239.195.255.255 */ + if ((ipv4 & 0xfffc0000) == 0xefc00000) + ipv4 = 0xefc3ffff; + else + if ((ipv4 & 0xff000000) == 0xef000000) + ipv4 = 0; + else + /* other addresses => 224.2.127.254 */ + { + /* SSM: 232.0.0.0/8 */ + b_ssm = (ipv4 >> 24) == 232; + ipv4 = 0xe0027ffe; + } + + if( ipv4 == 0 ) + { + msg_Err( p_sap, "Out-of-scope multicast address " + "not supported by SAP" ); + vlc_object_unlock( p_sap ); + return VLC_EGENERIC; + } + + ((struct sockaddr_in *)&addr)->sin_addr.s_addr = htonl( ipv4 ); + break; + } + + default: + vlc_object_unlock( p_sap ); + msg_Err( p_sap, "Address family %d not supported by SAP", + addr.ss_family ); + return VLC_EGENERIC; + } + + i = vlc_getnameinfo( (struct sockaddr *)&addr, addrlen, + psz_addr, sizeof( psz_addr ), NULL, NI_NUMERICHOST ); + + if( i ) + { + vlc_object_unlock( p_sap ); + msg_Err( p_sap, "%s", vlc_gai_strerror( i ) ); + return VLC_EGENERIC; + } + + msg_Dbg( p_sap, "using SAP address: %s", psz_addr); + + /* XXX: Check for dupes */ + p_sap_session = (sap_session_t*)malloc(sizeof(sap_session_t)); + p_sap_session->p_sd = p_session; + p_sap_session->p_address = NULL; + + /* Add the address to the buffer */ + for( i = 0; i < p_sap->i_addresses; i++) + { + if( !strcmp( psz_addr, p_sap->pp_addresses[i]->psz_address ) ) + { + p_sap_session->p_address = p_sap->pp_addresses[i]; + break; + } + } + + if( p_sap_session->p_address == NULL ) + { + sap_address_t *p_address = (sap_address_t *) + malloc( sizeof(sap_address_t) ); + if( !p_address ) + { + vlc_object_unlock( p_sap ); + return VLC_ENOMEM; + } + p_address->psz_address = strdup( psz_addr ); + p_address->i_wfd = net_ConnectUDP( VLC_OBJECT(p_sap), psz_addr, SAP_PORT, 255 ); + if( p_address->i_wfd != -1 ) + { + shutdown( p_address->i_wfd, SHUT_RD ); + p_address->origlen = sizeof (p_address->orig); + getsockname (p_address->i_wfd, (struct sockaddr *)&p_address->orig, + &p_address->origlen); + } + + if( p_sap->b_control == true ) + { + p_address->i_rfd = net_ListenUDP1( (vlc_object_t*)p_sap, psz_addr, SAP_PORT ); + if( p_address->i_rfd != -1 ) + shutdown( p_address->i_rfd, SHUT_WR ); + p_address->i_buff = 0; + p_address->b_enabled = true; + p_address->b_ready = false; + p_address->i_limit = 10000; /* 10000 bps */ + p_address->t1 = 0; + } + else + { + p_address->b_enabled = true; + p_address->b_ready = true; + p_address->i_interval = config_GetInt( p_sap,"sap-interval"); + p_address->i_rfd = -1; + } + + if( p_address->i_wfd == -1 || (p_address->i_rfd == -1 + && p_sap->b_control ) ) + { + msg_Warn( p_sap, "disabling address" ); + p_address->b_enabled = false; + } + + INSERT_ELEM( p_sap->pp_addresses, + p_sap->i_addresses, + p_sap->i_addresses, + p_address ); + p_sap_session->p_address = p_address; + } + + memcpy (&p_session->orig, &p_sap_session->p_address->orig, + p_session->origlen = p_sap_session->p_address->origlen); + + size_t headsize = 20; + switch (p_session->orig.ss_family) + { +#ifdef AF_INET6 + case AF_INET6: + headsize += 16; + break; +#endif + case AF_INET: + headsize += 4; + break; + default: + msg_Err( p_sap, "Address family %d not supported by SAP", + addr.ss_family ); + vlc_object_unlock( p_sap ); + return VLC_EGENERIC; + } + + /* If needed, build the SDP */ + assert( p_session->psz_sdp != NULL ); + + p_sap_session->i_last = 0; + p_sap_session->i_length = headsize + strlen (p_session->psz_sdp); + p_sap_session->psz_data = malloc (p_sap_session->i_length + 1); + if (p_sap_session->psz_data == NULL) + { + free (p_session->psz_sdp); + vlc_object_unlock( p_sap ); + return VLC_ENOMEM; + } + + /* Build the SAP Headers */ + uint8_t *psz_head = p_sap_session->psz_data; + + /* SAPv1, not encrypted, not compressed */ + psz_head[0] = 0x20; + psz_head[1] = 0x00; /* No authentification length */ + + i_hash = mdate(); + psz_head[2] = i_hash >> 8; /* Msg id hash */ + psz_head[3] = i_hash; /* Msg id hash 2 */ + + headsize = 4; + switch (p_session->orig.ss_family) + { +#ifdef AF_INET6 + case AF_INET6: + { + struct in6_addr *a6 = + &((struct sockaddr_in6 *)&p_session->orig)->sin6_addr; + memcpy (psz_head + headsize, a6, 16); + psz_head[0] |= 0x10; /* IPv6 flag */ + headsize += 16; + break; + } +#endif + case AF_INET: + { + uint32_t ipv4 = + (((struct sockaddr_in *)&p_session->orig)->sin_addr.s_addr); + memcpy (psz_head + headsize, &ipv4, 4); + headsize += 4; + break; + } + + } + + memcpy (psz_head + headsize, "application/sdp", 16); + headsize += 16; + + /* Build the final message */ + strcpy( (char *)psz_head + headsize, p_session->psz_sdp); + + /* Enqueue the announce */ + INSERT_ELEM( p_sap->pp_sessions, + p_sap->i_sessions, + p_sap->i_sessions, + p_sap_session ); + msg_Dbg( p_sap,"%i addresses, %i sessions", + p_sap->i_addresses,p_sap->i_sessions); + + vlc_object_unlock( p_sap ); + + return VLC_SUCCESS; +} + +/* Remove a SAP Announce */ +static int announce_SAPAnnounceDel( sap_handler_t *p_sap, + session_descriptor_t *p_session ) +{ + int i; + vlc_object_lock( p_sap ); + + msg_Dbg( p_sap, "removing session %p from SAP", p_session); + + /* Dequeue the announce */ + for( i = 0; i< p_sap->i_sessions; i++) + { + if( p_session == p_sap->pp_sessions[i]->p_sd ) + { + free( p_session->psz_sdp ); + sap_session_t *p_mysession = p_sap->pp_sessions[i]; + REMOVE_ELEM( p_sap->pp_sessions, + p_sap->i_sessions, + i ); + + free( p_mysession->psz_data ); + free( p_mysession ); + break; + } + } + + /* XXX: Dequeue the address too if it is not used anymore + * TODO: - address refcount + - send a SAP deletion packet */ + + msg_Dbg( p_sap,"%i announcements remaining", p_sap->i_sessions ); + + vlc_object_unlock( p_sap ); + + return VLC_SUCCESS; +} + +static int announce_SendSAPAnnounce( sap_handler_t *p_sap, + sap_session_t *p_session ) +{ + int i_ret; + + /* This announce has never been sent yet */ + if( p_session->i_last == 0 ) + { + p_session->i_next = mdate()+ p_session->p_address->i_interval*1000000; + p_session->i_last = 1; + return VLC_SUCCESS; + } + + if( p_session->i_next < mdate() ) + { +#ifdef EXTRA_DEBUG + msg_Dbg( p_sap, "sending announce"); +#endif + i_ret = net_Write( p_sap, p_session->p_address->i_wfd, NULL, + p_session->psz_data, + p_session->i_length ); + if( i_ret != (int)p_session->i_length ) + { + msg_Warn( p_sap, "SAP send failed on address %s (%i %i)", + p_session->p_address->psz_address, + i_ret, p_session->i_length ); + } + p_session->i_last = p_session->i_next; + p_session->i_next = p_session->i_last + + p_session->p_address->i_interval*1000000; + } + return VLC_SUCCESS; +} + +static int ComputeRate( sap_address_t *p_address ) +{ + uint8_t buffer[SAP_MAX_BUFFER]; + ssize_t i_tot = 0; + mtime_t i_temp; + int i_rate; + + if( p_address->t1 == 0 ) + { + p_address->t1 = mdate(); + return VLC_SUCCESS; + } + for (;;) + { + /* Might be too slow if we have huge data */ + ssize_t i_read = recv( p_address->i_rfd, buffer, SAP_MAX_BUFFER, 0 ); + if (i_read == -1) + break; + i_tot += i_read; + } + + i_temp = mdate(); + + /* We calculate the rate every 5 seconds */ + if( i_temp - p_address->t1 < 5000000 ) + { + p_address->i_buff += i_tot; + return VLC_SUCCESS; + } + + /* Bits/second */ + i_rate = (int)(8*1000000*((mtime_t)p_address->i_buff + (mtime_t)i_tot ) / + (i_temp - p_address->t1 )); + + p_address->i_limit = 10000; + + p_address->i_interval = ((1000*i_rate / p_address->i_limit) * + (MAX_INTERVAL - MIN_INTERVAL))/1000 + MIN_INTERVAL; + + if( p_address->i_interval > MAX_INTERVAL || p_address->i_interval < 0 ) + { + p_address->i_interval = MAX_INTERVAL; + } +#ifdef EXTRA_DEBUG + msg_Dbg( p_sap,"%s:%i: rate=%i, interval = %i s", + p_address->psz_address,SAP_PORT, i_rate, p_address->i_interval ); +#endif + + p_address->b_ready = true; + + p_address->t1 = i_temp; + p_address->i_buff = 0; + + return VLC_SUCCESS; +} diff --git a/VLC/stream_output/sdp.c b/VLC/stream_output/sdp.c new file mode 100644 index 0000000..036de10 --- /dev/null +++ b/VLC/stream_output/sdp.c @@ -0,0 +1,305 @@ +/***************************************************************************** + * sdp.c : SDP creation helpers + ***************************************************************************** + * Copyright © 2007 Rémi Denis-Courmont + * $Id$ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#include +#include +#include +#include +#include +#include "vlc_network.h" +#include "vlc_charset.h" + +#include "stream_output.h" + +#define MAXSDPADDRESS 47 + +static +char *AddressToSDP (const struct sockaddr *addr, socklen_t addrlen, char *buf) +{ + if (addrlen < offsetof (struct sockaddr, sa_family) + + sizeof (addr->sa_family)) + return NULL; + + strcpy (buf, "IN IP* "); + + if (vlc_getnameinfo (addr, addrlen, buf + 7, MAXSDPADDRESS - 7, NULL, + NI_NUMERICHOST)) + return NULL; + + switch (addr->sa_family) + { + case AF_INET: + { + if (net_SockAddrIsMulticast (addr, addrlen)) + strcat (buf, "/255"); // obsolete in RFC4566, dummy value + buf[5] = '4'; + break; + } + +#ifdef AF_INET6 + case AF_INET6: + { + char *ptr = strchr (buf, '%'); + if (ptr != NULL) + *ptr = '\0'; // remove scope ID + buf[5] = '6'; + break; + } +#endif + + default: + return NULL; + } + + return buf; +} + + +static bool IsSDPString (const char *str) +{ + if (strchr (str, '\r') != NULL) + return false; + if (strchr (str, '\n') != NULL) + return false; + if (!IsUTF8 (str)) + return false; + return true; +} + + +static +char *sdp_Start (const char *name, const char *description, const char *url, + const char *email, const char *phone, + const struct sockaddr *src, size_t srclen, + const struct sockaddr *addr, size_t addrlen) +{ + uint64_t now = NTPtime64 (); + char *sdp; + char connection[MAXSDPADDRESS], hostname[256], + sfilter[MAXSDPADDRESS + sizeof ("\r\na=source-filter: incl * ")]; + const char *preurl = "\r\nu=", *premail = "\r\ne=", *prephone = "\r\np="; + + gethostname (hostname, sizeof (hostname)); + + if (name == NULL) + name = "Unnamed"; + if (description == NULL) + description = "N/A"; + if (url == NULL) + preurl = url = ""; + if (email == NULL) + premail = email = ""; + if (phone == NULL) + prephone = phone = ""; + + if (!IsSDPString (name) || !IsSDPString (description) + || !IsSDPString (url) || !IsSDPString (email) || !IsSDPString (phone) + || (AddressToSDP (addr, addrlen, connection) == NULL)) + return NULL; + + strcpy (sfilter, ""); + if (srclen > 0) + { + char machine[MAXSDPADDRESS]; + + if (AddressToSDP (src, srclen, machine) != NULL) + sprintf (sfilter, "\r\na=source-filter: incl IN IP%c * %s", + machine[5], machine + 7); + } + + if (asprintf (&sdp, "v=0" + "\r\no=- %"PRIu64" %"PRIu64" IN IP%c %s" + "\r\ns=%s" + "\r\ni=%s" + "%s%s" // optional URL + "%s%s" // optional email + "%s%s" // optional phone number + "\r\nc=%s" + // bandwidth not specified + "\r\nt=0 0" // one dummy time span + // no repeating + // no time zone adjustment (silly idea anyway) + // no encryption key (deprecated) + "\r\na=tool:"PACKAGE_STRING + "\r\na=recvonly" + "\r\na=type:broadcast" + "\r\na=charset:UTF-8" + "%s" // optional source filter + "\r\n", + /* o= */ now, now, connection[5], hostname, + /* s= */ name, + /* i= */ description, + /* u= */ preurl, url, + /* e= */ premail, email, + /* p= */ prephone, phone, + /* c= */ connection, + /* source-filter */ sfilter) == -1) + return NULL; + return sdp; +} + + +static char * +vsdp_AddAttribute (char **sdp, const char *name, const char *fmt, va_list ap) +{ + size_t oldlen = strlen (*sdp); + size_t addlen = sizeof ("a=\r\n") + strlen (name); + + if (fmt != NULL) + { + va_list aq; + + va_copy (aq, ap); + addlen += 1 + vsnprintf (NULL, 0, fmt, aq); + va_end (aq); + } + + char *ret = realloc (*sdp, oldlen + addlen); + if (ret == NULL) + return NULL; + + oldlen += sprintf (ret + oldlen, "a=%s", name); + if (fmt != NULL) + { + ret[oldlen++] = ':'; + oldlen += vsprintf (ret + oldlen, fmt, ap); + } + + strcpy (ret + oldlen, "\r\n"); + return *sdp = ret; +} + + +char *sdp_AddAttribute (char **sdp, const char *name, const char *fmt, ...) +{ + char *ret; + va_list ap; + + va_start (ap, fmt); + ret = vsdp_AddAttribute (sdp, name, fmt, ap); + va_end (ap); + + return ret; +} + + +char *sdp_AddMedia (char **sdp, + const char *type, const char *protocol, int dport, + unsigned pt, bool bw_indep, unsigned bw, + const char *ptname, unsigned clock, unsigned chans, + const char *fmtp) +{ + char *newsdp, *ptr; + size_t inlen = strlen (*sdp), outlen = inlen; + + /* Some default values */ + if (type == NULL) + type = "video"; + if (protocol == NULL) + protocol = "RTP/AVP"; + assert (pt < 128u); + + outlen += snprintf (NULL, 0, + "m=%s %u %s %d\r\n" + "b=TIAS:%u\r\n" + "b=RR:0\r\n", + type, dport, protocol, pt, bw); + + newsdp = realloc (*sdp, outlen + 1); + if (newsdp == NULL) + return NULL; + + *sdp = newsdp; + ptr = newsdp + inlen; + + ptr += sprintf (ptr, "m=%s %u %s %u\r\n", + type, dport, protocol, pt); + if (bw > 0) + ptr += sprintf (ptr, "b=%s:%u\r\n", bw_indep ? "TIAS" : "AS", bw); + ptr += sprintf (ptr, "b=RR:0\r\n"); + + /* RTP payload type map */ + if (ptname != NULL) + { + if ((strcmp (type, "audio") == 0) && (chans != 1)) + sdp_AddAttribute (sdp, "rtpmap", "%u %s/%u/%u", pt, ptname, clock, + chans); + else + sdp_AddAttribute (sdp, "rtpmap", "%u %s/%u", pt, ptname, clock); + } + /* Format parameters */ + if (fmtp != NULL) + sdp_AddAttribute (sdp, "fmtp", "%u %s", pt, fmtp); + + return newsdp; +} + + +char *vlc_sdp_Start (vlc_object_t *obj, const char *cfgpref, + const struct sockaddr *src, size_t srclen, + const struct sockaddr *addr, size_t addrlen) +{ + size_t cfglen = strlen (cfgpref); + if (cfglen > 100) + return NULL; + + char varname[cfglen + sizeof ("description")], *subvar = varname + cfglen; + strcpy (varname, cfgpref); + + strcpy (subvar, "name"); + char *name = var_GetNonEmptyString (obj, varname); + strcpy (subvar, "description"); + char *description = var_GetNonEmptyString (obj, varname); + strcpy (subvar, "url"); + char *url = var_GetNonEmptyString (obj, varname); + strcpy (subvar, "email"); + char *email = var_GetNonEmptyString (obj, varname); + strcpy (subvar, "phone"); + char *phone = var_GetNonEmptyString (obj, varname); + + char *sdp = sdp_Start (name, description, url, email, phone, + src, srclen, addr, addrlen); + free (name); + free (description); + free (url); + free (email); + free (phone); + + if (sdp == NULL) + return NULL; + + /* Totally non-standard */ + strcpy (subvar, "group"); + char *group = var_GetNonEmptyString (obj, varname); + if (group != NULL) + { + sdp_AddAttribute (&sdp, "x-plgroup", "%s", group); + free (group); + } + + return sdp; +} diff --git a/VLC/stream_output/stream_output.c b/VLC/stream_output/stream_output.c new file mode 100644 index 0000000..5d870bb --- /dev/null +++ b/VLC/stream_output/stream_output.c @@ -0,0 +1,874 @@ +/***************************************************************************** + * stream_output.c : stream output module + ***************************************************************************** + * Copyright (C) 2002-2007 the VideoLAN team + * $Id$ + * + * Authors: Christophe Massiot + * Laurent Aimar + * Eric Petit + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#include /* free() */ +#include /* sprintf() */ +#include + +#include "vlc_sout.h" + +#include "stream_output.h" + +#include "vlc_meta.h" + +#include "input_internal.h" + +#undef DEBUG_BUFFER +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +#define sout_stream_url_to_chain( p, s ) \ + _sout_stream_url_to_chain( VLC_OBJECT(p), s ) +static char *_sout_stream_url_to_chain( vlc_object_t *, const char * ); + +/* + * Generic MRL parser + * + */ + +typedef struct +{ + char *psz_access; + char *psz_way; + char *psz_name; +} mrl_t; + +/* mrl_Parse: parse psz_mrl and fill p_mrl */ +static int mrl_Parse( mrl_t *p_mrl, const char *psz_mrl ); +/* mrl_Clean: clean p_mrl after a call to mrl_Parse */ +static void mrl_Clean( mrl_t *p_mrl ); + +/***************************************************************************** + * sout_NewInstance: creates a new stream output instance + *****************************************************************************/ +sout_instance_t *__sout_NewInstance( vlc_object_t *p_parent, char * psz_dest ) +{ + static const char typename[] = "stream output"; + sout_instance_t *p_sout; + + /* *** Allocate descriptor *** */ + p_sout = vlc_custom_create( p_parent, sizeof( *p_sout ), + VLC_OBJECT_GENERIC, typename ); + if( p_sout == NULL ) + return NULL; + + /* *** init descriptor *** */ + p_sout->psz_sout = strdup( psz_dest ); + p_sout->p_meta = NULL; + p_sout->i_out_pace_nocontrol = 0; + p_sout->p_sys = NULL; + + vlc_mutex_init( &p_sout->lock ); + if( psz_dest && psz_dest[0] == '#' ) + { + p_sout->psz_chain = strdup( &psz_dest[1] ); + } + else + { + p_sout->psz_chain = sout_stream_url_to_chain( p_sout, psz_dest ); + msg_Dbg( p_sout, "using sout chain=`%s'", p_sout->psz_chain ); + } + p_sout->p_stream = NULL; + + /* attach it for inherit */ + vlc_object_attach( p_sout, p_parent ); + + /* */ + var_Create( p_sout, "sout-mux-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); + + /* */ + p_sout->p_stream = sout_StreamNew( p_sout, p_sout->psz_chain ); + if( p_sout->p_stream == NULL ) + { + msg_Err( p_sout, "stream chain failed for `%s'", p_sout->psz_chain ); + + FREENULL( p_sout->psz_sout ); + FREENULL( p_sout->psz_chain ); + + vlc_object_detach( p_sout ); + vlc_object_release( p_sout ); + return NULL; + } + + return p_sout; +} + +/***************************************************************************** + * sout_DeleteInstance: delete a previously allocated instance + *****************************************************************************/ +void sout_DeleteInstance( sout_instance_t * p_sout ) +{ + /* remove the stream out chain */ + sout_StreamDelete( p_sout->p_stream ); + + /* *** free all string *** */ + FREENULL( p_sout->psz_sout ); + FREENULL( p_sout->psz_chain ); + + /* delete meta */ + if( p_sout->p_meta ) + { + vlc_meta_Delete( p_sout->p_meta ); + } + + vlc_mutex_destroy( &p_sout->lock ); + + /* *** free structure *** */ + vlc_object_release( p_sout ); +} + +/***************************************************************************** + * + *****************************************************************************/ +void sout_UpdateStatistic( sout_instance_t *p_sout, sout_statistic_t i_type, int i_delta ) +{ + input_thread_t *p_input; + int i_bytes; /* That's pretty stupid to define it as an integer, it will overflow + really fast ... */ + + if( !libvlc_stats (p_sout) ) + return; + + /* FIXME that's ugly + * TODO add a private (ie not VLC_EXPORTed) input_UpdateStatistic for that */ + p_input = vlc_object_find( p_sout, VLC_OBJECT_INPUT, FIND_PARENT ); + if( !p_input || p_input->i_state == INIT_S || p_input->i_state == ERROR_S ) + return; + + switch( i_type ) + { +#define I(c) stats_UpdateInteger( p_input, p_input->p->counters.c, i_delta, NULL ) + case SOUT_STATISTIC_DECODED_VIDEO: + I(p_decoded_video); + break; + case SOUT_STATISTIC_DECODED_AUDIO: + I(p_decoded_audio); + break; + case SOUT_STATISTIC_DECODED_SUBTITLE: + I(p_decoded_sub); + break; +#if 0 + case SOUT_STATISTIC_ENCODED_VIDEO: + case SOUT_STATISTIC_ENCODED_AUDIO: + case SOUT_STATISTIC_ENCODED_SUBTITLE: + msg_Warn( p_sout, "Not yet supported statistic type %d", i_type ); + break; +#endif + + case SOUT_STATISTIC_SENT_PACKET: + I(p_sout_sent_packets); + break; +#undef I + case SOUT_STATISTIC_SENT_BYTE: + if( !stats_UpdateInteger( p_input, p_input->p->counters.p_sout_sent_bytes, i_delta, &i_bytes ) ) + stats_UpdateFloat( p_input, p_input->p->counters.p_sout_send_bitrate, i_bytes, NULL ); + break; + + default: + msg_Err( p_sout, "Invalid statistic type %d (internal error)", i_type ); + break; + } + vlc_object_release( p_input ); +} +/***************************************************************************** + * Packetizer/Input + *****************************************************************************/ +sout_packetizer_input_t *sout_InputNew( sout_instance_t *p_sout, + es_format_t *p_fmt ) +{ + sout_packetizer_input_t *p_input; + + /* *** create a packetizer input *** */ + p_input = malloc( sizeof( sout_packetizer_input_t ) ); + if( !p_input ) return NULL; + p_input->p_sout = p_sout; + p_input->p_fmt = p_fmt; + + msg_Dbg( p_sout, "adding a new sout input (sout_input:%p)", p_input ); + + if( p_fmt->i_codec == VLC_FOURCC( 'n', 'u', 'l', 'l' ) ) + { + vlc_object_release( p_sout ); + return p_input; + } + + /* *** add it to the stream chain */ + vlc_mutex_lock( &p_sout->lock ); + p_input->id = p_sout->p_stream->pf_add( p_sout->p_stream, p_fmt ); + vlc_mutex_unlock( &p_sout->lock ); + + if( p_input->id == NULL ) + { + free( p_input ); + return NULL; + } + + return( p_input ); +} + +/***************************************************************************** + * + *****************************************************************************/ +int sout_InputDelete( sout_packetizer_input_t *p_input ) +{ + sout_instance_t *p_sout = p_input->p_sout; + + msg_Dbg( p_sout, "removing a sout input (sout_input:%p)", p_input ); + + if( p_input->p_fmt->i_codec != VLC_FOURCC( 'n', 'u', 'l', 'l' ) ) + { + vlc_mutex_lock( &p_sout->lock ); + p_sout->p_stream->pf_del( p_sout->p_stream, p_input->id ); + vlc_mutex_unlock( &p_sout->lock ); + } + + free( p_input ); + + return( VLC_SUCCESS); +} + +/***************************************************************************** + * + *****************************************************************************/ +int sout_InputSendBuffer( sout_packetizer_input_t *p_input, + block_t *p_buffer ) +{ + sout_instance_t *p_sout = p_input->p_sout; + int i_ret; + + if( p_input->p_fmt->i_codec == VLC_FOURCC( 'n', 'u', 'l', 'l' ) ) + { + block_Release( p_buffer ); + return VLC_SUCCESS; + } + if( p_buffer->i_dts <= 0 ) + { + msg_Warn( p_sout, "trying to send non-dated packet to stream output!"); + block_Release( p_buffer ); + return VLC_SUCCESS; + } + + vlc_mutex_lock( &p_sout->lock ); + i_ret = p_sout->p_stream->pf_send( p_sout->p_stream, + p_input->id, p_buffer ); + vlc_mutex_unlock( &p_sout->lock ); + + return i_ret; +} + +/***************************************************************************** + * sout_AccessOutNew: allocate a new access out + *****************************************************************************/ +sout_access_out_t *sout_AccessOutNew( sout_instance_t *p_sout, + const char *psz_access, const char *psz_name ) +{ + static const char typename[] = "access out"; + sout_access_out_t *p_access; + char *psz_next; + + p_access = vlc_custom_create( p_sout, sizeof( *p_access ), + VLC_OBJECT_GENERIC, typename ); + if( !p_access ) + return NULL; + + psz_next = config_ChainCreate( &p_access->psz_access, &p_access->p_cfg, + psz_access ); + free( psz_next ); + p_access->psz_path = strdup( psz_name ? psz_name : "" ); + p_access->p_sout = p_sout; + p_access->p_sys = NULL; + p_access->pf_seek = NULL; + p_access->pf_read = NULL; + p_access->pf_write = NULL; + p_access->pf_control = NULL; + p_access->p_module = NULL; + + p_access->i_writes = 0; + p_access->i_sent_bytes = 0; + + vlc_object_attach( p_access, p_sout ); + + p_access->p_module = + module_Need( p_access, "sout access", p_access->psz_access, true ); + + if( !p_access->p_module ) + { + free( p_access->psz_access ); + free( p_access->psz_path ); + vlc_object_detach( p_access ); + vlc_object_release( p_access ); + return( NULL ); + } + + return p_access; +} +/***************************************************************************** + * sout_AccessDelete: delete an access out + *****************************************************************************/ +void sout_AccessOutDelete( sout_access_out_t *p_access ) +{ + vlc_object_detach( p_access ); + if( p_access->p_module ) + { + module_Unneed( p_access, p_access->p_module ); + } + free( p_access->psz_access ); + + config_ChainDestroy( p_access->p_cfg ); + + free( p_access->psz_path ); + + vlc_object_release( p_access ); +} + +/***************************************************************************** + * sout_AccessSeek: + *****************************************************************************/ +int sout_AccessOutSeek( sout_access_out_t *p_access, off_t i_pos ) +{ + return p_access->pf_seek( p_access, i_pos ); +} + +/***************************************************************************** + * sout_AccessRead: + *****************************************************************************/ +ssize_t sout_AccessOutRead( sout_access_out_t *p_access, block_t *p_buffer ) +{ + return( p_access->pf_read ? + p_access->pf_read( p_access, p_buffer ) : VLC_EGENERIC ); +} + +/***************************************************************************** + * sout_AccessWrite: + *****************************************************************************/ +ssize_t sout_AccessOutWrite( sout_access_out_t *p_access, block_t *p_buffer ) +{ + const unsigned i_packets_gather = 30; + p_access->i_writes++; + p_access->i_sent_bytes += p_buffer->i_buffer; + if( (p_access->i_writes % i_packets_gather) == 0 ) + { + sout_UpdateStatistic( p_access->p_sout, SOUT_STATISTIC_SENT_PACKET, i_packets_gather ); + sout_UpdateStatistic( p_access->p_sout, SOUT_STATISTIC_SENT_BYTE, p_access->i_sent_bytes ); + p_access->i_sent_bytes = 0; + } + return p_access->pf_write( p_access, p_buffer ); +} + +/** + * sout_AccessOutControl + */ +int sout_AccessOutControl (sout_access_out_t *access, int query, va_list args) +{ + return (access->pf_control) ? access->pf_control (access, query, args) + : VLC_EGENERIC; +} + +/***************************************************************************** + * sout_MuxNew: create a new mux + *****************************************************************************/ +sout_mux_t * sout_MuxNew( sout_instance_t *p_sout, char *psz_mux, + sout_access_out_t *p_access ) +{ + static const char typename[] = "mux"; + sout_mux_t *p_mux; + char *psz_next; + + p_mux = vlc_custom_create( p_sout, sizeof( *p_mux ), VLC_OBJECT_GENERIC, + typename); + if( p_mux == NULL ) + return NULL; + + p_mux->p_sout = p_sout; + psz_next = config_ChainCreate( &p_mux->psz_mux, &p_mux->p_cfg, psz_mux ); + free( psz_next ); + + p_mux->p_access = p_access; + p_mux->pf_control = NULL; + p_mux->pf_addstream = NULL; + p_mux->pf_delstream = NULL; + p_mux->pf_mux = NULL; + p_mux->i_nb_inputs = 0; + p_mux->pp_inputs = NULL; + + p_mux->p_sys = NULL; + p_mux->p_module = NULL; + + p_mux->b_add_stream_any_time = false; + p_mux->b_waiting_stream = true; + p_mux->i_add_stream_start = -1; + + vlc_object_attach( p_mux, p_sout ); + + p_mux->p_module = + module_Need( p_mux, "sout mux", p_mux->psz_mux, true ); + + if( p_mux->p_module == NULL ) + { + FREENULL( p_mux->psz_mux ); + + vlc_object_detach( p_mux ); + vlc_object_release( p_mux ); + return NULL; + } + + /* *** probe mux capacity *** */ + if( p_mux->pf_control ) + { + int b_answer = false; + + if( sout_MuxControl( p_mux, MUX_CAN_ADD_STREAM_WHILE_MUXING, + &b_answer ) ) + { + b_answer = false; + } + + if( b_answer ) + { + msg_Dbg( p_sout, "muxer support adding stream at any time" ); + p_mux->b_add_stream_any_time = true; + p_mux->b_waiting_stream = false; + + /* If we control the output pace then it's better to wait before + * starting muxing (generates better streams/files). */ + if( !p_sout->i_out_pace_nocontrol ) + { + b_answer = true; + } + else if( sout_MuxControl( p_mux, MUX_GET_ADD_STREAM_WAIT, + &b_answer ) ) + { + b_answer = false; + } + + if( b_answer ) + { + msg_Dbg( p_sout, "muxer prefers to wait for all ES before " + "starting to mux" ); + p_mux->b_waiting_stream = true; + } + } + } + + return p_mux; +} + +/***************************************************************************** + * sout_MuxDelete: + *****************************************************************************/ +void sout_MuxDelete( sout_mux_t *p_mux ) +{ + vlc_object_detach( p_mux ); + if( p_mux->p_module ) + { + module_Unneed( p_mux, p_mux->p_module ); + } + free( p_mux->psz_mux ); + + config_ChainDestroy( p_mux->p_cfg ); + + vlc_object_release( p_mux ); +} + +/***************************************************************************** + * sout_MuxAddStream: + *****************************************************************************/ +sout_input_t *sout_MuxAddStream( sout_mux_t *p_mux, es_format_t *p_fmt ) +{ + sout_input_t *p_input; + + if( !p_mux->b_add_stream_any_time && !p_mux->b_waiting_stream ) + { + msg_Err( p_mux, "cannot add a new stream (unsupported while muxing " + "to this format). You can try increasing sout-mux-caching value" ); + return NULL; + } + + msg_Dbg( p_mux, "adding a new input" ); + + /* create a new sout input */ + p_input = malloc( sizeof( sout_input_t ) ); + if( !p_input ) + return NULL; + p_input->p_sout = p_mux->p_sout; + p_input->p_fmt = p_fmt; + p_input->p_fifo = block_FifoNew(); + p_input->p_sys = NULL; + + TAB_APPEND( p_mux->i_nb_inputs, p_mux->pp_inputs, p_input ); + if( p_mux->pf_addstream( p_mux, p_input ) < 0 ) + { + msg_Err( p_mux, "cannot add this stream" ); + TAB_REMOVE( p_mux->i_nb_inputs, p_mux->pp_inputs, p_input ); + block_FifoRelease( p_input->p_fifo ); + free( p_input ); + return NULL; + } + + return p_input; +} + +/***************************************************************************** + * sout_MuxDeleteStream: + *****************************************************************************/ +void sout_MuxDeleteStream( sout_mux_t *p_mux, sout_input_t *p_input ) +{ + int i_index; + + if( p_mux->b_waiting_stream + && block_FifoCount( p_input->p_fifo ) > 0 ) + { + /* We stop waiting, and call the muxer for taking care of the data + * before we remove this es */ + p_mux->b_waiting_stream = false; + p_mux->pf_mux( p_mux ); + } + + TAB_FIND( p_mux->i_nb_inputs, p_mux->pp_inputs, p_input, i_index ); + if( i_index >= 0 ) + { + if( p_mux->pf_delstream( p_mux, p_input ) < 0 ) + { + msg_Err( p_mux, "cannot delete this stream from mux" ); + } + + /* remove the entry */ + TAB_REMOVE( p_mux->i_nb_inputs, p_mux->pp_inputs, p_input ); + + if( p_mux->i_nb_inputs == 0 ) + { + msg_Warn( p_mux, "no more input streams for this mux" ); + } + + block_FifoRelease( p_input->p_fifo ); + free( p_input ); + } +} + +/***************************************************************************** + * sout_MuxSendBuffer: + *****************************************************************************/ +void sout_MuxSendBuffer( sout_mux_t *p_mux, sout_input_t *p_input, + block_t *p_buffer ) +{ + block_FifoPut( p_input->p_fifo, p_buffer ); + + if( p_mux->p_sout->i_out_pace_nocontrol ) + { + mtime_t current_date = mdate(); + if ( current_date > p_buffer->i_dts ) + msg_Warn( p_mux, "late buffer for mux input (%"PRId64")", + current_date - p_buffer->i_dts ); + } + + if( p_mux->b_waiting_stream ) + { + const int64_t i_caching = var_GetInteger( p_mux->p_sout, "sout-mux-caching" ) * INT64_C(1000); + + if( p_mux->i_add_stream_start < 0 ) + p_mux->i_add_stream_start = p_buffer->i_dts; + + /* Wait until we have enought data before muxing */ + if( p_mux->i_add_stream_start < 0 || + p_buffer->i_dts < p_mux->i_add_stream_start + i_caching ) + return; + p_mux->b_waiting_stream = false; + } + p_mux->pf_mux( p_mux ); +} + +/***************************************************************************** + * + *****************************************************************************/ +static int mrl_Parse( mrl_t *p_mrl, const char *psz_mrl ) +{ + char * psz_dup = strdup( psz_mrl ); + char * psz_parser = psz_dup; + const char * psz_access; + const char * psz_way; + char * psz_name; + + /* *** first parse psz_dest */ + while( *psz_parser && *psz_parser != ':' ) + { + if( *psz_parser == '{' ) + { + while( *psz_parser && *psz_parser != '}' ) + { + psz_parser++; + } + if( *psz_parser ) + { + psz_parser++; + } + } + else + { + psz_parser++; + } + } +#if defined( WIN32 ) || defined( UNDER_CE ) + if( psz_parser - psz_dup == 1 ) + { + /* msg_Warn( p_sout, "drive letter %c: found in source string", + *psz_dup ) ; */ + psz_parser = ""; + } +#endif + + if( !*psz_parser ) + { + psz_access = psz_way = ""; + psz_name = psz_dup; + } + else + { + *psz_parser++ = '\0'; + + /* let's skip '//' */ + if( psz_parser[0] == '/' && psz_parser[1] == '/' ) + { + psz_parser += 2 ; + } + + psz_name = psz_parser ; + + /* Come back to parse the access and mux plug-ins */ + psz_parser = psz_dup; + + if( !*psz_parser ) + { + /* No access */ + psz_access = ""; + } + else if( *psz_parser == '/' ) + { + /* No access */ + psz_access = ""; + psz_parser++; + } + else + { + psz_access = psz_parser; + + while( *psz_parser && *psz_parser != '/' ) + { + if( *psz_parser == '{' ) + { + while( *psz_parser && *psz_parser != '}' ) + { + psz_parser++; + } + if( *psz_parser ) + { + psz_parser++; + } + } + else + { + psz_parser++; + } + } + + if( *psz_parser == '/' ) + { + *psz_parser++ = '\0'; + } + } + + if( !*psz_parser ) + { + /* No mux */ + psz_way = ""; + } + else + { + psz_way = psz_parser; + } + } + + p_mrl->psz_access = strdup( psz_access ); + p_mrl->psz_way = strdup( psz_way ); + p_mrl->psz_name = strdup( psz_name ); + + free( psz_dup ); + return( VLC_SUCCESS ); +} + + +/* mrl_Clean: clean p_mrl after a call to mrl_Parse */ +static void mrl_Clean( mrl_t *p_mrl ) +{ + FREENULL( p_mrl->psz_access ); + FREENULL( p_mrl->psz_way ); + FREENULL( p_mrl->psz_name ); +} + + +/**************************************************************************** + **************************************************************************** + ** + ** + ** + **************************************************************************** + ****************************************************************************/ + +/* create a complete chain */ +/* chain format: + module{option=*:option=*}[:module{option=*:...}] + */ + +/* + * parse module{options=str, option="str "}: + * return a pointer on the rest + * XXX: psz_chain is modified + */ + +/* + * XXX name and p_cfg are used (-> do NOT free them) + */ +sout_stream_t *sout_StreamNew( sout_instance_t *p_sout, char *psz_chain ) +{ + static const char typename[] = "stream out"; + sout_stream_t *p_stream; + + if( !psz_chain ) + { + msg_Err( p_sout, "invalid chain" ); + return NULL; + } + + p_stream = vlc_custom_create( p_sout, sizeof( *p_stream ), + VLC_OBJECT_GENERIC, typename ); + if( !p_stream ) + return NULL; + + p_stream->p_sout = p_sout; + p_stream->p_sys = NULL; + + p_stream->psz_next = + config_ChainCreate( &p_stream->psz_name, &p_stream->p_cfg, psz_chain); + + msg_Dbg( p_sout, "stream=`%s'", p_stream->psz_name ); + + vlc_object_attach( p_stream, p_sout ); + + p_stream->p_module = + module_Need( p_stream, "sout stream", p_stream->psz_name, true ); + + if( !p_stream->p_module ) + { + sout_StreamDelete( p_stream ); + return NULL; + } + + return p_stream; +} + +void sout_StreamDelete( sout_stream_t *p_stream ) +{ + msg_Dbg( p_stream, "destroying chain... (name=%s)", p_stream->psz_name ); + + vlc_object_detach( p_stream ); + if( p_stream->p_module ) module_Unneed( p_stream, p_stream->p_module ); + + FREENULL( p_stream->psz_name ); + FREENULL( p_stream->psz_next ); + + config_ChainDestroy( p_stream->p_cfg ); + + msg_Dbg( p_stream, "destroying chain done" ); + vlc_object_release( p_stream ); +} + +static char *_sout_stream_url_to_chain( vlc_object_t *p_this, + const char *psz_url ) +{ + mrl_t mrl; + char *psz_chain; + + mrl_Parse( &mrl, psz_url ); + + /* Check if the URLs goes to #rtp - otherwise we'll use #standard */ + static const char rtplist[] = "dccp\0sctp\0tcp\0udplite\0"; + for (const char *a = rtplist; *a; a += strlen (a) + 1) + if (strcmp (a, mrl.psz_access) == 0) + goto rtp; + + if (strcmp (mrl.psz_access, "rtp") == 0) + { + char *port; + /* For historical reasons, rtp:// means RTP over UDP */ + strcpy (mrl.psz_access, "udp"); +rtp: + if (mrl.psz_name[0] == '[') + { + port = strstr (mrl.psz_name, "]:"); + if (port != NULL) + port++; + } + else + port = strchr (mrl.psz_name, ':'); + if (port != NULL) + *port++ = '\0'; /* erase ':' */ + + if (asprintf (&psz_chain, + "rtp{mux=\"%s\",proto=\"%s\",dst=\"%s%s%s\"}", + mrl.psz_way, mrl.psz_access, mrl.psz_name, + port ? "\",port=\"" : "", port ? port : "") == -1) + psz_chain = NULL; + } + else + { + /* Convert the URL to a basic standard sout chain */ + if (asprintf (&psz_chain, + "standard{mux=\"%s\",access=\"%s\",dst=\"%s\"}", + mrl.psz_way, mrl.psz_access, mrl.psz_name) == -1) + psz_chain = NULL; + } + + /* Duplicate and wrap if sout-display is on */ + if (psz_chain && (config_GetInt( p_this, "sout-display" ) > 0)) + { + char *tmp; + if (asprintf (&tmp, "duplicate{dst=display,dst=%s}", tmp) == -1) + tmp = NULL; + free (psz_chain); + psz_chain = tmp; + } + + mrl_Clean( &mrl ); + return psz_chain; +} diff --git a/VLC/stream_output/stream_output.h b/VLC/stream_output/stream_output.h new file mode 100644 index 0000000..6d229b1 --- /dev/null +++ b/VLC/stream_output/stream_output.h @@ -0,0 +1,104 @@ +/***************************************************************************** + * stream_output.h : internal stream output + ***************************************************************************** + * Copyright (C) 2002-2005 the VideoLAN team + * $Id$ + * + * Authors: Christophe Massiot + * Laurent Aimar + * Eric Petit + * Jean-Paul Saman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + ***************************************************************************/ + +#if defined(__PLUGIN__) || defined(__BUILTIN__) || !defined(__LIBVLC__) + +#endif + +#ifndef VLC_SRC_STREAMOUT_H +# define VLC_SRC_STREAMOUT_H 1 + +# include "vlc_sout.h" +# include "vlc_network.h" + +/**************************************************************************** + * sout_packetizer_input_t: p_sout <-> p_packetizer + ****************************************************************************/ +struct sout_packetizer_input_t +{ + sout_instance_t *p_sout; + + es_format_t *p_fmt; + + sout_stream_id_t *id; +}; + +#define sout_NewInstance(a,b) __sout_NewInstance(VLC_OBJECT(a),b) +VLC_EXPORT( sout_instance_t *, __sout_NewInstance, ( vlc_object_t *, char * ) ); +VLC_EXPORT( void, sout_DeleteInstance, ( sout_instance_t * ) ); + +VLC_EXPORT( sout_packetizer_input_t *, sout_InputNew,( sout_instance_t *, es_format_t * ) ); +VLC_EXPORT( int, sout_InputDelete, ( sout_packetizer_input_t * ) ); +VLC_EXPORT( int, sout_InputSendBuffer, ( sout_packetizer_input_t *, block_t* ) ); + +/* Announce system */ + +/* The SAP handler, running in a separate thread */ +struct sap_handler_t +{ + VLC_COMMON_MEMBERS /* needed to create a thread */ + + sap_session_t **pp_sessions; + sap_address_t **pp_addresses; + + bool b_control; + + int i_sessions; + int i_addresses; + + int i_current_session; + + int (*pf_add) ( sap_handler_t*, session_descriptor_t *); + int (*pf_del) ( sap_handler_t*, session_descriptor_t *); + + /* private data, not in p_sys as there is one kind of sap_handler_t */ +}; + +struct session_descriptor_t +{ + struct sockaddr_storage orig; + socklen_t origlen; + struct sockaddr_storage addr; + socklen_t addrlen; + + char *psz_sdp; + bool b_ssm; +}; + +/* The main announce handler object */ +struct announce_handler_t +{ + VLC_COMMON_MEMBERS + + sap_handler_t *p_sap; +}; + +int announce_HandlerDestroy( announce_handler_t * ); + +/* Release it with vlc_object_release() */ +sap_handler_t *announce_SAPHandlerCreate( announce_handler_t *p_announce ); + +#endif diff --git a/VLC/strings.c b/VLC/strings.c new file mode 100644 index 0000000..44f8ec4 --- /dev/null +++ b/VLC/strings.c @@ -0,0 +1,1095 @@ +/***************************************************************************** + * strings.c: String related functions + ***************************************************************************** + * Copyright (C) 2006 the VideoLAN team + * $Id$ + * + * Authors: Antoine Cellerier + * Daniel Stranger + * Rémi Denis-Courmont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include + +/* Needed by str_format_time */ +#include + +/* Needed by str_format_meta */ +#include "vlc_input.h" +#include "vlc_meta.h" +#include "vlc_playlist.h" +#include "vlc_aout.h" + +#include "vlc_strings.h" +#include "vlc_url.h" +#include "vlc_charset.h" + +/** + * Unescape URI encoded string + * \return decoded duplicated string + */ +char *unescape_URI_duplicate( const char *psz ) +{ + char *psz_dup = strdup( psz ); + unescape_URI( psz_dup ); + return psz_dup; +} + +/** + * Unescape URI encoded string in place + * \return nothing + */ +void unescape_URI( char *psz ) +{ + unsigned char *in = (unsigned char *)psz, *out = in, c; + if( psz == NULL ) + return; + + while( ( c = *in++ ) != '\0' ) + { + switch( c ) + { + case '%': + { + char val[5], *pval = val; + unsigned long cp; + + switch( c = *in++ ) + { + case '\0': + return; + + case 'u': + case 'U': + if( ( *pval++ = *in++ ) == '\0' ) + return; + if( ( *pval++ = *in++ ) == '\0' ) + return; + c = *in++; + + default: + *pval++ = c; + if( ( *pval++ = *in++ ) == '\0' ) + return; + *pval = '\0'; + } + + cp = strtoul( val, NULL, 0x10 ); + if( cp < 0x80 ) + *out++ = cp; + else + if( cp < 0x800 ) + { + *out++ = (( cp >> 6) | 0xc0); + *out++ = (( cp & 0x3f) | 0x80); + } + else + { + assert( cp < 0x10000 ); + *out++ = (( cp >> 12) | 0xe0); + *out++ = (((cp >> 6) & 0x3f) | 0x80); + *out++ = (( cp & 0x3f) | 0x80); + } + break; + } + + /* + is not a special case - it means plus, not space. */ + + default: + /* Inserting non-ASCII or non-printable characters is unsafe, + * and no sane browser will send these unencoded */ + if( ( c < 32 ) || ( c > 127 ) ) + *out++ = '?'; + else + *out++ = c; + } + } + *out = '\0'; +} + +/** + * Decode encoded URI string + * \return decoded duplicated string + */ +char *decode_URI_duplicate( const char *psz ) +{ + char *psz_dup = strdup( psz ); + decode_URI( psz_dup ); + return psz_dup; +} + +/** + * Decode encoded URI string in place + * \return nothing + */ +void decode_URI( char *psz ) +{ + unsigned char *in = (unsigned char *)psz, *out = in, c; + if( psz == NULL ) + return; + + while( ( c = *in++ ) != '\0' ) + { + switch( c ) + { + case '%': + { + char hex[3]; + + if( ( ( hex[0] = *in++ ) == 0 ) + || ( ( hex[1] = *in++ ) == 0 ) ) + return; + + hex[2] = '\0'; + *out++ = (unsigned char)strtoul( hex, NULL, 0x10 ); + break; + } + + case '+': + *out++ = ' '; + break; + + default: + /* Inserting non-ASCII or non-printable characters is unsafe, + * and no sane browser will send these unencoded */ + if( ( c < 32 ) || ( c > 127 ) ) + *out++ = '?'; + else + *out++ = c; + } + } + *out = '\0'; + EnsureUTF8( psz ); +} + +static inline int isurlsafe( int c ) +{ + return ( (unsigned char)( c - 'a' ) < 26 ) + || ( (unsigned char)( c - 'A' ) < 26 ) + || ( (unsigned char)( c - '0' ) < 10 ) + /* Hmm, we should not encode character that are allowed in URLs + * (even if they are not URL-safe), nor URL-safe characters. + * We still encode some of them because of Microsoft's crap browser. + */ + || ( strchr( "-_.", c ) != NULL ); +} + +static inline char url_hexchar( int c ) +{ + return ( c < 10 ) ? c + '0' : c + 'A' - 10; +} + +/** + * encode_URI_component + * Encodes an URI component. + * + * @param psz_url nul-terminated UTF-8 representation of the component. + * Obviously, you can't pass an URI containing a nul character, but you don't + * want to do that, do you? + * + * @return encoded string (must be free()'d) + */ +char *encode_URI_component( const char *psz_url ) +{ + char psz_enc[3 * strlen( psz_url ) + 1], *out = psz_enc; + const uint8_t *in; + + for( in = (const uint8_t *)psz_url; *in; in++ ) + { + uint8_t c = *in; + + if( isurlsafe( c ) ) + *out++ = (char)c; + else + if ( c == ' ') + *out++ = '+'; + else + { + *out++ = '%'; + *out++ = url_hexchar( c >> 4 ); + *out++ = url_hexchar( c & 0xf ); + } + } + *out++ = '\0'; + + return strdup( psz_enc ); +} + +/** + * Converts "<", ">" and "&" to "<", ">" and "&" + * \param string to convert + */ +void resolve_xml_special_chars( char *psz_value ) +{ + char *p_pos = psz_value; + + while ( *psz_value ) + { + if( *psz_value == '&' ) + { +#define TRY_CHAR( src, len, dst ) \ + if( !strncmp( psz_value, src, len ) ) \ + { \ + *p_pos = dst; \ + psz_value += len; \ + } +#define TRY_LONGCHAR( src, len, dst ) \ + if( !strncmp( psz_value, src, len ) ) \ + { \ + strncpy( p_pos, dst, strlen( dst ) ); \ + p_pos += strlen( dst ) - 1; \ + psz_value += len; \ + } + TRY_CHAR( "<", 4, '<' ) + else TRY_CHAR( ">", 4, '>' ) + else TRY_CHAR( "&", 5, '&' ) + else TRY_CHAR( """, 6, '"' ) + else TRY_CHAR( "'", 6, '\'' ) + else if( psz_value[1] == '#' ) + { + char *psz_end; + int i = strtol( psz_value+2, &psz_end, 10 ); + if( *psz_end == ';' ) + { + if( i >= 32 && i <= 126 ) + { + *p_pos = (char)i; + psz_value = psz_end+1; + } + else + { + /* Unhandled code, FIXME */ + *p_pos = *psz_value; + psz_value++; + } + } + else + { + /* Invalid entity number */ + *p_pos = *psz_value; + psz_value++; + } + } + else TRY_LONGCHAR( "À", 8, "À" ) + else TRY_LONGCHAR( "Á", 8, "Á" ) + else TRY_LONGCHAR( "Â", 7, "Â" ) + else TRY_LONGCHAR( "Ã", 8, "Ã" ) + else TRY_LONGCHAR( "Ä", 6, "Ä" ) + else TRY_LONGCHAR( "Å", 7, "Å" ) + else TRY_LONGCHAR( "Æ", 7, "Æ" ) + else TRY_LONGCHAR( "Ç", 8, "Ç" ) + else TRY_LONGCHAR( "È", 8, "È" ) + else TRY_LONGCHAR( "É", 8, "É" ) + else TRY_LONGCHAR( "Ê", 7, "Ê" ) + else TRY_LONGCHAR( "Ë", 6, "Ë" ) + else TRY_LONGCHAR( "Ì", 8, "Ì" ) + else TRY_LONGCHAR( "Í", 8, "Í" ) + else TRY_LONGCHAR( "Î", 7, "Î" ) + else TRY_LONGCHAR( "Ï", 6, "Ï" ) + else TRY_LONGCHAR( "Ð", 5, "Ð" ) + else TRY_LONGCHAR( "Ñ", 8, "Ñ" ) + else TRY_LONGCHAR( "Ò", 8, "Ò" ) + else TRY_LONGCHAR( "Ó", 8, "Ó" ) + else TRY_LONGCHAR( "Ô", 7, "Ô" ) + else TRY_LONGCHAR( "Õ", 8, "Õ" ) + else TRY_LONGCHAR( "Ö", 6, "Ö" ) + else TRY_LONGCHAR( "Ø", 8, "Ø" ) + else TRY_LONGCHAR( "Ù", 8, "Ù" ) + else TRY_LONGCHAR( "Ú", 8, "Ú" ) + else TRY_LONGCHAR( "Û", 7, "Û" ) + else TRY_LONGCHAR( "Ü", 6, "Ü" ) + else TRY_LONGCHAR( "Ý", 8, "Ý" ) + else TRY_LONGCHAR( "Þ", 7, "Þ" ) + else TRY_LONGCHAR( "ß", 7, "ß" ) + else TRY_LONGCHAR( "à", 8, "à" ) + else TRY_LONGCHAR( "á", 8, "á" ) + else TRY_LONGCHAR( "â", 7, "â" ) + else TRY_LONGCHAR( "ã", 8, "ã" ) + else TRY_LONGCHAR( "ä", 6, "ä" ) + else TRY_LONGCHAR( "å", 7, "å" ) + else TRY_LONGCHAR( "æ", 7, "æ" ) + else TRY_LONGCHAR( "ç", 8, "ç" ) + else TRY_LONGCHAR( "è", 8, "è" ) + else TRY_LONGCHAR( "é", 8, "é" ) + else TRY_LONGCHAR( "ê", 7, "ê" ) + else TRY_LONGCHAR( "ë", 6, "ë" ) + else TRY_LONGCHAR( "ì", 8, "ì" ) + else TRY_LONGCHAR( "í", 8, "í" ) + else TRY_LONGCHAR( "î", 7, "î" ) + else TRY_LONGCHAR( "ï", 6, "ï" ) + else TRY_LONGCHAR( "ð", 5, "ð" ) + else TRY_LONGCHAR( "ñ", 8, "ñ" ) + else TRY_LONGCHAR( "ò", 8, "ò" ) + else TRY_LONGCHAR( "ó", 8, "ó" ) + else TRY_LONGCHAR( "ô", 7, "ô" ) + else TRY_LONGCHAR( "õ", 8, "õ" ) + else TRY_LONGCHAR( "ö", 6, "ö" ) + else TRY_LONGCHAR( "ø", 8, "ø" ) + else TRY_LONGCHAR( "ù", 8, "ù" ) + else TRY_LONGCHAR( "ú", 8, "ú" ) + else TRY_LONGCHAR( "û", 7, "û" ) + else TRY_LONGCHAR( "ü", 6, "ü" ) + else TRY_LONGCHAR( "ý", 8, "ý" ) + else TRY_LONGCHAR( "þ", 7, "þ" ) + else TRY_LONGCHAR( "ÿ", 6, "ÿ" ) + else TRY_LONGCHAR( "¡", 7, "¡" ) + else TRY_LONGCHAR( "¤", 8, "¤" ) + else TRY_LONGCHAR( "¢", 6, "¢" ) + else TRY_LONGCHAR( "£", 7, "£" ) + else TRY_LONGCHAR( "¥", 5, "¥" ) + else TRY_LONGCHAR( "¦", 8, "¦" ) + else TRY_LONGCHAR( "§", 6, "§" ) + else TRY_LONGCHAR( "¨", 5, "¨" ) + else TRY_LONGCHAR( "©", 6, "©" ) + else TRY_LONGCHAR( "ª", 6, "ª" ) + else TRY_LONGCHAR( "«", 7, "«" ) + else TRY_LONGCHAR( "¬", 5, "¬" ) + else TRY_LONGCHAR( "­", 5, "­" ) + else TRY_LONGCHAR( "®", 5, "®" ) + else TRY_LONGCHAR( "™", 7, "™" ) + else TRY_LONGCHAR( "¯", 6, "¯" ) + else TRY_LONGCHAR( "°", 5, "°" ) + else TRY_LONGCHAR( "±", 8, "±" ) + else TRY_LONGCHAR( "²", 6, "²" ) + else TRY_LONGCHAR( "³", 6, "³" ) + else TRY_LONGCHAR( "´", 7, "´" ) + else TRY_LONGCHAR( "µ", 7, "µ" ) + else TRY_LONGCHAR( "¶", 6, "¶" ) + else TRY_LONGCHAR( "·", 8, "·" ) + else TRY_LONGCHAR( "¸", 7, "¸" ) + else TRY_LONGCHAR( "¹", 6, "¹" ) + else TRY_LONGCHAR( "º", 6, "º" ) + else TRY_LONGCHAR( "»", 7, "»" ) + else TRY_LONGCHAR( "¼", 8, "¼" ) + else TRY_LONGCHAR( "½", 8, "½" ) + else TRY_LONGCHAR( "¾", 8, "¾" ) + else TRY_LONGCHAR( "¿", 8, "¿" ) + else TRY_LONGCHAR( "×", 7, "×" ) + else TRY_LONGCHAR( "÷", 8, "÷" ) + else TRY_LONGCHAR( "Œ", 7, "Œ" ) + else TRY_LONGCHAR( "œ", 7, "œ" ) + else TRY_LONGCHAR( "Š", 8, "Š" ) + else TRY_LONGCHAR( "š", 8, "š" ) + else TRY_LONGCHAR( "Ÿ", 6, "Ÿ" ) + else TRY_LONGCHAR( "ˆ", 6, "ˆ" ) + else TRY_LONGCHAR( "˜", 7, "˜" ) + else TRY_LONGCHAR( "–", 7, "–" ) + else TRY_LONGCHAR( "—", 7, "—" ) + else TRY_LONGCHAR( "‘", 7, "‘" ) + else TRY_LONGCHAR( "’", 7, "’" ) + else TRY_LONGCHAR( "‚", 7, "‚" ) + else TRY_LONGCHAR( "“", 7, "“" ) + else TRY_LONGCHAR( "”", 7, "”" ) + else TRY_LONGCHAR( "„", 7, "„" ) + else TRY_LONGCHAR( "†", 8, "†" ) + else TRY_LONGCHAR( "‡", 8, "‡" ) + else TRY_LONGCHAR( "…", 8, "…" ) + else TRY_LONGCHAR( "‰", 8, "‰" ) + else TRY_LONGCHAR( "‹", 8, "‹" ) + else TRY_LONGCHAR( "›", 8, "›" ) + else TRY_LONGCHAR( "€", 6, "€" ) + else + { + *p_pos = *psz_value; + psz_value++; + } + } + else + { + *p_pos = *psz_value; + psz_value++; + } + + p_pos++; + } + + *p_pos = '\0'; +} + +/** + * Converts '<', '>', '\"', '\'' and '&' to their html entities + * \param psz_content simple element content that is to be converted + */ +char *convert_xml_special_chars( const char *psz_content ) +{ + char *psz_temp = malloc( 6 * strlen( psz_content ) + 1 ); + const char *p_from = psz_content; + char *p_to = psz_temp; + + while ( *p_from ) + { + if ( *p_from == '<' ) + { + strcpy( p_to, "<" ); + p_to += 4; + } + else if ( *p_from == '>' ) + { + strcpy( p_to, ">" ); + p_to += 4; + } + else if ( *p_from == '&' ) + { + strcpy( p_to, "&" ); + p_to += 5; + } + else if( *p_from == '\"' ) + { + strcpy( p_to, """ ); + p_to += 6; + } + else if( *p_from == '\'' ) + { + strcpy( p_to, "'" ); + p_to += 6; + } + else + { + *p_to = *p_from; + p_to++; + } + p_from++; + } + *p_to = '\0'; + + return psz_temp; +} + +/* Base64 encoding */ +char *vlc_b64_encode_binary( const uint8_t *src, size_t i_src ) +{ + static const char b64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + char *ret = malloc( ( i_src + 4 ) * 4 / 3 ); + char *dst = ret; + + if( dst == NULL ) + return NULL; + + while( i_src > 0 ) + { + /* pops (up to) 3 bytes of input, push 4 bytes */ + uint32_t v; + + /* 1/3 -> 1/4 */ + v = *src++ << 24; + *dst++ = b64[v >> 26]; + v = v << 6; + + /* 2/3 -> 2/4 */ + if( i_src >= 2 ) + v |= *src++ << 22; + *dst++ = b64[v >> 26]; + v = v << 6; + + /* 3/3 -> 3/4 */ + if( i_src >= 3 ) + v |= *src++ << 20; // 3/3 + *dst++ = ( i_src >= 2 ) ? b64[v >> 26] : '='; // 3/4 + v = v << 6; + + /* -> 4/4 */ + *dst++ = ( i_src >= 3 ) ? b64[v >> 26] : '='; // 4/4 + + if( i_src <= 3 ) + break; + i_src -= 3; + } + + *dst = '\0'; + + return ret; +} + +char *vlc_b64_encode( const char *src ) +{ + if( src ) + return vlc_b64_encode_binary( (const uint8_t*)src, strlen(src) ); + else + return vlc_b64_encode_binary( (const uint8_t*)"", 0 ); +} + +/* Base64 decoding */ +size_t vlc_b64_decode_binary_to_buffer( uint8_t *p_dst, size_t i_dst, const char *p_src ) +{ + static const int b64[256] = { + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */ + 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */ + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */ + 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */ + -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */ + 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */ + }; + uint8_t *p_start = p_dst; + uint8_t *p = (uint8_t *)p_src; + + int i_level; + int i_last; + + for( i_level = 0, i_last = 0; (size_t)( p_dst - p_start ) < i_dst && *p != '\0'; p++ ) + { + const int c = b64[(unsigned int)*p]; + if( c == -1 ) + continue; + + switch( i_level ) + { + case 0: + i_level++; + break; + case 1: + *p_dst++ = ( i_last << 2 ) | ( ( c >> 4)&0x03 ); + i_level++; + break; + case 2: + *p_dst++ = ( ( i_last << 4 )&0xf0 ) | ( ( c >> 2 )&0x0f ); + i_level++; + break; + case 3: + *p_dst++ = ( ( i_last &0x03 ) << 6 ) | c; + i_level = 0; + } + i_last = c; + } + + return p_dst - p_start; +} +size_t vlc_b64_decode_binary( uint8_t **pp_dst, const char *psz_src ) +{ + const int i_src = strlen( psz_src ); + uint8_t *p_dst; + + *pp_dst = p_dst = malloc( i_src ); + if( !p_dst ) + return 0; + return vlc_b64_decode_binary_to_buffer( p_dst, i_src, psz_src ); +} +char *vlc_b64_decode( const char *psz_src ) +{ + const int i_src = strlen( psz_src ); + char *p_dst = malloc( i_src + 1 ); + size_t i_dst; + if( !p_dst ) + return NULL; + + i_dst = vlc_b64_decode_binary_to_buffer( (uint8_t*)p_dst, i_src, psz_src ); + p_dst[i_dst] = '\0'; + + return p_dst; +} + +/**************************************************************************** + * String formating functions + ****************************************************************************/ +char *str_format_time( const char *tformat ) +{ + char buffer[255]; + time_t curtime; + struct tm loctime; + + /* Get the current time. */ + curtime = time( NULL ); + + /* Convert it to local time representation. */ + localtime_r( &curtime, &loctime ); + strftime( buffer, 255, tformat, &loctime ); + return strdup( buffer ); +} + +#define INSERT_STRING( string ) \ + if( string != NULL ) \ + { \ + int len = strlen( string ); \ + dst = realloc( dst, i_size = i_size + len );\ + memcpy( (dst+d), string, len ); \ + d += len; \ + free( string ); \ + } \ + else \ + { \ + *(dst+d) = '-'; \ + d++; \ + } \ + +/* same than INSERT_STRING, except that string won't be freed */ +#define INSERT_STRING_NO_FREE( string ) \ + { \ + int len = strlen( string ); \ + dst = realloc( dst, i_size = i_size + len );\ + memcpy( dst+d, string, len ); \ + d += len; \ + } +char *__str_format_meta( vlc_object_t *p_object, const char *string ) +{ + const char *s = string; + int b_is_format = 0; + int b_empty_if_na = 0; + char buf[10]; + int i_size = strlen( string ) + 1; /* +1 to store '\0' */ + char *dst = strdup( string ); + if( !dst ) return NULL; + int d = 0; + + playlist_t *p_playlist = pl_Yield( p_object ); + input_thread_t *p_input = playlist_CurrentInput( p_playlist ); + input_item_t *p_item = NULL; + pl_Release( p_object ); + if( p_input ) + { + p_item = input_GetItem(p_input); + } + + while( *s ) + { + if( b_is_format ) + { + switch( *s ) + { + case 'a': + if( p_item ) + { + INSERT_STRING( input_item_GetArtist( p_item ) ); + } + break; + case 'b': + if( p_item ) + { + INSERT_STRING( input_item_GetAlbum( p_item ) ); + } + break; + case 'c': + if( p_item ) + { + INSERT_STRING( input_item_GetCopyright( p_item ) ); + } + break; + case 'd': + if( p_item ) + { + INSERT_STRING( input_item_GetDescription( p_item ) ); + } + break; + case 'e': + if( p_item ) + { + INSERT_STRING( input_item_GetEncodedBy( p_item ) ); + } + break; + case 'f': + if( p_item && p_item->p_stats ) + { + snprintf( buf, 10, "%d", + p_item->p_stats->i_displayed_pictures ); + } + else + { + sprintf( buf, b_empty_if_na ? "" : "-" ); + } + INSERT_STRING_NO_FREE( buf ); + break; + case 'g': + if( p_item ) + { + INSERT_STRING( input_item_GetGenre( p_item ) ); + } + break; + case 'l': + if( p_item ) + { + INSERT_STRING( input_item_GetLanguage( p_item ) ); + } + break; + case 'n': + if( p_item ) + { + INSERT_STRING( input_item_GetTrackNum( p_item ) ); + } + break; + case 'p': + if( p_item ) + { + INSERT_STRING( input_item_GetNowPlaying( p_item ) ); + } + break; + case 'r': + if( p_item ) + { + INSERT_STRING( input_item_GetRating( p_item ) ); + } + break; + case 's': + { + char *lang = NULL; + if( p_input ) + lang = var_GetNonEmptyString( p_input, "sub-language" ); + if( lang == NULL ) + lang = strdup( b_empty_if_na ? "" : "-" ); + INSERT_STRING( lang ); + break; + } + case 't': + if( p_item ) + { + INSERT_STRING( input_item_GetTitle( p_item ) ); + } + break; + case 'u': + if( p_item ) + { + INSERT_STRING( input_item_GetURL( p_item ) ); + } + break; + case 'A': + if( p_item ) + { + INSERT_STRING( input_item_GetDate( p_item ) ); + } + break; + case 'B': + if( p_input ) + { + snprintf( buf, 10, "%d", + var_GetInteger( p_input, "bit-rate" )/1000 ); + } + else + { + sprintf( buf, b_empty_if_na ? "" : "-" ); + } + INSERT_STRING_NO_FREE( buf ); + break; + case 'C': + if( p_input ) + { + snprintf( buf, 10, "%d", + var_GetInteger( p_input, "chapter" ) ); + } + else + { + sprintf( buf, b_empty_if_na ? "" : "-" ); + } + INSERT_STRING_NO_FREE( buf ); + break; + case 'D': + if( p_item ) + { + mtime_t i_duration = input_item_GetDuration( p_item ); + sprintf( buf, "%02d:%02d:%02d", + (int)(i_duration/(3600000000)), + (int)((i_duration/(60000000))%60), + (int)((i_duration/1000000)%60) ); + } + else + { + sprintf( buf, b_empty_if_na ? "" : "--:--:--" ); + } + INSERT_STRING_NO_FREE( buf ); + break; + case 'F': + if( p_item ) + { + INSERT_STRING( input_item_GetURI( p_item ) ); + } + break; + case 'I': + if( p_input ) + { + snprintf( buf, 10, "%d", + var_GetInteger( p_input, "title" ) ); + } + else + { + sprintf( buf, b_empty_if_na ? "" : "-" ); + } + INSERT_STRING_NO_FREE( buf ); + break; + case 'L': + if( p_item && p_input ) + { + mtime_t i_duration = input_item_GetDuration( p_item ); + int64_t i_time = p_input->i_time; + sprintf( buf, "%02d:%02d:%02d", + (int)( ( i_duration - i_time ) / 3600000000 ), + (int)( ( ( i_duration - i_time ) / 60000000 ) % 60 ), + (int)( ( ( i_duration - i_time ) / 1000000 ) % 60 ) ); + } + else + { + sprintf( buf, b_empty_if_na ? "" : "--:--:--" ); + } + INSERT_STRING_NO_FREE( buf ); + break; + case 'N': + if( p_item ) + { + INSERT_STRING( input_item_GetName( p_item ) ); + } + break; + case 'O': + { + char *lang = NULL; + if( p_input ) + lang = var_GetNonEmptyString( p_input, + "audio-language" ); + if( lang == NULL ) + lang = strdup( b_empty_if_na ? "" : "-" ); + INSERT_STRING( lang ); + break; + } + case 'P': + if( p_input ) + { + snprintf( buf, 10, "%2.1lf", + var_GetFloat( p_input, "position" ) * 100. ); + } + else + { + sprintf( buf, b_empty_if_na ? "" : "--.-%%" ); + } + INSERT_STRING_NO_FREE( buf ); + break; + case 'R': + if( p_input ) + { + int r = var_GetInteger( p_input, "rate" ); + snprintf( buf, 10, "%d.%d", r/1000, r%1000 ); + } + else + { + sprintf( buf, b_empty_if_na ? "" : "-" ); + } + INSERT_STRING_NO_FREE( buf ); + break; + case 'S': + if( p_input ) + { + int r = var_GetInteger( p_input, "sample-rate" ); + snprintf( buf, 10, "%d.%d", r/1000, (r/100)%10 ); + } + else + { + sprintf( buf, b_empty_if_na ? "" : "-" ); + } + INSERT_STRING_NO_FREE( buf ); + break; + case 'T': + if( p_input ) + { + sprintf( buf, "%02d:%02d:%02d", + (int)( p_input->i_time / ( 3600000000 ) ), + (int)( ( p_input->i_time / ( 60000000 ) ) % 60 ), + (int)( ( p_input->i_time / 1000000 ) % 60 ) ); + } + else + { + sprintf( buf, b_empty_if_na ? "" : "--:--:--" ); + } + INSERT_STRING_NO_FREE( buf ); + break; + case 'U': + if( p_item ) + { + INSERT_STRING( input_item_GetPublisher( p_item ) ); + } + break; + case 'V': + { + audio_volume_t volume; + aout_VolumeGet( p_object, &volume ); + snprintf( buf, 10, "%d", volume ); + INSERT_STRING_NO_FREE( buf ); + break; + } + case '_': + *(dst+d) = '\n'; + d++; + break; + + case ' ': + b_empty_if_na = 1; + break; + + default: + *(dst+d) = *s; + d++; + break; + } + if( *s != ' ' ) + b_is_format = 0; + } + else if( *s == '$' ) + { + b_is_format = 1; + b_empty_if_na = 0; + } + else + { + *(dst+d) = *s; + d++; + } + s++; + } + *(dst+d) = '\0'; + + if( p_input ) + vlc_object_release( p_input ); + + return dst; +} + +/** + * Apply str format time and str format meta + */ +char *__str_format( vlc_object_t *p_this, const char *psz_src ) +{ + char *psz_buf1, *psz_buf2; + psz_buf1 = str_format_time( psz_src ); + psz_buf2 = str_format_meta( p_this, psz_buf1 ); + free( psz_buf1 ); + return psz_buf2; +} +size_t strnlen (const char *string, size_t maxlen); +char *strndup (const char *s, size_t n) +{ + size_t len = strnlen (s, n); + char *new = malloc (len + 1); + + if (new == NULL) + return NULL; + + new[len] = '\0'; + return memcpy (new, s, len); +} +size_t +strnlen (const char *string, size_t maxlen) +{ + const char *end = memchr (string, '\0', maxlen); + return end ? (size_t) (end - string) : maxlen; +} +/** + * Remove forbidden characters from filenames (including slashes) + */ +void filename_sanitize( char *str ) +{ + if( *str == '.' && (str[1] == '\0' || (str[1] == '.' && str[2] == '\0' ) ) ) + { + while( *str ) + { + *str = '_'; + str++; + } + return; + } + + while( *str ) + { + switch( *str ) + { + case '/': +#if defined( __APPLE__ ) + case ':': +#elif defined( WIN32 ) + case '\\': + case '*': + case '"': + case '?': + case ':': + case '|': + case '<': + case '>': +#endif + *str = '_'; + } + str++; + } +} + +/** + * Remove forbidden characters from full paths (leaves slashes) + */ +void path_sanitize( char *str ) +{ +#if 0 + /* + * Uncomment the two blocks to prevent /../ or /./, i'm not sure that we + * want to. + */ + char *prev = str - 1; +#endif +#ifdef WIN32 + /* check drive prefix if path is absolute */ + if( isalpha(*str) && (':' == *(str+1)) ) + str += 2; +#endif + while( *str ) + { +#if defined( __APPLE__ ) + if( *str == ':' ) + *str = '_'; +#elif defined( WIN32 ) + switch( *str ) + { + case '*': + case '"': + case '?': + case ':': + case '|': + case '<': + case '>': + *str = '_'; + } +#endif +#if 0 + if( *str == '/' +#ifdef WIN32 + || *str == '\\' +#endif + ) + { + if( str - prev == 2 && prev[1] == '.' ) + { + prev[1] = '.'; + } + else if( str - prev == 3 && prev[1] == '.' && prev[2] == '.' ) + { + prev[1] = '_'; + prev[2] = '_'; + } + prev = str; + } +#endif + str++; + } +} diff --git a/VLC/tcp.c b/VLC/tcp.c new file mode 100644 index 0000000..bed46fc --- /dev/null +++ b/VLC/tcp.c @@ -0,0 +1,546 @@ +/***************************************************************************** + * tcp.c: + ***************************************************************************** + * Copyright (C) 2004-2005 the VideoLAN team + * Copyright (C) 2005-2006 Rémi Denis-Courmont + * $Id$ + * + * Authors: Laurent Aimar + * Rémi Denis-Courmont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#include +#include + +#ifdef HAVE_FCNTL_H +# include +#endif +#ifdef HAVE_SYS_TIME_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif +#ifdef HAVE_POLL +# include +#endif + +#include "vlc_network.h" +#if defined (WIN32) || defined (UNDER_CE) +# undef EINPROGRESS +# define EINPROGRESS WSAEWOULDBLOCK +# undef EINTR +# define EINTR WSAEINTR +# undef ETIMEDOUT +# define ETIMEDOUT WSAETIMEDOUT +#endif + +static int SocksNegotiate( vlc_object_t *, int fd, int i_socks_version, + const char *psz_user, const char *psz_passwd ); +static int SocksHandshakeTCP( vlc_object_t *, + int fd, int i_socks_version, + const char *psz_user, const char *psz_passwd, + const char *psz_host, int i_port ); +extern int net_Socket( vlc_object_t *p_this, int i_family, int i_socktype, + int i_protocol ); + +/***************************************************************************** + * __net_Connect: + ***************************************************************************** + * Open a network connection. + * @return socket handler or -1 on error. + *****************************************************************************/ +int __net_Connect( vlc_object_t *p_this, const char *psz_host, int i_port, + int type, int proto ) +{ + struct addrinfo hints, *res, *ptr; + const char *psz_realhost; + char *psz_socks; + int i_realport, i_val, i_handle = -1; + + int evfd = vlc_object_waitpipe (p_this); + if (evfd == -1) + return -1; + + memset( &hints, 0, sizeof( hints ) ); + hints.ai_socktype = SOCK_STREAM; + + psz_socks = var_CreateGetNonEmptyString( p_this, "socks" ); + if( psz_socks != NULL ) + { + char *psz = strchr( psz_socks, ':' ); + + if( psz ) + *psz++ = '\0'; + + psz_realhost = psz_socks; + i_realport = ( psz != NULL ) ? atoi( psz ) : 1080; + hints.ai_flags &= ~AI_NUMERICHOST; + + msg_Dbg( p_this, "net: connecting to %s port %d (SOCKS) " + "for %s port %d", psz_realhost, i_realport, + psz_host, i_port ); + + /* We only implement TCP with SOCKS */ + switch( type ) + { + case 0: + type = SOCK_STREAM; + case SOCK_STREAM: + break; + default: + msg_Err( p_this, "Socket type not supported through SOCKS" ); + free( psz_socks ); + return -1; + } + switch( proto ) + { + case 0: + proto = IPPROTO_TCP; + case IPPROTO_TCP: + break; + default: + msg_Err( p_this, "Transport not supported through SOCKS" ); + free( psz_socks ); + return -1; + } + } + else + { + psz_realhost = psz_host; + i_realport = i_port; + + msg_Dbg( p_this, "net: connecting to %s port %d", psz_realhost, + i_realport ); + } + + i_val = vlc_getaddrinfo( p_this, psz_realhost, i_realport, &hints, &res ); + free( psz_socks ); + + if( i_val ) + { + msg_Err( p_this, "cannot resolve %s port %d : %s", psz_realhost, + i_realport, vlc_gai_strerror( i_val ) ); + return -1; + } + + for( ptr = res; ptr != NULL; ptr = ptr->ai_next ) + { + int fd = net_Socket( p_this, ptr->ai_family, type ?: ptr->ai_socktype, + proto ?: ptr->ai_protocol ); + if( fd == -1 ) + { + msg_Dbg( p_this, "socket error: %m" ); + continue; + } + + if( connect( fd, ptr->ai_addr, ptr->ai_addrlen ) ) + { + int timeout, val; + + if( net_errno != EINPROGRESS && net_errno != EINTR ) + { + msg_Err( p_this, "connection failed: %m" ); + goto next_ai; + } + msg_Dbg( p_this, "connection: %m" ); + + timeout = var_CreateGetInteger (p_this, "ipv4-timeout"); + if (timeout < 0) + { + msg_Err( p_this, "invalid negative value for ipv4-timeout" ); + timeout = 0; + } + + struct pollfd ufd[2] = { + { .fd = fd, .events = POLLOUT }, + { .fd = evfd, .events = POLLIN }, + }; + + do + /* NOTE: timeout screwed up if we catch a signal (EINTR) */ + val = poll (ufd, sizeof (ufd) / sizeof (ufd[0]), timeout); + while ((val == -1) && (net_errno == EINTR)); + + switch (val) + { + case -1: /* error */ + msg_Err (p_this, "connection polling error: %m"); + goto next_ai; + + case 0: /* timeout */ + msg_Warn (p_this, "connection timed out"); + goto next_ai; + + default: /* something happended */ + if (ufd[1].revents) + goto next_ai; /* LibVLC object killed */ + } + + /* There is NO WAY around checking SO_ERROR. + * Don't ifdef it out!!! */ + if (getsockopt (fd, SOL_SOCKET, SO_ERROR, &val, + &(socklen_t){ sizeof (val) }) || val) + { + errno = val; + msg_Err (p_this, "connection failed: %m"); + goto next_ai; + } + } + + msg_Dbg( p_this, "connection succeeded (socket = %d)", fd ); + i_handle = fd; /* success! */ + break; + +next_ai: /* failure */ + net_Close( fd ); + continue; + } + + vlc_freeaddrinfo( res ); + + if( i_handle == -1 ) + return -1; + + if( psz_socks != NULL ) + { + /* NOTE: psz_socks already free'd! */ + char *psz_user = var_CreateGetNonEmptyString( p_this, "socks-user" ); + char *psz_pwd = var_CreateGetNonEmptyString( p_this, "socks-pwd" ); + + if( SocksHandshakeTCP( p_this, i_handle, 5, psz_user, psz_pwd, + psz_host, i_port ) ) + { + msg_Err( p_this, "SOCKS handshake failed" ); + net_Close( i_handle ); + i_handle = -1; + } + + free( psz_user ); + free( psz_pwd ); + } + + return i_handle; +} + + +int net_AcceptSingle (vlc_object_t *obj, int lfd) +{ + int fd; + do + fd = accept (lfd, NULL, NULL); + while (fd == -1 && errno == EINTR); + + if (fd == -1) + { + if (net_errno != EAGAIN) + msg_Err (obj, "accept failed (from socket %d): %m", lfd); + return -1; + } + + msg_Dbg (obj, "accepted socket %d (from socket %d)", fd, lfd); + net_SetupSocket (fd); + return fd; +} + + +/***************************************************************************** + * __net_Accept: + ***************************************************************************** + * Accept a connection on a set of listening sockets and return it + *****************************************************************************/ +int __net_Accept( vlc_object_t *p_this, int *pi_fd, mtime_t i_wait ) +{ + int timeout = (i_wait < 0) ? -1 : i_wait / 1000; + int evfd = vlc_object_waitpipe (p_this); + + if (evfd == -1) + return -1; + + assert( pi_fd != NULL ); + + for (;;) + { + unsigned n = 0; + while (pi_fd[n] != -1) + n++; + struct pollfd ufd[n + 1]; + + /* Initialize file descriptor set */ + for (unsigned i = 0; i <= n; i++) + { + ufd[i].fd = (i < n) ? pi_fd[i] : evfd; + ufd[i].events = POLLIN; + ufd[i].revents = 0; + } + if (evfd == -1) + n--; /* avoid EBADF */ + + switch (poll (ufd, n, timeout)) + { + case -1: + if (net_errno == EINTR) + continue; + msg_Err (p_this, "poll error: %m"); + return -1; + case 0: + errno = ETIMEDOUT; + return -1; + } + + if (ufd[n].revents) + { + errno = EINTR; + break; + } + + for (unsigned i = 0; i < n; i++) + { + if (ufd[i].revents == 0) + continue; + + int sfd = ufd[i].fd; + int fd = net_AcceptSingle (p_this, sfd); + if (fd == -1) + continue; + + /* + * Move listening socket to the end to let the others in the + * set a chance next time. + */ + memmove (pi_fd + i, pi_fd + i + 1, n - (i + 1)); + pi_fd[n - 1] = sfd; + return fd; + } + } + return -1; +} + + +/***************************************************************************** + * SocksNegotiate: + ***************************************************************************** + * Negotiate authentication with a SOCKS server. + *****************************************************************************/ +static int SocksNegotiate( vlc_object_t *p_obj, + int fd, int i_socks_version, + const char *psz_socks_user, + const char *psz_socks_passwd ) +{ + uint8_t buffer[128+2*256]; + int i_len; + bool b_auth = false; + + if( i_socks_version != 5 ) + return VLC_SUCCESS; + + /* We negotiate authentication */ + + if( ( psz_socks_user == NULL ) && ( psz_socks_passwd == NULL ) ) + b_auth = true; + + buffer[0] = i_socks_version; /* SOCKS version */ + if( b_auth ) + { + buffer[1] = 2; /* Number of methods */ + buffer[2] = 0x00; /* - No auth required */ + buffer[3] = 0x02; /* - USer/Password */ + i_len = 4; + } + else + { + buffer[1] = 1; /* Number of methods */ + buffer[2] = 0x00; /* - No auth required */ + i_len = 3; + } + + if( net_Write( p_obj, fd, NULL, buffer, i_len ) != i_len ) + return VLC_EGENERIC; + if( net_Read( p_obj, fd, NULL, buffer, 2, true ) != 2 ) + return VLC_EGENERIC; + + msg_Dbg( p_obj, "socks: v=%d method=%x", buffer[0], buffer[1] ); + + if( buffer[1] == 0x00 ) + { + msg_Dbg( p_obj, "socks: no authentication required" ); + } + else if( buffer[1] == 0x02 ) + { + int i_len1 = __MIN( strlen(psz_socks_user), 255 ); + int i_len2 = __MIN( strlen(psz_socks_passwd), 255 ); + msg_Dbg( p_obj, "socks: username/password authentication" ); + + /* XXX: we don't support user/pwd > 255 (truncated)*/ + buffer[0] = i_socks_version; /* Version */ + buffer[1] = i_len1; /* User length */ + memcpy( &buffer[2], psz_socks_user, i_len1 ); + buffer[2+i_len1] = i_len2; /* Password length */ + memcpy( &buffer[2+i_len1+1], psz_socks_passwd, i_len2 ); + + i_len = 3 + i_len1 + i_len2; + + if( net_Write( p_obj, fd, NULL, buffer, i_len ) != i_len ) + return VLC_EGENERIC; + + if( net_Read( p_obj, fd, NULL, buffer, 2, true ) != 2 ) + return VLC_EGENERIC; + + msg_Dbg( p_obj, "socks: v=%d status=%x", buffer[0], buffer[1] ); + if( buffer[1] != 0x00 ) + { + msg_Err( p_obj, "socks: authentication rejected" ); + return VLC_EGENERIC; + } + } + else + { + if( b_auth ) + msg_Err( p_obj, "socks: unsupported authentication method %x", + buffer[0] ); + else + msg_Err( p_obj, "socks: authentification needed" ); + return VLC_EGENERIC; + } + + return VLC_SUCCESS; +} + +/***************************************************************************** + * SocksHandshakeTCP: + ***************************************************************************** + * Open a TCP connection using a SOCKS server and return a handle (RFC 1928) + *****************************************************************************/ +static int SocksHandshakeTCP( vlc_object_t *p_obj, + int fd, + int i_socks_version, + const char *psz_user, const char *psz_passwd, + const char *psz_host, int i_port ) +{ + uint8_t buffer[128+2*256]; + + if( i_socks_version != 4 && i_socks_version != 5 ) + { + msg_Warn( p_obj, "invalid socks protocol version %d", i_socks_version ); + i_socks_version = 5; + } + + if( i_socks_version == 5 && + SocksNegotiate( p_obj, fd, i_socks_version, + psz_user, psz_passwd ) ) + return VLC_EGENERIC; + + if( i_socks_version == 4 ) + { + struct addrinfo hints, *p_res; + + /* v4 only support ipv4 */ + memset (&hints, 0, sizeof (hints)); + hints.ai_family = AF_INET; + if( vlc_getaddrinfo( p_obj, psz_host, 0, &hints, &p_res ) ) + return VLC_EGENERIC; + + buffer[0] = i_socks_version; + buffer[1] = 0x01; /* CONNECT */ + SetWBE( &buffer[2], i_port ); /* Port */ + memcpy( &buffer[4], /* Address */ + &((struct sockaddr_in *)(p_res->ai_addr))->sin_addr, 4 ); + vlc_freeaddrinfo( p_res ); + + buffer[8] = 0; /* Empty user id */ + + if( net_Write( p_obj, fd, NULL, buffer, 9 ) != 9 ) + return VLC_EGENERIC; + if( net_Read( p_obj, fd, NULL, buffer, 8, true ) != 8 ) + return VLC_EGENERIC; + + msg_Dbg( p_obj, "socks: v=%d cd=%d", + buffer[0], buffer[1] ); + + if( buffer[1] != 90 ) + return VLC_EGENERIC; + } + else if( i_socks_version == 5 ) + { + int i_hlen = __MIN(strlen( psz_host ), 255); + int i_len; + + buffer[0] = i_socks_version; /* Version */ + buffer[1] = 0x01; /* Cmd: connect */ + buffer[2] = 0x00; /* Reserved */ + buffer[3] = 3; /* ATYP: for now domainname */ + + buffer[4] = i_hlen; + memcpy( &buffer[5], psz_host, i_hlen ); + SetWBE( &buffer[5+i_hlen], i_port ); + + i_len = 5 + i_hlen + 2; + + + if( net_Write( p_obj, fd, NULL, buffer, i_len ) != i_len ) + return VLC_EGENERIC; + + /* Read the header */ + if( net_Read( p_obj, fd, NULL, buffer, 5, true ) != 5 ) + return VLC_EGENERIC; + + msg_Dbg( p_obj, "socks: v=%d rep=%d atyp=%d", + buffer[0], buffer[1], buffer[3] ); + + if( buffer[1] != 0x00 ) + { + msg_Err( p_obj, "socks: CONNECT request failed\n" ); + return VLC_EGENERIC; + } + + /* Read the remaining bytes */ + if( buffer[3] == 0x01 ) + i_len = 4-1 + 2; + else if( buffer[3] == 0x03 ) + i_len = buffer[4] + 2; + else if( buffer[3] == 0x04 ) + i_len = 16-1+2; + else + return VLC_EGENERIC; + + if( net_Read( p_obj, fd, NULL, buffer, i_len, true ) != i_len ) + return VLC_EGENERIC; + } + + return VLC_SUCCESS; +} + +void net_ListenClose( int *pi_fd ) +{ + if( pi_fd != NULL ) + { + int *pi; + + for( pi = pi_fd; *pi != -1; pi++ ) + net_Close( *pi ); + free( pi_fd ); + } +} diff --git a/VLC/text/charset.c b/VLC/text/charset.c new file mode 100644 index 0000000..7087c48 --- /dev/null +++ b/VLC/text/charset.c @@ -0,0 +1,102 @@ +/***************************************************************************** + * charset.c: Locale's character encoding stuff. + ***************************************************************************** + * See also unicode.c for Unicode to locale conversion helpers. + * + * Copyright (C) 2003-2008 the VideoLAN team + * + * Authors: Christophe Massiot + * Rémi Denis-Courmont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#if !defined WIN32 +# include +#else +# include +#endif + +#ifdef __APPLE__ +# include +# include +# include +#endif + +#include "libvlc.h" +#include "vlc_charset.h" + +char *vlc_fix_readdir( const char *psz_string ) +{ +#ifdef __APPLE__ + vlc_iconv_t hd = vlc_iconv_open( "UTF-8", "UTF-8-MAC" ); + + if (hd != (vlc_iconv_t)(-1)) + { + const char *psz_in = psz_string; + size_t i_in = strlen(psz_in); + size_t i_out = i_in * 2; + char *psz_utf8 = malloc(i_out + 1); + char *psz_out = psz_utf8; + + size_t i_ret = vlc_iconv (hd, &psz_in, &i_in, &psz_out, &i_out); + vlc_iconv_close (hd); + if( i_ret == (size_t)(-1) || i_in ) + { + free( psz_utf8 ); + return strdup( psz_string ); + } + + *psz_out = '\0'; + return psz_utf8; + } +#endif + return strdup( psz_string ); +} + + +/** + * us_strtod() has the same prototype as ANSI C strtod() but it uses the + * POSIX/C decimal format, regardless of the current numeric locale. + */ +double us_strtod( const char *str, char **end ) +{ + locale_t loc = newlocale (LC_NUMERIC_MASK, "C", NULL); + locale_t oldloc = uselocale (loc); + double res = strtod (str, end); + + if (loc != (locale_t)0) + { + uselocale (oldloc); + freelocale (loc); + } + return res; +} + +/** + * us_atof() has the same prototype as ANSI C atof() but it expects a dot + * as decimal separator, regardless of the system locale. + */ +double us_atof( const char *str ) +{ + return us_strtod( str, NULL ); +} + diff --git a/VLC/text/iso-639_def.h b/VLC/text/iso-639_def.h new file mode 100644 index 0000000..c936cb3 --- /dev/null +++ b/VLC/text/iso-639_def.h @@ -0,0 +1,206 @@ +/***************************************************************************** + * iso_lang.h: languages codes and abbreviations + ***************************************************************************** + * Copyright (C) 1998-2004 the VideoLAN team + * $Id$ + * + * This is used in iso_lang.cpp and is taken from the GNU glibc 2.2.5 + * tarball. It has been partially completed with native language names. + * Authors: Stéphane Borel + * Arnaud de Bossoreille de Ribou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/* Define the languages codes and abbreviations according to ISO 639-[12]. + * Format is:( "English name", 639-1-code, 639-2/T-code, 639-2/B-code) + * If you find something missing or wrong contact */ + +/* Some spellings were converted to pure ASCII: + * Provençal -> Provencal + * Volapk -> Volapuk + * Bokm? -> Bokmaal */ + +static const iso639_lang_t p_languages[] = +{ + { N_( "Afar" ), "", "aa", "aar", "aar" }, + { N_( "Abkhazian" ), "", "ab", "abk", "abk" }, + { N_( "Afrikaans" ), "", "af", "afr", "afr" }, + { N_( "Albanian" ), "", "sq", "sqi", "alb" }, + { N_( "Amharic" ), "", "am", "amh", "amh" }, + { N_( "Arabic" ), "", "ar", "ara", "ara" }, + { N_( "Armenian" ), "", "hy", "hye", "arm" }, + { N_( "Assamese" ), "", "as", "asm", "asm" }, + { N_( "Avestan" ), "", "ae", "ave", "ave" }, + { N_( "Aymara" ), "", "ay", "aym", "aym" }, + { N_( "Azerbaijani" ), "", "az", "aze", "aze" }, + { N_( "Bashkir" ), "", "ba", "bak", "bak" }, + { N_( "Basque" ), "", "eu", "eus", "baq" }, + { N_( "Belarusian" ), "", "be", "bel", "bel" }, + { N_( "Bengali" ), "", "bn", "ben", "ben" }, + { N_( "Bihari" ), "", "bh", "bih", "bih" }, + { N_( "Bislama" ), "", "bi", "bis", "bis" }, + { N_( "Bosnian" ), "", "bs", "bos", "bos" }, + { N_( "Breton" ), "", "br", "bre", "bre" }, + { N_( "Bulgarian" ), "", "bg", "bul", "bul" }, + { N_( "Burmese" ), "", "my", "mya", "bur" }, + { N_( "Catalan" ), "Catala", "ca", "cat", "cat" }, + { N_( "Chamorro" ), "", "ch", "cha", "cha" }, + { N_( "Chechen" ), "", "ce", "che", "che" }, + { N_( "Chinese" ), "", "zh", "zho", "chi" }, + { N_( "Church Slavic" ), "", "cu", "chu", "chu" }, + { N_( "Chuvash" ), "", "cv", "chv", "chv" }, + { N_( "Cornish" ), "", "kw", "cor", "cor" }, + { N_( "Corsican" ), "", "co", "cos", "cos" }, + { N_( "Czech" ), "", "cs", "ces", "cze" }, + { N_( "Danish" ), "Dansk", "da", "dan", "dan" }, + { N_( "Dutch" ), "Nederlands", "nl", "nld", "dut" }, + { N_( "Dzongkha" ), "", "dz", "dzo", "dzo" }, + { N_( "English" ), "English", "en", "eng", "eng" }, + { N_( "Esperanto" ), "", "eo", "epo", "epo" }, + { N_( "Estonian" ), "", "et", "est", "est" }, + { N_( "Faroese" ), "", "fo", "fao", "fao" }, + { N_( "Fijian" ), "", "fj", "fij", "fij" }, + { N_( "Finnish" ), "Suomi", "fi", "fin", "fin" }, + { N_( "French" ), "Francais", "fr", "fra", "fre" }, + { N_( "Frisian" ), "", "fy", "fry", "fry" }, + { N_( "Georgian" ), "", "ka", "kat", "geo" }, + { N_( "German" ), "Deutsch", "de", "deu", "ger" }, + { N_( "Gaelic (Scots)" ), "", "gd", "gla", "gla" }, + { N_( "Irish" ), "", "ga", "gle", "gle" }, + { N_( "Gallegan" ), "", "gl", "glg", "glg" }, + { N_( "Manx" ), "", "gv", "glv", "glv" }, + { N_( "Greek, Modern ()" ), "", "el", "gre", "ell" }, + { N_( "Guarani" ), "", "gn", "grn", "grn" }, + { N_( "Gujarati" ), "", "gu", "guj", "guj" }, + { N_( "Hebrew" ), "", "he", "heb", "heb" }, + { N_( "Herero" ), "", "hz", "her", "her" }, + { N_( "Hindi" ), "", "hi", "hin", "hin" }, + { N_( "Hiri Motu" ), "", "ho", "hmo", "hmo" }, + { N_( "Hungarian" ), "Magyar", "hu", "hun", "hun" }, + { N_( "Icelandic" ), "Islenska", "is", "isl", "ice" }, + { N_( "Inuktitut" ), "", "iu", "iku", "iku" }, + { N_( "Interlingue" ), "", "ie", "ile", "ile" }, + { N_( "Interlingua" ), "", "ia", "ina", "ina" }, + { N_( "Indonesian" ), "", "id", "ind", "ind" }, + { N_( "Inupiaq" ), "", "ik", "ipk", "ipk" }, + { N_( "Italian" ), "Italiano", "it", "ita", "ita" }, + { N_( "Javanese" ), "", "jv", "jaw", "jav" }, + { N_( "Japanese" ), "", "ja", "jpn", "jpn" }, + { N_( "Kalaallisut (Greenlandic)" ), "", "kl", "kal", "kal" }, + { N_( "Kannada" ), "", "kn", "kan", "kan" }, + { N_( "Kashmiri" ), "", "ks", "kas", "kas" }, + { N_( "Kazakh" ), "", "kk", "kaz", "kaz" }, + { N_( "Khmer" ), "", "km", "khm", "khm" }, + { N_( "Kikuyu" ), "", "ki", "kik", "kik" }, + { N_( "Kinyarwanda" ), "", "rw", "kin", "kin" }, + { N_( "Kirghiz" ), "", "ky", "kir", "kir" }, + { N_( "Komi" ), "", "kv", "kom", "kom" }, + { N_( "Korean" ), "", "ko", "kor", "kor" }, + { N_( "Kuanyama" ), "", "kj", "kua", "kua" }, + { N_( "Kurdish" ), "", "ku", "kur", "kur" }, + { N_( "Lao" ), "", "lo", "lao", "lao" }, + { N_( "Latin" ), "", "la", "lat", "lat" }, + { N_( "Latvian" ), "", "lv", "lav", "lav" }, + { N_( "Lingala" ), "", "ln", "lin", "lin" }, + { N_( "Lithuanian" ), "", "lt", "lit", "lit" }, + { N_( "Letzeburgesch" ), "", "lb", "ltz", "ltz" }, + { N_( "Macedonian" ), "", "mk", "mkd", "mac" }, + { N_( "Marshall" ), "", "mh", "mah", "mah" }, + { N_( "Malayalam" ), "", "ml", "mal", "mal" }, + { N_( "Maori" ), "", "mi", "mri", "mao" }, + { N_( "Marathi" ), "", "mr", "mar", "mar" }, + { N_( "Malay" ), "", "ms", "msa", "may" }, + { N_( "Malagasy" ), "", "mg", "mlg", "mlg" }, + { N_( "Maltese" ), "", "mt", "mlt", "mlt" }, + { N_( "Moldavian" ), "", "mo", "mol", "mol" }, + { N_( "Mongolian" ), "", "mn", "mon", "mon" }, + { N_( "Nauru" ), "", "na", "nau", "nau" }, + { N_( "Navajo" ), "", "nv", "nav", "nav" }, + { N_( "Ndebele, South" ), "", "nr", "nbl", "nbl" }, + { N_( "Ndebele, North" ), "", "nd", "nde", "nde" }, + { N_( "Ndonga" ), "", "ng", "ndo", "ndo" }, + { N_( "Nepali" ), "", "ne", "nep", "nep" }, + { N_( "Norwegian" ), "Norsk", "no", "nor", "nor" }, + { N_( "Norwegian Nynorsk" ), "", "nn", "nno", "nno" }, + { N_( "Norwegian Bokmaal" ), "", "nb", "nob", "nob" }, + { N_( "Chichewa; Nyanja" ), "", "ny", "nya", "nya" }, + { N_( "Occitan (post 1500); Provencal" ), "", "oc", "oci", "oci" }, + { N_( "Oriya" ), "", "or", "ori", "ori" }, + { N_( "Oromo" ), "", "om", "orm", "orm" }, + { N_( "On Screen Display" ),"On Screen Display", "od", "osd", "osd" }, + { N_( "Ossetian; Ossetic" ), "", "os", "oss", "oss" }, + { N_( "Panjabi" ), "", "pa", "pan", "pan" }, + { N_( "Persian" ), "", "fa", "fas", "per" }, + { N_( "Pali" ), "", "pi", "pli", "pli" }, + { N_( "Polish" ), "", "pl", "pol", "pol" }, + { N_( "Portuguese" ), "Portugues", "pt", "por", "por" }, + { N_( "Pushto" ), "", "ps", "pus", "pus" }, + { N_( "Quechua" ), "", "qu", "que", "que" }, + { N_( "Original audio" ), "", "", "qaa", "qaa" }, + { N_( "Raeto-Romance" ), "", "rm", "roh", "roh" }, + { N_( "Romanian" ), "", "ro", "ron", "rum" }, + { N_( "Rundi" ), "", "rn", "run", "run" }, + { N_( "Russian" ), "", "ru", "rus", "rus" }, + { N_( "Sango" ), "", "sg", "sag", "sag" }, + { N_( "Sanskrit" ), "", "sa", "san", "san" }, + { N_( "Serbian" ), "", "sr", "srp", "scc" }, + { N_( "Croatian" ), "Hrvatski", "hr", "hrv", "scr" }, + { N_( "Sinhalese" ), "", "si", "sin", "sin" }, + { N_( "Slovak" ), "", "sk", "slk", "slo" }, + { N_( "Slovenian" ), "", "sl", "slv", "slv" }, + { N_( "Northern Sami" ), "", "se", "sme", "sme" }, + { N_( "Samoan" ), "", "sm", "smo", "smo" }, + { N_( "Shona" ), "", "sn", "sna", "sna" }, + { N_( "Sindhi" ), "", "sd", "snd", "snd" }, + { N_( "Somali" ), "", "so", "som", "som" }, + { N_( "Sotho, Southern" ), "", "st", "sot", "sot" }, + { N_( "Spanish" ), "Espanol", "es", "spa", "spa" }, + { N_( "Sardinian" ), "", "sc", "srd", "srd" }, + { N_( "Swati" ), "", "ss", "ssw", "ssw" }, + { N_( "Sundanese" ), "", "su", "sun", "sun" }, + { N_( "Swahili" ), "", "sw", "swa", "swa" }, + { N_( "Swedish" ), "Svenska", "sv", "swe", "swe" }, + { N_( "Tahitian" ), "", "ty", "tah", "tah" }, + { N_( "Tamil" ), "", "ta", "tam", "tam" }, + { N_( "Tatar" ), "", "tt", "tat", "tat" }, + { N_( "Telugu" ), "", "te", "tel", "tel" }, + { N_( "Tajik" ), "", "tg", "tgk", "tgk" }, + { N_( "Tagalog" ), "", "tl", "tgl", "tgl" }, + { N_( "Thai" ), "", "th", "tha", "tha" }, + { N_( "Tibetan" ), "", "bo", "bod", "tib" }, + { N_( "Tigrinya" ), "", "ti", "tir", "tir" }, + { N_( "Tonga (Tonga Islands)" ), "", "to", "ton", "ton" }, + { N_( "Tswana" ), "", "tn", "tsn", "tsn" }, + { N_( "Tsonga" ), "", "ts", "tso", "tso" }, + { N_( "Turkish" ), "", "tr", "tur", "tur" }, + { N_( "Turkmen" ), "", "tk", "tuk", "tuk" }, + { N_( "Twi" ), "", "tw", "twi", "twi" }, + { N_( "Uighur" ), "", "ug", "uig", "uig" }, + { N_( "Ukrainian" ), "", "uk", "ukr", "ukr" }, + { N_( "Urdu" ), "", "ur", "urd", "urd" }, + { N_( "Uzbek" ), "", "uz", "uzb", "uzb" }, + { N_( "Vietnamese" ), "", "vi", "vie", "vie" }, + { N_( "Volapuk" ), "", "vo", "vol", "vol" }, + { N_( "Welsh" ), "", "cy", "cym", "wel" }, + { N_( "Wolof" ), "", "wo", "wol", "wol" }, + { N_( "Xhosa" ), "", "xh", "xho", "xho" }, + { N_( "Yiddish" ), "", "yi", "yid", "yid" }, + { N_( "Yoruba" ), "", "yo", "yor", "yor" }, + { N_( "Zhuang" ), "", "za", "zha", "zha" }, + { N_( "Zulu" ), "", "zu", "zul", "zul" }, + { NULL, NULL, NULL, NULL, NULL } +}; + diff --git a/VLC/text/iso_lang.c b/VLC/text/iso_lang.c new file mode 100644 index 0000000..f2c66b6 --- /dev/null +++ b/VLC/text/iso_lang.c @@ -0,0 +1,79 @@ +/***************************************************************************** + * iso_lang.c: function to decode language code (in dvd or a52 for instance). + ***************************************************************************** + * Copyright (C) 1998-2004 the VideoLAN team + * $Id$ + * + * Author: Stéphane Borel + * Arnaud de Bossoreille de Ribou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include "vlc_common.h" + +#include "vlc_iso_lang.h" + +/***************************************************************************** + * Local tables + *****************************************************************************/ +#include "iso-639_def.h" + +static const iso639_lang_t unknown_language = + { "Unknown", "Unknown", "??", "???", "???" }; + +const iso639_lang_t * GetLang_1( const char * psz_code ) +{ + const iso639_lang_t *p_lang; + + for( p_lang = p_languages; p_lang->psz_eng_name; p_lang++ ) + if( !strncmp( p_lang->psz_iso639_1, psz_code, 2 ) ) + return p_lang; + + return &unknown_language; +} + +const iso639_lang_t * GetLang_2T( const char * psz_code ) +{ + const iso639_lang_t *p_lang; + + for( p_lang = p_languages; p_lang->psz_eng_name; p_lang++ ) + if( !strncmp( p_lang->psz_iso639_2T, psz_code, 3 ) ) + return p_lang; + + return &unknown_language; +} + +const iso639_lang_t * GetLang_2B( const char * psz_code ) +{ + const iso639_lang_t *p_lang; + + for( p_lang = p_languages; p_lang->psz_eng_name; p_lang++ ) + if( !strncmp( p_lang->psz_iso639_2B, psz_code, 3 ) ) + return p_lang; + + return &unknown_language; +} + diff --git a/VLC/text/strings.c b/VLC/text/strings.c new file mode 100644 index 0000000..170ff6c --- /dev/null +++ b/VLC/text/strings.c @@ -0,0 +1,1078 @@ +/***************************************************************************** + * strings.c: String related functions + ***************************************************************************** + * Copyright (C) 2006 the VideoLAN team + * $Id$ + * + * Authors: Antoine Cellerier + * Daniel Stranger + * Rémi Denis-Courmont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include + +/* Needed by str_format_time */ +#include + +/* Needed by str_format_meta */ +#include "vlc_input.h" +#include "vlc_meta.h" +#include "vlc_playlist.h" +#include "vlc_aout.h" + +#include "vlc_strings.h" +#include "vlc_url.h" +#include "vlc_charset.h" + +/** + * Unescape URI encoded string + * \return decoded duplicated string + */ +char *unescape_URI_duplicate( const char *psz ) +{ + char *psz_dup = strdup( psz ); + unescape_URI( psz_dup ); + return psz_dup; +} + +/** + * Unescape URI encoded string in place + * \return nothing + */ +void unescape_URI( char *psz ) +{ + unsigned char *in = (unsigned char *)psz, *out = in, c; + if( psz == NULL ) + return; + + while( ( c = *in++ ) != '\0' ) + { + switch( c ) + { + case '%': + { + char val[5], *pval = val; + unsigned long cp; + + switch( c = *in++ ) + { + case '\0': + return; + + case 'u': + case 'U': + if( ( *pval++ = *in++ ) == '\0' ) + return; + if( ( *pval++ = *in++ ) == '\0' ) + return; + c = *in++; + + default: + *pval++ = c; + if( ( *pval++ = *in++ ) == '\0' ) + return; + *pval = '\0'; + } + + cp = strtoul( val, NULL, 0x10 ); + if( cp < 0x80 ) + *out++ = cp; + else + if( cp < 0x800 ) + { + *out++ = (( cp >> 6) | 0xc0); + *out++ = (( cp & 0x3f) | 0x80); + } + else + { + assert( cp < 0x10000 ); + *out++ = (( cp >> 12) | 0xe0); + *out++ = (((cp >> 6) & 0x3f) | 0x80); + *out++ = (( cp & 0x3f) | 0x80); + } + break; + } + + /* + is not a special case - it means plus, not space. */ + + default: + /* Inserting non-ASCII or non-printable characters is unsafe, + * and no sane browser will send these unencoded */ + if( ( c < 32 ) || ( c > 127 ) ) + *out++ = '?'; + else + *out++ = c; + } + } + *out = '\0'; +} + +/** + * Decode encoded URI string + * \return decoded duplicated string + */ +char *decode_URI_duplicate( const char *psz ) +{ + char *psz_dup = strdup( psz ); + decode_URI( psz_dup ); + return psz_dup; +} + +/** + * Decode encoded URI string in place + * \return nothing + */ +void decode_URI( char *psz ) +{ + unsigned char *in = (unsigned char *)psz, *out = in, c; + if( psz == NULL ) + return; + + while( ( c = *in++ ) != '\0' ) + { + switch( c ) + { + case '%': + { + char hex[3]; + + if( ( ( hex[0] = *in++ ) == 0 ) + || ( ( hex[1] = *in++ ) == 0 ) ) + return; + + hex[2] = '\0'; + *out++ = (unsigned char)strtoul( hex, NULL, 0x10 ); + break; + } + + case '+': + *out++ = ' '; + break; + + default: + /* Inserting non-ASCII or non-printable characters is unsafe, + * and no sane browser will send these unencoded */ + if( ( c < 32 ) || ( c > 127 ) ) + *out++ = '?'; + else + *out++ = c; + } + } + *out = '\0'; + EnsureUTF8( psz ); +} + +static inline int isurlsafe( int c ) +{ + return ( (unsigned char)( c - 'a' ) < 26 ) + || ( (unsigned char)( c - 'A' ) < 26 ) + || ( (unsigned char)( c - '0' ) < 10 ) + /* Hmm, we should not encode character that are allowed in URLs + * (even if they are not URL-safe), nor URL-safe characters. + * We still encode some of them because of Microsoft's crap browser. + */ + || ( strchr( "-_.", c ) != NULL ); +} + +static inline char url_hexchar( int c ) +{ + return ( c < 10 ) ? c + '0' : c + 'A' - 10; +} + +/** + * encode_URI_component + * Encodes an URI component. + * + * @param psz_url nul-terminated UTF-8 representation of the component. + * Obviously, you can't pass an URI containing a nul character, but you don't + * want to do that, do you? + * + * @return encoded string (must be free()'d) + */ +char *encode_URI_component( const char *psz_url ) +{ + char psz_enc[3 * strlen( psz_url ) + 1], *out = psz_enc; + const uint8_t *in; + + for( in = (const uint8_t *)psz_url; *in; in++ ) + { + uint8_t c = *in; + + if( isurlsafe( c ) ) + *out++ = (char)c; + else + if ( c == ' ') + *out++ = '+'; + else + { + *out++ = '%'; + *out++ = url_hexchar( c >> 4 ); + *out++ = url_hexchar( c & 0xf ); + } + } + *out++ = '\0'; + + return strdup( psz_enc ); +} + +/** + * Converts "<", ">" and "&" to "<", ">" and "&" + * \param string to convert + */ +void resolve_xml_special_chars( char *psz_value ) +{ + char *p_pos = psz_value; + + while ( *psz_value ) + { + if( *psz_value == '&' ) + { +#define TRY_CHAR( src, len, dst ) \ + if( !strncmp( psz_value, src, len ) ) \ + { \ + *p_pos = dst; \ + psz_value += len; \ + } +#define TRY_LONGCHAR( src, len, dst ) \ + if( !strncmp( psz_value, src, len ) ) \ + { \ + strncpy( p_pos, dst, strlen( dst ) ); \ + p_pos += strlen( dst ) - 1; \ + psz_value += len; \ + } + TRY_CHAR( "<", 4, '<' ) + else TRY_CHAR( ">", 4, '>' ) + else TRY_CHAR( "&", 5, '&' ) + else TRY_CHAR( """, 6, '"' ) + else TRY_CHAR( "'", 6, '\'' ) + else if( psz_value[1] == '#' ) + { + char *psz_end; + int i = strtol( psz_value+2, &psz_end, 10 ); + if( *psz_end == ';' ) + { + if( i >= 32 && i <= 126 ) + { + *p_pos = (char)i; + psz_value = psz_end+1; + } + else + { + /* Unhandled code, FIXME */ + *p_pos = *psz_value; + psz_value++; + } + } + else + { + /* Invalid entity number */ + *p_pos = *psz_value; + psz_value++; + } + } + else TRY_LONGCHAR( "À", 8, "À" ) + else TRY_LONGCHAR( "Á", 8, "Á" ) + else TRY_LONGCHAR( "Â", 7, "Â" ) + else TRY_LONGCHAR( "Ã", 8, "Ã" ) + else TRY_LONGCHAR( "Ä", 6, "Ä" ) + else TRY_LONGCHAR( "Å", 7, "Å" ) + else TRY_LONGCHAR( "Æ", 7, "Æ" ) + else TRY_LONGCHAR( "Ç", 8, "Ç" ) + else TRY_LONGCHAR( "È", 8, "È" ) + else TRY_LONGCHAR( "É", 8, "É" ) + else TRY_LONGCHAR( "Ê", 7, "Ê" ) + else TRY_LONGCHAR( "Ë", 6, "Ë" ) + else TRY_LONGCHAR( "Ì", 8, "Ì" ) + else TRY_LONGCHAR( "Í", 8, "Í" ) + else TRY_LONGCHAR( "Î", 7, "Î" ) + else TRY_LONGCHAR( "Ï", 6, "Ï" ) + else TRY_LONGCHAR( "Ð", 5, "Ð" ) + else TRY_LONGCHAR( "Ñ", 8, "Ñ" ) + else TRY_LONGCHAR( "Ò", 8, "Ò" ) + else TRY_LONGCHAR( "Ó", 8, "Ó" ) + else TRY_LONGCHAR( "Ô", 7, "Ô" ) + else TRY_LONGCHAR( "Õ", 8, "Õ" ) + else TRY_LONGCHAR( "Ö", 6, "Ö" ) + else TRY_LONGCHAR( "Ø", 8, "Ø" ) + else TRY_LONGCHAR( "Ù", 8, "Ù" ) + else TRY_LONGCHAR( "Ú", 8, "Ú" ) + else TRY_LONGCHAR( "Û", 7, "Û" ) + else TRY_LONGCHAR( "Ü", 6, "Ü" ) + else TRY_LONGCHAR( "Ý", 8, "Ý" ) + else TRY_LONGCHAR( "Þ", 7, "Þ" ) + else TRY_LONGCHAR( "ß", 7, "ß" ) + else TRY_LONGCHAR( "à", 8, "à" ) + else TRY_LONGCHAR( "á", 8, "á" ) + else TRY_LONGCHAR( "â", 7, "â" ) + else TRY_LONGCHAR( "ã", 8, "ã" ) + else TRY_LONGCHAR( "ä", 6, "ä" ) + else TRY_LONGCHAR( "å", 7, "å" ) + else TRY_LONGCHAR( "æ", 7, "æ" ) + else TRY_LONGCHAR( "ç", 8, "ç" ) + else TRY_LONGCHAR( "è", 8, "è" ) + else TRY_LONGCHAR( "é", 8, "é" ) + else TRY_LONGCHAR( "ê", 7, "ê" ) + else TRY_LONGCHAR( "ë", 6, "ë" ) + else TRY_LONGCHAR( "ì", 8, "ì" ) + else TRY_LONGCHAR( "í", 8, "í" ) + else TRY_LONGCHAR( "î", 7, "î" ) + else TRY_LONGCHAR( "ï", 6, "ï" ) + else TRY_LONGCHAR( "ð", 5, "ð" ) + else TRY_LONGCHAR( "ñ", 8, "ñ" ) + else TRY_LONGCHAR( "ò", 8, "ò" ) + else TRY_LONGCHAR( "ó", 8, "ó" ) + else TRY_LONGCHAR( "ô", 7, "ô" ) + else TRY_LONGCHAR( "õ", 8, "õ" ) + else TRY_LONGCHAR( "ö", 6, "ö" ) + else TRY_LONGCHAR( "ø", 8, "ø" ) + else TRY_LONGCHAR( "ù", 8, "ù" ) + else TRY_LONGCHAR( "ú", 8, "ú" ) + else TRY_LONGCHAR( "û", 7, "û" ) + else TRY_LONGCHAR( "ü", 6, "ü" ) + else TRY_LONGCHAR( "ý", 8, "ý" ) + else TRY_LONGCHAR( "þ", 7, "þ" ) + else TRY_LONGCHAR( "ÿ", 6, "ÿ" ) + else TRY_LONGCHAR( "¡", 7, "¡" ) + else TRY_LONGCHAR( "¤", 8, "¤" ) + else TRY_LONGCHAR( "¢", 6, "¢" ) + else TRY_LONGCHAR( "£", 7, "£" ) + else TRY_LONGCHAR( "¥", 5, "¥" ) + else TRY_LONGCHAR( "¦", 8, "¦" ) + else TRY_LONGCHAR( "§", 6, "§" ) + else TRY_LONGCHAR( "¨", 5, "¨" ) + else TRY_LONGCHAR( "©", 6, "©" ) + else TRY_LONGCHAR( "ª", 6, "ª" ) + else TRY_LONGCHAR( "«", 7, "«" ) + else TRY_LONGCHAR( "¬", 5, "¬" ) + else TRY_LONGCHAR( "­", 5, "­" ) + else TRY_LONGCHAR( "®", 5, "®" ) + else TRY_LONGCHAR( "™", 7, "™" ) + else TRY_LONGCHAR( "¯", 6, "¯" ) + else TRY_LONGCHAR( "°", 5, "°" ) + else TRY_LONGCHAR( "±", 8, "±" ) + else TRY_LONGCHAR( "²", 6, "²" ) + else TRY_LONGCHAR( "³", 6, "³" ) + else TRY_LONGCHAR( "´", 7, "´" ) + else TRY_LONGCHAR( "µ", 7, "µ" ) + else TRY_LONGCHAR( "¶", 6, "¶" ) + else TRY_LONGCHAR( "·", 8, "·" ) + else TRY_LONGCHAR( "¸", 7, "¸" ) + else TRY_LONGCHAR( "¹", 6, "¹" ) + else TRY_LONGCHAR( "º", 6, "º" ) + else TRY_LONGCHAR( "»", 7, "»" ) + else TRY_LONGCHAR( "¼", 8, "¼" ) + else TRY_LONGCHAR( "½", 8, "½" ) + else TRY_LONGCHAR( "¾", 8, "¾" ) + else TRY_LONGCHAR( "¿", 8, "¿" ) + else TRY_LONGCHAR( "×", 7, "×" ) + else TRY_LONGCHAR( "÷", 8, "÷" ) + else TRY_LONGCHAR( "Œ", 7, "Œ" ) + else TRY_LONGCHAR( "œ", 7, "œ" ) + else TRY_LONGCHAR( "Š", 8, "Š" ) + else TRY_LONGCHAR( "š", 8, "š" ) + else TRY_LONGCHAR( "Ÿ", 6, "Ÿ" ) + else TRY_LONGCHAR( "ˆ", 6, "ˆ" ) + else TRY_LONGCHAR( "˜", 7, "˜" ) + else TRY_LONGCHAR( "–", 7, "–" ) + else TRY_LONGCHAR( "—", 7, "—" ) + else TRY_LONGCHAR( "‘", 7, "‘" ) + else TRY_LONGCHAR( "’", 7, "’" ) + else TRY_LONGCHAR( "‚", 7, "‚" ) + else TRY_LONGCHAR( "“", 7, "“" ) + else TRY_LONGCHAR( "”", 7, "”" ) + else TRY_LONGCHAR( "„", 7, "„" ) + else TRY_LONGCHAR( "†", 8, "†" ) + else TRY_LONGCHAR( "‡", 8, "‡" ) + else TRY_LONGCHAR( "…", 8, "…" ) + else TRY_LONGCHAR( "‰", 8, "‰" ) + else TRY_LONGCHAR( "‹", 8, "‹" ) + else TRY_LONGCHAR( "›", 8, "›" ) + else TRY_LONGCHAR( "€", 6, "€" ) + else + { + *p_pos = *psz_value; + psz_value++; + } + } + else + { + *p_pos = *psz_value; + psz_value++; + } + + p_pos++; + } + + *p_pos = '\0'; +} + +/** + * Converts '<', '>', '\"', '\'' and '&' to their html entities + * \param psz_content simple element content that is to be converted + */ +char *convert_xml_special_chars( const char *psz_content ) +{ + char *psz_temp = malloc( 6 * strlen( psz_content ) + 1 ); + const char *p_from = psz_content; + char *p_to = psz_temp; + + while ( *p_from ) + { + if ( *p_from == '<' ) + { + strcpy( p_to, "<" ); + p_to += 4; + } + else if ( *p_from == '>' ) + { + strcpy( p_to, ">" ); + p_to += 4; + } + else if ( *p_from == '&' ) + { + strcpy( p_to, "&" ); + p_to += 5; + } + else if( *p_from == '\"' ) + { + strcpy( p_to, """ ); + p_to += 6; + } + else if( *p_from == '\'' ) + { + strcpy( p_to, "'" ); + p_to += 6; + } + else + { + *p_to = *p_from; + p_to++; + } + p_from++; + } + *p_to = '\0'; + + return psz_temp; +} + +/* Base64 encoding */ +char *vlc_b64_encode_binary( const uint8_t *src, size_t i_src ) +{ + static const char b64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + char *ret = malloc( ( i_src + 4 ) * 4 / 3 ); + char *dst = ret; + + if( dst == NULL ) + return NULL; + + while( i_src > 0 ) + { + /* pops (up to) 3 bytes of input, push 4 bytes */ + uint32_t v; + + /* 1/3 -> 1/4 */ + v = *src++ << 24; + *dst++ = b64[v >> 26]; + v = v << 6; + + /* 2/3 -> 2/4 */ + if( i_src >= 2 ) + v |= *src++ << 22; + *dst++ = b64[v >> 26]; + v = v << 6; + + /* 3/3 -> 3/4 */ + if( i_src >= 3 ) + v |= *src++ << 20; // 3/3 + *dst++ = ( i_src >= 2 ) ? b64[v >> 26] : '='; // 3/4 + v = v << 6; + + /* -> 4/4 */ + *dst++ = ( i_src >= 3 ) ? b64[v >> 26] : '='; // 4/4 + + if( i_src <= 3 ) + break; + i_src -= 3; + } + + *dst = '\0'; + + return ret; +} + +char *vlc_b64_encode( const char *src ) +{ + if( src ) + return vlc_b64_encode_binary( (const uint8_t*)src, strlen(src) ); + else + return vlc_b64_encode_binary( (const uint8_t*)"", 0 ); +} + +/* Base64 decoding */ +size_t vlc_b64_decode_binary_to_buffer( uint8_t *p_dst, size_t i_dst, const char *p_src ) +{ + static const int b64[256] = { + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */ + 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */ + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */ + 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */ + -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */ + 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */ + }; + uint8_t *p_start = p_dst; + uint8_t *p = (uint8_t *)p_src; + + int i_level; + int i_last; + + for( i_level = 0, i_last = 0; (size_t)( p_dst - p_start ) < i_dst && *p != '\0'; p++ ) + { + const int c = b64[(unsigned int)*p]; + if( c == -1 ) + continue; + + switch( i_level ) + { + case 0: + i_level++; + break; + case 1: + *p_dst++ = ( i_last << 2 ) | ( ( c >> 4)&0x03 ); + i_level++; + break; + case 2: + *p_dst++ = ( ( i_last << 4 )&0xf0 ) | ( ( c >> 2 )&0x0f ); + i_level++; + break; + case 3: + *p_dst++ = ( ( i_last &0x03 ) << 6 ) | c; + i_level = 0; + } + i_last = c; + } + + return p_dst - p_start; +} +size_t vlc_b64_decode_binary( uint8_t **pp_dst, const char *psz_src ) +{ + const int i_src = strlen( psz_src ); + uint8_t *p_dst; + + *pp_dst = p_dst = malloc( i_src ); + if( !p_dst ) + return 0; + return vlc_b64_decode_binary_to_buffer( p_dst, i_src, psz_src ); +} +char *vlc_b64_decode( const char *psz_src ) +{ + const int i_src = strlen( psz_src ); + char *p_dst = malloc( i_src + 1 ); + size_t i_dst; + if( !p_dst ) + return NULL; + + i_dst = vlc_b64_decode_binary_to_buffer( (uint8_t*)p_dst, i_src, psz_src ); + p_dst[i_dst] = '\0'; + + return p_dst; +} + +/**************************************************************************** + * String formating functions + ****************************************************************************/ +char *str_format_time( const char *tformat ) +{ + char buffer[255]; + time_t curtime; + struct tm loctime; + + /* Get the current time. */ + curtime = time( NULL ); + + /* Convert it to local time representation. */ + localtime_r( &curtime, &loctime ); + strftime( buffer, 255, tformat, &loctime ); + return strdup( buffer ); +} + +#define INSERT_STRING( string ) \ + if( string != NULL ) \ + { \ + int len = strlen( string ); \ + dst = realloc( dst, i_size = i_size + len );\ + memcpy( (dst+d), string, len ); \ + d += len; \ + free( string ); \ + } \ + else \ + { \ + *(dst+d) = '-'; \ + d++; \ + } \ + +/* same than INSERT_STRING, except that string won't be freed */ +#define INSERT_STRING_NO_FREE( string ) \ + { \ + int len = strlen( string ); \ + dst = realloc( dst, i_size = i_size + len );\ + memcpy( dst+d, string, len ); \ + d += len; \ + } +char *__str_format_meta( vlc_object_t *p_object, const char *string ) +{ + const char *s = string; + int b_is_format = 0; + int b_empty_if_na = 0; + char buf[10]; + int i_size = strlen( string ) + 1; /* +1 to store '\0' */ + char *dst = strdup( string ); + if( !dst ) return NULL; + int d = 0; + + playlist_t *p_playlist = pl_Yield( p_object ); + input_thread_t *p_input = playlist_CurrentInput( p_playlist ); + input_item_t *p_item = NULL; + pl_Release( p_object ); + if( p_input ) + { + p_item = input_GetItem(p_input); + } + + while( *s ) + { + if( b_is_format ) + { + switch( *s ) + { + case 'a': + if( p_item ) + { + INSERT_STRING( input_item_GetArtist( p_item ) ); + } + break; + case 'b': + if( p_item ) + { + INSERT_STRING( input_item_GetAlbum( p_item ) ); + } + break; + case 'c': + if( p_item ) + { + INSERT_STRING( input_item_GetCopyright( p_item ) ); + } + break; + case 'd': + if( p_item ) + { + INSERT_STRING( input_item_GetDescription( p_item ) ); + } + break; + case 'e': + if( p_item ) + { + INSERT_STRING( input_item_GetEncodedBy( p_item ) ); + } + break; + case 'f': + if( p_item && p_item->p_stats ) + { + snprintf( buf, 10, "%d", + p_item->p_stats->i_displayed_pictures ); + } + else + { + sprintf( buf, b_empty_if_na ? "" : "-" ); + } + INSERT_STRING_NO_FREE( buf ); + break; + case 'g': + if( p_item ) + { + INSERT_STRING( input_item_GetGenre( p_item ) ); + } + break; + case 'l': + if( p_item ) + { + INSERT_STRING( input_item_GetLanguage( p_item ) ); + } + break; + case 'n': + if( p_item ) + { + INSERT_STRING( input_item_GetTrackNum( p_item ) ); + } + break; + case 'p': + if( p_item ) + { + INSERT_STRING( input_item_GetNowPlaying( p_item ) ); + } + break; + case 'r': + if( p_item ) + { + INSERT_STRING( input_item_GetRating( p_item ) ); + } + break; + case 's': + { + char *lang = NULL; + if( p_input ) + lang = var_GetNonEmptyString( p_input, "sub-language" ); + if( lang == NULL ) + lang = strdup( b_empty_if_na ? "" : "-" ); + INSERT_STRING( lang ); + break; + } + case 't': + if( p_item ) + { + INSERT_STRING( input_item_GetTitle( p_item ) ); + } + break; + case 'u': + if( p_item ) + { + INSERT_STRING( input_item_GetURL( p_item ) ); + } + break; + case 'A': + if( p_item ) + { + INSERT_STRING( input_item_GetDate( p_item ) ); + } + break; + case 'B': + if( p_input ) + { + snprintf( buf, 10, "%d", + var_GetInteger( p_input, "bit-rate" )/1000 ); + } + else + { + sprintf( buf, b_empty_if_na ? "" : "-" ); + } + INSERT_STRING_NO_FREE( buf ); + break; + case 'C': + if( p_input ) + { + snprintf( buf, 10, "%d", + var_GetInteger( p_input, "chapter" ) ); + } + else + { + sprintf( buf, b_empty_if_na ? "" : "-" ); + } + INSERT_STRING_NO_FREE( buf ); + break; + case 'D': + if( p_item ) + { + mtime_t i_duration = input_item_GetDuration( p_item ); + sprintf( buf, "%02d:%02d:%02d", + (int)(i_duration/(3600000000)), + (int)((i_duration/(60000000))%60), + (int)((i_duration/1000000)%60) ); + } + else + { + sprintf( buf, b_empty_if_na ? "" : "--:--:--" ); + } + INSERT_STRING_NO_FREE( buf ); + break; + case 'F': + if( p_item ) + { + INSERT_STRING( input_item_GetURI( p_item ) ); + } + break; + case 'I': + if( p_input ) + { + snprintf( buf, 10, "%d", + var_GetInteger( p_input, "title" ) ); + } + else + { + sprintf( buf, b_empty_if_na ? "" : "-" ); + } + INSERT_STRING_NO_FREE( buf ); + break; + case 'L': + if( p_item && p_input ) + { + mtime_t i_duration = input_item_GetDuration( p_item ); + int64_t i_time = p_input->i_time; + sprintf( buf, "%02d:%02d:%02d", + (int)( ( i_duration - i_time ) / 3600000000 ), + (int)( ( ( i_duration - i_time ) / 60000000 ) % 60 ), + (int)( ( ( i_duration - i_time ) / 1000000 ) % 60 ) ); + } + else + { + sprintf( buf, b_empty_if_na ? "" : "--:--:--" ); + } + INSERT_STRING_NO_FREE( buf ); + break; + case 'N': + if( p_item ) + { + INSERT_STRING( input_item_GetName( p_item ) ); + } + break; + case 'O': + { + char *lang = NULL; + if( p_input ) + lang = var_GetNonEmptyString( p_input, + "audio-language" ); + if( lang == NULL ) + lang = strdup( b_empty_if_na ? "" : "-" ); + INSERT_STRING( lang ); + break; + } + case 'P': + if( p_input ) + { + snprintf( buf, 10, "%2.1lf", + var_GetFloat( p_input, "position" ) * 100. ); + } + else + { + sprintf( buf, b_empty_if_na ? "" : "--.-%%" ); + } + INSERT_STRING_NO_FREE( buf ); + break; + case 'R': + if( p_input ) + { + int r = var_GetInteger( p_input, "rate" ); + snprintf( buf, 10, "%d.%d", r/1000, r%1000 ); + } + else + { + sprintf( buf, b_empty_if_na ? "" : "-" ); + } + INSERT_STRING_NO_FREE( buf ); + break; + case 'S': + if( p_input ) + { + int r = var_GetInteger( p_input, "sample-rate" ); + snprintf( buf, 10, "%d.%d", r/1000, (r/100)%10 ); + } + else + { + sprintf( buf, b_empty_if_na ? "" : "-" ); + } + INSERT_STRING_NO_FREE( buf ); + break; + case 'T': + if( p_input ) + { + sprintf( buf, "%02d:%02d:%02d", + (int)( p_input->i_time / ( 3600000000 ) ), + (int)( ( p_input->i_time / ( 60000000 ) ) % 60 ), + (int)( ( p_input->i_time / 1000000 ) % 60 ) ); + } + else + { + sprintf( buf, b_empty_if_na ? "" : "--:--:--" ); + } + INSERT_STRING_NO_FREE( buf ); + break; + case 'U': + if( p_item ) + { + INSERT_STRING( input_item_GetPublisher( p_item ) ); + } + break; + case 'V': + { + audio_volume_t volume; + aout_VolumeGet( p_object, &volume ); + snprintf( buf, 10, "%d", volume ); + INSERT_STRING_NO_FREE( buf ); + break; + } + case '_': + *(dst+d) = '\n'; + d++; + break; + + case ' ': + b_empty_if_na = 1; + break; + + default: + *(dst+d) = *s; + d++; + break; + } + if( *s != ' ' ) + b_is_format = 0; + } + else if( *s == '$' ) + { + b_is_format = 1; + b_empty_if_na = 0; + } + else + { + *(dst+d) = *s; + d++; + } + s++; + } + *(dst+d) = '\0'; + + if( p_input ) + vlc_object_release( p_input ); + + return dst; +} + +/** + * Apply str format time and str format meta + */ +char *__str_format( vlc_object_t *p_this, const char *psz_src ) +{ + char *psz_buf1, *psz_buf2; + psz_buf1 = str_format_time( psz_src ); + psz_buf2 = str_format_meta( p_this, psz_buf1 ); + free( psz_buf1 ); + return psz_buf2; +} + +/** + * Remove forbidden characters from filenames (including slashes) + */ +void filename_sanitize( char *str ) +{ + if( *str == '.' && (str[1] == '\0' || (str[1] == '.' && str[2] == '\0' ) ) ) + { + while( *str ) + { + *str = '_'; + str++; + } + return; + } + + while( *str ) + { + switch( *str ) + { + case '/': +#if defined( __APPLE__ ) + case ':': +#elif defined( WIN32 ) + case '\\': + case '*': + case '"': + case '?': + case ':': + case '|': + case '<': + case '>': +#endif + *str = '_'; + } + str++; + } +} + +/** + * Remove forbidden characters from full paths (leaves slashes) + */ +void path_sanitize( char *str ) +{ +#if 0 + /* + * Uncomment the two blocks to prevent /../ or /./, i'm not sure that we + * want to. + */ + char *prev = str - 1; +#endif +#ifdef WIN32 + /* check drive prefix if path is absolute */ + if( isalpha(*str) && (':' == *(str+1)) ) + str += 2; +#endif + while( *str ) + { +#if defined( __APPLE__ ) + if( *str == ':' ) + *str = '_'; +#elif defined( WIN32 ) + switch( *str ) + { + case '*': + case '"': + case '?': + case ':': + case '|': + case '<': + case '>': + *str = '_'; + } +#endif +#if 0 + if( *str == '/' +#ifdef WIN32 + || *str == '\\' +#endif + ) + { + if( str - prev == 2 && prev[1] == '.' ) + { + prev[1] = '.'; + } + else if( str - prev == 3 && prev[1] == '.' && prev[2] == '.' ) + { + prev[1] = '_'; + prev[2] = '_'; + } + prev = str; + } +#endif + str++; + } +} diff --git a/VLC/text/unicode.c b/VLC/text/unicode.c new file mode 100644 index 0000000..496fba1 --- /dev/null +++ b/VLC/text/unicode.c @@ -0,0 +1,809 @@ +/***************************************************************************** + * unicode.c: Unicode <-> locale functions + ***************************************************************************** + * Copyright (C) 2005-2006 the VideoLAN team + * Copyright © 2005-2008 Rémi Denis-Courmont + * + * Authors: Rémi Denis-Courmont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "vlc_charset.h" +#include "libvlc.h" /* utf8_mkdir */ + +#include + +#include +#include +#include +#include +#include +#ifdef HAVE_DIRENT_H +# include +#endif +#ifdef UNDER_CE +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef HAVE_FCNTL_H +# include +#endif +#ifdef WIN32 +# include +#else +# include +#endif + +#ifndef HAVE_LSTAT +# define lstat( a, b ) stat(a, b) +#endif + +#ifdef __APPLE__ +/* Define this if the OS always use UTF-8 internally */ +# define ASSUME_UTF8 1 +#endif + +#if defined (ASSUME_UTF8) +/* Cool */ +#elif defined (WIN32) || defined (UNDER_CE) +# define USE_MB2MB 1 +#elif defined (HAVE_ICONV) +# define USE_ICONV 1 +#else +# error No UTF8 charset conversion implemented on this platform! +#endif + +#if defined (USE_ICONV) +# include +static char charset[sizeof ("CSISO11SWEDISHFORNAMES")] = ""; + +static void find_charset_once (void) +{ + strlcpy (charset, nl_langinfo (CODESET), sizeof (charset)); + if (!strcasecmp (charset, "ASCII") + || !strcasecmp (charset, "ANSI_X3.4-1968")) + strcpy (charset, "UTF-8"); /* superset... */ +} + +static int find_charset (void) +{ + static pthread_once_t once = PTHREAD_ONCE_INIT; + pthread_once (&once, find_charset_once); + return !strcasecmp (charset, "UTF-8"); +} +#endif + + +static char *locale_fast (const char *string, bool from) +{ +#if defined (USE_ICONV) + if (find_charset ()) + return (char *)string; + + vlc_iconv_t hd = vlc_iconv_open (from ? "UTF-8" : charset, + from ? charset : "UTF-8"); + if (hd == (vlc_iconv_t)(-1)) + return NULL; /* Uho! */ + + const char *iptr = string; + size_t inb = strlen (string); + size_t outb = inb * 6 + 1; + char output[outb], *optr = output; + + if (string == NULL) + return NULL; + + while (vlc_iconv (hd, &iptr, &inb, &optr, &outb) == (size_t)(-1)) + { + *optr++ = '?'; + outb--; + iptr++; + inb--; + vlc_iconv (hd, NULL, NULL, NULL, NULL); /* reset */ + } + *optr = '\0'; + vlc_iconv_close (hd); + + assert (inb == 0); + assert (*iptr == '\0'); + assert (*optr == '\0'); + assert (strlen (output) == (size_t)(optr - output)); + return strdup (output); +#elif defined (USE_MB2MB) + char *out; + int len; + + if (string == NULL) + return NULL; + + len = 1 + MultiByteToWideChar (from ? CP_ACP : CP_UTF8, + 0, string, -1, NULL, 0); + wchar_t wide[len]; + + MultiByteToWideChar (from ? CP_ACP : CP_UTF8, 0, string, -1, wide, len); + len = 1 + WideCharToMultiByte (from ? CP_UTF8 : CP_ACP, 0, wide, -1, + NULL, 0, NULL, NULL); + out = malloc (len); + if (out == NULL) + return NULL; + + WideCharToMultiByte (from ? CP_UTF8 : CP_ACP, 0, wide, -1, out, len, + NULL, NULL); + return out; +#else + (void)from; + return (char *)string; +#endif +} + + +static inline char *locale_dup (const char *string, bool from) +{ + assert( string ); + +#if defined (USE_ICONV) + if (find_charset ()) + return strdup (string); + return locale_fast (string, from); +#elif defined (USE_MB2MB) + return locale_fast (string, from); +#else + (void)from; + return strdup (string); +#endif +} + +/** + * Releases (if needed) a localized or uniformized string. + * @param str non-NULL return value from FromLocale() or ToLocale(). + */ +void LocaleFree (const char *str) +{ +#if defined (USE_ICONV) + if (!find_charset ()) + free ((char *)str); +#elif defined (USE_MB2MB) + free ((char *)str); +#else + (void)str; +#endif +} + + +/** + * Converts a string from the system locale character encoding to UTF-8. + * + * @param locale nul-terminated string to convert + * + * @return a nul-terminated UTF-8 string, or NULL in case of error. + * To avoid memory leak, you have to pass the result to LocaleFree() + * when it is no longer needed. + */ +char *FromLocale (const char *locale) +{ + return locale_fast (locale, true); +} + +/** + * converts a string from the system locale character encoding to utf-8, + * the result is always allocated on the heap. + * + * @param locale nul-terminated string to convert + * + * @return a nul-terminated utf-8 string, or null in case of error. + * The result must be freed using free() - as with the strdup() function. + */ +char *FromLocaleDup (const char *locale) +{ + return locale_dup (locale, true); +} + + +/** + * ToLocale: converts an UTF-8 string to local system encoding. + * + * @param utf8 nul-terminated string to be converted + * + * @return a nul-terminated string, or NULL in case of error. + * To avoid memory leak, you have to pass the result to LocaleFree() + * when it is no longer needed. + */ +char *ToLocale (const char *utf8) +{ + return locale_fast (utf8, false); +} + + +/** + * converts a string from UTF-8 to the system locale character encoding, + * the result is always allocated on the heap. + * + * @param utf8 nul-terminated string to convert + * + * @return a nul-terminated string, or null in case of error. + * The result must be freed using free() - as with the strdup() function. + */ +char *ToLocaleDup (const char *utf8) +{ + return locale_dup (utf8, false); +} + + +/** + * Opens a system file handle using UTF-8 paths. + * + * @param filename file path to open (with UTF-8 encoding) + * @param flags open() flags, see the C library open() documentation + * @param mode file permissions if creating a new file + * @return a file handle on success, -1 on error (see errno). + */ +int utf8_open (const char *filename, int flags, mode_t mode) +{ +#if defined (WIN32) || defined (UNDER_CE) + if (GetVersion() < 0x80000000) + { + /* for Windows NT and above */ + wchar_t wpath[MAX_PATH + 1]; + + if (!MultiByteToWideChar (CP_UTF8, 0, filename, -1, wpath, MAX_PATH)) + { + errno = ENOENT; + return -1; + } + wpath[MAX_PATH] = L'\0'; + + /* + * open() cannot open files with non-“ANSI” characters on Windows. + * We use _wopen() instead. Same thing for mkdir() and stat(). + */ + return _wopen (wpath, flags, mode); + } +#endif + const char *local_name = ToLocale (filename); + + if (local_name == NULL) + { + errno = ENOENT; + return -1; + } + + int fd = open (local_name, flags, mode); + LocaleFree (local_name); + return fd; +} + +/** + * Opens a FILE pointer using UTF-8 filenames. + * @param filename file path, using UTF-8 encoding + * @param mode fopen file open mode + * @return NULL on error, an open FILE pointer on success. + */ +FILE *utf8_fopen (const char *filename, const char *mode) +{ + int rwflags = 0, oflags = 0; + bool append = false; + + for (const char *ptr = mode; *ptr; ptr++) + { + switch (*ptr) + { + case 'r': + rwflags = O_RDONLY; + break; + + case 'a': + rwflags = O_WRONLY; + oflags |= O_CREAT; + append = true; + break; + + case 'w': + rwflags = O_WRONLY; + oflags |= O_CREAT | O_TRUNC; + break; + + case '+': + rwflags = O_RDWR; + break; + +#ifdef O_TEXT + case 't': + oflags |= O_TEXT; + break; +#endif + } + } + + int fd = utf8_open (filename, rwflags | oflags, 0666); + if (fd == -1) + return NULL; + + if (append && (lseek (fd, 0, SEEK_END) == -1)) + { + close (fd); + return NULL; + } + + FILE *stream = fdopen (fd, mode); + if (stream == NULL) + close (fd); + + return stream; +} + +/** + * Creates a directory using UTF-8 paths. + * + * @param dirname a UTF-8 string with the name of the directory that you + * want to create. + * @param mode directory permissions + * @return 0 on success, -1 on error (see errno). + */ +int utf8_mkdir( const char *dirname, mode_t mode ) +{ +#if defined (UNDER_CE) || defined (WIN32) + VLC_UNUSED( mode ); + + wchar_t wname[MAX_PATH + 1]; + char mod[MAX_PATH + 1]; + int i; + + /* Convert '/' into '\' */ + for( i = 0; *dirname; i++ ) + { + if( i == MAX_PATH ) + return -1; /* overflow */ + + if( *dirname == '/' ) + mod[i] = '\\'; + else + mod[i] = *dirname; + dirname++; + + } + mod[i] = 0; + + if( MultiByteToWideChar( CP_UTF8, 0, mod, -1, wname, MAX_PATH ) == 0 ) + { + errno = ENOENT; + return -1; + } + wname[MAX_PATH] = L'\0'; + + if( CreateDirectoryW( wname, NULL ) == 0 ) + { + if( GetLastError( ) == ERROR_ALREADY_EXISTS ) + errno = EEXIST; + else + errno = ENOENT; + return -1; + } + return 0; +#else + char *locname = ToLocale( dirname ); + int res; + + if( locname == NULL ) + { + errno = ENOENT; + return -1; + } + res = mkdir( locname, mode ); + + LocaleFree( locname ); + return res; +#endif +} + +/** + * Opens a DIR pointer using UTF-8 paths + * + * @param dirname UTF-8 representation of the directory name + * @return a pointer to the DIR struct, or NULL in case of error. + * Release with standard closedir(). + */ +DIR *utf8_opendir( const char *dirname ) +{ +#ifdef WIN32 + wchar_t wname[MAX_PATH + 1]; + + if (MultiByteToWideChar (CP_UTF8, 0, dirname, -1, wname, MAX_PATH)) + { + wname[MAX_PATH] = L'\0'; + return (DIR *)vlc_wopendir (wname); + } +#else + const char *local_name = ToLocale( dirname ); + + if( local_name != NULL ) + { + DIR *dir = opendir( local_name ); + LocaleFree( local_name ); + return dir; + } +#endif + + errno = ENOENT; + return NULL; +} + +/** + * Reads the next file name from an open directory. + * + * @param dir The directory that is being read + * + * @return a UTF-8 string of the directory entry. + * Use free() to free this memory. + */ +char *utf8_readdir( DIR *dir ) +{ +#ifdef WIN32 + struct _wdirent *ent = vlc_wreaddir (dir); + if (ent == NULL) + return NULL; + + return FromWide (ent->d_name); +#else + struct dirent *ent; + + ent = readdir( (DIR *)dir ); + if( ent == NULL ) + return NULL; + + return vlc_fix_readdir( ent->d_name ); +#endif +} + +static int dummy_select( const char *str ) +{ + (void)str; + return 1; +} + +/** + * Does the same as utf8_scandir(), but takes an open directory pointer + * instead of a directory path. + */ +int utf8_loaddir( DIR *dir, char ***namelist, + int (*select)( const char * ), + int (*compar)( const char **, const char ** ) ) +{ + if( select == NULL ) + select = dummy_select; + + if( dir == NULL ) + return -1; + else + { + char **tab = NULL; + char *entry; + unsigned num = 0; + + rewinddir( dir ); + + while( ( entry = utf8_readdir( dir ) ) != NULL ) + { + char **newtab; + + if( !select( entry ) ) + { + free( entry ); + continue; + } + + newtab = realloc( tab, sizeof( char * ) * (num + 1) ); + if( newtab == NULL ) + { + free( entry ); + goto error; + } + tab = newtab; + tab[num++] = entry; + } + + if( compar != NULL ) + qsort( tab, num, sizeof( tab[0] ), + (int (*)( const void *, const void *))compar ); + + *namelist = tab; + return num; + + error:{ + unsigned i; + + for( i = 0; i < num; i++ ) + free( tab[i] ); + if( tab != NULL ) + free( tab ); + } + } + return -1; +} + +/** + * Selects file entries from a directory, as GNU C scandir(), yet using + * UTF-8 file names. + * + * @param dirname UTF-8 diretory path + * @param pointer [OUT] pointer set, on succesful completion, to the address + * of a table of UTF-8 filenames. All filenames must be freed with free(). + * The table itself must be freed with free() as well. + * + * @return How many file names were selected (possibly 0), + * or -1 in case of error. + */ +int utf8_scandir( const char *dirname, char ***namelist, + int (*select)( const char * ), + int (*compar)( const char **, const char ** ) ) +{ + DIR *dir = utf8_opendir (dirname); + int val = -1; + + if (dir != NULL) + { + val = utf8_loaddir (dir, namelist, select, compar); + closedir (dir); + } + return val; +} + +static int utf8_statEx( const char *filename, struct stat *buf, + bool deref ) +{ +#if defined (WIN32) || defined (UNDER_CE) + /* retrieve Windows OS version */ + if( GetVersion() < 0x80000000 ) + { + /* for Windows NT and above */ + wchar_t wpath[MAX_PATH + 1]; + + if( !MultiByteToWideChar( CP_UTF8, 0, filename, -1, wpath, MAX_PATH ) ) + { + errno = ENOENT; + return -1; + } + wpath[MAX_PATH] = L'\0'; + + return _wstati64( wpath, buf ); + } +#endif +#ifdef HAVE_SYS_STAT_H + const char *local_name = ToLocale( filename ); + + if( local_name != NULL ) + { + int res = deref ? stat( local_name, buf ) + : lstat( local_name, buf ); + LocaleFree( local_name ); + return res; + } + errno = ENOENT; +#endif + return -1; +} + +/** + * Finds file/inode informations, as stat(). + * Consider usign fstat() instead, if possible. + * + * @param filename UTF-8 file path + */ +int utf8_stat( const char *filename, struct stat *buf) +{ + return utf8_statEx( filename, buf, true ); +} + +/** + * Finds file/inode informations, as lstat(). + * Consider usign fstat() instead, if possible. + * + * @param filename UTF-8 file path + */ +int utf8_lstat( const char *filename, struct stat *buf) +{ + return utf8_statEx( filename, buf, false ); +} + +/** + * Removes a file. + * + * @param filename a UTF-8 string with the name of the file you want to delete. + * @return A 0 return value indicates success. A -1 return value indicates an + * error, and an error code is stored in errno + */ +int utf8_unlink( const char *filename ) +{ +#if defined (WIN32) || defined (UNDER_CE) + if( GetVersion() < 0x80000000 ) + { + /* for Windows NT and above */ + wchar_t wpath[MAX_PATH + 1]; + + if( !MultiByteToWideChar( CP_UTF8, 0, filename, -1, wpath, MAX_PATH ) ) + { + errno = ENOENT; + return -1; + } + wpath[MAX_PATH] = L'\0'; + + /* + * unlink() cannot open files with non-“ANSI” characters on Windows. + * We use _wunlink() instead. + */ + return _wunlink( wpath ); + } +#endif + const char *local_name = ToLocale( filename ); + + if( local_name == NULL ) + { + errno = ENOENT; + return -1; + } + + int ret = unlink( local_name ); + LocaleFree( local_name ); + return ret; +} + + + +/** + * Formats an UTF-8 string as vasprintf(), then print it to stdout, with + * appropriate conversion to local encoding. + */ +static int utf8_vasprintf( char **str, const char *fmt, va_list ap ) +{ + char *utf8; + int res = vasprintf( &utf8, fmt, ap ); + if( res == -1 ) + return -1; + + *str = ToLocaleDup( utf8 ); + free( utf8 ); + return res; +} + +/** + * Formats an UTF-8 string as vfprintf(), then print it, with + * appropriate conversion to local encoding. + */ +int utf8_vfprintf( FILE *stream, const char *fmt, va_list ap ) +{ + char *str; + int res = utf8_vasprintf( &str, fmt, ap ); + if( res == -1 ) + return -1; + + fputs( str, stream ); + free( str ); + return res; +} + +/** + * Formats an UTF-8 string as fprintf(), then print it, with + * appropriate conversion to local encoding. + */ +int utf8_fprintf( FILE *stream, const char *fmt, ... ) +{ + va_list ap; + int res; + + va_start( ap, fmt ); + res = utf8_vfprintf( stream, fmt, ap ); + va_end( ap ); + return res; +} + + +static char *CheckUTF8( char *str, char rep ) +{ + uint8_t *ptr = (uint8_t *)str; + assert (str != NULL); + + for (;;) + { + uint8_t c = ptr[0]; + int charlen = -1; + + if (c == '\0') + break; + + for (int i = 0; i < 7; i++) + if ((c >> (7 - i)) == ((0xff >> (7 - i)) ^ 1)) + { + charlen = i; + break; + } + + switch (charlen) + { + case 0: // 7-bit ASCII character -> OK + ptr++; + continue; + + case -1: // 1111111x -> error + case 1: // continuation byte -> error + goto error; + } + + assert (charlen >= 2); + + uint32_t cp = c & ~((0xff >> (7 - charlen)) << (7 - charlen)); + for (int i = 1; i < charlen; i++) + { + assert (cp < (1 << 26)); + c = ptr[i]; + + if ((c == '\0') // unexpected end of string + || ((c >> 6) != 2)) // not a continuation byte + goto error; + + cp = (cp << 6) | (ptr[i] & 0x3f); + } + + if (cp < 128) // overlong (special case for ASCII) + goto error; + if (cp < (1u << (5 * charlen - 3))) // overlong + goto error; + + ptr += charlen; + continue; + + error: + if (rep == 0) + return NULL; + *ptr++ = rep; + str = NULL; + } + + return str; +} + +/** + * Replaces invalid/overlong UTF-8 sequences with question marks. + * Note that it is not possible to convert from Latin-1 to UTF-8 on the fly, + * so we don't try that, even though it would be less disruptive. + * + * @return str if it was valid UTF-8, NULL if not. + */ +char *EnsureUTF8( char *str ) +{ + return CheckUTF8( str, '?' ); +} + + +/** + * Checks whether a string is a valid UTF-8 byte sequence. + * + * @param str nul-terminated string to be checked + * + * @return str if it was valid UTF-8, NULL if not. + */ +const char *IsUTF8( const char *str ) +{ + return CheckUTF8( (char *)str, 0 ); +} diff --git a/VLC/text/wincp.c b/VLC/text/wincp.c new file mode 100644 index 0000000..004d990 --- /dev/null +++ b/VLC/text/wincp.c @@ -0,0 +1,227 @@ +/***************************************************************************** + * wincp.c: Guessing "local" ANSI code page on Microsoft Windows® + ***************************************************************************** + * + * Copyright © 2006-2007 Rémi Denis-Courmont + * $Id$ + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/*** We need your help to complete this file!! Look for FIXME ***/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#ifndef WIN32 +# include +#else +# include +#endif + +#ifdef __APPLE__ +# include +# include +#endif + +#include "vlc_charset.h" + + +#ifndef WIN32 /* should work on Win32, but useless */ +static inline int locale_match (const char *tab, const char *locale) +{ + for (;*tab; tab += 2) + if (memcmp (tab, locale, 2) == 0) + return 0; + return 1; +} + + +/** + * @return a fallback characters encoding to be used, given a locale. + */ +static const char *FindFallbackEncoding (const char *locale) +{ + if ((locale == NULL) || (strlen (locale) < 2) + || !strcasecmp (locale, "POSIX")) + return "CP1252"; /* Yeah, this is totally western-biased */ + + + /*** The ISO-8859 series (anything but Asia) ***/ + // Latin-1 Western-European languages (ISO-8859-1) + static const char western[] = + "aa" "af" "an" "br" "ca" "da" "de" "en" "es" "et" "eu" "fi" "fo" "fr" + "ga" "gd" "gl" "gv" "id" "is" "it" "kl" "kw" "mg" "ms" "nb" "nl" "nn" + "no" "oc" "om" "pt" "so" "sq" "st" "sv" "tl" "uz" "wa" "xh" "zu" + "eo" "mt" "cy"; + if (!locale_match (western, locale)) + return "CP1252"; // Compatible Microsoft superset + + // Latin-2 Slavic languages (ISO-8859-2) + static const char slavic[] = "bs" "cs" "hr" "hu" "pl" "ro" "sk" "sl"; + if (!locale_match (slavic, locale)) + return "CP1250"; // CP1250 is more common, but incompatible + + // Latin-3 Southern European languages (ISO-8859-3) + // "eo" and "mt" -> Latin-1 instead, I presume(?). + // "tr" -> ISO-8859-9 instead + + // Latin-4 North-European languages (ISO-8859-4) + // -> Latin-1 instead + + /* Cyrillic alphabet languages (ISO-8859-5) */ + static const char cyrillic[] = "be" "bg" "mk" "ru" "sr"; + if (!locale_match (cyrillic, locale)) + return "CP1251"; // KOI8, ISO-8859-5 and CP1251 are incompatible(?) + + /* Arabic (ISO-8859-6) */ + if (!locale_match ("ar", locale)) + // FIXME: someone check if we should return CP1256 or ISO-8859-6 + return "CP1256"; // CP1256 is(?) more common, but incompatible(?) + + /* Greek (ISO-8859-7) */ + if (!locale_match ("el", locale)) + // FIXME: someone check if we should return CP1253 or ISO-8859-7 + return "CP1253"; // CP1253 is(?) more common and less incompatible + + /* Hebrew (ISO-8859-8) */ + if (!locale_match ("he" "iw" "yi", locale)) + return "ISO-8859-8"; // CP1255 is reportedly screwed up + + /* Latin-5 Turkish (ISO-8859-9) */ + if (!locale_match ("tr" "ku", locale)) + return "CP1254"; // Compatible Microsoft superset + + /* Latin-6 “North-European” languages (ISO-8859-10) */ + /* It is so much north European that glibc only uses that for Luganda + * which is spoken in Uganda... unless someone complains, I'm not + * using this one; let's fallback to CP1252 here. */ + + // ISO-8859-11 does arguably not exist. Thai is handled below. + + // ISO-8859-12 really doesn't exist. + + // Latin-7 Baltic languages (ISO-8859-13) + if (!locale_match ("lt" "lv" "mi", locale)) + // FIXME: mi = New Zealand, doesn't sound baltic! + return "CP1257"; // Compatible Microsoft superset + + // Latin-8 Celtic languages (ISO-8859-14) + // "cy" -> use Latin-1 instead (most likely English or French) + + // Latin-9 (ISO-8859-15) -> see Latin-1 + + // Latin-10 (ISO-8859-16) does not seem to be used + + /*** KOI series ***/ + // For Russian, we use CP1251 + if (!locale_match ("uk", locale)) + return "KOI8-U"; + + if (!locale_match ("tg", locale)) + return "KOI8-T"; + + /*** Asia ***/ + // Japanese + if (!locale_match ("jp", locale)) + return "SHIFT-JIS"; // Shift-JIS is way more common than EUC-JP + + // Korean + if (!locale_match ("ko", locale)) + return "EUC-KR"; + + // Thai + if (!locale_match ("th", locale)) + return "TIS-620"; + + // Vietnamese (FIXME: more infos needed) + if (!locale_match ("vt", locale)) + /* VISCII is probably a bad idea as it is not extended ASCII */ + /* glibc has TCVN5712-1 */ + return "CP1258"; + + /* Kazakh (FIXME: more infos needed) */ + if (!locale_match ("kk", locale)) + return "PT154"; + + // Chinese. The politically incompatible character sets. + if (!locale_match ("zh", locale)) + { + if ((strlen (locale) >= 5) && (locale[2] != '_')) + locale += 3; + + // Hong Kong + if (!locale_match ("HK", locale)) + return "BIG5-HKSCS"; /* FIXME: use something else? */ + + // Taiwan island + if (!locale_match ("TW", locale)) + return "BIG5"; + + // People's Republic of China and Singapore + /* + * GB18030 can represent any Unicode code point + * (like UTF-8), while remaining compatible with GBK + * FIXME: is it compatible with GB2312? if not, should we + * use GB2312 instead? + */ + return "GB18030"; + } + + return "ASCII"; +} +#endif + +/** + * GetFallbackEncoding() suggests an encoding to be used for non UTF-8 + * text files accord to the system's local settings. It is only a best + * guess. + */ +const char *GetFallbackEncoding( void ) +{ +#ifndef WIN32 + const char *psz_lang; + + psz_lang = getenv ("LC_ALL"); + if ((psz_lang == NULL) || !*psz_lang) + { + psz_lang = getenv ("LC_CTYPE"); + if ((psz_lang == NULL) || !*psz_lang) + psz_lang = getenv ("LANG"); + } + + return FindFallbackEncoding (psz_lang); +#else + static char buf[16] = ""; + + if (buf[0] == 0) + { + int cp = GetACP (); + + switch (cp) + { + case 1255: // Hebrew, CP1255 screws up somewhat + strcpy (buf, "ISO-8859-8"); + break; + default: + snprintf (buf, sizeof (buf), "CP%u", cp); + } + } + return buf; +#endif +} diff --git a/VLC/thread.c b/VLC/thread.c new file mode 100644 index 0000000..a28f934 --- /dev/null +++ b/VLC/thread.c @@ -0,0 +1,204 @@ +/***************************************************************************** + * thread.c : Playlist management functions + ***************************************************************************** + * Copyright © 1999-2008 the VideoLAN team + * $Id$ + * + * Authors: Samuel Hocevar + * Clément Stenac + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "vlc_es.h" +#include "vlc_input.h" +#include "vlc_interface.h" +#include "vlc_playlist.h" +#include "playlist_internal.h" + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +static void* RunControlThread ( vlc_object_t * ); +static void* RunPreparse ( vlc_object_t * ); +static void* RunFetcher ( vlc_object_t * ); +static void PreparseDestructor ( vlc_object_t * ); +static void FetcherDestructor ( vlc_object_t * ); + +/***************************************************************************** + * Main functions for the global thread + *****************************************************************************/ + +/** + * Create the main playlist thread + * Additionally to the playlist, this thread controls : + * - Statistics + * - VLM + * \param p_parent + * \return an object with a started thread + */ +void __playlist_ThreadCreate( vlc_object_t *p_parent ) +{ + playlist_t *p_playlist = playlist_Create( p_parent ); + if( !p_playlist ) return; + + // Preparse + static const char ppname[] = "preparser"; + p_playlist->p_preparse = + vlc_custom_create( p_playlist, sizeof( playlist_preparse_t ), + VLC_OBJECT_GENERIC, ppname ); + if( !p_playlist->p_preparse ) + { + msg_Err( p_playlist, "unable to create preparser" ); + vlc_object_release( p_playlist ); + return; + } + p_playlist->p_preparse->psz_object_name = strdup( "preparser" ); + p_playlist->p_preparse->i_waiting = 0; + p_playlist->p_preparse->pp_waiting = NULL; + + vlc_object_set_destructor( p_playlist->p_preparse, PreparseDestructor ); + + vlc_object_attach( p_playlist->p_preparse, p_playlist ); + if( vlc_thread_create( p_playlist->p_preparse, "preparser", + RunPreparse, VLC_THREAD_PRIORITY_LOW, true ) ) + { + msg_Err( p_playlist, "cannot spawn preparse thread" ); + vlc_object_release( p_playlist->p_preparse ); + return; + } + + // Secondary Preparse + static const char fname[] = "fetcher"; + p_playlist->p_fetcher = + vlc_custom_create( p_playlist, sizeof( playlist_fetcher_t ), + VLC_OBJECT_GENERIC, fname ); + if( !p_playlist->p_fetcher ) + { + msg_Err( p_playlist, "unable to create secondary preparser" ); + vlc_object_release( p_playlist ); + return; + } + p_playlist->p_fetcher->psz_object_name = strdup( "fetcher" ); + p_playlist->p_fetcher->i_waiting = 0; + p_playlist->p_fetcher->pp_waiting = NULL; + p_playlist->p_fetcher->i_art_policy = var_CreateGetInteger( p_playlist, + "album-art" ); + + vlc_object_set_destructor( p_playlist->p_fetcher, FetcherDestructor ); + + vlc_object_attach( p_playlist->p_fetcher, p_playlist ); + if( vlc_thread_create( p_playlist->p_fetcher, + "fetcher", + RunFetcher, + VLC_THREAD_PRIORITY_LOW, true ) ) + { + msg_Err( p_playlist, "cannot spawn secondary preparse thread" ); + vlc_object_release( p_playlist->p_fetcher ); + return; + } + + // Start the thread + if( vlc_thread_create( p_playlist, "playlist", RunControlThread, + VLC_THREAD_PRIORITY_LOW, true ) ) + { + msg_Err( p_playlist, "cannot spawn playlist thread" ); + vlc_object_release( p_playlist ); + return; + } + + /* The object has been initialized, now attach it */ + vlc_object_attach( p_playlist, p_parent ); + + return; +} + +/** + * Run the main control thread itself + */ +static void* RunControlThread ( vlc_object_t *p_this ) +{ + playlist_t *p_playlist = (playlist_t*)p_this; + /* Tell above that we're ready */ + vlc_thread_ready( p_playlist ); + + vlc_object_lock( p_playlist ); + while( vlc_object_alive( p_playlist ) ) + { + playlist_MainLoop( p_playlist ); + + /* The playlist lock has been unlocked, so we can't tell if + * someone has killed us in the meantime. Check now. */ + if( !vlc_object_alive( p_playlist ) ) + break; + + if( p_playlist->b_cant_sleep ) + { + /* 100 ms is an acceptable delay for playlist operations */ + vlc_object_unlock( p_playlist ); + + msleep( INTF_IDLE_SLEEP*2 ); + + vlc_object_lock( p_playlist ); + } + else + { + vlc_object_wait( p_playlist ); + } + } + vlc_object_unlock( p_playlist ); + + playlist_LastLoop( p_playlist ); + return NULL; +} + +/***************************************************************************** + * Preparse-specific functions + *****************************************************************************/ +static void* RunPreparse ( vlc_object_t *p_this ) +{ + playlist_preparse_t *p_obj = (playlist_preparse_t*)p_this; + /* Tell above that we're ready */ + vlc_thread_ready( p_obj ); + playlist_PreparseLoop( p_obj ); + return NULL; +} + +static void* RunFetcher( vlc_object_t *p_this ) +{ + playlist_fetcher_t *p_obj = (playlist_fetcher_t *)p_this; + /* Tell above that we're ready */ + vlc_thread_ready( p_obj ); + playlist_FetcherLoop( p_obj ); + return NULL; +} + +static void PreparseDestructor( vlc_object_t * p_this ) +{ + playlist_preparse_t * p_preparse = (playlist_preparse_t *)p_this; + free( p_preparse->pp_waiting ); + msg_Dbg( p_this, "Destroyed" ); +} + +static void FetcherDestructor( vlc_object_t * p_this ) +{ + playlist_fetcher_t * p_fetcher = (playlist_fetcher_t *)p_this; + free( p_fetcher->pp_waiting ); + msg_Dbg( p_this, "Destroyed" ); +} diff --git a/VLC/threads.c b/VLC/threads.c new file mode 100644 index 0000000..7032cea --- /dev/null +++ b/VLC/threads.c @@ -0,0 +1,760 @@ +/***************************************************************************** + * threads.c : threads implementation for the VideoLAN client + ***************************************************************************** + * Copyright (C) 1999-2007 the VideoLAN team + * $Id$ + * + * Authors: Jean-Marc Dressler + * Samuel Hocevar + * Gildas Bazin + * Clément Sténac + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#include "libvlc.h" +#include +#ifdef HAVE_UNISTD_H +# include +#endif +#include + +#define VLC_THREADS_UNINITIALIZED 0 +#define VLC_THREADS_PENDING 1 +#define VLC_THREADS_ERROR 2 +#define VLC_THREADS_READY 3 + +/***************************************************************************** + * Global mutex for lazy initialization of the threads system + *****************************************************************************/ +static volatile unsigned i_initializations = 0; + +#if defined( LIBVLC_USE_PTHREAD ) +# include + +static pthread_mutex_t once_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif + +/** + * Global process-wide VLC object. + * Contains inter-instance data, such as the module cache and global mutexes. + */ +static libvlc_global_data_t *p_root; + +libvlc_global_data_t *vlc_global( void ) +{ + assert( i_initializations > 0 ); + return p_root; +} + +#ifndef NDEBUG +/** + * Object running the current thread + */ +static vlc_threadvar_t thread_object_key; + +vlc_object_t *vlc_threadobj (void) +{ + return vlc_threadvar_get (&thread_object_key); +} +#endif + +vlc_threadvar_t msg_context_global_key; + +#if defined(LIBVLC_USE_PTHREAD) +static inline unsigned long vlc_threadid (void) +{ + union { pthread_t th; unsigned long int i; } v = { }; + v.th = pthread_self (); + return v.i; +} + +#if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) +# include +#endif + +/***************************************************************************** + * vlc_thread_fatal: Report an error from the threading layer + ***************************************************************************** + * This is mostly meant for debugging. + *****************************************************************************/ +void vlc_pthread_fatal (const char *action, int error, + const char *file, unsigned line) +{ + fprintf (stderr, "LibVLC fatal error %s in thread %lu at %s:%u: %d\n", + action, vlc_threadid (), file, line, error); + + /* Sometimes strerror_r() crashes too, so make sure we print an error + * message before we invoke it */ +#ifdef __GLIBC__ + /* Avoid the strerror_r() prototype brain damage in glibc */ + errno = error; + fprintf (stderr, " Error message: %m at:\n"); +#else + char buf[1000]; + const char *msg; + + switch (strerror_r (error, buf, sizeof (buf))) + { + case 0: + msg = buf; + break; + case ERANGE: /* should never happen */ + msg = "unknwon (too big to display)"; + break; + default: + msg = "unknown (invalid error number)"; + break; + } + fprintf (stderr, " Error message: %s\n", msg); +#endif + fflush (stderr); + +#ifdef HAVE_BACKTRACE + void *stack[20]; + int len = backtrace (stack, sizeof (stack) / sizeof (stack[0])); + backtrace_symbols_fd (stack, len, 2); +#endif + + abort (); +} +#else +void vlc_pthread_fatal (const char *action, int error, + const char *file, unsigned line) +{ + (void)action; (void)error; (void)file; (void)line; + abort(); +} +#endif + +/***************************************************************************** + * vlc_threads_init: initialize threads system + ***************************************************************************** + * This function requires lazy initialization of a global lock in order to + * keep the library really thread-safe. Some architectures don't support this + * and thus do not guarantee the complete reentrancy. + *****************************************************************************/ +int vlc_threads_init( void ) +{ + int i_ret = VLC_SUCCESS; + + /* If we have lazy mutex initialization, use it. Otherwise, we just + * hope nothing wrong happens. */ +#if defined( LIBVLC_USE_PTHREAD ) + pthread_mutex_lock( &once_mutex ); +#endif + + if( i_initializations == 0 ) + { + p_root = vlc_custom_create( (vlc_object_t *)NULL, sizeof( *p_root ), + VLC_OBJECT_GENERIC, "root" ); + if( p_root == NULL ) + { + i_ret = VLC_ENOMEM; + goto out; + } + + /* We should be safe now. Do all the initialization stuff we want. */ +#ifndef NDEBUG + vlc_threadvar_create( &thread_object_key, NULL ); +#endif + vlc_threadvar_create( &msg_context_global_key, msg_StackDestroy ); + } + i_initializations++; + +out: + /* If we have lazy mutex initialization support, unlock the mutex. + * Otherwize, we are screwed. */ +#if defined( LIBVLC_USE_PTHREAD ) + pthread_mutex_unlock( &once_mutex ); +#endif + + return i_ret; +} + +/***************************************************************************** + * vlc_threads_end: stop threads system + ***************************************************************************** + * FIXME: This function is far from being threadsafe. + *****************************************************************************/ +void vlc_threads_end( void ) +{ +#if defined( LIBVLC_USE_PTHREAD ) + pthread_mutex_lock( &once_mutex ); +#endif + + assert( i_initializations > 0 ); + + if( i_initializations == 1 ) + { + vlc_object_release( p_root ); + vlc_threadvar_delete( &msg_context_global_key ); +#ifndef NDEBUG + vlc_threadvar_delete( &thread_object_key ); +#endif + } + i_initializations--; + +#if defined( LIBVLC_USE_PTHREAD ) + pthread_mutex_unlock( &once_mutex ); +#endif +} + +#if defined (__GLIBC__) && (__GLIBC_MINOR__ < 6) +/* This is not prototyped under glibc, though it exists. */ +int pthread_mutexattr_setkind_np( pthread_mutexattr_t *attr, int kind ); +#endif + +/***************************************************************************** + * vlc_mutex_init: initialize a mutex + *****************************************************************************/ +int vlc_mutex_init( vlc_mutex_t *p_mutex ) +{ +#if defined( LIBVLC_USE_PTHREAD ) + pthread_mutexattr_t attr; + int i_result; + + pthread_mutexattr_init( &attr ); + +# ifndef NDEBUG + /* Create error-checking mutex to detect problems more easily. */ +# if defined (__GLIBC__) && (__GLIBC_MINOR__ < 6) + pthread_mutexattr_setkind_np( &attr, PTHREAD_MUTEX_ERRORCHECK_NP ); +# else + pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_ERRORCHECK ); +# endif +# endif + i_result = pthread_mutex_init( p_mutex, &attr ); + pthread_mutexattr_destroy( &attr ); + return i_result; +#elif defined( UNDER_CE ) + InitializeCriticalSection( &p_mutex->csection ); + return 0; + +#elif defined( WIN32 ) + *p_mutex = CreateMutex( 0, FALSE, 0 ); + return (*p_mutex != NULL) ? 0 : ENOMEM; + +#elif defined( HAVE_KERNEL_SCHEDULER_H ) + /* check the arguments and whether it's already been initialized */ + if( p_mutex == NULL ) + { + return B_BAD_VALUE; + } + + if( p_mutex->init == 9999 ) + { + return EALREADY; + } + + p_mutex->lock = create_sem( 1, "BeMutex" ); + if( p_mutex->lock < B_NO_ERROR ) + { + return( -1 ); + } + + p_mutex->init = 9999; + return B_OK; + +#endif +} + +/***************************************************************************** + * vlc_mutex_init: initialize a recursive mutex (Do not use) + *****************************************************************************/ +int vlc_mutex_init_recursive( vlc_mutex_t *p_mutex ) +{ +#if defined( LIBVLC_USE_PTHREAD ) + pthread_mutexattr_t attr; + int i_result; + + pthread_mutexattr_init( &attr ); +# if defined (__GLIBC__) && (__GLIBC_MINOR__ < 6) + pthread_mutexattr_setkind_np( &attr, PTHREAD_MUTEX_RECURSIVE_NP ); +# else + pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE ); +# endif + i_result = pthread_mutex_init( p_mutex, &attr ); + pthread_mutexattr_destroy( &attr ); + return( i_result ); +#elif defined( WIN32 ) + /* Create mutex returns a recursive mutex */ + *p_mutex = CreateMutex( 0, FALSE, 0 ); + return (*p_mutex != NULL) ? 0 : ENOMEM; +#else +# error Unimplemented! +#endif +} + + +/***************************************************************************** + * vlc_mutex_destroy: destroy a mutex, inner version + *****************************************************************************/ +void __vlc_mutex_destroy( const char * psz_file, int i_line, vlc_mutex_t *p_mutex ) +{ +#if defined( LIBVLC_USE_PTHREAD ) + int val = pthread_mutex_destroy( p_mutex ); + VLC_THREAD_ASSERT ("destroying mutex"); + +#elif defined( UNDER_CE ) + VLC_UNUSED( psz_file); VLC_UNUSED( i_line ); + + DeleteCriticalSection( &p_mutex->csection ); + +#elif defined( WIN32 ) + VLC_UNUSED( psz_file); VLC_UNUSED( i_line ); + + CloseHandle( *p_mutex ); + +#elif defined( HAVE_KERNEL_SCHEDULER_H ) + if( p_mutex->init == 9999 ) + delete_sem( p_mutex->lock ); + + p_mutex->init = 0; + +#endif +} + +/***************************************************************************** + * vlc_cond_init: initialize a condition + *****************************************************************************/ +int __vlc_cond_init( vlc_cond_t *p_condvar ) +{ +#if defined( LIBVLC_USE_PTHREAD ) + pthread_condattr_t attr; + int ret; + + ret = pthread_condattr_init (&attr); + if (ret) + return ret; + +# if !defined (_POSIX_CLOCK_SELECTION) + /* Fairly outdated POSIX support (that was defined in 2001) */ +# define _POSIX_CLOCK_SELECTION (-1) +# endif +# if (_POSIX_CLOCK_SELECTION >= 0) + /* NOTE: This must be the same clock as the one in mtime.c */ + pthread_condattr_setclock (&attr, CLOCK_MONOTONIC); +# endif + + ret = pthread_cond_init (p_condvar, &attr); + pthread_condattr_destroy (&attr); + return ret; + +#elif defined( UNDER_CE ) || defined( WIN32 ) + /* Create an auto-reset event. */ + *p_condvar = CreateEvent( NULL, /* no security */ + FALSE, /* auto-reset event */ + FALSE, /* start non-signaled */ + NULL ); /* unnamed */ + return *p_condvar ? 0 : ENOMEM; + +#elif defined( HAVE_KERNEL_SCHEDULER_H ) + if( !p_condvar ) + { + return B_BAD_VALUE; + } + + if( p_condvar->init == 9999 ) + { + return EALREADY; + } + + p_condvar->thread = -1; + p_condvar->init = 9999; + return 0; + +#endif +} + +/***************************************************************************** + * vlc_cond_destroy: destroy a condition, inner version + *****************************************************************************/ +void __vlc_cond_destroy( const char * psz_file, int i_line, vlc_cond_t *p_condvar ) +{ +#if defined( LIBVLC_USE_PTHREAD ) + int val = pthread_cond_destroy( p_condvar ); + VLC_THREAD_ASSERT ("destroying condition"); + +#elif defined( UNDER_CE ) || defined( WIN32 ) + VLC_UNUSED( psz_file); VLC_UNUSED( i_line ); + + CloseHandle( *p_condvar ); + +#elif defined( HAVE_KERNEL_SCHEDULER_H ) + p_condvar->init = 0; + +#endif +} + +/***************************************************************************** + * vlc_tls_create: create a thread-local variable + *****************************************************************************/ +int vlc_threadvar_create( vlc_threadvar_t *p_tls, void (*destr) (void *) ) +{ + int i_ret; + +#if defined( LIBVLC_USE_PTHREAD ) + i_ret = pthread_key_create( p_tls, destr ); +#elif defined( UNDER_CE ) + i_ret = ENOSYS; +#elif defined( WIN32 ) + /* FIXME: remember/use the destr() callback and stop leaking whatever */ + *p_tls = TlsAlloc(); + i_ret = (*p_tls == TLS_OUT_OF_INDEXES) ? EAGAIN : 0; +#else +# error Unimplemented! +#endif + return i_ret; +} + +void vlc_threadvar_delete (vlc_threadvar_t *p_tls) +{ +#if defined( LIBVLC_USE_PTHREAD ) + pthread_key_delete (*p_tls); +#elif defined( UNDER_CE ) +#elif defined( WIN32 ) + TlsFree (*p_tls); +#else +# error Unimplemented! +#endif +} + +struct vlc_thread_boot +{ + void * (*entry) (vlc_object_t *); + vlc_object_t *object; +}; + +#if defined (LIBVLC_USE_PTHREAD) +# define THREAD_RTYPE void * +# define THREAD_RVAL NULL +#elif defined (WIN32) +# define THREAD_RTYPE __stdcall unsigned +# define THREAD_RVAL 0 +#endif + +static THREAD_RTYPE thread_entry (void *data) +{ + vlc_object_t *obj = ((struct vlc_thread_boot *)data)->object; + void *(*func) (vlc_object_t *) = ((struct vlc_thread_boot *)data)->entry; + + free (data); +#ifndef NDEBUG + vlc_threadvar_set (&thread_object_key, obj); +#endif + msg_Dbg (obj, "thread started"); + func (obj); + msg_Dbg (obj, "thread ended"); + + return THREAD_RVAL; +} + +/***************************************************************************** + * vlc_thread_create: create a thread, inner version + ***************************************************************************** + * Note that i_priority is only taken into account on platforms supporting + * userland real-time priority threads. + *****************************************************************************/ +int __vlc_thread_create( vlc_object_t *p_this, const char * psz_file, int i_line, + const char *psz_name, void * ( *func ) ( vlc_object_t * ), + int i_priority, bool b_wait ) +{ + int i_ret; + vlc_object_internals_t *p_priv = vlc_internals( p_this ); + + struct vlc_thread_boot *boot = malloc (sizeof (*boot)); + if (boot == NULL) + return errno; + boot->entry = func; + boot->object = p_this; + + vlc_object_lock( p_this ); + + /* Make sure we don't re-create a thread if the object has already one */ + assert( !p_priv->b_thread ); + +#if defined( LIBVLC_USE_PTHREAD ) + pthread_attr_t attr; + pthread_attr_init (&attr); + + /* Block the signals that signals interface plugin handles. + * If the LibVLC caller wants to handle some signals by itself, it should + * block these before whenever invoking LibVLC. And it must obviously not + * start the VLC signals interface plugin. + * + * LibVLC will normally ignore any interruption caused by an asynchronous + * signal during a system call. But there may well be some buggy cases + * where it fails to handle EINTR (bug reports welcome). Some underlying + * libraries might also not handle EINTR properly. + */ + sigset_t set, oldset; + sigemptyset (&set); + sigdelset (&set, SIGHUP); + sigaddset (&set, SIGINT); + sigaddset (&set, SIGQUIT); + sigaddset (&set, SIGTERM); + + sigaddset (&set, SIGPIPE); /* We don't want this one, really! */ + pthread_sigmask (SIG_BLOCK, &set, &oldset); + +#ifndef __APPLE__ + if( config_GetInt( p_this, "rt-priority" ) > 0 ) +#endif + { + struct sched_param p = { .sched_priority = i_priority, }; + int policy; + + /* Hack to avoid error msg */ + if( config_GetType( p_this, "rt-offset" ) ) + p.sched_priority += config_GetInt( p_this, "rt-offset" ); + if( p.sched_priority <= 0 ) + p.sched_priority += sched_get_priority_max (policy = SCHED_OTHER); + else + p.sched_priority += sched_get_priority_min (policy = SCHED_RR); + + pthread_attr_setschedpolicy (&attr, policy); + pthread_attr_setschedparam (&attr, &p); + } + + i_ret = pthread_create( &p_priv->thread_id, &attr, thread_entry, boot ); + pthread_sigmask (SIG_SETMASK, &oldset, NULL); + pthread_attr_destroy (&attr); + +#elif defined( WIN32 ) || defined( UNDER_CE ) + /* When using the MSVCRT C library you have to use the _beginthreadex + * function instead of CreateThread, otherwise you'll end up with + * memory leaks and the signal functions not working (see Microsoft + * Knowledge Base, article 104641) */ +#if defined( UNDER_CE ) + HANDLE hThread = CreateThread( NULL, 0, thread_entry, + (LPVOID)boot, CREATE_SUSPENDED, NULL ); +#else + HANDLE hThread = (HANDLE)(uintptr_t) + _beginthreadex( NULL, 0, thread_entry, boot, CREATE_SUSPENDED, NULL ); +#endif + if( hThread ) + { + p_priv->thread_id = hThread; + ResumeThread (hThread); + i_ret = 0; + if( i_priority && !SetThreadPriority (hThread, i_priority) ) + { + msg_Warn( p_this, "couldn't set a faster priority" ); + i_priority = 0; + } + } + else + i_ret = errno; + +#elif defined( HAVE_KERNEL_SCHEDULER_H ) + p_priv->thread_id = spawn_thread( (thread_func)thread_entry, psz_name, + i_priority, p_data ); + i_ret = resume_thread( p_priv->thread_id ); + +#endif + + if( i_ret == 0 ) + { + if( b_wait ) + { + msg_Dbg( p_this, "waiting for thread initialization" ); + vlc_object_wait( p_this ); + } + + p_priv->b_thread = true; + msg_Dbg( p_this, "thread %lu (%s) created at priority %d (%s:%d)", + (unsigned long)p_priv->thread_id, psz_name, i_priority, + psz_file, i_line ); + } + else + { + errno = i_ret; + msg_Err( p_this, "%s thread could not be created at %s:%d (%m)", + psz_name, psz_file, i_line ); + } + + vlc_object_unlock( p_this ); + return i_ret; +} + +/***************************************************************************** + * vlc_thread_set_priority: set the priority of the current thread when we + * couldn't set it in vlc_thread_create (for instance for the main thread) + *****************************************************************************/ +int __vlc_thread_set_priority( vlc_object_t *p_this, const char * psz_file, + int i_line, int i_priority ) +{ + vlc_object_internals_t *p_priv = vlc_internals( p_this ); + + if( !p_priv->b_thread ) + { + msg_Err( p_this, "couldn't set priority of non-existent thread" ); + return ESRCH; + } + +#if defined( LIBVLC_USE_PTHREAD ) +# ifndef __APPLE__ + if( config_GetInt( p_this, "rt-priority" ) > 0 ) +# endif + { + int i_error, i_policy; + struct sched_param param; + + memset( ¶m, 0, sizeof(struct sched_param) ); + if( config_GetType( p_this, "rt-offset" ) ) + i_priority += config_GetInt( p_this, "rt-offset" ); + if( i_priority <= 0 ) + { + param.sched_priority = (-1) * i_priority; + i_policy = SCHED_OTHER; + } + else + { + param.sched_priority = i_priority; + i_policy = SCHED_RR; + } + if( (i_error = pthread_setschedparam( p_priv->thread_id, + i_policy, ¶m )) ) + { + errno = i_error; + msg_Warn( p_this, "couldn't set thread priority (%s:%d): %m", + psz_file, i_line ); + i_priority = 0; + } + } + +#elif defined( WIN32 ) || defined( UNDER_CE ) + VLC_UNUSED( psz_file); VLC_UNUSED( i_line ); + + if( !SetThreadPriority(p_priv->thread_id, i_priority) ) + { + msg_Warn( p_this, "couldn't set a faster priority" ); + return 1; + } + +#endif + + return 0; +} + +/***************************************************************************** + * vlc_thread_join: wait until a thread exits, inner version + *****************************************************************************/ +void __vlc_thread_join( vlc_object_t *p_this, const char * psz_file, int i_line ) +{ + vlc_object_internals_t *p_priv = vlc_internals( p_this ); + int i_ret = 0; + +#if defined( LIBVLC_USE_PTHREAD ) + /* Make sure we do return if we are calling vlc_thread_join() + * from the joined thread */ + if (pthread_equal (pthread_self (), p_priv->thread_id)) + { + msg_Warn (p_this, "joining the active thread (VLC might crash)"); + i_ret = pthread_detach (p_priv->thread_id); + } + else + i_ret = pthread_join (p_priv->thread_id, NULL); + +#elif defined( UNDER_CE ) || defined( WIN32 ) + HMODULE hmodule; + BOOL (WINAPI *OurGetThreadTimes)( HANDLE, FILETIME*, FILETIME*, + FILETIME*, FILETIME* ); + FILETIME create_ft, exit_ft, kernel_ft, user_ft; + int64_t real_time, kernel_time, user_time; + HANDLE hThread; + + /* + ** object will close its thread handle when destroyed, duplicate it here + ** to be on the safe side + */ + if( ! DuplicateHandle(GetCurrentProcess(), + p_priv->thread_id, + GetCurrentProcess(), + &hThread, + 0, + FALSE, + DUPLICATE_SAME_ACCESS) ) + { + p_priv->b_thread = false; + i_ret = GetLastError(); + goto error; + } + + WaitForSingleObject( hThread, INFINITE ); + +#if defined( UNDER_CE ) + hmodule = GetModuleHandle( _T("COREDLL") ); +#else + hmodule = GetModuleHandle( _T("KERNEL32") ); +#endif + OurGetThreadTimes = (BOOL (WINAPI*)( HANDLE, FILETIME*, FILETIME*, + FILETIME*, FILETIME* )) + GetProcAddress( hmodule, _T("GetThreadTimes") ); + + if( OurGetThreadTimes && + OurGetThreadTimes( hThread, + &create_ft, &exit_ft, &kernel_ft, &user_ft ) ) + { + real_time = + ((((int64_t)exit_ft.dwHighDateTime)<<32)| exit_ft.dwLowDateTime) - + ((((int64_t)create_ft.dwHighDateTime)<<32)| create_ft.dwLowDateTime); + real_time /= 10; + + kernel_time = + ((((int64_t)kernel_ft.dwHighDateTime)<<32)| + kernel_ft.dwLowDateTime) / 10; + + user_time = + ((((int64_t)user_ft.dwHighDateTime)<<32)| + user_ft.dwLowDateTime) / 10; + + msg_Dbg( p_this, "thread times: " + "real %"PRId64"m%fs, kernel %"PRId64"m%fs, user %"PRId64"m%fs", + real_time/60/1000000, + (double)((real_time%(60*1000000))/1000000.0), + kernel_time/60/1000000, + (double)((kernel_time%(60*1000000))/1000000.0), + user_time/60/1000000, + (double)((user_time%(60*1000000))/1000000.0) ); + } + CloseHandle( hThread ); +error: + +#elif defined( HAVE_KERNEL_SCHEDULER_H ) + int32_t exit_value; + i_ret = (B_OK == wait_for_thread( p_priv->thread_id, &exit_value )); + +#endif + + if( i_ret ) + { + errno = i_ret; + msg_Err( p_this, "thread_join(%lu) failed at %s:%d (%m)", + (unsigned long)p_priv->thread_id, psz_file, i_line ); + } + else + msg_Dbg( p_this, "thread %lu joined (%s:%d)", + (unsigned long)p_priv->thread_id, psz_file, i_line ); + + p_priv->b_thread = false; +} diff --git a/VLC/tls.c b/VLC/tls.c new file mode 100644 index 0000000..77c9b79 --- /dev/null +++ b/VLC/tls.c @@ -0,0 +1,228 @@ +/***************************************************************************** + * tls.c + ***************************************************************************** + * Copyright © 2004-2007 Rémi Denis-Courmont + * $Id$ + * + * Authors: Rémi Denis-Courmont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/** + * @file + * libvlc interface to the Transport Layer Security (TLS) plugins. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "libvlc.h" + +#include "vlc_tls.h" + +/** + * Allocates a whole server's TLS credentials. + * + * @param cert_path required (Unicode) path to an x509 certificate, + * if NULL, anonymous key exchange will be used. + * @param key_path (UTF-8) path to the PKCS private key for the certificate, + * if NULL; cert_path will be used. + * + * @return NULL on error. + */ +tls_server_t * +tls_ServerCreate (vlc_object_t *obj, const char *cert_path, + const char *key_path) +{ + tls_server_t *srv; + + srv = (tls_server_t *)vlc_custom_create (obj, sizeof (*srv), + VLC_OBJECT_GENERIC, + "tls server"); + if (srv == NULL) + return NULL; + + var_Create (srv, "tls-x509-cert", VLC_VAR_STRING); + var_Create (srv, "tls-x509-key", VLC_VAR_STRING); + + if (cert_path != NULL) + { + var_SetString (srv, "tls-x509-cert", cert_path); + + if (key_path == NULL) + key_path = cert_path; + var_SetString (srv, "tls-x509-key", key_path); + } + + srv->p_module = module_Need (srv, "tls server", 0, 0); + if (srv->p_module == NULL) + { + msg_Err (srv, "TLS server plugin not available"); + vlc_object_release (srv); + return NULL; + } + + vlc_object_attach (srv, obj); + msg_Dbg (srv, "TLS server plugin initialized"); + return srv; +} + + +/** + * Releases data allocated with tls_ServerCreate. + * @param srv TLS server object to be destroyed, or NULL + */ +void tls_ServerDelete (tls_server_t *srv) +{ + if (srv == NULL) + return; + + module_Unneed (srv, srv->p_module); + vlc_object_detach (srv); + vlc_object_release (srv); +} + + +/** + * Adds one or more certificate authorities from a file. + * @return -1 on error, 0 on success. + */ +int tls_ServerAddCA (tls_server_t *srv, const char *path) +{ + return srv->pf_add_CA (srv, path); +} + + +/** + * Adds one or more certificate revocation list from a file. + * @return -1 on error, 0 on success. + */ +int tls_ServerAddCRL (tls_server_t *srv, const char *path) +{ + return srv->pf_add_CRL (srv, path); +} + + +tls_session_t *tls_ServerSessionPrepare (tls_server_t *srv) +{ + tls_session_t *ses; + + ses = srv->pf_open (srv); + if (ses == NULL) + return NULL; + + vlc_object_attach (ses, srv); + return ses; +} + + +void tls_ServerSessionClose (tls_session_t *ses) +{ + tls_server_t *srv = (tls_server_t *)(ses->p_parent); + srv->pf_close (srv, ses); +} + + +int tls_ServerSessionHandshake (tls_session_t *ses, int fd) +{ + ses->pf_set_fd (ses, fd); + return 2; +} + + +int tls_SessionContinueHandshake (tls_session_t *ses) +{ + int val = ses->pf_handshake (ses); + if (val < 0) + tls_ServerSessionClose (ses); + return val; +} + + +/** + * Allocates a client's TLS credentials and shakes hands through the network. + * This is a blocking network operation. + * + * @param fd stream socket through which to establish the secure communication + * layer. + * @param psz_hostname Server Name Indication to pass to the server, or NULL. + * + * @return NULL on error. + **/ +tls_session_t * +tls_ClientCreate (vlc_object_t *obj, int fd, const char *psz_hostname) +{ + tls_session_t *cl; + int val; + + cl = (tls_session_t *)vlc_custom_create (obj, sizeof (*cl), + VLC_OBJECT_GENERIC, + "tls client"); + if (cl == NULL) + return NULL; + + var_Create (cl, "tls-server-name", VLC_VAR_STRING); + if (psz_hostname != NULL) + { + msg_Dbg (cl, "requested server name: %s", psz_hostname); + var_SetString (cl, "tls-server-name", psz_hostname); + } + else + msg_Dbg (cl, "requested anonymous server"); + + cl->p_module = module_Need (cl, "tls client", 0, 0); + if (cl->p_module == NULL) + { + msg_Err (cl, "TLS client plugin not available"); + vlc_object_release (cl); + return NULL; + } + + cl->pf_set_fd (cl, fd); + + do + val = cl->pf_handshake (cl); + while (val > 0); + + if (val == 0) + { + msg_Dbg (cl, "TLS client session initialized"); + vlc_object_attach (cl, obj); + return cl; + } + msg_Err (cl, "TLS client session handshake error"); + + module_Unneed (cl, cl->p_module); + vlc_object_release (cl); + return NULL; +} + + +/** + * Releases data allocated with tls_ClientCreate. + * It is your job to close the underlying socket. + */ +void tls_ClientDelete (tls_session_t *cl) +{ + if (cl == NULL) + return; + + module_Unneed (cl, cl->p_module); + vlc_object_detach (cl); + vlc_object_release (cl); +} diff --git a/VLC/tree.c b/VLC/tree.c new file mode 100644 index 0000000..1dfeba0 --- /dev/null +++ b/VLC/tree.c @@ -0,0 +1,660 @@ +/***************************************************************************** + * tree.c : Playlist tree walking functions + ***************************************************************************** + * Copyright (C) 1999-2007 the VideoLAN team + * $Id$ + * + * Authors: Clément Stenac + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include +#include "vlc_playlist.h" +#include "playlist_internal.h" + +/************************************************************************ + * Local prototypes + ************************************************************************/ +playlist_item_t *GetNextUncle( playlist_t *p_playlist, playlist_item_t *p_item, + playlist_item_t *p_root ); +playlist_item_t *GetPrevUncle( playlist_t *p_playlist, playlist_item_t *p_item, + playlist_item_t *p_root ); + +playlist_item_t *GetNextItem( playlist_t *p_playlist, + playlist_item_t *p_root, + playlist_item_t *p_item ); +playlist_item_t *GetPrevItem( playlist_t *p_playlist, + playlist_item_t *p_item, + playlist_item_t *p_root ); + +/** + * Create a playlist node + * + * \param p_playlist the playlist + * \param psz_name the name of the node + * \param p_parent the parent node to attach to or NULL if no attach + * \param p_flags miscellaneous flags + * \param p_input the input_item to attach to or NULL if it has to be created + * \return the new node + */ +playlist_item_t * playlist_NodeCreate( playlist_t *p_playlist, + const char *psz_name, + playlist_item_t *p_parent, int i_flags, + input_item_t *p_input ) +{ + input_item_t *p_new_input = NULL; + playlist_item_t *p_item; + + PL_ASSERT_LOCKED; + if( !psz_name ) psz_name = _("Undefined"); + + if( !p_input ) + p_new_input = input_item_NewWithType( VLC_OBJECT(p_playlist), NULL, + psz_name, 0, NULL, -1, ITEM_TYPE_NODE ); + p_item = playlist_ItemNewFromInput( p_playlist, + p_input ? p_input : p_new_input ); + if( p_new_input ) + vlc_gc_decref( p_new_input ); + + if( p_item == NULL ) return NULL; + p_item->i_children = 0; + + ARRAY_APPEND(p_playlist->all_items, p_item); + + if( p_parent != NULL ) + playlist_NodeAppend( p_playlist, p_item, p_parent ); + playlist_SendAddNotify( p_playlist, p_item->i_id, + p_parent ? p_parent->i_id : -1, + !( i_flags & PLAYLIST_NO_REBUILD )); + return p_item; +} + +/** + * Remove all the children of a node + * + * This function must be entered with the playlist lock + * + * \param p_playlist the playlist + * \param p_root the node + * \param b_delete_items do we have to delete the children items ? + * \return VLC_SUCCESS or an error + */ +int playlist_NodeEmpty( playlist_t *p_playlist, playlist_item_t *p_root, + bool b_delete_items ) +{ + PL_ASSERT_LOCKED; + int i; + if( p_root->i_children == -1 ) + { + return VLC_EGENERIC; + } + + /* Delete the children */ + for( i = p_root->i_children-1 ; i >= 0 ;i-- ) + { + if( p_root->pp_children[i]->i_children > -1 ) + { + playlist_NodeDelete( p_playlist, p_root->pp_children[i], + b_delete_items , false ); + } + else if( b_delete_items ) + { + /* Delete the item here */ + playlist_DeleteFromItemId( p_playlist, + p_root->pp_children[i]->i_id ); + } + } + return VLC_SUCCESS; +} + +/** + * Remove all the children of a node and removes the node + * + * \param p_playlist the playlist + * \param p_root the node + * \param b_delete_items do we have to delete the children items ? + * \return VLC_SUCCESS or an error + */ +int playlist_NodeDelete( playlist_t *p_playlist, playlist_item_t *p_root, + bool b_delete_items, bool b_force ) +{ + PL_ASSERT_LOCKED; + int i; + + if( p_root->i_children == -1 ) + { + return VLC_EGENERIC; + } + + /* Delete the children */ + for( i = p_root->i_children - 1 ; i >= 0; i-- ) + { + if( p_root->pp_children[i]->i_children > -1 ) + { + playlist_NodeDelete( p_playlist, p_root->pp_children[i], + b_delete_items , b_force ); + } + else if( b_delete_items ) + { + playlist_DeleteFromItemId( p_playlist, + p_root->pp_children[i]->i_id ); + } + } + /* Delete the node */ + if( p_root->i_flags & PLAYLIST_RO_FLAG && !b_force ) + { + } + else + { + int i; + var_SetInteger( p_playlist, "item-deleted", p_root->i_id ); + ARRAY_BSEARCH( p_playlist->all_items, ->i_id, int, + p_root->i_id, i ); + if( i != -1 ) + ARRAY_REMOVE( p_playlist->all_items, i ); + + /* Remove the item from its parent */ + if( p_root->p_parent ) + playlist_NodeRemoveItem( p_playlist, p_root, p_root->p_parent ); + + playlist_ItemRelease( p_root ); + } + return VLC_SUCCESS; +} + + +/** + * Adds an item to the children of a node + * + * \param p_playlist the playlist + * \param p_item the item to append + * \param p_parent the parent node + * \return VLC_SUCCESS or an error + */ +int playlist_NodeAppend( playlist_t *p_playlist, + playlist_item_t *p_item, + playlist_item_t *p_parent ) +{ + return playlist_NodeInsert( p_playlist, p_item, p_parent, -1 ); +} + +int playlist_NodeInsert( playlist_t *p_playlist, + playlist_item_t *p_item, + playlist_item_t *p_parent, + int i_position ) +{ + PL_ASSERT_LOCKED; + (void)p_playlist; + assert( p_parent && p_parent->i_children != -1 ); + if( i_position == -1 ) i_position = p_parent->i_children ; + + INSERT_ELEM( p_parent->pp_children, + p_parent->i_children, + i_position, + p_item ); + p_item->p_parent = p_parent; + return VLC_SUCCESS; +} + +/** + * Deletes an item from the children of a node + * + * \param p_playlist the playlist + * \param p_item the item to remove + * \param p_parent the parent node + * \return VLC_SUCCESS or an error + */ +int playlist_NodeRemoveItem( playlist_t *p_playlist, + playlist_item_t *p_item, + playlist_item_t *p_parent ) +{ + PL_ASSERT_LOCKED; + (void)p_playlist; + + for(int i= 0; i< p_parent->i_children ; i++ ) + { + if( p_parent->pp_children[i] == p_item ) + { + REMOVE_ELEM( p_parent->pp_children, p_parent->i_children, i ); + } + } + + return VLC_SUCCESS; +} + + +/** + * Count the children of a node + * + * \param p_playlist the playlist + * \param p_node the node + * \return the number of children + */ +int playlist_NodeChildrenCount( playlist_t *p_playlist, playlist_item_t*p_node) +{ + PL_ASSERT_LOCKED; + int i; + int i_nb = 0; + + if( p_node->i_children == -1 ) + return 0; + + i_nb = p_node->i_children; + for( i=0 ; i< p_node->i_children;i++ ) + { + if( p_node->pp_children[i]->i_children == -1 ) + break; + else + i_nb += playlist_NodeChildrenCount( p_playlist, + p_node->pp_children[i] ); + } + return i_nb; +} + +/** + * Search a child of a node by its name + * + * \param p_node the node + * \param psz_search the name of the child to search + * \return the child item or NULL if not found or error + */ +playlist_item_t *playlist_ChildSearchName( playlist_item_t *p_node, + const char *psz_search ) +{ + playlist_t * p_playlist = p_node->p_playlist; /* For assert_locked */ + PL_ASSERT_LOCKED; + int i; + + if( p_node->i_children < 0 ) + { + return NULL; + } + for( i = 0 ; i< p_node->i_children; i++ ) + { + if( !strcmp( p_node->pp_children[i]->p_input->psz_name, psz_search ) ) + { + return p_node->pp_children[i]; + } + } + return NULL; +} + +/** + * Create a pair of nodes in the category and onelevel trees. + * They share the same input item. + * \param p_playlist the playlist + * \param psz_name the name of the nodes + * \param pp_node_cat pointer to return the node in category tree + * \param pp_node_one pointer to return the node in onelevel tree + * \param b_for_sd For Services Discovery ? (make node read-only and unskipping) + */ +void playlist_NodesPairCreate( playlist_t *p_playlist, const char *psz_name, + playlist_item_t **pp_node_cat, + playlist_item_t **pp_node_one, + bool b_for_sd ) +{ + PL_ASSERT_LOCKED; + *pp_node_cat = playlist_NodeCreate( p_playlist, psz_name, + p_playlist->p_root_category, 0, NULL ); + *pp_node_one = playlist_NodeCreate( p_playlist, psz_name, + p_playlist->p_root_onelevel, 0, + (*pp_node_cat)->p_input ); + if( b_for_sd ) + { + (*pp_node_cat)->i_flags |= PLAYLIST_RO_FLAG; + (*pp_node_cat)->i_flags |= PLAYLIST_SKIP_FLAG; + (*pp_node_one)->i_flags |= PLAYLIST_RO_FLAG; + (*pp_node_one)->i_flags |= PLAYLIST_SKIP_FLAG; + } +} + +/** + * Get the node in the preferred tree from a node in one of category + * or onelevel tree. + */ +playlist_item_t * playlist_GetPreferredNode( playlist_t *p_playlist, + playlist_item_t *p_node ) +{ + PL_ASSERT_LOCKED; + int i; + if( p_node->p_parent == p_playlist->p_root_category ) + { + if( p_playlist->b_tree || p_node->p_input->b_prefers_tree ) + return p_node; + for( i = 0 ; i< p_playlist->p_root_onelevel->i_children; i++ ) + { + if( p_playlist->p_root_onelevel->pp_children[i]->p_input->i_id == + p_node->p_input->i_id ) + return p_playlist->p_root_onelevel->pp_children[i]; + } + } + else if( p_node->p_parent == p_playlist->p_root_onelevel ) + { + if( !p_playlist->b_tree || !p_node->p_input->b_prefers_tree ) + return p_node; + for( i = 0 ; i< p_playlist->p_root_category->i_children; i++ ) + { + if( p_playlist->p_root_category->pp_children[i]->p_input->i_id == + p_node->p_input->i_id ) + return p_playlist->p_root_category->pp_children[i]; + } + } + return NULL; +} + +/********************************************************************** + * Tree walking functions + **********************************************************************/ + +playlist_item_t *playlist_GetLastLeaf(playlist_t *p_playlist, + playlist_item_t *p_root ) +{ + PL_ASSERT_LOCKED; + int i; + playlist_item_t *p_item; + for ( i = p_root->i_children - 1; i >= 0; i-- ) + { + if( p_root->pp_children[i]->i_children == -1 ) + return p_root->pp_children[i]; + else if( p_root->pp_children[i]->i_children > 0) + { + p_item = playlist_GetLastLeaf( p_playlist, + p_root->pp_children[i] ); + if ( p_item != NULL ) + return p_item; + } + else if( i == 0 ) + return NULL; + } + return NULL; +} + +/** + * Finds the next item to play + * + * \param p_playlist the playlist + * \param p_root the root node + * \param p_item the previous item (NULL if none ) + * \return the next item to play, or NULL if none found + */ +playlist_item_t *playlist_GetNextLeaf( playlist_t *p_playlist, + playlist_item_t *p_root, + playlist_item_t *p_item, + bool b_ena, bool b_unplayed ) +{ + PL_ASSERT_LOCKED; + playlist_item_t *p_next; + + assert( p_root && p_root->i_children != -1 ); + + PL_DEBUG2( "finding next of %s within %s", + PLI_NAME( p_item ), PLI_NAME( p_root ) ); + + /* Now, walk the tree until we find a suitable next item */ + p_next = p_item; + while( 1 ) + { + bool b_ena_ok = true, b_unplayed_ok = true; + p_next = GetNextItem( p_playlist, p_root, p_next ); + if( !p_next || p_next == p_root ) + break; + if( p_next->i_children == -1 ) + { + if( b_ena && p_next->i_flags & PLAYLIST_DBL_FLAG ) + b_ena_ok = false; + if( b_unplayed && p_next->p_input->i_nb_played != 0 ) + b_unplayed_ok = false; + if( b_ena_ok && b_unplayed_ok ) break; + } + } + if( p_next == NULL ) PL_DEBUG2( "at end of node" ); + return p_next; +} + +/** + * Finds the previous item to play + * + * \param p_playlist the playlist + * \param p_root the root node + * \param p_item the previous item (NULL if none ) + * \return the next item to play, or NULL if none found + */ +playlist_item_t *playlist_GetPrevLeaf( playlist_t *p_playlist, + playlist_item_t *p_root, + playlist_item_t *p_item, + bool b_ena, bool b_unplayed ) +{ + PL_ASSERT_LOCKED; + playlist_item_t *p_prev; + + PL_DEBUG2( "finding previous os %s within %s", PLI_NAME( p_item ), + PLI_NAME( p_root ) ); + assert( p_root && p_root->i_children != -1 ); + + /* Now, walk the tree until we find a suitable previous item */ + p_prev = p_item; + while( 1 ) + { + bool b_ena_ok = true, b_unplayed_ok = true; + p_prev = GetPrevItem( p_playlist, p_root, p_prev ); + if( !p_prev || p_prev == p_root ) + break; + if( p_prev->i_children == -1 ) + { + if( b_ena && p_prev->i_flags & PLAYLIST_DBL_FLAG ) + b_ena_ok = false; + if( b_unplayed && p_prev->p_input->i_nb_played != 0 ) + b_unplayed_ok = false; + if( b_ena_ok && b_unplayed_ok ) break; + } + } + if( p_prev == NULL ) PL_DEBUG2( "at beginning of node" ); + return p_prev; +} + +/************************************************************************ + * Following functions are local + ***********************************************************************/ + +/** + * Get the next item in the tree + * If p_item is NULL, return the first child of root + **/ +playlist_item_t *GetNextItem( playlist_t *p_playlist, + playlist_item_t *p_root, + playlist_item_t *p_item ) +{ + playlist_item_t *p_parent; + int i; + + /* Node with children, get the first one */ + if( p_item && p_item->i_children > 0 ) + return p_item->pp_children[0]; + + if( p_item != NULL ) + p_parent = p_item->p_parent; + else + p_parent = p_root; + for( i= 0 ; i < p_parent->i_children ; i++ ) + { + if( p_item == NULL || p_parent->pp_children[i] == p_item ) + { + if( p_item == NULL ) + i = -1; + + if( i+1 >= p_parent->i_children ) + { + /* Was already the last sibling. Look for uncles */ + PL_DEBUG2( "Current item is the last of the node," + "looking for uncle from %s", + p_parent->p_input->psz_name ); + + if( p_parent == p_root ) + { + PL_DEBUG2( "already at root" ); + return NULL; + } + return GetNextUncle( p_playlist, p_item, p_root ); + } + else + { + return p_parent->pp_children[i+1]; + } + } + } + return NULL; +} + +playlist_item_t *GetNextUncle( playlist_t *p_playlist, playlist_item_t *p_item, + playlist_item_t *p_root ) +{ + playlist_item_t *p_parent = p_item->p_parent; + playlist_item_t *p_grandparent; + bool b_found = false; + + (void)p_playlist; + + if( p_parent != NULL ) + { + p_grandparent = p_parent->p_parent; + while( p_grandparent ) + { + int i; + for( i = 0 ; i< p_grandparent->i_children ; i++ ) + { + if( p_parent == p_grandparent->pp_children[i] ) + { + PL_DEBUG2( "parent %s found as child %i of grandparent %s", + p_parent->p_input->psz_name, i, + p_grandparent->p_input->psz_name ); + b_found = true; + break; + } + } + if( b_found && i + 1 < p_grandparent->i_children ) + { + return p_grandparent->pp_children[i+1]; + } + /* Not found at root */ + if( p_grandparent == p_root ) + { + return NULL; + } + else + { + p_parent = p_grandparent; + p_grandparent = p_parent->p_parent; + } + } + } + /* We reached root */ + return NULL; +} + +playlist_item_t *GetPrevUncle( playlist_t *p_playlist, playlist_item_t *p_item, + playlist_item_t *p_root ) +{ + playlist_item_t *p_parent = p_item->p_parent; + playlist_item_t *p_grandparent; + bool b_found = false; + + (void)p_playlist; + + if( p_parent != NULL ) + { + p_grandparent = p_parent->p_parent; + while( 1 ) + { + int i; + for( i = p_grandparent->i_children -1 ; i >= 0; i-- ) + { + if( p_parent == p_grandparent->pp_children[i] ) + { + b_found = true; + break; + } + } + if( b_found && i - 1 > 0 ) + { + return p_grandparent->pp_children[i-1]; + } + /* Not found at root */ + if( p_grandparent == p_root ) + { + return NULL; + } + else + { + p_parent = p_grandparent; + p_grandparent = p_parent->p_parent; + } + } + } + /* We reached root */ + return NULL; +} + + +/* Recursively search the tree for previous item */ +playlist_item_t *GetPrevItem( playlist_t *p_playlist, + playlist_item_t *p_root, + playlist_item_t *p_item ) +{ + playlist_item_t *p_parent; + int i; + + /* Node with children, get the last one */ + if( p_item && p_item->i_children > 0 ) + return p_item->pp_children[p_item->i_children - 1]; + + /* Last child of its parent ? */ + if( p_item != NULL ) + p_parent = p_item->p_parent; + else + { + msg_Err( p_playlist, "Get the last one" ); + abort(); + }; + + for( i = p_parent->i_children -1 ; i >= 0 ; i-- ) + { + if( p_parent->pp_children[i] == p_item ) + { + if( i-1 < 0 ) + { + /* Was already the first sibling. Look for uncles */ + PL_DEBUG2( "current item is the first of its node," + "looking for uncle from %s", + p_parent->p_input->psz_name ); + if( p_parent == p_root ) + { + PL_DEBUG2( "already at root" ); + return NULL; + } + return GetPrevUncle( p_playlist, p_item, p_root ); + } + else + { + return p_parent->pp_children[i-1]; + } + } + } + return NULL; +} diff --git a/VLC/udp.c b/VLC/udp.c new file mode 100644 index 0000000..4c230c9 --- /dev/null +++ b/VLC/udp.c @@ -0,0 +1,888 @@ +/***************************************************************************** + * udp.c: + ***************************************************************************** + * Copyright (C) 2004-2006 the VideoLAN team + * Copyright © 2006-2007 Rémi Denis-Courmont + * + * $Id$ + * + * Authors: Laurent Aimar + * Rémi Denis-Courmont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#include + +#ifdef HAVE_SYS_TIME_H +# include +#endif + +#include "vlc_network.h" + +#ifdef WIN32 +# if defined(UNDER_CE) +# undef IP_MULTICAST_TTL +# define IP_MULTICAST_TTL 3 +# undef IP_ADD_MEMBERSHIP +# define IP_ADD_MEMBERSHIP 5 +# endif +# define EAFNOSUPPORT WSAEAFNOSUPPORT +# define if_nametoindex( str ) atoi( str ) +#else +# include +# ifdef HAVE_NET_IF_H +# include +# endif +#endif + +#ifdef HAVE_LINUX_DCCP_H +# include +# ifndef SOCK_DCCP /* provisional API */ +# define SOCK_DCCP 6 +# endif +#endif + +#ifndef SOL_IP +# define SOL_IP IPPROTO_IP +#endif +#ifndef SOL_IPV6 +# define SOL_IPV6 IPPROTO_IPV6 +#endif +#ifndef IPPROTO_IPV6 +# define IPPROTO_IPV6 41 /* IANA */ +#endif +#ifndef SOL_DCCP +# define SOL_DCCP IPPROTO_DCCP +#endif +#ifndef IPPROTO_DCCP +# define IPPROTO_DCCP 33 /* IANA */ +#endif +#ifndef SOL_UDPLITE +# define SOL_UDPLITE IPPROTO_UDPLITE +#endif +#ifndef IPPROTO_UDPLITE +# define IPPROTO_UDPLITE 136 /* IANA */ +#endif + +#if defined (HAVE_NETINET_UDPLITE_H) +# include +#elif defined (__linux__) +/* still missing from glibc 2.6 */ +# define UDPLITE_SEND_CSCOV 10 +# define UDPLITE_RECV_CSCOV 11 +#endif + +extern int net_Socket( vlc_object_t *p_this, int i_family, int i_socktype, + int i_protocol ); + +static int net_ListenSingle (vlc_object_t *obj, const char *host, int port, + int family, int protocol) +{ + struct addrinfo hints, *res; + + memset (&hints, 0, sizeof( hints )); + hints.ai_family = family; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_flags = AI_PASSIVE; + + if (host && !*host) + host = NULL; + + msg_Dbg (obj, "net: opening %s datagram port %d", host ?: "any", port); + + int val = vlc_getaddrinfo (obj, host, port, &hints, &res); + if (val) + { + msg_Err (obj, "Cannot resolve %s port %d : %s", host, port, + vlc_gai_strerror (val)); + return -1; + } + + val = -1; + + for (const struct addrinfo *ptr = res; ptr != NULL; ptr = ptr->ai_next) + { + int fd = net_Socket (obj, ptr->ai_family, ptr->ai_socktype, + protocol ?: ptr->ai_protocol); + if (fd == -1) + { + msg_Dbg (obj, "socket error: %m"); + continue; + } + + if (ptr->ai_next != NULL) + { +#ifdef IPV6_V6ONLY + if ((ptr->ai_family != AF_INET6) + || setsockopt (fd, SOL_IPV6, IPV6_V6ONLY, &(int){ 0 }, + sizeof (int))) +#endif + { + msg_Err (obj, "Multiple network protocols present"); + msg_Err (obj, "Please select network protocol manually"); + } + } + + /* Bind the socket */ +#if defined (WIN32) || defined (UNDER_CE) + if (net_SockAddrIsMulticast (ptr->ai_addr, ptr->ai_addrlen) + && (sizeof (struct sockaddr_storage) >= ptr->ai_addrlen)) + { + struct sockaddr_in6 dumb = + { + .sin6_family = ptr->ai_addr->sa_family, + .sin6_port = ((struct sockaddr_in *)(ptr->ai_addr))->sin_port + }; + bind (fd, (struct sockaddr *)&dumb, ptr->ai_addrlen); + } + else +#endif + if (bind (fd, ptr->ai_addr, ptr->ai_addrlen)) + { + msg_Err (obj, "socket bind error (%m)"); + net_Close (fd); + continue; + } + + if (net_SockAddrIsMulticast (ptr->ai_addr, ptr->ai_addrlen) + && net_Subscribe (obj, fd, ptr->ai_addr, ptr->ai_addrlen)) + { + net_Close (fd); + continue; + } + + val = fd; + break; + } + + vlc_freeaddrinfo (res); + return val; +} + + +static int net_SetMcastHopLimit( vlc_object_t *p_this, + int fd, int family, int hlim ) +{ + int proto, cmd; + + /* There is some confusion in the world whether IP_MULTICAST_TTL + * takes a byte or an int as an argument. + * BSD seems to indicate byte so we are going with that and use + * int as a fallback to be safe */ + switch( family ) + { +#ifdef IP_MULTICAST_TTL + case AF_INET: + proto = SOL_IP; + cmd = IP_MULTICAST_TTL; + break; +#endif + +#ifdef IPV6_MULTICAST_HOPS + case AF_INET6: + proto = SOL_IPV6; + cmd = IPV6_MULTICAST_HOPS; + break; +#endif + + default: + errno = EAFNOSUPPORT; + msg_Warn( p_this, "%m" ); + return VLC_EGENERIC; + } + + if( setsockopt( fd, proto, cmd, &hlim, sizeof( hlim ) ) < 0 ) + { + /* BSD compatibility */ + unsigned char buf; + + buf = (unsigned char)(( hlim > 255 ) ? 255 : hlim); + if( setsockopt( fd, proto, cmd, &buf, sizeof( buf ) ) ) + return VLC_EGENERIC; + } + + return VLC_SUCCESS; +} + + +static int net_SetMcastOutIface (int fd, int family, int scope) +{ + switch (family) + { +#ifdef IPV6_MULTICAST_IF + case AF_INET6: + return setsockopt (fd, SOL_IPV6, IPV6_MULTICAST_IF, + &scope, sizeof (scope)); +#endif + +#ifdef __linux__ + case AF_INET: + { + struct ip_mreqn req = { .imr_ifindex = scope }; + + return setsockopt (fd, SOL_IP, IP_MULTICAST_IF, &req, + sizeof (req)); + } +#endif + } + + errno = EAFNOSUPPORT; + return -1; +} + + +static inline int net_SetMcastOutIPv4 (int fd, struct in_addr ipv4) +{ +#ifdef IP_MULTICAST_IF + return setsockopt( fd, SOL_IP, IP_MULTICAST_IF, &ipv4, sizeof (ipv4)); +#else + errno = EAFNOSUPPORT; + return -1; +#endif +} + + +static int net_SetMcastOut (vlc_object_t *p_this, int fd, int family, + const char *iface, const char *addr) +{ + if (iface != NULL) + { + int scope = if_nametoindex (iface); + if (scope == 0) + { + msg_Err (p_this, "invalid multicast interface: %s", iface); + return -1; + } + + if (net_SetMcastOutIface (fd, family, scope) == 0) + return 0; + + msg_Err (p_this, "%s: %m", iface); + } + + if (addr != NULL) + { + if (family == AF_INET) + { + struct in_addr ipv4; + if (inet_pton (AF_INET, addr, &ipv4) <= 0) + { + msg_Err (p_this, "invalid IPv4 address for multicast: %s", + addr); + return -1; + } + + if (net_SetMcastOutIPv4 (fd, ipv4) == 0) + return 0; + + msg_Err (p_this, "%s: %m", addr); + } + } + + return -1; +} + + +/** + * Old-style any-source multicast join. + * In use on Windows XP/2003 and older. + */ +static int +net_IPv4Join (vlc_object_t *obj, int fd, + const struct sockaddr_in *src, const struct sockaddr_in *grp) +{ +#ifdef IP_ADD_MEMBERSHIP + union + { + struct ip_mreq gr4; +# ifdef IP_ADD_SOURCE_MEMBERSHIP + struct ip_mreq_source gsr4; +# endif + } opt; + int cmd; + struct in_addr id = { .s_addr = INADDR_ANY }; + socklen_t optlen; + + /* Multicast interface IPv4 address */ + char *iface = var_CreateGetNonEmptyString (obj, "miface-addr"); + if ((iface != NULL) + && (inet_pton (AF_INET, iface, &id) <= 0)) + { + msg_Err (obj, "invalid multicast interface address %s", iface); + free (iface); + goto error; + } + free (iface); + + memset (&opt, 0, sizeof (opt)); + if (src != NULL) + { +# ifdef IP_ADD_SOURCE_MEMBERSHIP + cmd = IP_ADD_SOURCE_MEMBERSHIP; + opt.gsr4.imr_multiaddr = grp->sin_addr; + opt.gsr4.imr_sourceaddr = src->sin_addr; + opt.gsr4.imr_interface = id; + optlen = sizeof (opt.gsr4); +# else + errno = ENOSYS; + goto error; +# endif + } + else + { + cmd = IP_ADD_MEMBERSHIP; + opt.gr4.imr_multiaddr = grp->sin_addr; + opt.gr4.imr_interface = id; + optlen = sizeof (opt.gr4); + } + + msg_Dbg (obj, "IP_ADD_%sMEMBERSHIP multicast request", + (src != NULL) ? "SOURCE_" : ""); + + if (setsockopt (fd, SOL_IP, cmd, &opt, optlen) == 0) + return 0; + +error: +#endif + + msg_Err (obj, "cannot join IPv4 multicast group (%m)"); + return -1; +} + + +static int +net_IPv6Join (vlc_object_t *obj, int fd, const struct sockaddr_in6 *src) +{ +#ifdef IPV6_JOIN_GROUP + struct ipv6_mreq gr6; + memset (&gr6, 0, sizeof (gr6)); + gr6.ipv6mr_interface = src->sin6_scope_id; + memcpy (&gr6.ipv6mr_multiaddr, &src->sin6_addr, 16); + + msg_Dbg (obj, "IPV6_JOIN_GROUP multicast request"); + + if (!setsockopt (fd, SOL_IPV6, IPV6_JOIN_GROUP, &gr6, sizeof (gr6))) + return 0; +#else + errno = ENOSYS; +#endif + + msg_Err (obj, "cannot join IPv6 any-source multicast group (%m)"); + return -1; +} + + +#if defined (WIN32) && !defined (MCAST_JOIN_SOURCE_GROUP) +/* + * I hate manual definitions: Error-prone. Portability hell. + * Developers shall use UP-TO-DATE compilers. Full point. + * If you remove the warning, you remove the whole ifndef. + */ +# warning Your C headers are out-of-date. Please update. + +# define MCAST_JOIN_GROUP 41 +struct group_req +{ + ULONG gr_interface; + struct sockaddr_storage gr_group; +}; + +# define MCAST_JOIN_SOURCE_GROUP 45 /* from */ +struct group_source_req +{ + uint32_t gsr_interface; + struct sockaddr_storage gsr_group; + struct sockaddr_storage gsr_source; +}; +#endif + +/** + * IP-agnostic multicast join, + * with fallback to old APIs, and fallback from SSM to ASM. + */ +static int +net_SourceSubscribe (vlc_object_t *obj, int fd, + const struct sockaddr *src, socklen_t srclen, + const struct sockaddr *grp, socklen_t grplen) +{ + int level, iid = 0; + + char *iface = var_CreateGetNonEmptyString (obj, "miface"); + if (iface != NULL) + { + iid = if_nametoindex (iface); + if (iid == 0) + { + msg_Err (obj, "invalid multicast interface: %s", iface); + free (iface); + return -1; + } + free (iface); + } + + switch (grp->sa_family) + { +#ifdef AF_INET6 + case AF_INET6: + level = SOL_IPV6; + if (((const struct sockaddr_in6 *)grp)->sin6_scope_id) + iid = ((const struct sockaddr_in6 *)grp)->sin6_scope_id; + break; +#endif + + case AF_INET: + level = SOL_IP; + break; + + default: + errno = EAFNOSUPPORT; + return -1; + } + + if (src != NULL) + switch (src->sa_family) + { +#ifdef AF_INET6 + case AF_INET6: + if (memcmp (&((const struct sockaddr_in6 *)src)->sin6_addr, + &in6addr_any, sizeof (in6addr_any)) == 0) + src = NULL; + break; +#endif + + case AF_INET: + if (((const struct sockaddr_in *)src)->sin_addr.s_addr + == INADDR_ANY) + src = NULL; + break; + } + + + /* Agnostic ASM/SSM multicast join */ +#ifdef MCAST_JOIN_SOURCE_GROUP + union + { + struct group_req gr; + struct group_source_req gsr; + } opt; + socklen_t optlen; + + memset (&opt, 0, sizeof (opt)); + + if (src != NULL) + { + if ((grplen > sizeof (opt.gsr.gsr_group)) + || (srclen > sizeof (opt.gsr.gsr_source))) + return -1; + + opt.gsr.gsr_interface = iid; + memcpy (&opt.gsr.gsr_source, src, srclen); + memcpy (&opt.gsr.gsr_group, grp, grplen); + optlen = sizeof (opt.gsr); + } + else + { + if (grplen > sizeof (opt.gr.gr_group)) + return -1; + + opt.gr.gr_interface = iid; + memcpy (&opt.gr.gr_group, grp, grplen); + optlen = sizeof (opt.gr); + } + + msg_Dbg (obj, "Multicast %sgroup join request", src ? "source " : ""); + + if (setsockopt (fd, level, + src ? MCAST_JOIN_SOURCE_GROUP : MCAST_JOIN_GROUP, + (void *)&opt, optlen) == 0) + return 0; +#endif + + /* Fallback to IPv-specific APIs */ + if ((src != NULL) && (src->sa_family != grp->sa_family)) + return -1; + + switch (grp->sa_family) + { + case AF_INET: + if ((grplen < sizeof (struct sockaddr_in)) + || ((src != NULL) && (srclen < sizeof (struct sockaddr_in)))) + return -1; + + if (net_IPv4Join (obj, fd, (const struct sockaddr_in *)src, + (const struct sockaddr_in *)grp) == 0) + return 0; + break; + +#ifdef AF_INET6 + case AF_INET6: + if ((grplen < sizeof (struct sockaddr_in6)) + || ((src != NULL) && (srclen < sizeof (struct sockaddr_in6)))) + return -1; + + /* IPv6-specific SSM API does not exist. So if we're here + * it means IPv6 SSM is not supported on this OS and we + * directly fallback to ASM */ + + if (net_IPv6Join (obj, fd, (const struct sockaddr_in6 *)grp) == 0) + return 0; + break; +#endif + } + + msg_Err (obj, "Multicast group join error (%m)"); + + if (src != NULL) + { + msg_Warn (obj, "Trying ASM instead of SSM..."); + return net_Subscribe (obj, fd, grp, grplen); + } + + msg_Err (obj, "Multicast not supported"); + return -1; +} + + +int net_Subscribe (vlc_object_t *obj, int fd, + const struct sockaddr *addr, socklen_t addrlen) +{ + return net_SourceSubscribe (obj, fd, NULL, 0, addr, addrlen); +} + + +static int net_SetDSCP( int fd, uint8_t dscp ) +{ + struct sockaddr_storage addr; + if( getsockname( fd, (struct sockaddr *)&addr, &(socklen_t){ sizeof (addr) }) ) + return -1; + + int level, cmd; + + switch( addr.ss_family ) + { +#ifdef IPV6_TCLASS + case AF_INET6: + level = SOL_IPV6; + cmd = IPV6_TCLASS; + break; +#endif + + case AF_INET: + level = SOL_IP; + cmd = IP_TOS; + break; + + default: +#ifdef ENOPROTOOPT + errno = ENOPROTOOPT; +#endif + return -1; + } + + return setsockopt( fd, level, cmd, &(int){ dscp }, sizeof (int)); +} + + +/***************************************************************************** + * __net_ConnectDgram: + ***************************************************************************** + * Open a datagram socket to send data to a defined destination, with an + * optional hop limit. + *****************************************************************************/ +int __net_ConnectDgram( vlc_object_t *p_this, const char *psz_host, int i_port, + int i_hlim, int proto ) +{ + struct addrinfo hints, *res, *ptr; + int i_val, i_handle = -1; + bool b_unreach = false; + + if( i_hlim < 1 ) + i_hlim = var_CreateGetInteger( p_this, "ttl" ); + + memset( &hints, 0, sizeof( hints ) ); + hints.ai_socktype = SOCK_DGRAM; + + msg_Dbg( p_this, "net: connecting to [%s]:%d", psz_host, i_port ); + + i_val = vlc_getaddrinfo( p_this, psz_host, i_port, &hints, &res ); + if( i_val ) + { + msg_Err( p_this, "cannot resolve [%s]:%d : %s", psz_host, i_port, + vlc_gai_strerror( i_val ) ); + return -1; + } + + for( ptr = res; ptr != NULL; ptr = ptr->ai_next ) + { + char *str; + int fd = net_Socket (p_this, ptr->ai_family, ptr->ai_socktype, + proto ?: ptr->ai_protocol); + if (fd == -1) + continue; + +#if !defined( SYS_BEOS ) + /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) + * to avoid packet loss caused by scheduling problems */ + setsockopt (fd, SOL_SOCKET, SO_RCVBUF, &(int){ 0x80000 }, sizeof (int)); + setsockopt (fd, SOL_SOCKET, SO_SNDBUF, &(int){ 0x80000 }, sizeof (int)); + + /* Allow broadcast sending */ + setsockopt (fd, SOL_SOCKET, SO_BROADCAST, &(int){ 1 }, sizeof (int)); +#endif + + if( i_hlim > 0 ) + net_SetMcastHopLimit( p_this, fd, ptr->ai_family, i_hlim ); + + str = var_CreateGetNonEmptyString (p_this, "miface"); + if (str != NULL) + { + net_SetMcastOut (p_this, fd, ptr->ai_family, str, NULL); + free (str); + } + + str = var_CreateGetNonEmptyString (p_this, "miface-addr"); + if (str != NULL) + { + net_SetMcastOut (p_this, fd, ptr->ai_family, NULL, str); + free (str); + } + + net_SetDSCP (fd, var_CreateGetInteger (p_this, "dscp")); + + if( connect( fd, ptr->ai_addr, ptr->ai_addrlen ) == 0 ) + { + /* success */ + i_handle = fd; + break; + } + +#if defined( WIN32 ) || defined( UNDER_CE ) + if( WSAGetLastError () == WSAENETUNREACH ) +#else + if( errno == ENETUNREACH ) +#endif + b_unreach = true; + else + { + msg_Warn( p_this, "%s port %d : %m", psz_host, i_port); + net_Close( fd ); + continue; + } + } + + vlc_freeaddrinfo( res ); + + if( i_handle == -1 ) + { + if( b_unreach ) + msg_Err( p_this, "Host %s port %d is unreachable", psz_host, + i_port ); + return -1; + } + + return i_handle; +} + + +/***************************************************************************** + * __net_OpenDgram: + ***************************************************************************** + * OpenDgram a datagram socket and return a handle + *****************************************************************************/ +int __net_OpenDgram( vlc_object_t *obj, const char *psz_bind, int i_bind, + const char *psz_server, int i_server, + int family, int protocol ) +{ + if ((psz_server == NULL) || (psz_server[0] == '\0')) + return net_ListenSingle (obj, psz_bind, i_bind, family, protocol); + + msg_Dbg (obj, "net: connecting to [%s]:%d from [%s]:%d", + psz_server, i_server, psz_bind, i_bind); + + struct addrinfo hints, *loc, *rem; + int val; + + memset (&hints, 0, sizeof (hints)); + hints.ai_family = family; + hints.ai_socktype = SOCK_DGRAM; + + val = vlc_getaddrinfo (obj, psz_server, i_server, &hints, &rem); + if (val) + { + msg_Err (obj, "cannot resolve %s port %d : %s", psz_bind, i_bind, + vlc_gai_strerror (val)); + return -1; + } + + hints.ai_flags = AI_PASSIVE; + val = vlc_getaddrinfo (obj, psz_bind, i_bind, &hints, &loc); + if (val) + { + msg_Err (obj, "cannot resolve %s port %d : %s", psz_bind, i_bind, + vlc_gai_strerror (val)); + vlc_freeaddrinfo (rem); + return -1; + } + + for (struct addrinfo *ptr = loc; ptr != NULL; ptr = ptr->ai_next) + { + int fd = net_Socket (obj, ptr->ai_family, ptr->ai_socktype, + protocol ?: ptr->ai_protocol); + if (fd == -1) + continue; // usually, address family not supported + +#ifdef SO_REUSEPORT + setsockopt (fd, SOL_SOCKET, SO_REUSEPORT, &(int){ 1 }, sizeof (int)); +#endif + +#ifdef SO_RCVBUF + /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) + * to avoid packet loss caused in case of scheduling hiccups */ + setsockopt (fd, SOL_SOCKET, SO_RCVBUF, + (void *)&(int){ 0x80000 }, sizeof (int)); + setsockopt (fd, SOL_SOCKET, SO_SNDBUF, + (void *)&(int){ 0x80000 }, sizeof (int)); +#endif + +#if defined (WIN32) || defined (UNDER_CE) + if (net_SockAddrIsMulticast (ptr->ai_addr, ptr->ai_addrlen) + && (sizeof (struct sockaddr_storage) >= ptr->ai_addrlen)) + { + // This works for IPv4 too - don't worry! + struct sockaddr_in6 dumb = + { + .sin6_family = ptr->ai_addr->sa_family, + .sin6_port = ((struct sockaddr_in *)(ptr->ai_addr))->sin_port + }; + + bind (fd, (struct sockaddr *)&dumb, ptr->ai_addrlen); + } + else +#endif + if (bind (fd, ptr->ai_addr, ptr->ai_addrlen)) + { + net_Close (fd); + continue; + } + + val = -1; + for (struct addrinfo *ptr2 = rem; ptr2 != NULL; ptr2 = ptr2->ai_next) + { + if ((ptr2->ai_family != ptr->ai_family) + || (ptr2->ai_socktype != ptr->ai_socktype) + || (ptr2->ai_protocol != ptr->ai_protocol)) + continue; + + if (net_SockAddrIsMulticast (ptr->ai_addr, ptr->ai_addrlen) + ? net_SourceSubscribe (obj, fd, + ptr2->ai_addr, ptr2->ai_addrlen, + ptr->ai_addr, ptr->ai_addrlen) + : connect (fd, ptr2->ai_addr, ptr2->ai_addrlen)) + { + msg_Err (obj, "cannot connect to %s port %d: %m", + psz_server, i_server); + continue; + } + val = fd; + break; + } + + if (val != -1) + break; + + close (fd); + } + + vlc_freeaddrinfo (rem); + vlc_freeaddrinfo (loc); + return val; +} + + +/** + * net_SetCSCov: + * Sets the send and receive checksum coverage of a socket: + * @param fd socket + * @param sendcov payload coverage of sent packets (bytes), -1 for full + * @param recvcov minimum payload coverage of received packets, -1 for full + */ +int net_SetCSCov (int fd, int sendcov, int recvcov) +{ + int type; + + if (getsockopt (fd, SOL_SOCKET, SO_TYPE, + &type, &(socklen_t){ sizeof (type) })) + return VLC_EGENERIC; + + switch (type) + { +#ifdef UDPLITE_RECV_CSCOV + case SOCK_DGRAM: /* UDP-Lite */ + if (sendcov == -1) + sendcov = 0; + else + sendcov += 8; /* partial */ + if (setsockopt (fd, SOL_UDPLITE, UDPLITE_SEND_CSCOV, &sendcov, + sizeof (sendcov))) + return VLC_EGENERIC; + + if (recvcov == -1) + recvcov = 0; + else + recvcov += 8; + if (setsockopt (fd, SOL_UDPLITE, UDPLITE_RECV_CSCOV, + &recvcov, sizeof (recvcov))) + return VLC_EGENERIC; + + return VLC_SUCCESS; +#endif +#ifdef DCCP_SOCKOPT_SEND_CSCOV + case SOCK_DCCP: /* DCCP and its ill-named socket type */ + if ((sendcov == -1) || (sendcov > 56)) + sendcov = 0; + else + sendcov = (sendcov + 3) / 4; + if (setsockopt (fd, SOL_DCCP, DCCP_SOCKOPT_SEND_CSCOV, + &sendcov, sizeof (sendcov))) + return VLC_EGENERIC; + + if ((recvcov == -1) || (recvcov > 56)) + recvcov = 0; + else + recvcov = (recvcov + 3) / 4; + if (setsockopt (fd, SOL_DCCP, DCCP_SOCKOPT_RECV_CSCOV, + &recvcov, sizeof (recvcov))) + return VLC_EGENERIC; + + return VLC_SUCCESS; +#endif + } +#if !defined( UDPLITE_RECV_CSCOV ) && !defined( DCCP_SOCKOPT_SEND_CSCOV ) + VLC_UNUSED(sendcov); + VLC_UNUSED(recvcov); +#endif + + return VLC_EGENERIC; +} diff --git a/VLC/unicode.c b/VLC/unicode.c new file mode 100644 index 0000000..8eed9d4 --- /dev/null +++ b/VLC/unicode.c @@ -0,0 +1,809 @@ +/***************************************************************************** + * unicode.c: Unicode <-> locale functions + ***************************************************************************** + * Copyright (C) 2005-2006 the VideoLAN team + * Copyright © 2005-2008 Rémi Denis-Courmont + * + * Authors: Rémi Denis-Courmont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "vlc_charset.h" +#include "libvlc.h" /* utf8_mkdir */ + +#include + +#include +#include +#include +#include +#include +#ifdef HAVE_DIRENT_H +# include +#endif +#ifdef UNDER_CE +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif + +# include + +#ifdef WIN32 +# include +#else +# include +#endif + +#ifndef HAVE_LSTAT +# define lstat( a, b ) stat(a, b) +#endif + +#ifdef __APPLE__ +/* Define this if the OS always use UTF-8 internally */ +# define ASSUME_UTF8 1 +#endif + +#if defined (ASSUME_UTF8) +/* Cool */ +#elif defined (WIN32) || defined (UNDER_CE) +# define USE_MB2MB 1 +#elif defined (HAVE_ICONV) +# define USE_ICONV 1 +#else +# error No UTF8 charset conversion implemented on this platform! +#endif + +#if defined (USE_ICONV) +# include +static char charset[sizeof ("CSISO11SWEDISHFORNAMES")] = ""; + +static void find_charset_once (void) +{ + strlcpy (charset, nl_langinfo (CODESET), sizeof (charset)); + if (!strcasecmp (charset, "ASCII") + || !strcasecmp (charset, "ANSI_X3.4-1968")) + strcpy (charset, "UTF-8"); /* superset... */ +} + +static int find_charset (void) +{ + static pthread_once_t once = PTHREAD_ONCE_INIT; + pthread_once (&once, find_charset_once); + return !strcasecmp (charset, "UTF-8"); +} +#endif + + +static char *locale_fast (const char *string, bool from) +{ +#if defined (USE_ICONV) + if (find_charset ()) + return (char *)string; + + vlc_iconv_t hd = vlc_iconv_open (from ? "UTF-8" : charset, + from ? charset : "UTF-8"); + if (hd == (vlc_iconv_t)(-1)) + return NULL; /* Uho! */ + + const char *iptr = string; + size_t inb = strlen (string); + size_t outb = inb * 6 + 1; + char output[outb], *optr = output; + + if (string == NULL) + return NULL; + + while (vlc_iconv (hd, &iptr, &inb, &optr, &outb) == (size_t)(-1)) + { + *optr++ = '?'; + outb--; + iptr++; + inb--; + vlc_iconv (hd, NULL, NULL, NULL, NULL); /* reset */ + } + *optr = '\0'; + vlc_iconv_close (hd); + + assert (inb == 0); + assert (*iptr == '\0'); + assert (*optr == '\0'); + assert (strlen (output) == (size_t)(optr - output)); + return strdup (output); +#elif defined (USE_MB2MB) + char *out; + int len; + + if (string == NULL) + return NULL; + + len = 1 + MultiByteToWideChar (from ? CP_ACP : CP_UTF8, + 0, string, -1, NULL, 0); + wchar_t wide[len]; + + MultiByteToWideChar (from ? CP_ACP : CP_UTF8, 0, string, -1, wide, len); + len = 1 + WideCharToMultiByte (from ? CP_UTF8 : CP_ACP, 0, wide, -1, + NULL, 0, NULL, NULL); + out = malloc (len); + if (out == NULL) + return NULL; + + WideCharToMultiByte (from ? CP_UTF8 : CP_ACP, 0, wide, -1, out, len, + NULL, NULL); + return out; +#else + (void)from; + return (char *)string; +#endif +} + + +static inline char *locale_dup (const char *string, bool from) +{ + assert( string ); + +#if defined (USE_ICONV) + if (find_charset ()) + return strdup (string); + return locale_fast (string, from); +#elif defined (USE_MB2MB) + return locale_fast (string, from); +#else + (void)from; + return strdup (string); +#endif +} + +/** + * Releases (if needed) a localized or uniformized string. + * @param str non-NULL return value from FromLocale() or ToLocale(). + */ +void LocaleFree (const char *str) +{ +#if defined (USE_ICONV) + if (!find_charset ()) + free ((char *)str); +#elif defined (USE_MB2MB) + free ((char *)str); +#else + (void)str; +#endif +} + + +/** + * Converts a string from the system locale character encoding to UTF-8. + * + * @param locale nul-terminated string to convert + * + * @return a nul-terminated UTF-8 string, or NULL in case of error. + * To avoid memory leak, you have to pass the result to LocaleFree() + * when it is no longer needed. + */ +char *FromLocale (const char *locale) +{ + return locale_fast (locale, true); +} + +/** + * converts a string from the system locale character encoding to utf-8, + * the result is always allocated on the heap. + * + * @param locale nul-terminated string to convert + * + * @return a nul-terminated utf-8 string, or null in case of error. + * The result must be freed using free() - as with the strdup() function. + */ +char *FromLocaleDup (const char *locale) +{ + return locale_dup (locale, true); +} + + +/** + * ToLocale: converts an UTF-8 string to local system encoding. + * + * @param utf8 nul-terminated string to be converted + * + * @return a nul-terminated string, or NULL in case of error. + * To avoid memory leak, you have to pass the result to LocaleFree() + * when it is no longer needed. + */ +char *ToLocale (const char *utf8) +{ + return locale_fast (utf8, false); +} + + +/** + * converts a string from UTF-8 to the system locale character encoding, + * the result is always allocated on the heap. + * + * @param utf8 nul-terminated string to convert + * + * @return a nul-terminated string, or null in case of error. + * The result must be freed using free() - as with the strdup() function. + */ +char *ToLocaleDup (const char *utf8) +{ + return locale_dup (utf8, false); +} + + +/** + * Opens a system file handle using UTF-8 paths. + * + * @param filename file path to open (with UTF-8 encoding) + * @param flags open() flags, see the C library open() documentation + * @param mode file permissions if creating a new file + * @return a file handle on success, -1 on error (see errno). + */ +int utf8_open (const char *filename, int flags, mode_t mode) +{ +#if defined (WIN32) || defined (UNDER_CE) + if (GetVersion() < 0x80000000) + { + /* for Windows NT and above */ + wchar_t wpath[MAX_PATH + 1]; + + if (!MultiByteToWideChar (CP_UTF8, 0, filename, -1, wpath, MAX_PATH)) + { + errno = ENOENT; + return -1; + } + wpath[MAX_PATH] = L'\0'; + + /* + * open() cannot open files with non-“ANSI” characters on Windows. + * We use _wopen() instead. Same thing for mkdir() and stat(). + */ + return _wopen (wpath, flags, mode); + } +#endif + const char *local_name = ToLocale (filename); + + if (local_name == NULL) + { + errno = ENOENT; + return -1; + } + + int fd = open (local_name, flags, mode); + LocaleFree (local_name); + return fd; +} + +/** + * Opens a FILE pointer using UTF-8 filenames. + * @param filename file path, using UTF-8 encoding + * @param mode fopen file open mode + * @return NULL on error, an open FILE pointer on success. + */ +FILE *utf8_fopen (const char *filename, const char *mode) +{ + int rwflags = 0, oflags = 0; + bool append = false; + + for (const char *ptr = mode; *ptr; ptr++) + { + switch (*ptr) + { + case 'r': + rwflags = O_RDONLY; + break; + + case 'a': + rwflags = O_WRONLY; + oflags |= O_CREAT; + append = true; + break; + + case 'w': + rwflags = O_WRONLY; + oflags |= O_CREAT | O_TRUNC; + break; + + case '+': + rwflags = O_RDWR; + break; + +#ifdef O_TEXT + case 't': + oflags |= O_TEXT; + break; +#endif + } + } + + int fd = utf8_open (filename, rwflags | oflags, 0666); + if (fd == -1) + return NULL; + + if (append && (lseek (fd, 0, SEEK_END) == -1)) + { + close (fd); + return NULL; + } + + FILE *stream = fdopen (fd, mode); + if (stream == NULL) + close (fd); + + return stream; +} + +/** + * Creates a directory using UTF-8 paths. + * + * @param dirname a UTF-8 string with the name of the directory that you + * want to create. + * @param mode directory permissions + * @return 0 on success, -1 on error (see errno). + */ +int utf8_mkdir( const char *dirname, mode_t mode ) +{ +#if defined (UNDER_CE) || defined (WIN32) + VLC_UNUSED( mode ); + + wchar_t wname[MAX_PATH + 1]; + char mod[MAX_PATH + 1]; + int i; + + /* Convert '/' into '\' */ + for( i = 0; *dirname; i++ ) + { + if( i == MAX_PATH ) + return -1; /* overflow */ + + if( *dirname == '/' ) + mod[i] = '\\'; + else + mod[i] = *dirname; + dirname++; + + } + mod[i] = 0; + + if( MultiByteToWideChar( CP_UTF8, 0, mod, -1, wname, MAX_PATH ) == 0 ) + { + errno = ENOENT; + return -1; + } + wname[MAX_PATH] = L'\0'; + + if( CreateDirectoryW( wname, NULL ) == 0 ) + { + if( GetLastError( ) == ERROR_ALREADY_EXISTS ) + errno = EEXIST; + else + errno = ENOENT; + return -1; + } + return 0; +#else + char *locname = ToLocale( dirname ); + int res; + + if( locname == NULL ) + { + errno = ENOENT; + return -1; + } + res = mkdir( locname, mode ); + + LocaleFree( locname ); + return res; +#endif +} + +/** + * Opens a DIR pointer using UTF-8 paths + * + * @param dirname UTF-8 representation of the directory name + * @return a pointer to the DIR struct, or NULL in case of error. + * Release with standard closedir(). + */ +DIR *utf8_opendir( const char *dirname ) +{ +#ifdef WIN32 + wchar_t wname[MAX_PATH + 1]; + + if (MultiByteToWideChar (CP_UTF8, 0, dirname, -1, wname, MAX_PATH)) + { + wname[MAX_PATH] = L'\0'; + return (DIR *)vlc_wopendir (wname); + } +#else + const char *local_name = ToLocale( dirname ); + + if( local_name != NULL ) + { + DIR *dir = opendir( local_name ); + LocaleFree( local_name ); + return dir; + } +#endif + + errno = ENOENT; + return NULL; +} + +/** + * Reads the next file name from an open directory. + * + * @param dir The directory that is being read + * + * @return a UTF-8 string of the directory entry. + * Use free() to free this memory. + */ +char *utf8_readdir( DIR *dir ) +{ +#ifdef WIN32 + struct _wdirent *ent = vlc_wreaddir (dir); + if (ent == NULL) + return NULL; + + return FromWide (ent->d_name); +#else + struct dirent *ent; + + ent = readdir( (DIR *)dir ); + if( ent == NULL ) + return NULL; + + return vlc_fix_readdir( ent->d_name ); +#endif +} + +static int dummy_select( const char *str ) +{ + (void)str; + return 1; +} + +/** + * Does the same as utf8_scandir(), but takes an open directory pointer + * instead of a directory path. + */ +int utf8_loaddir( DIR *dir, char ***namelist, + int (*select)( const char * ), + int (*compar)( const char **, const char ** ) ) +{ + if( select == NULL ) + select = dummy_select; + + if( dir == NULL ) + return -1; + else + { + char **tab = NULL; + char *entry; + unsigned num = 0; + + rewinddir( dir ); + + while( ( entry = utf8_readdir( dir ) ) != NULL ) + { + char **newtab; + + if( !select( entry ) ) + { + free( entry ); + continue; + } + + newtab = realloc( tab, sizeof( char * ) * (num + 1) ); + if( newtab == NULL ) + { + free( entry ); + goto error; + } + tab = newtab; + tab[num++] = entry; + } + + if( compar != NULL ) + qsort( tab, num, sizeof( tab[0] ), + (int (*)( const void *, const void *))compar ); + + *namelist = tab; + return num; + + error:{ + unsigned i; + + for( i = 0; i < num; i++ ) + free( tab[i] ); + if( tab != NULL ) + free( tab ); + } + } + return -1; +} + +/** + * Selects file entries from a directory, as GNU C scandir(), yet using + * UTF-8 file names. + * + * @param dirname UTF-8 diretory path + * @param pointer [OUT] pointer set, on succesful completion, to the address + * of a table of UTF-8 filenames. All filenames must be freed with free(). + * The table itself must be freed with free() as well. + * + * @return How many file names were selected (possibly 0), + * or -1 in case of error. + */ +int utf8_scandir( const char *dirname, char ***namelist, + int (*select)( const char * ), + int (*compar)( const char **, const char ** ) ) +{ + DIR *dir = utf8_opendir (dirname); + int val = -1; + + if (dir != NULL) + { + val = utf8_loaddir (dir, namelist, select, compar); + closedir (dir); + } + return val; +} + +static int utf8_statEx( const char *filename, struct stat *buf, + bool deref ) +{ +#if defined (WIN32) || defined (UNDER_CE) + /* retrieve Windows OS version */ + if( GetVersion() < 0x80000000 ) + { + /* for Windows NT and above */ + wchar_t wpath[MAX_PATH + 1]; + + if( !MultiByteToWideChar( CP_UTF8, 0, filename, -1, wpath, MAX_PATH ) ) + { + errno = ENOENT; + return -1; + } + wpath[MAX_PATH] = L'\0'; + + return _wstati64( wpath, buf ); + } +#endif +#ifdef HAVE_SYS_STAT_H + const char *local_name = ToLocale( filename ); + + if( local_name != NULL ) + { + int res = deref ? stat( local_name, buf ) + : lstat( local_name, buf ); + LocaleFree( local_name ); + return res; + } + errno = ENOENT; +#endif + return -1; +} + +/** + * Finds file/inode informations, as stat(). + * Consider usign fstat() instead, if possible. + * + * @param filename UTF-8 file path + */ +int utf8_stat( const char *filename, struct stat *buf) +{ + return utf8_statEx( filename, buf, true ); +} + +/** + * Finds file/inode informations, as lstat(). + * Consider usign fstat() instead, if possible. + * + * @param filename UTF-8 file path + */ +int utf8_lstat( const char *filename, struct stat *buf) +{ + return utf8_statEx( filename, buf, false ); +} + +/** + * Removes a file. + * + * @param filename a UTF-8 string with the name of the file you want to delete. + * @return A 0 return value indicates success. A -1 return value indicates an + * error, and an error code is stored in errno + */ +int utf8_unlink( const char *filename ) +{ +#if defined (WIN32) || defined (UNDER_CE) + if( GetVersion() < 0x80000000 ) + { + /* for Windows NT and above */ + wchar_t wpath[MAX_PATH + 1]; + + if( !MultiByteToWideChar( CP_UTF8, 0, filename, -1, wpath, MAX_PATH ) ) + { + errno = ENOENT; + return -1; + } + wpath[MAX_PATH] = L'\0'; + + /* + * unlink() cannot open files with non-“ANSI” characters on Windows. + * We use _wunlink() instead. + */ + return _wunlink( wpath ); + } +#endif + const char *local_name = ToLocale( filename ); + + if( local_name == NULL ) + { + errno = ENOENT; + return -1; + } + + int ret = unlink( local_name ); + LocaleFree( local_name ); + return ret; +} + + + +/** + * Formats an UTF-8 string as vasprintf(), then print it to stdout, with + * appropriate conversion to local encoding. + */ +static int utf8_vasprintf( char **str, const char *fmt, va_list ap ) +{ + char *utf8; + int res = vasprintf( &utf8, fmt, ap ); + if( res == -1 ) + return -1; + + *str = ToLocaleDup( utf8 ); + free( utf8 ); + return res; +} + +/** + * Formats an UTF-8 string as vfprintf(), then print it, with + * appropriate conversion to local encoding. + */ +int utf8_vfprintf( FILE *stream, const char *fmt, va_list ap ) +{ + char *str; + int res = utf8_vasprintf( &str, fmt, ap ); + if( res == -1 ) + return -1; + + fputs( str, stream ); + free( str ); + return res; +} + +/** + * Formats an UTF-8 string as fprintf(), then print it, with + * appropriate conversion to local encoding. + */ +int utf8_fprintf( FILE *stream, const char *fmt, ... ) +{ + va_list ap; + int res; + + va_start( ap, fmt ); + res = utf8_vfprintf( stream, fmt, ap ); + va_end( ap ); + return res; +} + + +static char *CheckUTF8( char *str, char rep ) +{ + uint8_t *ptr = (uint8_t *)str; + assert (str != NULL); + + for (;;) + { + uint8_t c = ptr[0]; + int charlen = -1; + + if (c == '\0') + break; + + for (int i = 0; i < 7; i++) + if ((c >> (7 - i)) == ((0xff >> (7 - i)) ^ 1)) + { + charlen = i; + break; + } + + switch (charlen) + { + case 0: // 7-bit ASCII character -> OK + ptr++; + continue; + + case -1: // 1111111x -> error + case 1: // continuation byte -> error + goto error; + } + + assert (charlen >= 2); + + uint32_t cp = c & ~((0xff >> (7 - charlen)) << (7 - charlen)); + for (int i = 1; i < charlen; i++) + { + assert (cp < (1 << 26)); + c = ptr[i]; + + if ((c == '\0') // unexpected end of string + || ((c >> 6) != 2)) // not a continuation byte + goto error; + + cp = (cp << 6) | (ptr[i] & 0x3f); + } + + if (cp < 128) // overlong (special case for ASCII) + goto error; + if (cp < (1u << (5 * charlen - 3))) // overlong + goto error; + + ptr += charlen; + continue; + + error: + if (rep == 0) + return NULL; + *ptr++ = rep; + str = NULL; + } + + return str; +} + +/** + * Replaces invalid/overlong UTF-8 sequences with question marks. + * Note that it is not possible to convert from Latin-1 to UTF-8 on the fly, + * so we don't try that, even though it would be less disruptive. + * + * @return str if it was valid UTF-8, NULL if not. + */ +char *EnsureUTF8( char *str ) +{ + return CheckUTF8( str, '?' ); +} + + +/** + * Checks whether a string is a valid UTF-8 byte sequence. + * + * @param str nul-terminated string to be checked + * + * @return str if it was valid UTF-8, NULL if not. + */ +const char *IsUTF8( const char *str ) +{ + return CheckUTF8( (char *)str, 0 ); +} diff --git a/VLC/variables.c b/VLC/variables.c new file mode 100644 index 0000000..1282f7f --- /dev/null +++ b/VLC/variables.c @@ -0,0 +1,1613 @@ +/***************************************************************************** + * variables.c: routines for object variables handling + ***************************************************************************** + * Copyright (C) 2002-2006 the VideoLAN team + * $Id$ + * + * Authors: Samuel Hocevar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "variables.h" + +#include "libvlc.h" + +#include "vlc_interface.h" + +/***************************************************************************** + * Private types + *****************************************************************************/ +struct callback_entry_t +{ + vlc_callback_t pf_callback; + void * p_data; +}; + +/***************************************************************************** + * Local comparison functions, returns 0 if v == w, < 0 if v < w, > 0 if v > w + *****************************************************************************/ +static int CmpBool( vlc_value_t v, vlc_value_t w ) { return v.b_bool ? w.b_bool ? 0 : 1 : w.b_bool ? -1 : 0; } +static int CmpInt( vlc_value_t v, vlc_value_t w ) { return v.i_int == w.i_int ? 0 : v.i_int > w.i_int ? 1 : -1; } +static int CmpTime( vlc_value_t v, vlc_value_t w ) +{ + return v.i_time == w.i_time ? 0 : v.i_time > w.i_time ? 1 : -1; +} +static int CmpString( vlc_value_t v, vlc_value_t w ) +{ + if( !v.psz_string ) + return !w.psz_string ? 0 : -1; + else + return !w.psz_string ? 1 : strcmp( v.psz_string, w.psz_string ); +} +static int CmpFloat( vlc_value_t v, vlc_value_t w ) { return v.f_float == w.f_float ? 0 : v.f_float > w.f_float ? 1 : -1; } +static int CmpAddress( vlc_value_t v, vlc_value_t w ) { return v.p_address == w.p_address ? 0 : v.p_address > w.p_address ? 1 : -1; } + +/***************************************************************************** + * Local duplication functions, and local deallocation functions + *****************************************************************************/ +static void DupDummy( vlc_value_t *p_val ) { (void)p_val; /* unused */ } +static void DupString( vlc_value_t *p_val ) { if( p_val->psz_string ) p_val->psz_string = strdup( p_val->psz_string ); } + +static void DupList( vlc_value_t *p_val ) +{ + int i; + vlc_list_t *p_list = malloc( sizeof(vlc_list_t) ); + + p_list->i_count = p_val->p_list->i_count; + if( p_val->p_list->i_count ) + { + p_list->p_values = malloc( p_list->i_count * sizeof(vlc_value_t) ); + p_list->pi_types = malloc( p_list->i_count * sizeof(int) ); + } + else + { + p_list->p_values = NULL; + p_list->pi_types = NULL; + } + + for( i = 0; i < p_list->i_count; i++ ) + { + p_list->p_values[i] = p_val->p_list->p_values[i]; + p_list->pi_types[i] = p_val->p_list->pi_types[i]; + switch( p_val->p_list->pi_types[i] & VLC_VAR_TYPE ) + { + case VLC_VAR_STRING: + + DupString( &p_list->p_values[i] ); + break; + default: + break; + } + } + + p_val->p_list = p_list; +} + +static void FreeDummy( vlc_value_t *p_val ) { (void)p_val; /* unused */ } +static void FreeString( vlc_value_t *p_val ) { free( p_val->psz_string ); } +static void FreeMutex( vlc_value_t *p_val ) { vlc_mutex_destroy( (vlc_mutex_t*)p_val->p_address ); free( p_val->p_address ); } + +static void FreeList( vlc_value_t *p_val ) +{ + int i; + for( i = 0; i < p_val->p_list->i_count; i++ ) + { + switch( p_val->p_list->pi_types[i] & VLC_VAR_TYPE ) + { + case VLC_VAR_STRING: + FreeString( &p_val->p_list->p_values[i] ); + break; + case VLC_VAR_MUTEX: + FreeMutex( &p_val->p_list->p_values[i] ); + break; + default: + break; + } + } + + if( p_val->p_list->i_count ) + { + free( p_val->p_list->p_values ); + free( p_val->p_list->pi_types ); + } + free( p_val->p_list ); +} + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +static int GetUnused ( vlc_object_t *, const char * ); +static uint32_t HashString ( const char * ); +static int Insert ( variable_t *, int, const char * ); +static int InsertInner ( variable_t *, int, uint32_t ); +static int Lookup ( variable_t *, int, const char * ); +static int LookupInner ( variable_t *, int, uint32_t ); + +static void CheckValue ( variable_t *, vlc_value_t * ); + +static int InheritValue( vlc_object_t *, const char *, vlc_value_t *, + int ); + +/** + * Initialize a vlc variable + * + * We hash the given string and insert it into the sorted list. The insertion + * may require slow memory copies, but think about what we gain in the log(n) + * lookup phase when setting/getting the variable value! + * + * \param p_this The object in which to create the variable + * \param psz_name The name of the variable + * \param i_type The variables type. Must be one of \ref var_type combined with + * zero or more \ref var_flags + */ +int __var_Create( vlc_object_t *p_this, const char *psz_name, int i_type ) +{ + int i_new; + variable_t *p_var; + static vlc_list_t dummy_null_list = {0, NULL, NULL}; + vlc_object_internals_t *p_priv = vlc_internals( p_this ); + + vlc_refcheck( p_this ); + vlc_mutex_lock( &p_priv->var_lock ); + + /* FIXME: if the variable already exists, we don't duplicate it. But we + * duplicate the lookups. It's not that serious, but if anyone finds some + * time to rework Insert() so that only one lookup has to be done, feel + * free to do so. */ + i_new = Lookup( p_priv->p_vars, p_priv->i_vars, psz_name ); + + if( i_new >= 0 ) + { + /* If the types differ, variable creation failed. */ + if( (i_type & ~(VLC_VAR_DOINHERIT|VLC_VAR_ISCOMMAND)) != p_priv->p_vars[i_new].i_type ) + { + vlc_mutex_unlock( &p_priv->var_lock ); + return VLC_EBADVAR; + } + + p_priv->p_vars[i_new].i_usage++; + if( i_type & VLC_VAR_ISCOMMAND ) + p_priv->p_vars[i_new].i_type |= VLC_VAR_ISCOMMAND; + vlc_mutex_unlock( &p_priv->var_lock ); + return VLC_SUCCESS; + } + + i_new = Insert( p_priv->p_vars, p_priv->i_vars, psz_name ); + + if( (p_priv->i_vars & 15) == 15 ) + { + p_priv->p_vars = realloc( p_priv->p_vars, + (p_priv->i_vars+17) * sizeof(variable_t) ); + } + + memmove( p_priv->p_vars + i_new + 1, + p_priv->p_vars + i_new, + (p_priv->i_vars - i_new) * sizeof(variable_t) ); + + p_priv->i_vars++; + + p_var = &p_priv->p_vars[i_new]; + memset( p_var, 0, sizeof(*p_var) ); + + p_var->i_hash = HashString( psz_name ); + p_var->psz_name = strdup( psz_name ); + p_var->psz_text = NULL; + + p_var->i_type = i_type & ~VLC_VAR_DOINHERIT; + memset( &p_var->val, 0, sizeof(vlc_value_t) ); + + p_var->pf_dup = DupDummy; + p_var->pf_free = FreeDummy; + + p_var->i_usage = 1; + + p_var->i_default = -1; + p_var->choices.i_count = 0; + p_var->choices.p_values = NULL; + p_var->choices_text.i_count = 0; + p_var->choices_text.p_values = NULL; + + p_var->b_incallback = false; + p_var->i_entries = 0; + p_var->p_entries = NULL; + + /* Always initialize the variable, even if it is a list variable; this + * will lead to errors if the variable is not initialized, but it will + * not cause crashes in the variable handling. */ + switch( i_type & VLC_VAR_TYPE ) + { + case VLC_VAR_BOOL: + p_var->pf_cmp = CmpBool; + p_var->val.b_bool = false; + break; + case VLC_VAR_INTEGER: + case VLC_VAR_HOTKEY: + p_var->pf_cmp = CmpInt; + p_var->val.i_int = 0; + break; + case VLC_VAR_STRING: + case VLC_VAR_MODULE: + case VLC_VAR_FILE: + case VLC_VAR_DIRECTORY: + case VLC_VAR_VARIABLE: + p_var->pf_cmp = CmpString; + p_var->pf_dup = DupString; + p_var->pf_free = FreeString; + p_var->val.psz_string = NULL; + break; + case VLC_VAR_FLOAT: + p_var->pf_cmp = CmpFloat; + p_var->val.f_float = 0.0; + break; + case VLC_VAR_TIME: + p_var->pf_cmp = CmpTime; + p_var->val.i_time = 0; + break; + case VLC_VAR_ADDRESS: + p_var->pf_cmp = CmpAddress; + p_var->val.p_address = NULL; + break; + case VLC_VAR_MUTEX: + p_var->pf_cmp = CmpAddress; + p_var->pf_free = FreeMutex; + p_var->val.p_address = malloc( sizeof(vlc_mutex_t) ); + vlc_mutex_init( (vlc_mutex_t*)p_var->val.p_address ); + break; + case VLC_VAR_LIST: + p_var->pf_cmp = CmpAddress; + p_var->pf_dup = DupList; + p_var->pf_free = FreeList; + p_var->val.p_list = &dummy_null_list; + break; + } + + /* Duplicate the default data we stored. */ + p_var->pf_dup( &p_var->val ); + + if( i_type & VLC_VAR_DOINHERIT ) + { + vlc_value_t val; + + if( InheritValue( p_this, psz_name, &val, p_var->i_type ) + == VLC_SUCCESS ) + { + /* Free data if needed */ + p_var->pf_free( &p_var->val ); + /* Set the variable */ + p_var->val = val; + + if( i_type & VLC_VAR_HASCHOICE ) + { + /* We must add the inherited value to our choice list */ + p_var->i_default = 0; + + INSERT_ELEM( p_var->choices.p_values, p_var->choices.i_count, + 0, val ); + INSERT_ELEM( p_var->choices_text.p_values, + p_var->choices_text.i_count, 0, val ); + p_var->pf_dup( &p_var->choices.p_values[0] ); + p_var->choices_text.p_values[0].psz_string = NULL; + } + } + } + + vlc_mutex_unlock( &p_priv->var_lock ); + + return VLC_SUCCESS; +} + +/** + * Destroy a vlc variable + * + * Look for the variable and destroy it if it is found. As in var_Create we + * do a call to memmove() but we have performance counterparts elsewhere. + * + * \param p_this The object that holds the variable + * \param psz_name The name of the variable + */ +int __var_Destroy( vlc_object_t *p_this, const char *psz_name ) +{ + int i_var, i; + variable_t *p_var; + vlc_object_internals_t *p_priv = vlc_internals( p_this ); + + vlc_refcheck( p_this ); + vlc_mutex_lock( &p_priv->var_lock ); + + i_var = GetUnused( p_this, psz_name ); + if( i_var < 0 ) + { + vlc_mutex_unlock( &p_priv->var_lock ); + return i_var; + } + + p_var = &p_priv->p_vars[i_var]; + + if( p_var->i_usage > 1 ) + { + p_var->i_usage--; + vlc_mutex_unlock( &p_priv->var_lock ); + return VLC_SUCCESS; + } + + /* Free value if needed */ + p_var->pf_free( &p_var->val ); + + /* Free choice list if needed */ + if( p_var->choices.i_count ) + { + for( i = 0 ; i < p_var->choices.i_count ; i++ ) + { + p_var->pf_free( &p_var->choices.p_values[i] ); + free( p_var->choices_text.p_values[i].psz_string ); + } + free( p_var->choices.p_values ); + free( p_var->choices_text.p_values ); + } + + /* Free callbacks if needed */ + if( p_var->p_entries ) + { + free( p_var->p_entries ); + } + + free( p_var->psz_name ); + free( p_var->psz_text ); + + memmove( p_priv->p_vars + i_var, + p_priv->p_vars + i_var + 1, + (p_priv->i_vars - i_var - 1) * sizeof(variable_t) ); + + if( (p_priv->i_vars & 15) == 0 ) + { + p_priv->p_vars = realloc( p_priv->p_vars, + (p_priv->i_vars) * sizeof( variable_t ) ); + } + + p_priv->i_vars--; + + vlc_mutex_unlock( &p_priv->var_lock ); + + return VLC_SUCCESS; +} + +/** + * Perform an action on a variable + * + * \param p_this The object that holds the variable + * \param psz_name The name of the variable + * \param i_action The action to perform. Must be one of \ref var_action + * \param p_val First action parameter + * \param p_val2 Second action parameter + */ +int __var_Change( vlc_object_t *p_this, const char *psz_name, + int i_action, vlc_value_t *p_val, vlc_value_t *p_val2 ) +{ + int i_var, i; + variable_t *p_var; + vlc_value_t oldval; + vlc_object_internals_t *p_priv = vlc_internals( p_this ); + + vlc_refcheck( p_this ); + vlc_mutex_lock( &p_priv->var_lock ); + + i_var = Lookup( p_priv->p_vars, p_priv->i_vars, psz_name ); + + if( i_var < 0 ) + { + vlc_mutex_unlock( &p_priv->var_lock ); + return VLC_ENOVAR; + } + + p_var = &p_priv->p_vars[i_var]; + + switch( i_action ) + { + case VLC_VAR_SETMIN: + if( p_var->i_type & VLC_VAR_HASMIN ) + { + p_var->pf_free( &p_var->min ); + } + p_var->i_type |= VLC_VAR_HASMIN; + p_var->min = *p_val; + p_var->pf_dup( &p_var->min ); + CheckValue( p_var, &p_var->val ); + break; + case VLC_VAR_GETMIN: + if( p_var->i_type & VLC_VAR_HASMIN ) + { + *p_val = p_var->min; + } + break; + case VLC_VAR_SETMAX: + if( p_var->i_type & VLC_VAR_HASMAX ) + { + p_var->pf_free( &p_var->max ); + } + p_var->i_type |= VLC_VAR_HASMAX; + p_var->max = *p_val; + p_var->pf_dup( &p_var->max ); + CheckValue( p_var, &p_var->val ); + break; + case VLC_VAR_GETMAX: + if( p_var->i_type & VLC_VAR_HASMAX ) + { + *p_val = p_var->max; + } + break; + case VLC_VAR_SETSTEP: + if( p_var->i_type & VLC_VAR_HASSTEP ) + { + p_var->pf_free( &p_var->step ); + } + p_var->i_type |= VLC_VAR_HASSTEP; + p_var->step = *p_val; + p_var->pf_dup( &p_var->step ); + CheckValue( p_var, &p_var->val ); + break; + case VLC_VAR_GETSTEP: + if( p_var->i_type & VLC_VAR_HASSTEP ) + { + *p_val = p_var->step; + } + break; + case VLC_VAR_ADDCHOICE: + i = p_var->choices.i_count; + + INSERT_ELEM( p_var->choices.p_values, p_var->choices.i_count, + i, *p_val ); + INSERT_ELEM( p_var->choices_text.p_values, + p_var->choices_text.i_count, i, *p_val ); + p_var->pf_dup( &p_var->choices.p_values[i] ); + p_var->choices_text.p_values[i].psz_string = + ( p_val2 && p_val2->psz_string ) ? + strdup( p_val2->psz_string ) : NULL; + + CheckValue( p_var, &p_var->val ); + break; + case VLC_VAR_DELCHOICE: + for( i = 0 ; i < p_var->choices.i_count ; i++ ) + { + if( p_var->pf_cmp( p_var->choices.p_values[i], *p_val ) == 0 ) + { + break; + } + } + + if( i == p_var->choices.i_count ) + { + /* Not found */ + vlc_mutex_unlock( &p_priv->var_lock ); + return VLC_EGENERIC; + } + + if( p_var->i_default > i ) + { + p_var->i_default--; + } + else if( p_var->i_default == i ) + { + p_var->i_default = -1; + } + + p_var->pf_free( &p_var->choices.p_values[i] ); + free( p_var->choices_text.p_values[i].psz_string ); + REMOVE_ELEM( p_var->choices.p_values, p_var->choices.i_count, i ); + REMOVE_ELEM( p_var->choices_text.p_values, + p_var->choices_text.i_count, i ); + + CheckValue( p_var, &p_var->val ); + break; + case VLC_VAR_CHOICESCOUNT: + p_val->i_int = p_var->choices.i_count; + break; + case VLC_VAR_CLEARCHOICES: + for( i = 0 ; i < p_var->choices.i_count ; i++ ) + { + p_var->pf_free( &p_var->choices.p_values[i] ); + } + for( i = 0 ; i < p_var->choices_text.i_count ; i++ ) + free( p_var->choices_text.p_values[i].psz_string ); + + if( p_var->choices.i_count ) free( p_var->choices.p_values ); + if( p_var->choices_text.i_count ) free( p_var->choices_text.p_values ); + + p_var->choices.i_count = 0; + p_var->choices.p_values = NULL; + p_var->choices_text.i_count = 0; + p_var->choices_text.p_values = NULL; + p_var->i_default = -1; + break; + case VLC_VAR_SETDEFAULT: + /* FIXME: the list is sorted, dude. Use something cleverer. */ + for( i = 0 ; i < p_var->choices.i_count ; i++ ) + { + if( p_var->pf_cmp( p_var->choices.p_values[i], *p_val ) == 0 ) + { + break; + } + } + + if( i == p_var->choices.i_count ) + { + /* Not found */ + break; + } + + p_var->i_default = i; + CheckValue( p_var, &p_var->val ); + break; + case VLC_VAR_SETVALUE: + /* Duplicate data if needed */ + p_var->pf_dup( p_val ); + /* Backup needed stuff */ + oldval = p_var->val; + /* Check boundaries and list */ + CheckValue( p_var, p_val ); + /* Set the variable */ + p_var->val = *p_val; + /* Free data if needed */ + p_var->pf_free( &oldval ); + break; + case VLC_VAR_GETCHOICES: + case VLC_VAR_GETLIST: + p_val->p_list = malloc( sizeof(vlc_list_t) ); + if( p_val2 ) p_val2->p_list = malloc( sizeof(vlc_list_t) ); + if( p_var->choices.i_count ) + { + p_val->p_list->p_values = malloc( p_var->choices.i_count + * sizeof(vlc_value_t) ); + p_val->p_list->pi_types = malloc( p_var->choices.i_count + * sizeof(int) ); + if( p_val2 ) + { + p_val2->p_list->p_values = + malloc( p_var->choices.i_count * sizeof(vlc_value_t) ); + p_val2->p_list->pi_types = + malloc( p_var->choices.i_count * sizeof(int) ); + } + } + p_val->p_list->i_count = p_var->choices.i_count; + if( p_val2 ) p_val2->p_list->i_count = p_var->choices.i_count; + for( i = 0 ; i < p_var->choices.i_count ; i++ ) + { + p_val->p_list->p_values[i] = p_var->choices.p_values[i]; + p_val->p_list->pi_types[i] = p_var->i_type; + p_var->pf_dup( &p_val->p_list->p_values[i] ); + if( p_val2 ) + { + p_val2->p_list->p_values[i].psz_string = + p_var->choices_text.p_values[i].psz_string ? + strdup(p_var->choices_text.p_values[i].psz_string) : NULL; + p_val2->p_list->pi_types[i] = VLC_VAR_STRING; + } + } + break; + case VLC_VAR_FREELIST: + FreeList( p_val ); + if( p_val2 && p_val2->p_list ) + { + for( i = 0; i < p_val2->p_list->i_count; i++ ) + free( p_val2->p_list->p_values[i].psz_string ); + if( p_val2->p_list->i_count ) + { + free( p_val2->p_list->p_values ); + free( p_val2->p_list->pi_types ); + } + free( p_val2->p_list ); + } + break; + case VLC_VAR_SETTEXT: + free( p_var->psz_text ); + if( p_val && p_val->psz_string ) + p_var->psz_text = strdup( p_val->psz_string ); + break; + case VLC_VAR_GETTEXT: + p_val->psz_string = NULL; + if( p_var->psz_text ) + { + p_val->psz_string = strdup( p_var->psz_text ); + } + break; + case VLC_VAR_INHERITVALUE: + { + vlc_value_t val; + + if( InheritValue( p_this, + p_val2 ? p_val2->psz_string : psz_name, + &val, p_var->i_type ) + == VLC_SUCCESS ) + { + /* Duplicate already done */ + + /* Backup needed stuff */ + oldval = p_var->val; + /* Check boundaries and list */ + CheckValue( p_var, &val ); + /* Set the variable */ + p_var->val = val; + /* Free data if needed */ + p_var->pf_free( &oldval ); + } + + if( p_val ) + { + *p_val = p_var->val; + p_var->pf_dup( p_val ); + } + } + break; + case VLC_VAR_TRIGGER_CALLBACKS: + { + /* Deal with callbacks. Tell we're in a callback, release the lock, + * call stored functions, retake the lock. */ + if( p_var->i_entries ) + { + int i_var; + int i_entries = p_var->i_entries; + callback_entry_t *p_entries = p_var->p_entries; + + p_var->b_incallback = true; + vlc_mutex_unlock( &p_priv->var_lock ); + + /* The real calls */ + for( ; i_entries-- ; ) + { + p_entries[i_entries].pf_callback( p_this, psz_name, p_var->val, p_var->val, + p_entries[i_entries].p_data ); + } + + vlc_mutex_lock( &p_priv->var_lock ); + + i_var = Lookup( p_priv->p_vars, p_priv->i_vars, psz_name ); + if( i_var < 0 ) + { + msg_Err( p_this, "variable %s has disappeared", psz_name ); + vlc_mutex_unlock( &p_priv->var_lock ); + return VLC_ENOVAR; + } + + p_var = &p_priv->p_vars[i_var]; + p_var->b_incallback = false; + } + } + break; + + case VLC_VAR_SETISCOMMAND: + p_var->i_type |= VLC_VAR_ISCOMMAND; + break; + + default: + break; + } + + vlc_mutex_unlock( &p_priv->var_lock ); + + return VLC_SUCCESS; +} + +/** + * Request a variable's type + * + * \return The variable type if it exists, or 0 if the + * variable could not be found. + * \see \ref var_type + */ +int __var_Type( vlc_object_t *p_this, const char *psz_name ) +{ + int i_var, i_type; + vlc_object_internals_t *p_priv = vlc_internals( p_this ); + + vlc_mutex_lock( &p_priv->var_lock ); + + i_var = Lookup( p_priv->p_vars, p_priv->i_vars, psz_name ); + + if( i_var < 0 ) + { + vlc_mutex_unlock( &p_priv->var_lock ); + return 0; + } + + i_type = p_priv->p_vars[i_var].i_type; + + vlc_mutex_unlock( &p_priv->var_lock ); + + return i_type; +} + +/** + * Set a variable's value + * + * \param p_this The object that hold the variable + * \param psz_name The name of the variable + * \param val the value to set + */ +int __var_Set( vlc_object_t *p_this, const char *psz_name, vlc_value_t val ) +{ + int i_var; + variable_t *p_var; + vlc_value_t oldval; + vlc_object_internals_t *p_priv = vlc_internals( p_this ); + + vlc_refcheck( p_this ); + vlc_mutex_lock( &p_priv->var_lock ); + + i_var = GetUnused( p_this, psz_name ); + if( i_var < 0 ) + { + vlc_mutex_unlock( &p_priv->var_lock ); + return i_var; + } + + p_var = &p_priv->p_vars[i_var]; + + /* Duplicate data if needed */ + p_var->pf_dup( &val ); + + /* Backup needed stuff */ + oldval = p_var->val; + + /* Check boundaries and list */ + CheckValue( p_var, &val ); + + /* Set the variable */ + p_var->val = val; + + /* Deal with callbacks. Tell we're in a callback, release the lock, + * call stored functions, retake the lock. */ + if( p_var->i_entries ) + { + int i_var; + int i_entries = p_var->i_entries; + callback_entry_t *p_entries = p_var->p_entries; + + p_var->b_incallback = true; + vlc_mutex_unlock( &p_priv->var_lock ); + + /* The real calls */ + for( ; i_entries-- ; ) + { + p_entries[i_entries].pf_callback( p_this, psz_name, oldval, val, + p_entries[i_entries].p_data ); + } + + vlc_mutex_lock( &p_priv->var_lock ); + + i_var = Lookup( p_priv->p_vars, p_priv->i_vars, psz_name ); + if( i_var < 0 ) + { + msg_Err( p_this, "variable %s has disappeared", psz_name ); + vlc_mutex_unlock( &p_priv->var_lock ); + return VLC_ENOVAR; + } + + p_var = &p_priv->p_vars[i_var]; + p_var->b_incallback = false; + } + + /* Free data if needed */ + p_var->pf_free( &oldval ); + + vlc_mutex_unlock( &p_priv->var_lock ); + + return VLC_SUCCESS; +} + +/** + * Get a variable's value + * + * \param p_this The object that holds the variable + * \param psz_name The name of the variable + * \param p_val Pointer to a vlc_value_t that will hold the variable's value + * after the function is finished + */ +int __var_Get( vlc_object_t *p_this, const char *psz_name, vlc_value_t *p_val ) +{ + int i_var; + variable_t *p_var; + vlc_object_internals_t *p_priv = vlc_internals( p_this ); + + vlc_refcheck( p_this ); + vlc_mutex_lock( &p_priv->var_lock ); + + i_var = Lookup( p_priv->p_vars, p_priv->i_vars, psz_name ); + + if( i_var < 0 ) + { + vlc_mutex_unlock( &p_priv->var_lock ); + return VLC_ENOVAR; + } + + p_var = &p_priv->p_vars[i_var]; + + /* Really get the variable */ + *p_val = p_var->val; + + /* Duplicate value if needed */ + p_var->pf_dup( p_val ); + + vlc_mutex_unlock( &p_priv->var_lock ); + + return VLC_SUCCESS; +} + + +/** + * Finds a process-wide mutex, creates it if needed, and locks it. + * Unlock with vlc_mutex_unlock(). + */ +vlc_mutex_t *var_AcquireMutex( const char *name ) +{ + libvlc_global_data_t *p_global = vlc_global(); + vlc_value_t val; + + if( var_Create( p_global, name, VLC_VAR_MUTEX ) ) + return NULL; + + var_Get( p_global, name, &val ); + vlc_mutex_lock( val.p_address ); + return val.p_address; +} + + +/** + * Register a callback in a variable + * + * We store a function pointer that will be called upon variable + * modification. + * + * \param p_this The object that holds the variable + * \param psz_name The name of the variable + * \param pf_callback The function pointer + * \param p_data A generic pointer that will be passed as the last + * argument to the callback function. + * + * \warning The callback function is run in the thread that calls var_Set on + * the variable. Use proper locking. This thread may not have much + * time to spare, so keep callback functions short. + */ +int __var_AddCallback( vlc_object_t *p_this, const char *psz_name, + vlc_callback_t pf_callback, void *p_data ) +{ + int i_var; + variable_t *p_var; + callback_entry_t entry; + vlc_object_internals_t *p_priv = vlc_internals( p_this ); + + vlc_refcheck( p_this ); + entry.pf_callback = pf_callback; + entry.p_data = p_data; + + vlc_mutex_lock( &p_priv->var_lock ); + + i_var = GetUnused( p_this, psz_name ); + if( i_var < 0 ) + { + vlc_mutex_unlock( &p_priv->var_lock ); + return i_var; + } + + p_var = &p_priv->p_vars[i_var]; + + INSERT_ELEM( p_var->p_entries, + p_var->i_entries, + p_var->i_entries, + entry ); + + vlc_mutex_unlock( &p_priv->var_lock ); + + return VLC_SUCCESS; +} + +/** + * Remove a callback from a variable + * + * pf_callback and p_data have to be given again, because different objects + * might have registered the same callback function. + */ +int __var_DelCallback( vlc_object_t *p_this, const char *psz_name, + vlc_callback_t pf_callback, void *p_data ) +{ + int i_entry, i_var; + variable_t *p_var; + vlc_object_internals_t *p_priv = vlc_internals( p_this ); + + vlc_refcheck( p_this ); + vlc_mutex_lock( &p_priv->var_lock ); + + i_var = GetUnused( p_this, psz_name ); + if( i_var < 0 ) + { + vlc_mutex_unlock( &p_priv->var_lock ); + return i_var; + } + + p_var = &p_priv->p_vars[i_var]; + + for( i_entry = p_var->i_entries ; i_entry-- ; ) + { + if( p_var->p_entries[i_entry].pf_callback == pf_callback + && p_var->p_entries[i_entry].p_data == p_data ) + { + break; + } + } + + if( i_entry < 0 ) + { + vlc_mutex_unlock( &p_priv->var_lock ); + return VLC_EGENERIC; + } + + REMOVE_ELEM( p_var->p_entries, p_var->i_entries, i_entry ); + + vlc_mutex_unlock( &p_priv->var_lock ); + + return VLC_SUCCESS; +} + +/** + * Trigger callback on a variable + * + * \param p_this The object that hold the variable + * \param psz_name The name of the variable + */ +int __var_TriggerCallback( vlc_object_t *p_this, const char *psz_name ) +{ + int i_var; + variable_t *p_var; + vlc_value_t oldval; + vlc_object_internals_t *p_priv = vlc_internals( p_this ); + + vlc_mutex_lock( &p_priv->var_lock ); + + i_var = GetUnused( p_this, psz_name ); + if( i_var < 0 ) + { + vlc_mutex_unlock( &p_priv->var_lock ); + return i_var; + } + + p_var = &p_priv->p_vars[i_var]; + + /* Backup needed stuff */ + oldval = p_var->val; + + /* Deal with callbacks. Tell we're in a callback, release the lock, + * call stored functions, retake the lock. */ + if( p_var->i_entries ) + { + int i_var; + int i_entries = p_var->i_entries; + callback_entry_t *p_entries = p_var->p_entries; + + p_var->b_incallback = true; + vlc_mutex_unlock( &p_priv->var_lock ); + + /* The real calls */ + for( ; i_entries-- ; ) + { + p_entries[i_entries].pf_callback( p_this, psz_name, oldval, oldval, + p_entries[i_entries].p_data ); + } + + vlc_mutex_lock( &p_priv->var_lock ); + + i_var = Lookup( p_priv->p_vars, p_priv->i_vars, psz_name ); + if( i_var < 0 ) + { + msg_Err( p_this, "variable %s has disappeared", psz_name ); + vlc_mutex_unlock( &p_priv->var_lock ); + return VLC_ENOVAR; + } + + p_var = &p_priv->p_vars[i_var]; + p_var->b_incallback = false; + } + + vlc_mutex_unlock( &p_priv->var_lock ); + return VLC_SUCCESS; +} + +/** Parse a stringified option + * This function parse a string option and create the associated object + * variable + * The option must be of the form "[no[-]]foo[=bar]" where foo is the + * option name and bar is the value of the option. + * \param p_obj the object in which the variable must be created + * \param psz_option the option to parse + * \param trusted whether the option is set by a trusted input or not + * \return nothing + */ +void var_OptionParse( vlc_object_t *p_obj, const char *psz_option, + bool trusted ) +{ + char *psz_name, *psz_value; + int i_type; + bool b_isno = false; + vlc_value_t val; + + val.psz_string = NULL; + + /* It's too much of a hassle to remove the ':' when we parse + * the cmd line :) */ + if( psz_option[0] == ':' ) + psz_option++; + + if( !psz_option[0] ) + return; + + psz_name = strdup( psz_option ); + if( psz_name == NULL ) + return; + + psz_value = strchr( psz_name, '=' ); + if( psz_value != NULL ) + *psz_value++ = '\0'; + + /* FIXME: :programs should be handled generically */ + if( !strcmp( psz_name, "programs" ) ) + i_type = VLC_VAR_LIST; + else + i_type = config_GetType( p_obj, psz_name ); + + if( !i_type && !psz_value ) + { + /* check for "no-foo" or "nofoo" */ + if( !strncmp( psz_name, "no-", 3 ) ) + { + memmove( psz_name, psz_name + 3, strlen(psz_name) + 1 - 3 ); + } + else if( !strncmp( psz_name, "no", 2 ) ) + { + memmove( psz_name, psz_name + 2, strlen(psz_name) + 1 - 2 ); + } + else goto cleanup; /* Option doesn't exist */ + + b_isno = true; + i_type = config_GetType( p_obj, psz_name ); + } + if( !i_type ) goto cleanup; /* Option doesn't exist */ + + if( ( i_type != VLC_VAR_BOOL ) && + ( !psz_value || !*psz_value ) ) goto cleanup; /* Invalid value */ + + /* check if option is unsafe */ + if( !trusted ) + { + module_config_t *p_config = config_FindConfig( p_obj, psz_name ); + if( !p_config->b_safe ) + { + msg_Err( p_obj, "unsafe option \"%s\" has been ignored for " + "security reasons", psz_name ); + return; + } + } + + /* Create the variable in the input object. + * Children of the input object will be able to retreive this value + * thanks to the inheritance property of the object variables. */ + var_Create( p_obj, psz_name, i_type ); + + switch( i_type ) + { + case VLC_VAR_BOOL: + val.b_bool = !b_isno; + break; + + case VLC_VAR_INTEGER: + val.i_int = strtol( psz_value, NULL, 0 ); + break; + + case VLC_VAR_FLOAT: + val.f_float = atof( psz_value ); + break; + + case VLC_VAR_STRING: + case VLC_VAR_MODULE: + case VLC_VAR_FILE: + case VLC_VAR_DIRECTORY: + val.psz_string = psz_value; + break; + + case VLC_VAR_LIST: + { + char *psz_orig, *psz_var; + vlc_list_t *p_list = malloc(sizeof(vlc_list_t)); + val.p_list = p_list; + p_list->i_count = 0; + + psz_var = psz_orig = strdup(psz_value); + while( psz_var && *psz_var ) + { + char *psz_item = psz_var; + vlc_value_t val2; + while( *psz_var && *psz_var != ',' ) psz_var++; + if( *psz_var == ',' ) + { + *psz_var = '\0'; + psz_var++; + } + val2.i_int = strtol( psz_item, NULL, 0 ); + INSERT_ELEM( p_list->p_values, p_list->i_count, + p_list->i_count, val2 ); + /* p_list->i_count is incremented twice by INSERT_ELEM */ + p_list->i_count--; + INSERT_ELEM( p_list->pi_types, p_list->i_count, + p_list->i_count, VLC_VAR_INTEGER ); + } + free( psz_orig ); + break; + } + + default: + goto cleanup; + } + + var_Set( p_obj, psz_name, val ); + +cleanup: + free( psz_name ); +} + + +/* Following functions are local */ + +/***************************************************************************** + * GetUnused: find an unused variable from its name + ***************************************************************************** + * We do i_tries tries before giving up, just in case the variable is being + * modified and called from a callback. + *****************************************************************************/ +static int GetUnused( vlc_object_t *p_this, const char *psz_name ) +{ + int i_var, i_tries = 0; + vlc_object_internals_t *p_priv = vlc_internals( p_this ); + + while( true ) + { + i_var = Lookup( p_priv->p_vars, p_priv->i_vars, psz_name ); + if( i_var < 0 ) + { + return VLC_ENOVAR; + } + + if( ! p_priv->p_vars[i_var].b_incallback ) + { + return i_var; + } + + if( i_tries++ > 100 ) + { + msg_Err( p_this, "caught in a callback deadlock? ('%s')", psz_name ); + return VLC_ETIMEOUT; + } + + vlc_mutex_unlock( &p_priv->var_lock ); + msleep( THREAD_SLEEP ); + vlc_mutex_lock( &p_priv->var_lock ); + } +} + +/***************************************************************************** + * HashString: our cool hash function + ***************************************************************************** + * This function is not intended to be crypto-secure, we only want it to be + * fast and not suck too much. This one is pretty fast and did 0 collisions + * in wenglish's dictionary. + *****************************************************************************/ +static uint32_t HashString( const char *psz_string ) +{ + uint32_t i_hash = 0; + + while( *psz_string ) + { + i_hash += *psz_string++; + i_hash += i_hash << 10; + i_hash ^= i_hash >> 8; + } + + return i_hash; +} + +/***************************************************************************** + * Insert: find an empty slot to insert a new variable + ***************************************************************************** + * We use a recursive inner function indexed on the hash. This function does + * nothing in the rare cases where a collision may occur, see Lookup() + * to see how we handle them. + * XXX: does this really need to be written recursively? + *****************************************************************************/ +static int Insert( variable_t *p_vars, int i_count, const char *psz_name ) +{ + if( i_count == 0 ) + { + return 0; + } + + return InsertInner( p_vars, i_count, HashString( psz_name ) ); +} + +static int InsertInner( variable_t *p_vars, int i_count, uint32_t i_hash ) +{ + int i_middle; + + if( i_hash <= p_vars[0].i_hash ) + { + return 0; + } + + if( i_hash >= p_vars[i_count - 1].i_hash ) + { + return i_count; + } + + i_middle = i_count / 2; + + /* We know that 0 < i_middle */ + if( i_hash < p_vars[i_middle].i_hash ) + { + return InsertInner( p_vars, i_middle, i_hash ); + } + + /* We know that i_middle + 1 < i_count */ + if( i_hash > p_vars[i_middle + 1].i_hash ) + { + return i_middle + 1 + InsertInner( p_vars + i_middle + 1, + i_count - i_middle - 1, + i_hash ); + } + + return i_middle + 1; +} + +/***************************************************************************** + * Lookup: find an existing variable given its name + ***************************************************************************** + * We use a recursive inner function indexed on the hash. Care is taken of + * possible hash collisions. + * XXX: does this really need to be written recursively? + *****************************************************************************/ +static int Lookup( variable_t *p_vars, int i_count, const char *psz_name ) +{ + uint32_t i_hash; + int i, i_pos; + + if( i_count == 0 ) + { + return -1; + } + + i_hash = HashString( psz_name ); + + i_pos = LookupInner( p_vars, i_count, i_hash ); + + /* Hash not found */ + if( i_hash != p_vars[i_pos].i_hash ) + { + return -1; + } + + /* Hash found, entry found */ + if( !strcmp( psz_name, p_vars[i_pos].psz_name ) ) + { + return i_pos; + } + + /* Hash collision! This should be very rare, but we cannot guarantee + * it will never happen. Just do an exhaustive search amongst all + * entries with the same hash. */ + for( i = i_pos - 1 ; i > 0 && i_hash == p_vars[i].i_hash ; i-- ) + { + if( !strcmp( psz_name, p_vars[i].psz_name ) ) + { + return i; + } + } + + for( i = i_pos + 1 ; i < i_count && i_hash == p_vars[i].i_hash ; i++ ) + { + if( !strcmp( psz_name, p_vars[i].psz_name ) ) + { + return i; + } + } + + /* Hash found, but entry not found */ + return -1; +} + +static int LookupInner( variable_t *p_vars, int i_count, uint32_t i_hash ) +{ + int i_middle; + + if( i_hash <= p_vars[0].i_hash ) + { + return 0; + } + + if( i_hash >= p_vars[i_count-1].i_hash ) + { + return i_count - 1; + } + + i_middle = i_count / 2; + + /* We know that 0 < i_middle */ + if( i_hash < p_vars[i_middle].i_hash ) + { + return LookupInner( p_vars, i_middle, i_hash ); + } + + /* We know that i_middle + 1 < i_count */ + if( i_hash > p_vars[i_middle].i_hash ) + { + return i_middle + LookupInner( p_vars + i_middle, + i_count - i_middle, + i_hash ); + } + + return i_middle; +} + +/***************************************************************************** + * CheckValue: check that a value is valid wrt. a variable + ***************************************************************************** + * This function checks p_val's value against p_var's limitations such as + * minimal and maximal value, step, in-list position, and modifies p_val if + * necessary. + ****************************************************************************/ +static void CheckValue ( variable_t *p_var, vlc_value_t *p_val ) +{ + /* Check that our variable is in the list */ + if( p_var->i_type & VLC_VAR_HASCHOICE && p_var->choices.i_count ) + { + int i; + + /* FIXME: the list is sorted, dude. Use something cleverer. */ + for( i = p_var->choices.i_count ; i-- ; ) + { + if( p_var->pf_cmp( *p_val, p_var->choices.p_values[i] ) == 0 ) + { + break; + } + } + + /* If not found, change it to anything vaguely valid */ + if( i < 0 ) + { + /* Free the old variable, get the new one, dup it */ + p_var->pf_free( p_val ); + *p_val = p_var->choices.p_values[p_var->i_default >= 0 + ? p_var->i_default : 0 ]; + p_var->pf_dup( p_val ); + } + } + + /* Check that our variable is within the bounds */ + switch( p_var->i_type & VLC_VAR_TYPE ) + { + case VLC_VAR_INTEGER: + if( p_var->i_type & VLC_VAR_HASSTEP && p_var->step.i_int + && (p_val->i_int % p_var->step.i_int) ) + { + p_val->i_int = (p_val->i_int + (p_var->step.i_int / 2)) + / p_var->step.i_int * p_var->step.i_int; + } + if( p_var->i_type & VLC_VAR_HASMIN + && p_val->i_int < p_var->min.i_int ) + { + p_val->i_int = p_var->min.i_int; + } + if( p_var->i_type & VLC_VAR_HASMAX + && p_val->i_int > p_var->max.i_int ) + { + p_val->i_int = p_var->max.i_int; + } + break; + case VLC_VAR_FLOAT: + if( p_var->i_type & VLC_VAR_HASSTEP && p_var->step.f_float ) + { + float f_round = p_var->step.f_float * (float)(int)( 0.5 + + p_val->f_float / p_var->step.f_float ); + if( p_val->f_float != f_round ) + { + p_val->f_float = f_round; + } + } + if( p_var->i_type & VLC_VAR_HASMIN + && p_val->f_float < p_var->min.f_float ) + { + p_val->f_float = p_var->min.f_float; + } + if( p_var->i_type & VLC_VAR_HASMAX + && p_val->f_float > p_var->max.f_float ) + { + p_val->f_float = p_var->max.f_float; + } + break; + case VLC_VAR_TIME: + /* FIXME: TODO */ + break; + } +} + +/***************************************************************************** + * InheritValue: try to inherit the value of this variable from the same one + * in our closest parent. + *****************************************************************************/ +static int InheritValue( vlc_object_t *p_this, const char *psz_name, + vlc_value_t *p_val, int i_type ) +{ + int i_var; + variable_t *p_var; + + /* No need to take the structure lock, + * we are only looking for our parents */ + + if( !p_this->p_parent ) + { + switch( i_type & VLC_VAR_TYPE ) + { + case VLC_VAR_FILE: + case VLC_VAR_DIRECTORY: + case VLC_VAR_STRING: + case VLC_VAR_MODULE: + p_val->psz_string = config_GetPsz( p_this, psz_name ); + if( !p_val->psz_string ) p_val->psz_string = strdup(""); + break; + case VLC_VAR_FLOAT: + p_val->f_float = config_GetFloat( p_this, psz_name ); + break; + case VLC_VAR_INTEGER: + case VLC_VAR_HOTKEY: + p_val->i_int = config_GetInt( p_this, psz_name ); + break; + case VLC_VAR_BOOL: + p_val->b_bool = config_GetInt( p_this, psz_name ); + break; + case VLC_VAR_LIST: + { + char *psz_orig, *psz_var; + vlc_list_t *p_list = malloc(sizeof(vlc_list_t)); + p_val->p_list = p_list; + p_list->i_count = 0; + + psz_var = psz_orig = config_GetPsz( p_this, psz_name ); + while( psz_var && *psz_var ) + { + char *psz_item = psz_var; + vlc_value_t val; + while( *psz_var && *psz_var != ',' ) psz_var++; + if( *psz_var == ',' ) + { + *psz_var = '\0'; + psz_var++; + } + val.i_int = strtol( psz_item, NULL, 0 ); + INSERT_ELEM( p_list->p_values, p_list->i_count, + p_list->i_count, val ); + /* p_list->i_count is incremented twice by INSERT_ELEM */ + p_list->i_count--; + INSERT_ELEM( p_list->pi_types, p_list->i_count, + p_list->i_count, VLC_VAR_INTEGER ); + } + free( psz_orig ); + break; + } + default: + return VLC_ENOOBJ; + break; + } + + return VLC_SUCCESS; + } + + vlc_object_internals_t *p_priv = vlc_internals( p_this->p_parent ); + + /* Look for the variable */ + vlc_mutex_lock( &p_priv->var_lock ); + + i_var = Lookup( p_priv->p_vars, p_priv->i_vars, psz_name ); + + if( i_var >= 0 ) + { + /* We found it! */ + p_var = &p_priv->p_vars[i_var]; + + /* Really get the variable */ + *p_val = p_var->val; + + /* Duplicate value if needed */ + p_var->pf_dup( p_val ); + + vlc_mutex_unlock( &p_priv->var_lock ); + return VLC_SUCCESS; + } + + vlc_mutex_unlock( &p_priv->var_lock ); + + /* We're still not there */ + + return InheritValue( p_this->p_parent, psz_name, p_val, i_type ); +} + +/********************************************************************** + * Execute a var command on an object identified by its name + **********************************************************************/ +int __var_Command( vlc_object_t *p_this, const char *psz_name, + const char *psz_cmd, const char *psz_arg, char **psz_msg ) +{ + vlc_object_t *p_obj = vlc_object_find_name( p_this->p_libvlc, + psz_name, FIND_CHILD ); + int i_type, i_ret; + + if( !p_obj ) + { + if( psz_msg ) + *psz_msg = strdup( "Unknown destination object." ); + return VLC_ENOOBJ; + } + + vlc_refcheck( p_this ); + i_type = var_Type( p_obj, psz_cmd ); + if( !( i_type&VLC_VAR_ISCOMMAND ) ) + { + vlc_object_release( p_obj ); + if( psz_msg ) + *psz_msg = strdup( "Variable doesn't exist or isn't a command." ); + return VLC_EGENERIC; + } + + i_type &= 0xf0; + switch( i_type ) + { + case VLC_VAR_INTEGER: + i_ret = var_SetInteger( p_obj, psz_cmd, atoi( psz_arg ) ); + break; + case VLC_VAR_FLOAT: + i_ret = var_SetFloat( p_obj, psz_cmd, atof( psz_arg ) ); + break; + case VLC_VAR_STRING: + i_ret = var_SetString( p_obj, psz_cmd, psz_arg ); + break; + case VLC_VAR_BOOL: + i_ret = var_SetBool( p_obj, psz_cmd, atoi( psz_arg ) ); + break; + default: + i_ret = VLC_EGENERIC; + break; + } + + vlc_object_release( p_obj ); + + if( psz_msg ) + { + *psz_msg = (char*)malloc( 80 ); + sprintf( *psz_msg, "%s on object %s returned %i (%s)", + psz_cmd, psz_name, i_ret, vlc_error( i_ret ) ); + } + + return i_ret; +} diff --git a/VLC/variables.h b/VLC/variables.h new file mode 100644 index 0000000..67ebace --- /dev/null +++ b/VLC/variables.h @@ -0,0 +1,78 @@ +/***************************************************************************** + * variables.h: object variables typedefs + ***************************************************************************** + * Copyright (C) 2002-2006 the VideoLAN team + * $Id$ + * + * Authors: Samuel Hocevar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#if defined(__PLUGIN__) || defined(__BUILTIN__) || !defined(__LIBVLC__) + +#endif + +#ifndef __LIBVLC_VARIABLES_H +# define __LIBVLC_VARIABLES_H 1 + +typedef struct callback_entry_t callback_entry_t; + +/** + * The structure describing a variable. + * \note vlc_value_t is the common union for variable values + */ +struct variable_t +{ + /** The variable's exported value */ + vlc_value_t val; + + char * psz_name; /**< The variable unique name */ + uint32_t i_hash; /**< (almost) unique hashed value */ + int i_type; /**< The type of the variable */ + + /** The variable display name, mainly for use by the interfaces */ + char * psz_text; + + /** A pointer to a comparison function */ + int ( * pf_cmp ) ( vlc_value_t, vlc_value_t ); + /** A pointer to a duplication function */ + void ( * pf_dup ) ( vlc_value_t * ); + /** A pointer to a deallocation function */ + void ( * pf_free ) ( vlc_value_t * ); + + /** Creation count: we only destroy the variable if it reaches 0 */ + int i_usage; + + /** If the variable has min/max/step values */ + vlc_value_t min, max, step; + + /** Index of the default choice, if the variable is to be chosen in + * a list */ + int i_default; + /** List of choices */ + vlc_list_t choices; + /** List of friendly names for the choices */ + vlc_list_t choices_text; + + /** Set to TRUE if the variable is in a callback */ + bool b_incallback; + + /** Number of registered callbacks */ + int i_entries; + /** Array of registered callbacks */ + callback_entry_t * p_entries; +}; +#endif diff --git a/VLC/version.c b/VLC/version.c new file mode 100644 index 0000000..dcaaab2 --- /dev/null +++ b/VLC/version.c @@ -0,0 +1,62 @@ +/***************************************************************************** + * version.c: LibVLC version infos + ***************************************************************************** + * Copyright (C) 1998-2008 the VideoLAN team + * + * Authors: Vincent Seguin + * Samuel Hocevar + * Gildas Bazin + * Derk-Jan Hartman + * Rémi Denis-Courmont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +/***************************************************************************** + * VLC_Version: return the libvlc version. + ***************************************************************************** + * This function returns full version string (numeric version and codename). + *****************************************************************************/ +char const * VLC_Version( void ) +{ + return "0.9.1"; +} + +/***************************************************************************** + * VLC_CompileBy, VLC_CompileHost, VLC_CompileDomain, + * VLC_Compiler, VLC_Changeset + *****************************************************************************/ +#define DECLARE_VLC_VERSION( func, var ) \ +char const * VLC_##func ( void ) \ +{ \ + return VLC_##var ; \ +} + +DECLARE_VLC_VERSION( CompileBy, COMPILE_BY ); +DECLARE_VLC_VERSION( CompileHost, COMPILE_HOST ); +DECLARE_VLC_VERSION( CompileDomain, COMPILE_DOMAIN ); +DECLARE_VLC_VERSION( Compiler, COMPILER ); + +//extern const char psz_vlc_changeset[]; +const char* VLC_Changeset( void ) +{ + return ""; +} diff --git a/VLC/video.c b/VLC/video.c new file mode 100644 index 0000000..61ba1af --- /dev/null +++ b/VLC/video.c @@ -0,0 +1,534 @@ +/***************************************************************************** + * video.c: libvlc new API video functions + ***************************************************************************** + * Copyright (C) 2005 the VideoLAN team + * + * $Id$ + * + * Authors: Cl�ent Stenac + * Filippo Carone + * Jean-Paul Saman + * Damien Fouilleul + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#include "libvlc_internal.h" + +#include +#include +#include + +/* + * Remember to release the returned vout_thread_t. + */ +static vout_thread_t *GetVout( libvlc_media_player_t *p_mi, + libvlc_exception_t *p_exception ) +{ + input_thread_t *p_input_thread = libvlc_get_input_thread( p_mi, p_exception ); + vout_thread_t *p_vout = NULL; + + if( p_input_thread ) + { + p_vout = vlc_object_find( p_input_thread, VLC_OBJECT_VOUT, FIND_CHILD ); + if( !p_vout ) + { + libvlc_exception_raise( p_exception, "No active video output" ); + } + vlc_object_release( p_input_thread ); + } + return p_vout; +} + +/********************************************************************** + * Exported functions + **********************************************************************/ + +void libvlc_set_fullscreen( libvlc_media_player_t *p_mi, int b_fullscreen, + libvlc_exception_t *p_e ) +{ + /* We only work on the first vout */ + vout_thread_t *p_vout = GetVout( p_mi, p_e ); + + /* GetVout will raise the exception for us */ + if( !p_vout ) return; + + var_SetBool( p_vout, "fullscreen", b_fullscreen ); + + vlc_object_release( p_vout ); +} + +int libvlc_get_fullscreen( libvlc_media_player_t *p_mi, + libvlc_exception_t *p_e ) +{ + /* We only work on the first vout */ + vout_thread_t *p_vout = GetVout( p_mi, p_e ); + int i_ret; + + /* GetVout will raise the exception for us */ + if( !p_vout ) return 0; + + i_ret = var_GetBool( p_vout, "fullscreen" ); + + vlc_object_release( p_vout ); + + return i_ret; +} + +void libvlc_toggle_fullscreen( libvlc_media_player_t *p_mi, + libvlc_exception_t *p_e ) +{ + /* We only work on the first vout */ + vout_thread_t *p_vout = GetVout( p_mi, p_e ); + bool ret; + + /* GetVout will raise the exception for us */ + if( !p_vout ) return; + + ret = var_GetBool( p_vout, "fullscreen" ); + var_SetBool( p_vout, "fullscreen", !ret ); + + vlc_object_release( p_vout ); +} + +void +libvlc_video_take_snapshot( libvlc_media_player_t *p_mi, char *psz_filepath, + unsigned int i_width, unsigned int i_height, libvlc_exception_t *p_e ) +{ + vout_thread_t *p_vout = GetVout( p_mi, p_e ); + input_thread_t *p_input_thread; + + /* GetVout will raise the exception for us */ + if( !p_vout ) return; + + if( !psz_filepath ) + { + libvlc_exception_raise( p_e, "filepath is null" ); + return; + } + + var_SetInteger( p_vout, "snapshot-width", i_width ); + var_SetInteger( p_vout, "snapshot-height", i_height ); + + p_input_thread = p_mi->p_input_thread; + if( !p_mi->p_input_thread ) + { + libvlc_exception_raise( p_e, "Input does not exist" ); + return; + } + + var_SetString( p_vout, "snapshot-path", psz_filepath ); + var_SetString( p_vout, "snapshot-format", "png" ); + + vout_Control( p_vout, VOUT_SNAPSHOT ); + vlc_object_release( p_vout ); +} + +int libvlc_video_get_height( libvlc_media_player_t *p_mi, + libvlc_exception_t *p_e ) +{ + int height; + + vout_thread_t *p_vout = GetVout( p_mi, p_e ); + if( !p_vout ) return 0; + + height = p_vout->i_window_height; + + vlc_object_release( p_vout ); + + return height; +} + +int libvlc_video_get_width( libvlc_media_player_t *p_mi, + libvlc_exception_t *p_e ) +{ + int width; + + vout_thread_t *p_vout = GetVout( p_mi, p_e ); + if( !p_vout ) return 0; + + width = p_vout->i_window_width; + + vlc_object_release( p_vout ); + + return width; +} + +int libvlc_media_player_has_vout( libvlc_media_player_t *p_mi, + libvlc_exception_t *p_e ) +{ + input_thread_t *p_input_thread = libvlc_get_input_thread(p_mi, p_e); + bool has_vout = false; + + if( p_input_thread ) + { + vout_thread_t *p_vout; + + p_vout = vlc_object_find( p_input_thread, VLC_OBJECT_VOUT, FIND_CHILD ); + if( p_vout ) + { + has_vout = true; + vlc_object_release( p_vout ); + } + vlc_object_release( p_input_thread ); + } + return has_vout; +} + +int libvlc_video_reparent( libvlc_media_player_t *p_mi, libvlc_drawable_t d, + libvlc_exception_t *p_e ) +{ + vout_thread_t *p_vout = GetVout( p_mi, p_e ); + + if( p_vout ) + { + vout_Control( p_vout , VOUT_REPARENT, d); + vlc_object_release( p_vout ); + } + return 0; +} + +void libvlc_video_resize( libvlc_media_player_t *p_mi, int width, int height, libvlc_exception_t *p_e ) +{ + vout_thread_t *p_vout = GetVout( p_mi, p_e ); + if( p_vout ) + { + vout_Control( p_vout, VOUT_SET_SIZE, width, height ); + vlc_object_release( p_vout ); + } +} + +void libvlc_video_redraw_rectangle( libvlc_media_player_t *p_mi, + const libvlc_rectangle_t *area, + libvlc_exception_t *p_e ) +{ + if( (NULL != area) + && ((area->bottom - area->top) > 0) + && ((area->right - area->left) > 0) ) + { + vout_thread_t *p_vout = GetVout( p_mi, p_e ); + if( p_vout ) + { + /* tell running vout to redraw area */ + vout_Control( p_vout , VOUT_REDRAW_RECT, + area->top, area->left, area->bottom, area->right ); + vlc_object_release( p_vout ); + } + } +} + +/* global video settings */ + +/* Deprecated use libvlc_media_player_set_drawable() */ +void libvlc_video_set_parent( libvlc_instance_t *p_instance, libvlc_drawable_t d, + libvlc_exception_t *p_e ) +{ + /* set as default for future vout instances */ + var_SetInteger(p_instance->p_libvlc_int, "drawable", (int)d); + + libvlc_media_player_t *p_mi = libvlc_playlist_get_media_player(p_instance, p_e); + if( p_mi ) + { + libvlc_media_player_set_drawable( p_mi, d, p_e ); + libvlc_media_player_release(p_mi); + } +} + +/* Deprecated use libvlc_media_player_get_drawable() */ +libvlc_drawable_t libvlc_video_get_parent( libvlc_instance_t *p_instance, libvlc_exception_t *p_e ) +{ + VLC_UNUSED(p_e); + + libvlc_drawable_t result; + + result = var_GetInteger( p_instance->p_libvlc_int, "drawable" ); + + return result; +} + + +void libvlc_video_set_size( libvlc_instance_t *p_instance, int width, int height, + libvlc_exception_t *p_e ) +{ + /* set as default for future vout instances */ + config_PutInt(p_instance->p_libvlc_int, "width", width); + config_PutInt(p_instance->p_libvlc_int, "height", height); + + libvlc_media_player_t *p_mi = libvlc_playlist_get_media_player(p_instance, p_e); + if( p_mi ) + { + vout_thread_t *p_vout = GetVout( p_mi, p_e ); + if( p_vout ) + { + /* tell running vout to re-size */ + vout_Control( p_vout , VOUT_SET_SIZE, width, height); + vlc_object_release( p_vout ); + } + libvlc_media_player_release(p_mi); + } +} + +void libvlc_video_set_viewport( libvlc_instance_t *p_instance, + const libvlc_rectangle_t *view, const libvlc_rectangle_t *clip, + libvlc_exception_t *p_e ) +{ + if( !view ) + { + libvlc_exception_raise( p_e, "viewport is NULL" ); + return; + } + + /* if clip is NULL, then use view rectangle as clip */ + if( !clip ) + clip = view; + + /* set as default for future vout instances */ + var_SetInteger( p_instance->p_libvlc_int, "drawable-view-top", view->top ); + var_SetInteger( p_instance->p_libvlc_int, "drawable-view-left", view->left ); + var_SetInteger( p_instance->p_libvlc_int, "drawable-view-bottom", view->bottom ); + var_SetInteger( p_instance->p_libvlc_int, "drawable-view-right", view->right ); + var_SetInteger( p_instance->p_libvlc_int, "drawable-clip-top", clip->top ); + var_SetInteger( p_instance->p_libvlc_int, "drawable-clip-left", clip->left ); + var_SetInteger( p_instance->p_libvlc_int, "drawable-clip-bottom", clip->bottom ); + var_SetInteger( p_instance->p_libvlc_int, "drawable-clip-right", clip->right ); + + libvlc_media_player_t *p_mi = libvlc_playlist_get_media_player(p_instance, p_e); + if( p_mi ) + { + vout_thread_t *p_vout = GetVout( p_mi, p_e ); + if( p_vout ) + { + /* change viewport for running vout */ + vout_Control( p_vout , VOUT_SET_VIEWPORT, + view->top, view->left, view->bottom, view->right, + clip->top, clip->left, clip->bottom, clip->right ); + vlc_object_release( p_vout ); + } + libvlc_media_player_release(p_mi); + } +} + +char *libvlc_video_get_aspect_ratio( libvlc_media_player_t *p_mi, + libvlc_exception_t *p_e ) +{ + char *psz_aspect = 0; + vout_thread_t *p_vout = GetVout( p_mi, p_e ); + + if( !p_vout ) return 0; + + psz_aspect = var_GetNonEmptyString( p_vout, "aspect-ratio" ); + vlc_object_release( p_vout ); + return psz_aspect ? psz_aspect : strdup(""); +} + +void libvlc_video_set_aspect_ratio( libvlc_media_player_t *p_mi, + char *psz_aspect, libvlc_exception_t *p_e ) +{ + vout_thread_t *p_vout = GetVout( p_mi, p_e ); + int i_ret = -1; + + if( !p_vout ) return; + + i_ret = var_SetString( p_vout, "aspect-ratio", psz_aspect ); + if( i_ret ) + libvlc_exception_raise( p_e, + "Unexpected error while setting aspect-ratio value" ); + + vlc_object_release( p_vout ); +} + +int libvlc_video_get_spu( libvlc_media_player_t *p_mi, + libvlc_exception_t *p_e ) +{ + input_thread_t *p_input_thread = libvlc_get_input_thread( p_mi, p_e ); + vlc_value_t val_list; + vlc_value_t val; + int i_spu = -1; + int i_ret = -1; + int i; + + if( !p_input_thread ) return -1; + + i_ret = var_Get( p_input_thread, "spu-es", &val ); + if( i_ret < 0 ) + { + libvlc_exception_raise( p_e, "Getting subtitle information failed" ); + vlc_object_release( p_input_thread ); + return i_ret; + } + + var_Change( p_input_thread, "spu-es", VLC_VAR_GETCHOICES, &val_list, NULL ); + for( i = 0; i < val_list.p_list->i_count; i++ ) + { + vlc_value_t spu_val = val_list.p_list->p_values[i]; + if( val.i_int == spu_val.i_int ) + { + i_spu = i; + break; + } + } + vlc_object_release( p_input_thread ); + return i_spu; +} + +void libvlc_video_set_spu( libvlc_media_player_t *p_mi, int i_spu, + libvlc_exception_t *p_e ) +{ + input_thread_t *p_input_thread = libvlc_get_input_thread( p_mi, p_e ); + vlc_value_t val_list; + int i_ret = -1; + int i; + + if( !p_input_thread ) return; + + var_Change( p_input_thread, "spu-es", VLC_VAR_GETCHOICES, &val_list, NULL ); + for( i = 0; i < val_list.p_list->i_count; i++ ) + { + vlc_value_t val = val_list.p_list->p_values[i]; + if( i_spu == val.i_int ) + { + vlc_value_t new_val; + + new_val.i_int = val.i_int; + i_ret = var_Set( p_input_thread, "spu-es", new_val ); + if( i_ret < 0 ) + { + libvlc_exception_raise( p_e, "Setting subtitle value failed" ); + } + vlc_object_release( p_input_thread ); + return; + } + } + libvlc_exception_raise( p_e, "Subtitle value out of range" ); + vlc_object_release( p_input_thread ); +} + +int libvlc_video_set_subtitle_file( libvlc_media_player_t *p_mi, + char *psz_subtitle, + libvlc_exception_t *p_e ) +{ + input_thread_t *p_input_thread = libvlc_get_input_thread ( p_mi, p_e ); + bool b_ret = false; + + if( p_input_thread ) + { + if( input_AddSubtitles( p_input_thread, psz_subtitle, true ) ) + b_ret = true; + vlc_object_release( p_input_thread ); + } + return b_ret; +} + +char *libvlc_video_get_crop_geometry( libvlc_media_player_t *p_mi, + libvlc_exception_t *p_e ) +{ + char *psz_geometry = 0; + vout_thread_t *p_vout = GetVout( p_mi, p_e ); + + if( !p_vout ) return 0; + + psz_geometry = var_GetNonEmptyString( p_vout, "crop" ); + vlc_object_release( p_vout ); + return psz_geometry ? psz_geometry : strdup(""); +} + +void libvlc_video_set_crop_geometry( libvlc_media_player_t *p_mi, + char *psz_geometry, libvlc_exception_t *p_e ) +{ + vout_thread_t *p_vout = GetVout( p_mi, p_e ); + int i_ret = -1; + + if( !p_vout ) return; + + i_ret = var_SetString( p_vout, "crop", psz_geometry ); + if( i_ret ) + libvlc_exception_raise( p_e, + "Unexpected error while setting crop geometry" ); + + vlc_object_release( p_vout ); +} + +int libvlc_video_get_teletext( libvlc_media_player_t *p_mi, + libvlc_exception_t *p_e ) +{ + vout_thread_t *p_vout = GetVout( p_mi, p_e ); + vlc_object_t *p_vbi; + int i_ret = -1; + + if( !p_vout ) return i_ret; + + p_vbi = (vlc_object_t *) vlc_object_find_name( p_vout, "zvbi", + FIND_ANYWHERE ); + if( p_vbi ) + { + i_ret = var_GetInteger( p_vbi, "vbi-page" ); + vlc_object_release( p_vbi ); + } + + vlc_object_release( p_vout ); + return i_ret; +} + +void libvlc_video_set_teletext( libvlc_media_player_t *p_mi, int i_page, + libvlc_exception_t *p_e ) +{ + vout_thread_t *p_vout = GetVout( p_mi, p_e ); + vlc_object_t *p_vbi; + int i_ret = -1; + + if( !p_vout ) return; + + p_vbi = (vlc_object_t *) vlc_object_find_name( p_vout, "zvbi", + FIND_ANYWHERE ); + if( p_vbi ) + { + i_ret = var_SetInteger( p_vbi, "vbi-page", i_page ); + vlc_object_release( p_vbi ); + } + if( i_ret ) + libvlc_exception_raise( p_e, + "Unexpected error while setting teletext page" ); + vlc_object_release( p_vout ); +} + +void libvlc_toggle_teletext( libvlc_media_player_t *p_mi, + libvlc_exception_t *p_e ) +{ + /* We only work on the first vout */ + vout_thread_t *p_vout = GetVout( p_mi, p_e ); + bool opaque; int i_ret; + + /* GetVout will raise the exception for us */ + if( !p_vout ) return; + + opaque = var_GetBool( p_vout, "vbi-opaque" ); + i_ret = var_SetBool( p_vout, "vbi-opaque", !opaque ); + if( i_ret ) + libvlc_exception_raise( p_e, + "Unexpected error while setting teletext value" ); + + vlc_object_release( p_vout ); +} + +int libvlc_video_destroy( libvlc_media_player_t *p_mi, + libvlc_exception_t *p_e ) +{ + vout_thread_t *p_vout = GetVout( p_mi, p_e ); + vlc_object_detach( p_vout ); + vlc_object_release( p_vout ); + vlc_object_release( p_vout ); + + return 0; +} diff --git a/VLC/video_output.c b/VLC/video_output.c new file mode 100644 index 0000000..d30654d --- /dev/null +++ b/VLC/video_output.c @@ -0,0 +1,1578 @@ +/***************************************************************************** + * video_output.c : video output thread + * + * This module describes the programming interface for video output threads. + * It includes functions allowing to open a new thread, send pictures to a + * thread, and destroy a previously oppened video output thread. + ***************************************************************************** + * Copyright (C) 2000-2007 the VideoLAN team + * $Id$ + * + * Authors: Vincent Seguin + * Gildas Bazin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#include /* free() */ +#include + + +#ifdef HAVE_SYS_TIMES_H +# include +#endif + +#include "vlc_vout.h" + +#include "vlc_filter.h" +#include "vlc_osd.h" + +#if defined( __APPLE__ ) +/* Include darwin_specific.h here if needed */ +#endif + +/** FIXME This is quite ugly but needed while we don't have counters + * helpers */ +#include "input_internal.h" + +#include "modules.h" +#include + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +static int InitThread ( vout_thread_t * ); +static void* RunThread ( vlc_object_t * ); +static void ErrorThread ( vout_thread_t * ); +static void CleanThread ( vout_thread_t * ); +static void EndThread ( vout_thread_t * ); + +static void AspectRatio ( int, int *, int * ); + +static void VideoFormatImportRgb( video_format_t *, const picture_heap_t * ); +static void PictureHeapFixRgb( picture_heap_t * ); + +static void vout_Destructor ( vlc_object_t * p_this ); + +/* Object variables callbacks */ +static int DeinterlaceCallback( vlc_object_t *, char const *, + vlc_value_t, vlc_value_t, void * ); +static int FilterCallback( vlc_object_t *, char const *, + vlc_value_t, vlc_value_t, void * ); +static int VideoFilter2Callback( vlc_object_t *, char const *, + vlc_value_t, vlc_value_t, void * ); + +/* From vout_intf.c */ +int vout_Snapshot( vout_thread_t *, picture_t * ); + +/* Display media title in OSD */ +static void DisplayTitleOnOSD( vout_thread_t *p_vout ); + +/***************************************************************************** + * Video Filter2 functions + *****************************************************************************/ +static picture_t *video_new_buffer_filter( filter_t *p_filter ) +{ + picture_t *p_picture; + vout_thread_t *p_vout = (vout_thread_t*)p_filter->p_owner; + + p_picture = vout_CreatePicture( p_vout, 0, 0, 0 ); + + return p_picture; +} + +static void video_del_buffer_filter( filter_t *p_filter, picture_t *p_pic ) +{ + vout_DestroyPicture( (vout_thread_t*)p_filter->p_owner, p_pic ); +} + +static int video_filter_buffer_allocation_init( filter_t *p_filter, void *p_data ) +{ + p_filter->pf_vout_buffer_new = video_new_buffer_filter; + p_filter->pf_vout_buffer_del = video_del_buffer_filter; + p_filter->p_owner = p_data; /* p_vout */ + return VLC_SUCCESS; +} + +/***************************************************************************** + * vout_Request: find a video output thread, create one, or destroy one. + ***************************************************************************** + * This function looks for a video output thread matching the current + * properties. If not found, it spawns a new one. + *****************************************************************************/ +vout_thread_t *__vout_Request( vlc_object_t *p_this, vout_thread_t *p_vout, + video_format_t *p_fmt ) +{ + if( !p_fmt ) + { + /* Video output is no longer used. + * TODO: support for reusing video outputs with proper _thread-safe_ + * reference handling. */ + if( p_vout ) + vout_CloseAndRelease( p_vout ); + return NULL; + } + + /* If a video output was provided, lock it, otherwise look for one. */ + if( p_vout ) + { + vlc_object_yield( p_vout ); + } + + /* TODO: find a suitable unused video output */ + + /* If we now have a video output, check it has the right properties */ + if( p_vout ) + { + char *psz_filter_chain; + vlc_value_t val; + + /* We don't directly check for the "vout-filter" variable for obvious + * performance reasons. */ + if( p_vout->b_filter_change ) + { + var_Get( p_vout, "vout-filter", &val ); + psz_filter_chain = val.psz_string; + + if( psz_filter_chain && !*psz_filter_chain ) + { + free( psz_filter_chain ); + psz_filter_chain = NULL; + } + if( p_vout->psz_filter_chain && !*p_vout->psz_filter_chain ) + { + free( p_vout->psz_filter_chain ); + p_vout->psz_filter_chain = NULL; + } + + if( !psz_filter_chain && !p_vout->psz_filter_chain ) + { + p_vout->b_filter_change = false; + } + + free( psz_filter_chain ); + } + + if( ( p_vout->fmt_render.i_width != p_fmt->i_width ) || + ( p_vout->fmt_render.i_height != p_fmt->i_height ) || + ( p_vout->fmt_render.i_aspect != p_fmt->i_aspect ) || + p_vout->b_filter_change ) + { + /* We are not interested in this format, close this vout */ + vout_CloseAndRelease( p_vout ); + vlc_object_release( p_vout ); + p_vout = NULL; + } + else + { + /* This video output is cool! Hijack it. */ + spu_Attach( p_vout->p_spu, p_this, true ); + vlc_object_attach( p_vout, p_this ); + if( p_vout->b_title_show ) + DisplayTitleOnOSD( p_vout ); + vlc_object_release( p_vout ); + } + } + + if( !p_vout ) + { + msg_Dbg( p_this, "no usable vout present, spawning one" ); + + p_vout = vout_Create( p_this, p_fmt ); + } + + return p_vout; +} + +/***************************************************************************** + * vout_Create: creates a new video output thread + ***************************************************************************** + * This function creates a new video output thread, and returns a pointer + * to its description. On error, it returns NULL. + *****************************************************************************/ +vout_thread_t * __vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt ) +{ + vout_thread_t * p_vout; /* thread descriptor */ + input_thread_t * p_input_thread; + int i_index; /* loop variable */ + vlc_value_t val, text; + + unsigned int i_width = p_fmt->i_width; + unsigned int i_height = p_fmt->i_height; + vlc_fourcc_t i_chroma = p_fmt->i_chroma; + unsigned int i_aspect = p_fmt->i_aspect; + + config_chain_t *p_cfg; + char *psz_parser; + char *psz_name; + + /* Allocate descriptor */ + static const char typename[] = "video output"; + p_vout = vlc_custom_create( p_parent, sizeof( *p_vout ), VLC_OBJECT_VOUT, + typename ); + if( p_vout == NULL ) + return NULL; + + /* Initialize pictures - translation tables and functions + * will be initialized later in InitThread */ + for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES + 1; i_index++) + { + p_vout->p_picture[i_index].pf_lock = NULL; + p_vout->p_picture[i_index].pf_unlock = NULL; + p_vout->p_picture[i_index].i_status = FREE_PICTURE; + p_vout->p_picture[i_index].i_type = EMPTY_PICTURE; + p_vout->p_picture[i_index].b_slow = 0; + } + + /* No images in the heap */ + p_vout->i_heap_size = 0; + + /* Initialize the rendering heap */ + I_RENDERPICTURES = 0; + + vlc_ureduce( &p_fmt->i_sar_num, &p_fmt->i_sar_den, + p_fmt->i_sar_num, p_fmt->i_sar_den, 50000 ); + p_vout->fmt_render = *p_fmt; /* FIXME palette */ + p_vout->fmt_in = *p_fmt; /* FIXME palette */ + + p_vout->render.i_width = i_width; + p_vout->render.i_height = i_height; + p_vout->render.i_chroma = i_chroma; + p_vout->render.i_aspect = i_aspect; + + p_vout->render.i_rmask = p_fmt->i_rmask; + p_vout->render.i_gmask = p_fmt->i_gmask; + p_vout->render.i_bmask = p_fmt->i_bmask; + + p_vout->render.i_last_used_pic = -1; + p_vout->render.b_allow_modify_pics = 1; + + /* Zero the output heap */ + I_OUTPUTPICTURES = 0; + p_vout->output.i_width = 0; + p_vout->output.i_height = 0; + p_vout->output.i_chroma = 0; + p_vout->output.i_aspect = 0; + + p_vout->output.i_rmask = 0; + p_vout->output.i_gmask = 0; + p_vout->output.i_bmask = 0; + + /* Initialize misc stuff */ + p_vout->i_changes = 0; + p_vout->f_gamma = 0; + p_vout->b_grayscale = 0; + p_vout->b_info = 0; + p_vout->b_interface = 0; + p_vout->b_scale = 1; + p_vout->b_fullscreen = 0; + p_vout->i_alignment = 0; + p_vout->render_time = 10; + p_vout->c_fps_samples = 0; + p_vout->b_filter_change = 0; + p_vout->pf_control = NULL; + p_vout->p_window = NULL; + p_vout->i_par_num = p_vout->i_par_den = 1; + + /* Initialize locks */ + vlc_mutex_init( &p_vout->picture_lock ); + vlc_mutex_init( &p_vout->change_lock ); + vlc_mutex_init( &p_vout->vfilter_lock ); + + /* Mouse coordinates */ + var_Create( p_vout, "mouse-x", VLC_VAR_INTEGER ); + var_Create( p_vout, "mouse-y", VLC_VAR_INTEGER ); + var_Create( p_vout, "mouse-button-down", VLC_VAR_INTEGER ); + var_Create( p_vout, "mouse-moved", VLC_VAR_BOOL ); + var_Create( p_vout, "mouse-clicked", VLC_VAR_INTEGER ); + + /* Initialize subpicture unit */ + p_vout->p_spu = spu_Create( p_vout ); + spu_Attach( p_vout->p_spu, p_parent, true ); + + /* Attach the new object now so we can use var inheritance below */ + vlc_object_attach( p_vout, p_parent ); + + spu_Init( p_vout->p_spu ); + + /* Take care of some "interface/control" related initialisations */ + vout_IntfInit( p_vout ); + + /* If the parent is not a VOUT object, that means we are at the start of + * the video output pipe */ + if( p_parent->i_object_type != VLC_OBJECT_VOUT ) + { + /* Look for the default filter configuration */ + p_vout->psz_filter_chain = + var_CreateGetStringCommand( p_vout, "vout-filter" ); + + /* Apply video filter2 objects on the first vout */ + p_vout->psz_vf2 = + var_CreateGetStringCommand( p_vout, "video-filter" ); + } + else + { + /* continue the parent's filter chain */ + char *psz_tmp; + + /* Ugly hack to jump to our configuration chain */ + p_vout->psz_filter_chain + = ((vout_thread_t *)p_parent)->psz_filter_chain; + p_vout->psz_filter_chain + = config_ChainCreate( &psz_tmp, &p_cfg, p_vout->psz_filter_chain ); + config_ChainDestroy( p_cfg ); + free( psz_tmp ); + + /* Create a video filter2 var ... but don't inherit values */ + var_Create( p_vout, "video-filter", + VLC_VAR_STRING | VLC_VAR_ISCOMMAND ); + p_vout->psz_vf2 = var_GetString( p_vout, "video-filter" ); + } + + var_AddCallback( p_vout, "video-filter", VideoFilter2Callback, NULL ); + p_vout->p_vf2_chain = filter_chain_New( p_vout, "video filter2", + false, video_filter_buffer_allocation_init, NULL, p_vout ); + + /* Choose the video output module */ + if( !p_vout->psz_filter_chain || !*p_vout->psz_filter_chain ) + { + var_Create( p_vout, "vout", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); + var_Get( p_vout, "vout", &val ); + psz_parser = val.psz_string; + } + else + { + psz_parser = strdup( p_vout->psz_filter_chain ); + } + + /* Create the vout thread */ + char* psz_tmp = config_ChainCreate( &psz_name, &p_cfg, psz_parser ); + free( psz_parser ); + free( psz_tmp ); + p_vout->p_cfg = p_cfg; + p_vout->p_module = module_Need( p_vout, + ( p_vout->psz_filter_chain && *p_vout->psz_filter_chain ) ? + "video filter" : "video output", psz_name, p_vout->psz_filter_chain && *p_vout->psz_filter_chain ); + free( psz_name ); + + if( p_vout->p_module == NULL ) + { + msg_Err( p_vout, "no suitable vout module" ); + // FIXME it's ugly but that's exactly the function that need to be called. + EndThread( p_vout ); + vlc_object_detach( p_vout ); + vlc_object_release( p_vout ); + return NULL; + } + + /* Create a few object variables for interface interaction */ + var_Create( p_vout, "deinterlace", VLC_VAR_STRING | VLC_VAR_HASCHOICE ); + text.psz_string = _("Deinterlace"); + var_Change( p_vout, "deinterlace", VLC_VAR_SETTEXT, &text, NULL ); + val.psz_string = (char *)""; text.psz_string = _("Disable"); + var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text ); + val.psz_string = (char *)"discard"; text.psz_string = _("Discard"); + var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text ); + val.psz_string = (char *)"blend"; text.psz_string = _("Blend"); + var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text ); + val.psz_string = (char *)"mean"; text.psz_string = _("Mean"); + var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text ); + val.psz_string = (char *)"bob"; text.psz_string = _("Bob"); + var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text ); + val.psz_string = (char *)"linear"; text.psz_string = _("Linear"); + var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text ); + val.psz_string = (char *)"x"; text.psz_string = (char *)"X"; + var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text ); + + if( var_Get( p_vout, "deinterlace-mode", &val ) == VLC_SUCCESS ) + { + var_Set( p_vout, "deinterlace", val ); + free( val.psz_string ); + } + var_AddCallback( p_vout, "deinterlace", DeinterlaceCallback, NULL ); + + var_Create( p_vout, "vout-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); + text.psz_string = _("Filters"); + var_Change( p_vout, "vout-filter", VLC_VAR_SETTEXT, &text, NULL ); + var_AddCallback( p_vout, "vout-filter", FilterCallback, NULL ); + + /* Calculate delay created by internal caching */ + p_input_thread = (input_thread_t *)vlc_object_find( p_vout, + VLC_OBJECT_INPUT, FIND_ANYWHERE ); + if( p_input_thread ) + { + p_vout->i_pts_delay = p_input_thread->i_pts_delay; + vlc_object_release( p_input_thread ); + } + else + { + p_vout->i_pts_delay = DEFAULT_PTS_DELAY; + } + + if( vlc_thread_create( p_vout, "video output", RunThread, + VLC_THREAD_PRIORITY_OUTPUT, true ) ) + { + module_Unneed( p_vout, p_vout->p_module ); + vlc_object_release( p_vout ); + return NULL; + } + + vlc_object_set_destructor( p_vout, vout_Destructor ); + + if( p_vout->b_error ) + { + msg_Err( p_vout, "video output creation failed" ); + vout_CloseAndRelease( p_vout ); + return NULL; + } + + return p_vout; +} + +/***************************************************************************** + * vout_Close: Close a vout created by vout_Create. + ***************************************************************************** + * You HAVE to call it on vout created by vout_Create before vlc_object_release. + * You should NEVER call it on vout not obtained though vout_Create + * (like with vout_Request or vlc_object_find.) + * You can use vout_CloseAndRelease() as a convenient method. + *****************************************************************************/ +void vout_Close( vout_thread_t *p_vout ) +{ + assert( p_vout ); + + vlc_object_kill( p_vout ); + vlc_thread_join( p_vout ); + module_Unneed( p_vout, p_vout->p_module ); + p_vout->p_module = NULL; +} + +/* */ +static void vout_Destructor( vlc_object_t * p_this ) +{ + vout_thread_t *p_vout = (vout_thread_t *)p_this; + + /* Make sure the vout was stopped first */ + assert( !p_vout->p_module ); + + /* Destroy the locks */ + vlc_mutex_destroy( &p_vout->picture_lock ); + vlc_mutex_destroy( &p_vout->change_lock ); + vlc_mutex_destroy( &p_vout->vfilter_lock ); + + free( p_vout->psz_filter_chain ); + + config_ChainDestroy( p_vout->p_cfg ); + +#ifndef __APPLE__ + vout_thread_t *p_another_vout; + + /* This is a dirty hack mostly for Linux, where there is no way to get the + * GUI back if you closed it while playing video. This is solved in + * Mac OS X, where we have this novelty called menubar, that will always + * allow you access to the applications main functionality. They should try + * that on linux sometime. */ + p_another_vout = vlc_object_find( p_this->p_libvlc, + VLC_OBJECT_VOUT, FIND_ANYWHERE ); + if( p_another_vout == NULL ) + var_SetBool( p_this->p_libvlc, "intf-show", true ); + else + vlc_object_release( p_another_vout ); +#endif +} + +/***************************************************************************** + * InitThread: initialize video output thread + ***************************************************************************** + * This function is called from RunThread and performs the second step of the + * initialization. It returns 0 on success. Note that the thread's flag are not + * modified inside this function. + * XXX You have to enter it with change_lock taken. + *****************************************************************************/ +static int ChromaCreate( vout_thread_t *p_vout ); +static void ChromaDestroy( vout_thread_t *p_vout ); +static void DropPicture( vout_thread_t *p_vout, picture_t *p_picture ); + +static int InitThread( vout_thread_t *p_vout ) +{ + int i, i_aspect_x, i_aspect_y; + +#ifdef STATS + p_vout->c_loops = 0; +#endif + + /* Initialize output method, it allocates direct buffers for us */ + if( p_vout->pf_init( p_vout ) ) + return VLC_EGENERIC; + + if( !I_OUTPUTPICTURES ) + { + msg_Err( p_vout, "plugin was unable to allocate at least " + "one direct buffer" ); + p_vout->pf_end( p_vout ); + return VLC_EGENERIC; + } + + if( I_OUTPUTPICTURES > VOUT_MAX_PICTURES ) + { + msg_Err( p_vout, "plugin allocated too many direct buffers, " + "our internal buffers must have overflown." ); + p_vout->pf_end( p_vout ); + return VLC_EGENERIC; + } + + msg_Dbg( p_vout, "got %i direct buffer(s)", I_OUTPUTPICTURES ); + + AspectRatio( p_vout->fmt_render.i_aspect, &i_aspect_x, &i_aspect_y ); + + msg_Dbg( p_vout, "picture in %ix%i (%i,%i,%ix%i), " + "chroma %4.4s, ar %i:%i, sar %i:%i", + p_vout->fmt_render.i_width, p_vout->fmt_render.i_height, + p_vout->fmt_render.i_x_offset, p_vout->fmt_render.i_y_offset, + p_vout->fmt_render.i_visible_width, + p_vout->fmt_render.i_visible_height, + (char*)&p_vout->fmt_render.i_chroma, + i_aspect_x, i_aspect_y, + p_vout->fmt_render.i_sar_num, p_vout->fmt_render.i_sar_den ); + + AspectRatio( p_vout->fmt_in.i_aspect, &i_aspect_x, &i_aspect_y ); + + msg_Dbg( p_vout, "picture user %ix%i (%i,%i,%ix%i), " + "chroma %4.4s, ar %i:%i, sar %i:%i", + p_vout->fmt_in.i_width, p_vout->fmt_in.i_height, + p_vout->fmt_in.i_x_offset, p_vout->fmt_in.i_y_offset, + p_vout->fmt_in.i_visible_width, + p_vout->fmt_in.i_visible_height, + (char*)&p_vout->fmt_in.i_chroma, + i_aspect_x, i_aspect_y, + p_vout->fmt_in.i_sar_num, p_vout->fmt_in.i_sar_den ); + + if( !p_vout->fmt_out.i_width || !p_vout->fmt_out.i_height ) + { + p_vout->fmt_out.i_width = p_vout->fmt_out.i_visible_width = + p_vout->output.i_width; + p_vout->fmt_out.i_height = p_vout->fmt_out.i_visible_height = + p_vout->output.i_height; + p_vout->fmt_out.i_x_offset = p_vout->fmt_out.i_y_offset = 0; + + p_vout->fmt_out.i_aspect = p_vout->output.i_aspect; + p_vout->fmt_out.i_chroma = p_vout->output.i_chroma; + } + if( !p_vout->fmt_out.i_sar_num || !p_vout->fmt_out.i_sar_num ) + { + p_vout->fmt_out.i_sar_num = p_vout->fmt_out.i_aspect * + p_vout->fmt_out.i_height; + p_vout->fmt_out.i_sar_den = VOUT_ASPECT_FACTOR * + p_vout->fmt_out.i_width; + } + + vlc_ureduce( &p_vout->fmt_out.i_sar_num, &p_vout->fmt_out.i_sar_den, + p_vout->fmt_out.i_sar_num, p_vout->fmt_out.i_sar_den, 0 ); + + AspectRatio( p_vout->fmt_out.i_aspect, &i_aspect_x, &i_aspect_y ); + + msg_Dbg( p_vout, "picture out %ix%i (%i,%i,%ix%i), " + "chroma %4.4s, ar %i:%i, sar %i:%i", + p_vout->fmt_out.i_width, p_vout->fmt_out.i_height, + p_vout->fmt_out.i_x_offset, p_vout->fmt_out.i_y_offset, + p_vout->fmt_out.i_visible_width, + p_vout->fmt_out.i_visible_height, + (char*)&p_vout->fmt_out.i_chroma, + i_aspect_x, i_aspect_y, + p_vout->fmt_out.i_sar_num, p_vout->fmt_out.i_sar_den ); + + /* FIXME removed the need of both fmt_* and heap infos */ + /* Calculate shifts from system-updated masks */ + PictureHeapFixRgb( &p_vout->render ); + VideoFormatImportRgb( &p_vout->fmt_render, &p_vout->render ); + + PictureHeapFixRgb( &p_vout->output ); + VideoFormatImportRgb( &p_vout->fmt_out, &p_vout->output ); + + /* Check whether we managed to create direct buffers similar to + * the render buffers, ie same size and chroma */ + if( ( p_vout->output.i_width == p_vout->render.i_width ) + && ( p_vout->output.i_height == p_vout->render.i_height ) + && ( vout_ChromaCmp( p_vout->output.i_chroma, p_vout->render.i_chroma ) ) ) + { + /* Cool ! We have direct buffers, we can ask the decoder to + * directly decode into them ! Map the first render buffers to + * the first direct buffers, but keep the first direct buffer + * for memcpy operations */ + p_vout->b_direct = 1; + + for( i = 1; i < VOUT_MAX_PICTURES; i++ ) + { + if( p_vout->p_picture[ i ].i_type != DIRECT_PICTURE && + I_RENDERPICTURES >= VOUT_MIN_DIRECT_PICTURES - 1 && + p_vout->p_picture[ i - 1 ].i_type == DIRECT_PICTURE ) + { + /* We have enough direct buffers so there's no need to + * try to use system memory buffers. */ + break; + } + PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ]; + I_RENDERPICTURES++; + } + + msg_Dbg( p_vout, "direct render, mapping " + "render pictures 0-%i to system pictures 1-%i", + VOUT_MAX_PICTURES - 2, VOUT_MAX_PICTURES - 1 ); + } + else + { + /* Rats... Something is wrong here, we could not find an output + * plugin able to directly render what we decode. See if we can + * find a chroma plugin to do the conversion */ + p_vout->b_direct = 0; + + if( ChromaCreate( p_vout ) ) + { + p_vout->pf_end( p_vout ); + return VLC_EGENERIC; + } + + msg_Dbg( p_vout, "indirect render, mapping " + "render pictures 0-%i to system pictures %i-%i", + VOUT_MAX_PICTURES - 1, I_OUTPUTPICTURES, + I_OUTPUTPICTURES + VOUT_MAX_PICTURES - 1 ); + + /* Append render buffers after the direct buffers */ + for( i = I_OUTPUTPICTURES; i < 2 * VOUT_MAX_PICTURES; i++ ) + { + PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ]; + I_RENDERPICTURES++; + + /* Check if we have enough render pictures */ + if( I_RENDERPICTURES == VOUT_MAX_PICTURES ) + break; + } + } + + /* Link pictures back to their heap */ + for( i = 0 ; i < I_RENDERPICTURES ; i++ ) + { + PP_RENDERPICTURE[ i ]->p_heap = &p_vout->render; + } + + for( i = 0 ; i < I_OUTPUTPICTURES ; i++ ) + { + PP_OUTPUTPICTURE[ i ]->p_heap = &p_vout->output; + } + + return VLC_SUCCESS; +} + +/***************************************************************************** + * RunThread: video output thread + ***************************************************************************** + * Video output thread. This function does only returns when the thread is + * terminated. It handles the pictures arriving in the video heap and the + * display device events. + *****************************************************************************/ +static void* RunThread( vlc_object_t *p_this ) +{ + vout_thread_t *p_vout = (vout_thread_t *)p_this; + int i_index; /* index in heap */ + int i_idle_loops = 0; /* loops without displaying a picture */ + mtime_t current_date; /* current date */ + mtime_t display_date; /* display date */ + + picture_t * p_picture; /* picture pointer */ + picture_t * p_last_picture = NULL; /* last picture */ + picture_t * p_directbuffer; /* direct buffer to display */ + + subpicture_t * p_subpic = NULL; /* subpicture pointer */ + + input_thread_t *p_input = NULL ; /* Parent input, if it exists */ + + vlc_value_t val; + bool b_drop_late; + + int i_displayed = 0, i_lost = 0, i_loops = 0; + + /* + * Initialize thread + */ + vlc_mutex_lock( &p_vout->change_lock ); + p_vout->b_error = InitThread( p_vout ); + + var_Create( p_vout, "drop-late-frames", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); + var_Get( p_vout, "drop-late-frames", &val ); + b_drop_late = val.b_bool; + + /* signal the creation of the vout */ + vlc_thread_ready( p_vout ); + + if( p_vout->b_error ) + { + EndThread( p_vout ); + vlc_mutex_unlock( &p_vout->change_lock ); + return NULL; + } + + vlc_object_lock( p_vout ); + + if( p_vout->b_title_show ) + DisplayTitleOnOSD( p_vout ); + + /* + * Main loop - it is not executed if an error occurred during + * initialization + */ + while( vlc_object_alive( p_vout ) && !p_vout->b_error ) + { + /* Initialize loop variables */ + p_picture = NULL; + display_date = 0; + current_date = mdate(); + + i_loops++; + if( !p_input ) + { + p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT, + FIND_PARENT ); + } + if( p_input ) + { + vlc_mutex_lock( &p_input->p->counters.counters_lock ); + stats_UpdateInteger( p_vout, p_input->p->counters.p_lost_pictures, + i_lost , NULL); + stats_UpdateInteger( p_vout, + p_input->p->counters.p_displayed_pictures, + i_displayed , NULL); + i_displayed = i_lost = 0; + vlc_mutex_unlock( &p_input->p->counters.counters_lock ); + vlc_object_release( p_input ); + p_input = NULL; + } +#if 0 + p_vout->c_loops++; + if( !(p_vout->c_loops % VOUT_STATS_NB_LOOPS) ) + { + msg_Dbg( p_vout, "picture heap: %d/%d", + I_RENDERPICTURES, p_vout->i_heap_size ); + } +#endif + + /* + * Find the picture to display (the one with the earliest date). + * This operation does not need lock, since only READY_PICTUREs + * are handled. */ + for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ ) + { + if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE) + && ( (p_picture == NULL) || + (PP_RENDERPICTURE[i_index]->date < display_date) ) ) + { + p_picture = PP_RENDERPICTURE[i_index]; + display_date = p_picture->date; + } + } + + if( p_picture ) + { + /* If we met the last picture, parse again to see whether there is + * a more appropriate one. */ + if( p_picture == p_last_picture ) + { + for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ ) + { + if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE) + && (PP_RENDERPICTURE[i_index] != p_last_picture) + && ((p_picture == p_last_picture) || + (PP_RENDERPICTURE[i_index]->date < display_date)) ) + { + p_picture = PP_RENDERPICTURE[i_index]; + display_date = p_picture->date; + } + } + } + + /* If we found better than the last picture, destroy it */ + if( p_last_picture && p_picture != p_last_picture ) + { + DropPicture( p_vout, p_last_picture ); + p_last_picture = NULL; + } + + /* Compute FPS rate */ + p_vout->p_fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ] + = display_date; + + if( !p_picture->b_force && + p_picture != p_last_picture && + display_date < current_date + p_vout->render_time && + b_drop_late ) + { + /* Picture is late: it will be destroyed and the thread + * will directly choose the next picture */ + DropPicture( p_vout, p_picture ); + i_lost++; + msg_Warn( p_vout, "late picture skipped (%"PRId64")", + current_date - display_date ); + continue; + } + + if( display_date > + current_date + p_vout->i_pts_delay + VOUT_BOGUS_DELAY ) + { + /* Picture is waaay too early: it will be destroyed */ + DropPicture( p_vout, p_picture ); + i_lost++; + msg_Warn( p_vout, "vout warning: early picture skipped " + "(%"PRId64")", display_date - current_date + - p_vout->i_pts_delay ); + continue; + } + + if( display_date > current_date + VOUT_DISPLAY_DELAY ) + { + /* A picture is ready to be rendered, but its rendering date + * is far from the current one so the thread will perform an + * empty loop as if no picture were found. The picture state + * is unchanged */ + p_picture = NULL; + display_date = 0; + } + else if( p_picture == p_last_picture ) + { + /* We are asked to repeat the previous picture, but we first + * wait for a couple of idle loops */ + if( i_idle_loops < 4 ) + { + p_picture = NULL; + display_date = 0; + } + else + { + /* We set the display date to something high, otherwise + * we'll have lots of problems with late pictures */ + display_date = current_date + p_vout->render_time; + } + } + } + + if( p_picture == NULL ) + { + i_idle_loops++; + } + + if( p_picture ) + { + p_picture = filter_chain_VideoFilter( p_vout->p_vf2_chain, + p_picture ); + } + + if( p_picture && p_vout->b_snapshot ) + { + p_vout->b_snapshot = false; + vout_Snapshot( p_vout, p_picture ); + } + + /* + * Check for subpictures to display + */ + if( display_date > 0 ) + { + if( !p_input ) + { + p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT, + FIND_PARENT ); + } + p_subpic = spu_SortSubpictures( p_vout->p_spu, display_date, + p_input ? var_GetBool( p_input, "state" ) == PAUSE_S : false ); + if( p_input ) + vlc_object_release( p_input ); + p_input = NULL; + } + + /* + * Perform rendering + */ + i_displayed++; + p_directbuffer = vout_RenderPicture( p_vout, p_picture, p_subpic ); + + /* + * Call the plugin-specific rendering method if there is one + */ + if( p_picture != NULL && p_directbuffer != NULL && p_vout->pf_render ) + { + /* Render the direct buffer returned by vout_RenderPicture */ + p_vout->pf_render( p_vout, p_directbuffer ); + } + + /* + * Sleep, wake up + */ + if( display_date != 0 && p_directbuffer != NULL ) + { + mtime_t current_render_time = mdate() - current_date; + /* if render time is very large we don't include it in the mean */ + if( current_render_time < p_vout->render_time + + VOUT_DISPLAY_DELAY ) + { + /* Store render time using a sliding mean weighting to + * current value in a 3 to 1 ratio*/ + p_vout->render_time *= 3; + p_vout->render_time += current_render_time; + p_vout->render_time >>= 2; + } + } + + /* Give back change lock */ + vlc_mutex_unlock( &p_vout->change_lock ); + + vlc_object_unlock( p_vout ); + + /* Sleep a while or until a given date */ + if( display_date != 0 ) + { + /* If there are filters in the chain, better give them the picture + * in advance */ + if( !p_vout->psz_filter_chain || !*p_vout->psz_filter_chain ) + { + mwait( display_date - VOUT_MWAIT_TOLERANCE ); + } + } + else + { + msleep( VOUT_IDLE_SLEEP ); + } + + /* On awakening, take back lock and send immediately picture + * to display. */ + vlc_object_lock( p_vout ); + /* Note: vlc_object_alive() could be false here, and we + * could be dead */ + vlc_mutex_lock( &p_vout->change_lock ); + + /* + * Display the previously rendered picture + */ + if( p_picture != NULL && p_directbuffer != NULL ) + { + /* Display the direct buffer returned by vout_RenderPicture */ + if( p_vout->pf_display ) + { + p_vout->pf_display( p_vout, p_directbuffer ); + } + + /* Tell the vout this was the last picture and that it does not + * need to be forced anymore. */ + p_last_picture = p_picture; + p_last_picture->b_force = 0; + } + + if( p_picture != NULL ) + { + /* Reinitialize idle loop count */ + i_idle_loops = 0; + } + + /* + * Check events and manage thread + */ + if( p_vout->pf_manage && p_vout->pf_manage( p_vout ) ) + { + /* A fatal error occurred, and the thread must terminate + * immediately, without displaying anything - setting b_error to 1 + * causes the immediate end of the main while() loop. */ + // FIXME pf_end + p_vout->b_error = 1; + break; + } + + if( p_vout->i_changes & VOUT_SIZE_CHANGE ) + { + /* this must only happen when the vout plugin is incapable of + * rescaling the picture itself. In this case we need to destroy + * the current picture buffers and recreate new ones with the right + * dimensions */ + int i; + + p_vout->i_changes &= ~VOUT_SIZE_CHANGE; + + assert( !p_vout->b_direct ); + + ChromaDestroy( p_vout ); + + vlc_mutex_lock( &p_vout->picture_lock ); + + p_vout->pf_end( p_vout ); + + for( i = 0; i < I_OUTPUTPICTURES; i++ ) + p_vout->p_picture[ i ].i_status = FREE_PICTURE; + + I_OUTPUTPICTURES = 0; + + if( p_vout->pf_init( p_vout ) ) + { + msg_Err( p_vout, "cannot resize display" ); + /* FIXME: pf_end will be called again in EndThread() */ + p_vout->b_error = 1; + } + + vlc_mutex_unlock( &p_vout->picture_lock ); + + /* Need to reinitialise the chroma plugin. Since we might need + * resizing too and it's not sure that we already had it, + * recreate the chroma plugin chain from scratch. */ + /* dionoea */ + if( ChromaCreate( p_vout ) ) + { + msg_Err( p_vout, "WOW THIS SUCKS BIG TIME!!!!!" ); + p_vout->b_error = 1; + } + if( p_vout->b_error ) + break; + } + + if( p_vout->i_changes & VOUT_PICTURE_BUFFERS_CHANGE ) + { + /* This happens when the picture buffers need to be recreated. + * This is useful on multimonitor displays for instance. + * + * Warning: This only works when the vout creates only 1 picture + * buffer!! */ + p_vout->i_changes &= ~VOUT_PICTURE_BUFFERS_CHANGE; + + if( !p_vout->b_direct ) + ChromaDestroy( p_vout ); + + vlc_mutex_lock( &p_vout->picture_lock ); + + p_vout->pf_end( p_vout ); + + I_OUTPUTPICTURES = I_RENDERPICTURES = 0; + + p_vout->b_error = InitThread( p_vout ); + if( p_vout->b_error ) + msg_Err( p_vout, "InitThread after VOUT_PICTURE_BUFFERS_CHANGE failed\n" ); + + vlc_mutex_unlock( &p_vout->picture_lock ); + + if( p_vout->b_error ) + break; + } + + /* Check for "video filter2" changes */ + vlc_mutex_lock( &p_vout->vfilter_lock ); + if( p_vout->psz_vf2 ) + { + es_format_t fmt; + + es_format_Init( &fmt, VIDEO_ES, p_vout->fmt_render.i_chroma ); + fmt.video = p_vout->fmt_render; + filter_chain_Reset( p_vout->p_vf2_chain, &fmt, &fmt ); + + if( filter_chain_AppendFromString( p_vout->p_vf2_chain, + p_vout->psz_vf2 ) < 0 ) + msg_Err( p_vout, "Video filter chain creation failed" ); + + free( p_vout->psz_vf2 ); + p_vout->psz_vf2 = NULL; + } + vlc_mutex_unlock( &p_vout->vfilter_lock ); + } + + + if( p_input ) + { + vlc_object_release( p_input ); + } + + /* + * Error loop - wait until the thread destruction is requested + */ + if( p_vout->b_error ) + ErrorThread( p_vout ); + + /* End of thread */ + CleanThread( p_vout ); + EndThread( p_vout ); + vlc_mutex_unlock( &p_vout->change_lock ); + + vlc_object_unlock( p_vout ); + return NULL; +} + +/***************************************************************************** + * ErrorThread: RunThread() error loop + ***************************************************************************** + * This function is called when an error occurred during thread main's loop. + * The thread can still receive feed, but must be ready to terminate as soon + * as possible. + *****************************************************************************/ +static void ErrorThread( vout_thread_t *p_vout ) +{ + /* Wait until a `die' order */ + while( vlc_object_alive( p_vout ) ) + vlc_object_wait( p_vout ); +} + +/***************************************************************************** + * CleanThread: clean up after InitThread + ***************************************************************************** + * This function is called after a sucessful + * initialization. It frees all resources allocated by InitThread. + * XXX You have to enter it with change_lock taken. + *****************************************************************************/ +static void CleanThread( vout_thread_t *p_vout ) +{ + int i_index; /* index in heap */ + + if( !p_vout->b_direct ) + ChromaDestroy( p_vout ); + + /* Destroy all remaining pictures */ + for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES + 1; i_index++ ) + { + if ( p_vout->p_picture[i_index].i_type == MEMORY_PICTURE ) + { + free( p_vout->p_picture[i_index].p_data_orig ); + } + } + + /* Destroy translation tables */ + if( !p_vout->b_error ) + p_vout->pf_end( p_vout ); +} + +/***************************************************************************** + * EndThread: thread destruction + ***************************************************************************** + * This function is called when the thread ends. + * It frees all resources not allocated by InitThread. + * XXX You have to enter it with change_lock taken. + *****************************************************************************/ +static void EndThread( vout_thread_t *p_vout ) +{ +#ifdef STATS + { + struct tms cpu_usage; + times( &cpu_usage ); + + msg_Dbg( p_vout, "cpu usage (user: %d, system: %d)", + cpu_usage.tms_utime, cpu_usage.tms_stime ); + } +#endif + + /* FIXME does that function *really* need to be called inside the thread ? */ + + /* Destroy subpicture unit */ + spu_Attach( p_vout->p_spu, VLC_OBJECT(p_vout), false ); + spu_Destroy( p_vout->p_spu ); + + /* Destroy the video filters2 */ + filter_chain_Delete( p_vout->p_vf2_chain ); +} + +/* Thread helpers */ +static picture_t *ChromaGetPicture( filter_t *p_filter ) +{ + picture_t *p_pic = (picture_t *)p_filter->p_owner; + p_filter->p_owner = NULL; + return p_pic; +} + +static int ChromaCreate( vout_thread_t *p_vout ) +{ + static const char typename[] = "chroma"; + filter_t *p_chroma; + + /* Choose the best module */ + p_chroma = p_vout->p_chroma = + vlc_custom_create( p_vout, sizeof(filter_t), VLC_OBJECT_GENERIC, + typename ); + + vlc_object_attach( p_chroma, p_vout ); + + /* TODO: Set the fmt_in and fmt_out stuff here */ + p_chroma->fmt_in.video = p_vout->fmt_render; + p_chroma->fmt_out.video = p_vout->fmt_out; + VideoFormatImportRgb( &p_chroma->fmt_in.video, &p_vout->render ); + VideoFormatImportRgb( &p_chroma->fmt_out.video, &p_vout->output ); + + p_chroma->p_module = module_Need( p_chroma, "video filter2", NULL, 0 ); + + if( p_chroma->p_module == NULL ) + { + msg_Err( p_vout, "no chroma module for %4.4s to %4.4s i=%dx%d o=%dx%d", + (char*)&p_vout->render.i_chroma, + (char*)&p_vout->output.i_chroma, + p_chroma->fmt_in.video.i_width, p_chroma->fmt_in.video.i_height, + p_chroma->fmt_out.video.i_width, p_chroma->fmt_out.video.i_height + ); + + vlc_object_release( p_vout->p_chroma ); + p_vout->p_chroma = NULL; + return VLC_EGENERIC; + } + p_chroma->pf_vout_buffer_new = ChromaGetPicture; + return VLC_SUCCESS; +} + +static void ChromaDestroy( vout_thread_t *p_vout ) +{ + assert( !p_vout->b_direct ); + + if( !p_vout->p_chroma ) + return; + + module_Unneed( p_vout->p_chroma, p_vout->p_chroma->p_module ); + vlc_object_release( p_vout->p_chroma ); + p_vout->p_chroma = NULL; +} + +static void DropPicture( vout_thread_t *p_vout, picture_t *p_picture ) +{ + vlc_mutex_lock( &p_vout->picture_lock ); + if( p_picture->i_refcount ) + { + /* Pretend we displayed the picture, but don't destroy + * it since the decoder might still need it. */ + p_picture->i_status = DISPLAYED_PICTURE; + } + else + { + /* Destroy the picture without displaying it */ + p_picture->i_status = DESTROYED_PICTURE; + p_vout->i_heap_size--; + } + vlc_mutex_unlock( &p_vout->picture_lock ); +} + + + +/* following functions are local */ + +static int ReduceHeight( int i_ratio ) +{ + int i_dummy = VOUT_ASPECT_FACTOR; + int i_pgcd = 1; + + if( !i_ratio ) + { + return i_pgcd; + } + + /* VOUT_ASPECT_FACTOR is (2^7 * 3^3 * 5^3), we just check for 2, 3 and 5 */ + while( !(i_ratio & 1) && !(i_dummy & 1) ) + { + i_ratio >>= 1; + i_dummy >>= 1; + i_pgcd <<= 1; + } + + while( !(i_ratio % 3) && !(i_dummy % 3) ) + { + i_ratio /= 3; + i_dummy /= 3; + i_pgcd *= 3; + } + + while( !(i_ratio % 5) && !(i_dummy % 5) ) + { + i_ratio /= 5; + i_dummy /= 5; + i_pgcd *= 5; + } + + return i_pgcd; +} + +static void AspectRatio( int i_aspect, int *i_aspect_x, int *i_aspect_y ) +{ + unsigned int i_pgcd = ReduceHeight( i_aspect ); + *i_aspect_x = i_aspect / i_pgcd; + *i_aspect_y = VOUT_ASPECT_FACTOR / i_pgcd; +} + +/** + * This function copies all RGB informations from a picture_heap_t into + * a video_format_t + */ +static void VideoFormatImportRgb( video_format_t *p_fmt, const picture_heap_t *p_heap ) +{ + p_fmt->i_rmask = p_heap->i_rmask; + p_fmt->i_gmask = p_heap->i_gmask; + p_fmt->i_bmask = p_heap->i_bmask; + p_fmt->i_rrshift = p_heap->i_rrshift; + p_fmt->i_lrshift = p_heap->i_lrshift; + p_fmt->i_rgshift = p_heap->i_rgshift; + p_fmt->i_lgshift = p_heap->i_lgshift; + p_fmt->i_rbshift = p_heap->i_rbshift; + p_fmt->i_lbshift = p_heap->i_lbshift; +} + +/** + * This funtion copes all RGB informations from a video_format_t into + * a picture_heap_t + */ +static void VideoFormatExportRgb( const video_format_t *p_fmt, picture_heap_t *p_heap ) +{ + p_heap->i_rmask = p_fmt->i_rmask; + p_heap->i_gmask = p_fmt->i_gmask; + p_heap->i_bmask = p_fmt->i_bmask; + p_heap->i_rrshift = p_fmt->i_rrshift; + p_heap->i_lrshift = p_fmt->i_lrshift; + p_heap->i_rgshift = p_fmt->i_rgshift; + p_heap->i_lgshift = p_fmt->i_lgshift; + p_heap->i_rbshift = p_fmt->i_rbshift; + p_heap->i_lbshift = p_fmt->i_lbshift; +} + +/** + * This function computes rgb shifts from masks + */ +static void PictureHeapFixRgb( picture_heap_t *p_heap ) +{ + video_format_t fmt; + + /* */ + fmt.i_chroma = p_heap->i_chroma; + VideoFormatImportRgb( &fmt, p_heap ); + + /* */ + video_format_FixRgb( &fmt ); + + VideoFormatExportRgb( &fmt, p_heap ); +} + +/***************************************************************************** + * Helper thread for object variables callbacks. + * Only used to avoid deadlocks when using the video embedded mode. + *****************************************************************************/ +typedef struct suxor_thread_t +{ + VLC_COMMON_MEMBERS + input_thread_t *p_input; + +} suxor_thread_t; + +static void* SuxorRestartVideoES( vlc_object_t * p_vlc_t ) +{ + suxor_thread_t *p_this = (suxor_thread_t *) p_vlc_t; + /* Now restart current video stream */ + int val = var_GetInteger( p_this->p_input, "video-es" ); + if( val >= 0 ) + { + var_SetInteger( p_this->p_input, "video-es", -VIDEO_ES ); + var_SetInteger( p_this->p_input, "video-es", val ); + } + + vlc_object_release( p_this->p_input ); + + vlc_object_release( p_this ); + return NULL; +} + +/***************************************************************************** + * object variables callbacks: a bunch of object variables are used by the + * interfaces to interact with the vout. + *****************************************************************************/ +static int DeinterlaceCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) +{ + vout_thread_t *p_vout = (vout_thread_t *)p_this; + input_thread_t *p_input; + vlc_value_t val; + + char *psz_mode = newval.psz_string; + char *psz_filter, *psz_deinterlace = NULL; + (void)psz_cmd; (void)oldval; (void)p_data; + + var_Get( p_vout, "vout-filter", &val ); + psz_filter = val.psz_string; + if( psz_filter ) psz_deinterlace = strstr( psz_filter, "deinterlace" ); + + if( !psz_mode || !*psz_mode ) + { + if( psz_deinterlace ) + { + char *psz_src = psz_deinterlace + sizeof("deinterlace") - 1; + if( psz_src[0] == ':' ) psz_src++; + memmove( psz_deinterlace, psz_src, strlen(psz_src) + 1 ); + } + } + else if( !psz_deinterlace ) + { + psz_filter = realloc( psz_filter, strlen( psz_filter ) + + sizeof(":deinterlace") ); + if( psz_filter && *psz_filter ) strcat( psz_filter, ":" ); + strcat( psz_filter, "deinterlace" ); + } + + p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT, + FIND_PARENT ); + if( !p_input ) return VLC_EGENERIC; + + if( psz_mode && *psz_mode ) + { + /* Modify input as well because the vout might have to be restarted */ + val.psz_string = psz_mode; + var_Create( p_input, "deinterlace-mode", VLC_VAR_STRING ); + var_Set( p_input, "deinterlace-mode", val ); + } + vlc_object_release( p_input ); + + val.b_bool = true; + var_Set( p_vout, "intf-change", val ); + + val.psz_string = psz_filter; + var_Set( p_vout, "vout-filter", val ); + free( psz_filter ); + + return VLC_SUCCESS; +} + +static int FilterCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) +{ + vout_thread_t *p_vout = (vout_thread_t *)p_this; + input_thread_t *p_input; + vlc_value_t val; + (void)psz_cmd; (void)oldval; (void)p_data; + + p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT, + FIND_PARENT ); + if (!p_input) + { + msg_Err( p_vout, "Input not found" ); + return( VLC_EGENERIC ); + } + + val.b_bool = true; + var_Set( p_vout, "intf-change", val ); + + /* Modify input as well because the vout might have to be restarted */ + val.psz_string = newval.psz_string; + var_Create( p_input, "vout-filter", VLC_VAR_STRING ); + + var_Set( p_input, "vout-filter", val ); + + /* Now restart current video stream */ + var_Get( p_input, "video-es", &val ); + if( val.i_int >= 0 ) + { + static const char typename[] = "kludge"; + suxor_thread_t *p_suxor = + vlc_custom_create( p_vout, sizeof(suxor_thread_t), + VLC_OBJECT_GENERIC, typename ); + p_suxor->p_input = p_input; + p_vout->b_filter_change = true; + vlc_object_yield( p_input ); + vlc_thread_create( p_suxor, "suxor", SuxorRestartVideoES, + VLC_THREAD_PRIORITY_LOW, false ); + } + + vlc_object_release( p_input ); + + return VLC_SUCCESS; +} + +/***************************************************************************** + * Video Filter2 stuff + *****************************************************************************/ +static int VideoFilter2Callback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) +{ + vout_thread_t *p_vout = (vout_thread_t *)p_this; + (void)psz_cmd; (void)oldval; (void)p_data; + + vlc_mutex_lock( &p_vout->vfilter_lock ); + p_vout->psz_vf2 = strdup( newval.psz_string ); + vlc_mutex_unlock( &p_vout->vfilter_lock ); + + return VLC_SUCCESS; +} + +static void DisplayTitleOnOSD( vout_thread_t *p_vout ) +{ + input_thread_t *p_input; + mtime_t i_now, i_stop; + + if( !config_GetInt( p_vout, "osd" ) ) return; + + p_input = (input_thread_t *)vlc_object_find( p_vout, + VLC_OBJECT_INPUT, FIND_ANYWHERE ); + if( p_input ) + { + i_now = mdate(); + i_stop = i_now + (mtime_t)(p_vout->i_title_timeout * 1000); + char *psz_nowplaying = + input_item_GetNowPlaying( input_GetItem( p_input ) ); + char *psz_artist = input_item_GetArtist( input_GetItem( p_input ) ); + char *psz_name = input_item_GetTitle( input_GetItem( p_input ) ); + if( EMPTY_STR( psz_name ) ) + { + free( psz_name ); + psz_name = input_item_GetName( input_GetItem( p_input ) ); + } + if( !EMPTY_STR( psz_nowplaying ) ) + { + vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN, + psz_nowplaying, NULL, + p_vout->i_title_position, + 30 + p_vout->fmt_in.i_width + - p_vout->fmt_in.i_visible_width + - p_vout->fmt_in.i_x_offset, + 20 + p_vout->fmt_in.i_y_offset, + i_now, i_stop ); + } + else if( !EMPTY_STR( psz_artist ) ) + { + char *psz_string = NULL; + if( asprintf( &psz_string, "%s - %s", psz_name, psz_artist ) != -1 ) + { + vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN, + psz_string, NULL, + p_vout->i_title_position, + 30 + p_vout->fmt_in.i_width + - p_vout->fmt_in.i_visible_width + - p_vout->fmt_in.i_x_offset, + 20 + p_vout->fmt_in.i_y_offset, + i_now, i_stop ); + free( psz_string ); + } + } + else + { + vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN, + psz_name, NULL, + p_vout->i_title_position, + 30 + p_vout->fmt_in.i_width + - p_vout->fmt_in.i_visible_width + - p_vout->fmt_in.i_x_offset, + 20 + p_vout->fmt_in.i_y_offset, + i_now, i_stop ); + } + vlc_object_release( p_input ); + free( psz_artist ); + free( psz_name ); + free( psz_nowplaying ); + } +} + diff --git a/VLC/video_text.c b/VLC/video_text.c new file mode 100644 index 0000000..a70e6bc --- /dev/null +++ b/VLC/video_text.c @@ -0,0 +1,152 @@ +/***************************************************************************** + * video_text.c : text manipulation functions + ***************************************************************************** + * Copyright (C) 1999-2007 the VideoLAN team + * $Id$ + * + * Author: Sigmund Augdal Helberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "vlc_vout.h" +#include "vlc_block.h" +#include "vlc_filter.h" +#include "vlc_osd.h" + +/** + * \brief Show text on the video for some time + * \param p_vout pointer to the vout the text is to be showed on + * \param i_channel Subpicture channel + * \param psz_string The text to be shown + * \param p_style Pointer to a struct with text style info + * \param i_flags flags for alignment and such + * \param i_hmargin horizontal margin in pixels + * \param i_vmargin vertical margin in pixels + * \param i_duration Amount of time the text is to be shown. + */ +int vout_ShowTextRelative( vout_thread_t *p_vout, int i_channel, + char *psz_string, text_style_t *p_style, + int i_flags, int i_hmargin, int i_vmargin, + mtime_t i_duration ) +{ + mtime_t i_now = mdate(); + + return vout_ShowTextAbsolute( p_vout, i_channel, psz_string, + p_style, i_flags, i_hmargin, i_vmargin, + i_now, i_now + i_duration ); +} + +/** + * \brief Show text on the video from a given start date to a given end date + * \param p_vout pointer to the vout the text is to be showed on + * \param i_channel Subpicture channel + * \param psz_string The text to be shown + * \param p_style Pointer to a struct with text style info + * \param i_flags flags for alignment and such + * \param i_hmargin horizontal margin in pixels + * \param i_vmargin vertical margin in pixels + * \param i_start the time when this string is to appear on the video + * \param i_stop the time when this string should stop to be displayed + * if this is 0 the string will be shown untill the next string + * is about to be shown + */ +int vout_ShowTextAbsolute( vout_thread_t *p_vout, int i_channel, + const char *psz_string, text_style_t *p_style, + int i_flags, int i_hmargin, int i_vmargin, + mtime_t i_start, mtime_t i_stop ) +{ + (void)p_style; + subpicture_t *p_spu; + video_format_t fmt; + /* (void)p_style; FIXME: <-- why ask for this if it's unused?!? */ + + if( !psz_string ) return VLC_EGENERIC; + + p_spu = spu_CreateSubpicture( p_vout->p_spu ); + if( !p_spu ) return VLC_EGENERIC; + + /* Create a new subpicture region */ + memset( &fmt, 0, sizeof(video_format_t) ); + fmt.i_chroma = VLC_FOURCC('T','E','X','T'); + fmt.i_aspect = 0; + fmt.i_width = fmt.i_height = 0; + fmt.i_x_offset = fmt.i_y_offset = 0; + p_spu->p_region = p_spu->pf_create_region( VLC_OBJECT(p_vout), &fmt ); + if( !p_spu->p_region ) + { + msg_Err( p_vout, "cannot allocate SPU region" ); + spu_DestroySubpicture( p_vout->p_spu, p_spu ); + return VLC_EGENERIC; + } + + p_spu->p_region->psz_text = strdup( psz_string ); + p_spu->p_region->i_align = i_flags & SUBPICTURE_ALIGN_MASK; + p_spu->i_start = i_start; + p_spu->i_stop = i_stop; + p_spu->b_ephemer = true; + p_spu->b_absolute = false; + p_spu->b_fade = true; + + p_spu->i_x = i_hmargin; + p_spu->i_y = i_vmargin; + p_spu->i_flags = i_flags & ~SUBPICTURE_ALIGN_MASK; + p_spu->i_channel = i_channel; + + spu_DisplaySubpicture( p_vout->p_spu, p_spu ); + + return VLC_SUCCESS; +} + + +/** + * \brief Write an informative message at the default location, + * for the default duration and only if the OSD option is enabled. + * \param p_caller The object that called the function. + * \param i_channel Subpicture channel + * \param psz_format printf style formatting + **/ +void __vout_OSDMessage( vlc_object_t *p_caller, int i_channel, + const char *psz_format, ... ) +{ + vout_thread_t *p_vout; + char *psz_string = NULL; + va_list args; + + if( !config_GetInt( p_caller, "osd" ) ) return; + + p_vout = vlc_object_find( p_caller, VLC_OBJECT_VOUT, FIND_ANYWHERE ); + if( p_vout ) + { + va_start( args, psz_format ); + if( vasprintf( &psz_string, psz_format, args ) != -1 ) + { + vout_ShowTextRelative( p_vout, i_channel, psz_string, NULL, + OSD_ALIGN_TOP|OSD_ALIGN_RIGHT, + 30 + p_vout->fmt_in.i_width + - p_vout->fmt_in.i_visible_width + - p_vout->fmt_in.i_x_offset, + 20 + p_vout->fmt_in.i_y_offset, 1000000 ); + free( psz_string ); + } + vlc_object_release( p_vout ); + va_end( args ); + } +} diff --git a/VLC/video_widgets.c b/VLC/video_widgets.c new file mode 100644 index 0000000..c40a25a --- /dev/null +++ b/VLC/video_widgets.c @@ -0,0 +1,82 @@ +/***************************************************************************** + * video_widgets.c : OSD widgets manipulation functions + ***************************************************************************** + * Copyright (C) 2004-2005 the VideoLAN team + * $Id$ + * + * Author: Yoann Peronneau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "vlc_vout.h" +#include "vlc_osd.h" + +#include "vlc_filter.h" + +/***************************************************************************** + * Displays an OSD slider. + * Types are: OSD_HOR_SLIDER and OSD_VERT_SLIDER. + *****************************************************************************/ +void vout_OSDSlider( vlc_object_t *p_caller, int i_channel, int i_position, + short i_type ) +{ + vout_thread_t *p_vout = vlc_object_find( p_caller, VLC_OBJECT_VOUT, + FIND_ANYWHERE ); + + if( p_vout && ( config_GetInt( p_caller, "osd" ) || ( i_position >= 0 ) ) ) + { + osd_Slider( p_caller, p_vout->p_spu, p_vout->render.i_width, + p_vout->render.i_height, p_vout->fmt_in.i_x_offset, + p_vout->fmt_in.i_height - p_vout->fmt_in.i_visible_height + - p_vout->fmt_in.i_y_offset, + i_channel, i_position, i_type ); + } + vlc_object_release( p_vout ); +} + +/***************************************************************************** + * Displays an OSD icon. + * Types are: OSD_PLAY_ICON, OSD_PAUSE_ICON, OSD_SPEAKER_ICON, OSD_MUTE_ICON + *****************************************************************************/ +void vout_OSDIcon( vlc_object_t *p_caller, int i_channel, short i_type ) +{ + vout_thread_t *p_vout = vlc_object_find( p_caller, VLC_OBJECT_VOUT, + FIND_ANYWHERE ); + + if( !p_vout ) return; + + if( config_GetInt( p_caller, "osd" ) ) + { + osd_Icon( p_caller, + p_vout->p_spu, + p_vout->render.i_width, + p_vout->render.i_height, + p_vout->fmt_in.i_width - p_vout->fmt_in.i_visible_width + - p_vout->fmt_in.i_x_offset, + p_vout->fmt_in.i_y_offset, + i_channel, i_type ); + } + vlc_object_release( p_vout ); +} + diff --git a/VLC/vlc/deprecated.h b/VLC/vlc/deprecated.h new file mode 100644 index 0000000..c683ecb --- /dev/null +++ b/VLC/vlc/deprecated.h @@ -0,0 +1,244 @@ +/***************************************************************************** + * deprecated.h: libvlc deprecated API + ***************************************************************************** + * Copyright (C) 1998-2008 the VideoLAN team + * $Id$ + * + * Authors: Clément Stenac + * Jean-Paul Saman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef LIBVLC_DEPRECATED_H +#define LIBVLC_DEPRECATED_H 1 + +/** + * \file + * This file defines libvlc depreceated API + */ + +# ifdef __cplusplus +extern "C" { +# endif + +/** + * Set the default video output's parent. + * + * This setting will be used as default for all video outputs. + * + * \param p_instance libvlc instance + * \param drawable the new parent window (Drawable on X11, CGrafPort on MacOSX, HWND on Win32) + * \param p_e an initialized exception pointer + * @deprecated Use libvlc_media_player_set_drawable + */ +VLC_PUBLIC_API void libvlc_video_set_parent( libvlc_instance_t *, libvlc_drawable_t, libvlc_exception_t * ); + +/** + * Set the default video output parent. + * + * This setting will be used as default for all video outputs. + * + * \param p_instance libvlc instance + * \param drawable the new parent window (Drawable on X11, CGrafPort on MacOSX, HWND on Win32) + * \param p_e an initialized exception pointer + * @deprecated Use libvlc_media_player_get_drawable + */ +VLC_PUBLIC_API libvlc_drawable_t libvlc_video_get_parent( libvlc_instance_t *, libvlc_exception_t * ); + +/* + * This function shall not be used at all. It may lead to crash and race condition. + */ +VLC_DEPRECATED_API int libvlc_video_destroy( libvlc_media_player_t *, libvlc_exception_t *); + +/***************************************************************************** + * Playlist (Deprecated) + *****************************************************************************/ +/** \defgroup libvlc_playlist libvlc_playlist (Deprecated) + * \ingroup libvlc + * LibVLC Playlist handling (Deprecated) + * @deprecated Use media_list + * @{ + */ + +/** + * Set the playlist's loop attribute. If set, the playlist runs continuously + * and wraps around when it reaches the end. + * + * \param p_instance the playlist instance + * \param loop the loop attribute. 1 sets looping, 0 disables it + * \param p_e an initialized exception pointer + */ +VLC_DEPRECATED_API void libvlc_playlist_loop( libvlc_instance_t* , int, + libvlc_exception_t * ); + +/** + * Start playing. + * + * Additionnal playlist item options can be specified for addition to the + * item before it is played. + * + * \param p_instance the playlist instance + * \param i_id the item to play. If this is a negative number, the next + * item will be selected. Otherwise, the item with the given ID will be + * played + * \param i_options the number of options to add to the item + * \param ppsz_options the options to add to the item + * \param p_e an initialized exception pointer + */ +VLC_DEPRECATED_API void libvlc_playlist_play( libvlc_instance_t*, int, int, + char **, libvlc_exception_t * ); + +/** + * Toggle the playlist's pause status. + * + * If the playlist was running, it is paused. If it was paused, it is resumed. + * + * \param p_instance the playlist instance to pause + * \param p_e an initialized exception pointer + */ +VLC_DEPRECATED_API void libvlc_playlist_pause( libvlc_instance_t *, + libvlc_exception_t * ); + +/** + * Checks whether the playlist is running + * + * \param p_instance the playlist instance + * \param p_e an initialized exception pointer + * \return 0 if the playlist is stopped or paused, 1 if it is running + */ +VLC_DEPRECATED_API int libvlc_playlist_isplaying( libvlc_instance_t *, + libvlc_exception_t * ); + +/** + * Get the number of items in the playlist + * + * \param p_instance the playlist instance + * \param p_e an initialized exception pointer + * \return the number of items + */ +VLC_DEPRECATED_API int libvlc_playlist_items_count( libvlc_instance_t *, + libvlc_exception_t * ); + +VLC_DEPRECATED_API int libvlc_playlist_get_current_index( libvlc_instance_t *, + libvlc_exception_t *); +/** + * Lock the playlist. + * + * \param p_instance the playlist instance + */ +VLC_DEPRECATED_API void libvlc_playlist_lock( libvlc_instance_t * ); + +/** + * Unlock the playlist. + * + * \param p_instance the playlist instance + */ +VLC_DEPRECATED_API void libvlc_playlist_unlock( libvlc_instance_t * ); + +/** + * Stop playing. + * + * \param p_instance the playlist instance to stop + * \param p_e an initialized exception pointer + */ +VLC_DEPRECATED_API void libvlc_playlist_stop( libvlc_instance_t *, + libvlc_exception_t * ); + +/** + * Go to the next playlist item. If the playlist was stopped, playback + * is started. + * + * \param p_instance the playlist instance + * \param p_e an initialized exception pointer + */ +VLC_DEPRECATED_API void libvlc_playlist_next( libvlc_instance_t *, + libvlc_exception_t * ); + +/** + * Go to the previous playlist item. If the playlist was stopped, playback + * is started. + * + * \param p_instance the playlist instance + * \param p_e an initialized exception pointer + */ +VLC_DEPRECATED_API void libvlc_playlist_prev( libvlc_instance_t *, + libvlc_exception_t * ); + +/** + * Empty a playlist. All items in the playlist are removed. + * + * \param p_instance the playlist instance + * \param p_e an initialized exception pointer + */ +VLC_DEPRECATED_API void libvlc_playlist_clear( libvlc_instance_t *, + libvlc_exception_t * ); + +/** + * Append an item to the playlist. The item is added at the end. If more + * advanced options are required, \see libvlc_playlist_add_extended instead. + * + * \param p_instance the playlist instance + * \param psz_uri the URI to open, using VLC format + * \param psz_name a name that you might want to give or NULL + * \param p_e an initialized exception pointer + * \return the identifier of the new item + */ +VLC_DEPRECATED_API int libvlc_playlist_add( libvlc_instance_t *, const char *, + const char *, libvlc_exception_t * ); + +/** + * Append an item to the playlist. The item is added at the end, with + * additional input options. + * + * \param p_instance the playlist instance + * \param psz_uri the URI to open, using VLC format + * \param psz_name a name that you might want to give or NULL + * \param i_options the number of options to add + * \param ppsz_options strings representing the options to add + * \param p_e an initialized exception pointer + * \return the identifier of the new item + */ +VLC_DEPRECATED_API int libvlc_playlist_add_extended( libvlc_instance_t *, const char *, + const char *, int, const char **, + libvlc_exception_t * ); + +/** + * Delete the playlist item with the given ID. + * + * \param p_instance the playlist instance + * \param i_id the id to remove + * \param p_e an initialized exception pointer + * \return 0 in case of success, a non-zero value otherwise + */ +VLC_DEPRECATED_API int libvlc_playlist_delete_item( libvlc_instance_t *, int, + libvlc_exception_t * ); + +/** Get the input that is currently being played by the playlist. + * + * \param p_instance the playlist instance to use + * \param p_e an initialized exception pointern + * \return a media instance object + */ +VLC_DEPRECATED_API libvlc_media_player_t * libvlc_playlist_get_media_player( + libvlc_instance_t *, libvlc_exception_t * ); + +/** @}*/ + +# ifdef __cplusplus +} +# endif + +#endif /* _LIBVLC_DEPRECATED_H */ diff --git a/VLC/vlc/libvlc.h b/VLC/vlc/libvlc.h new file mode 100644 index 0000000..7f2f384 --- /dev/null +++ b/VLC/vlc/libvlc.h @@ -0,0 +1,1278 @@ +/***************************************************************************** + * libvlc.h: libvlc external API + ***************************************************************************** + * Copyright (C) 1998-2005 the VideoLAN team + * $Id$ + * + * Authors: Clément Stenac + * Jean-Paul Saman + * Pierre d'Herbemont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/** + * \file + * This file defines libvlc external API + */ + +/** + * \defgroup libvlc libvlc + * This is libvlc, the base library of the VLC program. + * + * @{ + */ + +#ifndef VLC_LIBVLC_H +#define VLC_LIBVLC_H 1 + +#if defined (WIN32) && defined (DLL_EXPORT) +# define VLC_PUBLIC_API __declspec(dllexport) +#else +# define VLC_PUBLIC_API +#endif + +#ifdef __LIBVLC__ +/* Avoid unuseful warnings from libvlc with our deprecated APIs */ +# define VLC_DEPRECATED_API VLC_PUBLIC_API +#elif defined(__GNUC__) && \ + (__GNUC__ > 3 || __GNUC__ == 3 && __GNUC_MINOR__ > 0) +# define VLC_DEPRECATED_API VLC_PUBLIC_API __attribute__((deprecated)) +#else +# define VLC_DEPRECATED_API VLC_PUBLIC_API +#endif + +# ifdef __cplusplus +extern "C" { +# endif + +/***************************************************************************** + * Exception handling + *****************************************************************************/ +/** \defgroup libvlc_exception libvlc_exception + * \ingroup libvlc_core + * LibVLC Exceptions handling + * @{ + */ + +/** + * Initialize an exception structure. This can be called several times to + * reuse an exception structure. + * + * \param p_exception the exception to initialize + */ +VLC_PUBLIC_API void libvlc_exception_init( libvlc_exception_t *p_exception ); + +/** + * Has an exception been raised? + * + * \param p_exception the exception to query + * \return 0 if the exception was raised, 1 otherwise + */ +VLC_PUBLIC_API int +libvlc_exception_raised( const libvlc_exception_t *p_exception ); + +/** + * Raise an exception using a user-provided message. + * + * \param p_exception the exception to raise + * \param psz_format the exception message format string + * \param ... the format string arguments + */ +VLC_PUBLIC_API void +libvlc_exception_raise( libvlc_exception_t *p_exception, + const char *psz_format, ... ); + +/** + * Clear an exception object so it can be reused. + * The exception object must have be initialized. + * + * \param p_exception the exception to clear + */ +VLC_PUBLIC_API void libvlc_exception_clear( libvlc_exception_t * ); + +/** + * Get an exception's message. + * + * \param p_exception the exception to query + * \return the exception message or NULL if not applicable (exception not + * raised, for example) + */ +VLC_PUBLIC_API const char * +libvlc_exception_get_message( const libvlc_exception_t *p_exception ); + +/**@} */ + +/***************************************************************************** + * Core handling + *****************************************************************************/ + +/** \defgroup libvlc_core libvlc_core + * \ingroup libvlc + * LibVLC Core + * @{ + */ + +/** + * Create and initialize a libvlc instance. + * + * \param argc the number of arguments + * \param argv command-line-type arguments. argv[0] must be the path of the + * calling program. + * \param p_e an initialized exception pointer + * \return the libvlc instance + */ +VLC_PUBLIC_API libvlc_instance_t * +libvlc_new( int , const char *const *, libvlc_exception_t *); + +/** + * Return a libvlc instance identifier for legacy APIs. Use of this + * function is discouraged, you should convert your program to use the + * new API. + * + * \param p_instance the instance + * \return the instance identifier + */ +VLC_PUBLIC_API int libvlc_get_vlc_id( libvlc_instance_t *p_instance ); + +/** + * Decrement the reference count of a libvlc instance, and destroy it + * if it reaches zero. + * + * \param p_instance the instance to destroy + */ +VLC_PUBLIC_API void libvlc_release( libvlc_instance_t * ); + +/** + * Increments the reference count of a libvlc instance. + * The initial reference count is 1 after libvlc_new() returns. + * + * \param p_instance the instance to reference + */ +VLC_PUBLIC_API void libvlc_retain( libvlc_instance_t * ); + +/** + * Try to start a user interface for the libvlc instance, and wait until the + * user exits. + * + * \param p_instance the instance + * \param name interface name, or NULL for default + * \param p_exception an initialized exception pointer + */ +VLC_PUBLIC_API +void libvlc_add_intf( libvlc_instance_t *p_instance, const char *name, + libvlc_exception_t *p_exception ); + +/** + * Waits until an interface causes the instance to exit. + * You should start at least one interface first, using libvlc_add_intf(). + * + * \param p_instance the instance + */ +VLC_PUBLIC_API +void libvlc_wait( libvlc_instance_t *p_instance ); + +/** + * Retrieve libvlc version. + * + * Example: "0.9.0-git Grishenko" + * + * \return a string containing the libvlc version + */ +VLC_PUBLIC_API const char * libvlc_get_version(void); + +/** + * Retrieve libvlc compiler version. + * + * Example: "gcc version 4.2.3 (Ubuntu 4.2.3-2ubuntu6)" + * + * \return a string containing the libvlc compiler version + */ +VLC_PUBLIC_API const char * libvlc_get_compiler(void); + +/** + * Retrieve libvlc changeset. + * + * Example: "aa9bce0bc4" + * + * \return a string containing the libvlc changeset + */ +VLC_PUBLIC_API const char * libvlc_get_changeset(void); + +/** @}*/ + +/***************************************************************************** + * media + *****************************************************************************/ +/** \defgroup libvlc_media libvlc_media + * \ingroup libvlc + * LibVLC Media + * @{ + */ + +/** + * Create a media with the given MRL. + * + * \param p_instance the instance + * \param psz_mrl the MRL to read + * \param p_e an initialized exception pointer + * \return the newly created media + */ +VLC_PUBLIC_API libvlc_media_t * libvlc_media_new( + libvlc_instance_t *p_instance, + const char * psz_mrl, + libvlc_exception_t *p_e ); + +/** + * Create a media as an empty node with the passed name. + * + * \param p_instance the instance + * \param psz_name the name of the node + * \param p_e an initialized exception pointer + * \return the new empty media + */ +VLC_PUBLIC_API libvlc_media_t * libvlc_media_new_as_node( + libvlc_instance_t *p_instance, + const char * psz_name, + libvlc_exception_t *p_e ); + +/** + * Add an option to the media. + * + * This option will be used to determine how the media_player will + * read the media. This allows to use VLC's advanced + * reading/streaming options on a per-media basis. + * + * The options are detailed in vlc --long-help, for instance "--sout-all" + * + * \param p_instance the instance + * \param ppsz_options the options (as a string) + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_media_add_option( + libvlc_media_t * p_md, + const char * ppsz_options, + libvlc_exception_t * p_e ); + +/** + * Retain a reference to a media descriptor object (libvlc_media_t). Use + * libvlc_media_release() to decrement the reference count of a + * media descriptor object. + * + * \param p_meta_desc a media descriptor object. + */ +VLC_PUBLIC_API void libvlc_media_retain( + libvlc_media_t *p_meta_desc ); + +/** + * Decrement the reference count of a media descriptor object. If the + * reference count is 0, then libvlc_media_release() will release the + * media descriptor object. It will send out an libvlc_MediaFreed event + * to all listeners. If the media descriptor object has been released it + * should not be used again. + * + * \param p_meta_desc a media descriptor object. + */ +VLC_PUBLIC_API void libvlc_media_release( + libvlc_media_t *p_meta_desc ); + + +/** + * Get the media resource locator (mrl) from a media descriptor object + * + * \param p_md a media descriptor object + * \param p_e an initialized exception object + * \return string with mrl of media descriptor object + */ +VLC_PUBLIC_API char * libvlc_media_get_mrl( libvlc_media_t * p_md, + libvlc_exception_t * p_e ); + +/** + * Duplicate a media descriptor object. + * + * \param p_meta_desc a media descriptor object. + */ +VLC_PUBLIC_API libvlc_media_t * libvlc_media_duplicate( libvlc_media_t * ); + +/** + * Read the meta of the media. + * + * \param p_meta_desc the media to read + * \param e_meta_desc the meta to read + * \param p_e an initialized exception pointer + * \return the media's meta + */ +VLC_PUBLIC_API char * libvlc_media_get_meta( + libvlc_media_t *p_meta_desc, + libvlc_meta_t e_meta, + libvlc_exception_t *p_e ); +/** + * Get current state of media descriptor object. Possible media states + * are defined in libvlc_structures.c ( libvlc_NothingSpecial=0, + * libvlc_Opening, libvlc_Buffering, libvlc_Playing, libvlc_Paused, + * libvlc_Stopped, libvlc_Forward, libvlc_Backward, libvlc_Ended, + * libvlc_Error). + * + * @see libvlc_state_t + * \param p_meta_desc a media descriptor object + * \param p_e an initialized exception object + * \return state of media descriptor object + */ +VLC_PUBLIC_API libvlc_state_t libvlc_media_get_state( + libvlc_media_t *p_meta_desc, + libvlc_exception_t *p_e ); + +/** + * Get subitems of media descriptor object. This will increment + * the reference count of supplied media descriptor object. Use + * libvlc_media_list_release() to decrement the reference counting. + * + * \param p_md media descriptor object + * \param p_e initalized exception object + * \return list of media descriptor subitems or NULL + */ +VLC_PUBLIC_API libvlc_media_list_t * + libvlc_media_subitems( libvlc_media_t *p_md, + libvlc_exception_t *p_e ); +/** + * Get event manager from media descriptor object. + * NOTE: this function doesn't increment reference counting. + * + * \param p_md a media descriptor object + * \param p_e an initialized exception object + * \return event manager object + */ +VLC_PUBLIC_API libvlc_event_manager_t * + libvlc_media_event_manager( libvlc_media_t * p_md, + libvlc_exception_t * p_e ); + +/** + * Get duration of media descriptor object item. + * + * \param p_md media descriptor object + * \param p_e an initialized exception object + * \return duration of media item + */ +VLC_PUBLIC_API libvlc_time_t + libvlc_media_get_duration( libvlc_media_t * p_md, + libvlc_exception_t * p_e ); + +/** + * Get preparsed status for media descriptor object. + * + * \param p_md media descriptor object + * \param p_e an initialized exception object + * \return true if media object has been preparsed otherwise it returns false + */ +VLC_PUBLIC_API int + libvlc_media_is_preparsed( libvlc_media_t * p_md, + libvlc_exception_t * p_e ); + +/** + * Sets media descriptor's user_data. user_data is specialized data + * accessed by the host application, VLC.framework uses it as a pointer to + * an native object that references a libvlc_media_t pointer + * + * \param p_md media descriptor object + * \param p_new_user_data pointer to user data + * \param p_e an initialized exception object + */ +VLC_PUBLIC_API void + libvlc_media_set_user_data( libvlc_media_t * p_md, + void * p_new_user_data, + libvlc_exception_t * p_e); + +/** + * Get media descriptor's user_data. user_data is specialized data + * accessed by the host application, VLC.framework uses it as a pointer to + * an native object that references a libvlc_media_t pointer + * + * \param p_md media descriptor object + * \param p_e an initialized exception object + */ +VLC_PUBLIC_API void * + libvlc_media_get_user_data( libvlc_media_t * p_md, + libvlc_exception_t * p_e); + +/** @}*/ + +/***************************************************************************** + * Media Player + *****************************************************************************/ +/** \defgroup libvlc_media_player libvlc_media_player + * \ingroup libvlc + * LibVLC Media Player, object that let you play a media + * in a libvlc_drawable_t + * @{ + */ + +/** + * Create an empty Media Player object + * + * \param p_libvlc_instance the libvlc instance in which the Media Player + * should be created. + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API libvlc_media_player_t * libvlc_media_player_new( libvlc_instance_t *, libvlc_exception_t * ); + +/** + * Create a Media Player object from a Media + * + * \param p_md the media. Afterwards the p_md can be safely + * destroyed. + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API libvlc_media_player_t * libvlc_media_player_new_from_media( libvlc_media_t *, libvlc_exception_t * ); + +/** + * Release a media_player after use + * Decrement the reference count of a media player object. If the + * reference count is 0, then libvlc_media_player_release() will + * release the media player object. If the media player object + * has been released, then it should not be used again. + * + * \param p_mi the Media Player to free + */ +VLC_PUBLIC_API void libvlc_media_player_release( libvlc_media_player_t * ); + +/** + * Retain a reference to a media player object. Use + * libvlc_media_player_release() to decrement reference count. + * + * \param p_mi media player object + */ +VLC_PUBLIC_API void libvlc_media_player_retain( libvlc_media_player_t * ); + +/** + * Set the media that will be used by the media_player. If any, + * previous md will be released. + * + * \param p_mi the Media Player + * \param p_md the Media. Afterwards the p_md can be safely + * destroyed. + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_media_player_set_media( libvlc_media_player_t *, libvlc_media_t *, libvlc_exception_t * ); + +/** + * Get the media used by the media_player. + * + * \param p_mi the Media Player + * \param p_e an initialized exception pointer + * \return the media associated with p_mi, or NULL if no + * media is associated + */ +VLC_PUBLIC_API libvlc_media_t * libvlc_media_player_get_media( libvlc_media_player_t *, libvlc_exception_t * ); + +/** + * Get the Event Manager from which the media player send event. + * + * \param p_mi the Media Player + * \param p_e an initialized exception pointer + * \return the event manager associated with p_mi + */ +VLC_PUBLIC_API libvlc_event_manager_t * libvlc_media_player_event_manager ( libvlc_media_player_t *, libvlc_exception_t * ); + +/** + * Play + * + * \param p_mi the Media Player + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_media_player_play ( libvlc_media_player_t *, libvlc_exception_t * ); + +/** + * Pause + * + * \param p_mi the Media Player + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_media_player_pause ( libvlc_media_player_t *, libvlc_exception_t * ); + +/** + * Stop + * + * \param p_mi the Media Player + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_media_player_stop ( libvlc_media_player_t *, libvlc_exception_t * ); + +/** + * Set the drawable where the media player should render its video output + * + * \param p_mi the Media Player + * \param drawable the libvlc_drawable_t where the media player + * should render its video + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_media_player_set_drawable ( libvlc_media_player_t *, libvlc_drawable_t, libvlc_exception_t * ); + +/** + * Get the drawable where the media player should render its video output + * + * \param p_mi the Media Player + * \param p_e an initialized exception pointer + * \return the libvlc_drawable_t where the media player + * should render its video + */ +VLC_PUBLIC_API libvlc_drawable_t + libvlc_media_player_get_drawable ( libvlc_media_player_t *, libvlc_exception_t * ); + +/** \bug This might go away ... to be replaced by a broader system */ + +/** + * Get the current movie length (in ms). + * + * \param p_mi the Media Player + * \param p_e an initialized exception pointer + * \return the movie length (in ms). + */ +VLC_PUBLIC_API libvlc_time_t libvlc_media_player_get_length( libvlc_media_player_t *, libvlc_exception_t *); + +/** + * Get the current movie time (in ms). + * + * \param p_mi the Media Player + * \param p_e an initialized exception pointer + * \return the movie time (in ms). + */ +VLC_PUBLIC_API libvlc_time_t libvlc_media_player_get_time( libvlc_media_player_t *, libvlc_exception_t *); + +/** + * Set the movie time (in ms). + * + * \param p_mi the Media Player + * \param the movie time (in ms). + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_media_player_set_time( libvlc_media_player_t *, libvlc_time_t, libvlc_exception_t *); + +/** + * Get movie position. + * + * \param p_mi the Media Player + * \param p_e an initialized exception pointer + * \return movie position + */ +VLC_PUBLIC_API float libvlc_media_player_get_position( libvlc_media_player_t *, libvlc_exception_t *); + +/** + * Set movie position. + * + * \param p_mi the Media Player + * \param p_e an initialized exception pointer + * \return movie position + */ +VLC_PUBLIC_API void libvlc_media_player_set_position( libvlc_media_player_t *, float, libvlc_exception_t *); + +/** + * Set movie chapter + * + * \param p_mi the Media Player + * \param i_chapter chapter number to play + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_media_player_set_chapter( libvlc_media_player_t *, int, libvlc_exception_t *); + +/** + * Get movie chapter + * + * \param p_mi the Media Player + * \param p_e an initialized exception pointer + * \return chapter number currently playing + */ +VLC_PUBLIC_API int libvlc_media_player_get_chapter( libvlc_media_player_t *, libvlc_exception_t * ); + +/** + * Get movie chapter count + * + * \param p_mi the Media Player + * \param p_e an initialized exception pointer + * \return number of chapters in movie + */ +VLC_PUBLIC_API int libvlc_media_player_get_chapter_count( libvlc_media_player_t *, libvlc_exception_t *); +VLC_PUBLIC_API int libvlc_media_player_will_play ( libvlc_media_player_t *, libvlc_exception_t *); + +/** + * Get movie play rate + * + * \param p_mi the Media Player + * \param p_e an initialized exception pointer + * \return movie play rate + */ +VLC_PUBLIC_API float libvlc_media_player_get_rate( libvlc_media_player_t *, libvlc_exception_t *); + +/** + * Set movie play rate + * + * \param p_mi the Media Player + * \param movie play rate to set + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_media_player_set_rate( libvlc_media_player_t *, float, libvlc_exception_t *); + +/** + * Get current movie state + * + * \param p_mi the Media Player + * \param p_e an initialized exception pointer + * \return current movie state as libvlc_state_t + */ +VLC_PUBLIC_API libvlc_state_t libvlc_media_player_get_state( libvlc_media_player_t *, libvlc_exception_t *); + +/** + * Get movie fps rate + * + * \param p_mi the Media Player + * \param p_e an initialized exception pointer + * \return frames per second (fps) for this playing movie + */ +VLC_PUBLIC_API float libvlc_media_player_get_fps( libvlc_media_player_t *, libvlc_exception_t *); + +/** end bug */ + +/** + * Does this media player have a video output? + * + * \param p_md the media player + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API int libvlc_media_player_has_vout( libvlc_media_player_t *, libvlc_exception_t *); + +/** + * Is this media player seekable? + * + * \param p_input the input + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API int libvlc_media_player_is_seekable( libvlc_media_player_t *p_mi, libvlc_exception_t *p_e ); + +/** + * Can this media player be paused? + * + * \param p_input the input + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API int libvlc_media_player_can_pause( libvlc_media_player_t *p_mi, libvlc_exception_t *p_e ); + +/** \defgroup libvlc_video libvlc_video + * \ingroup libvlc_media_player + * LibVLC Video handling + * @{ + */ + +/** + * Toggle fullscreen status on video output. + * + * \param p_mediaplayer the media player + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_toggle_fullscreen( libvlc_media_player_t *, libvlc_exception_t * ); + +/** + * Enable or disable fullscreen on a video output. + * + * \param p_mediaplayer the media player + * \param b_fullscreen boolean for fullscreen status + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_set_fullscreen( libvlc_media_player_t *, int, libvlc_exception_t * ); + +/** + * Get current fullscreen status. + * + * \param p_mediaplayer the media player + * \param p_e an initialized exception pointer + * \return the fullscreen status (boolean) + */ +VLC_PUBLIC_API int libvlc_get_fullscreen( libvlc_media_player_t *, libvlc_exception_t * ); + +/** + * Get current video height. + * + * \param p_mediaplayer the media player + * \param p_e an initialized exception pointer + * \return the video height + */ +VLC_PUBLIC_API int libvlc_video_get_height( libvlc_media_player_t *, libvlc_exception_t * ); + +/** + * Get current video width. + * + * \param p_mediaplayer the media player + * \param p_e an initialized exception pointer + * \return the video width + */ +VLC_PUBLIC_API int libvlc_video_get_width( libvlc_media_player_t *, libvlc_exception_t * ); + +/** + * Get current video aspect ratio. + * + * \param p_mediaplayer the media player + * \param p_e an initialized exception pointer + * \return the video aspect ratio + */ +VLC_PUBLIC_API char *libvlc_video_get_aspect_ratio( libvlc_media_player_t *, libvlc_exception_t * ); + +/** + * Set new video aspect ratio. + * + * \param p_mediaplayer the media player + * \param psz_aspect new video aspect-ratio + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_video_set_aspect_ratio( libvlc_media_player_t *, char *, libvlc_exception_t * ); + +/** + * Get current video subtitle. + * + * \param p_mediaplayer the media player + * \param p_e an initialized exception pointer + * \return the video subtitle selected + */ +VLC_PUBLIC_API int libvlc_video_get_spu( libvlc_media_player_t *, libvlc_exception_t * ); + +/** + * Set new video subtitle. + * + * \param p_mediaplayer the media player + * \param i_spu new video subtitle to select + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_video_set_spu( libvlc_media_player_t *, int , libvlc_exception_t * ); + +/** + * Set new video subtitle file. + * + * \param p_mediaplayer the media player + * \param psz_subtitle new video subtitle file + * \param p_e an initialized exception pointer + * \return the success status (boolean) + */ +VLC_PUBLIC_API int libvlc_video_set_subtitle_file( libvlc_media_player_t *, char *, libvlc_exception_t * ); + +/** + * Get current crop filter geometry. + * + * \param p_mediaplayer the media player + * \param p_e an initialized exception pointer + * \return the crop filter geometry + */ +VLC_PUBLIC_API char *libvlc_video_get_crop_geometry( libvlc_media_player_t *, libvlc_exception_t * ); + +/** + * Set new crop filter geometry. + * + * \param p_mediaplayer the media player + * \param psz_geometry new crop filter geometry + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_video_set_crop_geometry( libvlc_media_player_t *, char *, libvlc_exception_t * ); + +/** + * Toggle teletext transparent status on video output. + * + * \param p_mediaplayer the media player + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_toggle_teletext( libvlc_media_player_t *, libvlc_exception_t * ); + +/** + * Get current teletext page requested. + * + * \param p_mediaplayer the media player + * \param p_e an initialized exception pointer + * \return the current teletext page requested. + */ +VLC_PUBLIC_API int libvlc_video_get_teletext( libvlc_media_player_t *, libvlc_exception_t * ); + +/** + * Set new teletext page to retrieve. + * + * \param p_mediaplayer the media player + * \param i_page teletex page number requested + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_video_set_teletext( libvlc_media_player_t *, int, libvlc_exception_t * ); + +/** + * Take a snapshot of the current video window. + * + * If i_width AND i_height is 0, original size is used. + * If i_width XOR i_height is 0, original aspect-ratio is preserved. + * + * \param p_mediaplayer the media player + * \param psz_filepath the path where to save the screenshot to + * \param i_width the snapshot's width + * \param i_height the snapshot's height + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_video_take_snapshot( libvlc_media_player_t *, char *,unsigned int, unsigned int, libvlc_exception_t * ); + +/** + * Resize the current video output window. + * + * \param p_instance libvlc instance + * \param width new width for video output window + * \param height new height for video output window + * \param p_e an initialized exception pointer + * \return the success status (boolean) + */ +VLC_PUBLIC_API void libvlc_video_resize( libvlc_media_player_t *, int, int, libvlc_exception_t *); + +/** + * Change the parent for the current the video output. + * + * \param p_instance libvlc instance + * \param drawable the new parent window (Drawable on X11, CGrafPort on MacOSX, HWND on Win32) + * \param p_e an initialized exception pointer + * \return the success status (boolean) + */ +VLC_PUBLIC_API int libvlc_video_reparent( libvlc_media_player_t *, libvlc_drawable_t, libvlc_exception_t * ); + +/** + * Tell windowless video output to redraw rectangular area (MacOS X only). + * + * \param p_instance libvlc instance + * \param area coordinates within video drawable + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_video_redraw_rectangle( libvlc_media_player_t *, const libvlc_rectangle_t *, libvlc_exception_t * ); + +/** + * Set the default video output size. + * + * This setting will be used as default for all video outputs. + * + * \param p_instance libvlc instance + * \param width new width for video drawable + * \param height new height for video drawable + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_video_set_size( libvlc_instance_t *, int, int, libvlc_exception_t * ); + +/** + * Set the default video output viewport for a windowless video output + * (MacOS X only). + * + * This setting will be used as default for all video outputs. + * + * \param p_instance libvlc instance + * \param view coordinates within video drawable + * \param clip coordinates within video drawable + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_video_set_viewport( libvlc_instance_t *, const libvlc_rectangle_t *, const libvlc_rectangle_t *, libvlc_exception_t * ); + +/** @} video */ + +/** \defgroup libvlc_audio libvlc_audio + * \ingroup libvlc_media_player + * LibVLC Audio handling + * @{ + */ + +/** + * Toggle mute status. + * + * \param p_instance libvlc instance + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_audio_toggle_mute( libvlc_instance_t *, libvlc_exception_t * ); + +/** + * Get current mute status. + * + * \param p_instance libvlc instance + * \param p_e an initialized exception pointer + * \return the mute status (boolean) + */ +VLC_PUBLIC_API int libvlc_audio_get_mute( libvlc_instance_t *, libvlc_exception_t * ); + +/** + * Set mute status. + * + * \param p_instance libvlc instance + * \param status If status is true then mute, otherwise unmute + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_audio_set_mute( libvlc_instance_t *, int , libvlc_exception_t * ); + +/** + * Get current audio level. + * + * \param p_instance libvlc instance + * \param p_e an initialized exception pointer + * \return the audio level (int) + */ +VLC_PUBLIC_API int libvlc_audio_get_volume( libvlc_instance_t *, libvlc_exception_t * ); + +/** + * Set current audio level. + * + * \param p_instance libvlc instance + * \param i_volume the volume (int) + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_audio_set_volume( libvlc_instance_t *, int, libvlc_exception_t *); + +/** + * Get number of available audio tracks. + * + * \param p_mi media player + * \param p_e an initialized exception + * \return the number of available audio tracks (int) + */ +VLC_PUBLIC_API int libvlc_audio_get_track_count( libvlc_media_player_t *, libvlc_exception_t * ); + +/** + * Get current audio track. + * + * \param p_mi media player + * \param p_e an initialized exception pointer + * \return the audio track (int) + */ +VLC_PUBLIC_API int libvlc_audio_get_track( libvlc_media_player_t *, libvlc_exception_t * ); + +/** + * Set current audio track. + * + * \param p_mi media player + * \param i_track the track (int) + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_audio_set_track( libvlc_media_player_t *, int, libvlc_exception_t * ); + +/** + * Get current audio channel. + * + * \param p_instance vlc instance + * \param p_e an initialized exception pointer + * \return the audio channel (int) + */ +VLC_PUBLIC_API int libvlc_audio_get_channel( libvlc_instance_t *, libvlc_exception_t * ); + +/** + * Set current audio channel. + * + * \param p_instance vlc instance + * \param i_channel the audio channel (int) + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_audio_set_channel( libvlc_instance_t *, int, libvlc_exception_t * ); + +/** @} audio */ + +/** @} media_player */ + +/***************************************************************************** + * Event handling + *****************************************************************************/ + +/** \defgroup libvlc_event libvlc_event + * \ingroup libvlc_core + * LibVLC Events + * @{ + */ + +/** + * Register for an event notification. + * + * \param p_event_manager the event manager to which you want to attach to. + * Generally it is obtained by vlc_my_object_event_manager() where + * my_object is the object you want to listen to. + * \param i_event_type the desired event to which we want to listen + * \param f_callback the function to call when i_event_type occurs + * \param user_data user provided data to carry with the event + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_event_attach( libvlc_event_manager_t *p_event_manager, + libvlc_event_type_t i_event_type, + libvlc_callback_t f_callback, + void *user_data, + libvlc_exception_t *p_e ); + +/** + * Unregister an event notification. + * + * \param p_event_manager the event manager + * \param i_event_type the desired event to which we want to unregister + * \param f_callback the function to call when i_event_type occurs + * \param p_user_data user provided data to carry with the event + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_event_detach( libvlc_event_manager_t *p_event_manager, + libvlc_event_type_t i_event_type, + libvlc_callback_t f_callback, + void *p_user_data, + libvlc_exception_t *p_e ); + +/** + * Get an event's type name. + * + * \param i_event_type the desired event + */ +VLC_PUBLIC_API const char * libvlc_event_type_name( libvlc_event_type_t event_type ); + +/** @} */ + +/***************************************************************************** + * Media Library + *****************************************************************************/ +/** \defgroup libvlc_media_library libvlc_media_library + * \ingroup libvlc + * LibVLC Media Library + * @{ + */ +VLC_PUBLIC_API libvlc_media_library_t * + libvlc_media_library_new( libvlc_instance_t * p_inst, + libvlc_exception_t * p_e ); + +/** + * Release media library object. This functions decrements the + * reference count of the media library object. If it reaches 0, + * then the object will be released. + * + * \param p_mlib media library object + */ +VLC_PUBLIC_API void + libvlc_media_library_release( libvlc_media_library_t * p_mlib ); + +/** + * Retain a reference to a media library object. This function will + * increment the reference counting for this object. Use + * libvlc_media_library_release() to decrement the reference count. + * + * \param p_mlib media library object + */ +VLC_PUBLIC_API void + libvlc_media_library_retain( libvlc_media_library_t * p_mlib ); + +/** + * Load media library. + * + * \param p_mlib media library object + * \param p_e an initialized exception object. + */ +VLC_PUBLIC_API void + libvlc_media_library_load( libvlc_media_library_t * p_mlib, + libvlc_exception_t * p_e ); + +/** + * Save media library. + * + * \param p_mlib media library object + * \param p_e an initialized exception object. + */ +VLC_PUBLIC_API void + libvlc_media_library_save( libvlc_media_library_t * p_mlib, + libvlc_exception_t * p_e ); + +/** + * Get media library subitems. + * + * \param p_mlib media library object + * \param p_e an initialized exception object. + * \return media list subitems + */ +VLC_PUBLIC_API libvlc_media_list_t * + libvlc_media_library_media_list( libvlc_media_library_t * p_mlib, + libvlc_exception_t * p_e ); + + +/** @} */ + + +/***************************************************************************** + * Services/Media Discovery + *****************************************************************************/ +/** \defgroup libvlc_media_discoverer libvlc_media_discoverer + * \ingroup libvlc + * LibVLC Media Discoverer + * @{ + */ + +/** + * Discover media service by name. + * + * \param p_inst libvlc instance + * \param psz_name service name + * \param p_e an initialized exception object + * \return media discover object + */ +VLC_PUBLIC_API libvlc_media_discoverer_t * +libvlc_media_discoverer_new_from_name( libvlc_instance_t * p_inst, + const char * psz_name, + libvlc_exception_t * p_e ); + +/** + * Release media discover object. If the reference count reaches 0, then + * the object will be released. + * + * \param p_mdis media service discover object + */ +VLC_PUBLIC_API void libvlc_media_discoverer_release( libvlc_media_discoverer_t * p_mdis ); + +/** + * Get media service discover object its localized name. + * + * \param media discover object + * \return localized name + */ +VLC_PUBLIC_API char * libvlc_media_discoverer_localized_name( libvlc_media_discoverer_t * p_mdis ); + +/** + * Get media service discover media list. + * + * \param p_mdis media service discover object + * \return list of media items + */ +VLC_PUBLIC_API libvlc_media_list_t * libvlc_media_discoverer_media_list( libvlc_media_discoverer_t * p_mdis ); + +/** + * Get event manager from media service discover object. + * + * \param p_mdis media service discover object + * \return event manager object. + */ +VLC_PUBLIC_API libvlc_event_manager_t * + libvlc_media_discoverer_event_manager( libvlc_media_discoverer_t * p_mdis ); + +/** + * Query if media service discover object is running. + * + * \param p_mdis media service discover object + * \return true if running, false if not + */ +VLC_PUBLIC_API int + libvlc_media_discoverer_is_running( libvlc_media_discoverer_t * p_mdis ); + +/**@} */ + + +/***************************************************************************** + * Message log handling + *****************************************************************************/ + +/** \defgroup libvlc_log libvlc_log + * \ingroup libvlc_core + * LibVLC Message Logging + * @{ + */ + +/** + * Return the VLC messaging verbosity level. + * + * \param p_instance libvlc instance + * \param p_e an initialized exception pointer + * \return verbosity level for messages + */ +VLC_PUBLIC_API unsigned libvlc_get_log_verbosity( const libvlc_instance_t *p_instance, + libvlc_exception_t *p_e ); + +/** + * Set the VLC messaging verbosity level. + * + * \param p_instance libvlc log instance + * \param level log level + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_set_log_verbosity( libvlc_instance_t *p_instance, unsigned level, + libvlc_exception_t *p_e ); + +/** + * Open a VLC message log instance. + * + * \param p_instance libvlc instance + * \param p_e an initialized exception pointer + * \return log message instance + */ +VLC_PUBLIC_API libvlc_log_t *libvlc_log_open( libvlc_instance_t *, libvlc_exception_t *); + +/** + * Close a VLC message log instance. + * + * \param p_log libvlc log instance + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_log_close( libvlc_log_t *, libvlc_exception_t *); + +/** + * Returns the number of messages in a log instance. + * + * \param p_log libvlc log instance + * \param p_e an initialized exception pointer + * \return number of log messages + */ +VLC_PUBLIC_API unsigned libvlc_log_count( const libvlc_log_t *, libvlc_exception_t *); + +/** + * Clear a log instance. + * + * All messages in the log are removed. The log should be cleared on a + * regular basis to avoid clogging. + * + * \param p_log libvlc log instance + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_log_clear( libvlc_log_t *, libvlc_exception_t *); + +/** + * Allocate and returns a new iterator to messages in log. + * + * \param p_log libvlc log instance + * \param p_e an initialized exception pointer + * \return log iterator object + */ +VLC_PUBLIC_API libvlc_log_iterator_t *libvlc_log_get_iterator( const libvlc_log_t *, libvlc_exception_t *); + +/** + * Release a previoulsy allocated iterator. + * + * \param p_iter libvlc log iterator + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_log_iterator_free( libvlc_log_iterator_t *p_iter, libvlc_exception_t *p_e ); + +/** + * Return whether log iterator has more messages. + * + * \param p_iter libvlc log iterator + * \param p_e an initialized exception pointer + * \return true if iterator has more message objects, else false + */ +VLC_PUBLIC_API int libvlc_log_iterator_has_next( const libvlc_log_iterator_t *p_iter, libvlc_exception_t *p_e ); + +/** + * Return the next log message. + * + * The message contents must not be freed + * + * \param p_iter libvlc log iterator + * \param p_buffer log buffer + * \param p_e an initialized exception pointer + * \return log message object + */ +VLC_PUBLIC_API libvlc_log_message_t *libvlc_log_iterator_next( libvlc_log_iterator_t *p_iter, + libvlc_log_message_t *p_buffer, + libvlc_exception_t *p_e ); + +/** @} */ + +# ifdef __cplusplus +} +# endif + +#endif /* */ diff --git a/VLC/vlc/libvlc_events.h b/VLC/vlc/libvlc_events.h new file mode 100644 index 0000000..1aa0f2f --- /dev/null +++ b/VLC/vlc/libvlc_events.h @@ -0,0 +1,223 @@ +/***************************************************************************** + * libvlc_events.h: libvlc_events external API structure + ***************************************************************************** + * Copyright (C) 1998-2008 the VideoLAN team + * $Id $ + * + * Authors: Filippo Carone + * Pierre d'Herbemont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef LIBVLC_EVENTS_H +#define LIBVLC_EVENTS_H 1 + +/** + * \file + * This file defines libvlc_event external API + */ + +# ifdef __cplusplus +extern "C" { +# endif + +/***************************************************************************** + * Events handling + *****************************************************************************/ + +/** \defgroup libvlc_event libvlc_event + * \ingroup libvlc_core + * LibVLC Available Events + * @{ + */ + +typedef enum libvlc_event_type_t { + libvlc_MediaMetaChanged, + libvlc_MediaSubItemAdded, + libvlc_MediaDurationChanged, + libvlc_MediaPreparsedChanged, + libvlc_MediaFreed, + libvlc_MediaStateChanged, + + libvlc_MediaPlayerNothingSpecial, + libvlc_MediaPlayerOpening, + libvlc_MediaPlayerBuffering, + libvlc_MediaPlayerPlaying, + libvlc_MediaPlayerPaused, + libvlc_MediaPlayerStopped, + libvlc_MediaPlayerForward, + libvlc_MediaPlayerBackward, + libvlc_MediaPlayerEndReached, + libvlc_MediaPlayerEncounteredError, + libvlc_MediaPlayerTimeChanged, + libvlc_MediaPlayerPositionChanged, + libvlc_MediaPlayerSeekableChanged, + libvlc_MediaPlayerPausableChanged, + + libvlc_MediaListItemAdded, + libvlc_MediaListWillAddItem, + libvlc_MediaListItemDeleted, + libvlc_MediaListWillDeleteItem, + + libvlc_MediaListViewItemAdded, + libvlc_MediaListViewWillAddItem, + libvlc_MediaListViewItemDeleted, + libvlc_MediaListViewWillDeleteItem, + + libvlc_MediaListPlayerPlayed, + libvlc_MediaListPlayerNextItemSet, + libvlc_MediaListPlayerStopped, + + libvlc_MediaDiscovererStarted, + libvlc_MediaDiscovererEnded + +} libvlc_event_type_t; + +/** + * An Event + * \param type the even type + * \param p_obj the sender object + * \param u Event dependent content + */ + +typedef struct libvlc_event_t +{ + libvlc_event_type_t type; + void * p_obj; + union event_type_specific + { + /* media descriptor */ + struct + { + libvlc_meta_t meta_type; + } media_meta_changed; + struct + { + libvlc_media_t * new_child; + } media_subitem_added; + struct + { + int64_t new_duration; + } media_duration_changed; + struct + { + int new_status; + } media_preparsed_changed; + struct + { + libvlc_media_t * md; + } media_freed; + struct + { + libvlc_state_t new_state; + } media_state_changed; + + /* media instance */ + struct + { + float new_position; + } media_player_position_changed; + struct + { + libvlc_time_t new_time; + } media_player_time_changed; + struct + { + libvlc_time_t new_seekable; + } media_player_seekable_changed; + struct + { + libvlc_time_t new_pausable; + } media_player_pausable_changed; + + /* media list */ + struct + { + libvlc_media_t * item; + int index; + } media_list_item_added; + struct + { + libvlc_media_t * item; + int index; + } media_list_will_add_item; + struct + { + libvlc_media_t * item; + int index; + } media_list_item_deleted; + struct + { + libvlc_media_t * item; + int index; + } media_list_will_delete_item; + + /* media list view */ + struct + { + libvlc_media_t * item; + int index; + } media_list_view_item_added; + struct + { + libvlc_media_t * item; + int index; + } media_list_view_will_add_item; + struct + { + libvlc_media_t * item; + int index; + } media_list_view_item_deleted; + struct + { + libvlc_media_t * item; + int index; + } media_list_view_will_delete_item; + + /* media discoverer */ + struct + { + void * unused; + } media_media_discoverer_started; + struct + { + void * unused; + } media_media_discoverer_ended; + + } u; +} libvlc_event_t; + +/** + * Event manager that belongs to a libvlc object, and from whom events can + * be received. + */ + +typedef struct libvlc_event_manager_t libvlc_event_manager_t; + +/** + * Callback function notification + * \param p_event the event triggering the callback + */ + +typedef void ( *libvlc_callback_t )( const libvlc_event_t *, void * ); + +/**@} */ + +# ifdef __cplusplus +} +# endif + +#endif /* _LIBVLC_EVENTS_H */ diff --git a/VLC/vlc/libvlc_media_list.h b/VLC/vlc/libvlc_media_list.h new file mode 100644 index 0000000..94a4f7b --- /dev/null +++ b/VLC/vlc/libvlc_media_list.h @@ -0,0 +1,504 @@ +/***************************************************************************** + * libvlc_media_list.h: libvlc_media_list API + ***************************************************************************** + * Copyright (C) 1998-2008 the VideoLAN team + * $Id$ + * + * Authors: Pierre d'Herbemont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef LIBVLC_MEDIA_LIST_H +#define LIBVLC_MEDIA_LIST_H 1 + +/** + * \file + * This file defines libvlc_media_list API + */ + +# ifdef __cplusplus +extern "C" { +# endif + +/***************************************************************************** + * Media List + *****************************************************************************/ +/** \defgroup libvlc_media_list libvlc_media_list + * \ingroup libvlc + * LibVLC Media List, a media list holds multiple media descriptors + * @{ + */ + +/** + * Create an empty media list. + * + * \param p_libvlc libvlc instance + * \param p_e an initialized exception pointer + * \return empty media list + */ +VLC_PUBLIC_API libvlc_media_list_t * + libvlc_media_list_new( libvlc_instance_t *, libvlc_exception_t * ); + +/** + * Release media list created with libvlc_media_list_new(). + * + * \param p_ml a media list created with libvlc_media_list_new() + */ +VLC_PUBLIC_API void + libvlc_media_list_release( libvlc_media_list_t * ); + +/** + * Retain reference to a media list + * + * \param p_ml a media list created with libvlc_media_list_new() + */ +VLC_PUBLIC_API void + libvlc_media_list_retain( libvlc_media_list_t * ); + +VLC_DEPRECATED_API void + libvlc_media_list_add_file_content( libvlc_media_list_t * p_mlist, + const char * psz_uri, + libvlc_exception_t * p_e ); + +/** + * Associate media instance with this media list instance. + * If another media instance was present it will be released. + * The libvlc_media_list_lock should NOT be held upon entering this function. + * + * \param p_ml a media list instance + * \param p_mi media instance to add + * \param p_e initialized exception object + */ +VLC_PUBLIC_API void + libvlc_media_list_set_media( libvlc_media_list_t *, + libvlc_media_t *, + libvlc_exception_t *); + +/** + * Get media instance from this media list instance. This action will increase + * the refcount on the media instance. + * The libvlc_media_list_lock should NOT be held upon entering this function. + * + * \param p_ml a media list instance + * \param p_e initialized exception object + * \return media instance + */ +VLC_PUBLIC_API libvlc_media_t * + libvlc_media_list_media( libvlc_media_list_t *, + libvlc_exception_t *); + +/** + * Add media instance to media list + * The libvlc_media_list_lock should be held upon entering this function. + * + * \param p_ml a media list instance + * \param p_mi a media instance + * \param p_e initialized exception object + */ +VLC_PUBLIC_API void + libvlc_media_list_add_media( libvlc_media_list_t *, + libvlc_media_t *, + libvlc_exception_t * ); + +/** + * Insert media instance in media list on a position + * The libvlc_media_list_lock should be held upon entering this function. + * + * \param p_ml a media list instance + * \param p_mi a media instance + * \param i_pos position in array where to insert + * \param p_e initialized exception object + */ +VLC_PUBLIC_API void + libvlc_media_list_insert_media( libvlc_media_list_t *, + libvlc_media_t *, + int, + libvlc_exception_t * ); +/** + * Remove media instance from media list on a position + * The libvlc_media_list_lock should be held upon entering this function. + * + * \param p_ml a media list instance + * \param i_pos position in array where to insert + * \param p_e initialized exception object + */ +VLC_PUBLIC_API void + libvlc_media_list_remove_index( libvlc_media_list_t *, int, + libvlc_exception_t * ); + +/** + * Get count on media list items + * The libvlc_media_list_lock should be held upon entering this function. + * + * \param p_ml a media list instance + * \param p_e initialized exception object + * \return number of items in media list + */ +VLC_PUBLIC_API int + libvlc_media_list_count( libvlc_media_list_t * p_mlist, + libvlc_exception_t * p_e ); + +/** + * List media instance in media list at a position + * The libvlc_media_list_lock should be held upon entering this function. + * + * \param p_ml a media list instance + * \param i_pos position in array where to insert + * \param p_e initialized exception object + * \return media instance at position i_pos and libvlc_media_retain() has been called to increase the refcount on this object. + */ +VLC_PUBLIC_API libvlc_media_t * + libvlc_media_list_item_at_index( libvlc_media_list_t *, int, + libvlc_exception_t * ); +/** + * Find index position of List media instance in media list. + * Warning: the function will return the first matched position. + * The libvlc_media_list_lock should be held upon entering this function. + * + * \param p_ml a media list instance + * \param p_mi media list instance + * \param p_e initialized exception object + * \return position of media instance + */ +VLC_PUBLIC_API int + libvlc_media_list_index_of_item( libvlc_media_list_t *, + libvlc_media_t *, + libvlc_exception_t * ); + +/** + * This indicates if this media list is read-only from a user point of view + * + * \param p_ml media list instance + * \return 0 on readonly, 1 on readwrite + */ +VLC_PUBLIC_API int + libvlc_media_list_is_readonly( libvlc_media_list_t * p_mlist ); + +/** + * Get lock on media list items + * + * \param p_ml a media list instance + */ +VLC_PUBLIC_API void + libvlc_media_list_lock( libvlc_media_list_t * ); + +/** + * Release lock on media list items + * The libvlc_media_list_lock should be held upon entering this function. + * + * \param p_ml a media list instance + */ +VLC_PUBLIC_API void + libvlc_media_list_unlock( libvlc_media_list_t * ); + +/** + * Get a flat media list view of media list items + * + * \param p_ml a media list instance + * \param p_ex an excpetion instance + * \return flat media list view instance + */ +VLC_PUBLIC_API libvlc_media_list_view_t * + libvlc_media_list_flat_view( libvlc_media_list_t *, + libvlc_exception_t * ); + +/** + * Get a hierarchical media list view of media list items + * + * \param p_ml a media list instance + * \param p_ex an excpetion instance + * \return hierarchical media list view instance + */ +VLC_PUBLIC_API libvlc_media_list_view_t * + libvlc_media_list_hierarchical_view( libvlc_media_list_t *, + libvlc_exception_t * ); + +VLC_PUBLIC_API libvlc_media_list_view_t * + libvlc_media_list_hierarchical_node_view( libvlc_media_list_t *, + libvlc_exception_t * ); + +/** + * Get libvlc_event_manager from this media list instance. + * The p_event_manager is immutable, so you don't have to hold the lock + * + * \param p_ml a media list instance + * \param p_ex an excpetion instance + * \return libvlc_event_manager + */ +VLC_PUBLIC_API libvlc_event_manager_t * + libvlc_media_list_event_manager( libvlc_media_list_t *, + libvlc_exception_t * ); + +/***************************************************************************** + * Media List View + *****************************************************************************/ +/** \defgroup libvlc_media_list_view libvlc_media_list_view + * \ingroup libvlc_media_list + * LibVLC Media List View, represent a media_list using a different layout + * @{ */ + +/** + * Retain reference to a media list view + * + * \param p_mlv a media list view created with libvlc_media_list_view_new() + */ +VLC_PUBLIC_API void + libvlc_media_list_view_retain( libvlc_media_list_view_t * p_mlv ); + +/** + * Release reference to a media list view. If the refcount reaches 0, then + * the object will be released. + * + * \param p_mlv a media list view created with libvlc_media_list_view_new() + */ +VLC_PUBLIC_API void + libvlc_media_list_view_release( libvlc_media_list_view_t * p_mlv ); + +/** + * Get libvlc_event_manager from this media list view instance. + * The p_event_manager is immutable, so you don't have to hold the lock + * + * \param p_mlv a media list view instance + * \return libvlc_event_manager + */ +VLC_PUBLIC_API libvlc_event_manager_t * + libvlc_media_list_view_event_manager( libvlc_media_list_view_t * p_mlv ); + +/** + * Get count on media list view items + * + * \param p_mlv a media list view instance + * \param p_e initialized exception object + * \return number of items in media list view + */ +VLC_PUBLIC_API int + libvlc_media_list_view_count( libvlc_media_list_view_t * p_mlv, + libvlc_exception_t * p_e ); + +/** + * List media instance in media list view at an index position + * + * \param p_mlv a media list view instance + * \param i_index index position in array where to insert + * \param p_e initialized exception object + * \return media instance at position i_pos and libvlc_media_retain() has been called to increase the refcount on this object. + */ +VLC_PUBLIC_API libvlc_media_t * + libvlc_media_list_view_item_at_index( libvlc_media_list_view_t * p_mlv, + int i_index, + libvlc_exception_t * p_e ); + +VLC_PUBLIC_API libvlc_media_list_view_t * + libvlc_media_list_view_children_at_index( libvlc_media_list_view_t * p_mlv, + int index, + libvlc_exception_t * p_e ); + +VLC_PUBLIC_API libvlc_media_list_view_t * + libvlc_media_list_view_children_for_item( libvlc_media_list_view_t * p_mlv, + libvlc_media_t * p_md, + libvlc_exception_t * p_e ); + +/** + * Get index position of media instance in media list view. + * The function will return the first occurence. + * + * \param p_mlv a media list view instance + * \param p_md media instance + * \param p_e initialized exception object + * \return index position in array of p_md + */ +VLC_PUBLIC_API int + libvlc_media_list_view_index_of_item( libvlc_media_list_view_t * p_mlv, + libvlc_media_t * p_md, + libvlc_exception_t * p_e ); + +/** + * Insert media instance in media list view at index position + * + * \param p_mlv a media list view instance + * \param p_md media instance + * \param index position in array where to insert + * \param p_e initialized exception object + */ +VLC_PUBLIC_API void + libvlc_media_list_view_insert_at_index( libvlc_media_list_view_t * p_mlv, + libvlc_media_t * p_md, + int index, + libvlc_exception_t * p_e ); + +/** + * Remove media instance in media list view from index position + * + * \param p_mlv a media list view instance + * \param index position in array of media instance to remove + * \param p_e initialized exception object + */ +VLC_PUBLIC_API void + libvlc_media_list_view_remove_at_index( libvlc_media_list_view_t * p_mlv, + int index, + libvlc_exception_t * p_e ); + +VLC_PUBLIC_API void + libvlc_media_list_view_add_item( libvlc_media_list_view_t * p_mlv, + libvlc_media_t * p_md, + libvlc_exception_t * p_e ); + +VLC_PUBLIC_API libvlc_media_list_t * + libvlc_media_list_view_parent_media_list( libvlc_media_list_view_t * p_mlv, + libvlc_exception_t * p_e ); + +/** @} media_list_view */ + +/***************************************************************************** + * Media List Player + *****************************************************************************/ +/** \defgroup libvlc_media_list_player libvlc_media_list_player + * \ingroup libvlc_media_list_player + * LibVLC Media List Player, play a media_list. You can see that as a media + * instance subclass + * @{ + */ + +/** + * Create new media_list_player. + * + * \param p_instance libvlc instance + * \param p_e initialized exception instance + * \return media list player instance + */ +VLC_PUBLIC_API libvlc_media_list_player_t * + libvlc_media_list_player_new( libvlc_instance_t * p_instance, + libvlc_exception_t * p_e ); + +/** + * Release media_list_player. + * + * \param p_mlp media list player instance + */ +VLC_PUBLIC_API void + libvlc_media_list_player_release( libvlc_media_list_player_t * p_mlp ); + +/** + * Replace media player in media_list_player with this instance. + * + * \param p_mlp media list player instance + * \param p_mi media player instance + * \param p_e initialized exception instance + */ +VLC_PUBLIC_API void + libvlc_media_list_player_set_media_player( + libvlc_media_list_player_t * p_mlp, + libvlc_media_player_t * p_mi, + libvlc_exception_t * p_e ); + +VLC_PUBLIC_API void + libvlc_media_list_player_set_media_list( + libvlc_media_list_player_t * p_mlp, + libvlc_media_list_t * p_mlist, + libvlc_exception_t * p_e ); + +/** + * Play media list + * + * \param p_mlp media list player instance + * \param p_e initialized exception instance + */ +VLC_PUBLIC_API void + libvlc_media_list_player_play( libvlc_media_list_player_t * p_mlp, + libvlc_exception_t * p_e ); + +/** + * Pause media list + * + * \param p_mlp media list player instance + * \param p_e initialized exception instance + */ +VLC_PUBLIC_API void + libvlc_media_list_player_pause( libvlc_media_list_player_t * p_mlp, + libvlc_exception_t * p_e ); + +/** + * Is media list playing? + * + * \param p_mlp media list player instance + * \param p_e initialized exception instance + * \return true for playing and false for not playing + */ +VLC_PUBLIC_API int + libvlc_media_list_player_is_playing( libvlc_media_list_player_t * p_mlp, + libvlc_exception_t * p_e ); + +/** + * Get current libvlc_state of media list player + * + * \param p_mlp media list player instance + * \param p_e initialized exception instance + * \return libvlc_state_t for media list player + */ +VLC_PUBLIC_API libvlc_state_t + libvlc_media_list_player_get_state( libvlc_media_list_player_t * p_mlp, + libvlc_exception_t * p_e ); + +/** + * Play media list item at position index + * + * \param p_mlp media list player instance + * \param i_index index in media list to play + * \param p_e initialized exception instance + */ +VLC_PUBLIC_API void + libvlc_media_list_player_play_item_at_index( + libvlc_media_list_player_t * p_mlp, + int i_index, + libvlc_exception_t * p_e ); + +VLC_PUBLIC_API void + libvlc_media_list_player_play_item( + libvlc_media_list_player_t * p_mlp, + libvlc_media_t * p_md, + libvlc_exception_t * p_e ); + +/** + * Stop playing media list + * + * \param p_mlp media list player instance + * \param p_e initialized exception instance + */ +VLC_PUBLIC_API void + libvlc_media_list_player_stop( libvlc_media_list_player_t * p_mlp, + libvlc_exception_t * p_e ); + +/** + * Play next item from media list + * + * \param p_mlp media list player instance + * \param p_e initialized exception instance + */ +VLC_PUBLIC_API void + libvlc_media_list_player_next( libvlc_media_list_player_t * p_mlp, + libvlc_exception_t * p_e ); + +/* NOTE: shouldn't there also be a libvlc_media_list_player_prev() */ + +/** @} media_list_player */ + +/** @} media_list */ + +# ifdef __cplusplus +} +# endif + +#endif /* _LIBVLC_MEDIA_LIST_H */ diff --git a/VLC/vlc/libvlc_structures.h b/VLC/vlc/libvlc_structures.h new file mode 100644 index 0000000..76427ad --- /dev/null +++ b/VLC/vlc/libvlc_structures.h @@ -0,0 +1,288 @@ +/***************************************************************************** + * libvlc.h: libvlc_* new external API structures + ***************************************************************************** + * Copyright (C) 1998-2008 the VideoLAN team + * $Id $ + * + * Authors: Filippo Carone + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef LIBVLC_STRUCTURES_H +#define LIBVLC_STRUCTURES_H 1 + +/** + * \file + * This file defines libvlc_* new external API structures + */ + +#include + +# ifdef __cplusplus +extern "C" { +# endif + +/** This structure is opaque. It represents a libvlc instance */ +typedef struct libvlc_instance_t libvlc_instance_t; + +/***************************************************************************** + * Exceptions + *****************************************************************************/ + +/** \defgroup libvlc_exception libvlc_exception + * \ingroup libvlc_core + * LibVLC Exceptions handling + * @{ + */ + +typedef struct libvlc_exception_t +{ + int b_raised; + int i_code; + char *psz_message; +} libvlc_exception_t; + +/**@} */ + +/***************************************************************************** + * Time + *****************************************************************************/ +/** \defgroup libvlc_time libvlc_time + * \ingroup libvlc_core + * LibVLC Time support in libvlc + * @{ + */ + +typedef int64_t libvlc_time_t; + +/**@} */ + +/***************************************************************************** + * Media Descriptor + *****************************************************************************/ +/** \defgroup libvlc_media libvlc_media + * \ingroup libvlc + * LibVLC Media Descriptor handling + * @{ + */ + +/* Meta Handling */ +/** defgroup libvlc_meta libvlc_meta + * \ingroup libvlc_media + * LibVLC Media Meta + * @{ + */ + +typedef enum libvlc_meta_t { + libvlc_meta_Title, + libvlc_meta_Artist, + libvlc_meta_Genre, + libvlc_meta_Copyright, + libvlc_meta_Album, + libvlc_meta_TrackNumber, + libvlc_meta_Description, + libvlc_meta_Rating, + libvlc_meta_Date, + libvlc_meta_Setting, + libvlc_meta_URL, + libvlc_meta_Language, + libvlc_meta_NowPlaying, + libvlc_meta_Publisher, + libvlc_meta_EncodedBy, + libvlc_meta_ArtworkURL, + libvlc_meta_TrackID +} libvlc_meta_t; + +/**@} */ + +typedef struct libvlc_media_t libvlc_media_t; + +/**@} */ + + +/***************************************************************************** + * Media Instance + *****************************************************************************/ +/** \defgroup libvlc_media_player libvlc_media_player + * \ingroup libvlc + * LibVLC Media Instance handling + * @{ + */ + +typedef struct libvlc_media_player_t libvlc_media_player_t; + +/** + * Note the order of libvlc_state_t enum must match exactly the order of + * @see mediacontrol_PlayerStatus and @see input_state_e enums. + * + * Expected states by web plugins are: + * IDLE/CLOSE=0, OPENING=1, BUFFERING=2, PLAYING=3, PAUSED=4, + * STOPPING=5, FORWARD=6, BACKWARD=7, ENDED=8, ERROR=9 + */ +typedef enum libvlc_state_t +{ + libvlc_NothingSpecial=0, + libvlc_Opening, + libvlc_Buffering, + libvlc_Playing, + libvlc_Paused, + libvlc_Stopped, + libvlc_Forward, + libvlc_Backward, + libvlc_Ended, + libvlc_Error +} libvlc_state_t; + +/**@} */ + +/***************************************************************************** + * Media List + *****************************************************************************/ +/** \defgroup libvlc_media_list libvlc_media_list + * \ingroup libvlc + * LibVLC Media List handling + * @{ + */ + +typedef struct libvlc_media_list_t libvlc_media_list_t; +typedef struct libvlc_media_list_view_t libvlc_media_list_view_t; + + +/***************************************************************************** + * Media List Player + *****************************************************************************/ +/** \defgroup libvlc_media_list_player libvlc_media_list_player + * \ingroup libvlc_media_list + * LibVLC Media List Player handling + * @{ + */ + +typedef struct libvlc_media_list_player_t libvlc_media_list_player_t; + +/**@} libvlc_media_list_player */ + +/**@} libvlc_media_list */ + +/***************************************************************************** + * Media Library + *****************************************************************************/ +/** \defgroup libvlc_media_library libvlc_media_library + * \ingroup libvlc + * LibVLC Media Library + * @{ + */ + +typedef struct libvlc_media_library_t libvlc_media_library_t; + +/**@} */ + +/***************************************************************************** + * Playlist + *****************************************************************************/ +/** \defgroup libvlc_playlist libvlc_playlist (Deprecated) + * \ingroup libvlc + * LibVLC Playlist handling (Deprecated) + * @deprecated Use media_list + * @{ + */ + +typedef struct libvlc_playlist_item_t +{ + int i_id; + char * psz_uri; + char * psz_name; + +} libvlc_playlist_item_t; + +/**@} */ + + +/***************************************************************************** + * Video + *****************************************************************************/ +/** \defgroup libvlc_video libvlc_video + * \ingroup libvlc_media_player + * LibVLC Video handling + * @{ + */ + +/** +* Downcast to this general type as placeholder for a platform specific one, such as: +* Drawable on X11, +* CGrafPort on MacOSX, +* HWND on win32 +*/ +typedef int libvlc_drawable_t; + +/** +* Rectangle type for video geometry +*/ +typedef struct libvlc_rectangle_t +{ + int top, left; + int bottom, right; +} +libvlc_rectangle_t; + +/**@} */ + + +/***************************************************************************** + * Services/Media Discovery + *****************************************************************************/ +/** \defgroup libvlc_media_discoverer libvlc_media_discoverer + * \ingroup libvlc + * LibVLC Media Discoverer + * @{ + */ + +typedef struct libvlc_media_discoverer_t libvlc_media_discoverer_t; + +/**@} */ + +/***************************************************************************** + * Message log handling + *****************************************************************************/ + +/** \defgroup libvlc_log libvlc_log + * \ingroup libvlc_core + * LibVLC Message Logging + * @{ + */ + +/** This structure is opaque. It represents a libvlc log instance */ +typedef struct libvlc_log_t libvlc_log_t; + +/** This structure is opaque. It represents a libvlc log iterator */ +typedef struct libvlc_log_iterator_t libvlc_log_iterator_t; + +typedef struct libvlc_log_message_t +{ + unsigned sizeof_msg; /* sizeof() of message structure, must be filled in by user */ + int i_severity; /* 0=INFO, 1=ERR, 2=WARN, 3=DBG */ + const char *psz_type; /* module type */ + const char *psz_name; /* module name */ + const char *psz_header; /* optional header */ + const char *psz_message; /* message */ +} libvlc_log_message_t; + +/**@} */ + +# ifdef __cplusplus +} +# endif + +#endif diff --git a/VLC/vlc/libvlc_vlm.h b/VLC/vlc/libvlc_vlm.h new file mode 100644 index 0000000..de314e4 --- /dev/null +++ b/VLC/vlc/libvlc_vlm.h @@ -0,0 +1,254 @@ +/***************************************************************************** + * libvlc_vlm.h: libvlc_* new external API + ***************************************************************************** + * Copyright (C) 1998-2008 the VideoLAN team + * $Id$ + * + * Authors: Clément Stenac + * Jean-Paul Saman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef LIBVLC_VLM_H +#define LIBVLC_VLM_H 1 + +/** + * \file + * This file defines libvlc_vlm_* external API + */ + +# ifdef __cplusplus +extern "C" { +# endif + +/***************************************************************************** + * VLM + *****************************************************************************/ +/** \defgroup libvlc_vlm libvlc_vlm + * \ingroup libvlc + * LibVLC VLM + * @{ + */ + + +/** + * Release the vlm instance related to the given libvlc_instance_t + * + * \param p_instance the instance + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_vlm_release( libvlc_instance_t *, libvlc_exception_t * ); + +/** + * Add a broadcast, with one input. + * + * \param p_instance the instance + * \param psz_name the name of the new broadcast + * \param psz_input the input MRL + * \param psz_output the output MRL (the parameter to the "sout" variable) + * \param i_options number of additional options + * \param ppsz_options additional options + * \param b_enabled boolean for enabling the new broadcast + * \param b_loop Should this broadcast be played in loop ? + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_vlm_add_broadcast( libvlc_instance_t *, char *, char *, char* , + int, char **, int, int, libvlc_exception_t * ); + +/** + * Add a vod, with one input. + * + * \param p_instance the instance + * \param psz_name the name of the new vod media + * \param psz_input the input MRL + * \param i_options number of additional options + * \param ppsz_options additional options + * \param b_enabled boolean for enabling the new vod + * \param psz_mux the muxer of the vod media + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_vlm_add_vod( libvlc_instance_t *, char *, char *, int, char **, + int, char *, libvlc_exception_t * ); + +/** + * Delete a media (VOD or broadcast). + * + * \param p_instance the instance + * \param psz_name the media to delete + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_vlm_del_media( libvlc_instance_t *, char *, libvlc_exception_t * ); + +/** + * Enable or disable a media (VOD or broadcast). + * + * \param p_instance the instance + * \param psz_name the media to work on + * \param b_enabled the new status + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_vlm_set_enabled( libvlc_instance_t *, char *, int, + libvlc_exception_t *); + +/** + * Set the output for a media. + * + * \param p_instance the instance + * \param psz_name the media to work on + * \param psz_output the output MRL (the parameter to the "sout" variable) + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_vlm_set_output( libvlc_instance_t *, char *, char*, + libvlc_exception_t *); + +/** + * Set a media's input MRL. This will delete all existing inputs and + * add the specified one. + * + * \param p_instance the instance + * \param psz_name the media to work on + * \param psz_input the input MRL + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_vlm_set_input( libvlc_instance_t *, char *, char*, + libvlc_exception_t *); + +/** + * Add a media's input MRL. This will add the specified one. + * + * \param p_instance the instance + * \param psz_name the media to work on + * \param psz_input the input MRL + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_vlm_add_input( libvlc_instance_t *, char *, char *, + libvlc_exception_t *p_exception ); +/** + * Set a media's loop status. + * + * \param p_instance the instance + * \param psz_name the media to work on + * \param b_loop the new status + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_vlm_set_loop( libvlc_instance_t *, char *, int, + libvlc_exception_t *); + +/** + * Set a media's vod muxer. + * + * \param p_instance the instance + * \param psz_name the media to work on + * \param psz_mux the new muxer + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_vlm_set_mux( libvlc_instance_t *p_instance, char *psz_name, + char *psz_mux, libvlc_exception_t *p_exception ); + +/** + * Edit the parameters of a media. This will delete all existing inputs and + * add the specified one. + * + * \param p_instance the instance + * \param psz_name the name of the new broadcast + * \param psz_input the input MRL + * \param psz_output the output MRL (the parameter to the "sout" variable) + * \param i_options number of additional options + * \param ppsz_options additional options + * \param b_enabled boolean for enabling the new broadcast + * \param b_loop Should this broadcast be played in loop ? + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_vlm_change_media( libvlc_instance_t *, char *, char *, char* , + int, char **, int, int, libvlc_exception_t * ); + +/** + * Play the named broadcast. + * + * \param p_instance the instance + * \param psz_name the name of the broadcast + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_vlm_play_media ( libvlc_instance_t *, char *, libvlc_exception_t * ); + +/** + * Stop the named broadcast. + * + * \param p_instance the instance + * \param psz_name the name of the broadcast + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_vlm_stop_media ( libvlc_instance_t *, char *, libvlc_exception_t * ); + +/** + * Pause the named broadcast. + * + * \param p_instance the instance + * \param psz_name the name of the broadcast + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_vlm_pause_media( libvlc_instance_t *, char *, libvlc_exception_t * ); + +/** + * Seek in the named broadcast. + * + * \param p_instance the instance + * \param psz_name the name of the broadcast + * \param f_percentage the percentage to seek to + * \param p_e an initialized exception pointer + */ +VLC_PUBLIC_API void libvlc_vlm_seek_media( libvlc_instance_t *, char *, + float, libvlc_exception_t * ); + +/** + * Return information about the named broadcast. + * + * \param p_instance the instance + * \param psz_name the name of the broadcast + * \param p_e an initialized exception pointer + * \return string with information about named media + */ +VLC_PUBLIC_API char* libvlc_vlm_show_media( libvlc_instance_t *, char *, libvlc_exception_t * ); + +/** + * Get information about media attribute from vlm. + * + * \param libvlc instance + * \param type of information + * \param default value + * \return value of media attribute + */ +#define LIBVLC_VLM_GET_MEDIA_ATTRIBUTE( attr, returnType, getType, default)\ +returnType libvlc_vlm_get_media_instance_## attr( libvlc_instance_t *, \ + char *, int , libvlc_exception_t * ); + +VLC_PUBLIC_API LIBVLC_VLM_GET_MEDIA_ATTRIBUTE( position, float, Float, -1); +VLC_PUBLIC_API LIBVLC_VLM_GET_MEDIA_ATTRIBUTE( time, int, Integer, -1); +VLC_PUBLIC_API LIBVLC_VLM_GET_MEDIA_ATTRIBUTE( length, int, Integer, -1); +VLC_PUBLIC_API LIBVLC_VLM_GET_MEDIA_ATTRIBUTE( rate, int, Integer, -1); +VLC_PUBLIC_API LIBVLC_VLM_GET_MEDIA_ATTRIBUTE( title, int, Integer, 0); +VLC_PUBLIC_API LIBVLC_VLM_GET_MEDIA_ATTRIBUTE( chapter, int, Integer, 0); +VLC_PUBLIC_API LIBVLC_VLM_GET_MEDIA_ATTRIBUTE( seekable, int, Bool, 0); + +#undef LIBVLC_VLM_GET_MEDIA_ATTRIBUTE + +/** @} */ + +# ifdef __cplusplus +} +# endif + +#endif /* */ diff --git a/VLC/vlc/mediacontrol.h b/VLC/vlc/mediacontrol.h new file mode 100644 index 0000000..7f04455 --- /dev/null +++ b/VLC/vlc/mediacontrol.h @@ -0,0 +1,342 @@ +/***************************************************************************** + * mediacontrol.h: global header for mediacontrol + ***************************************************************************** + * Copyright (C) 2005-2008 the VideoLAN team + * $Id$ + * + * Authors: Olivier Aubert + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/** + * \file + * This file defines libvlc mediacontrol_* external API + */ + +/** + * \defgroup mediacontrol MediaControl + * This is the MediaControl API, * intended to provide a generic API to movie players. + * + * @{ + */ + + +#ifndef VLC_CONTROL_H +#define VLC_CONTROL_H 1 + +# ifdef __cplusplus +extern "C" { +# endif + +#if defined( WIN32 ) +#include +typedef HWND WINDOWHANDLE; +#else +typedef int WINDOWHANDLE; +#endif + +#include +#include + +/** + * mediacontrol_Instance is an opaque structure, defined in + * mediacontrol_internal.h. API users do not have to mess with it. + */ +typedef struct mediacontrol_Instance mediacontrol_Instance; + +/************************************************************************** + * Helper functions + ***************************************************************************/ + +/** + * Free a RGBPicture structure. + * \param pic: the RGBPicture structure + */ +VLC_PUBLIC_API void mediacontrol_RGBPicture__free( mediacontrol_RGBPicture *pic ); + +VLC_PUBLIC_API void mediacontrol_PlaylistSeq__free( mediacontrol_PlaylistSeq *ps ); + +/** + * Free a StreamInformation structure. + * \param pic: the StreamInformation structure + */ +VLC_PUBLIC_API void +mediacontrol_StreamInformation__free( mediacontrol_StreamInformation* p_si ); + +/** + * Instanciate and initialize an exception structure. + * \return the exception + */ +VLC_PUBLIC_API mediacontrol_Exception * + mediacontrol_exception_create( void ); + +/** + * Initialize an existing exception structure. + * \param p_exception the exception to initialize. + */ +VLC_PUBLIC_API void + mediacontrol_exception_init( mediacontrol_Exception *exception ); + +/** + * Clean up an existing exception structure after use. + * \param p_exception the exception to clean up. + */ +VLC_PUBLIC_API void +mediacontrol_exception_cleanup( mediacontrol_Exception *exception ); + +/** + * Free an exception structure created with mediacontrol_exception_create(). + * \return the exception + */ +VLC_PUBLIC_API void mediacontrol_exception_free(mediacontrol_Exception *exception); + +/***************************************************************************** + * Core functions + *****************************************************************************/ + +/** + * Create a MediaControl instance with parameters + * \param argc the number of arguments + * \param argv parameters + * \param exception an initialized exception pointer + * \return a mediacontrol_Instance + */ +VLC_PUBLIC_API mediacontrol_Instance * +mediacontrol_new( int argc, char **argv, mediacontrol_Exception *exception ); + +/** + * Create a MediaControl instance from an existing libvlc instance + * \param p_instance the libvlc instance + * \param exception an initialized exception pointer + * \return a mediacontrol_Instance + */ +VLC_PUBLIC_API mediacontrol_Instance * +mediacontrol_new_from_instance( libvlc_instance_t* p_instance, + mediacontrol_Exception *exception ); + +/** + * Get the associated libvlc instance + * \param self: the mediacontrol instance + * \return a libvlc instance + */ +VLC_PUBLIC_API libvlc_instance_t* +mediacontrol_get_libvlc_instance( mediacontrol_Instance* self ); + +/** + * Get the associated libvlc_media_player + * \param self: the mediacontrol instance + * \return a libvlc_media_player_t instance + */ +VLC_PUBLIC_API libvlc_media_player_t* +mediacontrol_get_media_player( mediacontrol_Instance* self ); + +/** + * Get the current position + * \param self the mediacontrol instance + * \param an_origin the position origin + * \param a_key the position unit + * \param exception an initialized exception pointer + * \return a mediacontrol_Position + */ +VLC_PUBLIC_API mediacontrol_Position * mediacontrol_get_media_position( + mediacontrol_Instance *self, + const mediacontrol_PositionOrigin an_origin, + const mediacontrol_PositionKey a_key, + mediacontrol_Exception *exception ); + +/** + * Set the position + * \param self the mediacontrol instance + * \param a_position a mediacontrol_Position + * \param exception an initialized exception pointer + */ +VLC_PUBLIC_API void mediacontrol_set_media_position( mediacontrol_Instance *self, + const mediacontrol_Position *a_position, + mediacontrol_Exception *exception ); + +/** + * Play the movie at a given position + * \param self the mediacontrol instance + * \param a_position a mediacontrol_Position + * \param exception an initialized exception pointer + */ +VLC_PUBLIC_API void mediacontrol_start( mediacontrol_Instance *self, + const mediacontrol_Position *a_position, + mediacontrol_Exception *exception ); + +/** + * Pause the movie at a given position + * \param self the mediacontrol instance + * \param exception an initialized exception pointer + */ +VLC_PUBLIC_API void mediacontrol_pause( mediacontrol_Instance *self, + mediacontrol_Exception *exception ); + +/** + * Resume the movie at a given position + * \param self the mediacontrol instance + * \param exception an initialized exception pointer + */ +VLC_PUBLIC_API void mediacontrol_resume( mediacontrol_Instance *self, + mediacontrol_Exception *exception ); + +/** + * Stop the movie at a given position + * \param self the mediacontrol instance + * \param exception an initialized exception pointer + */ +VLC_PUBLIC_API void mediacontrol_stop( mediacontrol_Instance *self, + mediacontrol_Exception *exception ); + +/** + * Exit the player + * \param self the mediacontrol instance + */ +VLC_PUBLIC_API void mediacontrol_exit( mediacontrol_Instance *self ); + +/** + * Set the MRL to be played. + * \param self the mediacontrol instance + * \param psz_file the MRL + * \param exception an initialized exception pointer + */ +VLC_PUBLIC_API void mediacontrol_set_mrl( mediacontrol_Instance *self, + const char* psz_file, + mediacontrol_Exception *exception ); + +/** + * Get the MRL to be played. + * \param self the mediacontrol instance + * \param exception an initialized exception pointer + */ +VLC_PUBLIC_API char * mediacontrol_get_mrl( mediacontrol_Instance *self, + mediacontrol_Exception *exception ); + +/***************************************************************************** + * A/V functions + *****************************************************************************/ +/** + * Get a snapshot + * \param self the mediacontrol instance + * \param a_position the desired position (ignored for now) + * \param exception an initialized exception pointer + * \return a RGBpicture + */ +VLC_PUBLIC_API mediacontrol_RGBPicture * + mediacontrol_snapshot( mediacontrol_Instance *self, + const mediacontrol_Position *a_position, + mediacontrol_Exception *exception ); + +/** + * Displays the message string, between "begin" and "end" positions. + * \param self the mediacontrol instance + * \param message the message to display + * \param begin the begin position + * \param end the end position + * \param exception an initialized exception pointer + */ +VLC_PUBLIC_API void mediacontrol_display_text( mediacontrol_Instance *self, + const char *message, + const mediacontrol_Position *begin, + const mediacontrol_Position *end, + mediacontrol_Exception *exception ); + +/** + * Get information about a stream + * \param self the mediacontrol instance + * \param a_key the time unit + * \param exception an initialized exception pointer + * \return a mediacontrol_StreamInformation + */ +VLC_PUBLIC_API mediacontrol_StreamInformation * + mediacontrol_get_stream_information( mediacontrol_Instance *self, + mediacontrol_PositionKey a_key, + mediacontrol_Exception *exception ); + +/** + * Get the current audio level, normalized in [0..100] + * \param self the mediacontrol instance + * \param exception an initialized exception pointer + * \return the volume + */ +VLC_PUBLIC_API unsigned short + mediacontrol_sound_get_volume( mediacontrol_Instance *self, + mediacontrol_Exception *exception ); +/** + * Set the audio level + * \param self the mediacontrol instance + * \param volume the volume (normalized in [0..100]) + * \param exception an initialized exception pointer + */ +VLC_PUBLIC_API void mediacontrol_sound_set_volume( mediacontrol_Instance *self, + const unsigned short volume, + mediacontrol_Exception *exception ); + +/** + * Set the video output window + * \param self the mediacontrol instance + * \param visual_id the Xid or HWND, depending on the platform + * \param exception an initialized exception pointer + */ +VLC_PUBLIC_API int mediacontrol_set_visual( mediacontrol_Instance *self, + WINDOWHANDLE visual_id, + mediacontrol_Exception *exception ); + +/** + * Get the current playing rate, in percent + * \param self the mediacontrol instance + * \param exception an initialized exception pointer + * \return the rate + */ +VLC_PUBLIC_API int mediacontrol_get_rate( mediacontrol_Instance *self, + mediacontrol_Exception *exception ); + +/** + * Set the playing rate, in percent + * \param self the mediacontrol instance + * \param rate the desired rate + * \param exception an initialized exception pointer + */ +VLC_PUBLIC_API void mediacontrol_set_rate( mediacontrol_Instance *self, + const int rate, + mediacontrol_Exception *exception ); + +/** + * Get current fullscreen status + * \param self the mediacontrol instance + * \param exception an initialized exception pointer + * \return the fullscreen status + */ +VLC_PUBLIC_API int mediacontrol_get_fullscreen( mediacontrol_Instance *self, + mediacontrol_Exception *exception ); + +/** + * Set fullscreen status + * \param self the mediacontrol instance + * \param b_fullscreen the desired status + * \param exception an initialized exception pointer + */ +VLC_PUBLIC_API void mediacontrol_set_fullscreen( mediacontrol_Instance *self, + const int b_fullscreen, + mediacontrol_Exception *exception ); + +# ifdef __cplusplus +} +# endif + +#endif + +/** @} */ diff --git a/VLC/vlc/mediacontrol_structures.h b/VLC/vlc/mediacontrol_structures.h new file mode 100644 index 0000000..30dea59 --- /dev/null +++ b/VLC/vlc/mediacontrol_structures.h @@ -0,0 +1,145 @@ +/***************************************************************************** + * mediacontrol_structures.h: global header for mediacontrol + ***************************************************************************** + * Copyright (C) 2005-2008 the VideoLAN team + * $Id$ + * + * Authors: Olivier Aubert + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/** + * \file + * This file defines libvlc mediacontrol_* data structures + */ + +/** + * \defgroup mediacontrol_structures MediaControl Structures + * Data structures used in the MediaControl API. + * + * @{ + */ + +#ifndef VLC_CONTROL_STRUCTURES_H +#define VLC_CONTROL_STRUCTURES_H 1 + +# ifdef __cplusplus +extern "C" { +# endif + +#include + +/** + * A position may have different origins: + * - absolute counts from the movie start + * - relative counts from the current position + * - modulo counts from the current position and wraps at the end of the movie + */ +typedef enum { + mediacontrol_AbsolutePosition, + mediacontrol_RelativePosition, + mediacontrol_ModuloPosition +} mediacontrol_PositionOrigin; + +/** + * Units available in mediacontrol Positions + * - ByteCount number of bytes + * - SampleCount number of frames + * - MediaTime time in milliseconds + */ +typedef enum { + mediacontrol_ByteCount, + mediacontrol_SampleCount, + mediacontrol_MediaTime +} mediacontrol_PositionKey; + +/** + * Possible player status + * Note the order of these enums must match exactly the order of + * libvlc_state_t and input_state_e enums. + */ +typedef enum { + mediacontrol_UndefinedStatus=0, mediacontrol_InitStatus, + mediacontrol_BufferingStatus, mediacontrol_PlayingStatus, + mediacontrol_PauseStatus, mediacontrol_StopStatus, + mediacontrol_ForwardStatus, mediacontrol_BackwardStatus, + mediacontrol_EndStatus, mediacontrol_ErrorStatus, +} mediacontrol_PlayerStatus; + +/** + * MediaControl Position + */ +typedef struct { + mediacontrol_PositionOrigin origin; + mediacontrol_PositionKey key; + int64_t value; +} mediacontrol_Position; + +/** + * RGBPicture structure + * This generic structure holds a picture in an encoding specified by type. + */ +typedef struct { + int width; + int height; + uint32_t type; + int64_t date; + int size; + char *data; +} mediacontrol_RGBPicture; + +/** + * Playlist sequence + * A simple list of strings. + */ +typedef struct { + int size; + char **data; +} mediacontrol_PlaylistSeq; + +typedef struct { + int code; + char *message; +} mediacontrol_Exception; + +/** + * Exception codes + */ +#define mediacontrol_PositionKeyNotSupported 1 +#define mediacontrol_PositionOriginNotSupported 2 +#define mediacontrol_InvalidPosition 3 +#define mediacontrol_PlaylistException 4 +#define mediacontrol_InternalException 5 + +/** + * Stream information + * This structure allows to quickly get various informations about the stream. + */ +typedef struct { + mediacontrol_PlayerStatus streamstatus; + char *url; /* The URL of the current media stream */ + int64_t position; /* actual location in the stream (in ms) */ + int64_t length; /* total length of the stream (in ms) */ +} mediacontrol_StreamInformation; + + +# ifdef __cplusplus +} +# endif + +#endif + +/** @} */ diff --git a/VLC/vlc/vlc.h b/VLC/vlc/vlc.h new file mode 100644 index 0000000..42cb93c --- /dev/null +++ b/VLC/vlc/vlc.h @@ -0,0 +1,51 @@ +/***************************************************************************** + * vlc.h: global header for libvlc + ***************************************************************************** + * Copyright (C) 1998-2008 the VideoLAN team + * $Id$ + * + * Authors: Vincent Seguin + * Samuel Hocevar + * Gildas Bazin + * Derk-Jan Hartman + * Pierre d'Herbemont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_VLC_H +#define VLC_VLC_H 1 + +/** + * \file + * This file defines libvlc new external API + */ + +# ifdef __cplusplus +extern "C" { +# endif + +#include "libvlc_structures.h" +#include "libvlc_events.h" +#include "libvlc.h" +#include "libvlc_media_list.h" +#include "libvlc_vlm.h" +#include "deprecated.h" + +# ifdef __cplusplus +} +# endif + +#endif /* _VLC_VLC_H */ diff --git a/VLC/vlc_access.h b/VLC/vlc_access.h new file mode 100644 index 0000000..cc2a721 --- /dev/null +++ b/VLC/vlc_access.h @@ -0,0 +1,174 @@ +/***************************************************************************** + * vlc_access.h: Access descriptor, queries and methods + ***************************************************************************** + * Copyright (C) 1999-2006 the VideoLAN team + * $Id$ + * + * Authors: Laurent Aimar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_ACCESS_H +#define VLC_ACCESS_H 1 + +/** + * \file + * This file defines functions and definitions for access object + */ + +#include "vlc_block.h" + +/** + * \defgroup access Access + * @{ + */ + +enum access_query_e +{ + /* capabilities */ + ACCESS_CAN_SEEK, /* arg1= bool* cannot fail */ + ACCESS_CAN_FASTSEEK, /* arg1= bool* cannot fail */ + ACCESS_CAN_PAUSE, /* arg1= bool* cannot fail */ + ACCESS_CAN_CONTROL_PACE,/* arg1= bool* cannot fail */ + + /* */ + ACCESS_GET_MTU, /* arg1= int* cannot fail(0 if no sense)*/ + ACCESS_GET_PTS_DELAY, /* arg1= int64_t* cannot fail */ + /* */ + ACCESS_GET_TITLE_INFO, /* arg1=input_title_t*** arg2=int* can fail */ + /* Meta data */ + ACCESS_GET_META, /* arg1= vlc_meta_t ** res=can fail */ + + /* */ + ACCESS_SET_PAUSE_STATE, /* arg1= bool can fail */ + + /* */ + ACCESS_SET_TITLE, /* arg1= int can fail */ + ACCESS_SET_SEEKPOINT, /* arg1= int can fail */ + + /* Special mode for access/demux communication + * XXX: avoid to use it unless you can't */ + ACCESS_SET_PRIVATE_ID_STATE, /* arg1= int i_private_data, bool b_selected can fail */ + ACCESS_SET_PRIVATE_ID_CA, /* arg1= int i_program_number, uint16_t i_vpid, uint16_t i_apid1, uint16_t i_apid2, uint16_t i_apid3, uint8_t i_length, uint8_t *p_data */ + ACCESS_GET_PRIVATE_ID_STATE, /* arg1=int i_private_data arg2=bool * res=can fail */ + + ACCESS_GET_CONTENT_TYPE, /* arg1=char **ppsz_content_type */ +}; + +struct access_t +{ + VLC_COMMON_MEMBERS + + /* Module properties */ + module_t *p_module; + + /* Access name (empty if non forced) */ + char *psz_access; + /* Access path/url/filename/.... */ + char *psz_path; + /* Access source for access_filter (NULL for regular access) */ + access_t *p_source; + + /* Access can fill this entry to force a demuxer + * XXX: fill it once you know for sure you will succeed + * (if you fail, this value won't be reseted */ + char *psz_demux; + + /* pf_read/pf_block is used to read data. + * XXX A access should set one and only one of them */ + ssize_t (*pf_read) ( access_t *, uint8_t *, size_t ); /* Return -1 if no data yet, 0 if no more data, else real data read */ + block_t *(*pf_block)( access_t * ); /* return a block of data in his 'natural' size, NULL if not yet data or eof */ + + /* Called for each seek. + * XXX can be null */ + int (*pf_seek) ( access_t *, int64_t ); /* can be null if can't seek */ + + /* Used to retreive and configure the access + * XXX mandatory. look at access_query_e to know what query you *have to* support */ + int (*pf_control)( access_t *, int i_query, va_list args); + + /* Access has to maintain them uptodate */ + struct + { + unsigned int i_update; /* Access sets them on change, + Input removes them once take into account*/ + + int64_t i_size; /* Write only for access, read only for input */ + int64_t i_pos; /* idem */ + bool b_eof; /* idem */ + + int i_title; /* idem, start from 0 (could be menu) */ + int i_seekpoint;/* idem, start from 0 */ + + bool b_prebuffered; /* Read only for input */ + } info; + access_sys_t *p_sys; +}; + +static inline int access_vaControl( access_t *p_access, int i_query, va_list args ) +{ + if( !p_access ) return VLC_EGENERIC; + return p_access->pf_control( p_access, i_query, args ); +} + +static inline int access_Control( access_t *p_access, int i_query, ... ) +{ + va_list args; + int i_result; + + va_start( args, i_query ); + i_result = access_vaControl( p_access, i_query, args ); + va_end( args ); + return i_result; +} + +static inline char *access_GetContentType( access_t *p_access ) +{ + char *res; + if( access_Control( p_access, ACCESS_GET_CONTENT_TYPE, &res ) ) + return NULL; + return res; +} + +static inline void access_InitFields( access_t *p_a ) +{ + p_a->info.i_update = 0; + p_a->info.i_size = 0; + p_a->info.i_pos = 0; + p_a->info.b_eof = false; + p_a->info.i_title = 0; + p_a->info.i_seekpoint = 0; +} + +#define ACCESS_SET_CALLBACKS( read, block, control, seek ) \ + p_access->pf_read = read; \ + p_access->pf_block = block; \ + p_access->pf_control = control; \ + p_access->pf_seek = seek; \ + +#define STANDARD_READ_ACCESS_INIT \ + access_InitFields( p_access ); \ + ACCESS_SET_CALLBACKS( Read, NULL, Control, Seek ); \ + MALLOC_ERR( p_access->p_sys, access_sys_t ); \ + p_sys = p_access->p_sys; memset( p_sys, 0, sizeof( access_sys_t ) ); + +#define STANDARD_BLOCK_ACCESS_INIT \ + access_InitFields( p_access ); \ + ACCESS_SET_CALLBACKS( NULL, Block, Control, Seek ); \ + MALLOC_ERR( p_access->p_sys, access_sys_t ); \ + p_sys = p_access->p_sys; memset( p_sys, 0, sizeof( access_sys_t ) ); + +#endif diff --git a/VLC/vlc_acl.h b/VLC/vlc_acl.h new file mode 100644 index 0000000..5e1ebd5 --- /dev/null +++ b/VLC/vlc_acl.h @@ -0,0 +1,40 @@ +/***************************************************************************** + * vlc_acl.h: interface to the network Access Control List internal API + ***************************************************************************** + * Copyright (C) 2005 Rémi Denis-Courmont + * Copyright (C) 2005 the VideoLAN team + * $Id$ + * + * Authors: Rémi Denis-Courmont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_ACL_H +# define VLC_ACL_H + +#define ACL_Create(a, b) __ACL_Create(VLC_OBJECT(a), b) +#define ACL_Duplicate(a,b) __ACL_Duplicate(VLC_OBJECT(a),b) + +VLC_EXPORT( int, ACL_Check, ( vlc_acl_t *p_acl, const char *psz_ip ) ); +VLC_EXPORT( vlc_acl_t *, __ACL_Create, ( vlc_object_t *p_this, bool b_allow ) ); +VLC_EXPORT( vlc_acl_t *, __ACL_Duplicate, ( vlc_object_t *p_this, const vlc_acl_t *p_acl ) ); +VLC_EXPORT( void, ACL_Destroy, ( vlc_acl_t *p_acl ) ); + +#define ACL_AddHost(a,b,c) ACL_AddNet(a,b,-1,c) +VLC_EXPORT( int, ACL_AddNet, ( vlc_acl_t *p_acl, const char *psz_ip, int i_len, bool b_allow ) ); +VLC_EXPORT( int, ACL_LoadFile, ( vlc_acl_t *p_acl, const char *path ) ); + +#endif diff --git a/VLC/vlc_aout.h b/VLC/vlc_aout.h new file mode 100644 index 0000000..4e05c9f --- /dev/null +++ b/VLC/vlc_aout.h @@ -0,0 +1,413 @@ +/***************************************************************************** + * audio_output.h : audio output interface + ***************************************************************************** + * Copyright (C) 2002-2005 the VideoLAN team + * $Id$ + * + * Authors: Christophe Massiot + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_AOUT_H +#define VLC_AOUT_H 1 + +/** + * \file + * This file defines functions, structures and macros for audio output object + */ + +# ifdef __cplusplus +extern "C" { +# endif + +#include "vlc_es.h" + +#define AOUT_FMTS_IDENTICAL( p_first, p_second ) ( \ + ((p_first)->i_format == (p_second)->i_format) \ + && ((p_first)->i_rate == (p_second)->i_rate) \ + && ((p_first)->i_physical_channels == (p_second)->i_physical_channels)\ + && ((p_first)->i_original_channels == (p_second)->i_original_channels) ) + +/* Check if i_rate == i_rate and i_channels == i_channels */ +#define AOUT_FMTS_SIMILAR( p_first, p_second ) ( \ + ((p_first)->i_rate == (p_second)->i_rate) \ + && ((p_first)->i_physical_channels == (p_second)->i_physical_channels)\ + && ((p_first)->i_original_channels == (p_second)->i_original_channels) ) + +#ifdef WORDS_BIGENDIAN +# define AOUT_FMT_S16_NE VLC_FOURCC('s','1','6','b') +# define AOUT_FMT_U16_NE VLC_FOURCC('u','1','6','b') +# define AOUT_FMT_S24_NE VLC_FOURCC('s','2','4','b') +# define AOUT_FMT_S32_NE VLC_FOURCC('s','3','2','b') +# define AOUT_FMT_SPDIF_NE VLC_FOURCC('s','p','d','b') +#else +# define AOUT_FMT_S16_NE VLC_FOURCC('s','1','6','l') +# define AOUT_FMT_U16_NE VLC_FOURCC('u','1','6','l') +# define AOUT_FMT_S24_NE VLC_FOURCC('s','2','4','l') +# define AOUT_FMT_S32_NE VLC_FOURCC('s','3','2','l') +# define AOUT_FMT_SPDIF_NE VLC_FOURCC('s','p','d','i') +#endif + +#define AOUT_FMT_NON_LINEAR( p_format ) \ + ( ((p_format)->i_format == VLC_FOURCC('s','p','d','i')) \ + || ((p_format)->i_format == VLC_FOURCC('s','p','d','b')) \ + || ((p_format)->i_format == VLC_FOURCC('a','5','2',' ')) \ + || ((p_format)->i_format == VLC_FOURCC('d','t','s',' ')) ) + +/* This is heavily borrowed from libmad, by Robert Leslie */ +/* + * Fixed-point format: 0xABBBBBBB + * A == whole part (sign + 3 bits) + * B == fractional part (28 bits) + * + * Values are signed two's complement, so the effective range is: + * 0x80000000 to 0x7fffffff + * -8.0 to +7.9999999962747097015380859375 + * + * The smallest representable value is: + * 0x00000001 == 0.0000000037252902984619140625 (i.e. about 3.725e-9) + * + * 28 bits of fractional accuracy represent about + * 8.6 digits of decimal accuracy. + * + * Fixed-point numbers can be added or subtracted as normal + * integers, but multiplication requires shifting the 64-bit result + * from 56 fractional bits back to 28 (and rounding.) + */ +typedef int32_t vlc_fixed_t; +#define FIXED32_FRACBITS 28 +#define FIXED32_MIN ((vlc_fixed_t) -0x80000000L) +#define FIXED32_MAX ((vlc_fixed_t) +0x7fffffffL) +#define FIXED32_ONE ((vlc_fixed_t) 0x10000000) + +/* + * Channels descriptions + */ + +/* Values available for physical and original channels */ +#define AOUT_CHAN_CENTER 0x1 +#define AOUT_CHAN_LEFT 0x2 +#define AOUT_CHAN_RIGHT 0x4 +#define AOUT_CHAN_REARCENTER 0x10 +#define AOUT_CHAN_REARLEFT 0x20 +#define AOUT_CHAN_REARRIGHT 0x40 +#define AOUT_CHAN_MIDDLELEFT 0x100 +#define AOUT_CHAN_MIDDLERIGHT 0x200 +#define AOUT_CHAN_LFE 0x1000 + +/* Values available for original channels only */ +#define AOUT_CHAN_DOLBYSTEREO 0x10000 +#define AOUT_CHAN_DUALMONO 0x20000 +#define AOUT_CHAN_REVERSESTEREO 0x40000 + +#define AOUT_CHAN_PHYSMASK 0xFFFF +#define AOUT_CHAN_MAX 9 + +/* Values used for the audio-device and audio-channels object variables */ +#define AOUT_VAR_MONO 1 +#define AOUT_VAR_STEREO 2 +#define AOUT_VAR_2F2R 4 +#define AOUT_VAR_3F2R 5 +#define AOUT_VAR_5_1 6 +#define AOUT_VAR_6_1 7 +#define AOUT_VAR_7_1 8 +#define AOUT_VAR_SPDIF 10 + +#define AOUT_VAR_CHAN_STEREO 1 +#define AOUT_VAR_CHAN_RSTEREO 2 +#define AOUT_VAR_CHAN_LEFT 3 +#define AOUT_VAR_CHAN_RIGHT 4 +#define AOUT_VAR_CHAN_DOLBYS 5 + +/***************************************************************************** + * Main audio output structures + *****************************************************************************/ + +/** audio output buffer */ +struct aout_buffer_t +{ + uint8_t * p_buffer; + int i_alloc_type; + /* i_size is the real size of the buffer (used for debug ONLY), i_nb_bytes + * is the number of significative bytes in it. */ + size_t i_size, i_nb_bytes; + unsigned int i_nb_samples; + mtime_t start_date, end_date; + bool b_discontinuity; /* Set on discontinuity (for non pcm stream) */ + + struct aout_buffer_t * p_next; + + /** Private data (aout_buffer_t will disappear soon so no need for an + * aout_buffer_sys_t type) */ + void * p_sys; + + /** This way the release can be overloaded */ + void (*pf_release)( aout_buffer_t * ); +}; + +#define aout_BufferFree( p_buffer ) do { \ + if( p_buffer != NULL && (p_buffer)->i_alloc_type == AOUT_ALLOC_HEAP ) \ + { \ + free( p_buffer ); \ + } \ + p_buffer = NULL; } while(0) + +/* Size of a frame for S/PDIF output. */ +#define AOUT_SPDIF_SIZE 6144 + +/* Number of samples in an A/52 frame. */ +#define A52_FRAME_NB 1536 + +/* Max input rate factor (1/4 -> 4) */ +#define AOUT_MAX_INPUT_RATE (4) + +/** date incrementation helper structure without long-term + * rounding errors + */ +struct audio_date_t +{ + mtime_t date; + uint32_t i_divider; + uint32_t i_remainder; +}; + +/** allocation of memory in the audio output */ +typedef struct aout_alloc_t +{ + int i_alloc_type; + int i_bytes_per_sec; +} aout_alloc_t; + +#define AOUT_ALLOC_NONE 0 +#define AOUT_ALLOC_STACK 1 +#define AOUT_ALLOC_HEAP 2 + +/** audio output mixer */ +typedef struct aout_mixer_t +{ + audio_sample_format_t mixer; + aout_alloc_t output_alloc; + + module_t * p_module; + struct aout_mixer_sys_t * p_sys; + void (* pf_do_work)( struct aout_instance_t *, + struct aout_buffer_t * ); + + /** If b_error == 1, there is no mixer. */ + bool b_error; + /** Multiplier used to raise or lower the volume of the sound in + * software. Beware, this creates sound distortion and should be avoided + * as much as possible. This isn't available for non-float32 mixer. */ + float f_multiplier; +} aout_mixer_t; + +/** audio output buffer FIFO */ +struct aout_fifo_t +{ + aout_buffer_t * p_first; + aout_buffer_t ** pp_last; + audio_date_t end_date; +}; + +/** audio output filter */ +struct aout_filter_t +{ + VLC_COMMON_MEMBERS + + audio_sample_format_t input; + audio_sample_format_t output; + aout_alloc_t output_alloc; + + module_t * p_module; + struct aout_filter_sys_t * p_sys; + void (* pf_do_work)( struct aout_instance_t *, + struct aout_filter_t *, + struct aout_buffer_t *, + struct aout_buffer_t * ); + bool b_in_place; + bool b_continuity; +}; + +#define AOUT_RESAMPLING_NONE 0 +#define AOUT_RESAMPLING_UP 1 +#define AOUT_RESAMPLING_DOWN 2 +/** an input stream for the audio output */ +struct aout_input_t +{ + /* When this lock is taken, the pipeline cannot be changed by a + * third-party. */ + vlc_mutex_t lock; + + /* The input thread that spawned this input */ + input_thread_t *p_input_thread; + + audio_sample_format_t input; + aout_alloc_t input_alloc; + + /* pre-filters */ + aout_filter_t * pp_filters[AOUT_MAX_FILTERS]; + int i_nb_filters; + + aout_filter_t * p_playback_rate_filter; + + /* resamplers */ + aout_filter_t * pp_resamplers[AOUT_MAX_FILTERS]; + int i_nb_resamplers; + int i_resampling_type; + mtime_t i_resamp_start_date; + int i_resamp_start_drift; + + aout_fifo_t fifo; + + /* Mixer information */ + uint8_t * p_first_byte_to_mix; + audio_replay_gain_t replay_gain; + float f_multiplier; + + /* If b_restart == 1, the input pipeline will be re-created. */ + bool b_restart; + + /* If b_error == 1, there is no input pipeline. */ + bool b_error; + + /* Did we just change the output format? (expect buffer inconsistencies) */ + bool b_changed; + + /* last rate from input */ + int i_last_input_rate; + /* internal caching delay from input */ + int i_pts_delay; + /* desynchronisation delay request by the user */ + int i_desync; + +}; + +/** an output stream for the audio output */ +typedef struct aout_output_t +{ + audio_sample_format_t output; + /* Indicates whether the audio output is currently starving, to avoid + * printing a 1,000 "output is starving" messages. */ + bool b_starving; + + /* post-filters */ + aout_filter_t * pp_filters[AOUT_MAX_FILTERS]; + int i_nb_filters; + + aout_fifo_t fifo; + + struct module_t * p_module; + struct aout_sys_t * p_sys; + void (* pf_play)( aout_instance_t * ); + int (* pf_volume_get )( aout_instance_t *, audio_volume_t * ); + int (* pf_volume_set )( aout_instance_t *, audio_volume_t ); + int (* pf_volume_infos )( aout_instance_t *, audio_volume_t * ); + int i_nb_samples; + + /* Current volume for the output - it's just a placeholder, the plug-in + * may or may not use it. */ + audio_volume_t i_volume; + + /* If b_error == 1, there is no audio output pipeline. */ + bool b_error; +} aout_output_t; + +/** audio output thread descriptor */ +struct aout_instance_t +{ + VLC_COMMON_MEMBERS + + /* Locks : please note that if you need several of these locks, it is + * mandatory (to avoid deadlocks) to take them in the following order : + * mixer_lock, p_input->lock, output_fifo_lock, input_fifos_lock. + * --Meuuh */ + /* When input_fifos_lock is taken, none of the p_input->fifo structures + * can be read or modified by a third-party thread. */ + vlc_mutex_t input_fifos_lock; + /* When mixer_lock is taken, all decoder threads willing to mix a + * buffer must wait until it is released. The output pipeline cannot + * be modified. No input stream can be added or removed. */ + vlc_mutex_t mixer_lock; + /* When output_fifo_lock is taken, the p_aout->output.fifo structure + * cannot be read or written by a third-party thread. */ + vlc_mutex_t output_fifo_lock; + + /* Input streams & pre-filters */ + aout_input_t * pp_inputs[AOUT_MAX_INPUTS]; + int i_nb_inputs; + + /* Mixer */ + aout_mixer_t mixer; + + /* Output plug-in */ + aout_output_t output; +}; + +/***************************************************************************** + * Prototypes + *****************************************************************************/ + +/* From common.c : */ +VLC_EXPORT( void, aout_DateInit, ( audio_date_t *, uint32_t ) ); +VLC_EXPORT( void, aout_DateSet, ( audio_date_t *, mtime_t ) ); +VLC_EXPORT( void, aout_DateMove, ( audio_date_t *, mtime_t ) ); +VLC_EXPORT( mtime_t, aout_DateGet, ( const audio_date_t * ) ); +VLC_EXPORT( mtime_t, aout_DateIncrement, ( audio_date_t *, uint32_t ) ); + +VLC_EXPORT( aout_buffer_t *, aout_OutputNextBuffer, ( aout_instance_t *, mtime_t, bool ) ); + +VLC_EXPORT( int, aout_CheckChannelReorder, ( const uint32_t *, const uint32_t *, uint32_t, int, int * ) ); +VLC_EXPORT( void, aout_ChannelReorder, ( uint8_t *, int, int, const int *, int ) ); + +VLC_EXPORT( unsigned int, aout_FormatNbChannels, ( const audio_sample_format_t * p_format ) ); +VLC_EXPORT( unsigned int, aout_BitsPerSample, ( vlc_fourcc_t i_format ) ); +VLC_EXPORT( void, aout_FormatPrepare, ( audio_sample_format_t * p_format ) ); +VLC_EXPORT( void, aout_FormatPrint, ( aout_instance_t * p_aout, const char * psz_text, const audio_sample_format_t * p_format ) ); +VLC_EXPORT( const char *, aout_FormatPrintChannels, ( const audio_sample_format_t * ) ); + +VLC_EXPORT( mtime_t, aout_FifoFirstDate, ( aout_instance_t *, aout_fifo_t * ) ); +VLC_EXPORT( aout_buffer_t *, aout_FifoPop, ( aout_instance_t * p_aout, aout_fifo_t * p_fifo ) ); + +/* From intf.c : */ +VLC_EXPORT( void, aout_VolumeSoftInit, ( aout_instance_t * ) ); +VLC_EXPORT( void, aout_VolumeNoneInit, ( aout_instance_t * ) ); +#define aout_VolumeGet(a, b) __aout_VolumeGet(VLC_OBJECT(a), b) +VLC_EXPORT( int, __aout_VolumeGet, ( vlc_object_t *, audio_volume_t * ) ); +#define aout_VolumeSet(a, b) __aout_VolumeSet(VLC_OBJECT(a), b) +VLC_EXPORT( int, __aout_VolumeSet, ( vlc_object_t *, audio_volume_t ) ); +#define aout_VolumeInfos(a, b) __aout_VolumeInfos(VLC_OBJECT(a), b) +VLC_EXPORT( int, __aout_VolumeInfos, ( vlc_object_t *, audio_volume_t * ) ); +#define aout_VolumeUp(a, b, c) __aout_VolumeUp(VLC_OBJECT(a), b, c) +VLC_EXPORT( int, __aout_VolumeUp, ( vlc_object_t *, int, audio_volume_t * ) ); +#define aout_VolumeDown(a, b, c) __aout_VolumeDown(VLC_OBJECT(a), b, c) +VLC_EXPORT( int, __aout_VolumeDown, ( vlc_object_t *, int, audio_volume_t * ) ); +#define aout_VolumeMute(a, b) __aout_VolumeMute(VLC_OBJECT(a), b) +VLC_EXPORT( int, __aout_VolumeMute, ( vlc_object_t *, audio_volume_t * ) ); +VLC_EXPORT( int, aout_FindAndRestart, ( vlc_object_t *, const char *, vlc_value_t, vlc_value_t, void * ) ); +VLC_EXPORT( int, aout_ChannelsRestart, ( vlc_object_t *, const char *, vlc_value_t, vlc_value_t, void * ) ); + +VLC_EXPORT( void, aout_EnableFilter, (vlc_object_t *, const char *, bool )); + +#define aout_VisualNext(a) aout_VisualChange( VLC_OBJECT(a),1 ) +#define aout_VisualPrev(a) aout_VisualChange( VLC_OBJECT(a),-1 ) + +VLC_EXPORT( char *, aout_VisualChange, (vlc_object_t *, int ) ); + +# ifdef __cplusplus +} +# endif + +#endif /* _VLC_AOUT_H */ diff --git a/VLC/vlc_arrays.h b/VLC/vlc_arrays.h new file mode 100644 index 0000000..faf69d4 --- /dev/null +++ b/VLC/vlc_arrays.h @@ -0,0 +1,593 @@ +/***************************************************************************** + * vlc_arrays.h : Arrays and data structures handling + ***************************************************************************** + * Copyright (C) 1999-2004 the VideoLAN team + * $Id$ + * + * Authors: Samuel Hocevar + * Clément Stenac + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ + +#ifndef VLC_ARRAYS_H_ +#define VLC_ARRAYS_H_ + +/** + * \file + * This file defines functions, structures and macros for handling arrays in vlc + */ + +/** + * Simple dynamic array handling. Array is realloced at each insert/removal + */ +#if defined( _MSC_VER ) && _MSC_VER < 1300 && !defined( UNDER_CE ) +# define VLCCVP (void**) /* Work-around for broken compiler */ +#else +# define VLCCVP +#endif +#define INSERT_ELEM( p_ar, i_oldsize, i_pos, elem ) \ + do \ + { \ + if( !i_oldsize ) (p_ar) = NULL; \ + (p_ar) = VLCCVP realloc( p_ar, ((i_oldsize) + 1) * sizeof(*(p_ar)) ); \ + if( (i_oldsize) - (i_pos) ) \ + { \ + memmove( (p_ar) + (i_pos) + 1, (p_ar) + (i_pos), \ + ((i_oldsize) - (i_pos)) * sizeof( *(p_ar) ) ); \ + } \ + (p_ar)[i_pos] = elem; \ + (i_oldsize)++; \ + } \ + while( 0 ) + +#define REMOVE_ELEM( p_ar, i_oldsize, i_pos ) \ + do \ + { \ + if( (i_oldsize) - (i_pos) - 1 ) \ + { \ + memmove( (p_ar) + (i_pos), \ + (p_ar) + (i_pos) + 1, \ + ((i_oldsize) - (i_pos) - 1) * sizeof( *(p_ar) ) ); \ + } \ + if( i_oldsize > 1 ) \ + { \ + (p_ar) = realloc( p_ar, ((i_oldsize) - 1) * sizeof( *(p_ar) ) ); \ + } \ + else \ + { \ + free( p_ar ); \ + (p_ar) = NULL; \ + } \ + (i_oldsize)--; \ + } \ + while( 0 ) + +#define TAB_INIT( count, tab ) \ + do { \ + (count) = 0; \ + (tab) = NULL; \ + } while(0) + +#define TAB_CLEAN( count, tab ) \ + do { \ + free( tab ); \ + (count)= 0; \ + (tab)= NULL; \ + } while(0) + +#define TAB_APPEND_CAST( cast, count, tab, p ) \ + do { \ + if( (count) > 0 ) \ + (tab) = cast realloc( tab, sizeof( void ** ) * ( (count) + 1 ) ); \ + else \ + (tab) = cast malloc( sizeof( void ** ) ); \ + (tab)[count] = (p); \ + (count)++; \ + } while(0) + +#define TAB_APPEND( count, tab, p ) \ + TAB_APPEND_CAST( , count, tab, p ) +#define TAB_APPEND_CPP( type, count, tab, p ) \ + TAB_APPEND_CAST( (type**), count, tab, p ) + +#define TAB_FIND( count, tab, p, index ) \ + do { \ + int _i_; \ + (index) = -1; \ + for( _i_ = 0; _i_ < (count); _i_++ ) \ + { \ + if( (tab)[_i_] == (p) ) \ + { \ + (index) = _i_; \ + break; \ + } \ + } \ + } while(0) + + +#define TAB_REMOVE( count, tab, p ) \ + do { \ + int _i_index_; \ + TAB_FIND( count, tab, p, _i_index_ ); \ + if( _i_index_ >= 0 ) \ + { \ + if( (count) > 1 ) \ + { \ + memmove( ((void**)(tab) + _i_index_), \ + ((void**)(tab) + _i_index_+1), \ + ( (count) - _i_index_ - 1 ) * sizeof( void* ) );\ + } \ + (count)--; \ + if( (count) == 0 ) \ + { \ + free( tab ); \ + (tab) = NULL; \ + } \ + } \ + } while(0) + +#define TAB_INSERT_CAST( cast, count, tab, p, index ) do { \ + if( (count) > 0 ) \ + (tab) = cast realloc( tab, sizeof( void ** ) * ( (count) + 1 ) ); \ + else \ + (tab) = cast malloc( sizeof( void ** ) ); \ + if( (count) - (index) > 0 ) \ + memmove( (void**)(tab) + (index) + 1, \ + (void**)(tab) + (index), \ + ((count) - (index)) * sizeof(*(tab)) );\ + (tab)[(index)] = (p); \ + (count)++; \ +} while(0) + +#define TAB_INSERT( count, tab, p, index ) \ + TAB_INSERT_CAST( , count, tab, p, index ) + +/** + * Binary search in a sorted array. The key must be comparable by < and > + * \param entries array of entries + * \param count number of entries + * \param elem key to check within an entry (like .id, or ->i_id) + * \param zetype type of the key + * \param key value of the key + * \param answer index of answer within the array. -1 if not found + */ +#define BSEARCH( entries, count, elem, zetype, key, answer ) \ + do { \ + int low = 0, high = count - 1; \ + answer = -1; \ + while( low <= high ) {\ + int mid = (low + high ) / 2; /* Just don't care about 2^30 tables */ \ + zetype mid_val = entries[mid] elem;\ + if( mid_val < key ) \ + low = mid + 1; \ + else if ( mid_val > key ) \ + high = mid -1; \ + else \ + { \ + answer = mid; break; \ + }\ + } \ + } while(0) + + +/************************************************************************ + * Dynamic arrays with progressive allocation + ************************************************************************/ + +/* Internal functions */ +#define _ARRAY_ALLOC(array, newsize) { \ + array.i_alloc = newsize; \ + array.p_elems = VLCCVP realloc( array.p_elems, array.i_alloc * \ + sizeof(*array.p_elems) ); \ +} + +#define _ARRAY_GROW1(array) { \ + if( array.i_alloc < 10 ) \ + _ARRAY_ALLOC(array, 10 ) \ + else if( array.i_alloc == array.i_size ) \ + _ARRAY_ALLOC(array, (int)(array.i_alloc * 1.5) ) \ +} + +#define _ARRAY_GROW(array,additional) { \ + int i_first = array.i_alloc; \ + while( array.i_alloc - i_first < additional ) \ + { \ + if( array.i_alloc < 10 ) \ + _ARRAY_ALLOC(array, 10 ) \ + else if( array.i_alloc == array.i_size ) \ + _ARRAY_ALLOC(array, (int)(array.i_alloc * 1.5) ) \ + else break; \ + } \ +} + +#define _ARRAY_SHRINK(array) { \ + if( array.i_size > 10 && array.i_size < (int)(array.i_alloc / 1.5) ) { \ + _ARRAY_ALLOC(array, array.i_size + 5); \ + } \ +} + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +/* API */ +#define DECL_ARRAY(type) struct { \ + int i_alloc; \ + int i_size; \ + type *p_elems; \ +} + +#define TYPEDEF_ARRAY(type, name) typedef DECL_ARRAY(type) name; + +#define ARRAY_INIT(array) \ + array.i_alloc = 0; \ + array.i_size = 0; \ + array.p_elems = NULL; + +#define ARRAY_RESET(array) \ + array.i_alloc = 0; \ + array.i_size = 0; \ + free( array.p_elems ); array.p_elems = NULL; + +#define ARRAY_APPEND(array, elem) { \ + _ARRAY_GROW1(array); \ + array.p_elems[array.i_size] = elem; \ + array.i_size++; \ +} + +#define ARRAY_INSERT(array,elem,pos) { \ + _ARRAY_GROW1(array); \ + if( array.i_size - pos ) { \ + memmove( array.p_elems + pos + 1, array.p_elems + pos, \ + (array.i_size-pos) * sizeof(*array.p_elems) ); \ + } \ + array.p_elems[pos] = elem; \ + array.i_size++; \ +} + +#define ARRAY_REMOVE(array,pos) { \ + if( array.i_size - (pos) - 1 ) \ + { \ + memmove( array.p_elems + pos, array.p_elems + pos + 1, \ + ( array.i_size - pos - 1 ) *sizeof(*array.p_elems) ); \ + } \ + array.i_size--; \ + _ARRAY_SHRINK(array); \ +} + +#define ARRAY_VAL(array, pos) array.p_elems[pos] + +#define ARRAY_BSEARCH(array, elem, zetype, key, answer) \ + BSEARCH( array.p_elems, array.i_size, elem, zetype, key, answer) + +#define FOREACH_ARRAY( item, array ) { \ + int fe_idx; \ + for( fe_idx = 0 ; fe_idx < array.i_size ; fe_idx++ ) \ + { \ + item = array.p_elems[fe_idx]; + +#define FOREACH_END() } } + + +/************************************************************************ + * Dynamic arrays with progressive allocation (Preferred API) + ************************************************************************/ +typedef struct vlc_array_t +{ + int i_count; + void ** pp_elems; +} vlc_array_t; + +static inline void vlc_array_init( vlc_array_t * p_array ) +{ + memset( p_array, 0, sizeof(vlc_array_t) ); +} + +static inline void vlc_array_clear( vlc_array_t * p_array ) +{ + free( p_array->pp_elems ); + memset( p_array, 0, sizeof(vlc_array_t) ); +} + +static inline vlc_array_t * vlc_array_new( void ) +{ + vlc_array_t * ret = (vlc_array_t *)malloc( sizeof(vlc_array_t) ); + if( ret ) vlc_array_init( ret ); + return ret; +} + +static inline void vlc_array_destroy( vlc_array_t * p_array ) +{ + if( !p_array ) + return; + vlc_array_clear( p_array ); + free( p_array ); +} + + +/* Read */ +static inline int +vlc_array_count( vlc_array_t * p_array ) +{ + return p_array->i_count; +} + +static inline void * +vlc_array_item_at_index( vlc_array_t * p_array, int i_index ) +{ + return p_array->pp_elems[i_index]; +} + +static inline int +vlc_array_index_of_item( vlc_array_t * p_array, void * item ) +{ + int i; + for( i = 0; i < p_array->i_count; i++) + { + if( p_array->pp_elems[i] == item ) + return i; + } + return -1; +} + +/* Write */ +static inline void +vlc_array_insert( vlc_array_t * p_array, void * p_elem, int i_index ) +{ + TAB_INSERT_CAST( (void **), p_array->i_count, p_array->pp_elems, p_elem, i_index ); +} + +static inline void +vlc_array_append( vlc_array_t * p_array, void * p_elem ) +{ + vlc_array_insert( p_array, p_elem, p_array->i_count ); +} + +static inline void +vlc_array_remove( vlc_array_t * p_array, int i_index ) +{ + if( i_index >= 0 ) + { + if( p_array->i_count > 1 ) + { + memmove( p_array->pp_elems + i_index, + p_array->pp_elems + i_index+1, + ( p_array->i_count - i_index - 1 ) * sizeof( void* ) ); + } + p_array->i_count--; + if( p_array->i_count == 0 ) + { + free( p_array->pp_elems ); + p_array->pp_elems = NULL; + } + } +} + + +/************************************************************************ + * Dictionaries + ************************************************************************/ + +/* This function is not intended to be crypto-secure, we only want it to be + * fast and not suck too much. This one is pretty fast and did 0 collisions + * in wenglish's dictionary. + */ +static inline uint64_t DictHash( const char *psz_string, int hashsize ) +{ + uint64_t i_hash = 0; + if( psz_string ) + { + while( *psz_string ) + { + i_hash += *psz_string++; + i_hash += i_hash << 10; + i_hash ^= i_hash >> 8; + } + } + return i_hash % hashsize; +} + +struct vlc_dictionary_entry_t +{ + char * psz_key; + void * p_value; + struct vlc_dictionary_entry_t * p_next; +}; + +typedef struct vlc_dictionary_t +{ + int i_size; + struct vlc_dictionary_entry_t ** p_entries; +} vlc_dictionary_t; + +static void * const kVLCDictionaryNotFound = NULL; + +static inline void vlc_dictionary_init( vlc_dictionary_t * p_dict, int i_size ) +{ + if( i_size > 0 ) + { + p_dict->p_entries = (struct vlc_dictionary_entry_t **)malloc(sizeof(struct vlc_dictionary_entry_t *) * i_size); + memset( p_dict->p_entries, 0, sizeof(struct vlc_dictionary_entry_t *) * i_size ); + } + else + p_dict->p_entries = NULL; + p_dict->i_size = i_size; +} + +static inline void vlc_dictionary_clear( vlc_dictionary_t * p_dict ) +{ + int i; + struct vlc_dictionary_entry_t * p_current, * p_next; + if( p_dict->p_entries ) + { + for( i = 0; i < p_dict->i_size; i++ ) + { + p_current = p_dict->p_entries[i]; + while( p_current ) + { + p_next = p_current->p_next; + free( p_current->psz_key ); + free( p_current ); + p_current = p_next; + } + } + free( p_dict->p_entries ); + } + p_dict->i_size = 0; +} + + + +static inline void * +vlc_dictionary_value_for_key( const vlc_dictionary_t * p_dict, const char * psz_key ) +{ + if( !p_dict->p_entries ) + return kVLCDictionaryNotFound; + + int i_pos = DictHash( psz_key, p_dict->i_size ); + struct vlc_dictionary_entry_t * p_entry = p_dict->p_entries[i_pos]; + + if( !p_entry ) + return kVLCDictionaryNotFound; + + /* Make sure we return the right item. (Hash collision) */ + do { + if( !strcmp( psz_key, p_entry->psz_key ) ) + return p_entry->p_value; + p_entry = p_entry->p_next; + } while( p_entry ); + + return kVLCDictionaryNotFound; +} + +static inline int +vlc_dictionary_keys_count( const vlc_dictionary_t * p_dict ) +{ + struct vlc_dictionary_entry_t * p_entry; + int i, count = 0; + + if( !p_dict->p_entries ) + return 0; + + for( i = 0; i < p_dict->i_size; i++ ) + { + for( p_entry = p_dict->p_entries[i]; p_entry; p_entry = p_entry->p_next ) count++; + } + return count; +} + +static inline char ** +vlc_dictionary_all_keys( const vlc_dictionary_t * p_dict ) +{ + struct vlc_dictionary_entry_t * p_entry; + char ** ppsz_ret; + int i, count = vlc_dictionary_keys_count( p_dict ); + + ppsz_ret = (char**)malloc(sizeof(char *) * (count + 1)); + + count = 0; + for( i = 0; i < p_dict->i_size; i++ ) + { + for( p_entry = p_dict->p_entries[i]; p_entry; p_entry = p_entry->p_next ) + ppsz_ret[count++] = strdup( p_entry->psz_key ); + } + ppsz_ret[count] = NULL; + return ppsz_ret; +} + +static inline void +__vlc_dictionary_insert( vlc_dictionary_t * p_dict, const char * psz_key, + void * p_value, bool rebuild ) +{ + if( !p_dict->p_entries ) + vlc_dictionary_init( p_dict, 1 ); + + int i_pos = DictHash( psz_key, p_dict->i_size ); + struct vlc_dictionary_entry_t * p_entry; + + p_entry = (struct vlc_dictionary_entry_t *)malloc(sizeof(struct vlc_dictionary_entry_t)); + p_entry->psz_key = strdup( psz_key ); + p_entry->p_value = p_value; + p_entry->p_next = p_dict->p_entries[i_pos]; + p_dict->p_entries[i_pos] = p_entry; + if( rebuild ) + { + /* Count how many items there was */ + int count; + for( count = 1; p_entry->p_next; count++ ) p_entry = p_entry->p_next; + if( count > 3 ) /* XXX: this need tuning */ + { + /* Here it starts to be not good, rebuild a bigger dictionary */ + struct vlc_dictionary_t new_dict; + int i_new_size = ( (p_dict->i_size+2) * 3) / 2; /* XXX: this need tuning */ + int i; + vlc_dictionary_init( &new_dict, i_new_size ); + for( i = 0; i < p_dict->i_size; i++ ) + { + p_entry = p_dict->p_entries[i]; + while( p_entry ) + { + __vlc_dictionary_insert( &new_dict, p_entry->psz_key, + p_entry->p_value, + 0 /* To avoid multiple rebuild loop */); + p_entry = p_entry->p_next; + } + } + + vlc_dictionary_clear( p_dict ); + p_dict->i_size = new_dict.i_size; + p_dict->p_entries = new_dict.p_entries; + } + } +} + +static inline void +vlc_dictionary_insert( vlc_dictionary_t * p_dict, const char * psz_key, void * p_value ) +{ + __vlc_dictionary_insert( p_dict, psz_key, p_value, 1 ); +} + +static inline void +vlc_dictionary_remove_value_for_key( const vlc_dictionary_t * p_dict, const char * psz_key ) +{ + if( !p_dict->p_entries ) + return; + + int i_pos = DictHash( psz_key, p_dict->i_size ); + struct vlc_dictionary_entry_t * p_entry = p_dict->p_entries[i_pos]; + struct vlc_dictionary_entry_t * p_prev; + + if( !p_entry ) + return; /* Not found, nothing to do */ + + /* Hash collision */ + p_prev = NULL; + do { + if( !strcmp( psz_key, p_entry->psz_key ) ) + { + if( !p_prev ) + p_dict->p_entries[i_pos] = p_entry->p_next; + else + p_prev->p_next = p_entry->p_next; + free( p_entry->psz_key ); + free( p_entry ); + return; + } + p_prev = p_entry; + p_entry = p_entry->p_next; + } while( p_entry ); + + /* No key was found */ +} + +#endif diff --git a/VLC/vlc_bits.h b/VLC/vlc_bits.h new file mode 100644 index 0000000..4dcfa2f --- /dev/null +++ b/VLC/vlc_bits.h @@ -0,0 +1,195 @@ +/***************************************************************************** + * bits.h : Bit handling helpers + ***************************************************************************** + * Copyright (C) 2003 the VideoLAN team + * $Id$ + * + * Authors: Laurent Aimar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_BITS_H +#define VLC_BITS_H 1 + +/** + * \file + * This file defines functions, structures for handling streams of bits in vlc + */ + +typedef struct bs_s +{ + uint8_t *p_start; + uint8_t *p; + uint8_t *p_end; + + int i_left; /* i_count number of available bits */ +} bs_t; + +static inline void bs_init( bs_t *s, void *p_data, int i_data ) +{ + s->p_start = p_data; + s->p = p_data; + s->p_end = s->p + i_data; + s->i_left = 8; +} + +static inline int bs_pos( bs_t *s ) +{ + return( 8 * ( s->p - s->p_start ) + 8 - s->i_left ); +} + +static inline int bs_eof( bs_t *s ) +{ + return( s->p >= s->p_end ? 1: 0 ); +} + +static inline uint32_t bs_read( bs_t *s, int i_count ) +{ + static const uint32_t i_mask[33] = + { 0x00, + 0x01, 0x03, 0x07, 0x0f, + 0x1f, 0x3f, 0x7f, 0xff, + 0x1ff, 0x3ff, 0x7ff, 0xfff, + 0x1fff, 0x3fff, 0x7fff, 0xffff, + 0x1ffff, 0x3ffff, 0x7ffff, 0xfffff, + 0x1fffff, 0x3fffff, 0x7fffff, 0xffffff, + 0x1ffffff, 0x3ffffff, 0x7ffffff, 0xfffffff, + 0x1fffffff,0x3fffffff,0x7fffffff,0xffffffff}; + int i_shr; + uint32_t i_result = 0; + + while( i_count > 0 ) + { + if( s->p >= s->p_end ) + { + break; + } + + if( ( i_shr = s->i_left - i_count ) >= 0 ) + { + /* more in the buffer than requested */ + i_result |= ( *s->p >> i_shr )&i_mask[i_count]; + s->i_left -= i_count; + if( s->i_left == 0 ) + { + s->p++; + s->i_left = 8; + } + return( i_result ); + } + else + { + /* less in the buffer than requested */ + i_result |= (*s->p&i_mask[s->i_left]) << -i_shr; + i_count -= s->i_left; + s->p++; + s->i_left = 8; + } + } + + return( i_result ); +} + +static inline uint32_t bs_read1( bs_t *s ) +{ + if( s->p < s->p_end ) + { + unsigned int i_result; + + s->i_left--; + i_result = ( *s->p >> s->i_left )&0x01; + if( s->i_left == 0 ) + { + s->p++; + s->i_left = 8; + } + return i_result; + } + + return 0; +} + +static inline uint32_t bs_show( bs_t *s, int i_count ) +{ + bs_t s_tmp = *s; + return bs_read( &s_tmp, i_count ); +} + +static inline void bs_skip( bs_t *s, int i_count ) +{ + s->i_left -= i_count; + + while( s->i_left <= 0 ) + { + s->p++; + s->i_left += 8; + } +} + +static inline void bs_write( bs_t *s, int i_count, uint32_t i_bits ) +{ + while( i_count > 0 ) + { + if( s->p >= s->p_end ) + { + break; + } + + i_count--; + + if( ( i_bits >> i_count )&0x01 ) + { + *s->p |= 1 << ( s->i_left - 1 ); + } + else + { + *s->p &= ~( 1 << ( s->i_left - 1 ) ); + } + s->i_left--; + if( s->i_left == 0 ) + { + s->p++; + s->i_left = 8; + } + } +} + +static inline void bs_align( bs_t *s ) +{ + if( s->i_left != 8 ) + { + s->i_left = 8; + s->p++; + } +} + +static inline void bs_align_0( bs_t *s ) +{ + if( s->i_left != 8 ) + { + bs_write( s, s->i_left, 0 ); + } +} + +static inline void bs_align_1( bs_t *s ) +{ + while( s->i_left != 8 ) + { + bs_write( s, 1, 1 ); + } +} + +#endif diff --git a/VLC/vlc_block.h b/VLC/vlc_block.h new file mode 100644 index 0000000..a17530e --- /dev/null +++ b/VLC/vlc_block.h @@ -0,0 +1,275 @@ +/***************************************************************************** + * vlc_block.h: Data blocks management functions + ***************************************************************************** + * Copyright (C) 2003 the VideoLAN team + * $Id$ + * + * Authors: Laurent Aimar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_BLOCK_H +#define VLC_BLOCK_H 1 + +/** + * \file + * This file implements functions and structures to handle blocks of data in vlc + * + */ + +/**************************************************************************** + * block: + **************************************************************************** + * - block_sys_t is opaque and thus block_t->p_sys is PRIVATE + * - i_flags may not always be set (ie could be 0, even for a key frame + * it depends where you receive the buffer (before/after a packetizer + * and the demux/packetizer implementations. + * - i_dts/i_pts could be 0, it means no pts + * - i_length: length in microseond of the packet, can be null except in the + * sout where it is mandatory. + * - i_rate 0 or a valid input rate, look at vlc_input.h + * + * - i_buffer number of valid data pointed by p_buffer + * you can freely decrease it but never increase it yourself + * (use block_Realloc) + * - p_buffer: pointer over datas. You should never overwrite it, you can + * only incremment it to skip datas, in others cases use block_Realloc + * (don't duplicate yourself in a bigger buffer, block_Realloc is + * optimised for prehader/postdatas increase) + ****************************************************************************/ +typedef struct block_sys_t block_sys_t; + +/** The content doesn't follow the last block, or is probably broken */ +#define BLOCK_FLAG_DISCONTINUITY 0x0001 +/** Intra frame */ +#define BLOCK_FLAG_TYPE_I 0x0002 +/** Inter frame with backward reference only */ +#define BLOCK_FLAG_TYPE_P 0x0004 +/** Inter frame with backward and forward reference */ +#define BLOCK_FLAG_TYPE_B 0x0008 +/** For inter frame when you don't know the real type */ +#define BLOCK_FLAG_TYPE_PB 0x0010 +/** Warn that this block is a header one */ +#define BLOCK_FLAG_HEADER 0x0020 +/** This is the last block of the frame */ +#define BLOCK_FLAG_END_OF_FRAME 0x0040 +/** This is not a key frame for bitrate shaping */ +#define BLOCK_FLAG_NO_KEYFRAME 0x0080 +/** This block contains the last part of a sequence */ +#define BLOCK_FLAG_END_OF_SEQUENCE 0x0100 +/** This block contains a clock reference */ +#define BLOCK_FLAG_CLOCK 0x0200 +/** This block is scrambled */ +#define BLOCK_FLAG_SCRAMBLED 0x0400 +/** This block has to be decoded but not be displayed */ +#define BLOCK_FLAG_PREROLL 0x0800 +/** This block is corrupted and/or there is data loss */ +#define BLOCK_FLAG_CORRUPTED 0x1000 + +#define BLOCK_FLAG_PRIVATE_MASK 0xffff0000 +#define BLOCK_FLAG_PRIVATE_SHIFT 16 + +typedef void (*block_free_t) (block_t *); + +struct block_t +{ + block_t *p_next; + block_t *p_prev; + + uint32_t i_flags; + + mtime_t i_pts; + mtime_t i_dts; + mtime_t i_length; + + int i_samples; /* Used for audio */ + int i_rate; + + size_t i_buffer; + uint8_t *p_buffer; + + /* Rudimentary support for overloading block (de)allocation. */ + block_free_t pf_release; +}; + +/**************************************************************************** + * Blocks functions: + **************************************************************************** + * - block_Alloc : create a new block with the requested size ( >= 0 ), return + * NULL for failure. + * - block_Release : release a block allocated with block_Alloc. + * - block_Realloc : realloc a block, + * i_pre: how many bytes to insert before body if > 0, else how many + * bytes of body to skip (the latter can be done without using + * block_Realloc i_buffer -= -i_pre, p_buffer += -i_pre as i_pre < 0) + * i_body (>= 0): the final size of the body (decreasing it can directly + * be done with i_buffer = i_body). + * with preheader and or body (increase + * and decrease are supported). Use it as it is optimised. + * - block_Duplicate : create a copy of a block. + ****************************************************************************/ +VLC_EXPORT( void, block_Init, ( block_t *, void *, size_t ) ); +VLC_EXPORT( block_t *, block_Alloc, ( size_t ) ); +VLC_EXPORT( block_t *, block_Realloc, ( block_t *, ssize_t i_pre, size_t i_body ) ); + +#define block_New( dummy, size ) block_Alloc(size) + +static inline block_t *block_Duplicate( block_t *p_block ) +{ + block_t *p_dup = block_Alloc( p_block->i_buffer ); + if( p_dup == NULL ) + return NULL; + + p_dup->i_dts = p_block->i_dts; + p_dup->i_pts = p_block->i_pts; + p_dup->i_flags = p_block->i_flags; + p_dup->i_length = p_block->i_length; + p_dup->i_rate = p_block->i_rate; + p_dup->i_samples = p_block->i_samples; + memcpy( p_dup->p_buffer, p_block->p_buffer, p_block->i_buffer ); + + return p_dup; +} + +static inline void block_Release( block_t *p_block ) +{ + p_block->pf_release( p_block ); +} + +VLC_EXPORT( block_t *, block_mmap_Alloc, (void *addr, size_t length) ); +VLC_EXPORT( block_t *, block_File, (int fd) ); + +/**************************************************************************** + * Chains of blocks functions helper + **************************************************************************** + * - block_ChainAppend : append a block to the last block of a chain. Try to + * avoid using with a lot of data as it's really slow, prefer + * block_ChainLastAppend + * - block_ChainLastAppend : use a pointer over a pointer to the next blocks, + * and update it. + * - block_ChainRelease : release a chain of block + * - block_ChainExtract : extract data from a chain, return real bytes counts + * - block_ChainGather : gather a chain, free it and return one block. + ****************************************************************************/ +static inline void block_ChainAppend( block_t **pp_list, block_t *p_block ) +{ + if( *pp_list == NULL ) + { + *pp_list = p_block; + } + else + { + block_t *p = *pp_list; + + while( p->p_next ) p = p->p_next; + p->p_next = p_block; + } +} + +static inline void block_ChainLastAppend( block_t ***ppp_last, block_t *p_block ) +{ + block_t *p_last = p_block; + + **ppp_last = p_block; + + while( p_last->p_next ) p_last = p_last->p_next; + *ppp_last = &p_last->p_next; +} + +static inline void block_ChainRelease( block_t *p_block ) +{ + while( p_block ) + { + block_t *p_next = p_block->p_next; + block_Release( p_block ); + p_block = p_next; + } +} + +static size_t block_ChainExtract( block_t *p_list, void *p_data, size_t i_max ) +{ + size_t i_total = 0; + uint8_t *p = (uint8_t*)p_data; + + while( p_list && i_max ) + { + size_t i_copy = __MIN( i_max, p_list->i_buffer ); + memcpy( p, p_list->p_buffer, i_copy ); + i_max -= i_copy; + i_total += i_copy; + p += i_copy; + + p_list = p_list->p_next; + } + return i_total; +} + +static inline block_t *block_ChainGather( block_t *p_list ) +{ + size_t i_total = 0; + mtime_t i_length = 0; + block_t *b, *g; + + if( p_list->p_next == NULL ) + return p_list; /* Already gathered */ + + for( b = p_list; b != NULL; b = b->p_next ) + { + i_total += b->i_buffer; + i_length += b->i_length; + } + + g = block_Alloc( i_total ); + block_ChainExtract( p_list, g->p_buffer, g->i_buffer ); + + g->i_flags = p_list->i_flags; + g->i_pts = p_list->i_pts; + g->i_dts = p_list->i_dts; + g->i_length = i_length; + + /* free p_list */ + block_ChainRelease( p_list ); + return g; +} + +/**************************************************************************** + * Fifos of blocks. + **************************************************************************** + * - block_FifoNew : create and init a new fifo + * - block_FifoRelease : destroy a fifo and free all blocks in it. + * - block_FifoEmpty : free all blocks in a fifo + * - block_FifoPut : put a block + * - block_FifoGet : get a packet from the fifo (and wait if it is empty) + * - block_FifoShow : show the first packet of the fifo (and wait if + * needed), be carefull, you can use it ONLY if you are sure to be the + * only one getting data from the fifo. + * - block_FifoCount : how many packets are waiting in the fifo + * - block_FifoSize : how many cumulated bytes are waiting in the fifo + * - block_FifoWake : wake ups a thread with block_FifoGet() = NULL + * (this is used to wakeup a thread when there is no data to queue) + ****************************************************************************/ + +VLC_EXPORT( block_fifo_t *, block_FifoNew, ( void ) ); +VLC_EXPORT( void, block_FifoRelease, ( block_fifo_t * ) ); +VLC_EXPORT( void, block_FifoEmpty, ( block_fifo_t * ) ); +VLC_EXPORT( size_t, block_FifoPut, ( block_fifo_t *, block_t * ) ); +VLC_EXPORT( void, block_FifoWake, ( block_fifo_t * ) ); +VLC_EXPORT( block_t *, block_FifoGet, ( block_fifo_t * ) ); +VLC_EXPORT( block_t *, block_FifoShow, ( block_fifo_t * ) ); +VLC_EXPORT( size_t, block_FifoSize, ( const block_fifo_t *p_fifo ) ); +VLC_EXPORT( size_t, block_FifoCount, ( const block_fifo_t *p_fifo ) ); + +#endif /* VLC_BLOCK_H */ diff --git a/VLC/vlc_block_helper.h b/VLC/vlc_block_helper.h new file mode 100644 index 0000000..679d0e5 --- /dev/null +++ b/VLC/vlc_block_helper.h @@ -0,0 +1,509 @@ +/***************************************************************************** + * vlc_block_helper.h: Helper functions for data blocks management. + ***************************************************************************** + * Copyright (C) 2003 the VideoLAN team + * $Id$ + * + * Authors: Gildas Bazin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_BLOCK_HELPER_H +#define VLC_BLOCK_HELPER_H 1 + +#include + +typedef struct block_bytestream_t +{ + block_t *p_chain; + block_t *p_block; + size_t i_offset; + +} block_bytestream_t; + +/***************************************************************************** + * block_bytestream_t management + *****************************************************************************/ +static inline block_bytestream_t block_BytestreamInit( void ) +{ + block_bytestream_t bytestream; + + bytestream.i_offset = 0; + bytestream.p_chain = bytestream.p_block = NULL; + + return bytestream; +} + +static inline void block_BytestreamRelease( block_bytestream_t *p_bytestream ) +{ + while( p_bytestream->p_chain ) + { + block_t *p_next; + p_next = p_bytestream->p_chain->p_next; + p_bytestream->p_chain->pf_release( p_bytestream->p_chain ); + p_bytestream->p_chain = p_next; + } + p_bytestream->i_offset = 0; + p_bytestream->p_chain = p_bytestream->p_block = NULL; +} + +static inline void block_BytestreamFlush( block_bytestream_t *p_bytestream ) +{ + while( p_bytestream->p_chain != p_bytestream->p_block ) + { + block_t *p_next; + p_next = p_bytestream->p_chain->p_next; + p_bytestream->p_chain->pf_release( p_bytestream->p_chain ); + p_bytestream->p_chain = p_next; + } + while( p_bytestream->p_block && + (p_bytestream->p_block->i_buffer - p_bytestream->i_offset) == 0 ) + { + block_t *p_next; + p_next = p_bytestream->p_chain->p_next; + p_bytestream->p_chain->pf_release( p_bytestream->p_chain ); + p_bytestream->p_chain = p_bytestream->p_block = p_next; + p_bytestream->i_offset = 0; + } +} + +static inline void block_BytestreamPush( block_bytestream_t *p_bytestream, + block_t *p_block ) +{ + block_ChainAppend( &p_bytestream->p_chain, p_block ); + if( !p_bytestream->p_block ) p_bytestream->p_block = p_block; +} + +static inline block_t *block_BytestreamPop( block_bytestream_t *p_bytestream ) +{ + block_t *p_block; + + block_BytestreamFlush( p_bytestream ); + + p_block = p_bytestream->p_block; + if( p_block == NULL ) + { + return NULL; + } + else if( !p_block->p_next ) + { + p_block->p_buffer += p_bytestream->i_offset; + p_block->i_buffer -= p_bytestream->i_offset; + p_bytestream->i_offset = 0; + p_bytestream->p_chain = p_bytestream->p_block = NULL; + return p_block; + } + + while( p_block->p_next && p_block->p_next->p_next ) + p_block = p_block->p_next; + + { + block_t *p_block_old = p_block; + p_block = p_block->p_next; + p_block_old->p_next = NULL; + } + + return p_block; +} + +static inline int block_SkipByte( block_bytestream_t *p_bytestream ) +{ + /* Most common case first */ + if( p_bytestream->p_block->i_buffer - p_bytestream->i_offset ) + { + p_bytestream->i_offset++; + return VLC_SUCCESS; + } + else + { + block_t *p_block; + + /* Less common case which is also slower */ + for( p_block = p_bytestream->p_block->p_next; + p_block != NULL; p_block = p_block->p_next ) + { + if( p_block->i_buffer ) + { + p_bytestream->i_offset = 1; + p_bytestream->p_block = p_block; + return VLC_SUCCESS; + } + } + } + + /* Not enough data, bail out */ + return VLC_EGENERIC; +} + +static inline int block_PeekByte( block_bytestream_t *p_bytestream, + uint8_t *p_data ) +{ + /* Most common case first */ + if( p_bytestream->p_block->i_buffer - p_bytestream->i_offset ) + { + *p_data = p_bytestream->p_block->p_buffer[p_bytestream->i_offset]; + return VLC_SUCCESS; + } + else + { + block_t *p_block; + + /* Less common case which is also slower */ + for( p_block = p_bytestream->p_block->p_next; + p_block != NULL; p_block = p_block->p_next ) + { + if( p_block->i_buffer ) + { + *p_data = p_block->p_buffer[0]; + return VLC_SUCCESS; + } + } + } + + /* Not enough data, bail out */ + return VLC_EGENERIC; +} + +static inline int block_GetByte( block_bytestream_t *p_bytestream, + uint8_t *p_data ) +{ + /* Most common case first */ + if( p_bytestream->p_block->i_buffer - p_bytestream->i_offset ) + { + *p_data = p_bytestream->p_block->p_buffer[p_bytestream->i_offset]; + p_bytestream->i_offset++; + return VLC_SUCCESS; + } + else + { + block_t *p_block; + + /* Less common case which is also slower */ + for( p_block = p_bytestream->p_block->p_next; + p_block != NULL; p_block = p_block->p_next ) + { + if( p_block->i_buffer ) + { + *p_data = p_block->p_buffer[0]; + p_bytestream->i_offset = 1; + p_bytestream->p_block = p_block; + return VLC_SUCCESS; + } + } + } + + /* Not enough data, bail out */ + return VLC_EGENERIC; +} + +static inline int block_WaitBytes( block_bytestream_t *p_bytestream, + size_t i_data ) +{ + block_t *p_block; + size_t i_offset, i_copy, i_size; + + /* Check we have that much data */ + i_offset = p_bytestream->i_offset; + i_size = i_data; + i_copy = 0; + for( p_block = p_bytestream->p_block; + p_block != NULL; p_block = p_block->p_next ) + { + i_copy = __MIN( i_size, p_block->i_buffer - i_offset ); + i_size -= i_copy; + i_offset = 0; + + if( !i_size ) break; + } + + if( i_size ) + { + /* Not enough data, bail out */ + return VLC_EGENERIC; + } + return VLC_SUCCESS; +} + +static inline int block_SkipBytes( block_bytestream_t *p_bytestream, + size_t i_data ) +{ + block_t *p_block; + size_t i_offset, i_copy; + + /* Check we have that much data */ + i_offset = p_bytestream->i_offset; + i_copy = 0; + for( p_block = p_bytestream->p_block; + p_block != NULL; p_block = p_block->p_next ) + { + i_copy = __MIN( i_data, p_block->i_buffer - i_offset ); + i_data -= i_copy; + + if( !i_data ) break; + + i_offset = 0; + } + + if( i_data ) + { + /* Not enough data, bail out */ + return VLC_EGENERIC; + } + + p_bytestream->p_block = p_block; + p_bytestream->i_offset = i_offset + i_copy; + return VLC_SUCCESS; +} + +static inline int block_PeekBytes( block_bytestream_t *p_bytestream, + uint8_t *p_data, size_t i_data ) +{ + block_t *p_block; + size_t i_offset, i_copy, i_size; + + /* Check we have that much data */ + i_offset = p_bytestream->i_offset; + i_size = i_data; + i_copy = 0; + for( p_block = p_bytestream->p_block; + p_block != NULL; p_block = p_block->p_next ) + { + i_copy = __MIN( i_size, p_block->i_buffer - i_offset ); + i_size -= i_copy; + i_offset = 0; + + if( !i_size ) break; + } + + if( i_size ) + { + /* Not enough data, bail out */ + return VLC_EGENERIC; + } + + /* Copy the data */ + i_offset = p_bytestream->i_offset; + i_size = i_data; + i_copy = 0; + for( p_block = p_bytestream->p_block; + p_block != NULL; p_block = p_block->p_next ) + { + i_copy = __MIN( i_size, p_block->i_buffer - i_offset ); + i_size -= i_copy; + + if( i_copy ) + { + memcpy( p_data, p_block->p_buffer + i_offset, i_copy ); + p_data += i_copy; + } + + i_offset = 0; + + if( !i_size ) break; + } + + return VLC_SUCCESS; +} + +static inline int block_GetBytes( block_bytestream_t *p_bytestream, + uint8_t *p_data, size_t i_data ) +{ + block_t *p_block; + size_t i_offset, i_copy, i_size; + + /* Check we have that much data */ + i_offset = p_bytestream->i_offset; + i_size = i_data; + i_copy = 0; + for( p_block = p_bytestream->p_block; + p_block != NULL; p_block = p_block->p_next ) + { + i_copy = __MIN( i_size, p_block->i_buffer - i_offset ); + i_size -= i_copy; + i_offset = 0; + + if( !i_size ) break; + } + + if( i_size ) + { + /* Not enough data, bail out */ + return VLC_EGENERIC; + } + + /* Copy the data */ + i_offset = p_bytestream->i_offset; + i_size = i_data; + i_copy = 0; + for( p_block = p_bytestream->p_block; + p_block != NULL; p_block = p_block->p_next ) + { + i_copy = __MIN( i_size, p_block->i_buffer - i_offset ); + i_size -= i_copy; + + if( i_copy ) + { + memcpy( p_data, p_block->p_buffer + i_offset, i_copy ); + p_data += i_copy; + } + + if( !i_size ) break; + + i_offset = 0; + } + + /* No buffer given, just skip the data */ + p_bytestream->p_block = p_block; + p_bytestream->i_offset = i_offset + i_copy; + + return VLC_SUCCESS; +} + +static inline int block_PeekOffsetBytes( block_bytestream_t *p_bytestream, + size_t i_peek_offset, uint8_t *p_data, size_t i_data ) +{ + block_t *p_block; + size_t i_offset, i_copy, i_size; + + /* Check we have that much data */ + i_offset = p_bytestream->i_offset; + i_size = i_data + i_peek_offset; + i_copy = 0; + for( p_block = p_bytestream->p_block; + p_block != NULL; p_block = p_block->p_next ) + { + i_copy = __MIN( i_size, p_block->i_buffer - i_offset ); + i_size -= i_copy; + i_offset = 0; + + if( !i_size ) break; + } + + if( i_size ) + { + /* Not enough data, bail out */ + return VLC_EGENERIC; + } + + /* Find the right place */ + i_offset = p_bytestream->i_offset; + i_size = i_peek_offset; + i_copy = 0; + for( p_block = p_bytestream->p_block; + p_block != NULL; p_block = p_block->p_next ) + { + i_copy = __MIN( i_size, p_block->i_buffer - i_offset ); + i_size -= i_copy; + + if( !i_size ) break; + + i_offset = 0; + } + + /* Copy the data */ + i_offset += i_copy; + i_size = i_data; + i_copy = 0; + for( ; p_block != NULL; p_block = p_block->p_next ) + { + i_copy = __MIN( i_size, p_block->i_buffer - i_offset ); + i_size -= i_copy; + + if( i_copy ) + { + memcpy( p_data, p_block->p_buffer + i_offset, i_copy ); + p_data += i_copy; + } + + i_offset = 0; + + if( !i_size ) break; + } + + return VLC_SUCCESS; +} + +static inline int block_FindStartcodeFromOffset( + block_bytestream_t *p_bytestream, size_t *pi_offset, + uint8_t *p_startcode, int i_startcode_length ) +{ + block_t *p_block, *p_block_backup = 0; + int i_size = 0; + size_t i_offset, i_offset_backup = 0; + int i_caller_offset_backup = 0, i_match; + + /* Find the right place */ + i_size = *pi_offset + p_bytestream->i_offset; + for( p_block = p_bytestream->p_block; + p_block != NULL; p_block = p_block->p_next ) + { + i_size -= p_block->i_buffer; + if( i_size < 0 ) break; + } + + if( i_size >= 0 ) + { + /* Not enough data, bail out */ + return VLC_EGENERIC; + } + + /* Begin the search. + * We first look for an occurrence of the 1st startcode byte and + * if found, we do a more thorough check. */ + i_size += p_block->i_buffer; + *pi_offset -= i_size; + i_match = 0; + for( ; p_block != NULL; p_block = p_block->p_next ) + { + for( i_offset = i_size; i_offset < p_block->i_buffer; i_offset++ ) + { + if( p_block->p_buffer[i_offset] == p_startcode[i_match] ) + { + if( !i_match ) + { + p_block_backup = p_block; + i_offset_backup = i_offset; + i_caller_offset_backup = *pi_offset; + } + + if( i_match + 1 == i_startcode_length ) + { + /* We have it */ + *pi_offset += i_offset - i_match; + return VLC_SUCCESS; + } + + i_match++; + } + else if ( i_match ) + { + /* False positive */ + p_block = p_block_backup; + i_offset = i_offset_backup; + *pi_offset = i_caller_offset_backup; + i_match = 0; + } + + } + i_size = 0; + *pi_offset += i_offset; + } + + *pi_offset -= i_match; + return VLC_EGENERIC; +} + +#endif /* VLC_BLOCK_HELPER_H */ diff --git a/VLC/vlc_charset.h b/VLC/vlc_charset.h new file mode 100644 index 0000000..5bd3faa --- /dev/null +++ b/VLC/vlc_charset.h @@ -0,0 +1,86 @@ +/***************************************************************************** + * charset.h: Unicode UTF-8 wrappers function + ***************************************************************************** + * Copyright (C) 2003-2005 the VideoLAN team + * Copyright © 2005-2006 Rémi Denis-Courmont + * $Id$ + * + * Author: Rémi Denis-Courmont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_CHARSET_H +#define VLC_CHARSET_H 1 + +/** + * \file + * This files handles locale conversions in vlc + */ + +#include +#include +#include + +VLC_EXPORT( void, LocaleFree, ( const char * ) ); +VLC_EXPORT( char *, FromLocale, ( const char * ) ); +VLC_EXPORT( char *, FromLocaleDup, ( const char * ) ); +VLC_EXPORT( char *, ToLocale, ( const char * ) ); +VLC_EXPORT( char *, ToLocaleDup, ( const char * ) ); + +/* TODO: move all of this to "vlc_fs.h" or something like that */ +VLC_EXPORT( int, utf8_open, ( const char *filename, int flags, mode_t mode ) ); +VLC_EXPORT( FILE *, utf8_fopen, ( const char *filename, const char *mode ) ); +VLC_EXPORT( DIR *, utf8_opendir, ( const char *dirname ) ); +VLC_EXPORT( char *, utf8_readdir, ( DIR *dir ) ); +VLC_EXPORT( int, utf8_loaddir, ( DIR *dir, char ***namelist, int (*select)( const char * ), int (*compar)( const char **, const char ** ) ) ); +VLC_EXPORT( int, utf8_scandir, ( const char *dirname, char ***namelist, int (*select)( const char * ), int (*compar)( const char **, const char ** ) ) ); +VLC_EXPORT( int, utf8_mkdir, ( const char *filename, mode_t mode ) ); +VLC_EXPORT( int, utf8_unlink, ( const char *filename ) ); + +#ifdef WIN32 +# define stat _stati64 +#endif + +VLC_EXPORT( int, utf8_stat, ( const char *filename, struct stat *buf ) ); +VLC_EXPORT( int, utf8_lstat, ( const char *filename, struct stat *buf ) ); + +VLC_EXPORT( int, utf8_vfprintf, ( FILE *stream, const char *fmt, va_list ap ) ); +VLC_EXPORT( int, utf8_fprintf, ( FILE *, const char *, ... ) LIBVLC_FORMAT( 2, 3 ) ); + +VLC_EXPORT( char *, EnsureUTF8, ( char * ) ); +VLC_EXPORT( const char *, IsUTF8, ( const char * ) ); + +#ifdef WIN32 +static inline char *FromWide (const wchar_t *wide) +{ + size_t len = WideCharToMultiByte (CP_UTF8, 0, wide, -1, NULL, 0, NULL, NULL); + if (len == 0) + return NULL; + + char *out = (char *)malloc (len); + + if (out) + WideCharToMultiByte (CP_UTF8, 0, wide, -1, out, len, NULL, NULL); + return out; +} +#endif + +VLC_EXPORT( const char *, GetFallbackEncoding, ( void ) ); + +VLC_EXPORT( double, us_strtod, ( const char *, char ** ) ); +VLC_EXPORT( double, us_atof, ( const char * ) ); + +#endif diff --git a/VLC/vlc_codec.h b/VLC/vlc_codec.h new file mode 100644 index 0000000..92b5b7f --- /dev/null +++ b/VLC/vlc_codec.h @@ -0,0 +1,154 @@ +/***************************************************************************** + * vlc_codec.h: Definition of the decoder and encoder structures + ***************************************************************************** + * Copyright (C) 1999-2003 the VideoLAN team + * $Id$ + * + * Authors: Gildas Bazin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_CODEC_H +#define VLC_CODEC_H 1 + +#include "vlc_block.h" +#include "vlc_es.h" + +/** + * \file + * This file defines the structure and types used by decoders and encoders + */ + +typedef struct decoder_owner_sys_t decoder_owner_sys_t; + +/** + * \defgroup decoder Decoder + * + * The structure describing a decoder + * + * @{ + */ + +/* + * BIG FAT WARNING : the code relies in the first 4 members of filter_t + * and decoder_t to be the same, so if you have anything to add, do it + * at the end of the structure. + */ +struct decoder_t +{ + VLC_COMMON_MEMBERS + + /* Module properties */ + module_t * p_module; + decoder_sys_t * p_sys; + + /* Input format ie from demuxer (XXX: a lot of field could be invalid) */ + es_format_t fmt_in; + + /* Output format of decoder/packetizer */ + es_format_t fmt_out; + + /* Some decoders only accept packetized data (ie. not truncated) */ + bool b_need_packetized; + + /* Tell the decoder if it is allowed to drop frames */ + bool b_pace_control; + + /* */ + picture_t * ( * pf_decode_video )( decoder_t *, block_t ** ); + aout_buffer_t * ( * pf_decode_audio )( decoder_t *, block_t ** ); + subpicture_t * ( * pf_decode_sub) ( decoder_t *, block_t ** ); + block_t * ( * pf_packetize ) ( decoder_t *, block_t ** ); + + /* Closed Caption (CEA 608/708) extraction. + * If set, it *may* be called after pf_decode_video/pf_packetize + * returned data. It should return CC for the pictures returned by the + * last pf_packetize/pf_decode_video call only, + * pb_present will be used to known which cc channel are present (but + * globaly, not necessary for the current packet */ + block_t * ( * pf_get_cc ) ( decoder_t *, bool pb_present[4] ); + + /* + * Buffers allocation + */ + + /* Audio output callbacks */ + aout_buffer_t * ( * pf_aout_buffer_new) ( decoder_t *, int ); + void ( * pf_aout_buffer_del) ( decoder_t *, aout_buffer_t * ); + + /* Video output callbacks */ + picture_t * ( * pf_vout_buffer_new) ( decoder_t * ); + void ( * pf_vout_buffer_del) ( decoder_t *, picture_t * ); + void ( * pf_picture_link) ( decoder_t *, picture_t * ); + void ( * pf_picture_unlink) ( decoder_t *, picture_t * ); + + /* SPU output callbacks */ + subpicture_t * ( * pf_spu_buffer_new) ( decoder_t * ); + void ( * pf_spu_buffer_del) ( decoder_t *, subpicture_t * ); + + /* Private structure for the owner of the decoder */ + decoder_owner_sys_t *p_owner; +}; + +/** + * @} + */ + +/** + * \defgroup decoder Encoder + * + * The structure describing a Encoder + * + * @{ + */ + +struct encoder_t +{ + VLC_COMMON_MEMBERS + + /* Module properties */ + module_t * p_module; + encoder_sys_t * p_sys; + + /* Properties of the input data fed to the encoder */ + es_format_t fmt_in; + + /* Properties of the output of the encoder */ + es_format_t fmt_out; + + block_t * ( * pf_encode_video )( encoder_t *, picture_t * ); + block_t * ( * pf_encode_audio )( encoder_t *, aout_buffer_t * ); + block_t * ( * pf_encode_sub )( encoder_t *, subpicture_t * ); + + /* Common encoder options */ + int i_threads; /* Number of threads to use during encoding */ + int i_iframes; /* One I frame per i_iframes */ + int i_bframes; /* One B frame per i_bframes */ + int i_tolerance; /* Bitrate tolerance */ + + /* Encoder config */ + config_chain_t *p_cfg; +}; + +/** + * @} + */ + +VLC_EXPORT( input_attachment_t *, decoder_GetInputAttachment, ( decoder_t *, const char *psz_name ) ); +VLC_EXPORT( int, decoder_GetInputAttachments, ( decoder_t *p_dec, input_attachment_t ***ppp_attachment, int *pi_attachment ) ); +VLC_EXPORT( mtime_t, decoder_GetDisplayDate, ( decoder_t *, mtime_t ) ); + +#endif /* _VLC_CODEC_H */ diff --git a/VLC/vlc_codec_synchro.h b/VLC/vlc_codec_synchro.h new file mode 100644 index 0000000..0725790 --- /dev/null +++ b/VLC/vlc_codec_synchro.h @@ -0,0 +1,49 @@ +/***************************************************************************** + * vlc_codec_synchro.h: frame-dropping structures + ***************************************************************************** + * Copyright (C) 1999-2005 the VideoLAN team + * $Id$ + * + * Authors: Christophe Massiot + * Jean-Marc Dressler + * Stéphane Borel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * decoder_synchro_t : timers for the video synchro + *****************************************************************************/ +/* Read the discussion on top of decoder_synchro.c for more information. */ +/* Pictures types */ +#define I_CODING_TYPE 1 +#define P_CODING_TYPE 2 +#define B_CODING_TYPE 3 +#define D_CODING_TYPE 4 /* MPEG-1 ONLY */ +/* other values are reserved */ + +/***************************************************************************** + * Prototypes + *****************************************************************************/ +VLC_EXPORT( decoder_synchro_t *, decoder_SynchroInit, ( decoder_t *, int ) ); +VLC_EXPORT( void, decoder_SynchroRelease, ( decoder_synchro_t * ) ); +VLC_EXPORT( void, decoder_SynchroReset, ( decoder_synchro_t * ) ); +VLC_EXPORT( bool, decoder_SynchroChoose, ( decoder_synchro_t *, int, int, bool ) ); +VLC_EXPORT( void, decoder_SynchroTrash, ( decoder_synchro_t * ) ); +VLC_EXPORT( void, decoder_SynchroDecode, ( decoder_synchro_t * ) ); +VLC_EXPORT( void, decoder_SynchroEnd, ( decoder_synchro_t *, int, bool ) ); +VLC_EXPORT( mtime_t, decoder_SynchroDate, ( decoder_synchro_t * ) ); +VLC_EXPORT( void, decoder_SynchroNewPicture, ( decoder_synchro_t *, int, int, mtime_t, mtime_t, int, bool ) ); + diff --git a/VLC/vlc_codecs.h b/VLC/vlc_codecs.h new file mode 100644 index 0000000..4604711 --- /dev/null +++ b/VLC/vlc_codecs.h @@ -0,0 +1,422 @@ +/***************************************************************************** + * codecs.h: codec related structures needed by the demuxers and decoders + ***************************************************************************** + * Copyright (C) 1999-2001 the VideoLAN team + * $Id$ + * + * Author: Gildas Bazin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_CODECS_H +#define VLC_CODECS_H 1 + +/** + * \file + * This file defines codec related structures needed by the demuxers and decoders + */ + +#ifdef HAVE_ATTRIBUTE_PACKED +# define ATTR_PACKED __attribute__((__packed__)) +#else +# error FIXME +#endif + +/* Structures exported to the demuxers and decoders */ + +#if !(defined _GUID_DEFINED || defined GUID_DEFINED) +#define GUID_DEFINED +typedef struct _GUID +{ + uint32_t Data1; + uint16_t Data2; + uint16_t Data3; + uint8_t Data4[8]; +} GUID, *REFGUID, *LPGUID; +#endif /* GUID_DEFINED */ + +#ifndef _WAVEFORMATEX_ +#define _WAVEFORMATEX_ +typedef struct +ATTR_PACKED +_WAVEFORMATEX { + uint16_t wFormatTag; + uint16_t nChannels; + uint32_t nSamplesPerSec; + uint32_t nAvgBytesPerSec; + uint16_t nBlockAlign; + uint16_t wBitsPerSample; + uint16_t cbSize; +} WAVEFORMATEX, *PWAVEFORMATEX, *NPWAVEFORMATEX, *LPWAVEFORMATEX; +#endif /* _WAVEFORMATEX_ */ + +#ifndef _WAVEFORMATEXTENSIBLE_ +#define _WAVEFORMATEXTENSIBLE_ +typedef struct +ATTR_PACKED +_WAVEFORMATEXTENSIBLE { + WAVEFORMATEX Format; + union { + uint16_t wValidBitsPerSample; + uint16_t wSamplesPerBlock; + uint16_t wReserved; + } Samples; + uint32_t dwChannelMask; + GUID SubFormat; +} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE; +#endif /* _WAVEFORMATEXTENSIBLE_ */ + +#ifndef _WAVEHEADER_ +#define _WAVEHEADER_ +typedef struct +ATTR_PACKED +_WAVEHEADER { + uint32_t MainChunkID; + uint32_t Length; + uint32_t ChunkTypeID; + uint32_t SubChunkID; + uint32_t SubChunkLength; + uint16_t Format; + uint16_t Modus; + uint32_t SampleFreq; + uint32_t BytesPerSec; + uint16_t BytesPerSample; + uint16_t BitsPerSample; + uint32_t DataChunkID; + uint32_t DataLength; +} WAVEHEADER; +#endif /* _WAVEHEADER_ */ + +#if !defined(_BITMAPINFOHEADER_) && !defined(WIN32) +#define _BITMAPINFOHEADER_ +typedef struct +ATTR_PACKED +{ + uint32_t biSize; + uint32_t biWidth; + uint32_t biHeight; + uint16_t biPlanes; + uint16_t biBitCount; + uint32_t biCompression; + uint32_t biSizeImage; + uint32_t biXPelsPerMeter; + uint32_t biYPelsPerMeter; + uint32_t biClrUsed; + uint32_t biClrImportant; +} BITMAPINFOHEADER, *PBITMAPINFOHEADER, *LPBITMAPINFOHEADER; + +typedef struct +ATTR_PACKED +{ + BITMAPINFOHEADER bmiHeader; + int bmiColors[1]; +} BITMAPINFO, *LPBITMAPINFO; +#endif + +#ifndef _RECT32_ +#define _RECT32_ +typedef struct +ATTR_PACKED +{ + int left, top, right, bottom; +} RECT32; +#endif + +#ifndef _REFERENCE_TIME_ +#define _REFERENCE_TIME_ +typedef int64_t REFERENCE_TIME; +#endif + +#ifndef _VIDEOINFOHEADER_ +#define _VIDEOINFOHEADER_ +typedef struct +ATTR_PACKED +{ + RECT32 rcSource; + RECT32 rcTarget; + uint32_t dwBitRate; + uint32_t dwBitErrorRate; + REFERENCE_TIME AvgTimePerFrame; + BITMAPINFOHEADER bmiHeader; +} VIDEOINFOHEADER; +#endif + +#ifndef _RGBQUAD_ +#define _RGBQUAD_ +typedef struct +ATTR_PACKED +{ + uint8_t rgbBlue; + uint8_t rgbGreen; + uint8_t rgbRed; + uint8_t rgbReserved; +} RGBQUAD1; +#endif + +#ifndef _TRUECOLORINFO_ +#define _TRUECOLORINFO_ +typedef struct +ATTR_PACKED +{ + uint32_t dwBitMasks[3]; + RGBQUAD1 bmiColors[256]; +} TRUECOLORINFO; +#endif + +#ifndef _VIDEOINFO_ +#define _VIDEOINFO_ +typedef struct +ATTR_PACKED +{ + RECT32 rcSource; + RECT32 rcTarget; + uint32_t dwBitRate; + uint32_t dwBitErrorRate; + REFERENCE_TIME AvgTimePerFrame; + BITMAPINFOHEADER bmiHeader; + + union + { + RGBQUAD1 bmiColors[256]; /* Colour palette */ + uint32_t dwBitMasks[3]; /* True colour masks */ + TRUECOLORINFO TrueColorInfo; /* Both of the above */ + }; + +} VIDEOINFO; +#endif + +/* WAVE format wFormatTag IDs */ +#define WAVE_FORMAT_UNKNOWN 0x0000 /* Microsoft Corporation */ +#define WAVE_FORMAT_PCM 0x0001 /* Microsoft Corporation */ +#define WAVE_FORMAT_ADPCM 0x0002 /* Microsoft Corporation */ +#define WAVE_FORMAT_IEEE_FLOAT 0x0003 /* Microsoft Corporation */ +#define WAVE_FORMAT_ALAW 0x0006 /* Microsoft Corporation */ +#define WAVE_FORMAT_MULAW 0x0007 /* Microsoft Corporation */ +#define WAVE_FORMAT_DTS_MS 0x0008 /* Microsoft Corporation */ +#define WAVE_FORMAT_WMAS 0x000a /* WMA 9 Speech */ +#define WAVE_FORMAT_IMA_ADPCM 0x0011 /* Intel Corporation */ +#define WAVE_FORMAT_GSM610 0x0031 /* Microsoft Corporation */ +#define WAVE_FORMAT_MSNAUDIO 0x0032 /* Microsoft Corporation */ +#define WAVE_FORMAT_G726 0x0045 /* ITU-T standard */ +#define WAVE_FORMAT_MPEG 0x0050 /* Microsoft Corporation */ +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */ +#define WAVE_FORMAT_DOLBY_AC3_SPDIF 0x0092 /* Sonic Foundry */ + +#define WAVE_FORMAT_A52 0x2000 +#define WAVE_FORMAT_DTS 0x2001 +#define WAVE_FORMAT_WMA1 0x0160 /* WMA version 1 */ +#define WAVE_FORMAT_WMA2 0x0161 /* WMA (v2) 7, 8, 9 Series */ +#define WAVE_FORMAT_WMAP 0x0162 /* WMA 9 Professional */ +#define WAVE_FORMAT_WMAL 0x0163 /* WMA 9 Lossless */ +#define WAVE_FORMAT_DIVIO_AAC 0x4143 +#define WAVE_FORMAT_AAC 0x00FF +#define WAVE_FORMAT_FFMPEG_AAC 0x706D + +/* Need to check these */ +#define WAVE_FORMAT_DK3 0x0061 +#define WAVE_FORMAT_DK4 0x0062 + +/* At least FFmpeg use that ID: from libavformat/riff.c ('Vo' == 0x566f) + * { CODEC_ID_VORBIS, ('V'<<8)+'o' }, //HACK/FIXME, does vorbis in WAV/AVI have an (in)official id? + */ +#define WAVE_FORMAT_VORBIS 0x566f + +/* It seems that these IDs are used by braindead & obsolete VorbisACM encoder + * (Windows only) + * A few info is available except VorbisACM source (remember, Windows only) + * (available on http://svn.xiph.org), but it seems that vo3+ at least is + * made of Vorbis data encapsulated in Ogg container... + */ +#define WAVE_FORMAT_VORB_1 0x674f +#define WAVE_FORMAT_VORB_2 0x6750 +#define WAVE_FORMAT_VORB_3 0x6751 +#define WAVE_FORMAT_VORB_1PLUS 0x676f +#define WAVE_FORMAT_VORB_2PLUS 0x6770 +#define WAVE_FORMAT_VORB_3PLUS 0x6771 + +#define WAVE_FORMAT_SPEEX 0xa109 /* Speex audio */ + + +#if !defined(WAVE_FORMAT_EXTENSIBLE) +#define WAVE_FORMAT_EXTENSIBLE 0xFFFE /* Microsoft */ +#endif + +/* GUID SubFormat IDs */ +/* We need both b/c const variables are not compile-time constants in C, giving + * us an error if we use the const GUID in an enum */ + +#ifndef _KSDATAFORMAT_SUBTYPE_PCM_ +#define _KSDATAFORMAT_SUBTYPE_PCM_ {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} +static const GUID VLC_KSDATAFORMAT_SUBTYPE_PCM = {0xE923AABF, 0xCB58, 0x4471, {0xA1, 0x19, 0xFF, 0xFA, 0x01, 0xE4, 0xCE, 0x62}}; +#define KSDATAFORMAT_SUBTYPE_PCM VLC_KSDATAFORMAT_SUBTYPE_PCM +#endif + +#ifndef _KSDATAFORMAT_SUBTYPE_UNKNOWN_ +#define _KSDATAFORMAT_SUBTYPE_UNKNOWN_ {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}} +static const GUID VLC_KSDATAFORMAT_SUBTYPE_UNKNOWN = {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; +#define KSDATAFORMAT_SUBTYPE_UNKNOWN VLC_KSDATAFORMAT_SUBTYPE_UNKNOWN +#endif + +/* Microsoft speaker definitions */ +#define WAVE_SPEAKER_FRONT_LEFT 0x1 +#define WAVE_SPEAKER_FRONT_RIGHT 0x2 +#define WAVE_SPEAKER_FRONT_CENTER 0x4 +#define WAVE_SPEAKER_LOW_FREQUENCY 0x8 +#define WAVE_SPEAKER_BACK_LEFT 0x10 +#define WAVE_SPEAKER_BACK_RIGHT 0x20 +#define WAVE_SPEAKER_FRONT_LEFT_OF_CENTER 0x40 +#define WAVE_SPEAKER_FRONT_RIGHT_OF_CENTER 0x80 +#define WAVE_SPEAKER_BACK_CENTER 0x100 +#define WAVE_SPEAKER_SIDE_LEFT 0x200 +#define WAVE_SPEAKER_SIDE_RIGHT 0x400 +#define WAVE_SPEAKER_TOP_CENTER 0x800 +#define WAVE_SPEAKER_TOP_FRONT_LEFT 0x1000 +#define WAVE_SPEAKER_TOP_FRONT_CENTER 0x2000 +#define WAVE_SPEAKER_TOP_FRONT_RIGHT 0x4000 +#define WAVE_SPEAKER_TOP_BACK_LEFT 0x8000 +#define WAVE_SPEAKER_TOP_BACK_CENTER 0x10000 +#define WAVE_SPEAKER_TOP_BACK_RIGHT 0x20000 +#define WAVE_SPEAKER_RESERVED 0x80000000 + +static const struct +{ + uint16_t i_tag; + vlc_fourcc_t i_fourcc; + const char *psz_name; +} +wave_format_tag_to_fourcc[] = +{ + { WAVE_FORMAT_PCM, VLC_FOURCC( 'a', 'r', 'a', 'w' ), "Raw audio" }, + { WAVE_FORMAT_ADPCM, VLC_FOURCC( 'm', 's', 0x00,0x02), "ADPCM" }, + { WAVE_FORMAT_IEEE_FLOAT, VLC_FOURCC( 'a', 'f', 'l', 't' ), "IEEE Float audio" }, + { WAVE_FORMAT_ALAW, VLC_FOURCC( 'a', 'l', 'a', 'w' ), "A-Law" }, + { WAVE_FORMAT_MULAW, VLC_FOURCC( 'm', 'l', 'a', 'w' ), "Mu-Law" }, + { WAVE_FORMAT_IMA_ADPCM, VLC_FOURCC( 'm', 's', 0x00,0x11), "Ima-ADPCM" }, + { WAVE_FORMAT_G726, VLC_FOURCC( 'g', '7', '2', '6' ), "G.726 ADPCM" }, + { WAVE_FORMAT_MPEGLAYER3, VLC_FOURCC( 'm', 'p', 'g', 'a' ), "Mpeg Audio" }, + { WAVE_FORMAT_MPEG, VLC_FOURCC( 'm', 'p', 'g', 'a' ), "Mpeg Audio" }, + { WAVE_FORMAT_A52, VLC_FOURCC( 'a', '5', '2', ' ' ), "A/52" }, + { WAVE_FORMAT_WMA1, VLC_FOURCC( 'w', 'm', 'a', '1' ), "Window Media Audio v1" }, + { WAVE_FORMAT_WMA2, VLC_FOURCC( 'w', 'm', 'a', '2' ), "Window Media Audio v2" }, + { WAVE_FORMAT_WMA2, VLC_FOURCC( 'w', 'm', 'a', ' ' ), "Window Media Audio v2" }, + { WAVE_FORMAT_WMAP, VLC_FOURCC( 'w', 'm', 'a', 'p' ), "Window Media Audio 9 Professional" }, + { WAVE_FORMAT_WMAL, VLC_FOURCC( 'w', 'm', 'a', 'l' ), "Window Media Audio 9 Lossless" }, + { WAVE_FORMAT_WMAS, VLC_FOURCC( 'w', 'm', 'a', 's' ), "Window Media Audio 9 Speech" }, + { WAVE_FORMAT_DK3, VLC_FOURCC( 'm', 's', 0x00,0x61), "Duck DK3" }, + { WAVE_FORMAT_DK4, VLC_FOURCC( 'm', 's', 0x00,0x62), "Duck DK4" }, + { WAVE_FORMAT_DTS, VLC_FOURCC( 'd', 't', 's', ' ' ), "DTS Coherent Acoustics" }, + { WAVE_FORMAT_DTS_MS, VLC_FOURCC( 'd', 't', 's', ' ' ), "DTS Coherent Acoustics" }, + { WAVE_FORMAT_DIVIO_AAC, VLC_FOURCC( 'm', 'p', '4', 'a' ), "MPEG-4 Audio (Divio)" }, + { WAVE_FORMAT_AAC, VLC_FOURCC( 'm', 'p', '4', 'a' ), "MPEG-4 Audio" }, + { WAVE_FORMAT_FFMPEG_AAC, VLC_FOURCC( 'm', 'p', '4', 'a' ), "MPEG-4 Audio" }, + { WAVE_FORMAT_VORBIS, VLC_FOURCC( 'v', 'o', 'r', 'b' ), "Vorbis Audio" }, + { WAVE_FORMAT_VORB_1, VLC_FOURCC( 'v', 'o', 'r', '1' ), "Vorbis 1 Audio" }, + { WAVE_FORMAT_VORB_1PLUS, VLC_FOURCC( 'v', 'o', '1', '+' ), "Vorbis 1+ Audio" }, + { WAVE_FORMAT_VORB_2, VLC_FOURCC( 'v', 'o', 'r', '2' ), "Vorbis 2 Audio" }, + { WAVE_FORMAT_VORB_2PLUS, VLC_FOURCC( 'v', 'o', '2', '+' ), "Vorbis 2+ Audio" }, + { WAVE_FORMAT_VORB_3, VLC_FOURCC( 'v', 'o', 'r', '3' ), "Vorbis 3 Audio" }, + { WAVE_FORMAT_VORB_3PLUS, VLC_FOURCC( 'v', 'o', '3', '+' ), "Vorbis 3+ Audio" }, + { WAVE_FORMAT_SPEEX, VLC_FOURCC( 's', 'p', 'x', ' ' ), "Speex Audio" }, + { WAVE_FORMAT_UNKNOWN, VLC_FOURCC( 'u', 'n', 'd', 'f' ), "Unknown" } +}; + +static inline void wf_tag_to_fourcc( uint16_t i_tag, vlc_fourcc_t *fcc, + const char **ppsz_name ) +{ + int i; + for( i = 0; wave_format_tag_to_fourcc[i].i_tag != 0; i++ ) + { + if( wave_format_tag_to_fourcc[i].i_tag == i_tag ) break; + } + if( fcc ) *fcc = wave_format_tag_to_fourcc[i].i_fourcc; + if( ppsz_name ) *ppsz_name = wave_format_tag_to_fourcc[i].psz_name; +} + +static inline void fourcc_to_wf_tag( vlc_fourcc_t fcc, uint16_t *pi_tag ) +{ + int i; + for( i = 0; wave_format_tag_to_fourcc[i].i_tag != 0; i++ ) + { + if( wave_format_tag_to_fourcc[i].i_fourcc == fcc ) break; + } + if( pi_tag ) *pi_tag = wave_format_tag_to_fourcc[i].i_tag; +} + +/* If wFormatTag is WAVEFORMATEXTENSIBLE, we must look at the SubFormat tag + * to determine the actual format. Microsoft has stopped giving out wFormatTag + * assignments in lieu of letting 3rd parties generate their own GUIDs + */ +static const struct +{ + GUID guid_tag; + vlc_fourcc_t i_fourcc; + const char *psz_name; +} +sub_format_tag_to_fourcc[] = +{ + { _KSDATAFORMAT_SUBTYPE_PCM_, VLC_FOURCC( 'p', 'c', 'm', ' ' ), "PCM" }, + { _KSDATAFORMAT_SUBTYPE_UNKNOWN_, VLC_FOURCC( 'u', 'n', 'd', 'f' ), "Unknown" } +}; + +/* compares two GUIDs, returns 1 if identical, 0 otherwise */ +static inline int guidcmp( const GUID *s1, const GUID *s2 ) +{ + return( s1->Data1 == s2->Data1 && s1->Data2 == s2->Data2 && + s1->Data3 == s2->Data3 && !memcmp( s1->Data4, s2->Data4, 8 ) ); +} + +static inline void sf_tag_to_fourcc( GUID *guid_tag, + vlc_fourcc_t *fcc, const char **ppsz_name ) +{ + int i; + + for( i = 0; !guidcmp( &sub_format_tag_to_fourcc[i].guid_tag, + &KSDATAFORMAT_SUBTYPE_UNKNOWN ); i++ ) + { + if( guidcmp( &sub_format_tag_to_fourcc[i].guid_tag, guid_tag ) ) break; + } + if( fcc ) *fcc = sub_format_tag_to_fourcc[i].i_fourcc; + if( ppsz_name ) *ppsz_name = sub_format_tag_to_fourcc[i].psz_name; +} + +/** + * Structure to hold information concerning subtitles. + * Used between demuxers and decoders of subtitles. + */ +typedef struct es_sys_t +{ + char *psz_header; /* for 'ssa ' and 'subt' */ + + /* for spudec */ + unsigned int i_orig_height; + unsigned int i_orig_width; + unsigned int i_origin_x; + unsigned int i_origin_y; + unsigned int i_scale_h; + unsigned int i_scale_v; + unsigned int i_alpha; + bool b_smooth; + mtime_t i_fade_in; + mtime_t i_fade_out; + unsigned int i_align; + mtime_t i_time_offset; + bool b_forced_subs; + unsigned int palette[16]; + unsigned int colors[4]; + +} subtitle_data_t; + +#endif /* "codecs.h" */ diff --git a/VLC/vlc_common.h b/VLC/vlc_common.h new file mode 100644 index 0000000..e5781e4 --- /dev/null +++ b/VLC/vlc_common.h @@ -0,0 +1,906 @@ +/***************************************************************************** + * common.h: common definitions + * Collection of useful common types and macros definitions + ***************************************************************************** + * Copyright (C) 1998-2005 the VideoLAN team + * $Id$ + * + * Authors: Samuel Hocevar + * Vincent Seguin + * Gildas Bazin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/** + * \file + * This file is a collection of common definitions and types + */ + +#ifndef VLC_COMMON_H +# define VLC_COMMON_H 1 + +/***************************************************************************** + * Required vlc headers + *****************************************************************************/ +#if defined( _MSC_VER ) +# pragma warning( disable : 4244 ) +#endif + +#include "vlc_config.h" + +/***************************************************************************** + * Required system headers + *****************************************************************************/ +#include +#include + +#include +#include +#include + +#ifndef __cplusplus +# include +#endif +#define COPYRIGHT_MESSAGE "Copyright (C) 1998-2005 the VideoLAN team" +/***************************************************************************** + * Basic types definitions + *****************************************************************************/ +#if defined( WIN32 ) || defined( UNDER_CE ) +# include +# ifndef PATH_MAX +# define PATH_MAX MAX_PATH +# endif +#endif + +/* Counter for statistics and profiling */ +typedef unsigned long count_t; + +/* Audio volume */ +typedef uint16_t audio_volume_t; + +/** + * High precision date or time interval + * + * Store a high precision date or time interval. The maximum precision is the + * microsecond, and a 64 bits integer is used to avoid overflows (maximum + * time interval is then 292271 years, which should be long enough for any + * video). Dates are stored as microseconds since a common date (usually the + * epoch). Note that date and time intervals can be manipulated using regular + * arithmetic operators, and that no special functions are required. + */ +typedef int64_t mtime_t; + +/** + * The vlc_fourcc_t type. + * + * See http://www.webartz.com/fourcc/ for a very detailed list. + */ +typedef uint32_t vlc_fourcc_t; + +#ifdef WORDS_BIGENDIAN +# define VLC_FOURCC( a, b, c, d ) \ + ( ((uint32_t)d) | ( ((uint32_t)c) << 8 ) \ + | ( ((uint32_t)b) << 16 ) | ( ((uint32_t)a) << 24 ) ) +# define VLC_TWOCC( a, b ) \ + ( (uint16_t)(b) | ( (uint16_t)(a) << 8 ) ) + +#else +# define VLC_FOURCC( a, b, c, d ) \ + ( ((uint32_t)a) | ( ((uint32_t)b) << 8 ) \ + | ( ((uint32_t)c) << 16 ) | ( ((uint32_t)d) << 24 ) ) +# define VLC_TWOCC( a, b ) \ + ( (uint16_t)(a) | ( (uint16_t)(b) << 8 ) ) + +#endif + +static inline void __vlc_fourcc_to_char( vlc_fourcc_t fcc, char *psz_fourcc ) +{ + memcpy( psz_fourcc, &fcc, 4 ); +} + +#define vlc_fourcc_to_char( a, b ) \ + __vlc_fourcc_to_char( (vlc_fourcc_t)(a), (char *)(b) ) +#define N_(a) \ + a +#define _(a) \ + a +#define PACKAGE_STRING "" +#define VLC_COMPILE_BY "" +#define VLC_COMPILER "gcc-4.0" +#define VLC_COMPILE_HOST "iphoneOS-2.0" +#define VLC_COMPILE_DOMAIN "" +/***************************************************************************** + * Classes declaration + *****************************************************************************/ + +/* Internal types */ +typedef struct vlc_list_t vlc_list_t; +typedef struct vlc_object_t vlc_object_t; +typedef struct libvlc_int_t libvlc_int_t; +typedef struct variable_t variable_t; +typedef struct date_t date_t; +typedef struct dict_entry_t dict_entry_t; +typedef struct dict_t dict_t; +typedef struct gc_object_t gc_object_t ; + +/* Messages */ +typedef struct msg_subscription_t msg_subscription_t; + +/* Playlist */ + +/* FIXME */ +/** + * Playlist commands + */ +typedef enum { + PLAYLIST_PLAY, /**< No arg. res=can fail*/ + PLAYLIST_VIEWPLAY, /**< arg1= playlist_item_t*,*/ + /** arg2 = playlist_item_t* , res=can fail */ + PLAYLIST_PAUSE, /**< No arg res=can fail*/ + PLAYLIST_STOP, /**< No arg res=can fail*/ + PLAYLIST_SKIP, /**< arg1=int, res=can fail*/ +} playlist_command_t; + + +typedef struct playlist_t playlist_t; +typedef struct playlist_item_t playlist_item_t; +typedef struct playlist_view_t playlist_view_t; +typedef struct playlist_export_t playlist_export_t; +typedef struct services_discovery_t services_discovery_t; +typedef struct services_discovery_sys_t services_discovery_sys_t; +typedef struct playlist_add_t playlist_add_t; +typedef struct playlist_preparse_t playlist_preparse_t; +typedef struct playlist_fetcher_t playlist_fetcher_t; + +/* Modules */ +typedef struct module_bank_t module_bank_t; +typedef struct module_t module_t; +typedef struct module_config_t module_config_t; +typedef struct module_symbols_t module_symbols_t; +typedef struct module_cache_t module_cache_t; + +typedef struct config_category_t config_category_t; + +/* Interface */ +typedef struct intf_thread_t intf_thread_t; +typedef struct intf_sys_t intf_sys_t; +typedef struct intf_console_t intf_console_t; +typedef struct intf_msg_t intf_msg_t; +typedef struct interaction_t interaction_t; +typedef struct interaction_dialog_t interaction_dialog_t; +typedef struct user_widget_t user_widget_t; + +/* Input */ +typedef struct input_thread_t input_thread_t; +typedef struct input_thread_sys_t input_thread_sys_t; +typedef struct input_item_t input_item_t; +typedef struct access_t access_t; +typedef struct access_sys_t access_sys_t; +typedef struct stream_t stream_t; +typedef struct stream_sys_t stream_sys_t; +typedef struct demux_t demux_t; +typedef struct demux_meta_t demux_meta_t; +typedef struct demux_sys_t demux_sys_t; +typedef struct es_out_t es_out_t; +typedef struct es_out_id_t es_out_id_t; +typedef struct es_out_sys_t es_out_sys_t; +typedef struct es_descriptor_t es_descriptor_t; +typedef struct seekpoint_t seekpoint_t; +typedef struct info_t info_t; +typedef struct info_category_t info_category_t; +typedef struct input_attachment_t input_attachment_t; + +/* Format */ +typedef struct audio_format_t audio_format_t; +typedef struct video_format_t video_format_t; +typedef struct subs_format_t subs_format_t; +typedef struct es_format_t es_format_t; +typedef struct video_palette_t video_palette_t; + +/* Audio */ +typedef struct aout_instance_t aout_instance_t; +typedef struct aout_sys_t aout_sys_t; +typedef struct aout_fifo_t aout_fifo_t; +typedef struct aout_input_t aout_input_t; +typedef struct aout_buffer_t aout_buffer_t; +typedef audio_format_t audio_sample_format_t; +typedef struct audio_date_t audio_date_t; +typedef struct aout_filter_t aout_filter_t; + +/* Video */ +typedef struct vout_thread_t vout_thread_t; +typedef struct vout_sys_t vout_sys_t; + +typedef video_format_t video_frame_format_t; +typedef struct picture_t picture_t; +typedef struct picture_sys_t picture_sys_t; +typedef struct picture_heap_t picture_heap_t; + +/* Subpictures */ +typedef struct spu_t spu_t; +typedef struct subpicture_t subpicture_t; +typedef struct subpicture_sys_t subpicture_sys_t; +typedef struct subpicture_region_t subpicture_region_t; +typedef struct text_style_t text_style_t; + +typedef struct image_handler_t image_handler_t; + +/* Stream output */ +typedef struct sout_instance_t sout_instance_t; +typedef struct sout_instance_sys_t sout_instance_sys_t; + +typedef struct sout_input_t sout_input_t; +typedef struct sout_packetizer_input_t sout_packetizer_input_t; + +typedef struct sout_access_out_t sout_access_out_t; +typedef struct sout_access_out_sys_t sout_access_out_sys_t; + +typedef struct sout_mux_t sout_mux_t; +typedef struct sout_mux_sys_t sout_mux_sys_t; + +typedef struct sout_stream_t sout_stream_t; +typedef struct sout_stream_sys_t sout_stream_sys_t; + +typedef struct config_chain_t config_chain_t; +typedef struct sap_session_t sap_session_t; +typedef struct sap_address_t sap_address_t; +typedef struct session_descriptor_t session_descriptor_t; +typedef struct announce_method_t announce_method_t; +typedef struct announce_handler_t announce_handler_t; +typedef struct sap_handler_t sap_handler_t; + +typedef struct sout_param_t sout_param_t; +typedef struct sout_pcat_t sout_pcat_t; +typedef struct sout_std_t sout_std_t; +typedef struct sout_display_t sout_display_t; +typedef struct sout_duplicate_t sout_duplicate_t; +typedef struct sout_transcode_t sout_transcode_t; +typedef struct sout_chain_t sout_chain_t; +typedef struct streaming_profile_t streaming_profile_t; +typedef struct sout_module_t sout_module_t; +typedef struct sout_gui_descr_t sout_gui_descr_t; +typedef struct profile_parser_t profile_parser_t; + +/* Decoders */ +typedef struct decoder_t decoder_t; +typedef struct decoder_sys_t decoder_sys_t; +typedef struct decoder_synchro_t decoder_synchro_t; + +/* Encoders */ +typedef struct encoder_t encoder_t; +typedef struct encoder_sys_t encoder_sys_t; + +/* Filters */ +typedef struct filter_t filter_t; +typedef struct filter_sys_t filter_sys_t; + +/* Network */ +typedef struct network_socket_t network_socket_t; +typedef struct virtual_socket_t v_socket_t; +typedef struct sockaddr sockaddr; +typedef struct addrinfo addrinfo; +typedef struct vlc_acl_t vlc_acl_t; +typedef struct vlc_url_t vlc_url_t; + +/* Misc */ +typedef struct iso639_lang_t iso639_lang_t; +typedef struct device_t device_t; +typedef struct device_probe_t device_probe_t; +typedef struct probe_sys_t probe_sys_t; + +/* block */ +typedef struct block_t block_t; +typedef struct block_fifo_t block_fifo_t; + +/* httpd */ +typedef struct httpd_t httpd_t; +typedef struct httpd_host_t httpd_host_t; +typedef struct httpd_url_t httpd_url_t; +typedef struct httpd_client_t httpd_client_t; +typedef struct httpd_callback_sys_t httpd_callback_sys_t; +typedef struct httpd_message_t httpd_message_t; +typedef int (*httpd_callback_t)( httpd_callback_sys_t *, httpd_client_t *, httpd_message_t *answer, const httpd_message_t *query ); +typedef struct httpd_file_t httpd_file_t; +typedef struct httpd_file_sys_t httpd_file_sys_t; +typedef int (*httpd_file_callback_t)( httpd_file_sys_t *, httpd_file_t *, uint8_t *psz_request, uint8_t **pp_data, int *pi_data ); +typedef struct httpd_handler_t httpd_handler_t; +typedef struct httpd_handler_sys_t httpd_handler_sys_t; +typedef int (*httpd_handler_callback_t)( httpd_handler_sys_t *, httpd_handler_t *, char *psz_url, uint8_t *psz_request, int i_type, uint8_t *p_in, int i_in, char *psz_remote_addr, char *psz_remote_host, uint8_t **pp_data, int *pi_data ); +typedef struct httpd_redirect_t httpd_redirect_t; +typedef struct httpd_stream_t httpd_stream_t; + +/* TLS support */ +typedef struct tls_server_t tls_server_t; +typedef struct tls_session_t tls_session_t; + +/* Hashing */ +typedef struct md5_s md5_t; + +/* XML */ +typedef struct xml_t xml_t; +typedef struct xml_sys_t xml_sys_t; +typedef struct xml_reader_t xml_reader_t; +typedef struct xml_reader_sys_t xml_reader_sys_t; + +/* vod server */ +typedef struct vod_t vod_t; +typedef struct vod_sys_t vod_sys_t; +typedef struct vod_media_t vod_media_t; + +/* opengl */ +typedef struct opengl_t opengl_t; +typedef struct opengl_sys_t opengl_sys_t; + +/* osdmenu */ +typedef struct osd_menu_t osd_menu_t; +typedef struct osd_state_t osd_state_t; +typedef struct osd_event_t osd_event_t; +typedef struct osd_button_t osd_button_t; +typedef struct osd_menu_state_t osd_menu_state_t; + +/* VLM */ +typedef struct vlm_t vlm_t; +typedef struct vlm_message_t vlm_message_t; + +/* divers */ +typedef struct vlc_meta_t vlc_meta_t; +typedef struct meta_export_t meta_export_t; + +/* Stats */ +typedef struct counter_t counter_t; +typedef struct counter_sample_t counter_sample_t; +typedef struct stats_handler_t stats_handler_t; +typedef struct input_stats_t input_stats_t; +typedef struct global_stats_t global_stats_t; + +/* Update */ +typedef struct update_t update_t; +typedef struct update_iterator_t update_iterator_t; + +/* Meta engine */ +typedef struct meta_engine_t meta_engine_t; + +/* stat/lstat/fstat */ +#ifdef WIN32 +#include +struct _stati64; +#define stat _stati64 +#define fstat _fstati64 +/* You should otherwise use utf8_stat and utf8_lstat. */ +#else +struct stat; +#endif + +/** + * VLC value structure + */ +typedef union +{ + int i_int; + bool b_bool; + float f_float; + char * psz_string; + void * p_address; + vlc_object_t * p_object; + vlc_list_t * p_list; + mtime_t i_time; + + struct { char *psz_name; int i_object_id; } var; + + /* Make sure the structure is at least 64bits */ + struct { char a, b, c, d, e, f, g, h; } padding; + +} vlc_value_t; + +/** + * VLC list structure + */ +struct vlc_list_t +{ + int i_count; + vlc_value_t * p_values; + int * pi_types; + +}; + +/** + * \defgroup var_type Variable types + * These are the different types a vlc variable can have. + * @{ + */ +#define VLC_VAR_VOID 0x0010 +#define VLC_VAR_BOOL 0x0020 +#define VLC_VAR_INTEGER 0x0030 +#define VLC_VAR_HOTKEY 0x0031 +#define VLC_VAR_STRING 0x0040 +#define VLC_VAR_MODULE 0x0041 +#define VLC_VAR_FILE 0x0042 +#define VLC_VAR_DIRECTORY 0x0043 +#define VLC_VAR_VARIABLE 0x0044 +#define VLC_VAR_FLOAT 0x0050 +#define VLC_VAR_TIME 0x0060 +#define VLC_VAR_ADDRESS 0x0070 +#define VLC_VAR_MUTEX 0x0080 +#define VLC_VAR_LIST 0x0090 +/**@}*/ + +/***************************************************************************** + * Error values (shouldn't be exposed) + *****************************************************************************/ +#define VLC_SUCCESS -0 /* No error */ +#define VLC_ENOMEM -1 /* Not enough memory */ +#define VLC_ETHREAD -2 /* Thread error */ +#define VLC_ETIMEOUT -3 /* Timeout */ + +#define VLC_ENOMOD -10 /* Module not found */ + +#define VLC_ENOOBJ -20 /* Object not found */ + +#define VLC_ENOVAR -30 /* Variable not found */ +#define VLC_EBADVAR -31 /* Bad variable value */ + +#define VLC_ENOITEM -40 /**< Item not found */ + +#define VLC_EEXIT -255 /* Program exited */ +#define VLC_EEXITSUCCESS -999 /* Program exited successfully */ +#define VLC_EGENERIC -666 /* Generic error */ + +/***************************************************************************** + * Variable callbacks + *****************************************************************************/ +typedef int ( * vlc_callback_t ) ( vlc_object_t *, /* variable's object */ + char const *, /* variable name */ + vlc_value_t, /* old value */ + vlc_value_t, /* new value */ + void * ); /* callback data */ + +/***************************************************************************** + * Plug-in stuff + *****************************************************************************/ + +#ifdef __cplusplus +# define LIBVLC_EXTERN extern "C" +#else +# define LIBVLC_EXTERN extern +#endif +#if defined (WIN32) && defined (DLL_EXPORT) +# define LIBVLC_EXPORT __declspec(dllexport) +#else +# define LIBVLC_EXPORT +#endif +#define VLC_EXPORT( type, name, args ) \ + LIBVLC_EXTERN LIBVLC_EXPORT type name args + +/***************************************************************************** + * OS-specific headers and thread types + *****************************************************************************/ +#if defined( WIN32 ) || defined( UNDER_CE ) +# define WIN32_LEAN_AND_MEAN +# include +# if defined( UNDER_CE ) +# define IS_WINNT 0 +# else +# define IS_WINNT ( GetVersion() < 0x80000000 ) +# endif +#endif + +#include "vlc_mtime.h" +#include "vlc_threads.h" + +typedef struct vlc_object_internals_t vlc_object_internals_t; + +/***************************************************************************** + * Common structure members + *****************************************************************************/ + +/* VLC_COMMON_MEMBERS : members common to all basic vlc objects */ +#define VLC_COMMON_MEMBERS \ +/** \name VLC_COMMON_MEMBERS \ + * these members are common for all vlc objects \ + */ \ +/**@{*/ \ + int i_object_id; \ + int i_object_type; \ + const char *psz_object_type; \ + char *psz_object_name; \ + \ + /* Messages header */ \ + char *psz_header; \ + int i_flags; \ + \ + /* Object properties */ \ + volatile bool b_error; /**< set by the object */ \ + volatile bool b_die; /**< set by the outside */ \ + volatile bool b_dead; /**< set by the object */ \ + bool b_force; /**< set by the outside (eg. module_Need()) */ \ + \ + /* Stuff related to the libvlc structure */ \ + libvlc_int_t *p_libvlc; /**< (root of all evil) - 1 */ \ + \ + vlc_object_t * p_parent; /**< our parent */ \ + \ + /* Private data */ \ + void * p_private; \ + \ + /** Just a reminder so that people don't cast garbage */ \ + int be_sure_to_add_VLC_COMMON_MEMBERS_to_struct; \ +/**@}*/ \ + +/* VLC_OBJECT: attempt at doing a clever cast */ +#ifdef __GNUC__ +# define VLC_OBJECT( x ) \ + (((vlc_object_t *)(x))+0*(((__typeof__(x))0)->be_sure_to_add_VLC_COMMON_MEMBERS_to_struct)) +#else +# define VLC_OBJECT( x ) ((vlc_object_t *)(x)) +#endif + +#define VLC_GC_MEMBERS \ +/** \name VLC_GC_MEMBERS \ + * these members are common to all objects that wish to be garbage-collected \ + */ \ +/**@{*/ \ + int i_gc_refcount; \ + void (*pf_destructor) ( gc_object_t * ); \ + void *p_destructor_arg; \ +/**@}*/ + +struct gc_object_t +{ + VLC_GC_MEMBERS +}; + +VLC_EXPORT(void, __vlc_gc_incref, ( gc_object_t * p_gc )); +VLC_EXPORT(void, __vlc_gc_decref, ( gc_object_t * p_gc )); +VLC_EXPORT(void, __vlc_gc_init, ( gc_object_t * p_gc, + void (*pf_destructor)( gc_object_t * ), void * arg)); + +#define vlc_gc_incref( a ) __vlc_gc_incref( (gc_object_t *)a ) +#define vlc_gc_decref( a ) __vlc_gc_decref( (gc_object_t *)a ) +#define vlc_gc_init( a,b,c ) __vlc_gc_init( (gc_object_t *)a,b,c ) + + +/***************************************************************************** + * Macros and inline functions + *****************************************************************************/ + +/* CEIL: division with round to nearest greater integer */ +#define CEIL(n, d) ( ((n) / (d)) + ( ((n) % (d)) ? 1 : 0) ) + +/* PAD: PAD(n, d) = CEIL(n ,d) * d */ +#define PAD(n, d) ( ((n) % (d)) ? ((((n) / (d)) + 1) * (d)) : (n) ) + +/* __MAX and __MIN: self explanatory */ +#ifndef __MAX +# define __MAX(a, b) ( ((a) > (b)) ? (a) : (b) ) +#endif +#ifndef __MIN +# define __MIN(a, b) ( ((a) < (b)) ? (a) : (b) ) +#endif + +static inline int64_t GCD( int64_t a, int64_t b ) +{ + while( b ) + { + int64_t c = a % b; + a = b; + b = c; + } + return a; +} + +/* function imported from libavutil/common.h */ +static inline uint8_t clip_uint8_vlc( int32_t a ) +{ + if( a&(~255) ) return (-a)>>31; + else return a; +} + +/* Malloc with automatic error */ +#define MALLOC_VOID( var, type ) do { var = (type*)malloc( sizeof( type) ); \ + if( !var ) return; } while(0) +#define MALLOC_NULL( var, type ) do { var = (type*)malloc( sizeof( type) ); \ + if( !var ) return NULL; } while(0) +#define MALLOC_ERR( var, type ) do { var = (type*)malloc( sizeof( type) ); \ + if( !var ) return VLC_ENOMEM; } while(0) +#define MALLOC_GOTOERR( var, type ) do { var = (type*)malloc( sizeof( type) ); \ + if( !var ) goto error; } while(0) +#define DECMALLOC_VOID( var, type ) type* var = (type*)malloc( sizeof(type) );\ + if( !var ) return; +#define DECMALLOC_ERR( var, type ) type* var = (type*)malloc( sizeof(type) );\ + if( !var ) return VLC_ENOMEM; +#define DECMALLOC_NULL( var, type ) type* var = (type*)malloc( sizeof(type) );\ + if( !var ) return NULL; + +#define FREENULL(a) do { free( a ); a = NULL; } while(0) + +#define EMPTY_STR(str) (!str || !*str) + +VLC_EXPORT( char const *, vlc_error, ( int ) ); + +#include "vlc_arrays.h" + +/* MSB (big endian)/LSB (little endian) conversions - network order is always + * MSB, and should be used for both network communications and files. */ +static inline uint16_t U16_AT( const void * _p ) +{ + const uint8_t * p = (const uint8_t *)_p; + return ( ((uint16_t)p[0] << 8) | p[1] ); +} +static inline uint32_t U32_AT( const void * _p ) +{ + const uint8_t * p = (const uint8_t *)_p; + return ( ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) + | ((uint32_t)p[2] << 8) | p[3] ); +} +static inline uint64_t U64_AT( const void * _p ) +{ + const uint8_t * p = (const uint8_t *)_p; + return ( ((uint64_t)p[0] << 56) | ((uint64_t)p[1] << 48) + | ((uint64_t)p[2] << 40) | ((uint64_t)p[3] << 32) + | ((uint64_t)p[4] << 24) | ((uint64_t)p[5] << 16) + | ((uint64_t)p[6] << 8) | p[7] ); +} + +static inline uint16_t GetWLE( const void * _p ) +{ + const uint8_t * p = (const uint8_t *)_p; + return ( ((uint16_t)p[1] << 8) | p[0] ); +} +static inline uint32_t GetDWLE( const void * _p ) +{ + const uint8_t * p = (const uint8_t *)_p; + return ( ((uint32_t)p[3] << 24) | ((uint32_t)p[2] << 16) + | ((uint32_t)p[1] << 8) | p[0] ); +} +static inline uint64_t GetQWLE( const void * _p ) +{ + const uint8_t * p = (const uint8_t *)_p; + return ( ((uint64_t)p[7] << 56) | ((uint64_t)p[6] << 48) + | ((uint64_t)p[5] << 40) | ((uint64_t)p[4] << 32) + | ((uint64_t)p[3] << 24) | ((uint64_t)p[2] << 16) + | ((uint64_t)p[1] << 8) | p[0] ); +} + +#define GetWBE( p ) U16_AT( p ) +#define GetDWBE( p ) U32_AT( p ) +#define GetQWBE( p ) U64_AT( p ) + +/* Helper writer functions */ +#define SetWLE( p, v ) _SetWLE( (uint8_t*)(p), v) +static inline void _SetWLE( uint8_t *p, uint16_t i_dw ) +{ + p[1] = ( i_dw >> 8 )&0xff; + p[0] = ( i_dw )&0xff; +} + +#define SetDWLE( p, v ) _SetDWLE( (uint8_t*)(p), v) +static inline void _SetDWLE( uint8_t *p, uint32_t i_dw ) +{ + p[3] = ( i_dw >> 24 )&0xff; + p[2] = ( i_dw >> 16 )&0xff; + p[1] = ( i_dw >> 8 )&0xff; + p[0] = ( i_dw )&0xff; +} +#define SetQWLE( p, v ) _SetQWLE( (uint8_t*)(p), v) +static inline void _SetQWLE( uint8_t *p, uint64_t i_qw ) +{ + SetDWLE( p, i_qw&0xffffffff ); + SetDWLE( p+4, ( i_qw >> 32)&0xffffffff ); +} +#define SetWBE( p, v ) _SetWBE( (uint8_t*)(p), v) +static inline void _SetWBE( uint8_t *p, uint16_t i_dw ) +{ + p[0] = ( i_dw >> 8 )&0xff; + p[1] = ( i_dw )&0xff; +} + +#define SetDWBE( p, v ) _SetDWBE( (uint8_t*)(p), v) +static inline void _SetDWBE( uint8_t *p, uint32_t i_dw ) +{ + p[0] = ( i_dw >> 24 )&0xff; + p[1] = ( i_dw >> 16 )&0xff; + p[2] = ( i_dw >> 8 )&0xff; + p[3] = ( i_dw )&0xff; +} +#define SetQWBE( p, v ) _SetQWBE( (uint8_t*)(p), v) +static inline void _SetQWBE( uint8_t *p, uint64_t i_qw ) +{ + SetDWBE( p+4, i_qw&0xffffffff ); + SetDWBE( p, ( i_qw >> 32)&0xffffffff ); +} + +#define hton16(i) htons(i) +#define hton32(i) htonl(i) +#define ntoh16(i) ntohs(i) +#define ntoh32(i) ntohl(i) + +static inline uint64_t ntoh64 (uint64_t ll) +{ + union { uint64_t qw; uint8_t b[16]; } v = { ll }; + return ((uint64_t)v.b[0] << 56) + | ((uint64_t)v.b[1] << 48) + | ((uint64_t)v.b[2] << 40) + | ((uint64_t)v.b[3] << 32) + | ((uint64_t)v.b[4] << 24) + | ((uint64_t)v.b[5] << 16) + | ((uint64_t)v.b[6] << 8) + | ((uint64_t)v.b[7] << 0); +} +#define hton64(i) ntoh64(i) + +/* Format string sanity checks */ +#ifdef __GNUC__ +# define LIBVLC_FORMAT(x,y) __attribute__ ((format(printf,x,y))) +#else +# define LIBVLC_FORMAT(x,y) +#endif + +/* */ +#define VLC_UNUSED(x) (void)(x) + +/* Stuff defined in src/extras/libc.c */ +VLC_EXPORT( size_t, vlc_strlcpy, ( char *, const char *, size_t ) ); +VLC_EXPORT( long long, vlc_strtoll, ( const char *nptr, char **endptr, int base ) ); + +VLC_EXPORT( char *, vlc_strcasestr, ( const char *s1, const char *s2 ) ); + +#if defined(WIN32) || defined(UNDER_CE) +/* win32, cl and icl support */ +# if defined( _MSC_VER ) || !defined( __MINGW32__ ) +# define __attribute__(x) +# define __inline__ __inline +# define S_IFBLK 0x3000 /* Block */ +# define S_ISBLK(m) (0) +# define S_ISCHR(m) (0) +# define S_ISFIFO(m) (((m)&_S_IFMT) == _S_IFIFO) +# define S_ISREG(m) (((m)&_S_IFMT) == _S_IFREG) +# endif + +/* several type definitions */ +# if defined( __MINGW32__ ) +# if !defined( _OFF_T_ ) + typedef long long _off_t; + typedef _off_t off_t; +# define _OFF_T_ +# else +# ifdef off_t +# undef off_t +# endif +# define off_t long long +# endif +# endif + +# if defined( _MSC_VER ) && !defined( __WXMSW__ ) +# if !defined( _OFF_T_DEFINED ) + typedef __int64 off_t; +# define _OFF_T_DEFINED +# else + /* for wx compatibility typedef long off_t; */ +# define off_t __int64 +# endif +# endif + +# if defined( __BORLANDC__ ) +# undef off_t +# define off_t unsigned __int64 +# endif + +# ifndef O_NONBLOCK +# define O_NONBLOCK 0 +# endif + +# ifndef alloca +# define alloca _alloca +# endif + + /* These two are not defined in mingw32 (bug?) */ +# ifndef snprintf +# define snprintf _snprintf +# endif +# ifndef vsnprintf +# define vsnprintf _vsnprintf +# endif + +# include +#endif + +VLC_EXPORT( bool, vlc_ureduce, ( unsigned *, unsigned *, uint64_t, uint64_t, uint64_t ) ); + +/* vlc_wraptext (defined in src/extras/libc.c) */ +#define wraptext vlc_wraptext +VLC_EXPORT( char *, vlc_wraptext, ( const char *, int ) ); + +/* iconv wrappers (defined in src/extras/libc.c) */ +typedef void *vlc_iconv_t; +VLC_EXPORT( vlc_iconv_t, vlc_iconv_open, ( const char *, const char * ) ); +VLC_EXPORT( size_t, vlc_iconv, ( vlc_iconv_t, const char **, size_t *, char **, size_t * ) ); +VLC_EXPORT( int, vlc_iconv_close, ( vlc_iconv_t ) ); + +/* execve wrapper (defined in src/extras/libc.c) */ +VLC_EXPORT( int, __vlc_execve, ( vlc_object_t *p_object, int i_argc, char *const *pp_argv, char *const *pp_env, const char *psz_cwd, const char *p_in, size_t i_in, char **pp_data, size_t *pi_data ) ); +#define vlc_execve(a,b,c,d,e,f,g,h,i) __vlc_execve(VLC_OBJECT(a),b,c,d,e,f,g,h,i) + +/* dir wrappers (defined in src/extras/libc.c) */ +VLC_EXPORT(int, vlc_wclosedir, ( void *_p_dir )); + +/***************************************************************************** + * CPU capabilities + *****************************************************************************/ +#define CPU_CAPABILITY_NONE 0 +#define CPU_CAPABILITY_486 (1<<0) +#define CPU_CAPABILITY_586 (1<<1) +#define CPU_CAPABILITY_PPRO (1<<2) +#define CPU_CAPABILITY_MMX (1<<3) +#define CPU_CAPABILITY_3DNOW (1<<4) +#define CPU_CAPABILITY_MMXEXT (1<<5) +#define CPU_CAPABILITY_SSE (1<<6) +#define CPU_CAPABILITY_SSE2 (1<<7) +#define CPU_CAPABILITY_ALTIVEC (1<<16) +#define CPU_CAPABILITY_FPU (1<<31) +VLC_EXPORT( unsigned, vlc_CPU, ( void ) ); + +typedef void *(*vlc_memcpy_t) (void *tgt, const void *src, size_t n); +typedef void *(*vlc_memset_t) (void *tgt, int c, size_t n); + +VLC_EXPORT( void, vlc_fastmem_register, (vlc_memcpy_t cpy, vlc_memset_t set) ); +VLC_EXPORT( void *, vlc_memcpy, ( void *, const void *, size_t ) ); +VLC_EXPORT( void *, vlc_memset, ( void *, int, size_t ) ); + +/***************************************************************************** + * I18n stuff + *****************************************************************************/ +VLC_EXPORT( char *, vlc_gettext, ( const char *msgid ) ); + +/***************************************************************************** + * libvlc features + *****************************************************************************/ +VLC_EXPORT( const char *, VLC_Version, ( void ) ); +VLC_EXPORT( const char *, VLC_CompileBy, ( void ) ); +VLC_EXPORT( const char *, VLC_CompileHost, ( void ) ); +VLC_EXPORT( const char *, VLC_CompileDomain, ( void ) ); +VLC_EXPORT( const char *, VLC_Compiler, ( void ) ); +VLC_EXPORT( const char *, VLC_Error, ( int ) ); +VLC_EXPORT( const char *, VLC_Changeset, ( void ) ); + +/***************************************************************************** + * Additional vlc stuff + *****************************************************************************/ +#include "vlc_messages.h" +#include "vlc_variables.h" +#include "vlc_objects.h" +#include "vlc_modules.h" +#include "vlc_main.h" +#include "vlc_configuration.h" + +#if defined( WIN32 ) || defined( UNDER_CE ) +# define DIR_SEP_CHAR '\\' +# define DIR_SEP "\\" +# define PATH_SEP_CHAR ';' +# define PATH_SEP ";" +#else +# define DIR_SEP_CHAR '/' +# define DIR_SEP "/" +# define PATH_SEP_CHAR ':' +# define PATH_SEP ":" +#endif + +#define LICENSE_MSG \ + _("This program comes with NO WARRANTY, to the extent permitted by " \ + "law.\nYou may redistribute it under the terms of the GNU General " \ + "Public License;\nsee the file named COPYING for details.\n" \ + "Written by the VideoLAN team; see the AUTHORS file.\n") + +#endif /* !VLC_COMMON_H */ diff --git a/VLC/vlc_config.h b/VLC/vlc_config.h new file mode 100644 index 0000000..4a51435 --- /dev/null +++ b/VLC/vlc_config.h @@ -0,0 +1,213 @@ +/***************************************************************************** + * vlc_config.h: limits and configuration + * Defines all compilation-time configuration constants and size limits + ***************************************************************************** + * Copyright (C) 1999-2003 the VideoLAN team + * + * Authors: Vincent Seguin + * Samuel Hocevar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/** + * \file + * This file defines of values used in interface, vout, aout and vlc core functions. + */ + +/* Conventions regarding names of symbols and variables + * ---------------------------------------------------- + * + * - Symbols should begin with a prefix indicating in which module they are + * used, such as INTF_, VOUT_ or AOUT_. + */ + +/***************************************************************************** + * General configuration + *****************************************************************************/ + +#define CLOCK_FREQ 1000000 + + +/* When creating or destroying threads in blocking mode, delay to poll thread + * status */ +#define THREAD_SLEEP ((mtime_t)(0.010*CLOCK_FREQ)) + +/***************************************************************************** + * Interface configuration + *****************************************************************************/ + +/* Base delay in micro second for interface sleeps */ +#define INTF_IDLE_SLEEP ((mtime_t)(0.050*CLOCK_FREQ)) + +/* Step for changing gamma, and minimum and maximum values */ +#define INTF_GAMMA_STEP .1 +#define INTF_GAMMA_LIMIT 3 + +/***************************************************************************** + * Input thread configuration + *****************************************************************************/ + +#define DEFAULT_INPUT_ACTIVITY 1 +#define TRANSCODE_ACTIVITY 10 + +/* Used in ErrorThread */ +#define INPUT_IDLE_SLEEP ((mtime_t)(0.100*CLOCK_FREQ)) + +/* Time to wait in case of read error */ +#define INPUT_ERROR_SLEEP ((mtime_t)(0.10*CLOCK_FREQ)) + +/* Number of read() calls needed until we check the file size through + * fstat() */ +#define INPUT_FSTAT_NB_READS 10 + +/* + * General limitations + */ + +/* Duration between the time we receive the data packet, and the time we will + * mark it to be presented */ +#define DEFAULT_PTS_DELAY (mtime_t)(.3*CLOCK_FREQ) + +/* DVD and VCD devices */ +#if !defined( WIN32 ) && !defined( UNDER_CE ) +# define CD_DEVICE "/dev/cdrom" +# define DVD_DEVICE "/dev/dvd" +#else +# define CD_DEVICE "D:" +# define DVD_DEVICE NULL +#endif +#define VCD_DEVICE CD_DEVICE +#define CDAUDIO_DEVICE CD_DEVICE + +/***************************************************************************** + * Audio configuration + *****************************************************************************/ + +/* Volume */ +/* If you are coding an interface, please see src/audio_output/intf.c */ +#define AOUT_VOLUME_DEFAULT 256 +#define AOUT_VOLUME_STEP 32 +#define AOUT_VOLUME_MAX 1024 +#define AOUT_VOLUME_MIN 0 + +/* Max number of pre-filters per input, and max number of post-filters */ +#define AOUT_MAX_FILTERS 10 + +/* Max number of inputs */ +#define AOUT_MAX_INPUTS 5 + +/* Buffers which arrive in advance of more than AOUT_MAX_ADVANCE_TIME + * will be considered as bogus and be trashed */ +#define AOUT_MAX_ADVANCE_TIME (mtime_t)(DEFAULT_PTS_DELAY * 5) + +/* Buffers which arrive in advance of more than AOUT_MAX_PREPARE_TIME + * will cause the calling thread to sleep */ +#define AOUT_MAX_PREPARE_TIME (mtime_t)(.5*CLOCK_FREQ) + +/* Buffers which arrive after pts - AOUT_MIN_PREPARE_TIME will be trashed + * to avoid too heavy resampling */ +#define AOUT_MIN_PREPARE_TIME (mtime_t)(.04*CLOCK_FREQ) + +/* Max acceptable delay between the coded PTS and the actual presentation + * time, without resampling */ +#define AOUT_PTS_TOLERANCE (mtime_t)(.04*CLOCK_FREQ) + +/* Max acceptable resampling (in %) */ +#define AOUT_MAX_RESAMPLING 10 + +/***************************************************************************** + * Video configuration + *****************************************************************************/ + +/* + * Default settings for video output threads + */ + +/* Multiplier value for aspect ratio calculation (2^7 * 3^3 * 5^3) */ +#define VOUT_ASPECT_FACTOR 432000 + +/* Maximum width of a scaled source picture - this should be relatively high, + * since higher stream values will result in no display at all. */ +#define VOUT_MAX_WIDTH 4096 + +/* Number of planes in a picture */ +#define VOUT_MAX_PLANES 5 + +/* Video heap size - remember that a decompressed picture is big + * (~1 Mbyte) before using huge values */ +#define VOUT_MAX_PICTURES 8 + +/* Minimum number of direct pictures the video output will accept without + * creating additional pictures in system memory */ +#define VOUT_MIN_DIRECT_PICTURES 6 + +/* Number of simultaneous subpictures */ +#define VOUT_MAX_SUBPICTURES 8 + +/* Statistics are displayed every n loops (=~ pictures) */ +#define VOUT_STATS_NB_LOOPS 100 + +/* + * Time settings + */ + +/* Time during which the thread will sleep if it has nothing to + * display (in micro-seconds) */ +#define VOUT_IDLE_SLEEP ((int)(0.020*CLOCK_FREQ)) + +/* Maximum lap of time allowed between the beginning of rendering and + * display. If, compared to the current date, the next image is too + * late, the thread will perform an idle loop. This time should be + * at least VOUT_IDLE_SLEEP plus the time required to render a few + * images, to avoid trashing of decoded images */ +#define VOUT_DISPLAY_DELAY ((int)(0.200*CLOCK_FREQ)) + +/* Pictures which are VOUT_BOGUS_DELAY or more in advance probably have + * a bogus PTS and won't be displayed */ +#define VOUT_BOGUS_DELAY ((mtime_t)(DEFAULT_PTS_DELAY * 30)) + +/* Delay (in microseconds) before an idle screen is displayed */ +#define VOUT_IDLE_DELAY (5*CLOCK_FREQ) + +/* Number of pictures required to computes the FPS rate */ +#define VOUT_FPS_SAMPLES 20 + +/* Better be in advance when awakening than late... */ +#define VOUT_MWAIT_TOLERANCE ((mtime_t)(0.020*CLOCK_FREQ)) + +/* Time to sleep when waiting for a buffer (from vout or the video fifo). + * It should be approximately the time needed to perform a complete picture + * loop. Since it only happens when the video heap is full, it does not need + * to be too low, even if it blocks the decoder. */ +#define VOUT_OUTMEM_SLEEP ((mtime_t)(0.020*CLOCK_FREQ)) + +/* The default video output window title */ +#define VOUT_TITLE "VLC" + +/***************************************************************************** + * Messages and console interfaces configuration + *****************************************************************************/ + +/* Maximal size of a message to be stored in the mesage queue, + * it is needed when vasprintf is not available */ +#define INTF_MAX_MSG_SIZE 512 + +/* Maximal size of the message queue - in case of overflow, all messages in the + * queue are printed, but not sent to the threads */ +#define VLC_MSG_QSIZE 256 + +/* Maximal depth of the object tree output by vlc_dumpstructure */ +#define MAX_DUMPSTRUCTURE_DEPTH 100 diff --git a/VLC/vlc_config_cat.h b/VLC/vlc_config_cat.h new file mode 100644 index 0000000..9696abc --- /dev/null +++ b/VLC/vlc_config_cat.h @@ -0,0 +1,323 @@ +/***************************************************************************** + * vlc_config_cat.h : Definition of configuration categories + ***************************************************************************** + * Copyright (C) 2003 the VideoLAN team + * $Id$ + * + * Authors: Clément Stenac + * Anil Daoud + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_HELP_H +#define VLC_HELP_H 1 + +/* + * First, we need help strings for the General Settings and for the + * Plugins screen + */ +#define MAIN_TITLE N_( "VLC preferences" ) +#define MAIN_HELP N_( \ + "Select \"Advanced Options\" to see all options." ) + +#define GENERAL_TITLE N_("General") + +/* Interface */ +#define INTF_TITLE N_("Interface") +#define INTF_HELP N_( "Settings for VLC's interfaces" ) + +#define INTF_GENERAL_HELP N_( "General interface settings" ) + +#define INTF_MAIN_TITLE N_( "Main interfaces" ) +#define INTF_MAIN_HELP N_( "Settings for the main interface" ) + +#define INTF_CONTROL_TITLE N_( "Control interfaces" ) +#define INTF_CONTROL_HELP N_( "Settings for VLC's control interfaces" ) + +#define INTF_HOTKEYS_TITLE N_( "Hotkeys settings" ) +#define INTF_HOTKEYS_HELP N_( "Hotkeys settings" ) + +/* Audio */ +#define AUDIO_TITLE N_( "Audio" ) +#define AUDIO_HELP N_( "Audio settings" ) + +#define AUDIO_GENERAL_TITLE N_( "General audio settings" ) +#define AUDIO_GENERAL_HELP N_("General audio settings") + +#define AFILTER_TITLE N_("Filters") +#define AFILTER_HELP N_( \ + "Audio filters are used to postprocess the audio stream." ) + +#define AVISUAL_TITLE N_("Visualizations") +#define AVISUAL_HELP N_( \ + "Audio visualizations" ) + +#define AOUT_TITLE N_( "Output modules" ) +#define AOUT_HELP N_("These are general settings for audio output modules.") + +#define AMISC_TITLE N_("Miscellaneous") +#define AMISC_HELP N_( "Miscellaneous audio settings and modules." ) + +/* Video */ +#define VIDEO_TITLE N_("Video") +#define VIDEO_HELP N_("Video settings") + +#define VIDEO_GENERAL_TITLE N_( "General video settings") +#define VIDEO_GENERAL_HELP N_( "General video settings" ) + +#define _VOUT_TITLE N_("Output modules" ) +#define VOUT_HELP N_( \ + "Choose your preferred video output and configure it here." ) + +#define VFILTER_TITLE N_("Filters" ) +#define VFILTER_HELP N_( \ + "Video filters are used to postprocess the video stream." ) + +#define SUBPIC_TITLE N_( "Subtitles/OSD") +#define SUBPIC_HELP N_( "Miscellaneous settings related to On-Screen-Display,"\ + " subtitles and \"overlay subpictures\".") +/* +#define TEXT_TITLE N_("Text rendering") +#define TEXT_HELP N_( \ + "Use the settings of the \"freetype\" module to choose the font you " \ + "want VLC to use for text rendering (to display subtitles for example).") +*/ +/* Input */ +#define INPUT_TITLE N_( "Input / Codecs" ) +#define INPUT_HELP N_( "These are the settings for the input, demultiplexing " \ + "and decoding parts of VLC. Encoder settings can also be found here." ) + +#define ACCESS_TITLE N_( "Access modules" ) +#define ACCESS_HELP N_( \ + "Settings related to the various access methods used by VLC. " \ + "Common settings you may want to alter are HTTP proxy or " \ + "caching settings." ) + +#define ACCESS_FILTER_TITLE N_( "Access filters" ) +#define ACCESS_FILTER_HELP N_( \ + "Access filters are special modules that allow advanced operations on " \ + "the input side of VLC. You should not touch anything here unless you " \ + "know what you are doing." ) + +#define DEMUX_TITLE N_("Demuxers") +#define DEMUX_HELP N_( "Demuxers are used to separate audio and video streams." ) + +#define VDEC_TITLE N_( "Video codecs" ) +#define VDEC_HELP N_( "Settings for the video-only decoders and encoders." ) + +#define ADEC_TITLE N_( "Audio codecs" ) +#define ADEC_HELP N_( "Settings for the audio-only decoders and encoders." ) + +#define SDEC_TITLE N_( "Other codecs") +#define SDEC_HELP N_( "Settings for audio+video and miscellaneous decoders and encoders." ) + +#define ADVANCED_TITLE N_("General") +#define ADVANCED_HELP N_( "General input settings. Use with care." ) + +/* Sout */ +#define SOUT_TITLE N_( "Stream output" ) +#define SOUT_HELP N_( \ + "Stream output is what allows VLC to act as a streaming server " \ + "or to save incoming streams.\n" \ + "Streams are first muxed and then sent through an \"access output\" "\ + "module that can either save the stream to a file, or stream " \ + "it (UDP, HTTP, RTP/RTSP).\n" \ + "Sout streams modules allow advanced stream processing (transcoding, "\ + "duplicating...).") + +#define SOUT_GENERAL_HELP N_( "General stream output settings") + +#define SOUT_MUX_TITLE N_( "Muxers" ) +#define SOUT_MUX_HELP N_( \ + "Muxers create the encapsulation formats that are used to " \ + "put all the elementary streams (video, audio, ...) " \ + "together. This setting allows you to always force a specific muxer. " \ + "You should probably not do that.\n" \ + "You can also set default parameters for each muxer." ) + +#define SOUT_ACO_TITLE N_( "Access output" ) +#define SOUT_ACO_HELP N_( \ + "Access output modules control the ways the muxed streams are sent. " \ + "This setting allows you to always force a specific access output method. " \ + "You should probably not do that.\n" \ + "You can also set default parameters for each access output.") + +#define SOUT_PACKET_TITLE N_( "Packetizers" ) +#define SOUT_PACKET_HELP N_( \ + "Packetizers are used to \"preprocess\" the elementary "\ + "streams before muxing. " \ + "This setting allows you to always force a packetizer. " \ + "You should probably not do that.\n" \ + "You can also set default parameters for each packetizer." ) + +#define SOUT_STREAM_TITLE N_("Sout stream") +#define SOUT_STREAM_HELP N_( "Sout stream modules allow to build a sout " \ + "processing chain. Please refer to the Streaming Howto for " \ + "more information. You can configure default options for " \ + "each sout stream module here.") + +#define SOUT_SAP_TITLE N_( "SAP" ) +#define SOUT_SAP_HELP N_( \ + "SAP is a way to publically announce streams that are being "\ + "sent using multicast UDP or RTP." ) + +#define SOUT_VOD_TITLE N_( "VOD" ) +#define SOUT_VOD_HELP N_( "VLC's implementation of Video On Demand" ) + + +/* Playlist */ +#define PLAYLIST_TITLE N_( "Playlist" ) +#define PLAYLIST_HELP N_( "Settings related to playlist behaviour " \ + "(e.g. playback mode) and to modules that automatically add "\ + "items to the playlist (\"service discovery\" modules).") + +#define PGENERAL_HELP N_( "General playlist behaviour") +#define SD_TITLE N_("Services discovery") +#define SD_HELP N_("Services discovery modules are facilities "\ + "that automatically add items to playlist.") + +/* Advanced */ +#define AADVANCED_TITLE N_( "Advanced" ) +#define AADVANCED_HELP N_( "Advanced settings. Use with care.") + +#define CPU_TITLE N_( "CPU features" ) +#define CPU_HELP N_( "You can choose to disable some CPU accelerations " \ + "here. You should probably not change these settings." ) + +#define MISC_TITLE N_( "Advanced settings" ) +#define MISC_HELP N_( "Other advanced settings") + +#define NETWORK_TITLE N_( "Network" ) +#define NETWORK_HELP N_( "These modules provide network functions to all " \ + "other parts of VLC." ) + +/* OLD */ + +#define CHROMA_TITLE N_("Chroma modules settings") +#define CHROMA_HELP N_("These settings affect chroma transformation modules.") + +#define PACKETIZER_TITLE N_("Packetizer modules settings" ) +#define PACKETIZER_HELP "These are general settings for the "\ + "packetizers used in VLC's stream output subsystem." + +#define ENCODER_TITLE N_("Encoders settings") +#define ENCODER_HELP N_( \ + "These are general settings for video/audio/subtitles encoding modules.") + + +#define DIALOGS_TITLE N_("Dialog providers settings") +#define DIALOGS_HELP N_( \ + "Dialog providers can be configured here.") + +#define SUBTITLE_DEMUX_TITLE N_("Subtitle demuxer settings") +#define SUBTITLE_DEMUX_HELP N_( \ + "In this section you can force the behavior of the subtitle demuxer, " \ + "for example by setting the subtitles type or file name.") + +/* + * A little help for modules with unknown capabilities + */ + +#define UNKNOWN_TITLE N_("No help available" ) +#define UNKNOWN_HELP N_("There is no help available for these modules.") + +/* This function is deprecated and is kept only for compatibility */ +static inline const char * GetCapabilityHelp( char *psz_capability, int i_type) +{ + (void)psz_capability; (void)i_type; + return " "; +} + +static const struct config_category_t categories_array[] = +{ + /* Interface */ + { CAT_INTERFACE, INTF_TITLE, INTF_HELP }, + { SUBCAT_INTERFACE_GENERAL, GENERAL_TITLE, INTF_GENERAL_HELP }, + { SUBCAT_INTERFACE_MAIN, INTF_MAIN_TITLE, INTF_MAIN_HELP }, + { SUBCAT_INTERFACE_CONTROL, INTF_CONTROL_TITLE, INTF_CONTROL_HELP }, + { SUBCAT_INTERFACE_HOTKEYS, INTF_HOTKEYS_TITLE, INTF_HOTKEYS_HELP }, + + { CAT_AUDIO, AUDIO_TITLE, AUDIO_HELP }, + { SUBCAT_AUDIO_GENERAL, AUDIO_GENERAL_TITLE, AUDIO_GENERAL_HELP }, + { SUBCAT_AUDIO_AOUT, AOUT_TITLE, AOUT_HELP }, + { SUBCAT_AUDIO_AFILTER, AFILTER_TITLE, AFILTER_HELP }, + { SUBCAT_AUDIO_VISUAL, AVISUAL_TITLE, AVISUAL_HELP }, + { SUBCAT_AUDIO_MISC, AMISC_TITLE, AMISC_HELP }, + + { CAT_VIDEO, VIDEO_TITLE, VIDEO_HELP }, + { SUBCAT_VIDEO_GENERAL, VIDEO_GENERAL_TITLE, VIDEO_GENERAL_HELP }, + { SUBCAT_VIDEO_VOUT, _VOUT_TITLE, VOUT_HELP }, + { SUBCAT_VIDEO_VFILTER, VFILTER_TITLE, VFILTER_HELP }, + { SUBCAT_VIDEO_SUBPIC, SUBPIC_TITLE, SUBPIC_HELP }, + + { CAT_INPUT, INPUT_TITLE, INPUT_HELP }, + { SUBCAT_INPUT_GENERAL, ADVANCED_TITLE, ADVANCED_HELP }, + { SUBCAT_INPUT_ACCESS, ACCESS_TITLE, ACCESS_HELP }, + { SUBCAT_INPUT_ACCESS_FILTER, ACCESS_FILTER_TITLE, ACCESS_FILTER_HELP }, + { SUBCAT_INPUT_DEMUX, DEMUX_TITLE, DEMUX_HELP }, + { SUBCAT_INPUT_VCODEC, VDEC_TITLE, VDEC_HELP }, + { SUBCAT_INPUT_ACODEC, ADEC_TITLE, ADEC_HELP }, + { SUBCAT_INPUT_SCODEC, SDEC_TITLE, SDEC_HELP }, + + { CAT_SOUT, SOUT_TITLE, SOUT_HELP }, + { SUBCAT_SOUT_GENERAL, GENERAL_TITLE, SOUT_GENERAL_HELP }, + { SUBCAT_SOUT_STREAM, SOUT_STREAM_TITLE, SOUT_STREAM_HELP }, + { SUBCAT_SOUT_MUX, SOUT_MUX_TITLE, SOUT_MUX_HELP }, + { SUBCAT_SOUT_ACO, SOUT_ACO_TITLE, SOUT_ACO_HELP }, + { SUBCAT_SOUT_PACKETIZER, SOUT_PACKET_TITLE, SOUT_PACKET_HELP }, + { SUBCAT_SOUT_SAP, SOUT_SAP_TITLE, SOUT_SAP_HELP }, + { SUBCAT_SOUT_VOD, SOUT_VOD_TITLE, SOUT_VOD_HELP }, + + { CAT_PLAYLIST, PLAYLIST_TITLE , PLAYLIST_HELP }, + { SUBCAT_PLAYLIST_GENERAL, GENERAL_TITLE, PGENERAL_HELP }, + { SUBCAT_PLAYLIST_SD, SD_TITLE, SD_HELP }, + + { CAT_ADVANCED, AADVANCED_TITLE, AADVANCED_HELP }, + { SUBCAT_ADVANCED_CPU, CPU_TITLE, CPU_HELP }, + { SUBCAT_ADVANCED_MISC, MISC_TITLE, MISC_HELP }, + + { -1, NULL, NULL } +}; + +static inline const char *config_CategoryNameGet( int i_value ) +{ + int i = 0 ; + while( categories_array[i].psz_name != NULL ) + { + if( categories_array[i].i_id == i_value ) + { + return _(categories_array[i].psz_name); + } + i++; + } + return NULL; +} + +static inline const char *config_CategoryHelpGet( int i_value ) +{ + int i = 0 ; + while( categories_array[i].psz_help != NULL ) + { + if( categories_array[i].i_id == i_value ) + { + return _(categories_array[i].psz_help); + } + i++; + } + return NULL; +} + +#endif /* VLC_HELP_H */ diff --git a/VLC/vlc_configuration.h b/VLC/vlc_configuration.h new file mode 100644 index 0000000..36d46cb --- /dev/null +++ b/VLC/vlc_configuration.h @@ -0,0 +1,261 @@ +/***************************************************************************** + * configuration.h : configuration management module + * This file describes the programming interface for the configuration module. + * It includes functions allowing to declare, get or set configuration options. + ***************************************************************************** + * Copyright (C) 1999-2006 the VideoLAN team + * $Id$ + * + * Authors: Gildas Bazin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_CONFIGURATION_H +#define VLC_CONFIGURATION_H 1 + +/** + * \file + * This file describes the programming interface for the configuration module. + * It includes functions allowing to declare, get or set configuration options. + */ + +# ifdef __cplusplus +extern "C" { +# endif + +/***************************************************************************** + * Macros used to build the configuration structure. + *****************************************************************************/ + +/* Configuration hint types */ + + +#define CONFIG_HINT_CATEGORY 0x0002 /* Start of new category */ +#define CONFIG_HINT_SUBCATEGORY 0x0003 /* Start of sub-category */ +#define CONFIG_HINT_SUBCATEGORY_END 0x0004 /* End of sub-category */ +#define CONFIG_HINT_USAGE 0x0005 /* Usage information */ + +#define CONFIG_CATEGORY 0x0006 /* Set category */ +#define CONFIG_SUBCATEGORY 0x0007 /* Set subcategory */ +#define CONFIG_SECTION 0x0008 /* Start of new section */ + +#define CONFIG_HINT 0x000F + +/* Configuration item types */ +#define CONFIG_ITEM_STRING 0x0010 /* String option */ +#define CONFIG_ITEM_FILE 0x0020 /* File option */ +#define CONFIG_ITEM_MODULE 0x0030 /* Module option */ +#define CONFIG_ITEM_INTEGER 0x0040 /* Integer option */ +#define CONFIG_ITEM_BOOL 0x0050 /* Bool option */ +#define CONFIG_ITEM_FLOAT 0x0060 /* Float option */ +#define CONFIG_ITEM_DIRECTORY 0x0070 /* Directory option */ +#define CONFIG_ITEM_KEY 0x0080 /* Hot key option */ +#define CONFIG_ITEM_MODULE_CAT 0x0090 /* Module option */ +#define CONFIG_ITEM_MODULE_LIST 0x00A0 /* Module option */ +#define CONFIG_ITEM_MODULE_LIST_CAT 0x00B0 /* Module option */ +#define CONFIG_ITEM_FONT 0x00C0 /* Font option */ +#define CONFIG_ITEM_PASSWORD 0x00D0 /* Password option (*) */ + +#define CONFIG_ITEM 0x00F0 + +/******************************************************************* + * All predefined categories and subcategories + *******************************************************************/ +#define CAT_INTERFACE 1 + #define SUBCAT_INTERFACE_GENERAL 101 + #define SUBCAT_INTERFACE_MAIN 102 + #define SUBCAT_INTERFACE_CONTROL 103 + #define SUBCAT_INTERFACE_HOTKEYS 104 + +#define CAT_AUDIO 2 + #define SUBCAT_AUDIO_GENERAL 201 + #define SUBCAT_AUDIO_AOUT 202 + #define SUBCAT_AUDIO_AFILTER 203 + #define SUBCAT_AUDIO_VISUAL 204 + #define SUBCAT_AUDIO_MISC 205 + +#define CAT_VIDEO 3 + #define SUBCAT_VIDEO_GENERAL 301 + #define SUBCAT_VIDEO_VOUT 302 + #define SUBCAT_VIDEO_VFILTER 303 + #define SUBCAT_VIDEO_TEXT 304 + #define SUBCAT_VIDEO_SUBPIC 305 + #define SUBCAT_VIDEO_VFILTER2 306 + +#define CAT_INPUT 4 + #define SUBCAT_INPUT_GENERAL 401 + #define SUBCAT_INPUT_ACCESS 402 + #define SUBCAT_INPUT_ACCESS_FILTER 403 + #define SUBCAT_INPUT_DEMUX 404 + #define SUBCAT_INPUT_VCODEC 405 + #define SUBCAT_INPUT_ACODEC 406 + #define SUBCAT_INPUT_SCODEC 407 + +#define CAT_SOUT 5 + #define SUBCAT_SOUT_GENERAL 501 + #define SUBCAT_SOUT_STREAM 502 + #define SUBCAT_SOUT_MUX 503 + #define SUBCAT_SOUT_ACO 504 + #define SUBCAT_SOUT_PACKETIZER 505 + #define SUBCAT_SOUT_SAP 506 + #define SUBCAT_SOUT_VOD 507 + +#define CAT_ADVANCED 6 + #define SUBCAT_ADVANCED_CPU 601 + #define SUBCAT_ADVANCED_MISC 602 + #define SUBCAT_ADVANCED_NETWORK 603 + #define SUBCAT_ADVANCED_XML 604 + +#define CAT_PLAYLIST 7 + #define SUBCAT_PLAYLIST_GENERAL 701 + #define SUBCAT_PLAYLIST_SD 702 + #define SUBCAT_PLAYLIST_EXPORT 703 + +#define CAT_OSD 8 + #define SUBCAT_OSD_IMPORT 801 + +struct config_category_t +{ + int i_id; + const char *psz_name; + const char *psz_help; +}; + +typedef union +{ + char *psz; + int i; + float f; +} module_value_t; + +typedef union +{ + int i; + float f; +} module_nvalue_t; + +struct module_config_t +{ + int i_type; /* Configuration type */ + char *psz_type; /* Configuration subtype */ + char *psz_name; /* Option name */ + char i_short; /* Optional short option name */ + char *psz_text; /* Short comment on the configuration option */ + char *psz_longtext; /* Long comment on the configuration option */ + module_value_t value; /* Option value */ + module_value_t orig; + module_value_t saved; + module_nvalue_t min; + module_nvalue_t max; + + /* Function to call when commiting a change */ + vlc_callback_t pf_callback; + void *p_callback_data; + + /* Values list */ + char ** ppsz_list; /* List of possible values for the option */ + int *pi_list; /* Idem for integers */ + char **ppsz_list_text; /* Friendly names for list values */ + int i_list; /* Options list size */ + vlc_callback_t pf_update_list; /*callback to initialize dropdownlists */ + + /* Actions list */ + vlc_callback_t *ppf_action; /* List of possible actions for a config */ + char **ppsz_action_text; /* Friendly names for actions */ + int i_action; /* actions list size */ + + /* Misc */ + vlc_mutex_t *p_lock; /* Lock to use when modifying the config */ + bool b_dirty; /* Dirty flag to indicate a config change */ + bool b_advanced; /* Flag to indicate an advanced option */ + bool b_internal; /* Flag to indicate option is not to be shown */ + bool b_restart; /* Flag to indicate the option needs a restart */ + /* to take effect */ + + /* Deprecated */ + char *psz_oldname; /* Old option name */ + bool b_removed; + + /* Option values loaded from config file */ + bool b_autosave; /* Config will be auto-saved at exit time */ + bool b_unsaveable; /* Config should not be saved */ + + bool b_safe; +}; + +/***************************************************************************** + * Prototypes - these methods are used to get, set or manipulate configuration + * data. + *****************************************************************************/ +VLC_EXPORT( int, __config_GetType, (vlc_object_t *, const char *) ); +VLC_EXPORT( int, __config_GetInt, (vlc_object_t *, const char *) ); +VLC_EXPORT( void, __config_PutInt, (vlc_object_t *, const char *, int) ); +VLC_EXPORT( float, __config_GetFloat, (vlc_object_t *, const char *) ); +VLC_EXPORT( void, __config_PutFloat, (vlc_object_t *, const char *, float) ); +VLC_EXPORT( char *, __config_GetPsz, (vlc_object_t *, const char *) ); +VLC_EXPORT( void, __config_PutPsz, (vlc_object_t *, const char *, const char *) ); + +#define config_SaveConfigFile(a,b) __config_SaveConfigFile(VLC_OBJECT(a),b) +VLC_EXPORT( int, __config_SaveConfigFile, ( vlc_object_t *, const char * ) ); +#define config_ResetAll(a) __config_ResetAll(VLC_OBJECT(a)) +VLC_EXPORT( void, __config_ResetAll, ( vlc_object_t * ) ); + +VLC_EXPORT( module_config_t *, config_FindConfig,( vlc_object_t *, const char * ) ); + +VLC_EXPORT(const char *, config_GetDataDir, ( void )); +VLC_EXPORT(const char *, config_GetConfDir, ( void ) ); +VLC_EXPORT(const char *, config_GetHomeDir, ( void )); +VLC_EXPORT(char *, config_GetUserConfDir, ( void ) ); +VLC_EXPORT(char *, config_GetUserDataDir, ( void ) ); +VLC_EXPORT(char *, config_GetCacheDir, ( void ) ); + +VLC_EXPORT( void, __config_AddIntf, ( vlc_object_t *, const char * ) ); +VLC_EXPORT( void, __config_RemoveIntf, ( vlc_object_t *, const char * ) ); +VLC_EXPORT( bool, __config_ExistIntf, ( vlc_object_t *, const char * ) ); + +#define config_GetType(a,b) __config_GetType(VLC_OBJECT(a),b) +#define config_GetInt(a,b) __config_GetInt(VLC_OBJECT(a),b) +#define config_PutInt(a,b,c) __config_PutInt(VLC_OBJECT(a),b,c) +#define config_GetFloat(a,b) __config_GetFloat(VLC_OBJECT(a),b) +#define config_PutFloat(a,b,c) __config_PutFloat(VLC_OBJECT(a),b,c) +#define config_GetPsz(a,b) __config_GetPsz(VLC_OBJECT(a),b) +#define config_PutPsz(a,b,c) __config_PutPsz(VLC_OBJECT(a),b,c) + +#define config_AddIntf(a,b) __config_AddIntf(VLC_OBJECT(a),b) +#define config_RemoveIntf(a,b) __config_RemoveIntf(VLC_OBJECT(a),b) +#define config_ExistIntf(a,b) __config_ExistIntf(VLC_OBJECT(a),b) + +/**************************************************************************** + * config_chain_t: + ****************************************************************************/ +struct config_chain_t +{ + config_chain_t *p_next; + + char *psz_name; + char *psz_value; +}; + +#define config_ChainParse( a, b, c, d ) __config_ChainParse( VLC_OBJECT(a), b, c, d ) +VLC_EXPORT( void, __config_ChainParse, ( vlc_object_t *, const char *psz_prefix, const char *const *ppsz_options, config_chain_t * ) ); +VLC_EXPORT( char *, config_ChainCreate, ( char **, config_chain_t **, const char * ) ); +VLC_EXPORT( void, config_ChainDestroy, ( config_chain_t * ) ); + +# ifdef __cplusplus +} +# endif + +#endif /* _VLC_CONFIGURATION_H */ diff --git a/VLC/vlc_demux.h b/VLC/vlc_demux.h new file mode 100644 index 0000000..db62da8 --- /dev/null +++ b/VLC/vlc_demux.h @@ -0,0 +1,250 @@ +/***************************************************************************** + * vlc_demux.h: Demuxer descriptor, queries and methods + ***************************************************************************** + * Copyright (C) 1999-2005 the VideoLAN team + * $Id$ + * + * Authors: Laurent Aimar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_DEMUX_H +#define VLC_DEMUX_H 1 + +/** + * \file + * This files defines functions and structures used by demux objects in vlc + */ + +#include "vlc_es.h" +#include "vlc_stream.h" +#include "vlc_es_out.h" + +/** + * \defgroup demux Demux + * @{ + */ + +struct demux_t +{ + VLC_COMMON_MEMBERS + + /* Module properties */ + module_t *p_module; + + /* eg informative but needed (we can have access+demux) */ + char *psz_access; + char *psz_demux; + char *psz_path; + + /* input stream */ + stream_t *s; /* NULL in case of a access+demux in one */ + + /* es output */ + es_out_t *out; /* our p_es_out */ + + /* set by demuxer */ + int (*pf_demux) ( demux_t * ); /* demux one frame only */ + int (*pf_control)( demux_t *, int i_query, va_list args); + + /* Demux has to maintain them uptodate + * when it is responsible of seekpoint/title */ + struct + { + unsigned int i_update; /* Demux sets them on change, + Input removes them once take into account*/ + /* Seekpoint/Title at demux level */ + int i_title; /* idem, start from 0 (could be menu) */ + int i_seekpoint; /* idem, start from 0 */ + } info; + demux_sys_t *p_sys; +}; + + +/* demux_meta_t is returned by "meta reader" module to the demuxer */ +struct demux_meta_t +{ + vlc_meta_t *p_meta; /**< meta data */ + + int i_attachments; /**< number of attachments */ + input_attachment_t **attachments; /**< array of attachments */ +}; + +enum demux_query_e +{ + /* I. Common queries to access_demux and demux */ + /* POSITION double between 0.0 and 1.0 */ + DEMUX_GET_POSITION, /* arg1= double * res= */ + DEMUX_SET_POSITION, /* arg1= double res=can fail */ + + /* LENGTH/TIME in microsecond, 0 if unknown */ + DEMUX_GET_LENGTH, /* arg1= int64_t * res= */ + DEMUX_GET_TIME, /* arg1= int64_t * res= */ + DEMUX_SET_TIME, /* arg1= int64_t res=can fail */ + + /* TITLE_INFO only if more than 1 title or 1 chapter */ + DEMUX_GET_TITLE_INFO, /* arg1=input_title_t*** arg2=int* + arg3=int*pi_title_offset(0), arg4=int*pi_seekpoint_offset(0) can fail */ + /* TITLE/SEEKPOINT, only when TITLE_INFO succeed */ + DEMUX_SET_TITLE, /* arg1= int can fail */ + DEMUX_SET_SEEKPOINT, /* arg1= int can fail */ + + /* DEMUX_SET_GROUP only a hit for demuxer (mainly DVB) to allow not + * reading everything (you should not use this to call es_out_Control) + * if you don't know what to do with it, just IGNORE it, it is safe(r) + * -1 means all group, 0 default group (first es added) */ + DEMUX_SET_GROUP, /* arg1= int can fail */ + + /* Ask the demux to demux until the given date at the next pf_demux call + * but not more (and not less, at the precision available of course). + * XXX: not mandatory (except for subtitle demux) but I will help a lot + * for multi-input + */ + DEMUX_SET_NEXT_DEMUX_TIME, /* arg1= int64_t * can fail */ + /* FPS for correct subtitles handling */ + DEMUX_GET_FPS, /* arg1= double * res=can fail */ + + /* Meta data */ + DEMUX_GET_META, /* arg1= vlc_meta_t ** res=can fail */ + DEMUX_HAS_UNSUPPORTED_META, /* arg1= bool * res can fail */ + + /* Attachments */ + DEMUX_GET_ATTACHMENTS, /* arg1=input_attachment_t***, int* res=can fail */ + + /* II. Specific access_demux queries */ + DEMUX_CAN_PAUSE, /* arg1= bool* can fail (assume false)*/ + DEMUX_SET_PAUSE_STATE, /* arg1= bool can fail */ + + DEMUX_GET_PTS_DELAY, /* arg1= int64_t* cannot fail */ + + /* DEMUX_CAN_CONTROL_PACE returns true (*pb_pace) if we can read the + * data at our pace */ + DEMUX_CAN_CONTROL_PACE, /* arg1= bool*pb_pace can fail (assume false) */ + + /* DEMUX_CAN_CONTROL_RATE is called only if DEMUX_CAN_CONTROL_PACE has returned false. + * *pb_rate should be true when the rate can be changed (using DEMUX_SET_RATE) + * *pb_ts_rescale should be true when the timestamps (pts/dts/pcr) have to be rescaled */ + DEMUX_CAN_CONTROL_RATE, /* arg1= bool*pb_rate arg2= bool*pb_ts_rescale can fail(assume false) */ + /* DEMUX_SET_RATE is called only if DEMUX_CAN_CONTROL_RATE has returned true. + * It should return the value really used in *pi_rate */ + DEMUX_SET_RATE, /* arg1= int*pi_rate can fail */ + + DEMUX_CAN_SEEK, /* arg1= bool* can fail (assume false)*/ +}; + +VLC_EXPORT( int, demux_vaControlHelper, ( stream_t *, int64_t i_start, int64_t i_end, int i_bitrate, int i_align, int i_query, va_list args ) ); + +/************************************************************************* + * Miscellaneous helpers for demuxers + *************************************************************************/ + +static inline bool demux_IsPathExtension( demux_t *p_demux, const char *psz_extension ) +{ + const char *psz_ext = strrchr ( p_demux->psz_path, '.' ); + if( !psz_ext || strcasecmp( psz_ext, psz_extension ) ) + return false; + return true; +} + +static inline bool demux_IsForced( demux_t *p_demux, const char *psz_name ) +{ + if( !p_demux->psz_demux || strcmp( p_demux->psz_demux, psz_name ) ) + return false; + return true; +} + +#define DEMUX_INIT_COMMON() do { \ + p_demux->pf_control = Control; \ + p_demux->pf_demux = Demux; \ + MALLOC_ERR( p_demux->p_sys, demux_sys_t ); \ + memset( p_demux->p_sys, 0, sizeof( demux_sys_t ) ); } while(0) + +#define STANDARD_DEMUX_INIT_MSG( msg ) do { \ + DEMUX_INIT_COMMON(); \ + msg_Dbg( p_demux, "%s", msg ); } while(0) + +#define DEMUX_BY_EXTENSION( ext ) \ + demux_t *p_demux = (demux_t *)p_this; \ + if( !demux_IsPathExtension( p_demux, ext ) ) \ + return VLC_EGENERIC; \ + DEMUX_INIT_COMMON(); + +#define DEMUX_BY_EXTENSION_MSG( ext, msg ) \ + demux_t *p_demux = (demux_t *)p_this; \ + if( !demux_IsPathExtension( p_demux, ext ) ) \ + return VLC_EGENERIC; \ + STANDARD_DEMUX_INIT_MSG( msg ); + +#define DEMUX_BY_EXTENSION_OR_FORCED( ext, module ) \ + demux_t *p_demux = (demux_t *)p_this; \ + if( !demux_IsPathExtension( p_demux, ext ) && !demux_IsForced( p_demux, module ) ) \ + return VLC_EGENERIC; \ + DEMUX_INIT_COMMON(); + +#define DEMUX_BY_EXTENSION_OR_FORCED_MSG( ext, module, msg ) \ + demux_t *p_demux = (demux_t *)p_this; \ + if( !demux_IsPathExtension( p_demux, ext ) && !demux_IsForced( p_demux, module ) ) \ + return VLC_EGENERIC; \ + STANDARD_DEMUX_INIT_MSG( msg ); + +#define CHECK_PEEK( zepeek, size ) \ + if( stream_Peek( p_demux->s , &zepeek, size ) < size ){ \ + msg_Dbg( p_demux, "not enough data" ); return VLC_EGENERIC; } + +#define CHECK_PEEK_GOTO( zepeek, size ) \ + if( stream_Peek( p_demux->s , &zepeek, size ) < size ) { \ + msg_Dbg( p_demux, "not enough data" ); goto error; } + +#define POKE( peek, stuff, size ) (strncasecmp( (const char *)peek, stuff, size )==0) + +#define COMMON_INIT_PACKETIZER( location ) \ + location = vlc_object_create( p_demux, VLC_OBJECT_PACKETIZER ); \ + location->pf_decode_audio = 0; \ + location->pf_decode_video = 0; \ + location->pf_decode_sub = 0; \ + location->pf_packetize = 0; \ + +#define INIT_APACKETIZER( location, a,b,c,d ) \ + COMMON_INIT_PACKETIZER(location ); \ + es_format_Init( &location->fmt_in, AUDIO_ES, \ + VLC_FOURCC( a, b, c, d ) ); + +#define INIT_VPACKETIZER( location, a,b,c,d ) \ + COMMON_INIT_PACKETIZER(location ); \ + es_format_Init( &location->fmt_in, VIDEO_ES, \ + VLC_FOURCC( a, b, c, d ) ); + +/* BEWARE ! This can lead to memory leaks ! */ +#define LOAD_PACKETIZER_OR_FAIL( location, msg ) \ + location->p_module = \ + module_Need( location, "packetizer", NULL, 0 ); \ + if( location->p_module == NULL ) \ + { \ + vlc_object_release( location ); \ + msg_Err( p_demux, "cannot find packetizer for " # msg ); \ + free( p_sys ); \ + return VLC_EGENERIC; \ + } + +#define DESTROY_PACKETIZER( location ) \ + if( location->p_module ) module_Unneed( location, location->p_module ); \ + vlc_object_release( location ); + +/** + * @} + */ + +#endif diff --git a/VLC/vlc_devices.h b/VLC/vlc_devices.h new file mode 100644 index 0000000..ba0d5f8 --- /dev/null +++ b/VLC/vlc_devices.h @@ -0,0 +1,68 @@ +/***************************************************************************** + * vlc_devices.h : Devices handling + ***************************************************************************** + * Copyright (C) 1999-2006 the VideoLAN team + * $Id$ + * + * Authors: Clément Stenac + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_DEVICES_H +#define VLC_DEVICES_H 1 + +/** + * \file + * This file implements functions, structures for probing devices (DVD, CD, VCD) + */ + +enum +{ + DEVICE_CAN_DVD, + DEVICE_CAN_CD, +}; + +enum +{ + MEDIA_TYPE_CDDA, + MEDIA_TYPE_VCD, + MEDIA_TYPE_DVD, +}; + +struct device_t +{ + int i_capabilities; + int i_media_type; + bool b_seen; + char *psz_uri; + char *psz_name; +}; + +struct device_probe_t +{ + VLC_COMMON_MEMBERS; + int i_devices; + device_t **pp_devices; + + probe_sys_t *p_sys; + void ( *pf_run ) ( device_probe_t * ); /** Run function */ +}; + +static inline void device_GetDVD(void) +{ +} + +#endif diff --git a/VLC/vlc_epg.h b/VLC/vlc_epg.h new file mode 100644 index 0000000..f1ffabe --- /dev/null +++ b/VLC/vlc_epg.h @@ -0,0 +1,120 @@ +/***************************************************************************** + * vlc_epg.h: Electronic Program Guide + ***************************************************************************** + * Copyright (C) 2007 the VideoLAN team + * $Id$ + * + * Authors: Laurent Aimar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_EPG_H +#define VLC_EPG_H 1 + +/** + * \file + * This file defines functions and structures for storing dvb epg information + */ + +typedef struct +{ + int64_t i_start; /* Interpreted as a value return by time() */ + int i_duration; /* Duration of the event in second */ + + char *psz_name; + char *psz_short_description; + char *psz_description; + +} vlc_epg_event_t; + +typedef struct +{ + char *psz_name; + vlc_epg_event_t *p_current; /* Can be null or should be the same than one of pp_event entry */ + + int i_event; + vlc_epg_event_t **pp_event; +} vlc_epg_t; + +static inline void vlc_epg_Init( vlc_epg_t *p_epg, const char *psz_name ) +{ + p_epg->psz_name = psz_name ? strdup( psz_name ) : NULL; + p_epg->p_current = NULL; + TAB_INIT( p_epg->i_event, p_epg->pp_event ); +} + +static inline void vlc_epg_Clean( vlc_epg_t *p_epg ) +{ + int i; + for( i = 0; i < p_epg->i_event; i++ ) + { + vlc_epg_event_t *p_evt = p_epg->pp_event[i]; + free( p_evt->psz_name ); + free( p_evt->psz_short_description ); + free( p_evt->psz_description ); + free( p_evt ); + } + TAB_CLEAN( p_epg->i_event, p_epg->pp_event ); + free( p_epg->psz_name ); +} + +static inline void vlc_epg_AddEvent( vlc_epg_t *p_epg, int64_t i_start, int i_duration, + const char *psz_name, const char *psz_short_description, const char *psz_description ) +{ + vlc_epg_event_t *p_evt = (vlc_epg_event_t*)malloc( sizeof(vlc_epg_event_t) ); + if( !p_evt ) + return; + p_evt->i_start = i_start; + p_evt->i_duration = i_duration; + p_evt->psz_name = psz_name ? strdup( psz_name ) : NULL; + p_evt->psz_short_description = psz_short_description ? strdup( psz_short_description ) : NULL; + p_evt->psz_description = psz_description ? strdup( psz_description ) : NULL; + TAB_APPEND_CPP( vlc_epg_event_t, p_epg->i_event, p_epg->pp_event, p_evt ); +} + +static inline vlc_epg_t *vlc_epg_New( const char *psz_name ) +{ + vlc_epg_t *p_epg = (vlc_epg_t*)malloc( sizeof(vlc_epg_t) ); + if( p_epg ) + vlc_epg_Init( p_epg, psz_name ); + return p_epg; +} + +static inline void vlc_epg_Delete( vlc_epg_t *p_epg ) +{ + vlc_epg_Clean( p_epg ); + free( p_epg ); +} + +static inline void vlc_epg_SetCurrent( vlc_epg_t *p_epg, int64_t i_start ) +{ + int i; + p_epg->p_current = NULL; + if( i_start < 0 ) + return; + + for( i = 0; i < p_epg->i_event; i++ ) + { + if( p_epg->pp_event[i]->i_start == i_start ) + { + p_epg->p_current = p_epg->pp_event[i]; + break; + } + } +} + +#endif + diff --git a/VLC/vlc_es.h b/VLC/vlc_es.h new file mode 100644 index 0000000..ad45797 --- /dev/null +++ b/VLC/vlc_es.h @@ -0,0 +1,246 @@ +/***************************************************************************** + * vlc_es.h: Elementary stream formats descriptions + ***************************************************************************** + * Copyright (C) 1999-2001 the VideoLAN team + * $Id$ + * + * Authors: Laurent Aimar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_ES_H +#define VLC_ES_H 1 + +/* FIXME: i'm not too sure about this include but it fixes compilation of + * video chromas -- dionoea */ +#include "vlc_common.h" + +/** + * \file + * This file defines the elementary streams format types + */ + +/** + * video palette data + * \see video_format_t + * \see subs_format_t + */ +struct video_palette_t +{ + int i_entries; /**< to keep the compatibility with ffmpeg's palette */ + uint8_t palette[256][4]; /**< 4-byte RGBA/YUVA palette */ +}; + +/** + * audio replay gain description + */ +#define AUDIO_REPLAY_GAIN_MAX (2) +#define AUDIO_REPLAY_GAIN_TRACK (0) +#define AUDIO_REPLAY_GAIN_ALBUM (1) +typedef struct +{ + /* true if we have the peak value */ + bool pb_peak[AUDIO_REPLAY_GAIN_MAX]; + /* peak value where 1.0 means full sample value */ + float pf_peak[AUDIO_REPLAY_GAIN_MAX]; + + /* true if we have the gain value */ + bool pb_gain[AUDIO_REPLAY_GAIN_MAX]; + /* gain value in dB */ + float pf_gain[AUDIO_REPLAY_GAIN_MAX]; +} audio_replay_gain_t; + +/** + * audio format description + */ +struct audio_format_t +{ + vlc_fourcc_t i_format; /**< audio format fourcc */ + unsigned int i_rate; /**< audio sample-rate */ + + /* Describes the channels configuration of the samples (ie. number of + * channels which are available in the buffer, and positions). */ + uint32_t i_physical_channels; + + /* Describes from which original channels, before downmixing, the + * buffer is derived. */ + uint32_t i_original_channels; + + /* Optional - for A/52, SPDIF and DTS types : */ + /* Bytes used by one compressed frame, depends on bitrate. */ + unsigned int i_bytes_per_frame; + + /* Number of sampleframes contained in one compressed frame. */ + unsigned int i_frame_length; + /* Please note that it may be completely arbitrary - buffers are not + * obliged to contain a integral number of so-called "frames". It's + * just here for the division : + * buffer_size = i_nb_samples * i_bytes_per_frame / i_frame_length + */ + + /* FIXME ? (used by the codecs) */ + unsigned i_bitspersample; + unsigned i_blockalign; + uint8_t i_channels; /* must be <=32 */ + uint8_t i_flavor; +}; + +#ifdef WORDS_BIGENDIAN +# define AUDIO_FMT_S16_NE VLC_FOURCC('s','1','6','b') +# define AUDIO_FMT_U16_NE VLC_FOURCC('u','1','6','b') +#else +# define AUDIO_FMT_S16_NE VLC_FOURCC('s','1','6','l') +# define AUDIO_FMT_U16_NE VLC_FOURCC('u','1','6','l') +#endif + +/** + * video format description + */ +struct video_format_t +{ + vlc_fourcc_t i_chroma; /**< picture chroma */ + unsigned int i_aspect; /**< aspect ratio */ + + unsigned int i_width; /**< picture width */ + unsigned int i_height; /**< picture height */ + unsigned int i_x_offset; /**< start offset of visible area */ + unsigned int i_y_offset; /**< start offset of visible area */ + unsigned int i_visible_width; /**< width of visible area */ + unsigned int i_visible_height; /**< height of visible area */ + + unsigned int i_bits_per_pixel; /**< number of bits per pixel */ + + unsigned int i_sar_num; /**< sample/pixel aspect ratio */ + unsigned int i_sar_den; + + unsigned int i_frame_rate; /**< frame rate numerator */ + unsigned int i_frame_rate_base; /**< frame rate denominator */ + + int i_rmask, i_gmask, i_bmask; /**< color masks for RGB chroma */ + int i_rrshift, i_lrshift; + int i_rgshift, i_lgshift; + int i_rbshift, i_lbshift; + video_palette_t *p_palette; /**< video palette from demuxer */ +}; + +/** + * subtitles format description + */ +struct subs_format_t +{ + /* the character encoding of the text of the subtitle. + * all gettext recognized shorts can be used */ + char *psz_encoding; + + + int i_x_origin; /**< x coordinate of the subtitle. 0 = left */ + int i_y_origin; /**< y coordinate of the subtitle. 0 = top */ + + struct + { + /* */ + uint32_t palette[16+1]; + + /* the width of the original movie the spu was extracted from */ + int i_original_frame_width; + /* the height of the original movie the spu was extracted from */ + int i_original_frame_height; + } spu; + + struct + { + int i_id; + } dvb; +}; + +/** + * ES definition + */ +typedef struct extra_languages_t +{ + char *psz_language; + char *psz_description; +} extra_languages_t; + + +struct es_format_t +{ + int i_cat; + vlc_fourcc_t i_codec; + + int i_id; /* -1: let the core mark the right id + >=0: valid id */ + int i_group; /* -1 : standalone + >= 0 then a "group" (program) is created + for each value */ + int i_priority; /* -2 : mean not selectable by the users + -1 : mean not selected by default even + when no other stream + >=0: priority */ + + char *psz_language; + char *psz_description; + int i_extra_languages; + extra_languages_t *p_extra_languages; + + audio_format_t audio; + audio_replay_gain_t audio_replay_gain; + video_format_t video; + subs_format_t subs; + + unsigned int i_bitrate; + + bool b_packetized; /* wether the data is packetized + (ie. not truncated) */ + int i_extra; + void *p_extra; + +}; + +/* ES Categories */ +enum es_format_category_e +{ + UNKNOWN_ES = 0x00, + VIDEO_ES = 0x01, + AUDIO_ES = 0x02, + SPU_ES = 0x03, + NAV_ES = 0x04, +}; + +/** + * This function will fill all RGB shift from RGB masks. + */ +VLC_EXPORT( void, video_format_FixRgb, ( video_format_t * ) ); + +/** + * This funtion will initialize a es_format_t structure. + */ +VLC_EXPORT( void, es_format_Init, ( es_format_t *, int i_cat, vlc_fourcc_t i_codec ) ); + +/** + * This functions will copy a es_format_t. + */ +VLC_EXPORT( int, es_format_Copy, ( es_format_t *p_dst, const es_format_t *p_src ) ); + +/** + * This function will clean up a es_format_t and relasing all associated + * resources. + * You can call it multiple times on the same structure. + */ +VLC_EXPORT( void, es_format_Clean, ( es_format_t *fmt ) ); + +#endif + diff --git a/VLC/vlc_es_out.h b/VLC/vlc_es_out.h new file mode 100644 index 0000000..bd65448 --- /dev/null +++ b/VLC/vlc_es_out.h @@ -0,0 +1,145 @@ +/***************************************************************************** + * vlc_es_out.h: es_out (demuxer output) descriptor, queries and methods + ***************************************************************************** + * Copyright (C) 1999-2004 the VideoLAN team + * $Id$ + * + * Authors: Laurent Aimar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_ES_OUT_H +#define VLC_ES_OUT_H 1 + +/** + * \file + * This file defines functions and structures for handling es_out in stream output + */ + +/** + * \defgroup es out Es Out + * @{ + */ + +enum es_out_mode_e +{ + ES_OUT_MODE_NONE, /* don't select anything */ + ES_OUT_MODE_ALL, /* eg for stream output */ + ES_OUT_MODE_AUTO, /* best audio/video or for input follow audio-track, sub-track */ + ES_OUT_MODE_PARTIAL /* select programs given after --programs */ +}; + +enum es_out_query_e +{ + /* activate application of mode */ + ES_OUT_SET_ACTIVE, /* arg1= bool */ + /* see if mode is currently aplied or not */ + ES_OUT_GET_ACTIVE, /* arg1= bool* */ + + /* set/get mode */ + ES_OUT_SET_MODE, /* arg1= int */ + ES_OUT_GET_MODE, /* arg2= int* */ + + /* set ES selected for the es category (audio/video/spu) */ + ES_OUT_SET_ES, /* arg1= es_out_id_t* */ + + /* set 'default' tag on ES (copied across from container) */ + ES_OUT_SET_DEFAULT, /* arg1= es_out_id_t* */ + + /* force selection/unselection of the ES (bypass current mode) */ + ES_OUT_SET_ES_STATE,/* arg1= es_out_id_t* arg2=bool */ + ES_OUT_GET_ES_STATE,/* arg1= es_out_id_t* arg2=bool* */ + + /* */ + ES_OUT_SET_GROUP, /* arg1= int */ + ES_OUT_GET_GROUP, /* arg1= int* */ + + /* PCR handling, DTS/PTS will be automatically computed using thoses PCR + * XXX: SET_PCR(_GROUP) are in charge of the pace control. They will wait + * to slow down the demuxer so that it reads at the right speed. + * XXX: if you want PREROLL just call RESET_PCR and + * ES_OUT_SET_NEXT_DISPLAY_TIME and send data to the decoder *without* + * calling SET_PCR until preroll is finished. + */ + ES_OUT_SET_PCR, /* arg1=int64_t i_pcr(microsecond!) (using default group 0)*/ + ES_OUT_SET_GROUP_PCR, /* arg1= int i_group, arg2=int64_t i_pcr(microsecond!)*/ + ES_OUT_RESET_PCR, /* no arg */ + + /* Timestamp handling, convert an input timestamp to a global clock one. + * (shouldn't have to be used by input plugins directly) */ + ES_OUT_GET_TS, /* arg1=int64_t i_ts(microsecond!) (using default group 0), arg2=int64_t* converted i_ts */ + + /* Try not to use this one as it is a bit hacky */ + ES_OUT_SET_FMT, /* arg1= es_out_id_t* arg2=es_format_t* */ + + /* Allow preroll of data (data with dts/pts < i_pts for one ES will be decoded but not displayed */ + ES_OUT_SET_NEXT_DISPLAY_TIME, /* arg1=es_out_id_t* arg2=int64_t i_pts(microsecond) */ + /* Set meta data for group (dynamic) */ + ES_OUT_SET_GROUP_META, /* arg1=int i_group arg2=vlc_meta_t */ + /* Set epg for group (dynamic) */ + ES_OUT_SET_GROUP_EPG, /* arg1=int i_group arg2=vlc_epg_t */ + /* */ + ES_OUT_DEL_GROUP /* arg1=int i_group */ +}; + +struct es_out_t +{ + es_out_id_t *(*pf_add) ( es_out_t *, es_format_t * ); + int (*pf_send) ( es_out_t *, es_out_id_t *, block_t * ); + void (*pf_del) ( es_out_t *, es_out_id_t * ); + int (*pf_control)( es_out_t *, int i_query, va_list ); + bool b_sout; + + es_out_sys_t *p_sys; +}; + +static inline es_out_id_t * es_out_Add( es_out_t *out, es_format_t *fmt ) +{ + return out->pf_add( out, fmt ); +} + +static inline void es_out_Del( es_out_t *out, es_out_id_t *id ) +{ + out->pf_del( out, id ); +} + +static inline int es_out_Send( es_out_t *out, es_out_id_t *id, + block_t *p_block ) +{ + return out->pf_send( out, id, p_block ); +} + +static inline int es_out_vaControl( es_out_t *out, int i_query, va_list args ) +{ + return out->pf_control( out, i_query, args ); +} + +static inline int es_out_Control( es_out_t *out, int i_query, ... ) +{ + va_list args; + int i_result; + + va_start( args, i_query ); + i_result = es_out_vaControl( out, i_query, args ); + va_end( args ); + return i_result; +} + +/** + * @} + */ + +#endif diff --git a/VLC/vlc_events.h b/VLC/vlc_events.h new file mode 100644 index 0000000..17f9777 --- /dev/null +++ b/VLC/vlc_events.h @@ -0,0 +1,259 @@ +/***************************************************************************** + * events.h: events definitions + * Interface used to send events. + ***************************************************************************** + * Copyright (C) 2007 the VideoLAN team + * $Id$ + * + * Authors: Pierre d'Herbemont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_EVENTS_H +# define VLC_EVENTS_H + +#include "vlc_arrays.h" +#include "vlc_meta.h" + +/** + * \file + * This file is the interface definition for events + * (implementation in src/misc/events.c) + */ + +/***************************************************************************** + * Documentation + *****************************************************************************/ +/* + **** Background + * + * This implements a way to send and receive event for an object (which can be + * a simple C struct or less). + * + * This is in direct concurrency with the Variable based Callback + * (see src/misc/variables.c). + * + * It has the following advantages over Variable based Callback: + * - No need to implement the whole VLC_COMMON_MEMBERS in the object, + * thus it reduce it size. This is especially true for input_item_t which + * doesn't have VLC_COMMON_MEMBERS. This is the first reason of existence of + * this implementation. + * - Libvlc can easily be based upon that. + * - Existing event are clearly declared (in include/vlc_events.h) + * + * + **** Example usage + * + * (vlc_cool_object_t doesn't need to have the VLC_COMMON_MEMBERS.) + * + * struct vlc_cool_object_t + * { + * ... + * vlc_event_manager_t p_event_manager; + * ... + * } + * + * vlc_my_cool_object_new() + * { + * ... + * vlc_event_manager_init( &p_self->p_event_manager, p_self, p_a_libvlc_object ); + * vlc_event_manager_register_event_type(p_self->p_event_manager, + * vlc_MyCoolObjectDidSomething, p_e) + * ... + * } + * + * vlc_my_cool_object_release() + * { + * ... + * vlc_event_manager_fini( &p_self->p_event_manager ); + * ... + * } + * + * vlc_my_cool_object_do_something() + * { + * ... + * vlc_event_t event; + * event.type = vlc_MyCoolObjectDidSomething; + * event.u.my_cool_object_did_something.what_it_did = kSomething; + * vlc_event_send( &p_self->p_event_manager, &event ); + * } + * */ + + /***************************************************************************** + * Event Type + *****************************************************************************/ + +/* Private structure defined in misc/events.c */ +struct vlc_event_listeners_group_t; + +/* Event manager type */ +typedef struct vlc_event_manager_t +{ + void * p_obj; + vlc_mutex_t object_lock; + vlc_mutex_t event_sending_lock; + vlc_object_t *p_parent_object; + DECL_ARRAY(struct vlc_event_listeners_group_t *) listeners_groups; +} vlc_event_manager_t; + +/* List of event */ +/* Be sure to keep sync-ed with misc/events.c debug name table */ +typedef enum vlc_event_type_t { + /* Input (thread) events */ + vlc_InputStateChanged, + vlc_InputSelectedStreamChanged, + + /* Input item events */ + vlc_InputItemMetaChanged, + vlc_InputItemSubItemAdded, + vlc_InputItemDurationChanged, + vlc_InputItemPreparsedChanged, + vlc_InputItemNameChanged, + vlc_InputItemInfoChanged, + vlc_InputItemErrorWhenReadingChanged, + + /* Service Discovery event */ + vlc_ServicesDiscoveryItemAdded, + vlc_ServicesDiscoveryItemRemoved, + vlc_ServicesDiscoveryStarted, + vlc_ServicesDiscoveryEnded +} vlc_event_type_t; + +/* Event definition */ +typedef struct vlc_event_t +{ + vlc_event_type_t type; + void * p_obj; /* Sender object, automatically filled by vlc_event_send() */ + union vlc_event_type_specific + { + /* Input (thread) events */ + struct vlc_input_state_changed + { + int new_state; + } input_state_changed; + struct vlc_input_selected_stream_changed + { + void * unused; + } input_selected_stream_changed; + + /* Input item events */ + struct vlc_input_item_meta_changed + { + vlc_meta_type_t meta_type; + } input_item_meta_changed; + struct vlc_input_item_subitem_added + { + input_item_t * p_new_child; + } input_item_subitem_added; + struct vlc_input_item_duration_changed + { + mtime_t new_duration; + } input_item_duration_changed; + struct vlc_input_item_preparsed_changed + { + int new_status; + } input_item_preparsed_changed; + struct vlc_input_item_name_changed + { + const char * new_name; + } input_item_name_changed; + struct vlc_input_item_info_changed + { + void * unused; + } input_item_info_changed; + struct input_item_error_when_reading_changed + { + bool new_value; + } input_item_error_when_reading_changed; + + /* Service discovery events */ + struct vlc_services_discovery_item_added + { + input_item_t * p_new_item; + const char * psz_category; + } services_discovery_item_added; + struct vlc_services_discovery_item_removed + { + input_item_t * p_item; + } services_discovery_item_removed; + struct vlc_services_discovery_started + { + void * unused; + } services_discovery_started; + struct vlc_services_discovery_ended + { + void * unused; + } services_discovery_ended; + + } u; +} vlc_event_t; + +/* Event callback type */ +typedef void ( *vlc_event_callback_t )( const vlc_event_t *, void * ); + + /***************************************************************************** + * Event manager + *****************************************************************************/ + +/* + * p_obj points to the object that owns the event manager, and from + * which events are sent + * p_obj is here to give us a libvlc instance + */ +#define vlc_event_manager_init_with_vlc_object(a,b) \ + vlc_event_manager_init( a, b, b ) + +#define vlc_event_manager_init(a,b,c) \ + __vlc_event_manager_init(a, b, VLC_OBJECT(c)) +VLC_EXPORT(int, __vlc_event_manager_init, ( vlc_event_manager_t * p_em, + void * p_obj, vlc_object_t * )); + +/* + * Destroy + */ +VLC_EXPORT(void, vlc_event_manager_fini, ( vlc_event_manager_t * p_em )); + +/* + * Tells a specific event manager that it will handle event_type object + */ +VLC_EXPORT(int, vlc_event_manager_register_event_type, + ( vlc_event_manager_t * p_em, vlc_event_type_t event_type )); + +/* + * Send an event to the listener attached to this p_em. + */ +VLC_EXPORT(void, vlc_event_send, ( vlc_event_manager_t * p_em, + vlc_event_t * p_event )); + +/* + * Add a callback for an event. + */ +#define vlc_event_attach(a, b, c, d) __vlc_event_attach(a, b, c, d, #c) +VLC_EXPORT(int, __vlc_event_attach, ( vlc_event_manager_t * p_event_manager, + vlc_event_type_t event_type, + vlc_event_callback_t pf_callback, + void *p_user_data, + const char * psz_debug_name )); + +/* + * Remove a callback for an event. + */ +VLC_EXPORT(int, vlc_event_detach, ( vlc_event_manager_t *p_event_manager, + vlc_event_type_t event_type, + vlc_event_callback_t pf_callback, + void *p_user_data )); + +#endif /* VLC_EVENTS_H */ diff --git a/VLC/vlc_filter.h b/VLC/vlc_filter.h new file mode 100644 index 0000000..066a3f4 --- /dev/null +++ b/VLC/vlc_filter.h @@ -0,0 +1,319 @@ +/***************************************************************************** + * vlc_filter.h: filter related structures and functions + ***************************************************************************** + * Copyright (C) 1999-2008 the VideoLAN team + * $Id$ + * + * Authors: Gildas Bazin + * Antoine Cellerier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_FILTER_H +#define VLC_FILTER_H 1 + +#include "vlc_es.h" + +/** + * \file + * This file defines the structure and types used by video and audio filters + */ + +typedef struct filter_owner_sys_t filter_owner_sys_t; + +/** Structure describing a filter + * @warning BIG FAT WARNING : the code relies on the first 4 members of + * filter_t and decoder_t to be the same, so if you have anything to add, + * do it at the end of the structure. + */ +struct filter_t +{ + VLC_COMMON_MEMBERS + + /* Module properties */ + module_t * p_module; + filter_sys_t * p_sys; + + /* Input format */ + es_format_t fmt_in; + + /* Output format of filter */ + es_format_t fmt_out; + bool b_allow_fmt_out_change; + + /* Filter configuration */ + config_chain_t * p_cfg; + + picture_t * ( * pf_video_filter ) ( filter_t *, picture_t * ); + block_t * ( * pf_audio_filter ) ( filter_t *, block_t * ); + void ( * pf_video_blend ) ( filter_t *, picture_t *, + picture_t *, picture_t *, + int, int, int ); + + subpicture_t * ( *pf_sub_filter ) ( filter_t *, mtime_t ); + int ( *pf_render_text ) ( filter_t *, subpicture_region_t *, + subpicture_region_t * ); + int ( *pf_render_html ) ( filter_t *, subpicture_region_t *, + subpicture_region_t * ); + + /* + * Buffers allocation + */ + + /* Audio output callbacks */ + block_t * ( * pf_audio_buffer_new) ( filter_t *, int ); + + /* Video output callbacks */ + picture_t * ( * pf_vout_buffer_new) ( filter_t * ); + void ( * pf_vout_buffer_del) ( filter_t *, picture_t * ); + /* void ( * pf_picture_link) ( picture_t * ); + void ( * pf_picture_unlink) ( picture_t * ); */ + + /* SPU output callbacks */ + subpicture_t * ( * pf_sub_buffer_new) ( filter_t * ); + void ( * pf_sub_buffer_del) ( filter_t *, subpicture_t * ); + + /* Private structure for the owner of the decoder */ + filter_owner_sys_t *p_owner; +}; + +/** + * This function will return a new picture usable by p_filter as an output + * buffer. You have to release it using filter_DeletePicture or by returning + * it to the caller as a pf_video_filter return value. + * Provided for convenience. + * + * \param p_filter filter_t object + * \return new picture on success or NULL on failure + */ +static inline picture_t *filter_NewPicture( filter_t *p_filter ) +{ + picture_t *p_picture = p_filter->pf_vout_buffer_new( p_filter ); + if( !p_picture ) + msg_Warn( p_filter, "can't get output picture" ); + return p_picture; +} + +/** + * This function will release a picture create by filter_NewPicture. + * Provided for convenience. + * + * \param p_filter filter_t object + * \param p_picture picture to be deleted + */ +static inline void filter_DeletePicture( filter_t *p_filter, picture_t *p_picture ) +{ + p_filter->pf_vout_buffer_del( p_filter, p_picture ); +} + +/** + * This function will return a new subpicture usable by p_filter as an output + * buffer. You have to release it using filter_DeleteSubpicture or by returning + * it to the caller as a pf_sub_filter return value. + * Provided for convenience. + * + * \param p_filter filter_t object + * \return new subpicture + */ +static inline subpicture_t *filter_NewSubpicture( filter_t *p_filter ) +{ + subpicture_t *p_subpicture = p_filter->pf_sub_buffer_new( p_filter ); + if( !p_subpicture ) + msg_Warn( p_filter, "can't get output subpicture" ); + return p_subpicture; +} + +/** + * This function will release a subpicture create by filter_NewSubicture. + * Provided for convenience. + * + * \param p_filter filter_t object + * \param p_subpicture to be released + */ +static inline void filter_DeleteSubpicture( filter_t *p_filter, subpicture_t *p_subpicture ) +{ + p_filter->pf_sub_buffer_del( p_filter, p_subpicture ); +} + +/** + * This function will return a new audio buffer usable by p_filter as an + * output buffer. You have to release it using block_Release or by returning + * it to the caller as a pf_audio_filter return value. + * Provided for convenience. + * + * \param p_filter filter_t object + * \param i_size size of audio buffer requested + * \return block to be used as audio output buffer + */ +static inline block_t *filter_NewAudioBuffer( filter_t *p_filter, int i_size ) +{ + block_t *p_block = p_filter->pf_audio_buffer_new( p_filter, i_size ); + if( !p_block ) + msg_Warn( p_filter, "can't get output block" ); + return p_block; +} + +/** + * Create a picture_t *(*)( filter_t *, picture_t * ) compatible wrapper + * using a void (*)( filter_t *, picture_t *, picture_t * ) function + * + * Currently used by the chroma video filters + */ +#define VIDEO_FILTER_WRAPPER( name ) \ + static picture_t *name ## _Filter ( filter_t *p_filter, \ + picture_t *p_pic ) \ + { \ + picture_t *p_outpic = filter_NewPicture( p_filter ); \ + if( !p_outpic ) \ + { \ + picture_Release( p_pic ); \ + return NULL; \ + } \ + \ + name( p_filter, p_pic, p_outpic ); \ + \ + picture_CopyProperties( p_outpic, p_pic ); \ + picture_Release( p_pic ); \ + \ + return p_outpic; \ + } + +/** + * Filter chain management API + * The filter chain management API is used to dynamically construct filters + * and add them in a chain. + */ + +typedef struct filter_chain_t filter_chain_t; + +/** + * Create new filter chain + * + * \param p_object pointer to a vlc object + * \param psz_capability vlc capability of filters in filter chain + * \param b_allow_format_fmt_change allow changing of fmt + * \param pf_buffer_allocation_init callback function to initialize buffer allocations + * \param pf_buffer_allocation_clear callback function to clear buffer allocation initialization + * \param p_buffer_allocation_data pointer to private allocation data + * \return pointer to a filter chain + */ +VLC_EXPORT( filter_chain_t *, __filter_chain_New, ( vlc_object_t *, const char *, bool, int (*)( filter_t *, void * ), void (*)( filter_t * ), void * ) ); +#define filter_chain_New( a, b, c, d, e, f ) __filter_chain_New( VLC_OBJECT( a ), b, c, d, e, f ) + +/** + * Delete filter chain will delete all filters in the chain and free all + * allocated data. The pointer to the filter chain is then no longer valid. + * + * \param p_chain pointer to filter chain + */ +VLC_EXPORT( void, filter_chain_Delete, ( filter_chain_t * ) ); + +/** + * Reset filter chain will delete all filters in the chain and + * reset p_fmt_in and p_fmt_out to the new values. + * + * \param p_chain pointer to filter chain + * \param p_fmt_in new fmt_in params + * \param p_fmt_out new fmt_out params + */ +VLC_EXPORT( void, filter_chain_Reset, ( filter_chain_t *, const es_format_t *, const es_format_t * ) ); + +/** + * Append filter to the end of the chain. + * + * \param p_chain pointer to filter chain + * \param psz_name name of filter + * \param p_cfg + * \param p_fmt_in input es_format_t + * \param p_fmt_out output es_format_t + * \return pointer to filter chain + */ +VLC_EXPORT( filter_t *, filter_chain_AppendFilter, ( filter_chain_t *, const char *, config_chain_t *, const es_format_t *, const es_format_t * ) ); + +/** + * Append new filter to filter chain from string. + * + * \param p_chain pointer to filter chain + * \param psz_string string of filters + * \return 0 for success + */ +VLC_EXPORT( int, filter_chain_AppendFromString, ( filter_chain_t *, const char * ) ); + +/** + * Delete filter from filter chain. This function also releases the filter + * object and unloads the filter modules. The pointer to p_filter is no + * longer valid after this function successfully returns. + * + * \param p_chain pointer to filter chain + * \param p_filter pointer to filter object + * \return VLC_SUCCESS on succes, else VLC_EGENERIC + */ +VLC_EXPORT( int, filter_chain_DeleteFilter, ( filter_chain_t *, filter_t * ) ); + +/** + * Get filter by name of position in the filter chain. + * + * \param p_chain pointer to filter chain + * \param i_position position of filter in filter chain + * \param psz_name name of filter to get + * \return filter object based on position or name provided + */ +VLC_EXPORT( filter_t *, filter_chain_GetFilter, ( filter_chain_t *, int, const char * ) ); + +/** + * Get the number of filters in the filter chain. + * + * \param p_chain pointer to filter chain + * \return number of filters in this filter chain + */ +VLC_EXPORT( int, filter_chain_GetLength, ( filter_chain_t * ) ); + +/** + * Get last p_fmt_out in the chain. + * + * \param p_chain pointer to filter chain + * \return last p_fmt (es_format_t) of this filter chain + */ +VLC_EXPORT( const es_format_t *, filter_chain_GetFmtOut, ( filter_chain_t * ) ); + +/** + * Apply the filter chain to a video picture. + * + * \param p_chain pointer to filter chain + * \param p_picture picture to apply filters on + * \return modified picture after applying all video filters + */ +VLC_EXPORT( picture_t *, filter_chain_VideoFilter, ( filter_chain_t *, picture_t * ) ); + +/** + * Apply the filter chain to a audio block. + * + * \param p_chain pointer to filter chain + * \param p_block audio frame to apply filters on + * \return modified audio frame after applying all audio filters + */ +VLC_EXPORT( block_t *, filter_chain_AudioFilter, ( filter_chain_t *, block_t * ) ); + +/** + * Apply filter chain to subpictures. + * + * \param p_chain pointer to filter chain + * \param display_date of subpictures + */ +VLC_EXPORT( void, filter_chain_SubFilter, ( filter_chain_t *, mtime_t ) ); + +#endif /* _VLC_FILTER_H */ + diff --git a/VLC/vlc_fixups.h b/VLC/vlc_fixups.h new file mode 100644 index 0000000..df250fc --- /dev/null +++ b/VLC/vlc_fixups.h @@ -0,0 +1,238 @@ +/***************************************************************************** + * fixups.h: portability fixups included from config.h + ***************************************************************************** + * Copyright © 1998-2008 the VideoLAN project + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/** + * \file + * This file is a collection of portability fixes + */ + +#ifndef LIBVLC_FIXUPS_H +# define LIBVLC_FIXUPS_H 1 + +#ifndef HAVE_STRDUP +# include +# include +static inline char *strdup (const char *str) +{ + size_t len = strlen (str) + 1; + char *res = (char *)malloc (len); + if (res) memcpy (res, str, len); + return res; +} +#endif + +#ifndef HAVE_VASPRINTF +# include +# include +# include +static inline int vasprintf (char **strp, const char *fmt, va_list ap) +{ + int len = vsnprintf (NULL, 0, fmt, ap) + 1; + char *res = (char *)malloc (len); + if (res == NULL) + return -1; + *strp = res; + return vsprintf (res, fmt, ap); +} +#endif + +#ifndef HAVE_ASPRINTF +# include +# include +static inline int asprintf (char **strp, const char *fmt, ...) +{ + va_list ap; + int ret; + va_start (ap, fmt); + ret = vasprintf (strp, fmt, ap); + va_end (ap); + return ret; +} +#endif + +#ifndef HAVE_STRNLEN +# include +static inline size_t strnlen (const char *str, size_t max) +{ + const char *end = (const char *) memchr (str, 0, max); + return end ? (size_t)(end - str) : max; +} +#endif + +#ifndef HAVE_STRNDUP +# include +# include +static inline char *strndup (const char *str, size_t max) +{ + size_t len = strnlen (str, max); + char *res = (char *) malloc (len + 1); + if (res) + { + memcpy (res, str, len); + res[len] = '\0'; + } + return res; +} +#endif + +#ifndef HAVE_STRLCPY +# define strlcpy vlc_strlcpy +#endif + +#ifndef HAVE_STRTOF +# define strtof( a, b ) ((float)strtod (a, b)) +#endif + +#ifndef HAVE_ATOF +# define atof( str ) (strtod ((str), (char **)NULL, 10)) +#endif + +#ifndef HAVE_STRTOLL +# define strtoll vlc_strtoll +#endif + +#ifndef HAVE_ATOLL +# define atoll( str ) (strtoll ((str), (char **)NULL, 10)) +#endif + +#ifndef HAVE_LLDIV +typedef struct { + long long quot; /* Quotient. */ + long long rem; /* Remainder. */ +} lldiv_t; + +static inline lldiv_t lldiv (long long numer, long long denom) +{ + lldiv_t d = { .quot = numer / denom, .rem = numer % denom }; + return d; +} +#endif + +#ifndef HAVE_SCANDIR +# define scandir vlc_scandir +# define alphasort vlc_alphasort +#endif + +#ifndef HAVE_GETENV +static inline getenv (const char *name) +{ + (void)name; + return NULL; +} +#endif + +#ifndef HAVE_STRCASECMP +# ifndef HAVE_STRICMP +# include +static inline int strcasecmp (const char *s1, const char *s2) +{ + for (size_t i = 0;; i++) + { + int d = tolower (s1[i]) - tolower (s2[i]); + if (d || !s1[i]) return d; + } + return 0; +} +# else +# define strcasecmp stricmp +# endif +#endif + +#ifndef HAVE_STRNCASECMP +# ifndef HAVE_STRNICMP +# include +static inline int strncasecmp (const char *s1, const char *s2, size_t n) +{ + for (size_t i = 0; i < n; i++) + { + int d = tolower (s1[i]) - tolower (s2[i]); + if (d || !s1[i]) return d; + } + return 0; +} +# else +# define strncasecmp strnicmp +# endif +#endif + +#ifndef HAVE_STRCASESTR +# ifndef HAVE_STRISTR +# define strcasestr vlc_strcasestr +# else +# define strcasestr stristr +# endif +#endif + +#ifndef HAVE_LOCALTIME_R +/* If localtime_r() is not provided, we assume localtime() uses + * thread-specific storage. */ +# include +static inline struct tm *localtime_r (const time_t *timep, struct tm *result) +{ + struct tm *s = localtime (timep); + if (s == NULL) + return NULL; + + *result = *s; + return result; +} +static inline struct tm *gmtime_r (const time_t *timep, struct tm *result) +{ + struct tm *s = gmtime (timep); + if (s == NULL) + return NULL; + + *result = *s; + return result; +} +#endif + +/* Alignment of critical static data structures */ +#ifdef ATTRIBUTE_ALIGNED_MAX +# define ATTR_ALIGN(align) __attribute__ ((__aligned__ ((ATTRIBUTE_ALIGNED_MAX < align) ? ATTRIBUTE_ALIGNED_MAX : align))) +#else +# define ATTR_ALIGN(align) +#endif + +#ifndef HAVE_USELOCALE +typedef void *locale_t; +# define newlocale( a, b, c ) ((locale_t)0) +# define uselocale( a ) ((locale_t)0) +# define freelocale( a ) (void)0 +#endif + +#ifdef WIN32 +# include +# define opendir Use_utf8_opendir_or_vlc_wopendir_instead! +# define readdir Use_utf8_readdir_or_vlc_wreaddir_instead! +# define closedir vlc_wclosedir +#endif + +/* libintl support */ +#define _(str) vlc_gettext (str) + +#if defined (ENABLE_NLS) +# include +#endif + +#define N_(str) gettext_noop (str) +#define gettext_noop(str) (str) + +#endif /* !LIBVLC_FIXUPS_H */ diff --git a/VLC/vlc_gcrypt.h b/VLC/vlc_gcrypt.h new file mode 100644 index 0000000..8502910 --- /dev/null +++ b/VLC/vlc_gcrypt.h @@ -0,0 +1,94 @@ +/***************************************************************************** + * vlc_gcrypt.h: VLC thread support for gcrypt + ***************************************************************************** + * Copyright (C) 2004-2008 Rémi Denis-Courmont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/** + * \file + * This file implements gcrypt support functions in vlc + */ + +#ifdef LIBVLC_USE_PTHREAD +/** + * If possible, use gcrypt-provided thread implementation. This is so that + * other non-VLC components (inside the process) can also use gcrypt safely. + */ +GCRY_THREAD_OPTION_PTHREAD_IMPL; +# define gcry_threads_vlc gcry_threads_pthread +#else + +/** + * gcrypt thread option VLC implementation + */ + +static int gcry_vlc_mutex_init( void **p_sys ) +{ + int i_val; + vlc_mutex_t *p_lock = (vlc_mutex_t *)malloc( sizeof( vlc_mutex_t ) ); + + if( p_lock == NULL) + return ENOMEM; + + i_val = vlc_mutex_init( p_lock ); + if( i_val ) + free( p_lock ); + else + *p_sys = p_lock; + return i_val; +} + +static int gcry_vlc_mutex_destroy( void **p_sys ) +{ + vlc_mutex_t *p_lock = (vlc_mutex_t *)*p_sys; + vlc_mutex_destroy( p_lock ); + free( p_lock ); + return VLC_SUCCESS; +} + +static int gcry_vlc_mutex_lock( void **p_sys ) +{ + vlc_mutex_lock( (vlc_mutex_t *)*p_sys ); + return VLC_SUCCESS; +} + +static int gcry_vlc_mutex_unlock( void **lock ) +{ + vlc_mutex_unlock( (vlc_mutex_t *)*lock ); + return VLC_SUCCESS; +} + +static const struct gcry_thread_cbs gcry_threads_vlc = +{ + GCRY_THREAD_OPTION_USER, + NULL, + gcry_vlc_mutex_init, + gcry_vlc_mutex_destroy, + gcry_vlc_mutex_lock, + gcry_vlc_mutex_unlock +}; +#endif + +/** + * Initializes gcrypt with proper locking. + */ +static inline void vlc_gcrypt_init (void) +{ + vlc_mutex_t *lock = var_AcquireMutex ("gcrypt_mutex"); + gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_vlc); + vlc_mutex_unlock (lock); +} diff --git a/VLC/vlc_httpd.h b/VLC/vlc_httpd.h new file mode 100644 index 0000000..5febb37 --- /dev/null +++ b/VLC/vlc_httpd.h @@ -0,0 +1,149 @@ +/***************************************************************************** + * vlc_httpd.h: builtin HTTP/RTSP server. + ***************************************************************************** + * Copyright (C) 2004-2006 the VideoLAN team + * $Id$ + * + * Authors: Laurent Aimar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_HTTPD_H +#define VLC_HTTPD_H 1 + +/** + * \file + * This file defines functions, structures, enums and macros for httpd functionality in vlc. + */ + +enum +{ + HTTPD_MSG_NONE, + + /* answer */ + HTTPD_MSG_ANSWER, + + /* channel communication */ + HTTPD_MSG_CHANNEL, + + /* http request */ + HTTPD_MSG_GET, + HTTPD_MSG_HEAD, + HTTPD_MSG_POST, + + /* rtsp request */ + HTTPD_MSG_OPTIONS, + HTTPD_MSG_DESCRIBE, + HTTPD_MSG_SETUP, + HTTPD_MSG_PLAY, + HTTPD_MSG_PAUSE, + HTTPD_MSG_GETPARAMETER, + HTTPD_MSG_TEARDOWN, + + /* just to track the count of MSG */ + HTTPD_MSG_MAX +}; + +enum +{ + HTTPD_PROTO_NONE, + HTTPD_PROTO_HTTP, /* HTTP/1.x */ + HTTPD_PROTO_RTSP, /* RTSP/1.x */ + HTTPD_PROTO_HTTP0, /* HTTP/0.x */ +}; + +struct httpd_message_t +{ + httpd_client_t *cl; /* NULL if not throught a connection e vlc internal */ + + uint8_t i_type; + uint8_t i_proto; + uint8_t i_version; + + /* for an answer */ + int i_status; + + /* for a query */ + char *psz_url; + /* FIXME find a clean way to handle GET(psz_args) + and POST(body) through the same code */ + uint8_t *psz_args; + + /* for rtp over rtsp */ + int i_channel; + + /* options */ + int i_name; + char **name; + int i_value; + char **value; + + /* body */ + int64_t i_body_offset; + int i_body; + uint8_t *p_body; + +}; + +/* create a new host */ +VLC_EXPORT( httpd_host_t *, httpd_HostNew, ( vlc_object_t *, const char *psz_host, int i_port ) ); +VLC_EXPORT( httpd_host_t *, httpd_TLSHostNew, ( vlc_object_t *, const char *, int, const char *, const char *, const char *, const char * ) ); + +/* delete a host */ +VLC_EXPORT( void, httpd_HostDelete, ( httpd_host_t * ) ); + +/* register a new url */ +VLC_EXPORT( httpd_url_t *, httpd_UrlNew, ( httpd_host_t *, const char *psz_url, const char *psz_user, const char *psz_password, const vlc_acl_t *p_acl ) ); +VLC_EXPORT( httpd_url_t *, httpd_UrlNewUnique, ( httpd_host_t *, const char *psz_url, const char *psz_user, const char *psz_password, const vlc_acl_t *p_acl ) ); +/* register callback on a url */ +VLC_EXPORT( int, httpd_UrlCatch, ( httpd_url_t *, int i_msg, httpd_callback_t, httpd_callback_sys_t * ) ); +/* delete an url */ +VLC_EXPORT( void, httpd_UrlDelete, ( httpd_url_t * ) ); + +/* Default client mode is FILE, use these to change it */ +VLC_EXPORT( void, httpd_ClientModeStream, ( httpd_client_t *cl ) ); +VLC_EXPORT( void, httpd_ClientModeBidir, ( httpd_client_t *cl ) ); +VLC_EXPORT( char*, httpd_ClientIP, ( const httpd_client_t *cl, char *psz_ip ) ); +VLC_EXPORT( char*, httpd_ServerIP, ( const httpd_client_t *cl, char *psz_ip ) ); + +/* High level */ + +VLC_EXPORT( httpd_file_t *, httpd_FileNew, ( httpd_host_t *, const char *psz_url, const char *psz_mime, const char *psz_user, const char *psz_password, const vlc_acl_t *p_acl, httpd_file_callback_t pf_fill, httpd_file_sys_t * ) ); +VLC_EXPORT( httpd_file_sys_t *, httpd_FileDelete, ( httpd_file_t * ) ); + + +VLC_EXPORT( httpd_handler_t *, httpd_HandlerNew, ( httpd_host_t *, const char *psz_url, const char *psz_user, const char *psz_password, const vlc_acl_t *p_acl, httpd_handler_callback_t pf_fill, httpd_handler_sys_t * ) ); +VLC_EXPORT( httpd_handler_sys_t *, httpd_HandlerDelete, ( httpd_handler_t * ) ); + + +VLC_EXPORT( httpd_redirect_t *, httpd_RedirectNew, ( httpd_host_t *, const char *psz_url_dst, const char *psz_url_src ) ); +VLC_EXPORT( void, httpd_RedirectDelete, ( httpd_redirect_t * ) ); + + +VLC_EXPORT( httpd_stream_t *, httpd_StreamNew, ( httpd_host_t *, const char *psz_url, const char *psz_mime, const char *psz_user, const char *psz_password, const vlc_acl_t *p_acl ) ); +VLC_EXPORT( void, httpd_StreamDelete, ( httpd_stream_t * ) ); +VLC_EXPORT( int, httpd_StreamHeader, ( httpd_stream_t *, uint8_t *p_data, int i_data ) ); +VLC_EXPORT( int, httpd_StreamSend, ( httpd_stream_t *, uint8_t *p_data, int i_data ) ); + + +/* Msg functions facilities */ +VLC_EXPORT( void, httpd_MsgInit, ( httpd_message_t * ) ); +VLC_EXPORT( void, httpd_MsgAdd, ( httpd_message_t *, const char *psz_name, const char *psz_value, ... ) LIBVLC_FORMAT( 3, 4 ) ); +/* return "" if not found. The string is not allocated */ +VLC_EXPORT( const char *, httpd_MsgGet, ( const httpd_message_t *, const char *psz_name ) ); +VLC_EXPORT( void, httpd_MsgClean, ( httpd_message_t * ) ); + +#endif /* _VLC_HTTPD_H */ diff --git a/VLC/vlc_image.h b/VLC/vlc_image.h new file mode 100644 index 0000000..b20fa86 --- /dev/null +++ b/VLC/vlc_image.h @@ -0,0 +1,77 @@ +/***************************************************************************** + * vlc_image.h : wrapper for image reading/writing facilities + ***************************************************************************** + * Copyright (C) 2004 the VideoLAN team + * $Id$ + * + * Authors: Gildas Bazin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_IMAGE_H +#define VLC_IMAGE_H 1 + +/** + * \file + * This file defines functions and structures for image conversions in vlc + */ + +#include "vlc_vout.h" + +# ifdef __cplusplus +extern "C" { +# endif + +struct image_handler_t +{ + picture_t * (*pf_read) ( image_handler_t *, block_t *, + video_format_t *, video_format_t * ); + picture_t * (*pf_read_url) ( image_handler_t *, const char *, + video_format_t *, video_format_t * ); + block_t * (*pf_write) ( image_handler_t *, picture_t *, + video_format_t *, video_format_t * ); + int (*pf_write_url) ( image_handler_t *, picture_t *, + video_format_t *, video_format_t *, + const char * ); + + picture_t * (*pf_convert) ( image_handler_t *, picture_t *, + video_format_t *, video_format_t * ); + picture_t * (*pf_filter) ( image_handler_t *, picture_t *, + video_format_t *, const char * ); + + /* Private properties */ + vlc_object_t *p_parent; + decoder_t *p_dec; + encoder_t *p_enc; + filter_t *p_filter; +}; + +VLC_EXPORT( image_handler_t *, __image_HandlerCreate, ( vlc_object_t * ) ); +#define image_HandlerCreate( a ) __image_HandlerCreate( VLC_OBJECT(a) ) +VLC_EXPORT( void, image_HandlerDelete, ( image_handler_t * ) ); + +#define image_Read( a, b, c, d ) a->pf_read( a, b, c, d ) +#define image_ReadUrl( a, b, c, d ) a->pf_read_url( a, b, c, d ) +#define image_Write( a, b, c, d ) a->pf_write( a, b, c, d ) +#define image_WriteUrl( a, b, c, d, e ) a->pf_write_url( a, b, c, d, e ) +#define image_Convert( a, b, c, d ) a->pf_convert( a, b, c, d ) +#define image_Filter( a, b, c, d ) a->pf_filter( a, b, c, d ) + +# ifdef __cplusplus +} +# endif + +#endif /* _VLC_IMAGE_H */ diff --git a/VLC/vlc_input.h b/VLC/vlc_input.h new file mode 100644 index 0000000..b85f672 --- /dev/null +++ b/VLC/vlc_input.h @@ -0,0 +1,556 @@ +/***************************************************************************** + * vlc_input.h: Core input structures + ***************************************************************************** + * Copyright (C) 1999-2006 the VideoLAN team + * $Id$ + * + * Authors: Christophe Massiot + * Laurent Aimar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/* __ is need because conflict with */ +#ifndef VLC__INPUT_H +#define VLC__INPUT_H 1 + +/** + * \file + * This file defines functions, structures and enums for input objects in vlc + */ + +#include "vlc_es.h" +#include "vlc_meta.h" +#include "vlc_epg.h" +#include "vlc_events.h" + +#include /* strcasestr() */ + +struct vlc_meta_t; + +/***************************************************************************** + * input_item_t: Describes an input and is used to spawn input_thread_t objects + *****************************************************************************/ +struct info_t +{ + char *psz_name; /**< Name of this info */ + char *psz_value; /**< Value of the info */ +}; + +struct info_category_t +{ + char *psz_name; /**< Name of this category */ + int i_infos; /**< Number of infos in the category */ + struct info_t **pp_infos; /**< Pointer to an array of infos */ +}; + +struct input_item_t +{ + VLC_GC_MEMBERS + int i_id; /**< Identifier of the item */ + + char *psz_name; /**< text describing this item */ + char *psz_uri; /**< mrl of this item */ + bool b_fixed_name; /**< Can the interface change the name ?*/ + + int i_options; /**< Number of input options */ + char **ppsz_options; /**< Array of input options */ + uint8_t *optflagv; /**< Some flags of input options */ + unsigned optflagc; + + mtime_t i_duration; /**< Duration in milliseconds*/ + + uint8_t i_type; /**< Type (file, disc, ...) */ + bool b_prefers_tree; /**< Do we prefer being displayed as tree*/ + + int i_categories; /**< Number of info categories */ + info_category_t **pp_categories; /**< Pointer to the first info category */ + + int i_es; /**< Number of es format descriptions */ + es_format_t **es; /**< Es formats */ + + input_stats_t *p_stats; /**< Statistics */ + int i_nb_played; /**< Number of times played */ + + bool b_error_when_reading; /**< Error When Reading */ + + vlc_meta_t *p_meta; + + vlc_event_manager_t event_manager; + + vlc_mutex_t lock; /**< Lock for the item */ +}; + +#define ITEM_TYPE_UNKNOWN 0 +#define ITEM_TYPE_FILE 1 +#define ITEM_TYPE_DIRECTORY 2 +#define ITEM_TYPE_DISC 3 +#define ITEM_TYPE_CDDA 4 +#define ITEM_TYPE_CARD 5 +#define ITEM_TYPE_NET 6 +#define ITEM_TYPE_PLAYLIST 7 +#define ITEM_TYPE_NODE 8 +#define ITEM_TYPE_NUMBER 9 + +VLC_EXPORT( void, input_item_CopyOptions, ( input_item_t *p_parent, input_item_t *p_child ) ); +VLC_EXPORT( void, input_item_SetName, ( input_item_t *p_item, const char *psz_name ) ); + +/* This won't hold the item, but can tell to interested third parties + * Like the playlist, that there is a new sub item. With this design + * It is not the input item's responsability to keep all the ref of + * the input item children. */ +VLC_EXPORT( void, input_item_AddSubItem, ( input_item_t *p_parent, input_item_t *p_child ) ); + + +/* Flags handled past input_item_AddOpt() */ +#define VLC_INPUT_OPTION_TRUSTED 0x2 + +/* Flags handled within input_item_AddOpt() */ +#define VLC_INPUT_OPTION_UNIQUE 0x100 + +VLC_EXPORT( int, input_item_AddOpt, ( input_item_t *, const char *str, unsigned flags ) ); +VLC_EXPORT( int, input_item_AddOption, (input_item_t *item, const char *str) ); +VLC_EXPORT( int ,input_item_AddOption, (input_item_t *item, const char *str) ); +VLC_EXPORT( bool,input_item_HasErrorWhenReading, (input_item_t *item) ); +VLC_EXPORT( void, input_item_SetMeta, ( input_item_t *p_i, vlc_meta_type_t meta_type, const char *psz_val )); + +VLC_EXPORT( bool,input_item_HasErrorWhenReading, (input_item_t *item) ); + +VLC_EXPORT( bool,input_item_MetaMatch, ( input_item_t *p_i, vlc_meta_type_t meta_type, const char *psz ) ); +VLC_EXPORT( char *, input_item_GetMeta, ( input_item_t *p_i, vlc_meta_type_t meta_type ) ); +VLC_EXPORT( char *, input_item_GetName, ( input_item_t * p_i ) ); +VLC_EXPORT( char *, input_item_GetURI, ( input_item_t * p_i ) ); +VLC_EXPORT( void, input_item_SetURI, ( input_item_t * p_i, char * psz_uri )); +VLC_EXPORT(mtime_t, input_item_GetDuration, ( input_item_t * p_i ) ); +VLC_EXPORT( void, input_item_SetDuration, ( input_item_t * p_i, mtime_t i_duration )); +VLC_EXPORT( bool, input_item_IsPreparsed, ( input_item_t *p_i )); +VLC_EXPORT( bool, input_item_IsArtFetched, ( input_item_t *p_i )); +VLC_EXPORT( const vlc_meta_t *, input_item_GetMetaObject, ( input_item_t *p_i )); +VLC_EXPORT( void, input_item_MetaMerge, ( input_item_t *p_i, const vlc_meta_t * p_new_meta )); + + +#define input_item_SetTitle( item, b ) input_item_SetMeta( item, vlc_meta_Title, b ) +#define input_item_SetArtist( item, b ) input_item_SetMeta( item, vlc_meta_Artist, b ) +#define input_item_SetGenre( item, b ) input_item_SetMeta( item, vlc_meta_Genre, b ) +#define input_item_SetCopyright( item, b ) input_item_SetMeta( item, vlc_meta_Copyright, b ) +#define input_item_SetAlbum( item, b ) input_item_SetMeta( item, vlc_meta_Album, b ) +#define input_item_SetTrackNum( item, b ) input_item_SetMeta( item, vlc_meta_TrackNumber, b ) +#define input_item_SetDescription( item, b ) input_item_SetMeta( item, vlc_meta_Description, b ) +#define input_item_SetRating( item, b ) input_item_SetMeta( item, vlc_meta_Rating, b ) +#define input_item_SetDate( item, b ) input_item_SetMeta( item, vlc_meta_Date, b ) +#define input_item_SetSetting( item, b ) input_item_SetMeta( item, vlc_meta_Setting, b ) +#define input_item_SetURL( item, b ) input_item_SetMeta( item, vlc_meta_URL, b ) +#define input_item_SetLanguage( item, b ) input_item_SetMeta( item, vlc_meta_Language, b ) +#define input_item_SetNowPlaying( item, b ) input_item_SetMeta( item, vlc_meta_NowPlaying, b ) +#define input_item_SetPublisher( item, b ) input_item_SetMeta( item, vlc_meta_Publisher, b ) +#define input_item_SetEncodedBy( item, b ) input_item_SetMeta( item, vlc_meta_EncodedBy, b ) +#define input_item_SetArtURL( item, b ) input_item_SetMeta( item, vlc_meta_ArtworkURL, b ) +#define input_item_SetTrackID( item, b ) input_item_SetMeta( item, vlc_meta_TrackID, b ) + +#define input_item_GetTitle( item ) input_item_GetMeta( item, vlc_meta_Title ) +#define input_item_GetArtist( item ) input_item_GetMeta( item, vlc_meta_Artist ) +#define input_item_GetGenre( item ) input_item_GetMeta( item, vlc_meta_Genre ) +#define input_item_GetCopyright( item ) input_item_GetMeta( item, vlc_meta_Copyright ) +#define input_item_GetAlbum( item ) input_item_GetMeta( item, vlc_meta_Album ) +#define input_item_GetTrackNum( item ) input_item_GetMeta( item, vlc_meta_TrackNumber ) +#define input_item_GetDescription( item ) input_item_GetMeta( item, vlc_meta_Description ) +#define input_item_GetRating( item ) input_item_GetMeta( item, vlc_meta_Rating ) +#define input_item_GetDate( item ) input_item_GetMeta( item, vlc_meta_Date ) +#define input_item_GetGetting( item ) input_item_GetMeta( item, vlc_meta_Getting ) +#define input_item_GetURL( item ) input_item_GetMeta( item, vlc_meta_URL ) +#define input_item_GetLanguage( item ) input_item_GetMeta( item, vlc_meta_Language ) +#define input_item_GetNowPlaying( item ) input_item_GetMeta( item, vlc_meta_NowPlaying ) +#define input_item_GetPublisher( item ) input_item_GetMeta( item, vlc_meta_Publisher ) +#define input_item_GetEncodedBy( item ) input_item_GetMeta( item, vlc_meta_EncodedBy ) +#define input_item_GetArtURL( item ) input_item_GetMeta( item, vlc_meta_ArtworkURL ) +#define input_item_GetTrackID( item ) input_item_GetMeta( item, vlc_meta_TrackID ) +#define input_item_GetSetting( item ) input_item_GetMeta( item, vlc_meta_Setting ) + +VLC_EXPORT( char *, input_item_GetInfo, ( input_item_t *p_i, const char *psz_cat,const char *psz_name ) ); +VLC_EXPORT(int, input_item_AddInfo, ( input_item_t *p_i, const char *psz_cat, const char *psz_name, const char *psz_format, ... ) LIBVLC_FORMAT( 4, 5 ) ); + +#define input_item_New( a,b,c ) input_item_NewExt( a, b, c, 0, NULL, -1 ) +#define input_item_NewExt(a,b,c,d,e,f) __input_item_NewExt( VLC_OBJECT(a),b,c,d,e,f) +VLC_EXPORT( input_item_t *, __input_item_NewExt, (vlc_object_t *, const char *, const char*, int, const char *const *, mtime_t i_duration ) ); +VLC_EXPORT( input_item_t *, input_item_NewWithType, ( vlc_object_t *, const char *, const char *e, int, const char *const *, mtime_t i_duration, int ) ); + +#define input_item_GetById(a,b) __input_item_GetById( VLC_OBJECT(a),b ) +VLC_EXPORT( input_item_t *, __input_item_GetById, (vlc_object_t *, int ) ); + +/***************************************************************************** + * Meta data helpers + *****************************************************************************/ +static inline void vlc_audio_replay_gain_MergeFromMeta( audio_replay_gain_t *p_dst, + const vlc_meta_t *p_meta ) +{ + char * psz_value; + + if( !p_meta ) + return; + + if( (psz_value = (char *)vlc_dictionary_value_for_key( &p_meta->extra_tags, "REPLAYGAIN_TRACK_GAIN" )) || + (psz_value = (char *)vlc_dictionary_value_for_key( &p_meta->extra_tags, "RG_RADIO" )) ) + { + p_dst->pb_gain[AUDIO_REPLAY_GAIN_TRACK] = true; + p_dst->pf_gain[AUDIO_REPLAY_GAIN_TRACK] = atof( psz_value ); + } + else if( (psz_value = (char *)vlc_dictionary_value_for_key( &p_meta->extra_tags, "REPLAYGAIN_TRACK_PEAK" )) || + (psz_value = (char *)vlc_dictionary_value_for_key( &p_meta->extra_tags, "RG_PEAK" )) ) + { + p_dst->pb_peak[AUDIO_REPLAY_GAIN_TRACK] = true; + p_dst->pf_peak[AUDIO_REPLAY_GAIN_TRACK] = atof( psz_value ); + } + else if( (psz_value = (char *)vlc_dictionary_value_for_key( &p_meta->extra_tags, "REPLAYGAIN_ALBUM_GAIN" )) || + (psz_value = (char *)vlc_dictionary_value_for_key( &p_meta->extra_tags, "RG_AUDIOPHILE" )) ) + { + p_dst->pb_gain[AUDIO_REPLAY_GAIN_ALBUM] = true; + p_dst->pf_gain[AUDIO_REPLAY_GAIN_ALBUM] = atof( psz_value ); + } + else if( (psz_value = (char *)vlc_dictionary_value_for_key( &p_meta->extra_tags, "REPLAYGAIN_ALBUM_PEAK" )) ) + { + p_dst->pb_peak[AUDIO_REPLAY_GAIN_ALBUM] = true; + p_dst->pf_peak[AUDIO_REPLAY_GAIN_ALBUM] = atof( psz_value ); + } +} + +/***************************************************************************** + * Seek point: (generalisation of chapters) + *****************************************************************************/ +struct seekpoint_t +{ + int64_t i_byte_offset; + int64_t i_time_offset; + char *psz_name; + int i_level; +}; + +static inline seekpoint_t *vlc_seekpoint_New( void ) +{ + seekpoint_t *point = (seekpoint_t*)malloc( sizeof( seekpoint_t ) ); + point->i_byte_offset = + point->i_time_offset = -1; + point->i_level = 0; + point->psz_name = NULL; + return point; +} + +static inline void vlc_seekpoint_Delete( seekpoint_t *point ) +{ + if( !point ) return; + free( point->psz_name ); + free( point ); +} + +static inline seekpoint_t *vlc_seekpoint_Duplicate( seekpoint_t *src ) +{ + seekpoint_t *point = vlc_seekpoint_New(); + if( src->psz_name ) point->psz_name = strdup( src->psz_name ); + point->i_time_offset = src->i_time_offset; + point->i_byte_offset = src->i_byte_offset; + return point; +} + +/***************************************************************************** + * Title: + *****************************************************************************/ +typedef struct +{ + char *psz_name; + + bool b_menu; /* Is it a menu or a normal entry */ + + int64_t i_length; /* Length(microsecond) if known, else 0 */ + int64_t i_size; /* Size (bytes) if known, else 0 */ + + /* Title seekpoint */ + int i_seekpoint; + seekpoint_t **seekpoint; + +} input_title_t; + +static inline input_title_t *vlc_input_title_New(void) +{ + input_title_t *t = (input_title_t*)malloc( sizeof( input_title_t ) ); + + t->psz_name = NULL; + t->b_menu = false; + t->i_length = 0; + t->i_size = 0; + t->i_seekpoint = 0; + t->seekpoint = NULL; + + return t; +} + +static inline void vlc_input_title_Delete( input_title_t *t ) +{ + int i; + if( t == NULL ) + return; + + free( t->psz_name ); + for( i = 0; i < t->i_seekpoint; i++ ) + { + free( t->seekpoint[i]->psz_name ); + free( t->seekpoint[i] ); + } + free( t->seekpoint ); + free( t ); +} + +static inline input_title_t *vlc_input_title_Duplicate( input_title_t *t ) +{ + input_title_t *dup = vlc_input_title_New( ); + int i; + + if( t->psz_name ) dup->psz_name = strdup( t->psz_name ); + dup->b_menu = t->b_menu; + dup->i_length = t->i_length; + dup->i_size = t->i_size; + dup->i_seekpoint = t->i_seekpoint; + if( t->i_seekpoint > 0 ) + { + dup->seekpoint = (seekpoint_t**)calloc( t->i_seekpoint, + sizeof(seekpoint_t*) ); + + for( i = 0; i < t->i_seekpoint; i++ ) + { + dup->seekpoint[i] = vlc_seekpoint_Duplicate( t->seekpoint[i] ); + } + } + + return dup; +} + +/***************************************************************************** + * Attachments + *****************************************************************************/ +struct input_attachment_t +{ + char *psz_name; + char *psz_mime; + char *psz_description; + + int i_data; + void *p_data; +}; + +static inline input_attachment_t *vlc_input_attachment_New( const char *psz_name, + const char *psz_mime, + const char *psz_description, + const void *p_data, + int i_data ) +{ + input_attachment_t *a = + (input_attachment_t*)malloc( sizeof(input_attachment_t) ); + if( !a ) + return NULL; + a->psz_name = strdup( psz_name ? psz_name : "" ); + a->psz_mime = strdup( psz_mime ? psz_mime : "" ); + a->psz_description = strdup( psz_description ? psz_description : "" ); + a->i_data = i_data; + a->p_data = NULL; + if( i_data > 0 ) + { + a->p_data = malloc( i_data ); + if( a->p_data && p_data ) + memcpy( a->p_data, p_data, i_data ); + } + return a; +} +static inline input_attachment_t *vlc_input_attachment_Duplicate( const input_attachment_t *a ) +{ + return vlc_input_attachment_New( a->psz_name, a->psz_mime, a->psz_description, + a->p_data, a->i_data ); +} +static inline void vlc_input_attachment_Delete( input_attachment_t *a ) +{ + if( !a ) + return; + free( a->psz_name ); + free( a->psz_mime ); + free( a->psz_description ); + free( a->p_data ); + free( a ); +} + +/***************************************************************************** + * input defines/constants. + *****************************************************************************/ + +/* "state" value */ +/* NOTE: you need to update ppsz_input_state in the RC interface + * if you modify this list. */ +typedef enum input_state_e +{ + INIT_S = 0, + OPENING_S, + BUFFERING_S, + PLAYING_S, + PAUSE_S, + STOP_S, + FORWARD_S, + BACKWARD_S, + END_S, + ERROR_S, +} input_state_e; + +/* "rate" default, min/max + * A rate below 1000 plays the movie faster, + * A rate above 1000 plays the movie slower. + */ +#define INPUT_RATE_DEFAULT 1000 +#define INPUT_RATE_MIN 125 /* Up to 8/1 */ +#define INPUT_RATE_MAX 32000 /* Up to 1/32 */ + +/* i_update field of access_t/demux_t */ +#define INPUT_UPDATE_NONE 0x0000 +#define INPUT_UPDATE_SIZE 0x0001 +#define INPUT_UPDATE_TITLE 0x0010 +#define INPUT_UPDATE_SEEKPOINT 0x0020 +#define INPUT_UPDATE_META 0x0040 + +/* Input control XXX: internal */ +#define INPUT_CONTROL_FIFO_SIZE 100 + +/** Get the input item for an input thread */ +VLC_EXPORT(input_item_t*, input_GetItem, (input_thread_t*)); + +typedef struct input_thread_private_t input_thread_private_t; + +/** + * Main structure representing an input thread. This structure is mostly + * private. The only public fields are READ-ONLY. You must use the helpers + * to modify them + */ +struct input_thread_t +{ + VLC_COMMON_MEMBERS; + + bool b_eof; + bool b_preparsing; + + int i_state; + bool b_can_pace_control; + int64_t i_time; /* Current time */ + + /* Internal caching common to all inputs */ + mtime_t i_pts_delay; + + /* All other data is input_thread is PRIVATE. You can't access it + * outside of src/input */ + input_thread_private_t *p; +}; + +/***************************************************************************** + * Prototypes + *****************************************************************************/ + +/* input_CreateThread + * Release the returned input_thread_t using vlc_object_release() */ +#define input_CreateThread(a,b) __input_CreateThread(VLC_OBJECT(a),b) +VLC_EXPORT( input_thread_t *, __input_CreateThread, ( vlc_object_t *, input_item_t * ) ); + +#define input_Preparse(a,b) __input_Preparse(VLC_OBJECT(a),b) +VLC_EXPORT( int, __input_Preparse, ( vlc_object_t *, input_item_t * ) ); + +#define input_Read(a,b,c) __input_Read(VLC_OBJECT(a),b, c) +VLC_EXPORT( int, __input_Read, ( vlc_object_t *, input_item_t *, bool ) ); +VLC_EXPORT( void, input_StopThread, ( input_thread_t * ) ); + +enum input_query_e +{ + /* input variable "position" */ + INPUT_GET_POSITION, /* arg1= double * res= */ + INPUT_SET_POSITION, /* arg1= double res=can fail */ + + /* input variable "length" */ + INPUT_GET_LENGTH, /* arg1= int64_t * res=can fail */ + + /* input variable "time" */ + INPUT_GET_TIME, /* arg1= int64_t * res= */ + INPUT_SET_TIME, /* arg1= int64_t res=can fail */ + + /* input variable "rate" (1 is DEFAULT_RATE) */ + INPUT_GET_RATE, /* arg1= int * res= */ + INPUT_SET_RATE, /* arg1= int res=can fail */ + + /* input variable "state" */ + INPUT_GET_STATE, /* arg1= int * res= */ + INPUT_SET_STATE, /* arg1= int res=can fail */ + + /* input variable "audio-delay" and "sub-delay" */ + INPUT_GET_AUDIO_DELAY, /* arg1 = int* res=can fail */ + INPUT_SET_AUDIO_DELAY, /* arg1 = int res=can fail */ + INPUT_GET_SPU_DELAY, /* arg1 = int* res=can fail */ + INPUT_SET_SPU_DELAY, /* arg1 = int res=can fail */ + + /* Meta datas */ + INPUT_ADD_INFO, /* arg1= char* arg2= char* arg3=... res=can fail */ + INPUT_GET_INFO, /* arg1= char* arg2= char* arg3= char** res=can fail */ + INPUT_DEL_INFO, /* arg1= char* arg2= char* res=can fail */ + INPUT_SET_NAME, /* arg1= char* res=can fail */ + + /* Input config options */ + INPUT_ADD_OPTION, /* arg1= char * arg2= char * res=can fail*/ + + /* Input properties */ + INPUT_GET_BYTE_POSITION, /* arg1= int64_t * res= */ + INPUT_SET_BYTE_SIZE, /* arg1= int64_t * res= */ + INPUT_GET_VIDEO_FPS, /* arg1= double * res=can fail */ + + /* bookmarks */ + INPUT_GET_BOOKMARKS, /* arg1= seekpoint_t *** arg2= int * res=can fail */ + INPUT_CLEAR_BOOKMARKS, /* res=can fail */ + INPUT_ADD_BOOKMARK, /* arg1= seekpoint_t * res=can fail */ + INPUT_CHANGE_BOOKMARK, /* arg1= seekpoint_t * arg2= int * res=can fail */ + INPUT_DEL_BOOKMARK, /* arg1= seekpoint_t * res=can fail */ + INPUT_SET_BOOKMARK, /* arg1= int res=can fail */ + + /* Attachments */ + INPUT_GET_ATTACHMENTS, /* arg1=input_attachment_t***, arg2=int* res=can fail */ + INPUT_GET_ATTACHMENT, /* arg1=input_attachment_t**, arg2=char* res=can fail */ + + /* On the fly input slave */ + INPUT_ADD_SLAVE /* arg1= char * */ +}; + +VLC_EXPORT( int, input_vaControl,( input_thread_t *, int i_query, va_list ) ); +VLC_EXPORT( int, input_Control, ( input_thread_t *, int i_query, ... ) ); + +static inline input_state_e input_GetState( input_thread_t * p_input ) +{ + input_state_e state = INIT_S; + input_Control( p_input, INPUT_GET_STATE, &state ); + return state; +} +VLC_EXPORT( decoder_t *, input_DecoderNew, ( input_thread_t *, es_format_t *, bool b_force_decoder ) ); +VLC_EXPORT( void, input_DecoderDelete, ( decoder_t * ) ); +VLC_EXPORT( void, input_DecoderDecode,( decoder_t *, block_t * ) ); + +VLC_EXPORT( bool, input_AddSubtitles, ( input_thread_t *, char *, bool ) ); + +VLC_EXPORT( vlc_event_manager_t *, input_get_event_manager, ( input_thread_t * ) ); + +/** + * This function allows to split a MRL into access, demux and path part. + * + * You should not write into access and demux string as they may not point into + * the provided buffer. + * The buffer provided by psz_dup will be modified. + */ +VLC_EXPORT( void, input_SplitMRL, ( const char **ppsz_access, const char **ppsz_demux, char **ppsz_path, char *psz_dup ) ); + +#endif diff --git a/VLC/vlc_interface.h b/VLC/vlc_interface.h new file mode 100644 index 0000000..0fb7b6c --- /dev/null +++ b/VLC/vlc_interface.h @@ -0,0 +1,331 @@ +/***************************************************************************** + * vlc_interface.h: interface access for other threads + * This library provides basic functions for threads to interact with user + * interface, such as message output. + ***************************************************************************** + * Copyright (C) 1999, 2000 the VideoLAN team + * $Id$ + * + * Authors: Vincent Seguin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_INTF_H_ +#define VLC_INTF_H_ + +# ifdef __cplusplus +extern "C" { +# endif + +typedef struct intf_dialog_args_t intf_dialog_args_t; + +/** + * \file + * This file contains structures and function prototypes for + * interface management in vlc + */ + +/** + * \defgroup vlc_interface Interface + * These functions and structures are for interface management + * @{ + */ + +/** Describe all interface-specific data of the interface thread */ +struct intf_thread_t +{ + VLC_COMMON_MEMBERS + + /* Thread properties and locks */ +#ifdef __APPLE__ + bool b_should_run_on_first_thread; +#endif + + /* Specific interfaces */ + intf_console_t * p_console; /** console */ + intf_sys_t * p_sys; /** system interface */ + char * psz_intf; /** intf name specified */ + + /** Interface module */ + module_t * p_module; + void ( *pf_run ) ( intf_thread_t * ); /** Run function */ + + /** Specific for dialogs providers */ + void ( *pf_show_dialog ) ( intf_thread_t *, int, int, + intf_dialog_args_t * ); + + /** Interaction stuff */ + bool b_interaction; + + /* XXX: new message passing stuff will go here */ + vlc_mutex_t change_lock; + bool b_menu_change; + bool b_menu; + + /* Provides the ability to switch an interface on the fly */ + char *psz_switch_intf; +}; + +/** \brief Arguments passed to a dialogs provider + * This describes the arguments passed to the dialogs provider. They are + * mainly used with INTF_DIALOG_FILE_GENERIC. + */ +struct intf_dialog_args_t +{ + intf_thread_t *p_intf; + char *psz_title; + + char **psz_results; + int i_results; + + void (*pf_callback) ( intf_dialog_args_t * ); + void *p_arg; + + /* Specifically for INTF_DIALOG_FILE_GENERIC */ + char *psz_extensions; + bool b_save; + bool b_multiple; + + /* Specific to INTF_DIALOG_INTERACTION */ + interaction_dialog_t *p_dialog; +}; + +/***************************************************************************** + * Prototypes + *****************************************************************************/ +#define intf_Create(a,b) __intf_Create(VLC_OBJECT(a),b) +VLC_EXPORT( intf_thread_t *, __intf_Create, ( vlc_object_t *, const char * ) ); +VLC_EXPORT( int, intf_RunThread, ( intf_thread_t * ) ); +VLC_EXPORT( void, intf_StopThread, ( intf_thread_t * ) ); + +/* If the interface is in the main thread, it should listen both to + * p_intf->b_die and p_libvlc->b_die */ +#define intf_ShouldDie( p_intf ) (p_intf->b_die || p_intf->p_libvlc->b_die ) + +#define intf_Eject(a,b) __intf_Eject(VLC_OBJECT(a),b) +VLC_EXPORT( int, __intf_Eject, ( vlc_object_t *, const char * ) ); + +/*@}*/ + +/***************************************************************************** + * Macros + *****************************************************************************/ +#if defined( WIN32 ) && !defined( UNDER_CE ) +# define CONSOLE_INTRO_MSG \ + if( !getenv( "PWD" ) || !getenv( "PS1" ) ) /* detect cygwin shell */ \ + { \ + AllocConsole(); \ + freopen( "CONOUT$", "w", stdout ); \ + freopen( "CONOUT$", "w", stderr ); \ + freopen( "CONIN$", "r", stdin ); \ + } \ + msg_Info( p_intf, "%s", COPYRIGHT_MESSAGE ); \ + msg_Info( p_intf, _("\nWarning: if you can't access the GUI " \ + "anymore, open a command-line window, go to the " \ + "directory where you installed VLC and run " \ + "\"vlc -I qt\"\n") ) +#else +# define CONSOLE_INTRO_MSG +#endif + +/* Interface dialog ids for dialog providers */ +typedef enum vlc_dialog { + INTF_DIALOG_FILE_SIMPLE = 1, + INTF_DIALOG_FILE, + INTF_DIALOG_DISC, + INTF_DIALOG_NET, + INTF_DIALOG_CAPTURE, + INTF_DIALOG_SAT, + INTF_DIALOG_DIRECTORY, + + INTF_DIALOG_STREAMWIZARD, + INTF_DIALOG_WIZARD, + + INTF_DIALOG_PLAYLIST, + INTF_DIALOG_MESSAGES, + INTF_DIALOG_FILEINFO, + INTF_DIALOG_PREFS, + INTF_DIALOG_BOOKMARKS, + INTF_DIALOG_EXTENDED, + + INTF_DIALOG_POPUPMENU = 20, + INTF_DIALOG_AUDIOPOPUPMENU, + INTF_DIALOG_VIDEOPOPUPMENU, + INTF_DIALOG_MISCPOPUPMENU, + + INTF_DIALOG_FILE_GENERIC = 30, + INTF_DIALOG_INTERACTION = 50, + + INTF_DIALOG_UPDATEVLC = 90, + INTF_DIALOG_VLM, + + INTF_DIALOG_EXIT = 99 +} vlc_dialog_t; + +/* Useful text messages shared by interfaces */ +#define INTF_ABOUT_MSG LICENSE_MSG + +#define EXTENSIONS_AUDIO "*.a52;*.aac;*.ac3;*.dts;*.flac;*.m4a;*.m4p;*.mka;" \ + "*.mod;*.mp1;*.mp2;*.mp3;*.ogg;*.oma;*.spx;" \ + "*.wav;*.wma;*.xm" + +#define EXTENSIONS_VIDEO "*.asf;*.avi;*.divx;*.dv;*.flv;*.gxf;*.m1v;*.m2v;" \ + "*.m2ts;*.m4v;*.mkv;*.mov;*.mp2;*.mp4;*.mpeg;*.mpeg1;" \ + "*.mpeg2;*.mpeg4;*.mpg;*.mts;*.mxf;*.ogg;*.ogm;" \ + "*.ps;*.ts;*.vob;*.wmv" + +#define EXTENSIONS_PLAYLIST "*.asx;*.b4s;*.m3u;*.pls;*.vlc;*.xspf" + +#define EXTENSIONS_MEDIA EXTENSIONS_VIDEO ";" EXTENSIONS_AUDIO ";" \ + EXTENSIONS_PLAYLIST + +#define EXTENSIONS_SUBTITLE "*.cdg;*.idx;*.srt;*.sub;*.utf;*.ass;*.ssa;*.aqt;" \ + "*.jss;*.psb;*.rt;*.smi" + +/** \defgroup vlc_interaction Interaction + * \ingroup vlc_interface + * Interaction between user and modules + * @{ + */ + +/** + * This structure describes a piece of interaction with the user + */ +struct interaction_dialog_t +{ + int i_id; ///< Unique ID + int i_type; ///< Type identifier + char *psz_title; ///< Title + char *psz_description; ///< Descriptor string + char *psz_default_button; ///< default button title (~OK) + char *psz_alternate_button;///< alternate button title (~NO) + /// other button title (optional,~Cancel) + char *psz_other_button; + + char *psz_returned[1]; ///< returned responses from the user + + vlc_value_t val; ///< value coming from core for dialogue + int i_timeToGo; ///< time (in sec) until shown progress is finished + bool b_cancelled; ///< was the dialogue cancelled ? + + void * p_private; ///< Private interface data + + int i_status; ///< Dialog status; + int i_action; ///< Action to perform; + int i_flags; ///< Misc flags + int i_return; ///< Return status + + interaction_t *p_interaction; ///< Parent interaction object + vlc_object_t *p_parent; ///< The vlc object that asked + //for interaction +}; + +/** + * Possible flags . Dialog types + */ +#define DIALOG_GOT_ANSWER 0x01 +#define DIALOG_YES_NO_CANCEL 0x02 +#define DIALOG_LOGIN_PW_OK_CANCEL 0x04 +#define DIALOG_PSZ_INPUT_OK_CANCEL 0x08 +#define DIALOG_BLOCKING_ERROR 0x10 +#define DIALOG_NONBLOCKING_ERROR 0x20 +#define DIALOG_WARNING 0x40 +#define DIALOG_USER_PROGRESS 0x80 +#define DIALOG_INTF_PROGRESS 0x100 + +/** Possible return codes */ +enum +{ + DIALOG_DEFAULT, + DIALOG_OK_YES, + DIALOG_NO, + DIALOG_CANCELLED +}; + +/** Possible status */ +enum +{ + NEW_DIALOG, ///< Just created + SENT_DIALOG, ///< Sent to interface + UPDATED_DIALOG, ///< Update to send + ANSWERED_DIALOG, ///< Got "answer" + HIDING_DIALOG, ///< Hiding requested + HIDDEN_DIALOG, ///< Now hidden. Requesting destruction + DESTROYED_DIALOG, ///< Interface has destroyed it +}; + +/** Possible interaction types */ +enum +{ + INTERACT_DIALOG_ONEWAY, ///< Dialog box without feedback + INTERACT_DIALOG_TWOWAY, ///< Dialog box with feedback +}; + +/** Possible actions */ +enum +{ + INTERACT_NEW, + INTERACT_UPDATE, + INTERACT_HIDE, + INTERACT_DESTROY +}; + +/** + * This structure contains the active interaction dialogs, and is + * used by the manager + */ +struct interaction_t +{ + VLC_COMMON_MEMBERS + + int i_dialogs; ///< Number of dialogs + interaction_dialog_t **pp_dialogs; ///< Dialogs + intf_thread_t *p_intf; ///< Interface to use + int i_last_id; ///< Last attributed ID +}; + +/*************************************************************************** + * Exported symbols + ***************************************************************************/ + +#define intf_UserFatal( a, b, c, d, e... ) __intf_UserFatal( VLC_OBJECT(a),b,c,d, ## e ) +VLC_EXPORT( int, __intf_UserFatal,( vlc_object_t*, bool, const char*, const char*, ...) LIBVLC_FORMAT( 4, 5 ) ); +#define intf_UserWarn( a, c, d, e... ) __intf_UserWarn( VLC_OBJECT(a),c,d, ## e ) +VLC_EXPORT( int, __intf_UserWarn,( vlc_object_t*, const char*, const char*, ...) LIBVLC_FORMAT( 3, 4 ) ); +#define intf_UserLoginPassword( a, b, c, d, e... ) __intf_UserLoginPassword( VLC_OBJECT(a),b,c,d,e) +VLC_EXPORT( int, __intf_UserLoginPassword,( vlc_object_t*, const char*, const char*, char **, char **) ); +#define intf_UserYesNo( a, b, c, d, e, f ) __intf_UserYesNo( VLC_OBJECT(a),b,c, d, e, f ) +VLC_EXPORT( int, __intf_UserYesNo,( vlc_object_t*, const char*, const char*, const char*, const char*, const char*) ); +#define intf_UserStringInput( a, b, c, d ) __intf_UserStringInput( VLC_OBJECT(a),b,c,d ) +VLC_EXPORT( int, __intf_UserStringInput,(vlc_object_t*, const char*, const char*, char **) ); + +#define intf_IntfProgress( a, b, c ) __intf_Progress( VLC_OBJECT(a), NULL, b,c, -1 ) +#define intf_UserProgress( a, b, c, d, e ) __intf_Progress( VLC_OBJECT(a),b,c,d,e ) +VLC_EXPORT( int, __intf_Progress,( vlc_object_t*, const char*, const char*, float, int) ); +#define intf_ProgressUpdate( a, b, c, d, e ) __intf_ProgressUpdate( VLC_OBJECT(a),b,c,d,e ) +VLC_EXPORT( void, __intf_ProgressUpdate,( vlc_object_t*, int, const char*, float, int) ); +#define intf_ProgressIsCancelled( a, b ) __intf_UserProgressIsCancelled( VLC_OBJECT(a),b ) +VLC_EXPORT( bool, __intf_UserProgressIsCancelled,( vlc_object_t*, int ) ); +#define intf_UserHide( a, b ) __intf_UserHide( VLC_OBJECT(a), b ) +VLC_EXPORT( void, __intf_UserHide,( vlc_object_t *, int )); + +/** @} */ +/** @} */ + +# ifdef __cplusplus +} +# endif +#endif diff --git a/VLC/vlc_intf_strings.h b/VLC/vlc_intf_strings.h new file mode 100644 index 0000000..c709a66 --- /dev/null +++ b/VLC/vlc_intf_strings.h @@ -0,0 +1,136 @@ +/***************************************************************************** + * vlc_intf_strings.h : Strings for main interfaces + ***************************************************************************** + * Copyright (C) 2003 the VideoLAN team + * $Id$ + * + * Authors: Clément Stenac + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_INTF_STRINGS_H +#define VLC_INTF_STRINGS_H 1 + +/** + * \file + * This file defines a number of strings used in user interfaces + */ + +/*************** Open dialogs **************/ + +#define I_OP_OPF N_("Quick &Open File...") +#define I_OP_ADVOP N_("&Advanced Open...") +#define I_OP_OPDIR N_("Open &Directory...") + +#define I_OP_SEL_FILES N_("Select one or more files to open") + +/******************* Menus *****************/ + +#define I_MENU_INFO N_("Media &Information...") +#define I_MENU_CODECINFO N_("&Codec Information...") +#define I_MENU_MSG N_("&Messages...") +#define I_MENU_EXT N_("&Extended Settings...") +#define I_MENU_GOTOTIME N_("Go to Specific &Time...") +#define I_MENU_BOOKMARK N_("&Bookmarks...") +#define I_MENU_VLM N_("&VLM Configuration...") + +#define I_MENU_ABOUT N_("&About...") + +/* Playlist popup */ +#define I_POP_PLAY N_("Play") +#define I_POP_PREPARSE N_("Fetch Information") +#define I_POP_DEL N_("Delete") +#define I_POP_INFO N_("Information...") +#define I_POP_SORT N_("Sort") +#define I_POP_ADD N_("Add Node") +#define I_POP_STREAM N_("Stream...") +#define I_POP_SAVE N_("Save...") +#define I_POP_EXPLORE N_("Open Folder...") + +/*************** Playlist *************/ + +#define I_PL_LOOP N_("Repeat all") +#define I_PL_REPEAT N_("Repeat one") +#define I_PL_NOREPEAT N_("No repeat") + +#define I_PL_RANDOM N_("Random") +#define I_PL_NORANDOM N_("Random off") + +#define I_PL_ADDPL N_("Add to playlist") +#define I_PL_ADDML N_("Add to media library") + +#define I_PL_ADDF N_("Add file...") +#define I_PL_ADVADD N_("Advanced open...") +#define I_PL_ADDDIR N_("Add directory...") + +#define I_PL_SAVE N_("Save Playlist to &File...") +#define I_PL_LOAD N_("&Load Playlist File...") + +#define I_PL_SEARCH N_("Search") +#define I_PL_FILTER N_("Search Filter") + +#define I_PL_SD N_("Additional &Sources") + +/*************** Preferences *************/ + +#define I_HIDDEN_ADV N_( "Some options are available but hidden. "\ + "Check \"Advanced options\" to see them." ) + +/*************** Video filters **************/ + +#define I_CLONE N_("Image clone") +#define I_CLONE_TIP N_("Clone the image") + +#define I_MAGNIFY N_("Magnification") +#define I_MAGNIFY_TIP N_("Magnify a part of the video. You can select " \ + "which part of the image should be magnified." ) + +#define I_WAVE N_("Waves") +#define I_WAVE_TIP N_("\"Waves\" video distortion effect") + +#define I_RIPPLE_TIP N_("\"Water surface\" video distortion effect") + +#define I_INVERT_TIP N_("Image colors inversion") + +#define I_WALL_TIP N_("Split the image to make an image wall") + +#define I_PUZZLE_TIP N_("Create a \"puzzle game\" with the video.\n" \ + "The video gets split in parts that you must sort.") + +#define I_GRADIENT_TIP N_("\"Edge detection\" video distortion effect.\n" \ + "Try changing the various settings for different effects" ) + +#define I_COLORTHRES_TIP N_("\"Color detection\" effect. The whole image " \ + "will be turned to black and white, except the parts that "\ + "are of the color that you select in the settings.") + +#define I_LONGHELP N_("" \ + "

Welcome to VLC media player Help

" \ + "

Documentation

" \ + "

You can find VLC documentation on VideoLAN's wiki website.

" \ + "

If you are a newcomer to VLC media player, please read the
Introduction to VLC media player.

" \ + "

You will find some information on how to use the player in the
\"How to play files with VLC media player\" document.

" \ + "

For all the saving, converting, transcoding, encoding, muxing and streaming tasks, you should find useful information in the Streaming Documentation.

" \ + "

If you are unsure about terminology, please consult the knowledge base.

" \ + "

To understand the main keyboard shortcuts, read the shortcuts page.

" \ + "

Help

" \ + "

Before asking any question, please refer yourself to the FAQ.

" \ + "

You might then get (and give) help on the Forums, the mailing-lists or our IRC channel ( #videolan on irc.freenode.net ).

" \ + "

Contribute to the project

" \ + "

You can help the VideoLAN project giving some of your time to help the community, to design skins, to translate the documentation, to test and to code. You can also give funds and material to help us. And of course, you can promote VLC media player.

" \ + "") + +#endif diff --git a/VLC/vlc_iso_lang.h b/VLC/vlc_iso_lang.h new file mode 100644 index 0000000..2d8ddb6 --- /dev/null +++ b/VLC/vlc_iso_lang.h @@ -0,0 +1,48 @@ +/***************************************************************************** + * iso_lang.h: function to decode language code (in dvd or a52 for instance). + ***************************************************************************** + * Copyright (C) 1998-2001 the VideoLAN team + * $Id$ + * + * Author: Stéphane Borel + * Arnaud de Bossoreille de Ribou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/** + * \file + * This file defines functions and structures for iso639 language codes + */ + +struct iso639_lang_t +{ + const char * psz_eng_name; /* Description in English */ + const char * psz_native_name; /* Description in native language */ + const char * psz_iso639_1; /* ISO-639-1 (2 characters) code */ + const char * psz_iso639_2T; /* ISO-639-2/T (3 characters) English code */ + const char * psz_iso639_2B; /* ISO-639-2/B (3 characters) native code */ +}; + +#if defined( __cplusplus ) +extern "C" { +#endif +VLC_EXPORT( const iso639_lang_t *, GetLang_1, ( const char * ) ); +VLC_EXPORT( const iso639_lang_t *, GetLang_2T, ( const char * ) ); +VLC_EXPORT( const iso639_lang_t *, GetLang_2B, ( const char * ) ); +#if defined( __cplusplus ) +} +#endif + diff --git a/VLC/vlc_keys.h b/VLC/vlc_keys.h new file mode 100644 index 0000000..a3fca57 --- /dev/null +++ b/VLC/vlc_keys.h @@ -0,0 +1,342 @@ +/***************************************************************************** + * vlc_keys.h: keycode defines + ***************************************************************************** + * Copyright (C) 2003 the VideoLAN team + * $Id$ + * + * Authors: Sigmund Augdal Helberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_KEYS_H +#define VLC_KEYS_H 1 + +/** + * \file + * This file defines keys, functions and structures for hotkey handling in vlc + * + */ + +#define KEY_MODIFIER 0xFF000000 +#define KEY_MODIFIER_ALT 0x01000000 +#define KEY_MODIFIER_SHIFT 0x02000000 +#define KEY_MODIFIER_CTRL 0x04000000 +#define KEY_MODIFIER_META 0x08000000 +#define KEY_MODIFIER_COMMAND 0x10000000 + +#define KEY_SPECIAL 0x00FF0000 +#define KEY_LEFT 0x00010000 +#define KEY_RIGHT 0x00020000 +#define KEY_UP 0x00030000 +#define KEY_DOWN 0x00040000 +#define KEY_SPACE 0x00050000 +#define KEY_ENTER 0x00060000 +#define KEY_F1 0x00070000 +#define KEY_F2 0x00080000 +#define KEY_F3 0x00090000 +#define KEY_F4 0x000A0000 +#define KEY_F5 0x000B0000 +#define KEY_F6 0x000C0000 +#define KEY_F7 0x000D0000 +#define KEY_F8 0x000E0000 +#define KEY_F9 0x000F0000 +#define KEY_F10 0x00100000 +#define KEY_F11 0x00110000 +#define KEY_F12 0x00120000 +#define KEY_HOME 0x00130000 +#define KEY_END 0x00140000 +#define KEY_INSERT 0x00150000 +#define KEY_DELETE 0x00160000 +#define KEY_MENU 0x00170000 +#define KEY_ESC 0x00180000 +#define KEY_PAGEUP 0x00190000 +#define KEY_PAGEDOWN 0x001A0000 +#define KEY_TAB 0x001B0000 +#define KEY_BACKSPACE 0x001C0000 +#define KEY_MOUSEWHEELUP 0x001D0000 +#define KEY_MOUSEWHEELDOWN 0x001E0000 +#define KEY_MOUSEWHEELLEFT 0x001F0000 +#define KEY_MOUSEWHEELRIGHT 0x00200000 + +/* TODO: + * The media keys are only used in win32. Support for other OSes needs to + * be added */ +#define KEY_BROWSER_BACK 0x001F0000 +#define KEY_BROWSER_FORWARD 0x00200000 +#define KEY_BROWSER_REFRESH 0x00210000 +#define KEY_BROWSER_STOP 0x00220000 +#define KEY_BROWSER_SEARCH 0x00230000 +#define KEY_BROWSER_FAVORITES 0x00240000 +#define KEY_BROWSER_HOME 0x00250000 +#define KEY_VOLUME_MUTE 0x00260000 +#define KEY_VOLUME_DOWN 0x00270000 +#define KEY_VOLUME_UP 0x00280000 +#define KEY_MEDIA_NEXT_TRACK 0x00290000 +#define KEY_MEDIA_PREV_TRACK 0x002a0000 +#define KEY_MEDIA_STOP 0x002b0000 +#define KEY_MEDIA_PLAY_PAUSE 0x002c0000 + +#define KEY_ASCII 0x0000007F +#define KEY_UNSET 0 + +typedef struct key_descriptor_s +{ + const char *psz_key_string; + int i_key_code; +} key_descriptor_t; + +#define ADD_KEY(a) { a, *a } + +static const struct key_descriptor_s vlc_modifiers[] = +{ + { "Alt", KEY_MODIFIER_ALT }, + { "Shift", KEY_MODIFIER_SHIFT }, + { "Ctrl", KEY_MODIFIER_CTRL }, + { "Meta", KEY_MODIFIER_META }, + { "Command", KEY_MODIFIER_COMMAND } +}; + +static const struct key_descriptor_s vlc_keys[] = +{ + { "Unset", KEY_UNSET }, + { "Left", KEY_LEFT }, + { "Right", KEY_RIGHT }, + { "Up", KEY_UP }, + { "Down", KEY_DOWN }, + { "Space", KEY_SPACE }, + { "Enter", KEY_ENTER }, + { "F1", KEY_F1 }, + { "F2", KEY_F2 }, + { "F3", KEY_F3 }, + { "F4", KEY_F4 }, + { "F5", KEY_F5 }, + { "F6", KEY_F6 }, + { "F7", KEY_F7 }, + { "F8", KEY_F8 }, + { "F9", KEY_F9 }, + { "F10", KEY_F10 }, + { "F11", KEY_F11 }, + { "F12", KEY_F12 }, + { "Home", KEY_HOME }, + { "End", KEY_END }, + { "Insert", KEY_INSERT }, + { "Delete", KEY_DELETE }, + { "Menu", KEY_MENU }, + { "Esc", KEY_ESC }, + { "Page Up", KEY_PAGEUP }, + { "Page Down", KEY_PAGEDOWN }, + { "Tab", KEY_TAB }, + { "Backspace", KEY_BACKSPACE }, + { "Mouse Wheel Up", KEY_MOUSEWHEELUP }, + { "Mouse Wheel Down", KEY_MOUSEWHEELDOWN }, + { "0", '0' }, + { "1", '1' }, + { "2", '2' }, + { "3", '3' }, + { "4", '4' }, + { "5", '5' }, + { "6", '6' }, + { "7", '7' }, + { "8", '8' }, + { "9", '9' }, + { "a", 'a' }, + { "b", 'b' }, + { "c", 'c' }, + { "d", 'd' }, + { "e", 'e' }, + { "f", 'f' }, + { "g", 'g' }, + { "h", 'h' }, + { "i", 'i' }, + { "j", 'j' }, + { "k", 'k' }, + { "l", 'l' }, + { "m", 'm' }, + { "n", 'n' }, + { "o", 'o' }, + { "p", 'p' }, + { "q", 'q' }, + { "r", 'r' }, + { "s", 's' }, + { "t", 't' }, + { "u", 'u' }, + { "v", 'v' }, + { "w", 'w' }, + { "x", 'x' }, + { "y", 'y' }, + { "z", 'z' }, + { "+", '+' }, + { "=", '=' }, + { "-", '-' }, + { ",", ',' }, + { ".", '.' }, + { "<", '<' }, + { ">", '>' }, + { "`", '`' }, + { "/", '/' }, + { ";", ';' }, + { "'", '\'' }, + { "\\", '\\' }, + { "[", '[' }, + { "]", ']' }, + { "*", '*' }, + { "Browser Back", KEY_BROWSER_BACK }, + { "Browser Forward", KEY_BROWSER_FORWARD }, + { "Browser Refresh", KEY_BROWSER_REFRESH }, + { "Browser Stop", KEY_BROWSER_STOP }, + { "Browser Search", KEY_BROWSER_SEARCH }, + { "Browser Favorites", KEY_BROWSER_FAVORITES }, + { "Browser Home", KEY_BROWSER_HOME }, + { "Volume Mute", KEY_VOLUME_MUTE }, + { "Volume Down", KEY_VOLUME_DOWN }, + { "Volume Up", KEY_VOLUME_UP }, + { "Media Next Track", KEY_MEDIA_NEXT_TRACK }, + { "Media Prev Track", KEY_MEDIA_PREV_TRACK }, + { "Media Stop", KEY_MEDIA_STOP }, + { "Media Play Pause", KEY_MEDIA_PLAY_PAUSE } +}; + +static inline const char *KeyToString( int i_key ) +{ + unsigned int i = 0; + for ( i = 0; i < sizeof(vlc_keys) / sizeof(key_descriptor_t); i++ ) + { + if ( vlc_keys[i].i_key_code == i_key ) + { + return vlc_keys[i].psz_key_string; + } + } + return NULL; +} + +static inline int StringToKey( char *psz_key ) +{ + unsigned int i = 0; + for ( i = 0; i < sizeof(vlc_keys) / sizeof(key_descriptor_t); i++ ) + { + if ( !strcmp( vlc_keys[i].psz_key_string, psz_key )) + { + return vlc_keys[i].i_key_code; + } + } + return 0; +} + +typedef enum vlc_key { + ACTIONID_QUIT = 1, + ACTIONID_PLAY_PAUSE, + ACTIONID_PLAY, + ACTIONID_PAUSE, + ACTIONID_STOP, + ACTIONID_PREV, + ACTIONID_NEXT, + ACTIONID_SLOWER, + ACTIONID_FASTER, + ACTIONID_TOGGLE_FULLSCREEN, + ACTIONID_VOL_UP, + ACTIONID_VOL_DOWN, + ACTIONID_NAV_ACTIVATE, + ACTIONID_NAV_UP, + ACTIONID_NAV_DOWN, + ACTIONID_NAV_LEFT, + ACTIONID_NAV_RIGHT, + ACTIONID_JUMP_BACKWARD_EXTRASHORT, + ACTIONID_JUMP_FORWARD_EXTRASHORT, + ACTIONID_JUMP_BACKWARD_SHORT, + ACTIONID_JUMP_FORWARD_SHORT, + ACTIONID_JUMP_BACKWARD_MEDIUM, + ACTIONID_JUMP_FORWARD_MEDIUM, + ACTIONID_JUMP_BACKWARD_LONG, + ACTIONID_JUMP_FORWARD_LONG, + ACTIONID_POSITION, + ACTIONID_VOL_MUTE, +/* let ACTIONID_SET_BOOMARK* and ACTIONID_PLAY_BOOKMARK* be contiguous */ + ACTIONID_SET_BOOKMARK1, + ACTIONID_SET_BOOKMARK2, + ACTIONID_SET_BOOKMARK3, + ACTIONID_SET_BOOKMARK4, + ACTIONID_SET_BOOKMARK5, + ACTIONID_SET_BOOKMARK6, + ACTIONID_SET_BOOKMARK7, + ACTIONID_SET_BOOKMARK8, + ACTIONID_SET_BOOKMARK9, + ACTIONID_SET_BOOKMARK10, + ACTIONID_PLAY_BOOKMARK1, + ACTIONID_PLAY_BOOKMARK2, + ACTIONID_PLAY_BOOKMARK3, + ACTIONID_PLAY_BOOKMARK4, + ACTIONID_PLAY_BOOKMARK5, + ACTIONID_PLAY_BOOKMARK6, + ACTIONID_PLAY_BOOKMARK7, + ACTIONID_PLAY_BOOKMARK8, + ACTIONID_PLAY_BOOKMARK9, + ACTIONID_PLAY_BOOKMARK10, + /* end of contiguous zone */ + ACTIONID_SUBDELAY_UP, + ACTIONID_SUBDELAY_DOWN, + ACTIONID_HISTORY_BACK, + ACTIONID_HISTORY_FORWARD, + ACTIONID_AUDIO_TRACK, + ACTIONID_SUBTITLE_TRACK, + ACTIONID_CUBESPEED_UP, + ACTIONID_CUBESPEED_DOWN, + ACTIONID_INTF_SHOW, + ACTIONID_INTF_HIDE, + /* chapter and title navigation */ + ACTIONID_TITLE_PREV, + ACTIONID_TITLE_NEXT, + ACTIONID_CHAPTER_PREV, + ACTIONID_CHAPTER_NEXT, + /* end of chapter and title navigation */ + ACTIONID_AUDIODELAY_UP, + ACTIONID_AUDIODELAY_DOWN, + ACTIONID_SNAPSHOT, + ACTIONID_RECORD, + ACTIONID_DISC_MENU, + ACTIONID_ASPECT_RATIO, + ACTIONID_CROP, + ACTIONID_DEINTERLACE, + ACTIONID_ZOOM, + ACTIONID_UNZOOM, + ACTIONID_CROP_TOP, + ACTIONID_UNCROP_TOP, + ACTIONID_CROP_LEFT, + ACTIONID_UNCROP_LEFT, + ACTIONID_CROP_BOTTOM, + ACTIONID_UNCROP_BOTTOM, + ACTIONID_CROP_RIGHT, + ACTIONID_UNCROP_RIGHT, + ACTIONID_DUMP, + ACTIONID_RANDOM, + ACTIONID_LOOP, + ACTIONID_WALLPAPER, + ACTIONID_LEAVE_FULLSCREEN, + ACTIONID_MENU_ON, + ACTIONID_MENU_OFF, + ACTIONID_MENU_RIGHT, + ACTIONID_MENU_LEFT, + ACTIONID_MENU_UP, + ACTIONID_MENU_DOWN, + ACTIONID_MENU_SELECT, + /* Zoom */ + ACTIONID_ZOOM_QUARTER, + ACTIONID_ZOOM_HALF, + ACTIONID_ZOOM_ORIGINAL, + ACTIONID_ZOOM_DOUBLE, + /* Cycle Through Audio Devices */ + ACTIONID_AUDIODEVICE_CYCLE +} vlc_key_t; +#endif diff --git a/VLC/vlc_main.h b/VLC/vlc_main.h new file mode 100644 index 0000000..119b177 --- /dev/null +++ b/VLC/vlc_main.h @@ -0,0 +1,51 @@ +/***************************************************************************** + * main.h: access to all program variables + * Declaration and extern access to LibVLC instance object. + ***************************************************************************** + * Copyright (C) 1999, 2000, 2001, 2002, 2008 the VideoLAN team + * + * Authors: Vincent Seguin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/** + * \file + * This file defines libvlc_int_t internal libvlc instance + */ + +TYPEDEF_ARRAY(input_item_t*, input_item_array_t); + +/***************************************************************************** + * libvlc_internal_instance_t + ***************************************************************************** + * This structure is a LibVLC instance, for use by libvlc core and plugins + *****************************************************************************/ +struct libvlc_int_t +{ + VLC_COMMON_MEMBERS + + /* FIXME: this is only used by the logger module! */ + global_stats_t *p_stats; ///< Global statistics + + /* Structure storing the action name / key associations */ + struct hotkey + { + const char *psz_action; + int i_action; + int i_key; + } *p_hotkeys; +}; + diff --git a/VLC/vlc_md5.h b/VLC/vlc_md5.h new file mode 100644 index 0000000..d051269 --- /dev/null +++ b/VLC/vlc_md5.h @@ -0,0 +1,74 @@ +/***************************************************************************** + * vlc_md5.h: MD5 hash + ***************************************************************************** + * Copyright (C) 2004-2005 the VideoLAN team + * $Id$ + * + * Authors: Jon Lech Johansen + * Sam Hocevar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_MD5_H +# define VLC_MD5_H + +/** + * \file + * This file defines functions and structures for handling md5 checksums + */ + +/***************************************************************************** + * md5_s: MD5 message structure + ***************************************************************************** + * This structure stores the static information needed to compute an MD5 + * hash. It has an extra data buffer to allow non-aligned writes. + *****************************************************************************/ +struct md5_s +{ + uint64_t i_bits; /* Total written bits */ + uint32_t p_digest[4]; /* The MD5 digest */ + uint32_t p_data[16]; /* Buffer to cache non-aligned writes */ +}; + +VLC_EXPORT(void, InitMD5, ( struct md5_s * ) ); +VLC_EXPORT(void, AddMD5, ( struct md5_s *, const void *, size_t ) ); +VLC_EXPORT(void, EndMD5, ( struct md5_s * ) ); + +/** + * Returns a char representation of the md5 hash, as shown by UNIX md5 or + * md5sum tools. + */ +static inline char * psz_md5_hash( struct md5_s *md5_s ) +{ + char *psz = malloc( 33 ); /* md5 string is 32 bytes + NULL character */ + if( !psz ) return NULL; + + int i; + for ( i = 0; i < 4; i++ ) + { + sprintf( &psz[8*i], "%02x%02x%02x%02x", + md5_s->p_digest[i] & 0xff, + ( md5_s->p_digest[i] >> 8 ) & 0xff, + ( md5_s->p_digest[i] >> 16 ) & 0xff, + md5_s->p_digest[i] >> 24 + ); + } + psz[32] = '\0'; + + return psz; +} + +#endif diff --git a/VLC/vlc_messages.h b/VLC/vlc_messages.h new file mode 100644 index 0000000..f4cf9fb --- /dev/null +++ b/VLC/vlc_messages.h @@ -0,0 +1,324 @@ +/***************************************************************************** + * messages.h: messages interface + * This library provides basic functions for threads to interact with user + * interface, such as message output. + ***************************************************************************** + * Copyright (C) 1999, 2000, 2001, 2002 the VideoLAN team + * $Id$ + * + * Authors: Vincent Seguin + * Samuel Hocevar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_MESSAGES_H_ +#define VLC_MESSAGES_H_ + +/** + * \file + * This file defines structures and functions to handle messages and statistics gathering + */ + +#include + +/** + * \defgroup messages Messages + * This library provides basic functions for threads to interact with user + * interface, such as message output. + * + * @{ + */ + +/** + * Store a single message sent to user. + */ +typedef struct +{ + int i_type; /**< message type, see below */ + int i_object_id; + const char *psz_object_type; + char * psz_module; + char * psz_msg; /**< the message itself */ + char * psz_header; /**< Additional header */ + + mtime_t date; /**< Message date */ +} msg_item_t; + +/* Message types */ +/** standard messages */ +#define VLC_MSG_INFO 0 +/** error messages */ +#define VLC_MSG_ERR 1 +/** warning messages */ +#define VLC_MSG_WARN 2 +/** debug messages */ +#define VLC_MSG_DBG 3 + +/** + * Used by interface plugins which subscribe to the message bank. + */ +struct msg_subscription_t +{ + int i_start; + int* pi_stop; + + msg_item_t* p_msg; + vlc_mutex_t* p_lock; +}; + +/***************************************************************************** + * Prototypes + *****************************************************************************/ +VLC_EXPORT( void, __msg_Generic, ( vlc_object_t *, int, const char *, const char *, ... ) LIBVLC_FORMAT( 4, 5 ) ); +VLC_EXPORT( void, __msg_GenericVa, ( vlc_object_t *, int, const char *, const char *, va_list args ) ); +#define msg_GenericVa(a, b, c, d, e) __msg_GenericVa(VLC_OBJECT(a), b, c, d, e) +VLC_EXPORT( void, __msg_Info, ( vlc_object_t *, const char *, ... ) LIBVLC_FORMAT( 2, 3 ) ); +VLC_EXPORT( void, __msg_Err, ( vlc_object_t *, const char *, ... ) LIBVLC_FORMAT( 2, 3 ) ); +VLC_EXPORT( void, __msg_Warn, ( vlc_object_t *, const char *, ... ) LIBVLC_FORMAT( 2, 3 ) ); +VLC_EXPORT( void, __msg_Dbg, ( vlc_object_t *, const char *, ... ) LIBVLC_FORMAT( 2, 3 ) ); +#define MODULE_STRING "" +#define msg_Info( p_this, ... ) \ + __msg_Generic( VLC_OBJECT(p_this), VLC_MSG_INFO, \ + MODULE_STRING, __VA_ARGS__ ) +#define msg_Err( p_this, ... ) \ + __msg_Generic( VLC_OBJECT(p_this), VLC_MSG_ERR, \ + MODULE_STRING, __VA_ARGS__ ) +#define msg_Warn( p_this, ... ) \ + __msg_Generic( VLC_OBJECT(p_this), VLC_MSG_WARN, \ + MODULE_STRING, __VA_ARGS__ ) +#define msg_Dbg( p_this, ... ) \ + __msg_Generic( VLC_OBJECT(p_this), VLC_MSG_DBG, \ + MODULE_STRING, __VA_ARGS__ ) + +#define msg_Subscribe(a) __msg_Subscribe(VLC_OBJECT(a)) +#define msg_Unsubscribe(a,b) __msg_Unsubscribe(VLC_OBJECT(a),b) +VLC_EXPORT( msg_subscription_t*, __msg_Subscribe, ( vlc_object_t * ) ); +VLC_EXPORT( void, __msg_Unsubscribe, ( vlc_object_t *, msg_subscription_t * ) ); + +/** + * @} + */ + +/** + * \defgroup statistics Statistics + * + * @{ + */ + +/**************************** + * Generic stats stuff + ****************************/ +enum +{ + STATS_LAST, + STATS_COUNTER, + STATS_MAX, + STATS_MIN, + STATS_DERIVATIVE, + STATS_TIMER +}; + +struct counter_sample_t +{ + vlc_value_t value; + mtime_t date; +}; + +struct counter_t +{ + unsigned int i_id; + char * psz_name; + int i_type; + void * p_obj; + int i_compute_type; + int i_samples; + counter_sample_t ** pp_samples; + + mtime_t update_interval; + mtime_t last_update; +}; + +enum +{ + STATS_INPUT_BITRATE, + STATS_READ_BYTES, + STATS_READ_PACKETS, + STATS_DEMUX_READ, + STATS_DEMUX_BITRATE, + STATS_PLAYED_ABUFFERS, + STATS_LOST_ABUFFERS, + STATS_DECODED_AUDIO, + STATS_DECODED_VIDEO, + STATS_DECODED_SUB, + STATS_CLIENT_CONNECTIONS, + STATS_ACTIVE_CONNECTIONS, + STATS_SOUT_SENT_PACKETS, + STATS_SOUT_SENT_BYTES, + STATS_SOUT_SEND_BITRATE, + STATS_DISPLAYED_PICTURES, + STATS_LOST_PICTURES, + + STATS_TIMER_PLAYLIST_BUILD, + STATS_TIMER_ML_LOAD, + STATS_TIMER_ML_DUMP, + STATS_TIMER_INTERACTION, + STATS_TIMER_PREPARSE, + STATS_TIMER_INPUT_LAUNCHING, + STATS_TIMER_MODULE_NEED, + STATS_TIMER_VIDEO_FRAME_ENCODING, + STATS_TIMER_AUDIO_FRAME_ENCODING, + + STATS_TIMER_SKINS_PLAYTREE_IMAGE, +}; + +#define stats_Update(a,b,c) __stats_Update( VLC_OBJECT(a), b, c ) +VLC_EXPORT( int, __stats_Update, (vlc_object_t*, counter_t *, vlc_value_t, vlc_value_t *) ); +#define stats_CounterCreate(a,b,c) __stats_CounterCreate( VLC_OBJECT(a), b, c ) +VLC_EXPORT( counter_t *, __stats_CounterCreate, (vlc_object_t*, int, int) ); +#define stats_Get(a,b,c) __stats_Get( VLC_OBJECT(a), b, c) +VLC_EXPORT( int, __stats_Get, (vlc_object_t*, counter_t *, vlc_value_t*) ); + +VLC_EXPORT (void, stats_CounterClean, (counter_t * ) ); + +#define stats_GetInteger(a,b,c) __stats_GetInteger( VLC_OBJECT(a), b, c ) +static inline int __stats_GetInteger( vlc_object_t *p_obj, counter_t *p_counter, + int *value ) +{ + int i_ret; + vlc_value_t val; val.i_int = 0; + if( !p_counter ) return VLC_EGENERIC; + i_ret = __stats_Get( p_obj, p_counter, &val ); + *value = val.i_int; + return i_ret; +} + +#define stats_GetFloat(a,b,c) __stats_GetFloat( VLC_OBJECT(a), b, c ) +static inline int __stats_GetFloat( vlc_object_t *p_obj, counter_t *p_counter, + float *value ) +{ + int i_ret; + vlc_value_t val; val.f_float = 0.0; + if( !p_counter ) return VLC_EGENERIC; + i_ret = __stats_Get( p_obj, p_counter, &val ); + *value = val.f_float; + return i_ret; +} +#define stats_UpdateInteger(a,b,c,d) __stats_UpdateInteger( VLC_OBJECT(a),b,c,d ) +static inline int __stats_UpdateInteger( vlc_object_t *p_obj,counter_t *p_co, + int i, int *pi_new ) +{ + int i_ret; + vlc_value_t val; + vlc_value_t new_val; new_val.i_int = 0; + if( !p_co ) return VLC_EGENERIC; + val.i_int = i; + i_ret = __stats_Update( p_obj, p_co, val, &new_val ); + if( pi_new ) + *pi_new = new_val.i_int; + return i_ret; +} +#define stats_UpdateFloat(a,b,c,d) __stats_UpdateFloat( VLC_OBJECT(a),b,c,d ) +static inline int __stats_UpdateFloat( vlc_object_t *p_obj, counter_t *p_co, + float f, float *pf_new ) +{ + vlc_value_t val; + int i_ret; + vlc_value_t new_val;new_val.f_float = 0.0; + if( !p_co ) return VLC_EGENERIC; + val.f_float = f; + i_ret = __stats_Update( p_obj, p_co, val, &new_val ); + if( pf_new ) + *pf_new = new_val.f_float; + return i_ret; +} + +/****************** + * Input stats + ******************/ +struct input_stats_t +{ + vlc_mutex_t lock; + + /* Input */ + int i_read_packets; + int i_read_bytes; + float f_input_bitrate; + float f_average_input_bitrate; + + /* Demux */ + int i_demux_read_packets; + int i_demux_read_bytes; + float f_demux_bitrate; + float f_average_demux_bitrate; + + /* Decoders */ + int i_decoded_audio; + int i_decoded_video; + + /* Vout */ + int i_displayed_pictures; + int i_lost_pictures; + + /* Sout */ + int i_sent_packets; + int i_sent_bytes; + float f_send_bitrate; + + /* Aout */ + int i_played_abuffers; + int i_lost_abuffers; +}; + +VLC_EXPORT( void, stats_ComputeInputStats, (input_thread_t*, input_stats_t*) ); +VLC_EXPORT( void, stats_ReinitInputStats, (input_stats_t *) ); +VLC_EXPORT( void, stats_DumpInputStats, (input_stats_t *) ); + +/******************** + * Global stats + *******************/ +struct global_stats_t +{ + vlc_mutex_t lock; + + float f_input_bitrate; + float f_demux_bitrate; + float f_output_bitrate; + + int i_http_clients; +}; + +#define stats_ComputeGlobalStats(a,b) __stats_ComputeGlobalStats( VLC_OBJECT(a),b) +VLC_EXPORT( void, __stats_ComputeGlobalStats, (vlc_object_t*,global_stats_t*)); + + +/********* + * Timing + ********/ +#define stats_TimerStart(a,b,c) __stats_TimerStart( VLC_OBJECT(a), b,c ) +#define stats_TimerStop(a,b) __stats_TimerStop( VLC_OBJECT(a), b ) +#define stats_TimerDump(a,b) __stats_TimerDump( VLC_OBJECT(a), b ) +#define stats_TimersDumpAll(a) __stats_TimersDumpAll( VLC_OBJECT(a) ) +VLC_EXPORT( void,__stats_TimerStart, (vlc_object_t*, const char *, unsigned int ) ); +VLC_EXPORT( void,__stats_TimerStop, (vlc_object_t*, unsigned int) ); +VLC_EXPORT( void,__stats_TimerDump, (vlc_object_t*, unsigned int) ); +VLC_EXPORT( void,__stats_TimersDumpAll, (vlc_object_t*) ); +#define stats_TimersCleanAll(a) __stats_TimersCleanAll( VLC_OBJECT(a) ) +VLC_EXPORT( void, __stats_TimersCleanAll, (vlc_object_t * ) ); + +#define stats_TimerClean(a,b) __stats_TimerClean( VLC_OBJECT(a), b ) +VLC_EXPORT( void, __stats_TimerClean, (vlc_object_t *, unsigned int ) ); + +#endif diff --git a/VLC/vlc_meta.h b/VLC/vlc_meta.h new file mode 100644 index 0000000..5cfe2ef --- /dev/null +++ b/VLC/vlc_meta.h @@ -0,0 +1,196 @@ +/***************************************************************************** + * vlc_meta.h: Stream meta-data + ***************************************************************************** + * Copyright (C) 2004 the VideoLAN team + * $Id$ + * + * Authors: Laurent Aimar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_META_H +#define VLC_META_H 1 + +/** + * \file + * This file defines functions and structures for stream meta-data in vlc + * + */ + +#include "vlc_arrays.h" + +typedef enum vlc_meta_type_t +{ + vlc_meta_Title, + vlc_meta_Artist, + vlc_meta_Genre, + vlc_meta_Copyright, + vlc_meta_Album, + vlc_meta_TrackNumber, + vlc_meta_Description, + vlc_meta_Rating, + vlc_meta_Date, + vlc_meta_Setting, + vlc_meta_URL, + vlc_meta_Language, + vlc_meta_NowPlaying, + vlc_meta_Publisher, + vlc_meta_EncodedBy, + vlc_meta_ArtworkURL, + vlc_meta_TrackID +} vlc_meta_type_t; + +#define VLC_META_TYPE_COUNT 17 + +/* Returns a localizes string describing the meta */ +VLC_EXPORT(const char *, input_MetaTypeToLocalizedString, ( vlc_meta_type_t meta_type ) ); + +#define ITEM_PREPARSED 0x01 +#define ITEM_ARTURL_FETCHED 0x02 +#define ITEM_ART_FETCHED 0x04 +#define ITEM_ART_NOTFOUND 0x08 + +struct vlc_meta_t +{ + char * ppsz_meta[VLC_META_TYPE_COUNT]; + + vlc_dictionary_t extra_tags; + + int i_status; +}; + +/* Setters for meta. + * Warning: Make sure to use the input_item meta setters (defined in vlc_input.h) + * instead of those one. */ +#define vlc_meta_SetTitle( meta, b ) vlc_meta_Set( meta, vlc_meta_Title, b ) +#define vlc_meta_SetArtist( meta, b ) vlc_meta_Set( meta, vlc_meta_Artist, b ) +#define vlc_meta_SetGenre( meta, b ) vlc_meta_Set( meta, vlc_meta_Genre, b ) +#define vlc_meta_SetCopyright( meta, b ) vlc_meta_Set( meta, vlc_meta_Copyright, b ) +#define vlc_meta_SetAlbum( meta, b ) vlc_meta_Set( meta, vlc_meta_Album, b ) +#define vlc_meta_SetTracknum( meta, b ) vlc_meta_Set( meta, vlc_meta_TrackNumber, b ) +#define vlc_meta_SetDescription( meta, b ) vlc_meta_Set( meta, vlc_meta_Description, b ) +#define vlc_meta_SetRating( meta, b ) vlc_meta_Set( meta, vlc_meta_Rating, b ) +#define vlc_meta_SetDate( meta, b ) vlc_meta_Set( meta, vlc_meta_Date, b ) +#define vlc_meta_SetSetting( meta, b ) vlc_meta_Set( meta, vlc_meta_Setting, b ) +#define vlc_meta_SetURL( meta, b ) vlc_meta_Set( meta, vlc_meta_URL, b ) +#define vlc_meta_SetLanguage( meta, b ) vlc_meta_Set( meta, vlc_meta_Language, b ) +#define vlc_meta_SetNowPlaying( meta, b ) vlc_meta_Set( meta, vlc_meta_NowPlaying, b ) +#define vlc_meta_SetPublisher( meta, b ) vlc_meta_Set( meta, vlc_meta_Publisher, b ) +#define vlc_meta_SetEncodedBy( meta, b ) vlc_meta_Set( meta, vlc_meta_EncodedBy, b ) +#define vlc_meta_SetArtURL( meta, b ) vlc_meta_Set( meta, vlc_meta_ArtworkURL, b ) +#define vlc_meta_SetTrackID( meta, b ) vlc_meta_Set( meta, vlc_meta_TrackID, b ) + +static inline void vlc_meta_Set( vlc_meta_t * p_meta, vlc_meta_type_t meta_type, const char * psz_val ) +{ + free( p_meta->ppsz_meta[meta_type] ); + p_meta->ppsz_meta[meta_type] = psz_val ? strdup( psz_val ) : NULL; +} + +static inline const char * vlc_meta_Get( const vlc_meta_t * p_meta, vlc_meta_type_t meta_type ) +{ + return p_meta->ppsz_meta[meta_type]; +} + +static inline vlc_meta_t *vlc_meta_New( void ) +{ + vlc_meta_t *m = (vlc_meta_t*)malloc( sizeof( vlc_meta_t ) ); + if( !m ) return NULL; + memset( m->ppsz_meta, 0, sizeof(m->ppsz_meta) ); + m->i_status = 0; + vlc_dictionary_init( &m->extra_tags, 0 ); + return m; +} + +static inline void vlc_meta_Delete( vlc_meta_t *m ) +{ + int i; + for( i = 0; i < VLC_META_TYPE_COUNT ; i++ ) + free( m->ppsz_meta[i] ); + vlc_dictionary_clear( &m->extra_tags ); + free( m ); +} + +static inline void vlc_meta_AddExtra( vlc_meta_t *m, const char *psz_name, const char *psz_value ) +{ + char * psz_oldvalue = (char *)vlc_dictionary_value_for_key( &m->extra_tags, psz_name ); + if( psz_oldvalue != kVLCDictionaryNotFound ) + { + free( psz_oldvalue ); + vlc_dictionary_remove_value_for_key( &m->extra_tags, psz_name ); + } + vlc_dictionary_insert( &m->extra_tags, psz_name, strdup(psz_value) ); +} + +static inline void vlc_meta_Merge( vlc_meta_t *dst, const vlc_meta_t *src ) +{ + char ** ppsz_all_keys; + int i; + + if( !dst || !src ) return; + + for( i = 0; i < VLC_META_TYPE_COUNT; i++ ) + { + if( src->ppsz_meta[i] ) + { + free( dst->ppsz_meta[i] ); + dst->ppsz_meta[i] = strdup( src->ppsz_meta[i] ); + } + } + + /* XXX: If speed up are needed, it is possible */ + ppsz_all_keys = vlc_dictionary_all_keys( &src->extra_tags ); + for( i = 0; ppsz_all_keys[i]; i++ ) + { + /* Always try to remove the previous value */ + vlc_dictionary_remove_value_for_key( &dst->extra_tags, ppsz_all_keys[i] ); + void * p_value = vlc_dictionary_value_for_key( &src->extra_tags, ppsz_all_keys[i] ); + vlc_dictionary_insert( &dst->extra_tags, ppsz_all_keys[i], p_value ); + free( ppsz_all_keys[i] ); + } + free( ppsz_all_keys ); +} + +#define VLC_META_TITLE input_MetaTypeToLocalizedString( vlc_meta_Title ) +#define VLC_META_ARTIST input_MetaTypeToLocalizedString( vlc_meta_Artist ) +#define VLC_META_GENRE input_MetaTypeToLocalizedString( vlc_meta_Genre ) +#define VLC_META_COPYRIGHT input_MetaTypeToLocalizedString( vlc_meta_Copyright ) +#define VLC_META_ALBUM input_MetaTypeToLocalizedString( vlc_meta_Album ) +#define VLC_META_TRACK_NUMBER input_MetaTypeToLocalizedString( vlc_meta_TrackNumber ) +#define VLC_META_DESCRIPTION input_MetaTypeToLocalizedString( vlc_meta_Description ) +#define VLC_META_RATING input_MetaTypeToLocalizedString( vlc_meta_Rating ) +#define VLC_META_DATE input_MetaTypeToLocalizedString( vlc_meta_Date ) +#define VLC_META_SETTING input_MetaTypeToLocalizedString( vlc_meta_Setting ) +#define VLC_META_URL input_MetaTypeToLocalizedString( vlc_meta_URL ) +#define VLC_META_LANGUAGE input_MetaTypeToLocalizedString( vlc_meta_Language ) +#define VLC_META_NOW_PLAYING input_MetaTypeToLocalizedString( vlc_meta_NowPlaying ) +#define VLC_META_PUBLISHER input_MetaTypeToLocalizedString( vlc_meta_Publisher ) +#define VLC_META_ENCODED_BY input_MetaTypeToLocalizedString( vlc_meta_EncodedBy ) +#define VLC_META_ART_URL input_MetaTypeToLocalizedString( vlc_meta_ArtworkURL ) +#define VLC_META_TRACKID input_MetaTypeToLocalizedString( vlc_meta_TrackID ) + +enum { + ALBUM_ART_WHEN_ASKED, + ALBUM_ART_WHEN_PLAYED, + ALBUM_ART_ALL +}; + +struct meta_export_t +{ + input_item_t *p_item; + const char *psz_file; +}; + +#endif diff --git a/VLC/vlc_modules.h b/VLC/vlc_modules.h new file mode 100644 index 0000000..9702397 --- /dev/null +++ b/VLC/vlc_modules.h @@ -0,0 +1,72 @@ +/***************************************************************************** + * modules.h : Module descriptor and load functions + ***************************************************************************** + * Copyright (C) 2001 the VideoLAN team + * $Id$ + * + * Authors: Samuel Hocevar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/** + * \file + * This file defines functions for modules in vlc + */ + +/***************************************************************************** + * Exported functions. + *****************************************************************************/ + +#define module_Need(a,b,c,d) __module_Need(VLC_OBJECT(a),b,c,d) +VLC_EXPORT( module_t *, __module_Need, ( vlc_object_t *, const char *, const char *, bool ) ); +#define module_Unneed(a,b) __module_Unneed(VLC_OBJECT(a),b) +VLC_EXPORT( void, __module_Unneed, ( vlc_object_t *, module_t * ) ); +#define module_Exists(a,b) __module_Exists(VLC_OBJECT(a),b) +VLC_EXPORT( bool, __module_Exists, ( vlc_object_t *, const char * ) ); + +#define module_Find(a,b) __module_Find(VLC_OBJECT(a),b) +VLC_EXPORT( module_t *, __module_Find, ( vlc_object_t *, const char * ) ); +VLC_EXPORT( void, module_Put, ( module_t *module ) ); + +VLC_EXPORT( module_config_t *, module_GetConfig, ( const module_t *, unsigned * ) ); +VLC_EXPORT( void, module_PutConfig, ( module_config_t * ) ); + +/* Return a NULL terminated array with the names of the modules that have a + * certain capability. + * Free after uses both the string and the table. */ + #define module_GetModulesNamesForCapability(a,b,c) \ + __module_GetModulesNamesForCapability(VLC_OBJECT(a),b,c) +VLC_EXPORT(char **, __module_GetModulesNamesForCapability, + ( vlc_object_t *p_this, const char * psz_capability, + char ***psz_longname ) ); + +VLC_EXPORT( bool, module_IsCapable, ( const module_t *m, const char *cap ) ); +VLC_EXPORT( const char *, module_GetObjName, ( const module_t *m ) ); +VLC_EXPORT( const char *, module_GetName, ( const module_t *m, bool long_name ) ); +#define module_GetLongName( m ) module_GetName( m, true ) +VLC_EXPORT( const char *, module_GetHelp, ( const module_t *m ) ); + + +#define module_GetMainModule(a) __module_GetMainModule(VLC_OBJECT(a)) +static inline module_t * __module_GetMainModule( vlc_object_t * p_this ) +{ + return module_Find( p_this, "main" ); +} + +static inline bool module_IsMainModule( const module_t * p_module ) +{ + return !strcmp( module_GetObjName( p_module ), "main" ); +} diff --git a/VLC/vlc_mtime.h b/VLC/vlc_mtime.h new file mode 100644 index 0000000..d8cfca9 --- /dev/null +++ b/VLC/vlc_mtime.h @@ -0,0 +1,85 @@ +/***************************************************************************** + * vlc_mtime.h: high resolution time management functions + ***************************************************************************** + * This header provides portable high precision time management functions, + * which should be the only ones used in other segments of the program, since + * functions like gettimeofday() and ftime() are not always supported. + * Most functions are declared as inline or as macros since they are only + * interfaces to system calls and have to be called frequently. + * 'm' stands for 'micro', since maximum resolution is the microsecond. + * Functions prototyped are implemented in interface/mtime.c. + ***************************************************************************** + * Copyright (C) 1996, 1997, 1998, 1999, 2000 the VideoLAN team + * $Id$ + * + * Authors: Vincent Seguin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * LAST_MDATE: date which will never happen + ***************************************************************************** + * This date can be used as a 'never' date, to mark missing events in a function + * supposed to return a date, such as nothing to display in a function + * returning the date of the first image to be displayed. It can be used in + * comparaison with other values: all existing dates will be earlier. + *****************************************************************************/ +#define LAST_MDATE ((mtime_t)((uint64_t)(-1)/2)) + +/***************************************************************************** + * MSTRTIME_MAX_SIZE: maximum possible size of mstrtime + ***************************************************************************** + * This values is the maximal possible size of the string returned by the + * mstrtime() function, including '-' and the final '\0'. It should be used to + * allocate the buffer. + *****************************************************************************/ +#define MSTRTIME_MAX_SIZE 22 + +/* Well, Duh? But it does clue us in that we are converting from + millisecond quantity to a second quantity or vice versa. +*/ +#define MILLISECONDS_PER_SEC 1000 + +#define msecstotimestr(psz_buffer, msecs) \ + secstotimestr( psz_buffer, (msecs / (int) MILLISECONDS_PER_SEC) ) + +/***************************************************************************** + * Prototypes + *****************************************************************************/ +VLC_EXPORT( char *, mstrtime, ( char *psz_buffer, mtime_t date ) ); +VLC_EXPORT( mtime_t, mdate, ( void ) ); +VLC_EXPORT( void, mwait, ( mtime_t date ) ); +VLC_EXPORT( void, msleep, ( mtime_t delay ) ); +VLC_EXPORT( char *, secstotimestr, ( char *psz_buffer, int secs ) ); + +/***************************************************************************** + * date_t: date incrementation without long-term rounding errors + *****************************************************************************/ +struct date_t +{ + mtime_t date; + uint32_t i_divider_num; + uint32_t i_divider_den; + uint32_t i_remainder; +}; + +VLC_EXPORT( void, date_Init, ( date_t *, uint32_t, uint32_t ) ); +VLC_EXPORT( void, date_Change, ( date_t *, uint32_t, uint32_t ) ); +VLC_EXPORT( void, date_Set, ( date_t *, mtime_t ) ); +VLC_EXPORT( mtime_t, date_Get, ( const date_t * ) ); +VLC_EXPORT( void, date_Move, ( date_t *, mtime_t ) ); +VLC_EXPORT( mtime_t, date_Increment, ( date_t *, uint32_t ) ); +VLC_EXPORT( uint64_t, NTPtime64, ( void ) ); diff --git a/VLC/vlc_network.h b/VLC/vlc_network.h new file mode 100644 index 0000000..611fe3e --- /dev/null +++ b/VLC/vlc_network.h @@ -0,0 +1,374 @@ +/***************************************************************************** + * vlc_network.h: interface to communicate with network plug-ins + ***************************************************************************** + * Copyright (C) 2002-2005 the VideoLAN team + * Copyright © 2006-2007 Rémi Denis-Courmont + * $Id$ + * + * Authors: Christophe Massiot + * Laurent Aimar + * Rémi Denis-Courmont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_NETWORK_H +# define VLC_NETWORK_H + +/** + * \file + * This file defines interface to communicate with network plug-ins + */ + +#if defined( WIN32 ) +# if !defined(UNDER_CE) +# define _NO_OLDNAMES 1 +# include +# endif +# include +# include +# define ENETUNREACH WSAENETUNREACH +# define net_errno (WSAGetLastError()) +extern const char *net_strerror( int val ); + +struct iovec +{ + void *iov_base; + size_t iov_len; +}; + +struct msghdr +{ + void *msg_name; + size_t msg_namelen; + struct iovec *msg_iov; + size_t msg_iovlen; + void *msg_control; + size_t msg_controllen; + int msg_flags; +}; + +#define sendmsg vlc_sendmsg +#define recvmsg vlc_recvmsg + +# ifndef IPV6_V6ONLY +# define IPV6_V6ONLY 27 +# endif +#else +# include +# ifdef HAVE_NETINET_IN_H +# include +# endif +# ifdef HAVE_ARPA_INET_H +# include +# elif defined( SYS_BEOS ) +# include +# endif +# include +# define net_errno errno +#endif + +VLC_EXPORT( ssize_t, vlc_sendmsg, ( int, struct msghdr *, int ) ); +VLC_EXPORT( ssize_t, vlc_recvmsg, ( int, struct msghdr *, int ) ); + +# ifdef __cplusplus +extern "C" { +# endif + +/* Portable networking layer communication */ +int net_Socket (vlc_object_t *obj, int family, int socktype, int proto); +int net_SetupSocket (int fd); + +#define net_Connect(a, b, c, d, e) __net_Connect(VLC_OBJECT(a), b, c, d, e) +VLC_EXPORT( int, __net_Connect, (vlc_object_t *p_this, const char *psz_host, int i_port, int socktype, int protocol) ); + +VLC_EXPORT( int *, net_Listen, (vlc_object_t *p_this, const char *psz_host, int i_port, int protocol) ); + +#define net_ListenTCP(a, b, c) net_Listen(VLC_OBJECT(a), b, c, IPPROTO_TCP) +#define net_ConnectTCP(a, b, c) __net_ConnectTCP(VLC_OBJECT(a), b, c) + +static inline int __net_ConnectTCP (vlc_object_t *obj, const char *host, int port) +{ + return __net_Connect (obj, host, port, SOCK_STREAM, IPPROTO_TCP); +} + + +VLC_EXPORT( int, net_AcceptSingle, (vlc_object_t *obj, int lfd) ); + +#define net_Accept(a, b, c) __net_Accept(VLC_OBJECT(a), b, c) +VLC_EXPORT( int, __net_Accept, ( vlc_object_t *, int *, mtime_t ) ); + +#define net_ConnectDgram(a, b, c, d, e ) __net_ConnectDgram(VLC_OBJECT(a), b, c, d, e) +VLC_EXPORT( int, __net_ConnectDgram, ( vlc_object_t *p_this, const char *psz_host, int i_port, int hlim, int proto ) ); + +static inline int net_ConnectUDP (vlc_object_t *obj, const char *host, int port, int hlim) +{ + return net_ConnectDgram (obj, host, port, hlim, IPPROTO_UDP); +} + +#define net_OpenDgram( a, b, c, d, e, g, h ) __net_OpenDgram(VLC_OBJECT(a), b, c, d, e, g, h) +VLC_EXPORT( int, __net_OpenDgram, ( vlc_object_t *p_this, const char *psz_bind, int i_bind, const char *psz_server, int i_server, int family, int proto ) ); + +static inline int net_ListenUDP1 (vlc_object_t *obj, const char *host, int port) +{ + return net_OpenDgram (obj, host, port, NULL, 0, 0, IPPROTO_UDP); +} + +VLC_EXPORT( void, net_ListenClose, ( int *fd ) ); + +int net_Subscribe (vlc_object_t *obj, int fd, const struct sockaddr *addr, + socklen_t addrlen); + +VLC_EXPORT( int, net_SetCSCov, ( int fd, int sendcov, int recvcov ) ); + +/* Functions to read from or write to the networking layer */ +struct virtual_socket_t +{ + void *p_sys; + int (*pf_recv) ( void *, void *, int ); + int (*pf_send) ( void *, const void *, int ); +}; + +#define net_Read(a,b,c,d,e,f) __net_Read(VLC_OBJECT(a),b,c,d,e,f) +VLC_EXPORT( ssize_t, __net_Read, ( vlc_object_t *p_this, int fd, const v_socket_t *, uint8_t *p_data, size_t i_data, bool b_retry ) ); + +#define net_Write(a,b,c,d,e) __net_Write(VLC_OBJECT(a),b,c,d,e) +VLC_EXPORT( ssize_t, __net_Write, ( vlc_object_t *p_this, int fd, const v_socket_t *, const uint8_t *p_data, size_t i_data ) ); + +#define net_Gets(a,b,c) __net_Gets(VLC_OBJECT(a),b,c) +VLC_EXPORT( char *, __net_Gets, ( vlc_object_t *p_this, int fd, const v_socket_t * ) ); + +VLC_EXPORT( ssize_t, net_Printf, ( vlc_object_t *p_this, int fd, const v_socket_t *, const char *psz_fmt, ... ) LIBVLC_FORMAT( 4, 5 ) ); + +#define net_vaPrintf(a,b,c,d,e) __net_vaPrintf(VLC_OBJECT(a),b,c,d,e) +VLC_EXPORT( ssize_t, __net_vaPrintf, ( vlc_object_t *p_this, int fd, const v_socket_t *, const char *psz_fmt, va_list args ) ); + + +/* Don't go to an extra call layer if we have the symbol */ +#ifndef HAVE_INET_PTON +#define inet_pton vlc_inet_pton +#endif +#ifndef HAVE_INET_NTOP +#define inet_ntop vlc_inet_ntop +#endif + +VLC_EXPORT (int, vlc_inet_pton, (int af, const char *src, void *dst) ); +VLC_EXPORT (const char *, vlc_inet_ntop, (int af, const void *src, + char *dst, socklen_t cnt) ); + +#ifndef HAVE_POLL +enum +{ + POLLIN=1, + POLLOUT=2, + POLLPRI=4, + POLLERR=8, // unsupported stub + POLLHUP=16, // unsupported stub + POLLNVAL=32 // unsupported stub +}; + +struct pollfd +{ + int fd; + int events; + int revents; +}; +# define poll(a, b, c) vlc_poll(a, b, c) +#endif +struct pollfd; +VLC_EXPORT (int, vlc_poll, (struct pollfd *fds, unsigned nfds, int timeout)); + + +#ifdef WIN32 +/* Microsoft: same semantic, same value, different name... go figure */ +# define SHUT_RD SD_RECEIVE +# define SHUT_WR SD_SEND +# define SHUT_RDWR SD_BOTH +# define net_Close( fd ) closesocket ((SOCKET)fd) +#else +#ifdef HAVE_UNISTD_H +#include +#endif +# define net_Close( fd ) (void)close (fd) +#endif + +/* Portable network names/addresses resolution layer */ + +/* GAI error codes */ +# ifndef EAI_BADFLAGS +# define EAI_BADFLAGS -1 +# endif +# ifndef EAI_NONAME +# define EAI_NONAME -2 +# endif +# ifndef EAI_AGAIN +# define EAI_AGAIN -3 +# endif +# ifndef EAI_FAIL +# define EAI_FAIL -4 +# endif +# ifndef EAI_NODATA +# define EAI_NODATA -5 +# endif +# ifndef EAI_FAMILY +# define EAI_FAMILY -6 +# endif +# ifndef EAI_SOCKTYPE +# define EAI_SOCKTYPE -7 +# endif +# ifndef EAI_SERVICE +# define EAI_SERVICE -8 +# endif +# ifndef EAI_ADDRFAMILY +# define EAI_ADDRFAMILY -9 +# endif +# ifndef EAI_MEMORY +# define EAI_MEMORY -10 +# endif +#ifndef EAI_OVERFLOW +# define EAI_OVERFLOW -11 +#endif +# ifndef EAI_SYSTEM +# define EAI_SYSTEM -12 +# endif + + +# ifndef NI_MAXHOST +# define NI_MAXHOST 1025 +# define NI_MAXSERV 32 +# endif +# define NI_MAXNUMERICHOST 64 + +# ifndef NI_NUMERICHOST +# define NI_NUMERICHOST 0x01 +# define NI_NUMERICSERV 0x02 +# define NI_NOFQDN 0x04 +# define NI_NAMEREQD 0x08 +# define NI_DGRAM 0x10 +# endif + + /* +# ifndef HAVE_STRUCT_ADDRINFO +struct addrinfo +{ + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + size_t ai_addrlen; + struct sockaddr *ai_addr; + char *ai_canonname; + struct addrinfo *ai_next; +}; +# define AI_PASSIVE 1 +# define AI_CANONNAME 2 +# define AI_NUMERICHOST 4 +# endif //if !HAVE_STRUCT_ADDRINFO +*/ +#ifndef AI_NUMERICSERV +# define AI_NUMERICSERV 0 +#endif + +VLC_EXPORT( const char *, vlc_gai_strerror, ( int ) ); +VLC_EXPORT( int, vlc_getnameinfo, ( const struct sockaddr *, int, char *, int, int *, int ) ); +VLC_EXPORT( int, vlc_getaddrinfo, ( vlc_object_t *, const char *, int, const struct addrinfo *, struct addrinfo ** ) ); +VLC_EXPORT( void, vlc_freeaddrinfo, ( struct addrinfo * ) ); + + +static inline bool +net_SockAddrIsMulticast (const struct sockaddr *addr, socklen_t len) +{ + switch (addr->sa_family) + { +#ifdef IN_MULTICAST + case AF_INET: + { + const struct sockaddr_in *v4 = (const struct sockaddr_in *)addr; + if ((size_t)len < sizeof (*v4)) + return false; + return IN_MULTICAST (ntohl (v4->sin_addr.s_addr)) != 0; + } +#endif + +#ifdef IN6_IS_ADDR_MULTICAST + case AF_INET6: + { + const struct sockaddr_in6 *v6 = (const struct sockaddr_in6 *)addr; + if ((size_t)len < sizeof (*v6)) + return false; + return IN6_IS_ADDR_MULTICAST (&v6->sin6_addr) != 0; + } +#endif + } + + return false; +} + + +static inline int net_GetSockAddress( int fd, char *address, int *port ) +{ + struct sockaddr_storage addr; + socklen_t addrlen = sizeof( addr ); + + return getsockname( fd, (struct sockaddr *)&addr, &addrlen ) + || vlc_getnameinfo( (struct sockaddr *)&addr, addrlen, address, + NI_MAXNUMERICHOST, port, NI_NUMERICHOST ) + ? VLC_EGENERIC : 0; +} + +static inline int net_GetPeerAddress( int fd, char *address, int *port ) +{ + struct sockaddr_storage addr; + socklen_t addrlen = sizeof( addr ); + + return getpeername( fd, (struct sockaddr *)&addr, &addrlen ) + || vlc_getnameinfo( (struct sockaddr *)&addr, addrlen, address, + NI_MAXNUMERICHOST, port, NI_NUMERICHOST ) + ? VLC_EGENERIC : 0; +} + +static inline uint16_t net_GetPort (const struct sockaddr *addr) +{ + switch (addr->sa_family) + { +#ifdef AF_INET6 + case AF_INET6: + return ((const struct sockaddr_in6 *)addr)->sin6_port; +#endif + case AF_INET: + return ((const struct sockaddr_in *)addr)->sin_port; + } + return 0; +} + +static inline void net_SetPort (struct sockaddr *addr, uint16_t port) +{ + switch (addr->sa_family) + { +#ifdef AF_INET6 + case AF_INET6: + ((struct sockaddr_in6 *)addr)->sin6_port = port; + break; +#endif + case AF_INET: + ((struct sockaddr_in *)addr)->sin_port = port; + break; + } +} +# ifdef __cplusplus +} +# endif + +#endif diff --git a/VLC/vlc_objects.h b/VLC/vlc_objects.h new file mode 100644 index 0000000..34149d0 --- /dev/null +++ b/VLC/vlc_objects.h @@ -0,0 +1,199 @@ +/***************************************************************************** + * vlc_objects.h: vlc_object_t definition and manipulation methods + ***************************************************************************** + * Copyright (C) 2002-2008 the VideoLAN team + * $Id$ + * + * Authors: Samuel Hocevar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/** + * \file + * This file defines the vlc_object_t structure and object types. + */ + +/** + * \defgroup vlc_object Objects + * @{ + */ + +/* Object types */ +#define VLC_OBJECT_LIBVLC (-2) +#define VLC_OBJECT_MODULE (-3) +#define VLC_OBJECT_INTF (-4) +#define VLC_OBJECT_PLAYLIST (-5) +#define VLC_OBJECT_INPUT (-7) +#define VLC_OBJECT_DECODER (-8) +#define VLC_OBJECT_VOUT (-9) +#define VLC_OBJECT_AOUT (-10) +#define VLC_OBJECT_PACKETIZER (-13) +#define VLC_OBJECT_ENCODER (-14) +#define VLC_OBJECT_ANNOUNCE (-17) +#define VLC_OBJECT_OPENGL (-21) +#define VLC_OBJECT_OSDMENU (-28) +/* Please add new object types below -34 */ +/* Please do not add new object types anyway */ +#define VLC_OBJECT_GENERIC (-666) + +/* Object search mode */ +#define FIND_PARENT 0x0001 +#define FIND_CHILD 0x0002 +#define FIND_ANYWHERE 0x0003 + +#define FIND_STRICT 0x0010 + +/* Object flags */ +#define OBJECT_FLAGS_NODBG 0x0001 +#define OBJECT_FLAGS_QUIET 0x0002 +#define OBJECT_FLAGS_NOINTERACT 0x0004 + +/* Types */ +typedef void (*vlc_destructor_t)(struct vlc_object_t *); + +/***************************************************************************** + * The vlc_object_t type. Yes, it's that simple :-) + *****************************************************************************/ +/** The main vlc_object_t structure */ +struct vlc_object_t +{ + VLC_COMMON_MEMBERS +}; + +/***************************************************************************** + * Prototypes + *****************************************************************************/ +VLC_EXPORT( void *, __vlc_object_create, ( vlc_object_t *, int ) ); +VLC_EXPORT( void, __vlc_object_set_destructor, ( vlc_object_t *, vlc_destructor_t ) ); +VLC_EXPORT( void, __vlc_object_attach, ( vlc_object_t *, vlc_object_t * ) ); +VLC_EXPORT( void, __vlc_object_detach, ( vlc_object_t * ) ); +VLC_EXPORT( void *, vlc_object_get, ( int ) ); +VLC_EXPORT( void *, __vlc_object_find, ( vlc_object_t *, int, int ) ); +VLC_EXPORT( void *, __vlc_object_find_name, ( vlc_object_t *, const char *, int ) ); +VLC_EXPORT( void, __vlc_object_yield, ( vlc_object_t * ) ); +VLC_EXPORT( void, __vlc_object_release, ( vlc_object_t * ) ); +VLC_EXPORT( vlc_list_t *, __vlc_list_find, ( vlc_object_t *, int, int ) ); +VLC_EXPORT( vlc_list_t *, __vlc_list_children, ( vlc_object_t * ) ); +VLC_EXPORT( void, vlc_list_release, ( vlc_list_t * ) ); + +/* __vlc_object_dump */ +VLC_EXPORT( void, __vlc_object_dump, ( vlc_object_t *p_this ) ); + +/*}@*/ + +#define vlc_object_create(a,b) \ + __vlc_object_create( VLC_OBJECT(a), b ) + +#define vlc_object_set_destructor(a,b) \ + __vlc_object_set_destructor( VLC_OBJECT(a), b ) + +#define vlc_object_detach(a) \ + __vlc_object_detach( VLC_OBJECT(a) ) + +#define vlc_object_attach(a,b) \ + __vlc_object_attach( VLC_OBJECT(a), VLC_OBJECT(b) ) + +#define vlc_object_find(a,b,c) \ + __vlc_object_find( VLC_OBJECT(a),b,c) + +#define vlc_object_find_name(a,b,c) \ + __vlc_object_find_name( VLC_OBJECT(a),b,c) + +#define vlc_object_yield(a) \ + __vlc_object_yield( VLC_OBJECT(a) ) + +#define vlc_object_release(a) \ + __vlc_object_release( VLC_OBJECT(a) ) + +#define vlc_list_find(a,b,c) \ + __vlc_list_find( VLC_OBJECT(a),b,c) + +#define vlc_list_children(a) \ + __vlc_list_children( VLC_OBJECT(a) ) + +#define vlc_object_dump(a) \ + __vlc_object_dump( VLC_OBJECT(a)) + + +/* Objects and threading */ +VLC_EXPORT( void, __vlc_object_lock, ( vlc_object_t * ) ); +#define vlc_object_lock( obj ) \ + __vlc_object_lock( VLC_OBJECT( obj ) ) + +VLC_EXPORT( void, __vlc_object_unlock, ( vlc_object_t * ) ); +#define vlc_object_unlock( obj ) \ + __vlc_object_unlock( VLC_OBJECT( obj ) ) + +VLC_EXPORT( void, __vlc_object_wait, ( vlc_object_t * ) ); +#define vlc_object_wait( obj ) \ + __vlc_object_wait( VLC_OBJECT( obj ) ) + +VLC_EXPORT( int, __vlc_object_timedwait, ( vlc_object_t *, mtime_t ) ); +#define vlc_object_timedwait( obj, d ) \ + __vlc_object_timedwait( VLC_OBJECT( obj ), d ) + +VLC_EXPORT( void, __vlc_object_signal_unlocked, ( vlc_object_t * ) ); +#define vlc_object_signal_unlocked( obj ) \ + __vlc_object_signal_unlocked( VLC_OBJECT( obj ) ) + +static inline void __vlc_object_signal( vlc_object_t *obj ) +{ + vlc_object_lock( obj ); + vlc_object_signal_unlocked( obj ); + vlc_object_unlock( obj ); +} +#define vlc_object_signal( obj ) \ + __vlc_object_signal( VLC_OBJECT( obj ) ) + +VLC_EXPORT( void, __vlc_object_kill, ( vlc_object_t * ) ); +#define vlc_object_kill(a) \ + __vlc_object_kill( VLC_OBJECT(a) ) + +static inline bool __vlc_object_alive (const vlc_object_t *obj) +{ + barrier (); + return !obj->b_die; +} + +#define vlc_object_alive(a) \ + __vlc_object_alive( VLC_OBJECT(a) ) + +VLC_EXPORT( int, __vlc_object_waitpipe, ( vlc_object_t *obj )); +#define vlc_object_waitpipe(a) \ + __vlc_object_waitpipe( VLC_OBJECT(a) ) + +/* NOTE: this function is a *temporary* convenience. + * See the vlc_object_alive() documentation for a better alternative. + */ +static inline +bool __vlc_object_lock_and_wait( vlc_object_t *obj ) +{ + bool b; + + vlc_object_lock( obj ); + b = vlc_object_alive( obj ); + if( b ) + { + vlc_object_wait( obj ); + b = vlc_object_alive( obj ); + } + vlc_object_unlock( obj ); + return b; +} +#define vlc_object_lock_and_wait( obj ) \ + __vlc_object_lock_and_wait( VLC_OBJECT(obj) ) + + diff --git a/VLC/vlc_osd.h b/VLC/vlc_osd.h new file mode 100644 index 0000000..36d7b4d --- /dev/null +++ b/VLC/vlc_osd.h @@ -0,0 +1,685 @@ +/***************************************************************************** + * vlc_osd.h - OSD menu and subpictures definitions and function prototypes + ***************************************************************************** + * Copyright (C) 1999-2006 the VideoLAN team + * Copyright (C) 2004-2005 M2X + * $Id$ + * + * Authors: Jean-Paul Saman + * Gildas Bazin + * + * Added code from include/osd.h written by: + * Copyright (C) 2003-2005 the VideoLAN team + * Authors: Sigmund Augdal Helberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_OSD_H +#define VLC_OSD_H 1 + +#include "vlc_vout.h" + +# ifdef __cplusplus +extern "C" { +# endif + +/** + * \file + * This file defines SPU subpicture and OSD functions and object types. + */ + +/********************************************************************** + * Base SPU structures + **********************************************************************/ +/** + * \defgroup spu Subpicture Unit + * This module describes the programming interface for the subpicture unit. + * It includes functions allowing to create/destroy an spu, create/destroy + * subpictures and render them. + * @{ + */ + +#include "vlc_vout.h" + +/** + * Subpicture unit descriptor + */ +struct spu_t +{ + VLC_COMMON_MEMBERS + + vlc_mutex_t subpicture_lock; /**< subpicture heap lock */ + subpicture_t p_subpicture[VOUT_MAX_SUBPICTURES]; /**< subpictures */ + int i_channel; /**< number of subpicture channels registered */ + + filter_t *p_blend; /**< alpha blending module */ + filter_t *p_text; /**< text renderer module */ + filter_t *p_scale_yuvp; /**< scaling module for YUVP */ + filter_t *p_scale; /**< scaling module (all but YUVP) */ + bool b_force_crop; /**< force cropping of subpicture */ + int i_crop_x, i_crop_y, i_crop_width, i_crop_height; /**< cropping */ + + int i_margin; /**< force position of a subpicture */ + bool b_force_palette; /**< force palette of subpicture */ + uint8_t palette[4][4]; /**< forced palette */ + + int ( *pf_control ) ( spu_t *, int, va_list ); + + /* Supciture filters */ + filter_chain_t *p_chain; +}; + +static inline int spu_vaControl( spu_t *p_spu, int i_query, va_list args ) +{ + if( p_spu->pf_control ) + return p_spu->pf_control( p_spu, i_query, args ); + else + return VLC_EGENERIC; +} + +static inline int spu_Control( spu_t *p_spu, int i_query, ... ) +{ + va_list args; + int i_result; + + va_start( args, i_query ); + i_result = spu_vaControl( p_spu, i_query, args ); + va_end( args ); + return i_result; +} + +enum spu_query_e +{ + SPU_CHANNEL_REGISTER, /* arg1= int * res= */ + SPU_CHANNEL_CLEAR /* arg1= int res= */ +}; + +#define spu_Create(a) __spu_Create(VLC_OBJECT(a)) +VLC_EXPORT( spu_t *, __spu_Create, ( vlc_object_t * ) ); +VLC_EXPORT( int, spu_Init, ( spu_t * ) ); +VLC_EXPORT( void, spu_Destroy, ( spu_t * ) ); +void spu_Attach( spu_t *, vlc_object_t *, bool ); + +VLC_EXPORT( subpicture_t *, spu_CreateSubpicture, ( spu_t * ) ); +VLC_EXPORT( void, spu_DestroySubpicture, ( spu_t *, subpicture_t * ) ); +VLC_EXPORT( void, spu_DisplaySubpicture, ( spu_t *, subpicture_t * ) ); + +#define spu_CreateRegion(a,b) __spu_CreateRegion(VLC_OBJECT(a),b) +VLC_EXPORT( subpicture_region_t *,__spu_CreateRegion, ( vlc_object_t *, video_format_t * ) ); +#define spu_MakeRegion(a,b,c) __spu_MakeRegion(VLC_OBJECT(a),b,c) +VLC_EXPORT( subpicture_region_t *,__spu_MakeRegion, ( vlc_object_t *, video_format_t *, picture_t * ) ); +#define spu_DestroyRegion(a,b) __spu_DestroyRegion(VLC_OBJECT(a),b) +VLC_EXPORT( void, __spu_DestroyRegion, ( vlc_object_t *, subpicture_region_t * ) ); +VLC_EXPORT( subpicture_t *, spu_SortSubpictures, ( spu_t *, mtime_t, bool ) ); +VLC_EXPORT( void, spu_RenderSubpictures, ( spu_t *, video_format_t *, picture_t *, picture_t *, subpicture_t *, int, int ) ); + +/** @}*/ + +/********************************************************************** + * OSD Menu + **********************************************************************/ +/** + * \defgroup osdmenu OSD Menu + * The OSD menu core creates the OSD menu structure in memory. It parses a + * configuration file that defines all elements that are part of the menu. The + * core also handles all actions and menu structure updates on behalf of video + * subpicture filters. + * + * The file modules/video_filters/osdmenu.c implements a subpicture filter that + * specifies the final information on positioning of the current state image. + * A subpicture filter is called each time a video picture has to be rendered, + * it also gives a start and end date to the subpicture. The subpicture can be + * streamed if used inside a transcoding command. For example: + * + * vlc dvdsimple:///dev/dvd --extraintf rc + * --sout='#transcode{osd}:std{access=udp,mux=ts,dst=dest_ipaddr}' + * --osdmenu-file=share/osdmenu/dvd.cfg + * + * An example for local usage of the OSD menu is: + * + * vlc dvdsimple:///dev/dvd --extraintf rc + * --sub-filter osdmenu + * --osdmenu-file=share/osdmenu/dvd.cfg + * + * Each OSD menu element, called "action", defines a hotkey action. Each action + * can have several states (unselect, select, pressed). Each state has an image + * that represents the state visually. The commands "menu right", "menu left", + * "menu up" and "menu down" are used to navigate through the OSD menu structure. + * The commands "menu on" or "menu show" and "menu off" or "menu hide" respectively + * show and hide the OSD menu subpictures. + * + * There is one special element called "range". A range is an arbritary range + * of state images that can be browsed using "menu up" and "menu down" commands + * on the rc interface. + * + * The OSD menu configuration file uses a very simple syntax and basic parser. + * A configuration file has the ".cfg". + * An example is "share/osdmenu/dvd256.cfg". + * @{ + */ + +/** + * \brief The OSD Menu configuration file format. + * + * The configuration file syntax is very basic and so is its parser. See the + * BNF formal representation below: + * + * The keywords FILENAME and PATHNAME represent the filename and pathname + * specification that is valid for the Operating System VLC is compiled for. + * + * The hotkey actions that are supported by VLC are documented in the file + * src/libvlc. The file include/vlc_keys.h defines some hotkey internals. + * + * CONFIG_FILE = FILENAME '.cfg' + * WS = [ ' ' | '\t' ]+ + * OSDMENU_PATH = PATHNAME + * DIR = 'dir' WS OSDMENU_PATH '\n' + * STYLE = 'style' [ 'default' | 'concat' ] '\n' + * STATE = [ 'unselect' | 'select' | 'pressed' ] + * HOTKEY_ACTION = 'key-' [ 'a' .. 'z', 'A' .. 'Z', '-' ]+ + * + * ACTION_TYPE = 'type' 'volume' '\n' + * ACTION_BLOCK_START = 'action' WS HOTKEY_ACTION WS '('POS','POS')' '\n' + * ACTION_BLOCK_END = 'end' '\n' + * ACTION_STATE = STATE WS FILENAME '\n' + * ACTION_RANGE_START = 'range' WS HOTKEY_ACTION WS DEFAULT_INDEX '\n' + * ACTION_RANGE_END = 'end' '\n' + * ACTION_RANGE_STATE = FILENAME '\n' + * + * ACTION_BLOCK_RANGE = ACTION_RANGE_START [WS ACTION_RANGE_STATE]+ WS ACTION_RANGE_END + * ACTION_BLOCK = ACTION_BLOCK_START [WS ACTION_TYPE*] [ [WS ACTION_STATE]+3 | [WS ACTION_BLOCK_RANGE]+1 ] ACTION_BLOCK_END + * CONFIG_FILE_CONTENTS = DIR [ACTION_BLOCK]+ + * + */ + +/** + * OSD menu position and picture type defines + */ + +#define OSD_ALIGN_LEFT 0x1 +#define OSD_ALIGN_RIGHT 0x2 +#define OSD_ALIGN_TOP 0x4 +#define OSD_ALIGN_BOTTOM 0x8 + +#define OSD_HOR_SLIDER 1 +#define OSD_VERT_SLIDER 2 + +#define OSD_PLAY_ICON 1 +#define OSD_PAUSE_ICON 2 +#define OSD_SPEAKER_ICON 3 +#define OSD_MUTE_ICON 4 + +/** + * Text style + * + * A text style is used to specify the formatting of text. + * A font renderer can use the supplied information to render the + * text specified. + */ +struct text_style_t +{ + char * psz_fontname; /**< The name of the font */ + int i_font_size; /**< The font size in pixels */ + int i_font_color; /**< The color of the text 0xRRGGBB + (native endianness) */ + int i_font_alpha; /**< The transparency of the text. + 0x00 is fully opaque, + 0xFF fully transparent */ + int i_style_flags; /**< Formatting style flags */ + int i_outline_color; /**< The color of the outline 0xRRGGBB */ + int i_outline_alpha; /**< The transparency of the outline. + 0x00 is fully opaque, + 0xFF fully transparent */ + int i_shadow_color; /**< The color of the shadow 0xRRGGBB */ + int i_shadow_alpha; /**< The transparency of the shadow. + 0x00 is fully opaque, + 0xFF fully transparent */ + int i_background_color;/**< The color of the background 0xRRGGBB */ + int i_background_alpha;/**< The transparency of the background. + 0x00 is fully opaque, + 0xFF fully transparent */ + int i_karaoke_background_color;/**< Background color for karaoke 0xRRGGBB */ + int i_karaoke_background_alpha;/**< The transparency of the karaoke bg. + 0x00 is fully opaque, + 0xFF fully transparent */ + int i_outline_width; /**< The width of the outline in pixels */ + int i_shadow_width; /**< The width of the shadow in pixels */ + int i_spacing; /**< The spaceing between glyphs in pixels */ +}; + +/* Style flags for \ref text_style_t */ +#define STYLE_BOLD 1 +#define STYLE_ITALIC 2 +#define STYLE_OUTLINE 4 +#define STYLE_SHADOW 8 +#define STYLE_BACKGROUND 16 +#define STYLE_UNDERLINE 32 +#define STYLE_STRIKEOUT 64 + +static const text_style_t default_text_style = { NULL, 22, 0xffffff, 0xff, STYLE_OUTLINE, + 0x000000, 0xff, 0x000000, 0xff, 0xffffff, 0x80, 0xffffff, 0xff, 1, 0, -1 }; + +/** + * OSD menu button states + * + * Every button has three states, either it is unselected, selected or pressed. + * An OSD menu skin can associate images with each state. + * + * OSD_BUTTON_UNSELECT 0 + * OSD_BUTTON_SELECT 1 + * OSD_BUTTON_PRESSED 2 + */ +#define OSD_BUTTON_UNSELECT 0 +#define OSD_BUTTON_SELECT 1 +#define OSD_BUTTON_PRESSED 2 + +/** + * OSD State object + * + * The OSD state object holds the state and associated images for a + * particular state on the screen. The picture is displayed when this + * state is the active state. + */ +struct osd_state_t +{ + osd_state_t *p_next; /*< pointer to next state */ + osd_state_t *p_prev; /*< pointer to previous state */ + picture_t *p_pic; /*< picture of state */ + + char *psz_state; /*< state name */ + int i_state; /*< state index */ + + int i_x; /*< x-position of button state image */ + int i_y; /*< y-position of button state image */ + int i_width; /*< width of button state image */ + int i_height; /*< height of button state image */ +}; + +/** + * OSD Button object + * + * An OSD Button has different states. Each state has an image for display. + */ +struct osd_button_t +{ + osd_button_t *p_next; /*< pointer to next button */ + osd_button_t *p_prev; /*< pointer to previous button */ + osd_button_t *p_up; /*< pointer to up button */ + osd_button_t *p_down; /*< pointer to down button */ + + osd_state_t *p_current_state; /*< pointer to current state image */ + osd_state_t *p_states; /*< doubly linked list of states */ + picture_t *p_feedback; /*< feedback picture */ + + char *psz_name; /*< name of button */ + + /* These member should probably be a struct hotkey */ + char *psz_action; /*< hotkey action name on button*/ + char *psz_action_down; /*< hotkey action name on range buttons + for command "menu down" */ + /* end of hotkey specifics */ + + int i_x; /*< x-position of button visible state image */ + int i_y; /*< y-position of button visible state image */ + int i_width; /*< width of button visible state image */ + int i_height; /*< height of button visible state image */ + + /* range style button */ + bool b_range; /*< button should be interpreted as range */ + int i_ranges; /*< number of states */ +}; + +/** + * OSD Menu Style + * + * The images that make up an OSD menu can be created in such away that + * they contain all buttons in the same picture, with the selected one + * highlighted or being a concatenation of all the seperate images. The + * first case is the default. + * + * To change the default style the keyword 'style' should be set to 'concat'. + */ + +#define OSD_MENU_STYLE_SIMPLE 0x0 +#define OSD_MENU_STYLE_CONCAT 0x1 + +/** + * OSD Menu State object + * + * Represents the current state as displayed. + */ +/* Represent the menu state */ +struct osd_menu_state_t +{ + int i_x; /*< x position of spu region */ + int i_y; /*< y position of spu region */ + int i_width; /*< width of spu region */ + int i_height; /*< height of spu region */ + + picture_t *p_pic; /*< pointer to picture to display */ + osd_button_t *p_visible; /*< shortcut to visible button */ + + bool b_menu_visible; /*< menu currently visible? */ + bool b_update; /*< update OSD Menu when true */ + + /* quick hack to volume state. */ + osd_button_t *p_volume; /*< pointer to volume range object. */ +}; + +/** + * OSD Menu object + * + * The main OSD Menu object, which holds a linked list to all buttons + * and images that defines the menu. The p_state variable represents the + * current state of the OSD Menu. + */ +struct osd_menu_t +{ + VLC_COMMON_MEMBERS + + int i_x; /*< x-position of OSD Menu on the video screen */ + int i_y; /*< y-position of OSD Menu on the video screen */ + int i_width; /*< width of OSD Menu on the video screen */ + int i_height; /*< height of OSD Menu on the video screen */ + int i_style; /*< style of spu region generation */ + int i_position; /*< display position */ + + char *psz_path; /*< directory where OSD menu images are stored */ + osd_button_t *p_button; /*< doubly linked list of buttons */ + osd_menu_state_t *p_state; /*< current state of OSD menu */ + + /* quick link in the linked list. */ + osd_button_t *p_last_button; /*< pointer to last button in the list */ + + /* misc parser */ + module_t *p_parser; /*< pointer to parser module */ + char *psz_file; /*< Config file name */ + image_handler_t *p_image; /*< handler to image loading and conversion libraries */ +}; + +/** + * Initialize an osd_menu_t object + * + * This functions has to be called before any call to other osd_menu_t* + * functions. It creates the osd_menu object and holds a pointer to it + * during its lifetime. + */ +VLC_EXPORT( osd_menu_t *, __osd_MenuCreate, ( vlc_object_t *, const char * ) ); + +/** + * Delete the osd_menu_t object + * + * This functions has to be called to release the associated module and + * memory for the osdmenu. After return of this function the pointer to + * osd_menu_t* is invalid. + */ +VLC_EXPORT( void, __osd_MenuDelete, ( vlc_object_t *, osd_menu_t * ) ); + +#define osd_MenuCreate(object,file) __osd_MenuCreate( VLC_OBJECT(object), file ) +#define osd_MenuDelete(object,osd) __osd_MenuDelete( VLC_OBJECT(object), osd ) + +/** + * Find OSD Menu button at position x,y + */ +VLC_EXPORT( osd_button_t *, __osd_ButtonFind, ( vlc_object_t *p_this, + int, int, int, int, int, int ) ); + +#define osd_ButtonFind(object,x,y,h,w,sh,sw) __osd_ButtonFind(object,x,y,h,w,sh,sw) + +/** + * Select the button provided as the new active button + */ +VLC_EXPORT( void, __osd_ButtonSelect, ( vlc_object_t *, osd_button_t *) ); + +#define osd_ButtonSelect(object,button) __osd_ButtonSelect(object,button) + +/** + * Show the OSD menu. + * + * Show the OSD menu on the video output or mux it into the stream. + * Every change to the OSD menu will now be visible in the output. An output + * can be a video output window or a stream (\see stream output) + */ +VLC_EXPORT( void, __osd_MenuShow, ( vlc_object_t * ) ); + +/** + * Hide the OSD menu. + * + * Stop showing the OSD menu on the video output or mux it into the stream. + */ +VLC_EXPORT( void, __osd_MenuHide, ( vlc_object_t * ) ); + +/** + * Activate the action of this OSD menu item. + * + * The rc interface command "menu select" triggers the sending of an + * hotkey action to the hotkey interface. The hotkey that belongs to + * the current highlighted OSD menu item will be used. + */ +VLC_EXPORT( void, __osd_MenuActivate, ( vlc_object_t * ) ); + +#define osd_MenuShow(object) __osd_MenuShow( VLC_OBJECT(object) ) +#define osd_MenuHide(object) __osd_MenuHide( VLC_OBJECT(object) ) +#define osd_MenuActivate(object) __osd_MenuActivate( VLC_OBJECT(object) ) + +/** + * Next OSD menu item + * + * Select the next OSD menu item to be highlighted. + * Note: The actual position on screen of the menu item is determined by + * the OSD menu configuration file. + */ +VLC_EXPORT( void, __osd_MenuNext, ( vlc_object_t * ) ); + +/** + * Previous OSD menu item + * + * Select the previous OSD menu item to be highlighted. + * Note: The actual position on screen of the menu item is determined by + * the OSD menu configuration file. + */ +VLC_EXPORT( void, __osd_MenuPrev, ( vlc_object_t * ) ); + +/** + * OSD menu item above + * + * Select the OSD menu item above the current item to be highlighted. + * Note: The actual position on screen of the menu item is determined by + * the OSD menu configuration file. + */ +VLC_EXPORT( void, __osd_MenuUp, ( vlc_object_t * ) ); + +/** + * OSD menu item below + * + * Select the next OSD menu item below the current item to be highlighted. + * Note: The actual position on screen of the menu item is determined by + * the OSD menu configuration file. + */ +VLC_EXPORT( void, __osd_MenuDown, ( vlc_object_t * ) ); + +#define osd_MenuNext(object) __osd_MenuNext( VLC_OBJECT(object) ) +#define osd_MenuPrev(object) __osd_MenuPrev( VLC_OBJECT(object) ) +#define osd_MenuUp(object) __osd_MenuUp( VLC_OBJECT(object) ) +#define osd_MenuDown(object) __osd_MenuDown( VLC_OBJECT(object) ) + +/** + * Display the audio volume bitmap. + * + * Display the correct audio volume bitmap that corresponds to the + * current Audio Volume setting. + */ +VLC_EXPORT( void, __osd_Volume, ( vlc_object_t * ) ); + +#define osd_Volume(object) __osd_Volume( VLC_OBJECT(object) ) + +/** + * Retrieve a non modifyable pointer to the OSD Menu state + * + */ +static inline const osd_menu_state_t *osd_GetMenuState( osd_menu_t *p_osd ) +{ + return( p_osd->p_state ); +} + +/** + * Get the last key press received by the OSD Menu + * + * Returns 0 when no key has been pressed or the value of the key pressed. + */ +static inline bool osd_GetKeyPressed( osd_menu_t *p_osd ) +{ + return( p_osd->p_state->b_update ); +} + +/** + * Set the key pressed to a value. + * + * Assign a new key value to the last key pressed on the OSD Menu. + */ +static inline void osd_SetKeyPressed( vlc_object_t *p_this, int i_value ) +{ + vlc_value_t val; + + val.i_int = i_value; + var_Set( p_this, "key-pressed", val ); +} + +/** + * Update the OSD Menu visibility flag. + * + * true means OSD Menu should be shown. false means OSD Menu + * should not be shown. + */ +static inline void osd_SetMenuVisible( osd_menu_t *p_osd, bool b_value ) +{ + vlc_value_t val; + + val.b_bool = p_osd->p_state->b_menu_visible = b_value; + var_Set( p_osd, "osd-menu-visible", val ); +} + +/** + * Update the OSD Menu update flag + * + * If the OSD Menu should be updated then set the update flag to + * true, else to false. + */ +static inline void osd_SetMenuUpdate( osd_menu_t *p_osd, bool b_value ) +{ + vlc_value_t val; + + val.b_bool = p_osd->p_state->b_update = b_value; + var_Set( p_osd, "osd-menu-update", val ); +} + +/** + * Textual feedback + * + * Functions that provide the textual feedback on the OSD. They are shown + * on hotkey commands. The feedback is also part of the osd_button_t + * object. The types are declared in the include file include/vlc_osd.h + * @see vlc_osd.h + */ +VLC_EXPORT( int, osd_ShowTextRelative, ( spu_t *, int, char *, text_style_t *, int, int, int, mtime_t ) ); +VLC_EXPORT( int, osd_ShowTextAbsolute, ( spu_t *, int, char *, text_style_t *, int, int, int, mtime_t, mtime_t ) ); +VLC_EXPORT( void,osd_Message, ( spu_t *, int, char *, ... ) LIBVLC_FORMAT( 3, 4 ) ); + +/** + * Default feedback images + * + * Functions that provide the default OSD feedback images on hotkey + * commands. These feedback images are also part of the osd_button_t + * object. The types are declared in the include file include/vlc_osd.h + * @see vlc_osd.h + */ +VLC_EXPORT( int, osd_Slider, ( vlc_object_t *, spu_t *, int, int, int, int, int, int, short ) ); +VLC_EXPORT( int, osd_Icon, ( vlc_object_t *, spu_t *, int, int, int, int, int, short ) ); + +/** @} */ + +/********************************************************************** + * Vout text and widget overlays + **********************************************************************/ + +/** + * Show text on the video for some time + * \param p_vout pointer to the vout the text is to be showed on + * \param i_channel Subpicture channel + * \param psz_string The text to be shown + * \param p_style Pointer to a struct with text style info + * \param i_flags flags for alignment and such + * \param i_hmargin horizontal margin in pixels + * \param i_vmargin vertical margin in pixels + * \param i_duration Amount of time the text is to be shown. + */ +VLC_EXPORT( int, vout_ShowTextRelative, ( vout_thread_t *, int, char *, text_style_t *, int, int, int, mtime_t ) ); + +/** + * Show text on the video from a given start date to a given end date + * \param p_vout pointer to the vout the text is to be showed on + * \param i_channel Subpicture channel + * \param psz_string The text to be shown + * \param p_style Pointer to a struct with text style info + * \param i_flags flags for alignment and such + * \param i_hmargin horizontal margin in pixels + * \param i_vmargin vertical margin in pixels + * \param i_start the time when this string is to appear on the video + * \param i_stop the time when this string should stop to be displayed + * if this is 0 the string will be shown untill the next string + * is about to be shown + */ +VLC_EXPORT( int, vout_ShowTextAbsolute, ( vout_thread_t *, int, const char *, text_style_t *, int, int, int, mtime_t, mtime_t ) ); + +/** + * Write an informative message at the default location, + * for the default duration and only if the OSD option is enabled. + * \param p_caller The object that called the function. + * \param i_channel Subpicture channel + * \param psz_format printf style formatting + **/ +VLC_EXPORT( void, __vout_OSDMessage, ( vlc_object_t *, int, const char *, ... ) LIBVLC_FORMAT( 3, 4 ) ); + +/** + * Same as __vlc_OSDMessage() but with automatic casting + */ +#define vout_OSDMessage( obj, chan, ...) \ + __vout_OSDMessage( VLC_OBJECT(obj), chan, __VA_ARGS__ ) + +/** + * Display a slider on the video output. + * \param p_this The object that called the function. + * \param i_channel Subpicture channel + * \param i_postion Current position in the slider + * \param i_type Types are: OSD_HOR_SLIDER and OSD_VERT_SLIDER. + * @see vlc_osd.h + */ +VLC_EXPORT( void, vout_OSDSlider, ( vlc_object_t *, int, int , short ) ); + +/** + * Display an Icon on the video output. + * \param p_this The object that called the function. + * \param i_channel Subpicture channel + * \param i_type Types are: OSD_PLAY_ICON, OSD_PAUSE_ICON, OSD_SPEAKER_ICON, OSD_MUTE_ICON + * @see vlc_osd.h + */ +VLC_EXPORT( void, vout_OSDIcon, ( vlc_object_t *, int, short ) ); + +# ifdef __cplusplus +} +# endif + +#endif /* _VLC_OSD_H */ diff --git a/VLC/vlc_pgpkey.h b/VLC/vlc_pgpkey.h new file mode 100644 index 0000000..6aedee3 --- /dev/null +++ b/VLC/vlc_pgpkey.h @@ -0,0 +1,81 @@ +/***************************************************************************** + * vlc_pgpkey.h: VideoLAN PGP Public Key used to sign releases + ***************************************************************************** + * Copyright © 2008 the VideoLAN team + * $Id$ + * + * Authors: Rafaël Carré + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either release 2 of the License, or + * (at your option) any later release. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + +/* We trust this public key, and by extension, also keys signed by it. */ + +/* + * VideoLAN Release Signing Key (2008) + * expirates on 2009-01-01 + */ + +static uint8_t videolan_public_key_longid[8] = { + 0x8B, 0x08, 0x52, 0x31, 0xD0, 0x38, 0x35, 0x37 +}; + +/* gpg --export --armor "VideoLAN Release"|sed -e s/^/\"/ -e s/\$/\\\\n\"/ */ +static uint8_t videolan_public_key[] = { +"-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +"Version: GnuPG v2.0.4 (FreeBSD)\n" +"\n" +"mQGiBEd7jcYRBAD4NRNnzqPIq6QMI6M8nmI7G569zJjy8NQNhqtuTlpqRlNqhDdt\n" +"aYcYFSBKW7YXs03BCcDNFfUpB4wexsD9z+aOTzAFs+tVmB0XyKlPc2IaMuwV9tYS\n" +"6LG2TITzWgZ5kyEtyVdDr4xvdTD1S/E2sraW/i1CgJkA/5HtgC3LksvirwCg2yQn\n" +"d+sA8KQEC66+ELV4hNn4eAsD/0ObYdZEM0B6E0hVAyabKTVYGs7MT6UjbHTaxhzV\n" +"PN6Qss1Zmm/oKA5ClNIrvSO6dqzSC+OMQwwHYizOgfObO116LWzMo+YSDyWNonRT\n" +"Ex5BtJcvyA18qbNkka79I+VYCsoLlk7pRyEc14HhMCBpR0dVl53w102RmwkXigO3\n" +"FL5kBAC4Hvy3FsV7DmwM/QccrfTDzD7SFPXnn+w5HluhCXseoiYkCSjNa8iDpG/e\n" +"AKrlwnWwEH50Q/tsD+hysnLd7dk/tGP0a4VkqcZ69pyxAql8vClBpd76udrquMKq\n" +"IFN8m2MFzkYdYSezR4yro4NLmgyri4xomjxVjboR2eXnQPUnlrQjVmlkZW9MQU4g\n" +"UmVsZWFzZSBTaWduaW5nIEtleSAoMjAwOCmIZgQTEQIAJgUCR3uNxgIbAwUJAeEz\n" +"gAYLCQgHAwIEFQIIAwQWAgMBAh4BAheAAAoJEIsIUjHQODU3xtoAoLgtA2m+qmOD\n" +"0W07hdZkqtJPW8frAJwOr4Le14j1FB6jKs8FvDsW6EL1bIhGBBARAgAGBQJHe45m\n" +"AAoJEGFgnhjAr/EP4F8AoKa8Ip/bUqk/+yASpBuKNqLZgYduAKCKqJfK4Z8zN2We\n" +"8NvZLTT66/zGxIhGBBARAgAGBQJHe46fAAoJEJAoF+SqX03m1dEAoOWl4gQsSOQG\n" +"fHfke5hAy9O2FdFDAJwOynmqM7ZAlHmvlQsUHuP1gZXGBbkEDQRHe44rEBAAzygZ\n" +"HacW1jQCOt9pI1g3ilvQYEOAosXNUV9R7c+tUySFR+t8wIwkYnUZ9WMg94oBn618\n" +"7hQHFuRoKxlinH11Elv0PvkBQPbhLq2QFX7ItAkuoVMejoZ+vUHSuJt7UNJ1YOWg\n" +"cIxOkVDkgDLl5HVbXVFU/RzKfFDr45o02NnNi8wbyIU65QFnvPNz1lLjcqQ9nTCy\n" +"8ntdW1XozQap6IFE07ZmPhNfGeMx2JlauHnZvgxORTrDjDX9o5LjTt0ubmR7Nt0x\n" +"ShXcXU+HyIAn8ZD8GmvhiDDTYJjVUnrugzBFtpyGrT8J+x1GHKNNUXfXmzw9i5jK\n" +"WWa9XxDKoyi7ktr7ZrmJBHjYinLQs1KfAFHYWw9zdjtTnx3q5kPIPnE2PVR0zkbj\n" +"tD2dPrpdbcjZ/XgiJOUVx+wcGGaYSMlPor/Wii8fJLHbp6/ZV2NzXOm0v7+uIRR+\n" +"9SfG/Tx0B88ehw8pxmPXmsgawzz3XXz+indGv9SYm/0ZQLEQrIzpsyrQk3BlCnFg\n" +"AuyDHbKzsVg+bz8u3vJ3ELls9/A9g0Aka4RoHjstm/mcDsZ7gQ5+mO0kfVydg+Rt\n" +"V2Yct3dWwxAU8JxBlkE/iQ46dllrRXGlC+x3Sn8VUZn3WpoRQHwzt+ZNtirl5VOy\n" +"jilh44FqHqvAJj+nDRu3pDITDqkpuYO5Z2MqcNsAAwYP/3p4vW/UD4xC6zLwgznx\n" +"3wZLa1/ct9BA1OKThV3NE2QswajiIRWzEdk9ZbJwkSBx8TXFYXPcfvbxOvhmdlWY\n" +"o/0HuAkShymTcfroEAsznh1qpu3jEdVMMHNCbkPRtWdealXTGzH+MH4EmkoxDxZ4\n" +"qqQjMc1YjCEOFUiuzPiJryMepQhRlZ0Vgvvzw/1A6uEFXu28KV+xehgerALNDAWe\n" +"JHKSPBoJupykEM+c/Avg83NE5AayKXVPuWlehUfxAcKZwAHxQ+HwCmUoSJiyLYBF\n" +"CFfYGiwB7WrbD65AfBDU1sVD58H+MZhbj3lT5h8PPG57PelcVPXSbKD93qIW51TN\n" +"iSxGM77hFA0fnNj3FiMRnjM9wCE5FmmK/J0pP5aAekWE4IpaklzKSl7VlDqj097o\n" +"gA5nlfEIZjqtRhxtdYHSbXV/+Yy9PxoZAGImFSNf8ZlcMw9ioC8TpXkRcxQr2iBO\n" +"YmD3NRNGnSl7lG7fDdtAnZ9BbAYUtxFMaHNrwWHlqJn+X4rZsk5CZs2oF6obkQSI\n" +"FO27OgupwFOHIUcc38RTPTZN6wTLGY/j1twBmQdVpSHsRjjtdQ0qEOXe1rZK9Nh9\n" +"unX70TDBo1Ig0CGpKqk4I8hloyjrOk6szIfOpJFlT2LTrSWbDtPE0tMdwh9fnZUL\n" +"Rt021q8MvoRxyTbTWO7Nurw0iE8EGBECAA8FAkd7jisCGwwFCQHhM4AACgkQiwhS\n" +"MdA4NTeFXwCfc0eO+gbbE+aSCMoTTxZ8ivsjlR0An3WCvfP6aTEJnzJbmpqO4AMu\n" +"FltR\n" +"=Ic/K\n" +"-----END PGP PUBLIC KEY BLOCK-----\n" +}; diff --git a/VLC/vlc_playlist.h b/VLC/vlc_playlist.h new file mode 100644 index 0000000..9591a80 --- /dev/null +++ b/VLC/vlc_playlist.h @@ -0,0 +1,455 @@ +/***************************************************************************** + * vlc_playlist.h : Playlist functions + ***************************************************************************** + * Copyright (C) 1999-2004 the VideoLAN team + * $Id$ + * + * Authors: Samuel Hocevar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_PLAYLIST_H_ +#define VLC_PLAYLIST_H_ + +# ifdef __cplusplus +extern "C" { +# endif + +#include "vlc_input.h" +#include "vlc_events.h" +#include "vlc_services_discovery.h" +#include +#include + +TYPEDEF_ARRAY(playlist_item_t*, playlist_item_array_t); + +/** + * \file + * This file contain structures and function prototypes related + * to the playlist in vlc + * + * \defgroup vlc_playlist Playlist + * + * The VLC playlist system has a tree structure. This allows advanced + * categorization, like for SAP streams (which are grouped by "sap groups"). + * + * The base structure for all playlist operations is the input_item_t. This + * contains all information needed to play a stream and get info, ie, mostly, + * mrl and metadata. This structure contains a unique i_id field. ids are + * not recycled when an item is destroyed. + * + * Input items are not used directly, but through playlist items. + * The playlist items are themselves in a tree structure. They only contain + * a link to the input item, a unique id and a few flags. the playlist + * item id is NOT the same as the input item id. + * Several playlist items can be attached to a single input item. The input + * item is refcounted and is automatically destroyed when it is not used + * anymore. + * + * In the playlist itself, there are two trees, that should always be kept + * in sync. The "category" tree contains the whole tree structure with + * several levels, while the onelevel tree contains only one level :), ie + * it only contains "real" items, not nodes + * For example, if you open a directory, you will have + *\verbatim + * Category tree: Onelevel tree: + * Playlist Playlist + * - Dir - item1 + * - Subdir - item2 + * - item1 + * - item2 + *\endverbatim + * The top-level items of both tree are the same, and they are reproduced + * in the left-part of the playlist GUIs, they are the "sources" from the + * source selectors. Top-level items include: playlist, media library, SAP, + * Shoutcast, devices, ... + * + * It is envisioned that a third tree will appear: VLM, but it's not done yet + * + * The playlist also stores, for utility purposes, an array of all input + * items, an array of all playlist items and an array of all playlist items + * and nodes (both are represented by the same structure). + * + * So, here is an example: + * \verbatim + * Inputs array + * - input 1 -> name = foo 1 uri = ... + * - input 2 -> name = foo 2 uri = ... + * + * Category tree Onelevel tree + * - playlist (id 1) - playlist (id 3) + * - category 1 (id 2) - foo 2 (id 8 - input 2) + * - foo 2 (id 6 - input 2) - media library (id 4) + * - media library (id 2) - foo 1 (id6 - input 1) + * - foo 1 (id 5 - input 1) + * \endverbatim + * Sometimes, an item must be transformed to a node. This happens for the + * directory access for example. In that case, the item is removed from + * the onelevel tree, as it is not a real item anymore. + * + * For "standard" item addition, you can use playlist_Add, playlist_AddExt + * (more options) or playlist_AddInput if you already created your input + * item. This will add the item at the root of "Playlist" or of "Media library" + * in each of the two trees. + * + * If you want more control (like, adding the item as the child of a given + * node in the category tree, use playlist_BothAddInput. You'll have to provide + * the node in the category tree. The item will be added as a child of + * this node in the category tree, and as a child of the matching top-level + * node in the onelevel tree. (Nodes are created with playlist_NodeCreate) + * + * Generally speaking, playlist_NodeAddInput should not be used in newer code, it + * will maybe become useful again when we merge VLM; + * + * To delete an item, use playlist_DeleteFromInput( input_id ) which will + * remove all occurrences of the input in both trees + * + * @{ + */ + +/** Helper structure to export to file part of the playlist */ +struct playlist_export_t +{ + char *psz_filename; + FILE *p_file; + playlist_item_t *p_root; +}; + +/** playlist item / node */ +struct playlist_item_t +{ + input_item_t *p_input; /**< Linked input item */ + /** Number of children, -1 if not a node */ + int i_children; + playlist_item_t **pp_children; /**< Children nodes/items */ + playlist_item_t *p_parent; /**< Item parent */ + + int i_id; /**< Playlist item specific id */ + uint8_t i_flags; /**< Flags */ + playlist_t *p_playlist; /**< Parent playlist */ +}; + +#define PLAYLIST_SAVE_FLAG 0x0001 /**< Must it be saved */ +#define PLAYLIST_SKIP_FLAG 0x0002 /**< Must playlist skip after it ? */ +#define PLAYLIST_DBL_FLAG 0x0004 /**< Is it disabled ? */ +#define PLAYLIST_RO_FLAG 0x0008 /**< Write-enabled ? */ +#define PLAYLIST_REMOVE_FLAG 0x0010 /**< Remove this item at the end */ +#define PLAYLIST_EXPANDED_FLAG 0x0020 /**< Expanded node */ + +/** Playlist status */ +typedef enum +{ PLAYLIST_STOPPED,PLAYLIST_RUNNING,PLAYLIST_PAUSED } playlist_status_t; + +/** Structure containing information about the playlist */ +struct playlist_t +{ + VLC_COMMON_MEMBERS + + struct playlist_services_discovery_support_t { + /* the playlist items for category and onelevel */ + playlist_item_t* p_cat; + playlist_item_t* p_one; + services_discovery_t * p_sd; /**< Loaded service discovery modules */ + } ** pp_sds; + int i_sds; /**< Number of service discovery modules */ + + playlist_item_array_t items; /**< Arrays of items */ + playlist_item_array_t all_items; /**< Array of items and nodes */ + playlist_item_array_t items_to_delete; /**< Array of items and nodes to + delete... At the very end. This sucks. */ + + playlist_item_array_t current; /**< Items currently being played */ + int i_current_index; /**< Index in current array */ + /** Reset current item array */ + bool b_reset_currently_playing; + mtime_t last_rebuild_date; + + int i_last_playlist_id; /**< Last id to an item */ + + /* Predefined items */ + playlist_item_t * p_root_category; /**< Root of category tree */ + playlist_item_t * p_root_onelevel; /**< Root of onelevel tree */ + playlist_item_t * p_local_category; /** < "Playlist" in CATEGORY view */ + playlist_item_t * p_ml_category; /** < "Library" in CATEGORY view */ + playlist_item_t * p_local_onelevel; /** < "Playlist" in ONELEVEL view */ + playlist_item_t * p_ml_onelevel; /** < "Library" in ONELEVEL view */ + + bool b_tree; /**< Display as a tree */ + + bool b_doing_ml; /**< Doing media library stuff, + * get quicker */ + bool b_auto_preparse; + + /* Runtime */ + input_thread_t * p_input; /**< the input thread associated + * with the current item */ + int i_sort; /**< Last sorting applied to the playlist */ + int i_order; /**< Last ordering applied to the playlist */ + mtime_t gc_date; + bool b_cant_sleep; + playlist_preparse_t *p_preparse; /**< Preparser object */ + playlist_fetcher_t *p_fetcher;/**< Meta and art fetcher object */ + + struct { + /* Current status. These fields are readonly, only the playlist + * main loop can touch it*/ + playlist_status_t i_status; /**< Current status of playlist */ + playlist_item_t * p_item; /**< Currently playing/active item */ + playlist_item_t * p_node; /**< Current node to play from */ + } status; + + struct { + /* Request. Use this to give orders to the playlist main loop */ + playlist_status_t i_status; /**< requested playlist status */ + playlist_item_t * p_node; /**< requested node to play from */ + playlist_item_t * p_item; /**< requested item to play in the node */ + + int i_skip; /**< Number of items to skip */ + + bool b_request;/**< Set to true by the requester + The playlist sets it back to false + when processing the request */ + vlc_mutex_t lock; /**< Lock to protect request */ + } request; +}; + +/** Helper to add an item */ +struct playlist_add_t +{ + int i_node; + int i_item; + int i_position; +}; + +#define SORT_ID 0 +#define SORT_TITLE 1 +#define SORT_TITLE_NODES_FIRST 2 +#define SORT_ARTIST 3 +#define SORT_GENRE 4 +#define SORT_RANDOM 5 +#define SORT_DURATION 6 +#define SORT_TITLE_NUMERIC 7 +#define SORT_ALBUM 8 +#define SORT_TRACK_NUMBER 9 +#define SORT_DESCRIPTION 10 +#define SORT_RATING 11 + +#define ORDER_NORMAL 0 +#define ORDER_REVERSE 1 + +/* Used by playlist_Import */ +#define PLAYLIST_INSERT 0x0001 +#define PLAYLIST_APPEND 0x0002 +#define PLAYLIST_GO 0x0004 +#define PLAYLIST_PREPARSE 0x0008 +#define PLAYLIST_SPREPARSE 0x0010 +#define PLAYLIST_NO_REBUILD 0x0020 + +#define PLAYLIST_END -666 + +enum pl_locked_state +{ + pl_Locked = true, + pl_Unlocked = false +}; + +/***************************************************************************** + * Prototypes + *****************************************************************************/ + +/* Helpers */ +#define PL_LOCK vlc_object_lock( p_playlist ) +#define PL_UNLOCK vlc_object_unlock( p_playlist ) + +VLC_EXPORT( playlist_t *, __pl_Yield, ( vlc_object_t * ) ); +#define pl_Yield( a ) __pl_Yield( VLC_OBJECT(a) ) + +VLC_EXPORT( void, __pl_Release, ( vlc_object_t * ) ); +#define pl_Release(a) __pl_Release( VLC_OBJECT(a) ) + +/* Playlist control */ +#define playlist_Play(p) playlist_Control(p,PLAYLIST_PLAY, pl_Unlocked ) +#define playlist_Pause(p) playlist_Control(p,PLAYLIST_PAUSE, pl_Unlocked ) +#define playlist_Stop(p) playlist_Control(p,PLAYLIST_STOP, pl_Unlocked ) +#define playlist_Next(p) playlist_Control(p,PLAYLIST_SKIP, pl_Unlocked, 1) +#define playlist_Prev(p) playlist_Control(p,PLAYLIST_SKIP, pl_Unlocked, -1) +#define playlist_Skip(p,i) playlist_Control(p,PLAYLIST_SKIP, pl_Unlocked, i) + +/** + * Do a playlist action. + * If there is something in the playlist then you can do playlist actions. + * Possible queries are listed in vlc_common.h + * \param p_playlist the playlist to do the command on + * \param i_query the command to do + * \param b_locked TRUE if playlist is locked when entering this function + * \param variable number of arguments + * \return VLC_SUCCESS or an error + */ +VLC_EXPORT( int, playlist_Control, ( playlist_t *p_playlist, int i_query, bool b_locked, ... ) ); + +/** Get current playing input. The object is retained. + */ +VLC_EXPORT( input_thread_t *, playlist_CurrentInput, ( playlist_t *p_playlist ) ); + +/** Clear the playlist + * \param b_locked TRUE if playlist is locked when entering this function + */ +VLC_EXPORT( void, playlist_Clear, ( playlist_t *, bool ) ); + +/** Enqueue an input item for preparsing */ +VLC_EXPORT( int, playlist_PreparseEnqueue, (playlist_t *, input_item_t *) ); + +/** Enqueue a playlist item and all of its children if any for preparsing */ +VLC_EXPORT( int, playlist_PreparseEnqueueItem, (playlist_t *, playlist_item_t *) ); +/** Request the art for an input item to be fetched */ +VLC_EXPORT( int, playlist_AskForArtEnqueue, (playlist_t *, input_item_t *) ); + +/********************** Services discovery ***********************/ + +/** Add a list of comma-separated service discovery modules */ +VLC_EXPORT( int, playlist_ServicesDiscoveryAdd, (playlist_t *, const char *)); +/** Remove a services discovery module by name */ +VLC_EXPORT( int, playlist_ServicesDiscoveryRemove, (playlist_t *, const char *)); +/** Check whether a given SD is loaded */ +VLC_EXPORT( bool, playlist_IsServicesDiscoveryLoaded, ( playlist_t *,const char *)); + +/* Playlist sorting */ +VLC_EXPORT( int, playlist_TreeMove, ( playlist_t *, playlist_item_t *, playlist_item_t *, int ) ); +VLC_EXPORT( int, playlist_RecursiveNodeSort, ( playlist_t *, playlist_item_t *,int, int ) ); + +/** + * Export a node of the playlist to a certain type of playlistfile + * \param p_playlist the playlist to export + * \param psz_filename the location where the exported file will be saved + * \param p_export_root the root node to export + * \param psz_type the type of playlist file to create (m3u, pls, ..) + * \return VLC_SUCCESS on success + */ +VLC_EXPORT( int, playlist_Export, ( playlist_t *p_playlist, const char *psz_name, playlist_item_t *p_export_root, const char *psz_type ) ); + +/******************************************************** + * Item management + ********************************************************/ + +/*************************** Item creation **************************/ + +VLC_EXPORT( playlist_item_t* , playlist_ItemNewWithType, ( playlist_t *,const char *,const char *, int , const char *const *, int, int) ); + +/** Create a new item, without adding it to the playlist + * \param p_obj a vlc object (anyone will do) + * \param psz_uri the mrl of the item + * \param psz_name a text giving a name or description of the item + * \return the new item or NULL on failure + */ +#define playlist_ItemNew( a , b, c ) \ + playlist_ItemNewWithType( VLC_OBJECT(a) , b , c, 0, NULL, -1, 0 ) + + +/*************************** Item deletion **************************/ +VLC_EXPORT( int, playlist_DeleteFromInput, ( playlist_t *, int, bool ) ); + +/*************************** Item fields accessors **************************/ +VLC_EXPORT( int, playlist_ItemSetName, (playlist_item_t *, const char * ) ); + +/******************** Item addition ********************/ +VLC_EXPORT( int, playlist_Add, ( playlist_t *, const char *, const char *, int, int, bool, bool ) ); +VLC_EXPORT( int, playlist_AddExt, ( playlist_t *, const char *, const char *, int, int, mtime_t, const char *const *,int, bool, bool ) ); +VLC_EXPORT( int, playlist_AddInput, ( playlist_t *, input_item_t *, int, int, bool, bool ) ); +VLC_EXPORT( int, playlist_BothAddInput, ( playlist_t *, input_item_t *,playlist_item_t *,int , int, int*, int*, bool ) ); + +/********************** Misc item operations **********************/ +VLC_EXPORT( playlist_item_t*, playlist_ItemToNode, (playlist_t *,playlist_item_t *, bool) ); + +/********************************** Item search *************************/ +VLC_EXPORT( playlist_item_t *, playlist_ItemGetById, (playlist_t *, int, bool ) ); +VLC_EXPORT( playlist_item_t *, playlist_ItemGetByInput, (playlist_t *,input_item_t *, bool ) ); +VLC_EXPORT( playlist_item_t *, playlist_ItemGetByInputId, (playlist_t *, int, playlist_item_t *) ); + +VLC_EXPORT( int, playlist_LiveSearchUpdate, (playlist_t *, playlist_item_t *, const char *) ); + +/******************************************************** + * Tree management + ********************************************************/ +VLC_EXPORT( int, playlist_NodeChildrenCount, (playlist_t *,playlist_item_t* ) ); + +/* Node management */ +VLC_EXPORT( playlist_item_t *, playlist_NodeCreate, ( playlist_t *, const char *, playlist_item_t * p_parent, int i_flags, input_item_t * ) ); +VLC_EXPORT( int, playlist_NodeAppend, (playlist_t *,playlist_item_t*,playlist_item_t *) ); +VLC_EXPORT( int, playlist_NodeInsert, (playlist_t *,playlist_item_t*,playlist_item_t *, int) ); +VLC_EXPORT( int, playlist_NodeRemoveItem, (playlist_t *,playlist_item_t*,playlist_item_t *) ); +VLC_EXPORT( playlist_item_t *, playlist_ChildSearchName, (playlist_item_t*, const char* ) ); +VLC_EXPORT( int, playlist_NodeDelete, ( playlist_t *, playlist_item_t *, bool , bool ) ); +VLC_EXPORT( int, playlist_NodeEmpty, ( playlist_t *, playlist_item_t *, bool ) ); +VLC_EXPORT( void, playlist_NodesPairCreate, (playlist_t *, const char *, playlist_item_t **, playlist_item_t **, bool ) ); +VLC_EXPORT( playlist_item_t *, playlist_GetPreferredNode, ( playlist_t *p_playlist, playlist_item_t *p_node ) ); +VLC_EXPORT( playlist_item_t *, playlist_GetNextLeaf, ( playlist_t *p_playlist, playlist_item_t *p_root, playlist_item_t *p_item, bool b_ena, bool b_unplayed ) ); +VLC_EXPORT( playlist_item_t *, playlist_GetPrevLeaf, ( playlist_t *p_playlist, playlist_item_t *p_root, playlist_item_t *p_item, bool b_ena, bool b_unplayed ) ); +VLC_EXPORT( playlist_item_t *, playlist_GetLastLeaf, ( playlist_t *p_playlist, playlist_item_t *p_root ) ); + +/*********************************************************************** + * Inline functions + ***********************************************************************/ +/** Open a playlist file, add its content to the current playlist */ +static inline int playlist_Import( playlist_t *p_playlist, const char *psz_file) +{ + char psz_uri[256+10]; + input_item_t *p_input; + snprintf( psz_uri, 256+9, "file/://%s", psz_file ); + const char *const psz_option = "meta-file"; + p_input = input_item_NewExt( p_playlist, psz_uri, psz_file, + 1, &psz_option, -1 ); + playlist_AddInput( p_playlist, p_input, PLAYLIST_APPEND, PLAYLIST_END, + true, false ); + input_Read( p_playlist, p_input, true ); + return VLC_SUCCESS; +} + +/** Small helper tp get current playing input or NULL. Release the input after use. */ +#define pl_CurrentInput(a) __pl_CurrentInput( VLC_OBJECT(a) ) +static inline input_thread_t * __pl_CurrentInput( vlc_object_t * p_this ) +{ + playlist_t * p_playlist = pl_Yield( p_this ); + if( !p_playlist ) return NULL; + input_thread_t * p_input = playlist_CurrentInput( p_playlist ); + pl_Release( p_this ); + return p_input; +} + +/** Tell if the playlist is currently running */ +#define playlist_IsPlaying( pl ) ( pl->status.i_status == PLAYLIST_RUNNING && \ + !(pl->request.b_request && pl->request.i_status == PLAYLIST_STOPPED) ) + +#define playlist_IsStopped( pl ) ( pl->status.i_status == PLAYLIST_STOPPED || \ + (pl->request.b_request && pl->request.i_status == PLAYLIST_STOPPED) ) + +/** Tell if the playlist is empty */ +#define playlist_IsEmpty( pl ) ( pl->items.i_size == 0 ) + +/** Tell the number of items in the current playing context */ +#define playlist_CurrentSize( pl ) pl->current.i_size + +/** Tell the current item id in current playing context */ +#define playlist_CurrentId( pl ) pl->status.p_item->i_id + +/** Ask the playlist to do some work */ +#define playlist_Signal( p_playlist ) vlc_object_signal( p_playlist ) + +/** @} */ +# ifdef __cplusplus +} +# endif + +#endif diff --git a/VLC/vlc_plugin.h b/VLC/vlc_plugin.h new file mode 100644 index 0000000..04149dd --- /dev/null +++ b/VLC/vlc_plugin.h @@ -0,0 +1,485 @@ +/***************************************************************************** + * vlc_plugin.h : Macros used from within a module. + ***************************************************************************** + * Copyright (C) 2001-2006 the VideoLAN team + * Copyright © 2007-2008 Rémi Denis-Courmont + * + * Authors: Samuel Hocevar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef LIBVLC_MODULES_MACROS_H +# define LIBVLC_MODULES_MACROS_H 1 + +/** + * \file + * This file implements plugin (module) macros used to define a vlc module. + */ + +/***************************************************************************** + * If we are not within a module, assume we're in the vlc core. + *****************************************************************************/ +#if !defined( __PLUGIN__ ) && !defined( __BUILTIN__ ) +# define MODULE_NAME main +#endif + +/** + * Current plugin ABI version + */ +# define MODULE_SYMBOL 0_9_0m +# define MODULE_SUFFIX "__0_9_0m" + +/***************************************************************************** + * Add a few defines. You do not want to read this section. Really. + *****************************************************************************/ + +/* Explanation: + * + * if linking a module statically, we will need: + * #define MODULE_FUNC( zog ) module_foo_zog + * + * this can't easily be done with the C preprocessor, thus a few ugly hacks. + */ + +/* I need to do _this_ to change « foo bar » to « module_foo_bar » ! */ +#define CONCATENATE( y, z ) CRUDE_HACK( y, z ) +#define CRUDE_HACK( y, z ) y##__##z + +/* If the module is built-in, then we need to define foo_InitModule instead + * of InitModule. Same for Activate- and DeactivateModule. */ +#ifdef __PLUGIN__ +# define E_( function ) CONCATENATE( function, MODULE_SYMBOL ) +# define __VLC_SYMBOL( symbol ) CONCATENATE( symbol, MODULE_SYMBOL ) +#else +# define E_( function ) CONCATENATE( function, MODULE_NAME ) +# define __VLC_SYMBOL( symbol ) CONCATENATE( symbol, MODULE_NAME ) +#endif + +#if defined( __PLUGIN__ ) && ( defined( WIN32 ) || defined( UNDER_CE ) ) +# define DLL_SYMBOL __declspec(dllexport) +# define CDECL_SYMBOL __cdecl +#else +# define DLL_SYMBOL +# define CDECL_SYMBOL +#endif + +#if defined( __cplusplus ) +# define EXTERN_SYMBOL extern "C" +#else +# define EXTERN_SYMBOL +#endif +#define MODULE_STRING "" +/* + * InitModule: this function is called once and only once, when the module + * is looked at for the first time. We get the useful data from it, for + * instance the module name, its shortcuts, its capabilities... we also create + * a copy of its config because the module can be unloaded at any time. + */ +#define vlc_module_begin( ) \ + EXTERN_SYMBOL DLL_SYMBOL int CDECL_SYMBOL \ + E_(vlc_entry) ( module_t *p_module ); \ + \ + EXTERN_SYMBOL DLL_SYMBOL int CDECL_SYMBOL \ + __VLC_SYMBOL(vlc_entry) ( module_t *p_module ) \ + { \ + module_config_t *p_config = NULL; \ + const char *domain = NULL; \ + if (vlc_module_set (p_module, VLC_MODULE_NAME, \ + (const char *)(MODULE_STRING))) \ + goto error; \ + { \ + module_t *p_submodule = p_module; + +#define vlc_module_end( ) \ + } \ + (void)p_config; \ + return VLC_SUCCESS; \ + \ + error: \ + return VLC_EGENERIC; \ + } \ + VLC_METADATA_EXPORTS + +#define add_submodule( ) \ + p_submodule = vlc_submodule_create( p_module ); + +#define add_requirement( cap ) \ + if (vlc_module_set (p_module, VLC_MODULE_CPU_REQUIREMENT, \ + (int)(CPU_CAPABILITY_##cap))) \ + goto error; + +#define add_shortcut( shortcut ) \ + if (vlc_module_set (p_submodule, VLC_MODULE_SHORTCUT, \ + (const char *)(shortcut))) \ + goto error; + +#define set_shortname( shortname ) \ + if (vlc_module_set (p_submodule, VLC_MODULE_SHORTNAME, domain, \ + (const char *)(shortname))) \ + goto error; + +#define set_description( desc ) \ + if (vlc_module_set (p_submodule, VLC_MODULE_DESCRIPTION, domain, \ + (const char *)(desc))) \ + goto error; + +#define set_help( help ) \ + if (vlc_module_set (p_submodule, VLC_MODULE_HELP, domain, \ + (const char *)(help))) \ + goto error; + +#define set_capability( cap, score ) \ + if (vlc_module_set (p_submodule, VLC_MODULE_CAPABILITY, \ + (const char *)(cap)) \ + || vlc_module_set (p_submodule, VLC_MODULE_SCORE, (int)(score))) \ + goto error; + +#define set_callbacks( activate, deactivate ) \ + if (vlc_module_set (p_submodule, VLC_MODULE_CB_OPEN, activate) \ + || vlc_module_set (p_submodule, VLC_MODULE_CB_CLOSE, deactivate)) \ + goto error; + +#define linked_with_a_crap_library_which_uses_atexit( ) \ + if (vlc_module_set (p_submodule, VLC_MODULE_NO_UNLOAD)) \ + goto error; + +#define set_text_domain( dom ) domain = (dom); + +VLC_EXPORT( module_t *, vlc_module_create, ( vlc_object_t * ) ); +VLC_EXPORT( module_t *, vlc_submodule_create, ( module_t * ) ); +VLC_EXPORT( int, vlc_module_set, (module_t *module, int propid, ...) ); +VLC_EXPORT( module_config_t *, vlc_config_create, (module_t *, int type) ); +VLC_EXPORT( int, vlc_config_set, (module_config_t *, int, ...) ); + +enum vlc_module_properties +{ + /* DO NOT EVER REMOVE, INSERT OR REPLACE ANY ITEM! It would break the ABI! + * Append new items at the end ONLY. */ + VLC_MODULE_CPU_REQUIREMENT, + VLC_MODULE_SHORTCUT, + VLC_MODULE_SHORTNAME_NODOMAIN, + VLC_MODULE_DESCRIPTION_NODOMAIN, + VLC_MODULE_HELP_NODOMAIN, + VLC_MODULE_CAPABILITY, + VLC_MODULE_SCORE, + VLC_MODULE_PROGRAM, /* obsoleted */ + VLC_MODULE_CB_OPEN, + VLC_MODULE_CB_CLOSE, + VLC_MODULE_NO_UNLOAD, + VLC_MODULE_NAME, + VLC_MODULE_SHORTNAME, + VLC_MODULE_DESCRIPTION, + VLC_MODULE_HELP, +}; + +enum vlc_config_properties +{ + /* DO NOT EVER REMOVE, INSERT OR REPLACE ANY ITEM! It would break the ABI! + * Append new items at the end ONLY. */ + + VLC_CONFIG_NAME, + /* command line name (args=const char *, vlc_callback_t) */ + + VLC_CONFIG_DESC_NODOMAIN, + /* description (args=const char *, const char *) */ + + VLC_CONFIG_VALUE, + /* actual value (args=int/double/const char *) */ + + VLC_CONFIG_RANGE, + /* minimum value (args=int/double/const char * twice) */ + + VLC_CONFIG_ADVANCED, + /* enable advanced flag (args=none) */ + + VLC_CONFIG_VOLATILE, + /* don't write variable to storage (args=none) */ + + VLC_CONFIG_PERSISTENT, + /* always write variable to storage (args=none) */ + + VLC_CONFIG_RESTART, + /* restart required to apply value change (args=none) */ + + VLC_CONFIG_PRIVATE, + /* hide from user (args=none) */ + + VLC_CONFIG_REMOVED, + /* tag as no longer supported (args=none) */ + + VLC_CONFIG_CAPABILITY, + /* capability for a module or list thereof (args=const char*) */ + + VLC_CONFIG_SHORTCUT, + /* one-character (short) command line option name (args=char) */ + + VLC_CONFIG_LIST_NODOMAIN, + /* possible values list + * (args=size_t, const *, const char *const *) */ + + VLC_CONFIG_ADD_ACTION_NODOMAIN, + /* add value change callback (args=vlc_callback_t, const char *) */ + + VLC_CONFIG_OLDNAME, + /* former option name (args=const char *) */ + + VLC_CONFIG_SAFE, + /* tag as modifiable by untrusted input item "sources" (args=none) */ + + VLC_CONFIG_DESC, + /* description (args=const char *, const char *, const char *) */ + + VLC_CONFIG_LIST, + /* possible values list + * (args=const char *, size_t, const *, const char *const *) */ + + VLC_CONFIG_ADD_ACTION, + /* add value change callback + * (args=const char *, vlc_callback_t, const char *) */ +}; + +/***************************************************************************** + * Macros used to build the configuration structure. + * + * Note that internally we support only 3 types of config data: int, float + * and string. + * The other types declared here just map to one of these 3 basic types but + * have the advantage of also providing very good hints to a configuration + * interface so as to make it more user friendly. + * The configuration structure also includes category hints. These hints can + * provide a configuration interface with some very useful data and again + * allow for a more user friendly interface. + *****************************************************************************/ + +#define add_type_inner( type ) \ + p_config = vlc_config_create (p_module, type); + +#define add_typedesc_inner( type, text, longtext ) \ + add_type_inner( type ) \ + vlc_config_set (p_config, VLC_CONFIG_DESC, domain, \ + (const char *)(text), (const char *)(longtext)); + +#define add_typeadv_inner( type, text, longtext, advc ) \ + add_typedesc_inner( type, text, longtext ) \ + if (advc) vlc_config_set (p_config, VLC_CONFIG_ADVANCED); + +#define add_typename_inner( type, name, text, longtext, advc, cb ) \ + add_typeadv_inner( type, text, longtext, advc ) \ + vlc_config_set (p_config, VLC_CONFIG_NAME, \ + (const char *)(name), (vlc_callback_t)(cb)); + +#define add_string_inner( type, name, text, longtext, advc, cb, v ) \ + add_typename_inner( type, name, text, longtext, advc, cb ) \ + vlc_config_set (p_config, VLC_CONFIG_VALUE, (const char *)(v)); + +#define add_int_inner( type, name, text, longtext, advc, cb, v ) \ + add_typename_inner( type, name, text, longtext, advc, cb ) \ + vlc_config_set (p_config, VLC_CONFIG_VALUE, (int)(v)); + + +#define set_category( i_id ) \ + add_type_inner( CONFIG_CATEGORY ) \ + vlc_config_set (p_config, VLC_CONFIG_VALUE, (int)(i_id)); + +#define set_subcategory( i_id ) \ + add_type_inner( CONFIG_SUBCATEGORY ) \ + vlc_config_set (p_config, VLC_CONFIG_VALUE, (int)(i_id)); + +#define set_section( text, longtext ) \ + add_typedesc_inner( CONFIG_SECTION, text, longtext ) + +#define add_category_hint( text, longtext, advc ) \ + add_typeadv_inner( CONFIG_HINT_CATEGORY, text, longtext, advc ) + +#define add_subcategory_hint( text, longtext ) \ + add_typedesc_inner( CONFIG_HINT_SUBCATEGORY, text, longtext ) + +#define end_subcategory_hint \ + add_type_inner( CONFIG_HINT_SUBCATEGORY_END ) + +#define add_usage_hint( text ) \ + add_typedesc_inner( CONFIG_HINT_USAGE, text, NULL ) + +#define add_string( name, value, p_callback, text, longtext, advc ) \ + add_string_inner( CONFIG_ITEM_STRING, name, text, longtext, advc, \ + p_callback, value ) + +#define add_password( name, value, p_callback, text, longtext, advc ) \ + add_string_inner( CONFIG_ITEM_PASSWORD, name, text, longtext, advc, \ + p_callback, value ) + +#define add_file( name, value, p_callback, text, longtext, advc ) \ + add_string_inner( CONFIG_ITEM_FILE, name, text, longtext, advc, \ + p_callback, value ) + +#define add_directory( name, value, p_callback, text, longtext, advc ) \ + add_string_inner( CONFIG_ITEM_DIRECTORY, name, text, longtext, advc, \ + p_callback, value ) + +#define add_module( name, psz_caps, value, p_callback, text, longtext, advc ) \ + add_string_inner( CONFIG_ITEM_MODULE, name, text, longtext, advc, \ + p_callback, value ) \ + vlc_config_set (p_config, VLC_CONFIG_CAPABILITY, (const char *)(psz_caps)); + +#define add_module_list( name, psz_caps, value, p_callback, text, longtext, advc ) \ + add_string_inner( CONFIG_ITEM_MODULE_LIST, name, text, longtext, advc, \ + p_callback, value ) \ + vlc_config_set (p_config, VLC_CONFIG_CAPABILITY, (const char *)(psz_caps)); + +#ifndef __PLUGIN__ +#define add_module_cat( name, i_subcategory, value, p_callback, text, longtext, advc ) \ + add_string_inner( CONFIG_ITEM_MODULE_CAT, name, text, longtext, advc, \ + p_callback, value ) \ + p_config->min.i = i_subcategory /* gruik */; + +#define add_module_list_cat( name, i_subcategory, value, p_callback, text, longtext, advc ) \ + add_string_inner( CONFIG_ITEM_MODULE_LIST_CAT, name, text, longtext, \ + advc, p_callback, value ) \ + p_config->min.i = i_subcategory /* gruik */; +#endif + +#define add_integer( name, value, p_callback, text, longtext, advc ) \ + add_int_inner( CONFIG_ITEM_INTEGER, name, text, longtext, advc, \ + p_callback, value ) + +#define add_key( name, value, p_callback, text, longtext, advc ) \ + add_int_inner( CONFIG_ITEM_KEY, name, text, longtext, advc, p_callback, \ + value ) + +#define add_integer_with_range( name, value, i_min, i_max, p_callback, text, longtext, advc ) \ + add_integer( name, value, p_callback, text, longtext, advc ) \ + change_integer_range( i_min, i_max ) + +#define add_float( name, v, p_callback, text, longtext, advc ) \ + add_typename_inner( CONFIG_ITEM_FLOAT, name, text, longtext, advc, p_callback ) \ + vlc_config_set (p_config, VLC_CONFIG_VALUE, (double)(v)); + +#define add_float_with_range( name, value, f_min, f_max, p_callback, text, longtext, advc ) \ + add_float( name, value, p_callback, text, longtext, advc ) \ + change_float_range( f_min, f_max ) + +#define add_bool( name, v, p_callback, text, longtext, advc ) \ + add_typename_inner( CONFIG_ITEM_BOOL, name, text, longtext, advc, \ + p_callback ) \ + if (v) vlc_config_set (p_config, VLC_CONFIG_VALUE, (int)true); + +/* For removed option */ +#define add_obsolete_inner( name, type ) \ + add_type_inner( type ) \ + vlc_config_set (p_config, VLC_CONFIG_NAME, \ + (const char *)(name), (vlc_callback_t)NULL); \ + vlc_config_set (p_config, VLC_CONFIG_REMOVED); + +#define add_obsolete_bool( name ) \ + add_obsolete_inner( name, CONFIG_ITEM_BOOL ) + +#define add_obsolete_integer( name ) \ + add_obsolete_inner( name, CONFIG_ITEM_INTEGER ) + +#define add_obsolete_float( name ) \ + add_obsolete_inner( name, CONFIG_ITEM_FLOAT ) + +#define add_obsolete_string( name ) \ + add_obsolete_inner( name, CONFIG_ITEM_STRING ) + +/* Modifier macros for the config options (used for fine tuning) */ + +#define add_deprecated_alias( name ) \ + vlc_config_set (p_config, VLC_CONFIG_OLDNAME, (const char *)(name)) + +#define change_short( ch ) \ + vlc_config_set (p_config, VLC_CONFIG_SHORTCUT, (int)(ch)); + +#define change_string_list( list, list_text, list_update_func ) \ + vlc_config_set (p_config, VLC_CONFIG_LIST, domain, \ + (size_t)(sizeof (list) / sizeof (char *)), \ + (const char *const *)(list), \ + (const char *const *)(list_text), \ + (vlc_callback_t)(list_update_func)); + +#define change_integer_list( list, list_text, list_update_func ) \ + vlc_config_set (p_config, VLC_CONFIG_LIST, domain, \ + (size_t)(sizeof (list) / sizeof (int)), \ + (const int *)(list), \ + (const char *const *)(list_text), \ + (vlc_callback_t)(list_update_func)); + +#define change_float_list( list, list_text, list_update_func ) \ + vlc_config_set (p_config, VLC_CONFIG_LIST, domain, \ + (size_t)(sizeof (list) / sizeof (float)), \ + (const float *)(list), \ + (const char *const *)(list_text), \ + (vlc_callback_t)(list_update_func)); + +#define change_integer_range( minv, maxv ) \ + vlc_config_set (p_config, VLC_CONFIG_RANGE, (int)(minv), (int)(maxv)); + +#define change_float_range( minv, maxv ) \ + vlc_config_set (p_config, VLC_CONFIG_RANGE, \ + (double)(minv), (double)(maxv)); + +#define change_action_add( pf_action, text ) \ + vlc_config_set (p_config, VLC_CONFIG_ADD_ACTION, domain, \ + (vlc_callback_t)(pf_action), (const char *)(text)); + +#define change_internal() \ + vlc_config_set (p_config, VLC_CONFIG_PRIVATE); + +#define change_need_restart() \ + vlc_config_set (p_config, VLC_CONFIG_RESTART); + +#define change_autosave() \ + vlc_config_set (p_config, VLC_CONFIG_PERSISTENT); + +#define change_unsaveable() \ + vlc_config_set (p_config, VLC_CONFIG_VOLATILE); + +#define change_unsafe() (void)0; /* no-op */ + +#define change_safe() \ + vlc_config_set (p_config, VLC_CONFIG_SAFE); + +/* Meta data plugin exports */ +#define VLC_META_EXPORT( name, value ) \ + EXTERN_SYMBOL DLL_SYMBOL const char * CDECL_SYMBOL \ + E_(vlc_entry_ ## name) (void); \ + EXTERN_SYMBOL DLL_SYMBOL const char * CDECL_SYMBOL \ + __VLC_SYMBOL(vlc_entry_ ## name) (void) \ + { \ + return value; \ + } + +#if defined (__LIBVLC__) +# define VLC_COPYRIGHT_EXPORT VLC_META_EXPORT (copyright, \ + "\x43\x6f\x70\x79\x72\x69\x67\x68\x74\x20\x28\x43\x29\x20\x74\x68" \ + "\x65\x20\x56\x69\x64\x65\x6f\x4c\x41\x4e\x20\x56\x4c\x43\x20\x6d" \ + "\x65\x64\x69\x61\x20\x70\x6c\x61\x79\x65\x72\x20\x64\x65\x76\x65" \ + "\x6c\x6f\x70\x70\x65\x72\x73" ) +#elif !defined (VLC_COPYRIGHT_EXPORT) +# define VLC_COPYRIGHT_EXPORT +#endif +#define VLC_LICENSE_EXPORT VLC_META_EXPORT (license, \ + "\x4c\x69\x63\x65\x6e\x73\x65\x64\x20\x75\x6e\x64\x65\x72\x20\x74" \ + "\x68\x65\x20\x74\x65\x72\x6d\x73\x20\x6f\x66\x20\x74\x68\x65\x20" \ + "\x47\x4e\x55\x20\x47\x65\x6e\x65\x72\x61\x6c\x20\x50\x75\x62\x6c" \ + "\x69\x63\x20\x4c\x69\x63\x65\x6e\x73\x65\x2c\x20\x76\x65\x72\x73" \ + "\x69\x6f\x6e\x20\x32\x20\x6f\x72\x20\x6c\x61\x74\x65\x72\x2e" ) + +#define VLC_METADATA_EXPORTS \ + VLC_COPYRIGHT_EXPORT \ + VLC_LICENSE_EXPORT + +#endif diff --git a/VLC/vlc_rand.h b/VLC/vlc_rand.h new file mode 100644 index 0000000..56e07af --- /dev/null +++ b/VLC/vlc_rand.h @@ -0,0 +1,32 @@ +/***************************************************************************** + * vlc_rand.h: RNG + ***************************************************************************** + * Copyright © 2007 Rémi Denis-Courmont + * $Id$ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_RAND_H +# define VLC_RAND_H + +/** + * \file + * This file defined random number generator function in vlc + */ + +VLC_EXPORT( void, vlc_rand_bytes, (void *buf, size_t len) ); + +#endif diff --git a/VLC/vlc_services_discovery.h b/VLC/vlc_services_discovery.h new file mode 100644 index 0000000..9479de8 --- /dev/null +++ b/VLC/vlc_services_discovery.h @@ -0,0 +1,91 @@ +/***************************************************************************** + * vlc_services_discovery.h : Services Discover functions + ***************************************************************************** + * Copyright (C) 1999-2004 the VideoLAN team + * $Id$ + * + * Authors: Pierre d'Herbemont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_SERVICES_DISCOVERY_H_ +#define VLC_SERVICES_DISCOVERY_H_ + +/** + * \file + * This file functions and structures for service discovery in vlc + */ + +# ifdef __cplusplus +extern "C" { +# endif + +/* + * @{ + */ + +#include "vlc_input.h" +#include "vlc_events.h" + +struct services_discovery_t +{ + VLC_COMMON_MEMBERS + char * psz_module; + module_t * p_module; + + char * psz_localized_name; /* Accessed through Setters for non class function */ + vlc_event_manager_t event_manager; /* Accessed through Setters for non class function */ + + services_discovery_sys_t *p_sys; + void (*pf_run) ( services_discovery_t *); +}; + + +/*********************************************************************** + * Service Discovery + ***********************************************************************/ + +/* Get the services discovery modules names to use in Create(), in a null + * terminated string array. Array and string must be freed after use. */ +VLC_EXPORT( char **, __services_discovery_GetServicesNames, ( vlc_object_t * p_super, char ***pppsz_longnames ) ); +#define services_discovery_GetServicesNames(a,b) \ + __services_discovery_GetServicesNames(VLC_OBJECT(a),b) + +/* Creation of a service_discovery object */ +VLC_EXPORT( services_discovery_t *, services_discovery_Create, ( vlc_object_t * p_super, const char * psz_service_name ) ); +VLC_EXPORT( void, services_discovery_Destroy, ( services_discovery_t * p_this ) ); +VLC_EXPORT( int, services_discovery_Start, ( services_discovery_t * p_this ) ); +VLC_EXPORT( void, services_discovery_Stop, ( services_discovery_t * p_this ) ); + +/* Read info from discovery object */ +VLC_EXPORT( char *, services_discovery_GetLocalizedName, ( services_discovery_t * p_this ) ); + +/* Receive event notification (preferred way to get new items) */ +VLC_EXPORT( vlc_event_manager_t *, services_discovery_EventManager, ( services_discovery_t * p_this ) ); + +/* Used by services_discovery to post update about their items */ +VLC_EXPORT( void, services_discovery_SetLocalizedName, ( services_discovery_t * p_this, const char * ) ); + /* About the psz_category, it is a legacy way to add info to the item, + * for more options, directly set the (meta) data on the input item */ +VLC_EXPORT( void, services_discovery_AddItem, ( services_discovery_t * p_this, input_item_t * p_item, const char * psz_category ) ); +VLC_EXPORT( void, services_discovery_RemoveItem, ( services_discovery_t * p_this, input_item_t * p_item ) ); + +/** @} */ +# ifdef __cplusplus +} +# endif + +#endif diff --git a/VLC/vlc_sout.h b/VLC/vlc_sout.h new file mode 100644 index 0000000..4aab35b --- /dev/null +++ b/VLC/vlc_sout.h @@ -0,0 +1,246 @@ +/***************************************************************************** + * stream_output.h : stream output module + ***************************************************************************** + * Copyright (C) 2002-2007 the VideoLAN team + * $Id$ + * + * Authors: Christophe Massiot + * Laurent Aimar + * Eric Petit + * Jean-Paul Saman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_SOUT_H_ +#define VLC_SOUT_H_ + +/** + * \file + * This file defines structures and functions for stream ouput in vlc + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "vlc_es.h" + +/** Stream output instance */ +struct sout_instance_t +{ + VLC_COMMON_MEMBERS + + char *psz_sout; + char *psz_chain; + + /* meta data (Read only) XXX it won't be set before the first packet received */ + vlc_meta_t *p_meta; + + /** count of output that can't control the space */ + int i_out_pace_nocontrol; + + vlc_mutex_t lock; + sout_stream_t *p_stream; + + /** Private */ + sout_instance_sys_t *p_sys; +}; + +/** Stream output statistics */ +typedef enum +{ + SOUT_STATISTIC_DECODED_VIDEO, + SOUT_STATISTIC_DECODED_AUDIO, + SOUT_STATISTIC_DECODED_SUBTITLE, + + /* Use them only if you do not goes through a access_out module */ + SOUT_STATISTIC_SENT_PACKET, + SOUT_STATISTIC_SENT_BYTE, + +} sout_statistic_t; + +VLC_EXPORT( void, sout_UpdateStatistic, ( sout_instance_t *p_sout, sout_statistic_t, int ) ); + +/**************************************************************************** + * sout_stream_id_t: opaque (private for all sout_stream_t) + ****************************************************************************/ +typedef struct sout_stream_id_t sout_stream_id_t; + +/** Stream output access_output */ +struct sout_access_out_t +{ + VLC_COMMON_MEMBERS + + module_t *p_module; + char *psz_access; + + int i_writes; + /** Local counter reset each time it is transferred to stats */ + int64_t i_sent_bytes; + + char *psz_path; + sout_access_out_sys_t *p_sys; + int (*pf_seek)( sout_access_out_t *, off_t ); + ssize_t (*pf_read)( sout_access_out_t *, block_t * ); + ssize_t (*pf_write)( sout_access_out_t *, block_t * ); + int (*pf_control)( sout_access_out_t *, int, va_list); + + config_chain_t *p_cfg; + sout_instance_t *p_sout; +}; + +VLC_EXPORT( sout_access_out_t *,sout_AccessOutNew, ( sout_instance_t *, const char *psz_access, const char *psz_name ) ); +VLC_EXPORT( void, sout_AccessOutDelete, ( sout_access_out_t * ) ); +VLC_EXPORT( int, sout_AccessOutSeek, ( sout_access_out_t *, off_t ) ); +VLC_EXPORT( ssize_t, sout_AccessOutRead, ( sout_access_out_t *, block_t * ) ); +VLC_EXPORT( ssize_t, sout_AccessOutWrite, ( sout_access_out_t *, block_t * ) ); +VLC_EXPORT( int, sout_AccessOutControl,( sout_access_out_t *, int, va_list ) ); + +/** Muxer structure */ +struct sout_mux_t +{ + VLC_COMMON_MEMBERS + module_t *p_module; + + sout_instance_t *p_sout; + + char *psz_mux; + config_chain_t *p_cfg; + + sout_access_out_t *p_access; + + int (*pf_addstream)( sout_mux_t *, sout_input_t * ); + int (*pf_delstream)( sout_mux_t *, sout_input_t * ); + int (*pf_mux) ( sout_mux_t * ); + int (*pf_control) ( sout_mux_t *, int, va_list ); + + /* here are all inputs accepted by muxer */ + int i_nb_inputs; + sout_input_t **pp_inputs; + + /* mux private */ + sout_mux_sys_t *p_sys; + + /* XXX private to stream_output.c */ + /* if muxer doesn't support adding stream at any time then we first wait + * for stream then we refuse all stream and start muxing */ + bool b_add_stream_any_time; + bool b_waiting_stream; + /* we wait one second after first stream added */ + mtime_t i_add_stream_start; +}; + +enum sout_mux_query_e +{ + /* capabilities */ + MUX_CAN_ADD_STREAM_WHILE_MUXING, /* arg1= bool *, res=cannot fail */ + /* properties */ + MUX_GET_ADD_STREAM_WAIT, /* arg1= bool *, res=cannot fail */ + MUX_GET_MIME, /* arg1= char ** res=can fail */ +}; + +struct sout_input_t +{ + sout_instance_t *p_sout; + + es_format_t *p_fmt; + block_fifo_t *p_fifo; + + void *p_sys; +}; + + +VLC_EXPORT( sout_mux_t *, sout_MuxNew, ( sout_instance_t*, char *, sout_access_out_t * ) ); +VLC_EXPORT( sout_input_t *, sout_MuxAddStream, ( sout_mux_t *, es_format_t * ) ); +VLC_EXPORT( void, sout_MuxDeleteStream, ( sout_mux_t *, sout_input_t * ) ); +VLC_EXPORT( void, sout_MuxDelete, ( sout_mux_t * ) ); +VLC_EXPORT( void, sout_MuxSendBuffer, ( sout_mux_t *, sout_input_t *, block_t * ) ); + +static inline int sout_MuxControl( sout_mux_t *p_mux, int i_query, ... ) +{ + va_list args; + int i_result; + + va_start( args, i_query ); + i_result = p_mux->pf_control( p_mux, i_query, args ); + va_end( args ); + return i_result; +} + +/**************************************************************************** + * sout_stream: + ****************************************************************************/ +struct sout_stream_t +{ + VLC_COMMON_MEMBERS + + module_t *p_module; + sout_instance_t *p_sout; + + char *psz_name; + config_chain_t *p_cfg; + char *psz_next; + + /* Subpicture unit */ + spu_t *p_spu; + + /* add, remove a stream */ + sout_stream_id_t *(*pf_add)( sout_stream_t *, es_format_t * ); + int (*pf_del)( sout_stream_t *, sout_stream_id_t * ); + /* manage a packet */ + int (*pf_send)( sout_stream_t *, sout_stream_id_t *, block_t* ); + + /* private */ + sout_stream_sys_t *p_sys; +}; + +VLC_EXPORT( sout_stream_t *, sout_StreamNew, ( sout_instance_t *, char *psz_chain ) ); +VLC_EXPORT( void, sout_StreamDelete, ( sout_stream_t * ) ); + +static inline sout_stream_id_t *sout_StreamIdAdd( sout_stream_t *s, es_format_t *fmt ) +{ + return s->pf_add( s, fmt ); +} +static inline int sout_StreamIdDel( sout_stream_t *s, sout_stream_id_t *id ) +{ + return s->pf_del( s, id ); +} +static inline int sout_StreamIdSend( sout_stream_t *s, sout_stream_id_t *id, block_t *b ) +{ + return s->pf_send( s, id, b ); +} + +/**************************************************************************** + * Announce handler + ****************************************************************************/ +VLC_EXPORT(session_descriptor_t*,sout_AnnounceRegisterSDP, ( sout_instance_t *, const char *, const char *, announce_method_t* ) ); +VLC_EXPORT( int, sout_AnnounceUnRegister, (sout_instance_t *,session_descriptor_t* ) ); + +VLC_EXPORT(announce_method_t*, sout_SAPMethod, (void) ); +VLC_EXPORT(void, sout_MethodRelease, (announce_method_t *) ); + +/** SDP */ + +VLC_EXPORT( char *, vlc_sdp_Start, ( vlc_object_t *obj, const char *cfgpref, const struct sockaddr *src, size_t srclen, const struct sockaddr *addr, size_t addrlen ) ); +VLC_EXPORT( char *, sdp_AddMedia, (char **sdp, const char *type, const char *protocol, int dport, unsigned pt, bool bw_indep, unsigned bw, const char *ptname, unsigned clockrate, unsigned channels, const char *fmtp) ); +VLC_EXPORT( char *, sdp_AddAttribute, (char **sdp, const char *name, const char *fmt, ...) LIBVLC_FORMAT( 3, 4 ) ); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/VLC/vlc_stream.h b/VLC/vlc_stream.h new file mode 100644 index 0000000..fcd7219 --- /dev/null +++ b/VLC/vlc_stream.h @@ -0,0 +1,152 @@ +/***************************************************************************** + * vlc_stream.h: Stream (between access and demux) descriptor and methods + ***************************************************************************** + * Copyright (C) 1999-2004 the VideoLAN team + * $Id$ + * + * Authors: Laurent Aimar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_STREAM_H +#define VLC_STREAM_H 1 + +#include "vlc_block.h" + +/** + * \file + * This file defines structures and functions for stream (between access and demux) descriptor in vlc + */ + +# ifdef __cplusplus +extern "C" { +# endif + +/** + * \defgroup stream Stream + * + * This will allow you to easily handle read/seek in demuxer modules. + * @{ + */ + +/** + * Possible commands to send to stream_Control() and stream_vaControl() + */ +enum stream_query_e +{ + /* capabilities */ + STREAM_CAN_SEEK, /**< arg1= bool * res=cannot fail*/ + STREAM_CAN_FASTSEEK, /**< arg1= bool * res=cannot fail*/ + + /* */ + STREAM_SET_POSITION, /**< arg1= int64_t res=can fail */ + STREAM_GET_POSITION, /**< arg1= int64_t * res=cannot fail*/ + + STREAM_GET_SIZE, /**< arg1= int64_t * res=cannot fail (0 if no sense)*/ + + STREAM_GET_MTU, /**< arg1= int * res=cannot fail (0 if no sense)*/ + + /* Special for direct access control from demuxer. + * XXX: avoid using it by all means */ + STREAM_CONTROL_ACCESS, /* arg1= int i_access_query, args res: can fail + if access unreachable or access control answer */ + + STREAM_GET_CONTENT_TYPE, /**< arg1= char ** res=can file */ +}; + +VLC_EXPORT( int, stream_Read, ( stream_t *s, void *p_read, int i_read ) ); +VLC_EXPORT( int, stream_Peek, ( stream_t *s, const uint8_t **pp_peek, int i_peek ) ); +VLC_EXPORT( int, stream_vaControl, ( stream_t *s, int i_query, va_list args ) ); +VLC_EXPORT( void, stream_Delete, ( stream_t *s ) ); +VLC_EXPORT( int, stream_Control, ( stream_t *s, int i_query, ... ) ); +VLC_EXPORT( block_t *, stream_Block, ( stream_t *s, int i_size ) ); +VLC_EXPORT( char *, stream_ReadLine, ( stream_t * ) ); + +/** + * Get the current position in a stream + */ +static inline int64_t stream_Tell( stream_t *s ) +{ + int64_t i_pos; + stream_Control( s, STREAM_GET_POSITION, &i_pos ); + return i_pos; +} + +/** + * Get the size of the stream. + */ +static inline int64_t stream_Size( stream_t *s ) +{ + int64_t i_pos; + stream_Control( s, STREAM_GET_SIZE, &i_pos ); + return i_pos; +} + +static inline int stream_MTU( stream_t *s ) +{ + int i_mtu; + stream_Control( s, STREAM_GET_MTU, &i_mtu ); + return i_mtu; +} + +static inline int stream_Seek( stream_t *s, int64_t i_pos ) +{ + return stream_Control( s, STREAM_SET_POSITION, i_pos ); +} + +/** + * Get the Content-Type of a stream, or NULL if unknown. + * Result must be free()'d. + */ +static inline char *stream_ContentType( stream_t *s ) +{ + char *res; + if( stream_Control( s, STREAM_GET_CONTENT_TYPE, &res ) ) + return NULL; + return res; +} + +/** + * Create a special stream and a demuxer, this allows chaining demuxers + */ +#define stream_DemuxNew( a, b, c ) __stream_DemuxNew( VLC_OBJECT(a), b, c) +VLC_EXPORT( stream_t *,__stream_DemuxNew, ( vlc_object_t *p_obj, const char *psz_demux, es_out_t *out ) ); +VLC_EXPORT( void, stream_DemuxSend, ( stream_t *s, block_t *p_block ) ); +VLC_EXPORT( void, stream_DemuxDelete,( stream_t *s ) ); + +#define stream_MemoryNew( a, b, c, d ) __stream_MemoryNew( VLC_OBJECT(a), b, c, d ) +VLC_EXPORT( stream_t *,__stream_MemoryNew, (vlc_object_t *p_obj, uint8_t *p_buffer, int64_t i_size, bool i_preserve_memory ) ); +#define stream_UrlNew( a, b ) __stream_UrlNew( VLC_OBJECT(a), b ) +VLC_EXPORT( stream_t *,__stream_UrlNew, (vlc_object_t *p_this, const char *psz_url ) ); + +/** + * @} + */ + +# ifdef __cplusplus +} +# endif + +# if defined (__PLUGIN__) || defined (__BUILTIN__) + /* FIXME UGLY HACK to keep VLC_OBJECT working */ + /* Maybe we should make VLC_OBJECT a simple cast noawadays... */ +struct stream_t +{ + VLC_COMMON_MEMBERS +}; +# endif + +#endif diff --git a/VLC/vlc_strings.h b/VLC/vlc_strings.h new file mode 100644 index 0000000..eab608e --- /dev/null +++ b/VLC/vlc_strings.h @@ -0,0 +1,60 @@ +/***************************************************************************** + * vlc_strings.h: String functions + ***************************************************************************** + * Copyright (C) 2006 the VideoLAN team + * $Id$ + * + * Authors: Antoine Cellerier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_STRINGS_H +#define VLC_STRINGS_H 1 + +/** + * \file + * This file defines functions and structures handling misc strings + */ + +/** + * \defgroup strings Strings + * @{ + */ + +VLC_EXPORT( void, resolve_xml_special_chars, ( char *psz_value ) ); +VLC_EXPORT( char *, convert_xml_special_chars, ( const char *psz_content ) ); + +VLC_EXPORT( char *, vlc_b64_encode_binary, ( const uint8_t *, size_t ) ); +VLC_EXPORT( char *, vlc_b64_encode, ( const char * ) ); + +VLC_EXPORT( size_t, vlc_b64_decode_binary_to_buffer, ( uint8_t *p_dst, size_t i_dst_max, const char *psz_src ) ); +VLC_EXPORT( size_t, vlc_b64_decode_binary, ( uint8_t **pp_dst, const char *psz_src ) ); +VLC_EXPORT( char *, vlc_b64_decode, ( const char *psz_src ) ); + +VLC_EXPORT( char *, str_format_time, ( const char * ) ); +#define str_format_meta( a, b ) __str_format_meta( VLC_OBJECT( a ), b ) +VLC_EXPORT( char *, __str_format_meta, ( vlc_object_t *, const char * ) ); +#define str_format( a, b ) __str_format( VLC_OBJECT( a ), b ) +VLC_EXPORT( char *, __str_format, ( vlc_object_t *, const char * ) ); + +VLC_EXPORT( void, filename_sanitize, ( char * ) ); +VLC_EXPORT( void, path_sanitize, ( char * ) ); + +/** + * @} + */ + +#endif diff --git a/VLC/vlc_threads.h b/VLC/vlc_threads.h new file mode 100644 index 0000000..2eac7f9 --- /dev/null +++ b/VLC/vlc_threads.h @@ -0,0 +1,600 @@ +/***************************************************************************** + * vlc_threads.h : threads implementation for the VideoLAN client + * This header provides portable declarations for mutexes & conditions + ***************************************************************************** + * Copyright (C) 1999, 2002 the VideoLAN team + * $Id$ + * + * Authors: Jean-Marc Dressler + * Samuel Hocevar + * Gildas Bazin + * Christophe Massiot + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_THREADS_H_ +#define VLC_THREADS_H_ + +/** + * \file + * This file defines structures and functions for handling threads in vlc + * + */ + +#if defined( UNDER_CE ) + /* WinCE API */ +#elif defined( WIN32 ) +# include /* Win32 API */ +# include + +#elif defined( SYS_BEOS ) /* BeOS */ +# include +# include +# include + +#else /* pthreads (like Linux & BSD) */ +# define LIBVLC_USE_PTHREAD 1 +# define _APPLE_C_SOURCE 1 /* Proper pthread semantics on OSX */ + +# include /* lldiv_t definition (only in C99) */ +# include /* _POSIX_SPIN_LOCKS */ +# include + /* Needed for pthread_cond_timedwait */ +# include +# include + +#endif + +/***************************************************************************** + * Constants + *****************************************************************************/ + +/* Thread priorities */ +#ifdef __APPLE__ +# define VLC_THREAD_PRIORITY_LOW 0 +# define VLC_THREAD_PRIORITY_INPUT 22 +# define VLC_THREAD_PRIORITY_AUDIO 22 +# define VLC_THREAD_PRIORITY_VIDEO 0 +# define VLC_THREAD_PRIORITY_OUTPUT 22 +# define VLC_THREAD_PRIORITY_HIGHEST 22 + +#elif defined(SYS_BEOS) +# define VLC_THREAD_PRIORITY_LOW 5 +# define VLC_THREAD_PRIORITY_INPUT 10 +# define VLC_THREAD_PRIORITY_AUDIO 10 +# define VLC_THREAD_PRIORITY_VIDEO 5 +# define VLC_THREAD_PRIORITY_OUTPUT 15 +# define VLC_THREAD_PRIORITY_HIGHEST 15 + +#elif defined(LIBVLC_USE_PTHREAD) +# define VLC_THREAD_PRIORITY_LOW 0 +# define VLC_THREAD_PRIORITY_INPUT 10 +# define VLC_THREAD_PRIORITY_AUDIO 5 +# define VLC_THREAD_PRIORITY_VIDEO 0 +# define VLC_THREAD_PRIORITY_OUTPUT 15 +# define VLC_THREAD_PRIORITY_HIGHEST 20 + +#elif defined(WIN32) || defined(UNDER_CE) +/* Define different priorities for WinNT/2K/XP and Win9x/Me */ +# define VLC_THREAD_PRIORITY_LOW 0 +# define VLC_THREAD_PRIORITY_INPUT \ + (IS_WINNT ? THREAD_PRIORITY_ABOVE_NORMAL : 0) +# define VLC_THREAD_PRIORITY_AUDIO \ + (IS_WINNT ? THREAD_PRIORITY_HIGHEST : 0) +# define VLC_THREAD_PRIORITY_VIDEO \ + (IS_WINNT ? 0 : THREAD_PRIORITY_BELOW_NORMAL ) +# define VLC_THREAD_PRIORITY_OUTPUT \ + (IS_WINNT ? THREAD_PRIORITY_ABOVE_NORMAL : 0) +# define VLC_THREAD_PRIORITY_HIGHEST \ + (IS_WINNT ? THREAD_PRIORITY_TIME_CRITICAL : 0) + +#else +# define VLC_THREAD_PRIORITY_LOW 0 +# define VLC_THREAD_PRIORITY_INPUT 0 +# define VLC_THREAD_PRIORITY_AUDIO 0 +# define VLC_THREAD_PRIORITY_VIDEO 0 +# define VLC_THREAD_PRIORITY_OUTPUT 0 +# define VLC_THREAD_PRIORITY_HIGHEST 0 + +#endif + +/***************************************************************************** + * Type definitions + *****************************************************************************/ + +#if defined (LIBVLC_USE_PTHREAD) +typedef pthread_t vlc_thread_t; +typedef pthread_mutex_t vlc_mutex_t; +typedef pthread_cond_t vlc_cond_t; +typedef pthread_key_t vlc_threadvar_t; + +#elif defined( WIN32 ) || defined( UNDER_CE ) +typedef HANDLE vlc_thread_t; +typedef HANDLE vlc_mutex_t; +typedef HANDLE vlc_cond_t; +typedef DWORD vlc_threadvar_t; + +#elif defined( SYS_BEOS ) +/* This is the BeOS implementation of the vlc threads, note that the mutex is + * not a real mutex and the cond_var is not like a pthread cond_var but it is + * enough for what we need */ + +typedef thread_id vlc_thread_t; + +typedef struct +{ + int32_t init; + sem_id lock; +} vlc_mutex_t; + +typedef struct +{ + int32_t init; + thread_id thread; +} vlc_cond_t; + +typedef struct +{ +} vlc_threadvar_t; + +#endif + +#if defined( WIN32 ) && !defined ETIMEDOUT +# define ETIMEDOUT 10060 /* This is the value in winsock.h. */ +#endif + +/***************************************************************************** + * Function definitions + *****************************************************************************/ +VLC_EXPORT( int, vlc_mutex_init, ( vlc_mutex_t * ) ); +VLC_EXPORT( int, vlc_mutex_init_recursive, ( vlc_mutex_t * ) ); +VLC_EXPORT( void, __vlc_mutex_destroy, ( const char *, int, vlc_mutex_t * ) ); +VLC_EXPORT( int, __vlc_cond_init, ( vlc_cond_t * ) ); +VLC_EXPORT( void, __vlc_cond_destroy, ( const char *, int, vlc_cond_t * ) ); +VLC_EXPORT( int, vlc_threadvar_create, (vlc_threadvar_t * , void (*) (void *) ) ); +VLC_EXPORT( void, vlc_threadvar_delete, (vlc_threadvar_t *) ); +VLC_EXPORT( int, __vlc_thread_create, ( vlc_object_t *, const char *, int, const char *, void * ( * ) ( vlc_object_t * ), int, bool ) ); +VLC_EXPORT( int, __vlc_thread_set_priority, ( vlc_object_t *, const char *, int, int ) ); +VLC_EXPORT( void, __vlc_thread_join, ( vlc_object_t *, const char *, int ) ); + +#define vlc_thread_ready vlc_object_signal + +/***************************************************************************** + * vlc_mutex_lock: lock a mutex + *****************************************************************************/ +#define vlc_mutex_lock( P_MUTEX ) \ + __vlc_mutex_lock( __FILE__, __LINE__, P_MUTEX ) + +VLC_EXPORT(void, vlc_pthread_fatal, (const char *action, int error, const char *file, unsigned line)); + +#if defined(LIBVLC_USE_PTHREAD) +# define VLC_THREAD_ASSERT( action ) \ + if (val) \ + vlc_pthread_fatal (action, val, psz_file, i_line) +#else +# define VLC_THREAD_ASSERT ((void)(val)) +#endif + +static inline void __vlc_mutex_lock( const char * psz_file, int i_line, + vlc_mutex_t * p_mutex ) +{ +#if defined(LIBVLC_USE_PTHREAD) +# define vlc_assert_locked( m ) \ + assert (pthread_mutex_lock (m) == EDEADLK) + int val = pthread_mutex_lock( p_mutex ); + VLC_THREAD_ASSERT ("locking mutex"); + +#elif defined( UNDER_CE ) + (void)psz_file; (void)i_line; + + EnterCriticalSection( &p_mutex->csection ); + +#elif defined( WIN32 ) + (void)psz_file; (void)i_line; + + WaitForSingleObject( *p_mutex, INFINITE ); + +#elif defined( SYS_BEOS ) + acquire_sem( p_mutex->lock ); + +#endif +} + +#ifndef vlc_assert_locked +# define vlc_assert_locked( m ) (void)m +#endif + +/***************************************************************************** + * vlc_mutex_unlock: unlock a mutex + *****************************************************************************/ +#define vlc_mutex_unlock( P_MUTEX ) \ + __vlc_mutex_unlock( __FILE__, __LINE__, P_MUTEX ) + +static inline void __vlc_mutex_unlock( const char * psz_file, int i_line, + vlc_mutex_t *p_mutex ) +{ +#if defined(LIBVLC_USE_PTHREAD) + int val = pthread_mutex_unlock( p_mutex ); + VLC_THREAD_ASSERT ("unlocking mutex"); + +#elif defined( UNDER_CE ) + (void)psz_file; (void)i_line; + + LeaveCriticalSection( &p_mutex->csection ); + +#elif defined( WIN32 ) + (void)psz_file; (void)i_line; + + ReleaseMutex( *p_mutex ); + +#elif defined( SYS_BEOS ) + release_sem( p_mutex->lock ); + +#endif +} + +/***************************************************************************** + * vlc_mutex_destroy: destroy a mutex + *****************************************************************************/ +#define vlc_mutex_destroy( P_MUTEX ) \ + __vlc_mutex_destroy( __FILE__, __LINE__, P_MUTEX ) + +/***************************************************************************** + * vlc_cond_init: initialize a condition + *****************************************************************************/ +#define vlc_cond_init( P_THIS, P_COND ) \ + __vlc_cond_init( P_COND ) + +/***************************************************************************** + * vlc_cond_signal: start a thread on condition completion + *****************************************************************************/ +#define vlc_cond_signal( P_COND ) \ + __vlc_cond_signal( __FILE__, __LINE__, P_COND ) + +static inline void __vlc_cond_signal( const char * psz_file, int i_line, + vlc_cond_t *p_condvar ) +{ +#if defined(LIBVLC_USE_PTHREAD) + int val = pthread_cond_signal( p_condvar ); + VLC_THREAD_ASSERT ("signaling condition variable"); + +#elif defined( UNDER_CE ) || defined( WIN32 ) + (void)psz_file; (void)i_line; + + /* Release one waiting thread if one is available. */ + /* For this trick to work properly, the vlc_cond_signal must be surrounded + * by a mutex. This will prevent another thread from stealing the signal */ + /* PulseEvent() only works if none of the waiting threads is suspended. + * This is particularily problematic under a debug session. + * as documented in http://support.microsoft.com/kb/q173260/ */ + PulseEvent( *p_condvar ); + +#elif defined( SYS_BEOS ) + while( p_condvar->thread != -1 ) + { + thread_info info; + if( get_thread_info(p_condvar->thread, &info) == B_BAD_VALUE ) + return; + + if( info.state != B_THREAD_SUSPENDED ) + { + /* The waiting thread is not suspended so it could + * have been interrupted beetwen the unlock and the + * suspend_thread line. That is why we sleep a little + * before retesting p_condver->thread. */ + snooze( 10000 ); + } + else + { + /* Ok, we have to wake up that thread */ + resume_thread( p_condvar->thread ); + } + } + +#endif +} + +/***************************************************************************** + * vlc_cond_wait: wait until condition completion + *****************************************************************************/ +#define vlc_cond_wait( P_COND, P_MUTEX ) \ + __vlc_cond_wait( __FILE__, __LINE__, P_COND, P_MUTEX ) + +static inline void __vlc_cond_wait( const char * psz_file, int i_line, + vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex ) +{ +#if defined(LIBVLC_USE_PTHREAD) + int val = pthread_cond_wait( p_condvar, p_mutex ); + VLC_THREAD_ASSERT ("waiting on condition"); + +#elif defined( UNDER_CE ) + LeaveCriticalSection( &p_mutex->csection ); + WaitForSingleObject( *p_condvar, INFINITE ); + + /* Reacquire the mutex before returning. */ + vlc_mutex_lock( p_mutex ); + +#elif defined( WIN32 ) + (void)psz_file; (void)i_line; + + /* Increase our wait count */ + SignalObjectAndWait( *p_mutex, *p_condvar, INFINITE, FALSE ); + + /* Reacquire the mutex before returning. */ + vlc_mutex_lock( p_mutex ); + +#elif defined( SYS_BEOS ) + /* The p_condvar->thread var is initialized before the unlock because + * it enables to identify when the thread is interrupted beetwen the + * unlock line and the suspend_thread line */ + p_condvar->thread = find_thread( NULL ); + vlc_mutex_unlock( p_mutex ); + suspend_thread( p_condvar->thread ); + p_condvar->thread = -1; + + vlc_mutex_lock( p_mutex ); + +#endif +} + + +/***************************************************************************** + * vlc_cond_timedwait: wait until condition completion or expiration + ***************************************************************************** + * Returns 0 if object signaled, an error code in case of timeout or error. + *****************************************************************************/ +#define vlc_cond_timedwait( P_COND, P_MUTEX, DEADLINE ) \ + __vlc_cond_timedwait( __FILE__, __LINE__, P_COND, P_MUTEX, DEADLINE ) + +static inline int __vlc_cond_timedwait( const char * psz_file, int i_line, + vlc_cond_t *p_condvar, + vlc_mutex_t *p_mutex, + mtime_t deadline ) +{ +#if defined(LIBVLC_USE_PTHREAD) + lldiv_t d = lldiv( deadline, 1000000 ); + struct timespec ts = { d.quot, d.rem * 1000 }; + + int val = pthread_cond_timedwait (p_condvar, p_mutex, &ts); + if (val == ETIMEDOUT) + return ETIMEDOUT; /* this error is perfectly normal */ + VLC_THREAD_ASSERT ("timed-waiting on condition"); + +#elif defined( UNDER_CE ) + mtime_t delay_ms = (deadline - mdate())/1000; + DWORD result; + if( delay_ms < 0 ) + delay_ms = 0; + + LeaveCriticalSection( &p_mutex->csection ); + result = WaitForSingleObject( *p_condvar, delay_ms ); + + /* Reacquire the mutex before returning. */ + vlc_mutex_lock( p_mutex ); + + if(result == WAIT_TIMEOUT) + return ETIMEDOUT; /* this error is perfectly normal */ + + (void)psz_file; (void)i_line; + +#elif defined( WIN32 ) + mtime_t total = (deadline - mdate())/1000; + DWORD result; + if( total < 0 ) + total = 0; + + do + { + DWORD delay = (total > 0x7fffffff) ? 0x7fffffff : total; + result = SignalObjectAndWait( *p_mutex, *p_condvar, + delay, FALSE ); + total -= delay; + vlc_mutex_lock (p_mutex); + } + while (total); + + /* Reacquire the mutex before returning. */ + if(result == WAIT_TIMEOUT) + return ETIMEDOUT; /* this error is perfectly normal */ + + (void)psz_file; (void)i_line; + +#elif defined( SYS_BEOS ) +# error Unimplemented + +#endif + + return 0; +} + +/***************************************************************************** + * vlc_cond_destroy: destroy a condition + *****************************************************************************/ +#define vlc_cond_destroy( P_COND ) \ + __vlc_cond_destroy( __FILE__, __LINE__, P_COND ) + +/***************************************************************************** + * vlc_threadvar_set: create: set the value of a thread-local variable + *****************************************************************************/ +static inline int vlc_threadvar_set( vlc_threadvar_t * p_tls, void *p_value ) +{ + int i_ret; + +#if defined(LIBVLC_USE_PTHREAD) + i_ret = pthread_setspecific( *p_tls, p_value ); + +#elif defined( SYS_BEOS ) + i_ret = EINVAL; + +#elif defined( UNDER_CE ) || defined( WIN32 ) + i_ret = TlsSetValue( *p_tls, p_value ) ? EINVAL : 0; + +#endif + + return i_ret; +} + +/***************************************************************************** + * vlc_threadvar_get: create: get the value of a thread-local variable + *****************************************************************************/ +static inline void* vlc_threadvar_get( vlc_threadvar_t * p_tls ) +{ + void *p_ret; + +#if defined(LIBVLC_USE_PTHREAD) + p_ret = pthread_getspecific( *p_tls ); + +#elif defined( SYS_BEOS ) + p_ret = NULL; + +#elif defined( UNDER_CE ) || defined( WIN32 ) + p_ret = TlsGetValue( *p_tls ); + +#endif + + return p_ret; +} + +# if defined (_POSIX_SPIN_LOCKS) && ((_POSIX_SPIN_LOCKS - 0) > 0) +typedef pthread_spinlock_t vlc_spinlock_t; + +/** + * Initializes a spinlock. + */ +static inline int vlc_spin_init (vlc_spinlock_t *spin) +{ + return pthread_spin_init (spin, PTHREAD_PROCESS_PRIVATE); +} + +/** + * Acquires a spinlock. + */ +static inline void vlc_spin_lock (vlc_spinlock_t *spin) +{ + pthread_spin_lock (spin); +} + +/** + * Releases a spinlock. + */ +static inline void vlc_spin_unlock (vlc_spinlock_t *spin) +{ + pthread_spin_unlock (spin); +} + +/** + * Deinitializes a spinlock. + */ +static inline void vlc_spin_destroy (vlc_spinlock_t *spin) +{ + pthread_spin_destroy (spin); +} + +#elif defined( WIN32 ) + +typedef CRITICAL_SECTION vlc_spinlock_t; + +/** + * Initializes a spinlock. + */ +static inline int vlc_spin_init (vlc_spinlock_t *spin) +{ + return !InitializeCriticalSectionAndSpinCount(spin, 4000); +} + +/** + * Acquires a spinlock. + */ +static inline void vlc_spin_lock (vlc_spinlock_t *spin) +{ + EnterCriticalSection(spin); +} + +/** + * Releases a spinlock. + */ +static inline void vlc_spin_unlock (vlc_spinlock_t *spin) +{ + LeaveCriticalSection(spin); +} + +/** + * Deinitializes a spinlock. + */ +static inline void vlc_spin_destroy (vlc_spinlock_t *spin) +{ + DeleteCriticalSection(spin); +} + +#else + +/* Fallback to plain mutexes if spinlocks are not available */ +typedef vlc_mutex_t vlc_spinlock_t; + +static inline int vlc_spin_init (vlc_spinlock_t *spin) +{ + return vlc_mutex_init (spin); +} + +# define vlc_spin_lock vlc_mutex_lock +# define vlc_spin_unlock vlc_mutex_unlock +# define vlc_spin_destroy vlc_mutex_destroy +#endif + +/** + * Issues a full memory barrier. + */ +#if defined (__APPLE__) +# include /* OSMemoryBarrier() */ +#endif +static inline void barrier (void) +{ +#if defined (__GNUC__) && (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) + __sync_synchronize (); +#elif defined(__APPLE__) + OSMemoryBarrier (); +#elif defined(__powerpc__) + asm volatile ("sync":::"memory"); +#elif defined(__i386__) + asm volatile ("mfence":::"memory"); +#else + vlc_spinlock_t spin; + vlc_spin_init (&spin); + vlc_spin_lock (&spin); + vlc_spin_unlock (&spin); + vlc_spin_destroy (&spin); +#endif +} + +/***************************************************************************** + * vlc_thread_create: create a thread + *****************************************************************************/ +#define vlc_thread_create( P_THIS, PSZ_NAME, FUNC, PRIORITY, WAIT ) \ + __vlc_thread_create( VLC_OBJECT(P_THIS), __FILE__, __LINE__, PSZ_NAME, FUNC, PRIORITY, WAIT ) + +/***************************************************************************** + * vlc_thread_set_priority: set the priority of the calling thread + *****************************************************************************/ +#define vlc_thread_set_priority( P_THIS, PRIORITY ) \ + __vlc_thread_set_priority( VLC_OBJECT(P_THIS), __FILE__, __LINE__, PRIORITY ) + +/***************************************************************************** + * vlc_thread_join: wait until a thread exits + *****************************************************************************/ +#define vlc_thread_join( P_THIS ) \ + __vlc_thread_join( VLC_OBJECT(P_THIS), __FILE__, __LINE__ ) + +#endif /* !_VLC_THREADS_H */ diff --git a/VLC/vlc_tls.h b/VLC/vlc_tls.h new file mode 100644 index 0000000..44af3cc --- /dev/null +++ b/VLC/vlc_tls.h @@ -0,0 +1,83 @@ +/***************************************************************************** + * tls.c: Transport Layer Security API + ***************************************************************************** + * Copyright (C) 2004-2007 the VideoLAN team + * $Id$ + * + * Authors: Rémi Denis-Courmont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_TLS_H +# define VLC_TLS_H + +/** + * \file + * This file defines Transport Layer Security API (TLS) in vlc + */ + +# include "vlc_network.h" + +typedef struct tls_server_sys_t tls_server_sys_t; + +struct tls_server_t +{ + VLC_COMMON_MEMBERS + + module_t *p_module; + tls_server_sys_t *p_sys; + + int (*pf_add_CA) ( tls_server_t *, const char * ); + int (*pf_add_CRL) ( tls_server_t *, const char * ); + + tls_session_t * (*pf_open) ( tls_server_t * ); + void (*pf_close) ( tls_server_t *, tls_session_t * ); +}; + +typedef struct tls_session_sys_t tls_session_sys_t; + +struct tls_session_t +{ + VLC_COMMON_MEMBERS + + module_t *p_module; + tls_session_sys_t *p_sys; + + struct virtual_socket_t sock; + void (*pf_set_fd) ( tls_session_t *, int ); + int (*pf_handshake) ( tls_session_t * ); +}; + + +tls_server_t *tls_ServerCreate (vlc_object_t *, const char *, const char *); +void tls_ServerDelete (tls_server_t *); +int tls_ServerAddCA (tls_server_t *srv, const char *path); +int tls_ServerAddCRL (tls_server_t *srv, const char *path); + +tls_session_t *tls_ServerSessionPrepare (tls_server_t *); +int tls_ServerSessionHandshake (tls_session_t *, int fd); +int tls_SessionContinueHandshake (tls_session_t *); +void tls_ServerSessionClose (tls_session_t *); + +VLC_EXPORT( tls_session_t *, tls_ClientCreate, ( vlc_object_t *, int, const char * ) ); +VLC_EXPORT( void, tls_ClientDelete, ( tls_session_t * ) ); + +/* NOTE: It is assumed that a->sock.p_sys = a */ +# define tls_Send( a, b, c ) (((tls_session_t *)a)->sock.pf_send (a, b, c )) + +# define tls_Recv( a, b, c ) (((tls_session_t *)a)->sock.pf_recv (a, b, c )) + +#endif diff --git a/VLC/vlc_update.h b/VLC/vlc_update.h new file mode 100644 index 0000000..1bb91f9 --- /dev/null +++ b/VLC/vlc_update.h @@ -0,0 +1,72 @@ +/***************************************************************************** + * vlc_update.h: VLC update download + ***************************************************************************** + * Copyright © 2005-2007 the VideoLAN team + * $Id$ + * + * Authors: Antoine Cellerier + * Rafaël Carré + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either release 2 of the License, or + * (at your option) any later release. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + +#ifndef VLC_UPDATE_H +#define VLC_UPDATE_H + +/** + * \file + * This file defines update API in vlc + */ + +/** + * \defgroup update Update + * + * @{ + */ + +#ifdef UPDATE_CHECK + +/** + * Describes an update VLC release number + */ +struct update_release_t +{ + int i_major; ///< Version major + int i_minor; ///< Version minor + int i_revision; ///< Version revision + unsigned char extra;///< Version extra + char* psz_url; ///< Download URL + char* psz_desc; ///< Release description +}; + +#endif /* UPDATE_CHECK */ + +typedef struct update_release_t update_release_t; + +#define update_New( a ) __update_New( VLC_OBJECT( a ) ) + +VLC_EXPORT( update_t *, __update_New, ( vlc_object_t * ) ); +VLC_EXPORT( void, update_Delete, ( update_t * ) ); +VLC_EXPORT( void, update_Check, ( update_t *, void (*callback)( void*, bool ), void * ) ); +VLC_EXPORT( bool, update_NeedUpgrade, ( update_t * ) ); +VLC_EXPORT( void, update_Download, ( update_t *, const char* ) ); +VLC_EXPORT( update_release_t*, update_GetRelease, ( update_t * ) ); +VLC_EXPORT( void, update_WaitDownload, ( update_t * ) ); + +/** + * @} + */ + +#endif /* _VLC_UPDATE_H */ diff --git a/VLC/vlc_url.h b/VLC/vlc_url.h new file mode 100644 index 0000000..7cedd5b --- /dev/null +++ b/VLC/vlc_url.h @@ -0,0 +1,234 @@ +/***************************************************************************** + * vlc_url.h: URL related macros + ***************************************************************************** + * Copyright (C) 2002-2006 the VideoLAN team + * $Id$ + * + * Authors: Christophe Massiot + * Rémi Denis-Courmont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_URL_H +# define VLC_URL_H + +/** + * \file + * This file defines functions for manipulating URL in vlc + */ + +struct vlc_url_t +{ + char *psz_protocol; + char *psz_username; + char *psz_password; + char *psz_host; + int i_port; + + char *psz_path; + + char *psz_option; + + char *psz_buffer; /* to be freed */ +}; + +VLC_EXPORT( char *, unescape_URI_duplicate, ( const char *psz ) ); +VLC_EXPORT( void, unescape_URI, ( char *psz ) ); +VLC_EXPORT( char *, decode_URI_duplicate, ( const char *psz ) ); +VLC_EXPORT( void, decode_URI, ( char *psz ) ); +VLC_EXPORT( char *, encode_URI_component, ( const char *psz ) ); + +/***************************************************************************** + * vlc_UrlParse: + ***************************************************************************** + * option : if != 0 then path is split at this char + * + * format [protocol://[login[:password]@]][host[:port]]/path[OPTIONoption] + *****************************************************************************/ +static inline void vlc_UrlParse( vlc_url_t *url, const char *psz_url, + char option ) +{ + char *psz_dup; + char *psz_parse; + char *p; + char *p2; + + url->psz_protocol = NULL; + url->psz_username = NULL; + url->psz_password = NULL; + url->psz_host = NULL; + url->i_port = 0; + url->psz_path = NULL; + url->psz_option = NULL; + + if( psz_url == NULL ) + { + url->psz_buffer = NULL; + return; + } + url->psz_buffer = psz_parse = psz_dup = strdup( psz_url ); + + /* Search a valid protocol */ + p = strstr( psz_parse, ":/" ); + if( p != NULL ) + { + char *p2; + for( p2 = psz_parse; p2 < p; p2++ ) + { +#define I(i,a,b) ( (a) <= (i) && (i) <= (b) ) + if( !I(*p2, 'a', 'z' ) && !I(*p2, 'A', 'Z') && !I(*p2, '0', '9') && *p2 != '+' && *p2 != '-' && *p2 != '.' ) + { + p = NULL; + break; + } +#undef I + } + } + + if( p != NULL ) + { + /* we have a protocol */ + + /* skip :// */ + *p++ = '\0'; + if( p[1] == '/' ) + p += 2; + url->psz_protocol = psz_parse; + psz_parse = p; + } + p = strchr( psz_parse, '@' ); + p2 = strchr( psz_parse, '/' ); + if( p != NULL && ( p2 != NULL ? p < p2 : 1 ) ) + { + /* We have a login */ + url->psz_username = psz_parse; + *p++ = '\0'; + + psz_parse = strchr( psz_parse, ':' ); + if( psz_parse != NULL ) + { + /* We have a password */ + *psz_parse++ = '\0'; + url->psz_password = psz_parse; + decode_URI( url->psz_password ); + } + decode_URI( url->psz_username ); + psz_parse = p; + } + + p = strchr( psz_parse, '/' ); + if( !p || psz_parse < p ) + { + char *p2; + + /* We have a host[:port] */ + url->psz_host = strdup( psz_parse ); + if( p ) + { + url->psz_host[p - psz_parse] = '\0'; + } + + if( *url->psz_host == '[' ) + { + /* Ipv6 address */ + p2 = strchr( url->psz_host, ']' ); + if( p2 ) + { + p2 = strchr( p2, ':' ); + } + } + else + { + p2 = strchr( url->psz_host, ':' ); + } + if( p2 ) + { + *p2++ = '\0'; + url->i_port = atoi( p2 ); + } + } + psz_parse = p; + + /* Now parse psz_path and psz_option */ + if( psz_parse ) + { + url->psz_path = psz_parse; + if( option != '\0' ) + { + p = strchr( url->psz_path, option ); + if( p ) + { + *p++ = '\0'; + url->psz_option = p; + } + } + } +} + +/***************************************************************************** + * vlc_UrlClean: + *****************************************************************************/ +static inline void vlc_UrlClean( vlc_url_t *url ) +{ + free( url->psz_buffer ); + free( url->psz_host ); + + url->psz_protocol = NULL; + url->psz_username = NULL; + url->psz_password = NULL; + url->psz_host = NULL; + url->i_port = 0; + url->psz_path = NULL; + url->psz_option = NULL; + + url->psz_buffer = NULL; +} + +static inline char *vlc_UrlEncode( const char *psz_url ) +{ + /* FIXME: do not encode / : ? and & _when_ not needed */ + return encode_URI_component( psz_url ); +} + +#include + +/** Check whether a given string is not a valid URL and must hence be + * encoded */ +static inline int vlc_UrlIsNotEncoded( const char *psz_url ) +{ + const char *ptr; + + for( ptr = psz_url; *ptr; ptr++ ) + { + char c = *ptr; + + if( c == '%' ) + { + if( !isxdigit( ptr[1] ) || !isxdigit( ptr[2] ) ) + return 1; /* not encoded */ + ptr += 2; + } + else + if( ( (unsigned char)( c - 'a' ) < 26 ) + || ( (unsigned char)( c - 'A' ) < 26 ) + || ( (unsigned char)( c - '0' ) < 10 ) + || ( strchr( "-_.", c ) != NULL ) ) + return 1; + } + return 0; /* looks fine - but maybe it is not encoded */ +} + +#endif diff --git a/VLC/vlc_variables.h b/VLC/vlc_variables.h new file mode 100644 index 0000000..aa1c6b4 --- /dev/null +++ b/VLC/vlc_variables.h @@ -0,0 +1,619 @@ +/***************************************************************************** + * variables.h: variables handling + ***************************************************************************** + * Copyright (C) 2002-2004 the VideoLAN team + * $Id$ + * + * Authors: Samuel Hocevar + * Gildas Bazin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_VARIABLES_H +#define VLC_VARIABLES_H 1 + +/** + * \file + * This file defines functions and structures for dynamic variables in vlc + */ + +/** + * \defgroup variables Variables + * + * Functions for using the object variables in vlc. + * + * Vlc have a very powerful "object variable" infrastructure useful + * for many things. + * + * @{ + */ + +/***************************************************************************** + * Variable types - probably very incomplete + *****************************************************************************/ +#define VLC_VAR_TYPE 0x00ff +#define VLC_VAR_FLAGS 0xff00 + +/** \defgroup var_flags Additive flags + * These flags are added to the type field of the variable. Most as a result of + * a __var_Change() call, but some may be added at creation time + * @{ + */ +#define VLC_VAR_HASCHOICE 0x0100 +#define VLC_VAR_HASMIN 0x0200 +#define VLC_VAR_HASMAX 0x0400 +#define VLC_VAR_HASSTEP 0x0800 + +#define VLC_VAR_ISCOMMAND 0x2000 + +/** Creation flag */ +#define VLC_VAR_DOINHERIT 0x8000 +/**@}*/ + +/** + * \defgroup var_action Variable actions + * These are the different actions that can be used with __var_Change(). + * The parameters given are the meaning of the two last parameters of + * __var_Change() when this action is being used. + * @{ + */ + +/** + * Set the minimum value of this variable + * \param p_val The new minimum value + * \param p_val2 Unused + */ +#define VLC_VAR_SETMIN 0x0010 +/** + * Set the maximum value of this variable + * \param p_val The new maximum value + * \param p_val2 Unused + */ +#define VLC_VAR_SETMAX 0x0011 +#define VLC_VAR_SETSTEP 0x0012 + +/** + * Set the value of this variable without triggering any callbacks + * \param p_val The new value + * \param p_val2 Unused + */ +#define VLC_VAR_SETVALUE 0x0013 + +#define VLC_VAR_SETTEXT 0x0014 +#define VLC_VAR_GETTEXT 0x0015 + +#define VLC_VAR_GETMIN 0x0016 +#define VLC_VAR_GETMAX 0x0017 +#define VLC_VAR_GETSTEP 0x0018 + +#define VLC_VAR_ADDCHOICE 0x0020 +#define VLC_VAR_DELCHOICE 0x0021 +#define VLC_VAR_CLEARCHOICES 0x0022 +#define VLC_VAR_SETDEFAULT 0x0023 +#define VLC_VAR_GETCHOICES 0x0024 +#define VLC_VAR_FREECHOICES 0x0025 +#define VLC_VAR_GETLIST 0x0026 +#define VLC_VAR_FREELIST 0x0027 +#define VLC_VAR_CHOICESCOUNT 0x0028 + +#define VLC_VAR_INHERITVALUE 0x0030 +#define VLC_VAR_TRIGGER_CALLBACKS 0x0035 + +#define VLC_VAR_SETISCOMMAND 0x0040 +/**@}*/ + +/***************************************************************************** + * Prototypes + *****************************************************************************/ +VLC_EXPORT( int, __var_Create, ( vlc_object_t *, const char *, int ) ); +VLC_EXPORT( int, __var_Destroy, ( vlc_object_t *, const char * ) ); + +VLC_EXPORT( int, __var_Change, ( vlc_object_t *, const char *, int, vlc_value_t *, vlc_value_t * ) ); + +VLC_EXPORT( int, __var_Type, ( vlc_object_t *, const char * ) ); +VLC_EXPORT( int, __var_Set, ( vlc_object_t *, const char *, vlc_value_t ) ); +VLC_EXPORT( int, __var_Get, ( vlc_object_t *, const char *, vlc_value_t * ) ); + +#define var_Command(a,b,c,d,e) __var_Command( VLC_OBJECT( a ), b, c, d, e ) +VLC_EXPORT( int, __var_Command, ( vlc_object_t *, const char *, const char *, const char *, char ** ) ); + +VLC_EXPORT( vlc_mutex_t *, var_AcquireMutex, ( const char * ) ); + +/** + * __var_Create() with automatic casting. + */ +#define var_Create(a,b,c) __var_Create( VLC_OBJECT(a), b, c ) +/** + * __var_Destroy() with automatic casting + */ +#define var_Destroy(a,b) __var_Destroy( VLC_OBJECT(a), b ) + +/** + * __var_Change() with automatic casting + */ +#define var_Change(a,b,c,d,e) __var_Change( VLC_OBJECT(a), b, c, d, e ) + +/** + * __var_Type() with automatic casting + */ +#define var_Type(a,b) __var_Type( VLC_OBJECT(a), b ) +/** + * __var_Set() with automatic casting + */ +#define var_Set(a,b,c) __var_Set( VLC_OBJECT(a), b, c ) +/** + * __var_Get() with automatic casting + */ +#define var_Get(a,b,c) __var_Get( VLC_OBJECT(a), b, c ) + +/***************************************************************************** + * Variable callbacks + ***************************************************************************** + * int MyCallback( vlc_object_t *p_this, + * char const *psz_variable, + * vlc_value_t oldvalue, + * vlc_value_t newvalue, + * void *p_data); + *****************************************************************************/ +VLC_EXPORT( int, __var_AddCallback, ( vlc_object_t *, const char *, vlc_callback_t, void * ) ); +VLC_EXPORT( int, __var_DelCallback, ( vlc_object_t *, const char *, vlc_callback_t, void * ) ); +VLC_EXPORT( int, __var_TriggerCallback, ( vlc_object_t *, const char * ) ); + +/** + * __var_AddCallback() with automatic casting + */ +#define var_AddCallback(a,b,c,d) __var_AddCallback( VLC_OBJECT(a), b, c, d ) + +/** + * __var_DelCallback() with automatic casting + */ +#define var_DelCallback(a,b,c,d) __var_DelCallback( VLC_OBJECT(a), b, c, d ) + +/** + * __var_TriggerCallback() with automatic casting + */ +#define var_TriggerCallback(a,b) __var_TriggerCallback( VLC_OBJECT(a), b ) + +/***************************************************************************** + * helpers functions + *****************************************************************************/ + +/** + * Set the value of an integer variable + * + * \param p_obj The object that holds the variable + * \param psz_name The name of the variable + * \param i The new integer value of this variable + */ +static inline int __var_SetInteger( vlc_object_t *p_obj, const char *psz_name, int i ) +{ + vlc_value_t val; + val.i_int = i; + return __var_Set( p_obj, psz_name, val ); +} +#define var_SetInteger(a,b,c) __var_SetInteger( VLC_OBJECT(a),b,c) +/** + * Set the value of an boolean variable + * + * \param p_obj The object that holds the variable + * \param psz_name The name of the variable + * \param b The new boolean value of this variable + */ +static inline int __var_SetBool( vlc_object_t *p_obj, const char *psz_name, bool b ) +{ + vlc_value_t val; + val.b_bool = b; + return __var_Set( p_obj, psz_name, val ); +} + +/** + * Set the value of a time variable + * + * \param p_obj The object that holds the variable + * \param psz_name The name of the variable + * \param i The new time value of this variable + */ +static inline int __var_SetTime( vlc_object_t *p_obj, const char *psz_name, int64_t i ) +{ + vlc_value_t val; + val.i_time = i; + return __var_Set( p_obj, psz_name, val ); +} + +/** + * Set the value of a float variable + * + * \param p_obj The object that holds the variable + * \param psz_name The name of the variable + * \param f The new float value of this variable + */ +static inline int __var_SetFloat( vlc_object_t *p_obj, const char *psz_name, float f ) +{ + vlc_value_t val; + val.f_float = f; + return __var_Set( p_obj, psz_name, val ); +} + +/** + * Set the value of a string variable + * + * \param p_obj The object that holds the variable + * \param psz_name The name of the variable + * \param psz_string The new string value of this variable + */ +static inline int __var_SetString( vlc_object_t *p_obj, const char *psz_name, const char *psz_string ) +{ + vlc_value_t val; + val.psz_string = (char *)psz_string; + return __var_Set( p_obj, psz_name, val ); +} + +/** + * Trigger the callbacks on a void variable + * + * \param p_obj The object that holds the variable + * \param psz_name The name of the variable + */ +static inline int __var_SetVoid( vlc_object_t *p_obj, const char *psz_name ) +{ + vlc_value_t val; + val.b_bool = true; + return __var_Set( p_obj, psz_name, val ); +} +#define var_SetVoid(a,b) __var_SetVoid( VLC_OBJECT(a),b) + +/** + * __var_SetBool() with automatic casting + */ +#define var_SetBool(a,b,c) __var_SetBool( VLC_OBJECT(a),b,c) + +/** + * __var_SetTime() with automatic casting + */ +#define var_SetTime(a,b,c) __var_SetTime( VLC_OBJECT(a),b,c) +/** + * __var_SetFloat() with automatic casting + */ +#define var_SetFloat(a,b,c) __var_SetFloat( VLC_OBJECT(a),b,c) +/** + * __var_SetString() with automatic casting + */ +#define var_SetString(a,b,c) __var_SetString( VLC_OBJECT(a),b,c) + +/** + * Get an integer value +* + * \param p_obj The object that holds the variable + * \param psz_name The name of the variable + */ +static inline int __var_GetInteger( vlc_object_t *p_obj, const char *psz_name ) +{ + vlc_value_t val;val.i_int = 0; + if( !__var_Get( p_obj, psz_name, &val ) ) + return val.i_int; + else + return 0; +} + +/** + * Get a boolean value + * + * \param p_obj The object that holds the variable + * \param psz_name The name of the variable + */ +static inline int __var_GetBool( vlc_object_t *p_obj, const char *psz_name ) +{ + vlc_value_t val; val.b_bool = false; + if( !__var_Get( p_obj, psz_name, &val ) ) + return val.b_bool; + else + return false; +} + +/** + * Get a time value + * + * \param p_obj The object that holds the variable + * \param psz_name The name of the variable + */ +static inline int64_t __var_GetTime( vlc_object_t *p_obj, const char *psz_name ) +{ + vlc_value_t val; val.i_time = 0L; + if( !__var_Get( p_obj, psz_name, &val ) ) + return val.i_time; + else + return 0; +} + +/** + * Get a float value + * + * \param p_obj The object that holds the variable + * \param psz_name The name of the variable + */ +static inline float __var_GetFloat( vlc_object_t *p_obj, const char *psz_name ) +{ + vlc_value_t val; val.f_float = 0.0; + if( !__var_Get( p_obj, psz_name, &val ) ) + return val.f_float; + else + return 0.0; +} + +/** + * Get a string value + * + * \param p_obj The object that holds the variable + * \param psz_name The name of the variable + */ +static inline char *__var_GetString( vlc_object_t *p_obj, const char *psz_name ) +{ + vlc_value_t val; val.psz_string = NULL; + if( __var_Get( p_obj, psz_name, &val ) ) + return NULL; + else + return val.psz_string; +} + +static inline char *__var_GetNonEmptyString( vlc_object_t *obj, const char *name ) +{ + vlc_value_t val; + if( __var_Get( obj, name, &val ) ) + return NULL; + if( *val.psz_string ) + return val.psz_string; + free( val.psz_string ); + return NULL; +} + + +/** + * __var_GetInteger() with automatic casting + */ +#define var_GetInteger(a,b) __var_GetInteger( VLC_OBJECT(a),b) +/** + * __var_GetBool() with automatic casting + */ +#define var_GetBool(a,b) __var_GetBool( VLC_OBJECT(a),b) +/** + * __var_GetTime() with automatic casting + */ +#define var_GetTime(a,b) __var_GetTime( VLC_OBJECT(a),b) +/** + * __var_GetFloat() with automatic casting + */ +#define var_GetFloat(a,b) __var_GetFloat( VLC_OBJECT(a),b) +/** + * __var_GetString() with automatic casting + */ +#define var_GetString(a,b) __var_GetString( VLC_OBJECT(a),b) +#define var_GetNonEmptyString(a,b) __var_GetNonEmptyString( VLC_OBJECT(a),b) + + + +/** + * Increment an integer variable + * \param p_obj the object that holds the variable + * \param psz_name the name of the variable + */ +static inline void __var_IncInteger( vlc_object_t *p_obj, const char *psz_name ) +{ + int i_val = __var_GetInteger( p_obj, psz_name ); + __var_SetInteger( p_obj, psz_name, ++i_val ); +} +#define var_IncInteger(a,b) __var_IncInteger( VLC_OBJECT(a), b ) + +/** + * Decrement an integer variable + * \param p_obj the object that holds the variable + * \param psz_name the name of the variable + */ +static inline void __var_DecInteger( vlc_object_t *p_obj, const char *psz_name ) +{ + int i_val = __var_GetInteger( p_obj, psz_name ); + __var_SetInteger( p_obj, psz_name, --i_val ); +} +#define var_DecInteger(a,b) __var_DecInteger( VLC_OBJECT(a), b ) + +/** + * Create a integer variable with inherit and get its value. + * + * \param p_obj The object that holds the variable + * \param psz_name The name of the variable + */ +static inline int __var_CreateGetInteger( vlc_object_t *p_obj, const char *psz_name ) +{ + __var_Create( p_obj, psz_name, VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); + return __var_GetInteger( p_obj, psz_name ); +} + +/** + * Create a boolean variable with inherit and get its value. + * + * \param p_obj The object that holds the variable + * \param psz_name The name of the variable + */ +static inline int __var_CreateGetBool( vlc_object_t *p_obj, const char *psz_name ) +{ + __var_Create( p_obj, psz_name, VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); + return __var_GetBool( p_obj, psz_name ); +} + +/** + * Create a time variable with inherit and get its value. + * + * \param p_obj The object that holds the variable + * \param psz_name The name of the variable + */ +static inline int64_t __var_CreateGetTime( vlc_object_t *p_obj, const char *psz_name ) +{ + __var_Create( p_obj, psz_name, VLC_VAR_TIME | VLC_VAR_DOINHERIT ); + return __var_GetTime( p_obj, psz_name ); +} + +/** + * Create a float variable with inherit and get its value. + * + * \param p_obj The object that holds the variable + * \param psz_name The name of the variable + */ +static inline float __var_CreateGetFloat( vlc_object_t *p_obj, const char *psz_name ) +{ + __var_Create( p_obj, psz_name, VLC_VAR_FLOAT | VLC_VAR_DOINHERIT ); + return __var_GetFloat( p_obj, psz_name ); +} + +/** + * Create a string variable with inherit and get its value. + * + * \param p_obj The object that holds the variable + * \param psz_name The name of the variable + */ +static inline char *__var_CreateGetString( vlc_object_t *p_obj, + const char *psz_name ) +{ + __var_Create( p_obj, psz_name, VLC_VAR_STRING | VLC_VAR_DOINHERIT ); + return __var_GetString( p_obj, psz_name ); +} + +static inline char *__var_CreateGetNonEmptyString( vlc_object_t *p_obj, + const char *psz_name ) +{ + __var_Create( p_obj, psz_name, VLC_VAR_STRING | VLC_VAR_DOINHERIT ); + return __var_GetNonEmptyString( p_obj, psz_name ); +} + +/** + * __var_CreateGetInteger() with automatic casting + */ +#define var_CreateGetInteger(a,b) __var_CreateGetInteger( VLC_OBJECT(a),b) +/** + * __var_CreateGetBool() with automatic casting + */ +#define var_CreateGetBool(a,b) __var_CreateGetBool( VLC_OBJECT(a),b) +/** + * __var_CreateGetTime() with automatic casting + */ +#define var_CreateGetTime(a,b) __var_CreateGetTime( VLC_OBJECT(a),b) +/** + * __var_CreateGetFloat() with automatic casting + */ +#define var_CreateGetFloat(a,b) __var_CreateGetFloat( VLC_OBJECT(a),b) +/** + * __var_CreateGetString() with automatic casting + */ +#define var_CreateGetString(a,b) __var_CreateGetString( VLC_OBJECT(a),b) +#define var_CreateGetNonEmptyString(a,b) __var_CreateGetNonEmptyString( VLC_OBJECT(a),b) + +/** + * Create a integer command variable with inherit and get its value. + * + * \param p_obj The object that holds the variable + * \param psz_name The name of the variable + */ +static inline int __var_CreateGetIntegerCommand( vlc_object_t *p_obj, const char *psz_name ) +{ + __var_Create( p_obj, psz_name, VLC_VAR_INTEGER | VLC_VAR_DOINHERIT + | VLC_VAR_ISCOMMAND ); + return __var_GetInteger( p_obj, psz_name ); +} + +/** + * Create a boolean command variable with inherit and get its value. + * + * \param p_obj The object that holds the variable + * \param psz_name The name of the variable + */ +static inline int __var_CreateGetBoolCommand( vlc_object_t *p_obj, const char *psz_name ) +{ + __var_Create( p_obj, psz_name, VLC_VAR_BOOL | VLC_VAR_DOINHERIT + | VLC_VAR_ISCOMMAND ); + return __var_GetBool( p_obj, psz_name ); +} + +/** + * Create a time command variable with inherit and get its value. + * + * \param p_obj The object that holds the variable + * \param psz_name The name of the variable + */ +static inline int64_t __var_CreateGetTimeCommand( vlc_object_t *p_obj, const char *psz_name ) +{ + __var_Create( p_obj, psz_name, VLC_VAR_TIME | VLC_VAR_DOINHERIT + | VLC_VAR_ISCOMMAND ); + return __var_GetTime( p_obj, psz_name ); +} + +/** + * Create a float command variable with inherit and get its value. + * + * \param p_obj The object that holds the variable + * \param psz_name The name of the variable + */ +static inline float __var_CreateGetFloatCommand( vlc_object_t *p_obj, const char *psz_name ) +{ + __var_Create( p_obj, psz_name, VLC_VAR_FLOAT | VLC_VAR_DOINHERIT + | VLC_VAR_ISCOMMAND ); + return __var_GetFloat( p_obj, psz_name ); +} + +/** + * Create a string command variable with inherit and get its value. + * + * \param p_obj The object that holds the variable + * \param psz_name The name of the variable + */ +static inline char *__var_CreateGetStringCommand( vlc_object_t *p_obj, + const char *psz_name ) +{ + __var_Create( p_obj, psz_name, VLC_VAR_STRING | VLC_VAR_DOINHERIT + | VLC_VAR_ISCOMMAND ); + return __var_GetString( p_obj, psz_name ); +} + +static inline char *__var_CreateGetNonEmptyStringCommand( vlc_object_t *p_obj, + const char *psz_name ) +{ + __var_Create( p_obj, psz_name, VLC_VAR_STRING | VLC_VAR_DOINHERIT + | VLC_VAR_ISCOMMAND ); + return __var_GetNonEmptyString( p_obj, psz_name ); +} + +/** + * __var_CreateGetInteger() with automatic casting + */ +#define var_CreateGetIntegerCommand(a,b) __var_CreateGetIntegerCommand( VLC_OBJECT(a),b) +/** + * __var_CreateGetBoolCommand() with automatic casting + */ +#define var_CreateGetBoolCommand(a,b) __var_CreateGetBoolCommand( VLC_OBJECT(a),b) +/** + * __var_CreateGetTimeCommand() with automatic casting + */ +#define var_CreateGetTimeCommand(a,b) __var_CreateGetTimeCommand( VLC_OBJECT(a),b) +/** + * __var_CreateGetFloat() with automatic casting + */ +#define var_CreateGetFloatCommand(a,b) __var_CreateGetFloatCommand( VLC_OBJECT(a),b) +/** + * __var_CreateGetStringCommand() with automatic casting + */ +#define var_CreateGetStringCommand(a,b) __var_CreateGetStringCommand( VLC_OBJECT(a),b) +#define var_CreateGetNonEmptyStringCommand(a,b) __var_CreateGetNonEmptyStringCommand( VLC_OBJECT(a),b) +/** + * @} + */ +#endif /* _VLC_VARIABLES_H */ diff --git a/VLC/vlc_vlm.h b/VLC/vlc_vlm.h new file mode 100644 index 0000000..eafe32d --- /dev/null +++ b/VLC/vlc_vlm.h @@ -0,0 +1,288 @@ +/***************************************************************************** + * vlc_vlm.h: VLM core structures + ***************************************************************************** + * Copyright (C) 2000, 2001 the VideoLAN team + * $Id$ + * + * Authors: Simon Latapie + * Laurent Aimar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_VLM_H +#define VLC_VLM_H 1 + +/** + * \file + * This file defines VLM core functions and structures in vlc + */ + +#include "vlc_input.h" + +/** + * \defgroup server VLM + * VLM is the server core in vlc that allows streaming of multiple media streams + * at the same time. It provides broadcast, schedule and video on demand features + * for streaming using several streaming and network protocols. + * @{ + */ + +/* VLM media */ +typedef struct +{ + int64_t id; + bool b_enabled; + + /* */ + char *psz_name; + + /* */ + int i_input; + char **ppsz_input; + + int i_option; + char **ppsz_option; + + char *psz_output; + + /* */ + bool b_vod; + struct + { + bool b_loop; + } broadcast; + struct + { + char *psz_mux; + } vod; + +} vlm_media_t; + +/* VLM media instance */ +typedef struct +{ + char *psz_name; + + int64_t i_time; + int64_t i_length; + double d_position; + bool b_paused; + int i_rate; // normal is INPUT_RATE_DEFAULT +} vlm_media_instance_t; + +#if 0 +typedef struct +{ + +} vlm_schedule_t +#endif + +/* VLM control query */ +enum vlm_query_e +{ + /* --- Media control */ + /* Get all medias */ + VLM_GET_MEDIAS, /* arg1=vlm_media_t ***, int *pi_media */ + /* Delete all medias */ + VLM_CLEAR_MEDIAS, /* no arg */ + + /* Add a new media */ + VLM_ADD_MEDIA, /* arg1=vlm_media_t* arg2=int64_t *p_id res=can fail */ + /* Delete an existing media */ + VLM_DEL_MEDIA, /* arg1=int64_t id */ + /* Change properties of an existing media (all fields but id and b_vod) */ + VLM_CHANGE_MEDIA, /* arg1=vlm_media_t* res=can fail */ + /* Get 1 media by it's ID */ + VLM_GET_MEDIA, /* arg1=int64_t id arg2=vlm_media_t ** */ + /* Get media ID from its name */ + VLM_GET_MEDIA_ID, /* arg1=const char *psz_name arg2=int64_t* */ + + /* Media instance control XXX VOD control are for internal use only */ + /* Get all media instances */ + VLM_GET_MEDIA_INSTANCES, /* arg1=int64_t id arg2=vlm_media_instance_t *** arg3=int *pi_instance */ + /* Delete all media instances */ + VLM_CLEAR_MEDIA_INSTANCES, /* arg1=int64_t id */ + /* Control broadcast instance */ + VLM_START_MEDIA_BROADCAST_INSTANCE, /* arg1=int64_t id, arg2=const char *psz_instance_name, int i_input_index res=can fail */ + /* Control VOD instance */ + VLM_START_MEDIA_VOD_INSTANCE, /* arg1=int64_t id, arg2=const char *psz_instance_name, int i_input_index char *psz_vod_output res=can fail */ + /* Stop an instance */ + VLM_STOP_MEDIA_INSTANCE, /* arg1=int64_t id, arg2=const char *psz_instance_name res=can fail */ + /* Pause an instance */ + VLM_PAUSE_MEDIA_INSTANCE, /* arg1=int64_t id, arg2=const char *psz_instance_name res=can fail */ + /* Get instance position time (in microsecond) */ + VLM_GET_MEDIA_INSTANCE_TIME, /* arg1=int64_t id, arg2=const char *psz_instance_name arg3=int64_t * */ + /* Set instance position time (in microsecond) */ + VLM_SET_MEDIA_INSTANCE_TIME, /* arg1=int64_t id, arg2=const char *psz_instance_name arg3=int64_t */ + /* Get instance position ([0.0 .. 1.0]) */ + VLM_GET_MEDIA_INSTANCE_POSITION, /* arg1=int64_t id, arg2=const char *psz_instance_name arg3=double * */ + /* Set instance position ([0.0 .. 1.0]) */ + VLM_SET_MEDIA_INSTANCE_POSITION, /* arg1=int64_t id, arg2=const char *psz_instance_name arg3=double */ + + /* Schedule control */ + VLM_CLEAR_SCHEDULES, /* no arg */ + /* TODO: missing schedule control */ + + /* */ +}; + + +/* VLM specific - structures and functions */ + +/* ok, here is the structure of a vlm_message: + The parent node is ( name_of_the_command , NULL ), or + ( name_of_the_command , message_error ) on error. + If a node has children, it should not have a value (=NULL).*/ +struct vlm_message_t +{ + char *psz_name; + char *psz_value; + + int i_child; + vlm_message_t **child; +}; + + +#ifdef __cpluplus +extern "C" { +#endif + +#define vlm_New( a ) __vlm_New( VLC_OBJECT(a) ) +VLC_EXPORT( vlm_t *, __vlm_New, ( vlc_object_t * ) ); +VLC_EXPORT( void, vlm_Delete, ( vlm_t * ) ); +VLC_EXPORT( int, vlm_ExecuteCommand, ( vlm_t *, const char *, vlm_message_t ** ) ); +VLC_EXPORT( int, vlm_Control, ( vlm_t *p_vlm, int i_query, ... ) ); + +VLC_EXPORT( vlm_message_t *, vlm_MessageNew, ( const char *, const char *, ... ) LIBVLC_FORMAT( 2, 3 ) ); +VLC_EXPORT( vlm_message_t *, vlm_MessageAdd, ( vlm_message_t *, vlm_message_t * ) ); +VLC_EXPORT( void, vlm_MessageDelete, ( vlm_message_t * ) ); + +/* media helpers */ +static inline void vlm_media_Init( vlm_media_t *p_media ) +{ + memset( p_media, 0, sizeof(vlm_media_t) ); + p_media->id = 0; // invalid id + p_media->psz_name = NULL; + TAB_INIT( p_media->i_input, p_media->ppsz_input ); + TAB_INIT( p_media->i_option, p_media->ppsz_option ); + p_media->psz_output = NULL; + p_media->b_vod = false; + + p_media->vod.psz_mux = NULL; + p_media->broadcast.b_loop = false; +} + +static inline void vlm_media_Copy( vlm_media_t *p_dst, vlm_media_t *p_src ) +{ + int i; + + memset( p_dst, 0, sizeof(vlm_media_t) ); + p_dst->id = p_src->id; + p_dst->b_enabled = p_src->b_enabled; + if( p_src->psz_name ) + p_dst->psz_name = strdup( p_src->psz_name ); + + for( i = 0; i < p_src->i_input; i++ ) + TAB_APPEND_CPP( char, p_dst->i_input, p_dst->ppsz_input, strdup(p_src->ppsz_input[i]) ); + for( i = 0; i < p_src->i_option; i++ ) + TAB_APPEND_CPP( char, p_dst->i_option, p_dst->ppsz_option, strdup(p_src->ppsz_option[i]) ); + + if( p_src->psz_output ) + p_dst->psz_output = strdup( p_src->psz_output ); + + p_dst->b_vod = p_src->b_vod; + if( p_src->b_vod ) + { + if( p_src->vod.psz_mux ) + p_dst->vod.psz_mux = strdup( p_src->vod.psz_mux ); + } + else + { + p_dst->broadcast.b_loop = p_src->broadcast.b_loop; + } +} +static inline void vlm_media_Clean( vlm_media_t *p_media ) +{ + int i; + free( p_media->psz_name ); + + for( i = 0; i < p_media->i_input; i++ ) + free( p_media->ppsz_input[i]) ; + TAB_CLEAN(p_media->i_input, p_media->ppsz_input ); + + for( i = 0; i < p_media->i_option; i++ ) + free( p_media->ppsz_option[i]) ; + TAB_CLEAN(p_media->i_option, p_media->ppsz_option ); + + free( p_media->psz_output ); + if( p_media->b_vod ) + free( p_media->vod.psz_mux ); +} +static inline vlm_media_t *vlm_media_New(void) +{ + vlm_media_t *p_media = (vlm_media_t *)malloc( sizeof(vlm_media_t) ); + if( p_media ) + vlm_media_Init( p_media ); + return p_media; +} +static inline void vlm_media_Delete( vlm_media_t *p_media ) +{ + vlm_media_Clean( p_media ); + free( p_media ); +} +static inline vlm_media_t *vlm_media_Duplicate( vlm_media_t *p_src ) +{ + vlm_media_t *p_dst = vlm_media_New(); + if( p_dst ) + vlm_media_Copy( p_dst, p_src ); + return p_dst; +} + +/* media instance helpers */ +static inline void vlm_media_instance_Init( vlm_media_instance_t *p_instance ) +{ + memset( p_instance, 0, sizeof(vlm_media_instance_t) ); + p_instance->psz_name = NULL; + p_instance->i_time = 0; + p_instance->i_length = 0; + p_instance->d_position = 0.0; + p_instance->b_paused = false; + p_instance->i_rate = INPUT_RATE_DEFAULT; +} +static inline void vlm_media_instance_Clean( vlm_media_instance_t *p_instance ) +{ + free( p_instance->psz_name ); +} +static inline vlm_media_instance_t *vlm_media_instance_New(void) +{ + vlm_media_instance_t *p_instance = (vlm_media_instance_t *) malloc( sizeof(vlm_media_instance_t) ); + if( p_instance ) + vlm_media_instance_Init( p_instance ); + return p_instance; +} +static inline void vlm_media_instance_Delete( vlm_media_instance_t *p_instance ) +{ + vlm_media_instance_Clean( p_instance ); + free( p_instance ); +} + +#ifdef __cpluplus +} +#endif + +/**@}*/ + +#endif diff --git a/VLC/vlc_vod.h b/VLC/vlc_vod.h new file mode 100644 index 0000000..17cd2e4 --- /dev/null +++ b/VLC/vlc_vod.h @@ -0,0 +1,83 @@ +/***************************************************************************** + * vlc_vod.h: interface for VoD server modules + ***************************************************************************** + * Copyright (C) 2000, 2001 the VideoLAN team + * $Id$ + * + * Author: Gildas Bazin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_VOD_H +#define VLC_VOD_H 1 + +/** + * \file + * This file defines an interface for VOD server modules in vlc + */ + +/** + * \defgroup server Video On Demand (VOD) + * Video On Demand (VOD) functionality is provided from VLM. + * @{ + */ + +struct vod_t +{ + VLC_COMMON_MEMBERS + + /* Module properties */ + module_t *p_module; + vod_sys_t *p_sys; + + vod_media_t * (*pf_media_new) ( vod_t *, const char *, input_item_t * ); + void (*pf_media_del) ( vod_t *, vod_media_t * ); + int (*pf_media_add_es)( vod_t *, vod_media_t *, es_format_t * ); + void (*pf_media_del_es)( vod_t *, vod_media_t *, es_format_t * ); + + /* Owner properties */ + int (*pf_media_control) ( void *, vod_media_t *, const char *, int, va_list ); + void *p_data; +}; + +static inline int vod_MediaControl( vod_t *p_vod, vod_media_t *p_media, + const char *psz_id, int i_query, ... ) +{ + va_list args; + int i_result; + + if( !p_vod->pf_media_control ) return VLC_EGENERIC; + + va_start( args, i_query ); + i_result = p_vod->pf_media_control( p_vod->p_data, p_media, psz_id, + i_query, args ); + va_end( args ); + return i_result; +} + +enum vod_query_e +{ + VOD_MEDIA_PLAY, /* arg1= char * res= */ + VOD_MEDIA_PAUSE, /* arg1= res= */ + VOD_MEDIA_STOP, /* arg1= res=can fail */ + VOD_MEDIA_SEEK, /* arg1= double res= */ + VOD_MEDIA_REWIND, /* arg1= double res= */ + VOD_MEDIA_FORWARD, /* arg1= double res= */ +}; + +/**}*/ + +#endif diff --git a/VLC/vlc_vout.h b/VLC/vlc_vout.h new file mode 100644 index 0000000..e8955aa --- /dev/null +++ b/VLC/vlc_vout.h @@ -0,0 +1,732 @@ +/***************************************************************************** + * vlc_video.h: common video definitions + ***************************************************************************** + * Copyright (C) 1999 - 2008 the VideoLAN team + * $Id$ + * + * Authors: Vincent Seguin + * Samuel Hocevar + * Olivier Aubert + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_VOUT_H_ +#define VLC_VOUT_H_ 1 + +/** + * \file + * This file defines common video output structures and functions in vlc + */ + +#include "vlc_es.h" +#include "vlc_filter.h" + +/** Description of a planar graphic field */ +typedef struct plane_t +{ + uint8_t *p_pixels; /**< Start of the plane's data */ + + /* Variables used for fast memcpy operations */ + int i_lines; /**< Number of lines, including margins */ + int i_pitch; /**< Number of bytes in a line, including margins */ + + /** Size of a macropixel, defaults to 1 */ + int i_pixel_pitch; + + /* Variables used for pictures with margins */ + int i_visible_lines; /**< How many visible lines are there ? */ + int i_visible_pitch; /**< How many visible pixels are there ? */ + +} plane_t; + +/** + * Video picture + * + * Any picture destined to be displayed by a video output thread should be + * stored in this structure from it's creation to it's effective display. + * Picture type and flags should only be modified by the output thread. Note + * that an empty picture MUST have its flags set to 0. + */ +struct picture_t +{ + /** + * The properties of the picture + */ + video_frame_format_t format; + + /** Picture data - data can always be freely modified, but p_data may + * NEVER be modified. A direct buffer can be handled as the plugin + * wishes, it can even swap p_pixels buffers. */ + uint8_t *p_data; + void *p_data_orig; /**< pointer before memalign */ + plane_t p[ VOUT_MAX_PLANES ]; /**< description of the planes */ + int i_planes; /**< number of allocated planes */ + + /** \name Type and flags + * Should NOT be modified except by the vout thread + * @{*/ + int i_status; /**< picture flags */ + int i_type; /**< is picture a direct buffer ? */ + bool b_slow; /**< is picture in slow memory ? */ + int i_matrix_coefficients; /**< in YUV type, encoding type */ + /**@}*/ + + /** \name Picture management properties + * These properties can be modified using the video output thread API, + * but should never be written directly */ + /**@{*/ + unsigned i_refcount; /**< link reference counter */ + mtime_t date; /**< display date */ + bool b_force; + /**@}*/ + + /** \name Picture dynamic properties + * Those properties can be changed by the decoder + * @{ + */ + bool b_progressive; /**< is it a progressive frame ? */ + unsigned int i_nb_fields; /**< # of displayed fields */ + bool b_top_field_first; /**< which field is first */ + /**@}*/ + + /** The picture heap we are attached to */ + picture_heap_t* p_heap; + + /* Some vouts require the picture to be locked before it can be modified */ + int (* pf_lock) ( vout_thread_t *, picture_t * ); + int (* pf_unlock) ( vout_thread_t *, picture_t * ); + + /** Private data - the video output plugin might want to put stuff here to + * keep track of the picture */ + picture_sys_t * p_sys; + + /** This way the picture_Release can be overloaded */ + void (*pf_release)( picture_t * ); + + /** Next picture in a FIFO a pictures */ + struct picture_t *p_next; +}; + +/** + * This function will create a new picture. + * The picture created will implement a default release management compatible + * with picture_Yield and picture_Release. This default management will release + * picture_sys_t *p_sys field if non NULL. + */ +VLC_EXPORT( picture_t *, picture_New, ( vlc_fourcc_t i_chroma, int i_width, int i_height, int i_aspect ) ); + +/** + * This function will force the destruction a picture. + * The value of the picture reference count should be 0 before entering this + * function. + * Unless used for reimplementing pf_release, you should not use this + * function but picture_Release. + */ +VLC_EXPORT( void, picture_Delete, ( picture_t * ) ); + +/** + * This function will increase the picture reference count. + * It will not have any effect on picture obtained from vout + */ +static inline void picture_Yield( picture_t *p_picture ) +{ + if( p_picture->pf_release ) + p_picture->i_refcount++; +} +/** + * This function will release a picture. + * It will not have any effect on picture obtained from vout + */ +static inline void picture_Release( picture_t *p_picture ) +{ + /* FIXME why do we let pf_release handle the i_refcount ? */ + if( p_picture->pf_release ) + p_picture->pf_release( p_picture ); +} + +/** + * This function will copy all picture dynamic properties. + */ +static inline void picture_CopyProperties( picture_t *p_dst, const picture_t *p_src ) +{ + p_dst->date = p_src->date; + p_dst->b_force = p_src->b_force; + + p_dst->b_progressive = p_src->b_progressive; + p_dst->i_nb_fields = p_src->i_nb_fields; + p_dst->b_top_field_first = p_src->b_top_field_first; +} + +/** + * This function will copy the picture pixels. + * You can safely copy between pictures that do not have the same size, + * only the compatible(smaller) part will be copied. + */ +VLC_EXPORT( void, picture_CopyPixels, ( picture_t *p_dst, const picture_t *p_src ) ); +VLC_EXPORT( void, plane_CopyPixels, ( plane_t *p_dst, const plane_t *p_src ) ); + +/** + * This function will copy both picture dynamic properties and pixels. + * You have to notice that sometime a simple picture_Yield may do what + * you want without the copy overhead. + * Provided for convenience. + */ +static inline void picture_Copy( picture_t *p_dst, const picture_t *p_src ) +{ + picture_CopyPixels( p_dst, p_src ); + picture_CopyProperties( p_dst, p_src ); +} + +/** + * Video picture heap, either render (to store pictures used + * by the decoder) or output (to store pictures displayed by the vout plugin) + */ +struct picture_heap_t +{ + int i_pictures; /**< current heap size */ + + /* \name Picture static properties + * Those properties are fixed at initialization and should NOT be modified + * @{ + */ + unsigned int i_width; /**< picture width */ + unsigned int i_height; /**< picture height */ + vlc_fourcc_t i_chroma; /**< picture chroma */ + unsigned int i_aspect; /**< aspect ratio */ + /**@}*/ + + /* Real pictures */ + picture_t* pp_picture[VOUT_MAX_PICTURES]; /**< pictures */ + int i_last_used_pic; /**< last used pic in heap */ + bool b_allow_modify_pics; + + /* Stuff used for truecolor RGB planes */ + uint32_t i_rmask; int i_rrshift, i_lrshift; + uint32_t i_gmask; int i_rgshift, i_lgshift; + uint32_t i_bmask; int i_rbshift, i_lbshift; + + /** Stuff used for palettized RGB planes */ + void (* pf_setpalette) ( vout_thread_t *, uint16_t *, uint16_t *, uint16_t * ); +}; + +/***************************************************************************** + * Flags used to describe the status of a picture + *****************************************************************************/ + +/* Picture type */ +#define EMPTY_PICTURE 0 /* empty buffer */ +#define MEMORY_PICTURE 100 /* heap-allocated buffer */ +#define DIRECT_PICTURE 200 /* direct buffer */ + +/* Picture status */ +#define FREE_PICTURE 0 /* free and not allocated */ +#define RESERVED_PICTURE 1 /* allocated and reserved */ +#define RESERVED_DATED_PICTURE 2 /* waiting for DisplayPicture */ +#define RESERVED_DISP_PICTURE 3 /* waiting for a DatePicture */ +#define READY_PICTURE 4 /* ready for display */ +#define DISPLAYED_PICTURE 5 /* been displayed but is linked */ +#define DESTROYED_PICTURE 6 /* allocated but no more used */ + +/***************************************************************************** + * Shortcuts to access image components + *****************************************************************************/ + +/* Plane indices */ +#define Y_PLANE 0 +#define U_PLANE 1 +#define V_PLANE 2 +#define A_PLANE 3 + +/* Shortcuts */ +#define Y_PIXELS p[Y_PLANE].p_pixels +#define Y_PITCH p[Y_PLANE].i_pitch +#define U_PIXELS p[U_PLANE].p_pixels +#define U_PITCH p[U_PLANE].i_pitch +#define V_PIXELS p[V_PLANE].p_pixels +#define V_PITCH p[V_PLANE].i_pitch +#define A_PIXELS p[A_PLANE].p_pixels +#define A_PITCH p[A_PLANE].i_pitch + +/** + * \defgroup subpicture Video Subpictures + * Subpictures are pictures that should be displayed on top of the video, like + * subtitles and OSD + * \ingroup video_output + * @{ + */ + +/** + * Video subtitle region + * + * A subtitle region is defined by a picture (graphic) and its rendering + * coordinates. + * Subtitles contain a list of regions. + */ +struct subpicture_region_t +{ + video_format_t fmt; /**< format of the picture */ + picture_t picture; /**< picture comprising this region */ + + int i_x; /**< position of region */ + int i_y; /**< position of region */ + int i_align; /**< alignment within a region */ + int i_alpha; /**< transparency */ + + char *psz_text; /**< text string comprising this region */ + char *psz_html; /**< HTML version of subtitle (NULL = use psz_text) */ + text_style_t *p_style; /**< a description of the text style formatting */ + + subpicture_region_t *p_next; /**< next region in the list */ + subpicture_region_t *p_cache; /**< modified version of this region */ +}; + +/** + * Video subtitle + * + * Any subtitle destined to be displayed by a video output thread should + * be stored in this structure from it's creation to it's effective display. + * Subtitle type and flags should only be modified by the output thread. Note + * that an empty subtitle MUST have its flags set to 0. + */ +struct subpicture_t +{ + /** \name Channel ID */ + /**@{*/ + int i_channel; /**< subpicture channel ID */ + /**@}*/ + + /** \name Type and flags + Should NOT be modified except by the vout thread */ + /**@{*/ + int i_type; /**< type */ + int i_status; /**< flags */ + subpicture_t * p_next; /**< next subtitle to be displayed */ + /**@}*/ + + /** \name Date properties */ + /**@{*/ + mtime_t i_start; /**< beginning of display date */ + mtime_t i_stop; /**< end of display date */ + bool b_ephemer; /**< If this flag is set to true the subtitle + will be displayed untill the next one appear */ + bool b_fade; /**< enable fading */ + bool b_pausable; /**< subpicture will be paused if + stream is paused */ + /**@}*/ + + subpicture_region_t *p_region; /**< region list composing this subtitle */ + + /** \name Display properties + * These properties are only indicative and may be + * changed by the video output thread, or simply ignored depending of the + * subtitle type. */ + /**@{*/ + int i_x; /**< offset from alignment position */ + int i_y; /**< offset from alignment position */ + int i_width; /**< picture width */ + int i_height; /**< picture height */ + int i_alpha; /**< transparency */ + int i_original_picture_width; /**< original width of the movie */ + int i_original_picture_height;/**< original height of the movie */ + bool b_absolute; /**< position is absolute */ + int i_flags; /**< position flags */ + /**@}*/ + + /** Pointer to function that renders this subtitle in a picture */ + void ( *pf_render ) ( vout_thread_t *, picture_t *, const subpicture_t * ); + /** Pointer to function that cleans up the private data of this subtitle */ + void ( *pf_destroy ) ( subpicture_t * ); + + /** Pointer to functions for region management */ + subpicture_region_t * ( *pf_create_region ) ( vlc_object_t *, + video_format_t * ); + subpicture_region_t * ( *pf_make_region ) ( vlc_object_t *, + video_format_t *, picture_t * ); + void ( *pf_destroy_region ) ( vlc_object_t *, subpicture_region_t * ); + + void ( *pf_pre_render ) ( video_format_t *, spu_t *, subpicture_t * ); + void ( *pf_update_regions ) ( video_format_t *, spu_t *, + subpicture_t *, mtime_t ); + + /** Private data - the subtitle plugin might want to put stuff here to + * keep track of the subpicture */ + subpicture_sys_t *p_sys; /* subpicture data */ +}; + +/* Subpicture type */ +#define EMPTY_SUBPICTURE 0 /* subtitle slot is empty and available */ +#define MEMORY_SUBPICTURE 100 /* subpicture stored in memory */ + +/* Default subpicture channel ID */ +#define DEFAULT_CHAN 1 + +/* Subpicture status */ +#define FREE_SUBPICTURE 0 /* free and not allocated */ +#define RESERVED_SUBPICTURE 1 /* allocated and reserved */ +#define READY_SUBPICTURE 2 /* ready for display */ + +/* Subpicture position flags */ +#define SUBPICTURE_ALIGN_LEFT 0x1 +#define SUBPICTURE_ALIGN_RIGHT 0x2 +#define SUBPICTURE_ALIGN_TOP 0x4 +#define SUBPICTURE_ALIGN_BOTTOM 0x8 +#define SUBPICTURE_ALIGN_MASK ( SUBPICTURE_ALIGN_LEFT|SUBPICTURE_ALIGN_RIGHT| \ + SUBPICTURE_ALIGN_TOP |SUBPICTURE_ALIGN_BOTTOM ) + +/* Subpicture rendered flag - should only be used by vout_subpictures */ +#define SUBPICTURE_RENDERED 0x10 + +/***************************************************************************** + * Prototypes + *****************************************************************************/ + +/** + * Copy the source picture onto the destination picture. + * \param p_this a vlc object + * \param p_dst pointer to the destination picture. + * \param p_src pointer to the source picture. + */ +#define vout_CopyPicture(a,b,c) __vout_CopyPicture(VLC_OBJECT(a),b,c) +VLC_EXPORT( void, __vout_CopyPicture, ( vlc_object_t *p_this, picture_t *p_dst, picture_t *p_src ) ); + +/** + * Initialise different fields of a picture_t (but does not allocate memory). + * \param p_this a vlc object + * \param p_pic pointer to the picture structure. + * \param i_chroma the wanted chroma for the picture. + * \param i_width the wanted width for the picture. + * \param i_height the wanted height for the picture. + * \param i_aspect the wanted aspect ratio for the picture. + */ +#define vout_InitPicture(a,b,c,d,e,f) \ + __vout_InitPicture(VLC_OBJECT(a),b,c,d,e,f) +VLC_EXPORT( int, __vout_InitPicture, ( vlc_object_t *p_this, picture_t *p_pic, uint32_t i_chroma, int i_width, int i_height, int i_aspect ) ); + +/** + * Initialise different fields of a picture_t and allocates the picture buffer. + * \param p_this a vlc object + * \param p_pic pointer to the picture structure. + * \param i_chroma the wanted chroma for the picture. + * \param i_width the wanted width for the picture. + * \param i_height the wanted height for the picture. + * \param i_aspect the wanted aspect ratio for the picture. + */ +#define vout_AllocatePicture(a,b,c,d,e,f) \ + __vout_AllocatePicture(VLC_OBJECT(a),b,c,d,e,f) +VLC_EXPORT( int, __vout_AllocatePicture,( vlc_object_t *p_this, picture_t *p_pic, uint32_t i_chroma, int i_width, int i_height, int i_aspect ) ); + + +/** + * \defgroup video_output Video Output + * This module describes the programming interface for video output threads. + * It includes functions allowing to open a new thread, send pictures to a + * thread, and destroy a previously opened video output thread. + * @{ + */ + +/** + * Video output thread descriptor + * + * Any independent video output device, such as an X11 window or a GGI device, + * is represented by a video output thread, and described using the following + * structure. + */ +struct vout_thread_t +{ + VLC_COMMON_MEMBERS + + /** \name Thread properties and locks */ + /**@{*/ + vlc_mutex_t picture_lock; /**< picture heap lock */ + vlc_mutex_t subpicture_lock; /**< subpicture heap lock */ + vlc_mutex_t change_lock; /**< thread change lock */ + vlc_mutex_t vfilter_lock; /**< video filter2 change lock */ + vout_sys_t * p_sys; /**< system output method */ + /**@}*/ + + /** \name Current display properties */ + /**@{*/ + uint16_t i_changes; /**< changes made to the thread. + \see \ref vout_changes */ + float f_gamma; /**< gamma */ + bool b_grayscale; /**< color or grayscale display */ + bool b_info; /**< print additional information */ + bool b_interface; /**< render interface */ + bool b_scale; /**< allow picture scaling */ + bool b_fullscreen; /**< toogle fullscreen display */ + uint32_t render_time; /**< last picture render time */ + unsigned int i_window_width; /**< video window width */ + unsigned int i_window_height; /**< video window height */ + unsigned int i_alignment; /**< video alignment in window */ + unsigned int i_par_num; /**< monitor pixel aspect-ratio */ + unsigned int i_par_den; /**< monitor pixel aspect-ratio */ + + struct vout_window_t *p_window; /**< window for embedded vout (if any) */ + /**@}*/ + + /** \name Plugin used and shortcuts to access its capabilities */ + /**@{*/ + module_t * p_module; + int ( *pf_init ) ( vout_thread_t * ); + void ( *pf_end ) ( vout_thread_t * ); + int ( *pf_manage ) ( vout_thread_t * ); + void ( *pf_render ) ( vout_thread_t *, picture_t * ); + void ( *pf_display ) ( vout_thread_t *, picture_t * ); + void ( *pf_swap ) ( vout_thread_t * ); /* OpenGL only */ + int ( *pf_lock ) ( vout_thread_t * ); /* OpenGL only */ + void ( *pf_unlock ) ( vout_thread_t * ); /* OpenGL only */ + int ( *pf_control ) ( vout_thread_t *, int, va_list ); + /**@}*/ + + /** \name Statistics + * These numbers are not supposed to be accurate, but are a + * good indication of the thread status */ + /**@{*/ + count_t c_fps_samples; /**< picture counts */ + mtime_t p_fps_sample[VOUT_FPS_SAMPLES]; /**< FPS samples dates */ + /**@}*/ + + /** \name Video heap and translation tables */ + /**@{*/ + int i_heap_size; /**< heap size */ + picture_heap_t render; /**< rendered pictures */ + picture_heap_t output; /**< direct buffers */ + bool b_direct; /**< rendered are like direct ? */ + filter_t *p_chroma; /**< translation tables */ + + video_format_t fmt_render; /* render format (from the decoder) */ + video_format_t fmt_in; /* input (modified render) format */ + video_format_t fmt_out; /* output format (for the video output) */ + /**@}*/ + + /* Picture heap */ + picture_t p_picture[2*VOUT_MAX_PICTURES+1]; /**< pictures */ + + /* Subpicture unit */ + spu_t *p_spu; + + /* Statistics */ + count_t c_loops; + count_t c_pictures, c_late_pictures; + mtime_t display_jitter; /**< average deviation from the PTS */ + count_t c_jitter_samples; /**< number of samples used + for the calculation of the + jitter */ + /** delay created by internal caching */ + int i_pts_delay; + + /* Filter chain */ + char *psz_filter_chain; + bool b_filter_change; + + /* Video filter2 chain */ + filter_chain_t *p_vf2_chain; + char *psz_vf2; + + /* Misc */ + bool b_snapshot; /**< take one snapshot on the next loop */ + + /* Video output configuration */ + config_chain_t *p_cfg; + + /* Show media title on videoutput */ + bool b_title_show; + mtime_t i_title_timeout; + int i_title_position; +}; + +#define I_OUTPUTPICTURES p_vout->output.i_pictures +#define PP_OUTPUTPICTURE p_vout->output.pp_picture +#define I_RENDERPICTURES p_vout->render.i_pictures +#define PP_RENDERPICTURE p_vout->render.pp_picture + +/** \defgroup vout_changes Flags for changes + * These flags are set in the vout_thread_t::i_changes field when another + * thread changed a variable + * @{ + */ +/** b_info changed */ +#define VOUT_INFO_CHANGE 0x0001 +/** b_grayscale changed */ +#define VOUT_GRAYSCALE_CHANGE 0x0002 +/** b_interface changed */ +#define VOUT_INTF_CHANGE 0x0004 +/** b_scale changed */ +#define VOUT_SCALE_CHANGE 0x0008 +/** gamma changed */ +#define VOUT_GAMMA_CHANGE 0x0010 +/** b_cursor changed */ +#define VOUT_CURSOR_CHANGE 0x0020 +/** b_fullscreen changed */ +#define VOUT_FULLSCREEN_CHANGE 0x0040 +/** size changed */ +#define VOUT_SIZE_CHANGE 0x0200 +/** depth changed */ +#define VOUT_DEPTH_CHANGE 0x0400 +/** change chroma tables */ +#define VOUT_CHROMA_CHANGE 0x0800 +/** cropping parameters changed */ +#define VOUT_CROP_CHANGE 0x1000 +/** aspect ratio changed */ +#define VOUT_ASPECT_CHANGE 0x2000 +/** change/recreate picture buffers */ +#define VOUT_PICTURE_BUFFERS_CHANGE 0x4000 +/**@}*/ + +/* Alignment flags */ +#define VOUT_ALIGN_LEFT 0x0001 +#define VOUT_ALIGN_RIGHT 0x0002 +#define VOUT_ALIGN_HMASK 0x0003 +#define VOUT_ALIGN_TOP 0x0004 +#define VOUT_ALIGN_BOTTOM 0x0008 +#define VOUT_ALIGN_VMASK 0x000C + +#define MAX_JITTER_SAMPLES 20 + +/***************************************************************************** + * Prototypes + *****************************************************************************/ + +/** + * This function will + * - returns a suitable vout (if requested by a non NULL p_fmt) + * - recycles an old vout (if given) by either destroying it or by saving it + * for latter usage. + * + * The purpose of this function is to avoid unnecessary creation/destruction of + * vout (and to allow optional vout reusing). + * + * You can call vout_Request on a vout created by vout_Create or by a previous + * call to vout_Request. + * You can release the returned value either by vout_Request or vout_Close() + * followed by a vlc_object_release() or shorter vout_CloseAndRelease() + * + * \param p_this a vlc object + * \param p_vout a vout candidate + * \param p_fmt the video format requested or NULL + * \return a vout if p_fmt is non NULL and the request is successfull, NULL + * otherwise + */ +#define vout_Request(a,b,c) __vout_Request(VLC_OBJECT(a),b,c) +VLC_EXPORT( vout_thread_t *, __vout_Request, ( vlc_object_t *p_this, vout_thread_t *p_vout, video_format_t *p_fmt ) ); + +/** + * This function will create a suitable vout for a given p_fmt. It will never + * reuse an already existing unused vout. + * + * You have to call either vout_Close or vout_Request on the returned value + * \param p_this a vlc object to which the returned vout will be attached + * \param p_fmt the video format requested + * \return a vout if the request is successfull, NULL otherwise + */ +#define vout_Create(a,b) __vout_Create(VLC_OBJECT(a),b) +VLC_EXPORT( vout_thread_t *, __vout_Create, ( vlc_object_t *p_this, video_format_t *p_fmt ) ); + +/** + * This function will close a vout created by vout_Create or vout_Request. + * The associated vout module is closed. + * Note: It is not released yet, you'll have to call vlc_object_release() + * or use the convenient vout_CloseAndRelease(). + * + * \param p_vout the vout to close + */ +VLC_EXPORT( void, vout_Close, ( vout_thread_t *p_vout ) ); + +/** + * This function will close a vout created by vout_Create + * and then release it. + * + * \param p_vout the vout to close and release + */ +static inline void vout_CloseAndRelease( vout_thread_t *p_vout ) +{ + vout_Close( p_vout ); + vlc_object_release( p_vout ); +} + +/* */ +VLC_EXPORT( int, vout_ChromaCmp, ( uint32_t, uint32_t ) ); + +VLC_EXPORT( picture_t *, vout_CreatePicture, ( vout_thread_t *, bool, bool, unsigned int ) ); +VLC_EXPORT( void, vout_InitFormat, ( video_frame_format_t *, uint32_t, int, int, int ) ); +VLC_EXPORT( void, vout_DestroyPicture, ( vout_thread_t *, picture_t * ) ); +VLC_EXPORT( void, vout_DisplayPicture, ( vout_thread_t *, picture_t * ) ); +VLC_EXPORT( void, vout_DatePicture, ( vout_thread_t *, picture_t *, mtime_t ) ); +VLC_EXPORT( void, vout_LinkPicture, ( vout_thread_t *, picture_t * ) ); +VLC_EXPORT( void, vout_UnlinkPicture, ( vout_thread_t *, picture_t * ) ); +VLC_EXPORT( void, vout_PlacePicture, ( vout_thread_t *, unsigned int, unsigned int, unsigned int *, unsigned int *, unsigned int *, unsigned int * ) ); + +/* DO NOT use vout_RenderPicture unless you are in src/video_ouput */ +picture_t * vout_RenderPicture ( vout_thread_t *, picture_t *, + subpicture_t * ); + +/* DO NOT use vout_CountPictureAvailable unless your are in src/input/dec.c (no exception) */ +int vout_CountPictureAvailable( vout_thread_t * ); + +VLC_EXPORT( int, vout_vaControlDefault, ( vout_thread_t *, int, va_list ) ); +VLC_EXPORT( void *, vout_RequestWindow, ( vout_thread_t *, int *, int *, unsigned int *, unsigned int * ) ); +VLC_EXPORT( void, vout_ReleaseWindow, ( vout_thread_t *, void * ) ); +VLC_EXPORT( int, vout_ControlWindow, ( vout_thread_t *, void *, int, va_list ) ); +void vout_IntfInit( vout_thread_t * ); +VLC_EXPORT( int, vout_Snapshot, ( vout_thread_t *p_vout, picture_t *p_pic ) ); +VLC_EXPORT( void, vout_EnableFilter, ( vout_thread_t *, char *,bool , bool ) ); + + +static inline int vout_vaControl( vout_thread_t *p_vout, int i_query, + va_list args ) +{ + if( p_vout->pf_control ) + return p_vout->pf_control( p_vout, i_query, args ); + else + return VLC_EGENERIC; +} + +static inline int vout_Control( vout_thread_t *p_vout, int i_query, ... ) +{ + va_list args; + int i_result; + + va_start( args, i_query ); + i_result = vout_vaControl( p_vout, i_query, args ); + va_end( args ); + return i_result; +} + +enum output_query_e +{ + VOUT_GET_SIZE, /* arg1= unsigned int*, arg2= unsigned int*, res= */ + VOUT_SET_SIZE, /* arg1= unsigned int, arg2= unsigned int, res= */ + VOUT_SET_STAY_ON_TOP, /* arg1= bool res= */ + VOUT_REPARENT, + VOUT_SNAPSHOT, + VOUT_CLOSE, + VOUT_SET_FOCUS, /* arg1= bool res= */ + VOUT_SET_VIEWPORT, /* arg1= view rect, arg2=clip rect, res= */ + VOUT_REDRAW_RECT, /* arg1= area rect, res= */ +}; + +typedef struct snapshot_t { + char *p_data; /* Data area */ + + int i_width; /* In pixels */ + int i_height; /* In pixels */ + int i_datasize; /* In bytes */ + mtime_t date; /* Presentation time */ +} snapshot_t; + +/**@}*/ + +#endif /* _VLC_VIDEO_H */ diff --git a/VLC/vlc_window.h b/VLC/vlc_window.h new file mode 100644 index 0000000..9e95a6b --- /dev/null +++ b/VLC/vlc_window.h @@ -0,0 +1,49 @@ +/***************************************************************************** + * vlc_window.h: Embedded video output window + ***************************************************************************** + * Copyright (C) 2008 Rémi Denis-Courmont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef LIBVLCCORE_WINDOW_H +# define LIBVLCCORE_WINDOW_H 1 + +/** + * \file + * This file defines functions and structures for output windows + */ + +# include + +typedef struct vout_window_t vout_window_t; + +struct vout_window_t +{ + VLC_COMMON_MEMBERS + + module_t *module; + vout_thread_t *vout; + void *handle; /* OS-specific Window handle */ + + unsigned width; /* pixels width */ + unsigned height; /* pixels height */ + int pos_x; /* horizontal position hint */ + int pos_y; /* vertical position hint */ + + int (*control) (struct vout_window_t *, int, va_list); +}; + +#endif /* !LIBVLCCORE_WINDOW_H */ diff --git a/VLC/vlc_xml.h b/VLC/vlc_xml.h new file mode 100644 index 0000000..20b0970 --- /dev/null +++ b/VLC/vlc_xml.h @@ -0,0 +1,92 @@ +/***************************************************************************** + * xml.h: XML abstraction layer + ***************************************************************************** + * Copyright (C) 2004 the VideoLAN team + * $Id$ + * + * Author: Gildas Bazin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifndef VLC_XML_H +#define VLC_XML_H + +/** + * \file + * This file defines functions and structures to handle xml tags in vlc + * + */ + +# ifdef __cplusplus +extern "C" { +# endif + +struct xml_t +{ + VLC_COMMON_MEMBERS + + /* Module properties */ + module_t *p_module; + xml_sys_t *p_sys; + + xml_reader_t * (*pf_reader_create) ( xml_t *, stream_t * ); + void (*pf_reader_delete) ( xml_reader_t * ); + + void (*pf_catalog_load) ( xml_t *, const char * ); + void (*pf_catalog_add) ( xml_t *, const char *, const char *, + const char * ); +}; + +#define xml_Create( a ) __xml_Create( VLC_OBJECT(a) ) +VLC_EXPORT( xml_t *, __xml_Create, ( vlc_object_t * ) ); +VLC_EXPORT( void, xml_Delete, ( xml_t * ) ); + +#define xml_ReaderCreate( a, b ) a->pf_reader_create( a, b ) +#define xml_ReaderDelete( a, b ) a->pf_reader_delete( b ) +#define xml_CatalogLoad( a, b ) a->pf_catalog_load( a, b ) +#define xml_CatalogAdd( a, b, c, d ) a->pf_catalog_add( a, b, c, d ) + +struct xml_reader_t +{ + xml_t *p_xml; + xml_reader_sys_t *p_sys; + + int (*pf_read) ( xml_reader_t * ); + int (*pf_node_type) ( xml_reader_t * ); + char * (*pf_name) ( xml_reader_t * ); + char * (*pf_value) ( xml_reader_t * ); + int (*pf_next_attr) ( xml_reader_t * ); + + int (*pf_use_dtd) ( xml_reader_t *, bool ); +}; + +#define xml_ReaderRead( a ) a->pf_read( a ) +#define xml_ReaderNodeType( a ) a->pf_node_type( a ) +#define xml_ReaderName( a ) a->pf_name( a ) +#define xml_ReaderValue( a ) a->pf_value( a ) +#define xml_ReaderNextAttr( a ) a->pf_next_attr( a ) +#define xml_ReaderUseDTD( a, b ) a->pf_use_dtd( a, b ) + +#define XML_READER_NONE 0 +#define XML_READER_STARTELEM 1 +#define XML_READER_ENDELEM 2 +#define XML_READER_TEXT 3 + +# ifdef __cplusplus +} +# endif + +#endif diff --git a/VLC/vlm.c b/VLC/vlm.c new file mode 100644 index 0000000..5fe5468 --- /dev/null +++ b/VLC/vlm.c @@ -0,0 +1,518 @@ +/***************************************************************************** + * vlm.c: libvlc new API VLM handling functions + ***************************************************************************** + * Copyright (C) 2005 the VideoLAN team + * $Id$ + * + * Authors: Clément Stenac + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#include "libvlc_internal.h" + +#include +#include +#include +#include + +#if 0 +/* local function to be used in libvlc_vlm_show_media only */ +static char* recurse_answer( char* psz_prefix, vlm_message_t *p_answer ) { + char* psz_childprefix; + char* psz_response=""; + char* response_tmp; + int i; + vlm_message_t *aw_child, **paw_child; + + asprintf( &psz_childprefix, "%s%s.", psz_prefix, p_answer->psz_name ); + + if ( p_answer->i_child ) + { + paw_child = p_answer->child; + aw_child = *( paw_child ); + for( i = 0; i < p_answer->i_child; i++ ) + { + asprintf( &response_tmp, "%s%s%s:%s\n", + psz_response, psz_prefix, aw_child->psz_name, + aw_child->psz_value ); + free( psz_response ); + psz_response = response_tmp; + if ( aw_child->i_child ) + { + asprintf(&response_tmp, "%s%s", psz_response, + recurse_answer(psz_childprefix, aw_child)); + free( psz_response ); + psz_response = response_tmp; + } + paw_child++; + aw_child = *( paw_child ); + } + } + free( psz_childprefix ); + return psz_response; +} + +char* libvlc_vlm_show_media( libvlc_instance_t *p_instance, char *psz_name, + libvlc_exception_t *p_exception ) +{ + char *psz_message; + vlm_message_t *answer; + char *psz_response; + + CHECK_VLM; +#ifdef ENABLE_VLM + asprintf( &psz_message, "show %s", psz_name ); + asprintf( &psz_response, "", psz_name ); + vlm_ExecuteCommand( p_instance->p_vlm, psz_message, &answer ); + if( answer->psz_value ) + { + libvlc_exception_raise( p_exception, "Unable to call show %s: %s", + psz_name, answer->psz_value ); + } + else + { + if ( answer->child ) + { + psz_response = recurse_answer( "", answer ); + } + } + free( psz_message ); + return(psz_response ); +#else + libvlc_exception_raise( p_exception, "VLM has been disabled in this libvlc." ); + return NULL; +#endif +} +#endif /* 0 */ + +static int libvlc_vlm_init( libvlc_instance_t *p_instance, + libvlc_exception_t *p_exception ) +{ +#ifdef ENABLE_VLM + if( !p_instance->p_vlm ) + p_instance->p_vlm = vlm_New( p_instance->p_libvlc_int ); +#else + libvlc_exception_raise( p_exception, "VLM has been disabled in this libvlc." ); + return VLC_EGENERIC; +#endif + + if( !p_instance->p_vlm ) + { + libvlc_exception_raise( p_exception, + "Unable to create VLM." ); + return VLC_EGENERIC; + } + return VLC_SUCCESS; +} +#define VLM_RET(p,ret) do { \ + if( libvlc_vlm_init( p_instance, p_exception ) ) return ret;\ + (p) = p_instance->p_vlm; \ + } while(0) +#define VLM(p) VLM_RET(p,) + +static vlm_media_instance_t *libvlc_vlm_get_media_instance( libvlc_instance_t *p_instance, + char *psz_name, int i_minstance_idx, + libvlc_exception_t *p_exception ) +{ +#ifdef ENABLE_VLM + vlm_t *p_vlm; + vlm_media_instance_t **pp_minstance; + vlm_media_instance_t *p_minstance; + int i_minstance; + int64_t id; + + VLM_RET(p_vlm, NULL); + + if( vlm_Control( p_vlm, VLM_GET_MEDIA_ID, psz_name, &id ) || + vlm_Control( p_vlm, VLM_GET_MEDIA_INSTANCES, id, &pp_minstance, &i_minstance ) ) + { + libvlc_exception_raise( p_exception, "Unable to get %s instances", psz_name ); + return NULL; + } + p_minstance = NULL; + if( i_minstance_idx >= 0 && i_minstance_idx < i_minstance ) + { + p_minstance = pp_minstance[i_minstance_idx]; + TAB_REMOVE( i_minstance, pp_minstance, p_minstance ); + } + while( i_minstance > 0 ) + vlm_media_instance_Delete( pp_minstance[--i_minstance] ); + TAB_CLEAN( i_minstance, pp_minstance ); + return p_minstance; +#else + libvlc_exception_raise( p_exception, "VLM has been disabled in this libvlc." ); + return VLC_EGENERIC; +#endif +} + + +void libvlc_vlm_release( libvlc_instance_t *p_instance, libvlc_exception_t *p_exception) +{ +#ifdef ENABLE_VLM + vlm_t *p_vlm; + + VLM(p_vlm); + + vlm_Delete( p_vlm ); +#else + libvlc_exception_raise( p_exception, "VLM has been disabled in this libvlc." ); + return VLC_EGENERIC; +#endif +} + + +void libvlc_vlm_add_broadcast( libvlc_instance_t *p_instance, char *psz_name, + char *psz_input, char *psz_output, + int i_options, char **ppsz_options, + int b_enabled, int b_loop, + libvlc_exception_t *p_exception ) +{ +#ifdef ENABLE_VLM + vlm_t *p_vlm; + vlm_media_t m; + int n; + + VLM(p_vlm); + + vlm_media_Init( &m ); + m.psz_name = strdup( psz_name ); + m.b_enabled = b_enabled; + m.b_vod = false; + m.broadcast.b_loop = b_loop; + if( psz_input ) + TAB_APPEND( m.i_input, m.ppsz_input, strdup(psz_input) ); + if( psz_output ) + m.psz_output = strdup( psz_output ); + for( n = 0; n < i_options; n++ ) + TAB_APPEND( m.i_option, m.ppsz_option, strdup(ppsz_options[n]) ); + + if( vlm_Control( p_vlm, VLM_ADD_MEDIA, &m, NULL ) ) + { + vlm_media_Clean( &m ); + libvlc_exception_raise( p_exception, "Media %s creation failed", psz_name ); + } + vlm_media_Clean( &m ); +#else + libvlc_exception_raise( p_exception, "VLM has been disabled in this libvlc." ); + return VLC_EGENERIC; +#endif +} + +void libvlc_vlm_add_vod( libvlc_instance_t *p_instance, char *psz_name, + char *psz_input, int i_options, + char **ppsz_options, int b_enabled, + char *psz_mux, libvlc_exception_t *p_exception ) +{ +#ifdef ENABLE_VLM + vlm_t *p_vlm; + vlm_media_t m; + int n; + + VLM(p_vlm); + + vlm_media_Init( &m ); + m.psz_name = strdup( psz_name ); + m.b_enabled = b_enabled; + m.b_vod = true; + m.vod.psz_mux = psz_mux ? strdup( psz_mux ) : NULL; + if( psz_input ) + TAB_APPEND( m.i_input, m.ppsz_input, strdup(psz_input) ); + for( n = 0; n < i_options; n++ ) + TAB_APPEND( m.i_option, m.ppsz_option, strdup(ppsz_options[n]) ); + + if( vlm_Control( p_vlm, VLM_ADD_MEDIA, &m, NULL ) ) + { + vlm_media_Clean( &m ); + libvlc_exception_raise( p_exception, "Media %s creation failed", psz_name ); + } + vlm_media_Clean( &m ); +#else + libvlc_exception_raise( p_exception, "VLM has been disabled in this libvlc." ); + return VLC_EGENERIC; +#endif +} + +void libvlc_vlm_del_media( libvlc_instance_t *p_instance, char *psz_name, + libvlc_exception_t *p_exception ) +{ +#ifdef ENABLE_VLM + vlm_t *p_vlm; + int64_t id; + + VLM(p_vlm); + + if( vlm_Control( p_vlm, VLM_GET_MEDIA_ID, psz_name, &id ) || + vlm_Control( p_vlm, VLM_DEL_MEDIA, id ) ) + { + libvlc_exception_raise( p_exception, "Unable to delete %s", psz_name ); + } +#else + libvlc_exception_raise( p_exception, "VLM has been disabled in this libvlc." ); + return VLC_EGENERIC; +#endif +} + +#define VLM_CHANGE(psz_error, code ) do { \ + vlm_media_t *p_media; \ + vlm_t *p_vlm; \ + int64_t id; \ + VLM(p_vlm); \ + if( vlm_Control( p_vlm, VLM_GET_MEDIA_ID, psz_name, &id ) || \ + vlm_Control( p_vlm, VLM_GET_MEDIA, id, &p_media ) ) { \ + libvlc_exception_raise( p_exception, psz_error, psz_name ); \ + return; \ + } \ + if( !p_media ) goto error; \ + \ + code; \ + \ + if( vlm_Control( p_vlm, VLM_CHANGE_MEDIA, p_media ) ) { \ + vlm_media_Delete( p_media ); \ + goto error; \ + } \ + vlm_media_Delete( p_media ); \ + return; \ + error: \ + libvlc_exception_raise( p_exception, psz_error, psz_name );\ + } while(0) + +void libvlc_vlm_set_enabled( libvlc_instance_t *p_instance, char *psz_name, + int b_enabled, libvlc_exception_t *p_exception ) +{ +#ifdef ENABLE_VLM +#define VLM_CHANGE_CODE { p_media->b_enabled = b_enabled; } + VLM_CHANGE( "Unable to delete %s", VLM_CHANGE_CODE ); +#undef VLM_CHANGE_CODE +#else + libvlc_exception_raise( p_exception, "VLM has been disabled in this libvlc." ); + return VLC_EGENERIC; +#endif +} + +void libvlc_vlm_set_loop( libvlc_instance_t *p_instance, char *psz_name, + int b_loop, libvlc_exception_t *p_exception ) +{ +#ifdef ENABLE_VLM +#define VLM_CHANGE_CODE { p_media->broadcast.b_loop = b_loop; } + VLM_CHANGE( "Unable to change %s loop property", VLM_CHANGE_CODE ); +#undef VLM_CHANGE_CODE +#else + libvlc_exception_raise( p_exception, "VLM has been disabled in this libvlc." ); + return VLC_EGENERIC; +#endif +} + +void libvlc_vlm_set_mux( libvlc_instance_t *p_instance, char *psz_name, + char *psz_mux, libvlc_exception_t *p_exception ) +{ +#ifdef ENABLE_VLM +#define VLM_CHANGE_CODE { if( p_media->b_vod ) { \ + free( p_media->vod.psz_mux ); \ + p_media->vod.psz_mux = psz_mux ? strdup( psz_mux ) : NULL; \ + } } + VLM_CHANGE( "Unable to change %s mux property", VLM_CHANGE_CODE ); +#undef VLM_CHANGE_CODE +#else + libvlc_exception_raise( p_exception, "VLM has been disabled in this libvlc." ); + return VLC_EGENERIC; +#endif +} + +void libvlc_vlm_set_output( libvlc_instance_t *p_instance, char *psz_name, + char *psz_output, libvlc_exception_t *p_exception ) +{ +#ifdef ENABLE_VLM +#define VLM_CHANGE_CODE { free( p_media->psz_output ); \ + p_media->psz_output = strdup( psz_output ); } + VLM_CHANGE( "Unable to change %s output property", VLM_CHANGE_CODE ); +#undef VLM_CHANGE_CODE +#else + libvlc_exception_raise( p_exception, "VLM has been disabled in this libvlc." ); + return VLC_EGENERIC; +#endif +} + +void libvlc_vlm_set_input( libvlc_instance_t *p_instance, char *psz_name, + char *psz_input, libvlc_exception_t *p_exception ) +{ +#ifdef ENABLE_VLM +#define VLM_CHANGE_CODE { while( p_media->i_input > 0 ) \ + free( p_media->ppsz_input[--p_media->i_input] );\ + TAB_APPEND( p_media->i_input, p_media->ppsz_input, strdup(psz_input) ); } + VLM_CHANGE( "Unable to change %s input property", VLM_CHANGE_CODE ); +#undef VLM_CHANGE_CODE +#else + libvlc_exception_raise( p_exception, "VLM has been disabled in this libvlc." ); + return VLC_EGENERIC; +#endif +} + +void libvlc_vlm_add_input( libvlc_instance_t *p_instance, char *psz_name, + char *psz_input, libvlc_exception_t *p_exception ) +{ +#ifdef ENABLE_VLM +#define VLM_CHANGE_CODE { TAB_APPEND( p_media->i_input, p_media->ppsz_input, strdup(psz_input) ); } + VLM_CHANGE( "Unable to change %s input property", VLM_CHANGE_CODE ); +#undef VLM_CHANGE_CODE +#else + libvlc_exception_raise( p_exception, "VLM has been disabled in this libvlc." ); + return VLC_EGENERIC; +#endif +} + +void libvlc_vlm_change_media( libvlc_instance_t *p_instance, char *psz_name, + char *psz_input, char *psz_output, int i_options, + char **ppsz_options, int b_enabled, int b_loop, + libvlc_exception_t *p_exception ) +{ +#ifdef ENABLE_VLM +#define VLM_CHANGE_CODE { int n; \ + p_media->b_enabled = b_enabled; \ + p_media->broadcast.b_loop = b_loop; \ + while( p_media->i_input > 0 ) \ + free( p_media->ppsz_input[--p_media->i_input] ); \ + if( psz_input ) \ + TAB_APPEND( p_media->i_input, p_media->ppsz_input, strdup(psz_input) ); \ + free( p_media->psz_output ); \ + p_media->psz_output = psz_output ? strdup( psz_output ) : NULL; \ + while( p_media->i_option > 0 ) \ + free( p_media->ppsz_option[--p_media->i_option] ); \ + for( n = 0; n < i_options; n++ ) \ + TAB_APPEND( p_media->i_option, p_media->ppsz_option, strdup(ppsz_options[n]) ); \ + } + VLM_CHANGE( "Unable to change %s properties", VLM_CHANGE_CODE ); +#undef VLM_CHANGE_CODE +#else + libvlc_exception_raise( p_exception, "VLM has been disabled in this libvlc." ); + return VLC_EGENERIC; +#endif +} + +void libvlc_vlm_play_media( libvlc_instance_t *p_instance, char *psz_name, + libvlc_exception_t *p_exception ) +{ +#ifdef ENABLE_VLM + vlm_t *p_vlm; + int64_t id; + + VLM(p_vlm); + + if( vlm_Control( p_vlm, VLM_GET_MEDIA_ID, psz_name, &id ) || + vlm_Control( p_vlm, VLM_START_MEDIA_BROADCAST_INSTANCE, id, NULL, 0 ) ) + { + libvlc_exception_raise( p_exception, "Unable to play %s", psz_name ); + } +#else + libvlc_exception_raise( p_exception, "VLM has been disabled in this libvlc." ); + return VLC_EGENERIC; +#endif +} + +void libvlc_vlm_stop_media( libvlc_instance_t *p_instance, char *psz_name, + libvlc_exception_t *p_exception ) +{ +#ifdef ENABLE_VLM + vlm_t *p_vlm; + int64_t id; + + VLM(p_vlm); + + if( vlm_Control( p_vlm, VLM_GET_MEDIA_ID, psz_name, &id ) || + vlm_Control( p_vlm, VLM_STOP_MEDIA_INSTANCE, id, NULL ) ) + { + libvlc_exception_raise( p_exception, "Unable to stop %s", psz_name ); + } +#else + libvlc_exception_raise( p_exception, "VLM has been disabled in this libvlc." ); + return VLC_EGENERIC; +#endif +} + +void libvlc_vlm_pause_media( libvlc_instance_t *p_instance, char *psz_name, + libvlc_exception_t *p_exception ) +{ +#ifdef ENABLE_VLM + vlm_t *p_vlm; + int64_t id; + + VLM(p_vlm); + + if( vlm_Control( p_vlm, VLM_GET_MEDIA_ID, psz_name, &id ) || + vlm_Control( p_vlm, VLM_PAUSE_MEDIA_INSTANCE, id, NULL ) ) + { + libvlc_exception_raise( p_exception, "Unable to pause %s", psz_name ); + } +#else + libvlc_exception_raise( p_exception, "VLM has been disabled in this libvlc." ); + return VLC_EGENERIC; +#endif +} + +void libvlc_vlm_seek_media( libvlc_instance_t *p_instance, char *psz_name, + float f_percentage, libvlc_exception_t *p_exception ) +{ +#ifdef ENABLE_VLM + vlm_t *p_vlm; + int64_t id; + + VLM(p_vlm); + + if( vlm_Control( p_vlm, VLM_GET_MEDIA_ID, psz_name, &id ) || + vlm_Control( p_vlm, VLM_SET_MEDIA_INSTANCE_POSITION, id, NULL, f_percentage ) ) + { + libvlc_exception_raise( p_exception, "Unable to seek %s to %f", psz_name, f_percentage ); + } +#else + libvlc_exception_raise( p_exception, "VLM has been disabled in this libvlc." ); + return VLC_EGENERIC; +#endif +} + +#define LIBVLC_VLM_GET_MEDIA_ATTRIBUTE( attr, returnType, getType, ret, code )\ +returnType libvlc_vlm_get_media_instance_## attr( libvlc_instance_t *p_instance, \ + char *psz_name, int i_instance, libvlc_exception_t *p_exception ) \ +{ \ + vlm_media_instance_t *p_mi = libvlc_vlm_get_media_instance( p_instance, psz_name, i_instance, \ + p_exception ); \ + if( p_mi ) { \ + returnType ret_value; \ + code; \ + vlm_media_instance_Delete( p_mi ); \ + return ret_value; \ + } \ + libvlc_exception_raise( p_exception, "Unable to get %s "#attr "attribute" ); \ + return ret; \ +} + +LIBVLC_VLM_GET_MEDIA_ATTRIBUTE( position, float, Float, -1, ret_value = p_mi->d_position; ); +LIBVLC_VLM_GET_MEDIA_ATTRIBUTE( time, int, Integer, -1, ret_value = p_mi->i_time ); +LIBVLC_VLM_GET_MEDIA_ATTRIBUTE( length, int, Integer, -1, ret_value = p_mi->i_length ); +LIBVLC_VLM_GET_MEDIA_ATTRIBUTE( rate, int, Integer, -1, ret_value = p_mi->i_rate ); +/* FIXME extend vlm_media_instance_t to be able to implement them */ +LIBVLC_VLM_GET_MEDIA_ATTRIBUTE( title, int, Integer, 0, ret_value = 0 ); +LIBVLC_VLM_GET_MEDIA_ATTRIBUTE( chapter, int, Integer, 0, ret_value = 0 ); +LIBVLC_VLM_GET_MEDIA_ATTRIBUTE( seekable, int, Bool, 0, ret_value = false ); + +#undef LIBVLC_VLM_GET_MEDIA_ATTRIBUTE + +char* libvlc_vlm_show_media( libvlc_instance_t *p_instance, char *psz_name, + libvlc_exception_t *p_exception ) +{ + (void)p_instance; + /* FIXME is it needed ? */ + libvlc_exception_raise( p_exception, "Unable to call show %s", psz_name ); + return NULL; +} diff --git a/VLC/vlmshell.c b/VLC/vlmshell.c new file mode 100644 index 0000000..f35cb14 --- /dev/null +++ b/VLC/vlmshell.c @@ -0,0 +1,1815 @@ +/***************************************************************************** + * vlm.c: VLM interface plugin + ***************************************************************************** + * Copyright (C) 2000-2005 the VideoLAN team + * $Id$ + * + * Authors: Simon Latapie + * Laurent Aimar + * Gildas Bazin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#include +#include /* tolower() */ +#include + +#include "vlc_vlm.h" + +#ifdef ENABLE_VLM + +#ifndef WIN32 +# include /* gettimeofday() */ +#endif + +#ifdef HAVE_TIME_H +# include /* ctime() */ +# include /* ftime() */ +#endif + +#include "vlc_input.h" +#include "input_internal.h" +#include "vlc_stream.h" +#include "vlm_internal.h" +#include "vlc_vod.h" +#include "vlc_charset.h" +#include "vlc_sout.h" +#include "stream_output.h" +#include "libvlc.h" + +/***************************************************************************** + * Local prototypes. + *****************************************************************************/ + +/* ugly kludge to avoid "null format string" warnings, + * even if we handle NULL format string in vlm_MessageNew() */ +static const char *vlm_NULL = NULL; + +/* */ +static vlm_message_t *vlm_Show( vlm_t *, vlm_media_sys_t *, vlm_schedule_sys_t *, const char * ); + +static vlm_schedule_sys_t *vlm_ScheduleSearch( vlm_t *, const char * ); + +static char *Save( vlm_t * ); +static int Load( vlm_t *, char * ); + +static vlm_schedule_sys_t *vlm_ScheduleNew( vlm_t *vlm, const char *psz_name ); +static int vlm_ScheduleSetup( vlm_schedule_sys_t *schedule, const char *psz_cmd, + const char *psz_value ); + +/* */ +static vlm_media_sys_t *vlm_MediaSearch( vlm_t *, const char *); + +static const char quotes[] = "\"'"; +/** + * FindCommandEnd: look for the end of a possibly quoted string + * @return NULL on mal-formatted string, + * pointer past the last character otherwise. + */ +void vlm_MessageDelete( vlm_message_t *p_message ) +{ + free( p_message->psz_name ); + free( p_message->psz_value ); + while( p_message->i_child-- ) + vlm_MessageDelete( p_message->child[p_message->i_child] ); + free( p_message->child ); + free( p_message ); +} +static const char *FindCommandEnd( const char *psz_sent ) +{ + char c, quote = 0; + + while( (c = *psz_sent) != '\0' ) + { + if( !quote ) + { + if( strchr(quotes,c) ) // opening quote + quote = c; + else if( isspace(c) ) // non-escaped space + return psz_sent; + else if( c == '\\' ) + { + psz_sent++; // skip escaped character + if( *psz_sent == '\0' ) + return psz_sent; + } + } + else + { + if( c == quote ) // non-escaped matching quote + quote = 0; + else if( (quote == '"') && (c == '\\') ) + { + psz_sent++; // skip escaped character + if (*psz_sent == '\0') + return NULL; // error, closing quote missing + } + } + psz_sent++; + } + + // error (NULL) if we could not find a matching quote + return quote ? NULL : psz_sent; +} + + +/** + * Unescape a nul-terminated string. + * Note that in and out can be identical. + * + * @param out output buffer (at least characters long) + * @param in nul-terminated string to be unescaped + * + * @return 0 on success, -1 on error. + */ +static int Unescape( char *out, const char *in ) +{ + char c, quote = 0; + + while( (c = *in++) != '\0' ) + { + if( !quote ) + { + if (strchr(quotes,c)) // opening quote + { + quote = c; + continue; + } + else if( c == '\\' ) + { + switch (c = *in++) + { + case '"': + case '\'': + case '\\': + *out++ = c; + continue; + + case '\0': + *out = '\0'; + return 0; + } + if( isspace(c) ) + { + *out++ = c; + continue; + } + /* None of the special cases - copy the backslash */ + *out++ = '\\'; + } + } + else + { + if( c == quote ) // non-escaped matching quote + { + quote = 0; + continue; + } + if( (quote == '"') && (c == '\\') ) + { + switch( c = *in++ ) + { + case '"': + case '\\': + *out++ = c; + continue; + + case '\0': // should never happen + *out = '\0'; + return -1; + } + /* None of the special cases - copy the backslash */ + *out++ = '\\'; + } + } + *out++ = c; + } + + *out = '\0'; + return 0; +} + + +/***************************************************************************** + * ExecuteCommand: The main state machine + ***************************************************************************** + * Execute a command which ends with '\0' (string) + *****************************************************************************/ +static int ExecuteSyntaxError( const char *psz_cmd, vlm_message_t **pp_status ) +{ + *pp_status = vlm_MessageNew( psz_cmd, "Wrong command syntax" ); + return VLC_EGENERIC; +} + +static bool ExecuteIsMedia( vlm_t *p_vlm, const char *psz_name ) +{ + int64_t id; + + if( !psz_name || vlm_ControlInternal( p_vlm, VLM_GET_MEDIA_ID, psz_name, &id ) ) + return false; + return true; +} +static bool ExecuteIsSchedule( vlm_t *p_vlm, const char *psz_name ) +{ + if( !psz_name || !vlm_ScheduleSearch( p_vlm, psz_name ) ) + return false; + return true; +} + +static int ExecuteDel( vlm_t *p_vlm, const char *psz_name, vlm_message_t **pp_status ) +{ + vlm_media_sys_t *p_media; + vlm_schedule_sys_t *p_schedule; + + p_media = vlm_MediaSearch( p_vlm, psz_name ); + p_schedule = vlm_ScheduleSearch( p_vlm, psz_name ); + + if( p_schedule != NULL ) + { + vlm_ScheduleDelete( p_vlm, p_schedule ); + } + else if( p_media != NULL ) + { + vlm_ControlInternal( p_vlm, VLM_DEL_MEDIA, p_media->cfg.id ); + } + else if( !strcmp(psz_name, "media") ) + { + vlm_ControlInternal( p_vlm, VLM_CLEAR_MEDIAS ); + } + else if( !strcmp(psz_name, "schedule") ) + { + vlm_ControlInternal( p_vlm, VLM_CLEAR_SCHEDULES ); + } + else if( !strcmp(psz_name, "all") ) + { + vlm_ControlInternal( p_vlm, VLM_CLEAR_MEDIAS ); + vlm_ControlInternal( p_vlm, VLM_CLEAR_SCHEDULES ); + } + else + { + *pp_status = vlm_MessageNew( "del", "%s: media unknown", psz_name ); + return VLC_EGENERIC; + } + + *pp_status = vlm_MessageNew( "del", vlm_NULL ); + return VLC_SUCCESS; +} + +static int ExecuteShow( vlm_t *p_vlm, const char *psz_name, vlm_message_t **pp_status ) +{ + vlm_media_sys_t *p_media; + vlm_schedule_sys_t *p_schedule; + + if( !psz_name ) + { + *pp_status = vlm_Show( p_vlm, NULL, NULL, NULL ); + return VLC_SUCCESS; + } + + p_media = vlm_MediaSearch( p_vlm, psz_name ); + p_schedule = vlm_ScheduleSearch( p_vlm, psz_name ); + + if( p_schedule != NULL ) + *pp_status = vlm_Show( p_vlm, NULL, p_schedule, NULL ); + else if( p_media != NULL ) + *pp_status = vlm_Show( p_vlm, p_media, NULL, NULL ); + else + *pp_status = vlm_Show( p_vlm, NULL, NULL, psz_name ); + + return VLC_SUCCESS; +} + +static int ExecuteHelp( vlm_message_t **pp_status ) +{ + vlm_message_t *message_child; + +#define MessageAdd( a ) \ + vlm_MessageAdd( *pp_status, vlm_MessageNew( a, vlm_NULL ) ); +#define MessageAddChild( a ) \ + vlm_MessageAdd( message_child, vlm_MessageNew( a, vlm_NULL ) ); + + *pp_status = vlm_MessageNew( "help", vlm_NULL ); + + message_child = MessageAdd( "Commands Syntax:" ); + MessageAddChild( "new (name) vod|broadcast|schedule [properties]" ); + MessageAddChild( "setup (name) (properties)" ); + MessageAddChild( "show [(name)|media|schedule]" ); + MessageAddChild( "del (name)|all|media|schedule" ); + MessageAddChild( "control (name) [instance_name] (command)" ); + MessageAddChild( "save (config_file)" ); + MessageAddChild( "export" ); + MessageAddChild( "load (config_file)" ); + + message_child = MessageAdd( "Media Proprieties Syntax:" ); + MessageAddChild( "input (input_name)" ); + MessageAddChild( "inputdel (input_name)|all" ); + MessageAddChild( "inputdeln input_number" ); + MessageAddChild( "output (output_name)" ); + MessageAddChild( "option (option_name)[=value]" ); + MessageAddChild( "enabled|disabled" ); + MessageAddChild( "loop|unloop (broadcast only)" ); + MessageAddChild( "mux (mux_name)" ); + + message_child = MessageAdd( "Schedule Proprieties Syntax:" ); + MessageAddChild( "enabled|disabled" ); + MessageAddChild( "append (command_until_rest_of_the_line)" ); + MessageAddChild( "date (year)/(month)/(day)-(hour):(minutes):" + "(seconds)|now" ); + MessageAddChild( "period (years_aka_12_months)/(months_aka_30_days)/" + "(days)-(hours):(minutes):(seconds)" ); + MessageAddChild( "repeat (number_of_repetitions)" ); + + message_child = MessageAdd( "Control Commands Syntax:" ); + MessageAddChild( "play [input_number]" ); + MessageAddChild( "pause" ); + MessageAddChild( "stop" ); + MessageAddChild( "seek [+-](percentage) | [+-](seconds)s | [+-](miliseconds)ms" ); + + return VLC_SUCCESS; +} + +static int ExecuteControl( vlm_t *p_vlm, const char *psz_name, const int i_arg, char ** ppsz_arg, vlm_message_t **pp_status ) +{ + vlm_media_sys_t *p_media; + const char *psz_control = NULL; + const char *psz_instance = NULL; + const char *psz_argument = NULL; + int i_index; + int i_result; + + if( !ExecuteIsMedia( p_vlm, psz_name ) ) + { + *pp_status = vlm_MessageNew( "control", "%s: media unknown", psz_name ); + return VLC_EGENERIC; + } + + assert( i_arg > 0 ); + +#define IS(txt) ( !strcmp( ppsz_arg[i_index], (txt) ) ) + i_index = 0; + if( !IS("play") && !IS("stop") && !IS("pause") && !IS("seek") ) + { + i_index = 1; + psz_instance = ppsz_arg[0]; + + if( i_index >= i_arg || ( !IS("play") && !IS("stop") && !IS("pause") && !IS("seek") ) ) + return ExecuteSyntaxError( "control", pp_status ); + } +#undef IS + psz_control = ppsz_arg[i_index]; + + if( i_index+1 < i_arg ) + psz_argument = ppsz_arg[i_index+1]; + + p_media = vlm_MediaSearch( p_vlm, psz_name ); + assert( p_media ); + + if( !strcmp( psz_control, "play" ) ) + { + int i_input_index = 0; + int i; + + if( ( psz_argument && sscanf(psz_argument, "%d", &i) == 1 ) && i > 0 && i-1 < p_media->cfg.i_input ) + { + i_input_index = i-1; + } + else if( psz_argument ) + { + int j; + vlm_media_t *p_cfg = &p_media->cfg; + for ( j=0; j < p_cfg->i_input; j++) + { + if( !strcmp( p_cfg->ppsz_input[j], psz_argument ) ) + { + i_input_index = j; + break; + } + } + } + + if( p_media->cfg.b_vod ) + i_result = vlm_ControlInternal( p_vlm, VLM_START_MEDIA_VOD_INSTANCE, p_media->cfg.id, psz_instance, i_input_index, NULL ); // we should get here now + else + i_result = vlm_ControlInternal( p_vlm, VLM_START_MEDIA_BROADCAST_INSTANCE, p_media->cfg.id, psz_instance, i_input_index ); + } + else if( !strcmp( psz_control, "seek" ) ) + { + if( psz_argument ) + { + bool b_relative; + if( psz_argument[0] == '+' || psz_argument[0] == '-' ) + b_relative = true; + else + b_relative = false; + + if( strstr( psz_argument, "ms" ) || strstr( psz_argument, "s" ) ) + { + /* Time (ms or s) */ + int64_t i_new_time; + + if( strstr( psz_argument, "ms" ) ) + i_new_time = 1000 * (int64_t)atoi( psz_argument ); + else + i_new_time = 1000000 * (int64_t)atoi( psz_argument ); + + if( b_relative ) + { + int64_t i_time = 0; + vlm_ControlInternal( p_vlm, VLM_GET_MEDIA_INSTANCE_TIME, p_media->cfg.id, psz_instance, &i_time ); + i_new_time += i_time; + } + if( i_new_time < 0 ) + i_new_time = 0; + i_result = vlm_ControlInternal( p_vlm, VLM_SET_MEDIA_INSTANCE_TIME, p_media->cfg.id, psz_instance, i_new_time ); + } + else + { + /* Percent */ + double d_new_position = us_atof( psz_argument ) / 100.0; + + if( b_relative ) + { + double d_position = 0.0; + + vlm_ControlInternal( p_vlm, VLM_GET_MEDIA_INSTANCE_POSITION, p_media->cfg.id, psz_instance, &d_position ); + d_new_position += d_position; + } + if( d_new_position < 0.0 ) + d_new_position = 0.0; + else if( d_new_position > 1.0 ) + d_new_position = 1.0; + i_result = vlm_ControlInternal( p_vlm, VLM_SET_MEDIA_INSTANCE_POSITION, p_media->cfg.id, psz_instance, d_new_position ); + } + } + else + { + i_result = VLC_EGENERIC; + } + } + else if( !strcmp( psz_control, "rewind" ) ) + { + if( psz_argument ) + { + const double d_scale = us_atof( psz_argument ); + double d_position; + + vlm_ControlInternal( p_vlm, VLM_GET_MEDIA_INSTANCE_POSITION, p_media->cfg.id, psz_instance, &d_position ); + d_position -= (d_scale / 1000.0); + if( d_position < 0.0 ) + d_position = 0.0; + i_result = vlm_ControlInternal( p_vlm, VLM_SET_MEDIA_INSTANCE_POSITION, p_media->cfg.id, psz_instance, d_position ); + } + else + { + i_result = VLC_EGENERIC; + } + } + else if( !strcmp( psz_control, "forward" ) ) + { + if( psz_argument ) + { + const double d_scale = us_atof( psz_argument ); + double d_position; + + vlm_ControlInternal( p_vlm, VLM_GET_MEDIA_INSTANCE_POSITION, p_media->cfg.id, psz_instance, &d_position ); + d_position += (d_scale / 1000.0); + if( d_position > 1.0 ) + d_position = 1.0; + i_result = vlm_ControlInternal( p_vlm, VLM_SET_MEDIA_INSTANCE_POSITION, p_media->cfg.id, psz_instance, d_position ); + + } + else + { + i_result = VLC_EGENERIC; + } + } + else if( !strcmp( psz_control, "stop" ) ) + { + i_result = vlm_ControlInternal( p_vlm, VLM_STOP_MEDIA_INSTANCE, p_media->cfg.id, psz_instance ); + } + else if( !strcmp( psz_control, "pause" ) ) + { + i_result = vlm_ControlInternal( p_vlm, VLM_PAUSE_MEDIA_INSTANCE, p_media->cfg.id, psz_instance ); + } + else + { + i_result = VLC_EGENERIC; + } + + if( i_result ) + { + *pp_status = vlm_MessageNew( "control", "unknown error" ); + return VLC_SUCCESS; + } + *pp_status = vlm_MessageNew( "control", vlm_NULL ); + return VLC_SUCCESS; +} + +static int ExecuteExport( vlm_t *p_vlm, vlm_message_t **pp_status ) +{ + char *psz_export = Save( p_vlm ); + + *pp_status = vlm_MessageNew( "export", psz_export ); + free( psz_export ); + return VLC_SUCCESS; +} + +static int ExecuteSave( vlm_t *p_vlm, const char *psz_file, vlm_message_t **pp_status ) +{ + FILE *f = utf8_fopen( psz_file, "wt" ); + char *psz_save = NULL; + + if( !f ) + goto error; + + psz_save = Save( p_vlm ); + if( psz_save == NULL ) + goto error; + if( fputs( psz_save, f ) == EOF ) + goto error;; + if( fclose( f ) ) + { + f = NULL; + goto error; + } + + free( psz_save ); + + *pp_status = vlm_MessageNew( "save", vlm_NULL ); + return VLC_SUCCESS; + +error: + free( psz_save ); + if( f ) + fclose( f ); + *pp_status = vlm_MessageNew( "save", "Unable to save to file"); + return VLC_EGENERIC; +} + +static int ExecuteLoad( vlm_t *p_vlm, const char *psz_url, vlm_message_t **pp_status ) +{ + stream_t *p_stream = stream_UrlNew( p_vlm, psz_url ); + int64_t i_size; + char *psz_buffer; + + if( !p_stream ) + { + *pp_status = vlm_MessageNew( "load", "Unable to load from file" ); + return VLC_EGENERIC; + } + + /* FIXME needed ? */ + if( stream_Seek( p_stream, 0 ) != 0 ) + { + stream_Delete( p_stream ); + + *pp_status = vlm_MessageNew( "load", "Read file error" ); + return VLC_EGENERIC; + } + + i_size = stream_Size( p_stream ); + + psz_buffer = malloc( i_size + 1 ); + if( !psz_buffer ) + { + stream_Delete( p_stream ); + + *pp_status = vlm_MessageNew( "load", "Read file error" ); + return VLC_EGENERIC; + } + + stream_Read( p_stream, psz_buffer, i_size ); + psz_buffer[i_size] = '\0'; + + stream_Delete( p_stream ); + + if( Load( p_vlm, psz_buffer ) ) + { + free( psz_buffer ); + + *pp_status = vlm_MessageNew( "load", "Error while loading file" ); + return VLC_EGENERIC; + } + + free( psz_buffer ); + + *pp_status = vlm_MessageNew( "load", vlm_NULL ); + return VLC_SUCCESS; +} + +static int ExecuteScheduleProperty( vlm_t *p_vlm, vlm_schedule_sys_t *p_schedule, bool b_new, + const int i_property, char *ppsz_property[], vlm_message_t **pp_status ) +{ + const char *psz_cmd = b_new ? "new" : "setup"; + int i; + + for( i = 0; i < i_property; i++ ) + { + if( !strcmp( ppsz_property[i], "enabled" ) || + !strcmp( ppsz_property[i], "disabled" ) ) + { + if ( vlm_ScheduleSetup( p_schedule, ppsz_property[i], NULL ) ) + goto error; + } + else if( !strcmp( ppsz_property[i], "append" ) ) + { + char *psz_line; + int j; + /* Beware: everything behind append is considered as + * command line */ + + if( ++i >= i_property ) + break; + + psz_line = strdup( ppsz_property[i] ); + for( j = i+1; j < i_property; j++ ) + { + psz_line = realloc( psz_line, strlen(psz_line) + strlen(ppsz_property[j]) + 1 + 1 ); + strcat( psz_line, " " ); + strcat( psz_line, ppsz_property[j] ); + } + + if( vlm_ScheduleSetup( p_schedule, "append", psz_line ) ) + goto error; + break; + } + else + { + if( i + 1 >= i_property ) + { + if( b_new ) + vlm_ScheduleDelete( p_vlm, p_schedule ); + return ExecuteSyntaxError( psz_cmd, pp_status ); + } + + if( vlm_ScheduleSetup( p_schedule, ppsz_property[i], ppsz_property[i+1] ) ) + goto error; + i++; + } + } + *pp_status = vlm_MessageNew( psz_cmd, vlm_NULL ); + return VLC_SUCCESS; + +error: + *pp_status = vlm_MessageNew( psz_cmd, "Error while setting the property '%s' to the schedule", + ppsz_property[i] ); + return VLC_EGENERIC; +} + +static int ExecuteMediaProperty( vlm_t *p_vlm, int64_t id, bool b_new, + const int i_property, char *ppsz_property[], vlm_message_t **pp_status ) +{ + const char *psz_cmd = b_new ? "new" : "setup"; + vlm_media_t *p_cfg = NULL; + int i_result; + int i; + +#undef ERROR +#undef MISSING +#define ERROR( txt ) do { *pp_status = vlm_MessageNew( psz_cmd, txt); goto error; } while(0) + if( vlm_ControlInternal( p_vlm, VLM_GET_MEDIA, id, &p_cfg ) ) + ERROR( "unknown media" ); + +#define MISSING(cmd) do { if( !psz_value ) ERROR( "missing argument for " cmd ); } while(0) + for( i = 0; i < i_property; i++ ) + { + const char *psz_option = ppsz_property[i]; + const char *psz_value = i+1 < i_property ? ppsz_property[i+1] : NULL; + + if( !strcmp( psz_option, "enabled" ) ) + { + p_cfg->b_enabled = true; + } + else if( !strcmp( psz_option, "disabled" ) ) + { + p_cfg->b_enabled = false; + } + else if( !strcmp( psz_option, "input" ) ) + { + MISSING( "input" ); + TAB_APPEND( p_cfg->i_input, p_cfg->ppsz_input, strdup(psz_value) ); + i++; + } + else if( !strcmp( psz_option, "inputdel" ) && psz_value && !strcmp( psz_value, "all" ) ) + { + while( p_cfg->i_input > 0 ) + TAB_REMOVE( p_cfg->i_input, p_cfg->ppsz_input, p_cfg->ppsz_input[0] ); + i++; + } + else if( !strcmp( psz_option, "inputdel" ) ) + { + int j; + + MISSING( "inputdel" ); + + for( j = 0; j < p_cfg->i_input; j++ ) + { + if( !strcmp( p_cfg->ppsz_input[j], psz_value ) ) + { + TAB_REMOVE( p_cfg->i_input, p_cfg->ppsz_input, p_cfg->ppsz_input[j] ); + break; + } + } + i++; + } + else if( !strcmp( psz_option, "inputdeln" ) ) + { + int i_index; + + MISSING( "inputdeln" ); + + i_index = atoi( psz_value ); + if( i_index > 0 && i_index <= p_cfg->i_input ) + TAB_REMOVE( p_cfg->i_input, p_cfg->ppsz_input, p_cfg->ppsz_input[i_index-1] ); + i++; + } + else if( !strcmp( psz_option, "output" ) ) + { + MISSING( "output" ); + + free( p_cfg->psz_output ); + p_cfg->psz_output = *psz_value ? strdup( psz_value ) : NULL; + i++; + } + else if( !strcmp( psz_option, "option" ) ) + { + MISSING( "option" ); + + TAB_APPEND( p_cfg->i_option, p_cfg->ppsz_option, strdup( psz_value ) ); + i++; + } + else if( !strcmp( psz_option, "loop" ) ) + { + if( p_cfg->b_vod ) + ERROR( "invalid loop option for vod" ); + p_cfg->broadcast.b_loop = true; + } + else if( !strcmp( psz_option, "unloop" ) ) + { + if( p_cfg->b_vod ) + ERROR( "invalid unloop option for vod" ); + p_cfg->broadcast.b_loop = false; + } + else if( !strcmp( psz_option, "mux" ) ) + { + MISSING( "mux" ); + if( !p_cfg->b_vod ) + ERROR( "invalid mux option for broadcast" ); + + free( p_cfg->vod.psz_mux ); + p_cfg->vod.psz_mux = *psz_value ? strdup( psz_value ) : NULL; + i++; + } + else + { + fprintf( stderr, "PROP: name=%s unknown\n", psz_option ); + ERROR( "Wrong command syntax" ); + } + } +#undef MISSING +#undef ERROR + + /* */ + i_result = vlm_ControlInternal( p_vlm, VLM_CHANGE_MEDIA, p_cfg ); + vlm_media_Delete( p_cfg ); + + *pp_status = vlm_MessageNew( psz_cmd, vlm_NULL ); + return i_result; + +error: + if( p_cfg ) + { + if( b_new ) + vlm_ControlInternal( p_vlm, VLM_DEL_MEDIA, p_cfg->id ); + vlm_media_Delete( p_cfg ); + } + return VLC_EGENERIC; +} + +static int ExecuteNew( vlm_t *p_vlm, const char *psz_name, const char *psz_type, const int i_property, char *ppsz_property[], vlm_message_t **pp_status ) +{ + /* Check name */ + if( !strcmp( psz_name, "all" ) || !strcmp( psz_name, "media" ) || !strcmp( psz_name, "schedule" ) ) + { + *pp_status = vlm_MessageNew( "new", "\"all\", \"media\" and \"schedule\" are reserved names" ); + return VLC_EGENERIC; + } + if( ExecuteIsMedia( p_vlm, psz_name ) || ExecuteIsSchedule( p_vlm, psz_name ) ) + { + *pp_status = vlm_MessageNew( "new", "%s: Name already in use", psz_name ); + return VLC_EGENERIC; + } + /* */ + if( !strcmp( psz_type, "schedule" ) ) + { + vlm_schedule_sys_t *p_schedule = vlm_ScheduleNew( p_vlm, psz_name ); + if( !p_schedule ) + { + *pp_status = vlm_MessageNew( "new", "could not create schedule" ); + return VLC_EGENERIC; + } + return ExecuteScheduleProperty( p_vlm, p_schedule, true, i_property, ppsz_property, pp_status ); + } + else if( !strcmp( psz_type, "vod" ) || !strcmp( psz_type, "broadcast" ) ) + { + vlm_media_t cfg; + int64_t id; + + vlm_media_Init( &cfg ); + cfg.psz_name = strdup( psz_name ); + cfg.b_vod = !strcmp( psz_type, "vod" ); + + if( vlm_ControlInternal( p_vlm, VLM_ADD_MEDIA, &cfg, &id ) ) + { + vlm_media_Clean( &cfg ); + *pp_status = vlm_MessageNew( "new", "could not create media" ); + return VLC_EGENERIC; + } + vlm_media_Clean( &cfg ); + return ExecuteMediaProperty( p_vlm, id, true, i_property, ppsz_property, pp_status ); + } + else + { + *pp_status = vlm_MessageNew( "new", "%s: Choose between vod, broadcast or schedule", psz_type ); + return VLC_EGENERIC; + } +} + +static int ExecuteSetup( vlm_t *p_vlm, const char *psz_name, const int i_property, char *ppsz_property[], vlm_message_t **pp_status ) +{ + if( ExecuteIsSchedule( p_vlm, psz_name ) ) + { + vlm_schedule_sys_t *p_schedule = vlm_ScheduleSearch( p_vlm, psz_name ); + return ExecuteScheduleProperty( p_vlm, p_schedule, false, i_property, ppsz_property, pp_status ); + } + else if( ExecuteIsMedia( p_vlm, psz_name ) ) + { + int64_t id; + if( vlm_ControlInternal( p_vlm, VLM_GET_MEDIA_ID, psz_name, &id ) ) + goto error; + return ExecuteMediaProperty( p_vlm, id, false, i_property, ppsz_property, pp_status ); + } + +error: + *pp_status = vlm_MessageNew( "setup", "%s unknown", psz_name ); + return VLC_EGENERIC; +} + +int ExecuteCommand( vlm_t *p_vlm, const char *psz_command, + vlm_message_t **pp_message ) +{ + size_t i_command = 0; + char buf[strlen (psz_command) + 1], *psz_buf = buf; + char *ppsz_command[3+sizeof (buf) / 2]; + vlm_message_t *p_message = NULL; + + /* First, parse the line and cut it */ + while( *psz_command != '\0' ) + { + const char *psz_temp; + + if(isspace (*psz_command)) + { + psz_command++; + continue; + } + + /* support for comments */ + if( i_command == 0 && *psz_command == '#') + { + p_message = vlm_MessageNew( "", vlm_NULL ); + goto success; + } + + psz_temp = FindCommandEnd( psz_command ); + + if( psz_temp == NULL ) + { + p_message = vlm_MessageNew( "Incomplete command", psz_command ); + goto error; + } + + assert (i_command < (sizeof (ppsz_command) / sizeof (ppsz_command[0]))); + + ppsz_command[i_command] = psz_buf; + memcpy (psz_buf, psz_command, psz_temp - psz_command); + psz_buf[psz_temp - psz_command] = '\0'; + + Unescape (psz_buf, psz_buf); + + i_command++; + psz_buf += psz_temp - psz_command + 1; + psz_command = psz_temp; + + assert (buf + sizeof (buf) >= psz_buf); + } + + /* + * And then Interpret it + */ + +#define IF_EXECUTE( name, check, cmd ) if( !strcmp(ppsz_command[0], name ) ) { if( (check) ) goto syntax_error; if( (cmd) ) goto error; goto success; } + if( i_command == 0 ) + { + p_message = vlm_MessageNew( "", vlm_NULL ); + goto success; + } + else IF_EXECUTE( "del", (i_command != 2), ExecuteDel(p_vlm, ppsz_command[1], &p_message) ) + else IF_EXECUTE( "show", (i_command > 2), ExecuteShow(p_vlm, i_command > 1 ? ppsz_command[1] : NULL, &p_message) ) + else IF_EXECUTE( "help", (i_command != 1), ExecuteHelp( &p_message ) ) + else IF_EXECUTE( "control", (i_command < 3), ExecuteControl(p_vlm, ppsz_command[1], i_command - 2, &ppsz_command[2], &p_message) ) + else IF_EXECUTE( "save", (i_command != 2), ExecuteSave(p_vlm, ppsz_command[1], &p_message) ) + else IF_EXECUTE( "export", (i_command != 1), ExecuteExport(p_vlm, &p_message) ) + else IF_EXECUTE( "load", (i_command != 2), ExecuteLoad(p_vlm, ppsz_command[1], &p_message) ) + else IF_EXECUTE( "new", (i_command < 3), ExecuteNew(p_vlm, ppsz_command[1], ppsz_command[2], i_command-3, &ppsz_command[3], &p_message) ) + else IF_EXECUTE( "setup", (i_command < 2), ExecuteSetup(p_vlm, ppsz_command[1], i_command-2, &ppsz_command[2], &p_message) ) + else + { + p_message = vlm_MessageNew( ppsz_command[0], "Unknown command" ); + goto error; + } +#undef IF_EXECUTE + +success: + *pp_message = p_message; + return VLC_SUCCESS; + +syntax_error: + return ExecuteSyntaxError( ppsz_command[0], pp_message ); + +error: + *pp_message = p_message; + return VLC_EGENERIC; +} + +/***************************************************************************** + * Media handling + *****************************************************************************/ +vlm_media_sys_t *vlm_MediaSearch( vlm_t *vlm, const char *psz_name ) +{ + int i; + + for( i = 0; i < vlm->i_media; i++ ) + { + if( strcmp( psz_name, vlm->media[i]->cfg.psz_name ) == 0 ) + return vlm->media[i]; + } + + return NULL; +} + +/***************************************************************************** + * Schedule handling + *****************************************************************************/ +static vlm_schedule_sys_t *vlm_ScheduleNew( vlm_t *vlm, const char *psz_name ) +{ + if( !psz_name ) + return NULL; + + vlm_schedule_sys_t *p_sched = malloc( sizeof( vlm_schedule_sys_t ) ); + if( !p_sched ) + return NULL; + + p_sched->psz_name = strdup( psz_name ); + p_sched->b_enabled = false; + p_sched->i_command = 0; + p_sched->command = NULL; + p_sched->i_date = 0; + p_sched->i_period = 0; + p_sched->i_repeat = -1; + + TAB_APPEND( vlm->i_schedule, vlm->schedule, p_sched ); + + return p_sched; +} + +/* for now, simple delete. After, del with options (last arg) */ +void vlm_ScheduleDelete( vlm_t *vlm, vlm_schedule_sys_t *sched ) +{ + if( sched == NULL ) return; + + TAB_REMOVE( vlm->i_schedule, vlm->schedule, sched ); + + if( vlm->i_schedule == 0 ) free( vlm->schedule ); + free( sched->psz_name ); + while( sched->i_command ) + { + char *psz_cmd = sched->command[0]; + TAB_REMOVE( sched->i_command, sched->command, psz_cmd ); + free( psz_cmd ); + } + free( sched ); +} + +static vlm_schedule_sys_t *vlm_ScheduleSearch( vlm_t *vlm, const char *psz_name ) +{ + int i; + + for( i = 0; i < vlm->i_schedule; i++ ) + { + if( strcmp( psz_name, vlm->schedule[i]->psz_name ) == 0 ) + { + return vlm->schedule[i]; + } + } + + return NULL; +} + +/* Ok, setup schedule command will be able to support only one (argument value) at a time */ +static int vlm_ScheduleSetup( vlm_schedule_sys_t *schedule, const char *psz_cmd, + const char *psz_value ) +{ + if( !strcmp( psz_cmd, "enabled" ) ) + { + schedule->b_enabled = true; + } + else if( !strcmp( psz_cmd, "disabled" ) ) + { + schedule->b_enabled = false; + } + else if( !strcmp( psz_cmd, "date" ) ) + { + struct tm time; + const char *p; + time_t date; + + time.tm_sec = 0; /* seconds */ + time.tm_min = 0; /* minutes */ + time.tm_hour = 0; /* hours */ + time.tm_mday = 0; /* day of the month */ + time.tm_mon = 0; /* month */ + time.tm_year = 0; /* year */ + time.tm_wday = 0; /* day of the week */ + time.tm_yday = 0; /* day in the year */ + time.tm_isdst = -1; /* daylight saving time */ + + /* date should be year/month/day-hour:minutes:seconds */ + p = strchr( psz_value, '-' ); + + if( !strcmp( psz_value, "now" ) ) + { + schedule->i_date = 0; + } + else if(p == NULL) + { + return 1; + } + else + { + unsigned i,j,k; + + switch( sscanf( p + 1, "%u:%u:%u", &i, &j, &k ) ) + { + case 1: + time.tm_sec = i; + break; + case 2: + time.tm_min = i; + time.tm_sec = j; + break; + case 3: + time.tm_hour = i; + time.tm_min = j; + time.tm_sec = k; + break; + default: + return 1; + } + + switch( sscanf( psz_value, "%d/%d/%d", &i, &j, &k ) ) + { + case 1: + time.tm_mday = i; + break; + case 2: + time.tm_mon = i - 1; + time.tm_mday = j; + break; + case 3: + time.tm_year = i - 1900; + time.tm_mon = j - 1; + time.tm_mday = k; + break; + default: + return 1; + } + + date = mktime( &time ); + schedule->i_date = ((mtime_t) date) * 1000000; + } + } + else if( !strcmp( psz_cmd, "period" ) ) + { + struct tm time; + const char *p; + const char *psz_time = NULL, *psz_date = NULL; + time_t date; + unsigned i,j,k; + + /* First, if date or period are modified, repeat should be equal to -1 */ + schedule->i_repeat = -1; + + time.tm_sec = 0; /* seconds */ + time.tm_min = 0; /* minutes */ + time.tm_hour = 0; /* hours */ + time.tm_mday = 0; /* day of the month */ + time.tm_mon = 0; /* month */ + time.tm_year = 0; /* year */ + time.tm_wday = 0; /* day of the week */ + time.tm_yday = 0; /* day in the year */ + time.tm_isdst = -1; /* daylight saving time */ + + /* date should be year/month/day-hour:minutes:seconds */ + p = strchr( psz_value, '-' ); + if( p ) + { + psz_date = psz_value; + psz_time = p + 1; + } + else + { + psz_time = psz_value; + } + + switch( sscanf( psz_time, "%u:%u:%u", &i, &j, &k ) ) + { + case 1: + time.tm_sec = i; + break; + case 2: + time.tm_min = i; + time.tm_sec = j; + break; + case 3: + time.tm_hour = i; + time.tm_min = j; + time.tm_sec = k; + break; + default: + return 1; + } + if( psz_date ) + { + switch( sscanf( psz_date, "%u/%u/%u", &i, &j, &k ) ) + { + case 1: + time.tm_mday = i; + break; + case 2: + time.tm_mon = i; + time.tm_mday = j; + break; + case 3: + time.tm_year = i; + time.tm_mon = j; + time.tm_mday = k; + break; + default: + return 1; + } + } + + /* ok, that's stupid... who is going to schedule streams every 42 years ? */ + date = (((( time.tm_year * 12 + time.tm_mon ) * 30 + time.tm_mday ) * 24 + time.tm_hour ) * 60 + time.tm_min ) * 60 + time.tm_sec ; + schedule->i_period = ((mtime_t) date) * 1000000; + } + else if( !strcmp( psz_cmd, "repeat" ) ) + { + int i; + + if( sscanf( psz_value, "%d", &i ) == 1 ) + { + schedule->i_repeat = i; + } + else + { + return 1; + } + } + else if( !strcmp( psz_cmd, "append" ) ) + { + char *command = strdup( psz_value ); + + TAB_APPEND( schedule->i_command, schedule->command, command ); + } + else + { + return 1; + } + return 0; +} + +/***************************************************************************** + * Message handling functions + *****************************************************************************/ +vlm_message_t *vlm_MessageNew( const char *psz_name, + const char *psz_format, ... ) +{ + vlm_message_t *p_message; + va_list args; + + if( !psz_name ) return NULL; + + p_message = malloc( sizeof(vlm_message_t) ); + if( !p_message) + { + return NULL; + } + + p_message->psz_value = 0; + + if( psz_format ) + { + va_start( args, psz_format ); + if( vasprintf( &p_message->psz_value, psz_format, args ) == -1 ) + { + va_end( args ); + free( p_message ); + return NULL; + } + va_end( args ); + } + + p_message->psz_name = strdup( psz_name ); + p_message->i_child = 0; + p_message->child = NULL; + + return p_message; +} + + + +/* Add a child */ +vlm_message_t *vlm_MessageAdd( vlm_message_t *p_message, + vlm_message_t *p_child ) +{ + if( p_message == NULL ) return NULL; + + if( p_child ) + { + TAB_APPEND( p_message->i_child, p_message->child, p_child ); + } + + return p_child; +} + +/***************************************************************************** + * Misc utility functions + *****************************************************************************/ +static vlm_message_t *vlm_ShowMedia( vlm_media_sys_t *p_media ) +{ + vlm_media_t *p_cfg = &p_media->cfg; + vlm_message_t *p_msg; + vlm_message_t *p_msg_sub; + int i; + + p_msg = vlm_MessageNew( p_cfg->psz_name, vlm_NULL ); + vlm_MessageAdd( p_msg, + vlm_MessageNew( "type", p_cfg->b_vod ? "vod" : "broadcast" ) ); + vlm_MessageAdd( p_msg, + vlm_MessageNew( "enabled", p_cfg->b_enabled ? "yes" : "no" ) ); + + if( p_cfg->b_vod ) + vlm_MessageAdd( p_msg, + vlm_MessageNew( "mux", p_cfg->vod.psz_mux ) ); + else + vlm_MessageAdd( p_msg, + vlm_MessageNew( "loop", p_cfg->broadcast.b_loop ? "yes" : "no" ) ); + + p_msg_sub = vlm_MessageAdd( p_msg, vlm_MessageNew( "inputs", vlm_NULL ) ); + for( i = 0; i < p_cfg->i_input; i++ ) + { + char *psz_tmp; + if( asprintf( &psz_tmp, "%d", i+1 ) != -1 ) + { + vlm_MessageAdd( p_msg_sub, + vlm_MessageNew( psz_tmp, p_cfg->ppsz_input[i] ) ); + free( psz_tmp ); + } + } + + vlm_MessageAdd( p_msg, + vlm_MessageNew( "output", p_cfg->psz_output ? p_cfg->psz_output : "" ) ); + + p_msg_sub = vlm_MessageAdd( p_msg, vlm_MessageNew( "options", vlm_NULL ) ); + for( i = 0; i < p_cfg->i_option; i++ ) + vlm_MessageAdd( p_msg_sub, vlm_MessageNew( p_cfg->ppsz_option[i], vlm_NULL ) ); + + p_msg_sub = vlm_MessageAdd( p_msg, vlm_MessageNew( "instances", vlm_NULL ) ); + for( i = 0; i < p_media->i_instance; i++ ) + { + vlm_media_instance_sys_t *p_instance = p_media->instance[i]; + vlc_value_t val; + vlm_message_t *p_msg_instance; + char *psz_tmp; + + val.i_int = END_S; + if( p_instance->p_input ) + var_Get( p_instance->p_input, "state", &val ); + + p_msg_instance = vlm_MessageAdd( p_msg_sub, vlm_MessageNew( "instance" , vlm_NULL ) ); + + vlm_MessageAdd( p_msg_instance, + vlm_MessageNew( "name" , p_instance->psz_name ? p_instance->psz_name : "default" ) ); + vlm_MessageAdd( p_msg_instance, + vlm_MessageNew( "state", + val.i_int == PLAYING_S ? "playing" : + val.i_int == PAUSE_S ? "paused" : + "stopped" ) ); + + /* FIXME should not do that this way */ + if( p_instance->p_input ) + { +#define APPEND_INPUT_INFO( a, format, type ) \ + if( asprintf( &psz_tmp, format, \ + var_Get ## type( p_instance->p_input, a ) ) != -1 ) \ + { \ + vlm_MessageAdd( p_msg_instance, vlm_MessageNew( a, \ + psz_tmp ) ); \ + free( psz_tmp ); \ + } + APPEND_INPUT_INFO( "position", "%f", Float ); + APPEND_INPUT_INFO( "time", "%"PRIi64, Time ); + APPEND_INPUT_INFO( "length", "%"PRIi64, Time ); + APPEND_INPUT_INFO( "rate", "%d", Integer ); + APPEND_INPUT_INFO( "title", "%d", Integer ); + APPEND_INPUT_INFO( "chapter", "%d", Integer ); + APPEND_INPUT_INFO( "seekable", "%d", Bool ); + } +#undef APPEND_INPUT_INFO + if( asprintf( &psz_tmp, "%d", p_instance->i_index + 1 ) != -1 ) + { + vlm_MessageAdd( p_msg_instance, vlm_MessageNew( "playlistindex", + psz_tmp ) ); + free( psz_tmp ); + } + } + return p_msg; +} + +static vlm_message_t *vlm_Show( vlm_t *vlm, vlm_media_sys_t *media, + vlm_schedule_sys_t *schedule, + const char *psz_filter ) +{ + if( media != NULL ) + { + vlm_message_t *p_msg = vlm_MessageNew( "show", vlm_NULL ); + if( p_msg ) + vlm_MessageAdd( p_msg, vlm_ShowMedia( media ) ); + return p_msg; + } + + else if( schedule != NULL ) + { + int i; + vlm_message_t *msg; + vlm_message_t *msg_schedule; + vlm_message_t *msg_child; + char buffer[100]; + + msg = vlm_MessageNew( "show", vlm_NULL ); + msg_schedule = + vlm_MessageAdd( msg, vlm_MessageNew( schedule->psz_name, vlm_NULL ) ); + + vlm_MessageAdd( msg_schedule, vlm_MessageNew("type", "schedule") ); + + vlm_MessageAdd( msg_schedule, + vlm_MessageNew( "enabled", schedule->b_enabled ? + "yes" : "no" ) ); + + if( schedule->i_date != 0 ) + { + struct tm date; + time_t i_time = (time_t)( schedule->i_date / 1000000 ); + char *psz_date; + + localtime_r( &i_time, &date); + if( asprintf( &psz_date, "%d/%d/%d-%d:%d:%d", + date.tm_year + 1900, date.tm_mon + 1, date.tm_mday, + date.tm_hour, date.tm_min, date.tm_sec ) != -1 ) + { + vlm_MessageAdd( msg_schedule, + vlm_MessageNew( "date", psz_date ) ); + free( psz_date ); + } + } + else + vlm_MessageAdd( msg_schedule, vlm_MessageNew("date", "now") ); + + if( schedule->i_period != 0 ) + { + time_t i_time = (time_t) ( schedule->i_period / 1000000 ); + struct tm date; + + date.tm_sec = (int)( i_time % 60 ); + i_time = i_time / 60; + date.tm_min = (int)( i_time % 60 ); + i_time = i_time / 60; + date.tm_hour = (int)( i_time % 24 ); + i_time = i_time / 24; + date.tm_mday = (int)( i_time % 30 ); + i_time = i_time / 30; + /* okay, okay, months are not always 30 days long */ + date.tm_mon = (int)( i_time % 12 ); + i_time = i_time / 12; + date.tm_year = (int)i_time; + + sprintf( buffer, "%d/%d/%d-%d:%d:%d", date.tm_year, date.tm_mon, + date.tm_mday, date.tm_hour, date.tm_min, date.tm_sec); + + vlm_MessageAdd( msg_schedule, vlm_MessageNew("period", buffer) ); + } + else + vlm_MessageAdd( msg_schedule, vlm_MessageNew("period", "0") ); + + sprintf( buffer, "%d", schedule->i_repeat ); + vlm_MessageAdd( msg_schedule, vlm_MessageNew( "repeat", buffer ) ); + + msg_child = + vlm_MessageAdd( msg_schedule, vlm_MessageNew("commands", vlm_NULL ) ); + + for( i = 0; i < schedule->i_command; i++ ) + { + vlm_MessageAdd( msg_child, + vlm_MessageNew( schedule->command[i], vlm_NULL ) ); + } + + return msg; + + } + + else if( psz_filter && !strcmp( psz_filter, "media" ) ) + { + vlm_message_t *p_msg; + vlm_message_t *p_msg_child; + int i_vod = 0, i_broadcast = 0; + int i; + char *psz_count; + + for( i = 0; i < vlm->i_media; i++ ) + { + if( vlm->media[i]->cfg.b_vod ) + i_vod++; + else + i_broadcast++; + } + + if( asprintf( &psz_count, "( %d broadcast - %d vod )", i_broadcast, + i_vod) == -1 ) + return NULL; + p_msg = vlm_MessageNew( "show", vlm_NULL ); + p_msg_child = vlm_MessageAdd( p_msg, vlm_MessageNew( "media", psz_count ) ); + free( psz_count ); + + for( i = 0; i < vlm->i_media; i++ ) + vlm_MessageAdd( p_msg_child, vlm_ShowMedia( vlm->media[i] ) ); + + return p_msg; + } + + else if( psz_filter && !strcmp( psz_filter, "schedule" ) ) + { + int i; + vlm_message_t *msg; + vlm_message_t *msg_child; + + msg = vlm_MessageNew( "show", vlm_NULL ); + msg_child = vlm_MessageAdd( msg, vlm_MessageNew( "schedule", vlm_NULL ) ); + + for( i = 0; i < vlm->i_schedule; i++ ) + { + vlm_schedule_sys_t *s = vlm->schedule[i]; + vlm_message_t *msg_schedule; + mtime_t i_time, i_next_date; + + msg_schedule = vlm_MessageAdd( msg_child, + vlm_MessageNew( s->psz_name, vlm_NULL ) ); + vlm_MessageAdd( msg_schedule, + vlm_MessageNew( "enabled", s->b_enabled ? + "yes" : "no" ) ); + + /* calculate next date */ + i_time = vlm_Date(); + i_next_date = s->i_date; + + if( s->i_period != 0 ) + { + int j = 0; + while( s->i_date + j * s->i_period <= i_time && + s->i_repeat > j ) + { + j++; + } + + i_next_date = s->i_date + j * s->i_period; + } + + if( i_next_date > i_time ) + { + time_t i_date = (time_t) (i_next_date / 1000000) ; + +#if !defined( UNDER_CE ) +#ifdef HAVE_CTIME_R + char psz_date[500]; + ctime_r( &i_date, psz_date ); +#else + char *psz_date = ctime( &i_date ); +#endif + + vlm_MessageAdd( msg_schedule, + vlm_MessageNew( "next launch", psz_date ) ); +#endif + } + } + + return msg; + } + + else if( ( psz_filter == NULL ) && ( media == NULL ) && ( schedule == NULL ) ) + { + vlm_message_t *show1 = vlm_Show( vlm, NULL, NULL, "media" ); + vlm_message_t *show2 = vlm_Show( vlm, NULL, NULL, "schedule" ); + + vlm_MessageAdd( show1, show2->child[0] ); + + /* We must destroy the parent node "show" of show2 + * and not the children */ + free( show2->psz_name ); + free( show2 ); + + return show1; + } + + else + { + return vlm_MessageNew( "show", vlm_NULL ); + } +} + +/***************************************************************************** + * Config handling functions + *****************************************************************************/ +static int Load( vlm_t *vlm, char *file ) +{ + char *pf = file; + int i_line = 1; + + while( *pf != '\0' ) + { + vlm_message_t *message = NULL; + int i_end = 0; + + while( pf[i_end] != '\n' && pf[i_end] != '\0' && pf[i_end] != '\r' ) + { + i_end++; + } + + if( pf[i_end] == '\r' || pf[i_end] == '\n' ) + { + pf[i_end] = '\0'; + i_end++; + if( pf[i_end] == '\n' ) i_end++; + } + + if( *pf && ExecuteCommand( vlm, pf, &message ) ) + { + if( message ) + { + if( message->psz_value ) + msg_Err( vlm, "Load error on line %d: %s: %s", + i_line, message->psz_name, message->psz_value ); + vlm_MessageDelete( message ); + } + return 1; + } + if( message ) vlm_MessageDelete( message ); + + pf += i_end; + i_line++; + } + + return 0; +} + +static char *Save( vlm_t *vlm ) +{ + char *save = NULL; + char psz_header[] = "\n" + "# VLC media player VLM command batch\n" + "# http://www.videolan.org/vlc/\n\n" ; + char *p; + int i,j; + int i_length = strlen( psz_header ); + + for( i = 0; i < vlm->i_media; i++ ) + { + vlm_media_sys_t *media = vlm->media[i]; + vlm_media_t *p_cfg = &media->cfg; + + if( p_cfg->b_vod ) + i_length += strlen( "new * vod " ) + strlen(p_cfg->psz_name); + else + i_length += strlen( "new * broadcast " ) + strlen(p_cfg->psz_name); + + if( p_cfg->b_enabled == true ) + i_length += strlen( "enabled" ); + else + i_length += strlen( "disabled" ); + + if( !p_cfg->b_vod && p_cfg->broadcast.b_loop == true ) + i_length += strlen( " loop\n" ); + else + i_length += strlen( "\n" ); + + for( j = 0; j < p_cfg->i_input; j++ ) + i_length += strlen( "setup * input \"\"\n" ) + strlen( p_cfg->psz_name ) + strlen( p_cfg->ppsz_input[j] ); + + if( p_cfg->psz_output != NULL ) + i_length += strlen( "setup * output \n" ) + strlen(p_cfg->psz_name) + strlen(p_cfg->psz_output); + + for( j = 0; j < p_cfg->i_option; j++ ) + i_length += strlen("setup * option \n") + strlen(p_cfg->psz_name) + strlen(p_cfg->ppsz_option[j]); + + if( p_cfg->b_vod && p_cfg->vod.psz_mux ) + i_length += strlen("setup * mux \n") + strlen(p_cfg->psz_name) + strlen(p_cfg->vod.psz_mux); + } + + for( i = 0; i < vlm->i_schedule; i++ ) + { + vlm_schedule_sys_t *schedule = vlm->schedule[i]; + + i_length += strlen( "new schedule " ) + strlen( schedule->psz_name ); + + if( schedule->b_enabled == true ) + { + i_length += strlen( "date //-:: enabled\n" ) + 14; + } + else + { + i_length += strlen( "date //-:: disabled\n" ) + 14; + } + + + if( schedule->i_period != 0 ) + { + i_length += strlen( "setup " ) + strlen( schedule->psz_name ) + + strlen( "period //-::\n" ) + 14; + } + + if( schedule->i_repeat >= 0 ) + { + char buffer[12]; + + sprintf( buffer, "%d", schedule->i_repeat ); + i_length += strlen( "setup repeat \n" ) + + strlen( schedule->psz_name ) + strlen( buffer ); + } + else + { + i_length++; + } + + for( j = 0; j < schedule->i_command; j++ ) + { + i_length += strlen( "setup append \n" ) + + strlen( schedule->psz_name ) + strlen( schedule->command[j] ); + } + + } + + /* Don't forget the '\0' */ + i_length++; + /* now we have the length of save */ + + p = save = malloc( i_length ); + if( !save ) return NULL; + *save = '\0'; + + p += sprintf( p, "%s", psz_header ); + + /* finally we can write in it */ + for( i = 0; i < vlm->i_media; i++ ) + { + vlm_media_sys_t *media = vlm->media[i]; + vlm_media_t *p_cfg = &media->cfg; + + if( p_cfg->b_vod ) + p += sprintf( p, "new %s vod ", p_cfg->psz_name ); + else + p += sprintf( p, "new %s broadcast ", p_cfg->psz_name ); + + if( p_cfg->b_enabled ) + p += sprintf( p, "enabled" ); + else + p += sprintf( p, "disabled" ); + + if( !p_cfg->b_vod && p_cfg->broadcast.b_loop ) + p += sprintf( p, " loop\n" ); + else + p += sprintf( p, "\n" ); + + for( j = 0; j < p_cfg->i_input; j++ ) + p += sprintf( p, "setup %s input \"%s\"\n", p_cfg->psz_name, p_cfg->ppsz_input[j] ); + + if( p_cfg->psz_output ) + p += sprintf( p, "setup %s output %s\n", p_cfg->psz_name, p_cfg->psz_output ); + + for( j = 0; j < p_cfg->i_option; j++ ) + p += sprintf( p, "setup %s option %s\n", p_cfg->psz_name, p_cfg->ppsz_option[j] ); + + if( p_cfg->b_vod && p_cfg->vod.psz_mux ) + p += sprintf( p, "setup %s mux %s\n", p_cfg->psz_name, p_cfg->vod.psz_mux ); + } + + /* and now, the schedule scripts */ + for( i = 0; i < vlm->i_schedule; i++ ) + { + vlm_schedule_sys_t *schedule = vlm->schedule[i]; + struct tm date; + time_t i_time = (time_t) ( schedule->i_date / 1000000 ); + + localtime_r( &i_time, &date); + p += sprintf( p, "new %s schedule ", schedule->psz_name); + + if( schedule->b_enabled == true ) + { + p += sprintf( p, "date %d/%d/%d-%d:%d:%d enabled\n", + date.tm_year + 1900, date.tm_mon + 1, date.tm_mday, + date.tm_hour, date.tm_min, date.tm_sec ); + } + else + { + p += sprintf( p, "date %d/%d/%d-%d:%d:%d disabled\n", + date.tm_year + 1900, date.tm_mon + 1, date.tm_mday, + date.tm_hour, date.tm_min, date.tm_sec); + } + + if( schedule->i_period != 0 ) + { + p += sprintf( p, "setup %s ", schedule->psz_name ); + + i_time = (time_t) ( schedule->i_period / 1000000 ); + + date.tm_sec = (int)( i_time % 60 ); + i_time = i_time / 60; + date.tm_min = (int)( i_time % 60 ); + i_time = i_time / 60; + date.tm_hour = (int)( i_time % 24 ); + i_time = i_time / 24; + date.tm_mday = (int)( i_time % 30 ); + i_time = i_time / 30; + /* okay, okay, months are not always 30 days long */ + date.tm_mon = (int)( i_time % 12 ); + i_time = i_time / 12; + date.tm_year = (int)i_time; + + p += sprintf( p, "period %d/%d/%d-%d:%d:%d\n", + date.tm_year, date.tm_mon, date.tm_mday, + date.tm_hour, date.tm_min, date.tm_sec); + } + + if( schedule->i_repeat >= 0 ) + { + p += sprintf( p, "setup %s repeat %d\n", + schedule->psz_name, schedule->i_repeat ); + } + else + { + p += sprintf( p, "\n" ); + } + + for( j = 0; j < schedule->i_command; j++ ) + { + p += sprintf( p, "setup %s append %s\n", + schedule->psz_name, schedule->command[j] ); + } + + } + + return save; +} + +#endif /* ENABLE_VLM */ diff --git a/VLC/vout_intf.c b/VLC/vout_intf.c new file mode 100644 index 0000000..f74aa52 --- /dev/null +++ b/VLC/vout_intf.c @@ -0,0 +1,1221 @@ +/***************************************************************************** + * vout_intf.c : video output interface + ***************************************************************************** + * Copyright (C) 2000-2007 the VideoLAN team + * + * Authors: Gildas Bazin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#include +#include /* free() */ +#include /* opendir() */ +#include /* opendir() */ +#include + +#include "vlc_interface.h" +#include "vlc_block.h" +#include "vlc_playlist.h" + +#include "vlc_vout.h" +#include "vlc_window.h" +#include "vlc_image.h" +#include "vlc_osd.h" +#include "vlc_charset.h" + +#include "vlc_strings.h" +#include "vlc_charset.h" +#include "libvlc.h" + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +static void InitWindowSize( vout_thread_t *, unsigned *, unsigned * ); + +/* Object variables callbacks */ +static int ZoomCallback( vlc_object_t *, char const *, + vlc_value_t, vlc_value_t, void * ); +static int CropCallback( vlc_object_t *, char const *, + vlc_value_t, vlc_value_t, void * ); +static int AspectCallback( vlc_object_t *, char const *, + vlc_value_t, vlc_value_t, void * ); +static int OnTopCallback( vlc_object_t *, char const *, + vlc_value_t, vlc_value_t, void * ); +static int FullscreenCallback( vlc_object_t *, char const *, + vlc_value_t, vlc_value_t, void * ); +static int SnapshotCallback( vlc_object_t *, char const *, + vlc_value_t, vlc_value_t, void * ); + +static int TitleShowCallback( vlc_object_t *, char const *, + vlc_value_t, vlc_value_t, void * ); +static int TitleTimeoutCallback( vlc_object_t *, char const *, + vlc_value_t, vlc_value_t, void * ); +static int TitlePositionCallback( vlc_object_t *, char const *, + vlc_value_t, vlc_value_t, void * ); + +/***************************************************************************** + * vout_RequestWindow: Create/Get a video window if possible. + ***************************************************************************** + * This function looks for the main interface and tries to request + * a new video window. If it fails then the vout will still need to create the + * window by itself. + *****************************************************************************/ +void *vout_RequestWindow( vout_thread_t *p_vout, + int *pi_x_hint, int *pi_y_hint, + unsigned int *pi_width_hint, + unsigned int *pi_height_hint ) +{ + /* Small kludge */ + if( !var_Type( p_vout, "aspect-ratio" ) ) vout_IntfInit( p_vout ); + + /* Get requested coordinates */ + *pi_x_hint = var_GetInteger( p_vout, "video-x" ); + *pi_y_hint = var_GetInteger( p_vout, "video-y" ); + + *pi_width_hint = p_vout->i_window_width; + *pi_height_hint = p_vout->i_window_height; + + /* Check whether someone provided us with a window ID */ + int drawable = var_CreateGetInteger( p_vout, "drawable" ); + if( drawable ) return (void *)(intptr_t)drawable; + + vout_window_t *wnd = vlc_custom_create (VLC_OBJECT(p_vout), sizeof (*wnd), + VLC_OBJECT_GENERIC, "window"); + if (wnd == NULL) + return NULL; + + wnd->vout = p_vout; + wnd->width = *pi_width_hint; + wnd->height = *pi_height_hint; + wnd->pos_x = *pi_x_hint; + wnd->pos_y = *pi_y_hint; + vlc_object_attach (wnd, p_vout); + + wnd->module = module_Need (wnd, "vout window", 0, 0); + if (wnd->module == NULL) + { + msg_Dbg (wnd, "no window provider available"); + vlc_object_release (wnd); + return NULL; + } + p_vout->p_window = wnd; + *pi_width_hint = wnd->width; + *pi_height_hint = wnd->height; + *pi_x_hint = wnd->pos_x; + *pi_y_hint = wnd->pos_y; + return wnd->handle; +} + +void vout_ReleaseWindow( vout_thread_t *p_vout, void *dummy ) +{ + vout_window_t *wnd = p_vout->p_window; + + if (wnd == NULL) + return; + p_vout->p_window = NULL; + + assert (wnd->module); + module_Unneed (wnd, wnd->module); + + vlc_object_release (wnd); + (void)dummy; +} + +int vout_ControlWindow( vout_thread_t *p_vout, void *dummy, + int i_query, va_list args ) +{ + (void)dummy; + vout_window_t *wnd = p_vout->p_window; + + if (wnd == NULL) + return VLC_EGENERIC; + + assert (wnd->control); + return wnd->control (wnd, i_query, args); +} + +/***************************************************************************** + * vout_IntfInit: called during the vout creation to initialise misc things. + *****************************************************************************/ +static const struct +{ + double f_value; + const char *psz_label; +} p_zoom_values[] = { + { 0.25, N_("1:4 Quarter") }, + { 0.5, N_("1:2 Half") }, + { 1, N_("1:1 Original") }, + { 2, N_("2:1 Double") }, + { 0, NULL } }; + +static const struct +{ + const char *psz_value; + const char *psz_label; +} p_crop_values[] = { + { "", N_("Default") }, + { "16:10", "16:10" }, + { "16:9", "16:9" }, + { "185:100", "1.85:1" }, + { "221:100", "2.21:1" }, + { "235:100", "2.35:1" }, + { "239:100", "2.39:1" }, + { "5:3", "5:3" }, + { "4:3", "4:3" }, + { "5:4", "5:4" }, + { "1:1", "1:1" }, + { NULL, NULL } }; + +static const struct +{ + const char *psz_value; + const char *psz_label; +} p_aspect_ratio_values[] = { + { "", N_("Default") }, + { "1:1", "1:1" }, + { "4:3", "4:3" }, + { "16:9", "16:9" }, + { "16:10", "16:10" }, + { "221:100", "2.21:1" }, + { "5:4", "5:4" }, + { NULL, NULL } }; + +static void AddCustomRatios( vout_thread_t *p_vout, const char *psz_var, + char *psz_list ) +{ + if( psz_list && *psz_list ) + { + char *psz_cur = psz_list; + char *psz_next; + while( psz_cur && *psz_cur ) + { + vlc_value_t val, text; + psz_next = strchr( psz_cur, ',' ); + if( psz_next ) + { + *psz_next = '\0'; + psz_next++; + } + val.psz_string = psz_cur; + text.psz_string = psz_cur; + var_Change( p_vout, psz_var, VLC_VAR_ADDCHOICE, &val, &text); + psz_cur = psz_next; + } + } +} + +void vout_IntfInit( vout_thread_t *p_vout ) +{ + vlc_value_t val, text, old_val; + bool b_force_par = false; + char *psz_buf; + int i; + + /* Create a few object variables we'll need later on */ + var_Create( p_vout, "snapshot-path", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); + var_Create( p_vout, "snapshot-prefix", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); + var_Create( p_vout, "snapshot-format", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); + var_Create( p_vout, "snapshot-preview", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); + var_Create( p_vout, "snapshot-sequential", + VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); + var_Create( p_vout, "snapshot-num", VLC_VAR_INTEGER ); + var_SetInteger( p_vout, "snapshot-num", 1 ); + var_Create( p_vout, "snapshot-width", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); + var_Create( p_vout, "snapshot-height", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); + + var_Create( p_vout, "width", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); + var_Create( p_vout, "height", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); + p_vout->i_alignment = var_CreateGetInteger( p_vout, "align" ); + + var_Create( p_vout, "video-x", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); + var_Create( p_vout, "video-y", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); + + var_Create( p_vout, "mouse-hide-timeout", + VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); + + p_vout->b_title_show = var_CreateGetBool( p_vout, "video-title-show" ); + p_vout->i_title_timeout = + (mtime_t)var_CreateGetInteger( p_vout, "video-title-timeout" ); + p_vout->i_title_position = + var_CreateGetInteger( p_vout, "video-title-position" ); + + var_AddCallback( p_vout, "video-title-show", TitleShowCallback, NULL ); + var_AddCallback( p_vout, "video-title-timeout", TitleTimeoutCallback, NULL ); + var_AddCallback( p_vout, "video-title-position", TitlePositionCallback, NULL ); + + /* Zoom object var */ + var_Create( p_vout, "zoom", VLC_VAR_FLOAT | VLC_VAR_ISCOMMAND | + VLC_VAR_HASCHOICE | VLC_VAR_DOINHERIT ); + + text.psz_string = _("Zoom"); + var_Change( p_vout, "zoom", VLC_VAR_SETTEXT, &text, NULL ); + + var_Get( p_vout, "zoom", &old_val ); + + for( i = 0; p_zoom_values[i].f_value; i++ ) + { + if( old_val.f_float == p_zoom_values[i].f_value ) + var_Change( p_vout, "zoom", VLC_VAR_DELCHOICE, &old_val, NULL ); + val.f_float = p_zoom_values[i].f_value; + text.psz_string = _( p_zoom_values[i].psz_label ); + var_Change( p_vout, "zoom", VLC_VAR_ADDCHOICE, &val, &text ); + } + + var_Set( p_vout, "zoom", old_val ); /* Is this really needed? */ + + var_AddCallback( p_vout, "zoom", ZoomCallback, NULL ); + + /* Crop offset vars */ + var_Create( p_vout, "crop-left", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND ); + var_Create( p_vout, "crop-top", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND ); + var_Create( p_vout, "crop-right", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND ); + var_Create( p_vout, "crop-bottom", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND ); + + var_AddCallback( p_vout, "crop-left", CropCallback, NULL ); + var_AddCallback( p_vout, "crop-top", CropCallback, NULL ); + var_AddCallback( p_vout, "crop-right", CropCallback, NULL ); + var_AddCallback( p_vout, "crop-bottom", CropCallback, NULL ); + + /* Crop object var */ + var_Create( p_vout, "crop", VLC_VAR_STRING | VLC_VAR_ISCOMMAND | + VLC_VAR_HASCHOICE | VLC_VAR_DOINHERIT ); + + text.psz_string = _("Crop"); + var_Change( p_vout, "crop", VLC_VAR_SETTEXT, &text, NULL ); + + val.psz_string = (char*)""; + var_Change( p_vout, "crop", VLC_VAR_DELCHOICE, &val, 0 ); + + for( i = 0; p_crop_values[i].psz_value; i++ ) + { + val.psz_string = (char*)p_crop_values[i].psz_value; + text.psz_string = _( p_crop_values[i].psz_label ); + var_Change( p_vout, "crop", VLC_VAR_ADDCHOICE, &val, &text ); + } + + /* update triggered every time the vout's crop parameters are changed */ + var_Create( p_vout, "crop-update", VLC_VAR_VOID ); + + /* Add custom crop ratios */ + psz_buf = config_GetPsz( p_vout, "custom-crop-ratios" ); + AddCustomRatios( p_vout, "crop", psz_buf ); + free( psz_buf ); + + var_AddCallback( p_vout, "crop", CropCallback, NULL ); + var_Get( p_vout, "crop", &old_val ); + if( old_val.psz_string && *old_val.psz_string ) + var_Change( p_vout, "crop", VLC_VAR_TRIGGER_CALLBACKS, 0, 0 ); + free( old_val.psz_string ); + + /* Monitor pixel aspect-ratio */ + var_Create( p_vout, "monitor-par", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); + var_Get( p_vout, "monitor-par", &val ); + if( val.psz_string && *val.psz_string ) + { + char *psz_parser = strchr( val.psz_string, ':' ); + unsigned int i_aspect_num = 0, i_aspect_den = 0; + float i_aspect = 0; + if( psz_parser ) + { + i_aspect_num = strtol( val.psz_string, 0, 10 ); + i_aspect_den = strtol( ++psz_parser, 0, 10 ); + } + else + { + i_aspect = atof( val.psz_string ); + vlc_ureduce( &i_aspect_num, &i_aspect_den, + i_aspect *VOUT_ASPECT_FACTOR, VOUT_ASPECT_FACTOR, 0 ); + } + if( !i_aspect_num || !i_aspect_den ) i_aspect_num = i_aspect_den = 1; + + p_vout->i_par_num = i_aspect_num; + p_vout->i_par_den = i_aspect_den; + + vlc_ureduce( &p_vout->i_par_num, &p_vout->i_par_den, + p_vout->i_par_num, p_vout->i_par_den, 0 ); + + msg_Dbg( p_vout, "overriding monitor pixel aspect-ratio: %i:%i", + p_vout->i_par_num, p_vout->i_par_den ); + b_force_par = true; + } + free( val.psz_string ); + + /* Aspect-ratio object var */ + var_Create( p_vout, "aspect-ratio", VLC_VAR_STRING | VLC_VAR_ISCOMMAND | + VLC_VAR_HASCHOICE | VLC_VAR_DOINHERIT ); + + text.psz_string = _("Aspect-ratio"); + var_Change( p_vout, "aspect-ratio", VLC_VAR_SETTEXT, &text, NULL ); + + val.psz_string = (char*)""; + var_Change( p_vout, "aspect-ratio", VLC_VAR_DELCHOICE, &val, 0 ); + + for( i = 0; p_aspect_ratio_values[i].psz_value; i++ ) + { + val.psz_string = (char*)p_aspect_ratio_values[i].psz_value; + text.psz_string = _( p_aspect_ratio_values[i].psz_label ); + var_Change( p_vout, "aspect-ratio", VLC_VAR_ADDCHOICE, &val, &text ); + } + + /* Add custom aspect ratios */ + psz_buf = config_GetPsz( p_vout, "custom-aspect-ratios" ); + AddCustomRatios( p_vout, "aspect-ratio", psz_buf ); + free( psz_buf ); + + var_AddCallback( p_vout, "aspect-ratio", AspectCallback, NULL ); + var_Get( p_vout, "aspect-ratio", &old_val ); + if( (old_val.psz_string && *old_val.psz_string) || b_force_par ) + var_Change( p_vout, "aspect-ratio", VLC_VAR_TRIGGER_CALLBACKS, 0, 0 ); + free( old_val.psz_string ); + + /* Initialize the dimensions of the video window */ + InitWindowSize( p_vout, &p_vout->i_window_width, + &p_vout->i_window_height ); + + /* Add a variable to indicate if the window should be on top of others */ + var_Create( p_vout, "video-on-top", VLC_VAR_BOOL | VLC_VAR_DOINHERIT + | VLC_VAR_ISCOMMAND ); + text.psz_string = _("Always on top"); + var_Change( p_vout, "video-on-top", VLC_VAR_SETTEXT, &text, NULL ); + var_AddCallback( p_vout, "video-on-top", OnTopCallback, NULL ); + + /* Add a variable to indicate whether we want window decoration or not */ + var_Create( p_vout, "video-deco", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); + + /* Add a fullscreen variable */ + if( var_CreateGetBoolCommand( p_vout, "fullscreen" ) ) + { + /* user requested fullscreen */ + p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE; + } + text.psz_string = _("Fullscreen"); + var_Change( p_vout, "fullscreen", VLC_VAR_SETTEXT, &text, NULL ); + var_AddCallback( p_vout, "fullscreen", FullscreenCallback, NULL ); + + /* Add a snapshot variable */ + var_Create( p_vout, "video-snapshot", VLC_VAR_VOID | VLC_VAR_ISCOMMAND ); + text.psz_string = _("Snapshot"); + var_Change( p_vout, "video-snapshot", VLC_VAR_SETTEXT, &text, NULL ); + var_AddCallback( p_vout, "video-snapshot", SnapshotCallback, NULL ); + + /* Mouse coordinates */ + var_Create( p_vout, "mouse-x", VLC_VAR_INTEGER ); + var_Create( p_vout, "mouse-y", VLC_VAR_INTEGER ); + var_Create( p_vout, "mouse-button-down", VLC_VAR_INTEGER ); + var_Create( p_vout, "mouse-moved", VLC_VAR_BOOL ); + var_Create( p_vout, "mouse-clicked", VLC_VAR_INTEGER ); + + var_Create( p_vout, "intf-change", VLC_VAR_BOOL ); + var_SetBool( p_vout, "intf-change", true ); +} + +/***************************************************************************** + * vout_Snapshot: generates a snapshot. + *****************************************************************************/ +int vout_Snapshot( vout_thread_t *p_vout, picture_t *p_pic ) +{ + image_handler_t *p_image = image_HandlerCreate( p_vout ); + video_format_t fmt_in, fmt_out; + char *psz_filename = NULL; + subpicture_t *p_subpic; + picture_t *p_pif; + vlc_value_t val, format; + DIR *path; + int i_ret; + + memset( &fmt_in, 0, sizeof(video_format_t) ); + memset( &fmt_out, 0, sizeof(video_format_t) ); + + var_Get( p_vout, "snapshot-path", &val ); + if( val.psz_string && !*val.psz_string ) + { + free( val.psz_string ); + val.psz_string = 0; + } + + /* Embedded snapshot : if snapshot-path == object:object-id, then + create a snapshot_t* and store it in + object(object-id)->p_private, then unlock and signal the + waiting object. + */ + if( val.psz_string && !strncmp( val.psz_string, "object:", 7 ) ) + { + int i_id; + vlc_object_t* p_dest; + block_t *p_block; + snapshot_t *p_snapshot; + int i_size; + + /* Destination object-id is following object: */ + i_id = atoi( &val.psz_string[7] ); + p_dest = ( vlc_object_t* )vlc_object_get( i_id ); + if( !p_dest ) + { + msg_Err( p_vout, "Cannot find calling object" ); + image_HandlerDelete( p_image ); + return VLC_EGENERIC; + } + /* Object must be locked. We will unlock it once we get the + snapshot and written it to p_private */ + p_dest->p_private = NULL; + + /* Save the snapshot to a memory zone */ + fmt_in = p_vout->fmt_in; + fmt_out.i_sar_num = fmt_out.i_sar_den = 1; + fmt_out.i_chroma = VLC_FOURCC( 'p','n','g',' ' ); + + fmt_out.i_width = var_GetInteger( p_vout, "snapshot-width" ); + fmt_out.i_height = var_GetInteger( p_vout, "snapshot-height" ); + /* If snapshot-width and/or snapshot height were not specified, + use a default snapshot width of 320 */ + if( fmt_out.i_width == 0 && fmt_out.i_height == 0 ) + { + fmt_out.i_width = 320; + } + + if( fmt_out.i_width == 0 && fmt_out.i_height > 0 ) + { + fmt_out.i_width = (fmt_in.i_width * fmt_out.i_height) / fmt_in.i_height; + } + else if( fmt_out.i_height == 0 && fmt_out.i_width > 0 ) + { + fmt_out.i_height = (fmt_in.i_height * fmt_out.i_width) / fmt_in.i_width; + } + else + { + fmt_out.i_width = fmt_in.i_width; + fmt_out.i_height = fmt_in.i_height; + } + + p_block = ( block_t* ) image_Write( p_image, p_pic, &fmt_in, &fmt_out ); + if( !p_block ) + { + msg_Err( p_vout, "Could not get snapshot" ); + image_HandlerDelete( p_image ); + vlc_object_signal( p_dest ); + vlc_object_release( p_dest ); + return VLC_EGENERIC; + } + + /* Copy the p_block data to a snapshot structure */ + /* FIXME: get the timestamp */ + p_snapshot = ( snapshot_t* ) malloc( sizeof( snapshot_t ) ); + if( !p_snapshot ) + { + block_Release( p_block ); + image_HandlerDelete( p_image ); + vlc_object_signal( p_dest ); + vlc_object_release( p_dest ); + return VLC_ENOMEM; + } + + i_size = p_block->i_buffer; + + p_snapshot->i_width = fmt_out.i_width; + p_snapshot->i_height = fmt_out.i_height; + p_snapshot->i_datasize = i_size; + p_snapshot->date = p_block->i_pts; /* FIXME ?? */ + p_snapshot->p_data = ( char* ) malloc( i_size ); + if( !p_snapshot->p_data ) + { + block_Release( p_block ); + free( p_snapshot ); + image_HandlerDelete( p_image ); + vlc_object_signal( p_dest ); + vlc_object_release( p_dest ); + return VLC_ENOMEM; + } + memcpy( p_snapshot->p_data, p_block->p_buffer, p_block->i_buffer ); + + p_dest->p_private = p_snapshot; + + block_Release( p_block ); + + /* Unlock the object */ + vlc_object_signal( p_dest ); + vlc_object_release( p_dest ); + + image_HandlerDelete( p_image ); + return VLC_SUCCESS; + } + +#if defined(__APPLE__) || defined(SYS_BEOS) + if( !val.psz_string ) + { + if( asprintf( &val.psz_string, "%s/Desktop", + config_GetHomeDir() ) == -1 ) + val.psz_string = NULL; + } + +#elif defined(WIN32) && !defined(UNDER_CE) + if( !val.psz_string ) + { + /* Get the My Pictures folder path */ + + char *p_mypicturesdir = NULL; + typedef HRESULT (WINAPI *SHGETFOLDERPATH)( HWND, int, HANDLE, DWORD, + LPWSTR ); + #ifndef CSIDL_FLAG_CREATE + # define CSIDL_FLAG_CREATE 0x8000 + #endif + #ifndef CSIDL_MYPICTURES + # define CSIDL_MYPICTURES 0x27 + #endif + #ifndef SHGFP_TYPE_CURRENT + # define SHGFP_TYPE_CURRENT 0 + #endif + + HINSTANCE shfolder_dll; + SHGETFOLDERPATH SHGetFolderPath ; + + /* load the shfolder dll to retrieve SHGetFolderPath */ + if( ( shfolder_dll = LoadLibrary( _T("SHFolder.dll") ) ) != NULL ) + { + wchar_t wdir[PATH_MAX]; + SHGetFolderPath = (void *)GetProcAddress( shfolder_dll, + _T("SHGetFolderPathW") ); + if ((SHGetFolderPath != NULL ) + && SUCCEEDED (SHGetFolderPath (NULL, + CSIDL_MYPICTURES | CSIDL_FLAG_CREATE, + NULL, SHGFP_TYPE_CURRENT, + wdir))) + p_mypicturesdir = FromWide (wdir); + + FreeLibrary( shfolder_dll ); + } + + if( p_mypicturesdir == NULL ) + val.psz_string = strdup( config_GetHomeDir() ); + else + val.psz_string = p_mypicturesdir; + } + +#else + /* XXX: This saves in the data directory. Shouldn't we try saving + * to psz_homedir/Desktop or something nicer ? */ + char *psz_datadir = config_GetUserDataDir(); + if( !val.psz_string && psz_datadir ) + { + if( asprintf( &val.psz_string, "%s", psz_datadir ) == -1 ) + val.psz_string = NULL; + } + free( psz_datadir ); +#endif + + if( !val.psz_string ) + { + msg_Err( p_vout, "no path specified for snapshots" ); + image_HandlerDelete( p_image ); + return VLC_EGENERIC; + } + var_Get( p_vout, "snapshot-format", &format ); + if( !format.psz_string || !*format.psz_string ) + { + free( format.psz_string ); + format.psz_string = strdup( "png" ); + } + + /* + * Did the user specify a directory? If not, path = NULL. + */ + path = utf8_opendir ( (const char *)val.psz_string ); + if( path != NULL ) + { + char *psz_prefix = var_GetNonEmptyString( p_vout, "snapshot-prefix" ); + if( psz_prefix == NULL ) + psz_prefix = strdup( "vlcsnap-" ); + else + { + char *psz_tmp = str_format( p_vout, psz_prefix ); + filename_sanitize( psz_tmp ); + free( psz_prefix ); + psz_prefix = psz_tmp; + } + + closedir( path ); + if( var_GetBool( p_vout, "snapshot-sequential" ) == true ) + { + int i_num = var_GetInteger( p_vout, "snapshot-num" ); + FILE *p_file; + do + { + if( asprintf( &psz_filename, "%s" DIR_SEP "%s%05d.%s", + val.psz_string, psz_prefix, i_num++, + format.psz_string ) == -1 ) + { + msg_Err( p_vout, "could not create snapshot" ); + image_HandlerDelete( p_image ); + return VLC_EGENERIC; + } + } + while( ( p_file = utf8_fopen( psz_filename, "r" ) ) && !fclose( p_file ) ); + var_SetInteger( p_vout, "snapshot-num", i_num ); + } + else + { + if( asprintf( &psz_filename, "%s" DIR_SEP "%s%u.%s", + val.psz_string, psz_prefix, + (unsigned int)(p_pic->date / 100000) & 0xFFFFFF, + format.psz_string ) == -1 ) + { + msg_Err( p_vout, "could not create snapshot" ); + image_HandlerDelete( p_image ); + return VLC_EGENERIC; + } + } + + free( psz_prefix ); + } + else // The user specified a full path name (including file name) + { + psz_filename = str_format( p_vout, val.psz_string ); + path_sanitize( psz_filename ); + } + + free( val.psz_string ); + free( format.psz_string ); + + fmt_out.i_width = var_GetInteger( p_vout, "snapshot-width" ); + fmt_out.i_height = var_GetInteger( p_vout, "snapshot-height" ); + + fmt_in = p_vout->fmt_in; + + if( fmt_out.i_width == 0 && fmt_out.i_height > 0 ) + { + fmt_out.i_width = (fmt_in.i_width * fmt_out.i_height) / fmt_in.i_height; + } + else if( fmt_out.i_height == 0 && fmt_out.i_width > 0 ) + { + fmt_out.i_height = (fmt_in.i_height * fmt_out.i_width) / fmt_in.i_width; + } + else + { + fmt_out.i_width = fmt_in.i_width; + fmt_out.i_height = fmt_in.i_height; + } + + /* Save the snapshot */ + fmt_out.i_sar_num = fmt_out.i_sar_den = 1; + i_ret = image_WriteUrl( p_image, p_pic, &fmt_in, &fmt_out, psz_filename ); + if( i_ret != VLC_SUCCESS ) + { + msg_Err( p_vout, "could not create snapshot %s", psz_filename ); + free( psz_filename ); + image_HandlerDelete( p_image ); + return VLC_EGENERIC; + } + + msg_Dbg( p_vout, "snapshot taken (%s)", psz_filename ); + vout_OSDMessage( VLC_OBJECT( p_vout ), DEFAULT_CHAN, + "%s", psz_filename ); + free( psz_filename ); + + if( var_GetBool( p_vout, "snapshot-preview" ) ) + { + /* Inject a subpicture with the snapshot */ + memset( &fmt_out, 0, sizeof(fmt_out) ); + fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A'); + p_pif = image_Convert( p_image, p_pic, &fmt_in, &fmt_out ); + image_HandlerDelete( p_image ); + if( !p_pif ) return VLC_EGENERIC; + + p_subpic = spu_CreateSubpicture( p_vout->p_spu ); + if( p_subpic == NULL ) + { + p_pif->pf_release( p_pif ); + return VLC_EGENERIC; + } + + p_subpic->i_channel = 0; + p_subpic->i_start = mdate(); + p_subpic->i_stop = mdate() + 4000000; + p_subpic->b_ephemer = true; + p_subpic->b_fade = true; + p_subpic->i_original_picture_width = p_vout->render.i_width * 4; + p_subpic->i_original_picture_height = p_vout->render.i_height * 4; + + p_subpic->p_region = spu_CreateRegion( p_vout->p_spu, &fmt_out ); + vout_CopyPicture( p_image->p_parent, &p_subpic->p_region->picture, + p_pif ); + p_pif->pf_release( p_pif ); + + spu_DisplaySubpicture( p_vout->p_spu, p_subpic ); + } + else + { + image_HandlerDelete( p_image ); + } + + return VLC_SUCCESS; +} + +/***************************************************************************** + * Handle filters + *****************************************************************************/ + +void vout_EnableFilter( vout_thread_t *p_vout, char *psz_name, + bool b_add, bool b_setconfig ) +{ + char *psz_parser; + char *psz_string = config_GetPsz( p_vout, "vout-filter" ); + + /* Todo : Use some generic chain manipulation functions */ + if( !psz_string ) psz_string = strdup(""); + + psz_parser = strstr( psz_string, psz_name ); + if( b_add ) + { + if( !psz_parser ) + { + psz_parser = psz_string; + if( asprintf( &psz_string, (*psz_string) ? "%s:%s" : "%s%s", + psz_string, psz_name ) == -1 ) + { + free( psz_parser ); + return; + } + free( psz_parser ); + } + else + return; + } + else + { + if( psz_parser ) + { + memmove( psz_parser, psz_parser + strlen(psz_name) + + (*(psz_parser + strlen(psz_name)) == ':' ? 1 : 0 ), + strlen(psz_parser + strlen(psz_name)) + 1 ); + + /* Remove trailing : : */ + if( *(psz_string+strlen(psz_string ) -1 ) == ':' ) + { + *(psz_string+strlen(psz_string ) -1 ) = '\0'; + } + } + else + { + free( psz_string ); + return; + } + } + if( b_setconfig ) + config_PutPsz( p_vout, "vout-filter", psz_string ); + + var_SetString( p_vout, "vout-filter", psz_string ); + free( psz_string ); +} + +/***************************************************************************** + * vout_ControlDefault: default methods for video output control. + *****************************************************************************/ +int vout_vaControlDefault( vout_thread_t *p_vout, int i_query, va_list args ) +{ + (void)args; + switch( i_query ) + { + case VOUT_REPARENT: + case VOUT_CLOSE: + vout_ReleaseWindow( p_vout, NULL ); + return VLC_SUCCESS; + + case VOUT_SNAPSHOT: + p_vout->b_snapshot = true; + return VLC_SUCCESS; + + default: + msg_Dbg( p_vout, "control query not supported" ); + } + return VLC_EGENERIC; +} + +/***************************************************************************** + * InitWindowSize: find the initial dimensions the video window should have. + ***************************************************************************** + * This function will check the "width", "height" and "zoom" config options and + * will calculate the size that the video window should have. + *****************************************************************************/ +static void InitWindowSize( vout_thread_t *p_vout, unsigned *pi_width, + unsigned *pi_height ) +{ + vlc_value_t val; + int i_width, i_height; + uint64_t ll_zoom; + +#define FP_FACTOR 1000 /* our fixed point factor */ + + var_Get( p_vout, "width", &val ); + i_width = val.i_int; + var_Get( p_vout, "height", &val ); + i_height = val.i_int; + var_Get( p_vout, "zoom", &val ); + ll_zoom = (uint64_t)( FP_FACTOR * val.f_float ); + + if( i_width > 0 && i_height > 0) + { + *pi_width = (int)( i_width * ll_zoom / FP_FACTOR ); + *pi_height = (int)( i_height * ll_zoom / FP_FACTOR ); + goto initwsize_end; + } + else if( i_width > 0 ) + { + *pi_width = (int)( i_width * ll_zoom / FP_FACTOR ); + *pi_height = (int)( p_vout->fmt_in.i_visible_height * ll_zoom * + p_vout->fmt_in.i_sar_den * i_width / p_vout->fmt_in.i_sar_num / + FP_FACTOR / p_vout->fmt_in.i_visible_width ); + goto initwsize_end; + } + else if( i_height > 0 ) + { + *pi_height = (int)( i_height * ll_zoom / FP_FACTOR ); + *pi_width = (int)( p_vout->fmt_in.i_visible_width * ll_zoom * + p_vout->fmt_in.i_sar_num * i_height / p_vout->fmt_in.i_sar_den / + FP_FACTOR / p_vout->fmt_in.i_visible_height ); + goto initwsize_end; + } + + if( p_vout->fmt_in.i_sar_num == 0 || p_vout->fmt_in.i_sar_den == 0 ) { + msg_Warn( p_vout, "fucked up aspect" ); + *pi_width = (int)( p_vout->fmt_in.i_visible_width * ll_zoom / FP_FACTOR ); + *pi_height = (int)( p_vout->fmt_in.i_visible_height * ll_zoom /FP_FACTOR); + } + else if( p_vout->fmt_in.i_sar_num >= p_vout->fmt_in.i_sar_den ) + { + *pi_width = (int)( p_vout->fmt_in.i_visible_width * ll_zoom * + p_vout->fmt_in.i_sar_num / p_vout->fmt_in.i_sar_den / FP_FACTOR ); + *pi_height = (int)( p_vout->fmt_in.i_visible_height * ll_zoom + / FP_FACTOR ); + } + else + { + *pi_width = (int)( p_vout->fmt_in.i_visible_width * ll_zoom + / FP_FACTOR ); + *pi_height = (int)( p_vout->fmt_in.i_visible_height * ll_zoom * + p_vout->fmt_in.i_sar_den / p_vout->fmt_in.i_sar_num / FP_FACTOR ); + } + +initwsize_end: + msg_Dbg( p_vout, "window size: %dx%d", p_vout->i_window_width, + p_vout->i_window_height ); + +#undef FP_FACTOR +} + +/***************************************************************************** + * Object variables callbacks + *****************************************************************************/ +static int ZoomCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) +{ + vout_thread_t *p_vout = (vout_thread_t *)p_this; + (void)psz_cmd; (void)oldval; (void)newval; (void)p_data; + InitWindowSize( p_vout, &p_vout->i_window_width, + &p_vout->i_window_height ); + vout_Control( p_vout, VOUT_SET_SIZE, p_vout->i_window_width, + p_vout->i_window_height ); + return VLC_SUCCESS; +} + +static int CropCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) +{ + vout_thread_t *p_vout = (vout_thread_t *)p_this; + int64_t i_aspect_num, i_aspect_den; + unsigned int i_width, i_height; + + (void)oldval; (void)p_data; + + /* Restore defaults */ + p_vout->fmt_in.i_x_offset = p_vout->fmt_render.i_x_offset; + p_vout->fmt_in.i_visible_width = p_vout->fmt_render.i_visible_width; + p_vout->fmt_in.i_y_offset = p_vout->fmt_render.i_y_offset; + p_vout->fmt_in.i_visible_height = p_vout->fmt_render.i_visible_height; + + if( !strcmp( psz_cmd, "crop" ) ) + { + char *psz_end = NULL, *psz_parser = strchr( newval.psz_string, ':' ); + if( psz_parser ) + { + /* We're using the 3:4 syntax */ + i_aspect_num = strtol( newval.psz_string, &psz_end, 10 ); + if( psz_end == newval.psz_string || !i_aspect_num ) goto crop_end; + + i_aspect_den = strtol( ++psz_parser, &psz_end, 10 ); + if( psz_end == psz_parser || !i_aspect_den ) goto crop_end; + + i_width = p_vout->fmt_in.i_sar_den*p_vout->fmt_render.i_visible_height * + i_aspect_num / i_aspect_den / p_vout->fmt_in.i_sar_num; + i_height = p_vout->fmt_render.i_visible_width*p_vout->fmt_in.i_sar_num * + i_aspect_den / i_aspect_num / p_vout->fmt_in.i_sar_den; + + if( i_width < p_vout->fmt_render.i_visible_width ) + { + p_vout->fmt_in.i_x_offset = p_vout->fmt_render.i_x_offset + + (p_vout->fmt_render.i_visible_width - i_width) / 2; + p_vout->fmt_in.i_visible_width = i_width; + } + else + { + p_vout->fmt_in.i_y_offset = p_vout->fmt_render.i_y_offset + + (p_vout->fmt_render.i_visible_height - i_height) / 2; + p_vout->fmt_in.i_visible_height = i_height; + } + } + else + { + psz_parser = strchr( newval.psz_string, 'x' ); + if( psz_parser ) + { + /* Maybe we're using the x++ syntax */ + unsigned int i_crop_width, i_crop_height, i_crop_top, i_crop_left; + + i_crop_width = strtol( newval.psz_string, &psz_end, 10 ); + if( psz_end != psz_parser ) goto crop_end; + + psz_parser = strchr( ++psz_end, '+' ); + i_crop_height = strtol( psz_end, &psz_end, 10 ); + if( psz_end != psz_parser ) goto crop_end; + + psz_parser = strchr( ++psz_end, '+' ); + i_crop_left = strtol( psz_end, &psz_end, 10 ); + if( psz_end != psz_parser ) goto crop_end; + + psz_end++; + i_crop_top = strtol( psz_end, &psz_end, 10 ); + if( *psz_end != '\0' ) goto crop_end; + + i_width = i_crop_width; + p_vout->fmt_in.i_visible_width = i_width; + + i_height = i_crop_height; + p_vout->fmt_in.i_visible_height = i_height; + + p_vout->fmt_in.i_x_offset = i_crop_left; + p_vout->fmt_in.i_y_offset = i_crop_top; + } + else + { + /* Maybe we're using the +++ syntax */ + unsigned int i_crop_top, i_crop_left, i_crop_bottom, i_crop_right; + + psz_parser = strchr( newval.psz_string, '+' ); + i_crop_left = strtol( newval.psz_string, &psz_end, 10 ); + if( psz_end != psz_parser ) goto crop_end; + + psz_parser = strchr( ++psz_end, '+' ); + i_crop_top = strtol( psz_end, &psz_end, 10 ); + if( psz_end != psz_parser ) goto crop_end; + + psz_parser = strchr( ++psz_end, '+' ); + i_crop_right = strtol( psz_end, &psz_end, 10 ); + if( psz_end != psz_parser ) goto crop_end; + + psz_end++; + i_crop_bottom = strtol( psz_end, &psz_end, 10 ); + if( *psz_end != '\0' ) goto crop_end; + + i_width = p_vout->fmt_render.i_visible_width + - i_crop_left - i_crop_right; + p_vout->fmt_in.i_visible_width = i_width; + + i_height = p_vout->fmt_render.i_visible_height + - i_crop_top - i_crop_bottom; + p_vout->fmt_in.i_visible_height = i_height; + + p_vout->fmt_in.i_x_offset = i_crop_left; + p_vout->fmt_in.i_y_offset = i_crop_top; + } + } + } + else if( !strcmp( psz_cmd, "crop-top" ) + || !strcmp( psz_cmd, "crop-left" ) + || !strcmp( psz_cmd, "crop-bottom" ) + || !strcmp( psz_cmd, "crop-right" ) ) + { + unsigned int i_crop_top, i_crop_left, i_crop_bottom, i_crop_right; + + i_crop_top = var_GetInteger( p_vout, "crop-top" ); + i_crop_left = var_GetInteger( p_vout, "crop-left" ); + i_crop_right = var_GetInteger( p_vout, "crop-right" ); + i_crop_bottom = var_GetInteger( p_vout, "crop-bottom" ); + + i_width = p_vout->fmt_render.i_visible_width + - i_crop_left - i_crop_right; + p_vout->fmt_in.i_visible_width = i_width; + + i_height = p_vout->fmt_render.i_visible_height + - i_crop_top - i_crop_bottom; + p_vout->fmt_in.i_visible_height = i_height; + + p_vout->fmt_in.i_x_offset = i_crop_left; + p_vout->fmt_in.i_y_offset = i_crop_top; + } + + crop_end: + InitWindowSize( p_vout, &p_vout->i_window_width, + &p_vout->i_window_height ); + + p_vout->i_changes |= VOUT_CROP_CHANGE; + + msg_Dbg( p_vout, "cropping picture %ix%i to %i,%i,%ix%i", + p_vout->fmt_in.i_width, p_vout->fmt_in.i_height, + p_vout->fmt_in.i_x_offset, p_vout->fmt_in.i_y_offset, + p_vout->fmt_in.i_visible_width, + p_vout->fmt_in.i_visible_height ); + + var_SetVoid( p_vout, "crop-update" ); + + return VLC_SUCCESS; +} + +static int AspectCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) +{ + vout_thread_t *p_vout = (vout_thread_t *)p_this; + unsigned int i_aspect_num, i_aspect_den, i_sar_num, i_sar_den; + vlc_value_t val; + + char *psz_end, *psz_parser = strchr( newval.psz_string, ':' ); + (void)psz_cmd; (void)oldval; (void)p_data; + + /* Restore defaults */ + p_vout->fmt_in.i_sar_num = p_vout->fmt_render.i_sar_num; + p_vout->fmt_in.i_sar_den = p_vout->fmt_render.i_sar_den; + p_vout->fmt_in.i_aspect = p_vout->fmt_render.i_aspect; + p_vout->render.i_aspect = p_vout->fmt_render.i_aspect; + + if( !psz_parser ) goto aspect_end; + + i_aspect_num = strtol( newval.psz_string, &psz_end, 10 ); + if( psz_end == newval.psz_string || !i_aspect_num ) goto aspect_end; + + i_aspect_den = strtol( ++psz_parser, &psz_end, 10 ); + if( psz_end == psz_parser || !i_aspect_den ) goto aspect_end; + + i_sar_num = i_aspect_num * p_vout->fmt_render.i_visible_height; + i_sar_den = i_aspect_den * p_vout->fmt_render.i_visible_width; + vlc_ureduce( &i_sar_num, &i_sar_den, i_sar_num, i_sar_den, 0 ); + p_vout->fmt_in.i_sar_num = i_sar_num; + p_vout->fmt_in.i_sar_den = i_sar_den; + p_vout->fmt_in.i_aspect = i_aspect_num * VOUT_ASPECT_FACTOR / i_aspect_den; + p_vout->render.i_aspect = p_vout->fmt_in.i_aspect; + + aspect_end: + if( p_vout->i_par_num && p_vout->i_par_den ) + { + p_vout->fmt_in.i_sar_num *= p_vout->i_par_den; + p_vout->fmt_in.i_sar_den *= p_vout->i_par_num; + p_vout->fmt_in.i_aspect = p_vout->fmt_in.i_aspect * + p_vout->i_par_den / p_vout->i_par_num; + p_vout->render.i_aspect = p_vout->fmt_in.i_aspect; + } + + p_vout->i_changes |= VOUT_ASPECT_CHANGE; + + vlc_ureduce( &i_aspect_num, &i_aspect_den, + p_vout->fmt_in.i_aspect, VOUT_ASPECT_FACTOR, 0 ); + msg_Dbg( p_vout, "new aspect-ratio %i:%i, sample aspect-ratio %i:%i", + i_aspect_num, i_aspect_den, + p_vout->fmt_in.i_sar_num, p_vout->fmt_in.i_sar_den ); + + if( var_Get( p_vout, "crop", &val ) ) + return VLC_EGENERIC; + + int i_ret = CropCallback( p_this, "crop", val, val, 0 ); + free( val.psz_string ); + return i_ret; +} + +static int OnTopCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) +{ + vout_thread_t *p_vout = (vout_thread_t *)p_this; + vout_Control( p_vout, VOUT_SET_STAY_ON_TOP, newval.b_bool ); + (void)psz_cmd; (void)oldval; (void)p_data; + + /* Modify libvlc as well because the vout might have to be restarted */ + var_Create( p_vout->p_libvlc, "video-on-top", VLC_VAR_BOOL ); + var_Set( p_vout->p_libvlc, "video-on-top", newval ); + + return VLC_SUCCESS; +} + +static int FullscreenCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) +{ + vout_thread_t *p_vout = (vout_thread_t *)p_this; + vlc_value_t val; + (void)psz_cmd; (void)oldval; (void)p_data; + + p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE; + + /* Modify libvlc as well because the vout might have to be restarted */ + var_Create( p_vout->p_libvlc, "fullscreen", VLC_VAR_BOOL ); + var_Set( p_vout->p_libvlc, "fullscreen", newval ); + + val.b_bool = true; + var_Set( p_vout, "intf-change", val ); + return VLC_SUCCESS; +} + +static int SnapshotCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) +{ + VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); + VLC_UNUSED(newval); VLC_UNUSED(p_data); + vout_thread_t *p_vout = (vout_thread_t *)p_this; + vout_Control( p_vout, VOUT_SNAPSHOT ); + return VLC_SUCCESS; +} + +static int TitleShowCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) +{ + VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); + VLC_UNUSED(p_data); + vout_thread_t *p_vout = (vout_thread_t *)p_this; + p_vout->b_title_show = newval.b_bool; + return VLC_SUCCESS; +} + +static int TitleTimeoutCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) +{ + VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data); + vout_thread_t *p_vout = (vout_thread_t *)p_this; + p_vout->i_title_timeout = (mtime_t) newval.i_int; + return VLC_SUCCESS; +} + +static int TitlePositionCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) +{ + VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); + VLC_UNUSED(p_data); + vout_thread_t *p_vout = (vout_thread_t *)p_this; + p_vout->i_title_position = newval.i_int; + return VLC_SUCCESS; +} diff --git a/VLC/vout_pictures.c b/VLC/vout_pictures.c new file mode 100644 index 0000000..487a121 --- /dev/null +++ b/VLC/vout_pictures.c @@ -0,0 +1,1095 @@ +/***************************************************************************** + * vout_pictures.c : picture management functions + ***************************************************************************** + * Copyright (C) 2000-2004 the VideoLAN team + * $Id$ + * + * Authors: Vincent Seguin + * Samuel Hocevar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "vlc_vout.h" +#include "vlc_osd.h" +#include "vlc_filter.h" +#include "vout_pictures.h" + +#include + +/** + * Display a picture + * + * Remove the reservation flag of a picture, which will cause it to be ready + * for display. The picture won't be displayed until vout_DatePicture has been + * called. + */ +void vout_DisplayPicture( vout_thread_t *p_vout, picture_t *p_pic ) +{ + vlc_mutex_lock( &p_vout->picture_lock ); + switch( p_pic->i_status ) + { + case RESERVED_PICTURE: + p_pic->i_status = RESERVED_DISP_PICTURE; + break; + case RESERVED_DATED_PICTURE: + p_pic->i_status = READY_PICTURE; + break; + default: + msg_Err( p_vout, "picture to display %p has invalid status %d", + p_pic, p_pic->i_status ); + break; + } + + vlc_mutex_unlock( &p_vout->picture_lock ); +} + +/** + * Date a picture + * + * Remove the reservation flag of a picture, which will cause it to be ready + * for display. The picture won't be displayed until vout_DisplayPicture has + * been called. + * \param p_vout The vout in question + * \param p_pic The picture to date + * \param date The date to display the picture + */ +void vout_DatePicture( vout_thread_t *p_vout, + picture_t *p_pic, mtime_t date ) +{ + vlc_mutex_lock( &p_vout->picture_lock ); + p_pic->date = date; + switch( p_pic->i_status ) + { + case RESERVED_PICTURE: + p_pic->i_status = RESERVED_DATED_PICTURE; + break; + case RESERVED_DISP_PICTURE: + p_pic->i_status = READY_PICTURE; + break; + default: + msg_Err( p_vout, "picture to date %p has invalid status %d", + p_pic, p_pic->i_status ); + break; + } + + vlc_mutex_unlock( &p_vout->picture_lock ); +} + +/** + * Allocate a picture in the video output heap. + * + * This function creates a reserved image in the video output heap. + * A null pointer is returned if the function fails. This method provides an + * already allocated zone of memory in the picture data fields. + * It needs locking since several pictures can be created by several producers + * threads. + */ +int vout_CountPictureAvailable( vout_thread_t *p_vout ) +{ + int i_free = 0; + int i_pic; + + vlc_mutex_lock( &p_vout->picture_lock ); + for( i_pic = 0; i_pic < I_RENDERPICTURES; i_pic++ ) + { + picture_t *p_pic = PP_RENDERPICTURE[(p_vout->render.i_last_used_pic + i_pic + 1) % I_RENDERPICTURES]; + + switch( p_pic->i_status ) + { + case DESTROYED_PICTURE: + i_free++; + break; + + case FREE_PICTURE: + i_free++; + break; + + default: + break; + } + } + vlc_mutex_unlock( &p_vout->picture_lock ); + + return i_free; +} + +picture_t *vout_CreatePicture( vout_thread_t *p_vout, + bool b_progressive, + bool b_top_field_first, + unsigned int i_nb_fields ) +{ + int i_pic; /* picture index */ + picture_t * p_pic; + picture_t * p_freepic = NULL; /* first free picture */ + + /* Get lock */ + vlc_mutex_lock( &p_vout->picture_lock ); + + /* + * Look for an empty place in the picture heap. + */ + for( i_pic = 0; i_pic < I_RENDERPICTURES; i_pic++ ) + { + p_pic = PP_RENDERPICTURE[(p_vout->render.i_last_used_pic + i_pic + 1) + % I_RENDERPICTURES]; + + switch( p_pic->i_status ) + { + case DESTROYED_PICTURE: + /* Memory will not be reallocated, and function can end + * immediately - this is the best possible case, since no + * memory allocation needs to be done */ + p_pic->i_status = RESERVED_PICTURE; + p_pic->i_refcount = 0; + p_pic->b_force = 0; + + p_pic->b_progressive = b_progressive; + p_pic->i_nb_fields = i_nb_fields; + p_pic->b_top_field_first = b_top_field_first; + + p_vout->i_heap_size++; + p_vout->render.i_last_used_pic = + ( p_vout->render.i_last_used_pic + i_pic + 1 ) + % I_RENDERPICTURES; + vlc_mutex_unlock( &p_vout->picture_lock ); + return( p_pic ); + + case FREE_PICTURE: + /* Picture is empty and ready for allocation */ + p_vout->render.i_last_used_pic = + ( p_vout->render.i_last_used_pic + i_pic + 1 ) + % I_RENDERPICTURES; + p_freepic = p_pic; + break; + + default: + break; + } + } + + /* + * Prepare picture + */ + if( p_freepic != NULL ) + { + vout_AllocatePicture( VLC_OBJECT(p_vout), + p_freepic, p_vout->render.i_chroma, + p_vout->render.i_width, p_vout->render.i_height, + p_vout->render.i_aspect ); + + if( p_freepic->i_planes ) + { + /* Copy picture information, set some default values */ + p_freepic->i_status = RESERVED_PICTURE; + p_freepic->i_type = MEMORY_PICTURE; + p_freepic->b_slow = 0; + + p_freepic->i_refcount = 0; + p_freepic->b_force = 0; + + p_freepic->b_progressive = b_progressive; + p_freepic->i_nb_fields = i_nb_fields; + p_freepic->b_top_field_first = b_top_field_first; + + p_freepic->i_matrix_coefficients = 1; + + p_vout->i_heap_size++; + } + else + { + /* Memory allocation failed : set picture as empty */ + p_freepic->i_status = FREE_PICTURE; + p_freepic = NULL; + + msg_Err( p_vout, "picture allocation failed" ); + } + + vlc_mutex_unlock( &p_vout->picture_lock ); + + return( p_freepic ); + } + + /* No free or destroyed picture could be found, but the decoder + * will try again in a while. */ + vlc_mutex_unlock( &p_vout->picture_lock ); + + return( NULL ); +} + +/** + * Remove a permanent or reserved picture from the heap + * + * This function frees a previously reserved picture or a permanent + * picture. It is meant to be used when the construction of a picture aborted. + * Note that the picture will be destroyed even if it is linked ! + */ +void vout_DestroyPicture( vout_thread_t *p_vout, picture_t *p_pic ) +{ + vlc_mutex_lock( &p_vout->picture_lock ); + +#ifndef NDEBUG + /* Check if picture status is valid */ + if( (p_pic->i_status != RESERVED_PICTURE) && + (p_pic->i_status != RESERVED_DATED_PICTURE) && + (p_pic->i_status != RESERVED_DISP_PICTURE) ) + { + msg_Err( p_vout, "picture to destroy %p has invalid status %d", + p_pic, p_pic->i_status ); + } +#endif + + p_pic->i_status = DESTROYED_PICTURE; + p_vout->i_heap_size--; + + vlc_mutex_unlock( &p_vout->picture_lock ); +} + +/** + * Increment reference counter of a picture + * + * This function increments the reference counter of a picture in the video + * heap. It needs a lock since several producer threads can access the picture. + */ +void vout_LinkPicture( vout_thread_t *p_vout, picture_t *p_pic ) +{ + vlc_mutex_lock( &p_vout->picture_lock ); + p_pic->i_refcount++; + vlc_mutex_unlock( &p_vout->picture_lock ); +} + +/** + * Decrement reference counter of a picture + * + * This function decrement the reference counter of a picture in the video heap + */ +void vout_UnlinkPicture( vout_thread_t *p_vout, picture_t *p_pic ) +{ + vlc_mutex_lock( &p_vout->picture_lock ); + p_pic->i_refcount--; + + if( ( p_pic->i_refcount == 0 ) && + ( p_pic->i_status == DISPLAYED_PICTURE ) ) + { + p_pic->i_status = DESTROYED_PICTURE; + p_vout->i_heap_size--; + } + + vlc_mutex_unlock( &p_vout->picture_lock ); +} + +/** + * Render a picture + * + * This function chooses whether the current picture needs to be copied + * before rendering, does the subpicture magic, and tells the video output + * thread which direct buffer needs to be displayed. + */ +picture_t * vout_RenderPicture( vout_thread_t *p_vout, picture_t *p_pic, + subpicture_t *p_subpic ) +{ + int i_scale_width, i_scale_height; + + if( p_pic == NULL ) + { + /* XXX: subtitles */ + return NULL; + } + + i_scale_width = p_vout->fmt_out.i_visible_width * 1000 / + p_vout->fmt_in.i_visible_width; + i_scale_height = p_vout->fmt_out.i_visible_height * 1000 / + p_vout->fmt_in.i_visible_height; + + if( p_pic->i_type == DIRECT_PICTURE ) + { + if( !p_vout->render.b_allow_modify_pics || p_pic->i_refcount || + p_pic->b_force ) + { + /* Picture is in a direct buffer and is still in use, + * we need to copy it to another direct buffer before + * displaying it if there are subtitles. */ + if( p_subpic != NULL ) + { + /* We have subtitles. First copy the picture to + * the spare direct buffer, then render the + * subtitles. */ + vout_CopyPicture( p_vout, PP_OUTPUTPICTURE[0], p_pic ); + + spu_RenderSubpictures( p_vout->p_spu, &p_vout->fmt_out, + PP_OUTPUTPICTURE[0], p_pic, p_subpic, + i_scale_width, i_scale_height ); + + return PP_OUTPUTPICTURE[0]; + } + + /* No subtitles, picture is in a directbuffer so + * we can display it directly even if it is still + * in use. */ + return p_pic; + } + + /* Picture is in a direct buffer but isn't used by the + * decoder. We can safely render subtitles on it and + * display it. */ + spu_RenderSubpictures( p_vout->p_spu, &p_vout->fmt_out, p_pic, p_pic, + p_subpic, i_scale_width, i_scale_height ); + + return p_pic; + } + + /* Not a direct buffer. We either need to copy it to a direct buffer, + * or render it if the chroma isn't the same. */ + if( p_vout->b_direct ) + { + /* Picture is not in a direct buffer, but is exactly the + * same size as the direct buffers. A memcpy() is enough, + * then render the subtitles. */ + + if( PP_OUTPUTPICTURE[0]->pf_lock ) + if( PP_OUTPUTPICTURE[0]->pf_lock( p_vout, PP_OUTPUTPICTURE[0] ) ) + return NULL; + + vout_CopyPicture( p_vout, PP_OUTPUTPICTURE[0], p_pic ); + spu_RenderSubpictures( p_vout->p_spu, &p_vout->fmt_out, + PP_OUTPUTPICTURE[0], p_pic, + p_subpic, i_scale_width, i_scale_height ); + + if( PP_OUTPUTPICTURE[0]->pf_unlock ) + PP_OUTPUTPICTURE[0]->pf_unlock( p_vout, PP_OUTPUTPICTURE[0] ); + + return PP_OUTPUTPICTURE[0]; + } + + /* Picture is not in a direct buffer, and needs to be converted to + * another size/chroma. Then the subtitles need to be rendered as + * well. This usually means software YUV, or hardware YUV with a + * different chroma. */ + + if( p_subpic != NULL && p_vout->p_picture[0].b_slow ) + { + /* The picture buffer is in slow memory. We'll use + * the "2 * VOUT_MAX_PICTURES + 1" picture as a temporary + * one for subpictures rendering. */ + picture_t *p_tmp_pic = &p_vout->p_picture[2 * VOUT_MAX_PICTURES]; + if( p_tmp_pic->i_status == FREE_PICTURE ) + { + vout_AllocatePicture( VLC_OBJECT(p_vout), + p_tmp_pic, p_vout->fmt_out.i_chroma, + p_vout->fmt_out.i_width, + p_vout->fmt_out.i_height, + p_vout->fmt_out.i_aspect ); + p_tmp_pic->i_type = MEMORY_PICTURE; + p_tmp_pic->i_status = RESERVED_PICTURE; + /* some modules (such as blend) needs to know the extra information in picture heap */ + p_tmp_pic->p_heap = &p_vout->output; + } + + /* Convert image to the first direct buffer */ + p_vout->p_chroma->p_owner = (filter_owner_sys_t *)p_tmp_pic; + p_vout->p_chroma->pf_video_filter( p_vout->p_chroma, p_pic ); + + /* Render subpictures on the first direct buffer */ + spu_RenderSubpictures( p_vout->p_spu, &p_vout->fmt_out, p_tmp_pic, + p_tmp_pic, p_subpic, + i_scale_width, i_scale_height ); + + if( p_vout->p_picture[0].pf_lock ) + if( p_vout->p_picture[0].pf_lock( p_vout, &p_vout->p_picture[0] ) ) + return NULL; + + vout_CopyPicture( p_vout, &p_vout->p_picture[0], p_tmp_pic ); + } + else + { + if( p_vout->p_picture[0].pf_lock ) + if( p_vout->p_picture[0].pf_lock( p_vout, &p_vout->p_picture[0] ) ) + return NULL; + + /* Convert image to the first direct buffer */ + p_vout->p_chroma->p_owner = (filter_owner_sys_t *)&p_vout->p_picture[0]; + p_vout->p_chroma->pf_video_filter( p_vout->p_chroma, p_pic ); + + /* Render subpictures on the first direct buffer */ + spu_RenderSubpictures( p_vout->p_spu, &p_vout->fmt_out, + &p_vout->p_picture[0], &p_vout->p_picture[0], + p_subpic, i_scale_width, i_scale_height ); + } + + if( p_vout->p_picture[0].pf_unlock ) + p_vout->p_picture[0].pf_unlock( p_vout, &p_vout->p_picture[0] ); + + return &p_vout->p_picture[0]; +} + +/** + * Calculate image window coordinates + * + * This function will be accessed by plugins. It calculates the relative + * position of the output window and the image window. + */ +void vout_PlacePicture( vout_thread_t *p_vout, + unsigned int i_width, unsigned int i_height, + unsigned int *pi_x, unsigned int *pi_y, + unsigned int *pi_width, unsigned int *pi_height ) +{ + if( (i_width <= 0) || (i_height <=0) ) + { + *pi_width = *pi_height = *pi_x = *pi_y = 0; + return; + } + + if( p_vout->b_scale ) + { + *pi_width = i_width; + *pi_height = i_height; + } + else + { + *pi_width = __MIN( i_width, p_vout->fmt_in.i_visible_width ); + *pi_height = __MIN( i_height, p_vout->fmt_in.i_visible_height ); + } + + if( p_vout->fmt_in.i_visible_width * (int64_t)p_vout->fmt_in.i_sar_num * + *pi_height / p_vout->fmt_in.i_visible_height / + p_vout->fmt_in.i_sar_den > *pi_width ) + { + *pi_height = p_vout->fmt_in.i_visible_height * + (int64_t)p_vout->fmt_in.i_sar_den * *pi_width / + p_vout->fmt_in.i_visible_width / p_vout->fmt_in.i_sar_num; + } + else + { + *pi_width = p_vout->fmt_in.i_visible_width * + (int64_t)p_vout->fmt_in.i_sar_num * *pi_height / + p_vout->fmt_in.i_visible_height / p_vout->fmt_in.i_sar_den; + } + + switch( p_vout->i_alignment & VOUT_ALIGN_HMASK ) + { + case VOUT_ALIGN_LEFT: + *pi_x = 0; + break; + case VOUT_ALIGN_RIGHT: + *pi_x = i_width - *pi_width; + break; + default: + *pi_x = ( i_width - *pi_width ) / 2; + } + + switch( p_vout->i_alignment & VOUT_ALIGN_VMASK ) + { + case VOUT_ALIGN_TOP: + *pi_y = 0; + break; + case VOUT_ALIGN_BOTTOM: + *pi_y = i_height - *pi_height; + break; + default: + *pi_y = ( i_height - *pi_height ) / 2; + } +} + +/** + * Allocate a new picture in the heap. + * + * This function allocates a fake direct buffer in memory, which can be + * used exactly like a video buffer. The video output thread then manages + * how it gets displayed. + */ +int __vout_AllocatePicture( vlc_object_t *p_this, picture_t *p_pic, + vlc_fourcc_t i_chroma, + int i_width, int i_height, int i_aspect ) +{ + int i_bytes, i_index, i_width_aligned, i_height_aligned; + + /* Make sure the real dimensions are a multiple of 16 */ + i_width_aligned = (i_width + 15) >> 4 << 4; + i_height_aligned = (i_height + 15) >> 4 << 4; + + if( vout_InitPicture( p_this, p_pic, i_chroma, + i_width, i_height, i_aspect ) != VLC_SUCCESS ) + { + p_pic->i_planes = 0; + return VLC_EGENERIC; + } + + /* Calculate how big the new image should be */ + i_bytes = p_pic->format.i_bits_per_pixel * + i_width_aligned * i_height_aligned / 8; + + p_pic->p_data = vlc_memalign( &p_pic->p_data_orig, 16, i_bytes ); + + if( p_pic->p_data == NULL ) + { + p_pic->i_planes = 0; + return VLC_EGENERIC; + } + + /* Fill the p_pixels field for each plane */ + p_pic->p[ 0 ].p_pixels = p_pic->p_data; + + for( i_index = 1; i_index < p_pic->i_planes; i_index++ ) + { + p_pic->p[i_index].p_pixels = p_pic->p[i_index-1].p_pixels + + p_pic->p[i_index-1].i_lines * p_pic->p[i_index-1].i_pitch; + } + + return VLC_SUCCESS; +} + +/** + * Initialise the video format fields given chroma/size. + * + * This function initializes all the video_frame_format_t fields given the + * static properties of a picture (chroma and size). + * \param p_format Pointer to the format structure to initialize + * \param i_chroma Chroma to set + * \param i_width Width to set + * \param i_height Height to set + * \param i_aspect Aspect ratio + */ +void vout_InitFormat( video_frame_format_t *p_format, vlc_fourcc_t i_chroma, + int i_width, int i_height, int i_aspect ) +{ + p_format->i_chroma = i_chroma; + p_format->i_width = p_format->i_visible_width = i_width; + p_format->i_height = p_format->i_visible_height = i_height; + p_format->i_x_offset = p_format->i_y_offset = 0; + p_format->i_aspect = i_aspect; + +#if 0 + /* Assume we have square pixels */ + if( i_width && i_height ) + p_format->i_aspect = i_width * VOUT_ASPECT_FACTOR / i_height; + else + p_format->i_aspect = 0; +#endif + + switch( i_chroma ) + { + case FOURCC_YUVA: + p_format->i_bits_per_pixel = 32; + break; + case FOURCC_I444: + case FOURCC_J444: + p_format->i_bits_per_pixel = 24; + break; + case FOURCC_I422: + case FOURCC_YUY2: + case FOURCC_UYVY: + case FOURCC_J422: + p_format->i_bits_per_pixel = 16; + break; + case FOURCC_I440: + case FOURCC_J440: + p_format->i_bits_per_pixel = 16; + break; + case FOURCC_I411: + case FOURCC_YV12: + case FOURCC_I420: + case FOURCC_J420: + case FOURCC_IYUV: + p_format->i_bits_per_pixel = 12; + break; + case FOURCC_I410: + case FOURCC_YVU9: + p_format->i_bits_per_pixel = 9; + break; + case FOURCC_Y211: + p_format->i_bits_per_pixel = 8; + break; + case FOURCC_YUVP: + p_format->i_bits_per_pixel = 8; + break; + + case FOURCC_RV32: + case FOURCC_RGBA: + p_format->i_bits_per_pixel = 32; + break; + case FOURCC_RV24: + p_format->i_bits_per_pixel = 24; + break; + case FOURCC_RV15: + case FOURCC_RV16: + p_format->i_bits_per_pixel = 16; + break; + case FOURCC_RGB2: + p_format->i_bits_per_pixel = 8; + break; + + case FOURCC_GREY: + case FOURCC_Y800: + case FOURCC_Y8: + p_format->i_bits_per_pixel = 8; + break; + + default: + p_format->i_bits_per_pixel = 0; + break; + } +} + +/** + * Initialise the picture_t fields given chroma/size. + * + * This function initializes most of the picture_t fields given a chroma and + * size. It makes the assumption that stride == width. + * \param p_this The calling object + * \param p_pic Pointer to the picture to initialize + * \param i_chroma The chroma fourcc to set + * \param i_width The width of the picture + * \param i_height The height of the picture + * \param i_aspect The aspect ratio of the picture + */ +int __vout_InitPicture( vlc_object_t *p_this, picture_t *p_pic, + vlc_fourcc_t i_chroma, + int i_width, int i_height, int i_aspect ) +{ + int i_index, i_width_aligned, i_height_aligned; + + /* Store default values */ + for( i_index = 0; i_index < VOUT_MAX_PLANES; i_index++ ) + { + p_pic->p[i_index].p_pixels = NULL; + p_pic->p[i_index].i_pixel_pitch = 1; + } + + p_pic->pf_release = 0; + p_pic->pf_lock = 0; + p_pic->pf_unlock = 0; + p_pic->i_refcount = 0; + + vout_InitFormat( &p_pic->format, i_chroma, i_width, i_height, i_aspect ); + + /* Make sure the real dimensions are a multiple of 16 */ + i_width_aligned = (i_width + 15) >> 4 << 4; + i_height_aligned = (i_height + 15) >> 4 << 4; + + /* Calculate coordinates */ + switch( i_chroma ) + { + case FOURCC_I411: + p_pic->p[ Y_PLANE ].i_lines = i_height_aligned; + p_pic->p[ Y_PLANE ].i_visible_lines = i_height; + p_pic->p[ Y_PLANE ].i_pitch = i_width_aligned; + p_pic->p[ Y_PLANE ].i_visible_pitch = i_width; + p_pic->p[ U_PLANE ].i_lines = i_height_aligned; + p_pic->p[ U_PLANE ].i_visible_lines = i_height; + p_pic->p[ U_PLANE ].i_pitch = i_width_aligned / 4; + p_pic->p[ U_PLANE ].i_visible_pitch = i_width / 4; + p_pic->p[ V_PLANE ].i_lines = i_height_aligned; + p_pic->p[ V_PLANE ].i_visible_lines = i_height; + p_pic->p[ V_PLANE ].i_pitch = i_width_aligned / 4; + p_pic->p[ V_PLANE ].i_visible_pitch = i_width / 4; + p_pic->i_planes = 3; + break; + + case FOURCC_I410: + case FOURCC_YVU9: + p_pic->p[ Y_PLANE ].i_lines = i_height_aligned; + p_pic->p[ Y_PLANE ].i_visible_lines = i_height; + p_pic->p[ Y_PLANE ].i_pitch = i_width_aligned; + p_pic->p[ Y_PLANE ].i_visible_pitch = i_width; + p_pic->p[ U_PLANE ].i_lines = i_height_aligned / 4; + p_pic->p[ U_PLANE ].i_visible_lines = i_height / 4; + p_pic->p[ U_PLANE ].i_pitch = i_width_aligned / 4; + p_pic->p[ U_PLANE ].i_visible_pitch = i_width / 4; + p_pic->p[ V_PLANE ].i_lines = i_height_aligned / 4; + p_pic->p[ V_PLANE ].i_visible_lines = i_height / 4; + p_pic->p[ V_PLANE ].i_pitch = i_width_aligned / 4; + p_pic->p[ V_PLANE ].i_visible_pitch = i_width / 4; + p_pic->i_planes = 3; + break; + + case FOURCC_YV12: + case FOURCC_I420: + case FOURCC_IYUV: + case FOURCC_J420: + p_pic->p[ Y_PLANE ].i_lines = i_height_aligned; + p_pic->p[ Y_PLANE ].i_visible_lines = i_height; + p_pic->p[ Y_PLANE ].i_pitch = i_width_aligned; + p_pic->p[ Y_PLANE ].i_visible_pitch = i_width; + p_pic->p[ U_PLANE ].i_lines = i_height_aligned / 2; + p_pic->p[ U_PLANE ].i_visible_lines = i_height / 2; + p_pic->p[ U_PLANE ].i_pitch = i_width_aligned / 2; + p_pic->p[ U_PLANE ].i_visible_pitch = i_width / 2; + p_pic->p[ V_PLANE ].i_lines = i_height_aligned / 2; + p_pic->p[ V_PLANE ].i_visible_lines = i_height / 2; + p_pic->p[ V_PLANE ].i_pitch = i_width_aligned / 2; + p_pic->p[ V_PLANE ].i_visible_pitch = i_width / 2; + p_pic->i_planes = 3; + break; + + case FOURCC_I422: + case FOURCC_J422: + p_pic->p[ Y_PLANE ].i_lines = i_height_aligned; + p_pic->p[ Y_PLANE ].i_visible_lines = i_height; + p_pic->p[ Y_PLANE ].i_pitch = i_width_aligned; + p_pic->p[ Y_PLANE ].i_visible_pitch = i_width; + p_pic->p[ U_PLANE ].i_lines = i_height_aligned; + p_pic->p[ U_PLANE ].i_visible_lines = i_height; + p_pic->p[ U_PLANE ].i_pitch = i_width_aligned / 2; + p_pic->p[ U_PLANE ].i_visible_pitch = i_width / 2; + p_pic->p[ V_PLANE ].i_lines = i_height_aligned; + p_pic->p[ V_PLANE ].i_visible_lines = i_height; + p_pic->p[ V_PLANE ].i_pitch = i_width_aligned / 2; + p_pic->p[ V_PLANE ].i_visible_pitch = i_width / 2; + p_pic->i_planes = 3; + break; + + case FOURCC_I440: + case FOURCC_J440: + p_pic->p[ Y_PLANE ].i_lines = i_height_aligned; + p_pic->p[ Y_PLANE ].i_visible_lines = i_height; + p_pic->p[ Y_PLANE ].i_pitch = i_width_aligned; + p_pic->p[ Y_PLANE ].i_visible_pitch = i_width; + p_pic->p[ U_PLANE ].i_lines = i_height_aligned / 2; + p_pic->p[ U_PLANE ].i_visible_lines = i_height / 2; + p_pic->p[ U_PLANE ].i_pitch = i_width_aligned; + p_pic->p[ U_PLANE ].i_visible_pitch = i_width; + p_pic->p[ V_PLANE ].i_lines = i_height_aligned / 2; + p_pic->p[ V_PLANE ].i_visible_lines = i_height / 2; + p_pic->p[ V_PLANE ].i_pitch = i_width_aligned; + p_pic->p[ V_PLANE ].i_visible_pitch = i_width; + p_pic->i_planes = 3; + break; + + case FOURCC_I444: + case FOURCC_J444: + p_pic->p[ Y_PLANE ].i_lines = i_height_aligned; + p_pic->p[ Y_PLANE ].i_visible_lines = i_height; + p_pic->p[ Y_PLANE ].i_pitch = i_width_aligned; + p_pic->p[ Y_PLANE ].i_visible_pitch = i_width; + p_pic->p[ U_PLANE ].i_lines = i_height_aligned; + p_pic->p[ U_PLANE ].i_visible_lines = i_height; + p_pic->p[ U_PLANE ].i_pitch = i_width_aligned; + p_pic->p[ U_PLANE ].i_visible_pitch = i_width; + p_pic->p[ V_PLANE ].i_lines = i_height_aligned; + p_pic->p[ V_PLANE ].i_visible_lines = i_height; + p_pic->p[ V_PLANE ].i_pitch = i_width_aligned; + p_pic->p[ V_PLANE ].i_visible_pitch = i_width; + p_pic->i_planes = 3; + break; + + case FOURCC_YUVA: + p_pic->p[ Y_PLANE ].i_lines = i_height_aligned; + p_pic->p[ Y_PLANE ].i_visible_lines = i_height; + p_pic->p[ Y_PLANE ].i_pitch = i_width_aligned; + p_pic->p[ Y_PLANE ].i_visible_pitch = i_width; + p_pic->p[ U_PLANE ].i_lines = i_height_aligned; + p_pic->p[ U_PLANE ].i_visible_lines = i_height; + p_pic->p[ U_PLANE ].i_pitch = i_width_aligned; + p_pic->p[ U_PLANE ].i_visible_pitch = i_width; + p_pic->p[ V_PLANE ].i_lines = i_height_aligned; + p_pic->p[ V_PLANE ].i_visible_lines = i_height; + p_pic->p[ V_PLANE ].i_pitch = i_width_aligned; + p_pic->p[ V_PLANE ].i_visible_pitch = i_width; + p_pic->p[ A_PLANE ].i_lines = i_height_aligned; + p_pic->p[ A_PLANE ].i_visible_lines = i_height; + p_pic->p[ A_PLANE ].i_pitch = i_width_aligned; + p_pic->p[ A_PLANE ].i_visible_pitch = i_width; + p_pic->i_planes = 4; + break; + + case FOURCC_YUVP: + p_pic->p->i_lines = i_height_aligned; + p_pic->p->i_visible_lines = i_height; + p_pic->p->i_pitch = i_width_aligned; + p_pic->p->i_visible_pitch = i_width; + p_pic->p->i_pixel_pitch = 8; + p_pic->i_planes = 1; + break; + + case FOURCC_Y211: + p_pic->p->i_lines = i_height_aligned; + p_pic->p->i_visible_lines = i_height; + p_pic->p->i_pitch = i_width_aligned; + p_pic->p->i_visible_pitch = i_width; + p_pic->p->i_pixel_pitch = 4; + p_pic->i_planes = 1; + break; + + case FOURCC_UYVY: + case FOURCC_YUY2: + p_pic->p->i_lines = i_height_aligned; + p_pic->p->i_visible_lines = i_height; + p_pic->p->i_pitch = i_width_aligned * 2; + p_pic->p->i_visible_pitch = i_width * 2; + p_pic->p->i_pixel_pitch = 4; + p_pic->i_planes = 1; + break; + + case FOURCC_RGB2: + p_pic->p->i_lines = i_height_aligned; + p_pic->p->i_visible_lines = i_height; + p_pic->p->i_pitch = i_width_aligned; + p_pic->p->i_visible_pitch = i_width; + p_pic->p->i_pixel_pitch = 1; + p_pic->i_planes = 1; + break; + + case FOURCC_RV15: + p_pic->p->i_lines = i_height_aligned; + p_pic->p->i_visible_lines = i_height; + p_pic->p->i_pitch = i_width_aligned * 2; + p_pic->p->i_visible_pitch = i_width * 2; + p_pic->p->i_pixel_pitch = 2; + p_pic->i_planes = 1; + break; + + case FOURCC_RV16: + p_pic->p->i_lines = i_height_aligned; + p_pic->p->i_visible_lines = i_height; + p_pic->p->i_pitch = i_width_aligned * 2; + p_pic->p->i_visible_pitch = i_width * 2; + p_pic->p->i_pixel_pitch = 2; + p_pic->i_planes = 1; + break; + + case FOURCC_RV24: + p_pic->p->i_lines = i_height_aligned; + p_pic->p->i_visible_lines = i_height; + p_pic->p->i_pitch = i_width_aligned * 3; + p_pic->p->i_visible_pitch = i_width * 3; + p_pic->p->i_pixel_pitch = 3; + p_pic->i_planes = 1; + break; + + case FOURCC_RV32: + case FOURCC_RGBA: + p_pic->p->i_lines = i_height_aligned; + p_pic->p->i_visible_lines = i_height; + p_pic->p->i_pitch = i_width_aligned * 4; + p_pic->p->i_visible_pitch = i_width * 4; + p_pic->p->i_pixel_pitch = 4; + p_pic->i_planes = 1; + break; + + case FOURCC_GREY: + case FOURCC_Y800: + case FOURCC_Y8: + p_pic->p->i_lines = i_height_aligned; + p_pic->p->i_visible_lines = i_height; + p_pic->p->i_pitch = i_width_aligned; + p_pic->p->i_visible_pitch = i_width; + p_pic->p->i_pixel_pitch = 1; + p_pic->i_planes = 1; + break; + + default: + if( p_this ) + msg_Err( p_this, "unknown chroma type 0x%.8x (%4.4s)", + i_chroma, (char*)&i_chroma ); + p_pic->i_planes = 0; + return VLC_EGENERIC; + } + + return VLC_SUCCESS; +} + +/** + * Compare two chroma values + * + * This function returns 1 if the two fourcc values given as argument are + * the same format (eg. UYVY/UYNV) or almost the same format (eg. I420/YV12) + */ +int vout_ChromaCmp( vlc_fourcc_t i_chroma, vlc_fourcc_t i_amorhc ) +{ + /* If they are the same, they are the same ! */ + if( i_chroma == i_amorhc ) + { + return 1; + } + + /* Check for equivalence classes */ + switch( i_chroma ) + { + case FOURCC_I420: + case FOURCC_IYUV: + case FOURCC_YV12: + switch( i_amorhc ) + { + case FOURCC_I420: + case FOURCC_IYUV: + case FOURCC_YV12: + return 1; + + default: + return 0; + } + + case FOURCC_UYVY: + case FOURCC_UYNV: + case FOURCC_Y422: + switch( i_amorhc ) + { + case FOURCC_UYVY: + case FOURCC_UYNV: + case FOURCC_Y422: + return 1; + + default: + return 0; + } + + case FOURCC_YUY2: + case FOURCC_YUNV: + switch( i_amorhc ) + { + case FOURCC_YUY2: + case FOURCC_YUNV: + return 1; + + default: + return 0; + } + + case FOURCC_GREY: + case FOURCC_Y800: + case FOURCC_Y8: + switch( i_amorhc ) + { + case FOURCC_GREY: + case FOURCC_Y800: + case FOURCC_Y8: + return 1; + + default: + return 0; + } + + default: + return 0; + } +} + +/***************************************************************************** + * vout_CopyPicture: copy a picture to another one + ***************************************************************************** + * This function takes advantage of the image format, and reduces the + * number of calls to memcpy() to the minimum. Source and destination + * images must have same width (hence i_visible_pitch), height, and chroma. + *****************************************************************************/ +void __vout_CopyPicture( vlc_object_t *p_this, + picture_t *p_dest, picture_t *p_src ) +{ + VLC_UNUSED(p_this); + picture_Copy( p_dest, p_src ); +} + +/***************************************************************************** + * + *****************************************************************************/ +static void PictureReleaseCallback( picture_t *p_picture ) +{ + if( --p_picture->i_refcount > 0 ) + return; + picture_Delete( p_picture ); +} +/***************************************************************************** + * + *****************************************************************************/ +picture_t *picture_New( vlc_fourcc_t i_chroma, int i_width, int i_height, int i_aspect ) +{ + picture_t *p_picture = malloc( sizeof(*p_picture) ); + + if( !p_picture ) + return NULL; + + memset( p_picture, 0, sizeof(*p_picture) ); + if( __vout_AllocatePicture( NULL, p_picture, + i_chroma, i_width, i_height, i_aspect ) ) + { + free( p_picture ); + return NULL; + } + + p_picture->i_refcount = 1; + p_picture->pf_release = PictureReleaseCallback; + p_picture->i_status = RESERVED_PICTURE; + + return p_picture; +} + +/***************************************************************************** + * + *****************************************************************************/ +void picture_Delete( picture_t *p_picture ) +{ + assert( p_picture && p_picture->i_refcount == 0 ); + + free( p_picture->p_data_orig ); + free( p_picture->p_sys ); + free( p_picture ); +} + +/***************************************************************************** + * + *****************************************************************************/ +void picture_CopyPixels( picture_t *p_dst, const picture_t *p_src ) +{ + int i; + + for( i = 0; i < p_src->i_planes ; i++ ) + plane_CopyPixels( p_dst->p+i, p_src->p+i ); +} + +void plane_CopyPixels( plane_t *p_dst, const plane_t *p_src ) +{ + const unsigned i_width = __MIN( p_dst->i_visible_pitch, + p_src->i_visible_pitch ); + const unsigned i_height = __MIN( p_dst->i_visible_lines, + p_src->i_visible_lines ); + + if( p_src->i_pitch == p_dst->i_pitch ) + { + /* There are margins, but with the same width : perfect ! */ + vlc_memcpy( p_dst->p_pixels, p_src->p_pixels, + p_src->i_pitch * i_height ); + } + else + { + /* We need to proceed line by line */ + uint8_t *p_in = p_src->p_pixels; + uint8_t *p_out = p_dst->p_pixels; + int i_line; + + assert( p_in ); + assert( p_out ); + + for( i_line = i_height; i_line--; ) + { + vlc_memcpy( p_out, p_in, i_width ); + p_in += p_src->i_pitch; + p_out += p_dst->i_pitch; + } + } +} + +/***************************************************************************** + * + *****************************************************************************/ + + diff --git a/VLC/vout_pictures.h b/VLC/vout_pictures.h new file mode 100644 index 0000000..456f310 --- /dev/null +++ b/VLC/vout_pictures.h @@ -0,0 +1,141 @@ +/***************************************************************************** + * vout_pictures.h : picture management definitions + ***************************************************************************** + * Copyright (C) 2002-2004 the VideoLAN team + * $Id$ + * + * Authors: Samuel Hocevar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Fourcc definitions that we can handle internally + *****************************************************************************/ + +/* Packed RGB for 8bpp */ +#define FOURCC_BI_RGB 0x00000000 +#define FOURCC_RGB2 VLC_FOURCC('R','G','B','2') + +/* Packed RGB for 16, 24, 32bpp */ +#define FOURCC_BI_BITFIELDS 0x00000003 + +/* Packed RGB 15bpp, usually 0x7c00, 0x03e0, 0x001f */ +#define FOURCC_RV15 VLC_FOURCC('R','V','1','5') + +/* Packed RGB 16bpp, usually 0xf800, 0x07e0, 0x001f */ +#define FOURCC_RV16 VLC_FOURCC('R','V','1','6') + +/* Packed RGB 24bpp, usually 0x00ff0000, 0x0000ff00, 0x000000ff */ +#define FOURCC_RV24 VLC_FOURCC('R','V','2','4') + +/* Packed RGB 32bpp, usually 0x00ff0000, 0x0000ff00, 0x000000ff */ +#define FOURCC_RV32 VLC_FOURCC('R','V','3','2') + +/* Packed RGBA 32bpp, like RV32 with 0xff000000 used for alpha */ +#define FOURCC_RGBA VLC_FOURCC('R','G','B','A') + +/* Planar YUV 4:2:0, Y:U:V */ +#define FOURCC_I420 VLC_FOURCC('I','4','2','0') +#define FOURCC_IYUV VLC_FOURCC('I','Y','U','V') +#define FOURCC_J420 VLC_FOURCC('J','4','2','0') + +/* Planar YUV 4:2:0, Y:V:U */ +#define FOURCC_YV12 VLC_FOURCC('Y','V','1','2') + +/* Packed YUV 4:2:2, U:Y:V:Y, interlaced */ +#define FOURCC_IUYV VLC_FOURCC('I','U','Y','V') + +/* Packed YUV 4:2:2, U:Y:V:Y */ +#define FOURCC_UYVY VLC_FOURCC('U','Y','V','Y') +#define FOURCC_UYNV VLC_FOURCC('U','Y','N','V') +#define FOURCC_Y422 VLC_FOURCC('Y','4','2','2') + +/* Packed YUV 4:2:2, U:Y:V:Y, reverted */ +#define FOURCC_cyuv VLC_FOURCC('c','y','u','v') + +/* Packed YUV 4:2:2, Y:U:Y:V */ +#define FOURCC_YUY2 VLC_FOURCC('Y','U','Y','2') +#define FOURCC_YUNV VLC_FOURCC('Y','U','N','V') + +/* Packed YUV 4:2:2, Y:V:Y:U */ +#define FOURCC_YVYU VLC_FOURCC('Y','V','Y','U') + +/* Packed YUV 2:1:1, Y:U:Y:V */ +#define FOURCC_Y211 VLC_FOURCC('Y','2','1','1') + +/* Planar YUV 4:1:1, Y:U:V */ +#define FOURCC_I411 VLC_FOURCC('I','4','1','1') + +/* Planar YUV 4:1:0, Y:U:V */ +#define FOURCC_I410 VLC_FOURCC('I','4','1','0') +#define FOURCC_YVU9 VLC_FOURCC('Y','V','U','9') + +/* Planar Y, packed UV, from Matrox */ +#define FOURCC_YMGA VLC_FOURCC('Y','M','G','A') + +/* Planar 4:2:2, Y:U:V */ +#define FOURCC_I422 VLC_FOURCC('I','4','2','2') +#define FOURCC_J422 VLC_FOURCC('J','4','2','2') + +/* Planar 4:4:0, Y:U:V */ +#define FOURCC_I440 VLC_FOURCC('I','4','4','0') +#define FOURCC_J440 VLC_FOURCC('J','4','4','0') + +/* Planar 4:4:4, Y:U:V */ +#define FOURCC_I444 VLC_FOURCC('I','4','4','4') +#define FOURCC_J444 VLC_FOURCC('J','4','4','4') + +/* Planar 4:4:4:4 Y:U:V:A */ +#define FOURCC_YUVA VLC_FOURCC('Y','U','V','A') + +/* Palettized YUV with palette element Y:U:V:A */ +#define FOURCC_YUVP VLC_FOURCC('Y','U','V','P') + +/* Planar 8-bit grayscale */ +#define FOURCC_GREY VLC_FOURCC('G','R','E','Y') +#define FOURCC_Y800 VLC_FOURCC('Y','8','0','0') +#define FOURCC_Y8 VLC_FOURCC('Y','8',' ',' ') + +/* Alignment of critical dynamic data structure + * + * Not all platforms support memalign so we provide a vlc_memalign wrapper + * void *vlc_memalign( size_t align, size_t size, void **pp_orig ) + * *pp_orig is the pointer that has to be freed afterwards. + */ +static inline +void *vlc_memalign (void **pp, size_t align, size_t size) +{ +#if defined (HAVE_POSIX_MEMALIGN) + return posix_memalign (pp, align, size) ? NULL : *pp; +#elif defined (HAVE_MEMALIGN) + return *pp = memalign (align, size); +#else + unsigned char *ptr; + + if (align < 1) + return NULL; + + align--; + ptr = malloc (size + align); + if (ptr == NULL) + return NULL; + + *pp = ptr; + ptr += align; + return (void *)(((uintptr_t)ptr) & ~align); +#endif +} + diff --git a/VLC/vout_subpictures.c b/VLC/vout_subpictures.c new file mode 100644 index 0000000..5a6db02 --- /dev/null +++ b/VLC/vout_subpictures.c @@ -0,0 +1,1505 @@ +/***************************************************************************** + * vout_subpictures.c : subpicture management functions + ***************************************************************************** + * Copyright (C) 2000-2007 the VideoLAN team + * $Id$ + * + * Authors: Vincent Seguin + * Samuel Hocevar + * Gildas Bazin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" +#include "vlc_vout.h" +#include "vlc_block.h" +#include "vlc_filter.h" +#include "vlc_osd.h" +#include "libvlc.h" + +#include + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +static void UpdateSPU ( spu_t *, vlc_object_t * ); +static int CropCallback( vlc_object_t *, char const *, + vlc_value_t, vlc_value_t, void * ); + +static int spu_vaControlDefault( spu_t *, int, va_list ); + +static subpicture_t *sub_new_buffer( filter_t * ); +static void sub_del_buffer( filter_t *, subpicture_t * ); +static subpicture_t *spu_new_buffer( filter_t * ); +static void spu_del_buffer( filter_t *, subpicture_t * ); +static picture_t *spu_new_video_buffer( filter_t * ); +static void spu_del_video_buffer( filter_t *, picture_t * ); + +static int spu_ParseChain( spu_t * ); +static int SubFilterCallback( vlc_object_t *, char const *, + vlc_value_t, vlc_value_t, void * ); + +static int sub_filter_allocation_init( filter_t *, void * ); +static void sub_filter_allocation_clear( filter_t * ); +struct filter_owner_sys_t +{ + spu_t *p_spu; + int i_channel; +}; + +enum { + SCALE_DEFAULT, + SCALE_TEXT, + SCALE_SIZE +}; + +static void FilterRelease( filter_t *p_filter ) +{ + if( p_filter->p_module ) + module_Unneed( p_filter, p_filter->p_module ); + + vlc_object_detach( p_filter ); + vlc_object_release( p_filter ); +} + +/** + * Creates the subpicture unit + * + * \param p_this the parent object which creates the subpicture unit + */ +spu_t *__spu_Create( vlc_object_t *p_this ) +{ + int i_index; + spu_t *p_spu = vlc_custom_create( p_this, sizeof( spu_t ), + VLC_OBJECT_GENERIC, "subpicture" ); + + for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++) + { + p_spu->p_subpicture[i_index].i_status = FREE_SUBPICTURE; + } + + p_spu->p_blend = NULL; + p_spu->p_text = NULL; + p_spu->p_scale = NULL; + p_spu->p_scale_yuvp = NULL; + p_spu->pf_control = spu_vaControlDefault; + + /* Register the default subpicture channel */ + p_spu->i_channel = 2; + + vlc_mutex_init( &p_spu->subpicture_lock ); + + vlc_object_attach( p_spu, p_this ); + + p_spu->p_chain = filter_chain_New( p_spu, "sub filter", false, + sub_filter_allocation_init, + sub_filter_allocation_clear, + p_spu ); + return p_spu; +} + +/** + * Initialise the subpicture unit + * + * \param p_spu the subpicture unit object + */ +int spu_Init( spu_t *p_spu ) +{ + vlc_value_t val; + + /* If the user requested a sub margin, we force the position. */ + var_Create( p_spu, "sub-margin", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); + var_Get( p_spu, "sub-margin", &val ); + p_spu->i_margin = val.i_int; + + var_Create( p_spu, "sub-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); + var_AddCallback( p_spu, "sub-filter", SubFilterCallback, p_spu ); + + spu_ParseChain( p_spu ); + + return VLC_SUCCESS; +} + +int spu_ParseChain( spu_t *p_spu ) +{ + char *psz_parser = var_GetString( p_spu, "sub-filter" ); + if( filter_chain_AppendFromString( p_spu->p_chain, psz_parser ) < 0 ) + { + free( psz_parser ); + return VLC_EGENERIC; + } + + free( psz_parser ); + return VLC_SUCCESS; +} + +/** + * Destroy the subpicture unit + * + * \param p_this the parent object which destroys the subpicture unit + */ +void spu_Destroy( spu_t *p_spu ) +{ + int i_index; + + /* Destroy all remaining subpictures */ + for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ ) + { + if( p_spu->p_subpicture[i_index].i_status != FREE_SUBPICTURE ) + { + spu_DestroySubpicture( p_spu, &p_spu->p_subpicture[i_index] ); + } + } + + if( p_spu->p_blend ) + FilterRelease( p_spu->p_blend ); + + if( p_spu->p_text ) + FilterRelease( p_spu->p_text ); + + if( p_spu->p_scale_yuvp ) + FilterRelease( p_spu->p_scale_yuvp ); + + if( p_spu->p_scale ) + FilterRelease( p_spu->p_scale ); + + filter_chain_Delete( p_spu->p_chain ); + + vlc_mutex_destroy( &p_spu->subpicture_lock ); + vlc_object_release( p_spu ); +} + +/** + * Attach/Detach the SPU from any input + * + * \param p_this the object in which to destroy the subpicture unit + * \param b_attach to select attach or detach + */ +void spu_Attach( spu_t *p_spu, vlc_object_t *p_this, bool b_attach ) +{ + vlc_object_t *p_input; + + p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_PARENT ); + if( !p_input ) return; + + if( b_attach ) + { + UpdateSPU( p_spu, VLC_OBJECT(p_input) ); + var_AddCallback( p_input, "highlight", CropCallback, p_spu ); + vlc_object_release( p_input ); + } + else + { + /* Delete callback */ + var_DelCallback( p_input, "highlight", CropCallback, p_spu ); + vlc_object_release( p_input ); + } +} + +/** + * Create a subpicture region + * + * \param p_this vlc_object_t + * \param p_fmt the format that this subpicture region should have + */ +static void RegionPictureRelease( picture_t *p_pic ) +{ + free( p_pic->p_data_orig ); + /* We use pf_release nullity to know if the picture has already been released. */ + p_pic->pf_release = NULL; +} +subpicture_region_t *__spu_CreateRegion( vlc_object_t *p_this, + video_format_t *p_fmt ) +{ + subpicture_region_t *p_region = malloc( sizeof(subpicture_region_t) ); + if( !p_region ) return NULL; + + memset( p_region, 0, sizeof(subpicture_region_t) ); + p_region->i_alpha = 0xff; + p_region->p_next = NULL; + p_region->p_cache = NULL; + p_region->fmt = *p_fmt; + p_region->psz_text = NULL; + p_region->p_style = NULL; + + if( p_fmt->i_chroma == VLC_FOURCC('Y','U','V','P') ) + p_fmt->p_palette = p_region->fmt.p_palette = + malloc( sizeof(video_palette_t) ); + else p_fmt->p_palette = p_region->fmt.p_palette = NULL; + + p_region->picture.p_data_orig = NULL; + + if( p_fmt->i_chroma == VLC_FOURCC('T','E','X','T') ) return p_region; + + vout_AllocatePicture( p_this, &p_region->picture, p_fmt->i_chroma, + p_fmt->i_width, p_fmt->i_height, p_fmt->i_aspect ); + + if( !p_region->picture.i_planes ) + { + free( p_region ); + free( p_fmt->p_palette ); + return NULL; + } + + p_region->picture.pf_release = RegionPictureRelease; + + return p_region; +} + +/** + * Make a subpicture region from an existing picture_t + * + * \param p_this vlc_object_t + * \param p_fmt the format that this subpicture region should have + * \param p_pic a pointer to the picture creating the region (not freed) + */ +subpicture_region_t *__spu_MakeRegion( vlc_object_t *p_this, + video_format_t *p_fmt, + picture_t *p_pic ) +{ + subpicture_region_t *p_region = malloc( sizeof(subpicture_region_t) ); + (void)p_this; + if( !p_region ) return NULL; + memset( p_region, 0, sizeof(subpicture_region_t) ); + p_region->i_alpha = 0xff; + p_region->p_next = 0; + p_region->p_cache = 0; + p_region->fmt = *p_fmt; + p_region->psz_text = 0; + p_region->p_style = NULL; + + if( p_fmt->i_chroma == VLC_FOURCC('Y','U','V','P') ) + p_fmt->p_palette = p_region->fmt.p_palette = + malloc( sizeof(video_palette_t) ); + else p_fmt->p_palette = p_region->fmt.p_palette = NULL; + + memcpy( &p_region->picture, p_pic, sizeof(picture_t) ); + p_region->picture.pf_release = RegionPictureRelease; + + return p_region; +} + +/** + * Destroy a subpicture region + * + * \param p_this vlc_object_t + * \param p_region the subpicture region to destroy + */ +void __spu_DestroyRegion( vlc_object_t *p_this, subpicture_region_t *p_region ) +{ + if( !p_region ) return; + if( p_region->picture.pf_release ) + p_region->picture.pf_release( &p_region->picture ); + free( p_region->fmt.p_palette ); + if( p_region->p_cache ) __spu_DestroyRegion( p_this, p_region->p_cache ); + + free( p_region->psz_text ); + free( p_region->psz_html ); + //free( p_region->p_style ); FIXME --fenrir plugin does not allocate the memory for it. I think it might lead to segfault, video renderer can live longer than the decoder + free( p_region ); +} + +/** + * Display a subpicture + * + * Remove the reservation flag of a subpicture, which will cause it to be + * ready for display. + * \param p_spu the subpicture unit object + * \param p_subpic the subpicture to display + */ +void spu_DisplaySubpicture( spu_t *p_spu, subpicture_t *p_subpic ) +{ + /* Check if status is valid */ + if( p_subpic->i_status != RESERVED_SUBPICTURE ) + { + msg_Err( p_spu, "subpicture %p has invalid status #%d", + p_subpic, p_subpic->i_status ); + } + + /* Remove reservation flag */ + p_subpic->i_status = READY_SUBPICTURE; + + if( p_subpic->i_channel == DEFAULT_CHAN ) + { + p_subpic->i_channel = 0xFFFF; + spu_Control( p_spu, SPU_CHANNEL_CLEAR, DEFAULT_CHAN ); + p_subpic->i_channel = DEFAULT_CHAN; + } +} + +/** + * Allocate a subpicture in the spu heap. + * + * This function create a reserved subpicture in the spu heap. + * A null pointer is returned if the function fails. This method provides an + * already allocated zone of memory in the spu data fields. It needs locking + * since several pictures can be created by several producers threads. + * \param p_spu the subpicture unit in which to create the subpicture + * \return NULL on error, a reserved subpicture otherwise + */ +subpicture_t *spu_CreateSubpicture( spu_t *p_spu ) +{ + int i_subpic; /* subpicture index */ + subpicture_t * p_subpic = NULL; /* first free subpicture */ + + /* Get lock */ + vlc_mutex_lock( &p_spu->subpicture_lock ); + + /* + * Look for an empty place + */ + p_subpic = NULL; + for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ ) + { + if( p_spu->p_subpicture[i_subpic].i_status == FREE_SUBPICTURE ) + { + /* Subpicture is empty and ready for allocation */ + p_subpic = &p_spu->p_subpicture[i_subpic]; + p_spu->p_subpicture[i_subpic].i_status = RESERVED_SUBPICTURE; + break; + } + } + + /* If no free subpicture could be found */ + if( p_subpic == NULL ) + { + msg_Err( p_spu, "subpicture heap is full" ); + vlc_mutex_unlock( &p_spu->subpicture_lock ); + return NULL; + } + + /* Copy subpicture information, set some default values */ + memset( p_subpic, 0, sizeof(subpicture_t) ); + p_subpic->i_status = RESERVED_SUBPICTURE; + p_subpic->b_absolute = true; + p_subpic->b_pausable = false; + p_subpic->b_fade = false; + p_subpic->i_alpha = 0xFF; + p_subpic->p_region = NULL; + p_subpic->pf_render = NULL; + p_subpic->pf_destroy = NULL; + p_subpic->p_sys = NULL; + vlc_mutex_unlock( &p_spu->subpicture_lock ); + + p_subpic->pf_create_region = __spu_CreateRegion; + p_subpic->pf_make_region = __spu_MakeRegion; + p_subpic->pf_destroy_region = __spu_DestroyRegion; + + return p_subpic; +} + +/** + * Remove a subpicture from the heap + * + * This function frees a previously reserved subpicture. + * It is meant to be used when the construction of a picture aborted. + * This function does not need locking since reserved subpictures are ignored + * by the spu. + */ +void spu_DestroySubpicture( spu_t *p_spu, subpicture_t *p_subpic ) +{ + /* Get lock */ + vlc_mutex_lock( &p_spu->subpicture_lock ); + + /* There can be race conditions so we need to check the status */ + if( p_subpic->i_status == FREE_SUBPICTURE ) + { + vlc_mutex_unlock( &p_spu->subpicture_lock ); + return; + } + + /* Check if status is valid */ + if( ( p_subpic->i_status != RESERVED_SUBPICTURE ) + && ( p_subpic->i_status != READY_SUBPICTURE ) ) + { + msg_Err( p_spu, "subpicture %p has invalid status %d", + p_subpic, p_subpic->i_status ); + } + + while( p_subpic->p_region ) + { + subpicture_region_t *p_region = p_subpic->p_region; + p_subpic->p_region = p_region->p_next; + spu_DestroyRegion( p_spu, p_region ); + } + + if( p_subpic->pf_destroy ) + { + p_subpic->pf_destroy( p_subpic ); + } + + p_subpic->i_status = FREE_SUBPICTURE; + + vlc_mutex_unlock( &p_spu->subpicture_lock ); +} + +/***************************************************************************** + * spu_RenderSubpictures: render a subpicture list + ***************************************************************************** + * This function renders all sub picture units in the list. + *****************************************************************************/ +static void SpuRenderCreateBlend( spu_t *p_spu, vlc_fourcc_t i_chroma, int i_aspect ) +{ + filter_t *p_blend; + + assert( !p_spu->p_blend ); + + p_spu->p_blend = + p_blend = vlc_custom_create( p_spu, sizeof(filter_t), + VLC_OBJECT_GENERIC, "blend" ); + if( !p_blend ) + return; + + es_format_Init( &p_blend->fmt_in, VIDEO_ES, 0 ); + + es_format_Init( &p_blend->fmt_out, VIDEO_ES, 0 ); + p_blend->fmt_out.video.i_x_offset = 0; + p_blend->fmt_out.video.i_y_offset = 0; + p_blend->fmt_out.video.i_chroma = i_chroma; + p_blend->fmt_out.video.i_aspect = i_aspect; + + /* The blend module will be loaded when needed with the real + * input format */ + p_blend->p_module = NULL; + + /* */ + vlc_object_attach( p_blend, p_spu ); +} +static void SpuRenderUpdateBlend( spu_t *p_spu, int i_out_width, int i_out_height, const video_format_t *p_in_fmt ) +{ + filter_t *p_blend = p_spu->p_blend; + + assert( p_blend ); + + /* */ + if( p_blend->p_module && p_blend->fmt_in.video.i_chroma != p_in_fmt->i_chroma ) + { + /* The chroma is not the same, we need to reload the blend module + * XXX to match the old behaviour just test !p_blend->fmt_in.video.i_chroma */ + module_Unneed( p_blend, p_blend->p_module ); + p_blend->p_module = NULL; + } + + /* */ + p_blend->fmt_in.video = *p_in_fmt; + + /* */ + p_blend->fmt_out.video.i_width = + p_blend->fmt_out.video.i_visible_width = i_out_width; + p_blend->fmt_out.video.i_height = + p_blend->fmt_out.video.i_visible_height = i_out_height; + + /* */ + if( !p_blend->p_module ) + p_blend->p_module = module_Need( p_blend, "video blending", 0, 0 ); +} +static void SpuRenderCreateAndLoadText( spu_t *p_spu, int i_width, int i_height ) +{ + filter_t *p_text; + + assert( !p_spu->p_text ); + + p_spu->p_text = + p_text = vlc_custom_create( p_spu, sizeof(filter_t), + VLC_OBJECT_GENERIC, "spu text" ); + if( !p_text ) + return; + + es_format_Init( &p_text->fmt_in, VIDEO_ES, 0 ); + + es_format_Init( &p_text->fmt_out, VIDEO_ES, 0 ); + p_text->fmt_out.video.i_width = + p_text->fmt_out.video.i_visible_width = i_width; + p_text->fmt_out.video.i_height = + p_text->fmt_out.video.i_visible_height = i_height; + + p_text->pf_sub_buffer_new = spu_new_buffer; + p_text->pf_sub_buffer_del = spu_del_buffer; + + vlc_object_attach( p_text, p_spu ); + + /* FIXME TOCHECK shouldn't module_Need( , , psz_modulename, false ) do the + * same than these 2 calls ? */ + char *psz_modulename = var_CreateGetString( p_spu, "text-renderer" ); + if( psz_modulename && *psz_modulename ) + { + p_text->p_module = module_Need( p_text, "text renderer", + psz_modulename, true ); + } + free( psz_modulename ); + + if( !p_text->p_module ) + p_text->p_module = module_Need( p_text, "text renderer", NULL, false ); +} + +static filter_t *CreateAndLoadScale( vlc_object_t *p_obj, vlc_fourcc_t i_chroma ) +{ + filter_t *p_scale; + + p_scale = vlc_custom_create( p_obj, sizeof(filter_t), + VLC_OBJECT_GENERIC, "scale" ); + if( !p_scale ) + return NULL; + + es_format_Init( &p_scale->fmt_in, VIDEO_ES, 0 ); + p_scale->fmt_in.video.i_chroma = i_chroma; + p_scale->fmt_in.video.i_width = + p_scale->fmt_in.video.i_height = 32; + + es_format_Init( &p_scale->fmt_out, VIDEO_ES, 0 ); + p_scale->fmt_out.video.i_chroma = i_chroma; + p_scale->fmt_out.video.i_width = + p_scale->fmt_out.video.i_height = 16; + + p_scale->pf_vout_buffer_new = spu_new_video_buffer; + p_scale->pf_vout_buffer_del = spu_del_video_buffer; + + vlc_object_attach( p_scale, p_obj ); + p_scale->p_module = module_Need( p_scale, "video filter2", 0, 0 ); + + return p_scale; +} + +static void SpuRenderCreateAndLoadScale( spu_t *p_spu ) +{ + /* FIXME: We'll also be using it for YUVA and RGBA blending ... */ + + assert( !p_spu->p_scale ); + assert( !p_spu->p_scale_yuvp ); + p_spu->p_scale = CreateAndLoadScale( VLC_OBJECT(p_spu), VLC_FOURCC('Y','U','V','A') ); + p_spu->p_scale_yuvp = p_spu->p_scale_yuvp = CreateAndLoadScale( VLC_OBJECT(p_spu), VLC_FOURCC('Y','U','V','P') ); +} + +static void SpuRenderText( spu_t *p_spu, bool *pb_rerender_text, + subpicture_t *p_subpic, subpicture_region_t *p_region, int i_min_scale_ratio ) +{ + assert( p_region->fmt.i_chroma == VLC_FOURCC('T','E','X','T') ); + + if( !p_spu->p_text || !p_spu->p_text->p_module ) + goto exit; + + /* Setup 3 variables which can be used to render + * time-dependent text (and effects). The first indicates + * the total amount of time the text will be on screen, + * the second the amount of time it has already been on + * screen (can be a negative value as text is layed out + * before it is rendered) and the third is a feedback + * variable from the renderer - if the renderer sets it + * then this particular text is time-dependent, eg. the + * visual progress bar inside the text in karaoke and the + * text needs to be rendered multiple times in order for + * the effect to work - we therefore need to return the + * region to its original state at the end of the loop, + * instead of leaving it in YUVA or YUVP. + * Any renderer which is unaware of how to render + * time-dependent text can happily ignore the variables + * and render the text the same as usual - it should at + * least show up on screen, but the effect won't change + * the text over time. + */ + + /* FIXME why these variables are recreated every time and not + * when text renderer module was created ? */ + var_Create( p_spu->p_text, "spu-duration", VLC_VAR_TIME ); + var_Create( p_spu->p_text, "spu-elapsed", VLC_VAR_TIME ); + var_Create( p_spu->p_text, "text-rerender", VLC_VAR_BOOL ); + var_Create( p_spu->p_text, "scale", VLC_VAR_INTEGER ); + + var_SetTime( p_spu->p_text, "spu-duration", p_subpic->i_stop - p_subpic->i_start ); + var_SetTime( p_spu->p_text, "spu-elapsed", mdate() - p_subpic->i_start ); + var_SetBool( p_spu->p_text, "text-rerender", false ); + var_SetInteger( p_spu->p_text, "scale", i_min_scale_ratio ); + + if( p_spu->p_text->pf_render_html && p_region->psz_html ) + { + p_spu->p_text->pf_render_html( p_spu->p_text, + p_region, p_region ); + } + else if( p_spu->p_text->pf_render_text ) + { + p_spu->p_text->pf_render_text( p_spu->p_text, + p_region, p_region ); + } + *pb_rerender_text = var_GetBool( p_spu->p_text, "text-rerender" ); + + var_Destroy( p_spu->p_text, "spu-duration" ); + var_Destroy( p_spu->p_text, "spu-elapsed" ); + var_Destroy( p_spu->p_text, "text-rerender" ); + var_Destroy( p_spu->p_text, "scale" ); + +exit: + p_region->i_align |= SUBPICTURE_RENDERED; +} + +static void SpuRenderRegion( spu_t *p_spu, + picture_t *p_pic_dst, picture_t *p_pic_src, + subpicture_t *p_subpic, subpicture_region_t *p_region, + const int i_scale_width_orig, const int i_scale_height_orig, + const int pi_subpic_x[SCALE_SIZE], + const int pi_scale_width[SCALE_SIZE], + const int pi_scale_height[SCALE_SIZE], + const video_format_t *p_fmt ) +{ + video_format_t fmt_original; + bool b_rerender_text; + bool b_restore_format = false; + int i_fade_alpha; + int i_x_offset; + int i_y_offset; + int i_scale_idx; + int i_inv_scale_x; + int i_inv_scale_y; + filter_t *p_scale; + + vlc_assert_locked( &p_spu->subpicture_lock ); + + fmt_original = p_region->fmt; + b_rerender_text = false; + if( p_region->fmt.i_chroma == VLC_FOURCC('T','E','X','T') ) + { + SpuRenderText( p_spu, &b_rerender_text, p_subpic, p_region, __MIN(i_scale_width_orig, i_scale_height_orig) ); + b_restore_format = b_rerender_text; + + /* Check if the rendering has failed ... */ + if( p_region->fmt.i_chroma == VLC_FOURCC('T','E','X','T') ) + goto exit; + } + + if( p_region->i_align & SUBPICTURE_RENDERED ) + { + /* We are using a region which come from rendered text */ + i_scale_idx = SCALE_TEXT; + i_inv_scale_x = i_scale_width_orig; + i_inv_scale_y = i_scale_height_orig; + } + else + { + i_scale_idx = SCALE_DEFAULT; + i_inv_scale_x = 1000; + i_inv_scale_y = 1000; + } + + i_x_offset = (p_region->i_x + pi_subpic_x[ i_scale_idx ]) * i_inv_scale_x / 1000; + i_y_offset = (p_region->i_y + p_subpic->i_y) * i_inv_scale_y / 1000; + + /* Force palette if requested + * FIXME b_force_palette and b_force_crop are applied to all subpictures using palette + * instead of only the right one (being the dvd spu). + */ + const bool b_using_palette = p_region->fmt.i_chroma == VLC_FOURCC('Y','U','V','P'); + const bool b_force_palette = b_using_palette && p_spu->b_force_palette; + const bool b_force_crop = b_force_palette && p_spu->b_force_crop; + + if( b_force_palette ) + { + /* It looks so wrong I won't comment + * p_palette->palette is [256][4] with a int i_entries + * p_spu->palette is [4][4] + * */ + p_region->fmt.p_palette->i_entries = 4; + memcpy( p_region->fmt.p_palette->palette, p_spu->palette, 4*sizeof(uint32_t) ); + } + + if( b_using_palette ) + p_scale = p_spu->p_scale_yuvp; + else + p_scale = p_spu->p_scale; + + if( p_scale && + ( ( pi_scale_width[i_scale_idx] > 0 && pi_scale_width[i_scale_idx] != 1000 ) || + ( pi_scale_height[i_scale_idx] > 0 && pi_scale_height[i_scale_idx] != 1000 ) || + ( b_force_palette ) ) ) + { + const unsigned i_dst_width = p_region->fmt.i_width * pi_scale_width[i_scale_idx] / 1000; + const unsigned i_dst_height = p_region->fmt.i_height * pi_scale_height[i_scale_idx] / 1000; + + /* Destroy if cache is unusable */ + if( p_region->p_cache ) + { + if( p_region->p_cache->fmt.i_width != i_dst_width || + p_region->p_cache->fmt.i_height != i_dst_height || + b_force_palette ) + { + p_subpic->pf_destroy_region( VLC_OBJECT(p_spu), + p_region->p_cache ); + p_region->p_cache = NULL; + } + } + + /* Scale if needed into cache */ + if( !p_region->p_cache ) + { + picture_t *p_pic; + + p_scale->fmt_in.video = p_region->fmt; + p_scale->fmt_out.video = p_region->fmt; + + p_region->p_cache = + p_subpic->pf_create_region( VLC_OBJECT(p_spu), + &p_scale->fmt_out.video ); + p_region->p_cache->p_next = p_region->p_next; + + if( p_scale->fmt_out.video.p_palette ) + *p_scale->fmt_out.video.p_palette = + *p_region->fmt.p_palette; + + vout_CopyPicture( p_spu, &p_region->p_cache->picture, + &p_region->picture ); + + p_scale->fmt_out.video.i_width = i_dst_width; + p_scale->fmt_out.video.i_height = i_dst_height; + + p_scale->fmt_out.video.i_visible_width = + p_region->fmt.i_visible_width * pi_scale_width[ i_scale_idx ] / 1000; + p_scale->fmt_out.video.i_visible_height = + p_region->fmt.i_visible_height * pi_scale_height[ i_scale_idx ] / 1000; + + p_region->p_cache->fmt = p_scale->fmt_out.video; + p_region->p_cache->i_x = p_region->i_x * pi_scale_width[ i_scale_idx ] / 1000; + p_region->p_cache->i_y = p_region->i_y * pi_scale_height[ i_scale_idx ] / 1000; + p_region->p_cache->i_align = p_region->i_align; + p_region->p_cache->i_alpha = p_region->i_alpha; + + p_pic = NULL; + if( p_scale->p_module ) + p_pic = p_scale->pf_video_filter( p_scale, &p_region->p_cache->picture ); + else + msg_Err( p_spu, "scaling failed (module not loaded)" ); + if( p_pic ) + { + p_region->p_cache->picture = *p_pic; + free( p_pic ); + } + } + + /* And use the scaled picture */ + if( p_region->p_cache ) + { + p_region = p_region->p_cache; + fmt_original = p_region->fmt; + } + } + + if( p_region->i_align & SUBPICTURE_ALIGN_BOTTOM ) + { + i_y_offset = p_fmt->i_height - p_region->fmt.i_height - + (p_subpic->i_y + p_region->i_y) * i_inv_scale_y / 1000; + } + else if ( !(p_region->i_align & SUBPICTURE_ALIGN_TOP) ) + { + i_y_offset = p_fmt->i_height / 2 - p_region->fmt.i_height / 2; + } + + if( p_region->i_align & SUBPICTURE_ALIGN_RIGHT ) + { + i_x_offset = p_fmt->i_width - p_region->fmt.i_width - + (pi_subpic_x[ i_scale_idx ] + p_region->i_x) + * i_inv_scale_x / 1000; + } + else if ( !(p_region->i_align & SUBPICTURE_ALIGN_LEFT) ) + { + i_x_offset = p_fmt->i_width / 2 - p_region->fmt.i_width / 2; + } + + if( p_subpic->b_absolute ) + { + i_x_offset = (p_region->i_x + + pi_subpic_x[ i_scale_idx ] * + pi_scale_width[ i_scale_idx ] / 1000) + * i_inv_scale_x / 1000; + i_y_offset = (p_region->i_y + + p_subpic->i_y * pi_scale_height[ i_scale_idx ] / 1000) + * i_inv_scale_y / 1000; + + } + + i_x_offset = __MAX( i_x_offset, 0 ); + i_y_offset = __MAX( i_y_offset, 0 ); + + if( p_spu->i_margin != 0 && !b_force_crop ) + { + int i_diff = 0; + int i_low = (i_y_offset - p_spu->i_margin) * i_inv_scale_y / 1000; + int i_high = i_low + p_region->fmt.i_height; + + /* crop extra margin to keep within bounds */ + if( i_low < 0 ) + i_diff = i_low; + if( i_high > (int)p_fmt->i_height ) + i_diff = i_high - p_fmt->i_height; + i_y_offset -= ( p_spu->i_margin * i_inv_scale_y / 1000 + i_diff ); + } + + /* Force cropping if requested */ + if( b_force_crop ) + { + video_format_t *p_fmt = &p_region->fmt; + int i_crop_x = p_spu->i_crop_x * pi_scale_width[ i_scale_idx ] / 1000 + * i_inv_scale_x / 1000; + int i_crop_y = p_spu->i_crop_y * pi_scale_height[ i_scale_idx ] / 1000 + * i_inv_scale_y / 1000; + int i_crop_width = p_spu->i_crop_width * pi_scale_width[ i_scale_idx ] / 1000 + * i_inv_scale_x / 1000; + int i_crop_height = p_spu->i_crop_height * pi_scale_height[ i_scale_idx ] / 1000 + * i_inv_scale_y / 1000; + + /* Find the intersection */ + if( i_crop_x + i_crop_width <= i_x_offset || + i_x_offset + (int)p_fmt->i_visible_width < i_crop_x || + i_crop_y + i_crop_height <= i_y_offset || + i_y_offset + (int)p_fmt->i_visible_height < i_crop_y ) + { + /* No intersection */ + p_fmt->i_visible_width = p_fmt->i_visible_height = 0; + } + else + { + int i_x, i_y, i_x_end, i_y_end; + i_x = __MAX( i_crop_x, i_x_offset ); + i_y = __MAX( i_crop_y, i_y_offset ); + i_x_end = __MIN( i_crop_x + i_crop_width, + i_x_offset + (int)p_fmt->i_visible_width ); + i_y_end = __MIN( i_crop_y + i_crop_height, + i_y_offset + (int)p_fmt->i_visible_height ); + + p_fmt->i_x_offset = i_x - i_x_offset; + p_fmt->i_y_offset = i_y - i_y_offset; + p_fmt->i_visible_width = i_x_end - i_x; + p_fmt->i_visible_height = i_y_end - i_y; + + i_x_offset = i_x; + i_y_offset = i_y; + } + b_restore_format = true; + } + + i_x_offset = __MAX( i_x_offset, 0 ); + i_y_offset = __MAX( i_y_offset, 0 ); + + /* Compute alpha blend value */ + i_fade_alpha = 255; + if( p_subpic->b_fade ) + { + mtime_t i_fade_start = ( p_subpic->i_stop + + p_subpic->i_start ) / 2; + mtime_t i_now = mdate(); + if( i_now >= i_fade_start && p_subpic->i_stop > i_fade_start ) + { + i_fade_alpha = 255 * ( p_subpic->i_stop - i_now ) / + ( p_subpic->i_stop - i_fade_start ); + } + } + + /* Update the blender */ + SpuRenderUpdateBlend( p_spu, p_fmt->i_width, p_fmt->i_height, &p_region->fmt ); + + if( p_spu->p_blend->p_module ) + { + p_spu->p_blend->pf_video_blend( p_spu->p_blend, p_pic_dst, + p_pic_src, &p_region->picture, i_x_offset, i_y_offset, + i_fade_alpha * p_subpic->i_alpha * p_region->i_alpha / 65025 ); + } + else + { + msg_Err( p_spu, "blending %4.4s to %4.4s failed", + (char *)&p_spu->p_blend->fmt_out.video.i_chroma, + (char *)&p_spu->p_blend->fmt_out.video.i_chroma ); + } + +exit: + if( b_rerender_text ) + { + /* Some forms of subtitles need to be re-rendered more than + * once, eg. karaoke. We therefore restore the region to its + * pre-rendered state, so the next time through everything is + * calculated again. + */ + p_region->picture.pf_release( &p_region->picture ); + memset( &p_region->picture, 0, sizeof( picture_t ) ); + p_region->i_align &= ~SUBPICTURE_RENDERED; + } + if( b_restore_format ) + p_region->fmt = fmt_original; +} + +void spu_RenderSubpictures( spu_t *p_spu, video_format_t *p_fmt, + picture_t *p_pic_dst, picture_t *p_pic_src, + subpicture_t *p_subpic, + int i_scale_width_orig, int i_scale_height_orig ) +{ + int i_source_video_width; + int i_source_video_height; + subpicture_t *p_subpic_v; + + /* Get lock */ + vlc_mutex_lock( &p_spu->subpicture_lock ); + + for( p_subpic_v = p_subpic; + p_subpic_v != NULL && p_subpic_v->i_status != FREE_SUBPICTURE; + p_subpic_v = p_subpic_v->p_next ) + { + if( p_subpic_v->pf_pre_render ) + p_subpic_v->pf_pre_render( p_fmt, p_spu, p_subpic_v ); + } + + if( i_scale_width_orig <= 0 ) + i_scale_width_orig = 1000; + if( i_scale_height_orig <= 0 ) + i_scale_height_orig = 1000; + + i_source_video_width = p_fmt->i_width * 1000 / i_scale_width_orig; + i_source_video_height = p_fmt->i_height * 1000 / i_scale_height_orig; + + /* Check i_status again to make sure spudec hasn't destroyed the subpic */ + for( ; ( p_subpic != NULL ) && ( p_subpic->i_status != FREE_SUBPICTURE ); p_subpic = p_subpic->p_next ) + { + subpicture_region_t *p_region; + int pi_scale_width[ SCALE_SIZE ]; + int pi_scale_height[ SCALE_SIZE ]; + int pi_subpic_x[ SCALE_SIZE ]; + int k; + + /* If the source video and subtitles stream agree on the size of + * the video then disregard all further references to the subtitle + * stream. + */ + if( ( i_source_video_height == p_subpic->i_original_picture_height ) && + ( i_source_video_width == p_subpic->i_original_picture_width ) ) + { + /* FIXME this looks wrong */ + p_subpic->i_original_picture_height = 0; + p_subpic->i_original_picture_width = 0; + } + + for( k = 0; k < SCALE_SIZE ; k++ ) + pi_subpic_x[ k ] = p_subpic->i_x; + + if( p_subpic->pf_update_regions ) + { + /* TODO do not reverse the scaling that was done before calling + * spu_RenderSubpictures, just pass it along (or do it inside + * spu_RenderSubpictures) */ + video_format_t fmt_org = *p_fmt; + fmt_org.i_width = + fmt_org.i_visible_width = i_source_video_width; + fmt_org.i_height = + fmt_org.i_visible_height = i_source_video_height; + + p_subpic->pf_update_regions( &fmt_org, p_spu, p_subpic, mdate() ); + } + + /* */ + p_region = p_subpic->p_region; + if( !p_region ) + continue; + + /* Create the blending module */ + if( !p_spu->p_blend ) + SpuRenderCreateBlend( p_spu, p_fmt->i_chroma, p_fmt->i_aspect ); + + /* Load the text rendering module; it is possible there is a + * text region somewhere in the subpicture other than the first + * element in the region list, so just load it anyway as we'll + * probably want it sooner or later. */ + if( !p_spu->p_text ) + SpuRenderCreateAndLoadText( p_spu, p_fmt->i_width, p_fmt->i_height ); + + if( p_spu->p_text ) + { + subpicture_region_t *p_text_region = p_subpic->p_region; + + /* Only overwrite the size fields if the region is still in + * pre-rendered TEXT format. We have to traverse the subregion + * list because if more than one subregion is present, the text + * region isn't guarentteed to be the first in the list, and + * only text regions use this flag. All of this effort assists + * with the rescaling of text that has been rendered at native + * resolution, rather than video resolution. + */ + while( p_text_region && + p_text_region->fmt.i_chroma != VLC_FOURCC('T','E','X','T') ) + { + p_text_region = p_text_region->p_next; + } + + if( p_text_region && + ( ( p_text_region->i_align & SUBPICTURE_RENDERED ) == 0 ) ) + { + if( p_subpic->i_original_picture_height > 0 && + p_subpic->i_original_picture_width > 0 ) + { + p_spu->p_text->fmt_out.video.i_width = + p_spu->p_text->fmt_out.video.i_visible_width = + p_subpic->i_original_picture_width; + p_spu->p_text->fmt_out.video.i_height = + p_spu->p_text->fmt_out.video.i_visible_height = + p_subpic->i_original_picture_height; + } + else + { + p_spu->p_text->fmt_out.video.i_width = + p_spu->p_text->fmt_out.video.i_visible_width = + p_fmt->i_width; + p_spu->p_text->fmt_out.video.i_height = + p_spu->p_text->fmt_out.video.i_visible_height = + p_fmt->i_height; + } + } + + /* XXX for text: + * scale[] allows to pass from rendered size (by text module) to video output size */ + pi_scale_width[SCALE_TEXT] = p_fmt->i_width * 1000 / + p_spu->p_text->fmt_out.video.i_width; + pi_scale_height[SCALE_TEXT]= p_fmt->i_height * 1000 / + p_spu->p_text->fmt_out.video.i_height; + } + else + { + /* Just set a value to avoid using invalid memory while looping over the array */ + pi_scale_width[SCALE_TEXT] = + pi_scale_height[SCALE_TEXT]= 1000; + } + + /* XXX for default: + * scale[] allows to pass from native (either video or original) size to output size */ + + if( p_subpic->i_original_picture_height > 0 && + p_subpic->i_original_picture_width > 0 ) + { + pi_scale_width[SCALE_DEFAULT] = p_fmt->i_width * 1000 / p_subpic->i_original_picture_width; + pi_scale_height[SCALE_DEFAULT] = p_fmt->i_height * 1000 / p_subpic->i_original_picture_height; + } + else + { + pi_scale_width[ SCALE_DEFAULT ] = i_scale_width_orig; + pi_scale_height[ SCALE_DEFAULT ] = i_scale_height_orig; + } + + for( k = 0; k < SCALE_SIZE ; k++ ) + { + /* Case of both width and height being specified has been dealt + * with above by instead rendering to an output pane of the + * explicit dimensions specified - we don't need to scale it. + */ + if( p_subpic->i_original_picture_height > 0 && + p_subpic->i_original_picture_width <= 0 ) + { + pi_scale_height[ k ] = pi_scale_height[ k ] * i_source_video_height / + p_subpic->i_original_picture_height; + pi_scale_width[ k ] = pi_scale_width[ k ] * i_source_video_height / + p_subpic->i_original_picture_height; + } + } + + /* Set default subpicture aspect ratio */ + if( !p_region->fmt.i_sar_num || !p_region->fmt.i_sar_den ) + { + if( p_region->fmt.i_aspect != 0 ) + { + p_region->fmt.i_sar_den = p_region->fmt.i_aspect; + p_region->fmt.i_sar_num = VOUT_ASPECT_FACTOR; + } + else + { + p_region->fmt.i_sar_den = p_fmt->i_sar_den; + p_region->fmt.i_sar_num = p_fmt->i_sar_num; + } + } + + /* Take care of the aspect ratio */ + if( ( p_region->fmt.i_sar_num * p_fmt->i_sar_den ) != + ( p_region->fmt.i_sar_den * p_fmt->i_sar_num ) ) + { + for( k = 0; k < SCALE_SIZE; k++ ) + { + pi_scale_width[k] = pi_scale_width[ k ] * + (int64_t)p_region->fmt.i_sar_num * p_fmt->i_sar_den / + p_region->fmt.i_sar_den / p_fmt->i_sar_num; + + pi_subpic_x[k] = p_subpic->i_x * pi_scale_width[ k ] / 1000; + } + } + + /* Load the scaling module when needed */ + if( !p_spu->p_scale ) + { + bool b_scale_used = false; + + for( k = 0; k < SCALE_SIZE; k++ ) + { + const int i_scale_w = pi_scale_width[k]; + const int i_scale_h = pi_scale_height[k]; + if( ( i_scale_w > 0 && i_scale_w != 1000 ) || ( i_scale_h > 0 && i_scale_h != 1000 ) ) + b_scale_used = true; + } + + if( b_scale_used ) + SpuRenderCreateAndLoadScale( p_spu ); + } + + for( ; p_region != NULL; p_region = p_region->p_next ) + SpuRenderRegion( p_spu, p_pic_dst, p_pic_src, + p_subpic, p_region, i_scale_width_orig, i_scale_height_orig, + pi_subpic_x, pi_scale_width, pi_scale_height, + p_fmt ); + } + + vlc_mutex_unlock( &p_spu->subpicture_lock ); +} + +/***************************************************************************** + * spu_SortSubpictures: find the subpictures to display + ***************************************************************************** + * This function parses all subpictures and decides which ones need to be + * displayed. This operation does not need lock, since only READY_SUBPICTURE + * are handled. If no picture has been selected, display_date will depend on + * the subpicture. + * We also check for ephemer DVD subpictures (subpictures that have + * to be removed if a newer one is available), which makes it a lot + * more difficult to guess if a subpicture has to be rendered or not. + *****************************************************************************/ +subpicture_t *spu_SortSubpictures( spu_t *p_spu, mtime_t display_date, + bool b_paused ) +{ + int i_index, i_channel; + subpicture_t *p_subpic = NULL; + subpicture_t *p_ephemer; + mtime_t ephemer_date; + + /* Run subpicture filters */ + filter_chain_SubFilter( p_spu->p_chain, display_date ); + + /* We get an easily parsable chained list of subpictures which + * ends with NULL since p_subpic was initialized to NULL. */ + for( i_channel = 0; i_channel < p_spu->i_channel; i_channel++ ) + { + p_ephemer = 0; + ephemer_date = 0; + + for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ ) + { + if( p_spu->p_subpicture[i_index].i_channel != i_channel || + p_spu->p_subpicture[i_index].i_status != READY_SUBPICTURE ) + { + continue; + } + if( display_date && + display_date < p_spu->p_subpicture[i_index].i_start ) + { + /* Too early, come back next monday */ + continue; + } + + if( p_spu->p_subpicture[i_index].i_start > ephemer_date ) + ephemer_date = p_spu->p_subpicture[i_index].i_start; + + if( display_date > p_spu->p_subpicture[i_index].i_stop && + ( !p_spu->p_subpicture[i_index].b_ephemer || + p_spu->p_subpicture[i_index].i_stop > + p_spu->p_subpicture[i_index].i_start ) && + !( p_spu->p_subpicture[i_index].b_pausable && + b_paused ) ) + { + /* Too late, destroy the subpic */ + spu_DestroySubpicture( p_spu, &p_spu->p_subpicture[i_index] ); + continue; + } + + /* If this is an ephemer subpic, add it to our list */ + if( p_spu->p_subpicture[i_index].b_ephemer ) + { + p_spu->p_subpicture[i_index].p_next = p_ephemer; + p_ephemer = &p_spu->p_subpicture[i_index]; + + continue; + } + + p_spu->p_subpicture[i_index].p_next = p_subpic; + p_subpic = &p_spu->p_subpicture[i_index]; + } + + /* If we found ephemer subpictures, check if they have to be + * displayed or destroyed */ + while( p_ephemer != NULL ) + { + subpicture_t *p_tmp = p_ephemer; + p_ephemer = p_ephemer->p_next; + + if( p_tmp->i_start < ephemer_date ) + { + /* Ephemer subpicture has lived too long */ + spu_DestroySubpicture( p_spu, p_tmp ); + } + else + { + /* Ephemer subpicture can still live a bit */ + p_tmp->p_next = p_subpic; + p_subpic = p_tmp; + } + } + } + + return p_subpic; +} + +/***************************************************************************** + * SpuClearChannel: clear an spu channel + ***************************************************************************** + * This function destroys the subpictures which belong to the spu channel + * corresponding to i_channel_id. + *****************************************************************************/ +static void SpuClearChannel( spu_t *p_spu, int i_channel, bool b_locked ) +{ + int i_subpic; /* subpicture index */ + subpicture_t *p_subpic = NULL; /* first free subpicture */ + + if( !b_locked ) + vlc_mutex_lock( &p_spu->subpicture_lock ); + + for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ ) + { + p_subpic = &p_spu->p_subpicture[i_subpic]; + if( p_subpic->i_status == FREE_SUBPICTURE + || ( p_subpic->i_status != RESERVED_SUBPICTURE + && p_subpic->i_status != READY_SUBPICTURE ) ) + { + continue; + } + + if( p_subpic->i_channel == i_channel ) + { + while( p_subpic->p_region ) + { + subpicture_region_t *p_region = p_subpic->p_region; + p_subpic->p_region = p_region->p_next; + spu_DestroyRegion( p_spu, p_region ); + } + + if( p_subpic->pf_destroy ) p_subpic->pf_destroy( p_subpic ); + p_subpic->i_status = FREE_SUBPICTURE; + } + } + + if( !b_locked ) + vlc_mutex_unlock( &p_spu->subpicture_lock ); +} + +/***************************************************************************** + * spu_ControlDefault: default methods for the subpicture unit control. + *****************************************************************************/ +static int spu_vaControlDefault( spu_t *p_spu, int i_query, va_list args ) +{ + int *pi, i; + + switch( i_query ) + { + case SPU_CHANNEL_REGISTER: + pi = (int *)va_arg( args, int * ); + if( pi ) *pi = p_spu->i_channel++; + break; + + case SPU_CHANNEL_CLEAR: + i = (int)va_arg( args, int ); + SpuClearChannel( p_spu, i, false ); + break; + + default: + msg_Dbg( p_spu, "control query not supported" ); + return VLC_EGENERIC; + } + + return VLC_SUCCESS; +} + +/***************************************************************************** + * Object variables callbacks + *****************************************************************************/ + +/***************************************************************************** + * UpdateSPU: update subpicture settings + ***************************************************************************** + * This function is called from CropCallback and at initialization time, to + * retrieve crop information from the input. + *****************************************************************************/ +static void UpdateSPU( spu_t *p_spu, vlc_object_t *p_object ) +{ + vlc_value_t val; + + vlc_mutex_lock( &p_spu->subpicture_lock ); + + p_spu->b_force_palette = false; + p_spu->b_force_crop = false; + + if( var_Get( p_object, "highlight", &val ) || !val.b_bool ) + { + vlc_mutex_unlock( &p_spu->subpicture_lock ); + return; + } + + p_spu->b_force_crop = true; + var_Get( p_object, "x-start", &val ); + p_spu->i_crop_x = val.i_int; + var_Get( p_object, "y-start", &val ); + p_spu->i_crop_y = val.i_int; + var_Get( p_object, "x-end", &val ); + p_spu->i_crop_width = val.i_int - p_spu->i_crop_x; + var_Get( p_object, "y-end", &val ); + p_spu->i_crop_height = val.i_int - p_spu->i_crop_y; + + if( var_Get( p_object, "menu-palette", &val ) == VLC_SUCCESS ) + { + memcpy( p_spu->palette, val.p_address, 16 ); + p_spu->b_force_palette = true; + } + vlc_mutex_unlock( &p_spu->subpicture_lock ); + + msg_Dbg( p_object, "crop: %i,%i,%i,%i, palette forced: %i", + p_spu->i_crop_x, p_spu->i_crop_y, + p_spu->i_crop_width, p_spu->i_crop_height, + p_spu->b_force_palette ); +} + +/***************************************************************************** + * CropCallback: called when the highlight properties are changed + ***************************************************************************** + * This callback is called from the input thread when we need cropping + *****************************************************************************/ +static int CropCallback( vlc_object_t *p_object, char const *psz_var, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) +{ + (void)psz_var; (void)oldval; (void)newval; + UpdateSPU( (spu_t *)p_data, p_object ); + return VLC_SUCCESS; +} + +/***************************************************************************** + * Buffers allocation callbacks for the filters + *****************************************************************************/ +static subpicture_t *sub_new_buffer( filter_t *p_filter ) +{ + filter_owner_sys_t *p_sys = p_filter->p_owner; + subpicture_t *p_subpicture = spu_CreateSubpicture( p_sys->p_spu ); + if( p_subpicture ) p_subpicture->i_channel = p_sys->i_channel; + return p_subpicture; +} + +static void sub_del_buffer( filter_t *p_filter, subpicture_t *p_subpic ) +{ + filter_owner_sys_t *p_sys = p_filter->p_owner; + spu_DestroySubpicture( p_sys->p_spu, p_subpic ); +} + +static subpicture_t *spu_new_buffer( filter_t *p_filter ) +{ + (void)p_filter; + subpicture_t *p_subpic = (subpicture_t *)malloc(sizeof(subpicture_t)); + if( !p_subpic ) return NULL; + memset( p_subpic, 0, sizeof(subpicture_t) ); + p_subpic->b_absolute = true; + + p_subpic->pf_create_region = __spu_CreateRegion; + p_subpic->pf_make_region = __spu_MakeRegion; + p_subpic->pf_destroy_region = __spu_DestroyRegion; + + return p_subpic; +} + +static void spu_del_buffer( filter_t *p_filter, subpicture_t *p_subpic ) +{ + while( p_subpic->p_region ) + { + subpicture_region_t *p_region = p_subpic->p_region; + p_subpic->p_region = p_region->p_next; + p_subpic->pf_destroy_region( VLC_OBJECT(p_filter), p_region ); + } + + free( p_subpic ); +} + +static picture_t *spu_new_video_buffer( filter_t *p_filter ) +{ + picture_t *p_picture = malloc( sizeof(picture_t) ); + if( !p_picture ) return NULL; + if( vout_AllocatePicture( p_filter, p_picture, + p_filter->fmt_out.video.i_chroma, + p_filter->fmt_out.video.i_width, + p_filter->fmt_out.video.i_height, + p_filter->fmt_out.video.i_aspect ) + != VLC_SUCCESS ) + { + free( p_picture ); + return NULL; + } + + p_picture->pf_release = RegionPictureRelease; + + return p_picture; +} + +static void spu_del_video_buffer( filter_t *p_filter, picture_t *p_pic ) +{ + (void)p_filter; + if( p_pic ) + { + free( p_pic->p_data_orig ); + free( p_pic ); + } +} + +static int SubFilterCallback( vlc_object_t *p_object, char const *psz_var, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) +{ + VLC_UNUSED(p_object); VLC_UNUSED(oldval); + VLC_UNUSED(newval); VLC_UNUSED(psz_var); + + spu_t *p_spu = (spu_t *)p_data; + vlc_mutex_lock( &p_spu->subpicture_lock ); + filter_chain_Reset( p_spu->p_chain, NULL, NULL ); + spu_ParseChain( p_spu ); + vlc_mutex_unlock( &p_spu->subpicture_lock ); + return VLC_SUCCESS; +} + +static int sub_filter_allocation_init( filter_t *p_filter, void *p_data ) +{ + spu_t *p_spu = (spu_t *)p_data; + + p_filter->pf_sub_buffer_new = sub_new_buffer; + p_filter->pf_sub_buffer_del = sub_del_buffer; + + filter_owner_sys_t *p_sys = malloc( sizeof(filter_owner_sys_t) ); + if( !p_sys ) return VLC_EGENERIC; + + p_filter->p_owner = p_sys; + spu_Control( p_spu, SPU_CHANNEL_REGISTER, &p_sys->i_channel ); + p_sys->p_spu = p_spu; + + return VLC_SUCCESS; +} + +static void sub_filter_allocation_clear( filter_t *p_filter ) +{ + filter_owner_sys_t *p_sys = p_filter->p_owner; + SpuClearChannel( p_sys->p_spu, p_sys->i_channel, true ); + free( p_filter->p_owner ); +} diff --git a/VLC/wincp.c b/VLC/wincp.c new file mode 100644 index 0000000..004d990 --- /dev/null +++ b/VLC/wincp.c @@ -0,0 +1,227 @@ +/***************************************************************************** + * wincp.c: Guessing "local" ANSI code page on Microsoft Windows® + ***************************************************************************** + * + * Copyright © 2006-2007 Rémi Denis-Courmont + * $Id$ + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/*** We need your help to complete this file!! Look for FIXME ***/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "vlc_common.h" + +#ifndef WIN32 +# include +#else +# include +#endif + +#ifdef __APPLE__ +# include +# include +#endif + +#include "vlc_charset.h" + + +#ifndef WIN32 /* should work on Win32, but useless */ +static inline int locale_match (const char *tab, const char *locale) +{ + for (;*tab; tab += 2) + if (memcmp (tab, locale, 2) == 0) + return 0; + return 1; +} + + +/** + * @return a fallback characters encoding to be used, given a locale. + */ +static const char *FindFallbackEncoding (const char *locale) +{ + if ((locale == NULL) || (strlen (locale) < 2) + || !strcasecmp (locale, "POSIX")) + return "CP1252"; /* Yeah, this is totally western-biased */ + + + /*** The ISO-8859 series (anything but Asia) ***/ + // Latin-1 Western-European languages (ISO-8859-1) + static const char western[] = + "aa" "af" "an" "br" "ca" "da" "de" "en" "es" "et" "eu" "fi" "fo" "fr" + "ga" "gd" "gl" "gv" "id" "is" "it" "kl" "kw" "mg" "ms" "nb" "nl" "nn" + "no" "oc" "om" "pt" "so" "sq" "st" "sv" "tl" "uz" "wa" "xh" "zu" + "eo" "mt" "cy"; + if (!locale_match (western, locale)) + return "CP1252"; // Compatible Microsoft superset + + // Latin-2 Slavic languages (ISO-8859-2) + static const char slavic[] = "bs" "cs" "hr" "hu" "pl" "ro" "sk" "sl"; + if (!locale_match (slavic, locale)) + return "CP1250"; // CP1250 is more common, but incompatible + + // Latin-3 Southern European languages (ISO-8859-3) + // "eo" and "mt" -> Latin-1 instead, I presume(?). + // "tr" -> ISO-8859-9 instead + + // Latin-4 North-European languages (ISO-8859-4) + // -> Latin-1 instead + + /* Cyrillic alphabet languages (ISO-8859-5) */ + static const char cyrillic[] = "be" "bg" "mk" "ru" "sr"; + if (!locale_match (cyrillic, locale)) + return "CP1251"; // KOI8, ISO-8859-5 and CP1251 are incompatible(?) + + /* Arabic (ISO-8859-6) */ + if (!locale_match ("ar", locale)) + // FIXME: someone check if we should return CP1256 or ISO-8859-6 + return "CP1256"; // CP1256 is(?) more common, but incompatible(?) + + /* Greek (ISO-8859-7) */ + if (!locale_match ("el", locale)) + // FIXME: someone check if we should return CP1253 or ISO-8859-7 + return "CP1253"; // CP1253 is(?) more common and less incompatible + + /* Hebrew (ISO-8859-8) */ + if (!locale_match ("he" "iw" "yi", locale)) + return "ISO-8859-8"; // CP1255 is reportedly screwed up + + /* Latin-5 Turkish (ISO-8859-9) */ + if (!locale_match ("tr" "ku", locale)) + return "CP1254"; // Compatible Microsoft superset + + /* Latin-6 “North-European” languages (ISO-8859-10) */ + /* It is so much north European that glibc only uses that for Luganda + * which is spoken in Uganda... unless someone complains, I'm not + * using this one; let's fallback to CP1252 here. */ + + // ISO-8859-11 does arguably not exist. Thai is handled below. + + // ISO-8859-12 really doesn't exist. + + // Latin-7 Baltic languages (ISO-8859-13) + if (!locale_match ("lt" "lv" "mi", locale)) + // FIXME: mi = New Zealand, doesn't sound baltic! + return "CP1257"; // Compatible Microsoft superset + + // Latin-8 Celtic languages (ISO-8859-14) + // "cy" -> use Latin-1 instead (most likely English or French) + + // Latin-9 (ISO-8859-15) -> see Latin-1 + + // Latin-10 (ISO-8859-16) does not seem to be used + + /*** KOI series ***/ + // For Russian, we use CP1251 + if (!locale_match ("uk", locale)) + return "KOI8-U"; + + if (!locale_match ("tg", locale)) + return "KOI8-T"; + + /*** Asia ***/ + // Japanese + if (!locale_match ("jp", locale)) + return "SHIFT-JIS"; // Shift-JIS is way more common than EUC-JP + + // Korean + if (!locale_match ("ko", locale)) + return "EUC-KR"; + + // Thai + if (!locale_match ("th", locale)) + return "TIS-620"; + + // Vietnamese (FIXME: more infos needed) + if (!locale_match ("vt", locale)) + /* VISCII is probably a bad idea as it is not extended ASCII */ + /* glibc has TCVN5712-1 */ + return "CP1258"; + + /* Kazakh (FIXME: more infos needed) */ + if (!locale_match ("kk", locale)) + return "PT154"; + + // Chinese. The politically incompatible character sets. + if (!locale_match ("zh", locale)) + { + if ((strlen (locale) >= 5) && (locale[2] != '_')) + locale += 3; + + // Hong Kong + if (!locale_match ("HK", locale)) + return "BIG5-HKSCS"; /* FIXME: use something else? */ + + // Taiwan island + if (!locale_match ("TW", locale)) + return "BIG5"; + + // People's Republic of China and Singapore + /* + * GB18030 can represent any Unicode code point + * (like UTF-8), while remaining compatible with GBK + * FIXME: is it compatible with GB2312? if not, should we + * use GB2312 instead? + */ + return "GB18030"; + } + + return "ASCII"; +} +#endif + +/** + * GetFallbackEncoding() suggests an encoding to be used for non UTF-8 + * text files accord to the system's local settings. It is only a best + * guess. + */ +const char *GetFallbackEncoding( void ) +{ +#ifndef WIN32 + const char *psz_lang; + + psz_lang = getenv ("LC_ALL"); + if ((psz_lang == NULL) || !*psz_lang) + { + psz_lang = getenv ("LC_CTYPE"); + if ((psz_lang == NULL) || !*psz_lang) + psz_lang = getenv ("LANG"); + } + + return FindFallbackEncoding (psz_lang); +#else + static char buf[16] = ""; + + if (buf[0] == 0) + { + int cp = GetACP (); + + switch (cp) + { + case 1255: // Hebrew, CP1255 screws up somewhat + strcpy (buf, "ISO-8859-8"); + break; + default: + snprintf (buf, sizeof (buf), "CP%u", cp); + } + } + return buf; +#endif +} diff --git a/build/Tuve.build/Tuve.pbxindex/categories.pbxbtree b/build/Tuve.build/Tuve.pbxindex/categories.pbxbtree new file mode 100644 index 0000000..a0f395d --- /dev/null +++ b/build/Tuve.build/Tuve.pbxindex/categories.pbxbtree Binary files differ diff --git a/build/Tuve.build/Tuve.pbxindex/cdecls.pbxbtree b/build/Tuve.build/Tuve.pbxindex/cdecls.pbxbtree new file mode 100644 index 0000000..5126ca8 --- /dev/null +++ b/build/Tuve.build/Tuve.pbxindex/cdecls.pbxbtree Binary files differ diff --git a/build/Tuve.build/Tuve.pbxindex/decls.pbxbtree b/build/Tuve.build/Tuve.pbxindex/decls.pbxbtree new file mode 100644 index 0000000..4024301 --- /dev/null +++ b/build/Tuve.build/Tuve.pbxindex/decls.pbxbtree Binary files differ diff --git a/build/Tuve.build/Tuve.pbxindex/files.pbxbtree b/build/Tuve.build/Tuve.pbxindex/files.pbxbtree new file mode 100644 index 0000000..fa62134 --- /dev/null +++ b/build/Tuve.build/Tuve.pbxindex/files.pbxbtree Binary files differ diff --git a/build/Tuve.build/Tuve.pbxindex/imports.pbxbtree b/build/Tuve.build/Tuve.pbxindex/imports.pbxbtree new file mode 100644 index 0000000..e67b970 --- /dev/null +++ b/build/Tuve.build/Tuve.pbxindex/imports.pbxbtree Binary files differ diff --git a/build/Tuve.build/Tuve.pbxindex/pbxindex.header b/build/Tuve.build/Tuve.pbxindex/pbxindex.header new file mode 100644 index 0000000..5eda34f --- /dev/null +++ b/build/Tuve.build/Tuve.pbxindex/pbxindex.header Binary files differ diff --git a/build/Tuve.build/Tuve.pbxindex/protocols.pbxbtree b/build/Tuve.build/Tuve.pbxindex/protocols.pbxbtree new file mode 100644 index 0000000..9e2e818 --- /dev/null +++ b/build/Tuve.build/Tuve.pbxindex/protocols.pbxbtree Binary files differ diff --git a/build/Tuve.build/Tuve.pbxindex/refs.pbxbtree b/build/Tuve.build/Tuve.pbxindex/refs.pbxbtree new file mode 100644 index 0000000..c1a86d7 --- /dev/null +++ b/build/Tuve.build/Tuve.pbxindex/refs.pbxbtree Binary files differ diff --git a/build/Tuve.build/Tuve.pbxindex/strings.pbxstrings/control b/build/Tuve.build/Tuve.pbxindex/strings.pbxstrings/control new file mode 100644 index 0000000..1f3cfba --- /dev/null +++ b/build/Tuve.build/Tuve.pbxindex/strings.pbxstrings/control Binary files differ diff --git a/build/Tuve.build/Tuve.pbxindex/strings.pbxstrings/strings b/build/Tuve.build/Tuve.pbxindex/strings.pbxstrings/strings new file mode 100644 index 0000000..209c792 --- /dev/null +++ b/build/Tuve.build/Tuve.pbxindex/strings.pbxstrings/strings Binary files differ diff --git a/build/Tuve.build/Tuve.pbxindex/subclasses.pbxbtree b/build/Tuve.build/Tuve.pbxindex/subclasses.pbxbtree new file mode 100644 index 0000000..ab28f71 --- /dev/null +++ b/build/Tuve.build/Tuve.pbxindex/subclasses.pbxbtree Binary files differ diff --git a/build/Tuve.build/Tuve.pbxindex/symbols0.pbxsymbols b/build/Tuve.build/Tuve.pbxindex/symbols0.pbxsymbols new file mode 100644 index 0000000..4ea5313 --- /dev/null +++ b/build/Tuve.build/Tuve.pbxindex/symbols0.pbxsymbols Binary files differ diff --git a/main.m b/main.m new file mode 100644 index 0000000..4d775b7 --- /dev/null +++ b/main.m @@ -0,0 +1,17 @@ +// +// main.m +// Tuve +// +// Created by Philippe Hausler on 9/8/08. +// Copyright Philippe Hausler 2008. All rights reserved. +// + +#import + +int main(int argc, char *argv[]) { + + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + int retVal = UIApplicationMain(argc, argv, nil, nil); + [pool release]; + return retVal; +}