使用 ffmpeg muxer 到 MPEG2TS,音频流无法在 vlc 播放器中播放

Use ffmpeg muxer to MPEG2TS , audio stream can't play in vlc palayer

我使用libavformat将一个h264流和一个aac流封装成一个可以播放的mp4文件。但是封装成ts文件后,在Win10播放器中可以正常播放,但在vlc播放器中没有声音。封装的时候,打印的是音频流,但是用fprobe,打印的是channel=0的音频流。这可能是什么原因? 而h264源文件是没有的pts.So我自己算的

ffprobe print

ffmpeg print

这是我的代码。

#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>


static void log_packet(const AVFormatContext* fmt_ctx, const AVPacket* pkt, const char* tag)
{
    AVRational* time_base = &fmt_ctx->streams[pkt->stream_index]->time_base;
    printf("%s num=%d  den=%d\n", tag, time_base->num, time_base->den);
    printf("%s: pts:%s pts_time:%s dts:%s dts_time:%s duration:%s duration_time:%s stream_index:%d\n",
        tag,
        av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, time_base),
        av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, time_base),
        av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, time_base),
        pkt->stream_index);
}

int main()
{
    const char* in_filename_v = "test.h264";
    const char* in_filename_a = "aoutput.aac";
    const char* out_filename = "lol.ts";


    //Video Input AVFormatContext
    AVFormatContext* ifmt_ctx_v = NULL;
    int ret = avformat_open_input(&ifmt_ctx_v, in_filename_v, 0, 0);
    if (ret < 0)
    {
        fprintf(stderr, "Could not open input_v %s", in_filename_v);
        return -1;
    }

    //Find Video Stream Info
    ret = avformat_find_stream_info(ifmt_ctx_v, 0);
    if (ret < 0)
    {
        fprintf(stderr, "Could not find input_v stream info");
        return -1;
    }

    //Audio Input AVFormatContext
    AVFormatContext* ifmt_ctx_a = NULL;
    ret = avformat_open_input(&ifmt_ctx_a, in_filename_a, 0, 0);
    if (ret < 0)
    {
        fprintf(stderr, "Could not open input_a %s", in_filename_a);
        return -1;
    }

    //Find Audio Stream Info
    ret = avformat_find_stream_info(ifmt_ctx_a, 0);
    if (ret < 0)
    {
        fprintf(stderr, "Could not find input_a stream info");
        return -1;
    }


    printf("===========Input Information==========\n");
    av_dump_format(ifmt_ctx_v, 0, in_filename_v, 0);
    av_dump_format(ifmt_ctx_a, 0, in_filename_a, 0);
    printf("======================================\n");


    //Output AVFormatContext
    AVFormatContext* ofmt_ctx = NULL;
    avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename);
    if (!ofmt_ctx)
    {
        fprintf(stderr, "cannot alloc OutputFromat context!");
        ret = AVERROR_UNKNOWN;
        return -1;
    }
    AVOutputFormat* ofmt = ofmt_ctx->oformat;

    //Alloc AVSTREAM
    int istream_index_v = 0, istream_index_a = 0, ostream_index_v = 0, ostream_index_a = 0;
    for (int i = 0; i < ifmt_ctx_v->nb_streams; i++)
    {
        AVStream* outstream;
        AVStream* in_stream = ifmt_ctx_v->streams[i];
        AVCodecParameters* in_codecpar = in_stream->codecpar;
        if (in_codecpar->codec_type != AVMEDIA_TYPE_VIDEO)
            continue;

        outstream = avformat_new_stream(ofmt_ctx, NULL);
        if (!outstream)
        {
            fprintf(stderr, "Failed allocating output stream\n");
            return -1;
        }

        ret = avcodec_parameters_copy(outstream->codecpar, in_codecpar);
        if (ret < 0)
        {
            fprintf(stderr, "Failed to copy codec parameters\n");
            return -1;
        }
        outstream->codecpar->codec_tag = 0;
        // Remeber video stream id
        istream_index_v = i;
        ostream_index_v = 0;
        break;
    }

    for (int i = 0; i < ifmt_ctx_a->nb_streams; i++)
    {
        AVStream* outstream;
        AVStream* in_stream = ifmt_ctx_a->streams[i];
        AVCodecParameters* in_codecpar = in_stream->codecpar;
        if (in_codecpar->codec_type != AVMEDIA_TYPE_AUDIO)
            continue;

        outstream = avformat_new_stream(ofmt_ctx, NULL);
        if (!outstream)
        {
            fprintf(stderr, "Failed allocating output stream\n");
            return -1;
        }

        ret = avcodec_parameters_copy(outstream->codecpar, in_codecpar);
        if (ret < 0)
        {
            fprintf(stderr, "Failed to copy codec parameters\n");
            return -1;
        }
        outstream->codecpar->codec_tag = 0;
        // Remeber audio stream id
        istream_index_a = i;
        ostream_index_a = 1;
        break;
    }

    printf("===========Output Information==========\n");
    av_dump_format(ofmt_ctx, 0, out_filename, 1);
    printf("======================================\n");

    if (!(ofmt->flags & AVFMT_NOFILE))
    {
        ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);
        if (ret < 0)
        {
            fprintf(stderr, "Could not open output file '%s'", out_filename);
            return -1;
        }
    }

    //Write file header
    ret = avformat_write_header(ofmt_ctx, NULL);
    if (ret < 0) {
        fprintf(stderr, "Error occurred when opening output file\n");
        return -1;
    }

    //read and write packet
    AVPacket* pkt = av_packet_alloc();
    if (!pkt)
    {
        fprintf(stderr, "Could not allocate AVPacket\n");
        return -1;
    }
    while (1)
    {
        AVStream* in_stream, * outstream;
        ret = av_read_frame(ifmt_ctx_v, pkt);
        if (ret < 0)
            break;
        in_stream = ifmt_ctx_v->streams[pkt->stream_index];
        if (pkt->stream_index != istream_index_v)
        {
            av_packet_unref(pkt);
            continue;
        }


        pkt->stream_index = ostream_index_v;
        outstream = ofmt_ctx->streams[pkt->stream_index];
        // in log info
        log_packet(ifmt_ctx_v, pkt, "in");

        if (pkt->pts == AV_NOPTS_VALUE)
        {
            AVRational time_base1 = in_stream->time_base;
            // 
            int64_t calc_duration = (double)AV_TIME_BASE / av_q2d(in_stream->r_frame_rate);
            static int frame_index = 0;
            pkt->pts = (double)(frame_index * calc_duration) / (double)(av_q2d(time_base1) * AV_TIME_BASE);
            pkt->dts = pkt->pts;
            pkt->duration = (double)calc_duration / (double)(av_q2d(time_base1) * AV_TIME_BASE);
            frame_index++;
        }


        // duration between two frames(us)
        av_packet_rescale_ts(pkt, in_stream->time_base, outstream->time_base);
        pkt->pos = -1;
        // out log info
        log_packet(ofmt_ctx, pkt, "out");

        ret = av_interleaved_write_frame(ofmt_ctx, pkt);
        if (ret < 0)
        {
            fprintf(stderr, "Error muxing packet\n");
            break;
        }
    }

    while (1)
    {
        AVStream* in_stream, * outstream;
        ret = av_read_frame(ifmt_ctx_a, pkt);
        if (ret < 0)
            break;
        in_stream = ifmt_ctx_a->streams[pkt->stream_index];
        if (pkt->stream_index != istream_index_a)
        {
            av_packet_unref(pkt);
            continue;
        }

        if (pkt->pts == AV_NOPTS_VALUE)
        {
            AVRational time_base1 = in_stream->time_base;
            // duration between two frames(us)
            int64_t calc_duration = (double)AV_TIME_BASE / av_q2d(in_stream->r_frame_rate);
            static int frame_index = 0;
            pkt->pts = (double)(frame_index * calc_duration) / (double)(av_q2d(time_base1) * AV_TIME_BASE);
            pkt->dts = pkt->pts;
            pkt->duration = (double)calc_duration / (double)(av_q2d(time_base1) * AV_TIME_BASE);
            frame_index++;
        }

        // in log info
        log_packet(ifmt_ctx_a, pkt, "in");

        pkt->stream_index = ostream_index_a;
        outstream = ofmt_ctx->streams[pkt->stream_index];


        //change timestamp
        av_packet_rescale_ts(pkt, in_stream->time_base, outstream->time_base);
        pkt->pos = -1;
        // out log info
        log_packet(ofmt_ctx, pkt, "out");

        ret = av_interleaved_write_frame(ofmt_ctx, pkt);
        if (ret < 0)
        {
            fprintf(stderr, "Error muxing packet\n");
            break;
        }
    }

    //write file trailer
    av_write_trailer(ofmt_ctx);


    printf("===========Output Information==========\n");
    av_dump_format(ofmt_ctx, 0, out_filename, 1);
    printf("======================================\n");

}

