31 июля 2010 г.

Конвертирование видео для iPhone/iPod Touch/ iPad с несколькими звуковыми дорожками и субтитрами

Конвертировать будем в систем Linux. Используемые утилиты:
ffmpeg - получение информации о фильме, извлечение дорожек, декодирование
mkvtools - получение информации о фильме, извлечение дорожек
MP4Box - собирание дорожек в m4v в файл
AtomicParsley - пропитывание тэгов

Алгоритм работы:
1. Извлекаем информацию о видео-файле
используем ffmpeg:

  1. $ ffmpeg -i <file_name>

ffmpeg не выводит тип субтитров (ASS/SRT), поэтому если тип файла видео mkv используем утилиту mkvinfo:

  1. $ mkvinfo <file_name>


2. Вычисляем конечного разрешение видео. Дело в том, что разрешение видео для iPhone что по вертикали, что по горизонтали должно быть кратно 16. Мы же будем привязывать разрешение к размеру экрана:

  1. # код на Python, переменные w,h - разрешение исходного видео ролика, _w, _h - разрешение финального видео
  2. _w = 480 # ширина будет равна ширине экрана
  3. _h = (h*_w)/w # вычисляем высоту
  4. tmp = _h%16
  5. if _h%16>7: # округляем высоту до ближайшего числа кратного 16
  6. _h += 16-_h%16
  7. else:
  8. _h -= _h%16


3. Конвертируем видео используя ffmpeg (используем двухпроходное кодирование), как собрать ffmpeg c поддержкой x264 я описывал здесь:

  1. $ ffmpeg -y -i <input_video> -an -vcodec "libx264" -b "600k" -s "<_w>x<_h>" -flags "+loop" -cmp "+chroma" -partitions "+parti4x4+partp8x8+partb8x8" -subq "5" -trellis "1" -refs "1" -coder "0" -me_range "16" -g "300" -keyint_min "25" -sc_threshold "40" -i_qfactor "0.71" -maxrate "300k" -bufsize "300k" -rc_eq "blurCplx^(1-qComp)" -qcomp "0.6" -qmin "15" -qmax "51" -qdiff "4" -level "30" <out_video>.mp4

в результате мы получили отдельно видео поток в <out_video>.mp4

4. Конвертируем аудио. Нам нужно получить стерео дорожку в формате AAC
Все хорошо кодируется если исходная аудио дорожка в стерео:

  1. $ ffmpeg -y -i <input_video> -map 0.1 -vn -acodec libfaac -ab 128k -ac 2 -ar 44100 <out_audio>.aac

если же аудио каналов больше то прогоняем через промежуточный файл:

  1. $ ffmpeg -y -i <input_video> -map 0.1 -vn -acodec ac3 -ab 448k -ar 44100 -ac 6 ./tmp.ac3
  2. $ ffmpeg -y -i ./tmp.ac3 -vn -acodec libfaac -ab 128k -ar 44100 -ac 2 <out_audio>.aac

есть лучше вариант:

  1. $ ffmpeg -y -i <input_video> -map 0.x -vn -acodec libfaac -ab 128k -ac 2 -ar 48000 -strict experimental <out.aac>


5. Извлекаем субтитры, если в формате SRT то хорошо, если в ASS - конвертируем в SRT

6. Собираем все наши дорожки в один файл M4V:

  1. $ MP4Box -add <out_video>.mp4 -add <out_audio1>.aac:lang=xxx:group=1 -add <out_audio2>:lang=xxx:disable:group=1 -add <out_subs1>.srt:group=2:lang=xxx -add <out_subs2>.srt:group=2:lang=xxx:disable <out_video>.m4v -new


7. MP4Box не правильно прописывает заголовки субтитров (alternativeGroup и subType), исправляем. В одном из следующих постов я расскажу о формате заголовка m4v файла.

8. Прописываем теги утилитой AtomicParsley

Скрипт который все это реализует я в общий доступ не выкладываю (он написан довольно криво, да и состоит в стадии тестирования), но если вы хотите, я могу его вым выслать с инструкцией по применению. Мой ящик

Так-же смотрите структуру MPEG4 файла

27 июля 2010 г.

MapKit в iOS

В моем старом приложении требовалась поддержка прошивки iPhone OS 2.х. Поэтому я не мог использовать все прелести фраемворка MapKit, который появился в 3.0 прошивке. В те далекие времена (около года назад) я отображал карту через UIWebView. Так как сейчас количество устройств с установленной на них прошивками 2.х ничтожно мало, было принято решение переходить на встроеные компоненты отображения карты.

