Това е авторът на FLVMeta тук.
Може да искате да погледнете моя отговор на този въпрос: https://stackoverflow.com/a/13803143/393701
В допълнение към тази информация, мога да обясня някои части от C++ софтуер, който написах (за съжаление не е с отворен код за момента), чиято цел е да предава FLV файлове към RTMP сървър.
Това е тествано и работи добре с Wowza, nginx-rtmp, както и със сървъра за стрийминг на живо Dailymotion.
Инициализиране
std::string rtmp_url = "rtmp://server/app/stream";
// add this if doing live streaming:
rtmp_url.append(" live=1");
RTMP * rtmp = RTMP_Alloc();
if (!rtmp) {
std::cerr << "Unable to create rtmp object\n";
return;
}
RTMP_Init(rtmp);
RTMP_LogSetLevel(RTMP_LOGINFO);
RTMP_LogSetOutput(stderr);
RTMP_SetupURL(rtmp, const_cast<char*>(rtmp_url.c_str())); // librtmp is a mess
RTMP_EnableWrite(rtmp);
Странното const_cast
се дължи на факта, че librtmp е проектиран странно и използва неконстантен char *
като URL, въпреки че може да не е модифициран в действителност от библиотеката.
Освен това буферът ще се използва за докато RTMP обектът живее, така че низът не трябва да се освобождава преди изтриването на RTMP обекта.
Свързване
// connect to server
if (!RTMP_Connect(rtmp, NULL)) {
std::cerr << "Unable to connect to server\n";
RTMP_Free(rtmp);
return;
}
Вторият аргумент на RTMP_Connect
е незадължителен указател към RTMP пакет, който да се изпрати като параметър към връзката вместо пакета за връзка по подразбиране, изпратен от librtmp. Това обикновено не трябва да се използва при свързване към „нормални“ RTMP сървъри.
// connect to stream (this will be the stream specified in the RTMP URL)
if (!RTMP_ConnectStream(rtmp, 0)) {
cerr << "Unable to connect to stream\n";
RTMP_Free(rtmp);
return;
}
Вторият аргумент на RTMP_ConnectStream
е цяло число, представляващо клеймото за време, към което да се търси при възпроизвеждане на видеоклип. Когато публикувате, използването на 0
е безопасен залог.
Публикуване на пакети
Този раздел предполага използването на FLV кода, намерен в проекта FLVMeta (вижте https://github.com/noirotm/flvmeta/blob/master/src/flv.h#L175).
flv_stream * flvin = flv_open(input_file.c_str());
if (!flvin) {
std::cerr << "Unable to open " << input_file << '\n';
return;
}
Първо, добра идея е да се уверите, че сме отворили истински FLV файл.
flv_header header;
int res = flv_read_header(flvin, &header);
if (res == FLV_ERROR_NO_FLV || res == FLV_ERROR_EOF) {
std::cerr << "Input file is not an FLV video\n";
flv_close(flvin);
return;
}
Сега прочетете всеки таг във файла и ги изпратете на RTMP сървъра.
// 10 MB copy buffer should be enough
#define BUFFER_SIZE 10000000
char buffer[BUFFER_SIZE];
flv_tag tag;
while (flv_read_tag(flvin, &tag) != FLV_ERROR_EOF) {
// copy tag header
flv_copy_tag(buffer, &tag, FLVSTREAMER_BUFFER_SIZE);
// copy tag body
size_t data_size = flv_read_tag_body(flvin, buffer + FLV_TAG_SIZE, BUFFER_SIZE - FLV_TAG_SIZE);
// copy previous tag size
uint32 pts;
flv_read_prev_tag_size(flvin, &pts);
flv_copy_prev_tag_size(
buffer + FLV_TAG_SIZE + flv_tag_get_body_length(tag),
pts,
BUFFER_SIZE - (FLV_TAG_SIZE + flv_tag_get_body_length(tag))
);
// write the packet
int size = FLV_TAG_SIZE + data_size + sizeof(uint32);
if (RTMP_Write(rtmp, buffer, size) <= 0) {
std::cerr << "Unable to write to server" << endl;
break;
}
}
Различните flv_*
функции се грижат за справянето със спецификите на FLV формата, например проблеми с endianness и евентуално неправилно формиране на данни, така че бих препоръчал да ги използвате, ако вашата програма е съвместима с GPL лиценза.
Ако не, тя не е сложно да се използва стандартен I/O за итерация през тагове, стига FLV форматът да се разбира добре.
Също така е важно да се отбележи, че заглавката на FLV файла не трябва да се изпраща. librtmp ще открие всеки опит за изпращане на FLV хедъра и ще го предотврати.
Не е задължително да четете входящи RTMP пакети, докато публикувате, но ако искате да боравите правилно с протокола, се препоръчва подходът, използван от @szatmary в неговия код.
Друг важен аспект, който не се разглежда тук, е, че при публикуване на поток на живо е задължително да се приложи някаква форма на дроселиране, за да се изпращат данни с номиналния битрейт на видеото, в противен случай RTMP сървърът ще пропусне пакети и възпроизвеждането на видео ще пострада много.
Често срещан подход за решаване на този проблем може да бъде четене на времевия печат на всеки етикет и заспиване, ако пишем твърде бързо.
Почистване
flv_close(flvin);
RTMP_Free(rtmp);
Функцията RTMP_Free
ще се погрижи за затварянето на потока, връзката и освобождаването на всяка разпределена памет.
person
SirDarius
schedule
10.08.2014