DVB Insepctor video DVB Insepctor audio

感谢@aergistal。

  • 原因是 av_interleaved_write_frame 有缓冲区 limit.I 之前没有想过所以我写了所有视频包 首先然后写入所有音频packages.In ts文件,前面是 很多视频包,然后是很多音频包,以及 最后两个包交错。
  • 由于MPEG-TS是一个流由包组成,所以播放器长时间找不到音频包导致没有声音。

这是我可以使用的新代码。

#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>
#define EXTRAL 1

static void log_packet(const AVFormatContext* fmt_ctx, const AVPacket* pkt, const char* tag)
{
    AVRational* time_base = &fmt_ctx->streams[pkt->stream_index]->time_base;

    FILE* fp = fopen("fflog.log", "a+");
    char buf[200];
    sprintf(buf, "%s num=%d  den=%d\n", tag, time_base->num, time_base->den);
    for (int i = 0; *(buf + i) != '[=10=]'; i++)
    {
        fwrite(buf + i, 1, 1, fp);
    }
    sprintf(buf, "%s: pts:%s pts_time:%s dts:%s dts_time:%s duration:%s duration_time:%s stream_index:%d\n",
        tag,
        av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, time_base),
        av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, time_base),
        av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, time_base),
        pkt->stream_index);
    for (int i = 0; *(buf + i) != '[=10=]'; i++)
    {
        fwrite(buf + i, 1, 1, fp);
    }
    fclose(fp);
}

