Мы используем Adobe FMS 4.5 для записи видео с веб-камеры пользователей. Мы не транслируем это видео в прямом эфире, мы просто хотим захватить его и сохранить на сервере, чтобы сделать с ним что-нибудь позже (прикрепить к учетной записи пользователя, показать как содержимое на странице и т. Д.).
Мы написали надежное приложение для захвата, которое передает поток на сервер, правильно отменяет публикацию и позволяет пользователю просматривать видео после завершения отмены публикации и т. Д. Это действительно работает, поскольку сохраняет видеофайлы так, как мы ожидали.
Проблема в том, что у этих записанных видео вначале есть небольшой пробел. Это значительно меньше одной секунды. Мы говорим, может быть, от 5/100 до 1/10 секунды без видеоданных. Действительно похоже, что в самом начале ролика нет ключевого кадра видео.
Обычно это не проблема - видео просто немного начинается с воспроизведения и воспроизводится нормально в большинстве плееров. Проблема в том, что мы позволяем пользователям произвольно обрезать видео позже с помощью другого инструмента - настраиваемого визуального интерфейса для FFmpeg. Если они начинают обрезку видео до этого первого ключевого кадра, в конечном результате появляется уродливый серый беспорядок, потому что нет визуальных данных в области, где FFmpeg начал резку.
Моей первой мыслью было: «О, мне нужно прикрепить камеру (иначе | в другой точке | при каком-то обратном вызове | и т. Д.)». Я поигрался с подключением камеры перед вызовом публикации, после NetStream.Publish.Start и т. Д. И т. Д.
Я упустил что-то существенное? Или я просто работаю с неправильной идеей, или я неправильно понимаю процесс?
Конечно, я полностью ожидаю, но не хочу, чтобы ответ был: «Вот как это делает FMS». :) Мы могли бы добавить серверный процесс для удаления первых XX бит видео, но это произвольно; мы не знаем, сколько обрезать, и не хотим рисковать потерять какие-либо пользовательские данные.
Вот обобщение кода, который мы использовали:
private function init():void
{
var my_errors:Array = [];
if ( !Camera.isSupported )
{
my_errors.push( 'camera is not supported' );
}
else
{
camera = Camera.getCamera();
if ( !camera )
{
my_errors.push( 'no camera found' );
}
else if ( camera.muted )
{
Security.showSettings( SecurityPanel.PRIVACY );
}
}
mic = Microphone.getMicrophone();
if ( !mic )
{
my_errors.push( 'no microphone found' );
}
if ( my_errors.length )
{
this.fatal_error( my_errors );
return;
}
camera.setMode( camera_width, camera_height, camera_fps, true );
camera.setQuality( 0, camera_quality );
netconnect = new NetConnection();
netconnect.addEventListener( NetStatusEvent.NET_STATUS, net_status_handler );
netconnect.connect( publish_url );
}
private function net_status_handler( ev:NetStatusEvent ):void
{
switch ( ev.info.code )
{
case 'NetConnection.Connect.Success':
trace( 'CONNECT: Connected to "' + publish_url + '"' );
begin_stream();
break;
}
}
private function begin_stream():void
{
if ( this.recording )
return;
this.recording = true;
guid = GUID.create();
netstream = new NetStream( netconnect );
netstream.addEventListener( NetStatusEvent.NET_STATUS, net_stream_handler );
netstream.attachCamera( camera );
netstream.attachAudio( mic );
netstream.publish( guid, 'record' );
}