H.264使用C / C ++中的ffmpeg库从CCTV(HW)编码器复制到MP4,如何获取AVCodecContext :: extradata

时间:2016-05-25 17:07:25

标签: c ffmpeg mp4 h.264 muxer

我正在尝试通过CCTV摄像头的SDK API收到的数据包将视频复制到mp4文件。来自CCTV的流似乎只包含h264 I和P帧的数据包(没有B帧)。

问题是,当我将AVCodecContext :: extradata留空时,文件在Daum PotPlayer中无法播放,但是,我可以在VLC中播放该文件。如果我提供额外的数据(可能不正确 - 我只是从HW编码器收到的数据包中复制前50个字节),该文件可以在PotPlayer中播放,但不能在VLC中播放。

我不知道,如何正确准备extradata。

下面的代码显示了多路复用。

void WriteVideo()
{
    AVFormatContext* formatContext = nullptr;
    AVStream* stream = nullptr;

    int ret = 0;

    ret = avformat_alloc_output_context2(&formatContext, nullptr, "mp4", OUTPUT_V_FILE_NAME);
    if (ret < 0)
    {
        fprintf(stderr, "Error occurred when allocating output context: %d\n", ret);
        return;
    }

    stream = avformat_new_stream(formatContext, nullptr);
    if (!stream)
    {
        avformat_free_context(formatContext);
        formatContext = nullptr;
        fprintf(stderr, "Error occurred creating new stream");
        return;
    }

    stream->codec->codec_tag = 0;

    if (formatContext->oformat->flags & AVFMT_GLOBALHEADER)
    {
        stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
    }


    stream->codec->codec_type = AVMEDIA_TYPE_VIDEO;
    stream->codec->codec_id = AV_CODEC_ID_H264;
    stream->codec->pix_fmt = AV_PIX_FMT_YUV420P;
    stream->codec->bit_rate = 4096000; // 8192000; // 4096000; // 384000;
    stream->codec->width = 1920; // 352
    stream->codec->height = 1080; // 288
    stream->codec->gop_size = 50;

    AVRational tb;
    tb.num = 1;
    tb.den = 25;
    stream->codec->time_base = tb;
    //stream->codec->field_order = AVFieldOrder::AV_FIELD_UNKNOWN;
    //stream->codec->color_primaries = AVColorPrimaries::AVCOL_PRI_BT470BG;
    //stream->codec->color_trc = AVColorTransferCharacteristic::AVCOL_TRC_GAMMA22;
    //stream->codec->colorspace = AVColorSpace::AVCOL_SPC_BT470BG;
    //stream->codec->chroma_sample_location = AVChromaLocation::AVCHROMA_LOC_CENTER;
    AVRational aratio;
    aratio.num = 1;
    aratio.den = 1;
    stream->codec->sample_aspect_ratio = aratio;
    // stream->codec->delay = 0;
    // stream->codec->color_range = AVColorRange::AVCOL_RANGE_MPEG;

    av_dump_format(formatContext, 0, formatContext->filename, 1);

    if (!(formatContext->oformat->flags & AVFMT_NOFILE))
    {
        if (avio_open(&formatContext->pb, formatContext->filename, AVIO_FLAG_WRITE) < 0)
        {
            avformat_free_context(formatContext);
            formatContext = nullptr;
            fprintf(stderr, "Error occurred when opening output file.");
            return;

        }
    }

    avformat_write_header(formatContext, nullptr);

    int frameIdx = 0;
    int pts = 0;
    int dts = 0;
    int pkt_pos = 0;
    int size = static_cast<int>(tmpRawFrameBuffer.size());

    AVPacket pkt = { 0 };
    av_init_packet(&pkt);

    while (frameIdx < size)
    {
        pkt.data = tmpRawFrameBuffer[frameIdx];
        pkt.size = tmpRawFrameSizes[frameIdx];

        // debug purphose start
        FILE* f;
        char filename[MAX_PATH];
        sprintf_s(filename, MAX_PATH, OUTPUT_PACKET_FILE_PATTERN_NAME, frameIdx);
        auto err = fopen_s(&f, filename, "wb");
        if (err != 0)
        {
            fprintf(stderr, "Could not open %s\n", OUTPUT_V_FILE_NAME);
            exit(1);
        }

        fflush(stdout);
        fwrite(pkt.data, 1, pkt.size, f);
        fclose(f);
        // debug purphose end

        if (tmpRawFrameTypes[frameIdx] == VIDEO_I_FRAME)
        {
            pkt.flags |= AV_PKT_FLAG_KEY;
            stream->codec->extradata_size = 50;
            stream->codec->extradata = (uint8_t*)av_malloc(stream->codec->extradata_size);
            memcpy(stream->codec->extradata, pkt.data, stream->codec->extradata_size);
        }
        pkt.pts = pts++;
        pkt.dts = dts++;
        /* rescale output packet timestamp values from codec to stream timebase */
        // av_packet_rescale_ts(&pkt, stream->codec->time_base, stream->time_base);
        pkt.pts = av_rescale_q(pkt.pts, stream->codec->time_base, stream->time_base);
        pkt.dts = av_rescale_q(pkt.dts, stream->codec->time_base, stream->time_base);
        pkt.duration = 512; // should be calculated (derived from FPS 25, and 12800)
        pkt.pos = -1;
        pkt.stream_index = stream->index;

        auto ret = av_write_frame(formatContext, &pkt);
        if (ret < 0)
        {
            fprintf(stderr, "Error while writing video frame: %d\n", ret);
            break;
        }
        av_packet_unref(&pkt);
        ++frameIdx;
    }

    if (formatContext)
    {
        av_write_trailer(formatContext);
        if (!(formatContext->oformat->flags & AVFMT_NOFILE)) avio_close(formatContext->pb); // close the output file
        avformat_free_context(formatContext);
        formatContext = nullptr;
    }
}

看来,来自摄像头的数据包(缓存在tmpRawFrameBuffer向量中)包含一些标题,但我无法解析它们,我不明白标题的含义。

I帧数据包的第一个字节看起来像转储:

00 00 01 FC 02 19 F0 87 A0 23 73 41 B6 C0 01 00
00 00 00 01 67 4D 00 2A 95 A8 1E 00 89 F9 61 00
00 03 00 01 00 00 03 00 32 84 00 00 00 01 68 EE 
3C 80 00 00 00 01 06 E5 01 19 80 00 00 00 01 65 
B8 00 00 08 C7 B0 23 FF F7 80 EE FE 63 B6 FB F5
...

第一个P帧的第一个字节:

00 00 01 FD E5 24 00 00 00 00 00 01 61 E0 22 27 
FF D6 B0 D7 A4 2B 71 6B 19 C5 87 CA BB 8B BF 60 
14 59 B4 00 CC BC 0F C0 9E FD 84 B5 FB C4 83 DB 
5A 8B 80 FC EC D6 33 6D DE 10 96 6F 31 41 86 5C 
D4 22 F9 33 48 5B CE 77 38 17 0C D6 DD C7 6C E8
...

下一个P帧的第一个字节:

00 00 01 FD 5E 2F 00 00 00 00 00 01 61 E0 42 2F 
FF E7 06 DD 3C 66 26 15 94 93 7A F1 30 8A 6D B8 
AD DD 6B 0F 38 89 1D 1B 5C AC 44 6A D7 D1 21 3B 
E2 29 F8 14 BB 98 1C 06 4D B6 10 BB DB B9 CA 4F 
0B ED B1 A9 06 78 8C EC 06 6D 9F 4F 79 0C 35 5B
...

...

开始下一个I帧:

00 00 01 FC 02 19 F0 87 A2 23 73 41 75 89 01 00 
00 00 00 01 67 4D 00 2A 95 A8 1E 00 89 F9 61 00 
00 03 00 01 00 00 03 00 32 84 00 00 00 01 68 EE 
3C 80 00 00 00 01 06 E5 01 1B 80 00 00 00 01 65 
B8 00 00 0F 07 F0 7F F6 6C 69 43 0F F0 28 DF 97
...

有人知道,如何正确填写extradata?我刚刚制作了前50个字节的副本,但看起来,这是不正确的。

标题可能是(AUD)(SPS)(PPS)(I-Slice)(PPS)(P-Slice)(PPS)(P-Slice)...(AUD)(SPS)(PPS)( I-Slice)(https://stackoverflow.com/a/20686267/1699328)但是,我不知道,如何为

提取数据
stream->codec->extradata

我试图在这篇文章H.264 muxed to MP4 using libavformat not playing back中获得一些灵感,但我无法弄清楚spsFrameLen,ppsFrameLen和spsFrame的值是什么。

muxer的结果(mp4文件的第一个和最后一个字节):

00 00 00 20 66 74 79 70 69 73 6F 6D 00 00 02 00 
69 73 6F 6D 69 73 6F 32 61 76 63 31 6D 70 34 31 
00 00 00 08 66 72 65 65 00 26 2E 6D 6D 64 61 74 
00 00 00 0D FC 02 19 F0 87 A0 23 73 41 B6 C0 01 
00 00 00 00 16 67 4D 00 2A 95 A8 1E 00 89 F9 61 
00 00 03 00 01 00 00 03 00 32 84 00 00 00 04 68 
EE 3C 80 00 00 00 05 06 E5 01 19 80 00 01 C0 87 
65 B8 00 00 08 C7 B0 23 FF F7 80 EE FE 63 B6 FB 
F5 97 A8 6B 48 39 61 99 FD 99 27 41 F2 78 54 EE 
D1 38 8E E8 18 DD 05 E4 BA F4 EB 69 CF 91 5C 34
...
...
...
95 B8 D8 D4 C3 AF A1 BA AC 28 F0 D4 D4 7C 48 9A 
0C A6 8C 4C 98 00 00 05 1E 6D 6F 6F 76 00 00 00 
6C 6D 76 68 64 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 03 E8 00 00 13 B0 00 01 00 00 01 00 00 
00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 40 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 02 00 00 04 48 74 72 61 
6B 00 00 00 5C 74 6B 68 64 00 00 00 03 00 00 00 
00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 13 
B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 40 00 00 00 07 80 00 00 04 38 00 00 00 00 00 
24 65 64 74 73 00 00 00 1C 65 6C 73 74 00 00 00 
00 00 00 00 01 00 00 13 B0 00 00 00 00 00 01 00 
00 00 00 03 C0 6D 64 69 61 00 00 00 20 6D 64 68 
64 00 00 00 00 00 00 00 00 00 00 00 00 00 00 32 
00 00 00 FC 00 55 C4 00 00 00 00 00 2D 68 64 6C 
72 00 00 00 00 00 00 00 00 76 69 64 65 00 00 00 
00 00 00 00 00 00 00 00 00 56 69 64 65 6F 48 61 
6E 64 6C 65 72 00 00 00 03 6B 6D 69 6E 66 00 00 
00 14 76 6D 68 64 00 00 00 01 00 00 00 00 00 00 
00 00 00 00 00 24 64 69 6E 66 00 00 00 1C 64 72 
65 66 00 00 00 00 00 00 00 01 00 00 00 0C 75 72 
6C 20 00 00 00 01 00 00 03 2B 73 74 62 6C 00 00 
00 93 73 74 73 64 00 00 00 00 00 00 00 01 00 00 
00 83 61 76 63 31 00 00 00 00 00 00 00 01 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 07 80 
04 38 00 48 00 00 00 48 00 00 00 00 00 00 00 01 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 18 FF FF 00 00 00 2D 61 76 63 43 01 4D 00 2A 
FF E1 00 16 67 4D 00 2A 95 A8 1E 00 89 F9 61 00 
00 03 00 01 00 00 03 00 32 84 01 00 04 68 EE 3C 
80 00 00 00 18 73 74 74 73 00 00 00 00 00 00 00 
01 00 00 00 7E 00 00 02 00 00 00 00 1C 73 74 73 
73 00 00 00 00 00 00 00 03 00 00 00 01 00 00 00 
33 00 00 00 65 00 00 00 34 73 74 73 63 00 00 00 
00 00 00 00 03 00 00 00 01 00 00 00 3B 00 00 00 
01 00 00 00 02 00 00 00 37 00 00 00 01 00 00 00 
03 00 00 00 0C 00 00 00 01 00 00 02 0C 73 74 73 
7A 00 00 00 00 00 00 00 00 00 00 00 7E 00 01 C0 
C7 00 00 24 EE 00 00 2F 67 00 00 1D 83 00 00 2E 
8F 00 00 30 B6 00 00 2F 44 00 00 2F 50 00 00 34 
6B 00 00 30 BE 00 00 31 0C 00 00 31 E7 00 00 30 
EA 00 00 31 4E 00 00 31 A8 00 00 32 21 00 00 31 
E6 00 00 31 B5 00 00 31 14 00 00 31 AF 00 00 31 
9D 00 00 33 60 00 00 32 11 00 00 32 4C 00 00 31 
F0 00 00 32 91 00 00 43 43 00 00 44 29 00 00 44 
EC 00 00 44 20 00 00 44 86 00 00 45 AD 00 00 47 
47 00 00 46 9F 00 00 46 D9 00 00 47 BE 00 00 48 
CD 00 00 3E 50 00 00 40 98 00 00 41 0E 00 00 40 
43 00 00 41 07 00 00 41 BB 00 00 41 FF 00 00 30 
5E 00 00 33 C7 00 00 34 B7 00 00 33 F1 00 00 33 
0D 00 00 32 DB 00 01 89 86 00 00 3B E1 00 00 3C 
55 00 00 3C 64 00 00 3C B7 00 00 3C FD 00 00 3E 
54 00 00 3E C5 00 00 3E 1C 00 00 3E 94 00 00 3E 
44 00 00 3E D7 00 00 3F CC 00 00 3E D6 00 00 40 
00 00 00 40 4D 00 00 40 04 00 00 3F A9 00 00 40 
82 00 00 41 0F 00 00 41 64 00 00 41 E5 00 00 42 
1E 00 00 42 2C 00 00 42 80 00 00 42 4D 00 00 43 
9F 00 00 43 DA 00 00 44 45 00 00 44 21 00 00 44 
B7 00 00 45 22 00 00 45 E3 00 00 45 BF 00 00 46 
18 00 00 47 4B 00 00 45 05 00 00 47 34 00 00 46 
60 00 00 46 97 00 00 46 66 00 00 46 29 00 00 46 
38 00 00 47 1D 00 00 47 42 00 00 47 18 00 00 47 
13 00 00 46 52 00 00 47 48 00 00 46 F8 00 01 BE 
E3 00 00 3F 56 00 00 3B 32 00 00 38 F8 00 00 37 
56 00 00 36 2D 00 00 35 DA 00 00 34 6B 00 00 3E 
BE 00 00 3E B5 00 00 3F 33 00 00 3F AC 00 00 3F 
38 00 00 42 32 00 01 1B DC 00 01 80 50 00 01 14 
06 00 00 C2 BB 00 00 96 12 00 00 6D EC 00 00 54 
E6 00 00 3A AC 00 00 32 00 00 00 2F 0A 00 00 2D 
F1 00 00 1B 7F 00 00 00 1C 73 74 63 6F 00 00 00 
00 00 00 00 03 00 00 00 30 00 0F DD AB 00 1F 7D 
9E 00 00 00 62 75 64 74 61 00 00 00 5A 6D 65 74 
61 00 00 00 00 00 00 00 21 68 64 6C 72 00 00 00 
00 00 00 00 00 6D 64 69 72 61 70 70 6C 00 00 00 
00 00 00 00 00 00 00 00 00 2D 69 6C 73 74 00 00 
00 25 A9 74 6F 6F 00 00 00 1D 64 61 74 61 00 00 
00 01 00 00 00 00 4C 61 76 66 35 37 2E 32 33 2E 
31 30 30

非常感谢任何建议。

1 个答案:

答案 0 :(得分:2)

首先,有些数据不属于视频流。你需要弄清楚如何删除它。其他所有内容都可以在这里得到解答:Possible Locations for Sequence/Picture Parameter Set(s) for H.264 Stream