Отобразить в вашем приложении карту можно используя класс MKMapView из фраемворка MapKit. Класс представляется множеством очевидных методов: mapType, zoomEnabled, region, showUserLocation, которые настраивают внешний вид карты. Но это все просто, мне было интересно добавлять на карту свои объекты. С этим мы и будем разбираться в данной статье.

Для добавления на карту элемента привязанного к одной геокоординате используется метод addAnnotation:. В качестве аргумента он принимает объект который наследует протокол MKAnnotation. Протокол реализует методы задания координаты нашей аннотации (coordinate), задание заголовка аннотации (title) и ее текста (subtitle). Вот самый простой пример объекта аннотации:

marker.h
  1. @interface marker : NSObject <MKAnnotation>
  2. {
  3. }
  4. @end


marker.m
  1. #import "marker.h"
  2.  
  3. @implementation marker
  4. #pragma mark MKAnnotation
  5. - (CLLocationCoordinate2D) coordinate
  6. {
  7.   return CLLocationCoordinate2DMake(46.46258530.750186);
  8. }
  9.  
  10. - (NSString *) title
  11. {
  12.   return @"This annotation";
  13. }

создадим экземпляр этого объекта и добавим его на карту:

  1. marker *= [[marker alloc] init];
  2. [mapView addAnnotation:m];
  3. [m release];

довольно просто, правда ведь? Если мы хотим кустимизировать нашу аннотацию, например изменить иконку иголочки, то нам необходимо использовать метод mapView:viewForAnnotation: делегата нашей карты, пример приведен в конце статьи.

Пусть теперь мы хотим нарисовать на карте объект привязанный к нескольким геокоординатам (например маршрут из точки А в точку В). Тут нужно добавлять не аннотацию, а оверлей addOverlay:. Добавим в него MKPolyline:

  1. CLLocationCoordinate2D mapCoords[6];
  2. mapCoords[ 0] = CLLocationCoordinate2DMake(46.476472, 30.704776);
  3. mapCoords[ 1] = CLLocationCoordinate2DMake(46.469664, 30.732229);
  4. mapCoords[ 2] = CLLocationCoordinate2DMake(46.462585, 30.750186);
  5. mapCoords[ 3] = CLLocationCoordinate2DMake(46.447197, 30.743040);
  6. mapCoords[ 4] = CLLocationCoordinate2DMake(46.415384, 30.723226);
  7. mapCoords[ 5] = CLLocationCoordinate2DMake(46.409143, 30.729909);
  8. MKPolyline *polyLine = [MKPolyline polylineWithCoordinates:mapCoords count:6];
  9. [mapView addOverlay:polyLine];
  10. [mapView setDelegate:self];