int main()
{
    const char* in_filename_v = "test.h264";
    const char* in_filename_a = "aoutput.aac";
    const char* out_filename = "lol.ts";


    AVFormatContext* ifmt_ctx_v = NULL;
    int ret = avformat_open_input(&ifmt_ctx_v, in_filename_v, 0, 0);
    if (ret < 0)
    {
        fprintf(stderr, "Could not open input_v %s", in_filename_v);
        return -1;
    }

    ret = avformat_find_stream_info(ifmt_ctx_v, 0);
    if (ret < 0)
    {
        fprintf(stderr, "Could not find input_v stream info");
        return -1;
    }

    AVFormatContext* ifmt_ctx_a = NULL;
    ret = avformat_open_input(&ifmt_ctx_a, in_filename_a, 0, 0);
    if (ret < 0)
    {
        fprintf(stderr, "Could not open input_a %s", in_filename_a);
        return -1;
    }

    ret = avformat_find_stream_info(ifmt_ctx_a, 0);
    if (ret < 0)
    {
        fprintf(stderr, "Could not find input_a stream info");
        return -1;
    }

#if EXTRAL
    printf("===========Input Information==========\n");
    av_dump_format(ifmt_ctx_v, 0, in_filename_v, 0);
    av_dump_format(ifmt_ctx_a, 0, in_filename_a, 0);
    printf("======================================\n");
#endif

    AVFormatContext* ofmt_ctx = NULL;
    avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename);
    if (!ofmt_ctx)
    {
        fprintf(stderr, "cannot alloc OutputFromat context!");
        ret = AVERROR_UNKNOWN;
        return -1;
    }
    AVOutputFormat* ofmt = ofmt_ctx->oformat;

    int istream_index_v = 0, istream_index_a = 0, ostream_index_v = 0, ostream_index_a = 0;
    for (int i = 0; i < ifmt_ctx_v->nb_streams; i++)
    {
        AVStream* outstream;
        AVStream* in_stream = ifmt_ctx_v->streams[i];
        AVCodecParameters* in_codecpar = in_stream->codecpar;
        if (in_codecpar->codec_type != AVMEDIA_TYPE_VIDEO)
            continue;

        outstream = avformat_new_stream(ofmt_ctx, NULL);
        if (!outstream)
        {
            fprintf(stderr, "Failed allocating output stream\n");
            return -1;
        }

        ret = avcodec_parameters_copy(outstream->codecpar, in_codecpar);
        if (ret < 0)
        {
            fprintf(stderr, "Failed to copy codec parameters\n");
            return -1;
        }
        outstream->codecpar->codec_tag = 0;
        istream_index_v = i;
        ostream_index_v = 0;
        break;
    }

    for (int i = 0; i < ifmt_ctx_a->nb_streams; i++)
    {
        AVStream* outstream;
        AVStream* in_stream = ifmt_ctx_a->streams[i];
        AVCodecParameters* in_codecpar = in_stream->codecpar;
        if (in_codecpar->codec_type != AVMEDIA_TYPE_AUDIO)
            continue;
        outstream = avformat_new_stream(ofmt_ctx, NULL);
        if (!outstream)
        {
            fprintf(stderr, "Failed allocating output stream\n");
            return -1;
        }

        ret = avcodec_parameters_copy(outstream->codecpar, in_codecpar);
        if (ret < 0)
        {
            fprintf(stderr, "Failed to copy codec parameters\n");
            return -1;
        }
        outstream->codecpar->codec_tag = 0;
        istream_index_a = i;
        ostream_index_a = 1;
        break;
    }

