-
-
Save j0sh/05252a48edd163b01e65f1b363de1631 to your computer and use it in GitHub Desktop.
Proof of concept native RTMP to HLS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "lpms_ffmpeg.h" | |
#include <libavformat/avformat.h> | |
void lpms_init() | |
{ | |
av_register_all(); | |
avcodec_register_all(); | |
avformat_network_init(); | |
//av_log_set_level(AV_LOG_DEBUG); | |
} | |
void lpms_deinit() | |
{ | |
avformat_network_deinit(); | |
} | |
int lpms_rtmp2hls(char *listen, char *outf, char* seg_time) | |
{ | |
#define r2h_err(str) {\ | |
ret = 1; \ | |
errstr = str; \ | |
goto handle_r2h_err; \ | |
} | |
char *errstr = NULL; | |
int ret = 0; | |
AVFormatContext *ic = NULL; | |
AVFormatContext *oc = NULL; | |
AVOutputFormat *ofmt = NULL; | |
AVStream *ist = NULL; | |
AVStream *ost = NULL; | |
AVDictionary *dd = NULL; | |
AVDictionary *md = NULL; | |
AVCodec *codec = NULL; | |
int64_t prev_ts[2] = {AV_NOPTS_VALUE, AV_NOPTS_VALUE}; | |
int stream_map[2] = {-1, -1}; | |
AVPacket pkt; | |
av_dict_set(&dd, "listen", "1", 0); | |
ret = avformat_open_input(&ic, listen, NULL, &dd); | |
if (ret < 0) r2h_err("Unable to open input\n"); | |
ret = avformat_find_stream_info(ic, NULL); | |
if (ret < 0) r2h_err("Unable to find any input streams\n"); | |
ofmt = av_guess_format(NULL, outf, NULL); | |
if (!ofmt) r2h_err("Could not deduce output format from file extension\n"); | |
ret = avformat_alloc_output_context2(&oc, ofmt, NULL, outf); | |
if (ret < 0) r2h_err("Unable to allocate output context\n"); | |
stream_map[0] = av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0); | |
if (stream_map[0] < 0) r2h_err("Unable to find video stream\n"); | |
stream_map[1] = av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO, -1, -1, &codec, 0); | |
if (stream_map[1] < 0) r2h_err("Unable to find audio stream\n"); | |
ist = ic->streams[stream_map[0]]; | |
ost = avformat_new_stream(oc, NULL); | |
if (!ost) r2h_err("Unable to allocate output video stream\n"); | |
avcodec_parameters_copy(ost->codecpar, ist->codecpar); | |
ist = ic->streams[stream_map[1]]; | |
ost = avformat_new_stream(oc, NULL); | |
if (!ost) r2h_err("Unable to allocate output audio stream\n"); | |
avcodec_parameters_copy(ost->codecpar, ist->codecpar); | |
av_dict_set(&md, "hls_time", seg_time, 0); | |
ret = avformat_write_header(oc, NULL); | |
if (ret < 0) r2h_err("Error writing header\n"); | |
av_init_packet(&pkt); | |
while (1) { | |
ret = av_read_frame(ic, &pkt); | |
if (ret == AVERROR_EOF) { | |
av_interleaved_write_frame(oc, NULL); // flush | |
break; | |
} else if (ret < 0) r2h_err("Error reading\n"); | |
// rescale timestamps | |
if (pkt.stream_index == stream_map[0]) pkt.stream_index = 0; | |
else if (pkt.stream_index == stream_map[1]) pkt.stream_index = 1; | |
else goto r2hloop_end; | |
ist = ic->streams[pkt.stream_index]; | |
ost = oc->streams[stream_map[pkt.stream_index]]; | |
int64_t dts_next = pkt.dts, dts_prev = prev_ts[pkt.stream_index]; | |
if (AV_NOPTS_VALUE == dts_prev) dts_prev = dts_next; | |
pkt.pts = av_rescale_q_rnd(pkt.pts, ist->time_base, ost->time_base, | |
AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); | |
pkt.dts = av_rescale_q_rnd(pkt.dts, ist->time_base, ost->time_base, | |
AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); | |
if (!pkt.duration) pkt.duration = dts_next - dts_prev; | |
pkt.duration = av_rescale_q(pkt.duration, ist->time_base, ost->time_base); | |
prev_ts[pkt.stream_index] = dts_next; | |
// write the thing | |
ret = av_interleaved_write_frame(oc, &pkt); | |
if (ret < 0) r2h_err("Unable to write output frame\n"); | |
r2hloop_end: | |
av_packet_unref(&pkt); | |
} | |
ret = av_write_trailer(oc); | |
if (ret < 0) r2h_err("Unable to write trailer\n"); | |
handle_r2h_err: | |
fprintf(stderr, errstr); | |
if (ic) avformat_close_input(&ic); | |
if (oc) avformat_free_context(oc); | |
if (dd) av_dict_free(&dd); | |
if (md) av_dict_free(&md); | |
return ret; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void lpms_init(); | |
void lpms_deinit(); | |
int lpms_rtmp2hls(char *listen, char *outf, char *seg_time); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
// #cgo pkg-config: libavformat | |
// #include "lpms_ffmpeg.h" | |
import "C" | |
import "fmt" | |
func main() { | |
C.lpms_init() | |
ret := C.int(C.lpms_rtmp2hls( | |
C.CString("rtmp://localhost/stream/new"), | |
C.CString("out/lp.m3u8"), | |
C.CString("4"))) | |
fmt.Printf("RTMP2HLS Transmux Return : %d\n", ret) | |
C.lpms_deinit() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@j0sh this looks good - is there a way to stream the output segments out instead of having to rely on writing to the file system? That feels more stable (otherwise we have to pull the fs later and guess what it means when something unexpected happens)