mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
Remove srslibrtmp
This commit is contained in:
parent
8e519cedae
commit
812403a44d
26 changed files with 0 additions and 8335 deletions
3
trunk/research/librtmp/.gitignore
vendored
3
trunk/research/librtmp/.gitignore
vendored
|
@ -1,3 +0,0 @@
|
|||
*.mp4
|
||||
*.gcno
|
||||
*.gcda
|
|
@ -1,125 +0,0 @@
|
|||
GCC = gcc
|
||||
|
||||
ifeq ($(HANDSHAKE),)
|
||||
ST_ALL = help
|
||||
else
|
||||
ST_ALL = objs/srs_flv_parser \
|
||||
objs/srs_flv_injecter objs/srs_publish objs/srs_play \
|
||||
objs/srs_ingest_flv objs/srs_ingest_rtmp objs/srs_detect_rtmp \
|
||||
objs/srs_bandwidth_check objs/srs_h264_raw_publish \
|
||||
objs/srs_audio_raw_publish objs/srs_aac_raw_publish \
|
||||
objs/srs_rtmp_dump objs/srs_ingest_mp4
|
||||
endif
|
||||
|
||||
.PHONY: default clean help ssl nossl
|
||||
|
||||
default: $(ST_ALL)
|
||||
|
||||
help:
|
||||
@echo "Usage: make <help>|<clean>|<ssl>|<nossl>"
|
||||
@echo " help display this help"
|
||||
@echo " clean cleanup build"
|
||||
@echo " ssl all tools link ssl"
|
||||
@echo " nossl all tools never link ssl"
|
||||
@echo "ssl/nossl will build the following tools:"
|
||||
@echo " srs_flv_parser parse flv file, print detail info."
|
||||
@echo " srs_flv_injecter inject keyframes information to metadata."
|
||||
@echo " srs_publish publish program using srs-librtmp"
|
||||
@echo " srs_h264_raw_publish publish raw h.264 stream to SSR by srs-librtmp"
|
||||
@echo " srs_audio_raw_publish publish raw audio stream to SSR by srs-librtmp"
|
||||
@echo " srs_aac_raw_publish publish raw aac stream to SSR by srs-librtmp"
|
||||
@echo " srs_play play program using srs-librtmp"
|
||||
@echo " srs_ingest_flv ingest flv file and publish to RTMP server."
|
||||
@echo " srs_ingest_mp4 ingest mp4 file and publish to RTMP server."
|
||||
@echo " srs_ingest_rtmp ingest RTMP and publish to RTMP server."
|
||||
@echo " srs_detect_rtmp detect RTMP stream info."
|
||||
@echo " srs_bandwidth_check bandwidth check/test tool."
|
||||
@echo " srs_rtmp_dump dump rtmp stream to flv file."
|
||||
@echo "Remark: about simple/complex handshake, see: http://blog.csdn.net/win_lin/article/details/13006803"
|
||||
@echo "Remark: srs Makefile will auto invoke this by --with/without-ssl, "
|
||||
@echo " that is, if user specified ssl(by --with-ssl), srs will make this by 'make ssl'"
|
||||
@echo " that is, if user not use ssl(by --without-ssl), use 'make nossl'"
|
||||
@echo " see: https://github.com/ossrs/srs/wiki/v1_CN_v1_Build"
|
||||
@echo "Remark: before make this sample, user must make the srs, with/without ssl"
|
||||
|
||||
clean:
|
||||
@rm -rf objs
|
||||
|
||||
# srs library root
|
||||
SRS_OBJS = ../../objs
|
||||
# srs-librtmp for publish/play, built by srs.
|
||||
SRS_LIBRTMP_I = $(SRS_OBJS)/include/srs_librtmp.h
|
||||
SRS_LIBRTMP_L = $(SRS_OBJS)/lib/srs_librtmp.a
|
||||
# openssl for complex handshake, built by srs.
|
||||
SRS_LIBSSL_L =
|
||||
# public depends, the Makefile or public headers.
|
||||
SRS_RESEARCH_DEPS = Makefile
|
||||
|
||||
# for x86/x64 platform
|
||||
ifeq ($(GCC), gcc)
|
||||
OTHER_FLAGS += -g -O0 -ldl -lstdc++ -lm
|
||||
endif
|
||||
# for arm.
|
||||
ifeq ($(GCC), arm-linux-gnueabi-gcc)
|
||||
OTHER_FLAGS += -g -O0 -ldl -static -lstdc++ -lm
|
||||
endif
|
||||
# for mips, add -lgcc_eh, or stl compile failed.
|
||||
ifeq ($(GCC), mipsel-openwrt-linux-gcc)
|
||||
OTHER_FLAGS += -g -O0 -ldl -lstdc++ -lm -lgcc_eh
|
||||
endif
|
||||
# for ssl or nossl
|
||||
ifeq ($(HANDSHAKE), SSL)
|
||||
SRS_LIBSSL_L = $(SRS_OBJS)/openssl/lib/libssl.a $(SRS_OBJS)/openssl/lib/libcrypto.a
|
||||
ifneq ($(shell test -f $(SRS_OBJS)/openssl/lib/libssl.a && echo yes), yes)
|
||||
SRS_LIBSSL_L = -lssl -lcrypto
|
||||
endif
|
||||
endif
|
||||
|
||||
ssl:
|
||||
@mkdir -p objs
|
||||
$(MAKE) HANDSHAKE="SSL"
|
||||
nossl:
|
||||
@mkdir -p objs
|
||||
$(MAKE) HANDSHAKE="NOSSL"
|
||||
|
||||
CXXFLAGS += $(OTHER_FLAGS) $(EXTRA_CXXFLAGS)
|
||||
|
||||
objs/srs_flv_parser: srs_flv_parser.c $(SRS_RESEARCH_DEPS) $(SRS_LIBRTMP_I) $(SRS_LIBRTMP_L)
|
||||
$(GCC) srs_flv_parser.c $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(CXXFLAGS) -o objs/srs_flv_parser
|
||||
|
||||
objs/srs_flv_injecter: srs_flv_injecter.c $(SRS_RESEARCH_DEPS) $(SRS_LIBRTMP_I) $(SRS_LIBRTMP_L)
|
||||
$(GCC) srs_flv_injecter.c $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(CXXFLAGS) -o objs/srs_flv_injecter
|
||||
|
||||
objs/srs_publish: srs_publish.c $(SRS_RESEARCH_DEPS) $(SRS_LIBRTMP_I) $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L)
|
||||
$(GCC) srs_publish.c $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(CXXFLAGS) -o objs/srs_publish
|
||||
|
||||
objs/srs_h264_raw_publish: srs_h264_raw_publish.c $(SRS_RESEARCH_DEPS) $(SRS_LIBRTMP_I) $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L)
|
||||
$(GCC) srs_h264_raw_publish.c $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(CXXFLAGS) -o objs/srs_h264_raw_publish
|
||||
|
||||
objs/srs_audio_raw_publish: srs_audio_raw_publish.c $(SRS_RESEARCH_DEPS) $(SRS_LIBRTMP_I) $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L)
|
||||
$(GCC) srs_audio_raw_publish.c $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(CXXFLAGS) -o objs/srs_audio_raw_publish
|
||||
|
||||
objs/srs_aac_raw_publish: srs_aac_raw_publish.c $(SRS_RESEARCH_DEPS) $(SRS_LIBRTMP_I) $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L)
|
||||
$(GCC) srs_aac_raw_publish.c $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(CXXFLAGS) -o objs/srs_aac_raw_publish
|
||||
|
||||
objs/srs_play: srs_play.c $(SRS_RESEARCH_DEPS) $(SRS_LIBRTMP_I) $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L)
|
||||
$(GCC) srs_play.c $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(CXXFLAGS) -o objs/srs_play
|
||||
|
||||
objs/srs_ingest_flv: srs_ingest_flv.c $(SRS_RESEARCH_DEPS) $(SRS_LIBRTMP_I) $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L)
|
||||
$(GCC) srs_ingest_flv.c $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(CXXFLAGS) -o objs/srs_ingest_flv
|
||||
|
||||
objs/srs_ingest_mp4: srs_ingest_mp4.c $(SRS_RESEARCH_DEPS) $(SRS_LIBRTMP_I) $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L)
|
||||
$(GCC) srs_ingest_mp4.c $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(CXXFLAGS) -o objs/srs_ingest_mp4
|
||||
|
||||
objs/srs_ingest_rtmp: srs_ingest_rtmp.c $(SRS_RESEARCH_DEPS) $(SRS_LIBRTMP_I) $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L)
|
||||
$(GCC) srs_ingest_rtmp.c $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(CXXFLAGS) -o objs/srs_ingest_rtmp
|
||||
|
||||
objs/srs_detect_rtmp: srs_detect_rtmp.c $(SRS_RESEARCH_DEPS) $(SRS_LIBRTMP_I) $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L)
|
||||
$(GCC) srs_detect_rtmp.c $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(CXXFLAGS) -o objs/srs_detect_rtmp
|
||||
|
||||
objs/srs_bandwidth_check: srs_bandwidth_check.c $(SRS_RESEARCH_DEPS) $(SRS_LIBRTMP_I) $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L)
|
||||
$(GCC) srs_bandwidth_check.c $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(CXXFLAGS) -o objs/srs_bandwidth_check
|
||||
|
||||
objs/srs_rtmp_dump: srs_rtmp_dump.c $(SRS_RESEARCH_DEPS) $(SRS_LIBRTMP_I) $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L)
|
||||
$(GCC) srs_rtmp_dump.c $(SRS_LIBRTMP_L) $(SRS_LIBSSL_L) $(CXXFLAGS) -o objs/srs_rtmp_dump
|
||||
|
|
@ -1,187 +0,0 @@
|
|||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2018 Winlin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// for open audio raw file.
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "../../objs/include/srs_librtmp.h"
|
||||
|
||||
// https://github.com/ossrs/srs/issues/212#issuecomment-64145910
|
||||
int read_audio_frame(char* data, int size, char** pp, char** frame, int* frame_size)
|
||||
{
|
||||
char* p = *pp;
|
||||
|
||||
// @remark, for this demo, to publish aac raw file to SRS,
|
||||
// we search the adts frame from the buffer which cached the aac data.
|
||||
// please get aac adts raw data from device, it always a encoded frame.
|
||||
if (!srs_aac_is_adts(p, size - (p - data))) {
|
||||
srs_human_trace("aac adts raw data invalid.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// @see srs_audio_write_raw_frame
|
||||
// each frame prefixed aac adts header, '1111 1111 1111'B, that is 0xFFF.,
|
||||
// for instance, frame = FF F1 5C 80 13 A0 FC 00 D0 33 83 E8 5B
|
||||
*frame = p;
|
||||
// skip some data.
|
||||
// @remark, user donot need to do this.
|
||||
p += srs_aac_adts_frame_size(p, size - (p - data));
|
||||
|
||||
*pp = p;
|
||||
*frame_size = p - *frame;
|
||||
if (*frame_size <= 0) {
|
||||
srs_human_trace("aac adts raw data invalid.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
printf("publish raw audio as rtmp stream to server like FMLE/FFMPEG/Encoder\n");
|
||||
printf("SRS(ossrs) client librtmp library.\n");
|
||||
printf("version: %d.%d.%d\n", srs_version_major(), srs_version_minor(), srs_version_revision());
|
||||
|
||||
if (argc <= 2) {
|
||||
printf("Usage: %s <audio_raw_file> <rtmp_publish_url>\n", argv[0]);
|
||||
printf(" audio_raw_file: the audio raw steam file.\n");
|
||||
printf(" rtmp_publish_url: the rtmp publish url.\n");
|
||||
printf("For example:\n");
|
||||
printf(" %s ./audio.raw.aac rtmp://127.0.0.1:1935/live/livestream\n", argv[0]);
|
||||
printf("Where the file: http://winlinvip.github.io/srs.release/3rdparty/audio.raw.aac\n");
|
||||
printf("See: https://github.com/ossrs/srs/issues/212\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
const char* raw_file = argv[1];
|
||||
const char* rtmp_url = argv[2];
|
||||
srs_human_trace("raw_file=%s, rtmp_url=%s", raw_file, rtmp_url);
|
||||
|
||||
// open file
|
||||
int raw_fd = open(raw_file, O_RDONLY);
|
||||
if (raw_fd < 0) {
|
||||
srs_human_trace("open audio raw file %s failed.", raw_file);
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
|
||||
off_t file_size = lseek(raw_fd, 0, SEEK_END);
|
||||
if (file_size <= 0) {
|
||||
srs_human_trace("audio raw file %s empty.", raw_file);
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
srs_human_trace("read entirely audio raw file, size=%dKB", (int)(file_size / 1024));
|
||||
|
||||
char* audio_raw = (char*)malloc(file_size);
|
||||
if (!audio_raw) {
|
||||
srs_human_trace("alloc raw buffer failed for file %s.", raw_file);
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
|
||||
lseek(raw_fd, 0, SEEK_SET);
|
||||
ssize_t nb_read = 0;
|
||||
if ((nb_read = read(raw_fd, audio_raw, file_size)) != file_size) {
|
||||
srs_human_trace("buffer %s failed, expect=%dKB, actual=%dKB.",
|
||||
raw_file, (int)(file_size / 1024), (int)(nb_read / 1024));
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
|
||||
// connect rtmp context
|
||||
srs_rtmp_t rtmp = srs_rtmp_create(rtmp_url);
|
||||
|
||||
if (srs_rtmp_handshake(rtmp) != 0) {
|
||||
srs_human_trace("simple handshake failed.");
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
srs_human_trace("simple handshake success");
|
||||
|
||||
if (srs_rtmp_connect_app(rtmp) != 0) {
|
||||
srs_human_trace("connect vhost/app failed.");
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
srs_human_trace("connect vhost/app success");
|
||||
|
||||
if (srs_rtmp_publish_stream(rtmp) != 0) {
|
||||
srs_human_trace("publish stream failed.");
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
srs_human_trace("publish stream success");
|
||||
|
||||
uint32_t timestamp = 0;
|
||||
uint32_t time_delta = 45;
|
||||
// @remark, to decode the file.
|
||||
char* p = audio_raw;
|
||||
for (;p < audio_raw + file_size;) {
|
||||
// @remark, read a frame from file buffer.
|
||||
char* data = NULL;
|
||||
int size = 0;
|
||||
if (read_audio_frame(audio_raw, file_size, &p, &data, &size) < 0) {
|
||||
srs_human_trace("read a frame from file buffer failed.");
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
|
||||
// 0 = Linear PCM, platform endian
|
||||
// 1 = ADPCM
|
||||
// 2 = MP3
|
||||
// 7 = G.711 A-law logarithmic PCM
|
||||
// 8 = G.711 mu-law logarithmic PCM
|
||||
// 10 = AAC
|
||||
// 11 = Speex
|
||||
char sound_format = 10;
|
||||
// 2 = 22 kHz
|
||||
char sound_rate = 2;
|
||||
// 1 = 16-bit samples
|
||||
char sound_size = 1;
|
||||
// 1 = Stereo sound
|
||||
char sound_type = 1;
|
||||
|
||||
timestamp += time_delta;
|
||||
|
||||
int ret = 0;
|
||||
if ((ret = srs_audio_write_raw_frame(rtmp, sound_format, sound_rate, sound_size, sound_type, data, size, timestamp)) != 0) {
|
||||
srs_human_trace("send audio raw data failed. ret=%d", ret);
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
|
||||
srs_human_trace("sent packet: type=%s, time=%d, size=%d, codec=%d, rate=%d, sample=%d, channel=%d",
|
||||
srs_human_flv_tag_type2string(SRS_RTMP_TYPE_AUDIO), timestamp, size, sound_format, sound_rate, sound_size,
|
||||
sound_type);
|
||||
|
||||
// @remark, when use encode device, it not need to sleep.
|
||||
usleep(1000 * time_delta);
|
||||
}
|
||||
|
||||
rtmp_destroy:
|
||||
srs_rtmp_destroy(rtmp);
|
||||
close(raw_fd);
|
||||
free(audio_raw);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,183 +0,0 @@
|
|||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2018 Winlin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// for open audio raw file.
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "../../objs/include/srs_librtmp.h"
|
||||
|
||||
// https://github.com/ossrs/srs/issues/212#issuecomment-63648892
|
||||
// allspace:
|
||||
// Take this file as an example: https://github.com/allspace/files/blob/master/srs.pcm
|
||||
// It's captured using SDK callback method. I have filtered out h264 video, so it's audio only now.
|
||||
// For every frame, it's a 8 bytes vendor specific header, following 160 bytes audio frame.
|
||||
// The header part can be ignored.
|
||||
int read_audio_frame(char* audio_raw, int file_size, char** pp, char** pdata, int* psize)
|
||||
{
|
||||
char* p = *pp;
|
||||
|
||||
if (file_size - (p - audio_raw) < 168) {
|
||||
srs_human_trace("audio must be 160+8 bytes. left %d bytes.", (int)(file_size - (p - audio_raw)));
|
||||
return - 1;
|
||||
}
|
||||
|
||||
// ignore 8bytes vendor specific header.
|
||||
p += 8;
|
||||
|
||||
// 160 bytes audio frame
|
||||
*pdata = p;
|
||||
*psize = 160;
|
||||
|
||||
// next frame.
|
||||
*pp = p + *psize;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
printf("publish raw audio as rtmp stream to server like FMLE/FFMPEG/Encoder\n");
|
||||
printf("SRS(ossrs) client librtmp library.\n");
|
||||
printf("version: %d.%d.%d\n", srs_version_major(), srs_version_minor(), srs_version_revision());
|
||||
|
||||
if (argc <= 2) {
|
||||
printf("Usage: %s <audio_raw_file> <rtmp_publish_url>\n", argv[0]);
|
||||
printf(" audio_raw_file: the audio raw steam file.\n");
|
||||
printf(" rtmp_publish_url: the rtmp publish url.\n");
|
||||
printf("For example:\n");
|
||||
printf(" %s ./audio.raw.pcm rtmp://127.0.0.1:1935/live/livestream\n", argv[0]);
|
||||
printf("Where the file: http://winlinvip.github.io/srs.release/3rdparty/audio.raw.pcm\n");
|
||||
printf("See: https://github.com/ossrs/srs/issues/212\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
const char* raw_file = argv[1];
|
||||
const char* rtmp_url = argv[2];
|
||||
srs_human_trace("raw_file=%s, rtmp_url=%s", raw_file, rtmp_url);
|
||||
|
||||
// open file
|
||||
int raw_fd = open(raw_file, O_RDONLY);
|
||||
if (raw_fd < 0) {
|
||||
srs_human_trace("open audio raw file %s failed.", raw_file);
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
|
||||
off_t file_size = lseek(raw_fd, 0, SEEK_END);
|
||||
if (file_size <= 0) {
|
||||
srs_human_trace("audio raw file %s empty.", raw_file);
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
srs_human_trace("read entirely audio raw file, size=%dKB", (int)(file_size / 1024));
|
||||
|
||||
char* audio_raw = (char*)malloc(file_size);
|
||||
if (!audio_raw) {
|
||||
srs_human_trace("alloc raw buffer failed for file %s.", raw_file);
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
|
||||
lseek(raw_fd, 0, SEEK_SET);
|
||||
ssize_t nb_read = 0;
|
||||
if ((nb_read = read(raw_fd, audio_raw, file_size)) != file_size) {
|
||||
srs_human_trace("buffer %s failed, expect=%dKB, actual=%dKB.",
|
||||
raw_file, (int)(file_size / 1024), (int)(nb_read / 1024));
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
|
||||
// connect rtmp context
|
||||
srs_rtmp_t rtmp = srs_rtmp_create(rtmp_url);
|
||||
|
||||
if (srs_rtmp_handshake(rtmp) != 0) {
|
||||
srs_human_trace("simple handshake failed.");
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
srs_human_trace("simple handshake success");
|
||||
|
||||
if (srs_rtmp_connect_app(rtmp) != 0) {
|
||||
srs_human_trace("connect vhost/app failed.");
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
srs_human_trace("connect vhost/app success");
|
||||
|
||||
if (srs_rtmp_publish_stream(rtmp) != 0) {
|
||||
srs_human_trace("publish stream failed.");
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
srs_human_trace("publish stream success");
|
||||
|
||||
uint32_t timestamp = 0;
|
||||
uint32_t time_delta = 17;
|
||||
// @remark, to decode the file.
|
||||
char* p = audio_raw;
|
||||
for (;p < audio_raw + file_size;) {
|
||||
// @remark, read a frame from file buffer.
|
||||
char* data = NULL;
|
||||
int size = 0;
|
||||
if (read_audio_frame(audio_raw, file_size, &p, &data, &size) < 0) {
|
||||
srs_human_trace("read a frame from file buffer failed.");
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
|
||||
// 0 = Linear PCM, platform endian
|
||||
// 1 = ADPCM
|
||||
// 2 = MP3
|
||||
// 7 = G.711 A-law logarithmic PCM
|
||||
// 8 = G.711 mu-law logarithmic PCM
|
||||
// 10 = AAC
|
||||
// 11 = Speex
|
||||
char sound_format = 1;
|
||||
// 3 = 44 kHz
|
||||
char sound_rate = 3;
|
||||
// 1 = 16-bit samples
|
||||
char sound_size = 1;
|
||||
// 1 = Stereo sound
|
||||
char sound_type = 1;
|
||||
|
||||
timestamp += time_delta;
|
||||
|
||||
if (srs_audio_write_raw_frame(rtmp, sound_format, sound_rate, sound_size, sound_type, data, size, timestamp) != 0) {
|
||||
srs_human_trace("send audio raw data failed.");
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
|
||||
srs_human_trace("sent packet: type=%s, time=%d, size=%d, codec=%d, rate=%d, sample=%d, channel=%d",
|
||||
srs_human_flv_tag_type2string(SRS_RTMP_TYPE_AUDIO), timestamp, size, sound_format, sound_rate, sound_size,
|
||||
sound_type);
|
||||
|
||||
// @remark, when use encode device, it not need to sleep.
|
||||
usleep(1000 * time_delta);
|
||||
}
|
||||
|
||||
rtmp_destroy:
|
||||
srs_rtmp_destroy(rtmp);
|
||||
close(raw_fd);
|
||||
free(audio_raw);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,143 +0,0 @@
|
|||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2018 Winlin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../../objs/include/srs_librtmp.h"
|
||||
|
||||
// srs debug info.
|
||||
char* ip = NULL;
|
||||
char* sig = NULL;
|
||||
int pid = 0, cid = 0;
|
||||
int major = 0, minor = 0, revision= 0, build = 0;
|
||||
// bandwidth test data.
|
||||
int64_t start_time = 0;
|
||||
int64_t end_time = 0;
|
||||
int play_kbps = 0;
|
||||
int publish_kbps = 0;
|
||||
int play_bytes = 0;
|
||||
int publish_bytes = 0;
|
||||
int play_duration = 0;
|
||||
int publish_duration = 0;
|
||||
|
||||
int do_check(srs_rtmp_t rtmp)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if ((ret = srs_rtmp_handshake(rtmp)) != 0) {
|
||||
srs_human_trace("simple handshake failed.");
|
||||
return ret;
|
||||
}
|
||||
srs_human_trace("simple handshake success");
|
||||
|
||||
if ((ret = srs_rtmp_connect_app(rtmp)) != 0) {
|
||||
srs_human_trace("connect vhost/app failed.");
|
||||
return ret;
|
||||
}
|
||||
srs_human_trace("connect vhost/app success");
|
||||
|
||||
if ((ret = srs_rtmp_get_server_sig(rtmp, &sig)) != 0) {
|
||||
srs_human_trace("Retrieve server ID failed, ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
if ((ret = srs_rtmp_get_server_id(rtmp, &ip, &pid, &cid)) != 0) {
|
||||
srs_human_trace("Retrieve server ID failed, ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
if ((ret = srs_rtmp_get_server_version(rtmp, &major, &minor, &revision, &build)) != 0) {
|
||||
srs_human_trace("Retrieve server version failed, ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((ret = srs_rtmp_bandwidth_check(rtmp,
|
||||
&start_time, &end_time, &play_kbps, &publish_kbps, &play_bytes, &publish_bytes,
|
||||
&play_duration, &publish_duration)) != 0
|
||||
) {
|
||||
srs_human_trace("bandwidth check/test failed.");
|
||||
return ret;
|
||||
}
|
||||
srs_human_trace("bandwidth check/test success");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int ret = 0;
|
||||
srs_rtmp_t rtmp;
|
||||
|
||||
printf("RTMP bandwidth check/test with server.\n");
|
||||
printf("srs(ossrs) client librtmp library.\n");
|
||||
printf("version: %d.%d.%d\n", srs_version_major(), srs_version_minor(), srs_version_revision());
|
||||
|
||||
if (argc <= 1) {
|
||||
printf("RTMP bandwidth check/test with server.\n"
|
||||
"Usage: %s <rtmp_url>\n"
|
||||
" rtmp_url RTMP bandwidth url to check. format: rtmp://server:port/app?key=xxx,vhost=xxx\n"
|
||||
"For example:\n"
|
||||
" %s rtmp://127.0.0.1:1935/app?key=35c9b402c12a7246868752e2878f7e0e,vhost=bandcheck.srs.com\n"
|
||||
" %s rtmp://127.0.0.1:1935/app?key=35c9b402c12a7246868752e2878f7e0e,vhost=bandcheck.srs.com>/dev/null\n"
|
||||
"@remark, output text to stdout, while json to stderr.\n",
|
||||
argv[0], argv[0], argv[0]);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
char url[512];
|
||||
snprintf(url, sizeof(url), "%s/%s", argv[1], "livestream");
|
||||
rtmp = srs_rtmp_create((const char*)url);
|
||||
srs_human_trace("bandwidth check/test url: %s", argv[1]);
|
||||
|
||||
if ((ret = do_check(rtmp)) != 0) {
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
|
||||
srs_human_trace("\n%s, %s, %d.%d.%d.%d, srs_pid=%d, srs_id=%d\n"
|
||||
"duration: %dms(%d+%d), play: %dkbps, publish: %dkbps",
|
||||
(char*)sig, (char*)ip, major, minor, revision, build, pid, cid,
|
||||
(int)(end_time - start_time), play_duration, publish_duration, play_kbps, publish_kbps);
|
||||
|
||||
rtmp_destroy:
|
||||
fprintf(stderr, "{\"code\":%d,"
|
||||
"\"srs_server\":\"%s\", \"srs_primary\":\"\", \"srs_authors\":\"\", \"srs_server_ip\":\"%s\", "
|
||||
"\"srs_version\":\"%d.%d.%d.%d\", "
|
||||
"\"srs_pid\":%d, "
|
||||
"\"srs_id\":%d, "
|
||||
"\"duration\":%d, "
|
||||
"\"play_duration\":%d, "
|
||||
"\"publish_duration\":%d,"
|
||||
"\"play_kbps\":%d, "
|
||||
"\"publish_kbps\":%d"
|
||||
"}",
|
||||
ret,
|
||||
(char*)sig, (char*)ip,
|
||||
major, minor, revision, build, pid, cid,
|
||||
(int)(end_time - start_time), play_duration, publish_duration,
|
||||
play_kbps, publish_kbps);
|
||||
srs_rtmp_destroy(rtmp);
|
||||
|
||||
srs_human_trace(" ");
|
||||
srs_human_trace("completed");
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -1,239 +0,0 @@
|
|||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2018 Winlin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../../objs/include/srs_librtmp.h"
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int ret = 0;
|
||||
srs_rtmp_t rtmp;
|
||||
|
||||
// time
|
||||
int64_t time_startup = srs_utils_time_ms();
|
||||
int64_t time_dns_resolve = 0;
|
||||
int64_t time_socket_connect = 0;
|
||||
int64_t time_play_stream = 0;
|
||||
int64_t time_first_packet = 0;
|
||||
int64_t time_cleanup = 0;
|
||||
// bytes
|
||||
int64_t bytes_nsend = 0;
|
||||
int time_duration = 0;
|
||||
int64_t bytes_nrecv = 0;
|
||||
|
||||
// packet data
|
||||
int size;
|
||||
char type;
|
||||
char* data;
|
||||
uint32_t timestamp;
|
||||
uint32_t basetime = 0;
|
||||
|
||||
// user options
|
||||
const char* rtmp_url = NULL;
|
||||
int duration = 0;
|
||||
int timeout = 0;
|
||||
enum srs_url_schema sus;
|
||||
|
||||
printf("detect rtmp stream\n");
|
||||
printf("srs(ossrs) client librtmp library.\n");
|
||||
printf("version: %d.%d.%d\n", srs_version_major(), srs_version_minor(), srs_version_revision());
|
||||
|
||||
if (argc <= 3) {
|
||||
printf("detect stream on RTMP server, print result to stderr.\n"
|
||||
"Usage: %s <rtmp_url> <duration> <timeout> [url_schema]\n"
|
||||
" rtmp_url RTMP stream url to play\n"
|
||||
" duration how long to play, in seconds, stream time.\n"
|
||||
" timeout how long to timeout, in seconds, system time.\n"
|
||||
" url_schema the schema of url, default to vis, can be:\n"
|
||||
" normal: rtmp://vhost:port/app/stream\n"
|
||||
" via : rtmp://ip:port/vhost/app/stream\n"
|
||||
" vis : rtmp://ip:port/app/stream?vhost=xxx\n"
|
||||
" vis2 : rtmp://ip:port/app/stream?domain=xxx\n"
|
||||
"For example:\n"
|
||||
" %s rtmp://127.0.0.1:1935/bravo.chnvideo.com/live/livestream 3 10\n",
|
||||
argv[0], argv[0]);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
rtmp_url = argv[1];
|
||||
duration = atoi(argv[2]);
|
||||
timeout = atoi(argv[3]);
|
||||
|
||||
if (1) {
|
||||
char *p = "vis";
|
||||
if (argc > 4) {
|
||||
p = argv[4];
|
||||
}
|
||||
|
||||
if (strcmp(p, "normal") == 0) {
|
||||
sus = srs_url_schema_normal;
|
||||
} else if (strcmp(p, "via") == 0) {
|
||||
sus = srs_url_schema_via;
|
||||
} else if (strcmp(p, "vis") == 0) {
|
||||
sus = srs_url_schema_vis;
|
||||
} else if (strcmp(p, "vis2") == 0){
|
||||
sus = srs_url_schema_vis2;
|
||||
} else {
|
||||
srs_human_trace("url_schema must be normal/via/vis/vis2");
|
||||
exit(-2);
|
||||
}
|
||||
srs_human_trace("url schema: %s", p);
|
||||
}
|
||||
|
||||
srs_human_trace("rtmp url: %s", rtmp_url);
|
||||
srs_human_trace("duration: %ds, timeout:%ds", duration, timeout);
|
||||
|
||||
if (duration <= 0 || timeout <= 0) {
|
||||
srs_human_trace("duration and timeout must be positive.");
|
||||
exit(-3);
|
||||
}
|
||||
|
||||
if ((rtmp = srs_rtmp_create(rtmp_url)) == NULL) {
|
||||
srs_human_trace("create rtmp failed");
|
||||
ret = -1;
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
if ((ret = srs_rtmp_set_timeout(rtmp, timeout * 1000, timeout * 1000)) != 0) {
|
||||
srs_human_trace("set timeout for rtmp failed. errno=%d", ret);
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
|
||||
if ((ret = srs_rtmp_dns_resolve(rtmp)) != 0) {
|
||||
srs_human_trace("dns resolve failed. ret=%d", ret);
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
srs_human_trace("dns resolve success");
|
||||
time_dns_resolve = srs_utils_time_ms();
|
||||
|
||||
if ((ret = srs_rtmp_connect_server(rtmp)) != 0) {
|
||||
srs_human_trace("socket connect failed. ret=%d", ret);
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
srs_human_trace("socket connect success");
|
||||
time_socket_connect = srs_utils_time_ms();
|
||||
|
||||
if ((ret = srs_rtmp_do_simple_handshake(rtmp)) != 0) {
|
||||
srs_human_trace("do simple handshake failed. ret=%d", ret);
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
srs_human_trace("do simple handshake success");
|
||||
|
||||
if ((ret = srs_rtmp_set_schema(rtmp, sus)) != 0) {
|
||||
srs_human_trace("set url schema=%d failed, ret=%d", sus, ret);
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
|
||||
if ((ret = srs_rtmp_connect_app(rtmp)) != 0) {
|
||||
srs_human_trace("connect vhost/app failed. ret=%d", ret);
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
srs_human_trace("connect vhost/app success");
|
||||
|
||||
if ((ret = srs_rtmp_play_stream(rtmp)) != 0) {
|
||||
srs_human_trace("play stream failed. ret=%d", ret);
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
srs_human_trace("play stream success");
|
||||
time_play_stream = srs_utils_time_ms();
|
||||
|
||||
for (;;) {
|
||||
if ((ret = srs_rtmp_read_packet(rtmp, &type, ×tamp, &data, &size)) != 0) {
|
||||
srs_human_trace("read packet failed. ret=%d", ret);
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
srs_human_trace("got packet: type=%s, time=%d, size=%d", srs_human_flv_tag_type2string(type), timestamp, size);
|
||||
|
||||
if (SRS_RTMP_TYPE_VIDEO == type || SRS_RTMP_TYPE_AUDIO == type) {
|
||||
if (time_first_packet <= 0) {
|
||||
time_first_packet = srs_utils_time_ms();
|
||||
}
|
||||
if (basetime <= 0) {
|
||||
basetime = timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
free(data);
|
||||
|
||||
if (srs_utils_time_ms() - time_startup > timeout * 1000) {
|
||||
srs_human_trace("timeout, terminate.");
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
|
||||
if (timestamp > basetime && (timestamp - basetime) > duration * 1000) {
|
||||
srs_human_trace("duration exceed, terminate.");
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
}
|
||||
|
||||
rtmp_destroy:
|
||||
bytes_nsend = srs_utils_send_bytes(rtmp);
|
||||
bytes_nrecv = srs_utils_recv_bytes(rtmp);
|
||||
|
||||
srs_rtmp_destroy(rtmp);
|
||||
time_cleanup = srs_utils_time_ms();
|
||||
time_duration = (int)(time_cleanup - time_startup);
|
||||
|
||||
// print result to stderr.
|
||||
fprintf(stderr, "{"
|
||||
"\"%s\":%d, " //#0
|
||||
"\"%s\":%d, " //#1
|
||||
"\"%s\":%d, " // #2
|
||||
"\"%s\":%d, " // #3
|
||||
"\"%s\":%d, " // #4
|
||||
"\"%s\":%d, " // #5
|
||||
"\"%s\":%d, " // #6
|
||||
"\"%s\":%d, " // #7
|
||||
"\"%s\":%d, " // #8
|
||||
"\"%s\":%d, " // #9
|
||||
"\"%s\":%d, " // #10
|
||||
"%s,%s,%s,%s}",
|
||||
"code", ret, //#0
|
||||
// total = dns + tcp_connect + start_play + first_packet + last_packet
|
||||
"total", time_duration, //#1
|
||||
"dns", (int)(time_dns_resolve - time_startup), //#2
|
||||
"tcp_connect", (int)(time_socket_connect - time_dns_resolve), //#3
|
||||
"start_play", (int)(time_play_stream - time_socket_connect), //#4
|
||||
"first_packet", (int)(time_first_packet - time_play_stream), //#5
|
||||
"last_packet", (int)(time_cleanup - time_first_packet), //#6
|
||||
"stream", (int)(timestamp - basetime), //#7
|
||||
// expect = time_cleanup - time_first_packet
|
||||
// actual = stream
|
||||
// delay = actual - expect
|
||||
"delay", (int)(timestamp - basetime - (time_cleanup - time_first_packet)), //#8
|
||||
"publish_kbps", (int)((time_duration <= 0)? 0:(bytes_nsend * 8 / time_duration)), //#9
|
||||
"play_kbps", (int)((time_duration <= 0)? 0:(bytes_nrecv * 8 / time_duration)), //#10
|
||||
// unit in ms.
|
||||
"\"unit\": \"ms\"",
|
||||
"\"remark0\": \"total = dns + tcp_connect + start_play + first_packet + last_packet\"",
|
||||
"\"remark1\": \"delay = stream - (time_cleanup - time_first_packet)\"",
|
||||
"\"remark2\": \"if code is not 0, user must ignore all data\""
|
||||
);
|
||||
|
||||
srs_human_trace(" ");
|
||||
srs_human_trace("completed");
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -1,383 +0,0 @@
|
|||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2018 Winlin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "../../objs/include/srs_librtmp.h"
|
||||
|
||||
#define ERROR_INJECTED 10000
|
||||
|
||||
int process(const char* in_flv_file, const char* out_flv_file, srs_flv_t* pic, srs_flv_t* poc);
|
||||
int build_keyframes(srs_flv_t ic, srs_amf0_t *pname, srs_amf0_t* pdata, srs_amf0_t* pfilepositions, int64_t* pmetadata_end_offset);
|
||||
int do_inject_flv(srs_flv_t ic, srs_flv_t oc, srs_amf0_t amf0_name, srs_amf0_t amf0_data, srs_amf0_t filepositions, int64_t metadata_end_offset);
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
// user options.
|
||||
char* in_flv_file;
|
||||
char* out_flv_file;
|
||||
// flv handler
|
||||
srs_flv_t ic = NULL;
|
||||
srs_flv_t oc = NULL;
|
||||
|
||||
// temp variables.
|
||||
int tmp_file_size = 0;
|
||||
char* tmp_file;
|
||||
|
||||
printf("inject flv file keyframes to metadata.\n");
|
||||
printf("srs(ossrs) client librtmp library.\n");
|
||||
printf("version: %d.%d.%d\n", srs_version_major(), srs_version_minor(), srs_version_revision());
|
||||
|
||||
if (argc <= 2) {
|
||||
printf("inject flv file keyframes to metadata\n"
|
||||
"Usage: %s in_flv_file out_flv_file\n"
|
||||
" in_flv_file input flv file to inject.\n"
|
||||
" out_flv_file the inject output file, can be in_flv_file.\n"
|
||||
"For example:\n"
|
||||
" %s doc/source.200kbps.768x320.flv injected.flv\n"
|
||||
" %s ../../doc/source.200kbps.768x320.flv injected.flv\n",
|
||||
argv[0], argv[0], argv[0]);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
in_flv_file = argv[1];
|
||||
out_flv_file = argv[2];
|
||||
|
||||
tmp_file_size = strlen(out_flv_file) + strlen(".tmp") + 1;
|
||||
tmp_file = (char*)malloc(tmp_file_size);
|
||||
snprintf(tmp_file, tmp_file_size, "%s.tmp", out_flv_file);
|
||||
|
||||
srs_human_trace("input: %s", in_flv_file);
|
||||
srs_human_trace("output: %s", out_flv_file);
|
||||
srs_human_trace("tmp_file: %s", tmp_file);
|
||||
|
||||
ret = process(in_flv_file, tmp_file, &ic, &oc);
|
||||
|
||||
srs_flv_close(ic);
|
||||
srs_flv_close(oc);
|
||||
|
||||
if (ret != 0) {
|
||||
unlink(tmp_file);
|
||||
if (ret == ERROR_INJECTED) {
|
||||
ret = 0;
|
||||
srs_human_trace("file already injected.");
|
||||
} else {
|
||||
srs_human_trace("error, remove tmp file.");
|
||||
}
|
||||
} else {
|
||||
rename(tmp_file, out_flv_file);
|
||||
srs_human_trace("completed, rename to %s", out_flv_file);
|
||||
}
|
||||
|
||||
free(tmp_file);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int process(const char* in_flv_file, const char* out_flv_file, srs_flv_t* pic, srs_flv_t* poc)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
srs_flv_t ic;
|
||||
srs_flv_t oc;
|
||||
|
||||
// to adjust metadata.
|
||||
// the ic metadata end offset, the next tag start offset.
|
||||
// all oc metadata must adjust according to:
|
||||
// adjust = new_metadata_end_offset - metadata_end_offset
|
||||
int64_t metadata_end_offset = 0;
|
||||
|
||||
// metadata
|
||||
srs_amf0_t amf0_name = NULL;
|
||||
srs_amf0_t amf0_data = NULL;
|
||||
srs_amf0_t filepositions = NULL;
|
||||
|
||||
if ((ic = srs_flv_open_read(in_flv_file)) == NULL) {
|
||||
ret = 2;
|
||||
srs_human_trace("open input flv file failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
*pic = ic;
|
||||
|
||||
if ((oc = srs_flv_open_write(out_flv_file)) == NULL) {
|
||||
ret = 2;
|
||||
srs_human_trace("open output flv file failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
*poc = oc;
|
||||
|
||||
/**
|
||||
* we use two roundtrip to avoid the paddings of metadata,
|
||||
* to support large keyframes videos without padding fields.
|
||||
*/
|
||||
// build keyframes offset to metadata.
|
||||
if ((ret = build_keyframes(ic, &amf0_name, &amf0_data, &filepositions, &metadata_end_offset)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// inject the metadata to oc.
|
||||
if ((ret = do_inject_flv(ic, oc, amf0_name, amf0_data, filepositions, metadata_end_offset)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// TODO: FIXME: mem leak when error.
|
||||
srs_amf0_free(amf0_name);
|
||||
srs_amf0_free(amf0_data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int parse_metadata(char* data, int size, srs_amf0_t* pname, srs_amf0_t* pdata)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
int nparsed = 0;
|
||||
*pname = srs_amf0_parse(data, size, &nparsed);
|
||||
|
||||
if (*pname == NULL || nparsed >= size) {
|
||||
srs_human_trace("invalid amf0 name data.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
*pdata = srs_amf0_parse(data + nparsed, size - nparsed, &nparsed);
|
||||
if (*pdata == NULL || nparsed > size) {
|
||||
srs_human_trace("invalid amf0 value data");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int build_keyframes(srs_flv_t ic, srs_amf0_t *pname, srs_amf0_t* pdata, srs_amf0_t* pfilepositions, int64_t* pmetadata_end_offset)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
// flv header
|
||||
char header[13];
|
||||
|
||||
// packet data
|
||||
char type;
|
||||
uint32_t timestamp = 0;
|
||||
char* data = NULL;
|
||||
int32_t size;
|
||||
int64_t offset = 0;
|
||||
|
||||
// metadata
|
||||
srs_amf0_t amf0_name = NULL;
|
||||
srs_amf0_t amf0_data = NULL;
|
||||
|
||||
srs_amf0_t keyframes = NULL;
|
||||
srs_amf0_t filepositions = NULL;
|
||||
srs_amf0_t times = NULL;
|
||||
|
||||
// reset to generate metadata
|
||||
srs_flv_lseek(ic, 0);
|
||||
|
||||
if ((ret = srs_flv_read_header(ic, header)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
srs_human_trace("build keyframe infos from flv");
|
||||
for (;;) {
|
||||
offset = srs_flv_tellg(ic);
|
||||
|
||||
// tag header
|
||||
if ((ret = srs_flv_read_tag_header(ic, &type, &size, ×tamp)) != 0) {
|
||||
if (srs_flv_is_eof(ret)) {
|
||||
srs_human_trace("parse completed.");
|
||||
return 0;
|
||||
}
|
||||
srs_human_trace("flv get packet failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (size <= 0) {
|
||||
srs_human_trace("invalid size=%d", size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// TODO: FIXME: mem leak when error.
|
||||
data = (char*)malloc(size);
|
||||
if ((ret = srs_flv_read_tag_data(ic, data, size)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// data tag
|
||||
if (type == SRS_RTMP_TYPE_VIDEO) {
|
||||
if (!srs_flv_is_sequence_header(data, size) && srs_flv_is_keyframe(data, size)) {
|
||||
srs_amf0_strict_array_append(filepositions, srs_amf0_create_number(offset));
|
||||
srs_amf0_strict_array_append(times, srs_amf0_create_number(((double)timestamp)/ 1000));
|
||||
}
|
||||
} else if (type == SRS_RTMP_TYPE_SCRIPT) {
|
||||
*pmetadata_end_offset = srs_flv_tellg(ic);
|
||||
if ((ret = parse_metadata(data, size, &amf0_name, &amf0_data)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
*pname = amf0_name;
|
||||
*pdata = amf0_data;
|
||||
|
||||
if (srs_amf0_is_object(amf0_data)) {
|
||||
keyframes = srs_amf0_object_property(amf0_data, "keyframes");
|
||||
if (keyframes == NULL) {
|
||||
keyframes = srs_amf0_create_object();
|
||||
srs_amf0_object_property_set(amf0_data, "keyframes", keyframes);
|
||||
}
|
||||
// always clear the old keyframes.
|
||||
srs_amf0_object_clear(keyframes);
|
||||
|
||||
*pfilepositions = filepositions = srs_amf0_create_strict_array();
|
||||
srs_amf0_object_property_set(keyframes, "filepositions", filepositions);
|
||||
|
||||
times = srs_amf0_create_strict_array();
|
||||
srs_amf0_object_property_set(keyframes, "times", times);
|
||||
} else if (srs_amf0_is_ecma_array(amf0_data)) {
|
||||
keyframes = srs_amf0_ecma_array_property(amf0_data, "keyframes");
|
||||
if (keyframes == NULL) {
|
||||
keyframes = srs_amf0_create_object();
|
||||
srs_amf0_ecma_array_property_set(amf0_data, "keyframes", keyframes);
|
||||
}
|
||||
// always clear the old keyframes.
|
||||
srs_amf0_object_clear(keyframes);
|
||||
|
||||
*pfilepositions = filepositions = srs_amf0_create_strict_array();
|
||||
srs_amf0_object_property_set(keyframes, "filepositions", filepositions);
|
||||
|
||||
times = srs_amf0_create_strict_array();
|
||||
srs_amf0_object_property_set(keyframes, "times", times);
|
||||
}
|
||||
}
|
||||
|
||||
free(data);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int do_inject_flv(srs_flv_t ic, srs_flv_t oc, srs_amf0_t amf0_name, srs_amf0_t amf0_data, srs_amf0_t filepositions, int64_t metadata_end_offset)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
// flv header
|
||||
char header[13];
|
||||
// packet data
|
||||
char type;
|
||||
uint32_t timestamp = 0;
|
||||
char* data = NULL;
|
||||
int32_t size;
|
||||
|
||||
// metadata
|
||||
srs_amf0_t fileposition = NULL;
|
||||
int amf0_name_size = 0;
|
||||
int i;
|
||||
|
||||
// the metadata end offset, the next tag start offset.
|
||||
int64_t new_metadata_end_offset = 0;
|
||||
int offset_adjust = 0;
|
||||
|
||||
// reset to write injected file
|
||||
srs_flv_lseek(ic, 0);
|
||||
|
||||
if ((ret = srs_flv_read_header(ic, header)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((ret = srs_flv_write_header(oc, header)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// write metadata
|
||||
if (amf0_name != NULL && amf0_data != NULL) {
|
||||
amf0_name_size = srs_amf0_size(amf0_name);
|
||||
size = amf0_name_size + srs_amf0_size(amf0_data);
|
||||
|
||||
// adjust all offset of keyframes.
|
||||
new_metadata_end_offset = srs_flv_tellg(oc) + srs_flv_size_tag(size);
|
||||
// the adjust is new offset sub the old offset of metadata end.
|
||||
offset_adjust = new_metadata_end_offset - metadata_end_offset;
|
||||
for (i = 0; i < srs_amf0_strict_array_property_count(filepositions); i++) {
|
||||
fileposition = srs_amf0_strict_array_property_at(filepositions, i);
|
||||
srs_amf0_set_number(fileposition, srs_amf0_to_number(fileposition) + offset_adjust);
|
||||
}
|
||||
|
||||
data = (char*)malloc(size);
|
||||
memset(data, 0, size);
|
||||
if ((ret = srs_amf0_serialize(amf0_name, data, amf0_name_size)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
if ((ret = srs_amf0_serialize(amf0_data, data + amf0_name_size, size - amf0_name_size)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
if ((ret = srs_flv_write_tag(oc, SRS_RTMP_TYPE_SCRIPT, 0, data, size)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
free(data);
|
||||
}
|
||||
|
||||
srs_human_trace("build keyframe infos from flv");
|
||||
for (;;) {
|
||||
// tag header
|
||||
if ((ret = srs_flv_read_tag_header(ic, &type, &size, ×tamp)) != 0) {
|
||||
if (srs_flv_is_eof(ret)) {
|
||||
srs_human_trace("parse completed.");
|
||||
return 0;
|
||||
}
|
||||
srs_human_trace("flv get packet failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (size <= 0) {
|
||||
srs_human_trace("invalid size=%d", size);
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: FIXME: mem leak when error.
|
||||
data = (char*)malloc(size);
|
||||
if ((ret = srs_flv_read_tag_data(ic, data, size)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// data tag
|
||||
if (type == SRS_RTMP_TYPE_SCRIPT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// copy
|
||||
if ((ret = srs_flv_write_tag(oc, type, timestamp, data, size)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
free(data);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -1,188 +0,0 @@
|
|||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2018 Winlin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "../../objs/include/srs_librtmp.h"
|
||||
|
||||
int parse_flv(srs_flv_t flv);
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
// user options.
|
||||
char* in_flv_file;
|
||||
// flv handler
|
||||
srs_flv_t flv;
|
||||
|
||||
printf("parse and show flv file detail.\n");
|
||||
printf("srs(ossrs) client librtmp library.\n");
|
||||
printf("version: %d.%d.%d\n", srs_version_major(), srs_version_minor(), srs_version_revision());
|
||||
|
||||
if (argc <= 1) {
|
||||
printf("parse and show flv file detail\n"
|
||||
"Usage: %s in_flv_file\n"
|
||||
" in_flv_file flv file to parse and show.\n"
|
||||
"For example:\n"
|
||||
" %s doc/source.200kbps.768x320.flv\n"
|
||||
" %s ../../doc/source.200kbps.768x320.flv\n",
|
||||
argv[0], argv[0], argv[0]);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
in_flv_file = argv[1];
|
||||
srs_human_trace("input: %s", in_flv_file);
|
||||
|
||||
if ((flv = srs_flv_open_read(in_flv_file)) == NULL) {
|
||||
ret = 2;
|
||||
srs_human_trace("open flv file failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = parse_flv(flv);
|
||||
srs_flv_close(flv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void digit_to_char(char* src, int ssize, char* dst, int dsize)
|
||||
{
|
||||
int i, j;
|
||||
char v;
|
||||
|
||||
for (i = 0, j = 0; i < ssize && j < dsize; i++) {
|
||||
if (j >= dsize) {
|
||||
break;
|
||||
}
|
||||
v = (src[i] >> 4) & 0x0F;
|
||||
if (v < 10) {
|
||||
dst[j++] = '0' + v;
|
||||
} else {
|
||||
dst[j++] = 'A' + (v - 10);
|
||||
}
|
||||
|
||||
if (j >= dsize) {
|
||||
break;
|
||||
}
|
||||
v = src[i] & 0x0F;
|
||||
if (v < 10) {
|
||||
dst[j++] = '0' + v;
|
||||
} else {
|
||||
dst[j++] = 'A' + (v - 10);
|
||||
}
|
||||
|
||||
if (j >= dsize) {
|
||||
break;
|
||||
}
|
||||
if (i < ssize - 1) {
|
||||
dst[j++] = ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int parse_bytes(char* data, int size, char* hbuf, int hsize, char* tbuf, int tsize, int print_size)
|
||||
{
|
||||
memset(hbuf, 0, hsize);
|
||||
memset(tbuf, 0, tsize);
|
||||
|
||||
if (size > 0) {
|
||||
digit_to_char(data, size, hbuf, hsize - 1);
|
||||
}
|
||||
|
||||
if (size > print_size * 2) {
|
||||
digit_to_char(data + size - print_size, size, tbuf, tsize - 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parse_flv(srs_flv_t flv)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
// flv header
|
||||
char header[13];
|
||||
// packet data
|
||||
char type;
|
||||
uint32_t timestamp = 0;
|
||||
char* data = NULL;
|
||||
int32_t size;
|
||||
int64_t offset = 0;
|
||||
|
||||
if ((ret = srs_flv_read_header(flv, header)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
srs_human_trace("start parse flv");
|
||||
char buffer[1024];
|
||||
for (;;) {
|
||||
offset = srs_flv_tellg(flv);
|
||||
|
||||
// tag header
|
||||
if ((ret = srs_flv_read_tag_header(flv, &type, &size, ×tamp)) != 0) {
|
||||
if (srs_flv_is_eof(ret)) {
|
||||
srs_human_trace("parse completed.");
|
||||
return 0;
|
||||
}
|
||||
srs_human_trace("flv get packet failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (size <= 0) {
|
||||
srs_human_trace("invalid size=%d", size);
|
||||
break;
|
||||
}
|
||||
|
||||
data = (char*)malloc(size);
|
||||
|
||||
if ((ret = srs_flv_read_tag_data(flv, data, size)) == 0) {
|
||||
if ((ret = srs_human_format_rtmp_packet(buffer, sizeof(buffer), type, timestamp, data, size)) == 0) {
|
||||
srs_human_trace("%s", buffer);
|
||||
|
||||
char hbuf[48]; char tbuf[48];
|
||||
parse_bytes(data, size, hbuf, sizeof(hbuf), tbuf, sizeof(tbuf), 16);
|
||||
srs_human_raw("offset=%d, first and last 16 bytes:\n[+00, +15] %s\n[-15, EOF] %s\n", (int)offset, hbuf, tbuf);
|
||||
} else {
|
||||
srs_human_trace("print packet failed. ret=%d", ret);
|
||||
}
|
||||
} else {
|
||||
srs_human_trace("read flv failed. ret=%d", ret);
|
||||
}
|
||||
|
||||
free(data);
|
||||
|
||||
if (ret != 0) {
|
||||
srs_human_trace("parse failed, ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -1,203 +0,0 @@
|
|||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2018 Winlin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// for open h264 raw file.
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "../../objs/include/srs_librtmp.h"
|
||||
|
||||
int read_h264_frame(char* data, int size, char** pp, int* pnb_start_code, int fps,
|
||||
char** frame, int* frame_size, int* dts, int* pts)
|
||||
{
|
||||
char* p = *pp;
|
||||
|
||||
// @remark, for this demo, to publish h264 raw file to SRS,
|
||||
// we search the h264 frame from the buffer which cached the h264 data.
|
||||
// please get h264 raw data from device, it always a encoded frame.
|
||||
if (!srs_h264_startswith_annexb(p, size - (p - data), pnb_start_code)) {
|
||||
srs_human_trace("h264 raw data invalid.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// @see srs_write_h264_raw_frames
|
||||
// each frame prefixed h.264 annexb header, by N[00] 00 00 01, where N>=0,
|
||||
// for instance, frame = header(00 00 00 01) + payload(67 42 80 29 95 A0 14 01 6E 40)
|
||||
*frame = p;
|
||||
p += *pnb_start_code;
|
||||
|
||||
for (;p < data + size; p++) {
|
||||
if (srs_h264_startswith_annexb(p, size - (p - data), NULL)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*pp = p;
|
||||
*frame_size = p - *frame;
|
||||
if (*frame_size <= 0) {
|
||||
srs_human_trace("h264 raw data invalid.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// @remark, please get the dts and pts from device,
|
||||
// we assume there is no B frame, and the fps can guess the fps and dts,
|
||||
// while the dts and pts must read from encode lib or device.
|
||||
*dts += 1000 / fps;
|
||||
*pts = *dts;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
printf("publish raw h.264 as rtmp stream to server like FMLE/FFMPEG/Encoder\n");
|
||||
printf("SRS(ossrs) client librtmp library.\n");
|
||||
printf("version: %d.%d.%d\n", srs_version_major(), srs_version_minor(), srs_version_revision());
|
||||
|
||||
if (argc <= 3) {
|
||||
printf("Usage: %s <h264_raw_file> <rtmp_publish_url> <fps>\n", argv[0]);
|
||||
printf(" h264_raw_file: the h264 raw steam file.\n");
|
||||
printf(" rtmp_publish_url: the rtmp publish url.\n");
|
||||
printf(" fps: the video average fps, for example, 25.\n");
|
||||
printf("For example:\n");
|
||||
printf(" %s ./720p.h264.raw rtmp://127.0.0.1:1935/live/livestream 25\n", argv[0]);
|
||||
printf("Where the file: http://winlinvip.github.io/srs.release/3rdparty/720p.h264.raw\n");
|
||||
printf(" See: https://github.com/ossrs/srs/issues/66\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
const char* raw_file = argv[1];
|
||||
const char* rtmp_url = argv[2];
|
||||
// @remark, the dts and pts if read from device, for instance, the encode lib,
|
||||
// so we assume the fps is 25, and each h264 frame is 1000ms/25fps=40ms/f.
|
||||
double fps = atof(argv[3]);
|
||||
srs_human_trace("raw_file=%s, rtmp_url=%s, fps=%.2f", raw_file, rtmp_url, fps);
|
||||
|
||||
// open file
|
||||
int raw_fd = open(raw_file, O_RDONLY);
|
||||
if (raw_fd < 0) {
|
||||
srs_human_trace("open h264 raw file %s failed.", raw_file);
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
|
||||
off_t file_size = lseek(raw_fd, 0, SEEK_END);
|
||||
if (file_size <= 0) {
|
||||
srs_human_trace("h264 raw file %s empty.", raw_file);
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
srs_human_trace("read entirely h264 raw file, size=%dKB", (int)(file_size / 1024));
|
||||
|
||||
char* h264_raw = (char*)malloc(file_size);
|
||||
if (!h264_raw) {
|
||||
srs_human_trace("alloc raw buffer failed for file %s.", raw_file);
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
|
||||
lseek(raw_fd, 0, SEEK_SET);
|
||||
ssize_t nb_read = 0;
|
||||
if ((nb_read = read(raw_fd, h264_raw, file_size)) != file_size) {
|
||||
srs_human_trace("buffer %s failed, expect=%dKB, actual=%dKB.",
|
||||
raw_file, (int)(file_size / 1024), (int)(nb_read / 1024));
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
|
||||
// connect rtmp context
|
||||
srs_rtmp_t rtmp = srs_rtmp_create(rtmp_url);
|
||||
|
||||
if (srs_rtmp_handshake(rtmp) != 0) {
|
||||
srs_human_trace("simple handshake failed.");
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
srs_human_trace("simple handshake success");
|
||||
|
||||
if (srs_rtmp_connect_app(rtmp) != 0) {
|
||||
srs_human_trace("connect vhost/app failed.");
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
srs_human_trace("connect vhost/app success");
|
||||
|
||||
if (srs_rtmp_publish_stream(rtmp) != 0) {
|
||||
srs_human_trace("publish stream failed.");
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
srs_human_trace("publish stream success");
|
||||
|
||||
int dts = 0;
|
||||
int pts = 0;
|
||||
// @remark, to decode the file.
|
||||
char* p = h264_raw;
|
||||
int count = 0;
|
||||
for (; p < h264_raw + file_size;) {
|
||||
// @remark, read a frame from file buffer.
|
||||
char* data = NULL;
|
||||
int size = 0;
|
||||
int nb_start_code = 0;
|
||||
if (read_h264_frame(h264_raw, (int)file_size, &p, &nb_start_code, fps, &data, &size, &dts, &pts) < 0) {
|
||||
srs_human_trace("read a frame from file buffer failed.");
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
|
||||
// send out the h264 packet over RTMP
|
||||
int ret = srs_h264_write_raw_frames(rtmp, data, size, dts, pts);
|
||||
if (ret != 0) {
|
||||
if (srs_h264_is_dvbsp_error(ret)) {
|
||||
srs_human_trace("ignore drop video error, code=%d", ret);
|
||||
} else if (srs_h264_is_duplicated_sps_error(ret)) {
|
||||
srs_human_trace("ignore duplicated sps, code=%d", ret);
|
||||
} else if (srs_h264_is_duplicated_pps_error(ret)) {
|
||||
srs_human_trace("ignore duplicated pps, code=%d", ret);
|
||||
} else {
|
||||
srs_human_trace("send h264 raw data failed. ret=%d", ret);
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
}
|
||||
|
||||
// 5bits, 7.3.1 NAL unit syntax,
|
||||
// ISO_IEC_14496-10-AVC-2003.pdf, page 44.
|
||||
// 7: SPS, 8: PPS, 5: I Frame, 1: P Frame, 9: AUD, 6: SEI
|
||||
uint8_t nut = (char)data[nb_start_code] & 0x1f;
|
||||
srs_human_trace("sent packet: type=%s, time=%d, size=%d, fps=%.2f, b[%d]=%#x(%s)",
|
||||
srs_human_flv_tag_type2string(SRS_RTMP_TYPE_VIDEO), dts, size, fps, nb_start_code, (char)data[nb_start_code],
|
||||
(nut == 7? "SPS":(nut == 8? "PPS":(nut == 5? "I":(nut == 1? "P":(nut == 9? "AUD":(nut == 6? "SEI":"Unknown")))))));
|
||||
|
||||
// @remark, when use encode device, it not need to sleep.
|
||||
if (count++ == 9) {
|
||||
usleep(1000 * 1000 * count / fps);
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
srs_human_trace("h264 raw data completed");
|
||||
|
||||
rtmp_destroy:
|
||||
srs_rtmp_destroy(rtmp);
|
||||
close(raw_fd);
|
||||
free(h264_raw);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,273 +0,0 @@
|
|||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2018 Winlin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "../../objs/include/srs_librtmp.h"
|
||||
|
||||
int proxy(srs_flv_t flv, srs_rtmp_t ortmp);
|
||||
int connect_oc(srs_rtmp_t ortmp);
|
||||
|
||||
#define RE_PULSE_MS 300
|
||||
#define RE_PULSE_JITTER_MS 3000
|
||||
int64_t re_create();
|
||||
void re_update(int64_t re, int32_t starttime, uint32_t time);
|
||||
void re_cleanup(int64_t re, int32_t starttime, uint32_t time);
|
||||
|
||||
int64_t tools_main_entrance_startup_time;
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
// main function
|
||||
tools_main_entrance_startup_time = srs_utils_time_ms();
|
||||
|
||||
// user option parse index.
|
||||
int opt = 0;
|
||||
// user options.
|
||||
char* in_flv_file = NULL;
|
||||
char* out_rtmp_url = NULL;
|
||||
// rtmp handler
|
||||
srs_rtmp_t ortmp;
|
||||
// flv handler
|
||||
srs_flv_t flv;
|
||||
|
||||
printf("ingest flv file and publish to RTMP server like FFMPEG.\n");
|
||||
printf("srs(ossrs) client librtmp library.\n");
|
||||
printf("version: %d.%d.%d\n", srs_version_major(), srs_version_minor(), srs_version_revision());
|
||||
|
||||
if (argc <= 2) {
|
||||
printf("ingest flv file and publish to RTMP server\n"
|
||||
"Usage: %s <-i in_flv_file> <-y out_rtmp_url>\n"
|
||||
" in_flv_file input flv file, ingest from this file.\n"
|
||||
" out_rtmp_url output rtmp url, publish to this url.\n"
|
||||
"For example:\n"
|
||||
" %s -i doc/source.200kbps.768x320.flv -y rtmp://127.0.0.1/live/livestream\n"
|
||||
" %s -i ../../doc/source.200kbps.768x320.flv -y rtmp://127.0.0.1/live/livestream\n",
|
||||
argv[0], argv[0], argv[0]);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
for (opt = 0; opt < argc; opt++) {
|
||||
srs_human_trace("argv[%d]=%s", opt, argv[opt]);
|
||||
}
|
||||
|
||||
// fill the options for mac
|
||||
for (opt = 0; opt < argc - 1; opt++) {
|
||||
// ignore all options except -i and -y.
|
||||
char* p = argv[opt];
|
||||
|
||||
// only accept -x
|
||||
if (p[0] != '-' || p[1] == 0 || p[2] != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// parse according the option name.
|
||||
switch (p[1]) {
|
||||
case 'i': in_flv_file = argv[opt + 1]; break;
|
||||
case 'y': out_rtmp_url = argv[opt + 1]; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!in_flv_file) {
|
||||
srs_human_trace("input invalid, use -i <input>");
|
||||
return -1;
|
||||
}
|
||||
if (!out_rtmp_url) {
|
||||
srs_human_trace("output invalid, use -y <output>");
|
||||
return -1;
|
||||
}
|
||||
|
||||
srs_human_trace("input: %s", in_flv_file);
|
||||
srs_human_trace("output: %s", out_rtmp_url);
|
||||
|
||||
if ((flv = srs_flv_open_read(in_flv_file)) == NULL) {
|
||||
ret = 2;
|
||||
srs_human_trace("open flv file failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ortmp = srs_rtmp_create(out_rtmp_url);
|
||||
|
||||
ret = proxy(flv, ortmp);
|
||||
srs_human_trace("ingest flv to RTMP completed");
|
||||
|
||||
srs_rtmp_destroy(ortmp);
|
||||
srs_flv_close(flv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int do_proxy(srs_flv_t flv, srs_rtmp_t ortmp, int64_t re, int32_t* pstarttime, uint32_t* ptimestamp)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
// packet data
|
||||
char type;
|
||||
int size;
|
||||
char* data = NULL;
|
||||
|
||||
srs_human_trace("start ingest flv to RTMP stream");
|
||||
char buffer[1024];
|
||||
for (;;) {
|
||||
// tag header
|
||||
if ((ret = srs_flv_read_tag_header(flv, &type, &size, ptimestamp)) != 0) {
|
||||
if (srs_flv_is_eof(ret)) {
|
||||
srs_human_trace("parse completed.");
|
||||
return 0;
|
||||
}
|
||||
srs_human_trace("flv get packet failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (size <= 0) {
|
||||
srs_human_trace("invalid size=%d", size);
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: FIXME: mem leak when error.
|
||||
data = (char*)malloc(size);
|
||||
if ((ret = srs_flv_read_tag_data(flv, data, size)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t timestamp = *ptimestamp;
|
||||
|
||||
if ((ret = srs_human_format_rtmp_packet(buffer, sizeof(buffer), type, timestamp, data, size)) != 0) {
|
||||
srs_human_trace("print packet failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
srs_human_trace("%s", buffer);
|
||||
|
||||
if ((ret = srs_rtmp_write_packet(ortmp, type, *ptimestamp, data, size)) != 0) {
|
||||
srs_human_trace("irtmp get packet failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (*pstarttime < 0 && srs_utils_flv_tag_is_av(type)) {
|
||||
*pstarttime = *ptimestamp;
|
||||
}
|
||||
|
||||
re_update(re, *pstarttime, *ptimestamp);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int proxy(srs_flv_t flv, srs_rtmp_t ortmp)
|
||||
{
|
||||
int ret = 0;
|
||||
uint32_t timestamp = 0;
|
||||
int32_t starttime = -1;
|
||||
|
||||
char header[13];
|
||||
if ((ret = srs_flv_read_header(flv, header)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
if ((ret = connect_oc(ortmp)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
int64_t re = re_create();
|
||||
|
||||
ret = do_proxy(flv, ortmp, re, &starttime, ×tamp);
|
||||
|
||||
// for the last pulse, always sleep.
|
||||
re_cleanup(re, starttime, timestamp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int connect_oc(srs_rtmp_t ortmp)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if ((ret = srs_rtmp_handshake(ortmp)) != 0) {
|
||||
srs_human_trace("ortmp simple handshake failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
srs_human_trace("ortmp simple handshake success");
|
||||
|
||||
if ((ret = srs_rtmp_connect_app(ortmp)) != 0) {
|
||||
srs_human_trace("ortmp connect vhost/app failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
srs_human_trace("ortmp connect vhost/app success");
|
||||
|
||||
if ((ret = srs_rtmp_publish_stream(ortmp)) != 0) {
|
||||
srs_human_trace("ortmp publish stream failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
srs_human_trace("ortmp publish stream success");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int64_t re_create()
|
||||
{
|
||||
// if not very precise, we can directly use this as re.
|
||||
int64_t re = srs_utils_time_ms();
|
||||
|
||||
// use the starttime to get the deviation
|
||||
int64_t deviation = re - tools_main_entrance_startup_time;
|
||||
srs_human_trace("deviation is %d ms, pulse is %d ms", (int)(deviation), (int)(RE_PULSE_MS));
|
||||
|
||||
// so, we adjust time to max(0, deviation)
|
||||
// because the last pulse, we already sleeped
|
||||
int adjust = (int)(deviation);
|
||||
if (adjust > 0) {
|
||||
srs_human_trace("adjust re time for %d ms", adjust);
|
||||
re -= adjust;
|
||||
} else {
|
||||
srs_human_trace("no need to adjust re time");
|
||||
}
|
||||
|
||||
return re;
|
||||
}
|
||||
void re_update(int64_t re, int32_t starttime, uint32_t time)
|
||||
{
|
||||
// send by pulse algorithm.
|
||||
int64_t now = srs_utils_time_ms();
|
||||
int64_t diff = time - starttime - (now -re);
|
||||
if (diff > RE_PULSE_MS && diff < RE_PULSE_JITTER_MS) {
|
||||
usleep((useconds_t)(diff * 1000));
|
||||
}
|
||||
}
|
||||
void re_cleanup(int64_t re, int32_t starttime, uint32_t time)
|
||||
{
|
||||
// for the last pulse, always sleep.
|
||||
// for the virtual live encoder long time publishing.
|
||||
int64_t now = srs_utils_time_ms();
|
||||
int64_t diff = time - starttime - (now -re);
|
||||
if (diff > 0) {
|
||||
srs_human_trace("re_cleanup, diff=%d, start=%d, last=%d ms", (int)diff, starttime, time);
|
||||
usleep((useconds_t)(diff * 1000));
|
||||
}
|
||||
}
|
|
@ -1,261 +0,0 @@
|
|||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2018 Winlin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../../objs/include/srs_librtmp.h"
|
||||
|
||||
int64_t tools_main_entrance_startup_time;
|
||||
int proxy(srs_mp4_t mp4, srs_rtmp_t ortmp);
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
// main function
|
||||
tools_main_entrance_startup_time = srs_utils_time_ms();
|
||||
|
||||
printf("Ingest mp4 file and publish to RTMP server like FFMPEG.\n");
|
||||
printf("SRS(OSSRS) client librtmp library.\n");
|
||||
printf("Version: %d.%d.%d\n", srs_version_major(), srs_version_minor(), srs_version_revision());
|
||||
|
||||
if (argc <= 2) {
|
||||
printf("Ingest mp4 file and publish to RTMP server\n"
|
||||
"Usage: %s <-i in_mp4_file> <-y out_rtmp_url>\n"
|
||||
" in_mp4_file input mp4 file, ingest from this file.\n"
|
||||
" out_rtmp_url output rtmp url, publish to this url.\n"
|
||||
"@remark Only support non-seek mp4, see https://github.com/ossrs/srs/issues/738#issuecomment-276343669\n"
|
||||
"For example:\n"
|
||||
" %s -i avatar.mp4 -y rtmp://127.0.0.1/live/livestream\n",
|
||||
argv[0], argv[0]);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
int opt;
|
||||
for (opt = 0; opt < argc; opt++) {
|
||||
srs_human_trace("The argv[%d]=%s", opt, argv[opt]);
|
||||
}
|
||||
|
||||
// fill the options for mac
|
||||
char* in_file = NULL;
|
||||
char* out_rtmp_url = NULL;
|
||||
for (opt = 0; opt < argc - 1; opt++) {
|
||||
// ignore all options except -i and -y.
|
||||
char* p = argv[opt];
|
||||
|
||||
// only accept -x
|
||||
if (p[0] != '-' || p[1] == 0 || p[2] != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// parse according the option name.
|
||||
switch (p[1]) {
|
||||
case 'i': in_file = argv[opt + 1]; break;
|
||||
case 'y': out_rtmp_url = argv[opt + 1]; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!in_file) {
|
||||
srs_human_trace("Invalid input file, use -i <input>");
|
||||
return -1;
|
||||
}
|
||||
if (!out_rtmp_url) {
|
||||
srs_human_trace("Invalid output url, use -y <output>");
|
||||
return -1;
|
||||
}
|
||||
|
||||
srs_human_trace("Input file: %s", in_file);
|
||||
srs_human_trace("Output url: %s", out_rtmp_url);
|
||||
|
||||
int ret = 0;
|
||||
|
||||
srs_mp4_t mp4 = NULL;
|
||||
if ((mp4 = srs_mp4_open_read(in_file)) == NULL) {
|
||||
ret = 2;
|
||||
srs_human_trace("open mp4 file failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
srs_rtmp_t ortmp = srs_rtmp_create(out_rtmp_url);
|
||||
|
||||
ret = proxy(mp4, ortmp);
|
||||
srs_human_trace("Ingest mp4 to RTMP ok.");
|
||||
|
||||
srs_rtmp_destroy(ortmp);
|
||||
srs_mp4_close(mp4);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int connect_oc(srs_rtmp_t ortmp)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if ((ret = srs_rtmp_handshake(ortmp)) != 0) {
|
||||
srs_human_trace("ortmp simple handshake failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
srs_human_trace("ortmp simple handshake success");
|
||||
|
||||
if ((ret = srs_rtmp_connect_app(ortmp)) != 0) {
|
||||
srs_human_trace("ortmp connect vhost/app failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
srs_human_trace("ortmp connect vhost/app success");
|
||||
|
||||
if ((ret = srs_rtmp_publish_stream(ortmp)) != 0) {
|
||||
srs_human_trace("ortmp publish stream failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
srs_human_trace("ortmp publish stream success");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define RE_PULSE_MS 300
|
||||
#define RE_PULSE_JITTER_MS 3000
|
||||
|
||||
int64_t re_create()
|
||||
{
|
||||
// if not very precise, we can directly use this as re.
|
||||
int64_t re = srs_utils_time_ms();
|
||||
|
||||
// use the starttime to get the deviation
|
||||
int64_t deviation = re - tools_main_entrance_startup_time;
|
||||
srs_human_trace("deviation is %d ms, pulse is %d ms", (int)(deviation), (int)(RE_PULSE_MS));
|
||||
|
||||
// so, we adjust time to max(0, deviation)
|
||||
// because the last pulse, we already sleeped
|
||||
int adjust = (int)(deviation);
|
||||
if (adjust > 0) {
|
||||
srs_human_trace("adjust re time for %d ms", adjust);
|
||||
re -= adjust;
|
||||
} else {
|
||||
srs_human_trace("no need to adjust re time");
|
||||
}
|
||||
|
||||
return re;
|
||||
}
|
||||
|
||||
void re_update(int64_t re, int32_t starttime, uint32_t time)
|
||||
{
|
||||
// send by pulse algorithm.
|
||||
int64_t now = srs_utils_time_ms();
|
||||
int64_t diff = time - starttime - (now -re);
|
||||
if (diff > RE_PULSE_MS && diff < RE_PULSE_JITTER_MS) {
|
||||
usleep((useconds_t)(diff * 1000));
|
||||
}
|
||||
}
|
||||
|
||||
void re_cleanup(int64_t re, int32_t starttime, uint32_t time)
|
||||
{
|
||||
// for the last pulse, always sleep.
|
||||
// for the virtual live encoder long time publishing.
|
||||
int64_t now = srs_utils_time_ms();
|
||||
int64_t diff = time - starttime - (now -re);
|
||||
if (diff > 0) {
|
||||
srs_human_trace("re_cleanup, diff=%d, start=%d, last=%d ms",
|
||||
(int)diff, starttime, time);
|
||||
usleep((useconds_t)(diff * 1000));
|
||||
}
|
||||
}
|
||||
|
||||
int do_proxy(srs_mp4_t mp4, srs_rtmp_t ortmp, int64_t re, int32_t* pstarttime, uint32_t* ptimestamp)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
srs_human_trace("start ingest mp4 to RTMP stream");
|
||||
char buffer[1024];
|
||||
for (;;) {
|
||||
// packet data
|
||||
char type;
|
||||
int32_t size;
|
||||
char* data = NULL;
|
||||
|
||||
// Read a mp4 sample and convert to flv tag.
|
||||
if (1) {
|
||||
srs_mp4_sample_t sample;
|
||||
if ((ret = srs_mp4_read_sample(mp4, &sample)) != 0) {
|
||||
if (srs_mp4_is_eof(ret)) {
|
||||
srs_human_trace("parse completed.");
|
||||
return 0;
|
||||
}
|
||||
srs_human_trace("mp4 get sample failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
size = srs_mp4_sizeof(mp4, &sample);
|
||||
data = (char*)malloc(size);
|
||||
|
||||
if ((ret = srs_mp4_to_flv_tag(mp4, &sample, &type, ptimestamp, data, size)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
srs_mp4_free_sample(&sample);
|
||||
}
|
||||
uint32_t timestamp = *ptimestamp;
|
||||
|
||||
if ((ret = srs_human_format_rtmp_packet(buffer, sizeof(buffer), type, timestamp, data, size)) != 0) {
|
||||
srs_human_trace("print packet failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
srs_human_trace("%s", buffer);
|
||||
|
||||
if ((ret = srs_rtmp_write_packet(ortmp, type, *ptimestamp, data, size)) != 0) {
|
||||
srs_human_trace("irtmp get packet failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (*pstarttime < 0 && srs_utils_flv_tag_is_av(type)) {
|
||||
*pstarttime = *ptimestamp;
|
||||
}
|
||||
|
||||
re_update(re, *pstarttime, *ptimestamp);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int proxy(srs_mp4_t mp4, srs_rtmp_t ortmp)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if ((ret = srs_mp4_init_demuxer(mp4)) != 0) {
|
||||
srs_human_trace("init demuxer failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
if ((ret = connect_oc(ortmp)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
int64_t re = re_create();
|
||||
|
||||
uint32_t timestamp = 0;
|
||||
int32_t starttime = -1;
|
||||
ret = do_proxy(mp4, ortmp, re, &starttime, ×tamp);
|
||||
|
||||
// for the last pulse, always sleep.
|
||||
re_cleanup(re, starttime, timestamp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1,279 +0,0 @@
|
|||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2018 Winlin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../../objs/include/srs_librtmp.h"
|
||||
|
||||
int connect_ic(srs_rtmp_t irtmp);
|
||||
int connect_oc(srs_rtmp_t ortmp);
|
||||
int proxy(srs_rtmp_t irtmp, srs_rtmp_t ortmp);
|
||||
|
||||
// whether use verbose log.
|
||||
int verbose = 0;
|
||||
// 2000 is about 30s.
|
||||
#define PITHY_PRINT_EVERY_MSGS 2000
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
// user option parse index.
|
||||
int opt = 0;
|
||||
// user options.
|
||||
char* in_rtmp_url = NULL;
|
||||
char* out_rtmp_url = NULL;
|
||||
// rtmp handler
|
||||
srs_rtmp_t irtmp, ortmp;
|
||||
|
||||
printf("Ingest RTMP to server like FFMPEG over srs-librtmp %d.%d.%d\n",
|
||||
srs_version_major(), srs_version_minor(), srs_version_revision());
|
||||
|
||||
if (argc <= 2) {
|
||||
printf("ingest RTMP and publish to RTMP server\n"
|
||||
"Usage: %s <-i in_rtmp_url> <-y out_rtmp_url> [-v verbose]\n"
|
||||
" in_rtmp_url input rtmp url, ingest from this url.\n"
|
||||
" out_rtmp_url output rtmp url, publish to this url.\n"
|
||||
" verbose output verbose log.\n"
|
||||
"For example:\n"
|
||||
" %s -i rtmp://127.0.0.1/live/livestream -y rtmp://127.0.0.1/live/demo\n"
|
||||
" %s -i rtmp://127.0.0.1/live/livestream -y rtmp://127.0.0.1/live/demo -v verbose\n",
|
||||
argv[0], argv[0], argv[0]);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// fill the options for mac
|
||||
for (opt = 0; opt < argc - 1; opt++) {
|
||||
// ignore all options except -i and -y.
|
||||
char* p = argv[opt];
|
||||
|
||||
// only accept -x
|
||||
if (p[0] != '-' || p[1] == 0 || p[2] != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// parse according the option name.
|
||||
switch (p[1]) {
|
||||
case 'i': in_rtmp_url = argv[++opt]; break;
|
||||
case 'y': out_rtmp_url = argv[++opt]; break;
|
||||
case 'v': verbose=1; opt++; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!in_rtmp_url) {
|
||||
srs_human_trace("input invalid, use -i <input>");
|
||||
return -1;
|
||||
}
|
||||
if (!out_rtmp_url) {
|
||||
srs_human_trace("output invalid, use -y <output>");
|
||||
return -1;
|
||||
}
|
||||
|
||||
srs_human_trace("ingest %s to %s, verbose=%d", in_rtmp_url, out_rtmp_url, verbose);
|
||||
|
||||
irtmp = srs_rtmp_create(in_rtmp_url);
|
||||
ortmp = srs_rtmp_create(out_rtmp_url);
|
||||
|
||||
ret = proxy(irtmp, ortmp);
|
||||
srs_human_trace("proxy completed");
|
||||
|
||||
srs_rtmp_destroy(irtmp);
|
||||
srs_rtmp_destroy(ortmp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int proxy(srs_rtmp_t irtmp, srs_rtmp_t ortmp)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
// packet data
|
||||
int size;
|
||||
char type;
|
||||
char* data;
|
||||
uint32_t timestamp;
|
||||
uint64_t nb_msgs = 0;
|
||||
|
||||
if ((ret = connect_ic(irtmp)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
if ((ret = connect_oc(ortmp)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
srs_human_trace("start proxy RTMP stream");
|
||||
} else {
|
||||
srs_human_verbose("start proxy RTMP stream");
|
||||
}
|
||||
|
||||
char buffer[1024];
|
||||
for (;;) {
|
||||
if ((ret = srs_rtmp_read_packet(irtmp, &type, ×tamp, &data, &size)) != 0) {
|
||||
srs_human_trace("irtmp get packet failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!srs_utils_flv_tag_is_ok(type)) {
|
||||
if (verbose) {
|
||||
srs_human_trace("ignore invalid flv tag=%d, dts=%d, %d bytes", type, timestamp, size);
|
||||
} else {
|
||||
srs_human_verbose("ignore invalid flv tag=%d, dts=%d, %d bytes", type, timestamp, size);
|
||||
}
|
||||
free(data);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (verbose || ((nb_msgs++ % PITHY_PRINT_EVERY_MSGS) == 0 && nb_msgs > 10)) {
|
||||
if ((ret = srs_human_format_rtmp_packet(buffer, sizeof(buffer), type, timestamp, data, size)) != 0) {
|
||||
srs_human_trace("print packet failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
srs_human_trace("%s", buffer);
|
||||
}
|
||||
|
||||
if ((ret = srs_rtmp_write_packet(ortmp, type, timestamp, data, size)) != 0) {
|
||||
srs_human_trace("irtmp get packet failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
srs_human_trace("ortmp sent packet: type=%s, time=%d, size=%d",
|
||||
srs_human_flv_tag_type2string(type), timestamp, size);
|
||||
} else {
|
||||
srs_human_verbose("ortmp sent packet: type=%s, time=%d, size=%d",
|
||||
srs_human_flv_tag_type2string(type), timestamp, size);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int connect_ic(srs_rtmp_t irtmp)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
// srs debug info.
|
||||
char* ip = NULL;
|
||||
char* sig = NULL;
|
||||
int pid = 0, cid = 0;
|
||||
int major = 0, minor = 0, revision= 0, build = 0;
|
||||
|
||||
if ((ret = srs_rtmp_handshake(irtmp)) != 0) {
|
||||
srs_human_trace("irtmp simple handshake failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
if (verbose) {
|
||||
srs_human_trace("irtmp simple handshake success");
|
||||
} else {
|
||||
srs_human_verbose("irtmp simple handshake success");
|
||||
}
|
||||
|
||||
if (srs_rtmp_connect_app(irtmp) != 0) {
|
||||
srs_human_trace("irtmp connect vhost/app failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((ret = srs_rtmp_get_server_sig(irtmp, &sig)) != 0) {
|
||||
srs_human_trace("Retrieve server ID failed, ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
if ((ret = srs_rtmp_get_server_id(irtmp, &ip, &pid, &cid)) != 0) {
|
||||
srs_human_trace("Retrieve server ID failed, ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
if ((ret = srs_rtmp_get_server_version(irtmp, &major, &minor, &revision, &build)) != 0) {
|
||||
srs_human_trace("Retrieve server version failed, ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
srs_human_trace("irtmp connect ok, ip=%s, server=%s/%d.%d.%d.%d, pid=%d, cid=%d",
|
||||
ip, sig, major, minor, revision, build, pid, cid);
|
||||
|
||||
if ((ret = srs_rtmp_play_stream(irtmp)) != 0) {
|
||||
srs_human_trace("irtmp play stream failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
if (verbose) {
|
||||
srs_human_trace("irtmp play stream success");
|
||||
} else {
|
||||
srs_human_verbose("irtmp play stream success");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int connect_oc(srs_rtmp_t ortmp)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
// srs debug info.
|
||||
char* ip = NULL;
|
||||
char* sig = NULL;
|
||||
int pid = 0, cid = 0;
|
||||
int major = 0, minor = 0, revision= 0, build = 0;
|
||||
|
||||
if ((ret = srs_rtmp_handshake(ortmp)) != 0) {
|
||||
srs_human_trace("ortmp simple handshake failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
if (verbose) {
|
||||
srs_human_trace("ortmp simple handshake success");
|
||||
} else {
|
||||
srs_human_verbose("ortmp simple handshake success");
|
||||
}
|
||||
|
||||
if (srs_rtmp_connect_app(ortmp) != 0) {
|
||||
srs_human_trace("ortmp connect vhost/app failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((ret = srs_rtmp_get_server_sig(ortmp, &sig)) != 0) {
|
||||
srs_human_trace("Retrieve server ID failed, ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
if ((ret = srs_rtmp_get_server_id(ortmp, &ip, &pid, &cid)) != 0) {
|
||||
srs_human_trace("Retrieve server ID failed, ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
if ((ret = srs_rtmp_get_server_version(ortmp, &major, &minor, &revision, &build)) != 0) {
|
||||
srs_human_trace("Retrieve server version failed, ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
srs_human_trace("ortmp connect ok, ip=%s, server=%s/%d.%d.%d.%d, pid=%d, cid=%d",
|
||||
ip, sig, major, minor, revision, build, pid, cid);
|
||||
|
||||
if ((ret = srs_rtmp_publish_stream(ortmp)) != 0) {
|
||||
srs_human_trace("ortmp publish stream failed. ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
if (verbose) {
|
||||
srs_human_trace("ortmp publish stream success");
|
||||
} else {
|
||||
srs_human_verbose("ortmp publish stream success");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2018 Winlin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../../objs/include/srs_librtmp.h"
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
printf("suck rtmp stream like rtmpdump\n");
|
||||
printf("srs(ossrs) client librtmp library.\n");
|
||||
printf("version: %d.%d.%d\n", srs_version_major(), srs_version_minor(), srs_version_revision());
|
||||
|
||||
if (argc <= 1) {
|
||||
printf("Usage: %s <rtmp_url>\n"
|
||||
" rtmp_url RTMP stream url to play\n"
|
||||
"For example:\n"
|
||||
" %s rtmp://127.0.0.1:1935/live/livestream\n",
|
||||
argv[0], argv[0]);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
srs_human_trace("rtmp url: %s", argv[1]);
|
||||
srs_rtmp_t rtmp = srs_rtmp_create(argv[1]);
|
||||
|
||||
if (srs_rtmp_handshake(rtmp) != 0) {
|
||||
srs_human_trace("simple handshake failed.");
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
srs_human_trace("simple handshake success");
|
||||
|
||||
if (srs_rtmp_connect_app(rtmp) != 0) {
|
||||
srs_human_trace("connect vhost/app failed.");
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
srs_human_trace("connect vhost/app success");
|
||||
|
||||
if (srs_rtmp_play_stream(rtmp) != 0) {
|
||||
srs_human_trace("play stream failed.");
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
srs_human_trace("play stream success");
|
||||
|
||||
char buffer[1024];
|
||||
for (;;) {
|
||||
int size;
|
||||
char type;
|
||||
char* data;
|
||||
uint32_t timestamp;
|
||||
|
||||
if (srs_rtmp_read_packet(rtmp, &type, ×tamp, &data, &size) != 0) {
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
|
||||
if (srs_human_format_rtmp_packet(buffer, sizeof(buffer), type, timestamp, data, size) != 0) {
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
srs_human_trace("%s", buffer);
|
||||
|
||||
free(data);
|
||||
}
|
||||
|
||||
rtmp_destroy:
|
||||
srs_rtmp_destroy(rtmp);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2018 Winlin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../../objs/include/srs_librtmp.h"
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
printf("publish rtmp stream to server like FMLE/FFMPEG/Encoder\n");
|
||||
printf("srs(ossrs) client librtmp library.\n");
|
||||
printf("version: %d.%d.%d\n", srs_version_major(), srs_version_minor(), srs_version_revision());
|
||||
|
||||
if (argc <= 1) {
|
||||
printf("Usage: %s <rtmp_url>\n"
|
||||
" rtmp_url RTMP stream url to publish\n"
|
||||
"For example:\n"
|
||||
" %s rtmp://127.0.0.1:1935/live/livestream\n",
|
||||
argv[0], argv[0]);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// warn it .
|
||||
// @see: https://github.com/ossrs/srs/issues/126
|
||||
srs_human_trace("\033[33m%s\033[0m",
|
||||
"[warning] it's only a sample to use librtmp. "
|
||||
"please never use it to publish and test forward/transcode/edge/HLS whatever. "
|
||||
"you should refer to this tool to use the srs-librtmp to publish the real media stream."
|
||||
"read about: https://github.com/ossrs/srs/issues/126");
|
||||
srs_human_trace("rtmp url: %s", argv[1]);
|
||||
srs_rtmp_t rtmp = srs_rtmp_create(argv[1]);
|
||||
|
||||
if (srs_rtmp_handshake(rtmp) != 0) {
|
||||
srs_human_trace("simple handshake failed.");
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
srs_human_trace("simple handshake success");
|
||||
|
||||
if (srs_rtmp_connect_app(rtmp) != 0) {
|
||||
srs_human_trace("connect vhost/app failed.");
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
srs_human_trace("connect vhost/app success");
|
||||
|
||||
if (srs_rtmp_publish_stream(rtmp) != 0) {
|
||||
srs_human_trace("publish stream failed.");
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
srs_human_trace("publish stream success");
|
||||
|
||||
uint32_t timestamp = 0;
|
||||
for (;;) {
|
||||
char type = SRS_RTMP_TYPE_VIDEO;
|
||||
int size = 4096;
|
||||
char* data = (char*)malloc(4096);
|
||||
|
||||
timestamp += 40;
|
||||
|
||||
if (srs_rtmp_write_packet(rtmp, type, timestamp, data, size) != 0) {
|
||||
goto rtmp_destroy;
|
||||
}
|
||||
srs_human_trace("sent packet: type=%s, time=%d, size=%d", srs_human_flv_tag_type2string(type), timestamp, size);
|
||||
|
||||
usleep(40 * 1000);
|
||||
}
|
||||
|
||||
rtmp_destroy:
|
||||
srs_rtmp_destroy(rtmp);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,350 +0,0 @@
|
|||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2018 Winlin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "../../objs/include/srs_librtmp.h"
|
||||
|
||||
void parse_amf0_object(char* p, srs_amf0_t args)
|
||||
{
|
||||
char opvt = 0; // object property value type.
|
||||
const char* opnp = NULL; // object property name ptr.
|
||||
const char* opvp = NULL; // object property value ptr.
|
||||
|
||||
while (*p) {
|
||||
switch (*p++) {
|
||||
case 'O':
|
||||
while (*p && *p++ != ':') {
|
||||
}
|
||||
if (*p++ == '1') {
|
||||
printf("amf0 object start\n");
|
||||
} else {
|
||||
printf("amf0 object end\n");
|
||||
}
|
||||
break;
|
||||
case 'N':
|
||||
opvt = *p++;
|
||||
if (*p++ != ':') {
|
||||
printf("object property must split by :.\n");
|
||||
exit(-1);
|
||||
}
|
||||
opnp = p++;
|
||||
while (*p && *p++ != ':') {
|
||||
}
|
||||
p[-1] = 0;
|
||||
opvp = p;
|
||||
printf("amf0 %c property[%s]=%s\n", opvt, opnp, opvp);
|
||||
switch(opvt) {
|
||||
case 'S':
|
||||
srs_amf0_object_property_set(args, opnp, srs_amf0_create_string(opvp));
|
||||
break;
|
||||
default:
|
||||
printf("unsupported object property.\n");
|
||||
exit(-1);
|
||||
}
|
||||
*p=0;
|
||||
break;
|
||||
default:
|
||||
printf("only supports an object arg.\n");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// srs debug info.
|
||||
char* ip = NULL;
|
||||
char* sig = NULL;
|
||||
int pid = 0, cid = 0;
|
||||
int major = 0, minor = 0, revision= 0, build = 0;
|
||||
// User options.
|
||||
int complex_handshake = 0;
|
||||
const char* rtmp_url = NULL;
|
||||
const char* output_flv = NULL;
|
||||
const char* swfUrl = NULL;
|
||||
const char* tcUrl = NULL;
|
||||
const char* pageUrl = NULL;
|
||||
srs_amf0_t args = NULL;
|
||||
|
||||
int do_proxy(srs_rtmp_t rtmp, srs_flv_t flv)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if ((ret = srs_rtmp_dns_resolve(rtmp)) != 0) {
|
||||
srs_human_trace("dns resolve failed, ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((ret = srs_rtmp_connect_server(rtmp)) != 0) {
|
||||
srs_human_trace("connect to server failed, ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (complex_handshake) {
|
||||
if ((ret = srs_rtmp_do_complex_handshake(rtmp)) != 0) {
|
||||
srs_human_trace("complex handshake failed, ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
srs_human_trace("do complex handshake success");
|
||||
} else {
|
||||
if ((ret = srs_rtmp_do_simple_handshake(rtmp)) != 0) {
|
||||
srs_human_trace("simple handshake failed, ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
srs_human_trace("do simple handshake success");
|
||||
}
|
||||
|
||||
if ((ret = srs_rtmp_set_connect_args(rtmp, tcUrl, swfUrl, pageUrl, args)) != 0) {
|
||||
srs_human_trace("set connect args failed, ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((ret = srs_rtmp_connect_app(rtmp)) != 0) {
|
||||
srs_human_trace("connect vhost/app failed, ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((ret = srs_rtmp_get_server_sig(rtmp, &sig)) != 0) {
|
||||
srs_human_trace("Retrieve server ID failed, ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
if ((ret = srs_rtmp_get_server_id(rtmp, &ip, &pid, &cid)) != 0) {
|
||||
srs_human_trace("Retrieve server ID failed, ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
if ((ret = srs_rtmp_get_server_version(rtmp, &major, &minor, &revision, &build)) != 0) {
|
||||
srs_human_trace("Retrieve server version failed, ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
srs_human_trace("connect ok, ip=%s, server=%s/%d.%d.%d.%d, pid=%d, cid=%d",
|
||||
ip, sig, major, minor, revision, build, pid, cid);
|
||||
|
||||
if ((ret = srs_rtmp_play_stream(rtmp)) != 0) {
|
||||
srs_human_trace("play stream failed, ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
srs_human_trace("play stream success");
|
||||
|
||||
if (flv) {
|
||||
// flv header
|
||||
char header[9];
|
||||
// 3bytes, signature, "FLV",
|
||||
header[0] = 'F';
|
||||
header[1] = 'L';
|
||||
header[2] = 'V';
|
||||
// 1bytes, version, 0x01,
|
||||
header[3] = 0x01;
|
||||
// 1bytes, flags, UB[5] 0, UB[1] audio present, UB[1] 0, UB[1] video present.
|
||||
header[4] = 0x03; // audio + video.
|
||||
// 4bytes, dataoffset
|
||||
header[5] = 0x00;
|
||||
header[6] = 0x00;
|
||||
header[7] = 0x00;
|
||||
header[8] = 0x09;
|
||||
if ((ret = srs_flv_write_header(flv, header)) != 0) {
|
||||
srs_human_trace("write flv header failed, ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t nb_packets = 0;
|
||||
uint32_t pre_timestamp = 0;
|
||||
int64_t pre_now = -1;
|
||||
int64_t start_time = -1;
|
||||
char buffer[1024];
|
||||
for (;;) {
|
||||
int size;
|
||||
char type;
|
||||
char* data;
|
||||
uint32_t timestamp;
|
||||
|
||||
if ((ret = srs_rtmp_read_packet(rtmp, &type, ×tamp, &data, &size)) != 0) {
|
||||
srs_human_trace("read rtmp packet failed, ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (pre_now == -1) {
|
||||
pre_now = srs_utils_time_ms();
|
||||
}
|
||||
if (start_time == -1) {
|
||||
start_time = srs_utils_time_ms();
|
||||
}
|
||||
|
||||
if ((ret = srs_human_format_rtmp_packet2(buffer, sizeof(buffer), type, timestamp, data, size, pre_timestamp, pre_now, start_time, nb_packets++)) != 0) {
|
||||
srs_human_trace("print rtmp packet failed, ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
srs_human_trace("%s", buffer);
|
||||
|
||||
pre_timestamp = timestamp;
|
||||
pre_now = srs_utils_time_ms();
|
||||
|
||||
// we only write some types of messages to flv file.
|
||||
int is_flv_msg = type == SRS_RTMP_TYPE_AUDIO
|
||||
|| type == SRS_RTMP_TYPE_VIDEO || type == SRS_RTMP_TYPE_SCRIPT;
|
||||
|
||||
// for script data, ignore except onMetaData
|
||||
if (type == SRS_RTMP_TYPE_SCRIPT) {
|
||||
if (!srs_rtmp_is_onMetaData(type, data, size)) {
|
||||
is_flv_msg = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (flv) {
|
||||
if (is_flv_msg) {
|
||||
if ((ret = srs_flv_write_tag(flv, type, timestamp, data, size)) != 0) {
|
||||
srs_human_trace("dump rtmp packet failed, ret=%d", ret);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
srs_human_trace("drop message type=%#x, size=%dB", type, size);
|
||||
}
|
||||
}
|
||||
|
||||
free(data);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
srs_flv_t flv = NULL;
|
||||
srs_rtmp_t rtmp = NULL;
|
||||
|
||||
printf("dump rtmp stream to flv file\n");
|
||||
printf("srs(ossrs) client librtmp library.\n");
|
||||
printf("version: %d.%d.%d\n", srs_version_major(), srs_version_minor(), srs_version_revision());
|
||||
printf("@refer to http://rtmpdump.mplayerhq.hu/rtmpdump.1.html\n");
|
||||
|
||||
int show_help = 0;
|
||||
const char* short_options = "hxr:o:s:t:p:C:";
|
||||
struct option long_options[] = {
|
||||
{"rtmp", required_argument, 0, 'r'},
|
||||
{"flv", required_argument, 0, 'o'},
|
||||
{"swfUrl", required_argument, 0, 's'},
|
||||
{"tcUrl", required_argument, 0, 't'},
|
||||
{"pageUrl", required_argument, 0, 'p'},
|
||||
{"conn", required_argument, 0, 'C'},
|
||||
{"complex", no_argument, 0, 'x'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
int opt = 0;
|
||||
int option_index = 0;
|
||||
while((opt = getopt_long(argc, argv, short_options, long_options, &option_index)) != -1){
|
||||
switch(opt){
|
||||
case 'r':
|
||||
rtmp_url = optarg;
|
||||
break;
|
||||
case 'o':
|
||||
output_flv = optarg;
|
||||
break;
|
||||
case 's':
|
||||
swfUrl = optarg;
|
||||
break;
|
||||
case 't':
|
||||
tcUrl = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
pageUrl = optarg;
|
||||
break;
|
||||
case 'C':
|
||||
if (!args) {
|
||||
args = srs_amf0_create_object();
|
||||
}
|
||||
char* p = (char*)optarg;
|
||||
parse_amf0_object(p, args);
|
||||
break;
|
||||
case 'x':
|
||||
complex_handshake = 1;
|
||||
break;
|
||||
case 'h':
|
||||
show_help = 1;
|
||||
break;
|
||||
default:
|
||||
printf("unsupported opt.\n");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!rtmp_url || show_help) {
|
||||
printf("Usage: %s -r url [-o output] [-s swfUrl] [-t tcUrl] [-p pageUrl] [-C conndata] [--complex] [-h]\n"
|
||||
"Options:\n"
|
||||
" --rtmp -r url\n"
|
||||
" URL of the server and media content.\n"
|
||||
" --flv -o output\n"
|
||||
" Specify the output file name. If the name is − or is omitted, the stream is written to stdout.\n"
|
||||
" --complex\n"
|
||||
" Whether use complex handshake(srs-librtmp with ssl required).\n"
|
||||
" --swfUrl -s url\n"
|
||||
" URL of the SWF player for the media. By default no value will be sent.\n"
|
||||
" --tcUrl -t url\n"
|
||||
" URL of the target stream. Defaults to rtmp[e]://host[:port]/app/playpath.\n"
|
||||
" --pageUrl -p url\n"
|
||||
" URL of the web page in which the media was embedded. By default no value will be sent.\n"
|
||||
" −−conn −C type:data\n"
|
||||
" Append arbitrary AMF data to the Connect message. The type must be B for Boolean, N for number, S for string, O for object, or Z for null. For Booleans the data must be either 0 or 1 for FALSE or TRUE, respectively. Likewise for Objects the data must be 0 or 1 to end or begin an object, respectively. Data items in subobjects may be named, by prefixing the type with 'N' and specifying the name before the value, e.g. NB:myFlag:1. This option may be used multiple times to construct arbitrary AMF sequences. E.g.\n"
|
||||
" −C B:1 −C S:authMe −C O:1 −C NN:code:1.23 −C NS:flag:ok −C O:0\n"
|
||||
" -C O:1 -C NS:CONN:\" -C B:4Rg9vr0\" -C O:0\n"
|
||||
" @remark, support a object args only.\n"
|
||||
" --help -h\n"
|
||||
" Print a summary of command options.\n"
|
||||
"For example:\n"
|
||||
" %s -r rtmp://127.0.0.1:1935/live/livestream -o output.flv\n"
|
||||
" %s -h\n",
|
||||
argv[0], argv[0], argv[0]);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
srs_human_trace("rtmp url: %s", rtmp_url);
|
||||
srs_human_trace("handshake: %s", (complex_handshake? "complex" : "simple"));
|
||||
srs_human_trace("swfUrl: %s", swfUrl);
|
||||
srs_human_trace("pageUrl: %s", pageUrl);
|
||||
srs_human_trace("tcUrl: %s", tcUrl);
|
||||
if (output_flv) {
|
||||
srs_human_trace("flv output path: %s", output_flv);
|
||||
} else {
|
||||
srs_human_trace("output to console");
|
||||
}
|
||||
|
||||
rtmp = srs_rtmp_create(rtmp_url);
|
||||
if (output_flv) {
|
||||
flv = srs_flv_open_write(output_flv);
|
||||
}
|
||||
|
||||
int ret = 0;
|
||||
if ((ret = do_proxy(rtmp, flv)) != 0) {
|
||||
srs_human_trace("Dump RTMP failed, ret=%d", ret);
|
||||
}
|
||||
|
||||
srs_rtmp_destroy(rtmp);
|
||||
srs_flv_close(flv);
|
||||
srs_human_trace("completed");
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue