ffmpeg学习(12)音视频转码(1)使用sws、swr
at 2年前 ca FFmpeg pv 1437 by touch
ffmpeg学习(10)音视频文件muxer(1)封装格式转换中介绍了媒体文件的封装格式转换,ffmpeg学习(11)音视频文件muxer(2)多输入混流 中介绍了音视频的混流,本文介绍基于ffmpeg的转码,将一种视频格式(编码格式、封装格式)转换为另一种视频格式,该过程先解码、再编码,以下图为例说明转码流程。

输入视频的封装格式是MP4,视频编码标准是H.264,音频编码标准是AAC;输出视频的封装格式是AVI,视频编码标准是MPEG4,音频编码标准是MP3。首先从输入视频中分离出视频码流和音频压缩码流,然后分别将视频码流和音频码流进行解码,获取到非压缩的像素数据/音频采样数据,接着将非压缩的像素数据/音频采样数据重新进行编码,获得重新编码后的视频码流和音频码流,最后将视频码流和音频码流重新封装成一个文件。
在转码过程中,解码后的非压缩的像素数据/音频采样数据可能需要进行图像变换/重采样才能再送入编码器中进行编码。按如下两种方式分文介绍:
1、使用sws_scale()、swr_convert()函数
先对解码后的非压缩数据先进行转换,再进行编码。转换功能单一,结构流程简单,但是转换代码复杂。
2、使用AVFilterGraph
可根据输入输出的要求创建一个AVFilterGraph,可以实现复杂功能。对解码的每一帧数据进行filter,在将处理的结果进行编码。新加入的AVFilterGraph创建初始化复杂,但是其使用方式简单。
本文先介绍第一种方式,流程图如下

