H.264, мультиплексированный в MP4 с использованием libavformat, не воспроизводится

Я пытаюсь объединить данные H.264 в файл MP4. Похоже, что при сохранении данных H.264 Annex B в файл MP4 ошибок нет, но файл не воспроизводится.

Я провел двоичное сравнение файлов, и проблема, похоже, где-то в том, что записывается в нижний колонтитул (трейлер) файла MP4.

Я подозреваю, что это должно быть связано с тем, как создается поток, или что-то в этом роде.

В этом:

AVOutputFormat* fmt = av_guess_format( 0, "out.mp4", 0 );
oc = avformat_alloc_context();
oc->oformat = fmt;
strcpy(oc->filename, filename);

Частью этого прототипа приложения является создание файла png для каждого IFrame. Поэтому, когда встречается первый IFrame, я создаю видеопоток и пишу заголовок av и т. Д.:

void addVideoStream(AVCodecContext* decoder)
{
    videoStream = av_new_stream(oc, 0);
    if (!videoStream)
    {
         cout << "ERROR creating video stream" << endl;
         return;        
    }
    vi = videoStream->index;    
    videoContext = videoStream->codec;      
    videoContext->codec_type = AVMEDIA_TYPE_VIDEO;
    videoContext->codec_id = decoder->codec_id;
    videoContext->bit_rate = 512000;
    videoContext->width = decoder->width;
    videoContext->height = decoder->height;
    videoContext->time_base.den = 25;
    videoContext->time_base.num = 1;    
    videoContext->gop_size = decoder->gop_size;
    videoContext->pix_fmt = decoder->pix_fmt;       

    if (oc->oformat->flags & AVFMT_GLOBALHEADER)
        videoContext->flags |= CODEC_FLAG_GLOBAL_HEADER;

    av_dump_format(oc, 0, filename, 1);

    if (!(oc->oformat->flags & AVFMT_NOFILE))
    {
        if (avio_open(&oc->pb, filename, AVIO_FLAG_WRITE) < 0) {
        cout << "Error opening file" << endl;
    }
    avformat_write_header(oc, NULL);
}

Я выписываю пакеты:

unsigned char* data = block->getData();
unsigned char videoFrameType = data[4];
int dataLen = block->getDataLen();

// store pps
if (videoFrameType == 0x68)
{
    if (ppsFrame != NULL)
    {
        delete ppsFrame; ppsFrameLength = 0; ppsFrame = NULL;
    }
    ppsFrameLength = block->getDataLen();
    ppsFrame = new unsigned char[ppsFrameLength];
    memcpy(ppsFrame, block->getData(), ppsFrameLength);
}
else if (videoFrameType == 0x67)
{
    // sps
    if (spsFrame != NULL)
    {
        delete spsFrame; spsFrameLength = 0; spsFrame = NULL;
}
    spsFrameLength = block->getDataLen();
    spsFrame = new unsigned char[spsFrameLength];
    memcpy(spsFrame, block->getData(), spsFrameLength);                 
}                                           

if (videoFrameType == 0x65 || videoFrameType == 0x41)
{
    videoFrameNumber++;
}
if (videoFrameType == 0x65)
{
    decodeIFrame(videoFrameNumber, spsFrame, spsFrameLength, ppsFrame, ppsFrameLength, data, dataLen);
}

if (videoStream != NULL)
{
    AVPacket pkt = { 0 };
    av_init_packet(&pkt);
    pkt.stream_index = vi;
    pkt.flags = 0;                      
    pkt.pts = pkt.dts = 0;                                  

    if (videoFrameType == 0x65)
    {
        // combine the SPS PPS & I frames together
        pkt.flags |= AV_PKT_FLAG_KEY;                                                   
        unsigned char* videoFrame = new unsigned char[spsFrameLength+ppsFrameLength+dataLen];
        memcpy(videoFrame, spsFrame, spsFrameLength);
        memcpy(&videoFrame[spsFrameLength], ppsFrame, ppsFrameLength);
        memcpy(&videoFrame[spsFrameLength+ppsFrameLength], data, dataLen);

        // overwrite the start code (00 00 00 01 with a 32-bit length)
        setLength(videoFrame, spsFrameLength-4);
        setLength(&videoFrame[spsFrameLength], ppsFrameLength-4);
        setLength(&videoFrame[spsFrameLength+ppsFrameLength], dataLen-4);
        pkt.size = dataLen + spsFrameLength + ppsFrameLength;
        pkt.data = videoFrame;
        av_interleaved_write_frame(oc, &pkt);
        delete videoFrame; videoFrame = NULL;
    }
    else if (videoFrameType != 0x67 && videoFrameType != 0x68)
    {   
        // Send other frames except pps & sps which are caught and stored                   
        pkt.size = dataLen;
        pkt.data = data;
        setLength(data, dataLen-4);                     
        av_interleaved_write_frame(oc, &pkt);
    }

Наконец, чтобы закрыть файл:

av_write_trailer(oc);
int i = 0;
for (i = 0; i < oc->nb_streams; i++)
{
    av_freep(&oc->streams[i]->codec);
    av_freep(&oc->streams[i]);      
}

if (!(oc->oformat->flags & AVFMT_NOFILE))
{
    avio_close(oc->pb);
}
av_free(oc);

Если я возьму только данные H.264 и конвертирую их:

ffmpeg -i recording.h264 -vcodec copy recording.mp4

Все файлы, кроме "нижнего колонтитула", такие же.

Вывод моей программы: readrec record.tcp out.mp4 **** START **** 01-03-2013 14:26:01 180000 Вывод # 0, mp4, в 'out.mp4': Stream # 0: 0 : Видео: h264, yuv420p, 352x288, q = 2-31, 512 кб / с, 90k tbn, 25 tbc **** END **** 03.01.2013 14:27:01 102000 Написано 1499 кадров видео.

Если я попытаюсь преобразовать с помощью ffmpeg файл MP4, созданный с помощью CODE:

ffmpeg -i out.mp4 -vcodec copy out2.mp4
ffmpeg version 0.11.1 Copyright (c) 2000-2012 the FFmpeg developers
      built on Mar  7 2013 12:49:22 with suncc 0x5110
      configuration: --extra-cflags=-KPIC -g --disable-mmx
      --disable-protocol=udp --disable-encoder=nellymoser --cc=cc --cxx=CC
libavutil      51. 54.100 / 51. 54.100
libavcodec     54. 23.100 / 54. 23.100
libavformat    54.  6.100 / 54.  6.100
libavdevice    54.  0.100 / 54.  0.100
libavfilter     2. 77.100 /  2. 77.100
libswscale      2.  1.100 /  2.  1.100
libswresample   0. 15.100 /  0. 15.100
h264 @ 12eaac0] no frame!
    Last message repeated 1 times
[h264 @ 12eaac0] slice type too large (0) at 0 0
[h264 @ 12eaac0] decode_slice_header error
[h264 @ 12eaac0] no frame!
    Last message repeated 23 times
[h264 @ 12eaac0] slice type too large (0) at 0 0
[h264 @ 12eaac0] decode_slice_header error
[h264 @ 12eaac0] no frame!
    Last message repeated 74 times
[h264 @ 12eaac0] slice type too large (0) at 0 0
[h264 @ 12eaac0] decode_slice_header error
[h264 @ 12eaac0] no frame!
    Last message repeated 64 times
[h264 @ 12eaac0] slice type too large (0) at 0 0
[h264 @ 12eaac0] decode_slice_header error
[h264 @ 12eaac0] no frame!
    Last message repeated 34 times
[h264 @ 12eaac0] slice type too large (0) at 0 0
[h264 @ 12eaac0] decode_slice_header error
[h264 @ 12eaac0] no frame!
    Last message repeated 49 times
[h264 @ 12eaac0] slice type too large (0) at 0 0
[h264 @ 12eaac0] decode_slice_header error
[h264 @ 12eaac0] no frame!
    Last message repeated 24 times
[h264 @ 12eaac0] Partitioned H.264 support is incomplete
[h264 @ 12eaac0] no frame!
    Last message repeated 23 times
[h264 @ 12eaac0] sps_id out of range
[h264 @ 12eaac0] no frame!
    Last message repeated 148 times
[h264 @ 12eaac0] sps_id (32) out of range
    Last message repeated 1 times
[h264 @ 12eaac0] no frame!
    Last message repeated 33 times
[h264 @ 12eaac0] slice type too large (0) at 0 0
[h264 @ 12eaac0] decode_slice_header error
[h264 @ 12eaac0] no frame!
    Last message repeated 128 times
[h264 @ 12eaac0] sps_id (32) out of range
    Last message repeated 1 times
[h264 @ 12eaac0] no frame!
    Last message repeated 3 times
[h264 @ 12eaac0] slice type too large (0) at 0 0
[h264 @ 12eaac0] decode_slice_header error
[h264 @ 12eaac0] no frame!
    Last message repeated 3 times
[h264 @ 12eaac0] slice type too large (0) at 0 0
[h264 @ 12eaac0] decode_slice_header error
[h264 @ 12eaac0] no frame!
    Last message repeated 309 times
[h264 @ 12eaac0] sps_id (32) out of range
    Last message repeated 1 times
[h264 @ 12eaac0] no frame!
    Last message repeated 192 times
[h264 @ 12eaac0] Partitioned H.264 support is incomplete
[h264 @ 12eaac0] no frame!
    Last message repeated 73 times
[h264 @ 12eaac0] sps_id (32) out of range
    Last message repeated 1 times
[h264 @ 12eaac0] no frame!
    Last message repeated 99 times
[h264 @ 12eaac0] sps_id (32) out of range
    Last message repeated 1 times
[h264 @ 12eaac0] no frame!
    Last message repeated 197 times
[mov,mp4,m4a,3gp,3g2,mj2 @ 12e3100] decoding for stream 0 failed
[mov,mp4,m4a,3gp,3g2,mj2 @ 12e3100] Could not find codec parameters
(Video: h264 (avc1 / 0x31637661), 393539 kb/s)
out.mp4: could not find codec parameters

Я действительно не знаю, в чем проблема, за исключением того, что это должно быть связано с тем, как настраиваются потоки. Я просмотрел фрагменты кода, в которых другие люди делают то же самое, и попытался использовать этот совет при настройке потоков, но безрезультатно!


Последний код, который дал мне мультиплексированный (синхронизированный) файл H.264 / AAC, выглядит следующим образом. Сначала немного справочной информации. Данные поступают с IP-камеры. Данные представлены через сторонний API в виде видео / аудио пакетов. Видеопакеты представлены как данные полезной нагрузки RTP (без заголовка) и состоят из блоков NALU, которые реконструируются и конвертируются в видео H.264 в формате Приложения B. Аудио AAC представляется как необработанный AAC и преобразуется в формат adts для воспроизведения. Эти пакеты были помещены в формат битового потока, который позволяет передавать метку времени (64-битные миллисекунды с 1 января 1970 года) вместе с некоторыми другими вещами.

Это более или менее прототип и он не чист во всех отношениях. Вероятно, он плохо протекает. Однако я надеюсь, что это поможет кому-то еще, пытаясь достичь чего-то похожего на меня.

Глобалы:

AVFormatContext* oc = NULL;
AVCodecContext* videoContext = NULL;
AVStream* videoStream = NULL;
AVCodecContext* audioContext = NULL;
AVStream* audioStream = NULL;
AVCodec* videoCodec = NULL;
AVCodec* audioCodec = NULL;
int vi = 0;  // Video stream
int ai = 1;  // Audio stream

uint64_t firstVideoTimeStamp = 0;
uint64_t firstAudioTimeStamp = 0;
int audioStartOffset = 0;

char* filename = NULL;

Boolean first = TRUE;

int videoFrameNumber = 0;
int audioFrameNumber = 0;

Главный:

int main(int argc, char* argv[])
{
    if (argc != 3)
    {   
        cout << argv[0] << " <stream playback file> <output mp4 file>" << endl;
        return 0;
    }
    char* input_stream_file = argv[1];
    filename = argv[2];

    av_register_all();    

    fstream inFile;
    inFile.open(input_stream_file, ios::in);

    // Used to store the latest pps & sps frames
    unsigned char* ppsFrame = NULL;
    int ppsFrameLength = 0;
    unsigned char* spsFrame = NULL;
    int spsFrameLength = 0;

    // Setup MP4 output file
    AVOutputFormat* fmt = av_guess_format( 0, filename, 0 );
    oc = avformat_alloc_context();
    oc->oformat = fmt;
    strcpy(oc->filename, filename);

    // Setup the bitstream filter for AAC in adts format.  Could probably also achieve
    // this by stripping the first 7 bytes!
    AVBitStreamFilterContext* bsfc = av_bitstream_filter_init("aac_adtstoasc");
    if (!bsfc)
    {       
        cout << "Error creating adtstoasc filter" << endl;
        return -1;
    }

    while (inFile.good())
    {
        TcpAVDataBlock* block = new TcpAVDataBlock();
        block->readStruct(inFile);
        DateTime dt = block->getTimestampAsDateTime();
        switch (block->getPacketType())
        {
            case TCP_PACKET_H264:
            {       
                if (firstVideoTimeStamp == 0)
                    firstVideoTimeStamp = block->getTimeStamp();
                unsigned char* data = block->getData();
                unsigned char videoFrameType = data[4];
                int dataLen = block->getDataLen();

                // pps
                if (videoFrameType == 0x68)
                {
                    if (ppsFrame != NULL)
                    {
                        delete ppsFrame; ppsFrameLength = 0;
                        ppsFrame = NULL;
                    }
                    ppsFrameLength = block->getDataLen();
                    ppsFrame = new unsigned char[ppsFrameLength];
                    memcpy(ppsFrame, block->getData(), ppsFrameLength);
                }
                else if (videoFrameType == 0x67)
                {
                    // sps
                    if (spsFrame != NULL)
                    {
                        delete spsFrame; spsFrameLength = 0;
                        spsFrame = NULL;
                    }
                    spsFrameLength = block->getDataLen();
                    spsFrame = new unsigned char[spsFrameLength];
                    memcpy(spsFrame, block->getData(), spsFrameLength);                   
                }                                           

                if (videoFrameType == 0x65 || videoFrameType == 0x41)
                {
                    videoFrameNumber++;
                }
                // Extract a thumbnail for each I-Frame
                if (videoFrameType == 0x65)
                {
                    decodeIFrame(h264, spsFrame, spsFrameLength, ppsFrame, ppsFrameLength, data, dataLen);
                }
                if (videoStream != NULL)
                {
                    AVPacket pkt = { 0 };
                    av_init_packet(&pkt);
                    pkt.stream_index = vi;
                    pkt.flags = 0;           
                    pkt.pts = videoFrameNumber;
                    pkt.dts = videoFrameNumber;           
                    if (videoFrameType == 0x65)
                    {
                        pkt.flags = 1;                           

                        unsigned char* videoFrame = new unsigned char[spsFrameLength+ppsFrameLength+dataLen];
                        memcpy(videoFrame, spsFrame, spsFrameLength);
                        memcpy(&videoFrame[spsFrameLength], ppsFrame, ppsFrameLength);

                        memcpy(&videoFrame[spsFrameLength+ppsFrameLength], data, dataLen);
                        pkt.data = videoFrame;
                        av_interleaved_write_frame(oc, &pkt);
                        delete videoFrame; videoFrame = NULL;
                    }
                    else if (videoFrameType != 0x67 && videoFrameType != 0x68)
                    {                       
                        pkt.size = dataLen;
                        pkt.data = data;
                        av_interleaved_write_frame(oc, &pkt);
                    }                       
                }
                break;
            }

        case TCP_PACKET_AAC:

            if (firstAudioTimeStamp == 0)
            {
                firstAudioTimeStamp = block->getTimeStamp();
                uint64_t millseconds_difference = firstAudioTimeStamp - firstVideoTimeStamp;
                audioStartOffset = millseconds_difference * 16000 / 1000;
                cout << "audio offset: " << audioStartOffset << endl;
            }

            if (audioStream != NULL)
            {
                AVPacket pkt = { 0 };
                av_init_packet(&pkt);
                pkt.stream_index = ai;
                pkt.flags = 1;           
                pkt.pts = audioFrameNumber*1024;
                pkt.dts = audioFrameNumber*1024;
                pkt.data = block->getData();
                pkt.size = block->getDataLen();
                pkt.duration = 1024;

                AVPacket newpacket = pkt;                       
                int rc = av_bitstream_filter_filter(bsfc, audioContext,
                    NULL,
                    &newpacket.data, &newpacket.size,
                    pkt.data, pkt.size,
                    pkt.flags & AV_PKT_FLAG_KEY);

                if (rc >= 0)
                {
                    //cout << "Write audio frame" << endl;
                    newpacket.pts = audioFrameNumber*1024;
                    newpacket.dts = audioFrameNumber*1024;
                    audioFrameNumber++;
                    newpacket.duration = 1024;                   

                    av_interleaved_write_frame(oc, &newpacket);
                    av_free_packet(&newpacket);
                }   
                else
                {
                    cout << "Error filtering aac packet" << endl;

                }
            }
            break;

        case TCP_PACKET_START:
            break;

        case TCP_PACKET_END:
            break;
        }
        delete block;
    }
    inFile.close();

    av_write_trailer(oc);
    int i = 0;
    for (i = 0; i < oc->nb_streams; i++)
    {
        av_freep(&oc->streams[i]->codec);
        av_freep(&oc->streams[i]);       
    }

    if (!(oc->oformat->flags & AVFMT_NOFILE))
    {
        avio_close(oc->pb);
    }

    av_free(oc);

    delete spsFrame; spsFrame = NULL;
    delete ppsFrame; ppsFrame = NULL;

    cout << "Wrote " << videoFrameNumber << " video frames." << endl;

    return 0;
}

Поток потока / кодеки добавляются, а заголовок создается в функции addVideoAndAudioStream (). Эта функция вызывается из decodeIFrame (), поэтому есть несколько предположений (которые не обязательно верны) 1. Первым идет видеопакет 2. Присутствует AAC

DecodeIFrame был своего рода отдельным прототипом, где я создавал эскиз для каждого I-кадра. Код для создания эскизов был из: https://gnunet.org/svn/Extractor/src/plugins/thumbnailffmpeg_extractor.c.

Функция decodeIFrame передает AVCodecContext в addVideoAudioStream:

void addVideoAndAudioStream(AVCodecContext* decoder = NULL)
{
    videoStream = av_new_stream(oc, 0);
    if (!videoStream)
    {
        cout << "ERROR creating video stream" << endl;
        return;       
    }
    vi = videoStream->index;   
    videoContext = videoStream->codec;       
    videoContext->codec_type = AVMEDIA_TYPE_VIDEO;
    videoContext->codec_id = decoder->codec_id;
    videoContext->bit_rate = 512000;
    videoContext->width = decoder->width;
    videoContext->height = decoder->height;
    videoContext->time_base.den = 25;
    videoContext->time_base.num = 1;
    videoContext->gop_size = decoder->gop_size;
    videoContext->pix_fmt = decoder->pix_fmt;       

    audioStream = av_new_stream(oc, 1);
    if (!audioStream)
    {
        cout << "ERROR creating audio stream" << endl;
        return;
    }
    ai = audioStream->index;
    audioContext = audioStream->codec;
    audioContext->codec_type = AVMEDIA_TYPE_AUDIO;
    audioContext->codec_id = CODEC_ID_AAC;
    audioContext->bit_rate = 64000;
    audioContext->sample_rate = 16000;
    audioContext->channels = 1;

    if (oc->oformat->flags & AVFMT_GLOBALHEADER)
    {
        videoContext->flags |= CODEC_FLAG_GLOBAL_HEADER;
        audioContext->flags |= CODEC_FLAG_GLOBAL_HEADER;
    }

    av_dump_format(oc, 0, filename, 1);

    if (!(oc->oformat->flags & AVFMT_NOFILE))
    {
        if (avio_open(&oc->pb, filename, AVIO_FLAG_WRITE) < 0) {
            cout << "Error opening file" << endl;
        }
    }

    avformat_write_header(oc, NULL);
}

Насколько я могу судить, ряд предположений не имел значения, например: 1. Скорость передачи данных. Фактическая скорость передачи видео была ~ 262k, тогда как я указал 2 канала AAC 512kbit. Я указал моно, хотя фактический выход был стерео из памяти.

Вам все равно нужно знать, какова частота кадров (временная база) для видео и аудио.

В отличие от множества других примеров, при установке pts & dts для видеопакетов он не воспроизводился. Мне нужно было знать временную базу (25 кадров в секунду), а затем установить pts & dts в соответствии с этой временной базой, то есть первый кадр = 0 (PPS, SPS, I), второй кадр = 1 (промежуточный кадр, как бы он ни назывался;)) .

