Generate tkhd and pasp boxes properly with non-square pixels

- Width/Height in tkhd box should be display resolution instead of
  coded resolution.
- pasp was not generated earlier.
- Assuming SAR as 1:1 if it failed to be extracted successfully.
- Also update test files for mp4_media_parser_unittest.

This fixes Issue #35.

Change-Id: Iedbe6a44465aac6723b6ea2de30aed180821eccd
This commit is contained in:
Kongqun Yang 2015-08-05 16:39:03 -07:00 committed by KongQun Yang
parent 4f60bfc6c3
commit e5b6096857
10 changed files with 35 additions and 26 deletions

View File

@ -83,6 +83,11 @@ VideoStreamInfo::VideoStreamInfo(int track_id,
DVLOG_IF(2, pixel_width_ == 0 || pixel_height_ == 0) DVLOG_IF(2, pixel_width_ == 0 || pixel_height_ == 0)
<< "Failed to extract sar_width and sar_height."; << "Failed to extract sar_width and sar_height.";
} }
if (pixel_width_ == 0 || pixel_height_ == 0) {
LOG(WARNING) << "SAR is not extracted successfully. Assuming 1:1.";
pixel_width_ = 1;
pixel_height_ = 1;
}
} }
VideoStreamInfo::~VideoStreamInfo() {} VideoStreamInfo::~VideoStreamInfo() {}

View File

@ -372,9 +372,6 @@ bool TrackHeader::ReadWrite(BoxBuffer* buffer) {
// Set default value for volume, if track is audio, 0x100 else 0. // Set default value for volume, if track is audio, 0x100 else 0.
if (volume == -1) if (volume == -1)
volume = (width != 0 && height != 0) ? 0 : 0x100; volume = (width != 0 && height != 0) ? 0 : 0x100;
// Convert integer to 16.16 fix point.
width <<= 16;
height <<= 16;
} }
std::vector<uint8_t> matrix(kUnityMatrix, std::vector<uint8_t> matrix(kUnityMatrix,
kUnityMatrix + arraysize(kUnityMatrix)); kUnityMatrix + arraysize(kUnityMatrix));
@ -386,9 +383,6 @@ bool TrackHeader::ReadWrite(BoxBuffer* buffer) {
buffer->ReadWriteVector(&matrix, matrix.size()) && buffer->ReadWriteVector(&matrix, matrix.size()) &&
buffer->ReadWriteUInt32(&width) && buffer->ReadWriteUInt32(&width) &&
buffer->ReadWriteUInt32(&height)); buffer->ReadWriteUInt32(&height));
// Convert 16.16 fixed point to integer.
width >>= 16;
height >>= 16;
return true; return true;
} }
@ -1001,8 +995,7 @@ bool VideoSampleEntry::ReadWrite(BoxBuffer* buffer) {
buffer->ReadWriteUInt16(&video_depth) && buffer->ReadWriteUInt16(&video_depth) &&
buffer->ReadWriteInt16(&predefined)); buffer->ReadWriteInt16(&predefined));
RCHECK(buffer->PrepareChildren() && RCHECK(buffer->PrepareChildren());
buffer->TryReadWriteChild(&pixel_aspect));
if (format == FOURCC_ENCV) { if (format == FOURCC_ENCV) {
if (buffer->Reading()) { if (buffer->Reading()) {
@ -1021,6 +1014,7 @@ bool VideoSampleEntry::ReadWrite(BoxBuffer* buffer) {
(format == FOURCC_ENCV && sinf.format.format == FOURCC_AVC1)) { (format == FOURCC_ENCV && sinf.format.format == FOURCC_AVC1)) {
RCHECK(buffer->ReadWriteChild(&avcc)); RCHECK(buffer->ReadWriteChild(&avcc));
} }
RCHECK(buffer->TryReadWriteChild(&pixel_aspect));
return true; return true;
} }

View File

@ -134,6 +134,8 @@ struct TrackHeader : FullBox {
int16_t layer; int16_t layer;
int16_t alternate_group; int16_t alternate_group;
int16_t volume; int16_t volume;
// width and height specify the track's visual presentation size as
// fixed-point 16.16 values.
uint32_t width; uint32_t width;
uint32_t height; uint32_t height;
}; };

View File