解码再解码流程
/*
转码,非压缩数据转换使用sws_scale()、swr_convert()函数
*/
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
#include "libavutil/opt.h"
#ifdef __cplusplus
}
#endif
// 输入、输出封装上下文
static AVFormatContext *ifmt_ctx;
static AVFormatContext *ofmt_ctx;
// 编解码器上下文对,便于参数传递
typedef struct StreamContext {
AVCodecContext *dec_ctx;
AVCodecContext *enc_ctx;
} StreamContext;
static StreamContext *stream_ctx; // 数组指针,元素个数对应流个数
static int open_input_file(const char *filename)
{
int ret;
ifmt_ctx = NULL;
if((ret = avformat_open_input(&ifmt_ctx, filename, NULL, NULL)) < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot open input file %s\n", filename);
return ret;
}
if((ret = avformat_find_stream_info(ifmt_ctx, NULL)) < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot find stream information\n");
return ret;
}
stream_ctx = (StreamContext *)av_mallocz_array(ifmt_ctx->nb_streams, sizeof(StreamContext));
if(!stream_ctx) {
return AVERROR(ENOMEM);
}
for(unsigned int i = 0; i < ifmt_ctx->nb_streams; i++) {
AVStream *stream = ifmt_ctx->streams[i];
AVCodec *dec = avcodec_find_decoder(stream->codecpar->codec_id);
if(!dec) {
av_log(NULL, AV_LOG_ERROR, "Failed to find decoder for stream #%u\n", i);
return AVERROR_DECODER_NOT_FOUND;
}
AVCodecContext *codec_ctx = avcodec_alloc_context3(dec);
if(!codec_ctx) {
av_log(NULL, AV_LOG_ERROR, "Failed to allocate the decoder context for stream #%u\n", i);
return AVERROR(ENOMEM);
}
ret = avcodec_parameters_to_context(codec_ctx, stream->codecpar);
if(ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Failed to copy decoder parameters to input decoder context "
"for stream #%u\n", i);
return ret;
}
/* Reencode video & audio and remux subtitles etc. */
if(codec_ctx->codec_type == AVMEDIA_TYPE_VIDEO || codec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {
if(codec_ctx->codec_type == AVMEDIA_TYPE_VIDEO)
codec_ctx->framerate = av_guess_frame_rate(ifmt_ctx, stream, NULL);
/* Open decoder */
if((ret = avcodec_open2(codec_ctx, dec, NULL)) < 0) {
av_log(NULL, AV_LOG_ERROR, "Failed to open decoder for stream #%u\n", i);
return ret;
}
// 去除警告 Could not update timestamps for skipped samples
codec_ctx->pkt_timebase = stream->time_base;
stream_ctx[i].dec_ctx = codec_ctx; // 只处理音视频
}
//stream_ctx[i].dec_ctx = codec_ctx;
}
av_dump_format(ifmt_ctx, 0, filename, 0);
return 0;
}
static int open_output_file(const char *filename)
{
int ret;
ofmt_ctx = NULL;
avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, filename);
if(!ofmt_ctx) {
av_log(NULL, AV_LOG_ERROR, "Could not create output context\n");
return AVERROR_UNKNOWN;
}
for(unsigned int i = 0; i < ifmt_ctx->nb_streams; i++) {
AVStream *in_stream = ifmt_ctx->streams[i];
AVStream *out_stream = avformat_new_stream(ofmt_ctx, NULL);
if(!out_stream) {
av_log(NULL, AV_LOG_ERROR, "Failed allocating output stream\n");
return AVERROR_UNKNOWN;
}
AVCodecContext *dec_ctx = stream_ctx[i].dec_ctx;
if(dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO || dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {
AVCodec *encoder = avcodec_find_encoder(dec_ctx->codec_id);
if(!encoder) {
av_log(NULL, AV_LOG_FATAL, "Necessary encoder not found\n");
return AVERROR_INVALIDDATA;
}
AVCodecContext *enc_ctx = avcodec_alloc_context3(encoder);
if(!enc_ctx) {
av_log(NULL, AV_LOG_FATAL, "Failed to allocate the encoder context\n");
return AVERROR(ENOMEM);
}
/* encoder parameters */
if(dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO) {
/* 输入输出参数一致 */
enc_ctx->height = dec_ctx->height;
enc_ctx->width = dec_ctx->width;
enc_ctx->sample_aspect_ratio = dec_ctx->sample_aspect_ratio;
/* take first format from list of supported formats */
enc_ctx->pix_fmt = encoder->sample_fmts ? encoder->pix_fmts[0] : dec_ctx->pix_fmt;
/* video time_base can be set to whatever is handy and supported by encoder */
enc_ctx->time_base = av_inv_q(dec_ctx->framerate);
}
else{ // dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO
/* 输入输出参数一致 */
enc_ctx->sample_rate = dec_ctx->sample_rate;
enc_ctx->channel_layout = dec_ctx->channel_layout;
enc_ctx->channels = av_get_channel_layout_nb_channels(enc_ctx->channel_layout);
/* take first format from list of supported formats */
enc_ctx->sample_fmt = encoder->sample_fmts ? encoder->sample_fmts[0] : dec_ctx->sample_fmt;
enc_ctx->time_base = AVRational{1, enc_ctx->sample_rate};
}
/* Third parameter can be used to pass settings to encoder */
if((ret = avcodec_open2(enc_ctx, encoder, NULL)) < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot open video encoder for stream #%u\n", i);
return ret;
}
if((ret = avcodec_parameters_from_context(out_stream->codecpar, enc_ctx)) < 0) {
av_log(NULL, AV_LOG_ERROR, "Failed to copy encoder parameters to output stream #%u\n", i);
return ret;
}
if(ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
enc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
out_stream->time_base = enc_ctx->time_base;
//out_stream->codecpar->codec_tag = 0;
stream_ctx[i].enc_ctx = enc_ctx; // 只处理音视频
}
}
av_dump_format(ofmt_ctx, 0, filename, 1);
if(!(ofmt_ctx->oformat->flags & AVFMT_NOFILE)) {
if((ret = avio_open(&ofmt_ctx->pb, filename, AVIO_FLAG_WRITE)) < 0) {
av_log(NULL, AV_LOG_ERROR, "Could not open output file '%s'", filename);
return ret;
}
}
/* init muxer, write output file header */
if((ret = avformat_write_header(ofmt_ctx, NULL)) < 0) {
av_log(NULL, AV_LOG_ERROR, "Error occurred when opening output file\n");
return ret;
}
return 0;
}
static int encode_write_frame(AVFrame *frame, unsigned int stream_index)
{
AVPacket *enc_pkt = av_packet_alloc();
int ret = avcodec_send_frame(stream_ctx[stream_index].enc_ctx, frame);
while(ret >= 0) {
ret = avcodec_receive_packet(stream_ctx[stream_index].enc_ctx, enc_pkt);
if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
return ret;
}
else if(ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Error during encoding\n");
exit(1);
}
// prepare packet for muxing
enc_pkt->stream_index = stream_index;
av_packet_rescale_ts(enc_pkt,
ifmt_ctx->streams[stream_index]->time_base,
ofmt_ctx->streams[stream_index]->time_base);
// mux encoded frame
ret = av_interleaved_write_frame(ofmt_ctx, enc_pkt);
av_packet_unref(enc_pkt);
}
av_packet_free(&enc_pkt);
return ret;
}
int main()
{
const char* input_file = "../files/Titanic.mp4";
const char* output_file = "Titanic.flv";
int ret;
if((ret = open_input_file(input_file)) < 0) {
goto end;
}
if((ret = open_output_file(output_file)) < 0) {
goto end;
}
AVPacket *pkt = av_packet_alloc();
AVFrame *frame = av_frame_alloc();
unsigned int stream_index;
while(1)
{
if((ret = av_read_frame(ifmt_ctx, pkt)) < 0)
break;
stream_index = pkt->stream_index;
// 解码
ret = avcodec_send_packet(stream_ctx[stream_index].dec_ctx, pkt);
if(pkt->pts == AV_NOPTS_VALUE) {
printf("========\n");
}
while(ret >= 0) {
ret = avcodec_receive_frame(stream_ctx[stream_index].dec_ctx, frame);
if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
}
else if(ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Error while receiving a frame the decoder\n");
goto end;
}
// 编码、保存
encode_write_frame(frame, stream_index);
av_frame_unref(frame);
}
av_packet_unref(pkt);
}
// flush
for(unsigned int i = 0; i < ifmt_ctx->nb_streams; i++) {
// flush decoder
ret = avcodec_send_packet(stream_ctx[i].dec_ctx, NULL);
while(ret >= 0) {
ret = avcodec_receive_frame(stream_ctx[i].dec_ctx, frame);
if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
}
else if(ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Error while receiving a frame the decoder\n");
goto end;
}
// 编码、保存
encode_write_frame(frame, i);
av_frame_unref(frame);
}
// flush encoder
encode_write_frame(NULL, i);
}
av_write_trailer(ofmt_ctx);
end:
av_packet_free(&pkt);
av_frame_free(&frame);
for(unsigned int i = 0; i < ifmt_ctx->nb_streams; i++) {
avcodec_free_context(&stream_ctx[i].dec_ctx);
if(ofmt_ctx && ofmt_ctx->nb_streams > i && ofmt_ctx->streams[i] && stream_ctx[i].enc_ctx)
avcodec_free_context(&stream_ctx[i].enc_ctx);
}
avformat_close_input(&ifmt_ctx);
if(ofmt_ctx && !(ofmt_ctx->oformat->flags & AVFMT_NOFILE))
avio_closep(&ofmt_ctx->pb);
avformat_free_context(ofmt_ctx);
return 0;
}注意以下几点:
1、sws_scale()、swr_convert()函数转换得到的AVFrame的pts是没有的,需要从输入的pts赋值得到。
2、由于延时需要的待转换输入采样数据会增加,重新分配内存空间以保存需要采样数据量。
3、音频采样数据使用swr_convert()函数转换后,写入到输出的采样数据的个数应该为其返回值,即要处理 frame_tmp->nb_samples = ret;,并且写完之后要恢复其实际分配采样数据个数,避免反复分配释放空间。
输入、输出的结果截图如下:

4、视频编码参数可以直接指定输出分辨率为输入的一半
如
/* 输入输出参数一致 */ enc_ctx->height = dec_ctx->height / 2; enc_ctx->width = dec_ctx->width / 2;

5、提示Could not update timestamps for skipped samples错误信息
当前错误提示不影响结果输出,解码器参数设置 codec_ctx->pkt_timebase = stream->time_base; 可以解决。
解码再解码(参数变化)流程
音频修改采样频率,能够保证输出音频的时长与输入时长一致。但是,对于视频帧数固定的输入,仅调整帧率,输出视频帧数不一定保持不变。这个问题可能导致同步处理会有问题因此这里仅使用sws_scale()和swr_convert()转码,不涉及视频帧率、采样频率的调整。
这里的转码,修改视频编码为为H265,输出分辨率减半;音频调整为MP3编码(frame size从AAC 的1024 调整到1152)。
/*
转码,非压缩数据转换使用sws_scale()、swr_convert()函数
*/
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
#include "libavutil/opt.h"
#ifdef __cplusplus
}
#endif
// 输入、输出封装上下文
static AVFormatContext *ifmt_ctx;
static AVFormatContext *ofmt_ctx;
// 编解码器上下文对,便于参数传递
typedef struct StreamContext {
AVCodecContext *dec_ctx;
AVCodecContext *enc_ctx;
SwsContext *sws_ctx;
AVFrame *frame_v;
SwrContext *swr_ctx;
AVFrame *frame_a;
} StreamContext;
static StreamContext *stream_ctx; // 数组指针,元素个数对应流个数
static int open_input_file(const char *filename)
{
int ret;
ifmt_ctx = NULL;
if((ret = avformat_open_input(&ifmt_ctx, filename, NULL, NULL)) < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot open input file %s\n", filename);
return ret;
}
if((ret = avformat_find_stream_info(ifmt_ctx, NULL)) < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot find stream information\n");
return ret;
}
stream_ctx = (StreamContext *)av_mallocz_array(ifmt_ctx->nb_streams, sizeof(StreamContext));
if(!stream_ctx) {
return AVERROR(ENOMEM);
}
for(unsigned int i = 0; i < ifmt_ctx->nb_streams; i++) {
AVStream *stream = ifmt_ctx->streams[i];
AVCodec *dec = avcodec_find_decoder(stream->codecpar->codec_id);
if(!dec) {
av_log(NULL, AV_LOG_ERROR, "Failed to find decoder for stream #%u\n", i);
return AVERROR_DECODER_NOT_FOUND;
}
AVCodecContext *codec_ctx = avcodec_alloc_context3(dec);
if(!codec_ctx) {
av_log(NULL, AV_LOG_ERROR, "Failed to allocate the decoder context for stream #%u\n", i);
return AVERROR(ENOMEM);
}
ret = avcodec_parameters_to_context(codec_ctx, stream->codecpar);
if(ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Failed to copy decoder parameters to input decoder context "
"for stream #%u\n", i);
return ret;
}
/* Reencode video & audio and remux subtitles etc. */
if(codec_ctx->codec_type == AVMEDIA_TYPE_VIDEO || codec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {
if(codec_ctx->codec_type == AVMEDIA_TYPE_VIDEO)
codec_ctx->framerate = av_guess_frame_rate(ifmt_ctx, stream, NULL);
/* Open decoder */
if((ret = avcodec_open2(codec_ctx, dec, NULL)) < 0) {
av_log(NULL, AV_LOG_ERROR, "Failed to open decoder for stream #%u\n", i);
return ret;
}
// 去除警告 Could not update timestamps for skipped samples
codec_ctx->pkt_timebase = stream->time_base;
}
stream_ctx[i].dec_ctx = codec_ctx;
}
av_dump_format(ifmt_ctx, 0, filename, 0);
return 0;
}
static int open_output_file(const char *filename)
{
int ret;
ofmt_ctx = NULL;
avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, filename);
if(!ofmt_ctx) {
av_log(NULL, AV_LOG_ERROR, "Could not create output context\n");
return AVERROR_UNKNOWN;
}
for(unsigned int i = 0; i < ifmt_ctx->nb_streams; i++) {
AVStream *in_stream = ifmt_ctx->streams[i];
AVStream *out_stream = avformat_new_stream(ofmt_ctx, NULL);
if(!out_stream) {
av_log(NULL, AV_LOG_ERROR, "Failed allocating output stream\n");
return AVERROR_UNKNOWN;
}
AVCodecContext *dec_ctx = stream_ctx[i].dec_ctx;
AVCodec *encoder = NULL;
AVCodecContext *enc_ctx = NULL;
if(dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO || dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {
/* encoder parameters */
if(dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO) {
//encoder = avcodec_find_encoder(dec_ctx->codec_id);
AVCodecID enc_codec_id = AV_CODEC_ID_HEVC;
encoder = avcodec_find_encoder(enc_codec_id);
if(!encoder) {
av_log(NULL, AV_LOG_FATAL, "Necessary encoder not found\n");
return AVERROR_INVALIDDATA;
}
enc_ctx = avcodec_alloc_context3(encoder);
if(!enc_ctx) {
av_log(NULL, AV_LOG_FATAL, "Failed to allocate the encoder context\n");
return AVERROR(ENOMEM);
}
/* 输入输出参数一致 */
//enc_ctx->height = dec_ctx->height;
//enc_ctx->width = dec_ctx->width;
//enc_ctx->sample_aspect_ratio = dec_ctx->sample_aspect_ratio;
///* take first format from list of supported formats */
//enc_ctx->pix_fmt = encoder->sample_fmts ? encoder->pix_fmts[0] : dec_ctx->pix_fmt;
///* video time_base can be set to whatever is handy and supported by encoder */
//enc_ctx->time_base = av_inv_q(dec_ctx->framerate);
/* 输入输出参数不一致 */
enc_ctx->height = dec_ctx->height / 2;
enc_ctx->width = dec_ctx->width / 2;
//enc_ctx->sample_aspect_ratio = dec_ctx->sample_aspect_ratio;
enc_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
enc_ctx->time_base = av_inv_q(dec_ctx->framerate);
修改帧率,视频文件长短发生变化(或者音视频不能对应)
enc_ctx->framerate = {15,1};
//enc_ctx->time_base = {1,15};
stream_ctx[i].sws_ctx = sws_getContext(dec_ctx->width, dec_ctx->height, dec_ctx->pix_fmt,
enc_ctx->width, enc_ctx->height, enc_ctx->pix_fmt,
SWS_BILINEAR, NULL, NULL, NULL);
stream_ctx[i].frame_v = av_frame_alloc();
stream_ctx[i].frame_v->format = enc_ctx->pix_fmt;
stream_ctx[i].frame_v->height = enc_ctx->height;
stream_ctx[i].frame_v->width = enc_ctx->width;
av_frame_get_buffer(stream_ctx[i].frame_v, 0);
}
else if(dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {
//encoder = avcodec_find_encoder(dec_ctx->codec_id);
AVCodecID enc_codec_id = AV_CODEC_ID_MP3;
encoder = avcodec_find_encoder(enc_codec_id);
if(!encoder) {
av_log(NULL, AV_LOG_FATAL, "Necessary encoder not found\n");
return AVERROR_INVALIDDATA;
}
enc_ctx = avcodec_alloc_context3(encoder);
if(!enc_ctx) {
av_log(NULL, AV_LOG_FATAL, "Failed to allocate the encoder context\n");
return AVERROR(ENOMEM);
}
/* 输入输出参数一致 */
//enc_ctx->sample_rate = dec_ctx->sample_rate;
//enc_ctx->channel_layout = dec_ctx->channel_layout;
//enc_ctx->channels = av_get_channel_layout_nb_channels(enc_ctx->channel_layout);
///* take first format from list of supported formats */
//enc_ctx->sample_fmt = encoder->sample_fmts ? encoder->sample_fmts[0] : dec_ctx->sample_fmt;
//enc_ctx->time_base = AVRational{1, enc_ctx->sample_rate};
/* 输入输出参数不一致 */
enc_ctx->sample_rate = dec_ctx->sample_rate; //44100; 修改帧率,视频文件长短发生变化(或者音视频不能对应)
enc_ctx->channel_layout = AV_CH_LAYOUT_STEREO;
enc_ctx->channels = av_get_channel_layout_nb_channels(enc_ctx->channel_layout);
enc_ctx->sample_fmt = AV_SAMPLE_FMT_S16P;
enc_ctx->time_base = AVRational{1, enc_ctx->sample_rate};
stream_ctx[i].swr_ctx = swr_alloc_set_opts(stream_ctx[i].swr_ctx,
enc_ctx->channel_layout, enc_ctx->sample_fmt, enc_ctx->sample_rate,
dec_ctx->channel_layout, dec_ctx->sample_fmt, dec_ctx->sample_rate, 0, NULL);
swr_init(stream_ctx[i].swr_ctx);
stream_ctx[i].frame_a = av_frame_alloc();
stream_ctx[i].frame_a->sample_rate = enc_ctx->sample_rate;
stream_ctx[i].frame_a->format = enc_ctx->sample_fmt;
stream_ctx[i].frame_a->channel_layout = enc_ctx->channel_layout;
stream_ctx[i].frame_a->nb_samples = av_rescale_rnd(dec_ctx->frame_size, enc_ctx->sample_rate, dec_ctx->sample_rate, AV_ROUND_UP);
av_frame_get_buffer(stream_ctx[i].frame_a, 0);
}
/* Third parameter can be used to pass settings to encoder */
if((ret = avcodec_open2(enc_ctx, encoder, NULL)) < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot open video encoder for stream #%u\n", i);
return ret;
}
if((ret = avcodec_parameters_from_context(out_stream->codecpar, enc_ctx)) < 0) {
av_log(NULL, AV_LOG_ERROR, "Failed to copy encoder parameters to output stream #%u\n", i);
return ret;
}
if(ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
enc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
out_stream->time_base = enc_ctx->time_base;
out_stream->codecpar->codec_tag = 0;
stream_ctx[i].enc_ctx = enc_ctx;
}
else if(dec_ctx->codec_type == AVMEDIA_TYPE_UNKNOWN) {
av_log(NULL, AV_LOG_FATAL, "Elementary stream #%d is of unknown type, cannot proceed\n", i);
return AVERROR_INVALIDDATA;
}
else {
/* if this stream must be remuxed */
ret = avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar);
if(ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Copying parameters for stream #%u failed\n", i);
return ret;
}
out_stream->time_base = in_stream->time_base;
}
}
av_dump_format(ofmt_ctx, 0, filename, 1);
if(!(ofmt_ctx->oformat->flags & AVFMT_NOFILE)) {
if((ret = avio_open(&ofmt_ctx->pb, filename, AVIO_FLAG_WRITE)) < 0) {
av_log(NULL, AV_LOG_ERROR, "Could not open output file '%s'", filename);
return ret;
}
}
/* init muxer, write output file header */
if((ret = avformat_write_header(ofmt_ctx, NULL)) < 0) {
av_log(NULL, AV_LOG_ERROR, "Error occurred when opening output file\n");
return ret;
}
return 0;
}
static int encode_write_frame(AVFrame *frame, unsigned int stream_index)
{
AVPacket *enc_pkt = av_packet_alloc();
int ret = avcodec_send_frame(stream_ctx[stream_index].enc_ctx, frame);
while(ret >= 0) {
ret = avcodec_receive_packet(stream_ctx[stream_index].enc_ctx, enc_pkt);
if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
return ret;
}
else if(ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Error during encoding\n");
exit(1);
}
// prepare packet for muxing
enc_pkt->stream_index = stream_index;
av_packet_rescale_ts(enc_pkt,
stream_ctx[stream_index].enc_ctx->time_base,
ofmt_ctx->streams[stream_index]->time_base);
printf(" pts %lld, dts %lld, duration %lld \n", enc_pkt->pts, enc_pkt->dts, enc_pkt->duration);
// mux encoded frame
ret = av_interleaved_write_frame(ofmt_ctx, enc_pkt);
av_packet_unref(enc_pkt);
}
av_packet_free(&enc_pkt);
return ret;
}
static int trans_encode_write_frame(AVFrame *frame, unsigned int stream_index)
{
int ret = 0;
if(stream_ctx[stream_index].enc_ctx->codec_type == AVMEDIA_TYPE_VIDEO) {
AVFrame *frame_tmp = stream_ctx[stream_index].frame_v;
ret = sws_scale(stream_ctx[stream_index].sws_ctx,
frame->data, frame->linesize, 0, frame->height,
frame_tmp->data, frame_tmp->linesize);
if(ret < 0)
return ret;
frame_tmp->pts = frame->pts;
//frame_tmp->pkt_pts = frame->pkt_pts;
//frame_tmp->pkt_dts = frame->pkt_dts;
//frame_tmp->pkt_duration = frame->pkt_duration;
ret = encode_write_frame(frame_tmp, stream_index);
}
else if(stream_ctx[stream_index].enc_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {
AVFrame *frame_tmp = stream_ctx[stream_index].frame_a;
int dst_nb_samples = av_rescale_rnd(
swr_get_delay(stream_ctx[stream_index].swr_ctx, frame->sample_rate) + frame->nb_samples,
frame_tmp->sample_rate,
frame->sample_rate, AV_ROUND_UP);
if(dst_nb_samples > frame_tmp->nb_samples) {
int format = frame_tmp->format;
int chn_layout = frame_tmp->channel_layout;
int sample_rate = frame_tmp->sample_rate;
av_frame_unref(frame_tmp);
frame_tmp->format = format;
frame_tmp->channel_layout = chn_layout;
frame_tmp->nb_samples = dst_nb_samples; // 更新
frame_tmp->sample_rate = sample_rate;
av_frame_get_buffer(frame_tmp, 0);
printf("frame resize %d\n", frame_tmp->nb_samples);
}
//ret = swr_convert_frame(stream_ctx[stream_index].swr_ctx, frame_tmp, frame);
ret = swr_convert(stream_ctx[stream_index].swr_ctx,
(uint8_t **)&frame_tmp->data[0], frame_tmp->nb_samples,
(const uint8_t **)&frame->data[0], frame->nb_samples);
if(ret < 0) {
return ret;
}
frame_tmp->pts = frame->pts;
//frame_tmp->pkt_pts = frame->pkt_pts;
//frame_tmp->pkt_dts = frame->pkt_dts;
//frame_tmp->pkt_duration = frame->pkt_duration;
int tmp_nb_samples = frame_tmp->nb_samples;
frame_tmp->nb_samples = ret; // 重要,实际转码的采样数据个数
ret = encode_write_frame(frame_tmp, stream_index);
frame_tmp->nb_samples = tmp_nb_samples; // 修改回原值,避免反复分配释放空间
}
return ret;
}
int main()
{
const char* input_file = "../files/Titanic.mp4";
const char* output_file = "Titanic.mp4";
int ret;
if((ret = open_input_file(input_file)) < 0) {
goto end;
}
if((ret = open_output_file(output_file)) < 0) {
goto end;
}
AVPacket *pkt = av_packet_alloc();
AVFrame *frame = av_frame_alloc();
unsigned int stream_index;
AVMediaType media_type;
uint64_t frame_idx = 0;
while(1) {
if((ret = av_read_frame(ifmt_ctx, pkt)) < 0)
break;
stream_index = pkt->stream_index;
media_type = ifmt_ctx->streams[stream_index]->codecpar->codec_type;
av_packet_rescale_ts(pkt,
ifmt_ctx->streams[stream_index]->time_base,
stream_ctx[stream_index].dec_ctx->time_base);
// 解码
ret = avcodec_send_packet(stream_ctx[stream_index].dec_ctx, pkt);
if(pkt->pts == AV_NOPTS_VALUE) {
printf("========\n");
}
while(ret >= 0) {
ret = avcodec_receive_frame(stream_ctx[stream_index].dec_ctx, frame);
if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
}
else if(ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Error while receiving a frame the decoder\n");
goto end;
}
if(media_type == AVMEDIA_TYPE_VIDEO) {
frame_idx++;
// printf("frame index %5lld: pts %lld, pkt_pts %lld, pkt_dts %lld, pkt_duration %lld \n", frame_idx, frame->pts, frame->pkt_pts, frame->pkt_dts, frame->pkt_duration);
frame->pts = frame_idx;
//frame->pkt_pts = frame->pts;
//frame->pkt_dts = frame->pts;
//frame->pkt_duration = stream_ctx[stream_index].dec_ctx->ticks_per_frame;
}
// 编码、保存
//encode_write_frame(frame, stream_index);
// 转换,编码、保存
trans_encode_write_frame(frame, stream_index);
av_frame_unref(frame);
}
av_packet_unref(pkt);
}
// flush
for(unsigned int i = 0; i < ifmt_ctx->nb_streams; i++) {
//if(ifmt_ctx->streams[i]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO)
// continue;
// flush decoder
ret = avcodec_send_packet(stream_ctx[i].dec_ctx, NULL);
while(ret >= 0) {
ret = avcodec_receive_frame(stream_ctx[i].dec_ctx, frame);
if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
}
else if(ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Error while receiving a frame the decoder\n");
goto end;
}
if(media_type == AVMEDIA_TYPE_VIDEO) {
frame_idx++;
frame->pts = frame_idx;
}
// 编码、保存
//encode_write_frame(frame, i);
// 转换,编码、保存
trans_encode_write_frame(frame, i);
av_frame_unref(frame);
}
// flush encoder
encode_write_frame(NULL, i);
}
av_write_trailer(ofmt_ctx);
end:
av_packet_free(&pkt);
av_frame_free(&frame);
for(unsigned int i = 0; i < ifmt_ctx->nb_streams; i++) {
avcodec_free_context(&stream_ctx[i].dec_ctx);
if(ofmt_ctx && ofmt_ctx->nb_streams > i && ofmt_ctx->streams[i] && stream_ctx[i].enc_ctx)
avcodec_free_context(&stream_ctx[i].enc_ctx);
if(stream_ctx[i].sws_ctx) sws_freeContext(stream_ctx[i].sws_ctx);
av_frame_free(&stream_ctx[i].frame_v);
}
avformat_close_input(&ifmt_ctx);
if(ofmt_ctx && !(ofmt_ctx->oformat->flags & AVFMT_NOFILE))
avio_closep(&ofmt_ctx->pb);
avformat_free_context(ofmt_ctx);
return 0;
}
原文地址 CSDN
版权声明
本文仅代表作者观点,不代表码农殇立场。
本文系作者授权码农殇发表,未经许可,不得转载。