mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 21:45:19 +00:00
228 lines
6.4 KiB
Objective-C
Executable File
228 lines
6.4 KiB
Objective-C
Executable File
#import <FFMpegBinding/FFMpegVideoWriter.h>
|
|
#import <FFMpegBinding/FFMpegAVFrame.h>
|
|
|
|
#include "libavformat/avformat.h"
|
|
#include "libavcodec/avcodec.h"
|
|
#include "libavutil/imgutils.h"
|
|
|
|
@interface FFMpegVideoWriter ()
|
|
|
|
@property (nonatomic) AVFormatContext *formatContext;
|
|
@property (nonatomic) AVCodecContext *codecContext;
|
|
@property (nonatomic) AVStream *stream;
|
|
@property (nonatomic) int64_t framePts;
|
|
|
|
@end
|
|
|
|
@implementation FFMpegVideoWriter
|
|
|
|
- (instancetype)init {
|
|
self = [super init];
|
|
if (self) {
|
|
_framePts = -1;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (bool)setupWithOutputPath:(NSString *)outputPath width:(int)width height:(int)height bitrate:(int64_t)bitrate framerate:(int32_t)framerate {
|
|
avformat_alloc_output_context2(&_formatContext, nil, "matroska", [outputPath UTF8String]);
|
|
if (!_formatContext) {
|
|
return false;
|
|
}
|
|
|
|
if (avio_open(&_formatContext->pb, [outputPath UTF8String], AVIO_FLAG_WRITE) < 0) {
|
|
avformat_free_context(_formatContext);
|
|
_formatContext = nil;
|
|
return false;
|
|
}
|
|
|
|
const AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_VP9);
|
|
if (!codec) {
|
|
avio_closep(&_formatContext->pb);
|
|
avformat_free_context(_formatContext);
|
|
_formatContext = nil;
|
|
return false;
|
|
}
|
|
|
|
_codecContext = avcodec_alloc_context3(codec);
|
|
if (!_codecContext) {
|
|
avio_closep(&_formatContext->pb);
|
|
avformat_free_context(_formatContext);
|
|
_formatContext = nil;
|
|
return false;
|
|
}
|
|
|
|
_codecContext->codec_id = AV_CODEC_ID_VP9;
|
|
_codecContext->codec_type = AVMEDIA_TYPE_VIDEO;
|
|
_codecContext->pix_fmt = AV_PIX_FMT_YUVA420P;
|
|
_codecContext->color_range = AVCOL_RANGE_MPEG;
|
|
_codecContext->color_primaries = AVCOL_PRI_BT709;
|
|
_codecContext->colorspace = AVCOL_SPC_BT709;
|
|
_codecContext->width = width;
|
|
_codecContext->height = height;
|
|
_codecContext->time_base = (AVRational){1, framerate};
|
|
_codecContext->framerate = (AVRational){framerate, 1};
|
|
_codecContext->bit_rate = bitrate;
|
|
|
|
if (_formatContext->oformat->flags & AVFMT_GLOBALHEADER) {
|
|
_codecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
|
|
}
|
|
|
|
if (avcodec_open2(_codecContext, codec, NULL) < 0) {
|
|
avcodec_free_context(&_codecContext);
|
|
_codecContext = nil;
|
|
avio_closep(&_formatContext->pb);
|
|
avformat_free_context(_formatContext);
|
|
_formatContext = nil;
|
|
return false;
|
|
}
|
|
|
|
_stream = avformat_new_stream(_formatContext, codec);
|
|
if (!_stream) {
|
|
#if LIBAVFORMAT_VERSION_MAJOR >= 59
|
|
#else
|
|
avcodec_close(_codecContext);
|
|
#endif
|
|
avcodec_free_context(&_codecContext);
|
|
_codecContext = nil;
|
|
avio_closep(&_formatContext->pb);
|
|
avformat_free_context(_formatContext);
|
|
_formatContext = nil;
|
|
return false;
|
|
}
|
|
|
|
_stream->codecpar->codec_id = _codecContext->codec_id;
|
|
_stream->codecpar->codec_type = _codecContext->codec_type;
|
|
_stream->codecpar->width = _codecContext->width;
|
|
_stream->codecpar->height = _codecContext->height;
|
|
_stream->codecpar->format = _codecContext->pix_fmt;
|
|
_stream->time_base = _codecContext->time_base;
|
|
|
|
int ret = avcodec_parameters_from_context(_stream->codecpar, _codecContext);
|
|
if (ret < 0) {
|
|
#if LIBAVFORMAT_VERSION_MAJOR >= 59
|
|
#else
|
|
avcodec_close(_codecContext);
|
|
#endif
|
|
avcodec_free_context(&_codecContext);
|
|
_codecContext = nil;
|
|
avio_closep(&_formatContext->pb);
|
|
avformat_free_context(_formatContext);
|
|
_formatContext = nil;
|
|
return false;
|
|
}
|
|
|
|
ret = avformat_write_header(_formatContext, nil);
|
|
if (ret < 0) {
|
|
#if LIBAVFORMAT_VERSION_MAJOR >= 59
|
|
#else
|
|
avcodec_close(_codecContext);
|
|
#endif
|
|
avcodec_free_context(&_codecContext);
|
|
_codecContext = nil;
|
|
avio_closep(&_formatContext->pb);
|
|
avformat_free_context(_formatContext);
|
|
_formatContext = nil;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
- (bool)encodeFrame:(FFMpegAVFrame *)frame {
|
|
if (!_codecContext || !_stream) {
|
|
return false;
|
|
}
|
|
|
|
self.framePts++;
|
|
|
|
AVFrame *frameImpl = (AVFrame *)[frame impl];
|
|
|
|
frameImpl->pts = self.framePts;
|
|
frameImpl->color_range = AVCOL_RANGE_MPEG;
|
|
frameImpl->color_primaries = AVCOL_PRI_BT709;
|
|
frameImpl->colorspace = AVCOL_SPC_BT709;
|
|
|
|
int sendRet = avcodec_send_frame(_codecContext, frameImpl);
|
|
if (sendRet < 0) {
|
|
return false;
|
|
}
|
|
|
|
AVPacket *pkt = nil;
|
|
pkt = av_packet_alloc();
|
|
pkt->data = nil;
|
|
pkt->size = 0;
|
|
|
|
while (sendRet >= 0) {
|
|
int recvRet = avcodec_receive_packet(_codecContext, pkt);
|
|
if (recvRet == AVERROR(EAGAIN) || recvRet == AVERROR_EOF) {
|
|
break;
|
|
} else if (recvRet < 0) {
|
|
av_packet_unref(pkt);
|
|
break;
|
|
}
|
|
|
|
av_packet_rescale_ts(pkt, _codecContext->time_base, _stream->time_base);
|
|
pkt->stream_index = _stream->index;
|
|
|
|
int ret = av_interleaved_write_frame(_formatContext, pkt);
|
|
av_packet_unref(pkt);
|
|
if (ret < 0) {
|
|
return false;
|
|
}
|
|
}
|
|
av_packet_free(&pkt);
|
|
|
|
return true;
|
|
}
|
|
|
|
- (bool)finalizeVideo {
|
|
if (!_codecContext) {
|
|
return false;
|
|
}
|
|
|
|
int sendRet = avcodec_send_frame(_codecContext, NULL);
|
|
if (sendRet >= 0) {
|
|
AVPacket *pkt = nil;
|
|
pkt = av_packet_alloc();
|
|
pkt->data = nil;
|
|
pkt->size = 0;
|
|
|
|
while (avcodec_receive_packet(_codecContext, pkt) == 0) {
|
|
av_packet_rescale_ts(pkt, _codecContext->time_base, _stream->time_base);
|
|
pkt->stream_index = _stream->index;
|
|
|
|
av_interleaved_write_frame(_formatContext, pkt);
|
|
av_packet_unref(pkt);
|
|
}
|
|
|
|
av_packet_free(&pkt);
|
|
}
|
|
|
|
if (_formatContext) {
|
|
av_write_trailer(_formatContext);
|
|
}
|
|
|
|
if (_formatContext && _formatContext->pb) {
|
|
avio_closep(&_formatContext->pb);
|
|
}
|
|
|
|
if (_codecContext) {
|
|
#if LIBAVFORMAT_VERSION_MAJOR >= 59
|
|
#else
|
|
avcodec_close(_codecContext);
|
|
#endif
|
|
avcodec_free_context(&_codecContext);
|
|
_codecContext = nil;
|
|
}
|
|
|
|
if (_formatContext) {
|
|
avformat_free_context(_formatContext);
|
|
_formatContext = nil;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
@end
|