Due to an unexpected need in the company project, it is required to extract H.264 from an MP4 file. It is recommended to use the open-source tool Bento4.
In most embedded Linux environments, only pure C source code is supported.
About Bento4:
A fast, modern, open source C++ toolkit for all your MP4 and DASH/HLS/CMAF media format needs.
https://www.bento4.com/downloads/
Import Bento4 to buildroot
- at buildroot/src/package/Config.in
source "package/bento4/Config.in"
- create src/package/bento4 folder
add:
2.1 Config.in
config BR2_PACKAGE_BENTO4
bool "bento4"
depends on BR2_INSTALL_LIBSTDCPP
help
Bento4 is a C++ class library designed to read and write
ISO-MP4 files.
https://www.bento4.com/
comment "bento4 support needs a toolchain with C++"
depends on !BR2_INSTALL_LIBSTDCPP
2.2 bento4.hash
# Locally calculated
sha256 9f3eb912207d7ed9c1e6e05315083404b32a11f8aacd604a9b2bdcb10bf79eb9 bento4-1.6.0-639.tar.gz
sha256 7daae92c8628ada28def8d096fe2fde298b72ec3e2d64a3c408afce38edb361b Documents/LICENSE.txt
2.3 bento4.mk
################################################################################
#
# bento4
#
################################################################################
BENTO4_VERSION = 1.6.0-639
BENTO4_SITE = $(call github,axiomatic-systems,Bento4,v$(BENTO4_VERSION))
BENTO4_INSTALL_STAGING = YES
BENTO4_LICENSE = GPL-2.0+
BENTO4_LICENSE_FILES = Documents/LICENSE.txt
BENTO4_CPE_ID_VENDOR = axiosys
# Source/C++/Core/Ap4Config.h
ifeq ($(BR2_ENDIAN),"BIG")
BENTO4_BYTE_ORDER = 0
else
BENTO4_BYTE_ORDER = 1
endif
BENTO4_CONF_OPTS += \
-DBUILD_APPS=OFF \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_CXX_FLAGS="$(TARGET_CXXFLAGS) -std=c++11 -fPIC -DAP4_PLATFORM_BYTE_ORDER=$(BENTO4_BYTE_ORDER)"
$(eval $(cmake-package))
- Add all .patch to src/package/bento4/
list
Cover C++ interface to C
My plan is to repackage the MP4 to H264/AAC API into a pure C interface. All I need:
header | extract.h |
src | Mp42Aac_extract.cpp |
src | Mp42Aac_extract.cpp |
Makefile | Makefile |
Header file
src/Mp4Extract/extract.h
#ifndef __EXTRACT_H__
#define __EXTRACT_H__
#ifdef __cplusplus
extern "C" {
#endif
int AVC_extract_avc(int argc, char **argv);
int AVC_getSampleNum(char *file_name, int *num);
int AVC_getFrameData(char *file_name, int index, char *ptr, int *frame_len);
int AVC_releaseExtractor();
int extract_aac(int argc, char **argv);
int AAC_getSampleNum(char *file_name, int *num);
int AAC_getFrameData(int index, char *ptr, int *frame_len);
int AAC_releaseExtractor();
#ifdef __cplusplus
}
#endif
#endif
src/Mp4Extract/Mp42Aac_extract.cpp
/*****************************************************************
|
| AP4 - MP4 to AAC File Converter
|
| Copyright 2002-2008 Axiomatic Systems, LLC
|
|
| This file is part of Bento4/AP4 (MP4 Atom Processing Library).
|
| Unless you have obtained Bento4 under a difference license,
| this version of Bento4 is Bento4|GPL.
| Bento4|GPL is free software; you can redistribute it and/or modify
| it under the terms of the GNU General Public License as published by
| the Free Software Foundation; either version 2, or (at your option)
| any later version.
|
| Bento4|GPL is distributed in the hope that it will be useful,
| but WITHOUT ANY WARRANTY; without even the implied warranty of
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
| GNU General Public License for more details.
|
| You should have received a copy of the GNU General Public License
| along with Bento4|GPL; see the file COPYING. If not, write to the
| Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
| 02111-1307, USA.
|
****************************************************************/
/*----------------------------------------------------------------------
| includes
+---------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include "Ap4.h"
#include "extract.h"
/*----------------------------------------------------------------------
| constants
+---------------------------------------------------------------------*/
#define BANNER \
"MP4 To AAC File Converter - Version 1.0\n" \
"(Bento4 Version " AP4_VERSION_STRING ")\n" \
"(c) 2002-2008 Axiomatic Systems, LLC"
/*----------------------------------------------------------------------
| PrintUsageAndExit
+---------------------------------------------------------------------*/
static void PrintUsageAndExit()
{
fprintf(stderr, BANNER "\n\nusage: mp42aac [options] <input> <output>\n"
" Options:\n"
" --key <hex>: 128-bit decryption key (in hex: 32 chars)\n");
exit(1);
}
/*----------------------------------------------------------------------
| GetSamplingFrequencyIndex
+---------------------------------------------------------------------*/
static unsigned int GetSamplingFrequencyIndex(unsigned int sampling_frequency)
{
switch (sampling_frequency) {
case 96000:
return 0;
case 88200:
return 1;
case 64000:
return 2;
case 48000:
return 3;
case 44100:
return 4;
case 32000:
return 5;
case 24000:
return 6;
case 22050:
return 7;
case 16000:
return 8;
case 12000:
return 9;
case 11025:
return 10;
case 8000:
return 11;
case 7350:
return 12;
default:
return 0;
}
}
/*----------------------------------------------------------------------
| WriteAdtsHeader
+---------------------------------------------------------------------*/
static AP4_Result WriteAdtsHeader(AP4_ByteStream *output, unsigned int frame_size,
unsigned int sampling_frequency_index, unsigned int channel_configuration)
{
unsigned char bits[7];
bits[0] = 0xFF;
bits[1] = 0xF1; // 0xF9 (MPEG2)
#if (1)
channel_configuration = 1; // Mono
bits[2] = 0x40 | (sampling_frequency_index << 2) | (channel_configuration >> 2);
bits[3] = ((channel_configuration & 0x3) << 6) | ((frame_size + 7) >> 11);
bits[4] = ((frame_size + 7) >> 3) & 0xFF;
bits[5] = (((frame_size + 7) << 5) & 0xFF) | 0x1F;
bits[6] = 0xFC;
#else
int profile = 2; // AAC LC
int freqIdx = 11; // 8KHz
int chanCfg = 1; // mono
bits[2] = 0x40 | (freqIdx << 2) | (chanCfg >> 2);
bits[3] = ((chanCfg & 0x3) << 6) | ((frame_size + 7) >> 11);
bits[4] = ((frame_size + 7) >> 3) & 0xFF;
bits[5] = (((frame_size + 7) << 5) & 0xFF) | 0x1F;
#endif
return output->Write(bits, 7);
/*
0: syncword 12 always: '111111111111'
12: ID 1 0: MPEG-4, 1: MPEG-2
13: layer 2 always: '00'
15: protection_absent 1
16: profile 2
18: sampling_frequency_index 4
22: private_bit 1
23: channel_configuration 3
26: original/copy 1
27: home 1
28: emphasis 2 only if ID == 0
ADTS Variable header: these can change from frame to frame
28: copyright_identification_bit 1
29: copyright_identification_start 1
30: aac_frame_length 13 length of the frame including header (in bytes)
43: adts_buffer_fullness 11 0x7FF indicates VBR
54: no_raw_data_blocks_in_frame 2
ADTS Error check
crc_check 16 only if protection_absent == 0
*/
}
static AP4_Result WriteAdtsHeaderPtr(unsigned int frame_size, unsigned int sampling_frequency_index,
unsigned int channel_configuration, char *ptr)
{
unsigned char bits[7];
bits[0] = 0xFF;
bits[1] = 0xF1; // 0xF9 (MPEG2)
channel_configuration = 1; // Mono
bits[2] = 0x40 | (sampling_frequency_index << 2) | (channel_configuration >> 2);
bits[3] = ((channel_configuration & 0x3) << 6) | ((frame_size + 7) >> 11);
bits[4] = ((frame_size + 7) >> 3) & 0xFF;
bits[5] = (((frame_size + 7) << 5) & 0xFF) | 0x1F;
bits[6] = 0xFC;
memcpy(ptr, &bits[0], 7);
return 0;
}
/*----------------------------------------------------------------------
| DecryptAndWriteSamples
+---------------------------------------------------------------------*/
static void DecryptAndWriteSamples(AP4_Track *track, AP4_SampleDescription *sdesc, AP4_Byte *key,
AP4_ByteStream *output)
{
AP4_ProtectedSampleDescription *pdesc = AP4_DYNAMIC_CAST(AP4_ProtectedSampleDescription, sdesc);
if (pdesc == NULL) {
fprintf(stderr, "ERROR: unable to obtain cipher info\n");
return;
}
AP4_AudioSampleDescription *audio_desc =
AP4_DYNAMIC_CAST(AP4_AudioSampleDescription, pdesc->GetOriginalSampleDescription());
if (audio_desc == NULL) {
fprintf(stderr, "ERROR: sample description is not audio\n");
return;
}
unsigned int sampling_frequency_index = GetSamplingFrequencyIndex(audio_desc->GetSampleRate());
unsigned int channel_configuration = audio_desc->GetChannelCount();
// create the decrypter
AP4_SampleDecrypter *decrypter = AP4_SampleDecrypter::Create(pdesc, key, 16);
if (decrypter == NULL) {
fprintf(stderr, "ERROR: unable to create decrypter\n");
return;
}
AP4_Sample sample;
AP4_DataBuffer encrypted_data;
AP4_DataBuffer decrypted_data;
AP4_Ordinal index = 0;
AP4_UI32 poolid = 0;
while (AP4_SUCCEEDED(track->ReadSample(index, sample, encrypted_data))) {
if (AP4_FAILED(decrypter->DecryptSampleData(poolid, encrypted_data, decrypted_data, NULL))) {
fprintf(stderr, "ERROR: failed to decrypt sample\n");
return;
}
WriteAdtsHeader(output, decrypted_data.GetDataSize(), sampling_frequency_index, channel_configuration);
output->Write(decrypted_data.GetData(), decrypted_data.GetDataSize());
index++;
}
}
/*----------------------------------------------------------------------
| WriteSamples
+---------------------------------------------------------------------*/
static void WriteSamples(AP4_Track *track, AP4_SampleDescription *sdesc, AP4_ByteStream *output)
{
AP4_AudioSampleDescription *audio_desc = AP4_DYNAMIC_CAST(AP4_AudioSampleDescription, sdesc);
if (audio_desc == NULL) {
fprintf(stderr, "ERROR: sample description is not audio\n");
return;
}
unsigned int sampling_frequency_index = GetSamplingFrequencyIndex(audio_desc->GetSampleRate());
unsigned int channel_configuration = audio_desc->GetChannelCount();
AP4_Sample sample;
AP4_DataBuffer data;
AP4_Ordinal index = 0;
while (AP4_SUCCEEDED(track->ReadSample(index, sample, data))) {
printf("[%d]freq idx:%d, sample:%d, data;%d\n", index, sampling_frequency_index, sample.GetSize(),
data.GetDataSize());
WriteAdtsHeader(output, sample.GetSize(), sampling_frequency_index, channel_configuration);
output->Write(data.GetData(), data.GetDataSize());
index++;
}
}
static void getSingleSample(AP4_Track *track, AP4_SampleDescription *sdesc, int index, char *ptr, int *frame_len)
{
AP4_AudioSampleDescription *audio_desc = AP4_DYNAMIC_CAST(AP4_AudioSampleDescription, sdesc);
if (audio_desc == NULL) {
fprintf(stderr, "ERROR: sample description is not audio\n");
return;
}
unsigned int sampling_frequency_index = GetSamplingFrequencyIndex(audio_desc->GetSampleRate());
unsigned int channel_configuration = audio_desc->GetChannelCount();
AP4_Sample sample;
AP4_DataBuffer data;
AP4_Ordinal aacindex = index;
if (AP4_SUCCEEDED(track->ReadSample(aacindex, sample, data))) {
WriteAdtsHeaderPtr(sample.GetSize(), sampling_frequency_index, channel_configuration, ptr);
*frame_len = (int)(data.GetDataSize() + 7);
memcpy(ptr + 7, (char *)data.GetData(), (int)data.GetDataSize());
}
}
/*----------------------------------------------------------------------
| main
+---------------------------------------------------------------------*/
int extract_aac(int argc, char **argv)
{
int return_value = 1;
if (argc < 3) {
PrintUsageAndExit();
}
// parse command line
AP4_Result result;
char **args = argv + 1;
unsigned char key[16];
bool key_option = false;
if (!strcmp(*args, "--key")) {
if (argc != 5) {
fprintf(stderr, "ERROR: invalid command line\n");
return 1;
}
++args;
if (AP4_ParseHex(*args++, key, 16)) {
fprintf(stderr, "ERROR: invalid hex format for key\n");
return 1;
}
key_option = true;
}
AP4_ByteStream *input = NULL;
AP4_File *input_file = NULL;
AP4_ByteStream *output = NULL;
AP4_Movie *movie = NULL;
AP4_Track *audio_track = NULL;
// create the input stream
result = AP4_FileByteStream::Create(*args++, AP4_FileByteStream::STREAM_MODE_READ, input);
if (AP4_FAILED(result)) {
fprintf(stderr, "ERROR: cannot open input (%d)\n", result);
goto end;
}
// create the output stream
result = AP4_FileByteStream::Create(*args++, AP4_FileByteStream::STREAM_MODE_WRITE, output);
if (AP4_FAILED(result)) {
fprintf(stderr, "ERROR: cannot open output (%d)\n", result);
goto end;
}
// open the file
input_file = new AP4_File(*input);
// get the movie
AP4_SampleDescription *sample_description;
movie = input_file->GetMovie();
if (movie == NULL) {
fprintf(stderr, "ERROR: no movie in file\n");
goto end;
}
// get the audio track
audio_track = movie->GetTrack(AP4_Track::TYPE_AUDIO);
if (audio_track == NULL) {
fprintf(stderr, "ERROR: no audio track found\n");
goto end;
}
// check that the track is of the right type
sample_description = audio_track->GetSampleDescription(0);
if (sample_description == NULL) {
fprintf(stderr, "ERROR: unable to parse sample description\n");
goto end;
}
// show info
AP4_Debug("Audio Track:\n");
AP4_Debug(" duration: %u ms\n", (int)audio_track->GetDurationMs());
AP4_Debug(" sample count: %u\n", (int)audio_track->GetSampleCount());
switch (sample_description->GetType()) {
case AP4_SampleDescription::TYPE_MPEG: {
WriteSamples(audio_track, sample_description, output);
return_value = 0;
break;
}
case AP4_SampleDescription::TYPE_PROTECTED:
if (!key_option) {
fprintf(stderr, "ERROR: encrypted tracks require a key\n");
return_value = 1;
break;
}
DecryptAndWriteSamples(audio_track, sample_description, key, output);
result = 0;
break;
default:
fprintf(stderr, "ERROR: unsupported sample type\n");
return_value = 1;
break;
}
end:
delete input_file;
if (input)
input->Release();
if (output)
output->Release();
return return_value;
}
/*reuse global constuctor*/
AP4_ByteStream *g_input = NULL;
AP4_File *g_input_file = NULL;
AP4_Movie *g_movie = NULL;
AP4_Track *g_audio_track = NULL;
int AAC_getSampleNum(char *file_name, int *num)
{
AP4_Result result;
int return_value = 1;
// create the input stream
result = AP4_FileByteStream::Create(file_name, AP4_FileByteStream::STREAM_MODE_READ, g_input);
if (AP4_FAILED(result)) {
fprintf(stderr, "ERROR: cannot open input (%d)\n", result);
goto end;
}
// open the file
g_input_file = new AP4_File(*g_input);
g_movie = g_input_file->GetMovie();
if (g_movie == NULL) {
fprintf(stderr, "ERROR: no movie in file\n");
goto end;
}
// get the audio track
g_audio_track = g_movie->GetTrack(AP4_Track::TYPE_AUDIO);
if (g_audio_track == NULL) {
fprintf(stderr, "ERROR: no audio track found\n");
goto end;
}
AP4_Debug(" duration: %u ms\n", (int)g_audio_track->GetDurationMs());
//AP4_Debug(" sample count: %u\n", (int)audio_track->GetSampleCount());
*num = (int)g_audio_track->GetSampleCount();
end:
return return_value;
}
int AAC_getFrameData(int index, char *ptr, int *frame_len)
{
int return_value = 1;
AP4_SampleDescription *sample_description;
// check that the track is of the right type
sample_description = g_audio_track->GetSampleDescription(0);
if (sample_description == NULL) {
fprintf(stderr, "ERROR: unable to parse sample description\n");
goto end;
}
getSingleSample(g_audio_track, sample_description, index, ptr, frame_len);
end:
return return_value;
}
int AAC_releaseExtractor()
{
delete g_input_file;
if (g_input)
g_input->Release();
return 0;
}
src/Mp4Extract/Mp42Avc_extract.cpp
/*****************************************************************
|
| AP4 - MP4 to AVC File Converter
|
| Copyright 2002-2009 Axiomatic Systems, LLC
|
|
| This file is part of Bento4/AP4 (MP4 Atom Processing Library).
|
| Unless you have obtained Bento4 under a difference license,
| this version of Bento4 is Bento4|GPL.
| Bento4|GPL is free software; you can redistribute it and/or modify
| it under the terms of the GNU General Public License as published by
| the Free Software Foundation; either version 2, or (at your option)
| any later version.
|
| Bento4|GPL is distributed in the hope that it will be useful,
| but WITHOUT ANY WARRANTY; without even the implied warranty of
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
| GNU General Public License for more details.
|
| You should have received a copy of the GNU General Public License
| along with Bento4|GPL; see the file COPYING. If not, write to the
| Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
| 02111-1307, USA.
|
****************************************************************/
/*----------------------------------------------------------------------
| includes
+---------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include "Ap4.h"
#include "extract.h"
/*----------------------------------------------------------------------
| constants
+---------------------------------------------------------------------*/
#define BANNER \
"MP4 To AVC File Converter - Version 1.0\n" \
"(Bento4 Version " AP4_VERSION_STRING ")\n" \
"(c) 2002-2009 Axiomatic Systems, LLC"
/*----------------------------------------------------------------------
| PrintUsageAndExit
+---------------------------------------------------------------------*/
static void PrintUsageAndExit()
{
fprintf(stderr, BANNER "\n\nusage: mp42avc [options] <input> <output>\n"
" Options:\n"
" --key <hex>: 128-bit decryption key (in hex: 32 chars)\n");
exit(1);
}
/*----------------------------------------------------------------------
| WriteSample
+---------------------------------------------------------------------*/
static void WriteSample(const AP4_DataBuffer &sample_data, AP4_DataBuffer &prefix, unsigned int nalu_length_size,
AP4_ByteStream *output)
{
const unsigned char *data = sample_data.GetData();
unsigned int data_size = sample_data.GetDataSize();
// allocate a buffer for the PES packet
AP4_DataBuffer frame_data;
unsigned char *frame_buffer = NULL;
// add a delimiter if we don't already have one
bool have_access_unit_delimiter =
(data_size > nalu_length_size) &&
((data[nalu_length_size] & 0x1F) == AP4_AVC_NAL_UNIT_TYPE_ACCESS_UNIT_DELIMITER);
if (!have_access_unit_delimiter) {
AP4_Size frame_data_size = frame_data.GetDataSize();
frame_data.SetDataSize(frame_data_size + 6);
frame_buffer = frame_data.UseData() + frame_data_size;
// start of access unit
frame_buffer[0] = 0;
frame_buffer[1] = 0;
frame_buffer[2] = 0;
frame_buffer[3] = 1;
frame_buffer[4] = 9; // NAL type = Access Unit Delimiter;
frame_buffer[5] = 0xE0; // Slice types = ANY
}
// write the NAL units
bool prefix_added = false;
while (data_size) {
// sanity check
if (data_size < nalu_length_size)
break;
// get the next NAL unit
AP4_UI32 nalu_size;
if (nalu_length_size == 1) {
nalu_size = *data++;
data_size--;
} else if (nalu_length_size == 2) {
nalu_size = AP4_BytesToInt16BE(data);
data += 2;
data_size -= 2;
} else if (nalu_length_size == 4) {
nalu_size = AP4_BytesToInt32BE(data);
data += 4;
data_size -= 4;
} else {
break;
}
if (nalu_size > data_size)
break;
// add the prefix if needed
if (prefix.GetDataSize() && !prefix_added && !have_access_unit_delimiter) {
AP4_Size frame_data_size = frame_data.GetDataSize();
frame_data.SetDataSize(frame_data_size + prefix.GetDataSize());
frame_buffer = frame_data.UseData() + frame_data_size;
AP4_CopyMemory(frame_buffer, prefix.GetData(), prefix.GetDataSize());
prefix_added = true;
}
// add a start code before the NAL unit
AP4_Size frame_data_size = frame_data.GetDataSize();
frame_data.SetDataSize(frame_data_size + 3 + nalu_size);
frame_buffer = frame_data.UseData() + frame_data_size;
frame_buffer[0] = 0;
frame_buffer[1] = 0;
frame_buffer[2] = 1;
AP4_CopyMemory(frame_buffer + 3, data, nalu_size);
// add the prefix if needed
if (prefix.GetDataSize() && !prefix_added) {
AP4_Size frame_data_size = frame_data.GetDataSize();
frame_data.SetDataSize(frame_data_size + prefix.GetDataSize());
frame_buffer = frame_data.UseData() + frame_data_size;
AP4_CopyMemory(frame_buffer, prefix.GetData(), prefix.GetDataSize());
prefix_added = true;
}
// move to the next NAL unit
data += nalu_size;
data_size -= nalu_size;
}
output->Write(frame_data.GetData(), frame_data.GetDataSize());
}
void getSample(const AP4_DataBuffer &sample_data, AP4_DataBuffer &prefix, unsigned int nalu_length_size, char *ptr,
int *len)
{
const unsigned char *data = sample_data.GetData();
unsigned int data_size = sample_data.GetDataSize();
// allocate a buffer for the PES packet
AP4_DataBuffer frame_data;
unsigned char *frame_buffer = NULL;
// add a delimiter if we don't already have one
bool have_access_unit_delimiter =
(data_size > nalu_length_size) &&
((data[nalu_length_size] & 0x1F) == AP4_AVC_NAL_UNIT_TYPE_ACCESS_UNIT_DELIMITER);
if (!have_access_unit_delimiter) {
AP4_Size frame_data_size = frame_data.GetDataSize();
frame_data.SetDataSize(frame_data_size + 6);
frame_buffer = frame_data.UseData() + frame_data_size;
// start of access unit
frame_buffer[0] = 0;
frame_buffer[1] = 0;
frame_buffer[2] = 0;
frame_buffer[3] = 1;
frame_buffer[4] = 9; // NAL type = Access Unit Delimiter;
frame_buffer[5] = 0xE0; // Slice types = ANY
}
// write the NAL units
bool prefix_added = false;
while (data_size) {
// sanity check
if (data_size < nalu_length_size)
break;
// get the next NAL unit
AP4_UI32 nalu_size;
if (nalu_length_size == 1) {
nalu_size = *data++;
data_size--;
} else if (nalu_length_size == 2) {
nalu_size = AP4_BytesToInt16BE(data);
data += 2;
data_size -= 2;
} else if (nalu_length_size == 4) {
nalu_size = AP4_BytesToInt32BE(data);
data += 4;
data_size -= 4;
} else {
break;
}
if (nalu_size > data_size)
break;
// add the prefix if needed
if (prefix.GetDataSize() && !prefix_added && !have_access_unit_delimiter) {
AP4_Size frame_data_size = frame_data.GetDataSize();
frame_data.SetDataSize(frame_data_size + prefix.GetDataSize());
frame_buffer = frame_data.UseData() + frame_data_size;
AP4_CopyMemory(frame_buffer, prefix.GetData(), prefix.GetDataSize());
prefix_added = true;
}
// add a start code before the NAL unit
AP4_Size frame_data_size = frame_data.GetDataSize();
frame_data.SetDataSize(frame_data_size + 3 + nalu_size);
frame_buffer = frame_data.UseData() + frame_data_size;
frame_buffer[0] = 0;
frame_buffer[1] = 0;
frame_buffer[2] = 1;
AP4_CopyMemory(frame_buffer + 3, data, nalu_size);
// add the prefix if needed
if (prefix.GetDataSize() && !prefix_added) {
AP4_Size frame_data_size = frame_data.GetDataSize();
frame_data.SetDataSize(frame_data_size + prefix.GetDataSize());
frame_buffer = frame_data.UseData() + frame_data_size;
AP4_CopyMemory(frame_buffer, prefix.GetData(), prefix.GetDataSize());
prefix_added = true;
}
// move to the next NAL unit
data += nalu_size;
data_size -= nalu_size;
}
//printf("get data size:%d\n", frame_data.GetDataSize());
memcpy(ptr, (char *)frame_data.GetData(), (int)frame_data.GetDataSize());
*len = (int)frame_data.GetDataSize();
}
/*----------------------------------------------------------------------
| MakeFramePrefix
+---------------------------------------------------------------------*/
static AP4_Result MakeFramePrefix(AP4_SampleDescription *sdesc, AP4_DataBuffer &prefix, unsigned int &nalu_length_size)
{
AP4_AvcSampleDescription *avc_desc = AP4_DYNAMIC_CAST(AP4_AvcSampleDescription, sdesc);
if (avc_desc == NULL) {
fprintf(stderr, "ERROR: track does not contain an AVC stream\n");
return AP4_FAILURE;
}
if (sdesc->GetFormat() == AP4_SAMPLE_FORMAT_AVC3 || sdesc->GetFormat() == AP4_SAMPLE_FORMAT_AVC4 ||
sdesc->GetFormat() == AP4_SAMPLE_FORMAT_DVAV) {
// no need for a prefix, SPS/PPS NALs should be in the elementary stream already
return AP4_SUCCESS;
}
// make the SPS/PPS prefix
nalu_length_size = avc_desc->GetNaluLengthSize();
for (unsigned int i = 0; i < avc_desc->GetSequenceParameters().ItemCount(); i++) {
AP4_DataBuffer &buffer = avc_desc->GetSequenceParameters()[i];
unsigned int prefix_size = prefix.GetDataSize();
prefix.SetDataSize(prefix_size + 4 + buffer.GetDataSize());
unsigned char *p = prefix.UseData() + prefix_size;
*p++ = 0;
*p++ = 0;
*p++ = 0;
*p++ = 1;
AP4_CopyMemory(p, buffer.GetData(), buffer.GetDataSize());
}
for (unsigned int i = 0; i < avc_desc->GetPictureParameters().ItemCount(); i++) {
AP4_DataBuffer &buffer = avc_desc->GetPictureParameters()[i];
unsigned int prefix_size = prefix.GetDataSize();
prefix.SetDataSize(prefix_size + 4 + buffer.GetDataSize());
unsigned char *p = prefix.UseData() + prefix_size;
*p++ = 0;
*p++ = 0;
*p++ = 0;
*p++ = 1;
AP4_CopyMemory(p, buffer.GetData(), buffer.GetDataSize());
}
return AP4_SUCCESS;
}
/*----------------------------------------------------------------------
| DecryptAndWriteSamples
+---------------------------------------------------------------------*/
static void DecryptAndWriteSamples(AP4_Track *track, AP4_SampleDescription *sdesc, AP4_Byte *key,
AP4_ByteStream *output)
{
AP4_ProtectedSampleDescription *pdesc = AP4_DYNAMIC_CAST(AP4_ProtectedSampleDescription, sdesc);
if (pdesc == NULL) {
fprintf(stderr, "ERROR: unable to obtain cipher info\n");
return;
}
// get the original sample description and make the prefix
AP4_SampleDescription *orig_sdesc = pdesc->GetOriginalSampleDescription();
unsigned int nalu_length_size = 0;
AP4_DataBuffer prefix;
if (AP4_FAILED(MakeFramePrefix(orig_sdesc, prefix, nalu_length_size))) {
return;
}
// create the decrypter
AP4_SampleDecrypter *decrypter = AP4_SampleDecrypter::Create(pdesc, key, 16);
if (decrypter == NULL) {
fprintf(stderr, "ERROR: unable to create decrypter\n");
return;
}
AP4_Sample sample;
AP4_DataBuffer encrypted_data;
AP4_DataBuffer decrypted_data;
AP4_Ordinal index = 0;
AP4_UI32 poolid = 0;
while (AP4_SUCCEEDED(track->ReadSample(index, sample, encrypted_data))) {
if (AP4_FAILED(decrypter->DecryptSampleData(poolid, encrypted_data, decrypted_data, NULL))) {
fprintf(stderr, "ERROR: failed to decrypt sample\n");
return;
}
WriteSample(decrypted_data, prefix, nalu_length_size, output);
index++;
}
}
/*----------------------------------------------------------------------
| WriteSamples
+---------------------------------------------------------------------*/
static void WriteSamples(AP4_Track *track, AP4_SampleDescription *sdesc, AP4_ByteStream *output)
{
// make the frame prefix
unsigned int nalu_length_size = 0;
AP4_DataBuffer prefix;
if (AP4_FAILED(MakeFramePrefix(sdesc, prefix, nalu_length_size))) {
return;
}
AP4_Sample sample;
AP4_DataBuffer data;
AP4_Ordinal index = 0;
while (AP4_SUCCEEDED(track->ReadSample(index, sample, data))) {
WriteSample(data, prefix, nalu_length_size, output);
index++;
}
}
int getSingleFrame(AP4_Track *track, AP4_SampleDescription *sdesc, AP4_Ordinal index, char *ptr, int *frame_len)
{
// make the frame prefix
unsigned int nalu_length_size = 0;
AP4_DataBuffer prefix;
if (AP4_FAILED(MakeFramePrefix(sdesc, prefix, nalu_length_size))) {
return -1;
}
AP4_Sample sample;
AP4_DataBuffer data;
int ret = AP4_SUCCESS;
ret = track->ReadSample(index, sample, data);
if (AP4_SUCCEEDED(ret)) {
getSample(data, prefix, nalu_length_size, ptr, frame_len);
} else if (AP4_FAILED(ret)) {
return -1;
}
return 0;
}
/*----------------------------------------------------------------------
| main
+---------------------------------------------------------------------*/
int AVC_extract_avc(int argc, char **argv)
{
if (argc < 3) {
PrintUsageAndExit();
}
// parse command line
AP4_Result result;
char **args = argv + 1;
unsigned char key[16];
bool key_option = false;
if (!strcmp(*args, "--key")) {
if (argc != 5) {
fprintf(stderr, "ERROR: invalid command line\n");
return 1;
}
++args;
if (AP4_ParseHex(*args++, key, 16)) {
fprintf(stderr, "ERROR: invalid hex format for key\n");
return 1;
}
key_option = true;
}
// create the input stream
AP4_ByteStream *input = NULL;
result = AP4_FileByteStream::Create(*args++, AP4_FileByteStream::STREAM_MODE_READ, input);
if (AP4_FAILED(result)) {
fprintf(stderr, "ERROR: cannot open input (%d)\n", result);
}
// create the output stream
AP4_ByteStream *output = NULL;
result = AP4_FileByteStream::Create(*args++, AP4_FileByteStream::STREAM_MODE_WRITE, output);
if (AP4_FAILED(result)) {
fprintf(stderr, "ERROR: cannot open output (%d)\n", result);
}
// open the file
AP4_File *input_file = new AP4_File(*input);
// get the movie
AP4_SampleDescription *sample_description;
AP4_Track *video_track;
AP4_Movie *movie = input_file->GetMovie();
if (movie == NULL) {
fprintf(stderr, "ERROR: no movie in file\n");
goto end;
}
// get the video track
video_track = movie->GetTrack(AP4_Track::TYPE_VIDEO);
if (video_track == NULL) {
fprintf(stderr, "ERROR: no video track found\n");
goto end;
}
// check that the track is of the right type
sample_description = video_track->GetSampleDescription(0);
if (sample_description == NULL) {
fprintf(stderr, "ERROR: unable to parse sample description\n");
goto end;
}
// show info
AP4_Debug("Video Track: type:%d\n", sample_description->GetType());
AP4_Debug(" duration: %u ms\n", (int)video_track->GetDurationMs());
AP4_Debug(" sample count: %u\n", (int)video_track->GetSampleCount());
switch (sample_description->GetType()) {
case AP4_SampleDescription::TYPE_AVC:
WriteSamples(video_track, sample_description, output);
break;
case AP4_SampleDescription::TYPE_PROTECTED:
if (!key_option) {
fprintf(stderr, "ERROR: encrypted tracks require a key\n");
goto end;
}
DecryptAndWriteSamples(video_track, sample_description, key, output);
break;
default:
fprintf(stderr, "ERROR: unsupported sample type\n");
break;
}
end:
delete input_file;
input->Release();
output->Release();
return 0;
}
AP4_ByteStream *g_vinput;
AP4_File *g_vinput_file;
AP4_Movie *g_vmovie;
AP4_Track *gvideo_track;;
int AVC_getSampleNum(char *file_name, int *num)
{
// create the input stream
AP4_Result result;
result = AP4_FileByteStream::Create(file_name, AP4_FileByteStream::STREAM_MODE_READ, g_vinput);
if (AP4_FAILED(result)) {
fprintf(stderr, "ERROR: cannot open input (%d)\n", result);
return -1;
}
g_vinput_file = new AP4_File(*g_vinput);
g_vmovie = g_vinput_file->GetMovie();
if (g_vmovie == NULL) {
fprintf(stderr, "ERROR: no movie in file\n");
return -1;
}
// get the video track
gvideo_track = g_vmovie->GetTrack(AP4_Track::TYPE_VIDEO);
if (gvideo_track == NULL) {
fprintf(stderr, "ERROR: no video track found\n");
return -1;
}
AP4_Debug("sample count: %u\n", (int)gvideo_track->GetSampleCount());
*num = (int)gvideo_track->GetSampleCount();
return 0;
}
int AVC_getFrameData(char *file_name, int index, char *ptr, int *frame_len)
{
AP4_Result result;
int ret;
#if(0)
// create the input stream
AP4_ByteStream *input = NULL;
result = AP4_FileByteStream::Create(file_name, AP4_FileByteStream::STREAM_MODE_READ, input);
if (AP4_FAILED(result)) {
fprintf(stderr, "ERROR: cannot open input (%d)\n", result);
}
AP4_File *input_file = new AP4_File(*input);
AP4_Movie *movie = input_file->GetMovie();
if (movie == NULL) {
fprintf(stderr, "ERROR: no movie in file\n");
}
AP4_Track *video_track;
// get the video track
video_track = movie->GetTrack(AP4_Track::TYPE_VIDEO);
if (video_track == NULL) {
fprintf(stderr, "ERROR: no video track found\n");
}
#endif
AP4_SampleDescription *sample_description;
// check that the track is of the right type
sample_description = gvideo_track->GetSampleDescription(0);
if (sample_description == NULL) {
fprintf(stderr, "ERROR: unable to parse sample description\n");
return -1;
}
ret = getSingleFrame(gvideo_track, sample_description, index, ptr, frame_len);
if (ret == -1) {
return -1;
}
return 0;
}
int AVC_releaseExtractor()
{
delete g_vinput_file;
if (g_vinput)
g_vinput->Release();
return 0;
}
Makefile
TARGET =extract_avc
SHARE_LIB_TARGET = libextract.so
MAIN_CXX = main.cpp
CXX = $(CROSS_COMPILE)g++
CC = $(CROSS_COMPILE)gcc
STRIP = $(CROSS_COMPILE)strip
CXXFLAGS = -std=c++11 -g -O2 -fPIC
SRC = Mp42Aac_extract.cpp Mp42Avc_extract.cpp $(BENTO4_LIB)/libap4.a
INC = extract.h
INCS = Adapters Apps CApi Codecs Core Crypto MetaData System
BENTO4_INCS = $(addprefix -I$(BENTO4_INC),$(INCS))
# specify targets
.DEFAULT_GOAL := share
.PHONY: clean $(TARGET)
$(TARGET):
$(CXX) -o $(TARGET) $(MAIN_CXX) $(SRC) -I$(BENTO4_INCS)
clean :
rm -f $(TARGET)* $(SHARE_LIB_TARGET)
.PHONY: share
share:
$(Q)$(CXX) $(CXXFLAGS) -shared -o $(SHARE_LIB_TARGET) $(SRC) -I$(BENTO4_INCS) -s -DNDEBUG
.PHONY: install uninstall
install:
cp -f $(SHARE_LIB_TARGET) $(SYSTEM_LIB)
uninstall:
rm -rf $(SYSTEM_LIB)/$(SHARE_LIB_TARGET)