Ответ 1
API FFmpeg (libavformat/codec, в данном случае) API сопоставляет аргументы командной строки ffmpeg.exe довольно близко. Чтобы открыть файл, используйте avformat_open_input_file(). Последние два аргумента могут быть NULL. Это заполняет AVFormatContext для вас. Теперь вы начинаете читать фреймы с помощью av_read_frame() в цикле. Pkt.stream_index скажет вам, к какому потоку принадлежит каждый пакет, а avformatcontext- > streams [pkt.stream_index] - информация о сопроводительном потоке, которая сообщает вам, какой кодек он использует, будь то видео/аудио и т.д. Используйте avformat_close() для закрытия.
Для мультиплексирования вы используете обратный, см. muxing для получения дополнительной информации. В основном это выделить, avio_open2, добавить потоки для каждого существующего потока во входном файле (в основном context- > streams []), avformat_write_header(), av_interleaved_write_frame() в цикле av_write_trailer(), чтобы закрыть (и бесплатно выделенный контекст в конце).
Кодирование/декодирование видеопотока выполняется с помощью libavcodec. Для каждого AVPacket, который вы получаете из мультиплеера, используйте avcodec_decode_video2(). Используйте avcodec_encode_video2() для кодирования выходного AVFrame. Обратите внимание, что оба будут вводить задержку, поэтому первые несколько вызовов для каждой функции не будут возвращать какие-либо данные, и вам необходимо очистить кэшированные данные, вызвав каждую функцию с входными данными NULL, чтобы получить из нее хвостовые пакеты/фреймы. av_interleave_write_frame будет правильно чередовать пакеты, чтобы потоки видео/аудио не десинхронизировались (например, в видеопакетах той же метки времени появляются MBs после аудиопакетов в файле ts).
Если вам нужны более подробные примеры для avcodec_decode_video2, avcodec_encode_video2, av_read_frame или av_interleaved_write_frame, просто Google "пример функции $", и вы увидите полнофункциональные примеры, показывающие, как их правильно использовать. Для кодировки x264 установите некоторые параметры по умолчанию в AVCodecContext при вызове avcodec_open2 для настроек качества кодирования. В C API вы делаете это с помощью AVDictionary, например:
AVDictionary opts = *NULL;
av_dict_set(&opts, "preset", "veryslow", 0);
// use either crf or b, not both! See the link above on H264 encoding options
av_dict_set_int(&opts, "b", 1000, 0);
av_dict_set_int(&opts, "crf", 10, 0);
[править] О, я забыл одну часть, timestamping. Каждый AVPacket и AVFrame имеет переменную pts в своей структуре, и вы можете использовать это, чтобы решить, включать ли пакет/кадр в выходной поток. Итак, для аудио вы используете AVPacket.pts с шага demuxing как разделитель, а для видео вы будете использовать AVFrame.pts с этапа декодирования в виде разделителя. Их соответствующая документация сообщает вам, в какой части они находятся.
[edit2] Я вижу, что у вас все еще есть некоторые проблемы без реального кода, поэтому здесь есть реальный (рабочий) транскодер, который перекодирует видео и ремиксирует аудио. Вероятно, у него много ошибок, утечек и ошибок в отчетах об ошибках, а также не касается временных меток (я оставляю это для вас как упражнение), но он делает основные вещи, о которых вы просили:
#include <stdio.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
static AVFormatContext *inctx, *outctx;
#define MAX_STREAMS 16
static AVCodecContext *inavctx[MAX_STREAMS];
static AVCodecContext *outavctx[MAX_STREAMS];
static int openInputFile(const char *file) {
int res;
inctx = NULL;
res = avformat_open_input(& inctx, file, NULL, NULL);
if (res != 0)
return res;
res = avformat_find_stream_info(inctx, NULL);
if (res < 0)
return res;
return 0;
}
static void closeInputFile(void) {
int n;
for (n = 0; n < inctx->nb_streams; n++)
if (inavctx[n]) {
avcodec_close(inavctx[n]);
avcodec_free_context(&inavctx[n]);
}
avformat_close_input(&inctx);
}
static int openOutputFile(const char *file) {
int res, n;
outctx = avformat_alloc_context();
outctx->oformat = av_guess_format(NULL, file, NULL);
if ((res = avio_open2(&outctx->pb, file, AVIO_FLAG_WRITE, NULL, NULL)) < 0)
return res;
for (n = 0; n < inctx->nb_streams; n++) {
AVStream *inst = inctx->streams[n];
AVCodecContext *inc = inst->codec;
if (inc->codec_type == AVMEDIA_TYPE_VIDEO) {
// video decoder
inavctx[n] = avcodec_alloc_context3(inc->codec);
avcodec_copy_context(inavctx[n], inc);
if ((res = avcodec_open2(inavctx[n], avcodec_find_decoder(inc->codec_id), NULL)) < 0)
return res;
// video encoder
AVCodec *encoder = avcodec_find_encoder_by_name("libx264");
AVStream *outst = avformat_new_stream(outctx, encoder);
outst->codec->width = inavctx[n]->width;
outst->codec->height = inavctx[n]->height;
outst->codec->pix_fmt = inavctx[n]->pix_fmt;
AVDictionary *dict = NULL;
av_dict_set(&dict, "preset", "veryslow", 0);
av_dict_set_int(&dict, "crf", 10, 0);
outavctx[n] = avcodec_alloc_context3(encoder);
avcodec_copy_context(outavctx[n], outst->codec);
if ((res = avcodec_open2(outavctx[n], encoder, &dict)) < 0)
return res;
} else if (inc->codec_type == AVMEDIA_TYPE_AUDIO) {
avformat_new_stream(outctx, inc->codec);
inavctx[n] = outavctx[n] = NULL;
} else {
fprintf(stderr, "Don’t know what to do with stream %d\n", n);
return -1;
}
}
if ((res = avformat_write_header(outctx, NULL)) < 0)
return res;
return 0;
}
static void closeOutputFile(void) {
int n;
av_write_trailer(outctx);
for (n = 0; n < outctx->nb_streams; n++)
if (outctx->streams[n]->codec)
avcodec_close(outctx->streams[n]->codec);
avformat_free_context(outctx);
}
static int encodeFrame(int stream_index, AVFrame *frame, int *gotOutput) {
AVPacket outPacket;
int res;
av_init_packet(&outPacket);
if ((res = avcodec_encode_video2(outavctx[stream_index], &outPacket, frame, gotOutput)) < 0) {
fprintf(stderr, "Failed to encode frame\n");
return res;
}
if (*gotOutput) {
outPacket.stream_index = stream_index;
if ((res = av_interleaved_write_frame(outctx, &outPacket)) < 0) {
fprintf(stderr, "Failed to write packet\n");
return res;
}
}
av_free_packet(&outPacket);
return 0;
}
static int decodePacket(int stream_index, AVPacket *pkt, AVFrame *frame, int *frameFinished) {
int res;
if ((res = avcodec_decode_video2(inavctx[stream_index], frame,
frameFinished, pkt)) < 0) {
fprintf(stderr, "Failed to decode frame\n");
return res;
}
if (*frameFinished){
int hasOutput;
frame->pts = frame->pkt_pts;
return encodeFrame(stream_index, frame, &hasOutput);
} else {
return 0;
}
}
int main(int argc, char *argv[]) {
char *input = argv[1];
char *output = argv[2];
int res, n;
printf("Converting %s to %s\n", input, output);
av_register_all();
if ((res = openInputFile(input)) < 0) {
fprintf(stderr, "Failed to open input file %s\n", input);
return res;
}
if ((res = openOutputFile(output)) < 0) {
fprintf(stderr, "Failed to open output file %s\n", input);
return res;
}
AVFrame *frame = av_frame_alloc();
AVPacket inPacket;
av_init_packet(&inPacket);
while (av_read_frame(inctx, &inPacket) >= 0) {
if (inavctx[inPacket.stream_index] != NULL) {
int frameFinished;
if ((res = decodePacket(inPacket.stream_index, &inPacket, frame, &frameFinished)) < 0) {
return res;
}
} else {
if ((res = av_interleaved_write_frame(outctx, &inPacket)) < 0) {
fprintf(stderr, "Failed to write packet\n");
return res;
}
}
}
for (n = 0; n < inctx->nb_streams; n++) {
if (inavctx[n]) {
// flush decoder
int frameFinished;
do {
inPacket.data = NULL;
inPacket.size = 0;
if ((res = decodePacket(n, &inPacket, frame, &frameFinished)) < 0)
return res;
} while (frameFinished);
// flush encoder
int gotOutput;
do {
if ((res = encodeFrame(n, NULL, &gotOutput)) < 0)
return res;
} while (gotOutput);
}
}
av_free_packet(&inPacket);
closeInputFile();
closeOutputFile();
return 0;
}