#if EXTRAL
    printf("===========Output Information==========\n");
    av_dump_format(ofmt_ctx, 0, out_filename, 1);
    printf("======================================\n");
#endif
    if (!(ofmt->flags & AVFMT_NOFILE))
    {
        ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);
        if (ret < 0)
        {
            fprintf(stderr, "Could not open output file '%s'", out_filename);
            return -1;
        }
    }

    ret = avformat_write_header(ofmt_ctx, NULL);
    if (ret < 0) {
        fprintf(stderr, "Error occurred when opening output file\n");
        return -1;
    }

    AVPacket* pkt = av_packet_alloc();
    if (!pkt)
    {
        fprintf(stderr, "Could not allocate AVPacket\n");
        return -1;
    }
    int64_t pts_v = 0, pts_a = 0;
    while (1)
    {
        if (av_compare_ts(pts_a, ifmt_ctx_a->streams[istream_index_a]->time_base, pts_v, ifmt_ctx_v->streams[istream_index_v]->time_base) <= 0)
        {

            AVStream* in_stream, * outstream;
            ret = av_read_frame(ifmt_ctx_a, pkt);
            if (ret < 0)
                break;
            in_stream = ifmt_ctx_a->streams[pkt->stream_index];
            if (pkt->stream_index != istream_index_a)
            {
                av_packet_unref(pkt);
                continue;
            }

            if (pkt->pts == AV_NOPTS_VALUE)
            {
                AVRational time_base1 = in_stream->time_base;
                int64_t calc_duration = (double)AV_TIME_BASE / av_q2d(in_stream->r_frame_rate);
                static int frame_index = 0;
                pkt->pts = (double)(frame_index * calc_duration) / (double)(av_q2d(time_base1) * AV_TIME_BASE);
                pkt->dts = pkt->pts;
                pkt->duration = (double)calc_duration / (double)(av_q2d(time_base1) * AV_TIME_BASE);
                frame_index++;
            }
            pts_a = pkt->pts;
            // in log info
            log_packet(ifmt_ctx_a, pkt, "in audio");

            pkt->stream_index = ostream_index_a;
            outstream = ofmt_ctx->streams[pkt->stream_index];


            av_packet_rescale_ts(pkt, in_stream->time_base, outstream->time_base);
            pkt->pos = -1;
            // out log info
            log_packet(ofmt_ctx, pkt, "out audio");

            ret = av_interleaved_write_frame(ofmt_ctx, pkt);
            if (ret < 0)
            {
                fprintf(stderr, "Error muxing packet\n");
                return -1;
            }
        }
        else
        {

            AVStream* in_stream, * outstream;
            ret = av_read_frame(ifmt_ctx_v, pkt);
            if (ret < 0)
                break;
            in_stream = ifmt_ctx_v->streams[pkt->stream_index];
            if (pkt->stream_index != istream_index_v)
            {
                av_packet_unref(pkt);
                continue;
            }


            pkt->stream_index = ostream_index_v;
            outstream = ofmt_ctx->streams[pkt->stream_index];


            if (pkt->pts == AV_NOPTS_VALUE)
            {
                AVRational time_base1 = in_stream->time_base;
                int64_t calc_duration = (double)AV_TIME_BASE / av_q2d(in_stream->r_frame_rate);
                static int frame_index = 0;
                pkt->pts = (double)(frame_index * calc_duration) / (double)(av_q2d(time_base1) * AV_TIME_BASE);
                pkt->dts = pkt->pts;
                pkt->duration = (double)calc_duration / (double)(av_q2d(time_base1) * AV_TIME_BASE);
                frame_index++;
            }
            pts_v = pkt->pts;

            // in log info
            log_packet(ifmt_ctx_v, pkt, "in video");

            av_packet_rescale_ts(pkt, in_stream->time_base, outstream->time_base);
            pkt->pos = -1;
            // out log info
            log_packet(ofmt_ctx, pkt, "out video");

            ret = av_interleaved_write_frame(ofmt_ctx, pkt);
            if (ret < 0)
            {
                fprintf(stderr, "Error muxing packet\n");
                return -1;
            }
        }
    }


    ret = av_write_trailer(ofmt_ctx);
    if (ret < 0)
    {
        fprintf(stderr, "Error av_write_trailer\n");
    }
#if EXTRAL
    printf("===========Output Information==========\n");
    av_dump_format(ofmt_ctx, 0, out_filename, 1);
    printf("======================================\n");
#endif
    av_packet_free(&pkt);
    avformat_close_input(&ifmt_ctx_v);
    avformat_close_input(&ifmt_ctx_a);
    if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
        avio_closep(&ofmt_ctx->pb);
    avformat_free_context(ofmt_ctx);
    return 0;
}