198 lines
5.2 KiB
C++
198 lines
5.2 KiB
C++
#include "sockutil.h"
|
|
#include "sys/system.h"
|
|
#include "rtmp-client.h"
|
|
#include "flv-reader.h"
|
|
#include "flv-proto.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include <time.h>
|
|
|
|
//#define CORRUPT_RTMP_CHUNK_DATA
|
|
|
|
#if defined(CORRUPT_RTMP_CHUNK_DATA)
|
|
static void rtmp_corrupt_data(const void* data, size_t bytes)
|
|
{
|
|
static unsigned int seed;
|
|
if (0 == seed)
|
|
{
|
|
seed = (unsigned int)time(NULL);
|
|
srand(seed);
|
|
}
|
|
|
|
if (bytes < 1)
|
|
return;
|
|
|
|
//size_t i = bytes > 20 ? 20 : bytes;
|
|
//i = rand() % i;
|
|
|
|
//uint8_t v = ((uint8_t*)data)[i];
|
|
//((uint8_t*)data)[i] = rand() % 255;
|
|
//printf("rtmp_corrupt_data[%d] %d == %d\n", i, (int)v, (int)((uint8_t*)data)[i]);
|
|
|
|
if (5 == rand() % 10)
|
|
{
|
|
size_t i = rand() % bytes;
|
|
uint8_t v = ((uint8_t*)data)[i];
|
|
((uint8_t*)data)[i] = rand() % 255;
|
|
printf("rtmp_corrupt_data[%d] %d == %d\n", i, (int)v, (int)((uint8_t*)data)[i]);
|
|
}
|
|
}
|
|
|
|
static uint8_t s_buffer[4 * 1024 * 1024];
|
|
static size_t s_offset;
|
|
static FILE* s_fp;
|
|
static void fwritepacket(uint32_t timestamp)
|
|
{
|
|
assert(4 == fwrite(&s_offset, 1, 4, s_fp));
|
|
assert(4 == fwrite(×tamp, 1, 4, s_fp));
|
|
assert(s_offset == fwrite(s_buffer, 1, s_offset, s_fp));
|
|
s_offset = 0;
|
|
}
|
|
#endif
|
|
|
|
static int rtmp_client_send(void* param, const void* header, size_t len, const void* data, size_t bytes)
|
|
{
|
|
socket_t* socket = (socket_t*)param;
|
|
socket_bufvec_t vec[2];
|
|
socket_setbufvec(vec, 0, (void*)header, len);
|
|
socket_setbufvec(vec, 1, (void*)data, bytes);
|
|
|
|
#if defined(CORRUPT_RTMP_CHUNK_DATA)
|
|
//if (len > 0)
|
|
//{
|
|
// assert(s_offset + len < sizeof(s_buffer));
|
|
// memcpy(s_buffer + s_offset, header, len);
|
|
// s_offset += len;
|
|
//}
|
|
//if (bytes > 0)
|
|
//{
|
|
// assert(s_offset + bytes < sizeof(s_buffer));
|
|
// memcpy(s_buffer + s_offset, data, bytes);
|
|
// s_offset += bytes;
|
|
//}
|
|
|
|
rtmp_corrupt_data(header, len);
|
|
rtmp_corrupt_data(data, bytes);
|
|
#endif
|
|
return socket_send_v_all_by_time(*socket, vec, bytes > 0 ? 2 : 1, 0, 5000);
|
|
}
|
|
|
|
static void rtmp_client_push(const char* flv, rtmp_client_t* rtmp)
|
|
{
|
|
int r, type;
|
|
int avcrecord = 0;
|
|
int aacconfig = 0;
|
|
size_t taglen;
|
|
uint32_t timestamp;
|
|
uint32_t s_timestamp = 0;
|
|
uint32_t diff = 0;
|
|
uint32_t clock;
|
|
|
|
static char packet[2 * 1024 * 1024];
|
|
while (1)
|
|
{
|
|
void* f = flv_reader_create(flv);
|
|
|
|
clock = system_clock(); // timestamp start from 0
|
|
while (1 == flv_reader_read(f, &type, ×tamp, &taglen, packet, sizeof(packet)))
|
|
{
|
|
uint32_t t = system_clock();
|
|
if (clock + timestamp > t && clock + timestamp < t + 3 * 1000) // dts skip
|
|
system_sleep(clock + timestamp - t);
|
|
else if (clock + timestamp > t + 3 * 1000)
|
|
clock = t - timestamp;
|
|
|
|
timestamp += diff;
|
|
s_timestamp = timestamp > s_timestamp ? timestamp : s_timestamp;
|
|
|
|
if (FLV_TYPE_AUDIO == type)
|
|
{
|
|
if (0 == packet[1])
|
|
{
|
|
if(0 != aacconfig)
|
|
continue;
|
|
aacconfig = 1;
|
|
}
|
|
r = rtmp_client_push_audio(rtmp, packet, taglen, timestamp);
|
|
}
|
|
else if (FLV_TYPE_VIDEO == type)
|
|
{
|
|
if (0 == packet[1] || 2 == packet[1])
|
|
{
|
|
if (0 != avcrecord)
|
|
continue;
|
|
avcrecord = 1;
|
|
}
|
|
//bool keyframe = 1 == ((packet[0] & 0xF0) >> 4);
|
|
//printf("timestamp: %u, s_timestamp: %u%s\n", timestamp, s_timestamp, keyframe ? " [I]" : "");
|
|
//if (timestamp > 10 * 1000 && keyframe)
|
|
//{
|
|
// uint8_t header[5];
|
|
// header[0] = (1 << 4) /* FrameType */ | 7 /* AVC */;
|
|
// header[1] = 2; // AVC end of sequence
|
|
// header[2] = 0;
|
|
// header[3] = 0;
|
|
// header[4] = 0;
|
|
// r = rtmp_client_push_video(rtmp, header, 5, timestamp);
|
|
// system_sleep(600 * 1000);
|
|
//}
|
|
r = rtmp_client_push_video(rtmp, packet, taglen, timestamp);
|
|
}
|
|
else if (FLV_TYPE_SCRIPT == type)
|
|
{
|
|
r = rtmp_client_push_script(rtmp, packet, taglen, timestamp);
|
|
}
|
|
else
|
|
{
|
|
assert(0);
|
|
r = 0; // ignore
|
|
}
|
|
|
|
if (0 != r)
|
|
{
|
|
assert(0);
|
|
break; // TODO: handle send failed
|
|
}
|
|
}
|
|
|
|
flv_reader_destroy(f);
|
|
|
|
diff = s_timestamp + 30;
|
|
}
|
|
|
|
EXIT:
|
|
return;
|
|
}
|
|
|
|
// rtmp://video-center.alivecdn.com/live/hello?vhost=your.domain
|
|
// rtmp_publish_test("video-center.alivecdn.com", "live", "hello?vhost=your.domain", local-flv-file-name)
|
|
void rtmp_publish_test(const char* host, const char* app, const char* stream, const char* flv)
|
|
{
|
|
static char packet[2 * 1024 * 1024];
|
|
snprintf(packet, sizeof(packet), "rtmp://%s/%s", host, app); // tcurl
|
|
|
|
struct rtmp_client_handler_t handler;
|
|
memset(&handler, 0, sizeof(handler));
|
|
handler.send = rtmp_client_send;
|
|
|
|
socket_init();
|
|
socket_t socket = socket_connect_host(host, 1935, 2000);
|
|
socket_setnonblock(socket, 0);
|
|
|
|
rtmp_client_t* rtmp = rtmp_client_create(app, stream, packet/*tcurl*/, &socket, &handler);
|
|
int r = rtmp_client_start(rtmp, 0);
|
|
|
|
while (4 != rtmp_client_getstate(rtmp) && (r = socket_recv(socket, packet, sizeof(packet), 0)) > 0)
|
|
{
|
|
assert(0 == rtmp_client_input(rtmp, packet, r));
|
|
}
|
|
|
|
rtmp_client_push(flv, rtmp);
|
|
|
|
rtmp_client_destroy(rtmp);
|
|
socket_close(socket);
|
|
socket_cleanup();
|
|
}
|