H.264データをMP4ファイルに多重化しようとしています。このH.264 Annex BのデータをMP4ファイルに保存する際にエラーはないようですが、ファイルは再生に失敗します。libabformatを使用してH.264をMP4に多重化して再生しない
私はファイルのバイナリ比較を行いましたが、問題はMP4ファイルのフッター(トレーラー)に書き込まれている場所のどこかにあるようです。
私はそれがストリームが作成されている方法など何かでなければならないと考えます。
INIT:私は、各iframe対応のPNGファイルを作成されている。このプロトタイプアプリの
AVOutputFormat* fmt = av_guess_format(0, "out.mp4", 0);
oc = avformat_alloc_context();
oc->oformat = fmt;
strcpy(oc->filename, filename);
パート。最初の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 recording.tcpのout.mp4 **** START **** 2013年1月3日午前14時26分01秒180000 出力#0、MP4、out.mp4」へ': ストリーム#0:0:ビデオ:h264、yuv420p、352x288、q = 2-31,512 kb/s、90k tbn、25 tbc **** END **** 01-03-2013 14: 27:01 102000 1499ビデオフレームを書きました。
私は、MP4ファイルは、コードを使用して作成ffmpegの使用して変換しようとした場合:問題がある場合、それはストリームがされている方法とは何かしなければなら除き、私は本当に、知らない
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 muxed(synced)ファイルを与えた最後のコードは次のとおりです。最初に背景情報のビット。データはIPカメラからのものです。データは、ビデオ/オーディオパケットとして第三者APIを介して表示されます。ビデオパケットは、RTPペイロードデータ(ヘッダなし)として提示され、再構成され、Annex BフォーマットのH.264ビデオに変換されたNALUで構成される。 AACオーディオは生のAACとして表示され、再生を可能にするためにadts形式に変換されます。これらのパケットは、タイムスタンプ(1970年1月1日以降の64ビットのミリ秒)の送信を可能にするビットストリーム形式に入れられており、他のいくつかのものと一緒になっています。
これは多かれ少なかれプロトタイプであり、どの点でもきれいではありません。それはおそらく悪いリークです。私はしかし、これは他の誰かが私のような何かを達成しようとするのを助けることを願っています。
グローバル:
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;
}
)ストリームstream /コーデックを加え、ヘッダはaddVideoAndAudioStream(呼び出された関数で作成されています。この関数は、(decodeIFrameから呼び出される)は(必ずしも良好ではない)いくつかの仮定 1は、ビデオパケットが存在する第一 2 AACはどこによってdecodeIFrameは別個プロトタイプのようなものであった
存在してい私は各Iフレームのサムネイルを作成していました。 https://gnunet.org/svn/Extractor/src/plugins/thumbnailffmpeg_extractor.c
decodeIFrame機能がaddVideoAudioStreamにAVCodecContextを渡します:サムネイルを生成するためのコードからだった
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でしたが、私は512kbitを指定しました。 2. AACチャンネル。私はモノラルを指定しましたが、実際の出力はメモリからのステレオでした
ビデオのフレームレート(タイムベース)は、まだ&のオーディオであることが分かります。
多くの他の例とは対照的に、ビデオパケットにpts & dtsを設定すると、再生できませんでした。最初のフレーム= 0(PPS、SPS、I)、2番目のフレーム= 1(中間フレーム、それが呼び出されたものであっても)、タイムベース(25fps)を知ってから、そのタイムベース、すなわち、& dtsに従って、 。
AAC私は16000hzと仮定しなければなりませんでした。 AACパケットごとに1024サンプル(私が思うとAAC @ 960のサンプルを持つこともできます)で、オーディオの「オフセット」を決定します。これをpts & dtsに追加しました。だから、pts/dtsはそれを再生するサンプル番号です。また、書き込む前に、パケットの長さが1024に設定されていることも確認する必要があります。
-
私は、附属書Bは、フォーマットが実際に使用しなければならないので、AVCC、他のプレーヤーと実際に互換性がないことを、今日さらに発見しました。
これらのURLは助け:
// 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 &:Problem to Decode H264 video over RTP with ffmpeg (libavcodec) http://aviadr1.blogspot.com.au/2010/05/h264-extradata-partially-explained-for.html
ビデオストリームを構築 は、私がextradata & extradata_sizeを記入しましたPPSフレームでは、Iフレーム&のPフレームを書き出すだけです。さらに、最初の4バイト(0x00 0x00 0x00 0x01)に含まれるAnnex BスタートコードをI/Pフレームのサイズに置き換えます。
なぜSPS + PPS + Iフレームを組み合わせて書き込みますか?また、 'setLength()'関数が原因である可能性もありますが、ffmpegコマンドライン再出力のバイナリ比較とストリームの違いがない場合はそうなりません。 –
SPSとPPSとI Frameを組み合わせることは後の考察でした。私はそれらを最初に別々にしていましたが、どちらもうまくいかなかったのです。私はiframeでデコードするときに、デコードするためにspsとppsが必要だったので、それらを別々に取らなかったので、それらを組み合わせました。 setLength()は、開始コードを32ビット長で置き換えるだけです。あなたが言ったように、フッターまでは何も変わりません。 –
デコーダにはSPSとPPSを組み合わせても問題ありませんが、マルチプレクサ(MP4形式)では危険です。私はまた、あなたがマルチプレクサにスライスを送るとき、あなたはNALUヘッダーを取り除くべきだと信じています。 –