AAC Мне также пришлось сделать предположение, что это 16000 Гц. 1024 сэмпла на пакет AAC (я думаю, вы также можете иметь сэмплы AAC @ 960) для определения «смещения» звука. Я добавил это в pts & dts. Таким образом, pts / dts - это номер сэмпла, под которым он должен воспроизводиться. Перед записью также необходимо убедиться, что в пакете установлена ​​длительность 1024.

--

Сегодня я обнаружил, что Приложение B на самом деле несовместимо с каким-либо другим плеером, поэтому действительно следует использовать формат AVCC.

Эти URL-адреса помогли: Проблема декодирования видео H264 через RTP с помощью ffmpeg (libavcodec) http://aviadr1.blogspot.com.au/2010/05/h264-extradata-parfully-explained-for.html

При построении видеопотока я заполнил extradata & extradata_size:

// Extradata contains PPS & SPS for AVCC format
int extradata_len = 8 + spsFrameLen-4 + 1 + 2 + ppsFrameLen-4;
videoContext->extradata = (uint8_t*)av_mallocz(extradata_len);
videoContext->extradata_size = extradata_len;
videoContext->extradata[0] = 0x01;
videoContext->extradata[1] = spsFrame[4+1];
videoContext->extradata[2] = spsFrame[4+2];
videoContext->extradata[3] = spsFrame[4+3];
videoContext->extradata[4] = 0xFC | 3;
videoContext->extradata[5] = 0xE0 | 1;
int tmp = spsFrameLen - 4;
videoContext->extradata[6] = (tmp >> 8) & 0x00ff;
videoContext->extradata[7] = tmp & 0x00ff;
int i = 0;
for (i=0;i<tmp;i++)
    videoContext->extradata[8+i] = spsFrame[4+i];
