Skip to content

Instantly share code, notes, and snippets.

@j0sh
Created January 23, 2018 16:12
Show Gist options
  • Save j0sh/05252a48edd163b01e65f1b363de1631 to your computer and use it in GitHub Desktop.
Save j0sh/05252a48edd163b01e65f1b363de1631 to your computer and use it in GitHub Desktop.
Proof of concept native RTMP to HLS
#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;
}
void lpms_init();
void lpms_deinit();
int lpms_rtmp2hls(char *listen, char *outf, char *seg_time);
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()
}
@ericxtang
Copy link

@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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment