|
|
@ -156,4 +156,97 @@ void StreamForwarder::processFrame(AVFrame* frame) {
|
|
|
|
frame->width, frame->height);
|
|
|
|
frame->width, frame->height);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool StreamForwarder::initialize(const std::string& input, const std::string& output) {
|
|
|
|
|
|
|
|
inputUrl = input;
|
|
|
|
|
|
|
|
outputUrl = output;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (avformat_open_input(&inputFormatContext, inputUrl.c_str(), nullptr, nullptr) < 0) {
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (avformat_find_stream_info(inputFormatContext, nullptr) < 0) {
|
|
|
|
|
|
|
|
cleanup();
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (avformat_alloc_output_context2(&outputFormatContext, nullptr, "mpegts",
|
|
|
|
|
|
|
|
outputUrl.c_str()) < 0) {
|
|
|
|
|
|
|
|
cleanup();
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool StreamForwarder::start()
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (!inputFormatContext || !outputFormatContext) {
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < inputFormatContext->nb_streams; i++) {
|
|
|
|
|
|
|
|
AVStream* inStream = inputFormatContext->streams[i];
|
|
|
|
|
|
|
|
AVStream* outStream = avformat_new_stream(outputFormatContext,
|
|
|
|
|
|
|
|
inStream->codec->codec);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!outStream) {
|
|
|
|
|
|
|
|
cleanup();
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (avcodec_parameters_copy(outStream->codecpar,
|
|
|
|
|
|
|
|
inStream->codecpar) < 0) {
|
|
|
|
|
|
|
|
cleanup();
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!(outputFormatContext->oformat->flags & AVFMT_NOFILE)) {
|
|
|
|
|
|
|
|
if (avio_open(&outputFormatContext->pb, outputUrl.c_str(),
|
|
|
|
|
|
|
|
AVIO_FLAG_WRITE) < 0) {
|
|
|
|
|
|
|
|
cleanup();
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (avformat_write_header(outputFormatContext, nullptr) < 0) {
|
|
|
|
|
|
|
|
cleanup();
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
AVPacket packet;
|
|
|
|
|
|
|
|
while (!stopRequested && av_read_frame(inputFormatContext, &packet) >= 0) {
|
|
|
|
|
|
|
|
av_write_frame(outputFormatContext, &packet);
|
|
|
|
|
|
|
|
av_packet_unref(&packet);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
av_write_trailer(outputFormatContext);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool StreamForwarder::stop()
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
stopRequested = true;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool StreamForwarder::isStreaming() const
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return !stopRequested;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void StreamForwarder::cleanup() {
|
|
|
|
|
|
|
|
if (inputFormatContext) {
|
|
|
|
|
|
|
|
avformat_close_input(&inputFormatContext);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (outputFormatContext) {
|
|
|
|
|
|
|
|
if (!(outputFormatContext->oformat->flags & AVFMT_NOFILE)) {
|
|
|
|
|
|
|
|
avio_closep(&outputFormatContext->pb);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
avformat_free_context(outputFormatContext);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|