videoContext->extradata[8+tmp] = 0x01;
int tmp2 = ppsFrameLen-4;   
videoContext->extradata[8+tmp+1] = (tmp2 >> 8) & 0x00ff;
videoContext->extradata[8+tmp+2] = tmp2 & 0x00ff;
for (i=0;i<tmp2;i++)
    videoContext->extradata[8+tmp+3+i] = ppsFrame[4+i];

При записи кадров не добавляйте кадры SPS и PPS, просто записывайте кадры I Frame и P. Кроме того, замените начальный код Приложения B, содержащийся в первых 4 байтах (0x00 0x00 0x00 0x01), размером кадра I / P.


person Brad Mitchell    schedule 07.03.2013    source источник
comment
Почему вы комбинируете SPS + PPS + I-frame вместе для письма? Кроме того, может быть ответственна функция setLength(), но это маловероятно, если ваш двоичный файл, сравниваемый с выводом повторного пакетирования командной строки ffmpeg, не показывает различий в потоке.   -  person Alex Cohn    schedule 07.03.2013
comment
Об объединении SPS, PPS и I Frame мы думали позже. У меня они изначально были отдельно, и тогда это тоже не сработало. Я объединил их, потому что, когда я выполняю декодирование в iframe, ему нужны sps и pps для декодирования, и он не берет их по отдельности. setLength () просто заменяет начальный код длиной 32 бита, и, как вы сказали, ничего не изменится до нижнего колонтитула.   -  person Brad Mitchell    schedule 07.03.2013
comment
Можно комбинировать SPS и PPS для декодера, но это может быть опасно для мультиплексора (формат mp4). Я также считаю, что когда вы отправляете срезы в мультиплексор, вы должны убрать заголовок NALU.   -  person Alex Cohn    schedule 07.03.2013
comment
Заголовок NALU отсутствует, поскольку он уже удален. Формат, отправляемый в декодер, - это приложение B. Начальный код приложения B (4 байта 0x00 0x00 0x00 0x01) заменяется длиной пакета H.264, что согласуется с форматом, который я вижу в MP4, созданном ffmpeg. . Я изменю код обратно, чтобы не объединять SPS и PPS с I Frame для файла MP4. Даже при таком создании файла MP4 он все равно не будет правильно воспроизводить / декодировать или обрабатывать с помощью ffmpeg.   -  person Brad Mitchell    schedule 08.03.2013
comment
Итак, наконец-то я получил результат. Я решил прочитать поток h264 из файла с помощью av_read_packet и записать его, используя тот же метод, поэтому никаких изменений в пакетах и ​​т. Д. Av_read_packet считал из файла h.264 кадр SPS + PPS + I. Я записал это в файл MP4. Это было играбельно. Я не думал, что вы можете встроить формат приложения B в MP4, но это доказало мою неправоту. Шестнадцатеричный дамп показывает стартовые коды 00 00 00 01. Я удалил setLength, чтобы сохранить стартовые коды, и это работает, однако мне нужно установить значение pts / dts, иначе оно будет воспроизводиться только в течение секунды.   -  person Brad Mitchell    schedule 08.03.2013
comment
откуда взять spsFrame и ppsFrame?   -  person Bill Yan    schedule 25.03.2015
comment
Кадры SPS и PPS обычно идут перед I кадром.   -  person Brad Mitchell    schedule 29.04.2015