и в делегате нашей карты зададим свойства этой линии

  1. - (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay
  2. {
  3.  MKPolylineView *polylineView = [[[MKPolylineView alloc] initWithOverlay:overlay] autorelease];
  4.  polylineView.strokeColor = [UIColor blueColor];
  5.  polylineView.lineWidth = 3.0;
  6.  return polylineView;
  7. }


Пример проекта.

Так же смотрите:
- Конвертирование "человеческого" адреса в широту и долготу

21 июля 2010 г.

Работа с музыкальной библиотекой iPod в iOS

Возникла у меня задача, проверить программно, присутствует ли конкретная песня в библиотеке пользователя на iPhone/iPod Touch, и если есть, то проиграть ее. Итак, будем программировать работу с библиотекой iPod.

Для взаимодействия с музыкальной библиотекой в iOS существует класс MPMusicPlayerController из фраемворка MediaPlayer. Функций вроде и не много, но они покрывают практически все возможности приложения iPod.app. При создании экземпляра класса можно выбрать, каким плейером будем пользоваться, созданным отдельно для приложения(метод applicationMusicPlayer), или глобальным плейером iPod(метод iPodMusicPlayer). Первый выгрузится при закрытии приложения, второй может продолжать работать после закрытия приложения. Будем работать с глобальным плейером iPod.

Вызвав метод play мы уже можем услышать музыку доносящуюся из динамиков устройства (iPhone Sumulator не подключается к библиотеке iTunes). Если нужно знать, когда закончилась/сменилась песня нужно зарегистрировать слушателя:

  1. MPMusicPlayerController *ipod = [MPMusicPlayerController iPodMusicPlayer];
  2. [[NSNotificationCenter defaultCenter] addObserver:self
  3.   selector:@selector(playbackItemChanged:)
  4.   name:MPMusicPlayerControllerNowPlayingItemDidChangeNotification
  5.   object:ipod];
  6. [ipod beginGeneratingPlaybackNotifications];

тут мы регистрируем функцию playbackItemChanged: текущего класса как слушателя события изменения песни в iPod.

Хорошо, сейчас мы можем прослушивать песни из пленэра, но не можем выбирать, что слушать. Звуковой файл представляется в библиотеке классом MPMediaItem. Но просто так его создать нельзя, его нужно получать фильтруя из музыкальной библиотеки данные. Для фильтрации используется класс MPMediaQuery. Тут можно отфильтровать библиотеку по разным множеству категориям (таким как жанр, аудиокниги, подкасты, плечисты), а также можно добавлять свои фильтры.

  1. [ipod setQueueWithQuery:[MPMediaQuery songsQuery]];
  2. [ipod play];

И по традиции пример приложения.

16 июля 2010 г.

Проигрывание музыки с помощью iPhone SDK

Присутствие звуковых эффектов и музыки сильно улучшает ваше приложение. Сегодня я расскажу как с помощью iPhone SDK управлять проигрыванием звукового файла. Речь пойдет о проигрывали больших звуковых файлов с возможностью приостановки и изменении позиции проигрывания.
Я буду использовать класс AVAudioPlayer из фраемворка AVFoundation. Напишем приложение которое будет циклически проигрывать файл с возможностью перемотки. Я не использую Interface Builder поэтому не буду приводить скриншоты расположения элементов на экране. Итак, в в нашем вью присутствует кнопка старта/остановки проигрывания (playStopBtn), слайдер показывающий позицию в проигрываемом файле (tmSlider) и свичер разрешающий играть музыку при залоченом экране.
Первое, что необходимо сделать, так это создать наш плейер и настроить его. Например в методе init или loadView прописываем:

  1. NSURL *file = [[NSURL alloc] initFileURLWithPath:
  2.     [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"a_hot.caf"]];
  3. NSError *err = nil;
  4. player = [[AVAudioPlayer alloc] initWithContentsOfURL:file error:&err];
  5. [file release];
  6. player.numberOfLoops = -1;
  7. player.delegate = self;
  8. [player prepareToPlay];

в результате плейер у нас создан, и в него загружен наш звуковой файл.
Описываем событие нажатия на кнопку playStopBtn:

  1. if (player.playing)
  2. {
  3.  [player stop];
  4.  [playStopBtn setTitle:NSLocalizedString(@"Play", @"") forState:UIControlStateNormal];
  5.  [tmUpdaterTimer invalidate];
  6.  [tmUpdaterTimer release];
  7.  tmUpdaterTimer = nil;
  8. }
  9. else
  10. {
  11.  [player play];
  12.  [playStopBtn setTitle:NSLocalizedString(@"Stop", @"") forState:UIControlStateNormal];
  13.  tmUpdaterTimer = [[NSTimer scheduledTimerWithTimeInterval:0.2
  14.  target:self selector:@selector(timerAction:)
  15.  userInfo:nil repeats:YES] retain];
  16. }

тут все просто, если сейчас играет музыка - останавливаем ее проигрывание, исли плейер на паузе - начинаем проигрывание. Так-же тут создается/уничтожается таймер, который изменяет позицию слайдера при проигрывали:

  1. - (void) timerAction:(NSTimer *) sender
  2. {
  3.   [tmSlider setValue:player.currentTime animated:YES];
  4. } 

У слайдера показывающего позицию в проигрываемом файле тоже есть два слушателя на изменение позиции и окончание изменения позиции:

  1. - (void) changeTime:(UISlider *) sender
  2. {
  3.   if (!moving)
  4.   {
  5.     isPlaying = player.playing;
  6.     if (player.playing)
  7.     {
  8.       [player stop];
  9.     }
  10.     moving = YES;
  11.   }
  12.   player.currentTime = sender.value;
  13. }
  14.  
  15. - (void) endChangeTime:(UISlider *) sender
  16. {
  17.   moving = NO;
  18.   if (isPlaying)
  19.   {
  20.     [player play];
  21.   }
  22. }

при начале перетаскивания ползунка проверяем, играет ли музыка, если да, то приостанавливаем проигрывание в конце востанавливаем состояние.

Все проигрывается, но при блокировке экрана музыка приостанавливается. Следующий код разрешает проигрывание музыки при выключенном экране устройства:

  1. AudioSessionInitialize(NULL, kCFRunLoopDefaultMode, NULL, self);
  2. UInt32 sessionCategory = kAudioSessionCategory_MediaPlayback;
  3. AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(sessionCategory), &sessionCategory);
  4. AudioSessionSetActive(true);

