H.264 muxed en MP4 en utilisant libavformat ne pas lire

Je suis en train de mux H. 264 de données en un fichier MP4. Il semble qu'il y ait pas d'erreurs dans l'enregistrement de ce H. 264 Annexe B données à un fichier MP4, mais le fichier ne parvient pas à la lecture.

J'ai fait une comparaison binaire sur les fichiers et le problème semble être quelque part dans ce qui est écrit dans le pied de page (semi-remorque) d'un fichier MP4.

Je la soupçonne d'être quelque chose avec la façon dont le flux est en cours de création ou de quelque chose.

Init:

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

Partie de ce prototype d'application que j'ai est la création d'un fichier png pour chaque IFrame. Ainsi, lorsque la première IFrame est rencontrés, j'ai créer le flux vidéo et écrire l'en-tête av etc:

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);
}

J'écris des paquets:

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);
}

Enfin pour fermer le fichier off:

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);

Si je prends le H. 264 seul et de le convertir:

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

Tous, mais le "pied de page" des fichiers sont les mêmes.

Sortie de mon programme:
readrec l'enregistrement.tcp.mp4
**** DÉBUT de la **** 01-03-2013 14:26:01 180000
Sortie de #0, mp4,'.mp4":
Flux de #0:0: Video: h264, yuv420p, 352x288, q=2-31, 512 kb/s, 90k d'alcalinité, de 25 à confirmer
**** FIN **** 01-03-2013 14:27:01 102000
Écrit 1499 des images vidéo.

Si j'essaie de le convertir à l'aide de ffmpeg le MP4 fichier créé à l'aide de 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

Je ne sais vraiment pas où le problème, sauf qu'il y a quelque chose à voir avec la façon dont les flux sont mis en place. J'ai regardé les bits de code à partir de là où d'autres personnes sont en train de faire quelque chose de similaire, et essayé d'utiliser ces conseils dans la mise en place du cours d'eau, mais en vain!


Le code final qui m'a donné un H. 264/AAC multiplexé (synchronisé) est comme suit. D'abord un peu d'information de fond. Les données proviennent d'une caméra IP. Les données sont présentées par l'intermédiaire d'un 3ème partie de l'API en vidéo/audio de paquets. La vidéo paquets sont présentés comme la RTP données de la charge utile (sans en-tête) et sont composées de NALU qui sont reconstruit et transformé H. 264 vidéo à l'Annexe B format. Audio AAC est présenté comme raw AAC pour se convertir adts format pour permettre la lecture. Ces paquets ont été mis dans un format bitstream qui permet la transmission de l'horodatage (64 bits nombre de millisecondes depuis le 1er janvier 1970) ainsi que quelques autres choses.

C'est plus ou moins un prototype et n'est pas propre à tous les égards. Il a probablement des fuites mauvais. Je dois toutefois, espérons que cela aide quelqu'un d'autre à essayer d'obtenir quelque chose de similaire à ce que je suis.

Globals:

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;

Principal:

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;
}

Le flux stream/codecs sont ajoutés et l'en-tête est créé dans une fonction appelée addVideoAndAudioStream(). Cette fonction est appelée depuis decodeIFrame (), alors il existe quelques hypothèses (qui ne sont pas nécessairement bon)
1. Une vidéo de paquet vient en premier
2. AAC est présent

La decodeIFrame était un peu distincte d'un prototype par où j'étais en train de créer une vignette pour chaque Image I. Le code pour générer des miniatures a partir de: https://gnunet.org/svn/Extractor/src/plugins/thumbnailffmpeg_extractor.c

La decodeIFrame fonction passe un AVCodecContext en 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);
}

Aussi loin que je peux dire, un certain nombre d'hypothèses ne semble pas à la matière, par exemple:
1. Le Débit Binaire. La vidéo débit binaire ~262k alors que j'ai spécifié 512kbit
2. AAC canaux. J'ai précisé mono, bien que la production réelle a été Stéréo à partir de la mémoire

Vous auriez encore besoin de savoir ce que le taux de trame (base de temps) est pour la vidéo & audio.

Contrairement à beaucoup d'autres exemples, lors de la mise pts & dts sur la vidéo paquets, il n'était pas jouable. J'ai besoin de savoir la base de temps (25fps), puis définissez le pts & dts selon que la base de temps, c'est à dire de la première frame = 0 (PPS, SPS, I), deuxième frame = 1 (cadre intermédiaire, quel que soit son nom ;)).

AAC j'ai également eu à faire l'hypothèse que c'était 16000 hz. 1024 échantillons par AAC paquet (Vous pouvez également AAC @ 960 échantillons je crois) pour déterminer l'audio "offset". J'ai ajouté ceci à la pts & dts. De sorte que le pts/dts sont le numéro de l'échantillon qu'il est lu. Vous devez également vous assurer que la durée de 1024 est situé dans le paquet avant de l'écrire aussi.

--

J'ai trouvé de plus aujourd'hui que l'Annexe B n'est pas vraiment compatible avec n'importe quel autre joueur AVCC format devrait vraiment être utilisé.

Ces URL aidé:
Problème de Décoder de la vidéo H264 sur RTP avec ffmpeg (libavcodec)
http://aviadr1.blogspot.com.au/2010/05/h264-extradata-partially-explained-for.html

Lors de la construction de la diffusion de la vidéo, j'ai rempli le 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];

Lors de l'écriture du cadres de, ne pas ajouter le SPS & PPS images, il suffit d'écrire l'Image I & P trames. En outre, remplacer l'Annexe B code de départ contenues dans les 4 premiers octets (0x00 0x00 0x00 0x01) avec la taille de l'I/P image.

source d'informationauteur Brad Mitchell