me
me copied to clipboard
学习 rtmp 开发 (Part 2: hello world)
一档起步
main.c
#include <librtmp/rtmp.h>
#include <stdio.h>
int main() {
int version = RTMP_LibVersion();
printf("0x%06x\n", version); // 0x020300 means 2.3
return 0;
}
Makefile
CC=clang
ARCH=x86_64
CFLAGS=`pkg-config --cflags librtmp`
LDFLAGS=`pkg-config --libs librtmp`
build: src/main.c
mkdir -p build
$(CC) -arch ${ARCH} $(CFLAGS) $(LDFLAGS) -o build/app src/main.c
run: build
cd build && ./app
clean:
rm -rf build
.PHONY: build run clean
几个点:
- 学习librtmp需要从source code出发,header就是api,rtmpdump就是sample
- 更多的知识在代码以外,比如需要掌握一下flv, rtmp protocol,代码只是逻辑的实现
- 凡事都不能操之过急,代码这个东西就是需要一步步小心翼翼解决所有不确定性
- 雷神yyds
保存到flv
这本身就是rtmpdump的功能,结合雷神的sample,形成如下代码:
main.c
#include <librtmp/rtmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define APP_SUCCESS 0
#define APP_FAILED 1
// rtmpdump -r rtmp://media3.scctv.net/live/scctv_800 -o test.flv
static void usage();
static void parse_args(int, char **, char **, char **);
static void sigIntHandler(int);
int main(int argc, char *argv[]) {
// data
FILE *file = 0;
char *output = "out.flv";
char *url;
RTMP rtmp = {0};
// parse options and arguments
parse_args(argc, argv, &url, &output);
printf("rtmp url: %s\n", url);
printf("output: %s\n", output);
if (!(file = fopen(output, "wb"))) {
fprintf(stderr, "Open file FAILED\n");
exit(APP_FAILED);
}
signal(SIGINT, sigIntHandler);
RTMP_Init(&rtmp);
if (!RTMP_SetupURL(&rtmp, url)) {
fprintf(stderr, "RTMP SetupURL FAILED\n");
return APP_FAILED;
}
if (!RTMP_Connect(&rtmp, NULL)) {
fprintf(stderr, "RTMP Connect FAILED\n");
RTMP_Close(&rtmp);
return APP_FAILED;
}
if (!RTMP_ConnectStream(&rtmp, 0)) {
fprintf(stderr, "RTMP ConnectStream FAILED\n");
RTMP_Close(&rtmp);
return APP_FAILED;
}
int size = 2 * 1024 * 1024; // 2M bytes
char buffer[size];
int count;
int total;
while (!RTMP_ctrlC && (count = RTMP_Read(&rtmp, buffer, size)) > 0) {
if (fwrite(buffer, sizeof(char), count, file) != count) {
fprintf(stderr, "RTMP read FAILED\n");
break;
}
total += count;
printf("Receive: %5d Byte, Total: %5.2f kB\n", count, total * 1.0 / 1024);
}
printf("# EOF");
fclose(file);
RTMP_Close(&rtmp);
return APP_SUCCESS;
}
static void usage() {
printf("Usage: app -o out.flv rtmp://media3.scctv.net/live/scctv_800\n");
}
static void parse_args(int argc, char *argv[], char **url, char **output) {
int c;
while ((c = getopt(argc, argv, "o:")) != -1) {
switch (c) {
case 'o':
*output = optarg;
break;
default:
usage();
break;
}
}
*url = argv[optind];
if (*url == NULL) {
usage();
exit(APP_FAILED);
}
}
static void sigIntHandler(int sig) {
RTMP_ctrlC = TRUE;
printf(" Caught signal: %d, cleaning up, just a second...\n", sig);
signal(SIGINT, SIG_IGN);
}
Makefile
CC=clang
ARCH=x86_64
CFLAGS=`pkg-config --cflags librtmp`
LDFLAGS=`pkg-config --libs librtmp`
build: src/main.c
mkdir -p build
$(CC) -arch ${ARCH} $(CFLAGS) $(LDFLAGS) -o build/app src/main.c
run: build
cd build && ./app -o ../out.flv rtmp://your-domain/app/stream
clean:
rm -rf build
.PHONY: build run clean
通过ffplay out.flv
:
ffplay out.flv
ffplay version 5.0.1 Copyright (c) 2003-2022 the FFmpeg developers
built with Apple clang version 13.1.6 (clang-1316.0.21.2)
configuration: --prefix...
libavutil 57. 17.100 / 57. 17.100
libavcodec 59. 18.100 / 59. 18.100
libavformat 59. 16.100 / 59. 16.100
libavdevice 59. 4.100 / 59. 4.100
libavfilter 8. 24.100 / 8. 24.100
libswscale 6. 4.100 / 6. 4.100
libswresample 4. 3.100 / 4. 3.100
libpostproc 56. 3.100 / 56. 3.100
Input #0, flv, from 'out.flv':
Metadata:
major_brand : mp42
minor_version : 0
compatible_brands: mp42mp41
encoder : Lavf59.24.100
server : SRS/4.0.251(Leo)
server_version : 4.0.251
Duration: 00:00:06.72, start: 0.021000, bitrate: 4900 kb/s
Stream #0:0: Audio: aac (LC), 48000 Hz, stereo, fltp, 128 kb/s
Stream #0:1: Video: h264 (High), yuv420p(tv, bt709, progressive), 1920x1080, 30 fps, 30 tbr, 1k tbn
- 正好100行代码, 无脑的将RTMP_Read的内容直接搬到flv文件
- 很简单,没有什么需要说明的内容,后续直接面对几个问题: 1. flv format and f4v/mp4 format, 2. flv 和 h264/sps/pps/nalu 的关系,3. swift 如何 bridge 到 librtmp