Packages
membrane_rtmp_plugin
0.2.0
0.29.5
0.29.4
0.29.3
0.29.2
0.29.1
0.29.0
0.28.1
0.28.0
0.27.3
0.27.2
0.27.0
0.26.0
0.25.0
0.24.0
0.23.3
0.23.2
0.23.1
0.23.0
0.22.1
0.22.0
0.21.0
0.20.2
0.20.1
0.20.0
0.19.3
0.19.2
retired
0.19.1
0.19.0
0.18.0
0.17.3
0.17.2
0.17.1
0.17.0
0.16.0
0.15.0
0.14.0
0.13.2
0.13.1
0.13.0
0.12.1
0.12.0
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.1
0.9.0
0.8.1
0.8.0
0.7.0
0.6.1
0.6.0
0.5.0
0.4.1
0.4.0
0.3.0
0.2.1
0.2.0
0.1.1
0.1.0
RTMP Plugin for Membrane Multimedia Framework
Current section
Files
Jump to
Current section
Files
c_src/membrane_rtmp_plugin/rtmp.c
#include "rtmp.h"
#include <stdbool.h>
void handle_init_state(State *);
UNIFEX_TERM native_create(UnifexEnv *env, char *url, char *timeout) {
State *s = unifex_alloc_state(env);
handle_init_state(s);
if (s->h264_bsf_ctx == NULL) {
unifex_release_state(env, s);
return unifex_raise(env, "Could not find filter h264_mp4toannexb");
}
UNIFEX_TERM ret;
AVDictionary *d = NULL;
av_dict_set(&d, "listen", "1", 0);
// 0 indicates that timeout should be infinity
if (strcmp(timeout, "0") != 0) {
av_dict_set(&d, "timeout", timeout, 0);
}
if (avformat_open_input(&s->input_ctx, url, NULL, &d) < 0) {
ret = native_create_result_error(
env, "Couldn't open input. This might be caused by invalid address, occupied port or connection timeout");
goto err;
}
if (avformat_find_stream_info(s->input_ctx, NULL) < 0) {
ret = native_create_result_error(env, "Couldn't get stream info");
goto err;
}
s->number_of_streams = s->input_ctx->nb_streams;
if(s->number_of_streams == 0) {
ret = native_create_result_error(env, "No streams found - at least one stream is required");
goto err;
}
for (int i = 0; i < s->number_of_streams; i++) {
AVStream *in_stream = s->input_ctx->streams[i];
AVCodecParameters *in_codecpar = in_stream->codecpar;
if (in_codecpar->codec_type != AVMEDIA_TYPE_AUDIO &&
in_codecpar->codec_type != AVMEDIA_TYPE_VIDEO) {
continue;
}
if (in_codecpar->codec_id != AV_CODEC_ID_H264 && in_codecpar->codec_id != AV_CODEC_ID_AAC) {
ret = native_create_result_error(env, "Unsupported codec. Only H264 and AAC are supported");
goto err;
}
}
av_bsf_init(s->h264_bsf_ctx);
return native_create_result_ok(env, s);
err:
unifex_release_state(env, s);
return ret;
}
UNIFEX_TERM get_audio_params(UnifexEnv* env, State* s) {
for(int i = 0; i < s->number_of_streams; i++) {
if(s->input_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
UnifexPayload payload;
unifex_payload_alloc(env, UNIFEX_PAYLOAD_BINARY, s->input_ctx->streams[i]->codecpar->extradata_size, &payload);
memcpy(payload.data, s->input_ctx->streams[i]->codecpar->extradata, s->input_ctx->streams[i]->codecpar->extradata_size);
UNIFEX_TERM result = get_audio_params_result_ok(env, &payload);
unifex_payload_release(&payload);
return result;
}
}
return get_audio_params_result_error(env);
}
UNIFEX_TERM get_video_params(UnifexEnv* env, State* s) {
for(int i = 0; i < s->number_of_streams; i++) {
if(s->input_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
UnifexPayload payload;
unifex_payload_alloc(env, UNIFEX_PAYLOAD_BINARY, s->input_ctx->streams[i]->codecpar->extradata_size, &payload);
memcpy(payload.data, s->input_ctx->streams[i]->codecpar->extradata, s->input_ctx->streams[i]->codecpar->extradata_size);
UNIFEX_TERM result = get_video_params_result_ok(env, &payload);
unifex_payload_release(&payload);
return result;
}
}
return get_video_params_result_error(env);
}
UNIFEX_TERM read_frame(UnifexEnv* env, State* s) {
AVPacket packet;
AVStream* in_stream;
enum AVMediaType codec_type;
UNIFEX_TERM result;
while(true) {
if(av_read_frame(s->input_ctx, &packet) < 0) {
result = read_frame_result_end_of_stream(env);
goto end;
}
if(packet.stream_index >= s->number_of_streams) {
result = read_frame_result_error(env, "Invalid stream index");
goto end;
}
in_stream = s->input_ctx->streams[packet.stream_index];
codec_type = in_stream->codecpar->codec_type;
if(codec_type != AVMEDIA_TYPE_AUDIO && codec_type != AVMEDIA_TYPE_VIDEO) {
av_packet_unref(&packet);
} else {
break;
}
}
UNIFEX_TERM (*result_func)(UnifexEnv*, UnifexPayload*) = NULL;
switch(codec_type) {
case AVMEDIA_TYPE_VIDEO:
av_bsf_send_packet(s->h264_bsf_ctx, &packet);
av_bsf_receive_packet(s->h264_bsf_ctx, &packet);
result_func = &read_frame_result_video;
break;
case AVMEDIA_TYPE_AUDIO:
result_func = &read_frame_result_audio;
break;
default:
return unifex_raise(env, "Unsupported frame type");
}
UnifexPayload payload;
unifex_payload_alloc(env, UNIFEX_PAYLOAD_BINARY, packet.size, &payload);
memcpy(payload.data, packet.data, packet.size);
av_packet_unref(&packet);
result = result_func(env, &payload);
unifex_payload_release(&payload);
end:
av_packet_unref(&packet);
return result;
}
void handle_init_state(State *s) {
s->input_ctx = avformat_alloc_context();
s->ready = false;
const AVBitStreamFilter *h264_filter = av_bsf_get_by_name("h264_mp4toannexb");
av_bsf_alloc(h264_filter, &s->h264_bsf_ctx);
}
void handle_destroy_state(UnifexEnv *env, State *s) {
UNIFEX_UNUSED(env);
if(s->h264_bsf_ctx) {
av_bsf_free(&s->h264_bsf_ctx);
}
if (s->input_ctx) {
avformat_close_input(&s->input_ctx);
}
}