parent
6ebced2d49
commit
5ea59231e7
@ -0,0 +1,104 @@ |
||||
//
|
||||
// Created by xu fulong on 2022/9/9.
|
||||
//
|
||||
|
||||
#include "ff_http_pusher.h" |
||||
|
||||
#define PUSH_TAG "HttpPusher" |
||||
|
||||
int FFHttpPusher::open(const char *inputPath, const char *outputPath) { |
||||
int ret; |
||||
|
||||
avformat_network_init(); |
||||
ret = avformat_open_input(&inFormatCtx, inputPath, nullptr, nullptr); |
||||
if (ret < 0) { |
||||
LOGE(PUSH_TAG, "avformat_open_input err=%s", av_err2str(ret)); |
||||
return ret; |
||||
} |
||||
avformat_find_stream_info(inFormatCtx, nullptr); |
||||
av_dump_format(inFormatCtx, 0, inputPath, 0); |
||||
ret = avformat_alloc_output_context2(&outFormatCtx, nullptr, "flv", outputPath); |
||||
if (ret < 0 || !outFormatCtx) { |
||||
LOGE(PUSH_TAG, "alloc format_context err=%s", av_err2str(ret)); |
||||
return ret; |
||||
} |
||||
|
||||
for (int i = 0; i < inFormatCtx->nb_streams; ++i) { |
||||
AVStream *in_stream = inFormatCtx->streams[i]; |
||||
const auto *codec = in_stream->codec->codec; |
||||
AVStream *out_stream = avformat_new_stream(outFormatCtx, codec); |
||||
avcodec_parameters_from_context(out_stream->codecpar, in_stream->codec); |
||||
if (outFormatCtx->oformat->flags & AVFMT_GLOBALHEADER) { |
||||
out_stream->codec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; |
||||
} |
||||
out_stream->codecpar->codec_tag = 0; |
||||
} |
||||
|
||||
if (!(outFormatCtx->oformat->flags & AVFMT_NOFILE)) { |
||||
ret = avio_open2(&outFormatCtx->pb, outputPath, AVIO_FLAG_WRITE, nullptr, nullptr); |
||||
if (ret < 0) { |
||||
LOGE(PUSH_TAG, "avio open error=%s", av_err2str(ret)); |
||||
return ret; |
||||
} |
||||
} |
||||
|
||||
ret = avformat_write_header(outFormatCtx, nullptr); |
||||
if (ret < 0) { |
||||
LOGE(PUSH_TAG, "avformat_write_header err=%s", av_err2str(ret)); |
||||
} |
||||
return ret; |
||||
} |
||||
|
||||
void rescale(AVFormatContext *in_format_ctx, AVFormatContext *out_format_ctx, AVPacket *packet) { |
||||
AVStream *in_stream = in_format_ctx->streams[packet->stream_index]; |
||||
AVStream *out_stream = out_format_ctx->streams[packet->stream_index]; |
||||
|
||||
packet->pts = av_rescale_q(packet->pts, in_stream->time_base, out_stream->time_base); |
||||
packet->dts = av_rescale_q(packet->dts, in_stream->time_base, out_stream->time_base); |
||||
packet->duration = av_rescale_q(packet->duration, in_stream->time_base, out_stream->time_base); |
||||
packet->pos = -1; |
||||
} |
||||
|
||||
int FFHttpPusher::push() { |
||||
int ret; |
||||
int64_t startTime = av_gettime(); |
||||
|
||||
while (true) { |
||||
ret = av_read_frame(inFormatCtx, &packet); |
||||
if (ret < 0) { |
||||
LOGE(PUSH_TAG, "av_read_frame err=%s", av_err2str(ret)); |
||||
break; |
||||
} |
||||
|
||||
AVRational time_base = inFormatCtx->streams[packet.stream_index]->time_base; |
||||
int64_t pts_time = av_rescale_q(packet.pts, time_base, AV_TIME_BASE_Q); |
||||
int64_t cur_time = av_gettime() - startTime; |
||||
if (pts_time > cur_time) { |
||||
av_usleep((unsigned int)(pts_time - cur_time)); |
||||
} |
||||
|
||||
rescale(inFormatCtx, outFormatCtx, &packet); |
||||
|
||||
ret = av_interleaved_write_frame(outFormatCtx, &packet); |
||||
if (ret < 0) { |
||||
LOGE(PUSH_TAG, "write frame err=%s", av_err2str(ret)); |
||||
break; |
||||
} |
||||
|
||||
av_packet_unref(&packet); |
||||
} |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
void FFHttpPusher::close() { |
||||
if (outFormatCtx) { |
||||
av_write_trailer(outFormatCtx); |
||||
avformat_close_input(&outFormatCtx); |
||||
outFormatCtx = nullptr; |
||||
} |
||||
if (inFormatCtx) { |
||||
avformat_close_input(&inFormatCtx); |
||||
inFormatCtx = nullptr; |
||||
} |
||||
} |
@ -0,0 +1,39 @@ |
||||
//
|
||||
// Created by xu fulong on 2022/9/9.
|
||||
//
|
||||
|
||||
#ifndef FF_HTTP_PUSHER_H |
||||
#define FF_HTTP_PUSHER_H |
||||
|
||||
#include "ffmpeg_jni_define.h" |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
#include "libavformat/avformat.h" |
||||
#include "libavcodec/avcodec.h" |
||||
#include "libavutil/time.h" |
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
class FFHttpPusher { |
||||
private: |
||||
AVFormatContext *inFormatCtx; |
||||
AVFormatContext *outFormatCtx; |
||||
|
||||
AVPacket packet; |
||||
int video_index = -1; |
||||
int audio_index = -1; |
||||
|
||||
public: |
||||
|
||||
int open(const char *inputPath, const char *outputPath); |
||||
|
||||
int push(); |
||||
|
||||
void close(); |
||||
|
||||
}; |
||||
|
||||
#endif //FF_HTTP_PUSHER_H
|
@ -1,148 +0,0 @@ |
||||
//
|
||||
// Created by frank on 2018/2/2.
|
||||
//
|
||||
|
||||
#include <jni.h> |
||||
#include <string> |
||||
#include "ffmpeg_jni_define.h" |
||||
|
||||
#define TAG "FFmpegPusher" |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
#include <libavcodec/avcodec.h> |
||||
#include <libavformat/avformat.h> |
||||
#include <libavfilter/avfilter.h> |
||||
#include <libavutil/time.h> |
||||
|
||||
PUSHER_FUNC(jint, pushStream, jstring filePath, jstring liveUrl) { |
||||
|
||||
int ret; |
||||
AVPacket packet; |
||||
int64_t start_time; |
||||
int video_index = -1; |
||||
int frame_index = 0; |
||||
const char *file_path; |
||||
const char *live_url; |
||||
AVFormatContext *in_format = nullptr; |
||||
AVFormatContext *out_format = nullptr; |
||||
|
||||
file_path = env->GetStringUTFChars(filePath, nullptr); |
||||
live_url = env->GetStringUTFChars(liveUrl, nullptr); |
||||
|
||||
avformat_network_init(); |
||||
if ((ret = avformat_open_input(&in_format, file_path, nullptr, nullptr)) < 0) { |
||||
LOGE(TAG, "could not open input file..."); |
||||
goto end; |
||||
} |
||||
if ((ret = avformat_find_stream_info(in_format, nullptr)) < 0) { |
||||
LOGE(TAG, "could not find stream info..."); |
||||
goto end; |
||||
} |
||||
for (int i = 0; i < in_format->nb_streams; i++) { |
||||
if (in_format->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { |
||||
video_index = i; |
||||
break; |
||||
} |
||||
} |
||||
av_dump_format(in_format, 0, file_path, 0); |
||||
avformat_alloc_output_context2(&out_format, nullptr, "flv", live_url); |
||||
if (!out_format) { |
||||
LOGE(TAG, "could not alloc output context..."); |
||||
ret = AVERROR_UNKNOWN; |
||||
goto end; |
||||
} |
||||
//Create output stream according to input stream
|
||||
for (int i = 0; i < in_format->nb_streams; i++) { |
||||
AVStream *in_stream = in_format->streams[i]; |
||||
AVStream *out_stream = avformat_new_stream(out_format, in_stream->codec->codec); |
||||
if (!out_stream) { |
||||
LOGE(TAG, "could not alloc output stream..."); |
||||
ret = AVERROR_UNKNOWN; |
||||
goto end; |
||||
} |
||||
//Copy context
|
||||
ret = avcodec_parameters_from_context(out_stream->codecpar, in_stream->codec); |
||||
if (ret < 0) { |
||||
LOGE(TAG, "could not copy context..."); |
||||
goto end; |
||||
} |
||||
out_stream->codecpar->codec_tag = 0; |
||||
if (out_format->oformat->flags & AVFMT_GLOBALHEADER) { |
||||
out_stream->codec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; |
||||
} |
||||
} |
||||
|
||||
if (!(out_format->oformat->flags & AVFMT_NOFILE)) { |
||||
ret = avio_open(&out_format->pb, live_url, AVIO_FLAG_WRITE); |
||||
if (ret < 0) { |
||||
LOGE(TAG, "could not open output url '%s'", live_url); |
||||
goto end; |
||||
} |
||||
} |
||||
//Write header
|
||||
ret = avformat_write_header(out_format, nullptr); |
||||
if (ret < 0) { |
||||
LOGE(TAG, "could not write header..."); |
||||
goto end; |
||||
} |
||||
start_time = av_gettime(); |
||||
while (true) { |
||||
AVStream *in_stream, *out_stream; |
||||
ret = av_read_frame(in_format, &packet); |
||||
if (ret < 0) { |
||||
break; |
||||
} |
||||
if (packet.stream_index == video_index) { |
||||
AVRational time_base = in_format->streams[video_index]->time_base; |
||||
AVRational time_base_q = {1, AV_TIME_BASE}; |
||||
int64_t pts_time = av_rescale_q(packet.dts, time_base, time_base_q); |
||||
int64_t now_time = av_gettime() - start_time; |
||||
//sleep to keep waiting
|
||||
if (pts_time > now_time) { |
||||
av_usleep((unsigned int) (pts_time - now_time)); |
||||
} |
||||
} |
||||
in_stream = in_format->streams[packet.stream_index]; |
||||
out_stream = out_format->streams[packet.stream_index]; |
||||
|
||||
//calculate: pts dts duration
|
||||
packet.pts = av_rescale_q_rnd(packet.pts, in_stream->time_base, out_stream->time_base, |
||||
(AVRounding) (AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)); |
||||
packet.dts = av_rescale_q_rnd(packet.dts, in_stream->time_base, out_stream->time_base, |
||||
(AVRounding) (AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)); |
||||
packet.duration = av_rescale_q(packet.duration, in_stream->time_base, |
||||
out_stream->time_base); |
||||
packet.pos = -1; |
||||
|
||||
if (packet.stream_index == video_index) { |
||||
frame_index++; |
||||
LOGI(TAG, "write frame = %d", frame_index); |
||||
} |
||||
//Write a frame
|
||||
ret = av_interleaved_write_frame(out_format, &packet); |
||||
if (ret < 0) { |
||||
LOGE(TAG, "could not write frame..."); |
||||
break; |
||||
} |
||||
av_packet_unref(&packet); |
||||
} |
||||
//Write tail
|
||||
av_write_trailer(out_format); |
||||
|
||||
end: |
||||
if (out_format && !(out_format->flags & AVFMT_NOFILE)) { |
||||
avio_close(out_format->pb); |
||||
} |
||||
avformat_free_context(out_format); |
||||
avformat_close_input(&in_format); |
||||
env->ReleaseStringUTFChars(filePath, file_path); |
||||
env->ReleaseStringUTFChars(liveUrl, live_url); |
||||
|
||||
return (ret < 0 && ret != AVERROR_EOF) ? -1 : 0; |
||||
} |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
@ -0,0 +1,25 @@ |
||||
//
|
||||
// Created by xu fulong on 2022/9/9.
|
||||
//
|
||||
|
||||
#include <jni.h> |
||||
#include "ff_http_pusher.h" |
||||
|
||||
PUSHER_FUNC(int, pushStream, jstring inputPath, jstring outputPath) { |
||||
int ret; |
||||
const char *input_path = env->GetStringUTFChars(inputPath, JNI_FALSE); |
||||
const char *output_path = env->GetStringUTFChars(outputPath, JNI_FALSE); |
||||
auto *httpPusher = new FFHttpPusher(); |
||||
ret = httpPusher->open(input_path, output_path); |
||||
if (ret < 0) { |
||||
LOGE("HttpPusher", "open error=%d", ret); |
||||
return ret; |
||||
} |
||||
ret = httpPusher->push(); |
||||
|
||||
httpPusher->close(); |
||||
env->ReleaseStringUTFChars(inputPath, input_path); |
||||
env->ReleaseStringUTFChars(outputPath, output_path); |
||||
|
||||
return ret; |
||||
} |
Loading…
Reference in new issue