AIMP Forum
AIMP for Windows => Дополнения / Addons => Разработка / Development => Topic started by: DesweR on September 03, 2014, 14:51:56
-
Задача: проигрывать аудио с "облака" (я.диск, гугл.драйв и т.п.)
Нюанс: прямой ссылки на файл нет, воспроизводить как поток не получится.
Идея: своими силами организовать скачивание файла во временное хранилище и воспроизводить его как обычный файл.
Затык: если файл скачивается медленнее, чем идёт воспроизведение - воспроизведение моментально обрывается, т.е. оно не ждёт когда файл подгрузится.
Идея №2: свой декодер, который будет ждать подгрузки и потом отдавать данные.
Собственно по Идеи №2 и вопрос.
Тема декодоров покрыта тенью и мраком, их умеют писать не только лишь все, мало кто может это делать.
Вопрос №1: правильно ли я понял, можно же ничего не декодировать, а просто сказать, что это MP3 (допустим) и отдать данные как есть (предварительно подождав, когда они подкачаются).
Вопрос №2: А может кто-нибудь набросать пример декодера, делающий вышеописанное.
Ибо у меня получается 'TASOSOXRResampler: I/O ratio out-of-range'. Сразу после вызовов DecoderGetInfo и DecoderIsRealTimeStream, я пробовал их и заполнять и не заполнять, без разницы.
{ TAIMPInputPluginHeader }
function TAIMPInputPluginHeader.CreateDecoder(AFileName: PWideChar;
out ADecoder: IAIMPInputPluginDecoder): LongBool;
begin
ADecoder := TAIMPInputPluginDecoder.Create(AFileName);
Result := True;
end;
function TAIMPInputPluginHeader.CreateDecoderEx(AStream: IAIMPInputStream;
out ADecoder: IAIMPInputPluginDecoder): LongBool;
begin
Result := False;
end;
function TAIMPInputPluginHeader.Initialize: LongBool;
begin
Result := True;
end;
function TAIMPInputPluginHeader.Finalize: LongBool;
begin
Result := True;
end;
function TAIMPInputPluginHeader.GetFileInfo(AFileName: PWideChar;
AFileInfo: PAIMPFileInfo): LongBool;
begin
Result := False;
end;
function TAIMPInputPluginHeader.GetPluginAuthor: PWideChar;
begin
Result := 'Test';
end;
function TAIMPInputPluginHeader.GetPluginFlags: DWORD;
begin
Result := AIMP_INPUT_FLAG_FILE
end;
function TAIMPInputPluginHeader.GetPluginInfo: PWideChar;
begin
Result := 'Test';
end;
function TAIMPInputPluginHeader.GetPluginName: PWideChar;
begin
Result := 'Test';
end;
function TAIMPInputPluginHeader.GetSupportsFormats: PWideChar;
begin
Result := 'Test|*.test;|';
end;
{ TAIMPInputPluginDecoder }
constructor TAIMPInputPluginDecoder.Create(const AFileName: string);
begin
FS := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyRead);
end;
function TAIMPInputPluginDecoder.DecoderGetFormatType: PWideChar;
begin
Result := 'MP3';
end;
function TAIMPInputPluginDecoder.DecoderGetInfo(out ASampleRate, AChannels,
ABitDepth: Integer): LongBool;
begin
Result := False;
end;
function TAIMPInputPluginDecoder.DecoderGetPosition: Int64;
begin
Result := FS.Position;
end;
function TAIMPInputPluginDecoder.DecoderGetSize: Int64;
begin
Result := FS.Size;
end;
function TAIMPInputPluginDecoder.DecoderGetTags(
AFileInfo: PAIMPFileInfo): LongBool;
begin
Result := False;
end;
function TAIMPInputPluginDecoder.DecoderIsRealTimeStream: LongBool;
begin
Result := False;
end;
function TAIMPInputPluginDecoder.DecoderIsSeekable: LongBool;
begin
Result := True;
end;
function TAIMPInputPluginDecoder.DecoderRead(Buffer: PByte;
Size: Integer): Integer;
begin
FS.Write(Buffer, Size);
end;
function TAIMPInputPluginDecoder.DecoderSetPosition(
const AValue: Int64): LongBool;
begin
FS.Position := AValue;
end;
destructor TAIMPInputPluginDecoder.Destroy;
begin
FS.Free;
end;
-
прямой ссылки на файл нет, воспроизводить как поток не получится.
Если прямой ссылки нет, то каким образом вы собираетесь скачивать файл? Как правило сервисы отдают временную прямую ссылку на файл, по которому их могут скачать браузеры и т.п.
Затык: если файл скачивается медленнее, чем идёт воспроизведение - воспроизведение моментально обрывается, т.е. оно не ждёт когда файл подгрузится.
А как вы пытались реализовать это?
свой декодер, который будет ждать подгрузки и потом отдавать данные.
Но отдавать данные вам нужно уже в PCM-формате, а не в исходном.
Что касается вашего кода, как минимум у вас ошибки:
1. в методе DecoderRead - вместо чтения, вы пишите.
2. DecoderGetInfo ничего не возвращает, поэтому вы и получаете исключение.
Что касается самой задачи:
1. я бы изучил вопрос на счет прямой ссылки, раз вы можете скачать файл по ссылке, значит вы можете и проиграть его по ссылке. Однако, если ссылка временная (а не постоянная), то вам нужно подменять постоянную непрямую ссылку, на прямую при попытке проиграть файл. Такая возможность в старом API есть - см. IAIMPAddonsPlayerAsyncHook.
2. Базируясь на API для 3.60, можно реализовать поток, который будет ждать скачки данных, а декодирование и все остальное будет происходить на уровне плеера.
3. Подход с написанием собственного декодера.
Я расположил подходы по мере возрастания их сложности. Еще, я бы рекомендовал писать с использованием нового API: во-первых, оно имеет больше возможностей, во-вторых, оно более безопасное (в плане использования потоков, обработки ошибок и т.п) и продуманное, нежели старое, в-третьих, со временем я планирую отказаться от старого API полностью. К слову сказать, Input-плагины на старом API в 3.60 уже не поддерживаются (я не стал переносить эту ветку API, т.к. по сути ни одного плагина не было написано)
-
Если прямой ссылки нет, то каким образом вы собираетесь скачивать файл? Как правило сервисы отдают временную прямую ссылку на файл, по которому их могут скачать браузеры и т.п.
Средствами API самого сервера. Оно в отличии от временной ссылки будет работать всегда, а не временно. Есть парочка вариантов, как "обмануть" аимп и реализовать это.
А как вы пытались реализовать это?
Пробовал два варианта:
- Забил вторую половину файла "нулями" и воспроизвел. На середине воспроизведение моментально обрывается и начинается сначала.
- Удалил вторую половину файла. При воспроизведении продолжительность файла стала в два раза меньше.
Но отдавать данные вам нужно уже в PCM-формате, а не в исходном.
Т.е. нет никакой возможности передать данные следующему "подходящему" декодеру?
1. я бы изучил вопрос на счет прямой ссылки, раз вы можете скачать файл по ссылке, значит вы можете и проиграть его по ссылке.
Это был запасной вариант, если ответ на вышеуказанный вопрос с декодером - отрицательный, буду пробовать его.
Базируясь на API для 3.60
Так оно уже доступно или нет?
-
Средствами API самого сервера. Оно в отличии от временной ссылки будет работать всегда, а не временно.
Поясните, пожалуйста. Вы посылаете какой-то запрос, а сервер отдает файл, так?
Пробовал два варианта:
- Забил вторую половину файла "нулями" и воспроизвел. На середине воспроизведение моментально обрывается и начинается сначала.
- Удалил вторую половину файла. При воспроизведении продолжительность файла стала в два раза меньше.
Нет, такие варианты не подойдут. Правильным в данной ситуации будет морозить вызывающий поток до того момента, пока данные не подоспеют.
Т.е. нет никакой возможности передать данные следующему "подходящему" декодеру?
Только в 3.60 посредством реализации виртуальных файлов.
Так оно уже доступно или нет?
Да, доступно. Лежит на сайте в разделе SDK.
-
Поясните, пожалуйста. Вы посылаете какой-то запрос, а сервер отдает файл, так?
Точно, отсылаю запрос - и ответом идут данные. Только сейчас дошло до меня... никакой временной ссылки я не получаю, по крайней мере по протоколу WebDav.
Нет, такие варианты не подойдут. Правильным в данной ситуации будет морозить вызывающий поток до того момента, пока данные не подоспеют.
А каким подходом это делать? Не начинать воспроизведение, пока не закачается всё? Отслеживать воспроизведение и каким то образом просчитывать, что впереди "обрыв" и приостанавливаться (пока даже не представляю как)?
Да, доступно. Лежит на сайте в разделе SDK.
А где этот раздел?
Нашёл, на главной.
-
А каким подходом это делать? Не начинать воспроизведение, пока не закачается всё? Отслеживать воспроизведение и каким то образом просчитывать, что впереди "обрыв" и приостанавливаться (пока даже не представляю как)?
Можно, не начинать воспроизведение, пока не скачается все, этот вариант более простой, но не очень хороший. Я бы попробовал реализовать просчет обрыва и тормозил читающий поток плеера только в том случае, если данных реально еще нет.
В новом SDK, ознакомьтесь с разделом FileManager, интерфейсом IAIMPVirtualFile и все, что с ним связано.
-
В новом SDK, ознакомьтесь с разделом FileManager, интерфейсом IAIMPVirtualFile и все, что с ним связано.
Возник вопрос, при каких условиях срабатывает IAIMPExtensionFileExpander.Expand (т.к. пока никак не срабатывает)? И как добавляется в плейлист путь к "виртуальному файлу" (т.е. как проще это сделать, в целях отладки)?
-
IAIMPExtensionFileExpander срабатывает при добавлении файлов (за исключением добавления URL-ов), и при обращении к виртуальному файлу, если IAIMPVirtualFile не найден в кэше.
Добавить файлы в плейлист можно через API для работы с плейлистом. Вам будет достаточно сгенерировать виртуальное имя файла самому и добавить его в список.
Точно, отсылаю запрос - и ответом идут данные. Только сейчас дошло до меня... никакой временной ссылки я не получаю, по крайней мере по протоколу WebDav.
Так если ответом идут данные, нельзя ли подменить временный-URL из плейлиста на этот запрос? Ведь в таком случае плеер сам все декодирует и проиграет.
-
Так если ответом идут данные, нельзя ли подменить временный-URL из плейлиста на этот запрос? Ведь в таком случае плеер сам все декодирует и проиграет.
Точно, заголовки запроса же входят в состав url. Можно попробовать.
-
Точно, заголовки запроса же входят в состав url. Можно попробовать.
Яндекс.Диск не хочет работать через токен в параметрах url, только в теле запроса. Причем остальные сервисы яндекса так умеют.
-
Яндекс.Диск не хочет работать через токен в параметрах url, только в теле запроса. Причем остальные сервисы яндекса так умеют.
В таком случае придется работать через виртуальные файлы.
-
Яндекс.Диск не хочет работать через токен в параметрах url, только в теле запроса. Причем остальные сервисы яндекса так умеют.
Правильно ли я понимаю, что токен отправляется через POST запрос? Или он отправляется в поле HTTP-GET заголовка?
-
Правильно ли я понимаю, что токен отправляется через POST запрос? Или он отправляется в поле HTTP-GET заголовка?
Get запрос, в заголовке нужно передать "Authorization: OAuth <токен>".
-
Get запрос, в заголовке нужно передать "Authorization: OAuth <токен>".
Это можно реализовать и сейчас через подмену URL-а при подключении. Указать кастомный параметр можно вот так: [url]\r\nheader1\r\n. \r\n - в дельфях заменяете на #13#10.
-
Указать кастомный параметр можно вот так: [url]\r\nheader1\r\n. \r\n - в дельфях заменяете на #13#10.
А как должен выглядеть окончательный url?
webdav.yandex.ru/123.mp3
Accept: */*
Authorization: OAuth <токен>
Как то так?
'https://webdav.yandex.ru/123.mp3' #13#10 'Accept: */*' #13#10 'Authorization: OAuth <токен>' #13#10
Заголовки нужно кодировать в url-формат?
Accept%3A+*%2F*
Authorization%3A+OAuth+%3C%3F%3F%3F%3F%3F%3E
Или можно передавать в том виде, как есть?
Accept: */*
Authorization: OAuth <токен>
-
Заголовки нужно кодировать в url-формат?
По идее не нужно