У меня есть DLL, которую одно из моих приложений использует для получения видео с камер RTSP. Под капотом DLL использует библиотеки FFMPEG из этого выпуска zip:
ffmpeg-20141022-git-6dc99fd-win64-shared.7z
У нас есть широкий выбор камер в доме, и большинство из них работают отлично. Однако на одном конкретном номере модели Pelco: IXE20DN-OCP я не могу подключиться. Я протестировал камеру и строку подключения по протоколу RTSP на VLC, и она просто отлично подключается к камере.
Я нашел строку подключения здесь: http://www.ispyconnect.com/man.aspx?n=Pelco
rtsp://IPADDRESS:554/1/stream1
Как ни странно, даже если я оставлю порт вне VLC, он подключится, так что я предполагаю, что это порт RTSP по умолчанию или что VLC пробует разные вещи на основе вашего ввода.
В любом случае, когда я пытаюсь подключиться, я получаю ошибку от av_format_open_input. Возвращает код -135. Когда я посмотрел в списке кодов ошибок, я не увидел его в списке. Для удобства я распечатал все ошибки в файле error.h, чтобы посмотреть, каковы их значения.
DumpErrorCodes - Error Code : AVERROR_BSF_NOT_FOUND = -1179861752
DumpErrorCodes - Error Code : AVERROR_BUG = -558323010
DumpErrorCodes - Error Code : AVERROR_BUFFER_TOO_SMALL = -1397118274
DumpErrorCodes - Error Code : AVERROR_DECODER_NOT_FOUND = -1128613112
DumpErrorCodes - Error Code : AVERROR_DEMUXER_NOT_FOUND = -1296385272
DumpErrorCodes - Error Code : AVERROR_ENCODER_NOT_FOUND = -1129203192
DumpErrorCodes - Error Code : AVERROR_EOF = -541478725
DumpErrorCodes - Error Code : AVERROR_EXIT = -1414092869
DumpErrorCodes - Error Code : AVERROR_EXTERNAL = -542398533
DumpErrorCodes - Error Code : AVERROR_FILTER_NOT_FOUND = -1279870712
DumpErrorCodes - Error Code : AVERROR_INVALIDDATA = -1094995529
DumpErrorCodes - Error Code : AVERROR_MUXER_NOT_FOUND = -1481985528
DumpErrorCodes - Error Code : AVERROR_OPTION_NOT_FOUND = -1414549496
DumpErrorCodes - Error Code : AVERROR_PATCHWELCOME = -1163346256
DumpErrorCodes - Error Code : AVERROR_PROTOCOL_NOT_FOUND = -1330794744
DumpErrorCodes - Error Code : AVERROR_STREAM_NOT_FOUND = -1381258232
DumpErrorCodes - Error Code : AVERROR_BUG2 = -541545794
DumpErrorCodes - Error Code : AVERROR_UNKNOWN = -1313558101
DumpErrorCodes - Error Code : AVERROR_EXPERIMENTAL = -733130664
DumpErrorCodes - Error Code : AVERROR_INPUT_CHANGED = -1668179713
DumpErrorCodes - Error Code : AVERROR_OUTPUT_CHANGED = -1668179714
DumpErrorCodes - Error Code : AVERROR_HTTP_BAD_REQUEST = -808465656
DumpErrorCodes - Error Code : AVERROR_HTTP_UNAUTHORIZED = -825242872
DumpErrorCodes - Error Code : AVERROR_HTTP_FORBIDDEN = -858797304
DumpErrorCodes - Error Code : AVERROR_HTTP_NOT_FOUND = -875574520
DumpErrorCodes - Error Code : AVERROR_HTTP_OTHER_4XX = -1482175736
DumpErrorCodes - Error Code : AVERROR_HTTP_SERVER_ERROR = -1482175992
Ничего, даже близко к -135. Я нашел эту ошибку, вроде переполнения стека, здесь ошибка времени выполнения при связывании библиотек ffmpeg в qt creator где автор утверждает, что это ошибка загрузки DLL. Я не уверен, что заставило его так думать, но я последовал совету и использовал ходок зависимости (http://www.dependencywalker.com/), чтобы проверить, какие зависимости он думал, что моя DLL нужна. Он перечислил несколько, но они уже были включены в мой установочный пакет.
Чтобы убедиться, что он их забирает, я вручную удалил их из установки и заметил радикальное изменение в поведении программы (поскольку моя DLL не загружалась и вообще не запускалась).
Итак, у меня есть немного кода инициализации:
void FfmpegInitialize()
{
av_lockmgr_register(&LockManagerCb);
av_register_all();
LOG_DEBUG0("av_register_all returned\n");
}
Тогда у меня есть моя основная процедура открытого соединения …
int RTSPConnect(const char *URL, int width, int height, frameReceived callbackFunction)
{
int errCode =0;
if ((errCode = avformat_network_init()) != 0)
{
LOG_ERROR1("avformat_network_init returned error code %d\n", errCode);
}
LOG_DEBUG0("avformat_network_init returned\n");
//Allocate space and setup the the object to be used for storing all info needed for this connection
fContextReadFrame = avformat_alloc_context(); // free'd in the Close method
if (fContextReadFrame == 0)
{
LOG_ERROR1("Unable to set rtsp_transport options. Error code = %d\n", errCode);
return FFMPEG_OPTION_SET_FAILURE;
}
LOG_DEBUG1("avformat_alloc_context returned %p\n", fContextReadFrame);
AVDictionary *opts = 0;
if ((errCode = av_dict_set(&opts, "rtsp_transport", "tcp", 0)) < 0)
{
LOG_ERROR1("Unable to set rtsp_transport options. Error code = %d\n", errCode);
return FFMPEG_OPTION_SET_FAILURE;
}
LOG_DEBUG1("av_dict_set returned %d\n", errCode);
//open rtsp
DumpErrorCodes();
if ((errCode = avformat_open_input(&fContextReadFrame, URL, NULL, &opts)) < 0)
{
LOG_ERROR2("Unable to open avFormat RF inputs. URL = %s, and Error code = %d\n", URL, errCode);
LOG_ERROR2("Error Code %d = %s\n", errCode, errMsg(errCode));
// NOTE context is free'd on failure.
return FFMPEG_FORMAT_OPEN_FAILURE;
}
...
Чтобы быть уверенным, что я не понял код ошибки, я напечатал сообщение об ошибке из ffmpeg, но ошибка не обнаружена, и вместо этого возвращается мое постоянное сообщение об ошибке.
Следующим моим шагом было подключение wireshark к моей попытке подключения и к попытке подключения к VLC, а также попытка выяснить, какие различия (если таковые имеются) вызывают проблему, и что я могу сделать, чтобы ffmpeg заставил его работать. Как я уже сказал, у меня есть дюжина других камер, которые используют RTSP, и они работают с моей DLL. Некоторые также используют имена пользователей / пароли / и т. Д. (Так что я знаю, что это не проблема).
Кроме того, мои журналы запуска:
FfmpegInitialize - av_register_all returned
Open - Open called. Pointers valid, passing control.
Rtsp::RtspInterface::Open - Rtsp::RtspInterface::Open called
Rtsp::RtspInterface::Open - VideoSourceString(35) = rtsp://192.168.14.60:554/1/stream1
Rtsp::RtspInterface::Open - Base URL = (192.168.14.60:554/1/stream1)
Rtsp::RtspInterface::Open - Attempting to open (rtsp://192.168.14.60:554/1/stream1) for WxH(320x240) video
RTSPSetFormatH264 - RTSPSetFormatH264
RTSPConnect - Called
LockManagerCb - LockManagerCb invoked for op 1
LockManagerCb - LockManagerCb invoked for op 2
RTSPConnect - avformat_network_init returned
RTSPConnect - avformat_alloc_context returned 019E6000
RTSPConnect - av_dict_set returned 0
DumpErrorCodes - Error Code : AVERROR_BSF_NOT_FOUND = -1179861752
...
DumpErrorCodes - Error Code : AVERROR_HTTP_SERVER_ERROR = -1482175992
RTSPConnect - Unable to open avFormat RF inputs. URL = rtsp://192.168.14.60:554/1/stream1, and Error code = -135
RTSPConnect - Error Code -135 = No Error Message Available
Я собираюсь двигаться дальше с wireshark, но хотел бы знать происхождение кода ошибки -135 из ffmpeg. Когда я смотрю на код, если ‘ret’ устанавливается в -135, это должно происходить в результате кода возврата из вспомогательного метода, а не непосредственно в avformat_open_input метод.
https://www.ffmpeg.org/doxygen/2.5/libavformat_2utils_8c_source.html#l00398
После обновления до последней ежедневной сборки ffmpeg я получаю данные о wireshark. Потоковый протокол в реальном времени:
Request: SETUP rtsp://192.168.14.60/stream1/track1 RTSP/1.0\r\n
Method: SETUP
URL: rtsp://192.168.14.60/stream1/track1
Transport: RTP/AVP/TCP;unicast;interleaved=0-1
CSeq: 3\r\n
User-Agent: Lavf56.31.100\r\n
\r\n
Ответ на это — первая «ошибка», которую я могу обнаружить при инициации.
Response: RTSP/1.0 461 Unsupported Transport\r\n
Status: 461
CSeq: 3\r\n
Date: Sun, Jan 04 1970 16:03:05 GMT\r\n
\r\n
Я собираюсь догадаться, что … это означает, что выбранный нами транспорт не был поддержан. Быстрая проверка кода показывает, что я выбрал «tcp». Просматривая ответ на команду DESCRIBE, он выглядит так:
Media Protocol: RTP/AVP
Кроме того, когда ffmpeg выдает SETUP, он указывает:
Transport: RTP/AVP/TCP;unicast;interleaved=0-1
Я собираюсь при неудаче выбрать другой тип транспорта и посмотреть, как это работает. До сих пор не знаю, откуда взялся -135.
Решение оказалось в том, что эта конкретная камера не поддерживает RTSP через транспорт TCP. Хотел UDP.
Я обновил код, чтобы попробовать TCP, и, если это не удалось, использовать альтернативный набор опций для UDP и другой вызов, чтобы попытаться открыть все.
if ((errCode = av_dict_set(&opts, "rtsp_transport", "udp", 0)) < 0)
Работает как шарм. Все еще обеспокоен происхождением кодов ошибок -135 и -22, которые не появляются в файле error.h. Может быть, ошибка ffmpeg, из-за которой допускается случайный код ошибки.