Ответы (1)


Пожалуйста, позвольте мне подвести итог: проблема с вашим (исходным) кодом заключалась в том, что ввод в av_interleaved_write_frame() не должен начинаться с длины пакета. Файл может быть воспроизведен, если вы не удалите 00 00 00 01 стартовые коды, но это ИМХО является устойчивым поведением игрока, и я бы не стал на это рассчитывать.

person Alex Cohn    schedule 08.03.2013
comment
Вид. Использование ffmpeg для преобразования, т.е. ffmpeg -i file.h264 -vcodec copy out.mp4, похоже, заменяет начальный код (00 00 00 01) на длину кадра. По крайней мере, это соответствовало шестнадцатеричному дампу файла. Попытка сделать то же самое из кода по какой-то причине не сработала. Это должно быть связано со способом настройки потока или даже со значениями pts / dts, я не уверен. Однако не разборка окончательно исправила это. Я должен вставить код того, что у меня есть, просто чтобы показать окончательный результат, поскольку я получил синхронизацию видео и аудио в файле MP4. Закачу через несколько часов. - person Brad Mitchell; 09.03.2013
comment
Спасибо за помощь, Алекс. Ваши комментарии о стартовом коде заставили меня задуматься о том, чтобы взглянуть на него под другим углом, поэтому я воспринимаю это как ответ. - person Brad Mitchell; 09.03.2013