我正在尝试将多个文件中的所有流复制到一个文件中,而不对流进行转码。您通常使用
ffmpeg
的
ffmpeg -i “file_with_audio.mp4” -i “file_with_video.mp4” -c copy -shortest file_with_audio_and_video.mp4
实用程序做的事情
这是代码:
int ffmpegOpenInputFile(const char* filename, AVFormatContext **ic) {
int ret;
unsigned int i;
*ic = avformat_alloc_context();
if (!(*ic))
return -1; // Couldn't allocate input context
if((ret = avformat_open_input(ic, filename, NULL, NULL)) < 0)
return ret; // Couldn't open file
// Get format info (retrieve stream information)
if ((ret = avformat_find_stream_info(*ic, NULL)) < 0)
return ret; // Couldn't find stream information
for (int i = 0; i < (*ic)->nb_streams; i++) {
AVStream *stream;
AVCodecContext *codec_ctx;
stream = (*ic)->streams[i];
codec_ctx = stream->codec;
/* Reencode video & audio and remux subtitles etc. */
if (codec_ctx->codec_type == AVMEDIA_TYPE_VIDEO
|| codec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {
/* Open decoder */
ret = avcodec_open2(codec_ctx,
avcodec_find_decoder(codec_ctx->codec_id), NULL);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Failed to open decoder for stream #%u\n", i);
return ret;
}
}
}
// Dump information about file onto standard error
av_dump_format(*ic, 0, filename, 0);
return 0;
}
int main(int argc, char *argv[]) {
const char *inputFilename1 = "/avfiles/video_input.mp4";
const char *inputFilename2 = "/avfiles/audio_input.mp4";
const char *filename = "/avfiles/out.mp4";
int ret;
av_register_all();
AVFormatContext *ic1 = nullptr;
AVFormatContext *ic2 = nullptr;
AVFormatContext *oc = nullptr;
if ((ret = ffmpegOpenInputFile(inputFilename1, &ic1)) < 0)
return ret; // and free resources and
if ((ret = ffmpegOpenInputFile(inputFilename2, &ic2)) < 0)
return ret; // and free resources and
AVOutputFormat *outfmt = av_guess_format(NULL, filename, NULL);
if (outfmt == NULL)
return -1; // Could not guess output format
avformat_alloc_output_context2(&oc, outfmt, NULL, filename);
if (!oc)
return AVERROR_UNKNOWN; // Could not create output context
// populate input streams from all input files
AVStream **input_streams = NULL;
int nb_input_streams = 0;
for (int i = 0; i < ic1->nb_streams; i++) {
input_streams = (AVStream **) grow_array(input_streams, sizeof(*input_streams), &nb_input_streams,
nb_input_streams + 1);
input_streams[nb_input_streams - 1] = ic1->streams[i];
}
for (int i = 0; i < ic2->nb_streams; i++) {
input_streams = (AVStream **) grow_array(input_streams, sizeof(*input_streams), &nb_input_streams,
nb_input_streams + 1);
input_streams[nb_input_streams - 1] = ic2->streams[i];
}
for (int i = 0; i < nb_input_streams; i++) {
AVStream *ist = input_streams[i]; // could be named 'm_in_vid_strm'
// if output context has video codec support and current input stream is video
if (/*oc->video_codec_id*/ oc->oformat->video_codec != AV_CODEC_ID_NONE && ist != NULL
&& ist->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
AVCodec *out_vid_codec = avcodec_find_encoder(oc->oformat->video_codec);
if (NULL == out_vid_codec)
return -1; // Couldn't find video encoder
AVStream *m_out_vid_strm = avformat_new_stream(oc, out_vid_codec);
if (NULL == m_out_vid_strm)
return -1; // Couldn't output video stream
m_out_vid_strm->id = 0; // XXX:
ret = avcodec_copy_context(m_out_vid_strm->codec, ist->codec);
if (ret < 0)
return ret; // Failed to copy context
}
// if output context has audio codec support and current input stream is audio
if (/*oc->audio_codec_id*/ oc->oformat->audio_codec != AV_CODEC_ID_NONE && ist != NULL
&& ist->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
AVCodec *out_aud_codec = avcodec_find_encoder(oc->oformat->audio_codec);
if (nullptr == out_aud_codec)
return -1; // couldn't find audio codec
AVStream *m_out_aud_strm = avformat_new_stream(oc, out_aud_codec);
if (nullptr == m_out_aud_strm)
return -1; // couldn't allocate audio out stream
ret = avcodec_copy_context(m_out_aud_strm->codec, ist->codec);
if (ret < 0)
return ret; // couldn't copy context
}
}
// finally output header
if (!(oc->flags & AVFMT_NOFILE)) {
ret = avio_open(&oc->pb, filename, AVIO_FLAG_WRITE);
if (ret < 0)
return ret; // Could not open output file
av_dump_format(oc, 0, filename, 1);
ret = avformat_write_header(oc, NULL);
if (ret < 0)
return ret; // Error occurred when opening output file
}
return 0;
}
avformat_write_header(oc, NULL);
总是返回错误,我看到以下消息:
[mp4 @ 0x7f84ec900a00] Using AVStream.codec.time_base as a timebase hint to the muxer is deprecated. Set AVStream.time_base instead.
[mp4 @ 0x7f84ec900a00] Tag avc1/0x31637661 incompatible with output codec id '28' ([33][0][0][0])
但是输入和输出流匹配:
Input streams from 2 files:
Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1920x1080 [SAR 1:1 DAR 16:9], 2834 kb/s, 23.98 fps, 23.98 tbr, 90k tbn, 47.95 tbc (default)
Stream #0:0(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 125 kb/s (default)
Output #0, mp4, to '/Users/alex/Workspace/_qt/tubisto/avfiles/out.mp4':
Stream #0:0: Video: h264 (libx264) (avc1 / 0x31637661), yuv420p, 1920x1080 [SAR 1:1 DAR 16:9], q=2-31, 2834 kb/s, 47.95 tbc
Stream #0:1: Audio: aac (libvo_aacenc) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 125 kb/s
为什么会出现输出编解码器不兼容的错误? 我的代码有什么问题以及如何使其能够将所有输入文件中的所有流复制到输出文件?
原因是你没有正确设置编码器的
codec_tag
。
您应该设置正确的
codec_tag
,或者将 codec_tag
设置为 0,然后让复用器为您处理。
以下是如何操作的示例代码。
AVFormatContext *ofmt_ctx; // todo
AVCodec *oc; // todo
AVStream *stream = avformat_new_stream(ofmt_ctx, oc);
if (stream != NULL) {
// ...
unsigned int tag = 0;
// for ffmpeg new api 3.x
AVCodecParameters *parameters = stream->codecpar;
if (av_codec_get_tag2(ofmt_ctx->oformat->codec_tag, oc->id, &tag) == 0) {
av_log(NULL, AV_LOG_ERROR, "could not find codec tag for codec id %d, default to 0.\n", oc->id);
}
parameters->codec_tag = tag;
stream->codec = avcodec_alloc_context3(oc);
// more setting for stream->codec
avcodec_parameters_to_context(stream->codec, parameters);
// for old ffmpeg version
stream->codec->codec_tag = tag;
// ...
}
对于可能正在寻找直接使用 FFmpeg 二进制文件的解决方案的人,FFmpeg 问题跟踪器上提到了一个解决方案: https://trac.ffmpeg.org/ticket/8338#comment:22