@ -114,15 +114,13 @@ TEST_F(MP4MediaParserTest, UnalignedAppend) {
// the container has a 'pasp' box. // the container has a 'pasp' box.
TEST_F(MP4MediaParserTest, PixelWidthPixelHeightFromPaspBox) { TEST_F(MP4MediaParserTest, PixelWidthPixelHeightFromPaspBox) {
// This content has a 'pasp' box that has the aspect ratio. // This content has a 'pasp' box that has the aspect ratio.
EXPECT_TRUE(ParseMP4File("bear-1280x720.mp4", 512)); EXPECT_TRUE(ParseMP4File("bear-640x360-non_square_pixel-with_pasp.mp4", 512));
// Track ID 2 has the video stream which should have pixel width and height const int kVideoTrackId = 1;
// both 1. EXPECT_EQ(8u,
const int kVideoTrackId = 2;
EXPECT_EQ(1u,
reinterpret_cast<VideoStreamInfo*>(stream_map_[kVideoTrackId].get()) reinterpret_cast<VideoStreamInfo*>(stream_map_[kVideoTrackId].get())
->pixel_width()); ->pixel_width());
EXPECT_EQ(1u, EXPECT_EQ(9u,
reinterpret_cast<VideoStreamInfo*>(stream_map_[kVideoTrackId].get()) reinterpret_cast<VideoStreamInfo*>(stream_map_[kVideoTrackId].get())
->pixel_height()); ->pixel_height());
} }
@ -132,13 +130,11 @@ TEST_F(MP4MediaParserTest, PixelWidthPixelHeightFromPaspBox) {
// No 'pasp' box. // No 'pasp' box.
TEST_F(MP4MediaParserTest, TEST_F(MP4MediaParserTest,
PixelWidthPixelHeightFromAVCDecoderConfigurationRecord) { PixelWidthPixelHeightFromAVCDecoderConfigurationRecord) {
// This file doesn't have pasp. SPS for the video has // This file doesn't have pasp. The stream should extract pixel width and
// sar_width = sar_height = 0. So the stream info should return 1 for both // height from SPS.
// pixel_width and pixel_height. EXPECT_TRUE(
EXPECT_TRUE(ParseMP4File("hb2_v_frag.mp4", 512)); ParseMP4File("bear-640x360-non_square_pixel-without_pasp.mp4", 512));
// Track ID 1 has the video stream which should have pixel width and height
// both 1.
const int kVideoTrackId = 1; const int kVideoTrackId = 1;
EXPECT_EQ(8u, EXPECT_EQ(8u,
reinterpret_cast<VideoStreamInfo*>(stream_map_[kVideoTrackId].get()) reinterpret_cast<VideoStreamInfo*>(stream_map_[kVideoTrackId].get())
@ -156,10 +152,8 @@ TEST_F(MP4MediaParserTest,
// This file doesn't have pasp. SPS for the video has // This file doesn't have pasp. SPS for the video has
// sar_width = sar_height = 0. So the stream info should return 1 for both // sar_width = sar_height = 0. So the stream info should return 1 for both
// pixel_width and pixel_height. // pixel_width and pixel_height.
EXPECT_TRUE(ParseMP4File("bear-1280x720-av_frag.mp4", 512)); EXPECT_TRUE(ParseMP4File("bear-640x360-av_frag.mp4", 512));
// Track ID 1 has the video stream which should have pixel width and height
// both 1.
const int kVideoTrackId = 1; const int kVideoTrackId = 1;
EXPECT_EQ(1u, EXPECT_EQ(1u,
reinterpret_cast<VideoStreamInfo*>(stream_map_[kVideoTrackId].get()) reinterpret_cast<VideoStreamInfo*>(stream_map_[kVideoTrackId].get())

View File

@ -159,8 +159,14 @@ void MP4Muxer::GenerateVideoTrak(const VideoStreamInfo* video_info,
uint32_t track_id) { uint32_t track_id) {
InitializeTrak(video_info, trak); InitializeTrak(video_info, trak);
trak->header.width = video_info->width(); // width and height specify the track's visual presentation size as
trak->header.height = video_info->height(); // fixed-point 16.16 values.
const double sample_aspect_ratio =
static_cast<double>(video_info->pixel_width()) /
video_info->pixel_height();
trak->header.width = video_info->width() * sample_aspect_ratio * 0x10000;
trak->header.height = video_info->height() * 0x10000;
trak->media.handler.type = kVideo; trak->media.handler.type = kVideo;
VideoSampleEntry video; VideoSampleEntry video;
@ -168,6 +174,10 @@ void MP4Muxer::GenerateVideoTrak(const VideoStreamInfo* video_info,
video.width = video_info->width(); video.width = video_info->width();
video.height = video_info->height(); video.height = video_info->height();
video.avcc.data = video_info->extra_data(); video.avcc.data = video_info->extra_data();
if (video_info->pixel_width() != 1 || video_info->pixel_height() != 1) {
video.pixel_aspect.h_spacing = video_info->pixel_width();
video.pixel_aspect.v_spacing = video_info->pixel_height();
}
SampleDescription& sample_description = SampleDescription& sample_description =
trak->media.information.sample_table.description; trak->media.information.sample_table.description;

View File

@ -44,6 +44,10 @@ bear-640x360-v_frag-cenc.mp4 - A fragmented MP4 version of the video track of be
[1] 30313233343536373839303132333435 [1] 30313233343536373839303132333435
[2] ebdd62f16814d27b68ef122afce4ae3c [2] ebdd62f16814d27b68ef122afce4ae3c
// Non square pixels.
bear-640x360-non_square_pixel-with_pasp.mp4 - A non-square pixel version of the video track of bear-640x360.mp4 with PixelAspectRatio box.
bear-640x360-non_square_pixel-without_pasp.mp4 - A non-square pixel version of the video track of bear-640x360.mp4 without PixelAspectRatio box.
// Container Tests (additional containers derived from bear.ogv) // Container Tests (additional containers derived from bear.ogv)
bear.ac3 -- created using "avconv -i bear.ogv -f ac3 -b 192k bear.ac3". bear.ac3 -- created using "avconv -i bear.ogv -f ac3 -b 192k bear.ac3".
bear.adts -- created using "avconv -i bear.ogv -f adts -strict experimental bear.adts". bear.adts -- created using "avconv -i bear.ogv -f adts -strict experimental bear.adts".

View File

@ -97,7 +97,7 @@
'media/filters/filters.gyp:filters_unittest', 'media/filters/filters.gyp:filters_unittest',
'media/formats/mp2t/mp2t.gyp:mp2t_unittest', 'media/formats/mp2t/mp2t.gyp:mp2t_unittest',
'media/formats/mp4/mp4.gyp:mp4_unittest', 'media/formats/mp4/mp4.gyp:mp4_unittest',
# 'media/formats/wvm/wvm.gyp:wvm_unittest', 'media/formats/wvm/wvm.gyp:wvm_unittest',
'mpd/mpd.gyp:mpd_unittest', 'mpd/mpd.gyp:mpd_unittest',
'packager_test', 'packager_test',
], ],