Мой пример использующий все выше написанное можно скачать

13 июля 2010 г.

Отсылка почты из вашего приложения встроенным клиентом Mail в iOS

Вот решил написать несколько статей по программированию под iPhone/iPod Touch/iPad.
Для начала выбрал простенькую тему описывающуюся одним классом в SDK и несколькими методами. Итак, отсылка почты из вашего приложения встроенным клиентом Mail.
Работу отсылки почты осуществляет класс MFMailComposeViewController наследник от стандартного UIViewController-а. Первое, что необходимо сделать, это подключить к вашему проекту фраемворк MessageUI.

Потом подключить описание этого фраемворка.

Теперь можно кодить.

  1. // Перед тем как вызывать диалог отсылки почты необходимо проверить настроен ли у пользователя вообще почтовый клиент.
  2. if (![MFMailComposeViewController canSendMail])
  3. {
  4.   // Клиент не настроен, показываем всплывающее окно.
  5.  UIAlertView *tmp = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Warning", @"") message:NSLocalizedString(@"Your e-mail application not configured", @"") delegate:nil cancelButtonTitle:NSLocalizedString(@"OK", @"") otherButtonTitles:nil];
  6.  [tmp show];
  7.  [tmp release];
  8.  return ;
  9. }
  10.  // создаем экземпляр обекта MFMailComposeViewController
  11. MFMailComposeViewController *mcvc = [[MFMailComposeViewController alloc] init];
  12.  // указываем обект делегата (слушателя)
  13. mcvc.mailComposeDelegate = self;
  14.  // заполняем поле "Кому"
  15. [mcvc setToRecipients:[NSArray arrayWithObject:@"test@example.com"]];
  16.  // заполняем поле "Тема"
  17. [mcvc setSubject:NSLocalizedString(@"Testing mail", @"")];
  18.  // пишем тело письма
  19. [mcvc setMessageBody:NSLocalizedString(@"This is body of mail", @"") isHTML:NO];
  20.  // прикрепляем к письму данные как текст и указываем имя файла приложения к письму
  21. [mcvc addAttachmentData:[@"Text on attach" dataUsingEncoding: NSUTF8StringEncoding] mimeType:@"text/plain" fileName:@"test.txt"];
  22.  // показываем заполненный контрол
  23. [self presentModalViewController:mcvc animated:YES];

Нужно помнить, что контрол сам не убирается после нажатия кнопки "Отправить", поэтому в методе слушателе (mailComposeController:didFinishWithResult:error:) закроем наш контрол:

  1. - (void) mailComposeController:(MFMailComposeViewController*) controller didFinishWithResult:(MFMailComposeResult) result error:(NSError*) error
  2. {
  3.  [controller dismissModalViewControllerAnimated:YES];
  4. }

Проект для xCode можно скачать.

5 июля 2010 г.

Обжим сетевого кабеля напрямую и кроссовер (crossover)

Обжим сетевого прямого и кроссовер (crossover) кабеля 10/100Mbit.

1.При соединении Computer-Hub/Switch (карта-хаб/свитч) используется следующая схема:

С одной стороны
1: Бело-оранжевый
2: Оранжевый
3: Бело-зелёный
4: Синий
5: Бело-синий
6: Зелёный
7: Бело-коричневый
8: Коричневый

С другой стороны
1: Бело-оранжевый
2: Оранжевый
3: Бело-зелёный
4: Синий
5: Бело-синий
6: Зелёный
7: Бело-коричневый
8: Коричневый


2. При соединении двух компьютеров crossover (карта-карта) используется следующая схема обжима:

С одной стороны
1: Бело-оранжевый
2: Оранжевый
3: Бело-зелёный
4: Синий
5: Бело-синий
6: Зелёный
7: Бело-коричневый
8: Коричневый

С другой стороны
1: Бело-зеленый
2: Зеленый
3: Бело-оранжевый
4: Синий
5: Бело-синий
6: Оранжевый
7: Бело-коричневый
8: Коричневый